Last active
July 9, 2024 21:39
-
-
Save lakshayg/669978717e509a3aaa39758d48a89b4b to your computer and use it in GitHub Desktop.
Tic-Tac-Toe in C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <assert.h> | |
#include <stdbool.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <time.h> | |
/// TicTacToe ========================================================================================================== | |
typedef char Player; | |
const Player EMPTY = ' '; | |
const Player CROSS = 'x'; | |
const Player CIRCL = 'o'; | |
typedef struct | |
{ | |
union { | |
Player arr[9]; | |
Player mat[3][3]; | |
}; | |
int empty; | |
} __attribute__((aligned(16))) TicTacToe; | |
void init_game(TicTacToe *game) | |
{ | |
memset(game->arr, EMPTY, 9); | |
game->empty = 9; | |
} | |
void clear(TicTacToe *game, int k) | |
{ | |
assert(game->arr[k] != EMPTY); | |
game->arr[k] = EMPTY; | |
game->empty += 1; | |
} | |
// returns 2 if player won | |
// returns 1 if the game is a draw | |
// returns 0 if the game must go on | |
int play(TicTacToe *game, Player player, int k) | |
{ | |
assert(game->arr[k] == EMPTY); | |
game->arr[k] = player; | |
game->empty -= 1; | |
int r = k / 3; | |
int c = k % 3; | |
if (((game->mat[r][0] == player) & (game->mat[r][1] == player) & (game->mat[r][2] == player)) | | |
((game->mat[0][c] == player) & (game->mat[1][c] == player) & (game->mat[2][c] == player)) | | |
((game->mat[0][0] == player) & (game->mat[1][1] == player) & (game->mat[2][2] == player)) | | |
((game->mat[0][2] == player) & (game->mat[1][1] == player) & (game->mat[2][0] == player))) | |
{ | |
return 2; | |
} | |
else if (game->empty == 0) | |
{ | |
return 1; | |
} | |
return 0; | |
} | |
void print(TicTacToe *game) | |
{ | |
printf(" %c | %c | %c\n" | |
"---+---+---\n" | |
" %c | %c | %c\n" | |
"---+---+---\n" | |
" %c | %c | %c\n", | |
game->arr[0], game->arr[1], game->arr[2], | |
game->arr[3], game->arr[4], game->arr[5], | |
game->arr[6], game->arr[7], game->arr[8]); | |
} | |
///===================================================================================================================== | |
int best_outcome(TicTacToe *game, Player player, int *move) | |
{ | |
assert(player != EMPTY); | |
Player opponent = (player == CIRCL) ? CROSS : CIRCL; | |
// this array represents the outcomes from various moves | |
// table[0] -> moves that lose the game | |
// table[1] -> moves that draw the game | |
// table[2] -> moves that win the game | |
int table_size[3] = {0}; | |
int table[3][9]; | |
for (int k = 0; k < 9; ++k) | |
{ | |
if (game->arr[k] != EMPTY) | |
{ | |
continue; | |
} | |
int outcome = play(game, player, k); | |
if (outcome == 0) // we don't know if it's a win/draw/loss yet | |
{ | |
outcome = 2 - best_outcome(game, opponent, NULL); | |
} | |
int index = table_size[outcome]++; | |
table[outcome][index] = k; | |
clear(game, k); | |
} | |
int outcome = table_size[2] ? 2 : (table_size[1] ? 1 : 0); | |
if (move != NULL) | |
{ | |
int index = rand() % table_size[outcome]; | |
*move = table[outcome][index]; | |
} | |
return outcome; | |
} | |
char read_character() | |
{ | |
char c = 0; | |
char value = 0; | |
bool read = true; | |
while ((c = getchar()) != '\n' && c != EOF) | |
{ | |
if (read) | |
{ | |
value = c; | |
read = false; | |
} | |
} | |
return value; | |
} | |
void get_user_input(int *k) | |
{ | |
printf("move? "); | |
*k = read_character() - '0'; | |
} | |
int main() | |
{ | |
srand(time(NULL)); | |
TicTacToe game; | |
while (true) | |
{ | |
printf("Choose player [ox]: "); | |
char symbol = read_character(); | |
init_game(&game); | |
print(&game); | |
Player player = CROSS; | |
while (true) | |
{ | |
int k = 0; | |
player = (Player)((int)CIRCL + (int)CROSS - (int)player); | |
printf("\n"); | |
if (player == symbol) | |
{ | |
get_user_input(&k); | |
} | |
else | |
{ | |
best_outcome(&game, player, &k); | |
} | |
int result = play(&game, player, k); | |
print(&game); | |
if (result == 2) | |
{ | |
printf("%c wins\n", player); | |
break; | |
} | |
else if (result == 1) | |
{ | |
printf("it's a draw\n"); | |
break; | |
} | |
} | |
} | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment