parent
c911c4d72a
commit
cbe51dc6e1
@ -0,0 +1,263 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::{BinaryHeap, HashMap, HashSet};
|
||||
use std::error::Error;
|
||||
use std::str::FromStr;
|
||||
|
||||
use advent_of_code::main;
|
||||
|
||||
main!();
|
||||
|
||||
fn solve(input: &str) -> Result<String, Box<Error>> {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
struct Combat {
|
||||
map: Map,
|
||||
}
|
||||
|
||||
impl Combat {}
|
||||
|
||||
struct Map {
|
||||
walls: HashSet<Square>,
|
||||
units: HashMap<Square, Unit>,
|
||||
}
|
||||
|
||||
impl Map {
|
||||
fn is_open(&self, square: &Square) -> bool {
|
||||
!self.walls.contains(&square) && !self.units.contains_key(&square)
|
||||
}
|
||||
|
||||
fn open_neighbors(&self, square: &Square) -> HashSet<Square> {
|
||||
square
|
||||
.neighbors()
|
||||
.iter()
|
||||
.filter(|x| self.is_open(x))
|
||||
.map(|&x| x)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn distances(&self, from: &Square) -> HashMap<Square, usize> {
|
||||
#[derive(Eq, PartialEq)]
|
||||
struct State {
|
||||
distance: usize,
|
||||
square: Square,
|
||||
}
|
||||
|
||||
impl Ord for State {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
other.distance.cmp(&self.distance)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for State {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
let mut distances = HashMap::new();
|
||||
let mut queue = BinaryHeap::new();
|
||||
queue.push(State{distance: 0, square: *from});
|
||||
|
||||
while let Some(State {distance, square}) = queue.pop() {
|
||||
distances.entry(square).or_insert(distance);
|
||||
|
||||
self
|
||||
.open_neighbors(&square)
|
||||
.iter()
|
||||
.filter(|x| !distances.contains_key(x))
|
||||
.map(|&x| State {
|
||||
distance: distance + 1,
|
||||
square: x,
|
||||
})
|
||||
.for_each(|x| queue.push(x));
|
||||
}
|
||||
|
||||
distances
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Map {
|
||||
type Err = Box<Error>;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut walls = HashSet::new();
|
||||
let mut units = HashMap::new();
|
||||
s.trim()
|
||||
.lines()
|
||||
.enumerate()
|
||||
.flat_map(|(y, line)| {
|
||||
line.chars()
|
||||
.enumerate()
|
||||
.map(move |(x, c)| (Square((y, x)), c))
|
||||
})
|
||||
.for_each(|(square, c)| {
|
||||
match c {
|
||||
'#' => {
|
||||
walls.insert(square);
|
||||
}
|
||||
'G' => {
|
||||
units.insert(square, Unit::new(Race::Goblin));
|
||||
}
|
||||
'E' => {
|
||||
units.insert(square, Unit::new(Race::Elf));
|
||||
}
|
||||
'.' => {}
|
||||
_ => panic!(),
|
||||
};
|
||||
});
|
||||
|
||||
Ok(Self { walls, units })
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_distances() {
|
||||
let map: Map = r"
|
||||
#######
|
||||
#E..G.#
|
||||
#...#.#
|
||||
#.G.#G#
|
||||
#######
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
let distances = map.distances(&Square((1, 1)));
|
||||
assert!(!distances.contains_key(&Square((0, 0))));
|
||||
assert!(!distances.contains_key(&Square((1, 4))));
|
||||
assert!(!distances.contains_key(&Square((1, 5))));
|
||||
assert_eq!(distances.get(&Square((2, 2))).unwrap(), &2);
|
||||
assert_eq!(distances.get(&Square((1, 3))).unwrap(), &2);
|
||||
assert_eq!(distances.get(&Square((3, 3))).unwrap(), &4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_map_from_str() {
|
||||
let input = r"
|
||||
#######
|
||||
#.G.E.#
|
||||
#E.G.E#
|
||||
#.G.E.#
|
||||
#######
|
||||
";
|
||||
|
||||
let map: Map = input.parse().unwrap();
|
||||
|
||||
assert!(map.walls.contains(&Square((0, 0))));
|
||||
assert!(!map.walls.contains(&Square((1, 1))));
|
||||
|
||||
assert_eq!(map.units.get(&Square((1, 2))).unwrap().race, Race::Goblin);
|
||||
assert_eq!(map.units.get(&Square((1, 4))).unwrap().race, Race::Elf);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
struct Square((usize, usize));
|
||||
|
||||
impl Square {
|
||||
fn neighbors(&self) -> HashSet<Self> {
|
||||
let (y, x) = self.0;
|
||||
let mut set = HashSet::new();
|
||||
set.insert(Square((y - 1, x)));
|
||||
set.insert(Square((y + 1, x)));
|
||||
set.insert(Square((y, x - 1)));
|
||||
set.insert(Square((y, x + 1)));
|
||||
set
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Square {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.0.cmp(&other.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Square {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_square_ord() {
|
||||
let mut squares: Vec<Square> = vec![(1, 2), (1, 4), (2, 1), (2, 3), (2, 5), (3, 2), (3, 4)]
|
||||
.iter()
|
||||
.map(|&x| Square(x))
|
||||
.collect();
|
||||
squares.sort();
|
||||
assert_eq!(
|
||||
squares.iter().map(|x| x.0).collect::<Vec<_>>(),
|
||||
vec![(1, 2), (1, 4), (2, 1), (2, 3), (2, 5), (3, 2), (3, 4)]
|
||||
);
|
||||
}
|
||||
|
||||
struct Unit {
|
||||
race: Race,
|
||||
hp: usize,
|
||||
}
|
||||
|
||||
impl Unit {
|
||||
fn new(race: Race) -> Self {
|
||||
let hp = 200;
|
||||
Unit { race, hp }
|
||||
}
|
||||
|
||||
fn targets(&self, map: &Map) -> HashSet<Square> {
|
||||
map.units
|
||||
.iter()
|
||||
.filter(|(_, x)| x.race == self.race.enemy())
|
||||
.map(|(&x, _)| x)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn in_range(&self, map: &Map) -> HashSet<Square> {
|
||||
self.targets(&map)
|
||||
.iter()
|
||||
.flat_map(|x| map.open_neighbors(x))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unit_targets() {
|
||||
let map: Map = r"
|
||||
#######
|
||||
#E..G.#
|
||||
#...#.#
|
||||
#.G.#G#
|
||||
#######
|
||||
"
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
let square = Square((1, 1));
|
||||
let unit = map.units.get(&square).unwrap();
|
||||
|
||||
let targets = unit.targets(&map);
|
||||
assert_eq!(targets.len(), 3);
|
||||
assert!(vec![(1, 4), (3, 2), (3, 5)]
|
||||
.iter()
|
||||
.map(|&x| Square(x))
|
||||
.all(|x| targets.contains(&x)));
|
||||
|
||||
let in_range = unit.in_range(&map);
|
||||
assert_eq!(in_range.len(), 6);
|
||||
assert!(vec![(1, 3), (1, 5), (2, 2), (2, 5), (3, 1), (3, 3)]
|
||||
.iter()
|
||||
.map(|&x| Square(x))
|
||||
.all(|x| in_range.contains(&x)));
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
enum Race {
|
||||
Elf,
|
||||
Goblin,
|
||||
}
|
||||
|
||||
impl Race {
|
||||
fn enemy(&self) -> Race {
|
||||
match self {
|
||||
Race::Elf => Race::Goblin,
|
||||
Race::Goblin => Race::Elf,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue