diff --git a/screenshot_integration.py b/screenshot_integration.py new file mode 100644 index 0000000..c6a2335 --- /dev/null +++ b/screenshot_integration.py @@ -0,0 +1,234 @@ +#!/usr/bin/env python3 +""" +Take screenshots at key moments during a synthetic game that exercises all fixed bugs. +Writes game-state.json at each step and captures the UI. +""" +import json, os, sys, time, subprocess +from datetime import datetime, timezone +from playwright.sync_api import sync_playwright + +sys.path.insert(0, os.path.dirname(__file__)) +from monop_parser import MonopParser, BOARD + +SITE_DIR = os.path.join(os.path.dirname(__file__), "site") +STATE_PATH = os.path.join(SITE_DIR, "game-state.json") +SCREENSHOTS_DIR = os.path.join(os.path.dirname(__file__), "screenshots") +os.makedirs(SCREENSHOTS_DIR, exist_ok=True) + +_t = [0] +def ts(): + _t[0] += 1 + return f"2026-01-01 00:{_t[0]//60:02d}:{_t[0]%60:02d}" + +def feed(p, lines): + for line in lines: + p.parse_line(f"{ts()}\t{line}") + +def write_state(p): + state = p.get_state() + state["lastUpdated"] = datetime.now(timezone.utc).isoformat() + with open(STATE_PATH, "w") as f: + json.dump(state, f, indent=2) + +def screenshot(page, name, wait=3000): + page.wait_for_timeout(wait) + path = os.path.join(SCREENSHOTS_DIR, f"{name}.png") + page.screenshot(path=path, full_page=True) + print(f"[screenshot] {name}") + +def main(): + server = subprocess.Popen( + [sys.executable, "-m", "http.server", "9998", "--directory", SITE_DIR], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL + ) + time.sleep(1) + + try: + with sync_playwright() as pw: + browser = pw.chromium.launch() + page = browser.new_page(viewport={"width": 1280, "height": 1600}) + + p = MonopParser() + + # === Setup: 3-player game === + feed(p, [ + "monop\tHow many players? ", + "monop\tPlayer 1, say 'me' please.", + "monop\talice (1) rolls 3", + "monop\tPlayer 2, say 'me' please.", + "monop\tbob (2) rolls 5", + "monop\tPlayer 3, say 'me' please.", + "monop\tcharlie (3) rolls 9", + "monop\tcharlie (3) goes first", + "monop\tcharlie (3) (cash $1500) on === GO ===", + "monop\t-- Command: ", + ]) + + # charlie buys Mediterranean + Oriental, gets lightblue monopoly + feed(p, [ + "charlie\t.", + "monop\troll is 1, 0", + "monop\tThat puts you on Mediterranean ave. (P)", + "monop\tThat would cost $60", + "monop\tDo you want to buy? ", + "charlie\t.y", + "monop\talice (1) (cash $1500) on === GO ===", + "monop\t-- Command: ", + ]) + g = p.game + g.property_owner[6] = 3 # Oriental + g.property_owner[8] = 3 # Vermont + g.property_owner[9] = 3 # Connecticut + + # alice buys Baltic, bob buys Reading RR + B&O RR + feed(p, [ + "alice\t.", + "monop\troll is 1, 2", + "monop\tThat puts you on Baltic ave. (P)", + "monop\tThat would cost $60", + "monop\tDo you want to buy? ", + "alice\t.y", + "monop\tbob (2) (cash $1500) on === GO ===", + "monop\t-- Command: ", + "bob\t.", + "monop\troll is 2, 3", + "monop\tThat puts you on Reading RR", + "monop\tThat would cost $200", + "monop\tDo you want to buy? ", + "bob\t.y", + "monop\tcharlie (3) (cash $1440) on Mediterranean ave. (P)", + "monop\t-- Command: ", + ]) + g.property_owner[25] = 2 # B&O RR for bob + + # === Screenshot 1: Mid-game with properties owned === + write_state(p) + page.goto("http://localhost:9998") + screenshot(page, "01_midgame_properties_owned") + + # === charlie buys houses (House sub-parser) === + feed(p, [ + "charlie\t.buy", + "monop\tOriental ave. (L) (0) Vermont ave. (L) (0) Connecticut ave. (L) (0) ", + "monop\tHouses will cost $50", + "monop\tHow many houses do you wish to buy for", + "monop\tOriental ave. (L) (0): ", + "charlie\t.3", + "monop\tVermont ave. (L) (0): ", + "charlie\t.3", + "monop\tConnecticut ave. (L) (0): ", + "charlie\t.3", + "monop\tYou asked for 9 houses for $450", + "monop\tIs that ok? ", + "charlie\t.y", + "monop\tcharlie (3) (cash $990) on Mediterranean ave. (P)", + "monop\t-- Command: ", + ]) + + # === Screenshot 2: Houses visible on board === + write_state(p) + page.reload() + screenshot(page, "02_houses_built") + + # === Trade: bob gives Reading RR to charlie for $300 === + feed(p, [ + "monop\talice (1) (cash $1440) on Baltic ave. (P)", + "monop\t-- Command: ", + "alice\t.", + "monop\troll is 2, 1", + "monop\tThat puts you on Oriental ave. (L)", + "monop\tOwned by charlie", + "monop\twith 3 houses, rent is 90", + "monop\tbob (2) (cash $1300) on Reading RR", + "monop\t-- Command: ", + "monop\tPlayer bob (2) gives:", + "monop\t Reading RR 2 200 1", + "monop\tPlayer charlie (3) gives:", + "monop\t $300", + "monop\tcharlie, is the trade ok? ", + "charlie\t.y", + "monop\tTrade is done!", + "monop\tbob (2) (cash $1600) on Reading RR", + "monop\t-- Command: ", + ]) + + # === Screenshot 3: After trade — Reading RR now charlie's === + write_state(p) + page.reload() + screenshot(page, "03_after_trade") + + # === alice mortgages Baltic === + feed(p, [ + "bob\t.", + "monop\troll is 1, 2", + "monop\tThat puts you on Vermont ave. (L)", + "monop\tOwned by charlie", + "monop\twith 3 houses, rent is 90", + "monop\talice (1) (cash $1350) on Oriental ave. (L)", + "monop\t-- Command: ", + "alice\t.mor", + "monop\tWhich property do you want to mortgage? ", + "alice\t.baltic", + "monop\tThat got you $30", + "monop\talice (1) (cash $1380) on Oriental ave. (L)", + "monop\t-- Command: ", + ]) + + # === Screenshot 4: Baltic mortgaged === + write_state(p) + page.reload() + screenshot(page, "04_baltic_mortgaged") + + # === bob resigns to bank === + feed(p, [ + "alice\t.", + "monop\troll is 1, 3", + "monop\tThat puts you on Connecticut ave. (L)", + "monop\tOwned by charlie", + "monop\twith 3 houses, rent is 90", + "monop\tbob (2) (cash $1510) on Vermont ave. (L)", + "monop\t-- Command: ", + "bob\t.resign", + "monop\tWho do you wish to resign to? ", + "bob\t.bank", + "monop\tDo you really want to resign? ", + "bob\t.y", + "monop\tresigning to bank", + "monop\tcharlie (1) (cash $1380) on Mediterranean ave. (P)", + "monop\t-- Command: ", + ]) + + # === Screenshot 5: Bob bankrupt === + write_state(p) + page.reload() + screenshot(page, "05_bob_bankrupt") + + # === alice resigns to charlie — game over === + feed(p, [ + "charlie\t.", + "monop\troll is 2, 3", + "monop\tThat puts you on Oriental ave. (L)", + "monop\tYou own it.", + "monop\talice (1) (cash $1260) on Connecticut ave. (L)", + "monop\t-- Command: ", + "monop\tYou would resign to charlie", + "monop\tDo you really want to resign? ", + "alice\t.y", + "monop\tresigning to player", + "monop\tTrade is done!", + "monop\tThen charlie WINS!!!!!", + ]) + + # === Screenshot 6: Game over === + write_state(p) + page.reload() + screenshot(page, "06_game_over") + + browser.close() + print("\nAll screenshots captured!") + finally: + server.terminate() + server.wait() + +if __name__ == "__main__": + main() diff --git a/screenshot_states.py b/screenshot_states.py new file mode 100644 index 0000000..168717c --- /dev/null +++ b/screenshot_states.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python3 +"""Take screenshots of specific game states by writing synthetic game-state.json.""" +import json, os, sys, time, subprocess +from datetime import datetime, timezone +from playwright.sync_api import sync_playwright + +SITE_DIR = os.path.join(os.path.dirname(__file__), "site") +STATE_PATH = os.path.join(SITE_DIR, "game-state.json") +SCREENSHOTS_DIR = os.path.join(os.path.dirname(__file__), "screenshots") +os.makedirs(SCREENSHOTS_DIR, exist_ok=True) +sys.path.insert(0, os.path.dirname(__file__)) + +from monop_parser import MonopParser +_p = MonopParser() +_p.parse_line("2026-01-01 00:00:00\tmonop\tHow many players? ") +_p.parse_line("2026-01-01 00:00:00\talice\t3") +SQUARES = _p.get_state()["squares"] + +def make_player(name, number, money, location, properties=None, inJail=False, bankrupt=False): + d = { + "name": name, "number": number, "money": money, "location": location, + "inJail": inJail, "jailTurns": 0, "doublesCount": 0, + "getOutOfJailFreeCards": 0, "properties": properties or [], + } + if bankrupt: + d["bankrupt"] = True + return d + +def write_state(state): + state["lastUpdated"] = datetime.now(timezone.utc).isoformat() + state.setdefault("squares", SQUARES) + with open(STATE_PATH, "w") as f: + json.dump(state, f, indent=2) + +def take_screenshot(page, name): + page.goto("http://localhost:9998") + page.wait_for_timeout(4000) + path = os.path.join(SCREENSHOTS_DIR, f"{name}.png") + page.screenshot(path=path, full_page=True) + print(f"[screenshot] {path}") + +def main(): + server = subprocess.Popen( + [sys.executable, "-m", "http.server", "9998", "--directory", SITE_DIR], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL + ) + time.sleep(1) + + try: + with sync_playwright() as p: + browser = p.chromium.launch() + page = browser.new_page(viewport={"width": 1280, "height": 1600}) + + # 1: Single player joined during setup + write_state({ + "players": [make_player("alice", 1, 1500, 0)], + "currentPlayer": None, + "log": [ + {"text": "Game for 3 players", "player": None, "timestamp": "2026-01-01 00:00:00"}, + {"text": "alice joined the game", "player": "alice", "timestamp": "2026-01-01 00:00:01"}, + ], + "phase": "setup", + "numPlayersExpected": 3, + }) + take_screenshot(page, "single_player_joined") + + # 2: Game over — players bankrupt + write_state({ + "players": [ + make_player("alice", 1, 4200, 24, [1, 3, 5, 11, 13, 14, 15, 21, 23, 24, 25, 37, 39]), + make_player("bob", 2, 0, 18, bankrupt=True), + make_player("charlie", 3, 0, 6, bankrupt=True), + ], + "currentPlayer": 1, + "log": [ + {"text": "bob is bankrupt!", "player": "bob", "timestamp": "2026-01-01 01:30:00"}, + {"text": "charlie is bankrupt!", "player": "charlie", "timestamp": "2026-01-01 01:45:00"}, + {"text": "alice WINS!", "player": "alice", "timestamp": "2026-01-01 01:45:01"}, + ], + "phase": "over", + }) + take_screenshot(page, "player_bankrupt_game_over") + + browser.close() + finally: + server.terminate() + server.wait() + +if __name__ == "__main__": + main() diff --git a/screenshots/01_midgame_properties_owned.png b/screenshots/01_midgame_properties_owned.png new file mode 100644 index 0000000..9a334a5 Binary files /dev/null and b/screenshots/01_midgame_properties_owned.png differ diff --git a/screenshots/02_houses_built.png b/screenshots/02_houses_built.png new file mode 100644 index 0000000..f6d7f71 Binary files /dev/null and b/screenshots/02_houses_built.png differ diff --git a/screenshots/03_after_trade.png b/screenshots/03_after_trade.png new file mode 100644 index 0000000..a492911 Binary files /dev/null and b/screenshots/03_after_trade.png differ diff --git a/screenshots/04_baltic_mortgaged.png b/screenshots/04_baltic_mortgaged.png new file mode 100644 index 0000000..61fd2d6 Binary files /dev/null and b/screenshots/04_baltic_mortgaged.png differ diff --git a/screenshots/05_bob_bankrupt.png b/screenshots/05_bob_bankrupt.png new file mode 100644 index 0000000..2374b0d Binary files /dev/null and b/screenshots/05_bob_bankrupt.png differ diff --git a/screenshots/06_game_over.png b/screenshots/06_game_over.png new file mode 100644 index 0000000..b865d77 Binary files /dev/null and b/screenshots/06_game_over.png differ diff --git a/screenshots/player_bankrupt_game_over.png b/screenshots/player_bankrupt_game_over.png new file mode 100644 index 0000000..a2aa968 Binary files /dev/null and b/screenshots/player_bankrupt_game_over.png differ diff --git a/screenshots/single_player_joined.png b/screenshots/single_player_joined.png new file mode 100644 index 0000000..dee52f8 Binary files /dev/null and b/screenshots/single_player_joined.png differ