Last active
December 5, 2016 02:55
-
-
Save mmstick/0277cee63754b207894b8cd2d68d4ef1 to your computer and use it in GitHub Desktop.
Advent of Code Day 4 Solution (Stdin Version)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![feature(alloc_system)] | |
extern crate alloc_system; | |
extern crate arrayvec; | |
use std::cmp::Ordering::{Less, Greater}; | |
use std::convert::From; | |
use std::io::{BufRead, Lines, StdinLock}; | |
// Used to eliminate dynamic heap allocations by allocating a fixed-sized vector on the stack. | |
use arrayvec::ArrayVec; | |
/// Contains the character as a `key` and it's frequency as the `value` | |
struct Frequency { key: u8, value: u8 } | |
/// A map of character frequencies | |
struct FrequencyMap { data: ArrayVec<[Frequency; 26]> } | |
impl FrequencyMap { | |
/// Increment the given key | |
fn increment_key(&mut self, key: u8) { | |
for element in &mut self.data { | |
if element.key == key { | |
element.value += 1; | |
return | |
} | |
} | |
} | |
/// Sort the frequency map by the greater number of occurrences first, and alphabetical order second. | |
fn sort(&mut self) { | |
self.data.sort_by(|a, b| { | |
if a.value > b.value { Less } else if a.value < b.value || a.key > b.key { Greater } else { Less } | |
}) | |
} | |
/// Collect the first five characters in the sorted frequency map as the checksum of the map. | |
fn collect_checksum(&mut self) -> ArrayVec<[u8; 5]> { | |
self.sort(); | |
self.data.iter().take(5).map(|x| x.key).collect::<ArrayVec<[u8; 5]>>() | |
} | |
} | |
impl<'a> From<&'a str> for FrequencyMap { | |
fn from(name: &'a str) -> FrequencyMap { | |
let mut freqmap = FrequencyMap { | |
// Look Ma, no heap allocation. 26 characters in the alphabet; 52 bytes of memory allocated on the stack. | |
data: (b'a'..b'z' + 1).map(|c| Frequency { key: c, value: 0 }).collect::<ArrayVec<[_; 26]>>() | |
}; | |
for character in name.bytes().filter(|&x| x != b'-') { freqmap.increment_key(character); } | |
freqmap | |
} | |
} | |
/// Take a character as a byte and wrap add the character by the alphabet. 'a' ... 'z' -> 'a' ... 'z' -> ... | |
fn wrap_to_char(character: u8, by: u32) -> char { | |
((character as u8 - b'a' + (by % 26) as u8) % 26 + b'a') as char | |
} | |
/// Iterates through a list of encrypted rooms and returns their decrypted names and associated room numbers. | |
struct RoomIterator<'a> { | |
lines: Lines<StdinLock<'a>> | |
} | |
impl<'a> RoomIterator<'a> { | |
fn new(stdin: Lines<StdinLock<'a>>) -> RoomIterator<'a> { | |
RoomIterator { lines: stdin } | |
} | |
} | |
impl<'a> Iterator for RoomIterator<'a> { | |
type Item = (String, u32); | |
fn next(&mut self) -> Option<(String, u32)> { | |
loop { | |
if let Some(Ok(line)) = self.lines.next() { | |
let (prefix, checksum) = line.split_at(line.find('[').unwrap()); | |
let (name, sector_id) = prefix.split_at(line.find(|x: char| x.is_numeric()).unwrap()); | |
let expected_checksum = checksum[1..checksum.len()-1].as_bytes(); | |
if &FrequencyMap::from(name).collect_checksum() == expected_checksum { | |
let sector_id = sector_id.parse::<u32>().unwrap(); | |
return Some((name.bytes().map(|x| { | |
if x == b'-' { ' ' } else { wrap_to_char(x, sector_id) } | |
}).collect::<String>(), sector_id)); | |
} else { | |
continue | |
} | |
} else { | |
return None; | |
} | |
} | |
} | |
} | |
fn main() { | |
let stdin = std::io::stdin(); | |
let room = RoomIterator::new(stdin.lock().lines()).find(|x| x.0.contains("north")).unwrap(); | |
println!("The north pole objects are stored in room {}", room.1) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment