mirror of
https://github.com/Team254/cheesy-arena-lite.git
synced 2026-03-09 13:46:44 -04:00
Update game-specific models for 2018.
This commit is contained in:
168
field/arena.go
168
field/arena.go
@@ -70,6 +70,11 @@ type Arena struct {
|
|||||||
AllianceSelectionNotifier *Notifier
|
AllianceSelectionNotifier *Notifier
|
||||||
LowerThirdNotifier *Notifier
|
LowerThirdNotifier *Notifier
|
||||||
ReloadDisplaysNotifier *Notifier
|
ReloadDisplaysNotifier *Notifier
|
||||||
|
scale *game.Seesaw
|
||||||
|
redSwitch *game.Seesaw
|
||||||
|
blueSwitch *game.Seesaw
|
||||||
|
redVault *game.Vault
|
||||||
|
blueVault *game.Vault
|
||||||
}
|
}
|
||||||
|
|
||||||
type ArenaStatus struct {
|
type ArenaStatus struct {
|
||||||
@@ -202,6 +207,12 @@ func (arena *Arena) LoadMatch(match *model.Match) error {
|
|||||||
arena.BlueRealtimeScore = NewRealtimeScore()
|
arena.BlueRealtimeScore = NewRealtimeScore()
|
||||||
arena.Plc.ResetCounts()
|
arena.Plc.ResetCounts()
|
||||||
arena.FieldReset = false
|
arena.FieldReset = false
|
||||||
|
arena.scale = new(game.Seesaw)
|
||||||
|
arena.redSwitch = new(game.Seesaw)
|
||||||
|
arena.blueSwitch = new(game.Seesaw)
|
||||||
|
arena.redVault = new(game.Vault)
|
||||||
|
arena.blueVault = new(game.Vault)
|
||||||
|
game.ResetPowerUps()
|
||||||
|
|
||||||
// Notify any listeners about the new match.
|
// Notify any listeners about the new match.
|
||||||
arena.MatchLoadTeamsNotifier.Notify(nil)
|
arena.MatchLoadTeamsNotifier.Notify(nil)
|
||||||
@@ -460,14 +471,12 @@ func (arena *Arena) Run() {
|
|||||||
|
|
||||||
// Calculates the red alliance score summary for the given realtime snapshot.
|
// Calculates the red alliance score summary for the given realtime snapshot.
|
||||||
func (arena *Arena) RedScoreSummary() *game.ScoreSummary {
|
func (arena *Arena) RedScoreSummary() *game.ScoreSummary {
|
||||||
return arena.RedRealtimeScore.CurrentScore.Summarize(arena.BlueRealtimeScore.CurrentScore.Fouls,
|
return arena.RedRealtimeScore.CurrentScore.Summarize(arena.BlueRealtimeScore.CurrentScore.Fouls)
|
||||||
arena.CurrentMatch.Type)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculates the blue alliance score summary for the given realtime snapshot.
|
// Calculates the blue alliance score summary for the given realtime snapshot.
|
||||||
func (arena *Arena) BlueScoreSummary() *game.ScoreSummary {
|
func (arena *Arena) BlueScoreSummary() *game.ScoreSummary {
|
||||||
return arena.BlueRealtimeScore.CurrentScore.Summarize(arena.RedRealtimeScore.CurrentScore.Fouls,
|
return arena.BlueRealtimeScore.CurrentScore.Summarize(arena.RedRealtimeScore.CurrentScore.Fouls)
|
||||||
arena.CurrentMatch.Type)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (arena *Arena) GetStatus() *ArenaStatus {
|
func (arena *Arena) GetStatus() *ArenaStatus {
|
||||||
@@ -604,54 +613,48 @@ func (arena *Arena) handlePlcInput() {
|
|||||||
arena.handleEstop("B2", blueEstops[1])
|
arena.handleEstop("B2", blueEstops[1])
|
||||||
arena.handleEstop("B3", blueEstops[2])
|
arena.handleEstop("B3", blueEstops[2])
|
||||||
|
|
||||||
matchStartTime := arena.MatchStartTime
|
if arena.MatchState == PreMatch || arena.MatchState == PostMatch {
|
||||||
currentTime := time.Now()
|
// Don't do anything if we're outside the match, otherwise we may overwrite manual edits.
|
||||||
if arena.MatchState == PreMatch {
|
|
||||||
// Set a match start time in the future.
|
|
||||||
matchStartTime = currentTime.Add(time.Second)
|
|
||||||
}
|
|
||||||
matchEndTime := game.GetMatchEndTime(matchStartTime)
|
|
||||||
inGracePeriod := currentTime.Before(matchEndTime.Add(game.BoilerTeleopGracePeriodSec * time.Second))
|
|
||||||
if arena.MatchState == PostMatch && (!inGracePeriod || arena.matchAborted) {
|
|
||||||
// Don't do anything if we're past the end of the match, otherwise we may overwrite manual edits.
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
matchStartTime := arena.MatchStartTime
|
||||||
|
currentTime := time.Now()
|
||||||
|
teleopStartTime := game.GetTeleopStartTime(matchStartTime)
|
||||||
|
|
||||||
redScore := &arena.RedRealtimeScore.CurrentScore
|
redScore := &arena.RedRealtimeScore.CurrentScore
|
||||||
oldRedScore := *redScore
|
oldRedScore := *redScore
|
||||||
blueScore := &arena.BlueRealtimeScore.CurrentScore
|
blueScore := &arena.BlueRealtimeScore.CurrentScore
|
||||||
oldBlueScore := *blueScore
|
oldBlueScore := *blueScore
|
||||||
|
|
||||||
// Handle balls.
|
// Handle scale and switch ownership.
|
||||||
redLow, redHigh, blueLow, blueHigh := arena.Plc.GetBalls()
|
scale, redSwitch, blueSwitch := arena.Plc.GetScaleAndSwitches()
|
||||||
arena.RedRealtimeScore.boiler.UpdateState(redLow, redHigh, matchStartTime, currentTime)
|
arena.scale.UpdateState(scale, currentTime)
|
||||||
redScore.AutoFuelLow = arena.RedRealtimeScore.boiler.AutoFuelLow
|
arena.redSwitch.UpdateState(redSwitch, currentTime)
|
||||||
redScore.AutoFuelHigh = arena.RedRealtimeScore.boiler.AutoFuelHigh
|
arena.blueSwitch.UpdateState(blueSwitch, currentTime)
|
||||||
redScore.FuelLow = arena.RedRealtimeScore.boiler.FuelLow
|
if arena.MatchState == AutoPeriod {
|
||||||
redScore.FuelHigh = arena.RedRealtimeScore.boiler.FuelHigh
|
redScore.AutoOwnershipPoints = 2 * int(arena.redSwitch.GetRedSeconds(matchStartTime, currentTime)+
|
||||||
arena.BlueRealtimeScore.boiler.UpdateState(blueLow, blueHigh, matchStartTime, currentTime)
|
arena.scale.GetRedSeconds(matchStartTime, currentTime))
|
||||||
blueScore.AutoFuelLow = arena.BlueRealtimeScore.boiler.AutoFuelLow
|
blueScore.AutoOwnershipPoints = 2 * int(arena.blueSwitch.GetBlueSeconds(matchStartTime, currentTime)+
|
||||||
blueScore.AutoFuelHigh = arena.BlueRealtimeScore.boiler.AutoFuelHigh
|
arena.scale.GetBlueSeconds(matchStartTime, currentTime))
|
||||||
blueScore.FuelLow = arena.BlueRealtimeScore.boiler.FuelLow
|
} else {
|
||||||
blueScore.FuelHigh = arena.BlueRealtimeScore.boiler.FuelHigh
|
redScore.TeleopOwnershipPoints = int(arena.redSwitch.GetRedSeconds(teleopStartTime, currentTime) +
|
||||||
|
arena.scale.GetRedSeconds(teleopStartTime, currentTime))
|
||||||
// Handle rotors.
|
blueScore.TeleopOwnershipPoints = int(arena.blueSwitch.GetBlueSeconds(teleopStartTime, currentTime) +
|
||||||
redRotor1, redOtherRotors, blueRotor1, blueOtherRotors := arena.Plc.GetRotors()
|
arena.scale.GetBlueSeconds(teleopStartTime, currentTime))
|
||||||
arena.RedRealtimeScore.rotorSet.UpdateState(redRotor1, redOtherRotors, matchStartTime, currentTime)
|
|
||||||
redScore.AutoRotors = arena.RedRealtimeScore.rotorSet.AutoRotors
|
|
||||||
redScore.Rotors = arena.RedRealtimeScore.rotorSet.Rotors
|
|
||||||
arena.BlueRealtimeScore.rotorSet.UpdateState(blueRotor1, blueOtherRotors, matchStartTime, currentTime)
|
|
||||||
blueScore.AutoRotors = arena.BlueRealtimeScore.rotorSet.AutoRotors
|
|
||||||
blueScore.Rotors = arena.BlueRealtimeScore.rotorSet.Rotors
|
|
||||||
|
|
||||||
// Handle touchpads.
|
|
||||||
redTouchpads, blueTouchpads := arena.Plc.GetTouchpads()
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
arena.RedRealtimeScore.touchpads[i].UpdateState(redTouchpads[i], matchStartTime, currentTime)
|
|
||||||
arena.BlueRealtimeScore.touchpads[i].UpdateState(blueTouchpads[i], matchStartTime, currentTime)
|
|
||||||
}
|
}
|
||||||
redScore.Takeoffs = game.CountTouchpads(&arena.RedRealtimeScore.touchpads, currentTime)
|
|
||||||
blueScore.Takeoffs = game.CountTouchpads(&arena.BlueRealtimeScore.touchpads, currentTime)
|
// Handle vaults.
|
||||||
|
redForceCubes, redLevitateCubes, redBoostCubes, blueForceCubes, blueLevitateCubes, blueBoostCubes :=
|
||||||
|
arena.Plc.GetVaults()
|
||||||
|
arena.redVault.UpdateCubes(redForceCubes, redLevitateCubes, redBoostCubes)
|
||||||
|
arena.blueVault.UpdateCubes(blueForceCubes, blueLevitateCubes, blueBoostCubes)
|
||||||
|
redForce, redLevitate, redBoost, blueForce, blueLevitate, blueBoost := arena.Plc.GetPowerUpButtons()
|
||||||
|
arena.redVault.UpdateButtons(redForce, redLevitate, redBoost, currentTime)
|
||||||
|
arena.blueVault.UpdateButtons(blueForce, blueLevitate, blueBoost, currentTime)
|
||||||
|
redScore.VaultCubes = arena.redVault.GetNumCubes()
|
||||||
|
redScore.Levitate = arena.redVault.LevitatePlayed
|
||||||
|
blueScore.VaultCubes = arena.blueVault.GetNumCubes()
|
||||||
|
blueScore.Levitate = arena.blueVault.LevitatePlayed
|
||||||
|
|
||||||
if !oldRedScore.Equals(redScore) || !oldBlueScore.Equals(blueScore) {
|
if !oldRedScore.Equals(redScore) || !oldBlueScore.Equals(blueScore) {
|
||||||
arena.RealtimeScoreNotifier.Notify(nil)
|
arena.RealtimeScoreNotifier.Notify(nil)
|
||||||
@@ -662,65 +665,28 @@ func (arena *Arena) handlePlcInput() {
|
|||||||
func (arena *Arena) handlePlcOutput() {
|
func (arena *Arena) handlePlcOutput() {
|
||||||
if arena.FieldTestMode != "" {
|
if arena.FieldTestMode != "" {
|
||||||
// PLC output is being manually overridden.
|
// PLC output is being manually overridden.
|
||||||
if arena.FieldTestMode == "flash" {
|
// TODO(patrick): Update for 2018.
|
||||||
blinkState := arena.Plc.GetCycleState(2, 0, 1)
|
/*
|
||||||
arena.Plc.SetTouchpadLights([3]bool{blinkState, blinkState, blinkState},
|
if arena.FieldTestMode == "flash" {
|
||||||
[3]bool{blinkState, blinkState, blinkState})
|
blinkState := arena.Plc.GetCycleState(2, 0, 1)
|
||||||
} else if arena.FieldTestMode == "cycle" {
|
arena.Plc.SetTouchpadLights([3]bool{blinkState, blinkState, blinkState},
|
||||||
arena.Plc.SetTouchpadLights(
|
[3]bool{blinkState, blinkState, blinkState})
|
||||||
[3]bool{arena.Plc.GetCycleState(3, 2, 1), arena.Plc.GetCycleState(3, 1, 1), arena.Plc.GetCycleState(3, 0, 1)},
|
} else if arena.FieldTestMode == "cycle" {
|
||||||
[3]bool{arena.Plc.GetCycleState(3, 0, 1), arena.Plc.GetCycleState(3, 1, 1), arena.Plc.GetCycleState(3, 2, 1)})
|
arena.Plc.SetTouchpadLights(
|
||||||
} else if arena.FieldTestMode == "chase" {
|
[3]bool{arena.Plc.GetCycleState(3, 2, 1), arena.Plc.GetCycleState(3, 1, 1), arena.Plc.GetCycleState(3, 0, 1)},
|
||||||
arena.Plc.SetTouchpadLights(
|
[3]bool{arena.Plc.GetCycleState(3, 0, 1), arena.Plc.GetCycleState(3, 1, 1), arena.Plc.GetCycleState(3, 2, 1)})
|
||||||
[3]bool{arena.Plc.GetCycleState(12, 2, 2), arena.Plc.GetCycleState(12, 1, 2), arena.Plc.GetCycleState(12, 0, 2)},
|
} else if arena.FieldTestMode == "chase" {
|
||||||
[3]bool{arena.Plc.GetCycleState(12, 3, 2), arena.Plc.GetCycleState(12, 4, 2), arena.Plc.GetCycleState(12, 5, 2)})
|
arena.Plc.SetTouchpadLights(
|
||||||
} else if arena.FieldTestMode == "slowChase" {
|
[3]bool{arena.Plc.GetCycleState(12, 2, 2), arena.Plc.GetCycleState(12, 1, 2), arena.Plc.GetCycleState(12, 0, 2)},
|
||||||
arena.Plc.SetTouchpadLights(
|
[3]bool{arena.Plc.GetCycleState(12, 3, 2), arena.Plc.GetCycleState(12, 4, 2), arena.Plc.GetCycleState(12, 5, 2)})
|
||||||
[3]bool{arena.Plc.GetCycleState(6, 2, 8), arena.Plc.GetCycleState(6, 1, 8), arena.Plc.GetCycleState(6, 0, 8)},
|
} else if arena.FieldTestMode == "slowChase" {
|
||||||
[3]bool{arena.Plc.GetCycleState(6, 3, 8), arena.Plc.GetCycleState(6, 4, 8), arena.Plc.GetCycleState(6, 5, 8)})
|
arena.Plc.SetTouchpadLights(
|
||||||
}
|
[3]bool{arena.Plc.GetCycleState(6, 2, 8), arena.Plc.GetCycleState(6, 1, 8), arena.Plc.GetCycleState(6, 0, 8)},
|
||||||
return
|
[3]bool{arena.Plc.GetCycleState(6, 3, 8), arena.Plc.GetCycleState(6, 4, 8), arena.Plc.GetCycleState(6, 5, 8)})
|
||||||
|
}
|
||||||
|
return
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle balls.
|
|
||||||
matchEndTime := game.GetMatchEndTime(arena.MatchStartTime)
|
|
||||||
inGracePeriod := time.Now().Before(matchEndTime.Add(game.BoilerTeleopGracePeriodSec * time.Second))
|
|
||||||
if arena.MatchTimeSec() > 0 || arena.MatchState == PostMatch && !arena.matchAborted && inGracePeriod {
|
|
||||||
arena.Plc.SetBoilerMotors(true)
|
|
||||||
} else {
|
|
||||||
arena.Plc.SetBoilerMotors(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle rotors.
|
|
||||||
redScore := &arena.RedRealtimeScore.CurrentScore
|
|
||||||
blueScore := &arena.BlueRealtimeScore.CurrentScore
|
|
||||||
if arena.MatchTimeSec() > 0 {
|
|
||||||
arena.Plc.SetRotorMotors(redScore.AutoRotors+redScore.Rotors, blueScore.AutoRotors+blueScore.Rotors)
|
|
||||||
} else {
|
|
||||||
arena.Plc.SetRotorMotors(0, 0)
|
|
||||||
}
|
|
||||||
arena.Plc.SetRotorLights(redScore.AutoRotors, blueScore.AutoRotors)
|
|
||||||
|
|
||||||
// Handle touchpads.
|
|
||||||
var redTouchpads, blueTouchpads [3]bool
|
|
||||||
currentTime := time.Now()
|
|
||||||
blinkStopTime := matchEndTime.Add(-time.Duration(game.MatchTiming.EndgameTimeLeftSec-2) * time.Second)
|
|
||||||
blinkState := arena.Plc.GetCycleState(2, 0, 1)
|
|
||||||
if arena.MatchState == EndgamePeriod && currentTime.Before(blinkStopTime) {
|
|
||||||
// Blink the touchpads at the endgame start point.
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
redTouchpads[i] = blinkState
|
|
||||||
blueTouchpads[i] = blinkState
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for i := 0; i < 3; i++ {
|
|
||||||
redState := arena.RedRealtimeScore.touchpads[i].GetState(currentTime)
|
|
||||||
redTouchpads[i] = redState == game.Held || redState == game.Triggered && blinkState
|
|
||||||
blueState := arena.BlueRealtimeScore.touchpads[i].GetState(currentTime)
|
|
||||||
blueTouchpads[i] = blueState == game.Held || blueState == game.Triggered && blinkState
|
|
||||||
}
|
|
||||||
}
|
|
||||||
arena.Plc.SetTouchpadLights(redTouchpads, blueTouchpads)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (arena *Arena) handleEstop(station string, state bool) {
|
func (arena *Arena) handleEstop(station string, state bool) {
|
||||||
|
|||||||
198
field/plc.go
198
field/plc.go
@@ -17,9 +17,9 @@ type Plc struct {
|
|||||||
address string
|
address string
|
||||||
handler *modbus.TCPClientHandler
|
handler *modbus.TCPClientHandler
|
||||||
client modbus.Client
|
client modbus.Client
|
||||||
Inputs [15]bool
|
Inputs [37]bool
|
||||||
Counters [10]uint16
|
Counters [0]uint16
|
||||||
Coils [24]bool
|
Coils [8]bool
|
||||||
cycleCounter int
|
cycleCounter int
|
||||||
resetCountCycles int
|
resetCountCycles int
|
||||||
}
|
}
|
||||||
@@ -34,60 +34,55 @@ const (
|
|||||||
// Discrete inputs
|
// Discrete inputs
|
||||||
const (
|
const (
|
||||||
fieldEstop = iota
|
fieldEstop = iota
|
||||||
|
scaleNear
|
||||||
|
scaleFar
|
||||||
redEstop1
|
redEstop1
|
||||||
redEstop2
|
redEstop2
|
||||||
redEstop3
|
redEstop3
|
||||||
redRotor1
|
redSwitchNear
|
||||||
redTouchpad1
|
redSwitchFar
|
||||||
redTouchpad2
|
redForceCube1
|
||||||
redTouchpad3
|
redForceCube2
|
||||||
|
redForceCube3
|
||||||
|
redForceButton
|
||||||
|
redLevitateCube1
|
||||||
|
redLevitateCube2
|
||||||
|
redLevitateCube3
|
||||||
|
redLevitateButton
|
||||||
|
redBoostCube1
|
||||||
|
redBoostCube2
|
||||||
|
redBoostCube3
|
||||||
|
redBoostButton
|
||||||
blueEstop1
|
blueEstop1
|
||||||
blueEstop2
|
blueEstop2
|
||||||
blueEstop3
|
blueEstop3
|
||||||
blueRotor1
|
blueSwitchNear
|
||||||
blueTouchpad1
|
blueSwitchFar
|
||||||
blueTouchpad2
|
blueForceCube1
|
||||||
blueTouchpad3
|
blueForceCube2
|
||||||
|
blueForceCube3
|
||||||
|
blueForceButton
|
||||||
|
blueLevitate1
|
||||||
|
blueLevitate2
|
||||||
|
blueLevitate3
|
||||||
|
blueLevitateButton
|
||||||
|
blueBoostCube1
|
||||||
|
blueBoostCube2
|
||||||
|
blueBoostCube3
|
||||||
|
blueBoostButton
|
||||||
)
|
)
|
||||||
|
|
||||||
// 16-bit registers
|
// 16-bit registers
|
||||||
const (
|
const ()
|
||||||
redRotor2Count = iota
|
|
||||||
redRotor3Count
|
|
||||||
redRotor4Count
|
|
||||||
redLowBoilerCount
|
|
||||||
redHighBoilerCount
|
|
||||||
blueRotor2Count
|
|
||||||
blueRotor3Count
|
|
||||||
blueRotor4Count
|
|
||||||
blueLowBoilerCount
|
|
||||||
blueHighBoilerCount
|
|
||||||
)
|
|
||||||
|
|
||||||
// Coils
|
// Coils
|
||||||
const (
|
const (
|
||||||
redSerializer = iota
|
redForceLight = iota
|
||||||
redBallLift
|
redLevitateLight
|
||||||
redRotorMotor1
|
redBoostLight
|
||||||
redRotorMotor2
|
blueForceLight
|
||||||
redRotorMotor3
|
blueLevitateLight
|
||||||
redRotorMotor4
|
blueBoostLight
|
||||||
redAutoLight1
|
|
||||||
redAutoLight2
|
|
||||||
redTouchpadLight1
|
|
||||||
redTouchpadLight2
|
|
||||||
redTouchpadLight3
|
|
||||||
blueSerializer
|
|
||||||
blueBallLift
|
|
||||||
blueRotorMotor1
|
|
||||||
blueRotorMotor2
|
|
||||||
blueRotorMotor3
|
|
||||||
blueRotorMotor4
|
|
||||||
blueAutoLight1
|
|
||||||
blueAutoLight2
|
|
||||||
blueTouchpadLight1
|
|
||||||
blueTouchpadLight2
|
|
||||||
blueTouchpadLight3
|
|
||||||
resetCounts
|
resetCounts
|
||||||
heartbeat
|
heartbeat
|
||||||
)
|
)
|
||||||
@@ -153,77 +148,66 @@ func (plc *Plc) GetTeamEstops() ([3]bool, [3]bool) {
|
|||||||
return redEstops, blueEstops
|
return redEstops, blueEstops
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the count of the red and blue low and high boilers.
|
// Returns the state of the scale and the red and blue switches.
|
||||||
func (plc *Plc) GetBalls() (int, int, int, int) {
|
func (plc *Plc) GetScaleAndSwitches() ([2]bool, [2]bool, [2]bool) {
|
||||||
return int(plc.Counters[redLowBoilerCount]), int(plc.Counters[redHighBoilerCount]),
|
var scale, redSwitch, blueSwitch [2]bool
|
||||||
int(plc.Counters[blueLowBoilerCount]), int(plc.Counters[blueHighBoilerCount])
|
|
||||||
|
scale[0] = plc.Inputs[scaleNear]
|
||||||
|
scale[1] = plc.Inputs[scaleFar]
|
||||||
|
redSwitch[0] = plc.Inputs[redSwitchNear]
|
||||||
|
redSwitch[1] = plc.Inputs[redSwitchFar]
|
||||||
|
blueSwitch[0] = plc.Inputs[blueSwitchNear]
|
||||||
|
blueSwitch[1] = plc.Inputs[blueSwitchFar]
|
||||||
|
|
||||||
|
return scale, redSwitch, blueSwitch
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the state of red and blue activated rotors.
|
// Returns the state of the red and blue vault power cube sensors.
|
||||||
func (plc *Plc) GetRotors() (bool, [3]int, bool, [3]int) {
|
func (plc *Plc) GetVaults() ([3]bool, [3]bool, [3]bool, [3]bool, [3]bool, [3]bool) {
|
||||||
var redOtherRotors, blueOtherRotors [3]int
|
var redForce, redLevitate, redBoost, blueForce, blueLevitate, blueBoost [3]bool
|
||||||
|
|
||||||
redOtherRotors[0] = int(plc.Counters[redRotor2Count])
|
redForce[0] = plc.Inputs[redForceCube1]
|
||||||
redOtherRotors[1] = int(plc.Counters[redRotor3Count])
|
redForce[1] = plc.Inputs[redForceCube2]
|
||||||
redOtherRotors[2] = int(plc.Counters[redRotor4Count])
|
redForce[2] = plc.Inputs[redForceCube3]
|
||||||
blueOtherRotors[0] = int(plc.Counters[blueRotor2Count])
|
redLevitate[0] = plc.Inputs[redLevitateCube1]
|
||||||
blueOtherRotors[1] = int(plc.Counters[blueRotor3Count])
|
redLevitate[1] = plc.Inputs[redLevitateCube2]
|
||||||
blueOtherRotors[2] = int(plc.Counters[blueRotor4Count])
|
redLevitate[2] = plc.Inputs[redLevitateCube3]
|
||||||
|
redBoost[0] = plc.Inputs[redBoostCube1]
|
||||||
|
redBoost[1] = plc.Inputs[redBoostCube2]
|
||||||
|
redBoost[2] = plc.Inputs[redBoostCube3]
|
||||||
|
blueForce[0] = plc.Inputs[blueForceCube1]
|
||||||
|
blueForce[1] = plc.Inputs[blueForceCube2]
|
||||||
|
blueForce[2] = plc.Inputs[blueForceCube3]
|
||||||
|
blueLevitate[0] = plc.Inputs[blueLevitate1]
|
||||||
|
blueLevitate[1] = plc.Inputs[blueLevitate2]
|
||||||
|
blueLevitate[2] = plc.Inputs[blueLevitate3]
|
||||||
|
blueBoost[0] = plc.Inputs[blueBoostCube1]
|
||||||
|
blueBoost[1] = plc.Inputs[blueBoostCube2]
|
||||||
|
blueBoost[2] = plc.Inputs[blueBoostCube3]
|
||||||
|
|
||||||
return plc.Inputs[redRotor1], redOtherRotors, plc.Inputs[blueRotor1], blueOtherRotors
|
return redForce, redLevitate, redBoost, blueForce, blueLevitate, blueBoost
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plc *Plc) GetTouchpads() ([3]bool, [3]bool) {
|
// Returns the state of the red and blue power up buttons on the vaults.
|
||||||
var redTouchpads, blueTouchpads [3]bool
|
func (plc *Plc) GetPowerUpButtons() (bool, bool, bool, bool, bool, bool) {
|
||||||
redTouchpads[0] = plc.Inputs[redTouchpad1]
|
return plc.Inputs[redForceButton], plc.Inputs[redLevitateButton], plc.Inputs[redBoostButton],
|
||||||
redTouchpads[1] = plc.Inputs[redTouchpad2]
|
plc.Inputs[blueForceButton], plc.Inputs[blueLevitateButton], plc.Inputs[blueBoostButton]
|
||||||
redTouchpads[2] = plc.Inputs[redTouchpad3]
|
|
||||||
blueTouchpads[0] = plc.Inputs[blueTouchpad1]
|
|
||||||
blueTouchpads[1] = plc.Inputs[blueTouchpad2]
|
|
||||||
blueTouchpads[2] = plc.Inputs[blueTouchpad3]
|
|
||||||
return redTouchpads, blueTouchpads
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resets the ball and rotor gear tooth counts to zero.
|
// Resets the counter counts to zero.
|
||||||
func (plc *Plc) ResetCounts() {
|
func (plc *Plc) ResetCounts() {
|
||||||
plc.Coils[resetCounts] = true
|
plc.Coils[resetCounts] = true
|
||||||
plc.resetCountCycles = 0
|
plc.resetCountCycles = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plc *Plc) SetBoilerMotors(on bool) {
|
// Sets the state of the lights inside the power up buttons on the vaults.
|
||||||
plc.Coils[redSerializer] = on
|
func (plc *Plc) SetPowerUpLights(redForce, redLevitate, redBoost, blueForce, blueLevitate, blueBoost bool) {
|
||||||
plc.Coils[redBallLift] = on
|
plc.Coils[redForceLight] = redForce
|
||||||
plc.Coils[blueSerializer] = on
|
plc.Coils[redLevitateLight] = redLevitate
|
||||||
plc.Coils[blueBallLift] = on
|
plc.Coils[redBoostLight] = redBoost
|
||||||
}
|
plc.Coils[blueForceLight] = blueForce
|
||||||
|
plc.Coils[blueLevitateLight] = blueLevitate
|
||||||
// Turns on/off the rotor motors based on how many rotors each alliance has.
|
plc.Coils[blueBoostLight] = blueBoost
|
||||||
func (plc *Plc) SetRotorMotors(redRotors, blueRotors int) {
|
|
||||||
plc.Coils[redRotorMotor1] = redRotors >= 1
|
|
||||||
plc.Coils[redRotorMotor2] = redRotors >= 2
|
|
||||||
plc.Coils[redRotorMotor3] = redRotors >= 3
|
|
||||||
plc.Coils[redRotorMotor4] = redRotors == 4
|
|
||||||
plc.Coils[blueRotorMotor1] = blueRotors >= 1
|
|
||||||
plc.Coils[blueRotorMotor2] = blueRotors >= 2
|
|
||||||
plc.Coils[blueRotorMotor3] = blueRotors >= 3
|
|
||||||
plc.Coils[blueRotorMotor4] = blueRotors == 4
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turns on/off the auto rotor lights based on how many auto rotors each alliance has.
|
|
||||||
func (plc *Plc) SetRotorLights(redAutoRotors, blueAutoRotors int) {
|
|
||||||
plc.Coils[redAutoLight1] = redAutoRotors >= 1
|
|
||||||
plc.Coils[redAutoLight2] = redAutoRotors == 2
|
|
||||||
plc.Coils[blueAutoLight1] = blueAutoRotors >= 1
|
|
||||||
plc.Coils[blueAutoLight2] = blueAutoRotors == 2
|
|
||||||
}
|
|
||||||
|
|
||||||
func (plc *Plc) SetTouchpadLights(redTouchpads, blueTouchpads [3]bool) {
|
|
||||||
plc.Coils[redTouchpadLight1] = redTouchpads[0]
|
|
||||||
plc.Coils[redTouchpadLight2] = redTouchpads[1]
|
|
||||||
plc.Coils[redTouchpadLight3] = redTouchpads[2]
|
|
||||||
plc.Coils[blueTouchpadLight1] = blueTouchpads[0]
|
|
||||||
plc.Coils[blueTouchpadLight2] = blueTouchpads[1]
|
|
||||||
plc.Coils[blueTouchpadLight3] = blueTouchpads[2]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (plc *Plc) GetCycleState(max, index, duration int) bool {
|
func (plc *Plc) GetCycleState(max, index, duration int) bool {
|
||||||
@@ -255,6 +239,10 @@ func (plc *Plc) resetConnection() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (plc *Plc) readInputs() bool {
|
func (plc *Plc) readInputs() bool {
|
||||||
|
if len(plc.Inputs) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
inputs, err := plc.client.ReadDiscreteInputs(0, uint16(len(plc.Inputs)))
|
inputs, err := plc.client.ReadDiscreteInputs(0, uint16(len(plc.Inputs)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("PLC error reading inputs: %v", err)
|
log.Printf("PLC error reading inputs: %v", err)
|
||||||
@@ -270,6 +258,10 @@ func (plc *Plc) readInputs() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (plc *Plc) readCounters() bool {
|
func (plc *Plc) readCounters() bool {
|
||||||
|
if len(plc.Counters) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
registers, err := plc.client.ReadHoldingRegisters(0, uint16(len(plc.Counters)))
|
registers, err := plc.client.ReadHoldingRegisters(0, uint16(len(plc.Counters)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("PLC error reading registers: %v", err)
|
log.Printf("PLC error reading registers: %v", err)
|
||||||
|
|||||||
@@ -12,9 +12,6 @@ type RealtimeScore struct {
|
|||||||
Cards map[string]string
|
Cards map[string]string
|
||||||
TeleopCommitted bool
|
TeleopCommitted bool
|
||||||
FoulsCommitted bool
|
FoulsCommitted bool
|
||||||
boiler game.Boiler
|
|
||||||
rotorSet game.RotorSet
|
|
||||||
touchpads [3]game.Touchpad
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRealtimeScore() *RealtimeScore {
|
func NewRealtimeScore() *RealtimeScore {
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
// Copyright 2017 Team 254. All Rights Reserved.
|
|
||||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
|
||||||
//
|
|
||||||
// Scoring logic for the 2017 boiler element.
|
|
||||||
|
|
||||||
package game
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
BoilerAutoGracePeriodSec = 5
|
|
||||||
BoilerTeleopGracePeriodSec = 5
|
|
||||||
)
|
|
||||||
|
|
||||||
type Boiler struct {
|
|
||||||
AutoFuelLow int
|
|
||||||
AutoFuelHigh int
|
|
||||||
FuelLow int
|
|
||||||
FuelHigh int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updates the internal counting state of the boiler given the current state of the hardware counts. Allows the score to
|
|
||||||
// accumulate before the match, since the counters will be reset in hardware.
|
|
||||||
func (boiler *Boiler) UpdateState(lowCount, highCount int, matchStartTime, currentTime time.Time) {
|
|
||||||
autoValidityDuration := time.Duration(MatchTiming.AutoDurationSec+BoilerAutoGracePeriodSec) * time.Second
|
|
||||||
autoValidityCutoff := matchStartTime.Add(autoValidityDuration)
|
|
||||||
teleopValidityDuration := time.Duration(MatchTiming.AutoDurationSec+MatchTiming.PauseDurationSec+
|
|
||||||
MatchTiming.TeleopDurationSec+BoilerTeleopGracePeriodSec) * time.Second
|
|
||||||
teleopValidityCutoff := matchStartTime.Add(teleopValidityDuration)
|
|
||||||
|
|
||||||
if currentTime.Before(autoValidityCutoff) {
|
|
||||||
boiler.AutoFuelLow = lowCount
|
|
||||||
boiler.AutoFuelHigh = highCount
|
|
||||||
boiler.FuelLow = 0
|
|
||||||
boiler.FuelHigh = 0
|
|
||||||
} else if currentTime.Before(teleopValidityCutoff) {
|
|
||||||
boiler.FuelLow = lowCount - boiler.AutoFuelLow
|
|
||||||
boiler.FuelHigh = highCount - boiler.AutoFuelHigh
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
// Copyright 2017 Team 254. All Rights Reserved.
|
|
||||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
|
||||||
|
|
||||||
package game
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var matchStartTime = time.Unix(10, 0)
|
|
||||||
|
|
||||||
func TestFuelBeforeMatch(t *testing.T) {
|
|
||||||
boiler := Boiler{}
|
|
||||||
|
|
||||||
boiler.UpdateState(1, 2, matchStartTime, timeAfterStart(-1))
|
|
||||||
checkBoilerCounts(t, 1, 2, 0, 0, &boiler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAutoFuel(t *testing.T) {
|
|
||||||
boiler := Boiler{}
|
|
||||||
|
|
||||||
boiler.UpdateState(3, 4, matchStartTime, timeAfterStart(1))
|
|
||||||
checkBoilerCounts(t, 3, 4, 0, 0, &boiler)
|
|
||||||
boiler.UpdateState(5, 6, matchStartTime, timeAfterStart(10))
|
|
||||||
checkBoilerCounts(t, 5, 6, 0, 0, &boiler)
|
|
||||||
boiler.UpdateState(7, 8, matchStartTime, timeAfterStart(19.9))
|
|
||||||
checkBoilerCounts(t, 7, 8, 0, 0, &boiler)
|
|
||||||
boiler.UpdateState(9, 11, matchStartTime, timeAfterStart(20.1))
|
|
||||||
checkBoilerCounts(t, 7, 8, 2, 3, &boiler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTeleopFuel(t *testing.T) {
|
|
||||||
boiler := Boiler{}
|
|
||||||
|
|
||||||
boiler.UpdateState(1, 2, matchStartTime, timeAfterStart(1))
|
|
||||||
boiler.UpdateState(3, 5, matchStartTime, timeAfterStart(21))
|
|
||||||
checkBoilerCounts(t, 1, 2, 2, 3, &boiler)
|
|
||||||
boiler.UpdateState(5, 7, matchStartTime, timeAfterStart(120))
|
|
||||||
checkBoilerCounts(t, 1, 2, 4, 5, &boiler)
|
|
||||||
boiler.UpdateState(7, 9, matchStartTime, timeAfterEnd(-1))
|
|
||||||
checkBoilerCounts(t, 1, 2, 6, 7, &boiler)
|
|
||||||
boiler.UpdateState(9, 11, matchStartTime, timeAfterEnd(4.9))
|
|
||||||
checkBoilerCounts(t, 1, 2, 8, 9, &boiler)
|
|
||||||
boiler.UpdateState(11, 13, matchStartTime, timeAfterEnd(5.1))
|
|
||||||
checkBoilerCounts(t, 1, 2, 8, 9, &boiler)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkBoilerCounts(t *testing.T, autoLow, autoHigh, low, high int, boiler *Boiler) {
|
|
||||||
assert.Equal(t, autoLow, boiler.AutoFuelLow)
|
|
||||||
assert.Equal(t, autoHigh, boiler.AutoFuelHigh)
|
|
||||||
assert.Equal(t, low, boiler.FuelLow)
|
|
||||||
assert.Equal(t, high, boiler.FuelHigh)
|
|
||||||
}
|
|
||||||
func timeAfterStart(sec float32) time.Time {
|
|
||||||
return matchStartTime.Add(time.Duration(1000*sec) * time.Millisecond)
|
|
||||||
}
|
|
||||||
|
|
||||||
func timeAfterEnd(sec float32) time.Time {
|
|
||||||
matchDuration := time.Duration(MatchTiming.AutoDurationSec+MatchTiming.PauseDurationSec+
|
|
||||||
MatchTiming.TeleopDurationSec) * time.Second
|
|
||||||
return matchStartTime.Add(matchDuration).Add(time.Duration(1000*sec) * time.Millisecond)
|
|
||||||
}
|
|
||||||
@@ -14,6 +14,14 @@ var MatchTiming = struct {
|
|||||||
EndgameTimeLeftSec int
|
EndgameTimeLeftSec int
|
||||||
}{15, 2, 135, 30}
|
}{15, 2, 135, 30}
|
||||||
|
|
||||||
|
func GetAutoEndTime(matchStartTime time.Time) time.Time {
|
||||||
|
return matchStartTime.Add(time.Duration(MatchTiming.AutoDurationSec))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTeleopStartTime(matchStartTime time.Time) time.Time {
|
||||||
|
return matchStartTime.Add(time.Duration(MatchTiming.AutoDurationSec + MatchTiming.PauseDurationSec))
|
||||||
|
}
|
||||||
|
|
||||||
func GetMatchEndTime(matchStartTime time.Time) time.Time {
|
func GetMatchEndTime(matchStartTime time.Time) time.Time {
|
||||||
return matchStartTime.Add(time.Duration(MatchTiming.AutoDurationSec+MatchTiming.PauseDurationSec+
|
return matchStartTime.Add(time.Duration(MatchTiming.AutoDurationSec+MatchTiming.PauseDurationSec+
|
||||||
MatchTiming.TeleopDurationSec) * time.Second)
|
MatchTiming.TeleopDurationSec) * time.Second)
|
||||||
|
|||||||
89
game/power_up.go
Normal file
89
game/power_up.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
// Copyright 2018 Team 254. All Rights Reserved.
|
||||||
|
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||||
|
//
|
||||||
|
// Scoring logic for the 2018 power ups.
|
||||||
|
|
||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const powerUpDurationSec = 10
|
||||||
|
|
||||||
|
// Power up type/effect enum.
|
||||||
|
const (
|
||||||
|
force = iota
|
||||||
|
boost
|
||||||
|
)
|
||||||
|
|
||||||
|
// Power up state enum.
|
||||||
|
const (
|
||||||
|
queued = iota
|
||||||
|
active
|
||||||
|
expired
|
||||||
|
)
|
||||||
|
|
||||||
|
type PowerUp struct {
|
||||||
|
alliance int
|
||||||
|
kind int
|
||||||
|
level int
|
||||||
|
startTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
var powerUpUses []*PowerUp
|
||||||
|
|
||||||
|
func ResetPowerUps() {
|
||||||
|
powerUpUses = powerUpUses[:0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (powerUp *PowerUp) GetState(currentTime time.Time) int {
|
||||||
|
if powerUp.startTime.After(currentTime) {
|
||||||
|
return queued
|
||||||
|
}
|
||||||
|
if powerUp.getEndTime().After(currentTime) {
|
||||||
|
return active
|
||||||
|
}
|
||||||
|
return expired
|
||||||
|
}
|
||||||
|
|
||||||
|
func (powerUp *PowerUp) getEndTime() time.Time {
|
||||||
|
return powerUp.startTime.Add(powerUpDurationSec * time.Second)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the current active power up, or nil if there isn't one.
|
||||||
|
func getActivePowerUp(currentTime time.Time) *PowerUp {
|
||||||
|
for _, powerUp := range powerUpUses {
|
||||||
|
if powerUp.GetState(currentTime) == active {
|
||||||
|
return powerUp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activates the given power up if it can be played, or if not, queues it if the active power up belongs to the other
|
||||||
|
// alliance. Returns the power up if successful and nil if it cannot be played.
|
||||||
|
func maybeActivatePowerUp(powerUp *PowerUp, currentTime time.Time) *PowerUp {
|
||||||
|
canActivate := false
|
||||||
|
if len(powerUpUses) == 0 {
|
||||||
|
canActivate = true
|
||||||
|
powerUp.startTime = currentTime
|
||||||
|
} else {
|
||||||
|
lastPowerUp := powerUpUses[len(powerUpUses)-1]
|
||||||
|
lastPowerUpState := lastPowerUp.GetState(currentTime)
|
||||||
|
if lastPowerUpState == expired {
|
||||||
|
canActivate = true
|
||||||
|
powerUp.startTime = currentTime
|
||||||
|
} else if lastPowerUpState == active && lastPowerUp.alliance != powerUp.alliance {
|
||||||
|
canActivate = true
|
||||||
|
powerUp.startTime = lastPowerUp.getEndTime()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if canActivate {
|
||||||
|
powerUpUses = append(powerUpUses, powerUp)
|
||||||
|
return powerUp
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
66
game/power_up_test.go
Normal file
66
game/power_up_test.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2018 Team 254. All Rights Reserved.
|
||||||
|
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||||
|
|
||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var matchStartTime = time.Unix(10, 0)
|
||||||
|
|
||||||
|
func TestPowerUpGetState(t *testing.T) {
|
||||||
|
powerUp := PowerUp{startTime: timeAfterStart(30)}
|
||||||
|
assert.Equal(t, queued, powerUp.GetState(timeAfterStart(25)))
|
||||||
|
assert.Equal(t, queued, powerUp.GetState(timeAfterStart(29.9)))
|
||||||
|
assert.Equal(t, active, powerUp.GetState(timeAfterStart(30.1)))
|
||||||
|
assert.Equal(t, active, powerUp.GetState(timeAfterStart(39.9)))
|
||||||
|
assert.Equal(t, expired, powerUp.GetState(timeAfterStart(40.1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPowerUpActivate(t *testing.T) {
|
||||||
|
powerUp1 := new(PowerUp)
|
||||||
|
if assert.NotNil(t, maybeActivatePowerUp(powerUp1, timeAfterStart(30))) {
|
||||||
|
assert.Equal(t, timeAfterStart(30), powerUp1.startTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
powerUp2 := new(PowerUp)
|
||||||
|
if assert.NotNil(t, maybeActivatePowerUp(powerUp2, timeAfterStart(45))) {
|
||||||
|
assert.Equal(t, timeAfterStart(45), powerUp2.startTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Nil(t, getActivePowerUp(timeAfterStart(29.9)))
|
||||||
|
assert.Equal(t, powerUp1, getActivePowerUp(timeAfterStart(30.1)))
|
||||||
|
assert.Equal(t, powerUp1, getActivePowerUp(timeAfterStart(39.9)))
|
||||||
|
assert.Nil(t, getActivePowerUp(timeAfterStart(42)))
|
||||||
|
assert.Equal(t, powerUp2, getActivePowerUp(timeAfterStart(45.1)))
|
||||||
|
assert.Equal(t, powerUp2, getActivePowerUp(timeAfterStart(54.9)))
|
||||||
|
assert.Nil(t, getActivePowerUp(timeAfterStart(55.1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPowerUpQueue(t *testing.T) {
|
||||||
|
powerUp1 := &PowerUp{alliance: redAlliance}
|
||||||
|
maybeActivatePowerUp(powerUp1, timeAfterStart(60))
|
||||||
|
|
||||||
|
powerUp2 := &PowerUp{alliance: redAlliance}
|
||||||
|
assert.Nil(t, maybeActivatePowerUp(powerUp2, timeAfterStart(65)))
|
||||||
|
powerUp2.alliance = blueAlliance
|
||||||
|
if assert.NotNil(t, maybeActivatePowerUp(powerUp2, timeAfterStart(65))) {
|
||||||
|
assert.Equal(t, timeAfterStart(70), powerUp2.startTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, powerUp1, getActivePowerUp(timeAfterStart(69.9)))
|
||||||
|
assert.Equal(t, powerUp2, getActivePowerUp(timeAfterStart(70.1)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeAfterStart(sec float32) time.Time {
|
||||||
|
return matchStartTime.Add(time.Duration(1000*sec) * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
func timeAfterEnd(sec float32) time.Time {
|
||||||
|
matchDuration := time.Duration(MatchTiming.AutoDurationSec+MatchTiming.PauseDurationSec+
|
||||||
|
MatchTiming.TeleopDurationSec) * time.Second
|
||||||
|
return matchStartTime.Add(matchDuration).Add(time.Duration(1000*sec) * time.Millisecond)
|
||||||
|
}
|
||||||
@@ -9,11 +9,10 @@ import "math/rand"
|
|||||||
|
|
||||||
type RankingFields struct {
|
type RankingFields struct {
|
||||||
RankingPoints int
|
RankingPoints int
|
||||||
MatchPoints int
|
ParkClimbPoints int
|
||||||
AutoPoints int
|
AutoPoints int
|
||||||
RotorPoints int
|
OwnershipPoints int
|
||||||
TakeoffPoints int
|
VaultPoints int
|
||||||
PressurePoints int
|
|
||||||
Random float64
|
Random float64
|
||||||
Wins int
|
Wins int
|
||||||
Losses int
|
Losses int
|
||||||
@@ -49,19 +48,18 @@ func (fields *RankingFields) AddScoreSummary(ownScore *ScoreSummary, opponentSco
|
|||||||
} else {
|
} else {
|
||||||
fields.Losses += 1
|
fields.Losses += 1
|
||||||
}
|
}
|
||||||
if ownScore.PressureGoalReached {
|
if ownScore.AutoQuest {
|
||||||
fields.RankingPoints += 1
|
fields.RankingPoints += 1
|
||||||
}
|
}
|
||||||
if ownScore.RotorGoalReached {
|
if ownScore.FaceTheBoss {
|
||||||
fields.RankingPoints += 1
|
fields.RankingPoints += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assign tiebreaker points.
|
// Assign tiebreaker points.
|
||||||
fields.MatchPoints += ownScore.Score
|
fields.ParkClimbPoints += ownScore.ParkClimbPoints
|
||||||
fields.AutoPoints += ownScore.AutoPoints
|
fields.AutoPoints += ownScore.AutoPoints
|
||||||
fields.RotorPoints += ownScore.RotorPoints
|
fields.OwnershipPoints += ownScore.OwnershipPoints
|
||||||
fields.TakeoffPoints += ownScore.TakeoffPoints
|
fields.VaultPoints += ownScore.VaultPoints
|
||||||
fields.PressurePoints += ownScore.PressurePoints
|
|
||||||
|
|
||||||
// Store a random value to be used as the last tiebreaker if necessary.
|
// Store a random value to be used as the last tiebreaker if necessary.
|
||||||
fields.Random = rand.Float64()
|
fields.Random = rand.Float64()
|
||||||
@@ -79,22 +77,19 @@ func (rankings Rankings) Less(i, j int) bool {
|
|||||||
|
|
||||||
// Use cross-multiplication to keep it in integer math.
|
// Use cross-multiplication to keep it in integer math.
|
||||||
if a.RankingPoints*b.Played == b.RankingPoints*a.Played {
|
if a.RankingPoints*b.Played == b.RankingPoints*a.Played {
|
||||||
if a.MatchPoints*b.Played == b.MatchPoints*a.Played {
|
if a.ParkClimbPoints*b.Played == b.ParkClimbPoints*a.Played {
|
||||||
if a.AutoPoints*b.Played == b.AutoPoints*a.Played {
|
if a.AutoPoints*b.Played == b.AutoPoints*a.Played {
|
||||||
if a.RotorPoints*b.Played == b.RotorPoints*a.Played {
|
if a.OwnershipPoints*b.Played == b.OwnershipPoints*a.Played {
|
||||||
if a.TakeoffPoints*b.Played == b.TakeoffPoints*a.Played {
|
if a.VaultPoints*b.Played == b.VaultPoints*a.Played {
|
||||||
if a.PressurePoints*b.Played == b.PressurePoints*a.Played {
|
return a.Random > b.Random
|
||||||
return a.Random > b.Random
|
|
||||||
}
|
|
||||||
return a.PressurePoints*b.Played > b.PressurePoints*a.Played
|
|
||||||
}
|
}
|
||||||
return a.TakeoffPoints*b.Played > b.TakeoffPoints*a.Played
|
return a.VaultPoints*b.Played > b.VaultPoints*a.Played
|
||||||
}
|
}
|
||||||
return a.RotorPoints*b.Played > b.RotorPoints*a.Played
|
return a.OwnershipPoints*b.Played > b.OwnershipPoints*a.Played
|
||||||
}
|
}
|
||||||
return a.AutoPoints*b.Played > b.AutoPoints*a.Played
|
return a.AutoPoints*b.Played > b.AutoPoints*a.Played
|
||||||
}
|
}
|
||||||
return a.MatchPoints*b.Played > b.MatchPoints*a.Played
|
return a.ParkClimbPoints*b.Played > b.ParkClimbPoints*a.Played
|
||||||
}
|
}
|
||||||
return a.RankingPoints*b.Played > b.RankingPoints*a.Played
|
return a.RankingPoints*b.Played > b.RankingPoints*a.Played
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,65 +14,61 @@ func TestAddScoreSummary(t *testing.T) {
|
|||||||
rand.Seed(0)
|
rand.Seed(0)
|
||||||
redScore := TestScore1()
|
redScore := TestScore1()
|
||||||
blueScore := TestScore2()
|
blueScore := TestScore2()
|
||||||
redSummary := redScore.Summarize(blueScore.Fouls, "qualification")
|
redSummary := redScore.Summarize(blueScore.Fouls)
|
||||||
blueSummary := blueScore.Summarize(redScore.Fouls, "qualification")
|
blueSummary := blueScore.Summarize(redScore.Fouls)
|
||||||
rankingFields := RankingFields{}
|
rankingFields := RankingFields{}
|
||||||
|
|
||||||
// Add a loss.
|
// Add a loss.
|
||||||
rankingFields.AddScoreSummary(redSummary, blueSummary, false)
|
rankingFields.AddScoreSummary(redSummary, blueSummary, false)
|
||||||
assert.Equal(t, RankingFields{1, 190, 80, 100, 50, 40, 0.9451961492941164, 0, 1, 0, 0, 1}, rankingFields)
|
assert.Equal(t, RankingFields{1, 90, 17, 59, 15, 0.9451961492941164, 0, 1, 0, 0, 1}, rankingFields)
|
||||||
|
|
||||||
// Add a win.
|
// Add a win.
|
||||||
rankingFields.AddScoreSummary(blueSummary, redSummary, false)
|
rankingFields.AddScoreSummary(blueSummary, redSummary, false)
|
||||||
assert.Equal(t, RankingFields{4, 623, 213, 300, 200, 58, 0.24496508529377975, 1, 1, 0, 0, 2}, rankingFields)
|
assert.Equal(t, RankingFields{4, 125, 52, 152, 45, 0.24496508529377975, 1, 1, 0, 0, 2}, rankingFields)
|
||||||
|
|
||||||
// Add a tie.
|
// Add a tie.
|
||||||
rankingFields.AddScoreSummary(redSummary, redSummary, false)
|
rankingFields.AddScoreSummary(redSummary, redSummary, false)
|
||||||
assert.Equal(t, RankingFields{6, 813, 293, 400, 250, 98, 0.6559562651954052, 1, 1, 1, 0, 3}, rankingFields)
|
assert.Equal(t, RankingFields{6, 215, 69, 211, 60, 0.6559562651954052, 1, 1, 1, 0, 3}, rankingFields)
|
||||||
|
|
||||||
// Add a disqualification.
|
// Add a disqualification.
|
||||||
rankingFields.AddScoreSummary(blueSummary, redSummary, true)
|
rankingFields.AddScoreSummary(blueSummary, redSummary, true)
|
||||||
assert.Equal(t, RankingFields{6, 813, 293, 400, 250, 98, 0.6559562651954052, 1, 1, 1, 1, 4}, rankingFields)
|
assert.Equal(t, RankingFields{6, 215, 69, 211, 60, 0.6559562651954052, 1, 1, 1, 1, 4}, rankingFields)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSortRankings(t *testing.T) {
|
func TestSortRankings(t *testing.T) {
|
||||||
// Check tiebreakers.
|
// Check tiebreakers.
|
||||||
rankings := make(Rankings, 14)
|
rankings := make(Rankings, 12)
|
||||||
rankings[0] = &Ranking{1, 0, RankingFields{50, 50, 50, 50, 50, 50, 0.49, 3, 2, 1, 0, 10}}
|
rankings[0] = &Ranking{1, 0, RankingFields{50, 50, 50, 50, 50, 0.49, 3, 2, 1, 0, 10}}
|
||||||
rankings[1] = &Ranking{2, 0, RankingFields{50, 50, 50, 50, 50, 50, 0.51, 3, 2, 1, 0, 10}}
|
rankings[1] = &Ranking{2, 0, RankingFields{50, 50, 50, 50, 50, 0.51, 3, 2, 1, 0, 10}}
|
||||||
rankings[2] = &Ranking{3, 0, RankingFields{50, 50, 50, 50, 50, 49, 0.50, 3, 2, 1, 0, 10}}
|
rankings[2] = &Ranking{3, 0, RankingFields{50, 50, 50, 50, 49, 0.50, 3, 2, 1, 0, 10}}
|
||||||
rankings[3] = &Ranking{4, 0, RankingFields{50, 50, 50, 50, 50, 51, 0.50, 3, 2, 1, 0, 10}}
|
rankings[3] = &Ranking{4, 0, RankingFields{50, 50, 50, 50, 51, 0.50, 3, 2, 1, 0, 10}}
|
||||||
rankings[4] = &Ranking{5, 0, RankingFields{50, 50, 50, 50, 49, 50, 0.50, 3, 2, 1, 0, 10}}
|
rankings[4] = &Ranking{5, 0, RankingFields{50, 50, 50, 49, 50, 0.50, 3, 2, 1, 0, 10}}
|
||||||
rankings[5] = &Ranking{6, 0, RankingFields{50, 50, 50, 50, 51, 50, 0.50, 3, 2, 1, 0, 10}}
|
rankings[5] = &Ranking{6, 0, RankingFields{50, 50, 50, 51, 50, 0.50, 3, 2, 1, 0, 10}}
|
||||||
rankings[6] = &Ranking{7, 0, RankingFields{50, 50, 50, 49, 50, 50, 0.50, 3, 2, 1, 0, 10}}
|
rankings[6] = &Ranking{7, 0, RankingFields{50, 50, 49, 50, 50, 0.50, 3, 2, 1, 0, 10}}
|
||||||
rankings[7] = &Ranking{8, 0, RankingFields{50, 50, 50, 51, 50, 50, 0.50, 3, 2, 1, 0, 10}}
|
rankings[7] = &Ranking{8, 0, RankingFields{50, 50, 51, 50, 50, 0.50, 3, 2, 1, 0, 10}}
|
||||||
rankings[8] = &Ranking{9, 0, RankingFields{50, 50, 49, 50, 50, 50, 0.50, 3, 2, 1, 0, 10}}
|
rankings[8] = &Ranking{9, 0, RankingFields{50, 49, 50, 50, 50, 0.50, 3, 2, 1, 0, 10}}
|
||||||
rankings[9] = &Ranking{10, 0, RankingFields{50, 50, 51, 50, 50, 50, 0.50, 3, 2, 1, 0, 10}}
|
rankings[9] = &Ranking{10, 0, RankingFields{50, 51, 50, 50, 50, 0.50, 3, 2, 1, 0, 10}}
|
||||||
rankings[10] = &Ranking{11, 0, RankingFields{50, 49, 50, 50, 50, 50, 0.50, 3, 2, 1, 0, 10}}
|
rankings[10] = &Ranking{11, 0, RankingFields{49, 50, 50, 50, 50, 0.50, 3, 2, 1, 0, 10}}
|
||||||
rankings[11] = &Ranking{12, 0, RankingFields{50, 51, 50, 50, 50, 50, 0.50, 3, 2, 1, 0, 10}}
|
rankings[11] = &Ranking{12, 0, RankingFields{51, 50, 50, 50, 50, 0.50, 3, 2, 1, 0, 10}}
|
||||||
rankings[12] = &Ranking{13, 0, RankingFields{49, 50, 50, 50, 50, 50, 0.50, 3, 2, 1, 0, 10}}
|
|
||||||
rankings[13] = &Ranking{14, 0, RankingFields{51, 50, 50, 50, 50, 50, 0.50, 3, 2, 1, 0, 10}}
|
|
||||||
sort.Sort(rankings)
|
sort.Sort(rankings)
|
||||||
assert.Equal(t, 14, rankings[0].TeamId)
|
assert.Equal(t, 12, rankings[0].TeamId)
|
||||||
assert.Equal(t, 12, rankings[1].TeamId)
|
assert.Equal(t, 10, rankings[1].TeamId)
|
||||||
assert.Equal(t, 10, rankings[2].TeamId)
|
assert.Equal(t, 8, rankings[2].TeamId)
|
||||||
assert.Equal(t, 8, rankings[3].TeamId)
|
assert.Equal(t, 6, rankings[3].TeamId)
|
||||||
assert.Equal(t, 6, rankings[4].TeamId)
|
assert.Equal(t, 4, rankings[4].TeamId)
|
||||||
assert.Equal(t, 4, rankings[5].TeamId)
|
assert.Equal(t, 2, rankings[5].TeamId)
|
||||||
assert.Equal(t, 2, rankings[6].TeamId)
|
assert.Equal(t, 1, rankings[6].TeamId)
|
||||||
assert.Equal(t, 1, rankings[7].TeamId)
|
assert.Equal(t, 3, rankings[7].TeamId)
|
||||||
assert.Equal(t, 3, rankings[8].TeamId)
|
assert.Equal(t, 5, rankings[8].TeamId)
|
||||||
assert.Equal(t, 5, rankings[9].TeamId)
|
assert.Equal(t, 7, rankings[9].TeamId)
|
||||||
assert.Equal(t, 7, rankings[10].TeamId)
|
assert.Equal(t, 9, rankings[10].TeamId)
|
||||||
assert.Equal(t, 9, rankings[11].TeamId)
|
assert.Equal(t, 11, rankings[11].TeamId)
|
||||||
assert.Equal(t, 11, rankings[12].TeamId)
|
|
||||||
assert.Equal(t, 13, rankings[13].TeamId)
|
|
||||||
|
|
||||||
// Check with unequal number of matches played.
|
// Check with unequal number of matches played.
|
||||||
rankings = make(Rankings, 3)
|
rankings = make(Rankings, 3)
|
||||||
rankings[0] = &Ranking{1, 0, RankingFields{10, 25, 25, 25, 25, 25, 0.49, 3, 2, 1, 0, 5}}
|
rankings[0] = &Ranking{1, 0, RankingFields{10, 25, 25, 25, 25, 0.49, 3, 2, 1, 0, 5}}
|
||||||
rankings[1] = &Ranking{2, 0, RankingFields{19, 50, 50, 50, 50, 50, 0.51, 3, 2, 1, 0, 9}}
|
rankings[1] = &Ranking{2, 0, RankingFields{19, 50, 50, 50, 50, 0.51, 3, 2, 1, 0, 9}}
|
||||||
rankings[2] = &Ranking{3, 0, RankingFields{20, 50, 50, 50, 50, 50, 0.51, 3, 2, 1, 0, 10}}
|
rankings[2] = &Ranking{3, 0, RankingFields{20, 50, 50, 50, 50, 0.51, 3, 2, 1, 0, 10}}
|
||||||
sort.Sort(rankings)
|
sort.Sort(rankings)
|
||||||
assert.Equal(t, 2, rankings[0].TeamId)
|
assert.Equal(t, 2, rankings[0].TeamId)
|
||||||
assert.Equal(t, 3, rankings[1].TeamId)
|
assert.Equal(t, 3, rankings[1].TeamId)
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
// Copyright 2017 Team 254. All Rights Reserved.
|
|
||||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
|
||||||
//
|
|
||||||
// Scoring logic for the 2017 rotor elements.
|
|
||||||
|
|
||||||
package game
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const rotorGearToothCount = 15
|
|
||||||
|
|
||||||
type RotorSet struct {
|
|
||||||
AutoRotors int
|
|
||||||
Rotors int
|
|
||||||
counterOffsets [3]int
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updates the internal counting state of the rotors given the current state of the sensors.
|
|
||||||
func (rotorSet *RotorSet) UpdateState(rotor1 bool, otherRotors [3]int, matchStartTime, currentTime time.Time) {
|
|
||||||
autoValidityCutoff := matchStartTime.Add(time.Duration(MatchTiming.AutoDurationSec) * time.Second)
|
|
||||||
teleopValidityCutoff := autoValidityCutoff.Add(time.Duration(MatchTiming.PauseDurationSec+
|
|
||||||
MatchTiming.TeleopDurationSec) * time.Second)
|
|
||||||
|
|
||||||
if currentTime.After(matchStartTime) {
|
|
||||||
if currentTime.Before(autoValidityCutoff) {
|
|
||||||
if rotorSet.AutoRotors == 0 && rotor1 {
|
|
||||||
rotorSet.AutoRotors++
|
|
||||||
rotorSet.counterOffsets[0] = otherRotors[0]
|
|
||||||
}
|
|
||||||
if rotorSet.AutoRotors == 1 && otherRotors[0]-rotorSet.counterOffsets[0] >= rotorGearToothCount {
|
|
||||||
rotorSet.AutoRotors++
|
|
||||||
rotorSet.counterOffsets[1] = otherRotors[1]
|
|
||||||
}
|
|
||||||
} else if currentTime.Before(teleopValidityCutoff) {
|
|
||||||
if rotorSet.totalRotors() == 0 && rotor1 {
|
|
||||||
rotorSet.Rotors++
|
|
||||||
rotorSet.counterOffsets[0] = otherRotors[0]
|
|
||||||
}
|
|
||||||
if rotorSet.totalRotors() == 1 && otherRotors[0]-rotorSet.counterOffsets[0] >= rotorGearToothCount {
|
|
||||||
rotorSet.Rotors++
|
|
||||||
rotorSet.counterOffsets[1] = otherRotors[1]
|
|
||||||
}
|
|
||||||
if rotorSet.totalRotors() == 2 && otherRotors[1]-rotorSet.counterOffsets[1] >= rotorGearToothCount {
|
|
||||||
rotorSet.Rotors++
|
|
||||||
rotorSet.counterOffsets[2] = otherRotors[2]
|
|
||||||
}
|
|
||||||
if rotorSet.totalRotors() == 3 && otherRotors[2]-rotorSet.counterOffsets[2] >= rotorGearToothCount {
|
|
||||||
rotorSet.Rotors++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rotorSet *RotorSet) totalRotors() int {
|
|
||||||
return rotorSet.AutoRotors + rotorSet.Rotors
|
|
||||||
}
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
// Copyright 2017 Team 254. All Rights Reserved.
|
|
||||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
|
||||||
|
|
||||||
package game
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRotorsBeforeMatch(t *testing.T) {
|
|
||||||
rotorSet := RotorSet{}
|
|
||||||
|
|
||||||
rotorSet.UpdateState(true, [3]int{15, 15, 0}, matchStartTime, timeAfterStart(-1))
|
|
||||||
checkRotorCounts(t, 0, 0, &rotorSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRotorCountThreshold(t *testing.T) {
|
|
||||||
rotorSet := RotorSet{}
|
|
||||||
|
|
||||||
rotorSet.UpdateState(true, [3]int{0, 0, 0}, matchStartTime, timeAfterStart(20))
|
|
||||||
checkRotorCounts(t, 0, 1, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{10, 0, 0}, matchStartTime, timeAfterStart(20))
|
|
||||||
checkRotorCounts(t, 0, 1, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{14, 0, 0}, matchStartTime, timeAfterStart(21))
|
|
||||||
checkRotorCounts(t, 0, 1, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{15, 0, 0}, matchStartTime, timeAfterStart(22))
|
|
||||||
checkRotorCounts(t, 0, 2, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{15, 13, 0}, matchStartTime, timeAfterStart(23))
|
|
||||||
checkRotorCounts(t, 0, 2, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{15, 16, 0}, matchStartTime, timeAfterStart(24))
|
|
||||||
checkRotorCounts(t, 0, 3, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{15, 16, 50}, matchStartTime, timeAfterStart(25))
|
|
||||||
checkRotorCounts(t, 0, 4, &rotorSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAutoRotors(t *testing.T) {
|
|
||||||
rotorSet := RotorSet{}
|
|
||||||
|
|
||||||
rotorSet.UpdateState(false, [3]int{0, 0, 0}, matchStartTime, timeAfterStart(1))
|
|
||||||
checkRotorCounts(t, 0, 0, &rotorSet)
|
|
||||||
rotorSet.UpdateState(false, [3]int{15, 15, 15}, matchStartTime, timeAfterStart(1))
|
|
||||||
checkRotorCounts(t, 0, 0, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{0, 0, 0}, matchStartTime, timeAfterStart(1))
|
|
||||||
checkRotorCounts(t, 1, 0, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{15, 0, 0}, matchStartTime, timeAfterStart(5))
|
|
||||||
checkRotorCounts(t, 2, 0, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{15, 15, 0}, matchStartTime, timeAfterStart(11))
|
|
||||||
checkRotorCounts(t, 2, 0, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{15, 15, 0}, matchStartTime, timeAfterStart(20))
|
|
||||||
checkRotorCounts(t, 2, 1, &rotorSet)
|
|
||||||
|
|
||||||
// Check timing threshold.
|
|
||||||
rotorSet = RotorSet{}
|
|
||||||
rotorSet.UpdateState(true, [3]int{0, 0, 0}, matchStartTime, timeAfterStart(5))
|
|
||||||
checkRotorCounts(t, 1, 0, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{15, 0, 0}, matchStartTime, timeAfterStart(15.1))
|
|
||||||
checkRotorCounts(t, 1, 1, &rotorSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTeleopRotors(t *testing.T) {
|
|
||||||
rotorSet := RotorSet{}
|
|
||||||
|
|
||||||
rotorSet.UpdateState(false, [3]int{0, 0, 0}, matchStartTime, timeAfterStart(14))
|
|
||||||
checkRotorCounts(t, 0, 0, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{0, 0, 0}, matchStartTime, timeAfterStart(20))
|
|
||||||
checkRotorCounts(t, 0, 1, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{15, 0, 0}, matchStartTime, timeAfterStart(30))
|
|
||||||
checkRotorCounts(t, 0, 2, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{15, 15, 0}, matchStartTime, timeAfterStart(100))
|
|
||||||
checkRotorCounts(t, 0, 3, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{15, 15, 15}, matchStartTime, timeAfterStart(120))
|
|
||||||
checkRotorCounts(t, 0, 4, &rotorSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRotorsAfterMatch(t *testing.T) {
|
|
||||||
rotorSet := RotorSet{}
|
|
||||||
|
|
||||||
rotorSet.UpdateState(true, [3]int{0, 0, 0}, matchStartTime, timeAfterEnd(1))
|
|
||||||
checkRotorCounts(t, 0, 0, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{15, 0, 0}, matchStartTime, timeAfterEnd(2))
|
|
||||||
checkRotorCounts(t, 0, 0, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{15, 15, 0}, matchStartTime, timeAfterEnd(3))
|
|
||||||
checkRotorCounts(t, 0, 0, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{15, 15, 15}, matchStartTime, timeAfterEnd(4))
|
|
||||||
checkRotorCounts(t, 0, 0, &rotorSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRotorLatching(t *testing.T) {
|
|
||||||
rotorSet := RotorSet{}
|
|
||||||
|
|
||||||
rotorSet.UpdateState(false, [3]int{15, 0, 0}, matchStartTime, timeAfterStart(1))
|
|
||||||
checkRotorCounts(t, 0, 0, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{0, 0, 0}, matchStartTime, timeAfterStart(2))
|
|
||||||
checkRotorCounts(t, 1, 0, &rotorSet)
|
|
||||||
rotorSet.UpdateState(false, [3]int{0, 0, 0}, matchStartTime, timeAfterStart(5))
|
|
||||||
checkRotorCounts(t, 1, 0, &rotorSet)
|
|
||||||
rotorSet.UpdateState(false, [3]int{15, 0, 0}, matchStartTime, timeAfterStart(10))
|
|
||||||
checkRotorCounts(t, 2, 0, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{15, 0, 0}, matchStartTime, timeAfterStart(10))
|
|
||||||
checkRotorCounts(t, 2, 0, &rotorSet)
|
|
||||||
rotorSet.UpdateState(false, [3]int{0, 0, 15}, matchStartTime, timeAfterStart(20))
|
|
||||||
checkRotorCounts(t, 2, 0, &rotorSet)
|
|
||||||
rotorSet.UpdateState(false, [3]int{0, 15, 0}, matchStartTime, timeAfterStart(30))
|
|
||||||
checkRotorCounts(t, 2, 1, &rotorSet)
|
|
||||||
rotorSet.UpdateState(false, [3]int{0, 0, 15}, matchStartTime, timeAfterStart(50))
|
|
||||||
checkRotorCounts(t, 2, 2, &rotorSet)
|
|
||||||
rotorSet.UpdateState(false, [3]int{0, 0, 0}, matchStartTime, timeAfterEnd(-1))
|
|
||||||
checkRotorCounts(t, 2, 2, &rotorSet)
|
|
||||||
rotorSet.UpdateState(false, [3]int{0, 0, 0}, matchStartTime, timeAfterEnd(1))
|
|
||||||
checkRotorCounts(t, 2, 2, &rotorSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRotorActivationOrder(t *testing.T) {
|
|
||||||
rotorSet := RotorSet{}
|
|
||||||
|
|
||||||
rotorSet.UpdateState(true, [3]int{0, 25, 50}, matchStartTime, timeAfterStart(20))
|
|
||||||
checkRotorCounts(t, 0, 1, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{20, 25, 50}, matchStartTime, timeAfterStart(21))
|
|
||||||
checkRotorCounts(t, 0, 2, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{20, 39, 50}, matchStartTime, timeAfterStart(22))
|
|
||||||
checkRotorCounts(t, 0, 2, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{20, 40, 70}, matchStartTime, timeAfterStart(23))
|
|
||||||
checkRotorCounts(t, 0, 3, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{20, 40, 84}, matchStartTime, timeAfterStart(24))
|
|
||||||
checkRotorCounts(t, 0, 3, &rotorSet)
|
|
||||||
rotorSet.UpdateState(true, [3]int{20, 40, 85}, matchStartTime, timeAfterStart(25))
|
|
||||||
checkRotorCounts(t, 0, 4, &rotorSet)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkRotorCounts(t *testing.T, autoRotors, rotors int, rotorSet *RotorSet) {
|
|
||||||
assert.Equal(t, autoRotors, rotorSet.AutoRotors)
|
|
||||||
assert.Equal(t, rotors, rotorSet.Rotors)
|
|
||||||
}
|
|
||||||
@@ -6,33 +6,32 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
type Score struct {
|
type Score struct {
|
||||||
AutoMobility int
|
AutoRuns int
|
||||||
AutoRotors int
|
AutoEndSwitchOwnership bool
|
||||||
AutoFuelLow int
|
AutoOwnershipPoints int
|
||||||
AutoFuelHigh int
|
TeleopOwnershipPoints int
|
||||||
Rotors int
|
VaultCubes int
|
||||||
FuelLow int
|
Levitate bool
|
||||||
FuelHigh int
|
Parks int
|
||||||
Takeoffs int
|
Climbs int
|
||||||
Fouls []Foul
|
Fouls []Foul
|
||||||
ElimDq bool
|
ElimDq bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type ScoreSummary struct {
|
type ScoreSummary struct {
|
||||||
AutoMobilityPoints int
|
AutoRunPoints int
|
||||||
AutoPoints int
|
AutoPoints int
|
||||||
RotorPoints int
|
OwnershipPoints int
|
||||||
TakeoffPoints int
|
VaultPoints int
|
||||||
PressurePoints int
|
ParkClimbPoints int
|
||||||
BonusPoints int
|
FoulPoints int
|
||||||
FoulPoints int
|
Score int
|
||||||
Score int
|
AutoQuest bool
|
||||||
PressureGoalReached bool
|
FaceTheBoss bool
|
||||||
RotorGoalReached bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculates and returns the summary fields used for ranking and display.
|
// Calculates and returns the summary fields used for ranking and display.
|
||||||
func (score *Score) Summarize(opponentFouls []Foul, matchType string) *ScoreSummary {
|
func (score *Score) Summarize(opponentFouls []Foul) *ScoreSummary {
|
||||||
summary := new(ScoreSummary)
|
summary := new(ScoreSummary)
|
||||||
|
|
||||||
// Leave the score at zero if the team was disqualified.
|
// Leave the score at zero if the team was disqualified.
|
||||||
@@ -41,27 +40,24 @@ func (score *Score) Summarize(opponentFouls []Foul, matchType string) *ScoreSumm
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Calculate autonomous score.
|
// Calculate autonomous score.
|
||||||
summary.AutoMobilityPoints = 5 * score.AutoMobility
|
summary.AutoRunPoints = 5 * score.AutoRuns
|
||||||
summary.AutoPoints = summary.AutoMobilityPoints + 60*score.AutoRotors + score.AutoFuelHigh +
|
summary.AutoPoints = summary.AutoRunPoints + score.AutoOwnershipPoints
|
||||||
score.AutoFuelLow/3
|
|
||||||
|
|
||||||
// Calculate teleop score.
|
// Calculate teleop score.
|
||||||
summary.RotorPoints = 60*score.AutoRotors + 40*score.Rotors
|
summary.OwnershipPoints = score.AutoOwnershipPoints + score.TeleopOwnershipPoints
|
||||||
summary.TakeoffPoints = 50 * score.Takeoffs
|
summary.VaultPoints = 5 * score.VaultCubes
|
||||||
summary.PressurePoints = (9*score.AutoFuelHigh + 3*score.AutoFuelLow + 3*score.FuelHigh + score.FuelLow) / 9
|
climbs := score.Climbs
|
||||||
|
if score.Levitate && score.Climbs < 3 {
|
||||||
|
climbs++
|
||||||
|
}
|
||||||
|
summary.ParkClimbPoints = 5*score.Parks + 30*climbs
|
||||||
|
|
||||||
// Calculate bonuses.
|
// Calculate bonuses.
|
||||||
if summary.PressurePoints >= 40 {
|
if score.AutoRuns == 3 && score.AutoEndSwitchOwnership {
|
||||||
summary.PressureGoalReached = true
|
summary.AutoQuest = true
|
||||||
if matchType == "elimination" {
|
|
||||||
summary.BonusPoints += 20
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if score.AutoRotors+score.Rotors == 4 {
|
if climbs == 3 {
|
||||||
summary.RotorGoalReached = true
|
summary.FaceTheBoss = true
|
||||||
if matchType == "elimination" {
|
|
||||||
summary.BonusPoints += 100
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate penalty points.
|
// Calculate penalty points.
|
||||||
@@ -69,17 +65,18 @@ func (score *Score) Summarize(opponentFouls []Foul, matchType string) *ScoreSumm
|
|||||||
summary.FoulPoints += foul.PointValue()
|
summary.FoulPoints += foul.PointValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
summary.Score = summary.AutoMobilityPoints + summary.RotorPoints + summary.TakeoffPoints + summary.PressurePoints +
|
summary.Score = summary.AutoRunPoints + summary.OwnershipPoints + summary.VaultPoints + summary.ParkClimbPoints +
|
||||||
summary.BonusPoints + summary.FoulPoints
|
summary.FoulPoints
|
||||||
|
|
||||||
return summary
|
return summary
|
||||||
}
|
}
|
||||||
|
|
||||||
func (score *Score) Equals(other *Score) bool {
|
func (score *Score) Equals(other *Score) bool {
|
||||||
if score.AutoMobility != other.AutoMobility || score.AutoRotors != other.AutoRotors ||
|
if score.AutoRuns != other.AutoRuns || score.AutoEndSwitchOwnership != other.AutoEndSwitchOwnership ||
|
||||||
score.AutoFuelLow != other.AutoFuelLow || score.AutoFuelHigh != other.AutoFuelHigh ||
|
score.AutoOwnershipPoints != other.AutoOwnershipPoints ||
|
||||||
score.Rotors != other.Rotors || score.FuelLow != other.FuelLow || score.FuelHigh != other.FuelHigh ||
|
score.TeleopOwnershipPoints != other.TeleopOwnershipPoints || score.VaultCubes != other.VaultCubes ||
|
||||||
score.Takeoffs != other.Takeoffs || score.ElimDq != other.ElimDq || len(score.Fouls) != len(other.Fouls) {
|
score.Levitate != other.Levitate || score.Parks != other.Parks || score.Climbs != other.Climbs ||
|
||||||
|
score.ElimDq != other.ElimDq || len(score.Fouls) != len(other.Fouls) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,64 +12,50 @@ func TestScoreSummary(t *testing.T) {
|
|||||||
redScore := TestScore1()
|
redScore := TestScore1()
|
||||||
blueScore := TestScore2()
|
blueScore := TestScore2()
|
||||||
|
|
||||||
redSummary := redScore.Summarize(blueScore.Fouls, "qualification")
|
redSummary := redScore.Summarize(blueScore.Fouls)
|
||||||
assert.Equal(t, 0, redSummary.AutoMobilityPoints)
|
assert.Equal(t, 5, redSummary.AutoRunPoints)
|
||||||
assert.Equal(t, 80, redSummary.AutoPoints)
|
assert.Equal(t, 17, redSummary.AutoPoints)
|
||||||
assert.Equal(t, 100, redSummary.RotorPoints)
|
assert.Equal(t, 59, redSummary.OwnershipPoints)
|
||||||
assert.Equal(t, 50, redSummary.TakeoffPoints)
|
assert.Equal(t, 15, redSummary.VaultPoints)
|
||||||
assert.Equal(t, 40, redSummary.PressurePoints)
|
assert.Equal(t, 90, redSummary.ParkClimbPoints)
|
||||||
assert.Equal(t, 0, redSummary.BonusPoints)
|
|
||||||
assert.Equal(t, 0, redSummary.FoulPoints)
|
assert.Equal(t, 0, redSummary.FoulPoints)
|
||||||
assert.Equal(t, 190, redSummary.Score)
|
assert.Equal(t, 169, redSummary.Score)
|
||||||
assert.Equal(t, true, redSummary.PressureGoalReached)
|
assert.Equal(t, false, redSummary.AutoQuest)
|
||||||
assert.Equal(t, false, redSummary.RotorGoalReached)
|
assert.Equal(t, true, redSummary.FaceTheBoss)
|
||||||
|
|
||||||
blueSummary := blueScore.Summarize(redScore.Fouls, "qualification")
|
blueSummary := blueScore.Summarize(redScore.Fouls)
|
||||||
assert.Equal(t, 10, blueSummary.AutoMobilityPoints)
|
assert.Equal(t, 15, blueSummary.AutoRunPoints)
|
||||||
assert.Equal(t, 133, blueSummary.AutoPoints)
|
assert.Equal(t, 35, blueSummary.AutoPoints)
|
||||||
assert.Equal(t, 200, blueSummary.RotorPoints)
|
assert.Equal(t, 93, blueSummary.OwnershipPoints)
|
||||||
assert.Equal(t, 150, blueSummary.TakeoffPoints)
|
assert.Equal(t, 30, blueSummary.VaultPoints)
|
||||||
assert.Equal(t, 18, blueSummary.PressurePoints)
|
assert.Equal(t, 35, blueSummary.ParkClimbPoints)
|
||||||
assert.Equal(t, 0, blueSummary.BonusPoints)
|
|
||||||
assert.Equal(t, 55, blueSummary.FoulPoints)
|
assert.Equal(t, 55, blueSummary.FoulPoints)
|
||||||
assert.Equal(t, 433, blueSummary.Score)
|
assert.Equal(t, 228, blueSummary.Score)
|
||||||
assert.Equal(t, false, blueSummary.PressureGoalReached)
|
assert.Equal(t, true, blueSummary.AutoQuest)
|
||||||
assert.Equal(t, true, blueSummary.RotorGoalReached)
|
assert.Equal(t, false, blueSummary.FaceTheBoss)
|
||||||
|
|
||||||
// Test pressure boundary conditions.
|
// Test Auto Quest boundary conditions.
|
||||||
redScore.AutoFuelHigh = 19
|
blueScore.AutoEndSwitchOwnership = false
|
||||||
assert.Equal(t, false, redScore.Summarize(blueScore.Fouls, "qualification").PressureGoalReached)
|
assert.Equal(t, false, blueScore.Summarize(redScore.Fouls).AutoQuest)
|
||||||
redScore.FuelLow = 18
|
blueScore.AutoEndSwitchOwnership = true
|
||||||
assert.Equal(t, true, redScore.Summarize(blueScore.Fouls, "qualification").PressureGoalReached)
|
blueScore.AutoRuns = 2
|
||||||
redScore.AutoFuelLow = 1
|
assert.Equal(t, false, blueScore.Summarize(redScore.Fouls).AutoQuest)
|
||||||
assert.Equal(t, false, redScore.Summarize(blueScore.Fouls, "qualification").PressureGoalReached)
|
|
||||||
redScore.FuelHigh = 56
|
|
||||||
assert.Equal(t, true, redScore.Summarize(blueScore.Fouls, "qualification").PressureGoalReached)
|
|
||||||
|
|
||||||
// Test rotor boundary conditions.
|
// Test Face the Boss boundary conditions.
|
||||||
blueScore.AutoRotors = 1
|
redScore.Levitate = false
|
||||||
assert.Equal(t, false, blueScore.Summarize(blueScore.Fouls, "qualification").RotorGoalReached)
|
assert.Equal(t, false, redScore.Summarize(blueScore.Fouls).FaceTheBoss)
|
||||||
blueScore.Rotors = 3
|
redScore.Climbs = 3
|
||||||
assert.Equal(t, true, blueScore.Summarize(blueScore.Fouls, "qualification").RotorGoalReached)
|
assert.Equal(t, true, redScore.Summarize(blueScore.Fouls).FaceTheBoss)
|
||||||
|
redScore.Climbs = 1
|
||||||
// Test elimination bonus.
|
redScore.Parks = 2
|
||||||
redSummary = redScore.Summarize(blueScore.Fouls, "elimination")
|
assert.Equal(t, false, redScore.Summarize(blueScore.Fouls).FaceTheBoss)
|
||||||
blueSummary = blueScore.Summarize(redScore.Fouls, "elimination")
|
|
||||||
assert.Equal(t, 20, redSummary.BonusPoints)
|
|
||||||
assert.Equal(t, 210, redSummary.Score)
|
|
||||||
assert.Equal(t, 100, blueSummary.BonusPoints)
|
|
||||||
assert.Equal(t, 513, blueSummary.Score)
|
|
||||||
redScore.Rotors = 3
|
|
||||||
redSummary = redScore.Summarize(blueScore.Fouls, "elimination")
|
|
||||||
assert.Equal(t, 120, redSummary.BonusPoints)
|
|
||||||
assert.Equal(t, 0, redScore.Summarize(blueScore.Fouls, "qualification").BonusPoints)
|
|
||||||
assert.Equal(t, 0, blueScore.Summarize(blueScore.Fouls, "qualification").BonusPoints)
|
|
||||||
|
|
||||||
// Test elimination disqualification.
|
// Test elimination disqualification.
|
||||||
redScore.ElimDq = true
|
redScore.ElimDq = true
|
||||||
|
assert.Equal(t, 0, redScore.Summarize(blueScore.Fouls).Score)
|
||||||
|
assert.NotEqual(t, 0, blueScore.Summarize(blueScore.Fouls).Score)
|
||||||
blueScore.ElimDq = true
|
blueScore.ElimDq = true
|
||||||
assert.Equal(t, 0, redScore.Summarize(blueScore.Fouls, "elimination").Score)
|
assert.Equal(t, 0, blueScore.Summarize(redScore.Fouls).Score)
|
||||||
assert.Equal(t, 0, blueScore.Summarize(redScore.Fouls, "elimination").Score)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestScoreEquals(t *testing.T) {
|
func TestScoreEquals(t *testing.T) {
|
||||||
@@ -82,42 +68,42 @@ func TestScoreEquals(t *testing.T) {
|
|||||||
assert.False(t, score1.Equals(score3))
|
assert.False(t, score1.Equals(score3))
|
||||||
assert.False(t, score3.Equals(score1))
|
assert.False(t, score3.Equals(score1))
|
||||||
|
|
||||||
score2.AutoMobility += 1
|
score2.AutoRuns += 1
|
||||||
assert.False(t, score1.Equals(score2))
|
assert.False(t, score1.Equals(score2))
|
||||||
assert.False(t, score2.Equals(score1))
|
assert.False(t, score2.Equals(score1))
|
||||||
|
|
||||||
score2 = TestScore1()
|
score2 = TestScore1()
|
||||||
score2.AutoRotors += 1
|
score2.AutoEndSwitchOwnership = !score2.AutoEndSwitchOwnership
|
||||||
assert.False(t, score1.Equals(score2))
|
assert.False(t, score1.Equals(score2))
|
||||||
assert.False(t, score2.Equals(score1))
|
assert.False(t, score2.Equals(score1))
|
||||||
|
|
||||||
score2 = TestScore1()
|
score2 = TestScore1()
|
||||||
score2.AutoFuelLow += 1
|
score2.AutoOwnershipPoints += 1
|
||||||
assert.False(t, score1.Equals(score2))
|
assert.False(t, score1.Equals(score2))
|
||||||
assert.False(t, score2.Equals(score1))
|
assert.False(t, score2.Equals(score1))
|
||||||
|
|
||||||
score2 = TestScore1()
|
score2 = TestScore1()
|
||||||
score2.AutoFuelHigh += 1
|
score2.TeleopOwnershipPoints += 1
|
||||||
assert.False(t, score1.Equals(score2))
|
assert.False(t, score1.Equals(score2))
|
||||||
assert.False(t, score2.Equals(score1))
|
assert.False(t, score2.Equals(score1))
|
||||||
|
|
||||||
score2 = TestScore1()
|
score2 = TestScore1()
|
||||||
score2.Rotors += 1
|
score2.VaultCubes += 1
|
||||||
assert.False(t, score1.Equals(score2))
|
assert.False(t, score1.Equals(score2))
|
||||||
assert.False(t, score2.Equals(score1))
|
assert.False(t, score2.Equals(score1))
|
||||||
|
|
||||||
score2 = TestScore1()
|
score2 = TestScore1()
|
||||||
score2.FuelLow += 1
|
score2.Levitate = !score2.Levitate
|
||||||
assert.False(t, score1.Equals(score2))
|
assert.False(t, score1.Equals(score2))
|
||||||
assert.False(t, score2.Equals(score1))
|
assert.False(t, score2.Equals(score1))
|
||||||
|
|
||||||
score2 = TestScore1()
|
score2 = TestScore1()
|
||||||
score2.FuelHigh += 1
|
score2.Parks += 1
|
||||||
assert.False(t, score1.Equals(score2))
|
assert.False(t, score1.Equals(score2))
|
||||||
assert.False(t, score2.Equals(score1))
|
assert.False(t, score2.Equals(score1))
|
||||||
|
|
||||||
score2 = TestScore1()
|
score2 = TestScore1()
|
||||||
score2.Takeoffs += 1
|
score2.Climbs += 1
|
||||||
assert.False(t, score1.Equals(score2))
|
assert.False(t, score1.Equals(score2))
|
||||||
assert.False(t, score2.Equals(score1))
|
assert.False(t, score2.Equals(score1))
|
||||||
|
|
||||||
|
|||||||
150
game/seesaw.go
Normal file
150
game/seesaw.go
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
// Copyright 2018 Team 254. All Rights Reserved.
|
||||||
|
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||||
|
//
|
||||||
|
// Scoring logic for the 2018 scale and switch elements.
|
||||||
|
|
||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
neitherAlliance = iota
|
||||||
|
redAlliance
|
||||||
|
blueAlliance
|
||||||
|
)
|
||||||
|
|
||||||
|
type Seesaw struct {
|
||||||
|
kind int
|
||||||
|
nearIsRed bool
|
||||||
|
ownerships []*Ownership
|
||||||
|
}
|
||||||
|
|
||||||
|
type Ownership struct {
|
||||||
|
seesaw *Seesaw
|
||||||
|
ownedBy int
|
||||||
|
startTime time.Time
|
||||||
|
endTime *time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets which side of the scale or switch belongs to which alliance. A value of true indicates that the side nearest the
|
||||||
|
// scoring table is red.
|
||||||
|
func (seesaw *Seesaw) SetRandomization(nearIsRed bool) {
|
||||||
|
seesaw.nearIsRed = nearIsRed
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates the internal timing state of the scale or switch given the current state of the sensors.
|
||||||
|
func (seesaw *Seesaw) UpdateState(state [2]bool, currentTime time.Time) {
|
||||||
|
ownedBy := neitherAlliance
|
||||||
|
|
||||||
|
// Check if there is an active force power up for this seesaw.
|
||||||
|
currentPowerUp := getActivePowerUp(currentTime)
|
||||||
|
if currentPowerUp != nil && currentPowerUp.kind == force &&
|
||||||
|
(seesaw.kind == neitherAlliance && currentPowerUp.level >= 2 ||
|
||||||
|
(seesaw.kind == currentPowerUp.alliance && (currentPowerUp.level == 1 || currentPowerUp.level == 3))) {
|
||||||
|
ownedBy = currentPowerUp.alliance
|
||||||
|
} else {
|
||||||
|
// Determine current ownership from sensor state.
|
||||||
|
if state[0] && !state[1] && seesaw.nearIsRed || state[1] && !state[0] && !seesaw.nearIsRed {
|
||||||
|
ownedBy = redAlliance
|
||||||
|
} else if state[0] && !state[1] && !seesaw.nearIsRed || state[1] && !state[0] && seesaw.nearIsRed {
|
||||||
|
ownedBy = blueAlliance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update data if ownership has changed since last cycle.
|
||||||
|
currentOwnership := seesaw.getCurrentOwnership()
|
||||||
|
if currentOwnership != nil && ownedBy != currentOwnership.ownedBy ||
|
||||||
|
currentOwnership == nil && ownedBy != neitherAlliance {
|
||||||
|
if currentOwnership != nil {
|
||||||
|
currentOwnership.endTime = ¤tTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if ownedBy != neitherAlliance {
|
||||||
|
newOwnership := &Ownership{seesaw: seesaw, ownedBy: ownedBy, startTime: currentTime}
|
||||||
|
seesaw.ownerships = append(seesaw.ownerships, newOwnership)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the auto and teleop period scores for the red alliance.
|
||||||
|
func (seesaw *Seesaw) GetRedSeconds(startTime, endTime time.Time) float64 {
|
||||||
|
return seesaw.getAllianceSeconds(redAlliance, startTime, endTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the auto and teleop period scores for the blue alliance.
|
||||||
|
func (seesaw *Seesaw) GetBlueSeconds(startTime, endTime time.Time) float64 {
|
||||||
|
return seesaw.getAllianceSeconds(blueAlliance, startTime, endTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (seesaw *Seesaw) getCurrentOwnership() *Ownership {
|
||||||
|
if len(seesaw.ownerships) > 0 {
|
||||||
|
lastOwnership := seesaw.ownerships[len(seesaw.ownerships)-1]
|
||||||
|
if lastOwnership.endTime == nil {
|
||||||
|
return lastOwnership
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (seesaw *Seesaw) getAllianceSeconds(ownedBy int, startTime, endTime time.Time) float64 {
|
||||||
|
var seconds float64
|
||||||
|
for _, ownership := range seesaw.ownerships {
|
||||||
|
if ownership.ownedBy == ownedBy {
|
||||||
|
seconds += ownership.getSeconds(startTime, endTime, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the scoring value for the ownership period, whether it is past or current.
|
||||||
|
func (ownership *Ownership) getSeconds(startTime, endTime time.Time, ignoreBoost bool) float64 {
|
||||||
|
var ownershipStartTime, ownershipEndTime time.Time
|
||||||
|
if ownership.startTime.Before(startTime) {
|
||||||
|
ownershipStartTime = startTime
|
||||||
|
} else {
|
||||||
|
ownershipStartTime = ownership.startTime
|
||||||
|
}
|
||||||
|
if ownership.endTime == nil || ownership.endTime.After(endTime) {
|
||||||
|
ownershipEndTime = endTime
|
||||||
|
} else {
|
||||||
|
ownershipEndTime = *ownership.endTime
|
||||||
|
}
|
||||||
|
|
||||||
|
if ownershipStartTime.After(ownershipEndTime) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
ownershipSeconds := ownershipEndTime.Sub(ownershipStartTime).Seconds()
|
||||||
|
|
||||||
|
// Find the boost power up applicable to this seesaw and alliance, if it exists.
|
||||||
|
var boostPowerUp *PowerUp
|
||||||
|
for _, powerUp := range powerUpUses {
|
||||||
|
if powerUp.kind == boost && ownership.ownedBy == powerUp.alliance {
|
||||||
|
if ownership.seesaw.kind == neitherAlliance && powerUp.level >= 2 ||
|
||||||
|
ownership.seesaw.kind != neitherAlliance && (powerUp.level == 1 || powerUp.level == 3) {
|
||||||
|
boostPowerUp = powerUp
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if boostPowerUp != nil {
|
||||||
|
// Adjust for the boost.
|
||||||
|
var boostStartTime, boostEndTime time.Time
|
||||||
|
if boostPowerUp.startTime.Before(ownershipStartTime) {
|
||||||
|
boostStartTime = ownershipStartTime
|
||||||
|
} else {
|
||||||
|
boostStartTime = boostPowerUp.startTime
|
||||||
|
}
|
||||||
|
if boostPowerUp.getEndTime().After(ownershipEndTime) {
|
||||||
|
boostEndTime = ownershipEndTime
|
||||||
|
} else {
|
||||||
|
boostEndTime = boostPowerUp.getEndTime()
|
||||||
|
}
|
||||||
|
if boostEndTime.After(boostStartTime) {
|
||||||
|
ownershipSeconds += boostEndTime.Sub(boostStartTime).Seconds()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ownershipSeconds
|
||||||
|
}
|
||||||
170
game/seesaw_test.go
Normal file
170
game/seesaw_test.go
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
// Copyright 2018 Team 254. All Rights Reserved.
|
||||||
|
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||||
|
|
||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestOwnership(t *testing.T) {
|
||||||
|
ownership := Ownership{nil, redAlliance, timeAfterStart(1), nil}
|
||||||
|
assert.Equal(t, 0, ownership.getSeconds(timeAfterStart(0), timeAfterStart(0), true))
|
||||||
|
assert.Equal(t, 0.5, ownership.getSeconds(timeAfterStart(0), timeAfterStart(1.5), true))
|
||||||
|
assert.Equal(t, 8.75, ownership.getSeconds(timeAfterStart(0), timeAfterStart(9.75), true))
|
||||||
|
|
||||||
|
// Check with truncated start.
|
||||||
|
assert.Equal(t, 2.5, ownership.getSeconds(timeAfterStart(1.5), timeAfterStart(4), true))
|
||||||
|
assert.Equal(t, 5, ownership.getSeconds(timeAfterStart(5), timeAfterStart(10), true))
|
||||||
|
|
||||||
|
// Check with end time.
|
||||||
|
endTime := timeAfterStart(13.5)
|
||||||
|
ownership.endTime = &endTime
|
||||||
|
assert.Equal(t, 12.5, ownership.getSeconds(timeAfterStart(0), timeAfterStart(15), true))
|
||||||
|
assert.Equal(t, 4, ownership.getSeconds(timeAfterStart(9.5), timeAfterStart(20), true))
|
||||||
|
|
||||||
|
// Check invalid/corner cases.
|
||||||
|
assert.Equal(t, 0, ownership.getSeconds(timeAfterStart(2), timeAfterStart(1), true))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSecondCounting(t *testing.T) {
|
||||||
|
ResetPowerUps()
|
||||||
|
|
||||||
|
redSwitch := &Seesaw{kind: redAlliance}
|
||||||
|
redSwitch.SetRandomization(true)
|
||||||
|
|
||||||
|
// Test that there is no accumulation before the start of the match.
|
||||||
|
redSwitch.UpdateState([2]bool{true, false}, timeAfterStart(-20))
|
||||||
|
redSwitch.UpdateState([2]bool{false, false}, timeAfterStart(-12))
|
||||||
|
redSwitch.UpdateState([2]bool{false, true}, timeAfterStart(-9))
|
||||||
|
redSwitch.UpdateState([2]bool{false, false}, timeAfterStart(-3))
|
||||||
|
assert.Equal(t, 0, redSwitch.GetRedSeconds(timeAfterStart(0), timeAfterStart(0)))
|
||||||
|
assert.Equal(t, 0, redSwitch.GetBlueSeconds(timeAfterStart(0), timeAfterStart(0)))
|
||||||
|
|
||||||
|
// Test autonomous.
|
||||||
|
redSwitch.UpdateState([2]bool{true, false}, timeAfterStart(1))
|
||||||
|
assert.Equal(t, 1, redSwitch.GetRedSeconds(timeAfterStart(0), timeAfterStart(2)))
|
||||||
|
assert.Equal(t, 5.5, redSwitch.GetRedSeconds(timeAfterStart(0), timeAfterStart(6.5)))
|
||||||
|
redSwitch.UpdateState([2]bool{false, false}, timeAfterStart(8.1))
|
||||||
|
assert.Equal(t, 7.1, redSwitch.GetRedSeconds(timeAfterStart(0), timeAfterStart(8.5)))
|
||||||
|
assert.Equal(t, 7.1, redSwitch.GetRedSeconds(timeAfterStart(0), timeAfterStart(10)))
|
||||||
|
redSwitch.UpdateState([2]bool{false, true}, timeAfterStart(10))
|
||||||
|
assert.Equal(t, 7.1, redSwitch.GetRedSeconds(timeAfterStart(0), timeAfterStart(13)))
|
||||||
|
redSwitch.UpdateState([2]bool{false, false}, timeAfterStart(13.5))
|
||||||
|
redSwitch.UpdateState([2]bool{true, false}, timeAfterStart(13.9))
|
||||||
|
assert.Equal(t, 8.2, redSwitch.GetRedSeconds(timeAfterStart(0), timeAfterStart(15)))
|
||||||
|
|
||||||
|
// Test teleop.
|
||||||
|
assert.Equal(t, 3, redSwitch.GetRedSeconds(timeAfterStart(17), timeAfterStart(20)))
|
||||||
|
redSwitch.UpdateState([2]bool{false, false}, timeAfterStart(30.8))
|
||||||
|
assert.Equal(t, 13.8, redSwitch.GetRedSeconds(timeAfterStart(17), timeAfterStart(34)))
|
||||||
|
redSwitch.UpdateState([2]bool{false, true}, timeAfterStart(35))
|
||||||
|
assert.Equal(t, 13.8, redSwitch.GetRedSeconds(timeAfterStart(17), timeAfterEnd(-10)))
|
||||||
|
redSwitch.UpdateState([2]bool{true, false}, timeAfterEnd(-5.1))
|
||||||
|
assert.Equal(t, 18.9, redSwitch.GetRedSeconds(timeAfterStart(17), timeAfterEnd(0)))
|
||||||
|
assert.Equal(t, 111.9, redSwitch.GetBlueSeconds(timeAfterStart(17), timeAfterEnd(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestForce(t *testing.T) {
|
||||||
|
ResetPowerUps()
|
||||||
|
|
||||||
|
blueSwitch := &Seesaw{kind: blueAlliance}
|
||||||
|
blueSwitch.SetRandomization(true)
|
||||||
|
scale := &Seesaw{kind: neitherAlliance}
|
||||||
|
scale.SetRandomization(true)
|
||||||
|
|
||||||
|
// Force switch only.
|
||||||
|
blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(0))
|
||||||
|
scale.UpdateState([2]bool{true, false}, timeAfterStart(0))
|
||||||
|
powerUp := &PowerUp{alliance: blueAlliance, kind: force, level: 1}
|
||||||
|
maybeActivatePowerUp(powerUp, timeAfterStart(2.5))
|
||||||
|
blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(2.5))
|
||||||
|
scale.UpdateState([2]bool{true, false}, timeAfterStart(2.5))
|
||||||
|
assert.Equal(t, 2.5, blueSwitch.GetBlueSeconds(timeAfterStart(0), timeAfterStart(5)))
|
||||||
|
assert.Equal(t, 0, scale.GetBlueSeconds(timeAfterStart(0), timeAfterStart(5)))
|
||||||
|
assert.Equal(t, 10, blueSwitch.GetBlueSeconds(timeAfterStart(0), timeAfterStart(12.5)))
|
||||||
|
assert.Equal(t, 0, scale.GetBlueSeconds(timeAfterStart(0), timeAfterStart(12.5)))
|
||||||
|
blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(12.5))
|
||||||
|
scale.UpdateState([2]bool{true, false}, timeAfterStart(12.5))
|
||||||
|
assert.Equal(t, 10, blueSwitch.GetBlueSeconds(timeAfterStart(0), timeAfterStart(15)))
|
||||||
|
assert.Equal(t, 0, scale.GetBlueSeconds(timeAfterStart(0), timeAfterStart(15)))
|
||||||
|
|
||||||
|
// Force scale only.
|
||||||
|
powerUp = &PowerUp{alliance: blueAlliance, kind: force, level: 2}
|
||||||
|
maybeActivatePowerUp(powerUp, timeAfterStart(20))
|
||||||
|
blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(20))
|
||||||
|
scale.UpdateState([2]bool{true, false}, timeAfterStart(20))
|
||||||
|
blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(30))
|
||||||
|
scale.UpdateState([2]bool{true, false}, timeAfterStart(30))
|
||||||
|
assert.Equal(t, 0, blueSwitch.GetBlueSeconds(timeAfterStart(20), timeAfterStart(40)))
|
||||||
|
assert.Equal(t, 10, scale.GetBlueSeconds(timeAfterStart(20), timeAfterStart(40)))
|
||||||
|
|
||||||
|
// Force both switch and scale.
|
||||||
|
powerUp = &PowerUp{alliance: blueAlliance, kind: force, level: 3}
|
||||||
|
maybeActivatePowerUp(powerUp, timeAfterStart(50))
|
||||||
|
blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(50))
|
||||||
|
scale.UpdateState([2]bool{true, false}, timeAfterStart(50))
|
||||||
|
blueSwitch.UpdateState([2]bool{true, false}, timeAfterStart(60))
|
||||||
|
scale.UpdateState([2]bool{true, false}, timeAfterStart(60))
|
||||||
|
assert.Equal(t, 10, blueSwitch.GetBlueSeconds(timeAfterStart(50), timeAfterStart(70)))
|
||||||
|
assert.Equal(t, 10, scale.GetBlueSeconds(timeAfterStart(50), timeAfterStart(70)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBoost(t *testing.T) {
|
||||||
|
ResetPowerUps()
|
||||||
|
|
||||||
|
blueSwitch := &Seesaw{kind: blueAlliance}
|
||||||
|
blueSwitch.SetRandomization(true)
|
||||||
|
scale := &Seesaw{kind: neitherAlliance}
|
||||||
|
scale.SetRandomization(false)
|
||||||
|
|
||||||
|
// Test within continuous ownership period.
|
||||||
|
blueSwitch.UpdateState([2]bool{false, true}, timeAfterStart(20))
|
||||||
|
scale.UpdateState([2]bool{true, false}, timeAfterStart(20))
|
||||||
|
powerUp := &PowerUp{alliance: blueAlliance, kind: boost, level: 2}
|
||||||
|
maybeActivatePowerUp(powerUp, timeAfterStart(25))
|
||||||
|
assert.Equal(t, 5, scale.GetBlueSeconds(timeAfterStart(0), timeAfterStart(25)))
|
||||||
|
assert.Equal(t, 6, scale.GetBlueSeconds(timeAfterStart(0), timeAfterStart(25.5)))
|
||||||
|
assert.Equal(t, 7.5, scale.GetBlueSeconds(timeAfterStart(0), timeAfterStart(26.25)))
|
||||||
|
assert.Equal(t, 15, scale.GetBlueSeconds(timeAfterStart(0), timeAfterStart(30)))
|
||||||
|
assert.Equal(t, 25, scale.GetBlueSeconds(timeAfterStart(0), timeAfterStart(35)))
|
||||||
|
assert.Equal(t, 30, scale.GetBlueSeconds(timeAfterStart(0), timeAfterStart(40)))
|
||||||
|
assert.Equal(t, 20, blueSwitch.GetBlueSeconds(timeAfterStart(0), timeAfterStart(40)))
|
||||||
|
|
||||||
|
// Test with no ownership at the start.
|
||||||
|
ResetPowerUps()
|
||||||
|
blueSwitch.UpdateState([2]bool{false, false}, timeAfterStart(44))
|
||||||
|
scale.UpdateState([2]bool{false, false}, timeAfterStart(44))
|
||||||
|
powerUp = &PowerUp{alliance: blueAlliance, kind: boost, level: 3}
|
||||||
|
maybeActivatePowerUp(powerUp, timeAfterStart(45))
|
||||||
|
assert.Equal(t, 0, blueSwitch.GetBlueSeconds(timeAfterStart(45), timeAfterStart(50)))
|
||||||
|
assert.Equal(t, 0, scale.GetBlueSeconds(timeAfterStart(45), timeAfterStart(50)))
|
||||||
|
blueSwitch.UpdateState([2]bool{false, true}, timeAfterStart(50))
|
||||||
|
scale.UpdateState([2]bool{true, false}, timeAfterStart(50))
|
||||||
|
assert.Equal(t, 10, blueSwitch.GetBlueSeconds(timeAfterStart(45), timeAfterStart(55)))
|
||||||
|
assert.Equal(t, 15, blueSwitch.GetBlueSeconds(timeAfterStart(45), timeAfterStart(60)))
|
||||||
|
assert.Equal(t, 10, scale.GetBlueSeconds(timeAfterStart(45), timeAfterStart(55)))
|
||||||
|
assert.Equal(t, 15, scale.GetBlueSeconds(timeAfterStart(45), timeAfterStart(60)))
|
||||||
|
|
||||||
|
// Test with interrupted ownership.
|
||||||
|
ResetPowerUps()
|
||||||
|
scale.UpdateState([2]bool{false, true}, timeAfterStart(65))
|
||||||
|
assert.Equal(t, 5, scale.GetRedSeconds(timeAfterStart(65), timeAfterStart(70)))
|
||||||
|
powerUp = &PowerUp{alliance: redAlliance, kind: boost, level: 2}
|
||||||
|
maybeActivatePowerUp(powerUp, timeAfterStart(70))
|
||||||
|
scale.UpdateState([2]bool{false, false}, timeAfterStart(72.5))
|
||||||
|
assert.Equal(t, 10, scale.GetRedSeconds(timeAfterStart(65), timeAfterStart(72.5)))
|
||||||
|
assert.Equal(t, 10, scale.GetRedSeconds(timeAfterStart(65), timeAfterStart(77.5)))
|
||||||
|
scale.UpdateState([2]bool{false, true}, timeAfterStart(77.5))
|
||||||
|
assert.Equal(t, 15, scale.GetRedSeconds(timeAfterStart(65), timeAfterStart(80)))
|
||||||
|
assert.Equal(t, 20, scale.GetRedSeconds(timeAfterStart(65), timeAfterStart(85)))
|
||||||
|
|
||||||
|
// Test with just the switch.
|
||||||
|
blueSwitch.UpdateState([2]bool{false, true}, timeAfterStart(100))
|
||||||
|
scale.UpdateState([2]bool{true, false}, timeAfterStart(100))
|
||||||
|
powerUp = &PowerUp{alliance: blueAlliance, kind: boost, level: 1}
|
||||||
|
maybeActivatePowerUp(powerUp, timeAfterStart(100))
|
||||||
|
assert.Equal(t, 20, blueSwitch.GetBlueSeconds(timeAfterStart(100), timeAfterStart(110)))
|
||||||
|
assert.Equal(t, 10, scale.GetBlueSeconds(timeAfterStart(100), timeAfterStart(110)))
|
||||||
|
}
|
||||||
@@ -8,17 +8,17 @@ package game
|
|||||||
func TestScore1() *Score {
|
func TestScore1() *Score {
|
||||||
fouls := []Foul{{Rule{"G22", false}, 25, 25.2}, {Rule{"G18", true}, 25, 150},
|
fouls := []Foul{{Rule{"G22", false}, 25, 25.2}, {Rule{"G18", true}, 25, 150},
|
||||||
{Rule{"G20", true}, 1868, 0}}
|
{Rule{"G20", true}, 1868, 0}}
|
||||||
return &Score{0, 1, 2, 20, 1, 12, 55, 1, fouls, false}
|
return &Score{1, true, 12, 47, 3, true, 0, 2, fouls, false}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestScore2() *Score {
|
func TestScore2() *Score {
|
||||||
return &Score{2, 2, 10, 0, 2, 65, 24, 3, []Foul{}, false}
|
return &Score{3, true, 20, 73, 6, false, 1, 1, []Foul{}, false}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRanking1() *Ranking {
|
func TestRanking1() *Ranking {
|
||||||
return &Ranking{254, 1, RankingFields{20, 625, 90, 554, 10, 50, 0.254, 3, 2, 1, 0, 10}}
|
return &Ranking{254, 1, RankingFields{20, 625, 90, 554, 10, 0.254, 3, 2, 1, 0, 10}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRanking2() *Ranking {
|
func TestRanking2() *Ranking {
|
||||||
return &Ranking{1114, 2, RankingFields{18, 700, 625, 90, 554, 9, 0.1114, 1, 3, 2, 0, 10}}
|
return &Ranking{1114, 2, RankingFields{18, 700, 625, 90, 554, 0.1114, 1, 3, 2, 0, 10}}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
// Copyright 2017 Team 254. All Rights Reserved.
|
|
||||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
|
||||||
//
|
|
||||||
// Scoring logic for the 2017 touchpad element.
|
|
||||||
|
|
||||||
package game
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
NotTriggered = iota
|
|
||||||
Triggered
|
|
||||||
Held
|
|
||||||
)
|
|
||||||
|
|
||||||
type Touchpad struct {
|
|
||||||
lastTriggered bool
|
|
||||||
triggeredTime *time.Time
|
|
||||||
untriggeredTime *time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updates the internal timing state of the touchpad given the current state of the sensor.
|
|
||||||
func (touchpad *Touchpad) UpdateState(triggered bool, matchStartTime, currentTime time.Time) {
|
|
||||||
matchEndTime := GetMatchEndTime(matchStartTime)
|
|
||||||
|
|
||||||
if triggered && !touchpad.lastTriggered && currentTime.Before(matchEndTime) {
|
|
||||||
touchpad.triggeredTime = ¤tTime
|
|
||||||
touchpad.untriggeredTime = nil
|
|
||||||
} else if !triggered && touchpad.lastTriggered {
|
|
||||||
if currentTime.Before(matchEndTime) || touchpad.GetState(currentTime) == Triggered {
|
|
||||||
touchpad.triggeredTime = nil
|
|
||||||
}
|
|
||||||
touchpad.untriggeredTime = ¤tTime
|
|
||||||
}
|
|
||||||
touchpad.lastTriggered = triggered
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determines the scoring status of the touchpad. Returns 0 if not triggered, 1 if triggered but not yet for a full
|
|
||||||
// second, and 2 if triggered and counting for points.
|
|
||||||
func (touchpad *Touchpad) GetState(currentTime time.Time) int {
|
|
||||||
if touchpad.triggeredTime != nil {
|
|
||||||
if touchpad.untriggeredTime != nil {
|
|
||||||
if touchpad.untriggeredTime.Sub(*touchpad.triggeredTime) >= time.Second {
|
|
||||||
return Held
|
|
||||||
} else {
|
|
||||||
return NotTriggered
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if currentTime.Sub(*touchpad.triggeredTime) >= time.Second {
|
|
||||||
return Held
|
|
||||||
} else {
|
|
||||||
return Triggered
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NotTriggered
|
|
||||||
}
|
|
||||||
|
|
||||||
func CountTouchpads(touchpads *[3]Touchpad, currentTime time.Time) int {
|
|
||||||
count := 0
|
|
||||||
for _, touchpad := range touchpads {
|
|
||||||
if touchpad.GetState(currentTime) == Held {
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
// Copyright 2017 Team 254. All Rights Reserved.
|
|
||||||
// Author: pat@patfairbank.com (Patrick Fairbank)
|
|
||||||
|
|
||||||
package game
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNotTriggered(t *testing.T) {
|
|
||||||
touchpad := Touchpad{}
|
|
||||||
touchpad.UpdateState(false, matchStartTime, timeAfterEnd(-10))
|
|
||||||
|
|
||||||
touchpad.UpdateState(false, matchStartTime, timeAfterEnd(-1))
|
|
||||||
assert.Equal(t, NotTriggered, touchpad.GetState(timeAfterEnd(-1)))
|
|
||||||
assert.Equal(t, NotTriggered, touchpad.GetState(timeAfterEnd(2)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTriggeredReleasedEarly(t *testing.T) {
|
|
||||||
touchpad := Touchpad{}
|
|
||||||
touchpad.UpdateState(false, matchStartTime, timeAfterEnd(-10))
|
|
||||||
|
|
||||||
touchpad.UpdateState(true, matchStartTime, timeAfterEnd(-5))
|
|
||||||
assert.Equal(t, Triggered, touchpad.GetState(timeAfterEnd(-4.9)))
|
|
||||||
assert.Equal(t, Held, touchpad.GetState(timeAfterEnd(-3)))
|
|
||||||
touchpad.UpdateState(false, matchStartTime, timeAfterEnd(-1))
|
|
||||||
assert.Equal(t, NotTriggered, touchpad.GetState(timeAfterEnd(-1.1)))
|
|
||||||
assert.Equal(t, NotTriggered, touchpad.GetState(timeAfterEnd(2)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTriggeredTooShort(t *testing.T) {
|
|
||||||
touchpad := Touchpad{}
|
|
||||||
touchpad.UpdateState(false, matchStartTime, timeAfterEnd(-10))
|
|
||||||
|
|
||||||
touchpad.UpdateState(true, matchStartTime, timeAfterEnd(-0.5))
|
|
||||||
touchpad.UpdateState(true, matchStartTime, timeAfterEnd(0))
|
|
||||||
assert.Equal(t, Triggered, touchpad.GetState(timeAfterEnd(0.2)))
|
|
||||||
touchpad.UpdateState(false, matchStartTime, timeAfterEnd(0.4))
|
|
||||||
assert.Equal(t, NotTriggered, touchpad.GetState(timeAfterEnd(0.5)))
|
|
||||||
assert.Equal(t, NotTriggered, touchpad.GetState(timeAfterEnd(2)))
|
|
||||||
|
|
||||||
touchpad.UpdateState(true, matchStartTime, timeAfterEnd(3))
|
|
||||||
assert.Equal(t, NotTriggered, touchpad.GetState(timeAfterEnd(5)))
|
|
||||||
touchpad.UpdateState(false, matchStartTime, timeAfterEnd(6))
|
|
||||||
assert.Equal(t, NotTriggered, touchpad.GetState(timeAfterEnd(8)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTriggeredHeld(t *testing.T) {
|
|
||||||
touchpad := Touchpad{}
|
|
||||||
touchpad.UpdateState(false, matchStartTime, timeAfterEnd(-10))
|
|
||||||
|
|
||||||
touchpad.UpdateState(true, matchStartTime, timeAfterEnd(-5))
|
|
||||||
touchpad.UpdateState(true, matchStartTime, timeAfterEnd(-3))
|
|
||||||
touchpad.UpdateState(true, matchStartTime, timeAfterEnd(1))
|
|
||||||
assert.Equal(t, Held, touchpad.GetState(timeAfterEnd(2)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTriggeredReleased(t *testing.T) {
|
|
||||||
touchpad := Touchpad{}
|
|
||||||
touchpad.UpdateState(false, matchStartTime, timeAfterEnd(-10))
|
|
||||||
|
|
||||||
touchpad.UpdateState(true, matchStartTime, timeAfterEnd(-5))
|
|
||||||
touchpad.UpdateState(true, matchStartTime, timeAfterEnd(-3))
|
|
||||||
touchpad.UpdateState(true, matchStartTime, timeAfterEnd(1))
|
|
||||||
assert.Equal(t, Held, touchpad.GetState(timeAfterEnd(2)))
|
|
||||||
touchpad.UpdateState(false, matchStartTime, timeAfterEnd(3))
|
|
||||||
assert.Equal(t, Held, touchpad.GetState(timeAfterEnd(4)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReTriggered(t *testing.T) {
|
|
||||||
touchpad := Touchpad{}
|
|
||||||
touchpad.UpdateState(false, matchStartTime, timeAfterEnd(-10))
|
|
||||||
|
|
||||||
touchpad.UpdateState(true, matchStartTime, timeAfterEnd(-5))
|
|
||||||
assert.Equal(t, Held, touchpad.GetState(timeAfterEnd(-3)))
|
|
||||||
touchpad.UpdateState(false, matchStartTime, timeAfterEnd(-1))
|
|
||||||
assert.Equal(t, NotTriggered, touchpad.GetState(timeAfterEnd(-1.1)))
|
|
||||||
touchpad.UpdateState(true, matchStartTime, timeAfterEnd(-0.1))
|
|
||||||
assert.Equal(t, Triggered, touchpad.GetState(timeAfterEnd(0.1)))
|
|
||||||
assert.Equal(t, Held, touchpad.GetState(timeAfterEnd(2)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestTriggeredLate(t *testing.T) {
|
|
||||||
touchpad := Touchpad{}
|
|
||||||
touchpad.UpdateState(false, matchStartTime, timeAfterEnd(-10))
|
|
||||||
|
|
||||||
touchpad.UpdateState(true, matchStartTime, timeAfterEnd(0.1))
|
|
||||||
assert.Equal(t, NotTriggered, touchpad.GetState(timeAfterEnd(0.2)))
|
|
||||||
assert.Equal(t, NotTriggered, touchpad.GetState(timeAfterEnd(2)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCountTouchpads(t *testing.T) {
|
|
||||||
var touchpads [3]Touchpad
|
|
||||||
touchpads[0].UpdateState(true, matchStartTime, timeAfterEnd(-5))
|
|
||||||
touchpads[1].UpdateState(true, matchStartTime, timeAfterEnd(-2))
|
|
||||||
touchpads[2].UpdateState(true, matchStartTime, timeAfterEnd(-0.1))
|
|
||||||
|
|
||||||
assert.Equal(t, 0, CountTouchpads(&touchpads, timeAfterEnd(-6)))
|
|
||||||
assert.Equal(t, 0, CountTouchpads(&touchpads, timeAfterEnd(-5.5)))
|
|
||||||
assert.Equal(t, 1, CountTouchpads(&touchpads, timeAfterEnd(-3)))
|
|
||||||
assert.Equal(t, 1, CountTouchpads(&touchpads, timeAfterEnd(-1.5)))
|
|
||||||
assert.Equal(t, 2, CountTouchpads(&touchpads, timeAfterEnd(0)))
|
|
||||||
assert.Equal(t, 3, CountTouchpads(&touchpads, timeAfterEnd(1)))
|
|
||||||
}
|
|
||||||
62
game/vault.go
Normal file
62
game/vault.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
// Copyright 2018 Team 254. All Rights Reserved.
|
||||||
|
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||||
|
//
|
||||||
|
// Scoring logic for the 2018 vault element.
|
||||||
|
|
||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Vault struct {
|
||||||
|
alliance int
|
||||||
|
numForceCubes int
|
||||||
|
numLevitateCubes int
|
||||||
|
numBoostCubes int
|
||||||
|
LevitatePlayed bool
|
||||||
|
ForcePowerUp *PowerUp
|
||||||
|
BoostPowerUp *PowerUp
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates the state of the vault given the state of the individual power cube sensors.
|
||||||
|
func (vault *Vault) UpdateCubes(forceCubes, levitateCubes, boostCubes [3]bool) {
|
||||||
|
vault.numForceCubes = countCubes(forceCubes)
|
||||||
|
vault.numLevitateCubes = countCubes(levitateCubes)
|
||||||
|
vault.numBoostCubes = countCubes(boostCubes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates the state of the vault given the state of the power up buttons.
|
||||||
|
func (vault *Vault) UpdateButtons(forceButton, levitateButton, boostButton bool, currentTime time.Time) {
|
||||||
|
if levitateButton && vault.numLevitateCubes == 3 && !vault.LevitatePlayed {
|
||||||
|
vault.LevitatePlayed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if forceButton && vault.numForceCubes > 0 && vault.ForcePowerUp == nil {
|
||||||
|
vault.ForcePowerUp = maybeActivatePowerUp(&PowerUp{kind: force, alliance: vault.alliance,
|
||||||
|
level: vault.numForceCubes}, currentTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
if boostButton && vault.numBoostCubes > 0 && vault.BoostPowerUp == nil {
|
||||||
|
vault.BoostPowerUp = maybeActivatePowerUp(&PowerUp{kind: boost, alliance: vault.alliance,
|
||||||
|
level: vault.numBoostCubes}, currentTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the total count of power cubes that have been placed in the vault.
|
||||||
|
func (vault *Vault) GetNumCubes() int {
|
||||||
|
return vault.numForceCubes + vault.numLevitateCubes + vault.numBoostCubes
|
||||||
|
}
|
||||||
|
|
||||||
|
func countCubes(cubes [3]bool) int {
|
||||||
|
if cubes[0] {
|
||||||
|
if cubes[1] {
|
||||||
|
if cubes[2] {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
156
game/vault_test.go
Normal file
156
game/vault_test.go
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
// Copyright 2018 Team 254. All Rights Reserved.
|
||||||
|
// Author: pat@patfairbank.com (Patrick Fairbank)
|
||||||
|
|
||||||
|
package game
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestVaultNumCubes(t *testing.T) {
|
||||||
|
vault := Vault{}
|
||||||
|
assert.Equal(t, 0, vault.GetNumCubes())
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{true, false, false}, [3]bool{false, false, false}, [3]bool{false, false, false})
|
||||||
|
assert.Equal(t, 1, vault.GetNumCubes())
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{false, false, false}, [3]bool{true, false, true}, [3]bool{true, false, false})
|
||||||
|
assert.Equal(t, 2, vault.GetNumCubes())
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{false, true, true}, [3]bool{false, false, true}, [3]bool{true, true, false})
|
||||||
|
assert.Equal(t, 2, vault.GetNumCubes())
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{true, true, false}, [3]bool{true, true, false}, [3]bool{true, true, true})
|
||||||
|
assert.Equal(t, 7, vault.GetNumCubes())
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{true, true, true}, [3]bool{true, true, true}, [3]bool{true, true, true})
|
||||||
|
assert.Equal(t, 9, vault.GetNumCubes())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVaultLevitate(t *testing.T) {
|
||||||
|
vault := Vault{}
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{false, false, false}, [3]bool{false, false, false}, [3]bool{false, false, false})
|
||||||
|
vault.UpdateButtons(false, true, false, time.Now())
|
||||||
|
assert.False(t, vault.LevitatePlayed)
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{false, false, false}, [3]bool{true, false, false}, [3]bool{false, false, false})
|
||||||
|
vault.UpdateButtons(false, true, false, time.Now())
|
||||||
|
assert.False(t, vault.LevitatePlayed)
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{false, false, false}, [3]bool{true, true, false}, [3]bool{false, false, false})
|
||||||
|
vault.UpdateButtons(false, true, false, time.Now())
|
||||||
|
assert.False(t, vault.LevitatePlayed)
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{false, false, false}, [3]bool{true, true, true}, [3]bool{false, false, false})
|
||||||
|
vault.UpdateButtons(true, false, true, time.Now())
|
||||||
|
assert.False(t, vault.LevitatePlayed)
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{false, false, false}, [3]bool{true, true, true}, [3]bool{false, false, false})
|
||||||
|
vault.UpdateButtons(false, true, false, time.Now())
|
||||||
|
assert.True(t, vault.LevitatePlayed)
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{false, false, false}, [3]bool{true, true, true}, [3]bool{false, false, false})
|
||||||
|
vault.UpdateButtons(false, false, false, time.Now())
|
||||||
|
assert.True(t, vault.LevitatePlayed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVaultForce(t *testing.T) {
|
||||||
|
vault := Vault{alliance: blueAlliance}
|
||||||
|
ResetPowerUps()
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{false, false, false}, [3]bool{false, false, false}, [3]bool{false, false, false})
|
||||||
|
vault.UpdateButtons(true, false, false, time.Now())
|
||||||
|
assert.Nil(t, vault.ForcePowerUp)
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{true, true, true}, [3]bool{false, false, false}, [3]bool{false, false, false})
|
||||||
|
vault.UpdateButtons(false, true, true, time.Now())
|
||||||
|
assert.Nil(t, vault.ForcePowerUp)
|
||||||
|
|
||||||
|
// Activation with one cube.
|
||||||
|
vault.UpdateCubes([3]bool{true, false, false}, [3]bool{false, false, false}, [3]bool{false, false, false})
|
||||||
|
vault.UpdateButtons(true, false, false, time.Now())
|
||||||
|
if assert.NotNil(t, vault.ForcePowerUp) {
|
||||||
|
assert.Equal(t, blueAlliance, vault.ForcePowerUp.alliance)
|
||||||
|
assert.Equal(t, force, vault.ForcePowerUp.kind)
|
||||||
|
assert.Equal(t, 1, vault.ForcePowerUp.level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activation with two cubes.
|
||||||
|
vault = Vault{alliance: redAlliance}
|
||||||
|
ResetPowerUps()
|
||||||
|
vault.UpdateCubes([3]bool{true, true, false}, [3]bool{false, false, false}, [3]bool{false, false, false})
|
||||||
|
vault.UpdateButtons(true, false, false, time.Now())
|
||||||
|
if assert.NotNil(t, vault.ForcePowerUp) {
|
||||||
|
assert.Equal(t, redAlliance, vault.ForcePowerUp.alliance)
|
||||||
|
assert.Equal(t, force, vault.ForcePowerUp.kind)
|
||||||
|
assert.Equal(t, 2, vault.ForcePowerUp.level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activation with three cubes.
|
||||||
|
vault = Vault{alliance: blueAlliance}
|
||||||
|
ResetPowerUps()
|
||||||
|
vault.UpdateCubes([3]bool{true, true, true}, [3]bool{false, false, false}, [3]bool{false, false, false})
|
||||||
|
vault.UpdateButtons(true, false, false, time.Now())
|
||||||
|
assert.NotNil(t, vault.ForcePowerUp)
|
||||||
|
if assert.NotNil(t, vault.ForcePowerUp) {
|
||||||
|
assert.Equal(t, blueAlliance, vault.ForcePowerUp.alliance)
|
||||||
|
assert.Equal(t, force, vault.ForcePowerUp.kind)
|
||||||
|
assert.Equal(t, 3, vault.ForcePowerUp.level)
|
||||||
|
}
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{true, true, true}, [3]bool{false, false, false}, [3]bool{false, false, false})
|
||||||
|
vault.UpdateButtons(false, false, false, time.Now())
|
||||||
|
assert.NotNil(t, vault.ForcePowerUp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVaultBoost(t *testing.T) {
|
||||||
|
vault := Vault{alliance: blueAlliance}
|
||||||
|
ResetPowerUps()
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{false, false, false}, [3]bool{false, false, false}, [3]bool{false, false, false})
|
||||||
|
vault.UpdateButtons(false, false, true, time.Now())
|
||||||
|
assert.Nil(t, vault.BoostPowerUp)
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{false, false, false}, [3]bool{false, false, false}, [3]bool{true, true, true})
|
||||||
|
vault.UpdateButtons(true, true, false, time.Now())
|
||||||
|
assert.Nil(t, vault.BoostPowerUp)
|
||||||
|
|
||||||
|
// Activation with one cube.
|
||||||
|
vault.UpdateCubes([3]bool{false, false, false}, [3]bool{false, false, false}, [3]bool{true, false, false})
|
||||||
|
vault.UpdateButtons(false, false, true, time.Now())
|
||||||
|
if assert.NotNil(t, vault.BoostPowerUp) {
|
||||||
|
assert.Equal(t, blueAlliance, vault.BoostPowerUp.alliance)
|
||||||
|
assert.Equal(t, boost, vault.BoostPowerUp.kind)
|
||||||
|
assert.Equal(t, 1, vault.BoostPowerUp.level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activation with two cubes.
|
||||||
|
vault = Vault{alliance: redAlliance}
|
||||||
|
ResetPowerUps()
|
||||||
|
vault.UpdateCubes([3]bool{false, false, false}, [3]bool{false, false, false}, [3]bool{true, true, false})
|
||||||
|
vault.UpdateButtons(false, false, true, time.Now())
|
||||||
|
if assert.NotNil(t, vault.BoostPowerUp) {
|
||||||
|
assert.Equal(t, redAlliance, vault.BoostPowerUp.alliance)
|
||||||
|
assert.Equal(t, boost, vault.BoostPowerUp.kind)
|
||||||
|
assert.Equal(t, 2, vault.BoostPowerUp.level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activation with three cubes.
|
||||||
|
vault = Vault{alliance: blueAlliance}
|
||||||
|
ResetPowerUps()
|
||||||
|
vault.UpdateCubes([3]bool{false, false, false}, [3]bool{false, false, false}, [3]bool{true, true, true})
|
||||||
|
vault.UpdateButtons(false, false, true, time.Now())
|
||||||
|
assert.NotNil(t, vault.BoostPowerUp)
|
||||||
|
if assert.NotNil(t, vault.BoostPowerUp) {
|
||||||
|
assert.Equal(t, blueAlliance, vault.BoostPowerUp.alliance)
|
||||||
|
assert.Equal(t, boost, vault.BoostPowerUp.kind)
|
||||||
|
assert.Equal(t, 3, vault.BoostPowerUp.level)
|
||||||
|
}
|
||||||
|
|
||||||
|
vault.UpdateCubes([3]bool{false, false, false}, [3]bool{false, false, false}, [3]bool{true, true, true})
|
||||||
|
vault.UpdateButtons(false, false, false, time.Now())
|
||||||
|
assert.NotNil(t, vault.BoostPowerUp)
|
||||||
|
}
|
||||||
@@ -96,12 +96,12 @@ func (database *Database) TruncateMatchResults() error {
|
|||||||
|
|
||||||
// Calculates and returns the summary fields used for ranking and display for the red alliance.
|
// Calculates and returns the summary fields used for ranking and display for the red alliance.
|
||||||
func (matchResult *MatchResult) RedScoreSummary() *game.ScoreSummary {
|
func (matchResult *MatchResult) RedScoreSummary() *game.ScoreSummary {
|
||||||
return matchResult.RedScore.Summarize(matchResult.BlueScore.Fouls, matchResult.MatchType)
|
return matchResult.RedScore.Summarize(matchResult.BlueScore.Fouls)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculates and returns the summary fields used for ranking and display for the blue alliance.
|
// Calculates and returns the summary fields used for ranking and display for the blue alliance.
|
||||||
func (matchResult *MatchResult) BlueScoreSummary() *game.ScoreSummary {
|
func (matchResult *MatchResult) BlueScoreSummary() *game.ScoreSummary {
|
||||||
return matchResult.BlueScore.Summarize(matchResult.RedScore.Fouls, matchResult.MatchType)
|
return matchResult.BlueScore.Summarize(matchResult.RedScore.Fouls)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks the score for disqualifications or a tie and adjusts it appropriately.
|
// Checks the score for disqualifications or a tie and adjusts it appropriately.
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ func TestMatchResultCrud(t *testing.T) {
|
|||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, matchResult, matchResult2)
|
assert.Equal(t, matchResult, matchResult2)
|
||||||
|
|
||||||
matchResult.BlueScore.AutoMobility = 12
|
matchResult.BlueScore.AutoRuns = 12
|
||||||
db.SaveMatchResult(matchResult)
|
db.SaveMatchResult(matchResult)
|
||||||
matchResult2, err = db.GetMatchResultForMatch(254)
|
matchResult2, err = db.GetMatchResultForMatch(254)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|||||||
106
partner/tba.go
106
partner/tba.go
@@ -10,7 +10,6 @@ import (
|
|||||||
"crypto/md5"
|
"crypto/md5"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/Team254/cheesy-arena/game"
|
|
||||||
"github.com/Team254/cheesy-arena/model"
|
"github.com/Team254/cheesy-arena/model"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -71,14 +70,13 @@ type TbaRanking struct {
|
|||||||
TeamKey string `json:"team_key"`
|
TeamKey string `json:"team_key"`
|
||||||
Rank int `json:"rank"`
|
Rank int `json:"rank"`
|
||||||
RP float32 `json:"RP"`
|
RP float32 `json:"RP"`
|
||||||
Match int `json:"Match"`
|
ParkClimb int
|
||||||
Auto int `json:"Auto"`
|
Auto int
|
||||||
Rotor int `json:"Rotor"`
|
Ownership int
|
||||||
Touchpad int `json:"Touchpad"`
|
Vault int
|
||||||
Pressure int `json:"Pressure"`
|
WinLossTie string `json:"W-L-T"`
|
||||||
WinLossTie string `json:"W-L-T"`
|
Dqs int `json:"dqs"`
|
||||||
Dqs int `json:"dqs"`
|
Played int `json:"played"`
|
||||||
Played int `json:"played"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TbaRankings struct {
|
type TbaRankings struct {
|
||||||
@@ -301,8 +299,8 @@ func (client *TbaClient) PublishRankings(database *model.Database) error {
|
|||||||
tbaRankings := make([]TbaRanking, len(rankings))
|
tbaRankings := make([]TbaRanking, len(rankings))
|
||||||
for i, ranking := range rankings {
|
for i, ranking := range rankings {
|
||||||
tbaRankings[i] = TbaRanking{getTbaTeam(ranking.TeamId), ranking.Rank,
|
tbaRankings[i] = TbaRanking{getTbaTeam(ranking.TeamId), ranking.Rank,
|
||||||
float32(ranking.RankingPoints) / float32(ranking.Played), ranking.MatchPoints, ranking.AutoPoints,
|
float32(ranking.RankingPoints) / float32(ranking.Played), ranking.ParkClimbPoints, ranking.AutoPoints,
|
||||||
ranking.RotorPoints, ranking.TakeoffPoints, ranking.PressurePoints,
|
ranking.OwnershipPoints, ranking.VaultPoints,
|
||||||
fmt.Sprintf("%d-%d-%d", ranking.Wins, ranking.Losses, ranking.Ties), ranking.Disqualifications,
|
fmt.Sprintf("%d-%d-%d", ranking.Wins, ranking.Losses, ranking.Ties), ranking.Disqualifications,
|
||||||
ranking.Played}
|
ranking.Played}
|
||||||
}
|
}
|
||||||
@@ -427,49 +425,51 @@ func (client *TbaClient) postRequest(resource string, action string, body []byte
|
|||||||
|
|
||||||
func createTbaScoringBreakdown(match *model.Match, matchResult *model.MatchResult, alliance string) *TbaScoreBreakdown {
|
func createTbaScoringBreakdown(match *model.Match, matchResult *model.MatchResult, alliance string) *TbaScoreBreakdown {
|
||||||
var breakdown TbaScoreBreakdown
|
var breakdown TbaScoreBreakdown
|
||||||
var score *game.Score
|
// TODO(patrick): Implement for 2018.
|
||||||
var scoreSummary *game.ScoreSummary
|
/*
|
||||||
if alliance == "red" {
|
var score *game.Score
|
||||||
score = matchResult.RedScore
|
var scoreSummary *game.ScoreSummary
|
||||||
scoreSummary = matchResult.RedScoreSummary()
|
if alliance == "red" {
|
||||||
} else {
|
score = matchResult.RedScore
|
||||||
score = matchResult.BlueScore
|
scoreSummary = matchResult.RedScoreSummary()
|
||||||
scoreSummary = matchResult.BlueScoreSummary()
|
} else {
|
||||||
}
|
score = matchResult.BlueScore
|
||||||
|
scoreSummary = matchResult.BlueScoreSummary()
|
||||||
breakdown.AutoFuelHigh = score.AutoFuelHigh
|
|
||||||
breakdown.AutoFuelLow = score.AutoFuelLow
|
|
||||||
breakdown.AutoFuelPoints = score.AutoFuelHigh + score.AutoFuelLow/3
|
|
||||||
breakdown.Rotor1Auto = score.AutoRotors >= 1
|
|
||||||
breakdown.Rotor2Auto = score.AutoRotors >= 2
|
|
||||||
breakdown.AutoRotorPoints = 60 * score.AutoRotors
|
|
||||||
breakdown.AutoMobilityPoints = scoreSummary.AutoMobilityPoints
|
|
||||||
breakdown.AutoPoints = scoreSummary.AutoPoints
|
|
||||||
breakdown.TeleopFuelHigh = score.FuelHigh
|
|
||||||
breakdown.TeleopFuelLow = score.FuelLow
|
|
||||||
breakdown.TeleopFuelPoints = scoreSummary.PressurePoints - breakdown.AutoFuelPoints
|
|
||||||
totalRotors := score.AutoRotors + score.Rotors
|
|
||||||
breakdown.Rotor1Engaged = totalRotors >= 1
|
|
||||||
breakdown.Rotor2Engaged = totalRotors >= 2
|
|
||||||
breakdown.Rotor3Engaged = totalRotors >= 3
|
|
||||||
breakdown.Rotor4Engaged = totalRotors >= 4
|
|
||||||
breakdown.TeleopRotorPoints = scoreSummary.RotorPoints - breakdown.AutoRotorPoints
|
|
||||||
breakdown.TeleopTakeoffPoints = scoreSummary.TakeoffPoints
|
|
||||||
breakdown.TeleopPoints = breakdown.TeleopFuelPoints + breakdown.TeleopRotorPoints +
|
|
||||||
breakdown.TeleopTakeoffPoints + scoreSummary.BonusPoints
|
|
||||||
if match.Type == "elimination" {
|
|
||||||
if scoreSummary.PressureGoalReached {
|
|
||||||
breakdown.KPaBonusPoints = 20
|
|
||||||
}
|
}
|
||||||
if scoreSummary.RotorGoalReached {
|
|
||||||
breakdown.RotorBonusPoints = 100
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
breakdown.KPaRankingPointAchieved = scoreSummary.PressureGoalReached
|
|
||||||
breakdown.RotorRankingPointAchieved = scoreSummary.RotorGoalReached
|
|
||||||
}
|
|
||||||
breakdown.FoulPoints = scoreSummary.FoulPoints
|
|
||||||
breakdown.TotalPoints = scoreSummary.Score
|
|
||||||
|
|
||||||
|
breakdown.AutoFuelHigh = score.AutoFuelHigh
|
||||||
|
breakdown.AutoFuelLow = score.AutoFuelLow
|
||||||
|
breakdown.AutoFuelPoints = score.AutoFuelHigh + score.AutoFuelLow/3
|
||||||
|
breakdown.Rotor1Auto = score.AutoRotors >= 1
|
||||||
|
breakdown.Rotor2Auto = score.AutoRotors >= 2
|
||||||
|
breakdown.AutoRotorPoints = 60 * score.AutoRotors
|
||||||
|
breakdown.AutoMobilityPoints = scoreSummary.AutoMobilityPoints
|
||||||
|
breakdown.AutoPoints = scoreSummary.AutoPoints
|
||||||
|
breakdown.TeleopFuelHigh = score.FuelHigh
|
||||||
|
breakdown.TeleopFuelLow = score.FuelLow
|
||||||
|
breakdown.TeleopFuelPoints = scoreSummary.PressurePoints - breakdown.AutoFuelPoints
|
||||||
|
totalRotors := score.AutoRotors + score.Rotors
|
||||||
|
breakdown.Rotor1Engaged = totalRotors >= 1
|
||||||
|
breakdown.Rotor2Engaged = totalRotors >= 2
|
||||||
|
breakdown.Rotor3Engaged = totalRotors >= 3
|
||||||
|
breakdown.Rotor4Engaged = totalRotors >= 4
|
||||||
|
breakdown.TeleopRotorPoints = scoreSummary.RotorPoints - breakdown.AutoRotorPoints
|
||||||
|
breakdown.TeleopTakeoffPoints = scoreSummary.TakeoffPoints
|
||||||
|
breakdown.TeleopPoints = breakdown.TeleopFuelPoints + breakdown.TeleopRotorPoints +
|
||||||
|
breakdown.TeleopTakeoffPoints + scoreSummary.BonusPoints
|
||||||
|
if match.Type == "elimination" {
|
||||||
|
if scoreSummary.PressureGoalReached {
|
||||||
|
breakdown.KPaBonusPoints = 20
|
||||||
|
}
|
||||||
|
if scoreSummary.RotorGoalReached {
|
||||||
|
breakdown.RotorBonusPoints = 100
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
breakdown.KPaRankingPointAchieved = scoreSummary.PressureGoalReached
|
||||||
|
breakdown.RotorRankingPointAchieved = scoreSummary.RotorGoalReached
|
||||||
|
}
|
||||||
|
breakdown.FoulPoints = scoreSummary.FoulPoints
|
||||||
|
breakdown.TotalPoints = scoreSummary.Score
|
||||||
|
*/
|
||||||
return &breakdown
|
return &breakdown
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
Rank,TeamId,RankingPoints,MatchPoints,AutoPoints,RotorPoints,TakeoffPoints,PressurePoints,Wins,Losses,Ties,Disqualifications,Played
|
Rank,TeamId,RankingPoints,ParkClimbPoints,AutoPoints,OwnershipPoints,VaultPoints,Wins,Losses,Ties,Disqualifications,Played
|
||||||
{{range $ranking := .}}{{$ranking.Rank}},{{$ranking.TeamId}},{{$ranking.RankingPoints}},{{$ranking.MatchPoints}},{{$ranking.AutoPoints}},{{$ranking.RotorPoints}},{{$ranking.TakeoffPoints}},{{$ranking.PressurePoints}},{{$ranking.Wins}},{{$ranking.Losses}},{{$ranking.Ties}},{{$ranking.Disqualifications}},{{$ranking.Played}}
|
{{range $ranking := .}}{{$ranking.Rank}},{{$ranking.TeamId}},{{$ranking.RankingPoints}},{{$ranking.ParkClimbPoints}},{{$ranking.AutoPoints}},{{$ranking.OwnershipPoints}},{{$ranking.VaultPoints}},{{$ranking.Wins}},{{$ranking.Losses}},{{$ranking.Ties}},{{$ranking.Disqualifications}},{{$ranking.Played}}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
|
@@ -133,7 +133,7 @@ func TestCommitMatch(t *testing.T) {
|
|||||||
web.arena.Database.CreateMatch(match)
|
web.arena.Database.CreateMatch(match)
|
||||||
matchResult = model.NewMatchResult()
|
matchResult = model.NewMatchResult()
|
||||||
matchResult.MatchId = match.Id
|
matchResult.MatchId = match.Id
|
||||||
matchResult.BlueScore = &game.Score{AutoMobility: 2}
|
matchResult.BlueScore = &game.Score{AutoRuns: 2}
|
||||||
err = web.commitMatchScore(match, matchResult, false)
|
err = web.commitMatchScore(match, matchResult, false)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 1, matchResult.PlayNumber)
|
assert.Equal(t, 1, matchResult.PlayNumber)
|
||||||
@@ -142,7 +142,7 @@ func TestCommitMatch(t *testing.T) {
|
|||||||
|
|
||||||
matchResult = model.NewMatchResult()
|
matchResult = model.NewMatchResult()
|
||||||
matchResult.MatchId = match.Id
|
matchResult.MatchId = match.Id
|
||||||
matchResult.RedScore = &game.Score{AutoMobility: 1}
|
matchResult.RedScore = &game.Score{AutoRuns: 1}
|
||||||
err = web.commitMatchScore(match, matchResult, false)
|
err = web.commitMatchScore(match, matchResult, false)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 2, matchResult.PlayNumber)
|
assert.Equal(t, 2, matchResult.PlayNumber)
|
||||||
@@ -177,7 +177,7 @@ func TestCommitEliminationTie(t *testing.T) {
|
|||||||
|
|
||||||
match := &model.Match{Id: 0, Type: "qualification", Red1: 1, Red2: 2, Red3: 3, Blue1: 4, Blue2: 5, Blue3: 6}
|
match := &model.Match{Id: 0, Type: "qualification", Red1: 1, Red2: 2, Red3: 3, Blue1: 4, Blue2: 5, Blue3: 6}
|
||||||
web.arena.Database.CreateMatch(match)
|
web.arena.Database.CreateMatch(match)
|
||||||
matchResult := &model.MatchResult{MatchId: match.Id, RedScore: &game.Score{FuelHigh: 15, Fouls: []game.Foul{{}}},
|
matchResult := &model.MatchResult{MatchId: match.Id, RedScore: &game.Score{VaultCubes: 1, Fouls: []game.Foul{{}}},
|
||||||
BlueScore: &game.Score{}}
|
BlueScore: &game.Score{}}
|
||||||
err := web.commitMatchScore(match, matchResult, false)
|
err := web.commitMatchScore(match, matchResult, false)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
@@ -233,7 +233,7 @@ func TestCommitCards(t *testing.T) {
|
|||||||
err = web.commitMatchScore(match, matchResult, false)
|
err = web.commitMatchScore(match, matchResult, false)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, 0, matchResult.RedScoreSummary().Score)
|
assert.Equal(t, 0, matchResult.RedScoreSummary().Score)
|
||||||
assert.Equal(t, 533, matchResult.BlueScoreSummary().Score)
|
assert.NotEqual(t, 0, matchResult.BlueScoreSummary().Score)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMatchPlayWebsocketCommands(t *testing.T) {
|
func TestMatchPlayWebsocketCommands(t *testing.T) {
|
||||||
@@ -303,12 +303,12 @@ func TestMatchPlayWebsocketCommands(t *testing.T) {
|
|||||||
readWebsocketType(t, ws, "status")
|
readWebsocketType(t, ws, "status")
|
||||||
readWebsocketType(t, ws, "setAudienceDisplay")
|
readWebsocketType(t, ws, "setAudienceDisplay")
|
||||||
assert.Equal(t, field.PostMatch, web.arena.MatchState)
|
assert.Equal(t, field.PostMatch, web.arena.MatchState)
|
||||||
web.arena.RedRealtimeScore.CurrentScore.AutoMobility = 1
|
web.arena.RedRealtimeScore.CurrentScore.AutoRuns = 1
|
||||||
web.arena.BlueRealtimeScore.CurrentScore.AutoFuelLow = 2
|
web.arena.BlueRealtimeScore.CurrentScore.VaultCubes = 2
|
||||||
ws.Write("commitResults", nil)
|
ws.Write("commitResults", nil)
|
||||||
readWebsocketMultiple(t, ws, 3) // reload, realtimeScore, setAllianceStationDisplay
|
readWebsocketMultiple(t, ws, 3) // reload, realtimeScore, setAllianceStationDisplay
|
||||||
assert.Equal(t, 1, web.arena.SavedMatchResult.RedScore.AutoMobility)
|
assert.Equal(t, 1, web.arena.SavedMatchResult.RedScore.AutoRuns)
|
||||||
assert.Equal(t, 2, web.arena.SavedMatchResult.BlueScore.AutoFuelLow)
|
assert.Equal(t, 2, web.arena.SavedMatchResult.BlueScore.VaultCubes)
|
||||||
assert.Equal(t, field.PreMatch, web.arena.MatchState)
|
assert.Equal(t, field.PreMatch, web.arena.MatchState)
|
||||||
ws.Write("discardResults", nil)
|
ws.Write("discardResults", nil)
|
||||||
readWebsocketMultiple(t, ws, 3) // reload, realtimeScore, setAllianceStationDisplay
|
readWebsocketMultiple(t, ws, 3) // reload, realtimeScore, setAllianceStationDisplay
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ func TestMatchReviewEditExistingResult(t *testing.T) {
|
|||||||
recorder := web.getHttpResponse("/match_review")
|
recorder := web.getHttpResponse("/match_review")
|
||||||
assert.Equal(t, 200, recorder.Code)
|
assert.Equal(t, 200, recorder.Code)
|
||||||
assert.Contains(t, recorder.Body.String(), "QF4-3")
|
assert.Contains(t, recorder.Body.String(), "QF4-3")
|
||||||
assert.Contains(t, recorder.Body.String(), "210") // The red score
|
assert.Contains(t, recorder.Body.String(), "169") // The red score
|
||||||
assert.Contains(t, recorder.Body.String(), "533") // The blue score
|
assert.Contains(t, recorder.Body.String(), "228") // The blue score
|
||||||
|
|
||||||
// Check response for non-existent match.
|
// Check response for non-existent match.
|
||||||
recorder = web.getHttpResponse(fmt.Sprintf("/match_review/%d/edit", 12345))
|
recorder = web.getHttpResponse(fmt.Sprintf("/match_review/%d/edit", 12345))
|
||||||
@@ -62,7 +62,7 @@ func TestMatchReviewEditExistingResult(t *testing.T) {
|
|||||||
assert.Contains(t, recorder.Body.String(), "QF4-3")
|
assert.Contains(t, recorder.Body.String(), "QF4-3")
|
||||||
|
|
||||||
// Update the score to something else.
|
// Update the score to something else.
|
||||||
postBody := "redScoreJson={\"AutoMobility\":3}&blueScoreJson={\"Rotors\":3," +
|
postBody := "redScoreJson={\"AutoRuns\":3}&blueScoreJson={\"VaultCubes\":3," +
|
||||||
"\"Fouls\":[{\"TeamId\":973,\"Rule\":\"G22\"}]}&redCardsJson={\"105\":\"yellow\"}&blueCardsJson={}"
|
"\"Fouls\":[{\"TeamId\":973,\"Rule\":\"G22\"}]}&redCardsJson={\"105\":\"yellow\"}&blueCardsJson={}"
|
||||||
recorder = web.postHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id), postBody)
|
recorder = web.postHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id), postBody)
|
||||||
assert.Equal(t, 303, recorder.Code)
|
assert.Equal(t, 303, recorder.Code)
|
||||||
@@ -71,8 +71,8 @@ func TestMatchReviewEditExistingResult(t *testing.T) {
|
|||||||
recorder = web.getHttpResponse("/match_review")
|
recorder = web.getHttpResponse("/match_review")
|
||||||
assert.Equal(t, 200, recorder.Code)
|
assert.Equal(t, 200, recorder.Code)
|
||||||
assert.Contains(t, recorder.Body.String(), "QF4-3")
|
assert.Contains(t, recorder.Body.String(), "QF4-3")
|
||||||
assert.Contains(t, recorder.Body.String(), "20") // The red score
|
assert.Contains(t, recorder.Body.String(), "20") // The red score
|
||||||
assert.Contains(t, recorder.Body.String(), "120") // The blue score
|
assert.Contains(t, recorder.Body.String(), "15") // The blue score
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMatchReviewCreateNewResult(t *testing.T) {
|
func TestMatchReviewCreateNewResult(t *testing.T) {
|
||||||
@@ -94,7 +94,7 @@ func TestMatchReviewCreateNewResult(t *testing.T) {
|
|||||||
assert.Contains(t, recorder.Body.String(), "QF4-3")
|
assert.Contains(t, recorder.Body.String(), "QF4-3")
|
||||||
|
|
||||||
// Update the score to something else.
|
// Update the score to something else.
|
||||||
postBody := "redScoreJson={\"AutoRotors\":1}&blueScoreJson={\"FuelHigh\":30," +
|
postBody := "redScoreJson={\"AutoOwnershipPoints\":60}&blueScoreJson={\"VaultCubes\":2," +
|
||||||
"\"Fouls\":[{\"TeamId\":973,\"Rule\":\"G22\"}]}&redCardsJson={\"105\":\"yellow\"}&blueCardsJson={}"
|
"\"Fouls\":[{\"TeamId\":973,\"Rule\":\"G22\"}]}&redCardsJson={\"105\":\"yellow\"}&blueCardsJson={}"
|
||||||
recorder = web.postHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id), postBody)
|
recorder = web.postHttpResponse(fmt.Sprintf("/match_review/%d/edit", match.Id), postBody)
|
||||||
assert.Equal(t, 303, recorder.Code)
|
assert.Equal(t, 303, recorder.Code)
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ func (web *Web) rankingsPdfReportHandler(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The widths of the table columns in mm, stored here so that they can be referenced for each row.
|
// The widths of the table columns in mm, stored here so that they can be referenced for each row.
|
||||||
colWidths := map[string]float64{"Rank": 13, "Team": 21, "RP": 18, "Match": 18, "Auto": 18, "Rotor": 18,
|
colWidths := map[string]float64{"Rank": 13, "Team": 21, "RP": 18, "Park/Climb": 18, "Auto": 18, "Ownership": 18,
|
||||||
"Takeoff": 18, "Pressure": 18, "W-L-T": 18, "DQ": 18, "Played": 18}
|
"Vault": 18, "W-L-T": 18, "DQ": 18, "Played": 18}
|
||||||
rowHeight := 6.5
|
rowHeight := 6.5
|
||||||
|
|
||||||
pdf := gofpdf.New("P", "mm", "Letter", "font")
|
pdf := gofpdf.New("P", "mm", "Letter", "font")
|
||||||
@@ -67,11 +67,10 @@ func (web *Web) rankingsPdfReportHandler(w http.ResponseWriter, r *http.Request)
|
|||||||
pdf.CellFormat(colWidths["Rank"], rowHeight, "Rank", "1", 0, "C", true, 0, "")
|
pdf.CellFormat(colWidths["Rank"], rowHeight, "Rank", "1", 0, "C", true, 0, "")
|
||||||
pdf.CellFormat(colWidths["Team"], rowHeight, "Team", "1", 0, "C", true, 0, "")
|
pdf.CellFormat(colWidths["Team"], rowHeight, "Team", "1", 0, "C", true, 0, "")
|
||||||
pdf.CellFormat(colWidths["RP"], rowHeight, "RP", "1", 0, "C", true, 0, "")
|
pdf.CellFormat(colWidths["RP"], rowHeight, "RP", "1", 0, "C", true, 0, "")
|
||||||
pdf.CellFormat(colWidths["Match"], rowHeight, "Match", "1", 0, "C", true, 0, "")
|
pdf.CellFormat(colWidths["Park/Climb"], rowHeight, "Park/Climb", "1", 0, "C", true, 0, "")
|
||||||
pdf.CellFormat(colWidths["Auto"], rowHeight, "Auto", "1", 0, "C", true, 0, "")
|
pdf.CellFormat(colWidths["Auto"], rowHeight, "Auto", "1", 0, "C", true, 0, "")
|
||||||
pdf.CellFormat(colWidths["Rotor"], rowHeight, "Rotor", "1", 0, "C", true, 0, "")
|
pdf.CellFormat(colWidths["Ownership"], rowHeight, "Rotor", "1", 0, "C", true, 0, "")
|
||||||
pdf.CellFormat(colWidths["Takeoff"], rowHeight, "Takeoff", "1", 0, "C", true, 0, "")
|
pdf.CellFormat(colWidths["Vault"], rowHeight, "Takeoff", "1", 0, "C", true, 0, "")
|
||||||
pdf.CellFormat(colWidths["Pressure"], rowHeight, "Pressure", "1", 0, "C", true, 0, "")
|
|
||||||
pdf.CellFormat(colWidths["W-L-T"], rowHeight, "W-L-T", "1", 0, "C", true, 0, "")
|
pdf.CellFormat(colWidths["W-L-T"], rowHeight, "W-L-T", "1", 0, "C", true, 0, "")
|
||||||
pdf.CellFormat(colWidths["DQ"], rowHeight, "DQ", "1", 0, "C", true, 0, "")
|
pdf.CellFormat(colWidths["DQ"], rowHeight, "DQ", "1", 0, "C", true, 0, "")
|
||||||
pdf.CellFormat(colWidths["Played"], rowHeight, "Played", "1", 1, "C", true, 0, "")
|
pdf.CellFormat(colWidths["Played"], rowHeight, "Played", "1", 1, "C", true, 0, "")
|
||||||
@@ -82,11 +81,10 @@ func (web *Web) rankingsPdfReportHandler(w http.ResponseWriter, r *http.Request)
|
|||||||
pdf.SetFont("Arial", "", 10)
|
pdf.SetFont("Arial", "", 10)
|
||||||
pdf.CellFormat(colWidths["Team"], rowHeight, strconv.Itoa(ranking.TeamId), "1", 0, "C", false, 0, "")
|
pdf.CellFormat(colWidths["Team"], rowHeight, strconv.Itoa(ranking.TeamId), "1", 0, "C", false, 0, "")
|
||||||
pdf.CellFormat(colWidths["RP"], rowHeight, strconv.Itoa(ranking.RankingPoints), "1", 0, "C", false, 0, "")
|
pdf.CellFormat(colWidths["RP"], rowHeight, strconv.Itoa(ranking.RankingPoints), "1", 0, "C", false, 0, "")
|
||||||
pdf.CellFormat(colWidths["Match"], rowHeight, strconv.Itoa(ranking.MatchPoints), "1", 0, "C", false, 0, "")
|
pdf.CellFormat(colWidths["Park/Climb"], rowHeight, strconv.Itoa(ranking.ParkClimbPoints), "1", 0, "C", false, 0, "")
|
||||||
pdf.CellFormat(colWidths["Auto"], rowHeight, strconv.Itoa(ranking.AutoPoints), "1", 0, "C", false, 0, "")
|
pdf.CellFormat(colWidths["Auto"], rowHeight, strconv.Itoa(ranking.AutoPoints), "1", 0, "C", false, 0, "")
|
||||||
pdf.CellFormat(colWidths["Rotor"], rowHeight, strconv.Itoa(ranking.RotorPoints), "1", 0, "C", false, 0, "")
|
pdf.CellFormat(colWidths["Ownership"], rowHeight, strconv.Itoa(ranking.OwnershipPoints), "1", 0, "C", false, 0, "")
|
||||||
pdf.CellFormat(colWidths["Takeoff"], rowHeight, strconv.Itoa(ranking.TakeoffPoints), "1", 0, "C", false, 0, "")
|
pdf.CellFormat(colWidths["Vault"], rowHeight, strconv.Itoa(ranking.VaultPoints), "1", 0, "C", false, 0, "")
|
||||||
pdf.CellFormat(colWidths["Pressure"], rowHeight, strconv.Itoa(ranking.PressurePoints), "1", 0, "C", false, 0, "")
|
|
||||||
record := fmt.Sprintf("%d-%d-%d", ranking.Wins, ranking.Losses, ranking.Ties)
|
record := fmt.Sprintf("%d-%d-%d", ranking.Wins, ranking.Losses, ranking.Ties)
|
||||||
pdf.CellFormat(colWidths["W-L-T"], rowHeight, record, "1", 0, "C", false, 0, "")
|
pdf.CellFormat(colWidths["W-L-T"], rowHeight, record, "1", 0, "C", false, 0, "")
|
||||||
pdf.CellFormat(colWidths["DQ"], rowHeight, strconv.Itoa(ranking.Disqualifications), "1", 0, "C", false, 0, "")
|
pdf.CellFormat(colWidths["DQ"], rowHeight, strconv.Itoa(ranking.Disqualifications), "1", 0, "C", false, 0, "")
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ func TestRankingsCsvReport(t *testing.T) {
|
|||||||
recorder := web.getHttpResponse("/reports/csv/rankings")
|
recorder := web.getHttpResponse("/reports/csv/rankings")
|
||||||
assert.Equal(t, 200, recorder.Code)
|
assert.Equal(t, 200, recorder.Code)
|
||||||
assert.Equal(t, "text/plain", recorder.HeaderMap["Content-Type"][0])
|
assert.Equal(t, "text/plain", recorder.HeaderMap["Content-Type"][0])
|
||||||
expectedBody := "Rank,TeamId,RankingPoints,MatchPoints,AutoPoints,RotorPoints,TakeoffPoints,PressurePoints,Wins," +
|
expectedBody := "Rank,TeamId,RankingPoints,ParkClimbPoints,AutoPoints,OwnershipPoints,VaultPoints,Wins," +
|
||||||
"Losses,Ties,Disqualifications,Played\n1,254,20,625,90,554,10,50,3,2,1,0,10\n2,1114,18,700,625,90,554,9,1,3," +
|
"Losses,Ties,Disqualifications,Played\n1,254,20,625,90,554,10,3,2,1,0,10\n2,1114,18,700,625,90,554,1,3," +
|
||||||
"2,0,10\n\n"
|
"2,0,10\n\n"
|
||||||
assert.Equal(t, expectedBody, recorder.Body.String())
|
assert.Equal(t, expectedBody, recorder.Body.String())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,14 +147,14 @@ func (web *Web) scoringDisplayWebsocketHandler(w http.ResponseWriter, r *http.Re
|
|||||||
switch messageType {
|
switch messageType {
|
||||||
case "mobility":
|
case "mobility":
|
||||||
if !autoCommitted {
|
if !autoCommitted {
|
||||||
if (*score).CurrentScore.AutoMobility < 3 {
|
if (*score).CurrentScore.AutoRuns < 3 {
|
||||||
(*score).CurrentScore.AutoMobility++
|
(*score).CurrentScore.AutoRuns++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "undoMobility":
|
case "undoMobility":
|
||||||
if !autoCommitted {
|
if !autoCommitted {
|
||||||
if (*score).CurrentScore.AutoMobility > 0 {
|
if (*score).CurrentScore.AutoRuns > 0 {
|
||||||
(*score).CurrentScore.AutoMobility--
|
(*score).CurrentScore.AutoRuns--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case "commit":
|
case "commit":
|
||||||
|
|||||||
@@ -64,8 +64,8 @@ func TestScoringDisplayWebsocket(t *testing.T) {
|
|||||||
readWebsocketType(t, blueWs, "score")
|
readWebsocketType(t, blueWs, "score")
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.Equal(t, 1, web.arena.RedRealtimeScore.CurrentScore.AutoMobility)
|
assert.Equal(t, 1, web.arena.RedRealtimeScore.CurrentScore.AutoRuns)
|
||||||
assert.Equal(t, 2, web.arena.BlueRealtimeScore.CurrentScore.AutoMobility)
|
assert.Equal(t, 2, web.arena.BlueRealtimeScore.CurrentScore.AutoRuns)
|
||||||
|
|
||||||
redWs.Write("mobility", nil)
|
redWs.Write("mobility", nil)
|
||||||
for i := 0; i < 1; i++ {
|
for i := 0; i < 1; i++ {
|
||||||
@@ -76,8 +76,8 @@ func TestScoringDisplayWebsocket(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure auto scores haven't changed in teleop.
|
// Make sure auto scores haven't changed in teleop.
|
||||||
assert.Equal(t, 1, web.arena.RedRealtimeScore.CurrentScore.AutoMobility)
|
assert.Equal(t, 1, web.arena.RedRealtimeScore.CurrentScore.AutoRuns)
|
||||||
assert.Equal(t, 2, web.arena.BlueRealtimeScore.CurrentScore.AutoMobility)
|
assert.Equal(t, 2, web.arena.BlueRealtimeScore.CurrentScore.AutoRuns)
|
||||||
|
|
||||||
// Test committing logic.
|
// Test committing logic.
|
||||||
redWs.Write("commitMatch", nil)
|
redWs.Write("commitMatch", nil)
|
||||||
|
|||||||
@@ -72,49 +72,52 @@ func (web *Web) fieldTestPostHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(patrick): Update for 2018.
|
||||||
mode := r.PostFormValue("mode")
|
mode := r.PostFormValue("mode")
|
||||||
switch mode {
|
/*
|
||||||
case "boiler":
|
switch mode {
|
||||||
web.arena.Plc.SetBoilerMotors(true)
|
case "boiler":
|
||||||
web.arena.Plc.SetRotorMotors(0, 0)
|
web.arena.Plc.SetBoilerMotors(true)
|
||||||
web.arena.Plc.SetRotorLights(0, 0)
|
web.arena.Plc.SetRotorMotors(0, 0)
|
||||||
web.arena.Plc.SetTouchpadLights([3]bool{false, false, false}, [3]bool{false, false, false})
|
web.arena.Plc.SetRotorLights(0, 0)
|
||||||
case "rotor1":
|
web.arena.Plc.SetTouchpadLights([3]bool{false, false, false}, [3]bool{false, false, false})
|
||||||
web.arena.Plc.SetBoilerMotors(false)
|
case "rotor1":
|
||||||
web.arena.Plc.SetRotorMotors(1, 1)
|
web.arena.Plc.SetBoilerMotors(false)
|
||||||
web.arena.Plc.SetRotorLights(1, 1)
|
web.arena.Plc.SetRotorMotors(1, 1)
|
||||||
web.arena.Plc.SetTouchpadLights([3]bool{true, false, false}, [3]bool{true, false, false})
|
web.arena.Plc.SetRotorLights(1, 1)
|
||||||
case "rotor2":
|
web.arena.Plc.SetTouchpadLights([3]bool{true, false, false}, [3]bool{true, false, false})
|
||||||
web.arena.Plc.SetBoilerMotors(false)
|
case "rotor2":
|
||||||
web.arena.Plc.SetRotorMotors(2, 2)
|
web.arena.Plc.SetBoilerMotors(false)
|
||||||
web.arena.Plc.SetRotorLights(2, 2)
|
web.arena.Plc.SetRotorMotors(2, 2)
|
||||||
web.arena.Plc.SetTouchpadLights([3]bool{false, true, false}, [3]bool{false, true, false})
|
web.arena.Plc.SetRotorLights(2, 2)
|
||||||
case "rotor3":
|
web.arena.Plc.SetTouchpadLights([3]bool{false, true, false}, [3]bool{false, true, false})
|
||||||
web.arena.Plc.SetBoilerMotors(false)
|
case "rotor3":
|
||||||
web.arena.Plc.SetRotorMotors(3, 3)
|
web.arena.Plc.SetBoilerMotors(false)
|
||||||
web.arena.Plc.SetRotorLights(2, 2)
|
web.arena.Plc.SetRotorMotors(3, 3)
|
||||||
web.arena.Plc.SetTouchpadLights([3]bool{false, false, true}, [3]bool{false, false, true})
|
web.arena.Plc.SetRotorLights(2, 2)
|
||||||
case "rotor4":
|
web.arena.Plc.SetTouchpadLights([3]bool{false, false, true}, [3]bool{false, false, true})
|
||||||
web.arena.Plc.SetBoilerMotors(false)
|
case "rotor4":
|
||||||
web.arena.Plc.SetRotorMotors(4, 4)
|
web.arena.Plc.SetBoilerMotors(false)
|
||||||
web.arena.Plc.SetRotorLights(2, 2)
|
web.arena.Plc.SetRotorMotors(4, 4)
|
||||||
web.arena.Plc.SetTouchpadLights([3]bool{false, false, false}, [3]bool{false, false, false})
|
web.arena.Plc.SetRotorLights(2, 2)
|
||||||
case "red":
|
web.arena.Plc.SetTouchpadLights([3]bool{false, false, false}, [3]bool{false, false, false})
|
||||||
web.arena.Plc.SetBoilerMotors(false)
|
case "red":
|
||||||
web.arena.Plc.SetRotorMotors(4, 0)
|
web.arena.Plc.SetBoilerMotors(false)
|
||||||
web.arena.Plc.SetRotorLights(2, 0)
|
web.arena.Plc.SetRotorMotors(4, 0)
|
||||||
web.arena.Plc.SetTouchpadLights([3]bool{true, true, true}, [3]bool{false, false, false})
|
web.arena.Plc.SetRotorLights(2, 0)
|
||||||
case "blue":
|
web.arena.Plc.SetTouchpadLights([3]bool{true, true, true}, [3]bool{false, false, false})
|
||||||
web.arena.Plc.SetBoilerMotors(false)
|
case "blue":
|
||||||
web.arena.Plc.SetRotorMotors(0, 4)
|
web.arena.Plc.SetBoilerMotors(false)
|
||||||
web.arena.Plc.SetRotorLights(0, 2)
|
web.arena.Plc.SetRotorMotors(0, 4)
|
||||||
web.arena.Plc.SetTouchpadLights([3]bool{false, false, false}, [3]bool{true, true, true})
|
web.arena.Plc.SetRotorLights(0, 2)
|
||||||
default:
|
web.arena.Plc.SetTouchpadLights([3]bool{false, false, false}, [3]bool{true, true, true})
|
||||||
web.arena.Plc.SetBoilerMotors(false)
|
default:
|
||||||
web.arena.Plc.SetRotorMotors(0, 0)
|
web.arena.Plc.SetBoilerMotors(false)
|
||||||
web.arena.Plc.SetRotorLights(0, 0)
|
web.arena.Plc.SetRotorMotors(0, 0)
|
||||||
web.arena.Plc.SetTouchpadLights([3]bool{false, false, false}, [3]bool{false, false, false})
|
web.arena.Plc.SetRotorLights(0, 0)
|
||||||
}
|
web.arena.Plc.SetTouchpadLights([3]bool{false, false, false}, [3]bool{false, false, false})
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
web.arena.FieldTestMode = mode
|
web.arena.FieldTestMode = mode
|
||||||
http.Redirect(w, r, "/setup/field", 303)
|
http.Redirect(w, r, "/setup/field", 303)
|
||||||
|
|||||||
Reference in New Issue
Block a user