DEV Community

Cover image for Writing a chess game in python [Day 1]
rinaarts
rinaarts

Posted on

Writing a chess game in python [Day 1]

We're in lockdown due to COVID-19, and it's a holiday (no school, not even zoom). So I decided to take a week off work and wind down, spend some time with the kids. And really, what better use of my time than to implement a small code project with my son? He's studying python at school, but they haven't gotten many classes in (remember COVID-19?) and aren't much past print, this could be an excellent opportunity for him to up his game and I get to write a cool blog-post series!!! Total win-win situation.

He's been into chess lately, so I asked if he wanted to implement a textual chess game and he seemed genuinely excited (pretty sure he was not faking it).

In this series I'll be going through how we implemented the game day by day. Remember that this is a 12 year old and I'm trying to use this as a teaching opportunity, I promise you by the end we'll have everything implemented with well structured and nicely optimized code, but I chose to start with a naive approach, arriving at conclusions on how to do things better together instead of me applying everything I already know upfront. I'm pretty cerntain we will end up with an undo/redo/replay game using command pattern, I know it even if he doesn't yet. Anyway, seeing the process is totally part of the fun.

I hope you enjoy this series, now let's dive in!


The first thing I did was to ask him what the basic building blocks of a chess game would be. He said that we should probably start with the board. As I mentioned, he'd learned print so we tried to "brute force" the board by printing lines and columns:

print("-------------------------")
print("|  |  |  |  |  |  |  |  |  |")
Enter fullscreen mode Exit fullscreen mode

Not too bad:

-------------------------
|  |  |  |  |  |  |  |  |  |
Enter fullscreen mode Exit fullscreen mode

Apparently he already new about string duplication with *, and about using the end parameter so we changed it to:

print("-"*25)
print("|  "*8, end="|\n")
Enter fullscreen mode Exit fullscreen mode

The code was prettier, but the output was still only one row! We needed 8. He already knew about loops, but didn't have experience with python syntax so I helped him out there and we came up with:

for x in range(8):
  print("-"*25)
  print("|  "*8, end="|\n")
Enter fullscreen mode Exit fullscreen mode

We noticed we were missing the closing row so we added:

print("-"*25)
Enter fullscreen mode Exit fullscreen mode

after the loop.
Look! We have a board.

-------------------------
|  |  |  |  |  |  |  |  |
-------------------------
|  |  |  |  |  |  |  |  |
-------------------------
|  |  |  |  |  |  |  |  |
-------------------------
|  |  |  |  |  |  |  |  |
-------------------------
|  |  |  |  |  |  |  |  |
-------------------------
|  |  |  |  |  |  |  |  |
-------------------------
|  |  |  |  |  |  |  |  |
-------------------------
|  |  |  |  |  |  |  |  |
-------------------------
Enter fullscreen mode Exit fullscreen mode

OK, this is nice - but if we want to be able to actually play, we need some kind of data structure to store information about the board status.

12yo knows about arrays, but reasoning about a two dimensional array was a bit of a jump, so I described it as a list of lists and that worked. So here's the code we started writing:

board = []
for i in range(8):
  row = []
  for j in range(8):
    row.append(None)
  board.append(row)
Enter fullscreen mode Exit fullscreen mode

At this point I wasn't sure if this would be the best teaching approach - but I just couldn't leave those awful nested for loops as they were, so I taught him this shorthand list comprehension:

board = [[None]*8]*8
Enter fullscreen mode Exit fullscreen mode

I started explaining what I was doing here, but turns out since he was already familiar with this type of syntax from strings - he just understood what was going on (lucky me!).
Now we had to change the way we print the board to... actually print the board instead of hard-coded strings.
We defined the following method:

def print_board(board):
  for row in board:
    print("-"*25)
    for column in row:
       print("|{}".format(column), end="")
    print("|")
  print("-"*25)

board = [[None]*8]*8

print_board(board)
Enter fullscreen mode Exit fullscreen mode

Output:

-------------------------
|None|None|None|None|None|None|None|None|
-------------------------
|None|None|None|None|None|None|None|None|
-------------------------
|None|None|None|None|None|None|None|None|
-------------------------
|None|None|None|None|None|None|None|None|
-------------------------
|None|None|None|None|None|None|None|None|
-------------------------
|None|None|None|None|None|None|None|None|
-------------------------
|None|None|None|None|None|None|None|None|
-------------------------
|None|None|None|None|None|None|None|None|
-------------------------
Enter fullscreen mode Exit fullscreen mode

OK... let's add the pieces onto the board.
We decided to use the standard one letter for each piece, and add the color W or B as a prefix.
We added:

board[0] = ["BR","BN","BB","BQ","BK","BB","BN","BR"]
board[1] = ["BP","BP","BP","BP","BP","BP","BP","BP"]
board[6] = ["BP","BP","BP","BP","BP","BP","BP","BP"]
board[7] = ["BR","BN","BB","BQ","BK","BB","BN","BR"]
Enter fullscreen mode Exit fullscreen mode

Output now is totally out of balance:

-------------------------
|BR|BN|BB|BQ|BK|BB|BN|BR|
-------------------------
|BP|BP|BP|BP|BP|BP|BP|BP|
-------------------------
|None|None|None|None|None|None|None|None|
-------------------------
|None|None|None|None|None|None|None|None|
-------------------------
|None|None|None|None|None|None|None|None|
-------------------------
|None|None|None|None|None|None|None|None|
-------------------------
|BP|BP|BP|BP|BP|BP|BP|BP|
-------------------------
|BR|BN|BB|BQ|BK|BB|BN|BR|
-------------------------
Enter fullscreen mode Exit fullscreen mode

12yo pointed out we should have row numbers and column letters, so we added those and changed the None value to two whitespaces so it would line up nicely. After a few tweaks we landed on:

def print_board(board):
    row_number = 8
    print("  ", end="")
    print(" ----"*8)
    for row in board:
        print(row_number, end=" ")
        row_number -= 1
        for cell in row:
            print("| {} ".format(cell), end="")
        print("|")
        print("  ", end="")
        print(" ----"*8)
    print("  ", end="")
    for letter in ['a','b','c','d','e','f','g','h']:
        print("  {}  ".format(letter), end="")
    print("")

board = [["  "]*8]*8
board[0] = ["BR","BN","BB","BQ","BK","BB","BN","BR"]
board[1] = ["BP","BP","BP","BP","BP","BP","BP","BP"]
board[6] = ["BP","BP","BP","BP","BP","BP","BP","BP"]
board[7] = ["BR","BN","BB","BQ","BK","BB","BN","BR"]

print_board(board)
Enter fullscreen mode Exit fullscreen mode

Output:

   ---- ---- ---- ---- ---- ---- ---- ----
8 | BR | BN | BB | BQ | BK | BB | BN | BR |
   ---- ---- ---- ---- ---- ---- ---- ----
7 | BP | BP | BP | BP | BP | BP | BP | BP |
   ---- ---- ---- ---- ---- ---- ---- ----
6 |    |    |    |    |    |    |    |    |
   ---- ---- ---- ---- ---- ---- ---- ----
5 |    |    |    |    |    |    |    |    |
   ---- ---- ---- ---- ---- ---- ---- ----
4 |    |    |    |    |    |    |    |    |
   ---- ---- ---- ---- ---- ---- ---- ----
3 |    |    |    |    |    |    |    |    |
   ---- ---- ---- ---- ---- ---- ---- ----
2 | BP | BP | BP | BP | BP | BP | BP | BP |
   ---- ---- ---- ---- ---- ---- ---- ----
1 | BR | BN | BB | BQ | BK | BB | BN | BR |
   ---- ---- ---- ---- ---- ---- ---- ----
    a    b    c    d    e    f    g    h  
Enter fullscreen mode Exit fullscreen mode

And that was it for the day.


Tune in next time to see how we're doing!

Oldest comments (0)