Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…t-sim

merging the pulled changes from the main branch
  • Loading branch information
Ashutoshbind15 committed Oct 16, 2024
2 parents 89ac5ec + 931e177 commit 9a9e846
Show file tree
Hide file tree
Showing 14 changed files with 340 additions and 152 deletions.
19 changes: 11 additions & 8 deletions app/games/[gameId]/result.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import { Doc } from "@/convex/_generated/dataModel";
import { api } from "@/convex/_generated/api";
import { useQuery } from "convex/react";
import { Visualizer } from "./visualizer";
import { ResultStatus } from "@/app/result-status";
import { Visualizer } from "../../visualizer";
import Link from "next/link";

export const Result = ({ result }: { result: Doc<"results"> }) => {
const map = useQuery(api.maps.getMapByLevel, {
Expand All @@ -24,14 +26,15 @@ export const Result = ({ result }: { result: Doc<"results"> }) => {

return (
<div className="flex items-center gap-8">
<div className="whitespace-nowrap">Level {map.level}</div>
<Visualizer map={result.map} />
<Link
href={`/play/${map.level}`}
className="whitespace-nowrap hover:underline cursor-pointer"
>
Level {map.level}
</Link>
<Visualizer map={result.map} autoStart={true} />
<div className="flex flex-col">
<div
className={`font-bold ${result.isWin ? "text-green-500" : "text-red-500"}`}
>
{result.isWin ? "Won" : "Lost"}
</div>
<ResultStatus result={result} />
{result.reasoning !== "" && <p>{result.reasoning}</p>}
</div>
</div>
Expand Down
2 changes: 1 addition & 1 deletion app/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export default function Header() {
</Link>

<nav className="flex items-center space-x-4">
<Link href="/">
<Link href="/watch">
<Button variant="ghost">Watch</Button>
</Link>
<Link href="/play">
Expand Down
61 changes: 49 additions & 12 deletions app/play/[level]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useState } from "react";
import { useQuery } from "convex/react";
import { api } from "@/convex/_generated/api";
import { Button } from "@/components/ui/button";
import { Visualizer } from "@/app/games/[gameId]/visualizer";
import { Visualizer } from "../../visualizer";
import { Map } from "@/app/map";
import Link from "next/link";
import { ChevronLeftIcon } from "@radix-ui/react-icons";
Expand All @@ -19,6 +19,10 @@ export default function PlayLevelPage({
const [playerMap, setPlayerMap] = useState<string[][]>([]);
const [isSimulating, setIsSimulating] = useState(false);
const [gameResult, setGameResult] = useState<"WON" | "LOST" | null>(null);
const [placementMode, setPlacementMode] = useState<"player" | "block">(
"player",
);
const [blockCount, setBlockCount] = useState(0);

if (!map) {
return <div>Loading...</div>;
Expand All @@ -27,6 +31,7 @@ export default function PlayLevelPage({
function handleRetryClicked() {
setIsSimulating(false);
setGameResult(null);
setBlockCount(0);
if (map) {
setPlayerMap(map.grid);
}
Expand All @@ -38,23 +43,35 @@ export default function PlayLevelPage({
const newMap =
playerMap.length > 0 ? [...playerMap] : map.grid.map((row) => [...row]);

// Remove existing player if any
for (let i = 0; i < newMap.length; i++) {
for (let j = 0; j < newMap[i].length; j++) {
if (newMap[i][j] === "P") {
newMap[i][j] = " ";
if (placementMode === "player") {
// Remove existing player if any
for (let i = 0; i < newMap.length; i++) {
for (let j = 0; j < newMap[i].length; j++) {
if (newMap[i][j] === "P") {
newMap[i][j] = " ";
}
}
}
}

// Place new player
if (newMap[y][x] === " ") {
newMap[y][x] = "P";
// Place new player
if (newMap[y][x] === " ") {
newMap[y][x] = "P";
}
} else if (placementMode === "block" && blockCount < 2) {
// Place new block
if (newMap[y][x] === " ") {
newMap[y][x] = "B";
setBlockCount(blockCount + 1);
}
}

setPlayerMap(newMap);
};

const handlePlacementModeChange = (mode: "player" | "block") => {
setPlacementMode(mode);
};

const runSimulation = () => {
if (!playerMap.some((row) => row.includes("P"))) {
alert(
Expand All @@ -70,6 +87,10 @@ export default function PlayLevelPage({
setGameResult(isWin ? "WON" : "LOST");
};

const mapWidth =
playerMap.length > 0 ? playerMap[0].length : map.grid[0].length;
const mapHeight = playerMap.length > 0 ? playerMap.length : map.grid.length;

return (
<div className="container mx-auto py-8">
<div className="flex justify-between items-center mb-6">
Expand All @@ -81,6 +102,22 @@ export default function PlayLevelPage({
<h1 className="text-3xl font-bold text-center">Level {level}</h1>
<div className="w-[100px]"></div> {/* Spacer for alignment */}
</div>
<div className="mb-4 flex justify-center gap-4">
<Button
onClick={() => handlePlacementModeChange("player")}
disabled={playerMap.some((row) => row.includes("P"))}
variant={placementMode === "player" ? "default" : "outline"}
>
Place Player
</Button>
<Button
onClick={() => handlePlacementModeChange("block")}
disabled={blockCount >= 2}
variant={placementMode === "block" ? "default" : "outline"}
>
Place Block ({2 - blockCount} left)
</Button>
</div>
<div className="mb-8 flex flex-col items-center">
<h2 className="text-xl font-semibold mb-4">
{isSimulating ? "Simulation Result" : "Place Your Player"}
Expand All @@ -106,8 +143,8 @@ export default function PlayLevelPage({
<div
className="absolute inset-0 grid"
style={{
gridTemplateColumns: `repeat(${map.width}, minmax(0, 1fr))`,
gridTemplateRows: `repeat(${map.height}, minmax(0, 1fr))`,
gridTemplateColumns: `repeat(${mapWidth}, minmax(0, 1fr))`,
gridTemplateRows: `repeat(${mapHeight}, minmax(0, 1fr))`,
}}
>
{(playerMap.length > 0 ? playerMap : map.grid).map((row, y) =>
Expand Down
11 changes: 11 additions & 0 deletions app/result-status.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Doc } from "@/convex/_generated/dataModel";

export function ResultStatus({ result }: { result: Doc<"results"> }) {
return (
<div
className={`font-bold ${result.isWin ? "text-green-500" : "text-red-500"}`}
>
{result.isWin ? "Won" : "Lost"}
</div>
);
}
62 changes: 43 additions & 19 deletions app/games/[gameId]/visualizer.tsx → app/visualizer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,25 @@ import { Button } from "@/components/ui/button";
import { ZombieSurvival } from "@/simulators/zombie-survival";
import { useEffect, useRef, useState } from "react";

const AUTO_REPLAY_SPEED = 1_500;
const REPLAY_SPEED = 600;

export function Visualizer({
map,
autoReplay = false,
autoStart = false,
controls = true,
map,
onSimulationEnd,
}: {
map: string[][];
autoReplay?: boolean;
autoStart?: boolean;
controls?: boolean;
map: string[][];
onSimulationEnd?: (isWin: boolean) => void;
}) {
const simulator = useRef<ZombieSurvival | null>(null);
const interval = useRef<NodeJS.Timeout | null>(null);
const timeout = useRef<NodeJS.Timeout | null>(null);
const [isRunning, setIsRunning] = useState(false);
const [mapState, setMapState] = useState(map);
const [needsReset, setNeedsReset] = useState(false);
Expand All @@ -25,17 +31,30 @@ export function Visualizer({
simulator.current = new ZombieSurvival(clonedMap);
setMapState(simulator.current!.getState());
setIsRunning(true);

interval.current = setInterval(() => {
if (simulator.current!.finished()) {
clearInterval(interval.current!);
interval.current = null;

if (autoReplay) {
timeout.current = setTimeout(() => {
timeout.current = null;
startSimulation();
}, AUTO_REPLAY_SPEED);

return;
}

setIsRunning(false);

if (onSimulationEnd) {
console.log("here");
onSimulationEnd(!simulator.current!.getPlayer().dead());
}

return;
}

simulator.current!.step();
setMapState(simulator.current!.getState());
}, REPLAY_SPEED);
Expand All @@ -52,6 +71,9 @@ export function Visualizer({
if (interval.current) {
clearInterval(interval.current);
}
if (timeout.current) {
clearTimeout(timeout.current);
}
};
}, []);

Expand All @@ -69,22 +91,24 @@ export function Visualizer({
))}
</div>
))}
<div className="flex gap-2 justify-center py-2">
<Button onClick={startSimulation} disabled={isRunning}>
Replay
</Button>
<Button
disabled={isRunning}
onClick={() => {
simulator.current = new ZombieSurvival(map);
setMapState(simulator.current!.getState());
setIsRunning(false);
setNeedsReset(false);
}}
>
Reset
</Button>
</div>
{controls && (
<div className="flex gap-2 justify-center py-2">
<Button onClick={startSimulation} disabled={isRunning}>
Replay
</Button>
<Button
disabled={isRunning}
onClick={() => {
simulator.current = new ZombieSurvival(map);
setMapState(simulator.current!.getState());
setIsRunning(false);
setNeedsReset(false);
}}
>
Reset
</Button>
</div>
)}
</div>
);
}
24 changes: 24 additions & 0 deletions app/watch/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"use client";

import { useQuery } from "convex/react";
import Result from "./result";
import { api } from "@/convex/_generated/api";

export default function GamePage() {
const results = useQuery(api.results.getLastCompletedResults);

if (results === undefined) {
return <p>Loading...</p>;
}

return (
<div className="container mx-auto pt-12 pb-24 space-y-8">
<h1 className="text-2xl font-bold">Recent Games</h1>
<div className="flex flex-wrap justify-betweengap-x-2 gap-12">
{results.map((result) => (
<Result key={result._id} result={result} />
))}
</div>
</div>
);
}
24 changes: 24 additions & 0 deletions app/watch/result.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
"use client";

import { ResultStatus } from "../result-status";
import { type ResultWithGame } from "@/convex/results";
import { Visualizer } from "../visualizer";
import { format } from "date-fns";

export default function Result({ result }: { result: ResultWithGame }) {
return (
<div className="flex gap-8 border rounded-xl p-4 bg-black">
{result.game !== null && (
<div className="flex flex-col gap-2">
<div className="flex gap-2">
<ResultStatus result={result} />
<p>at {format(new Date(result._creationTime), "h:mma")}</p>
</div>
<p>Level {result.level}</p>
<p>{result.game.modelId}</p>
</div>
)}
<Visualizer autoReplay autoStart controls={false} map={result.map} />
</div>
);
}
Loading

0 comments on commit 9a9e846

Please sign in to comment.