use "collections" use "files" use "format" class Value 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 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 OutOfBounds is Error let _loc: USize new create(loc: USize) => _loc = loc fun message(): String => "Memory access out of bounds: " + Format.int[USize](_loc) class UnknownOperand is Error let _op: I64 new 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 new create(s: Array[I64]) => storage = s 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 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 get_value(addr_loc: USize): (Value | Error) => let addr = try storage(addr_loc)? else return OutOfBounds(addr_loc) end let value = try storage(addr.usize())? else return OutOfBounds(addr.usize()) end Value(addr, value) fun ref get_op_data(): (Operands | Error) => let lhs = match get_value(pc+1) | let v: Value => v | let e: Error => return e end let rhs = match get_value(pc+2) | let v: Value => v | let e: Error => return e end let tgt = try storage(pc+3)? else return OutOfBounds(pc+1) end Operands(lhs, rhs, tgt) fun ref do_add(): (None | Error) => let data = match get_op_data() | let o: Operands => o | let e: Error => return e end try storage(data.tgt())? = data.val1() + data.val2() else return OutOfBounds(data.tgt()) end pc = pc + 4 None fun ref do_mul(): (None | Error) => let data = match get_op_data() | let o: Operands => o | let e: Error => return e end try storage(data.tgt())? = data.val1() * data.val2() else return OutOfBounds(data.tgt()) end pc = pc + 4 None fun ref run(): (None | Error) => try match storage(pc)? | 1 => do_add() | 2 => do_mul() | 99 => return None | let x: I64 => return UnknownOperand(x) end else return OutOfBounds(pc) 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 => Handler.run(env, run()) 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