-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathday16.nim
More file actions
142 lines (114 loc) · 4.02 KB
/
day16.nim
File metadata and controls
142 lines (114 loc) · 4.02 KB
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
import sequtils
import strscans
import strutils
import sugar
import tables
# Declare types
type
Range = tuple
min: int
max: int
TicketField = object
name: string
ranges: array[2, Range]
UnidentifiedTicketValues = seq[int]
Input = object
ticketFields: seq[TicketField]
myTicketValues: UnidentifiedTicketValues
nearbyTicketValues: seq[UnidentifiedTicketValues]
ParsingStage {.pure.} = enum
Fields
MyTicket
NearbyTickets
# Parse ticket field
proc parseField(line: string): TicketField =
discard line.scanf("$+: $i-$i or $i-$i", result.name, result.ranges[0].min,
result.ranges[0].max, result.ranges[1].min, result.ranges[1].max)
# Parse input file
proc parseInput(inputFile: string): Input =
var currentStage = ParsingStage.Fields
for line in lines inputFile:
case currentStage:
# Parse possible fields
of ParsingStage.Fields:
if line.len > 0:
result.ticketFields.add(parseField(line))
else:
currentStage = ParsingStage.MyTicket
# Parse my ticket
of ParsingStage.MyTicket:
if line.len == 0:
currentStage = ParsingStage.NearbyTickets
elif not line.startsWith("your"):
result.myTicketValues = line.split(",").map(parseInt)
# Parse nearby ticket
of ParsingStage.NearbyTickets:
if not line.startsWith("nearby"):
result.nearbyTicketValues.add(line.split(",").map(parseInt))
# Separates valid and invalid tickets and returns error rate
proc validateTickets(tickets: seq[UnidentifiedTicketValues],
validRanges: seq[TicketField],
validTickets, invalidTickets: var seq[UnidentifiedTicketValues]): int =
# Go ticket by ticker
for ticket in tickets:
var isValid = true
# Check field by field
for field in ticket:
block fieldChecking:
# Check if valid in any range
for validRange in validRanges:
if field in validRange.ranges[0].min .. validRange.ranges[0].max or
field in validRange.ranges[1].min .. validRange.ranges[1].max:
break fieldChecking
# If valid range not found, invalidate ticket and increase error rate
result += field
isValid = false
# Separate ticket
if isValid:
validTickets.add(ticket)
else:
invalidTickets.add(ticket)
# Parse input data
let inputFile = "resources/day16_input.txt"
var inputData = parseInput(inputFile)
# Check valid and invalid tickets
var validTickets, invalidTickets: seq[UnidentifiedTicketValues]
let errorRate = validateTickets(inputData.nearbyTicketValues,
inputData.ticketFields, validTickets, invalidTickets)
# Print results
echo "--- Part 1 Report ---"
echo "ErrorRate = " & $errorRate
## Part 2
# Identify each field
var possibleFields = newSeqWith(inputData.ticketFields.len,
inputData.ticketFields)
# Filter fields that allow values in valid nearby tickets
for ticket in validTickets:
for index, value in ticket:
possibleFields[index].keepIf(x =>
value in x.ranges[0].min .. x.ranges[0].max or
value in x.ranges[1].min .. x.ranges[1].max)
# Identify fields with just one possibility
var identifiedFields: Table[int, TicketField]
while true:
# Look for possitions with only allow one single field
let possibleFieldCount = possibleFields.map(x => len(x))
let trivialFieldIdx = possibleFieldCount.find(1)
if trivialFieldIdx != -1:
# Store identified field
let identifiedField = possibleFields[trivialFieldIdx][0]
identifiedFields[trivialFieldIdx] = identifiedField
# Remove from list of possible fields
for possibleFields in possibleFields.mitems:
possibleFields.keepIf(x => x.name != identifiedField.name)
else:
# No more fields can be identified
break
# Multiply values of my ticket of fields starting with "departre"
var accumulator = 1
for (possition, field) in toSeq(identifiedFields.pairs):
if field.name.startsWith("departure"):
accumulator *= inputData.myTicketValues[possition]
# Print results
echo "--- Part 2 Report ---"
echo "Product of fields = " & $accumulator