Skip to content

Commit

Permalink
optimizations all over the board
Browse files Browse the repository at this point in the history
  • Loading branch information
sictransit committed Oct 26, 2022
1 parent cf85d7c commit ae30376
Show file tree
Hide file tree
Showing 54 changed files with 22,888 additions and 5,807 deletions.
157 changes: 78 additions & 79 deletions Common/Board.cs

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions Common/BoardInternals.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ internal class BoardInternals
public static readonly ulong BlackQueensideRookCastlingSquare = new Square("d8").ToMask();


public static readonly Position WhiteKingsideRook =
new(new Piece(PieceType.Rook, PieceColor.White), WhiteKingsideRookStartingSquare.ToSquare());
public static readonly Position WhiteQueensideRook =
new(new Piece(PieceType.Rook, PieceColor.White), WhiteQueensideRookStartingSquare.ToSquare());
public static readonly Position BlackKingsideRook =
new(new Piece(PieceType.Rook, PieceColor.Black), BlackKingsideRookStartingSquare.ToSquare());
public static readonly Position BlackQueensideRook =
new(new Piece(PieceType.Rook, PieceColor.Black), BlackQueensideRookStartingSquare.ToSquare());
public static readonly Piece WhiteKingsideRook =
(Piece.Rook | Piece.White).SetMask(WhiteKingsideRookStartingSquare);
public static readonly Piece WhiteQueensideRook =
(Piece.Rook | Piece.White).SetMask(WhiteQueensideRookStartingSquare);
public static readonly Piece BlackKingsideRook =
(Piece.Rook | Piece.None).SetMask(BlackKingsideRookStartingSquare);
public static readonly Piece BlackQueensideRook =
(Piece.Rook | Piece.None).SetMask(BlackQueensideRookStartingSquare);

public Attacks Attacks { get; }

Expand Down
6 changes: 3 additions & 3 deletions Common/Extensions/BoardExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public static string PrettyPrint(this IBoard b)
{
var sb = new StringBuilder();

var positions = b.GetPositions(PieceColor.White).Concat(b.GetPositions(PieceColor.Black)).ToList();
var pieces = b.GetPieces(Piece.White).Concat(b.GetPieces(Piece.None)).ToList();

for (var rank = 7; rank >= 0; rank--)
{
Expand All @@ -21,9 +21,9 @@ public static string PrettyPrint(this IBoard b)
{
var square = new Square(file, rank);

var position = positions.SingleOrDefault(p => p.Square.Equals(square));
var piece = pieces.SingleOrDefault(p => p.GetSquare().Equals(square));

var c = position != null ? position.Piece.ToAlgebraicNotation() : ' ';
var c = piece != default ? piece.ToAlgebraicNotation() : ' ';

sb.Append($"{c} ");
}
Expand Down
14 changes: 7 additions & 7 deletions Common/Interfaces/IBoard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,27 @@ namespace SicTransit.Woodpusher.Common.Interfaces
{
public interface IBoard
{
PieceColor ActiveColor { get; }
Piece ActiveColor { get; }

IBoard PlayMove(Move move);

string GetHash();

bool IsPassedPawn(Position position);
bool IsPassedPawn(Piece piece);

IEnumerable<Move> GetOpeningBookMoves();

IEnumerable<Move> GetLegalMoves();

IEnumerable<Move> GetLegalMoves(Position position);
IEnumerable<Move> GetLegalMoves(Piece piece);

IEnumerable<Position> GetPositions(PieceColor pieceColor);
IEnumerable<Piece> GetPieces(Piece color);

IEnumerable<Position> GetPositions(PieceColor pieceColor, PieceType pieceType);
IEnumerable<Piece> GetPieces(Piece color, Piece type);

IBoard SetPosition(Position position);
IBoard SetPiece(Piece piece);

IEnumerable<Position> GetAttackers(ulong square, PieceColor color);
IEnumerable<Piece> GetAttackers(ulong square, Piece color);

int Score { get; }

Expand Down
24 changes: 11 additions & 13 deletions Common/Lookup/Attacks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,34 @@ namespace SicTransit.Woodpusher.Common.Lookup
{
public class Attacks
{
private readonly Dictionary<PieceColor, Dictionary<ulong, ThreatMask>> threatMasks = new();
private readonly Dictionary<Piece, ThreatMask> threatMasks = new();

public Attacks()
{
Initialize();
}

public ThreatMask GetThreatMask(PieceColor pieceColor, ulong current) => threatMasks[pieceColor][current];
public ThreatMask GetThreatMask(Piece piece) => threatMasks[piece];

private void Initialize()
{
foreach (var color in new[] { PieceColor.White, PieceColor.Black })
foreach (var color in new[] { Piece.White, Piece.None })
{
threatMasks.Add(color, new Dictionary<ulong, ThreatMask>());

var squares = Enumerable.Range(0, 8).Select(f => Enumerable.Range(0, 8).Select(r => new Square(f, r))).SelectMany(x => x).ToList();

foreach (var square in squares)
{
var queenMask = QueenMovement.GetTargetVectors(new Position(new Piece(PieceType.Queen, color), square)).SelectMany(v => v).Aggregate(0ul, (a, b) => a | b.GetTarget().ToMask());
var bishopMask = BishopMovement.GetTargetVectors(new Position(new Piece(PieceType.Bishop, color), square)).SelectMany(v => v).Aggregate(0ul, (a, b) => a | b.GetTarget().ToMask());
var knightMask = KnightMovement.GetTargetVectors(new Position(new Piece(PieceType.Knight, color), square)).SelectMany(v => v).Aggregate(0ul, (a, b) => a | b.GetTarget().ToMask());
var rookMask = RookMovement.GetTargetVectors(new Position(new Piece(PieceType.Rook, color), square)).SelectMany(v => v).Aggregate(0ul, (a, b) => a | b.GetTarget().ToMask());
var kingMask = KingMovement.GetTargetVectors(new Position(new Piece(PieceType.King, color.OpponentColor()), square)).SelectMany(v => v).Where(v => !v.Flags.HasFlag(SpecialMove.CastleQueen) && !v.Flags.HasFlag(SpecialMove.CastleKing)).Aggregate(0ul, (a, b) => a | b.GetTarget().ToMask());
var queenMask = QueenMovement.GetTargetVectors((Piece.Queen | color).SetSquare(square)).SelectMany(v => v).Aggregate(0ul, (a, b) => a | b.GetTarget().ToMask());
var bishopMask = BishopMovement.GetTargetVectors((Piece.Queen | color).SetSquare(square)).SelectMany(v => v).Aggregate(0ul, (a, b) => a | b.GetTarget().ToMask());
var knightMask = KnightMovement.GetTargetVectors((Piece.Queen | color).SetSquare(square)).SelectMany(v => v).Aggregate(0ul, (a, b) => a | b.GetTarget().ToMask());
var rookMask = RookMovement.GetTargetVectors((Piece.Queen | color).SetSquare(square)).SelectMany(v => v).Aggregate(0ul, (a, b) => a | b.GetTarget().ToMask());
var kingMask = KingMovement.GetTargetVectors((Piece.Queen | color.OpponentColor()).SetSquare(square)).SelectMany(v => v).Where(v => !v.Flags.HasFlag(SpecialMove.CastleQueen) && !v.Flags.HasFlag(SpecialMove.CastleKing)).Aggregate(0ul, (a, b) => a | b.GetTarget().ToMask());

var pawnMask = 0ul;

switch (color)
{
case PieceColor.White when square.Rank < 6:
case Piece.White when square.Rank < 6:
{
if (Square.TryCreate(square.File - 1, square.Rank + 1, out var upLeft))
{
Expand All @@ -49,7 +47,7 @@ private void Initialize()

break;
}
case PieceColor.Black when square.Rank > 1:
case Piece.None when square.Rank > 1:
{
if (Square.TryCreate(square.File - 1, square.Rank - 1, out var downLeft))
{
Expand All @@ -64,7 +62,7 @@ private void Initialize()
}
}

threatMasks[color].Add(square.ToMask(), new ThreatMask(pawnMask, rookMask, knightMask, bishopMask, queenMask, kingMask));
threatMasks.Add(color.SetSquare(square), new ThreatMask(pawnMask, rookMask, knightMask, bishopMask, queenMask, kingMask));
}
}
}
Expand Down
76 changes: 50 additions & 26 deletions Common/Lookup/Moves.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ namespace SicTransit.Woodpusher.Common.Lookup
{
public class Moves
{
private readonly Dictionary<Position, IReadOnlyCollection<Move[]>> vectors = new();
private readonly Dictionary<Piece, IReadOnlyCollection<Move[]>> vectors = new();
private readonly Dictionary<ulong, ulong> travelMasks = new();
private readonly Dictionary<Position, ulong> passedPawnMasks = new();
private readonly Dictionary<Piece, ulong> passedPawnMasks = new();

public Moves()
{
Expand Down Expand Up @@ -39,66 +39,90 @@ private void InitializeTravelMasks()

private void InitializePassedPawnMasks()
{
var pieces = new[] { PieceColor.White, PieceColor.Black }.Select(c => new Piece(PieceType.Pawn, c));
var pieceTypes = new[] { Piece.White, Piece.None }.Select(c => Piece.Pawn | c);
var squares = Enumerable.Range(0, 8).Select(f => Enumerable.Range(1, 6).Select(r => new Square(f, r))).SelectMany(x => x).ToList();

var positions = pieces.Select(p => squares.Select(s => new Position(p, s))).SelectMany(p => p);
var pieces = pieceTypes.Select(p => squares.Select(s => p.SetSquare(s))).SelectMany(p => p);

foreach (var position in positions)
foreach (var piece in pieces)
{
var mask = 0ul;
var minRank = position.Piece.Color == PieceColor.White ? position.Square.Rank + 1 : 1;
var maxRank = position.Piece.Color == PieceColor.White ? 6 : position.Square.Rank - 1;
var minRank = piece.Is(Piece.White) ? piece.GetSquare().Rank + 1 : 1;
var maxRank = piece.Is(Piece.White) ? 6 : piece.GetSquare().Rank - 1;

foreach (var dFile in new[] { -1, 0, 1 })
{
for (var rank = minRank; rank <= maxRank; rank++)
{
if (Square.TryCreate(position.Square.File + dFile, rank, out var square))
if (Square.TryCreate(piece.GetSquare().File + dFile, rank, out var square))
{
mask |= square.ToMask();
}
}
}

passedPawnMasks.Add(position, mask);
passedPawnMasks.Add(piece, mask);
}
}

public ulong GetTravelMask(ulong current, ulong target) => travelMasks[current | target];

public ulong GetPassedPawnMask(Position position) => passedPawnMasks[position];
public ulong GetPassedPawnMask(Piece piece) => passedPawnMasks[piece];

private void InitializeVectors()
{
var pieces = new[] { PieceColor.White, PieceColor.Black }.Select(c => new[] { PieceType.Pawn, PieceType.Rook, PieceType.Knight, PieceType.Bishop, PieceType.Queen, PieceType.King }.Select(t => new Piece(t, c))).SelectMany(x => x).ToList();
var pieces = new[] { Piece.White, Piece.None }.Select(c => new[] { Piece.Pawn, Piece.Rook, Piece.Knight, Piece.Bishop, Piece.Queen, Piece.King }.Select(t => t | c)).SelectMany(x => x).ToList();
var squares = Enumerable.Range(0, 8).Select(f => Enumerable.Range(0, 8).Select(r => new Square(f, r))).SelectMany(x => x).ToList();

pieces.ForEach(piece =>
pieces.ForEach(p =>
{
squares.ForEach(square =>
squares.ForEach(s =>
{
var position = new Position(piece, square);
var piece = p.SetSquare(s);
vectors.Add(position, CreateVectors(position).Select(v => v.ToArray()).ToArray());
vectors.Add(piece, CreateVectors(piece).Select(v => v.ToArray()).ToArray());
});
});
}

public IReadOnlyCollection<Move[]> GetVectors(Position position)
public IReadOnlyCollection<Move[]> GetVectors(Piece piece)
{
return vectors[position];
return vectors[piece];
}

private static IEnumerable<IEnumerable<Move>> CreateVectors(Position position) => position.Piece.Type switch
private static IEnumerable<IEnumerable<Move>> CreateVectors(Piece piece)
{
PieceType.Pawn => PawnMovement.GetTargetVectors(position),
PieceType.Rook => RookMovement.GetTargetVectors(position),
PieceType.Knight => KnightMovement.GetTargetVectors(position),
PieceType.Bishop => BishopMovement.GetTargetVectors(position),
PieceType.Queen => QueenMovement.GetTargetVectors(position),
PieceType.King => KingMovement.GetTargetVectors(position),
_ => throw new ArgumentOutOfRangeException(nameof(position)),
};
if (piece.Is(Piece.Pawn))
{
return PawnMovement.GetTargetVectors(piece);
}

if (piece.Is(Piece.Rook))
{
return RookMovement.GetTargetVectors(piece);
}

if (piece.Is(Piece.Knight))
{
return KnightMovement.GetTargetVectors(piece);
}

if (piece.Is(Piece.Bishop))
{
return BishopMovement.GetTargetVectors(piece);
}

if (piece.Is(Piece.Queen))
{
return QueenMovement.GetTargetVectors(piece);
}

if (piece.Is(Piece.King))
{
return KingMovement.GetTargetVectors(piece);
}

throw new ArgumentOutOfRangeException(nameof(piece));
}
}
}
59 changes: 30 additions & 29 deletions Common/Lookup/Scoring.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using SicTransit.Woodpusher.Model;
using SicTransit.Woodpusher.Model.Enums;
using SicTransit.Woodpusher.Model.Extensions;

namespace SicTransit.Woodpusher.Common.Lookup
{
Expand All @@ -8,8 +9,8 @@ namespace SicTransit.Woodpusher.Common.Lookup

public class Scoring
{
private readonly Dictionary<Position, int> middleGameEvaluations = new();
private readonly Dictionary<Position, int> endGameEvaluations = new();
private readonly Dictionary<Piece, int> middleGameEvaluations = new();
private readonly Dictionary<Piece, int> endGameEvaluations = new();

public const int MateScore = 1000000;
public const int DrawScore = 0;
Expand Down Expand Up @@ -161,58 +162,58 @@ public class Scoring
-53, -34, -21, -11, -28, -14, -24, -43
};

private static int GetModifierIndex(Position position) => position.Piece.Color == PieceColor.Black
? position.Square.File + position.Square.Rank * 8
: (position.Square.File + position.Square.Rank * 8) ^ 56;
private static int GetModifierIndex(Piece piece) => piece.Is(Piece.White)
? (piece.GetSquare().File + piece.GetSquare().Rank * 8) ^ 56
: piece.GetSquare().File + piece.GetSquare().Rank * 8;

public Scoring()
{
InitializeEvaluations(middleGameEvaluations, false);
InitializeEvaluations(endGameEvaluations, true);
}

private static void InitializeEvaluations(Dictionary<Position, int> evaluations, bool endGame)
private static void InitializeEvaluations(Dictionary<Piece, int> evaluations, bool endGame)
{
var pieces = new[] { PieceColor.Black, PieceColor.White }.Select(c => new[] { PieceType.Pawn, PieceType.Rook, PieceType.Knight, PieceType.Bishop, PieceType.Queen, PieceType.King }.Select(t => new Piece(t, c))).SelectMany(x => x).ToList();
var pieceTypes = new[] { Piece.None, Piece.White }.Select(c => new[] { Piece.Pawn, Piece.Rook, Piece.Knight, Piece.Bishop, Piece.Queen, Piece.King }.Select(t => t | c)).SelectMany(x => x).ToList();
var squares = Enumerable.Range(0, 8).Select(f => Enumerable.Range(0, 8).Select(r => new Square(f, r))).SelectMany(x => x).ToList();
var positions = pieces.Select(p => squares.Select(s => new Position(p, s))).SelectMany(p => p);
var pieces = pieceTypes.Select(p => squares.Select(s => p.SetSquare(s))).SelectMany(p => p);

foreach (var position in positions)
foreach (var piece in pieces)
{
var index = GetModifierIndex(position);
var evaluation = GetPieceValue(position.Piece.Type, endGame) + position.Piece.Type switch
var index = GetModifierIndex(piece);
var evaluation = GetPieceValue(piece.GetPieceType(), endGame) + piece.GetPieceType() switch
{
PieceType.Pawn => endGame ? PawnEndGameModifiers[index] : PawnMiddleGameModifiers[index],
PieceType.Knight => endGame ? KnightEndGameModifiers[index] : KnightMiddleGameModifiers[index],
PieceType.Bishop => endGame ? BishopEndGameModifiers[index] : BishopMiddleGameModifiers[index],
PieceType.Rook => endGame ? RookEndGameModifiers[index] : RookMiddleGameModifiers[index],
PieceType.Queen => endGame ? QueenEndGameModifiers[index] : QueenMiddleGameModifiers[index],
PieceType.King => endGame ? KingEndGameModifiers[index] : KingMiddleGameModifiers[index],
_ => throw new ArgumentException(position.Piece.Type.ToString()),
Piece.Pawn => endGame ? PawnEndGameModifiers[index] : PawnMiddleGameModifiers[index],
Piece.Knight => endGame ? KnightEndGameModifiers[index] : KnightMiddleGameModifiers[index],
Piece.Bishop => endGame ? BishopEndGameModifiers[index] : BishopMiddleGameModifiers[index],
Piece.Rook => endGame ? RookEndGameModifiers[index] : RookMiddleGameModifiers[index],
Piece.Queen => endGame ? QueenEndGameModifiers[index] : QueenMiddleGameModifiers[index],
Piece.King => endGame ? KingEndGameModifiers[index] : KingMiddleGameModifiers[index],
_ => throw new ArgumentException(piece.ToString()),
};
evaluations.Add(position, evaluation);
evaluations.Add(piece, evaluation);
}
}

private static int GetPieceValue(PieceType pieceType, bool endGame) => pieceType switch
private static int GetPieceValue(Piece pieceType, bool endGame) => pieceType switch
{
PieceType.Pawn => endGame ? 94 : 82,
PieceType.Knight => endGame ? 281 : 337,
PieceType.Bishop => endGame ? 297 : 365,
PieceType.Rook => endGame ? 512 : 477,
PieceType.Queen => endGame ? 936 : 1025,
PieceType.King => 0,
Piece.Pawn => endGame ? 94 : 82,
Piece.Knight => endGame ? 281 : 337,
Piece.Bishop => endGame ? 297 : 365,
Piece.Rook => endGame ? 512 : 477,
Piece.Queen => endGame ? 936 : 1025,
Piece.King => 0,
_ => throw new ArgumentException(pieceType.ToString()),
};


public int EvaluatePosition(Position position, int phase)
public int EvaluatePiece(Piece piece, int phase)
{
// TODO: This will not handle promotions.
phase = Math.Max(0, Math.Min(phase, 24));

var end = endGameEvaluations[position] * (24 - phase);
var middle = middleGameEvaluations[position] * phase;
var end = endGameEvaluations[piece] * (24 - phase);
var middle = middleGameEvaluations[piece] * phase;

return (middle + end) / 24;
}
Expand Down
Loading

0 comments on commit ae30376

Please sign in to comment.