ce655d7f4c9fd3af8fee8f8404894d92532d06f3
[progcomp2012.git] / manager / game.cpp
1 #include "game.h"
2
3 using namespace std;
4
5
6
7 Game* Game::theGame = NULL;
8 bool Game::gameCreated = false;
9
10 Game::Game(const char * redPath, const char * bluePath, const bool enableGraphics, double newStallTime, const bool allowIllegal, FILE * newLog, const  Piece::Colour & newReveal, int newMaxTurns, bool newPrintBoard) : red(NULL), blue(NULL), turn(Piece::RED), theBoard(10,10), graphicsEnabled(enableGraphics), stallTime(newStallTime), allowIllegalMoves(allowIllegal), log(newLog), reveal(newReveal), turnCount(0), input(NULL), maxTurns(newMaxTurns), printBoard(newPrintBoard)
11 {
12         gameCreated = false;
13         if (gameCreated)
14         {
15                 fprintf(stderr, "Game::Game - Error - Tried to create more than one Game!\n");
16                 exit(EXIT_FAILURE);
17         }
18         gameCreated = true;
19         Game::theGame = this;
20         signal(SIGPIPE, Game::HandleBrokenPipe);
21
22
23         if (graphicsEnabled && (!Graphics::Initialised()))
24                         Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32);
25
26         if (strcmp(redPath, "human") == 0)
27                 red = new Human_Controller(Piece::RED, graphicsEnabled);
28         else
29                 red = new AI_Controller(Piece::RED, redPath);
30         
31         
32         if (strcmp(bluePath, "human") == 0)
33                 blue = new Human_Controller(Piece::BLUE, graphicsEnabled);
34         else
35                 blue = new AI_Controller(Piece::BLUE, bluePath);
36
37
38 }
39
40 Game::Game(const char * fromFile, const bool enableGraphics, double newStallTime, const bool allowIllegal, FILE * newLog, const  Piece::Colour & newReveal, int newMaxTurns, bool newPrintBoard) : red(NULL), blue(NULL), turn(Piece::RED), theBoard(10,10), graphicsEnabled(enableGraphics), stallTime(newStallTime), allowIllegalMoves(allowIllegal), log(newLog), reveal(newReveal), turnCount(0), input(NULL), maxTurns(newMaxTurns), printBoard(newPrintBoard)
41 {
42         gameCreated = false;
43         if (gameCreated)
44         {
45                 fprintf(stderr, "Game::Game - Error - Tried to create more than one Game!\n");
46                 exit(EXIT_FAILURE);
47         }
48         gameCreated = true;
49         Game::theGame = this;
50         signal(SIGPIPE, Game::HandleBrokenPipe);
51
52
53         if (graphicsEnabled && (!Graphics::Initialised()))
54                         Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32);
55
56         input = fopen(fromFile, "r");
57
58         red = new FileController(Piece::RED, input);
59         blue = new FileController(Piece::BLUE, input);
60
61
62 }
63
64 Game::~Game()
65 {
66         
67         delete red;
68         delete blue;
69
70         if (log != NULL && log != stdout && log != stderr)
71                 fclose(log);
72
73         if (input != NULL && input != stdin)
74                 fclose(input);
75 }
76
77 /**
78  * Attempts to setup the board and controllers
79  * @param redName the name of the red AI
80  * @param blueName the name of the blue AI
81  * @returns A colour, indicating if there were any errors
82         Piece::NONE indicates no errors
83         Piece::BOTH indicates errors with both AI
84         Piece::RED / Piece::BLUE indicates an error with only one of the two AI
85  */
86 Piece::Colour Game::Setup(const char * redName, const char * blueName)
87 {
88
89         if (!red->Valid())
90         {
91                 logMessage("Controller for Player RED is invalid!\n");
92         }
93         if (!blue->Valid())
94         {
95                 logMessage("Controller for Player BLUE is invalid!\n");
96         }
97         if (!red->Valid())
98         {
99                 if (!blue->Valid())
100                         return Piece::BOTH;
101                 return Piece::RED;
102         }
103         else if (!blue->Valid())
104         {
105                 return Piece::BLUE;
106         }
107
108         for (int y = 4; y < 6; ++y)
109         {
110                 for (int x = 2; x < 4; ++x)
111                 {
112                         theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE);
113                 }
114                 for (int x = 6; x < 8; ++x)
115                 {
116                         theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE);
117                 }
118         }
119
120
121         MovementResult redSetup = red->Setup(blueName);
122         MovementResult blueSetup = blue->Setup(redName);
123
124
125         Piece::Colour result = Piece::NONE;
126         if (redSetup != MovementResult::OK)
127         {       
128                 if (blueSetup != MovementResult::OK)
129                 {
130                         logMessage("BOTH players give invalid setup!\n");
131                         result = Piece::BOTH;
132                 }
133                 else
134                 {
135                         logMessage("Player RED gave an invalid setup!\n");
136                         result = Piece::RED;
137                 }
138                 
139         }
140         else if (blueSetup != MovementResult::OK)
141         {
142                 logMessage("Player BLUE gave an invalid setup!\n");
143                 result = Piece::BLUE;
144         }
145
146         logMessage("%s RED SETUP\n", red->name.c_str());
147         for (int y=0; y < 4; ++y)
148         {
149                 for (int x=0; x < theBoard.Width(); ++x)
150                 {
151                         if (theBoard.GetPiece(x, y) != NULL)
152                                 logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, y)->type)]);
153                         else
154                                 logMessage(".");
155                 }
156                 logMessage("\n");
157         }       
158
159         logMessage("%s BLUE SETUP\n", blue->name.c_str());
160         for (int y=0; y < 4; ++y)
161         {
162                 for (int x=0; x < theBoard.Width(); ++x)
163                         logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, theBoard.Height()-4 + y)->type)]);
164                 logMessage("\n");
165         }       
166
167         
168         return result;
169
170 }
171
172 void Game::Wait(double wait)
173 {
174         if (wait <= 0)
175                 return;
176
177         TimerThread timer(wait*1000000); //Wait in seconds
178         timer.Start();
179
180         if (!graphicsEnabled)
181         {
182                 while (!timer.Finished());
183                 timer.Stop();
184                 return;
185         }
186
187
188         while (!timer.Finished())
189         {
190                 SDL_Event  event;
191                 while (SDL_PollEvent(&event))
192                 {
193                         switch (event.type)
194                         {
195                                 case SDL_QUIT:
196                                         timer.Stop();
197                                         exit(EXIT_SUCCESS);
198                                         break;
199                         }
200                 }
201         }
202         timer.Stop();
203         
204 }
205
206 void Game::HandleBrokenPipe(int sig)
207 {
208         if (theGame->turn == Piece::RED)
209         {
210                 theGame->logMessage("Game ends on RED's turn - REASON: ");
211                 theGame->blue->Message("DEFAULT");      
212         }
213         else if (theGame->turn == Piece::BLUE)
214         {
215         
216                 theGame->logMessage("Game ends on BLUE's turn - REASON: ");
217                 theGame->red->Message("DEFAULT");
218         }
219         else
220         {
221                 theGame->logMessage("Game ends on ERROR's turn - REASON: ");
222                         
223         }
224         
225         theGame->logMessage("SIGPIPE - Broken pipe (AI program may have segfaulted)\n");
226
227         if (Game::theGame->printBoard)
228                 Game::theGame->theBoard.PrintPretty(stdout, Piece::BOTH);
229
230         if (Game::theGame->graphicsEnabled && theGame->log == stdout)
231         {
232                 theGame->logMessage("CLOSE WINDOW TO EXIT\n");
233                 Game::theGame->theBoard.Draw(Piece::BOTH);
234                 while (true)
235                 {
236                         SDL_Event  event;
237                         while (SDL_PollEvent(&event))
238                         {
239                                 switch (event.type)
240                                 {
241                                         case SDL_QUIT:
242                                                 exit(EXIT_SUCCESS);
243                                                 break;
244                                 }
245                         }                       
246                 }
247         }
248         else
249         {
250                 if (theGame->log == stdout)
251                 {
252                         theGame->logMessage( "PRESS ENTER TO EXIT\n");
253                         theGame->theBoard.Print(theGame->log);
254                         while (fgetc(stdin) != '\n');
255                 }
256         }
257         
258
259         exit(EXIT_SUCCESS);
260 }
261
262 void Game::PrintEndMessage(const MovementResult & result)
263 {
264         if (turnCount == 0)
265         {
266                 logMessage("Game ends in the SETUP phase - REASON: ");
267         }
268         else
269         {
270                 if (turn == Piece::RED)
271                 {
272                         logMessage("Game ends on RED's turn - REASON: ");       
273                 }
274                 else if (turn == Piece::BLUE)
275                 {
276                         logMessage("Game ends on BLUE's turn - REASON: ");
277                 }
278                 else
279                 {
280                         logMessage("Game ends on ERROR's turn - REASON: ");
281                         
282                 }
283         }
284         switch (result.type)
285         {
286                 case MovementResult::OK:
287                         logMessage("Status returned OK, unsure why game halted...\n");
288                         break;
289                 case MovementResult::DIES:
290                         logMessage("Status returned DIES, unsure why game halted...\n");
291                         break;
292                 case MovementResult::KILLS:
293                         logMessage("Status returned KILLS, unsure why game halted...\n");
294                         break;
295                 case MovementResult::BOTH_DIE:
296                         logMessage("Status returned BOTH_DIE, unsure why game halted...\n");
297                         break;
298                 case MovementResult::NO_BOARD:
299                         logMessage("Board does not exit?!\n");
300                         break;
301                 case MovementResult::INVALID_POSITION:
302                         logMessage("Coords outside board\n");
303                         break;
304                 case MovementResult::NO_SELECTION:
305                         logMessage("Move does not select a piece\n");
306                         break;
307                 case MovementResult::NOT_YOUR_UNIT:
308                         logMessage("Selected piece belongs to other player\n");
309                         break;
310                 case MovementResult::IMMOBILE_UNIT:
311                         logMessage("Selected piece is not mobile (FLAG or BOMB)\n");
312                         break;
313                 case MovementResult::INVALID_DIRECTION:
314                         logMessage("Selected unit cannot move that way\n");
315                         break;
316                 case MovementResult::POSITION_FULL:
317                         logMessage("Attempted move into square occupied by neutral or allied piece\n");
318                         break;
319                 case MovementResult::VICTORY:
320                         logMessage("Captured the flag\n");
321                         break;
322                 case MovementResult::BAD_RESPONSE:
323                         logMessage("Unintelligable response\n");
324                         break;
325                 case MovementResult::NO_MOVE:
326                         logMessage("Did not make a move (may have exited)\n");
327                         break;
328                 case MovementResult::COLOUR_ERROR:
329                         logMessage("Internal controller error - COLOUR_ERROR\n");
330                         break;
331                 case MovementResult::ERROR:
332                         logMessage("Internal controller error - Unspecified ERROR\n");
333                         break;
334                 case MovementResult::DRAW_DEFAULT:
335                         logMessage("Game declared a draw after %d turns\n", turnCount);
336                         break;
337                 case MovementResult::DRAW:
338                         logMessage("Game declared a draw because neither player has mobile pieces\n");
339                         break;
340                 case MovementResult::SURRENDER:
341                         logMessage("This player has surrendered!\n");
342                         break;
343                 case MovementResult::BAD_SETUP:
344                         switch (turn)
345                         {
346                                 case Piece::RED:
347                                         logMessage("An illegal setup was made by RED\n");
348                                         break;
349                                 case Piece::BLUE:
350                                         logMessage("An illegal setup was made by BLUE\n");
351                                         break;
352                                 case Piece::BOTH:
353                                         logMessage("An illegal setup was made by BOTH players\n");
354                                         break;
355                                 case Piece::NONE:
356                                         logMessage("Unknown internal error.\n");
357                                         break;
358                         }
359                         break;
360
361         }
362
363         if (printBoard)
364         {
365                 system("clear");
366                 fprintf(stdout, "%d Final State\n", turnCount);
367                 theBoard.PrintPretty(stdout, Piece::BOTH);
368                 fprintf(stdout, "\n");
369         }
370         if (graphicsEnabled && log == stdout)
371         {
372                 logMessage("CLOSE WINDOW TO EXIT\n");
373                 theBoard.Draw(Piece::BOTH);
374                 while (true)
375                 {
376                         SDL_Event  event;
377                         while (SDL_PollEvent(&event))
378                         {
379                                 switch (event.type)
380                                 {
381                                         case SDL_QUIT:
382                                                 exit(EXIT_SUCCESS);
383                                                 break;
384                                 }
385                         }                       
386                 }
387         }
388         else
389         {
390                 if (log == stdout)
391                 {
392                         logMessage("PRESS ENTER TO EXIT\n");
393                         while (fgetc(stdin) != '\n');
394                 }
395         }
396
397 }
398
399
400
401 MovementResult Game::Play()
402 {
403
404         MovementResult result = MovementResult::OK;
405         turnCount = 1;
406         string buffer;
407
408         Piece::Colour toReveal = reveal;
409         
410         
411         
412
413
414         red->Message("START");
415         
416
417
418         while (!Board::HaltResult(result) && (turnCount < maxTurns || maxTurns < 0))
419         {
420                 if (red->HumanController())
421                         toReveal = Piece::RED;
422                 if (printBoard)
423                 {
424                         system("clear");
425                         if (turnCount == 0)
426                                 fprintf(stdout, "START:\n");
427                         else
428                                 fprintf(stdout, "%d BLUE:\n", turnCount);
429                         theBoard.PrintPretty(stdout, toReveal);
430                         fprintf(stdout, "\n\n");
431                 }
432
433                 if (graphicsEnabled)
434                         theBoard.Draw(toReveal);
435                 
436                 turn = Piece::RED;
437                 logMessage( "%d RED: ", turnCount);
438                 result = red->MakeMove(buffer);
439                 red->Message(buffer);
440                 blue->Message(buffer);
441                 logMessage( "%s\n", buffer.c_str());
442                 if (Board::HaltResult(result))
443                         break;
444
445                 if (stallTime > 0)
446                         Wait(stallTime);
447                 else
448                         ReadUserCommand();
449
450                 if (blue->HumanController())
451                         toReveal = Piece::BLUE;
452                 if (printBoard)
453                 {
454                         system("clear");
455                         fprintf(stdout, "%d RED:\n", turnCount);
456                         theBoard.PrintPretty(stdout, toReveal);
457                         fprintf(stdout, "\n\n");
458                 }
459                 if (graphicsEnabled)
460                         theBoard.Draw(toReveal);
461
462                 
463                 
464                 turn = Piece::BLUE;
465                 logMessage( "%d BLU: ", turnCount);
466                 result = blue->MakeMove(buffer);
467                 blue->Message(buffer);
468                 red->Message(buffer);
469                 logMessage( "%s\n", buffer.c_str());
470
471                 if (Board::HaltResult(result))
472                         break;
473
474                 
475
476                 
477
478                 if (stallTime > 0)
479                         Wait(stallTime);
480                 else
481                         ReadUserCommand();
482         
483                 if (theBoard.MobilePieces(Piece::BOTH) == 0)
484                         result = MovementResult::DRAW;
485
486                 ++turnCount;
487         }
488
489         if ((maxTurns >= 0 && turnCount >= maxTurns) && result == MovementResult::OK)
490         {
491                 result = MovementResult::DRAW_DEFAULT;
492         }
493
494         
495         return result;
496
497                 
498
499 }
500
501 /**
502  * Logs a message to the game's log file if it exists
503  * @param format the format string
504  * @param additional parameters - printed using va_args
505  * @returns the result of vfprintf or a negative number if the log file does not exist
506  */
507 int Game::logMessage(const char * format, ...)
508 {
509         if (log == NULL)
510                 return -666;
511                 va_list ap;
512         va_start(ap, format);
513
514         int result = vfprintf(log, format, ap);
515         va_end(ap);
516
517         return result;
518 }
519
520 /**
521  * Waits for a user command
522  * Currently ignores the command.
523  */
524 void Game::ReadUserCommand()
525 {
526         fprintf(stdout, "Waiting for user to press enter...\n");
527         string command("");
528         for (char c = fgetc(stdin); c != '\n' && (int)(c) != EOF; c = fgetc(stdin))
529         {
530                 command += c;
531         }
532 }
533
534 MovementResult FileController::QuerySetup(const char * opponentName, std::string setup[])
535 {
536
537         char c = fgetc(file);
538         name = "";
539         while (c != ' ')
540         {
541                 name += c;
542                 c = fgetc(file);
543         }
544
545         while (fgetc(file) != '\n');
546
547         for (int y = 0; y < 4; ++y)
548         {
549                 setup[y] = "";
550                 for (int x = 0; x < Game::theGame->theBoard.Width(); ++x)
551                 {
552                         setup[y] += fgetc(file);
553                 }
554
555                 if (fgetc(file) != '\n')
556                 {
557                         return MovementResult::BAD_RESPONSE;
558                 }
559         }
560         return MovementResult::OK;
561
562         
563 }
564
565 MovementResult FileController::QueryMove(std::string & buffer)
566 {
567         char buf[BUFSIZ];
568
569         fgets(buf, sizeof(buf), file);
570         char * s = (char*)(buf);
571         while (*s != ':' && *s != '\0')
572                 ++s;
573
574         s += 2;
575         
576         buffer = string(s);
577         return MovementResult::OK;
578 }
579
580

UCC git Repository :: git.ucc.asn.au