summaryrefslogtreecommitdiff
path: root/main.py
blob: c73a0116a1eb130d4c80232cf9c9a8f2fb588528 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import math
from distribution import Distribution


def d(edge_count):
    res = Distribution()
    if edge_count == 0:
        res.insert(0, 1)
        return res
    prob = 1/edge_count
    for val in range(1, edge_count+1):
        res.insert(val, prob)
    return res


class Roll:
    Fumble = -1
    Fail = 0
    Success = 1
    Crit = 2

    @staticmethod
    def get_natural_success_mod(value):
        if value <= 1:
            return -1
        if value >= 20:
            return 1
        return 0

    @staticmethod
    def check(value, dc, bonus=0):
        res = 0
        rolled = value + bonus
        if rolled >= dc + 10:
            res = Roll.Crit
        elif rolled >= dc:
            res = Roll.Success
        elif rolled <= dc - 10:
            res = Roll.Fumble
        res += Roll.get_natural_success_mod(value)
        return Roll.clamp(res)

    @staticmethod
    def clamp(value):
        return max(Roll.Fumble, min(Roll.Crit, value))


def treat_wounds(check_res, heal_bonus=0):
    if check_res <= Roll.Fumble:
        return -d(8)
    if check_res == Roll.Fail:
        return 0
    if check_res == Roll.Success:
        return 2*d(8) + heal_bonus
    if check_res == Roll.Crit:
        return 4*d(8) + heal_bonus

def strike(check_res, dmg, crit_dmg=0):
    if check_res == Roll.Success:
        return dmg
    if check_res == Roll.Crit:
        return 2*dmg + crit_dmg
    return 0


def show_stat(name, dist):
    print(name)
    values = list(dist.probabilities.keys())
    mn = min(values)
    mx = max(values)
    print("min", mn, dist.probabilities[mn])
    print("max", mx, dist.probabilities[mx])
    print("Exp", dist.expected())
    print("Std", dist.standard_deviation())
    print("<0", dist.accumulate_prob(lambda x: x < 0))
    print(">0", dist.accumulate_prob(lambda x: x > 0))
    print("")
    assert abs(dist.accumulate_prob(lambda x: True) - 1) < math.pow(10, -10)


coin = d(2)


def main():
    treat_options = [
        {"dc": 15, "heal_bonus": 0},
        {"dc": 20, "heal_bonus": 15}
    ]
    bonus = 10
    show_stat("Assurance 15", 2*d(8))
    for opt in treat_options:
        dc = opt["dc"]
        treat = d(20).apply(Roll.check, dc, bonus).apply(treat_wounds, opt["heal_bonus"])
        risky = d(20).apply(Roll.check, dc, bonus+2).apply(lambda x: Roll.Crit if x == Roll.Success else x)
        risky = risky.apply(treat_wounds, opt["heal_bonus"]) + (-d(8))

        show_stat("Treat Wounds " + str(dc), treat)
        show_stat("Risky Surgery " + str(dc), risky)

    treat = d(20).apply(Roll.check, 20, bonus+1).apply(treat_wounds, 15)
    show_stat("Treat Wounds 20 +1", treat)

def main2():
    ac = 18
    bonus = +8
    dmg = d(6)+4
    attack = d(20).apply(Roll.check, ac, bonus)
    show_stat("attack", attack)
    total_dmg = attack.apply(strike, dmg+1)
    show_stat("dogslicer", total_dmg)
    total_dmg = attack.apply(strike, dmg, d(8))
    show_stat("rapier", total_dmg)


if __name__ == '__main__':
    main2()