/
pawn_piece.cc
159 lines (137 loc) · 5.64 KB
/
pawn_piece.cc
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
#include "pawn_piece.h"
#include "queen_piece.h"
#include "rook_piece.h"
#include "knight_piece.h"
#include "bishop_piece.h"
namespace NFairyChess::NVanillaPieces {
using enum TPawnPiece::EMoveStatus;
namespace {
void AddMoveWithPromotionCheck(TPawnPiece pawnPiece, TMoveContext& ctx,
TBoardPosition movePosition, EPieceColor color)
{
if (movePosition.Row == ctx.Board.GetRows() - 1) {
// a pawn on the last row should be promoted to a valuable piece
std::array<TBoardPiece, 4> arr = {
TBoardPiece::Create<TQueenPiece>(color),
TBoardPiece::Create<TRookPiece>(color),
TBoardPiece::Create<TKnightPiece>(color),
TBoardPiece::Create<TBishopPiece>(color),
};
for (TBoardPiece boardPiece : arr) {
ctx.Moves.Add(
TMoveBuilder{}
.SetBoardPiece(movePosition, boardPiece)
.RemoveBoardPiece(ctx.Position)
.Build()
);
}
} else {
ctx.Moves.Add(
TMoveBuilder{}
.SetBoardPiece(movePosition, TBoardPiece::CreateFromExisting(color, pawnPiece))
.RemoveBoardPiece(ctx.Position)
.Build()
);
}
}
bool TryAddForwardMove(TPawnPiece pawnPiece, TMoveContext& ctx, int dist) {
// check move position for correctness
TBoardPosition movePosition = ctx.Position;
if (!ctx.Board.ShiftPosition(movePosition, TBoardPosition{.Column = 0, .Row = dist})) {
return false;
}
// break if this square is blocked by another piece
if (!ctx.Board.GetBoardPiece(movePosition).IsEmpty()) {
return false;
}
// we can move there! update the move status and add the move
if (dist == 2) {
pawnPiece.GetMoveStatus().SetValue(JustMovedTwoSquares);
} else {
pawnPiece.GetMoveStatus().SetValue(Moved);
}
const EPieceColor color = ctx.Board.GetBoardPiece(ctx.Position).GetColor();
AddMoveWithPromotionCheck(pawnPiece, ctx, movePosition, color);
return true;
}
void TryAddSimpleCapturingMove(TPawnPiece pawnPiece, TMoveContext& ctx,
TBoardPosition movePosition, EPieceColor currentColor)
{
TBoardPiece enemyBoardPiece = ctx.Board.GetBoardPiece(movePosition);
if (!enemyBoardPiece.IsEmpty() && enemyBoardPiece.GetColor() != currentColor) {
AddMoveWithPromotionCheck(pawnPiece, ctx, movePosition, currentColor);
}
}
void TryAddEnPassantCapturingMove(TPawnPiece pawnPiece, TMoveContext& ctx, TBoardPosition movePosition,
TBoardPosition enPassantPosition, EPieceColor currentColor)
{
TBoardPiece enPassantBoardPiece = ctx.Board.GetBoardPiece(enPassantPosition);
if (!enPassantBoardPiece.IsEmpty() && enPassantBoardPiece.GetColor() != currentColor) {
if (auto pawnOrEmpty = enPassantBoardPiece.GetPieceOrEmpty<TPawnPiece>(); !pawnOrEmpty.IsEmpty()) {
const TPawnPiece::EMoveStatus enemyMoveStatus =
pawnOrEmpty.GetPiece().GetMoveStatus().GetValue<TPawnPiece::EMoveStatus>();
if (enemyMoveStatus == CanBeCapturedEnPassant) {
// we can capture en passant!
ctx.Moves.Add(
TMoveBuilder{}
.SetBoardPiece(movePosition, TBoardPiece::CreateFromExisting(currentColor, pawnPiece))
.RemoveBoardPiece(enPassantPosition)
.RemoveBoardPiece(ctx.Position)
.Build()
);
}
}
}
}
void TryAddCapturingMove(TPawnPiece pawnPiece, TMoveContext& ctx, int deltaCol) {
// check move position for correctness
TBoardPosition movePosition = ctx.Position;
if (!ctx.Board.ShiftPosition(movePosition, TBoardPosition{.Column = deltaCol, .Row = 1})) {
return;
}
pawnPiece.GetMoveStatus().SetValue(Moved);
const EPieceColor color = ctx.Board.GetBoardPiece(ctx.Position).GetColor();
// can only capture existing piece of enemy color
TryAddSimpleCapturingMove(pawnPiece, ctx, movePosition, color);
// check if we can capture enemy's pawn en passant
TBoardPosition enPassantPosition = ctx.Position;
ctx.Board.ShiftPosition(enPassantPosition, TBoardPosition{.Column = deltaCol, .Row = 0});
TryAddEnPassantCapturingMove(pawnPiece, ctx, movePosition, enPassantPosition, color);
}
} // namespace
void TPawnPiece::FillMoves(TMoveContext& ctx) {
TBoardPiece boardPiece = ctx.Board.GetBoardPiece(ctx.Position);
const EPieceColor color = boardPiece.GetColor();
const EMoveStatus moveStatus = GetMoveStatus().GetValue<EMoveStatus>();
// the pawn can move two squares forward if it has not moved yet
int maxDistance = 1;
if (moveStatus == EMoveStatus::NotMoved) {
maxDistance = 2;
}
for (int dist = 1; dist <= maxDistance; ++dist) {
if (!TryAddForwardMove(*this, ctx, dist)) {
break;
}
}
// the pawn can capture a piece diagonally
for (int deltaCol : {-1, 1}) {
TryAddCapturingMove(*this, ctx, deltaCol);
}
}
bool TPawnPiece::AfterMoveApply(const TBoard& /* oldBoard */, const TMove& /* move */) {
auto moveStatus = GetMoveStatus();
switch (moveStatus.GetValue<EMoveStatus>()) {
case EMoveStatus::JustMovedTwoSquares: {
moveStatus.SetValue(EMoveStatus::CanBeCapturedEnPassant);
return true;
}
case EMoveStatus::CanBeCapturedEnPassant: {
moveStatus.SetValue(EMoveStatus::Moved);
return true;
}
default: {
return false;
}
}
};
} // namespace NFairyChess::NVanillaPieces