sports-schedule-generator
Version:
Generates schedules primarily used in team sports
140 lines (129 loc) • 4.76 kB
text/typescript
import * as R from "ramda";
import Gameday from "./gameday";
import Match from "./match";
export function initGameday<T>(allTeams: T[]): Gameday<T> {
const gameday = new Gameday<T>();
const bucketCount = allTeams.length / 2 - 1;
const rightJokerMatch = new Match<T>();
rightJokerMatch.away = R.last(allTeams);
gameday.rightJoker = rightJokerMatch;
let teams = R.init(allTeams);
for (let i = 0; i < bucketCount; i++) {
const firstTeam = R.head(teams);
const lastTeam = R.last(teams);
const match = new Match<T>();
match.home = firstTeam;
match.away = lastTeam;
gameday.buckets.push(match);
teams = R.init(R.tail(teams));
}
rightJokerMatch.home = teams[0]; // assign last remaining team
return gameday;
}
function shiftToLeft<T>(previousGameday: Gameday<T>): Gameday<T> {
const gameday = new Gameday<T>();
const leftJokerMatch = new Match<T>();
leftJokerMatch.home = previousGameday.rightJoker && previousGameday.rightJoker.away;
leftJokerMatch.away = previousGameday.buckets[0].home;
gameday.leftJoker = leftJokerMatch;
for (let i = 0; i < previousGameday.buckets.length - 1; i++) {
const match = new Match<T>();
match.home = previousGameday.buckets[i + 1].home;
match.away = previousGameday.buckets[i].away;
gameday.buckets.push(match);
}
const lastMatch = new Match<T>();
lastMatch.home = previousGameday.rightJoker && previousGameday.rightJoker.home;
lastMatch.away = R.last(previousGameday.buckets)?.away;
gameday.buckets.push(lastMatch);
delete gameday.rightJoker;
return gameday;
}
function shiftToRight<T>(previousGameday: Gameday<T>): Gameday<T> {
const gameday = new Gameday<T>();
const rightJokerMatch = new Match<T>();
rightJokerMatch.home = R.last(previousGameday.buckets)?.away;
rightJokerMatch.away = previousGameday.leftJoker && previousGameday.leftJoker.home;
gameday.rightJoker = rightJokerMatch;
for (let i = 1; i < previousGameday.buckets.length; i++) {
const match = new Match<T>();
match.away = previousGameday.buckets[i - 1].away;
match.home = previousGameday.buckets[i].home;
gameday.buckets.push(match);
}
const firstMatch = new Match<T>();
firstMatch.home = previousGameday.buckets[0].home;
firstMatch.away = previousGameday.leftJoker && previousGameday.leftJoker.away;
gameday.buckets = R.prepend(firstMatch, gameday.buckets);
delete gameday.leftJoker;
return gameday;
}
export function generateGameday<T>(previousGameday: Gameday<T>): Gameday<T> {
if (!previousGameday.leftJoker) {
return shiftToLeft(previousGameday);
}
return shiftToRight(previousGameday);
}
export function generateGamedays<T>(teams: T[]): Gameday<T>[] {
const schedule: Gameday<T>[] = [];
const firstGameday = initGameday(teams);
schedule.push(firstGameday);
let previousGameday = firstGameday;
const gamedayCount = teams.length % 2 == 0 ? teams.length - 1 : teams.length;
for (let i = 1; i < gamedayCount; i++) {
const gameday = generateGameday(previousGameday);
schedule.push(gameday);
previousGameday = gameday;
}
return schedule;
}
function createMatch<T>(match: Match<T>, switchSides = true): Match<T> {
const createdMatch = R.clone(match);
if (switchSides) {
createdMatch.home = match.away;
createdMatch.away = match.home;
}
return createdMatch;
}
function gamedayToMatchArray<T>(gameday: Gameday<T>, switchSides: boolean): Match<T>[] {
const matches: Match<T>[] = [];
if (gameday.leftJoker) {
const match = createMatch(gameday.leftJoker, false);
if (match.home && match.away) {
matches.push(match); // jokers already swept by algorithm
}
}
if (gameday.rightJoker) {
const match = createMatch(gameday.rightJoker, false);
if (match.home && match.away) {
matches.push(match); // jokers already swept by algorithm
}
}
for (const bucketMatch of gameday.buckets) {
const match = createMatch(bucketMatch, switchSides);
if (match.home && match.away) {
matches.push(match);
}
}
return matches;
}
function createRematchSchedule<T>(schedule: Match<T>[][]): Match<T>[][] {
const rematchSchedule: Match<T>[][] = [];
for (const matches of schedule) {
const rematches: Match<T>[] = R.map(createMatch, matches);
rematchSchedule.push(rematches);
}
return rematchSchedule;
}
export function generateSchedule<T>(teams: T[], rematch = false): Match<T>[][] {
let schedule: Match<T>[][] = [];
const gamedays = generateGamedays(teams);
for (let i = 0; i < gamedays.length; i++) {
const switchSides = i % 2 == 0;
schedule.push(gamedayToMatchArray(gamedays[i], switchSides));
}
if (rematch) {
schedule = R.concat(schedule, createRematchSchedule(schedule));
}
return schedule;
}