parent
4e26cd97ec
commit
b8e82c25be
@ -0,0 +1 @@
|
||||
/target
|
||||
@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "day6"
|
||||
version = "0.1.0"
|
||||
@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "day6"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
@ -0,0 +1,2 @@
|
||||
Time: 7 15 30
|
||||
Distance: 9 40 200
|
||||
@ -0,0 +1,2 @@
|
||||
Time: 40 70 98 79
|
||||
Distance: 215 1051 2147 1005
|
||||
@ -0,0 +1,59 @@
|
||||
use std::io::{Lines, StdinLock};
|
||||
|
||||
/// A simple, but panic-y reader for stdin
|
||||
pub fn simple_stdin() -> SimpleStdinLines {
|
||||
SimpleStdinLines::new()
|
||||
}
|
||||
|
||||
pub struct SimpleStdinLines {
|
||||
pub inner: Lines<StdinLock<'static>>,
|
||||
}
|
||||
|
||||
impl SimpleStdinLines {
|
||||
pub fn new() -> Self {
|
||||
Self { inner: std::io::stdin().lines() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for SimpleStdinLines {
|
||||
type Item = String;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next().transpose().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple, but panic-y parser of arguments
|
||||
pub fn simple_args() -> SimpleArgs {
|
||||
SimpleArgs::init()
|
||||
}
|
||||
|
||||
|
||||
pub struct SimpleArgs {
|
||||
args: Vec<String>,
|
||||
}
|
||||
|
||||
impl SimpleArgs {
|
||||
pub fn init() -> Self {
|
||||
Self { args: std::env::args().collect() }
|
||||
}
|
||||
|
||||
pub fn get_flag(&self, name: &str) -> bool {
|
||||
self.args.iter().any(|x| x == name)
|
||||
}
|
||||
|
||||
pub fn get_value(&self, name: &str, default: &str) -> String {
|
||||
let pos = self.args.iter().position(|x| x == name);
|
||||
if let Some(idx) = pos {
|
||||
if let Some(s) = self.args.iter().nth(idx + 1) {
|
||||
s.clone()
|
||||
}
|
||||
else {
|
||||
String::from(default)
|
||||
}
|
||||
}
|
||||
else {
|
||||
String::from(default)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
|
||||
const RADIX: u32 = 10;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Token {
|
||||
Integer(u32),
|
||||
Special(char),
|
||||
Keyword(String),
|
||||
}
|
||||
|
||||
pub fn tokenize_line(input: &str) -> Vec<Token> {
|
||||
let mut vals = vec![];
|
||||
let mut idx = 0;
|
||||
|
||||
while idx < input.len() {
|
||||
let c = input.chars().nth(idx).unwrap();
|
||||
|
||||
if c.is_digit(RADIX) {
|
||||
let end = match input.chars().skip(idx)
|
||||
.position(|x| !x.is_digit(RADIX)) {
|
||||
|
||||
Some(pos) => idx + pos,
|
||||
None => input.len(),
|
||||
};
|
||||
let substr = &input[idx..end];
|
||||
let v = u32::from_str_radix(&substr, RADIX).unwrap();
|
||||
vals.push(Token::Integer(v));
|
||||
idx = end;
|
||||
}
|
||||
else if is_special_char(c) {
|
||||
vals.push(Token::Special(c));
|
||||
idx = idx + 1;
|
||||
}
|
||||
else if is_keyword_char(c) {
|
||||
let end = match input.chars().skip(idx)
|
||||
.position(|x| !is_keyword_char(x)) {
|
||||
|
||||
Some(pos) => idx + pos,
|
||||
None => input.len(),
|
||||
};
|
||||
vals.push(Token::Keyword(String::from(&input[idx..end])));
|
||||
idx = end;
|
||||
}
|
||||
else {
|
||||
idx = idx + 1;
|
||||
}
|
||||
}
|
||||
|
||||
vals
|
||||
}
|
||||
|
||||
fn is_special_char(c: char) -> bool {
|
||||
match c {
|
||||
':' => true,
|
||||
'|' => true,
|
||||
'*' => true,
|
||||
'.' => true,
|
||||
'-' => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
fn is_keyword_char(c: char) -> bool {
|
||||
match c {
|
||||
'a'..='z'|'A'..='Z' => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_int<'a>(iter: &mut impl Iterator<Item = &'a Token>) -> Option<u32> {
|
||||
if let Some(Token::Integer(i)) = iter.next() {
|
||||
Some(*i)
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_keyword<'a>(iter: &mut impl Iterator<Item = &'a Token>) -> Option<String> {
|
||||
if let Some(Token::Keyword(k)) = iter.next() {
|
||||
Some(k.clone())
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_special<'a>(iter: &mut impl Iterator<Item = &'a Token>) -> Option<char> {
|
||||
if let Some(Token::Special(c)) = iter.next() {
|
||||
Some(*c)
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
mod input_tools;
|
||||
mod lexer;
|
||||
|
||||
use lexer::*;
|
||||
use input_tools::*;
|
||||
|
||||
fn quadradic_eq(a: f64, b: f64, c: f64) -> (f64, f64) {
|
||||
let term1 = -b / (2.0*a);
|
||||
let term2 = b*b - 4.0*a*c;
|
||||
let term3 = f64::sqrt(term2) / (2.0*a);
|
||||
|
||||
let sol1 = term1 - term3;
|
||||
let sol2 = term1 + term3;
|
||||
|
||||
let left = f64::min(sol1, sol2);
|
||||
let right = f64::max(sol1, sol2);
|
||||
|
||||
println!("Quad {}, {}, {} : {} {} {} -> ({}, {})", a, b, c, term1, term2, term3, left, right);
|
||||
|
||||
(f64::min(sol1, sol2), f64::max(sol1, sol2))
|
||||
}
|
||||
|
||||
fn puzzle1(verbose: bool) {
|
||||
println!("Part 1");
|
||||
|
||||
let mut stdin = simple_stdin();
|
||||
let times = stdin.next().unwrap();
|
||||
let distances = stdin.next().unwrap();
|
||||
|
||||
let time_toks = tokenize_line(×);
|
||||
let distance_toks = tokenize_line(&distances);
|
||||
|
||||
let num_extract = |x: &Token| match *x {
|
||||
Token::Integer(i) => i as f64,
|
||||
_ => panic!("Bad token")
|
||||
};
|
||||
|
||||
let times: Vec<f64> = time_toks.iter().skip(2).map(num_extract).collect();
|
||||
let distances: Vec<f64> = distance_toks.iter().skip(2).map(num_extract).collect();
|
||||
|
||||
let mut win_counts = vec![];
|
||||
for idx in 0..times.len() {
|
||||
let t = times[idx];
|
||||
let d = distances[idx];
|
||||
let (left, right) = quadradic_eq(-1.0, t, -d);
|
||||
let discrete_left = f64::ceil(left + 0.01) as u32;
|
||||
let discrete_right = f64::ceil(right) as u32;
|
||||
win_counts.push(discrete_right - discrete_left);
|
||||
}
|
||||
|
||||
|
||||
if verbose {
|
||||
println!("{:?}", times);
|
||||
println!("{:?}", distances);
|
||||
println!("{:?}", win_counts);
|
||||
}
|
||||
|
||||
let products = win_counts.iter().fold(1, |acc, x| acc * x);
|
||||
println!("Products: {}", products);
|
||||
|
||||
}
|
||||
|
||||
fn puzzle2(verbose: bool) {
|
||||
println!("Part 2");
|
||||
let mut stdin = simple_stdin();
|
||||
let times = stdin.next().unwrap();
|
||||
let distances = stdin.next().unwrap();
|
||||
|
||||
let race_time_str: String = times.chars().filter(|c| c.is_digit(10)).collect();
|
||||
let distance_str: String = distances.chars().filter(|c| c.is_digit(10)).collect();
|
||||
|
||||
let race_time = u64::from_str_radix(&race_time_str, 10).unwrap() ;
|
||||
let race_distance = u64::from_str_radix(&distance_str, 10).unwrap() as f64;
|
||||
|
||||
if verbose {
|
||||
println!("Time: {}", race_time);
|
||||
println!("Distance {}", race_distance);
|
||||
}
|
||||
|
||||
let (left, right) = quadradic_eq(-1.0, race_time as f64, (-race_distance) as f64);
|
||||
let discrete_left = f64::ceil(left + 0.01) as u32;
|
||||
let discrete_right = f64::ceil(right) as u32;
|
||||
|
||||
let win_counts = discrete_right - discrete_left;
|
||||
|
||||
println!("Product: {}", win_counts);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = simple_args();
|
||||
let verbose = args.get_flag("-v");
|
||||
let part = args.get_value("-p", "1");
|
||||
|
||||
match part.as_str() {
|
||||
"1" => puzzle1(verbose),
|
||||
"2" => puzzle2(verbose),
|
||||
_ => println!("Nothing to do")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue