day15 part1 combat with errors
This commit is contained in:
@@ -1,9 +1,7 @@
|
|||||||
#########
|
#######
|
||||||
#G..G..G#
|
#.G...#
|
||||||
#.......#
|
#...EG#
|
||||||
#.......#
|
#.#.#G#
|
||||||
#G..E..G#
|
#..G#E#
|
||||||
#.......#
|
#.....#
|
||||||
#.......#
|
#######
|
||||||
#G..G..G#
|
|
||||||
#########
|
|
||||||
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user