Day 23: zobrist hash, still wrong collisions (works only without hash)
This commit is contained in:
@@ -37,28 +37,33 @@ static const u64 cost[] = {
|
|||||||
#define ROOM_C 0xF0000 /* 0x11110000000000000000*/
|
#define ROOM_C 0xF0000 /* 0x11110000000000000000*/
|
||||||
#define ROOM_D 0xF00000 /* 0x111100000000000000000000*/
|
#define ROOM_D 0xF00000 /* 0x111100000000000000000000*/
|
||||||
#define ROOM 0xFFFF00
|
#define ROOM 0xFFFF00
|
||||||
|
#define BIT(c) (1 << (c))
|
||||||
|
|
||||||
|
#define RAND_SEED 1337 /* seed for random generator */
|
||||||
|
|
||||||
static u32 rooms[4] = { ROOM_A, ROOM_B, ROOM_C, ROOM_D };
|
static u32 rooms[4] = { ROOM_A, ROOM_B, ROOM_C, ROOM_D };
|
||||||
|
static u64 result = -1;
|
||||||
typedef struct pos {
|
typedef struct pos {
|
||||||
u32 amp[4]; /* bitboards */
|
u32 amp[4]; /* bitboards */
|
||||||
int moves;
|
int moves;
|
||||||
u64 cost;
|
u64 cost;
|
||||||
u32 occupied;
|
u32 occupied;
|
||||||
u32 available; /* ALLOWED & ~occupied */
|
|
||||||
u32 final; /* 1 if final destination */
|
u32 final; /* 1 if final destination */
|
||||||
int ok; /* amphipods in correct place */
|
int ok; /* amphipods in correct place */
|
||||||
|
u64 zobrist; /* for zobrist_2() */
|
||||||
|
struct {
|
||||||
|
u32 from, to;
|
||||||
|
} move_list[64];
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
} pos_t;
|
} pos_t;
|
||||||
|
|
||||||
typedef struct hash {
|
typedef struct hash {
|
||||||
u32 amp[4]; /* bitboards */
|
u64 zobrist; /* zobrist hash */
|
||||||
u64 cost;
|
u32 amp[4];
|
||||||
//u32 count; /* collisions */
|
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
} hash_t;
|
} hash_t;
|
||||||
|
|
||||||
#define HASH_SIZE 4096
|
#define HASH_SIZE 131071
|
||||||
struct {
|
struct {
|
||||||
u32 count;
|
u32 count;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
@@ -74,7 +79,50 @@ pool_t *pool_hash;
|
|||||||
|
|
||||||
LIST_HEAD(pos_queue);
|
LIST_HEAD(pos_queue);
|
||||||
|
|
||||||
static void init_hash()
|
static u32 zobrist_table[24][4];
|
||||||
|
|
||||||
|
static void zobrist_init()
|
||||||
|
{
|
||||||
|
log_f(1, "zobrist init. RAND_MAX=%d seed=%d\n", RAND_MAX, RAND_SEED);
|
||||||
|
srand(RAND_SEED);
|
||||||
|
for (int i = 0; i < 24; ++i) {
|
||||||
|
for (int j = 0; j < 4; ++j) {
|
||||||
|
zobrist_table[i][j] = rand();
|
||||||
|
log(10, "%d ", 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=%lu -> %lu (amp=%d from=%u to=%u)\n",
|
||||||
|
zobrist, zobrist % HASH_SIZE, amp, from, to);
|
||||||
|
return zobrist;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hash_init()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < HASH_SIZE; ++i) {
|
for (int i = 0; i < HASH_SIZE; ++i) {
|
||||||
hasht[i].count = 0;
|
hasht[i].count = 0;
|
||||||
@@ -91,7 +139,6 @@ static hash_t *get_hash(pos_t *pos)
|
|||||||
return NULL;
|
return NULL;
|
||||||
for (int i = 0; i < 4; ++i)
|
for (int i = 0; i < 4; ++i)
|
||||||
new->amp[i] = pos->amp[i];
|
new->amp[i] = pos->amp[i];
|
||||||
new->cost = pos->cost;
|
|
||||||
INIT_LIST_HEAD(&new->list);
|
INIT_LIST_HEAD(&new->list);
|
||||||
return new;
|
return new;
|
||||||
}
|
}
|
||||||
@@ -112,51 +159,43 @@ static void hash_stats()
|
|||||||
min, max, ncollisions, ncollisions / HASH_SIZE);
|
min, max, ncollisions, ncollisions / HASH_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* hash function from:
|
static u64 hash(pos_t *pos, int amp, u32 from, u32 to)
|
||||||
* http://www.isthe.com/chongo/tech/comp/fnv/
|
|
||||||
*/
|
|
||||||
#define FNV_offset_basis 2166136261
|
|
||||||
#define FNV_prime 16777619
|
|
||||||
|
|
||||||
static hash_t *hash(pos_t *pos)
|
|
||||||
{
|
{
|
||||||
hash_t *cur;
|
hash_t *cur;
|
||||||
u32 val = FNV_offset_basis;
|
u64 zobrist;
|
||||||
|
u32 val;
|
||||||
|
|
||||||
/* we use the 2 first chars of the amps 32, this should be enough
|
/* we use the 2 first chars of the amps 32, this should be enough
|
||||||
* to avoid most collisions
|
* to avoid most collisions
|
||||||
*/
|
*/
|
||||||
for (int i = 0; i < 4; ++i) {
|
zobrist = zobrist_2(pos, amp, from, to);
|
||||||
uchar *input = (uchar *) &pos->amp[i];
|
val = zobrist % HASH_SIZE;
|
||||||
for (int c = 0; c < 2; ++c) {
|
log_f(1, "zobrist=%lu->%u, count=%d\n", zobrist, val, hasht[val].count);
|
||||||
val ^= input[c];
|
|
||||||
val *= FNV_prime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//val ^= pos->cost;
|
|
||||||
log_f(1, "hash=%u", val);
|
|
||||||
val %= HASH_SIZE;
|
|
||||||
log_f(1, " ->%u, count=%d\n", val, hasht[val].count);
|
|
||||||
list_for_each_entry(cur, &hasht[val].list, list) {
|
list_for_each_entry(cur, &hasht[val].list, list) {
|
||||||
if (pos->amp[0] == cur->amp[0] &&
|
if (zobrist == cur->zobrist) {
|
||||||
pos->amp[1] == cur->amp[1] &&
|
u32 amp_tmp[4];
|
||||||
pos->amp[2] == cur->amp[2] &&
|
for (int i = 0; i < 4; ++i) {
|
||||||
pos->amp[3] == cur->amp[3]) {
|
amp_tmp[i] = pos->amp[i];
|
||||||
if (pos->cost >= cur->cost) {
|
|
||||||
log(1, "collision, worse cost.\n");
|
|
||||||
return NULL;
|
|
||||||
} else {
|
|
||||||
log(1, "collision, better cost.\n");
|
|
||||||
cur->cost = pos->cost; /* adjust better solution */
|
|
||||||
return cur;
|
|
||||||
}
|
}
|
||||||
|
amp_tmp[amp] ^= BIT(from);
|
||||||
|
amp_tmp[amp] |= BIT(to);
|
||||||
|
if (amp_tmp[0] == cur->amp[0] &&
|
||||||
|
amp_tmp[1] == cur->amp[1] &&
|
||||||
|
amp_tmp[2] == cur->amp[2] &&
|
||||||
|
amp_tmp[3] == cur->amp[3])
|
||||||
|
return 0;
|
||||||
|
log(1, "zobrist collision for different positions\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hasht[val].count++;
|
hasht[val].count++;
|
||||||
log(1, "adding hash count=%u\n", hasht[val].count);
|
log(1, "adding hash count=%u\n", hasht[val].count);
|
||||||
cur = get_hash(pos);
|
cur = get_hash(pos);
|
||||||
|
cur->zobrist = zobrist;
|
||||||
|
cur->amp[amp] ^= BIT(from);
|
||||||
|
cur->amp[amp] |= BIT(to);
|
||||||
|
|
||||||
list_add(&cur->list, &hasht[val].list);
|
list_add(&cur->list, &hasht[val].list);
|
||||||
return cur;
|
return zobrist;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -201,8 +240,6 @@ static s32 room_exit[4][2][6] = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#define BIT(c) (1 << (c))
|
|
||||||
|
|
||||||
static char *int2bin(u32 mask, char *ret)
|
static char *int2bin(u32 mask, char *ret)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 32; ++i) {
|
for (int i = 0; i < 32; ++i) {
|
||||||
@@ -305,7 +342,7 @@ static pos_t *get_pos(pos_t *from)
|
|||||||
if (!from) {
|
if (!from) {
|
||||||
new->amp[0] = new->amp[1] = new->amp[2] = new->amp[3] = 0;
|
new->amp[0] = new->amp[1] = new->amp[2] = new->amp[3] = 0;
|
||||||
new->moves = new->ok = 0;
|
new->moves = new->ok = 0;
|
||||||
new->cost = new->occupied = new->available = 0;
|
new->cost = new->occupied = 0;
|
||||||
} else {
|
} else {
|
||||||
*new = *from;
|
*new = *from;
|
||||||
}
|
}
|
||||||
@@ -501,23 +538,30 @@ static pos_t *newmove(pos_t *pos, amphipod_t amp, u32 from, u32 to)
|
|||||||
int rows = popcount32(pos->amp[0]);
|
int rows = popcount32(pos->amp[0]);
|
||||||
move_t *move = &moves[from][to];
|
move_t *move = &moves[from][to];
|
||||||
pos_t *newpos;
|
pos_t *newpos;
|
||||||
hash_t *collision;
|
u64 collision;
|
||||||
|
|
||||||
log_f(1, "rows=%d amp=%c from=%s to=%s dist=%u ok=%d cost=%lu\n",
|
log_f(1, "rows=%d amp=%c from=%s to=%s dist=%u ok=%d cost=%lu\n",
|
||||||
rows, amp + 'A',
|
rows, amp + 'A',
|
||||||
cells[from], cells[to],
|
cells[from], cells[to],
|
||||||
move->dist, pos->ok, move->dist * cost[amp]);
|
move->dist, pos->ok, move->dist * cost[amp]);
|
||||||
|
|
||||||
collision = hash(pos);
|
if (pos->ok < 0) {
|
||||||
if (!collision) {
|
collision = hash(pos, amp, from, to);
|
||||||
log(1, "collision, skipping move :\n");
|
if (!collision) {
|
||||||
burrow_print(pos);
|
log(1, "collision, skipping move :\n");
|
||||||
// free_pos(newpos);
|
pos->amp[amp] ^= BIT(from);
|
||||||
return NULL;
|
pos->amp[amp] |= BIT(to);
|
||||||
|
burrow_print(pos);
|
||||||
|
pos->amp[amp] ^= BIT(to);
|
||||||
|
pos->amp[amp] |= BIT(from);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!(newpos = get_pos(pos)))
|
if (!(newpos = get_pos(pos)))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
newpos->move_list[newpos->moves].from = from;
|
||||||
|
newpos->move_list[newpos->moves].to = to;
|
||||||
newpos->amp[amp] ^= BIT(from);
|
newpos->amp[amp] ^= BIT(from);
|
||||||
newpos->amp[amp] |= BIT(to);
|
newpos->amp[amp] |= BIT(to);
|
||||||
newpos->occupied ^= BIT(from);
|
newpos->occupied ^= BIT(from);
|
||||||
@@ -525,7 +569,6 @@ static pos_t *newmove(pos_t *pos, amphipod_t amp, u32 from, u32 to)
|
|||||||
newpos->moves++;
|
newpos->moves++;
|
||||||
newpos->cost += move->dist * cost[amp];
|
newpos->cost += move->dist * cost[amp];
|
||||||
|
|
||||||
|
|
||||||
if (to >= A1) { /* final destination */
|
if (to >= A1) { /* final destination */
|
||||||
newpos->ok++;
|
newpos->ok++;
|
||||||
newpos->final |= BIT(to);
|
newpos->final |= BIT(to);
|
||||||
@@ -535,6 +578,15 @@ static pos_t *newmove(pos_t *pos, amphipod_t amp, u32 from, u32 to)
|
|||||||
log(1, "found solution! cost=%lu\n", newpos->cost);
|
log(1, "found solution! cost=%lu\n", newpos->cost);
|
||||||
burrow_print(newpos);
|
burrow_print(newpos);
|
||||||
free_pos(newpos);
|
free_pos(newpos);
|
||||||
|
if (newpos->cost < result) {
|
||||||
|
result = newpos->cost;
|
||||||
|
log(1, "New best=%lu moves=%u List:", result, newpos->moves);
|
||||||
|
for (int i = 0; i < newpos->moves; ++i) {
|
||||||
|
log(1, " %s-%s", cells[newpos->move_list[i].from],
|
||||||
|
cells[newpos->move_list[i].to]);
|
||||||
|
}
|
||||||
|
log(1, "\n");
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -661,7 +713,6 @@ static pos_t *read_input(int part)
|
|||||||
bit = 8 + adjline - 2;
|
bit = 8 + adjline - 2;
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
int amp = buf[i * 2 + 3] - 'A';
|
int amp = buf[i * 2 + 3] - 'A';
|
||||||
//printf("bit = %lu char = %c\n", bit, amp + 'A');
|
|
||||||
pos->amp[amp] |= BIT(bit);
|
pos->amp[amp] |= BIT(bit);
|
||||||
log(3, "setting bit %d to %c\n", bit, amp + 'A');
|
log(3, "setting bit %d to %c\n", bit, amp + 'A');
|
||||||
bit += 4;
|
bit += 4;
|
||||||
@@ -671,7 +722,6 @@ static pos_t *read_input(int part)
|
|||||||
adjline++;
|
adjline++;
|
||||||
}
|
}
|
||||||
pos->occupied = get_occupancy(pos);
|
pos->occupied = get_occupancy(pos);
|
||||||
pos->available = ALLOWED & ~pos->occupied;
|
|
||||||
free(buf);
|
free(buf);
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
@@ -716,11 +766,14 @@ int main(int ac, char **av)
|
|||||||
|
|
||||||
pool_pos = pool_create("pos", 1024, sizeof(pos_t));
|
pool_pos = pool_create("pos", 1024, sizeof(pos_t));
|
||||||
pool_hash = pool_create("hash", 1024, sizeof(hash_t));
|
pool_hash = pool_create("hash", 1024, sizeof(hash_t));
|
||||||
init_hash();
|
|
||||||
|
|
||||||
|
zobrist_init();
|
||||||
|
hash_init();
|
||||||
init_moves();
|
init_moves();
|
||||||
print_moves(moves);
|
print_moves(moves);
|
||||||
pos = read_input(part);
|
pos = read_input(part);
|
||||||
|
|
||||||
|
zobrist_1(pos);
|
||||||
push_pos(pos);
|
push_pos(pos);
|
||||||
/*
|
/*
|
||||||
for (int i = 0; i < 4; ++ i) {
|
for (int i = 0; i < 4; ++ i) {
|
||||||
|
Reference in New Issue
Block a user