a geicko-2 based round robin ranking system designed to test c++ battleship submissions battleship.dunkirk.sh
at main 8.4 kB view raw
1#include "battleship.h" 2#include <sstream> 3#include <cctype> 4#include <vector> 5#include <mutex> 6 7// Global flag for debug output 8static bool g_debugEnabled = false; 9static bool g_guardTripped = false; 10static vector<string> g_debugLog; 11static mutex g_debugLogMutex; 12static const size_t MAX_DEBUG_LOG_SIZE = 1000; 13 14void setDebugMode(bool enabled) { 15 g_debugEnabled = enabled; 16} 17 18bool getGuardTripped() { 19 return g_guardTripped; 20} 21 22void resetGuardTripped() { 23 g_guardTripped = false; 24 lock_guard<mutex> lock(g_debugLogMutex); 25 g_debugLog.clear(); 26} 27 28vector<string> getDebugLog() { 29 lock_guard<mutex> lock(g_debugLogMutex); 30 return g_debugLog; 31} 32 33void welcome(bool debug) { 34 clearTheScreen(); 35 cout << "========================================" << endl; 36 cout << " BATTLESHIP - Lightweight" << endl; 37 cout << "========================================" << endl; 38 if (debug) { 39 cout << "Debug mode enabled" << endl; 40 } 41 cout << endl; 42} 43 44void clearTheScreen() { 45 // Simple cross-platform clear 46 cout << "\033[2J\033[1;1H"; 47} 48 49void pauseForEnter() { 50 cout << "Press Enter to continue..."; 51 cin.ignore(); 52 cin.get(); 53} 54 55void writeMessage(int x, int y, string message) { 56 cout << message << endl; 57} 58 59void writeResult(int x, int y, int result, int playerType) { 60 string player = (playerType == HUMAN) ? "Player" : "Computer"; 61 62 if (isASunk(result)) { 63 int shipNum = isShip(result); 64 char shipName; 65 switch(shipNum) { 66 case AC: shipName = 'A'; break; 67 case BS: shipName = 'B'; break; 68 case CR: shipName = 'C'; break; 69 case SB: shipName = 'S'; break; 70 case DS: shipName = 'D'; break; 71 default: shipName = '?'; break; 72 } 73 cout << player << " SUNK a ship (" << shipName << ")!" << endl; 74 } else if (isAHit(result)) { 75 cout << player << " HIT!" << endl; 76 } else { 77 cout << player << " MISS" << endl; 78 } 79} 80 81void displayBoard(int x, int y, int playerType, const Board &gameBoard) { 82 cout << " "; 83 for (int col = 0; col < BOARDSIZE; col++) { 84 cout << (col + 1) << " "; 85 } 86 cout << endl; 87 88 for (int row = 0; row < BOARDSIZE; row++) { 89 cout << char('A' + row) << " "; 90 for (int col = 0; col < BOARDSIZE; col++) { 91 char cell = gameBoard.grid[row][col]; 92 93 // Hide ships if showing to computer player 94 if (playerType == COMPUTER) { 95 if (cell != HIT_MARKER && cell != MISS_MARKER && cell != SUNK_MARKER) { 96 cell = EMPTY_MARKER; 97 } 98 } 99 100 cout << cell << " "; 101 } 102 cout << endl; 103 } 104 cout << endl; 105} 106 107bool placeShip(Board &gameBoard, int shipNum, int row, int col, int orient) { 108 Ship &ship = gameBoard.s[shipNum]; 109 110 // Check bounds 111 if (orient == HORZ) { 112 if (col + ship.size > BOARDSIZE) return false; 113 } else { 114 if (row + ship.size > BOARDSIZE) return false; 115 } 116 117 // Check for collisions 118 for (int i = 0; i < ship.size; i++) { 119 int r = (orient == VERT) ? row + i : row; 120 int c = (orient == HORZ) ? col + i : col; 121 if (gameBoard.grid[r][c] != EMPTY_MARKER) { 122 return false; 123 } 124 } 125 126 // Place the ship 127 ship.pos.startRow = row; 128 ship.pos.startCol = col; 129 ship.pos.orient = orient; 130 131 for (int i = 0; i < ship.size; i++) { 132 int r = (orient == VERT) ? row + i : row; 133 int c = (orient == HORZ) ? col + i : col; 134 gameBoard.grid[r][c] = ship.marker; 135 } 136 137 return true; 138} 139 140void initializeBoard(Board &gameBoard, bool file) { 141 // Initialize grid 142 for (int i = 0; i < BOARDSIZE; i++) { 143 for (int j = 0; j < BOARDSIZE; j++) { 144 gameBoard.grid[i][j] = EMPTY_MARKER; 145 } 146 } 147 148 // Initialize ships 149 gameBoard.s[AC].size = AC_SIZE; 150 gameBoard.s[AC].hitsToSink = AC_SIZE; 151 gameBoard.s[AC].marker = AC_MARKER; 152 153 gameBoard.s[BS].size = BS_SIZE; 154 gameBoard.s[BS].hitsToSink = BS_SIZE; 155 gameBoard.s[BS].marker = BS_MARKER; 156 157 gameBoard.s[CR].size = CR_SIZE; 158 gameBoard.s[CR].hitsToSink = CR_SIZE; 159 gameBoard.s[CR].marker = CR_MARKER; 160 161 gameBoard.s[SB].size = SB_SIZE; 162 gameBoard.s[SB].hitsToSink = SB_SIZE; 163 gameBoard.s[SB].marker = SB_MARKER; 164 165 gameBoard.s[DS].size = DS_SIZE; 166 gameBoard.s[DS].hitsToSink = DS_SIZE; 167 gameBoard.s[DS].marker = DS_MARKER; 168 169 // Place ships randomly 170 for (int shipNum = AC; shipNum <= DS; shipNum++) { 171 bool placed = false; 172 while (!placed) { 173 int row = rand() % BOARDSIZE; 174 int col = rand() % BOARDSIZE; 175 int orient = rand() % 2; 176 placed = placeShip(gameBoard, shipNum, row, col, orient); 177 } 178 } 179} 180 181int playMove(int row, int col, Board &gameBoard) { 182 char cell = gameBoard.grid[row][col]; 183 184 // Already hit 185 if (cell == HIT_MARKER || cell == MISS_MARKER || cell == SUNK_MARKER) { 186 return MISS; 187 } 188 189 // Miss 190 if (cell == EMPTY_MARKER) { 191 gameBoard.grid[row][col] = MISS_MARKER; 192 return MISS; 193 } 194 195 // Hit a ship 196 int shipNum = 0; 197 if (cell == AC_MARKER) shipNum = AC; 198 else if (cell == BS_MARKER) shipNum = BS; 199 else if (cell == CR_MARKER) shipNum = CR; 200 else if (cell == SB_MARKER) shipNum = SB; 201 else if (cell == DS_MARKER) shipNum = DS; 202 203 if (shipNum == 0) { 204 gameBoard.grid[row][col] = MISS_MARKER; 205 return MISS; 206 } 207 208 Ship &ship = gameBoard.s[shipNum]; 209 ship.hitsToSink--; 210 211 gameBoard.grid[row][col] = HIT_MARKER; 212 213 if (ship.hitsToSink == 0) { 214 // Mark all parts as sunk 215 for (int i = 0; i < ship.size; i++) { 216 int r = (ship.pos.orient == VERT) ? ship.pos.startRow + i : ship.pos.startRow; 217 int c = (ship.pos.orient == HORZ) ? ship.pos.startCol + i : ship.pos.startCol; 218 gameBoard.grid[r][c] = SUNK_MARKER; 219 } 220 return SUNK | shipNum; 221 } 222 223 return HIT | shipNum; 224} 225 226bool isAMiss(int playMoveResult) { 227 return !(playMoveResult & HIT); 228} 229 230bool isAHit(int playMoveResult) { 231 return (playMoveResult & HIT) != 0; 232} 233 234bool isASunk(int playMoveResult) { 235 return (playMoveResult & SUNK) != 0; 236} 237 238int isShip(int playMoveResult) { 239 return playMoveResult & SHIP; 240} 241 242string randomMove() { 243 int row = rand() % BOARDSIZE; 244 int col = rand() % BOARDSIZE; 245 246 char letter = 'A' + row; 247 return string(1, letter) + " " + to_string(col + 1); 248} 249 250int checkMove(string move, const Board &gameBoard, int &row, int &col) { 251 // Trim whitespace 252 move.erase(0, move.find_first_not_of(" \t\n\r")); 253 move.erase(move.find_last_not_of(" \t\n\r") + 1); 254 255 if (move.empty()) { 256 return ILLEGAL_FORMAT; 257 } 258 259 // Parse format: "A 5" or "A5" 260 char letter = toupper(move[0]); 261 if (letter < 'A' || letter > 'J') { 262 return ILLEGAL_FORMAT; 263 } 264 265 row = letter - 'A'; 266 267 // Extract number 268 string numStr = move.substr(1); 269 numStr.erase(0, numStr.find_first_not_of(" \t")); 270 271 if (numStr.empty()) { 272 return ILLEGAL_FORMAT; 273 } 274 275 try { 276 col = stoi(numStr) - 1; 277 } catch (...) { 278 return ILLEGAL_FORMAT; 279 } 280 281 if (col < 0 || col >= BOARDSIZE) { 282 return ILLEGAL_FORMAT; 283 } 284 285 // Check if already used 286 char cell = gameBoard.grid[row][col]; 287 if (cell == HIT_MARKER || cell == MISS_MARKER || cell == SUNK_MARKER) { 288 return REUSED_MOVE; 289 } 290 291 return VALID_MOVE; 292} 293 294void debug(string s, int x, int y) { 295 // Only accumulate logs if debug mode is enabled or we need to track guards 296 // This prevents memory bloat during benchmarks 297 if (g_debugEnabled || s.find("*** GUARD TRIPPED ***") != string::npos) { 298 lock_guard<mutex> lock(g_debugLogMutex); 299 g_debugLog.push_back(s); 300 301 // Limit log size to prevent unbounded growth 302 if (g_debugLog.size() > MAX_DEBUG_LOG_SIZE) { 303 g_debugLog.erase(g_debugLog.begin(), 304 g_debugLog.begin() + (g_debugLog.size() - MAX_DEBUG_LOG_SIZE)); 305 } 306 } 307 308 if (g_debugEnabled) { 309 cout << "[DEBUG] " << s << endl; 310 } 311} 312 313string numToString(int x) { 314 stringstream ss; 315 ss << x; 316 return ss.str(); 317}