Skip to main content

azalea/pathfinder/
costs.rs

1use std::sync::LazyLock;
2
3use num_traits::Float;
4
5// based on https://github.com/cabaletta/baritone/blob/1.20.1/src/api/java/baritone/api/pathing/movement/ActionCosts.java
6pub const WALK_ONE_BLOCK_COST: f32 = 20. / 4.317; // 4.633
7pub const SPRINT_ONE_BLOCK_COST: f32 = 20. / 5.612; // 3.564
8pub const WALK_OFF_BLOCK_COST: f32 = WALK_ONE_BLOCK_COST * 0.8; // 3.706
9pub const SPRINT_MULTIPLIER: f32 = SPRINT_ONE_BLOCK_COST / WALK_ONE_BLOCK_COST; // 0.769
10pub const JUMP_PENALTY: f32 = 2.;
11pub const ENTER_WATER_PENALTY: f32 = 3.;
12pub const CENTER_AFTER_FALL_COST: f32 = WALK_ONE_BLOCK_COST - WALK_OFF_BLOCK_COST; // 0.927
13pub const WALK_ONE_IN_WATER_COST: f32 = 20. / 1.960; // 10.204
14
15// explanation here:
16// https://github.com/cabaletta/baritone/blob/f147519a5c291015d4f18c94558a3f1bdcdb9588/src/api/java/baritone/api/Settings.java#L405
17// it's basically a multiplier used by some heuristics to convert x and z
18// distance to ticks
19pub const COST_HEURISTIC: f32 = 3.563;
20
21// this one is also from baritone, it's helpful as a tiebreaker to avoid
22// breaking blocks if it can be avoided
23pub const BLOCK_BREAK_ADDITIONAL_PENALTY: f32 = 2.;
24
25pub static FALL_1_25_BLOCKS_COST: LazyLock<f32> = LazyLock::new(|| distance_to_ticks(1.25));
26pub static FALL_0_25_BLOCKS_COST: LazyLock<f32> = LazyLock::new(|| distance_to_ticks(0.25));
27pub static JUMP_ONE_BLOCK_COST: LazyLock<f32> =
28    LazyLock::new(|| *FALL_1_25_BLOCKS_COST - *FALL_0_25_BLOCKS_COST); // 3.163
29
30// [0, 5.614727, 7.7880826, 9.468678, ..]
31pub static FALL_N_BLOCKS_COST: LazyLock<[f32; 4097]> = LazyLock::new(|| {
32    // mostly the same as calculating distance_to_ticks for every distance, but in
33    // linear time complexity
34
35    let mut fall_n_blocks_cost = [0.; 4097];
36    let mut last_distance_blocks = 0;
37    let mut current_distance = 0.;
38    let mut tick_count = 0;
39
40    'outer: loop {
41        let fall_distance_per_tick = velocity(tick_count);
42
43        let current_distance_blocks = (current_distance + fall_distance_per_tick) as usize;
44        if current_distance_blocks > last_distance_blocks {
45            for blocks in (last_distance_blocks + 1)..=current_distance_blocks {
46                if blocks == fall_n_blocks_cost.len() {
47                    break 'outer;
48                }
49
50                fall_n_blocks_cost[blocks] =
51                    tick_count as f32 + (blocks as f32 - current_distance) / fall_distance_per_tick;
52            }
53
54            last_distance_blocks = current_distance_blocks;
55        }
56
57        current_distance += fall_distance_per_tick;
58        tick_count += 1;
59    }
60
61    fall_n_blocks_cost
62});
63
64fn velocity(ticks: u32) -> f32 {
65    (0.98.powi(ticks as i32) - 1.) * -3.92
66}
67
68fn distance_to_ticks(distance: f32) -> f32 {
69    if distance == 0. {
70        return 0.;
71    }
72    let mut tick_count = 0;
73    let mut remaining_distance = distance;
74    loop {
75        let fall_distance_per_tick = velocity(tick_count);
76        if fall_distance_per_tick >= remaining_distance {
77            // add a bit extra to prefer smaller falls even if they're the same number of
78            // ticks
79            return (tick_count as f32) + (remaining_distance / fall_distance_per_tick);
80        }
81        remaining_distance -= fall_distance_per_tick;
82        tick_count += 1;
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use crate::pathfinder::costs::{FALL_N_BLOCKS_COST, distance_to_ticks};
89
90    #[test]
91    fn test_fall_n_blocks_cost() {
92        for i in 0..4 {
93            let a = FALL_N_BLOCKS_COST[i];
94            let b = distance_to_ticks(i as f32);
95            assert!((a - b).abs() < 0.1, "{a} != {b}");
96        }
97    }
98}