-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathchess_board.cpp
234 lines (212 loc) · 7.82 KB
/
chess_board.cpp
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#include <algorithm>
#include "chess_board.hpp"
#include "piece_factory.hpp"
// This is required to use the _1, and _2 placeholders
using namespace std::placeholders;
namespace chess
{
chess_board::chess_board()
{
for(char i = 'a'; i != 'i'; ++i)
{
for (size_t j = 0; j < 8; ++j)
{
m_board[i][j] = nullptr;
}
}
m_board['a'][0] = make_rook('w', {'a', 0});
m_board['b'][0] = make_knight('w', {'b', 0});
m_board['c'][0] = make_bishop('w', {'c', 0});
m_board['d'][0] = make_queen('w', {'d', 0});
m_board['e'][0] = make_king('w', {'e', 0});
m_board['f'][0] = make_bishop('w', {'f', 0});
m_board['g'][0] = make_knight('w', {'g', 0});
m_board['h'][0] = make_rook('w', {'h', 0});
m_board['a'][1] = make_pawn('w', {'a', 1});
m_board['b'][1] = make_pawn('w', {'b', 1});
m_board['c'][1] = make_pawn('w', {'c', 1});
m_board['d'][1] = make_pawn('w', {'d', 1});
m_board['e'][1] = make_pawn('w', {'e', 1});
m_board['f'][1] = make_pawn('w', {'f', 1});
m_board['g'][1] = make_pawn('w', {'g', 1});
m_board['h'][1] = make_pawn('w', {'h', 1});
m_board['a'][6] = make_pawn('b', {'a', 6});
m_board['b'][6] = make_pawn('b', {'b', 6});
m_board['c'][6] = make_pawn('b', {'c', 6});
m_board['d'][6] = make_pawn('b', {'d', 6});
m_board['e'][6] = make_pawn('b', {'e', 6});
m_board['f'][6] = make_pawn('b', {'f', 6});
m_board['g'][6] = make_pawn('b', {'g', 6});
m_board['h'][6] = make_pawn('b', {'h', 6});
m_board['a'][7] = make_rook('b', {'a', 7});
m_board['b'][7] = make_knight('b', {'b', 7});
m_board['c'][7] = make_bishop('b', {'c', 7});
m_board['d'][7] = make_queen('b', {'d', 7});
m_board['e'][7] = make_king('b', {'e', 7});
m_board['f'][7] = make_bishop('b', {'f', 7});
m_board['g'][7] = make_knight('b', {'g', 7});
m_board['h'][7] = make_rook('b', {'h', 7});
// std::function constructor accepts function objects,
// that is, functions and classes defining operator().
// has_piece is not a function but a method, it can
// not be invoked this way: has_piece(pos, c), but
// has to be called on an object: my_chess_board.has_piece(pos, c);
// Therefore, we need to "bind" the method to the current instance
// of chess_board (that is, this).
// We take the address of a method M of a class C with the syntax
// &M::C
// We use placeholders _1 and _2 to tall the compiler that we
// don't bind values to arguments of the function.
// More on bind can be found at https://en.cppreference.com/w/cpp/utility/functional/bind
m_callback = std::bind(&chess_board::has_piece, this, _1, _2);
// Initializes the white and black pieces lists
auto white_it = m_white_pieces.begin();
auto black_it = m_black_pieces.begin();
for (const auto& c: m_board)
{
*white_it++ = c[0];
*white_it++ = c[1];
*black_it++ = c[6];
*black_it++ = c[7];
}
// Initiliazes the king's "trackers"
p_white_king = m_board['e'][0];
p_black_king = m_board['e'][7];
m_current_color = 'w';
}
chess_board::~chess_board()
{
// This assumes empty cases are initialized with nullptr
std::for_each(m_board.begin(), m_board.end(), [](auto& row)
{
std::for_each(row.begin(), row.end(), [](auto* piece)
{
delete piece;
});
});
}
bool chess_board::can_move(const position_type& from, const position_type& to) const
{
chess_piece* pce = piece(from);
if (pce)
{
bool valid = check_bounds(from) && check_bounds(to);
// Checks that the user doe snot try to move an opponent piece
valid &= pce->get_color() == m_current_color;
valid &= pce->can_move(to, m_callback);
valid &= check_in_check(pce, to);
return valid;
}
else
{
return false;
}
}
void chess_board::move(const position_type& from, const position_type& to)
{
// Updates chess piece
chess_piece* pce = piece(from);
pce->move(to);
// Updates chess board - first deletes piece at th destination
// position if there is one (since the move is valid, it means
// that the piece is taken by another one)
chess_piece* must_die = piece(to);
if (must_die != nullptr)
{
color c = must_die->get_color();
if (c == 'b')
{
auto it = std::find(m_black_pieces.begin(), m_black_pieces.end(), must_die);
*it = nullptr;
}
else
{
auto it = std::find(m_white_pieces.begin(), m_white_pieces.end(), must_die);
*it = nullptr;
}
delete must_die;
}
piece(to) = pce;
piece(from) = nullptr;
pce->notify_move();
m_current_color = pce->get_opposite_color();
}
void chess_board::print(std::ostream& out) const
{
print_separator(out);
for (size_t i = 0; i < 8; ++i)
{
out << "| " << 8 - i << " |";
for (char j = 'a'; j != 'i'; ++j)
{
chess_piece* pce = m_board[j][7 - i];
if (pce != nullptr)
{
out << " " << (*pce) << " |";
}
else
{
out << " |";
}
}
out << std::endl;
print_separator(out);
}
out << "| | a | b | c | d | e | f | g | h | " << std::endl;
print_separator(out);
}
chess_board::piece_ptr& chess_board::piece(const position_type& pos)
{
return m_board[pos.first][pos.second];
}
const chess_board::piece_ptr& chess_board::piece(const position_type& pos) const
{
return m_board[pos.first][pos.second];
}
bool chess_board::has_piece(const position_type& pos, color c) const
{
piece_ptr pce = piece(pos);
return pce && (c == 'a' || pce->get_color() == c);
}
bool chess_board::check_bounds(const position_type& pos) const
{
return pos.first >= 'a' && pos.first <= 'h' && pos.second < 8u;
}
bool chess_board::check_in_check(piece_ptr piece, const position_type& new_pos) const
{
bool valid = true;
color c = piece->get_opposite_color();
// Let's emulate a move of piece to new_pos and check whether the king of opposite
// color is in check. To do so, we temporarily set the position of piece to new_pos,
// perform the check, and reset back piece to its original position
// Takes a copy, since we're going to alter the internal position of piece after
position_type current_pos = piece->get_position();
piece->move(new_pos);
if (c == 'b')
{
valid = check_in_check(m_white_pieces, p_black_king);
}
else
{
valid = check_in_check(m_black_pieces, p_white_king);
}
piece->move(current_pos);
return valid;
}
bool chess_board::check_in_check(const piece_list& l, piece_ptr king) const
{
bool valid = true;
for (auto p: l)
{
if (p != nullptr)
{
valid &= !p->can_move(king->get_position(), m_callback);
}
}
return valid;
}
void chess_board::print_separator(std::ostream& out) const
{
out << "+----+----+----+----+----+----+----+----+----+ " << std::endl;
}
}