77 lines
2.7 KiB
Scala
77 lines
2.7 KiB
Scala
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
|
|
}
|
|
|
|
}
|