Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /** Help the Christmas elves fetch presents in a magical labyrinth! **/
- #include <iostream>
- #include <string>
- #include <vector>
- #include <queue>
- #include <unordered_map>
- #include <unordered_set>
- #include <cmath>
- #define ROWS 7
- #define COLS 7
- #define DIRS 4
- #define UP 0
- #define RIGHT 1
- #define DOWN 2
- #define LEFT 3
- #define TURN_PUSH 0
- #define TURN_MOVE 1
- #define LARGE_DISTANCE 1000
- const std::string directions[] = { "UP", "RIGHT", "DOWN", "LEFT" };
- const std::string turnNames[] = { "Push", "Move" };
- int getOppDir(int dir)
- {
- switch (dir)
- {
- case UP:
- return DOWN;
- case DOWN:
- return UP;
- case RIGHT:
- return LEFT;
- case LEFT:
- return RIGHT;
- default:
- throw "Unknown direction";
- break;
- }
- }
- struct Cell
- {
- int row;
- int col;
- void set(int _row, int _col)
- {
- row = _row;
- col = _col;
- }
- int manhattan(const Cell& other) const
- {
- return abs(row - other.row) + abs(col - other.col);
- }
- int toInt() const // I use this as a key when storing a Cell in map or set
- {
- return row * COLS + col;
- }
- bool operator==(const Cell& other) const
- {
- return row == other.row && col == other.col;
- }
- Cell getNeighbor(int direction) const
- {
- Cell neighbor;
- switch (direction)
- {
- case UP:
- neighbor.set(row - 1, col);
- break;
- case DOWN:
- neighbor.set(row + 1, col);
- break;
- case RIGHT:
- neighbor.set(row, col + 1);
- break;
- case LEFT:
- neighbor.set(row, col - 1);
- break;
- default:
- throw "Unknown direction";
- break;
- }
- return neighbor;
- }
- bool inBounds() const
- {
- return row >= 0 && col >= 0 && row < ROWS && col < COLS;
- }
- friend std::ostream& operator<<(std::ostream& os, const Cell& cell)
- {
- os << '[' << cell.row << ',' << cell.col << ']';
- return os;
- }
- };
- struct Player
- {
- int numPlayerCards; // the total number of quests for a player (hidden and revealed)
- Cell playerCell;
- std::string playerTile;
- void set(int _numPlayerCards, int _playerX, int _playerY, const std::string& _playerTile)
- {
- numPlayerCards = _numPlayerCards;
- playerCell.set(_playerY, _playerX);
- playerTile = _playerTile;
- }
- friend std::ostream& operator<<(std::ostream& os, const Player& player)
- {
- os << player.numPlayerCards << " cards; " << player.playerCell << " tile = " << player.playerTile;
- return os;
- }
- };
- struct Item
- {
- std::string itemName;
- Cell itemCell;
- int itemPlayerId;
- void set(const std::string& _itemName, int _itemX, int _itemY, int _itemPlayerId)
- {
- itemName = _itemName;
- itemCell.set(_itemY, _itemX);
- itemPlayerId = _itemPlayerId;
- }
- friend std::ostream& operator<<(std::ostream& os, const Item& item)
- {
- os << item.itemName << ' ' << item.itemCell << " player-" << item.itemPlayerId;
- return os;
- }
- };
- struct Quest
- {
- std::string questItemName;
- int questPlayerId;
- void set(const std::string& _questItemName, int _questPlayerId)
- {
- questItemName = _questItemName;
- questPlayerId = _questPlayerId;
- }
- friend std::ostream& operator<<(std::ostream& os, const Quest& quest)
- {
- os << quest.questItemName << " player-" << quest.questPlayerId;
- return os;
- }
- };
- class Node // for Astar path search
- {
- public:
- Cell position;
- Node* parent;
- int dirFromParentToChild;
- int f;
- int g;
- int h;
- Node(const Cell& _position, Node* _parent, int _dirFromParentToChild, int _g, int _h)
- {
- position = _position;
- parent = _parent;
- dirFromParentToChild = _dirFromParentToChild;
- g = _g;
- h = _h;
- f = g + h;
- }
- bool operator<(const Node& other) const
- {
- return f < other.f;
- }
- };
- class mycomparison // can be used to create q priority queue for path
- {
- public:
- bool operator() (const Node* lhs, const Node* rhs) const
- {
- return lhs->operator<(*rhs);
- }
- };
- int getPathAsNearAsPossible(
- const std::string grid[ROWS][COLS], const Cell& startCell, const Cell& endCell, std::string& path)
- {
- /*
- You may notice that the return value is int
- Actually, it is the final distance (manhattan) from the nearest reachable
- cell to the endCell; (it will be 0 if endCell is reachable from startCell)
- the actual path is put in the passed-by-reference argument "path" (see last argument)
- *****
- Using Astar search,
- I search path from startCell to endCell. As endCell may not be reachable,
- I keep track of the nearest reached to end cell (by manhattan dist)
- */
- }
- struct State
- {
- int turnType;
- std::string grid[ROWS][COLS];
- Player players[2];
- Player* me; // this and the following are just aliases; for ease of use
- Player* opp;
- std::vector<Item> items;
- std::vector<Quest> quests;
- State()
- {
- me = &players[0]; // now we can use me or players[0] for Player0
- opp = &players[1];
- }
- friend std::ostream& operator<<(std::ostream& os, const State& state)
- {
- os << "Turn type: " << turnNames[state.turnType] << std::endl;
- os << "Grid:" << std::endl;
- for (int r = 0; r < ROWS; r++)
- {
- for (int c = 0; c < COLS; c++)
- os << state.grid[r][c] << " ";
- os << std::endl;
- }
- os << "Players:" << std::endl;
- for (int i = 0; i < 2; i++)
- os << state.players[i] << std::endl;
- os << "Items:" << std::endl;
- for (const auto& item : state.items)
- os << item << std::endl;
- os << "Quests:" << std::endl;
- for (const auto& quest : state.quests)
- os << quest << std::endl;
- return os;
- }
- // for the following two functions (pushing a row or column)
- // it is better to see the referee code they shared (link in problem statement)
- // I implemented it in a crude way
- std::string pushRow(int row, int direction, const std::string &tileInPushersHand)
- {
- /*
- the return value is the pushed tile, i.e,
- whenever a player pushes, the tile in player's hand gets inserted at
- one end of the grid, and the tile at the other end of the grid comes out
- into player's hand
- so, this is returned
- ********
- check if direction is one of LEFT and RIGHT
- (avoid using magic numbers as I used below, use words like COLS-1....)
- if LEFT
- pushedTile = leftMost tile
- for col = 0 to col = 5
- tile at col = tile at col+1
- tile at col6 = tile in player's hand
- else // RIGHT
- pushedTile = rightMost tile
- for col = 6 to col = 1
- tile at col = tile at col-1
- tile at col0 = tile in player's hand
- for each player
- if player in in pushed row
- change column
- if goes out of bounds
- warp to the other side
- for each item
- if item in pushed row
- change column
- else if item.col == -1 // implies it is in pusher's hand (assuming only p0 pushes)
- put it in appropriate place
- return pushedTile
- */
- }
- std::string pushCol(int col, int direction, const std::string &tileInPushersHand)
- {
- /*
- same as above but for pushing a column
- */
- }
- };
- struct Agent
- {
- State state;
- void read();
- void act();
- };
- void Agent::read()
- {
- // read turn type
- int turnType;
- std::cin >> turnType; std::cin.ignore();
- state.turnType = turnType;
- // read grid
- for (int r = 0; r < ROWS; r++) {
- for (int c = 0; c < COLS; c++) {
- std::string tile;
- std::cin >> tile; std::cin.ignore();
- state.grid[r][c] = tile;
- }
- }
- // read player data
- for (int i = 0; i < 2; i++) {
- int numPlayerCards; // the total number of quests for a player (hidden and revealed)
- int playerX;
- int playerY;
- std::string playerTile;
- std::cin >> numPlayerCards >> playerX >> playerY >> playerTile; std::cin.ignore();
- state.players[i].set(numPlayerCards, playerX, playerY, playerTile);
- }
- // read items
- state.items.clear();
- int numItems; // the total number of items available on board and on player tiles
- std::cin >> numItems; std::cin.ignore();
- for (int i = 0; i < numItems; i++) {
- std::string itemName;
- int itemX;
- int itemY;
- int itemPlayerId;
- std::cin >> itemName >> itemX >> itemY >> itemPlayerId; std::cin.ignore();
- state.items.emplace_back();
- state.items.back().set(itemName, itemX, itemY, itemPlayerId);
- }
- // read quests
- state.quests.clear();
- int numQuests; // the total number of revealed quests for both players
- std::cin >> numQuests; std::cin.ignore();
- for (int i = 0; i < numQuests; i++) {
- std::string questItemName;
- int questPlayerId;
- std::cin >> questItemName >> questPlayerId; std::cin.ignore();
- state.quests.emplace_back();
- state.quests.back().set(questItemName, questPlayerId);
- }
- }
- void Agent::act()
- {
- Item* itemSought;
- /*
- get the first of my quests from quests
- get the item corresponding to that quest from items, point itemSought to it
- */
- if (state.turnType == TURN_PUSH)
- {
- if (itemSought->itemCell.row == -2)
- {
- std::cerr << "Item sought is with opponent - Pushing 0 Right for now" << std::endl;
- std::cout << "PUSH 0 RIGHT" << std::endl;
- }
- else
- {
- std::string __path;
- int bestNearestDistance = LARGE_DISTANCE;
- int bestRowOrCol = ROWS;
- int bestDir = DIRS;
- int rowDirs[] = { LEFT, RIGHT };
- int colDirs[] = { UP, DOWN };
- for (int r = 0; r < ROWS; r++)
- {
- for (auto d : rowDirs)
- {
- // push a row
- auto pushed = state.pushRow(r, d, state.me->playerTile);
- // get path as near as possible to the quest item
- int distance = LARGE_DISTANCE;
- if (itemSought->itemCell.inBounds() && state.me->playerCell.inBounds())
- distance = getPathAsNearAsPossible(state.grid, state.me->playerCell, itemSought->itemCell, __path);
- // push in opposite direction with the tile that got pushed out earlier
- state.pushRow(r, getOppDir(d), pushed);
- // keep track if it is the best
- if (distance < bestNearestDistance)
- {
- bestNearestDistance = distance;
- bestRowOrCol = r;
- bestDir = d;
- }
- }
- }
- /*
- do the same with col in outer loop and colDirs in inner loop
- */
- std::cout << "PUSH " << bestRowOrCol << " " << directions[bestDir] << std::endl;
- }
- }
- else
- {
- std::string path;
- getPathAsNearAsPossible(state.grid, state.me->playerCell, itemSought->itemCell, path);
- if (path.size() == 0)
- std::cout << "PASS" << std::endl;
- else
- std::cout << "MOVE" << path << std::endl;
- }
- }
- int main()
- {
- Agent agent;
- // game loop
- while (true) {
- agent.read();
- std::cerr << agent.state << std::endl;
- agent.act();
- }
- }
Add Comment
Please, Sign In to add comment