Advent of Code
categories: hacking
tags: rust
Description/Summary
Rust programming practiceContent
GitHub repo: https://github.com/Aimeedeer/adventofcode-2020
Read and Learned
- Rust Macro
- Concise Control Flow with
if let - Differences between runtime and compile time. Answers from Stackoverflow: https://stackoverflow.com/questions/846103/runtime-vs-compile-time
- Abstract Syntax Tree (AST)
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
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
Learned to organize a mod:
fn mainfirst- Data types and their impls second
- Functions that are used in
fn mainfollow - Detailed functions(sub functions) that are used in previous functions
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
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
Use regex for parsing String.
let re = Regex::new(r"(\d+)-(\d+) ([[:alpha:]]): ([[:alpha:]]+)")?;
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
- Source code: https://github.com/Aimeedeer/adventofcode-2020/tree/master/day1
- Question: a better performance solution than 3 loops?
- Other one’s practice. Good to learn another totally different approach.
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 aforloop. However, asmap()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 useforthanmap().