woof, here's my new Kotlin intcode parser, as the day's specific code is now as simple as loading the file, splitting it, and running it.
Key rewrite pieces:
More monads.
Redid my janky parameter reader.
Switched from arrays to maps. (GIGANTIC SPEEDUP)
Kotlin data classes come with a copy function! Using constructors to manually do the same thing was the final and most pernicious bug. USE THE copy FUNCTION WHEN PRESERVING IMMUTABILITY!
packageintcodeimportarrow.core.*sealedclassMode{funassignable(first:Long,offset:Long)=when(this){isImmediate->throwError("Cannot assign to immediate value")isPosition->firstisRelative->(offset+first)}funvalueOf(first:Long,code:Map<Long,Long>,relativeBase:Long)=when(this){isImmediate->first.some()isPosition->code[first].toOption()isRelative->code[relativeBase+first].toOption()}.getOrElse{0L}objectImmediate:Mode()objectPosition:Mode()objectRelative:Mode()}data classCurrentState(valpointer:Option<Long>=0L.some(),valinputs:MutableList<Long>=mutableListOf(),valoutput:Option<Long>=Option.empty(),valrelativeBase:Long=0)operatorfunCurrentState.plus(n:Int)=copy(pointer=pointer.map{it+n})operatorfunCurrentState.plus(n:Long)=copy(pointer=pointer.map{it+n})funPair<Long,Mode>.index(state:CurrentState)=second.assignable(first,state.relativeBase)funPair<Long,Mode>.value(code:Map<Long,Long>,state:CurrentState)=second.valueOf(first,code,state.relativeBase)sealedclassInstruction{abstractvalopcodes:Intabstractfunexecute(code:MutableMap<Long,Long>,params:List<Pair<Long,Mode>>,state:CurrentState):Either<String,CurrentState>openfunfindInputs(code:MutableMap<Long,Long>,state:CurrentState):Either<String,List<Pair<Long,Mode>>>=state.pointer.fold({emptyList<Pair<Long,Mode>>().right()},{pointer->(pointer+1..pointer+3).map{code[it]?:0}.zip(extractParameterType(pointer,code)).fold(listOf<Pair<Long,Mode>>().right()){a:Either<String,List<Pair<Long,Mode>>>,b->a.flatMap{acc->b.second.fold({"Pointer $pointer: Problem parsing input ${b.first}: $it".left()},{(acc+(b.firsttoit)).right()})}}})privatefunextractParameterType(pointer:Long,code:MutableMap<Long,Long>)="%010d".format((code[pointer]?:0)/100).takeLast(opcodes).reversed().map{when(it){'0'->Mode.Position.right()'1'->Mode.Immediate.right()'2'->Mode.Relative.right()else->"Bad mode $it".left()}}sealedclassThreeParameterInstruction:Instruction(){overridevalopcodes:Int=3classAdd:ThreeParameterInstruction(){overridefunexecute(code:MutableMap<Long,Long>,params:List<Pair<Long,Mode>>,state:CurrentState):Either<String,CurrentState>{code[params[2].index(state)]=params[0].value(code,state)+params[1].value(code,state)return(state+4).right()}}classMultiply:ThreeParameterInstruction(){overridefunexecute(code:MutableMap<Long,Long>,params:List<Pair<Long,Mode>>,state:CurrentState):Either<String,CurrentState>{code[params[2].index(state)]=params[0].value(code,state)*params[1].value(code,state)return(state+4).right()}}classLessThan:ThreeParameterInstruction(){overridefunexecute(code:MutableMap<Long,Long>,params:List<Pair<Long,Mode>>,state:CurrentState):Either<String,CurrentState>{code[params[2].index(state)]=when{params[0].value(code,state)<params[1].value(code,state)->1Lelse->0L}return(state+4).right()}}classEqual:ThreeParameterInstruction(){overridefunexecute(code:MutableMap<Long,Long>,params:List<Pair<Long,Mode>>,state:CurrentState):Either<String,CurrentState>{code[params[2].index(state)]=when{params[0].value(code,state)==params[1].value(code,state)->1else->0L}return(state+4).right()}}}sealedclassTwoParameterInstruction:Instruction(){overridevalopcodes:Int=2classJumpIfTrue:TwoParameterInstruction(){overridefunexecute(code:MutableMap<Long,Long>,params:List<Pair<Long,Mode>>,state:CurrentState):Either<String,CurrentState>{returnwhen(params[0].value(code,state)){0L->state+3else->state.copy(pointer=params[1].value(code,state).some())}.right()}}classJumpIfFalse:TwoParameterInstruction(){overridefunexecute(code:MutableMap<Long,Long>,params:List<Pair<Long,Mode>>,state:CurrentState):Either<String,CurrentState>=when(params[0].value(code,state)){0L->state.copy(pointer=params[1].value(code,state).some())else->state+3}.right()}}classSetFromInput(privatevalinputOption:Option<Long>):Instruction(){overridevalopcodes:Int=1overridefunexecute(code:MutableMap<Long,Long>,params:List<Pair<Long,Mode>>,state:CurrentState)=inputOption.fold({state},{input->code[params[0].index(state)]=inputstate.copy(pointer=state.pointer.map{it+2})}).right()}classOutput:Instruction(){overridevalopcodes:Int=1overridefunexecute(code:MutableMap<Long,Long>,params:List<Pair<Long,Mode>>,state:CurrentState):Either<String,CurrentState>{println("OUTPUT: ${params[0].value(code, state)}")returnstate.copy(pointer=state.pointer.map{it+2},output=params[0].value(code,state).some()).right()}}classModifyRelativeBase:Instruction(){overridevalopcodes:Int=1overridefunexecute(code:MutableMap<Long,Long>,params:List<Pair<Long,Mode>>,state:CurrentState):Either<String,CurrentState>{returnstate.copy(pointer=state.pointer.map{it+2},relativeBase=state.relativeBase+params[0].value(code,state)).right()}}objectEnd:Instruction(){overridevalopcodes:Int=0overridefunexecute(code:MutableMap<Long,Long>,params:List<Pair<Long,Mode>>,state:CurrentState):Either<String,CurrentState>=state.copy(pointer=Option.empty()).right()}}funparseInstruction(instruction:Long,input:MutableList<Long>):Either<String,Pair<Instruction,MutableList<Long>>>=when(instruction%100){1L->(Instruction.ThreeParameterInstruction.Add()toinput).right()2L->(Instruction.ThreeParameterInstruction.Multiply()toinput).right()3L->(Instruction.SetFromInput(input.firstOrNone())toinput.drop(1).toMutableList()).right()4L->(Instruction.Output()toinput).right()5L->(Instruction.TwoParameterInstruction.JumpIfTrue()toinput).right()6L->(Instruction.TwoParameterInstruction.JumpIfFalse()toinput).right()7L->(Instruction.ThreeParameterInstruction.LessThan()toinput).right()8L->(Instruction.ThreeParameterInstruction.Equal()toinput).right()9L->(Instruction.ModifyRelativeBase()toinput).right()99L->(Instruction.Endtoinput).right()else->"Problem parsing instruction $instruction".left()}funhandleCodePoint(code:MutableMap<Long,Long>,eitherState:Either<String,CurrentState>):Either<String,CurrentState>=eitherState.flatMap{state->state.pointer.fold({"Program has stopped.".left()},{pointer->code[pointer].rightIfNotNull{"Code point $pointer undefined."}.flatMap{codeValue->parseInstruction(codeValue,state.inputs).flatMap{(instr,inp)->instr.findInputs(code,state).flatMap{params->instr.execute(code,params,state.copy(pointer=pointer.some(),inputs=inp))}}}})}funstep(code:MutableMap<Long,Long>,state:CurrentState):Either<String,Long>{returnstep(code,state.right())}tailrecfunstep(code:MutableMap<Long,Long>,state:Either<String,CurrentState>):Either<String,Long>=when(state){isEither.Left<String>->"Error: ${state.a}".left()isEither.Right<CurrentState>->{when(state.b.pointer){isNone->state.b.output.fold({"No output".left()},{it.right()})isSome<Long>->step(code,handleCodePoint(code,state))}}}
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
woof, here's my new Kotlin intcode parser, as the day's specific code is now as simple as loading the file, splitting it, and running it.
Key rewrite pieces:
copy
FUNCTION WHEN PRESERVING IMMUTABILITY!