-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlib.nim
131 lines (112 loc) · 3.41 KB
/
lib.nim
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
import std/sets, std/strutils, std/sequtils
type
Direction = enum
left, right, up, down, none
Command = object
direction: Direction
steps: int
Position = tuple[x: int, y: int]
Positions = tuple
head: Position
tail: Position
proc printField(knots: seq[Position], height: int, width: int) =
let emptyLine = toSeq(0..width).mapIt(".")
var lines = toSeq(0..height).mapIt(emptyLine)
for idx, knot in knots:
let (col, row) = knot
try:
lines[height - row][col] = if idx == 0: "H" else: $idx
except IndexDefect:
discard
for line in lines:
echo line.join("")
echo ""
proc parseInput(filename: string): seq[Command] =
result = readFile(filename)
.splitLines()
.filterIt(it != "")
.mapIt(it.split(" "))
.mapIt(Command(
direction: case it[0]
of "L": left
of "R": right
of "U": up
of "D": down
else: raise newException(ValueError,
"Invalid direction: " & it[0]),
steps: parseInt(it[1])))
proc step(positions: Positions, direction: Direction): Positions =
result = positions
case direction
of left: dec(result.head.x)
of right: inc(result.head.x)
of up: inc(result.head.y)
of down: dec(result.head.y)
else: discard
var delta_x = result.head.x - result.tail.x
var delta_y = result.head.y - result.tail.y
assert abs(delta_x) + abs(delta_y) <= 4
# horizontal movement
if (delta_x == 0):
if (delta_y == 2):
inc(result.tail.y)
return
elif (delta_y == -2):
dec(result.tail.y)
return
elif (delta_y == 0):
if (delta_x == 2):
inc(result.tail.x)
return
elif (delta_x == -2):
dec(result.tail.x)
return
# diagonal movement
if (delta_x == -2 and delta_y == -1) or
(delta_x == -1 and delta_y == -2) or
(delta_x == -2 and delta_y == -2):
dec(result.tail.x)
dec(result.tail.y)
elif (delta_x == -2 and delta_y == 1) or
(delta_x == -1 and delta_y == 2) or
(delta_x == -2 and delta_y == 2):
dec(result.tail.x)
inc(result.tail.y)
elif (delta_x == 1 and delta_y == -2) or
(delta_x == 2 and delta_y == -1) or
(delta_x == 2 and delta_y == -2):
inc(result.tail.x)
dec(result.tail.y)
elif (delta_x == 1 and delta_y == 2) or
(delta_x == 2 and delta_y == 1) or
(delta_x == 2 and delta_y == 2):
inc(result.tail.x)
inc(result.tail.y)
proc simulateRope(commands: seq[Command], numKnots: int):
(HashSet[Position], seq[Position]) =
var knots: seq[Position] = @[]
for i in 0..<numKnots:
knots.add((0, 0))
var tailPositions = initHashSet[Position]()
tailPositions.incl((0, 0))
for command in commands:
for s in 0 ..< command.steps:
for i in 0..<numKnots - 1:
var positions = (head: knots[i], tail: knots[i + 1])
if i == 0:
positions = step(positions, command.direction)
else:
positions = step(positions, none)
knots[i] = positions.head
knots[i + 1] = positions.tail
# printField(knots, 6, 8)
tailPositions.incl(knots[numKnots - 1])
result = (tailPositions, knots)
proc part1*(filename: string): int =
let commands = parseInput(filename)
let (tailPositions, _) = simulateRope(commands, 2)
result = tailPositions.len
proc part2*(filename: string): int =
let commands = parseInput(filename)
let (tailPositions, _) = simulateRope(commands, 10)
result = tailPositions.len