/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- main
- print_instructions
- print_board
- ask_yesno
- do_move
- is_win
- win_column
- win_row
- win_diag_left
- win_diag_right
- initialize_board
- read_string
- Strcmp
1 /*
2 * Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009
3 * The President and Fellows of Harvard College.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 /*
31 * These are some constants we use in our program.
32 * EMPTY is used to indicate empty spaces in the board.
33 * X_MARKER and O_MARKER are used to indicate where each
34 * players has moved
35 * DIM indicates the size of the board. For now
36 * we assume a conventional 3x3 playing board.
37 *
38 * This should work once the basic system calls are complete.
39 */
40
41 #include <stdio.h>
42 #include <unistd.h>
43
44 #define NEWLINE 012
45 #define EMPTY 0
46 #define X_PLAYER 1
47 #define O_PLAYER 2
48 #define X_MARKER 1
49 #define O_MARKER 2
50 #define DIM 3
51 #define DIMCHAR "2"
52 #define MAXSTRING 100
53
54 typedef enum { FALSE, TRUE } bool;
55
56 /* Function Declarations */
57 bool ask_yesno(const char *msg);
58 bool do_move(int player);
59 void initialize_board(void);
60 bool is_win(int x, int y);
61 int read_string(char *buf, int length);
62 void print_board(void);
63 void print_instructions(void);
64 bool win_column(int y, int marker);
65 bool win_diag_left(int x, int y, int marker);
66 bool win_diag_right(int x, int y, int marker);
67 bool win_row(int x, int marker);
68 bool Strcmp(const char *a, const char *b);
69
70
71 /*
72 * The board is gloabally defined.
73 */
74 int board[DIM][DIM];
75
76 /* Console I/O routines */
77
78 int
79 main()
80 {
81 bool win = FALSE;
82 int move, max_moves;
83 int player;
84
85 print_instructions();
86 max_moves = DIM * DIM; /* Maximum number of moves in a game */
87
88 while (TRUE) {
89 initialize_board();
90 for (move = 1; move <= max_moves; move++) {
91 player = move % 2 == 0 ? 2 : 1;
92 win = do_move(player);
93 print_board();
94 if (win) {
95 printf("Player %d, you WON!\n\n", player);
96 break; /* out of for loop */
97 }
98 }
99 /*
100 * If we got here by falling through the loop, then it is a
101 * tie game.
102 */
103 if (!win)
104 printf("Tie Game!\n\n");
105 if (!ask_yesno("Do you wish to play again?"))
106 break; /* out of while loop */
107 }
108 return 0;
109 }
110
111 /*
112 * print_instructions
113 * Displays the instructions for the game.
114 * Input
115 * None
116 * Output
117 * None
118 * Error
119 * None
120 */
121 void
122 print_instructions(void)
123 {
124 printf("Welcome to tic-tac-toe!\n");
125 printf("Player 1 always plays X and player 2 always play O\n");
126 printf("Good luck!\n\n\n");
127 }
128
129 void
130 /*
131 * print_board
132 * Display the DIM by DIM board.
133 * Input
134 * None.
135 * Output
136 * None.
137 * Errors
138 * None.
139 */
140 print_board(void)
141 {
142 int i, j;
143
144 /* Print labels across the top */
145 printf("\n 0 1 2\n");
146
147 for (i = 0; i < DIM; i++) {
148 /* Print row labels */
149 printf(" %d ", i);
150 for (j = 0; j < DIM; j++) {
151 switch (board[i][j]) {
152 case EMPTY: printf(" "); break;
153 case X_MARKER: printf(" X "); break;
154 case O_MARKER: printf(" O "); break;
155 default: printf("???"); break;
156 }
157 }
158 printf("\n");
159 }
160 printf("\n");
161 }
162
163 /*
164 * ask_yesno (taken from histo.c)
165 * This function prints out the message and asks the user to respond
166 * with either a yes or a no. It continues asking the questions until
167 * a valid reply is encountered and returns TRUE/FALSE indicating
168 * the answer (True for yes, false for no).
169 *
170 * Input
171 * Question to be asked.
172 * Output
173 * TRUE if response is yes
174 * FALSE if response is no
175 * Error
176 * None
177 */
178 bool
179 ask_yesno(const char *msg)
180 {
181 char answer[MAXSTRING];
182
183 while (TRUE) {
184 printf("%s [yes/no] ", msg);
185 if (read_string(answer, MAXSTRING) < 0)
186 return(FALSE);
187 if (Strcmp(answer, "yes"))
188 return(TRUE);
189 else if (Strcmp(answer, "no"))
190 return(FALSE);
191 else
192 printf("Please answer either yes or no\n");
193 }
194 }
195
196 /*
197 * do_move
198 * Processes one player move. The player enters a row and column
199 * and we have to determine if the square is valid (on the board)
200 * and that there is no mark already there. We continue to ask
201 * for row/column pairs until we receive a valid combination.
202 * Then we mark the board, check for a win, and return.
203 *
204 * Input
205 * player Indicates which player (1 or 2) is moving
206 * Output
207 * TRUE if this move won the game.
208 * FALSE if this move did not win the game.
209 * Error
210 * None
211 */
212 bool
213 do_move(int player)
214 {
215 int x, y;
216 bool first;
217 char answer[MAXSTRING];
218 char cx;
219
220 first = TRUE;
221 printf("Player %d (%c), your move\n", player,
222 player == X_PLAYER ? 'X' : 'O');
223
224 while (TRUE) {
225 printf("Which row [0-%d]: ", DIM-1);
226 if (read_string(answer, MAXSTRING) < 0)
227 return(FALSE);
228 cx = answer[0];
229 x = cx - '0';
230 if (x < 0 || x >= DIM) {
231 printf("Invalid row; must be >= 0 and < %d\n", DIM-1);
232 continue;
233 }
234 printf("Which column [0-%d]: ", DIM-1);
235 if (read_string(answer, MAXSTRING) < 0)
236 return(FALSE);
237 cx = answer[0];
238 y = cx - '0';
239 if (y < 0 || y >= DIM) {
240 printf("Invalid column; must be >= 0 and < %d\n",
241 DIM-1);
242 continue;
243 }
244
245 if (board[x][y] != EMPTY) {
246 printf("That location is occupied; please try again\n");
247 print_board();
248 } else
249 break;
250 }
251 board[x][y] = player == X_PLAYER ? X_MARKER : O_MARKER;
252
253 return(is_win(x, y));
254
255 }
256
257 /*
258 * is_win
259 * Checks if the move into position x, y created a tic-tac-toe.
260 * There are four possible ways to win -- horizontal, vertical,
261 * and the two diagonals; check each one using a separate routine.
262 *
263 * Four routines for checking the wins are:
264 * win_row The horizontal spots are all the same as this current mark.
265 * win_column The vertical spots are all the same as this current mark.
266 * win_diag_left The diagonal spots from left to right are all the
267 * same as the current mark.
268 * win_diag_right The diagonal spots from right to left are all the
269 * same as the current mark.
270 *
271 * Input (for all routines)
272 * x x coordinate
273 * y y coordinate
274 * marker the value just placed on the board.
275 * Output
276 * TRUE if player won
277 * FALSE otherwise
278 */
279 bool
280 is_win(int x, int y)
281 {
282 int marker;
283
284 marker = board[x][y];
285
286 /*
287 * Note that C "short circuit evaluation". As soon as any one
288 * of these functions returns TRUE, we know that the expression
289 * is true. Therefore, we can return TRUE without executing
290 * any of the other routines.
291 */
292 return(win_row(x, marker) || win_column(y, marker) ||
293 win_diag_left(x, y, marker) || win_diag_right(x, y, marker));
294 }
295
296 /*
297 * Four helper functions for determining a win.
298 */
299 bool
300 win_column(int y, int marker)
301 {
302 int i;
303 for (i = 0; i < DIM; i++)
304 if (board[i][y] != marker)
305 return(FALSE);
306 return(TRUE);
307 }
308
309 bool
310 win_row(int x, int marker)
311 {
312 int i;
313 for (i = 0; i < DIM; i++)
314 if (board[x][i] != marker)
315 return(FALSE);
316 return(TRUE);
317 }
318
319 bool
320 win_diag_left(int x, int y, int marker)
321 {
322 int i;
323
324 /* Check that move is on the diagonal */
325 if (x != y)
326 return(FALSE);
327
328 for (i = 0; i < DIM; i++)
329 if (board[i][i] != marker)
330 return(FALSE);
331 return(TRUE);
332 }
333
334 bool
335 win_diag_right(int x, int y, int marker)
336 {
337 int i;
338
339 /* Check that move is on the diagonal */
340 if (x + y != DIM - 1)
341 return(FALSE);
342 for (i = 0; i < DIM; i++)
343 if (board[i][DIM - 1 - i] != marker)
344 return(FALSE);
345 return(TRUE);
346 }
347
348 void
349 initialize_board(void)
350 {
351 int i, j;
352
353 for (i = 0; i < DIM; i++)
354 for (j = 0; j < DIM; j++)
355 board[i][j] = EMPTY;
356 }
357
358 int
359 read_string(char *buf, int length)
360 {
361 int char_read;
362 int i;
363
364 i = 0;
365 while ((char_read = getchar()) != EOF && char_read != NEWLINE &&
366 i < length) {
367 buf[i] = (char) char_read;
368 i++;
369 putchar(char_read);
370 }
371
372 if (char_read == EOF)
373 return(-1);
374
375 /*
376 * If the input overflows the buffer, just cut it short
377 * at length - 1 characters.
378 */
379 if (i >= length)
380 i--;
381 buf[i] = 0;
382 return(i);
383 }
384
385 bool
386 Strcmp(const char *a, const char *b)
387 {
388 if (a == NULL)
389 return(b == NULL);
390 if (b == NULL)
391 return(FALSE);
392
393 while (*a && *b)
394 if (*a++ != *b++)
395 return(FALSE);
396
397 return(*a == *b);
398
399 }