screeps_utils/math/
tower.rs

1use screeps::constants::*;
2
3/// Provides the amount of damage done by tower attacks at a given range, after
4/// accounting for reduction from [`TOWER_FALLOFF`].
5///
6/// [Code reference](https://github.com/screeps/engine/blob/c6c4fc9e656f160e0e0174b0dd9a817d2dd18976/src/processor/intents/towers/attack.js#L33-L38)
7pub fn tower_attack_power_at_range(mut range: u8) -> u32 {
8    let mut amount = TOWER_POWER_ATTACK as f64;
9    if range > TOWER_FALLOFF_RANGE {
10        range = TOWER_FALLOFF_RANGE;
11    }
12    amount -= amount * TOWER_FALLOFF * range.saturating_sub(TOWER_OPTIMAL_RANGE) as f64
13        / (TOWER_FALLOFF_RANGE - TOWER_OPTIMAL_RANGE) as f64;
14    amount as u32
15}
16
17/// Provides the amount of damage healed by tower healing at a given range,
18/// after accounting for reduction from [`TOWER_FALLOFF`].
19///
20/// [Code reference](https://github.com/screeps/engine/blob/c6c4fc9e656f160e0e0174b0dd9a817d2dd18976/src/processor/intents/towers/heal.js#L24-L30)
21pub fn tower_heal_power_at_range(mut range: u8) -> u32 {
22    let mut amount = TOWER_POWER_HEAL as f64;
23    if range > TOWER_FALLOFF_RANGE {
24        range = TOWER_FALLOFF_RANGE;
25    }
26    amount -= amount * TOWER_FALLOFF * range.saturating_sub(TOWER_OPTIMAL_RANGE) as f64
27        / (TOWER_FALLOFF_RANGE - TOWER_OPTIMAL_RANGE) as f64;
28    amount as u32
29}
30
31/// Provides the amount of damage repaired by towers at a given range, after
32/// accounting for reduction from [`TOWER_FALLOFF`].
33///
34/// [Code reference](https://github.com/screeps/engine/blob/c6c4fc9e656f160e0e0174b0dd9a817d2dd18976/src/processor/intents/towers/repair.js#L21-L26)
35pub fn tower_repair_power_at_range(mut range: u8) -> u32 {
36    let mut amount = TOWER_POWER_REPAIR as f64;
37    if range > TOWER_FALLOFF_RANGE {
38        range = TOWER_FALLOFF_RANGE;
39    }
40    amount -= amount * TOWER_FALLOFF * range.saturating_sub(TOWER_OPTIMAL_RANGE) as f64
41        / (TOWER_FALLOFF_RANGE - TOWER_OPTIMAL_RANGE) as f64;
42    amount as u32
43}
44
45#[cfg(test)]
46mod test {
47    use super::*;
48
49    #[test]
50    fn tower_attack_power_formula() {
51        // at optimal range, the damage should be 100% of the attack power
52        assert_eq!(
53            tower_attack_power_at_range(TOWER_OPTIMAL_RANGE),
54            TOWER_POWER_ATTACK
55        );
56        // at full falloff range, we should have 1 - TOWER_FALLOFF (25%) damage
57        assert_eq!(
58            tower_attack_power_at_range(TOWER_FALLOFF_RANGE),
59            (TOWER_POWER_ATTACK as f64 * (1. - TOWER_FALLOFF)) as u32
60        );
61        // test values generated in js using the engine's code
62        assert_eq!(tower_attack_power_at_range(5), 600);
63        assert_eq!(tower_attack_power_at_range(6), 570);
64        assert_eq!(tower_attack_power_at_range(7), 540);
65        assert_eq!(tower_attack_power_at_range(8), 510);
66        assert_eq!(tower_attack_power_at_range(9), 480);
67        assert_eq!(tower_attack_power_at_range(10), 450);
68        assert_eq!(tower_attack_power_at_range(11), 420);
69        assert_eq!(tower_attack_power_at_range(12), 390);
70        assert_eq!(tower_attack_power_at_range(13), 360);
71        assert_eq!(tower_attack_power_at_range(14), 330);
72        assert_eq!(tower_attack_power_at_range(15), 300);
73        assert_eq!(tower_attack_power_at_range(16), 270);
74        assert_eq!(tower_attack_power_at_range(17), 240);
75        assert_eq!(tower_attack_power_at_range(18), 210);
76        assert_eq!(tower_attack_power_at_range(19), 180);
77        assert_eq!(tower_attack_power_at_range(20), 150);
78        // falloff range stops at 20, make sure beyond that stays at 150
79        assert_eq!(tower_attack_power_at_range(25), 150);
80        // math should work even at range 0
81        assert_eq!(tower_attack_power_at_range(0), 600);
82    }
83
84    #[test]
85    fn tower_heal_power_formula() {
86        // at optimal range, the damage should be 100% of the heal power
87        assert_eq!(
88            tower_heal_power_at_range(TOWER_OPTIMAL_RANGE),
89            TOWER_POWER_HEAL
90        );
91        // at full falloff range, we should have 1 - TOWER_FALLOFF (25%) hits
92        assert_eq!(
93            tower_heal_power_at_range(TOWER_FALLOFF_RANGE),
94            (TOWER_POWER_HEAL as f64 * (1. - TOWER_FALLOFF)) as u32
95        );
96        // test values generated in js using the engine's code
97        assert_eq!(tower_heal_power_at_range(5), 400);
98        assert_eq!(tower_heal_power_at_range(6), 380);
99        assert_eq!(tower_heal_power_at_range(7), 360);
100        assert_eq!(tower_heal_power_at_range(8), 340);
101        assert_eq!(tower_heal_power_at_range(9), 320);
102        assert_eq!(tower_heal_power_at_range(10), 300);
103        assert_eq!(tower_heal_power_at_range(11), 280);
104        assert_eq!(tower_heal_power_at_range(12), 260);
105        assert_eq!(tower_heal_power_at_range(13), 240);
106        assert_eq!(tower_heal_power_at_range(14), 220);
107        assert_eq!(tower_heal_power_at_range(15), 200);
108        assert_eq!(tower_heal_power_at_range(16), 180);
109        assert_eq!(tower_heal_power_at_range(17), 160);
110        assert_eq!(tower_heal_power_at_range(18), 140);
111        assert_eq!(tower_heal_power_at_range(19), 120);
112        assert_eq!(tower_heal_power_at_range(20), 100);
113        // falloff range stops at 20, make sure beyond that stays at 100
114        assert_eq!(tower_heal_power_at_range(25), 100);
115        // math should work even at range 0
116        assert_eq!(tower_heal_power_at_range(0), 400);
117    }
118
119    #[test]
120    fn tower_repair_power_formula() {
121        // at optimal range, the damage should be 100% of the repair power
122        assert_eq!(
123            tower_repair_power_at_range(TOWER_OPTIMAL_RANGE),
124            TOWER_POWER_REPAIR
125        );
126        // at full falloff range, we should have 1 - TOWER_FALLOFF (25%) repair
127        assert_eq!(
128            tower_repair_power_at_range(TOWER_FALLOFF_RANGE),
129            (TOWER_POWER_REPAIR as f64 * (1. - TOWER_FALLOFF)) as u32
130        );
131        // test values generated in js using the engine's code
132        assert_eq!(tower_repair_power_at_range(5), 800);
133        assert_eq!(tower_repair_power_at_range(6), 760);
134        assert_eq!(tower_repair_power_at_range(7), 720);
135        assert_eq!(tower_repair_power_at_range(8), 680);
136        assert_eq!(tower_repair_power_at_range(9), 640);
137        assert_eq!(tower_repair_power_at_range(10), 600);
138        assert_eq!(tower_repair_power_at_range(11), 560);
139        assert_eq!(tower_repair_power_at_range(12), 520);
140        assert_eq!(tower_repair_power_at_range(13), 480);
141        assert_eq!(tower_repair_power_at_range(14), 440);
142        assert_eq!(tower_repair_power_at_range(15), 400);
143        assert_eq!(tower_repair_power_at_range(16), 360);
144        assert_eq!(tower_repair_power_at_range(17), 320);
145        assert_eq!(tower_repair_power_at_range(18), 280);
146        assert_eq!(tower_repair_power_at_range(19), 240);
147        assert_eq!(tower_repair_power_at_range(20), 200);
148        // falloff range stops at 20, make sure beyond that stays at 200
149        assert_eq!(tower_repair_power_at_range(25), 200);
150        // math should work even at range 0
151        assert_eq!(tower_repair_power_at_range(0), 800);
152    }
153}