3df968cc9dad319282197be3d522b19788d35cf0
[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, double newTimeoutTime) : 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), timeoutTime(newTimeoutTime)
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, timeoutTime);
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, timeoutTime);
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, double newTimeoutTime) : 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), timeoutTime(newTimeoutTime)
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 executable \"%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 executable \"%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                 if (theGame->blue->Valid()) //Should probably check this
247                         theGame->blue->Message("DEFAULT");      
248         }
249         else if (theGame->turn == Piece::BLUE)
250         {
251         
252                 theGame->logMessage("Game ends on BLUE's turn - REASON: ");
253                 if (theGame->red->Valid()) //Should probably check this
254                         theGame->red->Message("DEFAULT");
255         }
256         else
257         {
258                 theGame->logMessage("Game ends on ERROR's turn - REASON: ");
259                         
260         }
261         
262         theGame->logMessage("SIGPIPE - Broken pipe (AI program no longer running)\n");
263
264         if (Game::theGame->printBoard)
265                 Game::theGame->theBoard.PrintPretty(stdout, Piece::BOTH);
266
267         
268         #ifdef BUILD_GRAPHICS
269         if (Game::theGame->graphicsEnabled && theGame->log == stdout)
270         {
271                 theGame->logMessage("CLOSE WINDOW TO EXIT\n");
272                 Game::theGame->theBoard.Draw(Piece::BOTH);
273                 while (true)
274                 {
275                         SDL_Event  event;
276                         while (SDL_PollEvent(&event))
277                         {
278                                 switch (event.type)
279                                 {
280                                         case SDL_QUIT:
281                                                 exit(EXIT_SUCCESS);
282                                                 break;
283                                 }
284                         }                       
285                 }
286         }
287         else
288         #endif //BUILD_GRAPHICS
289         {
290                 if (theGame->log == stdout || theGame->log == stderr)
291                 {
292                         theGame->logMessage( "PRESS ENTER TO EXIT\n");
293                         theGame->theBoard.Print(theGame->log);
294                         while (fgetc(stdin) != '\n');
295                 }
296         }
297         
298
299         exit(EXIT_SUCCESS);
300 }
301
302 void Game::PrintEndMessage(const MovementResult & result)
303 {
304         if (turnCount == 0)
305         {
306                 logMessage("Game ends in the SETUP phase - REASON: ");
307         }
308         else
309         {
310                 if (turn == Piece::RED)
311                 {
312                         logMessage("Game ends on RED's turn - REASON: ");       
313                 }
314                 else if (turn == Piece::BLUE)
315                 {
316                         logMessage("Game ends on BLUE's turn - REASON: ");
317                 }
318                 else
319                 {
320                         logMessage("Game ends on ERROR's turn - REASON: ");
321                         
322                 }
323         }
324         switch (result.type)
325         {
326                 case MovementResult::OK:
327                         logMessage("Status returned OK, unsure why game halted...\n");
328                         break;
329                 case MovementResult::DIES:
330                         logMessage("Status returned DIES, unsure why game halted...\n");
331                         break;
332                 case MovementResult::KILLS:
333                         logMessage("Status returned KILLS, unsure why game halted...\n");
334                         break;
335                 case MovementResult::BOTH_DIE:
336                         logMessage("Status returned BOTH_DIE, unsure why game halted...\n");
337                         break;
338                 case MovementResult::NO_BOARD:
339                         logMessage("Board does not exit?!\n");
340                         break;
341                 case MovementResult::INVALID_POSITION:
342                         logMessage("Coords outside board\n");
343                         break;
344                 case MovementResult::NO_SELECTION:
345                         logMessage("Move does not select a piece\n");
346                         break;
347                 case MovementResult::NOT_YOUR_UNIT:
348                         logMessage("Selected piece belongs to other player\n");
349                         break;
350                 case MovementResult::IMMOBILE_UNIT:
351                         logMessage("Selected piece is not mobile (FLAG or BOMB)\n");
352                         break;
353                 case MovementResult::INVALID_DIRECTION:
354                         logMessage("Selected unit cannot move that way\n");
355                         break;
356                 case MovementResult::POSITION_FULL:
357                         logMessage("Attempted move into square occupied by neutral or allied piece\n");
358                         break;
359                 case MovementResult::VICTORY_FLAG:
360                         logMessage("Captured the flag\n");
361                         break;
362                 case MovementResult::VICTORY_ATTRITION:
363                         logMessage("Destroyed all mobile enemy pieces\n");
364                         break;
365                 case MovementResult::BAD_RESPONSE:
366                         logMessage("Unintelligable response\n");
367                         break;
368                 case MovementResult::NO_MOVE:
369                         logMessage("Response timeout after %2f seconds.\n", timeoutTime);
370                         break;
371                 case MovementResult::COLOUR_ERROR:
372                         logMessage("Internal controller error - COLOUR_ERROR\n");
373                         break;
374                 case MovementResult::ERROR:
375                         logMessage("Internal controller error - Unspecified ERROR\n");
376                         break;
377                 case MovementResult::DRAW_DEFAULT:
378                         logMessage("Game declared a draw after %d turns\n", turnCount);
379                         break;
380                 case MovementResult::DRAW:
381                         logMessage("Game declared a draw because neither player has mobile pieces\n");
382                         break;
383                 case MovementResult::SURRENDER:
384                         logMessage("This player has surrendered!\n");
385                         break;
386                 case MovementResult::BAD_SETUP:
387                         switch (turn)
388                         {
389                                 case Piece::RED:
390                                         logMessage("An illegal setup was made by RED\n");
391                                         break;
392                                 case Piece::BLUE:
393                                         logMessage("An illegal setup was made by BLUE\n");
394                                         break;
395                                 case Piece::BOTH:
396                                         logMessage("An illegal setup was made by BOTH players\n");
397                                         break;
398                                 case Piece::NONE:
399                                         logMessage("Unknown internal error.\n");
400                                         break;
401                         }
402                         break;
403
404         }
405
406         if (printBoard)
407         {
408                 system("clear");
409                 fprintf(stdout, "%d Final State\n", turnCount);
410                 theBoard.PrintPretty(stdout, Piece::BOTH);
411                 fprintf(stdout, "\n");
412         }
413
414         #ifdef BUILD_GRAPHICS
415         if (graphicsEnabled && log == stdout)
416         {
417                 logMessage("CLOSE WINDOW TO EXIT\n");
418                 theBoard.Draw(Piece::BOTH);
419                 while (true)
420                 {
421                         SDL_Event  event;
422                         while (SDL_PollEvent(&event))
423                         {
424                                 switch (event.type)
425                                 {
426                                         case SDL_QUIT:
427                                                 exit(EXIT_SUCCESS);
428                                                 break;
429                                 }
430                         }                       
431                 }
432         }
433         else
434         #endif //BUILD_GRAPHICS
435         {
436                 if (log == stdout)
437                 {
438                         logMessage("PRESS ENTER TO EXIT\n");
439                         while (fgetc(stdin) != '\n');
440                         exit(EXIT_SUCCESS); //Might want to actually exit, you foolish fool
441                 }
442         }
443
444 }
445
446
447
448 MovementResult Game::Play()
449 {
450
451         MovementResult result = MovementResult::OK;
452         turnCount = 1;
453         string buffer;
454
455         Piece::Colour toReveal = reveal;
456         
457         
458         
459
460
461         red->Message("START");
462         
463
464
465         while (!Board::HaltResult(result) && (turnCount < maxTurns || maxTurns < 0))
466         {
467                 if (red->HumanController() && blue->HumanController())
468                         toReveal = Piece::RED;
469                 if (printBoard)
470                 {
471                         system("clear");
472                         if (turnCount == 0)
473                                 fprintf(stdout, "START:\n");
474                         else
475                                 fprintf(stdout, "%d BLUE:\n", turnCount);
476                         theBoard.PrintPretty(stdout, toReveal);
477                         fprintf(stdout, "\n\n");
478                 }
479
480                 #ifdef BUILD_GRAPHICS
481                 if (graphicsEnabled)
482                         theBoard.Draw(toReveal);
483                 #endif //BUILD_GRAPHICS
484                 
485                 turn = Piece::RED;
486                 logMessage( "%d RED: ", turnCount);
487                 result = red->MakeMove(buffer);
488                 red->Message(buffer);
489                 blue->Message(buffer);
490                 logMessage( "%s\n", buffer.c_str());
491                 if (Board::HaltResult(result))
492                         break;
493
494                 if (theBoard.MobilePieces(Piece::BLUE) == 0)
495                 {
496                         if (theBoard.MobilePieces(Piece::RED) == 0)
497                                 result = MovementResult::DRAW;
498                         else
499                                 result = MovementResult::VICTORY_ATTRITION;
500                         break;                  
501                 }
502
503                 if (stallTime >= 0)
504                         Wait(stallTime);
505                 else
506                         ReadUserCommand();
507
508                 if (blue->HumanController() && red->HumanController())
509                         toReveal = Piece::BLUE;
510                 if (printBoard)
511                 {
512                         system("clear");
513                         fprintf(stdout, "%d RED:\n", turnCount);
514                         theBoard.PrintPretty(stdout, toReveal);
515                         fprintf(stdout, "\n\n");
516                 }
517                 
518                 #ifdef BUILD_GRAPHICS
519                 if (graphicsEnabled)
520                         theBoard.Draw(toReveal);
521                 #endif //BUILD_GRAPHICS
522
523                 
524                 
525                 turn = Piece::BLUE;
526                 logMessage( "%d BLU: ", turnCount);
527                 result = blue->MakeMove(buffer);
528                 blue->Message(buffer);
529                 red->Message(buffer);
530                 logMessage( "%s\n", buffer.c_str());
531
532                 if (Board::HaltResult(result))
533                         break;
534
535                 if (theBoard.MobilePieces(Piece::RED) == 0)
536                         result = MovementResult::DRAW;
537
538                 if (theBoard.MobilePieces(Piece::RED) == 0)
539                 {
540                         if (theBoard.MobilePieces(Piece::BLUE) == 0)
541                                 result = MovementResult::DRAW;
542                         else
543                                 result = MovementResult::VICTORY_ATTRITION;
544                         break;                  
545                 }
546
547                 if (stallTime >= 0)
548                         Wait(stallTime);
549                 else
550                         ReadUserCommand();
551         
552                 
553
554                 ++turnCount;
555         }
556
557         if ((maxTurns >= 0 && turnCount >= maxTurns) && result == MovementResult::OK)
558         {
559                 result = MovementResult::DRAW_DEFAULT;
560         }
561
562         
563         return result;
564
565                 
566
567 }
568
569 /**
570  * Logs a message to the game's log file if it exists
571  * @param format the format string
572  * @param additional parameters - printed using va_args
573  * @returns the result of vfprintf or a negative number if the log file does not exist
574  */
575 int Game::logMessage(const char * format, ...)
576 {
577         if (log == NULL)
578                 return -666;
579                 va_list ap;
580         va_start(ap, format);
581
582         int result = vfprintf(log, format, ap);
583         va_end(ap);
584
585         return result;
586 }
587
588 /**
589  * Waits for a user command
590  * Currently ignores the command.
591  */
592 void Game::ReadUserCommand()
593 {
594         fprintf(stdout, "Waiting for user to press enter... (type QUIT to exit)\n");
595         string command("");
596         for (char c = fgetc(stdin); c != '\n' && (int)(c) != EOF; c = fgetc(stdin))
597         {
598                 command += c;
599         }
600
601         if (command == "QUIT")
602         {
603                 fprintf(stdout, "Ordered to quit... exiting...\n");
604                 exit(EXIT_SUCCESS);
605         }
606 }
607
608 MovementResult FileController::QuerySetup(const char * opponentName, std::string setup[])
609 {
610
611         char c = fgetc(file);
612         name = "";
613         while (c != ' ')
614         {
615                 name += c;
616                 c = fgetc(file);
617         }
618
619         while (fgetc(file) != '\n');
620
621         for (int y = 0; y < 4; ++y)
622         {
623                 setup[y] = "";
624                 for (int x = 0; x < Game::theGame->theBoard.Width(); ++x)
625                 {
626                         setup[y] += fgetc(file);
627                 }
628
629                 if (fgetc(file) != '\n')
630                 {
631                         return MovementResult::BAD_RESPONSE;
632                 }
633         }
634         return MovementResult::OK;
635
636         
637 }
638
639 MovementResult FileController::QueryMove(std::string & buffer)
640 {
641         //This bit is kind of hacky and terrible, and yes I am mixing C with C++
642         //Yes I should have used fstream for the whole thing and it would be much easier.
643         //Oh well.
644
645         char buf[BUFSIZ];
646
647         fgets(buf, sizeof(buf), file);
648         char * s = (char*)(buf);
649         while (*s != ':' && *s != '\0')
650                 ++s;
651         
652         //Move forward to the start of the move information
653         for (int i=0; i < 2; ++i)
654         {
655                 if (*s != '\0' && *s != '\n')
656                         ++s;
657         }
658         
659         //Unfortunately we can't just copy the whole line
660         buffer = string(s);
661         //We have to remove the movement result tokens
662         
663
664         vector<string> tokens;
665         Game::Tokenise(tokens, buffer, ' ');
666         buffer.clear();
667
668         if (tokens.size() < 1)
669                 return MovementResult::BAD_RESPONSE;
670         buffer += tokens[0];
671
672         
673         if (tokens[0] == "NO_MOVE") //tokens[0] is either the x coordinate, or "NO_MOVE"
674                 return MovementResult::OK;
675         if (tokens.size() < 2)
676                 return MovementResult::BAD_RESPONSE;
677         buffer += " ";
678         buffer += tokens[1]; //The y coordinate
679         buffer += " ";
680         buffer += tokens[2]; //The direction
681         
682         //Check for a possible multiplier. If tokens[3] is an integer it will be the multiplier, otherwise it won't be.
683         if (tokens.size() > 3 && atoi(tokens[3].c_str()) != 0)
684         {
685                 buffer += " ";
686                 buffer += tokens[3];
687         }
688         else
689         {
690                 //(tokens[3] should include a new line)
691                 //buffer += "\n";
692         }
693
694         
695
696         
697         
698         
699         return MovementResult::OK;
700 }
701
702 /**
703  * Tokenise a string
704  */
705 int Game::Tokenise(std::vector<string> & buffer, std::string & str, char split)
706 {
707         string token = "";
708         for (unsigned int x = 0; x < str.size(); ++x)
709         {
710                 if (str[x] == split && token.size() > 0)
711                 {
712                         buffer.push_back(token);
713                         token = "";
714                 }
715                 if (str[x] != split)
716                         token += str[x];
717         }
718         if (token.size() > 0)
719                 buffer.push_back(token);
720         return buffer.size();
721 }
722
723

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