A little app to simulate every possible move in Connect Four.

Adjustable board size

Changed files
+85 -52
+52 -41
core.cpp
···
struct WinningBoardCollection
{
uint8_t num_boards;
-
uint64_t* boards;
+
binary_board* boards;
};
MakeMoveResult make_move_table_partial[2][MAKE_MOVE_TABLE_PARTIAL_SIZE];
MakeMoveResult make_move_table_full[2][MAKE_MOVE_TABLE_FULL_SIZE];
-
const uint8_t CHECK_FOR_WIN_PROCESSED_COLUMN_TABLE_SIZE = 0b01111111;
+
const uint8_t CHECK_FOR_WIN_PROCESSED_COLUMN_TABLE_SIZE = MAX_FULL_COLUMN + 1;
uint8_t check_for_win_processed_column_table[2][CHECK_FOR_WIN_PROCESSED_COLUMN_TABLE_SIZE];
-
WinningBoardCollection check_for_win_processed_winning_boards_table[6][7];
+
WinningBoardCollection check_for_win_processed_winning_boards_table[NUM_ROWS][NUM_COLUMNS];
void init_board(BoardState state)
{
-
for (uint8_t col = 0; col < 7; ++col)
+
for (uint8_t col = 0; col < NUM_COLUMNS; ++col)
{
state[col] = 1;
}
···
inline int8_t get_board_value(BoardState state, uint8_t row, uint8_t col)
{
-
assert(row < 6 && col < 7);
+
assert(row < NUM_ROWS && col < NUM_COLUMNS);
return state[col] < (1 << (row + 1)) ? -1 : ((state[col] & (1 << row)) >> row);
}
int8_t make_move_column(uint8_t player, uint8_t* column_state)
{
-
if (*column_state >= 0b01000000)
+
if (*column_state >= MIN_FULL_COLUMN)
return -1;
uint8_t mask = *column_state; // 0011 0000 (example)
···
}
}
-
inline uint64_t bit_set(uint64_t num, uint8_t row, uint8_t col)
+
inline binary_board bit_set(binary_board num, uint8_t row, uint8_t col)
{
-
return num | ((uint64_t)1 << (col * 8 + (row)));
+
return num | ((binary_board)1 << (col * 8 + (row)));
}
void init_core()
···
for (uint8_t player = 0; player < 2; ++player)
{
make_move_table_partial[player][0] = MakeMoveResult { -1, 0 };
-
for (uint8_t column_state = 1; column_state < 0b01000000; ++column_state)
+
for (uint8_t column_state = 1; column_state < MAKE_MOVE_TABLE_PARTIAL_SIZE; ++column_state)
{
MakeMoveResult result;
result.column_state = column_state;
···
}
// Building check_for_win winning board lookup table
-
for (uint8_t table_row = 0; table_row < 6; ++table_row)
+
for (uint8_t table_row = 0; table_row < NUM_ROWS; ++table_row)
{
-
for (uint8_t table_col = 0; table_col < 7; ++table_col)
+
for (uint8_t table_col = 0; table_col < NUM_COLUMNS; ++table_col)
{
-
uint64_t boards[32];
+
binary_board boards[32];
uint8_t num_boards = 0;
// Vertical
-
for (uint8_t row_start = 0; row_start < 3; ++row_start)
+
for (uint8_t row_start = 0; row_start <= (NUM_ROWS - NUM_TO_WIN); ++row_start)
{
-
uint64_t winning_board_state = 0;
-
for (uint8_t row_offset = 0; row_offset < 4; ++row_offset)
+
binary_board winning_board_state = 0;
+
for (uint8_t row_offset = 0; row_offset < NUM_TO_WIN; ++row_offset)
{
winning_board_state = bit_set(winning_board_state, row_start + row_offset, table_col);
}
···
}
// Horizontal
-
for (uint8_t col_start = 0; col_start < 4; ++col_start)
+
for (uint8_t col_start = 0; col_start <= (NUM_COLUMNS - NUM_TO_WIN); ++col_start)
{
-
uint64_t winning_board_state = 0;
-
for (uint8_t col_offset = 0; col_offset < 4; ++col_offset)
+
binary_board winning_board_state = 0;
+
for (uint8_t col_offset = 0; col_offset < NUM_TO_WIN; ++col_offset)
{
winning_board_state = bit_set(winning_board_state, table_row, col_start + col_offset);
}
···
--diag_col;
}
-
while (diag_row + 4 <= 6 && diag_col + 4 <= 7)
+
while (diag_row + NUM_TO_WIN <= NUM_ROWS && diag_col + NUM_TO_WIN <= NUM_COLUMNS)
{
-
uint64_t winning_board_state = 0;
-
for (uint8_t i = 0; i < 4; ++i)
+
binary_board winning_board_state = 0;
+
for (uint8_t i = 0; i < NUM_TO_WIN; ++i)
{
winning_board_state = bit_set(winning_board_state, diag_row + i, diag_col + i);
}
···
{
int8_t diag_row = table_row;
uint8_t diag_col = table_col;
-
while (diag_row + 1 < 6 && diag_col > 0)
+
while (diag_row + 1 < NUM_ROWS && diag_col > 0)
{
++diag_row;
--diag_col;
}
-
while ((diag_row + 1) - 4 >= 0 && diag_col + 4 <= 7)
+
while ((diag_row + 1) - NUM_TO_WIN >= 0 && diag_col + NUM_TO_WIN <= NUM_COLUMNS)
{
-
uint64_t winning_board_state = 0;
-
for (uint8_t i = 0; i < 4; ++i)
+
binary_board winning_board_state = 0;
+
for (uint8_t i = 0; i < NUM_TO_WIN; ++i)
{
winning_board_state = bit_set(winning_board_state, diag_row - i, diag_col + i);
}
···
WinningBoardCollection board_collection;
board_collection.num_boards = num_boards;
-
board_collection.boards = (uint64_t*)malloc(num_boards * sizeof(uint64_t));
-
memcpy(board_collection.boards, boards, num_boards * sizeof(uint64_t));
+
board_collection.boards = (binary_board*)malloc(num_boards * sizeof(binary_board));
+
memcpy(board_collection.boards, boards, num_boards * sizeof(binary_board));
check_for_win_processed_winning_boards_table[table_row][table_col] = board_collection;
}
···
int8_t make_move(uint8_t player, uint8_t column, BoardState state)
{
-
assert(column >= 0 && column < 7);
+
assert(column >= 0 && column < NUM_COLUMNS);
return make_move_column(player, &state[column]);
}
···
bool check_for_win(BoardState state, uint8_t last_move_player, uint8_t last_move_row, uint8_t last_move_col)
{
assert(last_move_player <= 1);
-
assert(last_move_row < 6 && last_move_col < 7);
+
assert(last_move_row < NUM_ROWS && last_move_col < NUM_COLUMNS);
-
uint8_t processed_board_state[8] = { 0 };
-
for (uint8_t col = 0; col < 7; ++col)
+
uint8_t processed_board_state[NUM_COLUMNS_PROCESSED] = { 0 };
+
for (uint8_t col = 0; col < NUM_COLUMNS; ++col)
{
-
assert(state[col] < 0b10000000);
+
assert(state[col] < MAX_FULL_COLUMN + 1);
processed_board_state[col] = check_for_win_processed_column_table[last_move_player][state[col]];
}
-
uint64_t* processed_board_state_binary = (uint64_t*)processed_board_state;
+
binary_board* processed_board_state_binary = (binary_board*)processed_board_state;
WinningBoardCollection winning_boards = check_for_win_processed_winning_boards_table[last_move_row][last_move_col];
for (uint8_t i = 0; i < winning_boards.num_boards; ++i)
{
-
uint64_t winning_board_state = winning_boards.boards[i];
+
binary_board winning_board_state = winning_boards.boards[i];
if ((winning_board_state & *processed_board_state_binary) == winning_board_state)
{
return true;
···
int64_t score = 0;
};
-
void get_move_scores(int64_t final_scores[7])
+
void get_move_scores(int64_t final_scores[NUM_COLUMNS])
{
MoveEvalNode nodes[44];
init_board(nodes[0].state);
···
++nodes[depth].curr_move_column;
// If we've checked every column...
-
while (nodes[depth].curr_move_column >= 7)
+
while (nodes[depth].curr_move_column >= NUM_COLUMNS)
{
min_depth = depth < min_depth ? depth : min_depth;
···
void sprint_board(BoardState state, char* buffer)
{
-
snprintf(buffer, 256, "_1_2_3_4_5_6_7_\n");
-
buffer += 16;
-
for (int8_t row = 6 - 1; row >= 0; --row)
+
for (uint8_t i = 0; i < NUM_COLUMNS; ++i)
+
{
+
snprintf(buffer, 256, "_%d", i + 1);
+
buffer += 2;
+
}
+
+
snprintf(buffer, 256, "_\n");
+
buffer += 2;
+
for (int8_t row = NUM_ROWS - 1; row >= 0; --row)
{
snprintf(buffer, 256, "|");
buffer += 1;
-
for (uint8_t col = 0; col < 7; ++col)
+
for (uint8_t col = 0; col < NUM_COLUMNS; ++col)
{
switch (get_board_value(state, row, col))
{
···
snprintf(buffer, 256, "\n");
buffer += 1;
}
-
snprintf(buffer, 256, "‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾\n");
+
for (uint8_t i = 0; i < NUM_COLUMNS; ++i)
+
{
+
snprintf(buffer, 256, "--");
+
buffer += 2;
+
}
+
snprintf(buffer, 256, "‾\n");
}
void print_board(BoardState state)
+16 -7
core.h
···
#include <cassert>
#include <stdint.h>
-
typedef uint8_t BoardState[7];
+
const uint8_t NUM_ROWS = 6;
+
const uint8_t NUM_COLUMNS = 7;
+
const uint8_t NUM_TO_WIN = 4;
+
const uint8_t MIN_FULL_COLUMN = 0b01000000;
+
const uint8_t MAX_FULL_COLUMN = 0b01111111;
+
typedef uint64_t binary_board;
+
+
const uint8_t NUM_COLUMNS_PROCESSED = (uint8_t)sizeof(binary_board);
+
+
typedef uint8_t BoardState[NUM_COLUMNS];
void init_board(BoardState state);
void init_core();
···
uint8_t column_state;
};
-
const uint8_t MAKE_MOVE_TABLE_PARTIAL_SIZE = 0b01000000;
-
const uint8_t MAKE_MOVE_TABLE_FULL_SIZE = 0b01111111;
+
const uint8_t MAKE_MOVE_TABLE_PARTIAL_SIZE = MIN_FULL_COLUMN;
+
const uint8_t MAKE_MOVE_TABLE_FULL_SIZE = MAX_FULL_COLUMN + 1;
extern MakeMoveResult make_move_table_partial[2][MAKE_MOVE_TABLE_PARTIAL_SIZE];
extern MakeMoveResult make_move_table_full[2][MAKE_MOVE_TABLE_FULL_SIZE];
inline int8_t make_move_lookup_partial(uint8_t player, uint8_t column, BoardState state)
{
-
assert(column >= 0 && column < 7);
+
assert(column >= 0 && column < NUM_COLUMNS);
-
if (state[column] >= 0b01000000)
+
if (state[column] >= MAKE_MOVE_TABLE_PARTIAL_SIZE)
return -1;
MakeMoveResult result = make_move_table_partial[player][state[column]];
···
inline int8_t make_move_lookup_full(uint8_t player, uint8_t column, BoardState state)
{
-
assert(column >= 0 && column < 7);
+
assert(column >= 0 && column < NUM_COLUMNS);
MakeMoveResult result = make_move_table_full[player][state[column]];
state[column] = result.column_state;
···
bool check_for_win(BoardState state, uint8_t last_move_player, uint8_t last_move_row, uint8_t last_move_col);
bool check_for_win(BoardState state);
-
void get_move_scores(int64_t final_scores[7]);
+
void get_move_scores(int64_t final_scores[NUM_COLUMNS]);
void sprint_board(BoardState state, char* buffer);
void print_board(BoardState state);
+17 -4
main.cpp
···
// }
// }
-
while (true)
-
{
+
// while (true)
+
// {
// // Print board
// printf("_1_2_3_4_5_6_7_\n");
// for (int8_t row = 6 - 1; row >= 0; --row)
···
// break;
// }
-
int64_t final_scores[7];
+
int64_t final_scores[NUM_COLUMNS];
get_move_scores(final_scores);
+
printf("final scores: ");
+
for (uint8_t i = 0; i < NUM_COLUMNS; ++i)
+
{
+
printf("%d", final_scores[i]);
+
if (i + 1 < NUM_COLUMNS)
+
{
+
printf(" | ");
+
}
+
else
+
{
+
printf("\n");
+
}
+
}
// uint64_t eval_end_time = SDL_GetPerformanceCounter();
// printf("Time to finish evaluation: %ld\n", (eval_end_time - eval_start_time) / 1000);
···
// }
// printf("\033[2J");
-
}
+
// }
return 0;
}