#include #include "connect4.h" void connect4_init(Connect4State *state) { for (int c = 0; c < COLS; c++) { for (int r = 0; r < ROWS; r++) { state->board[c][r] = 0; } } state->current_player = 1; state->status.kind = CONNECT4_IN_PROGRESS; state->status.winner = 0; } #define slot(state_p, col, row) (state_p)->board[(col)][(row)] #define check4(a, b, c, d) ((a) == (b) && (b) == (c) && (c) == (d) ? (a) : 0) Connect4Status connect4_check_status(Connect4State *state) { // check vertical for (int col = 0; col < COLS; col++) { for (int row = 0; row < 3; row++) { uint8_t check = check4(slot(state, col, row + 0), slot(state, col, row + 1), slot(state, col, row + 2), slot(state, col, row + 3)); if (check) { return (Connect4Status){.kind = CONNECT4_OVER, .winner = check}; } } } // check horizontal for (int col = 0; col < 4; col++) { for (int row = 0; row < ROWS; row++) { uint8_t check = check4(slot(state, col + 0, row), slot(state, col + 1, row), slot(state, col + 2, row), slot(state, col + 3, row)); if (check) { return (Connect4Status){.kind = CONNECT4_OVER, .winner = check}; } } } // check diagonal up for (int col = 0; col < 4; col++) { for (int row = 0; row < 3; row++) { uint8_t check = check4(slot(state, col + 0, row + 0), slot(state, col + 1, row + 1), slot(state, col + 2, row + 2), slot(state, col + 3, row + 3)); if (check) { return (Connect4Status){.kind = CONNECT4_OVER, .winner = check}; } } } // check diagonal down for (int col = 0; col < 4; col++) { for (int row = 3; row < 6; row++) { uint8_t check = check4(slot(state, col + 0, row - 0), slot(state, col + 1, row - 1), slot(state, col + 2, row - 2), slot(state, col + 3, row - 3)); if (check) { return (Connect4Status){.kind = CONNECT4_OVER, .winner = check}; } } } // check for possible moves for (int col = 0; col < COLS; col++) { if (slot(state, col, ROWS - 1) == 0) { return (Connect4Status){.kind = CONNECT4_IN_PROGRESS, .winner = 0}; } } // no possible moves, draw return (Connect4Status){.kind = CONNECT4_OVER, .winner = 0}; } bool connect4_check_action(Connect4State *state, uint8_t player, int col) { if (state->current_player != player) { // Not this player's turn. return false; } if (col < 0 || col >= COLS) { // Column out of bounds. return false; } if (slot(state, col, ROWS - 1) != 0) { // Column is full. return false; } return true; } void connect4_apply_action(Connect4State *state, uint8_t player, int col) { // Caller needs to assure this before calling. assert(connect4_check_action(state, player, col)); for (int row = 0; row < ROWS; row++) { if (slot(state, col, row) == 0) { slot(state, col, row) = player; Connect4Status status = connect4_check_status(state); state->status = status; if (status.kind == CONNECT4_OVER) { state->current_player = 0; } else { state->current_player = state->current_player == 1 ? 2 : 1; } return; } } assert(false); } void connect4_undo_action(Connect4State *state, uint8_t player, int col) { for (int row = ROWS - 1; row >= 0; row--) { if (slot(state, col, row) == player) { slot(state, col, row) = 0; state->status.kind = CONNECT4_IN_PROGRESS; state->status.winner = 0; state->current_player = player; return; } } assert(false); }