-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgeesespotter.cpp
More file actions
198 lines (178 loc) · 5.7 KB
/
geesespotter.cpp
File metadata and controls
198 lines (178 loc) · 5.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
#include "geesespotter_lib.h"
#include <iostream>
#include <stdexcept>
// ANSI Color Codes
const std::string RESET = "\033[0m";
const std::string RED = "\033[31m";
const std::string GREEN = "\033[32m";
const std::string LIGHT_BLUE = "\033[36m";
// ASCII Characters for Tiles
const char UNREVEALED_TILE = '#';
const char EMPTY_TILE = '.';
const char MARKED_TILE = 'M';
const char GEESE_TILE = 'G';
// Create the board and initialize it
char *create_board(std::size_t x_dim, std::size_t y_dim) {
if (x_dim == 0 || y_dim == 0) {
throw std::invalid_argument("Board dimensions must be greater than 0");
}
char *p_board = new (std::nothrow) char[x_dim * y_dim]{};
if (p_board == nullptr) {
throw std::bad_alloc();
}
for (std::size_t i = 0; i < x_dim * y_dim; ++i) {
p_board[i] = '\0';
}
return p_board;
}
// Safely deallocate the given board
void clean_board(char *&board) {
delete[] board;
board = nullptr;
}
// Prints out the board
void print_board(const char *board, std::size_t x_dim, std::size_t y_dim) {
if (board == nullptr) {
throw std::invalid_argument("Board pointer is null");
}
// Print column headers
std::cout << std::endl;
std::cout << " ";
for (std::size_t x = 0; x < x_dim; ++x) {
std::cout << x << " ";
}
std::cout << std::endl;
for (std::size_t y = 0; y < y_dim; ++y) {
// Print row header
std::cout << y << " ";
for (std::size_t x = 0; x < x_dim; ++x) {
std::size_t i = y * x_dim + x;
char cell = board[i];
if (cell & hidden_mask()) {
std::cout << ((cell & marked_mask()) ? LIGHT_BLUE + MARKED_TILE + RESET : std::string(1, UNREVEALED_TILE)) << " ";
} else {
int value = cell & value_mask();
std::string color = (value == 9) ? RED : GREEN;
std::cout << color << (value ? std::to_string(value) : std::string(1, EMPTY_TILE)) << RESET << " ";
}
}
std::cout << std::endl;
}
std::cout << std::endl;
}
// Hide all the field values
void hide_board(char *board, std::size_t x_dim, std::size_t y_dim) {
if (board == nullptr) {
throw std::invalid_argument("Board pointer is null");
}
for (std::size_t i = 0; i < x_dim * y_dim; ++i) {
board[i] |= hidden_mask();
}
}
// Player attempts to mark a field
int mark(char *board, std::size_t x_dim, std::size_t y_dim, std::size_t x_loc, std::size_t y_loc) {
std::size_t i{y_loc * x_dim + x_loc}; // index
if (!(board[i] & 0x20)) {
// if revealed
return 2;
} else {
// if hidden, toggle the marked bit
board[i] ^= 0x10;
return 0;
}
};
// Updates the board array: all fields without a goose have their value set to the number of adjacent geese
void compute_neighbours(char *board, std::size_t x_dim, std::size_t y_dim) {
for (std::size_t y = 0; y < y_dim; ++y) {
for (std::size_t x = 0; x < x_dim; ++x) {
std::size_t i = y * x_dim + x; // index
if (board[i] == 9) {
// No need to compute neighbours for a goose
continue;
}
int neighbours = 0;
// Check left
if (x > 0 && board[i - 1] == 9) {
neighbours++;
}
// Check right
if (x < x_dim - 1 && board[i + 1] == 9) {
neighbours++;
}
// Check up
if (y > 0 && board[i - x_dim] == 9) {
neighbours++;
}
// Check down
if (y < y_dim - 1 && board[i + x_dim] == 9) {
neighbours++;
}
// Check top-left
if (x > 0 && y > 0 && board[i - x_dim - 1] == 9) {
neighbours++;
}
// Check top-right
if (x < x_dim - 1 && y > 0 && board[i - x_dim + 1] == 9) {
neighbours++;
}
// Check bottom-left
if (x > 0 && y < y_dim - 1 && board[i + x_dim - 1] == 9) {
neighbours++;
}
// Check bottom-right
if (x < x_dim - 1 && y < y_dim - 1 && board[i + x_dim + 1] == 9) {
neighbours++;
}
// Assign the computed number of neighbours
board[i] = neighbours;
}
}
}
// The game is won when all fields that do not have a goose have been revealed
bool is_game_won(char *board, std::size_t x_dim, std::size_t y_dim) {
for (std::size_t i{}; i < x_dim * y_dim; ++i) {
if ((board[i] & 0xf) == 9) {
// if the field has a goose
continue;
}
if (board[i] & 0x20) {
// if a non-goose tile is hidden, the game cannot be won
return false;
}
}
// if all non-goose tiles are revealed, the game is won
return true;
};
int reveal(char *board, std::size_t x_dim, std::size_t y_dim, std::size_t x_loc, std::size_t y_loc) {
std::size_t i{y_loc * x_dim + x_loc};
if ((board[i] & 0x20) && !(board[i] & 0x10)) { // if hidden and not marked
// reveal field
board[i] ^= 0x20;
if (board[i] == 9) {
return 9; // goose found
} else if (board[i] == 0) {
// Recursively reveal adjacent fields
if (x_loc > 0)
reveal(board, x_dim, y_dim, x_loc - 1, y_loc); // left
if (x_loc < x_dim - 1)
reveal(board, x_dim, y_dim, x_loc + 1, y_loc); // right
if (y_loc > 0)
reveal(board, x_dim, y_dim, x_loc, y_loc - 1); // up
if (y_loc < y_dim - 1)
reveal(board, x_dim, y_dim, x_loc, y_loc + 1); // down
if (x_loc > 0 && y_loc > 0)
reveal(board, x_dim, y_dim, x_loc - 1, y_loc - 1); // top-left
if (x_loc < x_dim - 1 && y_loc > 0)
reveal(board, x_dim, y_dim, x_loc + 1, y_loc - 1); // top-right
if (x_loc > 0 && y_loc < y_dim - 1)
reveal(board, x_dim, y_dim, x_loc - 1, y_loc + 1); // bottom-left
if (x_loc < x_dim - 1 && y_loc < y_dim - 1)
reveal(board, x_dim, y_dim, x_loc + 1, y_loc + 1); // bottom-right
}
} else if (board[i] & 0x10) { // if marked
return 1;
} else if (!(board[i] & 0x20)) { // if revealed
return 2;
}
return 0;
}