Module aoc::year2018::day21

source ·
Expand description

§Chronal Conversion

Just like Day 19 we reverse engineer the assembly to figure out what the program is doing, then implement the program more efficiently in Rust.

    Raw               | Pseudo-Assembly                 | Pseudo-Rust
    ------------------+---------------------------------+---------------------------------------
    #ip 2             | # a = 0 b = 1 c = 3 d = 4 e = 5 |
    seti 123 0 3      |          c = 123                | // Test start
    bani 3 456 3      | alfa:    c &= 456               | //
    eqri 3 72 3       |          c = (c == 72) ? 1: 0   | // Irrelevant to rest of program.
    addr 3 2 2        |          if c == 1 goto bravo   | //
    seti 0 0 2        |          goto alfa              | // Test end
    seti 0 4 3        | bravo:   c = 0                  | do {
    bori 3 65536 4    | charlie: d = c | 65536          |     d = c | 0x10000;
    seti $SEED 3 3    |          c = $SEED              |     c = $SEED
    bani 4 255 5      | delta:   e = d & 255            |     while d > 0 {
    addr 3 5 3        |          c += e                 |
    bani 3 16777215 3 |          c &= 16777215          |         c = (c + (d & 0xff)) & 0xffffff;
    muli 3 65899 3    |          c *= 65899             |
    bani 3 16777215 3 |          c &= 16777215          |         c = (c * 65899) & 0xffffff;
    gtir 256 4 5      |          e = (256 > d) ? 1: 0   |         // Break condition for
    addr 5 2 2        |          if e == 1 goto echo    |         // loop. Loop will always
    addi 2 1 2        |          goto foxtrot           |         // execute 3 times.
    seti 27 0 2       | echo:    goto kilo              |
    seti 0 2 5        | foxtrot: e = 0                  |         // Start inner loop
    addi 5 1 1        | golf:    b = e + 1              |         //
    muli 1 256 1      |          b *= 256               |         //
    gtrr 1 4 1        |          b = (b > d) 1: 0       |         //
    addr 1 2 2        |          if b == 1 goto hotel   |         // This loop computes d
    addi 2 1 2        |          goto india             |         // shifted right by 8 bits.
    seti 25 3 2       | hotel:   goto juliet            |         //
    addi 5 1 5        | india:   e += 1                 |         //
    seti 17 3 2       |          goto golf              |         // End inner loop
    setr 5 3 4        | juliet:  d = e                  |         d >>= 8;
    seti 7 4 2        |          goto delta             |     }
    eqrr 3 0 5        | kilo:    e = (c == a) ? 1: 0    | } while (c != a);
    addr 5 2 2        |          if e == 1 goto end     |
    seti 5 8 2        |          goto charlie           |
                      | end:                            |

Starting with 0 the program computes a series of hashes, terminating once the hash is equal to register 0. $SEED is the only value that we need from the input.

For part one, in order to execute the fewest instructions, the loop should terminate after one repetition so register 0 should contain the value of the first hash.

Part two is more subtle. Analyzing the hash values shows that they eventually form a cycle. To execute the most instructions but still terminate, register 0 should be equal to the last value of the cycle (assuming that the seed value is chosen so that this hash does not appear outside the cycle). For example:

    0 => 7 => 1 =>  [4 = > 5 => 3 => 2] => [4 => ...]

The cycle starts with 4 and ends with 2, so the answer is 2.

Functions§

  • Execute the loop just once.
  • Find the last value in the cycle of output hashes.
  • step 🔒
    Implements the program hash function.