refactoring: cleanup engine code and add alpha beta to readme

master
TiynGER 4 years ago
parent b7480a0cd4
commit 3c97395ce8

@ -38,7 +38,8 @@ Due to easier off the board checking a
### Engine ### Engine
The engine uses a simple implementation of the The engine uses a simple implementation of the
[NegaMax](https://www.chessprogramming.org/NegaMax)-algorithm. [NegaMax](https://www.chessprogramming.org/NegaMax)-algorithm with
[Alpha-Beta-Pruning](https://www.chessprogramming.org/Alpha-Beta#Negamax_Framework).
For the evaluation function each piece has a corresponding value. For the evaluation function each piece has a corresponding value.
Additionally there are [piece-square tables](https://www.chessprogramming.org/Piece-Square_Tables) Additionally there are [piece-square tables](https://www.chessprogramming.org/Piece-Square_Tables)
to encurage putting pieces on active squares. to encurage putting pieces on active squares.

@ -667,8 +667,8 @@ proc genLegalKingMoves(chess: Chess, field: int, color: Color): seq[Move] =
proc genLegalMoves(chess: Chess, field: int, color: Color): seq[Move] = proc genLegalMoves(chess: Chess, field: int, color: Color): seq[Move] =
## Generates all legal moves in a `chess` starting from `field` for a `color`. ## Generates all legal moves in a `chess` starting from `field` for a `color`.
var legal_moves = newSeq[Move]() var legal_moves = newSeq[Move]()
var target = ord(color) * chess.board[field] var target = abs(chess.board[field])
if 0 < target and target < WEnPassant: if WPawn <= target and target <= WKing:
legal_moves = case target: legal_moves = case target:
of WPawn: of WPawn:
chess.genLegalPawnMoves(field, color) chess.genLegalPawnMoves(field, color)

@ -10,14 +10,15 @@ type
children: seq[Movetree] children: seq[Movetree]
const const
PawnVal = 10 ## `PawnVal` is the engines value for a pawn. PawnVal = 100 ## `PawnVal` is the engines value for a pawn.
KnightVal = 31 ## `KnightVal` is the engines value for a knight. KnightVal = 310 ## `KnightVal` is the engines value for a knight.
BishopVal = 33 ## `BishopVal` is the engines value for a bishop. BishopVal = 330 ## `BishopVal` is the engines value for a bishop.
RookVal = 50 ## `RookVal` is the engines value for a rook. RookVal = 500 ## `RookVal` is the engines value for a rook.
QueenVal = 90 ## `QueenVal` is the engines value for a queen. QueenVal = 900 ## `QueenVal` is the engines value for a queen.
CheckmateVal = -10000 ## `CheckmateVal` is the engines value for a checkmate. CheckmateVal = -100000 ## `CheckmateVal` is the engines value for a checkmate.
DrawVal = 0 ## `DrawVal` is the engines value for a draw. DrawVal = 0 ## `DrawVal` is the engines value for a draw.
LoVal = -1000000 ## `LoVal` is a value always lower than any evaluation. HiVal = 10000 ## `LoVal` is a value always lower than any evaluation.
LoVal = -HiVal ## `LoVal` is a value always lower than any evaluation.
pawnTable = [ 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, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -105,45 +106,36 @@ const
proc pieceEval(chess: Chess): int = proc pieceEval(chess: Chess): int =
## Returns the evaluation of existing pieces on the `board` ## Returns the evaluation of existing pieces on the `board`
var evaluation = DrawVal var evaluation: int
for ind, square in chess.board: for ind, piece in chess.board:
case square: let tmpEval = case piece:
of WPawn: of WPawn:
evaluation += ord(Color.White) * PawnVal ord(Color.White) * PawnVal + ord(Color.White) * pawnTable[ind]
evaluation += ord(Color.White) * pawnTable[ind]
of WKnight: of WKnight:
evaluation += ord(Color.White) * KnightVal ord(Color.White) * KnightVal + ord(Color.White) * knightTable[ind]
evaluation += ord(Color.White) * knightTable[ind]
of WBishop: of WBishop:
evaluation += ord(Color.White) * BishopVal ord(Color.White) * BishopVal + ord(Color.White) * bishopTable[ind]
evaluation += ord(Color.White) * bishopTable[ind]
of WRook: of WRook:
evaluation += ord(Color.White) * RookVal ord(Color.White) * RookVal + ord(Color.White) * rookTable[ind]
evaluation += ord(Color.White) * rookTable[ind]
of WQueen: of WQueen:
evaluation += ord(Color.White) * QueenVal ord(Color.White) * QueenVal + ord(Color.White) * queenTable[ind]
evaluation += ord(Color.White) * queenTable[ind]
of WKing: of WKing:
evaluation += ord(Color.White) * kingTable[ind] ord(Color.White) * kingTable[ind]
of BPawn: of BPawn:
evaluation += ord(Color.Black) * PawnVal ord(Color.Black) * PawnVal + ord(Color.Black) * pawnTable.reversed[ind]
evaluation += ord(Color.Black) * pawnTable.reversed[ind]
of BKnight: of BKnight:
evaluation += ord(Color.Black) * KnightVal ord(Color.Black) * KnightVal + ord(Color.Black) * knightTable.reversed[ind]
evaluation += ord(Color.Black) * knightTable.reversed[ind]
of BBishop: of BBishop:
evaluation += ord(Color.Black) * BishopVal ord(Color.Black) * BishopVal + ord(Color.Black) * bishopTable.reversed[ind]
evaluation += ord(Color.Black) * bishopTable.reversed[ind]
of BRook: of BRook:
evaluation += ord(Color.Black) * RookVal ord(Color.Black) * RookVal + ord(Color.Black) * rookTable.reversed[ind]
evaluation += ord(Color.Black) * rookTable.reversed[ind]
of BQueen: of BQueen:
evaluation += ord(Color.Black) * QueenVal ord(Color.Black) * QueenVal + ord(Color.Black) * queenTable.reversed[ind]
evaluation += ord(Color.Black) * queenTable.reversed[ind]
of BKing: of BKing:
evaluation += ord(Color.White) * kingTable.reversed[ind] ord(Color.White) * kingTable.reversed[ind]
else: else:
continue DrawVal
evaluation += tmpEval
return evaluation return evaluation
proc evaluate(chess: Chess): int = proc evaluate(chess: Chess): int =
@ -163,28 +155,23 @@ proc evaluate(chess: Chess): int =
evaluation = min(DrawVal, evaluation) evaluation = min(DrawVal, evaluation)
return evaluation return evaluation
proc spanMoveTree(chess: Chess, depth: int): MoveTree = proc negaMax(chess: Chess, depth: int, a: int, b: int): int =
## Create and return a Movetree of a given `chess` with a given maximum `depth`. ## Return the value of a given `chess` with `depth` and `a` and `b` for alpha-beta
var mTree: MoveTree ## pruning.
mTree.chess = chess var alpha = a
if depth != 0 and not chess.isCheckmate(chess.toMove) and var beta = b
not chess.isStalemate(chess.toMove): if depth <= 0 or chess.isCheckmate(chess.toMove) or chess.isStalemate(chess.toMove):
return chess.evaluate()
let possibleMoves = chess.genLegalMoves(chess.toMove) let possibleMoves = chess.genLegalMoves(chess.toMove)
for move in possibleMoves: for move in possibleMoves:
var tmpChess = chess var tmpChess = chess
tmpChess.checkedMove(move) tmpChess.checkedMove(move)
mTree.children.add(spanMoveTree(tmpChess, depth-1)) var tmpVal = -negaMax(tmpChess, depth - 1, -beta, -alpha)
return mTree if tmpVal >= beta:
return beta
proc negaMax(mTree: MoveTree): int = if tmpVal > alpha:
## Return the value of the root node of a given `MoveTree` alpha = tmpVal
if mTree.children == []: return alpha
return mTree.chess.evaluate()
var bestVal = LoVal
for child in mTree.children:
var tmpVal = -negaMax(child)
bestVal = max(bestVal, tmpVal)
return bestVal
proc bestMove*(chess: Chess, depth: int): Move = proc bestMove*(chess: Chess, depth: int): Move =
## Generate a MoveTree of a `chess` with a given `depth`, run negaMax and return ## Generate a MoveTree of a `chess` with a given `depth`, run negaMax and return
@ -196,8 +183,7 @@ proc bestMove*(chess: Chess, depth: int): Move =
for move in moves: for move in moves:
var tmpChess = chess var tmpChess = chess
tmpChess.checkedMove(move) tmpChess.checkedMove(move)
var tmpMTree = tmpChess.spanMoveTree(depth) var tmpEval = -tmpChess.negaMax(depth, LoVal, HiVal)
var tmpEval = -tmpMTree.negaMax()
echo("move:", moveToNotation(move), "; eval:", tmpEval) echo("move:", moveToNotation(move), "; eval:", tmpEval)
if tmpEval > bestEval: if tmpEval > bestEval:
bestEval = tmpEval bestEval = tmpEval

@ -25,20 +25,6 @@ testSuite ChessTest of TestSuite:
var pieceEvaluation = self.chess.pieceEval() var pieceEvaluation = self.chess.pieceEval()
self.check(pieceEvaluation == 0) self.check(pieceEvaluation == 0)
method testSpanMoveTree() =
self.chess = initChess([
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, WKing, 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, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, BKing, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
], Color.Black)
var mTree = self.chess.spanMoveTree(1)
self.check(mTree.children == [])
method testBestMoveProm() = method testBestMoveProm() =
self.chess = initChess([ self.chess = initChess([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

Loading…
Cancel
Save