#include #include #include #include #define pt cout << #define pter cerr << #define nl "\n" #define in cin >> #define all(x) x.begin(), x.end() using namespace std; // -------- START RAII HELPERS-------- unsigned long allocatedMem = 0; void *alloc(size_t size) { void *ptr = malloc(size); if (!ptr) { pter "Memory allocation failed" << nl; return NULL; } allocatedMem += 1; return ptr; } void dealloc(void *ptr) { if (ptr) free(ptr); allocatedMem -= 1; } bool assert_alloc() { return !allocatedMem; } // -------- END RAII HELPERS-------- // -------- START SIMPLE VEC IMPLEMENTATION -------- template class myVec { T *data; size_t capacity; size_t length; void resize() { if (length == capacity || !length) { capacity = capacity ? capacity << 1 : 1; T *newData = static_cast(alloc(capacity * sizeof(T))); for (size_t i = 0; i < length; i++) { newData[i] = data[i]; } dealloc(data); data = newData; } } public: myVec() { capacity = 0; length = 0; } void push_back(const T &val) { resize(); data[length++] = val; } void pop_back() { if (length) { length--; } } T &operator[](size_t index) { return data[index]; } int size() { return length; } }; // -------- END SIMPLE VEC IMPLEMENTATION -------- struct Move { char player; int moveX; int moveY; }; struct Moves { int x; int y; myVec moves; }; bool isValidPlayer(char c) { return c >= 'A' && c <= 'Z' && c != 'X'; } void swapChar(char &a, char &b) { if (a == b) { return; } a ^= b; b ^= a; a ^= b; } // Recursively reverse a string void reverseString(string &str, int start, int end) { if (start >= end) { return; } swapChar(str[start], str[end]); reverseString(str, start + 1, end - 1); } string toString(int x) { if (!x) return "0"; string result; while (x) { result.push_back(x % 10 + '0'); x /= 10; } reverseString(result, 0, result.size() - 1); return result; } Moves serialize(const string &s) { Moves result; istringstream iss(s); iss >> result.x >> result.y; if (result.x < 2 || result.y < 2) { string err = "Invalid board size: "; err.append(toString(result.x)); err.append("x"); err.append(toString(result.y)); throw invalid_argument(err); } result.x = (result.x << 1) - 1; result.y = (result.y << 1) - 1; string player; int x, y; while (iss >> player >> x >> y) { if (player == "END") break; // TODO: move this exceptions to runtime checks if (x >= result.x || y >= result.y) { string err = "Invalid move by: " + player + " (" + toString(x) + ", " + toString(y) + ")"; throw invalid_argument(err); } if (!isValidPlayer(player[0])) { string err = "Invalid player: " + player; throw invalid_argument(err); } Move move; move.player = player[0]; move.moveX = x; move.moveY = y; result.moves.push_back(move); } return result; } char **allocBoard(int x, int y) { char **board = static_cast(alloc(x * sizeof(char *))); for (int i = 0; i < x; ++i) { board[i] = static_cast(alloc(y * sizeof(char))); } return board; } void initBoard(char **board, int x, int y) { for (int i = 0; i < x; ++i) { for (int j = 0; j < y; j++) { board[i][j] = (j & 1 || i & 1) ? ' ' : '.'; } } } char **allocAndInitBoard(int x, int y) { char **board = allocBoard(x, y); initBoard(board, x, y); return board; } void deallocBoard(char **board, int x) { for (int i = 0; i < x; ++i) { dealloc(board[i]); } dealloc(board); } void printBoard(char **board, int x, int y) { for (int i = 0; i < x; ++i) { for (int j = 0; j < y; ++j) { pt board[i][j]; } pt nl; } pt endl; } char toLowerCase(char c) { return c | 32; } int normalize(char c) { return toLowerCase(c) - 'a'; } void swap(pair > *a, int i, int j) { pair > temp = a[i]; a[i] = a[j]; a[j] = temp; } void bubbleSort(pair > *a) { for (int i = 0; i < 25; i++) { for (int j = 0; j < 25 - i; j++) { if (a[j].second.first > a[j + 1].second.first) swap(a, j, j + 1); } } } void printScores(int *points, char blacklist) { pair > *arr = static_cast > *>(alloc( 26 * sizeof(pair >))); for (int i = 0; i < 26; i++) { pair > p; pair p1; p1.first = points[i]; p1.second = false; p.first = static_cast(i + 'A'); p.second = p1; arr[i] = p; } for (int i = 0; i < 26; i++) { if (arr[i].second.first == -1) { continue; } for (int j = 0; j < 26; j++) { if (arr[j].second.first == -1 || arr[j].first == arr[i].first) { continue; } if (arr[i].second.first == arr[j].second.first) { arr[i].second.second = true; arr[j].second.second = true; } } } bubbleSort(arr); for (int i = 0; i < 26; i++) { if (arr[i].second.first != -1) { pt arr[i].first << ": " << arr[i].second.first << " boxes" << ( (i == 25 || arr[i].second.second) && arr[i].first != blacklist ? arr[i].second.second ? " (tie)\n" : " (win)\n" : nl); } } dealloc(arr); pt endl; } bool isValidMove(int x, int y, char **board) { return (x & 1) != (y & 1) && board[x][y] == ' '; } void addPoint( int *points, char **board, int x, int y, char player ) { if (board[x][y] == ' ') { if (points[normalize(player)] == -1) { points[normalize(player)] = 1; } else { points[normalize(player)]++; } board[x][y] = player; } } void solveVertical( int moveX, int moveY, char **board, int *points, int x, int y, char player ) { // TODO: fix ambiguity: // - // | // - // in the condition above, we do not know what to do at the end of the board // need to check for x-1, x+1, y-1, y+1 // check for any box below current move if (moveX < x - 3 && board[moveX + 2][moveY] != ' ' && board[moveX + 1][moveY - 1] != ' ') { int rightEdge = moveY == y - 1 ? 1 : board[moveX + 1][moveY + 1] != ' '; if (rightEdge) { addPoint(points, board, moveX + 1, moveY, player); } } // check for any box above current move if (moveX > 1 && board[moveX - 2][moveY] != ' ' && board[moveX - 1][moveY - 1] != ' ') { int rightEdge = moveY == y - 1 ? 1 : board[moveX - 1][moveY + 1] != ' '; if (rightEdge) { addPoint(points, board, moveX - 1, moveY, player); } } } void solveHorizontal( int moveX, int moveY, char **board, int *points, int y, char player ) { if (moveY) { if (moveY == y - 1) { if (board[moveX][moveY - 2] != ' ' && ( board[moveX + 1][moveY - 1] != ' ' && board[moveX - 1][moveY - 1] != ' ')) { addPoint(points, board, moveX, moveY - 1, player); } } else { if (board[moveX][moveY - 2] != ' ' || board[moveX][moveY + 2] != ' ') { if (board[moveX + 1][moveY - 1] != ' ' && board[moveX - 1][moveY - 1] != ' ') { addPoint(points, board, moveX, moveY - 1, player); } if (board[moveX + 1][moveY + 1] != ' ' && board[moveX - 1][moveY + 1] != ' ') { addPoint(points, board, moveX, moveY + 1, player); } } } } else { if (board[moveX][moveY + 2] != ' ' && (board[moveX + 1][moveY + 1] != ' ' && board[moveX - 1][moveY + 1] != ' ')) { addPoint(points, board, moveX, moveY + 1, player); } } } void solve(Moves moves, char **board) { int *points = static_cast(alloc(26 * (sizeof(int)))); for (int i = 0; i < 26; i++) { points[i] = -1; } for (int i = 0; i < moves.moves.size(); i++) { Move move = moves.moves[i]; int moveX = move.moveX; int moveY = move.moveY; char player = move.player; points[normalize(player)] = max(0, points[normalize(player)]); if (!isValidMove(moveX, moveY, board)) { pt "Invalid or Repeated move by: " << player << " at (x, y): (" << moveX << ", " << moveY << ")" << nl; board[moveX][moveY] = 'X'; printBoard(board, moves.x, moves.y); printScores(points, player); pt "Exiting.." << nl; return; } board[moveX][moveY] = toLowerCase(player); if (!(moveX & 1) && (moveY & 1)) { solveVertical(moveX, moveY, board, points, moves.x, moves.y, player); } else if ((moveX & 1) && !(moveY & 1)) { solveHorizontal(moveX, moveY, board, points, moves.y, player); } else { pt "Invalid move by: " << player << " at (x, y): (" << moveX << ", " << moveY << ")" << nl; board[moveX][moveY] = 'X'; printBoard(board, moves.x, moves.y); printScores(points, player); pt "Exiting.." << nl; return; } } printBoard(board, moves.x, moves.y); printScores(points, '\0'); dealloc(points); } int init(char *fileName) { // --- IO START --- FILE *inp = fopen(fileName, "r"); if (!inp) { pter "File not found" << nl; return 1; } fseek(inp, 0L, SEEK_END); const long sz = ftell(inp); fseek(inp, 0L, SEEK_SET); char *fileInpPtr = static_cast(alloc(sz + 1 * sizeof(char))); fileInpPtr[sz] = '\0'; for (int i = 0; i < sz; ++i) { fscanf(inp, "%c", &fileInpPtr[i]); } // --- IO END --- // --- ALGORITHM START --- Moves moves = serialize(fileInpPtr); char **board = allocAndInitBoard(moves.x, moves.y); solve(moves, board); // --- ALGORITHM END --- // --- MEMORY CLEANUP START --- dealloc(fileInpPtr); deallocBoard(board, moves.x); fclose(inp); if (!assert_alloc()) { pter "Memory leak detected" << nl; return 1; } // --- MEMORY CLEANUP END --- return 0; } int main(int argc, char **argv) { if (argc < 2) { pter "No input file provided" << nl; pter "Usage: dotsandboxes /path/to/input.txt" << nl; return 1; } try { return init(argv[1]); } catch (const invalid_argument &e) { pter e.what() << nl; return 1; } }