a geicko-2 based round robin ranking system designed to test c++ battleship submissions
battleship.dunkirk.sh
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}