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 main
first- Data types and their impls second
- Functions that are used in
fn main
follow - 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 afor
loop. 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 usefor
thanmap()
.