-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathBoard.cs
More file actions
172 lines (144 loc) · 6.62 KB
/
Board.cs
File metadata and controls
172 lines (144 loc) · 6.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
using System;
using System.Collections.Generic;
using System.Linq;
namespace Chess
{
public class Board
{
public const int DefaultBoardSize = 8;
private readonly int _boardSize;
private readonly Piece[,] _pieces;
public int Size { get { return _boardSize; } }
public int PieceCount
{
get
{
return _pieces.Cast<Piece>().Count(p => p != null);
}
}
public Board(int boardSize = DefaultBoardSize)
{
VerifyBoardSizeOrThrow(boardSize);
_boardSize = boardSize;
_pieces = new Piece[boardSize, boardSize];
}
public void AddPiece(Piece piece, BoardCoordinate moveTarget)
{
if (piece == null)
throw new ArgumentNullException("piece");
if (!moveTarget.IsCoordinateValidForBoardSize(_boardSize))
throw new ArgumentException("moveTarget");
SetPiece(piece, moveTarget);
}
public void MovePiece(BoardCoordinate origin, BoardCoordinate destination)
{
VerifyCoordinatesOrThrow(origin, destination);
var pieceToMove = GetPiece(origin);
AddPiece(pieceToMove, destination);
RemovePiece(origin);
pieceToMove.HasMoved = true;
ReconcileEnPassant(origin, destination, pieceToMove);
}
public void RemovePiece(BoardCoordinate coordinateForRemoval)
{
if (!DoesPieceExistAt(coordinateForRemoval))
throw new ArgumentException("coordinateForRemoval");
SetPiece(null, coordinateForRemoval);
}
public Piece GetPiece(BoardCoordinate coordinateToRetrieve)
{
return _pieces[coordinateToRetrieve.X - 1, coordinateToRetrieve.Y - 1];
}
public bool DoesPieceExistAt(BoardCoordinate coordinateToCheck)
{
return GetPiece(coordinateToCheck) != null;
}
public IEnumerable<BoardCoordinate> GetMovesFrom(BoardCoordinate originCoordinate)
{
var piece = GetPiece(originCoordinate);
var checker = new CastlingStatusChecker(this);
var castlingMoves = checker.GetCastlingMovesFor(originCoordinate).ToList();
var allPossibleMoves = piece.GetMovesFrom(originCoordinate).Union(castlingMoves);
return allPossibleMoves.Where(move => IsMoveLegal(originCoordinate, move) && !IsMoveThreatenedKing(piece, move));
}
public bool IsMoveLegal(BoardCoordinate origin, BoardCoordinate destination)
{
var isCapture = destination.IsCoordinateValidForBoardSize(_boardSize) && GetPiece(destination) != null;
var isIllegalCapture = isCapture && !GetPiece(origin).IsCaptureAllowed(origin, destination);
var isIllegalNonCapture = !isCapture && !GetPiece(origin).IsNonCaptureAllowed(origin, destination);
return !isIllegalCapture && !isIllegalNonCapture && destination.IsCoordinateValidForBoardSize(_boardSize) &&
!IsBlocked(origin, destination) && !DoesFriendlyPieceExistAt(origin, destination);
}
#region EnPassantStuff
private void ReconcileEnPassant(BoardCoordinate origin, BoardCoordinate destination, Piece pieceToMove)
{
if (IsEnPassantApplicable(origin, destination, pieceToMove))
SetEnPassantIfCandidatePawnsArePresent(destination);
CleanEnPassantForPlayerThatJustMoved(pieceToMove);
}
private static bool IsEnPassantApplicable(BoardCoordinate origin, BoardCoordinate destination, Piece pieceToMove)
{
const int enPassantTrigger = 2;
var movementFactor = pieceToMove.IsFirstPlayerPiece ? -enPassantTrigger : enPassantTrigger;
return origin.Y == destination.Y + movementFactor;
}
private void SetEnPassantIfCandidatePawnsArePresent(BoardCoordinate destination)
{
var leftTarget = BoardCoordinate.For(destination.X - 1, destination.Y);
var rightTarget = BoardCoordinate.For(destination.X + 1, destination.Y);
SetEnPassantIfPawnExistsAtTarget(destination, leftTarget);
SetEnPassantIfPawnExistsAtTarget(destination, rightTarget);
}
private void SetEnPassantIfPawnExistsAtTarget(BoardCoordinate destination, BoardCoordinate enPassantTarget)
{
if (enPassantTarget.IsCoordinateValidForBoardSize(_boardSize))
{
var targetPawn = GetPiece(enPassantTarget) as Pawn;
if (targetPawn != null)
targetPawn.SetCanPerformEnPassantOn(destination);
}
}
private void CleanEnPassantForPlayerThatJustMoved(Piece pieceToMove)
{
foreach (var piece in _pieces)
{
var pawn = piece as Pawn;
if (pawn != null && pawn.IsFirstPlayerPiece == pieceToMove.IsFirstPlayerPiece)
pawn.ClearEnPassant();
}
}
#endregion
private bool DoesFriendlyPieceExistAt(BoardCoordinate origin, BoardCoordinate destination)
{
var piece = GetPiece(destination);
return piece != null && piece.IsFirstPlayerPiece == GetPiece(origin).IsFirstPlayerPiece;
}
private static void VerifyBoardSizeOrThrow(int boardSize)
{
if (boardSize <= 0)
throw new ArgumentException("boardSize");
}
private void SetPiece(Piece piece, BoardCoordinate location)
{
_pieces[location.X - 1, location.Y - 1] = piece;
}
private void VerifyCoordinatesOrThrow(params BoardCoordinate[] coordinates)
{
if (coordinates.Any(bc => !bc.IsCoordinateValidForBoardSize(_boardSize)))
throw new ArgumentException("coordinate");
}
private bool IsBlocked(BoardCoordinate origin, BoardCoordinate destination)
{
var checker = new PathMaker(origin, destination);
var spacesAlongPath = checker.GetPathToDestination();
var lastSpace = spacesAlongPath.LastOrDefault();
return spacesAlongPath.Any(space => DoesFriendlyPieceExistAt(origin, space)) || spacesAlongPath.Any(space => DoesPieceExistAt(space) && !space.Equals(lastSpace));
}
private bool IsMoveThreatenedKing(Piece moveCandidate, BoardCoordinate moveDestination)
{
var evaluator = new ThreatEvaluator(this);
return moveCandidate is King &&
evaluator.IsThreatened(moveDestination, moveCandidate.IsFirstPlayerPiece);
}
}
}