atagen

jelq

public

an http based roguelike rpg about jelqing

Star0
Watch0
97c05b741d8f0a39cec7b9e24bf5491f18c819c5
1
ssh://brewery@pls.lobotomise.me:33/atagen/jelq.git
src/tests/rolls.rsRust233 lines7.1 KB
1
use super::*;
2
3
#[tokio::test]
4
async fn holding_calms_and_sometimes_reflects() {
5
    let mut calmed = 0;
6
    let mut reflected = 0;
7
    let mut ruptured = 0;
8
    for seed in 0..600u64 {
9
        let mut run = Run::new("cafebabe".into(), "cafe".into(), seed);
10
        run.body.eq = 75;
11
        match run.hold() {
12
            Ok(_) if run.body.eq == 0 => reflected += 1,
13
            Ok(_) => calmed += 1,
14
            Err(_) => ruptured += 1,
15
        }
16
    }
17
    assert!(
18
        (25..=80).contains(&reflected),
19
        "one HOLD in twelve should reflect, about 50 of 600, got {reflected} and {calmed} calm"
20
    );
21
    assert!(
22
        ruptured <= 7,
23
        "the flat 1 in 250 ruptured {ruptured} of 600 holds, about 2.4 expected"
24
    );
25
}
26
27
#[tokio::test]
28
async fn the_burn_shifts_the_rupture_tiers() {
29
    async fn ruptures(burned: bool) -> u32 {
30
        let mut gone = 0;
31
        for seed in 0..400u64 {
32
            let app = app_with(Floor::Bedroom, seed, |run| {
33
                run.body.warmed_up = true;
34
                run.body.eq = 60;
35
                run.items.lubes.baby_oil = 9;
36
                run.body.conditions.friction_burn = burned;
37
            });
38
            let lube = [("content-type", "application/baby-oil")];
39
            let jelq = send(
40
                &app,
41
                "JELQ",
42
                "/runs/cafebabe?technique=wet&intensity=5",
43
                &lube,
44
                "",
45
            )
46
            .await;
47
            if jelq.status() == StatusCode::INTERNAL_SERVER_ERROR {
48
                gone += 1;
49
            }
50
        }
51
        gone
52
    }
53
    let burned = ruptures(true).await;
54
    let calm = ruptures(false).await;
55
    assert!(
56
        burned >= 8 && calm <= 4,
57
        "burned {burned} ruptures against {calm}"
58
    );
59
}
60
61
#[tokio::test]
62
async fn the_top_tier_rolls_a_single_third() {
63
    let mut ruptures = 0;
64
    let mut early = 0;
65
    for seed in 0..400u64 {
66
        let app = app_with(Floor::Bedroom, seed, |run| {
67
            run.body.warmed_up = true;
68
            run.body.eq = 96;
69
            run.items.lubes.baby_oil = 9;
70
            run.items.kit.blinds = true;
71
        });
72
        let lube = [("content-type", "application/baby-oil")];
73
        let jelq = send(
74
            &app,
75
            "JELQ",
76
            "/runs/cafebabe?technique=wet&intensity=5",
77
            &lube,
78
            "",
79
        )
80
        .await;
81
        match jelq.status() {
82
            StatusCode::INTERNAL_SERVER_ERROR => ruptures += 1,
83
            StatusCode::TOO_EARLY => early += 1,
84
            other => panic!("seed {seed} ended in {other}"),
85
        }
86
    }
87
    assert!(
88
        (90..=175).contains(&ruptures),
89
        "ruptures {ruptures} of 400 against {early} early"
90
    );
91
    assert!(
92
        (225..=310).contains(&early),
93
        "early {early} of 400 against {ruptures} ruptures"
94
    );
95
}
96
97
#[tokio::test]
98
async fn holding_rolls_a_flat_rupture_chance() {
99
    async fn ruptures(eq: i32, stamina: i32) -> u32 {
100
        let mut gone = 0;
101
        for seed in 0..600u64 {
102
            let app = app_with(Floor::Bedroom, seed, |run| {
103
                run.body.warmed_up = true;
104
                run.body.eq = eq;
105
                run.body.stamina = stamina;
106
            });
107
            let held = send(&app, "HOLD", "/runs/cafebabe", &[], "").await;
108
            if held.status() == StatusCode::INTERNAL_SERVER_ERROR {
109
                gone += 1;
110
            }
111
        }
112
        gone
113
    }
114
    let high = ruptures(90, 10).await;
115
    let low = ruptures(30, 2).await;
116
    assert!((0..=7).contains(&high), "high holds ruptured {high}/600");
117
    assert!((0..=7).contains(&low), "low holds ruptured {low}/600");
118
}
119
120
#[tokio::test]
121
async fn the_kitchen_pays_out_at_the_documented_odds() {
122
    let mut oil = 0;
123
    let mut drinks = 0;
124
    let mut packs = 0;
125
    let mut raids = 0;
126
    for seed in 0..800u64 {
127
        let mut run = Run::new("cafebabe".into(), "cafe".into(), seed);
128
        let held = (
129
            run.items.lubes.coconut_oil,
130
            run.items.stores.energy_drinks,
131
            run.items.stores.ice_packs,
132
        );
133
        if run.raid_kitchen().is_err() {
134
            continue;
135
        }
136
        raids += 1;
137
        oil += u32::from(run.items.lubes.coconut_oil > held.0);
138
        drinks += u32::from(run.items.stores.energy_drinks > held.1);
139
        packs += u32::from(run.items.stores.ice_packs > held.2);
140
    }
141
    let rate = |found: u32| f64::from(found) / f64::from(raids);
142
    assert!(
143
        (0.28..=0.39).contains(&rate(oil)),
144
        "coconut {oil} in {raids}"
145
    );
146
    assert!(
147
        (0.15..=0.25).contains(&rate(drinks)),
148
        "energy {drinks} in {raids}"
149
    );
150
    assert!(
151
        (0.08..=0.17).contains(&rate(packs)),
152
        "ice {packs} in {raids}"
153
    );
154
}
155
156
#[tokio::test]
157
async fn the_kitchen_rolls_the_normal_tiers() {
158
    async fn ruptures(eq: i32) -> u32 {
159
        let mut gone = 0;
160
        for seed in 0..600u64 {
161
            let app = app_with(Floor::Bedroom, seed, |run| run.body.eq = eq);
162
            let raid = send(&app, "POST", "/runs/cafebabe/kitchen", &[], "").await;
163
            if raid.status() == StatusCode::INTERNAL_SERVER_ERROR {
164
                gone += 1;
165
            }
166
        }
167
        gone
168
    }
169
    let hot = ruptures(96).await;
170
    let cool = ruptures(40).await;
171
    assert!((40..=80).contains(&hot), "hot kitchen ruptured {hot}/600");
172
    assert!((0..=9).contains(&cool), "cool kitchen ruptured {cool}/600");
173
}
174
175
#[tokio::test]
176
async fn degloving_takes_one_hard_dry_stroke_in_twenty() {
177
    let mut degloved = 0;
178
    for seed in 0..1200u64 {
179
        let app = app_with(Floor::Hotel, seed, |run| {
180
            run.here.wifi_accepted = true;
181
            run.body.warmed_up = true;
182
            run.body.eq = 55;
183
            run.body.foreskin = Some(1.0);
184
        });
185
        let dry = send(
186
            &app,
187
            "JELQ",
188
            "/runs/cafebabe?technique=dry&intensity=8",
189
            &[],
190
            "",
191
        )
192
        .await;
193
        if dry.status() == StatusCode::GONE
194
            && json(dry).await["type"]
195
                .as_str()
196
                .unwrap()
197
                .ends_with("degloved")
198
        {
199
            degloved += 1;
200
        }
201
    }
202
    assert!(
203
        (35..=90).contains(&degloved),
204
        "{degloved} degloved in 1200 hard dry strokes, about 60 expected"
205
    );
206
}
207
208
#[tokio::test]
209
async fn degloving_takes_one_cold_stroke_in_twenty() {
210
    let mut degloved = 0;
211
    for seed in 0..1200u64 {
212
        let app = app_with(Floor::Hotel, seed, |run| {
213
            run.here.wifi_accepted = true;
214
            run.body.eq = 55;
215
            run.body.foreskin = Some(1.0);
216
        });
217
        let cold = send(&app, "JELQ", "/runs/cafebabe?technique=stretch", &[], "").await;
218
        if cold.status() != StatusCode::GONE {
219
            continue;
220
        }
221
        let bare = header(&cold, "x-foreskin") == "gone";
222
        let problem = json(cold).await;
223
        if problem["type"].as_str().unwrap().ends_with("degloved") {
224
            assert!(bare, "degloving takes the foreskin with it");
225
            assert_eq!(problem["foreskin"], -1.0);
226
            degloved += 1;
227
        }
228
    }
229
    assert!(
230
        (35..=90).contains(&degloved),
231
        "{degloved} degloved in 1200 cold strokes, about 60 expected"
232
    );
233
}