Skip to content

Commit

Permalink
Improved tournament generation
Browse files Browse the repository at this point in the history
  • Loading branch information
thordy committed Nov 25, 2024
1 parent e0dd717 commit e0de38b
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 47 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Count badges for all leg types
- WLED support for venues
- TTS voice selection per venue
- Improved Tournament Generation
- Lots of new badges

#### Changed
Expand Down
12 changes: 10 additions & 2 deletions controllers/tournament_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ func NewTournament(w http.ResponseWriter, r *http.Request) {
// GenerateTournament will generate a new tournament
func GenerateTournament(w http.ResponseWriter, r *http.Request) {
SetHeaders(w)
var input models.Tournament
var input models.GenerateTournamentInput
err := json.NewDecoder(r.Body).Decode(&input)
if err != nil {
log.Println("Unable to deserialize body", err)
Expand Down Expand Up @@ -392,7 +392,15 @@ func GeneratePlayoffsTournament(w http.ResponseWriter, r *http.Request) {
return
}

tournament, err := data.GeneratePlayoffsTournament(id)
var input models.GeneratePlayoffsInput
err = json.NewDecoder(r.Body).Decode(&input)
if err != nil {
log.Println("Unable to deserialize body", err)
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

tournament, err := data.GeneratePlayoffsTournament(id, input)
if err != nil {
log.Println("Unable to create new tournament", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
Expand Down
8 changes: 4 additions & 4 deletions data/match.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,8 +472,8 @@ func SetScore(matchID int, result models.MatchResult) (*models.Match, error) {
// TODO Improve by only inserting legs where there is no score?

for i := 0; i < result.LooserScore; i++ {
res, err := tx.Exec(`INSERT INTO leg (end_time, starting_score, current_player_id, match_id, created_at, is_finished, winner_id, has_scores) VALUES
(NOW(), ?, ?, ?, NOW(), 1, ?, 0)`, match.Legs[0].StartingScore, result.LooserID, matchID, result.LooserID)
res, err := tx.Exec(`INSERT INTO leg (end_time, starting_score, current_player_id, match_id, created_at, is_finished, winner_id, has_scores, num_players) VALUES
(NOW(), ?, ?, ?, NOW(), 1, ?, 0, 2)`, match.Legs[0].StartingScore, result.LooserID, matchID, result.LooserID)
if err != nil {
tx.Rollback()
return nil, err
Expand All @@ -495,8 +495,8 @@ func SetScore(matchID int, result models.MatchResult) (*models.Match, error) {
}
var legID int64
for i := 0; i < result.WinnerScore; i++ {
res, err := tx.Exec(`INSERT INTO leg (end_time, starting_score, current_player_id, match_id, created_at, is_finished, winner_id, has_scores) VALUES
(NOW(), ?, ?, ?, NOW(), 1, ?, 0)`, match.Legs[0].StartingScore, result.WinnerID, matchID, result.WinnerID)
res, err := tx.Exec(`INSERT INTO leg (end_time, starting_score, current_player_id, match_id, created_at, is_finished, winner_id, has_scores, num_players) VALUES
(NOW(), ?, ?, ?, NOW(), 1, ?, 0, 2)`, match.Legs[0].StartingScore, result.WinnerID, matchID, result.WinnerID)
if err != nil {
tx.Rollback()
return nil, err
Expand Down
31 changes: 31 additions & 0 deletions data/player.go
Original file line number Diff line number Diff line change
Expand Up @@ -1384,3 +1384,34 @@ func GetPlayersMatchTypes() (map[int]int, error) {
}
return m, nil
}

// GetPlaceholderPlayers returns all placeholder players
func GetPlaceholderPlayers() ([]*models.Player, error) {
rows, err := models.DB.Query(`
SELECT
p.id, p.first_name, p.last_name, p.vocal_name, p.nickname, p.slack_handle, p.color, p.profile_pic_url, p.smartcard_uid,
p.board_stream_url, p.board_stream_css, p.active, p.office_id, p.is_bot, p.is_placeholder, p.is_supporter, p.created_at,
p.updated_at
FROM player p WHERE is_placeholder = 1`)
if err != nil {
return nil, err
}
defer rows.Close()

players := make([]*models.Player, 0)
for rows.Next() {
p := new(models.Player)
err := rows.Scan(&p.ID, &p.FirstName, &p.LastName, &p.VocalName, &p.Nickname, &p.SlackHandle, &p.Color, &p.ProfilePicURL,
&p.SmartcardUID, &p.BoardStreamURL, &p.BoardStreamCSS, &p.IsActive, &p.OfficeID, &p.IsBot, &p.IsPlaceholder, &p.IsSupporter,
&p.CreatedAt, &p.UpdatedAt)
if err != nil {
return nil, err
}
players = append(players, p)
}
if err = rows.Err(); err != nil {
return nil, err
}

return players, nil
}
109 changes: 71 additions & 38 deletions data/tournament.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package data

import (
"database/sql"
"errors"
"log"
"math"
"sort"
Expand Down Expand Up @@ -68,7 +69,7 @@ func AddTournamentGroup(group models.TournamentGroup) error {

// GetTournamentGroups will return all tournament groups
func GetTournamentGroups() (map[int]*models.TournamentGroup, error) {
rows, err := models.DB.Query("SELECT id, name, division FROM tournament_group")
rows, err := models.DB.Query("SELECT id, name, is_generated, is_playoffs, division FROM tournament_group")
if err != nil {
return nil, err
}
Expand All @@ -77,7 +78,7 @@ func GetTournamentGroups() (map[int]*models.TournamentGroup, error) {
groups := make(map[int]*models.TournamentGroup)
for rows.Next() {
group := new(models.TournamentGroup)
err := rows.Scan(&group.ID, &group.Name, &group.Division)
err := rows.Scan(&group.ID, &group.Name, &group.IsGenerated, &group.IsPlayoffs, &group.Division)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -827,19 +828,13 @@ func NewTournament(tournament models.Tournament) (*models.Tournament, error) {
}

// GenerateTournament generates a new tournament
func GenerateTournament(input models.Tournament) (*models.Tournament, error) {
preset, err := GetTournamentPreset(int(input.PresetID.Int64))
if err != nil {
return nil, err
}

func GenerateTournament(input models.GenerateTournamentInput) (*models.Tournament, error) {
officeID := input.OfficeID
tournament, err := NewTournament(models.Tournament{
Name: input.Name,
ShortName: input.ShortName,
IsPlayoffs: false,
IsPlayoffs: input.IsPlayoffs,
OfficeID: officeID,
PresetID: input.PresetID,
ManualAdmin: input.ManualAdmin,
Players: input.Players,
StartTime: null.TimeFrom(time.Now()),
Expand All @@ -849,6 +844,9 @@ func GenerateTournament(input models.Tournament) (*models.Tournament, error) {
return nil, err
}

matchType := models.MatchType{ID: input.MatchTypeID}
matchMode := models.MatchMode{ID: input.MatchModeID}

players := input.Players
for i := 0; i < len(players); i++ {
for j := i + 1; j < len(players); j++ {
Expand All @@ -858,15 +856,15 @@ func GenerateTournament(input models.Tournament) (*models.Tournament, error) {
}

match, err := NewMatch(models.Match{
MatchType: preset.MatchType,
MatchMode: preset.MatchMode,
MatchType: &matchType,
MatchMode: &matchMode,
//VenueID: 1,
OfficeID: null.IntFrom(int64(officeID)),
IsPractice: false,
TournamentID: null.IntFrom(int64(tournament.ID)),
Players: []int{players[i].PlayerID, players[j].PlayerID},
Legs: []*models.Leg{{
StartingScore: preset.StartingScore,
StartingScore: input.StartingScore,
Parameters: &models.LegParameters{OutshotType: &models.OutshotType{ID: models.OUTSHOTDOUBLE}}}},
})
if err != nil {
Expand All @@ -879,7 +877,7 @@ func GenerateTournament(input models.Tournament) (*models.Tournament, error) {
}

// GeneratePlayoffsTournament generates playoffs matches for the given tournament
func GeneratePlayoffsTournament(tournamentID int) (*models.Tournament, error) {
func GeneratePlayoffsTournament(tournamentID int, input models.GeneratePlayoffsInput) (*models.Tournament, error) {
tournament, err := GetTournament(tournamentID)
if err != nil {
return nil, err
Expand All @@ -902,13 +900,49 @@ func GeneratePlayoffsTournament(tournamentID int) (*models.Tournament, error) {
group2 = overview[keys[1]]
}

preset := tournament.Preset
playoffsGroupID := preset.PlayoffsTournamentGroup.ID
mt := preset.MatchType
walkoverPlayerID := preset.PlayerIDWalkover
placeholderHomeID := preset.PlayerIDPlaceholderHome
placeholderAwayID := preset.PlayerIDPlaceholderAway
startingScore := preset.StartingScore
// Get the playoff tournament group
groups, err := GetTournamentGroups()
if err != nil {
return nil, err
}
var playoffsGroup *models.TournamentGroup
for _, group := range groups {
if group.IsPlayoffs {
playoffsGroup = group
break
}
}
playoffsGroupID := playoffsGroup.ID

// Get type and starting score from the regular season matches
regularSeasonMatches, err := GetTournamentMatches(tournamentID)
if err != nil {
return nil, err
}
var regularSeasonMatch models.Match
var startingScore int
for _, value := range regularSeasonMatches {
regularSeasonMatch = *value[0]
legs, err := GetLegsForMatch(regularSeasonMatch.ID)
if err != nil {
return nil, err
}
startingScore = legs[0].StartingScore
break
}
matchType := regularSeasonMatch.MatchType

// Get placeholder players
placeholders, err := GetPlaceholderPlayers()
if err != nil {
return nil, err
}
if len(placeholders) < 3 {
return nil, errors.New("missing 3 placeholder players from database")
}
placeholderHomeID := placeholders[0].ID
placeholderAwayID := placeholders[1].ID
walkoverPlayerID := placeholders[2].ID

players := make([]*models.Player2Tournament, 0)
for _, groupPlayer := range append(group1, group2...) {
Expand All @@ -928,7 +962,6 @@ func GeneratePlayoffsTournament(tournamentID int) (*models.Tournament, error) {
IsPlayoffs: true,
OfficeID: tournament.OfficeID,
Players: players,
PresetID: tournament.PresetID,
StartTime: null.TimeFrom(time.Now()),
EndTime: null.TimeFrom(time.Now()),
ManualAdmin: tournament.ManualAdmin,
Expand All @@ -944,17 +977,17 @@ func GeneratePlayoffsTournament(tournamentID int) (*models.Tournament, error) {

matches := make([]*models.Match, 0)
// Create Grand Final
match, err := createTournamentMatch(playoffs.ID, []int{placeholderHomeID, placeholderAwayID}, startingScore, models.X01,
tournament.OfficeID, mt, preset.MatchModeGrandFinal)
match, err := createTournamentMatch(playoffs.ID, []int{placeholderHomeID, placeholderAwayID}, startingScore, -1,
tournament.OfficeID, matchType, input.MatchModeGFID)
if err != nil {
return nil, err
}
matches = append(matches, match)

// Create Semi Final Matches
if numPlayers > 4 {
semis, err := createTournamentMatches(2, playoffs.ID, []int{placeholderHomeID, placeholderAwayID}, startingScore, models.X01,
tournament.OfficeID, mt, preset.MatchModeSemiFinal)
semis, err := createTournamentMatches(2, playoffs.ID, []int{placeholderHomeID, placeholderAwayID}, startingScore, -1,
tournament.OfficeID, matchType, input.MatchModeSFID)
if err != nil {
return nil, err
}
Expand All @@ -979,8 +1012,8 @@ func GeneratePlayoffsTournament(tournamentID int) (*models.Tournament, error) {
// Walkover, so use placeholder
away = walkoverPlayerID
}
match, err := createTournamentMatch(playoffs.ID, []int{home, away}, startingScore, models.X01,
tournament.OfficeID, mt, preset.MatchModeSemiFinal)
match, err := createTournamentMatch(playoffs.ID, []int{home, away}, startingScore, -1,
tournament.OfficeID, matchType, input.MatchModeSFID)
if err != nil {
return nil, err
}
Expand All @@ -990,8 +1023,8 @@ func GeneratePlayoffsTournament(tournamentID int) (*models.Tournament, error) {

// Create Quarter Final Matches
if numPlayers > 8 {
quarters, err := createTournamentMatches(4, playoffs.ID, []int{placeholderHomeID, placeholderAwayID}, startingScore, models.X01,
tournament.OfficeID, mt, preset.MatchModeQuarterFinal)
quarters, err := createTournamentMatches(4, playoffs.ID, []int{placeholderHomeID, placeholderAwayID}, startingScore, -1,
tournament.OfficeID, matchType, input.MatchModeQFID)
if err != nil {
return nil, err
}
Expand All @@ -1010,8 +1043,8 @@ func GeneratePlayoffsTournament(tournamentID int) (*models.Tournament, error) {
// Walkover, so use placeholder
away = walkoverPlayerID
}
match, err := createTournamentMatch(playoffs.ID, []int{home, away}, startingScore, models.X01,
tournament.OfficeID, mt, preset.MatchModeLast16)
match, err := createTournamentMatch(playoffs.ID, []int{home, away}, startingScore, -1,
tournament.OfficeID, matchType, input.MatchModeLast16ID)
if err != nil {
return nil, err
}
Expand All @@ -1037,8 +1070,8 @@ func GeneratePlayoffsTournament(tournamentID int) (*models.Tournament, error) {
// Walkover, so use placeholder
away = walkoverPlayerID
}
match, err := createTournamentMatch(playoffs.ID, []int{home, away}, startingScore, models.X01,
tournament.OfficeID, mt, preset.MatchModeQuarterFinal)
match, err := createTournamentMatch(playoffs.ID, []int{home, away}, startingScore, -1,
tournament.OfficeID, matchType, input.MatchModeQFID)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1125,10 +1158,10 @@ func GeneratePlayoffsTournament(tournamentID int) (*models.Tournament, error) {
return GetTournament(playoffs.ID)
}

func createTournamentMatches(num int, tournamentID int, players []int, startingScore int, venueID int, officeID int, matchType *models.MatchType, matchMode *models.MatchMode) ([]*models.Match, error) {
func createTournamentMatches(num int, tournamentID int, players []int, startingScore int, venueID int, officeID int, matchType *models.MatchType, matchModeID int) ([]*models.Match, error) {
matches := make([]*models.Match, 0)
for i := 0; i < num; i++ {
match, err := createTournamentMatch(tournamentID, players, startingScore, venueID, officeID, matchType, matchMode)
match, err := createTournamentMatch(tournamentID, players, startingScore, venueID, officeID, matchType, matchModeID)
if err != nil {
return nil, err
}
Expand All @@ -1137,10 +1170,10 @@ func createTournamentMatches(num int, tournamentID int, players []int, startingS
return matches, nil
}

func createTournamentMatch(tournamentID int, players []int, startingScore int, venueID int, officeID int, matchType *models.MatchType, matchMode *models.MatchMode) (*models.Match, error) {
func createTournamentMatch(tournamentID int, players []int, startingScore int, venueID int, officeID int, matchType *models.MatchType, matchModeID int) (*models.Match, error) {
match, err := NewMatch(models.Match{
MatchType: matchType,
MatchMode: matchMode,
MatchMode: &models.MatchMode{ID: matchModeID},
//VenueID: null.IntFrom(int64(venueID)),
OfficeID: null.IntFrom(int64(officeID)),
IsPractice: false,
Expand Down
30 changes: 27 additions & 3 deletions models/tournament.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,11 @@ type Tournament struct {

// TournamentGroup struct for storing tournament groups
type TournamentGroup struct {
ID int `json:"id"`
Name string `json:"name"`
Division null.Int `json:"division,omitempty"`
ID int `json:"id"`
Name string `json:"name"`
IsGenerated bool `json:"is_generated"`
IsPlayoffs bool `json:"is_playoffs"`
Division null.Int `json:"division,omitempty"`
}

// Player2Tournament struct for storing player to tounament links
Expand Down Expand Up @@ -119,3 +121,25 @@ type TournamentPreset struct {
PlayerIDPlaceholderAway int `json:"player_id_placeholder_away"`
Description null.String `json:"description"`
}

// GenerateTournamentInput struct for storing generate tournament inputs
type GenerateTournamentInput struct {
Name string `json:"name"`
ShortName string `json:"short_name"`
IsPlayoffs bool `json:"is_playoffs"`
ManualAdmin bool `json:"manual_admin"`
OfficeID int `json:"office_id"`
MatchModeID int `json:"match_mode_id"`
MatchTypeID int `json:"match_type_id"`
StartingScore int `json:"starting_score"`
Players []*Player2Tournament `json:"players,omitempty"`
}

// GeneratePlayoffsInput struct for storing generate playoffs inputs
type GeneratePlayoffsInput struct {
MatchModeLast32ID int `json:"match_mode_last32"`
MatchModeLast16ID int `json:"match_mode_last16"`
MatchModeQFID int `json:"match_mode_quarterFinals"`
MatchModeSFID int `json:"match_mode_semiFinals"`
MatchModeGFID int `json:"match_mode_grandFinals"`
}

0 comments on commit e0de38b

Please sign in to comment.