11 bool __attribute__((format(printf
, 1, 2)))
12 debug(const char *fmt
, ...) {
14 if (getenv("DEBUG")) {
16 vfprintf(stderr
, fmt
, ap
);
34 typedef struct group group
;
49 static group immune
[LIMIT
];
50 static group infect
[LIMIT
];
51 static group
*order
[LIMIT
* 2];
53 static int list(int round
) {
55 int count1
= 0, count2
= 0;
56 debug("\nBeginning round %d\n", round
);
58 for (i
= 0; i
< LIMIT
; i
++)
59 if (immune
[i
].units
> 0) {
60 immune
[i
].attacker
= NULL
;
61 immune
[i
].attacking
= NULL
;
62 debug("Group %d(%d) contains %d units, effective power %d\n",
63 immune
[i
].id
, immune
[i
].initiative
,
64 immune
[i
].units
, immune
[i
].units
* immune
[i
].attack
);
65 order
[count1
++] = &immune
[i
];
68 for (i
= 0; i
< LIMIT
; i
++)
69 if (infect
[i
].units
> 0) {
70 infect
[i
].attacker
= NULL
;
71 infect
[i
].attacking
= NULL
;
72 debug("Group %d(%d) contains %d units, effective power %d\n",
73 infect
[i
].id
, infect
[i
].initiative
,
74 infect
[i
].units
, infect
[i
].units
* infect
[i
].attack
);
75 order
[count1
+ count2
++] = &infect
[i
];
77 return count1
&& count2
? count1
+ count2
: 0;
80 static int sort_effective(const void *one
, const void *two
) {
81 const group
*a
= *(const group
**)one
;
82 const group
*b
= *(const group
**)two
;
85 if (b
->units
* b
->attack
- a
->units
* a
->attack
)
86 return b
->units
* b
->attack
- a
->units
* a
->attack
;
87 return b
->initiative
- a
->initiative
;
90 static int sort_initiative(const void *one
, const void *two
) {
91 const group
*a
= *(const group
**)one
;
92 const group
*b
= *(const group
**)two
;
95 return b
->initiative
- a
->initiative
;
98 static void __attribute__((noreturn
)) die(void) {
99 fprintf(stderr
, "unexpected input\n");
103 static const char *lookup(Type t
) {
105 case RADIATION
: return "radiation";
106 case COLD
: return "cold";
107 case FIRE
: return "fire";
108 case BLUDGEONING
: return "bludgeoning";
109 case SLASHING
: return "slashing";
115 static Type
decode(const char *s
) {
116 if (!strcmp(s
, "radiation"))
118 if (!strcmp(s
, "cold"))
120 if (!strcmp(s
, "fire"))
122 if (!strcmp(s
, "bludgeoning"))
124 if (!strcmp(s
, "slashing"))
129 static void parse(const char *line
, group
*g
, int boost
) {
133 if (sscanf(line
, "%d units each with %d hit points", &g
->units
,
136 if ((p
= strstr(line
, "immune to"))) {
137 p
+= strlen("immune to");
139 while (*p
== ',' || *p
== ' ')
145 g
->immune
|= 1 << decode(buf
);
148 if ((p
= strstr(line
, "weak to"))) {
149 p
+= strlen("weak to");
151 while (*p
== ',' || *p
== ' ')
157 g
->weak
|= 1 << decode(buf
);
160 p
= strstr(line
, "with an attack");
161 if (!p
|| sscanf(p
, "with an attack that does %d %20[a-z] damage at "
162 "initiative %d\n", &g
->attack
, buf
, &g
->initiative
) != 3)
164 g
->type
= decode(buf
);
166 if (debug("%s group %d: %d units, %d hit points, attack %d %s, "
167 "initiative %d\n", g
->infect
? "infect" : "immune", g
->id
,
168 g
->units
, g
->hp
, g
->attack
, lookup(g
->type
), g
->initiative
)) {
169 for (int i
= 0; i
< _MAX
; i
++) {
170 assert(!(g
->weak
& (1 << i
)) || !(g
->immune
& (1 << i
)));
171 debug(" %s %s,", lookup(i
), g
->weak
& (1 << i
) ? "weak" :
172 g
->immune
& (1 << i
) ? "immune" : "normal");
178 static void selection(int count
) {
188 for (i
= 0; i
< count
; i
++) {
192 enemies
= attacker
->infect
? immune
: infect
;
193 for (j
= 0; j
< LIMIT
; j
++) {
194 if (enemies
[j
].units
> 0 && !enemies
[j
].attacker
195 && !(enemies
[j
].immune
& (1 << attacker
->type
))) {
196 damage
= attacker
->attack
* attacker
->units
;
197 if (enemies
[j
].weak
& (1 << attacker
->type
))
199 debug("%s group %d(%d) would deal group %d(%d) %d damage\n",
200 attacker
->infect
? "infect" : "immune", attacker
->id
,
201 attacker
->initiative
, enemies
[j
].id
, enemies
[j
].initiative
,
204 (damage
== best
&& enemies
[j
].attack
* enemies
[j
].units
>
205 target
->attack
* target
->units
) ||
206 (damage
== best
&& enemies
[j
].attack
* enemies
[j
].units
==
207 target
->attack
* target
->units
208 && enemies
[j
].initiative
> target
->initiative
)) {
210 target
= &enemies
[j
];
214 if (target
!= &dummy
) {
215 attacker
->attacking
= target
;
216 target
->attacker
= attacker
;
221 static int attack(int count
) {
229 for (i
= 0; i
< count
; i
++) {
231 if (!attacker
->attacking
|| attacker
->units
<= 0)
233 damage
= attacker
->attack
* attacker
->units
;
234 if (attacker
->attacking
->weak
& (1 << attacker
->type
))
236 ret
+= killed
= damage
/ attacker
->attacking
->hp
;
237 debug("%s group %d(%d) attacks group %d(%d), killing %d of %d units\n",
238 attacker
->infect
? "infect" : "immune", attacker
->id
,
239 attacker
->initiative
, attacker
->attacking
->id
,
240 attacker
->attacking
->initiative
, killed
, attacker
->attacking
->units
);
241 attacker
->attacking
->units
-= killed
;
246 int main(int argc
, char **argv
) {
247 size_t len
= 0, count
= 0;
254 /* Part 1 - read data */
256 boost
= atoi(argv
[2]);
258 if (!(stdin
= freopen(argv
[1], "r", stdin
))) {
263 if (getline(&line
, &len
, stdin
) < 0 || (strcmp(line
, "Immune System:\n")))
265 while (getline(&line
, &len
, stdin
) > 0) {
268 if (count
>= LIMIT
) {
269 fprintf(stderr
, "recompile with larger LIMIT!\n");
272 immune
[count
].id
= count
+ 1;
273 parse(line
, &immune
[count
++], boost
);
275 printf("Read %zu immune lines\n", count
);
277 if (getline(&line
, &len
, stdin
) < 0 || (strcmp(line
, "Infection:\n")))
279 while (getline(&line
, &len
, stdin
) > 0) {
280 if (count
>= LIMIT
) {
281 fprintf(stderr
, "recompile with larger LIMIT!\n");
284 infect
[count
].infect
= true;
285 infect
[count
].id
= count
+ 1;
286 parse(line
, &infect
[count
++], 0);
288 printf("Read %zu infect lines\n", count
);
290 /* Part 2 - battle */
291 while ((count
= list(round
))) {
293 qsort(order
, count
, sizeof *order
, sort_effective
);
295 qsort(order
, count
, sizeof *order
, sort_initiative
);
296 if (!attack(count
)) {
297 printf("Stalemate after %d rounds\n", round
);
301 for (i
= 0; i
< LIMIT
; i
++) {
302 if (immune
[i
].units
> 0)
303 sum
-= immune
[i
].units
;
304 if (infect
[i
].units
> 0)
305 sum
+= infect
[i
].units
;
307 printf("After %d rounds, %d %s units remain\n", round
, abs(sum
),
308 sum
> 0 ? "infect" : "immune");