Kinda sad the intcode machine is being discontinued. :(
This commit is contained in:
parent
e02dfaab32
commit
9af1db3734
11 changed files with 255 additions and 153 deletions
|
@ -1,31 +1,32 @@
|
||||||
package aoc.y2019
|
package aoc.y2019
|
||||||
|
|
||||||
import aoc.Day
|
import aoc.Day
|
||||||
|
import aoc.y2019.intcode.{Machine, Step}
|
||||||
import cats._
|
import cats._
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import cats.data.State
|
import cats.data.State
|
||||||
|
|
||||||
object Day02 extends Day {
|
object Day02 extends Day {
|
||||||
|
|
||||||
def initialReplacement(noun: Int, verb: Int): Intcode.Operation = Intcode.set(1, noun) *> Intcode.set(2, verb)
|
def initialReplacement(noun: Int, verb: Int): Step = Machine.set(1, noun) *> Machine.set(2, verb)
|
||||||
|
|
||||||
def runMachine(memory: Vector[Int], noun: Int, verb: Int): Int = {
|
def runMachine(memory: Vector[BigInt], noun: Int, verb: Int): BigInt = {
|
||||||
val initialState = Intcode.Machine(memory)
|
val initialState = Machine(memory)
|
||||||
val runMachine = for {
|
val runMachine = for {
|
||||||
_ <- initialReplacement(noun, verb)
|
_ <- initialReplacement(noun, verb)
|
||||||
ran <- Intcode.run()
|
ran <- Machine.run()
|
||||||
} yield ran
|
} yield ran
|
||||||
val endState = runMachine.runS(initialState).value
|
val endState = runMachine.runS(initialState).value
|
||||||
endState.memory(0)
|
endState.memory(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override def part1(input: String): String = {
|
override def part1(input: String): String = {
|
||||||
val memory = input.split(",").map(_.toInt).toVector
|
val memory = input.split(",").map(BigInt(_)).toVector
|
||||||
runMachine(memory, 12, 2).toString
|
runMachine(memory, 12, 2).toString
|
||||||
}
|
}
|
||||||
|
|
||||||
override def part2(input: String): String = {
|
override def part2(input: String): String = {
|
||||||
val memory = input.split(",").map(_.toInt).toVector
|
val memory = input.split(",").map(BigInt(_)).toVector
|
||||||
val magic = 19690720
|
val magic = 19690720
|
||||||
val solutions = for {
|
val solutions = for {
|
||||||
noun <- 0 to 99
|
noun <- 0 to 99
|
||||||
|
|
|
@ -3,18 +3,19 @@ package aoc.y2019
|
||||||
import cats._
|
import cats._
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
import aoc.Day
|
import aoc.Day
|
||||||
|
import aoc.y2019.intcode.{Machine, Step}
|
||||||
import cats.data.State
|
import cats.data.State
|
||||||
|
|
||||||
object Day05 extends Day {
|
object Day05 extends Day {
|
||||||
|
|
||||||
def diagnostic(input: String, diagnosticNumber: Int): Int = {
|
def diagnostic(input: String, diagnosticNumber: Int): BigInt = {
|
||||||
val memory = input.split(",").toVector.map(_.toInt)
|
val memory = input.split(",").map(BigInt(_)).toVector
|
||||||
val initialState = Intcode.Machine(memory)
|
val initialState = Machine(memory)
|
||||||
val machineInput = diagnosticNumber
|
val machineInput = diagnosticNumber
|
||||||
val runMachine: Intcode.Operation = for {
|
val runMachine: Step = for {
|
||||||
_ <- Intcode.input(machineInput)
|
_ <- Machine.input(machineInput)
|
||||||
ran <- Intcode.run()
|
ran <- Machine.run()
|
||||||
} yield ran
|
} yield ran.last
|
||||||
val endState = runMachine.runS(initialState).value
|
val endState = runMachine.runS(initialState).value
|
||||||
endState.output.last
|
endState.output.last
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,24 @@
|
||||||
package aoc.y2019
|
package aoc.y2019
|
||||||
|
|
||||||
import aoc.Day
|
import aoc.Day
|
||||||
|
import aoc.y2019.intcode.Machine
|
||||||
import cats._
|
import cats._
|
||||||
import cats.implicits._
|
import cats.implicits._
|
||||||
|
|
||||||
import scala.annotation.tailrec
|
import scala.annotation.tailrec
|
||||||
|
|
||||||
object Day07 extends Day {
|
object Day07 extends Day {
|
||||||
|
|
||||||
@tailrec def runAmplifiers(machine: Intcode.Machine, order: Seq[Int], input: Int = 0): Int = {
|
@tailrec def runAmplifiers(machine: Machine, order: Seq[Int], input: BigInt = 0): BigInt = {
|
||||||
if(order.isEmpty) {
|
if(order.isEmpty) {
|
||||||
input
|
input
|
||||||
} else {
|
} else {
|
||||||
val firstInput = order.head
|
val firstInput = order.head
|
||||||
val secondInput = input
|
val secondInput = input
|
||||||
val runAmplifier = for {
|
val runAmplifier = for {
|
||||||
_ <- Intcode.input(firstInput)
|
_ <- Machine.input(firstInput)
|
||||||
_ <- Intcode.input(secondInput)
|
_ <- Machine.input(secondInput)
|
||||||
ran <- Intcode.run()
|
ran <- Machine.run()
|
||||||
} yield ran
|
} yield ran
|
||||||
val output = runAmplifier.runS(machine).value.output.head
|
val output = runAmplifier.runS(machine).value.output.head
|
||||||
runAmplifiers(machine, order.tail, output)
|
runAmplifiers(machine, order.tail, output)
|
||||||
|
@ -24,28 +26,28 @@ object Day07 extends Day {
|
||||||
}
|
}
|
||||||
|
|
||||||
override def part1(input: String): String = {
|
override def part1(input: String): String = {
|
||||||
val memory = input.split(",").toVector.map(_.toInt)
|
val memory = input.split(",").map(BigInt(_)).toVector
|
||||||
val initialState = Intcode.Machine(memory)
|
val initialState = Machine(memory)
|
||||||
val maximumSignal = (0 to 4).permutations.map(runAmplifiers(initialState, _)).max
|
val maximumSignal = (0 to 4).permutations.map(runAmplifiers(initialState, _)).max
|
||||||
maximumSignal.toString
|
maximumSignal.toString
|
||||||
}
|
}
|
||||||
|
|
||||||
def loopAmplifiers(base: Intcode.Machine, initialInputs: Seq[Int]): Int = {
|
def loopAmplifiers(base: Machine, initialInputs: Seq[Int]): BigInt = {
|
||||||
val numAmplifiers = initialInputs.size
|
val numAmplifiers = initialInputs.size
|
||||||
val initialMachines = Vector.fill(numAmplifiers)(base).zip(initialInputs).map {
|
val initialMachines = Vector.fill(numAmplifiers)(base).zip(initialInputs).map {
|
||||||
case (machine, phase) => machine.copy(input = machine.input :+ phase)
|
case (machine, phase) => machine.copy(input = machine.input :+ phase)
|
||||||
}
|
}
|
||||||
val haltCondition = for {
|
val haltCondition = for {
|
||||||
waitingForInput <- Intcode.watingForInput
|
waitingForInput <- Machine.waitingForInput
|
||||||
isHalted <- Intcode.isHalted
|
isHalted <- Machine.isHalted
|
||||||
} yield waitingForInput || isHalted
|
} yield waitingForInput || isHalted
|
||||||
val runToNextMachine = Intcode.run(haltCondition = haltCondition)
|
val runToNextMachine = Machine.run(haltCondition = haltCondition)
|
||||||
@tailrec def go(index: Int, machines: Vector[Intcode.Machine], lastOutput: Int = 0): Vector[Intcode.Machine] = {
|
@tailrec def go(index: Int, machines: Vector[Machine], lastOutput: BigInt = 0): Vector[Machine] = {
|
||||||
if(machines.traverse(machine => Intcode.isHalted.runA(machine)).value.forall(identity)) machines
|
if(machines.traverse(machine => Machine.isHalted.runA(machine)).value.forall(identity)) machines
|
||||||
else {
|
else {
|
||||||
val thisMachine = machines(index)
|
val thisMachine = machines(index)
|
||||||
val inputAndRun = for {
|
val inputAndRun = for {
|
||||||
inputLastOutput <- Intcode.input(lastOutput)
|
inputLastOutput <- Machine.input(lastOutput)
|
||||||
runAsFarAsPossible<- runToNextMachine
|
runAsFarAsPossible<- runToNextMachine
|
||||||
} yield runAsFarAsPossible
|
} yield runAsFarAsPossible
|
||||||
val ranMachine = inputAndRun.runS(thisMachine).value
|
val ranMachine = inputAndRun.runS(thisMachine).value
|
||||||
|
@ -59,8 +61,8 @@ object Day07 extends Day {
|
||||||
}
|
}
|
||||||
|
|
||||||
override def part2(input: String): String = {
|
override def part2(input: String): String = {
|
||||||
val memory = input.split(",").toVector.map(_.toInt)
|
val memory = input.split(",").map(BigInt(_)).toVector
|
||||||
val initialState = Intcode.Machine(memory)
|
val initialState = Machine(memory)
|
||||||
val maximumSignal = (5 to 9).permutations.map(loopAmplifiers(initialState, _)).max
|
val maximumSignal = (5 to 9).permutations.map(loopAmplifiers(initialState, _)).max
|
||||||
maximumSignal.toString
|
maximumSignal.toString
|
||||||
}
|
}
|
||||||
|
|
21
aoc/src/main/scala/aoc/y2019/Day09.scala
Normal file
21
aoc/src/main/scala/aoc/y2019/Day09.scala
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package aoc.y2019
|
||||||
|
|
||||||
|
import aoc.Day
|
||||||
|
import aoc.y2019.intcode.Machine
|
||||||
|
|
||||||
|
object Day09 extends Day {
|
||||||
|
|
||||||
|
def boost(input: String, value: BigInt): BigInt = {
|
||||||
|
val code = input.split(",").map(BigInt(_)).toVector
|
||||||
|
val initialState = Machine(code)
|
||||||
|
val runMachine = for {
|
||||||
|
_ <- Machine.input(value)
|
||||||
|
ran <- Machine.run()
|
||||||
|
} yield ran
|
||||||
|
runMachine.runS(initialState).value.output.last
|
||||||
|
}
|
||||||
|
|
||||||
|
override def part1(input: String): String = boost(input, 1).toString
|
||||||
|
|
||||||
|
override def part2(input: String): String = boost(input, 2).toString
|
||||||
|
}
|
|
@ -1,125 +0,0 @@
|
||||||
package aoc.y2019
|
|
||||||
|
|
||||||
import cats._
|
|
||||||
import cats.implicits._
|
|
||||||
import cats.data.{Kleisli, State}
|
|
||||||
|
|
||||||
object Intcode {
|
|
||||||
|
|
||||||
case class Machine(memory: Vector[Int], pc: Int = 0, input: Vector[Int] = Vector.empty, output: Vector[Int] = Vector.empty)
|
|
||||||
type Operation = State[Machine, Unit]
|
|
||||||
|
|
||||||
type Instruction = Kleisli[State[Machine, *], LazyList[Boolean], Unit]
|
|
||||||
val instructions: Map[Int, Instruction] = Map(
|
|
||||||
1 -> Instruction.add,
|
|
||||||
2 -> Instruction.multiply,
|
|
||||||
3 -> Instruction.input,
|
|
||||||
4 -> Instruction.output,
|
|
||||||
5 -> Instruction.jumpIfTrue,
|
|
||||||
6 -> Instruction.jumpIfFalse,
|
|
||||||
7 -> Instruction.lessThan,
|
|
||||||
8 -> Instruction.equals,
|
|
||||||
)
|
|
||||||
|
|
||||||
def parse(instructions: Map[Int, Instruction] = instructions): State[Machine, (Instruction, LazyList[Boolean])] = State.inspect {
|
|
||||||
machine: Machine =>
|
|
||||||
val instructionAndFlags = machine.memory(machine.pc)
|
|
||||||
val instruction: Instruction = instructions(instructionAndFlags % 100)
|
|
||||||
val flags: LazyList[Boolean] = {
|
|
||||||
val flagString = instructionAndFlags.toString.reverseIterator.drop(2)
|
|
||||||
val flagIterator = flagString.map(_ == '1')
|
|
||||||
LazyList.from(flagIterator) ++ LazyList.continually(false)
|
|
||||||
}
|
|
||||||
(instruction, flags)
|
|
||||||
}
|
|
||||||
def step(instructions: Map[Int, Instruction] = instructions): Operation = for {
|
|
||||||
(instruction, flags) <- parse(instructions)
|
|
||||||
_ <- advance(1)
|
|
||||||
result <- instruction.run(flags)
|
|
||||||
} yield result
|
|
||||||
def run(instructions: Map[Int, Instruction] = instructions, haltCondition: State[Machine, Boolean] = isHalted): Operation =
|
|
||||||
step(instructions).untilM[Vector](haltCondition).map(_.last)
|
|
||||||
|
|
||||||
val noop: Operation = State.pure(())
|
|
||||||
|
|
||||||
def goto(address: Int): Operation = State.modify(_.copy(pc = address))
|
|
||||||
val location: State[Machine, Int] = State.inspect(_.pc)
|
|
||||||
def advance(by: Int): Operation = location.flatMap(c => goto(c + by))
|
|
||||||
|
|
||||||
def set(address: Int, value: Int): Operation = State.modify { machine =>
|
|
||||||
val newMemory = machine.memory.updated(address, value)
|
|
||||||
machine.copy(memory = newMemory)
|
|
||||||
}
|
|
||||||
def get(address: Int): State[Machine, Int] = State.inspect(_.memory(address))
|
|
||||||
|
|
||||||
def input(value: Int): Operation = State.modify { machine =>
|
|
||||||
machine.copy(input = machine.input :+ value)
|
|
||||||
}
|
|
||||||
val pullInput: State[Machine, Int] = State { machine =>
|
|
||||||
(machine.copy(input = machine.input.tail), machine.input.head)
|
|
||||||
}
|
|
||||||
|
|
||||||
def output(value: Int): Operation = State.modify { machine =>
|
|
||||||
machine.copy(output = machine.output :+ value)
|
|
||||||
}
|
|
||||||
|
|
||||||
val isHalted: State[Machine, Boolean] = State.inspect(machine => machine.memory(machine.pc) % 100 == 99)
|
|
||||||
val watingForInput: State[Machine, Boolean] = State.inspect(machine => machine.memory(machine.pc) % 100 == 3 && machine.input.isEmpty)
|
|
||||||
|
|
||||||
object Instruction {
|
|
||||||
|
|
||||||
def trans(operandCount: Int, operation: Kleisli[State[Machine, *], Vector[Int], Unit]): Instruction = Kleisli {
|
|
||||||
flags: LazyList[Boolean] =>
|
|
||||||
State.inspect { machine: Machine =>
|
|
||||||
machine.memory.view
|
|
||||||
.slice(machine.pc, machine.pc + operandCount)
|
|
||||||
.zip(flags)
|
|
||||||
.map {
|
|
||||||
case (argument, true) => argument
|
|
||||||
case (argument, false) => machine.memory(argument)
|
|
||||||
}
|
|
||||||
.toVector
|
|
||||||
}.flatMap(operation.run)
|
|
||||||
}
|
|
||||||
def op(operandCount: Int, operation: Vector[Int] => Int): Instruction =
|
|
||||||
trans(operandCount, Kleisli(operation.map { result =>
|
|
||||||
for {
|
|
||||||
loc <- location
|
|
||||||
outputAddr <- get(loc + operandCount)
|
|
||||||
_ <- set(outputAddr, result)
|
|
||||||
advance <- advance(operandCount + 1)
|
|
||||||
} yield advance
|
|
||||||
}))
|
|
||||||
|
|
||||||
val add: Instruction = op(operandCount = 2, { case Vector(a, b) => a + b })
|
|
||||||
val multiply: Instruction = op(operandCount = 2, { case Vector(a, b) => a * b })
|
|
||||||
val input: Instruction = Kleisli { _ =>
|
|
||||||
for {
|
|
||||||
loc <- location
|
|
||||||
toAddr <- get(loc)
|
|
||||||
input <- pullInput
|
|
||||||
_ <- set(toAddr, input)
|
|
||||||
advance <- advance(1)
|
|
||||||
} yield advance
|
|
||||||
}
|
|
||||||
val output: Instruction = trans(1, Kleisli {
|
|
||||||
case Vector(value) => Intcode.output(value) *> advance(1)
|
|
||||||
})
|
|
||||||
val jumpIfTrue: Instruction = trans(2, Kleisli {
|
|
||||||
case Vector(condition, newAddr) => if(condition != 0) goto(newAddr) else advance(2)
|
|
||||||
})
|
|
||||||
val jumpIfFalse: Instruction = trans(2, Kleisli {
|
|
||||||
case Vector(condition, newAddr) => if(condition == 0) goto(newAddr) else advance(2)
|
|
||||||
})
|
|
||||||
val lessThan: Instruction = op(2, {
|
|
||||||
case Vector(a, b) if a < b => 1
|
|
||||||
case Vector(_, _) => 0
|
|
||||||
})
|
|
||||||
val equals: Instruction = op(2, {
|
|
||||||
case Vector(a, b) if a == b => 1
|
|
||||||
case Vector(_, _) => 0
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
76
aoc/src/main/scala/aoc/y2019/intcode/Instruction.scala
Normal file
76
aoc/src/main/scala/aoc/y2019/intcode/Instruction.scala
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
package aoc.y2019.intcode
|
||||||
|
|
||||||
|
import cats._
|
||||||
|
import cats.implicits._
|
||||||
|
import cats.data.{Kleisli, State}
|
||||||
|
|
||||||
|
object Instruction {
|
||||||
|
|
||||||
|
val add: Instruction = op(2, _.sum)
|
||||||
|
val multiply: Instruction = op(2, _.product)
|
||||||
|
val input: Instruction = ins(0, Kleisli(_ => Machine.pull))
|
||||||
|
val output: Instruction = trans(1, Kleisli(_.traverse(Machine.output).map(_.last)))
|
||||||
|
val jumpIfTrue: Instruction = trans(2, Kleisli {
|
||||||
|
case Vector(a, b) if a != 0 => Machine.goto(b)
|
||||||
|
case _ => Machine.advance(2)
|
||||||
|
}, advance = false)
|
||||||
|
val jumpIfFalse: Instruction = trans(2, Kleisli {
|
||||||
|
case Vector(a, b) if a == 0 => Machine.goto(b)
|
||||||
|
case _ => Machine.advance(2)
|
||||||
|
}, advance = false)
|
||||||
|
val lessThan: Instruction = op(2, {
|
||||||
|
case Vector(a, b) if a < b => 1
|
||||||
|
case _ => 0
|
||||||
|
})
|
||||||
|
val equals: Instruction = op(2, {
|
||||||
|
case Vector(a, b) if a == b => 1
|
||||||
|
case _ => 0
|
||||||
|
})
|
||||||
|
val adjustBase: Instruction = trans(1, Kleisli(_.traverse(Machine.adjustOffset).map(_.last)))
|
||||||
|
|
||||||
|
val defaultInstructions: Map[Int, Instruction] = Map(
|
||||||
|
1 -> add,
|
||||||
|
2 -> multiply,
|
||||||
|
3 -> input,
|
||||||
|
4 -> output,
|
||||||
|
5 -> jumpIfTrue,
|
||||||
|
6 -> jumpIfFalse,
|
||||||
|
7 -> lessThan,
|
||||||
|
8 -> equals,
|
||||||
|
9 -> adjustBase,
|
||||||
|
)
|
||||||
|
|
||||||
|
def op(operandCount: Int, operation: Vector[BigInt] => BigInt): Instruction =
|
||||||
|
ins(operandCount, Kleisli(operation.map(State.pure)))
|
||||||
|
def ins(operandCount: Int, operation: Kleisli[Operation, Vector[BigInt], BigInt]): Instruction =
|
||||||
|
transOp(operandCount, operation.map(_.some))
|
||||||
|
def trans(argCount: Int, operation: Kleisli[Operation, Vector[BigInt], Unit], advance: Boolean = true): Instruction =
|
||||||
|
transOp(argCount, operation.map(_ => None), advance)
|
||||||
|
|
||||||
|
def transOp(argCount: Int, operation: Kleisli[Operation, Vector[BigInt], Option[BigInt]], advance: Boolean = true): Instruction =
|
||||||
|
Kleisli {
|
||||||
|
case (operandModes: OperandModes, flags: Flags) =>
|
||||||
|
val operandFuncs = flags.take(argCount).toList.map(operandModes(_).get)
|
||||||
|
for {
|
||||||
|
currentLoc <- Machine.location
|
||||||
|
argVals <- (currentLoc until currentLoc + argCount)
|
||||||
|
.toVector
|
||||||
|
.traverse(Machine.get)
|
||||||
|
args <- argVals
|
||||||
|
.zip(operandFuncs)
|
||||||
|
.traverse { case (arg: BigInt, f: Kleisli[Operation, BigInt, BigInt]) => f.run(arg) }
|
||||||
|
runOperation <- operation.run(args)
|
||||||
|
_ <- if(advance) Machine.advance(argCount) else Machine.noop
|
||||||
|
maybeOutput <- runOperation match {
|
||||||
|
case Some(output) =>
|
||||||
|
for {
|
||||||
|
outputValue <- Machine.get(currentLoc + argCount)
|
||||||
|
_ <- operandModes(flags(argCount)).set.run((outputValue, output))
|
||||||
|
advance <- Machine.advance(1)
|
||||||
|
} yield advance
|
||||||
|
case None => State.pure[Machine, Unit](())
|
||||||
|
}
|
||||||
|
} yield maybeOutput
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
68
aoc/src/main/scala/aoc/y2019/intcode/Machine.scala
Normal file
68
aoc/src/main/scala/aoc/y2019/intcode/Machine.scala
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
package aoc.y2019.intcode
|
||||||
|
|
||||||
|
import cats._
|
||||||
|
import cats.implicits._
|
||||||
|
import cats.data.State
|
||||||
|
|
||||||
|
case class Machine(
|
||||||
|
memory: Map[BigInt, BigInt] = Map.empty,
|
||||||
|
pc: BigInt = 0,
|
||||||
|
input: Vector[BigInt] = Vector.empty,
|
||||||
|
output: Vector[BigInt] = Vector.empty,
|
||||||
|
relativeBase: BigInt = 0
|
||||||
|
)
|
||||||
|
|
||||||
|
object Machine {
|
||||||
|
def apply(code: Vector[BigInt]): Machine =
|
||||||
|
Machine(code.zipWithIndex.map[(BigInt, BigInt)] { case (v, i) => (i, v) }.toMap)
|
||||||
|
|
||||||
|
def parseInstruction(instructions: Map[Int, Instruction] = Instruction.defaultInstructions): Operation[(Instruction, Flags)] =
|
||||||
|
for {
|
||||||
|
loc <- location
|
||||||
|
instructionAndFlags <- get(loc)
|
||||||
|
instruction = instructions((instructionAndFlags % 100).toInt)
|
||||||
|
flags = {
|
||||||
|
val flagString = instructionAndFlags.toString.reverseIterator.drop(2)
|
||||||
|
val flagIterator = flagString.map(_ - '0')
|
||||||
|
LazyList.from(flagIterator) ++ LazyList.continually(0)
|
||||||
|
}
|
||||||
|
} yield (instruction, flags)
|
||||||
|
def step(instructions: Map[Int, Instruction] = Instruction.defaultInstructions, operandModes: OperandModes = OperandMode.defaultOperandModes): Step =
|
||||||
|
for {
|
||||||
|
(instruction: Instruction, flags: Flags) <- parseInstruction(instructions)
|
||||||
|
_ <- advance(1)
|
||||||
|
result <- instruction.run(operandModes, flags)
|
||||||
|
} yield result
|
||||||
|
def run(instructions: Map[Int, Instruction] = Instruction.defaultInstructions, operandModes: OperandModes = OperandMode.defaultOperandModes, haltCondition: Operation[Boolean] = isHalted): Operation[Vector[Unit]] =
|
||||||
|
step(instructions, operandModes).untilM(haltCondition)
|
||||||
|
|
||||||
|
val noop: Step = State.pure(())
|
||||||
|
|
||||||
|
def goto(address: BigInt): Step = State.modify(_.copy(pc = address))
|
||||||
|
val location: Operation[BigInt] = State.inspect(_.pc)
|
||||||
|
def advance(by: BigInt): Step = for { loc <- location; go <- goto(by + loc) } yield go
|
||||||
|
|
||||||
|
val getOffset: Operation[BigInt] = State.inspect(_.relativeBase)
|
||||||
|
def setOffset(offset: BigInt): Step = State.modify(_.copy(relativeBase = offset))
|
||||||
|
def adjustOffset(adjustment: BigInt): Step = for { offset <- getOffset; set <- setOffset(offset + adjustment) } yield set
|
||||||
|
|
||||||
|
def set(address: BigInt, value: BigInt): Step = State.modify(m => m.copy(m.memory.updated(address, value)))
|
||||||
|
def get(address: BigInt): Operation[BigInt] = State.inspect(_.memory.getOrElse(address, 0))
|
||||||
|
|
||||||
|
def input(value: BigInt): Step = State.modify(m => m.copy(input = m.input :+ value))
|
||||||
|
val pull: Operation[BigInt] = State(m => (m.copy(input = m.input.tail), m.input.head))
|
||||||
|
|
||||||
|
def output(value: BigInt): Step = State.modify(m => m.copy(output = m.output :+ value))
|
||||||
|
|
||||||
|
val isHalted: Operation[Boolean] = for {
|
||||||
|
loc <- location
|
||||||
|
instruction <- get(loc)
|
||||||
|
} yield instruction % 100 == 99
|
||||||
|
val waitingForInput: Operation[Boolean] = for {
|
||||||
|
loc <- location
|
||||||
|
instruction <- get(loc)
|
||||||
|
onInputInstruction = instruction % 100 == 3
|
||||||
|
noInput <- State.inspect[Machine, Boolean](_.input.isEmpty)
|
||||||
|
} yield onInputInstruction && noInput
|
||||||
|
|
||||||
|
}
|
38
aoc/src/main/scala/aoc/y2019/intcode/OperandMode.scala
Normal file
38
aoc/src/main/scala/aoc/y2019/intcode/OperandMode.scala
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package aoc.y2019.intcode
|
||||||
|
|
||||||
|
import cats.data.{Kleisli, State}
|
||||||
|
|
||||||
|
case class OperandMode(get: Kleisli[Operation, BigInt, BigInt], set: Kleisli[Operation, (BigInt, BigInt), Unit])
|
||||||
|
|
||||||
|
object OperandMode {
|
||||||
|
|
||||||
|
val position: OperandMode = OperandMode(
|
||||||
|
Kleisli(Machine.get),
|
||||||
|
Kleisli { case (address, value) => Machine.set(address, value) }
|
||||||
|
)
|
||||||
|
val immediate: OperandMode = OperandMode(
|
||||||
|
Kleisli(State.pure),
|
||||||
|
Kleisli(_ => throw new IllegalArgumentException("Cannot set in immediate mode!"))
|
||||||
|
)
|
||||||
|
val relative: OperandMode = OperandMode(
|
||||||
|
Kleisli { address =>
|
||||||
|
for {
|
||||||
|
offset <- Machine.getOffset
|
||||||
|
value <- Machine.get(address + offset)
|
||||||
|
} yield value
|
||||||
|
},
|
||||||
|
Kleisli { case (address, value) =>
|
||||||
|
for {
|
||||||
|
offset <- Machine.getOffset
|
||||||
|
set <- Machine.set(address + offset, value)
|
||||||
|
} yield set
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
val defaultOperandModes: Map[Int, OperandMode] = Map(
|
||||||
|
0 -> position,
|
||||||
|
1 -> immediate,
|
||||||
|
2 -> relative,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
16
aoc/src/main/scala/aoc/y2019/intcode/package.scala
Normal file
16
aoc/src/main/scala/aoc/y2019/intcode/package.scala
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
package aoc.y2019
|
||||||
|
|
||||||
|
import cats.data.{Kleisli, State}
|
||||||
|
|
||||||
|
package object intcode {
|
||||||
|
|
||||||
|
type Flags = LazyList[Int]
|
||||||
|
|
||||||
|
type Operation[A] = State[Machine, A]
|
||||||
|
type Step = Operation[Unit]
|
||||||
|
|
||||||
|
type Instruction = Kleisli[Operation, (OperandModes, Flags), Unit]
|
||||||
|
|
||||||
|
type OperandModes = Map[Int, OperandMode]
|
||||||
|
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ package object y2019 extends Year {
|
||||||
"6" -> Day06,
|
"6" -> Day06,
|
||||||
"7" -> Day07,
|
"7" -> Day07,
|
||||||
"8" -> Day08,
|
"8" -> Day08,
|
||||||
|
"9" -> Day09,
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,9 @@ val sharedSettings = Seq(
|
||||||
mainClass := Some("tf.bug.aoc.Main"),
|
mainClass := Some("tf.bug.aoc.Main"),
|
||||||
addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.11.0" cross CrossVersion.full),
|
addCompilerPlugin("org.typelevel" %% "kind-projector" % "0.11.0" cross CrossVersion.full),
|
||||||
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1"),
|
addCompilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1"),
|
||||||
|
scalacOptions ++= Seq(
|
||||||
|
"-Ywarn-value-discard"
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
// lazy val aoc = crossProject(/* JSPlatform, */ JVMPlatform /* , NativePlatform */ )
|
// lazy val aoc = crossProject(/* JSPlatform, */ JVMPlatform /* , NativePlatform */ )
|
||||||
|
|
Loading…
Reference in a new issue