day24 p1 wrong (example ok)

This commit is contained in:
Johannes
2018-12-29 16:43:39 +01:00
parent 097cfac516
commit c162fcb6d9
2 changed files with 215 additions and 26 deletions

View File

@@ -1,42 +1,181 @@
use crate::utils;
use std::cmp::Reverse;
use std::collections::HashMap;
pub fn task1() {
let input = "Immune System:
17 units each with 5390 hit points (weak to radiation, bludgeoning) with an attack that does 4507 fire damage at initiative 2
989 units each with 1274 hit points (immune to fire; weak to bludgeoning, slashing) with an attack that does 25 slashing damage at initiative 3
17 units each with 5390 hit points (weak to radiation, bludgeoning) with an attack that does 4507 fire damage at initiative 2
989 units each with 1274 hit points (immune to fire; weak to bludgeoning, slashing) with an attack that does 25 slashing damage at initiative 3
Infection:
801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1
4485 units each with 2961 hit points (immune to radiation; weak to fire, cold) with an attack that does 12 slashing damage at initiative 4";
let mut groups_immune: Vec<Group> = Vec::new();
let mut groups_infection: Vec<Group> = Vec::new();
801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1
4485 units each with 2961 hit points (immune to radiation; weak to fire, cold) with an attack that does 12 slashing damage at initiative 4";
let input = utils::read_file("input/day24.txt");
let mut game = Game::create(&input);
let mut to_immune = false;
for line in input.lines() {
match line {
"Immune System:" => {
to_immune = true;
}
"Infection:" => {
to_immune = false;
}
"" => (),
group => {
if let Some(group) = Group::from_str(group) {
if to_immune {
groups_immune.push(group);
} else {
groups_infection.push(group);
println!(
"Immune start units: {}",
game.groups
.iter()
.filter(|(_, it)| it.team == Team::ImmuneSystem)
.map(|(_, it)| it.units)
.sum::<i64>()
);
println!(
"Infection start units: {}",
game.groups
.iter()
.filter(|(_, it)| it.team == Team::Infection)
.map(|(_, it)| it.units)
.sum::<i64>()
);
let mut rounds_played = 0;
while !game.is_over() {
game.round();
rounds_played += 1;
}
println!("{:#?}", game);
println!("Played {} rounds", rounds_played);
println!(
"Standing units after the game: {}",
game.groups.iter().map(|(_, it)| it.units).sum::<i64>()
);
// 21107 too high
// 21004 too high
}
#[derive(Debug)]
struct Game {
groups: HashMap<usize, Group>,
}
impl Game {
fn create(input: &str) -> Self {
let mut groups = HashMap::new();
let mut team = Team::ImmuneSystem;
for (id, line) in input.lines().enumerate() {
match line {
"Immune System:" => {
team = Team::ImmuneSystem;
}
"Infection:" => {
team = Team::Infection;
}
"" => (),
group => {
if let Some(group) = Group::from_str(group, team, id) {
groups.insert(id, group);
}
}
}
}
Game { groups }
}
println!("Immune System:\n{:?}", groups_immune);
println!("Infection:\n{:?}", groups_infection);
fn round(&mut self) {
let mut target: HashMap<usize, usize> = HashMap::new();
// lock targets ordered by effective power
let mut all_by_power: Vec<&Group> = self.groups.iter().map(|(_, it)| it).collect();
all_by_power.sort_unstable_by_key(|a| Reverse((a.effective_power(), a.initiative)));
// for group in all_by_power.iter() {
// println!(
// "{}: {} ({})",
// group.id,
// group.effective_power(),
// group.initiative
// );
// }
// println!("{:?}", all_by_power);
for group in all_by_power.iter() {
if let Some(t) = self
.groups
.iter()
.map(|(_, it)| it)
.filter(|it| it.team != group.team)
.filter(|it| !target.values().any(|t| *t == it.id))
// .filter(|it| group.compute_attack_damage_to(&it) >= it.hp_each)
// .inspect(|it| {
// println!(
// "{} would deal {} damage to {}",
// group.id,
// group.compute_attack_damage_to(it),
// it.id
// )
// })
.max_by_key(|it| {
(
group.compute_attack_damage_to(&it),
it.effective_power(),
it.initiative,
)
})
{
if group.compute_attack_damage_to(&t) <= 0 {
println!(
"Didn't find a target where {:?} can deal positive damage.",
group
);
continue;
} else {
target.insert(group.id, t.id);
}
}
}
// attack ordered by initiative
let mut all_ids_by_initiative: Vec<usize> =
self.groups.iter().map(|(_, it)| it.id).collect();
all_ids_by_initiative.sort_unstable_by_key(|id| Reverse(self.groups[id].initiative));
for active_id in all_ids_by_initiative {
if !self.groups[&active_id].alive() {
// was killed in this round
println!("Group {} already dead", active_id);
continue;
}
if let Some(enemy_id) = target.get(&active_id) {
let enemy = &self.groups[enemy_id];
let damage: i64 = self.groups[&active_id].compute_attack_damage_to(&enemy);
let dying_units = damage / enemy.hp_each;
if let Some(enemy) = self.groups.get_mut(enemy_id) {
enemy.units -= dying_units;
}
// println!(
// "{} dealt {} ({} units) damage to {}",
// active_id, damage, dying_units, enemy_id
// );
}
}
// clean up dead groups
self.groups.retain(|_, it| it.alive());
// println!("{:?}", self.groups);
}
fn is_over(&self) -> bool {
self.groups.len() == 0
|| self.groups.iter().all(|(_, it)| it.team == Team::Infection)
|| self
.groups
.iter()
.all(|(_, it)| it.team == Team::ImmuneSystem)
}
}
#[derive(Debug)]
#[derive(Debug, Copy, Clone, PartialEq)]
enum Team {
Infection,
ImmuneSystem,
}
#[derive(Debug, Clone)]
struct Group {
id: usize,
team: Team,
units: i64,
hp_each: i64,
weaknesses: Vec<String>,
@@ -47,7 +186,7 @@ struct Group {
}
impl Group {
fn from_str(input: &str) -> Option<Self> {
fn from_str(input: &str, team: Team, id: usize) -> Option<Self> {
// 801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1
use regex::Regex;
let regex = Regex::new(r"(\d+) units each with (\d+) hit points \((.+)\) with an attack that does (\d+) (\w+) damage at initiative (\d+)").unwrap();
@@ -68,6 +207,8 @@ impl Group {
}
let group = Group {
id,
team,
units,
hp_each,
weaknesses,
@@ -81,4 +222,29 @@ impl Group {
None
}
}
fn alive(&self) -> bool {
self.units > 0
}
fn effective_power(&self) -> i64 {
if !self.alive() {
panic!("I have no power, im dead!")
}
self.units * self.attack_damage
}
fn compute_attack_damage_to(&self, other: &Self) -> i64 {
if self.alive() {
if other.weaknesses.contains(&self.attack_type) {
self.effective_power() * 2
} else if other.immunities.contains(&self.attack_type) {
0
} else {
self.effective_power()
}
} else {
panic!("I'm not alive, I cannot attack anyone")
}
}
}