Skip to content

Commit

Permalink
Merge pull request #53 from webdevcody/feat-scheduler
Browse files Browse the repository at this point in the history
Scheduler/Models table
  • Loading branch information
webdevcody authored Oct 16, 2024
2 parents 93011a4 + 7176232 commit ac30ca5
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 49 deletions.
29 changes: 18 additions & 11 deletions app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"use client";

import { api } from "@/convex/_generated/api";
import { useAction, useMutation } from "convex/react";
import React, { useState } from "react";
import React from "react";
import { useQuery, useMutation } from "convex/react";
import { useRouter } from "next/navigation";
import { Button } from "@/components/ui/button";
import {
Select,
Expand All @@ -11,14 +11,20 @@ import {
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { AI_MODELS } from "@/convex/constants";
import { useRouter } from "next/navigation";
import { api } from "@/convex/_generated/api";

export default function MainPage() {
const models = useQuery(api.models.getActiveModels);
const startNewGame = useMutation(api.games.startNewGame);
const [model, setModel] = useState(AI_MODELS[0].model);
const [model, setModel] = React.useState("");
const router = useRouter();

React.useEffect(() => {
if (models !== undefined && models.length !== 0) {
setModel(models[0].slug);
}
}, [models]);

const handleClick = async () => {
await startNewGame({
modelId: model,
Expand All @@ -37,11 +43,12 @@ export default function MainPage() {
<SelectValue placeholder="Select model" />
</SelectTrigger>
<SelectContent>
{AI_MODELS.map((model) => (
<SelectItem key={model.model} value={model.model}>
{model.name}
</SelectItem>
))}
{models !== undefined &&
models.map((model) => (
<SelectItem key={model._id} value={model.slug}>
{model.name}
</SelectItem>
))}
</SelectContent>
</Select>
<Button onClick={handleClick}>Test Model</Button>
Expand Down
2 changes: 2 additions & 0 deletions convex/_generated/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import type * as http from "../http.js";
import type * as init from "../init.js";
import type * as leaderboard from "../leaderboard.js";
import type * as maps from "../maps.js";
import type * as models from "../models.js";
import type * as results from "../results.js";
import type * as scores from "../scores.js";
import type * as users from "../users.js";
Expand All @@ -42,6 +43,7 @@ declare const fullApi: ApiFromModules<{
init: typeof init;
leaderboard: typeof leaderboard;
maps: typeof maps;
models: typeof models;
results: typeof results;
scores: typeof scores;
users: typeof users;
Expand Down
10 changes: 10 additions & 0 deletions convex/cron.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { cronJobs } from "convex/server";
import { internal } from "./_generated/api";

const crons = cronJobs();

crons.interval(
"run games for all active models",
{ minutes: 5 },
internal.models.runActiveModelsGames,
);
7 changes: 3 additions & 4 deletions convex/init.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { internal } from "./_generated/api";
import { internalMutation } from "./_generated/server";
import { seedMaps } from "./maps";

export default internalMutation({
handler: async (ctx) => {
const maps = await ctx.db.query("maps").first();
if (maps) return;
await seedMaps(ctx, {});
await ctx.runMutation(internal.maps.seedMaps);
await ctx.runMutation(internal.models.seedModels);
},
});
30 changes: 14 additions & 16 deletions convex/maps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,19 +91,20 @@ const LEVELS = [

export const seedMaps = internalMutation({
handler: async (ctx) => {
// delete all existing maps
const maps = await ctx.db.query("maps").collect();
const firstMap = await ctx.db.query("maps").first();

for (const map of maps) {
await ctx.db.delete(map._id);
if (firstMap) {
return;
}

LEVELS.forEach((map, idx) => {
ctx.db.insert("maps", {
level: idx + 1,
grid: map.grid,
});
});
await Promise.all(
LEVELS.map((map, idx) =>
ctx.db.insert("maps", {
level: idx + 1,
grid: map.grid,
}),
),
);
},
});

Expand Down Expand Up @@ -139,12 +140,9 @@ export const playMapAction = internalAction({
},
);

const map: Doc<"maps"> | null = (await ctx.runQuery(
api.maps.getMapByLevel,
{
level: args.level,
},
)) as any;
const map = await ctx.runQuery(api.maps.getMapByLevel, {
level: args.level,
});

if (!map) {
throw new Error("Map not found");
Expand Down
49 changes: 49 additions & 0 deletions convex/models.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { AI_MODELS } from "./constants";
import { api } from "./_generated/api";
import { internalMutation, query } from "./_generated/server";

export const runActiveModelsGames = internalMutation({
handler: async (ctx) => {
const models = await ctx.runQuery(api.models.getActiveModels);

await Promise.all(
models.map((model) =>
ctx.runMutation(api.games.startNewGame, { modelId: model.slug }),
),
);
},
});

export const seedModels = internalMutation({
handler: async (ctx) => {
const models = await ctx.db.query("models").collect();
const promises = [];

for (const model of AI_MODELS) {
const existingModel = models.find((it) => it.slug === model.model);

if (existingModel !== undefined) {
continue;
}

promises.push(
ctx.db.insert("models", {
slug: model.model,
name: model.name,
active: true,
}),
);
}

await Promise.all(promises);
},
});

export const getActiveModels = query({
handler: async (ctx) => {
return await ctx.db
.query("models")
.withIndex("by_active", (q) => q.eq("active", true))
.collect();
},
});
62 changes: 45 additions & 17 deletions convex/results.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export const getLastCompletedResults = query({
handler: async ({ db }) => {
const results = await db
.query("results")
.filter((q) => q.eq(q.field("status"), "completed"))
.withIndex("by_status", (q) => q.eq("status", "completed"))
.order("desc")
.take(20);

Expand Down Expand Up @@ -68,6 +68,10 @@ export const failResult = internalMutation({
error: args.error,
status: "failed",
});

await ctx.runMutation(internal.results.scheduleNextPlay, {
resultId: args.resultId,
});
},
});

Expand All @@ -94,10 +98,6 @@ export const updateResult = internalMutation({

const game = await ctx.db.get(result.gameId);

const maps = await ctx.db.query("maps").collect();

const lastLevel = maps.reduce((max, map) => Math.max(max, map.level), 0);

if (!game) {
throw new Error("Game not found");
}
Expand All @@ -114,20 +114,48 @@ export const updateResult = internalMutation({
});
}

if (result.level < lastLevel) {
const map = await ctx.runQuery(api.maps.getMapByLevel, {
level: result.level + 1,
});
await ctx.runMutation(internal.results.scheduleNextPlay, {
resultId: args.resultId,
});
},
});

if (!map) {
throw new Error("Next map not found");
}
export const scheduleNextPlay = internalMutation({
args: {
resultId: v.id("results"),
},
handler: async (ctx, args) => {
const result = await ctx.db.get(args.resultId);

await ctx.scheduler.runAfter(0, internal.maps.playMapAction, {
gameId: result.gameId,
modelId: game.modelId,
level: result.level + 1,
});
if (!result) {
throw new Error("Result not found");
}

const maps = await ctx.db.query("maps").collect();
const lastLevel = maps.reduce((max, map) => Math.max(max, map.level), 0);

if (result.level >= lastLevel) {
return;
}

const map = await ctx.runQuery(api.maps.getMapByLevel, {
level: result.level + 1,
});

const game = await ctx.db.get(result.gameId);

if (!game) {
throw new Error("Game not found");
}

if (!map) {
throw new Error("Next map not found");
}

await ctx.scheduler.runAfter(0, internal.maps.playMapAction, {
gameId: result.gameId,
modelId: game.modelId,
level: result.level + 1,
});
},
});
9 changes: 8 additions & 1 deletion convex/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ export default defineSchema({
modelId: v.string(),
score: v.number(),
}).index("by_modelId", ["modelId"]),
models: defineTable({
slug: v.string(),
active: v.boolean(),
name: v.string(),
}).index("by_active", ["active"]),
results: defineTable({
gameId: v.id("games"),
level: v.number(),
Expand All @@ -31,7 +36,9 @@ export default defineSchema({
v.literal("completed"),
v.literal("failed"),
),
}).index("by_gameId_level", ["gameId", "level"]),
})
.index("by_gameId_level", ["gameId", "level"])
.index("by_status", ["status"]),
globalrankings: defineTable({
modelId: v.string(),
wins: v.number(),
Expand Down

0 comments on commit ac30ca5

Please sign in to comment.