Skip to content

Commit

Permalink
uci option to toggle opening book usage
Browse files Browse the repository at this point in the history
  • Loading branch information
sictransit authored Nov 27, 2024
1 parent 6fb539e commit d1e14f5
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 23 deletions.
9 changes: 9 additions & 0 deletions Common/EngineOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace SicTransit.Woodpusher.Common
{
public class EngineOptions
{
public bool UseOpeningBook { get; set; } = false;

public static EngineOptions Default => new();
}
}
2 changes: 1 addition & 1 deletion Common/Interfaces/IEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ public interface IEngine
{
IBoard Board { get; }

void Initialize();
void Initialize(EngineOptions options);

void Stop();

Expand Down
43 changes: 25 additions & 18 deletions Engine/Patzer.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Serilog;
using SicTransit.Woodpusher.Common;
using SicTransit.Woodpusher.Common.Extensions;
using SicTransit.Woodpusher.Common.Interfaces;
using SicTransit.Woodpusher.Common.Lookup;
Expand All @@ -23,8 +24,7 @@ public class Patzer : IEngine

private int PlayerSign => Board.ActiveColor.Is(Piece.White) ? 1 : -1;

private readonly OpeningBook whiteOpeningBook;
private readonly OpeningBook blackOpeningBook;
private OpeningBook? openingBook;

private readonly Action<string>? infoCallback;

Expand All @@ -38,20 +38,23 @@ public class Patzer : IEngine

private Move? evaluatedBestMove = null;

private EngineOptions engineOptions;

public Patzer(Action<string>? infoCallback = null)
{
whiteOpeningBook = new OpeningBook(Piece.White);
blackOpeningBook = new OpeningBook(Piece.None);

this.infoCallback = infoCallback;

Initialize();
Initialize(EngineOptions.Default);
}

public void Initialize()
public void Initialize(EngineOptions options)
{
engineOptions = options;

SetBoard(Common.Board.StartingPosition());

openingBook = null;

for (var i = 0; i < killerMoves.Length; i++)
{
killerMoves[i] = new ulong[2];
Expand Down Expand Up @@ -84,7 +87,8 @@ public void Play(Move move)

private Move? GetOpeningBookMove()
{
var openingBook = Board.ActiveColor == Piece.White ? whiteOpeningBook : blackOpeningBook;
openingBook ??= Board.ActiveColor == Piece.White ? new OpeningBook(Piece.White) : new OpeningBook(Piece.None);

var openingBookMoves = openingBook.GetMoves(Board.Hash);
var legalMoves = Board.GetLegalMoves().ToArray();
var legalOpeningBookMoves = openingBookMoves
Expand All @@ -96,7 +100,8 @@ public void Play(Move move)

private Move? GetTheoryMove()
{
var openingBook = Board.ActiveColor == Piece.White ? whiteOpeningBook : blackOpeningBook;
openingBook ??= Board.ActiveColor == Piece.White ? new OpeningBook(Piece.White) : new OpeningBook(Piece.None);

var theoryMoves = Board.PlayLegalMoves()
.Select(b => new { move = b.Counters.LastMove, count = openingBook?.GetMoves(b.Hash).Count() ?? 0 })
.Where(t => t.count > 0)
Expand Down Expand Up @@ -202,14 +207,17 @@ private void AddKillerMove(int ply, ulong hash)
var progress = new List<(int depth, long time)>();
Array.Clear(transpositionTable, 0, transpositionTable.Length);

var bookMove = GetOpeningBookMove() ?? GetTheoryMove();
if (bookMove != null)
if (engineOptions.UseOpeningBook)
{
UpdateBestLine(bookMove);
Log.Information("Returning book move: {0}", bookMove);
SendDebugInfo($"playing book move {bookMove.ToAlgebraicMoveNotation()}");
SendProgress(stopwatch, 0, null);
return new AlgebraicMove(bookMove);
var bookMove = GetOpeningBookMove() ?? GetTheoryMove();
if (bookMove != null)
{
UpdateBestLine(bookMove);
Log.Information("Returning book move: {0}", bookMove);
SendDebugInfo($"playing book move {bookMove.ToAlgebraicMoveNotation()}");
SendProgress(stopwatch, 0, null);
return new AlgebraicMove(bookMove);
}
}

while (maxDepth < engineMaxDepth)
Expand Down Expand Up @@ -469,8 +477,7 @@ private int EvaluateBoard(IBoard board, int depth, int α, int β, int sign)
{
bestScore = board.IsChecked ? -Scoring.MateScore + depth : Scoring.DrawScore;
}

if (transpositionTable[transpositionIndex].Depth <= maxDepth - depth)
else if (transpositionTable[transpositionIndex].Depth <= maxDepth - depth)
{
transpositionTable[transpositionIndex] = new TranspositionTableEntry(
bestScore <= α0 ? Enum.EntryType.UpperBound : bestScore >= β ? Enum.EntryType.LowerBound : Enum.EntryType.Exact,
Expand Down
2 changes: 1 addition & 1 deletion EngineTests/OpeningBookTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ static bool eloPredicate(PortableGameNotation game)

foreach (var game in games.Where(g => g.PgnMoves.Any() && g.Result == (white ? Result.WhiteWin : Result.BlackWin)).OrderByDescending(g => white ? g.WhiteElo : g.BlackElo).Take(1000))
{
engine.Initialize();
engine.Initialize(EngineOptions.Default);

try
{
Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@ A more or less UCI-compatible chess engine. Plug it into your favourite GUI.

## Features
- UCI-compatible
- PGN and FEN parsing
- Perft test
- Opening book
- Zobrist hashing
- Transposition table
- Killer heuristic

## UCI Commands

Expand All @@ -19,11 +23,25 @@ Initializes the engine and provides engine information.
**Example:**
```
uci
id name Woodpusher 1.0.0+169dfebfdb4749ad97aabcbd779ec507f051ad72
id name Woodpusher 1.2.0+17b3326627bad1b95724019c2ab68eb6041c4409
id author Mikael Fredriksson <[email protected]>
option name OwnBook type check default true
uciok
```

### `setoption`

Sets an engine option.

**Arguments:**
- `name <option_name>`: The name of the option.
- `value <option_value>`: The value of the option.

**Example:**
```
setoption name OwnBook value false
```

### `isready`
Checks if the engine is ready.

Expand Down
31 changes: 30 additions & 1 deletion Woodpusher/UniversalChessInterface.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Serilog;
using SicTransit.Woodpusher.Common;
using SicTransit.Woodpusher.Common.Extensions;
using SicTransit.Woodpusher.Common.Interfaces;
using SicTransit.Woodpusher.Common.Parsing;
Expand All @@ -22,6 +23,7 @@ public class UniversalChessInterface
private static readonly Regex PositionCommand = new(@"^position", RegexOptions.Compiled);
private static readonly Regex GoCommand = new(@"^go", RegexOptions.Compiled);
private static readonly Regex DisplayCommand = new(@"^d$", RegexOptions.Compiled);
private static readonly Regex SetOptionCommand = new(@"^setoption", RegexOptions.Compiled);


private static readonly Regex PositionRegex =
Expand All @@ -32,11 +34,14 @@ public class UniversalChessInterface
private static readonly Regex MovesToGoRegex = new(@"movestogo (\d+)", RegexOptions.Compiled);
private static readonly Regex MovetimeRegex = new(@"movetime (\d+)", RegexOptions.Compiled);
private static readonly Regex PerftRegex = new(@"perft (\d+)", RegexOptions.Compiled);
private static readonly Regex OptionRegex = new(@"^setoption name (\w+) value (\w+)$", RegexOptions.Compiled);

private const int engineLatency = 100;

private readonly IEngine engine;

private EngineOptions options = EngineOptions.Default;

public UniversalChessInterface(Action<string> consoleOutput, IEngine engine)
{
this.consoleOutput = consoleOutput;
Expand Down Expand Up @@ -75,6 +80,10 @@ public void ProcessCommand(string command)
{
task = Display();
}
else if (SetOptionCommand.IsMatch(command))
{
task = SetOption(command);
}
else if (QuitCommand.IsMatch(command))
{
Quit = true;
Expand Down Expand Up @@ -107,6 +116,7 @@ private Task Uci()

consoleOutput($"id name Woodpusher {version}");
consoleOutput("id author Mikael Fredriksson <[email protected]>");
consoleOutput("option name OwnBook type check default true");
consoleOutput("uciok");
}
});
Expand All @@ -118,7 +128,7 @@ private Task Initialize()
{
lock (engine)
{
engine.Initialize();
engine.Initialize(options);
}
});
}
Expand All @@ -142,6 +152,25 @@ private Task Stop()
});
}

private Task SetOption(string command)
{
return Task.Run(() =>
{
var match = OptionRegex.Match(command);
if (!match.Success)
{
Log.Error("Unable to parse: {Command}", command);
return;
}
var name = match.Groups[1].Value;
var value = match.Groups[2].Value;
if (name == "OwnBook")
{
options.UseOpeningBook = bool.Parse(value);
}
});
}

private Task Position(string command)
{
return Task.Run(() =>
Expand Down
2 changes: 1 addition & 1 deletion Woodpusher/Woodpusher.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<Nullable>enable</Nullable>
<RootNamespace>SicTransit.Woodpusher</RootNamespace>
<Authors>Mikael Fredriksson &lt;[email protected]&gt;</Authors>
<Version>1.1.0</Version>
<Version>1.2.0</Version>
</PropertyGroup>

<ItemGroup>
Expand Down

0 comments on commit d1e14f5

Please sign in to comment.