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