d11: Rust: functions are easy; traits are hard
parent
b5cc74e536
commit
4c4bb6744b
|
@ -0,0 +1,4 @@
|
||||||
|
if command -v nix-shell &> /dev/null
|
||||||
|
then
|
||||||
|
use flake
|
||||||
|
fi
|
|
@ -0,0 +1,2 @@
|
||||||
|
.direnv
|
||||||
|
target
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,11 @@
|
||||||
|
[package]
|
||||||
|
name = "d9"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
bacon = "2.2.8"
|
||||||
|
rstest = "0.16.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
itertools = "0.10.5"
|
|
@ -0,0 +1 @@
|
||||||
|
# Bootstrapping clojure project on
|
|
@ -0,0 +1,27 @@
|
||||||
|
Monkey 0:
|
||||||
|
Starting items: 79, 98
|
||||||
|
Operation: new = old * 19
|
||||||
|
Test: divisible by 23
|
||||||
|
If true: throw to monkey 2
|
||||||
|
If false: throw to monkey 3
|
||||||
|
|
||||||
|
Monkey 1:
|
||||||
|
Starting items: 54, 65, 75, 74
|
||||||
|
Operation: new = old + 6
|
||||||
|
Test: divisible by 19
|
||||||
|
If true: throw to monkey 2
|
||||||
|
If false: throw to monkey 0
|
||||||
|
|
||||||
|
Monkey 2:
|
||||||
|
Starting items: 79, 60, 97
|
||||||
|
Operation: new = old * old
|
||||||
|
Test: divisible by 13
|
||||||
|
If true: throw to monkey 1
|
||||||
|
If false: throw to monkey 3
|
||||||
|
|
||||||
|
Monkey 3:
|
||||||
|
Starting items: 74
|
||||||
|
Operation: new = old + 3
|
||||||
|
Test: divisible by 17
|
||||||
|
If true: throw to monkey 0
|
||||||
|
If false: throw to monkey 1
|
|
@ -0,0 +1,55 @@
|
||||||
|
Monkey 0:
|
||||||
|
Starting items: 59, 65, 86, 56, 74, 57, 56
|
||||||
|
Operation: new = old * 17
|
||||||
|
Test: divisible by 3
|
||||||
|
If true: throw to monkey 3
|
||||||
|
If false: throw to monkey 6
|
||||||
|
|
||||||
|
Monkey 1:
|
||||||
|
Starting items: 63, 83, 50, 63, 56
|
||||||
|
Operation: new = old + 2
|
||||||
|
Test: divisible by 13
|
||||||
|
If true: throw to monkey 3
|
||||||
|
If false: throw to monkey 0
|
||||||
|
|
||||||
|
Monkey 2:
|
||||||
|
Starting items: 93, 79, 74, 55
|
||||||
|
Operation: new = old + 1
|
||||||
|
Test: divisible by 2
|
||||||
|
If true: throw to monkey 0
|
||||||
|
If false: throw to monkey 1
|
||||||
|
|
||||||
|
Monkey 3:
|
||||||
|
Starting items: 86, 61, 67, 88, 94, 69, 56, 91
|
||||||
|
Operation: new = old + 7
|
||||||
|
Test: divisible by 11
|
||||||
|
If true: throw to monkey 6
|
||||||
|
If false: throw to monkey 7
|
||||||
|
|
||||||
|
Monkey 4:
|
||||||
|
Starting items: 76, 50, 51
|
||||||
|
Operation: new = old * old
|
||||||
|
Test: divisible by 19
|
||||||
|
If true: throw to monkey 2
|
||||||
|
If false: throw to monkey 5
|
||||||
|
|
||||||
|
Monkey 5:
|
||||||
|
Starting items: 77, 76
|
||||||
|
Operation: new = old + 8
|
||||||
|
Test: divisible by 17
|
||||||
|
If true: throw to monkey 2
|
||||||
|
If false: throw to monkey 1
|
||||||
|
|
||||||
|
Monkey 6:
|
||||||
|
Starting items: 74
|
||||||
|
Operation: new = old * 2
|
||||||
|
Test: divisible by 5
|
||||||
|
If true: throw to monkey 4
|
||||||
|
If false: throw to monkey 7
|
||||||
|
|
||||||
|
Monkey 7:
|
||||||
|
Starting items: 86, 85, 52, 86, 91, 95
|
||||||
|
Operation: new = old + 6
|
||||||
|
Test: divisible by 7
|
||||||
|
If true: throw to monkey 4
|
||||||
|
If false: throw to monkey 5
|
|
@ -0,0 +1,94 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1667395993,
|
||||||
|
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"flake-utils_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1659877975,
|
||||||
|
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1670064435,
|
||||||
|
"narHash": "sha256-+ELoY30UN+Pl3Yn7RWRPabykwebsVK/kYE9JsIsUMxQ=",
|
||||||
|
"owner": "nixos",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "61a8a98e6d557e6dd7ed0cdb54c3a3e3bbc5e25c",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nixos",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1665296151,
|
||||||
|
"narHash": "sha256-uOB0oxqxN9K7XGF1hcnY+PQnlQJ+3bP2vCn/+Ru/bbc=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "14ccaaedd95a488dd7ae142757884d8e125b3363",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"rust-overlay": "rust-overlay"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rust-overlay": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils_2",
|
||||||
|
"nixpkgs": "nixpkgs_2"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1670552927,
|
||||||
|
"narHash": "sha256-lCE51eAGrAFS4k9W5aDGFpVtOAwQQ/rFMN80PCDh0vo=",
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"rev": "a0fdafd18c9cf599fde17fbaf07dbb20fa57eecb",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "oxalica",
|
||||||
|
"repo": "rust-overlay",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
description = "Python + Rust workspace for AoC";
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
rust-overlay.url = "github:oxalica/rust-overlay";
|
||||||
|
};
|
||||||
|
outputs = { nixpkgs, flake-utils, rust-overlay, ... } @ inputs:
|
||||||
|
flake-utils.lib.eachSystem flake-utils.lib.defaultSystems (sys:
|
||||||
|
let
|
||||||
|
overlays = [ rust-overlay.overlays.default ];
|
||||||
|
pkgs = import nixpkgs { system = sys; overlays = overlays; };
|
||||||
|
shellHookAfter = ''
|
||||||
|
echo "The input files should be placed under ./data/{submission,example}.txt"
|
||||||
|
echo "This problem shares one input between two parts"
|
||||||
|
'';
|
||||||
|
py_pkgs = [ pkgs.python310 ];
|
||||||
|
rs_pkgs = [
|
||||||
|
pkgs.openssl
|
||||||
|
pkgs.pkg-config
|
||||||
|
|
||||||
|
# Add rust-src, which rust-analyzer seems to rely upon
|
||||||
|
(pkgs.rust-bin.selectLatestNightlyWith
|
||||||
|
(
|
||||||
|
toolchain:
|
||||||
|
toolchain.default.override {
|
||||||
|
extensions = [ "rust-src" ];
|
||||||
|
}
|
||||||
|
))
|
||||||
|
# rust devex
|
||||||
|
pkgs.bacon # kinda like nodemon
|
||||||
|
pkgs.rust-analyzer
|
||||||
|
];
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# Jack of all trades
|
||||||
|
devShell = pkgs.mkShell
|
||||||
|
{
|
||||||
|
buildInputs = py_pkgs ++ rs_pkgs;
|
||||||
|
shellHook = ''
|
||||||
|
echo "> Default runtime. This contains both rust and python3 env"
|
||||||
|
echo "Run ./run-py.sh for Python's output and ./run-rs.sh for Rust's output"
|
||||||
|
'' + shellHookAfter;
|
||||||
|
};
|
||||||
|
devShells = {
|
||||||
|
# nix develop ./#lua
|
||||||
|
# lua = pkgs.mkShell {
|
||||||
|
# nativeBuildInputs = lua_pkgs;
|
||||||
|
# shellHook = ''
|
||||||
|
# echo "> Lua runtime"
|
||||||
|
# echo "Run ./run-lua.sh to see the output of the solution"
|
||||||
|
# '' + shellHookAfter;
|
||||||
|
# };
|
||||||
|
|
||||||
|
# nix develop ./#fennel
|
||||||
|
|
||||||
|
# nix develop ./#python
|
||||||
|
rust = pkgs.mkShell {
|
||||||
|
nativeBuildInputs = rs_pkgs;
|
||||||
|
shellHook = ''
|
||||||
|
echo "> Rust runtime"
|
||||||
|
echo "Run ./run-rs.sh to see output of the solution"
|
||||||
|
echo "For quick feedback loop: bacon check-all"
|
||||||
|
'' + shellHookAfter;
|
||||||
|
};
|
||||||
|
python = pkgs.mkShell {
|
||||||
|
nativeBuildInputs = py_pkgs;
|
||||||
|
shellHook = ''
|
||||||
|
echo "> Python3 runtime"
|
||||||
|
echo "Run ./run-py.sh to see the output of the solution"
|
||||||
|
'' + shellHookAfter;
|
||||||
|
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
#!/usr/bin/env sh
|
||||||
|
echo "example"
|
||||||
|
python3 ./src/main.py ./data/example.txt
|
||||||
|
|
||||||
|
echo "submission"
|
||||||
|
python3 ./src/main.py ./data/submission.txt
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
from typing import Iterable
|
||||||
|
import os
|
||||||
|
|
||||||
|
def part1(insns: list[list[str]], init = 1):
|
||||||
|
reg = init
|
||||||
|
regs = [reg]
|
||||||
|
for insn in insns:
|
||||||
|
match insn:
|
||||||
|
case ["noop"]:
|
||||||
|
regs.append(regs[-1])
|
||||||
|
case ["addx", adder]:
|
||||||
|
reg += int(adder)
|
||||||
|
regs.append(regs[-1])
|
||||||
|
regs.append(reg)
|
||||||
|
case e:
|
||||||
|
raise RuntimeError(f"lol what case is this? {e=}")
|
||||||
|
print("regs", os.pathsep.join(str(reg) for reg in regs))
|
||||||
|
|
||||||
|
def ret(queries: Iterable[int]):
|
||||||
|
signal_bases = [(i, regs[i-1]) for i in queries]
|
||||||
|
print(signal_bases)
|
||||||
|
return sum(signal[0] * signal[1] for signal in signal_bases)
|
||||||
|
return (regs, ret)
|
||||||
|
|
||||||
|
def part2(insns: list[list[str]], init = 1, width = 40):
|
||||||
|
regs, _ = part1(insns, init)
|
||||||
|
position = lambda cycle: (cycle) % width
|
||||||
|
active = [['#' if (position(cycle) in range(regs[cycle]-1, regs[cycle]+2)) else '.'
|
||||||
|
for cycle in range(row, min(row + width, len(regs)))]
|
||||||
|
for row in range(0, len(regs),width)]
|
||||||
|
return active
|
||||||
|
|
||||||
|
def main(lines: Iterable[str]):
|
||||||
|
instructions = [s.split() for s in (line.strip() for line in lines) if len(s) > 0]
|
||||||
|
# print("part1", part1(instructions)([1,2,3,4,5]))
|
||||||
|
print("part1", part1(instructions)[1]([20, 60, 100, 140, 180, 220]))
|
||||||
|
part2_ans = part2(instructions)
|
||||||
|
print("part2")
|
||||||
|
for line in part2_ans:
|
||||||
|
print(str().join(line))
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _main(filename: str):
|
||||||
|
with open(filename, "r") as f:
|
||||||
|
main(f)
|
||||||
|
|
||||||
|
if __name__=="__main__":
|
||||||
|
_main(sys.argv[1])
|
||||||
|
|
||||||
|
# For neovim integrated repl which can evaluate per selection
|
||||||
|
REPL = """
|
||||||
|
_main("./data/example.txt")
|
||||||
|
_main("./data/submission.txt")
|
||||||
|
main([
|
||||||
|
"noop",
|
||||||
|
"addx 3",
|
||||||
|
"addx -5"
|
||||||
|
])
|
||||||
|
|
||||||
|
[1, 1, 1, 4, 4, -1, -1, -6, -6]
|
||||||
|
|
||||||
|
"""
|
|
@ -0,0 +1,292 @@
|
||||||
|
use core::result;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
use std::iter::Iterator;
|
||||||
|
use std::num::Wrapping;
|
||||||
|
use std::ops::{Add, Div, Mul, Sub};
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
fn ex_case() -> &'static str {
|
||||||
|
include_str!("../data/example.txt")
|
||||||
|
}
|
||||||
|
fn sub_case() -> &'static str {
|
||||||
|
include_str!("../data/submission.txt")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_input(input_str: &str) -> Result<Vec<Monkey>> {
|
||||||
|
Ok(collect_errs(parse_universe(input_str))
|
||||||
|
.map_err(|e| e.collect_vec().join("\n"))?
|
||||||
|
.collect_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<()> {
|
||||||
|
// let test = include_str!("../data/example.txt");
|
||||||
|
let sub = sub_case();
|
||||||
|
let universe = process_input(sub)?;
|
||||||
|
|
||||||
|
println!("part1: {}", part1(universe.iter()));
|
||||||
|
part2(universe.iter());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If there exists an [`Err`] in [`results`], this function filters out so that
|
||||||
|
/// it only returns Err values. Otherwise, returns [`Ok`]
|
||||||
|
fn collect_errs<T, E, ResultIter: Iterator<Item = core::result::Result<T, E>>>(
|
||||||
|
results: ResultIter,
|
||||||
|
) -> result::Result<impl Iterator<Item = T>, impl Iterator<Item = E>> {
|
||||||
|
let v = results.collect_vec();
|
||||||
|
let has_err = v.iter().any(|res| res.is_err());
|
||||||
|
if has_err {
|
||||||
|
Err(v.into_iter().filter_map(|e| e.err()))
|
||||||
|
} else {
|
||||||
|
Ok(v.into_iter().filter_map(|e| e.ok()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows for lazy results
|
||||||
|
trait CanResult {
|
||||||
|
type T;
|
||||||
|
type E;
|
||||||
|
fn result(self) -> result::Result<Self::T, Self::E>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl <T, E> CanResult for core::result::Result<T, E> {
|
||||||
|
type T = T;
|
||||||
|
type E = E;
|
||||||
|
fn result(self) -> result::Result<Self::T, Self::E> {self}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait CollectErrs {
|
||||||
|
type T;
|
||||||
|
type E;
|
||||||
|
type IterT: Iterator<Item=Self::T>;
|
||||||
|
type IterE: Iterator<Item=Self::E>;
|
||||||
|
type R: CanResult<T=Self::IterT, E=Self::IterE>;
|
||||||
|
fn collect_errs(self) -> Self::R;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CollectErrsResult<T, E, ResIter: Iterator<Item=core::result::Result<T,E>>> {
|
||||||
|
input: ResIter
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl <T, E, ResIter: Iterator<Item=core::result::Result<T,E>>> CanResult for CollectErrsResult<T, E, ResIter> {
|
||||||
|
fn result(self) -> result::Result<Self::T, Self::E> {
|
||||||
|
let v = self.input.collect_vec();
|
||||||
|
let has_err = v.iter().any(|res| res.is_err());
|
||||||
|
if has_err {
|
||||||
|
Err(v.into_iter().filter_map(|e| e.err()))
|
||||||
|
} else {
|
||||||
|
Ok(v.into_iter().filter_map(|e| e.ok()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<T, E, ResIter: Iterator<Item=core::result::Result<T,E>>>
|
||||||
|
CollectErrs for ResIter
|
||||||
|
{
|
||||||
|
type T = T;
|
||||||
|
type E = E;
|
||||||
|
type IterT;
|
||||||
|
type IterE;
|
||||||
|
|
||||||
|
fn collect_errs(self) -> result::Result<Self::IterT, Self::IterE> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// <T, E, ResultIter: Iterator<Item=core::result::Result<T, E>>>
|
||||||
|
fn part1<'a>(universe: impl Iterator<Item = &'a Monkey>) -> u32 {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
fn part2<'a>(universe: impl Iterator<Item = &'a Monkey>) {
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
|
|
||||||
|
type MyErr = String;
|
||||||
|
type Result<T> = core::result::Result<T, MyErr>;
|
||||||
|
|
||||||
|
type Int = Wrapping<u32>;
|
||||||
|
|
||||||
|
trait TypeContainer<T> {
|
||||||
|
type ContainedType; // AssociatedType default is unstable
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> TypeContainer<T> for Wrapping<T> {
|
||||||
|
type ContainedType = T;
|
||||||
|
}
|
||||||
|
|
||||||
|
trait MonadFrom<T>: TypeContainer<T> {
|
||||||
|
fn monad_ctor(t: T) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl <T, Contained> MonadFrom<Contained> for T where T: TypeContainer<Contained> {
|
||||||
|
// fn monad_ctor(t: Contained) -> Self {
|
||||||
|
// Self(t) // The Self constructor can only be used with tuple or unit structs
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl<T> MonadFrom<T> for Wrapping<T> {
|
||||||
|
fn monad_ctor(t: T) -> Self {
|
||||||
|
Self(t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
enum MonkeyOperator {
|
||||||
|
Const(Int),
|
||||||
|
Id,
|
||||||
|
ConstOp {
|
||||||
|
my_const: Int,
|
||||||
|
op: fn(Int, Int) -> Int,
|
||||||
|
},
|
||||||
|
SelfOp(fn(Int, Int) -> Int),
|
||||||
|
}
|
||||||
|
impl MonkeyOperator {
|
||||||
|
fn apply(&self, old: Int) -> Int {
|
||||||
|
match &self {
|
||||||
|
Self::Const(e) => *e,
|
||||||
|
Self::Id => old,
|
||||||
|
Self::ConstOp { my_const, op } => op(*my_const, old),
|
||||||
|
Self::SelfOp(op) => op(old, old),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Parses the expression `"new"{ }"="{ }<rval_tok>[{ }<op>{ }<rval_tok>]`
|
||||||
|
fn parse_monkey_op(input: &str) -> Result<MonkeyOperator> {
|
||||||
|
// should be in the following format: old <op> <rval>
|
||||||
|
fn _op_parse(op: &str) -> Result<fn(Int, Int) -> Int> {
|
||||||
|
match op {
|
||||||
|
"*" => Ok(Int::mul),
|
||||||
|
"/" => Ok(Int::div),
|
||||||
|
"+" => Ok(Int::add),
|
||||||
|
"-" => Ok(Int::sub),
|
||||||
|
e => Err(format!("idk wtf is the case with {e}")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn _parse(v: Vec<&str>) -> Result<MonkeyOperator> {
|
||||||
|
match &v[..2] {
|
||||||
|
["new", "="] => Ok(()),
|
||||||
|
_ => Err(format!(
|
||||||
|
"monkey operation expr should start with \"new = \""
|
||||||
|
)),
|
||||||
|
}?;
|
||||||
|
match &v[2..] {
|
||||||
|
["old"] => Ok(MonkeyOperator::Id),
|
||||||
|
// I want Int::ContainedType so bad :(
|
||||||
|
[e] => e
|
||||||
|
.parse::<<Int as TypeContainer<_>>::ContainedType>()
|
||||||
|
.map(|e| MonkeyOperator::Const(Int::monad_ctor(e)))
|
||||||
|
.map_err(|e| e.to_string()),
|
||||||
|
e => Err(format!("idk wtf is the case with {:?}", e)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_parse(input.split_whitespace().collect_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct Monkey {
|
||||||
|
monkey_id: u8,
|
||||||
|
worry_q: VecDeque<u32>,
|
||||||
|
operation: MonkeyOperator,
|
||||||
|
worry_divcheck: u32,
|
||||||
|
dest_div: usize, // if divisble by worry_divcheck, which monkey to throw to?
|
||||||
|
dest_not_div: usize, // if not divisible, which monkey to throw to?
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Turns the monkey's input as specified in ../data/example.txt
|
||||||
|
/// into Monkey
|
||||||
|
fn parse_monkey(monkey_input: &str) -> Result<Monkey> {
|
||||||
|
let mut spl = monkey_input.lines();
|
||||||
|
// "Monkey 0"
|
||||||
|
let monkey_id = spl
|
||||||
|
.next()
|
||||||
|
.and_then(|id_str| id_str.split(' ').last())
|
||||||
|
// "0:" \in possible(id)
|
||||||
|
.map(|id| id.chars().take(id.len() - 1))
|
||||||
|
.ok_or_else(|| "Failed to parse monkey id input".to_string())
|
||||||
|
// "0"
|
||||||
|
.and_then(|id| {
|
||||||
|
id.collect::<String>()
|
||||||
|
.parse::<u8>()
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
})?;
|
||||||
|
// | Starting items: [item{, item}]
|
||||||
|
let worry_q = spl
|
||||||
|
.next()
|
||||||
|
.and_then(|id_str| id_str.split(": ").last())
|
||||||
|
.map(|s| s.split(", "))
|
||||||
|
.ok_or_else(|| "Failed to parse worry_q".to_string())?;
|
||||||
|
.and_then(|int_spl| int_spl.map(|int_s| int_s.parse::<u32>()))
|
||||||
|
.map(|worries| worries.collect::<VecDeque<_>>())
|
||||||
|
let operation = spl
|
||||||
|
.next()
|
||||||
|
.and_then(|op_line| op_line.split(": ").last())
|
||||||
|
.ok_or_else(|| "Failed to parse monkey operation".to_string())
|
||||||
|
.and_then(|op_str| parse_monkey_op(op_str))?;
|
||||||
|
let worry_divcheck = spl
|
||||||
|
.next()
|
||||||
|
.and_then(|div_test_ln| div_test_ln.split_whitespace().last())
|
||||||
|
.ok_or_else(|| "Failed to parse divisible test value".to_string())
|
||||||
|
.and_then(|div_str| div_str.parse::<u32>().map_err(|e| e.to_string()))?;
|
||||||
|
let mut parse_monkey_idx_div = || {
|
||||||
|
spl.next()
|
||||||
|
.and_then(|ln| ln.split_whitespace().last())
|
||||||
|
.ok_or_else(|| "Failed to parse which monkey index to throw if divisible".to_string())
|
||||||
|
.and_then(|div_str| div_str.parse::<usize>().map_err(|e| e.to_string()))
|
||||||
|
};
|
||||||
|
let dest_div = parse_monkey_idx_div()?;
|
||||||
|
let dest_no_div = parse_monkey_idx_div()?;
|
||||||
|
Monkey {
|
||||||
|
monkey_id,
|
||||||
|
worry_q,
|
||||||
|
operation,
|
||||||
|
worry_divcheck,
|
||||||
|
dest_div,
|
||||||
|
dest_no_div,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_universe<'a>(input: &'a str) -> impl Iterator<Item = Result<Monkey>> + 'a + Clone {
|
||||||
|
input.split("\n\n").map(parse_monkey)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
#[test]
|
||||||
|
fn part1_example() {
|
||||||
|
assert_eq!(part1((process_input(ex_case())).unwrap()));
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn part2_example() {}
|
||||||
|
|
||||||
|
#[rstest::rstest]
|
||||||
|
#[case(0)]
|
||||||
|
#[case(1)]
|
||||||
|
#[case(2)]
|
||||||
|
#[case(3)]
|
||||||
|
#[case(414)]
|
||||||
|
fn parse_monkeyop_id(#[case] input: u32) {
|
||||||
|
let id = parse_monkey_op("new = old").unwrap();
|
||||||
|
assert_eq!(id.apply(Int::monad_ctor(input)), Int::monad_ctor(input));
|
||||||
|
}
|
||||||
|
#[rstest::rstest]
|
||||||
|
#[case(0, 14)]
|
||||||
|
#[case(0, 1)]
|
||||||
|
#[case(0, 7)]
|
||||||
|
#[case(7, 14)]
|
||||||
|
#[case(7, 69)]
|
||||||
|
fn parse_monkey_op_const(#[case] my_const: u32, #[case] ignored_old_val: u32) {
|
||||||
|
let const_ret = parse_monkey_op(format!("new = {my_const}").as_str()).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
const_ret.apply(Int::monad_ctor(ignored_old_val)),
|
||||||
|
Int::monad_ctor(my_const)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// #[test]
|
||||||
|
// fn panic() {
|
||||||
|
// panic!("lol")
|
||||||
|
// }
|
||||||
|
}
|
Loading…
Reference in New Issue