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

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