Browse Source

Added day 14 solution

dhasenan 3 years ago
parent
commit
a10ab3134d
1 changed files with 300 additions and 0 deletions
  1. 300
    0
      source/advent/day14.d

+ 300
- 0
source/advent/day14.d View File

@@ -0,0 +1,300 @@
1
+module advent.day14;
2
+
3
+import std.stdio;
4
+import std.math;
5
+import std.exception;
6
+import std.algorithm, std.string, std.conv;
7
+import std.range;
8
+import std.typecons;
9
+import advent.core;
10
+import datefmt;
11
+
12
+Advent getAdvent() { return new Cocoa; }
13
+
14
+class Cocoa : Advent
15
+{
16
+    override string part1(string data)
17
+    {
18
+        auto d = data.strip.to!size_t + 10;
19
+        ubyte[] buf;
20
+        // In case we go over, don't realloc
21
+        buf.reserve(d + 10);
22
+        buf ~= 3;
23
+        buf ~= 7;
24
+        size_t a = 0, b = 1;
25
+        while (buf.length < d)
26
+        {
27
+            ubyte sum = cast(ubyte)(buf[a] + buf[b]);
28
+            assert(sum <= 18);
29
+            if (sum < 10)
30
+            {
31
+                buf ~= sum;
32
+            }
33
+            else
34
+            {
35
+                buf ~= sum / 10;
36
+                buf ~= sum % 10;
37
+            }
38
+            a = (a + buf[a] + 1) % buf.length;
39
+            b = (b + buf[b] + 1) % buf.length;
40
+        }
41
+        char[10] r;
42
+        foreach (i, u; buf[$-10 .. $]) { r[i] = cast(char)(u + '0'); }
43
+        return r[].idup;
44
+    }
45
+
46
+    override string part2(string data)
47
+    {
48
+        ubyte[] search = data.strip.map!(x => cast(ubyte)(x - '0')).array;
49
+        ubyte[] buf;
50
+        bool checkSolution()
51
+        {
52
+            if (buf[$-1] == search[$-1] && buf.length >= search.length)
53
+            {
54
+                if (buf[$-search.length .. $] == search[])
55
+                {
56
+                    return true;
57
+                }
58
+            }
59
+            return false;
60
+        }
61
+        buf.reserve(10_000_000);
62
+        buf ~= 3;
63
+        buf ~= 7;
64
+        size_t a = 0, b = 1;
65
+        foreach (i; 0..1<<25)
66
+        {
67
+            ubyte sum = cast(ubyte)(buf[a] + buf[b]);
68
+            assert(sum <= 18);
69
+            if (sum < 10)
70
+            {
71
+                buf ~= sum;
72
+            }
73
+            else
74
+            {
75
+                buf ~= sum / 10;
76
+                if (checkSolution) break;
77
+                buf ~= sum % 10;
78
+            }
79
+            if (checkSolution) break;
80
+            a = (a + buf[a] + 1) % buf.length;
81
+            b = (b + buf[b] + 1) % buf.length;
82
+        }
83
+        if (!checkSolution) return "failed";
84
+        return (buf.length - search.length).to!string;
85
+    }
86
+}
87
+
88
+struct Point
89
+{
90
+    int x, y;
91
+
92
+    Point opBinary(string op: "+")(Point other)
93
+    {
94
+        return Point(x + other.x, y + other.y);
95
+    }
96
+
97
+    int opCmp(Point other) const
98
+    {
99
+        auto a = x - other.x;
100
+        if (a == 0) a = y - other.y;
101
+        return a;
102
+    }
103
+
104
+    string toString() { return "%s,%s".format(x, y); }
105
+}
106
+
107
+struct Segment
108
+{
109
+    char c;
110
+}
111
+
112
+class Track
113
+{
114
+    Segment[Point] segments;
115
+    Cart[] carts;
116
+
117
+    void advance()
118
+    {
119
+        foreach (cart; carts)
120
+        {
121
+            cart.advance;
122
+        }
123
+        sort(carts);
124
+    }
125
+
126
+    Nullable!Point collision()
127
+    {
128
+        foreach (cart; carts)
129
+        {
130
+            if (cart.crashed) return nullable(cart.position);
131
+        }
132
+        return Nullable!Point.init;
133
+    }
134
+
135
+    void removeCollisions()
136
+    {
137
+        Cart[] next;
138
+        foreach (i, v; carts)
139
+        {
140
+            if (v.crashed) break;
141
+            next = carts[0..i + 1];
142
+        }
143
+        if (next.length != carts.length)
144
+            writefln("removed %s carts out of %s", (carts.length - next.length), carts.length);
145
+        carts = next;
146
+    }
147
+}
148
+
149
+enum Dir : Point
150
+{
151
+    north = Point(0, -1), south = Point(0, 1), east = Point(1, 0), west = Point(-1, 0)
152
+}
153
+
154
+class Cart
155
+{
156
+    static int count = 0;
157
+    Track track;
158
+    Point position;
159
+    Dir direction;
160
+    TurnDir nextTurn = TurnDir.left;
161
+    bool crashed;
162
+    int id;
163
+
164
+    this(Track t, Point p, char c)
165
+    {
166
+        id = count++;
167
+        track = t;
168
+        position = p;
169
+        switch (c)
170
+        {
171
+            case '^': direction = Dir.north; break;
172
+            case 'v': direction = Dir.south; break;
173
+            case '<': direction = Dir.west; break;
174
+            case '>': direction = Dir.east; break;
175
+            default: assert(false);
176
+        }
177
+        writefln("at %s %s is going %s", position, c, direction);
178
+    }
179
+
180
+    override int opCmp(Object o)
181
+    {
182
+        auto other = cast(Cart) o;
183
+        if (other is null) return -1;
184
+        if (crashed && !other.crashed) return 1;
185
+        if (!crashed && other.crashed) return -1;
186
+        return position.opCmp(other.position);
187
+    }
188
+
189
+    void advance()
190
+    {
191
+        position = position + cast(Point)direction;
192
+        foreach (cart; track.carts)
193
+        {
194
+            if (cart is this) continue;
195
+            if (cart.position == position)
196
+            {
197
+                writefln("cart %s crashed with cart %s at %s", id, cart.id, position);
198
+                this.crashed = true;
199
+                cart.crashed = true;
200
+            }
201
+        }
202
+        auto s = track.segments[position];
203
+        if (s.c == '/') with (Dir)
204
+        {
205
+            if (direction == north) direction = east;
206
+            else if (direction == south) direction = west;
207
+            else if (direction == east) direction = north;
208
+            else if (direction == west) direction = south;
209
+        }
210
+        else if (s.c == '\\') with (Dir)
211
+        {
212
+            if (direction == north) direction = west;
213
+            else if (direction == south) direction = east;
214
+            else if (direction == east) direction = south;
215
+            else if (direction == west) direction = north;
216
+        }
217
+        else if (s.c == '+')
218
+        {
219
+            final switch (nextTurn) with (TurnDir)
220
+            {
221
+                case left: nextTurn = straight; direction = direction.turnLeft; break;
222
+                case straight: nextTurn = right; break;
223
+                case right: nextTurn = left; direction = direction.turnRight; break;
224
+            }
225
+        }
226
+        else if (s.c == '|')
227
+        {
228
+            enforce(direction == Dir.north || direction == Dir.south);
229
+        }
230
+        else if (s.c == '-')
231
+        {
232
+            enforce(direction == Dir.east || direction == Dir.west);
233
+        }
234
+        else
235
+        {
236
+            enforce(false, "unrecognized tile " ~ s.c);
237
+        }
238
+    }
239
+}
240
+enum TurnDir
241
+{
242
+    left, right, straight
243
+}
244
+Dir turnLeft(Dir d)
245
+{
246
+    with (Dir)
247
+    {
248
+        if (d == north) return west;
249
+        if (d == west) return south;
250
+        if (d == south) return east;
251
+        if (d == east) return north;
252
+        assert(false);
253
+    }
254
+}
255
+Dir turnRight(Dir d)
256
+{
257
+    with (Dir)
258
+    {
259
+        if (d == north) return east;
260
+        if (d == west) return north;
261
+        if (d == south) return west;
262
+        if (d == east) return south;
263
+        assert(false);
264
+    }
265
+}
266
+
267
+Track parse(string data)
268
+{
269
+    Track track = new Track;
270
+    foreach (y, line; data.splitter('\n').enumerate!int)
271
+    {
272
+        foreach (x, c; line.enumerate!int)
273
+        {
274
+            auto p = Point(x, y);
275
+            switch (c)
276
+            {
277
+                case '+':
278
+                case '-':
279
+                case '|':
280
+                case '\\':
281
+                case '/':
282
+                    track.segments[p] = Segment(cast(char)c);
283
+                    break;
284
+                case 'v':
285
+                case '^':
286
+                    track.segments[p] = Segment('|');
287
+                    track.carts ~= new Cart(track, p, cast(char)c);
288
+                    break;
289
+                case '<':
290
+                case '>':
291
+                    track.segments[p] = Segment('-');
292
+                    track.carts ~= new Cart(track, p, cast(char)c);
293
+                    break;
294
+                default:
295
+                    break;
296
+            }
297
+        }
298
+    }
299
+    return track;
300
+}