From abf20604117e4e5769afc2a9b2bb7a1ecf5f59ab Mon Sep 17 00:00:00 2001 From: Soren Date: Wed, 4 Dec 2019 22:15:10 -0800 Subject: [PATCH] Day 5 --- aoc/src/main/scala/aoc/y2019/Day02.scala | 49 ++----- aoc/src/main/scala/aoc/y2019/Day05.scala | 151 +++++++++++++++++++++ aoc/src/main/scala/aoc/y2019/package.scala | 1 + 3 files changed, 165 insertions(+), 36 deletions(-) create mode 100644 aoc/src/main/scala/aoc/y2019/Day05.scala diff --git a/aoc/src/main/scala/aoc/y2019/Day02.scala b/aoc/src/main/scala/aoc/y2019/Day02.scala index 2967ea6..ce519e3 100644 --- a/aoc/src/main/scala/aoc/y2019/Day02.scala +++ b/aoc/src/main/scala/aoc/y2019/Day02.scala @@ -5,52 +5,29 @@ import cats.data.State object Day02 extends Day { - case class Machine(memory: List[Int], pointer: Int) - type MachineState = State[Machine, Boolean] + import Day05.Machine, Day05.MachineState, Day05.executeMachine - val ops: Map[Int, (Int, Int) => Int] = Map( - 1 -> (_ + _), - 2 -> (_ * _) - ) - - def initialReplacement(noun: Int, verb: Int): MachineState = State { - case Machine(memory, 0) => - (Machine(memory.updated(1, noun).updated(2, verb), 0), false) + def initialReplacement(noun: Int, verb: Int): MachineState = State { m => + (m.copy(m.memory.updated(1, noun).updated(2, verb)), false) } - val step: MachineState = State { - case Machine(memory, pointer) => - memory(pointer) match { - case 99 => (Machine(memory, pointer), true) - case opcode => - val inputAAddress = memory(pointer + 1) - val inputBAddress = memory(pointer + 2) - val valueA = memory(inputAAddress) - val valueB = memory(inputBAddress) - val outputAddress = memory(pointer + 3) - val op = ops(opcode) - (Machine(memory.updated(outputAddress, op(valueA, valueB)), pointer + 4), false) - } - } - - def runMachine(memory: List[Int], noun: Int, verb: Int): Int = { - val initialState = Machine(memory, 0) - val initialReplacementState = initialReplacement(noun, verb).run(initialState).value - lazy val states: LazyList[(Machine, Boolean)] = - initialReplacementState #:: states.map { - case (currentState, _) => step.run(currentState).value - } - val (lastState, _) = states.takeWhile(!_._2).last - lastState.memory.head + def runMachine(memory: Vector[Int], noun: Int, verb: Int): Int = { + val initialState = Machine(memory) + val runMachine = for { + _ <- initialReplacement(noun, verb) + run <- executeMachine + } yield run + val endState = runMachine.runS(initialState).value + endState.memory(0) } override def part1(input: String): String = { - val memory = input.split(",").map(_.toInt).toList + val memory = input.split(",").map(_.toInt).toVector runMachine(memory, 12, 2).toString } override def part2(input: String): String = { - val memory = input.split(",").map(_.toInt).toList + val memory = input.split(",").map(_.toInt).toVector val magic = 19690720 val solutions = for { noun <- 0 to 99 diff --git a/aoc/src/main/scala/aoc/y2019/Day05.scala b/aoc/src/main/scala/aoc/y2019/Day05.scala new file mode 100644 index 0000000..fce6627 --- /dev/null +++ b/aoc/src/main/scala/aoc/y2019/Day05.scala @@ -0,0 +1,151 @@ +package aoc.y2019 + +import cats._ +import cats.implicits._ +import aoc.Day +import cats.data.State + +object Day05 extends Day { + + case class Oresult(result: Option[Int], appendOutput: Vector[Int], newPointer: Option[Int]) + object Oresult { + def apply(result: Int): Oresult = Oresult(result.some, Vector.empty, None) + } + + sealed trait Operation { + val opcode: Int + val operandCount: Int + val inputCount: Int + def apply(operands: Vector[Int], inputs: Vector[Int]): Oresult + } + case object Add extends Operation { + override val opcode: Int = 1 + override val operandCount: Int = 2 + override val inputCount: Int = 0 + def apply(operands: Vector[Int], inputs: Vector[Int]): Oresult = Oresult(operands(0) + operands(1)) + } + case object Multiply extends Operation { + override val opcode: Int = 2 + override val operandCount: Int = 2 + override val inputCount: Int = 0 + override def apply(operands: Vector[Int], inputs: Vector[Int]): Oresult = Oresult(operands(0) * operands(1)) + } + case object Input extends Operation { + override val opcode: Int = 3 + override val operandCount: Int = 0 + override val inputCount: Int = 1 + override def apply(operands: Vector[Int], inputs: Vector[Int]): Oresult = Oresult(inputs.headOption, Vector.empty, None) + } + case object Output extends Operation { + override val opcode: Int = 4 + override val operandCount: Int = 1 + override val inputCount: Int = 0 + override def apply(operands: Vector[Int], inputs: Vector[Int]): Oresult = Oresult(None, operands, None) + } + case object JumpIfTrue extends Operation { + override val opcode: Int = 5 + override val operandCount: Int = 2 + override val inputCount: Int = 0 + override def apply(operands: Vector[Int], inputs: Vector[Int]): Oresult = Oresult(None, Vector.empty, Option.when(operands(0) != 0)(operands(1))) + } + case object JumpIfFalse extends Operation { + override val opcode: Int = 6 + override val operandCount: Int = 2 + override val inputCount: Int = 0 + override def apply(operands: Vector[Int], inputs: Vector[Int]): Oresult = Oresult(None, Vector.empty, Option.when(operands(0) == 0)(operands(1))) + } + case object LessThan extends Operation { + override val opcode: Int = 7 + override val operandCount: Int = 2 + override val inputCount: Int = 0 + override def apply(operands: Vector[Int], inputs: Vector[Int]): Oresult = Oresult(if(operands(0) < operands(1)) 1 else 0) + } + case object Equal extends Operation { + override val opcode: Int = 8 + override val operandCount: Int = 2 + override val inputCount: Int = 0 + override def apply(operands: Vector[Int], inputs: Vector[Int]): Oresult = Oresult(if(operands(0) == operands(1)) 1 else 0) + } + + object Operation { + val all: Map[Int, Operation] = Seq( + Add, + Multiply, + Input, + Output, + JumpIfTrue, + JumpIfFalse, + LessThan, + Equal + ).map(op => (op.opcode, op)).toMap + } + + case class Machine(memory: Vector[Int], pointer: Int = 0 , input: Vector[Int] = Vector.empty, output: Vector[Int] = Vector.empty) + type MachineState = State[Machine, Boolean] + + def inputNumber(num: Int): MachineState = State { machine => + (machine.copy(input = machine.input :+ num), false) + } + + val executeMachine: MachineState = State { machine => + lazy val states: LazyList[(Machine, Boolean)] = + (machine, false) #:: states.map { + case (state, true) => (state, true) + case (m @ Machine(memory, pointer, input, output), false) => + memory(pointer) match { + case 99 => (m, true) + case opcode => + val operation = Operation.all(opcode % 100) + val operandCount = operation.operandCount + val inputCount = operation.inputCount + val inputs = input.take(inputCount) + val immediateParameters = (1 to operandCount).toVector.map { operandNum => + val divide = Math.pow(10, operandNum + 1).toInt + val modulo = Math.pow(10, operandNum + 2).toInt + ((opcode % modulo) / divide) == 1 + } + val operands = (1 to operandCount).toVector.zip(immediateParameters).map { + case (operandNum, false) => memory(memory(pointer + operandNum)) + case (operandNum, true) => memory(pointer + operandNum) + } + val Oresult(result, appendOutput, changePointer) = operation(operands, inputs) + + val newMemory = result match { + case Some(result) => memory.updated(memory(pointer + operandCount + 1), result) + case None => memory + } + val newPointer = (result, changePointer) match { + case (_, Some(newAddress)) => newAddress + case (Some(_), None) => pointer + operandCount + 2 + case (None, None) => pointer + operandCount + 1 + } + val newInput = input.drop(inputCount) + val newOutput = output ++ appendOutput + + (Machine(newMemory, newPointer, newInput, newOutput), false) + } + } + states.takeWhile(!_._2).last + } + + def diagnostic(input: String, diagnosticNumber: Int): Int = { + val memory = input.split(",").toVector.map(_.toInt) + val initialState = Machine(memory) + val machineInput = diagnosticNumber + val runMachine: MachineState = for { + _ <- inputNumber(machineInput) + ran <- executeMachine + } yield ran + val endState = runMachine.runS(initialState).value + endState.output.last + } + + override def part1(input: String): String = { + diagnostic(input, 1).toString + } + + override def part2(input: String): String = { + diagnostic(input, 5).toString + } + +} diff --git a/aoc/src/main/scala/aoc/y2019/package.scala b/aoc/src/main/scala/aoc/y2019/package.scala index e677d39..d09ff74 100644 --- a/aoc/src/main/scala/aoc/y2019/package.scala +++ b/aoc/src/main/scala/aoc/y2019/package.scala @@ -7,6 +7,7 @@ package object y2019 extends Year { "2" -> Day02, "3" -> Day03, "4" -> Day04, + "5" -> Day05, ) }