Ok, as promised, here's the code I wanted to write, but I was halfway done before I saw it, and I wanted the answer so bad I didn't give myself the time to rewrite.
EDIT 2: Oops, all monads.
importarrow.core.Eitherimportarrow.core.Optionimportarrow.core.flatMapimportarrow.core.getOrElseimportarrow.core.leftimportarrow.core.rightimportarrow.core.someimportjava.nio.file.Filesimportjava.nio.file.PathssealedclassMode{objectImmediate:Mode()objectPosition:Mode()}sealedclassInstruction{abstractvalopcodeFormat:Stringabstractfunexecute(pointer:Int,code:Array<Int>,params:List<Int>):Either<Option<String>,Int>openfunfindInputs(code:Array<Int>,pointer:Int)=code.drop(pointer+1).take(3).zip(opcodeFormat.format(code[pointer]/100).map{when(it){'0'->Mode.Position'1'->Mode.Immediateelse->throwError("Bad mode $it")}}.reversed()).map{(it,mode)->when(mode){Mode.Position->code[it]Mode.Immediate->it}}sealedclassThreeParameterInstruction:Instruction(){overridevalopcodeFormat="1%02d"classAdd:ThreeParameterInstruction(){overridefunexecute(pointer:Int,code:Array<Int>,params:List<Int>):Either<Option<String>,Int>{code[params[2]]=params[0]+params[1]return(pointer+4).right()}}classMultiply:ThreeParameterInstruction(){overridefunexecute(pointer:Int,code:Array<Int>,params:List<Int>):Either<Option<String>,Int>{code[params[2]]=params[0]*params[1]return(pointer+4).right()}}classLessThan:ThreeParameterInstruction(){overridefunexecute(pointer:Int,code:Array<Int>,params:List<Int>):Either<Option<String>,Int>{code[params[2]]=when{params[0]<params[1]->1else->0}return(pointer+4).right()}}classEqual:ThreeParameterInstruction(){overridefunexecute(pointer:Int,code:Array<Int>,params:List<Int>):Either<Option<String>,Int>{code[params[2]]=when{params[0]==params[1]->1else->0}return(pointer+4).right()}}}sealedclassTwoParameterInstruction:Instruction(){overridevalopcodeFormat="%02d"classJumpIfTrue:TwoParameterInstruction(){overridefunexecute(pointer:Int,code:Array<Int>,params:List<Int>)=when(params[0]){0->pointer+3else->params[1]}.right()}classJumpIfFalse:TwoParameterInstruction(){overridefunexecute(pointer:Int,code:Array<Int>,params:List<Int>)=when(params[0]){0->params[1]else->pointer+3}.right()}}classSetFromInput(privatevalinput:Int):Instruction(){overridevalopcodeFormat="1"overridefunexecute(pointer:Int,code:Array<Int>,params:List<Int>):Either<Option<String>,Int>{code[params[0]]=inputreturn(pointer+2).right()}}classOutput:Instruction(){overridevalopcodeFormat="0"overridefunexecute(pointer:Int,code:Array<Int>,params:List<Int>):Either<Option<String>,Int>{println("output: ${params[0]}")return(pointer+2).right()}}objectEnd:Instruction(){overridevalopcodeFormat:Stringget()=throwError("No opcode format for End instructions.")overridefunfindInputs(code:Array<Int>,pointer:Int)=emptyList<Int>()overridefunexecute(pointer:Int,code:Array<Int>,params:List<Int>):Either<Option<String>,Int>=Option.empty<String>().left()}}objectDay05{privatefunparseInstruction(instruction:String,input:Int)=when(instruction.takeLast(2).toInt()){1->Instruction.ThreeParameterInstruction.Add().right()2->Instruction.ThreeParameterInstruction.Multiply().right()3->Instruction.SetFromInput(input).right()4->Instruction.Output().right()5->Instruction.TwoParameterInstruction.JumpIfTrue().right()6->Instruction.TwoParameterInstruction.JumpIfFalse().right()7->Instruction.ThreeParameterInstruction.LessThan().right()8->Instruction.ThreeParameterInstruction.Equal().right()99->Instruction.End.right()else->"Problem parsing instruction $instruction".some().left()}privatefunhandleCodePoint(pointer:Int,input:Int,code:Array<Int>)=parseInstruction(code[pointer].toString(),input).flatMap{instr->instr.execute(pointer,code,instr.findInputs(code,pointer))}tailrecfunstep(code:Array<Int>,input:Int,instructionPointer:Either<Option<String>,Int>=Either.right(0)):String=when(instructionPointer){isEither.Left<Option<String>>->instructionPointer.a.getOrElse{"Program terminated successfully."}isEither.Right<Int>->{valnextInstruction=handleCodePoint(instructionPointer.b,input,code)step(code,input,nextInstruction)}}constvalFILENAME="src/main/resources/day05.txt"}funmain(){valproblemInput=Files.readAllLines(Paths.get(Day05.FILENAME)).first().split(",").map{it.toInt()}// Part 01println("Part 01")Day05.step(code=problemInput.toTypedArray(),input=1)// Part 02println("\nPart 02")Day05.step(code=problemInput.toTypedArray(),input=5)println("\nDay 02")valday02Code=Files.readAllLines(Paths.get("src/main/resources/day02.txt")).first().split(",").map{it.toInt()}.toTypedArray()Day05.step(code=day02Code,input=5)println(day02Code.take(10))}
Yeah, I wish there was a more elegant way of doing discriminated unions in Kotlin. I'm used to the lightweight kinds/types of Haskell. Arrow-KT lets you do basic stuff pretty nicely, but it gets ugly the more you try to be full functional style only.
I'm also disappointed in the type erasure.
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.
Ok, as promised, here's the code I wanted to write, but I was halfway done before I saw it, and I wanted the answer so bad I didn't give myself the time to rewrite.
EDIT 2: Oops, all monads.
Definitely can say this implementation has a lot of
class
. 🤣Yeah, I wish there was a more elegant way of doing discriminated unions in Kotlin. I'm used to the lightweight kinds/types of Haskell. Arrow-KT lets you do basic stuff pretty nicely, but it gets ugly the more you try to be full functional style only.
I'm also disappointed in the type erasure.