Compare commits

..

No commits in common. "603ee1827d494a83ef86851e27b49f6320db9fdf" and "4970f247f3d1c63e274abcc4345b3ea03964d52e" have entirely different histories.

19 changed files with 162 additions and 512 deletions

207
Cargo.lock generated
View file

@ -8,10 +8,10 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
"colonnade",
"derive_more", "derive_more",
"env_logger", "env_logger",
"log", "log",
"prettytable-rs",
"ron", "ron",
"serde", "serde",
"solutions_advent_of_code_2023", "solutions_advent_of_code_2023",
@ -97,12 +97,6 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.16" version = "4.5.16"
@ -143,6 +137,16 @@ version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
[[package]]
name = "colonnade"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "833230a1f610c0f3418a80b03c387be9de15b1172b3785b702961ead07e3e475"
dependencies = [
"strip-ansi-escapes",
"unicode-segmentation",
]
[[package]] [[package]]
name = "colorchoice" name = "colorchoice"
version = "1.0.2" version = "1.0.2"
@ -155,7 +159,7 @@ version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
dependencies = [ dependencies = [
"encode_unicode 0.3.6", "encode_unicode",
"lazy_static", "lazy_static",
"libc", "libc",
"windows-sys", "windows-sys",
@ -170,27 +174,6 @@ dependencies = [
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]]
name = "csv"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
dependencies = [
"csv-core",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "derive_more" name = "derive_more"
version = "1.0.0" version = "1.0.0"
@ -213,39 +196,12 @@ dependencies = [
"unicode-xid", "unicode-xid",
] ]
[[package]]
name = "dirs-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [
"cfg-if",
"dirs-sys-next",
]
[[package]]
name = "dirs-sys-next"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]] [[package]]
name = "encode_unicode" name = "encode_unicode"
version = "0.3.6" version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "encode_unicode"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
[[package]] [[package]]
name = "env_filter" name = "env_filter"
version = "0.1.2" version = "0.1.2"
@ -269,29 +225,12 @@ dependencies = [
"log", "log",
] ]
[[package]]
name = "getrandom"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.5.0" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hermit-abi"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
[[package]] [[package]]
name = "humantime" name = "humantime"
version = "2.1.0" version = "2.1.0"
@ -310,29 +249,12 @@ dependencies = [
"similar", "similar",
] ]
[[package]]
name = "is-terminal"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b"
dependencies = [
"hermit-abi",
"libc",
"windows-sys",
]
[[package]] [[package]]
name = "is_terminal_polyfill" name = "is_terminal_polyfill"
version = "1.70.1" version = "1.70.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
[[package]]
name = "itoa"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.5.0" version = "1.5.0"
@ -345,16 +267,6 @@ version = "0.2.158"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439"
[[package]]
name = "libredox"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
dependencies = [
"bitflags",
"libc",
]
[[package]] [[package]]
name = "linked-hash-map" name = "linked-hash-map"
version = "0.5.6" version = "0.5.6"
@ -373,20 +285,6 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "prettytable-rs"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eea25e07510aa6ab6547308ebe3c036016d162b8da920dbb079e3ba8acf3d95a"
dependencies = [
"csv",
"encode_unicode 1.0.0",
"is-terminal",
"lazy_static",
"term",
"unicode-width",
]
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.86" version = "1.0.86"
@ -405,17 +303,6 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "redox_users"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
dependencies = [
"getrandom",
"libredox",
"thiserror",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.10.6" version = "1.10.6"
@ -457,18 +344,6 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "rustversion"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
[[package]]
name = "ryu"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.209" version = "1.0.209"
@ -505,6 +380,15 @@ dependencies = [
"thiserror", "thiserror",
] ]
[[package]]
name = "strip-ansi-escapes"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa"
dependencies = [
"vte",
]
[[package]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"
@ -522,17 +406,6 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "term"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f"
dependencies = [
"dirs-next",
"rustversion",
"winapi",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.63" version = "1.0.63"
@ -565,12 +438,6 @@ version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-width"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
[[package]] [[package]]
name = "unicode-xid" name = "unicode-xid"
version = "0.2.5" version = "0.2.5"
@ -584,32 +451,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "wasi" name = "vte"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [ dependencies = [
"winapi-i686-pc-windows-gnu", "utf8parse",
"winapi-x86_64-pc-windows-gnu", "vte_generate_state_changes",
] ]
[[package]] [[package]]
name = "winapi-i686-pc-windows-gnu" name = "vte_generate_state_changes"
version = "0.4.0" version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" checksum = "2e369bee1b05d510a7b4ed645f5faa90619e05437111783ea5848f28d97d3c2e"
dependencies = [
[[package]] "proc-macro2",
name = "winapi-x86_64-pc-windows-gnu" "quote",
version = "0.4.0" ]
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"

View file

@ -15,4 +15,4 @@ env_logger = "0.11.5"
log = "0.4.22" log = "0.4.22"
anyhow = "1.0.86" anyhow = "1.0.86"
ron = "0.8.1" ron = "0.8.1"
prettytable-rs = "0.10.0" colonnade = "1.3.3"

View file

@ -1,52 +1,43 @@
pub use conducted_benchmark::ConductedBenchmark; pub use benchmark_result::BenchmarkResult;
pub use file_to_benchmark::FileToBenchmark; pub use file_to_benchmark::FileToBenchmark;
use prettytable::Table; mod benchmark_result;
mod conducted_benchmark;
mod file_to_benchmark; mod file_to_benchmark;
mod row_builder;
mod row_building; mod row_building;
use crate::{ use crate::{cli::BenchmarkCli, solving_given::solve_given, AppResult};
cli::BenchmarkCli,
solving_given::{solve_given, NoSolutionFound},
AppError, AppResult,
};
use anyhow::anyhow; use anyhow::anyhow;
use colonnade::Colonnade;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
pub type BenchmarkResult = Result<ConductedBenchmark, NoSolutionFound>; fn calc_average(sum: Duration, nanos_count: u64) -> Duration {
let average_raw = sum.div_duration_f64(Duration::from_nanos(nanos_count));
pub fn execute_benchmark(args: &BenchmarkCli) -> AppResult<Table> { Duration::from_nanos(average_raw.round() as u64)
}
pub fn execute_benchmark(args: &BenchmarkCli) -> AppResult<String> {
let loaded = load_benchmarks(args)?; let loaded = load_benchmarks(args)?;
let benchmarked = loaded
let benchmarked = loaded.into_iter().map(solve_and_keep_track_of_runtime); .into_iter()
.map(solve_and_keep_track_of_runtime)
let mut sum: Duration = Duration::ZERO; .collect::<AppResult<Vec<BenchmarkResult>>>()?;
let mut count: u64 = 0; let sum: Duration = benchmarked.iter().map(|result| result.how_long()).sum();
let after_header = row_building::create_rows_for_every_solutions(benchmarked, |to_add| { let average = calc_average(sum, benchmarked.len() as u64);
sum += to_add; let header = row_building::create_header();
count += 1; let after_header = row_building::create_rows_for_every_solutions(benchmarked);
}); let table: Vec<Vec<String>> = after_header
let average = calc_average(sum, count);
let mut table = Table::new();
let rows = after_header
.into_iter() .into_iter()
.chain(row_building::create_sum_row(sum)) .chain(row_building::create_sum_row(sum))
.chain(row_building::create_average_row(average)) .chain(row_building::create_average_row(average))
.chain(row_building::create_header()); .chain(header)
for next_row in rows { .collect();
table.add_row(next_row); let lines = Colonnade::new(5, 100).unwrap().tabulate(table)?;
}
Ok(table) Ok(lines.join("\n"))
} }
fn solve_and_keep_track_of_runtime( fn solve_and_keep_track_of_runtime(
(benchmark, content): (FileToBenchmark, String), (benchmark, content): (FileToBenchmark, String),
) -> Result<ConductedBenchmark, NoSolutionFound> { ) -> AppResult<BenchmarkResult> {
let (day, task) = (benchmark.given_day(), benchmark.given_task()); let (day, task) = (benchmark.given_day(), benchmark.given_task());
let before = Instant::now(); let before = Instant::now();
let actual_ouput = solve_given(day, task, &content)?; let actual_ouput = solve_given(day, task, &content)?;
@ -54,14 +45,15 @@ fn solve_and_keep_track_of_runtime(
let how_long = after - before; let how_long = after - before;
let result = ConductedBenchmark::new(benchmark, actual_ouput, how_long); let result = BenchmarkResult::new(benchmark, actual_ouput, how_long);
Ok(result) Ok(result)
} }
fn load_benchmarks(args: &BenchmarkCli) -> AppResult<Vec<(FileToBenchmark, String)>> { fn load_benchmarks(args: &BenchmarkCli) -> AppResult<Vec<(FileToBenchmark, String)>> {
args.files() let benchmarks = args
.files()
.iter() .iter()
.flat_map(|path| { .map(|path| {
let content = std::fs::read_to_string(path).map_err(|error| { let content = std::fs::read_to_string(path).map_err(|error| {
anyhow!( anyhow!(
"Could not read benchmark file at {:?}\n\ "Could not read benchmark file at {:?}\n\
@ -78,9 +70,14 @@ fn load_benchmarks(args: &BenchmarkCli) -> AppResult<Vec<(FileToBenchmark, Strin
error error
) )
})?; })?;
Ok::<_, AppError>(parsed) Ok(parsed)
}) })
.flatten() .collect::<AppResult<Vec<Vec<FileToBenchmark>>>>()?
.into_iter()
.flatten();
benchmarks
.into_iter()
.map(|benchmark| { .map(|benchmark| {
let where_to_look = benchmark.where_to_look(); let where_to_look = benchmark.where_to_look();
let content = std::fs::read_to_string(where_to_look).map_err(|error| { let content = std::fs::read_to_string(where_to_look).map_err(|error| {
@ -95,10 +92,5 @@ fn load_benchmarks(args: &BenchmarkCli) -> AppResult<Vec<(FileToBenchmark, Strin
})?; })?;
Ok((benchmark, content)) Ok((benchmark, content))
}) })
.collect() .collect::<AppResult<Vec<(FileToBenchmark, String)>>>()
}
fn calc_average(sum: Duration, nanos_count: u64) -> Duration {
let average_raw = sum.div_duration_f64(Duration::from_nanos(nanos_count));
Duration::from_nanos(average_raw.round() as u64)
} }

View file

@ -1,25 +1,21 @@
use std::{ use std::time::Duration;
path::{Path, PathBuf},
time::Duration,
};
use crate::cli::{GivenDay, GivenTask}; use crate::cli::{GivenDay, GivenTask};
use super::FileToBenchmark; use super::FileToBenchmark;
#[derive(Debug)] #[derive(Debug)]
pub struct ConductedBenchmark { pub struct BenchmarkResult {
task: GivenTask, task: GivenTask,
day: GivenDay, day: GivenDay,
how_long: Duration, how_long: Duration,
maybe_different_actual_output: Option<String>, maybe_different_actual_output: Option<String>,
expected_output: String, expected_output: String,
path_of_input: PathBuf,
} }
impl ConductedBenchmark { impl BenchmarkResult {
pub fn new(benchmarking: FileToBenchmark, actual_ouput: String, how_long: Duration) -> Self { pub fn new(benchmarking: FileToBenchmark, actual_ouput: String, how_long: Duration) -> Self {
let (day, task, expected_output, path_of_input) = benchmarking.into(); let (day, task, expected_output) = benchmarking.into();
let maybe_different_actual_output = if actual_ouput == expected_output { let maybe_different_actual_output = if actual_ouput == expected_output {
None None
} else { } else {
@ -31,7 +27,6 @@ impl ConductedBenchmark {
how_long, how_long,
maybe_different_actual_output, maybe_different_actual_output,
expected_output, expected_output,
path_of_input,
} }
} }
@ -68,8 +63,4 @@ impl ConductedBenchmark {
let mili_secs = how_long.as_nanos() % 1_000_000_000; let mili_secs = how_long.as_nanos() % 1_000_000_000;
format!("{}:{}", secs, mili_secs) format!("{}:{}", secs, mili_secs)
} }
pub fn path_of_input(&self) -> &Path {
&self.path_of_input
}
} }

View file

@ -19,14 +19,9 @@ pub struct FileToBenchmark {
expected_output: String, expected_output: String,
} }
impl From<FileToBenchmark> for (GivenDay, GivenTask, String, PathBuf) { impl From<FileToBenchmark> for (GivenDay, GivenTask, String) {
fn from(val: FileToBenchmark) -> Self { fn from(val: FileToBenchmark) -> Self {
( (val.given_day, val.given_task, val.expected_output)
val.given_day,
val.given_task,
val.expected_output,
val.where_to_look,
)
} }
} }

View file

@ -1,174 +0,0 @@
use std::{borrow::Cow, path::PathBuf, time::Duration};
const AVERAGE: &str = "Average";
const TOTAL: &str = "total";
use prettytable::{color, Row};
use crate::{
benchmarking::ConductedBenchmark,
cli::{GivenDay, GivenTask},
constants,
solving_given::NoSolutionFound,
};
type OptionalContent = Option<Cow<'static, str>>;
#[derive(Debug, Default)]
pub struct RowBuilder {
day: OptionalContent,
task: OptionalContent,
taken_time: OptionalContent,
actual_result: OptionalContent,
expected_result: OptionalContent,
path_to_input: OptionalContent,
}
impl From<NoSolutionFound> for RowBuilder {
fn from(value: NoSolutionFound) -> Self {
match value {
NoSolutionFound::DayNotFound(day) => Self::no_day_found(day),
NoSolutionFound::TaskNotFound { day, task } => Self::no_task_found(day, task),
}
}
}
impl From<ConductedBenchmark> for RowBuilder {
fn from(value: ConductedBenchmark) -> Self {
Self::new(
value.day(),
value.task(),
value.how_long(),
value
.maybe_different_actual_output()
.map(ToString::to_string)
.unwrap_or_default(),
value.expected_output().to_string(),
value.path_of_input().to_path_buf(),
)
}
}
fn into_borrowed_cell(some_thing: &'static str) -> OptionalContent {
Some(Cow::Borrowed(some_thing))
}
fn into_owned_cell(some_thing: impl ToString) -> OptionalContent {
Some(Cow::Owned(some_thing.to_string()))
}
fn into_owned_cell_with<T>(some_thing: T, on_convert: impl Fn(T) -> String) -> OptionalContent {
Some(Cow::Owned(on_convert(some_thing)))
}
impl RowBuilder {
pub fn new(
day: GivenDay,
task: GivenTask,
taken_time: Duration,
actual_result: String,
expected_result: String,
path: PathBuf,
) -> Self {
Self {
day: into_owned_cell(day),
task: into_owned_cell(task),
taken_time: into_owned_cell_with(
taken_time,
ConductedBenchmark::convert_duration_to_secs_and_mili_txt,
),
actual_result: into_owned_cell(actual_result),
expected_result: into_owned_cell(expected_result),
path_to_input: into_owned_cell_with(path, |path| path.to_string_lossy().to_string()),
}
}
pub fn no_day_found(wrong_day: GivenDay) -> Self {
let day = into_owned_cell_with(wrong_day, |to_convert| {
format!("Solution found for day {}", to_convert)
});
Self {
day,
..Default::default()
}
}
pub fn no_task_found(right_day: GivenDay, wrong_task: GivenTask) -> Self {
let day = into_owned_cell(right_day);
let task = into_owned_cell_with(wrong_task, |to_convert| {
format!("No solution found for task {}", to_convert)
});
Self {
day,
task,
..Default::default()
}
}
pub fn header() -> Self {
Self {
day: into_borrowed_cell("Day"),
task: into_borrowed_cell("Task"),
taken_time: into_borrowed_cell("How long (Seconds:Nano)"),
actual_result: into_borrowed_cell("Actual"),
expected_result: into_borrowed_cell("Expected"),
path_to_input: into_borrowed_cell("Used input file"),
}
}
pub fn average(average: Duration) -> Self {
Self::new_aggreate(AVERAGE, average)
}
pub fn total(total: Duration) -> Self {
Self::new_aggreate(TOTAL, total)
}
fn this_or_placeholder(value: OptionalContent) -> prettytable::Cell {
let value = value.unwrap_or(Cow::Borrowed(constants::PLACEHOLDER_IN_BENCHMARK_REPORTS));
prettytable::Cell::new(&value)
}
fn color_red_if_non_static_value(actual_result: OptionalContent) -> prettytable::Cell {
let has_different_acutual_than_expected_and_is_no_label = actual_result
.as_ref()
.is_some_and(|might_be_borrow| matches!(might_be_borrow, Cow::Owned(_)));
let tmp = Self::this_or_placeholder(actual_result);
if has_different_acutual_than_expected_and_is_no_label {
tmp.with_style(prettytable::Attr::ForegroundColor(color::RED))
} else {
tmp
}
}
pub fn into_row(self) -> Row {
let might_colored_red_actual = Self::color_red_if_non_static_value(self.actual_result);
let (day, task, taken_time, actual_result, expected_result, path) = (
Self::this_or_placeholder(self.day),
Self::this_or_placeholder(self.task),
Self::this_or_placeholder(self.taken_time),
might_colored_red_actual,
Self::this_or_placeholder(self.expected_result),
Self::this_or_placeholder(self.path_to_input),
);
prettytable::Row::new(vec![
day,
task,
taken_time,
actual_result,
expected_result,
path,
])
}
fn new_aggreate(label: &str, aggregate: Duration) -> Self {
let (day, task) = (into_owned_cell(label), into_owned_cell(label));
let taken_time = into_owned_cell_with(
aggregate,
ConductedBenchmark::convert_duration_to_secs_and_mili_txt,
);
Self {
day,
task,
taken_time,
..Default::default()
}
}
}

View file

@ -1,35 +1,54 @@
pub type MatrixReport = Vec<Row>;
pub type SingleRow = std::iter::Once<Row>;
use prettytable::Row;
use super::{row_builder::RowBuilder, BenchmarkResult};
use std::time::Duration; use std::time::Duration;
pub fn create_header() -> SingleRow { use super::BenchmarkResult;
std::iter::once(RowBuilder::header().into_row()) type MatrixReport = Vec<Vec<String>>;
pub fn create_header() -> std::iter::Once<Vec<String>> {
std::iter::once(vec![
"Day".to_string(),
"Task".to_string(),
"How long (Seconds:Mili)".to_string(),
"Expected".to_string(),
"Actual".to_string(),
])
} }
pub fn create_sum_row(sum: Duration) -> SingleRow { pub fn create_sum_row(sum: Duration) -> std::iter::Once<Vec<String>> {
std::iter::once(RowBuilder::total(sum).into_row()) let passed_txt = BenchmarkResult::convert_duration_to_secs_and_mili_txt(sum);
std::iter::once(vec![
"Total".to_string(),
"Total".to_string(),
passed_txt,
"-".to_string(),
"-".to_string(),
])
} }
pub fn create_average_row(average: Duration) -> SingleRow { pub fn create_average_row(average: Duration) -> std::iter::Once<Vec<String>> {
std::iter::once(RowBuilder::average(average).into_row()) let passed_txt = BenchmarkResult::convert_duration_to_secs_and_mili_txt(average);
std::iter::once(vec![
"Average".to_string(),
"Average".to_string(),
passed_txt,
"-".to_string(),
"-".to_string(),
])
} }
pub fn create_rows_for_every_solutions( pub fn create_rows_for_every_solutions(loaded: Vec<BenchmarkResult>) -> MatrixReport {
loaded: impl IntoIterator<Item = BenchmarkResult>,
mut on_ok: impl FnMut(Duration),
) -> MatrixReport {
loaded loaded
.into_iter() .into_iter()
.map(|result| { .map(|result| {
result vec![
.inspect(|on_success| on_ok(on_success.how_long())) result.day().to_string(),
.map(RowBuilder::from) result.task().to_string(),
.unwrap_or_else(RowBuilder::from) result.how_long_as_string(),
result.expected_output().to_string(),
result
.maybe_different_actual_output()
.map(|unowned| unowned.to_string())
.unwrap_or_else(|| result.expected_output().to_string()),
]
}) })
.map(RowBuilder::into_row)
.collect() .collect()
} }

View file

@ -1,3 +1,2 @@
pub const MAX_DAY: u32 = 25; pub const MAX_DAY: u32 = 25;
pub const MAX_TASK: u32 = 2; pub const MAX_TASK: u32 = 2;
pub const PLACEHOLDER_IN_BENCHMARK_REPORTS: &str = "-";

View file

@ -1,7 +1,3 @@
use std::fmt::Display;
use prettytable::Table;
pub mod benchmarking; pub mod benchmarking;
pub mod cli; pub mod cli;
pub mod constants; pub mod constants;
@ -10,27 +6,3 @@ pub mod solving_given;
pub type AppError = anyhow::Error; pub type AppError = anyhow::Error;
pub type AppResult<T = ()> = anyhow::Result<T>; pub type AppResult<T = ()> = anyhow::Result<T>;
#[derive(Debug)]
pub enum AppOutput {
JustString(String),
PrettyTable(Table),
}
impl AppOutput {
pub fn print_std_out(&self) {
match self {
AppOutput::JustString(string) => println!("{}", string),
AppOutput::PrettyTable(table) => table.printstd(),
}
}
}
impl Display for AppOutput {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AppOutput::JustString(string) => f.write_str(string),
AppOutput::PrettyTable(table) => f.write_str(&table.to_string()),
}
}
}

View file

@ -4,7 +4,7 @@ use advent_of_code_2023::{
benchmarking::execute_benchmark, benchmarking::execute_benchmark,
cli::{AppCliArgs, AppCliSubcommands}, cli::{AppCliArgs, AppCliSubcommands},
solving_given::solve_given_from_cli, solving_given::solve_given_from_cli,
AppOutput, AppResult, AppResult,
}; };
use clap::Parser; use clap::Parser;
@ -14,7 +14,7 @@ fn main() -> ExitCode {
let solution = handle_command(&args); let solution = handle_command(&args);
match solution { match solution {
Ok(found_solution) => { Ok(found_solution) => {
found_solution.print_std_out(); println!("{}", found_solution);
ExitCode::SUCCESS ExitCode::SUCCESS
} }
Err(error) => { Err(error) => {
@ -24,13 +24,9 @@ fn main() -> ExitCode {
} }
} }
fn handle_command(args: &AppCliArgs) -> AppResult<AppOutput> { fn handle_command(args: &AppCliArgs) -> AppResult<String> {
match args.sub_command() { match args.sub_command() {
AppCliSubcommands::Solve(to_solve) => { AppCliSubcommands::Solve(to_solve) => solve_given_from_cli(to_solve),
solve_given_from_cli(to_solve).map(AppOutput::JustString) AppCliSubcommands::Benchmark(benchmark) => execute_benchmark(benchmark),
}
AppCliSubcommands::Benchmark(benchmark) => {
execute_benchmark(benchmark).map(AppOutput::PrettyTable)
}
} }
} }

View file

@ -7,23 +7,16 @@ use crate::{
AppResult, AppResult,
}; };
pub fn solve_given( pub fn solve_given(given_day: GivenDay, given_task: GivenTask, content: &str) -> AppResult<String> {
given_day: GivenDay,
given_task: GivenTask,
content: &str,
) -> Result<String, NoSolutionFound> {
let found_task = { let found_task = {
let day: u32 = given_day.into(); let day: u32 = given_day.into();
let task: u32 = given_task.into(); let task: u32 = given_task.into();
let found_day = ALL_SOLUTIONS let found_day = ALL_SOLUTIONS
.get(day.saturating_sub(1) as usize) .get(day.saturating_sub(1) as usize)
.ok_or(NoSolutionFound::DayNotFound(given_day))?; .ok_or_else(|| CouldNotSolveError::DayNotFound(day))?;
found_day found_day
.get(task.saturating_sub(1) as usize) .get(task.saturating_sub(1) as usize)
.ok_or(NoSolutionFound::TaskNotFound { .ok_or_else(|| CouldNotSolveError::TaskNotFound { day, task })
day: given_day,
task: given_task,
})
}?; }?;
let solved = (found_task)(content); let solved = (found_task)(content);
@ -47,17 +40,11 @@ fn try_read_from_file_if_demanded(args: &CliSolutionToSolve) -> io::Result<Strin
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum CouldNotSolveError { enum CouldNotSolveError {
#[error("{0}")] #[error("There is no solution for the day {0}")]
NotFound(#[from] NoSolutionFound), DayNotFound(u32),
#[error("There is not solution for task {task} under the day {day}")]
TaskNotFound { day: u32, task: u32 },
#[error("Could not read puzzel input from the given file\n {0}")] #[error("Could not read puzzel input from the given file\n {0}")]
CouldNotReadFromFile(#[from] io::Error), CouldNotReadFromFile(#[from] io::Error),
} }
#[derive(Debug, Error)]
pub enum NoSolutionFound {
#[error("There is no solution for the day {0}")]
DayNotFound(GivenDay),
#[error("There is not solution for task {task} under the day {day}")]
TaskNotFound { day: GivenDay, task: GivenTask },
}

View file

@ -21,9 +21,12 @@ fn min_location_from_given_single_seeds(
let mut current_seed = *next_seed; let mut current_seed = *next_seed;
for next_layer in layers { for next_layer in layers {
for next_mapping in next_layer { for next_mapping in next_layer {
if let Some(found_mapping) = next_mapping.map_point(current_seed) { match next_mapping.map_point(current_seed) {
current_seed = found_mapping; Some(found_mapping) => {
break; current_seed = found_mapping;
break;
}
None => (),
} }
} }
} }
@ -116,6 +119,7 @@ fn combine_single_to_ranges_for_seeds(single_seeds: &[UnsignedNumber]) -> Vec<Se
assert!(single_seeds.len() % 2 == 0); assert!(single_seeds.len() % 2 == 0);
single_seeds single_seeds
.chunks(2) .chunks(2)
.into_iter()
.map(|array| match array { .map(|array| match array {
[start, end] => { [start, end] => {
let start = *start; let start = *start;

View file

@ -1,3 +1,5 @@
use std::usize;
mod parsing; mod parsing;
mod race_record; mod race_record;
@ -9,12 +11,12 @@ pub fn solve_task_1(input: &str) -> String {
for next_record in parsed { for next_record in parsed {
how_many_times_won *= all_rounds_of_durations(next_record.time()) how_many_times_won *= all_rounds_of_durations(next_record.time())
.into_iter()
.filter(|&distance| distance > next_record.reached_distance()) .filter(|&distance| distance > next_record.reached_distance())
.count() as NumericValue; .count() as NumericValue;
} }
how_many_times_won.to_string() how_many_times_won.to_string()
} }
pub fn solve_task_2(input: &str) -> String { pub fn solve_task_2(input: &str) -> String {
let parsed = parsing::parsing_part_2(input); let parsed = parsing::parsing_part_2(input);
@ -27,6 +29,7 @@ pub fn solve_task_2(input: &str) -> String {
const FIRST_CYCLE_NO_DISTANCE: usize = 1; const FIRST_CYCLE_NO_DISTANCE: usize = 1;
for (index, next_distance) in all_rounds_of_durations(parsed.time()) for (index, next_distance) in all_rounds_of_durations(parsed.time())
.into_iter()
.enumerate() .enumerate()
.skip(FIRST_CYCLE_NO_DISTANCE) .skip(FIRST_CYCLE_NO_DISTANCE)
{ {

View file

@ -43,7 +43,7 @@ fn categorize_and_sort_with_a_joker(parsed: Vec<DealtHand>, joker: char) -> Vec<
fn calc_total_winning(sorted: &[CategorizedHand]) -> usize { fn calc_total_winning(sorted: &[CategorizedHand]) -> usize {
sorted sorted
.iter() .into_iter()
.enumerate() .enumerate()
.map(|(index, hand)| { .map(|(index, hand)| {
let rank = index.saturating_add(1); let rank = index.saturating_add(1);

View file

@ -2,6 +2,7 @@ mod joker_ordered;
pub use joker_ordered::JokerOrdered; pub use joker_ordered::JokerOrdered;
use crate::day7::second_ordering; use crate::day7::second_ordering;
use std::usize;
use super::{dealt_hand::DealtHand, hand_kind::HandKind, second_ordering::StrengthOfSymbols}; use super::{dealt_hand::DealtHand, hand_kind::HandKind, second_ordering::StrengthOfSymbols};

View file

@ -1,4 +1,4 @@
use std::str::FromStr; use std::{str::FromStr, usize};
use thiserror::Error; use thiserror::Error;

View file

@ -42,7 +42,7 @@ impl From<(&'_ str, char)> for HandKind {
} }
} }
let duplicated_counted = calc_duplicate_counted(characters, |next| next != joker_char); let duplicated_counted = calc_duplicate_counted(&characters, |next| next != joker_char);
let mut current_kind = choose_kind_from(&duplicated_counted); let mut current_kind = choose_kind_from(&duplicated_counted);
let mut joker_count = get_joker_count(joker_char, characters); let mut joker_count = get_joker_count(joker_char, characters);
@ -61,7 +61,7 @@ impl From<(&'_ str, char)> for HandKind {
impl From<&'_ str> for HandKind { impl From<&'_ str> for HandKind {
fn from(value: &'_ str) -> Self { fn from(value: &'_ str) -> Self {
let duplicated_counted = calc_duplicate_counted(value, |_| true); let duplicated_counted = calc_duplicate_counted(&value, |_| true);
choose_kind_from(&duplicated_counted) choose_kind_from(&duplicated_counted)
} }
} }
@ -78,13 +78,13 @@ fn calc_duplicate_counted(
if on_consider_next_char(next_char) { if on_consider_next_char(next_char) {
count_of_same count_of_same
.entry(next_char) .entry(next_char)
.and_modify(|count| *count += 1) .and_modify(|count| *count = *count + 1)
.or_insert(FIRST_ENCOUNTERED); .or_insert(FIRST_ENCOUNTERED);
} }
} }
{ {
let mut to_sort: Vec<usize> = count_of_same.into_values().collect(); let mut to_sort: Vec<usize> = count_of_same.into_iter().map(|(_, count)| count).collect();
to_sort.sort_by_key(|&count| Reverse(count)); to_sort.sort_by_key(|&count| Reverse(count));
to_sort to_sort
} }
@ -119,9 +119,9 @@ fn choose_kind_from(to_choose_from: &[usize]) -> HandKind {
Some(kind) Some(kind)
} }
calc_kind(to_choose_from).unwrap_or_else(|| { calc_kind(&to_choose_from).unwrap_or_else(|| {
let default_value = HandKind::default();
HandKind::default() default_value
}) })
} }

View file

@ -1,4 +1,4 @@
use std::{cmp::Ordering, collections::HashMap, ops::RangeInclusive, sync::LazyLock}; use std::{cmp::Ordering, collections::HashMap, ops::RangeInclusive, sync::LazyLock, usize};
pub const JOKER: char = 'J'; pub const JOKER: char = 'J';
static LETTERS_WITHOUT_JOKER: &[char] = &['T', 'Q', 'K', 'A']; static LETTERS_WITHOUT_JOKER: &[char] = &['T', 'Q', 'K', 'A'];
@ -8,14 +8,17 @@ const NUMBERS: RangeInclusive<u32> = 0..=9;
pub type StrengthOfSymbols = HashMap<char, usize>; pub type StrengthOfSymbols = HashMap<char, usize>;
fn numbers_in_chars() -> impl Iterator<Item = char> { fn numbers_in_chars() -> impl Iterator<Item = char> {
NUMBERS.map(|number| std::char::from_digit(number, 10).unwrap()) NUMBERS
.into_iter()
.map(|number| std::char::from_digit(number, 10).unwrap())
} }
//A hand consists of five cards labeled one of A, K, Q, J, T, 9, 8, 7, 6, 5, 4, 3, or 2. //A hand consists of five cards labeled one of A, K, Q, J, T, 9, 8, 7, 6, 5, 4, 3, or 2.
//The relative strength of each card follows this order, where A is the highest and 2 is the lowest. //The relative strength of each card follows this order, where A is the highest and 2 is the lowest.
pub static LABEL_ORDERING: LazyLock<StrengthOfSymbols> = LazyLock::new(|| { pub static LABEL_ORDERING: LazyLock<StrengthOfSymbols> = LazyLock::new(|| {
numbers_in_chars() numbers_in_chars()
.chain(LETTERS.iter().copied()) .chain(LETTERS.into_iter().copied())
.into_iter()
.enumerate() .enumerate()
.map(|(index, character)| (character, index)) .map(|(index, character)| (character, index))
.collect() .collect()
@ -24,7 +27,8 @@ pub static LABEL_ORDERING: LazyLock<StrengthOfSymbols> = LazyLock::new(|| {
pub static LABEL_ORDERING_WITH_JOKER: LazyLock<StrengthOfSymbols> = LazyLock::new(|| { pub static LABEL_ORDERING_WITH_JOKER: LazyLock<StrengthOfSymbols> = LazyLock::new(|| {
std::iter::once(JOKER) std::iter::once(JOKER)
.chain(numbers_in_chars()) .chain(numbers_in_chars())
.chain(LETTERS_WITHOUT_JOKER.iter().copied()) .chain(LETTERS_WITHOUT_JOKER.into_iter().copied())
.into_iter()
.enumerate() .enumerate()
.map(|(index, character)| (character, index)) .map(|(index, character)| (character, index))
.collect() .collect()
@ -43,7 +47,7 @@ pub fn compare_along_str(
.get(&right_char) .get(&right_char)
.unwrap_or_else(|| panic!("right char ({}) is not valid", right_char)); .unwrap_or_else(|| panic!("right char ({}) is not valid", right_char));
let current_ordering = left_rank_value.cmp(right_rank_value); let current_ordering = left_rank_value.cmp(&right_rank_value);
let found_difference = current_ordering != Ordering::Equal; let found_difference = current_ordering != Ordering::Equal;
if found_difference { if found_difference {

View file

@ -5,7 +5,7 @@ pub fn blocks_of_lines_seperated_by<'a>(
let mut current_block = Vec::new(); let mut current_block = Vec::new();
let mut lines = input.lines(); let mut lines = input.lines();
std::iter::from_fn(move || { std::iter::from_fn(move || {
for next_line in lines.by_ref() { while let Some(next_line) = lines.next() {
if on_skip(next_line) { if on_skip(next_line) {
if !current_block.is_empty() { if !current_block.is_empty() {
let to_return = std::mem::take(&mut current_block); let to_return = std::mem::take(&mut current_block);
@ -23,7 +23,9 @@ pub fn blocks_of_lines_seperated_by<'a>(
None None
}) })
} }
pub fn blocks_of_lines_seperated_by_empty_lines(input: &str) -> impl Iterator<Item = Vec<&str>> { pub fn blocks_of_lines_seperated_by_empty_lines<'a>(
input: &'a str,
) -> impl Iterator<Item = Vec<&'a str>> {
blocks_of_lines_seperated_by(input, |line| line.trim().is_empty()) blocks_of_lines_seperated_by(input, |line| line.trim().is_empty())
} }