Advertisement
Guest User

Untitled

a guest
Sep 19th, 2020
38
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 25.35 KB | None | 0 0
  1. #include <cstring>
  2. #include <iostream>
  3. #include <iomanip>
  4. #include <climits>
  5. #include <time.h>
  6.  
  7. #define WIDTH  30
  8. #define HEIGHT 20
  9. #define MAX_X  (WIDTH - 1)
  10. #define MAX_Y  (HEIGHT - 1)
  11. #define PLAYERS 4
  12. #define TIME_LIMIT 90
  13. #define MAX_NEIGHBOURS 32
  14.  
  15. //#define WORST_CASE_TESTING
  16. #define NO_MANS_LAND
  17. // Each room which has a neighbour shared with an enemy has its space reduced by this ratio
  18. #define SHARED_ROOM_PENALTY 9 / 10
  19. // We get this much extra space for each room which is available to us but which we don't choose to enter
  20. // #define UNVISITED_ROOM_BONUS 1 / 10
  21. // Each door we pass through reduces our score by this much
  22. // #define DOOR_PENALTY 1
  23.  
  24. using namespace std;
  25.  
  26. const char* RIGHT = "RIGHT";
  27. const char* LEFT = "LEFT";
  28. const char* DOWN = "DOWN";
  29. const char* UP = "UP";
  30. const char* GULP = "GULP";
  31.  
  32. const char* const dirs[] = {RIGHT, LEFT, DOWN, UP};
  33.  
  34. const int xOffsets[] = {1, -1, 0, 0};
  35. const int yOffsets[] = {0, 0, 1, -1};
  36.  
  37. long millis() {
  38.     struct timespec ts;
  39.     clock_gettime(CLOCK_MONOTONIC, &ts);
  40.     return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
  41. }
  42.  
  43. class Player {
  44. public:
  45.     int x;
  46.     int y;
  47.  
  48.     Player() {
  49.         x = -1;
  50.         y = -1;
  51.     }
  52.  
  53.     Player(int p_x, int p_y) {
  54.         x = p_x;
  55.         y = p_y;
  56.     }
  57. };
  58.  
  59. class State {
  60. private:
  61.     unsigned char grid[WIDTH+2][HEIGHT+2];
  62.  
  63. public:
  64.     int numPlayers;
  65.     int thisPlayer;
  66.     int maxDepth;
  67.     int pruneMargin;
  68.     bool pruningEnabled;
  69.     int nodesSearched;
  70.     bool timeLimitEnabled;
  71.     long startTime;
  72.     bool timeLimitReached;
  73.     Player players[PLAYERS];
  74.     // this is a bitmask of living players
  75.     unsigned char alive;
  76.     int deathCount;
  77.     // a list of the players who have died, in chronological order of death
  78.     int deadList[PLAYERS];
  79.  
  80.     State() {
  81.         memset(grid, 0, (WIDTH + 2) * (HEIGHT + 2) * sizeof(char));
  82.         for (int x = 0; x < WIDTH + 2; x++) {
  83.             grid[x][0] = grid[x][HEIGHT + 1] = 255;
  84.         }
  85.         for (int y = 0; y < HEIGHT + 2; y++) {
  86.             grid[0][y] = grid[WIDTH + 1][y] = 255;
  87.         }
  88.         maxDepth = 8;
  89.         pruneMargin = 0;
  90.         pruningEnabled = false;
  91.         nodesSearched = 0;
  92.         timeLimitEnabled = true;
  93.         alive = 255;
  94.         deathCount = 0;
  95.         resetTimer();
  96.     }
  97.  
  98.     inline void resetTimer() {
  99.         startTime = millis();
  100.         timeLimitReached = false;
  101.     }
  102.  
  103.     inline int getMaxDepth() {
  104.         if (isTimeLimitReached()) {
  105.             return 1;
  106.         } else {
  107.             return maxDepth;
  108.         }
  109.     }
  110.  
  111.     inline bool isTimeLimitReached() {
  112.         if (!timeLimitEnabled) {
  113.             return false;
  114.         } else if (timeLimitReached) {
  115.             return true;
  116.         } else if (millis() - startTime >= TIME_LIMIT) {
  117.             timeLimitReached = true;
  118.             return true;
  119.         } else {
  120.             return false;
  121.         }
  122.     }
  123.  
  124.     // Starting from (x,y), move towards xOffset OR yOffset, and find out whether we pass through a door
  125.     inline bool isDoor(int x, int y, int xOffset, int yOffset) const {
  126.         if (xOffset) {
  127.             bool above = occupied(x, y - 1) || occupied(x + xOffset, y - 1);
  128.             bool below = occupied(x, y + 1) || occupied(x + xOffset, y + 1);
  129.             return above && below;
  130.         }
  131. #ifdef TRON_TRACE
  132.         if (yOffset) {
  133. #endif
  134.             bool left = occupied(x - 1, y) || occupied(x - 1, y + yOffset);
  135.             bool right = occupied(x + 1, y) || occupied(x + 1, y + yOffset);
  136.             return left && right;
  137. #ifdef TRON_TRACE
  138.         }
  139.         cerr << "Illegal arguments to isDoor" << endl;
  140.         return false;
  141. #endif
  142.     }
  143.  
  144.     inline bool occupied(int x, int y) const {
  145.         return grid[x + 1][y + 1] & alive;
  146.     }
  147.  
  148.     inline void occupy(int x, int y, int player) {
  149.         players[player].x = x;
  150.         players[player].y = y;
  151.  
  152.         grid[x + 1][y + 1] |= (1 << player);
  153.     }
  154.  
  155.     inline void unoccupy(int x, int y, int player) {
  156.         grid[x + 1][y + 1] &= ~(1 << player);
  157.     }
  158.  
  159.     inline void clear(int x, int y) {
  160.         grid[x + 1][y + 1] = 0;
  161.     }
  162.  
  163.     inline void kill(int player) {
  164.         alive &= ~(1 << player);
  165.         for (int i = 0; i < deathCount; i++) {
  166.             if (deadList[i] == player) {
  167.                 // already dead
  168.                 return;
  169.             }
  170.         }
  171.         deadList[deathCount++] = player;
  172.     }
  173.  
  174.     inline void revive(int player) {
  175.         alive |= (1 << player);
  176.         deathCount--;
  177.     }
  178.  
  179.     inline bool isAlive(int player) const {
  180.         return alive & (1 << player);
  181.     }
  182.  
  183.     inline int livingCount() {
  184.         return numPlayers - deathCount;
  185.     }
  186.  
  187.     inline int x() const {
  188.         return players[thisPlayer].x;
  189.     }
  190.  
  191.     inline int y() const {
  192.         return players[thisPlayer].y;
  193.     }
  194.  
  195.     inline void readTurn(istream& is) {
  196.         is >> numPlayers;
  197.         is >> thisPlayer;
  198.  
  199.         for (int i = 0; i < numPlayers; i++) {
  200.             int tailX;
  201.             int tailY;
  202.             int headX;
  203.             int headY;
  204.  
  205.             is >> tailX;
  206.             is >> tailY;
  207.             is >> headX;
  208.             is >> headY;
  209.  
  210.             if (headX < 0 || (players[i].x == headX && players[i].y == headY)) {
  211.                 // Player is already dead
  212.                 kill(i);
  213.             } else {
  214.                 occupy(tailX, tailY, i);
  215.                 occupy(headX, headY, i);
  216.             }
  217.         }
  218.  
  219.         resetTimer();
  220.         nodesSearched = 0;
  221.     }
  222.  
  223.     void print() {
  224.         for (int y = 0; y < HEIGHT; y++) {
  225.             cerr << "\"";
  226.             for (int x = 0; x < WIDTH; x++) {
  227.                 int player = -1;
  228.                 for (int i = 0; i < numPlayers; i++) {
  229.                     if (x == players[i].x && y == players[i].y) {
  230.                         player = i;
  231.                         break;
  232.                     }
  233.                 }
  234.                 if (player >= 0) {
  235.                     cerr << char('A' + player);
  236.                 } else {
  237.                     unsigned char b = grid[x + 1][y + 1];
  238.                     if (b == 0) {
  239.                         cerr << ' ';
  240.                     } else {
  241.                         int p = 0;
  242.                         while (b >>= 1) p++;
  243.                         cerr << p;
  244.                     }
  245.                 }
  246.             }
  247.             cerr << "\\n\"" << endl;
  248.         }
  249.     }
  250. };
  251.  
  252. class Vor {
  253. public:
  254.     unsigned char player;
  255.     unsigned char distance;
  256.     unsigned short room;
  257. };
  258.  
  259. class Coord {
  260. public:
  261.     unsigned char x;
  262.     unsigned char y;
  263. };
  264.  
  265. class Room {
  266. public:
  267.     short size;
  268.     short neighbourCount;
  269.     short neighbours[MAX_NEIGHBOURS];
  270.     bool shared;
  271.     bool visited;
  272. };
  273.  
  274. class Voronoi {
  275. private:
  276.     int sizes[PLAYERS];
  277.     int regions[PLAYERS];
  278.     Vor grid[WIDTH][HEIGHT];
  279.     Coord openNodes[10000];
  280.     Room rooms[10000];
  281.     int roomCount;
  282.     short equivalences[600];
  283.  
  284.     void clear() {
  285.         memset(grid, 255, sizeof(grid));
  286.         memset(equivalences, -1, sizeof(equivalences));
  287.         roomCount = 0;
  288.     }
  289.  
  290.     #define addNode(xx, yy) {            \
  291.         openNodes[nodeCount].x = xx;     \
  292.         openNodes[nodeCount].y = yy;     \
  293.         nodeCount++;                     \
  294.     }
  295.  
  296.     inline int addRoom() {
  297.         int id = roomCount++;
  298.         Room& room = rooms[id];
  299.         room.size = 0;
  300.         room.neighbourCount = 0;
  301.         room.shared = false;
  302.         room.visited = false;
  303.         return id;
  304.     }
  305.  
  306.     inline int trueId(int id) {
  307.         do {
  308.             register int equiv = equivalences[id];
  309.             if (equiv == -1) {
  310.                 return id;
  311.             }
  312.             id = equiv;
  313.         } while (true);
  314.     }
  315.  
  316.     inline Room& room(int id) {
  317.         return rooms[trueId(id)];
  318.     }
  319.  
  320.     inline void makeNeighbours(int fromId, int toId) {
  321.         Room& from = room(fromId);
  322. #ifdef TRON_TRACE
  323.         if (from.neighbourCount >= MAX_NEIGHBOURS) {
  324.             cerr << "Neighbour limit reached" << endl;
  325.             return;
  326.         }
  327.         if (room(toId).size < 0) {
  328.             cerr << "Making neigbour with dead room" << endl;
  329.         }
  330. #endif
  331.         from.neighbours[from.neighbourCount++] = toId;
  332.     }
  333.  
  334.     inline void combineRooms(int id1, int id2) {
  335. #ifdef TRON_TRACE
  336.         if (id1 == id2) {
  337.             cerr << "Room cannot be combined with itself" << endl;
  338.             return;
  339.         }
  340. #endif
  341.         int combinedId, oldId;
  342.         if (id1 < id2) {
  343.             combinedId = id1;
  344.             oldId = id2;
  345.         } else {
  346.             combinedId = id2;
  347.             oldId = id1;
  348.         }
  349. #ifdef TRON_TRACE
  350.         if (trueId(oldId) == trueId(combinedId)) {
  351.             cerr << "Room " << oldId << " already combined with " << combinedId << endl;
  352.             return;
  353.         }
  354. #endif
  355.  
  356.         Room& combined = room(combinedId);
  357.         Room& old = room(oldId);
  358.  
  359.         combined.size += old.size;
  360.         old.size = -1;
  361.  
  362.         combined.shared = combined.shared || old.shared;
  363.  
  364.         // Delete any neighbour relationship between new room and old, by swapping for the last neighbour in the list
  365.         for (int i = 0; i < combined.neighbourCount; i++) {
  366.             if (combined.neighbours[i] == oldId) {
  367.                 combined.neighbours[i] = combined.neighbours[combined.neighbourCount - 1];
  368.                 combined.neighbourCount--;
  369.                 i--;
  370.             }
  371.         }
  372.         // Follow the old neighbour relationships, fix the inverse relationships, and add as a neighbour of the new room
  373.         for (int i = 0; i < old.neighbourCount; i++) {
  374.             int neighbourId = old.neighbours[i];
  375.             makeNeighbours(combinedId, neighbourId);
  376.         }
  377.  
  378.         old.neighbourCount = 0;
  379.         equivalences[oldId] = combinedId;
  380.     }
  381.  
  382.     int calculateRegionSize(Room& room) {
  383.         if (room.visited) {
  384.             return 0;
  385.         }
  386. #ifdef TRON_TRACE
  387.         if (room.size < 0) {
  388.             cerr << "Dead room while calculating region size" << endl;
  389.         }
  390. #endif
  391.         room.visited = true;
  392.         int maxNeighbourSize = 0;
  393.         int totalNeighbourSize = 0;
  394.         bool sharedNeighbour = false;
  395.         for (int i = 0; i < room.neighbourCount; i++) {
  396.             Room& neighbour = getNeighbour(room, i);
  397.             if (!neighbour.visited) {
  398.                 int size = calculateRegionSize(neighbour);
  399. #ifdef DOOR_PENALTY
  400.                 if (size >= DOOR_PENALTY && room.size != 1 && neighbour.size == 1) {
  401.                     size -= DOOR_PENALTY;
  402.                 }
  403. #endif
  404.                 if (size > maxNeighbourSize) {
  405.                     maxNeighbourSize = size;
  406.                 }
  407.                 totalNeighbourSize += size;
  408.                 if (neighbour.shared) {
  409.                     sharedNeighbour = true;
  410.                 }
  411.             }
  412.         }
  413.         room.visited = false;
  414.         // Multiply the room size, so we can apply 'half cell' bonuses/penalties
  415.         int size = room.size * 2;
  416. #ifdef SHARED_ROOM_PENALTY
  417.         if (sharedNeighbour) {
  418.             size = size * SHARED_ROOM_PENALTY;
  419.         }
  420. #endif
  421. #ifdef UNVISITED_ROOM_BONUS
  422.         size += (totalNeighbourSize - maxNeighbourSize) * UNVISITED_ROOM_BONUS;
  423. #endif
  424.         return size + maxNeighbourSize;
  425.     }
  426.  
  427. public:
  428.     void calculate(const State& state, int turn = 0) {
  429.         clear();
  430.         int nodeCount = 0;
  431.  
  432.         int numPlayers = state.numPlayers;
  433.         for (int i = 0; i < numPlayers; i++) {
  434.             // Ensure that player id = room id, no matter how many players are alive
  435.             addRoom();
  436.             // Calculate in turn order, so the player with the first turn gets the edge on boundary territory
  437.             int playerNum = (i + turn) % state.numPlayers;
  438.             if (state.isAlive(playerNum)) {
  439.                 const Player& player = state.players[playerNum];
  440.                 int px = player.x;
  441.                 int py = player.y;
  442.                 Vor& v = grid[px][py];
  443.                 v.player = playerNum;
  444.                 v.distance = 0;
  445.                 v.room = playerNum;
  446.                 addNode(px, py);
  447.             }
  448.             regions[playerNum] = playerNum;
  449.         }
  450.  
  451.         for (int i = 0; i < nodeCount; i++) {
  452.             int x = openNodes[i].x;
  453.             int y = openNodes[i].y;
  454.             Vor& vor = grid[x][y];
  455.             if (vor.player == 254) {
  456.                 continue;
  457.             }
  458.  
  459.             for (int j = 0; j < 4; j++) {
  460.                 int xOffset = xOffsets[j];
  461.                 int yOffset = yOffsets[j];
  462.                 int xx = x + xOffset;
  463.                 int yy = y + yOffset;
  464.                 if (!state.occupied(xx, yy)) {
  465.                     int vorRoom = trueId(vor.room);
  466.                     Vor& neighbour = grid[xx][yy];
  467.                     int neighbourPlayer = neighbour.player;
  468.                     if (neighbourPlayer == 255) {
  469.                         neighbour.player = vor.player;
  470.                         neighbour.distance = vor.distance + 1;
  471.                         int neighbourRoom;
  472.                         if (state.isDoor(x, y, xOffset, yOffset)) {
  473.                             neighbourRoom = addRoom();
  474.                             makeNeighbours(vorRoom, neighbourRoom);
  475.                         } else {
  476.                             neighbourRoom = vorRoom;
  477.                         }
  478.                         addNode(xx, yy);
  479.                         neighbour.room = neighbourRoom;
  480.                         // neighbourRoom is definitely not dead
  481.                         rooms[neighbourRoom].size++;
  482.                     } else {
  483.                         int neighbourRoom = trueId(neighbour.room);
  484.                         if (vorRoom != neighbourRoom) {
  485.                             if (neighbourPlayer == vor.player) {
  486.                                 if (state.isDoor(x, y, xOffset, yOffset)) {
  487.                                     makeNeighbours(vorRoom, neighbourRoom);
  488.                                 } else {
  489.                                     combineRooms(vorRoom, neighbourRoom);
  490.                                 }
  491.                             } else {
  492.                                 // Join the regions (buggy, because it might assign p0.region = 1, then later p1.region = 0)
  493.                                 // regions[neighbourPlayer] = regions[vor.player];
  494. #ifdef NO_MANS_LAND
  495.                                 if (neighbourPlayer != 254) {
  496.                                     // Join the regions
  497.                                     regions[neighbourPlayer] = regions[vor.player];
  498.  
  499.                                     if (neighbour.distance == vor.distance + 1) {
  500.                                         // This is a shared boundary: remove it from the other player's territory
  501.                                         // neighbourRoom is definitely not dead
  502.                                         rooms[neighbourRoom].size--;
  503.                                         // This cell is no man's land
  504.                                         neighbour.player = 254;
  505.                                     }
  506.                                 }
  507. #endif
  508.                                 // Penalise both rooms
  509.                                 // neighbourRoom and vorRoom are definitely not dead
  510.                                 rooms[neighbourRoom].shared = true;
  511.                                 rooms[vorRoom].shared = true;
  512.                             }
  513.                         }
  514.                     }
  515.                 }
  516.             }
  517.         }
  518.  
  519.         for (int i = 0; i < numPlayers; i++) {
  520.             if (state.isAlive(i)) {
  521.                 Room& room = startingRoom(i);
  522.                 sizes[i] = calculateRegionSize(room);
  523.             } else {
  524.                 sizes[i] = 0;
  525.             }
  526.         }
  527.     }
  528.  
  529.     inline int playerRegionSize(int player) const {
  530.         return sizes[player];
  531.     }
  532.  
  533.     inline const Vor& get(int x, int y) const {
  534.         return grid[x][y];
  535.     }
  536.  
  537.     void print() const {
  538.         cerr << hex;
  539.         for (int y = 0; y <= MAX_Y; y++) {
  540.             for (int x = 0; x <= MAX_X; x++) {
  541.                 Vor vor = get(x, y);
  542.                 cerr << setw(3) << (int(vor.player) >= 255 ? 255 : int(vor.player));
  543.             }
  544.             cerr << endl;
  545.         }
  546.         cerr << dec;
  547.     }
  548.  
  549.     inline int regionForPlayer(int player) const {
  550.         return regions[player];
  551.     }
  552.  
  553.     inline Room& startingRoom(int player) {
  554.         return rooms[player];
  555.     }
  556.  
  557.     inline Room& getNeighbour(const Room& r, int index) {
  558.         int roomId = r.neighbours[index];
  559.         return room(roomId);
  560.     }
  561. };
  562.  
  563. class Scores {
  564. public:
  565.     int scores[PLAYERS];
  566.     int ranks[PLAYERS];
  567.     int regions[PLAYERS];
  568.     unsigned int losers;
  569.     const char* move;
  570. #ifdef TRON_TRACE
  571.     char moves[30];
  572. #endif
  573.  
  574.     inline Scores() {
  575.         losers = 0;
  576.         move = "";
  577.     }
  578.  
  579.     Scores(int score0, int score1) {
  580.         scores[0] = score0;
  581.         scores[1] = score1;
  582.         losers = 0;        
  583.     }
  584.  
  585.     inline void clearFlags() {
  586.         losers = 0;
  587.     }
  588.  
  589.     inline void setLoser(int player) {
  590.         losers |= (1 << player);
  591.     }
  592.  
  593.     inline bool isLoser(int player) {
  594.         return losers & (1 << player);
  595.     }
  596.  
  597.     inline void print() const {
  598.         for (int i = 0; i < PLAYERS; i++) {
  599.             if (i != 0) {
  600.                 cerr << " / ";
  601.             }
  602.             cerr << scores[i];
  603.         }
  604.         cerr << " / " << move;
  605. #ifdef TRON_TRACE
  606.         cerr << " / " << moves;
  607. #endif
  608.         cerr << endl;
  609.     }
  610. };
  611.  
  612. class Bounds {
  613. public:
  614.     int bounds[PLAYERS];
  615. #ifdef TRON_TRACE
  616.     char moves[PLAYERS][30];
  617. #endif
  618.  
  619.     Bounds() {
  620.         for (int i = 0; i < PLAYERS; i++) {
  621.             bounds[i] = INT_MIN;
  622.         }
  623.     }
  624. };
  625.  
  626. void calculateScores(Scores& scores, Voronoi& voronoi, State& state, int turn) {
  627.     voronoi.calculate(state, turn);
  628.  
  629.     // for (int i = 0; i < state.numPlayers; i++) {
  630.     //     scores.regions[i] = voronoi.regionForPlayer(i);
  631.     // }
  632.  
  633.     for (int i = 0; i < state.numPlayers; i++) {
  634.         scores.scores[i] = voronoi.playerRegionSize(i);
  635.     }
  636.  
  637.     // penalise dead people. revive everyone and go through the deaths in order.
  638.     bool dead[PLAYERS] = {false, false, false, false};
  639.     int aliveCount = state.numPlayers;
  640.     for (int j = 0; j < state.deathCount; j++) {
  641.         aliveCount--;
  642.         int player = state.deadList[j];
  643.         scores.scores[player] -= 1000;
  644.         dead[player] = true;
  645.  
  646.         // give the points to the players who were still alive at that point
  647.         if (aliveCount > 0) {
  648.             for (int i = 0; i < state.numPlayers; i++) {
  649.                 if (!dead[i]) {
  650.                     scores.scores[i] += (1000 / aliveCount);
  651.                 }
  652.             }
  653.         }
  654.     }
  655.  
  656.     // calculate ranks
  657.     for (int i = 0; i < state.numPlayers; i++) {
  658.         scores.ranks[i] = 0;
  659.         for (int j = 0; j < state.numPlayers; j++) {
  660.             if (i != j && scores.scores[j] < scores.scores[i]) {
  661.                 scores.ranks[i]++;
  662.             }
  663.         }
  664.     }
  665. }
  666.  
  667. typedef void (*ScoreCalculator)(Scores& scores, Bounds& bounds, State& state, int turn, void* scoreCalculator, void* data);
  668.  
  669. inline bool checkBounds(Bounds& bounds, Scores& scores, State& state, int player) {
  670.     if (!state.pruningEnabled) {
  671.         return false;
  672.     }
  673.     int region = scores.regions[player];
  674.     for (int i = 0; i < state.numPlayers; i++) {
  675.         // Is bound exceeded?
  676.         if (i != player && scores.scores[i] + state.pruneMargin <= bounds.bounds[i]
  677.             // Is this player's score connected to mine (negative correlation)?
  678.             && scores.regions[i] == region
  679.             // Is this player's score connected to mine (positive correlation)?
  680.             && !(scores.isLoser(i) && scores.isLoser(player))) {
  681. #ifdef TRON_TRACEX
  682.             cerr << "Score " << scores.scores[i] << " breaks bound " << bounds.bounds[i] << " for player " << i
  683.                 << " from " << bounds.moves[i] << endl;
  684.             scores.print();
  685. #endif
  686.             return true;
  687.         }
  688.     }
  689.     return false;
  690. }
  691.  
  692. // The given score will be chosen over the best score so far if it reduces *our* rank - this is an "avoid worst case" strategy
  693. inline bool worsensOurRank(Scores& scores, Scores& bestScores, int player, int thisPlayer) {
  694.     if (player == thisPlayer) {
  695.         return false;
  696.     }
  697.     return scores.ranks[thisPlayer] < bestScores.ranks[thisPlayer];
  698. }
  699.  
  700. // The given score will be chosen over the best score so far if it improves the player's score (preserving rank)
  701. inline bool improvesTheirScore(Scores& scores, Scores& bestScores, int player) {
  702.     return scores.ranks[player] == bestScores.ranks[player] && scores.scores[player] > bestScores.scores[player];
  703. }
  704.  
  705. // The given score will be chosen over the best score so far if it improves the player's rank
  706. inline bool improvesTheirRank(Scores& scores, Scores& bestScores, int player) {
  707.     return scores.ranks[player] > bestScores.ranks[player];
  708. }
  709.  
  710. void minimax(Scores& scores, Bounds& parentBounds, State& state, int turn, void* sc, void* data) {
  711.     state.nodesSearched++;
  712.     Bounds bounds = parentBounds;
  713.     ScoreCalculator scoreCalculator = (ScoreCalculator) sc;
  714.  
  715.     int player = (state.thisPlayer + turn) % state.numPlayers;
  716.  
  717.     // Skip dead players, and fast forward to scoring when only one player left alive
  718.     if (!state.isAlive(player) || state.livingCount() == 1) {
  719. #ifdef TRON_TRACE
  720.         scores.moves[turn] = 'X';
  721.         scores.moves[turn + 1] = 0;
  722. #endif
  723.         scoreCalculator(scores, bounds, state, turn + 1, (void*) scoreCalculator, data);
  724.         return;
  725.     }
  726.  
  727.     Scores bestScores;
  728.     bestScores.scores[player] = INT_MIN;
  729.     bestScores.ranks[player] = 0;
  730.  
  731.     int origX = state.players[player].x;
  732.     int origY = state.players[player].y;
  733.  
  734.     for (int i = 0; i < 4; i++) {
  735.         int x = origX + xOffsets[i];
  736.         int y = origY + yOffsets[i];
  737.         if (!state.occupied(x, y)) {
  738. #ifdef TRON_TRACE
  739.             scores.moves[turn] = dirs[i][0];
  740.             scores.moves[turn + 1] = 0;
  741. #endif
  742.             state.occupy(x, y, player);
  743.             scoreCalculator(scores, bounds, state, turn + 1, (void*) scoreCalculator, data);
  744.             state.unoccupy(x, y, player);
  745.             state.occupy(origX, origY, player); // restore player position
  746.             scores.move = dirs[i];
  747.             if (checkBounds(bounds, scores, state, player)) {
  748. #ifdef TRON_TRACEX
  749.                 cerr << "Pruned at " << scores.moves << endl;
  750.                 cerr << "          ";
  751.                 for (int j = 0; j < turn; j++) {
  752.                     cerr << " ";
  753.                 }
  754.                 cerr << "^" << endl;
  755. #endif
  756.                 return;
  757.             }
  758. #ifdef TRON_TRACE
  759.             for (int k = 0; k < turn; k++) cerr << "  ";
  760.             cerr << "player " << player << ": ";
  761.             scores.print();
  762. #endif
  763.             if (improvesTheirRank(scores, bestScores, player)
  764. #ifdef WORST_CASE_TESTING
  765.                     || worsensOurRank(scores, bestScores, player, state.thisPlayer)
  766. #endif
  767.                     || improvesTheirScore(scores, bestScores, player)) {
  768.                 bestScores = scores;
  769.                 if (state.pruningEnabled) {
  770.                     bounds.bounds[player] = scores.scores[player];
  771. #ifdef TRON_TRACE
  772.                     strcpy(bounds.moves[player], scores.moves);
  773. #endif
  774.                 }
  775.             }
  776.         }
  777.     }
  778.  
  779.     if (bestScores.scores[player] == INT_MIN) {
  780.         // All moves are illegal - player dies and turn passes to the next player
  781. #ifdef TRON_TRACE
  782.         scores.moves[turn] = 'G';
  783.         scores.moves[turn + 1] = 0;
  784. #endif
  785.         state.kill(player);
  786.         scoreCalculator(scores, bounds, state, turn + 1, (void*) scoreCalculator, data);
  787.         state.revive(player);
  788.         scores.move = "UP";
  789.     } else {
  790.         scores = bestScores;
  791. #ifdef TRON_TRACE
  792.         for (int k = 0; k < turn; k++) cerr << "  ";
  793.         cerr << "player " << player << " chose ";
  794.         scores.print();
  795. #endif
  796.     }
  797. }
  798.  
  799. void voronoiRecursive(Scores& scores, Bounds& bounds, State& state, int turn, void* sc, void* data) {
  800.     if (turn >= state.getMaxDepth()) {
  801.         calculateScores(scores, *((Voronoi*)data), state, turn);
  802.     } else {
  803.         minimax(scores, bounds, state, turn, sc, data);
  804.     }
  805. }
  806.  
  807. void run() {
  808.     State state;
  809.     Scores scores;
  810.     Voronoi voronoi;
  811.     Bounds bounds;
  812.  
  813.     while (1) {
  814.         state.readTurn(cin);
  815.  
  816.         // for (int i = 0; i < state.numPlayers; i++) {
  817.         //     cerr << state.players[i].x << "," << state.players[i].y << endl;
  818.         // }
  819.  
  820.         long start = millis();
  821.         minimax(scores, bounds, state, 0, (void*) voronoiRecursive, &voronoi);
  822.         long elapsed = millis() - start;
  823.         cerr << elapsed << "ms";
  824.         if (state.isTimeLimitReached()) {
  825.             cerr << " (timeout)";
  826.         }
  827.         cerr << endl;
  828.  
  829.         for (int i = 0; i < state.numPlayers; i++) {
  830.             cerr << scores.scores[i];
  831.             if (i == state.thisPlayer) {
  832.                 cerr << "*";
  833.             }
  834.             cerr << endl;
  835.         }
  836.         cerr << state.maxDepth << " plies" << endl;
  837.         cerr << state.nodesSearched << " nodes" << endl;
  838.  
  839.         cout << scores.move << endl;
  840.     }
  841. }
  842.  
  843. #if !defined(TRON_TESTS) && !defined(TRON_PROF)
  844. int main() {
  845.     run();
  846.     return 0;
  847. }
  848. #endif
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement