initial commit

This commit is contained in:
Felix Spöttel 2021-12-29 14:12:01 +01:00
commit 4449f5bc94
19 changed files with 531 additions and 0 deletions

5
.cargo/config Normal file
View File

@ -0,0 +1,5 @@
[build]
rustflags = ["-C", "target-cpu=native"]
[alias]
rr = "run --release"

16
.editorconfig Normal file
View File

@ -0,0 +1,16 @@
# EditorConfig is awesome: http://EditorConfig.org
root = true
[*]
indent_size = 4
indent_style = space
end_of_line = lf
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
[*.txt]
insert_final_newline = false
[*.md]
trim_trailing_whitespace = false

19
.github/workflows/readme-stars.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: Update README ⭐
on:
schedule:
- cron: "51 */4 * * *" # Every 4 hours
workflow_dispatch:
jobs:
update-readme:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: k2bd/advent-readme-stars@v1
with:
userId: ${{ secrets.AOC_USER_ID }}
sessionCookie: ${{ secrets.AOC_SESSION }}
year: ${{ secrets.AOC_YEAR }}
- uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: Update README stars

22
.github/workflows/rust.yml vendored Normal file
View File

@ -0,0 +1,22 @@
name: Rust
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose

20
.gitignore vendored Normal file
View File

@ -0,0 +1,20 @@
# Generated by Cargo
# will have compiled files and executables
debug/
target/
# These are backup files generated by rustfmt
**/*.rs.bk
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
# Added by cargo
/target
# Advent of Code
# @see https://old.reddit.com/r/adventofcode/comments/k99rod/sharing_input_data_were_we_requested_not_to/gf2ukkf/?context=3
inputs
!inputs/.keep

64
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,64 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in executable 'aoc'",
"cargo": {
"args": [
"test",
"--no-run",
"--bin=aoc",
"--package=aoc"
],
"filter": {
"name": "aoc",
"kind": "bin"
}
},
"args": [],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug executable 'aoc'",
"cargo": {
"args": [
"build",
"--bin=aoc",
"--package=aoc"
],
"filter": {
"name": "aoc",
"kind": "bin"
}
},
"args": ["1"],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug unit tests in library 'aoc'",
"cargo": {
"args": [
"test",
"--no-run",
"--lib",
"--package=aoc"
],
"filter": {
"name": "aoc",
"kind": "lib"
}
},
"args": [],
"cwd": "${workspaceFolder}"
}
]
}

25
Cargo.lock generated Normal file
View File

@ -0,0 +1,25 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "aoc"
version = "0.1.0"
dependencies = [
"itertools",
]
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "itertools"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
dependencies = [
"either",
]

13
Cargo.toml Normal file
View File

@ -0,0 +1,13 @@
[package]
name = "aoc"
version = "0.1.0"
authors = ["Felix Spöttel <1682504+fspoettel@users.noreply.github.com>"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[profile.release]
lto = "thin"
[dependencies]
itertools = "0.10.1"

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Felix Spoettel
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

124
README.md Normal file
View File

@ -0,0 +1,124 @@
<img src="./assets/christmas_ferris.png" width="164" align="center">
# 🎄 [Advent of Code](https://adventofcode.com/)
![Language](https://badgen.net/badge/Language/Rust/orange)
<!--- advent_readme_stars table --->
---
Generated with the [advent-of-code-rust](https://github.com/fspoettel/advent-of-code-rust) template.
## Create your own
1. Open the [advent-of-code-rust](https://github.com/fspoettel/advent-of-code-rust) template on Github.
2. Click `Use this template` and create your repository.
3. Clone the repository to your machine.
## Install
* Install the [Rust toolchain](https://www.rust-lang.org/tools/install).
* (optional) Install [rust-analyzer](https://rust-analyzer.github.io/manual.html) for your editor.
* (optional) Install a native debugger, e.g. [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb) for VS Code.
* (optional) Install [`aoc-cli`](https://github.com/scarvalhojr/aoc-cli/) and follow their setup guide to use the `download` script for puzzle inputs. (see below)
* (optional) Setup the README stars github action. (see below)
## Commands
### Setup new day
```sh
# example: `./scripts/scaffold.sh 1`
./scripts/scaffold.sh <day>
# output:
# Created module `src/solutions/day01.rs`
# Created input file `src/inputs/day01.txt`
# Created example file `src/examples/day01.txt`
# Linked new module in `src/main.rs`
# Linked new module in `src/solutions/mod.rs`
# Done! 🎄
```
Every solution file has _unit tests_ referencing the example input file. You can use these tests to develop and debug your solution. When editing a solution file, `rust-analyzer` will display buttons for these actions above the unit tests.
### Download inputs for a day
```sh
# example: `./scripts/download.sh 1`
./scripts/download.sh <day>
# output:
# Invoking `aoc` cli...
# Loaded session cookie from "/home/foo/.adventofcode.session".
# Downloading input for day 1, 2021...
# Saving puzzle input to "/tmp/..."...
# Done!
# Wrote input to `src/inputs/day01.txt`...
# Done! 🎄
```
Puzzle inputs are not checked into git. [See here](https://old.reddit.com/r/adventofcode/comments/k99rod/sharing_input_data_were_we_requested_not_to/gf2ukkf/?context=3) why.
### Run solutions for a day
```sh
# example: `cargo run 1`
cargo run <day>
# output:
# Running `target/debug/aoc 1`
# ----
#
# 🎄 Part 1 🎄
#
# 6 (elapsed: 37.03µs)
#
# 🎄 Part 2 🎄
#
# 9 (elapsed: 33.18µs)
#
# ----
```
To run an optimized version for benchmarking, use the `--release` flag or the alias `cargo rr <day>`.
### Run all solutions against example input
```sh
cargo test
```
### Format code
```sh
cargo fmt
```
### Lint code
```sh
cargo clippy
```
## Setup readme stars
This template includes [a Github action](https://github.com/k2bd/advent-readme-stars) that automatically updates the readme with your advent of code progress.
To enable it, you need to do two things:
1. set repository secrets.
2. create a private leaderboard.
### Repository secrets
Go to the _Secrets_ tab in your repository settings and create the following secrets:
* `AOC_USER_ID`: Go to [this page](https://adventofcode.com/settings) and copy your user id. It's the number behind the `#` symbol in the first name option. Example: `3031`
* `AOC_YEAR`: the year you want to track. Example: `2021`
* `AOC_SESSION`: an active session for the advent of code website. To get this, press F12 anywhere on the Advent of Code website to open your browser developer tools. Look in your Cookies under the Application or Storage tab, and copy out the `session` cookie.
### Private Leaderboard
Go to the leaderboard page of the year you want to track and click _Private Leaderboard_. If you have not created a leaderboard yet, create one by clicking _Create It_. Your leaderboard should be accessible under `https://adventofcode.com/{year}/leaderboard/private/view/{aoc_user_id}`.

BIN
assets/banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
assets/christmas_ferris.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

38
scripts/download.sh Executable file
View File

@ -0,0 +1,38 @@
#!/bin/bash
set -e;
if ! command -v 'aoc' &> /dev/null
then
echo "command \`aoc\` not found. Try running \`cargo install aoc-cli\` to install it."
exit 1
fi
if [ ! -n "$1" ]; then
>&2 echo "Argument is required for day."
exit 1
fi
day=$(echo $1 | sed 's/^0*//');
day_padded=`printf %02d $day`;
filename="day$day_padded";
input_path="src/inputs/$filename.txt";
tmp_dir=$(mktemp -d);
tmp_file_path="$tmp_dir/input";
aoc download --day $day --file $tmp_file_path;
cat $tmp_file_path > $input_path;
echo "Wrote input to \"$input_path\"...";
cat <<EOF
_==_ _
_,(",)|_|
\/. \-|
__( : )|_ Done!
EOF
# Make sure it gets removed even if the script exits abnormally.
trap "exit 1" HUP INT PIPE QUIT TERM
trap 'rm -rf "$tmp_dir"' EXIT

76
scripts/scaffold.sh Executable file
View File

@ -0,0 +1,76 @@
#!/bin/bash
set -e;
if [ ! -n "$1" ]; then
>&2 echo "Argument is required for day."
exit 1
fi
day=$(echo $1 | sed 's/^0*//');
day_padded=`printf %02d $day`;
filename="day$day_padded";
input_path="src/inputs/$filename.txt";
example_path="src/examples/$filename.txt";
module_path="src/solutions/$filename.rs";
touch $module_path;
cat > $module_path <<EOF
pub fn part_one(input: &str) -> u32 {
0
}
pub fn part_two(input: &str) -> u32 {
0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_part_one() {
use aoc::read_file;
let input = read_file("examples", day);
assert_eq!(part_one(&input), 0);
}
#[test]
fn test_part_two() {
use aoc::read_file;
let input = read_file("examples", day);
assert_eq!(part_two(&input), 0);
}
}
EOF
perl -pi -e "s,day,$day,g" $module_path;
echo "Created module \"$module_path\"";
touch $input_path;
echo "Created input file \"$input_path\"";
touch $example_path;
echo "Created example file \"$example_path\"";
line=" $day => solve_day!($filename, &input),"
perl -pi -le "print '$line' if(/^*.day not solved/);" "src/main.rs";
echo "Linked new module in \"src/main.rs\"";
LINE="pub mod $filename;";
FILE="src/solutions/mod.rs";
grep -qF -- "$LINE" "$FILE" || echo "$LINE" >> "$FILE";
echo "Linked new module in \"$FILE\"";
cat <<EOF
_==_ _
_,(",)|_|
\/. \-|
__( : )|_ Done!
EOF

0
src/examples/.keep Normal file
View File

14
src/lib.rs Normal file
View File

@ -0,0 +1,14 @@
use std::env;
use std::fs;
pub fn read_file(folder: &str, day: u8) -> String {
let cwd = env::current_dir().unwrap();
let filepath = cwd
.join("src")
.join(folder)
.join(format!("day{:02}.txt", day));
let f = fs::read_to_string(filepath);
f.expect("could not open input file")
}

74
src/main.rs Normal file
View File

@ -0,0 +1,74 @@
use crate::solutions::*;
use aoc::read_file;
use std::env;
use std::fmt::Display;
use std::time::Instant;
mod helpers;
mod solutions;
static ANSI_ITALIC: &str = "\x1b[3m";
static ANSI_BOLD: &str = "\x1b[1m";
static ANSI_RESET: &str = "\x1b[0m";
fn print_result<T: Display>(func: impl FnOnce(&str) -> T, input: &str) {
let timer = Instant::now();
let result = func(input);
let time = timer.elapsed();
println!(
"{} {}(elapsed: {:.2?}){}",
result, ANSI_ITALIC, time, ANSI_RESET
);
}
macro_rules! solve_day {
($day:path, $input:expr) => {{
use $day::*;
println!("----");
println!("");
println!("🎄 {}Part 1{} 🎄", ANSI_BOLD, ANSI_RESET);
println!("");
print_result(part_one, $input);
println!("");
println!("🎄 {}Part 2{} 🎄", ANSI_BOLD, ANSI_RESET);
println!("");
print_result(part_two, $input);
println!("");
println!("----");
}};
}
fn main() {
let args: Vec<String> = env::args().collect();
let day: u8 = args[1].clone().parse().unwrap();
let input = read_file("inputs", day);
match day {
1 => solve_day!(day01, &input),
2 => solve_day!(day02, &input),
3 => solve_day!(day03, &input),
4 => solve_day!(day04, &input),
5 => solve_day!(day05, &input),
6 => solve_day!(day06, &input),
7 => solve_day!(day07, &input),
8 => solve_day!(day08, &input),
9 => solve_day!(day09, &input),
10 => solve_day!(day10, &input),
11 => solve_day!(day11, &input),
12 => solve_day!(day12, &input),
13 => solve_day!(day13, &input),
14 => solve_day!(day14, &input),
15 => solve_day!(day15, &input),
16 => solve_day!(day16, &input),
17 => solve_day!(day17, &input),
18 => solve_day!(day18, &input),
19 => solve_day!(day19, &input),
20 => solve_day!(day20, &input),
21 => solve_day!(day21, &input),
22 => solve_day!(day22, &input),
23 => solve_day!(day23, &input),
24 => solve_day!(day24, &input),
25 => solve_day!(day25, &input),
_ => println!("day not solved: {}", day),
}
}

0
src/solutions/.keep Normal file
View File

0
src/solutions/mod.rs Normal file
View File