Rewritten without async

This commit is contained in:
Pyrex 2024-03-09 23:29:13 -08:00
parent 71bba236c0
commit 3f505bba71

180
main.py
View File

@ -1,36 +1,107 @@
import asyncio from functools import partial
import contextlib
import random import random
class Cell(object):
def __init__(self):
self.is_set = False
self.value = None
def set(self, x):
self.is_set = True
self.value = x
class Merge(object):
def __init__(self, xs, ys, on_complete):
self.xs = xs[:]
self.ys = ys[:]
self.output = []
self.on_complete = on_complete
self.running = True
self.settle()
def touch(self):
# print(f"Touching: {self}")
if ask(self.xs[0], self.ys[0]):
self.output.append(self.xs.pop(0))
else:
self.output.append(self.ys.pop(0))
self.settle()
def settle(self):
if len(self.xs) == 0:
self.output += self.ys
self.ys = []
self.complete()
return
if len(self.ys) == 0:
self.output += self.xs
self.xs = []
self.complete()
return
def complete(self):
self.on_complete(self.output)
self.running = False
def __repr__(self):
return f"Merge({self.xs}, {self.ys}, {self.output})"
def _sort(xs, merges, on_complete):
if len(xs) < 2:
on_complete(xs[:])
return
half = len(xs) // 2
fst = xs[:half]
snd = xs[half:]
sorted_fst = Cell()
sorted_snd = Cell()
def cb(setter, value):
setter(value)
if sorted_fst.is_set and sorted_snd.is_set:
new_merge = Merge(sorted_fst.value, sorted_snd.value, on_complete)
merges.append(new_merge)
_sort(fst, merges, partial(cb, sorted_fst.set))
_sort(snd, merges, partial(cb, sorted_snd.set))
def merge_sort(xs):
merges = []
result = Cell()
_sort(xs, merges, result.set)
while True:
# replace in place
for i in reversed(range(len(merges))):
if not merges[i].running:
merges.pop(i)
if len(merges) == 0:
break
# print(f"Number of ongoing merges: {merges}")
# print(f"Number of ongoing merges: {len(merges)}")
random.shuffle(merges)
for m in merges[:]:
m.touch()
assert result.is_set
return result.value
TRUTHY = "y yes 1 t true aye left up top first p".split() TRUTHY = "y yes 1 t true aye left up top first p".split()
FALSEY = "n no 0 2 f false nay right down bottom second nil".split() FALSEY = "n no 0 2 f false nay right down bottom second nil".split()
def sort(options): def ask(x, y):
loop = asyncio.new_event_loop()
postponed_questions = []
# counter for how many threads can still resume
# if it's 0, then we need to ask some questions or else we're deadlocked
n_active = 0
@contextlib.asynccontextmanager
async def semaphore(amt):
nonlocal n_active
n_active += amt
yield
n_active -= amt
def deadlocked():
return n_active == 0
async def ask(x, y):
async with semaphore(-1):
future = loop.create_future()
def ask_question():
if random.choice([False, True]): if random.choice([False, True]):
yes_result = True yes_result = True
a, b = x, y a, b = x, y
@ -41,72 +112,23 @@ def sort(options):
while True: while True:
choice = input(f"Is {a} better than {b}? ") choice = input(f"Is {a} better than {b}? ")
if choice in TRUTHY: if choice in TRUTHY:
future.set_result(yes_result) return yes_result
return
if choice in FALSEY: if choice in FALSEY:
future.set_result(not yes_result) return not yes_result
return
print(f"Huh? I didn't understand {choice}. Answer \"y\" or \"n\".") print(f"Huh? I didn't understand {choice}. Answer \"y\" or \"n\".")
postponed_questions.append(ask_question)
if deadlocked():
await ask_questions()
return await future
async def merge(xs, ys):
async with semaphore(1):
xs = xs[:]
ys = ys[:]
out = []
while len(xs) > 0 and len(ys) > 0:
if await ask(xs[0], ys[0]):
out.append(xs.pop(0))
else:
out.append(ys.pop(0))
out.extend(xs)
out.extend(ys)
return out
async def merge_sort(xs):
if len(xs) < 2:
return xs[:]
half = len(xs) // 2
fst_half, snd_half = await asyncio.gather(
merge_sort(xs[:half]),
merge_sort(xs[half:])
)
return await merge(fst_half, snd_half)
async def ask_questions():
ppq = postponed_questions[:]
postponed_questions.clear()
random.shuffle(ppq)
for q in ppq:
q()
result_future = merge_sort(options)
result = loop.run_until_complete(result_future)
return result
def main(): def main():
with open("input.txt", "rt") as f_in: with open("input.txt", "rt") as f_in:
lines_in = [i.strip() for i in f_in.read().split("\n") if i.strip()] lines_in = [i.strip() for i in f_in.read().split("\n") if i.strip()]
random.shuffle(lines_in) random.shuffle(lines_in)
results = sort(lines_in) results = merge_sort(lines_in)
with open("output.txt", "wt") as f_out: with open("output.txt", "wt") as f_out:
f_out.writelines(results) f_out.write("\n".join(results))
if __name__ == '__main__': if __name__ == '__main__':