Day 17: Chronospatial Computer

Megathread guidelines

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL

FAQ

  • mykl@lemmy.world
    link
    fedilink
    arrow-up
    2
    ·
    edit-2
    9 days ago

    Dart

    Part one was an exercise in building a simple OpCode machine. Part two was trickier. It was clear that the a register was repeatedly being divided by 8, and testing a few values showed that each 3-bits of the initial value defined one entry in the output, so I built a recursive routine that brute-forced each one in turn. Only <1ms to run though, so not that brutal.

    (It’s worth pointing out that at some stages multiple 3-bit values gave rise to the required value, causing a failure to resolve later on if not checked.)

    (edit: for-loop in buildQuine now avoids using a 0 for the initial triplet, as this should not be a valid value, and if it turns out to generate the required digit of the quine, you will get an infinite recursion. Thanks to SteveDinn for bringing this to my attention.)

    import 'dart:math';
    import 'package:collection/collection.dart';
    import 'package:more/more.dart';
    
    var prog = <int>[];
    typedef State = (int, int, int);
    State parse(List<String> lines) {
      var regs = lines.takeTo('').map((e) => int.parse(e.split(' ').last)).toList();
      var (a, b, c) = (regs[0], regs[1], regs[2]);
      prog = lines.last.split(' ').last.split(',').map(int.parse).toList();
      return (a, b, c);
    }
    
    List<int> runProg(State rec) {
      var (int a, int b, int c) = rec;
      combo(int v) => switch (v) { 4 => a, 5 => b, 6 => c, _ => v };
      var output = <int>[], pc = 0;
      while (pc < prog.length) {
        var code = prog[pc], arg = prog[pc + 1];
        var _ = switch (code) {
          0 => a ~/= pow(2, combo(arg)),
          1 => b ^= arg,
          2 => b = combo(arg) % 8,
          3 => (a != 0) ? (pc = arg - 2) : 0,
          4 => b ^= c, //ignores arg
          5 => output.add(combo(arg) % 8),
          6 => b = a ~/ pow(2, combo(arg)),
          7 => c = a ~/ pow(2, combo(arg)),
          _ => 0
        };
        pc += 2;
      }
      return output;
    }
    
    Function eq = const ListEquality().equals;
    Iterable<int> buildQuine(State rec, List<int> quine, [top = false]) sync* {
      var (int a0, int b0, int c0) = rec;
      if (top) a0 = 0;
      if (quine.length == prog.length) {
        yield a0;
        return;
      }
      for (var a in (top ? 1 : 0).to(8)) {
        var newState = (a0 * 8 + a, b0, c0);
        var newQuine = runProg(newState);
        if (eq(prog.slice(prog.length - newQuine.length), newQuine)) {
          yield* buildQuine(newState, newQuine);
        }
      }
    }
    
    part1(List<String> lines) => runProg(parse(lines)).join(',');
    
    part2(List<String> lines) => buildQuine(parse(lines), [], true).first;
    
    • SteveDinn@lemmy.ca
      link
      fedilink
      arrow-up
      3
      ·
      9 days ago

      I’m confused reading the buildQuine() method. It reads to me that when you call it from the top level with top = true, A0 will be set to 0, and then when we get to the 0 to 8 loop, the ‘A’ register will be 0 * 8 + 0 for the first iteration, and then recurse with top = false, but with a0 still ending up 0, causing infinite recursion.

      Am I missing something?

      I got it to work with a check that avoids the recursion if the last state’s A register value is the same as the new state’s value.

      • mykl@lemmy.world
        link
        fedilink
        arrow-up
        3
        ·
        9 days ago

        Oh, good catch. That’s certainly the case if an initial value of 0 correctly generates the required value of the quine. As I’d already started running some code against the live data that’s what I tested against, and so it’s only when I just tested it against the example data that I saw the problem.

        I have changed the for-loop to read for (var a in (top ? 1 : 0).to(8)) for maximum terseness :-)

        That still works for the example and my live data, and I don’t think there should be a valid solution that relies on the first triplet being 0. Thanks for your reply!