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

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