Skip to content

Instantly share code, notes, and snippets.

@mmstick
Last active December 5, 2016 02:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mmstick/0277cee63754b207894b8cd2d68d4ef1 to your computer and use it in GitHub Desktop.
Save mmstick/0277cee63754b207894b8cd2d68d4ef1 to your computer and use it in GitHub Desktop.
Advent of Code Day 4 Solution (Stdin Version)
#![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