Skip to content

Commit

Permalink
Merge branch 'master' into public-release
Browse files Browse the repository at this point in the history
  • Loading branch information
TheApplePieGod committed Jan 7, 2025
2 parents 620f59e + d5c70b6 commit ac7f8d5
Show file tree
Hide file tree
Showing 14 changed files with 210 additions and 78 deletions.
62 changes: 48 additions & 14 deletions client/src/components/sidebar/map-editor/MapGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import Match from '../../../playback/Match'
import { CurrentMap, StaticMap } from '../../../playback/Map'
import Round from '../../../playback/Round'
import Bodies from '../../../playback/Bodies'
import { BATTLECODE_YEAR, DIRECTIONS } from '../../../constants'
import { BATTLECODE_YEAR, DIRECTIONS, TEAM_COLOR_NAMES } from '../../../constants'
import { nativeAPI } from '../runner/native-api-wrapper'
import { Vector } from '../../../playback/Vector'
import { RobotType } from 'battlecode-schema/js/battlecode/schema'

export function loadFileAsMap(file: File): Promise<Game> {
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -54,8 +55,6 @@ function verifyMap(map: CurrentMap, bodies: Bodies): string {

// Validate map elements
let numWalls = 0
let numPaintTowers = 0
let numMoneyTowers = 0
const mapSize = map.width * map.height
for (let i = 0; i < mapSize; i++) {
const pos = map.indexToLocation(i)
Expand Down Expand Up @@ -104,8 +103,6 @@ function verifyMap(map: CurrentMap, bodies: Bodies): string {
}
}

numPaintTowers += body && body.robotType === schema.RobotType.PAINT_TOWER ? 1 : 0
numMoneyTowers += body && body.robotType === schema.RobotType.MONEY_TOWER ? 1 : 0
numWalls += wall
}

Expand All @@ -117,24 +114,61 @@ function verifyMap(map: CurrentMap, bodies: Bodies): string {
}

// Validate initial bodies
if (numPaintTowers !== 2) {
return `Expected exactly 2 paint towers, found ${numPaintTowers}`
}
if (numMoneyTowers !== 2) {
return `Expected exactly 2 money towers, found ${numMoneyTowers}`
}
const numPaintTowers = [0, 0]
const numMoneyTowers = [0, 0]
for (const body of bodies.bodies.values()) {
// Check distance to nearby ruins
// Check distance to nearby ruins, towers, and walls

if (body.robotType === RobotType.PAINT_TOWER) {
numPaintTowers[body.team.id - 1]++
} else if (body.robotType === RobotType.MONEY_TOWER) {
numMoneyTowers[body.team.id - 1]++
} else {
return `Tower at (${body.pos.x}, ${body.pos.y}) has invalid type!`
}

for (const checkRuin of map.staticMap.ruins) {
if (squareIntersects(checkRuin, body.pos, 2)) {
if (squareIntersects(checkRuin, body.pos, 4)) {
return (
`Tower at (${body.pos.x}, ${body.pos.y}) is too close to ruin ` +
`at (${checkRuin.x}, ${checkRuin.y}), must be ` +
`>= 3 away`
`>= 5 away`
)
}
}

for (const checkBody of bodies.bodies.values()) {
if (checkBody === body) continue
if (squareIntersects(checkBody.pos, body.pos, 4)) {
return (
`Tower at (${body.pos.x}, ${body.pos.y}) is too close to ruin ` +
`at (${checkBody.pos.x}, ${checkBody.pos.y}), must be ` +
`>= 5 away`
)
}
}

const wall = map.staticMap.walls.findIndex(
(v, i) => !!v && squareIntersects(map.indexToLocation(i), body.pos, 2)
)
if (wall !== -1) {
const pos = map.indexToLocation(wall)
return (
`Tower at (${body.pos.x}, ${body.pos.y}) is too close to wall ` +
`at (${pos.x}, ${pos.y}), must be ` +
`>= 3 away`
)
}
}

for (const teamIdx of [0, 1]) {
if (numPaintTowers[teamIdx] !== 2) {
return `Expected exactly 2 ${TEAM_COLOR_NAMES[teamIdx]} paint towers, found ${numPaintTowers[teamIdx]}`
}

if (numMoneyTowers[teamIdx] !== 2) {
return `Expected exactly 2 ${TEAM_COLOR_NAMES[teamIdx]} money towers, found ${numMoneyTowers[teamIdx]}`
}
}

return ''
Expand Down
2 changes: 1 addition & 1 deletion client/src/components/sidebar/map-editor/map-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ export const MapEditorPage: React.FC<Props> = (props) => {
GameRunner.setMatch(editGame.current.currentMatch)

const round = editGame.current.currentMatch!.currentRound
const brushes = round.map.getEditorBrushes().concat(round.bodies.getEditorBrushes(round.map.staticMap))
const brushes = round.map.getEditorBrushes(round).concat(round.bodies.getEditorBrushes(round))
brushes[0].open = true
setBrushes(brushes)
setCleared(round.bodies.isEmpty() && round.map.isEmpty())
Expand Down
2 changes: 1 addition & 1 deletion client/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const CLIENT_VERSION = '1.0.0'
export const CLIENT_VERSION = '1.1.0'
export const SPEC_VERSION = '1'
export const BATTLECODE_YEAR: number = 2025
export const MAP_SIZE_RANGE = {
Expand Down
13 changes: 11 additions & 2 deletions client/src/playback/Bodies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,8 +187,8 @@ export default class Bodies {
return this.bodies.size === 0
}

getEditorBrushes(map: StaticMap): MapEditorBrush[] {
return [new TowerBrush(this, map)]
getEditorBrushes(round: Round): MapEditorBrush[] {
return [new TowerBrush(round)]
}

toInitialBodyTable(builder: flatbuffers.Builder): number {
Expand Down Expand Up @@ -381,6 +381,15 @@ export class Body {
const squares2 = this.getAllLocationsWithinRadiusSquared(match, pos, this.metadata.visionRadiusSquared())
this.drawEdges(match, ctx, lightly, squares2)

// Currently vision/message radius are always the same
/*
ctx.beginPath()
ctx.strokeStyle = 'brown'
ctx.lineWidth = 0.1
const squares3 = this.getAllLocationsWithinRadiusSquared(match, pos, this.metadata.messageRadiusSquared())
this.drawEdges(match, ctx, lightly, squares3)
*/

ctx.globalAlpha = 1
}

Expand Down
92 changes: 59 additions & 33 deletions client/src/playback/Brushes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Bodies from './Bodies'
import { CurrentMap, StaticMap } from './Map'
import { Vector } from './Vector'
import { Team } from './Game'
import Round from './Round'

const applyInRadius = (
map: CurrentMap | StaticMap,
Expand Down Expand Up @@ -40,7 +41,35 @@ const squareIntersects = (check: Vector, center: Vector, radius: number) => {
)
}

const checkValidRuinPlacement = (check: Vector, map: StaticMap, bodies: Bodies) => {
// Check if ruin is too close to the border
if (check.x <= 1 || check.x >= map.width - 2 || check.y <= 1 || check.y >= map.height - 2) {
return false
}

// Check if this is a valid ruin location
const idx = map.locationToIndex(check.x, check.y)
const ruin = map.ruins.findIndex((l) => squareIntersects(l, check, 4))
const wall = map.walls.findIndex((v, i) => !!v && squareIntersects(map.indexToLocation(i), check, 2))
const paint = map.initialPaint[idx]

let tower = undefined
for (const b of bodies.bodies.values()) {
if (squareIntersects(check, b.pos, 4)) {
tower = b
break
}
}

if (tower || ruin !== -1 || wall !== -1 || paint) {
return false
}

return true
}

export class WallsBrush extends SymmetricMapEditorBrush<StaticMap> {
private readonly bodies: Bodies
public readonly name = 'Walls'
public readonly fields = {
shouldAdd: {
Expand All @@ -54,8 +83,9 @@ export class WallsBrush extends SymmetricMapEditorBrush<StaticMap> {
}
}

constructor(map: StaticMap) {
super(map)
constructor(round: Round) {
super(round.map.staticMap)
this.bodies = round.bodies
}

public symmetricApply(x: number, y: number, fields: Record<string, MapEditorBrushField>) {
Expand All @@ -64,7 +94,17 @@ export class WallsBrush extends SymmetricMapEditorBrush<StaticMap> {
const pos = this.map.indexToLocation(idx)
const ruin = this.map.ruins.findIndex((l) => squareIntersects(l, pos, 2))
const paint = this.map.initialPaint[idx]
if (ruin !== -1 || paint) return true

let tower = undefined
for (const b of this.bodies.bodies.values()) {
if (squareIntersects(pos, b.pos, 2)) {
tower = b
break
}
}

if (tower || ruin !== -1 || paint) return true

this.map.walls[idx] = 1
}

Expand Down Expand Up @@ -94,6 +134,7 @@ export class WallsBrush extends SymmetricMapEditorBrush<StaticMap> {
}

export class RuinsBrush extends SymmetricMapEditorBrush<StaticMap> {
private readonly bodies: Bodies
public readonly name = 'Ruins'
public readonly fields = {
shouldAdd: {
Expand All @@ -102,26 +143,14 @@ export class RuinsBrush extends SymmetricMapEditorBrush<StaticMap> {
}
}

constructor(map: StaticMap) {
super(map)
constructor(round: Round) {
super(round.map.staticMap)
this.bodies = round.bodies
}

public symmetricApply(x: number, y: number, fields: Record<string, MapEditorBrushField>) {
const add = (x: number, y: number) => {
// Check if ruin is too close to the border
if (x <= 1 || x >= this.map.width - 2 || y <= 1 || y >= this.map.height - 2) {
return true
}

// Check if this is a valid ruin location
const pos = { x, y }
const idx = this.map.locationToIndex(x, y)
const ruin = this.map.ruins.findIndex((l) => squareIntersects(l, pos, 4))
const wall = this.map.walls.findIndex(
(v, i) => !!v && squareIntersects(this.map.indexToLocation(i), pos, 2)
)
const paint = this.map.initialPaint[idx]
if (ruin !== -1 || wall !== -1 || paint) {
if (!checkValidRuinPlacement({ x, y }, this.map, this.bodies)) {
return true
}

Expand All @@ -145,6 +174,7 @@ export class RuinsBrush extends SymmetricMapEditorBrush<StaticMap> {
}

export class PaintBrush extends SymmetricMapEditorBrush<CurrentMap> {
private readonly bodies: Bodies
public readonly name = 'Paint'
public readonly fields = {
shouldAdd: {
Expand All @@ -171,8 +201,9 @@ export class PaintBrush extends SymmetricMapEditorBrush<CurrentMap> {
}
}

constructor(map: CurrentMap) {
super(map)
constructor(round: Round) {
super(round.map)
this.bodies = round.bodies
}

public symmetricApply(x: number, y: number, fields: Record<string, MapEditorBrushField>, robotOne: boolean) {
Expand Down Expand Up @@ -217,6 +248,7 @@ export class PaintBrush extends SymmetricMapEditorBrush<CurrentMap> {
}

export class TowerBrush extends SymmetricMapEditorBrush<StaticMap> {
private readonly bodies: Bodies
public readonly name = 'Towers'
public readonly fields = {
isTower: {
Expand All @@ -238,26 +270,20 @@ export class TowerBrush extends SymmetricMapEditorBrush<StaticMap> {
}
}

constructor(
private readonly bodies: Bodies,
map: StaticMap
) {
super(map)
constructor(round: Round) {
super(round.map.staticMap)
this.bodies = round.bodies
}

public symmetricApply(x: number, y: number, fields: Record<string, MapEditorBrushField>, robotOne: boolean) {
const towerType: schema.RobotType = fields.towerType.value
const isTower: boolean = fields.isTower.value

const add = (x: number, y: number, team: Team) => {
// Check if this is a valid tower location
const pos = { x, y }
const idx = this.map.locationToIndex(x, y)
const body = this.bodies.getBodyAtLocation(x, y)
const wall = this.map.walls[idx]
const ruin = this.map.ruins.findIndex((l) => squareIntersects(l, pos, 2))

if (body || wall || ruin !== -1) return null
if (!checkValidRuinPlacement(pos, this.map, this.bodies)) {
return null
}

const id = this.bodies.getNextID()
this.bodies.spawnBodyFromValues(id, towerType, team, pos)
Expand Down
25 changes: 13 additions & 12 deletions client/src/playback/Map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { DIVIDER_COLOR, TILE_COLOR, WALLS_COLOR, PAINT_COLORS, TEAM_COLORS, TEAM
import * as renderUtils from '../util/RenderUtil'
import { getImageIfLoaded } from '../util/ImageLoader'
import { ClientConfig } from '../client-config'
import Round from './Round'

export type Dimension = {
minCorner: Vector
Expand Down Expand Up @@ -121,14 +122,18 @@ export class CurrentMap {
}

if (config.showPaintMarkers) {
// Scale text by 0.5 because sometimes 0.5px text does not work

const markerA = this.markers[0][schemaIdx]
if (markerA) {
ctx.fillStyle = TEAM_COLORS[0]
const label = markerA === 1 ? '1' : '2' // Primary/secondary
ctx.font = '0.5px monospace'
ctx.font = '1px monospace'
ctx.shadowColor = 'black'
ctx.shadowBlur = 4
ctx.fillText(label, coords.x + 0.05, coords.y + 0.95)
ctx.scale(0.5, 0.5)
ctx.fillText(label, (coords.x + 0.05) * 2, (coords.y + 0.95) * 2)
ctx.scale(2, 2)
ctx.shadowColor = ''
ctx.shadowBlur = 0
}
Expand All @@ -137,10 +142,12 @@ export class CurrentMap {
if (markerB) {
ctx.fillStyle = TEAM_COLORS[1]
const label = markerB === 3 ? '1' : '2' // Primary/secondary
ctx.font = '0.5px monospace'
ctx.font = '1px monospace'
ctx.shadowColor = 'black'
ctx.shadowBlur = 4
ctx.fillText(label, coords.x + 0.65, coords.y + 0.95)
ctx.scale(0.5, 0.5)
ctx.fillText(label, (coords.x + 0.65) * 2, (coords.y + 0.95) * 2)
ctx.scale(2, 2)
ctx.shadowColor = ''
ctx.shadowBlur = 0
}
Expand Down Expand Up @@ -185,14 +192,8 @@ export class CurrentMap {
return info
}

getEditorBrushes() {
const brushes: MapEditorBrush[] = [
// ruins brush
// tower brush
new PaintBrush(this),
new RuinsBrush(this.staticMap),
new WallsBrush(this.staticMap)
]
getEditorBrushes(round: Round) {
const brushes: MapEditorBrush[] = [new PaintBrush(round), new RuinsBrush(round), new WallsBrush(round)]
return brushes.concat(this.staticMap.getEditorBrushes())
}

Expand Down
Loading

0 comments on commit ac7f8d5

Please sign in to comment.