brackt/app/models/draft-utils.ts

160 lines
5 KiB
TypeScript

import { database } from "~/database/context";
import * as schema from "~/database/schema";
import { eq, and, notInArray, desc } from "drizzle-orm";
import { getTeamQueue } from "./draft-queue";
import { isParticipantDrafted } from "./draft-pick";
/**
* Auto-pick for a team when their timer runs out
* 1. Check queue - pick first item if available
* 2. If queue empty, pick highest EV participant not drafted
*/
export async function autoPickForTeam(seasonId: string, teamId: string) {
// Check queue first
const queue = await getTeamQueue(teamId);
if (queue.length > 0) {
// Pick first item from queue
const firstInQueue = queue[0];
// Verify participant is not already drafted
const isDrafted = await isParticipantDrafted(seasonId, firstInQueue.participantId);
if (!isDrafted) {
return firstInQueue.participantId;
}
// If first item was drafted, try next items in queue
for (const item of queue.slice(1)) {
const isDrafted = await isParticipantDrafted(seasonId, item.participantId);
if (!isDrafted) {
return item.participantId;
}
}
}
// Queue is empty or all queued players drafted - pick highest EV available
return await getTopAvailableParticipant(seasonId);
}
/**
* Get the highest EV participant that hasn't been drafted yet
*/
export async function getTopAvailableParticipant(seasonId: string) {
const db = database();
// Get all drafted participant IDs
const draftedPicks = await db
.select({ participantId: schema.draftPicks.participantId })
.from(schema.draftPicks)
.where(eq(schema.draftPicks.seasonId, seasonId));
const draftedIds = draftedPicks.map((p: { participantId: string }) => p.participantId);
// Get all participants from season sports
const seasonSportsData = await db
.select({ sportsSeasonId: schema.seasonSports.sportsSeasonId })
.from(schema.seasonSports)
.where(eq(schema.seasonSports.seasonId, seasonId));
const sportsSeasonIds = seasonSportsData.map((s: { sportsSeasonId: string }) => s.sportsSeasonId);
if (sportsSeasonIds.length === 0) {
return null;
}
// Get top available participant by EV
let query = db
.select()
.from(schema.participants)
.where(eq(schema.participants.sportsSeasonId, sportsSeasonIds[0]))
.orderBy(desc(schema.participants.expectedValue), schema.participants.name);
// Filter out drafted participants if any exist
if (draftedIds.length > 0) {
query = db
.select()
.from(schema.participants)
.where(
and(
eq(schema.participants.sportsSeasonId, sportsSeasonIds[0]),
notInArray(schema.participants.id, draftedIds)
)
)
.orderBy(desc(schema.participants.expectedValue), schema.participants.name);
}
// Handle multiple sports seasons
if (sportsSeasonIds.length > 1) {
// For simplicity, we'll query all and sort in memory
// In production, might want to optimize this
const allParticipants = [];
for (const sportsSeasonId of sportsSeasonIds) {
let participantQuery = db
.select()
.from(schema.participants)
.where(eq(schema.participants.sportsSeasonId, sportsSeasonId));
if (draftedIds.length > 0) {
participantQuery = db
.select()
.from(schema.participants)
.where(
and(
eq(schema.participants.sportsSeasonId, sportsSeasonId),
notInArray(schema.participants.id, draftedIds)
)
);
}
const seasonParticipants = await participantQuery;
allParticipants.push(...seasonParticipants);
}
// Sort by EV desc, then name
allParticipants.sort((a, b) => {
if (b.expectedValue !== a.expectedValue) {
return b.expectedValue - a.expectedValue;
}
return a.name.localeCompare(b.name);
});
return allParticipants[0]?.id || null;
}
const [topParticipant] = await query;
return topParticipant?.id || null;
}
/**
* Calculate the current pick based on draft order and round
*/
export function calculatePickInfo(
pickNumber: number,
teamCount: number
): { round: number; pickInRound: number; teamIndex: number } {
const round = Math.ceil(pickNumber / teamCount);
const pickInRound = ((pickNumber - 1) % teamCount) + 1;
// Snake draft: odd rounds go forward, even rounds go backward
const isOddRound = round % 2 === 1;
const teamIndex = isOddRound ? pickInRound - 1 : teamCount - pickInRound;
return { round, pickInRound, teamIndex };
}
/**
* Get the team ID for a given pick number based on draft order
*/
export function getTeamForPick(
pickNumber: number,
draftOrder: { teamId: string; draftOrder: number }[]
): string | null {
const sortedOrder = [...draftOrder].sort((a, b) => a.draftOrder - b.draftOrder);
const teamCount = sortedOrder.length;
if (teamCount === 0) return null;
const { teamIndex } = calculatePickInfo(pickNumber, teamCount);
return sortedOrder[teamIndex]?.teamId || null;
}