day15 part1 combat with errors

This commit is contained in:
Johannes Schaefer
2018-12-18 17:46:44 +01:00
parent 2bf8edf315
commit ac3a741f0d
2 changed files with 75 additions and 35 deletions

View File

@@ -1,9 +1,7 @@
######### #######
#G..G..G# #.G...#
#.......# #...EG#
#.......# #.#.#G#
#G..E..G# #..G#E#
#.......# #.....#
#.......# #######
#G..G..G#
#########

View File

@@ -4,28 +4,34 @@ use std::collections::HashSet;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::fmt::Display; use std::fmt::Display;
const ATTACK_POWER: i32 = 3;
const HEALTH: i32 = 200;
pub fn task1() { pub fn task1() {
let input = utils::read_file("input/day15.txt"); let input = utils::read_file("input/day15.txt");
let mut game = Game::from_input(input.lines().collect()); let mut game = Game::from_input(input.lines().collect());
println!("{}", game); println!("{}", game);
game.round(); let mut round = 0;
println!("{}", game); while game.round() {
game.round(); round += 1;
println!("{}", game); println!("{}", game);
game.round(); println!("{:?}", game.units);
println!("{}", game); }
game.round(); println!("Final full round was {}", round);
println!("{}", game); println!(
"Remaining HP: {}",
game.units.iter().map(|it| it.health).sum::<i32>()
);
println!("{:?}", game.units);
} }
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
enum Tile { enum Tile {
Empty, Empty,
Wall, Wall,
Unit(WarriorType),
} }
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq, Debug)]
enum WarriorType { enum WarriorType {
Elve, Elve,
Goblin, Goblin,
@@ -79,6 +85,7 @@ impl Ord for Position {
} }
} }
#[derive(Debug)]
struct Warrior { struct Warrior {
warrior_type: WarriorType, warrior_type: WarriorType,
position: Position, position: Position,
@@ -129,7 +136,6 @@ impl Game {
.filter_map(|(delta, start)| { .filter_map(|(delta, start)| {
let map = Map::from_game(&self, *start); let map = Map::from_game(&self, *start);
if let Some(path) = map.shortest_path(*start, to) { if let Some(path) = map.shortest_path(*start, to) {
//println!("Path from {:?}: {:?}", start, path);
Some((delta + path.len() * 10, *path.first().unwrap_or(start))) Some((delta + path.len() * 10, *path.first().unwrap_or(start)))
} else { } else {
None 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); 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( if let Some(next_target_position) = self.closest_fighting_position(
self.units[curr].position, self.units[curr].position,
self.units[curr].warrior_type.enemy_type(), self.units[curr].warrior_type.enemy_type(),
@@ -154,20 +173,46 @@ impl Game {
if let Some(next_position) = if let Some(next_position) =
self.next_position(self.units[curr].position, next_target_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; let curr_pos = self.units[curr].position;
self.tiles[curr_pos.0][curr_pos.1] = Tile::Empty; self.tiles[curr_pos.0][curr_pos.1] = Tile::Empty;
self.units[curr].position = next_position; self.units[curr].position = next_position;
self.tiles[next_position.0][next_position.1] =
Tile::Unit(self.units[curr].warrior_type);
} else { } else {
panic!("We have a reachable target but no path to it!"); 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<usize> = 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 { fn from_input(input: Vec<&str>) -> Self {
@@ -190,17 +235,17 @@ impl Game {
units.push(Warrior { units.push(Warrior {
warrior_type: Elve, warrior_type: Elve,
position: Position(x, y), position: Position(x, y),
health: 300, health: HEALTH,
}); });
Unit(Elve) Empty
} }
'G' => { 'G' => {
units.push(Warrior { units.push(Warrior {
warrior_type: Goblin, warrior_type: Goblin,
position: Position(x, y), position: Position(x, y),
health: 300, health: HEALTH,
}); });
Unit(Goblin) Empty
} }
c => panic!("Unexpected input character '{}'", c), c => panic!("Unexpected input character '{}'", c),
} }
@@ -231,8 +276,6 @@ impl Display for Game {
line.push(match &self.tiles[x][y] { line.push(match &self.tiles[x][y] {
Empty => '.', Empty => '.',
Wall => '#', Wall => '#',
Unit(WarriorType::Elve) => 'E',
Unit(WarriorType::Goblin) => 'G',
}); });
} }
} }
@@ -335,7 +378,6 @@ impl Map {
} }
fn find_closest_target(&self, from: Position) -> Option<Position> { fn find_closest_target(&self, from: Position) -> Option<Position> {
// println!("{}", self);
let mut open: VecDeque<(usize, Position)> = VecDeque::new(); let mut open: VecDeque<(usize, Position)> = VecDeque::new();
open.push_back((0, from)); open.push_back((0, from));
let mut visited: HashSet<Position> = HashSet::new(); let mut visited: HashSet<Position> = HashSet::new();