Guy Scrum did some excellent work with the original Jupyter notebook establishing good strategies for winking isle, which can be found at https://nbviewer.jupyter.org/gist/anonymous/36587a02c35813bfb8883e485fe1335c. However, there was a small mistake in the outcome for Wait by the well, and the given strategy isn't quite optimal. This builds on that prior work to close the gap and find a truly optimal solution.
The primary attribute that we've got to worry about is Fasting and Meditating to a Foolish End, a.k.a. FAM. Every action we're basically given a choice of how much FAM we want to gamble. If we make our choice codified, then every value of FAM can point to two other values: one for success and one for failure. Let $x$ be the current level of FAM, $p$ be the probability of a successful gamble, $\Delta x_s$ be the amount gained on success, and $\Delta x_f$ be the amount lost on failure. Then the expected number of actions to completion is $$ E(x) = 1 + p E(x+\Delta x_s) + (1-p) E(x-\Delta x_f). $$ The expected number of actions is zero if $x$ is greater than or equal to the target value (either 10k or 60k, depending on what you're going for). This turns out to be a giant system of linear equations and we can solve it exactly using scipy and sparse matricies.
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
from scipy import sparse
from scipy.sparse import linalg as sparse_linalg
matplotlib.rcParams['figure.figsize'] = [12, 9]
The code here has been tweaked to have the proper support for the "wait" failure case, which does not change FAM.
well_actions = {
'wait': {
'requires': 77,
'success': 50,
'failure': 0,
'odds': 0.9,
},
'watch': {
'requires': 77,
'success': 500,
'failure': -385,
'odds': 0.7,
},
'circle': {
'requires': 100,
'success': 1000,
'failure': -770,
'odds': 0.6,
},
'inhale': {
'requires': 777,
'success': 2500,
'failure': -1925,
'odds': 0.5,
},
'look edge': {
'requires': 1000,
'success': 5000,
'failure': -3850,
'odds': 0.4,
},
'look up': {
'requires': 7777,
'success': 10000,
'failure': -7000,
'odds': 0.3,
},
}
print("Expected FAM per action:")
for name, level in well_actions.items():
level['avg'] = level['success']*level['odds'] + level['failure']*(1-level['odds'])
print(" ", name, level['avg'])
def matrix_for_strategy(strategy, target=10000):
m = sparse.coo_matrix((target,target))
# Setup diagonal
rows = list(range(target))
cols = list(range(target))
data = [1.0] * target
# Setup prepare actions, which are special because they clamp at multiples of 11
new_rows = np.arange(77)
rows.extend(new_rows)
cols.extend(new_rows - (new_rows%11) + 11)
data.extend(-np.ones_like(new_rows))
# Do everything else
last_level = target
for level in sorted(strategy.keys())[::-1]:
if level >= target:
continue
action = well_actions[strategy[level]]
odds = action['odds']
success = action['success']
failure = action['failure']
new_cols = np.arange(level+success, min(last_level+success, target))
cols.extend(new_cols)
rows.extend(new_cols - success)
data.extend(np.full(new_cols.shape[0], -odds))
new_rows = np.arange(level, last_level)
rows.extend(new_rows)
cols.extend(np.clip(new_rows + failure, 0, target - 1))
data.extend(np.full(new_rows.shape[0], -(1-odds)))
last_level = level
# Sparse solving needs CSC or CSR format to be efficient, and throws warnings otherwise
return sparse.coo_matrix((data, (rows, cols))).tocsc()
def solve_strategy(strategy, target=10000):
m = matrix_for_strategy(strategy, target)
return sparse_linalg.spsolve(m, np.ones(target))
def perturb_solution(strategy, solution):
target = solution.shape[0]
# E(x) = 1 ...
actions = well_actions.keys()
result = np.ones((len(actions), target))
for i, action in enumerate(actions):
odds = well_actions[action]['odds']
success = well_actions[action]['success']
failure = well_actions[action]['failure']
value = result[i]
# Find the difference to determine better/worse
value -= solution
# ... + p * E(x+success) ...
# We're using "target-success" instead of just -success to handle cases where success (or failure) are 0.
value[:target-success] += odds * solution[success:]
# ... + (1-p) * E(x+failure)
value += (1 - odds) * np.concatenate((np.full(-failure, solution[0]), solution[:target+failure]))
# Mask off beginning, we can't change the prepare steps
value[:77] = 0
return result
def steepest_descent(strategy, solution, constrained=True):
"""Generate a better strategy based on the passed strategy."""
actions = list(well_actions.keys())
result = perturb_solution(strategy, solution)
if constrained: # Overwrite selected results so that they won't be chosen
for i, x in enumerate(actions):
result[i,:well_actions[x]['requires']] = 1
best_actions = result.argmin(axis=0)
best_actions[:77] = -1 # Create artifical zone for prepare steps, diff will drop this.
# Add one because nonzero finds the index before the change
change_indices = np.diff(best_actions, append=False).nonzero()[0] + 1
return dict((x, actions[best_actions[x]]) for x in change_indices)
def print_perturb_ranges(strategy, solution):
result = perturb_solution(strategy, solution)
# We need a reasonably high tolerance for round-off errors
changes = np.diff(result < -1e-12, append=False)
print("Potential improvement ranges for strategy: %s" % strategy)
for i, action in enumerate(well_actions.keys()):
row = changes[i]
# Add one because nonzero finds the index before the change, reshape to pair up elements.
print("%s: " % action + ", ".join("%d-%d" % (x, y) for (x, y) in (row.nonzero()[0] + 1).reshape(-1, 2)))
Expected FAM per action: wait 45.0 watch 234.5 circle 292.0 inhale 287.5 look edge -310.0 look up -1900.0
Now let's start from a very basic strategy: Only circling. This isn't even valid (you can't start circling until 100 FAM), but that's OK. Once we have a solution, we'll perturb that solution by checking to see what happens if a single element in that strategy is changed. By doing this for all points, and all strategies, we can automatically create a new strategy based on the potential best strategy at each point.
strategy = {77: 'circle'}
solution = solve_strategy(strategy, 10**4)
print('Current cost:', solution[0])
new_strategy = steepest_descent(strategy, solution, constrained=False)
print(new_strategy)
Current cost: 49.947501922708284 {77: 'look up', 1155: 'watch', 1160: 'circle', 1166: 'watch', 1606: 'circle', 1832: 'watch', 1850: 'circle', 1890: 'watch', 1930: 'circle', 1947: 'watch', 1950: 'circle', 1956: 'watch', 2332: 'circle', 2350: 'watch', 2376: 'circle', 2385: 'watch', 2387: 'circle', 2590: 'watch', 2620: 'circle', 2670: 'watch', 2675: 'circle', 2682: 'watch', 2685: 'circle', 2695: 'watch', 2700: 'circle', 2740: 'watch', 2770: 'circle', 2775: 'watch', 2780: 'circle', 2810: 'watch', 2850: 'circle', 2890: 'watch', 2894: 'circle', 2899: 'watch', 2916: 'circle', 2923: 'watch', 2930: 'circle', 3040: 'watch', 3080: 'circle', 3280: 'watch', 3310: 'circle', 3360: 'watch', 3390: 'circle', 3465: 'watch', 3470: 'circle', 3510: 'watch', 3540: 'circle', 3580: 'watch', 3620: 'circle', 3695: 'watch', 3700: 'circle', 4270: 'watch', 4310: 'circle', 4360: 'watch', 4364: 'circle', 4370: 'watch', 4390: 'circle', 4465: 'watch', 4470: 'circle', 4500: 'watch', 4540: 'circle', 4590: 'watch', 4620: 'circle', 5270: 'watch', 5310: 'circle', 5385: 'watch', 5390: 'circle', 5465: 'watch', 5470: 'circle', 5500: 'inhale', 5540: 'circle', 5580: 'inhale', 5620: 'circle', 5695: 'inhale', 5700: 'circle', 5705: 'inhale', 5770: 'circle', 6270: 'watch', 6310: 'circle', 6385: 'watch', 6390: 'circle', 6465: 'watch', 6470: 'circle', 6500: 'inhale', 6770: 'circle', 6810: 'inhale', 6850: 'circle', 7270: 'watch', 7310: 'circle', 7465: 'watch', 7470: 'circle', 7500: 'inhale', 7850: 'circle', 7855: 'inhale', 8000: 'circle', 8500: 'watch', 8770: 'circle', 8810: 'watch', 8850: 'circle', 8925: 'watch', 8950: 'wait', 9000: 'circle', 9500: 'watch', 9950: 'wait'}
Of course, changing the strategy that radically changes what's optimal. We have to iterate several times, and even then it circles instead of converging on a single solution, due to round-off error. I postulate that among these (very complicated) local minimums is also the true global minimum, if constraints aren't taken into account.
for i in range(10):
solution = solve_strategy(new_strategy, 10**4)
print('Current cost:', solution[0])
new_strategy = steepest_descent(new_strategy, solution, constrained=False)
print_perturb_ranges(new_strategy, solution)
Current cost: 26.666666666666654 Current cost: 26.666666666666664 Current cost: 26.666666666666657 Current cost: 26.66666666666667 Current cost: 26.66666666666667 Current cost: 26.66666666666665 Current cost: 26.666666666666682 Current cost: 26.666666666666664 Current cost: 26.666666666666657 Current cost: 26.666666666666654 Potential improvement ranges for strategy: {77: 'look up', 1002: 'circle', 1655: 'watch', 1695: 'circle', 1952: 'wait', 1991: 'inhale', 2950: 'circle', 3352: 'inhale', 3540: 'watch', 3620: 'inhale', 3655: 'watch', 3695: 'inhale', 3770: 'watch', 3775: 'inhale', 3810: 'watch', 3852: 'inhale', 3916: 'look edge', 3925: 'inhale', 3927: 'look edge', 4725: 'inhale', 4841: 'circle', 4850: 'inhale', 4852: 'circle', 4950: 'inhale', 5000: 'look edge', 5145: 'inhale', 5777: 'circle', 5810: 'inhale', 6766: 'circle', 6770: 'inhale', 6771: 'circle', 6775: 'inhale', 6777: 'circle', 6784: 'inhale', 6787: 'circle', 6788: 'inhale', 6789: 'circle', 6790: 'inhale', 6791: 'circle', 6792: 'inhale', 6793: 'circle', 6801: 'inhale', 6802: 'circle', 6806: 'inhale', 6807: 'circle', 6810: 'inhale', 6811: 'circle', 6815: 'inhale', 6816: 'circle', 6819: 'inhale', 6820: 'circle', 6824: 'inhale', 6825: 'circle', 6827: 'inhale', 6828: 'circle', 6832: 'inhale', 6833: 'circle', 6835: 'inhale', 6837: 'circle', 6839: 'inhale', 6840: 'circle', 6847: 'inhale', 6853: 'circle', 6861: 'inhale', 6862: 'circle', 6864: 'inhale', 6865: 'circle', 6870: 'inhale', 6871: 'circle', 6872: 'inhale', 6873: 'circle', 6875: 'inhale', 8040: 'circle', 9450: 'watch', 9950: 'wait'} wait: watch: circle: inhale: look edge: look up:
However, this strategy is not actually valid, because of constraints: 100 FAM for circle to be used, 1000 FAM for inhale, etc. So, the strategy must be heavily modified away from what is theoretically optimal. We'll just start from "watch," and then use a constrained version of the single-step routine.
new_strategy = {77: 'watch'}
for i in range(10):
solution = solve_strategy(new_strategy, 10**4)
print('Current cost:', solution[0])
new_strategy = steepest_descent(new_strategy, solution, constrained=True)
print_perturb_ranges(new_strategy, solution)
Current cost: 54.187966736108145 Current cost: 48.36419043532721 Current cost: 47.97920213604451 Current cost: 47.89292554205005 Current cost: 47.869273100434434 Current cost: 47.86824987883867 Current cost: 47.86824987883867 Current cost: 47.86824987883867 Current cost: 47.86824987883867 Current cost: 47.86824987883867 Potential improvement ranges for strategy: {77: 'watch', 100: 'circle', 362: 'wait', 429: 'watch', 435: 'wait', 440: 'watch', 1606: 'circle', 5000: 'inhale', 5040: 'circle', 7000: 'inhale', 7040: 'circle', 7195: 'inhale', 7220: 'circle', 7465: 'inhale', 7770: 'circle', 9450: 'watch', 9950: 'wait'} wait: watch: circle: 77-100 inhale: 77-451 look edge: 77-671 look up: 77-870
This is a complicated strategy (although much better than the unconstrained version). It gets even more complicated when we solve for the 60k case, because the finicky inhale/circle pairs don't generalize.
new_strategy = {77: 'watch'}
for i in range(10):
solution = solve_strategy(new_strategy, 6*10**4)
print('Current cost:', solution[0])
new_strategy = steepest_descent(new_strategy, solution, constrained=True)
print_perturb_ranges(new_strategy, solution)
Current cost: 267.2969173941006 Current cost: 219.2260772853663 Current cost: 219.05892813357087 Current cost: 218.99283425973465 Current cost: 218.9700095129803 Current cost: 218.96581822992061 Current cost: 218.96540992675114 Current cost: 218.9653711354798 Current cost: 218.96537113547993 Current cost: 218.96537113547993 Potential improvement ranges for strategy: {77: 'watch', 100: 'circle', 362: 'wait', 429: 'watch', 435: 'wait', 440: 'watch', 1617: 'circle', 47500: 'inhale', 47540: 'circle', 49505: 'inhale', 49540: 'circle', 50000: 'inhale', 50040: 'circle', 50195: 'inhale', 50200: 'circle', 50215: 'inhale', 50220: 'circle', 50235: 'inhale', 50270: 'circle', 51005: 'inhale', 51006: 'circle', 51014: 'inhale', 51015: 'circle', 51021: 'inhale', 51022: 'circle', 51023: 'inhale', 51024: 'circle', 51025: 'inhale', 51027: 'circle', 51030: 'inhale', 51032: 'circle', 51035: 'inhale', 51036: 'circle', 51038: 'inhale', 51039: 'circle', 51950: 'inhale', 51965: 'circle', 52000: 'inhale', 52040: 'circle', 52465: 'inhale', 52540: 'circle', 52550: 'inhale', 52580: 'circle', 52695: 'inhale', 52770: 'circle', 53235: 'inhale', 53237: 'circle', 53238: 'inhale', 53239: 'circle', 53247: 'inhale', 53248: 'circle', 53251: 'inhale', 53252: 'circle', 53258: 'inhale', 53259: 'circle', 53465: 'inhale', 53467: 'circle', 53468: 'inhale', 53469: 'circle', 53470: 'inhale', 53471: 'circle', 53472: 'inhale', 53473: 'circle', 53474: 'inhale', 53477: 'circle', 53478: 'inhale', 53479: 'circle', 53481: 'inhale', 53483: 'circle', 53488: 'inhale', 53489: 'circle', 53504: 'inhale', 53505: 'circle', 53506: 'inhale', 53508: 'circle', 53511: 'inhale', 53512: 'circle', 53513: 'inhale', 53514: 'circle', 53516: 'inhale', 53517: 'circle', 53518: 'inhale', 53519: 'circle', 53521: 'inhale', 53523: 'circle', 53530: 'inhale', 53531: 'circle', 53534: 'inhale', 53535: 'circle', 53538: 'inhale', 53540: 'circle', 53950: 'inhale', 53965: 'circle', 54006: 'inhale', 54007: 'circle', 54009: 'inhale', 54010: 'circle', 54011: 'inhale', 54012: 'circle', 54013: 'inhale', 54015: 'circle', 54016: 'inhale', 54018: 'circle', 54020: 'inhale', 54021: 'circle', 54023: 'inhale', 54024: 'circle', 54026: 'inhale', 54027: 'circle', 54235: 'inhale', 54237: 'circle', 54238: 'inhale', 54239: 'circle', 54240: 'inhale', 54241: 'circle', 54242: 'inhale', 54245: 'circle', 54249: 'inhale', 54250: 'circle', 54251: 'inhale', 54252: 'circle', 54253: 'inhale', 54254: 'circle', 54450: 'inhale', 54490: 'circle', 54500: 'inhale', 54540: 'circle', 54695: 'inhale', 54720: 'circle', 54722: 'inhale', 54723: 'circle', 54727: 'inhale', 54729: 'circle', 54734: 'inhale', 54735: 'circle', 54950: 'inhale', 55080: 'circle', 55145: 'inhale', 55270: 'circle', 55492: 'inhale', 55493: 'circle', 55497: 'inhale', 55498: 'circle', 55504: 'inhale', 55505: 'circle', 55720: 'inhale', 55722: 'circle', 55724: 'inhale', 55725: 'circle', 55733: 'inhale', 55734: 'circle', 55736: 'inhale', 55737: 'circle', 55740: 'inhale', 55742: 'circle', 55746: 'inhale', 55751: 'circle', 55752: 'inhale', 55755: 'circle', 55758: 'inhale', 55759: 'circle', 55925: 'inhale', 55929: 'circle', 55931: 'inhale', 55932: 'circle', 55936: 'inhale', 55937: 'circle', 55938: 'inhale', 55940: 'circle', 55943: 'inhale', 55945: 'circle', 55946: 'inhale', 55949: 'circle', 55955: 'inhale', 55956: 'circle', 55958: 'inhale', 55961: 'circle', 55964: 'inhale', 55969: 'circle', 55972: 'inhale', 55973: 'circle', 55974: 'inhale', 55975: 'circle', 55976: 'inhale', 55979: 'circle', 55981: 'inhale', 55983: 'circle', 55985: 'inhale', 55986: 'circle', 55988: 'inhale', 55989: 'circle', 56003: 'inhale', 56004: 'circle', 56006: 'inhale', 56008: 'circle', 56010: 'inhale', 56011: 'circle', 56014: 'inhale', 56016: 'circle', 56017: 'inhale', 56019: 'circle', 56021: 'inhale', 56022: 'circle', 56023: 'inhale', 56024: 'circle', 56030: 'inhale', 56031: 'circle', 56032: 'inhale', 56035: 'circle', 56036: 'inhale', 56040: 'circle', 56450: 'inhale', 56465: 'circle', 56491: 'inhale', 56493: 'circle', 56494: 'inhale', 56495: 'circle', 56497: 'inhale', 56499: 'circle', 56502: 'inhale', 56503: 'circle', 56505: 'inhale', 56507: 'circle', 56508: 'inhale', 56509: 'circle', 56510: 'inhale', 56511: 'circle', 56514: 'inhale', 56517: 'circle', 56520: 'inhale', 56521: 'circle', 56523: 'inhale', 56525: 'circle', 56528: 'inhale', 56529: 'circle', 56695: 'inhale', 56696: 'circle', 56697: 'inhale', 56698: 'circle', 56699: 'inhale', 56700: 'circle', 56701: 'inhale', 56702: 'circle', 56704: 'inhale', 56705: 'circle', 56706: 'inhale', 56707: 'circle', 56712: 'inhale', 56715: 'circle', 56716: 'inhale', 56719: 'circle', 56721: 'inhale', 56722: 'circle', 56724: 'inhale', 56727: 'circle', 56731: 'inhale', 56732: 'circle', 56734: 'inhale', 56736: 'circle', 56738: 'inhale', 56739: 'circle', 56742: 'inhale', 56744: 'circle', 56745: 'inhale', 56746: 'circle', 56747: 'inhale', 56749: 'circle', 56752: 'inhale', 56754: 'circle', 56757: 'inhale', 56758: 'circle', 56759: 'inhale', 56760: 'circle', 56925: 'inhale', 56990: 'circle', 57000: 'inhale', 57040: 'circle', 57195: 'inhale', 57220: 'circle', 57450: 'inhale', 57770: 'circle', 59450: 'watch', 59950: 'wait'} wait: watch: circle: 77-100 inhale: 77-451 look edge: 77-680 look up: 77-1055
Instead, we can simplify the strategy to something that's much easier to remember without losing much of its value. This also generalizes to the 60k version.
strategy = {77: 'watch', 100: 'circle', 396: 'watch', 1606: 'circle', 9450: 'watch', 9950: 'wait'}
solution = solve_strategy(strategy, 10**4)
print('Current cost:', solution[0])
print_perturb_ranges(strategy, solution)
Current cost: 48.048751654356025 Potential improvement ranges for strategy: {77: 'watch', 100: 'circle', 396: 'watch', 1606: 'circle', 9450: 'watch', 9950: 'wait'} wait: 379-429, 435-440 watch: circle: 77-100 inhale: 77-451, 5500-5540, 5545-5570, 5575-5580, 6005-6030, 6035-6040, 6465-6470, 6475-6490, 6500-6655, 6695-6770, 7000-7040, 7465-7490, 7495-7770 look edge: 77-680 look up: 77-872
Graphically comparing the previous best and the new one, as well as a simplified new one that is almost as optimal: (Note for very vigilant readers: The "watch" range on the simplified strategy extends slightly farther than what was calculated above, to 1617 instead of 1606. This is because that will end up being more optimal for the 60k case, and it only makes the tiniest of differences here.)
def strategies(target):
return {
'old best': {
77: 'watch',
2000: 'circle',
target-500: 'watch',
target-50: 'wait',
},
'optimal': {
77: 'watch',
100: 'circle',
362: 'wait',
429: 'watch',
435: 'wait',
440: 'watch',
1606: 'circle',
target-10000+5000: 'inhale',
target-10000+5040: 'circle',
target-10000+7000: 'inhale',
target-10000+7040: 'circle',
target-10000+7195: 'inhale',
target-10000+7220: 'circle',
target-10000+7465: 'inhale',
target-10000+7770: 'circle',
target-550: 'watch',
target-50: 'wait',
},
'simplified': {
77: 'watch',
100: 'circle',
396: 'watch',
1617: 'circle',
target-550: 'watch',
target-50: 'wait',
},
'optimal60k': {
77: 'watch',
100: 'circle',
362: 'wait',
429: 'watch',
435: 'wait',
440: 'watch',
1617: 'circle',
47500: 'inhale', 47540: 'circle', 49505: 'inhale', 49540: 'circle',
50000: 'inhale', 50040: 'circle', 50195: 'inhale', 50200: 'circle',
50215: 'inhale', 50220: 'circle', 50235: 'inhale', 50270: 'circle',
51005: 'inhale', 51006: 'circle', 51014: 'inhale', 51015: 'circle',
51021: 'inhale', 51022: 'circle', 51023: 'inhale', 51024: 'circle',
51025: 'inhale', 51027: 'circle', 51030: 'inhale', 51032: 'circle',
51035: 'inhale', 51036: 'circle', 51038: 'inhale', 51039: 'circle',
51950: 'inhale', 51965: 'circle', 52000: 'inhale', 52040: 'circle',
52465: 'inhale', 52540: 'circle', 52550: 'inhale', 52580: 'circle',
52695: 'inhale', 52770: 'circle', 53235: 'inhale', 53237: 'circle',
53238: 'inhale', 53239: 'circle', 53247: 'inhale', 53248: 'circle',
53251: 'inhale', 53252: 'circle', 53258: 'inhale', 53259: 'circle',
53465: 'inhale', 53467: 'circle', 53468: 'inhale', 53469: 'circle',
53470: 'inhale', 53471: 'circle', 53472: 'inhale', 53473: 'circle',
53474: 'inhale', 53477: 'circle', 53478: 'inhale', 53479: 'circle',
53481: 'inhale', 53483: 'circle', 53488: 'inhale', 53489: 'circle',
53504: 'inhale', 53505: 'circle', 53506: 'inhale', 53508: 'circle',
53511: 'inhale', 53512: 'circle', 53513: 'inhale', 53514: 'circle',
53516: 'inhale', 53517: 'circle', 53518: 'inhale', 53519: 'circle',
53521: 'inhale', 53523: 'circle', 53530: 'inhale', 53531: 'circle',
53534: 'inhale', 53535: 'circle', 53538: 'inhale', 53540: 'circle',
53950: 'inhale', 53965: 'circle', 54006: 'inhale', 54007: 'circle',
54009: 'inhale', 54010: 'circle', 54011: 'inhale', 54012: 'circle',
54013: 'inhale', 54015: 'circle', 54016: 'inhale', 54018: 'circle',
54020: 'inhale', 54021: 'circle', 54023: 'inhale', 54024: 'circle',
54026: 'inhale', 54027: 'circle', 54235: 'inhale', 54237: 'circle',
54238: 'inhale', 54239: 'circle', 54240: 'inhale', 54241: 'circle',
54242: 'inhale', 54245: 'circle', 54249: 'inhale', 54250: 'circle',
54251: 'inhale', 54252: 'circle', 54253: 'inhale', 54254: 'circle',
54450: 'inhale', 54490: 'circle', 54500: 'inhale', 54540: 'circle',
54695: 'inhale', 54720: 'circle', 54722: 'inhale', 54723: 'circle',
54727: 'inhale', 54729: 'circle', 54734: 'inhale', 54735: 'circle',
54950: 'inhale', 55080: 'circle', 55145: 'inhale', 55270: 'circle',
55492: 'inhale', 55493: 'circle', 55497: 'inhale', 55498: 'circle',
55504: 'inhale', 55505: 'circle', 55720: 'inhale', 55722: 'circle',
55724: 'inhale', 55725: 'circle', 55733: 'inhale', 55734: 'circle',
55736: 'inhale', 55737: 'circle', 55740: 'inhale', 55742: 'circle',
55746: 'inhale', 55751: 'circle', 55752: 'inhale', 55755: 'circle',
55758: 'inhale', 55759: 'circle', 55925: 'inhale', 55929: 'circle',
55931: 'inhale', 55932: 'circle', 55936: 'inhale', 55937: 'circle',
55938: 'inhale', 55940: 'circle', 55943: 'inhale', 55945: 'circle',
55946: 'inhale', 55949: 'circle', 55955: 'inhale', 55956: 'circle',
55958: 'inhale', 55961: 'circle', 55964: 'inhale', 55969: 'circle',
55972: 'inhale', 55973: 'circle', 55974: 'inhale', 55975: 'circle',
55976: 'inhale', 55979: 'circle', 55981: 'inhale', 55983: 'circle',
55985: 'inhale', 55986: 'circle', 55988: 'inhale', 55989: 'circle',
56003: 'inhale', 56004: 'circle', 56006: 'inhale', 56008: 'circle',
56010: 'inhale', 56011: 'circle', 56014: 'inhale', 56016: 'circle',
56017: 'inhale', 56019: 'circle', 56021: 'inhale', 56022: 'circle',
56023: 'inhale', 56024: 'circle', 56030: 'inhale', 56031: 'circle',
56032: 'inhale', 56035: 'circle', 56036: 'inhale', 56040: 'circle',
56450: 'inhale', 56465: 'circle', 56491: 'inhale', 56493: 'circle',
56494: 'inhale', 56495: 'circle', 56497: 'inhale', 56499: 'circle',
56502: 'inhale', 56503: 'circle', 56505: 'inhale', 56507: 'circle',
56508: 'inhale', 56509: 'circle', 56510: 'inhale', 56511: 'circle',
56514: 'inhale', 56517: 'circle', 56520: 'inhale', 56521: 'circle',
56523: 'inhale', 56525: 'circle', 56528: 'inhale', 56529: 'circle',
56695: 'inhale', 56696: 'circle', 56697: 'inhale', 56698: 'circle',
56699: 'inhale', 56700: 'circle', 56701: 'inhale', 56702: 'circle',
56704: 'inhale', 56705: 'circle', 56706: 'inhale', 56707: 'circle',
56712: 'inhale', 56715: 'circle', 56716: 'inhale', 56719: 'circle',
56721: 'inhale', 56722: 'circle', 56724: 'inhale', 56727: 'circle',
56731: 'inhale', 56732: 'circle', 56734: 'inhale', 56736: 'circle',
56738: 'inhale', 56739: 'circle', 56742: 'inhale', 56744: 'circle',
56745: 'inhale', 56746: 'circle', 56747: 'inhale', 56749: 'circle',
56752: 'inhale', 56754: 'circle', 56757: 'inhale', 56758: 'circle',
56759: 'inhale', 56760: 'circle', 56925: 'inhale', 56990: 'circle',
57000: 'inhale', 57040: 'circle', 57195: 'inhale', 57220: 'circle',
57450: 'inhale', 57770: 'circle',
target-550: 'watch',
target-50: 'wait',
},
}
target = 10**4
print("Expected actions to reach {} FAM".format(target))
for i, (name, strategy) in enumerate(list(strategies(target).items())[:3]):
x = solve_strategy(strategy, target)
print(" ", name, x[0])
plt.plot(x, color=['g','b','pink'][i], label=name)
plt.legend()
plt.xlabel("Fasting and Meditating")
plt.ylabel("Expected Actions to Target")
Expected actions to reach 10000 FAM old best 48.51315784943411 optimal 47.86824987883867 simplified 48.04937879687207
Text(0, 0.5, 'Expected Actions to Target')
Because the "prepare" action is properly clamped to multiples of 11 now, most of the values in the matrix have become irrelevant, since they can never actually be reached. This is because all the normal actions increase/decrease FAM by multiples of 5, so all the action happens starting at 77 and every 5 thereafter.
We can see this illustrated by permuting the bounds by 1: It has no effect, except for rounding errors and one case where it crosses from 2 to 3, which is a relevant multiple of 5.
def print_with_adjusted_bounds(strategy, target, adjust=1):
strat_list = list(strategy.items())
widths = [max(len(str(x)), len(y)) for x, y in strat_list]
def print_values(strat_l, label):
print(' '.join('%*d' % (w, x) for w, (x, _) in zip(widths, strat_l)) + ' ' + str(label))
print(' '.join('%*s' % (w, x) for w, (_, x) in zip(widths, strat_list)))
print_values(strat_list, 'Original')
solution = solve_strategy(strategy, target)
orig = solution[0]
for i, (key,_) in enumerate(strat_list):
if key == 77:
continue # Can't move the initial 'watch'
for adj in [adjust, -adjust]:
copy = strat_list[:i] + strat_list[i+1:]
copy.append((key+adj, strategy[key]))
copy.sort()
x = solve_strategy(dict(copy), target)
print_values(copy, '% .8e' % (x[0] - orig))
print_with_adjusted_bounds(strategies(10**4)['optimal'], 10**4)
watch circle wait watch wait watch circle inhale circle inhale circle inhale circle inhale circle watch wait 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 Original 77 101 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 99 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 100 363 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 5.92035983e-06 77 100 361 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 430 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 428 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 -1.42108547e-14 77 100 362 429 436 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 434 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 435 441 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 435 439 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 435 440 1607 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 435 440 1605 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 435 440 1606 5001 5040 7000 7040 7195 7220 7465 7770 9450 9950 -2.13162821e-14 77 100 362 429 435 440 1606 4999 5040 7000 7040 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 435 440 1606 5000 5041 7000 7040 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 435 440 1606 5000 5039 7000 7040 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 435 440 1606 5000 5040 7001 7040 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 435 440 1606 5000 5040 6999 7040 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 435 440 1606 5000 5040 7000 7041 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 435 440 1606 5000 5040 7000 7039 7195 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 435 440 1606 5000 5040 7000 7040 7196 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 435 440 1606 5000 5040 7000 7040 7194 7220 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7221 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7219 7465 7770 9450 9950 0.00000000e+00 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7466 7770 9450 9950 0.00000000e+00 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7464 7770 9450 9950 0.00000000e+00 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7771 9450 9950 -2.13162821e-14 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7769 9450 9950 0.00000000e+00 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9451 9950 -2.13162821e-14 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9449 9950 0.00000000e+00 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9951 -2.13162821e-14 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9949 0.00000000e+00
Using the same technique, but permuting by 5, shows that we have found a (local) minimum. (With the exception of lowering when we start circling, which would be an improvement but is not allowed.) It also cross-validates that the minimum-finding technique is working correctly.
print_with_adjusted_bounds(strategies(10**4)['optimal'], 10**4, 5)
watch circle wait watch wait watch circle inhale circle inhale circle inhale circle inhale circle watch wait 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 Original 77 105 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 4.09541452e-04 77 95 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 -1.32179421e-04 77 100 367 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 5.92035983e-06 77 100 357 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 2.72056928e-05 77 100 362 434 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 8.81449028e-04 77 100 362 424 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 9.38220487e-04 77 100 362 429 440 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 6.75747026e-05 77 100 362 429 430 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 8.81449028e-04 77 100 362 429 435 445 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 2.50134566e-04 77 100 362 429 435 435 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 6.75747026e-05 77 100 362 429 435 440 1611 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 1.63625911e-04 77 100 362 429 435 440 1601 5000 5040 7000 7040 7195 7220 7465 7770 9450 9950 1.23377803e-04 77 100 362 429 435 440 1606 5005 5040 7000 7040 7195 7220 7465 7770 9450 9950 5.98200779e-05 77 100 362 429 435 440 1606 4995 5040 7000 7040 7195 7220 7465 7770 9450 9950 1.28261780e-02 77 100 362 429 435 440 1606 5000 5045 7000 7040 7195 7220 7465 7770 9450 9950 5.42760281e-04 77 100 362 429 435 440 1606 5000 5035 7000 7040 7195 7220 7465 7770 9450 9950 3.79969594e-03 77 100 362 429 435 440 1606 5000 5040 7005 7040 7195 7220 7465 7770 9450 9950 1.50553584e-04 77 100 362 429 435 440 1606 5000 5040 6995 7040 7195 7220 7465 7770 9450 9950 2.38963525e-03 77 100 362 429 435 440 1606 5000 5040 7000 7045 7195 7220 7465 7770 9450 9950 4.86580985e-04 77 100 362 429 435 440 1606 5000 5040 7000 7035 7195 7220 7465 7770 9450 9950 1.04136102e-03 77 100 362 429 435 440 1606 5000 5040 7000 7040 7200 7220 7465 7770 9450 9950 8.30932067e-06 77 100 362 429 435 440 1606 5000 5040 7000 7040 7190 7220 7465 7770 9450 9950 1.62274515e-04 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7225 7465 7770 9450 9950 2.23010272e-03 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7215 7465 7770 9450 9950 1.14757901e-04 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7470 7770 9450 9950 5.00710556e-06 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7460 7770 9450 9950 5.70716303e-07 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7775 9450 9950 1.62812538e-04 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7765 9450 9950 5.38133779e-03 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9455 9950 5.23871268e-03 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9445 9950 2.06672691e-04 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9955 7.17347464e-03 77 100 362 429 435 440 1606 5000 5040 7000 7040 7195 7220 7465 7770 9450 9945 6.20907148e-04
Validating that the same solutions are optimal/near-optimal at 60k: As we can see, the optimal solution for 60k is considerably more complicated.
target = 6 * 10**4
strategy = strategies(target)['optimal60k']
solution = solve_strategy(strategy, target)
print('Current cost:', solution[0])
print_perturb_ranges(strategy, solution)
print()
strategy = strategies(target)['simplified']
solution = solve_strategy(strategy, target)
print('Current cost:', solution[0])
print_perturb_ranges(strategy, solution)
Current cost: 218.96537113547984 Potential improvement ranges for strategy: {77: 'watch', 100: 'circle', 362: 'wait', 429: 'watch', 435: 'wait', 440: 'watch', 1617: 'circle', 47500: 'inhale', 47540: 'circle', 49505: 'inhale', 49540: 'circle', 50000: 'inhale', 50040: 'circle', 50195: 'inhale', 50200: 'circle', 50215: 'inhale', 50220: 'circle', 50235: 'inhale', 50270: 'circle', 51005: 'inhale', 51006: 'circle', 51014: 'inhale', 51015: 'circle', 51021: 'inhale', 51022: 'circle', 51023: 'inhale', 51024: 'circle', 51025: 'inhale', 51027: 'circle', 51030: 'inhale', 51032: 'circle', 51035: 'inhale', 51036: 'circle', 51038: 'inhale', 51039: 'circle', 51950: 'inhale', 51965: 'circle', 52000: 'inhale', 52040: 'circle', 52465: 'inhale', 52540: 'circle', 52550: 'inhale', 52580: 'circle', 52695: 'inhale', 52770: 'circle', 53235: 'inhale', 53237: 'circle', 53238: 'inhale', 53239: 'circle', 53247: 'inhale', 53248: 'circle', 53251: 'inhale', 53252: 'circle', 53258: 'inhale', 53259: 'circle', 53465: 'inhale', 53467: 'circle', 53468: 'inhale', 53469: 'circle', 53470: 'inhale', 53471: 'circle', 53472: 'inhale', 53473: 'circle', 53474: 'inhale', 53477: 'circle', 53478: 'inhale', 53479: 'circle', 53481: 'inhale', 53483: 'circle', 53488: 'inhale', 53489: 'circle', 53504: 'inhale', 53505: 'circle', 53506: 'inhale', 53508: 'circle', 53511: 'inhale', 53512: 'circle', 53513: 'inhale', 53514: 'circle', 53516: 'inhale', 53517: 'circle', 53518: 'inhale', 53519: 'circle', 53521: 'inhale', 53523: 'circle', 53530: 'inhale', 53531: 'circle', 53534: 'inhale', 53535: 'circle', 53538: 'inhale', 53540: 'circle', 53950: 'inhale', 53965: 'circle', 54006: 'inhale', 54007: 'circle', 54009: 'inhale', 54010: 'circle', 54011: 'inhale', 54012: 'circle', 54013: 'inhale', 54015: 'circle', 54016: 'inhale', 54018: 'circle', 54020: 'inhale', 54021: 'circle', 54023: 'inhale', 54024: 'circle', 54026: 'inhale', 54027: 'circle', 54235: 'inhale', 54237: 'circle', 54238: 'inhale', 54239: 'circle', 54240: 'inhale', 54241: 'circle', 54242: 'inhale', 54245: 'circle', 54249: 'inhale', 54250: 'circle', 54251: 'inhale', 54252: 'circle', 54253: 'inhale', 54254: 'circle', 54450: 'inhale', 54490: 'circle', 54500: 'inhale', 54540: 'circle', 54695: 'inhale', 54720: 'circle', 54722: 'inhale', 54723: 'circle', 54727: 'inhale', 54729: 'circle', 54734: 'inhale', 54735: 'circle', 54950: 'inhale', 55080: 'circle', 55145: 'inhale', 55270: 'circle', 55492: 'inhale', 55493: 'circle', 55497: 'inhale', 55498: 'circle', 55504: 'inhale', 55505: 'circle', 55720: 'inhale', 55722: 'circle', 55724: 'inhale', 55725: 'circle', 55733: 'inhale', 55734: 'circle', 55736: 'inhale', 55737: 'circle', 55740: 'inhale', 55742: 'circle', 55746: 'inhale', 55751: 'circle', 55752: 'inhale', 55755: 'circle', 55758: 'inhale', 55759: 'circle', 55925: 'inhale', 55929: 'circle', 55931: 'inhale', 55932: 'circle', 55936: 'inhale', 55937: 'circle', 55938: 'inhale', 55940: 'circle', 55943: 'inhale', 55945: 'circle', 55946: 'inhale', 55949: 'circle', 55955: 'inhale', 55956: 'circle', 55958: 'inhale', 55961: 'circle', 55964: 'inhale', 55969: 'circle', 55972: 'inhale', 55973: 'circle', 55974: 'inhale', 55975: 'circle', 55976: 'inhale', 55979: 'circle', 55981: 'inhale', 55983: 'circle', 55985: 'inhale', 55986: 'circle', 55988: 'inhale', 55989: 'circle', 56003: 'inhale', 56004: 'circle', 56006: 'inhale', 56008: 'circle', 56010: 'inhale', 56011: 'circle', 56014: 'inhale', 56016: 'circle', 56017: 'inhale', 56019: 'circle', 56021: 'inhale', 56022: 'circle', 56023: 'inhale', 56024: 'circle', 56030: 'inhale', 56031: 'circle', 56032: 'inhale', 56035: 'circle', 56036: 'inhale', 56040: 'circle', 56450: 'inhale', 56465: 'circle', 56491: 'inhale', 56493: 'circle', 56494: 'inhale', 56495: 'circle', 56497: 'inhale', 56499: 'circle', 56502: 'inhale', 56503: 'circle', 56505: 'inhale', 56507: 'circle', 56508: 'inhale', 56509: 'circle', 56510: 'inhale', 56511: 'circle', 56514: 'inhale', 56517: 'circle', 56520: 'inhale', 56521: 'circle', 56523: 'inhale', 56525: 'circle', 56528: 'inhale', 56529: 'circle', 56695: 'inhale', 56696: 'circle', 56697: 'inhale', 56698: 'circle', 56699: 'inhale', 56700: 'circle', 56701: 'inhale', 56702: 'circle', 56704: 'inhale', 56705: 'circle', 56706: 'inhale', 56707: 'circle', 56712: 'inhale', 56715: 'circle', 56716: 'inhale', 56719: 'circle', 56721: 'inhale', 56722: 'circle', 56724: 'inhale', 56727: 'circle', 56731: 'inhale', 56732: 'circle', 56734: 'inhale', 56736: 'circle', 56738: 'inhale', 56739: 'circle', 56742: 'inhale', 56744: 'circle', 56745: 'inhale', 56746: 'circle', 56747: 'inhale', 56749: 'circle', 56752: 'inhale', 56754: 'circle', 56757: 'inhale', 56758: 'circle', 56759: 'inhale', 56760: 'circle', 56925: 'inhale', 56990: 'circle', 57000: 'inhale', 57040: 'circle', 57195: 'inhale', 57220: 'circle', 57450: 'inhale', 57770: 'circle', 59450: 'watch', 59950: 'wait'} wait: watch: circle: 77-100 inhale: 77-451 look edge: 77-680 look up: 77-1055 Current cost: 219.2733885543293 Potential improvement ranges for strategy: {77: 'watch', 100: 'circle', 396: 'watch', 1617: 'circle', 59450: 'watch', 59950: 'wait'} wait: 379-429, 435-440 watch: circle: 77-100 inhale: 77-451, 44885-44890, 45115-45120, 45655-45660, 45855-45860, 45885-45890, 46115-46120, 46345-46350, 46625-46630, 46855-46860, 46865-46870, 46885-46890, 47085-47090, 47095-47100, 47105-47110, 47115-47120, 47315-47320, 47325-47330, 47345-47350, 47575-47580, 47625-47630, 47645-47650, 47855-47860, 47865-47870, 47875-47880, 47885-47890, 48085-48090, 48095-48100, 48105-48110, 48115-48120, 48315-48320, 48325-48330, 48335-48340, 48345-48350, 48545-48550, 48555-48560, 48575-48580, 48585-48590, 48625-48630, 48645-48650, 48855-48860, 48865-48870, 48875-48880, 48885-48890, 49085-49090, 49095-49100, 49105-49110, 49115-49120, 49315-49320, 49325-49340, 49345-49350, 49355-49360, 49545-49550, 49555-49560, 49565-49570, 49575-49580, 49585-49590, 49625-49630, 49645-49650, 49775-49780, 49805-49810, 49815-49820, 49855-49860, 49875-49880, 50045-50050, 50085-50090, 50095-50110, 50115-50120, 50275-50280, 50315-50320, 50325-50340, 50345-50350, 50545-50550, 50555-50570, 50575-50580, 50585-50590, 50645-50650, 50775-50780, 50785-50790, 50795-50800, 50805-50810, 50815-50820, 50855-50860, 50875-50880, 51005-51010, 51035-51040, 51045-51050, 51085-51090, 51095-51110, 51115-51120, 51275-51280, 51315-51340, 51345-51350, 51505-51510, 51545-51580, 51585-51590, 51595-51600, 51775-51780, 51785-51800, 51805-51810, 51815-51820, 52005-52010, 52015-52020, 52025-52030, 52035-52040, 52045-52050, 52055-52060, 52065-52070, 52085-52090, 52095-52100, 52105-52110, 52115-52120, 52235-52240, 52275-52280, 52285-52300, 52305-52310, 52315-52340, 52345-52350, 52500-52580, 52585-52590, 52595-52610, 52775-52800, 52805-52810, 53005-53030, 53035-53040, 53045-53050, 53055-53070, 53105-53110, 53235-53240, 53275-53300, 53305-53310, 53315-53320, 53325-53340, 53345-53350, 53465-53470, 53495-53580, 53585-53610, 53615-53620, 53645-53650, 53735-53740, 53775-53810, 54005-54040, 54045-54050, 54055-54070, 54235-54240, 54245-54250, 54255-54260, 54275-54280, 54285-54300, 54335-54340, 54465-54470, 54475-54490, 54495-54620, 54645-54650, 54695-54700, 54725-54730, 54735-54760, 54765-54770, 54775-54810, 55000-55040, 55045-55050, 55235-55240, 55245-55260, 55465-55620, 55625-55655, 55695-55770, 55775-55810, 56000-56040, 56235-56240, 56245-56260, 56455-56770, 56775-56800, 56805-56810, 57000-57040, 57195-57200, 57205-57220, 57450-57770 look edge: 77-685 look up: 77-1054
target = 6 * 10**4
print("Expected actions to reach {} FAM".format(target))
strats = list(strategies(target).items())
strats = [strats[0], strats[3], strats[2]]
for i, (name, strategy) in enumerate(strats):
x = solve_strategy(strategy, target)
print(" ", name, x[0])
plt.plot(x, color=['g','b','pink'][i], label=name)
plt.legend()
plt.xlabel("Fasting and Meditating")
plt.ylabel("Expected Actions to Target")
Expected actions to reach 60000 FAM old best 219.7200943292469 optimal60k 218.96537113547984 simplified 219.2733885543293
Text(0, 0.5, 'Expected Actions to Target')
We can verify that the minimum also holds with the larger target.
print_with_adjusted_bounds(strategies(6 * 10**4)['simplified'], 6 * 10**4, 5)
watch circle watch circle watch wait 77 100 396 1617 59450 59950 Original 77 105 396 1617 59450 59950 3.13089898e-04 77 95 396 1617 59450 59950 -1.11091330e-04 77 100 401 1617 59450 59950 1.25376934e-05 77 100 391 1617 59450 59950 3.12345193e-06 77 100 396 1622 59450 59950 2.10476662e-06 77 100 396 1612 59450 59950 1.98372968e-04 77 100 396 1617 59455 59950 2.34830767e-03 77 100 396 1617 59445 59950 5.45434352e-04 77 100 396 1617 59450 59955 5.59036402e-03 77 100 396 1617 59450 59945 3.49918251e-03