18 Commits

Author SHA1 Message Date
f904d050cc Reimplement day 4 2022 with ranges instead of ints 2022-12-06 07:55:54 +01:00
6802a7bf33 Refactor common parts 2022-12-04 11:18:03 +01:00
9d23e80256 Implement 2022 day 4 2022-12-04 11:14:23 +01:00
e1b3b9d179 Implement 2022 day 3 2022-12-03 20:23:14 +01:00
30d1a16075 Collect into vec for nicer vectorization 2022-12-02 09:52:09 +01:00
256d351f8e Implement day 2 2022 2022-12-02 09:06:59 +01:00
48594a75e6 Make parsers more robust 2022-12-01 11:28:59 +01:00
85a51b13c1 Implement 2022 day 1 2022-12-01 09:40:00 +01:00
2ae2d6baa8 Merge pull request #4 from bertptrs/setup-2022 2022-11-30 18:08:54 +01:00
4a55e53182 Update README and references 2022-11-24 08:23:58 +01:00
af0897300d Add caching to CI pipeline for speed 2022-11-05 16:17:47 +01:00
cabae7b1fd Convert 2021 CI to 2022 2022-11-05 16:17:47 +01:00
0635141ac6 Add skeleton for 2022 2022-11-05 16:17:47 +01:00
d9d5947c3b Replace unnecessary Vec with slice 2022-06-07 08:32:59 +02:00
cc8b4ce353 Update vulnerable dependencies
Not that this will affect anyone, but it's nice anyway.
2022-06-07 08:25:30 +02:00
0b91da04b3 Add benchmarking plots 2022-01-09 18:41:02 +01:00
dba146b299 Avoid instantiating translated sets 2022-01-09 15:45:22 +01:00
33111615be Directly infer matched pivot 2022-01-09 14:22:41 +01:00
53 changed files with 7116 additions and 28 deletions

View File

@@ -1,7 +1,7 @@
on:
- push
name: Advent of Code 2021
name: Advent of Code 2022
jobs:
ci:
@@ -20,7 +20,7 @@ jobs:
continue-on-error: ${{ matrix.experimental }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install toolchain
uses: actions-rs/toolchain@v1
@@ -30,17 +30,23 @@ jobs:
override: true
components: rustfmt, clippy
- name: Set up caching
uses: Swatinem/rust-cache@v2
with:
workspaces: >
2022 -> target
- name: Build binaries
working-directory: 2021
working-directory: 2022
run: >
cargo build --all-targets
- name: Run tests
working-directory: 2021
working-directory: 2022
run: >
cargo test
- name: Run clippy
working-directory: 2021
working-directory: 2022
run: >
cargo clippy -- --deny warnings

View File

@@ -4,4 +4,4 @@ version = "0.1.0"
authors = ["Bert Peters <bert.ljpeters@gmail.com>"]
[dependencies]
regex = "0.1"
regex = "1"

View File

@@ -73,10 +73,10 @@ fn main() {
let door_label = line.unwrap();
let caps = room_pattern.captures(&door_label).unwrap();
let name = caps.at(1).unwrap();
let checksum = caps.at(4).unwrap();
let name = caps.get(1).unwrap().as_str();
let checksum = caps.get(4).unwrap().as_str();
if is_valid(name, checksum) {
let sector_id = caps.at(3).unwrap().parse().unwrap();
let sector_id = caps.get(3).unwrap().as_str().parse().unwrap();
cur_sum += sector_id;
let decoded: String = name.chars()

View File

@@ -4,5 +4,5 @@ version = "0.1.0"
authors = ["Bert Peters <bert.ljpeters@gmail.com>"]
[dependencies]
regex = "^0.1"
lazy_static = "^0.2"
regex = "1"
lazy_static = "1"

View File

@@ -4,4 +4,4 @@ version = "0.1.0"
authors = ["Bert Peters <bert.ljpeters@gmail.com>"]
[dependencies]
regex="^0.1"
regex="1"

View File

@@ -20,3 +20,15 @@ OPTIONS:
-i, --input <INPUT> Read input from the given file instead of stdin
-t, --time Print time taken
```
## That goal was achieved
Runtime benchmarked with [Criterion], reading input directly from memory to avoid disk IO
inconsistencies.
![Cumulative time](./cumulative-time.svg)
![Time by day](./individual-time.svg)
[Criterion]: https://github.com/bheisler/criterion.rs

97
2021/create_timing_plots.py Executable file
View File

@@ -0,0 +1,97 @@
#!/usr/bin/env python3
import json
from pathlib import Path
from typing import Dict
import numpy as np
import matplotlib.pyplot as plt
def read_timings() -> Dict[int, Dict]:
timings = {}
for day in Path('target/criterion/part1').iterdir():
with open(day / 'new' / 'estimates.json', mode='rb') as f:
timings[int(day.parts[-1])] = {
1: json.load(f)
}
for day in Path('target/criterion/part2').iterdir():
with open(day / 'new' / 'estimates.json', mode='rb') as f:
timings[int(day.parts[-1])][2] = json.load(f)
return timings
def plot_cumulative_time(timings: Dict[int, Dict]):
plt.clf()
times = [0]
for day in range(min(timings.keys()), max(timings.keys()) + 1):
times.append(timings[day][1]['mean']['point_estimate'])
if day < 25:
times.append(timings[day][2]['mean']['point_estimate'])
else:
times.append(0)
cumulative = np.cumsum(times)
# Convert from nanoseconds to seconds
cumulative /= 1e9
x = np.arange(0.0, 25.5, 0.5)
plt.plot(x, cumulative, label="Cumulative time", drawstyle='steps-post')
plt.plot([0, 25], [0, 0.5], label="Target time")
plt.ylabel('Cumulative time (s)')
plt.xlabel('Days completed')
plt.legend()
plt.tight_layout()
plt.xlim(0, 25)
plt.ylim(0, 0.5)
plt.savefig('cumulative-time.svg')
def plot_individual_times(timings: Dict[int, Dict]):
plt.clf()
def plot(parts, **kwargs):
x = np.arange(1, len(parts) + 1)
values = np.array(list(part['mean']['point_estimate'] for part in parts))
upper = np.array(list(part['mean']['confidence_interval']['upper_bound'] for part in parts))
lower = np.array(list(part['mean']['confidence_interval']['lower_bound'] for part in parts))
# Convert from ns to s
yerr = np.array([upper - values, lower - values]) / 1e9
values = values / 1e9
plt.bar(x, values, yerr=yerr, align='edge', log=True, **kwargs)
pass
plot(list(timings[day][1] for day in range(1, 26)), label="Part 1", width=-0.4)
plot(list(timings[day][2] for day in range(1, 25)), label="Part 2", width=0.4)
plt.ylabel('Runtime (s)')
plt.xlabel('Day')
plt.xlim(0, 26)
plt.xticks(np.arange(1, 26))
plt.legend()
plt.tight_layout()
plt.savefig('individual-time.svg')
def main():
timings = read_timings()
plot_cumulative_time(timings)
plot_individual_times(timings)
if __name__ == '__main__':
main()

1
2021/cumulative-time.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 16 KiB

1
2021/individual-time.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -48,7 +48,7 @@ fn parse_fold(input: &[u8]) -> IResult<&[u8], Fold> {
)(input)
}
fn apply_fold(dots: &mut Vec<Coords>, fold: Fold) {
fn apply_fold(dots: &mut [Coords], fold: Fold) {
match fold {
Fold::X(coord) => dots.iter_mut().for_each(|(x, _)| {
if *x >= coord {

View File

@@ -174,30 +174,45 @@ fn parse_input(input: &[u8]) -> IResult<&[u8], Vec<Scanner>> {
separated_list1(newline, parse_scanner)(input)
}
fn find_pivot(group: &Scanner, related: &Scanner) -> Option<Point3> {
let mut counter = HashMap::new();
for (distance, &(a, b)) in &group.distances {
if related.distances.contains_key(distance) {
*counter.entry(a).or_insert(0) += 1;
*counter.entry(b).or_insert(0) += 1;
}
}
counter
.into_iter()
.max_by_key(|(_, count)| *count)
.map(|t| t.0)
}
fn try_overlap(matched: &Scanner, candidate: &Scanner) -> Option<(Point3, Scanner)> {
if !matched.can_overlap(candidate) {
return None;
}
let correct: Vec<(Point3, HashSet<Point3>)> = matched
.iter()
.map(|&base| (base, matched.iter().map(|&other| (other - base)).collect()))
.collect();
let matched_pivot = find_pivot(matched, candidate)?;
let mut relative = HashSet::new();
for rot in Rotations::new(&candidate.visible) {
let correct: HashSet<_> = matched.iter().map(|&base| base - matched_pivot).collect();
for rot in Rotations::new(candidate) {
for &start in &rot {
relative.clear();
let translated_iter = rot.iter().map(|&other| other - start);
relative.extend(rot.iter().map(|&other| other - start));
if let Some((base, _)) = correct.iter().find(|(_, correct_relative)| {
correct_relative.intersection(&relative).count() >= 12
}) {
if translated_iter
.clone()
.filter(|p| correct.contains(p))
.count()
>= 12
{
// Found a solution, build the correct output
let translated = relative.drain().map(|point| point + *base).collect();
let translated = translated_iter.map(|point| point + matched_pivot).collect();
return Some((start - *base, Scanner::new(translated)));
return Some((start - matched_pivot, Scanner::new(translated)));
}
}
}
@@ -232,6 +247,8 @@ fn parts_common(input: &mut dyn Read) -> (HashSet<Point3>, Vec<Point3>) {
}
}
assert!(scanners.is_empty());
(points, scanners_found)
}

27
2022/Cargo.toml Normal file
View File

@@ -0,0 +1,27 @@
[package]
name = "aoc_2022"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.66"
clap = { version = "4.0.19", features = ["derive"] }
itertools = "0.10.5"
nom = "7.1.1"
[dev-dependencies]
criterion = "0.4.0"
[profile.release]
# Keep debug information in release for better flamegraphs
debug = true
[profile.bench]
# And same for benchmarking
debug = true
[[bench]]
name = "days"
harness = false

20
2022/README.md Normal file
View File

@@ -0,0 +1,20 @@
# Advent of Code 2022
Another year and another Advent of Code in Rust. Because last year went so well, this time we'll be
aiming for a total time of under 250ms.
```console
$ target/release/aoc_2022 --help
Advent of Code 2022 runner
Usage: aoc_2022 [OPTIONS] <DAY>
Arguments:
<DAY> Which day to run
Options:
-t, --time Print time taken
-2, --part2 Run part 2 instead of part 1
-i, --input <INPUT> Read input from the given file instead of stdin
-h, --help Print help information
```

46
2022/benches/days.rs Normal file
View File

@@ -0,0 +1,46 @@
use std::fs::File;
use std::io::Read;
use aoc_2022::get_implementation;
use criterion::criterion_group;
use criterion::criterion_main;
use criterion::BenchmarkId;
use criterion::Criterion;
/// Number of days we have an implementation to benchmark
const DAYS_IMPLEMENTED: u8 = 4;
fn read_input(day: u8) -> Vec<u8> {
let input_path = format!("inputs/{:02}.txt", day);
let mut buffer = Vec::new();
File::open(input_path)
.expect("Failed to open input file")
.read_to_end(&mut buffer)
.expect("Failed to read input file");
buffer
}
pub fn benchmark_days(c: &mut Criterion) {
for day in 1..=DAYS_IMPLEMENTED {
let input = read_input(day);
let part1 = get_implementation(day, false).unwrap();
c.bench_with_input(BenchmarkId::new("part1", day), &input, |b, i| {
b.iter(|| part1(i));
});
if day < 25 {
let part2 = get_implementation(day, true).unwrap();
c.bench_with_input(BenchmarkId::new("part2", day), &input, |b, i| {
b.iter(|| part2(i));
});
}
}
}
criterion_group!(benches, benchmark_days);
criterion_main!(benches);

0
2022/inputs/.gitkeep Normal file
View File

2259
2022/inputs/01.txt Normal file

File diff suppressed because it is too large Load Diff

2500
2022/inputs/02.txt Normal file

File diff suppressed because it is too large Load Diff

300
2022/inputs/03.txt Normal file
View File

@@ -0,0 +1,300 @@
NJvhJcQWTJWTNTFFMTqqGqfTmB
VwVzPldRZVLVRmfsvfjvqfmm
ZDPDHZHVcvDhbvnv
FHHwHBzzVCWWmmCzCPrVmgBwbLTtRFFbbbttRGRLjTcLpbbT
vhZZvdsNSdSMdNvjncppCLcLnGnj
CDZZsNZMZqdNSdlNZCqrzPHDzgrgzwVVWwmwwm
ndlndntsFJntFvccLjjLrjBShcBBfc
GpCGHzVwmmzqQWSSSfWHBhQL
mpCMGGCZVzVwGGVwmJsZnFtZnTSTJtdsvl
nCnPDGmDNmVCsVQDmGSWqvzchWSjjcWGqS
gTnBRLpfTRnrTdZgdLfRdrThvqcvWWhFFWvcFSSgjqqzjv
pfZfTMwrbLTTfsbmQtlVtHHnbs
wNdSdsbTvTZMTvTv
rrdRWdWQhFVdHWBGWQmmmnnMvCfmnhvmCmtZ
rJrVDRWpGddpbSlNSlspPP
chTNrthMMwWMTjfsmRzZszJpwm
BLnFFCngbcBnbbldDlpRjGpmsCzGsGsRGmmG
dqvnvlgbqtcPPMhH
QcLNqZbCzJDQBJJRpwzRpdnRldgnpf
GmmmvVGsHrWffrlwdCWd
CMsFVVFjCmFStGQbbLZNBbJBcTjc
LQVggbQvcLbQLHgvVLhWGGsChssrMWfzGccc
qDnRTTRqJttPfWMChJhGslWlzh
qRTRwPBTBtRZdnjnqqqnQVbjbNLFbbfLgVmgHLQm
cZbzwCwZPlJcMLrNSNfHWNBBNZ
vsQsDCqtsDhmtjVrBNWNjBHrhr
TtDTGnvTlgbbRCGg
BgBlplHlsgNNsJlVpBtPwJhMPRRQSSttRtSP
bvhTnmdFTzddStwStQRddt
ZnZDLvnvqZzbbhFzzmTbnFsVjVlNgsCCNVsVLpNWVgsB
TdptqrrcVGhhzFtw
DRnSfwJlDmmDDVGv
RCSQNSCQZndwbcMqQrBB
wvRlrlwVwwqzgbZRdCJBWfmdzCWfBdhf
cFcsQpNtLLsGTtNGpMdPmDdPBmmBvJPWvDtC
TpjssTFFvLLLcFFQpwbwwHngjHRrZRqZVH
mqqddrPPcPmqPDlrQnjTrbvMvbHzzsjjpTvz
gtBWgGgVhLGWHzMDztzstDHj
hfWRhBBNBGgLNQDPwdNPcPdw
LhQzdhhbTzpMhddhhhTzhnZcBFllHZFtrrHZHMHFjlHr
mwwssqDvjptrvplr
NCSgVDPDwmDgVJVpLfTznQJdhfLhnhQQ
GzjzDhjhhZzcrRgQCBjBPBBjQCgT
vHHHmntsbSgLwbsSmNHbwNbvpqPCBVppCpFTpTPTBtqWBCqV
NJbwNSwdndvmvwhGhgzcfMcDJfgJ
GncgDvvcMGnttjDvrgRRFSZZLZFWdJFJwGQwZBWZ
bPqpChPfsshfZZBdZdLTFZ
lNqqsClmbsNlPbHqPsmblmsrHdvdMngcVrjggvrvggRDcn
bDvtgVVVpMQvjQWmQL
rwTflmlfZJBBdQWQWjQqdM
HsJJmZZwscHrwTrcRbzpcbPgtCSbgz
CsCsRvshMjpbqCqf
ncblgDBgtDmmmTlBgwlgbHHqMFHLqPDMHPHHpqWfFM
TcBctSmTZTtSTzsZvsvJZRsGVb
znznvngttwltzlLwhtThHbqHPvNbNHSSHmmNWHjP
FBcLrRMFQpPqpPSpqHHW
fRQMJZJfrcMcMVrQJJftnwCzVCltgTnstTVnVL
MfLlRfCMrLzRlQgwNqQFcsGd
jtTjjBTvbdqcGjqFcj
vvShDSBDppzhCmzq
plWMptTvfrnncvcRfwqzqLGhzhzThNzNNJqD
jSdSHFPQQbdPCQCssjSbBmhJGNZZNGNqqJNBlJqqLh
VCCCVCQgjdddjCgljCjbbwgRRttgrpftfWrgvpwpnf
MWlbBcPjjvvjPWWMPqgRQZfJZDGGbRZJffQQwh
HrHrnncHpzrJQJfVDQVR
zzsSTtSTLzsspSdtTmHHmpmtFgqcgPlgFqWBqqqBMdWWvFlg
nSqBbJbqlnBBClVZcMgZVgcP
FQwrwHrRwWWFBRPNgNgcCGZZZC
rWFWFTwpwwWzHrnDbfJDLDbBBbbz
BMmNtLMMtFCNFNMvvLmcndpgcdgppPrgrGPPrgJD
WVWWhbTtVnGpjrrPhr
HWssSTHWfRHRsQQFLvfvFFCLCNMNlt
sTmDsQffVrrLCjTFltTFWL
BnwwQBJbJndMMRzMwCLlWlLWWCWLLtRlWF
cqqBMcMqwnznMGzcvDmQhrvssHmPDVssrP
pQGQGJDDrDVJbbfVzvvgPcCZwhZhncscZWWc
SqMMlBBljMmRlchhPTqThCZnPs
FMjMBmjRNFHQJJpHVhVDhG
tHNNdBdNtBBBMgsMpsZm
wVPzVvbwqzhrVqvjqzzsZpDsZDsZmsCPCgZgCM
bVbvLThvvbrWqHmmnJLdHdJQLn
PzTspPZpdLLDZTplPLpPDpvbfhnqNvqzfvNMzQQfNwnQ
GWRHmjmFWMMSnhbhHw
JWWcmtBrBtWBFWGJpsgTgldhLVLpJl
DwLMDzLMhvMcwvgdVqWWlCVgvlqF
TTSBBRpbStHZVgjWFldjRVlV
SnbTBdJBmnpQzMPDMcMznr
nNlMNBPPNtJQnbZhZsgSbh
czzCjcwTdvSbgQNcgNQq
VTdNdGDTzDTdlFFPtBrtLtDr
FMbbfMlzvFsmgVZmmg
SrNTHGmdSQDqLhtQhhgggs
dRDTSDPPcHRdHGDHlwJBbmwljmMcfjbW
sQgWLtqLtWhdqlpNZRpG
blTHTjlvTCJnJvRZdGGhHHGZhFGV
CCDlJclnCmbrmBMgcwcLWtcBsB
vqPWWvqwwCFvFZfZPRFRrcGQrQwsDrNcrwnbDNcQ
LVgJLSBBVtzTLzBMmTMJmLnnDNQcrsGbsQbNbrbDjs
zggVSmmhVdfqFhvHWG
WwdndGGmmmLwwwmRwWSncLRnZqZqhqZthBtqtBqZBgtdtvMH
FfHHzlQQDsFzzrNsVTfttZvTvttTqqtbqb
lQjFDNQFPjCsVCCDjGCwwSGGnccwcHppGp
mrjggcFsFMjdjZRpSZpn
NCqfLCFNbQPzPPlPzNfSRTRZdSdWWwndpqRSSd
vDvzzbPQFNCFtllLLNMBhMcDHGBGMggMmcBc
jhjlBvvnjbtDNPjtSjBDBbDNgHggrQrhghRQrqRrZcRwwqVg
pLdTMsWdLLmpMdqZZdPdVqZgHPwH
WLTCGmMLfPSlbGjlnnJD
gtbwhgHbHgqqbgQthgQLtZZCRjMcjjnRnrRNJmMRJrNhRc
bGWVTTvDvfpVFFBpvvVTdRDMJcrccCrJnMRnNnNCcc
FVWTBsdvdTzTBFWssVQtLgSQtHqqPzPbqHbw
dlzrPTSSjSrllzWhsvVmVtTRTWtf
bJMpLGcqGhNbJQttVQmmvRWWsp
qLbMwqqbGHFGzrlZrjhPHCrj
rNrrffVlqqrfLlPpltcBBTTGRzzZRPRsBTcJ
msbsmWSsMmQwjdMbWMhMhQmcRZRzGjTBGTBcBJBjCHJGcC
FwWbvdhbmrsFrfrgsN
rHjrQHdhdQrvSddcHWLssBSVVpBSWWWWWf
JNfTGtqDwVWBMBMpwM
qlltZgfJFvcRgcRjvc
CqfcwfDqwwmRnnqmRdNRBTRTRrdGdNpTvF
WVbzsZszBbrsvpdMpdQM
tJhbVZHWLLHDgnSwnSSgHB
TZCqqlTsqpZVVsZQJSBSLpLmppnJzmFz
brSgNtGjjRjRRjDddDtrRJcJJbJmmwcmBmnPcJFwFB
jgdRtMjNNjfqlMvShvSZSZ
dJTdqCwMNCgqTQllGBdlGBmmmZ
fcVfVcnbVfrwDLWVfncZBQPlBHRGljLZQjHGQl
brwnnfSFDvfzCTqFzgMJTh
njnsPBjjsrrnGLnbTTjGvcldQPCMllNzMvRQPCdd
ggZgfZtmZVpqZqZWDgFmgqfCcQRcRcWhQcccQddMcvRQdQ
tfqgggVgHpDwDtfwbGLJRjbLjsrLTj
JmrfrmTlDWTfgQCdHCdpqBvQdD
jsZtVzNsSNVQQHnBlVQR
PljljFjPljSsLPtFLTTgTcFrrfMJmrrmrr
hmGcmmndhmGnfmtGnDzFLwrFJQsQFzNFrNJG
ZSqPlSWcWlbgqWVTVWRVZPrjQqjzjFNJzLsNJsLJNqNL
RHcWTZbSMMMPgZcWgSWPPbVMDnBffmtdpDBddfnnvmCdfC
vSJvsbFfJfvqCsTHJswssJnLTZjjhzrrzLrzLMrzhdjM
pBNQDPcpmWDcBNgMMnZPVjdddnndhH
QWlDgmpmgDBlGRgDDgffSqwSwGCwHfvqwSFJ
jvlgvMJclPdGdtdcjMVmMHbFHFVHWHbZHZ
CwhLzLhzQpnqfpfqDVHCHbsbDFZDmHmj
LnBzfQjSzQrPvJvdSSrr
wpcvcsqclDCnVCVvWfnZ
BLRMRtbnbbBLNCjNCjVVZhbC
rFgMPSRnrRpmqpJwqFDs
LZQNQbMrZppLNLQplvlGLNvVmmmfjbwVCfjbwJwCmBCwfj
ShTPRFtTHZPCsnwswsFwCF
WtHRPdThSqZTRtDqtdRWTdpGDLLzrNczvzMGLlQLGDDM
hdcffBvldjhCMljqPwWwWNwWdwqHZr
LtQmbQRVsZQZMZPQSN
tmMRsJMpDhjJzJhv
wNQCMFCDQDBmrHmmRWrrHN
SShLnfqpcqpSZSfrzJvRVrvfrrJH
cRpqdGclpScltTQQtsFQMQsTCT
NCjggZmgfBgnBmgWbcwcTFctcWWfvb
HsDGthRGrtppSQpbFFJTVcJdFbTRvd
rPDGhDDrSzZLtzBLZMCB
RsBBMBsCBlFFCgRsBJzlMjMPNSdPhSrSrzLbmSDrDNmDSd
pZHZZJpGHHHpTTHvTncZqVLdqLbhLrDLdhrSLLbLDDdD
tGtwnJccvCtCffMBgt
wbddvVjfwPhbjjbDbbvbjvTNCNmfHZfpCZRJNzCmJmnJNC
BslcLtclZWsZJWNrRRNRpRmR
BSLBlScGtFMcssMBBFGLlQZTDZQjPddVwwbTdvvdhTZb
NSZHzmLZBnzHmLLzLSntDttDDtddhDtttDWW
QgfjsrrvNNJwtMddcvcvtq
jrfgfQpQrTTVLSNBClFV
GQWcWWPPQRcrJQNDdRcDmmLCFSnqNSmqhCNvFnql
zHfwjzpMjwZmCLqvvnlljC
ZgtVZBtHHZtgQGgPrbPRJdPv
TWdWpJTJTdgLWfWLlLFLrfrgBGsNqhGslBGHqSNqqBNshnws
ZpQmjzbZZCjZCCCPZtttRCCwsBnHNssBHbShsshHqsGBqN
RDRRPpPCzmZCtRpVVJFrfTfWFLLJggJrDv
pDDFlglsvFMgntlTMMqNffmTdfddRM
jhGJLVCHQpHGQCCzLjWdTTdZZdNdcRWNccWfNN
jQjSGjrjCQLhzVSLSCSHGDpngbrnDFtFBwBglBnBvg
wsLzstsgszcpcGLHGpcgcghlDBvQvjQvbFbQCbJBtCCJJv
mnSqRSSqSRThWRnmWWRSJDFTFCFCblbBCFQFCjFj
rZRRWqSSdZZfMVnZLspPsMgHpzMhHGPg
mwHrCLSWWwrsHCHDDsVrsmhfFZFnSSBlFlgZbbgBglbggj
GJdpcRtGJvNRdcPtdpJJdbQZfjfQBlnQBjnBtbfFnB
qcPpqqzFzJqvPVCCmWrVwhrWrz
jjMbvbhDvnRjNRGMmjbMZftSSwwwthJSffStctcwqd
lTQrVlpCVvCcfdcSJqLVcw
srHFWCHrFlrHlrsBsprljjRmDZZnmbDngNBgbNZv
MgTlQJlTQJZWpgLrRssrVqqqpRts
bBNbbzSSjMBPjzhMjsPtRVVRVPRqLttGGs
SjHBbfjNCDfjZgTlZdMJnDJW
lpThgTwtplhghgwhThqnnrdZctSZSjSZcRSRfbdrrc
RBVBGvmBmfdrcvrbbr
PmVGNGmmGRLLQwwLqTnglQ
nHwnBwBTnFHQwRsMhwghmzcm
GtprdCpdtqWdbqbrfdnPPszsWmRzRnShPszS
dGptbCfCrlnVDBJNLDLLVDLQ
CZtCjhTndCzqbCNq
dwpGvpsmwGslDszrNNrzqDMzWMgJ
vmcGccvpBVPTVTjTdTTTdZ
jWZhvZLjZfCZDwrDrSSzJGhVdJccscGsgV
blMBlRqqqgSJLBLcsJ
blmHLmFMMMnRqLmMMFqHmfPDfjQDnCDDQrZvfCjvDr
rnvnHrDLFZmMFLvrHQBMGQggBztzglplRl
sbWWhdNzsshsfhcsjJJPPbWdtQGVGllRTRjRRgBgQlpRlppB
PPCCwNWhPhNfWCzbqmFnDFFnCDLSrvZS
GChNjwWlWJWTJZBggvdgnQgdhdnd
HPsHfHHrpHDpFFrcSfsfpCMmQdntLBMgtmtBgDdLLC
SqpPscpPzpSWzjlCjjCGjl
nvgLvcLgvgvngbLprpJNTDCCRNVJrNPlDDTV
WZsMtsffGQtMzWFqFmWmWsVNJNlDwwCDVRTwJlCCDVLz
BQfGZGmmsMWFstWFmfMsfBccdncbpbSbvbbvHnLbpc
tsmDsvswNZmcZTccfh
zCTpGCbWBRWFWHGRFZJbMbJfnrhnhfMnnZ
TzFGFBRLdpHHNNQddDQDvwQN
fhBBpJgdHddjZQfmVmNzNNLmFN
qvMRrvlbwqlbTTMBMvLssFNmVzzwFDmLLzVD
TRSRWqRRMcBHhGHcdGgPGp
lSjHmtmnpHStblnpSlHSrtmMzLWzqzqCZDDTzTTWqMFqCqVV
sLRLLfPPRQfCTqqVVqFT
dNJgRPNQNsJJhBRvdJvQvNNsjSrrSmrcctpbpHtBrBjLjmSH
nwFwpppjfwSlpLTsqsTgNshhjM
ccBRGvtsmgGNPqNNGP
BCcJHvssdcWBCVmVHSSrZrwVzblpwbzZnf
rcfQRrBPPczjcRBctZDNlnVNHbgZGjVDjN
TvMsFJGSFMhJnNZlwVVnDNTZ
qhSqqmqLCLhFdJLqSvLhmQRQRWcRPczPtzrCrWGRBp
JVhdPhsFPFqLDBHVdHLPvhHDCMwcgJJwbwRgnnCMbwGwcmGC
fzjzpTZTQQQLwCbgGgbMmQcR
jzNpTzfSZtfNSWZlVVtdFFFDHHqLHVqv
TwSNnSnSGVTpNppGlPTlTcVqQrRhVBqdqBRqZqQZqQ
DcDCMfDbCMHJdrRBqbdjRBRZ
gvftMCJHcHfCDmDLgfMmMmmWlwWnWsTTwlGTlWTwppNlGL
pbGMbllDQPhhWWQDpPgVGlMCvRRrQLcCCcfBBQzLBcvQBv
wqnJjSmjrstdqwwFBLcRsBRRszzLFC
qwdddTJTdHtjndqJqHZHmwVWGpDbGTlbWWpWWrPGhhhM
WGllqLjjLCpSffmBmvfpHs
dnrQwZzRTdZwnCThdzzFTVmcBHBJBmsHfBPHcfvcSVHs
QgQrzCdrTRCZzrZLbjGLqNMWGgNNLt
sgPnhPPTTPTTwlJfwNHlqcfs
LMCpFbLLbRpMGbMcCFLVlNlNqrHqVfbHHwNDwr
GjBcCCtWMtMRZTSvgWQTngvg
BCMtJJMpRDlMMvBJBBnfjtcjPhPmZgnhgdcf
NrsrsqFNvrVLVGVrsHsqFgfmcPGdcmhfjdPgfjcnZd
zFTzsNqHqFssLVLQqNTFbsBDwCCwvWlDwRMRCTRBDMDS
zQtLgvggSRtgvVRtLvvnzdnjnGwGdmmrlpnlGz
JssBFpqsDqPNnlWWjrrjqrnj
DHDFBNDfPbJBsFHNMPvpvStQvMRVTtgVTVtv
FvzttFvBTJJzLbvwhCnnVnWwjCnBNC
mQdZgZPDPdPPSsMSQPdZgCwVGmnwnWpGnGhqNWjWCG
ggdDgfQSdcjtFHjlLJfF
ghcgScNNSsCvGSzmpVFlZbrzcFcV
MWWRLRqqqdQwTtLjjmqMlFpFlzVnbFVDwplFzlDr
LHMHqdHWjdQMdMtLHHLtWjJRsGCGSNghmSvPBJBNhsGfvfGP
CbVqqqDbcbMHnnDqcCbrRFCfBvvwGjzrBwQGzrwwBjGwBQ
sTPmpNWdWPTJssSSLPfNljjBvflGtjwwBzMG
mmWgmgSZLTLMZWpnhqZbhFFCnhqnnn
QQmjmZqnmQrfTZlbbcVbBcfbHfzf
vpdSNShNppFdSRtdGBqvJBDlDzqbPPHVBH
tRNSNRFhNpSRhFRMFtGhRGswLZZsZqWnmrmZwqwsTZmmmQ
gGWCllFCGWtGGWdlGlWNZdwpnnSbwpMvpphZpndn
RsshDDLcQVMSJQwJwnvw
HVPzrPcDNhPFGhPC
jtHQGHjGGtdTLjnqTQlmvRPRPBBwRBnFPPWP
hZbzNzVrczZzcbNssVspZZVvBwbmPmJPWmvbBRvPlmvRJF
fzNVDsZMhzpVhpVhlZcMNfcDDdQTLTjGDTCqGCjtSQHdHL
GrbFggGrTrzSrgfwJjdTmwmNJZJd
VMPQplPDptchwdsjmlml
MqMWtBDPPWDWHQtvqQtWPjbzCGLgSBgGbzgrzFgnnz
fcJccCcwcDfcpbRnCfWJnQJqtqtqPQdsGdgPsgTQqg
LSjVMhzSFFrljdNbltNGtgdqQq
MMhSHFFMLzBWDcHHcfcHwb
rwmWtJWMwSNRJMtwNmMrrSsmtTjjlgqnTqZZZPlHnTngTTgn
BGqGqqFBFggjjdGHlj
QDhhLbDQCDFMNcmhRhqJNW
BnRnRvMnLGLSCHvvSnlRfWbbTNQJsJsbNbJTBfQT
tzMmmMwjhcpFjDmMcptrcjzFQggfQPTsWsfgNbbgfhJbPhQT
FdzcrtDwDMtcwtFGRZdRLvdnHRSZZv
HVpsSpvjpNjsBmbGFBnMNnDM
WRRWhZtfrVtLJrBZMnDmDbnZBTGF
thhPLzWzhzwPtLRLWrQlpPvvClcVcCppSvpl
lZPbhnZLRPnnPZZPdlGMBWcBMgMQHBBcvvvzBL
jpFjmwwwCDDbsjvjjgcvQgcNBQ
rbFmppbwhqhGRGZr
ggrLwFgWCBwbMWBbFwLMgNBZdmZHclJPllnJlNRPmSNZRR
ppszzDfhDfhsqpnvDVTfGpSPlPmclHcdRcZmmmdPPGSP
pvtDDVDVpqDfzDfngBLCwQrgCtCwFwrg
pbGjFFGGDjpbsGsmNhNFNRBBBtRhhhHv
JnczJVCvwWJvhPgghgNtNtNJ
nwVSSzdzzqSpvQSZQG
mssLLttQrsMrMzLCRmMmrrSQpvWpDNlBTBDlvNTccDQl
HdHJwJqVPwHnqJwbjJbGjnSgSTWPpNgWWpgBBgcvDWWN
ZHVwVZGwwdndqJVJqfHbGwnwrRLtLMftMvMMRrhmLMthhLmz
RgHGLbTqlZlPRZPHfvvfZttJnvfvjnzr
sVcChDVDccwNhhvjTvVzWJjnzFff
mpNcCMTCGmLqBLGH
wVJwHJHVMtMpBmDDWPQVPWDGDD
zCrlZzCblBvnCDWNGLmvGDLPNG
dqZglgbzrzbbgZqzTFSBHHFJSSSfjjSMfwhj
NMWJSjLMCnHHNMNNHWCHMbVVGBPZTrPVPBVDrBSDGTTr
zvttlFpgdtldwwvftPDPTWQdBZrsrWrGBZ
hFlFmhRFvfCbmWJWHcnj

1000
2022/inputs/04.txt Normal file

File diff suppressed because it is too large Load Diff

106
2022/src/common.rs Normal file
View File

@@ -0,0 +1,106 @@
//! Common helper utilities to all days
use anyhow::Result;
use nom::error::ErrorKind;
use nom::error::ParseError;
use nom::Finish;
use nom::IResult;
use nom::InputLength;
use nom::Parser;
/// Parse input from some nom parser and return as an anyhow result
///
/// This method exists as a convenience because nom's errors cannot otherwise be easily converted to
/// an anyhow error, and I don't want to keep track of custom error implementations here.
pub fn parse_input<'a, O>(
input: &'a [u8],
mut parser: impl Parser<&'a [u8], O, nom::error::Error<&'a [u8]>>,
) -> Result<O> {
let (unparsed, output) = parser.parse(input).finish().map_err(|e| {
anyhow::anyhow!(
"Parser error {:?} to parse at {}",
e.code,
String::from_utf8_lossy(e.input)
)
})?;
if !unparsed.is_empty() {
Err(anyhow::anyhow!(
"Not all input consumed: {}",
String::from_utf8_lossy(unparsed)
))
} else {
Ok(output)
}
}
/// Applies a parser iteratively and reduces the results using the given function. Fails if the
/// embedded parser doesn't return at least one result.
///
/// # Arguments
/// - `f`: the function to apply
/// - `g`: the function that combines the result o `f` with previous results
///
/// This implementation is based on [`nom::multi::fold_many1`] with minor differences. If
/// successful, this should probably be upstreamed.
pub fn reduce_many1<I, O, E, F>(
mut f: F,
mut g: impl FnMut(O, O) -> O,
) -> impl FnMut(I) -> IResult<I, O, E>
where
I: Clone + InputLength,
E: ParseError<I>,
F: Parser<I, O, E>,
{
// Cannot delegate to fold_many0 because that would make the function FnOnce rather than FnMut,
// since it would transfer ownership of the embedded parser to fold_many0.
move |i: I| {
let _i = i.clone();
match f.parse(_i) {
Err(nom::Err::Error(_)) => {
Err(nom::Err::Error(E::from_error_kind(i, ErrorKind::Many1)))
}
Err(e) => Err(e),
Ok((i1, mut acc)) => {
let mut input = i1;
loop {
let _input = input.clone();
let len = input.input_len();
match f.parse(_input) {
Err(nom::Err::Error(_)) => {
break;
}
Err(e) => return Err(e),
Ok((i, o)) => {
// infinite loop check: the parser must always consume
if i.input_len() == len {
return Err(nom::Err::Failure(E::from_error_kind(
i,
ErrorKind::Many1,
)));
}
acc = g(acc, o);
input = i;
}
}
}
Ok((input, acc))
}
}
}
}
/// Return the minimum and maximum of two unordered variables
pub fn minmax<T>(a: T, b: T) -> (T, T)
where
T: PartialOrd,
{
if a < b {
(a, b)
} else {
(b, a)
}
}

50
2022/src/day01.rs Normal file
View File

@@ -0,0 +1,50 @@
use std::ops::Add;
use anyhow::Result;
use nom::character::complete::newline;
use nom::multi::separated_list0;
use nom::sequence::terminated;
use nom::IResult;
use crate::common::parse_input;
use crate::common::reduce_many1;
fn parse_elf(input: &[u8]) -> IResult<&[u8], i32> {
reduce_many1(terminated(nom::character::complete::i32, newline), Add::add)(input)
}
pub fn part1(input: &[u8]) -> Result<String> {
let elves = parse_input(input, parse_elf_list)?;
Ok(elves.into_iter().fold(0, Ord::max).to_string())
}
fn parse_elf_list(input: &[u8]) -> IResult<&[u8], Vec<i32>> {
separated_list0(newline, parse_elf)(input)
}
pub fn part2(input: &[u8]) -> Result<String> {
let mut elves = parse_input(input, parse_elf_list)?;
let (first, third, _) = elves.select_nth_unstable_by(2, |a, b| Ord::cmp(b, a));
let result = first[1] + first[0] + *third;
Ok(result.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
const SAMPLE: &[u8] = include_bytes!("samples/01.txt");
#[test]
fn sample_part1() {
assert_eq!(part1(SAMPLE).unwrap(), "24000");
}
#[test]
fn sample_part2() {
assert_eq!(part2(SAMPLE).unwrap(), "45000");
}
}

125
2022/src/day02.rs Normal file
View File

@@ -0,0 +1,125 @@
use anyhow::Result;
use nom::character::complete::newline;
use nom::combinator::map_res;
use nom::multi::many0;
use nom::sequence::separated_pair;
use nom::sequence::terminated;
use nom::IResult;
use crate::common::parse_input;
#[derive(Copy, Clone, Eq, PartialEq)]
enum Rps {
Rock,
Paper,
Scissors,
}
impl Rps {
/// Score we get by playing this move
fn score(self) -> u32 {
match self {
Rps::Rock => 1,
Rps::Paper => 2,
Rps::Scissors => 3,
}
}
/// Score we get from the result from playing given other
fn score_against(self, other: Self) -> u32 {
match (self, other) {
(a, b) if a == b => 3,
(Rps::Rock, Rps::Paper) | (Rps::Paper, Rps::Scissors) | (Rps::Scissors, Rps::Rock) => 0,
_ => 6,
}
}
/// Score if the result is according to the instruction
fn score_result(self) -> u32 {
match self {
Rps::Rock => 0, // Rock is lose
Rps::Paper => 3, // Paper is draw
Rps::Scissors => 6, // Scissors is win
}
}
/// Move we need to achieve the result indicated by self
fn needed(self, other: Self) -> Self {
match (self, other) {
(Rps::Paper, other) => other,
(Rps::Rock, Rps::Rock) => Rps::Scissors,
(Rps::Rock, Rps::Paper) => Rps::Rock,
(Rps::Rock, Rps::Scissors) => Rps::Paper,
(Rps::Scissors, Rps::Rock) => Rps::Paper,
(Rps::Scissors, Rps::Paper) => Rps::Scissors,
(Rps::Scissors, Rps::Scissors) => Rps::Rock,
}
}
}
impl TryFrom<u8> for Rps {
type Error = anyhow::Error;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
b'A' | b'X' => Ok(Rps::Rock),
b'B' | b'Y' => Ok(Rps::Paper),
b'C' | b'Z' => Ok(Rps::Scissors),
_ => Err(anyhow::anyhow!("Invalid RPS: {value}")),
}
}
}
fn parse_plan(input: &[u8]) -> IResult<&[u8], Vec<(Rps, Rps)>> {
fn parse_rps(input: &[u8]) -> IResult<&[u8], Rps> {
// Note: alpha1 also sort of works but is significantly slower
map_res(nom::bytes::complete::take(1usize), |v: &[u8]| {
Rps::try_from(v[0])
})(input)
}
fn parse_line(input: &[u8]) -> IResult<&[u8], (Rps, Rps)> {
separated_pair(parse_rps, nom::character::complete::char(' '), parse_rps)(input)
}
many0(terminated(parse_line, newline))(input)
}
pub fn part1(input: &[u8]) -> Result<String> {
let plan = parse_input(input, parse_plan)?;
let result: u32 = plan
.into_iter()
.map(|(them, us)| us.score() + us.score_against(them))
.sum();
Ok(result.to_string())
}
pub fn part2(input: &[u8]) -> Result<String> {
let plan = parse_input(input, parse_plan)?;
let result: u32 = plan
.into_iter()
.map(|(them, us)| us.score_result() + us.needed(them).score())
.sum();
Ok(result.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
const SAMPLE: &[u8] = include_bytes!("samples/02.txt");
#[test]
fn sample_part1() {
assert_eq!(part1(SAMPLE).unwrap(), "15")
}
#[test]
fn sample_part2() {
assert_eq!(part2(SAMPLE).unwrap(), "12")
}
}

79
2022/src/day03.rs Normal file
View File

@@ -0,0 +1,79 @@
use anyhow::Result;
use itertools::Itertools;
fn priority(item: u8) -> u32 {
match item {
b'a'..=b'z' => item - b'a' + 1,
b'A'..=b'Z' => item - b'A' + 27,
_ => 0,
}
.into()
}
fn seen(backpack: &[u8]) -> u64 {
let mut seen = 0;
for &b in backpack {
seen |= 1 << priority(b);
}
seen
}
pub fn part1(input: &[u8]) -> Result<String> {
let mut total = 0;
for line in input.split(|&b| b == b'\n') {
let (first, last) = line.split_at(line.len() / 2);
let seen = seen(first);
for &b in last {
let prio = priority(b);
if (seen & (1 << prio)) != 0 {
total += prio;
break;
}
}
}
Ok(total.to_string())
}
pub fn part2(input: &[u8]) -> Result<String> {
let mut total = 0;
for chunk in &input.split(|&b| b == b'\n').chunks(3) {
let mut mask = u64::MAX;
for backpack in chunk {
let seen = seen(backpack);
mask &= seen;
}
if mask != 0 {
debug_assert_eq!(1, mask.count_ones());
total += mask.trailing_zeros();
}
}
Ok(total.to_string())
}
#[cfg(test)]
mod tests {
use super::*;
const SAMPLE: &[u8] = include_bytes!("samples/03.txt");
#[test]
fn sample_part1() {
assert_eq!(part1(SAMPLE).unwrap(), "157")
}
#[test]
fn sample_part2() {
assert_eq!(part2(SAMPLE).unwrap(), "70")
}
}

75
2022/src/day04.rs Normal file
View File

@@ -0,0 +1,75 @@
use std::ops::RangeInclusive;
use anyhow::Result;
use nom::bytes::complete::tag;
use nom::character::complete::newline;
use nom::combinator::map;
use nom::multi::many0;
use nom::sequence::separated_pair;
use nom::sequence::terminated;
use nom::IResult;
use crate::common::parse_input;
type Assignment = RangeInclusive<u32>;
fn parse_assignments(
input: &[u8],
) -> IResult<&[u8], Vec<(RangeInclusive<u32>, RangeInclusive<u32>)>> {
use nom::character::complete::u32;
fn parse_single(input: &[u8]) -> IResult<&[u8], Assignment> {
map(separated_pair(u32, tag("-"), u32), |(start, end)| {
start..=end
})(input)
}
let parse_line = separated_pair(parse_single, tag(","), parse_single);
many0(terminated(parse_line, newline))(input)
}
fn is_contained(a: &Assignment, b: &Assignment) -> bool {
if a.size_hint().0 > b.size_hint().0 {
a.contains(b.start()) && a.contains(b.end())
} else {
b.contains(a.start()) && b.contains(a.end())
}
}
fn is_overlapping(a: &Assignment, b: &Assignment) -> bool {
b.end() >= a.start() && b.start() <= a.end() || a.end() >= b.start() && a.start() <= b.end()
}
fn parts_common(input: &[u8], filter: impl Fn(&Assignment, &Assignment) -> bool) -> Result<String> {
let assigments = parse_input(input, parse_assignments)?;
let overlapping = assigments.into_iter().filter(|(a, b)| filter(a, b)).count();
Ok(overlapping.to_string())
}
pub fn part1(input: &[u8]) -> Result<String> {
parts_common(input, is_contained)
}
pub fn part2(input: &[u8]) -> Result<String> {
parts_common(input, is_overlapping)
}
#[cfg(test)]
mod tests {
use super::*;
const SAMPLE: &[u8] = include_bytes!("samples/04.txt");
#[test]
fn sample_part1() {
assert_eq!(part1(SAMPLE).unwrap(), "2")
}
#[test]
fn sample_part2() {
assert_eq!(part2(SAMPLE).unwrap(), "4")
}
}

9
2022/src/day05.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day06.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day07.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day08.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day09.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day10.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day11.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day12.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day13.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day14.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day15.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day16.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day17.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day18.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day19.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day20.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day21.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day22.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day23.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

9
2022/src/day24.rs Normal file
View File

@@ -0,0 +1,9 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}
pub fn part2(_input: &[u8]) -> Result<String> {
todo!()
}

5
2022/src/day25.rs Normal file
View File

@@ -0,0 +1,5 @@
use anyhow::Result;
pub fn part1(_input: &[u8]) -> Result<String> {
todo!()
}

91
2022/src/lib.rs Normal file
View File

@@ -0,0 +1,91 @@
use anyhow::Result;
mod common;
mod day01;
mod day02;
mod day03;
mod day04;
mod day05;
mod day06;
mod day07;
mod day08;
mod day09;
mod day10;
mod day11;
mod day12;
mod day13;
mod day14;
mod day15;
mod day16;
mod day17;
mod day18;
mod day19;
mod day20;
mod day21;
mod day22;
mod day23;
mod day24;
mod day25;
type Solution = fn(&[u8]) -> Result<String>;
pub fn get_implementation(day: u8, part2: bool) -> Result<Solution> {
if !part2 {
match day {
1 => Ok(day01::part1),
2 => Ok(day02::part1),
3 => Ok(day03::part1),
4 => Ok(day04::part1),
5 => Ok(day05::part1),
6 => Ok(day06::part1),
7 => Ok(day07::part1),
8 => Ok(day08::part1),
9 => Ok(day09::part1),
10 => Ok(day10::part1),
11 => Ok(day11::part1),
12 => Ok(day12::part1),
13 => Ok(day13::part1),
14 => Ok(day14::part1),
15 => Ok(day15::part1),
16 => Ok(day16::part1),
17 => Ok(day17::part1),
18 => Ok(day18::part1),
19 => Ok(day19::part1),
20 => Ok(day20::part1),
21 => Ok(day21::part1),
22 => Ok(day22::part1),
23 => Ok(day23::part1),
24 => Ok(day24::part1),
25 => Ok(day25::part1),
_ => anyhow::bail!("Invalid day for part 1: {day}"),
}
} else {
match day {
1 => Ok(day01::part2),
2 => Ok(day02::part2),
3 => Ok(day03::part2),
4 => Ok(day04::part2),
5 => Ok(day05::part2),
6 => Ok(day06::part2),
7 => Ok(day07::part2),
8 => Ok(day08::part2),
9 => Ok(day09::part2),
10 => Ok(day10::part2),
11 => Ok(day11::part2),
12 => Ok(day12::part2),
13 => Ok(day13::part2),
14 => Ok(day14::part2),
15 => Ok(day15::part2),
16 => Ok(day16::part2),
17 => Ok(day17::part2),
18 => Ok(day18::part2),
19 => Ok(day19::part2),
20 => Ok(day20::part2),
21 => Ok(day21::part2),
22 => Ok(day22::part2),
23 => Ok(day23::part2),
24 => Ok(day24::part2),
_ => anyhow::bail!("Invalid day for part 2: {day}"),
}
}
}

61
2022/src/main.rs Normal file
View File

@@ -0,0 +1,61 @@
use std::fs::File;
use std::io::Read;
use std::num::NonZeroU8;
use std::path::PathBuf;
use std::time::Instant;
use anyhow::Result;
use clap::Parser;
use aoc_2022::get_implementation;
/// Advent of Code 2022 runner
#[derive(Parser)]
struct Opts {
/// Which day to run
day: NonZeroU8,
/// Print time taken
#[clap(short, long)]
time: bool,
/// Run part 2 instead of part 1
#[clap(short = '2', long)]
part2: bool,
/// Read input from the given file instead of stdin
#[clap(short, long)]
input: Option<PathBuf>,
}
impl Opts {
fn input_data(&self) -> Result<Vec<u8>> {
let mut buffer = Vec::new();
if let Some(input) = &self.input {
File::open(input)?.read_to_end(&mut buffer)?;
} else {
std::io::stdin().read_to_end(&mut buffer)?;
}
Ok(buffer)
}
}
fn main() -> Result<()> {
let opts: Opts = Opts::parse();
let input = opts.input_data()?;
let implementation = get_implementation(opts.day.get(), opts.part2)?;
let begin = Instant::now();
let result = implementation(&input)?;
if opts.time {
eprintln!("Execution time: {:?}", Instant::now().duration_since(begin));
}
println!("{}", result);
Ok(())
}

View File

14
2022/src/samples/01.txt Normal file
View File

@@ -0,0 +1,14 @@
1000
2000
3000
4000
5000
6000
7000
8000
9000
10000

3
2022/src/samples/02.txt Normal file
View File

@@ -0,0 +1,3 @@
A Y
B X
C Z

6
2022/src/samples/03.txt Normal file
View File

@@ -0,0 +1,6 @@
vJrwpWtwJgWrhcsFMMfFFhFp
jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
PmmdzqPrVvPwwTWBwg
wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
ttgJtRGJQctTZtZT
CrZsJsPPZsGzwwsLwLmpwMDw

6
2022/src/samples/04.txt Normal file
View File

@@ -0,0 +1,6 @@
2-4,6-8
2-3,4-5
5-7,7-9
2-8,3-7
6-6,4-6
2-6,4-8

View File

@@ -1,6 +1,6 @@
# Advent of Code
[![Advent of Code 2021](https://github.com/bertptrs/adventofcode/actions/workflows/2021.yml/badge.svg)](https://github.com/bertptrs/adventofcode/actions/workflows/2021.yml)
[![Advent of Code 2021](https://github.com/bertptrs/adventofcode/actions/workflows/2022.yml/badge.svg)](https://github.com/bertptrs/adventofcode/actions/workflows/2022.yml)
This repository contains my solutions for Advent of Code. See:
@@ -11,3 +11,4 @@ This repository contains my solutions for Advent of Code. See:
- [2019 edition](./2019)
- [2020 edition](./2020)
- [2021 edition](./2021)
- [2022 edition](./2022)