From 0d522c7757aafc5b546db77056852f64ab39973f Mon Sep 17 00:00:00 2001 From: Cody Seibert Date: Wed, 16 Oct 2024 16:57:11 -0400 Subject: [PATCH] refactor everything --- app/leaderboard/page.tsx | 4 +-- app/map.tsx | 2 +- app/play/[level]/page.tsx | 15 ++++---- app/play/page.tsx | 37 ++++++++++++------- app/result-status.tsx | 2 +- app/visualizer.tsx | 9 ++++- app/watch/page.tsx | 76 +++++++++++++++++++++++++++++++++++---- app/watch/result.tsx | 49 ++++++++++++++++++++----- convex/constants.ts | 6 ++++ convex/games.ts | 4 +-- convex/maps.ts | 18 ++++++---- convex/results.ts | 53 ++++++++++++++------------- models/index.ts | 4 +-- 13 files changed, 201 insertions(+), 78 deletions(-) diff --git a/app/leaderboard/page.tsx b/app/leaderboard/page.tsx index 5ca9dbc..2d6521b 100644 --- a/app/leaderboard/page.tsx +++ b/app/leaderboard/page.tsx @@ -65,8 +65,8 @@ const LeaderBoard = () => { ); return ( -
-
Leaderboard
+
+

Leaderboard

diff --git a/app/map.tsx b/app/map.tsx index 5e8bab8..c34c499 100644 --- a/app/map.tsx +++ b/app/map.tsx @@ -8,7 +8,7 @@ export function Map({ map }: { map: string[][] }) { {row.map((cell, x) => (
{cell}
diff --git a/app/play/[level]/page.tsx b/app/play/[level]/page.tsx index a9535c3..bb92566 100644 --- a/app/play/[level]/page.tsx +++ b/app/play/[level]/page.tsx @@ -92,15 +92,14 @@ export default function PlayLevelPage({ const mapHeight = playerMap.length > 0 ? playerMap.length : map.grid.length; return ( -
+
+
- -

Level {level}

-
{/* Spacer for alignment */} +

Night #{level}

- -
+ + + + + + + ))}
diff --git a/app/result-status.tsx b/app/result-status.tsx index c9410a4..3053d85 100644 --- a/app/result-status.tsx +++ b/app/result-status.tsx @@ -5,7 +5,7 @@ export function ResultStatus({ result }: { result: Doc<"results"> }) {
- {result.isWin ? "Won" : "Lost"} + {result.isWin ? "WON" : "LOST"}
); } diff --git a/app/visualizer.tsx b/app/visualizer.tsx index 06a2dbe..8df6bd6 100644 --- a/app/visualizer.tsx +++ b/app/visualizer.tsx @@ -9,12 +9,14 @@ export function Visualizer({ autoReplay = false, autoStart = false, controls = true, + cellSize = "64", map, onSimulationEnd, }: { autoReplay?: boolean; autoStart?: boolean; controls?: boolean; + cellSize?: string; map: string[][]; onSimulationEnd?: (isWin: boolean) => void; }) { @@ -84,7 +86,12 @@ export function Visualizer({ {row.map((cell, x) => (
{cell}
diff --git a/app/watch/page.tsx b/app/watch/page.tsx index 35bebd1..f833e0b 100644 --- a/app/watch/page.tsx +++ b/app/watch/page.tsx @@ -3,21 +3,83 @@ import { useQuery } from "convex/react"; import Result from "./result"; import { api } from "@/convex/_generated/api"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableRow, +} from "@/components/ui/table"; +import { TableHeader } from "@/components/ui/table"; +import Link from "next/link"; +import { Button } from "@/components/ui/button"; export default function GamePage() { const results = useQuery(api.results.getLastCompletedResults); + const globalRanking = useQuery(api.leaderboard.getGlobalRankings); if (results === undefined) { - return

Loading...

; + return ( +
+

Recent Games

+

Loading...

+
+ ); + } + + if (results.length === 0) { + return ( +
+

Recent Games

+

No results yet

+
+ ); } return ( -
-

Recent Games

-
- {results.map((result) => ( - - ))} +
+
+

Recent Games

+
+ {results.map((result) => ( + + ))} +
+
+ +
+
+

LLM Leaderboard

+ +
+ + + + Model ID + Wins + Losses + Total Games + Win Ratio + + + + {globalRanking?.map((item) => ( + + {item.modelId} + {item.wins} + {item.losses} + + {item.wins + item.losses} + + + {((item.wins / (item.wins + item.losses)) * 100).toFixed(2)}% + + + ))} + +
); diff --git a/app/watch/result.tsx b/app/watch/result.tsx index b7d542c..41d8fba 100644 --- a/app/watch/result.tsx +++ b/app/watch/result.tsx @@ -4,21 +4,54 @@ import { ResultStatus } from "../result-status"; import { type ResultWithGame } from "@/convex/results"; import { Visualizer } from "../visualizer"; import { format } from "date-fns"; +import Link from "next/link"; +import { Card } from "@/components/ui/card"; export default function Result({ result }: { result: ResultWithGame }) { return ( -
+ + {result.status === "completed" && ( + + )} + {result.status === "failed" && ( +
+ Game failed: {result.error} +
+ )} + {result.status === "inProgress" && ( +
+

Game is playing...

+
+
+ )} + {result.game !== null && (
-
- -

at {format(new Date(result._creationTime), "h:mma")}

+
+ + Night #{result.level} + {" "} +
+ The {result.game.modelId} model + {result.status === "inProgress" ? ( + "Started" + ) : ( + + )}{" "} + at {format(new Date(result._creationTime), "h:mma")} +
-

Level {result.level}

-

{result.game.modelId}

)} - -
+
); } diff --git a/convex/constants.ts b/convex/constants.ts index 299421d..4fe7db2 100644 --- a/convex/constants.ts +++ b/convex/constants.ts @@ -10,3 +10,9 @@ export const AI_MODELS = [ ]; export const AI_MODEL_IDS = AI_MODELS.map((model) => model.model); + +// how long between each level when the AI models start playing. +// spacing out the levels to make it easier to watch in the games list and reduce ai token usage. +export const PLAY_DELAY = process.env.PLAY_DELAY + ? parseInt(process.env.PLAY_DELAY) + : 10_000; diff --git a/convex/games.ts b/convex/games.ts index a4aa84c..cd7c9da 100644 --- a/convex/games.ts +++ b/convex/games.ts @@ -1,6 +1,6 @@ import { v } from "convex/values"; -import { internalMutation, mutation, query } from "./_generated/server"; -import { api, internal } from "./_generated/api"; +import { mutation, query } from "./_generated/server"; +import { internal } from "./_generated/api"; import { AI_MODEL_IDS } from "./constants"; export const startNewGame = mutation({ diff --git a/convex/maps.ts b/convex/maps.ts index 40a4465..43fc716 100644 --- a/convex/maps.ts +++ b/convex/maps.ts @@ -184,16 +184,22 @@ export const playMapAction = internalAction({ ? error : "Unexpected error happened"; - await ctx.runMutation(internal.results.failResult, { + // await ctx.runMutation(internal.results.failResult, { + // resultId, + // error: errorMessage, + // }); + await ctx.runMutation(internal.results.updateResult, { resultId, + isWin: false, + reasoning: errorMessage, error: errorMessage, }); - await ctx.runMutation(internal.leaderboard.updateRankings, { - modelId: args.modelId, - level: args.level, - isWin: false, - }); + // await ctx.runMutation(internal.leaderboard.updateRankings, { + // modelId: args.modelId, + // level: args.level, + // isWin: false, + // }); } }, }); diff --git a/convex/results.ts b/convex/results.ts index c66db40..d6480ea 100644 --- a/convex/results.ts +++ b/convex/results.ts @@ -1,6 +1,7 @@ import { v } from "convex/values"; import { internalMutation, query } from "./_generated/server"; import { api, internal } from "./_generated/api"; +import { PLAY_DELAY } from "./constants"; export type ResultWithGame = Awaited< ReturnType @@ -20,11 +21,7 @@ export const getResults = query({ export const getLastCompletedResults = query({ handler: async ({ db }) => { - const results = await db - .query("results") - .filter((q) => q.eq(q.field("status"), "completed")) - .order("desc") - .take(20); + const results = await db.query("results").order("desc").take(20); return Promise.all( results.map(async (result) => ({ @@ -52,31 +49,32 @@ export const createInitialResult = internalMutation({ }, }); -export const failResult = internalMutation({ - args: { - resultId: v.id("results"), - error: v.string(), - }, - handler: async (ctx, args) => { - const result = await ctx.db.get(args.resultId); - - if (!result) { - throw new Error("Result not found"); - } - - await ctx.db.patch(args.resultId, { - error: args.error, - status: "failed", - }); - }, -}); +// export const failResult = internalMutation({ +// args: { +// resultId: v.id("results"), +// error: v.string(), +// }, +// handler: async (ctx, args) => { +// const result = await ctx.db.get(args.resultId); + +// if (!result) { +// throw new Error("Result not found"); +// } + +// await ctx.db.patch(args.resultId, { +// error: args.error, +// status: "failed", +// }); +// }, +// }); export const updateResult = internalMutation({ args: { resultId: v.id("results"), isWin: v.boolean(), reasoning: v.string(), - map: v.array(v.array(v.string())), + map: v.optional(v.array(v.array(v.string()))), + error: v.optional(v.string()), }, handler: async (ctx, args) => { const result = await ctx.db.get(args.resultId); @@ -88,8 +86,9 @@ export const updateResult = internalMutation({ await ctx.db.patch(args.resultId, { isWin: args.isWin, reasoning: args.reasoning, - status: "completed", - map: args.map, + status: args.error ? "failed" : "completed", + map: args.map ?? [], + error: args.error, }); const game = await ctx.db.get(result.gameId); @@ -123,7 +122,7 @@ export const updateResult = internalMutation({ throw new Error("Next map not found"); } - await ctx.scheduler.runAfter(0, internal.maps.playMapAction, { + await ctx.scheduler.runAfter(PLAY_DELAY, internal.maps.playMapAction, { gameId: result.gameId, modelId: game.modelId, level: result.level + 1, diff --git a/models/index.ts b/models/index.ts index b8bd75d..de8d56b 100644 --- a/models/index.ts +++ b/models/index.ts @@ -53,7 +53,7 @@ export async function runModel( const [playerRow, playerCol] = result.playerCoordinates; if (originalMap[playerRow][playerCol] !== " ") { - throw new Error("Cannot place player in a non-empty space"); + throw new Error("Tried to place player in a non-empty space, silly AI"); } originalMap[playerRow][playerCol] = "P"; @@ -62,7 +62,7 @@ export async function runModel( const [blockRow, blockCol] = block; if (originalMap[blockRow][blockCol] !== " ") { - throw new Error("Cannot place block in a non-empty space"); + throw new Error("Tried to place a block in a non-empty space, silly AI"); } originalMap[blockRow][blockCol] = "B";