10 : m_bAllTokens{BB_EMPTY},
11 m_bActivePTokens{BB_EMPTY},
12 m_movesLeft{N_COLUMNS * N_ROWS} {
14 assert(uint64_t_popcnt(BB_ALL_LEGAL_TOKENS) == N_COLUMNS * N_ROWS);
15 assert(uint64_t_popcnt(BB_ILLEGAL) ==
16 CHAR_BIT *
sizeof(TBitBoard) - N_COLUMNS * N_ROWS);
17 assert(getRowMask(0) == getMask({0, 9, 18, 27, 36, 45, 54}));
18 assert(getRowMask(5) == getMask({5, 14, 23, 32, 41, 50, 59}));
21bool Board::setBoard(
const std::vector<int> &moveSequence) {
24 for (
const auto mv : moveSequence) {
25 if (!b.playMove(mv))
return false;
31bool Board::setBoard(
const TBoardArray &board) {
32 if (!isValid(board)) {
35 TBitBoard allTokens{BB_EMPTY}, yellowTokens{BB_EMPTY};
36 for (
auto c = 0; c < N_COLUMNS; ++c) {
37 for (
auto r = 0; r < N_ROWS; ++r) {
38 auto val = board[c][r];
39 auto mask = getMaskColRow(c, r);
40 if (val == P_YELLOW) {
42 }
else if (val != P_RED) {
43 assert(val == P_EMPTY);
46 assert((allTokens & mask) == BB_EMPTY);
52 N_COLUMNS * N_ROWS -
static_cast<int>(uint64_t_popcnt(allTokens));
55 m_bActivePTokens = yellowTokens ^ (m_movesLeft & 1 ? allTokens : BB_EMPTY);
56 m_bAllTokens = allTokens;
61bool Board::isValid(
const TBoardArray &board) {
63 std::array<int, N_VALID_BOARD_VALUES> valCounts = {0UL};
64 for (
auto c =
static_cast<TBoardArray::size_type
>(0); c < N_COLUMNS; ++c) {
65 bool columnComplete =
false;
66 for (
auto r =
static_cast<TBoardArray::size_type
>(0); r < N_ROWS; ++r) {
67 const auto val = board[c][r];
69 if (val != P_RED && val != P_YELLOW && val != P_EMPTY) {
73 columnComplete =
true;
75 if (columnComplete)
return false;
82 if (
const auto diff = valCounts[P_YELLOW] - valCounts[P_RED];
83 diff < 0 || diff > 1) {
101bool Board::playMove(
const int column) {
103 if (!isLegalMove(column))
return false;
105 playMoveFast(column);
110bool Board::isLegalMove(
const int column)
const {
111 if (column < 0 || column >= N_COLUMNS)
return false;
113 TBitBoard columnMask = getColumnMask(column);
114 columnMask -= (UINT64_C(1) << (column * COLUMN_BIT_OFFSET));
115 return !(m_bAllTokens & columnMask & BB_TOP_ROW);
162Board::TBitBoard Board::winningPositions(
const TBitBoard x,
163 const bool verticals) {
165 TBitBoard wins = verticals ? (x << 1) & (x << 2) & (x << 3) : UINT64_C(0);
167 for (
auto b = COLUMN_BIT_OFFSET - 1; b <= COLUMN_BIT_OFFSET + 1; ++b) {
170 auto tmp = (x << b) & (x << 2 * b);
171 wins |= tmp & (x << 3 * b);
172 wins |= tmp & (x >> b);
175 tmp = (x >> b) & (x >> 2 * b);
176 wins |= tmp & (x << b);
177 wins |= tmp & (x >> 3 * b);
180 return wins & BB_ALL_LEGAL_TOKENS;
194bool Board::hasWin()
const {
196 const auto y = m_bActivePTokens ^ m_bAllTokens;
197 auto x = y & (y << 2);
198 if (x & (x << 1))
return true;
201 x = y & (y << 2 * COLUMN_BIT_OFFSET);
202 if (x & (x << COLUMN_BIT_OFFSET))
return true;
205 x = y & (y << 2 * (COLUMN_BIT_OFFSET - 1));
206 if (x & (x << (COLUMN_BIT_OFFSET - 1)))
return true;
209 x = y & (y << 2 * (COLUMN_BIT_OFFSET + 1));
210 if (x & (x << (COLUMN_BIT_OFFSET + 1)))
return true;
225MoveList Board::sortMoves(TBitBoard moves)
const {
229 const TBitBoard ownThreats = winningPositions(m_bActivePTokens,
false);
232 const auto mv = nextMove(moves);
233 assert(uint64_t_popcnt(mv) == 1);
237 winningPositions(m_bActivePTokens ^ mv,
true) & ~(m_bAllTokens ^ mv);
238 auto numThreats =
static_cast<int>(uint64_t_popcnt(threats));
242 if (ownThreats & (mv << 1)) numThreats--;
244 mvList.insert(mv, numThreats);
251Board::TBitBoard Board::findThreats(TBitBoard moves) {
252 auto threats = winningPositions(m_bActivePTokens,
true) & ~m_bAllTokens;
255 auto curNumThreats = uint64_t_popcnt(threats);
256 auto threatMoves = UINT64_C(0);
258 auto mv = lsb(moves);
267 winningPositions(m_bActivePTokens ^ mv,
true) & ~(m_bAllTokens ^ mv);
269 if (uint64_t_popcnt(threats & (moves ^ mv)) > 1)
272 if (
const auto numThreats = uint64_t_popcnt(threats);
273 numThreats > curNumThreats) {
282Board::TBitBoard Board::findOddThreats(TBitBoard moves) {
283 constexpr auto ODD_ROWS = getRowMask(2) | getRowMask(4);
284 auto threats = winningPositions(m_bActivePTokens,
true) & ~m_bAllTokens;
287 const auto curNumThreats = uint64_t_popcnt(threats & ODD_ROWS);
288 auto threatMoves = UINT64_C(0);
290 const auto mv = lsb(moves);
293 winningPositions(m_bActivePTokens ^ mv,
true) & ~(m_bAllTokens ^ mv);
294 if (
const auto numThreats = uint64_t_popcnt(threats & ODD_ROWS);
295 numThreats > curNumThreats) {
345bool Board::canWin()
const {
346 return winningPositions(m_bActivePTokens,
true) &
347 (m_bAllTokens + BB_BOTTOM_ROW);
350bool Board::canWin(
int column)
const {
351 return isLegalMove(column) &&
352 (winningPositions(m_bActivePTokens,
true) &
353 (m_bAllTokens + BB_BOTTOM_ROW) & getColumnMask(column));
356Board::TBitBoard Board::generateMoves()
const {
357 return (m_bAllTokens + BB_BOTTOM_ROW) & BB_ALL_LEGAL_TOKENS;
360Board::TBoardArray Board::toArray()
const {
361 TBoardArray board{{0}};
362 const auto activePlayer = (m_movesLeft & 1 ? P_RED : P_YELLOW);
363 const auto inactivePlayer = opponent(activePlayer);
364 for (
auto c = 0; c < N_COLUMNS; ++c) {
365 for (
auto r = 0; r < N_ROWS; ++r) {
366 if (
const auto mask = getMaskColRow(c, r); m_bActivePTokens & mask) {
367 board[c][r] = activePlayer;
368 }
else if (m_bAllTokens & mask) {
369 board[c][r] = inactivePlayer;
371 board[c][r] = P_EMPTY;
378std::string Board::toString()
const {
379 std::stringstream ss;
381 const auto arr = toArray();
382 for (
int r = N_ROWS - 1; r >= 0; r--) {
383 for (
int c = 0; c < N_COLUMNS; c++) {
384 if (arr[c][r] == P_RED) {
386 }
else if (arr[c][r] == P_YELLOW) {
389 assert(arr[c][r] == P_EMPTY);
398Board Board::mirror()
const {
400 mB.m_movesLeft = m_movesLeft;
401 mB.m_bActivePTokens = mirrorBitBoard(m_bActivePTokens);
402 mB.m_bAllTokens = mirrorBitBoard(m_bAllTokens);
403 assert(uint64_t_popcnt(mB.m_bActivePTokens) ==
404 uint64_t_popcnt(m_bActivePTokens));
405 assert(uint64_t_popcnt(mB.m_bAllTokens) == uint64_t_popcnt(m_bAllTokens));