use std::fmt; use std::fmt::Formatter; pub const SIZE: usize = 16384; pub const HOUSE_RANGE: usize = 500; pub struct City { prices: Vec, buyable_house_count: usize } impl City { pub fn read_from_file(filename: &str) -> Self { let values = std::fs::read(filename).unwrap(); let mut prices: Vec = Vec::new(); for y in 0..SIZE { for x in 0..SIZE { let price = (values[(y * SIZE + x) * 2] as u16) | ((values[(y * SIZE + x) * 2 + 1] as u16) << 8); prices.push(price); } } City::new(prices) } pub fn new(prices: Vec) -> Self { let mut buyable_house_count = 0; for &price in &prices { if price > 0 { buyable_house_count += 1; } } City { prices, buyable_house_count } } pub fn get_price(&self, house: House) -> u16 { self.prices[house.y * SIZE + house.x] } pub fn get_price_xy(&self, x: usize, y: usize) -> u16 { self.prices[y * SIZE + x] } pub fn is_house(&self, house: House) -> bool { self.get_price(house) > 0 } pub fn is_house_xy(&self, x: usize, y: usize) -> bool { self.get_price_xy(x, y) > 0 } pub fn get_house_count(&self) -> usize { self.buyable_house_count } } #[derive(Eq, PartialEq, Hash, Copy, Clone)] pub struct House { pub x: usize, pub y: usize, } impl House { pub fn new(x: usize, y: usize) -> Self { House { x, y } } pub fn range_rectangle(&self) -> Rectangle { let top = if self.y <= HOUSE_RANGE { 0 } else { self.y - HOUSE_RANGE }; let bottom = if self.y >= SIZE - 1 - HOUSE_RANGE { SIZE - 1 } else { self.y + HOUSE_RANGE }; let left = if self.x <= HOUSE_RANGE { 0 } else { self.x - HOUSE_RANGE }; let right = if self.x >= SIZE - 1 - HOUSE_RANGE { SIZE - 1 } else { self.x + HOUSE_RANGE }; Rectangle {top, bottom, left, right} } } /// Rectangle - a 2D range with inclusive bounds #[derive(Clone, Copy)] pub struct Rectangle { /// The smaller x coordinate. pub left: usize, /// The bigger x coordinate. pub right: usize, /// The smaller y coordinate. pub top: usize, /// The bigger y coordinate. pub bottom: usize, } impl fmt::Display for Rectangle { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!(f, "L{}-{}R T{}-{}B", self.left, self.right, self.top, self.bottom) } } impl Rectangle { pub fn is_inside(&self, x: usize, y: usize) -> bool { self.left <= x && x <= self.right && self.top <= y && y <= self.bottom } pub fn width(&self) -> usize { self.right - self.left } pub fn height(&self) -> usize { self.bottom - self.top } } pub struct HouseLayout<'a> { pub city: &'a City, reachable: Vec, houses: Vec, reachable_houses: usize } impl<'a> HouseLayout<'a> { pub fn new(city: &'a City) -> Self { HouseLayout { city, reachable: vec![0; SIZE * SIZE], houses: Vec::new(), reachable_houses: 0 } } pub fn cover_count(&self, house: House) -> u16 { self.reachable[house.y * SIZE + house.x] } pub fn cover_count_xy(&self, x: usize, y: usize) -> u16 { self.reachable[y * SIZE + x] } pub fn is_covered(&self, house: House) -> bool { self.cover_count(house) > 0 } pub fn add_house(&mut self, house: House) -> usize { let range_rect = house.range_rectangle(); for y in range_rect.top..=range_rect.bottom { for x in range_rect.left..=range_rect.right { let index = y as usize * SIZE + x as usize; if self.reachable[index] == 0 && self.city.is_house_xy(x as usize, y as usize) { self.reachable_houses += 1; } self.reachable[index] += 1; } } self.houses.push(house); self.houses.len() - 1 } pub fn remove_house(&mut self, index: usize) { let house = self.houses.swap_remove(index); let range_rect = house.range_rectangle(); for y in range_rect.top..=range_rect.bottom { for x in range_rect.left..=range_rect.right { let index = y as usize * SIZE + x as usize; self.reachable[index] -= 1; if self.reachable[index] == 0 && self.city.is_house_xy(x as usize, y as usize) { self.reachable_houses -= 1; } } } } pub fn is_valid(&self) -> bool { self.reachable_houses == self.city.get_house_count() } pub fn price(&self) -> u32 { get_price(self.city, &self.houses) } pub fn houses(&self) -> &Vec { &self.houses } } pub fn get_price(city: &City, houses: &Vec) -> u32 { let mut price = 0u32; for &house in houses { price += city.get_price(house) as u32; } price } pub fn is_valid(city: &City, houses: &Vec) -> Option { let mut reachable = vec![false; SIZE * SIZE]; let mut price = 0u32; for house in houses { assert!(city.prices[house.y * SIZE + house.x] > 0); let range_rect = house.range_rectangle(); for y in range_rect.top..=range_rect.bottom { for x in range_rect.left..=range_rect.right { reachable[y as usize * SIZE + x as usize] = true; } } price += city.get_price(*house) as u32; } for y in 0..SIZE { for x in 0..SIZE { if !reachable[y * SIZE + x] && city.prices[y * SIZE + x] > 0 { return None; } } } Some(price) } #[cfg(test)] mod tests { use super::*; #[test] fn house_rectangle_at_min() { let house = House::new(0, 0); let rect = house.range_rectangle(); assert_eq!(rect.top, 0); assert_eq!(rect.left, 0); assert_eq!(rect.right, HOUSE_RANGE); assert_eq!(rect.bottom, HOUSE_RANGE); } #[test] fn house_rectangle_at_max() { let house = House::new(SIZE - 1, SIZE - 1); let rect = house.range_rectangle(); assert_eq!(rect.top, SIZE - 1 - HOUSE_RANGE); assert_eq!(rect.left, SIZE - 1 - HOUSE_RANGE); assert_eq!(rect.right, SIZE - 1); assert_eq!(rect.bottom, SIZE - 1); } #[test] fn house_rect_in_middle() { let house = House::new(SIZE / 2, SIZE / 2); let rect = house.range_rectangle(); assert_eq!(rect.top, house.y - HOUSE_RANGE); assert_eq!(rect.left, house.x - HOUSE_RANGE); assert_eq!(rect.right, house.x + HOUSE_RANGE); assert_eq!(rect.bottom, house.y + HOUSE_RANGE); } }