Chess Simulator in Google Sheets
2024-02-22 • 5 min read
In this article I will walk through how I created a chess simulator in Google Sheets using a single (array)formula.
Demo
You can make a copy of the simulator from here. (File -> Make a copy)
Move a piece
To move a piece from [coord_x] to [coord_y], we type [coord_x][coord_y].
Remove a piece
To remove the piece in position [coord], we type x[coord].
Castling example
Here's an example of how we would perform castling.
Insert a piece
To insert a piece in position [coord], we type [coord]=[piece]. Below is an example where we insert a queen (♕) to promote a pawn (♙).
Note
This simulator merely executes the moves indicated by the user, it doesn't check for their validity.
Formula
The simulator is powered by the following formula inserted in cell B3
1. =ARRAYFORMULA(
2. LET(moves,B14,
3. coords,CHAR(SEQUENCE(1,8,97))&SEQUENCE(8,1,8,-1),
4. initial_board,{{"♜","♞","♝","♛","♚","♝","♞","♜"};
5. {"♟︎","♟︎","♟︎","♟︎","♟︎","♟︎","♟︎","♟︎"};
6. {"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" };
7. {"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" };
8. {"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" };
9. {"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" };
10. {"♙","♙","♙","♙","♙","♙","♙","♙"};
11. {"♖","♘","♗","♕","♔","♗","♘","♖"}},
12. UPDATE_POSITION,LAMBDA(board,coord,new,IF(coords=coord,new,board)),
13. GET_PIECE,LAMBDA(board,coord,TEXTJOIN(,,IF(coords=coord,board,))),
14. UPDATE_BOARD,LAMBDA(board,move,
15. LET(PATTERN,LAMBDA(re,REGEXMATCH(LOWER(move),re)),
16. from,LEFT(move,2),to,RIGHT(move,2),
17. IF(PATTERN("([a-h][1-8]){2}"),UPDATE_POSITION(UPDATE_POSITION(board,from,),to,GET_PIECE(board,from)),
18. IF(PATTERN("x[a-h][1-8]"),UPDATE_POSITION(board,to,),
19. IF(PATTERN("[a-h][1-8]=."),UPDATE_POSITION(board,from,RIGHT(move)),
20. board))))),
21. IF(moves="",initial_board,REDUCE(initial_board,SPLIT(moves,", "),UPDATE_BOARD))))
Where B14 is a sequence of moves, removals or insertions separated by a comma.
Explanation
1. Initializing the board coordinates and initial state
In line 3 we are initializing the coords array, which consists of the coordinates of the regular 8x8 chessboard.
=ARRAYFORMULA(CHAR(SEQUENCE(1,8,97))&SEQUENCE(8,1,8,-1))
In lines 4-11 we are declaring the constant initial_board in which we are storing the initial state of the chessboard.
={{"♜","♞","♝","♛","♚","♝","♞","♜"};
{"♟︎","♟︎","♟︎","♟︎","♟︎","♟︎","♟︎","♟︎"};
{"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" };
{"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" };
{"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" };
{"" ,"" ,"" ,"" ,"" ,"" ,"" ,"" };
{"♙","♙","♙","♙","♙","♙","♙","♙"};
{"♖","♘","♗","♕","♔","♗","♘","♖"}}
2. Defining the UPDATE_POSITION() and GET_PIECE() functions
In line 12 we are defining the UPDATE_POSITION(board,coord,new) function that given a board state board, a coordinate coordand a value (piece) new, it replaces the piece currently at position coord with the new piece while leaving the rest of the board unchanged.
=ARRAYFORMULA(
LET(coords,CHAR(SEQUENCE(1,8,97))&SEQUENCE(8,1,8,-1),
UPDATE_POSITION,LAMBDA(board,coord,new,IF(coords=coord,new,board)),
...)
In line 13 we are defining the GET_PIECE(board,coord) function that given a board state board and a coordinate coord it returns the piece at position coord.
=ARRAYFORMULA(
LET(coords,CHAR(SEQUENCE(1,8,97))&SEQUENCE(8,1,8,-1),
GET_PIECE,LAMBDA(board,coord,TEXTJOIN(,,IF(coords=coord,board,))),
...)
We will see these functions in action in the next paragraph.
3. Implementing the logic for updating the board
Everything we have done so far was just the setup process, now we can implement the actual logic for updating the board by using the auxiliary functions and variables declared before.
The entire logic of the simulator is in the UPDATE_BOARD(board,move) function, which is a function that takes as input a board state and a move and it executes the move.
Note
The word move in this context also includes removals and insertions.
The formula is composed of three IF statements, one for each possible action (movement, removal, insertion). To know which action the user intended, we analyze the structure of the move parameter.
Movement
First we check if the action intended is a movement, if it is, it will have the structure [start_coord][end_coord], which in regex is [a-h][1-8][a-h][1-8] or ([a-h][1-8]){2}
IF(PATTERN("([a-h][1-8]){2}"), ...)
If the action has this structure we update the board by moving the piece from the initial
position to the target position. This is performed in two steps: using the UPDATE_POSITION() function, first we remove the piece from the current
position. In this example, we will be moving the pawn from a2 to a4.
UPDATE_POSITION(board,from,)
Then, we insert the piece in the new position
UPDATE_POSITION(UPDATE_POSITION(board,from,),to,GET_PIECE(board,from))
To know which piece to insert, we used the GET_PIECE() function on the current position.
Removal
If the action is not a movement, we check if it's a removal, if it is, it will have the following structure: x[coord], which in regex is x[a-h][1-8]
IF(PATTERN("x[a-h][1-8]"), ...)
If it is a removal, we use the UPDATE_POSITION() function in the same way we have used it before
UPDATE_POSITION(board,to,)
Where to indicates the target coordinate.
Insertion
If the action is not a movement and it's not a removal, we check if it's an insertion, if it is, it will have the following structure [coord]=[piece], which in regex is [a-h][1-8]=.
IF(PATTERN("[a-h][1-8]=."), ...)
If it is an insertion, we use the UPDATE_POSITION() function in the same way we have used it before
UPDATE_POSITION(board,from,RIGHT(move))
Where from indicates the target coordinate.
If the action does not match any of the patterns mentioned above, it means that the user entered a move that's unrecognized by the simulator, so the function simply returns the current state of the board without updating it.
4. Iterating over each move
Now that we have defined a function that implements the logic for updating board, we just have to apply it to each move in the input. We can easily do this using the REDUCE function.
REDUCE(initial_board,SPLIT(moves,", "),UPDATE_BOARD)
Where moves is a string of moves separated by a comma.
And that's it! Thanks for reading. (: