Chess Engine¶
Description¶
The chess engine is an interface to any chess AI chosen. This project uses sunfish by thomasahle as the chess AI of choice. The chess engine spins up an instance of the sunfish program, and then sends the users plays to the AI. The AI then responds with what it decides should be the computer/robots move. To do this, some small modifications had to be made to the sunfish source code:
- Add a wait loop for the command queue to be filled with the user’s response. Inform the engine if the move is invalid or not.
pass_number = 1 # We set the first pass number before entering the loop
while move not in pos.gen_moves():
if pass_number > 1: # if on the second pass, the previous must've been invalid
valid_queue.put(0) # report to engine that the input was invalid
command = command_queue.get(block=True) # get a command from engine if available
match = re.match('([a-h][1-8])'*2, command)
if match:
move = parse(match.group(1)), parse(match.group(2))
else:
# Inform the user when invalid input (e.g. "help") is entered
print("Please enter a move like g8f6")
pass_number += 1
- Inform the game engine if the user’s move caused them to win.
# After our move we rotate the board and print it again.
# This allows us to see the effect of our move.
print_pos(pos.rotate())
if pos.score <= -MATE_LOWER:
print("You won")
valid_queue.put(1) # inform engine that the move was accepted and user won
break
- Send whether the computer won or not. Also, send the computers move back to the chess engine through a seperate queue.
print("Checkmate!")
valid_queue.put(2) # inform engine that the move was accepted and computer won
else:
valid_queue.put(3) # inform engine that the move was accepted and sunfish will reply
# The black player moves from a rotated position, so we have to
# 'back rotate' the move before printing it.
computer_move = render(119-move[0]) + render(119-move[1])
reply_queue.put(computer_move, block=True) # reply to engine
print("My move:", computer_move)
pos = pos.move(move)
These minimal changes mean that any AI that takes a users input as a chess command (e.g. ‘a2a4’) can be modified with minimal code to work with our chess engine.
Design¶
The CE (chess engine) is written by the team, it can provide all the chess functionality needed.
Tip
Lower case letters represent black pieces (p,r,n,b,k,q), and upper case letters represent white pieces (P,R,N,B,K,Q). This follows the model used in sunfish.
The BWE matrix provided by the Perception module is taken. This Black-White-Empty
matrix is simply a list of strings which represent each square on the board. The elements on the list correspond to the positions on the board as [A8, B7, C7, ..., F1, G1, H1]
. For the image below, this would be: ['B','B','B',...,'W','W','W']
.
This matrix is compared with an internally stored matrix in the CE. This means the CE can understand:
- where the piece moved from
- where it moved to
and construct a chess command from it. This BWE matrix is of course checked for logical inconsistencies by measuring the change in number of black, white or empty squares in a single turn. Now that a potential chess move has been obtained, it is added to the command queue, a shared resource that both the CE and the chess AI (sunfish) have access to.
Hint
Moving pawn piece A2 to A4 at the beginning of the game would require command ‘a2a4’
This move is trialled internally on the AI, and it responds with a number of options.
- The move is invalid, in which case reporting of an illegal move back to the user needs to be carried out
- The move is valid and has caused the user to win, in which case the user should be told
- The move is valid and the computer responds with move (which may be a checkmate)
If the computer replies with a move, it will put it on the reply queue which is shared with the CE. The CE must now understand exactly what the chess AI is asking of it. It will have received a command such as ‘n, b1c4’ (i.e. knight b1 to c4). The CE then splits this command into the start and end position of the move. It then converts these positions into indices, which it uses to search its internally stored board for the type of piece that is moving. In addition it checks if there is already a piece existing at the end position.
The CE first updates its internal board to remember the move the computer just made, then returns the following information to the caller (function):
- Firstly, if there is a piece to be killed, its location and type.
- Then, the start location of the piece moving, and its type.
- Finally, the end location of the piece moving.
Limitations¶
The chess engine has limitations, which could be implemented in later versions:
- No support for pawn piece conversion (bringing pieces back onto the board).
- No support for special chess moves such as castling.
Implementation¶
Example usage:
from chess.engine import ChessEngine
bwe_list # Get BWE from the camera
engine = ChessEngine()
code, result = engine.input_bwe(bwe_list)
if code == -1:
print("There was a problem with the BWE matrix")
elif code == 0:
print("Invalid move by user: ", result)
elif code == 1:
print("The user won the game")
elif code == 2:
print("The computer has won the game", result)
franka_move(result)
franka_celebrate()
elif code == 3:
print("The computer's move is: ", result)
franka_move(result)
else:
print("Error code not recognised: ", code)
Documentation:
This engine is designed to interface with the modified sunfish file to provide a specialised interface between the other modules in this project and the chess logic underneath.
-
class
chess.engine.
ChessEngine
(debug=False, suppress_sunfish=True)[source]¶ Engine that manages communication between main program and chess AI Sunfish (running in separate process).
It’s main purpose is to take a BWE matrix as the user’s potential move and provide an analysis of this move by either reporting back its invalidity or the AI’s response.
-
input_bwe
(bwe)[source]¶ Takes in the latest BWE and tries to input that to Sunfish AI.
- Returns:
- code : int
-1
, BWE was invalid0
, move was invalid1
, user won the game with move2
, computer won the game with checkmate3
, computer did not win and
- result
Depending on
code
, this will vary:-1
, None0
, Move that was invalid as string1
, None2
, Move that computer is playing to win as tuple3
, Move that computer is playing as tuple
Computer move is a list of tuples in the form:
[ (piece_to_move, move) ]
, or[ (piece_to_kill, location), (piece_to_move, move) ]
where,
- Pieces are single character strings.
- Locations are two character strings e.g. ‘a2’
- Moves are 4 character strings e.g. ‘a2a4’
-
-
class
chess.engine.
ChessState
(debug=False)[source]¶ Class holding the ongoing state of the chess board.
-
compare_bwe
(new_bwe)[source]¶ Takes a BWE list and compares it to the existing game state. Return tuple of
(move_from, move_to)
indices for the single move that’s detected. Does not verify if move is a legal one.
-
convert_to_index
(chess_pos)[source]¶ Takes an board position (e.g. ‘a2’) and converts to the corresponding board index ( 0-63).
-
convert_to_pos
(index_num)[source]¶ Takes an board index (0-63) and converts to the corresponding board position (e.g. ‘a2’).
-