Kool AI Chess - A command line Chess program using Python - Player vs Computer
For my Code Institute Portfolio Project 3,
I would like to implement a Chess Program in Python
in order to play Chess with a Computer opponent.
In my search for a suitable algorithm I came across this 476-line BASIC PROGRAM by DEAN MENEZES
Let me reiterate: the basis of my project is a Chess Program written in
BASIC (Beginners' All-purpose Symbolic Instruction Code) which is available here.
I found it amazing how Denezes has written such an highly interesting chess playing program in under 500 lines. My goal then is to convert Denezes' BASIC program to Python; moreover, to add castling and en passant chess moves so that the user can play a complete game of Chess against their Computer opponent, namely, Kool AI
To quote Boardgamegeek
Chess is a two-player, abstract strategy board game that represents medieval warfare on an 8x8 board with alternating light and dark squares. Opposing pieces, traditionally designated White and Black, are initially lined up on either side. Each type of piece has a unique form of movement and capturing occurs when a piece, via its movement, occupies the square of an opposing piece. Players take turns moving one of their pieces in an attempt to capture, attack, defend, or develop their positions.
Chess games can end in checkmate, resignation, or one of several types of draws.
Chess is one of the most popular games in the world, played by millions of people worldwide at home, in clubs, online, by correspondence, and in tournaments.
To quote Wikipedia
Chess is an abstract strategy game that involves no hidden information and no elements of chance. It is played on a chessboard with 64 squares arranged in an eight-by-eight grid.
At the start, each player controls sixteen pieces:
one king, one queen, two rooks, two bishops, two knights, and eight pawns.
White moves first, followed by Black. The game is won by checkmating the opponent's king,
i.e. threatening it with inescapable capture.
There are also several ways a game can end in a draw.
For the rules and further information on the game of Chess please refer to the following Wikipedia articles:
Thank You.
In the context of this game the user is referred to as The Player. So I will describe the user in this manner in the following sections.
The Player goes first and is prompted to do so. The Player is designated White however seeing that this is a monochrome console game,
the Player's pieces are depicted as lowercase letters at the bottom of the board.
Kool AI, your Computer opponent will go second. The Computer is designated Black.
The Computer's pieces are depicted as uppercase letters at the top of the board.
From this point onwards each, that is, the Player and the Computer will play their corresponding moves until:
- The Player beats Kool AI ! That is, Checkmate! The Player has Won!
- Kool AI recognises it cannot win therefore it resigns. The Player has Won!
- Kool AI beats the Player and informs the Player that the Player is in Checkmate. Kool AI has Won!
- The Player resigns because of one of the following reasons:
-
- The Player can foresee that one will be in Checkmate.
-
- The Player realises that the game is Stalemate.
(Stalemate is a situation in Chess where the player whose turn it is to move is not in Check and has no legal move.)
- The Player realises that the game is Stalemate.
-
- The Player realises that the game is a Draw.
-
- Or the Player chooses to no longer continue the game.
The chessboard has the following coordinates:
The chessboard at the beginning of the game is shown as:
Therefore, the form of how one enters a chess move is of the form <FromSquare><ToSquare> e.g. d2d4.
That is, pawn (p) from square d2 to square d4.
Another example, would be g1f3.
That is, knight (n) from square g1 to square f3.
From the outset a prompt (e.g. e2e4) is displayed reminding the Player of the format of a Chess move.
Each piece has a letter. Starting from the top of the board:
- R for Rook
- N for Knight
- B for Bishop
- Q for Queen
- K for King
- P for Pawn
The Computer's pieces (Black) are depicted as uppercase, capital letters - R, N, B, Q, K, P
The Player's pieces (White) are depicted as lowercase letters - r, n, b, q, k, p
Please Note: A description of both the Player's and Computer's moves is always shown.
The descriptions will be in these formats:
Player moves g1-f3 Piece: Pawn
Computer moves g7-g6 Piece: Pawn
Therefore, the description will always show:
- Who played
- The From Square
- The To Square
- The Piece Played
The program will prompt the Player then await the Player's input
The Player's input needs to be a four-character string which uses files-first Chess Algebraic notation
For example, to move one's pawn from square e2 to e4, enter e2-e4
The program will then respond with a message such as
Checking Player move for e2-e4 Piece: Pawn
Since the Player has moved the pawn from square e2 to e4 the program will display the following board:
Kool AI will inform the Player that it is Thinking - That is, evaluating its next move:
Please note: it may take up to 4 seconds for Kool AI to respond. Be patient :)
In this scenario, Kool AI has responded with e7e6
That is, Pawn from square E7 to E6
Please Note: A description of both the Player's and Computer's moves is always shown
The Player's input will always be validated. Here are some examples:
- When it cannot understand the input - that is, it does not fit the Chess move format
In this scenario, the Player entered the word, hello
- Trying to play a Blank Square
- Trying to play an opponent's piece
- Trying to play an illegal move for a piece
This is the general catchall response. Kool AI's algorithm will examine the Player's move against all the possible moves for the chosen piece. If the Player's move does not appear in the list of all possible moves it will display an Illegal Move message. In these scenarios it would be up to the user to see why such a move cannot be played. For example:
In this case, a rook cannot pass through pieces. It is blocked by a pawn.
Here is an example of Check:
Resignation in Chess is the player conceding the game to their opponent, that is, to acknowledge defeat.
Resignation immediately ends the game.
Please note however:
- Kool AI's algorithm will score each of its own potential moves before its play and if the score of a move is too low it will resign.
- Unfortunately, my program is not smart enough to determine whether a game is Stalemate or a Draw;
so it relies on the human user to end the game by entering 'R' to resign. - Also, I am a novice chess player. So in writing this program, there is the distinct possibility that my program may declare Checkmate against the human opponent when in fact, it is not!
(Personally, throughout my testing I have not come across such a scenario!)
Therefore, in considering the possibility of such a scenario; even after declaring Checkmate; I leave it up to the user to resign.
That is, my program does not force the end of the game - the player can play on!
- As a user I want to be welcomed by a start screen with the name of the game.
- As a user I want to be able to enjoy a game of Chess against a Computer Opponent
- As a user I want to know whether I am entering correct Chess moves. Moreover, if not,
then an explanation of why a move is incorrect ought to be displayed. - As a user, I want to know whether I have placed the Computer in Check.
- As a user, I want to know whether I have placed the Computer in Checkmate. That is, have I won?
- As a user, I want to know whether I am in Check.
- As a user, I want to know whether the Computer has placed me in Checkmate. That is, have I lost?
- As a user, I want the option to Resign when realising that I cannot beat my opponent or I no longer want to continue the game.
The game begins with this view
If for example, the user plays e2e4
Then the computer may respond with e7e5
- The ability to switch sides
- Undo/Redo ability when playing moves
- Saving board positions during the game
- A Colour Chessboard using a libary such as Colorama
The Chessboard and Pieces as designed by Dean Menezes had the following values:
-500,"R",-270,"N",-300,"B",-900,"Q",-7500,"K",-300,"B",-270,"N",-500,"R"
-100,"P",-100,"P",-100,"P",-100,"P",-100,"P",-100,"P",-100,"P",-100,"P"
0," ",0," ",0," ",0," ",0," ",0," ",0," ",0," "
0," ",0," ",0," ",0," ",0," ",0," ",0," ",0," "
0," ",0," ",0," ",0," ",0," ",0," ",0," ",0," "
0," ",0," ",0," ",0," ",0," ",0," ",0," ",0," "
100,"P",100,"P",100,"P",100,"P",100,"P",100,"P",100,"P",100,"P"
500,"R",270,"N",300,"B",900,"Q",5000,"K",300,"B",270,"N",500,"R"
- Black Pieces at the top have negative values
- Each piece has a value followed by its letter
- Zero and Space show the empty squares
- Then the White Pieces are at the bottom with corresponding positive values
I chose not to use a 8X8 array with numerical indices for the following reason.
In the Chess World, this is how a chessboard is depicted:
The chessboard consists of eight files and eight ranks.
Columns are known as files and are labelled left to right with letters, a to h.
Rows are known as ranks and are numbered from the bottom of the board upwards, 1 to 8.
Therefore, rather than a 8X8 array with numerical indices; instead I chose to use a Python dictionary to reflect the above scheme.
The keys being a string e.g. "h8" for the square h8
Then each value would be a Piece Class instance of the form Piece(VALUE, LETTER, SIGN)
Therefore the Dictionary would look like this:
{a8:value, b8:value, ..., d1:None, ..., e1:None, a1:value, ..., h1:value}
Blank squares have the value None
Thereby if a variable for example board represents the chessboard, each square can be accessed using a string index.
For example to refer to square "h3 I can use the code
square = board["h3"]
In order to incorporate Object Oriented programming I have used three classes in this program.
- Piece
- Game
- CustomException(Exception)
This is the Class that is the Base Classes for all the Chess Pieces.
As a basis I adopted the way that X.S. had implemented the Pieces' Class in this code
In my version the rationale is as follows:
Attributes:
-----------
piece : str
Each piece is depicted by a letter which represents
the name of a piece as following :-
Pawn -> P
Rook -> R
Knight -> N
Bishop -> B
Queen -> Q
King -> K
sign (colour) : is depicted by a number
1 if the piece belongs to the Player i.e. white
-1 if the piece belongs to the Computer i.e. black
Each piece has
-
self.sign: -1 for the Computer (Black Pieces); 1 for the Player (White Pieces)
-
self.letter: P R N B Q K
-
self.value:
-
- Player:
-
- Pawn's value is 100
-
- Rook's value is 500
-
- Knight's value is 270
-
- Bishop's value is 300
-
- Queen's value is 900
-
- Note: The Player's King value is 5000 and the Computer's King value is -7500
-
- The Computer's values of each of the above is the negative equivalent i.e. -100, -500, -270, -300, -900
-
- The Kings' values differ as shown above
-
Note: Blank squares have a value and a sign of ZERO
-
print_string(self): This method returns a string description of each piece to describe which piece has been taken in a game
-
- "Pawn"
-
- "Rook"
-
- "Knight"
-
- "Bishop"
-
- "Queen"
-
- Note: King piece cannot be taken - So no 'print_string'
This class represents the status of the Chess Game. It is the main workspace of the program containing all the global flags, properties and variables related to the state of play of the Game.
So instead of global variables I use Class Variables of this Class to hold important values.
I have tested my program to the best of my ability however I am a novice Chess Player.
So just in case my program does some absurd illegal move such as trying to take a king or capturing a piece of its own colour;
I have added a Try-Except method to catch a Custom Exception which will be generated if some strange chess move occurs.
Hopefully this will never happen! :)
In order to avoid the usage of magic numbers I created this file to hold all the constants needed for this program. So if this program needs to be amended, this would me the main place to look at for making adjustments of major values.
The exception is that the function showboard in run.py uses numbers and strings specific to usage on the ANSI terminal used for this project. So I suggest an entire new function will need to be written if displaying the chessboard on a different display media.
-
Passed the code through the PEP8 linter and confirmed there are no problems
-
Carried out tests of the program on both the local terminal and the Code Institute Heroku terminal
At the top level of the program I have added the following try-except :-
def main():
try:
main_part2()
except CustomException as error:
print(error)
handle_internal_error()
quit()
except Exception as error:
raise error
That way, if there is some logic error that I have not anticipated or some internal error occurs;
it would be caught here and a suitable message would be printed.
The message will be of the form:
Internal Error: <The Error Message>
Computer resigns due to an internal error
Please investigate
Thank You For Playing
Goodbye
Initially when I defined my 'Game' class I thought I could call the following 'initialise_class_variables' function to initialise all the relevant Class Variables
class Game:
"""
"""
def initialise_class_variables():
print("Initialise")
# the number of valid moves found for chosen piece
num_moves = -1
...
However when I tried to access a Class Variable defined that way I got
print(Game.num_moves)
^^^^^^^^^^^^^^
AttributeError: type object 'Game' has no attribute 'num_moves'
Solution:
I know the error has something to do with scoping however to resolve this issue, I removed the function 'initialise_class_variables' and defined all my Class Variables directly beneath the 'Game' Class definition
class Game:
player_first_move = True
...
...
No unfixed bugs.
The project is deployed on Heroku. These are the steps in order to deploy on Heroku
-
Create Heroku account.
-
Create new project.
-
Go into settings -> Config Var and add the fallowing:
- key by the name of PORT with the value of 8000.
-
Include the following buildpacks:
- Heroku/python
- Heroku/nodejs
- Please note: the order is significant - the Python buildpack must appear before the NodeJs buildback.
One can use the mouse to drag the buildpacks into the correct order.
-
Regarding your project:
- create a Procfile with the following one line entry
web: node index.js
-
Then Deploy the project to GitHub with the following files included.
-
On Heroku for Deployment Method pick Github and find the repo with the project you want to deploy.
-
Pick which branch you want to deploy -- Generally this would be main
-
Click deploy and wait until the project is built.
-
Ensure there are no errors.
- Python3
- os - I use this library for the clear function in order to clear the console before displaying an updated chessboard.
- re - I use regular expressions in order to validate user input of chess moves.
- GitHub - for hosting the site
- Gitpod - for editing the files
- Heroku - for the deployment of the site
- Code Institute's GitHub full template - in order to run Python on Heroku
- Background image photo by Chris Burns on Unsplash.
- I would like to acknowledge asiask97's CLI Battleship game which shows how to present a Python program against a background image.
- Thanks to Code Institute for the template by which a terminal could be created; in order that my game could be displayed on a webpage.
- Thanks to Code Institute for the CI Python Linter which ensures that Python code complies with PEP 8
- Boardgamegeek for the explanation of the game of Chess
- Wikipedia for the explanation of the game of Chess
- The depiction of the chessboard with letters and numbers is from Naming Ranks and Files in Chess
- Flowchart Fun used to create the flowchart for the Logic Flow of the Program
- Miro used to create the flowchart for the Evaluation Algorithm
- I would like to thank my mentor Derek McAuley for his advice and guidance.
- I would like to acknowledge Dean Menezes, the author of the BASIC program on which my project is based on.
- I would like to acknowledge Rod Bird who also adopted Menezes' code.
I preferred and adopted Bird's display of the Chessboard. - I would like to acknowledge the Pythoneer X.S..
- X.S.'s article How to Code a Simple Chess Game in Python
- X.S.'s Pythonic style of coding can be seen in the following Chess Program.