board.c (5801B)
1 #include "hex_server.h" 2 3 extern inline s16 4 board_segment_abs2rel(struct board_segment *base, struct board_segment *absptr); 5 6 extern inline struct board_segment * 7 board_segment_rel2abs(struct board_segment *base, s16 relptr); 8 9 struct board_segment * 10 board_segment_root(struct board_segment *self) 11 { 12 assert(self); 13 14 struct board_segment *parent, *grandparent; 15 while ((parent = board_segment_rel2abs(self, self->parent_relptr))) { 16 grandparent = board_segment_rel2abs(parent, parent->parent_relptr); 17 if (!grandparent) return parent; 18 19 /* compress the path from self->parent->grandparent to 20 * self->grandparent for faster future traversal 21 */ 22 self->parent_relptr = board_segment_abs2rel(self, grandparent); 23 24 self = grandparent; 25 } 26 27 return self; 28 } 29 30 void 31 board_segment_merge(struct board_segment *restrict self, struct board_segment *restrict elem) 32 { 33 assert(self); 34 assert(elem); 35 36 struct board_segment *self_root = board_segment_root(self); 37 struct board_segment *elem_root = board_segment_root(elem); 38 39 if (self_root == elem_root) return; /* NOTE: already merged */ 40 41 if (self_root->rank < elem_root->rank) { 42 self_root->parent_relptr = board_segment_abs2rel(self_root, elem_root); 43 } else if (self_root->rank > elem_root->rank) { 44 elem_root->parent_relptr = board_segment_abs2rel(elem_root, self_root); 45 } else { 46 self_root->parent_relptr = board_segment_abs2rel(self_root, elem_root); 47 elem_root->rank++; 48 } 49 } 50 51 b32 52 board_segment_joined(struct board_segment *self, struct board_segment *elem) 53 { 54 return board_segment_root(self) == board_segment_root(elem); 55 } 56 57 #define NEIGHBOURS_COUNT 6 58 59 static s8 neighbour_dx[NEIGHBOURS_COUNT] = { -1, -1, 0, 0, +1, +1, }; 60 static s8 neighbour_dy[NEIGHBOURS_COUNT] = { 0, +1, -1, +1, -1, 0, }; 61 62 static inline struct board_segment * 63 board_get_segment(struct board_state *self, s32 x, s32 y) 64 { 65 assert(self); 66 67 if (-1 < x && x < (s32) self->size && -1 < y && y < (s32) self->size) 68 return &self->segments[y * self->size + x]; 69 70 return NULL; 71 } 72 73 static size_t 74 board_neighbours(struct board_state *self, s32 x, s32 y, struct board_segment *buf[static NEIGHBOURS_COUNT]) 75 { 76 assert(self); 77 assert(buf); 78 79 size_t count = 0; 80 81 for (size_t i = 0; i < NEIGHBOURS_COUNT; i++) { 82 s32 px = x + neighbour_dx[i], py = y + neighbour_dy[i]; 83 84 struct board_segment *seg; 85 if ((seg = board_get_segment(self, px, py))) 86 buf[count++] = seg; 87 } 88 89 return count; 90 } 91 92 struct board_state * 93 board_alloc(size_t size) 94 { 95 struct board_state *board; 96 size_t segments = size * size; 97 size_t sz = sizeof *board + segments * sizeof *board->segments; 98 99 if (!(board = malloc(sz))) return NULL; 100 101 memset(board, 0, sz); 102 103 board->size = size; 104 105 return board; 106 } 107 108 void 109 board_free(struct board_state *self) 110 { 111 assert(self); 112 113 free(self); 114 } 115 116 void 117 board_print(struct board_state *self) 118 { 119 assert(self); 120 121 for (size_t y = 0; y < self->size; y++) { 122 for (size_t k = 0; k < y; k++) 123 dbglog(" "); 124 125 struct board_segment *seg; 126 for (size_t x = 0; x < self->size; x++) { 127 seg = board_get_segment(self, x, y); 128 129 switch (seg->cell) { 130 case CELL_EMPTY: dbglog(". "); break; 131 case CELL_BLACK: dbglog("B "); break; 132 case CELL_WHITE: dbglog("W "); break; 133 } 134 } 135 136 dbglog("\n"); 137 } 138 } 139 140 b32 141 board_play(struct board_state *self, enum hex_player player, s32 x, s32 y) 142 { 143 assert(self); 144 145 struct board_segment *seg = board_get_segment(self, x, y); 146 if (!seg) { 147 dbglog("[server] Agent %u played invalid move: (%" PRIi32 ", %" PRIi32 "); out of bounds\n", 148 player, x, y); 149 150 return false; 151 } 152 153 if (seg->cell != CELL_EMPTY) { 154 char *cell_strs[] = { 155 [CELL_EMPTY] = "empty", [CELL_BLACK] = "black", [CELL_WHITE] = "white", 156 }; 157 158 dbglog("[server] Agent %u played invalid move: (%" PRIi32 ", %" PRIi32 "); previously occupied by %s\n", 159 player, x, y, cell_strs[seg->cell]); 160 161 return false; 162 } 163 164 switch (player) { 165 case HEX_PLAYER_BLACK: seg->cell = CELL_BLACK; break; 166 case HEX_PLAYER_WHITE: seg->cell = CELL_WHITE; break; 167 } 168 169 /* NOTE: handle case where player plays a cell connected to their 170 * relevant edged (source/sink) 171 */ 172 if (player == HEX_PLAYER_BLACK) { 173 if (x == 0) { 174 board_segment_merge(seg, &self->black_source); 175 } else if (x == (s32) self->size - 1) { 176 board_segment_merge(seg, &self->black_sink); 177 } 178 } else if (player == HEX_PLAYER_WHITE) { 179 if (y == 0) { 180 board_segment_merge(seg, &self->white_source); 181 } else if (y == (s32) self->size - 1) { 182 board_segment_merge(seg, &self->white_sink); 183 } 184 } 185 186 /* NOTE: merge the played cell with all of its neighbours played by 187 * the same player 188 */ 189 struct board_segment *neighbours[NEIGHBOURS_COUNT]; 190 size_t neighbours_count = board_neighbours(self, x, y, neighbours); 191 192 for (size_t i = 0; i < neighbours_count; i++) { 193 if (neighbours[i]->cell == seg->cell) 194 board_segment_merge(seg, neighbours[i]); 195 } 196 197 return true; 198 } 199 200 void 201 board_swap(struct board_state *self) 202 { 203 assert(self); 204 205 for (size_t i = 0; i < self->size * self->size; i++) { 206 struct board_segment *seg = &self->segments[i]; 207 208 s32 x = i % self->size; 209 s32 y = i / self->size; 210 211 switch (seg->cell) { 212 case CELL_EMPTY: 213 break; /* skip empty cells */ 214 215 default: { 216 u8 old_cell_value = seg->cell; 217 218 seg->cell = CELL_EMPTY; 219 seg->parent_relptr = RELPTR_NULL; 220 seg->rank = 0; 221 222 switch (old_cell_value) { 223 case CELL_BLACK: board_play(self, HEX_PLAYER_WHITE, x, y); break; 224 case CELL_WHITE: board_play(self, HEX_PLAYER_BLACK, x, y); break; 225 } 226 } break; 227 } 228 } 229 } 230 231 b32 232 board_completed(struct board_state *self, enum hex_player *winner) 233 { 234 assert(self); 235 assert(winner); 236 237 if (board_segment_joined(&self->black_source, &self->black_sink)) { 238 *winner = HEX_PLAYER_BLACK; 239 return true; 240 } 241 242 if (board_segment_joined(&self->white_source, &self->white_sink)) { 243 *winner = HEX_PLAYER_WHITE; 244 return true; 245 } 246 247 return false; 248 }