I’ve added in some better AI functionality. In addition to version 0.4’s random AI, which just picks any random legal move, there is now a defense AI and an offense AI.
Defense AI’s GetMove() Priority
- Only make a “protected move” — don’t move a piece to a space where the enemy can capture it on the next turn.
- Put enemy king in check if possible.
- If a piece is in danger of being captured by the enemy next turn, move it to a safe place. There is a priority mechanism here — the AI will move its queen to safety before moving a pawn to safety, for instance.
- Pick a protected move that captures another piece. The same priority mechanism applies — capture the queen or other high-value piece before capturing a pawn.
- If no moves from prior steps, pick any remaining protected move.
- If no protected moves, revert to random AI and pick any legal move.
The offense AI is the same as the defense AI, except steps 3 and 4 are reversed. In other words, it will ignore its own pieces’ safety if there is an opportunity to harm the enemy. Maybe it should be called “berserk” mode. 🙂
In expanding the ChessAI module, I took the opportunity to play around with inheritance in Python. The ChessAI_defense class extends the ChessAI_random class, while the ChessAI_offense class extends ChessAI_defense. They each have a GetMove() functions, so I don’t have to worry which class my AI object is from when I need to get a move from it in the main game loop.
I also created a sideline program called PythonChessAIStats.py. Since I can pit one AI vs. a different AI, I thought it would be cool to see how well they do versus each other. This could be used in the future to rank new AI approaches. The AIs all rely on random numbers to some extent, so in order to make the tests repeatable, the script reads in numbers from a file and uses them to seed Python’s random number generator. (I used RANDOM.ORG to generate 100 random seeds.) Sometimes the AI vs. AI won’t end (they just end up moving the same pieces back and forth), so I end the game after 200 turns and call it a tie if no one is in checkmate yet.
Here’s a summary of some trials I did (X = no trial):
|Win – Loss – Tie||AI Opponent (black)|
|AI Player (white)||Random AI||Defense AI||Offense AI|
|Random AI||X||0 – 78 – 22||0 – 81 – 19|
|Defense AI||X||32 – 44 – 24||21 – 47 – 32|
|Offense AI||X||32 – 44 – 24||21 – 47 – 32|
The new AIs are clearly better than just randomly moving about, so that’s good. Interesting that numbers for Defense vs. Defense are the same as for Offense vs. Defense….
This version of Python Chess also has better command line parsing. I found out about Python’s OptionParser class and used it. Both PythonChessMain.py and PythonChessAIStats.py can be run from the command line with a number of options — the -h option should display all of the other available options and usage.
I tried to make a standalone windows executable using py2exe (there are a couple of scripts in the .zip file that try to do that), which is supposed to wrap up the code along with all of the required libraries and the python interpreter into an executable, so it can be run on a Windows machine without the user having to have everything pre-installed. Would be useful for producing a game for the masses, but it wouldn’t work for me. The script runs and builds an executable, but it crashes when I try to run it. I’ll have to investigate further.