-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday09.c
179 lines (150 loc) · 5.44 KB
/
day09.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
// AOC 2015 solution in C
// @chadsy
// Copyright (C) 2021 Chad Royal
// MIT License http://opensource.org/licenses/MIT
//
// Day 9
//
// The one about plotting the shortest course to visit some new delivery locations.
//
// This appears to be a classic traveling salesperson problem...right down to
// "must visit each location exactly once." So back to the algorithm books I go...
// Just use a naive solution to find the distance of all the permutations, and
// then pick the shortest one.
//
// I started down one path, which we'll call "Cleverly make a first-order struct
// out of everything and implement the algorithm (mostly) like a collection of
// graphs." After a couple of seg faults that showed up as `Bus error: 10` I
// decided to reboot my thinking and use a table for the distances, starting
// points on one axis and finishes on the other. This was cleaner and simpler,
// obvs, with the caveat that there's no smarts on the algorithm. It's O(n!) for
// sure.
//
// Part 2 is finding the longest distance. Santa is showing off. Refactored the
// recursive permutation walker to use a comparator function pointer. Also, as
// seems to be common for me, macros and global variables.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <stdbool.h>
char *trim(char *str) {
char *p = str + (strlen(str) - 1);
while (isspace(*p)) {
*p-- = '\0';
}
return str;
}
#define MAX_CITIES 20
typedef char hash_t;
int city_count = 0;
char *cities[MAX_CITIES];
#define add_city(c) city_count; cities[city_count++] = strdup(c)
#define city_from_hash(h) (cities[h % city_count])
hash_t hash_from_city(char *city) {
for (int i = 0; i < city_count; i++) {
if (strcmp(city, cities[i]) == 0)
return (hash_t)i;
}
return -1;
}
hash_t safe_add_city(char *city) {
hash_t hash = 0;
if ((hash = hash_from_city(city)) == -1) {
hash = add_city(city);
}
return hash;
}
int distances[MAX_CITIES * MAX_CITIES];
#define get_distance(from, to) *(distances + from + (to * MAX_CITIES))
#define set_distance(from, to, d) *(distances + from + (to * MAX_CITIES)) = d
void add_distance(char *str) {
int distance;
char from[40], to[40];
if (sscanf(str, "%s to %s = %d", from, to, &distance) < 3)
fprintf(stderr, "error: cannot parse '%s'\n", str);
hash_t f = safe_add_city(from);
hash_t t = safe_add_city(to);
set_distance(f, t, distance);
set_distance(t, f, distance);
}
hash_t final_journey[MAX_CITIES];
int shortest_distance = 1000000;
int longest_distance = 0;
#define swap(a, b) { hash_t tmp; tmp = a; a = b; b = tmp; }
int calculate_distance(hash_t *cities, int end) {
int distance = 0;
for (int i = 0; i < end; i++) {
distance += get_distance(cities[i], cities[i + 1]);
}
return distance;
}
void explore_journey(hash_t *cities, int start, int end, bool (*compare)(int dist)) {
if (start == end) {
// see if the distance is shortest, if so then...
int dist = calculate_distance(cities, end);
if (compare(dist)) {
memcpy(final_journey, cities, (end + 1) * sizeof(hash_t));
}
}
else {
for (int i = start; i <= end; i++) {
swap(cities[i], cities[start]);
explore_journey(cities, start + 1, end, compare);
swap(cities[i], cities[start]);
}
}
}
bool shortest_comparator(int dist) {
if (dist < shortest_distance) {
shortest_distance = dist;
return true;
}
return false;
}
bool longest_comparator(int dist) {
if (dist > longest_distance) {
longest_distance = dist;
return true;
}
return false;
}
int main(int argc, char **argv) {
FILE *input = stdin;
char arg[128];
while (fgets(arg, sizeof(arg) - 1, input)) {
trim(arg);
add_distance(arg);
}
// dump the distance table
printf(" ");
for (int city = 0; city < city_count; city++)
printf("- %-13s", city_from_hash(city));
printf("\n");
for (int r = 0; r < city_count; r++) {
printf("%-13s - ", city_from_hash(r));
for (int c = 0; c < city_count; c++) {
char dist[20];
sprintf(dist, "%d", get_distance(r,c));
printf("%-15s", dist);
}
printf("\n");
}
// setup a working journey buffer for the permutations to be explored
hash_t *working_journey = calloc(city_count, sizeof(hash_t));
for (int i = 0; i < city_count; i++)
working_journey[i] = i;
// explore the permutations, with the comparator looking for the shortest
explore_journey(working_journey, 0, city_count - 1, shortest_comparator);
// Dump the final shortest journey
printf("\nshortest distance %d: %s", shortest_distance, city_from_hash(final_journey[0]));
for (int city = 1; city < city_count; city++)
printf(" -%d- %s", get_distance(final_journey[city - 1], final_journey[city]), city_from_hash(final_journey[city]));
// explore the permutations, with the comparator looking for the longest
explore_journey(working_journey, 0, city_count - 1, longest_comparator);
// Dump the final longest journey
printf("\nlongest distance %d: %s", longest_distance, city_from_hash(final_journey[0]));
for (int city = 1; city < city_count; city++)
printf(" -%d- %s", get_distance(final_journey[city - 1], final_journey[city]), city_from_hash(final_journey[city]));
return 0;
}