Compare commits

...

2 Commits

Author SHA1 Message Date
Johannes
ba622fd408 day15 part 2 optimized
instead of senselessly trying to find the best starting point, just rely
on the order of neighbor point generation. Since we use a queue
to manage open points and he neighbors are added in the correct
order, they neighbor that has precedence due to reading order will
automatically be the one to be predecessor on a final path, if he is on
a shortest path.
2018-12-18 22:43:10 +01:00
Johannes
cef96d55be day15 part 2 2018-12-18 22:20:03 +01:00
2 changed files with 81 additions and 25 deletions

View File

@@ -1,4 +1,4 @@
fn main() { fn main() {
aoc_2018::tasks::day15::task1(); // aoc_2018::tasks::day15::task1();
// aoc_2018::tasks::day15::task2(); aoc_2018::tasks::day15::task2();
} }

View File

@@ -4,16 +4,17 @@ 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; 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(), 3, 3);
println!("{}", game); println!("{}", game);
let mut round = 0; let mut round = 0;
while game.round() { while game.round() {
round += 1; round += 1;
// println!("{}", game);
// println!("round was {}", round);
} }
println!("Final full round was {}", round); println!("Final full round was {}", round);
println!( println!(
@@ -22,6 +23,45 @@ pub fn task1() {
); );
} }
pub fn task2() {
let input = utils::read_file("input/day15.txt");
let input = input.lines().collect();
let mut highest_fail = 3;
let mut lowest_win = None::<i32>;
while lowest_win.is_none() || lowest_win.unwrap() - 1 > highest_fail {
let attack = match lowest_win {
Some(upper) => highest_fail + (upper - highest_fail) / 2,
None => 2 * highest_fail,
};
let mut game = Game::from_input(&input, 3, attack);
let initial_elve_count = game
.units
.iter()
.filter(|unit| unit.warrior_type == WarriorType::Elve)
.count();
let mut round = 0;
while game.round() {
round += 1;
}
if game
.units
.iter()
.filter(|unit| unit.warrior_type == WarriorType::Elve)
.count()
== initial_elve_count
{
lowest_win = Some(attack);
} else {
highest_fail = attack;
}
println!(
"Result: {}",
game.units.iter().map(|it| it.health).sum::<i32>() * round as i32
);
}
println!("Searching stopped with lowest win {:?}", lowest_win); // 7169 too low
}
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
enum Tile { enum Tile {
Empty, Empty,
@@ -101,6 +141,7 @@ struct Warrior {
warrior_type: WarriorType, warrior_type: WarriorType,
position: Position, position: Position,
health: i32, health: i32,
attack: i32,
} }
struct Game { struct Game {
@@ -136,25 +177,32 @@ impl Game {
if from == to { if from == to {
return Some(from); return Some(from);
} }
let input = vec![ // let input = vec![
(1, Position(from.0, from.1 - 1)), // (1, Position(from.0, from.1 - 1)),
(2, Position(from.0 - 1, from.1)), // (2, Position(from.0 - 1, from.1)),
(3, Position(from.0 + 1, from.1)), // (3, Position(from.0 + 1, from.1)),
(4, Position(from.0, from.1 + 1)), // (4, Position(from.0, from.1 + 1)),
]; // ];
if let Some((_, best)) = input // if let Some((_, best)) = input
.iter() // .iter()
.filter_map(|(delta, start)| { // .filter_map(|(delta, start)| {
let map = Map::from_game(&self, from); // let map = Map::from_game(&self, from);
if let Some(path) = map.shortest_path(*start, to) { // if let Some(path) = map.shortest_path(*start, to) {
Some((delta + path.len() * 10, *path.first().unwrap_or(start))) // Some((delta + path.len() * 10, *path.first().unwrap_or(start)))
} else { // } else {
None // None
} // }
}) // })
.min_by_key(|(d, _)| *d) // .min_by_key(|(d, _)| *d)
{ // {
Some(best) // Some(best)
// } else {
// None
// }
let map = Map::from_game(&self, from);
if let Some(path) = map.shortest_path(from, to) {
// println!("{:?}", path);
Some(*path.get(1).unwrap_or(&from))
} else { } else {
None None
} }
@@ -185,6 +233,10 @@ 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!(
// "{:?} moves to {:?} via {:?}",
// self.units[curr].position, next_target_position, next_position
// );
self.units[curr].position = next_position; self.units[curr].position = next_position;
} else { } else {
panic!("We have a reachable target but no path to it! {:?} wants to go to {:?}", self.units[curr], next_target_position); panic!("We have a reachable target but no path to it! {:?} wants to go to {:?}", self.units[curr], next_target_position);
@@ -219,8 +271,9 @@ impl Game {
{ {
panic!("Distance WTF") panic!("Distance WTF")
} }
let attack = self.units[curr].attack;
let enemy = &mut self.units[*closest_index]; let enemy = &mut self.units[*closest_index];
enemy.health -= ATTACK_POWER; enemy.health -= attack;
if enemy.health <= 0 { if enemy.health <= 0 {
let enemy = self.units.remove(*closest_index); let enemy = self.units.remove(*closest_index);
if *closest_index < curr { if *closest_index < curr {
@@ -234,7 +287,7 @@ impl Game {
return true; return true;
} }
fn from_input(input: Vec<&str>) -> Self { fn from_input(input: &Vec<&str>, goblin_attack: i32, elve_attack: i32) -> Self {
use self::Tile::*; use self::Tile::*;
use self::WarriorType::*; use self::WarriorType::*;
let width = input[0].len(); let width = input[0].len();
@@ -255,6 +308,7 @@ impl Game {
warrior_type: Elve, warrior_type: Elve,
position: Position(x, y), position: Position(x, y),
health: HEALTH, health: HEALTH,
attack: elve_attack,
}); });
Empty Empty
} }
@@ -263,6 +317,7 @@ impl Game {
warrior_type: Goblin, warrior_type: Goblin,
position: Position(x, y), position: Position(x, y),
health: HEALTH, health: HEALTH,
attack: goblin_attack,
}); });
Empty Empty
} }
@@ -391,6 +446,7 @@ impl Map {
if let Some(Some(_)) = predecessors.get(&to) { if let Some(Some(_)) = predecessors.get(&to) {
let mut result: Vec<Position> = Vec::new(); let mut result: Vec<Position> = Vec::new();
let mut current = to; let mut current = to;
result.push(current);
while let Some(Some(predecessor)) = predecessors.get(&current) { while let Some(Some(predecessor)) = predecessors.get(&current) {
result.push(*predecessor); result.push(*predecessor);
current = *predecessor; current = *predecessor;