Not a particularly elegant solution for Day 5, but a nice chance to use BTreeSet.
This commit is contained in:
157
src/bin/boarding_pass.rs
Normal file
157
src/bin/boarding_pass.rs
Normal file
@@ -0,0 +1,157 @@
|
||||
use advent2020::errors::{SeatParseError, TopLevelError};
|
||||
use std::cmp::{Ord, Ordering};
|
||||
use std::collections::BTreeSet;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::str::FromStr;
|
||||
|
||||
const PLANE_ROWS: usize = 128;
|
||||
const PLANE_COLUMNS: usize = 8;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Seat {
|
||||
row: usize,
|
||||
column: usize,
|
||||
id: usize,
|
||||
}
|
||||
|
||||
impl PartialOrd for Seat {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Seat {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Seat {}
|
||||
|
||||
impl Ord for Seat {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.id.cmp(&other.id)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Seat {
|
||||
type Err = SeatParseError;
|
||||
|
||||
fn from_str(map: &str) -> Result<Self, Self::Err> {
|
||||
if map.len() != 10 {
|
||||
return Err(SeatParseError::InvalidSeatIdentifier(map.to_string()));
|
||||
}
|
||||
|
||||
let (row_stuff, column_stuff) = map.split_at(7);
|
||||
|
||||
let mut row_high = PLANE_ROWS - 1;
|
||||
let mut row_low = 0;
|
||||
|
||||
for direction in row_stuff.chars() {
|
||||
match direction {
|
||||
'F' => row_high = row_low + ((row_high - row_low) / 2),
|
||||
'B' => row_low = row_low + ((row_high - row_low + 1) / 2),
|
||||
_ => return Err(SeatParseError::UnexpectedRowCharacter(direction)),
|
||||
}
|
||||
}
|
||||
|
||||
if row_high != row_low {
|
||||
return Err(SeatParseError::DidNotResolveRow(map.to_string()));
|
||||
}
|
||||
|
||||
let mut column_high = PLANE_COLUMNS - 1;
|
||||
let mut column_low = 0;
|
||||
|
||||
for direction in column_stuff.chars() {
|
||||
match direction {
|
||||
'L' => column_high = column_low + ((column_high - column_low) / 2),
|
||||
'R' => column_low = column_low + ((column_high - column_low + 1) / 2),
|
||||
_ => return Err(SeatParseError::UnexpectedColumnCharacter(direction)),
|
||||
}
|
||||
}
|
||||
|
||||
if column_high != column_low {
|
||||
return Err(SeatParseError::DidNotResolveColumn(map.to_string()));
|
||||
}
|
||||
|
||||
Ok(Seat {
|
||||
row: row_high,
|
||||
column: column_high,
|
||||
id: (row_high * PLANE_COLUMNS) + column_high,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn example_boarding_passes() {
|
||||
assert_eq!(
|
||||
Seat::from_str("FBFBBFFRLR"),
|
||||
Ok(Seat {
|
||||
row: 44,
|
||||
column: 5,
|
||||
id: 357
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
Seat::from_str("BFFFBBFRRR"),
|
||||
Ok(Seat {
|
||||
row: 70,
|
||||
column: 7,
|
||||
id: 567
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
Seat::from_str("FFFBBBFRRR"),
|
||||
Ok(Seat {
|
||||
row: 14,
|
||||
column: 7,
|
||||
id: 119
|
||||
})
|
||||
);
|
||||
assert_eq!(
|
||||
Seat::from_str("BBFFBBFRLL"),
|
||||
Ok(Seat {
|
||||
row: 102,
|
||||
column: 4,
|
||||
id: 820
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
fn real_main() -> Result<(), TopLevelError> {
|
||||
let mut seats = BTreeSet::new();
|
||||
let mut highest_id = 0;
|
||||
|
||||
for argument in env::args().skip(1) {
|
||||
let contents = fs::read_to_string(argument)?;
|
||||
|
||||
for line in contents.lines() {
|
||||
let seat = Seat::from_str(line)?;
|
||||
if seat.id > highest_id {
|
||||
highest_id = seat.id;
|
||||
}
|
||||
seats.insert(seat);
|
||||
}
|
||||
}
|
||||
|
||||
println!("Loaded {} seats.", seats.len());
|
||||
println!(" highest id is {}", highest_id);
|
||||
let mut last_id = 0;
|
||||
for seat in seats.iter() {
|
||||
if seat.id == last_id + 2 {
|
||||
println!(" my seat is {}", last_id + 1);
|
||||
return Ok(());
|
||||
}
|
||||
last_id = seat.id;
|
||||
}
|
||||
|
||||
Err(TopLevelError::NoSolutionFound)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
match real_main() {
|
||||
Err(e) => eprintln!("ERROR: {}", e),
|
||||
Ok(_) => {}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ pub enum TopLevelError {
|
||||
NoSolutionFound,
|
||||
UnknownError,
|
||||
PassportParseError(PassportParseError),
|
||||
SeatParseError(SeatParseError),
|
||||
}
|
||||
|
||||
impl fmt::Display for TopLevelError {
|
||||
@@ -28,6 +29,7 @@ impl fmt::Display for TopLevelError {
|
||||
TopLevelError::NoInputFound => write!(f, "No valid inputs found"),
|
||||
TopLevelError::NoSolutionFound => write!(f, "No solution found."),
|
||||
TopLevelError::PassportParseError(p) => write!(f, "Error parsing passport: {}", p),
|
||||
TopLevelError::SeatParseError(s) => write!(f, "Error parsing seat: {}", s),
|
||||
TopLevelError::UnknownError => {
|
||||
write!(f, "Unknown error occurred; this shouldn't be possible.")
|
||||
}
|
||||
@@ -89,3 +91,48 @@ impl fmt::Display for PassportParseError {
|
||||
}
|
||||
|
||||
convert_error!(PassportParseError, TopLevelError, PassportParseError);
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum SeatParseError {
|
||||
InvalidSeatIdentifier(String),
|
||||
BadSeatRowSectionSize(usize),
|
||||
BadSeatColumnSectionSize(usize),
|
||||
UnexpectedRowCharacter(char),
|
||||
UnexpectedColumnCharacter(char),
|
||||
DidNotResolveColumn(String),
|
||||
DidNotResolveRow(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for SeatParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
SeatParseError::InvalidSeatIdentifier(s) => {
|
||||
write!(f, "Invalid seat identifier: {:?}", s)
|
||||
}
|
||||
SeatParseError::BadSeatRowSectionSize(x) => write!(
|
||||
f,
|
||||
"Bad identifiers for rows; expected {} characters, got {}",
|
||||
7, x
|
||||
),
|
||||
SeatParseError::BadSeatColumnSectionSize(x) => write!(
|
||||
f,
|
||||
"Bad identifiers for columns; expected {} characters, got {}",
|
||||
3, x
|
||||
),
|
||||
SeatParseError::UnexpectedRowCharacter(c) => {
|
||||
write!(f, "Unexpected character when parsing rows: {:?}", c)
|
||||
}
|
||||
SeatParseError::UnexpectedColumnCharacter(c) => {
|
||||
write!(f, "Unexpected character when parsing columns: {:?}", c)
|
||||
}
|
||||
SeatParseError::DidNotResolveRow(s) => {
|
||||
write!(f, "Could not resolve row with {:?}", s)
|
||||
}
|
||||
SeatParseError::DidNotResolveColumn(s) => {
|
||||
write!(f, "Could not resolve row with {:?}", s)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
convert_error!(SeatParseError, TopLevelError, SeatParseError);
|
||||
|
||||
Reference in New Issue
Block a user