use "collections" use "files" use "format" interface Value fun value(): I64 class Position let _addr: I64 let _value: I64 new create(__addr: I64, __value: I64) => _addr = __addr _value = __value fun addr(): I64 => _addr fun value(): I64 => _value class Immediate let _value: I64 new create(__value: I64) => _value = __value fun value(): I64 => _value class Operands let _lhs: Value let _rhs: Value let _tgt: I64 new create(__lhs: Value, __rhs: Value, __tgt: I64) => _lhs = __lhs _rhs = __rhs _tgt = __tgt fun val1(): I64 => _lhs.value() fun val2(): I64 => _rhs.value() fun tgt(): USize => _tgt.usize() trait Error fun message(): String class val OutOfBounds is Error let _loc: USize new val create(loc: USize) => _loc = loc fun message(): String => "Memory access out of bounds: " + Format.int[USize](_loc) class val UnknownOperand is Error let _op: I64 new val create(op: I64) => _op = op fun message(): String => "Unknown operand: " + Format.int[I64](_op) primitive Handler fun run(env: Env, result: (None | Error)) => match result | None => None | let e: Error => env.err.print(e.message()) end class Program var storage: Array[I64] var pc: USize = 0 var errors: Array[Error val] new create(s: Array[I64]) => storage = s errors = Array[Error val]() new from_file(file: File) => storage = Array[I64](0) for line in file.lines() do for chunk in line.split(",").values() do try storage.push(chunk.i64()?) end end end errors = Array[Error val]() fun print(env: Env) => env.out.write("[") for x in storage.values() do env.out.write(Format.int[I64](x)) env.out.write(" ") end env.out.print("]") fun ref read_storage(addr: USize): I64 ? => try storage(addr)? else errors.push(OutOfBounds(addr)) error end fun ref write_storage(addr: USize, value: I64) ? => try storage(addr)? = value else errors.push(OutOfBounds(addr)) error end fun ref get_immediate(addr_loc: USize): Immediate ? => let value = read_storage(addr_loc)? Immediate(value) fun ref get_position(addr_loc: USize): Position ? => let addr = read_storage(addr_loc)? let value = read_storage(addr.usize())? Position(addr, value) fun ref get_op_data(): Operands ? => let lhs = get_position(pc+1)? let rhs = get_position(pc+2)? let tgt = read_storage(pc+3)? Operands(lhs, rhs, tgt) fun ref do_add() ? => let data = get_op_data()? write_storage(data.tgt(), data.val1() + data.val2())? pc = pc + 4 None fun ref do_mul() ? => let data = get_op_data()? write_storage(data.tgt(), data.val1() * data.val2())? pc = pc + 4 None fun ref run() ? => match read_storage(pc)? | 1 => do_add()? | 2 => do_mul()? | 99 => return None | let x: I64 => errors.push(UnknownOperand(x)) error end run()? fun make_trial(a: I64, b: I64): Program ? => var arr = storage.clone() arr(1)? = a arr(2)? = b Program(arr) fun ref run_trial(env: Env): Bool => try run()? else for err in errors.values() do env.out.print(err.message()) end end try storage(0)? == 19690720 else false end actor Main new create(env: Env) => let caps = recover val FileCaps.>set(FileRead).>set(FileStat) end try with file = OpenFile(FilePath(env.root as AmbientAuth, "input.txt", caps)?) as File do let program = Program.from_file(file) program.print(env) for a in Range[I64](0, 100) do for b in Range[I64](0, 100) do let trial = program.make_trial(a, b)? if trial.run_trial(env) then env.out.print("noun=" + Format.int[I64](a) + ", verb=" + Format.int[I64](b)) env.out.print("result=" + Format.int[I64]((a * 100) + b)) end end end end else // if something failed, then print an error message of some kind and exit env.err.print("Couldn't read expected file `input.txt'") env.exitcode(99) end