Commit 75d061ca authored by David's avatar David

I really like my IntCode machine

parent c775c9d2
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %.15c{1.} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="is.kow.adventofcode._2019" level="DEBUG"/>
<Root level="error">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
\ No newline at end of file
package is.kow.adventofcode._2019
import java.util
import scala.annotation.tailrec
import scala.collection.mutable
import scala.collection.mutable.ListBuffer
abstract class OpCode(val opValue: Int, val parameterSize: Int) {
val opCodeFormatted: String = codeFormat(opValue)
protected final val IMMEDIATE = '1'
protected final val POSITION = '0'
def codeFormat(input: Int): String = {
("%0" + (parameterSize + 2) + "d").format(input)
}
def operate(offset: Int, data: List[Int]): List[Int]
def matches(code: Int): Boolean = {
//convert the code to a string also
val actualOpCode = codeFormat(code).slice(parameterSize, parameterSize + 2)
//Determine if the last two of the formatted thing, as an integer, are our opcode
opCodeFormatted.slice(parameterSize, parameterSize + 2) == actualOpCode
}
}
class AddOpCode() extends OpCode(1, 3) {
override def operate(offset: Int, data: List[Int]): List[Int] = {
data.drop(offset) match {
case opParams :: in1 :: in2 :: dest :: xs =>
val formattedOpParams = codeFormat(opParams)
println(s"OpcodeFormatted: ${formattedOpParams}")
val operand1 = if (formattedOpParams(2) == POSITION) data(in1) else in1
val operand2 = if (formattedOpParams(1) == POSITION) data(in2) else in2
data.updated(dest, operand1 + operand2)
}
}
}
class MultOpCode() extends OpCode(2, 3) {
override def operate(offset: Int, data: List[Int]): List[Int] = {
data.drop(offset) match {
case opParams :: in1 :: in2 :: dest :: xs =>
val formattedOpParams = codeFormat(opParams)
val operand1 = if (formattedOpParams(2) == POSITION) data(in1) else in1
val operand2 = if (formattedOpParams(1) == POSITION) data(in2) else in2
data.updated(dest, operand1 * operand2)
}
}
}
class InputOpCode(val input: Int) extends OpCode(3, 1) {
override def operate(offset: Int, data: List[Int]): List[Int] = {
data.drop(offset) match {
case opParams :: p1 :: xs =>
data.updated(p1, input)
}
}
}
//Can have multiple outputs...
class OutputOpCode(output: (Int => Unit)) extends OpCode(4, 1) {
override def operate(offset: Int, data: List[Int]): List[Int] = {
data.drop(offset) match {
case opParams :: p1 :: xs =>
println(s"OUTPUT: ${data(p1)}")
output(data(p1))
data
}
}
}
case class EndOpCode() extends OpCode(99, 0) {
override def operate(offset: Int, data: List[Int]): List[Int] = {
data
}
}
class IntCode(data: List[Int]) {
......@@ -89,10 +15,17 @@ class IntCode(data: List[Int]) {
def execute(input: Option[Int]): List[Int] = {
val END_OP = EndOpCode()
val OP_CODES = List(new AddOpCode(), new MultOpCode(), END_OP, outputOpCode) ++ (if (input.isDefined) List(new InputOpCode(input.get)) else List.empty)
val OP_CODES = List(
new JumpIfFalseOpCode(),
new JumpIfTrueOpCode(),
new LessThanOpCode(),
new EqualsOpCode(),
new AddOpCode(),
new MultOpCode(),
END_OP,
outputOpCode) ++ (if (input.isDefined) List(new InputOpCode(input.get)) else List.empty)
@tailrec def process(completedOps: List[OpCode], acc: List[Int], opLocation: Int = 0): List[Int] = {
println(s"processing at ${opLocation}")
val currentOp = acc.drop(opLocation).head
if (END_OP.matches(currentOp)) {
acc
......@@ -104,7 +37,7 @@ class IntCode(data: List[Int]) {
throw new RuntimeException(s"Invalid Operation: $currentOp")
}
val op = maybeOp.get
process(op :: completedOps, operated, opLocation + 1 + op.parameterSize)
process(op :: completedOps, operated.data, operated.opPointer)
}
}
......
package is.kow.adventofcode._2019
import com.typesafe.scalalogging.LazyLogging
case class OperationResult(data: List[Int], opPointer: Int)
abstract class OpCode(val opValue: Int, val parameterSize: Int) extends LazyLogging {
val opCodeFormatted: String = codeFormat(opValue)
protected final val IMMEDIATE = '1'
protected final val POSITION = '0'
def codeFormat(input: Int): String = {
("%0" + (parameterSize + 2) + "d").format(input)
}
def operate(offset: Int, data: List[Int]): OperationResult
def matches(code: Int): Boolean = {
//convert the code to a string also
val actualOpCode = codeFormat(code).slice(parameterSize, parameterSize + 2)
//Determine if the last two of the formatted thing, as an integer, are our opcode
opCodeFormatted.slice(parameterSize, parameterSize + 2) == actualOpCode
}
protected def logOperation(index: Int, name: String, opParams: String, params: Int*): Unit = {
logger.whenDebugEnabled({
val paramSize = params.foldLeft(Int.MinValue) { (acc, p) =>
val paramLength = p.toString.length
if (paramLength > acc) paramLength else acc
}
val formatString = "%4d %10s: %5s " + params.foldLeft("") { (acc, p) =>
acc + s"%${paramSize}d "
}
val intermediary = Seq(index, name, opParams) ++ params
logger.debug(formatString.format(intermediary: _*))
})
}
}
class AddOpCode() extends OpCode(1, 3) {
override def operate(offset: Int, data: List[Int]): OperationResult = {
data.drop(offset) match {
case opParams :: in1 :: in2 :: dest :: xs =>
val formattedOpParams = codeFormat(opParams)
val operand1 = if (formattedOpParams(2) == POSITION) data(in1) else in1
val operand2 = if (formattedOpParams(1) == POSITION) data(in2) else in2
logOperation(offset, "ADD", formattedOpParams, operand1, operand2)
OperationResult(data.updated(dest, operand1 + operand2), offset + 1 + parameterSize)
}
}
}
class MultOpCode() extends OpCode(2, 3) {
override def operate(offset: Int, data: List[Int]): OperationResult = {
data.drop(offset) match {
case opParams :: in1 :: in2 :: dest :: xs =>
val formattedOpParams = codeFormat(opParams)
val operand1 = if (formattedOpParams(2) == POSITION) data(in1) else in1
val operand2 = if (formattedOpParams(1) == POSITION) data(in2) else in2
logOperation(offset, "MULT", formattedOpParams, operand1, operand2)
OperationResult(data.updated(dest, operand1 * operand2), offset + 1 + parameterSize)
}
}
}
class InputOpCode(val input: Int) extends OpCode(3, 1) {
override def operate(offset: Int, data: List[Int]): OperationResult = {
data.drop(offset) match {
case opParams :: p1 :: xs =>
logOperation(offset, "INPUT", codeFormat(opParams), p1, input)
OperationResult(data.updated(p1, input), offset + 1 + parameterSize)
}
}
}
//Can have multiple outputs...
class OutputOpCode(output: (Int => Unit)) extends OpCode(4, 1) {
override def operate(offset: Int, data: List[Int]): OperationResult = {
data.drop(offset) match {
case opParams :: p1 :: xs =>
val fOpParams = codeFormat(opParams)
val operand1 = if(fOpParams(0) == POSITION) data(p1) else p1
logOperation(offset, "OUTPUT", codeFormat(opParams), operand1)
output(operand1)
OperationResult(data, offset + 1 + parameterSize)
}
}
}
case class EndOpCode() extends OpCode(99, 0) {
override def operate(offset: Int, data: List[Int]): OperationResult = {
logOperation(offset, "END", codeFormat(99))
OperationResult(data, offset)
}
}
class JumpIfTrueOpCode() extends OpCode(5, 2) {
override def operate(offset: Int, data: List[Int]): OperationResult = {
data.drop(offset) match {
case opParams :: p1 :: p2 :: xs =>
val formattedOpParams = codeFormat(opParams)
val operand1 = if (formattedOpParams(1) == POSITION) data(p1) else p1
val operand2 = if (formattedOpParams(0) == POSITION) data(p2) else p2
logOperation(offset, "JMPTRUE", formattedOpParams, operand1, operand2)
if (operand1 != 0) {
OperationResult(data, operand2)
} else {
OperationResult(data, offset + 1 + parameterSize)
}
}
}
}
class JumpIfFalseOpCode() extends OpCode(6, 2) {
override def operate(offset: Int, data: List[Int]): OperationResult = {
data.drop(offset) match {
case opParams :: p1 :: p2 :: xs =>
val formattedOpParams = codeFormat(opParams)
val operand1 = if (formattedOpParams(1) == POSITION) data(p1) else p1
val operand2 = if (formattedOpParams(0) == POSITION) data(p2) else p2
logOperation(offset, "JMPFALSE", formattedOpParams, operand1, operand2)
if (operand1 == 0) {
OperationResult(data, operand2)
} else {
OperationResult(data, offset + 1 + parameterSize)
}
}
}
}
class LessThanOpCode() extends OpCode(7, 3) {
override def operate(offset: Int, data: List[Int]): OperationResult = {
data.drop(offset) match {
case opParams :: p1 :: p2 :: p3 :: xs =>
val fOpParams = codeFormat(opParams)
val operand1 = if (fOpParams(2) == POSITION) data(p1) else p1
val operand2 = if (fOpParams(1) == POSITION) data(p2) else p2
val dest = p3
val result = if (operand1 < operand2) 1 else 0
logOperation(offset, "LESSTHAN", fOpParams, operand1, operand2, dest)
OperationResult(data.updated(dest, result), offset + 1 + parameterSize)
}
}
}
class EqualsOpCode() extends OpCode(8, 3) {
override def operate(offset: Int, data: List[Int]): OperationResult = {
data.drop(offset) match {
case opParams :: p1 :: p2 :: p3 :: xs =>
val fOpParams = codeFormat(opParams)
val operand1 = if (fOpParams(2) == POSITION) data(p1) else p1
val operand2 = if (fOpParams(1) == POSITION) data(p2) else p2
val dest = p3
val result = if (operand1 == operand2) 1 else 0
logOperation(offset, "EQUALS", fOpParams, operand1, operand2, dest)
OperationResult(data.updated(dest, result), offset + 1 + parameterSize)
}
}
}
\ No newline at end of file
......@@ -16,7 +16,13 @@ object Puzzle5 extends App {
val output = intCodeEngine.outputCollector
println(s"output: ${output.mkString(",")}")
println(s"Part 1 Output: ${output.mkString(",")}")
val part2Engine = new IntCode(ops)
val result2 = part2Engine.execute(Option(5))
val output2 = part2Engine.outputCollector
println(s"Part 2 output: ${output2.mkString(",")}")
} finally {
source.close()
......
3,12,6,12,15,1,13,14,13,4,13,99,-1,0,1,9
\ No newline at end of file
3,3,1105,-1,9,1101,0,0,12,4,12,99,1
\ No newline at end of file
3,21,1008,21,8,20,1005,20,22,107,8,21,20,1006,20,31,1106,0,36,98,0,0,1002,21,125,20,4,20,1105,1,46,104,999,1105,1,46,1101,1000,1,20,4,20,1105,1,46,98,99
......@@ -4,10 +4,11 @@ import org.junit.runner.RunWith
import org.scalatest.funspec.AnyFunSpec
import org.scalatestplus.junit.JUnitRunner
import scala.io.Source
@RunWith(classOf[JUnitRunner])
class Puzzle5Spec extends AnyFunSpec {
describe("Old Opcode spec") {
Map(
List(1, 0, 0, 0, 99) -> List(2, 0, 0, 0, 99),
......@@ -27,7 +28,29 @@ class Puzzle5Spec extends AnyFunSpec {
}
describe("Day 5 part 2") {
List(
("inputTest1.txt", 800, 1),
("inputTest1.txt", 0, 0),
("inputTest2.txt", 800, 1),
("inputTest2.txt", 0, 0),
("inputTest3.txt", 7, 999),
("inputTest3.txt", 8, 1000),
("inputTest3.txt", 9, 1001),
).foreach { case (resource, input, output) =>
it(s"for $resource and $input gets $output") {
val source = Source.fromResource(resource)
try {
val ops = source.getLines().toList.head.split(",").map(_.toInt).toList
val intCodeEngine = new IntCode(ops)
intCodeEngine.execute(Option(input))
val myResult = intCodeEngine.outputCollector.head
assert(myResult == output)
} finally {
source.close()
}
}
}
}
}
......@@ -12,6 +12,11 @@ allprojects {
dependencies {
implementation("org.scala-lang:scala-library:2.13.1")
implementation("com.typesafe.scala-logging:scala-logging_2.13:3.9.2")
implementation("org.apache.logging.log4j:log4j-core:2.12.1")
implementation("org.apache.logging.log4j:log4j-slf4j-impl:2.12.1")
testImplementation("org.scalatest:scalatest_2.13:3.1.0")
testImplementation("junit:junit:4.11")
testImplementation("org.scalatestplus:scalatestplus-junit_2.13:1.0.0-M2")
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment