From 79428d619248abbe37ef3d62e002d9684f975851 Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Wed, 14 Sep 2022 20:02:54 +0200 Subject: [PATCH] day23: final version (could add priority queue to improve further ?) --- 2021/RESULTS.txt | 12 + 2021/day23/README.txt | 4 + 2021/day23/aoc-c.c | 830 ++++++++++-------------------------------- 3 files changed, 208 insertions(+), 638 deletions(-) diff --git a/2021/RESULTS.txt b/2021/RESULTS.txt index e81f888..ba61fe6 100644 --- a/2021/RESULTS.txt +++ b/2021/RESULTS.txt @@ -278,6 +278,18 @@ aoc-c : res=1162571910364852 time: 0:00.19 real, 0.18 user, 0.00 sys context-switch: 17+1, page-faults: 0+973 +========================================= +================= day23 ================= +========================================= + +aoc-c : res=11120 + time: 0:00.16 real, 0.14 user, 0.01 sys + context-switch: 34+1, page-faults: 0+4593 + +aoc-c : res=49232 + time: 0:00.09 real, 0.08 user, 0.00 sys + context-switch: 3+1, page-faults: 0+3454 + ========================================= ================= day24 ================= ========================================= diff --git a/2021/day23/README.txt b/2021/day23/README.txt index c75f787..3ebbb28 100644 --- a/2021/day23/README.txt +++ b/2021/day23/README.txt @@ -325,3 +325,7 @@ In this updated example, the least energy required to organize these amphipods i ######### Using the initial configuration from the full diagram, what is the least energy required to organize the amphipods? + +Your puzzle answer was 49232. + +Both parts of this puzzle are complete! They provide two gold stars: ** diff --git a/2021/day23/aoc-c.c b/2021/day23/aoc-c.c index 5f4895a..b8ccbce 100644 --- a/2021/day23/aoc-c.c +++ b/2021/day23/aoc-c.c @@ -29,54 +29,39 @@ static const u64 cost[] = { 1, 10, 100, 1000 }; -#define _HALLWAY 0x7F /* 0x000001111111 */ -#define _ROOM_A 0xF00 /* 0x111100000000*/ -#define _ROOM_B 0xF000 /* 0x1111000000000000*/ -#define _ROOM_C 0xF0000 /* 0x11110000000000000000*/ -#define _ROOM_D 0xF00000 /* 0x111100000000000000000000*/ -#define _ROOM 0xFFFF00 +#define _ROOM_A 0x00000F00 /* 000000000000111100000000*/ +#define _ROOM_B 0x0000F000 /* 000000001111000000000000*/ +#define _ROOM_C 0x000F0000 /* 000011110000000000000000*/ +#define _ROOM_D 0x00F00000 /* 111100000000000000000000*/ +#define _ROOM 0x00FFFF00 /* 111111111111111100000000 */ +#define _HALLWAY 0x0000007F /* 000000000000000001111111 */ -#define BIT(c) (1u << (c)) +#define BIT(c) (1 << (c)) -#define RAND_SEED 1 /* seed for random generator */ -#define HASH_SIZE 3000017 /* prime */ +#define RAND_SEED 1337 /* seed for random generator */ static u32 rooms[4] = { _ROOM_A, _ROOM_B, _ROOM_C, _ROOM_D }; static u64 result = -1; -typedef struct { - u32 from, to; -} move_t; - typedef struct pos { - u32 amp[4]; /* bitboards */ - u32 final; /* bitboard: 1 if final destination */ - int moves; - u64 cost; /* cost till this position */ - u64 eval; /* cost + estimation to final pos */ - u32 occupied; + u32 amp[4]; /* bitmask of amphipods position */ + u32 occupied; /* bitmask for all occupied cells */ + u32 final; /* bitmask for correct position */ u64 zobrist; /* for zobrist_2() */ - move_t moves_list[64]; - struct list_head list; + u64 cost; /* cost till this position */ + struct list_head list; /* */ } pos_t; -/* All possible moves between hallway and rooms - */ -typedef struct { - uint mask; /* blocking spaces */ - uint dist; -} possible_move_t; - -static possible_move_t moves[24][24]; - typedef struct hash { u64 zobrist; /* zobrist hash */ u32 amp[4]; - move_t moves_list[64]; - int moves; + u64 cost; struct list_head list; } hash_t; +#define HASH_SIZE 131071 +#define HASH_SEEN (HASH_SIZE + 1) + struct { u32 count; struct list_head list; @@ -94,13 +79,12 @@ LIST_HEAD(pos_queue); static u64 zobrist_table[24][4]; -char *cells[] = { - "H1", "H2", "H3", "H4", "H5", "H6", "H7", "BAD", - "A1", "A2", "A3", "A4", - "B1", "B2", "B3", "B4", - "C1", "C2", "C3", "C4", - "D1", "D2", "D3", "D4", -}; +/* from https://stackoverflow.com/a/33021408/3079831 + */ +#define IMAX_BITS(m) ((m)/((m)%255+1) / 255%255*8 + 7-86/((m)%255+12)) +#define RAND_MAX_WIDTH IMAX_BITS(RAND_MAX) +_Static_assert((RAND_MAX & (RAND_MAX + 1u)) == 0, "RAND_MAX not a Mersenne number"); + /* * ############# @@ -130,14 +114,13 @@ typedef enum { #define ROOM(b) ((b) & _ROOM) #define IN_HALLWAY(c) ((int)(c) >= _H1 && (int)(c) <= _H7) -#define IN_ROOM(c) (((int)(c) >= _A1 && (int)(c) <= _A4) || \ - ((int)(c) >= _B1 && (int)(c) <= _B4) || \ +#define IN_ROOM(c) (((int)(c) >= _A1 && (int)(c) <= _A4) || \ + ((int)(c) >= _B1 && (int)(c) <= _B4) || \ ((int)(c) >= _C1 && (int)(c) <= _C4) || \ ((int)(c) >= _D1 && (int)(c) <= _D4)) -#define HASH_SEEN (HASH_SIZE + 1) -#define LEFT 0 -#define RIGHT 1 +#define LEFT 0 +#define RIGHT 1 static s32 room_exit[4][2][6] = { { { _H2, _H1, -1 }, /* room A left */ @@ -154,379 +137,44 @@ static s32 room_exit[4][2][6] = { } }; -static char *int2bin(u32 mask) -{ - static char ret[64]; - for (int i = 0; i < 32; ++i) { - ret[31-i] = mask & BIT(i)? '1': '0'; - } - ret[32] = 0; - return ret; -} - -static char *int2pos(u32 mask) -{ - static char res[1024]; - char *p = res; - - for (int i = 0; i < 32; ++i) { - if (mask & BIT(i)) { - *p++ = ' '; - strcpy(p, cells[i]); - p += 2; - } - } - return res; -} - -static void moves_print(move_t *moves, int nmoves) -{ - for (int i = 0; i < nmoves; ++i) - printf(" %s-%s", cells[moves[i].from], cells[moves[i].to]); - printf("\n"); -} - -static void amp_print_flat(u32 *amp) -{ - int rows = popcount32(*amp); - - for (int i = _H1; i <= _H7; ++i) { /* hallway */ - if (i >= 2 && i <= 5) - log(1, "$"); - log(1, "%c", - BIT(i) & amp[A]? 'A': - BIT(i) & amp[B]? 'B': - BIT(i) & amp[C]? 'C': - BIT(i) & amp[D]? 'D': '.'); - } - for (int j = 0; j < 4; ++j) { - log(1, " "); - for (int i = 0; i < rows; ++i) { - int offset = 8 + i; - - log(1, "%c", - BIT(offset + 4 * j) & amp[A]? 'A': - BIT(offset + 4 * j) & amp[B]? 'B': - BIT(offset + 4 * j) & amp[C]? 'C': - BIT(offset + 4 * j) & amp[D]? 'D': '.'); - } - } - log(1, "\n"); - -} - -static void burrow_print_flat(pos_t *pos) -{ - int rows = popcount32(pos->amp[0]); - - log_f(1, "rows=%d moves=%2d cost=%6lu: ", rows, pos->moves, pos->cost); - amp_print_flat(pos->amp); -} - -static void burrow_print(pos_t *pos) -{ - u32 tmp; - int count; - int rows = popcount32(pos->amp[0]); - - log_f(1, "rows=%d moves=%d cost=%lu\n", rows, pos->moves, pos->cost); - for (int i = 0; i < 4; ++i) { - bit_for_each32_2(count, tmp, pos->amp[i]) { - log(1, " %d", count); - } - } - - log(1, "\n"); - - log(1, "#############\n#"); - for (int i = _H1; i <= _H7; ++i) { /* hallway */ - if (i >= 2 && i <= 5) - log(1, "$"); - log(1, "%c", - BIT(i) & pos->amp[A]? 'A': - BIT(i) & pos->amp[B]? 'B': - BIT(i) & pos->amp[C]? 'C': - BIT(i) & pos->amp[D]? 'D': '.'); - } - log(1, "#\n"); - for (int i = 0; i < rows; ++i) { - int offset = 8 + i; - log(1, i? " #": "###"); - for (int j = 0; j < 4; ++j) { - log(1, "%c#", - BIT(offset + 4 * j) & pos->amp[A]? 'A': - BIT(offset + 4 * j) & pos->amp[B]? 'B': - BIT(offset + 4 * j) & pos->amp[C]? 'C': - BIT(offset + 4 * j) & pos->amp[D]? 'D': '.'); - //log(4, "%c#", bits & BIT(pos + 4 * j)? 'X': '.'); - } - log(1, i? "\n": "##\n"); - } - log(1, " #########\n"); -} - -/* from https://stackoverflow.com/a/33021408/3079831 - */ -#define IMAX_BITS(m) ((m)/((m)%255+1) / 255%255*8 + 7-86/((m)%255+12)) -#define RAND_MAX_WIDTH IMAX_BITS(RAND_MAX) -_Static_assert((RAND_MAX & (RAND_MAX + 1u)) == 0, "RAND_MAX not a Mersenne number"); - -static u64 rand64(void) { - u64 r = 0; - for (int i = 0; i < 64; i += RAND_MAX_WIDTH) { - r <<= RAND_MAX_WIDTH; - r ^= (unsigned) rand(); - } - return r; -} - -static void zobrist_init() -{ - log_f(1, "seed=%d rand_max=%d\n", RAND_SEED, RAND_MAX); - srand(RAND_SEED); - for (int i = 0; i < 24; ++i) { - for (int j = 0; j < 4; ++j) { - zobrist_table[i][j] = rand64(); - log(10, "%lu ", zobrist_table[i][j]); - } - } -} - -static inline u64 zobrist_1(pos_t *pos) -{ - u32 tmp; - int bit; - u64 zobrist = 0; - - for (int amp = 0; amp < 4; ++amp) { - bit_for_each32_2(bit, tmp, pos->amp[amp]) { - //log_f(2, "amp=%d/%c bit=%d\n", amp, amp+'A', bit); - zobrist ^= zobrist_table[bit][amp]; - } - } - log_f(1, "zobrist=%lu -> %lu\n", zobrist, zobrist % HASH_SIZE); - return zobrist; -} - -/* calculate zobrist hash from previous zobrist value - */ -static inline u64 zobrist_2(pos_t *pos, int amp, u32 from, u32 to) -{ - u64 zobrist = pos->zobrist; - - zobrist ^= zobrist_table[from][amp]; - zobrist ^= zobrist_table[to][amp]; - log_f(1, "zobrist old=%lu new=%lu (%lu) (amp=%d from=%u to=%u)\n", - pos->zobrist, zobrist, zobrist % HASH_SIZE, amp, from, to); - - if (zobrist % HASH_SIZE == 2321581) { - log(1, "Zobi: "); - burrow_print_flat(pos); - log(1, "Zobi moves: "); - moves_print(pos->moves_list, pos->moves); - //for (int i = 0; i < pos->moves; ++i) { - // log(1, " %s-%s", cells[pos->moves_list[i].from], cells[pos->moves_list[i].to]); - //} - //log(1, "\n"); - - } - - return zobrist; -} - -static void hash_init() -{ - for (int i = 0; i < HASH_SIZE; ++i) { - hasht[i].count = 0; - INIT_LIST_HEAD(&hasht[i].list); - } -} - -/* get a position from memory pool - */ -static hash_t *get_hash(pos_t *pos) -{ - hash_t *new; - if (!(new = pool_get(pool_hash))) - return NULL; - for (int i = 0; i < 4; ++i) - new->amp[i] = pos->amp[i]; - /* copy moves list - */ - for (int i = 0; i < 64; ++i) - new->moves_list[i] = pos->moves_list[i]; - new->moves = pos->moves; - INIT_LIST_HEAD(&new->list); - return new; -} - -static void hash_stats() -{ - u64 ncollisions = 0; - u32 min = -1, max = 0, minpos = 0, maxpos = 0; - - for (int i = 0; i < HASH_SIZE; ++i) { - ncollisions += hasht[i].count; - if (hasht[i].count < min) { - min = hasht[i].count; - minpos = i; - } else if (hasht[i].count > max) { - max = hasht[i].count; - maxpos = i; - } - } - printf("hash: size=%d min=%u (pos=%u) max=%u (pos=%u) total=%lu average=%lu\n", - HASH_SIZE, - min, minpos, max, maxpos, ncollisions, ncollisions / HASH_SIZE); -} - -static u64 hash(pos_t *pos, int amp, u32 from, u32 to) -{ - hash_t *cur; - u64 zobrist; - int hashpos; - //pos_t pos_tmp = *pos; - - log(1, "Zobi0A: amp=%c from=%s to=%s bfrom=%d bto=%d\n", - amp + 'A', cells[from], cells[to], BIT(from), BIT(to)); - //log(1, "Zobi00: "); - //amp_print_flat(pos_tmp.amp); - //pos_tmp.amp[amp] ^= BIT(from); - //pos_tmp.amp[amp] |= BIT(to); - //pos_tmp.moves_list[pos_tmp.moves].from = from; - //pos_tmp.moves_list[pos_tmp.moves].to = to; - //pos_tmp.moves++; - log(1, "Zobi01: "); - amp_print_flat(pos->amp); - - /* we use the 2 first chars of the amps 32, this should be enough - * to avoid most collisions - */ - zobrist = zobrist_2(pos, amp, from, to); - pos->zobrist = zobrist; - hashpos = zobrist % HASH_SIZE; - log_f(1, "zobrist=%lu->%u, count=%d\n", zobrist, hashpos, hasht[hashpos].count); - list_for_each_entry(cur, &hasht[hashpos].list, list) { - if (zobrist == cur->zobrist) { - if (pos->amp[0] == cur->amp[0] && - pos->amp[1] == cur->amp[1] && - pos->amp[2] == cur->amp[2] && - pos->amp[3] == cur->amp[3]) { - log(1, "zobrist collision for same positions\n"); - log(1, "Zobirst=%lu->%u, count=%d\n", zobrist, hashpos, - hasht[hashpos].count); - log(1, "Zobi cur: "); - amp_print_flat(cur->amp); - log(1, "Zobi cur: "); - moves_print(cur->moves_list, cur->moves); - - log(1, "Zobi tmp: "); - amp_print_flat(pos->amp); - log(1, "Zobi tmp: "); - moves_print(pos->moves_list, pos->moves); - log(1, "Returning zobrist=HASH_SEEN\n"); - return HASH_SEEN; - } else { - log(1, "zobrist collision for different positions:\n"); - log(1, "Zobirst=%lu->%u, count=%d\n", zobrist, hashpos, - hasht[hashpos].count); - log(1, "Zobi cur: "); - amp_print_flat(cur->amp); - log(1, "Zobi cur: "); - moves_print(cur->moves_list, cur->moves); - log(1, "Zobi tmp: "); - amp_print_flat(pos->amp); - log(1, "Zobi tmp: "); - moves_print(pos->moves_list, pos->moves); - } - } - } - hasht[hashpos].count++; - log(1, "adding hash count=%u\n", hasht[hashpos].count); - cur = get_hash(pos); - cur->zobrist = zobrist; - cur->amp[amp] ^= BIT(from); - cur->amp[amp] |= BIT(to); - cur->moves_list[cur->moves].from = from; - cur->moves_list[cur->moves].to = to; - cur->moves++; - - list_add(&cur->list, &hasht[hashpos].list); - log(1, "Returning zobrist=%lu\n", zobrist); - return zobrist; -} - -/* evaluate current position : - * eval = cost + eval_to_dest - * cost is current cost - * eval is cost to join top of destination room - */ -static int room2room_dist[][4] = { - { 4, 4, 6, 8}, { 4, 4, 4, 6}, { 6, 4, 4, 4}, { 8, 6, 4, 4} +char *cells[] = { + "H1", "H2", "H3", "H4", "H5", "H6", "H7", "BAD", + "A1", "A2", "A3", "A4", + "B1", "B2", "B3", "B4", + "C1", "C2", "C3", "C4", + "D1", "D2", "D3", "D4", }; -/* static u64 eval_amp(pos_t *pos, int amp, u32 cell) */ -/* { */ -/* u32 bit = BIT(cell); */ -/* int rows = popcount32(pos->amp[0]); */ -/* int dist; */ -/* int left = rows - popcount32(rooms[amp] & pos->final); */ +typedef enum { + H1 = BIT(_H1), H2 = BIT(_H2), H3 = (_H3), H4 = (_H4), + H5 = (_H5), H6 = (_H6), H7 = (_H7), + A1 = (_A1), A2 = (_A2), A3 = (_A3), A4 = (_A4), + B1 = (_B1), B2 = (_B2), B3 = (_B3), B4 = (_B4), + C1 = (_C1), C2 = (_C2), C3 = (_C3), C4 = (_C4), + D1 = (_D1), D2 = (_D2), D3 = (_D3), D4 = (_D4), +} space_t; -/* if (ROOM(bit)) { /\* in a room *\/ */ -/* int curroom = cell / 4 - 2; */ +/* Steps to move to hallway + */ +typedef enum { + A1H = 1, A2H = 2, A3H = 3, A4H = 4 +} out_t; -/* dist = cell % 4; /\* distance to top of room *\/ */ -/* dist += room2room_dist[curroom][amp]; */ -/* dist += --left; /\* distance to final room *\/ */ -/* log(1, "eval %c room(%s) = %d\n", amp + 'A', cells[cell], dist); */ -/* } else { /\* in hallway *\/ */ -/* int dest = (amp + 2) * 4; */ -/* dist = moves[cell][dest].dist; */ -/* dist += --left; /\* distance to final room *\/ */ -/* log(1, "eval %c hallway(%s) = %d\n", amp + 'A', cells[cell], dist); */ -/* } */ -/* return cost[amp] * dist; */ -/* } */ +/* Mask which disallow moves to hallway + */ +typedef struct { + uint mask, dist; +} possible_move_t; -static u64 eval(pos_t *pos) -{ - u32 amp, tmp; - int cell; - int rows = popcount32(pos->amp[0]); - u64 eval = 0; - - for (amp = A; amp <= D; ++amp) { - u32 todo = pos->amp[amp] & ~pos->final; /* ignore finished ones */ - int destroom = amp, dist; - int left = rows - popcount32(rooms[destroom] & pos->final); - - printf("amp=%d %u final=%u\n", amp, pos->amp[amp], pos->final); - printf("amp=%c pos=%s\n", amp + 'A', int2bin(pos->amp[amp])); - printf("amp= tod=%s\n", int2bin(todo)); - printf("amp= fin=%s\n", int2bin(pos->final)); - bit_for_each32_2(cell, tmp, todo) { - u32 bit = BIT(cell); - if (ROOM(bit)) { /* in a room */ - int curroom = cell / 4 - 2; - - dist = cell % 4; /* distance to top of room */ - dist += room2room_dist[curroom][destroom]; - dist += --left; /* distance to final room */ - log(1, "eval %c room(%s) = %d\n", amp + 'A', cells[cell], dist); - } else { /* in hallway */ - int dest = (destroom + 2) * 4; - dist = moves[cell][dest].dist; - dist += --left; /* distance to final room */ - log(1, "eval %c hallway(%s) = %d\n", amp + 'A', cells[cell], dist); - } - //printf("eval = %lu %lu\n", cost[amp] * dist, eval_amp(pos, amp, cell)); - eval += cost[amp] * dist; - } - } - //log_f(1, "pos=%p amp=%d from=%u to=%u\n", pos, amp, from, to); - return eval; -} +/* Steps to move from space outside room to hallway destination + */ +typedef enum { + AH1 = 2, AH2 = 1, AH3 = 1, AH4 = 3, AH5 = 5, AH6 = 7 , AH7 = 8, + BH1 = 4, BH2 = 3, BH3 = 1, BH4 = 1, BH5 = 3, BH6 = 5 , BH7 = 6, + CH1 = 6, CH2 = 5, CH3 = 3, CH4 = 1, CH5 = 1, CH6 = 3 , CH7 = 4, + DH1 = 8, DH2 = 7, DH3 = 5, DH4 = 3, DH5 = 1, DH6 = 1 , DH7 = 2 +} steps_t; /* * ############# @@ -566,9 +214,7 @@ static pos_t *get_pos(pos_t *from) return NULL; if (!from) { new->amp[0] = new->amp[1] = new->amp[2] = new->amp[3] = 0; - new->final = new->occupied = new->zobrist = 0; - new->cost = new->eval = 0; - new->moves = 0; + new->cost = new->occupied = 0; } else { *new = *from; } @@ -583,7 +229,6 @@ static void free_pos(pos_t *pos) if (pos) { pool_add(pool_pos, pos); } else { - log(1, "Fatal bar\n"); exit(1); } } @@ -592,26 +237,11 @@ static void free_pos(pos_t *pos) */ static void push_pos(pos_t *pos) { - pos_t *cur; - - if (!pos) { - log(1, "Fatal foo\n"); + if (pos) { + list_add(&pos->list, &pos_queue); + } else { exit(1); } - if (!list_empty(&pos_queue)) { - //list_add_tail(&pos->list, &pos_queue); - //} else { - list_for_each_entry(cur, &pos_queue, list) { - if (cur->eval > pos->eval) { - log(1, "adding %lu before %lu\n", pos->eval, cur->eval); - list_add_tail(&pos->list, &cur->list); - return; - } else { - log(1, "not adding %lu before %lu\n", pos->eval, cur->eval); - } - } - } - list_add_tail(&pos->list, &pos_queue); } /* pop a position from stack @@ -625,62 +255,108 @@ static pos_t *pop_pos() return pos; } -static void print_moves(possible_move_t (*moves)[24]) -{ - int hallway, room; +static u64 rand64(void) { + u64 r = 0; + for (int i = 0; i < 64; i += RAND_MAX_WIDTH) { + r <<= RAND_MAX_WIDTH; + r ^= (unsigned) rand(); + } + return r; +} - log(4, "From rooms to hallway\n"); - for (room = _A1; room <= _D4; ++room) { - for (hallway = _H1; hallway <= _H7; ++hallway) { - log(4, "%s -> %s ", cells[room], cells[hallway]); - log(4, "dist=%d ", moves[room][hallway].dist); - log(4, "mask="); - log(4, "%s\n", int2pos(moves[room][hallway].mask)); - } - } - log(4, "From hallway to rooms\n"); - for (hallway = _H1; hallway <= _H7; ++hallway) { - for (room = _A1; room <= _D4; ++room) { - log(4, "%s -> %s ", cells[hallway], cells[room]); - log(4, "dist=%d ", moves[hallway][room].dist); - log(4, "mask="); - log(4, "%s\n", int2pos(moves[hallway][room].mask)); +static void zobrist_init() +{ + log_f(1, "seed=%d rand_max=%d\n", RAND_SEED, RAND_MAX); + srand(RAND_SEED); + for (int i = 0; i < 24; ++i) { + for (int j = 0; j < 4; ++j) { + zobrist_table[i][j] = rand64(); } } } -static void mask_print(u32 bits) +static inline u64 zobrist_1(pos_t *pos) { u32 tmp; - int count; - int rows = popcount32(bits); + int bit; + u64 zobrist = 0; - if (rows > 4) - rows /= 4; - log(10, "%u: popcount=%d ", bits, popcount32(bits)); - bit_for_each32_2(count, tmp, bits) { - log(10, " %d", count); - } - log(10, "\n"); - - log(10, "#############\n#"); - for (int i = _H1; i <= _H7; ++i) { /* hallway */ - if (i >= 2 && i <= 5) - log(10, "$"); - log(10, "%c", bits & BIT(i)? 'X': '.'); - } - log(10, "#\n"); - for (int i = 0; i < rows; ++i) { - int pos = 8 + i; - log(10, i? " #": "###"); - for (int j = 0; j < 4; ++j) { - log(10, "%c#", bits & BIT(pos + 4 * j)? 'X': '.'); + for (int amp = 0; amp < 4; ++amp) { + bit_for_each32_2(bit, tmp, pos->amp[amp]) { + //log_f(2, "amp=%d/%c bit=%d\n", amp, amp+'A', bit); + zobrist ^= zobrist_table[bit][amp]; } - log(10, i? "\n": "##\n"); } - log(10, " #########\n"); + log_f(1, "zobrist=%lu -> %lu\n", zobrist, zobrist % HASH_SIZE); + return zobrist; } +/* calculate zobrist hash from previous zobrist value + */ +static inline u64 zobrist_2(pos_t *pos, int amp, u32 from, u32 to) +{ + u64 zobrist = pos->zobrist; + + zobrist ^= zobrist_table[from][amp]; + zobrist ^= zobrist_table[to][amp]; + log_f(1, "zobrist(%d, %u, %u)=%lu -> %lu (amp=%d from=%u to=%u)\n", + amp, from, to, zobrist, zobrist % HASH_SIZE, amp, from, to); + return zobrist; +} + +static void hash_init() +{ + for (int i = 0; i < HASH_SIZE; ++i) { + hasht[i].count = 0; + INIT_LIST_HEAD(&hasht[i].list); + } +} + +/* get a position from memory pool + */ +static hash_t *get_hash(pos_t *pos) +{ + hash_t *new; + if (!(new = pool_get(pool_hash))) + return NULL; + for (int i = 0; i < 4; ++i) + new->amp[i] = pos->amp[i]; + new->cost = pos->cost; + new->zobrist = pos->zobrist; + + INIT_LIST_HEAD(&new->list); + return new; +} + +static hash_t *hash(pos_t *pos) +{ + hash_t *cur; + u32 hashpos; + u64 zobrist = pos->zobrist; + + hashpos = zobrist % HASH_SIZE; + list_for_each_entry(cur, &hasht[hashpos].list, list) { + if (zobrist == cur->zobrist) { + if (pos->amp[0] == cur->amp[0] && + pos->amp[1] == cur->amp[1] && + pos->amp[2] == cur->amp[2] && + pos->amp[3] == cur->amp[3] && + pos->cost == cur->cost) { + return NULL; + } + } + } + hasht[hashpos].count++; + cur = get_hash(pos); + + list_add(&cur->list, &hasht[hashpos].list); + return cur; +} + + +/* generate possible moves between hallway and rooms + */ +static possible_move_t moves[24][24]; /* calculate distance and move mask for all possible moves * (room -> hallway ans hallway -> room) @@ -694,19 +370,13 @@ static possible_move_t (*init_moves())[24] pos = (room >> 2) - 2; for (hallway = _H1; hallway <= _H7; ++hallway) { dist = h2h[pos][hallway] + room % 4 + 1; - log(3, "\ndist(%s, %s) = %d - h2h=%d pos=%d\n", cells[room], - cells[hallway], dist, - h2h[pos][hallway], pos); - /* from rool to hallway */ + /* from room to hallway */ if (room >> 2 > hallway) mask_h = setbits(hallway, room >> 2); else mask_h = setbits(room >> 2, hallway + 1); mask_r = setbits(room & ~3, room); - log(5, "\tmask_r=%d <%s>", mask_r, int2bin(mask_r)); - log(5, "\tmask_h=%d <%s>\n", mask_h, int2bin(mask_h)); - log(3, "%s\n", int2pos(mask_r | mask_h)); moves[room][hallway].mask = mask_r | mask_h; moves[room][hallway].dist = dist; @@ -716,9 +386,6 @@ static possible_move_t (*init_moves())[24] else mask_h = setbits(room >> 2, hallway); mask_r = setbits(room & ~3, room + 1); - log(5, "\tmask_r=%d <%s>", mask_r, int2bin(mask_r)); - log(5, "\tmask_h=%d <%s>\n", mask_h, int2bin(mask_h)); - log(3, "%s\n", int2pos(mask_r | mask_h)); moves[hallway][room].mask = mask_r | mask_h; moves[hallway][room].dist = dist; @@ -728,96 +395,43 @@ static possible_move_t (*init_moves())[24] return moves; } + static pos_t *newmove(pos_t *pos, amphipod_t amp, u32 from, u32 to) { int rows = popcount32(pos->amp[0]); possible_move_t *move = &moves[from][to]; - pos_t *newpos, pos_tmp = *pos; - u64 newcost = pos->cost + move->dist * cost[amp]; + pos_t *newpos; u32 bit_from = BIT(from), bit_to = BIT(to); - int ok = popcount32(pos->final); - u64 zobrist; - log_f(1, "rows=%d amp=%c from=%s to=%s dist=%u ok=%d cost=%lu zobrist=%lu\n", - rows, amp + 'A', - cells[from], cells[to], - move->dist, ok, move->dist * cost[amp], pos->zobrist); - - /* - if ((IN_HALLWAY(from) && !IN_ROOM(to)) || - (IN_ROOM(from) && !IN_HALLWAY(to)) || - (HALLWAY(bit_from) && !ROOM(bit_to)) || - (ROOM(bit_from) && !HALLWAY(bit_to))) { - log(1, "BUG genmove!\n"); - } - */ - if (newcost >= result) + if (pos->cost + move->dist * cost[amp] >= result) return NULL; - pos_tmp = *pos; - pos_tmp.amp[amp] ^= bit_from; - pos_tmp.amp[amp] |= bit_to; - pos_tmp.occupied ^= bit_from; - pos_tmp.occupied |= bit_to; - pos_tmp.moves_list[pos_tmp.moves].from = from; - pos_tmp.moves_list[pos_tmp.moves].to = to; - pos_tmp.moves++; - pos_tmp.cost = newcost; - if (ROOM(bit_to)) { /* to room */ - //newpos->ok++; - pos_tmp.final |= bit_to; - log(1, "Final destination %s, ok=%d, final=%s\n", cells[to], - popcount32(pos_tmp.final), int2pos(pos_tmp.final)); - if (popcount32(pos_tmp.final) == rows * 4) { - log(1, "found solution! cost=%lu\n", pos_tmp.cost); - if (pos_tmp.cost < result) { - result = pos_tmp.cost; - log(1, "New best=%lu moves=%u List:", result, pos_tmp.moves); - moves_print(pos_tmp.moves_list, pos_tmp.moves); - } - return NULL; - } - } - /* final move, cannot fail */ - /* - if (ok == rows * 4 - 1) { - u64 final = newcost; - log(1, "found solution! cost=%lu\nZobi10 ", final); - burrow_print_flat(pos); - if (final < result) { - result = final; - log(1, "New best=%lu moves=%u+1 List:", result, pos->moves); - for (int i = 0; i < pos->moves; ++i) { - log(1, " %s-%s", cells[pos->moves_list[i].from], - cells[pos->moves_list[i].to]); - } - log(1, "\n"); - } - } - */ - //if (zobrist != zobrist_1(&pos_tmp)) { - // printf("Zobi Fuck1\n"); - // exit(1); - //} - //if (zobrist != pos_tmp.zobrist) { - // printf("Zobi Fuck2\n"); - // exit(1); - //} - //zobrist = hash1(pos); - zobrist = hash(&pos_tmp, amp, from, to); - if (ok < rows * 4 - 1) { /* */ - if (zobrist == HASH_SEEN) { - log(1, "collision, skipping move : "); - burrow_print_flat(&pos_tmp); - return NULL; - } - } - pos_tmp.eval = eval(&pos_tmp) + pos_tmp.cost; - if (!(newpos = get_pos(&pos_tmp))) + if (!(newpos = get_pos(pos))) return NULL; - burrow_print_flat(newpos); + newpos->amp[amp] ^= bit_from; + newpos->amp[amp] |= bit_to; + newpos->occupied ^= bit_from; + newpos->occupied |= bit_to; + newpos->cost += move->dist * cost[amp]; + + if (HALLWAY(bit_from)) { /* from hallway */ + newpos->final |= BIT(to); + if (popcount32(newpos->final) == rows * 4) { + if (newpos->cost < result) + result = newpos->cost; + free_pos(newpos); + return NULL; + } + + } + + newpos->zobrist = zobrist_2(newpos, amp, from, to); + if (! hash(newpos)) { /* collision */ + free_pos(newpos); + return NULL; + } + push_pos(newpos); - log(1, "New position: moves=%d cost=%lu\n", newpos->moves, newpos->cost); return newpos; } @@ -830,36 +444,19 @@ static void genmoves(pos_t *pos) int bit; int rows = popcount32(pos->amp[0]); - if (pos->cost >= result) - return; - log_f(1, "position :\n"); - burrow_print(pos); for (amp = A; amp <= D; ++amp) { u32 cur = pos->amp[amp]; bit_for_each32_2(bit, tmp, cur) { - log(3, "toto -> %s %d\n", int2bin(bit), bit); - if (bit >= _A1) { /* in a room */ - if (BIT(bit) & pos->final) { - log(1, "position already ok\n"); + if (BIT(bit) & pos->final) continue; - } - /* TODO: already in dest room */ int room = (bit >> 2) - 2; - log(3, "room = %d\n", room); for (int side = LEFT; side <= RIGHT; ++side) { int *d = room_exit[room][side]; for (; *d != -1; ++d) { - log(3, "checking %c from %s to %s\n", - amp + 'A', cells[bit], cells[*d]); possible_move_t *move = &moves[bit][*d]; - log(3, "mask=%s dist=%d cost=%lu\n", - int2bin(move->mask), move->dist, - move->dist * cost[amp]); - if (move->mask & pos->occupied) { - log(3, "blocked!\n"); + if (move->mask & pos->occupied) break; - } newmove(pos, amp, bit, *d); } } @@ -868,31 +465,22 @@ static void genmoves(pos_t *pos) u32 room = rooms[amp], found = 0; int dest, count = 0, tmp; - log(1, "%c from %s to %#x (%s)\n", amp + 'A', cells[bit], room, - int2bin(room)); - if (room & pos->occupied && !(room & pos->final)) { - log(1, "room is occupied\n"); - continue; /* skip current amp pos */ - } - log(1, "room is available\n"); + /* we cannot enter final room if some amphipods were correctly + * placed from initial position AND we still have different + * amphipods in that room. + */ + if ((room & pos->final) != (room & pos->occupied)) + continue; bit_for_each32_2(dest, tmp, room) { possible_move_t *move = &moves[bit][dest]; - log(1, " %d(%s) -> %d(%s) : ", bit, cells[bit], - dest, cells[dest]); - if (move->mask & pos->occupied) { - log(1, "blocked\n"); + if (move->mask & pos->occupied) break; - } - log(1, "valid\n"); found = dest; if (++count >= rows) break; } - if (found) { - log(1, "found %s -> %s\n", cells[bit], cells[found]); + if (found) newmove(pos, amp, bit, found); - } - } } } @@ -915,7 +503,7 @@ static pos_t *read_input(int part) while ((buflen = getline(&buf, &alloc, stdin)) > 0) { buf[--buflen] = 0; - log(3, "line=%d str=%s\n", line, buf); + log(1, "line=%d str=%s\n", line, buf); if (line == 2 || line == 3) { if (part == 2 && line == 3) { @@ -925,7 +513,6 @@ static pos_t *read_input(int part) for (int j = 0; j < 4; ++j) { int amp = part2str[i][j * 2 + 3] - 'A'; pos->amp[amp] |= BIT(bit); - log(3, "setting bit %d to %c\n", bit, amp + 'A'); bit += 4; } adjline++; @@ -935,7 +522,6 @@ static pos_t *read_input(int part) for (int i = 0; i < 4; ++i) { int amp = buf[i * 2 + 3] - 'A'; pos->amp[amp] |= BIT(bit); - log(3, "setting bit %d to %c\n", bit, amp + 'A'); bit += 4; } } @@ -948,19 +534,14 @@ static pos_t *read_input(int part) for (int room = 0; room < 4; ++room) { u32 mask = pos->amp[room]; if (mask & rooms[room]) { - log(3, "found amp %c in room %d\n", room + 'A', room); mask &= rooms[room]; int room1 = 8 + room * 4; for (int cell = part * 2 - 1; cell >= 0; --cell) { if (BIT(room1 + cell) & mask) { - log(3, " -> Match for cell %d\n", cell + 1); pos->final |= BIT(room1 + cell); - //pos->ok++; } else { - log(3, " -> No Match for cell %d\n", cell + 1); break; } - //if (mask && mask & rooms[cell]) } } @@ -969,16 +550,6 @@ static pos_t *read_input(int part) return pos; } -static s64 part1() -{ - return 1; -} - -static s64 part2() -{ - return 2; -} - static int usage(char *prg) { fprintf(stderr, "Usage: %s [-d debug_level] [-p part]\n", prg); @@ -1013,32 +584,15 @@ int main(int ac, char **av) zobrist_init(); hash_init(); init_moves(); - print_moves(moves); - pos = read_input(part); pos->zobrist = zobrist_1(pos); push_pos(pos); - /* - for (int i = 0; i < 4; ++ i) { - log(1, "------------ %c\n", 'A' + i); - mask_print(pos->amp[i]); - } - */ - mask_print(pos->occupied); - - burrow_print(pos); - while ((pos = pop_pos())) { genmoves(pos); free_pos(pos); } - hash_stats(); printf("%s : res=%ld\n", *av, result); - printf("%s : res=%ld\n", *av, part == 1? part1(): part2()); - exit(0); - /* dummy calls for flycheck */ - eval(pos); }