Skip to content

Commit a7fe9d7

Browse files
authored
Merge pull request #2 from Tearth/v2.0-dev
v2.0 (Darkness), 19.10.2020
2 parents d208cb6 + 5ef14c6 commit a7fe9d7

File tree

135 files changed

+3504
-1331
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+3504
-1331
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,3 +349,4 @@ MigrationBackup/
349349
.ionide/
350350
/cloc.exe
351351
/cloc.bat
352+
/Cosette.Arbiter/settings.json

CHANGELOG.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,28 @@
1-
# Version 1.0 (Aqua), 19-09-2020
2-
* Initial version
1+
# Version 2.0 (Darkness), 19.10.2020
2+
- Added fifty-move rule detection
3+
- Added new evaluation functions: pawn shield, bishop pair, doubled rooks, a rook on open file
4+
- Added "evaluate" command to get FEN position evaluation
5+
- Added ability to postpone moves generation before PV move check
6+
- Added evaluation hash table
7+
- Added Arbiter app to speed up the process of testing engine
8+
- Added support for UCI's winc and binc
9+
- Fixed PV node detection
10+
- Fixed invalid detection of passing pawns
11+
- Fixed invalid best move when a search has been aborted
12+
- Fixed static exchange evaluation - in rare cases the table was returning an invalid score
13+
- Improved method of probing piece type at the specified field
14+
- Improved time management - now allocated time depends on the moves count
15+
- Improved move ordering: castling and better promotions are now prioritized
16+
- Improved transposition tables: entries are now smaller, have proper checkmate scores (relative to position) and are used between moves (aging)
17+
- Redefined and reduced the size of Move structure (from 4 bytes to 2 bytes)
18+
- Reduced size of transposition table entry (from 16 bytes to 12 bytes), evaluation hash table entry (from 8 bytes to 4 bytes) and pawn hash table entry (from 8 bytes to 4 bytes)
19+
- Optimized printing UCI output
20+
- Adjusted move ordering scores
21+
- Updated .NET Core runtime version to 3.1.403
22+
23+
Estimated strength: 1950 ELO
24+
25+
# Version 1.0 (Aqua), 19.09.2020
26+
- Initial version
327

428
Estimated strength: 1900 ELO
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netcoreapp3.1</TargetFramework>
5+
6+
<IsPackable>false</IsPackable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.0" />
11+
<PackageReference Include="xunit" Version="2.4.0" />
12+
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
13+
<PackageReference Include="coverlet.collector" Version="1.2.0" />
14+
</ItemGroup>
15+
16+
<ItemGroup>
17+
<ProjectReference Include="..\Cosette.Arbiter\Cosette.Arbiter.csproj" />
18+
</ItemGroup>
19+
20+
</Project>

Cosette.Arbiter.Tests/HashKeyTests.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Cosette.Arbiter.Book;
4+
using Xunit;
5+
6+
namespace Cosette.Arbiter.Tests
7+
{
8+
public class HashKeyTests
9+
{
10+
[Theory]
11+
[InlineData(new string[] { }, 5060803636482931868)]
12+
[InlineData(new[] { "e2e4" }, 9384546495678726550)]
13+
[InlineData(new[] { "e2e4", "d7d5" }, 528813709611831216)]
14+
[InlineData(new[] { "e2e4", "d7d5", "e4e5", }, 7363297126586722772)]
15+
[InlineData(new[] { "e2e4", "d7d5", "e4e5", "f7f5" }, 2496273314520498040)]
16+
[InlineData(new[] { "e2e4", "d7d5", "e4e5", "f7f5", "e1e2" }, 7289745035295343297)]
17+
[InlineData(new[] { "e2e4", "d7d5", "e4e5", "f7f5", "e1e2", "e8f7" }, 71445182323015129)]
18+
[InlineData(new[] { "a2a4", "b7b5", "h2h4", "b5b4", "c2c4" }, 4359805404264691255)]
19+
[InlineData(new[] { "a2a4", "b7b5", "h2h4", "b5b4", "c2c4", "b4c3", "a1a3" }, 6647202560273257824)]
20+
public void HashKey_FromInitialPosition(string[] moves, ulong expectedHashKey)
21+
{
22+
var polyglotBoard = new PolyglotBoard();
23+
polyglotBoard.InitDefaultState();
24+
25+
foreach (var move in moves)
26+
{
27+
polyglotBoard.MakeMove(move);
28+
}
29+
30+
Assert.Equal(expectedHashKey, polyglotBoard.CalculateHash());
31+
}
32+
}
33+
}

Cosette.Arbiter/Book/CastlingFlags.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
using System;
2+
3+
namespace Cosette.Arbiter.Book
4+
{
5+
[Flags]
6+
public enum CastlingFlags
7+
{
8+
None = 0,
9+
WhiteShort = 1,
10+
WhiteLong = 2,
11+
BlackShort = 4,
12+
BlackLong = 8,
13+
WhiteCastling = WhiteShort | WhiteLong,
14+
BlackCastling = BlackShort | BlackLong,
15+
Everything = WhiteShort | WhiteLong | BlackShort | BlackLong
16+
}
17+
}

Cosette.Arbiter/Book/ColorType.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace Cosette.Arbiter.Book
2+
{
3+
public enum ColorType
4+
{
5+
White,
6+
Black
7+
}
8+
}

Cosette.Arbiter/Book/PieceType.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
namespace Cosette.Arbiter.Book
2+
{
3+
public enum PieceType
4+
{
5+
None = -1,
6+
BlackPawn,
7+
WhitePawn,
8+
BlackKnight,
9+
WhiteKnight,
10+
BlackBishop,
11+
WhiteBishop,
12+
BlackRook,
13+
WhiteRook,
14+
BlackQueen,
15+
WhiteQueen,
16+
BlackKing,
17+
WhiteKing
18+
}
19+
}

Cosette.Arbiter/Book/PolyglotBoard.cs

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
using System;
2+
3+
namespace Cosette.Arbiter.Book
4+
{
5+
public class PolyglotBoard
6+
{
7+
private PieceType[,] _state;
8+
private CastlingFlags _castlingFlags;
9+
private ColorType _colorToMove;
10+
private int _enPassantFile;
11+
12+
public PolyglotBoard()
13+
{
14+
_state = new PieceType[8,8];
15+
_castlingFlags = CastlingFlags.Everything;
16+
_colorToMove = ColorType.White;
17+
_enPassantFile = -1;
18+
}
19+
20+
public void InitDefaultState()
21+
{
22+
for (var file = 0; file < 8; file++)
23+
{
24+
for (var rank = 0; rank < 8; rank++)
25+
{
26+
_state[file, rank] = PieceType.None;
27+
}
28+
}
29+
30+
_state[0, 0] = PieceType.WhiteRook;
31+
_state[1, 0] = PieceType.WhiteKnight;
32+
_state[2, 0] = PieceType.WhiteBishop;
33+
_state[3, 0] = PieceType.WhiteQueen;
34+
_state[4, 0] = PieceType.WhiteKing;
35+
_state[5, 0] = PieceType.WhiteBishop;
36+
_state[6, 0] = PieceType.WhiteKnight;
37+
_state[7, 0] = PieceType.WhiteRook;
38+
39+
_state[0, 1] = PieceType.WhitePawn;
40+
_state[1, 1] = PieceType.WhitePawn;
41+
_state[2, 1] = PieceType.WhitePawn;
42+
_state[3, 1] = PieceType.WhitePawn;
43+
_state[4, 1] = PieceType.WhitePawn;
44+
_state[5, 1] = PieceType.WhitePawn;
45+
_state[6, 1] = PieceType.WhitePawn;
46+
_state[7, 1] = PieceType.WhitePawn;
47+
48+
_state[0, 6] = PieceType.BlackPawn;
49+
_state[1, 6] = PieceType.BlackPawn;
50+
_state[2, 6] = PieceType.BlackPawn;
51+
_state[3, 6] = PieceType.BlackPawn;
52+
_state[4, 6] = PieceType.BlackPawn;
53+
_state[5, 6] = PieceType.BlackPawn;
54+
_state[6, 6] = PieceType.BlackPawn;
55+
_state[7, 6] = PieceType.BlackPawn;
56+
57+
_state[0, 7] = PieceType.BlackRook;
58+
_state[1, 7] = PieceType.BlackKnight;
59+
_state[2, 7] = PieceType.BlackBishop;
60+
_state[3, 7] = PieceType.BlackQueen;
61+
_state[4, 7] = PieceType.BlackKing;
62+
_state[5, 7] = PieceType.BlackBishop;
63+
_state[6, 7] = PieceType.BlackKnight;
64+
_state[7, 7] = PieceType.BlackRook;
65+
}
66+
67+
public void MakeMove(string move)
68+
{
69+
var (fromFile, fromRank) = (move[0] - 'a', move[1] - '1');
70+
var (toFile, toRank) = (move[2] - 'a', move[3] - '1');
71+
var pieceType = _state[fromFile, fromRank];
72+
var oldEnPassant = _enPassantFile;
73+
74+
// Pieces
75+
_state[toFile, toRank] = _state[fromFile, fromRank];
76+
_state[fromFile, fromRank] = PieceType.None;
77+
_enPassantFile = -1;
78+
79+
// Castling
80+
if (pieceType == PieceType.WhiteKing)
81+
{
82+
_castlingFlags &= ~CastlingFlags.WhiteCastling;
83+
84+
if (Math.Abs(fromFile - toFile) == 2)
85+
{
86+
// Short castling
87+
if (fromFile < toFile)
88+
{
89+
_state[7, 0] = PieceType.None;
90+
_state[5, 0] = PieceType.WhiteRook;
91+
}
92+
// Long castling
93+
else
94+
{
95+
_state[0, 0] = PieceType.None;
96+
_state[3, 0] = PieceType.WhiteRook;
97+
}
98+
}
99+
}
100+
else if (pieceType == PieceType.BlackKing)
101+
{
102+
_castlingFlags &= ~CastlingFlags.BlackCastling;
103+
104+
if (Math.Abs(fromFile - toFile) == 2)
105+
{
106+
// Short castling
107+
if (fromFile < toFile)
108+
{
109+
_state[7, 7] = PieceType.None;
110+
_state[5, 7] = PieceType.BlackRook;
111+
}
112+
// Long castling
113+
else
114+
{
115+
_state[0, 7] = PieceType.None;
116+
_state[3, 7] = PieceType.BlackRook;
117+
}
118+
}
119+
}
120+
else if (pieceType == PieceType.WhiteRook && fromFile == 0 && fromRank == 0)
121+
{
122+
_castlingFlags &= ~CastlingFlags.WhiteLong;
123+
}
124+
else if (pieceType == PieceType.WhiteRook && fromFile == 7 && fromRank == 0)
125+
{
126+
_castlingFlags &= ~CastlingFlags.WhiteShort;
127+
}
128+
else if (pieceType == PieceType.BlackRook && fromFile == 0 && fromRank == 7)
129+
{
130+
_castlingFlags &= ~CastlingFlags.BlackLong;
131+
}
132+
else if (pieceType == PieceType.BlackRook && fromFile == 7 && fromRank == 7)
133+
{
134+
_castlingFlags &= ~CastlingFlags.BlackShort;
135+
}
136+
137+
// En passant
138+
if (pieceType == PieceType.WhitePawn || pieceType == PieceType.BlackPawn)
139+
{
140+
if (Math.Abs(fromRank - toRank) == 2)
141+
{
142+
var targetPieceType = _colorToMove == ColorType.White ? PieceType.BlackPawn : PieceType.WhitePawn;
143+
if (toFile > 0 && _state[toFile - 1, toRank] == targetPieceType ||
144+
toFile < 7 && _state[toFile + 1, toRank] == targetPieceType)
145+
{
146+
_enPassantFile = toFile;
147+
}
148+
}
149+
else if (Math.Abs(fromFile - toFile) == 1 && toFile == oldEnPassant)
150+
{
151+
if (_colorToMove == ColorType.White)
152+
{
153+
_state[toFile, toRank - 1] = PieceType.None;
154+
}
155+
else if (_colorToMove == ColorType.Black)
156+
{
157+
_state[toFile, toRank + 1] = PieceType.None;
158+
}
159+
}
160+
}
161+
162+
// Color
163+
_colorToMove = _colorToMove == ColorType.White ? ColorType.Black : ColorType.White;
164+
}
165+
166+
public ulong CalculateHash()
167+
{
168+
ulong result = 0;
169+
170+
for (var file = 0; file < 8; file++)
171+
{
172+
for (var rank = 0; rank < 8; rank++)
173+
{
174+
if (_state[file, rank] != PieceType.None)
175+
{
176+
result ^= PolyglotConstants.Keys[64 * (int)_state[file, rank] + 8 * rank + file];
177+
}
178+
}
179+
}
180+
181+
if ((_castlingFlags & CastlingFlags.WhiteShort) != 0)
182+
{
183+
result ^= PolyglotConstants.Keys[768];
184+
}
185+
if ((_castlingFlags & CastlingFlags.WhiteLong) != 0)
186+
{
187+
result ^= PolyglotConstants.Keys[769];
188+
}
189+
if ((_castlingFlags & CastlingFlags.BlackShort) != 0)
190+
{
191+
result ^= PolyglotConstants.Keys[770];
192+
}
193+
if ((_castlingFlags & CastlingFlags.BlackLong) != 0)
194+
{
195+
result ^= PolyglotConstants.Keys[771];
196+
}
197+
198+
if (_enPassantFile != -1)
199+
{
200+
result ^= PolyglotConstants.Keys[772 + _enPassantFile];
201+
}
202+
203+
if (_colorToMove == ColorType.White)
204+
{
205+
result ^= PolyglotConstants.Keys[780];
206+
}
207+
208+
return result;
209+
}
210+
}
211+
}

0 commit comments

Comments
 (0)