├── .gitignore
├── BuildInstructions.txt
├── ChessCore.sln
├── ChessCore
├── ChessCore.csproj
└── Program.cs
├── ChessCoreEngine
├── Board.cs
├── Book.cs
├── ChessCoreEngine.csproj
├── Engine.cs
├── Evaluation.cs
├── FileIO.cs
├── MoveContent.cs
├── PGN.cs
├── Piece.cs
├── PieceMoves.cs
├── PieceSquareTable.cs
├── PieceValidMoves.cs
├── PiecesTaken.cs
├── Puzzle.cs
├── ResultBoards.cs
├── Search.cs
├── Square.cs
└── Test.cs
├── LICENSE
├── README.md
└── Releases
└── ChessCore1.0.1.zip
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Build results
17 | [Dd]ebug/
18 | [Dd]ebugPublic/
19 | [Rr]elease/
20 | x64/
21 | x86/
22 | [Aa][Rr][Mm]/
23 | [Aa][Rr][Mm]64/
24 | bld/
25 | [Bb]in/
26 | [Oo]bj/
27 | [Ll]og/
28 |
29 | # Visual Studio 2015/2017 cache/options directory
30 | .vs/
31 | # Uncomment if you have tasks that create the project's static files in wwwroot
32 | #wwwroot/
33 |
34 | # Visual Studio 2017 auto generated files
35 | Generated\ Files/
36 |
37 | # MSTest test Results
38 | [Tt]est[Rr]esult*/
39 | [Bb]uild[Ll]og.*
40 |
41 | # NUNIT
42 | *.VisualState.xml
43 | TestResult.xml
44 |
45 | # Build Results of an ATL Project
46 | [Dd]ebugPS/
47 | [Rr]eleasePS/
48 | dlldata.c
49 |
50 | # Benchmark Results
51 | BenchmarkDotNet.Artifacts/
52 |
53 | # .NET Core
54 | project.lock.json
55 | project.fragment.lock.json
56 | artifacts/
57 |
58 | # StyleCop
59 | StyleCopReport.xml
60 |
61 | # Files built by Visual Studio
62 | *_i.c
63 | *_p.c
64 | *_h.h
65 | *.ilk
66 | *.meta
67 | *.obj
68 | *.iobj
69 | *.pch
70 | *.pdb
71 | *.ipdb
72 | *.pgc
73 | *.pgd
74 | *.rsp
75 | *.sbr
76 | *.tlb
77 | *.tli
78 | *.tlh
79 | *.tmp
80 | *.tmp_proj
81 | *_wpftmp.csproj
82 | *.log
83 | *.vspscc
84 | *.vssscc
85 | .builds
86 | *.pidb
87 | *.svclog
88 | *.scc
89 |
90 | # Chutzpah Test files
91 | _Chutzpah*
92 |
93 | # Visual C++ cache files
94 | ipch/
95 | *.aps
96 | *.ncb
97 | *.opendb
98 | *.opensdf
99 | *.sdf
100 | *.cachefile
101 | *.VC.db
102 | *.VC.VC.opendb
103 |
104 | # Visual Studio profiler
105 | *.psess
106 | *.vsp
107 | *.vspx
108 | *.sap
109 |
110 | # Visual Studio Trace Files
111 | *.e2e
112 |
113 | # TFS 2012 Local Workspace
114 | $tf/
115 |
116 | # Guidance Automation Toolkit
117 | *.gpState
118 |
119 | # ReSharper is a .NET coding add-in
120 | _ReSharper*/
121 | *.[Rr]e[Ss]harper
122 | *.DotSettings.user
123 |
124 | # JustCode is a .NET coding add-in
125 | .JustCode
126 |
127 | # TeamCity is a build add-in
128 | _TeamCity*
129 |
130 | # DotCover is a Code Coverage Tool
131 | *.dotCover
132 |
133 | # AxoCover is a Code Coverage Tool
134 | .axoCover/*
135 | !.axoCover/settings.json
136 |
137 | # Visual Studio code coverage results
138 | *.coverage
139 | *.coveragexml
140 |
141 | # NCrunch
142 | _NCrunch_*
143 | .*crunch*.local.xml
144 | nCrunchTemp_*
145 |
146 | # MightyMoose
147 | *.mm.*
148 | AutoTest.Net/
149 |
150 | # Web workbench (sass)
151 | .sass-cache/
152 |
153 | # Installshield output folder
154 | [Ee]xpress/
155 |
156 | # DocProject is a documentation generator add-in
157 | DocProject/buildhelp/
158 | DocProject/Help/*.HxT
159 | DocProject/Help/*.HxC
160 | DocProject/Help/*.hhc
161 | DocProject/Help/*.hhk
162 | DocProject/Help/*.hhp
163 | DocProject/Help/Html2
164 | DocProject/Help/html
165 |
166 | # Click-Once directory
167 | publish/
168 |
169 | # Publish Web Output
170 | *.[Pp]ublish.xml
171 | *.azurePubxml
172 | # Note: Comment the next line if you want to checkin your web deploy settings,
173 | # but database connection strings (with potential passwords) will be unencrypted
174 | *.pubxml
175 | *.publishproj
176 |
177 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
178 | # checkin your Azure Web App publish settings, but sensitive information contained
179 | # in these scripts will be unencrypted
180 | PublishScripts/
181 |
182 | # NuGet Packages
183 | *.nupkg
184 | # The packages folder can be ignored because of Package Restore
185 | **/[Pp]ackages/*
186 | # except build/, which is used as an MSBuild target.
187 | !**/[Pp]ackages/build/
188 | # Uncomment if necessary however generally it will be regenerated when needed
189 | #!**/[Pp]ackages/repositories.config
190 | # NuGet v3's project.json files produces more ignorable files
191 | *.nuget.props
192 | *.nuget.targets
193 |
194 | # Microsoft Azure Build Output
195 | csx/
196 | *.build.csdef
197 |
198 | # Microsoft Azure Emulator
199 | ecf/
200 | rcf/
201 |
202 | # Windows Store app package directories and files
203 | AppPackages/
204 | BundleArtifacts/
205 | Package.StoreAssociation.xml
206 | _pkginfo.txt
207 | *.appx
208 |
209 | # Visual Studio cache files
210 | # files ending in .cache can be ignored
211 | *.[Cc]ache
212 | # but keep track of directories ending in .cache
213 | !*.[Cc]ache/
214 |
215 | # Others
216 | ClientBin/
217 | ~$*
218 | *~
219 | *.dbmdl
220 | *.dbproj.schemaview
221 | *.jfm
222 | *.pfx
223 | *.publishsettings
224 | orleans.codegen.cs
225 |
226 | # Including strong name files can present a security risk
227 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
228 | #*.snk
229 |
230 | # Since there are multiple workflows, uncomment next line to ignore bower_components
231 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
232 | #bower_components/
233 | # ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
234 | **/wwwroot/lib/
235 |
236 | # RIA/Silverlight projects
237 | Generated_Code/
238 |
239 | # Backup & report files from converting an old project file
240 | # to a newer Visual Studio version. Backup files are not needed,
241 | # because we have git ;-)
242 | _UpgradeReport_Files/
243 | Backup*/
244 | UpgradeLog*.XML
245 | UpgradeLog*.htm
246 | ServiceFabricBackup/
247 | *.rptproj.bak
248 |
249 | # SQL Server files
250 | *.mdf
251 | *.ldf
252 | *.ndf
253 |
254 | # Business Intelligence projects
255 | *.rdl.data
256 | *.bim.layout
257 | *.bim_*.settings
258 | *.rptproj.rsuser
259 |
260 | # Microsoft Fakes
261 | FakesAssemblies/
262 |
263 | # GhostDoc plugin setting file
264 | *.GhostDoc.xml
265 |
266 | # Node.js Tools for Visual Studio
267 | .ntvs_analysis.dat
268 | node_modules/
269 |
270 | # Visual Studio 6 build log
271 | *.plg
272 |
273 | # Visual Studio 6 workspace options file
274 | *.opt
275 |
276 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
277 | *.vbw
278 |
279 | # Visual Studio LightSwitch build output
280 | **/*.HTMLClient/GeneratedArtifacts
281 | **/*.DesktopClient/GeneratedArtifacts
282 | **/*.DesktopClient/ModelManifest.xml
283 | **/*.Server/GeneratedArtifacts
284 | **/*.Server/ModelManifest.xml
285 | _Pvt_Extensions
286 |
287 | # Paket dependency manager
288 | .paket/paket.exe
289 | paket-files/
290 |
291 | # FAKE - F# Make
292 | .fake/
293 |
294 | # JetBrains Rider
295 | .idea/
296 | *.sln.iml
297 |
298 | # CodeRush personal settings
299 | .cr/personal
300 |
301 | # Python Tools for Visual Studio (PTVS)
302 | __pycache__/
303 | *.pyc
304 |
305 | # Cake - Uncomment if you are using it
306 | # tools/**
307 | # !tools/packages.config
308 |
309 | # Tabs Studio
310 | *.tss
311 |
312 | # Telerik's JustMock configuration file
313 | *.jmconfig
314 |
315 | # BizTalk build output
316 | *.btp.cs
317 | *.btm.cs
318 | *.odx.cs
319 | *.xsd.cs
320 |
321 | # OpenCover UI analysis results
322 | OpenCover/
323 |
324 | # Azure Stream Analytics local run output
325 | ASALocalRun/
326 |
327 | # MSBuild Binary and Structured Log
328 | *.binlog
329 |
330 | # NVidia Nsight GPU debugger configuration file
331 | *.nvuser
332 |
333 | # MFractors (Xamarin productivity tool) working folder
334 | .mfractor/
335 |
336 | # Local History for Visual Studio
337 | .localhistory/
--------------------------------------------------------------------------------
/BuildInstructions.txt:
--------------------------------------------------------------------------------
1 |
2 | To Publish For Native OSX or Win10
3 |
4 | dotnet publish -c Release -r osx.10.11-x64
5 | dotnet publish -c Release -r win10-x64
6 |
--------------------------------------------------------------------------------
/ChessCore.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.28010.2048
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChessCore", "ChessCore\ChessCore.csproj", "{1D6C387C-1462-4E15-B36B-133C32B019AA}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChessCoreEngine", "ChessCoreEngine\ChessCoreEngine.csproj", "{817E0C3B-6373-413E-8F86-482129B8CE47}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {1D6C387C-1462-4E15-B36B-133C32B019AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {1D6C387C-1462-4E15-B36B-133C32B019AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {1D6C387C-1462-4E15-B36B-133C32B019AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {1D6C387C-1462-4E15-B36B-133C32B019AA}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {817E0C3B-6373-413E-8F86-482129B8CE47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {817E0C3B-6373-413E-8F86-482129B8CE47}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {817E0C3B-6373-413E-8F86-482129B8CE47}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {817E0C3B-6373-413E-8F86-482129B8CE47}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {AD967701-4905-4EF5-BE85-DDD51D3C48EF}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/ChessCore/ChessCore.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp2.1
6 | 1.0.1
7 | 3583 Bytes
8 | Adam Berent
9 |
10 |
11 |
12 |
13 | {1861FDA3-D8F8-4479-B914-C78B777B93D5}
14 | ChessCoreEngine
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/ChessCore/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ChessEngine.Engine;
3 |
4 | class Program
5 | {
6 |
7 | static void Main(string[] args)
8 | {
9 | RunEngine();
10 | }
11 |
12 | private static void RunEngine()
13 | {
14 | bool ShowBoard = false;
15 |
16 | var engine = new Engine();
17 |
18 |
19 |
20 | Console.WriteLine("Chess Core");
21 | Console.WriteLine("Created by Adam Berent");
22 | Console.WriteLine("Version: 1.0.1");
23 | Console.WriteLine("");
24 | Console.WriteLine("Type quit to exit");
25 | Console.WriteLine("Type show to show board");
26 | Console.WriteLine("");
27 | Console.WriteLine("feature setboard=1");
28 |
29 |
30 | while (true)
31 | {
32 | try
33 | {
34 | if (ShowBoard)
35 | {
36 | DrawBoard(engine);
37 | }
38 |
39 | if (engine.WhoseMove != engine.HumanPlayer)
40 | {
41 | MakeEngineMove(engine);
42 | }
43 | else
44 | {
45 | Console.WriteLine();
46 |
47 | string move = Console.ReadLine();
48 |
49 | if (String.IsNullOrEmpty(move))
50 | {
51 | continue;
52 | }
53 |
54 | move = move.Trim();
55 |
56 |
57 | if (move == "new")
58 | {
59 | engine.NewGame();
60 | continue;
61 | }
62 | if (move == "quit")
63 | {
64 | return;
65 | }
66 | if (move == "xboard")
67 | {
68 | continue;
69 | }
70 | if (move == "show")
71 | {
72 | ShowBoard = !ShowBoard;
73 |
74 | continue;
75 | }
76 | if (move.StartsWith("edit"))
77 | {
78 | continue;
79 | }
80 | if (move == "hint")
81 | {
82 | continue;
83 | }
84 | if (move == "bk")
85 | {
86 | continue;
87 | }
88 | if (move == "undo")
89 | {
90 | engine.Undo();
91 | continue;
92 | }
93 | if (move == "remove")
94 | {
95 | continue;
96 | }
97 | if (move == "remove")
98 | {
99 | continue;
100 | }
101 | if (move == "hard")
102 | {
103 | engine.GameDifficulty = Engine.Difficulty.Hard;
104 | continue;
105 | }
106 | if (move == "easy")
107 | {
108 | continue;
109 | }
110 | if (move.StartsWith("accepted"))
111 | {
112 | continue;
113 | }
114 | if (move.StartsWith("rejected"))
115 | {
116 | continue;
117 | }
118 | if (move.StartsWith("variant"))
119 | {
120 | continue;
121 | }
122 | if (move == "random")
123 | {
124 | continue;
125 | }
126 | if (move == "force")
127 | {
128 | continue;
129 | }
130 | if (move == "go")
131 | {
132 | continue;
133 | }
134 | if (move == "playother")
135 | {
136 | if (engine.WhoseMove == ChessPieceColor.White)
137 | {
138 | engine.HumanPlayer = ChessPieceColor.Black;
139 | }
140 | else if (engine.WhoseMove == ChessPieceColor.Black)
141 | {
142 | engine.HumanPlayer = ChessPieceColor.White;
143 | }
144 |
145 | continue;
146 | }
147 | if (move == "white")
148 | {
149 | engine.HumanPlayer = ChessPieceColor.Black;
150 |
151 | if (engine.WhoseMove != engine.HumanPlayer)
152 | {
153 | MakeEngineMove(engine);
154 | }
155 | continue;
156 | }
157 | if (move == "black")
158 | {
159 | engine.HumanPlayer = ChessPieceColor.White;
160 |
161 | if (engine.WhoseMove != engine.HumanPlayer)
162 | {
163 | MakeEngineMove(engine);
164 | }
165 | continue;
166 | }
167 |
168 | if (move.StartsWith("level"))
169 | {
170 | continue;
171 | }
172 | if (move.StartsWith("st"))
173 | {
174 | continue;
175 | }
176 | if (move.StartsWith("sd"))
177 | {
178 | continue;
179 | }
180 | if (move.StartsWith("time"))
181 | {
182 | continue;
183 | }
184 | if (move.StartsWith("otim"))
185 | {
186 | continue;
187 | }
188 | if (move.StartsWith("otim"))
189 | {
190 | continue;
191 | }
192 | if (move == "?")
193 | {
194 | continue;
195 | }
196 | if (move.StartsWith("ping"))
197 | {
198 | if (move.IndexOf(" ") > 0)
199 | {
200 | string pong = move.Substring(move.IndexOf(" "), move.Length - move.IndexOf(" "));
201 |
202 | Console.WriteLine("pong " + pong);
203 | }
204 | continue;
205 | }
206 |
207 | if (move.StartsWith("result"))
208 | {
209 | continue;
210 | }
211 |
212 | if (move.StartsWith("setboard"))
213 | {
214 | if (move.IndexOf(" ") > 0)
215 | {
216 | string fen = move.Substring(move.IndexOf(" "), move.Length - move.IndexOf(" ")).Trim();
217 |
218 | engine.InitiateBoard(fen);
219 | }
220 |
221 | continue;
222 | }
223 |
224 | if (move.StartsWith("setboard"))
225 | {
226 | continue;
227 | }
228 | if (move.StartsWith("edit"))
229 | {
230 | engine.NewGame();
231 | continue;
232 | }
233 | if (move.StartsWith("1/2-1/2"))
234 | {
235 | engine.NewGame();
236 | continue;
237 | }
238 | if (move.StartsWith("0-1"))
239 | {
240 | engine.NewGame();
241 | continue;
242 | }
243 | if (move.StartsWith("1-0"))
244 | {
245 | engine.NewGame();
246 | continue;
247 | }
248 |
249 | if (move.Length < 4)
250 | {
251 | continue;
252 | }
253 |
254 | if (move.Length > 5)
255 | {
256 | continue;
257 | }
258 |
259 | string src = move.Substring(0, 2);
260 | string dst = move.Substring(2, 2);
261 |
262 |
263 | byte srcCol;
264 | byte srcRow;
265 | byte dstRow;
266 | byte dstCol;
267 |
268 | try
269 | {
270 | srcCol = GetColumn(src);
271 | srcRow = GetRow(src);
272 | dstRow = GetRow(dst);
273 | dstCol = GetColumn(dst);
274 | }
275 | catch (Exception ex)
276 | {
277 | Console.WriteLine(ex.Message);
278 | continue;
279 | }
280 |
281 | if (!engine.IsValidMove(srcCol, srcRow, dstCol, dstRow))
282 | {
283 | Console.WriteLine("Invalid Move");
284 | continue;
285 | }
286 |
287 | engine.MovePiece(srcCol, srcRow, dstCol, dstRow);
288 |
289 | MakeEngineMove(engine);
290 |
291 |
292 | if (engine.StaleMate)
293 | {
294 | if (engine.InsufficientMaterial)
295 | {
296 | Console.WriteLine("1/2-1/2 {Draw by insufficient material}");
297 | }
298 | else if (engine.RepeatedMove)
299 | {
300 | Console.WriteLine("1/2-1/2 {Draw by repetition}");
301 | }
302 | else if (engine.FiftyMove)
303 | {
304 | Console.WriteLine("1/2-1/2 {Draw by fifty move rule}");
305 | }
306 | else
307 | {
308 | Console.WriteLine("1/2-1/2 {Stalemate}");
309 | }
310 | engine.NewGame();
311 | }
312 | else if (engine.GetWhiteMate())
313 | {
314 | Console.WriteLine("0-1 {Black mates}");
315 | engine.NewGame();
316 | }
317 | else if (engine.GetBlackMate())
318 | {
319 | Console.WriteLine("1-0 {White mates}");
320 | engine.NewGame();
321 | }
322 | }
323 | }
324 | catch (Exception ex)
325 | {
326 | Console.WriteLine(ex.Message);
327 | return;
328 | }
329 | }
330 | }
331 |
332 | private static void MakeEngineMove(Engine engine)
333 | {
334 | DateTime start = DateTime.Now;
335 |
336 | engine.AiPonderMove();
337 |
338 | MoveContent lastMove = engine.GetMoveHistory().ToArray()[0];
339 |
340 | string tmp = "";
341 |
342 | var sourceColumn = (byte)(lastMove.MovingPiecePrimary.SrcPosition % 8);
343 | var srcRow = (byte)(8 - (lastMove.MovingPiecePrimary.SrcPosition / 8));
344 | var destinationColumn = (byte)(lastMove.MovingPiecePrimary.DstPosition % 8);
345 | var destinationRow = (byte)(8 - (lastMove.MovingPiecePrimary.DstPosition / 8));
346 |
347 | tmp += GetPgnMove(lastMove.MovingPiecePrimary.PieceType);
348 |
349 | if (lastMove.MovingPiecePrimary.PieceType == ChessPieceType.Knight)
350 | {
351 | tmp += GetColumnFromInt(sourceColumn + 1);
352 | tmp += srcRow;
353 | }
354 | else if (lastMove.MovingPiecePrimary.PieceType == ChessPieceType.Rook)
355 | {
356 | tmp += GetColumnFromInt(sourceColumn + 1);
357 | tmp += srcRow;
358 | }
359 | else if (lastMove.MovingPiecePrimary.PieceType == ChessPieceType.Pawn)
360 | {
361 | if (sourceColumn != destinationColumn)
362 | {
363 | tmp += GetColumnFromInt(sourceColumn + 1);
364 | }
365 | }
366 |
367 | if (lastMove.TakenPiece.PieceType != ChessPieceType.None)
368 | {
369 | tmp += "x";
370 | }
371 |
372 | tmp += GetColumnFromInt(destinationColumn + 1);
373 |
374 | tmp += destinationRow;
375 |
376 | if (lastMove.PawnPromotedTo == ChessPieceType.Queen)
377 | {
378 | tmp += "=Q";
379 | }
380 | else if (lastMove.PawnPromotedTo == ChessPieceType.Rook)
381 | {
382 | tmp += "=R";
383 | }
384 | else if (lastMove.PawnPromotedTo == ChessPieceType.Knight)
385 | {
386 | tmp += "=K";
387 | }
388 | else if (lastMove.PawnPromotedTo == ChessPieceType.Bishop)
389 | {
390 | tmp += "=B";
391 | }
392 |
393 | DateTime end = DateTime.Now;
394 |
395 | TimeSpan ts = end - start;
396 |
397 | Console.Write(engine.PlyDepthReached + " ");
398 |
399 | int score = engine.GetScore();
400 |
401 | if (score > 0)
402 | {
403 | score = score / 10;
404 | }
405 |
406 | Console.Write(score + " ");
407 | Console.Write(ts.Seconds * 100 + " ");
408 | Console.Write(engine.NodesSearched + engine.NodesQuiessence + " ");
409 | Console.Write(engine.PvLine);
410 | Console.WriteLine();
411 |
412 | Console.Write("move ");
413 | Console.WriteLine(tmp);
414 | }
415 |
416 | public static string GetColumnFromInt(int column)
417 | {
418 | string returnColumnt;
419 |
420 | switch (column)
421 | {
422 | case 1:
423 | returnColumnt = "a";
424 | break;
425 | case 2:
426 | returnColumnt = "b";
427 | break;
428 | case 3:
429 | returnColumnt = "c";
430 | break;
431 | case 4:
432 | returnColumnt = "d";
433 | break;
434 | case 5:
435 | returnColumnt = "e";
436 | break;
437 | case 6:
438 | returnColumnt = "f";
439 | break;
440 | case 7:
441 | returnColumnt = "g";
442 | break;
443 | case 8:
444 | returnColumnt = "h";
445 | break;
446 | default:
447 | returnColumnt = "Unknown";
448 | break;
449 | }
450 |
451 | return returnColumnt;
452 | }
453 |
454 | private static string GetPgnMove(ChessPieceType pieceType)
455 | {
456 | string move = "";
457 |
458 | if (pieceType == ChessPieceType.Bishop)
459 | {
460 | move += "B";
461 | }
462 | else if (pieceType == ChessPieceType.King)
463 | {
464 | move += "K";
465 | }
466 | else if (pieceType == ChessPieceType.Knight)
467 | {
468 | move += "N";
469 | }
470 | else if (pieceType == ChessPieceType.Queen)
471 | {
472 | move += "Q";
473 | }
474 | else if (pieceType == ChessPieceType.Rook)
475 | {
476 | move += "R";
477 | }
478 |
479 | return move;
480 | }
481 |
482 | private static byte GetRow(string move)
483 | {
484 | if (move != null)
485 | {
486 | if (move.Length == 2)
487 | {
488 | return (byte)(8 - int.Parse(move.Substring(1, 1).ToLower()));
489 | }
490 | }
491 |
492 | return 255;
493 | }
494 |
495 | private static byte GetColumn(string move)
496 | {
497 | if (move != null)
498 | {
499 | if (move.Length == 2)
500 | {
501 | string col = move.Substring(0, 1).ToLower();
502 |
503 | switch (col)
504 | {
505 | case "a":
506 | {
507 | return 0;
508 | }
509 | case "b":
510 | {
511 | return 1;
512 | }
513 | case "c":
514 | {
515 | return 2;
516 | }
517 | case "d":
518 | {
519 | return 3;
520 | }
521 | case "e":
522 | {
523 | return 4;
524 | }
525 | case "f":
526 | {
527 | return 5;
528 | }
529 | case "g":
530 | {
531 | return 6;
532 | }
533 | case "h":
534 | {
535 | return 7;
536 | }
537 | default:
538 | return 255;
539 | }
540 | }
541 | }
542 |
543 | return 255;
544 | }
545 |
546 | private static void DrawBoard(Engine engine)
547 | {
548 | //Console.Clear();
549 |
550 | for (byte i = 0; i < 64; i++)
551 | {
552 | if (i % 8 == 0)
553 | {
554 | Console.WriteLine();
555 | Console.WriteLine(" ---------------------------------");
556 | Console.Write((8 - (i / 8)));
557 | }
558 |
559 | ChessPieceType PieceType = engine.GetPieceTypeAt(i);
560 | ChessPieceColor PieceColor = engine.GetPieceColorAt(i);
561 | string str;
562 |
563 | switch (PieceType)
564 | {
565 | case ChessPieceType.Pawn:
566 | {
567 | str = "| " + "P ";
568 | break;
569 | }
570 | case ChessPieceType.Knight:
571 | {
572 | str = "| " + "N ";
573 | break;
574 | }
575 | case ChessPieceType.Bishop:
576 | {
577 | str = "| " + "B ";
578 | break;
579 | }
580 | case ChessPieceType.Rook:
581 | {
582 | str = "| " + "R ";
583 | break;
584 | }
585 |
586 | case ChessPieceType.Queen:
587 | {
588 | str = "| " + "Q ";
589 | break;
590 | }
591 |
592 | case ChessPieceType.King:
593 | {
594 | str = "| " + "K ";
595 | break;
596 | }
597 | default:
598 | {
599 | str = "| " + " ";
600 | break;
601 | }
602 | }
603 |
604 | if (PieceColor == ChessPieceColor.Black)
605 | {
606 | str = str.ToLower();
607 | }
608 |
609 | Console.Write(str);
610 |
611 | if (i % 8 == 7)
612 | {
613 | Console.Write("|");
614 | }
615 | }
616 |
617 | Console.WriteLine();
618 | Console.WriteLine(" ---------------------------------");
619 | Console.WriteLine(" A B C D E F G H");
620 |
621 | }
622 | }
623 |
--------------------------------------------------------------------------------
/ChessCoreEngine/ChessCoreEngine.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.1
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/ChessCoreEngine/Evaluation.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace ChessEngine.Engine
3 | {
4 | internal static class Evaluation
5 | {
6 | private static short[] blackPawnCount;
7 | private static short[] whitePawnCount;
8 |
9 | private static readonly short[] PawnTable = new short[]
10 | {
11 | 0, 0, 0, 0, 0, 0, 0, 0,
12 | 50, 50, 50, 50, 50, 50, 50, 50,
13 | 20, 20, 30, 40, 40, 30, 20, 20,
14 | 5, 5, 10, 30, 30, 10, 5, 5,
15 | 0, 0, 0, 25, 25, 0, 0, 0,
16 | 5, -5,-10, 0, 0,-10, -5, 5,
17 | 5, 10, 10,-30,-30, 10, 10, 5,
18 | 0, 0, 0, 0, 0, 0, 0, 0
19 | };
20 |
21 | private static readonly short[] KnightTable = new short[]
22 | {
23 | -50,-40,-30,-30,-30,-30,-40,-50,
24 | -40,-20, 0, 0, 0, 0,-20,-40,
25 | -30, 0, 10, 15, 15, 10, 0,-30,
26 | -30, 5, 15, 20, 20, 15, 5,-30,
27 | -30, 0, 15, 20, 20, 15, 0,-30,
28 | -30, 5, 10, 15, 15, 10, 5,-30,
29 | -40,-20, 0, 5, 5, 0,-20,-40,
30 | -50,-30,-20,-30,-30,-20,-30,-50,
31 | };
32 |
33 | private static readonly short[] BishopTable = new short[]
34 | {
35 | -20,-10,-10,-10,-10,-10,-10,-20,
36 | -10, 0, 0, 0, 0, 0, 0,-10,
37 | -10, 0, 5, 10, 10, 5, 0,-10,
38 | -10, 5, 5, 10, 10, 5, 5,-10,
39 | -10, 0, 10, 10, 10, 10, 0,-10,
40 | -10, 10, 10, 10, 10, 10, 10,-10,
41 | -10, 5, 0, 0, 0, 0, 5,-10,
42 | -20,-10,-40,-10,-10,-40,-10,-20,
43 | };
44 |
45 | private static readonly short[] KingTable = new short[]
46 | {
47 | -30, -40, -40, -50, -50, -40, -40, -30,
48 | -30, -40, -40, -50, -50, -40, -40, -30,
49 | -30, -40, -40, -50, -50, -40, -40, -30,
50 | -30, -40, -40, -50, -50, -40, -40, -30,
51 | -20, -30, -30, -40, -40, -30, -30, -20,
52 | -10, -20, -20, -20, -20, -20, -20, -10,
53 | 20, 20, 0, 0, 0, 0, 20, 20,
54 | 20, 30, 10, 0, 0, 10, 30, 20
55 | };
56 |
57 | private static readonly short[] KingTableEndGame = new short[]
58 | {
59 | -50,-40,-30,-20,-20,-30,-40,-50,
60 | -30,-20,-10, 0, 0,-10,-20,-30,
61 | -30,-10, 20, 30, 30, 20,-10,-30,
62 | -30,-10, 30, 40, 40, 30,-10,-30,
63 | -30,-10, 30, 40, 40, 30,-10,-30,
64 | -30,-10, 20, 30, 30, 20,-10,-30,
65 | -30,-30, 0, 0, 0, 0,-30,-30,
66 | -50,-30,-30,-30,-30,-30,-30,-50
67 | };
68 |
69 | private static int EvaluatePieceScore(Square square, byte position, bool endGamePhase,
70 | ref byte knightCount, ref byte bishopCount, ref bool insufficientMaterial)
71 | {
72 | int score = 0;
73 |
74 | byte index = position;
75 |
76 | if (square.Piece.PieceColor == ChessPieceColor.Black)
77 | {
78 | index = (byte)(63-position);
79 | }
80 |
81 | //Calculate Piece Values
82 | score += square.Piece.PieceValue;
83 | score += square.Piece.DefendedValue;
84 | score -= square.Piece.AttackedValue;
85 |
86 | //Double Penalty for Hanging Pieces
87 | if (square.Piece.DefendedValue < square.Piece.AttackedValue)
88 | {
89 | score -= ((square.Piece.AttackedValue - square.Piece.DefendedValue)* 10);
90 | }
91 |
92 | //Add Points for Mobility
93 | if (square.Piece.ValidMoves != null)
94 | {
95 | score += square.Piece.ValidMoves.Count;
96 | }
97 |
98 | if (square.Piece.PieceType == ChessPieceType.Pawn)
99 | {
100 | insufficientMaterial = false;
101 |
102 | if (position % 8 == 0 || position % 8 == 7)
103 | {
104 | //Rook Pawns are worth 15% less because they can only attack one way
105 | score -= 15;
106 | }
107 |
108 | //Calculate Position Values
109 | score += PawnTable[index];
110 |
111 | if (square.Piece.PieceColor == ChessPieceColor.White)
112 | {
113 | if (whitePawnCount[position % 8] > 0)
114 | {
115 | //Doubled Pawn
116 | score -= 15;
117 | }
118 |
119 | if (position >= 8 && position <= 15)
120 | {
121 | if (square.Piece.AttackedValue == 0)
122 | {
123 | whitePawnCount[position % 8] += 50;
124 |
125 | if (square.Piece.DefendedValue != 0)
126 | whitePawnCount[position % 8] += 50;
127 | }
128 | }
129 | else if (position >= 16 && position <= 23)
130 | {
131 | if (square.Piece.AttackedValue == 0)
132 | {
133 | whitePawnCount[position % 8] += 25;
134 |
135 |
136 | if (square.Piece.DefendedValue != 0)
137 | whitePawnCount[position % 8] += 25;
138 | }
139 | }
140 |
141 | whitePawnCount[position % 8]+=10;
142 | }
143 | else
144 | {
145 | if (blackPawnCount[position % 8] > 0)
146 | {
147 | //Doubled Pawn
148 | score -= 15;
149 | }
150 |
151 | if (position >= 48 && position <= 55)
152 | {
153 | if (square.Piece.AttackedValue == 0)
154 | {
155 | blackPawnCount[position % 8] += 200;
156 |
157 | if (square.Piece.DefendedValue != 0)
158 | blackPawnCount[position % 8] += 50;
159 | }
160 | }
161 | //Pawns in 6th Row that are not attacked are worth more points.
162 | else if (position >= 40 && position <= 47)
163 | {
164 | if (square.Piece.AttackedValue == 0)
165 | {
166 | blackPawnCount[position % 8] += 100;
167 |
168 | if (square.Piece.DefendedValue != 0)
169 | blackPawnCount[position % 8] += 25;
170 | }
171 | }
172 |
173 | blackPawnCount[position % 8] += 10;
174 |
175 | }
176 | }
177 | else if (square.Piece.PieceType == ChessPieceType.Knight)
178 | {
179 | knightCount++;
180 |
181 | score += KnightTable[index];
182 |
183 | //In the end game remove a few points for Knights since they are worth less
184 | if (endGamePhase)
185 | {
186 | score -= 10;
187 | }
188 |
189 | }
190 | else if (square.Piece.PieceType == ChessPieceType.Bishop)
191 | {
192 | bishopCount++;
193 |
194 | if (bishopCount >= 2)
195 | {
196 | //2 Bishops receive a bonus
197 | score += 10;
198 | }
199 |
200 | //In the end game Bishops are worth more
201 | if (endGamePhase)
202 | {
203 | score += 10;
204 | }
205 |
206 | score += BishopTable[index];
207 | }
208 | else if (square.Piece.PieceType == ChessPieceType.Rook)
209 | {
210 | insufficientMaterial = false;
211 | }
212 | else if (square.Piece.PieceType == ChessPieceType.Queen)
213 | {
214 | insufficientMaterial = false;
215 |
216 | if (square.Piece.Moved && !endGamePhase)
217 | {
218 | score -= 10;
219 | }
220 | }
221 | else if (square.Piece.PieceType == ChessPieceType.King)
222 | {
223 | if (square.Piece.ValidMoves != null)
224 | {
225 | if (square.Piece.ValidMoves.Count < 2)
226 | {
227 | score -= 5;
228 | }
229 | }
230 |
231 | if (endGamePhase)
232 | {
233 | score += KingTableEndGame[index];
234 | }
235 | else
236 | {
237 | score += KingTable[index];
238 | }
239 |
240 |
241 |
242 |
243 | }
244 |
245 | return score;
246 | }
247 |
248 | internal static void EvaluateBoardScore(Board board)
249 | {
250 | //Black Score -
251 | //White Score +
252 | board.Score = 0;
253 |
254 | bool insufficientMaterial = true;
255 |
256 | if (board.StaleMate)
257 | {
258 | return;
259 | }
260 | if (board.HalfMoveClock >= 100)
261 | {
262 | return;
263 | }
264 | if (board.RepeatedMove >= 3)
265 | {
266 | return;
267 | }
268 | if (board.BlackMate)
269 | {
270 | board.Score = 32767;
271 | return;
272 | }
273 | if (board.WhiteMate)
274 | {
275 | board.Score = -32767;
276 | return;
277 | }
278 | if (board.BlackCheck)
279 | {
280 | board.Score += 70;
281 | if (board.EndGamePhase)
282 | board.Score += 10;
283 | }
284 | else if (board.WhiteCheck)
285 | {
286 | board.Score -= 70;
287 | if (board.EndGamePhase)
288 | board.Score -= 10;
289 | }
290 | if (board.BlackCastled)
291 | {
292 | board.Score -= 50;
293 | }
294 | if (board.WhiteCastled)
295 | {
296 | board.Score += 50;
297 | }
298 | //Add a small bonus for tempo (turn)
299 | if (board.WhoseMove == ChessPieceColor.White)
300 | {
301 | board.Score += 10;
302 | }
303 | else
304 | {
305 | board.Score -= 10;
306 | }
307 |
308 | byte blackBishopCount = 0;
309 | byte whiteBishopCount = 0;
310 |
311 | byte blackKnightCount = 0;
312 | byte whiteKnightCount = 0;
313 |
314 |
315 | byte knightCount = 0;
316 |
317 |
318 | blackPawnCount = new short[8];
319 | whitePawnCount = new short[8];
320 |
321 | for (byte x = 0; x < 64; x++)
322 | {
323 | Square square = board.Squares[x];
324 |
325 | if (square.Piece == null)
326 | continue;
327 |
328 |
329 | if (square.Piece.PieceColor == ChessPieceColor.White)
330 | {
331 | board.Score += EvaluatePieceScore(square, x, board.EndGamePhase,
332 | ref whiteKnightCount, ref whiteBishopCount, ref insufficientMaterial);
333 |
334 | if (square.Piece.PieceType == ChessPieceType.King)
335 | {
336 | if (x != 59 && x != 60)
337 | {
338 | int pawnPos = x - 8;
339 |
340 | board.Score += CheckPawnWall(board, pawnPos, x);
341 |
342 | pawnPos = x - 7;
343 |
344 | board.Score += CheckPawnWall(board, pawnPos, x);
345 |
346 | pawnPos = x - 9;
347 |
348 | board.Score += CheckPawnWall(board, pawnPos, x);
349 | }
350 | }
351 | }
352 | else if (square.Piece.PieceColor == ChessPieceColor.Black)
353 | {
354 | board.Score -= EvaluatePieceScore(square, x, board.EndGamePhase,
355 | ref blackKnightCount, ref blackBishopCount, ref insufficientMaterial);
356 |
357 |
358 | if (square.Piece.PieceType == ChessPieceType.King)
359 | {
360 | if (x != 3 && x != 4)
361 | {
362 | int pawnPos = x + 8;
363 |
364 | board.Score -= CheckPawnWall(board, pawnPos, x);
365 |
366 | pawnPos = x + 7;
367 |
368 | board.Score -= CheckPawnWall(board, pawnPos, x);
369 |
370 | pawnPos = x + 9;
371 |
372 | board.Score -= CheckPawnWall(board, pawnPos, x);
373 | }
374 |
375 | }
376 |
377 | }
378 |
379 | if (square.Piece.PieceType == ChessPieceType.Knight)
380 | {
381 | knightCount++;
382 |
383 | if (knightCount > 1)
384 | {
385 | insufficientMaterial = false;
386 | }
387 | }
388 |
389 | if ((blackBishopCount + whiteBishopCount) > 1)
390 | {
391 | insufficientMaterial = false;
392 | }
393 | else if ((blackBishopCount + blackKnightCount) > 1)
394 | {
395 | insufficientMaterial = false;
396 | }
397 | else if ((whiteBishopCount + whiteKnightCount) > 1)
398 | {
399 | insufficientMaterial = false;
400 | }
401 |
402 | }
403 |
404 | if (insufficientMaterial)
405 | {
406 | board.Score = 0;
407 | board.StaleMate = true;
408 | board.InsufficientMaterial = true;
409 | return;
410 | }
411 |
412 | if (board.EndGamePhase)
413 | {
414 | if (board.BlackCheck)
415 | {
416 | board.Score += 10;
417 | }
418 | else if (board.WhiteCheck)
419 | {
420 | board.Score -= 10;
421 | }
422 | }
423 | else
424 | {
425 | if (!board.WhiteCanCastle && !board.WhiteCastled)
426 | {
427 | board.Score -= 50;
428 | }
429 | if (!board.BlackCanCastle && !board.BlackCastled)
430 | {
431 | board.Score += 50;
432 | }
433 | }
434 |
435 | //Black Isolated Pawns
436 | if (blackPawnCount[0] >= 1 && blackPawnCount[1] == 0)
437 | {
438 | board.Score += 12;
439 | }
440 | if (blackPawnCount[1] >= 1 && blackPawnCount[0] == 0 &&
441 | blackPawnCount[2] == 0)
442 | {
443 | board.Score += 14;
444 | }
445 | if (blackPawnCount[2] >= 1 && blackPawnCount[1] == 0 &&
446 | blackPawnCount[3] == 0)
447 | {
448 | board.Score += 16;
449 | }
450 | if (blackPawnCount[3] >= 1 && blackPawnCount[2] == 0 &&
451 | blackPawnCount[4] == 0)
452 | {
453 | board.Score += 20;
454 | }
455 | if (blackPawnCount[4] >= 1 && blackPawnCount[3] == 0 &&
456 | blackPawnCount[5] == 0)
457 | {
458 | board.Score += 20;
459 | }
460 | if (blackPawnCount[5] >= 1 && blackPawnCount[4] == 0 &&
461 | blackPawnCount[6] == 0)
462 | {
463 | board.Score += 16;
464 | }
465 | if (blackPawnCount[6] >= 1 && blackPawnCount[5] == 0 &&
466 | blackPawnCount[7] == 0)
467 | {
468 | board.Score += 14;
469 | }
470 | if (blackPawnCount[7] >= 1 && blackPawnCount[6] == 0)
471 | {
472 | board.Score += 12;
473 | }
474 |
475 | //White Isolated Pawns
476 | if (whitePawnCount[0] >= 1 && whitePawnCount[1] == 0)
477 | {
478 | board.Score -= 12;
479 | }
480 | if (whitePawnCount[1] >= 1 && whitePawnCount[0] == 0 &&
481 | whitePawnCount[2] == 0)
482 | {
483 | board.Score -= 14;
484 | }
485 | if (whitePawnCount[2] >= 1 && whitePawnCount[1] == 0 &&
486 | whitePawnCount[3] == 0)
487 | {
488 | board.Score -= 16;
489 | }
490 | if (whitePawnCount[3] >= 1 && whitePawnCount[2] == 0 &&
491 | whitePawnCount[4] == 0)
492 | {
493 | board.Score -= 20;
494 | }
495 | if (whitePawnCount[4] >= 1 && whitePawnCount[3] == 0 &&
496 | whitePawnCount[5] == 0)
497 | {
498 | board.Score -= 20;
499 | }
500 | if (whitePawnCount[5] >= 1 && whitePawnCount[4] == 0 &&
501 | whitePawnCount[6] == 0)
502 | {
503 | board.Score -= 16;
504 | }
505 | if (whitePawnCount[6] >= 1 && whitePawnCount[5] == 0 &&
506 | whitePawnCount[7] == 0)
507 | {
508 | board.Score -= 14;
509 | }
510 | if (whitePawnCount[7] >= 1 && whitePawnCount[6] == 0)
511 | {
512 | board.Score -= 12;
513 | }
514 |
515 | //Black Passed Pawns
516 | if (blackPawnCount[0] >= 1 && whitePawnCount[0] == 0)
517 | {
518 | board.Score -= blackPawnCount[0];
519 | }
520 | if (blackPawnCount[1] >= 1 && whitePawnCount[1] == 0)
521 | {
522 | board.Score -= blackPawnCount[1];
523 | }
524 | if (blackPawnCount[2] >= 1 && whitePawnCount[2] == 0)
525 | {
526 | board.Score -= blackPawnCount[2];
527 | }
528 | if (blackPawnCount[3] >= 1 && whitePawnCount[3] == 0)
529 | {
530 | board.Score -= blackPawnCount[3];
531 | }
532 | if (blackPawnCount[4] >= 1 && whitePawnCount[4] == 0)
533 | {
534 | board.Score -= blackPawnCount[4];
535 | }
536 | if (blackPawnCount[5] >= 1 && whitePawnCount[5] == 0)
537 | {
538 | board.Score -= blackPawnCount[5];
539 | }
540 | if (blackPawnCount[6] >= 1 && whitePawnCount[6] == 0)
541 | {
542 | board.Score -= blackPawnCount[6];
543 | }
544 | if (blackPawnCount[7] >= 1 && whitePawnCount[7] == 0)
545 | {
546 | board.Score -= blackPawnCount[7];
547 | }
548 |
549 | //White Passed Pawns
550 | if (whitePawnCount[0] >= 1 && blackPawnCount[1] == 0)
551 | {
552 | board.Score += whitePawnCount[0];
553 | }
554 | if (whitePawnCount[1] >= 1 && blackPawnCount[1] == 0)
555 | {
556 | board.Score += whitePawnCount[1];
557 | }
558 | if (whitePawnCount[2] >= 1 && blackPawnCount[2] == 0)
559 | {
560 | board.Score += whitePawnCount[2];
561 | }
562 | if (whitePawnCount[3] >= 1 && blackPawnCount[3] == 0)
563 | {
564 | board.Score += whitePawnCount[3];
565 | }
566 | if (whitePawnCount[4] >= 1 && blackPawnCount[4] == 0)
567 | {
568 | board.Score += whitePawnCount[4];
569 | }
570 | if (whitePawnCount[5] >= 1 && blackPawnCount[5] == 0)
571 | {
572 | board.Score += whitePawnCount[5];
573 | }
574 | if (whitePawnCount[6] >= 1 && blackPawnCount[6] == 0)
575 | {
576 | board.Score += whitePawnCount[6];
577 | }
578 | if (whitePawnCount[7] >= 1 && blackPawnCount[7] == 0)
579 | {
580 | board.Score += whitePawnCount[7];
581 | }
582 | }
583 |
584 | private static int CheckPawnWall(Board board, int pawnPos, int kingPos)
585 | {
586 |
587 | if (kingPos % 8 == 7 && pawnPos % 8 == 0)
588 | {
589 | return 0;
590 | }
591 |
592 | if (kingPos % 8 == 0 && pawnPos % 8 == 7)
593 | {
594 | return 0;
595 | }
596 |
597 | if (pawnPos > 63 || pawnPos < 0)
598 | {
599 | return 0;
600 | }
601 |
602 | if (board.Squares[pawnPos].Piece != null)
603 | {
604 | if (board.Squares[pawnPos].Piece.PieceColor == board.Squares[kingPos].Piece.PieceColor)
605 | {
606 | if (board.Squares[pawnPos].Piece.PieceType == ChessPieceType.Pawn)
607 | {
608 | return 10;
609 | }
610 | }
611 | }
612 |
613 | return 0;
614 | }
615 | }
616 | }
--------------------------------------------------------------------------------
/ChessCoreEngine/FileIO.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Xml.Serialization;
5 | //using ChessEngine.XML;
6 |
7 | namespace ChessEngine.Engine
8 | {
9 | internal static class FileIO
10 | {
11 | internal static void SaveCurrentGameMove(Board currentBoard, Board previousBoard, ICollection gameBook, MoveContent bestMove)
12 | {
13 | try
14 | {
15 | var move = new OpeningMove();
16 |
17 | move.StartingFEN = Board.Fen(true, previousBoard);
18 | move.EndingFEN = Board.Fen(true, currentBoard);
19 | move.Moves.Add(bestMove);
20 |
21 | gameBook.Add(move);
22 |
23 | foreach (OpeningMove move1 in gameBook)
24 | {
25 | byte repeatedMoves = 0;
26 |
27 | foreach (OpeningMove move2 in gameBook)
28 | {
29 | if (move1.EndingFEN == move2.EndingFEN)
30 | {
31 | repeatedMoves++;
32 | }
33 | }
34 |
35 | if (previousBoard.RepeatedMove < repeatedMoves)
36 | {
37 | previousBoard.RepeatedMove = repeatedMoves;
38 | currentBoard.RepeatedMove = repeatedMoves;
39 | }
40 | }
41 | if (currentBoard.RepeatedMove >= 3)
42 | {
43 | currentBoard.StaleMate = true;
44 | }
45 | }
46 | catch (Exception)
47 | {
48 | return;
49 | }
50 |
51 | return;
52 | }
53 |
54 | internal static bool SaveGame(String filePath, Board chessBoard, ChessPieceColor whoseMove, Stack moveHistory)
55 | {
56 | if (String.IsNullOrEmpty(filePath))
57 | return false;
58 |
59 | /* var serializer = new XmlSerializer(typeof(XMLBoard));
60 | TextWriter writer = new StreamWriter(filePath);
61 |
62 | var xmlBoard = new XMLBoard();
63 | xmlBoard.Squares = new List();
64 | xmlBoard.MoveHistory = new List();
65 |
66 | xmlBoard.WhoseMove = whoseMove;
67 | xmlBoard.FiftyMoveCount = chessBoard.FiftyMove;
68 |
69 | for (byte x = 0; x < 64; x++)
70 | {
71 | var square = new XMLBoard.XMLSquare();
72 |
73 | if (chessBoard.Squares[x].Piece != null)
74 | {
75 | square.CurrentPiece = new XMLBoard.XMLChessPiece();
76 | square.CurrentPiece.Moved = chessBoard.Squares[x].Piece.Moved;
77 | square.CurrentPiece.PieceColor = chessBoard.Squares[x].Piece.PieceColor;
78 | square.CurrentPiece.PieceType = chessBoard.Squares[x].Piece.PieceType;
79 | square.BoardColumn = (byte)(x % 8);
80 | square.BoardRow = (byte)(x / 8);
81 | }
82 |
83 | xmlBoard.Squares.Add(square);
84 | }
85 | foreach (MoveContent move in moveHistory)
86 | {
87 | xmlBoard.MoveHistory.Add(move);
88 | }
89 |
90 | serializer.Serialize(writer, xmlBoard);
91 | writer.Close();*/
92 |
93 | return true;
94 | }
95 |
96 | internal static bool LoadGame(String filePath, ref Board chessBoard, ChessPieceColor whoseMove, ref Stack moveHistory, ref List currentGameBook, ref List undoGameBook)
97 | {
98 | if (String.IsNullOrEmpty(filePath))
99 | {
100 | return false;
101 | }
102 |
103 | /* chessBoard = new Board();
104 | moveHistory = new Stack();
105 |
106 | currentGameBook = new List();
107 | undoGameBook = new List();
108 |
109 | for (byte x = 0; x < 64; x++)
110 | {
111 | var square = new Square();
112 | square.Piece = null;
113 | chessBoard.Squares[x] = square;
114 | }
115 |
116 |
117 | var serializer = new XmlSerializer(typeof(XMLBoard));
118 | TextReader reader = new StreamReader(filePath);
119 |
120 | var xmlBoard = (XMLBoard)serializer.Deserialize(reader);
121 | reader.Close();
122 |
123 | foreach (XMLBoard.XMLSquare square in xmlBoard.Squares)
124 | {
125 | if (square.CurrentPiece != null)
126 | {
127 | RegisterPiece(square.BoardColumn, square.BoardRow, square.CurrentPiece, chessBoard);
128 | }
129 | }
130 |
131 | chessBoard.WhoseMove = xmlBoard.WhoseMove;
132 | chessBoard.FiftyMove = xmlBoard.FiftyMoveCount;
133 |
134 | chessBoard.ZobristHash = Zobrist.CalculateZobristHash(chessBoard);
135 |
136 | foreach (MoveContent move in xmlBoard.MoveHistory)
137 | {
138 | moveHistory.Push(move);
139 | }
140 |
141 | PieceValidMoves.GenerateValidMoves(chessBoard);
142 | Evaluation.EvaluateBoardScore(chessBoard);*/
143 |
144 | return true;
145 | }
146 |
147 | public static bool LoadOpeningBook(ref List openingBook)
148 | {
149 | /* if (File.Exists("OpeningBook\\OpeningBook.xml"))
150 | {
151 | var serializer = new XmlSerializer(typeof(List));
152 | TextReader reader =
153 | new StreamReader("OpeningBook\\OpeningBook.xml");
154 |
155 | openingBook = (List)serializer.Deserialize(reader);
156 | reader.Close();
157 | }
158 |
159 | List newOpeningBook = new List();
160 |
161 | //Delete Duplicates
162 | foreach (OpeningMove mv1 in openingBook)
163 | {
164 | bool duplicate = false;
165 | foreach (OpeningMove mv2 in newOpeningBook)
166 | {
167 | if (mv1.StartingFEN == mv2.StartingFEN)
168 | {
169 | if (mv1.EndingFEN == mv2.EndingFEN)
170 | {
171 | duplicate = true;
172 | }
173 | }
174 | }
175 | if (duplicate == false)
176 | {
177 | newOpeningBook.Add(mv1);
178 | }
179 | }
180 |
181 | openingBook = newOpeningBook; */
182 |
183 | return true;
184 | }
185 | /*
186 | private static void RegisterPiece(byte boardColumn, byte boardRow, XMLBoard.XMLChessPiece piece, Board chessBoard)
187 | {
188 | byte position = (byte)(boardColumn + (boardRow * 8));
189 | chessBoard.Squares[position].Piece = new Piece(piece.PieceType, piece.PieceColor);
190 | chessBoard.Squares[position].Piece.Moved = piece.Moved;
191 |
192 | return;
193 | }*/
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/ChessCoreEngine/MoveContent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ChessEngine.Engine
4 | {
5 |
6 | public struct PieceMoving
7 | {
8 | public byte DstPosition;
9 | public bool Moved;
10 | public ChessPieceColor PieceColor;
11 | public ChessPieceType PieceType;
12 | public byte SrcPosition;
13 |
14 | public PieceMoving(ChessPieceColor pieceColor, ChessPieceType pieceType, bool moved,
15 | byte srcPosition, byte dstPosition)
16 | {
17 | PieceColor = pieceColor;
18 | PieceType = pieceType;
19 | SrcPosition = srcPosition;
20 | DstPosition = dstPosition;
21 | Moved = moved;
22 | }
23 |
24 | public PieceMoving(PieceMoving pieceMoving)
25 | {
26 | PieceColor = pieceMoving.PieceColor;
27 | PieceType = pieceMoving.PieceType;
28 | SrcPosition = pieceMoving.SrcPosition;
29 | DstPosition = pieceMoving.DstPosition;
30 | Moved = pieceMoving.Moved;
31 | }
32 |
33 | public PieceMoving(ChessPieceType pieceType)
34 | {
35 | PieceType = pieceType;
36 | PieceColor = ChessPieceColor.White;
37 | SrcPosition = 0;
38 | DstPosition = 0;
39 | Moved = false;
40 | }
41 | }
42 |
43 | public struct PieceTaken
44 | {
45 | public bool Moved;
46 | public ChessPieceColor PieceColor;
47 | public ChessPieceType PieceType;
48 | public byte Position;
49 |
50 | public PieceTaken(ChessPieceColor pieceColor, ChessPieceType pieceType, bool moved,
51 | byte position)
52 | {
53 | PieceColor = pieceColor;
54 | PieceType = pieceType;
55 | Position = position;
56 | Moved = moved;
57 | }
58 |
59 | public PieceTaken(ChessPieceType pieceType)
60 | {
61 | PieceColor = ChessPieceColor.White;
62 | PieceType = pieceType;
63 | Position = 0;
64 | Moved = false;
65 | }
66 | }
67 |
68 | public sealed class MoveContent
69 | {
70 | public bool EnPassantOccured;
71 | public PieceMoving MovingPiecePrimary;
72 | public PieceMoving MovingPieceSecondary;
73 | public ChessPieceType PawnPromotedTo;
74 | public PieceTaken TakenPiece;
75 |
76 | public bool DoubleRowQueen;
77 | public bool DoubleColQueen;
78 |
79 | public bool DoubleRowRook;
80 | public bool DoubleColRook;
81 |
82 | public bool DoubleRowKnight;
83 | public bool DoubleColKnight;
84 |
85 | public string PgnMove;
86 |
87 |
88 | public MoveContent()
89 | {
90 | MovingPiecePrimary = new PieceMoving(ChessPieceType.None);
91 | MovingPieceSecondary = new PieceMoving(ChessPieceType.None);
92 | TakenPiece = new PieceTaken(ChessPieceType.None);
93 | }
94 |
95 | public MoveContent(MoveContent moveContent)
96 | {
97 | MovingPiecePrimary = new PieceMoving(moveContent.MovingPiecePrimary);
98 | MovingPieceSecondary = new PieceMoving(moveContent.MovingPieceSecondary);
99 |
100 | TakenPiece = new PieceTaken(moveContent.TakenPiece.PieceColor,
101 | moveContent.TakenPiece.PieceType,
102 | moveContent.TakenPiece.Moved,
103 | moveContent.TakenPiece.Position);
104 |
105 | EnPassantOccured = moveContent.EnPassantOccured;
106 | PawnPromotedTo = moveContent.PawnPromotedTo;
107 | }
108 |
109 | public MoveContent(string move) : this()
110 | {
111 | int srcCol =-1;
112 |
113 | bool comment = false;
114 | bool srcFound = false;
115 |
116 | if (move.Contains("=Q"))
117 | {
118 | PawnPromotedTo = ChessPieceType.Queen;
119 | }
120 | else if (move.Contains("=N"))
121 | {
122 | PawnPromotedTo = ChessPieceType.Knight;
123 | }
124 | else if (move.Contains("=R"))
125 | {
126 | PawnPromotedTo = ChessPieceType.Rook;
127 | }
128 | else if (move.Contains("=B"))
129 | {
130 | PawnPromotedTo = ChessPieceType.Bishop;
131 | }
132 |
133 | foreach (char c in move)
134 | {
135 | if (c=='{')
136 | {
137 | comment = true;
138 | continue;
139 | }
140 | if (c == '}')
141 | {
142 | comment = false;
143 | continue;
144 | }
145 |
146 | if (comment)
147 | {
148 | continue;
149 | }
150 |
151 | if (MovingPiecePrimary.PieceType == ChessPieceType.None)
152 | {
153 | //Get Piece Type
154 | MovingPiecePrimary.PieceType = GetPieceType(c);
155 |
156 | if (MovingPiecePrimary.PieceType == ChessPieceType.None)
157 | {
158 | MovingPiecePrimary.PieceType = ChessPieceType.Pawn;
159 |
160 | //This is a column character
161 | srcCol= GetIntFromColumn(c);
162 | }
163 | continue;
164 | }
165 | if (srcCol < 0)
166 | {
167 | srcCol = GetIntFromColumn(c);
168 | continue;
169 | }
170 | if (srcCol >= 0)
171 | {
172 | int srcRow = int.Parse(c.ToString());
173 |
174 | if (!srcFound)
175 | {
176 | MovingPiecePrimary.SrcPosition = GetBoardIndex(srcCol, 8 - srcRow);
177 | srcFound = true;
178 | }
179 | else
180 | {
181 | MovingPiecePrimary.DstPosition = GetBoardIndex(srcCol, 8 - srcRow);
182 | }
183 |
184 | srcCol = -1;
185 | continue;
186 | }
187 | }
188 | }
189 |
190 | public static bool ParseAN(string move, ref byte sourceColumn, ref byte sourceRow, ref byte destinationColumn, ref byte destinationRow)
191 | {
192 | if (move.Length != 4) return false;
193 | sourceColumn = (byte)GetIntFromColumn(move[0]);
194 | sourceRow = (byte)(8 - int.Parse(""+move[1]));
195 | destinationColumn = (byte)GetIntFromColumn(move[2]);
196 | destinationRow = (byte)(8 - int.Parse(""+move[3]));
197 | return true;
198 | }
199 |
200 | public string GetPureCoordinateNotation()
201 | {
202 | var srcCol = (byte) (MovingPiecePrimary.SrcPosition%8);
203 | var srcRow = (byte)(8 - (MovingPiecePrimary.SrcPosition / 8));
204 | var dstCol = (byte) (MovingPiecePrimary.DstPosition%8);
205 | var dstRow = (byte) (8 - (MovingPiecePrimary.DstPosition/8));
206 |
207 | string result = ""+(char)('a'+srcCol)+(srcRow) + (char)('a'+dstCol)+(dstRow);
208 | if (PawnPromotedTo == ChessPieceType.Queen)
209 | {
210 | result += "=Q";
211 | }
212 | else if (PawnPromotedTo == ChessPieceType.Rook)
213 | {
214 | result += "=R";
215 | }
216 | else if (PawnPromotedTo == ChessPieceType.Bishop)
217 | {
218 | result += "=B";
219 | }
220 | else if (PawnPromotedTo == ChessPieceType.Knight)
221 | {
222 | result += "=N";
223 | }
224 | return result;
225 | }
226 |
227 | public new string ToString()
228 | {
229 | if (!String.IsNullOrEmpty(PgnMove))
230 | {
231 | return PgnMove;
232 | }
233 |
234 | var srcCol = (byte) (MovingPiecePrimary.SrcPosition%8);
235 | var srcRow = (byte)(8 - (MovingPiecePrimary.SrcPosition / 8));
236 | var dstCol = (byte) (MovingPiecePrimary.DstPosition%8);
237 | var dstRow = (byte) (8 - (MovingPiecePrimary.DstPosition/8));
238 |
239 | if (MovingPieceSecondary.PieceType == ChessPieceType.Rook)
240 | {
241 | if (MovingPieceSecondary.PieceColor == ChessPieceColor.Black)
242 | {
243 | if (MovingPieceSecondary.SrcPosition == 7)
244 | {
245 | PgnMove += "O-O";
246 | }
247 | else if (MovingPieceSecondary.SrcPosition == 0)
248 | {
249 | PgnMove += "O-O-O";
250 | }
251 | }
252 | else if (MovingPieceSecondary.PieceColor == ChessPieceColor.White)
253 | {
254 | if (MovingPieceSecondary.SrcPosition == 63)
255 | {
256 | PgnMove += "O-O";
257 | }
258 | else if (MovingPieceSecondary.SrcPosition == 56)
259 | {
260 | PgnMove += "O-O-O";
261 | }
262 | }
263 | }
264 | else
265 | {
266 | PgnMove += GetPgnMove(MovingPiecePrimary.PieceType);
267 |
268 | switch (MovingPiecePrimary.PieceType)
269 | {
270 | case ChessPieceType.Knight:
271 | PgnMove += GetColumnFromInt(srcCol);
272 | PgnMove += srcRow;
273 | break;
274 | case ChessPieceType.Rook:
275 | PgnMove += GetColumnFromInt(srcCol);
276 | PgnMove += srcRow;
277 | break;
278 | case ChessPieceType.Pawn:
279 | if (srcCol != dstCol)
280 | {
281 | PgnMove += GetColumnFromInt(srcCol);
282 | }
283 | break;
284 | }
285 |
286 | if (TakenPiece.PieceType != ChessPieceType.None)
287 | {
288 | PgnMove += "x";
289 | }
290 |
291 | PgnMove += GetColumnFromInt(dstCol);
292 |
293 | PgnMove += dstRow;
294 |
295 | if (PawnPromotedTo == ChessPieceType.Queen)
296 | {
297 | PgnMove += "=Q";
298 | }
299 | else if (PawnPromotedTo == ChessPieceType.Rook)
300 | {
301 | PgnMove += "=R";
302 | }
303 | else if (PawnPromotedTo == ChessPieceType.Bishop)
304 | {
305 | PgnMove += "=B";
306 | }
307 | else if (PawnPromotedTo == ChessPieceType.Knight)
308 | {
309 | PgnMove += "=N";
310 | }
311 | }
312 |
313 | return PgnMove;
314 | }
315 |
316 | internal string GeneratePGNString(Board board)
317 | {
318 | if (!String.IsNullOrEmpty(PgnMove))
319 | {
320 | return PgnMove;
321 | }
322 |
323 | bool doubleColumn = false;
324 | bool doubleRow = false;
325 |
326 |
327 | bool doubleDestination = false;
328 |
329 | var srcCol = (byte)(MovingPiecePrimary.SrcPosition % 8);
330 | var srcRow = (byte)(8 - (MovingPiecePrimary.SrcPosition / 8));
331 | var dstCol = (byte)(MovingPiecePrimary.DstPosition % 8);
332 | var dstRow = (byte)(8 - (MovingPiecePrimary.DstPosition / 8));
333 |
334 | PgnMove = "";
335 |
336 | for (byte x = 0; x < 64; x++)
337 | {
338 | if (x == MovingPiecePrimary.DstPosition)
339 | continue;
340 |
341 | Square square = board.Squares[x];
342 |
343 | if (square.Piece == null)
344 | continue;
345 |
346 |
347 | if (square.Piece.PieceType == MovingPiecePrimary.PieceType)
348 | {
349 | if (square.Piece.PieceColor == MovingPiecePrimary.PieceColor)
350 | {
351 | foreach (byte move in square.Piece.ValidMoves)
352 | {
353 | if (move == MovingPiecePrimary.DstPosition)
354 | {
355 | doubleDestination = true;
356 |
357 | byte col = (byte)(x % 8);
358 | byte row = (byte)(8-(x / 8));
359 |
360 | if (col == srcCol)
361 | {
362 | doubleColumn = true;
363 | }
364 |
365 | if (row == srcRow)
366 | {
367 | doubleRow = true;
368 | }
369 |
370 | break;
371 | }
372 | }
373 |
374 |
375 | }
376 | }
377 | }
378 |
379 |
380 | if (MovingPieceSecondary.PieceType == ChessPieceType.Rook)
381 | {
382 | if (MovingPieceSecondary.PieceColor == ChessPieceColor.Black)
383 | {
384 | if (MovingPieceSecondary.SrcPosition == 7)
385 | {
386 | PgnMove += "O-O";
387 | }
388 | else if (MovingPieceSecondary.SrcPosition == 0)
389 | {
390 | PgnMove += "O-O-O";
391 | }
392 | }
393 | else if (MovingPieceSecondary.PieceColor == ChessPieceColor.White)
394 | {
395 | if (MovingPieceSecondary.SrcPosition == 63)
396 | {
397 | PgnMove += "O-O";
398 | }
399 | else if (MovingPieceSecondary.SrcPosition == 56)
400 | {
401 | PgnMove += "O-O-O";
402 | }
403 | }
404 | }
405 | else
406 | {
407 | PgnMove += GetPgnMove(MovingPiecePrimary.PieceType);
408 |
409 | switch (MovingPiecePrimary.PieceType)
410 | {
411 | case ChessPieceType.Knight:
412 | {
413 | if (doubleDestination)
414 | {
415 | if (!doubleColumn)
416 | {
417 | PgnMove += GetColumnFromInt(srcCol);
418 | }
419 | else
420 | {
421 | if (doubleRow)
422 | {
423 | PgnMove += GetColumnFromInt(srcCol);
424 | }
425 |
426 | PgnMove += srcRow;
427 | }
428 | }
429 | break;
430 | }
431 | case ChessPieceType.Bishop:
432 | {
433 | if (doubleDestination)
434 | {
435 | if (!doubleColumn)
436 | {
437 | PgnMove += GetColumnFromInt(srcCol);
438 | }
439 | else
440 | {
441 | if (doubleRow)
442 | {
443 | PgnMove += GetColumnFromInt(srcCol);
444 | }
445 |
446 | PgnMove += srcRow;
447 | }
448 | }
449 | break;
450 | }
451 | case ChessPieceType.Rook:
452 | {
453 | if (doubleDestination)
454 | {
455 | if (!doubleColumn)
456 | {
457 | PgnMove += GetColumnFromInt(srcCol);
458 | }
459 | else
460 | {
461 | if (doubleRow)
462 | {
463 | PgnMove += GetColumnFromInt(srcCol);
464 | }
465 |
466 | PgnMove += srcRow;
467 | }
468 | }
469 | break;
470 | }
471 | case ChessPieceType.Queen:
472 | {
473 | if (doubleDestination)
474 | {
475 | if (!doubleColumn)
476 | {
477 | PgnMove += GetColumnFromInt(srcCol);
478 | }
479 | else
480 | {
481 | if (doubleRow)
482 | {
483 | PgnMove += GetColumnFromInt(srcCol);
484 | }
485 |
486 | PgnMove += srcRow;
487 | }
488 | }
489 | break;
490 | }
491 | case ChessPieceType.Pawn:
492 | {
493 | if (doubleDestination && srcCol != dstCol)
494 | {
495 | PgnMove += GetColumnFromInt(srcCol);
496 | }
497 | else if (TakenPiece.PieceType != ChessPieceType.None)
498 | {
499 | PgnMove += GetColumnFromInt(srcCol);
500 | }
501 | break;
502 | }
503 | }
504 |
505 | if (TakenPiece.PieceType != ChessPieceType.None)
506 | {
507 |
508 | PgnMove += "x";
509 | }
510 |
511 | PgnMove += GetColumnFromInt(dstCol);
512 |
513 | PgnMove += dstRow;
514 |
515 | if (PawnPromotedTo == ChessPieceType.Queen)
516 | {
517 | PgnMove += "=Q";
518 | }
519 | else if (PawnPromotedTo == ChessPieceType.Rook)
520 | {
521 | PgnMove += "=R";
522 | }
523 | else if (PawnPromotedTo == ChessPieceType.Bishop)
524 | {
525 | PgnMove += "=B";
526 | }
527 | else if (PawnPromotedTo == ChessPieceType.Knight)
528 | {
529 | PgnMove += "=N";
530 | }
531 | }
532 |
533 | return PgnMove;
534 | }
535 |
536 | private static byte GetBoardIndex(int col, int row)
537 | {
538 | return (byte)(col + (row * 8));
539 | }
540 |
541 | private static string GetColumnFromInt(int column)
542 | {
543 | switch (column)
544 | {
545 | case 0:
546 | return "a";
547 | case 1:
548 | return "b";
549 | case 2:
550 | return "c";
551 | case 3:
552 | return "d";
553 | case 4:
554 | return "e";
555 | case 5:
556 | return "f";
557 | case 6:
558 | return "g";
559 | case 7:
560 | return "h";
561 | default:
562 | return "Unknown";
563 | }
564 | }
565 |
566 | private static int GetIntFromColumn(char column)
567 | {
568 | switch (column)
569 | {
570 | case 'a':
571 | return 0;
572 | case 'b':
573 | return 1;
574 | case 'c':
575 | return 2;
576 | case 'd':
577 | return 3;
578 | case 'e':
579 | return 4;
580 | case 'f':
581 | return 5;
582 | case 'g':
583 | return 6;
584 | case 'h':
585 | return 7;
586 | default:
587 | return -1;
588 | }
589 | }
590 |
591 | private static ChessPieceType GetPieceType(char c)
592 | {
593 | switch (c)
594 | {
595 | case 'B':
596 | return ChessPieceType.Bishop;
597 | case 'K':
598 | return ChessPieceType.King;
599 | case 'N':
600 | return ChessPieceType.Knight;
601 | case 'Q':
602 | return ChessPieceType.Queen;
603 | case 'R':
604 | return ChessPieceType.Rook;
605 | default:
606 | return ChessPieceType.None;
607 | }
608 | }
609 |
610 | private static string GetPgnMove(ChessPieceType pieceType)
611 | {
612 | switch (pieceType)
613 | {
614 | case ChessPieceType.Bishop:
615 | return "B";
616 |
617 | case ChessPieceType.King:
618 | return "K";
619 |
620 | case ChessPieceType.Knight:
621 | return "N";
622 |
623 | case ChessPieceType.Queen:
624 | return "Q";
625 |
626 | case ChessPieceType.Rook:
627 | return "R";
628 | default:
629 | return "";
630 | }
631 | }
632 | }
633 | }
--------------------------------------------------------------------------------
/ChessCoreEngine/PGN.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace ChessEngine.Engine
5 | {
6 | public class PGN
7 | {
8 | public enum Result
9 | {
10 | White,
11 | Black,
12 | Tie,
13 | Ongoing
14 | }
15 |
16 |
17 | public static string GeneratePGN(Stack moveHistory, int round, string whitePlayer, string blackPlayer, Result result)
18 | {
19 | int count = 0;
20 |
21 | string pgn = "";
22 |
23 | /*
24 | [Event "F/S Return Match"]
25 | [Site "Belgrade, Serbia Yugoslavia|JUG"]
26 | [Date "1992.11.04"]
27 | [Round "29"]
28 | [White "Fischer, Robert J."]
29 | [Black "Spassky, Boris V."]
30 | [Result "1/2-1/2"]
31 | */
32 |
33 | string pgnHeader = "";
34 |
35 | pgnHeader += "[Date \"" + DateTime.Now.Year + "." + DateTime.Now.Month + "." + DateTime.Now.Day + "\"]\r\n";
36 | pgnHeader += "[White \"" + whitePlayer + "\"]\r\n";
37 | pgnHeader += "[Black \"" + blackPlayer + "\"]\r\n";
38 |
39 | if (result == Result.Ongoing)
40 | {
41 | pgnHeader += "[Result \"" + "*" + "\"]\r\n";
42 | }
43 | else if (result == Result.White)
44 | {
45 | pgnHeader += "[Result \"" + "1-0" + "\"]\r\n";
46 | }
47 | else if (result == Result.Black)
48 | {
49 | pgnHeader += "[Result \"" + "0-1" + "\"]\r\n";
50 | }
51 | else if (result == Result.Tie)
52 | {
53 | pgnHeader += "[Result \"" + "1/2-1/2" + "\"]\r\n";
54 | }
55 |
56 | foreach (MoveContent move in moveHistory)
57 | {
58 | string tmp = "";
59 |
60 | if (move.MovingPiecePrimary.PieceColor == ChessPieceColor.White)
61 | {
62 | tmp += ((moveHistory.Count / 2) - count + 1) + ". ";
63 | }
64 |
65 | tmp += move.ToString();
66 | tmp += " ";
67 |
68 | tmp += pgn;
69 | pgn = tmp;
70 |
71 | if (move.MovingPiecePrimary.PieceColor == ChessPieceColor.Black)
72 | {
73 | count++;
74 | }
75 | }
76 |
77 | if (result == Result.White)
78 | {
79 | pgn += " 1-0";
80 | }
81 | else if (result == Result.Black)
82 | {
83 | pgn += " 0-1";
84 | }
85 | else if (result == Result.Tie)
86 | {
87 | pgn += " 1/2-1/2";
88 | }
89 |
90 | return pgnHeader + pgn;
91 | }
92 |
93 |
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/ChessCoreEngine/Piece.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace ChessEngine.Engine
5 | {
6 |
7 | #region Enums
8 |
9 | #region ChessPieceColor enum
10 |
11 | public enum ChessPieceColor
12 | {
13 | White,
14 | Black
15 | }
16 |
17 | #endregion
18 |
19 | #region ChessPieceType enum
20 |
21 | public enum ChessPieceType
22 | {
23 | King,
24 | Queen,
25 | Rook,
26 | Bishop,
27 | Knight,
28 | Pawn,
29 | None
30 | }
31 |
32 | #endregion
33 |
34 | #endregion
35 |
36 | internal sealed class Piece
37 | {
38 | #region InternalMembers
39 |
40 | internal ChessPieceColor PieceColor;
41 | internal ChessPieceType PieceType;
42 |
43 | internal short PieceValue;
44 | internal short PieceActionValue;
45 |
46 | internal short AttackedValue;
47 | internal short DefendedValue;
48 |
49 | internal int LastValidMoveCount;
50 | internal bool Moved;
51 |
52 | internal bool Selected;
53 |
54 | internal Stack ValidMoves;
55 |
56 | #endregion
57 |
58 | #region Constructors
59 |
60 | internal Piece(Piece piece)
61 | {
62 | PieceColor = piece.PieceColor;
63 | PieceType = piece.PieceType;
64 | Moved = piece.Moved;
65 | PieceValue = piece.PieceValue;
66 | PieceActionValue = piece.PieceActionValue;
67 |
68 | if (piece.ValidMoves != null)
69 | LastValidMoveCount = piece.ValidMoves.Count;
70 | }
71 |
72 | internal Piece(ChessPieceType chessPiece, ChessPieceColor chessPieceColor)
73 | {
74 | PieceType = chessPiece;
75 | PieceColor = chessPieceColor;
76 |
77 | if (PieceType == ChessPieceType.Pawn || PieceType == ChessPieceType.Knight)
78 | {
79 | LastValidMoveCount = 2;
80 | }
81 | else
82 | {
83 | LastValidMoveCount = 0;
84 | }
85 |
86 | ValidMoves = new Stack(LastValidMoveCount);
87 |
88 | PieceValue = CalculatePieceValue(PieceType);
89 | PieceActionValue = CalculatePieceActionValue(PieceType);
90 | }
91 |
92 | #endregion
93 |
94 | #region InternalMembers
95 |
96 | internal static string GetPieceTypeShort (ChessPieceType pieceType)
97 | {
98 | switch (pieceType)
99 | {
100 | case ChessPieceType.Pawn:
101 | {
102 | return "P";
103 | }
104 | case ChessPieceType.Knight:
105 | {
106 | return "N";
107 | }
108 | case ChessPieceType.Bishop:
109 | {
110 | return "B";
111 | }
112 | case ChessPieceType.Rook:
113 | {
114 | return "R";
115 | }
116 |
117 | case ChessPieceType.Queen:
118 | {
119 | return "Q";
120 | }
121 |
122 | case ChessPieceType.King:
123 | {
124 | return "K";
125 | }
126 | default:
127 | {
128 | return "P";
129 | }
130 | }
131 | }
132 |
133 | internal static short CalculatePieceValue(ChessPieceType pieceType)
134 | {
135 | switch (pieceType)
136 | {
137 | case ChessPieceType.Pawn:
138 | {
139 | return 100;
140 |
141 | }
142 | case ChessPieceType.Knight:
143 | {
144 | return 320;
145 | }
146 | case ChessPieceType.Bishop:
147 | {
148 | return 325;
149 | }
150 | case ChessPieceType.Rook:
151 | {
152 | return 500;
153 | }
154 |
155 | case ChessPieceType.Queen:
156 | {
157 | return 975;
158 | }
159 |
160 | case ChessPieceType.King:
161 | {
162 | return 32767;
163 | }
164 | default:
165 | {
166 | return 0;
167 | }
168 | }
169 | }
170 |
171 | internal static short CalculatePieceActionValue(ChessPieceType pieceType)
172 | {
173 | switch (pieceType)
174 | {
175 | case ChessPieceType.Pawn:
176 | {
177 | return 6;
178 |
179 | }
180 | case ChessPieceType.Knight:
181 | {
182 | return 3;
183 | }
184 | case ChessPieceType.Bishop:
185 | {
186 | return 3;
187 | }
188 | case ChessPieceType.Rook:
189 | {
190 | return 2;
191 | }
192 |
193 | case ChessPieceType.Queen:
194 | {
195 | return 1;
196 | }
197 |
198 | case ChessPieceType.King:
199 | {
200 | return 1;
201 | }
202 | default:
203 | {
204 | return 0;
205 | }
206 | }
207 | }
208 |
209 | #endregion
210 |
211 | public new string ToString()
212 | {
213 | return PieceType + " " + PieceColor + " " + PieceValue + " " + PieceActionValue + " " + ValidMoves.Count + " " + AttackedValue + " " + DefendedValue;
214 |
215 | }
216 |
217 | }
218 | }
--------------------------------------------------------------------------------
/ChessCoreEngine/PieceMoves.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace ChessEngine.Engine
4 | {
5 | internal struct PieceMoveSet
6 | {
7 | internal readonly List Moves;
8 |
9 | internal PieceMoveSet(List moves)
10 | {
11 | Moves = moves;
12 | }
13 | }
14 |
15 | internal struct MoveArrays
16 | {
17 | internal static PieceMoveSet[] BishopMoves1;
18 | internal static byte[] BishopTotalMoves1;
19 |
20 | internal static PieceMoveSet[] BishopMoves2;
21 | internal static byte[] BishopTotalMoves2;
22 |
23 | internal static PieceMoveSet[] BishopMoves3;
24 | internal static byte[] BishopTotalMoves3;
25 |
26 | internal static PieceMoveSet[] BishopMoves4;
27 | internal static byte[] BishopTotalMoves4;
28 |
29 | internal static PieceMoveSet[] BlackPawnMoves;
30 | internal static byte[] BlackPawnTotalMoves;
31 |
32 | internal static PieceMoveSet[] WhitePawnMoves;
33 | internal static byte[] WhitePawnTotalMoves;
34 |
35 | internal static PieceMoveSet[] KnightMoves;
36 | internal static byte[] KnightTotalMoves;
37 |
38 | internal static PieceMoveSet[] QueenMoves1;
39 | internal static byte[] QueenTotalMoves1;
40 | internal static PieceMoveSet[] QueenMoves2;
41 | internal static byte[] QueenTotalMoves2;
42 | internal static PieceMoveSet[] QueenMoves3;
43 | internal static byte[] QueenTotalMoves3;
44 | internal static PieceMoveSet[] QueenMoves4;
45 | internal static byte[] QueenTotalMoves4;
46 | internal static PieceMoveSet[] QueenMoves5;
47 | internal static byte[] QueenTotalMoves5;
48 | internal static PieceMoveSet[] QueenMoves6;
49 | internal static byte[] QueenTotalMoves6;
50 | internal static PieceMoveSet[] QueenMoves7;
51 | internal static byte[] QueenTotalMoves7;
52 | internal static PieceMoveSet[] QueenMoves8;
53 | internal static byte[] QueenTotalMoves8;
54 |
55 | internal static PieceMoveSet[] RookMoves1;
56 | internal static byte[] RookTotalMoves1;
57 | internal static PieceMoveSet[] RookMoves2;
58 | internal static byte[] RookTotalMoves2;
59 | internal static PieceMoveSet[] RookMoves3;
60 | internal static byte[] RookTotalMoves3;
61 | internal static PieceMoveSet[] RookMoves4;
62 | internal static byte[] RookTotalMoves4;
63 |
64 | internal static PieceMoveSet[] KingMoves;
65 | internal static byte[] KingTotalMoves;
66 | }
67 |
68 | internal static class PieceMoves
69 | {
70 | internal static bool Initiated;
71 |
72 | private static byte Position(byte col, byte row)
73 | {
74 | return (byte)(col + (row * 8));
75 | }
76 |
77 | #region IntitiateMotionMethods
78 |
79 | internal static void InitiateChessPieceMotion()
80 | {
81 | if (Initiated)
82 | {
83 | return;
84 | }
85 |
86 | Initiated = true;
87 |
88 | MoveArrays.WhitePawnMoves = new PieceMoveSet[64];
89 | MoveArrays.WhitePawnTotalMoves = new byte[64];
90 |
91 | MoveArrays.BlackPawnMoves = new PieceMoveSet[64];
92 | MoveArrays.BlackPawnTotalMoves = new byte[64];
93 |
94 | MoveArrays.KnightMoves = new PieceMoveSet[64];
95 | MoveArrays.KnightTotalMoves = new byte[64];
96 |
97 | MoveArrays.BishopMoves1 = new PieceMoveSet[64];
98 | MoveArrays.BishopTotalMoves1 = new byte[64];
99 |
100 | MoveArrays.BishopMoves2 = new PieceMoveSet[64];
101 | MoveArrays.BishopTotalMoves2 = new byte[64];
102 |
103 | MoveArrays.BishopMoves3 = new PieceMoveSet[64];
104 | MoveArrays.BishopTotalMoves3 = new byte[64];
105 |
106 | MoveArrays.BishopMoves4 = new PieceMoveSet[64];
107 | MoveArrays.BishopTotalMoves4 = new byte[64];
108 |
109 | MoveArrays.RookMoves1 = new PieceMoveSet[64];
110 | MoveArrays.RookTotalMoves1 = new byte[64];
111 |
112 | MoveArrays.RookMoves2 = new PieceMoveSet[64];
113 | MoveArrays.RookTotalMoves2 = new byte[64];
114 |
115 | MoveArrays.RookMoves3 = new PieceMoveSet[64];
116 | MoveArrays.RookTotalMoves3 = new byte[64];
117 |
118 | MoveArrays.RookMoves4 = new PieceMoveSet[64];
119 | MoveArrays.RookTotalMoves4 = new byte[64];
120 |
121 | MoveArrays.QueenMoves1 = new PieceMoveSet[64];
122 | MoveArrays.QueenTotalMoves1 = new byte[64];
123 |
124 | MoveArrays.QueenMoves2 = new PieceMoveSet[64];
125 | MoveArrays.QueenTotalMoves2 = new byte[64];
126 |
127 | MoveArrays.QueenMoves3 = new PieceMoveSet[64];
128 | MoveArrays.QueenTotalMoves3 = new byte[64];
129 |
130 | MoveArrays.QueenMoves4 = new PieceMoveSet[64];
131 | MoveArrays.QueenTotalMoves4 = new byte[64];
132 |
133 | MoveArrays.QueenMoves5 = new PieceMoveSet[64];
134 | MoveArrays.QueenTotalMoves5 = new byte[64];
135 |
136 | MoveArrays.QueenMoves6 = new PieceMoveSet[64];
137 | MoveArrays.QueenTotalMoves6 = new byte[64];
138 |
139 | MoveArrays.QueenMoves7 = new PieceMoveSet[64];
140 | MoveArrays.QueenTotalMoves7 = new byte[64];
141 |
142 | MoveArrays.QueenMoves8 = new PieceMoveSet[64];
143 | MoveArrays.QueenTotalMoves8 = new byte[64];
144 |
145 | MoveArrays.KingMoves = new PieceMoveSet[64];
146 | MoveArrays.KingTotalMoves = new byte[64];
147 |
148 | SetMovesWhitePawn();
149 | SetMovesBlackPawn();
150 | SetMovesKnight();
151 | SetMovesBishop();
152 | SetMovesRook();
153 | SetMovesQueen();
154 | SetMovesKing();
155 | }
156 |
157 | private static void SetMovesBlackPawn()
158 | {
159 | for (byte index = 8; index <= 55; index++)
160 | {
161 | var moveset = new PieceMoveSet(new List());
162 |
163 | byte x = (byte)(index % 8);
164 | byte y = (byte)((index / 8));
165 |
166 | //Diagonal Kill
167 | if (y < 7 && x < 7)
168 | {
169 | moveset.Moves.Add((byte)(index + 8 + 1));
170 | MoveArrays.BlackPawnTotalMoves[index]++;
171 | }
172 | if (x > 0 && y < 7)
173 | {
174 | moveset.Moves.Add((byte)(index + 8 - 1));
175 | MoveArrays.BlackPawnTotalMoves[index]++;
176 | }
177 |
178 | //One Forward
179 | moveset.Moves.Add((byte)(index + 8));
180 | MoveArrays.BlackPawnTotalMoves[index]++;
181 |
182 | //Starting Position we can jump 2
183 | if (y == 1)
184 | {
185 | moveset.Moves.Add((byte)(index + 16));
186 | MoveArrays.BlackPawnTotalMoves[index]++;
187 | }
188 |
189 | MoveArrays.BlackPawnMoves[index] = moveset;
190 | }
191 | }
192 |
193 | private static void SetMovesWhitePawn()
194 | {
195 | for (byte index = 8; index <= 55; index++)
196 | {
197 | byte x = (byte)(index % 8);
198 | byte y = (byte)((index / 8));
199 |
200 | var moveset = new PieceMoveSet(new List());
201 |
202 | //Diagonal Kill
203 | if (x < 7 && y > 0)
204 | {
205 | moveset.Moves.Add((byte)(index - 8 + 1));
206 | MoveArrays.WhitePawnTotalMoves[index]++;
207 | }
208 | if (x > 0 && y > 0)
209 | {
210 | moveset.Moves.Add((byte)(index - 8 - 1));
211 | MoveArrays.WhitePawnTotalMoves[index]++;
212 | }
213 |
214 | //One Forward
215 | moveset.Moves.Add((byte)(index - 8));
216 | MoveArrays.WhitePawnTotalMoves[index]++;
217 |
218 | //Starting Position we can jump 2
219 | if (y == 6)
220 | {
221 | moveset.Moves.Add((byte)(index - 16));
222 | MoveArrays.WhitePawnTotalMoves[index]++;
223 | }
224 |
225 | MoveArrays.WhitePawnMoves[index] = moveset;
226 | }
227 | }
228 |
229 | private static void SetMovesKnight()
230 | {
231 | for (byte y = 0; y < 8; y++)
232 | {
233 | for (byte x = 0; x < 8; x++)
234 | {
235 | byte index = (byte)(y + (x * 8));
236 |
237 | var moveset = new PieceMoveSet(new List());
238 |
239 | byte move;
240 |
241 | if (y < 6 && x > 0)
242 | {
243 | move = Position((byte)(y + 2), (byte)(x - 1));
244 |
245 | if (move < 64)
246 | {
247 | moveset.Moves.Add(move);
248 | MoveArrays.KnightTotalMoves[index]++;
249 | }
250 | }
251 |
252 | if (y > 1 && x < 7)
253 | {
254 | move = Position((byte)(y - 2), (byte)(x + 1));
255 |
256 | if (move < 64)
257 | {
258 | moveset.Moves.Add(move);
259 | MoveArrays.KnightTotalMoves[index]++;
260 | }
261 | }
262 |
263 | if (y > 1 && x > 0)
264 | {
265 | move = Position((byte)(y - 2), (byte)(x - 1));
266 |
267 | if (move < 64)
268 | {
269 | moveset.Moves.Add(move);
270 | MoveArrays.KnightTotalMoves[index]++;
271 | }
272 | }
273 |
274 | if (y < 6 && x < 7)
275 | {
276 | move = Position((byte)(y + 2), (byte)(x + 1));
277 |
278 | if (move < 64)
279 | {
280 | moveset.Moves.Add(move);
281 | MoveArrays.KnightTotalMoves[index]++;
282 | }
283 | }
284 |
285 | if (y > 0 && x < 6)
286 | {
287 | move = Position((byte)(y - 1), (byte)(x + 2));
288 |
289 | if (move < 64)
290 | {
291 | moveset.Moves.Add(move);
292 | MoveArrays.KnightTotalMoves[index]++;
293 | }
294 | }
295 |
296 | if (y < 7 && x > 1)
297 | {
298 | move = Position((byte)(y + 1), (byte)(x - 2));
299 |
300 | if (move < 64)
301 | {
302 | moveset.Moves.Add(move);
303 | MoveArrays.KnightTotalMoves[index]++;
304 | }
305 | }
306 |
307 | if (y > 0 && x > 1)
308 | {
309 | move = Position((byte)(y - 1), (byte)(x - 2));
310 |
311 | if (move < 64)
312 | {
313 | moveset.Moves.Add(move);
314 | MoveArrays.KnightTotalMoves[index]++;
315 | }
316 | }
317 |
318 | if (y < 7 && x < 6)
319 | {
320 | move = Position((byte)(y + 1), (byte)(x + 2));
321 |
322 | if (move < 64)
323 | {
324 | moveset.Moves.Add(move);
325 | MoveArrays.KnightTotalMoves[index]++;
326 | }
327 | }
328 |
329 | MoveArrays.KnightMoves[index] = moveset;
330 | }
331 | }
332 | }
333 |
334 | private static void SetMovesBishop()
335 | {
336 | for (byte y = 0; y < 8; y++)
337 | {
338 | for (byte x = 0; x < 8; x++)
339 | {
340 | byte index = (byte)(y + (x * 8));
341 |
342 | var moveset = new PieceMoveSet(new List());
343 | byte move;
344 |
345 | byte row = x;
346 | byte col = y;
347 |
348 | while (row < 7 && col < 7)
349 | {
350 | row++;
351 | col++;
352 |
353 | move = Position(col, row);
354 | moveset.Moves.Add(move);
355 | MoveArrays.BishopTotalMoves1[index]++;
356 | }
357 |
358 | MoveArrays.BishopMoves1[index] = moveset;
359 | moveset = new PieceMoveSet(new List());
360 |
361 | row = x;
362 | col = y;
363 |
364 | while (row < 7 && col > 0)
365 | {
366 | row++;
367 | col--;
368 |
369 | move = Position(col, row);
370 | moveset.Moves.Add(move);
371 | MoveArrays.BishopTotalMoves2[index]++;
372 | }
373 |
374 | MoveArrays.BishopMoves2[index] = moveset;
375 | moveset = new PieceMoveSet(new List());
376 |
377 | row = x;
378 | col = y;
379 |
380 | while (row > 0 && col < 7)
381 | {
382 | row--;
383 | col++;
384 |
385 | move = Position(col, row);
386 | moveset.Moves.Add(move);
387 | MoveArrays.BishopTotalMoves3[index]++;
388 | }
389 |
390 | MoveArrays.BishopMoves3[index] = moveset;
391 | moveset = new PieceMoveSet(new List());
392 |
393 | row = x;
394 | col = y;
395 |
396 | while (row > 0 && col > 0)
397 | {
398 | row--;
399 | col--;
400 |
401 | move = Position(col, row);
402 | moveset.Moves.Add(move);
403 | MoveArrays.BishopTotalMoves4[index]++;
404 | }
405 |
406 | MoveArrays.BishopMoves4[index] = moveset;
407 | }
408 | }
409 | }
410 |
411 | private static void SetMovesRook()
412 | {
413 | for (byte y = 0; y < 8; y++)
414 | {
415 | for (byte x = 0; x < 8; x++)
416 | {
417 | byte index = (byte)(y + (x * 8));
418 |
419 | var moveset = new PieceMoveSet(new List());
420 | byte move;
421 |
422 | byte row = x;
423 | byte col = y;
424 |
425 | while (row < 7)
426 | {
427 | row++;
428 |
429 | move = Position(col, row);
430 | moveset.Moves.Add(move);
431 | MoveArrays.RookTotalMoves1[index]++;
432 | }
433 |
434 | MoveArrays.RookMoves1[index] = moveset;
435 |
436 | moveset = new PieceMoveSet(new List());
437 | row = x;
438 | col = y;
439 |
440 | while (row > 0)
441 | {
442 | row--;
443 |
444 | move = Position(col, row);
445 | moveset.Moves.Add(move);
446 | MoveArrays.RookTotalMoves2[index]++;
447 | }
448 |
449 | MoveArrays.RookMoves2[index] = moveset;
450 |
451 | moveset = new PieceMoveSet(new List());
452 | row = x;
453 | col = y;
454 |
455 | while (col > 0)
456 | {
457 | col--;
458 |
459 | move = Position(col, row);
460 | moveset.Moves.Add(move);
461 | MoveArrays.RookTotalMoves3[index]++;
462 | }
463 |
464 | MoveArrays.RookMoves3[index] = moveset;
465 |
466 | moveset = new PieceMoveSet(new List());
467 | row = x;
468 | col = y;
469 |
470 | while (col < 7)
471 | {
472 | col++;
473 |
474 | move = Position(col, row);
475 | moveset.Moves.Add(move);
476 | MoveArrays.RookTotalMoves4[index]++;
477 | }
478 |
479 | MoveArrays.RookMoves4[index] = moveset;
480 | }
481 | }
482 | }
483 |
484 | private static void SetMovesQueen()
485 | {
486 | for (byte y = 0; y < 8; y++)
487 | {
488 | for (byte x = 0; x < 8; x++)
489 | {
490 | byte index = (byte)(y + (x * 8));
491 |
492 | var moveset = new PieceMoveSet(new List());
493 | byte move;
494 |
495 | byte row = x;
496 | byte col = y;
497 |
498 | while (row < 7)
499 | {
500 | row++;
501 |
502 | move = Position(col, row);
503 | moveset.Moves.Add(move);
504 | MoveArrays.QueenTotalMoves1[index]++;
505 | }
506 |
507 | MoveArrays.QueenMoves1[index] = moveset;
508 |
509 | moveset = new PieceMoveSet(new List());
510 | row = x;
511 | col = y;
512 |
513 | while (row > 0)
514 | {
515 | row--;
516 |
517 | move = Position(col, row);
518 | moveset.Moves.Add(move);
519 | MoveArrays.QueenTotalMoves2[index]++;
520 | }
521 |
522 | MoveArrays.QueenMoves2[index] = moveset;
523 |
524 | moveset = new PieceMoveSet(new List());
525 | row = x;
526 | col = y;
527 |
528 | while (col > 0)
529 | {
530 | col--;
531 |
532 | move = Position(col, row);
533 | moveset.Moves.Add(move);
534 | MoveArrays.QueenTotalMoves3[index]++;
535 | }
536 |
537 | MoveArrays.QueenMoves3[index] = moveset;
538 |
539 | moveset = new PieceMoveSet(new List());
540 | row = x;
541 | col = y;
542 |
543 | while (col < 7)
544 | {
545 | col++;
546 |
547 | move = Position(col, row);
548 | moveset.Moves.Add(move);
549 | MoveArrays.QueenTotalMoves4[index]++;
550 | }
551 |
552 | MoveArrays.QueenMoves4[index] = moveset;
553 |
554 | moveset = new PieceMoveSet(new List());
555 | row = x;
556 | col = y;
557 |
558 | while (row < 7 && col < 7)
559 | {
560 | row++;
561 | col++;
562 |
563 | move = Position(col, row);
564 | moveset.Moves.Add(move);
565 | MoveArrays.QueenTotalMoves5[index]++;
566 | }
567 |
568 | MoveArrays.QueenMoves5[index] = moveset;
569 |
570 | moveset = new PieceMoveSet(new List());
571 | row = x;
572 | col = y;
573 |
574 | while (row < 7 && col > 0)
575 | {
576 | row++;
577 | col--;
578 |
579 | move = Position(col, row);
580 | moveset.Moves.Add(move);
581 | MoveArrays.QueenTotalMoves6[index]++;
582 | }
583 |
584 | MoveArrays.QueenMoves6[index] = moveset;
585 |
586 | moveset = new PieceMoveSet(new List());
587 | row = x;
588 | col = y;
589 |
590 | while (row > 0 && col < 7)
591 | {
592 | row--;
593 | col++;
594 |
595 | move = Position(col, row);
596 | moveset.Moves.Add(move);
597 | MoveArrays.QueenTotalMoves7[index]++;
598 | }
599 |
600 | MoveArrays.QueenMoves7[index] = moveset;
601 |
602 | moveset = new PieceMoveSet(new List());
603 | row = x;
604 | col = y;
605 |
606 | while (row > 0 && col > 0)
607 | {
608 | row--;
609 | col--;
610 |
611 | move = Position(col, row);
612 | moveset.Moves.Add(move);
613 | MoveArrays.QueenTotalMoves8[index]++;
614 | }
615 |
616 | MoveArrays.QueenMoves8[index] = moveset;
617 | }
618 | }
619 | }
620 |
621 | private static void SetMovesKing()
622 | {
623 | for (byte y = 0; y < 8; y++)
624 | {
625 | for (byte x = 0; x < 8; x++)
626 | {
627 | byte index = (byte)(y + (x * 8));
628 |
629 | var moveset = new PieceMoveSet(new List());
630 | byte move;
631 |
632 | byte row = x;
633 | byte col = y;
634 |
635 | if (row < 7)
636 | {
637 | row++;
638 |
639 | move = Position(col, row);
640 | moveset.Moves.Add(move);
641 | MoveArrays.KingTotalMoves[index]++;
642 | }
643 |
644 | row = x;
645 | col = y;
646 |
647 | if (row > 0)
648 | {
649 | row--;
650 |
651 | move = Position(col, row);
652 | moveset.Moves.Add(move);
653 | MoveArrays.KingTotalMoves[index]++;
654 | }
655 |
656 | row = x;
657 | col = y;
658 |
659 | if (col > 0)
660 | {
661 | col--;
662 |
663 | move = Position(col, row);
664 | moveset.Moves.Add(move);
665 | MoveArrays.KingTotalMoves[index]++;
666 | }
667 |
668 | row = x;
669 | col = y;
670 |
671 | if (col < 7)
672 | {
673 | col++;
674 |
675 | move = Position(col, row);
676 | moveset.Moves.Add(move);
677 | MoveArrays.KingTotalMoves[index]++;
678 | }
679 |
680 | row = x;
681 | col = y;
682 |
683 | if (row < 7 && col < 7)
684 | {
685 | row++;
686 | col++;
687 |
688 | move = Position(col, row);
689 | moveset.Moves.Add(move);
690 | MoveArrays.KingTotalMoves[index]++;
691 | }
692 |
693 | row = x;
694 | col = y;
695 |
696 | if (row < 7 && col > 0)
697 | {
698 | row++;
699 | col--;
700 |
701 | move = Position(col, row);
702 | moveset.Moves.Add(move);
703 | MoveArrays.KingTotalMoves[index]++;
704 | }
705 |
706 | row = x;
707 | col = y;
708 |
709 | if (row > 0 && col < 7)
710 | {
711 | row--;
712 | col++;
713 |
714 | move = Position(col, row);
715 | moveset.Moves.Add(move);
716 | MoveArrays.KingTotalMoves[index]++;
717 | }
718 |
719 |
720 | row = x;
721 | col = y;
722 |
723 | if (row > 0 && col > 0)
724 | {
725 | row--;
726 | col--;
727 |
728 | move = Position(col, row);
729 | moveset.Moves.Add(move);
730 | MoveArrays.KingTotalMoves[index]++;
731 | }
732 |
733 | MoveArrays.KingMoves[index] = moveset;
734 | }
735 | }
736 | }
737 |
738 | #endregion
739 | }
740 | }
--------------------------------------------------------------------------------
/ChessCoreEngine/PieceSquareTable.cs:
--------------------------------------------------------------------------------
1 | namespace ChessEngine.Engine
2 | {
3 | internal static class PieceSquareTable
4 | {
5 | private static readonly short[] BishopTable = new short[]
6 | {
7 | -40, -20, -20, -20, -20, -20, -20, -40 ,
8 | -20, 0, 0, 0, 0, 0, 0, -20 ,
9 | -20, 0, 10, 20, 20, 10, 0, -20 ,
10 | -20, 10, 10, 20, 20, 10, 10, -20 ,
11 | -20, 0, 20, 20, 20, 20, 0, -20 ,
12 | -20, 20, 20, 20, 20, 20, 20, -20 ,
13 | -20, 10, 0, 0, 0, 0, 10, -20 ,
14 | -40, -20, -20, -20, -20, -20, -20, -40
15 | };
16 |
17 | private static readonly short[] KingEndGameTable = new short[]
18 | {
19 | -175,-175,-175,-175,-175,-175,-175,-175,
20 | -175, -50, -50, -50, -50, -50, -50,-175,
21 | -175, -50, 50, 50, 50, 50, -50,-175,
22 | -175, -50, 50, 150, 150, 50, -50,-175,
23 | -175, -50, 50, 100, 100, 50, -50,-175,
24 | -175, -50, 50, 50, 50, 50, -50,-175,
25 | -175, -50, -50, -50, -50, -50, -50,-175,
26 | -175,-175,-175,-175,-175,-175,-175,-175
27 | };
28 |
29 | private static readonly short[] KingMiddleGameTable = new short[]
30 | {
31 | -60, -80, -80, -2, -20, -80, -80, -60 ,
32 | -60, -80, -80, -2, -20, -80, -80, -60 ,
33 | -60, -80, -80, -2, -20, -80, -80, -60 ,
34 | -60, -80, -80, -2, -20, -80, -80, -60 ,
35 | -40, -60, -60, -8, -80, -60, -60, -40 ,
36 | -20, -40, -40, -40,-40, -40, -40, -20 ,
37 | 40, 40, 0, 0, 0, 0, 40, 40 ,
38 | 40, 60, 20, 0, 0, 20, 60, 40
39 | };
40 |
41 | private static readonly short[] KnightTable = new short[]
42 | {
43 | -20, -80, -60, -60, -60, -60, -80, -20 ,
44 | -80, -40, 0, 0, 0, 0, -40, -80 ,
45 | -60, 0, 20, 30, 30, 20, 0, -60 ,
46 | -60, 10, 30, 40, 40, 30, 10, -60 ,
47 | -60, 0, 30, 40, 40, 30, 0, -60 ,
48 | -60, 10, 20, 30, 30, 30, 1, -60 ,
49 | -80, -40, 0, 10, 10, 0, -4, -80 ,
50 | -20, -80, -60, -60, -60, -60, -80, -20 ,
51 | };
52 |
53 | private static readonly short[] PawnTable = new short[]
54 | {
55 | 9000,9000,9000,9000,9000,9000,9000,9000 ,
56 | 200, 200, 200, 200, 200, 200, 200, 200 ,
57 | 100, 100, 100, 100, 100, 100, 100, 100 ,
58 | 40, 40, 90, 100, 100, 90, 40, 40 ,
59 | 20, 20, 20, 100, 150, 20, 20, 20 ,
60 | 2, 4, 0, 15, 4, 0, 4, 2 ,
61 | -10, -10, -10, -20, -35, -10, -10, -10 ,
62 | 0, 0, 0, 0, 0, 0, 0, 0
63 | };
64 |
65 | private static readonly short[] PawnTableEndGame = new short[]
66 | {
67 | 9000,9000,9000,9000,9000,9000,9000,9000 ,
68 | 500, 500, 500, 500, 500, 500, 500, 500 ,
69 | 300, 300, 300, 300, 300, 300, 300, 300 ,
70 | 90, 90, 90, 100, 100, 90, 90, 90 ,
71 | 70, 70, 70, 85, 85, 70, 70, 70 ,
72 | 20, 20, 20, 20, 20, 20, 20, 20 ,
73 | -10, -10, -10, -10, -10, -10, -10, -10 ,
74 | 0, 0, 0, 0, 0, 0, 0, 0
75 | };
76 |
77 | private static readonly short[] QueenTable = new short[]
78 | {
79 | -40, -20, -20, -10, -10, -20, -20, -40 ,
80 | -20, 0, 0, 0, 0, 0, 0, -20 ,
81 | -20, 0, 10, 10, 10, 10, 0, -20 ,
82 | -10, 0, 10, 10, 10, 10, 0, -10 ,
83 | 0, 0, 10, 10, 10, 10, 0, -10 ,
84 | -20, 10, 10, 10, 10, 10, 0, -20 ,
85 | -20, 0, 10, 0, 0, 0, 0, -20 ,
86 | -40, -20, -20, -10, -10, -20, -20, -40
87 | };
88 |
89 | private static readonly short[] RookTable = new short[]
90 | {
91 | 0, 0, 0, 0, 0, 0, 0, 0 ,
92 | 10, 20, 20, 20, 20, 20, 20, 10 ,
93 | -10, 0, 0, 0, 0, 0, 0, -10 ,
94 | -10, 0, 0, 0, 0, 0, 0, -10 ,
95 | -10, 0, 0, 0, 0, 0, 0, -10 ,
96 | -10, 0, 0, 0, 0, 0, 0, -10 ,
97 | -10, 0, 0, 0, 0, 0, 0, -10 ,
98 | -30, 30, 40, 10, 10, 0, 0, -30
99 | };
100 |
101 | internal static int EvaluatePiecePosition( ChessPieceType PieceType,
102 | ChessPieceColor PieceColor,
103 | byte position, bool endGame )
104 | {
105 | switch (PieceColor)
106 | {
107 | case ChessPieceColor.White:
108 | if (PieceType == ChessPieceType.Pawn)
109 | {
110 | if (endGame)
111 | {
112 | return PawnTableEndGame[position];
113 | }
114 |
115 | return PawnTable[position];
116 | }
117 | if (PieceType == ChessPieceType.Knight)
118 | {
119 | return KnightTable[position];
120 | }
121 | if (PieceType == ChessPieceType.Bishop)
122 | {
123 | return BishopTable[position];
124 | }
125 | if (PieceType == ChessPieceType.Rook)
126 | {
127 | return RookTable[position];
128 | }
129 | if (PieceType == ChessPieceType.Queen)
130 | {
131 | return QueenTable[position];
132 | }
133 | if (PieceType == ChessPieceType.King)
134 | {
135 | if (endGame)
136 | {
137 | return KingEndGameTable[position];
138 | }
139 |
140 | return KingMiddleGameTable[position];
141 | }
142 | break;
143 | case ChessPieceColor.Black:
144 |
145 | byte index = (byte)(((byte)(position + 56)) - (byte)((byte)(position / 8) * 16));
146 |
147 | if (PieceType == ChessPieceType.Pawn)
148 | {
149 | if (endGame)
150 | {
151 | return PawnTableEndGame[index];
152 | }
153 |
154 | return PawnTable[index];
155 | }
156 | if (PieceType == ChessPieceType.Knight)
157 | {
158 | return KnightTable[index];
159 | }
160 | if (PieceType == ChessPieceType.Bishop)
161 | {
162 | return BishopTable[index];
163 | }
164 | if (PieceType == ChessPieceType.Rook)
165 | {
166 | return RookTable[index];
167 | }
168 | if (PieceType == ChessPieceType.Queen)
169 | {
170 | return QueenTable[index];
171 | }
172 | if (PieceType == ChessPieceType.King)
173 | {
174 | if (endGame)
175 | {
176 | return KingEndGameTable[index];
177 | }
178 |
179 | return KingMiddleGameTable[index];
180 | }
181 | break;
182 | }
183 |
184 | return 0;
185 | }
186 |
187 |
188 |
189 | internal static int EvaluatePawnWhitePosition(byte position, bool endGame)
190 | {
191 | if (endGame)
192 | {
193 | return PawnTableEndGame[position];
194 | }
195 |
196 | return PawnTable[position];
197 | }
198 |
199 | internal static int EvaluatePawnBlackPosition(byte position, bool endGame)
200 | {
201 | byte index = (byte)(((byte)(position + 56)) - (byte)((byte)(position / 8) * 16));
202 |
203 | if (endGame)
204 | {
205 | return PawnTableEndGame[index];
206 | }
207 |
208 | return PawnTable[index];
209 | }
210 | }
211 | }
--------------------------------------------------------------------------------
/ChessCoreEngine/PieceValidMoves.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace ChessEngine.Engine
4 | {
5 | internal static class PieceValidMoves
6 | {
7 | private static void AnalyzeMovePawn(Board board, byte dstPos, Piece pcMoving)
8 | {
9 | //Because Pawns only kill diagonaly we handle the En Passant scenario specialy
10 | if (board.EnPassantPosition > 0)
11 | {
12 | if (pcMoving.PieceColor != board.EnPassantColor)
13 | {
14 | if (board.EnPassantPosition == dstPos)
15 | {
16 | //We have an En Passant Possible
17 | pcMoving.ValidMoves.Push(dstPos);
18 |
19 | if (pcMoving.PieceColor == ChessPieceColor.White)
20 | {
21 | board.WhiteAttackBoard[dstPos] = true;
22 | }
23 | else
24 | {
25 | board.BlackAttackBoard[dstPos] = true;
26 | }
27 | }
28 | }
29 | }
30 |
31 | Piece pcAttacked = board.Squares[dstPos].Piece;
32 |
33 | //If there no piece there I can potentialy kill
34 | if (pcAttacked == null)
35 | return;
36 |
37 | //Regardless of what is there I am attacking this square
38 | if (pcMoving.PieceColor == ChessPieceColor.White)
39 | {
40 | board.WhiteAttackBoard[dstPos] = true;
41 |
42 | //if that piece is the same color
43 | if (pcAttacked.PieceColor == pcMoving.PieceColor)
44 | {
45 | pcAttacked.DefendedValue += pcMoving.PieceActionValue;
46 | return;
47 | }
48 |
49 | pcAttacked.AttackedValue += pcMoving.PieceActionValue;
50 |
51 | //If this is a king set it in check
52 | if (pcAttacked.PieceType == ChessPieceType.King)
53 | {
54 | board.BlackCheck = true;
55 | }
56 | else
57 | {
58 | //Add this as a valid move
59 | pcMoving.ValidMoves.Push(dstPos);
60 | }
61 | }
62 | else
63 | {
64 | board.BlackAttackBoard[dstPos] = true;
65 |
66 | //if that piece is the same color
67 | if (pcAttacked.PieceColor == pcMoving.PieceColor)
68 | {
69 | pcAttacked.DefendedValue += pcMoving.PieceActionValue;
70 | return;
71 | }
72 |
73 | pcAttacked.AttackedValue += pcMoving.PieceActionValue;
74 |
75 | //If this is a king set it in check
76 | if (pcAttacked.PieceType == ChessPieceType.King)
77 | {
78 | board.WhiteCheck = true;
79 | }
80 | else
81 | {
82 | //Add this as a valid move
83 | pcMoving.ValidMoves.Push(dstPos);
84 | }
85 | }
86 |
87 | return;
88 | }
89 |
90 | private static bool AnalyzeMove(Board board, byte dstPos, Piece pcMoving)
91 | {
92 | //If I am not a pawn everywhere I move I can attack
93 | if (pcMoving.PieceColor == ChessPieceColor.White)
94 | {
95 | board.WhiteAttackBoard[dstPos] = true;
96 | }
97 | else
98 | {
99 | board.BlackAttackBoard[dstPos] = true;
100 | }
101 |
102 | //If there no piece there I can potentialy kill just add the move and exit
103 | if (board.Squares[dstPos].Piece == null)
104 | {
105 | pcMoving.ValidMoves.Push(dstPos);
106 |
107 | return true;
108 | }
109 |
110 | Piece pcAttacked = board.Squares[dstPos].Piece;
111 |
112 | //if that piece is a different color
113 | if (pcAttacked.PieceColor != pcMoving.PieceColor)
114 | {
115 | pcAttacked.AttackedValue += pcMoving.PieceActionValue;
116 |
117 | //If this is a king set it in check
118 | if (pcAttacked.PieceType == ChessPieceType.King)
119 | {
120 | if (pcAttacked.PieceColor == ChessPieceColor.Black)
121 | {
122 | board.BlackCheck = true;
123 | }
124 | else
125 | {
126 | board.WhiteCheck = true;
127 | }
128 | }
129 | else
130 | {
131 | //Add this as a valid move
132 | pcMoving.ValidMoves.Push(dstPos);
133 | }
134 |
135 |
136 | //We don't continue movement past this piece
137 | return false;
138 | }
139 | //Same Color I am defending
140 | pcAttacked.DefendedValue += pcMoving.PieceActionValue;
141 |
142 | //Since this piece is of my kind I can't move there
143 | return false;
144 | }
145 |
146 | private static void CheckValidMovesPawn(List moves, Piece pcMoving, byte srcPosition,
147 | Board board, byte count)
148 | {
149 | for (byte i = 0; i < count; i++)
150 | {
151 | byte dstPos = moves[i];
152 |
153 | //Diagonal
154 | if (dstPos%8 != srcPosition%8)
155 | {
156 | //If there is a piece there I can potentialy kill
157 | AnalyzeMovePawn(board, dstPos, pcMoving);
158 |
159 | if (pcMoving.PieceColor == ChessPieceColor.White)
160 | {
161 | board.WhiteAttackBoard[dstPos] = true;
162 | }
163 | else
164 | {
165 | board.BlackAttackBoard[dstPos] = true;
166 | }
167 | }
168 | // if there is something if front pawns can't move there
169 | else if (board.Squares[dstPos].Piece != null)
170 | {
171 | return;
172 | }
173 | //if there is nothing in front of
174 | else
175 | {
176 | pcMoving.ValidMoves.Push(dstPos);
177 | }
178 | }
179 | }
180 |
181 | private static void GenerateValidMovesKing(Piece piece, Board board, byte srcPosition)
182 | {
183 | if (piece == null)
184 | {
185 | return;
186 | }
187 |
188 | for (byte i = 0; i < MoveArrays.KingTotalMoves[srcPosition]; i++)
189 | {
190 | byte dstPos = MoveArrays.KingMoves[srcPosition].Moves[i];
191 |
192 | if (piece.PieceColor == ChessPieceColor.White)
193 | {
194 | //I can't move where I am being attacked
195 | if (board.BlackAttackBoard[dstPos])
196 | {
197 | board.WhiteAttackBoard[dstPos] = true;
198 | continue;
199 | }
200 | }
201 | else
202 | {
203 | if (board.WhiteAttackBoard[dstPos])
204 | {
205 | board.BlackAttackBoard[dstPos] = true;
206 | continue;
207 | }
208 | }
209 |
210 | AnalyzeMove(board, dstPos, piece);
211 | }
212 | }
213 |
214 | private static void GenerateValidMovesKingCastle(Board board, Piece king)
215 | {
216 | //This code will add the castleling move to the pieces available moves
217 | if (king.PieceColor == ChessPieceColor.White)
218 | {
219 | if (board.Squares[63].Piece != null)
220 | {
221 | //Check if the Right Rook is still in the correct position
222 | if (board.Squares[63].Piece.PieceType == ChessPieceType.Rook)
223 | {
224 | if (board.Squares[63].Piece.PieceColor == king.PieceColor)
225 | {
226 | //Move one column to right see if its empty
227 | if (board.Squares[62].Piece == null)
228 | {
229 | if (board.Squares[61].Piece == null)
230 | {
231 | if (board.BlackAttackBoard[61] == false &&
232 | board.BlackAttackBoard[62] == false)
233 | {
234 | //Ok looks like move is valid lets add it
235 | king.ValidMoves.Push(62);
236 | board.WhiteAttackBoard[62] = true;
237 | }
238 | }
239 | }
240 | }
241 | }
242 | }
243 |
244 | if (board.Squares[56].Piece != null)
245 | {
246 | //Check if the Left Rook is still in the correct position
247 | if (board.Squares[56].Piece.PieceType == ChessPieceType.Rook)
248 | {
249 | if (board.Squares[56].Piece.PieceColor == king.PieceColor)
250 | {
251 | //Move one column to right see if its empty
252 | if (board.Squares[57].Piece == null)
253 | {
254 | if (board.Squares[58].Piece == null)
255 | {
256 | if (board.Squares[59].Piece == null)
257 | {
258 | if (board.BlackAttackBoard[58] == false &&
259 | board.BlackAttackBoard[59] == false)
260 | {
261 | //Ok looks like move is valid lets add it
262 | king.ValidMoves.Push(58);
263 | board.WhiteAttackBoard[58] = true;
264 | }
265 | }
266 | }
267 | }
268 | }
269 | }
270 | }
271 | }
272 | else if (king.PieceColor == ChessPieceColor.Black)
273 | {
274 | //There are two ways to castle, scenario 1:
275 | if (board.Squares[7].Piece != null)
276 | {
277 | //Check if the Right Rook is still in the correct position
278 | if (board.Squares[7].Piece.PieceType == ChessPieceType.Rook
279 | && !board.Squares[7].Piece.Moved)
280 | {
281 | if (board.Squares[7].Piece.PieceColor == king.PieceColor)
282 | {
283 | //Move one column to right see if its empty
284 |
285 | if (board.Squares[6].Piece == null)
286 | {
287 | if (board.Squares[5].Piece == null)
288 | {
289 | if (board.WhiteAttackBoard[5] == false && board.WhiteAttackBoard[6] == false)
290 | {
291 | //Ok looks like move is valid lets add it
292 | king.ValidMoves.Push(6);
293 | board.BlackAttackBoard[6] = true;
294 | }
295 | }
296 | }
297 | }
298 | }
299 | }
300 | //There are two ways to castle, scenario 2:
301 | if (board.Squares[0].Piece != null)
302 | {
303 | //Check if the Left Rook is still in the correct position
304 | if (board.Squares[0].Piece.PieceType == ChessPieceType.Rook &&
305 | !board.Squares[0].Piece.Moved)
306 | {
307 | if (board.Squares[0].Piece.PieceColor ==
308 | king.PieceColor)
309 | {
310 | //Move one column to right see if its empty
311 | if (board.Squares[1].Piece == null)
312 | {
313 | if (board.Squares[2].Piece == null)
314 | {
315 | if (board.Squares[3].Piece == null)
316 | {
317 | if (board.WhiteAttackBoard[2] == false &&
318 | board.WhiteAttackBoard[3] == false)
319 | {
320 | //Ok looks like move is valid lets add it
321 | king.ValidMoves.Push(2);
322 | board.BlackAttackBoard[2] = true;
323 | }
324 | }
325 | }
326 | }
327 | }
328 | }
329 | }
330 | }
331 | }
332 |
333 | internal static void GenerateValidMoves(Board board)
334 | {
335 | // Reset Board
336 | board.BlackCheck = false;
337 | board.WhiteCheck = false;
338 |
339 | byte blackRooksMoved = 0;
340 | byte whiteRooksMoved = 0;
341 |
342 | //Calculate Remaining Material on Board to make the End Game Decision
343 | int remainingPieces = 0;
344 |
345 | //Generate Moves
346 | for (byte x = 0; x < 64; x++)
347 | {
348 | Square sqr = board.Squares[x];
349 |
350 | if (sqr.Piece == null)
351 | continue;
352 |
353 | sqr.Piece.ValidMoves = new Stack(sqr.Piece.LastValidMoveCount);
354 |
355 | remainingPieces++;
356 |
357 | switch (sqr.Piece.PieceType)
358 | {
359 | case ChessPieceType.Pawn:
360 | {
361 | if (sqr.Piece.PieceColor == ChessPieceColor.White)
362 | {
363 | CheckValidMovesPawn(MoveArrays.WhitePawnMoves[x].Moves, sqr.Piece, x,
364 | board, MoveArrays.WhitePawnTotalMoves[x]);
365 | break;
366 | }
367 | if (sqr.Piece.PieceColor == ChessPieceColor.Black)
368 | {
369 | CheckValidMovesPawn(MoveArrays.BlackPawnMoves[x].Moves, sqr.Piece, x,
370 | board, MoveArrays.BlackPawnTotalMoves[x]);
371 | break;
372 | }
373 |
374 | break;
375 | }
376 | case ChessPieceType.Knight:
377 | {
378 | for (byte i = 0; i < MoveArrays.KnightTotalMoves[x]; i++)
379 | {
380 | AnalyzeMove(board, MoveArrays.KnightMoves[x].Moves[i], sqr.Piece);
381 | }
382 |
383 | break;
384 | }
385 | case ChessPieceType.Bishop:
386 | {
387 | for (byte i = 0; i < MoveArrays.BishopTotalMoves1[x]; i++)
388 | {
389 | if (
390 | AnalyzeMove(board, MoveArrays.BishopMoves1[x].Moves[i],
391 | sqr.Piece) ==
392 | false)
393 | {
394 | break;
395 | }
396 | }
397 | for (byte i = 0; i < MoveArrays.BishopTotalMoves2[x]; i++)
398 | {
399 | if (
400 | AnalyzeMove(board, MoveArrays.BishopMoves2[x].Moves[i],
401 | sqr.Piece) ==
402 | false)
403 | {
404 | break;
405 | }
406 | }
407 | for (byte i = 0; i < MoveArrays.BishopTotalMoves3[x]; i++)
408 | {
409 | if (
410 | AnalyzeMove(board, MoveArrays.BishopMoves3[x].Moves[i],
411 | sqr.Piece) ==
412 | false)
413 | {
414 | break;
415 | }
416 | }
417 | for (byte i = 0; i < MoveArrays.BishopTotalMoves4[x]; i++)
418 | {
419 | if (
420 | AnalyzeMove(board, MoveArrays.BishopMoves4[x].Moves[i],
421 | sqr.Piece) ==
422 | false)
423 | {
424 | break;
425 | }
426 | }
427 |
428 | break;
429 | }
430 | case ChessPieceType.Rook:
431 | {
432 | if (sqr.Piece.Moved)
433 | {
434 | if (sqr.Piece.PieceColor == ChessPieceColor.Black)
435 | {
436 | blackRooksMoved++;
437 | }
438 | else
439 | {
440 | whiteRooksMoved++;
441 | }
442 | }
443 |
444 |
445 | for (byte i = 0; i < MoveArrays.RookTotalMoves1[x]; i++)
446 | {
447 | if (
448 | AnalyzeMove(board, MoveArrays.RookMoves1[x].Moves[i], sqr.Piece) ==
449 | false)
450 | {
451 | break;
452 | }
453 | }
454 | for (byte i = 0; i < MoveArrays.RookTotalMoves2[x]; i++)
455 | {
456 | if (
457 | AnalyzeMove(board, MoveArrays.RookMoves2[x].Moves[i], sqr.Piece) ==
458 | false)
459 | {
460 | break;
461 | }
462 | }
463 | for (byte i = 0; i < MoveArrays.RookTotalMoves3[x]; i++)
464 | {
465 | if (
466 | AnalyzeMove(board, MoveArrays.RookMoves3[x].Moves[i], sqr.Piece) ==
467 | false)
468 | {
469 | break;
470 | }
471 | }
472 | for (byte i = 0; i < MoveArrays.RookTotalMoves4[x]; i++)
473 | {
474 | if (
475 | AnalyzeMove(board, MoveArrays.RookMoves4[x].Moves[i], sqr.Piece) ==
476 | false)
477 | {
478 | break;
479 | }
480 | }
481 |
482 | break;
483 | }
484 | case ChessPieceType.Queen:
485 | {
486 | for (byte i = 0; i < MoveArrays.QueenTotalMoves1[x]; i++)
487 | {
488 | if (
489 | AnalyzeMove(board, MoveArrays.QueenMoves1[x].Moves[i], sqr.Piece) ==
490 | false)
491 | {
492 | break;
493 | }
494 | }
495 | for (byte i = 0; i < MoveArrays.QueenTotalMoves2[x]; i++)
496 | {
497 | if (
498 | AnalyzeMove(board, MoveArrays.QueenMoves2[x].Moves[i], sqr.Piece) ==
499 | false)
500 | {
501 | break;
502 | }
503 | }
504 | for (byte i = 0; i < MoveArrays.QueenTotalMoves3[x]; i++)
505 | {
506 | if (
507 | AnalyzeMove(board, MoveArrays.QueenMoves3[x].Moves[i], sqr.Piece) ==
508 | false)
509 | {
510 | break;
511 | }
512 | }
513 | for (byte i = 0; i < MoveArrays.QueenTotalMoves4[x]; i++)
514 | {
515 | if (
516 | AnalyzeMove(board, MoveArrays.QueenMoves4[x].Moves[i], sqr.Piece) ==
517 | false)
518 | {
519 | break;
520 | }
521 | }
522 |
523 | for (byte i = 0; i < MoveArrays.QueenTotalMoves5[x]; i++)
524 | {
525 | if (
526 | AnalyzeMove(board, MoveArrays.QueenMoves5[x].Moves[i], sqr.Piece) ==
527 | false)
528 | {
529 | break;
530 | }
531 | }
532 | for (byte i = 0; i < MoveArrays.QueenTotalMoves6[x]; i++)
533 | {
534 | if (
535 | AnalyzeMove(board, MoveArrays.QueenMoves6[x].Moves[i], sqr.Piece) ==
536 | false)
537 | {
538 | break;
539 | }
540 | }
541 | for (byte i = 0; i < MoveArrays.QueenTotalMoves7[x]; i++)
542 | {
543 | if (
544 | AnalyzeMove(board, MoveArrays.QueenMoves7[x].Moves[i], sqr.Piece) ==
545 | false)
546 | {
547 | break;
548 | }
549 | }
550 | for (byte i = 0; i < MoveArrays.QueenTotalMoves8[x]; i++)
551 | {
552 | if (
553 | AnalyzeMove(board, MoveArrays.QueenMoves8[x].Moves[i], sqr.Piece) ==
554 | false)
555 | {
556 | break;
557 | }
558 | }
559 |
560 | break;
561 | }
562 | case ChessPieceType.King:
563 | {
564 | if (sqr.Piece.PieceColor == ChessPieceColor.White)
565 | {
566 | if (sqr.Piece.Moved)
567 | {
568 | board.WhiteCanCastle = false;
569 | }
570 | board.WhiteKingPosition = x;
571 | }
572 | else
573 | {
574 | if (sqr.Piece.Moved)
575 | {
576 | board.BlackCanCastle = false;
577 | }
578 | board.BlackKingPosition = x;
579 | }
580 |
581 | break;
582 | }
583 | }
584 | }
585 |
586 | if (blackRooksMoved > 1)
587 | {
588 | board.BlackCanCastle = false;
589 | }
590 | if (whiteRooksMoved > 1)
591 | {
592 | board.WhiteCanCastle = false;
593 | }
594 |
595 | if (remainingPieces < 10)
596 | {
597 | board.EndGamePhase = true;
598 | }
599 |
600 |
601 | if (board.WhoseMove == ChessPieceColor.White)
602 | {
603 | GenerateValidMovesKing(board.Squares[board.BlackKingPosition].Piece, board,
604 | board.BlackKingPosition);
605 | GenerateValidMovesKing(board.Squares[board.WhiteKingPosition].Piece, board,
606 | board.WhiteKingPosition);
607 | }
608 | else
609 | {
610 | GenerateValidMovesKing(board.Squares[board.WhiteKingPosition].Piece, board,
611 | board.WhiteKingPosition);
612 | GenerateValidMovesKing(board.Squares[board.BlackKingPosition].Piece, board,
613 | board.BlackKingPosition);
614 | }
615 |
616 |
617 | //Now that all the pieces were examined we know if the king is in check
618 | if (!board.WhiteCastled && board.WhiteCanCastle && !board.WhiteCheck)
619 | {
620 | GenerateValidMovesKingCastle(board, board.Squares[board.WhiteKingPosition].Piece);
621 | }
622 | if (!board.BlackCastled && board.BlackCanCastle && !board.BlackCheck)
623 | {
624 | GenerateValidMovesKingCastle(board, board.Squares[board.BlackKingPosition].Piece);
625 | }
626 | }
627 | }
628 | }
--------------------------------------------------------------------------------
/ChessCoreEngine/PiecesTaken.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace ChessEngine.Engine
3 | {
4 | public class PiecesTaken
5 | {
6 | public int WhiteQueen;
7 | public int WhiteRook;
8 | public int WhiteBishop;
9 | public int WhiteKnight;
10 | public int WhitePawn;
11 | public int BlackQueen;
12 | public int BlackRook;
13 | public int BlackBishop;
14 | public int BlackKnight;
15 | public int BlackPawn;
16 |
17 | public PiecesTaken()
18 | {
19 | WhiteQueen = 0;
20 | WhiteRook = 0;
21 | WhiteBishop = 0;
22 | WhiteKnight = 0;
23 | WhitePawn = 0;
24 | BlackQueen = 0;
25 | BlackRook = 0;
26 | BlackBishop = 0;
27 | BlackKnight = 0;
28 | BlackPawn = 0;
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/ChessCoreEngine/Puzzle.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 |
4 | namespace ChessEngine.Engine
5 | {
6 | public static class Puzzle
7 | {
8 | public static Engine NewPuzzleKnightBishopKing()
9 | {
10 | Engine engine;
11 |
12 | do
13 | {
14 | engine = PuzzleKnightBishopCandidate();
15 | }
16 | while (engine.IsGameOver() || engine.GetBlackCheck() || engine.GetWhiteCheck());
17 | return engine;
18 | }
19 |
20 | public static Engine NewPuzzleRookKing()
21 | {
22 | Engine engine;
23 |
24 | do
25 | {
26 | engine = PuzzleRookCandidate();
27 | }
28 | while (engine.IsGameOver() || engine.GetBlackCheck() || engine.GetWhiteCheck());
29 | return engine;
30 | }
31 |
32 | public static Engine NewPuzzlePawnKing()
33 | {
34 | Engine engine;
35 |
36 | do
37 | {
38 | engine = PuzzleKingPawnCandidate();
39 | }
40 | while (engine.IsGameOver() || engine.GetBlackCheck() || engine.GetWhiteCheck());
41 | return engine;
42 | }
43 |
44 | private static Engine PuzzleKnightBishopCandidate()
45 | {
46 | Engine engine = new Engine("");
47 |
48 | Random random = new Random(DateTime.Now.Second);
49 |
50 | byte whiteKingIndex;
51 | byte blackKingIndex;
52 | byte whiteKnightIndex;
53 | byte whiteBishopIndex;
54 |
55 | do
56 | {
57 | whiteKingIndex = (byte)random.Next(63);
58 | blackKingIndex = (byte)random.Next(63);
59 | whiteKnightIndex = (byte)random.Next(63);
60 | whiteBishopIndex = (byte)random.Next(63);
61 | }
62 | while (
63 | whiteKingIndex == blackKingIndex ||
64 | whiteKingIndex == whiteBishopIndex ||
65 | whiteKingIndex == whiteKnightIndex ||
66 | whiteKnightIndex == whiteBishopIndex ||
67 | blackKingIndex == whiteBishopIndex ||
68 | blackKingIndex == whiteKingIndex
69 | );
70 |
71 | Piece whiteKing = new Piece(ChessPieceType.King, ChessPieceColor.White);
72 | Piece whiteBishop = new Piece(ChessPieceType.Bishop, ChessPieceColor.White);
73 | Piece whiteKnight = new Piece(ChessPieceType.Knight, ChessPieceColor.White);
74 | Piece blackKing = new Piece(ChessPieceType.King, ChessPieceColor.Black);
75 |
76 | engine.SetChessPiece(whiteKing, whiteKingIndex);
77 | engine.SetChessPiece(blackKing, blackKingIndex);
78 | engine.SetChessPiece(whiteKnight, whiteKnightIndex);
79 | engine.SetChessPiece(whiteBishop, whiteBishopIndex);
80 |
81 | engine.GenerateValidMoves();
82 | engine.EvaluateBoardScore();
83 |
84 | return engine;
85 | }
86 |
87 | private static Engine PuzzleRookCandidate()
88 | {
89 | Engine engine = new Engine("");
90 |
91 | Random random = new Random(DateTime.Now.Second);
92 |
93 | byte whiteKingIndex;
94 | byte blackKingIndex;
95 | byte whiteRookIndex;
96 |
97 | do
98 | {
99 | whiteKingIndex = (byte)random.Next(63);
100 | blackKingIndex = (byte)random.Next(63);
101 | whiteRookIndex = (byte)random.Next(63);
102 | }
103 | while (
104 | whiteKingIndex == blackKingIndex ||
105 | whiteKingIndex == whiteRookIndex ||
106 | blackKingIndex == whiteKingIndex
107 | );
108 |
109 | Piece whiteKing = new Piece(ChessPieceType.King, ChessPieceColor.White);
110 | Piece whiteRook = new Piece(ChessPieceType.Rook, ChessPieceColor.White);
111 | Piece blackKing = new Piece(ChessPieceType.King, ChessPieceColor.Black);
112 |
113 | engine.SetChessPiece(whiteKing, whiteKingIndex);
114 | engine.SetChessPiece(blackKing, blackKingIndex);
115 | engine.SetChessPiece(whiteRook, whiteRookIndex);
116 |
117 | engine.GenerateValidMoves();
118 | engine.EvaluateBoardScore();
119 |
120 | return engine;
121 | }
122 |
123 | private static Engine PuzzleKingPawnCandidate()
124 | {
125 | Engine engine = new Engine("");
126 |
127 | Random random = new Random(DateTime.Now.Second);
128 |
129 | byte whiteKingIndex;
130 | byte blackKingIndex;
131 | byte whitePawnIndex;
132 |
133 | do
134 | {
135 | whiteKingIndex = (byte)random.Next(63);
136 | blackKingIndex = (byte)random.Next(63);
137 | whitePawnIndex = (byte)random.Next(63);
138 | }
139 | while (
140 | whiteKingIndex == blackKingIndex ||
141 | whiteKingIndex == whitePawnIndex ||
142 | blackKingIndex == whiteKingIndex ||
143 | whitePawnIndex <= 8 ||whitePawnIndex >= 56 ||
144 | whitePawnIndex < blackKingIndex
145 | );
146 |
147 | Piece whiteKing = new Piece(ChessPieceType.King, ChessPieceColor.White);
148 | Piece whitePawn = new Piece(ChessPieceType.Pawn, ChessPieceColor.White);
149 | Piece blackKing = new Piece(ChessPieceType.King, ChessPieceColor.Black);
150 |
151 | engine.SetChessPiece(whiteKing, whiteKingIndex);
152 | engine.SetChessPiece(blackKing, blackKingIndex);
153 | engine.SetChessPiece(whitePawn, whitePawnIndex);
154 |
155 | engine.GenerateValidMoves();
156 | engine.EvaluateBoardScore();
157 |
158 | return engine;
159 | }
160 |
161 | }
162 | }
163 |
--------------------------------------------------------------------------------
/ChessCoreEngine/ResultBoards.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace ChessEngine.Engine
4 | {
5 | internal struct ResultBoards
6 | {
7 | internal List Positions;
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/ChessCoreEngine/Search.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 |
5 | namespace ChessEngine.Engine
6 | {
7 | internal static class Search
8 | {
9 | internal static int progress;
10 |
11 | private static int piecesRemaining;
12 |
13 |
14 | private struct Position
15 | {
16 | internal byte SrcPosition;
17 | internal byte DstPosition;
18 | internal int Score;
19 | //internal bool TopSort;
20 | internal string Move;
21 |
22 | public new string ToString()
23 | {
24 | return Move;
25 | }
26 |
27 | }
28 |
29 | private static readonly Position[,] KillerMove = new Position[3,20];
30 | private static int kIndex;
31 |
32 | private static int Sort(Position s2, Position s1)
33 | {
34 | return (s1.Score).CompareTo(s2.Score);
35 | }
36 |
37 | private static int Sort(Board s2, Board s1)
38 | {
39 | return (s1.Score).CompareTo(s2.Score);
40 | }
41 |
42 | private static int SideToMoveScore(int score, ChessPieceColor color)
43 | {
44 | if (color == ChessPieceColor.Black)
45 | return -score;
46 |
47 | return score;
48 | }
49 |
50 |
51 |
52 | internal static MoveContent IterativeSearch(Board examineBoard, byte depth, ref int nodesSearched, ref int nodesQuiessence, ref string pvLine, ref byte plyDepthReached, ref byte rootMovesSearched, List currentGameBook)
53 | {
54 | List pvChild = new List();
55 | int alpha = -400000000;
56 | const int beta = 400000000;
57 |
58 |
59 | MoveContent bestMove = new MoveContent();
60 |
61 | //We are going to store our result boards here
62 | ResultBoards succ = GetSortValidMoves(examineBoard);
63 |
64 | rootMovesSearched = (byte)succ.Positions.Count;
65 |
66 | if (rootMovesSearched == 1)
67 | {
68 | //I only have one move
69 | return succ.Positions[0].LastMove;
70 | }
71 |
72 | //Can I make an instant mate?
73 | foreach (Board pos in succ.Positions)
74 | {
75 | int value = -AlphaBeta(pos, 1, -beta, -alpha, ref nodesSearched, ref nodesQuiessence, ref pvChild, true);
76 |
77 | if (value >= 32767)
78 | {
79 | return pos.LastMove;
80 | }
81 | }
82 |
83 | int currentBoard = 0;
84 |
85 | alpha = -400000000;
86 |
87 | succ.Positions.Sort(Sort);
88 |
89 | depth--;
90 |
91 | plyDepthReached = ModifyDepth(depth, succ.Positions.Count);
92 |
93 | foreach (Board pos in succ.Positions)
94 | {
95 | currentBoard++;
96 |
97 | progress = (int)((currentBoard / (decimal)succ.Positions.Count) * 100);
98 |
99 | pvChild = new List();
100 |
101 | int value = -AlphaBeta(pos, depth, -beta, -alpha, ref nodesSearched, ref nodesQuiessence, ref pvChild, false);
102 |
103 | if (value >= 32767)
104 | {
105 | return pos.LastMove;
106 | }
107 |
108 | if (examineBoard.RepeatedMove == 2)
109 | {
110 | string fen = Board.Fen(true, pos);
111 |
112 | foreach (OpeningMove move in currentGameBook)
113 | {
114 | if (move.EndingFEN == fen)
115 | {
116 | value = 0;
117 | break;
118 | }
119 | }
120 | }
121 |
122 | pos.Score = value;
123 |
124 | //If value is greater then alpha this is the best board
125 | if (value > alpha || alpha == -400000000)
126 | {
127 | pvLine = pos.LastMove.ToString();
128 |
129 | foreach (Position pvPos in pvChild)
130 | {
131 | pvLine += " " + pvPos.ToString();
132 | }
133 |
134 | alpha = value;
135 | bestMove = pos.LastMove;
136 | }
137 | }
138 |
139 | plyDepthReached++;
140 | progress=100;
141 |
142 | return bestMove;
143 | }
144 |
145 | private static ResultBoards GetSortValidMoves(Board examineBoard)
146 | {
147 | ResultBoards succ = new ResultBoards
148 | {
149 | Positions = new List(30)
150 | };
151 |
152 | piecesRemaining = 0;
153 |
154 | for (byte x = 0; x < 64; x++)
155 | {
156 | Square sqr = examineBoard.Squares[x];
157 |
158 | //Make sure there is a piece on the square
159 | if (sqr.Piece == null)
160 | continue;
161 |
162 | piecesRemaining++;
163 |
164 | //Make sure the color is the same color as the one we are moving.
165 | if (sqr.Piece.PieceColor != examineBoard.WhoseMove)
166 | continue;
167 |
168 | //For each valid move for this piece
169 | foreach (byte dst in sqr.Piece.ValidMoves)
170 | {
171 | //We make copies of the board and move so that we can move it without effecting the parent board
172 | Board board = examineBoard.FastCopy();
173 |
174 | //Make move so we can examine it
175 | Board.MovePiece(board, x, dst, ChessPieceType.Queen);
176 |
177 | //We Generate Valid Moves for Board
178 | PieceValidMoves.GenerateValidMoves(board);
179 |
180 | //Invalid Move
181 | if (board.WhiteCheck && examineBoard.WhoseMove == ChessPieceColor.White)
182 | {
183 | continue;
184 | }
185 |
186 | //Invalid Move
187 | if (board.BlackCheck && examineBoard.WhoseMove == ChessPieceColor.Black)
188 | {
189 | continue;
190 | }
191 |
192 | //We calculate the board score
193 | Evaluation.EvaluateBoardScore(board);
194 |
195 | //Invert Score to support Negamax
196 | board.Score = SideToMoveScore(board.Score, board.WhoseMove);
197 |
198 | succ.Positions.Add(board);
199 | }
200 | }
201 |
202 | succ.Positions.Sort(Sort);
203 | return succ;
204 | }
205 |
206 | private static int AlphaBeta(Board examineBoard, byte depth, int alpha, int beta, ref int nodesSearched, ref int nodesQuiessence, ref List pvLine, bool extended)
207 | {
208 | nodesSearched++;
209 |
210 | if (examineBoard.HalfMoveClock >= 100 || examineBoard.RepeatedMove >= 3)
211 | return 0;
212 |
213 | //End Main Search with Quiescence
214 | if (depth == 0)
215 | {
216 | if (!extended && examineBoard.BlackCheck || examineBoard.WhiteCheck)
217 | {
218 | depth++;
219 | extended = true;
220 | }
221 | else
222 | {
223 | //Perform a Quiessence Search
224 | return Quiescence(examineBoard, alpha, beta, ref nodesQuiessence);
225 | }
226 | }
227 |
228 | List positions = EvaluateMoves(examineBoard, depth);
229 |
230 | if (examineBoard.WhiteCheck || examineBoard.BlackCheck || positions.Count == 0)
231 | {
232 | if (SearchForMate(examineBoard.WhoseMove, examineBoard, ref examineBoard.BlackMate, ref examineBoard.WhiteMate, ref examineBoard.StaleMate))
233 | {
234 | if (examineBoard.BlackMate)
235 | {
236 | if (examineBoard.WhoseMove == ChessPieceColor.Black)
237 | return -32767-depth;
238 |
239 | return 32767 + depth;
240 | }
241 | if (examineBoard.WhiteMate)
242 | {
243 | if (examineBoard.WhoseMove == ChessPieceColor.Black)
244 | return 32767 + depth;
245 |
246 | return -32767 - depth;
247 | }
248 |
249 | //If Not Mate then StaleMate
250 | return 0;
251 | }
252 | }
253 |
254 | positions.Sort(Sort);
255 |
256 | foreach (Position move in positions)
257 | {
258 | List pvChild = new List();
259 |
260 | //Make a copy
261 | Board board = examineBoard.FastCopy();
262 |
263 | //Move Piece
264 | Board.MovePiece(board, move.SrcPosition, move.DstPosition, ChessPieceType.Queen);
265 |
266 | //We Generate Valid Moves for Board
267 | PieceValidMoves.GenerateValidMoves(board);
268 |
269 | if (board.BlackCheck)
270 | {
271 | if (examineBoard.WhoseMove == ChessPieceColor.Black)
272 | {
273 | //Invalid Move
274 | continue;
275 | }
276 | }
277 |
278 | if (board.WhiteCheck)
279 | {
280 | if (examineBoard.WhoseMove == ChessPieceColor.White)
281 | {
282 | //Invalid Move
283 | continue;
284 | }
285 | }
286 |
287 | int value = -AlphaBeta(board, (byte)(depth - 1), -beta, -alpha, ref nodesSearched, ref nodesQuiessence, ref pvChild, extended);
288 |
289 | if (value >= beta)
290 | {
291 | KillerMove[kIndex, depth].SrcPosition = move.SrcPosition;
292 | KillerMove[kIndex, depth].DstPosition = move.DstPosition;
293 |
294 | kIndex = ((kIndex + 1) % 2);
295 |
296 |
297 | return beta;
298 | }
299 | if (value > alpha)
300 | {
301 | Position pvPos = new Position();
302 |
303 | pvPos.SrcPosition = board.LastMove.MovingPiecePrimary.SrcPosition;
304 | pvPos.DstPosition = board.LastMove.MovingPiecePrimary.DstPosition;
305 | pvPos.Move = board.LastMove.ToString();
306 |
307 | pvChild.Insert(0, pvPos);
308 |
309 | pvLine = pvChild;
310 |
311 | alpha = (int)value;
312 | }
313 | }
314 |
315 | return alpha;
316 | }
317 |
318 | private static int Quiescence(Board examineBoard, int alpha, int beta, ref int nodesSearched)
319 | {
320 | nodesSearched++;
321 |
322 | //Evaluate Score
323 | Evaluation.EvaluateBoardScore(examineBoard);
324 |
325 | //Invert Score to support Negamax
326 | examineBoard.Score = SideToMoveScore(examineBoard.Score, examineBoard.WhoseMove);
327 |
328 | if (examineBoard.Score >= beta)
329 | return beta;
330 |
331 | if (examineBoard.Score > alpha)
332 | alpha = examineBoard.Score;
333 |
334 |
335 | List positions;
336 |
337 |
338 | if (examineBoard.WhiteCheck || examineBoard.BlackCheck)
339 | {
340 | positions = EvaluateMoves(examineBoard, 0);
341 | }
342 | else
343 | {
344 | positions = EvaluateMovesQ(examineBoard);
345 | }
346 |
347 | if (positions.Count == 0)
348 | {
349 | return examineBoard.Score;
350 | }
351 |
352 | positions.Sort(Sort);
353 |
354 | foreach (Position move in positions)
355 | {
356 | if (StaticExchangeEvaluation(examineBoard.Squares[move.DstPosition]) >= 0)
357 | {
358 | continue;
359 | }
360 |
361 | //Make a copy
362 | Board board = examineBoard.FastCopy();
363 |
364 | //Move Piece
365 | Board.MovePiece(board, move.SrcPosition, move.DstPosition, ChessPieceType.Queen);
366 |
367 | //We Generate Valid Moves for Board
368 | PieceValidMoves.GenerateValidMoves(board);
369 |
370 | if (board.BlackCheck)
371 | {
372 | if (examineBoard.WhoseMove == ChessPieceColor.Black)
373 | {
374 | //Invalid Move
375 | continue;
376 | }
377 | }
378 |
379 | if (board.WhiteCheck)
380 | {
381 | if (examineBoard.WhoseMove == ChessPieceColor.White)
382 | {
383 | //Invalid Move
384 | continue;
385 | }
386 | }
387 |
388 | int value = -Quiescence(board, - beta, -alpha, ref nodesSearched);
389 |
390 | if (value >= beta)
391 | {
392 | KillerMove[2, 0].SrcPosition = move.SrcPosition;
393 | KillerMove[2, 0].DstPosition = move.DstPosition;
394 |
395 | return beta;
396 | }
397 | if (value > alpha)
398 | {
399 | alpha = value;
400 | }
401 | }
402 |
403 | return alpha;
404 | }
405 |
406 | private static List EvaluateMoves(Board examineBoard, byte depth)
407 | {
408 |
409 | //We are going to store our result boards here
410 | List positions = new List();
411 |
412 | //bool foundPV = false;
413 |
414 |
415 | for (byte x = 0; x < 64; x++)
416 | {
417 | Piece piece = examineBoard.Squares[x].Piece;
418 |
419 | //Make sure there is a piece on the square
420 | if (piece == null)
421 | continue;
422 |
423 | //Make sure the color is the same color as the one we are moving.
424 | if (piece.PieceColor != examineBoard.WhoseMove)
425 | continue;
426 |
427 | //For each valid move for this piece
428 | foreach (byte dst in piece.ValidMoves)
429 | {
430 | Position move = new Position();
431 |
432 | move.SrcPosition = x;
433 | move.DstPosition = dst;
434 |
435 | if (move.SrcPosition == KillerMove[0, depth].SrcPosition && move.DstPosition == KillerMove[0, depth].DstPosition)
436 | {
437 | //move.TopSort = true;
438 | move.Score += 5000;
439 | positions.Add(move);
440 | continue;
441 | }
442 | if (move.SrcPosition == KillerMove[1, depth].SrcPosition && move.DstPosition == KillerMove[1, depth].DstPosition)
443 | {
444 | //move.TopSort = true;
445 | move.Score += 5000;
446 | positions.Add(move);
447 | continue;
448 | }
449 |
450 | Piece pieceAttacked = examineBoard.Squares[move.DstPosition].Piece;
451 |
452 | //If the move is a capture add it's value to the score
453 | if (pieceAttacked != null)
454 | {
455 | move.Score += pieceAttacked.PieceValue;
456 |
457 | if (piece.PieceValue < pieceAttacked.PieceValue)
458 | {
459 | move.Score += pieceAttacked.PieceValue - piece.PieceValue;
460 | }
461 | }
462 |
463 | if (!piece.Moved)
464 | {
465 | move.Score += 10;
466 | }
467 |
468 | move.Score += piece.PieceActionValue;
469 |
470 | //Add Score for Castling
471 | if (!examineBoard.WhiteCastled && examineBoard.WhoseMove == ChessPieceColor.White)
472 | {
473 |
474 | if (piece.PieceType == ChessPieceType.King)
475 | {
476 | if (move.DstPosition != 62 && move.DstPosition != 58)
477 | {
478 | move.Score -= 40;
479 | }
480 | else
481 | {
482 | move.Score += 40;
483 | }
484 | }
485 | if (piece.PieceType == ChessPieceType.Rook)
486 | {
487 | move.Score -= 40;
488 | }
489 | }
490 |
491 | if (!examineBoard.BlackCastled && examineBoard.WhoseMove == ChessPieceColor.Black)
492 | {
493 | if (piece.PieceType == ChessPieceType.King)
494 | {
495 | if (move.DstPosition != 6 && move.DstPosition != 2)
496 | {
497 | move.Score -= 40;
498 | }
499 | else
500 | {
501 | move.Score += 40;
502 | }
503 | }
504 | if (piece.PieceType == ChessPieceType.Rook)
505 | {
506 | move.Score -= 40;
507 | }
508 | }
509 |
510 | positions.Add(move);
511 | }
512 | }
513 |
514 | return positions;
515 | }
516 |
517 | private static List EvaluateMovesQ(Board examineBoard)
518 | {
519 | //We are going to store our result boards here
520 | List positions = new List();
521 |
522 | for (byte x = 0; x < 64; x++)
523 | {
524 | Piece piece = examineBoard.Squares[x].Piece;
525 |
526 | //Make sure there is a piece on the square
527 | if (piece == null)
528 | continue;
529 |
530 | //Make sure the color is the same color as the one we are moving.
531 | if (piece.PieceColor != examineBoard.WhoseMove)
532 | continue;
533 |
534 | //For each valid move for this piece
535 | foreach (byte dst in piece.ValidMoves)
536 | {
537 | if (examineBoard.Squares[dst].Piece == null)
538 | {
539 | continue;
540 | }
541 |
542 | Position move = new Position();
543 |
544 | move.SrcPosition = x;
545 | move.DstPosition = dst;
546 |
547 | if (move.SrcPosition == KillerMove[2, 0].SrcPosition && move.DstPosition == KillerMove[2, 0].DstPosition)
548 | {
549 | //move.TopSort = true;
550 | move.Score += 5000;
551 | positions.Add(move);
552 | continue;
553 | }
554 |
555 | Piece pieceAttacked = examineBoard.Squares[move.DstPosition].Piece;
556 |
557 | move.Score += pieceAttacked.PieceValue;
558 |
559 | if (piece.PieceValue < pieceAttacked.PieceValue)
560 | {
561 | move.Score += pieceAttacked.PieceValue - piece.PieceValue;
562 | }
563 |
564 | move.Score += piece.PieceActionValue;
565 |
566 |
567 | positions.Add(move);
568 | }
569 | }
570 |
571 | return positions;
572 | }
573 |
574 | internal static bool SearchForMate(ChessPieceColor movingSide, Board examineBoard, ref bool blackMate, ref bool whiteMate, ref bool staleMate)
575 | {
576 | bool foundNonCheckBlack = false;
577 | bool foundNonCheckWhite = false;
578 |
579 | for (byte x = 0; x < 64; x++)
580 | {
581 | Square sqr = examineBoard.Squares[x];
582 |
583 | //Make sure there is a piece on the square
584 | if (sqr.Piece == null)
585 | continue;
586 |
587 | //Make sure the color is the same color as the one we are moving.
588 | if (sqr.Piece.PieceColor != movingSide)
589 | continue;
590 |
591 | //For each valid move for this piece
592 | foreach (byte dst in sqr.Piece.ValidMoves)
593 | {
594 |
595 | //We make copies of the board and move so that we can move it without effecting the parent board
596 | Board board = examineBoard.FastCopy();
597 |
598 | //Make move so we can examine it
599 | Board.MovePiece(board, x, dst, ChessPieceType.Queen);
600 |
601 | //We Generate Valid Moves for Board
602 | PieceValidMoves.GenerateValidMoves(board);
603 |
604 | if (board.BlackCheck == false)
605 | {
606 | foundNonCheckBlack = true;
607 | }
608 | else if (movingSide == ChessPieceColor.Black)
609 | {
610 | continue;
611 | }
612 |
613 | if (board.WhiteCheck == false )
614 | {
615 | foundNonCheckWhite = true;
616 | }
617 | else if (movingSide == ChessPieceColor.White)
618 | {
619 | continue;
620 | }
621 | }
622 | }
623 |
624 | if (foundNonCheckBlack == false)
625 | {
626 | if (examineBoard.BlackCheck)
627 | {
628 | blackMate = true;
629 | return true;
630 | }
631 | if (!examineBoard.WhiteMate && movingSide != ChessPieceColor.White)
632 | {
633 | staleMate = true;
634 | return true;
635 | }
636 | }
637 |
638 | if (foundNonCheckWhite == false)
639 | {
640 | if (examineBoard.WhiteCheck)
641 | {
642 | whiteMate = true;
643 | return true;
644 | }
645 | if (!examineBoard.BlackMate && movingSide != ChessPieceColor.Black)
646 | {
647 | staleMate = true;
648 | return true;
649 | }
650 | }
651 |
652 | return false;
653 |
654 | }
655 |
656 | private static byte ModifyDepth(byte depth, int possibleMoves)
657 | {
658 | if (possibleMoves <= 20 || piecesRemaining < 14)
659 | {
660 | if (possibleMoves <= 10 || piecesRemaining < 6)
661 | {
662 | depth += 1;
663 | }
664 |
665 | depth += 1;
666 | }
667 |
668 | return depth;
669 | }
670 |
671 | private static int StaticExchangeEvaluation(Square examineSquare)
672 | {
673 | if (examineSquare.Piece == null)
674 | {
675 | return 0;
676 | }
677 | if (examineSquare.Piece.AttackedValue == 0)
678 | {
679 | return 0;
680 | }
681 |
682 | return examineSquare.Piece.PieceActionValue - examineSquare.Piece.AttackedValue + examineSquare.Piece.DefendedValue;
683 | }
684 |
685 | }
686 | }
687 |
--------------------------------------------------------------------------------
/ChessCoreEngine/Square.cs:
--------------------------------------------------------------------------------
1 | namespace ChessEngine.Engine
2 | {
3 |
4 | internal struct Square
5 | {
6 | internal Piece Piece;
7 |
8 | #region Constructors
9 |
10 | internal Square(Piece piece)
11 | {
12 | Piece = new Piece(piece);
13 | }
14 |
15 | #endregion
16 | }
17 | }
--------------------------------------------------------------------------------
/ChessCoreEngine/Test.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using NUnit.Framework;
4 |
5 | namespace ChessEngine.Engine
6 | {
7 | public class CorrectnessTest
8 | {
9 | public void RunAllCorrectnessTests()
10 | {
11 | TestBlankBoard();
12 | TestStandardBoardShortFEN();
13 | TestStandardBoardFullFEN();
14 | TestNotation();
15 | TestValidMoves();
16 | TestSimpleMoves();
17 | TestRealGame();
18 | Test50MoveRule();
19 | TestAI();
20 | }
21 |
22 | [Test]
23 | public void TestBlankBoard()
24 | {
25 | var board = new Board();
26 | var fen = Board.Fen(false, board);
27 | Assert.AreEqual("8/8/8/8/8/8/8/8 w - - 0 0", fen);
28 | }
29 |
30 | [Test]
31 | public void TestStandardBoardShortFEN()
32 | {
33 | var standardFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w - -";
34 | var board = new Board(standardFen);
35 | var fen = Board.Fen(true, board);
36 | Assert.AreEqual(standardFen, fen);
37 | }
38 |
39 | [Test]
40 | public void TestStandardBoardFullFEN()
41 | {
42 | var standardFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
43 | var board = new Board(standardFen);
44 | var fen = Board.Fen(false, board);
45 | Assert.AreEqual(standardFen, fen);
46 | }
47 |
48 | [Test]
49 | public void TestBoardOrientation()
50 | {
51 | var standardFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
52 | var board = new Board(standardFen);
53 |
54 | // 0,0 is a8
55 | // 7,7 is h1
56 |
57 | // is the board oriented the way we expect?
58 | var piece = board.Squares[0].Piece;
59 | Assert.AreEqual(ChessPieceColor.Black, piece.PieceColor);
60 | Assert.AreEqual(ChessPieceType.Rook, piece.PieceType);
61 |
62 | piece = board.Squares[3].Piece;
63 | Assert.AreEqual(ChessPieceColor.Black, piece.PieceColor);
64 | Assert.AreEqual(ChessPieceType.Queen, piece.PieceType);
65 |
66 | piece = board.Squares[63].Piece;
67 | Assert.AreEqual(ChessPieceColor.White, piece.PieceColor);
68 | Assert.AreEqual(ChessPieceType.Rook, piece.PieceType);
69 |
70 | piece = board.Squares[59].Piece;
71 | Assert.AreEqual(ChessPieceColor.White, piece.PieceColor);
72 | Assert.AreEqual(ChessPieceType.Queen, piece.PieceType);
73 | }
74 |
75 | [Test]
76 | public void TestNotation()
77 | {
78 | byte sourceColumn=0, sourceRow=0, destinationColumn=0, destinationRow=0;
79 |
80 | Assert.IsFalse(MoveContent.ParseAN("toolong", ref sourceColumn, ref sourceRow, ref destinationColumn, ref destinationRow));
81 | Assert.IsFalse(MoveContent.ParseAN("abc", ref sourceColumn, ref sourceRow, ref destinationColumn, ref destinationRow));
82 |
83 | Assert.IsTrue(MoveContent.ParseAN("a8h1", ref sourceColumn, ref sourceRow, ref destinationColumn, ref destinationRow));
84 | Assert.AreEqual(sourceColumn, 0);
85 | Assert.AreEqual(sourceRow, 0);
86 | Assert.AreEqual(destinationColumn, 7);
87 | Assert.AreEqual(destinationRow, 7);
88 |
89 | Assert.IsTrue(MoveContent.ParseAN("b3e4", ref sourceColumn, ref sourceRow, ref destinationColumn, ref destinationRow));
90 | Assert.AreEqual(sourceColumn, 1);
91 | Assert.AreEqual(sourceRow, 5);
92 | Assert.AreEqual(destinationColumn, 4);
93 | Assert.AreEqual(destinationRow, 4);
94 | }
95 |
96 | [Test]
97 | public void TestValidMoves()
98 | {
99 | var engine = new Engine("rnbqkbnr/ppppppp1/8/8/8/8/8/8 w KQkq - 0 1");
100 |
101 | // rook
102 | Assert.IsFalse(engine.IsValidMove(0,0,7,7));
103 | Assert.IsFalse(engine.IsValidMove(0,0,0,1));
104 | Assert.IsFalse(engine.IsValidMoveAN("a8g1"));
105 | Assert.IsFalse(engine.IsValidMoveAN("a8a7"));
106 |
107 | // pawn
108 | Assert.IsTrue(engine.IsValidMove(0,1,0,2));
109 | Assert.IsTrue(engine.IsValidMove(0,1,0,3));
110 | Assert.IsFalse(engine.IsValidMove(0,1,0,4));
111 | Assert.IsTrue(engine.IsValidMoveAN("a7a6"));
112 | Assert.IsTrue(engine.IsValidMoveAN("a7a5"));
113 | Assert.IsFalse(engine.IsValidMoveAN("a7a4"));
114 |
115 | // knight
116 | Assert.IsTrue(engine.IsValidMove(1,0,0,2));
117 | Assert.IsTrue(engine.IsValidMove(1,0,2,2));
118 | Assert.IsFalse(engine.IsValidMove(1,0,3,1));
119 | Assert.IsTrue(engine.IsValidMoveAN("b8a6"));
120 | Assert.IsTrue(engine.IsValidMoveAN("b8c6"));
121 | Assert.IsFalse(engine.IsValidMoveAN("b8d7"));
122 |
123 | // rook2
124 | Assert.IsTrue(engine.IsValidMove(7,0,7,1));
125 | Assert.IsTrue(engine.IsValidMove(7,0,7,7));
126 | Assert.IsFalse(engine.IsValidMove(7,0,6,7));
127 | Assert.IsTrue(engine.IsValidMoveAN("h8h7"));
128 | Assert.IsTrue(engine.IsValidMoveAN("h8h1"));
129 | Assert.IsFalse(engine.IsValidMoveAN("h8g1"));
130 | }
131 |
132 | [Test]
133 | public void TestSimpleMoves()
134 | {
135 | var standardFen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
136 | var engine = new Engine(standardFen);
137 |
138 | engine.MovePieceAN("e2e4");
139 | Assert.AreEqual("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1", engine.FEN);
140 |
141 | engine.MovePieceAN("c7c5");
142 | Assert.AreEqual("rnbqkbnr/pp1ppppp/8/2p5/4P3/8/PPPP1PPP/RNBQKBNR w KQkq c6 0 2", engine.FEN);
143 |
144 | engine.MovePieceAN("g1f3");
145 | Assert.AreEqual("rnbqkbnr/pp1ppppp/8/2p5/4P3/5N2/PPPP1PPP/RNBQKB1R b KQkq - 1 2", engine.FEN);
146 | }
147 |
148 | [Test]
149 | public void Test50MoveRule() {
150 | var engine = new Engine("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
151 | for (var i=0; i<24; i++) {
152 | engine.MovePieceAN("b1c3");
153 | engine.MovePieceAN("b8c6");
154 | engine.MovePieceAN("c3b1");
155 | engine.MovePieceAN("c6b8");
156 | Assert.IsFalse(engine.FiftyMove);
157 | }
158 | engine.MovePieceAN("b1c3");
159 | Assert.IsFalse(engine.FiftyMove);
160 | engine.MovePieceAN("b8c6");
161 | Assert.IsFalse(engine.FiftyMove);
162 | engine.MovePieceAN("c3b1");
163 | Assert.IsFalse(engine.FiftyMove);
164 | engine.MovePieceAN("c6b8");
165 | Assert.IsTrue(engine.FiftyMove);
166 | }
167 |
168 | [Test]
169 | public void TestRealGame() {
170 | /*
171 | // http://www.chessgames.com/perl/nph-chesspgn?text=1&gid=1067317
172 | string[] pgn = {
173 | "[Event \"Third Rosenwald Trophy\"]",
174 | "[Site \"New York, NY USA\"]",
175 | "[Date \"1956.10.17\"]",
176 | "[EventDate \"1956.10.07\"]",
177 | "[Round \"8\"]",
178 | "[Result \"0-1\"]",
179 | "[White \"Donald Byrne\"]",
180 | "[Black \"Robert James Fischer\"]",
181 | "[ECO \"D92\"]",
182 | "[WhiteElo \"?\"]",
183 | "[BlackElo \"?\"]",
184 | "[PlyCount \"82\"]",
185 | "",
186 | "1. Nf3 Nf6 2. c4 g6 3. Nc3 Bg7 4. d4 O-O 5. Bf4 d5 6. Qb3 dxc4",
187 | "7. Qxc4 c6 8. e4 Nbd7 9. Rd1 Nb6 10. Qc5 Bg4 11. Bg5 {11. Be2",
188 | "followed by 12. O-O would have been more prudent. The bishop",
189 | "move played allows a sudden crescendo of tactical points to be",
190 | "uncovered by Fischer. -- Wade} Na4 {!} 12. Qa3 {On 12. Nxa4",
191 | "Nxe4 and White faces considerable difficulties.} Nxc3 {At",
192 | "first glance, one might think that this move only helps White",
193 | "create a stronger pawn center; however, Fischer's plan is",
194 | "quite the opposite. By eliminating the Knight on c3, it",
195 | "becomes possible to sacrifice the exchange via Nxe4 and smash",
196 | "White's center, while the King remains trapped in the center.}",
197 | "13. bxc3 Nxe4 {The natural continuation of Black's plan.}",
198 | "14. Bxe7 Qb6 15. Bc4 Nxc3 16. Bc5 Rfe8+ 17. Kf1 Be6 {!! If",
199 | "this is the game of the century, then 17...Be6!! must be the",
200 | "counter of the century. Fischer offers his queen in exchange",
201 | "for a fierce attack with his minor pieces. Declining this",
202 | "offer is not so easy: 18. Bxe6 leads to a 'Philidor Mate'",
203 | "(smothered mate) with ...Qb5+ 19. Kg1 Ne2+ 20. Kf1 Ng3+",
204 | "21. Kg1 Qf1+ 22. Rxf1 Ne2#. Other ways to decline the queen",
205 | "also run into trouble: e.g., 18. Qxc3 Qxc5} 18. Bxb6 Bxc4+",
206 | "19. Kg1 Ne2+ 20. Kf1 Nxd4+ {This tactical scenario, where a",
207 | "king is repeatedly revealed to checks, is sometimes called a",
208 | "\"windmill.\"} 21. Kg1 Ne2+ 22. Kf1 Nc3+ 23. Kg1 axb6 24. Qb4",
209 | "Ra4 25. Qxb6 Nxd1 26. h3 Rxa2 27. Kh2 Nxf2 28. Re1 Rxe1",
210 | "29. Qd8+ Bf8 30. Nxe1 Bd5 31. Nf3 Ne4 32. Qb8 b5 {Every piece",
211 | "and pawn of the black camp is defended. The white queen has",
212 | "nothing to do.} 33. h4 h5 34. Ne5 Kg7 35. Kg1 Bc5+ 36. Kf1",
213 | "Ng3+ {Now Byrne is hopelessly entangled in Fischer's mating",
214 | "net.} 37. Ke1 Bb4+ 38. Kd1 Bb3+ 39. Kc1 Ne2+ 40. Kb1 Nc3+",
215 | "41. Kc1 Rc2# 0-1"
216 | };
217 | */
218 |
219 | var engine = new Engine("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
220 | string[] movelist = {
221 | "g1f3",
222 | "g8f6",
223 | "c2c4",
224 | "g7g6",
225 | "b1c3",
226 | "f8g7",
227 | "d2d4",
228 | "e8g8",
229 | "c1f4",
230 | "d7d5",
231 | "d1b3",
232 | "d5c4",
233 | "b3c4",
234 | "c7c6",
235 | "e2e4",
236 | "b8d7",
237 | "a1d1",
238 | "d7b6",
239 | "c4c5",
240 | "c8g4",
241 | "f4g5",
242 | "b6a4",
243 | "c5a3",
244 | "a4c3",
245 | "b2c3",
246 | "f6e4",
247 | "g5e7",
248 | "d8b6",
249 | "f1c4",
250 | "e4c3",
251 | "e7c5",
252 | "f8e8",
253 | "e1f1",
254 | "g4e6",
255 | "c5b6",
256 | "e6c4",
257 | "f1g1",
258 | "c3e2",
259 | "g1f1",
260 | "e2d4",
261 | "f1g1",
262 | "d4e2",
263 | "g1f1",
264 | "e2c3",
265 | "f1g1",
266 | "a7b6",
267 | "a3b4",
268 | "a8a4",
269 | "b4b6",
270 | "c3d1",
271 | "h2h3",
272 | "a4a2",
273 | "g1h2",
274 | "d1f2",
275 | "h1e1",
276 | "e8e1",
277 | "b6d8",
278 | "g7f8",
279 | "f3e1",
280 | "c4d5",
281 | "e1f3",
282 | "f2e4",
283 | "d8b8",
284 | "b7b5",
285 | "h3h4",
286 | "h7h5",
287 | "f3e5",
288 | "g8g7",
289 | "h2g1",
290 | "f8c5",
291 | "g1f1",
292 | "e4g3",
293 | "f1e1",
294 | "c5b4",
295 | "e1d1",
296 | "d5b3",
297 | "d1c1",
298 | "g3e2",
299 | "c1b1",
300 | "e2c3",
301 | "b1c1",
302 | "a2c2"
303 | };
304 | string[] fenlist = {
305 | "rnbqkbnr/pppppppp/8/8/8/5N2/PPPPPPPP/RNBQKB1R b KQkq - 1 1",
306 | "rnbqkb1r/pppppppp/5n2/8/8/5N2/PPPPPPPP/RNBQKB1R w KQkq - 2 2",
307 | "rnbqkb1r/pppppppp/5n2/8/2P5/5N2/PP1PPPPP/RNBQKB1R b KQkq c3 0 2",
308 | "rnbqkb1r/pppppp1p/5np1/8/2P5/5N2/PP1PPPPP/RNBQKB1R w KQkq - 0 3",
309 | "rnbqkb1r/pppppp1p/5np1/8/2P5/2N2N2/PP1PPPPP/R1BQKB1R b KQkq - 1 3",
310 | "rnbqk2r/ppppppbp/5np1/8/2P5/2N2N2/PP1PPPPP/R1BQKB1R w KQkq - 2 4",
311 | "rnbqk2r/ppppppbp/5np1/8/2PP4/2N2N2/PP2PPPP/R1BQKB1R b KQkq d3 0 4",
312 | "rnbq1rk1/ppppppbp/5np1/8/2PP4/2N2N2/PP2PPPP/R1BQKB1R w KQ - 1 5",
313 | "rnbq1rk1/ppppppbp/5np1/8/2PP1B2/2N2N2/PP2PPPP/R2QKB1R b KQ - 2 5",
314 | "rnbq1rk1/ppp1ppbp/5np1/3p4/2PP1B2/2N2N2/PP2PPPP/R2QKB1R w KQ d6 0 6",
315 | "rnbq1rk1/ppp1ppbp/5np1/3p4/2PP1B2/1QN2N2/PP2PPPP/R3KB1R b KQ - 1 6",
316 | "rnbq1rk1/ppp1ppbp/5np1/8/2pP1B2/1QN2N2/PP2PPPP/R3KB1R w KQ - 0 7",
317 | "rnbq1rk1/ppp1ppbp/5np1/8/2QP1B2/2N2N2/PP2PPPP/R3KB1R b KQ - 0 7",
318 | "rnbq1rk1/pp2ppbp/2p2np1/8/2QP1B2/2N2N2/PP2PPPP/R3KB1R w KQ - 0 8",
319 | "rnbq1rk1/pp2ppbp/2p2np1/8/2QPPB2/2N2N2/PP3PPP/R3KB1R b KQ e3 0 8",
320 | "r1bq1rk1/pp1nppbp/2p2np1/8/2QPPB2/2N2N2/PP3PPP/R3KB1R w KQ - 1 9",
321 | "r1bq1rk1/pp1nppbp/2p2np1/8/2QPPB2/2N2N2/PP3PPP/3RKB1R b K - 2 9",
322 | "r1bq1rk1/pp2ppbp/1np2np1/8/2QPPB2/2N2N2/PP3PPP/3RKB1R w K - 3 10",
323 | "r1bq1rk1/pp2ppbp/1np2np1/2Q5/3PPB2/2N2N2/PP3PPP/3RKB1R b K - 4 10",
324 | "r2q1rk1/pp2ppbp/1np2np1/2Q5/3PPBb1/2N2N2/PP3PPP/3RKB1R w K - 5 11",
325 | "r2q1rk1/pp2ppbp/1np2np1/2Q3B1/3PP1b1/2N2N2/PP3PPP/3RKB1R b K - 6 11",
326 | "r2q1rk1/pp2ppbp/2p2np1/2Q3B1/n2PP1b1/2N2N2/PP3PPP/3RKB1R w K - 7 12",
327 | "r2q1rk1/pp2ppbp/2p2np1/6B1/n2PP1b1/Q1N2N2/PP3PPP/3RKB1R b K - 8 12",
328 | "r2q1rk1/pp2ppbp/2p2np1/6B1/3PP1b1/Q1n2N2/PP3PPP/3RKB1R w K - 0 13",
329 | "r2q1rk1/pp2ppbp/2p2np1/6B1/3PP1b1/Q1P2N2/P4PPP/3RKB1R b K - 0 13",
330 | "r2q1rk1/pp2ppbp/2p3p1/6B1/3Pn1b1/Q1P2N2/P4PPP/3RKB1R w K - 0 14",
331 | "r2q1rk1/pp2Bpbp/2p3p1/8/3Pn1b1/Q1P2N2/P4PPP/3RKB1R b K - 0 14",
332 | "r4rk1/pp2Bpbp/1qp3p1/8/3Pn1b1/Q1P2N2/P4PPP/3RKB1R w K - 1 15",
333 | "r4rk1/pp2Bpbp/1qp3p1/8/2BPn1b1/Q1P2N2/P4PPP/3RK2R b K - 2 15",
334 | "r4rk1/pp2Bpbp/1qp3p1/8/2BP2b1/Q1n2N2/P4PPP/3RK2R w K - 0 16",
335 | "r4rk1/pp3pbp/1qp3p1/2B5/2BP2b1/Q1n2N2/P4PPP/3RK2R b K - 1 16",
336 | "r3r1k1/pp3pbp/1qp3p1/2B5/2BP2b1/Q1n2N2/P4PPP/3RK2R w K - 2 17",
337 | "r3r1k1/pp3pbp/1qp3p1/2B5/2BP2b1/Q1n2N2/P4PPP/3R1K1R b - - 3 17",
338 | "r3r1k1/pp3pbp/1qp1b1p1/2B5/2BP4/Q1n2N2/P4PPP/3R1K1R w - - 4 18",
339 | "r3r1k1/pp3pbp/1Bp1b1p1/8/2BP4/Q1n2N2/P4PPP/3R1K1R b - - 0 18",
340 | "r3r1k1/pp3pbp/1Bp3p1/8/2bP4/Q1n2N2/P4PPP/3R1K1R w - - 0 19",
341 | "r3r1k1/pp3pbp/1Bp3p1/8/2bP4/Q1n2N2/P4PPP/3R2KR b - - 1 19",
342 | "r3r1k1/pp3pbp/1Bp3p1/8/2bP4/Q4N2/P3nPPP/3R2KR w - - 2 20",
343 | "r3r1k1/pp3pbp/1Bp3p1/8/2bP4/Q4N2/P3nPPP/3R1K1R b - - 3 20",
344 | "r3r1k1/pp3pbp/1Bp3p1/8/2bn4/Q4N2/P4PPP/3R1K1R w - - 0 21",
345 | "r3r1k1/pp3pbp/1Bp3p1/8/2bn4/Q4N2/P4PPP/3R2KR b - - 1 21",
346 | "r3r1k1/pp3pbp/1Bp3p1/8/2b5/Q4N2/P3nPPP/3R2KR w - - 2 22",
347 | "r3r1k1/pp3pbp/1Bp3p1/8/2b5/Q4N2/P3nPPP/3R1K1R b - - 3 22",
348 | "r3r1k1/pp3pbp/1Bp3p1/8/2b5/Q1n2N2/P4PPP/3R1K1R w - - 4 23",
349 | "r3r1k1/pp3pbp/1Bp3p1/8/2b5/Q1n2N2/P4PPP/3R2KR b - - 5 23",
350 | "r3r1k1/1p3pbp/1pp3p1/8/2b5/Q1n2N2/P4PPP/3R2KR w - - 0 24",
351 | "r3r1k1/1p3pbp/1pp3p1/8/1Qb5/2n2N2/P4PPP/3R2KR b - - 1 24",
352 | "4r1k1/1p3pbp/1pp3p1/8/rQb5/2n2N2/P4PPP/3R2KR w - - 2 25",
353 | "4r1k1/1p3pbp/1Qp3p1/8/r1b5/2n2N2/P4PPP/3R2KR b - - 0 25",
354 | "4r1k1/1p3pbp/1Qp3p1/8/r1b5/5N2/P4PPP/3n2KR w - - 0 26",
355 | "4r1k1/1p3pbp/1Qp3p1/8/r1b5/5N1P/P4PP1/3n2KR b - - 0 26",
356 | "4r1k1/1p3pbp/1Qp3p1/8/2b5/5N1P/r4PP1/3n2KR w - - 0 27",
357 | "4r1k1/1p3pbp/1Qp3p1/8/2b5/5N1P/r4PPK/3n3R b - - 1 27",
358 | "4r1k1/1p3pbp/1Qp3p1/8/2b5/5N1P/r4nPK/7R w - - 0 28",
359 | "4r1k1/1p3pbp/1Qp3p1/8/2b5/5N1P/r4nPK/4R3 b - - 1 28",
360 | "6k1/1p3pbp/1Qp3p1/8/2b5/5N1P/r4nPK/4r3 w - - 0 29",
361 | "3Q2k1/1p3pbp/2p3p1/8/2b5/5N1P/r4nPK/4r3 b - - 1 29",
362 | "3Q1bk1/1p3p1p/2p3p1/8/2b5/5N1P/r4nPK/4r3 w - - 2 30",
363 | "3Q1bk1/1p3p1p/2p3p1/8/2b5/7P/r4nPK/4N3 b - - 0 30",
364 | "3Q1bk1/1p3p1p/2p3p1/3b4/8/7P/r4nPK/4N3 w - - 1 31",
365 | "3Q1bk1/1p3p1p/2p3p1/3b4/8/5N1P/r4nPK/8 b - - 2 31",
366 | "3Q1bk1/1p3p1p/2p3p1/3b4/4n3/5N1P/r5PK/8 w - - 3 32",
367 | "1Q3bk1/1p3p1p/2p3p1/3b4/4n3/5N1P/r5PK/8 b - - 4 32",
368 | "1Q3bk1/5p1p/2p3p1/1p1b4/4n3/5N1P/r5PK/8 w - b6 0 33",
369 | "1Q3bk1/5p1p/2p3p1/1p1b4/4n2P/5N2/r5PK/8 b - - 0 33",
370 | "1Q3bk1/5p2/2p3p1/1p1b3p/4n2P/5N2/r5PK/8 w - h6 0 34",
371 | "1Q3bk1/5p2/2p3p1/1p1bN2p/4n2P/8/r5PK/8 b - - 1 34",
372 | "1Q3b2/5pk1/2p3p1/1p1bN2p/4n2P/8/r5PK/8 w - - 2 35",
373 | "1Q3b2/5pk1/2p3p1/1p1bN2p/4n2P/8/r5P1/6K1 b - - 3 35",
374 | "1Q6/5pk1/2p3p1/1pbbN2p/4n2P/8/r5P1/6K1 w - - 4 36",
375 | "1Q6/5pk1/2p3p1/1pbbN2p/4n2P/8/r5P1/5K2 b - - 5 36",
376 | "1Q6/5pk1/2p3p1/1pbbN2p/7P/6n1/r5P1/5K2 w - - 6 37",
377 | "1Q6/5pk1/2p3p1/1pbbN2p/7P/6n1/r5P1/4K3 b - - 7 37",
378 | "1Q6/5pk1/2p3p1/1p1bN2p/1b5P/6n1/r5P1/4K3 w - - 8 38",
379 | "1Q6/5pk1/2p3p1/1p1bN2p/1b5P/6n1/r5P1/3K4 b - - 9 38",
380 | "1Q6/5pk1/2p3p1/1p2N2p/1b5P/1b4n1/r5P1/3K4 w - - 10 39",
381 | "1Q6/5pk1/2p3p1/1p2N2p/1b5P/1b4n1/r5P1/2K5 b - - 11 39",
382 | "1Q6/5pk1/2p3p1/1p2N2p/1b5P/1b6/r3n1P1/2K5 w - - 12 40",
383 | "1Q6/5pk1/2p3p1/1p2N2p/1b5P/1b6/r3n1P1/1K6 b - - 13 40",
384 | "1Q6/5pk1/2p3p1/1p2N2p/1b5P/1bn5/r5P1/1K6 w - - 14 41",
385 | "1Q6/5pk1/2p3p1/1p2N2p/1b5P/1bn5/r5P1/2K5 b - - 15 41",
386 | "1Q6/5pk1/2p3p1/1p2N2p/1b5P/1bn5/2r3P1/2K5 w - - 16 42"
387 | };
388 | var index=0;
389 | foreach (var move in movelist) {
390 | engine.MovePieceAN(move);
391 | Assert.AreEqual(fenlist[index], engine.FEN);
392 | index++;
393 | }
394 | }
395 |
396 | [Test]
397 | public void TestAI()
398 | {
399 | // set up a simple scenario with an obvious checkmate in 1 move
400 | var engine = new Engine("k7/7R/6R1/8/8/8/8/K7 w - - 0 1");
401 | engine.GameDifficulty = Engine.Difficulty.Easy;
402 | engine.AiPonderMove();
403 | MoveContent lastMove = engine.GetMoveHistory().ToArray()[0];
404 | string move = lastMove.GetPureCoordinateNotation();
405 | // did the AI find the checkmate?
406 | Assert.AreEqual("g6g8", move);
407 | }
408 | }
409 |
410 | public class PerformanceTest
411 | {
412 | private static ResultBoards resultBoards = new ResultBoards { Positions = new List(30) };
413 |
414 | private static ChessPieceColor GetOppositeColor(ChessPieceColor color)
415 | {
416 | return color == ChessPieceColor.Black ? ChessPieceColor.White : ChessPieceColor.Black;
417 | }
418 |
419 | private static int SideToMoveScore(int score, ChessPieceColor color)
420 | {
421 | if (color == ChessPieceColor.Black)
422 | return -score;
423 |
424 | return score;
425 | }
426 |
427 | internal static PerformanceResult RunPerfTest(int depth, Board board)
428 | {
429 | var performanceResult = new PerformanceResult();
430 |
431 | DateTime startTime = DateTime.Now;
432 |
433 | performanceResult.Nodes = Performance(depth, board, ChessPieceColor.White);
434 | performanceResult.TimeSpan = (DateTime.Now - startTime);
435 | performanceResult.Depth = depth;
436 |
437 | return performanceResult;
438 | }
439 |
440 | private static long Performance(int depth, Board board, ChessPieceColor color)
441 | {
442 | long nodes = 0;
443 |
444 | if (depth == 0) return 1;
445 |
446 | ResultBoards moveList = GetPossibleBoards(GetOppositeColor(color), board);
447 |
448 | for (int i = 0; i < moveList.Positions.Count; i++)
449 | {
450 | nodes += Performance(depth - 1, moveList.Positions[i], GetOppositeColor(color));
451 | }
452 |
453 | return nodes;
454 | }
455 |
456 | private static ResultBoards GetPossibleBoards(ChessPieceColor movingSide, Board examineBoard)
457 | {
458 | //We are going to store our result boards here
459 | resultBoards = new ResultBoards
460 | {
461 | Positions = new List()
462 | };
463 |
464 | for (byte x = 0; x < 64; x++)
465 | {
466 | Square sqr = examineBoard.Squares[x];
467 |
468 | //Make sure there is a piece on the square
469 | if (sqr.Piece == null)
470 | continue;
471 |
472 | //Make sure the color is the same color as the one we are moving.
473 | if (sqr.Piece.PieceColor != movingSide)
474 | continue;
475 |
476 | //For each valid move for this piece
477 | foreach (byte dst in sqr.Piece.ValidMoves)
478 | {
479 | //We make copies of the board and move so that we can move it without effecting the parent board
480 | Board board = examineBoard.FastCopy();
481 |
482 | //Make move so we can examine it
483 | Board.MovePiece(board, x, dst, ChessPieceType.Queen);
484 |
485 | //We Generate Valid Moves for Board
486 | PieceValidMoves.GenerateValidMoves(board);
487 |
488 |
489 | if (board.BlackCheck && movingSide == ChessPieceColor.Black)
490 | {
491 | continue;
492 | }
493 |
494 | if (board.WhiteCheck && movingSide == ChessPieceColor.White)
495 | {
496 | continue;
497 | }
498 |
499 | //We calculate the board score
500 | Evaluation.EvaluateBoardScore(board);
501 |
502 | //Invert Score to support Negamax
503 | board.Score = SideToMoveScore(board.Score, GetOppositeColor(movingSide));
504 |
505 | resultBoards.Positions.Add(board);
506 | }
507 | }
508 |
509 | return resultBoards;
510 | }
511 |
512 | #region Nested type: PerformanceResult
513 |
514 | public struct PerformanceResult
515 | {
516 | public int Depth;
517 | public long Nodes;
518 | public TimeSpan TimeSpan;
519 | }
520 |
521 | #endregion
522 | }
523 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Adam Berent
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ChessCore
2 | Chess Engine Implemented in .net core
3 |
4 | Winboard/Xboard Compatible Chess Engine Written in C#/.NET Core. ChessCore has 2 modes XBoard Protocol which allows you to use Winboard Chess GUI http://hgm.nubati.net or Console mode which shows the current chess board using ASCII.
5 |
6 | To Run type in: dotnet ChessCore.dll
7 |
8 |
9 | # About
10 |
11 | This project is a product of a rather rash decision in mid 2008 to learn to program my own Chess Game, hence began my journey into the art of computer chess. The main goal of the original project was to learn about how computers play chess while producing a chess engine that is easily understood and well documented. I feel that goal has now been achieved. As the next step I decided to release the full source code for my chess engine under the MIT license to allow other developers to learn and contribute to further improve & extend my chess engine.
12 |
13 | The documentation & tutorial on how to build a chess engine can be accessed in the following 2 formats:
14 |
15 | PDF
16 |
17 | http://www.adamberent.com/wp-content/uploads/2019/02/GuideToProgrammingChessEngine.pdf
18 |
19 | Website
20 |
21 | http://adamberent.com/home/chess/computer-chess/
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Releases/ChessCore1.0.1.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/3583Bytes/ChessCore/c08f1f13b5231a469e1018af7d3a2d6a79cb7a10/Releases/ChessCore1.0.1.zip
--------------------------------------------------------------------------------