categories | tags | Aimee's Blog
Aimee's Study Notes

It is updated automatically after each commit to the org-notes repo. It was last updated on Sep 20, 2022 16:16 UTC.


This page was created/modified in commit 499c6ad "update aoc" on 2021-01-31.
Markdown source of this page

Advent of Code

categories: hacking

tags: rust


Description/Summary

Rust programming practice

Content

GitHub repo: https://github.com/Aimeedeer/adventofcode-2020

Read and Learned

Day8

Assembly-like program. Learned a bit CS.

I am not happy with my code, though.

Day7

Started using lazy_static:

extern crate lazy_static;
use lazy_static::lazy_static;

lazy_static! {
    static ref RE: Regex = Regex::new(r"^(\w+ \w+) bags contain (.*)$").unwrap();
}

Recursive functions in Rust:

fn is_my_bag_in_map(candidate_bag: &str, map: &HashMap<String, HashMap<String, u32>>) -> bool {
    ...
}

fn bags_contained(candidate_bag: &str, map: &HashMap<String, HashMap<String, u32>>) -> u32 {
    ...
}

Day6

An easy puzzle, counting chars.

Day5

Day 5: Binary Boarding

Use const for 8. The magical number is defined in the problem description.

const NUM_COLUMNS: u32 = 8;
let seat_id = row_id * NUM_COLUMNS + column_id;

Abstract functions get_row_id and get_column_id as search_id.

fn get_row_id(input: &str) -> Result<u32> {
    search_id(input, 128, 'F', 'B', "row id")
}

fn get_column_id(input: &str) -> Result<u32> {
    search_id(input, 8, 'L', 'R', "column id")
}

fn search_id(
    input: &str,
    len: u32,
    match_char_one: char,
    match_char_two: char,
    msg: &str
) -> Result<u32> {
    let range = (0..len).into_iter().collect::<Vec<_>>();
    let mut range = range.as_slice();

    for c in input.chars() {
      let temp_len = range.len();
      let (one, two) = range.split_at(temp_len/2);

      if c == match_char_one {
          range = one;
      } else if c == match_char_two {
          range = two;
      } else {
          bail!("get {} failed", msg);
      }
    }

    let id = range[0];
    Ok(id)
}

Learned (0..10).into_iter(), and bail instead of anyhow for returning a result with an error message.

Day4

Day 4: Passport Processing

Learned to organize a mod:

Write a generic fn inside another function:

fn validate<T>(dest: &mut Option<T>,
             reference: &str,
             v: impl FnOnce(&str) -> Result<T>) {
    *dest = v(reference).ok();
}

for raw_item in raw_passport {
    let item = raw_item.split(':').collect::<Vec<_>>();
    match item[0] {
      "pid" => validate(&mut new_passport.pid, item[1], validate_pid),
      "cid" => validate(&mut new_passport.cid, item[1], validate_cid),
      "eyr" => validate(&mut new_passport.eyr, item[1], validate_eyr),
      "byr" => validate(&mut new_passport.byr, item[1], validate_byr),
      "iyr" => validate(&mut new_passport.iyr, item[1], validate_iyr),
      "ecl" => validate(&mut new_passport.ecl, item[1], validate_ecl),
      "hcl" => validate(&mut new_passport.hcl, item[1], validate_hcl),
      "hgt" => validate(&mut new_passport.hgt, item[1], validate_hgt),
      _ => {},
    };
}

Put parsing function in another method and make rules as a reference:

fn parse_and_capture<T: FromStr>(rule: &str, input: &str, msg: &str) -> Result<T> {
    let re = Regex::new(rule).unwrap();
    let caps = re.captures(input).ok_or(anyhow!("invalid {}: {}", msg, input))?;

    let output = caps[1].parse::<T>().map_err(|_| anyhow!("error parsing {}: {}", msg, input))?;
    Ok(output)
}

We can also set Regex to a global varial so that it doesn’t need to be created each time in the loop. Burntsushi’s code (in 2018) is a good example:

lazy_static! {
    static ref RE: Regex = Regex::new(r"(?x)
        \[
            (?P<year>[0-9]{4})-(?P<month>[0-9]{2})-(?P<day>[0-9]{2})
            \s+
            (?P<hour>[0-9]{2}):(?P<minute>[0-9]{2})
        \]
        \s+
        (?:Guard\ \#(?P<id>[0-9]+)\ begins\ shift|(?P<sleep>.+))
    ").unwrap();
}

Day3

Day 3: Toboggan Trajectory

Iterator:

// skip lines
for line in reader.lines().step_by(move_down) { ... }

// get interator's index while looping
for (line_index, line_value) in reader.lines().enumerate() { ... }

Put mutable variables in the same block of code while changing their values. For example, the index below.

for line in reader.lines().step_by(move_down) {
    let rules = line?;
    let rules = rules.chars().collect::<Vec<char>>();
    let char_num = rules.len();

    if rules[index] == '#' {
      tree_num += 1;
    }

    index += move_right;
    index %= char_num;
}

Day2

Day 2: Password Philosophy

Use regex for parsing String.

let re = Regex::new(r"(\d+)-(\d+) ([[:alpha:]]): ([[:alpha:]]+)")?;

Regex Syntax

Use xx.get(index) instead of xx[index], in case that index number is out of range.

let password = password.chars().collect::<Vec<char>>();

if password.get(index_1) == password.get(index_2) {
    continue;
}

anyhow error handling.

let caps = re.captures(&line).ok_or(anyhow!("parse line"))?;

char counting.

let num_valid_char = password.chars().filter(|c| *c == valid_char).count();

Day1

Day 1: Report Repair

His code:

use itertools::Itertools;

fn main() -> anyhow::Result<()> {
    let (a, b, c) = include_str!("input.txt")
        .split('\n')
        .map(str::parse::<i64>)
        .collect::<Result<Vec<_>, _>>()?
        .into_iter()
        .tuple_combinations()
        .find(|(a, b, c)| a + b + c == 2020)
        .expect("no tuple of length 3 had a sum of 2020");

    dbg!(a + b + c);
    dbg!(a * b * c);

    Ok(())
}

From Rust std: std/iter/trait.Iterator

map() is conceptually similar to a for loop. However, as map() is lazy, it is best used when you’re already working with other iterators. If you’re doing some sort of looping for a side effect, it’s considered more idiomatic to use for than map().

tuple_combinations


This site is generated with ox-hugo for Emacs/Org-mode + hugo-bare-min-theme [Aimee's Study Notes]