[TASK] Day 4 + performance improvements
This commit is contained in:
12
src/day1.rs
12
src/day1.rs
@@ -1,6 +1,5 @@
|
||||
use crate::day_solver::DaySolver;
|
||||
|
||||
use super::util;
|
||||
use crate::util::into_lines;
|
||||
|
||||
const NUMBERS: [[&'static str; 2]; 10] = [
|
||||
["0", "zero"],
|
||||
@@ -21,13 +20,10 @@ pub struct Day1 {
|
||||
|
||||
impl Day1 {
|
||||
|
||||
pub fn create() -> Self {
|
||||
let lines = util::read_file("input/day1.txt");
|
||||
// let lines = util::read_file("input/dayX.txt");
|
||||
|
||||
// Put the input into the day struct
|
||||
pub fn create(input: String) -> Self {
|
||||
// Put the input into the day struct
|
||||
return Day1 {
|
||||
lines
|
||||
lines: into_lines(input),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use crate::day_solver::DaySolver;
|
||||
|
||||
use super::util;
|
||||
|
||||
pub struct Day2 {
|
||||
games: Vec<Game>
|
||||
}
|
||||
@@ -19,9 +17,9 @@ struct Turn {
|
||||
|
||||
impl Day2 {
|
||||
|
||||
pub fn create() -> Self {
|
||||
pub fn create(input: String) -> Self {
|
||||
return Day2 {
|
||||
games: util::read_file("input/day2.txt").iter()
|
||||
games: input.lines()
|
||||
.map(|s| {
|
||||
let mut s_spl = s.split(": ");
|
||||
let game_id: u32 = s_spl.next().unwrap()[5..].parse().unwrap();
|
||||
|
||||
@@ -3,8 +3,6 @@ use std::collections::HashSet;
|
||||
use crate::day_solver::DaySolver;
|
||||
use crate::util::Coord;
|
||||
|
||||
use super::util;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Day3 {
|
||||
numbers: Vec<(Coord<usize>, u32)>,
|
||||
@@ -14,15 +12,13 @@ pub struct Day3 {
|
||||
|
||||
impl Day3 {
|
||||
|
||||
pub fn create(input_filename: String) -> Self {
|
||||
let lines = util::read_file(&input_filename);
|
||||
// let lines = util::read_file("input/dayX.txt");
|
||||
pub fn create(input: String) -> Self {
|
||||
|
||||
let mut numbers = Vec::new();
|
||||
let mut symbols = HashSet::new();
|
||||
let mut gears = Vec::new();
|
||||
|
||||
for (y, line) in lines.iter().enumerate() {
|
||||
for (y, line) in input.lines().enumerate() {
|
||||
let mut cur_num= Vec::new();
|
||||
for (x, c) in line.char_indices() {
|
||||
if c.is_numeric() {
|
||||
|
||||
91
src/day4.rs
Normal file
91
src/day4.rs
Normal file
@@ -0,0 +1,91 @@
|
||||
use crate::day_solver::DaySolver;
|
||||
|
||||
pub struct Day4 {
|
||||
cards: Vec<Card>
|
||||
}
|
||||
|
||||
struct Card {
|
||||
// id: u32,
|
||||
winning: Vec<u32>,
|
||||
having: Vec<u32>
|
||||
}
|
||||
|
||||
impl Card {
|
||||
fn win_count(&self) -> usize {
|
||||
self.having.iter().filter(|n| self.winning.contains(n)).count()
|
||||
}
|
||||
fn score(&self) -> u32 {
|
||||
let win_count = self.win_count();
|
||||
if win_count == 0 { return 0 };
|
||||
return 1 << (win_count - 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl Day4 {
|
||||
|
||||
pub fn create(input: String) -> Self {
|
||||
let lines = input.lines();
|
||||
// let lines = util::read_file("input/dayX.txt");
|
||||
|
||||
// For performance reasons, let's first figure out the how many winning numbers and "having" numbers we get per card
|
||||
let (_, right) = input.lines().next().unwrap().split_once(":").unwrap();
|
||||
let (win_str, having_str) = right.split_once("|").unwrap();
|
||||
let win_count = win_str.trim().split_whitespace().count();
|
||||
let having_count = having_str.trim().split_whitespace().count();
|
||||
|
||||
// Put the input into the day struct
|
||||
return Day4 {
|
||||
cards: lines
|
||||
.map(|l| {
|
||||
let (_, right) = l.split_once(": ").unwrap();
|
||||
let (win_str, having_str) = right.split_once("|").unwrap();
|
||||
|
||||
let mut winning_vec = Vec::with_capacity(win_count + 1);
|
||||
winning_vec.extend(win_str.trim().split_whitespace().map(|n| n.parse::<u32>().unwrap()));
|
||||
let mut having_vec = Vec::with_capacity(having_count + 1);
|
||||
having_vec.extend(having_str.trim().split_whitespace().map(|n| n.parse::<u32>().unwrap()));
|
||||
Card {
|
||||
// id: left[5..].trim().parse().unwrap(),
|
||||
winning: winning_vec,
|
||||
having: having_vec
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DaySolver for Day4 {
|
||||
|
||||
|
||||
fn solve_part1(&mut self) -> String {
|
||||
return self.cards.iter().map(|c| c.score()).sum::<u32>().to_string()
|
||||
}
|
||||
|
||||
fn solve_part2(&mut self) -> String {
|
||||
let mut card_counts: Vec<u32> = vec![1; self.cards.len()];
|
||||
card_counts.fill(1);
|
||||
|
||||
for (i, card) in self.cards.iter().enumerate() {
|
||||
let copies = card_counts[i];
|
||||
let win_count = card.win_count();
|
||||
for j in 0..win_count {
|
||||
card_counts[i + j + 1] += copies
|
||||
}
|
||||
}
|
||||
|
||||
return card_counts.iter().sum::<u32>().to_string();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part1() {
|
||||
let mut day = Day4::create("input/day04_example.txt".to_string());
|
||||
assert_eq!("13", day.solve_part1());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() {
|
||||
let mut day = Day4::create("input/day04_example.txt".to_string());
|
||||
assert_eq!("30", day.solve_part2());
|
||||
}
|
||||
14
src/dayX.rs
14
src/dayX.rs
@@ -7,7 +7,7 @@ pub struct DayX {
|
||||
|
||||
impl DayX {
|
||||
|
||||
pub fn create() -> Self {
|
||||
pub fn create(input_filename: String) -> Self {
|
||||
let lines = util::read_file("input/dayX_example.txt");
|
||||
// let lines = util::read_file("input/dayX.txt");
|
||||
|
||||
@@ -27,3 +27,15 @@ impl DaySolver for DayX {
|
||||
return 0.to_string();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part1() {
|
||||
let mut day = DayX::create("input/dayX_example.txt".to_string());
|
||||
assert_eq!("4361", day.solve_part1());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_part2() {
|
||||
let mut day = DayX::create("input/dayX_example.txt".to_string());
|
||||
assert_eq!("467835", day.solve_part2());
|
||||
}
|
||||
|
||||
47
src/main.rs
47
src/main.rs
@@ -4,15 +4,18 @@ use std::time::Instant;
|
||||
use crate::day1::Day1;
|
||||
use crate::day2::Day2;
|
||||
use crate::day3::Day3;
|
||||
use crate::day4::Day4;
|
||||
use crate::day_solver::DaySolver;
|
||||
use crate::util::read_file;
|
||||
|
||||
mod util;
|
||||
mod day1;
|
||||
mod day2;
|
||||
mod day3;
|
||||
mod day4;
|
||||
mod day_solver;
|
||||
|
||||
const MAX_DAY: u8 = 3;
|
||||
const MAX_DAY: u8 = 4;
|
||||
const DEFAULT_BENCHMARK_AMOUNT: u32 = 100;
|
||||
|
||||
fn main() {
|
||||
@@ -54,13 +57,18 @@ fn main() {
|
||||
|
||||
println!("Executed {} rounds in {:.3} s ({} μs, or {} μs on average per round)", bench_results.len(), total_time as f64 / 1000000f64, total_time, total_time / bench_amount as u128);
|
||||
print_bench_result(&bench_results, |b| b.total, "Execution");
|
||||
print_bench_result(&bench_results, |b| b.read_file, "Reading file");
|
||||
print_bench_result(&bench_results, |b| b.init, "Initialization");
|
||||
print_bench_result(&bench_results, |b| b.part1, "Part 1");
|
||||
print_bench_result(&bench_results, |b| b.part2, "Part 2");
|
||||
print_bench_result(&bench_results, |b| b.part1 + b.part2, "Part 1 & 2 together");
|
||||
|
||||
println!("First time took {} μs (init {} μs, part 1: {} μs, part 2: {} μs)", first_run_bench.total, first_run_bench.init, first_run_bench.part1, first_run_bench.part2);
|
||||
|
||||
println!("First time took {} μs (read {} μs, init {} μs, part 1: {} μs, part 2: {} μs)", first_run_bench.total, first_run_bench.read_file, first_run_bench.init, first_run_bench.part1, first_run_bench.part2);
|
||||
println!("Average: {} μs (read {} μs, init {} μs, part 1: {} μs, part 2: {} μs)",
|
||||
avg_bench(&bench_results, |b| b.total),
|
||||
avg_bench(&bench_results, |b| b.read_file),
|
||||
avg_bench(&bench_results, |b| b.init),
|
||||
avg_bench(&bench_results, |b| b.part1),
|
||||
avg_bench(&bench_results, |b| b.part2));
|
||||
} else {
|
||||
println!("Execution took {} μs (init {} μs, part 1: {} μs, part 2: {} μs)", first_run_bench.total, first_run_bench.init, first_run_bench.part1, first_run_bench.part2);
|
||||
}
|
||||
@@ -68,19 +76,20 @@ fn main() {
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct AocBenchResult {
|
||||
read_file: u128,
|
||||
init: u128,
|
||||
part1: u128,
|
||||
part2: u128,
|
||||
total: u128
|
||||
}
|
||||
|
||||
fn build_day_solver(day: u8) -> Option<Box<dyn DaySolver>> {
|
||||
fn build_day_solver(day: u8, input: String) -> Option<Box<dyn DaySolver>> {
|
||||
|
||||
let input_filename = format!("input/day{:02}.txt",day);
|
||||
match day {
|
||||
1 => Some(Box::new(Day1::create())),
|
||||
2 => Some(Box::new(Day2::create())),
|
||||
3 => Some(Box::new(Day3::create(input_filename))),
|
||||
1 => Some(Box::new(Day1::create(input))),
|
||||
2 => Some(Box::new(Day2::create(input))),
|
||||
3 => Some(Box::new(Day3::create(input))),
|
||||
4 => Some(Box::new(Day4::create(input))),
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
@@ -88,7 +97,13 @@ fn build_day_solver(day: u8) -> Option<Box<dyn DaySolver>> {
|
||||
fn solve(day: u8, silent: bool) -> AocBenchResult {
|
||||
|
||||
let now = Instant::now();
|
||||
let (solver, init_time) = bench(|| build_day_solver(day));
|
||||
|
||||
let (input, read_file_time) = bench(|| {
|
||||
let input_filename = format!("input/day{:02}.txt",day);
|
||||
read_file(&input_filename)
|
||||
});
|
||||
|
||||
let (solver, init_time) = bench(|| build_day_solver(day, input.to_owned()));
|
||||
|
||||
let part1_time: u128;
|
||||
let part2_time: u128;
|
||||
@@ -97,7 +112,7 @@ fn solve(day: u8, silent: bool) -> AocBenchResult {
|
||||
|
||||
let(part1, pt1_time) = bench(|| s.solve_part1());
|
||||
part1_time = pt1_time;
|
||||
|
||||
|
||||
if !silent {
|
||||
println!("Day {} Part 1: {}", day, part1);
|
||||
}
|
||||
@@ -111,6 +126,7 @@ fn solve(day: u8, silent: bool) -> AocBenchResult {
|
||||
}
|
||||
|
||||
return AocBenchResult{
|
||||
read_file: read_file_time,
|
||||
init: init_time,
|
||||
part1: part1_time,
|
||||
part2: part2_time,
|
||||
@@ -132,6 +148,14 @@ fn print_bench_result<F>(bench_results: &Vec<AocBenchResult>, f: F, bench_part_d
|
||||
benches[0], benches[benches.len() - 1], benches[benches.len() / 2])
|
||||
}
|
||||
|
||||
fn avg_bench<F>(bench_results: &Vec<AocBenchResult>, f: F) -> u128
|
||||
where
|
||||
F: FnMut(&AocBenchResult) -> u128 {
|
||||
|
||||
let benches: Vec<u128> = bench_results.iter().map(f).collect();
|
||||
return benches.iter().sum::<u128>() / (bench_results.len() as u128)
|
||||
}
|
||||
|
||||
fn run_once(single_day: bool, day: u8, silent: bool, bench_results: &mut Vec<AocBenchResult>) {
|
||||
|
||||
|
||||
@@ -149,6 +173,7 @@ fn solve_all(silent: bool) -> AocBenchResult {
|
||||
bench_results.push(solve(day, silent));
|
||||
}
|
||||
return AocBenchResult {
|
||||
read_file: bench_results.iter().map(|t| t.read_file).sum(),
|
||||
init: bench_results.iter().map(|t| t.init).sum(),
|
||||
part1: bench_results.iter().map(|t| t.part1).sum(),
|
||||
part2: bench_results.iter().map(|t| t.part2).sum(),
|
||||
|
||||
12
src/util.rs
12
src/util.rs
@@ -3,16 +3,14 @@ use std::fmt::Display;
|
||||
|
||||
use num::Integer;
|
||||
|
||||
pub fn read_file(filename: &str) -> Vec<String> {
|
||||
pub fn read_file(filename: &str) -> String {
|
||||
|
||||
let contents = fs::read_to_string(filename)
|
||||
return fs::read_to_string(filename)
|
||||
.expect("Couldn't read file!");
|
||||
}
|
||||
|
||||
let mut res: Vec<String> = Vec::new();
|
||||
contents.lines().for_each(|l| res.push(String::from(l)));
|
||||
|
||||
res
|
||||
|
||||
pub fn into_lines(input: String) -> Vec<String> {
|
||||
return input.lines().map(|l| l.to_owned()).collect();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user