This commit is contained in:
DomNomNomVR 2025-04-14 15:58:38 +12:00
parent 22ef750437
commit 028fb0bc3a
28 changed files with 2199 additions and 9 deletions

180
.gitignore vendored Normal file
View File

@ -0,0 +1,180 @@
# Dom's manually added things
bad apple/factorio-draftsman
typing-puzzles/
heartbound OCR/
signals/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# UV
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
#uv.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
.pdm.toml
.pdm-python
.pdm-build/
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Ruff stuff:
.ruff_cache/
# PyPI configuration file
.pypirc

View File

@ -0,0 +1,152 @@
from functools import cache
from collections import Counter, defaultdict
# pool = Counter(map(int, "000226688"))
# counted_digits = (0, 2, 6, 8)
# counts = (3, 2, 2, 2)
# 268
# 208
# 60
# 0
@cache
def choose_n_unique(counts, n):
if sum(counts) < n:
raise "you done fucked up"
if sum(counts) == n:
return set([counts])
out = set([])
for i, count in enumerate(counts):
if count <= 0:
continue
out.update(
choose_n_unique(counts[:i] + (count - 1,) + counts[i + 1 :], n)
)
return out
def sub(a, b): # a-b
return tuple(x - y for x, y in zip(a, b))
# print(choose_n_unique(counts, 3))
# print(find_sums(counts))
# print(possible_sums((3, 2, 2, 2)))
def has_len_digit(tup, length):
for t in tup:
if len(str(t)) == length:
return True
return False
# tup_0, tup_1 = (826,), (0, 0, 6, 820)
# print(has_len_digit(tup_0, 2))
# print(has_len_digit(tup_1, 2))
# exit()
def find_solutions(counted_digits, counts):
@cache
def possible_sums(counts): # sum -> set(tuple(addend))
if sum(counts) == 0:
return {0: set()}
if sum(counts) == 1:
for i, count in enumerate(counts):
if count == 0:
continue
digit = counted_digits[i]
out = {digit: set([(digit,)])}
return out
out = defaultdict(set)
for i, count in enumerate(counts):
if count <= 0:
continue
digit = counted_digits[i]
sub = possible_sums(counts[:i] + (count - 1,) + counts[i + 1 :])
for s, tups in sub.items():
for tup in tups:
out[s + digit].add(tuple(sorted((digit,) + tup)))
# try appending the digit to all sub subms
for tups in sub.values():
for tup in tups:
for j, val in enumerate(tup):
if j > 0 and val == tup[j - 1]:
continue
new_tup = tuple(
sorted((val * 10 + digit,) + tup[:j] + tup[j + 1 :])
)
out[sum(new_tup)].add(new_tup)
return out
out = []
for counts_0 in choose_n_unique(counts, 3):
counts_1 = sub(counts, counts_0)
# print(counts_1)
possible_0 = possible_sums(counts_0)
possible_1 = possible_sums(counts_1)
# print(possible_0.get(268, "nope"))
# print(possible_1.get(268, "nope"))
# print()
for total in possible_0.keys() & possible_1.keys():
tups_0 = possible_0[total]
tups_1 = possible_1[total]
for tup_0 in tups_0:
for tup_1 in tups_1:
if not (has_len_digit(tup_0, 1) or has_len_digit(tup_1, 1)):
continue
if not (has_len_digit(tup_0, 2) or has_len_digit(tup_1, 2)):
continue
if not (has_len_digit(tup_0, 3) or has_len_digit(tup_1, 3)):
continue
out.append((tup_0, tup_1))
return out
# print(find_solutions(counted_digits=(0, 2, 6, 8), counts=(3, 2, 2, 2)))
import random
rng = random.Random(4)
# best = 100
# for i in range(1000):
# # rng.randint()
# def r(radius):
# return rng.randint(-radius, radius)
# candidate = find_solutions(
# counted_digits=(1 + r(1), 2 + r(1), 6 + r(1), 8 + r(1)),
# counts=(3 + r(1), 2 + r(1), 2 + r(1), 2 + r(1)),
# )
# if not candidate:
# continue
# if len(candidate) <= best:
# best = len(candidate)
# print("yay")
# for tup in candidate:
# print(" ", tup)
def counted_digits_and_counts_from_solution(solution):
s = str(solution)
for to_remove in "(), ":
s = s.replace(to_remove, "")
print(s)
print("".join(sorted(s)))
c = Counter(s)
counted_digits = tuple(int(k) for k in sorted(c))
counts = tuple(int(c[k]) for k in sorted(c))
return counted_digits, counts
print(counted_digits_and_counts_from_solution("((261,), (7, 27, 227)))"))
print(find_solutions((1, 2, 6, 7), (1, 4, 1, 3)))

10
12345_combination_sum.py Normal file
View File

@ -0,0 +1,10 @@
import math
import itertools
# print(list))
start_digits = [1,2,3,4,5]
print(sum(int(''.join(str(digit) for digit in digits)) for digits in itertools.permutations(start_digits)))
print(math.factorial(len(start_digits)) * (sum(start_digits)/len(start_digits)) * 11111)

View File

@ -0,0 +1,180 @@
# https://youtu.be/wTJI_WuZSwE
from functools import reduce, cache
from typing import List, Tuple, Callable, Iterable
from math import sqrt, log2
from random import Random
def get_flip_pos_2x2(board: List[int], key: int) -> int:
assert all(x in [0,1] for x in board)
assert len(board) == 4
s = sum(board)
if s==0:
return key
if s==1:
pos = board.index(1)
if key == 0:
return pos # empty
if key == 1:
return [1,0,3,2][pos] # horizontal
if key == 2:
return [2,3,0,1][pos] # vertical
if key == 3:
return [3,2,1,0][pos] # diagonal
assert False
if s==2:
# ensure ``sum(board) in [1,3]``, pointing to key
if board[key] == 0: # sum(flipped board) == 3
possibilities = [i for i in range(len(board)) if i != key and board[i]==0]
else: # sum(flipped board) == 3
possibilities = [i for i in range(len(board)) if i != key and board[i]==1]
assert len(possibilities) == 1
return possibilities[0]
if s==3:
return get_flip_pos_2x2(all_flipped(board), key)
if s==4:
return get_flip_pos_2x2(all_flipped(board), key)
assert False
def all_flipped(board: List[int]) -> List[int]:
return [1-x for x in board]
def guess_key_2x2(board: List[int]) -> int:
s = sum(board)
if s==0:
return 0
if s==1:
return board.index(1)
if s==2:
if board==[1,1,0,0] or board==[0,0,1,1]:
return 1 # horizontal
if board==[1,0,1,0] or board==[0,1,0,1]:
return 2 # vertical
if board==[1,0,0,1] or board==[0,1,1,0]:
return 3 # diagonal
if s==3:
return guess_key_2x2(all_flipped(board))
if s==4:
return guess_key_2x2(all_flipped(board))
assert False
def flip_mutate(board: List[int], flip_pos: int):
board[flip_pos] = 1-board[flip_pos]
def flip_copy(board: List[int], flip_pos: int) -> List[int]:
board = board[:]
flip_mutate(board, flip_pos)
return board
def print_board(board: List[int]):
sidelength = sqrt(len(board))
assert sidelength == int(sidelength)
sidelength = int(sidelength)
for row_i in range(sidelength):
print(''.join(str(x) for x in board[row_i*sidelength:(row_i+1)*sidelength]))
def generate_boards(board_squre_count: int, sample_count: int) -> Iterable[int]:
if sample_count >= 2**board_squre_count:
print('doing exhaustive verification.')
yield from range(2**board_squre_count)
print(f'not doing exhaustive. need sample_count>={2**board_squre_count}, got sample_count={sample_count}')
rng = Random(4)
for i in range(sample_count):
yield rng.getrandbits(board_squre_count)
def validate(board_squre_count, get_flip_pos: Callable, guess_key: Callable, sample_count=2**16) -> bool:
success_boards = set()
for sample_i, board_int in enumerate(generate_boards(board_squre_count, sample_count)):
if board_int in success_boards:
continue
if sample_i %100 == 0:
print(sample_i/sample_count)
for key in range(board_squre_count):
# potential optimization: could re-use board and unflip guess
board = [(board_int>>i)&1 for i in reversed(range(board_squre_count))]
flip_pos = get_flip_pos(board, key)
flip_mutate(board, flip_pos)
guess = guess_key(board)
if guess != key:
print(f'Found counter example. Got guess {guess}, want {key}')
flip_mutate(board, flip_pos)
print('before flip:')
print_board(board)
flip_mutate(board, flip_pos)
print('after flip:')
print_board(board)
return False
success_boards.add(board_int)
if len(success_boards) == 2**board_squre_count:
# All boards have been validated
print('exhaustive, yay')
return True
print(f'non exhaustive finish. covered {len(success_boards)} cases ({len(success_boards) / 2**board_squre_count:%} of cases) successfully')
return True
# validate(4, get_flip_pos_2x2, guess_key_2x2)
# Scaler possibilities:
# Total key index % 4 if we split into 4-squares
# is each reg
# XOR each 4x4 section into a bit to determine which quadrant
# layer each 4x4 section with XOR, to determine which quadrant in the above 4x4
# layer each 2x2 section with XOR, to determine which quadrant in 2x2
def guess_key_pow2board(board: List[int]) -> int:
if len(board) == 4:
return guess_key_2x2(board)
sidelength = sqrt(len(board))
assert sidelength == int(sidelength)
sidelength = int(sidelength)
assert log2(sidelength) == int(log2(sidelength))
quarter = len(board) / 4
assert quarter == int(quarter)
quarter = int(quarter)
# Not actually quadrants but good enough
quadrants = [ board[i*quarter:(i+1)*quarter] for i in range(0,4) ]
quarter_i = guess_key_2x2([ reduce(lambda a,b: a^b, q) for q in quadrants ])
sub_i = guess_key_pow2board([a^b^c^d for a,c,b,d in zip(*quadrants)])
return quarter * quarter_i + sub_i
def make_get_flip_pos(guess_key: Callable) -> Callable:
# Flip the thing that works
def get_flip_pos(board, key) -> int:
possibilities = [
flip_pos for flip_pos in range(len(board))
if guess_key(flip_copy(board, flip_pos)) == key
]
assert len(possibilities) == 1, f"possibilities={possibilities} want length 1"
return possibilities[0]
return get_flip_pos
def get_flip_pos_pow2board(board: List[int], key: int) -> int:
if len(board) == 4:
return get_flip_pos_2x2(board, key)
quarter = len(board) / 4
assert quarter == int(quarter)
quarter = int(quarter)
# Not actually quadrants but good enough
quadrants = [ board[i*quarter:(i+1)*quarter] for i in range(0,4) ]
quarter_i = get_flip_pos_2x2([ reduce(lambda a,b: a^b, q) for q in quadrants ], key//4)
sub_i = get_flip_pos_pow2board([a^b^c^d for a,c,b,d in zip(*quadrants)], key%4)
return quarter * quarter_i + sub_i
# validate(4, make_get_flip_pos(guess_key_2x2), guess_key_2x2)
# validate(16, make_get_flip_pos(guess_key_pow2board), guess_key_pow2board)
# validate(16, get_flip_pos_pow2board, guess_key_pow2board)
validate(64, get_flip_pos_pow2board, guess_key_pow2board)
# validate(64, make_get_flip_pos(guess_key_pow2board), guess_key_pow2board)

View File

@ -0,0 +1,86 @@
from collections import Counter
from pprint import pprint
basis = [1,2,3,4,5,6]
def get_all_subsets(l):
if l == []:
return [[]]
subs = get_all_subsets(l[1:])
return subs + [[l[0]] + el for el in subs]
all_subsets = get_all_subsets(basis)
all_subsets = sorted([sub for sub in all_subsets if len(sub) >= 2])
# all_subsets = sorted([sub for sub in all_subsets if len(sub) >= 2])
# all_subsets = sorted([sub for sub in all_subsets if sum(sub) < 9])
def product(sub):
if len(sub) == 0:
return 1
return sub[0] * product(sub[1:])
def sort_keys(counter: Counter) -> Counter:
out = Counter()
for key in sorted(counter.keys()):
out[key] = counter[key]
return out
def print_sorted_by_key(counter: Counter) -> Counter:
print('{')
for key in sorted(counter.keys()):
print(f' {key}: {counter[key]},')
print('}')
sum_counter = Counter(sum(sub) for sub in all_subsets)
product_counter = Counter(product(sub) for sub in all_subsets)
product_counter_unique_sum = Counter(product(sub) for sub in all_subsets if sum_counter[sum(sub)] > 1)
#debug
print_sorted_by_key(sum_counter)
print_sorted_by_key(product_counter)
print(sum([3,4,5,6]))
print(product([3,4,5,6]))
print('a', [(sub, sum(sub)) for sub in all_subsets if sum(sub)==18])
print('b', [(sub, product(sub)) for sub in all_subsets if product(sub)==360])
print(sum([3,4,5,6]))
print(sum([1,3,4,5,6]))
print('xxx')
# solve it one way
for sub in all_subsets:
if sum_counter[sum(sub)] > 1 and product_counter_unique_sum[product(sub)] == 1:
print(sub, sum(sub), product(sub))
print('xxx')
# solve it one way
for sub in all_subsets:
if sum_counter[sum(sub)] > 1 and product_counter[product(sub)] == 1:
print(sub, sum(sub), product(sub))
print('yyy')
# solve it another way
for answer_sum in range(100):
if sum_counter[answer_sum] == 0:
continue
has_some_determined = False
has_some_undetermined = False
for sub in all_subsets:
if not sum(sub) == answer_sum:
continue
if product_counter[product(sub)] > 1:
has_some_undetermined += True
if product_counter[product(sub)] == 1:
has_some_determined += True
if not (has_some_determined and has_some_undetermined):
continue
print(answer_sum, has_some_determined, has_some_undetermined)
# print(sub, sum(sub), product(sub))
# print()

View File

@ -0,0 +1,148 @@
# https://www.youtube.com/watch?v=8xXslshomOs
from typing import Dict, Optional
from dataclasses import dataclass
@dataclass(frozen=True, order=True)
class State:
gold_a: int
gold_b: int
silver_a: int
silver_b: int
gems_a: int
gems_b: int
equal_priority: bool # 0 means "a", which is the protagonist
winner_cache : Dict[State, int] = {}
def resolve_auction(state: State, bet_a: int, bet_b: int) -> Optional[State]:
if bet_b > state.gems_b:
return None
assert bet_a <= state.gems_a
equal_priority = state.equal_priority
if bet_a > bet_b:
bet_winner = 0
elif bet_a < bet_b:
bet_winner = 1
else:
bet_winner = int(equal_priority)
equal_priority = 1 - equal_priority
doing_golds = state.gold_a + state.gold_b <3
return State(
gold_a=state.gold_a + 1 if doing_golds and bet_winner == 0 else state.gold_a,
gold_b=state.gold_b + 1 if doing_golds and bet_winner == 1 else state.gold_b,
silver_a=state.silver_a + 1 if not doing_golds and bet_winner == 0 else state.silver_a,
silver_b=state.silver_b + 1 if not doing_golds and bet_winner == 1 else state.silver_b,
gems_a=state.gems_a - bet_a if bet_winner == 0 else state.gems_a,
gems_b=state.gems_b - bet_b if bet_winner == 1 else state.gems_b,
equal_priority=equal_priority,
)
next_print = 1
def get_winner(state: State) -> int:
if state.gold_a >= 3:
winner_cache[state] = 0; return 0, (state,)
if state.gold_b >= 3:
winner_cache[state] = 1; return 1, (state,)
if state.silver_a >= 3:
winner_cache[state] = 0; return 0, (state,)
if state.silver_b >= 3:
winner_cache[state] = 1; return 1, (state,)
winner = winner_cache.get(state)
if winner is not None:
return winner, (state,)
doing_golds = state.gold_a + state.gold_b <3
child_states = []
path = tuple()
# minimax
winner_a = 1
for bet_a in range(state.gems_a+1):
(winner_b, path) = max([
get_winner(child)
for child in
[resolve_auction(state, bet_a, bet_b) for bet_b in {0, bet_a, bet_a+1}]
if child
])
winner_a = min(winner_a, winner_b)
if winner_a == 0:
path = (state, bet_a) + path
# break
winner_cache[state] = winner_a
global next_print
if len(winner_cache) > next_print:
print(len(winner_cache))
next_print *= 2
return winner_a, path
initial_state = State(
gold_a=1,
gold_b=0,
silver_a=0,
silver_b=0,
gems_a=75,
gems_b=99,
equal_priority=0,
)
import pickle
cache_file = __file__+".winner_cache.pickle"
# print(get_winner(initial_state))
# with open(cache_file, 'wb') as f:
# pickle.dump(winner_cache, f)
with open(cache_file, 'rb') as f:
winner_cache = pickle.load(f)
# print(winner_cache)
def find_children(state):
child_states = []
for bet_a in range(state.gems_a+1):
responses = [resolve_auction(state, bet_a, bet_b) for bet_b in {0, bet_a, bet_a+1}]
if any(winner_cache.get(res) == 1 for res in responses):
responses = [res for res in responses if winner_cache.get(res) == 1]
child_states.extend(responses)
return [state for state in child_states if state is not None]
def find_winning_children(state):
return [state for state in find_children(state) if winner_cache.get(state) == 0]
def print_bets(state0, state1):
for bet_a in range(state0.gems_a+1):
for bet_b in {0, bet_a, bet_a+1}:
res = resolve_auction(state0, bet_a, bet_b)
if res == state1:
print(f'bet_a={bet_a} bet_b={bet_b}')
def get_winning_path(state: State):
get_winning_option_fraction = lambda child: len(find_winning_children(child)) / max(1, len(find_children(child)))
print()
print(len(find_winning_children(state)), "/", len(find_children(state)))
print(state)
winner = winner_cache.get(state)
child_states = find_winning_children(state)
if not child_states:
print('fin')
return
choice = min(child_states, key=get_winning_option_fraction)
print_bets(state, choice)
# for child in child_states:
# print(" " + (">" if child is choice else " ") + " " + repr(child))
get_winning_path(choice)
get_winning_path(initial_state)

Binary file not shown.

77
bad apple/bad_apple.py Normal file
View File

@ -0,0 +1,77 @@
import pyperclip
from draftsman.blueprintable import Blueprint, BlueprintBook
from draftsman.constants import Direction
from draftsman.entity import ConstantCombinator
# from draftsman.data import items
import draftsman.data.signals
from PIL import Image
blueprint = Blueprint()
blueprint.label = "bad apple"
blueprint.description = "bad apple."
blueprint.version = (1, 0) # 1.0
frame = Image.open("frame.png")
threshold = 40
signals = list(draftsman.data.signals.raw)[3:] # skip the three special signals
pixels = frame.load()
# Create the combinator for the single frame
frame_combinator = ConstantCombinator()
frame_combinator.tile_position = (-1, 0)
for x in range(frame.width):
column_mask = 0
for y in range(frame.height):
if pixels[x, y][0] > threshold:
column_mask |= 1 << y
frame_combinator.set_signal(index=x, signal=signals[x], count=column_mask)
blueprint.entities.append(frame_combinator)
pyperclip.copy(blueprint.to_string())
# Code for creating the column filters below.
# for x in range(frame.width):
# blueprint.entities.append( # this is our y-combinator :^)
# "arithmetic-combinator",
# id=f"filter-{x}",
# tile_position=[x, 2],
# direction=Direction.NORTH,
# control_behavior={
# "arithmetic_conditions": {
# "first_signal": signals[x],
# "operation": "AND",
# "second_signal": {"type": "virtual", "name": "signal-each"},
# "output_signal": {"type": "virtual", "name": "signal-each"},
# }
# },
# )
# arithmetic_conditions = {
# "first_signal": {"type": "fluid", "name": "steam"},
# "second_signal": {"type": "virtual", "name": "signal-each"},
# "operation": "AND",
# "output_signal": {"type": "virtual", "name": "signal-each"},
# "first_signal_networks": {"red": True, "green": False},
# "second_signal_networks": {"red": False, "green": True},
# }
# from draftsman.utils import JSON_to_string
# # directly hack in these conditions because this is a new feature and we don't care about the validation bullshit
# d = blueprint.to_dict()
# for entity in d["blueprint"]["entities"]:
# entity["control_behavior"]["arithmetic_conditions"][
# "first_signal_networks"
# ] = {"red": True, "green": False}
# entity["control_behavior"]["arithmetic_conditions"][
# "second_signal_networks"
# ] = {"red": False, "green": True}
# s = JSON_to_string(d)
# pyperclip.copy(str(s))

BIN
bad apple/frame.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 803 B

60
beltomatic.py Normal file
View File

@ -0,0 +1,60 @@
# from math import abs
from collections import defaultdict
max_num = 16
initial_nums = list(range(1, max_num + 1))
initial_nums.remove(10)
# end number -> list of steps to get here which are
# (prev_number, operation description)
target = 7387
upper_bound = target * max_num
ops = []
for n in initial_nums:
ops.append((lambda x, n=n: x + n, f"+{n}"))
ops.append((lambda x, n=n: abs(x - n), f"-{n}"))
ops.append((lambda x, n=n: x * n, f"*{n}"))
ops.append((lambda x, n=n: x // n if x and x % n == 0 else x, f"/{n}"))
ops.append((lambda x, n=n: x**n if x**2 < upper_bound else x, f"**{n}"))
seen = {n: [] for n in initial_nums}
recent = seen.keys()
while target not in seen:
news = defaultdict(list)
for n in recent:
for op, description in ops:
new = op(n)
if new > upper_bound:
continue
if new in seen:
continue
news[new].append((n, description))
seen.update(news)
recent = sorted(news.keys())
if not news:
assert False
print(f"progress: {len(seen)=}")
pickers = [
lambda l: l[0],
lambda l: l[len(l) // 2],
lambda l: l[-1],
]
for picker in pickers:
steps = [target]
picks = []
while prevs := seen[steps[-1]]:
pick = picker(prevs)
picks.append(pick)
steps.append(pick[0])
print(f"{steps[-1]:>3}", end=" ")
for pick in reversed(picks):
print(f"{pick[1]:>4}", end=" ")
print(f" = {target}")

37
cheating royal riddle.py Normal file
View File

@ -0,0 +1,37 @@
# https://www.youtube.com/watch?v=hk9c7sJ08Bg
from collections import defaultdict
distribution = {0:1}
def print_sorted_by_key(counter):
print('{')
for key in sorted(counter.keys()):
print(f' {key}: {counter[key]},')
print('}')
def convolve(distribution, equally_weighted_dice):
d2 = defaultdict(float)
for k,v in distribution.items():
for red in equally_weighted_dice:
d2[k+red] += v/len(equally_weighted_dice)
return d2
for _ in range(20):
distribution = convolve(distribution, [2,7,7,12,12, 17])
distribution = convolve(distribution, [3,8,8,13,13, 18])
# print_sorted_by_key(distribution)
cumulative = defaultdict(float)
acc = 0
for k in sorted(distribution):
acc += distribution[k]
cumulative[k] = acc
# print_sorted_by_key(distribution)
# print(sum(distribution.values()))
print_sorted_by_key(cumulative)

55
chef puzzle.py Normal file
View File

@ -0,0 +1,55 @@
# https://www.youtube.com/watch?v=HyRjuPP9S3o
from collections import Counter
max_val = 1300
possibilities = list(range(13, max_val+1))
# exclusions = {64, 729}
perfect_squares = {x*x for x in range(100)}
perfect_cubes = {x*x*x for x in range(100) if x*x*x <= max_val}
def less_than_500(x):
return x < 500
def is_perfect_square(x):
return x in perfect_squares
def is_perfect_cube(x):
return x in perfect_cubes
def second_digit_1(x):
return str(x)[1] == "1"
# print([x for x in possibilities if is_perfect_cube(x) and is_perfect_square(x)])
# invert invert second digit is a 1
conditions = [less_than_500, is_perfect_square, is_perfect_cube]
for mask in range(2**3):
pos = set(possibilities)
for x in possibilities:
for i, condition in enumerate(conditions):
if not (condition(x) ^ (mask & (1 << i))):
pos.remove(x)
break
if 1 in Counter(second_digit_1(x) for x in pos).values():
print(mask, pos)
# print(mask, pos)
# What was said
# it is >500 (lie)
# it
# truth
# less than 500
# perfect square
# perfect

114
cnf_or_tuples.py Normal file
View File

@ -0,0 +1,114 @@
################ solver
from itertools import chain, combinations
from functools import reduce
def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1))
def check_solution(selection, operator):
return reduce(
lambda a, b: tuple(operator(x, y) for x, y in zip(a, b)), selection
)
def tuple_selector(tuples, operator):
solution_count = 0
for selection in powerset(tuples):
if not selection:
continue
if all(check_solution(selection, operator)):
print(f"found a solution! {selection}")
solution_count += 1
print(f"{solution_count=}")
################# problem statement
def operator_with_secret(a: int, b: int) -> int:
either = {a, b}
if 1 in either:
return a + b % 2
# if 0 in either:
# return 0
# final element that must be included
if 2 in either:
# The x's represent free choices.
# 0bxxxxxxx00
secret = 0b010110100
return 3 if secret in either else 0
# build your own number kit
return a | b
tuple_selector(
[
# make a choice to set a bit in the first number
(0b000000100, 0, 1, 0, 0, 0, 0, 0, 0),
(0b000001000, 0, 0, 1, 0, 0, 0, 0, 0),
(0b000010000, 0, 0, 0, 1, 0, 0, 0, 0),
(0b000100000, 0, 0, 0, 0, 1, 0, 0, 0),
(0b001000000, 0, 0, 0, 0, 0, 1, 0, 0),
(0b010000000, 0, 0, 0, 0, 0, 0, 1, 0),
(0b100000000, 0, 0, 0, 0, 0, 0, 0, 1),
# .. or not
(0b000000000, 0, 1, 0, 0, 0, 0, 0, 0),
(0b000000000, 0, 0, 1, 0, 0, 0, 0, 0),
(0b000000000, 0, 0, 0, 1, 0, 0, 0, 0),
(0b000000000, 0, 0, 0, 0, 1, 0, 0, 0),
(0b000000000, 0, 0, 0, 0, 0, 1, 0, 0),
(0b000000000, 0, 0, 0, 0, 0, 0, 1, 0),
(0b000000000, 0, 0, 0, 0, 0, 0, 0, 1),
# this final element that must be included
(0b000000010, 1, 0, 0, 0, 0, 0, 0, 0),
],
operator_with_secret,
)
# Commutative operator variant
def operator_with_secret(a: int, b: int) -> int:
# We're going to create a bitpacked representation of the state of the
# computation. The state is the number of records included, and the value
# is a bitwise OR of the records included. The state is stored in the
# first slot of each tuple, but this operator doesn't know which slot is
# first, so we just check to see if the value is greater than 1.
value_bits = 9
total_records_to_select = 8
def extract(bitpacked: int) -> Tuple[int, int]:
count = bitpacked >> value_bits
value = bitpacked & ((1 << value_bits) - 1)
return (count if count else 1), value
def pack(count: int, value: int) -> int:
return (count << value_bits) | value
either = {a, b}
# If we're building our state in the first slot, handle it
if any(x > 1 for x in either):
# Unpack the state from each input
# State is just how many records have been included so far, and the
# value which has been built
ac, av = extract(a)
bc, bv = extract(b)
count, value = ac + bc, av | bv
# If we haven't inluded eight records yet, just build up the number
if count < total_records_to_select:
return pack(count, value)
# If we've included eight records, we're done and need to check the
# value is what we wanted
# The x's represent free choices.
# 0bxxxxxxx00
secret = 0b010110100
return 3 if value == secret else 0
# Otherwise, we're just checking that we select at least one truthy value
return a | b

View File

@ -0,0 +1,153 @@
"""
Double decker train problem:
|^^^^^^^^^^^^^^| |^^^^^^^^^^^^^^|
| Double cargo | | wagon |
~~~~ ____ |--------------| |--------------|
Y_,___|[]| | Double | | cargo wagon |
{|_|_|_|PU|_,_|______________|_,_|______________|
//oo---OO=OO O-O O-O ^ O-O O-O ^
This is a deceptively simple programming puzzle where you get to make an ordering of rail wagons.
Each wagon has two floors and there is text on the outside of the top and bottom floor.
When your chosen wagons are linked together, the whole text on the top should match the whole bottom.
Output an empty list only when there is no non-empty choice of wagons with matching text.
Your ordering may include any wagon any number of times.
Example: (illustrated above)
Input wagons: [("Double cargo ", "Double "), ("wagon", "cargo wagon")]
Output ordering: [0, 1]
Explanation:
Both the top and bottom will read "Double cargo wagon" when concatenated.
Note that there is no need to mutate whitespace characters in the input.
"""
from typing import Callable, List, Optional, Tuple
Wagon = Tuple[str, str]
MAX_LENGTH = 10
def my_solver(wagons: List[Wagon]) -> List[int]:
# Handle empty wagon case
for i, wagon in enumerate(wagons):
if wagon[0] + wagon[1] == "":
return [i]
return solve("", "", [], prepare(wagons))
def prepare(wagons: List[Wagon]) -> List[Tuple[int, Wagon]]:
seen = set()
prepared = []
for i, wagon in enumerate(wagons):
if wagon in seen:
continue
prepared.append((i, wagon))
seen.add(wagon)
return prepared
def solve(top_text: str, bottom_text: str, ordering: List[int], wagons: List[Tuple[int, Wagon]]) -> List[int]:
if len(top_text) == 0 and len(bottom_text) == 0 and len(ordering) > 0:
return ordering
if len(ordering) > MAX_LENGTH:
return []
for z, (i, wagon) in enumerate(wagons):
top = top_text + wagon[0]
bottom = bottom_text + wagon[1]
reordered = wagons[:z] + wagons[z+1:] + [(i, wagon)]
if top.startswith(bottom):
solution = solve(top[len(bottom):], "", [*ordering, i], reordered)
if len(solution) > 0:
return solution
if bottom.startswith(top):
solution = solve("", bottom[len(top):], [*ordering, i], reordered)
if len(solution) > 0:
return solution
return []
def check_ordering(wagons: List[Wagon], ordering: List[int]) -> bool:
"""Checks if the text on top matches the text on the bottom"""
sentence0 = ""
sentence1 = ""
for i in ordering:
assert 0 <= i < len(wagons), f"wagon index out of range: {i}"
sentence0 += wagons[i][0]
sentence1 += wagons[i][1]
return sentence0 == sentence1
def run_test_case(
wagons: List[Wagon],
example_ordering: List[int],
ordering: List[int],
) -> str:
if ordering == []:
if example_ordering == []:
return "PASS"
return "You falsely claimed it was impossible."
if check_ordering(wagons, ordering):
if example_ordering == []:
return "PASS (and the problem setter is a dummy)"
return "PASS"
sentence0 = ""
sentence1 = ""
for i in ordering:
assert 0 <= i < len(wagons), f"wagon index out of range: {i}"
sentence0 += wagons[i][0]
sentence1 += wagons[i][1]
# return f"Your selected wagons had different sentences on the top and bottom.\n{ordering}\n{sentence0!r}\n{sentence1!r}"
return "Your selected wagons had different sentences on the top and bottom."
def run_test_cases(solver: Callable[[List[Wagon]], List[int]]):
test_cases: Tuple[List[Wagon], List[int]] = [
([("Double cargo ", "Double "), ("wagon", "cargo wagon")], [0, 1]),
([("wagon", "cargo wagon"), ("Double cargo ", "Double ")], [1, 0]),
([("Hello ", "Hello"), ("World", " World")], [0, 1]),
([("", "")], [0]),
([(".", "-.."), (".-", ".."), ("--.", "--")], [2, 1, 2, 0]),
([("bc", "ca"), ("a", "ab"), ("ca", "a"), ("abc", "c")], [1, 0, 1, 3]),
([("yy", "y"), ("xy", "yx"), ("z", "yz")], [0, 1, 1, 1, 1, 1, 2]),
([("", "42"), ("42", "")], [1, 0]),
([("aaaa", ""), ("", "a")], [0, 1, 1, 1, 1]),
([], []),
([("0", "1")], []),
([("0", "1"), (" ", " ")], []),
([("", " ")], []),
([("aa", "a"), ("aa", "a"), ("b", "ab")], [0, 2]),
([("aa", ""), ("ab", ""), ("", "aa")], [0, 2]),
([("aa", ""), ("ab", "")], []),
([("ab", "a"), ("ab", "b")], []),
([("aaaa", ""), ("", "a")], [0, 1, 1, 1, 1]),
([("ab"*1000, ""), ("a", "ab"), ("b", "ab")], [0] + [1,2]*1000),
([("a"*(2**4*3**5), ""), ("", "aa"), ("", "aaa")], [0]+[1]*2**4 +[2]*3**5),
# ([("ab", "a"), ("ab", "b")], []),
# TODO: add more test cases
]
for i, (wagons, example_ordering) in enumerate(test_cases):
print(f"Test case {i:>2}: ", end="")
if not check_ordering(wagons, example_ordering):
sentence0 = ""
sentence1 = ""
for i in example_ordering:
assert 0 <= i < len(wagons), f"wagon index out of range: {i}"
sentence0 += wagons[i][0]
sentence1 += wagons[i][1]
print(sentence0)
print(sentence1)
assert check_ordering(wagons, example_ordering), "example is invalid, dummy."
print(run_test_case(wagons, example_ordering, solver(wagons)))
run_test_cases(my_solver)

View File

@ -0,0 +1,32 @@
mega = 1000000
giga = mega * 1000
steam_joules = 5.8 * mega / 60
# print(steam_joulesw)
# target_power = d * giga / 120
target_power = 2.28 * giga
print(target_power)
turbine_power = 5.82*mega
turbine_count = target_power / turbine_power
print('number of turbines', turbine_count)
duration = 120
turbine_steam_per_second = 60
steam_required = turbine_count * duration * turbine_steam_per_second
steam_tanks = steam_required / 25000
steam_tanks = 12*12
print('steam_tanks', steam_tanks)
tanks_in_row = 6
row_count = steam_tanks / tanks_in_row
turbines_per_row = turbine_count / row_count
print(f'row_count: {row_count}')
print(f'turbines_per_row: {turbines_per_row}')
print(25*6)

View File

38
generators_example.py Normal file
View File

@ -0,0 +1,38 @@
from contextlib import contextmanager
@contextmanager
def my_context_manager():
print("begin")
yield "middle"
print("end")
with my_context_manager() as f:
print(f)
# from typing import Iterator
# # deeply_nested = [[[1, 2, 3], [4, 5]], [[6, 7], [8, 9]]]
# # def flatten(deeply_nested: list) -> Iterator[int]:
# # for a in deeply_nested:
# # for b in a:
# # for c in b:
# # yield c
# # print("here")
# # for inner in flatten(deeply_nested):
# # print(inner)
# def infinite(private_key):
# while True:
# yield from private_key
# for private_key_char, plaintext_char in zip(infinite("hello"), "my plaintext"):
# print("combine", private_key_char, plaintext_char)

1
heartbound OCR Submodule

@ -0,0 +1 @@
Subproject commit 5ed08772af63910da82395c5e1912556473f3bbe

View File

@ -0,0 +1,55 @@
def func(datalist, b=False):
output = []
for data in datalist:
data_a = funcA(data)
data_b = funcB(data_a) if b else data_a
data_c = funcC(data_b) if data.c else data_b
data_d = funcD(data_c)
output.append(data_d)
return output
def func_dom_simple(datalist, want_b=False):
out = []
for data in datalist:
want_c = data.c
data = funcA(data)
if want_b:
data = funcB(data)
if want_c:
data = funcC(data)
data = funcD(data)
out.append(data)
return out
def compose(*functions):
return lambda start: reduce(lambda f, x: f(x), functions, start)
def identity(x):
return x
def func_very_haskell(datalist, want_b=False):
return map(
lambda data: compose(
funcA,
funcB if b else identity,
funcC if data.c else identity,
funcD,
)(data),
datalist,
)
def func_very_haskell2(datalist, want_b=False):
funcAB = compose(funcA, *([funcB] if b else []))
return map(
lambda data: compose(
funcAB,
funcC if data.c else identity,
funcD,
)(data),
datalist,
)

163
light_horn.py Normal file
View File

@ -0,0 +1,163 @@
import pyqtgraph as pg
import numpy as np
import sys
from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QApplication, QHBoxLayout, QLabel, QSizePolicy, QSlider, QSpacerItem, \
QVBoxLayout, QWidget
class Slider(QWidget):
def __init__(self, minimum, maximum, parent=None):
super(Slider, self).__init__(parent=parent)
self.verticalLayout = QVBoxLayout(self)
self.label = QLabel(self)
self.verticalLayout.addWidget(self.label)
self.horizontalLayout = QHBoxLayout()
spacerItem = QSpacerItem(0, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(spacerItem)
self.slider = QSlider(self)
self.slider.setOrientation(Qt.Orientation.Vertical)
self.horizontalLayout.addWidget(self.slider)
spacerItem1 = QSpacerItem(0, 20, QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
self.horizontalLayout.addItem(spacerItem1)
self.verticalLayout.addLayout(self.horizontalLayout)
self.resize(self.sizeHint())
self.minimum = minimum
self.maximum = maximum
self.slider.valueChanged.connect(self._setLabelValue)
self.x = None
self._setLabelValue(self.slider.value())
def setValue(self, value : float):
self.slider.setValue(100 * (value - self.minimum) / (self.maximum - self.minimum))
# self.slider.setValue(value)
self._setLabelValue(self.slider.value())
def _setLabelValue(self, value):
self.x = self.minimum + (float(value) / (self.slider.maximum() - self.slider.minimum())) * (
self.maximum - self.minimum)
self.label.setText("{0:.4g}".format(self.x))
class Widget(QWidget):
def __init__(self, parent=None):
super(Widget, self).__init__(parent=parent)
self.horizontalLayout = QHBoxLayout(self)
self.w1 = Slider(0, 1)
self.w1.setValue(.4141414141414141414141414141)
# self.w1.setValue(50)
self.horizontalLayout.addWidget(self.w1)
self.w2 = Slider(-1, 1)
self.horizontalLayout.addWidget(self.w2)
self.w3 = Slider(-10, 10)
self.horizontalLayout.addWidget(self.w3)
self.w4 = Slider(-10, 10)
self.horizontalLayout.addWidget(self.w4)
self.win = pg.GraphicsWindow(title="Basic plotting examples")
self.horizontalLayout.addWidget(self.win)
self.p6 = self.win.addPlot(title="My Plot")
self.p6.setAspectLocked()
self.horn_upper = self.p6.plot(pen='r')
self.horn_lower = self.p6.plot(pen='r')
self.bounce_path = self.p6.plot(pen='w')
self.update_plot()
self.w1.slider.valueChanged.connect(self.update_plot)
self.w2.slider.valueChanged.connect(self.update_plot)
self.w3.slider.valueChanged.connect(self.update_plot)
self.w4.slider.valueChanged.connect(self.update_plot)
def update_plot(self):
k = self.w1.x
def ii(x):
return np.floor(2**x + .5)
def qq(x):
return ii(x)*x
def horn(x):
return -k *qq(x-np.log2(ii(x)))/ii(x) + 1
return np.exp(-k*x)
def horn_bot(x):
return -horn(x)
#create numpy arrays
#make the numbers large to show that the range shows data from 10000 to all the way 0
xs = np.linspace(-.5,10, 10000)
horn_upper = horn(xs)
horn_lower = -horn_upper
vel = np.array([1,0])
bounce_points = [np.array([xs[0], 1])]
def ray(t: float):
return bounce_points[-1] + t*vel
epsilon = 1e-10
# simulate bounces
for bounce_i in range(100):
bot_t = 0
top_t = .1
t = bot_t
horn_func = horn if vel[1] >= 0 else horn_bot
p = ray(t)
initial_side = p[1] < horn_func(p[0])
t = top_t
# expand search forwards
for i in range(64):
p = ray(top_t)
side = p[1] < horn_func(p[0])
if p[0] < -.5:
break
if side == initial_side:
(bot_t, top_t) = (top_t, top_t+1.1*(top_t - bot_t))
else:
break
if side == initial_side:
# print(f'never found a crossover point at bounce_i={bounce_i}')
# bounce_points.append(p)
break
# bisect
for i in range(64):
t = (bot_t + top_t) / 2
p = ray(t)
side = p[1] < horn_func(p[0])
if side == initial_side:
bot_t = t
else:
top_t = t
if bot_t == top_t:
break
dx = 2*epsilon
dy = horn_func(p[0] + epsilon) - horn_func(p[0] - epsilon)
n = np.array([-dy, dx])
n /= np.sqrt(n.dot(n)) # normal
# reflect
vel = vel - 2*(vel.dot(n))*n
bounce_points.append(p)
# (bot_t, top_t)
bounce_xs = [p[0] for p in bounce_points]
bounce_ys = [p[1] for p in bounce_points]
self.horn_upper.setData(x=xs, y=horn_upper)
self.horn_lower.setData(x=xs, y=horn_lower)
self.bounce_path.setData(x=bounce_xs, y=bounce_ys)
if __name__ == '__main__':
app = QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec())
# if __name__ == '__main__':
# pg.exec()

7
print_shelf.py Normal file
View File

@ -0,0 +1,7 @@
import shelve
import sys
if __name__ == "__main__":
with shelve.open(sys.argv[-1]) as shelf:
for k, v in sorted(shelf.items(), key=lambda tup: (tup[1], tup[0])):
print(k, v)

53
puzzle.py Normal file
View File

@ -0,0 +1,53 @@
from collections import deque
initial = ". . . "
transitions = [
'S SSS ',
' SS S ',
' S S ',
'S SSS ',
'S S S',
' S S ',
' S SS',
'S S S',
]
def toggle(c: str) -> str:
return ' ' if c == '.' else '.'
def apply(state: str, transition: str) -> str:
return ''.join(toggle(c) if t=='S' else c for c, t in zip(state, transition))
print(apply(initial, transitions[0]))
def bfs(start, neighbours_fn, is_finished):
source = {start: None}
q = deque([start])
while q:
s = q.popleft()
if is_finished(s):
steps = [s]
parent = source[s]
while parent is not None:
steps.append(parent)
parent = source[parent]
steps = steps[::-1]
for i in range(len(steps)-1):
steps[i] = steps[i] + f' {neighbours_fn(steps[i]).index(steps[i+1])}'
return steps
for neigh in neighbours_fn(s):
if neigh in source:
continue
source[neigh] = s
q.append(neigh)
raise ValueError('impossible')
print('\n'.join(bfs(
initial,
lambda s: [apply(s, t) for t in transitions],
lambda s: s==' '
# lambda s: s==' . '
)))

1
signals Submodule

@ -0,0 +1 @@
Subproject commit 171c1fae662b711a75f0df8be7a3da3db9aa95b3

View File

@ -1,3 +1,4 @@
from math import sqrt
import shelve import shelve
from collections import deque from collections import deque
from dataclasses import dataclass, replace from dataclasses import dataclass, replace
@ -622,13 +623,13 @@ def shelve_it(file_name):
return decorator return decorator
def get_path_from_heuristic( def get_paths_from_heuristic(
start: Skewb, heuristic_permutation: list[int] start: Skewb, heuristic_permutation: list[int]
) -> list[LowerAntiAxis]: ) -> list[list[LowerAntiAxis]]:
out: list[list[LowerAntiAxis]] = []
s = start s = start
mask = [0 for _ in heuristic_permutation] mask = [0 for _ in heuristic_permutation]
total_path_length = 0 total_path_length = 0
out: list[LowerAntiAxis] = []
for heuristic_i in heuristic_permutation: for heuristic_i in heuristic_permutation:
mask[heuristic_i] = 1 mask[heuristic_i] = 1
@ -643,17 +644,17 @@ def get_path_from_heuristic(
else: else:
path = breadth_first_search(s, step_finished) path = breadth_first_search(s, step_finished)
# print() # print()
# print(f"{mask=} {s=} {path=}") print(f"{mask=} {s=} {path=}")
if path is None: if path is None:
raise ValueError("oh no! solver could not find solution") raise ValueError("oh no! solver could not find solution")
out.extend(path) out.append(path)
s = apply_twists(s, path) s = apply_twists(s, path)
total_path_length += len(path) total_path_length += len(path)
return out return out
def get_total_path_length(start: Skewb, heuristic_permutation: list[int]) -> int: # def get_total_path_length(start: Skewb, heuristic_permutation: list[int]) -> int:
return len(get_path_from_heuristic(start, heuristic_permutation)) # return sum(len(p) for p in get_paths_from_heuristic(start, heuristic_permutation))
# close_to_wrongly_solved = Skewb(top=(R0, B0, O0, G0), bot=(B0, O0, G0, R0), mids=(BY, GRB, ORG, RY, YY)) # close_to_wrongly_solved = Skewb(top=(R0, B0, O0, G0), bot=(B0, O0, G0, R0), mids=(BY, GRB, ORG, RY, YY))
@ -664,13 +665,27 @@ start = near_end
HURISTIC_PERMUTATION_LENGTH = 4 + 4 + 5 HURISTIC_PERMUTATION_LENGTH = 4 + 4 + 5
def quadratic_mean(values: list[float]) -> float:
return sqrt(sum(x * x for x in values) / len(values))
def get_mean_path_length(
start: Skewb,
heuristic_permutation: list[int],
mean_fn: Callable[[list[float]], float] = quadratic_mean,
) -> float:
return mean_fn(
[len(p) for p in get_paths_from_heuristic(start, heuristic_permutation)]
)
@shelve_it("skewb_solver.evaluate_permutation.shelve.sqlite") @shelve_it("skewb_solver.evaluate_permutation.shelve.sqlite")
def evaluate_permutation( def evaluate_permutation(
heuristic_permutation: list[int], seed=4, sample_size: int = 10 heuristic_permutation: list[int], seed=4, sample_size: int = 10
) -> float: ) -> float:
pls = [] pls = []
for i in range(sample_size): for i in range(sample_size):
pl = get_total_path_length(random_skewb(seed=i + seed), heuristic_permutation) pl = get_mean_path_length(random_skewb(seed=i + seed), heuristic_permutation)
print(f"{pl=}") print(f"{pl=}")
pls.append(pl) pls.append(pl)
return sum(pls) / len(pls) return sum(pls) / len(pls)
@ -727,7 +742,7 @@ if __name__ == "__main__":
# print(f"{hp=} {evaluate_permutation(hp, seed=200, sample_size=200)=}") # print(f"{hp=} {evaluate_permutation(hp, seed=200, sample_size=200)=}")
hp = [10, 0, 1, 2, 3, 8, 9, 11, 12, 4, 5, 6, 7] hp = [10, 0, 1, 2, 3, 8, 9, 11, 12, 4, 5, 6, 7]
path = get_path_from_heuristic( path = get_paths_from_heuristic(
Skewb(top=(O0, B0, R0, G0), bot=(B2, R0, G1, O0), mids=(BY, YY, GY, RY, ORB)), Skewb(top=(O0, B0, R0, G0), bot=(B2, R0, G1, O0), mids=(BY, YY, GY, RY, ORB)),
hp, hp,
) )

180
starchild_teaching.py Normal file
View File

@ -0,0 +1,180 @@
from typing import List, Set, Dict, Tuple
# a: int = 2
# b: float = 2.3
# c: str = "lkasldkjaslkjdasd"
# cc: bytes = b"AAA"
# d: None = None
# e: bool = True
# f: bool = False
# k: Tuple[int, str, str] = (1, "hello", "world")
# g: List[int] = [7]
# j: Set[str] = {"friend a", "friend b"}
# i: Dict[str, int] = {"4": 6, "6": 8}
# import collections
# print(dir(collections))
# i["5"] = 7
# print(dict[str])
#
from dataclasses import dataclass
@dataclass
class Card:
suit: str
rank: int # 1 -> ace, 10+ -> jack, queen, king
def __str__(self) -> str:
if self.rank == 1:
rank_str = "ace"
elif self.rank == 10:
rank_str = "jack"
elif self.rank == 11:
rank_str = "queen"
elif self.rank == 12:
rank_str = "king"
else:
rank_str = str(self.rank)
return f"{rank_str} of {self.suit}"
# def __lt__(self, other) -> bool:
# if self.suit != other.suit:
# return self.suit < other.suit
# else:
# return self.rank < other.rank
hand: List[Card] = [
Card(suit="clubs", rank=6),
Card(suit="clubs", rank=5),
Card(suit="spades", rank=1),
Card(suit="hearts", rank=10),
]
def suit_first(card: Card) -> Tuple:
return (card.suit, card.rank)
def rank_first(card: Card) -> Tuple:
return (card.rank, card.suit)
# hand.sort(key=suit_first)
# hand.sort(key=rank_first)
# print(hand)
print((1, "clubs", "cinnamon") < (1, "clubs", "vanilla"))
# O(N ^ 2)
# for i in range(10):
# print(i)
# iterable = range(4)
# iterator = iter(iterable)
# # print(dir(iterator))
# # print(iterator.__str__()[0])
# # print(iterator)
# # print(dir(iterator.__next__))
# print(next.__doc__)
# try:
# while True:
# print(iterator.__next__())
# except StopIteration:
# print("handled!")
[
"__class__",
"__delattr__",
"__dir__",
"__doc__",
"__eq__",
"__format__",
"__ge__",
"__getattribute__",
"__gt__",
"__hash__",
"__init__",
"__init_subclass__",
"__iter__",
"__le__",
"__length_hint__",
"__lt__",
"__ne__",
"__new__",
"__next__",
"__reduce__",
"__reduce_ex__",
"__repr__",
"__setattr__",
"__setstate__",
"__sizeof__",
"__str__",
"__subclasshook__",
]
# print(next(iterator))
# print(next(iterator))
# print(next(iterator))
# print(next(iterator))
# print(next(iterator))
# def sum_until(x: int) -> int:
# """computes the sum of number from 0 to x (inclusive)"""
# to_sum = list(range(x + 1))
# print(to_sum)
# s = 0
# for num in to_sum:
# s += num
# return s
# print(sum_until(3))
# from abc import ABC
# from dataclasses import dataclass
# from typing import List
# class Animal(ABC):
# def make_noise(self):
# pass
# @dataclass
# class Cat(Animal):
# name: str
# def make_noise(self):
# print("meow")
# class Lion(Cat):
# def hunt(self):
# print("nom")
# class Kitty(Cat):
# def make_noise(self):
# print("purr")
# animals: List[Animal] = [
# Kitty(name="Star Child"),
# Lion(name="Simba"),
# ]
# for animal in animals:
# animal.make_noise()

27
try.py Normal file
View File

@ -0,0 +1,27 @@
import sys
class Foo(int):
def __init__(self, x):
super().__init__()
self.q = x + 1
# print(Foo(3).q)
print(sys.getsizeof(3))
print(sys.getsizeof(Foo(3)))
# from typing import List
# def contains_321(ints: List[int]) -> bool:
# try:
# i3 = ints.index(3)
# i2 = ints[i3+1:].index(2)
# i1 = ints[i2+1:].index(1)
# return True
# except ValueError:
# return False
# print(contains_321([3,2,1]))
# print(contains_321([3,1,2,1,1]))
# print(contains_321([1,2,3]))

View File

@ -0,0 +1,295 @@
#!/usr/bin/env python3
"""Simple HTTP Server With Upload.
This module builds on BaseHTTPServer by implementing the standard GET
and HEAD requests in a fairly straightforward manner.
see: https://gist.github.com/UniIsland/3346170
"""
__version__ = "0.1"
__all__ = ["SimpleHTTPRequestHandler"]
__author__ = "bones7456"
__home_page__ = "http://li2z.cn/"
import os
import posixpath
import http.server
import urllib.request, urllib.parse, urllib.error
import html
import shutil
import mimetypes
import re
from io import BytesIO
class SimpleHTTPRequestHandler(http.server.BaseHTTPRequestHandler):
"""Simple HTTP request handler with GET/HEAD/POST commands.
This serves files from the current directory and any of its
subdirectories. The MIME type for files is determined by
calling the .guess_type() method. And can reveive file uploaded
by client.
The GET/HEAD/POST requests are identical except that the HEAD
request omits the actual contents of the file.
"""
server_version = "SimpleHTTPWithUpload/" + __version__
def do_GET(self):
"""Serve a GET request."""
f = self.send_head()
if f:
self.copyfile(f, self.wfile)
f.close()
def do_HEAD(self):
"""Serve a HEAD request."""
f = self.send_head()
if f:
f.close()
def do_POST(self):
"""Serve a POST request."""
r, info = self.deal_post_data()
print((r, info, "by: ", self.client_address))
f = BytesIO()
f.write(b'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
f.write(b"<html>\n<title>Upload Result Page</title>\n")
f.write(b"<body>\n<h2>Upload Result Page</h2>\n")
f.write(b"<hr>\n")
if r:
f.write(b"<strong>Success:</strong>")
else:
f.write(b"<strong>Failed:</strong>")
f.write(info.encode())
f.write(("<br><a href=\"%s\">back</a>" % self.headers['referer']).encode())
f.write(b"<hr><small>Powerd By: bones7456, check new version at ")
f.write(b"<a href=\"http://li2z.cn/?s=SimpleHTTPServerWithUpload\">")
f.write(b"here</a>.</small></body>\n</html>\n")
length = f.tell()
f.seek(0)
self.send_response(200)
self.send_header("Content-type", "text/html")
self.send_header("Content-Length", str(length))
self.end_headers()
if f:
self.copyfile(f, self.wfile)
f.close()
def deal_post_data(self):
content_type = self.headers['content-type']
if not content_type:
return (False, "Content-Type header doesn't contain boundary")
boundary = content_type.split("=")[1].encode()
remainbytes = int(self.headers['content-length'])
line = self.rfile.readline()
remainbytes -= len(line)
if not boundary in line:
return (False, "Content NOT begin with boundary")
line = self.rfile.readline()
remainbytes -= len(line)
fn = re.findall(r'Content-Disposition.*name="file"; filename="(.*)"', line.decode())
if not fn:
return (False, "Can't find out file name...")
path = self.translate_path(self.path)
fn = os.path.join(path, fn[0])
line = self.rfile.readline()
remainbytes -= len(line)
line = self.rfile.readline()
remainbytes -= len(line)
try:
out = open(fn, 'wb')
except IOError:
return (False, "Can't create file to write, do you have permission to write?")
preline = self.rfile.readline()
remainbytes -= len(preline)
while remainbytes > 0:
line = self.rfile.readline()
remainbytes -= len(line)
if boundary in line:
preline = preline[0:-1]
if preline.endswith(b'\r'):
preline = preline[0:-1]
out.write(preline)
out.close()
return (True, "File '%s' upload success!" % fn)
else:
out.write(preline)
preline = line
return (False, "Unexpect Ends of data.")
def send_head(self):
"""Common code for GET and HEAD commands.
This sends the response code and MIME headers.
Return value is either a file object (which has to be copied
to the outputfile by the caller unless the command was HEAD,
and must be closed by the caller under all circumstances), or
None, in which case the caller has nothing further to do.
"""
path = self.translate_path(self.path)
f = None
if os.path.isdir(path):
if not self.path.endswith('/'):
# redirect browser - doing basically what apache does
self.send_response(301)
self.send_header("Location", self.path + "/")
self.end_headers()
return None
for index in "index.html", "index.htm":
index = os.path.join(path, index)
if os.path.exists(index):
path = index
break
else:
return self.list_directory(path)
ctype = self.guess_type(path)
try:
# Always read in binary mode. Opening files in text mode may cause
# newline translations, making the actual size of the content
# transmitted *less* than the content-length!
f = open(path, 'rb')
except IOError:
self.send_error(404, "File not found")
return None
self.send_response(200)
self.send_header("Content-type", ctype)
fs = os.fstat(f.fileno())
self.send_header("Content-Length", str(fs[6]))
self.send_header("Last-Modified", self.date_time_string(fs.st_mtime))
self.end_headers()
return f
def list_directory(self, path):
"""Helper to produce a directory listing (absent index.html).
Return value is either a file object, or None (indicating an
error). In either case, the headers are sent, making the
interface the same as for send_head().
"""
try:
list = os.listdir(path)
except os.error:
self.send_error(404, "No permission to list directory")
return None
list.sort(key=lambda a: a.lower())
f = BytesIO()
displaypath = html.escape(urllib.parse.unquote(self.path))
f.write(b'<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">')
f.write(("<html>\n<title>Directory listing for %s</title>\n" % displaypath).encode())
f.write(("<body>\n<h2>Directory listing for %s</h2>\n" % displaypath).encode())
f.write(b"<hr>\n")
f.write(b"<form ENCTYPE=\"multipart/form-data\" method=\"post\">")
f.write(b"<input name=\"file\" type=\"file\"/>")
f.write(b"<input type=\"submit\" value=\"upload\"/></form>\n")
f.write(b"<hr>\n<ul>\n")
for name in list:
fullname = os.path.join(path, name)
displayname = linkname = name
# Append / for directories or @ for symbolic links
if os.path.isdir(fullname):
displayname = name + "/"
linkname = name + "/"
if os.path.islink(fullname):
displayname = name + "@"
# Note: a link to a directory displays with @ and links with /
f.write(('<li><a href="%s">%s</a>\n'
% (urllib.parse.quote(linkname), html.escape(displayname))).encode())
f.write(b"</ul>\n<hr>\n</body>\n</html>\n")
length = f.tell()
f.seek(0)
self.send_response(200)
self.send_header("Content-type", "text/html")
self.send_header("Content-Length", str(length))
self.end_headers()
return f
def translate_path(self, path):
"""Translate a /-separated PATH to the local filename syntax.
Components that mean special things to the local file system
(e.g. drive or directory names) are ignored. (XXX They should
probably be diagnosed.)
"""
# abandon query parameters
path = path.split('?',1)[0]
path = path.split('#',1)[0]
path = posixpath.normpath(urllib.parse.unquote(path))
words = path.split('/')
words = [_f for _f in words if _f]
path = os.getcwd()
for word in words:
drive, word = os.path.splitdrive(word)
head, word = os.path.split(word)
if word in (os.curdir, os.pardir): continue
path = os.path.join(path, word)
return path
def copyfile(self, source, outputfile):
"""Copy all data between two file objects.
The SOURCE argument is a file object open for reading
(or anything with a read() method) and the DESTINATION
argument is a file object open for writing (or
anything with a write() method).
The only reason for overriding this would be to change
the block size or perhaps to replace newlines by CRLF
-- note however that this the default server uses this
to copy binary data as well.
"""
shutil.copyfileobj(source, outputfile)
def guess_type(self, path):
"""Guess the type of a file.
Argument is a PATH (a filename).
Return value is a string of the form type/subtype,
usable for a MIME Content-type header.
The default implementation looks the file's extension
up in the table self.extensions_map, using application/octet-stream
as a default; however it would be permissible (if
slow) to look inside the data to make a better guess.
"""
base, ext = posixpath.splitext(path)
if ext in self.extensions_map:
return self.extensions_map[ext]
ext = ext.lower()
if ext in self.extensions_map:
return self.extensions_map[ext]
else:
return self.extensions_map['']
if not mimetypes.inited:
mimetypes.init() # try to read system mime.types
extensions_map = mimetypes.types_map.copy()
extensions_map.update({
'': 'application/octet-stream', # Default
'.py': 'text/plain',
'.c': 'text/plain',
'.h': 'text/plain',
})
def test(HandlerClass = SimpleHTTPRequestHandler,
ServerClass = http.server.HTTPServer):
http.server.test(HandlerClass, ServerClass)
if __name__ == '__main__':
test()

71
when_to_quit.py Normal file
View File

@ -0,0 +1,71 @@
import numpy as np
import pyqtgraph as pg
from pyqtgraph.Qt import QtCore
initial_dollar = 100
factor_if_heads = 1.8
factor_if_tails = 0.5
population_size = 10000
num_tosses_visualized = 500
def want_to_play(money, toss) -> bool:
return True
if toss > 4:
return False
rng = np.random.default_rng(seed=4)
def evolve(want_to_play, money, toss):
# if money < 2:
# return 1
# if not want_to_play(money, toss):
# return money
if rng.choice([0,1]):
return money * factor_if_heads
else:
return money * factor_if_tails
population_histories = [[initial_dollar] for _ in range(population_size)]
for toss in range(num_tosses_visualized):
for hist in population_histories:
hist.append(evolve(want_to_play, hist[-1], toss))
app = pg.mkQApp("Plotting Example")
#mw = QtWidgets.QMainWindow()
#mw.resize(800,800)
win = pg.GraphicsLayoutWidget(show=True, title="Basic plotting examples")
win.resize(1000,600)
win.setWindowTitle('pyqtgraph example: Plotting')
# Enable antialiasing for prettier plots
pg.setConfigOptions(antialias=True)
p2 = win.addPlot(title="Multiple curves")
population_histories = np.array(population_histories)
# population_histories = np.log(population_histories + 1)
p2.setLogMode(False, True)
p2.plot(population_histories.mean(0), pen=(255, 0, 0))
p2.plot(np.median(population_histories, 0), pen=(0, 255, 0))
for percentile in range(0, 101):
print('percentile', percentile)
p2.plot(np.percentile(population_histories, percentile, 0), pen=(100, 10, 0))
# for hist in population_histories:
# p2.plot(hist, pen=(33,33,33))
# p2.plot(np.random.normal(size=100), pen=(255,0,0), name="Red curve")
# p2.plot(np.random.normal(size=110)+5, pen=(0,255,0), name="Green curve")
# p2.plot(np.random.normal(size=120)+10, pen=(0,0,255), name="Blue curve")
if __name__ == '__main__':
pg.exec()