cmpsc330hw2/main.cpp

539 lines
13 KiB
C++
Raw Normal View History

#include <algorithm>
2024-10-09 23:39:24 +00:00
#include <iostream>
#include <sstream>
2024-10-10 22:37:13 +00:00
#include <cstdio>
2024-10-09 23:39:24 +00:00
#define pt cout <<
2024-10-10 15:51:38 +00:00
#define pter cerr <<
2024-10-10 16:13:46 +00:00
#define nl "\n"
2024-10-09 23:39:24 +00:00
using namespace std;
2024-10-11 16:01:47 +00:00
// TODO: drop pair
// remove global def of std
2024-10-11 16:02:18 +00:00
// spam comments
2024-10-11 16:01:47 +00:00
2024-10-09 23:39:24 +00:00
// -------- START RAII HELPERS--------
unsigned long allocatedMem = 0;
2024-10-09 23:39:24 +00:00
void *alloc(size_t size) {
void *ptr = malloc(size);
2024-10-10 22:37:13 +00:00
if (!ptr) {
2024-10-10 15:51:38 +00:00
pter "Memory allocation failed" << nl;
2024-10-10 22:37:13 +00:00
return NULL;
2024-10-09 23:39:24 +00:00
}
allocatedMem += 1;
2024-10-09 23:39:24 +00:00
return ptr;
}
void dealloc(void *ptr) {
if (ptr) free(ptr);
allocatedMem -= 1;
2024-10-09 23:39:24 +00:00
}
bool assert_alloc() {
return !allocatedMem;
2024-10-09 23:39:24 +00:00
}
// -------- END RAII HELPERS--------
2024-10-10 22:37:13 +00:00
// -------- START SIMPLE VEC IMPLEMENTATION --------
template<typename T>
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<T *>(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;
}
2024-10-11 16:01:47 +00:00
myVec(size_t size) {
capacity = size;
length = 0;
data = static_cast<T *>(malloc(size * sizeof(T)));
}
2024-10-10 22:37:13 +00:00
void push_back(const T &val) {
resize();
data[length++] = val;
}
void pop_back() {
if (length) {
length--;
}
}
T &operator[](size_t index) {
return data[index];
}
2024-10-10 22:52:28 +00:00
2024-10-10 22:37:13 +00:00
int size() {
return length;
}
};
// -------- END SIMPLE VEC IMPLEMENTATION --------
2024-10-09 23:39:24 +00:00
struct Move {
char player;
2024-10-10 00:36:29 +00:00
int moveX;
int moveY;
2024-10-09 23:39:24 +00:00
};
struct Moves {
2024-10-10 00:36:29 +00:00
int x;
int y;
2024-10-10 22:37:13 +00:00
myVec<Move> moves;
2024-10-09 23:39:24 +00:00
};
bool isValidPlayer(char c) {
return c >= 'A' && c <= 'Z' && c != 'X';
}
2024-10-11 00:04:26 +00:00
void swapChar(char &a, char &b) {
2024-10-11 00:06:06 +00:00
if (a == b) {
return;
}
a ^= b;
b ^= a;
a ^= b;
2024-10-11 00:04:26 +00:00
}
// Recursively reverse a string
void reverseString(string &str, int start, int end) {
if (start >= end) {
return;
}
swapChar(str[start], str[end]);
2024-10-11 00:06:06 +00:00
2024-10-11 00:04:26 +00:00
reverseString(str, start + 1, end - 1);
}
2024-10-10 22:59:00 +00:00
string toString(int x) {
2024-10-10 23:36:22 +00:00
if (!x) return "0";
2024-10-10 22:59:00 +00:00
string result;
while (x) {
result.push_back(x % 10 + '0');
x /= 10;
}
2024-10-11 00:04:26 +00:00
reverseString(result, 0, result.size() - 1);
2024-10-10 22:59:00 +00:00
return result;
}
2024-10-10 15:51:38 +00:00
2024-10-10 22:59:00 +00:00
Moves serialize(const string &s) {
2024-10-09 23:39:24 +00:00
Moves result;
istringstream iss(s);
iss >> result.x >> result.y;
2024-10-10 22:59:00 +00:00
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);
}
2024-10-10 23:26:14 +00:00
result.x = (result.x << 1) - 1;
result.y = (result.y << 1) - 1;
2024-10-09 23:39:24 +00:00
string player;
int x, y;
while (iss >> player >> x >> y) {
if (player == "END") break;
2024-10-10 23:27:03 +00:00
// TODO: move this exceptions to runtime checks
2024-10-10 23:26:14 +00:00
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;
2024-10-10 02:00:20 +00:00
throw invalid_argument(err);
}
2024-10-10 22:16:59 +00:00
Move move;
move.player = player[0];
move.moveX = x;
move.moveY = y;
result.moves.push_back(move);
2024-10-09 23:39:24 +00:00
}
return result;
}
2024-10-10 15:51:38 +00:00
char **allocBoard(int x, int y) {
char **board = static_cast<char **>(alloc(x * sizeof(char *)));
for (int i = 0; i < x; ++i) {
board[i] = static_cast<char *>(alloc(y * sizeof(char)));
}
return board;
}
2024-10-10 00:36:29 +00:00
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) ? ' ' : '.';
2024-10-10 00:36:29 +00:00
}
}
2024-10-09 23:39:24 +00:00
}
2024-10-10 00:36:29 +00:00
char **allocAndInitBoard(int x, int y) {
2024-10-10 15:51:38 +00:00
char **board = allocBoard(x, y);
2024-10-10 00:36:29 +00:00
initBoard(board, x, y);
return board;
}
void deallocBoard(char **board, int x) {
for (int i = 0; i < x; ++i) {
dealloc(board[i]);
2024-10-09 23:39:24 +00:00
}
2024-10-10 00:36:29 +00:00
dealloc(board);
}
void printBoard(char **board, int x, int y) {
2024-10-11 16:01:47 +00:00
int boardX = x + 2;
int boardY = y + 2;
2024-10-11 17:31:02 +00:00
myVec<myVec<string> > ans(boardX);
2024-10-11 16:01:47 +00:00
for (int i = 0; i < boardX; ++i) {
2024-10-11 17:31:02 +00:00
ans[i] = myVec<string>(boardY);
2024-10-11 16:01:47 +00:00
for (int j = 0; j < boardY; ++j) {
2024-10-11 17:31:02 +00:00
ans[i][j] = " ";
2024-10-11 16:01:47 +00:00
}
}
for (int i = 0; i < y; ++i) {
if (!(i % 10)) {
2024-10-11 17:31:02 +00:00
ans[0][i] = toString((i / 10));
2024-10-11 16:01:47 +00:00
}
2024-10-11 17:31:02 +00:00
ans[1][i] = toString((i % 10));
2024-10-11 16:01:47 +00:00
}
for (int i = 0; i < x; ++i) {
if (!(i % 10)) {
2024-10-11 17:31:02 +00:00
ans[i + 2][0] = toString((i / 10));
2024-10-11 16:01:47 +00:00
}
2024-10-11 17:31:02 +00:00
ans[i + 2][1] = toString((i % 10));
2024-10-11 16:01:47 +00:00
}
2024-10-10 00:36:29 +00:00
for (int i = 0; i < x; ++i) {
for (int j = 0; j < y; ++j) {
2024-10-11 17:31:02 +00:00
ans[i + 2][j + 2] = string(1, board[i][j]);
2024-10-11 16:01:47 +00:00
}
}
// Calculate the maximum length of the row numbers plus two spaces
int maxLength = 0;
for (int i = 0; i < x; ++i) {
int length = 2 + (i >= 10 ? 2 : 1);
2024-10-11 17:31:02 +00:00
for(int j = 0; j < y; ++j) {
2024-10-11 17:32:32 +00:00
length = max(length, (int)ans[i + 2][j + 2].size());
2024-10-11 17:31:02 +00:00
}
maxLength = max(maxLength, length);
2024-10-11 16:01:47 +00:00
}
// Print the first two rows with the calculated spaces
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < maxLength; ++j) {
pt ' ';
}
for (int j = 0; j < y; ++j) {
pt ans[i][j];
2024-10-10 00:36:29 +00:00
}
pt nl;
}
2024-10-11 16:01:47 +00:00
// Print the rest of the board
for (int i = 2; i < boardX; ++i) {
for (int j = 0; j < boardY; ++j) {
2024-10-11 17:31:02 +00:00
if (j==2) {
2024-10-11 16:01:47 +00:00
pt ' ';
}
pt ans[i][j];
2024-10-11 17:35:24 +00:00
if (!j) {
pt ' ';
}
2024-10-11 16:01:47 +00:00
}
2024-10-11 17:31:02 +00:00
pt nl;
2024-10-11 16:01:47 +00:00
}
2024-10-10 23:36:22 +00:00
pt endl;
2024-10-10 00:36:29 +00:00
}
2024-10-09 23:39:24 +00:00
char toLowerCase(char c) {
return c | 32;
}
2024-10-10 16:13:46 +00:00
int normalize(char c) {
return toLowerCase(c) - 'a';
}
2024-10-10 23:55:31 +00:00
void swap(pair<char, pair<int, bool> > *a, int i, int j) {
pair<char, pair<int, bool> > temp = a[i];
2024-10-10 16:13:46 +00:00
a[i] = a[j];
a[j] = temp;
}
2024-10-10 23:55:31 +00:00
void bubbleSort(pair<char, pair<int, bool> > *a) {
2024-10-10 16:13:46 +00:00
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 25 - i; j++) {
2024-10-10 23:55:31 +00:00
if (a[j].second.first > a[j + 1].second.first)
2024-10-10 16:13:46 +00:00
swap(a, j, j + 1);
}
}
}
2024-10-10 16:13:46 +00:00
2024-10-10 22:52:28 +00:00
void printScores(int *points, char blacklist) {
2024-10-10 23:55:31 +00:00
pair<char, pair<int, bool> > *arr = static_cast<pair<char, pair<int, bool> > *>(alloc(
2024-10-11 16:01:47 +00:00
26 * sizeof(pair<char, pair<int, bool> >)));
2024-10-10 23:55:31 +00:00
2024-10-10 16:13:46 +00:00
for (int i = 0; i < 26; i++) {
2024-10-10 23:55:31 +00:00
pair<char, pair<int, bool> > p;
pair<int, bool> p1;
p1.first = points[i];
p1.second = false;
2024-10-10 23:20:42 +00:00
p.first = static_cast<char>(i + 'A');
2024-10-10 23:55:31 +00:00
p.second = p1;
2024-10-10 22:16:59 +00:00
arr[i] = p;
2024-10-10 16:13:46 +00:00
}
2024-10-10 23:55:31 +00:00
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;
}
}
}
2024-10-10 16:13:46 +00:00
2024-10-10 23:55:31 +00:00
bubbleSort(arr);
2024-10-10 23:20:42 +00:00
2024-10-11 16:01:47 +00:00
for (int i = 25; i > -1; i--) {
2024-10-10 23:55:31 +00:00
if (arr[i].second.first != -1) {
2024-10-11 16:38:35 +00:00
pt "Player " << arr[i].first << " has " << arr[i].second.first << " boxes" << (
2024-10-11 16:01:47 +00:00
(i == 25 || arr[i].second.second) && arr[i].first != blacklist
2024-10-11 00:04:33 +00:00
? arr[i].second.second
2024-10-11 16:01:47 +00:00
? " (tie)\n"
: " (win)\n"
2024-10-11 00:04:33 +00:00
: nl);
2024-10-10 16:13:46 +00:00
}
}
dealloc(arr);
2024-10-10 23:36:22 +00:00
pt endl;
2024-10-10 16:13:46 +00:00
}
2024-10-10 22:41:57 +00:00
bool isValidMove(int x, int y, char **board) {
return (x & 1) != (y & 1) && board[x][y] == ' ';
}
2024-10-10 15:51:38 +00:00
void addPoint(
2024-10-11 16:01:47 +00:00
int *points,
char **board,
int x,
int y,
char player
2024-10-10 15:51:38 +00:00
) {
if (board[x][y] == ' ') {
2024-10-10 16:13:46 +00:00
if (points[normalize(player)] == -1) {
points[normalize(player)] = 1;
} else {
points[normalize(player)]++;
}
2024-10-10 15:51:38 +00:00
board[x][y] = player;
}
}
void solveVertical(
2024-10-11 16:01:47 +00:00
int moveX,
int moveY,
char **board,
int *points,
int x,
int y,
char player
2024-10-10 15:51:38 +00:00
) {
// 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
2024-10-11 17:39:49 +00:00
if (moveX < x - 2 && board[moveX + 2][moveY] != ' ' && board[moveX + 1][moveY - 1] != ' ') {
2024-10-10 15:51:38 +00:00
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(
2024-10-11 16:01:47 +00:00
int moveX,
int moveY,
char **board,
int *points,
int y,
char player
2024-10-10 15:51:38 +00:00
) {
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] !=
2024-10-11 16:01:47 +00:00
' ')) {
2024-10-10 15:51:38 +00:00
addPoint(points, board, moveX, moveY + 1, player);
}
}
}
2024-10-10 00:36:29 +00:00
void solve(Moves moves, char **board) {
2024-10-10 16:13:46 +00:00
int *points = static_cast<int *>(alloc(26 * (sizeof(int))));
for (int i = 0; i < 26; i++) {
points[i] = -1;
}
2024-10-10 22:16:59 +00:00
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;
2024-10-10 23:20:42 +00:00
points[normalize(player)] = max(0, points[normalize(player)]);
2024-10-10 22:41:57 +00:00
if (!isValidMove(moveX, moveY, board)) {
2024-10-10 23:36:22 +00:00
pt "Invalid or Repeated move by: " << player << " at (x, y): (" << moveX << ", " << moveY << ")" << nl;
board[moveX][moveY] = 'X';
printBoard(board, moves.x, moves.y);
2024-10-10 22:52:28 +00:00
printScores(points, player);
pt "Exiting.." << nl;
return;
}
board[moveX][moveY] = toLowerCase(player);
2024-10-10 22:52:28 +00:00
if (!(moveX & 1) && (moveY & 1)) {
2024-10-10 15:51:38 +00:00
solveVertical(moveX, moveY, board, points, moves.x, moves.y, player);
2024-10-10 22:52:28 +00:00
} else if ((moveX & 1) && !(moveY & 1)) {
2024-10-10 15:51:38 +00:00
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);
2024-10-10 00:36:29 +00:00
2024-10-10 22:52:28 +00:00
printScores(points, player);
2024-10-10 15:51:38 +00:00
pt "Exiting.." << nl;
return;
}
}
printBoard(board, moves.x, moves.y);
2024-10-10 22:52:28 +00:00
printScores(points, '\0');
2024-10-10 16:13:46 +00:00
dealloc(points);
2024-10-10 00:36:29 +00:00
}
int init(char *fileName) {
// --- IO START ---
2024-10-09 23:39:24 +00:00
FILE *inp = fopen(fileName, "r");
2024-10-10 22:37:13 +00:00
if (!inp) {
2024-10-10 15:51:38 +00:00
pter "File not found" << nl;
2024-10-09 23:39:24 +00:00
return 1;
}
fseek(inp, 0L, SEEK_END);
2024-10-10 00:36:29 +00:00
const long sz = ftell(inp);
2024-10-09 23:39:24 +00:00
fseek(inp, 0L, SEEK_SET);
char *fileInpPtr = static_cast<char *>(alloc(sz + 1 * sizeof(char)));
fileInpPtr[sz] = '\0';
for (int i = 0; i < sz; ++i) {
fscanf(inp, "%c", &fileInpPtr[i]);
}
2024-10-10 00:36:29 +00:00
// --- IO END ---
2024-10-09 23:39:24 +00:00
2024-10-10 00:36:29 +00:00
// --- ALGORITHM START ---
Moves moves = serialize(fileInpPtr);
char **board = allocAndInitBoard(moves.x, moves.y);
solve(moves, board);
// --- ALGORITHM END ---
2024-10-09 23:39:24 +00:00
2024-10-10 23:56:52 +00:00
// --- MEMORY CLEANUP START ---
2024-10-09 23:39:24 +00:00
dealloc(fileInpPtr);
2024-10-10 00:36:29 +00:00
deallocBoard(board, moves.x);
2024-10-09 23:39:24 +00:00
fclose(inp);
if (!assert_alloc()) {
2024-10-10 15:51:38 +00:00
pter "Memory leak detected" << nl;
2024-10-09 23:39:24 +00:00
return 1;
}
2024-10-10 23:56:52 +00:00
// --- MEMORY CLEANUP END ---
2024-10-09 23:39:24 +00:00
return 0;
}
2024-10-10 00:36:29 +00:00
int main(int argc, char **argv) {
if (argc < 2) {
2024-10-10 15:51:38 +00:00
pter "No input file provided" << nl;
2024-10-10 22:16:59 +00:00
pter "Usage: dotsandboxes /path/to/input.txt" << nl;
return 1;
}
try {
return init(argv[1]);
2024-10-10 02:00:20 +00:00
} catch (const invalid_argument &e) {
2024-10-10 15:51:38 +00:00
pter e.what() << nl;
2024-10-10 00:36:29 +00:00
return 1;
}
}