92dc71ab349c96d0bad905ee9ae82926a1f8c176
[progcomp2012.git] / 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, bool server, bool client) : 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
29         if (client)
30         {
31                 red = new Client(Piece::RED, "server", redPath); //TODO: Retrieve server address
32         }
33         else
34         {
35                 assert(redPath != NULL);
36                 if (strcmp(redPath, "human") == 0)
37                         red = new Human_Controller(Piece::RED, graphicsEnabled);
38                 else
39                         red = new AI_Controller(Piece::RED, redPath, timeoutTime);
40         }
41         
42         
43         if (server)
44         {
45                 blue = new Server(Piece::BLUE, "client");
46         }
47         else
48         {
49                 assert(bluePath != NULL);
50                 if (strcmp(bluePath, "human") == 0)
51                         blue = new Human_Controller(Piece::BLUE, graphicsEnabled);
52                 else
53                         blue = new AI_Controller(Piece::BLUE, bluePath, timeoutTime);
54         }
55
56
57 }
58
59 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)
60 {
61         gameCreated = false;
62         if (gameCreated)
63         {
64                 fprintf(stderr, "Game::Game - Error - Tried to create more than one Game!\n");
65                 exit(EXIT_FAILURE);
66         }
67         gameCreated = true;
68         Game::theGame = this;
69         signal(SIGPIPE, Game::HandleBrokenPipe);
70
71         #ifdef BUILD_GRAPHICS
72         if (graphicsEnabled && (!Graphics::Initialised()))
73                         Graphics::Initialise("Stratego", theBoard.Width()*32, theBoard.Height()*32);
74         #endif //BUILD_GRAPHICS
75
76         input = fopen(fromFile, "r");
77
78         red = new FileController(Piece::RED, input);
79         blue = new FileController(Piece::BLUE, input);
80
81
82 }
83
84 Game::~Game()
85 {
86         
87         delete red;
88         delete blue;
89
90         if (log != NULL && log != stdout && log != stderr)
91                 fclose(log);
92
93         if (input != NULL && input != stdin)
94                 fclose(input);
95 }
96
97 /**
98  * Attempts to setup the board and controllers
99  * @param redName the name of the red AI
100  * @param blueName the name of the blue AI
101  * @returns A colour, indicating if there were any errors
102         Piece::NONE indicates no errors
103         Piece::BOTH indicates errors with both AI
104         Piece::RED / Piece::BLUE indicates an error with only one of the two AI
105  */
106 Piece::Colour Game::Setup(const char * redName, const char * blueName)
107 {
108
109         if (!red->Valid())
110         {
111                 logMessage("Controller for Player RED is invalid!\n");
112                 if (!red->HumanController())
113                         logMessage("Check that executable \"%s\" exists and has executable permissions set.\n", redName);
114         }
115         if (!blue->Valid())
116         {
117                 logMessage("Controller for Player BLUE is invalid!\n");
118                 if (!blue->HumanController())
119                         logMessage("Check that executable \"%s\" exists and has executable permissions set.\n", blueName);
120         }
121         if (!red->Valid())
122         {
123                 if (!blue->Valid())
124                         return Piece::BOTH;
125                 return Piece::RED;
126         }
127         else if (!blue->Valid())
128         {
129                 return Piece::BLUE;
130         }
131
132         for (int y = 4; y < 6; ++y)
133         {
134                 for (int x = 2; x < 4; ++x)
135                 {
136                         theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE);
137                 }
138                 for (int x = 6; x < 8; ++x)
139                 {
140                         theBoard.AddPiece(x,y,Piece::BOULDER, Piece::NONE);
141                 }
142         }
143
144
145         MovementResult redSetup = red->Setup(blueName);
146         MovementResult blueSetup = blue->Setup(redName);
147
148
149         Piece::Colour result = Piece::NONE;
150         if (redSetup != MovementResult::OK)
151         {       
152                 if (blueSetup != MovementResult::OK)
153                 {
154                         logMessage("BOTH players give invalid setup!\n");
155                         result = Piece::BOTH;
156                 }
157                 else
158                 {
159                         //logMessage("Player RED gave an invalid setup!\n");
160                         result = Piece::RED;
161                 }
162                 
163         }
164         else if (blueSetup != MovementResult::OK)
165         {
166                 //logMessage("Player BLUE gave an invalid setup!\n");
167                 result = Piece::BLUE;
168         }
169
170
171         logMessage("%s RED SETUP\n", red->name.c_str());
172         if (redSetup == MovementResult::OK)
173         {
174                 for (int y=0; y < 4; ++y)
175                 {
176                         for (int x=0; x < theBoard.Width(); ++x)
177                         {
178                                 if (theBoard.GetPiece(x, y) != NULL)
179                                         logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, y)->type)]);
180                                 else
181                                         logMessage(".");
182                         }
183                         logMessage("\n");
184                 }       
185         }
186         else
187         {
188                 logMessage("INVALID!\n");
189         }
190
191         logMessage("%s BLUE SETUP\n", blue->name.c_str());
192         if (blueSetup == MovementResult::OK)
193         {
194                 for (int y=0; y < 4; ++y)
195                 {
196                         for (int x=0; x < theBoard.Width(); ++x)
197                         {
198                                 if (theBoard.GetPiece(x, theBoard.Height()-4+y) != NULL)
199                                         logMessage("%c", Piece::tokens[(int)(theBoard.GetPiece(x, theBoard.Height()-4+y)->type)]);
200                                 else
201                                         logMessage(".");
202                         }
203                         logMessage("\n");
204                 }       
205         }
206         else
207         {
208                 logMessage("INVALID!\n");
209         }
210
211         
212         return result;
213
214 }
215
216 void Game::Wait(double wait)
217 {
218         if (wait <= 0)
219                 return;
220
221         TimerThread timer(wait*1000000); //Wait in seconds
222         timer.Start();
223
224         #ifdef BUILD_GRAPHICS
225         if (!graphicsEnabled)
226         {
227                 while (!timer.Finished());
228                 timer.Stop();
229                 return;
230         }
231         #endif //BUILD_GRAPHICS
232
233         while (!timer.Finished())
234         {
235                 #ifdef BUILD_GRAPHICS
236                 SDL_Event  event;
237                 while (SDL_PollEvent(&event))
238                 {
239                         switch (event.type)
240                         {
241                                 case SDL_QUIT:
242                                         timer.Stop();
243                                         exit(EXIT_SUCCESS);
244                                         break;
245                         }
246                 }
247                 #endif //BUILD_GRAPHICS
248         }
249         timer.Stop();
250         
251 }
252
253 void Game::HandleBrokenPipe(int sig)
254 {
255         if (theGame == NULL)
256         {
257                 fprintf(stderr, "ERROR - Recieved SIGPIPE during game exit!\n");
258                 exit(EXIT_FAILURE);
259         }
260         if (theGame->turn == Piece::RED)
261         {
262                 theGame->logMessage("Game ends on RED's turn - REASON: ");
263                 if (theGame->blue->Valid()) //Should probably check this
264                         theGame->blue->Message("DEFAULT");      
265         }
266         else if (theGame->turn == Piece::BLUE)
267         {
268         
269                 theGame->logMessage("Game ends on BLUE's turn - REASON: ");
270                 if (theGame->red->Valid()) //Should probably check this
271                         theGame->red->Message("DEFAULT");
272         }
273         else
274         {
275                 theGame->logMessage("Game ends on ERROR's turn - REASON: ");
276                         
277         }
278         
279         theGame->logMessage("SIGPIPE - Broken pipe (AI program no longer running)\n");
280
281         if (Game::theGame->printBoard)
282                 Game::theGame->theBoard.PrintPretty(stdout, Piece::BOTH);
283
284         
285         #ifdef BUILD_GRAPHICS
286         if (Game::theGame->graphicsEnabled && theGame->log == stdout)
287         {
288                 theGame->logMessage("CLOSE WINDOW TO EXIT\n");
289                 Game::theGame->theBoard.Draw(Piece::BOTH);
290                 while (true)
291                 {
292                         SDL_Event  event;
293                         while (SDL_PollEvent(&event))
294                         {
295                                 switch (event.type)
296                                 {
297                                         case SDL_QUIT:
298                                                 exit(EXIT_SUCCESS);
299                                                 break;
300                                 }
301                         }                       
302                 }
303         }
304         else
305         #endif //BUILD_GRAPHICS
306         {
307                 if (theGame->log == stdout || theGame->log == stderr)
308                 {
309                         theGame->logMessage( "PRESS ENTER TO EXIT\n");
310                         theGame->theBoard.Print(theGame->log);
311                         while (fgetc(stdin) != '\n');
312                 }
313         }
314         
315
316         exit(EXIT_SUCCESS);
317 }
318
319 void Game::PrintEndMessage(const MovementResult & result)
320 {
321         if (turnCount == 0)
322         {
323                 logMessage("Game ends in the SETUP phase - REASON: ");
324         }
325         else
326         {
327                 if (turn == Piece::RED)
328                 {
329                         logMessage("Game ends on RED's turn - REASON: ");       
330                 }
331                 else if (turn == Piece::BLUE)
332                 {
333                         logMessage("Game ends on BLUE's turn - REASON: ");
334                 }
335                 else
336                 {
337                         logMessage("Game ends on ERROR's turn - REASON: ");
338                         
339                 }
340         }
341         switch (result.type)
342         {
343                 case MovementResult::OK:
344                         logMessage("Status returned OK, unsure why game halted...\n");
345                         break;
346                 case MovementResult::DIES:
347                         logMessage("Status returned DIES, unsure why game halted...\n");
348                         break;
349                 case MovementResult::KILLS:
350                         logMessage("Status returned KILLS, unsure why game halted...\n");
351                         break;
352                 case MovementResult::BOTH_DIE:
353                         logMessage("Status returned BOTH_DIE, unsure why game halted...\n");
354                         break;
355                 case MovementResult::NO_BOARD:
356                         logMessage("Board does not exit?!\n");
357                         break;
358                 case MovementResult::INVALID_POSITION:
359                         logMessage("Coords outside board\n");
360                         break;
361                 case MovementResult::NO_SELECTION:
362                         logMessage("Move does not select a piece\n");
363                         break;
364                 case MovementResult::NOT_YOUR_UNIT:
365                         logMessage("Selected piece belongs to other player\n");
366                         break;
367                 case MovementResult::IMMOBILE_UNIT:
368                         logMessage("Selected piece is not mobile (FLAG or BOMB)\n");
369                         break;
370                 case MovementResult::INVALID_DIRECTION:
371                         logMessage("Selected unit cannot move that way\n");
372                         break;
373                 case MovementResult::POSITION_FULL:
374                         logMessage("Attempted move into square occupied by neutral or allied piece\n");
375                         break;
376                 case MovementResult::VICTORY_FLAG:
377                         logMessage("Captured the flag\n");
378                         break;
379                 case MovementResult::VICTORY_ATTRITION:
380                         logMessage("Destroyed all mobile enemy pieces\n");
381                         break;
382                 case MovementResult::BAD_RESPONSE:
383                         logMessage("Unintelligable response\n");
384                         break;
385                 case MovementResult::NO_MOVE:
386                         logMessage("Response timeout after %2f seconds.\n", timeoutTime);
387                         break;
388                 case MovementResult::COLOUR_ERROR:
389                         logMessage("Internal controller error - COLOUR_ERROR\n");
390                         break;
391                 case MovementResult::ERROR:
392                         logMessage("Internal controller error - Unspecified ERROR\n");
393                         break;
394                 case MovementResult::DRAW_DEFAULT:
395                         logMessage("Game declared a draw after %d turns\n", turnCount);
396                         break;
397                 case MovementResult::DRAW:
398                         logMessage("Game declared a draw because neither player has mobile pieces\n");
399                         break;
400                 case MovementResult::SURRENDER:
401                         logMessage("This player has surrendered!\n");
402                         break;
403                 case MovementResult::BAD_SETUP:
404                         switch (turn)
405                         {
406                                 case Piece::RED:
407                                         logMessage("An illegal setup was made by RED\n");
408                                         break;
409                                 case Piece::BLUE:
410                                         logMessage("An illegal setup was made by BLUE\n");
411                                         break;
412                                 case Piece::BOTH:
413                                         logMessage("An illegal setup was made by BOTH players\n");
414                                         break;
415                                 case Piece::NONE:
416                                         logMessage("Unknown internal error.\n");
417                                         break;
418                         }
419                         break;
420
421         }
422
423         if (printBoard)
424         {
425                 system("clear");
426                 fprintf(stdout, "%d Final State\n", turnCount);
427                 theBoard.PrintPretty(stdout, Piece::BOTH);
428                 fprintf(stdout, "\n");
429         }
430
431         #ifdef BUILD_GRAPHICS
432         if (graphicsEnabled && log == stdout)
433         {
434                 logMessage("CLOSE WINDOW TO EXIT\n");
435                 theBoard.Draw(Piece::BOTH);
436                 while (true)
437                 {
438                         SDL_Event  event;
439                         while (SDL_PollEvent(&event))
440                         {
441                                 switch (event.type)
442                                 {
443                                         case SDL_QUIT:
444                                                 exit(EXIT_SUCCESS);
445                                                 break;
446                                 }
447                         }                       
448                 }
449         }
450         else
451         #endif //BUILD_GRAPHICS
452         {
453                 if (log == stdout)
454                 {
455                         logMessage("PRESS ENTER TO EXIT\n");
456                         while (fgetc(stdin) != '\n');
457                         exit(EXIT_SUCCESS); //Might want to actually exit, you foolish fool
458                 }
459         }
460
461 }
462 /** Checks for victory by attrition (destroying all mobile pieces)
463  *
464  *  @returns OK for no victory, 
465  *      DRAW if both players have no pieces, or 
466  *      VICTORY_ATTRITION  if the current player has won by attrition
467  */
468 MovementResult Game::CheckVictoryAttrition()
469 {
470         if (theBoard.MobilePieces(Piece::OppositeColour(turn)) == 0)
471         {
472                 if (theBoard.MobilePieces(turn) == 0)
473                         return MovementResult::DRAW;
474                 else
475                         return MovementResult::VICTORY_ATTRITION;
476         }
477         return MovementResult::OK;
478
479 }
480 MovementResult Game::Play()
481 {
482
483         MovementResult result = MovementResult::OK;
484         turnCount = 1;
485         string buffer;
486
487         Piece::Colour toReveal = reveal;
488         
489         
490         
491
492
493         red->Message("START");
494         
495
496
497         while (!Board::HaltResult(result) && (turnCount < maxTurns || maxTurns < 0))
498         {
499                 if (red->HumanController() && blue->HumanController())
500                         toReveal = Piece::RED;
501                 if (printBoard)
502                 {
503                         system("clear");
504                         if (turnCount == 0)
505                                 fprintf(stdout, "START:\n");
506                         else
507                                 fprintf(stdout, "%d BLUE:\n", turnCount);
508                         theBoard.PrintPretty(stdout, toReveal);
509                         fprintf(stdout, "\n\n");
510                 }
511
512                 #ifdef BUILD_GRAPHICS
513                 if (graphicsEnabled)
514                         theBoard.Draw(toReveal);
515                 #endif //BUILD_GRAPHICS
516                 
517                 turn = Piece::RED;
518                 blue->Pause();
519                 red->Continue();
520                 if (!Board::HaltResult(result))
521                 {
522                         result = CheckVictoryAttrition();
523                 }
524                 if (Board::HaltResult(result))
525                         break;
526
527                 logMessage( "%d RED: ", turnCount);
528                 result = red->MakeMove(buffer);
529                 red->Message(buffer);
530                 blue->Message(buffer);
531                 logMessage( "%s\n", buffer.c_str());
532
533                 if (!Board::HaltResult(result))
534                 {
535                         result = CheckVictoryAttrition();
536                 }
537                 if (Board::HaltResult(result))
538                         break;
539
540                 if (stallTime >= 0)
541                         Wait(stallTime);
542                 else
543                         ReadUserCommand();
544
545                 if (blue->HumanController() && red->HumanController())
546                         toReveal = Piece::BLUE;
547                 if (printBoard)
548                 {
549                         system("clear");
550                         fprintf(stdout, "%d RED:\n", turnCount);
551                         theBoard.PrintPretty(stdout, toReveal);
552                         fprintf(stdout, "\n\n");
553                 }
554                 
555                 #ifdef BUILD_GRAPHICS
556                 if (graphicsEnabled)
557                         theBoard.Draw(toReveal);
558                 #endif //BUILD_GRAPHICS
559
560                 
561                 
562                 turn = Piece::BLUE;
563                 red->Pause();
564                 blue->Continue();
565                 if (!Board::HaltResult(result))
566                 {
567                         result = CheckVictoryAttrition();
568                 }
569                 if (Board::HaltResult(result))
570                         break;
571
572                 logMessage( "%d BLU: ", turnCount);
573                 result = blue->MakeMove(buffer);
574                 blue->Message(buffer);
575                 red->Message(buffer);
576                 logMessage( "%s\n", buffer.c_str());
577
578                 if (!Board::HaltResult(result))
579                 {
580                         result = CheckVictoryAttrition();
581                 }
582                 if (Board::HaltResult(result))
583                         break;
584
585                 if (theBoard.MobilePieces(Piece::RED) == 0)
586                         result = MovementResult::DRAW;
587
588                 if (theBoard.MobilePieces(Piece::RED) == 0)
589                 {
590                         if (theBoard.MobilePieces(Piece::BLUE) == 0)
591                                 result = MovementResult::DRAW;
592                         else
593                                 result = MovementResult::VICTORY_ATTRITION;
594                         break;                  
595                 }
596
597                 if (stallTime >= 0)
598                         Wait(stallTime);
599                 else
600                         ReadUserCommand();
601         
602                 
603
604                 ++turnCount;
605         }
606
607         if ((maxTurns >= 0 && turnCount >= maxTurns) && result == MovementResult::OK)
608         {
609                 result = MovementResult::DRAW_DEFAULT;
610         }
611
612         
613         return result;
614
615                 
616
617 }
618
619 /**
620  * Logs a message to the game's log file if it exists
621  * @param format the format string
622  * @param additional parameters - printed using va_args
623  * @returns the result of vfprintf or a negative number if the log file does not exist
624  */
625 int Game::logMessage(const char * format, ...)
626 {
627         if (log == NULL)
628                 return -666;
629                 va_list ap;
630         va_start(ap, format);
631
632         int result = vfprintf(log, format, ap);
633         va_end(ap);
634
635         return result;
636 }
637
638 /**
639  * Waits for a user command
640  * Currently ignores the command.
641  */
642 void Game::ReadUserCommand()
643 {
644         fprintf(stdout, "Waiting for user to press enter... (type QUIT to exit)\n");
645         string command("");
646         for (char c = fgetc(stdin); c != '\n' && (int)(c) != EOF; c = fgetc(stdin))
647         {
648                 command += c;
649         }
650
651         if (command == "QUIT")
652         {
653                 fprintf(stdout, "Ordered to quit... exiting...\n");
654                 exit(EXIT_SUCCESS);
655         }
656 }
657
658 MovementResult FileController::QuerySetup(const char * opponentName, std::string setup[])
659 {
660
661         char c = fgetc(file);
662         name = "";
663         while (c != ' ')
664         {
665                 name += c;
666                 c = fgetc(file);
667         }
668
669         while (fgetc(file) != '\n');
670
671         for (int y = 0; y < 4; ++y)
672         {
673                 setup[y] = "";
674                 for (int x = 0; x < Game::theGame->theBoard.Width(); ++x)
675                 {
676                         setup[y] += fgetc(file);
677                 }
678
679                 if (fgetc(file) != '\n')
680                 {
681                         return MovementResult::BAD_RESPONSE;
682                 }
683         }
684         return MovementResult::OK;
685
686         
687 }
688
689 MovementResult FileController::QueryMove(std::string & buffer)
690 {
691         //This bit is kind of hacky and terrible, and yes I am mixing C with C++
692         //Yes I should have used fstream for the whole thing and it would be much easier.
693         //Oh well.
694
695         char buf[BUFSIZ];
696
697         fgets(buf, sizeof(buf), file);
698         char * s = (char*)(buf);
699         while (*s != ':' && *s != '\0')
700                 ++s;
701         
702         //Move forward to the start of the move information
703         for (int i=0; i < 2; ++i)
704         {
705                 if (*s != '\0' && *s != '\n')
706                         ++s;
707         }
708         
709         //Unfortunately we can't just copy the whole line
710         buffer = string(s);
711         //We have to remove the movement result tokens
712         
713
714         vector<string> tokens;
715         Game::Tokenise(tokens, buffer, ' ');
716         buffer.clear();
717
718         if (tokens.size() < 1)
719                 return MovementResult::BAD_RESPONSE;
720         buffer += tokens[0];
721
722         
723         if (tokens[0] == "NO_MOVE") //tokens[0] is either the x coordinate, or "NO_MOVE"
724                 return MovementResult::OK;
725         if (tokens.size() < 2)
726                 return MovementResult::BAD_RESPONSE;
727         buffer += " ";
728         buffer += tokens[1]; //The y coordinate
729         buffer += " ";
730         buffer += tokens[2]; //The direction
731         
732         //Check for a possible multiplier. If tokens[3] is an integer it will be the multiplier, otherwise it won't be.
733         if (tokens.size() > 3 && atoi(tokens[3].c_str()) != 0)
734         {
735                 buffer += " ";
736                 buffer += tokens[3];
737         }
738         else
739         {
740                 //(tokens[3] should include a new line)
741                 //buffer += "\n";
742         }
743
744         
745
746         
747         
748         
749         return MovementResult::OK;
750 }
751
752 /**
753  * Tokenise a string
754  */
755 int Game::Tokenise(std::vector<string> & buffer, std::string & str, char split)
756 {
757         string token = "";
758         for (unsigned int x = 0; x < str.size(); ++x)
759         {
760                 if (str[x] == split && token.size() > 0)
761                 {
762                         buffer.push_back(token);
763                         token = "";
764                 }
765                 if (str[x] != split)
766                         token += str[x];
767         }
768         if (token.size() > 0)
769                 buffer.push_back(token);
770         return buffer.size();
771 }
772
773

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