10 void debug(const char *fmt
, ...) {
12 if (getenv("DEBUG")) {
14 vfprintf(stderr
, fmt
, ap
);
19 #define LIMIT 33 /* Grid size */
20 #define MAX 30 /* Initial units */
22 static uint8_t map
[LIMIT
][LIMIT
+ 1];
23 static uint8_t distance
[LIMIT
][LIMIT
];
24 static uint8_t from
[LIMIT
][LIMIT
];
27 char type
; /* E, G, or X */
28 uint8_t x
; /* coordinate on map */
34 static int sorter(const void *one
, const void *two
) {
35 const struct unit
*a
= one
;
36 const struct unit
*b
= two
;
37 if (a
->type
!= 'X' && b
->type
== 'X')
39 if (a
->type
== 'X' && b
->type
!= 'X')
52 /* Return character type of enemy */
53 static char enemy(struct unit
*u
) {
54 assert(u
->type
!= 'X');
55 return "EG"[u
->type
== 'E'];
58 /* Return bitmap of spots where c is found: 1 up, 2 left, 4 right, 8 down */
59 static int nextto(int x
, int y
, char c
) {
61 assert(x
&& x
< LIMIT
&& y
&& y
< LIMIT
);
62 if (map
[y
- 1][x
] == c
)
64 if (map
[y
][x
- 1] == c
)
66 if (map
[y
][x
+ 1] == c
)
68 if (map
[y
+ 1][x
] == c
)
73 /* Check for a best candidate */
74 static int check(int old
, int x
, int y
) {
75 if (old
== 0 || y
< (old
& 0xff) ||
76 (y
== (old
& 0xff) && (x
<< 8 < (old
& 0xff00))))
77 return (from
[y
][x
] << 16) | (x
<< 8) | y
;
81 /* Return (dir << 16) | (x << 8) | y of the current position or best '.'
82 position that neighbors @c, else -1 */
83 static int locate(int startx
, int starty
, char c
) {
87 if (nextto(startx
, starty
, c
))
88 return (startx
<< 8) | starty
;
89 memset(distance
, -1, sizeof(distance
));
90 memset(from
, 0, sizeof(from
));
91 i
= distance
[starty
][startx
] = 1;
92 from
[starty
- 1][startx
] = 1;
93 from
[starty
][startx
- 1] = 2;
94 from
[starty
][startx
+ 1] = 4;
95 from
[starty
+ 1][startx
] = 8;
96 while (!best
&& found
) {
98 for (y
= 1; y
< LIMIT
- 1; y
++)
99 for (x
= 1; x
< LIMIT
- 1; x
++)
100 if (distance
[y
][x
] == i
) {
101 if (map
[y
- 1][x
] == '.' && distance
[y
- 1][x
] > i
) {
102 from
[y
- 1][x
] |= from
[y
][x
];
103 if (nextto(x
, y
- 1, c
))
104 best
= check(best
, x
, y
- 1);
106 found
= (distance
[y
- 1][x
] = i
+ 1);
108 if (map
[y
][x
- 1] == '.' && distance
[y
][x
- 1] > i
) {
109 from
[y
][x
- 1] |= from
[y
][x
];
110 if (nextto(x
- 1, y
, c
))
111 best
= check(best
, x
- 1, y
);
113 found
= (distance
[y
][x
- 1] = i
+ 1);
115 if (map
[y
][x
+ 1] == '.' && distance
[y
][x
+ 1] > i
) {
116 from
[y
][x
+ 1] |= from
[y
][x
];
117 if (nextto(x
+ 1, y
, c
))
118 best
= check(best
, x
+ 1, y
);
120 found
= (distance
[y
][x
+ 1] = i
+ 1);
122 if (map
[y
+ 1][x
] == '.' && distance
[y
+ 1][x
] > i
) {
123 from
[y
+ 1][x
] |= from
[y
][x
];
124 if (nextto(x
, y
+ 1, c
))
125 best
= check(best
, x
, y
+ 1);
127 found
= (distance
[y
+ 1][x
] = i
+ 1);
132 return best
? best
: -1;
135 static void dump(int tick
, int rows
, int nunits
) {
137 debug("\ntick %d:\n", tick
);
138 for (i
= 0; (getenv("DEBUG") ?: "1")[0] == '2' && i
< rows
; i
++)
139 debug("%s\n", map
[i
]);
140 debug("%d units at:\n", nunits
);
141 for (i
= 0; i
< nunits
; i
++) {
142 if (units
[i
].type
== 'X')
143 debug("%d: dead\n", units
[i
].id
);
145 assert(map
[units
[i
].y
][units
[i
].x
] == units
[i
].type
);
146 debug("%d: %d,%d %c with hp %d attack %d\n", units
[i
].id
, units
[i
].x
,
147 units
[i
].y
, units
[i
].type
, units
[i
].hp
, units
[i
].attack
);
152 int main(int argc
, char **argv
) {
153 size_t len
= 0, count
= 0;
163 /* Part 1 - read in map */
165 attack
= atoi(argv
[2]);
166 if (argc
> 1 && !(stdin
= freopen(argv
[1], "r", stdin
))) {
171 while (getline(&line
, &len
, stdin
) >= 0) {
172 p
= strspn(line
, "#.GE") + line
;
173 if (*p
!= '\n' || p
- line
> LIMIT
) {
174 fprintf(stderr
, "recompile with larger limit\n");
177 p
= strcspn(line
, "GE\n") + line
;
179 units
[nunits
].id
= nunits
+ 1;
180 units
[nunits
].type
= *p
;
181 units
[nunits
].x
= p
- line
;
182 units
[nunits
].y
= count
;
183 units
[nunits
].hp
= 200;
184 units
[nunits
].attack
= *p
== 'G' ? 3 : attack
;
187 p
= strcspn(p
, "GE\n") + p
;
189 memcpy(map
[count
++], line
, p
- line
);
190 if (count
== LIMIT
) {
191 fprintf(stderr
, "recompile with larger limit\n");
195 printf("read %zu lines, found %d units\n", count
, nunits
);
196 qsort(units
, nunits
, sizeof *units
, sorter
);
197 dump(ticks
, count
, nunits
);
199 /* Part 2 - iterate until one side wins */
201 for (i
= 0; i
< nunits
; i
++) {
203 if (units
[i
].type
== 'X')
205 for (j
= 0; j
< nunits
; j
++) {
208 if (units
[j
].type
== enemy(&units
[i
]))
212 printf("combat ended during tick %d\n", ticks
);
217 int xy
= locate(units
[i
].x
, units
[i
].y
, enemy(&units
[i
]));
219 debug("No move possible for unit %d\n", units
[i
].id
);
220 else if ((xy
& 0xffff) != ((units
[i
].x
<< 8) | units
[i
].y
)) {
221 debug("Moving unit %d closer to %d,%d", units
[i
].id
,
222 xy
>> 8 & 0xff, xy
& 0xff);
224 map
[units
[i
].y
][units
[i
].x
] = '.';
227 else if (xy
& 0x20000)
229 else if (xy
& 0x40000)
233 debug(" via %d,%d\n", units
[i
].x
, units
[i
].y
);
234 map
[units
[i
].y
][units
[i
].x
] = units
[i
].type
;
236 debug("Unit %d already in range\n", units
[i
].id
);
238 /* Part 2c: Attack */
239 if (nextto(units
[i
].x
, units
[i
].y
, enemy(&units
[i
]))) {
242 for (j
= 0; j
< nunits
; j
++) {
243 if (units
[j
].type
!= enemy(&units
[i
]))
245 if (abs(units
[i
].x
- units
[j
].x
) +
246 abs(units
[i
].y
- units
[j
].y
) == 1) {
247 if (units
[j
].hp
< hp
) {
250 } else if (units
[j
].hp
== hp
&&
251 units
[j
].y
* LIMIT
+ units
[j
].x
<
252 units
[best
].y
* LIMIT
+ units
[best
].x
) {
258 units
[best
].hp
-= units
[i
].attack
;
259 debug("unit %d attacking %d to %d\n", units
[i
].id
, units
[best
].id
,
261 if (units
[best
].hp
<= 0) {
262 debug("%c unit %d killed\n", units
[best
].type
, units
[best
].id
);
263 if (units
[best
].type
== 'E') {
264 printf("attack %d too low: elf casualty in tick %d\n",
268 units
[best
].type
= 'X';
270 map
[units
[best
].y
][units
[best
].x
] = '.';
275 qsort(units
, nunits
, sizeof *units
, sorter
);
276 dump(++ticks
, count
, nunits
);
279 qsort(units
, nunits
, sizeof *units
, sorter
);
280 dump(ticks
+ 1, count
, nunits
);
281 for (i
= 0; i
< nunits
; i
++) {
284 printf("%c wins, remaining hitpoints %d, score %d\n", units
->type
, sum
,