diff --git a/match_play.go b/match_play.go index a9f179d..e7e99fb 100644 --- a/match_play.go +++ b/match_play.go @@ -9,6 +9,7 @@ import ( "fmt" "github.com/Team254/cheesy-arena/game" "github.com/Team254/cheesy-arena/model" + "github.com/Team254/cheesy-arena/tournament" "github.com/gorilla/mux" "github.com/mitchellh/mapstructure" "io" @@ -486,12 +487,12 @@ func CommitMatchScore(match *model.Match, matchResult *model.MatchResult, loadTo if match.Type != "practice" { // Regenerate the residual yellow cards that teams may carry. - db.CalculateTeamCards(match.Type) + tournament.CalculateTeamCards(db, match.Type) } if match.Type == "qualification" { // Recalculate all the rankings. - err = db.CalculateRankings() + err = tournament.CalculateRankings(db) if err != nil { return err } @@ -499,7 +500,7 @@ func CommitMatchScore(match *model.Match, matchResult *model.MatchResult, loadTo if match.Type == "elimination" { // Generate any subsequent elimination matches. - _, err = UpdateEliminationSchedule(db, time.Now().Add(time.Second*elimMatchSpacingSec)) + _, err = tournament.UpdateEliminationSchedule(db, time.Now().Add(time.Second*tournament.ElimMatchSpacingSec)) if err != nil { return err } diff --git a/match_play_test.go b/match_play_test.go index 3d4464b..61c3de5 100644 --- a/match_play_test.go +++ b/match_play_test.go @@ -8,6 +8,7 @@ import ( "fmt" "github.com/Team254/cheesy-arena/game" "github.com/Team254/cheesy-arena/model" + "github.com/Team254/cheesy-arena/tournament" "github.com/gorilla/websocket" "github.com/mitchellh/mapstructure" "github.com/stretchr/testify/assert" @@ -222,7 +223,7 @@ func TestCommitCards(t *testing.T) { assert.True(t, team.YellowCard) // Check that a red card in eliminations zeroes out the score. - createTestAlliances(db, 2) + tournament.CreateTestAlliances(db, 2) match.Type = "elimination" db.SaveMatch(match) matchResult = model.BuildTestMatchResult(match.Id, 10) diff --git a/match_review_test.go b/match_review_test.go index e1cf841..a343e3a 100644 --- a/match_review_test.go +++ b/match_review_test.go @@ -6,6 +6,7 @@ package main import ( "fmt" "github.com/Team254/cheesy-arena/model" + "github.com/Team254/cheesy-arena/tournament" "github.com/stretchr/testify/assert" "testing" ) @@ -43,7 +44,7 @@ func TestMatchReviewEditExistingResult(t *testing.T) { matchResult := model.BuildTestMatchResult(match.Id, 1) matchResult.MatchType = match.Type db.CreateMatchResult(matchResult) - createTestAlliances(db, 2) + tournament.CreateTestAlliances(db, 2) recorder := getHttpResponse("/match_review") assert.Equal(t, 200, recorder.Code) @@ -80,7 +81,7 @@ func TestMatchReviewCreateNewResult(t *testing.T) { match := model.Match{Type: "elimination", DisplayName: "QF4-3", Status: "complete", Winner: "R", Red1: 1001, Red2: 1002, Red3: 1003, Blue1: 1004, Blue2: 1005, Blue3: 1006} db.CreateMatch(&match) - createTestAlliances(db, 2) + tournament.CreateTestAlliances(db, 2) recorder := getHttpResponse("/match_review") assert.Equal(t, 200, recorder.Code) diff --git a/model/ranking.go b/model/ranking.go index 17a8efb..ba05cba 100644 --- a/model/ranking.go +++ b/model/ranking.go @@ -8,8 +8,6 @@ package model import ( "encoding/json" "github.com/Team254/cheesy-arena/game" - "sort" - "strconv" ) type RankingDb struct { @@ -78,156 +76,33 @@ func (database *Database) GetAllRankings() ([]game.Ranking, error) { return rankings, err } -// Determines the rankings from the stored match results, and saves them to the database. -func (database *Database) CalculateRankings() error { - matches, err := database.GetMatchesByType("qualification") - if err != nil { - return err - } - rankings := make(map[int]*game.Ranking) - for _, match := range matches { - if match.Status != "complete" { - continue - } - matchResult, err := database.GetMatchResultForMatch(match.Id) - if err != nil { - return err - } - if !match.Red1IsSurrogate { - addMatchResultToRankings(rankings, match.Red1, matchResult, true) - } - if !match.Red2IsSurrogate { - addMatchResultToRankings(rankings, match.Red2, matchResult, true) - } - if !match.Red3IsSurrogate { - addMatchResultToRankings(rankings, match.Red3, matchResult, true) - } - if !match.Blue1IsSurrogate { - addMatchResultToRankings(rankings, match.Blue1, matchResult, false) - } - if !match.Blue2IsSurrogate { - addMatchResultToRankings(rankings, match.Blue2, matchResult, false) - } - if !match.Blue3IsSurrogate { - addMatchResultToRankings(rankings, match.Blue3, matchResult, false) - } - } - - sortedRankings := sortRankings(rankings) - - // Stuff the rankings into the database in an atomic operation to prevent messing them up halfway. +// Deletes the existing rankings and inserts the given ones as a replacement, in a single transaction. +func (database *Database) ReplaceAllRankings(rankings game.Rankings) error { transaction, err := database.rankingMap.Begin() if err != nil { return err } + _, err = transaction.Exec("DELETE FROM rankings") if err != nil { + transaction.Rollback() return err } - for rank, ranking := range sortedRankings { - ranking.Rank = rank + 1 + + for _, ranking := range rankings { rankingDb, err := serializeRanking(ranking) if err != nil { + transaction.Rollback() return err } err = transaction.Insert(rankingDb) if err != nil { - return err - } - } - err = transaction.Commit() - if err != nil { - return err - } - - return nil -} - -// Checks all the match results for yellow and red cards, and updates the team model accordingly. -func (database *Database) CalculateTeamCards(matchType string) error { - teams, err := database.GetAllTeams() - if err != nil { - return err - } - teamsMap := make(map[string]Team) - for _, team := range teams { - team.YellowCard = false - teamsMap[strconv.Itoa(team.Id)] = team - } - - matches, err := database.GetMatchesByType(matchType) - if err != nil { - return err - } - for _, match := range matches { - if match.Status != "complete" { - continue - } - matchResult, err := database.GetMatchResultForMatch(match.Id) - if err != nil { - return err - } - - // Mark the team as having a yellow card if they got either a yellow or red in a previous match. - for teamId, card := range matchResult.RedCards { - if team, ok := teamsMap[teamId]; ok && card != "" { - team.YellowCard = true - teamsMap[teamId] = team - } - } - for teamId, card := range matchResult.BlueCards { - if team, ok := teamsMap[teamId]; ok && card != "" { - team.YellowCard = true - teamsMap[teamId] = team - } - } - } - - // Save the teams to the database. - for _, team := range teamsMap { - err = database.SaveTeam(&team) - if err != nil { + transaction.Rollback() return err } } - return nil -} - -// Incrementally accounts for the given match result in the set of rankings that are being built. -func addMatchResultToRankings(rankings map[int]*game.Ranking, teamId int, matchResult *MatchResult, isRed bool) { - ranking := rankings[teamId] - if ranking == nil { - ranking = &game.Ranking{TeamId: teamId} - rankings[teamId] = ranking - } - - // Determine whether the team was disqualified. - var cards map[string]string - if isRed { - cards = matchResult.RedCards - } else { - cards = matchResult.BlueCards - } - disqualified := false - if card, ok := cards[strconv.Itoa(teamId)]; ok && card == "red" { - disqualified = true - } - - if isRed { - ranking.AddScoreSummary(matchResult.RedScoreSummary(), matchResult.BlueScoreSummary(), disqualified) - } else { - ranking.AddScoreSummary(matchResult.BlueScoreSummary(), matchResult.RedScoreSummary(), disqualified) - } -} - -func sortRankings(rankings map[int]*game.Ranking) game.Rankings { - var sortedRankings game.Rankings - for _, ranking := range rankings { - sortedRankings = append(sortedRankings, ranking) - } - sort.Sort(sortedRankings) - return sortedRankings + return transaction.Commit() } // Converts the nested struct MatchResult to the DB version that has JSON fields. diff --git a/model/ranking_test.go b/model/ranking_test.go index 68ef437..35dff5c 100644 --- a/model/ranking_test.go +++ b/model/ranking_test.go @@ -67,84 +67,3 @@ func TestGetAllRankings(t *testing.T) { assert.Equal(t, i+1, rankings[i].TeamId) } } - -func TestCalculateRankings(t *testing.T) { - db := setupTestDb(t) - - setupMatchResultsForRankings(db) - err := db.CalculateRankings() - assert.Nil(t, err) - rankings, err := db.GetAllRankings() - assert.Nil(t, err) - if assert.Equal(t, 6, len(rankings)) { - assert.Equal(t, 4, rankings[0].TeamId) - assert.Equal(t, 5, rankings[1].TeamId) - assert.Equal(t, 6, rankings[2].TeamId) - assert.Equal(t, 1, rankings[3].TeamId) - assert.Equal(t, 3, rankings[4].TeamId) - assert.Equal(t, 2, rankings[5].TeamId) - } - - // Test after changing a match result. - matchResult3 := BuildTestMatchResult(3, 3) - matchResult3.RedScore, matchResult3.BlueScore = matchResult3.BlueScore, matchResult3.RedScore - err = db.CreateMatchResult(matchResult3) - assert.Nil(t, err) - err = db.CalculateRankings() - assert.Nil(t, err) - rankings, err = db.GetAllRankings() - assert.Nil(t, err) - if assert.Equal(t, 6, len(rankings)) { - assert.Equal(t, 6, rankings[0].TeamId) - assert.Equal(t, 5, rankings[1].TeamId) - assert.Equal(t, 4, rankings[2].TeamId) - assert.Equal(t, 1, rankings[3].TeamId) - assert.Equal(t, 3, rankings[4].TeamId) - assert.Equal(t, 2, rankings[5].TeamId) - } -} - -// Sets up a schedule and results that touches on all possible variables. -func setupMatchResultsForRankings(db *Database) { - match1 := Match{Type: "qualification", DisplayName: "1", Red1: 1, Red2: 2, Red3: 3, Blue1: 4, Blue2: 5, - Blue3: 6, Status: "complete"} - db.CreateMatch(&match1) - matchResult1 := BuildTestMatchResult(match1.Id, 1) - matchResult1.RedCards = map[string]string{"2": "red"} - db.CreateMatchResult(matchResult1) - - match2 := Match{Type: "qualification", DisplayName: "2", Red1: 1, Red2: 3, Red3: 5, Blue1: 2, Blue2: 4, - Blue3: 6, Status: "complete", Red2IsSurrogate: true, Blue3IsSurrogate: true} - db.CreateMatch(&match2) - matchResult2 := BuildTestMatchResult(match2.Id, 1) - matchResult2.BlueScore = matchResult2.RedScore - db.CreateMatchResult(matchResult2) - - match3 := Match{Type: "qualification", DisplayName: "3", Red1: 6, Red2: 5, Red3: 4, Blue1: 3, Blue2: 2, - Blue3: 1, Status: "complete", Red3IsSurrogate: true} - db.CreateMatch(&match3) - matchResult3 := BuildTestMatchResult(match3.Id, 1) - db.CreateMatchResult(matchResult3) - matchResult3 = NewMatchResult() - matchResult3.MatchId = match3.Id - matchResult3.PlayNumber = 2 - db.CreateMatchResult(matchResult3) - - match4 := Match{Type: "practice", DisplayName: "1", Red1: 1, Red2: 2, Red3: 3, Blue1: 4, Blue2: 5, - Blue3: 6, Status: "complete"} - db.CreateMatch(&match4) - matchResult4 := BuildTestMatchResult(match4.Id, 1) - db.CreateMatchResult(matchResult4) - - match5 := Match{Type: "elimination", DisplayName: "F-1", Red1: 1, Red2: 2, Red3: 3, Blue1: 4, Blue2: 5, - Blue3: 6, Status: "complete"} - db.CreateMatch(&match5) - matchResult5 := BuildTestMatchResult(match5.Id, 1) - db.CreateMatchResult(matchResult5) - - match6 := Match{Type: "qualification", DisplayName: "4", Red1: 7, Red2: 8, Red3: 9, Blue1: 10, Blue2: 11, - Blue3: 12, Status: ""} - db.CreateMatch(&match6) - matchResult6 := BuildTestMatchResult(match6.Id, 1) - db.CreateMatchResult(matchResult6) -} diff --git a/reports.go b/reports.go index 4f82e3f..8579b10 100644 --- a/reports.go +++ b/reports.go @@ -7,6 +7,7 @@ package main import ( "fmt" + "github.com/Team254/cheesy-arena/tournament" "github.com/gorilla/mux" "github.com/jung-kurt/gofpdf" "net/http" @@ -148,7 +149,7 @@ func SchedulePdfReportHandler(w http.ResponseWriter, r *http.Request) { } matchesPerTeam := 0 if len(teams) > 0 { - matchesPerTeam = len(matches) * teamsPerMatch / len(teams) + matchesPerTeam = len(matches) * tournament.TeamsPerMatch / len(teams) } // The widths of the table columns in mm, stored here so that they can be referenced for each row. diff --git a/setup_alliance_selection.go b/setup_alliance_selection.go index 41797d0..4432124 100644 --- a/setup_alliance_selection.go +++ b/setup_alliance_selection.go @@ -8,6 +8,7 @@ package main import ( "fmt" "github.com/Team254/cheesy-arena/model" + "github.com/Team254/cheesy-arena/tournament" "html/template" "net/http" "strconv" @@ -188,14 +189,14 @@ func AllianceSelectionFinalizeHandler(w http.ResponseWriter, r *http.Request) { } // Generate the first round of elimination matches. - _, err = UpdateEliminationSchedule(db, startTime) + _, err = tournament.UpdateEliminationSchedule(db, startTime) if err != nil { handleWebErr(w, err) return } // Reset yellow cards. - err = db.CalculateTeamCards("elimination") + err = tournament.CalculateTeamCards(db, "elimination") if err != nil { handleWebErr(w, err) return diff --git a/setup_schedule.go b/setup_schedule.go index 505277e..217b907 100644 --- a/setup_schedule.go +++ b/setup_schedule.go @@ -8,6 +8,7 @@ package main import ( "fmt" "github.com/Team254/cheesy-arena/model" + "github.com/Team254/cheesy-arena/tournament" "html/template" "net/http" "strconv" @@ -16,7 +17,7 @@ import ( // Global vars to hold schedules that are in the process of being generated. var cachedMatchType string -var cachedScheduleBlocks []ScheduleBlock +var cachedScheduleBlocks []tournament.ScheduleBlock var cachedMatches []model.Match var cachedTeamFirstMatches map[int]string @@ -30,7 +31,7 @@ func ScheduleGetHandler(w http.ResponseWriter, r *http.Request) { tomorrow := time.Now().AddDate(0, 0, 1) location, _ := time.LoadLocation("Local") startTime := time.Date(tomorrow.Year(), tomorrow.Month(), tomorrow.Day(), 9, 0, 0, 0, location) - cachedScheduleBlocks = append(cachedScheduleBlocks, ScheduleBlock{startTime, 10, 360}) + cachedScheduleBlocks = append(cachedScheduleBlocks, tournament.ScheduleBlock{startTime, 10, 360}) cachedMatchType = "practice" } renderSchedule(w, r, "") @@ -67,7 +68,7 @@ func ScheduleGeneratePostHandler(w http.ResponseWriter, r *http.Request) { "a schedule.", len(teams))) return } - matches, err := BuildRandomSchedule(teams, scheduleBlocks, r.PostFormValue("matchType")) + matches, err := tournament.BuildRandomSchedule(teams, scheduleBlocks, r.PostFormValue("matchType")) if err != nil { renderSchedule(w, r, fmt.Sprintf("Error generating schedule: %s.", err.Error())) return @@ -180,7 +181,7 @@ func renderSchedule(w http.ResponseWriter, r *http.Request, errorMessage string) data := struct { *model.EventSettings MatchType string - ScheduleBlocks []ScheduleBlock + ScheduleBlocks []tournament.ScheduleBlock NumTeams int Matches []model.Match TeamFirstMatches map[int]string @@ -195,13 +196,13 @@ func renderSchedule(w http.ResponseWriter, r *http.Request, errorMessage string) } // Converts the post form variables into a slice of schedule blocks. -func getScheduleBlocks(r *http.Request) ([]ScheduleBlock, error) { +func getScheduleBlocks(r *http.Request) ([]tournament.ScheduleBlock, error) { numScheduleBlocks, err := strconv.Atoi(r.PostFormValue("numScheduleBlocks")) if err != nil { - return []ScheduleBlock{}, err + return []tournament.ScheduleBlock{}, err } var returnErr error - scheduleBlocks := make([]ScheduleBlock, numScheduleBlocks) + scheduleBlocks := make([]tournament.ScheduleBlock, numScheduleBlocks) location, _ := time.LoadLocation("Local") for i := 0; i < numScheduleBlocks; i++ { scheduleBlocks[i].StartTime, err = time.ParseInLocation("2006-01-02 03:04:05 PM", diff --git a/setup_settings_test.go b/setup_settings_test.go index 57e1d79..d05f2ac 100644 --- a/setup_settings_test.go +++ b/setup_settings_test.go @@ -8,6 +8,7 @@ import ( "fmt" "github.com/Team254/cheesy-arena/game" "github.com/Team254/cheesy-arena/model" + "github.com/Team254/cheesy-arena/tournament" "github.com/stretchr/testify/assert" "io" "mime/multipart" @@ -72,7 +73,7 @@ func TestSetupSettingsClearDb(t *testing.T) { assert.Empty(t, matches) rankings, _ := db.GetAllRankings() assert.Empty(t, rankings) - db.CalculateRankings() + tournament.CalculateRankings(db) assert.Empty(t, rankings) alliances, _ := db.GetAllAlliances() assert.Empty(t, alliances) diff --git a/elimination_schedule.go b/tournament/elimination_schedule.go similarity index 98% rename from elimination_schedule.go rename to tournament/elimination_schedule.go index d00ae39..598d94d 100644 --- a/elimination_schedule.go +++ b/tournament/elimination_schedule.go @@ -3,7 +3,7 @@ // // Functions for creating and updating the elimination match schedule. -package main +package tournament import ( "fmt" @@ -12,7 +12,7 @@ import ( "time" ) -const elimMatchSpacingSec = 600 +const ElimMatchSpacingSec = 600 // Incrementally creates any elimination matches that can be created, based on the results of alliance // selection or prior elimination rounds. Returns the winning alliance once it has been determined. @@ -36,7 +36,7 @@ func UpdateEliminationSchedule(database *model.Database, startTime time.Time) ([ if match.Status == "complete" { continue } - match.Time = startTime.Add(time.Duration(matchIndex*elimMatchSpacingSec) * time.Second) + match.Time = startTime.Add(time.Duration(matchIndex*ElimMatchSpacingSec) * time.Second) database.SaveMatch(&match) matchIndex++ } diff --git a/elimination_schedule_test.go b/tournament/elimination_schedule_test.go similarity index 62% rename from elimination_schedule_test.go rename to tournament/elimination_schedule_test.go index bcef1e8..413a096 100644 --- a/elimination_schedule_test.go +++ b/tournament/elimination_schedule_test.go @@ -1,7 +1,7 @@ // Copyright 2014 Team 254. All Rights Reserved. // Author: pat@patfairbank.com (Patrick Fairbank) -package main +package tournament import ( "github.com/Team254/cheesy-arena/model" @@ -11,25 +11,25 @@ import ( ) func TestEliminationScheduleInitial(t *testing.T) { - setupTest(t) + database := setupTestDb(t) - createTestAlliances(db, 2) - _, err := UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 2) + _, err := UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err := db.GetMatchesByType("elimination") + matches, err := database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 3, len(matches)) { assertMatch(t, matches[0], "F-1", 1, 2) assertMatch(t, matches[1], "F-2", 1, 2) assertMatch(t, matches[2], "F-3", 1, 2) } - db.TruncateAllianceTeams() - db.TruncateMatches() + database.TruncateAllianceTeams() + database.TruncateMatches() - createTestAlliances(db, 3) - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 3) + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 6, len(matches)) { assertMatch(t, matches[0], "SF2-1", 2, 3) @@ -39,13 +39,13 @@ func TestEliminationScheduleInitial(t *testing.T) { assertMatch(t, matches[4], "F-2", 1, 0) assertMatch(t, matches[5], "F-3", 1, 0) } - db.TruncateAllianceTeams() - db.TruncateMatches() + database.TruncateAllianceTeams() + database.TruncateMatches() - createTestAlliances(db, 4) - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 4) + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 6, len(matches)) { assertMatch(t, matches[0], "SF1-1", 1, 4) @@ -55,13 +55,13 @@ func TestEliminationScheduleInitial(t *testing.T) { assertMatch(t, matches[4], "SF1-3", 1, 4) assertMatch(t, matches[5], "SF2-3", 2, 3) } - db.TruncateAllianceTeams() - db.TruncateMatches() + database.TruncateAllianceTeams() + database.TruncateMatches() - createTestAlliances(db, 5) - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 5) + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 9, len(matches)) { assertMatch(t, matches[0], "QF2-1", 4, 5) @@ -74,13 +74,13 @@ func TestEliminationScheduleInitial(t *testing.T) { assertMatch(t, matches[7], "SF1-3", 1, 0) assertMatch(t, matches[8], "SF2-3", 2, 3) } - db.TruncateAllianceTeams() - db.TruncateMatches() + database.TruncateAllianceTeams() + database.TruncateMatches() - createTestAlliances(db, 6) - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 6) + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 12, len(matches)) { assertMatch(t, matches[0], "QF2-1", 4, 5) @@ -96,13 +96,13 @@ func TestEliminationScheduleInitial(t *testing.T) { assertMatch(t, matches[10], "SF1-3", 1, 0) assertMatch(t, matches[11], "SF2-3", 2, 0) } - db.TruncateAllianceTeams() - db.TruncateMatches() + database.TruncateAllianceTeams() + database.TruncateMatches() - createTestAlliances(db, 7) - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 7) + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 12, len(matches)) { assertMatch(t, matches[0], "QF2-1", 4, 5) @@ -118,13 +118,13 @@ func TestEliminationScheduleInitial(t *testing.T) { assertMatch(t, matches[10], "SF1-2", 1, 0) assertMatch(t, matches[11], "SF1-3", 1, 0) } - db.TruncateAllianceTeams() - db.TruncateMatches() + database.TruncateAllianceTeams() + database.TruncateMatches() - createTestAlliances(db, 8) - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 8) + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 12, len(matches)) { assertMatch(t, matches[0], "QF1-1", 1, 8) @@ -140,13 +140,13 @@ func TestEliminationScheduleInitial(t *testing.T) { assertMatch(t, matches[10], "QF3-3", 2, 7) assertMatch(t, matches[11], "QF4-3", 3, 6) } - db.TruncateAllianceTeams() - db.TruncateMatches() + database.TruncateAllianceTeams() + database.TruncateMatches() - createTestAlliances(db, 9) - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 9) + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 15, len(matches)) { assertMatch(t, matches[0], "EF2-1", 8, 9) @@ -165,13 +165,13 @@ func TestEliminationScheduleInitial(t *testing.T) { assertMatch(t, matches[13], "QF3-3", 2, 7) assertMatch(t, matches[14], "QF4-3", 3, 6) } - db.TruncateAllianceTeams() - db.TruncateMatches() + database.TruncateAllianceTeams() + database.TruncateMatches() - createTestAlliances(db, 10) - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 10) + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 18, len(matches)) { assertMatch(t, matches[0], "EF2-1", 8, 9) @@ -193,13 +193,13 @@ func TestEliminationScheduleInitial(t *testing.T) { assertMatch(t, matches[16], "QF3-3", 2, 0) assertMatch(t, matches[17], "QF4-3", 3, 6) } - db.TruncateAllianceTeams() - db.TruncateMatches() + database.TruncateAllianceTeams() + database.TruncateMatches() - createTestAlliances(db, 11) - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 11) + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 21, len(matches)) { assertMatch(t, matches[0], "EF2-1", 8, 9) @@ -224,13 +224,13 @@ func TestEliminationScheduleInitial(t *testing.T) { assertMatch(t, matches[19], "QF3-3", 2, 0) assertMatch(t, matches[20], "QF4-3", 3, 0) } - db.TruncateAllianceTeams() - db.TruncateMatches() + database.TruncateAllianceTeams() + database.TruncateMatches() - createTestAlliances(db, 12) - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 12) + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 24, len(matches)) { assertMatch(t, matches[0], "EF2-1", 8, 9) @@ -258,13 +258,13 @@ func TestEliminationScheduleInitial(t *testing.T) { assertMatch(t, matches[22], "QF3-3", 2, 0) assertMatch(t, matches[23], "QF4-3", 3, 0) } - db.TruncateAllianceTeams() - db.TruncateMatches() + database.TruncateAllianceTeams() + database.TruncateMatches() - createTestAlliances(db, 13) - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 13) + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 24, len(matches)) { assertMatch(t, matches[0], "EF2-1", 8, 9) @@ -292,13 +292,13 @@ func TestEliminationScheduleInitial(t *testing.T) { assertMatch(t, matches[22], "QF3-3", 2, 0) assertMatch(t, matches[23], "QF4-3", 3, 0) } - db.TruncateAllianceTeams() - db.TruncateMatches() + database.TruncateAllianceTeams() + database.TruncateMatches() - createTestAlliances(db, 14) - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 14) + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 24, len(matches)) { assertMatch(t, matches[0], "EF2-1", 8, 9) @@ -326,13 +326,13 @@ func TestEliminationScheduleInitial(t *testing.T) { assertMatch(t, matches[22], "QF1-3", 1, 0) assertMatch(t, matches[23], "QF3-3", 2, 0) } - db.TruncateAllianceTeams() - db.TruncateMatches() + database.TruncateAllianceTeams() + database.TruncateMatches() - createTestAlliances(db, 15) - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 15) + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 24, len(matches)) { assertMatch(t, matches[0], "EF2-1", 8, 9) @@ -360,13 +360,13 @@ func TestEliminationScheduleInitial(t *testing.T) { assertMatch(t, matches[22], "QF1-2", 1, 0) assertMatch(t, matches[23], "QF1-3", 1, 0) } - db.TruncateAllianceTeams() - db.TruncateMatches() + database.TruncateAllianceTeams() + database.TruncateMatches() - createTestAlliances(db, 16) - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 16) + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 24, len(matches)) { assertMatch(t, matches[0], "EF1-1", 1, 16) @@ -394,113 +394,113 @@ func TestEliminationScheduleInitial(t *testing.T) { assertMatch(t, matches[22], "EF7-3", 3, 14) assertMatch(t, matches[23], "EF8-3", 6, 11) } - db.TruncateAllianceTeams() - db.TruncateMatches() + database.TruncateAllianceTeams() + database.TruncateMatches() } func TestEliminationScheduleErrors(t *testing.T) { - setupTest(t) + database := setupTestDb(t) - createTestAlliances(db, 1) - _, err := UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 1) + _, err := UpdateEliminationSchedule(database, time.Unix(0, 0)) if assert.NotNil(t, err) { assert.Equal(t, "Must have at least 2 alliances", err.Error()) } - db.TruncateAllianceTeams() + database.TruncateAllianceTeams() - createTestAlliances(db, 17) - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 17) + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) if assert.NotNil(t, err) { assert.Equal(t, "Round of depth 32 is not supported", err.Error()) } - db.TruncateAllianceTeams() + database.TruncateAllianceTeams() - db.CreateAllianceTeam(&model.AllianceTeam{0, 1, 0, 1}) - db.CreateAllianceTeam(&model.AllianceTeam{0, 1, 1, 2}) - db.CreateAllianceTeam(&model.AllianceTeam{0, 2, 0, 3}) - db.CreateAllianceTeam(&model.AllianceTeam{0, 2, 1, 4}) - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + database.CreateAllianceTeam(&model.AllianceTeam{0, 1, 0, 1}) + database.CreateAllianceTeam(&model.AllianceTeam{0, 1, 1, 2}) + database.CreateAllianceTeam(&model.AllianceTeam{0, 2, 0, 3}) + database.CreateAllianceTeam(&model.AllianceTeam{0, 2, 1, 4}) + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) if assert.NotNil(t, err) { assert.Equal(t, "Alliances must consist of at least 3 teams", err.Error()) } - db.TruncateAllianceTeams() + database.TruncateAllianceTeams() } func TestEliminationSchedulePopulatePartialMatch(t *testing.T) { - setupTest(t) + database := setupTestDb(t) // Final should be updated after semifinal is concluded. - createTestAlliances(db, 3) - UpdateEliminationSchedule(db, time.Unix(0, 0)) - scoreMatch(db, "SF2-1", "B") - scoreMatch(db, "SF2-2", "B") - _, err := UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 3) + UpdateEliminationSchedule(database, time.Unix(0, 0)) + scoreMatch(database, "SF2-1", "B") + scoreMatch(database, "SF2-2", "B") + _, err := UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err := db.GetMatchesByType("elimination") + matches, err := database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 5, len(matches)) { assertMatch(t, matches[2], "F-1", 1, 3) assertMatch(t, matches[3], "F-2", 1, 3) assertMatch(t, matches[4], "F-3", 1, 3) } - db.TruncateAllianceTeams() - db.TruncateMatches() - db.TruncateMatchResults() + database.TruncateAllianceTeams() + database.TruncateMatches() + database.TruncateMatchResults() // Final should be generated and populated as both semifinals conclude. - createTestAlliances(db, 4) - UpdateEliminationSchedule(db, time.Unix(0, 0)) - scoreMatch(db, "SF2-1", "R") - scoreMatch(db, "SF2-2", "R") - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 4) + UpdateEliminationSchedule(database, time.Unix(0, 0)) + scoreMatch(database, "SF2-1", "R") + scoreMatch(database, "SF2-2", "R") + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 8, len(matches)) { assertMatch(t, matches[5], "F-1", 0, 2) assertMatch(t, matches[6], "F-2", 0, 2) assertMatch(t, matches[7], "F-3", 0, 2) } - scoreMatch(db, "SF1-1", "R") - scoreMatch(db, "SF1-2", "R") - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "SF1-1", "R") + scoreMatch(database, "SF1-2", "R") + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 7, len(matches)) { assertMatch(t, matches[4], "F-1", 1, 2) assertMatch(t, matches[5], "F-2", 1, 2) assertMatch(t, matches[6], "F-3", 1, 2) } - db.TruncateAllianceTeams() - db.TruncateMatches() - db.TruncateMatchResults() + database.TruncateAllianceTeams() + database.TruncateMatches() + database.TruncateMatchResults() } func TestEliminationScheduleCreateNextRound(t *testing.T) { - setupTest(t) + database := setupTestDb(t) - createTestAlliances(db, 4) - UpdateEliminationSchedule(db, time.Unix(0, 0)) - scoreMatch(db, "SF1-1", "B") - _, err := UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 4) + UpdateEliminationSchedule(database, time.Unix(0, 0)) + scoreMatch(database, "SF1-1", "B") + _, err := UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, _ := db.GetMatchesByType("elimination") + matches, _ := database.GetMatchesByType("elimination") assert.Equal(t, 6, len(matches)) - scoreMatch(db, "SF2-1", "B") - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "SF2-1", "B") + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, _ = db.GetMatchesByType("elimination") + matches, _ = database.GetMatchesByType("elimination") assert.Equal(t, 6, len(matches)) - scoreMatch(db, "SF1-2", "B") - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "SF1-2", "B") + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, _ = db.GetMatchesByType("elimination") + matches, _ = database.GetMatchesByType("elimination") assert.Equal(t, 8, len(matches)) - scoreMatch(db, "SF2-2", "B") - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "SF2-2", "B") + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, _ = db.GetMatchesByType("elimination") + matches, _ = database.GetMatchesByType("elimination") if assert.Equal(t, 7, len(matches)) { assertMatch(t, matches[4], "F-1", 4, 3) assertMatch(t, matches[5], "F-2", 4, 3) @@ -509,122 +509,122 @@ func TestEliminationScheduleCreateNextRound(t *testing.T) { } func TestEliminationScheduleDetermineWinner(t *testing.T) { - setupTest(t) + database := setupTestDb(t) // Round with one tie and a sweep. - createTestAlliances(db, 2) - UpdateEliminationSchedule(db, time.Unix(0, 0)) - scoreMatch(db, "F-1", "T") - winner, err := UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 2) + UpdateEliminationSchedule(database, time.Unix(0, 0)) + scoreMatch(database, "F-1", "T") + winner, err := UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) assert.Empty(t, winner) - matches, _ := db.GetMatchesByType("elimination") + matches, _ := database.GetMatchesByType("elimination") assert.Equal(t, 3, len(matches)) - scoreMatch(db, "F-2", "B") - winner, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "F-2", "B") + winner, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) assert.Empty(t, winner) - matches, _ = db.GetMatchesByType("elimination") + matches, _ = database.GetMatchesByType("elimination") assert.Equal(t, 3, len(matches)) - scoreMatch(db, "F-3", "B") - winner, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "F-3", "B") + winner, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) if assert.Nil(t, err) { if assert.Equal(t, 3, len(winner)) { assert.Equal(t, 2, winner[0].TeamId) } } - matches, _ = db.GetMatchesByType("elimination") + matches, _ = database.GetMatchesByType("elimination") assert.Equal(t, 3, len(matches)) - db.TruncateAllianceTeams() - db.TruncateMatches() - db.TruncateMatchResults() + database.TruncateAllianceTeams() + database.TruncateMatches() + database.TruncateMatchResults() // Round with one tie and a split. - createTestAlliances(db, 2) - UpdateEliminationSchedule(db, time.Unix(0, 0)) - scoreMatch(db, "F-1", "R") - winner, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 2) + UpdateEliminationSchedule(database, time.Unix(0, 0)) + scoreMatch(database, "F-1", "R") + winner, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) assert.Empty(t, winner) - matches, _ = db.GetMatchesByType("elimination") + matches, _ = database.GetMatchesByType("elimination") assert.Equal(t, 3, len(matches)) - scoreMatch(db, "F-2", "T") - winner, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "F-2", "T") + winner, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) assert.Empty(t, winner) - matches, _ = db.GetMatchesByType("elimination") + matches, _ = database.GetMatchesByType("elimination") assert.Equal(t, 3, len(matches)) - scoreMatch(db, "F-3", "B") - winner, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "F-3", "B") + winner, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) assert.Empty(t, winner) - matches, _ = db.GetMatchesByType("elimination") + matches, _ = database.GetMatchesByType("elimination") assert.Equal(t, 4, len(matches)) assert.Equal(t, "F-4", matches[3].DisplayName) - scoreMatch(db, "F-4", "T") - winner, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "F-4", "T") + winner, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) assert.Empty(t, winner) - scoreMatch(db, "F-5", "R") - winner, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "F-5", "R") + winner, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) if assert.Nil(t, err) { if assert.Equal(t, 3, len(winner)) { assert.Equal(t, 1, winner[0].TeamId) } } - db.TruncateAllianceTeams() - db.TruncateMatches() - db.TruncateMatchResults() + database.TruncateAllianceTeams() + database.TruncateMatches() + database.TruncateMatchResults() // Round with two ties. - createTestAlliances(db, 2) - UpdateEliminationSchedule(db, time.Unix(0, 0)) - scoreMatch(db, "F-1", "T") - winner, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 2) + UpdateEliminationSchedule(database, time.Unix(0, 0)) + scoreMatch(database, "F-1", "T") + winner, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) assert.Empty(t, winner) - matches, _ = db.GetMatchesByType("elimination") + matches, _ = database.GetMatchesByType("elimination") assert.Equal(t, 3, len(matches)) - scoreMatch(db, "F-2", "B") - winner, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "F-2", "B") + winner, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) assert.Empty(t, winner) - matches, _ = db.GetMatchesByType("elimination") + matches, _ = database.GetMatchesByType("elimination") assert.Equal(t, 3, len(matches)) - scoreMatch(db, "F-3", "T") - winner, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "F-3", "T") + winner, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) assert.Empty(t, winner) - matches, _ = db.GetMatchesByType("elimination") + matches, _ = database.GetMatchesByType("elimination") assert.Equal(t, 5, len(matches)) assert.Equal(t, "F-4", matches[3].DisplayName) assert.Equal(t, "F-5", matches[4].DisplayName) - scoreMatch(db, "F-4", "B") - winner, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "F-4", "B") + winner, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) if assert.Nil(t, err) { if assert.Equal(t, 3, len(winner)) { assert.Equal(t, 2, winner[0].TeamId) } } - db.TruncateAllianceTeams() - db.TruncateMatches() - db.TruncateMatchResults() + database.TruncateAllianceTeams() + database.TruncateMatches() + database.TruncateMatchResults() // Round with repeated ties. - createTestAlliances(db, 2) - UpdateEliminationSchedule(db, time.Unix(0, 0)) - scoreMatch(db, "F-1", "T") - scoreMatch(db, "F-2", "T") - scoreMatch(db, "F-3", "T") - winner, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) - scoreMatch(db, "F-4", "T") - scoreMatch(db, "F-5", "T") - scoreMatch(db, "F-6", "T") - winner, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) - scoreMatch(db, "F-7", "R") - scoreMatch(db, "F-8", "B") - scoreMatch(db, "F-9", "R") - winner, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 2) + UpdateEliminationSchedule(database, time.Unix(0, 0)) + scoreMatch(database, "F-1", "T") + scoreMatch(database, "F-2", "T") + scoreMatch(database, "F-3", "T") + winner, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) + scoreMatch(database, "F-4", "T") + scoreMatch(database, "F-5", "T") + scoreMatch(database, "F-6", "T") + winner, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) + scoreMatch(database, "F-7", "R") + scoreMatch(database, "F-8", "B") + scoreMatch(database, "F-9", "R") + winner, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) if assert.Nil(t, err) { if assert.Equal(t, 3, len(winner)) { assert.Equal(t, 1, winner[0].TeamId) @@ -633,42 +633,42 @@ func TestEliminationScheduleDetermineWinner(t *testing.T) { } func TestEliminationScheduleRemoveUnneededMatches(t *testing.T) { - setupTest(t) + database := setupTestDb(t) - createTestAlliances(db, 2) - UpdateEliminationSchedule(db, time.Unix(0, 0)) - scoreMatch(db, "F-1", "R") - scoreMatch(db, "F-2", "R") - _, err := UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 2) + UpdateEliminationSchedule(database, time.Unix(0, 0)) + scoreMatch(database, "F-1", "R") + scoreMatch(database, "F-2", "R") + _, err := UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, _ := db.GetMatchesByType("elimination") + matches, _ := database.GetMatchesByType("elimination") assert.Equal(t, 2, len(matches)) // Check that the deleted match is recreated if the score is changed. - scoreMatch(db, "F-2", "B") - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "F-2", "B") + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, _ = db.GetMatchesByType("elimination") + matches, _ = database.GetMatchesByType("elimination") if assert.Equal(t, 3, len(matches)) { assert.Equal(t, "F-3", matches[2].DisplayName) } } func TestEliminationScheduleChangePreviousRoundResult(t *testing.T) { - setupTest(t) + database := setupTestDb(t) - createTestAlliances(db, 4) - _, err := UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 4) + _, err := UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - scoreMatch(db, "SF2-1", "R") - scoreMatch(db, "SF2-2", "B") - scoreMatch(db, "SF2-3", "R") - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "SF2-1", "R") + scoreMatch(database, "SF2-2", "B") + scoreMatch(database, "SF2-3", "R") + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - scoreMatch(db, "SF2-3", "B") - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "SF2-3", "B") + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err := db.GetMatchesByType("elimination") + matches, err := database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 9, len(matches)) { assertMatch(t, matches[6], "F-1", 0, 3) @@ -676,17 +676,17 @@ func TestEliminationScheduleChangePreviousRoundResult(t *testing.T) { assertMatch(t, matches[8], "F-3", 0, 3) } - scoreMatch(db, "SF1-1", "R") - scoreMatch(db, "SF1-2", "R") - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "SF1-1", "R") + scoreMatch(database, "SF1-2", "R") + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - scoreMatch(db, "SF1-2", "B") - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "SF1-2", "B") + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - scoreMatch(db, "SF1-3", "B") - _, err = UpdateEliminationSchedule(db, time.Unix(0, 0)) + scoreMatch(database, "SF1-3", "B") + _, err = UpdateEliminationSchedule(database, time.Unix(0, 0)) assert.Nil(t, err) - matches, err = db.GetMatchesByType("elimination") + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 9, len(matches)) { assertMatch(t, matches[6], "F-1", 4, 3) @@ -696,23 +696,23 @@ func TestEliminationScheduleChangePreviousRoundResult(t *testing.T) { } func TestEliminationScheduleUnscoredMatch(t *testing.T) { - setupTest(t) + database := setupTestDb(t) - createTestAlliances(db, 2) - UpdateEliminationSchedule(db, time.Unix(0, 0)) - scoreMatch(db, "F-1", "blorpy") - _, err := UpdateEliminationSchedule(db, time.Unix(0, 0)) + CreateTestAlliances(database, 2) + UpdateEliminationSchedule(database, time.Unix(0, 0)) + scoreMatch(database, "F-1", "blorpy") + _, err := UpdateEliminationSchedule(database, time.Unix(0, 0)) if assert.NotNil(t, err) { assert.Equal(t, "Completed match 1 has invalid winner 'blorpy'", err.Error()) } } func TestEliminationScheduleTiming(t *testing.T) { - setupTest(t) + database := setupTestDb(t) - createTestAlliances(db, 4) - UpdateEliminationSchedule(db, time.Unix(1000, 0)) - matches, err := db.GetMatchesByType("elimination") + CreateTestAlliances(database, 4) + UpdateEliminationSchedule(database, time.Unix(1000, 0)) + matches, err := database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 6, len(matches)) { assert.True(t, time.Unix(1000, 0).Equal(matches[0].Time)) @@ -722,10 +722,10 @@ func TestEliminationScheduleTiming(t *testing.T) { assert.True(t, time.Unix(3400, 0).Equal(matches[4].Time)) assert.True(t, time.Unix(4000, 0).Equal(matches[5].Time)) } - scoreMatch(db, "SF1-1", "R") - scoreMatch(db, "SF1-3", "B") - UpdateEliminationSchedule(db, time.Unix(5000, 0)) - matches, err = db.GetMatchesByType("elimination") + scoreMatch(database, "SF1-1", "R") + scoreMatch(database, "SF1-3", "B") + UpdateEliminationSchedule(database, time.Unix(5000, 0)) + matches, err = database.GetMatchesByType("elimination") assert.Nil(t, err) if assert.Equal(t, 6, len(matches)) { assert.True(t, time.Unix(1000, 0).Equal(matches[0].Time)) @@ -737,23 +737,15 @@ func TestEliminationScheduleTiming(t *testing.T) { } } -func createTestAlliances(db *model.Database, allianceCount int) { - for i := 1; i <= allianceCount; i++ { - db.CreateAllianceTeam(&model.AllianceTeam{0, i, 0, i}) - db.CreateAllianceTeam(&model.AllianceTeam{0, i, 1, i}) - db.CreateAllianceTeam(&model.AllianceTeam{0, i, 2, i}) - } -} - func assertMatch(t *testing.T, match model.Match, displayName string, redAlliance int, blueAlliance int) { assert.Equal(t, displayName, match.DisplayName) assert.Equal(t, redAlliance, match.Red1) assert.Equal(t, blueAlliance, match.Blue1) } -func scoreMatch(db *model.Database, displayName string, winner string) { - match, _ := db.GetMatchByName("elimination", displayName) +func scoreMatch(database *model.Database, displayName string, winner string) { + match, _ := database.GetMatchByName("elimination", displayName) match.Status = "complete" match.Winner = winner - db.SaveMatch(match) + database.SaveMatch(match) } diff --git a/tournament/qualification_rankings.go b/tournament/qualification_rankings.go new file mode 100644 index 0000000..918b5a9 --- /dev/null +++ b/tournament/qualification_rankings.go @@ -0,0 +1,147 @@ +// Copyright 2017 Team 254. All Rights Reserved. +// Author: pat@patfairbank.com (Patrick Fairbank) +// +// Functions for calculating the qualification rankings. + +package tournament + +import ( + "github.com/Team254/cheesy-arena/game" + "github.com/Team254/cheesy-arena/model" + "sort" + "strconv" +) + +// Determines the rankings from the stored match results, and saves them to the database. +func CalculateRankings(database *model.Database) error { + matches, err := database.GetMatchesByType("qualification") + if err != nil { + return err + } + rankings := make(map[int]*game.Ranking) + for _, match := range matches { + if match.Status != "complete" { + continue + } + matchResult, err := database.GetMatchResultForMatch(match.Id) + if err != nil { + return err + } + if !match.Red1IsSurrogate { + addMatchResultToRankings(rankings, match.Red1, matchResult, true) + } + if !match.Red2IsSurrogate { + addMatchResultToRankings(rankings, match.Red2, matchResult, true) + } + if !match.Red3IsSurrogate { + addMatchResultToRankings(rankings, match.Red3, matchResult, true) + } + if !match.Blue1IsSurrogate { + addMatchResultToRankings(rankings, match.Blue1, matchResult, false) + } + if !match.Blue2IsSurrogate { + addMatchResultToRankings(rankings, match.Blue2, matchResult, false) + } + if !match.Blue3IsSurrogate { + addMatchResultToRankings(rankings, match.Blue3, matchResult, false) + } + } + + sortedRankings := sortRankings(rankings) + for rank, ranking := range sortedRankings { + ranking.Rank = rank + 1 + } + err = database.ReplaceAllRankings(sortedRankings) + if err != nil { + return nil + } + + return nil +} + +// Checks all the match results for yellow and red cards, and updates the team model accordingly. +func CalculateTeamCards(database *model.Database, matchType string) error { + teams, err := database.GetAllTeams() + if err != nil { + return err + } + teamsMap := make(map[string]model.Team) + for _, team := range teams { + team.YellowCard = false + teamsMap[strconv.Itoa(team.Id)] = team + } + + matches, err := database.GetMatchesByType(matchType) + if err != nil { + return err + } + for _, match := range matches { + if match.Status != "complete" { + continue + } + matchResult, err := database.GetMatchResultForMatch(match.Id) + if err != nil { + return err + } + + // Mark the team as having a yellow card if they got either a yellow or red in a previous match. + for teamId, card := range matchResult.RedCards { + if team, ok := teamsMap[teamId]; ok && card != "" { + team.YellowCard = true + teamsMap[teamId] = team + } + } + for teamId, card := range matchResult.BlueCards { + if team, ok := teamsMap[teamId]; ok && card != "" { + team.YellowCard = true + teamsMap[teamId] = team + } + } + } + + // Save the teams to the database. + for _, team := range teamsMap { + err = database.SaveTeam(&team) + if err != nil { + return err + } + } + + return nil +} + +// Incrementally accounts for the given match result in the set of rankings that are being built. +func addMatchResultToRankings(rankings map[int]*game.Ranking, teamId int, matchResult *model.MatchResult, isRed bool) { + ranking := rankings[teamId] + if ranking == nil { + ranking = &game.Ranking{TeamId: teamId} + rankings[teamId] = ranking + } + + // Determine whether the team was disqualified. + var cards map[string]string + if isRed { + cards = matchResult.RedCards + } else { + cards = matchResult.BlueCards + } + disqualified := false + if card, ok := cards[strconv.Itoa(teamId)]; ok && card == "red" { + disqualified = true + } + + if isRed { + ranking.AddScoreSummary(matchResult.RedScoreSummary(), matchResult.BlueScoreSummary(), disqualified) + } else { + ranking.AddScoreSummary(matchResult.BlueScoreSummary(), matchResult.RedScoreSummary(), disqualified) + } +} + +func sortRankings(rankings map[int]*game.Ranking) game.Rankings { + var sortedRankings game.Rankings + for _, ranking := range rankings { + sortedRankings = append(sortedRankings, ranking) + } + sort.Sort(sortedRankings) + return sortedRankings +} diff --git a/tournament/qualification_rankings_test.go b/tournament/qualification_rankings_test.go new file mode 100644 index 0000000..d59de27 --- /dev/null +++ b/tournament/qualification_rankings_test.go @@ -0,0 +1,91 @@ +// Copyright 2017 Team 254. All Rights Reserved. +// Author: pat@patfairbank.com (Patrick Fairbank) + +package tournament + +import ( + "github.com/Team254/cheesy-arena/model" + "github.com/stretchr/testify/assert" + "testing" +) + +func TestCalculateRankings(t *testing.T) { + database := setupTestDb(t) + + setupMatchResultsForRankings(database) + err := CalculateRankings(database) + assert.Nil(t, err) + rankings, err := database.GetAllRankings() + assert.Nil(t, err) + if assert.Equal(t, 6, len(rankings)) { + assert.Equal(t, 4, rankings[0].TeamId) + assert.Equal(t, 5, rankings[1].TeamId) + assert.Equal(t, 6, rankings[2].TeamId) + assert.Equal(t, 1, rankings[3].TeamId) + assert.Equal(t, 3, rankings[4].TeamId) + assert.Equal(t, 2, rankings[5].TeamId) + } + + // Test after changing a match result. + matchResult3 := model.BuildTestMatchResult(3, 3) + matchResult3.RedScore, matchResult3.BlueScore = matchResult3.BlueScore, matchResult3.RedScore + err = database.CreateMatchResult(matchResult3) + assert.Nil(t, err) + err = CalculateRankings(database) + assert.Nil(t, err) + rankings, err = database.GetAllRankings() + assert.Nil(t, err) + if assert.Equal(t, 6, len(rankings)) { + assert.Equal(t, 6, rankings[0].TeamId) + assert.Equal(t, 5, rankings[1].TeamId) + assert.Equal(t, 4, rankings[2].TeamId) + assert.Equal(t, 1, rankings[3].TeamId) + assert.Equal(t, 3, rankings[4].TeamId) + assert.Equal(t, 2, rankings[5].TeamId) + } +} + +// Sets up a schedule and results that touches on all possible variables. +func setupMatchResultsForRankings(database *model.Database) { + match1 := model.Match{Type: "qualification", DisplayName: "1", Red1: 1, Red2: 2, Red3: 3, Blue1: 4, Blue2: 5, + Blue3: 6, Status: "complete"} + database.CreateMatch(&match1) + matchResult1 := model.BuildTestMatchResult(match1.Id, 1) + matchResult1.RedCards = map[string]string{"2": "red"} + database.CreateMatchResult(matchResult1) + + match2 := model.Match{Type: "qualification", DisplayName: "2", Red1: 1, Red2: 3, Red3: 5, Blue1: 2, Blue2: 4, + Blue3: 6, Status: "complete", Red2IsSurrogate: true, Blue3IsSurrogate: true} + database.CreateMatch(&match2) + matchResult2 := model.BuildTestMatchResult(match2.Id, 1) + matchResult2.BlueScore = matchResult2.RedScore + database.CreateMatchResult(matchResult2) + + match3 := model.Match{Type: "qualification", DisplayName: "3", Red1: 6, Red2: 5, Red3: 4, Blue1: 3, Blue2: 2, + Blue3: 1, Status: "complete", Red3IsSurrogate: true} + database.CreateMatch(&match3) + matchResult3 := model.BuildTestMatchResult(match3.Id, 1) + database.CreateMatchResult(matchResult3) + matchResult3 = model.NewMatchResult() + matchResult3.MatchId = match3.Id + matchResult3.PlayNumber = 2 + database.CreateMatchResult(matchResult3) + + match4 := model.Match{Type: "practice", DisplayName: "1", Red1: 1, Red2: 2, Red3: 3, Blue1: 4, Blue2: 5, + Blue3: 6, Status: "complete"} + database.CreateMatch(&match4) + matchResult4 := model.BuildTestMatchResult(match4.Id, 1) + database.CreateMatchResult(matchResult4) + + match5 := model.Match{Type: "elimination", DisplayName: "F-1", Red1: 1, Red2: 2, Red3: 3, Blue1: 4, Blue2: 5, + Blue3: 6, Status: "complete"} + database.CreateMatch(&match5) + matchResult5 := model.BuildTestMatchResult(match5.Id, 1) + database.CreateMatchResult(matchResult5) + + match6 := model.Match{Type: "qualification", DisplayName: "4", Red1: 7, Red2: 8, Red3: 9, Blue1: 10, Blue2: 11, + Blue3: 12, Status: ""} + database.CreateMatch(&match6) + matchResult6 := model.BuildTestMatchResult(match6.Id, 1) + database.CreateMatchResult(matchResult6) +} diff --git a/schedule.go b/tournament/schedule.go similarity index 95% rename from schedule.go rename to tournament/schedule.go index 52e54ce..4721b45 100644 --- a/schedule.go +++ b/tournament/schedule.go @@ -3,7 +3,7 @@ // // Functions for creating practice and qualification match schedules. -package main +package tournament import ( "encoding/csv" @@ -16,8 +16,9 @@ import ( "time" ) -const schedulesDir = "schedules" -const teamsPerMatch = 6 +var schedulesDir = "schedules" + +const TeamsPerMatch = 6 type ScheduleBlock struct { StartTime time.Time @@ -30,10 +31,10 @@ func BuildRandomSchedule(teams []model.Team, scheduleBlocks []ScheduleBlock, mat // Load the anonymized, pre-randomized match schedule for the given number of teams and matches per team. numTeams := len(teams) numMatches := countMatches(scheduleBlocks) - matchesPerTeam := int(float32(numMatches*teamsPerMatch) / float32(numTeams)) + matchesPerTeam := int(float32(numMatches*TeamsPerMatch) / float32(numTeams)) // Adjust the number of matches to remove any excess from non-perfect block scheduling. - numMatches = int(math.Ceil(float64(numTeams) * float64(matchesPerTeam) / teamsPerMatch)) + numMatches = int(math.Ceil(float64(numTeams) * float64(matchesPerTeam) / TeamsPerMatch)) file, err := os.Open(fmt.Sprintf("%s/%d_%d.csv", schedulesDir, numTeams, matchesPerTeam)) if err != nil { diff --git a/schedule_test.go b/tournament/schedule_test.go similarity index 93% rename from schedule_test.go rename to tournament/schedule_test.go index 4f45004..427b255 100644 --- a/schedule_test.go +++ b/tournament/schedule_test.go @@ -1,9 +1,10 @@ // Copyright 2014 Team 254. All Rights Reserved. // Author: pat@patfairbank.com (Patrick Fairbank) -package main +package tournament import ( + "fmt" "github.com/Team254/cheesy-arena/model" "github.com/stretchr/testify/assert" "math/rand" @@ -12,6 +13,11 @@ import ( "time" ) +func TestMain(m *testing.M) { + schedulesDir = "../schedules" + os.Exit(m.Run()) +} + func TestNonExistentSchedule(t *testing.T) { teams := make([]model.Team, 6) scheduleBlocks := []ScheduleBlock{{time.Unix(0, 0).UTC(), 2, 60}} @@ -23,8 +29,9 @@ func TestNonExistentSchedule(t *testing.T) { } func TestMalformedSchedule(t *testing.T) { - scheduleFile, _ := os.Create("schedules/6_1.csv") - defer os.Remove("schedules/6_1.csv") + filename := fmt.Sprintf("%s/6_1.csv", schedulesDir) + scheduleFile, _ := os.Create(filename) + defer os.Remove(filename) scheduleFile.WriteString("1,0,2,0,3,0,4,0,5,0,6,0\n6,0,5,0,4,0,3,0,2,0,1,0\n") scheduleFile.Close() teams := make([]model.Team, 6) @@ -35,8 +42,8 @@ func TestMalformedSchedule(t *testing.T) { assert.Equal(t, expectedErr, err.Error()) } - os.Remove("schedules/6_1.csv") - scheduleFile, _ = os.Create("schedules/6_1.csv") + os.Remove(filename) + scheduleFile, _ = os.Create(filename) scheduleFile.WriteString("1,0,asdf,0,3,0,4,0,5,0,6,0\n") scheduleFile.Close() _, err = BuildRandomSchedule(teams, scheduleBlocks, "test") diff --git a/tournament/test_helpers.go b/tournament/test_helpers.go new file mode 100644 index 0000000..157f6e5 --- /dev/null +++ b/tournament/test_helpers.go @@ -0,0 +1,23 @@ +// Copyright 2017 Team 254. All Rights Reserved. +// Author: pat@patfairbank.com (Patrick Fairbank) +// +// Helper methods for use in tests in this package and others. + +package tournament + +import ( + "github.com/Team254/cheesy-arena/model" + "testing" +) + +func CreateTestAlliances(database *model.Database, allianceCount int) { + for i := 1; i <= allianceCount; i++ { + database.CreateAllianceTeam(&model.AllianceTeam{0, i, 0, i}) + database.CreateAllianceTeam(&model.AllianceTeam{0, i, 1, i}) + database.CreateAllianceTeam(&model.AllianceTeam{0, i, 2, i}) + } +} + +func setupTestDb(t *testing.T) *model.Database { + return model.SetupTestDb(t, "tournament", "..") +}