Browse Source

Day 15, finally.

master
Chris Smith 5 years ago
parent
commit
1f17fb35d9
3 changed files with 208 additions and 0 deletions
  1. 2
    0
      answers/15.txt
  2. 32
    0
      data/15.txt
  3. 174
    0
      day15.nim

+ 2
- 0
answers/15.txt View File

@@ -0,0 +1,2 @@
1
+195811
2
+69867

+ 32
- 0
data/15.txt View File

@@ -0,0 +1,32 @@
1
+################################
2
+####################..#..#######
3
+####################.......#####
4
+####################..##...###.#
5
+####################...........#
6
+###########G.....###..##.......#
7
+############..##.G###....G.....#
8
+############G..#..###.........##
9
+##########..G..#.####.#G.......#
10
+##########.#.....####..........#
11
+##########.G.......#....E...####
12
+##########GG................####
13
+########..G..G#####..........###
14
+######.......#######.....#.#####
15
+#####GG..G..#########....#..####
16
+#####.......#########.......####
17
+####....G...#########...###.####
18
+####...G....#########..E###.####
19
+##....G.....#########.G.##..#..#
20
+#.....#......#######E..##......#
21
+##...###......#####...###......#
22
+####..................#.....E###
23
+####.######..G......#.......####
24
+#######.........E.....###...####
25
+#######..............E####.#####
26
+##########..G........###########
27
+##########............##########
28
+###########..E....#.E.##########
29
+#########...............########
30
+#########..E......##.....#######
31
+#########.#####.E.###.....######
32
+################################

+ 174
- 0
day15.nim View File

@@ -0,0 +1,174 @@
1
+import algorithm, deques, sequtils, strutils
2
+
3
+type
4
+    Species = enum
5
+        spElf, spGoblin
6
+
7
+    Point = tuple[x,y: int]
8
+
9
+    Creep = ref object
10
+        pos: Point
11
+        hp,ap: int
12
+        species: Species
13
+
14
+proc cmpCreeps(x, y: Creep): int =
15
+    if x.pos.y == y.pos.y:
16
+        x.pos.x - y.pos.x
17
+    else:
18
+        x.pos.y - y.pos.y
19
+
20
+iterator moves(pos: Point): Point =
21
+    yield (pos.x, pos.y-1)
22
+    yield (pos.x-1, pos.y)
23
+    yield (pos.x+1, pos.y)
24
+    yield (pos.x, pos.y+1)
25
+
26
+proc highAt(bitmasks: seq[uint64], pos: Point): bool {.inline.} =
27
+    let mask = 1'u64 shl pos.x
28
+    (bitmasks[pos.y] and mask) == mask
29
+
30
+func setLowAt(bitmasks: var seq[uint64], pos: Point) {.inline.} =
31
+    bitmasks[pos.y] = bitmasks[pos.y] and not (1'u64 shl pos.x) 
32
+
33
+func setHighAt(bitmasks: var seq[uint64], pos: Point) {.inline.} =
34
+    bitmasks[pos.y] = bitmasks[pos.y] or (1'u64 shl pos.x) 
35
+
36
+proc inRange(targets: seq[uint64], pos: Point): bool =
37
+    for move in pos.moves:
38
+        if targets.highAt(move):
39
+            return true
40
+
41
+proc allDead(targets: seq[uint64]): bool =
42
+    for line in targets:
43
+        if line != 0:
44
+            return false
45
+    return true
46
+
47
+proc totalHp(creeps: seq[Creep]): int =
48
+    for creep in creeps:
49
+        if creep.hp > 0:
50
+            result += creep.hp
51
+
52
+proc step(targets: seq[uint64], passable: seq[uint64], pos: Point): tuple[valid: bool, step: Point] =
53
+    type Step = tuple[firstStep, pos: Point]
54
+    var
55
+        queue = initDeque[Step]()
56
+        allowed: seq[uint64]
57
+    
58
+    allowed.deepCopy(passable)
59
+
60
+    for firstStep in pos.moves:
61
+        if allowed.highAt(firstStep):
62
+            queue.addLast((firstStep, firstStep))
63
+            allowed.setLowAt(firstStep)
64
+    while queue.len > 0:
65
+        let lastStep = queue.popFirst()
66
+        for nextStep in lastStep.pos.moves:
67
+            if targets.highAt(nextStep):
68
+                return (true, lastStep.firstStep)
69
+            if allowed.highAt(nextStep):
70
+                queue.addLast((lastStep.firstStep, nextStep))
71
+                allowed.setLowAt(nextStep)
72
+
73
+let
74
+    input = readFile("data/15.txt").strip.splitlines
75
+
76
+var
77
+    maxx = int.low
78
+    creeps: seq[Creep]
79
+    passable: seq[uint64]
80
+    goblins: seq[uint64]
81
+    elves: seq[uint64]
82
+
83
+proc load(elfAttackPower: int = 3) =
84
+    maxx = int.low
85
+    creeps.setLen(0)
86
+    passable.setLen(0)
87
+    goblins.setLen(0)
88
+    elves.setLen(0)
89
+
90
+    for y, line in input:
91
+        var linePassable, lineGoblins, lineElves: uint64
92
+        for x, c in line:
93
+            maxx = max(x, maxx)
94
+            if c == 'E' or c == 'G':
95
+                var creep = new(Creep)
96
+                creep.pos = (x,y)
97
+                creep.hp = 200
98
+                if c == 'E':
99
+                    creep.species = spElf
100
+                    lineElves = lineElves or (1'u64 shl x)
101
+                    creep.ap = elfAttackPower
102
+                else:
103
+                    creep.species = spGoblin
104
+                    lineGoblins = lineGoblins or (1'u64 shl x)
105
+                    creep.ap = 3
106
+                creeps.add(creep)
107
+            elif c == '.':
108
+                linePassable = linePassable or (1'u64 shl x)
109
+        passable.add(linePassable)
110
+        goblins.add(lineGoblins)
111
+        elves.add(lineElves)
112
+
113
+proc run(elfAttackPower: int = 3, allowElvesToDie: bool = true): int =
114
+    load(elfAttackPower)
115
+    var runs: int
116
+    while true:
117
+        creeps.sort(cmpCreeps)
118
+        for creep in creeps:
119
+            if creep.hp <= 0:
120
+                continue
121
+
122
+            let targets = if creep.species == spGoblin: elves else: goblins
123
+            if targets.allDead:
124
+                return runs * creeps.totalHp
125
+            if not targets.inRange(creep.pos):
126
+                let move = targets.step(passable, creep.pos)
127
+                if move.valid:
128
+                    if creep.species == spGoblin:
129
+                        goblins.setLowAt(creep.pos)
130
+                        goblins.setHighAt(move.step)
131
+                    else:
132
+                        elves.setLowAt(creep.pos)
133
+                        elves.setHighAt(move.step)
134
+                    passable.setHighAt(creep.pos)
135
+                    passable.setLowAt(move.step)
136
+                    creep.pos = move.step
137
+            if targets.inRange(creep.pos):
138
+                let spaces = toSeq(creep.pos.moves)
139
+                var
140
+                    lowestHp = int.high
141
+                    bestCreep: Creep
142
+                for other in creeps:
143
+                    # Find an enemy that's alive
144
+                    if other.species != creep.species and other.hp > 0 and
145
+                        # ... That's in range
146
+                        other.pos in spaces and
147
+                        # ... With lower HP
148
+                        (other.hp < lowestHp or
149
+                        # ... Or the same HP and higher up
150
+                        (other.hp == lowestHp and (other.pos.y < bestCreep.pos.y or
151
+                        # ... Or the same HP and height but further left
152
+                        (other.pos.y == bestCreep.pos.y and other.pos.x < bestCreep.pos.x)))):
153
+                        lowestHp = other.hp
154
+                        bestCreep = other
155
+                bestCreep.hp -= creep.ap
156
+                if bestCreep.hp <= 0:
157
+                    if bestCreep.species == spGoblin:
158
+                        goblins.setLowAt(bestCreep.pos)
159
+                    else:
160
+                        elves.setLowAt(bestCreep.pos)
161
+                        if not allowElvesToDie:
162
+                            return -1
163
+                    passable.setHighAt(bestCreep.pos)
164
+        runs.inc
165
+
166
+echo run()
167
+
168
+var ap = 4
169
+while true:
170
+    let res = run(ap, false)
171
+    if res > -1:
172
+        echo res
173
+        break
174
+    ap.inc