11 : m_bAllTokens{BB_EMPTY},
12 m_bActivePTokens{BB_EMPTY},
13 m_movesLeft{N_COLUMNS * N_ROWS} {
15 assert(uint64_t_popcnt(BB_ALL_LEGAL_TOKENS) == N_COLUMNS * N_ROWS);
16 assert(uint64_t_popcnt(BB_ILLEGAL) ==
17 CHAR_BIT *
sizeof(TBitBoard) - N_COLUMNS * N_ROWS);
18 assert(getRowMask(0) == getMask({0, 9, 18, 27, 36, 45, 54}));
19 assert(getRowMask(5) == getMask({5, 14, 23, 32, 41, 50, 59}));
22bool Board::setBoard(
const std::vector<int>& moveSequence) {
25 for (
const auto mv : moveSequence) {
26 if (!b.playMove(mv))
return false;
32bool Board::setBoard(
const std::string& moveSequence) {
33 for (
const char c : moveSequence) {
34 if (c <
'0' || c >
'6')
return false;
37 std::vector<int> moveSequenceInt;
38 moveSequenceInt.reserve(
40 std::transform(moveSequence.begin(), moveSequence.end(),
41 std::back_inserter(moveSequenceInt),
42 [](
const char c) { return c -
'0'; });
44 return setBoard(moveSequenceInt);
47Board::TBoardArray Board::transpose(
const TBoardArrayT& board) {
49 for (std::size_t c = 0; c < N_COLUMNS; ++c) {
50 for (std::size_t r = 0; r < N_ROWS; ++r) {
51 result[c][N_ROWS - r - 1] = board[r][c];
57bool Board::setBoard(
const TBoardArrayT& boardT) {
58 const auto board = transpose(boardT);
59 return setBoard(board);
62bool Board::setBoard(
const TBoardArray& board) {
63 if (!isValid(board)) {
66 TBitBoard allTokens{BB_EMPTY}, yellowTokens{BB_EMPTY};
67 for (
auto c = 0; c < N_COLUMNS; ++c) {
68 for (
auto r = 0; r < N_ROWS; ++r) {
69 auto val = board[c][r];
70 auto mask = getMaskColRow(c, r);
71 if (val == P_YELLOW) {
73 }
else if (val != P_RED) {
74 assert(val == P_EMPTY);
77 assert((allTokens & mask) == BB_EMPTY);
83 N_COLUMNS * N_ROWS -
static_cast<int>(uint64_t_popcnt(allTokens));
86 m_bActivePTokens = yellowTokens ^ (m_movesLeft & 1 ? allTokens : BB_EMPTY);
87 m_bAllTokens = allTokens;
92bool Board::isValid(
const TBoardArray& board) {
94 std::array<int, N_VALID_BOARD_VALUES> valCounts = {0UL};
95 for (
auto c =
static_cast<TBoardArray::size_type
>(0); c < N_COLUMNS; ++c) {
96 bool columnComplete =
false;
97 for (
auto r =
static_cast<TBoardArray::size_type
>(0); r < N_ROWS; ++r) {
98 const auto val = board[c][r];
100 if (val != P_RED && val != P_YELLOW && val != P_EMPTY) {
103 if (val == P_EMPTY) {
104 columnComplete =
true;
106 if (columnComplete)
return false;
113 if (
const auto diff = valCounts[P_YELLOW] - valCounts[P_RED];
114 diff < 0 || diff > 1) {
132bool Board::playMove(
const int column) {
134 if (!isLegalMove(column))
return false;
136 playMoveFast(column);
141bool Board::isLegalMove(
const int column)
const {
142 if (column < 0 || column >= N_COLUMNS)
return false;
144 TBitBoard columnMask = getColumnMask(column);
145 columnMask -= (UINT64_C(1) << (column * COLUMN_BIT_OFFSET));
146 return !(m_bAllTokens & columnMask & BB_TOP_ROW);
193Board::TBitBoard Board::winningPositions(
const TBitBoard x,
194 const bool verticals) {
196 TBitBoard wins = verticals ? (x << 1) & (x << 2) & (x << 3) : UINT64_C(0);
198 for (
auto b = COLUMN_BIT_OFFSET - 1; b <= COLUMN_BIT_OFFSET + 1; ++b) {
201 auto tmp = (x << b) & (x << 2 * b);
202 wins |= tmp & (x << 3 * b);
203 wins |= tmp & (x >> b);
206 tmp = (x >> b) & (x >> 2 * b);
207 wins |= tmp & (x << b);
208 wins |= tmp & (x >> 3 * b);
211 return wins & BB_ALL_LEGAL_TOKENS;
225bool Board::hasWin()
const {
227 const auto y = m_bActivePTokens ^ m_bAllTokens;
228 auto x = y & (y << 2);
229 if (x & (x << 1))
return true;
232 x = y & (y << 2 * COLUMN_BIT_OFFSET);
233 if (x & (x << COLUMN_BIT_OFFSET))
return true;
236 x = y & (y << 2 * (COLUMN_BIT_OFFSET - 1));
237 if (x & (x << (COLUMN_BIT_OFFSET - 1)))
return true;
240 x = y & (y << 2 * (COLUMN_BIT_OFFSET + 1));
241 if (x & (x << (COLUMN_BIT_OFFSET + 1)))
return true;
256MoveList Board::sortMoves(TBitBoard moves)
const {
260 const TBitBoard ownThreats = winningPositions(m_bActivePTokens,
false);
263 const auto mv = nextMove(moves);
264 assert(uint64_t_popcnt(mv) == 1);
268 winningPositions(m_bActivePTokens ^ mv,
true) & ~(m_bAllTokens ^ mv);
269 auto numThreats =
static_cast<int>(uint64_t_popcnt(threats));
273 if (ownThreats & (mv << 1)) numThreats--;
275 mvList.insert(mv, numThreats);
282Board::TBitBoard Board::findThreats(TBitBoard moves) {
283 auto threats = winningPositions(m_bActivePTokens,
true) & ~m_bAllTokens;
286 auto curNumThreats = uint64_t_popcnt(threats);
287 auto threatMoves = UINT64_C(0);
289 auto mv = lsb(moves);
298 winningPositions(m_bActivePTokens ^ mv,
true) & ~(m_bAllTokens ^ mv);
300 if (uint64_t_popcnt(threats & (moves ^ mv)) > 1)
303 if (
const auto numThreats = uint64_t_popcnt(threats);
304 numThreats > curNumThreats) {
313Board::TBitBoard Board::findOddThreats(TBitBoard moves) {
314 constexpr auto ODD_ROWS = getRowMask(2) | getRowMask(4);
315 auto threats = winningPositions(m_bActivePTokens,
true) & ~m_bAllTokens;
318 const auto curNumThreats = uint64_t_popcnt(threats & ODD_ROWS);
319 auto threatMoves = UINT64_C(0);
321 const auto mv = lsb(moves);
324 winningPositions(m_bActivePTokens ^ mv,
true) & ~(m_bAllTokens ^ mv);
325 if (
const auto numThreats = uint64_t_popcnt(threats & ODD_ROWS);
326 numThreats > curNumThreats) {
376bool Board::canWin()
const {
377 return winningPositions(m_bActivePTokens,
true) &
378 (m_bAllTokens + BB_BOTTOM_ROW);
381bool Board::canWin(
int column)
const {
382 return isLegalMove(column) &&
383 (winningPositions(m_bActivePTokens,
true) &
384 (m_bAllTokens + BB_BOTTOM_ROW) & getColumnMask(column));
387Board::TBitBoard Board::generateMoves()
const {
388 return (m_bAllTokens + BB_BOTTOM_ROW) & BB_ALL_LEGAL_TOKENS;
391Board::TBoardArray Board::toArray()
const {
392 TBoardArray board{{0}};
393 const auto activePlayer = (m_movesLeft & 1 ? P_RED : P_YELLOW);
394 const auto inactivePlayer = opponent(activePlayer);
395 for (
auto c = 0; c < N_COLUMNS; ++c) {
396 for (
auto r = 0; r < N_ROWS; ++r) {
397 if (
const auto mask = getMaskColRow(c, r); m_bActivePTokens & mask) {
398 board[c][r] = activePlayer;
399 }
else if (m_bAllTokens & mask) {
400 board[c][r] = inactivePlayer;
402 board[c][r] = P_EMPTY;
409std::string Board::toString()
const {
410 std::stringstream ss;
412 const auto arr = toArray();
413 for (
int r = N_ROWS - 1; r >= 0; r--) {
414 for (
int c = 0; c < N_COLUMNS; c++) {
415 if (arr[c][r] == P_RED) {
417 }
else if (arr[c][r] == P_YELLOW) {
420 assert(arr[c][r] == P_EMPTY);
429Board Board::mirror()
const {
431 mB.m_movesLeft = m_movesLeft;
432 mB.m_bActivePTokens = mirrorBitBoard(m_bActivePTokens);
433 mB.m_bAllTokens = mirrorBitBoard(m_bAllTokens);
434 assert(uint64_t_popcnt(mB.m_bActivePTokens) ==
435 uint64_t_popcnt(m_bActivePTokens));
436 assert(uint64_t_popcnt(mB.m_bAllTokens) == uint64_t_popcnt(m_bAllTokens));