@anonymous/

GrotesqueCreativeInitialization

Python

No description

fork
loading
Files
  • main.py

This Plugin Crashed!

Error: Error: must not create an existing file {"type":"CREATE_FILE","wid":"0.3971518885728569","path":"main.py","file":{"path":"main.py","content":{"asEncoding":{"base64":"aW1wb3J0IGRlY2ltYWwKCiMgSGVscGVyIG1ldGhvZCB0byBzYXZlIG9uIHR5cGluZwpkZWYgZChudW0pOgogICAgcmV0dXJuIGRlY2ltYWwuRGVjaW1hbChudW0pCgpkZWYgZ2V0TnVtYmVyKG1zZyk6CiAgICBwcmludChtc2cpCiAgICB0cnk6CiAgICAgICAgcmV0dXJuIGludChpbnB1dCgiID4gIikpCiAgICBleGNlcHQgRU9GRXJyb3I6CiAgICAgICAgcmV0dXJuIGdldE51bWJlcihtc2cpCiAgICBleGNlcHQgVmFsdWVFcnJvcjoKICAgICAgICBwcmludCgiVGhhdCdzIG5vdCBhIG51bWJlciEiKQogICAgICAgIHJldHVybiBnZXROdW1iZXIobXNnKQoKIyBSZXR1cm4gdGhlIG9kZHMgb2YgYSBzaW5nbGUgZHJhdyAqbm90KiBiZWluZyBvbmUgb2YgdGhlIGRlc2lyZWQgY2FyZHMuCmRlZiBzaW5nbGVEcmF3TWlzc0NoYW5jZShjb3BpZXMsIGRyYXduU29GYXIpOgogICAgcmV0dXJuIGQoMSkgLSAoY29waWVzIC8gZCgzOCAtIGRyYXduU29GYXIpKQoKIyBSZXR1cm4gdGhlIG9kZHMgb2YgTiBkcmF3cywgc3RhcnRpbmcgd2l0aCBhIGNlcnRhaW4gbnVtYmVyIG9mIGNhcmRzIGRyYXduIHNvIGZhciwgbm90IGJlaW5nIGFueSBvZiB0aGUgZGVzaXJlZCBjYXJkcy4KZGVmIG5EcmF3TWlzc0NoYW5jZShjb3BpZXMsIGRyYXduU29GYXIsIG51bURyYXdzKToKICAgIHJlc3VsdCA9IGQoMSkKICAgIGZvciBpIGluIHJhbmdlKDAsIG51bURyYXdzKToKICAgICAgICByZXN1bHQgPSByZXN1bHQgKiBzaW5nbGVEcmF3TWlzc0NoYW5jZShjb3BpZXMsIGRyYXduU29GYXIgKyBpKQogICAgCiAgICByZXR1cm4gcmVzdWx0CgojIFJldHVybiB0aGUgb2RkcyBvZiBhIHNpbmdsZSB0dXJuIGN5Y2xlIC0gb25lIHJlcGxhY2UgYW5kIG9uZSBkcmF3IC0gbm90IGJlaW5nIGFueSBvZiB0aGUgZGVzaXJlZCBjYXJkcy4KIyBUaGUgcmVwbGFjZSBkb2Vzbid0IG1ha2UgdGhlIGRlY2sgYW55IHNtYWxsZXIsIHNvIGRyYXduU29GYXIgZG9lc24ndCBoZWxwLgpkZWYgdHVybkN5Y2xlTWlzc0NoYW5jZShjb3BpZXMsIGRyYXduU29GYXIpOgogICAgcmV0dXJuIHNpbmdsZURyYXdNaXNzQ2hhbmNlKGNvcGllcywgZHJhd25Tb0ZhcikgKiBzaW5nbGVEcmF3TWlzc0NoYW5jZShjb3BpZXMsIGRyYXduU29GYXIpCgojIFJldHVybiB0aGUgb2RkcyB0aGF0IHRoZSBkZXNpcmVkIGNhcmRzIGFyZSAqbm90KiBkcmF3biBpbiB0aGUgb3BlbmluZyBoYW5kLgojIEZpdmUgZHJhd3MgKyB0d28gcmVwbGFjZXMsIHdpdGggbm8gcmVzdHJpY3Rpb24gb24gd2hpY2ggY2FyZHMgY2FuIGNvbWUgYmFjay4KIyBCb3RoIHJlcGxhY2VkIGNhcmRzIGFyZSByZXBsYWNlZCBhdCBvbmNlLCBhcyBmYXIgYXMgSSdtIGF3YXJlLiBUaGlzIG1lYW5zIG9uZSBvZiB0aGVtIHVzZXMgZHJhd25Tb0Zhcj0zCiMgYW5kIHRoZSBvdGhlciB1c2VzIGRyYXduU29GYXI9NC4KZGVmIG11bGxpZ2FuT2Rkcyhjb3BpZXMpOgogICAgcmV0dXJuIG5EcmF3TWlzc0NoYW5jZShjb3BpZXMsIDAsIDUpICogc2luZ2xlRHJhd01pc3NDaGFuY2UoY29waWVzLCAzKSAqIHNpbmdsZURyYXdNaXNzQ2hhbmNlKGNvcGllcywgNCkKCmRlZiBtYWluKCk6CiAgICBwcmludCgiIyMjIEhvdyBsdWNreSBpcyB5b3VyIFdhbmRlcmVyIG9wcG9uZW50PyAjIyMiKQoKICAgIGNvcGllcyA9IGdldE51bWJlcigiSG93IG1hbnkgY29waWVzIG9mIHRoZSBlZmZlY3QgdGhleSBuZWVkIGFyZSBpbiB0aGUgZGVjaz8iKQogICAgdHVybiA9IGdldE51bWJlcigiV2hhdCB0dXJuIGRpZCB0aGV5IG5lZWQgdG8gZHJhdyBpdCBieT8iKQoKICAgICMgVXNlIERlY2ltYWxzIGZvciBwcmVjaXNpb24uCiAgICBjb3BpZXMgPSBkKGNvcGllcykKCiAgICAjIEFjY3VtdWxhdGUgdGhlIHJlc3VsdC4gU3RhcnQgd2l0aCBhbiBvcGVuaW5nIGhhbmQsIHRoZW4gKHR1cm4gLSAxKSB0dXJucywgYW5kIGEgZmluYWwgcmVwbGFjZS4KICAgIG1pc3NDaGFuY2UgPSBtdWxsaWdhbk9kZHMoY29waWVzKQogICAgIyBXZSBkcmF3IG9uZSBjYXJkIGZyb20gdGhlIGRlY2sgcGVyIHR1cm4sIG9uIHRvcCBvZiB0aGUgZml2ZSB3ZSByZW1vdmVkIGR1cmluZyB0aGUgb3BlbmluZyBoYW5kLgogICAgIyBTbyBvbiB0dXJuIDEgd2UndmUgYWxyZWFkeSBkcmF3biA1IGNhcmRzLCBieSB0dXJuIDIgd2UndmUgZHJhd24gNiwgZXRjLgogICAgZHJhd25Tb0ZhciA9IDUKICAgIGZvciBpIGluIHJhbmdlKDEsIHR1cm4pOgogICAgICAgIG1pc3NDaGFuY2UgKj0gdHVybkN5Y2xlTWlzc0NoYW5jZShjb3BpZXMsIGRyYXduU29GYXIpCiAgICAgICAgZHJhd25Tb0ZhciArPSAxCiAgICAKICAgICMgT24gdGhlIHBpdm90YWwgdHVybiwgdGhlcmUncyBhbm90aGVyIHJlcGxhY2UgdG8gcmVzb2x2ZS4KICAgIG1pc3NDaGFuY2UgKj0gc2luZ2xlRHJhd01pc3NDaGFuY2UoY29waWVzLCBkcmF3blNvRmFyKQoKICAgICMgV2Ugbm93IGhhdmUgdGhlIGNoYW5jZSBvZiBmYWlsaW5nIHRvIGRyYXcgdGhlIG5lZWRlZCBjYXJkIGZvciB0aGUgZW50aXJlIGdhbWUgdXAgdG8gdGhpcyBwb2ludC4KICAgICMgSW52ZXJ0IGl0IHRvIGdldCB0aGUgcmVzdWx0IHdlIG5lZWQuCiAgICByZXN1bHQgPSBkKDEpIC0gbWlzc0NoYW5jZQogICAgcGVyY2VudGFnZSA9IDEwMCAqIHJlc3VsdAoKICAgIHByaW50KCJZb3VyIG9wcG9uZW50IGhhZCBhIHswOi4yZn0lIGNoYW5jZSBvZiBnZXR0aW5nIHdoYXQgdGhleSBuZWVkZWQuIi5mb3JtYXQocGVyY2VudGFnZSkpCiAgICBpZiByZXN1bHQgPj0gMC41OgogICAgICAgIHByaW50KCJZb3Ugc2hvdWxkIGhhdmUgcGxheWVkIGFyb3VuZCBpdCEiKQogICAgZWxzZToKICAgICAgICBwcmludCgiWWVhaCwgdGhleSdyZSBhIGx1Y2t5IHNjcnViLiBHbyBhaGVhZCBhbmQgZmxhbWUgdGhlbSBpbiBjaGF0LiIpCgppZiBfX25hbWVfXyA9PSAiX19tYWluX18iOgogICAgbWFpbigp"},"asBuffer":null},"loaded":true}}
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
import decimal

# Helper method to save on typing
def d(num):
    return decimal.Decimal(num)

def getNumber(msg):
    print(msg)
    try:
        return int(input(" > "))
    except EOFError:
        return getNumber(msg)
    except ValueError:
        print("That's not a number!")
        return getNumber(msg)

# Return the odds of a single draw *not* being one of the desired cards.
def singleDrawMissChance(copies, drawnSoFar):
    return d(1) - (copies / d(38 - drawnSoFar))

# Return the odds of N draws, starting with a certain number of cards drawn so far, not being any of the desired cards.
def nDrawMissChance(copies, drawnSoFar, numDraws):
    result = d(1)
    for i in range(0, numDraws):
        result = result * singleDrawMissChance(copies, drawnSoFar + i)
    
    return result

# Return the odds of a single turn cycle - one replace and one draw - not being any of the desired cards.
# The replace doesn't make the deck any smaller, so drawnSoFar doesn't help.
def turnCycleMissChance(copies, drawnSoFar):
    return singleDrawMissChance(copies, drawnSoFar) * singleDrawMissChance(copies, drawnSoFar)

# Return the odds that the desired cards are *not* drawn in the opening hand.
# Five draws + two replaces, with no restriction on which cards can come back.
# Both replaced cards are replaced at once, as far as I'm aware. This means one of them uses drawnSoFar=3
# and the other uses drawnSoFar=4.
def mulliganOdds(copies):
    return nDrawMissChance(copies, 0, 5) * singleDrawMissChance(copies, 3) * singleDrawMissChance(copies, 4)

def main():
    print("### How lucky is your Wanderer opponent? ###")

    copies = getNumber("How many copies of the effect they need are in the deck?")
    turn = getNumber("What turn did they need to draw it by?")

    # Use Decimals for precision.
    copies = d(copies)

    # Accumulate the result. Start with an opening hand, then (turn - 1) turns, and a final replace.
    missChance = mulliganOdds(copies)
    # We draw one card from the deck per turn, on top of the five we removed during the opening hand.
    # So on turn 1 we've already drawn 5 cards, by turn 2 we've drawn 6, etc.
    drawnSoFar = 5
    for i in range(1, turn):
        missChance *= turnCycleMissChance(copies, drawnSoFar)
        drawnSoFar += 1
    
    # On the pivotal turn, there's another replace to resolve.
    missChance *= singleDrawMissChance(copies, drawnSoFar)

    # We now have the chance of failing to draw the needed card for the entire game up to this point.
    # Invert it to get the result we need.
    result = d(1) - missChance
    percentage = 100 * result

    print("Your opponent had a {0:.2f}% chance of getting what they needed.".format(percentage))
    if result >= 0.5:
        print("You should have played around it!")
    else:
        print("Yeah, they're a lucky scrub. Go ahead and flame them in chat.")

if __name__ == "__main__":
    main()