banner



How To Draw A Chess Board Using C++

Cartoon Chess Positions

2008-03-30 • Python, Graphics, Chess, PIL, Characters • Comments

Dominus Connects

In a recent commodity Marker Dominus describes how he grew frustrated with his graphical editor and wrote a script to draw connectors:

Here's what I did instead. I wrote a program that would read an input similar this:

            >-v-<         '-+-`          

and produce a jpeg file that looks similar this: Line and box graphic

I haven't tried running the software, which, Dominus admits, isn't his virtually polished. What interests me is: the way he devises a mini-linguistic communication for describing these connectors, then combines hand-built and standard tools to produce the required event; and how quickly he ditches the Gimp and settles on this approach. Clearly he's done this sort of thing before.

Chessboards Revisited

Recently I wrote nigh a rather easier graphics problem, of drawing chessboards. My real mission, though, was to promote scripted graphics. A chessboard would brand a skillful starting indicate, I thought. I planned to get on to draw a more advanced drawing trouble, of putting pieces on the lath — a trouble requiring more pixel bashing and more than thought most inputs.

This article tackles that follow-on trouble. What I didn't realise — but really should have guessed — is that it's a problem which has been solved many times earlier in many unlike domains. You tin notice LaTeX packages and emacs modes for it. There's even a MediaWiki macro. So if you demand to describe chess positions please investigate what'southward already out at that place.

That said, the rest of this article follows on from its predecessor. Nosotros'll settle on a suitable notation for describing chess positions and use this as a basis for creating ASCII, Unicode + CSS, and PNG graphics. We'll as well discuss the advantages of using an interpreted, dynamic language for image processing.

Plan Input

Things I learned while writing this article: Forsyth-Edwards Note (FEN) is a standard way to record a chess position.

Mark Dominus invented his own input notation. Nosotros needn't. The Forsyth-Edwards notation for recording a particular board position is compact, simple and standard.

From Wikipedia:

A FEN "record" defines a detail game position, all in 1 text line and using only the ASCII character fix. A FEN record contains six fields. The separator between fields is a space. The fields are:

  1. Piece placement (from white's perspective). Each rank is described, starting with rank viii and ending with rank ane; within each rank, the contents of each square are described from file a through file h. Post-obit the Standard Algebraic Annotation (SAN), each piece is identified by a unmarried letter of the alphabet taken from the standard English names (pawn = "P", knight = "North", bishop = "B", rook = "R", queen = "Q" and king = "K"). White pieces are designated using upper-case letters ("PNBRQK") while Black take lowercase ("pnbrqk"). Bare squares are noted using digits 1 through 8 (the number of blank squares), and "/" split up ranks.

The remaining five fields store other pieces of state (whose plough it is, who tin can castle etc.) required for resuming a game. We'll omit them from our input.

So, for example, we record the start position:

rnbqkbnr/pppppppp/8/viii/8/8/PPPPPPPP/RNBQKBNR

Some moves subsequently, the game might exist at:

r2q1rk1/pp2ppbp/1np2np1/2Q3B1/3PP1b1/2N2N2/PP3PPP/3RKB1R

ASCII Chess Positions

The commencement field of the FEN tape is already close to an ASCII representation of a chessboard. If nosotros expand the digits into the spaces they represent and switch the forwards slashes for newlines, and then press the resulting string gives an 8x8 text square. (By the way, I've fixed the chessboard size at viii rather than make it an input parameter since the FEN notation won't work for a board size of 10x10 or bigger.) Information technology'south not difficult to add some ASCII dividers to tart upwardly this unproblematic graphic.

def expand_blanks(fen):     '''Expand the digits in an FEN string into spaces      >>> expand_blanks("rk4q3")     'rk    q   '     '''     def expand(lucifer):         return ' ' * int(match.group(0))     return re.compile(r'\d').sub(expand, fen)  def outer_join(sep, ss):     '''Like cord.join, only encloses the result with outer separators.      Case:     >>> outer_join('|', ['one', 'two', '3'])     '|one|ii|3|'     '''     return '%s%s%due south' % (sep, sep.join(ss), sep)  def ascii_draw_chess_position(fen):     '''Returns an ASCII movie of pieces on a chessboard.'''     pieces = expand_blanks(fen).replace('/', '')     divider = '+-+-+-+-+-+-+-+-+\north'     rows = ((outer_join('|', pieces[r: r + viii]) + '\n')             for r in range(0, 8 * viii, viii))     return outer_join(divider, rows)          

An example:

>>> fen = "r2q1rk1/pp2ppbp/1np2np1/2Q3B1/3PP1b1/2N2N2/PP3PPP/3RKB1R" >>> impress ascii_draw_chess_position(fen) +-+-+-+-+-+-+-+-+ |r| | |q| |r|chiliad| | +-+-+-+-+-+-+-+-+ |p|p| | |p|p|b|p| +-+-+-+-+-+-+-+-+ | |n|p| | |n|p| | +-+-+-+-+-+-+-+-+ | | |Q| | | |B| | +-+-+-+-+-+-+-+-+ | | | |P|P| |b| | +-+-+-+-+-+-+-+-+ | | |N| | |N| | | +-+-+-+-+-+-+-+-+ |P|P| | | |P|P|P| +-+-+-+-+-+-+-+-+ | | | |R|G|B| |R| +-+-+-+-+-+-+-+-+          

Unicode + CSS

Things I learned while writing this article: Unicode has code points for the black and white chess pieces.

This means we tin get a rather amend movie of a chess position using null more text. Notation that these Unicode characters solve the internationalisation trouble without the need for translators. (We will need a suitable font though!)

Hither's how we can create a dictionary which maps the FEN slice ASCII names to their HTML entity codes.

unicode_pieces=dict(     zip("KQRBNPkqrbnp",         ("&#x%x;" % uc for uc in range(0x2654, 0x2660))))          

I've used this dictionary to create the block of text shown beneath. I've tried using CSS to colour and place squares on the board — sorry if it doesn't piece of work in your browser, I'm no CSS expert!

                                                                                                                                                            






This image isn't ideal: the lath background is visible through the pieces, which is particularly noticeable for white pieces on night squares. I haven't figured out how to eliminate this flaw!

Wiki Macros

Things I learned while writing this article: MediaWiki has a fully-featured macro for chess diagrams.

Here's the opening chess position as a MediaWiki macro. If this seems similar too much effort to type, David A. Wheeler provides an online FEN-to-Wikipedia conversion tool.

{{Chess diagram|= | tright |  |=  8 |rd|nd|bd|qd|kd|bd|nd|rd|=  7 |pd|pd|pd|pd|pd|pd|pd|pd|=  six |  |  |  |  |  |  |  |  |=  v |  |  |  |  |  |  |  |  |=  4 |  |  |  |  |  |  |  |  |=  three |  |  |  |  |  |  |  |  |=  2 |pl|pl|pl|pl|pl|pl|pl|pl|=  i |rl|nl|bl|ql|kl|bl|nl|rl|=     a  b  c  d  e  f  k  h |  }}

Python Imaging Library

Things I learned while writing this commodity: Bone X has bug distinguishing upper- and lower-case filenames.

The OS Ten filename limitation came as a nasty surprise. Most of the time I utilize my Mac similar any other Unix box and so I naturally expected that Yard.png and k.png would co-reside happily in the same directory. They tin't! Manifestly information technology's for backwards compatibility, to proceed old software alive. Yuck!

Anyway, to render a chess position using the Python Imaging Library (PIL), nosotros'll need some suitable pictures of the pieces. I downloaded some from Wikipedia (cheers!) It's of import these images have an blastoff aqueduct. (The blastoff channel assigns an opacity to each pixel, which will exist used when we compose the paradigm with another: when we put the piece on the lath, that is. Without an alpha aqueduct, we wouldn't come across the squares underneath the pieces.)

Interacting with Images

An interpreted language comes into its own when working with an image. Here'south a session in which we open up a PNG (the blackness king) and poke effectually at it to find:

  • what information technology looks similar
  • its fashion and size
  • whether all pixels are grey
  • how many transparent and opaque pixels it has
  • the contents of a few pixels on the left of the image
>>> import Image >>> male monarch = Image.open('grand.png') >>> king.show() >>> rex.mode 'RGBA' >>> king.size (45, 45) >>> pixels = male monarch.load() >>> def is_grey(rgba): ...     r, g, b, a = rgba ...     return r == 1000 == b ...  >>> W, H = king.size >>> xys = [(x, y) for y in range(H) for 10 in range(Due west)] >>> all(is_grey(pixels[xy]) for xy in xys)  True >>> sum(1 for xy in xys if pixels[xy][3] == 0) 1243 >>> sum(1 for xy in xys if pixels[xy][iii] == 255) 612 >>> print "\n".join(map(repr, (pixels[x, twenty] for x in range(10)))) (0, 0, 0, 0) (0, 0, 0, 0) (0, 0, 0, 0) (0, 0, 0, 0) (0, 0, 0, 0) (15, 15, 15, 170) (247, 247, 247, 255) (148, 148, 148, 255) (0, 0, 0, 255) (0, 0, 0, 255)          

To place this piece on top of a background paradigm nosotros utilise Image.paste. Again, we tin experiment interactively.

>>> red_sq = Image.new('RGBA', rex.size, 'red') >>> assistance(red_sq.paste) Aid on method paste in module Image:  paste(self, im, box=None, mask=None) method of Image.Image instance     Paste other epitome into region  >>> mask = king.split()[3] >>> red_sq.paste(king, None, mask) >>> red_sq.show()          

Notation that Image.show allows us to view the image using some platform dependent utility.

The details

One time we've figured out how to put pieces on the lath using Epitome.paste, the rest is all details. I've decided to create a class for rendering chess positions. Creating a form instance pre-loads the piece graphics and sketches in the board groundwork; each time we phone call draw, the groundwork is copied and the pieces are pasted into place. The resulting paradigm is returned directly to the client, who can cull what to exercise with information technology.

#! /usr/bin/env python '''Code to draw chess board and pieces.  FEN notation to draw the organisation of peices on a chess board.  White pieces are coded: K, Q, B, Due north, R, P, for male monarch, queen, bishop, rook knight, pawn. Black pieces use lowercase k, q, b, north, r, p. Blank squares are noted with digits, and the "/" separates ranks.  As an example, the game starts at:  rnbqkbnr/pppppppp/eight/eight/8/8/PPPPPPPP/RNBQKBNR  See: http://en.wikipedia.org/wiki/Forsyth-Edwards_Notation ''' import re import Image import ImageDraw  grade BadChessboard(ValueError):     laissez passer  def expand_blanks(fen):     '''Expand the digits in an FEN string into spaces      >>> expand_blanks("rk4q3")     'rk    q   '     '''     def expand(lucifer):         return ' ' * int(match.grouping(0))     render re.compile(r'\d').sub(aggrandize, fen)  def check_valid(expanded_fen):     '''Asserts an expanded FEN cord is valid'''     match = re.compile(r'([KQBNRPkqbnrp ]{8}/){8}$').match     if not match(expanded_fen + '/'):         raise BadChessboard()  def expand_fen(fen):     '''Preprocesses a fen string into an internal format.      Each foursquare on the chessboard is represented by a unmarried      character in the output cord. The rank separator characters     are removed. Invalid inputs raise a BadChessboard error.     '''     expanded = expand_blanks(fen)     check_valid(expanded)     return expanded.supersede('/', '')  def draw_board(n=viii, sq_size=(20, 20)):     '''Return an image of a chessboard.      The board has n x n squares each of the supplied size.'''     from itertools import cycle     def square(i, j):         return i * sq_size[0], j * sq_size[i]     opaque_grey_background = 192, 255     board = Image.new('LA', square(n, n), opaque_grey_background)      draw_square = ImageDraw.Describe(board).rectangle     whites = ((square(i, j), foursquare(i + 1, j + 1))               for i_start, j in zilch(bike((0, 1)), range(n))               for i in range(i_start, n, 2))     for white_square in whites:         draw_square(white_square, fill up='white')     return lath  class DrawChessPosition(object):     '''Chess position renderer.      Create an example of this class, then call      '''     def __init__(self):         '''Initialise, preloading pieces and creating a blank lath.'''          self.n = 8         self.create_pieces()         self.create_blank_board()      def create_pieces(cocky):         '''Load the chess pieces from disk.          Besides extracts and caches the alpha masks for these pieces.          '''         whites = 'KQBNRP'         piece_images = dict(             zip(whites, (Epitome.open('pieces/w%s.png' % p) for p in whites)))         blacks = 'kqbnrp'         piece_images.update(dict(             zip(blacks, (Prototype.open up('pieces/%s.png' % p) for p in blacks))))         piece_sizes = ready(slice.size for slice in piece_images.values())         # Sanity check: the pieces should all exist the same size         assert len(piece_sizes) == 1         self.piece_w, self.piece_h = piece_sizes.pop()         cocky.piece_images = piece_images         self.piece_masks = dict((pc, img.split()[three]) for pc, img in                                  self.piece_images.iteritems())      def create_blank_board(self):         '''Pre-render a blank board.'''         cocky.board = draw_board(sq_size=(self.piece_w, self.piece_h))      def bespeak(self, i, j):         '''Return the top left of the foursquare at (i, j).'''         w, h = cocky.piece_w, cocky.piece_h         return i * h, j * w      def square(cocky, i, j):         '''Render the foursquare at (i, j).'''         t, 50 = self.point(i, j)         b, r = self.indicate(i + 1, j + 1)         return t, 50, b, r      def draw(cocky, fen):         '''Return an image depicting the input position.          fen - the first record of a FEN chess position.         Clients are responsible for resizing this image and saving information technology,         if required.         '''         lath = self.board.copy()         pieces = expand_fen(fen)         images, masks, n = self.piece_images, self.piece_masks, self.n         pts = (self.point(i, j) for j in range(n) for i in range(n))         def not_blank(pt_pc):             return pt_pc[1] != ' '         for pt, piece in filter(not_blank, zip(pts, pieces)):             board.paste(images[piece], pt, masks[piece])         return board          

This code depends on PNGs for the pieces being bachelor in the electric current directory, filed under the (case-sensitive!) names: {K,Q,B,North,R,P,k,q,b,n,r,p}.png. Information technology also requires all these PNGs to have the same dimensions. Here'due south how to employ it:

>>> renderer = DrawChessPosition() >>> fen = "r2q1rk1/pp2ppbp/1np2np1/2Q3B1/3PP1b1/2N2N2/PP3PPP/3RKB1R" >>> board = renderer.describe(fen) >>> board.bear witness() >>> lath.relieve("%s.png" % fen.supplant('/', '-'))          

And here's the resulting image (with a CSS double border):

Chess position with FEN r2q1rk1/pp2ppbp/1np2np1/2Q3B1/3PP1b1/2N2N2/PP3PPP/3RKB1R

Using a font with PIL

There is a problem with the approach taken in the previous section. We only get a good graphic if we save the returned image at its native size: scaling it up or downwards results in a suboptimal moving-picture show. That's considering the scaling doesn't have plenty information to continue — it has to work from a pixel raster when information technology actually needs strokes or vector graphics.

Then if we calibration the linear dimensions up or down:

>>> fen = "r2q1rk1/pp2ppbp/1np2np1/2Q3B1/3PP1b1/2N2N2/PP3PPP/3RKB1R" >>> renderer = DrawChessPosition() >>> lath = renderer.draw(fen) >>> board.size (360, 360) >>> small_board = board.resize((160, 160)) >>> big_board = board.resize((640, 640)) >>> big_4_squares = big_board.ingather([80, 160, 240, 320]) >>> big_4_squares.show() >>> small_board.prove()          

we'll get something like this:

Small chess board Section of large chess board

The way to avoid the aliasing bug is to work straight from a stroke representation of the chess pieces; for example, by using a suitable font. In one case once again, PIL can do the job (though you'll need to have installed PIL with FreeType support). I institute a freely available unicode truthful type font and plugged it into the following code:

unichr_pieces=dict(     zip("KQRBNPkqrbnp",         (unichr(uc) for uc in range(0x2654, 0x2660))))  def chess_position_using_font(fen, font_file, sq_size):     '''Return a chess position image.      font_file - the name of a font file     sq_size - the size of each foursquare on the chess board     '''     font = ImageFont.truetype(font_file, sq_size)     pieces = expand_fen(fen)     board = draw_board(sq_size=(sq_size, sq_size))     put_piece = ImageDraw.Draw(board).text     def signal(i, j):         render i * sq_size, j * sq_size     def not_blank(pt_pce):         return pt_pce[ane] != ' '     pts = (point(i, j) for j in range(eight) for i in range(8))     for pt, piece in filter(not_blank, zip(pts, pieces)):         put_piece(pt, unichr_pieces[slice], fill='blackness', font=font)     return board          

If we use this lawmaking to create modest and large chess pictures, much every bit before, we'll encounter something like this:

Small chess board Section of large chess board

Every bit with our CSS + Unicode motion picture, this image isn't platonic since the lath shows through the interiors of the pieces. And once again, I haven't figured out how to work around this problem.

Update, 2008-03-31. A reader has worked out a cunning solution:

Y'all can make white pieces by drawing a "black" piece in white, then overlaying that with a "white" piece in black.

I've given this thought a try and written upwards the results. Here's the knight at square c3 — a definite improvement!

White knight on a black square

LaTeX

Things I learned while writing this article: the skak package does chess with LaTeX.

My thanks Ivan Uemlianin for pointing out that I'd neglected to mention LaTeX as a suitable chess position type-setter, as shown in his blog. LaTeX has been high on my list of things I really ought to learn about for well over a decade, and I simply wasn't aware it could practice this.

Conclusions

This article has shown, again, the merits of scripting graphics. We've also seen that an interpreted language has much to offer in this area, allowing us to query and shape images dynamically, effectively bridging the gap betwixt our program and a GUI driven graphics parcel.

The Python Imaging Library has shown itself capable of working with shapes, colours, text and fonts. It's a great tool.

We've not done so well at our motivating job, of drawing a chess position. As I said at the starting time, if that'due south why you're hither I'd suggest taking some other look at the alternatives.

The subtext of this article is platform-dependence. I don't know if the Unicode + CSS combination works in your browser or feed-reader, or indeed whatever other user amanuensis; it depends on font contents and CSS rendering.

[PNG now!]

The "P" in PNG stands for "Portable", and I would hope you lot can run into the IMGs, which all source from PNGs — all except the off-site JPEG, another well supported format.

[FreeType now!]

One reason I similar Python and writing most Python is its platform independence. Linux, Windows, OS X etc. — we can all run the same code. For this particular application, that's less true. For a kickoff, you'll demand to install the not-standard PIL module. Then at that place's the filename case-insensitivity (which is easy to work around, but nonetheless an embarrassment). The capabilities of PIL itself depend on the presence of other thirdparty libraries: to become you going with this article you'll need libpng, which in turn depends on zlib, and FreeType, which again depends on zlib. On my Linux machine Image.show didn't piece of work until I'd installed fifteen from source, and that source needed some tweaking earlier it would build. So the code in this commodity is just portable once you've suitably prepared your platform — that is, the lawmaking isn't really portable!

Source: https://wordaligned.org/articles/drawing-chess-positions.html

Posted by: mezadogese.blogspot.com

0 Response to "How To Draw A Chess Board Using C++"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel