diff --git a/README.md b/README.md index 8b88bb7..16b4be1 100644 --- a/README.md +++ b/README.md @@ -39,3 +39,6 @@ Due to easier off the board checking a The engine uses a simple implementation of the [NegaMax](https://www.chessprogramming.org/NegaMax)-algorithm. +For the evaluation function each piece has a corresponding value. +Additionally there are [piece-square tables](https://www.chessprogramming.org/Piece-Square_Tables) +to encurage putting pieces on active squares. diff --git a/src/chess.nim b/src/chess.nim index 31b923e..da92160 100644 --- a/src/chess.nim +++ b/src/chess.nim @@ -54,7 +54,7 @@ const WQueen* = 5 ## \ ## `WQueen` is the value assigned to a square in a board with a white ## queen. - WKing = 6 ## \ + WKing* = 6 ## \ ## `WKing` is the value assigned to a square in a board with a white king. WEnPassant = 7 ## \ ## `WEnPassant` is assigned to a square in a board with an invisible white @@ -71,7 +71,7 @@ const ## `BRook` is the value assigned to a square in a board with a black rook. BQueen* = -WQueen ## \ ## `BQueen` is the value assigned to a square in a board with a black queen. - BKing = -WKing ## \ + BKing* = -WKing ## \ ## `BKing` is the value assigned to a square in a board with a black king. BEnPassant = -WEnPassant ## \ ## `BEnPassant` is assigned to a square in a board with an invisible black diff --git a/src/engine.nim b/src/engine.nim index bb07dd0..9cbe43f 100644 --- a/src/engine.nim +++ b/src/engine.nim @@ -1,47 +1,147 @@ +import algorithm + import ./chess.nim type - MoveTree* = object + MoveTree = object ## `Movetree` is a visualization for possible moves. - chess*: Chess + chess: Chess evaluation: float - children*: seq[Movetree] + children: seq[Movetree] const - PawnVal = 1 ## `PawnVal` is the engines value for a pawn. - KnightVal = 3 ## `KnightVal` is the engines value for a knight. - BishopVal = 3 ## `BishopVal` is the engines value for a bishop. - RookVal = 5 ## `RookVal` is the engines value for a rook. - QueenVal = 9 ## `QueenVal` is the engines value for a queen. - CheckmateVal = -1000 ## `CheckmateVal` is the engines value for a checkmate. + PawnVal = 10 ## `PawnVal` is the engines value for a pawn. + KnightVal = 31 ## `KnightVal` is the engines value for a knight. + BishopVal = 33 ## `BishopVal` is the engines value for a bishop. + RookVal = 50 ## `RookVal` is the engines value for a rook. + QueenVal = 90 ## `QueenVal` is the engines value for a queen. + CheckmateVal = -10000 ## `CheckmateVal` is the engines value for a checkmate. DrawVal = 0 ## `DrawVal` is the engines value for a draw. LoVal = -1000000 ## `LoVal` is a value always lower than any evaluation. + pawnTable = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 5, 10, 10, -20, -20, 10, 10, 5, 0, + 0, 5, -5, -10, 0, 0, -10, -5, 5, 0, + 0, 0, 0, 0, 20, 20, 0, 0, 0, 0, + 0, 5, 5, 10, 25, 25, 10, 5, 5, 0, + 0, 10, 10, 20, 30, 30, 20, 10, 10, 0, + 0, 50, 50, 50, 50, 50, 50, 50, 50, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] ## `pawnTable` is the piece-square table for pawns. + knightTable = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -50, -40, -30, -30, -30, -30, -40, -50, 0, + 0, -40, -20, 0, 5, 5, 0, -20, -40, 0, + 0, -30, 0, 10, 15, 15, 10, 0, -30, 0, + 0, -30, 5, 15, 20, 20, 15, 5, -30, 0, + 0, -30, 0, 15, 20, 20, 15, 0, -30, 0, + 0, -30, 5, 10, 15, 15, 10, 5, -30, 0, + 0, -40, -20, 0, 0, 0, 0, -20, -40, 0, + 0, -50, -40, -30, -30, -30, -30, -40, -50, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] ## `knightTable` is the piece-square table for pawns. + bishopTable = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -20, -10, -10, -10, -10, -10, -10, -20, 0, + 0, -10, 5, 0, 0, 0, 0, 5, -10, 0, + 0, -10, 10, 10, 10, 10, 10, 10, -10, 0, + 0, -10, 0, 10, 10, 10, 10, 0, -10, 0, + 0, -10, 5, 5, 10, 10, 5, 5, -10, 0, + 0, -10, 0, 5, 10, 10, 5, 0, -10, 0, + 0, -10, 0, 0, 0, 0, 0, 0, -10, 0, + 0, -20, -10, -10, -10, -10, -10, -10, -20, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] ## `bishopTable` is the piece-square table for pawns. + rookTable = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 5, 5, 0, 0, 0, 0, + 0, -5, 0, 0, 0, 0, 0, 0, -5, 0, + 0, -5, 0, 0, 0, 0, 0, 0, -5, 0, + 0, -5, 0, 0, 0, 0, 0, 0, -5, 0, + 0, -5, 0, 0, 0, 0, 0, 0, -5, 0, + 0, -5, 0, 0, 0, 0, 0, 0, -5, 0, + 0, 5, 10, 10, 10, 10, 10, 10, 5, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] ## `rookTable` is the piece-square table for pawns. + queenTable = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -20, -10, -10, -5, -5, -10, -10, -20, 0, + 0, -10, 0, 0, 0, 0, 5, 0, -10, 0, + 0, -10, 0, 5, 5, 5, 5, 0, -10, 0, + 0, -10, 0, 5, 5, 5, 5, 0, -10, 0, + 0, -10, 0, 5, 5, 5, 5, 0, -10, 0, + 0, -10, 0, 5, 5, 5, 5, 0, -10, 0, + 0, -10, 0, 0, 0, 0, 0, 0, -10, 0, + 0, -20, -10, -10, -5, -5, -10, -10, -20, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] ## `queenTable` is the piece-square table for pawns. + kingTable = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 20, 30, 10, 0, 0, 10, 30, 20, 0, + 0, 20, 20, 0, 0, 0, 0, 20, 20, 0, + 0, -10, -20, -20, -20, -20, -20, -20, -10, 0, + 0, -20, -30, -30, -40, -40, -30, -30, -20, 0, + 0, -30, -40, -40, -50, -50, -40, -40, -30, 0, + 0, -30, -40, -40, -50, -50, -40, -40, -30, 0, + 0, -30, -40, -40, -50, -50, -40, -40, -30, 0, + 0, -30, -40, -40, -50, -50, -40, -40, -30, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] ## `kingTable` is the piece-square table for pawns. proc pieceEval(chess: Chess): int = ## Returns the evaluation of existing pieces on the `board` var evaluation = DrawVal - for square in chess.board: + for ind, square in chess.board: case square: of WPawn: evaluation += ord(Color.White) * PawnVal + evaluation += ord(Color.White) * pawnTable[ind] of WKnight: evaluation += ord(Color.White) * KnightVal + evaluation += ord(Color.White) * knightTable[ind] of WBishop: evaluation += ord(Color.White) * BishopVal + evaluation += ord(Color.White) * bishopTable[ind] of WRook: evaluation += ord(Color.White) * RookVal + evaluation += ord(Color.White) * rookTable[ind] of WQueen: evaluation += ord(Color.White) * QueenVal + evaluation += ord(Color.White) * queenTable[ind] + of WKing: + evaluation += ord(Color.White) * kingTable[ind] of BPawn: evaluation += ord(Color.Black) * PawnVal + evaluation += ord(Color.Black) * pawnTable.reversed[ind] of BKnight: evaluation += ord(Color.Black) * KnightVal + evaluation += ord(Color.Black) * knightTable.reversed[ind] of BBishop: evaluation += ord(Color.Black) * BishopVal + evaluation += ord(Color.Black) * bishopTable.reversed[ind] of BRook: evaluation += ord(Color.Black) * RookVal + evaluation += ord(Color.Black) * rookTable.reversed[ind] of BQueen: evaluation += ord(Color.Black) * QueenVal + evaluation += ord(Color.Black) * queenTable.reversed[ind] + of BKing: + evaluation += ord(Color.White) * kingTable.reversed[ind] else: continue return evaluation @@ -67,7 +167,8 @@ proc spanMoveTree(chess: Chess, depth: int): MoveTree = ## Create and return a Movetree of a given `chess` with a given maximum `depth`. var mTree: MoveTree mTree.chess = chess - if depth != 0 and not chess.isCheckmate(chess.toMove) and not chess.isStalemate(chess.toMove): + if depth != 0 and not chess.isCheckmate(chess.toMove) and + not chess.isStalemate(chess.toMove): let possibleMoves = chess.genLegalMoves(chess.toMove) for move in possibleMoves: var tmpChess = chess @@ -97,7 +198,7 @@ proc bestMove*(chess: Chess, depth: int): Move = tmpChess.checkedMove(move) var tmpMTree = tmpChess.spanMoveTree(depth) var tmpEval = -tmpMTree.negaMax() - echo("move:", moveToNotation(move),"; eval:", tmpEval) + echo("move:", moveToNotation(move), "; eval:", tmpEval) if tmpEval > bestEval: bestEval = tmpEval bestMove = move diff --git a/src/engineTest.nim b/src/engineTest.nim index 9bf3479..dce35a6 100644 --- a/src/engineTest.nim +++ b/src/engineTest.nim @@ -86,7 +86,8 @@ testSuite ChessTest of TestSuite: ], Color.Black) var testBestMove = self.chess.bestMove(2) self.check(testBestMove.start != 0) - self.check(indToField(testBestMove.start) != "g5" or indToField(testBestMove.dest) != "f4") + self.check(indToField(testBestMove.start) != "g5" or indToField( + testBestMove.dest) != "f4") method testBestMoveTacticWhite() = self.chess = initChess([ @@ -101,7 +102,8 @@ testSuite ChessTest of TestSuite: ], Color.White) var testBestMove = self.chess.bestMove(2) self.check(testBestMove.start != 0) - self.check(indToField(testBestMove.start) != "g4" or indToField(testBestMove.dest) != "f5") + self.check(indToField(testBestMove.start) != "g4" or indToField( + testBestMove.dest) != "f5") when isMainModule: diff --git a/src/lichessBridge.nim b/src/lichessBridge.nim index 70ca38f..45f18d0 100644 --- a/src/lichessBridge.nim +++ b/src/lichessBridge.nim @@ -11,7 +11,7 @@ var session = berserk.TokenSession(secret.api_token) var client = berserk.Client(session=session) let engineID = "tiyn-ychess" -let engineDifficulty = 2 +let engineDifficulty = 3 let toAccept = ["tiynger"]