Modelling Chess Positions
April 21, 2013 5 Comments
Practically everything in programming involves making a representation of something from real life or someone’s imagination and visualising that model to the user of the program. In this tutorial we are going to put together a very basic model of the pieces on a chess board. A chess board looks like this:
![]()
Which is to say, the board itself has 8 rows (which run horizontally) and 8 columns (which run vertically). There are 8 pawns for each of white and black and each of white and black have an additional 8 pieces (capital pieces). A prime contender for representing the board is a list of lists, with each list corresponding to a row and column respectively. The usual way to represent the pieces is to use capital letters for white pieces (RNBKQP – Rook, kNight, Bishop, King, Queen, Pawn) and corresponding lower case letters for the black pieces. Thus, to represent the white rook in the lower left hand corner one would have the letter R in the first column of the first row of your list of lists.
Aside: Lists of lists
Lists can have any elements in them. In particular, a list can contain lists as its elements.
>>> a = [] >>> a.append(range(8)) # range returns a list >>> a [[0, 1, 2, 3, 4, 5, 6, 7]]
Here, the double square brackets [[ ]] indicate two levels of list. This is clearer if we add another element:
>>> a.append(range(4)) >>> a [[0, 1, 2, 3, 4, 5, 6, 7], [0, 1, 2, 3]]
The two elements in the list are separated by a comma:
>>> a[0] # remember the first element of a list is at index 0 not 1. [0, 1, 2, 3, 4, 5, 6, 7] >>> a[1] [0, 1, 2, 3]
There is a shortcut notation for accessing an element of an element of a list of lists [sic]:
>>> a[0][7] 7 >>> a[1][3] 3
So, a[0] refers to the first element of the list a, which is, itself, a list. But a[0][7] refers to the eighth element of that list.
Making our Model
With this knowledge we can make a model of the initial set up of a chess board using a 8×8 list of lists, with each element representing a square on the chess board.* The first row is the black pieces: [‘r’, ‘n’, ‘b’, ‘q’, ‘k’, ‘b’, ‘n’, ‘r’], the second row is the black pawns: [‘p’, ‘p’, ‘p’, ‘p’, ‘p’, ‘p’, ‘p’, ‘p’], then there are four rows of empty squares [‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘] followed by white pawns [‘P’, ‘P’, ‘P’, ‘P’, ‘P’, ‘P’, ‘P’, ‘P’], then white pieces [‘R’, ‘N’, ‘B’, ‘Q’, ‘K’, ‘B’, ‘N’, ‘R’].
Exercise: check there are 8 rows, each with 8 elements.
So, let’s make a class to hold this representation:
EMPTY_SQUARE = " "
class Model(object):
def __init__(self):
'''create a chess board with pieces positioned for a new game
row ordering is reversed from normal chess representations
but corresponds to a top left screen coordinate
'''
self.board = []
pawn_base = "P "*8
white_pieces = "R N B Q K B N R"
white_pawns = pawn_base.strip()
black_pieces = white_pieces.lower()
black_pawns = white_pawns.lower()
self.board.append(black_pieces.split(" "))
self.board.append(black_pawns.split(" "))
for i in range(4):
self.board.append([EMPTY_SQUARE]*8)
self.board.append(white_pawns.split(" "))
self.board.append(white_pieces.split(" "))
Each time the Model class is instantiated (that is whenever you see something like a = Model()) the instance will be created with an attribute called self.board which has an initial chess position represented in it.
Exercise: make an instance of Model() and print its attribute board.
Viewing the Model
At the moment it is hard to know whether our Model is properly representing a chess board. What we need is a way to view a given board. This could just be a view function, but, since there aren’t enough classes in the world already, I am going to make it a class:
column_reference = "a b c d e f g h".split(" ")
class View(object):
def __init__(self):
pass
def display(self, board):
print("%s: %s"%(" ", column_reference))
print("-"*50)
for i, row in enumerate(board):
row_marker = 8-i
print("%s: %s"%(row_marker, row))
So, let’s create a model and a view, then pass data from the model to the display method of the view:
>>> m = Model() #instantiate a model >>> v = View() #instantiate a view >>> v.display(m.board) # pass the model's data to the view's display method : ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'] -------------------------------------------------- 8: ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r'] 7: ['p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'] 6: [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '] 5: [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '] 4: [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '] 3: [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '] 2: ['P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'] 1: ['R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R']
I have prettied it up a little by adding coordinate headings (a-h and 1-8). This is a recognised chess notation for identifying board positions and making moves. So, to move the white pawn in front of the king two spaces forward, one writes e2-e4 – that is, e2 (the piece’s starting square) – (to) e4 (the destination square). These coordinates have the same concept as the ones we met in our earlier tutorials (eg here) except that they only have 8 positions in each axis (as opposed to hundreds of pixels in a window), and they start from the lower, rather than the upper left.
Controlling the View and Model
All we need now is something which allows you to feed back information to the model, so that it can change, and for those changes to be displayed back to us. The bit which fits between the model and view, controlling the interactions between them is called (not surprisingly) the Controller. First though, I want to add something for the Model to do – a move piece method, along with a position class (which is actually superfluous – I have only introduced because I wanted to refer to coordinates as .i and .j)
#/usr/bin/python2.7
'''
Representing a chess set in Python
Brendan Scott
19 April 2013
Dark square on a1
'''
#column_reference = "1 2 3 4 5 6 7 8".split(" ")
column_reference = "a b c d e f g h".split(" ")
EMPTY_SQUARE = " "
class Model(object):
def __init__(self):
'''create a chess board with pieces positioned for a new game
row ordering is reversed from normal chess representations
but corresponds to a top left screen coordinate
'''
self.board = []
pawn_base = "P "*8
white_pieces = "R N B Q K B N R"
white_pawns = pawn_base.strip()
black_pieces = white_pieces.lower()
black_pawns = white_pawns.lower()
self.board.append(black_pieces.split(" "))
self.board.append(black_pawns.split(" "))
for i in range(4):
self.board.append([EMPTY_SQUARE]*8)
self.board.append(white_pawns.split(" "))
self.board.append(white_pieces.split(" "))
def move(self, start, destination):
''' move a piece located at the start location to destination
(each an instance of BoardLocation)
Does not check whether the move is valid for the piece
'''
# error checking
for c in [start, destination]: # check coordinates are valid
if c.i > 7 or c.j > 7 or c.i <0 or c.j <0:
return
if start.i == destination.i and start.j == destination.j: # don't move to same location
return
if self.board[start.i][start.j] == EMPTY_SQUARE: #nothing to move
return
f = self.board[start.i][start.j]
self.board[destination.i][destination.j] = f
self.board[start.i][start.j] = EMPTY_SQUARE
class BoardLocation(object):
def __init__(self, i, j):
self.i = i
self.j = j
class View(object):
def __init__(self):
pass
def display(self, board):
print("%s: %s"%(" ", column_reference))
print("-"*50)
for i, row in enumerate(board):
row_marker = 8-i
print("%s: %s"%(row_marker, row))
class Controller(object):
def __init__(self):
self.model = Model()
self.view = View()
def run(self):
''' main loop'''
while True:
self.view.display(self.model.board)
move = raw_input("move (eg e2-e4) ")
move = move.lower()
if move =="q":
break
if move =="":
move = "e2-e4"
start, destination = self.parse_move(move)
self.model.move(start, destination)
def parse_move(self, move):
''' Very basic move parsing
given a move in the form ab-cd where a and c are in [a,b,c,d,e,f,g,h]
and b and d are numbers from 1 to 8 convert into BoardLocation instances
for start (ab) and destination (cd)
Does not deal with castling (ie 0-0 or 0-0-0) or bare pawn moves (e4)
or capture d4xe5 etc
No error checking! very fragile
'''
s, d = move.split("-")
i = 8- int(s[-1]) # board is "upside down" with reference to the representation
j = column_reference.index(s[0])
start = BoardLocation(i, j)
i = 8- int(d[-1])
j= column_reference.index(d[0])
destination = BoardLocation(i, j)
return start, destination
if __name__=="__main__":
C = Controller()
C.run()
Now, if you run this from a command line, it should allow you to move the pieces around on the board using the e2-e4 notation. It doesn’t play chess – or even checks that the moves are valid, but it does record the result of them.
Homework: Why did we go to all this trouble to separate the model from the view?
* Note: another way of doing this is to keep a dictionary of pieces and their locations…



















