-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path09.swift
129 lines (103 loc) · 3.02 KB
/
09.swift
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
import Foundation
guard let input = try? String(contentsOf: URL(fileURLWithPath: "09.txt")) else {
fatalError("File error")
}
typealias Coordinate = (Int, Int)
enum Direction: String {
case Up = "U"
case Down = "D"
case Left = "L"
case Right = "R"
func toCoordinate() -> Coordinate {
switch self {
case .Down: return (0, -1)
case .Up: return (0, 1)
case .Left: return (-1, 0)
case .Right: return (1, 0)
}
}
}
struct RuleSet {
struct Rule {
let direction: Direction
let distance: Int
}
let rules: [Rule]
init(_ list: String) {
let lines = list.components(separatedBy: .newlines)
self.rules = lines.map { line in
let parts = line.split(separator: " ")
return Rule(direction: Direction(rawValue: String(parts[0]))!, distance: Int(parts[1])!)
}
}
}
class Simulation {
var rope: [Coordinate]
init(ropeLength: Int) {
self.rope = Array(repeating: (0, 0), count: ropeLength)
}
var tail: Coordinate {
rope.last!
}
struct Position: Hashable {
let x: Int
let y: Int
}
var visited = Set<Position>()
func markAsVisited(_ coordinate: Coordinate) {
let (x, y) = coordinate
visited.insert(Position(x: x, y: y))
}
func moveHead(direction: Direction) {
let (xOffset, yOffset) = direction.toCoordinate()
let (x, y) = rope[0]
rope[0] = (x + xOffset, y + yOffset)
}
func moveTail(headIndex: Int, tailIndex: Int) {
let head = rope[headIndex]
let tail = rope[tailIndex]
if (head == tail) {
return
}
let (headX, headY) = head
let (tailX, tailY) = tail
let inSameColumn = headX == tailX
let inSameRow = headY == tailY
let xOffset = headX - tailX
let yOffset = headY - tailY
let isTouching = abs(xOffset) <= 1 && abs(yOffset) <= 1
if isTouching {
return
}
if inSameRow {
rope[tailIndex] = (tailX + xOffset.signum(), tailY)
} else if inSameColumn {
rope[tailIndex] = (tailX, tailY + yOffset.signum())
} else {
rope[tailIndex] = (tailX + xOffset.signum(), tailY + yOffset.signum())
}
}
func run(_ ruleSet: RuleSet) {
markAsVisited(tail)
for rule in ruleSet.rules {
for _ in 0..<rule.distance {
moveHead(direction: rule.direction)
for i in 1..<rope.count {
moveTail(headIndex: i - 1, tailIndex: i)
}
markAsVisited(tail)
}
}
}
}
let ruleSet = RuleSet(input)
let simulation1 = Simulation(ropeLength: 2)
simulation1.run(ruleSet)
let answer1 = simulation1.visited.count
print(answer1)
assert(answer1 == 6209)
let simulation2 = Simulation(ropeLength: 10)
simulation2.run(ruleSet)
let answer2 = simulation2.visited.count
print(answer2)
assert(answer2 == 2460)