mirror of
https://github.com/tiyn/yeschess.git
synced 2025-04-02 23:17:47 +02:00
engine: added piece-square tables
Piece square tables are a way to encurage the engine to put pieces on active squares. Each piece got a corresponding table.
This commit is contained in:
parent
b3c76fd2c1
commit
b7480a0cd4
@ -39,3 +39,6 @@ Due to easier off the board checking a
|
|||||||
|
|
||||||
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.
|
||||||
|
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.
|
||||||
|
@ -36,10 +36,10 @@ type
|
|||||||
b: int # `b` describes the amount of bishops.
|
b: int # `b` describes the amount of bishops.
|
||||||
r: int # `r` describes the amount of rooks.
|
r: int # `r` describes the amount of rooks.
|
||||||
q: int # `q` describes the amount of queens.
|
q: int # `q` describes the amount of queens.
|
||||||
Pieces = array[10,int]
|
Pieces = array[10, int]
|
||||||
|
|
||||||
const
|
const
|
||||||
Block = 999 ## \
|
Block = 999 ## \
|
||||||
## `Block` is the value assigned to empty blocked fields in a board.
|
## `Block` is the value assigned to empty blocked fields in a board.
|
||||||
WPawn* = 1
|
WPawn* = 1
|
||||||
## `WPawn` is the value assigned to a square in a board with a white pawn.
|
## `WPawn` is the value assigned to a square in a board with a white pawn.
|
||||||
@ -54,9 +54,9 @@ const
|
|||||||
WQueen* = 5 ## \
|
WQueen* = 5 ## \
|
||||||
## `WQueen` is the value assigned to a square in a board with a white
|
## `WQueen` is the value assigned to a square in a board with a white
|
||||||
## queen.
|
## queen.
|
||||||
WKing = 6 ## \
|
WKing* = 6 ## \
|
||||||
## `WKing` is the value assigned to a square in a board with a white king.
|
## `WKing` is the value assigned to a square in a board with a white king.
|
||||||
WEnPassant = 7 ## \
|
WEnPassant = 7 ## \
|
||||||
## `WEnPassant` is assigned to a square in a board with an invisible white
|
## `WEnPassant` is assigned to a square in a board with an invisible white
|
||||||
## en passant pawn.
|
## en passant pawn.
|
||||||
BPawn* = -WPawn ## \
|
BPawn* = -WPawn ## \
|
||||||
@ -71,9 +71,9 @@ const
|
|||||||
## `BRook` is the value assigned to a square in a board with a black rook.
|
## `BRook` is the value assigned to a square in a board with a black rook.
|
||||||
BQueen* = -WQueen ## \
|
BQueen* = -WQueen ## \
|
||||||
## `BQueen` is the value assigned to a square in a board with a black queen.
|
## `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.
|
## `BKing` is the value assigned to a square in a board with a black king.
|
||||||
BEnPassant = -WEnPassant ## \
|
BEnPassant = -WEnPassant ## \
|
||||||
## `BEnPassant` is assigned to a square in a board with an invisible black
|
## `BEnPassant` is assigned to a square in a board with an invisible black
|
||||||
## en passant pawn.
|
## en passant pawn.
|
||||||
N = 10 ## `N` describes a move a field up the board from whites perspective.
|
N = 10 ## `N` describes a move a field up the board from whites perspective.
|
||||||
@ -205,7 +205,8 @@ proc moveToNotation*(move: Move): string =
|
|||||||
res.add(dest)
|
res.add(dest)
|
||||||
var color = move.color
|
var color = move.color
|
||||||
var prom = PieceChar[move.prom]
|
var prom = PieceChar[move.prom]
|
||||||
if (color == Color.White and dest[1] == '8') or (color == Color.Black and dest[1] == '1'):
|
if (color == Color.White and dest[1] == '8') or (color == Color.Black and
|
||||||
|
dest[1] == '1'):
|
||||||
res.add(prom)
|
res.add(prom)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@ -812,8 +813,8 @@ proc threeMoveRep(chess: Chess): bool =
|
|||||||
var lastCastleRights = chess.previousCastleRights[chess.previousBoard.high]
|
var lastCastleRights = chess.previousCastleRights[chess.previousBoard.high]
|
||||||
var reps = 0
|
var reps = 0
|
||||||
for stateInd in (chess.previousBoard.low)..(chess.previousBoard.high):
|
for stateInd in (chess.previousBoard.low)..(chess.previousBoard.high):
|
||||||
if (chess.previousBoard[stateInd] == lastState and chess.previousCastleRights[
|
if (chess.previousBoard[stateInd] == lastState and
|
||||||
stateInd] == lastCastleRights):
|
chess.previousCastleRights[stateInd] == lastCastleRights):
|
||||||
reps = reps + 1
|
reps = reps + 1
|
||||||
return reps >= 3
|
return reps >= 3
|
||||||
|
|
||||||
@ -837,10 +838,12 @@ proc checkInsufficientMaterial(board: Board): bool =
|
|||||||
index = piece - WPawn # map lowest value to 0
|
index = piece - WPawn # map lowest value to 0
|
||||||
pieces[index] += 1
|
pieces[index] += 1
|
||||||
elif piece <= BPawn and piece >= BRook:
|
elif piece <= BPawn and piece >= BRook:
|
||||||
index = WRook - piece # map black pieces after whites
|
index = WRook - piece # map black pieces after whites
|
||||||
pieces[index] += 1
|
pieces[index] += 1
|
||||||
let wpieces: PieceAmount = (pieces[0], pieces[1], pieces[2], pieces[3], pieces[4])
|
let wpieces: PieceAmount = (pieces[0], pieces[1], pieces[2], pieces[3],
|
||||||
let bpieces: PieceAmount = (pieces[5], pieces[6], pieces[7], pieces[8], pieces[9])
|
pieces[4])
|
||||||
|
let bpieces: PieceAmount = (pieces[5], pieces[6], pieces[7], pieces[8],
|
||||||
|
pieces[9])
|
||||||
return (wpieces in InsufficientMaterial) and (bpieces in InsufficientMaterial)
|
return (wpieces in InsufficientMaterial) and (bpieces in InsufficientMaterial)
|
||||||
|
|
||||||
proc isStalemate*(chess: Chess, color: Color): bool =
|
proc isStalemate*(chess: Chess, color: Color): bool =
|
||||||
|
125
src/engine.nim
125
src/engine.nim
@ -1,47 +1,147 @@
|
|||||||
|
import algorithm
|
||||||
|
|
||||||
import ./chess.nim
|
import ./chess.nim
|
||||||
|
|
||||||
type
|
type
|
||||||
MoveTree* = object
|
MoveTree = object
|
||||||
## `Movetree` is a visualization for possible moves.
|
## `Movetree` is a visualization for possible moves.
|
||||||
chess*: Chess
|
chess: Chess
|
||||||
evaluation: float
|
evaluation: float
|
||||||
children*: seq[Movetree]
|
children: seq[Movetree]
|
||||||
|
|
||||||
const
|
const
|
||||||
PawnVal = 1 ## `PawnVal` is the engines value for a pawn.
|
PawnVal = 10 ## `PawnVal` is the engines value for a pawn.
|
||||||
KnightVal = 3 ## `KnightVal` is the engines value for a knight.
|
KnightVal = 31 ## `KnightVal` is the engines value for a knight.
|
||||||
BishopVal = 3 ## `BishopVal` is the engines value for a bishop.
|
BishopVal = 33 ## `BishopVal` is the engines value for a bishop.
|
||||||
RookVal = 5 ## `RookVal` is the engines value for a rook.
|
RookVal = 50 ## `RookVal` is the engines value for a rook.
|
||||||
QueenVal = 9 ## `QueenVal` is the engines value for a queen.
|
QueenVal = 90 ## `QueenVal` is the engines value for a queen.
|
||||||
CheckmateVal = -1000 ## `CheckmateVal` is the engines value for a checkmate.
|
CheckmateVal = -10000 ## `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.
|
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 =
|
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 = DrawVal
|
||||||
for square in chess.board:
|
for ind, square in chess.board:
|
||||||
case square:
|
case square:
|
||||||
of WPawn:
|
of WPawn:
|
||||||
evaluation += ord(Color.White) * PawnVal
|
evaluation += ord(Color.White) * PawnVal
|
||||||
|
evaluation += ord(Color.White) * pawnTable[ind]
|
||||||
of WKnight:
|
of WKnight:
|
||||||
evaluation += ord(Color.White) * KnightVal
|
evaluation += ord(Color.White) * KnightVal
|
||||||
|
evaluation += ord(Color.White) * knightTable[ind]
|
||||||
of WBishop:
|
of WBishop:
|
||||||
evaluation += ord(Color.White) * BishopVal
|
evaluation += ord(Color.White) * BishopVal
|
||||||
|
evaluation += ord(Color.White) * bishopTable[ind]
|
||||||
of WRook:
|
of WRook:
|
||||||
evaluation += ord(Color.White) * RookVal
|
evaluation += ord(Color.White) * RookVal
|
||||||
|
evaluation += ord(Color.White) * rookTable[ind]
|
||||||
of WQueen:
|
of WQueen:
|
||||||
evaluation += ord(Color.White) * QueenVal
|
evaluation += ord(Color.White) * QueenVal
|
||||||
|
evaluation += ord(Color.White) * queenTable[ind]
|
||||||
|
of WKing:
|
||||||
|
evaluation += ord(Color.White) * kingTable[ind]
|
||||||
of BPawn:
|
of BPawn:
|
||||||
evaluation += ord(Color.Black) * PawnVal
|
evaluation += ord(Color.Black) * PawnVal
|
||||||
|
evaluation += ord(Color.Black) * pawnTable.reversed[ind]
|
||||||
of BKnight:
|
of BKnight:
|
||||||
evaluation += ord(Color.Black) * KnightVal
|
evaluation += ord(Color.Black) * KnightVal
|
||||||
|
evaluation += ord(Color.Black) * knightTable.reversed[ind]
|
||||||
of BBishop:
|
of BBishop:
|
||||||
evaluation += ord(Color.Black) * BishopVal
|
evaluation += ord(Color.Black) * BishopVal
|
||||||
|
evaluation += ord(Color.Black) * bishopTable.reversed[ind]
|
||||||
of BRook:
|
of BRook:
|
||||||
evaluation += ord(Color.Black) * RookVal
|
evaluation += ord(Color.Black) * RookVal
|
||||||
|
evaluation += ord(Color.Black) * rookTable.reversed[ind]
|
||||||
of BQueen:
|
of BQueen:
|
||||||
evaluation += ord(Color.Black) * QueenVal
|
evaluation += ord(Color.Black) * QueenVal
|
||||||
|
evaluation += ord(Color.Black) * queenTable.reversed[ind]
|
||||||
|
of BKing:
|
||||||
|
evaluation += ord(Color.White) * kingTable.reversed[ind]
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
return evaluation
|
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`.
|
## Create and return a Movetree of a given `chess` with a given maximum `depth`.
|
||||||
var mTree: MoveTree
|
var mTree: MoveTree
|
||||||
mTree.chess = chess
|
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)
|
let possibleMoves = chess.genLegalMoves(chess.toMove)
|
||||||
for move in possibleMoves:
|
for move in possibleMoves:
|
||||||
var tmpChess = chess
|
var tmpChess = chess
|
||||||
@ -97,7 +198,7 @@ proc bestMove*(chess: Chess, depth: int): Move =
|
|||||||
tmpChess.checkedMove(move)
|
tmpChess.checkedMove(move)
|
||||||
var tmpMTree = tmpChess.spanMoveTree(depth)
|
var tmpMTree = tmpChess.spanMoveTree(depth)
|
||||||
var tmpEval = -tmpMTree.negaMax()
|
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
|
||||||
bestMove = move
|
bestMove = move
|
||||||
|
@ -86,7 +86,8 @@ testSuite ChessTest of TestSuite:
|
|||||||
], Color.Black)
|
], Color.Black)
|
||||||
var testBestMove = self.chess.bestMove(2)
|
var testBestMove = self.chess.bestMove(2)
|
||||||
self.check(testBestMove.start != 0)
|
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() =
|
method testBestMoveTacticWhite() =
|
||||||
self.chess = initChess([
|
self.chess = initChess([
|
||||||
@ -101,7 +102,8 @@ testSuite ChessTest of TestSuite:
|
|||||||
], Color.White)
|
], Color.White)
|
||||||
var testBestMove = self.chess.bestMove(2)
|
var testBestMove = self.chess.bestMove(2)
|
||||||
self.check(testBestMove.start != 0)
|
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:
|
when isMainModule:
|
||||||
|
@ -8,10 +8,10 @@ import ./engine.nim
|
|||||||
let berserk = pyImport("berserk")
|
let berserk = pyImport("berserk")
|
||||||
|
|
||||||
var session = berserk.TokenSession(secret.api_token)
|
var session = berserk.TokenSession(secret.api_token)
|
||||||
var client = berserk.Client(session=session)
|
var client = berserk.Client(session = session)
|
||||||
|
|
||||||
let engineID = "tiyn-ychess"
|
let engineID = "tiyn-ychess"
|
||||||
let engineDifficulty = 2
|
let engineDifficulty = 3
|
||||||
let toAccept = ["tiynger"]
|
let toAccept = ["tiynger"]
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user