├── .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 --------------------------------------------------------------------------------