Skip to content

Commit 2472926

Browse files
Merge branch 'master' into release-config
2 parents a300a53 + 9ad5dd1 commit 2472926

31 files changed

+180
-53
lines changed

client/src/components/forms.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,21 @@ export const NumInput: React.FC<NumInputProps> = (props) => {
6565
handleInputChange(tempValue || '')
6666
}
6767

68+
/*
69+
* TODO: these useEffects() are a little mid, they should really store the disabled state
70+
* and restore it instead of absolutely setting it to false
71+
*/
72+
6873
React.useEffect(() => {
6974
context.setState((prevState) => ({ ...prevState, disableHotkeys: focused }))
7075
}, [focused])
7176

77+
React.useEffect(() => {
78+
return () => {
79+
context.setState((prevState) => ({ ...prevState, disableHotkeys: false }))
80+
}
81+
}, [])
82+
7283
return (
7384
<input
7485
className={'border border-black py-0.5 px-1 rounded-md w-12 ' + (props.className ?? '')}

client/src/components/sidebar/game/game.tsx

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import { BiMedal } from 'react-icons/bi'
1010
import { EventType, useListenEvent } from '../../../app-events'
1111
import Tooltip from '../../tooltip'
1212
import { useForceUpdate } from '../../../util/react-util'
13+
import Match from '../../../playback/Match'
14+
import { Team } from '../../../playback/Game'
1315

1416
const NO_GAME_TEAM_NAME = '?????'
1517

@@ -28,25 +30,45 @@ export const GamePage: React.FC<Props> = React.memo((props) => {
2830

2931
if (!props.open) return null
3032

33+
const getWinCount = (team: Team) => {
34+
// Only return up to the current match if tournament mode is enabled
35+
if (!activeGame) return 0
36+
let stopCounting = false
37+
const isWinner = (match: Match) => {
38+
if (context.state.tournament && stopCounting) return 0
39+
if (match == activeGame.currentMatch) stopCounting = true
40+
return match.winner?.id === team.id ? 1 : 0
41+
}
42+
return activeGame.matches.reduce((val, match) => val + isWinner(match), 0)
43+
}
44+
3145
const teamBox = (teamIdx: number) => {
32-
let showMatchWinner =
33-
!context.state.tournament || (context.state.activeMatch && context.state.activeMatch.currentTurn.isEnd())
46+
const winCount = activeGame ? getWinCount(activeGame.teams[teamIdx]) : 0
47+
const isEndOfMatch = context.state.activeMatch && context.state.activeMatch.currentTurn.isEnd()
48+
49+
let showMatchWinner = !context.state.tournament || isEndOfMatch
3450
showMatchWinner = showMatchWinner && activeGame && activeGame.currentMatch?.winner === activeGame.teams[teamIdx]
35-
let showGameWinner =
36-
!context.state.tournament ||
37-
(showMatchWinner &&
38-
context.state.activeMatch ==
39-
context.state.activeGame?.matches[context.state.activeGame.matches.length - 1])
51+
let showGameWinner = !context.state.tournament || (showMatchWinner && winCount >= 3)
4052
showGameWinner = showGameWinner && activeGame && activeGame.winner === activeGame.teams[teamIdx]
4153

4254
return (
4355
<div className={'relative w-full py-2 px-3 text-center ' + (teamIdx == 0 ? 'bg-team0' : 'bg-team1')}>
4456
<div>{activeGame?.teams[teamIdx].name ?? NO_GAME_TEAM_NAME}</div>
4557
<div className="absolute top-2 left-3">
4658
{showMatchWinner && (
47-
<Tooltip text={'Current match winner'} location={'right'}>
48-
<BiMedal fontSize={'24px'} width={'20px'} color={'yellow'} />
49-
</Tooltip>
59+
<div className="relative flex items-center w-[24px] h-[24px]">
60+
<div className="absolute">
61+
<Tooltip text={'Current match winner'} location={'right'}>
62+
<BiMedal opacity={0.5} fontSize={'24px'} width={'20px'} color={'yellow'} />
63+
</Tooltip>
64+
</div>
65+
<div
66+
className="absolute w-full text-sm pointer-events-none z-5"
67+
style={{ textShadow: 'white 0px 0px 4px' }}
68+
>
69+
{winCount > 0 && winCount}
70+
</div>
71+
</div>
5072
)}
5173
</div>
5274
<div className="absolute top-3 right-3">

client/src/components/sidebar/game/team-table.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -160,20 +160,20 @@ export const UnitsTable: React.FC<UnitsTableProps> = ({ teamStat, teamIdx }) =>
160160
}
161161

162162
const GlobalUpgradeSection: React.FC<{ teamStat: TeamTurnStat | undefined }> = ({ teamStat }) => {
163-
const upgradeTypes: [schema.GlobalUpgradeType, string][] = [
164-
[schema.GlobalUpgradeType.ACTION_UPGRADE, 'Global Attack Upgrade'],
165-
[schema.GlobalUpgradeType.CAPTURING_UPGRADE, 'Global Capturing Upgrade'],
166-
[schema.GlobalUpgradeType.HEALING_UPGRADE, 'Global Healing Upgrade']
167-
]
163+
const upgradeTypes: Record<schema.GlobalUpgradeType, string> = {
164+
[schema.GlobalUpgradeType.ACTION_UPGRADE]: 'Global Attack Upgrade',
165+
[schema.GlobalUpgradeType.CAPTURING_UPGRADE]: 'Global Capturing Upgrade',
166+
[schema.GlobalUpgradeType.HEALING_UPGRADE]: 'Global Healing Upgrade'
167+
}
168168
if (!teamStat) return <> </>
169169
return (
170170
<>
171-
{upgradeTypes.map(
172-
([type, name]) =>
173-
teamStat.globalUpgrades.has(type) && (
171+
{teamStat.globalUpgrades.map(
172+
(type) =>
173+
upgradeTypes[type] && (
174174
<div className="text-sm flex flex-row justify-center font-bold" key={type}>
175175
<DoubleChevronUpIcon />
176-
{name}
176+
{upgradeTypes[type]}
177177
</div>
178178
)
179179
)}

client/src/components/sidebar/runner/runner.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { SectionHeader } from '../../section-header'
99
import { FixedSizeList, ListOnScrollProps } from 'react-window'
1010
import { OpenExternal } from '../../../icons/open-external'
1111
import { BasicDialog } from '../../basic-dialog'
12+
import { RingBuffer } from '../../../util/ring-buffer'
1213

1314
type RunnerPageProps = {
1415
open: boolean
@@ -67,7 +68,7 @@ export const RunnerPage: React.FC<RunnerPageProps> = ({ open, scaffold }) => {
6768
if (availablePlayers.size > 1) setTeamB([...availablePlayers][1])
6869
}, [availablePlayers])
6970

70-
const MemoConsole = React.useMemo(() => <Console lines={consoleLines} />, [consoleLines])
71+
const MemoConsole = React.useMemo(() => <Console lines={consoleLines} />, [consoleLines.effectiveLength()])
7172

7273
if (!open) return null
7374

@@ -299,7 +300,7 @@ const JavaSelector: React.FC<JavaSelectorProps> = (props) => {
299300
export type ConsoleLine = { content: string; type: 'output' | 'error' | 'bold' }
300301

301302
type Props = {
302-
lines: ConsoleLine[]
303+
lines: RingBuffer<ConsoleLine>
303304
}
304305

305306
export const Console: React.FC<Props> = ({ lines }) => {
@@ -320,8 +321,8 @@ export const Console: React.FC<Props> = ({ lines }) => {
320321
}
321322

322323
const ConsoleRow = (props: { index: number; style: any }) => (
323-
<span style={props.style} className={getLineClass(lines[props.index]) + ' text-xs whitespace-nowrap'}>
324-
{lines[props.index].content}
324+
<span style={props.style} className={getLineClass(lines.get(props.index)!) + ' text-xs whitespace-nowrap'}>
325+
{lines.get(props.index)!.content}
325326
</span>
326327
)
327328

@@ -345,17 +346,17 @@ export const Console: React.FC<Props> = ({ lines }) => {
345346
}
346347

347348
useEffect(() => {
348-
if (lines.length == 0) setTail(true)
349+
if (lines.effectiveLength() == 0) setTail(true)
349350
if (tail && consoleRef.current) {
350351
scrollToBottom()
351352
}
352-
}, [lines])
353+
}, [lines.effectiveLength()])
353354

354355
const lineList = (
355356
<FixedSizeList
356357
outerRef={consoleRef}
357358
height={2000}
358-
itemCount={lines.length}
359+
itemCount={lines.length()}
359360
itemSize={20}
360361
layout="vertical"
361362
width={'100%'}

client/src/components/sidebar/runner/scaffold.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import WebSocketListener from './websocket'
77
import { useAppContext } from '../../../app-context'
88
import Game from '../../../playback/Game'
99
import Match from '../../../playback/Match'
10+
import { RingBuffer } from '../../../util/ring-buffer'
1011

1112
export type JavaInstall = {
1213
display: string
@@ -23,7 +24,7 @@ type Scaffold = [
2324
scaffoldLoading: boolean,
2425
runMatch: (javaPath: string, teamA: string, teamB: string, selectedMaps: Set<string>) => Promise<void>,
2526
killMatch: (() => Promise<void>) | undefined,
26-
console: ConsoleLine[]
27+
console: RingBuffer<ConsoleLine>
2728
]
2829

2930
export function useScaffold(): Scaffold {
@@ -35,12 +36,15 @@ export function useScaffold(): Scaffold {
3536
const [scaffoldPath, setScaffoldPath] = useState<string | undefined>(undefined)
3637
const matchPID = useRef<string | undefined>(undefined)
3738
const forceUpdate = useForceUpdate()
38-
const [consoleLines, setConsoleLines] = useState<ConsoleLine[]>([])
39-
const log = (line: ConsoleLine) =>
40-
setConsoleLines((prev) => (prev.length > 10000 ? [...prev.slice(1), line] : [...prev, line]))
39+
const consoleLines = useRef<RingBuffer<ConsoleLine>>(new RingBuffer(10000))
4140

4241
const [webSocketListener, setWebSocketListener] = useState<WebSocketListener | undefined>()
4342

43+
const log = (line: ConsoleLine) => {
44+
consoleLines.current.push(line)
45+
forceUpdate()
46+
}
47+
4448
async function manuallySetupScaffold() {
4549
if (!nativeAPI) return
4650
setLoading(true)
@@ -52,6 +56,7 @@ export function useScaffold(): Scaffold {
5256
async function runMatch(javaPath: string, teamA: string, teamB: string, selectedMaps: Set<string>): Promise<void> {
5357
if (matchPID.current || !scaffoldPath) return
5458
const shouldProfile = false
59+
consoleLines.current.clear()
5560
try {
5661
const newPID = await dispatchMatch(
5762
javaPath,
@@ -63,10 +68,9 @@ export function useScaffold(): Scaffold {
6368
appContext.state.config.validateMaps,
6469
shouldProfile
6570
)
66-
setConsoleLines([])
6771
matchPID.current = newPID
6872
} catch (e: any) {
69-
setConsoleLines([{ content: e, type: 'error' }])
73+
consoleLines.current.push({ content: e, type: 'error' })
7074
}
7175
forceUpdate()
7276
}
@@ -193,7 +197,7 @@ export function useScaffold(): Scaffold {
193197
loading,
194198
runMatch,
195199
matchPID.current ? killMatch : undefined,
196-
consoleLines
200+
consoleLines.current
197201
]
198202
}
199203

client/src/constants.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { schema } from 'battlecode-schema'
22

3-
export const GAME_VERSION = '2.0.2'
4-
export const SPEC_VERSION = '2.0.2'
3+
export const GAME_VERSION = '3.0.0'
4+
export const SPEC_VERSION = '3.0.0'
55
export const BATTLECODE_YEAR: number = 2024
66
export const MAP_SIZE_RANGE = {
77
min: 30,
@@ -28,6 +28,24 @@ export const ENGINE_BUILTIN_MAP_NAMES: string[] = [
2828
'DefaultMedium',
2929
'DefaultLarge',
3030
'DefaultHuge',
31+
'BedWars',
32+
'Bunkers',
33+
'Checkered',
34+
'Diagonal',
35+
'Divergent',
36+
'EndAround',
37+
'FloodGates',
38+
'Foxes',
39+
'Fusbol',
40+
'GaltonBoard',
41+
'HeMustBeFreed',
42+
'Intercontinental',
43+
'Klein',
44+
'QueenOfHearts',
45+
'QuestionableChess',
46+
'Racetrack',
47+
'Rainbow',
48+
'TreeSearch',
3149
'AceOfSpades',
3250
'Alien',
3351
'Ambush',
@@ -38,6 +56,7 @@ export const ENGINE_BUILTIN_MAP_NAMES: string[] = [
3856
'Duck',
3957
'Fountain',
4058
'Hockey',
59+
'HungerGames',
4160
'MazeRunner',
4261
'Rivers',
4362
'Snake',

client/src/playback/Actions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ export const ACTION_DEFINITIONS: Record<schema.Action, typeof Action> = {
254254
[schema.Action.GLOBAL_UPGRADE]: class GlobalUpgrade extends Action {
255255
apply(turn: Turn): void {
256256
const team = turn.bodies.getById(this.robotID).team
257-
turn.stat.getTeamStat(team).globalUpgrades.add(this.target)
257+
turn.stat.getTeamStat(team).globalUpgrades.push(this.target)
258258
}
259259
}
260260
}

client/src/playback/TurnStat.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ export class TeamTurnStat {
88
specializationTotalLevels: [number, number, number, number, number] = [0, 0, 0, 0, 0]
99
resourceAmount: number = 0
1010
resourceAmountAverageDatapoint: number | undefined = undefined
11-
globalUpgrades: Set<schema.GlobalUpgradeType> = new Set()
11+
globalUpgrades: schema.GlobalUpgradeType[] = []
1212

1313
copy(): TeamTurnStat {
14-
const newStat = Object.assign(Object.create(Object.getPrototypeOf(this)), this)
14+
const newStat: TeamTurnStat = Object.assign(Object.create(Object.getPrototypeOf(this)), this)
1515

1616
// Copy any internal objects here
1717
newStat.robots = [...this.robots]
1818
newStat.specializationTotalLevels = [...this.specializationTotalLevels]
19-
newStat.globalUpgrades = new Set(this.globalUpgrades)
19+
newStat.globalUpgrades = [...this.globalUpgrades]
2020

2121
return newStat
2222
}

client/src/util/ring-buffer.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
export class RingBuffer<T> {
2+
private _array: T[]
3+
private _effectiveLength: number
4+
5+
constructor(n: number) {
6+
this._array = new Array(n)
7+
this._effectiveLength = 0
8+
}
9+
10+
public toString() {
11+
return '[object RingBuffer(' + this._array.length + ') effectiveLength ' + this._effectiveLength + ']'
12+
}
13+
14+
public length() {
15+
return Math.min(this._array.length, this._effectiveLength)
16+
}
17+
18+
public effectiveLength() {
19+
return this._effectiveLength
20+
}
21+
22+
public get(i: number) {
23+
if (i < 0 || i >= this.length()) return undefined
24+
const index = this.computeActualIndex(i)
25+
return this._array[index]
26+
}
27+
28+
public set(i: number, v: T) {
29+
if (i < 0 || i >= this.length()) throw new Error('set() Index out of range')
30+
const index = this.computeActualIndex(i)
31+
this._array[index] = v
32+
}
33+
34+
public push(v: T) {
35+
const index = this.computeActualIndex(this._effectiveLength)
36+
this._array[index] = v
37+
this._effectiveLength++
38+
}
39+
40+
public clear() {
41+
this._effectiveLength = 0
42+
}
43+
44+
public *[Symbol.iterator]() {
45+
for (let i = 0; i < this.length(); i++) {
46+
yield this.get(i)
47+
}
48+
}
49+
50+
private computeActualIndex(offset: number) {
51+
return Math.max((this._effectiveLength - this._array.length, 0) + offset) % this._array.length
52+
}
53+
}

engine/src/main/battlecode/common/GameConstants.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public class GameConstants {
99
/**
1010
* The current spec version the server compiles with.
1111
*/
12-
public static final String SPEC_VERSION = "2.0.2";
12+
public static final String SPEC_VERSION = "3.0.0";
1313

1414
// *********************************
1515
// ****** MAP CONSTANTS ************
@@ -90,7 +90,7 @@ public class GameConstants {
9090
public static final int PASSIVE_CRUMBS_INCREASE = 10;
9191

9292
/** The amount of crumbs you gain if your bot kills an enemy while in enemy territory */
93-
public static final int KILL_CRUMB_REWARD = 50;
93+
public static final int KILL_CRUMB_REWARD = 30;
9494

9595
/** The end of the setup rounds in the game */
9696
public static final int SETUP_ROUNDS = 200;

0 commit comments

Comments
 (0)