Skip to content

Commit ef8121a

Browse files
Extension Penalty [AARD-2011] (#1248)
Co-authored-by: Alexey Dmitriev <157652245+AlexD717@users.noreply.github.com>
2 parents 16dcb9c + 6f34d3e commit ef8121a

File tree

6 files changed

+211
-32
lines changed

6 files changed

+211
-32
lines changed

fission/src/systems/match_mode/DefaultMatchModeConfigs.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ class DefaultMatchModeConfigs {
1313
endgameTime: 20,
1414
ignoreRotation: true,
1515
maxHeight: Infinity,
16-
heightPenalty: 0,
16+
heightLimitPenalty: 0,
17+
sideMaxExtension: convertFeetToMeters(1.5),
18+
sideExtensionPenalty: 0,
1719
}
1820
}
1921

@@ -27,7 +29,9 @@ class DefaultMatchModeConfigs {
2729
endgameTime: 20,
2830
ignoreRotation: true,
2931
maxHeight: convertFeetToMeters(4),
30-
heightPenalty: 2,
32+
heightLimitPenalty: 2,
33+
sideMaxExtension: convertFeetToMeters(1),
34+
sideExtensionPenalty: 2,
3135
}
3236
}
3337

@@ -41,7 +45,9 @@ class DefaultMatchModeConfigs {
4145
endgameTime: 30,
4246
ignoreRotation: true,
4347
maxHeight: convertFeetToMeters(6.5),
44-
heightPenalty: 5,
48+
heightLimitPenalty: 5,
49+
sideMaxExtension: convertFeetToMeters(4),
50+
sideExtensionPenalty: 5,
4551
}
4652
}
4753

@@ -55,7 +61,9 @@ class DefaultMatchModeConfigs {
5561
endgameTime: 5,
5662
ignoreRotation: true,
5763
maxHeight: Infinity,
58-
heightPenalty: 0,
64+
heightLimitPenalty: 0,
65+
sideMaxExtension: Infinity,
66+
sideExtensionPenalty: 0,
5967
}
6068
}
6169

fission/src/systems/match_mode/MatchMode.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ import SimulationSystem from "../simulation/SimulationSystem"
99
import { SoundPlayer } from "../sound/SoundPlayer"
1010
import {
1111
DEFAULT_AUTONOMOUS_TIME,
12+
DEFAULT_TELEOP_TIME,
1213
DEFAULT_ENDGAME_TIME,
13-
DEFAULT_HEIGHT_PENALTY,
1414
DEFAULT_IGNORE_ROTATION,
1515
DEFAULT_MAX_HEIGHT,
16-
DEFAULT_TELEOP_TIME,
16+
DEFAULT_HEIGHT_LIMIT_PENALTY,
17+
DEFAULT_SIDE_MAX_EXTENSION,
18+
DEFAULT_SIDE_EXTENSION_PENALTY,
1719
MatchModeType,
1820
} from "./MatchModeTypes"
1921
import RobotDimensionTracker from "./RobotDimensionTracker"
@@ -42,7 +44,9 @@ class MatchMode {
4244
endgameTime: DEFAULT_ENDGAME_TIME,
4345
ignoreRotation: DEFAULT_IGNORE_ROTATION,
4446
maxHeight: DEFAULT_MAX_HEIGHT,
45-
heightPenalty: DEFAULT_HEIGHT_PENALTY,
47+
heightLimitPenalty: DEFAULT_HEIGHT_LIMIT_PENALTY,
48+
sideExtensionPenalty: DEFAULT_SIDE_EXTENSION_PENALTY,
49+
sideMaxExtension: DEFAULT_SIDE_MAX_EXTENSION,
4650
}
4751

4852
private constructor() {}
@@ -54,7 +58,13 @@ class MatchMode {
5458

5559
setMatchModeConfig(config: MatchModeConfig) {
5660
this._matchModeConfig = config
57-
RobotDimensionTracker.setConfigValues(config.ignoreRotation, config.maxHeight, config.heightPenalty)
61+
RobotDimensionTracker.setConfigValues(
62+
config.ignoreRotation,
63+
config.maxHeight,
64+
config.heightLimitPenalty,
65+
config.sideMaxExtension,
66+
config.sideExtensionPenalty
67+
)
5868
}
5969

6070
startTimer(duration: number, functionCall: () => void, updateTimeLeft: boolean = true) {
@@ -109,6 +119,7 @@ class MatchMode {
109119
start() {
110120
this.autonomousModeStart()
111121
SimulationSystem.resetScores()
122+
RobotDimensionTracker.matchStart()
112123
}
113124

114125
matchEnded() {

fission/src/systems/match_mode/MatchModeTypes.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,6 @@ export const DEFAULT_TELEOP_TIME = 135
1212
export const DEFAULT_ENDGAME_TIME = 20
1313
export const DEFAULT_IGNORE_ROTATION = true
1414
export const DEFAULT_MAX_HEIGHT = Infinity
15-
export const DEFAULT_HEIGHT_PENALTY = 2
15+
export const DEFAULT_HEIGHT_LIMIT_PENALTY = 2
16+
export const DEFAULT_SIDE_MAX_EXTENSION = Infinity
17+
export const DEFAULT_SIDE_EXTENSION_PENALTY = 2

fission/src/systems/match_mode/RobotDimensionTracker.ts

Lines changed: 45 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,34 @@
11
import { MiraType } from "@/mirabuf/MirabufLoader"
22
import MirabufSceneObject from "@/mirabuf/MirabufSceneObject"
33
import SimulationSystem from "@/systems/simulation/SimulationSystem"
4+
import World from "@/systems/World"
45
import type SceneRenderer from "../scene/SceneRenderer"
56
import MatchMode from "./MatchMode"
67

78
const BUFFER_HEIGHT = 0.1
9+
const SIDE_BUFFER = 0.1
810

911
class RobotDimensionTracker {
1012
private static _robotLastFramePenalty: Map<number, boolean> = new Map()
1113
private static _ignoreRotation: boolean = true
1214
private static _maxHeight: number = Infinity
13-
private static _heightPenalty: number = 0
15+
private static _heightLimitPenalty: number = 0
16+
private static _sideExtensionPenalty: number = 0
17+
private static _robotSize: Map<number, { width: number; depth: number }> = new Map()
18+
private static _sideMaxExtension: number = 0
1419

15-
public static setConfigValues(ignoreRotation: boolean, maxHeight: number, heightPenalty: number) {
20+
public static setConfigValues(
21+
ignoreRotation: boolean,
22+
maxHeight: number,
23+
heightLimitPenalty: number,
24+
sideMaxExtension: number,
25+
sideExtensionPenalty: number
26+
) {
1627
this._ignoreRotation = ignoreRotation
1728
this._maxHeight = maxHeight
18-
this._heightPenalty = heightPenalty
29+
this._heightLimitPenalty = heightLimitPenalty
30+
this._sideMaxExtension = sideMaxExtension
31+
this._sideExtensionPenalty = sideExtensionPenalty
1932
}
2033

2134
public static update(sceneRenderer: SceneRenderer): void {
@@ -30,12 +43,38 @@ class RobotDimensionTracker {
3043

3144
if (dimensions.height > this._maxHeight + BUFFER_HEIGHT) {
3245
if (!(this._robotLastFramePenalty.get(robot.id) ?? false)) {
33-
SimulationSystem.robotPenalty(robot, this._heightPenalty, "Height Expansion Limit")
46+
SimulationSystem.robotPenalty(robot, this._heightLimitPenalty, "Height Expansion Limit")
3447
}
3548
this._robotLastFramePenalty.set(robot.id, true)
36-
} else {
37-
this._robotLastFramePenalty.set(robot.id, false)
49+
return
3850
}
51+
52+
const startingRobotSize = this._robotSize.get(robot.id) ?? { width: Infinity, depth: Infinity }
53+
if (
54+
dimensions.width > startingRobotSize.width + this._sideMaxExtension + SIDE_BUFFER ||
55+
dimensions.depth > startingRobotSize.depth + this._sideMaxExtension + SIDE_BUFFER
56+
) {
57+
if (!(this._robotLastFramePenalty.get(robot.id) ?? false)) {
58+
SimulationSystem.robotPenalty(robot, this._sideExtensionPenalty, "Side Expansion Limit")
59+
}
60+
this._robotLastFramePenalty.set(robot.id, true)
61+
return
62+
}
63+
64+
this._robotLastFramePenalty.set(robot.id, false)
65+
})
66+
}
67+
68+
public static matchStart(): void {
69+
this._robotSize.clear()
70+
this._robotLastFramePenalty.clear()
71+
72+
const robots = [...World.sceneRenderer.sceneObjects.values()].filter(
73+
(obj): obj is MirabufSceneObject => obj instanceof MirabufSceneObject && obj.miraType === MiraType.ROBOT
74+
)
75+
76+
robots.forEach(robot => {
77+
this._robotSize.set(robot.id, robot.getDimensions())
3978
})
4079
}
4180
}

fission/src/test/RobotDimensionsTracker.test.ts

Lines changed: 103 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { MiraType } from "@/mirabuf/MirabufLoader"
33
import MirabufSceneObject from "@/mirabuf/MirabufSceneObject"
44
import RobotDimensionTracker from "@/systems/match_mode/RobotDimensionTracker"
55
import SimulationSystem from "@/systems/simulation/SimulationSystem"
6+
import World from "@/systems/World"
7+
import SceneObject from "@/systems/scene/SceneObject"
68

79
interface MockDimensions {
810
width: number
@@ -11,20 +13,30 @@ interface MockDimensions {
1113
}
1214

1315
interface MockRobotObject {
16+
_id: number | null
17+
id: number
1418
assemblyName: string
1519
miraType: MiraType
1620
getDimensions: () => MockDimensions
1721
getDimensionsWithoutRotation: () => MockDimensions
22+
setup: () => void
23+
update: () => void
24+
dispose: () => void
1825
}
1926

2027
interface MockNonRobotObject {
28+
_id: number | null
29+
id: number
2130
assemblyName: string
2231
miraType: MiraType
2332
getDimensions: () => MockDimensions
33+
setup: () => void
34+
update: () => void
35+
dispose: () => void
2436
}
2537

2638
interface MockSceneRenderer {
27-
sceneObjects: Map<string, MockRobotObject | MockNonRobotObject>
39+
sceneObjects: Map<number, MockRobotObject | MockNonRobotObject>
2840
}
2941

3042
type TrackerUpdateParam = Parameters<typeof RobotDimensionTracker.update>[0]
@@ -48,7 +60,9 @@ vi.mock("@/systems/match_mode/MatchMode", () => ({
4860
DEFAULT_ENDGAME_TIME: 20,
4961
DEFAULT_IGNORE_ROTATION: true,
5062
DEFAULT_MAX_HEIGHT: Infinity,
51-
DEFAULT_HEIGHT_PENALTY: 2,
63+
DEFAULT_HEIGHT_LIMIT_PENALTY: 2,
64+
DEFAULT_SIDE_MAX_EXTENSION: 1.5,
65+
DEFAULT_SIDE_EXTENSION_PENALTY: 2,
5266
}))
5367

5468
vi.mock("@/systems/simulation/SimulationSystem", () => ({
@@ -57,6 +71,14 @@ vi.mock("@/systems/simulation/SimulationSystem", () => ({
5771
},
5872
}))
5973

74+
vi.mock("@/systems/World", () => ({
75+
default: {
76+
sceneRenderer: {
77+
sceneObjects: new Map(),
78+
},
79+
},
80+
}))
81+
6082
describe("RobotDimensionTracker", () => {
6183
let mockSceneRenderer: MockSceneRenderer
6284
let mockRobot1: MockRobotObject
@@ -66,8 +88,12 @@ describe("RobotDimensionTracker", () => {
6688
beforeEach(() => {
6789
vi.clearAllMocks()
6890

69-
const tracker = RobotDimensionTracker as unknown as { _robotLastFramePenalty?: Map<number, boolean> }
91+
const tracker = RobotDimensionTracker as unknown as {
92+
_robotLastFramePenalty?: Map<number, boolean>
93+
_robotSize?: Map<number, { width: number; depth: number }>
94+
}
7095
tracker._robotLastFramePenalty?.clear()
96+
tracker._robotSize?.clear()
7197

7298
const robot1Base = Object.create(MirabufSceneObject.prototype)
7399
const robot2Base = Object.create(MirabufSceneObject.prototype)
@@ -97,34 +123,43 @@ describe("RobotDimensionTracker", () => {
97123
})
98124

99125
mockNonRobot = {
126+
_id: 3,
127+
id: 3,
100128
assemblyName: "Field",
101129
miraType: MiraType.FIELD,
102130
getDimensions: vi.fn(() => ({ height: 5.0, width: 10.0, depth: 10.0 })),
131+
setup: vi.fn(),
132+
update: vi.fn(),
133+
dispose: vi.fn(),
103134
}
104135

105136
mockSceneRenderer = {
106137
sceneObjects: new Map([
107-
["robot1", mockRobot1],
108-
["robot2", mockRobot2],
109-
["field", mockNonRobot],
138+
[1, mockRobot1],
139+
[2, mockRobot2],
140+
[3, mockNonRobot],
110141
]),
111142
}
143+
144+
World.sceneRenderer.sceneObjects.set(1, mockRobot1 as unknown as SceneObject)
145+
World.sceneRenderer.sceneObjects.set(2, mockRobot2 as unknown as SceneObject)
146+
World.sceneRenderer.sceneObjects.set(3, mockNonRobot as unknown as SceneObject)
112147
})
113148

114149
afterEach(() => {
115150
vi.restoreAllMocks()
116151
})
117152

118153
test("config values determine which dimension method is used", () => {
119-
RobotDimensionTracker.setConfigValues(false, 2, 15)
154+
RobotDimensionTracker.setConfigValues(false, 2, 15, 1.5, 15)
120155
RobotDimensionTracker.update(mockSceneRenderer as unknown as TrackerUpdateParam)
121156

122157
expect(mockRobot1.getDimensions).toHaveBeenCalled()
123158
expect(mockRobot1.getDimensionsWithoutRotation).not.toHaveBeenCalled()
124159
})
125160

126161
test("should penalize robot if it exceeds max height", () => {
127-
RobotDimensionTracker.setConfigValues(true, 3, 5)
162+
RobotDimensionTracker.setConfigValues(true, 3, 5, 1.5, 5)
128163

129164
mockRobot1.getDimensionsWithoutRotation = vi.fn().mockReturnValue({ height: 2.0, width: 1.0, depth: 1.0 })
130165
mockRobot2.getDimensionsWithoutRotation = vi.fn().mockReturnValue({ height: 3.5, width: 1.0, depth: 1.0 })
@@ -139,8 +174,65 @@ describe("RobotDimensionTracker", () => {
139174
)
140175
})
141176

177+
test("should penalize robot if it exceeds side max extension (width)", () => {
178+
RobotDimensionTracker.setConfigValues(true, 10, 5, 0.5, 2)
179+
180+
mockRobot1.getDimensions = vi.fn().mockReturnValue({ height: 2.0, width: 1.0, depth: 1.0 })
181+
mockRobot1.getDimensionsWithoutRotation = vi.fn().mockReturnValue({ height: 2.0, width: 1.0, depth: 1.0 })
182+
183+
mockRobot2.getDimensions = vi.fn().mockReturnValue({ height: 2.0, width: 1.0, depth: 1.0 })
184+
mockRobot2.getDimensionsWithoutRotation = vi.fn().mockReturnValue({ height: 2.0, width: 1.0, depth: 1.0 })
185+
186+
RobotDimensionTracker.matchStart()
187+
188+
mockRobot2.getDimensionsWithoutRotation = vi.fn().mockReturnValue({ height: 2.0, width: 1.7, depth: 1.0 })
189+
190+
RobotDimensionTracker.update(mockSceneRenderer as unknown as TrackerUpdateParam)
191+
192+
expect(SimulationSystem.robotPenalty).toHaveBeenCalledWith(mockRobot2, 2, expect.any(String))
193+
expect(SimulationSystem.robotPenalty).not.toHaveBeenCalledWith(
194+
mockRobot1,
195+
expect.any(Number),
196+
expect.any(String)
197+
)
198+
})
199+
200+
test("should penalize robot if it exceeds side max extension (depth)", () => {
201+
RobotDimensionTracker.setConfigValues(true, 10, 5, 0.3, 3)
202+
203+
mockRobot1.getDimensions = vi.fn().mockReturnValue({ height: 2.0, width: 1.0, depth: 1.2 })
204+
mockRobot1.getDimensionsWithoutRotation = vi.fn().mockReturnValue({ height: 2.0, width: 1.0, depth: 1.2 })
205+
206+
mockRobot2.getDimensions = vi.fn().mockReturnValue({ height: 2.0, width: 1.0, depth: 1.2 })
207+
mockRobot2.getDimensionsWithoutRotation = vi.fn().mockReturnValue({ height: 2.0, width: 1.0, depth: 1.2 })
208+
209+
RobotDimensionTracker.matchStart()
210+
211+
mockRobot2.getDimensionsWithoutRotation = vi.fn().mockReturnValue({ height: 2.0, width: 1.0, depth: 1.7 })
212+
213+
RobotDimensionTracker.update(mockSceneRenderer as unknown as TrackerUpdateParam)
214+
215+
expect(SimulationSystem.robotPenalty).toHaveBeenCalledWith(mockRobot2, 3, expect.any(String))
216+
expect(SimulationSystem.robotPenalty).not.toHaveBeenCalledWith(
217+
mockRobot1,
218+
expect.any(Number),
219+
expect.any(String)
220+
)
221+
})
222+
223+
test("should not penalize a robot for side extension if initial dimensions were not recorded", () => {
224+
RobotDimensionTracker.setConfigValues(false, 10, 5, 0.3, 3)
225+
226+
mockRobot1.getDimensions = vi.fn().mockReturnValue({ height: 2.0, width: 1.0, depth: 1.2 })
227+
mockRobot2.getDimensions = vi.fn().mockReturnValue({ height: 2.0, width: 1.0, depth: 1.2 })
228+
229+
RobotDimensionTracker.update(mockSceneRenderer as unknown as TrackerUpdateParam)
230+
231+
expect(SimulationSystem.robotPenalty).not.toHaveBeenCalled()
232+
})
233+
142234
test("should not penalize robot every frame", () => {
143-
RobotDimensionTracker.setConfigValues(true, 3, 5)
235+
RobotDimensionTracker.setConfigValues(true, 3, 5, 1.5, 5)
144236

145237
mockRobot1.getDimensionsWithoutRotation = vi.fn().mockReturnValue({ height: 12, width: 1.0, depth: 1.0 })
146238

@@ -151,7 +243,7 @@ describe("RobotDimensionTracker", () => {
151243
})
152244

153245
test("should penalize multiple robots", () => {
154-
RobotDimensionTracker.setConfigValues(true, 3.048, 5)
246+
RobotDimensionTracker.setConfigValues(true, 3.048, 5, 1.5, 5)
155247

156248
mockRobot1.getDimensionsWithoutRotation = vi.fn().mockReturnValue({ height: 12, width: 1.0, depth: 1.0 })
157249
mockRobot2.getDimensionsWithoutRotation = vi.fn().mockReturnValue({ height: 12, width: 1.0, depth: 1.0 })
@@ -162,7 +254,7 @@ describe("RobotDimensionTracker", () => {
162254
})
163255

164256
test("should not penalize robot if it is not a robot", () => {
165-
RobotDimensionTracker.setConfigValues(true, 3, 5)
257+
RobotDimensionTracker.setConfigValues(true, 3, 5, 1.5, 5)
166258

167259
mockNonRobot.getDimensions = vi.fn().mockReturnValue({ height: 12, width: 1.0, depth: 1.0 })
168260

0 commit comments

Comments
 (0)