#[macro_use] extern crate lazy_static; extern crate regex; use std::collections::HashMap; use std::str::FromStr; use std::io::{self, BufRead}; use regex::Regex; #[derive(Debug, PartialEq)] pub struct Room { enc_name: String, sector_id: u32, checksum: Vec } impl Room { pub fn encoded_name(&self) -> &str { self.enc_name.as_str() } pub fn decoded_name(&self) -> String { self.enc_name.chars() .map(|x| { if x == '-' { ' ' } else { ((((x as u32 - 97 + self.sector_id()) % 26) + 97) as u8) as char } }) .collect::() } pub fn sector_id(&self) -> u32 { self.sector_id } pub fn checksum(&self) -> &Vec { &self.checksum } pub fn calculate_checksum(&self) -> Vec { let map = self.enc_name.chars() .filter(|x| *x != '-') .fold(HashMap::new(), |mut map, x| { *map.entry(x).or_insert(0) += 1; map }); let mut vec: Vec<_> = map.iter().collect(); vec.sort_by(|a, b| if a.1 != b.1 { b.1.cmp(a.1) } else { a.0.cmp(b.0) }); vec.iter().take(5).map(|x| *x.0).collect::>() } pub fn verify(&self) -> bool { *self.checksum() == self.calculate_checksum() } } impl FromStr for Room { type Err = (); fn from_str(s: &str) -> Result { lazy_static! { static ref RE: Regex = Regex::new(r"^(.*)-(\d+)\[([a-z]+)\]$").unwrap(); } if let Some(caps) = RE.captures(s) { let enc_name = caps.at(1).unwrap().parse::().unwrap(); let sector_id = caps.at(2).unwrap().parse::().unwrap(); let checksum = caps.at(3).unwrap().chars().collect::>(); return Ok(Room { enc_name: enc_name, sector_id: sector_id, checksum: checksum }); } Err(()) } } fn main() { let stdin = io::stdin(); let input = stdin.lock().lines() .filter_map(|x| x.ok()) .filter_map(|x| Room::from_str(&x).ok()) .filter(|x| x.verify()) .collect::>(); let count: i64 = input.iter() .map(|x| x.sector_id() as i64) .sum(); for room in input { println!("{}: {}", room.sector_id(), room.decoded_name()); } println!("count={}", count); } #[cfg(test)] mod tests { use super::*; use std::str::FromStr; #[test] fn example_01() { let room = Room::from_str("aaaaa-bbb-z-y-x-123[abxyz]").unwrap(); assert_eq!("aaaaa-bbb-z-y-x", room.encoded_name()); assert_eq!(123, room.sector_id()); assert_eq!(&vec!['a', 'b', 'x', 'y', 'z'], room.checksum()); assert_eq!(true, room.verify()); } #[test] fn example_02() { let room = Room::from_str("a-b-c-d-e-f-g-h-987[abcde]").unwrap(); assert_eq!("a-b-c-d-e-f-g-h", room.encoded_name()); assert_eq!(987, room.sector_id()); assert_eq!(&vec!['a', 'b', 'c', 'd', 'e'], room.checksum()); assert_eq!(true, room.verify()); } #[test] fn example_03() { let room = Room::from_str("not-a-real-room-404[oarel]").unwrap(); assert_eq!("not-a-real-room", room.encoded_name()); assert_eq!(404, room.sector_id()); assert_eq!(&vec!['o', 'a', 'r', 'e', 'l'], room.checksum()); assert_eq!(true, room.verify()); } #[test] fn example_04() { let room = Room::from_str("totally-real-room-200[decoy]").unwrap(); assert_eq!("totally-real-room", room.encoded_name()); assert_eq!(200, room.sector_id()); assert_eq!(&vec!['d', 'e', 'c', 'o', 'y'], room.checksum()); assert_eq!(false, room.verify()); } #[test] fn example_05() { let room = Room::from_str("qzmt-zixmtkozy-ivhz-343[decoy]").unwrap(); assert_eq!(String::from("very encrypted name"), room.decoded_name()); } }