Skip to content

Instantly share code, notes, and snippets.

@lakshayg
Last active July 9, 2024 21:39
Show Gist options
  • Save lakshayg/669978717e509a3aaa39758d48a89b4b to your computer and use it in GitHub Desktop.
Save lakshayg/669978717e509a3aaa39758d48a89b4b to your computer and use it in GitHub Desktop.
Tic-Tac-Toe in C
#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