/** * Server-side bot tick: load bot zoos, run same logic as client tickBotZoos, persist. * Uses web/js/bot-zoo.js so bot rules stay in one place. */ import { getBotZoosForTick, updateBotZooState, expireSaleListings, getActiveSaleListings, placeBid } from "./db.js"; import { tickBotZoos } from "../web/js/bot-zoo.js"; const BOT_TICK_INTERVAL_MS = 15 * 1000; let lastTickAt = 0; /** * Run one bot tick and persist all bot zoos. * @returns {Promise} */ export async function runBotTick() { const nowUnix = Math.floor(Date.now() / 1000); const nowMs = Date.now(); const dt = lastTickAt > 0 ? Math.min((nowMs - lastTickAt) / 1000, 60) : 10; lastTickAt = nowMs; await expireSaleListings(); const botZoos = await getBotZoosForTick(); if (botZoos.length === 0) return; const activeListings = await getActiveSaleListings(); for (const listing of activeListings) { const minBid = (listing.best_bid_amount ?? listing.initial_price) + 1; const bidders = botZoos.filter( (z) => z.id !== listing.seller_zoo_id && (z.botState?.coins ?? 0) >= minBid ); if (bidders.length > 0) { const bidder = bidders[Math.floor(Math.random() * bidders.length)]; const result = await placeBid(listing.id, bidder.id, minBid); if (result.ok) break; } } const state = { worldZoos: botZoos }; tickBotZoos(state, nowUnix, dt); for (const zoo of botZoos) { await updateBotZooState(zoo.id, zoo.animalWeights, zoo.botState); } } /** * Start the periodic bot tick (call after server listen). * @returns {void} */ export function startBotTickInterval() { setInterval(() => { runBotTick().catch((err) => { console.error("bot tick failed", err); }); }, BOT_TICK_INTERVAL_MS); }