Tic Tac Toe - Two-Player Game

Next, we'll implement a two-player game. To make moves, users click in the game board dragarea. The game board's function, make_move(...) must translate a mouse click into a move, make the move, check for a winner, and redraw the game status.

One new display function has been added: show_move(...). show_move() displays an 'X', 'O', or blank in each board position.

Three functions have been added to support the tic tac toe game: init_board(), get_move(...), and check_win(...). init_board() simply clears all board positions and sets the winner, x_turn, move_count, and last_move variables to appropriate values. get_move() translates a mouse click into a row and column position. check_win() returns TRUE if a player has won the game; FALSE otherwise.

Finally, new_game() has been implemented. new_game() initializes the board, board window, and status winow.


[...]
#define GAME_MENU "game_menu"
#define UNDO "undo"
#define INSET 5

#define X_WIN "X won!"
#define O_WIN "O won!"
#define TIE   "Tie game."
#define X_MOVE "X's turn."
#define O_MOVE "O's turn."

[...]

/*  ---  Prototypes - display functions    ---  */
void show_board();
void show_status();
void show_move(int move_x, int move_y);
/*  */
 
/*  ---  Prototypes - game functions       ---  */
void init_board();
void get_move(int mouse_x, int mouse_y, int *move_x, int *move_y);
bool check_win(int move_r, int move_c);
/*  */

void main(int argc, void *argv[])
{
	init_board();
	GSinterfaceinit(NAME, IFILE, BSTORE, "wc", G640x480x256, argc, argv);
	GSinterface();
}

void init_board()
{
	int i, j;
	
	winner = PNONE;
	x_turn = TRUE;
	move_count = 0;
	last_move = NO_MOVE;
	
	for (i=0; i<3; i++)	
		for (j=0; j<3; j++)
			board[i][j] = PNONE;
}

[...]

bool new_game()
{
	init_board();
	show_board();
	show_status();
	GSdisablemenuitem(GAME_MENU, UNDO);
	return TRUE;
}

[...]

bool make_move(int mouse_x, int mouse_y, DragStatusType status)
{
	int move_r, move_c;
	
	if ((status == MOUSE_CLICK) || (status == DRAG_FINAL)) {
		if ((move_count < MAX_MOVES) && (winner == PNONE)) {
			get_move(mouse_x, mouse_y, &move_r, &move_c);
			
			if (board[move_r][move_c] == PNONE) {
				move_count++;
				board[move_r][move_c] = x_turn? X : O;
				show_move(move_r, move_c);
				
				last_move = move_r*3 + move_c;
				GSenablemenuitem(GAME_MENU, UNDO);
				
				if (check_win(move_r, move_c))
					winner = x_turn? X : O;
				
				x_turn = !x_turn;
				
				show_status();
				}
			}
		}
	return TRUE;
}

void show_board()
{
	int x, y, wd, ht;
	int i, j;
	
	GSgetdragareaspecs(GAME_WIND, GAME_DRAG, &x, &y, &wd, &ht, NULL, NULL);
	GSsetcurrentnamedwindow(GAME_WIND);
	
	/* show board */
	
	GSsetlinesize(5);
	GSsetcolor(GSconvertcolor("black"));
	GSwdrawline(x+(wd/3), y, x+(wd/3), y+ht);
	GSwdrawline(x+(2*wd/3), y, x+(2*wd/3), y+ht);
	GSwdrawline(x, y+(ht/3), x+wd, y+(ht/3));
	GSwdrawline(x, y+(2*ht/3), x+wd, y+(2*ht/3));
	
	
	/* show moves */
	
	for (i=0; i<3; i++)
		for (j=0; j<3; j++)
			show_move(i, j);
}

void show_move(int move_r, int move_c)
{
	int x, y, wd, ht;
	int bx, by, bwd, bht;
	GSColor col;
	
	GSgetdragareaspecs(GAME_WIND, GAME_DRAG, &x, &y, &wd, &ht, NULL, &col);

	bx = x + (wd/3)*move_c + INSET;
	by = y + (ht/3)*move_r + INSET;
	bwd = wd/3 - 2*INSET;
	bht = ht/3 - 2*INSET;

	GSsetcurrentnamedwindow(GAME_WIND);
	GSsetcolor(GSconvertcolor("black"));
	
	if (board[move_r][move_c] == X) {
		GSwdrawline(bx, by, bx+bwd, by+bht);
		GSwdrawline(bx+bwd, by, bx, by+bht);
		}
	else if (board[move_r][move_c] == O)
		GSwdrawellipse(bx, by, bwd, bht);
	else {
		GSsetcolor(col);
		GSwfillrect(bx-1, by-1, bwd+2, bht+2);
		}
}

[...]

void get_move(int mouse_x, int mouse_y, int *move_r, int *move_c)
{
	int wd, ht;
	
	GSgetdragareaspecs(GAME_WIND, GAME_DRAG, NULL, NULL, &wd, &ht, NULL, NULL);
	*move_r = mouse_y / (ht/3);
	*move_c = mouse_x / (wd/3);
}

bool check_win(move_r, move_c)
{
	int i, j;
	
	if ((board[0][move_c] == board[1][move_c])
		&& (board[1][move_c] == board[2][move_c])
		&& (board[0][move_c] != PNONE))
			return TRUE;
			
	if ((board[move_r][0] == board[move_r][1])
		&& (board[move_r][1] == board[move_r][2])
		&& (board[move_r][0] != PNONE))
			return TRUE;
		
	if (move_r == move_c) {
		if ((board[0][0] == board[1][1])
			&& (board[1][1] == board[2][2])
			&& (board[0][0] != PNONE))
				return TRUE;
		}
	
	if (move_r == 2-move_c) {
		if ((board[0][2] == board[1][1])
			&& (board[1][1] == board[2][0])
			&& (board[0][2] != PNONE))
				return TRUE;
		}
	
	return FALSE;
}
[...]

back forward