Implementing a Chess Coach

registration: 4861469

(Supervisor: Dr Barry Theobald) Abstract

The core component of every is it’s AI (Artificial Intelligence). AI has been an area of constant interest and research since the dawn of modern computing, holding interest for a number of important fields including not only computing but also, among others, philosophy, psychology and neurology. In this project, a chess program capable of fielding a game between two players was designed and implemented. Prominent methods of achieving both this and a chess engine capable of providing a challenging game experience to a player were discussed, and the methods used in this instance were justified. The program underwent user testing to ascertain it’s performance, and the results were discussed.

Acknowledgements

I would like to extend my sincere thanks to my supervisor, Dr Barry J. Theobald, for providing me with invaluable guidance and support throughout this challenging project. I would also like to express my gratitude to all of the users that provided me with helpful and constructive feedback. CMPC3P2Y Contents

Contents

1 Introduction 8 1.1 Background and History of Engines ...... 8 1.2 Aims ...... 10

2 Representing a Chess Board 11 2.1 Arrays ...... 11 2.2 Bitboards ...... 11

3 Tree Search Algorithms 14 3.1 Best-First Search ...... 14 3.2 Breadth-First Search ...... 16 3.3 Depth-First Search ...... 16 3.4 Machine Learning Approaches ...... 20

4 Evaluating a Chessboard 21 4.1 Heuristics ...... 21 4.2 Piece-Square Tables ...... 21

5 Weaknesses of Current Generation Chess Engines 23

6 Design and Implementation 24 6.1 Program Structure ...... 25 6.2 Board Representation ...... 27 6.3 Artificial Intelligence ...... 33

7 Testing 37 7.1 Framework ...... 37 7.2 Profiling ...... 38 7.3 Summary of Test Results ...... 38

Reg: 4861469 3 CMPC3P2Y Contents

8 Conclusions and Future Work 41 8.1 Future Work ...... 41

Reg: 4861469 4 CMPC3P2Y List of Figures

List of Figures

1.1 Estimated likelyhood of winning a game of chess based on the differ- ence in Elo rank between the two players. Given a difference of approx- imately 450 Elo between top humans and top computers, this shows that a top human player would have only approximately a 5% chance of defeating a top computer. Sourced from (Moser, 2010)...... 9 2.1 An example bitboard representing white pawns at the start of a chess game, formatted into the shape of a chessboard for ease of understand- ing. Each ‘1’ represents represents a white pawn...... 12 2.2 An illustration of the process of determining whether any of the posi- tions a white knight can attack contain a black piece...... 12 2.3 An example attack bitboard representing the possible lines of attack for a knight on square C3. ‘x’ represents the knight’s position and ‘1’ rep- resents a square that can be attacked by the knight...... 13 3.1 A demonstration of the order of node expansion when traversing a tree structure using breadth-first search...... 16 3.2 A demonstration of the order of node expansion when traversing a tree structure using depth-first search...... 17 3.3 An example minimax tree, searching 4 ply deep. The nodes highlighted in grey demonstrate the path through the tree that the algorithm will determine to be the best available move after completion...... 19 3.4 An example alpha-beta tree, searching 5 ply deep and building upon the minimax algorithm by eliminating subtrees that cannot provide a better score than another subtree that has already been evaluated. The eliminated subtrees are highlighted in grey...... 20 4.1 Example piece-square tables for white pawns, left, and white knights, right...... 22 6.1 Illustration of the cycle of the model-view-controller design pattern. This approach is used to help make large and complex programs modu- lar and maintainable...... 24 6.2 The hex values used to initialise the board at the start of a game. . . . . 27

Reg: 4861469 5 CMPC3P2Y List of Figures

6.3 Flowchart demonstrating the process of a human white player attempt- ing to make a move...... 28 6.4 Flowchart demonstrating the validation process that is applied to any attempted move with a pawn...... 29 6.5 An illustration of the process of determining whether the position under test contains a white pawn...... 30 6.6 Demonstration of the use of the text-based input/output system used for early testing purposes. A text file showing the numerical values of each board position was implemented to aid usability...... 31 6.7 Demonstration of the capabilities of the final GUI implementation. Se- lected friendly squares are highlighted green, unoccupied attackable squares are highlighted in light blue, and attackable squares occupied by opposing pieces are highlighted in red. Chessboard texture sourced from http://assets.freeprintable.com/images/item/original/ blank-chess-board.gif, chesspiece textures sourced from http: //www.wpclipart.com/recreation/games/chess/chess_set_ symbols.jpg...... 32 6.8 Illustration of the recursive minimax search function traversing the game tree to determine the best possible move...... 34 7.1 Results from profiling the memory usage of the program. The greatest source of memory usage is shown to be the allocation of Chessboard objects. This result is unexpected, as Chessboard objects individually use very little memory and should not be created in substantial enough numbers to use the indicated amount of memory...... 39 7.2 Results from profiling the CPU usage of the program. The greatest source of CPU usage is shown to be Thread.sleep(), which is not used while other operations are ongoing. All logic-heavy operations, such as move validation, use very little CPU-time, and so can be con- sidered efficient...... 40

Reg: 4861469 6 CMPC3P2Y List of Tables

List of Tables

6.1 Classes associated with the program’s framework ...... 26

Reg: 4861469 7 CMPC3P2Y 1 Introduction

1 Introduction

1.1 Background and History of Computer Chess Engines

The development of computer chess engines first began as an effort to improve AI tech- niques, led by the belief that the only way for a chess engine to attain any noteworthy level of ‘skill’ would be to strengthen the AI driving the engine. Many of the people involved believed that the successful creation of such an AI would irrefutably prove that human thinking can be artificially modelled, since chess was and still is widely regarded as the ultimate game of wits and cunning (Hsu et al., 1990). The original belief that only improved AI could sufficiently strengthen a chess engine for it to be a formidable opponent has proven false over the years, with the main point of progress being the constant and significant increase in the raw speed of the hardware running chess engines. The most notable example of this is Deep Blue, the computer purpose-built by IBM for playing chess that in 1997 famously defeated the then world chess champion Garry Kasparov. The computer was capable of evaluating 200 million positions per second and regularly searched over 20 ply deep, a feat impossible for any human player (Campbell et al., 2002). This degree of brute force easily compensated for it’s inferior tactical and strategic abilities, resulting in the first ever instance of a computer defeating a chess world champion. Contrast this computational power to the first fully-fledged chess computer, written by Alex Bernstein in 1957, which took 3 hours to search 4 ply deep. A second area in which chess programs have grown significantly more advanced over time is in the optimisation of their search algorithms. As hardware has grown faster, search algorithms have become less dependent upon hardware speed. In the last 10 years, chess programs have become increasingly viable on affordable, commercially available hardware. Modern mobile devices are now capable of playing chess well enough to easily overcome casual players, and where once supercomputers were re- quired to defeat grandmaster chess players, it is likely to eventually become the case that even mobile devices are capable of defeating them. In 1988, Kasparov was asked if he thought a computer would be able to defeat a chess grandmaster before the year 2000, to which he confidently replied ‘No way’ (Hsu et al.,

Reg: 4861469 8 CMPC3P2Y 1 Introduction

1990). It is testament to the astonishingly rapid progress of computer chess engines that an expert in the field like Kasparov could be proven so dramatically wrong, with grand- master Bent Larsen losing to Deep Thought just 10 months later (Simon and Schaeffer, 1990). Chess is a game of perfect information, a state in game theory where players ob- serve all previous moves, and can therefore determine all possible future game-states (Von Neumann and Morgenstern, 2007). The possession of perfect information allows the development of optimal strategies. However, there are far too many possible game- states in chess to compute a solution in a reasonable amount of time, if at all. The most powerful modern chess engines are all but untouchable to even the strongest human players. The required Elo (a ranking system used to measure the relative strengths of players in two-player games (Elo, 1978)) rating to qualify as a grandmaster is 2400, with the highest ever human Elo rating being 2851, belonging to Kasparov. Comparing this to the rating of the current strongest computer chess engine in the world, RYBKA 4, which has an unconfirmed but estimated 3300 Elo, the difference in playing strength between top humans and top computers becomes apparent. This fact is illustrated in Figure 1.1.

Figure 1.1: Estimated likelyhood of winning a game of chess based on the difference in Elo rank between the two players. Given a difference of approximately 450 Elo between top humans and top computers, this shows that a top hu- man player would have only approximately a 5% chance of defeating a top computer. Sourced from (Moser, 2010).

Reg: 4861469 9 CMPC3P2Y 1 Introduction

1.2 Aims

The primary aims of this project were to build an efficient chess framework that enforced all the rules of chess and allowed play between two human players, and a functional chess engine, capable of making on-the-fly decisions based on the ever-changing state of play, such that it is capable of providing a challenging game experience to a player of average skill. The secondary aim was a coaching mechanic that could assess any given board-state and suggest strong moves to a player based on the situation. The coach could also take a move as input from the player and rate the strength of that move.

1.2.1 Required Outcomes

• Playable chess game with opponent AI

• Generate a chess framework

• Implement a search engine capable of determining the best possible move avail- able for any given board-state

• User-friendly GUI

1.2.2 Desirable Outcomes

• Hint system that would suggest a good move to a player if requested

• Move-ranking system that would rate a player’s currently selected move if re- quested

• Customisable time-restrictions and AI-difficulty settings

Reg: 4861469 10 CMPC3P2Y 2 Representing a Chess Board

2 Representing a Chess Board

There are a number of ways of representing a chessboard. The problem initially seems trivial, as the board can only occupy a single state at any time. Considering that a search algorithm may read from this chessboard millions of times to make a single move however, it becomes apparent that an optimal solution is highly beneficial to the overall performance of the chess engine.

2.1 Arrays

Arrays are the most obvious and intuitive approach, but also the least efficient. An entire board can be represented using a single 64x1 or 8x8 array, using a different character or numerical value to represent each piece type. This means that arrays use little memory, but they make very inefficient use of processor time, since a search function may read to and write from this array millions of times to determine a single move, and array manipulation is very slow. This is because array manipulation is O(n), where n is the size of the array. As a result, arrays become slower to manipulate the larger they become.

2.2 Bitboards

A bitboard is a tool commonly used for board games such as chess, checkers and go, and can be implemented as an integer of any size. In the context of chess, a bitboard is a 64-bit integer, where every bit represents a square on the chessboard. The state of each bit acts as a boolean operator, with ‘1’ and ‘0’ representing ‘true’ and ‘false’ re- spectively. Each bit represents some information about it’s corresponding board square (San Segundo et al., 2006). For example, if the 7th bit of a bitboard representing white pawns is a ‘1’, then the 7th square of the chessboard, B1, contains a white pawn. Con- versely if it is ‘0’, then square B1 does not contain a white pawn. Figure 2.1 shows an example bitboard representing white pawns at the start of a chess game, formatted into the shape of a chessboard for ease of understanding.

Reg: 4861469 11 CMPC3P2Y 2 Representing a Chess Board

Figure 2.1: An example bitboard representing white pawns at the start of a chess game, formatted into the shape of a chessboard for ease of understanding. Each ‘1’ represents represents a white pawn.

Bitboards occupy very little memory, using just eight bytes each, and can be used together to form very efficient queries. These queries often take place using a tech- nique called bitshifting, whereby every bit in an integer is ‘shifted’ along to the left or right by the specified number of bits. If bitshifting the binary number 00011010 left by 2, it would become 01101000. As a practical example, to determine if there are any black pieces occupying the squares a white knight can attack, the query would be blackBoard AND Knight.attackBoard, occupying only a single processor instruction. Figure 2.2 illustrates this process.

Figure 2.2: An illustration of the process of determining whether any of the positions a white knight can attack contain a black piece.

As well as representing pieces, bitboards can also be used to quickly calculate at- tacked squares for any piece, avoiding the need to test attacking every square on the

Reg: 4861469 12 CMPC3P2Y 2 Representing a Chess Board board to determine which moves are valid (Rasmussen, 2004). An example attack bit- board for a knight on square C3 is shown in Figure 2.3.

Figure 2.3: An example attack bitboard representing the possible lines of attack for a knight on square C3. ‘x’ represents the knight’s position and ‘1’ represents a square that can be attacked by the knight.

Bitboards provide a superior solution to board representation compared to arrays. When using arrays, the elements of the arrays must be repeatedly traversed to find the sought information. This is particularly costly if the arrays are unsorted. Contrast this with bitboards, where a single logical operation can provide an immediate boolean result at a consistently high speed.

Reg: 4861469 13 CMPC3P2Y 3 Tree Search Algorithms

3 Tree Search Algorithms

When developing a chess engine, by far the most important and quality-defining aspect is the search algorithm, the method by which the system determines the best possible move it can make at any given time. When evaluating a chessboard, the diagrammatic representation of the process takes the form of a tree structure, with each node represent- ing a board-state and each branch representing a possible move from that board-state. This means that any algorithm used must be capable of searching tree structures. While there are many algorithms that have been developed, only a handful are considered pow- erful or influential enough to be worth noting.

3.1 Best-First Search

Best-first search algorithms are used for exploring graphs, seeking to find a solution by expanding the most promising node according to some set of rules. Algorithm 1 demonstrates the implementation with pseudo-code.

Algorithm 1 Best-First Search OPEN = [initial state] CLOSED = [] while OPEN is not empty do 1. Remove the best node from OPEN, call it n, add it to CLOSED. 2. If n is the goal state, backtrace path to n (through recorded parents) and return path. 3. Create n’s successors. 4. For each successor do: a. If it is not in CLOSED: evaluate it, add it to OPEN, and record its parent. b. Otherwise: change recorded parent if this new path is better than previous one. done

Reg: 4861469 14 CMPC3P2Y 3 Tree Search Algorithms

A variation exists that uses a heuristic to estimate the distance from the end of a path to a solution, allowing it to prioritise paths that appear closest to a solution. This is known as ‘greedy best-first search’ (Russell et al., 1995).

3.1.1 Dijkstra’s Algorithm

Dijkstra’s algorithm is a graph search algorithm designed to solve the shortest path problem by producing a shortest path tree (Dijkstra, 1959). This is useful for solving problems such as route planning for drivers, or network routing protocols. Algorithm 2 shows how the algorithm is executed.

Algorithm 2 Dijkstra’s Algorithm 1) Assign distance values to every node. The initial node is set to 0, every other node is set to null or infinity. 2) Mark all nodes as unvisited and set the initial node as the current node. 3) For the current node, calculate the tentative distances to all other unvisited nodes, and mark the current node as visited. 4) If a distance to a node shorter than the previously recorded distance to that node is found, overwrite the old sitance with the new one. 5) If the goal node is marked as visited, the shortest path has been found and the algorithm terminates. 6) If the closest node to the current node is unvisited, set that node to the current node and return to step 3.

This determines the shortest path as that which the algorithm followed.

3.1.2 A* Search

A* is a widely adopted best-first search technique, commonly used for pathfinding and graph traversal. It is a generalisation of Dijkstra’s algorithm, usually using heuristics to improve efficiency. Although it’s performance is strong, and it is thus widely used, in

Reg: 4861469 15 CMPC3P2Y 3 Tree Search Algorithms practical use it is generally outperformed by algorithms that can pre-process the graph (Sanders and Schultes, 2007).

3.2 Breadth-First Search

Breadth-first search (BFS) is used mostly for traversing graphs, but can also be used to traverse tree structures. In this method, no child node may be expanded until all nodes at the parent’s level have been expanded. This algorithm suffers from high space- complexity, since all nodes at a particular level are expanded simultaneously and each of these nodes must be stored in memory (Even, 2011). Figure 3.1 demonstrates the execution of the algorithm when traversing a tree structure.

Figure 3.1: A demonstration of the order of node expansion when traversing a tree struc- ture using breadth-first search.

3.3 Depth-First Search

Depth-first search (DFS), like BFS, is used for traversing graphs and tree structures, though graph traversal is not the predominant usage in this case. DFS expands the first child node it finds, and repeats this process until it either runs out of child nodes or

Reg: 4861469 16 CMPC3P2Y 3 Tree Search Algorithms

finds a solution. It then backtracks down the path it has taken until it encounters an unexpanded child node (Cormen et al., 2001). This eliminates the space-complexity issue suffered by BFS, but introduces the problem of potentially infinite paths, whereby a solution or childless node is never found and the algorithm is never able to terminate. The algorithm is typically implemented recursively for greater efficiency. Figure 3.1 demonstrates the execution of the algorithm when traversing a tree structure.

Figure 3.2: A demonstration of the order of node expansion when traversing a tree struc- ture using depth-first search.

3.3.1 Depth-Limited Search

Depth-Limited Search is a variation of DFS designed to overcome the issue of non- termination due to infinite paths. It addition to terminating upon finding a solution or a childless node, it also terminates if the depth of the node under test is equal to some maximum depth, treating that node as childless whether it is or not (Russell et al., 1995). Although this solves the non-termination problem, it introduces a new issue known as the ‘horizon effect’. This is where a move is made that appears to be strong, but actually proves to be detrimental past the depth of the search. This problem can itself be partially remedied through the use of a quiescence search, a process where interesting moves

Reg: 4861469 17 CMPC3P2Y 3 Tree Search Algorithms

(generally interpreted to mean highly active moves, such as those involving captures or promotions) are evaluated to a greater depth than others to check for unforeseen consequences ‘over the horizon’ (Kaindl, 1983).

3.3.2 Minimax

Minimax is an algorithm that employs a large degree of brute force when searching a game tree, examining every possible move for every piece on every turn. Assuming infinite processing power this would be the best method, potentially allowing chess to be solved, but in reality it is wildly impractical as it is computationally very expensive, even with the immense processing power of modern supercomputers. It was even more impractical at the time of it’s conception, when computers wielded only a tiny fraction of the processing power they do today (Marsland et al., 1987). The basic aim of the minimax search is to minimise the possible loss of a given scenario, or maximise the possible gain. The minimax theorem states that (Osborne and Rubinstein, 1994):

• Given player 2’s strategy, the best payoff possible for player 1 is V, and

• Given player 1’s strategy, the best payoff possible for player 2 is -V.

Where V is the score assigned to a given move. Figure 3.3 shows how the minimax algorithm searches and evaluates a tree 3 ply deep.

Reg: 4861469 18 CMPC3P2Y 3 Tree Search Algorithms

Figure 3.3: An example minimax tree, searching 4 ply deep. The nodes highlighted in grey demonstrate the path through the tree that the algorithm will determine to be the best available move after completion.

Although this algorithm is now outdated and deprecated, the theorem still forms the foundation of many more modern and efficient algorithms, such as Alpha-Beta (Knuth and Moore, 1975).

3.3.3 Alpha-Beta

One of the most prevalently used search algorithms in computer chess engines is the alpha-beta algorithm. This is an extension of the minimax algorithm. It is based on the same principle of maximising the player’s score while minimising the opponent’s score, but it attempts to solve minimax’s problem of needing to search every node of the tree. It does this by pruning subtrees that produce a worse score than a previously evaluated subtree, saving time that would be effectively wasted evaluating subtrees that have no chance of being selected (Knuth and Moore, 1975). Figure 3.4 shows how the algorithm searches and evaluates a tree 5 ply deep and prunes unnecessary subtrees. Although this algorithm greatly increases search efficiency, it can be fooled into prun- ing branches that lead to strong positions by the horizon effect. This can happen when it suffers a material loss within it’s search depth, causing a low score and for that branch to be pruned, causing it to miss a checkmate the move leads to past the search depth.

Reg: 4861469 19 CMPC3P2Y 3 Tree Search Algorithms

Figure 3.4: An example alpha-beta tree, searching 5 ply deep and building upon the min- imax algorithm by eliminating subtrees that cannot provide a better score than another subtree that has already been evaluated. The eliminated sub- trees are highlighted in grey.

3.4 Machine Learning Approaches

A new approach that is increasingly being adopted for chess engines is the use of ma- chine learning, to simulate a more human-like AI player, capable of learning to become a stronger player based on experience. This means that an engine that is initially weak could potentially escalate to the grandmaster level simply through extensive use. Such an approach still requires the use of one of the established search algorithms, such as alpha-beta, and of heuristics to score a given game-state. These functions may be subtly modified over time however, with particular respect to the heuristics used (Fürnkranz, 1996). Although no engine adopting this approach has yet had major success on any stage, the increased interest and usage is certain to yield improvements in the future.

Reg: 4861469 20 CMPC3P2Y 4 Evaluating a Chessboard

4 Evaluating a Chessboard

Another component central to every chess engine is it’s evaluation function, the method by which the strength of a move is determined. No matter how fast a chess engine is able to search the game-tree, if it cannot correctly distinguish between strong moves and weak moves it will still perform poorly.

4.1 Heuristics

Heuristics are a method of speeding up or approximating the solution of a problem based on dynamic situational knowledge, usually at the cost of decreased accuracy and/or pre- cision (Kahneman et al., 1982). Heuristics are used frequently in chess programs since evaluating every possible move on a chessboard to find an optimal solution is impracti- cal, and may not even be possible. This is because there are too many possible board- states and game variations to process all the required information on current hardware. As such, heuristics in chess programs serve both to speed up and to approximate the solution to move selection.

4.2 Piece-Square Tables

When evaluating a position, certain areas of the board give greater or lesser positional advantage than others, but these areas differ between both piece types and piece colours. For example, whilst knights and bishops are both strongest in the centre of board due to the increase in threat and manoeuvrability that provides them, knights suffer a more severe penalty for being on the edge of the board, as their small move-set makes it harder for them to escape attack in such positions (Breuker et al., 1994). Piece-square tables provide a fast and elegant solution. Each piece-type has a 64- element array, with each element representing the score of the corresponding board- square for that piece. When evaluating the board, every piece has the score of the ap- propriate table’s element corresponding to that pieces position on the chessboard added to it’s base value. These tables are normally stored from the perspective of the white player. To use them from the perspective of the black player, they are flipped top to

Reg: 4861469 21 CMPC3P2Y 4 Evaluating a Chessboard bottom using the following algorithm:

byte index = (byte)((i+56)-((i/8)*16))

Figure 4.1 shows example piece-square tables for white pawns and white knights, respectively (Berent, 2008).

Figure 4.1: Example piece-square tables for white pawns, left, and white knights, right.

Reg: 4861469 22 CMPC3P2Y 5 Weaknesses of Current Generation Chess Engines

5 Weaknesses of Current Generation Chess Engines

Although the most powerful modern chess engines now have such high playing strength that many would argue they possess no weaknesses, there are still areas of their play that can be particularly improved, if only for the purpose of defeating eachother. One area is board evaluation. Although this area of computer chess is already very strong, it can fall victim to being too materialistic, playing predictably and being tricked by long-term conservative play. A way to improve would be to analyse the playstyle of the opponent during the course of a match, adapting it’s own heuristics to best suit the situation. This would make the use of the described ‘anti-computer tactics’ less viable, as they would be detected and an appropriate counter-strategy could be employed. The most likely source of improvement in this regard is from machine learning techniques, as they have an inherent capacity for improvement. The other main area is search depth. This area is often neglected, as it continues to steadily improve due to hardware becoming more powerful, negating the need for better search algorithms. However, if chess is ever to be solved, both aspects require significant improvement, as the rate at which hardware is growing more powerful is insufficient for the task.

Reg: 4861469 23 CMPC3P2Y 6 Design and Implementation

6 Design and Implementation

This program was created using the Java programming language, and attempts to im- plement the model-view-controller (MVC) design pattern. The use of MVC allows a complex program to remain modular and maintainable. The controller receives all in- puts to the program, sending the information to the model. The view handles all outputs from the program. These outputs can take any form, such as sounds, images or even haptic feedback. The model handles all processing of information within the program, handling data input from the controller as well as executing program logic, and notify- ing the view of what it is required to output. Figure 6.1 illustrates this cycle.

Figure 6.1: Illustration of the cycle of the model-view-controller design pattern. This approach is used to help make large and complex programs modular and maintainable.

In this implementation, both the controller and the view are handled within the same class, ‘Display.java’, through an interactive GUI that accepts mouse actions as input, and updates the GUI based on both the model’s notifications and some interactions with the controller. This means that the MVC is not currently in use, as it requires the view and the controller to be in separate classes. However, modifying the class structure to have mouse input handled within a separate class is possible, and is a possible future

Reg: 4861469 24 CMPC3P2Y 6 Design and Implementation improvement.

6.1 Program Structure

The implementation of this program is divided into two distinct components - the frame- work and the engine. The framework handles the generation and validity checking of all moves, and allows play between two human players. The engine functions as a com- puter player, searching the game tree and evaluating the strength of moves, selecting and playing the strongest move found. The framework was fully implemented before work on the engine began, as the framework can function correctly without the engine, but the engine cannot function correctly without the framework. The framework consists of eleven classes. These classes are briefly described in Ta- ble 6.1. The engine consists of a single class, ‘Search.java’, which is called when it is a computer player’s turn to move. This method searches through the game tree to a spec- ified depth using minimax search, evaluating each move and storing the score, current position and requested position of the best move that currently been found, updating that information if a move with a better score than the current best is found. However, owing to implementation problems involving occasional non-termination of the search algorithm and insufficient time to fix the issue, the engine has been disabled for the purposes of this report.

Reg: 4861469 25 CMPC3P2Y 6 Design and Implementation

Table 6.1: Classes associated with the program’s framework

Class Description ChessProject.java The main thread of the program. Initiates the GUI and runs the game in a loop until one player wins, calling .move methods as needed. Chessboard.java Instantiated as an object. Contains a number of bitboards to rep- resent each piece type and colour, as well as boards for all pieces of each colour. Check.java Contains methods to determine whether the specified king is in check for a given board-state, one method for each colour. Also contains methods to determine the valid moves from a given board-square and return them in an array. Checkmate.java Contains methods to determine whether the specified king is in checkmate for a given board-state, one method for each colour. Display.java Handles all user mouse input, updates GUI based on user input and board-state. Pawn.java Contains methods, one for white and one for black, to move a pawn. Includes all move validation to ensure the move is legal. Rook.java Contains methods, one for white and one for black, to move a rook. Includes all move validation to ensure the move is legal. Bishop.java Contains methods, one for white and one for black, to move a bishop. Includes all move validation to ensure the move is legal. Knight.java Contains methods, one for white and one for black, to move a knight. Includes all move validation to ensure the move is legal. Queen.java Contains methods, one for white and one for black, to move a queen. Includes all move validation to ensure the move is legal. King.java Contains methods, one for white and one for black, to move a king. Includes all move validation to ensure the move is legal.

Reg: 4861469 26 CMPC3P2Y 6 Design and Implementation

6.2 Board Representation

6.2.1 Internal Board Representation

The board is represented by a Chessboard object. Each Chessboard object contains 14 bitboards. One to represent each piece type and colour, as well as one for all white pieces and one for all black pieces. At the start of a game, a new Chessboard is initialised with it’s default constructor, using the hex values shown in Figure 6.2. Chessboard objects can also be initialised by accepting another Chessboard in the constructor, useful for making temporary copies of the main Chessboard to use in test moves, such as those generated by minimax search or those used to test for check or checkmate.

Figure 6.2: The hex values used to initialise the board at the start of a game.

When attempting to make a move, a number of operations take place before the move is accepted and it becomes the opposing player’s turn. The flow of these operations is demonstrated in Figure 6.3. Figure 6.4 shows the validation process that occurs with the Pawn.moveWhite() or Pawn.moveBlack methods when attempting to move a pawn.

Reg: 4861469 27 CMPC3P2Y 6 Design and Implementation

Figure 6.3: Flowchart demonstrating the process of a human white player attempting to make a move.

Reg: 4861469 28 CMPC3P2Y 6 Design and Implementation

Figure 6.4: Flowchart demonstrating the validation process that is applied to any at- tempted move with a pawn.

Reg: 4861469 29 CMPC3P2Y 6 Design and Implementation

Many of the operations involved in attempting to make a move involve checks be- tween bitboards. For example, the first check performed when attempting to move a white pawn is if there is a white pawn at the selected position. This check is performed by the following statement:

if (!(board.getWhitePawns() & (1L « currentPos-1)))

In this statement, board.getWhitePawns() retrieves the bitboard storing the positions of all white pawns from the Chessboard board, which is passed to the method as a parameter. 1L « currentPos-1 creates a 64-bit integer initialised to 1, and left-bitshifts it by the value currentPos-1 (the ‘-1’ is required because the integer being bitshifted is initialised to 1, not 0). The & between the two statements is the logical AND operator, indicating that any bit that is set to 1 on both integers will be set to 1 in the resulting integer, otherwise that bit will be set to 0. If the statement returns true, there is a white pawn at the selected position. This statement is illustrated in Figure 6.5.

Figure 6.5: An illustration of the process of determining whether the position under test contains a white pawn.

6.2.2 External Board Representation

During the early phases of the implementation, before the GUI had been developed, a text-based input/output system was developed for early testing of framework function- ality. It operated through the Netbeans output window, taking positional input through text and displaying output through a chessboard-shaped grid of text. The output used

Reg: 4861469 30 CMPC3P2Y 6 Design and Implementation two-letter combinations to represent each piece, for example ‘WQ’ represents white queen. Figure 6.6 demonstrates this system in use.

Figure 6.6: Demonstration of the use of the text-based input/output system used for early testing purposes. A text file showing the numerical values of each board position was implemented to aid usability.

After the completion of the framework implementation, a GUI was developed us- ing Java2D to replace the text-based system. The priorities when developing it were simplicity, user-friendliness and the ability to accept mouse-input. As shown in Figure 6.7, the result was a 2D top-down perspective of the chessboard, where input is han- dled through mouse events. Further, to help fulfil the user-friendliness criteria, upon selecting a friendly piece the square under that piece is highlighted in green, all unoccu- pied squares the piece can move to are highlighted in light blue, and the squares of any opposing pieces that can be taken are highlighted in red.

Reg: 4861469 31 CMPC3P2Y 6 Design and Implementation

Figure 6.7: Demonstration of the capabilities of the final GUI implementa- tion. Selected friendly squares are highlighted green, unoccu- pied attackable squares are highlighted in light blue, and attack- able squares occupied by opposing pieces are highlighted in red. Chessboard texture sourced from http://assets.freeprintable. com/images/item/original/blank-chess-board.gif, chesspiece textures sourced from http://www.wpclipart.com/recreation/ games/chess/chess_set_symbols.jpg.

Reg: 4861469 32 CMPC3P2Y 6 Design and Implementation

6.3 Artificial Intelligence

6.3.1 Search

Although the engine has been disabled, the search has been implemented. The chosen search algorithm used is a depth-limited minimax search, as it is well suited to searching game trees of two-player turn-based games like chess, and it’s strength and runtime can be easily tuned by adjusting the maximum depth. Since the computer player is always black, and it is recommended to start the search on the maximising player, black is always treated as the maximising player, and white as the minimising player. The search begins by searching the board for a black piece. Upon finding one it attempts to move the piece to every square of the board. When it finds a valid move, it calls the max method, stores the returned output and makes the move using a temporary Chessboard object, then recursively calls itself with an incremented depth parameter. It then repeats the previous process of searching for a valid move, but when evaluating a move it finds, it instead calls the min method. It repeats this process until it scores a node that either has a depth equal to the max- imum depth or has no children. At this point it compares the score at that depth to the best score currently obtained. If the new score is higher, it replaces the best score and updates the bestCurrentPos and bestNewPos variables with the current and new positions from the depth 1 search that led to the currently explored node. When the algorithm has searched all positions up to the maximum depth, it makes a move based on the bestCurrentPos and bestNewPos coordinates stored, then passes to the opposing player’s turn. This process is illustrated in Figure 6.8.

Reg: 4861469 33 CMPC3P2Y 6 Design and Implementation

Figure 6.8: Illustration of the recursive minimax search function traversing the game tree to determine the best possible move.

Reg: 4861469 34 CMPC3P2Y 6 Design and Implementation

6.3.2 Evaluation

Although the engine has been disabled, the evaluation function for scoring moves is fully functional. There are a number of factors involved in evaluating a move. The simplest is the use of different base values for each piece. For example, even a beginner to chess knows that a queen is worth more than a pawn. The base values of each piece type are given (Berent, 2008):

• Pawn: 100

• Bishop: 325

• Rook: 500

• Knight: 320

• Queen: 975

• King: 10,000

The king is given an extremely large value as it is the most valuable piece on the board and, although this score is permanently in place as it cannot technically be taken, appropriate changes are made to the move’s score if check or checkmate is found. The total material score of a board is calculated by multiplying each base value by the number of that piece type remaining, and summing the totals. For example, the score for a white player with three pawns, one rook and the king remaining would be 10,800.

Pawns(300) + Rook(500) + King(10,000) = 10,800

Using only base piece values when evaluating a board would result in many evaluated boards having the same score, likely leading to poor play. To combat this, the positional advantages of different parts of the board for different pieces have to be considered. This is done using piece-square tables. Example piece-square tables are shown in 4.1. The tables used in this implementation are open-source and were acquired from (Berent, 2008).

Reg: 4861469 35 CMPC3P2Y 6 Design and Implementation

The final additions to the score come from checks of board-state. After all of the previously described heuristics are applied, the function tests for whether the position has castled, checked or checkmated. Castling adds 40 points, checking adds 75 points and checkmating, being the objective of the game, adds 15,000 points. All of the score-affecting factors are inverted for the minimising player, so for exam- ple checkmate subtracts 15,000 points instead of adding them.

Reg: 4861469 36 CMPC3P2Y 7 Testing

7 Testing

Testing is a crucial part of any implementation-based project. It is important to maintain a bug-free implementation to prevent unexpected behaviour, and to determine areas that could be targeted for future optimisation or improvement. The primary aim of this testing was to ensure that all move generation and validation performs as it should, and to identify possible bottlenecks in the program that could be optimised to improve the overall performance of the program. Testing was also expected to be performed on the program’s AI, but this has not been possible as it has not yet been successfully implemented.

7.1 Framework

Throughout the development of the program, with particular respect to move validation, it has been tested at regular intervals to ensure correct functionality. This testing was primarily carried out by the developer, but at key milestones such as the implementation of checkmate, it was also performed by four outside users, all familiar with the rules of chess. The purpose of this testing was to identify bugs and issues with the program as soon as they arose, so that they could be fixed before they were allowed to cause further problems. Since a game a chess can last a long time, and time constraints had been imposed on the project, testing rarely took the form of games played to completion, which was only performed for milestone testing. This meant that tests were usually carried out quickly and solely involved the part of the program being developed at the time. This allowed the potential for side-effect bugs to go undetected, a flaw in the testing process that could have been eliminated given more time. The testing process proved to be valuable and constructive, especially that performed by the outside users. As a result many bugs, such as checkmate not registering and various pieces being able to make invalid moves, were discovered and fixed. The final implementation of the framework has been reported bug free by all user testing, as well as extensive developer testing.

Reg: 4861469 37 CMPC3P2Y 7 Testing

7.2 Profiling

Code profiling is a useful programming tool that analyses many aspects of a program’s operation, such as time and space complexity, and the frequency of method calls as well as the time spent on each method call. It is primarily used to help programmers to optimise complex programs. For this method of testing, the Netbeans profiler was used. Figure 7.1 shows the results of profiling the memory usage of the program. This shows that the greatest source of memory usage is the allocation of Chessboard objects, using approximately 152 megabytes of memory. Given that each Chessboard object, containing 14 64-bit integers, should occupy approximately 0.1 kilobytes of memory, this is far too much and represents a serious memory leak. This is because far more are being created than expected, as the profiler shows almost 12 million Chessboard objects being allocated. This is a strong target for future optimisation. Figure 7.2 shows the results of profiling the CPU usage of the program. The largest use of CPU-time is Thread.sleep(), which can be safely ignored from a profil- ing perspective as this is merely a mechanism to prevent unnecessarily high processor usage while the program is polling for user input, and is not used during during other operations such as move validation or the AI’s search. The only other notable uses of CPU-time are the methods used to render the GUI. This is a good result, as it shows that all logic-heavy operations, such as move validation, use very little CPU-time and can be considered efficient. Profiling reveals that there is no need for optimisation of CPU usage within this implementation.

7.3 Summary of Test Results

User testing has shown that the framework is bug-free, and code profiling has revealed that it makes very efficient use of CPU-time. However, code profiling has also revealed that the allocation of Chessboard objects is using more memory than expected, and requires optimisation.

Reg: 4861469 38 CMPC3P2Y 7 Testing

Figure 7.1: Results from profiling the memory usage of the program. The greatest source of memory usage is shown to be the allocation of Chessboard ob- jects. This result is unexpected, as Chessboard objects individually use very little memory and should not be created in substantial enough numbers to use the indicated amount of memory.

Reg: 4861469 39 CMPC3P2Y 7 Testing

Figure 7.2: Results from profiling the CPU usage of the program. The greatest source of CPU usage is shown to be Thread.sleep(), which is not used while other operations are ongoing. All logic-heavy operations, such as move validation, use very little CPU-time, and so can be considered efficient.

Reg: 4861469 40 CMPC3P2Y 8 Conclusions and Future Work

8 Conclusions and Future Work

The aims of this project were to build an efficient chess framework that enforced all the rules of chess and allowed play between two human players, and a functional chess engine, capable of making on-the-fly decisions based on the ever-changing state of play, such that it is capable of providing a challenging game experience to a player of average skill. The first aim was achieved, as the framework was successfully and efficiently imple- mented, capable of recognising and enforcing the rules of chess to allow play between human players. The second aim was not achieved, as the complexity and time required to implement the framework, in particular the GUI, was underestimated and led to in- sufficient time remaining to complete the engine. However, it was attempted and given further time could be successfully implemented.

8.1 Future Work

There are many ways in which this project could be improved. The primary improve- ment would be to finish implementing the search, so that a human player could play against the AI. At this point further testing would be required to evaluate the AI’s per- formance, as it could be further improved by optimising the search to reduce the number of nodes it explores. This could be done by introducing pruning methods, such as con- verting the minimax search to alpha-beta, and possibly implementing null move pruning (this would depend on the strength of the engine, as null move pruning can reduce search accuracy). Adding a quiescence search to explore the most interesting positions found to a slightly greater depth would improve the accuracy of the search at minimal cost to search speed. Multi-threading could be used to be improve the performance of the search, as depth- first tree traversal is a strong candidate for parallelisation. For example, branching from each file could be handled by a different thread. Each file is likely the contain approxi- mately the same number of pieces of a given colour, so waiting times for a single thread to complete would be minimised. The increase in search speed this would provide would allow the tree to be searched to a greater depth, improving the accuracy of the search.

Reg: 4861469 41 CMPC3P2Y 8 Conclusions and Future Work

The evaluation function for scoring moves could be improved by making it more comprehensive. One way to achieve this is to allow it to recognise the three different game-states: early-game, middle-game, late/end-game. This is important as both the base and positional values of pieces change throughout the game based on this state. For example, a knight is worth less in the late game because it is harder to produce a checkmate with, and a kings positional values should encourage it to play more ag- gressively late game, as the smaller number of enemy pieces means there is a lesser threat of being checkmated. By incorporating this information into the evaluation, the engine should make stronger and more appropriate moves throughout the full duration of a game. Another improvement would be to alter the way moves are generated. Currently, ev- ery square of the board is looped through to find a piece, and then looped through again to find all the valid moves for that piece. A better system would be to numerically store all piece positions in a vector to shorten the first loop, and implement attack bitboards to shorten the second loop. This would reduce the positions searched by the first loop by a minimum of 75%, and greatly reduce the overhead from method calls attempting invalid moves in the second loop, as well as reducing the number of positions searched by over 50%. A commonly used tool in commercial chess engines is the use of opening and ending books. These are predefined strategies hard-coded into the engine, that allow for a number of optimal strategies to be played by rote at the start and end of a game. Opening books are easier to implement, as the position from which they come into use is always the same. Since there are many possible variations of the endgame, ending books are required to be more flexible and versatile than opening books. The books of the top chess engines are extensive, and allow for almost any scenario, making them extremely strong in these phases of the game. Finally, as outlined in Section 1.2.2, coaching tools could be implemented to aid players in identifying strong moves, allowing them to improve the strength of their game. Time controls could also be added to encourage competitive play.

Reg: 4861469 42 CMPC3P2Y References

References

Berent, A. (2008). Chess board evaluation. http://www.chessbin.com/post/ Chess-Board-Evaluation.aspx.

Breuker, D. M., Uiterwijk, J., and van den Herik, H. J. (1994). Replacement schemes for transposition tables. ICCA Journal, 17(4):183–193.

Campbell, M., Jr., A. H., and hsiung Hsu, F. (2002). Deep blue. Artificial Intelligence, 134:57 – 83.

Cormen, T. H., Leiserson, C. E., Rivest, R. L., and Stein, C. (2001). Introduction to algorithms. MIT press.

Dijkstra, E. W. (1959). A note on two problems in connexion with graphs. Numerische mathematik, 1(1):269–271.

Elo, A. E. (1978). The rating of chessplayers, past and present, volume 3. Batsford London.

Even, S. (2011). Graph algorithms. Cambridge University Press.

Fürnkranz, J. (1996). Machine learning in computer chess: The next generation. Inter- national Computer Chess Association Journal, 19(3):147–160.

Hsu, F., Anantharaman, T., Campbell, M., and Nowatzyk, A. (1990). A grandmaster chess machine. Scientific American, 263(4):44–50.

Kahneman, D., Slovic, P., and Tversky, A. (1982). Judgment under uncertainty: Heuris- tics and biases. Cambridge University Press.

Kaindl, H. (1983). Searching to variable depth in computer chess. In Int. Joint Conf. on Artificial Intelligence (IJCAI83), pages 760–762.

Knuth, D. E. and Moore, R. W. (1975). An analysis of alpha-beta pruning. Artificial Intelligence, 6(4):293 – 326.

Reg: 4861469 43 CMPC3P2Y References

Marsland, T. et al. (1987). Computer chess methods. Encyclopedia of Artificial Intelli- gence, pages 159–171.

Moser, J. (2010). Moserware. http://www.moserware.com/.

Osborne, M. and Rubinstein, A. (1994). A course in game theory. MIT press.

Rasmussen, D. (2004). Parallel chess searching and bitboards. PhD thesis, Technical University of Denmark, DTU, DK-2800 Kgs. Lyngby, Denmark.

Russell, S. J., Norvig, P., Canny, J. F., Malik, J. M., and Edwards, D. D. (1995). Artificial intelligence: a modern approach, volume 74. Prentice hall Englewood Cliffs.

San Segundo, P., Galan, R., Matia, F., Rodriguez-Losada, D., and Jimenez, A. (2006). Efficient search using bitboard models. In Tools with Artificial Intelligence, 2006. ICTAI’06. 18th IEEE International Conference on, pages 132–138. IEEE.

Sanders, P. and Schultes, D. (2007). Engineering fast route planning algorithms. In Experimental Algorithms, pages 23–36. Springer.

Simon, H. and Schaeffer, J. (1990). The game of chess. Technical report, DTIC Docu- ment.

Von Neumann, J. and Morgenstern, O. (2007). Theory of games and economic behavior (commemorative edition). Princeton university press.

Reg: 4861469 44