OpCodes.scala 6.07 KB
Newer Older
David's avatar
David committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
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)
    }
  }
}