Skip to content

Commit 8544860

Browse files
committed
Search now gathers the PV. No TriangularTable necessary anymore.
Reverted changes to TC. Cleanup&Refactoring. Reduced code size to 665 LOC and gained about +20 ELO over 0.5.0.
1 parent 6b9798c commit 8544860

File tree

16 files changed

+50
-161
lines changed

16 files changed

+50
-161
lines changed

MinimalChess/Attacks.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ private static byte[] WalkTheLine(int rank, int file, int dRank, int dFile)
8989
private static void TryAddSquare(this List<byte> buffer, int rank, int file)
9090
{
9191
if (IsLegalSquare(rank, file))
92-
IndexBuffer.AddSquare(rank, file);
92+
buffer.AddSquare(rank, file);
9393
}
9494

9595
private static void AddSquare(this List<byte> buffer, int rank, int file) => buffer.Add((byte)(rank * 8 + file));

MinimalChess/Board.cs

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -679,22 +679,5 @@ public ulong GetZobristHash()
679679
hash ^= Zobrist.EnPassant(_enPassantSquare);
680680
return hash;
681681
}
682-
683-
public override int GetHashCode()
684-
{
685-
//perft 5 on Kiwipete r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1
686-
//Moves: 193,690,690
687-
//Unique Positions: 30,216,804
688-
//Unique HashCodes: 30,111,082
689-
//HashCollisions: 105,722 (0 with 64bit)
690-
return (int)_zobristHash;
691-
}
692-
693-
//[Conditional("DEBUG")]
694-
//private void ValidateZobristHash()
695-
//{
696-
// ulong zobrist = GetZobristHash();
697-
// Debug.Assert(zobrist == _zobristHash);
698-
//}
699682
}
700683
}

MinimalChess/IterativeSearch.cs

Lines changed: 28 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
42

53
namespace MinimalChess
64
{
@@ -16,7 +14,7 @@ public class IterativeSearch
1614
public bool Aborted => NodesVisited >= _maxNodes || _killSwitch.Get(NodesVisited % QUERY_TC_FREQUENCY == 0);
1715
public bool GameOver => Evaluation.IsCheckmate(Score);
1816

19-
Board _root = null;
17+
Board _root;
2018
KillerMoves _killers;
2119
KillSwitch _killSwitch;
2220
long _maxNodes;
@@ -41,17 +39,9 @@ public void SearchDeeper(Func<bool> killSwitch = null)
4139

4240
Depth++;
4341
_killers.Resize(Depth);
44-
TriangularTable.Init(Depth);
4542
StorePVinTT(PrincipalVariation, Depth);
4643
_killSwitch = new KillSwitch(killSwitch);
47-
48-
int score = EvalPosition(_root, Depth, SearchWindow.Infinite);
49-
50-
if (!Aborted)
51-
{
52-
Score = score;
53-
PrincipalVariation = TriangularTable.GetLine(Depth);
54-
}
44+
(Score, PrincipalVariation) = EvalPosition(_root, Depth, SearchWindow.Infinite);
5545
}
5646

5747
private void StorePVinTT(Move[] pv, int depth)
@@ -64,30 +54,27 @@ private void StorePVinTT(Move[] pv, int depth)
6454
}
6555
}
6656

67-
private int EvalPositionTT(Board position, int depth, SearchWindow window, bool isNullMove = false)
57+
private (int Score, Move[] PV) EvalPositionTT(Board position, int depth, SearchWindow window, bool isNullMove = false)
6858
{
69-
if (Transpositions.GetScore(position, depth, window, out int score))
70-
{
71-
TriangularTable.Truncate(depth);
72-
return score;
73-
}
59+
if (Transpositions.GetScore(position, depth, window, out int ttScore))
60+
return (ttScore, Array.Empty<Move>());
7461

75-
score = EvalPosition(position, depth, window, isNullMove);
76-
Transpositions.Store(position.ZobristHash, depth, window, score, default);
77-
return score;
62+
var result = EvalPosition(position, depth, window, isNullMove);
63+
Transpositions.Store(position.ZobristHash, depth, window, result.Score, default);
64+
return result;
7865
}
7966

80-
private int EvalPosition(Board position, int depth, SearchWindow window, bool isNullMove = false)
67+
private (int Score, Move[] PV) EvalPosition(Board position, int depth, SearchWindow window, bool isNullMove = false)
8168
{
8269
if (depth <= 0)
8370
{
8471
Evaluation.DynamicScore = Evaluation.ComputeMobility(position);
85-
return QEval(position, window);
72+
return (QEval(position, window), Array.Empty<Move>());
8673
}
8774

8875
NodesVisited++;
8976
if (Aborted)
90-
return 0;
77+
return (0, Array.Empty<Move>());
9178

9279
Color color = position.SideToMove;
9380
//should we try null move pruning?
@@ -98,13 +85,14 @@ private int EvalPosition(Board position, int depth, SearchWindow window, bool is
9885
Board nullChild = Playmaker.PlayNullMove(position);
9986
//evaluate the position at reduced depth with a null-window around beta
10087
SearchWindow nullWindow = window.GetUpperBound(color);
101-
int nullScore = EvalPositionTT(nullChild, depth - R - 1, nullWindow, true);
88+
(int nullScore, _) = EvalPositionTT(nullChild, depth - R - 1, nullWindow, true);
10289
//is the evaluation "too good" despite null-move? then don't waste time on a branch that is likely going to fail-high
10390
if (nullWindow.Cut(nullScore, color))
104-
return nullScore;
91+
return (nullScore, Array.Empty<Move>());
10592
}
10693

10794
//do a regular expansion...
95+
Move[] pv = Array.Empty<Move>();
10896
int expandedNodes = 0;
10997
foreach ((Move move, Board child) in Playmaker.Play(position, depth, _killers))
11098
{
@@ -115,41 +103,37 @@ private int EvalPosition(Board position, int depth, SearchWindow window, bool is
115103
{
116104
//we can save a lot of nodes by searching with "null window" first, proving cheaply that the score is below alpha...
117105
SearchWindow nullWindow = window.GetLowerBound(color);
118-
int nullScore = EvalPositionTT(child, depth - 1, nullWindow);
119-
if (!nullWindow.Inside(nullScore, color))
106+
var nullResult = EvalPositionTT(child, depth - 1, nullWindow);
107+
if (!nullWindow.Inside(nullResult.Score, color))
120108
continue;
121109
}
122110

123111
//this node may raise alpha!
124-
int score = EvalPositionTT(child, depth - 1, window);
125-
if (window.Inside(score, color))
112+
var eval = EvalPositionTT(child, depth - 1, window);
113+
if (window.Inside(eval.Score, color))
126114
{
127-
Transpositions.Store(position.ZobristHash, depth, window, score, move);
128-
TriangularTable.Store(depth, move);
115+
Transpositions.Store(position.ZobristHash, depth, window, eval.Score, move);
116+
//store the PV beginning with move, followed by the PV of the childnode
117+
pv = new Move[eval.PV.Length + 1];
118+
pv[0] = move;
119+
Array.Copy(eval.PV, 0, pv, 1, eval.PV.Length);
129120
//...and maybe get a beta cutoff
130-
if (window.Cut(score, color))
121+
if (window.Cut(eval.Score, color))
131122
{
132123
//we remember killers like hat!
133124
if (position[move.ToSquare] == Piece.None)
134125
_killers.Add(move, depth);
135126

136-
return window.GetScore(color);
127+
return (window.GetScore(color), pv);
137128
}
138129
}
139130
}
140131

141-
//no playable moves in this position?
132+
//checkmate or draw?
142133
if (expandedNodes == 0)
143-
{
144-
//clear PV because the game is over
145-
TriangularTable.Truncate(depth);
146-
if (position.IsChecked(color))
147-
return Evaluation.Checkmate(color); //lost!
148-
else
149-
return 0; //draw!
150-
}
134+
return (position.IsChecked(color) ? Evaluation.Checkmate(color) : 0, Array.Empty<Move>());
151135

152-
return window.GetScore(color);
136+
return (window.GetScore(color), pv);
153137
}
154138

155139
private int QEval(Board position, SearchWindow window)

MinimalChess/KillSwitch.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public struct KillSwitch
1010
public KillSwitch(Func<bool> killSwitch = null)
1111
{
1212
_killSwitch = killSwitch;
13-
_aborted = _killSwitch == null ? false : _killSwitch();
13+
_aborted = _killSwitch != null && _killSwitch();
1414
}
1515

1616
public bool Get(bool update)

MinimalChess/KillerMoves.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
using System;
2-
using System.Collections.Generic;
32

43
namespace MinimalChess
54
{
65
class KillerMoves
76
{
87
Move[] _moves;
9-
int _depth = 0;
10-
int _width = 0;
8+
int _depth;
9+
int _width;
1110

1211
public KillerMoves(int width)
1312
{

MinimalChess/Move.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ namespace MinimalChess
44
{
55
public struct Move
66
{
7-
public byte FromSquare;
8-
public byte ToSquare;
9-
public Piece Promotion;
7+
public readonly byte FromSquare;
8+
public readonly byte ToSquare;
9+
public readonly Piece Promotion;
1010

1111
public Move(int fromIndex, int toIndex)
1212
{

MinimalChess/MoveCollection.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,6 @@ internal static MoveList Quiets(Board position)
4545
return quietMoves;
4646
}
4747

48-
internal static MoveList Captures(Board position)
49-
{
50-
MoveList captures = new MoveList();
51-
position.CollectCaptures(captures.Add);
52-
return captures;
53-
}
54-
5548
internal static MoveList SortedCaptures(Board position)
5649
{
5750
MoveList captures = new MoveList();

MinimalChess/Piece.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
namespace MinimalChess
1+
using System;
2+
3+
namespace MinimalChess
24
{
35
public enum Color
46
{
57
Black = -1,
68
White = +1
79
}
810

11+
[Flags]
912
public enum Piece : sbyte
1013
{
1114
//1st Bit = Piece or None?
@@ -49,7 +52,7 @@ public static class Pieces
4952
public const int MaxOrder = 6;
5053

5154
//Pawn = 1, Knight = 2, Bishop = 3; Rook = 4, Queen = 5, King = 6
52-
public static int Order(Piece piece) => ((int)piece >> 2);
55+
public static int Order(Piece piece) => (int)piece >> 2;
5356

5457
public static Piece Type(Piece piece) => piece & Piece.TypeMask;
5558

MinimalChess/Playmaker.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
1+
using System.Collections.Generic;
32

43
namespace MinimalChess
54
{

MinimalChess/SearchWindow.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public bool Cut(int score, Color color)
3838

3939
Ceiling = score;
4040
return Ceiling <= Floor; //Cutoff?
41-
};
41+
}
4242
}
4343

4444
public bool Inside(int score, Color color)

0 commit comments

Comments
 (0)