8d3eef0c398c7ab46ee64eaa144c8caf4e9b933f
[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 /** Checks for victory by attrition (destroying all mobile pieces)
446  *
447  *  @returns OK for no victory, 
448  *      DRAW if both players have no pieces, or 
449  *      VICTORY_ATTRITION  if the current player has won by attrition
450  */
451 MovementResult Game::CheckVictoryAttrition()
452 {
453         if (theBoard.MobilePieces(Piece::OppositeColour(turn)) == 0)
454         {
455                 if (theBoard.MobilePieces(turn) == 0)
456                         return MovementResult::DRAW;
457                 else
458                         return MovementResult::VICTORY_ATTRITION;
459         }
460         return MovementResult::OK;
461
462 }
463 MovementResult Game::Play()
464 {
465
466         MovementResult result = MovementResult::OK;
467         turnCount = 1;
468         string buffer;
469
470         Piece::Colour toReveal = reveal;
471         
472         
473         
474
475
476         red->Message("START");
477         
478
479
480         while (!Board::HaltResult(result) && (turnCount < maxTurns || maxTurns < 0))
481         {
482                 if (red->HumanController() && blue->HumanController())
483                         toReveal = Piece::RED;
484                 if (printBoard)
485                 {
486                         system("clear");
487                         if (turnCount == 0)
488                                 fprintf(stdout, "START:\n");
489                         else
490                                 fprintf(stdout, "%d BLUE:\n", turnCount);
491                         theBoard.PrintPretty(stdout, toReveal);
492                         fprintf(stdout, "\n\n");
493                 }
494
495                 #ifdef BUILD_GRAPHICS
496                 if (graphicsEnabled)
497                         theBoard.Draw(toReveal);
498                 #endif //BUILD_GRAPHICS
499                 
500                 turn = Piece::RED;
501                 blue->Pause();
502                 red->Continue();
503                 if (!Board::HaltResult(result))
504                 {
505                         result = CheckVictoryAttrition();
506                 }
507                 if (Board::HaltResult(result))
508                         break;
509
510                 logMessage( "%d RED: ", turnCount);
511                 result = red->MakeMove(buffer);
512                 red->Message(buffer);
513                 blue->Message(buffer);
514                 logMessage( "%s\n", buffer.c_str());
515
516                 if (!Board::HaltResult(result))
517                 {
518                         result = CheckVictoryAttrition();
519                 }
520                 if (Board::HaltResult(result))
521                         break;
522
523                 if (stallTime >= 0)
524                         Wait(stallTime);
525                 else
526                         ReadUserCommand();
527
528                 if (blue->HumanController() && red->HumanController())
529                         toReveal = Piece::BLUE;
530                 if (printBoard)
531                 {
532                         system("clear");
533                         fprintf(stdout, "%d RED:\n", turnCount);
534                         theBoard.PrintPretty(stdout, toReveal);
535                         fprintf(stdout, "\n\n");
536                 }
537                 
538                 #ifdef BUILD_GRAPHICS
539                 if (graphicsEnabled)
540                         theBoard.Draw(toReveal);
541                 #endif //BUILD_GRAPHICS
542
543                 
544                 
545                 turn = Piece::BLUE;
546                 red->Pause();
547                 blue->Continue();
548                 if (!Board::HaltResult(result))
549                 {
550                         result = CheckVictoryAttrition();
551                 }
552                 if (Board::HaltResult(result))
553                         break;
554
555                 logMessage( "%d BLU: ", turnCount);
556                 result = blue->MakeMove(buffer);
557                 blue->Message(buffer);
558                 red->Message(buffer);
559                 logMessage( "%s\n", buffer.c_str());
560
561                 if (!Board::HaltResult(result))
562                 {
563                         result = CheckVictoryAttrition();
564                 }
565                 if (Board::HaltResult(result))
566                         break;
567
568                 if (theBoard.MobilePieces(Piece::RED) == 0)
569                         result = MovementResult::DRAW;
570
571                 if (theBoard.MobilePieces(Piece::RED) == 0)
572                 {
573                         if (theBoard.MobilePieces(Piece::BLUE) == 0)
574                                 result = MovementResult::DRAW;
575                         else
576                                 result = MovementResult::VICTORY_ATTRITION;
577                         break;                  
578                 }
579
580                 if (stallTime >= 0)
581                         Wait(stallTime);
582                 else
583                         ReadUserCommand();
584         
585                 
586
587                 ++turnCount;
588         }
589
590         if ((maxTurns >= 0 && turnCount >= maxTurns) && result == MovementResult::OK)
591         {
592                 result = MovementResult::DRAW_DEFAULT;
593         }
594
595         
596         return result;
597
598                 
599
600 }
601
602 /**
603  * Logs a message to the game's log file if it exists
604  * @param format the format string
605  * @param additional parameters - printed using va_args
606  * @returns the result of vfprintf or a negative number if the log file does not exist
607  */
608 int Game::logMessage(const char * format, ...)
609 {
610         if (log == NULL)
611                 return -666;
612                 va_list ap;
613         va_start(ap, format);
614
615         int result = vfprintf(log, format, ap);
616         va_end(ap);
617
618         return result;
619 }
620
621 /**
622  * Waits for a user command
623  * Currently ignores the command.
624  */
625 void Game::ReadUserCommand()
626 {
627         fprintf(stdout, "Waiting for user to press enter... (type QUIT to exit)\n");
628         string command("");
629         for (char c = fgetc(stdin); c != '\n' && (int)(c) != EOF; c = fgetc(stdin))
630         {
631                 command += c;
632         }
633
634         if (command == "QUIT")
635         {
636                 fprintf(stdout, "Ordered to quit... exiting...\n");
637                 exit(EXIT_SUCCESS);
638         }
639 }
640
641 MovementResult FileController::QuerySetup(const char * opponentName, std::string setup[])
642 {
643
644         char c = fgetc(file);
645         name = "";
646         while (c != ' ')
647         {
648                 name += c;
649                 c = fgetc(file);
650         }
651
652         while (fgetc(file) != '\n');
653
654         for (int y = 0; y < 4; ++y)
655         {
656                 setup[y] = "";
657                 for (int x = 0; x < Game::theGame->theBoard.Width(); ++x)
658                 {
659                         setup[y] += fgetc(file);
660                 }
661
662                 if (fgetc(file) != '\n')
663                 {
664                         return MovementResult::BAD_RESPONSE;
665                 }
666         }
667         return MovementResult::OK;
668
669         
670 }
671
672 MovementResult FileController::QueryMove(std::string & buffer)
673 {
674         //This bit is kind of hacky and terrible, and yes I am mixing C with C++
675         //Yes I should have used fstream for the whole thing and it would be much easier.
676         //Oh well.
677
678         char buf[BUFSIZ];
679
680         fgets(buf, sizeof(buf), file);
681         char * s = (char*)(buf);
682         while (*s != ':' && *s != '\0')
683                 ++s;
684         
685         //Move forward to the start of the move information
686         for (int i=0; i < 2; ++i)
687         {
688                 if (*s != '\0' && *s != '\n')
689                         ++s;
690         }
691         
692         //Unfortunately we can't just copy the whole line
693         buffer = string(s);
694         //We have to remove the movement result tokens
695         
696
697         vector<string> tokens;
698         Game::Tokenise(tokens, buffer, ' ');
699         buffer.clear();
700
701         if (tokens.size() < 1)
702                 return MovementResult::BAD_RESPONSE;
703         buffer += tokens[0];
704
705         
706         if (tokens[0] == "NO_MOVE") //tokens[0] is either the x coordinate, or "NO_MOVE"
707                 return MovementResult::OK;
708         if (tokens.size() < 2)
709                 return MovementResult::BAD_RESPONSE;
710         buffer += " ";
711         buffer += tokens[1]; //The y coordinate
712         buffer += " ";
713         buffer += tokens[2]; //The direction
714         
715         //Check for a possible multiplier. If tokens[3] is an integer it will be the multiplier, otherwise it won't be.
716         if (tokens.size() > 3 && atoi(tokens[3].c_str()) != 0)
717         {
718                 buffer += " ";
719                 buffer += tokens[3];
720         }
721         else
722         {
723                 //(tokens[3] should include a new line)
724                 //buffer += "\n";
725         }
726
727         
728
729         
730         
731         
732         return MovementResult::OK;
733 }
734
735 /**
736  * Tokenise a string
737  */
738 int Game::Tokenise(std::vector<string> & buffer, std::string & str, char split)
739 {
740         string token = "";
741         for (unsigned int x = 0; x < str.size(); ++x)
742         {
743                 if (str[x] == split && token.size() > 0)
744                 {
745                         buffer.push_back(token);
746                         token = "";
747                 }
748                 if (str[x] != split)
749                         token += str[x];
750         }
751         if (token.size() > 0)
752                 buffer.push_back(token);
753         return buffer.size();
754 }
755
756

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