scripts: rewrite live script
This commit is contained in:
parent
93c80d9f0c
commit
5ee5268431
5 changed files with 151 additions and 75 deletions
|
@ -30,45 +30,6 @@ from heapq import heapify, heappop, heappush
|
|||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import pyperclip
|
||||
import z3
|
||||
|
||||
YEAR: int | None = None
|
||||
DAY: int | None = None
|
||||
|
||||
|
||||
def read_input(year: int, day: int, example: int | None = None) -> str:
|
||||
global YEAR, DAY
|
||||
if example is None:
|
||||
YEAR = year
|
||||
DAY = day
|
||||
if example is None:
|
||||
f = Path(__file__).parent / f"../../.cache/{year}/{day}"
|
||||
else:
|
||||
f = Path(__file__).parent / f"../../examples/{year}/{day}/{example}"
|
||||
return f.read_text()
|
||||
|
||||
|
||||
def ans(answer):
|
||||
print(answer)
|
||||
pyperclip.copy(str(answer))
|
||||
|
||||
if YEAR is None or DAY is None:
|
||||
print("\n(cannot submit solution of example)")
|
||||
return
|
||||
|
||||
submit_part = input("\nSubmit solution? level=")
|
||||
if submit_part not in ["1", "2"]:
|
||||
return
|
||||
|
||||
import bs4
|
||||
import requests
|
||||
|
||||
session = (Path(__file__).parent / f"../../.cache/session").read_text()
|
||||
resp = requests.post(
|
||||
f"https://adventofcode.com/{YEAR}/day/{DAY}/answer",
|
||||
cookies={"session": session},
|
||||
data={"level": submit_part, "answer": answer},
|
||||
).text
|
||||
bs = bs4.BeautifulSoup(resp, "html.parser")
|
||||
print(bs.main.article.p.text)
|
||||
|
|
28
flake.nix
28
flake.nix
|
@ -32,9 +32,6 @@
|
|||
with p; [
|
||||
z3
|
||||
numpy
|
||||
pyperclip
|
||||
requests
|
||||
beautifulsoup4
|
||||
networkx
|
||||
sympy
|
||||
]);
|
||||
|
@ -88,23 +85,18 @@
|
|||
beautifulsoup4
|
||||
];
|
||||
};
|
||||
live = pkgs.stdenvNoCC.mkDerivation {
|
||||
live = pkgs.python3.pkgs.buildPythonApplication {
|
||||
name = "aoc-live";
|
||||
pyproject = false;
|
||||
dontUnpack = true;
|
||||
nativeBuildInputs = with pkgs; [makeWrapper];
|
||||
installPhase = ''
|
||||
mkdir -p $out/bin
|
||||
cp ${./scripts/live.sh} $out/bin/aoc-live
|
||||
chmod +x $out/bin/*
|
||||
wrapProgram $out/bin/* --set PATH ${with pkgs;
|
||||
lib.makeBinPath [
|
||||
python
|
||||
bash
|
||||
coreutils
|
||||
inotify-tools
|
||||
wl-clipboard
|
||||
]}
|
||||
'';
|
||||
installPhase = "mkdir -p $out/bin; cp ${./scripts/live.py} $out/bin/aoc-live; chmod +x $out/bin/*";
|
||||
makeWrapperArgs = ["--set AOC_PYTHON ${python}/bin/python"];
|
||||
propagatedBuildInputs = with pkgs.python3.pkgs; [
|
||||
requests
|
||||
beautifulsoup4
|
||||
pyperclip
|
||||
watchdog
|
||||
];
|
||||
};
|
||||
};
|
||||
})
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from lib import *
|
||||
|
||||
input = read_input(2024, 1)
|
||||
input = open(0).read().strip()
|
||||
|
||||
blocks = input.split("\n\n")
|
||||
lines = input.splitlines()
|
||||
|
@ -11,4 +11,4 @@ for line in lines:
|
|||
match = re.match(r"^$", line)
|
||||
|
||||
|
||||
ans(out)
|
||||
print(out)
|
||||
|
|
139
scripts/live.py
Normal file
139
scripts/live.py
Normal file
|
@ -0,0 +1,139 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from multiprocessing import Process
|
||||
from pathlib import Path
|
||||
from threading import Thread
|
||||
from typing import Any, cast
|
||||
|
||||
import bs4
|
||||
import pyperclip
|
||||
import requests
|
||||
from watchdog.events import FileSystemEvent, FileSystemEventHandler
|
||||
from watchdog.observers import Observer
|
||||
|
||||
root_dir = Path(os.getcwd())
|
||||
while not (root_dir / "flake.nix").is_file():
|
||||
root_dir = root_dir.parent
|
||||
os.chdir(root_dir / "live")
|
||||
|
||||
if len(sys.argv) != 3:
|
||||
print(f"usage: {sys.argv[0]} YEAR DAY")
|
||||
exit(1)
|
||||
|
||||
year, day = map(int, sys.argv[1:3])
|
||||
|
||||
|
||||
def run_solution(input: Path) -> str | None:
|
||||
if not input.exists():
|
||||
print(f"\033[1m\033[31mInput file {input} does not exist\033[0m")
|
||||
return None
|
||||
py = os.environ.get("AOC_PYTHON", "python")
|
||||
inp = input.read_bytes()
|
||||
print(f"cmd: {py} live.py | input: {input.resolve().relative_to(root_dir)}")
|
||||
start = time.time()
|
||||
out = subprocess.run([py, "live.py"], input=inp, capture_output=True)
|
||||
delta = time.time() - start
|
||||
print(f"\033[3{'12'[out.returncode == 0]}mexit code: {out.returncode} | delta: {delta:.2f}s\033[0m")
|
||||
print("--- stderr ---")
|
||||
sys.stdout.buffer.write(out.stderr)
|
||||
print("--- stdout ---")
|
||||
sys.stdout.buffer.write(out.stdout)
|
||||
|
||||
if out.returncode != 0:
|
||||
return None
|
||||
|
||||
try:
|
||||
return out.stdout.decode().strip().splitlines()[-1].strip()
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def trigger():
|
||||
|
||||
print(end="\033[H\033[2J\033[0m")
|
||||
ex_dir = Path(f"../examples/{year}/{day}")
|
||||
for ex in sorted(
|
||||
(x for x in (ex_dir.iterdir() if ex_dir.is_dir() else []) if x.name.isnumeric()), key=lambda f: int(f.name)
|
||||
):
|
||||
n = int(ex.name)
|
||||
print(f"\033[1m\033[34m----- Example {n} -----\033[0m")
|
||||
run_solution(ex)
|
||||
print()
|
||||
|
||||
print("\033[1m\033[34m----- Puzzle Input -----\033[0m")
|
||||
ans = run_solution(Path(f"../.cache/{year}/{day}"))
|
||||
if ans is not None:
|
||||
print(f"\n\033[1m\033[32mAnswer: {ans}\033[0m")
|
||||
pyperclip.copy(ans)
|
||||
if (part := input(f"Submit? level=")) in ["1", "2"]:
|
||||
print(f"(submitting answer for part {part})")
|
||||
session = (root_dir / ".cache/session").read_text().strip()
|
||||
resp = requests.post(
|
||||
f"https://adventofcode.com/{year}/day/{day}/answer",
|
||||
cookies={"session": session},
|
||||
data={"level": part, "answer": ans},
|
||||
).text
|
||||
bs = bs4.BeautifulSoup(resp, "html.parser")
|
||||
resp = cast(Any, bs).main.article.p.text
|
||||
ok = resp.startswith("That's the right answer!")
|
||||
print(f"\033[1m\033[3{'12'[ok]}m{resp}\033[0m")
|
||||
else:
|
||||
print("\033[1m\033[31m(failed to find answer in program output)\033[0m")
|
||||
print("(waiting for changes to live.py)")
|
||||
|
||||
|
||||
proc: Process | None = None
|
||||
|
||||
|
||||
def spawn_trigger_process():
|
||||
global proc
|
||||
|
||||
if proc is not None and proc.is_alive():
|
||||
print("(process killed)")
|
||||
proc.kill()
|
||||
|
||||
def trigger_wrapper():
|
||||
sys.stdin = open(0)
|
||||
while True:
|
||||
trigger()
|
||||
input()
|
||||
|
||||
proc = Process(target=trigger_wrapper)
|
||||
proc.start()
|
||||
|
||||
|
||||
class Handler(FileSystemEventHandler):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.cnt = 0
|
||||
|
||||
def on_modified(self, event: FileSystemEvent) -> None:
|
||||
if event.src_path != "./live.py":
|
||||
return
|
||||
|
||||
self.cnt += 1
|
||||
cnt = self.cnt
|
||||
|
||||
def inner():
|
||||
time.sleep(0.1)
|
||||
if self.cnt == cnt:
|
||||
spawn_trigger_process()
|
||||
|
||||
t = Thread(target=inner)
|
||||
t.start()
|
||||
|
||||
|
||||
spawn_trigger_process()
|
||||
|
||||
handler = Handler()
|
||||
observer = Observer()
|
||||
observer.schedule(handler, ".", recursive=True)
|
||||
observer.start()
|
||||
|
||||
while True:
|
||||
time.sleep(1)
|
|
@ -1,16 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -m
|
||||
|
||||
while ! [[ -e flake.nix ]]; do cd ..; done
|
||||
cd live
|
||||
|
||||
while true; do
|
||||
printf '\033[H\033[J\033[3J\033[37m'
|
||||
python live.py &
|
||||
py=$!
|
||||
(inotifywait -qqe modify live.py; kill $py) &
|
||||
wa=$!
|
||||
fg %-
|
||||
wait $wa
|
||||
done
|
Loading…
Add table
Add a link
Reference in a new issue