Othello Game Implementation With AI Player
This is the Artificial Intelligence course final project in Shahid Beheshti University done by Morteza Kazemi and Shiva Zeymaran
Winter 2021
This project is a complete Othello game that has an intelligent agent to play with users. The project implements in python. The source file of code contains the following packages and python files:
--- src
|----- Agent
| |--- Heuristic
| |--- Minimax
| |--- Tree
|
|----- Genetics
| |--- Agent_vs_agent
| |--- Evolution
| |--- Gene
|
|----- Gui
| |--- Components
| |--- Othello_gui
|
|----- Logic
|--- Othello_logic
Phase one was about implementing the logic of the code and GUI. For this purpose, the “Logic” and “Gui” packages were implemented. The “Othello_logic.py” contains everything about the logic of the Othello game such as initializing the game board, checking valid movement requests, performing a special movement, and everything that we need to understand the end of the game and calculate the scores. It also considers the rules of the Othello game in every movement. The “Othello_gui.py”, as it’s clear from its name, contains everything about games’ graphical design using the “TKinter” library of python. This file has an implementation of displaying everything that the user can see when he/she runs the code and also it has a handler for valid clicks on board. The components that are shown in the final window are defined in the “Components.py”; Such as the gray board game, black and white disks, scoreboard, and the color of the player whose turn it is. The components are defined in this file and used to display in the “Othello_gui.py”.
Phase two was about the implementation of an intelligent agent to play and decide like a human.
The agent plays using Minimax with alpha-beta pruning to decide its next movement. Minimax is implemented as its traditional algorithm in “Minimax.py”; The extra parts added to the algorithm are as follows:
The utility of a state is the subtraction of AI player and human player scores(weighted summation of the player’s feature values). Feature weights were selected manually in this phase, but they are optimized through the learning algorithm in the third phase.
To have a deeper search in Minimax tree with the same response time, the width of the tree is reduced using the following strategy:
The three most promising actions(if there are not three actions, chooses as many as possible) causing greater utility values are selected to generate the next level Minimax tree nodes.
The proper depth that gives us good and logical results considering the response time is nine for this code, which was obtained by testing various numbers; Accordingly, the Minimax will dive into the game tree up to nine levels and then decide which action is the best one among all possible actions.
Each node of the game tree is an othello_logic by itself that keeps the special state of the game board. The structure of each Node is defined in the “Tree.py”.
To define a proper heuristic function, it’s important to extract proper features. Here we have nine features that are mapped to each position on the board; Such as, corners, edges, and 4*4 middle square with more details for each of them. To find the number of each feature for each state of the board, we should count the number of disks that are placed in each position and add it to the appropriate feature. Details of setting features are clear in the “Heuristic.py” and for more information, the comments and documentation are available.
The important thing about this part is to normalize the features at the end. It’s remarkable because features have different valid ranges since the number of corner positions, edge positions, etc. are different from each other.
Features have been normalized by dividing their values by normalization factors so that the ranges will become identical.
Phase three focused on evolution to reach the best estimation of weights for the feature vector. The first step was to define the structure of genes. Gene is a class that is implemented in “Gene.py”. Each gene has a list of weights, number of games it plays, number of games it wins or ties(winning has two scores for the winner and a tie has one score for each player), and the fitness of the gene. The fitness is calculated from the division of total wins by total games.
The movement logic for playing AI vs AI by using the Minimax algorithm is implemented in “Agent_vs_agent.py” making a few changes to the previous movement logic we had before.
The main part of Genetics implementation is in “Evolution.py”. To run the learning mechanism, the “Main” function in this class must be run. By running this code, it started to generate an initial population of genes with random weights. The weights are always between 1- and the population is fixed in every generation.
The next step is to divide the population into 5 leagues and let genes play two by two in each league. In this process, the number of wins and total games will be calculated for each gene. After this stage is completed, it’s time to generate the next generation. This work is started by sorting all genes according to their fitness value. Now, the logic of generating the next generation is:
Creating the next generation and repeating all the steps above, will continue until we reach the limit of the number of generations.
The last remarkable note is about the crossover and mutation procedure.
Crossover is started by choosing two random parents and a random
“alpha” which is the parameter that helps us get the weighted average
of every two features in two-parent genes. As a result, each feature of a
child will be calculated from the following statement:
A mutation will affect a few child genes. We have a constant mutation probability factor equal to 0.2 that is decreased in every generation(for the first generation this probability is equal to 0.2, 0.2/2 for the next one, 0.2/3, and so on). To perform mutation on each child, first, we get a random number between 0 and 1 and then compare the random value with mutation probability. If it is less than the probability, the mutation will be done in the following steps: