-
Notifications
You must be signed in to change notification settings - Fork 1
/
Day16.kt
160 lines (126 loc) · 6.63 KB
/
Day16.kt
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/*
* Copyright (c) 2018 by Todd Ginsberg
*/
/**
* Advent of Code 2018, Day 16 - Chronal Classification
*
* Problem Description: http://adventofcode.com/2018/day/16
* Blog Post/Commentary: https://todd.ginsberg.com/post/advent-of-code/2018/day16/
*/
package com.ginsberg.advent2018
typealias Registers = IntArray
typealias Instruction = IntArray
typealias Operation = (Registers, Instruction) -> Registers
class Day16(
part1RawInput: List<String>,
part2RawInput: List<String>
) {
private val part1Input: List<Input> = parsePart1Input(part1RawInput)
private val part2Input: List<Instruction> = parsePart2Input(part2RawInput)
fun solvePart1(): Int =
part1Input.count { countMatchingOperations(it) >= 3 }
fun solvePart2(): Int {
// Create all possible matches.
val functionToOpCodes: MutableMap<Operation, MutableSet<Int>> = part1Input.flatMap { input ->
Operations.operations.mapNotNull { operation ->
if (operation(input.registersBefore, input.instruction).contentEquals(input.expectedRegisters)) {
input.id to operation
} else {
null
}
}
}
.groupBy({ it.second }, { it.first })
.mapValues { (_, list) -> list.toMutableSet() }
.toMutableMap()
val operations = mutableMapOf<Int, Operation>()
while (functionToOpCodes.isNotEmpty()) {
// Find all that have only one outcome, map them into the operations map and remove them from contention,
functionToOpCodes
.filter { (_, codes) -> codes.size == 1 }
.map { Pair(it.key, it.value.first()) }
.forEach { (op, code) ->
operations[code] = op
functionToOpCodes.remove(op)
functionToOpCodes.forEach { (_, thoseFuncs) -> thoseFuncs.remove(code) }
}
functionToOpCodes.entries.removeIf { (_, value) -> value.isEmpty() }
}
// Run the code and return register 0
return part2Input.fold(intArrayOf(0, 0, 0, 0)) { registers, instruction ->
operations[instruction[0]]!!.invoke(registers, instruction)
}.first()
}
private fun countMatchingOperations(input: Input): Int =
Operations.operations.count { it(input.registersBefore, input.instruction).contentEquals(input.expectedRegisters) }
private companion object {
val digitsRegex = """[^0-9 ]""".toRegex()
fun parsePart1Input(rawInput: List<String>): List<Input> =
rawInput.chunked(4) { chunk ->
Input(
chunk[0].toIntArray(),
chunk[1].toIntArray(),
chunk[2].toIntArray()
)
}
fun parsePart2Input(rawInput: List<String>): List<Instruction> =
rawInput.map { it.toIntArray() }
private fun String.toIntArray(): IntArray =
this.replace(digitsRegex, "").trim().split(" ").map { it.toInt() }.toIntArray()
}
private class Input(val registersBefore: Registers, val instruction: Instruction, val expectedRegisters: Registers) {
val id = instruction[0]
}
private object Operations {
val operations: List<Operation> = listOf(
::addr,
::addi,
::mulr,
::muli,
::banr,
::bani,
::borr,
::bori,
::setr,
::seti,
::gtir,
::gtri,
::gtrr,
::eqir,
::eqri,
::eqrr
)
fun addr(registers: Registers, instruction: Instruction): Registers =
registers.copyOf().apply { this[instruction[3]] = registers[instruction[1]] + registers[instruction[2]] }
fun addi(registers: Registers, instruction: Instruction): Registers =
registers.copyOf().apply { this[instruction[3]] = registers[instruction[1]] + instruction[2] }
fun mulr(registers: Registers, instruction: Instruction): Registers =
registers.copyOf().apply { this[instruction[3]] = registers[instruction[1]] * registers[instruction[2]] }
fun muli(registers: Registers, instruction: Instruction): Registers =
registers.copyOf().apply { this[instruction[3]] = registers[instruction[1]] * instruction[2] }
fun banr(registers: Registers, instruction: Instruction): Registers =
registers.copyOf().apply { this[instruction[3]] = registers[instruction[1]] and registers[instruction[2]] }
fun bani(registers: Registers, instruction: Instruction): Registers =
registers.copyOf().apply { this[instruction[3]] = registers[instruction[1]] and instruction[2] }
fun borr(registers: Registers, instruction: Instruction): Registers =
registers.copyOf().apply { this[instruction[3]] = registers[instruction[1]] or registers[instruction[2]] }
fun bori(registers: Registers, instruction: Instruction): Registers =
registers.copyOf().apply { this[instruction[3]] = registers[instruction[1]] or instruction[2] }
fun setr(registers: Registers, instruction: Instruction): Registers =
registers.copyOf().apply { this[instruction[3]] = registers[instruction[1]] }
fun seti(registers: Registers, instruction: Instruction): Registers =
registers.copyOf().apply { this[instruction[3]] = instruction[1] }
fun gtir(registers: Registers, instruction: Instruction): Registers =
registers.copyOf().apply { this[instruction[3]] = if (instruction[1] > registers[instruction[2]]) 1 else 0 }
fun gtri(registers: Registers, instruction: Instruction): Registers =
registers.copyOf().apply { this[instruction[3]] = if (registers[instruction[1]] > instruction[2]) 1 else 0 }
fun gtrr(registers: Registers, instruction: Instruction): Registers =
registers.copyOf().apply { this[instruction[3]] = if (registers[instruction[1]] > registers[instruction[2]]) 1 else 0 }
fun eqir(registers: Registers, instruction: Instruction): Registers =
registers.copyOf().apply { this[instruction[3]] = if (instruction[1] == registers[instruction[2]]) 1 else 0 }
fun eqri(registers: Registers, instruction: Instruction): Registers =
registers.copyOf().apply { this[instruction[3]] = if (registers[instruction[1]] == instruction[2]) 1 else 0 }
fun eqrr(registers: Registers, instruction: Instruction): Registers =
registers.copyOf().apply { this[instruction[3]] = if (registers[instruction[1]] == registers[instruction[2]]) 1 else 0 }
}
}