2023 Day 3: Gear Ratios

Got stuck on part 1 because I had my unit test script using yesterday’s input!
Part 2 went okay, just required some careful reading.

Nothing about my solution feels optimal in terms of CPU cycles. Looking forward to seeing how everyone else decides to model this one.

I started to use a map, too, but then it occurred to me that an array of booleans would serve the same job, and probably be more efficient. So I took that route. Also, in part 2 I didn’t keep track of all the “spans”; I just summed up values as I went along. So between saving some on the first difference and possibly using more on the second, … who knows how the CPU cycles come out. :grin:

1 Like

My solution is there :blush:

Now includes a Rust version. Eventually I’ll get to a Modula-2 version, but a few remarks on my frustrations with Rust – which really surprised me:

  • :broken_heart: I really missed having access to custom arrays, and it’s also a lot harder in Rust to check indices. In Ada, I can do this:

    for Row_Offset in -1 .. 1 when Row + Row_Offset in Constraints loop
       -- ...
       if CH.Is_Decimal_Digit
              (Schematic (Row + Row_Offset) (Col + Col_Offset))
    

    …but in Rust I have to do something like this:

    (-1..2).filter(|offset| (0..MAX_IDX).contains(&(row + offset)))
       // ...
       self.schematic[(row + row_offset) as usize]
                      [(col + offset) as usize]
                .is_ascii_digit()
    

    …unless my Rust is even worse than I thought. Notice the conversions in Rust: in the first case, I’m using i32 because otherwise it will refuse to compile the sum of a usize and the -1 that begins the range; then, I have to convert the sum to a usize in order to index into the array.

  • :scream: There’s also the annoyance (which made me waste quite a bit of time debugging) where (-1..1) indicates the numbers -1 and 0, not 1. This is why I use (-1..2) above.

  • :heart: I do like the use of functional programming / “combinators” in Rust.

All in all, I wasn’t expecting the Rust to be harder to write and more annoying to look at and debug than the Ada. Maybe it’s due in part to my starting from Ada, and probably it’s due in large part to my not thinking things through as well as I should (I’ll just translate the Ada to Rust today; how hard can that be? )

1 Like

Updated with a Modula-2 implementation. (I haven’t yet implemented the solution to day 2 in M2.) I’m definitely appreciating some of Ada’s safety features, since the lack of them in Modula-2 hammered me a few times. For example:

  • :warning: You can define a Modula-2 variable to be of a certain range type, but Modula-2 won’t choke if it goes outside that range the way Ada will – unless this is a bug in gm2. In particular, this sort of thing passes muster in gm2 and works the way I wanted, even though it understandably raised a run-time error in Ada: the loop can only end if Left > MAX(Constraints), but if that happens then Left by definition no longer has a valid value for its type, and a run-time error should be raised, even in Modula-2 (?). I haven’t yet found a clear answer in the reference I’m using.

       WHILE (Left <= MAX(Constraints))
          AND CharClass.IsNumeric(Schematic[L.Row, Left])
       DO
          Value := Value * 10 + Digit(Schematic[L.Row, Left]);
          INC(Left);
       END;
    
  • :question: Can’t seem to define a constant type of a variant record. I’m not sure if that’s a misunderstanding on my part, or a bug in gm2.

  • :scream: I’m pretty sure this is a bug: if you neglect the parentheses on a function procedure that takes no parameters, gm2 treats it as if you want the address(?). Hence, the following line of the module repeatedly gave the wrong answer:

    InOut.WriteInt(Part1, 0); (* needs to be Part1() *)