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

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