Files
advent-of-code/2022/day09/aoc-c.c

201 lines
4.4 KiB
C

/* aoc-c.c: Advent of Code 2022, day 9
*
* Copyright (C) 2022 Bruno Raoult ("br")
* Licensed under the GNU General Public License v3.0 or later.
* Some rights reserved. See COPYING.
*
* You should have received a copy of the GNU General Public License along with this
* program. If not, see <https://www.gnu.org/licenses/gpl-3.0-standalone.html>.
*
* SPDX-License-Identifier: GPL-3.0-or-later <https://spdx.org/licenses/GPL-3.0-or-later.html>
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aoc.h"
#include "br.h"
#include "debug.h"
#include "pool.h"
#include "list.h"
#include "hashtable.h"
#include "pjwhash-inline.h"
#define HBITS 12 /* 12 bits: 4096 buckets */
typedef struct pos {
int x;
int y;
} pos_t;
typedef struct visited {
uint hash;
pos_t coord;
short nvisits;
struct hlist_node hlist;
} visited_t;
enum dir {
D = 'D',
L = 'L',
R = 'R',
U = 'U'
};
typedef struct move {
enum dir dir;
int nmoves;
struct list_head list;
} move_t;
DEFINE_HASHTABLE(h_visited, HBITS);
LIST_HEAD(moves);
static pool_t *pool_visited;
static pool_t *pool_move;
/**
* find_visited - find entry in an hashtable bucket
*/
static visited_t *find_visited(struct hlist_head *bucket, uint hash, pos_t *pos)
{
visited_t *cur;
hlist_for_each_entry(cur, bucket, hlist)
if (cur->hash == hash && cur->coord.x == pos->x && cur->coord.y == pos->y)
return cur;
return NULL;
}
static visited_t *add_visited(pos_t *pos, uint hash, uint bucket)
{
visited_t *new = pool_get(pool_visited);
new->hash = hash;
new->coord.x = pos->x;
new->coord.y = pos->y;
hlist_add_head(&new->hlist, &h_visited[bucket]);
return new;
}
static int add_visited_maybe(pos_t *pos)
{
uint hash = pjwhash(pos, sizeof (*pos));
uint bucket = hash_32(hash, HBITS);
if (! find_visited(h_visited + bucket, hash, pos)) {
add_visited(pos, hash, bucket);
return 1;
}
return 0;
}
static int move_tails(pos_t *pos, int ntails)
{
int i;
pos_t *cur, *next;
int didmove = 1;
for (i = 0; i < ntails && didmove; ++i) {
didmove = 0;
pos_t diff;
cur = pos + i;
next = cur + 1;
diff.x = cur->x - next->x;
diff.y = cur->y - next->y;
if (abs(diff.x) > 1 || abs(diff.y) > 1) { /* do move */
if (diff.x)
next->x += diff.x / abs(diff.x);
if (diff.y)
next->y += diff.y / abs(diff.y);
didmove = 1;
}
}
return didmove? add_visited_maybe(pos+ntails): 0;
}
static void move_head(pos_t *pos, move_t *move)
{
log_f(3, "(%d,%d,%c) -> ", pos->x, pos->y, move->dir);
switch (move->dir) {
case U:
pos->y++;
break;
case R:
pos->x++;
break;
case D:
pos->y--;
break;
case L:
pos->x--;
break;
default:
log(3, "ERR=%x ", move->dir);
}
log_f(3, "(%d,%d)\n", pos->x, pos->y);
}
static pos_t *part1(int *ntails)
{
static pos_t nodes[] = { [0 ... 1] = {.x = 0, .y = 0}};
*ntails = ARRAY_SIZE(nodes) - 1;
return nodes;
}
static pos_t *part2(int *ntails)
{
static pos_t nodes[] = { [0 ... 9] = {.x = 0, .y = 0}};
*ntails = ARRAY_SIZE(nodes) - 1;
return nodes;
}
static int solve(int part)
{
int ntails;
pos_t *nodes = part == 1? part1(&ntails): part2(&ntails);
move_t *move;
int res = 1; /* for (0,0) */
add_visited_maybe( &(pos_t) {0, 0});
list_for_each_entry(move, &moves, list) {
for (int i = 0; i < move->nmoves; ++i) {
move_head(nodes, move);
res += move_tails(nodes, ntails);
}
}
return res;
}
static int parse()
{
char dir;
int nmoves;
int count = 0;
move_t *move;
while (scanf(" %c %d", &dir, &nmoves) == 2) {
move = pool_get(pool_move);
move->dir = dir;
move->nmoves = nmoves;
list_add_tail(&move->list, &moves);
count++;
}
return count;
}
int main(int ac, char **av)
{
int part = parseargs(ac, av);
pool_visited = pool_create("visited", 128, sizeof(visited_t));
pool_move = pool_create("dirs", 128, sizeof(move_t));
parse();
printf("%s: res=%d\n", *av, solve(part));
pool_destroy(pool_visited);
pool_destroy(pool_move);
exit(0);
}