Cleanup for 2022 days 22 through 25: Turned into a lib and introduced parse errors
This commit is contained in:
parent
fcb2fed515
commit
bdec8d21fe
9 changed files with 288 additions and 199 deletions
126
2022/day25-full_of_hot_air/src/lib.rs
Normal file
126
2022/day25-full_of_hot_air/src/lib.rs
Normal file
|
@ -0,0 +1,126 @@
|
|||
use core::fmt::Display;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ParseError {
|
||||
InvalidChar(char),
|
||||
}
|
||||
|
||||
impl Display for ParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::InvalidChar(c) => write!(f, "Unexpected character: {c} should not be part of a SNAFU number."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum SnafuDigit { Zero, One, Two, Minus, DoubleMinus }
|
||||
struct Snafu(Vec<SnafuDigit>);
|
||||
|
||||
impl TryFrom<&str> for Snafu {
|
||||
type Error = ParseError;
|
||||
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
Ok(Self(value.chars().map(|c| match c {
|
||||
'0' => Ok(SnafuDigit::Zero),
|
||||
'1' => Ok(SnafuDigit::One),
|
||||
'2' => Ok(SnafuDigit::Two),
|
||||
'-' => Ok(SnafuDigit::Minus),
|
||||
'=' => Ok(SnafuDigit::DoubleMinus),
|
||||
c => Err(Self::Error::InvalidChar(c)),
|
||||
}).collect::<Result<Vec<_>, _>>()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SnafuDigit> for char {
|
||||
fn from(value: SnafuDigit) -> Self {
|
||||
match value {
|
||||
SnafuDigit::Zero => '0',
|
||||
SnafuDigit::One => '1',
|
||||
SnafuDigit::Two => '2',
|
||||
SnafuDigit::Minus => '-',
|
||||
SnafuDigit::DoubleMinus => '=',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Snafu {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.0.iter().cloned().map(char::from).collect::<String>())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Snafu> for isize {
|
||||
fn from(value: Snafu) -> Self {
|
||||
let mut res = 0;
|
||||
|
||||
for d in value.0 {
|
||||
res *= 5;
|
||||
match d {
|
||||
SnafuDigit::Zero => (),
|
||||
SnafuDigit::One => res += 1,
|
||||
SnafuDigit::Two => res += 2,
|
||||
SnafuDigit::Minus => res -= 1,
|
||||
SnafuDigit::DoubleMinus => res -= 2,
|
||||
}
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl From<isize> for Snafu {
|
||||
fn from(value: isize) -> Self {
|
||||
let mut digits = Vec::new();
|
||||
let mut value = value;
|
||||
|
||||
while value != 0 {
|
||||
let digit = value % 5;
|
||||
match digit {
|
||||
0 => digits.push(SnafuDigit::Zero),
|
||||
1 => digits.push(SnafuDigit::One),
|
||||
2 => digits.push(SnafuDigit::Two),
|
||||
3 => digits.push(SnafuDigit::DoubleMinus),
|
||||
4 => digits.push(SnafuDigit::Minus),
|
||||
_ => unreachable!("value%5 can only ever be one of the values above"),
|
||||
}
|
||||
if digit > 2 {
|
||||
value += 2;
|
||||
}
|
||||
value /= 5;
|
||||
}
|
||||
digits.reverse();
|
||||
|
||||
Self(digits)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(input: &str) -> Result<String, ParseError> {
|
||||
let total = input.lines()
|
||||
.map(|s| Snafu::try_from(s).map(isize::from))
|
||||
.sum::<Result<isize, _>>()?;
|
||||
|
||||
Ok(format!("{}", Snafu::from(total)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs::read_to_string;
|
||||
|
||||
fn read_file(name: &str) -> String {
|
||||
read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sample() {
|
||||
let sample_input = read_file("tests/sample_input");
|
||||
assert_eq!(run(&sample_input), Ok("2=-1=0".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_challenge() {
|
||||
let challenge_input = read_file("tests/challenge_input");
|
||||
assert_eq!(run(&challenge_input), Ok("2-0=11=-0-2-1==1=-22".to_string()));
|
||||
}
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
use std::fs;
|
||||
|
||||
fn from_snafu(snafu_num: &str) -> isize {
|
||||
let mut number = 0;
|
||||
|
||||
snafu_num.chars().for_each(|c| {
|
||||
number *= 5;
|
||||
match &c {
|
||||
'0' | '1' | '2' => number += (c as u8 - b'0') as isize,
|
||||
'-' => number -= 1,
|
||||
'=' => number -= 2,
|
||||
_ => panic!("Unexpected character: {c} should not be part of a SNAFU number."),
|
||||
}
|
||||
});
|
||||
number
|
||||
}
|
||||
|
||||
fn to_snafu(number: isize) -> String {
|
||||
let mut snafu_num = String::new();
|
||||
|
||||
let mut temp = number;
|
||||
while temp != 0 {
|
||||
let digit = ( temp % 5) as u8;
|
||||
match digit {
|
||||
0 | 1 | 2 => snafu_num.push((digit + b'0') as char),
|
||||
3 => {
|
||||
snafu_num.push('=');
|
||||
temp += 2;
|
||||
},
|
||||
_ => {
|
||||
snafu_num.push('-');
|
||||
temp += 2;
|
||||
},
|
||||
}
|
||||
temp /= 5;
|
||||
}
|
||||
|
||||
snafu_num.chars().rev().collect()
|
||||
}
|
||||
|
||||
fn read_file(path: &str) -> Vec<String> {
|
||||
fs::read_to_string(path)
|
||||
.expect("File not Found")
|
||||
.lines()
|
||||
.map(String::from)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let list = read_file("input");
|
||||
|
||||
let total = list.iter()
|
||||
.map(|snafu_num| from_snafu(snafu_num))
|
||||
.sum::<isize>();
|
||||
|
||||
println!("The total Fuel Usage is {total}, which is {} in SNAFU numbers.", to_snafu(total));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sample_input() {
|
||||
let list = read_file("tests/sample_input");
|
||||
let total = list.iter().map(|s| from_snafu(s)).sum::<isize>();
|
||||
|
||||
assert_eq!(total, 4890);
|
||||
assert_eq!(to_snafu(total), "2=-1=0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn challenge_input() {
|
||||
let list = read_file("tests/input");
|
||||
let total = list.iter().map(|s| from_snafu(s)).sum::<isize>();
|
||||
|
||||
assert_eq!(total, 34061028947237);
|
||||
assert_eq!(to_snafu(total), "2-0=11=-0-2-1==1=-22");
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue