Refactor tournament progression methods into separate package.

This commit is contained in:
Patrick Fairbank
2017-08-27 18:20:44 -07:00
parent 53d34ae82a
commit f0fe7df2b3
16 changed files with 550 additions and 488 deletions

View File

@@ -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.

View File

@@ -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)
}