mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-29 06:43:17 -05:00
Remove /play
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
import { GetAllLadderRegisteredTeamsData } from "prisma/queries/getAllLadderRegisteredTeams";
|
||||
import useSWR from "swr";
|
||||
|
||||
export const useLadderTeams = (skip?: boolean) =>
|
||||
useSWR<GetAllLadderRegisteredTeamsData>(skip ? null : "/api/play/teams");
|
||||
@@ -3,7 +3,6 @@ import { inferAsyncReturnType, inferProcedureOutput } from "@trpc/server";
|
||||
import * as trpcNext from "@trpc/server/adapters/next";
|
||||
import calendarApi from "routers/calendar";
|
||||
import freeAgentsApi from "routers/freeagents";
|
||||
import playApi from "routers/play";
|
||||
import plusApi from "routers/plus";
|
||||
import superjson from "superjson";
|
||||
import { getMySession } from "utils/api";
|
||||
@@ -23,8 +22,7 @@ export function createRouter() {
|
||||
export const appRouter = createRouter()
|
||||
.merge("plus.", plusApi)
|
||||
.merge("calendar.", calendarApi)
|
||||
.merge("freeAgents.", freeAgentsApi)
|
||||
.merge("play.", playApi);
|
||||
.merge("freeAgents.", freeAgentsApi);
|
||||
|
||||
// Exporting type _type_ AppRouter only exposes types that can be used for inference
|
||||
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#type-only-imports-and-export
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import { createRouter } from "pages/api/trpc/[trpc]";
|
||||
import service from "services/play";
|
||||
|
||||
const playApi = createRouter()
|
||||
.query("allRegisteredTeams", {
|
||||
resolve() {
|
||||
return service.allRegisteredTeams();
|
||||
},
|
||||
})
|
||||
.query("nextLadderDay", {
|
||||
resolve() {
|
||||
return service.nextLadderDay();
|
||||
},
|
||||
})
|
||||
.query("previousLadderDay", {
|
||||
resolve() {
|
||||
return service.previousLadderDay();
|
||||
},
|
||||
});
|
||||
export default playApi;
|
||||
128
services/play.ts
128
services/play.ts
@@ -1,128 +0,0 @@
|
||||
import { Prisma } from ".prisma/client";
|
||||
import prisma from "prisma/client";
|
||||
|
||||
const allRegisteredTeams = async () =>
|
||||
prisma.ladderRegisteredTeam.findMany({
|
||||
include: {
|
||||
roster: {
|
||||
select: {
|
||||
id: true,
|
||||
discordAvatar: true,
|
||||
discordId: true,
|
||||
discriminator: true,
|
||||
username: true,
|
||||
team: {
|
||||
select: {
|
||||
name: true,
|
||||
nameForUrl: true,
|
||||
twitterName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const allLadderRegisteredTeamsForMatches = async () =>
|
||||
prisma.ladderRegisteredTeam.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
roster: {
|
||||
select: {
|
||||
id: true,
|
||||
trueSkill: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export type NextLadderDay = Prisma.PromiseReturnType<typeof nextLadderDay>;
|
||||
|
||||
const nextLadderDay = async () => {
|
||||
return prisma.ladderDay.findFirst({
|
||||
where: { date: { gte: new Date() } },
|
||||
include: {
|
||||
matches: {
|
||||
select: {
|
||||
order: true,
|
||||
maplist: true,
|
||||
teamAScore: true,
|
||||
teamBScore: true,
|
||||
players: {
|
||||
select: {
|
||||
team: true,
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
discordAvatar: true,
|
||||
discordId: true,
|
||||
discriminator: true,
|
||||
username: true,
|
||||
team: {
|
||||
select: {
|
||||
name: true,
|
||||
nameForUrl: true,
|
||||
twitterName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export type PreviousLadderDay = Prisma.PromiseReturnType<
|
||||
typeof previousLadderDay
|
||||
>;
|
||||
|
||||
const previousLadderDay = async () => {
|
||||
return prisma.ladderDay.findFirst({
|
||||
where: { date: { lt: new Date() } },
|
||||
orderBy: { date: "desc" },
|
||||
select: {
|
||||
date: true,
|
||||
matches: {
|
||||
select: {
|
||||
order: true,
|
||||
maplist: true,
|
||||
teamAScore: true,
|
||||
teamBScore: true,
|
||||
players: {
|
||||
select: {
|
||||
team: true,
|
||||
user: {
|
||||
select: {
|
||||
id: true,
|
||||
discordAvatar: true,
|
||||
discordId: true,
|
||||
discriminator: true,
|
||||
username: true,
|
||||
team: {
|
||||
select: {
|
||||
name: true,
|
||||
nameForUrl: true,
|
||||
twitterName: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const playService = {
|
||||
allRegisteredTeams,
|
||||
allLadderRegisteredTeamsForMatches,
|
||||
nextLadderDay,
|
||||
previousLadderDay,
|
||||
};
|
||||
|
||||
export default playService;
|
||||
150
utils/play.ts
150
utils/play.ts
@@ -1,150 +0,0 @@
|
||||
import { GetAllLadderRegisteredTeamsForMatchesData } from "prisma/queries/getAllLadderRegisteredTeamsForMatches";
|
||||
import { quality, Rating } from "ts-trueskill";
|
||||
|
||||
type TeamsWithRanking = {
|
||||
id: number;
|
||||
roster: {
|
||||
id: number;
|
||||
rating: Rating;
|
||||
}[];
|
||||
};
|
||||
|
||||
export const getLadderRounds = (
|
||||
registeredTeams: GetAllLadderRegisteredTeamsForMatchesData
|
||||
) => {
|
||||
if (registeredTeams.length < 4) {
|
||||
throw Error("registeredTeams length less than 4");
|
||||
}
|
||||
const teamsWithRanking: TeamsWithRanking[] = registeredTeams.map(
|
||||
(registeredTeam) => ({
|
||||
id: registeredTeam.id,
|
||||
roster: registeredTeam.roster.map((user) => ({
|
||||
id: user.id,
|
||||
rating: user.trueSkill
|
||||
? new Rating(user.trueSkill.mu, user.trueSkill.sigma)
|
||||
: new Rating(),
|
||||
})),
|
||||
})
|
||||
);
|
||||
|
||||
// this chooses the teams to sit out each round if uneven number of teams
|
||||
// if even it just returns `teamsWithRanking` in both 0 index and 1 index
|
||||
const [teamsRoundOne, teamsRoundTwo] = getTeamsForRounds();
|
||||
|
||||
// helper variable accessed from generatePairings
|
||||
let bestRound: TeamsWithRanking[][] | undefined;
|
||||
// helper variable accessed from generatePairings
|
||||
let bestAverageQuality = -Infinity;
|
||||
|
||||
// first round matches actual
|
||||
let firstRound: TeamsWithRanking[][] | undefined;
|
||||
|
||||
generatePairings(teamsRoundOne, 0);
|
||||
|
||||
firstRound = bestRound;
|
||||
bestAverageQuality = -Infinity;
|
||||
|
||||
generatePairings(teamsRoundTwo, 0);
|
||||
|
||||
if (!firstRound || !bestRound || firstRound === bestRound) {
|
||||
throw Error("unexpected falsy firstRound or bestPairs");
|
||||
}
|
||||
|
||||
return [firstRound, bestRound];
|
||||
|
||||
// https://stackoverflow.com/a/37449857
|
||||
// start is the current position in the list, advancing by 2 each time
|
||||
// pass 0 as start when calling at the top level
|
||||
function generatePairings(items: TeamsWithRanking[], start: number) {
|
||||
if (items.length % 2 !== 0) {
|
||||
throw Error("uneven number of teams in generatePairings");
|
||||
}
|
||||
|
||||
// is this a complete pairing?
|
||||
if (start === items.length) {
|
||||
if (hasDuplicatePairing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let qualitySum = 0;
|
||||
for (let i = 0; i < items.length; i += 2) {
|
||||
const teamAlpha = items[i].roster.map((user) => user.rating);
|
||||
const teamBravo = items[i + 1].roster.map((user) => user.rating);
|
||||
|
||||
qualitySum += quality([teamAlpha, teamBravo]);
|
||||
}
|
||||
|
||||
qualitySum /= items.length / 2;
|
||||
if (qualitySum > bestAverageQuality) {
|
||||
bestAverageQuality = qualitySum;
|
||||
bestRound = items
|
||||
.map((team, i) => (i % 2 !== 0 ? null : [team, items[i + 1]]))
|
||||
.filter((team) => team) as TeamsWithRanking[][];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// for the next pair, choose the first element in the list for the
|
||||
// first item in the pair (meaning we don't have to do anything
|
||||
// but leave it in place), and each of the remaining elements for
|
||||
// the second item:
|
||||
for (let j = start + 1; j < items.length; j++) {
|
||||
// swap start+1 and j:
|
||||
let temp = items[start + 1];
|
||||
items[start + 1] = items[j];
|
||||
items[j] = temp;
|
||||
|
||||
// recurse:
|
||||
generatePairings(items, start + 2);
|
||||
|
||||
// swap them back:
|
||||
temp = items[start + 1];
|
||||
items[start + 1] = items[j];
|
||||
items[j] = temp;
|
||||
}
|
||||
|
||||
function hasDuplicatePairing() {
|
||||
if (!firstRound) return false;
|
||||
|
||||
for (let i = 0; i < items.length; i += 2) {
|
||||
const teamAlpha = items[i];
|
||||
const teamBravo = items[i + 1];
|
||||
|
||||
if (
|
||||
firstRound.some(
|
||||
([pairsAlpha, pairsBravo]) =>
|
||||
(pairsAlpha.id === teamAlpha.id &&
|
||||
pairsBravo.id === teamBravo.id) ||
|
||||
(pairsAlpha.id === teamBravo.id && pairsBravo.id === teamAlpha.id)
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getTeamsForRounds() {
|
||||
if (teamsWithRanking.length % 2 === 0) {
|
||||
return [teamsWithRanking, teamsWithRanking];
|
||||
}
|
||||
|
||||
const firstTeamToSitOut = randomChoiceIndex();
|
||||
let secondTeamToSitOut = randomChoiceIndex();
|
||||
while (secondTeamToSitOut === firstTeamToSitOut) {
|
||||
secondTeamToSitOut = randomChoiceIndex();
|
||||
}
|
||||
|
||||
return [
|
||||
teamsWithRanking.filter((_, i) => i !== firstTeamToSitOut),
|
||||
teamsWithRanking.filter((_, i) => i !== secondTeamToSitOut),
|
||||
];
|
||||
|
||||
function randomChoiceIndex() {
|
||||
return Math.floor(Math.random() * teamsWithRanking.length);
|
||||
}
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user