Skip to content

Instantly share code, notes, and snippets.

@ernestohs
Created December 2, 2024 16:56
Show Gist options
  • Save ernestohs/d5b0e1116ca99ab3b60597909a3c5d5b to your computer and use it in GitHub Desktop.
Save ernestohs/d5b0e1116ca99ab3b60597909a3c5d5b to your computer and use it in GitHub Desktop.
Ultimate Tic-Tac-Toe Bundle
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <termios.h>
#include <unistd.h>
#define BOARD_SIZE 3
#define EMPTY ' '
#define PLAYER_X 'X'
#define PLAYER_O 'O'
#define KEY_ESC 27
#define KEY_W 119
#define KEY_S 115
#define KEY_A 97
#define KEY_D 100
#define KEY_ENTER 10
typedef struct {
char small_boards[BOARD_SIZE][BOARD_SIZE][BOARD_SIZE][BOARD_SIZE];
char big_board[BOARD_SIZE][BOARD_SIZE];
int current_player;
int active_board_row;
int active_board_col;
int cursor_row;
int cursor_col;
bool first_move;
} GameState;
static struct termios orig_termios;
void enableRawMode() {
tcgetattr(STDIN_FILENO, &orig_termios);
struct termios raw = orig_termios;
raw.c_lflag &= ~(ECHO | ICANON);
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}
void disableRawMode() {
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
}
char getChar() {
char c;
read(STDIN_FILENO, &c, 1);
return c;
}
void initializeGame(GameState* game) {
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
for (int k = 0; k < BOARD_SIZE; k++) {
for (int l = 0; l < BOARD_SIZE; l++) {
game->small_boards[i][j][k][l] = EMPTY;
}
}
game->big_board[i][j] = EMPTY;
}
}
game->current_player = 0;
game->active_board_row = 1;
game->active_board_col = 1;
game->cursor_row = 1;
game->cursor_col = 1;
game->first_move = true;
}
bool checkWin(const char board[BOARD_SIZE][BOARD_SIZE], char player) {
// Check rows
for (int i = 0; i < BOARD_SIZE; i++) {
if (board[i][0] == player && board[i][1] == player && board[i][2] == player) {
return true;
}
}
// Check columns
for (int i = 0; i < BOARD_SIZE; i++) {
if (board[0][i] == player && board[1][i] == player && board[2][i] == player) {
return true;
}
}
// Check diagonals
if (board[0][0] == player && board[1][1] == player && board[2][2] == player) {
return true;
}
if (board[0][2] == player && board[1][1] == player && board[2][0] == player) {
return true;
}
return false;
}
bool isBoardFull(const char board[BOARD_SIZE][BOARD_SIZE]) {
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] == EMPTY) {
return false;
}
}
}
return true;
}
char getCurrentPlayer(const GameState* game) {
return (game->current_player == 0) ? PLAYER_X : PLAYER_O;
}
void drawBoard(const GameState* game) {
printf("\033[2J\033[H");
printf(" ");
for (int i = 0; i < BOARD_SIZE * 3; i++) {
printf("%d ", i);
}
printf("\n");
for (int big_row = 0; big_row < BOARD_SIZE; big_row++) {
for (int small_row = 0; small_row < BOARD_SIZE; small_row++) {
printf("%2d ", big_row * 3 + small_row);
for (int big_col = 0; big_col < BOARD_SIZE; big_col++) {
for (int small_col = 0; small_col < BOARD_SIZE; small_col++) {
char symbol = game->small_boards[big_row][big_col][small_row][small_col];
if (big_row == game->active_board_row && big_col == game->active_board_col &&
small_row == game->cursor_row && small_col == game->cursor_col) {
printf("\033[7m%c\033[0m", symbol);
} else if (big_row == game->active_board_row && big_col == game->active_board_col) {
printf("\033[1m%c\033[0m", symbol);
} else {
printf("%c", symbol);
}
printf(" ");
}
if (big_col < BOARD_SIZE - 1) printf("│");
}
printf("\n");
}
if (big_row < BOARD_SIZE - 1) {
printf(" ");
for (int i = 0; i < BOARD_SIZE * 11 - 1; i++) printf("─");
printf("\n");
}
}
printf("\nCurrent Player: %c\n", getCurrentPlayer(game));
if (!game->first_move) {
printf("Active Board: (%d,%d)\n", game->active_board_row, game->active_board_col);
} else {
printf("First move - play anywhere!\n");
}
printf("\nControls: WASD to move, Enter to place, ESC to quit\n");
}
bool isValidMove(const GameState* game) {
if (!game->first_move &&
game->active_board_row != -1 &&
(game->active_board_row != game->active_board_row ||
game->active_board_col != game->active_board_col)) {
return false;
}
if (game->big_board[game->active_board_row][game->active_board_col] != EMPTY) {
return false;
}
return game->small_boards[game->active_board_row][game->active_board_col]
[game->cursor_row][game->cursor_col] == EMPTY;
}
void updateActiveBoard(GameState* game) {
int next_row = game->cursor_row;
int next_col = game->cursor_col;
if (game->big_board[next_row][next_col] != EMPTY) {
game->active_board_row = -1;
game->active_board_col = -1;
} else {
game->active_board_row = next_row;
game->active_board_col = next_col;
}
}
bool isGameOver(const GameState* game) {
if (checkWin(game->big_board, PLAYER_X) || checkWin(game->big_board, PLAYER_O)) {
return true;
}
for (int i = 0; i < BOARD_SIZE; i++) {
for (int j = 0; j < BOARD_SIZE; j++) {
if (game->big_board[i][j] == EMPTY) {
return false;
}
}
}
return true;
}
bool makeMove(GameState* game) {
char symbol = getCurrentPlayer(game);
game->small_boards[game->active_board_row][game->active_board_col]
[game->cursor_row][game->cursor_col] = symbol;
if (checkWin(game->small_boards[game->active_board_row][game->active_board_col], symbol)) {
game->big_board[game->active_board_row][game->active_board_col] = symbol;
} else if (isBoardFull(game->small_boards[game->active_board_row][game->active_board_col])) {
game->big_board[game->active_board_row][game->active_board_col] = 'T';
}
updateActiveBoard(game);
game->current_player = 1 - game->current_player;
game->first_move = false;
if (isGameOver(game)) {
drawBoard(game);
if (checkWin(game->big_board, PLAYER_X)) {
printf("\nPlayer X wins!\n");
} else if (checkWin(game->big_board, PLAYER_O)) {
printf("\nPlayer O wins!\n");
} else {
printf("\nGame is a tie!\n");
}
return false;
}
return true;
}
bool handleQuit() {
printf("\nDo you want to quit? (y/n): ");
char response = getChar();
return (response == 'y' || response == 'Y');
}
bool handleInput(GameState* game) {
char c = getChar();
if (c == KEY_ESC) {
return handleQuit();
}
switch (c) {
case KEY_W:
if (game->cursor_row > 0) game->cursor_row--;
break;
case KEY_S:
if (game->cursor_row < BOARD_SIZE - 1) game->cursor_row++;
break;
case KEY_A:
if (game->cursor_col > 0) game->cursor_col--;
break;
case KEY_D:
if (game->cursor_col < BOARD_SIZE - 1) game->cursor_col++;
break;
case KEY_ENTER:
if (isValidMove(game)) {
return makeMove(game);
}
break;
}
return true;
}
int main() {
GameState game;
initializeGame(&game);
enableRawMode();
bool running = true;
while (running) {
drawBoard(&game);
running = handleInput(&game);
}
disableRawMode();
printf("\nGame Over!\n");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment