59
Carleton University School of Computer Science Bachelor of Computer Science (Algorithms) Honours Project Report - COMP4905 Web Based Chess Engine Implementation Author: Zoltan Hernadi Supervisor: Dr. John Oommen, School Of Computer Science 2015.08.13

Web Based Chess Engine Implementationservice.scs.carleton.ca/sites/default/files/honours_projects/2015/... · Carleton University School of Computer Science Bachelor of Computer Science

  • Upload
    ngongoc

  • View
    215

  • Download
    0

Embed Size (px)

Citation preview

Carleton University

School of Computer Science

Bachelor of Computer Science

(Algorithms)

Honours Project Report - COMP4905

Web Based Chess Engine Implementation

Author: Zoltan Hernadi

Supervisor: Dr. John Oommen, School Of Computer Science

2015.08.13

Abstract

Chess programming has always been at the heart of computer science and has been one of the oldest research areas in artificial intelligence. The purpose of this project has been to research and study modern chess programming techniques and AI algorithms in order to create a unique, web based chess playing program enabling it to play a reasonably strong game.

The core engine component uses a carefully selected, but necessarily limited collection of well established search methods and evaluation heuristics to achieve the goal of the project. More specifically, the engine has implemented a bitboard representation, the negamax and quiescence search algorithms, a move generator, hash tables, the universal chess interface protocol, and several evaluation heuristics.

i

Acknowledgments

I would like to thank the following people for their help in the production of this project:

Mr. Spencer Polk, teaching assistant supervisor, for guiding me, using his technical expertise in the field of artificial intelligence game theory and implementation, to successfully complete this complex undertaking.

Dr. John Oommen, project supervisor, without whose help and support, this project would not have been possible.

My thanks also go to the creators of those open source chess engines that are listed in section '8.4 Open Source Chess Engine References'. Their implementations have been extremely helpful throughout this project and have been sources of inspiration.

ii

Table of ContentsAbstract.......................................................................................................................................................iAcknowledgments.....................................................................................................................................iiList of Figures............................................................................................................................................vList of Illustrations....................................................................................................................................vi1. Introduction............................................................................................................................................1

1.1 Project Motivation...........................................................................................................................11.2 Objectives........................................................................................................................................21.3 Report Overview.............................................................................................................................3

2. Background............................................................................................................................................42.1 Description of the Chess Game Domain........................................................................................42.2 Chess Programming Introduction....................................................................................................5

2.2.1 Overview..................................................................................................................................52.2.2 Game Complexity....................................................................................................................62.2.3 Pruning Requirements.............................................................................................................72.2.4 Chess Engine Functional Considerations................................................................................82.2.5 Chess Engine Components......................................................................................................9

3. Chess Engine Architecture...................................................................................................................103.1 Communication Protocol..............................................................................................................103.2 Board Position Representation......................................................................................................10

3.2.1 Internal Board Model.............................................................................................................113.2.2 BitBoard Fundamentals.........................................................................................................123.2.3 Rotated BitBoards..................................................................................................................14

3.2.3.1 Non-Rotated BitBoard...................................................................................................143.2.3.2 Flipped BitBoard............................................................................................................153.2.3.3 The a1-h8 BitBoard........................................................................................................153.2.3.4 The a8-h1 BitBoard........................................................................................................16

3.2.4 Transposition Table................................................................................................................173.2.5 Evaluation Hash Table...........................................................................................................173.2.6 Pawn Hash Table...................................................................................................................183.2.7 Zobrist Hashing.....................................................................................................................18

3.3 Move Generation...........................................................................................................................203.3.1 Overview................................................................................................................................20

3.4 Position Evaluation Function........................................................................................................213.4.1 Overview................................................................................................................................213.4.2 Fundamental Evaluation Heuristics.......................................................................................22

3.5 Search............................................................................................................................................253.5.1 The Concept of the MiniMax Search Algorithm...................................................................263.5.2 Alpha-Beta Pruning with NegaMax Search...........................................................................283.5.3 Move Ordering.......................................................................................................................303.5.4 Quiescence Search.................................................................................................................313.5.5 Iterative Deepening Search....................................................................................................313.5.6 Principal Variation Search.....................................................................................................323.5.7 History Heuristic....................................................................................................................333.5.8 Killer Heuristic......................................................................................................................333.5.9 Futility Pruning......................................................................................................................33

iii

3.5.10 Razoring...............................................................................................................................333.5.11 Static Exchange Evaluation.................................................................................................34

4. Chess Program Implementation Solution............................................................................................354.1 Architectural Overview Diagram..................................................................................................354.2 Description of Application Modules.............................................................................................36

5. Engine Components.............................................................................................................................375.1 Engine Components Diagram.......................................................................................................375.2 Description of Engine Modules.....................................................................................................38

6. Graphical User Interface......................................................................................................................407. Conclusion and Evaluation..................................................................................................................41

7.1 Evaluation of Objectives...............................................................................................................417.2 Suggested Further Work................................................................................................................41

8. References and Bibliography...............................................................................................................428.1 General Chess References.............................................................................................................428.2 Chess Programming Related Artificial Intelligence References...................................................428.3 Internet References........................................................................................................................438.4 Open Source Chess Engine References.........................................................................................44

Appendix A..............................................................................................................................................44Appendix B..............................................................................................................................................47Appendix C..............................................................................................................................................49Appendix D..............................................................................................................................................52

iv

List of FiguresAlpha-Beta algorithm with Transposition Table......................................................................................19The Evaluation algorithm.........................................................................................................................21Calculate material balance.......................................................................................................................22 Example piece-square table for the Pawn pieces....................................................................................24Example piece-square table for the Knight piece....................................................................................24The MiniMax search algorithm................................................................................................................27NegaMax algorithm with Alpha-Beta pruning.........................................................................................29The Principle Variation Search algorithm................................................................................................32

v

List of IllustrationsThe Chess Game Tree..............................................................................................................................25Application Architectural Overview Diagram.........................................................................................34Engine Components Diagram..................................................................................................................36Application Graphical User Interface......................................................................................................39FEN Initial Position Board Diagram........................................................................................................50

vi

1. Introduction

1.1 Project Motivation

Chess is an exercise of infinite possibilities for the mind, one which develops mental abilities used throughout life: concentration, critical thinking, abstract reasoning, problem solving, pattern recognition, strategic planning, creativity, analysis, synthesis, and evaluation, to name a few. For AI purposes, chess provides a good backdrop for testing well established as well as new strategies for decision making. It is relatively easily defined as far as moves and victory conditions, yet very complex as far as strategy and tactics, making the game far from trivial. Chess clearly is an ideal way to study AI decision making and problem solving because it is a closed system with clearly defined rules.

Chess is a well known game, the origins of which can be traced back to India over 1500 years ago1, that has fascinated both chess players and computer scientists for a long time. Chess has always been at the heart of computer science and artificial intelligence. Creating a chess-playing program is extremely interesting for many reasons. For one, it is a remarkably large challenge requiring a lot of research, planning, coding, and testing. Consequently, learning the fundamentals of computer chess requires a thorough knowledge of many different aspects of a computing, from low-level bit manipulation to high-level artificial intelligence algorithms.

For these reasons, my main motivation for this project is to thoroughly understand the essential aspects of chess programming in order to enable me to develop an original chess playing program - engine, application and gui - that would use a carefully selected combination of AI techiques to play a relatively strong game.

1 Averbakh, Yuri,Kasparov, Garry. A History of Chess: From Chaturanga to the Present Day

1

1.2 Objectives

Extensive research has been conducted into computer chess, developing techniques that enable computers to not only play a legal game, but to excel at it, consistently defeating even the strongest human chess grandmasters.

My objectives for this project, therefore, are to study AI methods, algorithms and techniques used in modern chess engines design and development in order to create an overview of the common knowledge regarding this area of computing, and then applying this understanding to develop my own version of a chess engine that would be part of a web based chess application.

This implementation will include an opening book to be an integral part of the chess engine. In other implementation, usually the graphical chess client interface takes care of managing the opening book. Using an opening book would greatly improve the program’s performance in the initial phase of the game – which would then extend into an improved mid-game.

Lastly, this web based chess application implementation provides a graphical user interface to enable the player implementation to conveniently interact and test the game engine.

2

1.3 Report Overview

This work is divided into eight chapters. The first chapter, this one, presents the motivation and objectives for this project.

The second chapter provides a general background overview of the chess domain and a gentle chess programming introduction.

The third chapter, which is the main one, describes the chess engine architecture together with AI algorithms and techniques used. Algorithms, heuristics that are not implemented are mentioned in the discussion but not discussed in detail.

Chapters 4. and 5. provide the overall diagrams describing the application as well as the chess engine components and how they interact with each other.

Chapter 6. shows the application GUI.

Chapter 7. finalizes this report with conclusions and suggested further work.

For references used in this work, see Chapter 8. for details.

Finally, the appendices provide additional informations to the interested reader, such as the rules of chess, FEN board position representation, detailed discussion of the algebraic notations used in chess, etc.

3

2. Background

2.1 Description of the Chess Game Domain2

Chess is a board game for two players in which the players move pieces on the board in an attempt to subdue the other player. Each piece has different capabilities and limitations, and the number of possible moves at any stage of the game is usually fairly large making evaluation of moves a non-trivial problem.

One uses chessmen of a light colour (called White) while those of the other are usually of darker shade (called Black). The board is a square divided into 64 smaller alternating white and black squares. There exists a system of notation describing the situation on the board and the movement of the pieces. In this system, the vertical columns of squares called files are lettered from left to right: a, b, c, d, e, f, g and h. The horizontal rows of squares are called ranks and are numbered from 1 to 8. Each square, therefore, has its own letter and number.

At the start of the game each side has a King, a Queen, two Rooks, two Bishops, two Knights, and eight Pawns, thus the two sides have material equality. The Queen and Rooks are major pieces. The Bishops and Knights are minor pieces3. The chessmen are designated by the following contractions: King-K; Queen-Q; Rook-R; Bishop-B; Knight-N; Pawn-P. In game notations the abbreviation 'P' which stands for a Pawn is usually omitted.

The players take turns in making moves, with White always starting the game. A player cannot move a piece to a square occupied by one of his own pieces. The Knight is the only piece that can leap over a square occupied by another piece. The Rook can move any number of squares along a file or rank. The Bishop can move any number of squares along a diagonal. The Queen can move any number of squares along a file, rank or diagonal. The King can move only one square in any direction on a rank, file or diagonal. Finally, the Pawn, unlike the other pieces mentioned above, can only move one square ahead on its file at a time. From its initial position, it can move two squares ahead at once.

The piece making a move can capture an enemy piece standing in its way. That piece is removed from the board and its square is occupied by the piece making the move. The Pawn can capture obliquely, only one square along a diagonal.

The aim of the game is to capture the opposing King. This is called to 'checkmate' the King.

2 Sokolsky, Alexei. Your First Move. Raduga Publishers, 19813 https://en.wiktionary.org/wiki/Appendix:Glossary_of_chess

4

2.2 Chess Programming Introduction

2.2.1 Overview

Chess is by far the most well studied strategic game in the field of computer game playing. Many advances in computer science have resulted from this research, including developments in problem specification, exhaustive search techniques, parallel algorithms, etc.

The first who described an idea of creating a complete chess program and laid some theoretical foundations was Claude Shannon.4 Shannon considered chess as an interesting test field for developing concepts that today are generally assigned to the area of artificial intelligence, operating on symbols rather than numbers only, need for making choices instead of following a well-defined line, working with solutions that can be valued not only as good or bad, but also assigned some quality from a given range.

Chess is categorized as two-player deterministic zero-sum game - having no stochastic elements such a dice rolls or hidden variable - with perfect information, where perfect information means that a game has perfect information at any point in time, only one player makes a move, and knows all the actions that have been made until then. This is why all chess programs can all rely on search algorithms that make practical use of the MiniMax5 algorithm.

Game of chess is finite, as piece movement rules indicate a finite number of legal moves at any time. In addition, the threefold repetition rule ensures the game's finite length. This is because the number of legal chess positions is finite. Consequently, after certain number of moves the same position must appear on the chessboard for the third time.

Search techniques have proven to be quite effective for many of these games, provided the search space is relatively small, or can be restricted with good heuristics. To formalize, there is a fixed start state, various actions that can be taken at each state, a deterministic successor for each action taken in a state and several end states. The end state has a utility value associated with it based on whether it is a win, loss or draw. This game structure can be conveniently represented as a tree with the nodes representing the states and the edges representing actions; each alternating level of nodes corresponds to a player turn.

Move ordering is a critical part of the search. With a bad move ordering, alpha-beta performance could degenerate to a no pruning situation, where the quantity of moves searched would then explode, thereby reducing the search depth. Move ordering operates on two levels. The higher level relates to phased move generation - for example, the hash move. If hash move is available, then it is searched first. Given some luck, the hash move might cause a cut-off, thus avoiding the need to generate further moves. If there is no hash move, or the hash move doesn't cause a cut-off, the search engine then generates captures and promotions,and finally quiet moves as a last resort.

4 Shannon, C. Programming a Computer for Playing Chess. , 19505 See section 3.5.1 The Basic MiniMax Search Algorithm for the detailed discussion regarding this algorithm

5

During the course of a search, one can encounter the same position at various depths in the game tree. It is prudent to store information obtained during the original encounter to avoid duplicating work - therefore all chess engines have some kind of transposition table. One never stores an entire chess position into the transposition table, as this would be too slow to locate. Instead, one typically creates a Zobrist hash key as an index into the hash table. This key maps the entire chess board, plus castling rights and en-passant square into a 64-bit long integer. My program only uses the Transposition Table to store the 'best move', which was the move noted to cause a cutoff the last time the position was found.

2.2.2 Game Complexity

Game of chess can be conveniently expressed by a tree that has the starting position called the root, branches representing all legal moves and nodes corresponding to board positions.

As a side note, the game tree of chess is actually a directed acyclic graph. It is not cyclic because the rules of chess prohibit eternal loops and as a result, each sequential position is unique. Because different paths may lead to the same position it is a graph and not a tree. But since such a graph can be represented as a tree, it is often referred to as a tree and search methods treat it as such.

The initial chess position is the root, and we assing depth 0 to it; since White makes the first move, edges from nodes of even depth to nodes at odd depth represent moves of White, while from odd to even depth, Black’s.

The terms 'move' and 'ply' are not synonymous6; a move consists of a turn by each player while a ply is a 'half-move' - a turn taken by either White or Black. In a non-computer chess programming context, however, a move is often considered identical to a ply.

Number of edges from the root to nodes depth 1 in the initial position is 20 since there are twenty legal moves White can make initially. From each node at depth 1 to node at depth 2 are also 20 edges because of the twenty legal Black’s responses. During the middlegame there are approximately 35 legal moves exist for each side. If we consider a tree of depth d, and branching factor b, then number of all its nodes n - excluding the root node - can be calculated with formula n = b^d.

Length of an average chess game can be considered to be about 40 moves. 40 moves gives 80 plies. As the branching factor from each node is 35 on average, we can estimate the number of nodes in the chess game tree corresponding to a to be n ≈ 35^80 ≈ 3.35*10^123.7 This number is also called the Shannon's Number, to honor the father of computer chess.To conclude, the entire game tree cannot be searched in most positions in a reasonably length of time. Consequently, the general approach to writing chess programs is to explore only a sub-tree of the chess game tree.

6 https://en.wikipedia.org/wiki/Ply_(game_theory)7 Shannon's Number

6

2.2.3 Pruning Requirements

As the entire game tree cannot be searched in most positions some heuristics must be developed. The general approach to developing a chess search algorithm is to explore only a sub-tree of the game tree. This sub-tree is created by pruning everything below certain depth. There is a value assigned to the root according to some heuristic evaluation function. The value of this evaluation function is found at some intermediate nodes of the total game tree. These nodes are leaves of the considered sub-tree.

There are two basic methods of reducing amount of work needed to determine the best move in a given position. These two strategies were first described by Shannon in his original work.8 He called them strategy type A and type B. Strategy type A basically browsing through the tree for a certain depth, calculating the evaluation function at leaf nodes of the sub-tree. The best move with the highest evaluation value can be determined in that way.

It was a discovery of an Alpha-Beta algorithm that allowed more efficient analysis of the tree by means of so called backward pruning. The basic idea of the Alpha-Beta algorithm is to ignore branches that cannot possibly influence the choice of the 'best move' using data already obtained.

Strategy type B, also known as forward pruning, tries to use expert knowledge to prune moves that seem poor and analyse only a few promising branches. This type of strategy is not used in modern chess algorithms that much and is not explored any further in this work.

8 Shannon, C. Programming a Computer for Playing Chess. , 1950

7

2.2.4 Chess Engine Functional Considerations

To design and build the core chess engine, the following functional requirements have to be addressed first. Details will be described in later sections.

1. Implement a communication protocol (UCI is used in my implementation)9

2. Define a way to represent the board in memory developing an efficient chess board position representation that allows for the quick execution of a given move.

3. Represent the state of the game in a structure that contains everything relevant to a position. Such as where each piece is; which player is to move in the current position; positions encountered since the start of the game to enforce draws by threefold- repetition; plies elapsed since a capture or pawn move to enforce draws by fifty move rule; castling status for each player; possibility of an en-passant capture.

4. Provide for move generation. Given the current state of the game, generate and return a list of legal or pseudo-legal moves.

5. Finding the legal moves in a given position. Legality may also be checked at the graphical chessboard level.

6. Parsing and interpreting moves from an opponent.

7. Evaluate a position to determine which side has material, positional and/or tactical advantage. In other words, the evaluation function is given the current state of the game and returns a number representing how good the position is for the player to move.

8. Searching forward to determine the move that gives the engine the best position to move.

9. Play the best move found using the Search function by interacting with the board.

10. Determining when the game has ended.

9 http://wbec-ridderkerk.nl/html/UCIProtocol.html

8

2.2.5 Chess Engine Components

The considerations in the sub-section above can be distilled into five functional requirements that may be implemented as the five essential engine components:

1. A communication protocol interpreter.

2. A position representation mechanism that encapsulates the state of the game.

3. A move generator to generate all legal moves given a position. To be exact, the move generator within the program will initially create a list of pseudo-legal moves - in other words, moves that are fundamentally legal, but may breach rules of check – before checking these moves using a specific function.

4. A position evaluator function to rank these legal moves by optimality. This is the most complex part of the program from a chess point of view. The most significant aspect of the evaluation function is the material balance.

5. A search component to help a static evaluator to analyze the position at various depths.

9

3. Chess Engine Architecture

3.1 Communication Protocol

A chess engine communication protocol allows chess engines to communicate with a graphical user interface. They do this by passing specific text commands to the GUI to represent the current status of the engine and accepting text commands from the GUI to act upon. For my chess engine I have implemented a working subset of the UCI protocol.10

3.2 Board Position Representation

Board representation is the usage of some data structure to contain information about a chessboard. It encapsulates the state of the game at a given time. Therefore, choosing a good board representation is important for fast move generation.

After implementing a board representation and the UCI protocol, we could interpret an FEN11 string (a standard notation for specifying chess positions) from the GUI and setup the board at a given position.

For instance, the command

position fen rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1

would tell my engine to setup its board at the initial position of a chess game.

10 See Appendix B for the implemented functions of the UCI protocol.11 See Appendix D for the description of the FEN notation

10

3.2.1 Internal Board Model

A method of representing where each piece stands on the board must be decided on. There are a vast number of ways this can be done, but in general there are two approaches, square-centric and piece-centric. In a square-centric representation, we state what each square contains. In a piece-centric representation, we state where each piece stands and assume any other squares are empty.

In this implementation of the game of chess I have decided to take advantage of the speed and efficiency of the BitBoard as my means to represent the internal chessboard. BitBoards are a piece-centric board representation that remains a sophisticated choice to date.

A BitBoard is a set of 64 boolean values that represent, square by square, some specific aspect of a chess position, at a rate of 1 bit per square. For example, a BitBoard might contain the set of squares occupied by Black Pawns, another set of squares to which a Queen on e3 can move, or the set of White pieces currently attacked by Black Knights, etc. BitBoards are versatile and allow fast processing, because many operations that are repeated very often in the course of a chess game can be implemented as 1-cycle logic operations provided we use a 64-bit processor. This, consequently, enables the various operations to be performed using a single clock cycle.

The advantages of BitBoards are numerous. The main advantage is the exceptionally fast move generation that becomes possible. We may generate the moves for each piece type in parallel by simply bit-shifting the BitBoard the appropriate number of spaces. BitBoards are optimized for computers because they work directly with bit-wise operations. Bitwise operations are simple processes that a computer processor uses to work with binary numbers. The five major bitwise operations are the bitwise AND, bitwise OR, bitwise XOR, bitwise left shift, and bitwise right shift.

Using the BitBoard and the precomputed database, it is very fast to generate moves and perform many operations using processor’s bitwise operations. For example, verifying whether a White Queen is checking Black’s King looks as follows12:

1. Load the 'White Queen position' BitBoard.

2. Use it to index the database of BitBoards representing squares attacked by Queens.

3. Logical AND that BitBoard with the one representing 'Black King position'. If the result is non-zero, then White Queen is checking Black’s King.

12 Laramee, F. D. Chess Programming Part VI: Evaluation Functions, http://www.gamedev.net/reference/articles/article1208.asp

11

3.2.2 BitBoard Fundamentals

To begin, in the more general case, we have to represent 6 types of pieces for 2 different colors, resulting in 12 total BitBoards minimum. However, most programs also use three extra bitboards, representing White pieces, Black pieces, and all pieces. Thus, we need 15 BitBoards to represent a chess position.

The BitBoard structure is made up of twelve 64-bit bit-sets; one for each type of piece. They are visualized as being stacked on top of each other. Here, the 64 bit number is formatted in to 8 rows of 8 bits each, like a chessboard.

The full list of chess BitBoards at the beginning of a game are:

wPawns wKnights wBishops wRooks wQueen wKing 00000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 0000000011111111 00000000 00000000 00000000 00000000 0000000000000000 01000010 00100100 10000001 00010000 00001000

bPawns bKnights bBishops bRooks bQueen bKing 00000000 01000010 00100100 10000001 00001000 0001000011111111 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00000000 0000000000000000 00000000 00000000 00000000 00010000 00000000

The wPawns BitBoard, for example, is represented in memory as:0000000011111111000000000000000000000000000000000000000000000000

Then, we can OR these together to have a the complete chess set as initialized at the beginning of the game as:

AllPieces1111111111111111000000000000000000000000000000001111111111111111

12

BitBoard notation uses the logic operators (AND,OR,NOT). To represent the Pawn structure on the chess board, for example, the program simply precomputes an array of 64 BitBoards, one for each valid square a Pawn can occupy, plus 16 squares where a Pawn will never exist.

For the BitBoard for square 28 (e4), the program simply set bits for the nine squares listed, and save this BitBoard for use anytime it wants to ask if the White Pawn on e4 passed. To answer the question, the program simply AND this BitBoard with the locations of all Black Pawns - the Black Pawns BitBoard - and if the result is non-zero, the program knows that a Black Pawn stands on one of the squares that makes this White Pawn not passed.

Or, to generate all possible White Pawn moves I take the Pawn BitBoard and shift it right 8 bits, moving the bits 8 places to the right, and filling the 8 most significant bits on the left with zeros. This is the same as a divide by 2^8, or 256. What this gives is a list of all the places on the board that are exactly one square in front of a White Pawn.

All legal moves from a given position for Knights and Bishops are also easy to calculate. The program simply generates an array of BitBoards which, for each square in the board, give the available moves for the Knight or Bishop. For example, KnightMovesFrom[c2] might look like this:

0000000000000000000000000000000001010000100010000000000010001000

BishopMovesFrom[d4] might look like this:

0000000110000010010001000010100000000000001010000100010010000010

13

3.2.3 Rotated BitBoards13

In order to use the BitBoard representation for efficient move generation for the sliding pieces (Bishops, Rooks, Queens), a more advanced board representation is necessary.For non-sliding pieces such as Knights and Kings, all possible moves for all the squares of a chess board are pre-computed during the initialisation of the program and stored in arrays indexed by the 'from square' field. This technique works fine for the non-sliding pieces, however there are difficulties encountered when applying this strategy for the sliding pieces .

Computing all possible moves for all squares for the sliding pieces is not as straightforward as for the non- sliding pieces, because the possible moves for a sliding piece will depend on the configuration of the diagonal, rank and file the given piece is standing. The idea of rotated BitBoards is to store the BitBoards that represents the occupied squares not only in the traditional, unrotated way, but also in a rotated fashion. The necessary bits representing diagonals, ranks and files are ordered in those rotated BitBoards, as needed for the move generation. The rotated BitBoards are updated incrementally during the search, when a move is made or undone.

3.2.3.1 Non-Rotated BitBoard

#7 #6 #5 #4 #3 #2 #1 #0 Bit/Byte

a8 b8 c8 d8 e8 f8 g8 h8 #7

a7 b7 c7 d7 e7 f7 g7 h7 #6

a6 b6 c6 d6 e6 f6 g6 h6 #5

a5 b5 c5 d5 e5 f5 g5 h5 #4

a4 b4 c4 d4 e4 f4 g4 h4 #3

a3 b3 c3 d3 e3 f3 g3 h3 #2

a2 b2 c2 d2 e2 f2 g2 h2 #1

a1 b1 c1 d1 e1 f1 g1 h1 #0

13 http://people.csail.mit.edu/heinz/dt/node8.html and https://cis.uab.edu/hyatt/bitmaps.html

14

3.2.3.2 Flipped BitBoard

The flipped BitBoard is very efficient to generate moves along files for Rooks and Queens.

#7 #6 #5 #4 #3 #2 #1 #0 Bit/Byte

a8 a7 a6 a5 a4 a3 a2 a1 #7

b8 b7 b6 b5 b4 b3 b2 b1 #6

c8 c7 c6 c5 c4 c3 c2 c1 #5

d8 d7 d6 d5 d4 d3 d2 d1 #4

e8 e7 e6 e5 e4 e3 e2 e1 #3

f8 f7 f6 f5 f4 f3 f2 f1 #2

g8 g7 g6 g5 g4 g3 g2 g1 #1

h8 h7 h6 h5 h4 h3 h2 h1 #0

3.2.3.3 The a1-h8 BitBoard

The a1-h8 bitboard is used to generate diagonal moves in direction of the a1-h8 diagonal for Bishops and Queens. It must be assured that the given piece cannot jump over the edge of the board and re-enter on the other side; this would constitute an illegal move. This must be taken care of during the initialization of the BitBoards, where all the legal diagonal moves in the direction of the a1-h8 diagonal are properly encoded into the a1-h8 moves array. The edge of the board is indicated by vertical bars.

#7 #6 #5 #4 #3 #2 #1 #0 Bit/Byte

a8 | b1 c2 d3 e4 f5 g6 h7 #7

a7 b8 | c1 d2 e3 f4 g5 h6 #6

a6 b7 c8 | d1 e2 f3 g4 h5 #5

a5 b6 c7 d8 | e1 f2 g3 h4 #4

a4 b5 c6 d7 e8 | f1 g2 h3 #3

a3 b4 c5 d6 e7 f8 | g1 h2 #2

a2 b3 c4 d5 e6 f7 g8 | h1 #1

a1 b2 c3 d4 e5 f6 g7 h8 #0

15

3.2.3.4 The a8-h1 BitBoard

The a8-h1 BitBoard is also used to generate diagonal moves in the direction of the a8-h1 diagonal for Bishops and Queens.The edge of the board is again marked with the vertical bars.

#7 #6 #5 #4 #3 #2 #1 #0 Bit/Byte

a8 b7 c6 d5 e4 f3 g2 h1 #7

a7 b6 c5 d4 e3 f2 g1 | h8 #6

a6 b5 c4 d3 e2 f1 | g8 h7 #5

a5 b4 c3 d2 e1 | f8 g7 h6 #4

a4 b3 c2 d1 | e8 f7 g6 h5 #3

a3 b2 c1 | d8 e7 f6 g5 h4 #2

a2 b1 | c8 d7 e6 f5 g4 h3 #1

a1 | b8 c7 d6 e5 f4 g3 h2 #0

16

3.2.4 Transposition Table

An auxiliary type of representation in chess programming is transposition tables. The idea is that there are often many ways to reach the same position. For example, it does not matter whether we play two moves in either order, the game ends up in the same state, this is called transposing. A position resulting from a number of positions would not need to be re-evaluated again if a transpose of this position was searched and evaluated before and recorded in a transposition table.

A transposition table is then a repository of past search results. This is implemented as a hashing scheme to detect positions in different branches of the search tree that are identical. It works by creating a 'hash-value' for the current board position based on a random table of large integers. The expectation is that each board position has a separate hash-code.

There is a small possibility of hash collisions, meaning that two different positions will generate the same Zobrist key. If that happens, then the associated 'best move' could possibly be illegal in the current position. Therefore, before performing a search on a hash move, I call a method to verify the move to make sure that this hash move can be legally applied to the current position.

If a search arrives at a position that has been reached before and if the value obtained can be used, the position does not have to be searched again. If the value cannot be used, it is still possible to use the best move that was used previously at that position to improve the move ordering. A transposition table can save up to a factor 4 on tree size and thus on search time.

Because of the exponential nature of tree growth, this means that maybe one level deeper can be searched in the same amount of time. As soon as a search is completed then the transposition table is updated with the new results, storing the BitBoard hash key and the score which was returned. I also store the depth to which this position was searched. See Figure 1. for the Alpha-Beta algorithm with Transposition Table.

To conclude, using a transposition table can effectively give near-perfect move ordering and hence, very efficient pruning.

3.2.5 Evaluation Hash Table

The purpose of using a dedicated evaluation table is to store previously evaluated board position scores for future use. The idea here is very similar to that of the transposition table.If a position to be looked at has already been evaluated and its score recorded in the evaluation hash table, then the program logic does not need to re-evaluate the same position over again. All it has to do is, given the position hash, to look up the previously evaluation score for that position and return it to the evaluator to be included in the final score.

17

3.2.6 Pawn Hash Table

Pawn evaluation is one of the most time consuming components of evaluation, but its results can be stored for further reuse. It is done by means of a pawn hash table, that works similarly to the main transposition table. The hash key is calculated using the Zorbrist method, but considers pawns only. The hash table stores values of the pawn structure evaluation. As pawn structure does not change often, table probes achieve in games hit rate of 97-99%. It means that the time actually spent by program in the pawn evaluation routine is negligible .

3.2.7 Zobrist Hashing

Zobrist14 introduced a hashing method that makes it possible to store a chess position in a 64 bit number. The idea is to use a 3-dimensional array with indexes for piece, color and position, filled with random numbers (64 bit). The array could be accessed like:

zobristArray[King][Black][e8]

where King, Black and e8 are enumerated values in their ranges (0-5 for the six types of pieces, 0-1 for black and white, 0-63 for the squares respectively).

A hash key for a chess position can be created by iterating over all pieces on the board and binary XOR the hash key (initially 0) with the value from the 3 dimensional array like in the example above. Zobrist showed that with a good quality of random numbers, the chance that two chess positions would result in the same hash key can be ignored. Another good property of this method, is that a new hash key - after a change in a position as the result of a move - can be easily calculated by just applying XOR to the old value with the old 'zobristArray' value of the piece that is moved, with the new 'zobristArray' value of the piece that is moved. This way, the move e2-e4 can be expressed in terms of the new hash key like this:

hashKey ^= zobristArray[pawn][white][e2]; hashKey ^= zobristArray[pawn][white][e4];

Because of the reversibility of the XOR, this method is guaranteed to give the same result as recalculating the hash key completely by the first method. In order to incorporate all chess rules properties into the hash key, also extra random values must be used to represent the side to move and short and long castling values.

A typical entry in a transposition table would store the hash key together with the value that comes with the position. This can be an exact value – the value of a leaf in the search space, or the value that resulted in a cut-off: an upper bound or a lower bound. Also, the depth of the node in the search space must be stored, because a transposition at a depth that is smaller than the current search depth is worthless.

14 A Zobrist, A New Hashing Method with Application for Game Playing, University of Wisconsin, Madison, Technical Report 88, April 1970

18

19

int alphaBetaTT(Board board, int depth, int alpha, int beta){ int value; Entry tte = getEntry(board.getHashKey()); if (tte != null && tte.depth >= depth) { if (tte.type == EXACT_VALUE) return tte.value; if (tte.type == LOWER_BOUND && tte.value > alpha) alpha = tte.value; else if (tte.type == UPPER_BOUND && tte.value < beta) beta = tte.value; if (alpha >= beta) return tte.value; } if (depth == 0 || board.isEnded()) { value = evaluate(board); if (value <= alpha) storeEntry(board.getHashKey(), value, LOWER_BOUND, depth); else if (value >= beta) storeEntry(board.getHashKey(), value, UPPER_BOUND, depth); else storeEntry(board.getHashKey(), value, EXACT_VALUE, depth); return value; } board.getOrderedMoves(); int best = -MATE_VALUE - 1; int move; Board nextBoard; while (board.hasMoreMoves()) { move = board.getNextMove(); nextBoard = board.makeMove(move); value = -alphaBetaTT(nextBoard, depth - 1, -beta, -alpha); if (value > best) best = value; if (best > alpha) alpha = best; if (best >= beta) break; } if (best <= alpha) storeEntry(board.getHashKey(), best, LOWER_BOUND, depth); else if (best >= beta) storeEntry(board.getHashKey(), best, UPPER_BOUND, depth); else storeEntry(board.getHashKey(), best, EXACT_VALUE, depth); return best;}

Figure 1. Alpha-Beta algorithm with Transposition Table

3.3 Move Generation

3.3.1 Overview

Given a set of BitBoards corresponding to a chess position, we must return a set of integers that represent all legal moves available.

The board state has all the information required to generate the moves. Fundamentally, there are two ways to execute the move generation:

1. Generating all the moves possible for all the pieces on the board for the player.

2. Choosing only the legal moves from the list of moves generated. For example, a move is legal if the King will not be in check after the move

In any given situation, a player may have around 35 legal moves to choose from, some good, some not so good. For human players, it is easy to characterize the majority of these moves as bad, but coding that information into a chess program has proven to be difficult. So, today's chess engines rely on brute force solution for most of the time. If the engine can analyze all possible moves fast enough and predict their consequences far down the search tree, then it will discover a good move eventually.

Move generation can be categorized into the following three categories15:

1. Selective generation: Examine the board, come up with a small number of likely moves and discard everything else.

2. Incremental generation: Generate a few moves, hoping that one will prove so good or so bad that search along the current line of play can be terminated before generating the others.

3. Complete generation: Generate all moves, hoping that the transposition tables will contain relevant information on one of them and that there will be no need to search anything at all.

Incremental and full move generation are commonly used in today's chess engines.A pseudo-legal move is similar to a legal move except that we do not ensure the King is not left in check. Dropping this constraint simplifies and accelerates the move generation algorithm considerably. Executing a move involves updating the piece BitBoards and various statistics maintained in the game state object.

Good move ordering remains paramount to take into account – it may lead to many branches being skipped by the search routine. Therefore, move generator should return moves according to the 'first-best' rule wherever possible, reducing the need for reordering later.16

15 Laramee, F. D. Chess Programming Part VI: Evaluation Functions, http://www.gamedev.net/reference/articles/article1208.asp

16 See section 3.5.4 Move Ordering for more details

20

3.4 Position Evaluation Function

3.4.1 Overview

Before we can construct a search algorithm, we must have something to search for. So, once the engine generates the game search tree, then it needs to evaluate the generated board positions. The evaluation function is a central part of any chess engine; it returns a number that is obtained as a linear combination of various features considered important to the engine's designer. The unit used in evaluation is typically the centipawn - one pawn = 100 units.

This heuristic evaluation function is that part of the engine that allows comparison of positions in order to find a good move at the given position. It is not guaranteed that value of the function is the absolutely optimal. Creating a good evaluation function requires good understanding of the game of chess and detailed work of the program.

There are two basic aspects of the chess position that must be considered: material and positional. In most positions, it is material that plays dominant role in value of the evaluation function.

As mentioned above, the evaluation function is most often implemented using linear combinations. The approximation is composed of features of the game that the program designer believes to be relevant, and weight coefficients for each feature which indicates how much that feature should contribute to the total evaluation of the state. The basic algorithm is as follows:

At its core, the evaluation function is just a collection of heuristics. If a particular aspect of chess is often a good one, we assign it a bonus to be applied whenever the feature is present. Correspondingly, if a feature is often a bad one – for example, exposed King in the middlegame, doubled pawns, etc. - we assign it a penalty.

A good evaluation function should take the following basic heuristics into account.

21

P(x; w) = SUMf ( wf * f (x) )

where, f (x) = feature extracted from the board state x wf = weight associated with the corresponding feature f

Figure 2. The Evaluation algorithm

3.4.2 Fundamental Evaluation Heuristics

Different game positions can be rated and given a numeric value by an evaluation function. This value is then used in the search to choose which move is the best at some given point in the game.

Material Balance

The most obvious and important heuristic is the amount of material controlled by each side.Material balance is an account of which pieces are on the board for each side, where each piece is assigned a numeric value, with King having an infinite utility value, etc. Computing material balance is, therefore, straightforward; a given side’s material value is equal to sum of weights of each piece times the number of each piece on a side.

The basic material score for each piece is as follows. This arrangement using centi-pawns allows the engine to fine tune the material evaluator to be more responsive because it gives the program enough resolution to use integers which is faster than floating point operations.17

1. Pawn = 100

2. Knight = 300

3. Bishop = 300

4. Rook = 450

5. Queen = 90018

17 https://en.wikipedia.org/wiki/Chess_piece_relative_value18 These values are in centi-pawns. See 3.4.1 Overview

22

double evaluate(Game game){

int score = 0;score += total value of White's piecesscore -= total value of Black's pieces

if (White to move)return score;

else return score * -1;

}

Figure 3. Calculate material balance

Mobility

The second most important one is mobility. Mobility is the total number of moves that can be legally made by one’s pieces in a given state.

Board Control

A close concept to mobility is board control. In chess, a side controls a square if it has more pieces attacking it than the opponent. It is usually safe to move to a controlled square, and bad to move to one controlled by the opponent.

Piece Development

Development measures how many pieces are moved away from their original positions. For example, a well know heuristic is that Bishops and Knights should be brought into action as quickly as possible, that the King should castle early and that Rooks and Queens should stay put until it is time for a decisive attack. Central position measures the pieces in the center of the board. So, if a player has more pieces in the center of the board then one's opponent, then one is more advantageous.

Pawn Formation

Another heuristic that I found prevalent in literature was Pawn formations. As an example, two or more pawns of the same colour on the same file are usually bad, because they hinder each other’s movement. Most engine implementations penalize three types of weak Pawn structure .

1. Isolated

2. Doubled

3. Backward Pawns

King's Safety

King safety is something all sophisticated chess engines try to ensure, especially, in the opening and middle game. It is generally useful to maintain a strong defensive shield for the King during a game of chess. The engine should prevent the opponent from placing pieces in positions that are too close to the King. In the early phase of the game, castling is the best way to ensure the King's safety.

23

Piece-Square Table

Piece-square tables encode the concept of pieces being particularly effective on certain squares and ineffective on others.

To encourage a chess program to put its pieces in good squares is to use Piece-Square Tables. These tables are in fact simple arrays that give a given bonus (or penalty) for a piece on a specific square. For example, a piece-square table for Pawns would likely give bonuses for Pawns in the center and for those about to promote. See Figures 2. and 3. for example piece-square tables.19

19 https://chessprogramming.wikispaces.com/Simplified+evaluation+function

24

0, 0, 0, 0, 0, 0, 0, 0,50, 50, 50, 50, 50, 50, 50, 50,10, 10, 20, 30, 30, 20, 10, 10, 5, 5, 10, 25, 25, 10, 5, 5, 0, 0, 0, 20, 20, 0, 0, 0, 5, -5,-10, 0, 0,-10, -5, 5, 5, 10, 10,-20,-20, 10, 10, 5, 0, 0, 0, 0, 0, 0, 0, 0

Figure 4. Example piece-square table for the Pawn pieces

-50,-40,-30,-30,-30,-30,-40,-50,-40,-20, 0, 0, 0, 0,-20,-40,-30, 0, 10, 15, 15, 10, 0,-30,-30, 5, 15, 20, 20, 15, 5,-30,-30, 0, 15, 20, 20, 15, 0,-30,-30, 5, 10, 15, 15, 10, 5,-30,-40,-20, 0, 5, 5, 0,-20,-40,-50,-40,-30,-30,-30,-30,-40,-50,

Figure 5. Example piece-square table for the Knight piece

3.5 Search

The move generation step outputs all the valid moves corresponding to a given board state. The best move has to be chosen from the set of valid moves.

To enable search algorithm to find good moves, the game must be explicitly modeled as a graph representing the state space of the game. This state space, sometimes referred to as search space, is the collection of all possible board configuration, or states, that can arise in the game. Every state is represented by a node in the graph, where all the possible moves in each state are represented by edges to the successive nodes. The number of possible moves at a given node is called the branching factor. These graphs are then treated as search trees by the search algorithms, where the current state of the game, or board position becomes the root node of the tree, which is then expanded to each level by traversing the edges to each of the node’s siblings.

In chess, no computer has been able to calculate the game entire tree. The search space is just too huge. What a chess engine tries to do is generate the board position tree for moves into the future. Assuming that there are about 20 possible moves for any board position, a five level tree contains 3,200,000 board positions.

Figure 6. The Chess Game tree20

20 Image credit: http://electronics.howstuffworks.com/chess1.htm

25

3.5.1 The Concept of the MiniMax Search Algorithm

The MiniMax search algorithm finds the best move possible at a given state by searching the game tree to a given depth. A MiniMax search tree is constructed by the player that is about to move, expanding all nodes and their siblings recursively until it reaches a terminal node where no moves are possible. At a terminal node the game is over and the true outcome of the game can be observed and converted to a numerical value, such as -1 for losing, 0 for draws and 1 for winning, or some other numerical values.

This value is then used to perform a backup on the tree, selecting the maximum value for the player that is performing the search but the minimum value for its opponent. The two players are therefore often called Min and Max, which stems from the name and nature of the MiniMax theorem.

Unfortunately, constructing the whole game tree is only achievable in games with small state spaces. The complexity of MiniMax is directly proportional to the search space of size W^d, where W is the width of the tree and d the depth. The algorithm will visit every node – not only the leaves - so the number of nodes visited will be W^(d+1).

26

27

int minimax(Board board, int depth){

return maxLevel(board, depth);}

int max(Board board, int depth){ int value; if (depth == 0 || board.isEnded()) return evaluate(board); board.getMoves(); int best = -MATE_VALUE; int move; Board nextBoard; while (board.hasMoreMoves()) { move = board.getNextMove(); nextBoard = board.makeMove(move); value = minLevel(nextBoard, depth - 1); if (value > best) best = value; } return best;}

int min(Board board, int depth){ int value; if (depth == 0 || board.isEnded()) return evaluate(board); board.getMoves(); int best = MATE_VALUE; int move; Board nextBoard; while (board.hasMoreMoves()) { move = board.getNextMove(); nextBoard = board.makeMove(move); value = maxLevel(nextBoard, depth - 1); if (value < best) best = value; } return best;}

Figure 7. The MiniMax search algorithm

3.5.2 Alpha-Beta Pruning with NegaMax Search

The basic idea behind the Alpha-Beta Pruning algorithm is that, once the engine has a good move, it can quickly eliminate alternatives that lead to positional disaster. It reduces the number of tree nodes to evaluate by eliminating a move when at least one possibility was proved worse than a previously evaluated one.

This search algorithm is an enhancement to the basic MiniMax algorithm so that the algorithm does not have to search the entire game tree but only the parts that the players will be able to reach. The core idea of Alpha-Beta pruning, therefore, is based on the branch and bound principle. As we are searching (branching), we keep lower and upper bounds on each value we are trying to compute.

If the engine has already found a good move and search for alternatives, one refutation is enough to avoid it. No need to look for even stronger refutations. The algorithm maintains two values, Alpha and Beta. They represent the minimum score that the maximizing player is assured of and the maximum score that the minimizing player is assured of respectively.

The value Beta establishes an 'upper limit' which, when exceeded, causes Alpha-Beta to return a value early. If Alpha-Beta returns Alpha, then the position being evaluated was not deemed too good. Alpha changes value as it finds new positions that return better values.

The NegaMax algorithm is used to make the implementation of MiniMax or Alpha-Beta algorithms easier. It does so by making use of the following equivalence:

max{ min {x1, x2, ...}, min {y1, y2...}} = max { - max {-x1, -x2, .. }, - max { -y1, -y2 }}

Here, the x's and y's are the values of leafs of a tree. Therefore, this equivalence only holds when the evaluation results hold the zero sum property – which is the case with chess. Instead of having to alternate between Min and Max, the values only have to be negated from one level to the other (negation is the opposite value because of the zero sum property)

28

The algorithm is very similar to MiniMax – it includes two additional parameters sent to the function and cut-off tests. The parameters (Alpha and Beta) establish limits on the MiniMax value (they are modified as tree traversal follows), and cut-off tests serve to skip branches that lead to parts of the tree that cannot influence MiniMax value of the root.

Performance of the Alpha-Beta algorithm depends on ordering of the moves. If the moves are ordered according to the best-first rule then Alpha-Beta is able to cut-off maximum number of nodes.

29

int alphaBeta(ChessBoard board, int depth, int alpha, int beta) {

int value;

if(depth == 0 || board.isEnded()) {

value = evaluate(board); return value;

}

board.getOrderedMoves(); int best = -MATE_VALUE-1; int move; ChessBoard nextBoard;

while (board.hasMoreMoves()) {

move = board.getNextMove(); nextBoard = board.makeMove(move); value = -alphaBeta(nextBoard, depth-1,-beta,-alpha); if(value > best)

best = value; if(best > alpha)

alpha = best; if(best >= beta)

break; }

return best; }

Figure 8. Negamax algorithm with Alpha-Beta pruning

3.5.3 Move Ordering

Alpha-Beta pruning is most effective on strongly-ordered trees, where the best move is searched first, the second-best move is searched second, and so on up to the worst move which is searched last.

The speedup provided by Alpha-Beta pruning is related to the move ordering chosen. Good moves at a position are more likely to generate Beta cutoffs. In my implementation, I considered Pawn promotions and piece captures as more likely to be good moves and searched along these moves first.

There are more complicated move ordering heuristics such as using heuristics to decide which moves are more interesting among non-capture moves as well as ordering the captures themselves.

A basic move ordering scheme illustrated below:

Move using the Transposition TablePrincipal Variation (PV) move associated with the given plyCapturesQuiet promotions Other moves

30

3.5.4 Quiescence Search

The main problem with a fixed depth search is the so called ‘horizon effect’. Assume that the algorithm can search upto a depth of 5. Consider a position where, with best play, a Rook is lost after 5 moves. Suppose by sacrificing a Bishop, the chess engine can push the loss of the Rook two more moves. This is a worse move than giving up the Rook, since it leads to the loss of both pieces. However, because of the horizon effect, the computer is not able to see this and plays a worse move. This is the problem that quiescence search technique tries to solve.

The basic concept of quiescence search, therefore, is the following: once the algorithm has searched everything to a fixed depth, it continues each line of play selectively, by searching 'non-quiescent' moves only, until it finds a quiescent position, and only then apply the position evaluator function. Finding a 'quiet' position requires some knowledge about the game. For example, which moves are likely to cause a drastic change in the balance of power on the board? For chess, the material balance tends to be the overwhelming consideration in the evaluator, so anything that changes material is marked for action: captures - especially those of major pieces - and pawn promotions certainly qualify, while checks may also be worth a look just in case they might lead to checkmate.

So to summarize, once the algorithm reaches a leaf node in the search, instead of immediately returning an evaluation value it conducts a further selective search with the aim of reducing the position to one that is quiescent (quiet, that is). Once this has been achieved, it returns the evaluation as normal. Most engines consider the possibility of a capture or pawn promotion as indicative of a non-quiescent position.

Alpha beta pruning can be performed with quiescence search as well, with an Alpha value equal to the initial static evaluation of the board.

3.5.5 Iterative Deepening Search

The idea behind iterative deepening is to let the engine search at a shallow depth and iteratively deepen the search until it reaches a full depth. It is often a good strategy for the engine to search till depth d and update its best move before searching to a depth of d+1. This process continues until the allotted time has elapsed, at which point it abandons the search and return the best move found by the most recent completed iteration.So, with iterative deepening however, the search algorithm always has the results of the previous search depth.

Iterative deepening has the advantage that it facilitates move ordering. The program knows which move was best at the previous level of iterative deepening, and it searches this principal variation first at each new level. The extra time spent searching early levels is more than repaid by the gain due to accurate move ordering. Shallow searches provide a principal variation, which we may use to guide the deeper search. The moves in the principal variation are referred to as 'PV moves'.

31

3.5.6 Principal Variation Search

Principal Variation Searching (PVS) identifies the 'best move' at the current depth - the move that raises Alpha - and stores it in order for it to be found again at the next search depth. Most of the time this move remains the best move even as we increase our depth search, so after we find the principal variation at a low depth, we quickly find the best move at a high depth because we begin our search with the principal variation. Specifically, with perfect move ordering all moves outside the principal variation are worse than the principal variation.

32

int PVS(Board board, int depth, int alpha, int beta){ if(depth == 0 || board.isEnded()) return evaluate(board)(); board.getOrderedMoves(); int move; Board nextBoard; move = board.getNextMove(); nextBoard = board.makeMove(move); int i, temp, value = -PVS(nextBoard, -beta, -alpha, depth-1); while (board.hasMoreMoves()) { if(value >= beta) return value; if(value > alpha) alpha = value; move = board.getNextMove(); nextBoard = board.makeMove(move); temp = -PVS(nextBoard, -alpha-1, -alpha, depth-1); if(temp > value) { if(alpha < temp && temp < beta && depth > 2) value = -PVS(-beta, -temp, depth-1); } else value = temp; }}

Figure 9. The Principle Variation Search algorithm

3.5.7 History Heuristic

History heuristic is the technique of maintaining two 64x64 arrays - one for each side - that represents the 'from' and 'to' squares for moves that cause a value to be returned . When we progress to the next search depth, we will prioritize moves with high values in the history heuristic arrays because these are more likely to cause a Beta- cutoff or Alpha improvement.

3.5.8 Killer Heuristic

In many positions, most moves are quickly removed from consideration, often by the same move. Remembering the move that caused most cut-offs at each depth (killer move) and trying it at the beginning whenever the same search depth is considered may drastically increase the number of cut-offs, therefore reducing effective tree size and saving time.

3.5.9 Futility Pruning

Futility pruning suggest pruning nodes near a leaf where the sum of the current static evaluation value and some threshold is smaller than Alpha. In these positions, assuming that the value gained in the remaining moves until reaching the leaf is not greater than the threshold, it is safe to assume that the position is weak; it is worth pruning as its score will not be greater than Alpha. Naturally, the larger the threshold, the safer it is to apply futility pruning, although fewer nodes will be pruned.

3.5.10 Razoring

Very similar in concept to the idea of futility pruning. Typically, razoring is applied two or three plies away from quiescence, and if the static evaluation plus some margin is less than alpha, the search depth is reduced by one.

3.5.11 Static Exchange Evaluation

The Static Echange Evaluation (SEE) computes the value of a square on a chessboard based on the balances of power that affect the given square. It provides a means of estimating whether a given capture is likely to pay off. The sequence of captures in most positions is short, however quiescence search is called at all leaves of the search tree, therefore the quiescence nodes can easily dominate the total number of nodes. The Static Exchange Evaluation function attempts to evaluate a sequence of captures without any calls to the search. It is much more computationally extensive, but gives better estimation of the expected material gain, therefore allowing better move ordering.21

21 Reul, F.M.H. New Architectures in Computer Chess. Tilburg University, 2009

33

4. Chess Program Implementation Solution

4.1 Architectural Overview Diagram

34

4.2 Description of Application Modules

Engine Communication Service

This module handles the communication between the engine and the main web application.It is responsible to start the engine in a new thread.

Client Communication Service

This service component manages the communication traffic between the external gui and the web application controllers. Also, it is tasked to manage multiple player sessions.

Game Controller

This module keeps track of the state of the game; acts as a main application controller that directs messages beween the engine and the client gui. In addition, it takes care of saving and loading the game for each player.

PGN IO Manager

This I/O manager is the main link between the application and the database to persist unfinished games and to load them back to the application for the given player.

35

5. Engine Components

5.1 Engine Components Diagram

36

5.2 Description of Engine Modules

UCI Protocol Interpreter

The protocol interpreter acts as a gateway between the engine and the outside world (in my case, the outside world is the main application). It is started by the Engine Communication Service of the web application and which exchanges plain text commands with the application through its standard input and output channels, stdin/stdout.

Chess Board Model

This module handles the management and manipulation of the BitBoards which represent the chess board internally. The board model is the foundation of the chess engine, in that all other parts of the program rely on it.

Move Generator

This module is responsible for the generation and storage of possible moves for the chess engine to make. The move generator uses lots of pre-computed data when the engine starts to increase move generation speed. This precomputed data is made of arrays of BitBoards. One BitBoard represents all possible target squares for a given piece standing on a given square. The move generator relies on bit-wise operation that are performed on BitBoards together with pre-computed data.

Transposition Table

The transposition table is implemented as a hash table, using Zorbrist keys to address the table.

EvaluatorThe Evaluator is a static evaluation method that will compute the score of a given board layout. It checks for several heuristics to arrive at the best score for each computed board. The final evaluation function is broken down as follows.

final_score = material_score + pawn_score + mobility_score + development_score +castling_bonus + trapped_piece_penalty + bishop_score + knight_score +rook_score + queen_score + king_safety_score

37

Search Engine

The search engine manages and performs all of the move tree searching for chess engine.The following search techniques are implemented.

Opening Book Manager

Finally, this module manages the entire process of playing a move from the opening book. The opening book manager is be passed a book file name, and then selects a move. It will do this by reading in the contents of the file, pseudo-randomly selecting one of the moves in the file, and then passing information about this move to the rest of the program.

38

6. Graphical User Interface

39

7. Conclusion and Evaluation

7.1 Evaluation of Objectives

In implementing this project, I have learned a great deal about computer chess programming and AI methodologies to enable me come up with my project solution.On the plus side, the program features fully bitboard-based move generation, alpha-beta quiescence search algorithm, transposition and evaluation table, and various evaluation heuristics.

However, based on my testing and evaluation, the program makes no distinction between winning in one move and winning in many moves. Thus, it is often fooled by apparently sub-optimal moves down the line which alleviate the problem for the opponent when the program could have ended the game earlier. Time considerations prevented addressing this aspect.

At the end of the game, when there are only a few pieces left on the board, it is possible to explicitly reach the goal state via local searches, thus determining optimal moves for many possible positions. Putting these responses into a database would allow the program to perform flawlessly in the end-game phase; currently this stage of the game is a big weakness of my solution.

Since the project is developed using the Java programming language, it tends to be rather slow with multiple concurrent user sessions.

7.2 Suggested Further Work

Many improvements could be made to the implementation in its current form. The future work on this engine should be primarily concerned with increasing its playing strength. Another area in need of improvement is the evaluation function which is not that sophisticated compared to that of more mature engines.

Genuine machine learning algorithm could be of great value to this chess engine to enable it to mimic and counter the opponent's strength and playing style.

Pondering and time control should be part of any future enhancement of this implementation of my chess program to play a more realistic game.

The static exchange evaluator (SEE) is an important tool for the qualitative and quantitative evaluation of moves and threatened squares within the scope of a state-of-the-art computer-chess architecture . The precise results of a static exchange evaluation function can be used for the reduction of the branching factor for the search engine. An improved version of this important function also should be part of any future release of the engine.

40

8. References and Bibliography

8.1 General Chess References

Sokolsky, Alexei. Your First Move. Raduga Publishers, 1981

Lasker, Emanuel,Dvoretsky, Mark. Lasker's Manual of Chess

Averbakh, Yuri,Kasparov, Garry. A History of Chess: From Chaturanga to the Present Day

8.2 Chess Programming Related Artificial Intelligence References

Levy, David N.L. Computer Games I. Springer-Verlag, New York, 1987.

Levy, David N. L.,Newborn, Monty. How Computers Play Chess., 1991

Levy, David N.L. Computer Chess Compendium, 2009

Walls, Ben P. Beautiful Mates: Applying Principles of Beauty to Computer Chess Heuristics.

Stuart Russell and Peter Norvig. Artificial Intelligence: A Modern Approach.Prentice Hall New Jersey, 2003.

Shannon, C. Programming a Computer for Playing Chess. , 1950

Marsland, Antony. Computers, Chess, and Cognition, 1990

Turocy T. L., von Stengel B. Game Theory. CDAM Research Report, October 8, 2001.

Reul, F.M.H. New Architectures in Computer Chess. Tilburg University, 2009

41

8.3 Internet References

Chess Programming Wiki:https://chessprogramming.wikispaces.com/

Watson, Mark, Practical Artificial Intelligence Programming With Java,http://www.markwatson.com/opencontent/, November 11, 2008

'Computer Chess Programming' at: http://www.xs4all.nl/~verhelst/chess/search.html

Frayn, Colin. 'Computer Chess Programming Theory.http://www.frayn.net/beowulf/theory.html

Standard: Portable Game Notation Specification and Implementation Guidehttp://www.saremba.de/chessgml/standards/pgn/pgn-complete.htm

https://en.wiktionary.org/wiki/Appendix:Glossary_of_chess

On Magic BitBoards:http://www.pradu.us/old/Nov27_2008/Buzz/research/magic/Bitboards.pdf

Computer Chess Club: Programming and Technical Discussionshttp://talkchess.com/forum/viewforum.php?f=7

In Rotated BitBoards:http://people.csail.mit.edu/heinz/dt/node8.htmlhttps://cis.uab.edu/hyatt/bitmaps.html

42

8.4 Open Source Chess Engine References

Writing a chess program in 99 steps http://web.archive.org/web/20120621100214/http://www.sluijten.com/winglet/

Mediocre Chesshttp://mediocrechess.sourceforge.net/

Cuckoo Chesshttp://web.comhem.se/petero2home/javachess/index.html

Flux Enginehttp://www.fluxchess.com/flux/

Pulse Enginehttp://www.fluxchess.com/pulse/

Bagatur Chess Enginehttps://sites.google.com/site/bagaturchess/home

Carballo Enginehttps://github.com/albertoruibal/carballo

43

Appendix A

Rules of the Game22

Legal Moves

Pawn

A pawn moves forward only, in its same file. For white, the rank number must increase by 1 or 2. It can increase by 2 when it is in its starting position; rank 2. For black, the rank number must decrease by 1 or 2. It can decrease by 2 when the pawn is in it’s starting position of rank 7. A pawn captures on the diagonal: it will move into an adjacent file and forward one rank, replacing the piece that was there. There is one origin for all but the opening pawn moves: one rank back on the file in which the pawn ended its move. There is one origin for an opening pawn move that lands in rank 4 or 5: two ranks back on the file where the pawn ended its move. There are two possible origins for any pawn capture (one position on a file adjacent to the one in which the pawn ended its move).

Rook

A rook moves in ranks or files only, with no limit on distance. There are 16 possible origins for any rook move, including the entire rank or the entire file in which the rook ended its move.

Knight

A knight makes an L-shaped move. It moves two spaces in one direction, turns 90-degrees and moves one more space. From g1, a knight can move to either f3 or h3. The rank changes by 2 and the file by 1; or the file changes by 2 and the rank changes by 1. There are 8 places a knight could start from relative to its final location.

Bishop

A bishop moves diagonally. The amount of change in the rank must be the same as the change in the file. There are 16 places a bishop can start from on the two diagonals that intersect the final location.

Queen

The queen’s move combines bishop and rook: any number of spaces diagonally, horizontally or vertically. There are 16 places on the diagonals, plus 16 more places on the horizontals and verticals where the queen could originate. Pawns that reach the opposite side of the board are often promoted to queens, meaning there can be multiple queens late in the game.

King

The king is unique, there is only one. The king can only move one space hoizontally, vertically or diagonally.

22 Lasker, Emanuel,Dvoretsky, Mark. Lasker's Manual of Chess

44

King and Rook

The king and a rook can also engage in a move called castling: both pieces move. When the king and the closest rook (the one in file h) castle, this is king side and annoted O-O . The king moves from file e to file g and the rook from fiel h to file f. When the king and the queen’s side rook (the one in file a) castle, this is annotated O-O-O . The king moves from file e to file c and the rook move from file a to file d.

Castling can only be accomplished if (a) neither piece has moved and (b) space between them is unoccupied by other pieces. Part a of this rule requires that the game remember when a king or rook moves, and eliminate that side from available castling moves. Moving the rook in file a eliminates queen-side castling; moving the rook in file h eliminates king-side castling. Moving the king eliminates all castling.

45

Appendix B

Implemented functions of the Universal Chess Interface (UCI) Protocol23

The UCI protocol is an open communication protocol that enables the chess engine to communicate with the graphical user interface controlling it. Hence, implementing the UCI protocol abstracts out the work related to graphics and makes a chess engine testable using existing graphical chess interfaces.

GUI to engine:

uci tell engine to use the uci (universal chess interface), this will be sent once as a first command after program boot to tell the engine to switch to uci mode.

isready this is used to synchronize the engine with the GUI.

ucinewgame this is sent to the engine when the next search (started with "position" and "go") will

be from a different game.

position [fen <fenstring> | startpos ] moves <move1> .... <movei> set up the position described in fenstring on the internal board and play the moves on the internal chess board. if the game was played from the start position the string "startpos" will be sent

go start calculating on the current position set up with the "position" command.

stop stop calculating as soon as possible,

quit quit the program as soon as possible

23 See the complete reference http://www.shredderchess.com/chess-info/features/uci-universal-chess-interface.html

46

Engine to GUI:

id * name <x>

this must be sent after receiving the "uci" command to identify the engine, e.g. "id name Shredder X.Y\n"

* author <x> this must be sent after receiving the "uci" command to identify the engine, e.g. "id author Stefan MK\n"

uciok Must be sent after the id and optional options to tell the GUI that the engine has sent all infos and is ready in uci mode.

readyok This must be sent when the engine has received an "isready" command and has processed all input and is ready to accept new commands now.

bestmove <move1>the engine has stopped searching and found the move <move> best in this position.

info the engine wants to send information to the GUI. This should be done whenever one of the info has changed.

Additional info: * depth <x> search depth in plies

* time <x> the time searched in ms, this should be sent together with the pv.

* nodes <x> x nodes searched, the engine should send this info regularly

* pv <move1> ... <movei> the best line found

* score * cp <x> the score from the engine's point of view in centipawns.

* currmove <move> currently searching this move

* currmovenumber <x> currently searching move number x, for the first move x should be 1 not 0.

* nps <x> x nodes per second searched, the engine should send this info regularly

* string <str> any string str which will be displayed be the engine, if there is a string command the rest of the line will be interpreted as <str>.

* currline <cpunr> <move1> ... <movei> this is the current line the engine is

calculating.

47

Appendix C

FEN Board Position Notation

Overview

FEN stands for "Forsyth-Edwards Notation"; it is a standard for describing chess positions using a a single string of variable length that holds all the information about a given position.However, the Forsyth-Edwards Notation does not record the move history.

A single FEN record is composed of six data fields. Each field is composed only of non-blank printing ASCII characters. Adjacent fields are separated by a single ASCII space character.

Having a standard position notation is particularly important for chess programmers as it allows them to share position databases. For example, there exist standard position notation databases with many of the classical benchmark tests for chessplaying programs, and by using a common position notation format many hours of tedious data entry can be saved. Additionally, a position notation can be useful for page layout programs.

Origin

FEN is based on a 19th century standard for position recording designed by the Scotsman David Forsyth, a newspaper journalist. The original Forsyth standard has been slightly extended for use with chess software by Steven Edwards with assistance from commentators on the Internet. This new standard, FEN, was first implemented in Edwards' SAN Kit.

Description of the Fields

FEN specifies the piece placement, the active color, the castling availability, the en passant target square, the halfmove clock, and the fullmove number. These can all fit on a single text line in an easily read format. The length of a FEN position description varies somewhat according to the position.

Field #1: Piece Placement Data

The first field represents the placement of the pieces on the board. The board contents are specified starting with the eighth rank and ending with the first rank. For each rank, the squares are specified from file a to file h. White pieces are identified by uppercase SAN piece letters ("PNBRQK") and black pieces are identified by lowercase SAN piece letters ("pnbrqk"). Empty squares are represented by the digits one through eight; the digit used represents thecount of contiguous empty squares along a rank. A solidus character "/" is used to separate data of adjacent ranks.

48

Field #2: Active Color

The second field represents the active color. A lower case "w" is used if White is to move; a lower case "b" is used if Black is the active player.

Field #3: Castling Availability

The third field represents castling availability. This indicates potential future castling that may not be possible at the moment due to blocking pieces or enemy attacks. If there is no castling availability for either side, the single character symbol "-" is used. Otherwise, a combination of one to four characters are present. If White has King side castling availability,the uppercase letter "K" appear. If White has Queen side castling availability, the uppercase letter "Q" appears. If Black has King side castling availability, the lowercase letter "k" appears. If Black has Queen side castling availability, then the lowercase letter "q" appears. Those letters which appear will be ordered first uppercase before lowercase and secondkingside before queenside. There is no white space between the letters.

Field #4: En Passant Target Square

The fourth field is the en passant target square. If there is no en passant target square then the single character symbol "-" appears. If there is an en passant target square then is represented by a lowercase file character immediately followed by a rank digit. The rank digit will be "3" following a white pawn double advance (Black is the active color) or else bethe digit "6" after a black pawn double advance (White being the active color).

An en passant target square is given if and only if the last move was a pawn advance of two squares. Therefore, an en passant target square field may have a square name even if there is no pawn of the opposing side that may immediately execute the en passant capture.

Field #5: Halfmove clock

The fifth field is a nonnegative integer representing the halfmove clock. This number is the count of halfmoves (or ply) since the last pawn advance or capturing move. This value is used for the fifty move draw rule.

Field #6: Fullmove number

The sixth and last field is a positive integer that gives the fullmove number. This will have the value "1" for the first move of a game for both White and Black. It is incremented by one immediately after each move by Black.

49

Example

The starting position is described with FEN-notation as:

rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1

Start with the black pieces (8th rank, in lower-case for black), then '/' to move to the next rank (the 7th), there are the black pawns, then next rank where there are no pieces so we put the number 8 (as in eight empty squares).Then comes 3 more empty ranks, then the white pawns and pieces (in upper-case for white).

Next character 'w' determines it's white turn to move.

KQkq is for castling rights (K - white can castle kingside, Q - white can castle queenside, and same for black in lower-case). If none can castle a dash '-' is displayed.

Then comes the en passant square, since there is no en passants available in the starting position we have '-' there. There can only be one en passant square in any given position since there is only one pawn move at a time that can result in the square.

Next comes the half-moves since the last capture or pawn move. Finally the total of full-moves starting at 1 and incremented every time black moves.

50

Appendix D

Algebraic Notation

Overview

A chessboard can be thought of as a mapping from location names to pieces.In algebraic notation the locations have a rank-file address of a number and a letter; it uses letters a-h for the files from white’s left to right, and numbers for the ranks from white (1) to black (8). There are two forms of algebraic notation: short (or standard) algebraic notation (SAN), where only the destination is shown, and long algebraic notation (LAN) that shows the piece, the starting location and the destination.

Piece Symbol Movement Comment

Pawn 1 square forward (1 or 2 from the starting position)

Rook R Unlimited on the same rank or same file

Knight N 2 in one direction plus 1 in eigther left or right

Bishop B Unlimited diagonally

Queen Q Unlimited on the same rank or same file or diagonally

King K 1 square in any direction

Syntax – SAN

Format: [ P ] [ m ] [ d ] f r [ n ]

P: The piece name (omitted for pawns).m: The move is only specified for a capture (x).d: The discriminator: either a file (preferred) or a rank or both used to choose which piece

moved when there are multiple possibilities.f: The file (a-h) moving to.r: The rank (1-8) moving to.n: any notes about the move (+, #, !, !!, ?, ??). The notes may include = and a piece

letter when a pawn is promoted.

51

Syntax - LAN

Format: [ P ] f r m f r [ n ]

P: The piece name (omitted for pawns).f: The file (a-h) moving from and to.r: The rank (1-8) moving from and to.m: The move (- or x).n: any notes about the move (+, #, !, !!, ?, ??). The notes may include = and a piece

letter when a pawn is promoted.

Additional Notations

In both notations, the castle moves are written O-O or O-O-O (capital letters, not numbers). Similarly, the end of a game is often followed with a 1-0 (white wins), 0-1 (black wins), 1/2-1/2 (a draw), or * for a game that is unknown or abandoned. Each turn is preceeded by a turn number.

$1 good move (traditional “!” )$2 poor move (traditional “?” )$3 very good move (traditional “!!” )$4 very poor move (traditional “??” )

52