mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-05-25 13:03:59 -05:00
84 lines
2.2 KiB
TypeScript
84 lines
2.2 KiB
TypeScript
import * as R from "remeda";
|
|
import type { CommonUser } from "~/utils/kysely.server";
|
|
import { seededRandom } from "~/utils/random";
|
|
|
|
type MatchSide = "ALPHA" | "BRAVO";
|
|
|
|
type Rosters = {
|
|
alpha: CommonUser[];
|
|
bravo: CommonUser[];
|
|
};
|
|
|
|
export interface InferredSubstitution {
|
|
side: MatchSide;
|
|
playerOut: CommonUser;
|
|
playerIn: CommonUser;
|
|
}
|
|
|
|
/**
|
|
* Compares the rosters of two consecutive maps and pairs up any
|
|
* players that dropped from a side with new players that joined the same side.
|
|
* The pairs are returned in roster order, so the first player out is paired with
|
|
* the first new player in. When the counts don't match, unpaired players are ignored.
|
|
*/
|
|
export function inferSubstitutions(
|
|
previousRosters: Rosters,
|
|
currentRosters: Rosters,
|
|
): InferredSubstitution[] {
|
|
const result: InferredSubstitution[] = [];
|
|
|
|
for (const side of ["alpha", "bravo"] as const) {
|
|
const prevIds = new Set(previousRosters[side].map((u) => u.id));
|
|
const currIds = new Set(currentRosters[side].map((u) => u.id));
|
|
|
|
const out = previousRosters[side].filter((u) => !currIds.has(u.id));
|
|
const inn = currentRosters[side].filter((u) => !prevIds.has(u.id));
|
|
|
|
for (const [playerOut, playerIn] of R.zip(out, inn)) {
|
|
result.push({
|
|
side: side === "alpha" ? "ALPHA" : "BRAVO",
|
|
playerOut,
|
|
playerIn,
|
|
});
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
const NUM_MAP = {
|
|
"1": ["1", "2", "4"],
|
|
"2": ["2", "1", "3", "5"],
|
|
"3": ["3", "2", "6"],
|
|
"4": ["4", "1", "5", "7"],
|
|
"5": ["5", "2", "4", "6", "8"],
|
|
"6": ["6", "3", "5", "9"],
|
|
"7": ["7", "4", "8"],
|
|
"8": ["8", "7", "5", "9", "0"],
|
|
"9": ["9", "6", "8"],
|
|
"0": ["0", "8"],
|
|
};
|
|
|
|
/**
|
|
* Generates a deterministic 4-digit Splatoon private battle room password based on the provided seed.
|
|
*
|
|
* Given the same seed, this function will always return the same password.
|
|
*/
|
|
export function resolveRoomPass(seed: number | string) {
|
|
let pass = "5";
|
|
for (let i = 0; i < 3; i++) {
|
|
const { seededShuffle } = seededRandom(`${seed}-${i}`);
|
|
|
|
const key = pass[i] as keyof typeof NUM_MAP;
|
|
const opts = NUM_MAP[key];
|
|
const next = seededShuffle(opts)[0];
|
|
pass += next;
|
|
}
|
|
|
|
// prevent 5555 since many use it as a default pass
|
|
// making it a bit more common guess
|
|
if (pass === "5555") return "5800";
|
|
|
|
return pass;
|
|
}
|