I implemented a version of the popular Tic Tac Toe game. It currently only supports two-player mode, but I'm planning to add artificial intelligence for user vs computer mode (which I don't know how, yet).
#include <stdio.h>
#include "cmdio.h" /* this defines getnum, yesno and select_menu */
#include <stdlib.h> /* exit */
typedef struct {
int x;
int y;
} BoardPosition;
typedef enum {
NONE = 0,
USER,
COMP
} PlayerType;
typedef struct {
PlayerType type;
char symbol;
} Player;
typedef enum {
HORIZONTAL = 0,
VERTICAL,
L_DIAGONAL,
R_DIAGONAL
} WinMethod;
typedef struct {
Player *player;
WinMethod method;
} Winner;
typedef char Board[3][3];
/* interactive functions */
static void print_board(Board *);
static BoardPosition get_pos(Board *);
/* utility functions */
static void update_board(Board *, const BoardPosition, const Player *);
static Winner who_won(Board *, Player *, Player *);
static size_t count_board(Board *);
static void play_turn(Board *, Player *);
int main(void)
{
Player player1 = { USER, 'X'};
Player player2 = { USER, 'O' };
/* The winner, for now. */
Winner winner = { NULL /* none */, 0 /* whatever */};
/* settle on configuration */
puts("I want to play as ..");
/* select_menu returns the index of the option the user chose */
if (select_menu( (const char *[]) { "X", "O" }, 2, "> " )) {
/* switch symbols */
player1.symbol = 'O';
player2.symbol = 'X';
}
puts("I want to play with ..");
if (select_menu( (const char *[]) { "User", "Computer" }, 2, "> " ))
player2.type = COMP;
/* initializing the board */
Board brd;
for (int x = 0; x < 3; ++x)
for (int y = 0; y < 3; ++y)
brd[x][y] = 0;
Player *player = &player1;
for (;;) {
/* print board */
print_board(&brd);
/* demand a position to fill */
printf("Player %d's turn ..\n", (player == &player1) ? 1 : 2);
play_turn(&brd, player);
/* check for win */
winner = who_won(&brd, &player1, &player2);
if (winner.player == NULL) { /* no one won */
/* check for draw */
if (count_board(&brd) == 9) {
puts("Draw!");
return 0;
}
/* switch turns */
player = (player == &player1) ? &player2 : &player1;
} else if (winner.player == &player1) { /* player1 won */
if (player1.type == COMP)
puts("Oh! You lost .. Nevermind ... You'll win next time!");
else
puts("Congratulations! Player 1 won!");
return 0;
} else { /* player2 won */
if (player2.type == COMP)
puts("Oh! You lost .. Nevermind ... You'll win next time!");
else
puts("Congratulations! Player 2 won!");
return 0;
}
}
/* should never be reached */
fputs("Internal Error!", stderr);
return 1;
}
static Winner who_won(brd, player1, player2)
Board *brd;
Player *player1, *player2;
{
int stat1 = 1;
int stat2 = 1;
// check horizontal, then vertical
for (int i = 0; i < 2; ++i) {
for (int x = 0; x < 3; ++x) {
for (int y = 0; y < 3; ++y) {
stat1 &= ((*brd)[i ? x : y][i ? y : x] == player1->symbol);
stat2 &= ((*brd)[i ? x : y][i ? y : x] == player2->symbol);
}
if (stat1)
return (Winner){player1, i};
if (stat2)
return (Winner){player2, i};
stat1 = 1;
stat2 = 1;
}
}
// check left diagonal, then right diagonal
for (int i = 0; i < 2; ++i) {
for (int d = (i ? 0 : 2); i ? (d < 3) : (d >= 0); i ? (++d) : (--d)) {
stat1 &= ((*brd)[d][d] == player1->symbol);
stat2 &= ((*brd)[d][d] == player2->symbol);
}
if (stat1)
return (Winner){player1, i + 2};
if (stat2)
return (Winner){player2, i + 2};
stat1 = 1;
stat2 = 1;
}
return (Winner){NULL, 0};
}
static void print_board(brd)
Board *brd;
{
char start = '1';
for (int x = 0; x < 3; ++x) {
for (int y = 0; y < 3; ++y) {
if ((*brd)[x][y])
printf("%c", (*brd)[x][y]);
else
printf("%c", start);
if (y < 2)
printf(" | ");
else
printf("\n");
start += sizeof(char);
}
}
}
static BoardPosition get_pos(brd)
Board *brd;
{
BoardPosition pos;
int p;
while (1) {
getnum(stdin, "Enter position: ", 1, 9, (long long *) &p);
/* determine real position */
int i = 0;
for (pos.x = 0; pos.x < 3; ++pos.x)
for (pos.y = 0; pos.y < 3; ++pos.y)
if (++i == p && !(*brd)[pos.x][pos.y])
return pos;
puts("Please provide an available position.");
}
return pos;
}
static void update_board(brd, pos, player)
Board *brd;
const BoardPosition pos;
const Player *player;
{
if (!player->type)
return;
(*brd)[pos.x][pos.y] = player->symbol;
}
static size_t count_board(brd)
Board *brd;
{
size_t count;
for (int x = 0; x < 3; ++x)
for (int y = 0; y < 3; ++y)
count += !!(*brd)[x][y];
return count;
}
static void play_turn(brd, player)
Board *brd;
Player *player;
{
if (player->type == USER) {
update_board(brd, get_pos(brd), player);
} else {
/* TODO: add artificial intelligence */
fputs("Artificial Intelligence not yet implemented\n", stderr);
exit(1);
}
}
Well, what do you think?