/*1* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 20092* The President and Fellows of Harvard College.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. Neither the name of the University nor the names of its contributors13* may be used to endorse or promote products derived from this software14* without specific prior written permission.15*16* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829/*30* These are some constants we use in our program.31* EMPTY is used to indicate empty spaces in the board.32* X_MARKER and O_MARKER are used to indicate where each33* players has moved34* DIM indicates the size of the board. For now35* we assume a conventional 3x3 playing board.36*37* This should work once the basic system calls are complete.38*/3940#include <stdio.h>41#include <unistd.h>4243#define NEWLINE 01244#define EMPTY 045#define X_PLAYER 146#define O_PLAYER 247#define X_MARKER 148#define O_MARKER 249#define DIM 350#define DIMCHAR "2"51#define MAXSTRING 1005253typedef enum { FALSE, TRUE } bool;5455/* Function Declarations */56bool ask_yesno(const char *msg);57bool do_move(int player);58void initialize_board(void);59bool is_win(int x, int y);60int read_string(char *buf, int length);61void print_board(void);62void print_instructions(void);63bool win_column(int y, int marker);64bool win_diag_left(int x, int y, int marker);65bool win_diag_right(int x, int y, int marker);66bool win_row(int x, int marker);67bool Strcmp(const char *a, const char *b);686970/*71* The board is gloabally defined.72*/73int board[DIM][DIM];7475/* Console I/O routines */7677int78main()79{80bool win = FALSE;81int move, max_moves;82int player;8384print_instructions();85max_moves = DIM * DIM; /* Maximum number of moves in a game */8687while (TRUE) {88initialize_board();89for (move = 1; move <= max_moves; move++) {90player = move % 2 == 0 ? 2 : 1;91win = do_move(player);92print_board();93if (win) {94printf("Player %d, you WON!\n\n", player);95break; /* out of for loop */96}97}98/*99* If we got here by falling through the loop, then it is a100* tie game.101*/102if (!win)103printf("Tie Game!\n\n");104if (!ask_yesno("Do you wish to play again?"))105break; /* out of while loop */106}107return 0;108}109110/*111* print_instructions112* Displays the instructions for the game.113* Input114* None115* Output116* None117* Error118* None119*/120void121print_instructions(void)122{123printf("Welcome to tic-tac-toe!\n");124printf("Player 1 always plays X and player 2 always play O\n");125printf("Good luck!\n\n\n");126}127128void129/*130* print_board131* Display the DIM by DIM board.132* Input133* None.134* Output135* None.136* Errors137* None.138*/139print_board(void)140{141int i, j;142143/* Print labels across the top */144printf("\n 0 1 2\n");145146for (i = 0; i < DIM; i++) {147/* Print row labels */148printf(" %d ", i);149for (j = 0; j < DIM; j++) {150switch (board[i][j]) {151case EMPTY: printf(" "); break;152case X_MARKER: printf(" X "); break;153case O_MARKER: printf(" O "); break;154default: printf("???"); break;155}156}157printf("\n");158}159printf("\n");160}161162/*163* ask_yesno (taken from histo.c)164* This function prints out the message and asks the user to respond165* with either a yes or a no. It continues asking the questions until166* a valid reply is encountered and returns TRUE/FALSE indicating167* the answer (True for yes, false for no).168*169* Input170* Question to be asked.171* Output172* TRUE if response is yes173* FALSE if response is no174* Error175* None176*/177bool178ask_yesno(const char *msg)179{180char answer[MAXSTRING];181182while (TRUE) {183printf("%s [yes/no] ", msg);184if (read_string(answer, MAXSTRING) < 0)185return(FALSE);186if (Strcmp(answer, "yes"))187return(TRUE);188else if (Strcmp(answer, "no"))189return(FALSE);190else191printf("Please answer either yes or no\n");192}193}194195/*196* do_move197* Processes one player move. The player enters a row and column198* and we have to determine if the square is valid (on the board)199* and that there is no mark already there. We continue to ask200* for row/column pairs until we receive a valid combination.201* Then we mark the board, check for a win, and return.202*203* Input204* player Indicates which player (1 or 2) is moving205* Output206* TRUE if this move won the game.207* FALSE if this move did not win the game.208* Error209* None210*/211bool212do_move(int player)213{214int x, y;215bool first;216char answer[MAXSTRING];217char cx;218219first = TRUE;220printf("Player %d (%c), your move\n", player,221player == X_PLAYER ? 'X' : 'O');222223while (TRUE) {224printf("Which row [0-%d]: ", DIM-1);225if (read_string(answer, MAXSTRING) < 0)226return(FALSE);227cx = answer[0];228x = cx - '0';229if (x < 0 || x >= DIM) {230printf("Invalid row; must be >= 0 and < %d\n", DIM-1);231continue;232}233printf("Which column [0-%d]: ", DIM-1);234if (read_string(answer, MAXSTRING) < 0)235return(FALSE);236cx = answer[0];237y = cx - '0';238if (y < 0 || y >= DIM) {239printf("Invalid column; must be >= 0 and < %d\n",240DIM-1);241continue;242}243244if (board[x][y] != EMPTY) {245printf("That location is occupied; please try again\n");246print_board();247} else248break;249}250board[x][y] = player == X_PLAYER ? X_MARKER : O_MARKER;251252return(is_win(x, y));253254}255256/*257* is_win258* Checks if the move into position x, y created a tic-tac-toe.259* There are four possible ways to win -- horizontal, vertical,260* and the two diagonals; check each one using a separate routine.261*262* Four routines for checking the wins are:263* win_row The horizontal spots are all the same as this current mark.264* win_column The vertical spots are all the same as this current mark.265* win_diag_left The diagonal spots from left to right are all the266* same as the current mark.267* win_diag_right The diagonal spots from right to left are all the268* same as the current mark.269*270* Input (for all routines)271* x x coordinate272* y y coordinate273* marker the value just placed on the board.274* Output275* TRUE if player won276* FALSE otherwise277*/278bool279is_win(int x, int y)280{281int marker;282283marker = board[x][y];284285/*286* Note that C "short circuit evaluation". As soon as any one287* of these functions returns TRUE, we know that the expression288* is true. Therefore, we can return TRUE without executing289* any of the other routines.290*/291return(win_row(x, marker) || win_column(y, marker) ||292win_diag_left(x, y, marker) || win_diag_right(x, y, marker));293}294295/*296* Four helper functions for determining a win.297*/298bool299win_column(int y, int marker)300{301int i;302for (i = 0; i < DIM; i++)303if (board[i][y] != marker)304return(FALSE);305return(TRUE);306}307308bool309win_row(int x, int marker)310{311int i;312for (i = 0; i < DIM; i++)313if (board[x][i] != marker)314return(FALSE);315return(TRUE);316}317318bool319win_diag_left(int x, int y, int marker)320{321int i;322323/* Check that move is on the diagonal */324if (x != y)325return(FALSE);326327for (i = 0; i < DIM; i++)328if (board[i][i] != marker)329return(FALSE);330return(TRUE);331}332333bool334win_diag_right(int x, int y, int marker)335{336int i;337338/* Check that move is on the diagonal */339if (x + y != DIM - 1)340return(FALSE);341for (i = 0; i < DIM; i++)342if (board[i][DIM - 1 - i] != marker)343return(FALSE);344return(TRUE);345}346347void348initialize_board(void)349{350int i, j;351352for (i = 0; i < DIM; i++)353for (j = 0; j < DIM; j++)354board[i][j] = EMPTY;355}356357int358read_string(char *buf, int length)359{360int char_read;361int i;362363i = 0;364while ((char_read = getchar()) != EOF && char_read != NEWLINE &&365i < length) {366buf[i] = (char) char_read;367i++;368putchar(char_read);369}370371if (char_read == EOF)372return(-1);373374/*375* If the input overflows the buffer, just cut it short376* at length - 1 characters.377*/378if (i >= length)379i--;380buf[i] = 0;381return(i);382}383384bool385Strcmp(const char *a, const char *b)386{387if (a == NULL)388return(b == NULL);389if (b == NULL)390return(FALSE);391392while (*a && *b)393if (*a++ != *b++)394return(FALSE);395396return(*a == *b);397398}399400401