Last active
August 20, 2024 14:18
-
-
Save tslater2006/d865ffaf825ed7244be46fa5f578f272 to your computer and use it in GitHub Desktop.
Simulates the 1-20 game found here (https://fleetfoxx.github.io/1-to-20/) to see how often this strategy wins.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| This script simulates a game where numbers are drawn randomly between 1 and 1000 and placed into 20 slots. | |
| The goal is to place the numbers in the slots in the most optimal way to win the game. | |
| Numbers cannot be rearranged after placement and must be strictly in ascending order. | |
| The script keeps track of the number of games played and the minimum, maximum, and average number of games required to win. | |
| To see a visual representation of the game, uncomment the replay_game function call and set number of games to 1. | |
| In my testing it takes on average it takes around ~160,000 games to win one. | |
| """ | |
| import random | |
| from time import sleep | |
| # Rest of the code... | |
| import random | |
| from time import sleep | |
| def find_range(num, slots): | |
| if num in slots: | |
| return None, None | |
| lower = None | |
| upper = None | |
| for index in range(len(slots)): | |
| if slots[index] is None and lower is None: | |
| lower = index | |
| continue | |
| if slots[index] is not None and slots[index] < num: | |
| lower = None | |
| continue | |
| if slots[index] is not None and slots[index] > num: | |
| upper = None if index - 1 < 0 else index - 1 | |
| break | |
| if lower is not None and upper is None: | |
| upper = len(slots) - 1 | |
| return lower, upper | |
| def find_best_index(num, range_indexes, slots): | |
| # determine what the number before and after the valid range is | |
| num_before = 0 if range_indexes[0] == 0 else slots[range_indexes[0] - 1] | |
| num_after = ( | |
| 1001 if range_indexes[1] == len(slots) - 1 else slots[range_indexes[1] + 1] | |
| ) | |
| # of the numbers that the valid range needs to cover, how many are before and after the current number | |
| count_before = num - num_before | |
| count_after = num_after - num | |
| # what percent of the valid range is before the current number | |
| percent_before = count_before / (count_before + count_after) | |
| # we should put the number at the index that is the same percentage through the range of valid indexes | |
| index = range_indexes[0] + round( | |
| (range_indexes[1] - range_indexes[0]) * percent_before | |
| ) | |
| return index | |
| def play_number(slots, draw_order, index_order): | |
| # get random number between 1 and 1000 inclusive | |
| num = random.randint(1, 1000) | |
| draw_order.append(num) | |
| # print("Drew number: ", num) | |
| valid_range = find_range(num, slots) | |
| if valid_range[0] is None: | |
| # print("No valid move!") | |
| return False | |
| index = find_best_index(num, valid_range, slots) | |
| index_order.append(index) | |
| # print("Best index: ", index) | |
| slots[index] = num | |
| # print(slots) | |
| return True | |
| def play_game(): | |
| slots = [None] * 20 | |
| draw_order = [] | |
| index_order = [] | |
| for i in range(20): | |
| should_continue = play_number(slots, draw_order, index_order) | |
| if not should_continue: | |
| return False, slots, None, None | |
| return True, slots, draw_order, index_order | |
| def replay_game(draw_order, index_order): | |
| print("Replaying game...") | |
| slots = [" "] * 20 | |
| print("-" * ((20 * 6) + 4)) | |
| cols_found = [] | |
| for i in range(20): | |
| # draw vertical wall to match = signs | |
| num = draw_order[i] | |
| # print("Drawing number: ", num) | |
| index = index_order[i] | |
| # pad number with spaces so that all are 4 characters long | |
| slots[index] = f"{num:4}" | |
| print(f"|{(i+1):2}|", end="") | |
| for z in range(20): | |
| if z == index: | |
| # print the number in green | |
| print(f"\033[92m{slots[z]:5}\033[0m", end="") | |
| cols_found.append(z) | |
| else: | |
| # print(f"{slots[z]:5}", end="") | |
| if z in cols_found: | |
| print(f" . ", end="") | |
| else: | |
| print(f" ", end="") | |
| if z < 19: | |
| print("|", end="") | |
| print("|") | |
| print("|--|", end="") | |
| for z in range(20): | |
| print(f"{slots[z]:5}", end="") | |
| if z < 19: | |
| print("|", end="") | |
| print("|") | |
| print("-" * ((20 * 6) + 4)) | |
| return slots | |
| wins = [] | |
| num_games = 10 | |
| for i in range(num_games): | |
| game_count = 0 | |
| while True: | |
| # print("Playing game ", game_count) if game_count % 100000 == 0 else None | |
| game_won, slots, draw_order, index_order = play_game() | |
| game_count += 1 | |
| if game_won: | |
| print("Game won after ", game_count, " games!") | |
| # print("Draw order: ", draw_order) | |
| # print("Index order: ", index_order) | |
| # print("Slots: ", slots) | |
| print() | |
| # replay_game(draw_order, index_order) | |
| break | |
| wins.append(game_count) | |
| print("Current min games to win: ", min(wins)) | |
| print("Current max games to win: ", max(wins)) | |
| print( | |
| f"Current average games to win ({len(wins)} data points): ", | |
| int(sum(wins) / len(wins)), | |
| ) | |
| print() |
Author
tslater2006
commented
Aug 20, 2024
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment