Skip to content

Commit 3ca1cfe

Browse files
committed
Rust single binary
1 parent f52e877 commit 3ca1cfe

File tree

5 files changed

+376
-0
lines changed

5 files changed

+376
-0
lines changed

one/Cargo.toml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
[package]
2+
name = "one"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[features]
7+
ascii = []
8+
anim = ["image"]
9+
10+
[dependencies]
11+
aoc = { path = "../aoc" }
12+
assembunny = { path = "../2016/assembunny/" }
13+
intcode = { path = "../2019/intcode-rs/" }
14+
15+
rustc-hash = "*"
16+
colored = "*"
17+
pest = { version = "*", default-features = false }
18+
pest_derive = { version = "*", default-features = true }
19+
petgraph = "*"
20+
rand = "*"
21+
regex = "*"
22+
rustworkx-core = "*"
23+
scan_fmt = "*"
24+
serde_json = "*"
25+
strum = { version = "*", features = ["strum_macros", "derive"] }
26+
strum_macros = "*"
27+
text_io = "*"
28+
z3 = "*"
29+
nom = "*"
30+
num = "*"
31+
num-traits = "*"
32+
itertools = "*"
33+
lazy_static = "*"
34+
md5 = "*"
35+
image = { version = "*", optional = true }
36+
indicatif = "*"
37+
fraction = "*"
38+
geo = "*"
39+
geo-types = "*"
40+
bytecount = "*"
41+
divisors = "*"

one/build.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
fn main() {
2+
// Buy a Mac 
3+
println!(r"cargo:rustc-link-search=/opt/homebrew/lib");
4+
}

one/m.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/usr/bin/env python3
2+
3+
from pathlib import Path
4+
5+
6+
def gen(year):
7+
for src_dir in Path(f"../{year}").glob("day*"):
8+
if "_" in src_dir.name:
9+
continue
10+
11+
src_file = src_dir / (src_dir.name + ".rs")
12+
dest_dir = Path(f"src/year{year}") / src_dir.name
13+
dest_dir.mkdir(parents=True, exist_ok=True)
14+
15+
for txt in src_file.parent.glob("*.txt"):
16+
(dest_dir / txt.name).write_bytes(txt.read_bytes())
17+
18+
dest_file = dest_dir / (src_dir.name + ".rs")
19+
if src_file.is_file():
20+
21+
rs = src_file.read_text()
22+
rs = rs.replace('#[grammar = "day8.pest"]', '#[grammar = "src/year2017/day8/day8.pest"]')
23+
rs = rs.replace("\nfn main() {", "\npub fn main() {")
24+
rs = rs.replace("\nfn solve(", "\n#[must_use]\npub fn solve(")
25+
26+
dest_file.write_text(rs)
27+
28+
for txt in src_file.parent.glob("*.rs"):
29+
if txt.name != dest_file.name:
30+
z = dest_dir / dest_file.stem
31+
z.mkdir(parents=True, exist_ok=True)
32+
(z / txt.name).write_bytes(txt.read_bytes())
33+
34+
for txt in src_file.parent.glob("*.txt"):
35+
(dest_dir / txt.name).write_bytes(txt.read_bytes())
36+
37+
for txt in src_file.parent.glob("*.pest"):
38+
(dest_dir / txt.name).write_bytes(txt.read_bytes())
39+
40+
41+
gen(2015)
42+
gen(2016)
43+
gen(2017)
44+
gen(2018)
45+
gen(2019)
46+
gen(2020)
47+
gen(2021)
48+
gen(2022)
49+
gen(2023)
50+
gen(2024)

one/src/lib.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use std::iter::empty;
2+
3+
/// Get the array of all available solutions.
4+
#[must_use]
5+
pub fn solutions() -> Vec<Solution> {
6+
empty()
7+
.chain(year2015())
8+
.chain(year2016())
9+
.chain(year2017())
10+
.chain(year2018())
11+
.chain(year2019())
12+
.chain(year2020())
13+
.chain(year2021())
14+
.chain(year2022())
15+
.chain(year2024())
16+
.collect()
17+
}
18+
19+
/// A solution for given year and day.
20+
/// Offer two callbacks:
21+
/// - `solve` that takes the puzzle input and returns part one and two
22+
/// - `main` that acts like a standalone program for the given day
23+
#[derive(Clone)]
24+
pub struct Solution {
25+
pub year: u16,
26+
pub day: u8,
27+
pub solve: fn(&str) -> (String, String),
28+
pub main: fn() -> (),
29+
}
30+
31+
macro_rules! make_year {
32+
($year:tt $($day:tt),*) => {
33+
mod $year {
34+
$(pub mod $day { pub mod $day; })*
35+
}
36+
37+
#[must_use]
38+
pub fn $year() -> Vec<Solution> {
39+
vec![$({
40+
let year = stringify!($year)[4..].parse().unwrap();
41+
let day = stringify!($day)[3..].parse().unwrap();
42+
43+
let solve = |data: &str| {
44+
use crate::$year::$day::$day::solve;
45+
let (part1, part2) = solve(data);
46+
(part1.to_string(), part2.to_string())
47+
};
48+
49+
let main = || {
50+
use crate::$year::$day::$day::main;
51+
main();
52+
};
53+
54+
Solution { year, day, solve, main }
55+
},)*]
56+
}
57+
}
58+
}
59+
60+
make_year!(year2015
61+
day1,day2,day3,day4,day5,day6,day7,day8,day9,day10,day11,day12,day13,
62+
day14,day15,day16,day17,day18,day19,day20,day21,day22,day23,day24,day25
63+
);
64+
65+
make_year!(year2016
66+
day1,day2,day3,day4,day5,day6,day7,day8,day9,day10,day11,day12,day13,
67+
day14,day15,day16,day17,day18,day19,day20,day21,day22,day23,day24,day25
68+
);
69+
70+
make_year!(year2017
71+
day1,day2,day3,day4,day5,day6,day7,day8,day9,day10,day11,day12,day13,
72+
day14,day15,day16,day17,day18,day19,day20,day21,day22,day23,day24,day25
73+
);
74+
75+
make_year!(year2018
76+
day1,day2,day3,day4,day5,day6,day7,day8,day9,day10,day11,day12,day13,
77+
day14,day15,day16,day17,day18,day19,day20,day21,day22,day23,day24,day25
78+
);
79+
80+
make_year!(year2019
81+
day1,day2,day3,day4,day5,day6,day7,day8,day9,day10,day11,day12,day13,
82+
day14,day15,day16,day17,day18,day19,day20,day21,day22,day23,day24,day25
83+
);
84+
85+
make_year!(year2020
86+
day1,day2,day3,day4,day5,day6,day7,day8,day9,day10,day11,day12,day13,
87+
day14,day15,day16,day17,day18,day19,day20,day21,day22,day23,day24,day25
88+
);
89+
90+
make_year!(year2021
91+
day1,day2,day3,day4,day5,day6,day7,day8,day9,day10,day11,day12,day13,
92+
day14,day15,day16,day17,day18,day19,day20,day21,day22,day23,day24,day25
93+
);
94+
95+
make_year!(year2022
96+
day1,day2,day3,day4,day5,day6,day7,day8,day9,day10,day11,day12,day13,
97+
day14,day15,day16,day17,day18,day19,day20,day21,day22,day23,day24,day25
98+
);
99+
100+
make_year!(year2023
101+
day1,day2,day3,day4,day5,day6,day7,day8,day9,day10,day11,day12,day13,
102+
day14,day15,day16,day17,day18,day19,day20,day21,day22,day23,day24,day25
103+
);
104+
105+
make_year!(year2024
106+
day1,day2,day3,day4,day5,day6,day7,day8,day9,day10,day11,day12,day13,
107+
day14,day15,day16,day17,day18,day19,day20,day21,day22,day23,day24,day25
108+
);

one/src/main.rs

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
use colored::Colorize;
2+
use one::{solutions, Solution};
3+
use std::path::Path;
4+
use std::time::{Duration, Instant};
5+
6+
fn main() -> std::io::Result<()> {
7+
// in a YEAR/dayDAY directory, we act as the standalone binary
8+
if run_day()? {
9+
return Ok(());
10+
}
11+
12+
let args = aoc::parse_args_raw();
13+
14+
// get the year or year/day filter
15+
let mut year: Option<u16> = None;
16+
let mut day: Option<u8> = None;
17+
18+
if !args.params.is_empty() {
19+
let re = regex::Regex::new(r"(\d+)").unwrap();
20+
21+
let mut m = re.find_iter(&args.params[0]);
22+
23+
if let Some(y) = m.next() {
24+
year = y.as_str().parse().ok();
25+
}
26+
if let Some(d) = m.next() {
27+
day = d.as_str().parse().ok();
28+
}
29+
}
30+
31+
// and apply it to the solution inventory
32+
let sols = solutions()
33+
.iter()
34+
.filter(|sol| year.is_none_or(|x| x == sol.year))
35+
.filter(|sol| day.is_none_or(|x| x == sol.day))
36+
.cloned()
37+
.collect::<Vec<_>>();
38+
39+
// in raw mode (for runall.py) we need a file input path
40+
if args.has_option("-r") {
41+
if sols.len() != 1 {
42+
println!("-r requires a filter");
43+
return Ok(());
44+
}
45+
46+
if args.params.len() != 2 {
47+
println!("-r requires a path");
48+
return Ok(());
49+
}
50+
51+
let path = &args.params[1];
52+
let data = aoc::load_input_data(path);
53+
let sol = &sols[0];
54+
55+
args.run_data(sol.solve, &data);
56+
} else {
57+
run_all(&sols);
58+
}
59+
60+
Ok(())
61+
}
62+
63+
fn run_day() -> std::io::Result<bool> {
64+
let path = std::env::current_dir()?;
65+
66+
if let Some(day) = path.file_name() {
67+
if let Some(day) = day.to_str().unwrap().strip_prefix("day") {
68+
if let Some(year) = path.parent() {
69+
if let Some(year) = year
70+
.file_name()
71+
.unwrap()
72+
.to_str()
73+
.unwrap()
74+
.strip_prefix("year")
75+
{
76+
let year: u16 = year.parse().unwrap();
77+
let day: u8 = day.parse().unwrap();
78+
79+
for sol in &solutions() {
80+
if sol.day == day && sol.year == year {
81+
(sol.main)();
82+
break;
83+
}
84+
}
85+
}
86+
87+
return Ok(true);
88+
}
89+
}
90+
}
91+
92+
Ok(false)
93+
}
94+
95+
fn print_part_result(part: u8, answer: &str, ok: &str, day: u8) {
96+
if part == 2 && day == 25 {
97+
println!(
98+
" {} : {}",
99+
"Part 2".yellow(),
100+
"n/a".dimmed() // "Merry Christmas".bright_blue()
101+
);
102+
} else {
103+
print!(" {} : ", format!("Part {part}").yellow());
104+
if ok.is_empty() {
105+
println!("{answer}");
106+
} else if answer.trim_ascii() == ok.trim_ascii() {
107+
println!("{}", answer.bright_green());
108+
} else {
109+
println!("{}", answer.bright_red());
110+
};
111+
}
112+
}
113+
114+
fn run_all(sols: &[Solution]) {
115+
println!("💫 {} 🎄✨ 💫", "Advent of Code".green());
116+
117+
let mut total_elapsed = Duration::ZERO;
118+
let mut puzzles = 0;
119+
let mut success = 0;
120+
let mut failed = 0;
121+
122+
for sol in sols {
123+
let path = Path::new("input")
124+
.join(sol.year.to_string())
125+
.join(sol.day.to_string())
126+
.with_extension("in");
127+
128+
let ok = path.with_extension("ok");
129+
130+
println!();
131+
println!("{} day {}:", sol.year, sol.day);
132+
133+
if path.is_file() {
134+
if let Ok(data) = std::fs::read_to_string(&path) {
135+
// run the solution
136+
let instant = Instant::now();
137+
let (part1, part2) = (sol.solve)(&data);
138+
let elapsed = instant.elapsed();
139+
140+
total_elapsed += elapsed;
141+
puzzles += 1;
142+
143+
#[allow(clippy::cast_possible_truncation)]
144+
let micros = Duration::from_micros(elapsed.as_micros() as u64);
145+
146+
if let Ok(ok) = std::fs::read_to_string(ok) {
147+
let (ok1, ok2) = ok.trim_ascii().split_once('\n').unwrap_or((&ok, ""));
148+
149+
print_part_result(1, &part1, ok1, sol.day);
150+
print_part_result(2, &part2, ok2, sol.day);
151+
152+
if ok1.trim_ascii() == part1 && ok2.trim_ascii() == part2 {
153+
success += 1;
154+
} else {
155+
failed += 1;
156+
}
157+
} else {
158+
print_part_result(1, &part1, "", sol.day);
159+
print_part_result(2, &part2, "", sol.day);
160+
}
161+
162+
println!("{}", format!(" Elapsed : {micros:#?}").italic());
163+
}
164+
} else {
165+
println!(" missing file: {}", path.to_str().unwrap().red());
166+
}
167+
}
168+
169+
if puzzles > 1 {
170+
println!();
171+
println!("Elapsed: {total_elapsed:#?} for {puzzles} puzzle(s) - success: {success}, failed: {failed}");
172+
}
173+
}

0 commit comments

Comments
 (0)