Not Quite Lisp

AoC 2015 with Rust - Day 1

Jul 15 23 • 6 min read


Source of the final solution

This is the first post of the Aoc 2015 with Rust series, which will be a documented journey of me learning Rust language. I am a professional software developer so I already know a number of programming languages. For that I reason, I intend to skip parts that are common in other languages and, instead, focus on parts that are new or cryptic to me.

I will primarily use official docs and StackOverflow for more specific situations.

Toolchain is ready and installed thanks to rustup. I will initialize projects with cargo init, which should give us the base setup to work on. Aand let’s go!

Part 1

Santa wants to deliver presents in an apartment building but for floor directions, all he got is a set of parentheses. ( means one floor up, ) means one floor down. )())()) results in -3, and ))((((() results in 2.

Piece of cake. I can increment or decrement a counter as I iterate through the set of these characters accordingly.

I remember some basic stuff from my 15 minutes of Rust experience like how to declare variables, variables being immutable by default, the mut keyword, type inference etc. So I try some hello wordish things:

fn main() {
    let text = "oi";
    println!(text);
}

To my surprise, this did not work.

   Compiling aoc-2015-01 v0.1.0 (/home/oz/dev/aoc/aoc-2015-01)
error: format argument must be a string literal
 --> src/main.rs:3:14
  |
3 |     println!(text);
  |              ^^^^
  |
help: you might be missing a string literal to format with
  |
3 |     println!("{}", text);
  |              +++++

error: could not compile `aoc-2015-01` (bin "aoc-2015-01") due to previous error

Yeh, string literals…

fn main() {
    let text = "oi";
    println!("{}", text);
}

//oi

The method that I will use is string.chars(), which apparently returns an iterator (something that can be looped through), and with a for loop, I can check each character in the string and act upon it. The question also includes some warnings about Unicode characters, which can resolve to multiple characters (ex: becomes ['a', '\u{310}']), but since our input will only consist of parantheses, I don’t have to take any measures against this issue.

fn main() {
    let instructions = "))((((()";
    let mut currentFloor = 0;

    for char in instructions.chars() {
        if char == '(' {
            currentFloor = currentFloor + 1;
        } else {
            currentFloor = currentFloor - 1;
        }
    }

    println!("{}", currentFloor);
}

I found that strings has a method called chars() which returns an iterator and, surely, running the code prints out 2 as expected. One minor hiccup and surely, it prints out 2.

One minor issue I faced when comparing the character to an opening paranthesis was, characters are expected to be surrounded by single quotes, not double quotes but I tried the latter first and the compiler kindly warned me and steered me into the right direction:

error[E0308]: mismatched types
 --> src/main.rs:6:20
  |
6 |         if char == "(" {
  |            ----    ^^^ expected `char`, found `&str`
  |            |
  |            expected because this is `char`
  |
help: if you meant to write a `char` literal, use single quotes
  |
6 |         if char == '(' {
  |                    ~~~

For more information about this error, try `rustc --explain E0308`.

Now is the time for taking an input from AoC and pasting it into the instructions variable, and run the code, hoping that it works as expected.

AoC gave me a very long string that resolved to 280, and pasting this number into the solution box and sending it gave me this message:

Your puzzle answer was 280.

The first half of this puzzle is complete! It provides one gold star: *

Nice. On to part two!

P.S: At this point, I also realized that the compiler warned me about using camel case when naming the currentFloor variable, so I will keep that in mind for later changes.

Part 2

The second part of the puzzle wants me to find the position of the first character that causes Santa to enter the basement (floor -1).

I want to simply add an index to the iteration, so I can pinpoint the exact location when Santa reaches the basement for the first time.

fn main() {
    let instructions = "))((((()";
    let mut currentFloor = 0;
    let mut hasReachedBasement = false;

    for (idx, char) in instructions.chars().enumerate() {
        if char == '(' {
            currentFloor = currentFloor + 1;
        } else {
            currentFloor = currentFloor - 1;
        }

        if !hasReachedBasement && currentFloor < 0 {
           println!("{}", idx + 1); 
           hasReachedBasement = true;
        }
    }

    println!("{}", currentFloor);
}

And it works! Part 2 of our puzzle is now complete.

Retrospective

The first blog post in the series turned out to be a lot longer than I wanted, and I found myself to be explaining basic programming concepts unintentionally, mostly because I am new to writing blog posts. I initially planned this series to be a “log”, rather than long explanations, but here we are… I decided to post this blog as is, but going forward, I will try to keep it as concise as possible. Let’s see how it goes.

Update: chopped most parts according to the retro