19 //nothing, boulder, flag, spy, scout, miner, sergeant, lietenant, captain, major, colonel, general, marshal, bomb, error
20 char Piece::tokens[] = {'.','+','F','y','s','n','S','L','c','m','C','G','M','B','?'};
21 int Piece::maxUnits[] = {0,0,1,1,8,5,4,4,4,3,2,1,1,6,0};
24 int Board::redUnits[] = {0,0,1,1,8,5,4,4,4,3,2,1,1,6,0};
25 int Board::blueUnits[] = {0,0,1,1,8,5,4,4,4,3,2,1,1,6,0};
29 * Constructor for a piece
30 * @param newX - x coord
31 * @param newY - y coord
32 * @param newColour - colour
34 Piece::Piece(int newX, int newY,const Colour & newColour)
35 : x(newX), y(newY), colour(newColour), lastMove(0)
37 minRank[RED] = Piece::FLAG;
38 minRank[BLUE] = Piece::FLAG;
39 maxRank[RED] = Piece::BOMB;
40 maxRank[BLUE] = Piece::BOMB;
44 * Constructor for a piece
45 * @param newX - x coord
46 * @param newY - y coord
47 * @param newColour - colour
48 * @param rankKnownBy - Colour that knows the piece's rank
49 * @param fixedRank - Rank the piece has
51 Piece::Piece(int newX, int newY,const Colour & newColour, const Colour & rankKnownBy, const Type & fixedRank)
52 : x(newX), y(newY), colour(newColour), lastMove(0)
54 if (rankKnownBy == BOTH)
56 minRank[RED] = fixedRank;
57 minRank[BLUE] = fixedRank;
58 maxRank[RED] = fixedRank;
59 maxRank[BLUE] = fixedRank;
63 minRank[rankKnownBy] = fixedRank;
64 maxRank[rankKnownBy] = fixedRank;
66 Colour opposite = Opposite(rankKnownBy);
67 minRank[opposite] = Piece::FLAG;
68 maxRank[opposite] = Piece::BOMB;
80 * Returns the Piece::Type matching a given character
81 * @param token - The character to match
82 * @returns A Piece::Type corresponding to the character, or Piece::ERROR if none was found
84 Piece::Type Piece::GetType(char token)
86 for (int ii=0; ii < Piece::ERROR; ++ii)
88 if (Piece::tokens[ii] == token)
95 * Constructor for the board
96 * @param newWidth - width of the board
97 * @param newHeight - height of the board
100 Board::Board(int newWidth, int newHeight) : width(newWidth), height(newHeight), board(NULL), red(), blue()
102 board = new Piece**[width];
103 for (int x=0; x < width; ++x)
105 board[x] = new Piece*[height];
106 for (int y=0; y < height; ++y)
116 for (int x=0; x < width; ++x)
118 for (int y=0; y < height; ++y)
125 * Retrieve a piece from the board
126 * @param x - x coord of the piece
127 * @param y - y coord of the piece
128 * @returns Piece* to the piece found at (x,y), or NULL if there was no piece, or the coords were invalid
130 Piece * Board::Get(int x, int y) const
132 if (board == NULL || x < 0 || y < 0 || x > width || y > height)
138 * Add a piece to the board
139 * Also updates the red or blue arrays if necessary
140 * @param x - x coord of the piece
141 * @param y - y coord of the piece
142 * @param newPiece - pointer to the piece to add
143 * @returns newPiece if the piece was successfully added, NULL if it was not
146 Piece * Board::Set(int x, int y, Piece * newPiece)
148 if (board == NULL || x < 0 || y < 0 || x > width || y > height)
150 board[x][y] = newPiece;
152 //if (newPiece->GetColour() == Piece::RED)
153 // red.push_back(newPiece);
154 //else if (newPiece->GetColour() == Piece::BLUE)
155 // blue.push_back(newPiece);
162 * Convert a string to a direction
163 * @param str - The string to convert to a direction
164 * @returns The equivelent Direction
166 Board::Direction Board::StrToDir(const string & str)
170 else if (str == "DOWN")
172 else if (str == "LEFT")
174 else if (str == "RIGHT")
181 * Convert a Direction to a string
182 * @param dir - the Direction to convert
183 * @param str - A buffer string
185 void Board::DirToStr(const Direction & dir, string & str)
209 * Moves the co-ords in the specified direction
212 * @param dir - Direction to move in
213 * @param multiplier - Number of times to move
216 void Board::MoveInDirection(int & x, int & y, const Direction & dir, int multiplier)
238 * Returns the best direction to move in to get from one point to another
239 * @param x1 - x coord of point 1
240 * @param y1 - y coord of point 1
241 * @param x2 - x coord of point 2
242 * @param y2 - y coord of point 2
243 * @returns The best direction to move in
245 Board::Direction Board::DirectionBetween(int x1, int y1, int x2, int y2)
249 double xDist = (x2 - x1);
250 double yDist = (y2 - y1);
251 if (abs(xDist) >= abs(yDist))
273 * Searches the board's red and blue arrays for the piece, and removes it
274 * DOES NOT delete the piece. Calling function should delete piece after calling this function.
275 * @param forget - The Piece to forget about
276 * @returns true if the piece was actually found
278 bool Board::ForgetPiece(Piece * forget)
283 vector<Piece*> & in = GetPieces(forget->colour); bool result = false;
284 for (vector<Piece*>::iterator i=in.begin(); i != in.end(); ++i)
305 Forfax::Forfax() : board(NULL), colour(Piece::NONE), strColour("NONE"), turnNumber(0)
307 for (int ii=0; ii <= Piece::BOMB; ++ii)
309 remainingUnits[ii][Piece::RED][Piece::RED] = Piece::maxUnits[ii];
310 remainingUnits[ii][Piece::RED][Piece::BLUE] = Piece::maxUnits[ii];
311 remainingUnits[ii][Piece::BLUE][Piece::RED] = Piece::maxUnits[ii];
312 remainingUnits[ii][Piece::BLUE][Piece::BLUE] = Piece::maxUnits[ii];
323 //fprintf(stderr,"Curse you mortal for casting me into the fires of hell!\n");
330 * Calculate the probability that attacker beats defender in combat, from the point of view of a certain player
333 double Forfax::CombatSuccessChance(Piece * attacker, Piece * defender, const Piece::Colour & accordingTo) const
335 double probability=1;
336 for (Piece::Type aRank = attacker->minRank[accordingTo]; aRank <= attacker->maxRank[accordingTo]; aRank = (Piece::Type)((int)(aRank) + 1))
338 double lesserRanks; double greaterRanks;
339 for (Piece::Type dRank = defender->minRank[accordingTo]; dRank <= defender->maxRank[accordingTo]; dRank = (Piece::Type)((int)(dRank) + 1))
343 else if (dRank > aRank)
347 lesserRanks++; greaterRanks++;
350 probability *= lesserRanks/(lesserRanks + greaterRanks);
356 * Calculate the base score of a move
357 * @param piece - The Piece to move
358 * @param dir - The direction in which to move
359 * @param accordingTo - the colour to use for assumptions
361 double Forfax::MovementBaseScore(Piece * piece, const Board::Direction & dir, const Piece::Colour & accordingTo) const
363 int x2 = piece->x; int y2 = piece->y;
364 Board::MoveInDirection(x2, y2, dir);
366 if (board->Get(x2, y2) == NULL)
368 else if (board->Get(x2, y2)->colour == piece->colour)
371 return CombatSuccessChance(piece, board->Get(x2, y2), accordingTo);
375 * Calculate the total score of a move according to certain colour
376 * @param piece - the piece to move
377 * @param dir - the direction to move in
378 * @param accordingTo - the colour to use
380 double Forfax::MovementTotalScore(Piece * piece, const Board::Direction & dir, const Piece::Colour & accordingTo) const
382 double base = MovementBaseScore(piece, dir, accordingTo);
388 int x = piece->x; int y = piece->y;
389 Board::MoveInDirection(x, y, dir);
390 Piece * old = board->Get(x, y);
391 board->Set(x, y, piece);
392 board->Set(piece->x, piece->y, NULL);
394 list<MovementChoice> opponentMoves;
395 vector<Piece*> & enemies = board->GetPieces(Piece::Opposite(accordingTo));
396 for (vector<Piece*>::iterator i = enemies.begin(); i != enemies.end(); ++i)
398 opponentMoves.push_back(MovementChoice((*i), Board::UP, *this,Piece::Opposite(accordingTo)));
399 opponentMoves.push_back(MovementChoice((*i), Board::DOWN, *this,Piece::Opposite(accordingTo)));
400 opponentMoves.push_back(MovementChoice((*i), Board::LEFT, *this,Piece::Opposite(accordingTo)));
401 opponentMoves.push_back(MovementChoice((*i), Board::RIGHT, *this,Piece::Opposite(accordingTo)));
404 opponentMoves.sort();
408 MovementChoice & best = opponentMoves.back();
410 board->Set(x, y, old);
411 board->Set(piece->x, piece->y, piece);
413 return base / best.score;
422 * Forfax sets himself up
423 * Really should just make input and output stdin and stdout respectively, but whatever
427 //The first line is used to identify Forfax's colour, and the size of the board
428 //Currently the name of the opponent is ignored
431 string strOpponent; int boardWidth; int boardHeight;
433 cin >> strColour; cin >> strOpponent; cin >> boardWidth; cin >> boardHeight;
434 if (cin.get() != '\n')
437 if (strColour == "RED")
440 cout << "FB..........B.\n";
441 cout << "BBCM....cccc.C\n";
442 cout << "LSGmnsBmSsnsSm\n";
443 cout << "sLSBLnLssssnyn\n";
445 else if (strColour == "BLUE")
447 colour = Piece::BLUE;
448 cout << "sLSBLnLssssnyn\n";
449 cout << "LSGmnsBmSsnsSm\n";
450 cout << "BBCM....cccc.C\n";
451 cout << "FB..........B.\n";
462 board = new Board(boardWidth, boardHeight);
463 return (board != NULL);
467 * Forfax makes a move
470 bool Forfax::MakeMove()
473 cerr << "Forfax " << strColour << " making move number " << turnNumber << "...\n";
476 if (!MakeFirstMove())
484 if (!InterpretMove())
491 //Forfax ignores the board state; he only uses the move result lines
492 for (int y=0; y < board->Height(); ++y)
494 for (int x = 0; x < board->Width(); ++x)
496 if (cin.get() != '\n')
503 list<MovementTotalChoice> choices;
504 vector<Piece*> & allies = board->GetPieces(colour);
505 for (vector<Piece*>::iterator i = allies.begin(); i != allies.end(); ++i)
507 choices.push_back(MovementTotalChoice((*i), Board::UP, *this, colour));
508 choices.push_back(MovementTotalChoice((*i), Board::DOWN, *this, colour));
509 choices.push_back(MovementTotalChoice((*i), Board::LEFT, *this, colour));
510 choices.push_back(MovementTotalChoice((*i), Board::RIGHT, *this, colour));
514 MovementTotalChoice & choice = choices.back();
516 string direction; Board::DirToStr(choice.dir, direction);
517 cerr << "Forfax %s computed optimum move of " << choice.piece->x << " " << choice.piece->y << " " << direction << " [score=" << choice.score << "]\n";
518 cout << choice.piece->x << " " << choice.piece->y << " " << direction << "\n";
521 return InterpretMove();
526 bool Forfax::InterpretMove()
528 int x; int y; string direction; string result; int multiplier = 1; int attackerVal = (int)(Piece::BOMB); int defenderVal = (int)(Piece::BOMB);
530 cerr << "Forfax " << strColour << " waiting for movement information...\n";
531 cin >> x; cin >> y; cin >> direction; cin >> result;
532 if (cin.peek() != '\n')
534 cerr << "Forfax " << strColour << " reading multiplier\n";
535 stringstream s(result);
540 if (cin.peek() != '\n')
542 cerr << "Forfax " << strColour << " reading ranks of pieces\n";
543 s.clear(); s.str(result);
547 s.clear(); s.str(result);
554 if (cin.get() != '\n')
556 cerr << "Forfax " << strColour << " didn't recieve new line. Very angry.\n";
557 cerr << "Read result so far: " << x << " " << y << " " << direction << " " << result << " ...\n";
561 Piece::Type attackerRank = Piece::Type(Piece::BOMB - attackerVal);
562 Piece::Type defenderRank = Piece::Type(Piece::BOMB - defenderVal);
564 cerr << "Forfax " << strColour << " interpreting movement result of " << x << " " << y << " " << direction << " " << result << " ...\n";
567 int x2 = x; int y2 = y;
568 Board::Direction dir = Board::StrToDir(direction);
570 Board::MoveInDirection(x2, y2, dir, multiplier);
572 Piece * attacker = board->Get(x, y);
573 Piece * defender = board->Get(x2, y2);
575 if (attacker == NULL)
579 Piece::Colour oppositeColour = Piece::Opposite(attacker->colour);
580 if (attacker->minRank[oppositeColour] == Piece::FLAG)
581 attacker->minRank[oppositeColour] = Piece::SPY;
582 if (attacker->maxRank[oppositeColour] == Piece::BOMB)
583 attacker->maxRank[oppositeColour] = Piece::MARSHAL;
586 attacker->maxRank[oppositeColour] = Piece::SCOUT;
587 attacker->minRank[oppositeColour] = Piece::SCOUT;
596 if (defender != NULL)
598 board->Set(x2, y2, attacker);
599 board->Set(x, y, NULL);
603 else if (result == "KILLS")
605 if (defender == NULL || defender->colour == attacker->colour)
611 board->Set(x2, y2, attacker);
612 board->Set(x, y, NULL);
616 if (attacker->minRank[oppositeColour] < defender->maxRank[oppositeColour])
617 attacker->minRank[oppositeColour] = defender->maxRank[oppositeColour];
620 if (!board->ForgetPiece(defender))
625 else if (result == "DIES")
628 if (defender == NULL || defender->colour == attacker->colour)
630 cerr << "Forfax - Unit " << attacker << " dies \n";
631 if (!board->ForgetPiece(attacker))
635 board->Set(x, y, NULL);
637 else if (result == "BOTHDIE")
639 if (defender == NULL || defender->colour == attacker->colour)
641 if (board->ForgetPiece(attacker) == false)
643 if (board->ForgetPiece(defender) == false)
647 board->Set(x2, y2, NULL);
648 board->Set(x, y, NULL);
650 else if (result == "VICTORY")
661 bool Forfax::MakeFirstMove()
663 if (colour == Piece::RED)
669 if (cin.get() != '\n')
674 //TODO: Fix hack where BLUE ignores RED's first move
675 while (cin.get() != '\n');
678 for (int y=0; y < board->Height(); ++y)
680 for (int x = 0; x < board->Width(); ++x)
688 board->Set(x, y, new Piece(x, y, Piece::NONE, Piece::BOTH, Piece::BOULDER));
693 Piece * toAdd = new Piece(x, y, Piece::Opposite(colour));
694 board->Set(x, y, toAdd);
695 board->GetPieces(toAdd->colour).push_back(toAdd);
700 Piece::Type type = Piece::GetType(c);
701 Piece * toAdd = new Piece(x, y, colour, colour, type);
702 board->Set(x, y, toAdd);
703 board->GetPieces(toAdd->colour).push_back(toAdd);
708 if (cin.get() != '\n')