Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NFL Schedule Fix (Draft) #415

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

PhiloTFarnsworth
Copy link

@PhiloTFarnsworth PhiloTFarnsworth commented Apr 30, 2022

Draft

This PR deals with issue #333, and has the potential to improve scheduling across a number of league structures.

The initial commit contains two Python files, one which lightly explores the bye week issue, and another which generates 17 match-ups per team for a 32 team league using the NFL's Formula. These were written in Python just to ease the amount of overhead, and I can adapt them to typescript once all the kinks have been worked out (and I get a little more familiar with the code base.)

Schedule.py

This part of the code received the majority of my attention so far, but I think it was worth it as the schedule.py script generates varied schedules based on the NFL's Formula with the home/away logic cycling every 6 years. There's copious comments on the functionality, but it boils down to using the binary nature of home and away games to set the schedule before assigning matches.

This function would require that we receive the year the schedule is for, as well as a list of teamIDs in a specific order, as laid out in this table:

AFC AFC AFC AFC NFC NFC NFC NFC
Place East North South West East North South West
1st 0 1 2 3 4 5 6 7
2nd 8 9 10 11 12 13 14 15
3rd 16 17 18 19 20 21 22 23
4th 24 25 26 27 28 29 30 31

At the moment, I'd say the math is a little dogeared, as I wanted to create the NFL schedule, and then work back to adapt this schedule schema to leagues of varying size. At the moment, we're hard coding some values that should be derived, and to be honest, some of the math could be simplified by preparing the teams list into a more complex structure. There's also some very Python specific hacks that hopefully I won't have to resort to in the final typescript version.

This can eventually be adapted into a singular script for NFL matches, or possibly into some sort of modular scheduling system, though at this point I'd really be spitballing, but it does feel like formulas for assigning games based on each of the NFL's rules could be swapped out to create balanced schedules of varying length for leagues with varying amounts of teams.

Basic.py

This file contains some logic for an extremely simple method to collapse bye weeks for unsorted round robin type tournaments. It's not very flexible, rather shaggy, and it only appears to work on 2n teams playing 2n/2 games. But I thought I might as well include it as a starting point.

Extra Commentary

I do want to stress that these attached scripts are a very rough jumping off point. Beyond the need for adapting to typescript, some of my approach can be simplified or made less wasteful (the O2 approach to NFL scheduling could stand to be further refined). Also, there's some blindingly, embarrassingly obvious math I know I'm missing, so please feel free to point out where I can shape that up.

I'm going to turn my attention to the schedule condensing logic, and then once that's finished, port these scripts into typescript and place them in the appropriate areas.

@PhiloTFarnsworth
Copy link
Author

NFL Schedule POC

This commit finished the prototype for generating an schedule containing 18 weeks for 32 teams to play 17 games each with 1 bye in a manner which mimics the NFL's method.

ScheduleSort.py

This script contains the logic for generating an NFL schedule for 32 teams for 2022 and beyond. The initial 250 lines is schedule.py (with a few cosmetic changes), which generates the match ups for an NFL season from a list a teams arranged in the order they finished the previous season (see table in above comment). After the matchups are set, we build the desired amount of a full week slates of matchups, then sort the remaining games into partial weeks where teams would be expected to have byes.

Finally, we add the list of partial weeks into the middle of the full week schedule (to make weeks 8-14, where bye weeks fall in the 2022 season), and the result is an 18 week season that can easily be inserted using the addDaytoSchedule method.

What now?

With the general contours of the problem solved, I'm going to start porting this function into typescript, as well as clean it up and get the names a little more terse to match the existing codebase. I'll also explore exactly how to implement it within the codebase, and then #333 can be put to bed.

@PhiloTFarnsworth
Copy link
Author

PhiloTFarnsworth commented May 4, 2022

Typescript Port...

This commit translates the rough POC python scripts into Typescript and places them in the season directory with some rudimentary tests for both the sorting function, as well as the matchup generation function.

newScheduleSpeculative.football.ts

The Match generation from the earlier scripts has been improved, reducing the number of loops to assign all games from n2 to an n*teamsInDivision (this can likely be further improved, but it's in a decent state now). Porting some of the logic was a little tricky, but it now passes tests and outputs the correct schedule.

scheduleSortSpeculative.ts

This is a relatively straightforward port of the sorting algorithm sketched out in the python POC.

The spirit of this sort is to do two rather naive sorts, one where we build a full slate of games (Currently the NFL has 10 weeks with schedules, 8 weeks with byes), and then one where we build a partial week's slate. Following this, we then do a more intensive sort, where we assign matches until either we have no matches, or until we can no longer assign matches. If we can't assign matches, we 'dissolve' a partial week, put it back together with the unused matches, and continue to try to resolve to zero matches.

The problem is that this method isn't totally backed in math, so messing with the arrangement of bye weeks can be messy (Our naive sort will fail if we try to resolve over 14 full weeks, 4 weeks with byes, for example). From observations, I assume that if you choose a number of weeks with byes that is both a factor of total number of bye 'matches' (two teams = 1 bye match), and is over 33% of the total number of weeks, that this method should work.

What's really interesting is that if this sort works on the relatively difficult constraints of a NFL season, then it should be possible to adapt this sort for use in NHL and NBA seasons. By doing so, those schedules could be spread evenly across an actual calendar, which is always a really cool immersion touch, and also opens up cool little friviolites (like best performance on a given day).

Implementation Challenges

To be honest, I'm still not entirely sure how I would go about incorporating these functions.

For schedule sort, I'm fairly certain I need to add a check for whether it's a football sort, then pass the relevant parameters (for a 18 games season, default, for all others, need to know or generate the full week/ bye week split.

For the match generation function, I'm a little lost. I'd need to find how to pull either the standings table for a given year (and take the teamIDs from that), or generate each teams position on a standings table, then insert those teams in order into the teams array. On the bright side, it goes straight from there to sort, so it's a little simpler than the current logic, but I'm not entirely sure how to rate the challenge of implementation.

Making the Generate Matches function more flexible

At the moment, there are two big challenges to making the balanced schedules match generator flexible enough to handle any two conference league with an equal number of divisions (more than 1).

An N-sized array problem

To generate the offsets needed to cycle through divisions each year, we need a pattern that looks a like this:

[0,1,2,3]
[1,0,3,2]
[2,3,0,1]
[3,2,1,0]

But scaling for sized arrays. This pattern seems extremely familiar, so I'm hoping it's a personal mental block, but if we can generate the above pattern from 4, and the appropriate pattern for 5 and so on, then we can iterate through divisions in a regular manner. Very obvious in retrospect. See n-seized array generic method commit.

Generate possible binary matches for sets of games

Right now, we've got the possible binary representation of evenly distributed home/away games for a 4 game set hard coded as [3,5,6,9,10,12]. If we could pull those numbers for any given number of games (to some sane amount) without iterating over every possibility and counting 0s and 1s, that would be pretty cool, and allow for maximum possible diversity of scheduling. See bc8386d

@PhiloTFarnsworth
Copy link
Author

PhiloTFarnsworth commented May 4, 2022

Revert..., implement bit walker

This commit reverts the n-sized array generic method commit and adds a decent solution for providing the binary matches.

N-sized array

So I had a sort of shower thought and figured that I could just use a really easy solution for generating the conference offsets (the n-sized array problem). Turns out, I was wrong.

The pattern we're using to generate those is a special type of 'Latin square' which allows contains a special symmetry that makes it easy to allocate our divisional matches per year. The drawback of using this square is that we need different an entirely different formula for any number of divisions that doesn't satisfy 2^n divisions. So that leaves that pattern as really only relevant in conferences with 2, 4 or 8 divisions each.

On the other hand, it's not like an odd number of divisions can resolve into an even number of divisional matches, so this is probably not the end of the world, and would have to be addressed anyway to create schedules as a league expands or if it has a custom structure.

Bit walker

Stack Overflow spit out a pretty good answer for how to generate the patterns we need for generating even home/away splits, so this method was incorporated, and allows for any number of home/away game patterns to be generated. I'm thinking there should be an upper limit on the size of bit arrays generated. If we need to allocate something like 13 games between 2 teams, generate the combinations of 8 games, then the combinations of 5 games and join those two arrays.

@PhiloTFarnsworth
Copy link
Author

Latin square function...

This commit adds a function to build a Latin square as described above, for values that are powers of 2. Details of the implementation are provided in the comments. Added more type annotations and made those that existed more consistent.

@PhiloTFarnsworth
Copy link
Author

Speculative schedule partially implemented

This commit attempts to implement the speculative NFL schedule maker and the schedule sort algorithm.

While we can start a league and have the first season's schedule generated no problem, the generation of a schedule that's actually balanced around the previous years ranks is not yet functional.

Changes

To utilize the algorithms, I tried to be as unobtrusive as possible, though there is some minor modifications to setSchedule.ts (To allow us to pass a schedule that's already been sorted), as well as to newPhaseRegularSeason.ts to prepare the list teams in the order we require to generate the balanced schedule.

Next Steps

  • Need correct rankings method (I'm not entirely sure why it's wrong)
  • Need correct logic guarding the NFL schedule sort
    • Maybe it's as simple as checking whether a league is in a default configuration? I'm not sure.
  • General guidance to make the code more uniform with the rest of the code base.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant