advent2021

Advent of Code 2021 Solutions
git clone git://bsandro.tech/advent2021
Log | Files | Refs | README | LICENSE

puzzle.c (4804B)


      1 #define _DEFAULT_SOURCE
      2 
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <errno.h>
      6 #include <string.h>
      7 #include <strings.h>
      8 #include <assert.h>
      9 #include <ctype.h>
     10 #include <stdbool.h>
     11 #include <math.h>
     12 
     13 #include "util.h"
     14 
     15 #define STR_LEN 1024
     16 
     17 struct cache_t {
     18 	long p1, p2, p1s, p2s;
     19 	long long p1w;
     20 	long long p2w;
     21 	bool p1t;
     22 };
     23 
     24 struct result_t {
     25 	long long p1w;
     26 	long long p2w;
     27 };
     28 
     29 long long calc_result1(long p1, long p2);
     30 long long calc_result2(long p1, long p2);
     31 struct result_t roll_turn(struct array_t *cache, bool pp1t, long pp1, long pp2, long pp1s, long pp2s, long long p1w, long long p2w);
     32 struct cache_t * get_cache(struct array_t *cache, bool p1t, long p1, long p2, long p1s, long p2s);
     33 void set_cache(struct array_t *cache, bool p1t, long p1, long p2, long p1s, long p2s, long long p1w, long long p2w);
     34 
     35 void puzzle(const char *filename, long long *result1, long long *result2) {
     36 	FILE *infile = fopen(filename, "r");
     37 	if (infile == NULL) {
     38 		fprintf(stderr, "fopen() error: %s\n", strerror(errno));
     39 		return;
     40 	}
     41 
     42 	char buf[STR_LEN] = {0};
     43 	unsigned int line_num = 0;
     44 
     45 	long p1 = 0;
     46 	long p2 = 0;
     47 
     48 	while (fgets(buf, STR_LEN, infile) != NULL) {
     49 		int len = strlen(buf);
     50 		assert(len > 2);
     51 		if (line_num == 0) { // player 1
     52 			p1 = buf[len-2] - '0';
     53 		} else if (line_num == 1) { // player 2
     54 			p2 = buf[len-2] - '0';
     55 		}
     56 		++line_num;
     57 		bzero(buf, STR_LEN);
     58 	}
     59 
     60 	assert(p1 != 0 && p2 != 0);
     61 
     62 	*result1 = calc_result1(p1, p2);
     63 	*result2 = calc_result2(p1, p2);
     64 
     65 	// mutiny! ignoring feof/ferror.
     66 	fclose(infile);
     67 }
     68 
     69 long long calc_result1(long p1, long p2) {
     70 	long p1s = 0; // player 1 score
     71 	long p2s = 0; // player 2 score
     72 	long d = 1; // dice value
     73 	long rc = 0; // real rolls count
     74 
     75 	while (1) {
     76 		if (d == 1) {
     77 			p1 += 6;
     78 			d += 3;
     79 		} else if (d >= 98) {
     80 			if (d == 98) {
     81 				p1 += 297;
     82 				d = 1;
     83 			} else if (d == 99) {
     84 				p1 += 200;
     85 				d = 2;
     86 			} else if (d == 100) {
     87 				p1 += 103;
     88 				d = 3;
     89 			} else {
     90 				d = (d + 3) % 100;
     91 				p1 += d * 3 + 3;
     92 			}
     93 		} else {
     94 			p1 += d * 3 + 3;
     95 			d += 3;
     96 		}
     97 		p1 = p1 > 10 ? (p1 % 10) : p1;
     98 		p1 = p1 == 0 ? 10 : p1;
     99 		p1s += p1;
    100 		rc += 3;
    101 
    102 		if (p1s >= 1000) break;
    103 
    104 		if (d == 1) {
    105 			p2 += 6;
    106 			d += 3;
    107 		} else if (d >= 98) {
    108 			if (d == 98) {
    109 				p2 += 297;
    110 				d = 1;
    111 			} else if (d == 99) {
    112 				p2 += 200;
    113 				d = 2;
    114 			} else if (d == 100) {
    115 				p2 += 103;
    116 				d = 3;
    117 			} else {
    118 				d = (d + 3) % 100;
    119 				p2 += d * 3 + 3;
    120 			}
    121 		} else {
    122 			p2 += d * 3 + 3;
    123 			d += 3;
    124 		}
    125 		p2 = p2 > 10 ? (p2 % 10) : p2;
    126 		p2 = p2 == 0 ? 10 : p2;
    127 		p2s += p2;
    128 		rc += 3;
    129 
    130 		if (p2s >= 1000) break;
    131 	}
    132 
    133 	return fmin(p1s, p2s) * rc;
    134 }
    135 
    136 long long calc_result2(long p1, long p2) {
    137 	struct array_t cache = { .data = NULL };
    138 	array_init(&cache, sizeof(struct cache_t), 1000);
    139 	struct result_t result = roll_turn(&cache, true, p1, p2, 0, 0, 0, 0);
    140 	return fmaxl(result.p1w, result.p2w);
    141 }
    142 
    143 struct cache_t * get_cache(struct array_t *cache, bool p1t, long p1, long p2, long p1s, long p2s) {
    144 	struct cache_t *cache_data = cache->data;
    145 	for (size_t i = 0; i < cache->count; ++i) {
    146 		struct cache_t *c = &cache_data[i];
    147 		if (c->p1t == p1t && c->p1 == p1 && c->p2 == p2 && c->p1s == p1s && c->p2s == p2s) {
    148 			return c;
    149 		}
    150 	}
    151 	return NULL;
    152 }
    153 
    154 void set_cache(struct array_t *cache, bool p1t, long p1, long p2, long p1s, long p2s, long long p1w, long long p2w) {
    155 	if (cache->count >= cache->cap) array_expand(cache);
    156 	struct cache_t *cache_data = cache->data;
    157 
    158 	struct cache_t *c = get_cache(cache, p1t, p1, p2, p1s, p2s);
    159 	assert(c == NULL);
    160 	c = &cache_data[cache->count++];
    161 	c->p1t = p1t;
    162 	c->p1 = p1;
    163 	c->p2 = p2;
    164 	c->p1s = p1s;
    165 	c->p2s = p2s;
    166 	c->p1w = p1w;
    167 	c->p2w = p2w;
    168 }
    169 
    170 
    171 struct result_t roll_turn(struct array_t *cache, bool pp1t, long pp1, long pp2, long pp1s, long pp2s, long long p1w, long long p2w) {
    172 
    173 	struct result_t result = {0};
    174 	if (pp1s >= 21) {
    175 		result.p1w = 1;
    176 		return result;
    177 	}
    178 	if (pp2s >= 21) {
    179 		result.p2w = 1;
    180 		return result;
    181 	}
    182 
    183 	struct cache_t *c = get_cache(cache, pp1t, pp1, pp2, pp1s, pp2s);
    184 	if (c != NULL) {
    185 		result.p1w = c->p1w;
    186 		result.p2w = c->p2w;
    187 		return result;
    188 	}
    189 
    190 	for (int d1 = 1; d1 <= 3; ++d1) {
    191 	for (int d2 = 1; d2 <= 3; ++d2) {
    192 	for (int d3 = 1; d3 <= 3; ++d3) {
    193 
    194 		long dice = d1+d2+d3;
    195 		long p1 = pp1;
    196 		long p2 = pp2;
    197 		long p1s = pp1s;
    198 		long p2s = pp2s;
    199 		bool p1t = pp1t;
    200 
    201 		if (p1t) {
    202 			// player 1 turn
    203 			p1t = false;
    204 			p1 += dice;
    205 			p1 = p1 > 10 ? (p1 % 10) : p1;
    206 			p1 = (p1 == 0) ? 10 : p1;
    207 			p1s += p1;
    208 
    209 		} else {
    210 			// player 2 turn
    211 			p1t = true;
    212 			p2 += dice;
    213 			p2 = p2 > 10 ? (p2 % 10) : p2;
    214 			p2 = p2 == 0 ? 10 : p2;
    215 			p2s += p2;
    216 
    217 		}
    218 		struct result_t res = roll_turn(cache, p1t, p1, p2, p1s, p2s, p1w, p2w);
    219 		result.p1w += res.p1w;
    220 		result.p2w += res.p2w;
    221 	}
    222 	}
    223 	}
    224 	set_cache(cache, pp1t, pp1, pp2, pp1s, pp2s, result.p1w, result.p2w);
    225 	return result;
    226 }