Skip to content

Commit

Permalink
improved performance + not trying to make illegal moves
Browse files Browse the repository at this point in the history
  • Loading branch information
sictransit committed Oct 2, 2022
1 parent 3c17a17 commit f4179ff
Show file tree
Hide file tree
Showing 39 changed files with 624 additions and 594 deletions.
19 changes: 0 additions & 19 deletions Common/Interfaces/IEngine.cs

This file was deleted.

2 changes: 1 addition & 1 deletion Common/Logging.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public static void EnableLogging(LogEventLevel level = LogEventLevel.Information
}

Log.Logger = loggerConfiguration
.WriteTo.File(logFilename, LogEventLevel.Information)
.WriteTo.File(logFilename, LogEventLevel.Information, shared: true)
.CreateLogger();
}

Expand Down
5 changes: 5 additions & 0 deletions Engine/MoveEvaluation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,10 @@ public MoveEvaluation(Move move)
public Move Move { get; set; }

public int Score { get; set; }

public override string ToString()
{
return $"{Move} {Score} / {NodeCount}";
}
}
}
91 changes: 60 additions & 31 deletions Engine/Patzer.cs
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
using Serilog;
using SicTransit.Woodpusher.Common.Interfaces;
using SicTransit.Woodpusher.Model;
using SicTransit.Woodpusher.Model.Enums;
using SicTransit.Woodpusher.Model.Extensions;
using SicTransit.Woodpusher.Model.Interfaces;
using SicTransit.Woodpusher.Parsing;
using System.Diagnostics;

namespace SicTransit.Woodpusher.Engine
{
public class Patzer : IEngine
{
public Board Board { get; private set; }
public IBoard Board { get; private set; }

private readonly Random random = new();

private CancellationTokenSource cancellationTokenSource = new();

private const int MATE_SCORE = 1000000;
private const int WHITE_MATE_SCORE = -MATE_SCORE;
private const int BLACK_MATE_SCORE = MATE_SCORE;
private const int DRAW_SCORE = 0;
private const int MAX_DEPTH = 32;

private DateTime deadline;

public Patzer()
{
Board = ForsythEdwardsNotation.Parse(ForsythEdwardsNotation.StartingPosition);
Expand All @@ -34,18 +34,20 @@ public void Initialize()

public void Play(Move move)
{
Board = Board.Play(move);
Log.Information($"{Board.ActiveColor} plays: {move}");

Log.Debug($"played: {move}");
Board = Board.PlayMove(move);
}

public void Position(string fen, IEnumerable<AlgebraicMove> algebraicMoves)
public void Position(string fen, IEnumerable<AlgebraicMove>? algebraicMoves = null)
{
algebraicMoves ??= Enumerable.Empty<AlgebraicMove>();

Board = ForsythEdwardsNotation.Parse(fen);

foreach (var algebraicMove in algebraicMoves)
{
var move = Board.GetLegalMoves().SingleOrDefault(m => m.Position.Square.Equals(algebraicMove.From) && m.Target.Square.Equals(algebraicMove.To) && m.Target.PromotionType == algebraicMove.Promotion);
var move = Board.GetLegalMoves().SingleOrDefault(m => m.Position.Square.Equals(algebraicMove.From) && m.GetTarget().Equals(algebraicMove.To) && m.PromotionType == algebraicMove.Promotion);

if (move == null)
{
Expand All @@ -56,48 +58,75 @@ public void Position(string fen, IEnumerable<AlgebraicMove> algebraicMoves)
}
}

public AlgebraicMove FindBestMove(int limit = 1000, Action<string>? infoCallback = null)
public AlgebraicMove FindBestMove(int timeLimit = 1000, Action<string>? infoCallback = null)
{
cancellationTokenSource = new CancellationTokenSource();

ThreadPool.QueueUserWorkItem(_ =>
{
Thread.Sleep(timeLimit);
cancellationTokenSource.Cancel();
});

var sw = new Stopwatch();
sw.Start();

deadline = DateTime.UtcNow.AddMilliseconds(limit);

var sign = Board.ActiveColor == PieceColor.White ? 1 : -1;
var nodeCount = 0ul;

var evaluations = Board.GetLegalMoves().Select(m => new MoveEvaluation(m)).ToList();

foreach (var depth in new[] { 1, 2, 3, 5, 8, 13, 21 })
Log.Information($"Legal moves for {Board.ActiveColor}: {string.Join(';', evaluations.Select(e => e.Move))}");

var cancellationToken = cancellationTokenSource.Token;

var parallelOptions = new ParallelOptions
{
if (evaluations.Count() <= 1)
CancellationToken = cancellationToken,
MaxDegreeOfParallelism = -1
};

var depth = 0;

while (!cancellationTokenSource.IsCancellationRequested)
{
if (evaluations.Count <= 1)
{
break;
}

if (evaluations.Any(e => Math.Abs(Math.Abs(e.Score) - MATE_SCORE) < MAX_DEPTH))
if (evaluations.Any(e => Math.Abs(e.Score - MATE_SCORE) < MAX_DEPTH))
{
break;
}

// 2,1 Mnode/s
Parallel.ForEach(evaluations.OrderByDescending(e => e.Score).ToArray(), e =>
foreach (var chunk in evaluations.OrderByDescending(e => e.Score).Chunk(Environment.ProcessorCount))
{
if (DateTime.UtcNow > deadline)
try
{
Log.Debug("aborting due to lack of time");
Parallel.ForEach(chunk, parallelOptions, e =>
{
var board = Board.PlayMove(e.Move);
return;
}
var score = EvaluateBoard(board, board.ActiveColor == PieceColor.White, depth, e, int.MinValue, int.MaxValue, cancellationToken) * sign;
var board = Board.Play(e.Move);
if (!cancellationToken.IsCancellationRequested)
{
e.Score = score;
e.Score = EvaluateBoard(board, board.ActiveColor == PieceColor.White, depth, e) * sign;
nodeCount += e.NodeCount;
nodeCount += e.NodeCount;
infoCallback?.Invoke($"info depth {depth} nodes {nodeCount} score cp {e.Score * sign} pv {e.Move.ToAlgebraicMoveNotation()} nps {nodeCount * 1000 / (ulong)(1 + sw.ElapsedMilliseconds)}");
}
});
}
catch (OperationCanceledException)
{
break;
}
}

infoCallback?.Invoke($"info depth {depth} nodes {nodeCount} score cp {e.Score * sign} pv {e.Move.ToAlgebraicMoveNotation()} nps {nodeCount * 1000 / (ulong)sw.ElapsedMilliseconds}");
});
depth++;
}

if (evaluations.Any())
Expand All @@ -117,16 +146,16 @@ public AlgebraicMove FindBestMove(int limit = 1000, Action<string>? infoCallback
return null;
}

private int EvaluateBoard(Board board, bool maximizing, int depth, MoveEvaluation evaluation, int alpha = int.MinValue, int beta = int.MaxValue)
private int EvaluateBoard(IBoard board, bool maximizing, int depth, MoveEvaluation evaluation, int alpha, int beta, CancellationToken cancellationToken)
{
var moves = board.GetLegalMoves().ToArray();
var moves = board.GetLegalMoves();

if (moves.Length == 0)
if (!moves.Any())
{
return board.IsChecked ? maximizing ? WHITE_MATE_SCORE - depth : BLACK_MATE_SCORE + depth : DRAW_SCORE;
}

if (depth == 0 || DateTime.UtcNow > deadline)
if (depth == 0 || cancellationToken.IsCancellationRequested)
{
return board.Score;
}
Expand All @@ -137,7 +166,7 @@ private int EvaluateBoard(Board board, bool maximizing, int depth, MoveEvaluatio
{
evaluation.NodeCount++;

var score = EvaluateBoard(board.Play(move), !maximizing, depth - 1, evaluation, alpha, beta);
var score = EvaluateBoard(board.PlayMove(move), !maximizing, depth - 1, evaluation, alpha, beta, cancellationToken);

if (maximizing)
{
Expand Down Expand Up @@ -168,7 +197,7 @@ private int EvaluateBoard(Board board, bool maximizing, int depth, MoveEvaluatio

public void Stop()
{
deadline = DateTime.UtcNow;
cancellationTokenSource.Cancel();
}
}
}
69 changes: 63 additions & 6 deletions EngineTests/PatzerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public void PlayBestMoveTest()
{
var patzer = new Patzer();

var bestMove = patzer.FindBestMove();
var bestMove = patzer.FindBestMove(5000);

Assert.IsNotNull(bestMove);

Expand Down Expand Up @@ -84,7 +84,7 @@ public void TakeTheQueenTest()

var patzer = new Patzer();

patzer.Position("Q2K3k/8/2p5/3b4/1p6/1nP5/qq6/5r2 b - - 0 93", Enumerable.Empty<AlgebraicMove>());
patzer.Position("Q2K3k/8/2p5/3b4/1p6/1nP5/qq6/5r2 b - - 0 93");

var bestMove = patzer.FindBestMove();

Expand All @@ -95,7 +95,7 @@ public void TakeTheQueenTest()
public void FindMateInOneTest()
{
var patzer = new Patzer();
patzer.Position("k7/8/1QP5/8/8/8/8/7K w - - 0 1", Enumerable.Empty<AlgebraicMove>());
patzer.Position("k7/8/1QP5/8/8/8/8/7K w - - 0 1");

var bestMove = patzer.FindBestMove();

Expand All @@ -107,24 +107,81 @@ public void DoNotPlayQueenD2Test()
{
var patzer = new Patzer();

patzer.Position("rnbqkbnr/ppp2ppp/8/4p3/4N3/5N2/PPPP1PPP/R1BQKB1R b KQkq - 0 4", Enumerable.Empty<AlgebraicMove>());
patzer.Position("rnbqkbnr/ppp2ppp/8/4p3/4N3/5N2/PPPP1PPP/R1BQKB1R b KQkq - 0 4");

var bestMove = patzer.FindBestMove();

Assert.AreNotEqual("d8d2", bestMove.Notation);
}

[TestMethod]
public void PlayE8F8OrDieTest()
{
// e8f8 is a good move; pushing e.g. a B pawn is not

//+---+---+---+---+---+---+---+---+
//| r | n | b | q | k | | n | r | 8
//+---+---+---+---+---+---+---+---+
//| p | p | p | | b | | | p | 7
//+---+---+---+---+---+---+---+---+
//| | | | | | | P | | 6
//+---+---+---+---+---+---+---+---+
//| | | | p | | | | Q | 5
//+---+---+---+---+---+---+---+---+
//| | | | N | p | | | | 4
//+---+---+---+---+---+---+---+---+
//| | | N | | | | | | 3
//+---+---+---+---+---+---+---+---+
//| P | P | P | P | | P | P | P | 2
//+---+---+---+---+---+---+---+---+
//| R | | B | | K | B | | R | 1
//+---+---+---+---+---+---+---+---+
// a b c d e f g h

var patzer = new Patzer();

patzer.Position("rnbqk1nr/ppp1b2p/6P1/3p3Q/3Np3/2N5/PPPP1PPP/R1B1KB1R b KQkq - 0 7");

var bestMove = patzer.FindBestMove(10000);

Assert.AreEqual("e8f8", bestMove.Notation);
}

[TestMethod]
public void FindMateInThreeTest()
{
var patzer = new Patzer();

patzer.Position("8/pk6/3B4/1B6/8/P1N5/1Pb2KP1/4R3 w - - 1 38", Enumerable.Empty<AlgebraicMove>());
patzer.Position("8/pk6/3B4/1B6/8/P1N5/1Pb2KP1/4R3 w - - 1 38");

var bestMove = patzer.FindBestMove();
var bestMove = patzer.FindBestMove(10000);

Assert.AreEqual("e1e7", bestMove.Notation);
}

[TestMethod]
public void QueenG5IsNotAnOption()
{
// For some reason it preferred Qg5 before any other move?!

//8 r n b q k b n r
//7 p p p p p p
//6
//5 p p
//4 P
//3 P N
//2 P P P P P P
//1 R N B Q K B R
// A B C D E F G H

var patzer = new Patzer();

patzer.Position("rnbqkbnr/ppp2ppp/8/3pp3/4P3/3P1N2/PPP2PPP/RNBQKB1R b KQkq - 0 3");

var bestMove = patzer.FindBestMove(10000);

Assert.AreNotEqual("d8g5", bestMove.Notation);
}
}


Expand Down
2 changes: 1 addition & 1 deletion Model/AlgebraicMove.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ private AlgebraicMove(Square from, Square to, PieceType promotion)
Promotion = promotion;
}

public AlgebraicMove(Move move) : this(move.Position.Square, move.Target.Square, move.Target.PromotionType)
public AlgebraicMove(Move move) : this(move.Position.Square, move.GetTarget(), move.PromotionType)
{
}

Expand Down
Loading

0 comments on commit f4179ff

Please sign in to comment.