Mostly messing with "forfax" AI
[progcomp2012.git] / samples / forfax / forfax.cpp
1 /**
2  * "forfax", a sample Stratego AI for the UCC Programming Competition 2012
3  * Implementations of classes Piece, Board and Forfax
4  * @author Sam Moore (matches) [SZM]
5  * @website http://matches.ucc.asn.au/stratego
6  * @email [email protected] or [email protected]
7  * @git git.ucc.asn.au/progcomp2012.git
8  */
9
10 #include "forfax.h"
11
12 #include <cstdlib>
13 #include <sstream>
14 #include <iostream>
15 #include <string>
16 #include <vector>
17 #include <list>
18 #include <cmath>
19
20 using namespace std;
21
22
23
24
25 /**
26  * The characters used to represent various pieces
27  * NOTHING, BOULDER, FLAG, SPY, SCOUT, MINER, SERGEANT, LIETENANT, CAPTAIN, MAJOR, COLONEL, GENERAL, MARSHAL, BOMB, ERROR
28  */
29
30 char  Piece::tokens[] = {'.','+','F','y','s','n','S','L','c','m','C','G','M','B','?'};
31
32
33 /**
34  * The number of units remaining for each colour
35  * Accessed by [COLOUR][TYPE]
36  * COLOUR: RED, BLUE
37  * TYPE: NOTHING, BOULDER, FLAG, SPY, SCOUT, MINER, SERGEANT, LIETENANT, CAPTAIN, MAJOR, COLONEL, GENERAL, MARSHAL, BOMB, ERROR
38  */
39 int Forfax::remainingUnits[][15] = {{0,0,1,1,8,5,4,4,4,3,2,1,1,6,0},{0,0,1,1,8,5,4,4,4,3,2,1,1,6,0}};
40
41
42
43
44 /**
45  * Constructor for a piece of unknown rank
46  * @param newX - x coord
47  * @param newY - y coord
48  * @param newColour - colour
49  */
50 Piece::Piece(int newX, int newY,const Colour & newColour)
51         : x(newX), y(newY), colour(newColour), minRank(Piece::FLAG), maxRank(Piece::BOMB), lastMove(0)
52 {
53
54 }
55
56 /**
57  * Constructor for a piece of known rank
58  * @param newX - x coord
59  * @param newY - y coord
60  * @param newColour - colour
61  * @param fixedRank - rank of the new piece
62  */
63 Piece::Piece(int newX, int newY,const Colour & newColour, const Type & fixedRank)
64         : x(newX), y(newY), colour(newColour), minRank(fixedRank), maxRank(fixedRank), lastMove(0)
65 {
66         
67         
68 }
69
70
71
72
73
74
75 /**
76  * HELPER - Returns the Piece::Type matching a given character
77  * @param token - The character to match
78  * @returns A Piece::Type corresponding to the character, or Piece::ERROR if none was found
79  */
80 Piece::Type Piece::GetType(char token)
81 {
82         for (int ii=0; ii < Piece::ERROR; ++ii)
83         {
84                 if (Piece::tokens[ii] == token)
85                         return (Type)(ii);
86         }
87         return Piece::ERROR;
88 }
89
90 /**
91  * Constructor for the board
92  * @param newWidth - width of the board
93  * @param newHeight - height of the board
94  *
95  */
96 Board::Board(int newWidth, int newHeight) : width(newWidth), height(newHeight), board(NULL), red(), blue()
97 {
98         //Construct 2D array of Piece*'s
99         board = new Piece**[width];
100         for (int x=0; x < width; ++x)
101         {
102                 board[x] = new Piece*[height];
103                 for (int y=0; y < height; ++y)
104                         board[x][y] = NULL;
105         }
106 }
107
108 /**
109  * Destroy the board
110  */
111 Board::~Board()
112 {
113         //Destroy the 2D array of Piece*'s
114         for (int x=0; x < width; ++x)
115         {
116                 for (int y=0; y < height; ++y)
117                         delete board[x][y];
118                 delete [] board[x];
119         }
120 }
121
122 /**
123  * Retrieve a piece from the board at specified coordinates
124  * @param x - x coord of the piece
125  * @param y - y coord of the piece
126  * @returns Piece* to the piece found at (x,y), or NULL if there was no piece, or the coords were invalid
127  */
128 Piece * Board::Get(int x, int y) const
129 {
130         if (board == NULL || x < 0 || y < 0 || x >= width || y >= height)
131                 return NULL;
132         return board[x][y];
133 }
134
135 /**
136  * Add a piece to the board
137  *      Also updates the red or blue arrays if necessary
138  * @param x - x coord of the piece
139  * @param y - y coord of the piece
140  * @param newPiece - pointer to the piece to add
141  * @returns newPiece if the piece was successfully added, NULL if it was not (ie invalid coordinates specified)
142  *
143  */
144 Piece * Board::Set(int x, int y, Piece * newPiece)
145 {
146         if (board == NULL || x < 0 || y < 0 || x >= width || y >= height)
147                 return NULL;
148         board[x][y] = newPiece;
149
150         //if (newPiece->GetColour() == Piece::RED)
151         //      red.push_back(newPiece);
152         //else if (newPiece->GetColour() == Piece::BLUE)
153         //      blue.push_back(newPiece);
154
155         return newPiece;
156 }
157
158
159 /**
160  * HELPER - Convert a string to a direction
161  * @param str - The string to convert to a direction
162  * @returns The equivalent Direction
163  */
164 Board::Direction Board::StrToDir(const string & str)
165 {
166         if (str == "UP")
167                 return UP;
168         else if (str == "DOWN")
169                 return DOWN;
170         else if (str == "LEFT")
171                 return LEFT;
172         else if (str == "RIGHT")
173                 return RIGHT;
174
175         return NONE;
176 }
177
178 /**
179  * HELPER - Convert a Direction to a string
180  * @param dir - the Direction to convert
181  * @param str - A buffer string, which will contain the string representation of the Direction once this function returns.
182  */
183 void Board::DirToStr(const Direction & dir, string & str)
184 {
185         str.clear();
186         switch (dir)
187         {
188                 case UP:
189                         str = "UP";
190                         break;
191                 case DOWN:
192                         str = "DOWN";
193                         break;
194                 case LEFT:
195                         str = "LEFT";
196                         break;
197                 case RIGHT:
198                         str = "RIGHT";
199                         break;
200                 default:
201                         str = "NONE";
202                         break;
203         }
204 }
205
206 /**
207  * HELPER - Translates the given coordinates in a specified direction
208  * @param x - x coord
209  * @param y - y coord
210  * @param dir - Direction to move in
211  * @param multiplier - Number of times to move
212  *
213  */
214 void Board::MoveInDirection(int & x, int & y, const Direction & dir, int multiplier)
215 {
216         switch (dir)
217         {
218                 case UP:
219                         y -= multiplier;
220                         break;
221                 case DOWN:
222                         y += multiplier;
223                         break;
224                 case LEFT:
225                         x -= multiplier;
226                         break;
227                 case RIGHT:
228                         x += multiplier;
229                         break;
230                 default:
231                         break;
232         }
233 }
234
235 /**
236  * HELPER - Returns the best direction to move in to get from one point to another
237  * @param x1 - x coord of point 1
238  * @param y1 - y coord of point 1
239  * @param x2 - x coord of point 2
240  * @param y2 - y coord of point 2
241  * @returns The best direction to move in
242  */
243 Board::Direction Board::DirectionBetween(int x1, int y1, int x2, int y2)
244 {
245
246
247         double xDist = (x2 - x1);
248         double yDist = (y2 - y1);
249         if (abs(xDist) >= abs(yDist))
250         {
251                 if (xDist < 0)
252                         return LEFT;
253                 else 
254                         return RIGHT;
255         }
256         else
257         {
258                 if (yDist < 0)
259                         return UP;
260                 else
261                         return DOWN;
262         }
263         return NONE;
264
265         
266
267         
268 }
269
270 /**
271  * Searches the board's red and blue arrays for the piece, and removes it
272  * DOES NOT delete the piece. Calling function should delete piece after calling this function.
273  * @param forget - The Piece to forget about
274  * @returns true if the piece was actually found
275  */
276 bool Board::ForgetPiece(Piece * forget)
277 {       
278         if (forget == NULL)
279                 return false;
280         
281         vector<Piece*> & in = GetPieces(forget->colour); bool result = false;
282         for (vector<Piece*>::iterator i=in.begin(); i != in.end(); ++i)
283         {
284                 
285                 if ((*i) == forget)
286                 {
287                         i = in.erase(i);
288                         result = true;
289                         
290                         continue;
291                 }
292                 
293                 
294         }
295
296         
297         return result;
298 }
299
300 /**
301  * Construct the Forfax AI
302  */
303 Forfax::Forfax() : board(NULL), colour(Piece::NONE), strColour("NONE"), turnNumber(0)
304 {
305         //By default, Forfax knows nothing; the main function in main.cpp calls Forfax's initialisation functions
306 }
307
308 /**
309  * Destroy Forfax
310  */
311 Forfax::~Forfax()
312 {
313         //fprintf(stderr,"Curse you mortal for casting me into the fires of hell!\n");
314         //Errr...
315         if (board != NULL)
316                 delete board;
317 }
318
319 /**
320  * Calculate the probability that attacker beats defender in combat
321  * @param attacker The attacking piece
322  * @param defender The defending piece
323  * @returns A double between 0 and 1 indicating the probability of success
324  */
325
326 double Forfax::CombatSuccessChance(Piece * attacker, Piece * defender) const
327 {
328         double probability=1;
329         for (Piece::Type aRank = attacker->minRank; aRank <= attacker->maxRank; aRank = (Piece::Type)((int)(aRank) + 1))
330         {
331                 double lesserRanks=0; double greaterRanks=0;
332                 for (Piece::Type dRank = defender->minRank; dRank <= defender->maxRank; dRank = (Piece::Type)((int)(dRank) + 1))
333                 {
334                         if (dRank < aRank)
335                                 lesserRanks += remainingUnits[defender->colour][(int)(dRank)];
336                         else if (dRank > aRank)
337                                 greaterRanks += remainingUnits[defender->colour][(int)(dRank)];
338                         else
339                         {
340                                 lesserRanks++; greaterRanks++;
341                         }
342                 }
343                 probability *= lesserRanks/(lesserRanks + greaterRanks);
344         }
345         return probability;
346 }
347
348 /**
349  * Calculate the score of a move
350  * TODO: Alter this to make it better
351  * @param piece - The Piece to move
352  * @param dir - The direction in which to move
353  * @returns a number between 0 and 1, indicating how worthwhile the move is
354  */
355 double Forfax::MovementScore(Piece * piece, const Board::Direction & dir) const
356 {
357         assert(piece != NULL);
358
359         
360         int x2 = piece->x; int y2 = piece->y;
361         Board::MoveInDirection(x2, y2, dir);
362
363         
364
365         double basevalue;
366         if (!board->ValidPosition(x2, y2) || !piece->Mobile())
367         {
368                 
369                 basevalue = 0;
370         }
371         else if (board->Get(x2, y2) == NULL)
372         {
373                 basevalue = 0.5*IntrinsicWorth(x2, y2);
374                 
375         }
376         else if (board->Get(x2, y2)->colour != Piece::Opposite(piece->colour))
377         {
378                 basevalue = 0;
379         }
380         else 
381         {
382                 Piece * defender = board->Get(x2, y2);
383                 double combatSuccess = CombatSuccessChance(piece, defender);
384                 basevalue = IntrinsicWorth(x2, y2)*combatSuccess*VictoryScore(piece, defender) + (1.0 - combatSuccess)*DefeatScore(piece, defender);
385         }
386
387         if (basevalue > 0)
388         {
389                 double oldValue = basevalue;
390                 basevalue -= (double)(1.0/((double)(1.0 + (turnNumber - piece->lastMove))));
391                 if (basevalue < oldValue/8.0)
392                         basevalue = oldValue/8.0;
393         }
394         
395         return basevalue;
396 }
397
398
399 /**
400  * Initialisation for Forfax
401  * Reads information from stdin about the board, and Forfax's colour. Initialises board, and prints appropriate setup to stdout.
402  * @returns true if Forfax was successfully initialised, false otherwise.
403  */
404 Forfax::Status Forfax::Setup()
405 {
406         //The first line is used to identify Forfax's colour, and the size of the board
407         //Currently the name of the opponent is ignored.
408
409         //Forfax then responds with a setup.
410         //Forfax only uses one of two setups, depending on what colour he was assigned.
411         
412         
413         //Variables to store information read from stdin
414         strColour.clear();
415         string strOpponent; int boardWidth; int boardHeight;
416
417         cin >> strColour; cin >> strOpponent; cin >> boardWidth; cin >> boardHeight;
418         if (cin.get() != '\n')
419                 return NO_NEWLINE;
420         
421         //Determine Forfax's colour and respond with an appropriate setup
422         if (strColour == "RED")
423         {
424                 colour = Piece::RED;
425                 cout <<  "FBmSsnsnBn\n";
426                 cout << "BBCMccccyC\n";
427                 cout << "LSGmnsBsSm\n";
428                 cout << "sLSBLnLsss\n";
429         }
430         else if (strColour == "BLUE")
431         {
432                 colour = Piece::BLUE;
433                 cout << "sLSBLnLsss\n"; 
434                 cout << "LSGmnsBsSm\n";
435                 cout << "BBCMccccyC\n";
436                 cout <<  "FBmSsnsnBn\n";                
437         }
438         else
439                 return INVALID_QUERY;
440
441
442         //Create the board
443         //NOTE: At this stage, the board is still empty. The board is filled on Forfax's first turn
444         //      The reason for this is because the opponent AI has not placed pieces yet, so there is no point adding only half the pieces to the board
445         
446         board = new Board(boardWidth, boardHeight);
447         if (board == NULL)
448                 return BOARD_ERROR;
449         return OK;
450 }
451
452 /**
453  * Make a single move
454  * 1. Read result of previous move from stdin (or "START" if Forfax is RED and it is the very first move)
455  * 2. Read in board state from stdin (NOTE: Unused - all information needed to maintain board state is in 1. and 4.)
456  *      TODO: Considering removing this step from the protocol? (It makes debugging annoying because I have to type a lot more!)
457  * 3. Print desired move to stdout
458  * 4. Read in result of chosen move from stdin
459  * @returns true if everything worked, false if there was an error or unexpected query
460  */
461 Forfax::Status Forfax::MakeMove()
462 {
463         ++turnNumber;
464         
465         if (turnNumber == 1)
466         {
467                 Status firstMove = MakeFirstMove();
468                 if (firstMove != OK)
469                         return firstMove;
470         }
471         else
472         {
473                 //Read and interpret the result of the previous move
474                 Status interpret = InterpretMove();
475                 if (interpret != OK) {return interpret;}
476
477                 //Forfax ignores the board state; he only uses the move result lines
478                 
479                 for (int y=0; y < board->Height(); ++y)
480                 {
481                         for (int x = 0; x < board->Width(); ++x)
482                                 cin.get();
483                         if (cin.get() != '\n')
484                                 return NO_NEWLINE;
485                 }
486                 
487         }
488         
489         //Now compute the best move
490         // 1. Construct list of all possible moves
491         //      As each move is added to the list, a score is calculated for that move. 
492         //      WARNING: This is the "tricky" part!
493         // 2. Sort the moves based on their score
494         // 3. Simply use the highest scoring move!
495         
496         list<MovementChoice> choices;
497         vector<Piece*> & allies = board->GetPieces(colour);
498         for (vector<Piece*>::iterator i = allies.begin(); i != allies.end(); ++i)
499         {
500                 choices.push_back(MovementChoice((*i), Board::UP, *this));
501                 choices.push_back(MovementChoice((*i), Board::DOWN, *this));
502                 choices.push_back(MovementChoice((*i), Board::LEFT, *this));
503                 choices.push_back(MovementChoice((*i), Board::RIGHT, *this));
504
505         }
506         
507         choices.sort(); //Actually sort the choices!!!
508         MovementChoice & choice = choices.back(); //The best one is at the back, because sort() sorts the list in ascending order
509         
510         
511
512         //Convert information about the move into a printable form
513         string direction;  Board::DirToStr(choice.dir, direction);
514
515         //Print chosen move to stdout
516         cout << choice.piece->x << " " << choice.piece->y << " " << direction << "\n";
517
518         
519
520
521
522         
523         //Interpret the result of the chosen move
524         return InterpretMove();
525
526         
527
528 }
529
530 /**
531  * Reads and interprets the result of a move
532  * Reads information from stdin
533  * @returns true if the result was successfully interpreted, false if there was a contradiction or error
534  */
535 Forfax::Status Forfax::InterpretMove()
536 {
537         //Variables to store move information
538         int x; int y; string direction; string result = ""; int multiplier = 1; int attackerVal = (int)(Piece::BOMB); int defenderVal = (int)(Piece::BOMB);
539
540
541         //Read in information from stdin
542         cin >> x; cin >> y; cin >> direction; cin >> result;
543
544         //If necessary, read in the ranks of involved pieces (this happens if the outcome was DIES or KILLS or BOTHDIE)
545         if (cin.peek() != '\n')
546         {
547                 string buf = "";                
548                 stringstream s(buf);
549                 cin >> buf;
550                 s.clear(); s.str(buf);
551                 s >> attackerVal;
552
553
554                 buf.clear();
555                 cin >> buf;     
556                 s.clear(); s.str(buf);
557                 s >> defenderVal;
558
559                 
560         }
561         
562         //TODO: Deal with move multipliers somehow (when a scout moves more than one space)
563
564         //Check that the line ends where expected...
565         if (cin.get() != '\n')
566         {
567                 return NO_NEWLINE;
568         }
569
570
571         //Convert printed ranks into internal Piece::Type ranks
572         Piece::Type attackerRank = Piece::Type(Piece::BOMB - attackerVal);
573         Piece::Type defenderRank = Piece::Type(Piece::BOMB - defenderVal);
574
575
576
577         //Work out the square moved into
578         int x2 = x; int y2 = y;
579         Board::Direction dir = Board::StrToDir(direction);
580
581         Board::MoveInDirection(x2, y2, dir, multiplier);
582
583
584         //Determine the attacker and defender (if it exists)
585         Piece * attacker = board->Get(x, y);
586         Piece * defender = board->Get(x2, y2);
587
588
589         //If ranks were supplied, update the known ranks of the involved pieces
590         if (attackerRank != Piece::NOTHING && attacker != NULL)
591         {
592                 assert(attacker->minRank <= attackerRank && attacker->maxRank >= attackerRank);
593                 attacker->minRank = attackerRank;
594                 attacker->maxRank = attackerRank;
595         }
596         if (defenderRank != Piece::NOTHING && defender != NULL)
597         {
598                 assert(defender->minRank <= defenderRank && defender->maxRank >= defenderRank);
599                 defender->minRank = defenderRank;
600                 defender->maxRank = defenderRank;
601
602         }
603
604         //There should always be an attacking piece (but not necessarily a defender)
605         if (attacker == NULL)
606                 return EXPECTED_ATTACKER;
607
608
609         attacker->lastMove = turnNumber; //Update stats of attacking piece (last move)
610
611         //Eliminate certain ranks from the possibilities for the piece based on its movement
612         //(This is useful if the piece was an enemy piece)
613         if (attacker->minRank == Piece::FLAG)
614                 attacker->minRank = Piece::SPY;
615         if (attacker->maxRank == Piece::BOMB)
616                 attacker->maxRank = Piece::MARSHAL;
617         if (multiplier > 1)
618         {
619                 attacker->maxRank = Piece::SCOUT;
620                 attacker->minRank = Piece::SCOUT;
621         }
622
623
624
625
626         //Now look at the result of the move (I wish you could switch strings in C++)
627
628
629         //The move was uneventful (attacker moved into empty square)
630         if (result == "OK")
631         {
632                 if (defender != NULL)
633                         return UNEXPECTED_DEFENDER;
634
635                 //Update board and piece
636                 board->Set(x2, y2, attacker);
637                 board->Set(x, y, NULL);
638                 attacker->x = x2;
639                 attacker->y = y2;
640         }
641         else if (result == "KILLS") //The attacking piece killed the defending piece
642         {
643                 if (defender == NULL || defender->colour == attacker->colour)
644                         return COLOUR_MISMATCH;
645
646
647                 
648
649                 board->Set(x2, y2, attacker);
650                 board->Set(x, y, NULL);
651                 attacker->x = x2;
652                 attacker->y = y2;
653
654                 remainingUnits[(int)(defender->colour)][(int)(defenderRank)]--;
655                 
656
657                 if (!board->ForgetPiece(defender))
658                         return NO_DEFENDER;
659                 delete defender;
660
661         }
662         else if (result == "DIES") //The attacking piece was killed by the defending piece
663         {
664                 
665                 if (defender == NULL || defender->colour == attacker->colour)
666                         return COLOUR_MISMATCH;
667
668                 remainingUnits[(int)(attacker->colour)][(int)(attackerRank)]--;
669
670                 if (!board->ForgetPiece(attacker))
671                         return NO_ATTACKER;
672                 delete attacker;
673
674                 board->Set(x, y, NULL);
675         }
676         else if (result == "BOTHDIE") //Both attacking and defending pieces died
677         {
678                 if (defender == NULL || defender->colour == attacker->colour)
679                         return COLOUR_MISMATCH;
680
681                 remainingUnits[(int)(defender->colour)][(int)(defenderRank)]--;
682                 remainingUnits[(int)(attacker->colour)][(int)(attackerRank)]--;
683
684                 if (board->ForgetPiece(attacker) == false)
685                         return NO_ATTACKER;
686                 if (board->ForgetPiece(defender) == false)
687                         return NO_DEFENDER;
688                 delete attacker;
689                 delete defender;
690                 board->Set(x2, y2, NULL);
691                 board->Set(x, y, NULL);
692         }
693         else if (result == "VICTORY") //The attacking piece captured a flag
694         {
695                 return VICTORY; 
696                 
697         }
698         return OK;
699 }
700
701 /**
702  * Forfax's first move
703  * Sets the state of the board
704  * @returns true if the board was successfully read, false if an error occurred.
705  *
706  */
707 Forfax::Status Forfax::MakeFirstMove()
708 {
709         if (colour == Piece::RED)
710         {
711                 string derp;
712                 cin >> derp;
713                 if (derp != "START")
714                         return INVALID_QUERY;
715                 if (cin.get() != '\n')
716                         return NO_NEWLINE;
717         }
718         else
719         {
720                 //TODO: Fix hack where BLUE ignores RED's first move
721                 while (cin.get() != '\n');
722         }
723         
724         for (int y=0; y < board->Height(); ++y)
725         {
726                 for (int x = 0; x < board->Width(); ++x)        
727                 {
728                         char c = cin.get();
729                         switch (c)
730                         {
731                                 case '.': //Empty square
732                                         break;
733                                 case '+': //Boulder/Obstacle
734                                         board->Set(x, y, new Piece(x, y, Piece::NONE, Piece::BOULDER));
735                                         break;
736                                 case '#': //Enemy piece occupies square
737                                 case '*':
738                                 {
739                                         Piece * toAdd = new Piece(x, y, Piece::Opposite(colour));
740                                         board->Set(x, y, toAdd);
741                                         board->GetPieces(toAdd->colour).push_back(toAdd);
742                                         break;
743                                 }
744                                 default: //Allied piece occupies square
745                                 {
746                                         Piece::Type type = Piece::GetType(c);
747                                         Piece * toAdd = new Piece(x, y, colour, type);
748                                         board->Set(x, y, toAdd);
749                                         board->GetPieces(toAdd->colour).push_back(toAdd);
750                                         break;
751                                 }
752                         }
753                 }
754                 if (cin.get() != '\n')
755                         return NO_NEWLINE;
756         }
757         
758         return OK;
759 }
760
761 /**
762  * Calculates the intrinsic strategic worth of a point on the board
763  * @param x the x coordinate of the point
764  * @param y the y coordinate of the point
765  * @returns a value between 0 and 1, with 0 indicating worthless and 1 indicating highly desirable
766  * (NOTE: No points will actually be worth 0)
767  */
768 double Forfax::IntrinsicWorth(int x, int y) const
769 {
770         static double intrinsicWorth[][10][10] =
771         {
772                 //Red
773                 {
774                 {0.1,0.5,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1},
775                 {0.5,0.5,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1},
776                 {0.2,0.2,0.2,0.2,0.2,0.2,0.2,0.2,0.2,0.2},
777                 {0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3},
778                 {0.6,0.6,0.1,0.1,0.65,0.65,0.1,0.1,0.6,0.6},
779                 {0.6,0.6,0.1,0.1,0.65,0.65,0.1,0.1,0.6,0.6},
780                 {0.6,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.6},
781                 {0.6,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.6},
782                 {0.6,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.6},
783                 {0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7}
784
785
786                 },
787                 //Blue
788                 {
789                 {0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7},
790                 {0.6,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.6},
791                 {0.6,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.6},
792                 {0.6,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.7,0.6},
793                 {0.6,0.6,0.1,0.1,0.65,0.65,0.1,0.1,0.6,0.6},
794                 {0.6,0.6,0.1,0.1,0.65,0.65,0.1,0.1,0.6,0.6},
795                 {0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3,0.3},
796                 {0.2,0.2,0.2,0.2,0.2,0.2,0.2,0.2,0.2,0.2},
797                 {0.5,0.5,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1},
798                 {0.1,0.5,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1}
799                 }
800         };
801
802         return intrinsicWorth[(int)(colour)][x][y];
803 }
804
805 /**
806  * Calculates a score assuming that attacker will beat defender, indicating how much killing that piece is worth
807  * @param attacker the Attacking piece
808  * @param defender the Defending piece
809  * @returns a value between 0 and 1, with 0 indicating worthless and 1 indicating highly desirable
810  */
811 double Forfax::VictoryScore(Piece * attacker, Piece * defender) const
812 {
813         if (defender->minRank == defender->maxRank)
814         {
815                 if (defender->minRank == Piece::FLAG)
816                         return 1;
817                 else if (defender->minRank == Piece::BOMB)
818                         return 0.9;
819         }
820         return max<double>(((defender->maxRank / Piece::BOMB) + (defender->minRank / Piece::BOMB))/2, 0.6);
821 }
822
823 /**
824  * Calculates a score assuming that attacker will lose to defender, indicating how much learning the rank of that piece is worth
825  * @param attacker the Attacking piece
826  * @param defender the Defending piece
827  * @returns a value between 0 and 1, with 0 indicating worthless and 1 indicating highly desirable
828  */
829 double Forfax::DefeatScore(Piece * attacker, Piece * defender) const
830 {
831         if (attacker->minRank == Piece::SPY)
832                 return 0.05;
833
834         if (defender->minRank == defender->maxRank)
835         {
836                 if (defender->minRank == Piece::BOMB)
837                         return 1 - (double)((double)(attacker->minRank) / (double)(Piece::BOMB));
838                 else
839                         return 0.5;
840         }
841
842         double possibleRanks = 0; double totalRanks = 0;
843         for (Piece::Type rank = Piece::NOTHING; rank <= Piece::BOMB; rank = Piece::Type((int)(rank) + 1))
844         {
845                 totalRanks += remainingUnits[(int)(defender->colour)][(int)(rank)];
846                 if (rank >= defender->minRank && rank <= defender->maxRank)
847                         possibleRanks += remainingUnits[(int)(defender->colour)][(int)(rank)];
848                 
849         }
850
851         if (totalRanks > 0)
852                 return (possibleRanks/totalRanks) - (double)((double)(attacker->minRank) / (double)(Piece::BOMB));
853         return 0;
854 }               
855
856 /**
857  * DEBUG - Print the board seen by Forfax to a stream
858  * @param out The stream to print to
859  */
860 void Forfax::PrintBoard(ostream & out)
861 {
862         for (int y = 0; y < board->Height(); ++y)
863         {
864                 for (int x = 0; x < board->Width(); ++x)
865                 {
866                         Piece * at = board->Get(x, y);
867                         if (at == NULL)
868                                 out << ".";
869                         else
870                         {
871                                 if (at->colour == colour)
872                                 {
873                                         out << Piece::tokens[(int)(at->minRank)];
874                                 }
875                                 else if (at->colour == Piece::Opposite(colour))
876                                 {
877                                         out << "#";
878                                 }
879                                 else
880                                 {
881                                         out << "+";
882                                 }
883                         }
884                 }
885                 out << "\n";
886         }
887 }
888
889 //EOF
890

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