|
|
import os |
|
|
import sys |
|
|
import threading |
|
|
import time |
|
|
import numpy as np |
|
|
import gradio as gr |
|
|
import pygame |
|
|
|
|
|
|
|
|
os.environ["SDL_VIDEODRIVER"] = "dummy" |
|
|
os.environ["SDL_AUDIODRIVER"] = "dummy" |
|
|
|
|
|
|
|
|
current_dir = os.path.dirname(os.path.abspath(__file__)) |
|
|
pydino_path = os.path.join(current_dir, "pydino") |
|
|
sys.path.append(pydino_path) |
|
|
|
|
|
|
|
|
import pickle |
|
|
from dimensions import Dimensions |
|
|
from watch_model import ModelWatcher |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GAME_INSTANCE = None |
|
|
CURRENT_FRAME = None |
|
|
CURRENT_FRAME = None |
|
|
CURRENT_BRAIN_DATA = {"inputs": [], "hidden": [], "outputs": []} |
|
|
BRAIN_WEIGHTS_JSON = "{}" |
|
|
LOCK = threading.Lock() |
|
|
TARGET_TPS = 60 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def load_web_ui_asset(filename): |
|
|
asset_path = os.path.join(current_dir, "neurodino", "web-ui", filename) |
|
|
if not os.path.exists(asset_path): |
|
|
print(f"Warning: Asset not found: {asset_path}") |
|
|
return "" |
|
|
with open(asset_path, "r", encoding="utf-8") as f: |
|
|
return f.read() |
|
|
|
|
|
VISUALIZER_JS_TEMPLATE = load_web_ui_asset("visualizer.js") |
|
|
|
|
|
def load_brain(brain_path="best_brain.pkl"): |
|
|
"""Load the best brain.""" |
|
|
if not os.path.exists(brain_path): |
|
|
return None |
|
|
try: |
|
|
with open(brain_path, "rb") as f: |
|
|
data = pickle.load(f) |
|
|
if isinstance(data, tuple): |
|
|
return data[0] |
|
|
return data |
|
|
except Exception as e: |
|
|
print(f"Error loading brain: {e}") |
|
|
return None |
|
|
|
|
|
def game_thread_func(): |
|
|
"""Background thread calling the game update loop.""" |
|
|
global GAME_INSTANCE, CURRENT_FRAME, CURRENT_BRAIN_DATA, TARGET_TPS |
|
|
|
|
|
clock = pygame.time.Clock() |
|
|
print("Game thread started.") |
|
|
|
|
|
while True: |
|
|
try: |
|
|
if GAME_INSTANCE: |
|
|
pygame.event.pump() |
|
|
GAME_INSTANCE.update() |
|
|
|
|
|
if GAME_INSTANCE.crashed: |
|
|
GAME_INSTANCE.restart_game() |
|
|
|
|
|
view = pygame.surfarray.array3d(GAME_INSTANCE.screen) |
|
|
view = view.transpose([1, 0, 2]) |
|
|
|
|
|
if hasattr(GAME_INSTANCE, 'brain'): |
|
|
b = GAME_INSTANCE.brain |
|
|
|
|
|
inputs = np.array(b.last_inputs).flatten().tolist() |
|
|
hidden = np.array(b.last_hidden).flatten().tolist() |
|
|
outputs = np.array(b.last_outputs).flatten().tolist() |
|
|
|
|
|
brain_data = { |
|
|
"inputs": inputs, |
|
|
"hidden": hidden, |
|
|
"outputs": outputs |
|
|
} |
|
|
else: |
|
|
brain_data = {"inputs": [], "hidden": [], "outputs": []} |
|
|
|
|
|
with LOCK: |
|
|
CURRENT_FRAME = view |
|
|
CURRENT_BRAIN_DATA = brain_data |
|
|
|
|
|
if TARGET_TPS > 0: |
|
|
clock.tick(TARGET_TPS) |
|
|
else: |
|
|
clock.tick() |
|
|
|
|
|
except Exception as e: |
|
|
print(f"Error in game loop: {e}") |
|
|
time.sleep(1) |
|
|
|
|
|
def set_speed(choice): |
|
|
global TARGET_TPS |
|
|
if choice == "Yavaş (30 FPS)": |
|
|
TARGET_TPS = 30 |
|
|
elif choice == "Normal (60 FPS)": |
|
|
TARGET_TPS = 60 |
|
|
elif choice == "Hızlı (120 FPS)": |
|
|
TARGET_TPS = 120 |
|
|
elif choice == "Maksimum (Unlimited)": |
|
|
TARGET_TPS = 0 |
|
|
return f"Hız ayarlandı: {choice}" |
|
|
|
|
|
def start_game_server(): |
|
|
global GAME_INSTANCE, BRAIN_WEIGHTS_JSON |
|
|
|
|
|
import os |
|
|
os.environ["SDL_VIDEODRIVER"] = "dummy" |
|
|
pygame.init() |
|
|
brain = load_brain() |
|
|
if not brain: return False |
|
|
|
|
|
|
|
|
import json |
|
|
weights = { |
|
|
"ih": brain.weights_ih.tolist() if hasattr(brain.weights_ih, 'tolist') else brain.weights_ih, |
|
|
"ho": brain.weights_ho.tolist() if hasattr(brain.weights_ho, 'tolist') else brain.weights_ho, |
|
|
"bh": np.array(brain.bias_h).flatten().tolist(), |
|
|
"bo": np.array(brain.bias_o).flatten().tolist() |
|
|
} |
|
|
BRAIN_WEIGHTS_JSON = json.dumps(weights) |
|
|
|
|
|
pygame.display.set_mode((600, 150)) |
|
|
dims = Dimensions(width=600, height=150) |
|
|
game_surface = pygame.Surface((dims.width, dims.height)) |
|
|
|
|
|
GAME_INSTANCE = ModelWatcher(game_surface, dims, brain, silent=False) |
|
|
GAME_INSTANCE.start() |
|
|
|
|
|
t = threading.Thread(target=game_thread_func, daemon=True) |
|
|
t.start() |
|
|
return True |
|
|
|
|
|
def data_producer(): |
|
|
"""Yields (image, json_data) tuple.""" |
|
|
while True: |
|
|
with LOCK: |
|
|
yield (CURRENT_FRAME, CURRENT_BRAIN_DATA) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if TARGET_TPS == 0: |
|
|
time.sleep(0.05) |
|
|
else: |
|
|
time.sleep(0.1) |
|
|
|
|
|
|
|
|
description = """ |
|
|
# 🦖 NeuroDino Canlı Yayın |
|
|
Bu demo, **Genetik Algoritma** ile eğitilmiş bir Yapay Zeka'nın (Neural Network) canlı oynayışını gösterir. |
|
|
""" |
|
|
|
|
|
|
|
|
CANVAS_HTML = load_web_ui_asset("canvas.html") |
|
|
|
|
|
|
|
|
CSS = load_web_ui_asset("style.css") |
|
|
|
|
|
|
|
|
CUSTOM_CONTROLS_HTML = load_web_ui_asset("custom_controls.html") |
|
|
|
|
|
|
|
|
FORCE_LIGHT_JS = """ |
|
|
function refresh() { |
|
|
const url = new URL(window.location); |
|
|
if (url.searchParams.get('__theme') !== 'light') { |
|
|
url.searchParams.set('__theme', 'light'); |
|
|
window.location.href = url.href; |
|
|
} |
|
|
} |
|
|
""" |
|
|
|
|
|
with gr.Blocks(css=CSS, theme=gr.themes.Default(), js="document.body.classList.remove('dark');") as demo: |
|
|
|
|
|
with gr.Row(elem_id="main_row"): |
|
|
with gr.Column(scale=1.5, elem_id="left_game_col"): |
|
|
|
|
|
image_out = gr.Image(label="Oyun Görünümü", streaming=True, elem_id="game_display", show_label=False) |
|
|
|
|
|
|
|
|
gr.HTML(CUSTOM_CONTROLS_HTML) |
|
|
|
|
|
with gr.Row(): |
|
|
speed_radio = gr.Radio( |
|
|
choices=["Yavaş (30 FPS)", "Normal (60 FPS)", "Hızlı (120 FPS)", "Maksimum (Unlimited)"], |
|
|
value="Normal (60 FPS)", |
|
|
label="Oyun Hızı", |
|
|
interactive=True |
|
|
) |
|
|
|
|
|
with gr.Column(scale=2): |
|
|
|
|
|
gr.HTML(CANVAS_HTML) |
|
|
|
|
|
|
|
|
brain_data_sink = gr.JSON(visible=False) |
|
|
|
|
|
|
|
|
if start_game_server(): |
|
|
|
|
|
js_code = VISUALIZER_JS_TEMPLATE.replace("__WEIGHTS_PLACEHOLDER__", BRAIN_WEIGHTS_JSON) |
|
|
demo.load(None, None, None, js=js_code) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
demo.load(data_producer, inputs=None, outputs=[image_out, brain_data_sink]) |
|
|
|
|
|
|
|
|
brain_data_sink.change( |
|
|
fn=None, |
|
|
inputs=[brain_data_sink], |
|
|
js="(data) => { if(window.drawBrain) window.drawBrain(data); }" |
|
|
) |
|
|
|
|
|
|
|
|
status_msg = gr.Markdown(visible=False) |
|
|
speed_radio.change(fn=set_speed, inputs=speed_radio, outputs=status_msg) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
|
|
demo.queue().launch(server_name="0.0.0.0", server_port=7860, share=True) |
|
|
|