removed sharesborderwith cache

This commit is contained in:
evanpelle
2024-08-10 12:29:35 -07:00
parent ca5cc49763
commit 5efd9f4779
7 changed files with 69 additions and 138 deletions
+4 -2
View File
@@ -1,9 +1,11 @@
* fix conquer expansion DONE
* perf improvements on graphics (only draw images to canvas on ticks) DONE
* double join lobby bug
* better troop addition logic
* use draw rect instead of image data
* improve front page
* make boats larger
* better troop addition logic
* maybe cache neigbors?
* have boats not get close to shore
* better algorithm for name render placement
* better algorithm for name render placement
* re-enable directed expansion
+1 -1
View File
@@ -148,7 +148,7 @@ export class ClientGame {
const owner = tile.owner()
const targetID = owner.isPlayer() ? owner.id() : null
if (tile.owner() != this.myPlayer) {
if (this.myPlayer.sharesBorderWith(tile.owner())) {
if (this.myPlayer.sharesBorderWith(tile.owner())) {
this.sendAttackIntent(targetID, cell)
} else {
// TODO verify on ocean
+7 -5
View File
@@ -9,12 +9,17 @@ export type GameID = string
export type LobbyID = string
export class Cell {
private strRepr: string
constructor(
public readonly x,
public readonly y
) { }
) {
this.strRepr = `Cell[${this.x},${this.y}]`
}
toString(): string {return `Cell[${this.x},${this.y}]`}
toString(): string {return this.strRepr}
}
export interface ExecutionView {
@@ -85,8 +90,6 @@ export interface MutableBoat extends Boat {
export interface TerraNullius {
ownsTile(cell: Cell): boolean
isPlayer(): false
borderTilesWith(other: Player): ReadonlySet<Tile>
sharesBorderWith(other: Player): boolean
}
export interface Player {
@@ -98,7 +101,6 @@ export interface Player {
isAlive(): boolean
executions(): ExecutionView[]
borderTiles(): ReadonlySet<Tile>
borderTilesWith(other: Player | TerraNullius): ReadonlySet<Tile>
isPlayer(): this is Player
neighbors(): (Player | TerraNullius)[]
numTilesOwned(): number
+27 -102
View File
@@ -67,77 +67,14 @@ export class BoatImpl implements MutableBoat {
}
}
class Border {
borderWith: Map<Player | TerraNullius, Set<Tile>> = new Map()
public _borderTiles: Map<CellString, Tile> = new Map()
constructor(private gs: GameImpl, private player: Player | TerraNullius) { }
sharesBorderWith(other: Player | TerraNullius): boolean {
if (!this.borderWith.has(other)) {
return false
}
return this.borderWith.get(other).size > 0
}
addCalcBorderWithTile(tile: Tile) {
this.gs.neighbors(tile.cell()).map(c => this.gs.tile(c)).forEach(t => {
this.insertBorderWithTile(tile, t.owner())
})
}
removeCalcBorderWithTile(tile: Tile, oldNeighbor: Player | TerraNullius) {
const length = this.gs.neighbors(tile.cell()).map(c => this.gs.tile(c)).filter(t => t.owner() == oldNeighbor).length
if (length == 0) {
this.deleteBorderWithTile(tile, oldNeighbor)
}
}
insertBorderWithTile(tile: Tile, player: Player | TerraNullius) {
if (!this.borderWith.has(player)) {
this.borderWith.set(player, new Set())
}
if (player != this.player && tile.terrain() == TerrainTypes.Land) {
this.borderWith.get(player).add(tile)
}
}
deleteBorderWithTile(tile: Tile, player: Player | TerraNullius) {
if (!this.borderWith.has(player)) {
this.borderWith.set(player, new Set())
}
this.borderWith.get(player).delete(tile)
}
updateBorderWithTile(tile: Tile, oldOwner: Player | TerraNullius, newOwner: Player | TerraNullius) {
if (!this.borderWith.has(oldOwner)) {
this.borderWith.set(oldOwner, new Set())
}
if (!this.borderWith.has(newOwner)) {
this.borderWith.set(newOwner, new Set())
}
// Delete old neighbors
if (this.gs.tileNeighbors(tile).filter(t => t.owner() == newOwner).length == 0) {
this.borderWith.get(oldOwner).delete(tile)
}
}
borderTiles(): ReadonlySet<Tile> {
return new Set(this._borderTiles.values())
}
borderTilesWith(other: Player | TerraNullius): ReadonlySet<Tile> {
return this.borderWith.get(other) || new Set();
}
}
export class PlayerImpl implements MutablePlayer {
public _borderTiles: Map<CellString, Tile> = new Map()
public _borderTileSet: Set<Tile> = new Set()
public _boats: BoatImpl[] = []
public tiles: Map<CellString, Tile> = new Map<CellString, Tile>()
public border: Border
constructor(private gs: GameImpl, public readonly _id: PlayerID, public readonly playerInfo: PlayerInfo, private _troops) {
this.border = new Border(gs, this)
}
addBoat(troops: number, cell: Cell, target: Player | TerraNullius): BoatImpl {
@@ -150,24 +87,33 @@ export class PlayerImpl implements MutablePlayer {
return this._boats
}
sharesBorderWith(other: Player | TerraNullius): boolean {
return this.border.sharesBorderWith(other)
for (const border of this._borderTileSet) {
for (const neighbor of border.neighbors()) {
if (neighbor.owner() == other) {
return true
}
}
}
return false
}
numTilesOwned(): number {
return this.tiles.size
}
borderTiles(): ReadonlySet<Tile> {
return this.border.borderTiles()
return this._borderTileSet
}
neighbors(): (MutablePlayer | TerraNullius)[] {
const ns: (MutablePlayer | TerraNullius)[] = []
for (const [player, tiles] of this.border.borderWith) {
if (tiles.size > 0) {
ns.push(player as MutablePlayer)
const ns: Set<(MutablePlayer | TerraNullius)> = new Set()
for (const border of this.borderTiles()) {
for (const neighbor of border.neighbors()) {
if (neighbor.owner() != this) {
ns.add((neighbor as TileImpl)._owner)
}
}
}
return ns
return Array.from(ns)
}
addTroops(troops: number): void {
@@ -189,18 +135,13 @@ export class PlayerImpl implements MutablePlayer {
executions(): Execution[] {
return this.gs.executions().filter(exec => exec.owner().id() == this.id())
}
borderTilesWith(other: Player | TerraNullius): ReadonlySet<Tile> {
return this.border.borderTilesWith(other)
}
}
class TerraNulliusImpl implements TerraNullius {
public tiles: Map<Cell, Tile> = new Map<Cell, Tile>()
public border: Border
constructor(private gs: GameImpl) {
this.border = new Border(gs, this)
}
id(): PlayerID {
@@ -211,13 +152,6 @@ class TerraNulliusImpl implements TerraNullius {
}
isPlayer(): false {return false as const}
borderTilesWith(other: Player): ReadonlySet<Tile> {
return this.border.borderTilesWith(other)
}
sharesBorderWith(other: Player): boolean {
return this.border.sharesBorderWith(other)
}
}
export class TerrainMapImpl implements TerrainMap {
@@ -376,15 +310,18 @@ export class GameImpl implements MutableGame {
throw new Error("Must be a player")
}
let tile = this.tile(cell) as TileImpl
if (tile.terrain() == TerrainTypes.Water) {
throw new Error("Cannot conquer water")
}
let previousOwner = tile._owner
if (previousOwner.isPlayer()) {
previousOwner.tiles.delete(cell.toString())
previousOwner.border._borderTiles.delete(cell.toString())
previousOwner._borderTiles.delete(cell.toString())
previousOwner._borderTileSet.delete(tile)
}
tile._owner = owner
owner.tiles.set(cell.toString(), tile)
this.updateBorders(cell)
this.updateBordersWith(tile, previousOwner)
this.eventBus.emit(new TileEvent(tile))
}
@@ -394,27 +331,15 @@ export class GameImpl implements MutableGame {
this.neighbors(cell).forEach(c => cells.push(c))
cells.map(c => this.tile(c)).filter(c => c.hasOwner()).forEach(t => {
if (this.isBorder(t)) {
(t.owner() as PlayerImpl).border._borderTiles.set(t.cell().toString(), t)
(t.owner() as PlayerImpl)._borderTiles.set(t.cell().toString(), t);
(t.owner() as PlayerImpl)._borderTileSet.add(t)
} else {
(t.owner() as PlayerImpl).border._borderTiles.delete(t.cell().toString())
(t.owner() as PlayerImpl)._borderTiles.delete(t.cell().toString());
(t.owner() as PlayerImpl)._borderTileSet.delete(t)
}
})
}
private updateBordersWith(tile: TileImpl, previousOwner: PlayerImpl | TerraNulliusImpl) {
const newOwner = tile._owner
const neighbors = this.neighbors(tile.cell()).map(c => this.tile(c))
newOwner.border.addCalcBorderWithTile(tile)
neighbors.map(t => (t as TileImpl)).forEach(t => {
const p = t._owner
p.border.addCalcBorderWithTile(t)
p.border.removeCalcBorderWithTile(t, previousOwner)
previousOwner.border.deleteBorderWithTile(tile, p)
})
}
isBorder(tile: Tile): boolean {
this.assertIsOnMap(tile.cell())
if (!tile.hasOwner()) {
+1 -1
View File
@@ -25,7 +25,7 @@ export const defaultSettings = new class implements Settings {
return 1
}
turnIntervalMs(): number {
return 1000 / 10
return 1000 / 5
}
lobbyCreationRate(): number {
return 5 * 1000
+28 -26
View File
@@ -43,14 +43,14 @@ export class AttackExecution implements Execution {
// }
let numTilesPerTick = this._owner.borderTilesWith(this.target).size
let numTilesPerTick = this._owner.borderTiles().size / 2
while (numTilesPerTick > 0) {
if (this.troops < 1) {
this.active = false
return
}
if (this.toConquer.size() < this._owner.borderTilesWith(this.target).size / 1.5) {
if (this.toConquer.size() < 10) {
this.calculateToConquer()
}
if (this.toConquer.size() == 0) {
@@ -74,8 +74,6 @@ export class AttackExecution implements Execution {
private calculateToConquer() {
// console.profile('calc_to_conquer')
const enemyBorder = this.target.borderTilesWith(this._owner)
// let closestTile: Tile;
@@ -106,32 +104,36 @@ export class AttackExecution implements Execution {
this.toConquer.clear()
if (this.targetCell != null) {
let tiles = Array.from(enemyBorder)
tiles = tiles.slice().sort((a, b) => manhattanDist(a.cell(), this.targetCell) - manhattanDist(b.cell(), this.targetCell))
for (let i = 0; i < tiles.length; i++) {
const numOwnedByMe = tiles[i].neighbors()
.filter(t => t.terrain() == TerrainTypes.Land)
.filter(t => t.owner() == this._owner)
.length
// if (this.targetCell != null) {
// let tiles = Array.from(enemyBorder)
// tiles = tiles.slice().sort((a, b) => manhattanDist(a.cell(), this.targetCell) - manhattanDist(b.cell(), this.targetCell))
// for (let i = 0; i < tiles.length; i++) {
// const numOwnedByMe = tiles[i].neighbors()
// .filter(t => t.terrain() == TerrainTypes.Land)
// .filter(t => t.owner() == this._owner)
// .length
let distModifer = 0
if (this.targetCell != null) {
distModifer = i / tiles.length * 2
// let distModifer = 0
// if (this.targetCell != null) {
// distModifer = i / tiles.length * 2
// }
// this.toConquer.add(new TileContainer(tiles[i], distModifer - numOwnedByMe + this.random.nextInt(0, 2)))
// // this.toConquer.add(new TileContainer(tiles[i], i))
// }
// } else {
for (const tile of this._owner.borderTiles()) {
for (const neighbor of tile.neighbors()) {
if (neighbor.terrain() == TerrainTypes.Water || neighbor.owner() != this.target) {
continue
}
this.toConquer.add(new TileContainer(tiles[i], distModifer - numOwnedByMe + this.random.nextInt(0, 2)))
// this.toConquer.add(new TileContainer(tiles[i], i))
}
} else {
for (const tile of enemyBorder) {
const numOwnedByMe = tile.neighbors()
.filter(t => t.terrain() == TerrainTypes.Land)
.filter(t => t.owner() == this._owner)
.length
this.toConquer.add(new TileContainer(tile, - numOwnedByMe + this.random.nextInt(0, 2)))
// const numOwnedByMe = tile.neighbors()
// .filter(t => t.terrain() == TerrainTypes.Land)
// .filter(t => t.owner() == this._owner)
// .length
this.toConquer.add(new TileContainer(neighbor, + this.random.nextInt(0, 4)))
}
}
// }
// console.profileEnd('calc_to_conquer')
+1 -1
View File
@@ -35,7 +35,7 @@ export class BotExecution implements Execution {
const toAttack = ns[this.random.nextInt(0, ns.length)]
this.gs.addExecution(new AttackExecution(
this.bot.troops() / 15,
this.bot.troops() / 5,
this.bot.id(),
toAttack.isPlayer() ? toAttack.id() : null,
null