hex

hex.git
git clone git://git.lenczewski.org/hex.git
Log | Files | Refs

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 }