diff --git a/docker-compose.yml b/docker-compose.yml index 9b1bf73..bc31188 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ services: webui: image: minimax ports: - - "3000:3000" + - "4000:3000" volumes: - ./data:/app/data env_file: diff --git a/rust/minimax/src/agents/rhai.rs b/rust/minimax/src/agents/rhai.rs index ed68aac..a5bae6e 100644 --- a/rust/minimax/src/agents/rhai.rs +++ b/rust/minimax/src/agents/rhai.rs @@ -80,7 +80,6 @@ pub struct RhaiAgent { engine: Engine, script: AST, - scope: Scope<'static>, print_callback: Arc, } @@ -118,7 +117,6 @@ impl RhaiAgent { // Do not use FULL, rand_* functions are not pure engine.set_optimization_level(OptimizationLevel::Simple); - engine.disable_symbol("eval"); engine.set_max_expr_depths(100, 100); engine.set_max_strings_interned(1024); @@ -201,13 +199,11 @@ impl RhaiAgent { }; let script = engine.compile(script)?; - let scope = Scope::new(); // Not used Ok(Self { rng, engine, script, - scope, print_callback, }) } @@ -227,7 +223,7 @@ impl Agent for RhaiAgent { fn step_min(&mut self, board: &Board) -> Result { let res = self.engine.call_fn_with_options::( CallFnOptions::new().eval_ast(false), - &mut self.scope, + &mut Scope::new(), &self.script, "step_min", (board.clone(),), @@ -242,7 +238,7 @@ impl Agent for RhaiAgent { fn step_max(&mut self, board: &Board) -> Result { let res = self.engine.call_fn_with_options::( CallFnOptions::new().eval_ast(false), - &mut self.scope, + &mut Scope::new(), &self.script, "step_max", (board.clone(),), diff --git a/webui/src/app/api/get-script/route.ts b/webui/src/app/api/get-script/route.ts index a494a46..f2c516c 100644 --- a/webui/src/app/api/get-script/route.ts +++ b/webui/src/app/api/get-script/route.ts @@ -4,9 +4,12 @@ import { join } from "path"; import { existsSync } from "fs"; import { SAVE_CONFIG } from "@/lib/saveConfig"; +// Force dynamic rendering for this API route +export const dynamic = 'force-dynamic'; + export async function GET(request: NextRequest) { try { - const { searchParams } = new URL(request.url); + const { searchParams } = request.nextUrl; const name = searchParams.get("name"); if (!name) { @@ -28,7 +31,6 @@ export async function GET(request: NextRequest) { const filename = `${name}.rhai`; const filepath = join(saveDir, filename); - // Check if file exists if (!existsSync(filepath)) { return NextResponse.json( { error: `Script "${name}" not found` }, @@ -36,7 +38,6 @@ export async function GET(request: NextRequest) { ); } - // Read and return file content const content = await readFile(filepath, "utf8"); return NextResponse.json({ diff --git a/webui/src/app/api/list-scripts/route.ts b/webui/src/app/api/list-scripts/route.ts index 97f3274..000d18a 100644 --- a/webui/src/app/api/list-scripts/route.ts +++ b/webui/src/app/api/list-scripts/route.ts @@ -1,31 +1,40 @@ -import { NextResponse } from "next/server"; +import { NextRequest, NextResponse } from "next/server"; import { readdir } from "fs/promises"; -import { join } from "path"; import { existsSync } from "fs"; import { SAVE_CONFIG } from "@/lib/saveConfig"; -export async function GET() { +// Force dynamic rendering for this API route +export const dynamic = "force-dynamic"; + +const headers = { + "Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate", + Pragma: "no-cache", + Expires: "0", +}; + +export async function GET(_request: NextRequest) { try { const saveDir = SAVE_CONFIG.SAVE_DIRECTORY; // If save directory doesn't exist, return empty array if (!existsSync(saveDir)) { - return NextResponse.json({ scripts: [] }); + return NextResponse.json({ scripts: [] }, { headers }); } // Read directory and filter for .rhai files const files = await readdir(saveDir); + const scripts = files .filter((file) => file.endsWith(".rhai")) .map((file) => file.replace(".rhai", "")) .sort(); // Sort alphabetically - return NextResponse.json({ scripts }); + return NextResponse.json({ scripts }, { headers }); } catch (error) { console.error("List scripts error:", error); return NextResponse.json( { error: "Failed to list scripts" }, - { status: 500 } + { status: 500, headers } ); } } diff --git a/webui/src/components/Editor.tsx b/webui/src/components/Editor.tsx index d190e3c..7bb2696 100644 --- a/webui/src/components/Editor.tsx +++ b/webui/src/components/Editor.tsx @@ -128,7 +128,8 @@ export const Editor = forwardRef(function Editor( editorRef.current = null; } }; - }, []); // DO NOT FILL ARRAY + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); // DO NOT FILL ARRAY - intentionally empty to prevent re-initialization // Update font size when it changes useEffect(() => { diff --git a/webui/src/components/Playground.tsx b/webui/src/components/Playground.tsx index c7f550b..0e8a2c8 100644 --- a/webui/src/components/Playground.tsx +++ b/webui/src/components/Playground.tsx @@ -42,28 +42,6 @@ fn step_max(board) { const AGENTS = { // special-cased below Self: undefined, - - Random: `fn random_action(board) { - let symb = rand_symb(); - let pos = rand_int(0, 10); - let action = Action(symb, pos); - - while !board.can_play(action) { - let symb = rand_symb(); - let pos = rand_int(0, 10); - action = Action(symb, pos); - } - - return action; -} - -fn step_min(board) { - return random_action(board); -} - -fn step_max(board) { - return random_action(board); -}`, }; export default function Playground() { @@ -71,7 +49,7 @@ export default function Playground() { const [isEditorReady, setIsEditorReady] = useState(false); const [fontSize, setFontSize] = useState(14); const [bulkRounds, setBulkRounds] = useState(1000); - const [selectedAgent, setSelectedAgent] = useState("Random"); + const [selectedAgent, setSelectedAgent] = useState("Self"); const [isHelpOpen, setIsHelpOpen] = useState(false); const [scriptName, setScriptName] = useState(""); const [saveSecret, setSaveSecret] = useState(""); @@ -126,10 +104,7 @@ export default function Playground() { // Combine hardcoded agents with saved scripts, ensuring Self and Random are first const combinedAgents = [ "Self", - "Random", - ...Object.keys(AGENTS).filter( - (key) => key !== "Self" && key !== "Random" - ), + ...Object.keys(AGENTS).filter((key) => key !== "Self"), ...scripts, ]; diff --git a/webui/src/lib/worker_human.ts b/webui/src/lib/worker_human.ts index 342637c..f88203b 100644 --- a/webui/src/lib/worker_human.ts +++ b/webui/src/lib/worker_human.ts @@ -24,14 +24,18 @@ self.onmessage = async (event) => { if (type === "data") { if (currentGame !== null) { - currentGame.take_input(event_data.data); + try { + currentGame.take_input(event_data.data); - if (currentGame.is_error()) { - currentGame = null; - self.postMessage({ type: "complete" }); - } else if (currentGame.is_done()) { - currentGame = null; - self.postMessage({ type: "complete" }); + if (currentGame.is_error()) { + currentGame = null; + self.postMessage({ type: "complete" }); + } else if (currentGame.is_done()) { + currentGame = null; + self.postMessage({ type: "complete" }); + } + } catch (error) { + self.postMessage({ type: "error", error: String(error) }); } } } else if (type === "init") {