Drawing Chessboards
I wanted a picture of a chessboard. Rather than boot up some drawing software and cut and paste black and white squares I decided to write a program to create the picture.
If you’d like to know why anyone would ever create work for themselves in this way, skip to the end of this article, where you’ll find justification and a more challenging follow-on problem. Otherwise, please read on from top to bottom in the usual way.
The Python Imaging Library
Fredrik Lundh’s Python Imaging Library (commonly known as PIL) must surely rank as one of the most popular Python libraries which doesn’t come as standard1. It’s a fabulous tool which I’ve used to create the graphic above (though note that the double border around this graphic and subsequent ones is applied by a CSS style property). Here’s how.
def draw_chessboard(n=8, pixel_width=200): "Draw an n x n chessboard using PIL." import Image, ImageDraw from itertools import cycle def sq_start(i): "Return the x/y start coord of the square at column/row i." return i * pixel_width / n def square(i, j): "Return the square corners, suitable for use in PIL drawings" return map(sq_start, [i, j, i + 1, j + 1]) image = Image.new("L", (pixel_width, pixel_width)) draw_square = ImageDraw.Draw(image).rectangle squares = (square(i, j) for i_start, j in zip(cycle((0, 1)), range(n)) for i in range(i_start, n, 2)) for sq in squares: draw_square(sq, fill='white') image.save("chessboard-pil.png")
Note:
- We don’t draw any black squares, instead relying on the default image background being black.
- The “L” image type (Luminance?) specifies a greyscale image.
- PIL adopts the usual raster graphics convention, of the origin being in the top-left corner.
- As we progress down the board row by row, the first white square alternates between being the first and second square of each row.
Itertools.cycle((0, 1))
achieves this nicely. - A regular 8 x 8 chessboard will, then, have a black square at the bottom left, which is the usual convention. For odd values of n the bottom-left square would be white.
- There may be rounding problems with this code if the supplied pixel width isn’t an integral multiple of
n
. It’s probably better to guarantee the image size, rather than round down the board size. - It would be better to parametrise the output file name, or even return the created image to clients. For now, we’ll just save to a fixed-name PNG.
ImageMagick
PIL is a general purpose image processing library and it takes a little head-scratching and maths before we can even create something as simple as a chessboard. ImageMagick provides tools to perform a similar job from the command-line, making the chessboard a one-liner.
$ N=8 $ PIXEL_WIDTH=200 $ convert -size $((N*15))x$((N*15)) pattern:checkerboard \ -monochrome -resize $PIXEL_WIDTH chessboard-magick.png
Here, the checkerboard pattern is an ImageMagick built-in which, inspecting its output, happens to generate 15x15 squares (hence the 15’s in the script above). The -monochrome
filter renders the pattern in black and white, rather than its native light- on dark-grey. The -size
and -resize
parameters should need no further explanation. The ((double parentheses)) perform Bash shell arithmetic.
ImageMagick masquerades as a shell tool but really it’s a powerful and fully featured programmer’s imaging tool — a bit like a command-line version of Gimp2. Although well documented, my gut reaction is that it pushes the command-line interface too far. For more advanced image mangling, you’ll probably need a program to generate the one-liner needed to drive convert
. Despite this reservation, it does the simple things simply, and it can do complex things too. Recommended!
Google Chart API
For a bit of fun, we can persuade Google to render the chessboard for us — in this case as a scatter plot using a square black markers[3]. We flip the PIL processing around, drawing black squares on the (default) white background, and using the usual plotting convention which places the origin at the bottom left.
def chessboard_url(n=8, pixel_width=200): "Returns the URL of a chessboard graphic." def sq_midpt(i): "Return the x/y midpt of a square in column/row i." # For text encoding, the graphic's logical width is 100 return (0.5 + i) * 100. / n xys = [(sq_midpt(i), sq_midpt(j)) for i_start, j in zip(cycle((0, 1)), range(n)) for i in range(i_start, n, 2)] fields = dict(width=pixel_width, sqside=pixel_width/n, xs=",".join("%.02f" % x for x, _ in xys), ys=",".join("%.02f" % y for _, y in xys)) return ( "http://chart.apis.google.com/chart?" "cht=s&" # Draw a scatter graph "chd=t:%(xs)s|%(ys)s&" # using text encoding and "chm=s,000000,1,2.0,%(sqside)r&"# square black markers "chs=%(width)rx%(width)r" # at this size. ) % fields
Note that we plot our chart on a logical 100 x 100 rectangle, the coordinate space mandated by the encoding we’ve chosen, then resize it to the physical dimensions supplied by the client.
This function actually returns the URL of a PNG which the Google chart API serves up. Paste this URL into your browser address bar to see the graphic, or curl it to a local file.
http://chart.apis.google.com/chart?cht=s&chd=t:6.25,31.25…&chs=200x200
$ url=`python chessboard_url.py` $ curl $url > chessboard.png
We could embed the image into HTML using the IMG element, which is how I’ve embedded the image which you should see below.
>>> from cgi import escape >>> img = '<img src="%s" alt="chessboard graphic"/>' >>> img % escape(chessboard_url())
As you can see, we have plenty of options, but unfortunately the image itself isn’t suitable. You can’t get rid of the axes — or at least, I haven’t found a way to — and the rendered chart has some padding to the top and the right. And worse, we’re pretty much at the end of the line for this hack: if we wanted to do something more interesting, such as place pieces on the board, we’re out of luck.
Of course this isn’t a flaw in the Google Chart API: we’ve actually asked it to draw a scatter plot of the centres of black squares on a chessboard, using square black markers, a job it’s done well enough. Some examples showing the proper use of Google charts can be found in an article I wrote about maximum sum subsequences.
ASCII Text
The chart URL might be considered a text encoding of the image; the actual graphic is returned by a server. There are other, more direct, textual representations.
def outer_join(sep, ss): """Like string.join, but encloses the result with outer separators. Example: >>> outer_join('|', ['1', '2', '3']) '|1|2|3|' """ return "%s%s%s" % (sep, sep.join(ss), sep) def ascii_chessboard(n=8): """Draws an ASCII art chessboard. Returns a string representation of an n x n board. """ from itertools import islice, cycle divider = outer_join("+", "-" * n) + "\n" row0 = outer_join("|", islice(cycle(" B"), n)) + "\n" row1 = outer_join("|", islice(cycle("B "), n)) + "\n" return outer_join(divider, islice(cycle([row0, row1]), n))
I suspect this code was easier for me to write than it is for you to read! It treats the chessboard as a sequence of alternating rows of alternating squares, which are then joined together for output.
>>> print ascii_chessboard(8) +-+-+-+-+-+-+-+-+ | |B| |B| |B| |B| +-+-+-+-+-+-+-+-+ |B| |B| |B| |B| | +-+-+-+-+-+-+-+-+ | |B| |B| |B| |B| +-+-+-+-+-+-+-+-+ |B| |B| |B| |B| | +-+-+-+-+-+-+-+-+ | |B| |B| |B| |B| +-+-+-+-+-+-+-+-+ |B| |B| |B| |B| | +-+-+-+-+-+-+-+-+ | |B| |B| |B| |B| +-+-+-+-+-+-+-+-+ |B| |B| |B| |B| | +-+-+-+-+-+-+-+-+
Not pretty, but such graphics may be useful in source code, which is typically viewed in a plain-text editor, and where ASCII art provides a way of embedding pictures right where they’re needed.
On which point: if you’re working through “Structure and Interpretation of Computer Programs” you may like to know the book is available in Texinfo format, with the pictures all rendered in ASCII art. So you can split your editor window and run the code on one side, while browsing the book on the other. Here’s one of the figures:
*Figure 4.6:* The `or' combination of two queries is produced by operating on the stream of frames in parallel and merging the results. +---------------------------+ | (or A B) | | +---+ | input | +->| A |------------+ | output stream of | | +---+ V | stream of frames | | ^ +-------+ | frames -------------* | | merge +---------------> | | | +-------+ | | | | ^ | | | | +---+ | | | +------->| B +------+ | | | +---+ | | | ^ | | | | | | +--*--+ | +---------|-----------------+ | data base
Even though I own a copy of the book and the full text is available on-line, this primitive info version has become my preferred format when actually running the code examples and exercises.
Unicode Block Elements
Most programming languages may be stuck in ASCII, but we needn’t restict ourselves in this way. I found some block elements in the Geometrical Symbols section of the Unicode code charts (Unicode Block Elements (PDF)). Here’s a pre-rendered block of text composed of the light and dark shade block characters, U+2591 LIGHT SHADE and U+2593 DARK SHADE.
░▓░▓░▓░▓ ▓░▓░▓░▓░ ░▓░▓░▓░▓ ▓░▓░▓░▓░ ░▓░▓░▓░▓ ▓░▓░▓░▓░ ░▓░▓░▓░▓ ▓░▓░▓░▓░
And more
I can think of plenty of other ways to draw a chessboard. My favourite drawing environments are the pencil and paper, and the pen and whiteboard; combine the former with a scanner and the latter with a digital camera and you’ve got an easy route to an electronic version of your design.
For an HTML document I suspect SVG would be a good choice, but I don’t know enough about SVG to state this with confidence. I bet you could go a long way with CSS too. Wikipedia’s chess board is a table built on top of two small images, a light and a dark square, which I guess saves on bandwidth.
Why?
Why ever bother programming when all we want is a simple graphic?
Well, for one thing, there’s not that much programming. The actual work of pushing pixels around is done by Google, or PIL, or ImageMagick.
Once we’ve got a program written, it should be easy to adapt it. We’ve already put in hooks to specify the number of squares and the image dimensions. It’s equally easy to, for example, write out a JPEG rather than a PNG, or use different colours.
A programmatic solution is dynamic. Google’s chart API generates pictures on the fly, based on data points, ranges etc. which clients choose as and when. It’s rather like lazy-evaluation: pre-rendering all possibilities isn’t just expensive, it’s out of the question.
Teaser
That’s quite enough pixels and characters for now, so this article will have to appear in two parts. If I’ve still not convinced you of the merits of creating images programmatically, please consider the following puzzle.
How would you draw a position reached in a game of chess, showing both the board and the pieces?
And if I have convinced you, this exercise makes for a good workout.
Some Q&A’s.
- Q: What position, exactly?
- A: Any!
- Q: How will the position be described?
- A: Your choice — it’s an interesting part of the puzzle.
A great starting point would be to solve the puzzle using an ASCII art representation.
You can find my solution in this follow-up article.
Thanks
Thanks to Marius Gedminas and Johannes Hoff for their help bug-fixing this article.
1 I’m confused about where exactly PIL belongs; the official homepage seems to be on the PythonWare website (http://www.pythonware.com/library/pil/handbook/), but I usually head for the Effbot site, http://effbot.org/imagingbook/. I think the sites mirror the same information, so it boils down to whether you prefer a blue or green theme, and how off-putting you find all the ads-by-google.
2 Actually, you can use Gimp from the command-line, and it comes with some tools for creating and editing batch files, and indeed for creating a personal suite of image processing scripts. I’ve never used Gimp in this way, so I can’t say much more about this.
[3] In theory you could use the Google Chart API to render any image in a pointillist manner: just plot enough pixels in the right places.