[WIP] Day 1

This commit is contained in:
2023-12-01 08:59:12 +01:00
commit 446422e0d2
15 changed files with 1496 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

11
.idea/advent-of-code-2023-rust.iml generated Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="EMPTY_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/advent-of-code-2023-rust.iml" filepath="$PROJECT_DIR$/.idea/advent-of-code-2023-rust.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

92
Cargo.lock generated Normal file
View File

@@ -0,0 +1,92 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "advent-of-code-2023-rust"
version = "0.1.0"
dependencies = [
"num",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "num"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05180d69e3da0e530ba2a1dae5110317e49e3b7f3d41be227dc5f92e49ee7af"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-complex"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ba157ca0885411de85d6ca030ba7e2a83a28636056c7c699b07c8b6f7383214"
dependencies = [
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]

9
Cargo.toml Normal file
View File

@@ -0,0 +1,9 @@
[package]
name = "advent-of-code-2023-rust"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
num = "0.4"

1000
input/day1.txt Normal file

File diff suppressed because it is too large Load Diff

4
input/day1_example.txt Normal file
View File

@@ -0,0 +1,4 @@
1abc2
pqr3stu8vwx
a1b2c3d4e5f
treb7uchet

7
input/day1_example2.txt Normal file
View File

@@ -0,0 +1,7 @@
two1nine
eightwothree
abcone2threexyz
xtwone3four
4nineeightseven2
zoneight234
7pqrstsixteen

54
src/day1.rs Normal file
View File

@@ -0,0 +1,54 @@
use crate::day_solver::DaySolver;
use super::util;
pub struct Day1 {
lines: Vec<String>
}
impl Day1 {
pub fn create() -> Self {
let lines = util::read_file("input/day1_example2.txt");
// let lines = util::read_file("input/dayX.txt");
// Put the input into the day struct
return Day1 {
lines
}
}
fn solve(lines: &Vec<String>) -> u32 {
lines.iter()
.map(|l| {
let nrs: Vec<u8> = l.chars()
.filter(|c| c.is_numeric())
.map(|c| (c as u8) - ('0' as u8))
.collect();
u32::from(nrs.first().unwrap().to_owned()) * 10 + u32::from(nrs.last().unwrap().to_owned())
}).sum::<u32>()
}
}
impl DaySolver for Day1 {
fn solve_part1(&mut self) -> String {
return Self::solve(&self.lines).to_string()
}
fn solve_part2(&mut self) -> String {
return Self::solve(&self.lines.iter().map(
|l| l
.replace("one", "1")
.replace("two", "2")
.replace("three", "3")
.replace("four", "4")
.replace("five", "5")
.replace("six", "6")
.replace("seven", "7")
.replace("eight", "8")
.replace("nine", "9")
).collect()).to_string()
}
}

29
src/dayX.rs Normal file
View File

@@ -0,0 +1,29 @@
use crate::day_solver::DaySolver;
use super::util;
pub struct DayX {
}
impl DayX {
pub fn create() -> Self {
let lines = util::read_file("input/dayX_example.txt");
// let lines = util::read_file("input/dayX.txt");
// Put the input into the day struct
return DayX {}
}
}
impl DaySolver for DayX {
fn solve_part1(&mut self) -> String {
return 0.to_string();
}
fn solve_part2(&mut self) -> String {
return 0.to_string();
}
}

6
src/day_solver.rs Normal file
View File

@@ -0,0 +1,6 @@
pub trait DaySolver {
// fn create() -> Self;
fn solve_part1(&mut self) -> String;
fn solve_part2(&mut self) -> String;
}

158
src/main.rs Normal file
View File

@@ -0,0 +1,158 @@
extern crate core;
use std::time::Instant;
use crate::day1::Day1;
use crate::day_solver::DaySolver;
mod util;
mod day1;
mod day_solver;
const MAX_DAY: u8 = 1;
const DEFAULT_BENCHMARK_AMOUNT: u32 = 100;
fn main() {
let args: Vec<String> = std::env::args().collect();
let day_arg_idx = args.iter().position(|a| a == "-d");
let single_day = day_arg_idx.is_some();
let day = if single_day { args[day_arg_idx.unwrap() + 1].parse::<u8>().unwrap() } else { 0 };
let benchmark_arg_idx_option = args.iter().position(|a| a == "--bench").or(
args.iter().position(|a| a == "-b"));
let benchmark = benchmark_arg_idx_option.is_some();
let bench_amount: u32 = if let Some(benchmark_arg_idx) = benchmark_arg_idx_option {
args.get(benchmark_arg_idx + 1)
.map_or(DEFAULT_BENCHMARK_AMOUNT, |r| r.parse::<u32>().unwrap_or(DEFAULT_BENCHMARK_AMOUNT))
} else {
DEFAULT_BENCHMARK_AMOUNT
};
let mut bench_results: Vec<AocBenchResult> = Vec::new();
// This is essentially the warmup for the benchmark:
run_once(single_day, day, false, &mut bench_results);
let first_run_bench = bench_results[0].to_owned();
let (_, total_time) = bench(||
if benchmark {
// Ignore the warmup run in the rest of the benchmark:
bench_results.clear();
for _ in 0..bench_amount {
run_once(single_day, day, true, &mut bench_results);
}
});
if benchmark {
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.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);
} 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);
}
}
#[derive(Copy, Clone)]
struct AocBenchResult {
init: u128,
part1: u128,
part2: u128,
total: u128
}
fn build_day_solver(day: u8) -> Option<Box<dyn DaySolver>> {
match day {
1 => Some(Box::new(Day1::create())),
_ => None
}
}
fn solve(day: u8, silent: bool) -> AocBenchResult {
let now = Instant::now();
let (solver, init_time) = bench(|| build_day_solver(day));
let part1_time: u128;
let part2_time: u128;
match solver {
Some(mut s) => {
// let(part1, pt1_time) = bench(|| s.solve_part1());
// part1_time = pt1_time;
//
// if !silent {
// println!("Day {} Part 1: {}", day, part1);
// }
part1_time = 0;
let (part2, pt2_time) = bench(|| s.solve_part2());
part2_time = pt2_time;
if !silent {
println!("Day {} Part 2: {}", day, part2);
}
},
None => panic!("This day is not yet implemented")
}
return AocBenchResult{
init: init_time,
part1: part1_time,
part2: part2_time,
total: now.elapsed().as_micros()
}
}
fn print_bench_result<F>(bench_results: &Vec<AocBenchResult>, f: F, bench_part_description: &str)
where
F: FnMut(&AocBenchResult) -> u128 {
let mut benches: Vec<u128> = bench_results.iter().map(f).collect();
benches.sort();
let avg_runtime: u128 = benches.iter().sum::<u128>() / (bench_results.len() as u128);
println!("{} took {} μs {} (Min: {} μs, Max: {} μs, Median: {} μs)", bench_part_description, avg_runtime, if bench_results.len() > 1 { "on average"} else {""},
benches[0], benches[benches.len() - 1], benches[benches.len() / 2])
}
fn run_once(single_day: bool, day: u8, silent: bool, bench_results: &mut Vec<AocBenchResult>) {
let bench_result = if single_day {
solve(day, silent)
} else {
solve_all(silent)
};
bench_results.push(bench_result);
}
fn solve_all(silent: bool) -> AocBenchResult {
let mut bench_results = Vec::new();
for day in 1..(MAX_DAY + 1) {
bench_results.push(solve(day, silent));
}
return AocBenchResult {
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(),
total: bench_results.iter().map(|t| t.total).sum(),
}
}
fn bench<F, K>(mut f: F) -> (K, u128)
where F: FnMut() -> K {
let now = Instant::now();
let res = f();
(res, now.elapsed().as_micros())
}

103
src/util.rs Normal file
View File

@@ -0,0 +1,103 @@
use std::fs;
use std::fmt::Display;
use num::Integer;
pub fn read_file(filename: &str) -> Vec<String> {
let contents = 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
}
#[derive(Debug, Clone)]
pub struct Grid<T> {
pub(crate) width: usize,
pub(crate) data: Vec<T>
}
impl<T: Clone + Sized> Grid<T> {
#[allow(dead_code)]
pub fn set_row(&mut self, y: usize, v: T) {
// let range = &mut self.data[x * self.width..(x+1) * self.width];
for i in self.width * y..self.width * (y + 1) {
self.data[i] = v.to_owned();
}
}
#[allow(dead_code)]
pub fn set_col(&mut self, x: usize, v: T) {
for y in 0..self.height() {
let idx = self.idx(&x, &y);
self.data[idx] = v.to_owned();
}
}
pub(crate) fn idx(&self, x: &usize, y: &usize) -> usize {
y * self.width + x
}
pub(crate) fn height(&self) -> usize {
self.data.len() / self.width
}
pub(crate) fn get(&self, x: &usize, y: &usize) -> &T {
let idx = self.idx(x, y);
&self.data[idx]
}
pub fn set(&mut self, x: &usize, y: &usize, v: T) {
let idx = self.idx(x, y);
self.data[idx] = v;
}
}
impl <T: Display + Clone + Sized> Grid<T> {
#[allow(dead_code)]
pub fn print(&self) {
self.print_range(0, self.width, 0, self.height());
}
#[allow(dead_code)]
pub fn print_range(&self, from_x: usize, to_x: usize, from_y: usize, to_y: usize) {
for y in from_y.max(0)..to_y.min(self.height()) {
for x in from_x.max(0)..to_x.min(self.width) {
print!("{}", self.get(&x, &y))
}
println!();
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Coord<T> where T: Sized {
pub x: T,
pub y: T
}
#[allow(dead_code)]
impl<T> Coord<T> where T: Sized {
pub(crate) fn new(x: T, y: T) -> Self {
Coord { x, y }
}
}
#[allow(dead_code)]
impl <T> Coord<T> where T: Integer + Copy {
pub fn manhattan_dist(&self, other: &Coord<T>) -> T {
self.x.max(other.x).sub(self.x.min(other.x)) + self.y.max(other.y).sub(self.y.min(other.y))
}
}