game / app.py
luqmanabban's picture
Upload 5 files
11f2446 verified
import gradio as gr
from pathlib import Path
from playwright.async_api import async_playwright
import asyncio, os
MOVE_ORDER = ["ArrowUp", "ArrowLeft", "ArrowRight", "ArrowDown"]
async def get_score(page):
el = await page.query_selector(".score-container")
if not el:
return 0
text = (await el.inner_text()).strip()
try:
return int(text.split()[0].replace(",", ""))
except Exception:
return 0
async def is_game_over(page):
msg = await page.query_selector(".game-message.game-over")
return msg is not None
async def board_signature(page):
tiles = await page.query_selector_all(".tile")
parts = []
for t in tiles:
classes = (await t.get_attribute("class")) or ""
parts.append(classes)
return "|".join(sorted(parts))
async def play_offline_2048(moves: int = 200):
logs = []
img_path = Path("2048_offline_final.png")
html_path = Path(__file__).parent / "static" / "2048.html"
url = f"file://{html_path.resolve()}"
async with async_playwright() as p:
browser = await p.chromium.launch(
headless=True,
args=["--no-sandbox", "--disable-setuid-sandbox", "--allow-file-access-from-files"]
)
page = await browser.new_page(viewport={"width": 900, "height": 1100})
await page.goto(url, wait_until="load", timeout=60000)
logs.append("Offline 2048 loaded. Starting auto-play...")
game = await page.query_selector(".game-container")
if game:
await game.click()
last_sig = await board_signature(page)
invalid_count = 0
played = 0
for i in range(moves):
move = MOVE_ORDER[i % len(MOVE_ORDER)]
await page.keyboard.press(move)
await page.wait_for_timeout(60)
sig = await board_signature(page)
if sig == last_sig:
for alt in MOVE_ORDER[1:]:
await page.keyboard.press(alt)
await page.wait_for_timeout(50)
new_sig = await board_signature(page)
if new_sig != sig:
sig = new_sig
break
else:
invalid_count += 1
else:
invalid_count = 0
last_sig = sig
played += 1
score = await get_score(page)
logs.append(f"Move {played:03d} β†’ score {score}")
if await is_game_over(page):
logs.append("Game Over detected. Stopping.")
break
if invalid_count >= 3:
first = MOVE_ORDER.pop(0)
MOVE_ORDER.append(first)
invalid_count = 0
logs.append(f"Rotated move order to escape dead pattern: {MOVE_ORDER}")
await page.screenshot(path=str(img_path), full_page=True)
await browser.close()
return str(img_path), "\n".join(logs)
async def run(moves):
return await play_offline_2048(moves=int(moves))
ui = gr.Interface(
fn=run,
inputs=gr.Slider(50, 600, value=250, step=10, label="Max moves"),
outputs=[gr.Image(label="Final Screenshot"), gr.Textbox(label="Logs", lines=15)],
title="AI Auto-Player: 2048 (Offline, Self-Contained)",
description="Playwright launches a local offline copy of 2048 bundled in the Space. Works with internet disabled."
)
if __name__ == "__main__":
ui.launch()