Skip to content

Commit 6ef4f94

Browse files
committed
Solution 2015-07 (Some Assembly Required)
1 parent ded2688 commit 6ef4f94

File tree

4 files changed

+273
-0
lines changed

4 files changed

+273
-0
lines changed

src/main/kotlin/de/ronny_h/aoc/extensions/IntegralNumbers.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,13 @@ import kotlin.math.floor
66
* Checks if a Double number is integral.
77
*/
88
fun Double.isIntegral() = floor(this) == this
9+
10+
/**
11+
* Checks if a String can be converted to an Int.
12+
*/
13+
fun String.isInt(): Boolean = try {
14+
this.toInt()
15+
true
16+
} catch (_: NumberFormatException) {
17+
false
18+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package de.ronny_h.aoc.year2015.day07
2+
3+
import de.ronny_h.aoc.AdventOfCode
4+
import de.ronny_h.aoc.extensions.isInt
5+
import de.ronny_h.aoc.year2015.day07.Operator.BinaryOperator
6+
import de.ronny_h.aoc.year2015.day07.Operator.UnaryOperator
7+
import de.ronny_h.aoc.year2015.day07.OperatorOutput.NoSignal
8+
import de.ronny_h.aoc.year2015.day07.OperatorOutput.ValueOutput
9+
10+
fun main() = SomeAssemblyRequired().run(956, 40149)
11+
12+
class SomeAssemblyRequired : AdventOfCode<Int>(2015, 7) {
13+
14+
override fun part1(input: List<String>): Int = input
15+
.map(::parse)
16+
.calculateSignalForA()
17+
18+
private fun List<Operation>.calculateSignalForA(): Int {
19+
var operations = this
20+
val values = mutableMapOf<String, UShort>()
21+
while (values["a"] == null) {
22+
val remainingOperations = buildList {
23+
operations.forEach {
24+
val result = it.execute(values)
25+
if (result.out is ValueOutput) {
26+
values.put(result.wire, result.out.value)
27+
} else {
28+
this.add(it)
29+
}
30+
}
31+
}
32+
operations = remainingOperations
33+
}
34+
return values.getValue("a").toInt()
35+
}
36+
37+
fun parse(line: String): Operation {
38+
val (lhs, rhs) = line.split(" -> ")
39+
val operation = lhs.split(" ")
40+
return when (operation.size) {
41+
1 -> if (operation.first().isInt()) {
42+
Scalar(operation.first().toUShort(), rhs)
43+
} else {
44+
ReWire(operation.first(), rhs)
45+
}
46+
47+
2 -> UnaryOperation(operation[1], UnaryOperator.valueOf(operation.first()), rhs)
48+
3 -> BinaryOperation(operation[0], operation[2], BinaryOperator.valueOf(operation[1]), rhs)
49+
else -> error("invalid operation: $operation")
50+
}
51+
}
52+
53+
override fun part2(input: List<String>): Int {
54+
val signalOfA = part1(input)
55+
val operations = input.map(::parse)
56+
.filter { it !is Scalar || it.out != "b" } + Scalar(signalOfA.toUShort(), "b")
57+
return operations.calculateSignalForA()
58+
}
59+
}
60+
61+
62+
sealed interface Operation {
63+
fun execute(values: Map<String, UShort>): Output
64+
}
65+
66+
data class Scalar(val value: UShort, val out: String) : Operation {
67+
override fun execute(values: Map<String, UShort>) = Output(out, ValueOutput(value))
68+
}
69+
70+
data class ReWire(val from: String, val out: String) : Operation {
71+
override fun execute(values: Map<String, UShort>) = Output(out, values[from]?.let { ValueOutput(it) } ?: NoSignal)
72+
}
73+
74+
data class UnaryOperation(val operand: String, val operator: UnaryOperator, val out: String) : Operation {
75+
override fun execute(values: Map<String, UShort>) = Output(out, operator.execute(values, operand))
76+
}
77+
78+
data class BinaryOperation(val op1: String, val op2: String, val operator: BinaryOperator, val out: String) :
79+
Operation {
80+
override fun execute(values: Map<String, UShort>) = Output(out, operator.execute(values, op1, op2))
81+
}
82+
83+
84+
sealed interface Operator {
85+
fun execute(values: Map<String, UShort>, vararg operands: String): OperatorOutput
86+
87+
enum class BinaryOperator : Operator {
88+
AND {
89+
override fun execute(values: Map<String, UShort>, vararg operands: String) =
90+
execute(operands, values) { a, b -> a and b }
91+
},
92+
OR {
93+
override fun execute(values: Map<String, UShort>, vararg operands: String) =
94+
execute(operands, values) { a, b -> a or b }
95+
},
96+
RSHIFT {
97+
override fun execute(values: Map<String, UShort>, vararg operands: String) =
98+
execute(operands, values) { a, b -> (a.toInt() shr b.toInt()).toUShort() }
99+
},
100+
LSHIFT {
101+
override fun execute(values: Map<String, UShort>, vararg operands: String) =
102+
execute(operands, values) { a, b -> (a.toInt() shl b.toInt()).toUShort() }
103+
};
104+
105+
fun execute(
106+
operands: Array<out String>,
107+
values: Map<String, UShort>,
108+
operation: (UShort, UShort) -> UShort
109+
): OperatorOutput {
110+
require(operands.size == 2) { "invalid number of operands for AND: $operands" }
111+
fun valueOfParameter(idx: Int): UShort? =
112+
if (operands[idx].isInt()) operands[idx].toUShort() else values[operands[idx]]
113+
114+
val op1 = valueOfParameter(0)
115+
val op2 = valueOfParameter(1)
116+
if (op1 == null || op2 == null) {
117+
return NoSignal
118+
}
119+
return ValueOutput(operation(op1, op2))
120+
}
121+
}
122+
123+
enum class UnaryOperator : Operator {
124+
NOT {
125+
override fun execute(values: Map<String, UShort>, vararg operands: String): OperatorOutput {
126+
require(operands.size == 1) { "invalid number of operands for unary operator NOT: $operands" }
127+
return values[operands.first()]?.let { ValueOutput(it.inv()) } ?: NoSignal
128+
}
129+
}
130+
}
131+
}
132+
133+
data class Output(val wire: String, val out: OperatorOutput)
134+
135+
sealed interface OperatorOutput {
136+
data class ValueOutput(val value: UShort) : OperatorOutput
137+
object NoSignal : OperatorOutput
138+
}

src/test/kotlin/de/ronny_h/aoc/extensions/IntegralNumbersTest.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,15 @@ class IntegralNumbersTest : StringSpec({
2828
value.isIntegral() shouldBe false
2929
}
3030
}
31+
32+
"for an Int, isInt returns true" {
33+
"0".isInt() shouldBe true
34+
"42".isInt() shouldBe true
35+
"-42".isInt() shouldBe true
36+
}
37+
38+
"for something that is not an Int, isInt returns false" {
39+
"true".isInt() shouldBe false
40+
"some random stuff".isInt() shouldBe false
41+
}
3142
})
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package de.ronny_h.aoc.year2015.day07
2+
3+
import de.ronny_h.aoc.year2015.day07.Operator.BinaryOperator.*
4+
import de.ronny_h.aoc.year2015.day07.Operator.UnaryOperator.NOT
5+
import de.ronny_h.aoc.year2015.day07.OperatorOutput.NoSignal
6+
import de.ronny_h.aoc.year2015.day07.OperatorOutput.ValueOutput
7+
import io.kotest.core.spec.style.StringSpec
8+
import io.kotest.data.forAll
9+
import io.kotest.data.row
10+
import io.kotest.matchers.shouldBe
11+
12+
class SomeAssemblyRequiredTest : StringSpec({
13+
14+
"parse scalar" {
15+
SomeAssemblyRequired().parse("0 -> c") shouldBe Scalar(0U, "c")
16+
}
17+
18+
"parse re-wiring" {
19+
SomeAssemblyRequired().parse("lx -> a") shouldBe ReWire("lx", "a")
20+
}
21+
22+
"parse unary operation" {
23+
SomeAssemblyRequired().parse("NOT ii -> ij") shouldBe UnaryOperation("ii", NOT, "ij")
24+
}
25+
26+
"parse binary operation" {
27+
SomeAssemblyRequired().parse("af AND ah -> ai") shouldBe BinaryOperation("af", "ah", AND, "ai")
28+
SomeAssemblyRequired().parse("du OR dt -> dv") shouldBe BinaryOperation("du", "dt", OR, "dv")
29+
SomeAssemblyRequired().parse("eo LSHIFT 15 -> es") shouldBe BinaryOperation("eo", "15", LSHIFT, "es")
30+
SomeAssemblyRequired().parse("eo RSHIFT 5 -> es") shouldBe BinaryOperation("eo", "5", RSHIFT, "es")
31+
}
32+
33+
"a scalar always represents a value" {
34+
Scalar(1U, "a").execute(mapOf("a" to 2U)) shouldBe Output("a", ValueOutput(1U))
35+
}
36+
37+
"a re-wiring returns NoSignal if the argument is not provided" {
38+
ReWire("a", "b").execute(mapOf()) shouldBe Output("b", NoSignal)
39+
}
40+
41+
"a re-wiring returns the result if the argument not provided" {
42+
ReWire("a", "b").execute(mapOf("a" to 7U)) shouldBe Output("b", ValueOutput(7U))
43+
}
44+
45+
"an unary operation returns NoSignal if the argument is not provided" {
46+
UnaryOperation("b", NOT, "a").execute(mapOf()) shouldBe Output("a", NoSignal)
47+
}
48+
49+
"an unary operation returns the result if the argument is provided" {
50+
UnaryOperation("b", NOT, "a").execute(mapOf("b" to "0000000000000000".toUShort(2))) shouldBe Output(
51+
"a",
52+
ValueOutput("1111111111111111".toUShort(2))
53+
)
54+
}
55+
56+
"all binary operations return NoSignal if not both arguments are provided" {
57+
forAll(
58+
row(AND), row(OR), row(LSHIFT), row(RSHIFT)
59+
) { op ->
60+
BinaryOperation("a", "b", op, "c").execute(emptyMap()) shouldBe Output("c", NoSignal)
61+
BinaryOperation("a", "b", op, "c").execute(mapOf("a" to 0U)) shouldBe Output("c", NoSignal)
62+
BinaryOperation("a", "b", op, "c").execute(mapOf("b" to 0U)) shouldBe Output("c", NoSignal)
63+
}
64+
}
65+
66+
"all binary operations return the result if both arguments are provided" {
67+
val values = mapOf(
68+
"a" to "0000000011111111".toUShort(2),
69+
"b" to "0000000000000001".toUShort(2),
70+
)
71+
forAll(
72+
row(AND, "0000000000000001"),
73+
row(OR, "0000000011111111"),
74+
row(LSHIFT, "0000000111111110"),
75+
row(RSHIFT, "0000000001111111"),
76+
) { op, result ->
77+
BinaryOperation("a", "b", op, "c").execute(values) shouldBe Output("c", ValueOutput(result.toUShort(2)))
78+
}
79+
}
80+
81+
"all binary operations return the result if one argument is provided and the other is a scalar" {
82+
val values = mapOf(
83+
"a" to "0000000011111111".toUShort(2),
84+
)
85+
forAll(
86+
row(AND, "0000000000000001"),
87+
row(OR, "0000000011111111"),
88+
row(LSHIFT, "0000000111111110"),
89+
row(RSHIFT, "0000000001111111"),
90+
) { op, result ->
91+
BinaryOperation("a", "1", op, "c").execute(values) shouldBe Output("c", ValueOutput(result.toUShort(2)))
92+
}
93+
}
94+
95+
"part 1: the signal that is ultimately provided to wire a" {
96+
val input = listOf(
97+
"123 -> x",
98+
"456 -> y",
99+
"x AND y -> d",
100+
"x OR y -> e",
101+
"x LSHIFT 2 -> f",
102+
"y RSHIFT 2 -> g",
103+
"NOT x -> h",
104+
"NOT y -> i",
105+
"d -> a"
106+
)
107+
SomeAssemblyRequired().part1(input) shouldBe 72
108+
}
109+
110+
"part 2 the signal that is ultimately provided to wire a when b is overridden with a" {
111+
val input = listOf("NOT b -> a", "1 -> b")
112+
SomeAssemblyRequired().part2(input) shouldBe 1
113+
}
114+
})

0 commit comments

Comments
 (0)