From 4cff246fcd0a7e1448c0448ed5f7410bde06fa8b Mon Sep 17 00:00:00 2001 From: Cody Seibert Date: Tue, 15 Oct 2024 23:06:35 -0400 Subject: [PATCH] adding visualizer --- app/games/[gameId]/page.tsx | 3 +- app/games/[gameId]/result.tsx | 4 +- app/games/[gameId]/visualizer.tsx | 50 +++++++++++++++++++ convex/maps.ts | 46 +++++++++++++++++ convex/openai.ts | 16 ++++-- convex/results.ts | 3 ++ convex/schema.ts | 1 + convex/tsconfig.json | 2 +- .../zombie-survival}/Direction.ts | 0 .../zombie-survival}/Position.ts | 0 .../zombie-survival}/ZombieSurvival.spec.ts | 0 .../zombie-survival}/ZombieSurvival.ts | 2 +- .../zombie-survival}/entities/Box.ts | 0 .../zombie-survival}/entities/Entity.ts | 0 .../zombie-survival}/entities/Player.ts | 2 +- .../zombie-survival}/entities/Rock.ts | 0 .../zombie-survival}/entities/Zombie.ts | 2 +- .../zombie-survival}/index.ts | 0 .../zombie-survival/lib/closestEntity.ts | 0 .../zombie-survival/lib/entityAt.ts | 0 .../zombie-survival}/lib/pathfinder.ts | 0 21 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 app/games/[gameId]/visualizer.tsx rename {games/ZombieSurvival => simulators/zombie-survival}/Direction.ts (100%) rename {games/ZombieSurvival => simulators/zombie-survival}/Position.ts (100%) rename {games/ZombieSurvival => simulators/zombie-survival}/ZombieSurvival.spec.ts (100%) rename {games/ZombieSurvival => simulators/zombie-survival}/ZombieSurvival.ts (98%) rename {games/ZombieSurvival => simulators/zombie-survival}/entities/Box.ts (100%) rename {games/ZombieSurvival => simulators/zombie-survival}/entities/Entity.ts (100%) rename {games/ZombieSurvival => simulators/zombie-survival}/entities/Player.ts (91%) rename {games/ZombieSurvival => simulators/zombie-survival}/entities/Rock.ts (100%) rename {games/ZombieSurvival => simulators/zombie-survival}/entities/Zombie.ts (98%) rename {games/ZombieSurvival => simulators/zombie-survival}/index.ts (100%) rename games/ZombieSurvival/lib/closest-entity.ts => simulators/zombie-survival/lib/closestEntity.ts (100%) rename games/ZombieSurvival/lib/entity-at.ts => simulators/zombie-survival/lib/entityAt.ts (100%) rename {games/ZombieSurvival => simulators/zombie-survival}/lib/pathfinder.ts (100%) diff --git a/app/games/[gameId]/page.tsx b/app/games/[gameId]/page.tsx index 535aad8..0ffb499 100644 --- a/app/games/[gameId]/page.tsx +++ b/app/games/[gameId]/page.tsx @@ -1,6 +1,5 @@ "use client"; -import { Map } from "@/app/map"; import { api } from "@/convex/_generated/api"; import { Id } from "@/convex/_generated/dataModel"; import { useQuery } from "convex/react"; @@ -15,7 +14,7 @@ export default function GamePage({ params }: { params: { gameId: string } }) { }); return ( -
+

Game {params.gameId}

Model: {game?.modelId}

diff --git a/app/games/[gameId]/result.tsx b/app/games/[gameId]/result.tsx index d678e6c..2a4ac13 100644 --- a/app/games/[gameId]/result.tsx +++ b/app/games/[gameId]/result.tsx @@ -1,6 +1,6 @@ "use client"; -import { Map } from "@/app/map"; +import { Visualizer } from "./visualizer"; import { api } from "@/convex/_generated/api"; import { Doc } from "@/convex/_generated/dataModel"; import { useQuery } from "convex/react"; @@ -19,7 +19,7 @@ export const Result = ({ result }: { result: Doc<"results"> }) => { return (
Level {map.level}
- +
diff --git a/app/games/[gameId]/visualizer.tsx b/app/games/[gameId]/visualizer.tsx new file mode 100644 index 0000000..5bde609 --- /dev/null +++ b/app/games/[gameId]/visualizer.tsx @@ -0,0 +1,50 @@ +import { Button } from "@/components/ui/button"; +import { ZombieSurvival } from "@/simulators/zombie-survival"; +import { useRef, useState } from "react"; + +const REPLAY_SPEED = 600; + +export function Visualizer({ map }: { map: string[][] }) { + const simulator = useRef(null); + const interval = useRef(null); + const [isRunning, setIsRunning] = useState(false); + const [mapState, setMapState] = useState(map); + + return ( +
+ {mapState.map((row, y) => ( +
+ {row.map((cell, x) => ( +
+ {cell} +
+ ))} +
+ ))} + +
+ ); +} diff --git a/convex/maps.ts b/convex/maps.ts index abe6fce..8312ef2 100644 --- a/convex/maps.ts +++ b/convex/maps.ts @@ -26,6 +26,52 @@ const MAPS = [ width: 5, height: 5, }, + { + level: 3, + grid: [ + ["Z", " ", " ", "R", " ", " ", " "], + [" ", " ", " ", " ", " ", " ", " "], + [" ", " ", " ", " ", " ", " ", " "], + [" ", " ", " ", " ", " ", "Z", " "], + [" ", " ", " ", " ", " ", " ", " "], + [" ", "R", " ", " ", " ", " ", " "], + [" ", " ", " ", " ", "R", " ", " "], + ], + width: 7, + height: 7, + }, + { + level: 4, + grid: [ + [" ", "Z", " ", " ", "R", " ", " "], + [" ", " ", " ", " ", " ", " ", " "], + [" ", " ", " ", " ", " ", " ", " "], + [" ", " ", " ", " ", " ", " ", "Z"], + [" ", " ", "R", " ", " ", " ", " "], + [" ", " ", " ", " ", " ", " ", " "], + [" ", " ", " ", " ", " ", " ", " "], + ], + width: 7, + height: 7, + }, + { + level: 5, + grid: [ + [" ", " ", " ", " ", "R", "Z", " ", " ", " ", " ", " "], + [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], + ["Z", " ", " ", " ", " ", "R", " ", " ", " ", " ", " "], + [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], + [" ", " ", " ", " ", "R", " ", " ", " ", " ", "Z", " "], + [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], + [" ", " ", "R", " ", " ", " ", " ", " ", " ", " ", " "], + [" ", " ", " ", " ", " ", " ", "Z", " ", " ", " ", " "], + ["B", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], + [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "], + ["Z", " ", " ", " ", "Z", " ", " ", " ", " ", " ", "Z"], + ], + width: 11, + height: 11, + }, ]; export const seedMaps = internalMutation({ diff --git a/convex/openai.ts b/convex/openai.ts index 6fefa65..1dd48c0 100644 --- a/convex/openai.ts +++ b/convex/openai.ts @@ -1,10 +1,11 @@ import OpenAI from "openai"; -import { action, internalAction } from "./_generated/server"; +import { internalAction } from "./_generated/server"; import { v } from "convex/values"; import { z } from "zod"; import { zodResponseFormat } from "openai/helpers/zod"; import { api, internal } from "./_generated/api"; import { Doc } from "./_generated/dataModel"; +import { ZombieSurvival } from "../simulators/zombie-survival"; const ResponseSchema = z.object({ map: z.array(z.array(z.string())), @@ -68,7 +69,9 @@ export const playMapAction = internalAction({ "P" represents the player, who cannot move. The player's goal is to shoot and kill zombies before they reach them. "B" represents blocks that can be placed before the round begins to hinder the zombies. You can place up to two blocks on the map. Your goal is to place the player ("P") and two blocks ("B") in locations that maximize the player's survival by delaying the zombies' approach while allowing the player clear lines of sight to shoot them before they get too close. - Returning a 2d grid with the player and blocks placed in the optimal locations, with the coordinates player ("P") and the blocks ("B"), also provide reasoning for the choices.`, + Returning a 2d grid with the player and blocks placed in the optimal locations, with the coordinates player ("P") and the blocks ("B"), also provide reasoning for the choices. + + You can't replace rocks R or zombies Z with blocks. If there is no room to place a block, do not place any.`, }, { role: "user", @@ -80,12 +83,17 @@ export const playMapAction = internalAction({ const response = completion.choices[0].message; if (response.parsed) { - // TODO: run simulation here to determine if win is true or false + const game = new ZombieSurvival(response.parsed.map); + while (!game.finished()) { + game.step(); + } + const isWin = !game.getPlayer().dead(); await ctx.runMutation(internal.results.updateResult, { resultId, - isWin: true, + isWin, reasoning: response.parsed.reasoning, + map: response.parsed.map, }); } else if (response.refusal) { const refusal_res = response.refusal; diff --git a/convex/results.ts b/convex/results.ts index 4156e6e..912b045 100644 --- a/convex/results.ts +++ b/convex/results.ts @@ -26,6 +26,7 @@ export const createInitialResult = internalMutation({ reasoning: "", status: "inProgress", isWin: false, + map: [], }); }, }); @@ -35,6 +36,7 @@ export const updateResult = internalMutation({ resultId: v.id("results"), isWin: v.boolean(), reasoning: v.string(), + map: v.array(v.array(v.string())), }, handler: async (ctx, args) => { const result = await ctx.db.get(args.resultId); @@ -47,6 +49,7 @@ export const updateResult = internalMutation({ isWin: args.isWin, reasoning: args.reasoning, status: "completed", + map: args.map, }); const game = await ctx.db.get(result.gameId); diff --git a/convex/schema.ts b/convex/schema.ts index 753f950..ae66f4a 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -26,6 +26,7 @@ export default defineSchema({ level: v.number(), isWin: v.boolean(), reasoning: v.string(), + map: v.array(v.array(v.string())), status: v.union(v.literal("inProgress"), v.literal("completed")), }).index("by_gameId_level", ["gameId", "level"]), }); diff --git a/convex/tsconfig.json b/convex/tsconfig.json index 6fa874e..3a62739 100644 --- a/convex/tsconfig.json +++ b/convex/tsconfig.json @@ -20,6 +20,6 @@ "isolatedModules": true, "noEmit": true }, - "include": ["./**/*"], + "include": ["./**/*", "../simulators/**/*"], "exclude": ["./_generated"] } diff --git a/games/ZombieSurvival/Direction.ts b/simulators/zombie-survival/Direction.ts similarity index 100% rename from games/ZombieSurvival/Direction.ts rename to simulators/zombie-survival/Direction.ts diff --git a/games/ZombieSurvival/Position.ts b/simulators/zombie-survival/Position.ts similarity index 100% rename from games/ZombieSurvival/Position.ts rename to simulators/zombie-survival/Position.ts diff --git a/games/ZombieSurvival/ZombieSurvival.spec.ts b/simulators/zombie-survival/ZombieSurvival.spec.ts similarity index 100% rename from games/ZombieSurvival/ZombieSurvival.spec.ts rename to simulators/zombie-survival/ZombieSurvival.spec.ts diff --git a/games/ZombieSurvival/ZombieSurvival.ts b/simulators/zombie-survival/ZombieSurvival.ts similarity index 98% rename from games/ZombieSurvival/ZombieSurvival.ts rename to simulators/zombie-survival/ZombieSurvival.ts index 2d0efe6..5c4a2a1 100644 --- a/games/ZombieSurvival/ZombieSurvival.ts +++ b/simulators/zombie-survival/ZombieSurvival.ts @@ -3,7 +3,7 @@ import { Entity } from "./entities/Entity"; import { Player } from "./entities/Player"; import { Rock } from "./entities/Rock"; import { Zombie } from "./entities/Zombie"; -import { entityAt } from "./lib/entity-at"; +import { entityAt } from "./lib/entityAt"; export class ZombieSurvival { public readonly boardHeight: number; diff --git a/games/ZombieSurvival/entities/Box.ts b/simulators/zombie-survival/entities/Box.ts similarity index 100% rename from games/ZombieSurvival/entities/Box.ts rename to simulators/zombie-survival/entities/Box.ts diff --git a/games/ZombieSurvival/entities/Entity.ts b/simulators/zombie-survival/entities/Entity.ts similarity index 100% rename from games/ZombieSurvival/entities/Entity.ts rename to simulators/zombie-survival/entities/Entity.ts diff --git a/games/ZombieSurvival/entities/Player.ts b/simulators/zombie-survival/entities/Player.ts similarity index 91% rename from games/ZombieSurvival/entities/Player.ts rename to simulators/zombie-survival/entities/Player.ts index caa757e..f50982b 100644 --- a/games/ZombieSurvival/entities/Player.ts +++ b/simulators/zombie-survival/entities/Player.ts @@ -1,7 +1,7 @@ import { Entity, EntityType } from "./Entity"; import { Position } from "../Position"; import { ZombieSurvival } from "../ZombieSurvival"; -import { closestEntity } from "../lib/closest-entity"; +import { closestEntity } from "../lib/closestEntity"; export class Player extends Entity { public static Destructible = true; diff --git a/games/ZombieSurvival/entities/Rock.ts b/simulators/zombie-survival/entities/Rock.ts similarity index 100% rename from games/ZombieSurvival/entities/Rock.ts rename to simulators/zombie-survival/entities/Rock.ts diff --git a/games/ZombieSurvival/entities/Zombie.ts b/simulators/zombie-survival/entities/Zombie.ts similarity index 98% rename from games/ZombieSurvival/entities/Zombie.ts rename to simulators/zombie-survival/entities/Zombie.ts index 0b76524..d5f2263 100644 --- a/games/ZombieSurvival/entities/Zombie.ts +++ b/simulators/zombie-survival/entities/Zombie.ts @@ -2,7 +2,7 @@ import { Direction, allDirections, move } from "../Direction"; import { Entity, EntityType } from "./Entity"; import { Position } from "../Position"; import { ZombieSurvival } from "../ZombieSurvival"; -import { entityAt } from "../lib/entity-at"; +import { entityAt } from "../lib/entityAt"; import { pathfinder } from "../lib/pathfinder"; export class Zombie extends Entity { diff --git a/games/ZombieSurvival/index.ts b/simulators/zombie-survival/index.ts similarity index 100% rename from games/ZombieSurvival/index.ts rename to simulators/zombie-survival/index.ts diff --git a/games/ZombieSurvival/lib/closest-entity.ts b/simulators/zombie-survival/lib/closestEntity.ts similarity index 100% rename from games/ZombieSurvival/lib/closest-entity.ts rename to simulators/zombie-survival/lib/closestEntity.ts diff --git a/games/ZombieSurvival/lib/entity-at.ts b/simulators/zombie-survival/lib/entityAt.ts similarity index 100% rename from games/ZombieSurvival/lib/entity-at.ts rename to simulators/zombie-survival/lib/entityAt.ts diff --git a/games/ZombieSurvival/lib/pathfinder.ts b/simulators/zombie-survival/lib/pathfinder.ts similarity index 100% rename from games/ZombieSurvival/lib/pathfinder.ts rename to simulators/zombie-survival/lib/pathfinder.ts