aoc-old/aoc/src/main/scala/aoc/y2019/intcode/Instruction.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
}
}