166 lines
4.0 KiB
Rust
166 lines
4.0 KiB
Rust
#[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<char>
|
|
}
|
|
|
|
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::<String>()
|
|
}
|
|
|
|
pub fn sector_id(&self) -> u32 {
|
|
self.sector_id
|
|
}
|
|
|
|
pub fn checksum(&self) -> &Vec<char> {
|
|
&self.checksum
|
|
}
|
|
|
|
pub fn calculate_checksum(&self) -> Vec<char> {
|
|
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::<Vec<_>>()
|
|
}
|
|
|
|
pub fn verify(&self) -> bool {
|
|
*self.checksum() == self.calculate_checksum()
|
|
}
|
|
}
|
|
|
|
impl FromStr for Room {
|
|
type Err = ();
|
|
|
|
fn from_str(s: &str) -> Result<Room, ()> {
|
|
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::<String>().unwrap();
|
|
let sector_id = caps.at(2).unwrap().parse::<u32>().unwrap();
|
|
let checksum = caps.at(3).unwrap().chars().collect::<Vec<_>>();
|
|
|
|
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::<Vec<_>>();
|
|
|
|
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());
|
|
}
|
|
}
|