2020 day 22 part 2 (C, 1st version)
This commit is contained in:
@@ -1,4 +0,0 @@
|
||||
#! /bin/bash
|
||||
PATH=.:$PATH
|
||||
IN="$1"
|
||||
time { ex1.bash < $IN; ex2.bash < $IN; } 2>&1
|
@@ -1,5 +0,0 @@
|
||||
#! /bin/bash
|
||||
PATH=.:$PATH
|
||||
IN="$1"
|
||||
echo IN=$IN
|
||||
time for i in ex1-c ex2-c; do "$i" < "$IN"; done 2>&1
|
@@ -1,46 +1,212 @@
|
||||
/* aoc-c.c: Advent2020, day 22, part 1
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "hashtable.h"
|
||||
#include "list.h"
|
||||
#include "pool.h"
|
||||
#include "debug.h"
|
||||
|
||||
struct card {
|
||||
int card;
|
||||
struct list_head list;
|
||||
};
|
||||
typedef struct card {
|
||||
u8 card; /* card value */
|
||||
struct list_head list; /* list of cards */
|
||||
} card_t;
|
||||
|
||||
struct player {
|
||||
int ncards;
|
||||
struct list_head head;
|
||||
} players[2];
|
||||
typedef struct player {
|
||||
int ncards; /* player cards # */
|
||||
struct list_head head; /* head of cards list */
|
||||
} player_t;
|
||||
|
||||
/* zobrist hash used to find duplicate positions
|
||||
*/
|
||||
typedef struct hash {
|
||||
u32 zobrist;
|
||||
struct list_head players[2];
|
||||
struct hlist_node hlist;
|
||||
} hash_t;
|
||||
|
||||
#define HBITS 10 /* 10 bits: hash size is 1024 */
|
||||
#define CARDS 50
|
||||
|
||||
pool_t *pool_cards;
|
||||
pool_t *pool_hash;
|
||||
static u32 zobrist_table[2][CARDS][CARDS];
|
||||
|
||||
static void print_cards()
|
||||
static void zobrist_init()
|
||||
{
|
||||
for (int i = 0; i < 2; ++i)
|
||||
for (int j = 0; j < 50; ++j)
|
||||
for (int k = 0; k < 50; ++k)
|
||||
zobrist_table[i][j][k] = rand();
|
||||
}
|
||||
|
||||
static u32 zobrist(player_t *players)
|
||||
{
|
||||
u32 zobrist = 0;
|
||||
card_t *card;
|
||||
|
||||
for (int p = 0; p < 2; ++p) {
|
||||
int pos = 0;
|
||||
list_for_each_entry(card, &players[p].head, list) {
|
||||
zobrist ^= zobrist_table[p][pos][card->card - 1];
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
return zobrist;
|
||||
}
|
||||
|
||||
static __always_inline u32 hash(u32 h)
|
||||
{
|
||||
return hash_32(h, HBITS);
|
||||
}
|
||||
|
||||
static void print_cards(player_t *players)
|
||||
{
|
||||
struct card *card;
|
||||
|
||||
for (int player = 0; player < 2; ++player) {
|
||||
log(2, "player %d (%d cards): ", player + 1, players[player].ncards);
|
||||
int c = 0;
|
||||
log(2, "Player %d's deck: ", player + 1);
|
||||
list_for_each_entry(card, &players[player].head, list) {
|
||||
log(2, "%d ", card->card);
|
||||
if (c++)
|
||||
log(2, ",");
|
||||
log(2, " %d", card->card);
|
||||
}
|
||||
log(2, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void parse()
|
||||
static int equal_decks(hash_t *hasht, player_t *new)
|
||||
{
|
||||
//int i = 0;
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
card_t *c1 = list_first_entry_or_null(&hasht->players[i], card_t, list);
|
||||
card_t *c2 = list_first_entry_or_null(&new[i].head, card_t, list);
|
||||
log_f(3, "p=%d c1=%p c2=%p", i + 1, c1, c2);
|
||||
|
||||
if (!c1 || !c2) {
|
||||
/* one list is empty and one is not */
|
||||
//if ((!c1 && c2) || (c1 && !c2)) {
|
||||
log(3, "NULL\n");
|
||||
return 0;
|
||||
}
|
||||
log_f(3, "\nplayer=%d ", i + 1);
|
||||
|
||||
while (!list_entry_is_head(c1, &hasht->players[i], list) &&
|
||||
!list_entry_is_head(c2, &new[i].head, list) &&
|
||||
c1->card == c2->card) {
|
||||
log(3, "c1=%d c2=%d ", c1->card, c2->card);
|
||||
if (c1->card != c2->card) {
|
||||
log(3, "\n");
|
||||
return 0;
|
||||
}
|
||||
c1 = list_next_entry(c1, list);
|
||||
c2 = list_next_entry(c2, list);
|
||||
}
|
||||
log(3, "\nd1=%p c1=%p/%d\nd2=%p c2=%p/%d ",
|
||||
&hasht[i], c1, list_entry_is_head(c1, &hasht->players[i], list),
|
||||
&new[i], c2, list_entry_is_head(c2, &new[i].head, list));
|
||||
//log(3, "\nZOBI\n");
|
||||
if (!list_entry_is_head(c1, &hasht->players[i], list) ||
|
||||
!list_entry_is_head(c2, &new[i].head, list))
|
||||
return 0;
|
||||
log(3, "\n");
|
||||
//log(3, "\nZOBI\n");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static hash_t *create_hash(player_t *players, u32 h)
|
||||
{
|
||||
struct card *card;
|
||||
hash_t *hash = pool_get(pool_hash);
|
||||
INIT_HLIST_NODE(&hash->hlist);
|
||||
hash->zobrist = h;
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
log_f(4, "player %d: ", i + 1);
|
||||
INIT_LIST_HEAD(&hash->players[i]);
|
||||
list_for_each_entry(card, &players[i].head, list) {
|
||||
struct card *new = pool_get(pool_cards);
|
||||
new->card = card->card;
|
||||
list_add_tail(&new->list, &hash->players[i]);
|
||||
log(4, "%d ", card->card);
|
||||
}
|
||||
log(4, "\n");
|
||||
}
|
||||
log_f(3, "hash=%p p1-next=%p p2-next=%p\n", hash,
|
||||
hash->players[0].next, hash->players[1].next);
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
log_f(4, "duped player %d: ", i + 1);
|
||||
list_for_each_entry(card, &hash->players[i], list) {
|
||||
log(4, "%d ", card->card);
|
||||
}
|
||||
log(4, "\n");
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
static player_t *create_subgame(player_t *from, player_t *to)
|
||||
{
|
||||
struct card *card;
|
||||
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
int n = 0, ncards;
|
||||
|
||||
to[i].ncards = from[i].ncards - 1;
|
||||
INIT_LIST_HEAD(&to[i].head);
|
||||
list_for_each_entry(card, &from[i].head, list) {
|
||||
if (!n) {
|
||||
to[i].ncards = ncards = card->card;
|
||||
} else {
|
||||
struct card *new = pool_get(pool_cards);
|
||||
new->card = card->card;
|
||||
list_add_tail(&new->list, &to[i].head);
|
||||
log(4, "%d ", card->card);
|
||||
if (!--ncards)
|
||||
break;
|
||||
}
|
||||
n++;
|
||||
}
|
||||
}
|
||||
return to;
|
||||
}
|
||||
|
||||
/**
|
||||
* find_deck - find deck in an hashtable bucket
|
||||
*/
|
||||
static hash_t *find_deck(struct hlist_head *hasht, player_t *players)
|
||||
{
|
||||
hash_t *cur;
|
||||
u32 z = zobrist(players);
|
||||
u32 h = hash(z);
|
||||
log_f(3, "zobrist = %u/%u ", z, h);
|
||||
hlist_for_each_entry(cur, hasht + h, hlist) {
|
||||
log(3, "[%u]\n", cur->zobrist);
|
||||
if (cur->zobrist == z && equal_decks(cur, players)) {
|
||||
// && equal_decks(&cur->players[1], &players[1].head)) {
|
||||
log_f(3, "found\n");
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
log_f(3, "not found\n");
|
||||
cur = create_hash(players, z);
|
||||
hlist_add_head(&cur->hlist, &hasht[h]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static player_t *parse(player_t *players)
|
||||
{
|
||||
size_t alloc;
|
||||
ssize_t len;
|
||||
char *buf = NULL;
|
||||
int player = -1;
|
||||
int player = 0;
|
||||
struct card *card;
|
||||
|
||||
INIT_LIST_HEAD(&players[0].head);
|
||||
@@ -48,12 +214,11 @@ static void parse()
|
||||
players[0].ncards = players[1].ncards = 0;
|
||||
while ((len = getline(&buf, &alloc, stdin)) > 0) {
|
||||
buf[--len] = 0;
|
||||
if (len == 0)
|
||||
continue;
|
||||
if (*buf == 'P') {
|
||||
if (len == 0) {
|
||||
player++;
|
||||
continue;
|
||||
} else if (isdigit(*buf)) { /* card */
|
||||
}
|
||||
if (isdigit(*buf)) { /* card */
|
||||
card = pool_get(pool_cards);
|
||||
card->card = atoi(buf);
|
||||
players[player].ncards++;
|
||||
@@ -61,6 +226,7 @@ static void parse()
|
||||
}
|
||||
}
|
||||
free(buf);
|
||||
return players;
|
||||
}
|
||||
|
||||
static int usage(char *prg)
|
||||
@@ -69,46 +235,93 @@ static int usage(char *prg)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static long part1()
|
||||
static void winmove(player_t *winner, player_t *loser)
|
||||
{
|
||||
struct card *c1, *c2;
|
||||
int round = 0, mult = 1;
|
||||
long res = 0;
|
||||
card_t *win, *lose;
|
||||
|
||||
win = list_first_entry(&winner->head, struct card, list);
|
||||
lose = list_first_entry(&loser->head, struct card, list);
|
||||
list_move_tail(&win->list, &winner->head);
|
||||
list_move_tail(&lose->list, &winner->head);
|
||||
loser->ncards--;
|
||||
winner->ncards++;
|
||||
}
|
||||
|
||||
static long part1(player_t *players)
|
||||
{
|
||||
int round = 0, winner = 0;
|
||||
|
||||
while (players[0].ncards > 0 && players[1].ncards > 0) {
|
||||
c1 = list_first_entry_or_null(&players[0].head, struct card, list);
|
||||
c2 = list_first_entry_or_null(&players[1].head, struct card, list);
|
||||
if (c1->card > c2->card) {
|
||||
list_move_tail(&c1->list, &players[0].head);
|
||||
list_move_tail(&c2->list, &players[0].head);
|
||||
players[1].ncards--;
|
||||
players[0].ncards++;
|
||||
} else {
|
||||
list_move_tail(&c2->list, &players[1].head);
|
||||
list_move_tail(&c1->list, &players[1].head);
|
||||
players[0].ncards--;
|
||||
players[1].ncards++;
|
||||
}
|
||||
winner = 0;
|
||||
/* we can use list_first_entry() macro, as both lists are not empty */
|
||||
int val1 = list_first_entry(&players[0].head, struct card, list)->card;
|
||||
int val2 = list_first_entry(&players[1].head, struct card, list)->card;
|
||||
if (val2 > val1)
|
||||
winner = 1;
|
||||
winmove(players + winner, players + 1 - winner);
|
||||
round++;
|
||||
log(2, "\nafter round %d\n", round);
|
||||
print_cards();
|
||||
}
|
||||
list_for_each_entry_reverse(c1,
|
||||
players[0].ncards? &players[0].head: &players[1].head,
|
||||
list) {
|
||||
res += c1->card * mult++;
|
||||
};
|
||||
return res;
|
||||
|
||||
return winner;
|
||||
}
|
||||
|
||||
static long part2()
|
||||
static long part2(player_t *players)
|
||||
{
|
||||
return 2;
|
||||
int round = 1, winner = 0, game;
|
||||
static int maxgame = 0;
|
||||
DEFINE_HASHTABLE(hasht_deck, HBITS); /* htable for dup decks */
|
||||
|
||||
log(2, "=== Game %d ===\n", game = ++maxgame);
|
||||
while (players[0].ncards > 0 && players[1].ncards > 0) {
|
||||
int val1, val2;
|
||||
winner = 0;
|
||||
|
||||
log(2, "\n-- Round %d (Game %d) --\n", round, game);
|
||||
print_cards(players);
|
||||
|
||||
if (find_deck(hasht_deck, players)) {
|
||||
log(3, "dup found\n");
|
||||
goto end; // return winner;
|
||||
} else {
|
||||
log(3, "dup not found\n");
|
||||
}
|
||||
|
||||
/* we can use list_first_entry() macro, as both lists are not empty */
|
||||
val1 = list_first_entry(&players[0].head, struct card, list)->card;
|
||||
val2 = list_first_entry(&players[1].head, struct card, list)->card;
|
||||
log(2, "Player 1 plays: %d\n", val1);
|
||||
log(2, "Player 2 plays: %d\n", val2);
|
||||
|
||||
if (players[0].ncards > val1 && players[1].ncards > val2) {
|
||||
player_t sub[2];
|
||||
log(2, "Playing a sub-game to determine the winner...\n\n");
|
||||
winner = part2(create_subgame(players, sub));
|
||||
log(2, "\n...anyway, back to game %d\n", game);
|
||||
} else {
|
||||
if (val2 > val1)
|
||||
winner = 1;
|
||||
}
|
||||
winmove(players + winner, players + 1 - winner);
|
||||
log(2, "Player %d wins round %d of game %d!\n", winner + 1, round, game);
|
||||
|
||||
round++;
|
||||
}
|
||||
end:
|
||||
log(2, "The winner of game %d is player %d!\n", game, winner + 1);
|
||||
/* cleanup decks */
|
||||
card_t *card, *tmp;
|
||||
if (game != 1) {
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
list_for_each_entry_safe(card, tmp, &players[i].head, list) {
|
||||
list_del(&card->list);
|
||||
pool_add(pool_cards, card);
|
||||
}
|
||||
}
|
||||
}
|
||||
return winner;
|
||||
}
|
||||
|
||||
int main(ac, av)
|
||||
int ac;
|
||||
char **av;
|
||||
int main(int ac, char **av)
|
||||
{
|
||||
int opt, part = 1;
|
||||
|
||||
@@ -117,18 +330,35 @@ int main(ac, av)
|
||||
case 'd':
|
||||
debug_level_set(atoi(optarg));
|
||||
break;
|
||||
case 'p': /* 1 or 2 */
|
||||
case 'p': /* 1 or 2 */
|
||||
part = atoi(optarg);
|
||||
if (part < 1 || part > 2)
|
||||
default:
|
||||
default:
|
||||
return usage(*av);
|
||||
}
|
||||
}
|
||||
|
||||
pool_cards = pool_create("cards", 128, sizeof(struct card));
|
||||
pool_hash = pool_create("hash", 128, sizeof(struct hash));
|
||||
zobrist_init();
|
||||
player_t players[2];
|
||||
parse(players);
|
||||
|
||||
parse();
|
||||
print_cards();
|
||||
printf("%s : res=%ld\n", *av, part == 1? part1(): part2());
|
||||
exit (0);
|
||||
if (part == 1)
|
||||
part1(players);
|
||||
else
|
||||
part2(players);
|
||||
|
||||
/* we don't need to check for winner, as one list is empty */
|
||||
log(2, "\n== Post-game results ==\n");
|
||||
print_cards(players);
|
||||
card_t *card;
|
||||
long res = 0, mult = 1;
|
||||
list_for_each_entry_reverse(card, &players[0].head, list)
|
||||
res += card->card * mult++;
|
||||
list_for_each_entry_reverse(card, &players[1].head, list)
|
||||
res += card->card * mult++;
|
||||
|
||||
printf("%s : res=%ld\n", *av, res);
|
||||
exit(0);
|
||||
}
|
||||
|
Reference in New Issue
Block a user