# Pocket Camp Complete - Fortune Cookie Simulator
import random
NUM_OF_SIMULATIONS = 20
NUM_OF_COOKIE_TYPES = 182
# These are the rates pulled directly from the app.
# Each row must have all 10 item rates in ascending order.
LIST_OF_ALL_COOKIE_ODDS = [
[2.53, 7.06, 7.06, 7.06, 7.06, 13.85, 13.85, 13.85, 13.85, 13.85],
[3.41, 9.47, 9.47, 9.47, 9.47, 11.74, 11.74, 11.74, 11.74, 11.74],
[3.01, 7.33, 7.33, 7.33, 7.33, 13.53, 13.53, 13.53, 13.53, 13.53],
[3.11, 6.97, 6.97, 6.97, 6.97, 13.80, 13.80, 13.80, 13.80, 13.80],
[3.39, 9.83, 9.83, 9.83, 11.19, 11.19, 11.19, 11.19, 11.19, 11.19],
[3.01, 9.22, 9.22, 9.22, 9.22, 12.02, 12.02, 12.02, 12.02, 12.02],
[2.72, 7.26, 7.26, 7.26, 7.26, 13.65, 13.65, 13.65, 13.65, 13.65],
[2.83, 7.14, 7.14, 7.14, 7.14, 13.73, 13.73, 13.73, 13.73, 13.73],
[2.32, 6.75, 6.75, 6.75, 6.75, 14.14, 14.14, 14.14, 14.14, 14.14],
[2.61, 9.32, 9.32, 9.32, 9.32, 12.02, 12.02, 12.02, 12.02, 12.02],
[2.65, 6.82, 6.82, 6.82, 6.82, 14.01, 14.01, 14.01, 14.01, 14.01],
[3.03, 7.62, 7.62, 7.62, 7.62, 13.29, 13.29, 13.29, 13.29, 13.29],
[3.09, 7.82, 7.82, 7.82, 7.82, 13.13, 13.13, 13.13, 13.13, 13.13],
[2.93, 6.66, 6.66, 6.66, 6.66, 14.08, 14.08, 14.08, 14.08, 14.08],
[3.13, 9.03, 9.03, 9.03, 9.03, 12.15, 12.15, 12.15, 12.15, 12.15],
[2.75, 6.26, 6.26, 6.26, 6.26, 14.44, 14.44, 14.44, 14.44, 14.44]
]
# This is the number of times each cookie rate was seen in February and March
OCCURANCE_OF_COOKIE_ODDS = [3, 3, 2, 2, 7, 1, 1, 3, 2, 1, 1, 1, 1, 2, 1, 1]
def cookie_simulation_all_items_with_stamps(balk_num):
item_indexes = [i for i in range(10)]
pull_stats = [0] * NUM_OF_COOKIE_TYPES
stamp_count = 0
for c in range(NUM_OF_COOKIE_TYPES):
pull_count = 0
owned_items = [False] * 10
# Get a random set of cookie odds
cookie_odds = random.choices(population=LIST_OF_ALL_COOKIE_ODDS, weights=OCCURANCE_OF_COOKIE_ODDS, k=1)[0]
while not all(owned_items):
# Pull five cookies at a time
pulled_cookies = random.choices(population=item_indexes, weights=cookie_odds, k=5)
pull_count += 5
stamp_count += 6
# Mark which items were given
for p in pulled_cookies:
owned_items[p] = True
# If we've eaten a lot of cookies and still haven't gotten the item,
# try to buy it with stamps.
if not all(owned_items) and pull_count >= balk_num:
# Because this walks the array from forward to back, it tries to buy the 5-star
# item first. This is intentional. If you run out of stamps and still need
# to buy cookies, then you'll have better odds pulling the 3 or 4-star items.
for i in range(10):
if not owned_items[i]:
# 5-star item
if cookie_odds[i] < 4.0:
if stamp_count >= 100:
owned_items[i] = True
stamp_count -= 100
# 4-star item
elif cookie_odds[i] < 10.0:
if stamp_count >= 50:
owned_items[i] = True
stamp_count -= 50
# 3-star item
else:
if stamp_count >= 10:
owned_items[i] = True
stamp_count -= 10
pull_stats[c] = pull_count
return sum(pull_stats)
max_box_pull_balk_point = 12
results = [[0] * NUM_OF_SIMULATIONS for _ in range(max_box_pull_balk_point)]
for i in range(NUM_OF_SIMULATIONS):
for b in range(max_box_pull_balk_point):
results[b][i] = cookie_simulation_all_items_with_stamps(balk_num=(b+1)*5)
for b in range(max_box_pull_balk_point):
results[b] = sorted(results[b])
bottom_five_index = max(1, int(NUM_OF_SIMULATIONS * 0.05))
top_five_index = NUM_OF_SIMULATIONS - bottom_five_index - 1
median_index = int(NUM_OF_SIMULATIONS / 2)
print(f'Cumulative cookies purchased to complete {NUM_OF_COOKIE_TYPES} collections. Simulated {NUM_OF_SIMULATIONS} times.')
print('| Use stamps after X pulls | Best | 5% | Median | 95% | Worst | Average |')
for b in range(max_box_pull_balk_point):
print(f'| {(b+1)*5:02d} | {results[b][0]} | {results[b][bottom_five_index]} | {results[b][median_index]} | {results[b][top_five_index]} | {results[b][-1]} | {round(sum(results[b]) / NUM_OF_SIMULATIONS)} |')
IyBQb2NrZXQgQ2FtcCBDb21wbGV0ZSAtIEZvcnR1bmUgQ29va2llIFNpbXVsYXRvcgoKaW1wb3J0IHJhbmRvbQoKTlVNX09GX1NJTVVMQVRJT05TID0gMjAKTlVNX09GX0NPT0tJRV9UWVBFUyA9IDE4MgoKIyBUaGVzZSBhcmUgdGhlIHJhdGVzIHB1bGxlZCBkaXJlY3RseSBmcm9tIHRoZSBhcHAuCiMgRWFjaCByb3cgbXVzdCBoYXZlIGFsbCAxMCBpdGVtIHJhdGVzIGluIGFzY2VuZGluZyBvcmRlci4KTElTVF9PRl9BTExfQ09PS0lFX09ERFMgPSBbCiAgICBbMi41MywgNy4wNiwgNy4wNiwgNy4wNiwgNy4wNiwgMTMuODUsIDEzLjg1LCAxMy44NSwgMTMuODUsIDEzLjg1XSwKICAgIFszLjQxLCA5LjQ3LCA5LjQ3LCA5LjQ3LCA5LjQ3LCAxMS43NCwgMTEuNzQsIDExLjc0LCAxMS43NCwgMTEuNzRdLAogICAgWzMuMDEsIDcuMzMsIDcuMzMsIDcuMzMsIDcuMzMsIDEzLjUzLCAxMy41MywgMTMuNTMsIDEzLjUzLCAxMy41M10sCiAgICBbMy4xMSwgNi45NywgNi45NywgNi45NywgNi45NywgMTMuODAsIDEzLjgwLCAxMy44MCwgMTMuODAsIDEzLjgwXSwKICAgIFszLjM5LCA5LjgzLCA5LjgzLCA5LjgzLCAxMS4xOSwgMTEuMTksIDExLjE5LCAxMS4xOSwgMTEuMTksIDExLjE5XSwKICAgIFszLjAxLCA5LjIyLCA5LjIyLCA5LjIyLCA5LjIyLCAxMi4wMiwgMTIuMDIsIDEyLjAyLCAxMi4wMiwgMTIuMDJdLAogICAgWzIuNzIsIDcuMjYsIDcuMjYsIDcuMjYsIDcuMjYsIDEzLjY1LCAxMy42NSwgMTMuNjUsIDEzLjY1LCAxMy42NV0sCiAgICBbMi44MywgNy4xNCwgNy4xNCwgNy4xNCwgNy4xNCwgMTMuNzMsIDEzLjczLCAxMy43MywgMTMuNzMsIDEzLjczXSwKICAgIFsyLjMyLCA2Ljc1LCA2Ljc1LCA2Ljc1LCA2Ljc1LCAxNC4xNCwgMTQuMTQsIDE0LjE0LCAxNC4xNCwgMTQuMTRdLAogICAgWzIuNjEsIDkuMzIsIDkuMzIsIDkuMzIsIDkuMzIsIDEyLjAyLCAxMi4wMiwgMTIuMDIsIDEyLjAyLCAxMi4wMl0sCiAgICBbMi42NSwgNi44MiwgNi44MiwgNi44MiwgNi44MiwgMTQuMDEsIDE0LjAxLCAxNC4wMSwgMTQuMDEsIDE0LjAxXSwKICAgIFszLjAzLCA3LjYyLCA3LjYyLCA3LjYyLCA3LjYyLCAxMy4yOSwgMTMuMjksIDEzLjI5LCAxMy4yOSwgMTMuMjldLAogICAgWzMuMDksIDcuODIsIDcuODIsIDcuODIsIDcuODIsIDEzLjEzLCAxMy4xMywgMTMuMTMsIDEzLjEzLCAxMy4xM10sCiAgICBbMi45MywgNi42NiwgNi42NiwgNi42NiwgNi42NiwgMTQuMDgsIDE0LjA4LCAxNC4wOCwgMTQuMDgsIDE0LjA4XSwKICAgIFszLjEzLCA5LjAzLCA5LjAzLCA5LjAzLCA5LjAzLCAxMi4xNSwgMTIuMTUsIDEyLjE1LCAxMi4xNSwgMTIuMTVdLAogICAgWzIuNzUsIDYuMjYsIDYuMjYsIDYuMjYsIDYuMjYsIDE0LjQ0LCAxNC40NCwgMTQuNDQsIDE0LjQ0LCAxNC40NF0KXQoKIyBUaGlzIGlzIHRoZSBudW1iZXIgb2YgdGltZXMgZWFjaCBjb29raWUgcmF0ZSB3YXMgc2VlbiBpbiBGZWJydWFyeSBhbmQgTWFyY2gKT0NDVVJBTkNFX09GX0NPT0tJRV9PRERTID0gWzMsIDMsIDIsIDIsIDcsIDEsIDEsIDMsIDIsIDEsIDEsIDEsIDEsIDIsIDEsIDFdCgpkZWYgY29va2llX3NpbXVsYXRpb25fYWxsX2l0ZW1zX3dpdGhfc3RhbXBzKGJhbGtfbnVtKToKICAgIGl0ZW1faW5kZXhlcyA9IFtpIGZvciBpIGluIHJhbmdlKDEwKV0KICAgIHB1bGxfc3RhdHMgPSBbMF0gKiBOVU1fT0ZfQ09PS0lFX1RZUEVTCiAgICBzdGFtcF9jb3VudCA9IDAKCiAgICBmb3IgYyBpbiByYW5nZShOVU1fT0ZfQ09PS0lFX1RZUEVTKToKICAgICAgICBwdWxsX2NvdW50ID0gMAogICAgICAgIG93bmVkX2l0ZW1zID0gW0ZhbHNlXSAqIDEwCgogICAgICAgICMgR2V0IGEgcmFuZG9tIHNldCBvZiBjb29raWUgb2RkcwogICAgICAgIGNvb2tpZV9vZGRzID0gcmFuZG9tLmNob2ljZXMocG9wdWxhdGlvbj1MSVNUX09GX0FMTF9DT09LSUVfT0REUywgd2VpZ2h0cz1PQ0NVUkFOQ0VfT0ZfQ09PS0lFX09ERFMsIGs9MSlbMF0KCiAgICAgICAgd2hpbGUgbm90IGFsbChvd25lZF9pdGVtcyk6CiAgICAgICAgICAgICMgUHVsbCBmaXZlIGNvb2tpZXMgYXQgYSB0aW1lCiAgICAgICAgICAgIHB1bGxlZF9jb29raWVzID0gcmFuZG9tLmNob2ljZXMocG9wdWxhdGlvbj1pdGVtX2luZGV4ZXMsIHdlaWdodHM9Y29va2llX29kZHMsIGs9NSkKICAgICAgICAgICAgcHVsbF9jb3VudCArPSA1CiAgICAgICAgICAgIHN0YW1wX2NvdW50ICs9IDYKCiAgICAgICAgICAgICMgTWFyayB3aGljaCBpdGVtcyB3ZXJlIGdpdmVuCiAgICAgICAgICAgIGZvciBwIGluIHB1bGxlZF9jb29raWVzOgogICAgICAgICAgICAgICAgb3duZWRfaXRlbXNbcF0gPSBUcnVlCiAgICAgICAgICAgIAogICAgICAgICAgICAjIElmIHdlJ3ZlIGVhdGVuIGEgbG90IG9mIGNvb2tpZXMgYW5kIHN0aWxsIGhhdmVuJ3QgZ290dGVuIHRoZSBpdGVtLAogICAgICAgICAgICAjIHRyeSB0byBidXkgaXQgd2l0aCBzdGFtcHMuCiAgICAgICAgICAgIGlmIG5vdCBhbGwob3duZWRfaXRlbXMpIGFuZCBwdWxsX2NvdW50ID49IGJhbGtfbnVtOgogICAgICAgICAgICAgICAgIyBCZWNhdXNlIHRoaXMgd2Fsa3MgdGhlIGFycmF5IGZyb20gZm9yd2FyZCB0byBiYWNrLCBpdCB0cmllcyB0byBidXkgdGhlIDUtc3RhciAKICAgICAgICAgICAgICAgICMgaXRlbSBmaXJzdC4gIFRoaXMgaXMgaW50ZW50aW9uYWwuICBJZiB5b3UgcnVuIG91dCBvZiBzdGFtcHMgYW5kIHN0aWxsIG5lZWQKICAgICAgICAgICAgICAgICMgdG8gYnV5IGNvb2tpZXMsIHRoZW4geW91J2xsIGhhdmUgYmV0dGVyIG9kZHMgcHVsbGluZyB0aGUgMyBvciA0LXN0YXIgaXRlbXMuCiAgICAgICAgICAgICAgICBmb3IgaSBpbiByYW5nZSgxMCk6CiAgICAgICAgICAgICAgICAgICAgaWYgbm90IG93bmVkX2l0ZW1zW2ldOgogICAgICAgICAgICAgICAgICAgICAgICAjIDUtc3RhciBpdGVtCiAgICAgICAgICAgICAgICAgICAgICAgIGlmIGNvb2tpZV9vZGRzW2ldIDwgNC4wOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgc3RhbXBfY291bnQgPj0gMTAwOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG93bmVkX2l0ZW1zW2ldID0gVHJ1ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0YW1wX2NvdW50IC09IDEwMAogICAgICAgICAgICAgICAgICAgICAgICAjIDQtc3RhciBpdGVtCiAgICAgICAgICAgICAgICAgICAgICAgIGVsaWYgY29va2llX29kZHNbaV0gPCAxMC4wOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgc3RhbXBfY291bnQgPj0gNTA6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3duZWRfaXRlbXNbaV0gPSBUcnVlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhbXBfY291bnQgLT0gNTAKICAgICAgICAgICAgICAgICAgICAgICAgIyAzLXN0YXIgaXRlbQogICAgICAgICAgICAgICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgc3RhbXBfY291bnQgPj0gMTA6CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3duZWRfaXRlbXNbaV0gPSBUcnVlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhbXBfY291bnQgLT0gMTAKICAgICAgICAgICAgCiAgICAgICAgcHVsbF9zdGF0c1tjXSA9IHB1bGxfY291bnQKCiAgICByZXR1cm4gc3VtKHB1bGxfc3RhdHMpCgoKbWF4X2JveF9wdWxsX2JhbGtfcG9pbnQgPSAxMgpyZXN1bHRzID0gW1swXSAqIE5VTV9PRl9TSU1VTEFUSU9OUyBmb3IgXyBpbiByYW5nZShtYXhfYm94X3B1bGxfYmFsa19wb2ludCldCgpmb3IgaSBpbiByYW5nZShOVU1fT0ZfU0lNVUxBVElPTlMpOgogICAgZm9yIGIgaW4gcmFuZ2UobWF4X2JveF9wdWxsX2JhbGtfcG9pbnQpOgogICAgICAgIHJlc3VsdHNbYl1baV0gPSBjb29raWVfc2ltdWxhdGlvbl9hbGxfaXRlbXNfd2l0aF9zdGFtcHMoYmFsa19udW09KGIrMSkqNSkKCmZvciBiIGluIHJhbmdlKG1heF9ib3hfcHVsbF9iYWxrX3BvaW50KToKICAgIHJlc3VsdHNbYl0gPSBzb3J0ZWQocmVzdWx0c1tiXSkKYm90dG9tX2ZpdmVfaW5kZXggPSBtYXgoMSwgaW50KE5VTV9PRl9TSU1VTEFUSU9OUyAqIDAuMDUpKQp0b3BfZml2ZV9pbmRleCA9IE5VTV9PRl9TSU1VTEFUSU9OUyAtIGJvdHRvbV9maXZlX2luZGV4IC0gMQptZWRpYW5faW5kZXggPSBpbnQoTlVNX09GX1NJTVVMQVRJT05TIC8gMikKCnByaW50KGYnQ3VtdWxhdGl2ZSBjb29raWVzIHB1cmNoYXNlZCB0byBjb21wbGV0ZSB7TlVNX09GX0NPT0tJRV9UWVBFU30gY29sbGVjdGlvbnMuIFNpbXVsYXRlZCB7TlVNX09GX1NJTVVMQVRJT05TfSB0aW1lcy4nKQpwcmludCgnfCBVc2Ugc3RhbXBzIGFmdGVyIFggcHVsbHMgfCAgQmVzdCAgfCAgIDUlICAgfCBNZWRpYW4gfCAgIDk1JSAgfCAgV29yc3QgfCBBdmVyYWdlIHwnKQpmb3IgYiBpbiByYW5nZShtYXhfYm94X3B1bGxfYmFsa19wb2ludCk6CiAgICBwcmludChmJ3wgICAgICAgICAgICB7KGIrMSkqNTowMmR9ICAgICAgICAgICAgfCAge3Jlc3VsdHNbYl1bMF19ICB8ICB7cmVzdWx0c1tiXVtib3R0b21fZml2ZV9pbmRleF19ICB8ICB7cmVzdWx0c1tiXVttZWRpYW5faW5kZXhdfSAgfCAge3Jlc3VsdHNbYl1bdG9wX2ZpdmVfaW5kZXhdfSAgfCAge3Jlc3VsdHNbYl1bLTFdfSAgfCAge3JvdW5kKHN1bShyZXN1bHRzW2JdKSAvIE5VTV9PRl9TSU1VTEFUSU9OUyl9ICAgfCcpCg==