Skip to content

Commit aa820b5

Browse files
committed
2018 day 24
1 parent caf5107 commit aa820b5

File tree

12 files changed

+605
-13
lines changed

12 files changed

+605
-13
lines changed

2018/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# https://adventofcode.com/2018
22

33
[workspace]
4-
members = ["day*"]
4+
members = ["day*", "toto"]
55

66
resolver = "2"

2018/day21/day21.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! [Day 21: Chronal Conversion](https://adventofcode.com/2018/day/21)
22
3+
#![allow(clippy::cast_possible_truncation)]
4+
35
use std::collections::HashSet;
46
use std::fmt::Error;
57

2018/day24/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "day24"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
aoc = { path = "../../aoc" }
8+
regex = "1.10.2"
9+
10+
[[bin]]
11+
name = "day24"
12+
# path = "day24.rs"

2018/day24/src/army.rs

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
use std::ops::AddAssign;
2+
3+
use crate::group::Fight;
4+
use crate::group::Group;
5+
6+
#[derive(Debug, Clone)]
7+
pub struct Army {
8+
name: String,
9+
groups: Vec<Group>,
10+
}
11+
12+
impl Army {
13+
#[must_use]
14+
pub fn new() -> Self {
15+
Self {
16+
name: String::new(),
17+
groups: vec![],
18+
}
19+
}
20+
}
21+
22+
impl Default for Army {
23+
fn default() -> Self {
24+
Self::new()
25+
}
26+
}
27+
28+
impl std::str::FromStr for Army {
29+
type Err = Box<dyn std::error::Error>;
30+
31+
fn from_str(s: &str) -> Result<Self, Self::Err> {
32+
let mut a = Army::new();
33+
let mut id = 0;
34+
35+
for line in s.lines() {
36+
if let Some(line) = line.strip_suffix(':') {
37+
a.name = line.to_string();
38+
} else {
39+
let mut g: Group = line.parse()?;
40+
id.add_assign(1);
41+
g.set_id(&a.name, id);
42+
a.groups.push(g);
43+
}
44+
}
45+
46+
Ok(a)
47+
}
48+
}
49+
50+
impl Army {
51+
pub fn set_boost(&mut self, boost: u32) {
52+
for group in &mut self.groups {
53+
group.set_boost(boost);
54+
}
55+
}
56+
57+
#[must_use]
58+
pub fn is_alive(&self) -> bool {
59+
self.groups.iter().any(Group::is_alive)
60+
}
61+
62+
#[must_use]
63+
pub fn alive_units(&self) -> u32 {
64+
self.groups.iter().map(Group::alive_units).sum()
65+
}
66+
67+
#[must_use]
68+
pub fn select_fights<'a>(&'a self, other: &'a Army) -> Vec<Fight<'a>> {
69+
let mut fights: Vec<Fight<'a>> = vec![];
70+
71+
let mut attackers = self.alive_groups();
72+
let mut targets = other.alive_groups();
73+
74+
// sort by effective power then initiative
75+
attackers.sort_unstable();
76+
77+
for attacker in &attackers {
78+
if let Some(i) = attacker.select_target(&targets) {
79+
fights.push(Fight {
80+
attacker,
81+
opponent: targets[i],
82+
});
83+
84+
targets.remove(i);
85+
}
86+
}
87+
88+
fights
89+
}
90+
91+
#[must_use]
92+
pub fn alive_groups(&self) -> Vec<&Group> {
93+
self.groups.iter().filter(|g| g.is_alive()).collect()
94+
}
95+
}
96+
97+
impl std::fmt::Display for Army {
98+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99+
for g in &self.groups {
100+
writeln!(f, "{g}")?;
101+
}
102+
Ok(())
103+
}
104+
}

2018/day24/src/attacktype.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use std::string::ParseError;
2+
3+
/// Kind of attacks. Units can deal damage with these attacks, can be immune or weak.
4+
#[derive(PartialEq, Eq, Debug, Clone)]
5+
pub enum AttackType {
6+
Fire,
7+
Radiation,
8+
Slashing,
9+
Bludgeoning,
10+
Cold,
11+
}
12+
13+
impl std::str::FromStr for AttackType {
14+
type Err = ParseError;
15+
fn from_str(s: &str) -> Result<Self, Self::Err> {
16+
Ok(match s {
17+
"fire" => AttackType::Fire,
18+
"radiation" => AttackType::Radiation,
19+
"slashing" => AttackType::Slashing,
20+
"bludgeoning" => AttackType::Bludgeoning,
21+
"cold" => AttackType::Cold,
22+
_ => panic!(),
23+
})
24+
}
25+
}

2018/day24/src/combat.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use crate::army::Army;
2+
3+
const MAX_FIGHTS: usize = 10000;
4+
5+
#[derive(Clone)]
6+
pub struct Combat {
7+
army1: Army,
8+
army2: Army,
9+
}
10+
11+
impl Default for Combat {
12+
fn default() -> Self {
13+
Self::new()
14+
}
15+
}
16+
17+
impl Combat {
18+
#[must_use]
19+
pub fn new() -> Self {
20+
Self {
21+
army1: Army::new(),
22+
army2: Army::new(),
23+
}
24+
}
25+
26+
#[must_use]
27+
pub fn with_armies(army1: Army, army2: Army) -> Self {
28+
Self { army1, army2 }
29+
}
30+
31+
pub fn set_army1_boost(&mut self, boost: u32) {
32+
self.army1.set_boost(boost);
33+
}
34+
35+
#[must_use]
36+
pub fn immune_alive_units(&self) -> u32 {
37+
self.army1.alive_units()
38+
}
39+
40+
#[must_use]
41+
pub fn infection_alive_units(&self) -> u32 {
42+
self.army2.alive_units()
43+
}
44+
45+
#[must_use]
46+
pub fn fight_to_death(&self) -> u32 {
47+
for _ in 0..MAX_FIGHTS {
48+
if let Some(winner) = self.fight() {
49+
return winner.alive_units();
50+
}
51+
}
52+
0
53+
}
54+
55+
/// # Panics
56+
/// si both armies are alive
57+
#[must_use]
58+
pub fn fight(&self) -> Option<&Army> {
59+
let mut fights = self.army1.select_fights(&self.army2);
60+
fights.extend(self.army2.select_fights(&self.army1));
61+
62+
// sort by initiative, greater first
63+
fights.sort_unstable();
64+
65+
for f in &fights {
66+
f.fight();
67+
}
68+
69+
assert!(self.army1.is_alive() || self.army2.is_alive());
70+
71+
if !self.army1.is_alive() {
72+
Some(&self.army2)
73+
} else if !self.army2.is_alive() {
74+
Some(&self.army1)
75+
} else {
76+
None
77+
}
78+
}
79+
}
80+
81+
impl std::fmt::Display for Combat {
82+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83+
write!(f, "{}", self.army1)?;
84+
write!(f, "{}", self.army2)
85+
}
86+
}

0 commit comments

Comments
 (0)