[TASK] Day 4 + performance improvements

This commit is contained in:
2023-12-04 23:34:23 +01:00
parent 9ebb5c851a
commit ead47e232e
16 changed files with 385 additions and 38 deletions

View File

@@ -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),
}
}

View File

@@ -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();

View File

@@ -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
View 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());
}

View File

@@ -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());
}

View File

@@ -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(),

View File

@@ -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();
}