2
\$\begingroup\$

I'm also doing the fun little Advent of Code challenges in order to learn Rust. Until now things went quite smoothly, but while I'm proceeding with the tasks I'd love to learn where I can actually improve my Rust coding (and not only getting the correct results).

I'm specifically looking for feedback where I'm violating Rust idioms or suggestions e.g. for simplifications as my knowledge of the Rust STD library features is still quite limited. On the other hand in context of those coding challenges I don't really care too much about stuff like input validation/stability/error handling or even performance (as you would in actual production code).

As requested, a small summary of what the program does, as the linked description is admittedly a little exhaustive:

The program basically simulates very basic machine code. A set of instructions represented as a list of integers values is given by a text file (comma delimited values).

Instructions start with an "opcode", describing the operations. In our case this may either be an addition (1), multiplication (2) or program end (99). All other values are deemed invalid as an opcode. The 3 integers following the opcodes are the parameters, giving the addresses of the operands (offset 1 and 2), as well as the target address to store the result (offset 3).

The result of the program is defined to be stored at address 0. In the 2nd part of the task one is given a program result (target_value 19690720) and is supposed to figure out which values need to reside at the memory addresses at 1 and 2 before starting the program (which are in this context called "noun" and "verb") in order to get this result.

Here's my implementation part 2 of day 2:

use std::fs;

fn add_values (value_a: i32, value_b: i32, target: &mut i32) {
    *target = value_a + value_b
}

fn multiply_values (value_a: i32, value_b: i32, target: &mut i32) {
    *target = value_a * value_b
}

fn run_program(memory: &mut Vec<i32>) -> i32 {
    let command_stride = 4;
    let mut current_pos = 0;
    while current_pos < memory.len() {
        let current_instruction = memory[current_pos];
        let address_a = memory[current_pos + 1] as usize;
        let address_b = memory[current_pos + 2] as usize;
        let address_target = memory[current_pos + 3] as usize;
        match current_instruction {
            1 => add_values(memory[address_a], memory[address_b], &mut memory[address_target]),
            2 => multiply_values(memory[address_a], memory[address_b], &mut memory[address_target]),
            99 => break,
            _ => panic!("Invalid command value!")
        }
        current_pos += command_stride;
    }
    //return result of the program stored at address 0
    memory[0]
}

fn main() {
    let contents = fs::read_to_string("input.txt")
        .expect("Something went wrong reading the file");

    let split_codes = contents.split_terminator(',');
    let codes : Vec<i32> = split_codes.filter_map(|v| v.parse::<i32>().ok()).collect();

    let target_value = 19690720;

    for noun in 0..100 {
        for verb in 0..100 {
            let mut current_memory = codes.clone();
            //init noun and verb
            current_memory[1] = noun;
            current_memory[2] = verb;

            let current_result = run_program(&mut current_memory);

            if current_result == target_value {
                 println!("Target value reached, noun: {}, verb {}", noun, verb);
                 println!("Result = {}", 100*noun + verb);
                 break;
            }
        }
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ Thx for your comments - I've added a little summary of what the program does. Hope this helps understanding. \$\endgroup\$
    – cpattern
    Dec 7, 2019 at 20:40

1 Answer 1

2
\$\begingroup\$

Some things you could do :

  • remove these "add" and "multiply" functions
  • group operations when you read the file and when you split the input
  • use iterators on your splitted input
  • assign using the match statement
  • declare all your variables at once (it's debatable, but if you find it easier to read then go for it ! )
  • put info in your error message for the code (although it might not help much if you have a bug here, I admit)

It could look something like :

use std::fs;

fn run_program(memory: &mut Vec<i32>) -> i32 {
    for i in (0..memory.len()).step_by(4) {
        let (address_a, address_b, pos) = (memory[i + 1] as usize, memory[i + 2] as usize, memory[i + 3] as usize);
        memory[pos] = match memory[i] {
            1 => {
                memory[address_a] + memory[address_b]
            }
            2 => {
                memory[address_b] * memory[address_b]
            }
            99 => {
                return memory[0];
            }
            c => panic!("Invalid command value : {}", c)
        }
    }
    //return result of the program stored at address 0
    memory[0]
}

fn main() {
    let codes:Vec<i32> = fs::read_to_string("input.txt")
        .expect("Something went wrong reading the file")
        .split(',')
        .filter_map(|v| v.parse::<i32>().ok())
        .collect();

    let target_value = 19690720;

    for noun in 0..100 {
        for verb in 0..100 {
            let mut current_memory = codes.clone();
            //init noun and verb
            current_memory[1] = noun;
            current_memory[2] = verb;

            let current_result = run_program(&mut current_memory);

            if current_result == target_value {
                println!("Target value reached, noun: {}, verb {}", noun, verb);
                println!("Result = {}", 100*noun + verb);
                break;
            }
        }
    }
}

Note : the ".ok()" filter might be dangerous in your stream ! I had a problem for day 5 where I had used wrong types for the instructions, and when the parsing failed they were discarded : I was missing instructions in my "codes" Vec ! You might want to fail if this happens instead, it's way easier to debug...

\$\endgroup\$
2
  • \$\begingroup\$ Nice review. Welcome to Code Review! \$\endgroup\$
    – Edward
    Dec 9, 2019 at 11:19
  • \$\begingroup\$ Yes, thanks a lot for taking the time doing the review! That's indeed very helpful to me :) Also good point regarding the input parsing - didn't have any issues yet (starting day 4 next), but since the input files tend to be quite long this might indeed be tedious to debug otherwise! \$\endgroup\$
    – cpattern
    Dec 9, 2019 at 18:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.