diff --git a/src/book.c b/src/book.c index 5dd5bce..81ad21a 100644 --- a/src/book.c +++ b/src/book.c @@ -10,7 +10,7 @@ const struct Action* opening_move(const struct State* state) if (state->piece_count[P1] == 0 || state->piece_count[P2] == 0) { for (int i = 0; i < state->action_count; i++) { - switch (state->actions[i].from.r) { + switch (state->pieces[state->turn][state->actions[i].from.r].type) { case BEETLE: case GRASSHOPPER: case SPIDER: diff --git a/src/coords.c b/src/coords.c index a02bfeb..ffbed5c 100644 --- a/src/coords.c +++ b/src/coords.c @@ -105,6 +105,9 @@ bool Coords_map_adjacent(const struct Coords* coords, const struct Coords* other bool Coords_adjacent(const struct Coords* coords, const struct Coords* other) { + if (coords->q == IN_HAND || other->q == IN_HAND) { + return false; + } return Coords_map_adjacent(coords, other); } diff --git a/src/simulate.c b/src/simulate.c index f3fe360..706ae22 100644 --- a/src/simulate.c +++ b/src/simulate.c @@ -13,10 +13,10 @@ int State_beetle_seek_path( const struct Piece* piece, struct Coords* path) { - if (!state->queens[!piece->player]) { + if (state->pieces[!piece->player][QUEEN_INDEX].coords.q == IN_HAND) { return -1; } - struct Coords target = state->queens[!piece->player]->coords; + struct Coords target = state->pieces[!piece->player][QUEEN_INDEX].coords; struct Coords queue[MAX_PIECES]; unsigned int queue_size = 0; @@ -165,7 +165,8 @@ float State_simulate(struct State* state, if (state->beetle_move_count && (rand() / (float)RAND_MAX) < options->beetle_seek_move_bias) { - struct Piece* beetle = state->beetles[state->turn][rand() % state->beetle_count[state->turn]]; + + struct Piece* beetle = &state->pieces[state->turn][BEETLE_INDEX + rand() % (NUM_BEETLES - state->hands[state->turn][BEETLE])]; struct Coords path[MAX_PIECES]; int path_size = State_beetle_seek_path(state, beetle, path); @@ -242,8 +243,7 @@ float State_simulate(struct State* state, if (action->from.q != PLACE_ACTION && action->from.q != PASS_ACTION - && state->queens[!state->turn] - && Coords_adjacent(&action->from, &state->queens[!state->turn]->coords) + && Coords_adjacent(&action->from, &state->pieces[!state->turn][QUEEN_INDEX].coords) && (rand() / (float)RAND_MAX) < options->from_queen_pass) { #ifdef WATCH_SIMS printf("from queen pass\n"); diff --git a/src/state.c b/src/state.c index bf9e2ff..753b3b5 100644 --- a/src/state.c +++ b/src/state.c @@ -10,6 +10,30 @@ #include "errorcodes.h" #endif +const uint_fast8_t TYPE_INDEX[NUM_PIECETYPES] = { + ANT_INDEX, + BEETLE_INDEX, + GRASSHOPPER_INDEX, + SPIDER_INDEX, + QUEEN_INDEX +}; + +const uint_fast8_t TYPE_END[NUM_PIECETYPES] = { + ANT_INDEX + NUM_ANTS, + BEETLE_INDEX + NUM_BEETLES, + GRASSHOPPER_INDEX + NUM_GRASSHOPPERS, + SPIDER_INDEX + NUM_SPIDERS, + QUEEN_INDEX + NUM_QUEEN_BEES +}; + +const uint_fast8_t TYPE_COUNT[NUM_PIECETYPES] = { + NUM_ANTS, + NUM_BEETLES, + NUM_GRASSHOPPERS, + NUM_SPIDERS, + NUM_QUEEN_BEES +}; + bool State_cut_point_neighbor( const struct State* state, const struct Coords* coords) @@ -59,7 +83,7 @@ int State_height_at(const struct State* state, const struct Coords* coords) void State_derive_piece_players(struct State* state) { for (int p = 0; p < NUM_PLAYERS; p++) { - for (int i = 0; i < state->piece_count[p]; i++) { + for (int i = 0; i < PLAYER_PIECES; i++) { state->pieces[p][i].player = p; } } @@ -70,8 +94,11 @@ void State_derive_grid(struct State* state) memset(state->grid, 0, sizeof(struct Piece*) * GRID_SIZE * GRID_SIZE); for (int p = 0; p < NUM_PLAYERS; p++) { - for (int i = 0; i < state->piece_count[p]; i++) { + for (int i = 0; i < PLAYER_PIECES; i++) { struct Piece* piece = &state->pieces[p][i]; + if (piece->coords.q == IN_HAND) { + continue; + } // Check to see if this piece is under another piece // that's already on the grid @@ -97,8 +124,8 @@ void State_add_action(struct State* state, int piecei, action->to = *to; struct Piece* piece = &state->pieces[state->turn][piecei]; - struct Piece* turn_queen = state->queens[state->turn]; - struct Piece* other_queen = state->queens[!state->turn]; + struct Piece* turn_queen = &state->pieces[state->turn][QUEEN_INDEX]; + struct Piece* other_queen = &state->pieces[!state->turn][QUEEN_INDEX]; bool draw_action = false; @@ -174,8 +201,8 @@ void State_add_action(struct State* state, int piecei, && state->neighbor_count[state->turn][action->to.q][action->to.r] == 0 && !State_cut_point_neighbor(state, &action->to) // Don't bias moving an already-pinning piece - && !(state->neighbor_count[!state->turn][action->from.q][action->from.r] == 1 - && state->neighbor_count[state->turn][action->from.q][action->from.r] == 0)) { + && !(state->neighbor_count[!state->turn][from->q][from->r] == 1 + && state->neighbor_count[state->turn][from->q][from->r] == 0)) { state->pin_moves[state->pin_move_count++] = action; @@ -184,8 +211,8 @@ void State_add_action(struct State* state, int piecei, } } - if (state->neighbor_count[state->turn][action->from.q][action->from.r] == 1 - && state->neighbor_count[!state->turn][action->from.q][action->from.r] == 0) { + if (state->neighbor_count[state->turn][from->q][from->r] == 1 + && state->neighbor_count[!state->turn][from->q][from->r] == 0) { // We can't use this normal test, because it will always be a cut point //&& !State_cut_point_neighbor(state, &action->from)) { // Don't move to a new location that pins us @@ -197,14 +224,13 @@ void State_add_action(struct State* state, int piecei, state->unpin_moves[state->unpin_move_count++] = action; } - if (state->queens[state->turn] - && action->from.q != PLACE_ACTION - && action->from.q != PASS_ACTION - && State_hex_neighbor_count(state, &state->queens[state->turn]->coords) == NUM_DIRECTIONS - 1 - && Coords_adjacent(&state->queens[state->turn]->coords, &action->from) + if (from->q != PLACE_ACTION + && piecei != PASS_ACTION + && State_hex_neighbor_count(state, &state->pieces[state->turn][QUEEN_INDEX].coords) == NUM_DIRECTIONS - 1 + && Coords_adjacent(&state->pieces[state->turn][QUEEN_INDEX].coords, from) // TODO I think this is segfaulting; shouldn't any action from have a from on the grid (if not one of the above)? //&& !state->grid[action->from.q][action->from.r]->on_top - && (!Coords_adjacent(&state->queens[state->turn]->coords, &action->to) + && (!Coords_adjacent(&state->pieces[state->turn][QUEEN_INDEX].coords, &action->to) || state->grid[action->to.q][action->to.r])) { state->queen_away_moves[state->queen_away_move_count++] = action; @@ -341,13 +367,21 @@ void State_derive_cut_points(struct State* state) memset(state->cut_points, 0, sizeof(bool) * GRID_SIZE * GRID_SIZE); memset(state->cut_point_count, 0, sizeof(uint_fast8_t) * NUM_PLAYERS); - if (state->piece_count[P1] <= 1 && state->piece_count[P2] <= 1) { + struct Piece *start_piece = NULL; + for (int i = 0; i < PLAYER_PIECES; i++) { + if (state->pieces[P1][i].coords.q != IN_HAND) { + start_piece = &state->pieces[P1][i]; + break; + } + } + + if (!start_piece) { return; } struct Coords stack[MAX_PIECES]; int_fast8_t sp = 0; - stack[sp] = state->pieces[P1][0].coords; + stack[sp] = start_piece->coords; uint_fast8_t depth[MAX_PIECES]; depth[sp] = 0; @@ -432,116 +466,6 @@ void State_derive_cut_points(struct State* state) } } -// Counts the number of cut points each player has, without modifying the state -void State_count_cut_points( - const struct State* state, - const struct Coords* start_coords, - unsigned int cut_points[NUM_PLAYERS]) -{ - memset(cut_points, 0, sizeof(int) * NUM_PLAYERS); - - if (state->piece_count[P1] <= 1 && state->piece_count[P2] <= 1) { - return; - } - - struct Coords stack[MAX_PIECES]; - int_fast8_t sp = 0; - stack[sp] = *start_coords; - - uint_fast8_t depth[MAX_PIECES]; - depth[sp] = 0; - - uint_fast8_t lowpoint[MAX_PIECES]; - lowpoint[sp] = 0; - - // crumbs[q][r] is the same as depth[sp] for a particular Coords - int_fast8_t crumbs[GRID_SIZE][GRID_SIZE]; - memset(&crumbs, -1, sizeof(int_fast8_t) * GRID_SIZE * GRID_SIZE); - crumbs[stack[sp].q][stack[sp].r] = 0; - - uint_fast8_t next_direction[MAX_PIECES]; - next_direction[sp] = 0; - - uint_fast8_t parent_direction[MAX_PIECES]; - parent_direction[sp] = -1; - - // If the root (i.e. sp=0) has more than one child, it is an - // articulation point, because otherwise the DFS would have worked - // its away around to root's other children - uint_fast8_t root_children = 0; - - while (sp >= 0) { - struct Coords head; - bool found_head = false; - // Try each direction looking for an edge - while (!found_head && next_direction[sp] != NUM_DIRECTIONS) { - // Don't search back to the parent - if (next_direction[sp] == parent_direction[sp]) { - next_direction[sp]++; - continue; - } - head = stack[sp]; - Coords_move(&head, next_direction[sp]); - if (state->grid[head.q][head.r]) { - found_head = true; - } - next_direction[sp]++; - } - - if (found_head) { - if (crumbs[head.q][head.r] >= 0) { - if (crumbs[head.q][head.r] < lowpoint[sp]) { - lowpoint[sp] = crumbs[head.q][head.r]; - } - } else { - if (sp == 0) { - root_children++; - } - - depth[sp + 1] = depth[sp] + 1; - crumbs[head.q][head.r] = depth[sp] + 1; - lowpoint[sp + 1] = depth[sp]; - next_direction[sp + 1] = 0; - parent_direction[sp + 1] = OPPOSITE[next_direction[sp] - 1]; - stack[sp + 1] = head; - sp++; - } - } else { - if (sp != 0) { - if (lowpoint[sp] == depth[sp - 1] && (sp - 1 != 0)) { - cut_points[state->grid[stack[sp - 1].q][stack[sp - 1].r]->player]++; - } else { - if (lowpoint[sp] < lowpoint[sp - 1]) { - lowpoint[sp - 1] = lowpoint[sp]; - } - } - } - sp--; - } - } - - if (root_children > 1) { - cut_points[state->grid[stack[0].q][stack[0].r]->player]++; - } -} - -void State_derive_piece_pointers(struct State* state) -{ - for (int p = 0; p < NUM_PLAYERS; p++) { - state->queens[p] = NULL; - state->beetle_count[p] = 0; - for (int i = 0; i < state->piece_count[p]; i++) { - if (state->pieces[p][i].type == QUEEN_BEE) { - state->queens[p] = &state->pieces[p][i]; - } - - if (state->pieces[p][i].type == BEETLE) { - state->beetles[p][state->beetle_count[p]++] = &state->pieces[p][i]; - } - } - } -} - void State_derive_hands(struct State* state) { for (int p = 0; p < NUM_PLAYERS; p++) { @@ -576,9 +500,11 @@ void State_derive_neighbor_count(struct State* state) sizeof(uint_fast8_t) * NUM_PLAYERS * GRID_SIZE * GRID_SIZE); for (int p = 0; p < NUM_PLAYERS; p++) { - for (int i = 0; i < state->piece_count[p]; i++) { + for (int i = 0; i < PLAYER_PIECES; i++) { struct Piece* piece = &state->pieces[p][i]; - + if (piece->coords.q == IN_HAND) { + continue; + } if (piece->on_top) { continue; } @@ -592,10 +518,10 @@ void State_derive_result(struct State* state) { state->result = NO_RESULT; for (int p = 0; p < NUM_PLAYERS; p++) { - if (!state->queens[p]) { + struct Piece* queen = &state->pieces[p][QUEEN_INDEX]; + if (queen->coords.q == IN_HAND) { continue; } - struct Piece* queen = state->queens[p]; int neighbors = State_hex_neighbor_count(state, &queen->coords); if (neighbors == 6) { @@ -620,6 +546,10 @@ void State_derive_piece_moves(struct State* state, int piecei) { struct Piece* piece = &state->pieces[state->turn][piecei]; struct Coords* coords = &piece->coords; + + if (coords->q == IN_HAND) { + return; + } // A piece can't move if it's under another piece if (piece->on_top) { @@ -801,7 +731,7 @@ void State_derive_piece_moves(struct State* state, int piecei) void State_derive_actions(struct State* state) { state->action_count = 0; - for (int i = 0; i < state->piece_count[state->turn]; i++) { + for (int i = 0; i < PLAYER_PIECES; i++) { state->piece_move_count[i] = 0; } state->queen_move_count = 0; @@ -822,11 +752,12 @@ void State_derive_actions(struct State* state) // P1 start actions if (state->piece_count[P1] == 0) { for (int t = 0; t < NUM_PIECETYPES; t++) { - if (t == QUEEN_BEE) + if (t == QUEEN_BEE) { continue; + } state->actions[state->action_count].from.q = PLACE_ACTION; - state->actions[state->action_count].from.r = t; + state->actions[state->action_count].from.r = TYPE_INDEX[t]; state->actions[state->action_count].to.q = 0; state->actions[state->action_count++].to.r = 0; } @@ -842,7 +773,7 @@ void State_derive_actions(struct State* state) for (int d = 0; d < NUM_DIRECTIONS; d++) { state->actions[state->action_count].from.q = PLACE_ACTION; - state->actions[state->action_count].from.r = t; + state->actions[state->action_count].from.r = TYPE_INDEX[t]; state->actions[state->action_count].to = *p1_piece_coords; Coords_move(&state->actions[state->action_count++].to, d); } @@ -867,7 +798,10 @@ void State_derive_actions(struct State* state) int place_coords_count = 0; bool place_crumbs[GRID_SIZE][GRID_SIZE]; memset(place_crumbs, 0, sizeof(bool) * GRID_SIZE * GRID_SIZE); - for (int i = 0; i < state->piece_count[state->turn]; i++) { + for (int i = 0; i < PLAYER_PIECES; i++) { + if (state->pieces[state->turn][i].coords.q == IN_HAND) { + continue; + } for (int d = 0; d < NUM_DIRECTIONS; d++) { struct Coords coords = state->pieces[state->turn][i].coords; Coords_move(&coords, d); @@ -879,16 +813,24 @@ void State_derive_actions(struct State* state) } } for (int t = 0; t < NUM_PIECETYPES; t++) { - if (state->hands[state->turn][t] == 0) - continue; - if (force_queen_place && t != QUEEN_BEE) + if (state->hands[state->turn][t] == 0 + || (force_queen_place && t != QUEEN_BEE)) { continue; + } + + for (int piecei = TYPE_INDEX[t]; piecei < TYPE_END[t]; piecei++) { + if (state->pieces[state->turn][piecei].coords.q == IN_HAND) { + continue; + } + + for (int i = 0; i < place_coords_count; i++) { + struct Coords from; + from.q = PLACE_ACTION; + from.r = piecei; + State_add_action(state, piecei, &from, &place_coords[i]); + } - for (int i = 0; i < place_coords_count; i++) { - struct Coords from; - from.q = PLACE_ACTION; - from.r = t; - State_add_action(state, 0, &from, &place_coords[i]); + break; } } } @@ -900,7 +842,7 @@ void State_derive_actions(struct State* state) return; } - for (int i = 0; i < state->piece_count[state->turn]; i++) { + for (int i = 0; i < PLAYER_PIECES; i++) { State_derive_piece_moves(state, i); } @@ -921,7 +863,6 @@ void State_derive(struct State* state) State_derive_piece_players(state); State_derive_grid(state); State_derive_cut_points(state); - State_derive_piece_pointers(state); State_derive_hands(state); State_derive_neighbor_count(state); State_derive_result(state); @@ -936,7 +877,7 @@ void State_copy(const struct State* source, struct State* dest) // Transfer on_top pointers for (int p = 0; p < NUM_PLAYERS; p++) { - for (int i = 0; i < source->piece_count[p]; i++) { + for (int i = 0; PLAYER_PIECES; i++) { const struct Piece* source_piece = &source->pieces[p][i]; if (!source_piece->on_top) { continue; @@ -944,7 +885,7 @@ void State_copy(const struct State* source, struct State* dest) struct Piece* dest_piece = &dest->pieces[p][i]; struct Piece* source_on_top = source_piece->on_top; - for (int j = 0; j < source->piece_count[source_on_top->player]; j++) { + for (int j = 0; j < PLAYER_PIECES; j++) { if (&source->pieces[source_on_top->player][j] == source_on_top) { dest_piece->on_top = &dest->pieces[source_on_top->player][j]; break; @@ -960,6 +901,17 @@ void State_copy(const struct State* source, struct State* dest) void State_new(struct State* state) { memset(state, 0, sizeof(struct State)); + + // Set up static piece array + for (int p = 0; p < NUM_PLAYERS; p++) { + for (int t = 0; t < NUM_PIECETYPES; t++) { + for (int i = TYPE_INDEX[t]; i < TYPE_END[t]; i++) { + state->pieces[p][i].type = t; + state->pieces[p][i].coords.q = IN_HAND; + } + } + } + State_derive(state); } @@ -989,18 +941,12 @@ void State_act(struct State* state, const struct Action* action) // Place action if (action->from.q == PLACE_ACTION) { - piece = &state->pieces[state->turn][state->piece_count[state->turn]]; - piece->type = action->from.r; + piece = &state->pieces[state->turn][action->from.r]; piece->coords.q = action->to.q; piece->coords.r = action->to.r; piece->player = state->turn; state->grid[action->to.q][action->to.r] = piece; - if (piece->type == QUEEN_BEE) { - state->queens[state->turn] = piece; - } else if (piece->type == BEETLE) { - state->beetles[state->turn][state->beetle_count[state->turn]++] = piece; - } state->piece_count[state->turn]++; state->hands[state->turn][piece->type]--; diff --git a/src/state.h b/src/state.h index 08c22cb..f0910dc 100644 --- a/src/state.h +++ b/src/state.h @@ -8,10 +8,6 @@ #define NUM_PLAYERS 2 -#define PLAYER_PIECES 11 -#define MAX_PIECES (PLAYER_PIECES * NUM_PLAYERS) -#define GRID_SIZE 24 - #define NUM_PIECETYPES 5 #define NUM_ANTS 3 #define NUM_BEETLES 2 @@ -19,6 +15,16 @@ #define NUM_SPIDERS 2 #define NUM_QUEEN_BEES 1 +#define PLAYER_PIECES (NUM_ANTS + NUM_BEETLES + NUM_GRASSHOPPERS + NUM_SPIDERS + NUM_QUEEN_BEES) +#define MAX_PIECES (PLAYER_PIECES * NUM_PLAYERS) +#define GRID_SIZE (MAX_PIECES + 2) + +#define ANT_INDEX 0 +#define BEETLE_INDEX (ANT_INDEX + NUM_ANTS) +#define GRASSHOPPER_INDEX (BEETLE_INDEX + NUM_BEETLES) +#define SPIDER_INDEX (GRASSHOPPER_INDEX + NUM_GRASSHOPPERS) +#define QUEEN_INDEX (SPIDER_INDEX + NUM_SPIDERS) + /* Way-high estimate for max actions: * An ant on the end of a line of all pieces (greatest surface area) * would be able to move to 1 + 20*2 + 4 places. @@ -50,6 +56,9 @@ // If Action.from.q == PLACE_ACTION, it means the action is a place, with // Action.r = PieceType #define PLACE_ACTION (GRID_SIZE + 1) +// TODO IN_HAND will probably replace PLACE_ACTION +#define IN_HAND (GRID_SIZE + 1) + // TODO support passing when no moves are possible #define PASS_ACTION (GRID_SIZE + 2) @@ -59,6 +68,7 @@ enum Player { P1 = 0, P2 }; + enum PieceType { ANT = 0, BEETLE, @@ -66,6 +76,14 @@ enum PieceType { SPIDER, QUEEN_BEE }; + +// Index for state.pieces +extern const uint_fast8_t TYPE_INDEX[NUM_PIECETYPES]; + +extern const uint_fast8_t TYPE_END[NUM_PIECETYPES]; + +extern const uint_fast8_t TYPE_COUNT[NUM_PIECETYPES]; + enum Result { P1_WIN = 0, P2_WIN, @@ -104,13 +122,9 @@ struct State { struct Action* winning_action; - struct Piece* queens[NUM_PLAYERS]; struct Action* queen_moves[MAX_QUEEN_MOVES]; uint_fast8_t queen_move_count; - struct Piece* beetles[NUM_PLAYERS][NUM_BEETLES]; - uint_fast8_t beetle_count[NUM_PLAYERS]; - struct Action* piece_moves[PLAYER_PIECES][MAX_PIECE_MOVES]; uint_fast16_t piece_move_count[PLAYER_PIECES]; @@ -154,9 +168,4 @@ void State_act(struct State* state, const struct Action* action); int State_hex_neighbor_count(const struct State* state, const struct Coords* coords); -void State_count_cut_points( - const struct State* state, - const struct Coords* start_coords, - unsigned int cut_points[NUM_PLAYERS]); - #endif diff --git a/src/test.c b/src/test.c index 972e0d1..9f5da92 100644 --- a/src/test.c +++ b/src/test.c @@ -936,19 +936,6 @@ int main(int argc, char* argv[]) } } - // Queen cache - { - strcpy(state_string, "QbbabeAbfGcbgccqcd2"); - State_from_string(&state, state_string); - - if (!state.queens[P1] || state.queens[P1]->coords.q != 1 || state.queens[P1]->coords.r != 1) { - printf("Invalid queen cache for P1\n"); - } - if (!state.queens[P2] || state.queens[P2]->coords.q != 2 || state.queens[P2]->coords.r != 3) { - printf("Invalid queen cache for P2\n"); - } - } - // Queen adjacent action tracking { strcpy(state_string, "gbeqbfbcdbdcGecAedQfb1");