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 if (!b.play(moveSequence)) {
32bool Board::setBoard(
const std::string& moveSequence) {
36 if (!b.play(moveSequence))
return false;
43Board::TBoardArray Board::transpose(
const TBoardArrayT& board) {
45 for (std::size_t c = 0; c < N_COLUMNS; ++c) {
46 for (std::size_t r = 0; r < N_ROWS; ++r) {
47 result[c][N_ROWS - r - 1] = board[r][c];
53bool Board::setBoard(
const TBoardArrayT& board) {
54 const auto boardT = transpose(board);
55 return setBoard(boardT);
58bool Board::setBoard(
const TBoardArray& board) {
59 if (!isValid(board)) {
62 TBitBoard allTokens{BB_EMPTY}, yellowTokens{BB_EMPTY};
63 for (
auto c = 0; c < N_COLUMNS; ++c) {
64 for (
auto r = 0; r < N_ROWS; ++r) {
65 const auto val = board[c][r];
66 const auto mask = getMaskColRow(c, r);
67 if (val == P_YELLOW) {
69 }
else if (val != P_RED) {
70 assert(val == P_EMPTY);
73 assert((allTokens & mask) == BB_EMPTY);
79 N_COLUMNS * N_ROWS -
static_cast<int>(uint64_t_popcnt(allTokens));
82 m_bActivePTokens = yellowTokens ^ (m_movesLeft & 1 ? allTokens : BB_EMPTY);
83 m_bAllTokens = allTokens;
88bool Board::isValid(
const TBoardArray& board) {
90 std::array<int, N_VALID_BOARD_VALUES> valCounts = {0UL};
91 for (
auto c =
static_cast<TBoardArray::size_type
>(0); c < N_COLUMNS; ++c) {
92 bool columnComplete =
false;
93 for (
auto r =
static_cast<TBoardArray::size_type
>(0); r < N_ROWS; ++r) {
94 const auto val = board[c][r];
96 if (val != P_RED && val != P_YELLOW && val != P_EMPTY) {
100 columnComplete =
true;
102 if (columnComplete)
return false;
109 if (
const auto diff = valCounts[P_YELLOW] - valCounts[P_RED];
110 diff < 0 || diff > 1) {
128bool Board::play(
const int column) {
130 if (!isLegalMove(column))
return false;
132 playMoveFast(column);
137bool Board::play(
const std::vector<int>& moveSequence) {
140 for (
const auto mv : moveSequence) {
141 if (!b.play(mv))
return false;
147bool Board::play(
const std::string& moveSequence) {
148 for (
const char c : moveSequence) {
149 if (c <
'0' || c >=
'0' + N_COLUMNS)
return false;
152 std::vector<int> moveSequenceInt;
153 moveSequenceInt.reserve(
154 moveSequence.size());
155 std::transform(moveSequence.begin(), moveSequence.end(),
156 std::back_inserter(moveSequenceInt),
157 [](
const char c) { return c -
'0'; });
158 return play(moveSequenceInt);
161bool Board::isLegalMove(
const int column)
const {
162 if (column < 0 || column >= N_COLUMNS)
return false;
164 TBitBoard columnMask = getColumnMask(column);
165 columnMask -= (UINT64_C(1) << (column * COLUMN_BIT_OFFSET));
166 return !(m_bAllTokens & columnMask & BB_TOP_ROW);
213Board::TBitBoard Board::winningPositions(
const TBitBoard x,
214 const bool verticals) {
216 TBitBoard wins = verticals ? (x << 1) & (x << 2) & (x << 3) : UINT64_C(0);
218 for (
auto b = COLUMN_BIT_OFFSET - 1; b <= COLUMN_BIT_OFFSET + 1; ++b) {
221 auto tmp = (x << b) & (x << 2 * b);
222 wins |= tmp & (x << 3 * b);
223 wins |= tmp & (x >> b);
226 tmp = (x >> b) & (x >> 2 * b);
227 wins |= tmp & (x << b);
228 wins |= tmp & (x >> 3 * b);
231 return wins & BB_ALL_LEGAL_TOKENS;
245bool Board::hasWin()
const {
247 const auto y = m_bActivePTokens ^ m_bAllTokens;
248 auto x = y & (y << 2);
249 if (x & (x << 1))
return true;
252 x = y & (y << 2 * COLUMN_BIT_OFFSET);
253 if (x & (x << COLUMN_BIT_OFFSET))
return true;
256 x = y & (y << 2 * (COLUMN_BIT_OFFSET - 1));
257 if (x & (x << (COLUMN_BIT_OFFSET - 1)))
return true;
260 x = y & (y << 2 * (COLUMN_BIT_OFFSET + 1));
261 if (x & (x << (COLUMN_BIT_OFFSET + 1)))
return true;
276MoveList Board::sortMoves(TBitBoard moves)
const {
280 const TBitBoard ownThreats = winningPositions(m_bActivePTokens,
false);
283 const auto mv = nextMove(moves);
284 assert(uint64_t_popcnt(mv) == 1);
288 winningPositions(m_bActivePTokens ^ mv,
true) & ~(m_bAllTokens ^ mv);
289 auto numThreats =
static_cast<int>(uint64_t_popcnt(threats));
293 if (ownThreats & (mv << 1)) numThreats--;
295 mvList.insert(mv, numThreats);
302Board::TBitBoard Board::findThreats(TBitBoard moves) {
303 auto threats = winningPositions(m_bActivePTokens,
true) & ~m_bAllTokens;
306 auto curNumThreats = uint64_t_popcnt(threats);
307 auto threatMoves = UINT64_C(0);
309 auto mv = lsb(moves);
318 winningPositions(m_bActivePTokens ^ mv,
true) & ~(m_bAllTokens ^ mv);
320 if (uint64_t_popcnt(threats & (moves ^ mv)) > 1)
323 if (
const auto numThreats = uint64_t_popcnt(threats);
324 numThreats > curNumThreats) {
333Board::TBitBoard Board::findOddThreats(TBitBoard moves) {
334 constexpr auto ODD_ROWS = getRowMask(2) | getRowMask(4);
335 auto threats = winningPositions(m_bActivePTokens,
true) & ~m_bAllTokens;
338 const auto curNumThreats = uint64_t_popcnt(threats & ODD_ROWS);
339 auto threatMoves = UINT64_C(0);
341 const auto mv = lsb(moves);
344 winningPositions(m_bActivePTokens ^ mv,
true) & ~(m_bAllTokens ^ mv);
345 if (
const auto numThreats = uint64_t_popcnt(threats & ODD_ROWS);
346 numThreats > curNumThreats) {
396bool Board::canWin()
const {
397 return winningPositions(m_bActivePTokens,
true) &
398 (m_bAllTokens + BB_BOTTOM_ROW);
401bool Board::canWin(
int column)
const {
402 return isLegalMove(column) &&
403 (winningPositions(m_bActivePTokens,
true) &
404 (m_bAllTokens + BB_BOTTOM_ROW) & getColumnMask(column));
407Board::TBitBoard Board::legalMovesMask()
const {
408 return (m_bAllTokens + BB_BOTTOM_ROW) & BB_ALL_LEGAL_TOKENS;
413std::vector<int> Board::orderedLegalMovesFromMask(TBitBoard mvBits)
const {
414 auto sortedMoves = sortMoves(mvBits);
416 std::vector<int> cols;
417 cols.reserve(sortedMoves.getSize());
419 while (
auto mv = sortedMoves.pop()) {
420 int bit = ctz_u64(mv);
421 cols.push_back(bit / COLUMN_BIT_OFFSET);
427std::vector<int> Board::legalMovesFromMask(TBitBoard mvBits)
const {
428 auto bits = bits_set(mvBits);
430 std::transform(bits.begin(), bits.end(), bits.begin(),
431 [](
int bit) { return bit / COLUMN_BIT_OFFSET; });
436std::vector<int> Board::legalMoves(
bool nonLosing,
bool orderMoves)
const {
438 TBitBoard mvBits = nonLosing ? generateNonLosingMoves() : legalMovesMask();
439 return orderMoves ? orderedLegalMovesFromMask(mvBits)
440 : legalMovesFromMask(mvBits);
443Board::TBoardArray Board::toArray()
const {
444 TBoardArray board{{0}};
445 const auto activePlayer = (m_movesLeft & 1 ? P_RED : P_YELLOW);
446 const auto inactivePlayer = opponent(activePlayer);
447 for (
auto c = 0; c < N_COLUMNS; ++c) {
448 for (
auto r = 0; r < N_ROWS; ++r) {
449 if (
const auto mask = getMaskColRow(c, r); m_bActivePTokens & mask) {
450 board[c][r] = activePlayer;
451 }
else if (m_bAllTokens & mask) {
452 board[c][r] = inactivePlayer;
454 board[c][r] = P_EMPTY;
461std::string Board::toString()
const {
462 std::stringstream ss;
464 const auto arr = toArray();
465 for (
int r = N_ROWS - 1; r >= 0; r--) {
466 for (
int c = 0; c < N_COLUMNS; c++) {
467 if (arr[c][r] == P_RED) {
469 }
else if (arr[c][r] == P_YELLOW) {
472 assert(arr[c][r] == P_EMPTY);
481Board Board::mirror()
const {
483 mB.m_movesLeft = m_movesLeft;
484 mB.m_bActivePTokens = mirrorBitBoard(m_bActivePTokens);
485 mB.m_bAllTokens = mirrorBitBoard(m_bAllTokens);
486 assert(uint64_t_popcnt(mB.m_bActivePTokens) ==
487 uint64_t_popcnt(m_bActivePTokens));
488 assert(uint64_t_popcnt(mB.m_bAllTokens) == uint64_t_popcnt(m_bAllTokens));