aoc::year2018

Module 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.