From ac3a741f0dfe380e7ff127f5b2c1529795f3396e Mon Sep 17 00:00:00 2001 From: Johannes Schaefer Date: Tue, 18 Dec 2018 17:46:44 +0100 Subject: [PATCH] day15 part1 combat with errors --- input/day15.txt | 16 ++++---- src/tasks/day15.rs | 94 +++++++++++++++++++++++++++++++++------------- 2 files changed, 75 insertions(+), 35 deletions(-) diff --git a/input/day15.txt b/input/day15.txt index 5d6aaaa..63d31fe 100644 --- a/input/day15.txt +++ b/input/day15.txt @@ -1,9 +1,7 @@ -######### -#G..G..G# -#.......# -#.......# -#G..E..G# -#.......# -#.......# -#G..G..G# -######### \ No newline at end of file +####### +#.G...# +#...EG# +#.#.#G# +#..G#E# +#.....# +####### \ No newline at end of file diff --git a/src/tasks/day15.rs b/src/tasks/day15.rs index 504d9ef..d62ada2 100644 --- a/src/tasks/day15.rs +++ b/src/tasks/day15.rs @@ -4,28 +4,34 @@ use std::collections::HashSet; use std::collections::VecDeque; use std::fmt::Display; +const ATTACK_POWER: i32 = 3; +const HEALTH: i32 = 200; + pub fn task1() { let input = utils::read_file("input/day15.txt"); let mut game = Game::from_input(input.lines().collect()); println!("{}", game); - game.round(); - println!("{}", game); - game.round(); - println!("{}", game); - game.round(); - println!("{}", game); - game.round(); - println!("{}", game); + let mut round = 0; + while game.round() { + round += 1; + println!("{}", game); + println!("{:?}", game.units); + } + println!("Final full round was {}", round); + println!( + "Remaining HP: {}", + game.units.iter().map(|it| it.health).sum::() + ); + println!("{:?}", game.units); } #[derive(Clone, PartialEq)] enum Tile { Empty, Wall, - Unit(WarriorType), } -#[derive(Clone, Copy, PartialEq)] +#[derive(Clone, Copy, PartialEq, Debug)] enum WarriorType { Elve, Goblin, @@ -79,6 +85,7 @@ impl Ord for Position { } } +#[derive(Debug)] struct Warrior { warrior_type: WarriorType, position: Position, @@ -129,7 +136,6 @@ impl Game { .filter_map(|(delta, start)| { let map = Map::from_game(&self, *start); if let Some(path) = map.shortest_path(*start, to) { - //println!("Path from {:?}: {:?}", start, path); Some((delta + path.len() * 10, *path.first().unwrap_or(start))) } else { None @@ -143,10 +149,23 @@ impl Game { } } - fn round(&mut self) { + /// Returns true if a full round was played, false if the round aborted because all + /// enemies of one party are dead + fn round(&mut self) -> bool { self.units.sort_unstable_by_key(|it| it.position); - for curr in 0..self.units.len() { + let mut curr = 0; + while curr < self.units.len() { + if !self + .units + .iter() + .any(|warrior| warrior.warrior_type == self.units[curr].warrior_type.enemy_type()) + { + println!("There are no enemies anymore!"); + return false; + } + + // movement phase if let Some(next_target_position) = self.closest_fighting_position( self.units[curr].position, self.units[curr].warrior_type.enemy_type(), @@ -154,20 +173,46 @@ impl Game { if let Some(next_position) = self.next_position(self.units[curr].position, next_target_position) { - // println!( - // "{:?} -> {:?} ({:?})", - // self.units[curr].position, next_target_position, next_position - // ); let curr_pos = self.units[curr].position; self.tiles[curr_pos.0][curr_pos.1] = Tile::Empty; self.units[curr].position = next_position; - self.tiles[next_position.0][next_position.1] = - Tile::Unit(self.units[curr].warrior_type); } else { panic!("We have a reachable target but no path to it!"); } } + + // attack phase + let neighbors = self.units[curr].position.neighbors(self.width, self.height); + let mut close_enemies: Vec = self + .units + .iter() + .enumerate() + .filter(|(_, it)| neighbors.contains(&it.position)) + .map(|(i, _)| i) + .collect(); + close_enemies.sort_unstable_by(|a, b| { + let a = &self.units[*a]; + let b = &self.units[*b]; + if a.health == b.health { + a.position.cmp(&b.position) + } else { + a.health.cmp(&b.health) + } + }); + if let Some(closest_index) = close_enemies.first() { + let enemy = &mut self.units[*closest_index]; + enemy.health -= ATTACK_POWER; + if enemy.health <= 0 { + let enemy = self.units.remove(*closest_index); + if *closest_index < curr { + curr -= 1; + } + self.tiles[enemy.position.0][enemy.position.1] = Tile::Empty; + } + } + curr += 1; } + return true; } fn from_input(input: Vec<&str>) -> Self { @@ -190,17 +235,17 @@ impl Game { units.push(Warrior { warrior_type: Elve, position: Position(x, y), - health: 300, + health: HEALTH, }); - Unit(Elve) + Empty } 'G' => { units.push(Warrior { warrior_type: Goblin, position: Position(x, y), - health: 300, + health: HEALTH, }); - Unit(Goblin) + Empty } c => panic!("Unexpected input character '{}'", c), } @@ -231,8 +276,6 @@ impl Display for Game { line.push(match &self.tiles[x][y] { Empty => '.', Wall => '#', - Unit(WarriorType::Elve) => 'E', - Unit(WarriorType::Goblin) => 'G', }); } } @@ -335,7 +378,6 @@ impl Map { } fn find_closest_target(&self, from: Position) -> Option { - // println!("{}", self); let mut open: VecDeque<(usize, Position)> = VecDeque::new(); open.push_back((0, from)); let mut visited: HashSet = HashSet::new();