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

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