Remove more CRs.
[ttt.git] / ver0 / ttt.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <memory.h>
4 #include <time.h>
5 #include "ttt.h"
6
7 SQUARE g_sComputerPlays = O_MARK;             // what side comp plays
8
9 //+----------------------------------------------------------------------------
10 //
11 // Function:  SquareContentsToChar
12 //
13 // Synopsis:  Helper function for DrawBoard
14 //
15 // Arguments: IN SQUARE s - a square to return a char to represent
16 //            
17 // Returns:   char - character representing square
18 //
19 //+----------------------------------------------------------------------------
20 char SquareContentsToChar(IN SQUARE s)
21 {
22     static char c;
23     switch(s)
24     {
25         case X_MARK:
26             c = 'X';
27             break;
28         case O_MARK:
29             c = 'O';
30             break;
31         default:
32             c = '_';
33             break;
34     }
35     return(c);
36 }
37
38 //+----------------------------------------------------------------------------
39 //
40 // Function:  DrawBoard
41 //
42 // Synopsis:  Draw the board
43 //
44 // Arguments: IN POSITION *p - pointer to a position whose board to draw
45 //            
46 // Returns:   void
47 //
48 //+----------------------------------------------------------------------------
49 void DrawBoard(IN POSITION *p)
50 {
51     COORD x, y;
52
53     for (y = 0; y < BOARD_SIZE; y++)
54     {
55         for (x = 0; x < BOARD_SIZE; x++)
56         {
57             printf("%c ", SquareContentsToChar(p->sBoard[y][x]));
58         }
59         printf("\n");
60     }
61     printf("\n%c to move.\n", SquareContentsToChar(p->sWhoseTurn));
62 }
63
64 //+----------------------------------------------------------------------------
65 //
66 // Function:  ClearBoard
67 //
68 // Synopsis:  Clear the board
69 //
70 // Arguments: IN OUT POSITION *p - pointer to position whose board to clear
71 //            
72 // Returns:   void
73 //
74 //+----------------------------------------------------------------------------
75 void ClearBoard(IN OUT POSITION *p)
76 {
77     memset(p->sBoard, 0, sizeof(p->sBoard));
78     p->sWhoseTurn = X_MARK;
79     p->uNumEmpty = (BOARD_SIZE * BOARD_SIZE);
80 }
81
82 //+----------------------------------------------------------------------------
83 //
84 // Function:  IsLegalMove
85 //
86 // Synopsis:  Determine if a given move is legal on a given board
87 //
88 // Arguments: IN POSITION *p - the board to play the move on
89 //            IN MOVE *m - the move in question
90 //            
91 // Returns:   BOOL - TRUE if it's legal, FALSE otherwise
92 //
93 //+----------------------------------------------------------------------------
94 BOOL IsLegalMove(IN POSITION *p, IN MOVE *m)
95 {
96     if ((m->cVpos < BOARD_SIZE) && (m->cHpos < BOARD_SIZE))
97     {
98         if (IS_SQUARE_EMPTY(p->sBoard[m->cVpos][m->cHpos]))
99         {
100             return(TRUE);
101         }
102     }
103     return(FALSE);
104 }
105
106 //+----------------------------------------------------------------------------
107 //
108 // Function:  GetHumanMove
109 //
110 // Synopsis:  Ask the human for a move
111 //
112 // Arguments: IN POSITION *p - the current board
113 //            OUT MOVE *m - the move the human made; this struct is populated
114 //                      as a side-effect of this function.
115 //            
116 // Returns:   void* (populates the move struct)
117 //
118 //+----------------------------------------------------------------------------
119 void GetHumanMove(IN POSITION *p, OUT MOVE *m)
120 {
121     unsigned int x;
122
123     do
124     {
125         printf("Enter your move number: ");
126         scanf("%u", &x);
127         
128         m->cHpos = (x % BOARD_SIZE);
129         m->cVpos = (x / BOARD_SIZE);
130         m->sMark = g_sComputerPlays * -1;
131     }
132     while(FALSE == IsLegalMove(p, m));
133 }
134
135 //+----------------------------------------------------------------------------
136 //
137 // Function:  SearchForComputerMove
138 //
139 // Synopsis:  Use our sophisticated search algorithm to find a computer
140 //            move
141 //
142 // Arguments: IN POSITION *p - the current board
143 //            OUT MOVE *m - the move the computer chooses; this move struct
144 //                      is populated as a side-effect of this function.
145 //            
146 // Returns:   void* (populates move struct)
147 //
148 //+----------------------------------------------------------------------------
149 void SearchForComputerMove(IN POSITION *p, OUT MOVE *m)
150 {
151     unsigned int x;
152
153     do
154     {
155         x = rand() % (BOARD_SIZE * BOARD_SIZE);
156         m->cHpos = (x % BOARD_SIZE);
157         m->cVpos = (x / BOARD_SIZE);
158         m->sMark = g_sComputerPlays;
159     }
160     while(FALSE == IsLegalMove(p, m));
161 }
162
163 //+----------------------------------------------------------------------------
164 //
165 // Function:  MakeMove
166 //
167 // Synopsis:  Make a move on a board  
168 //
169 // Arguments: IN OUT POSITION *p - the board
170 //            IN MOVE *m - the move
171 //            
172 // Returns:   void
173 //
174 //+----------------------------------------------------------------------------
175 void MakeMove(IN OUT POSITION *p, IN MOVE *m)
176 {
177     if (TRUE == IsLegalMove(p, m))
178     {
179         p->sBoard[m->cVpos][m->cHpos] = m->sMark;
180         p->uNumEmpty--;
181         p->sWhoseTurn *= -1;
182     }
183 }
184
185 //+----------------------------------------------------------------------------
186 //
187 // Function:  GameOver
188 //
189 // Synopsis:  Is the game over? 
190 //
191 // Arguments: IN POSITION *p - the board
192 //            OUT SQUARE *psWhoWon - who won the game (if it's over)
193 //            
194 // Returns:   TRUE if the game is over.  Also sets psWhoWon telling
195 //            which side one if the game is over.
196 // 
197 //            FALSE if the game is not over.
198 //
199 //+----------------------------------------------------------------------------
200 BOOL GameOver(IN POSITION *p, OUT SQUARE *psWhoWon)
201 {
202     int iSum;
203     COORD x, y;
204
205     for (x = 0; x < BOARD_SIZE; x++)
206     {
207         iSum = 0;
208
209         for (y = 0; y < BOARD_SIZE; y++)
210         {
211             iSum += p->sBoard[x][y];
212         }
213         if (abs(iSum) == BOARD_SIZE) goto winner;
214     }
215
216     for (y = 0; y < BOARD_SIZE; y++)
217     {
218         iSum = 0;
219
220         for (x = 0; x < BOARD_SIZE; x++)
221         {
222             iSum += p->sBoard[x][y];
223         }
224         if (abs(iSum) == BOARD_SIZE) goto winner;
225     }
226
227     iSum = 0;
228     for (x = 0; x < BOARD_SIZE; x++)
229     {
230         iSum += p->sBoard[x][x];
231     }
232     if (abs(iSum) == BOARD_SIZE) goto winner;
233
234     iSum = 0;
235     for (x = 0; x < BOARD_SIZE; x++)
236     {
237         iSum += p->sBoard[x][(BOARD_SIZE - 1 - x)];
238     }
239     if (abs(iSum) == BOARD_SIZE) goto winner;
240     
241     *psWhoWon = EMPTY;
242     if (p->uNumEmpty == 0)
243     {
244         return(TRUE);
245     }
246     else
247     {
248         return(FALSE);
249     }
250
251  winner:
252     *psWhoWon = (iSum / BOARD_SIZE);
253     return(TRUE);
254 }
255
256 //+----------------------------------------------------------------------------
257 //
258 // Function:  main
259 //
260 // Synopsis:  The program entry point and main game loop.
261 //
262 // Arguments: void
263 //            
264 // Returns:   int
265 //
266 //+----------------------------------------------------------------------------
267 int main(void)
268 {
269     POSITION p;
270     MOVE mv;
271     SQUARE sResult;
272
273     //
274     // Randomize: the random numbers returned by rand() will be based on
275     // the system clock when the program starts up.
276     //
277     srand(time(0));
278
279     //
280     // Setup the board and draw it once.
281     //
282     ClearBoard(&p);
283     DrawBoard(&p);
284
285     //
286     // Main game loop
287     //
288     do
289     {
290         // 
291         // See whose turn it is -- the human's or the computers -- and
292         // get a move from whoever's turn it is.
293         //
294         if (p.sWhoseTurn == g_sComputerPlays)
295         {
296             SearchForComputerMove(&p, &mv);
297         }
298         else
299         {
300             GetHumanMove(&p, &mv);
301         }
302
303         //
304         // Make the move on the board and draw the board again.
305         //
306         MakeMove(&p, &mv);
307         DrawBoard(&p);
308     }
309     while(FALSE == GameOver(&p, &sResult));
310
311     //
312     // If we get here the game is over... see what happened.
313     //
314     switch(sResult)
315     {
316         case X_MARK:
317             printf("\nX's win.\n");
318             break;
319         case O_MARK:
320             printf("\nO's win.\n");
321             break;
322         default:
323             printf("Tie (what a surprise)\n");
324             break;
325     }
326
327     exit(0);
328 }
329