mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-24 01:38:55 +00:00
use TileRef instead of tile
This commit is contained in:
@@ -15,9 +15,16 @@ export interface Rectangle {
|
||||
}
|
||||
|
||||
|
||||
|
||||
export function placeName(game: Game, player: Player): NameViewData {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
size: 0
|
||||
}
|
||||
const boundingBox = calculateBoundingBox(player.borderTiles());
|
||||
|
||||
|
||||
const rawScalingFactor = (boundingBox.max.x - boundingBox.min.x) / 100
|
||||
const scalingFactor = within(Math.floor(rawScalingFactor), 1, 1000)
|
||||
|
||||
|
||||
@@ -14,7 +14,9 @@ export class TileView {
|
||||
constructor(private game: GameView, public data: TileUpdate, private _terrain: TerrainTile) { }
|
||||
|
||||
ref(): TileRef {
|
||||
throw new Error('uh oh')
|
||||
if (!this.data) { return 0 }
|
||||
|
||||
return this.data.pos.x * this.game.width() + this.data.pos.y
|
||||
}
|
||||
type(): TerrainType {
|
||||
return this._terrain.type()
|
||||
@@ -123,6 +125,9 @@ export class UnitView implements Unit {
|
||||
export class PlayerView implements Player {
|
||||
|
||||
constructor(private game: GameView, public data: PlayerUpdate, public nameData: NameViewData) { }
|
||||
borderTiles(): ReadonlySet<Tile> {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
async actions(tile: Tile): Promise<PlayerActions> {
|
||||
return this.game.worker.playerInteraction(this.id(), tile)
|
||||
@@ -187,7 +192,7 @@ export class PlayerView implements Player {
|
||||
allianceWith(other: Player): Alliance | null {
|
||||
return null
|
||||
}
|
||||
borderTiles(): ReadonlySet<Tile> {
|
||||
borderTileRefs(): ReadonlySet<TileRef> {
|
||||
return new Set()
|
||||
}
|
||||
units(...types: UnitType[]): Unit[] {
|
||||
|
||||
+12
-11
@@ -8,6 +8,7 @@ import { number } from 'zod';
|
||||
import { GameConfig, GameID, GameRecord, PlayerRecord, Turn } from './Schemas';
|
||||
import { customAlphabet, nanoid } from 'nanoid';
|
||||
import { GameView } from './GameView';
|
||||
import { TileRef } from './game/GameMap';
|
||||
|
||||
|
||||
|
||||
@@ -110,19 +111,19 @@ function closestOceanShoreTN(tile: Tile, searchDist: number): Tile {
|
||||
}
|
||||
|
||||
export function bfs(tile: Tile, filter: (tile: Tile) => boolean): Set<Tile> {
|
||||
const seen = new Set<Tile>
|
||||
const seen = new Map<TileRef, Tile>()
|
||||
const q: Tile[] = []
|
||||
q.push(tile)
|
||||
while (q.length > 0) {
|
||||
const curr = q.pop()
|
||||
seen.add(curr)
|
||||
seen.set(curr.ref(), curr)
|
||||
for (const n of curr.neighbors()) {
|
||||
if (!seen.has(n) && filter(n)) {
|
||||
if (!seen.has(n.ref()) && filter(n)) {
|
||||
q.push(n)
|
||||
}
|
||||
}
|
||||
}
|
||||
return seen
|
||||
return new Set(seen.values())
|
||||
}
|
||||
|
||||
export function simpleHash(str: string): number {
|
||||
@@ -166,20 +167,20 @@ export function inscribed(outer: { min: Cell; max: Cell }, inner: { min: Cell; m
|
||||
);
|
||||
}
|
||||
|
||||
export function getMode(list: string[]): string {
|
||||
export function getMode(list: number[]): number {
|
||||
// Count occurrences
|
||||
const counts: { [key: string]: number } = {};
|
||||
const counts = new Map<number, number>()
|
||||
for (const item of list) {
|
||||
counts[item] = (counts[item] || 0) + 1;
|
||||
counts.set(item, (counts.get(item) || 0) + 1);
|
||||
}
|
||||
|
||||
// Find the item with the highest count
|
||||
let mode = '';
|
||||
let mode = 0;
|
||||
let maxCount = 0;
|
||||
|
||||
for (const item in counts) {
|
||||
if (counts[item] > maxCount) {
|
||||
maxCount = counts[item];
|
||||
for (const [item, count] of counts) {
|
||||
if (count > maxCount) {
|
||||
maxCount = count
|
||||
mode = item;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,7 +189,8 @@ export class DefaultConfig implements Config {
|
||||
attackLogic(attackTroops: number, attacker: Player, defender: Player | TerraNullius, tileToConquer: MutableTile): { attackerTroopLoss: number; defenderTroopLoss: number; tilesPerTickUsed: number } {
|
||||
let mag = 0
|
||||
let speed = 0
|
||||
switch (tileToConquer.terrain().type()) {
|
||||
const type = tileToConquer.terrain().type()
|
||||
switch (type) {
|
||||
case TerrainType.Plains:
|
||||
mag = 80
|
||||
speed = 15
|
||||
@@ -202,9 +203,12 @@ export class DefaultConfig implements Config {
|
||||
mag = 120
|
||||
speed = 25
|
||||
break
|
||||
default:
|
||||
throw new Error(`terrain type ${type} not supported`)
|
||||
}
|
||||
mag *= tileToConquer.defenseBonus(attacker)
|
||||
speed *= tileToConquer.defenseBonus(attacker)
|
||||
// TODO
|
||||
// mag *= tileToConquer.defenseBonus(attacker)
|
||||
// speed *= tileToConquer.defenseBonus(attacker)
|
||||
if (tileToConquer.hasFallout()) {
|
||||
mag *= this.falloutDefenseModifier()
|
||||
speed *= this.falloutDefenseModifier()
|
||||
|
||||
@@ -4,6 +4,7 @@ import { PseudoRandom } from "../PseudoRandom";
|
||||
import { manhattanDist } from "../Util";
|
||||
import { MessageType } from '../game/Game';
|
||||
import { renderNumber } from "../../client/Utils";
|
||||
import { TileRef } from "../game/GameMap";
|
||||
|
||||
export class AttackExecution implements Execution {
|
||||
private breakAlliance = false
|
||||
@@ -25,7 +26,7 @@ export class AttackExecution implements Execution {
|
||||
|
||||
private mg: MutableGame
|
||||
|
||||
private border = new Set<Tile>()
|
||||
private border = new Set<TileRef>()
|
||||
|
||||
constructor(
|
||||
private troops: number | null,
|
||||
@@ -153,7 +154,7 @@ export class AttackExecution implements Execution {
|
||||
}
|
||||
|
||||
const tileToConquer = this.toConquer.dequeue().tile
|
||||
this.border.delete(tileToConquer)
|
||||
this.border.delete(tileToConquer.ref())
|
||||
|
||||
const onBorder = tileToConquer.neighbors().filter(t => t.owner() == this._owner).length > 0
|
||||
if (tileToConquer.owner() != this.target || !onBorder) {
|
||||
@@ -176,7 +177,7 @@ export class AttackExecution implements Execution {
|
||||
if (neighbor.terrain().isWater() || neighbor.owner() != this.target) {
|
||||
continue
|
||||
}
|
||||
this.border.add(neighbor)
|
||||
this.border.add(neighbor.ref())
|
||||
let numOwnedByMe = neighbor.neighbors()
|
||||
.filter(t => t.terrain().isLand())
|
||||
.filter(t => t.owner() == this._owner)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {Cell, Execution, MutableGame, MutablePlayer, Player, PlayerID, PlayerInfo, PlayerType, TerraNullius} from "../game/Game"
|
||||
import {PseudoRandom} from "../PseudoRandom"
|
||||
import {simpleHash} from "../Util";
|
||||
import {AttackExecution} from "./AttackExecution";
|
||||
import { Cell, Execution, MutableGame, MutablePlayer, Player, PlayerID, PlayerInfo, PlayerType, TerraNullius } from "../game/Game"
|
||||
import { PseudoRandom } from "../PseudoRandom"
|
||||
import { simpleHash } from "../Util";
|
||||
import { AttackExecution } from "./AttackExecution";
|
||||
|
||||
export class BotExecution implements Execution {
|
||||
|
||||
|
||||
@@ -400,7 +400,7 @@ export class FakeHumanExecution implements Execution {
|
||||
}
|
||||
|
||||
if (oceanShore == null) {
|
||||
oceanShore = Array.from(this.player.borderTiles()).filter(t => t.terrain().isOceanShore())
|
||||
oceanShore = Array.from(this.player.borderTileRefs()).filter(t => this.mg.M.isOceanShore(t)).map(tr => this.mg.fromRef(tr))
|
||||
}
|
||||
if (oceanShore.length == 0) {
|
||||
return
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Execution, MutableGame, MutablePlayer, Player, PlayerID, TerraNullius,
|
||||
import { bfs, calculateBoundingBox, getMode, inscribed, simpleHash } from "../Util"
|
||||
import { GameImpl } from "../game/GameImpl"
|
||||
import { consolex } from "../Consolex"
|
||||
import { TileRef } from "../game/GameMap"
|
||||
|
||||
export class PlayerExecution implements Execution {
|
||||
|
||||
@@ -79,7 +80,8 @@ export class PlayerExecution implements Execution {
|
||||
if (this.player.lastTileChange() > this.lastCalc) {
|
||||
this.lastCalc = ticks
|
||||
const start = performance.now()
|
||||
this.removeClusters()
|
||||
// TODO
|
||||
// this.removeClusters()
|
||||
const end = performance.now()
|
||||
if (end - start > 1000) {
|
||||
consolex.log(`player ${this.player.name()}, took ${end - start}ms`)
|
||||
@@ -109,15 +111,15 @@ export class PlayerExecution implements Execution {
|
||||
}
|
||||
}
|
||||
|
||||
private surroundedBySamePlayer(cluster: Set<Tile>): false | Player {
|
||||
const enemies = new Set<Player>()
|
||||
for (const tile of cluster) {
|
||||
if (tile.terrain().isOceanShore() || tile.neighbors().find(n => !n.hasOwner())) {
|
||||
private surroundedBySamePlayer(cluster: Set<TileRef>): false | Player {
|
||||
const enemies = new Set<number>()
|
||||
for (const ref of cluster) {
|
||||
if (this.mg.M.isOceanShore(ref) || this.mg.M.neighbors(ref).some(n => !this.mg.M.hasOwner(n))) {
|
||||
return false
|
||||
}
|
||||
tile.neighbors()
|
||||
.filter(n => n.hasOwner() && n.owner() != this.player)
|
||||
.forEach(p => enemies.add(p.owner() as Player))
|
||||
this.mg.M.neighbors(ref)
|
||||
.filter(n => this.mg.M.ownerID(n) != this.player.smallID())
|
||||
.forEach(p => enemies.add(this.mg.M.ownerID(p)))
|
||||
if (enemies.size != 1) {
|
||||
return false
|
||||
}
|
||||
@@ -125,63 +127,63 @@ export class PlayerExecution implements Execution {
|
||||
if (enemies.size != 1) {
|
||||
return false
|
||||
}
|
||||
return Array.from(enemies)[0]
|
||||
return this.mg.playerBySmallID(Array.from(enemies)[0]) as Player
|
||||
}
|
||||
|
||||
private isSurrounded(cluster: Set<Tile>): boolean {
|
||||
let enemyTiles = new Set<Tile>()
|
||||
for (const tile of cluster) {
|
||||
if (tile.terrain().isOceanShore()) {
|
||||
private isSurrounded(cluster: Set<TileRef>): boolean {
|
||||
let enemyTiles = new Set<TileRef>()
|
||||
for (const tr of cluster) {
|
||||
if (this.mg.M.isOceanShore(tr)) {
|
||||
return false
|
||||
}
|
||||
tile.neighbors()
|
||||
.filter(n => n.hasOwner() && n.owner() != this.player)
|
||||
this.mg.M.neighbors(tr)
|
||||
.filter(n => this.mg.M.ownerID(n) != this.player.smallID())
|
||||
.forEach(n => enemyTiles.add(n))
|
||||
}
|
||||
if (enemyTiles.size == 0) {
|
||||
return false
|
||||
}
|
||||
const enemyBox = calculateBoundingBox(enemyTiles)
|
||||
const clusterBox = calculateBoundingBox(cluster)
|
||||
const enemyBox = calculateBoundingBox(new Set(Array.from(enemyTiles).map(tr => this.mg.fromRef(tr))))
|
||||
const clusterBox = calculateBoundingBox(new Set(Array.from(cluster).map(tr => this.mg.fromRef(tr))))
|
||||
return inscribed(enemyBox, clusterBox)
|
||||
}
|
||||
|
||||
private removeCluster(cluster: Set<Tile>) {
|
||||
private removeCluster(cluster: Set<TileRef>) {
|
||||
const arr = Array.from(cluster)
|
||||
if (arr.some(t => t.owner() != this.player)) {
|
||||
// Other removeCluster operations could change tile owners,
|
||||
// so double check.
|
||||
return
|
||||
}
|
||||
const mode = getMode(arr.flatMap(t => t.neighbors()).filter(t => t.hasOwner() && t.owner() != this.player).map(t => t.owner().id()))
|
||||
if (!this.mg.hasPlayer(mode)) {
|
||||
const mode = getMode(
|
||||
arr.
|
||||
flatMap(t => this.mg.M.neighbors(t))
|
||||
.filter(t => this.mg.M.ownerID(t) != this.player.smallID())
|
||||
.map(t => this.mg.M.ownerID(t))
|
||||
)
|
||||
if (!this.mg.playerBySmallID(mode).isPlayer()) {
|
||||
consolex.warn('mode is not found')
|
||||
return
|
||||
}
|
||||
const firstTile = arr[0]
|
||||
const filter = (n: Tile): boolean => n.owner() == firstTile.owner()
|
||||
const tiles = bfs(firstTile, filter)
|
||||
const filter = (n: Tile): boolean => n.owner().smallID() == this.mg.M.ownerID(firstTile)
|
||||
const tiles = bfs(this.mg.fromRef(firstTile), filter)
|
||||
|
||||
const modePlayer = this.mg.player(mode)
|
||||
if (modePlayer == null) {
|
||||
const modePlayer = this.mg.playerBySmallID(mode)
|
||||
if (!modePlayer.isPlayer()) {
|
||||
consolex.warn('mode player is null')
|
||||
}
|
||||
for (const tile of tiles) {
|
||||
modePlayer.conquer(tile)
|
||||
(modePlayer as MutablePlayer).conquer(tile)
|
||||
}
|
||||
}
|
||||
|
||||
private calculateClusters(): Set<Tile>[] {
|
||||
const seen = new Set<Tile>()
|
||||
const border = this.player.borderTiles()
|
||||
const clusters: Set<Tile>[] = []
|
||||
private calculateClusters(): Set<TileRef>[] {
|
||||
const seen = new Set<TileRef>()
|
||||
const border = this.player.borderTileRefs()
|
||||
const clusters: Set<TileRef>[] = []
|
||||
for (const tile of border) {
|
||||
if (seen.has(tile)) {
|
||||
continue
|
||||
}
|
||||
|
||||
const cluster = new Set<Tile>()
|
||||
const queue: Tile[] = [tile]
|
||||
const cluster = new Set<TileRef>()
|
||||
const queue: TileRef[] = [tile]
|
||||
seen.add(tile)
|
||||
let loops = 0;
|
||||
while (queue.length > 0) {
|
||||
@@ -189,12 +191,12 @@ export class PlayerExecution implements Execution {
|
||||
const curr = queue.shift()
|
||||
cluster.add(curr)
|
||||
|
||||
const neighbors = (this.mg as GameImpl).neighborsWithDiag(curr)
|
||||
const neighbors = (this.mg as GameImpl).neighborsWithDiag(this.mg.fromRef(curr))
|
||||
for (const neighbor of neighbors) {
|
||||
if (neighbor.isBorder() && border.has(neighbor)) {
|
||||
if (!seen.has(neighbor)) {
|
||||
queue.push(neighbor)
|
||||
seen.add(neighbor)
|
||||
if (neighbor.isBorder() && border.has(neighbor.ref())) {
|
||||
if (!seen.has(neighbor.ref())) {
|
||||
queue.push(neighbor.ref())
|
||||
seen.add(neighbor.ref())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,6 +272,7 @@ export interface Player {
|
||||
type(): PlayerType
|
||||
units(...types: UnitType[]): Unit[]
|
||||
isAlive(): boolean
|
||||
borderTileRefs(): ReadonlySet<TileRef>
|
||||
borderTiles(): ReadonlySet<Tile>
|
||||
isPlayer(): this is Player
|
||||
numTilesOwned(): number
|
||||
@@ -316,7 +317,7 @@ export interface MutablePlayer extends Player {
|
||||
neighbors(): (Player | TerraNullius)[]
|
||||
tiles(): ReadonlySet<MutableTile>
|
||||
ownsTile(cell: Cell): boolean
|
||||
tiles(): ReadonlySet<MutableTile>
|
||||
tiles(): ReadonlySet<Tile>
|
||||
conquer(tile: Tile): void
|
||||
relinquish(tile: Tile): void
|
||||
executions(): Execution[]
|
||||
@@ -354,6 +355,7 @@ export interface MutablePlayer extends Player {
|
||||
}
|
||||
|
||||
export interface Game {
|
||||
M: GameMap
|
||||
// Throws exception is player not found
|
||||
player(id: PlayerID): Player
|
||||
playerByClientID(id: ClientID): Player | null
|
||||
@@ -376,7 +378,8 @@ export interface Game {
|
||||
displayMessage(message: string, type: MessageType, playerID: PlayerID | null): void
|
||||
units(...types: UnitType[]): Unit[]
|
||||
unitInfo(type: UnitType): UnitInfo
|
||||
|
||||
playerBySmallID(id: number): Player | TerraNullius
|
||||
fromRef(ref: TileRef): Tile
|
||||
map(): GameMap
|
||||
miniMap(): GameMap
|
||||
}
|
||||
|
||||
+29
-20
@@ -27,6 +27,8 @@ export class GameImpl implements MutableGame {
|
||||
private nations_: Nation[] = []
|
||||
|
||||
_players: Map<PlayerID, PlayerImpl> = new Map<PlayerID, PlayerImpl>
|
||||
_playersBySmallID = []
|
||||
|
||||
private execs: Execution[] = []
|
||||
private _width: number
|
||||
private _height: number
|
||||
@@ -41,14 +43,14 @@ export class GameImpl implements MutableGame {
|
||||
private updates: GameUpdates = createGameUpdatesMap()
|
||||
|
||||
constructor(
|
||||
private gameMap: GameMap,
|
||||
public readonly M: GameMap,
|
||||
private miniGameMap: GameMap,
|
||||
nationMap: NationMap,
|
||||
private _config: Config,
|
||||
) {
|
||||
this._terraNullius = new TerraNulliusImpl()
|
||||
this._width = gameMap.width();
|
||||
this._height = gameMap.height();
|
||||
this._width = M.width();
|
||||
this._height = M.height();
|
||||
this.nations_ = nationMap.nations
|
||||
.map(n => new Nation(
|
||||
n.name,
|
||||
@@ -56,8 +58,14 @@ export class GameImpl implements MutableGame {
|
||||
n.strength
|
||||
))
|
||||
}
|
||||
playerBySmallID(id: number): Player | TerraNullius {
|
||||
if (id == 0) {
|
||||
return this.terraNullius()
|
||||
}
|
||||
return this._playersBySmallID[id - 1]
|
||||
}
|
||||
map(): GameMap {
|
||||
return this.gameMap
|
||||
return this.M
|
||||
}
|
||||
miniMap(): GameMap {
|
||||
return this.miniGameMap
|
||||
@@ -79,7 +87,7 @@ export class GameImpl implements MutableGame {
|
||||
if (tile.hasOwner()) {
|
||||
throw Error(`cannot set fallout, tile ${tile} has owner`)
|
||||
}
|
||||
this.gameMap.setFallout(tile.ref(), true)
|
||||
this.M.setFallout(tile.ref(), true)
|
||||
this.addUpdate(ti.toUpdate())
|
||||
}
|
||||
|
||||
@@ -271,6 +279,7 @@ export class GameImpl implements MutableGame {
|
||||
|
||||
addPlayer(playerInfo: PlayerInfo, manpower: number): MutablePlayer {
|
||||
let player = new PlayerImpl(this, this.nextPlayerID, playerInfo, manpower)
|
||||
this._playersBySmallID.push(player)
|
||||
this.nextPlayerID++
|
||||
this._players.set(playerInfo.id, player)
|
||||
return player
|
||||
@@ -295,7 +304,7 @@ export class GameImpl implements MutableGame {
|
||||
|
||||
tile(cell: Cell): MutableTile {
|
||||
this.assertIsOnMap(cell)
|
||||
return new TileImpl(this, this.gameMap.ref(cell.x, cell.y))
|
||||
return new TileImpl(this, this.M.ref(cell.x, cell.y))
|
||||
}
|
||||
|
||||
isOnMap(cell: Cell): boolean {
|
||||
@@ -310,7 +319,7 @@ export class GameImpl implements MutableGame {
|
||||
}
|
||||
|
||||
neighbors(tile: Tile): Tile[] {
|
||||
return this.gameMap.neighbors(tile.ref()).map(tr => new TileImpl(this, tr))
|
||||
return this.M.neighbors(tile.ref()).map(tr => new TileImpl(this, tr))
|
||||
}
|
||||
|
||||
neighborsWithDiag(tile: Tile): Tile[] {
|
||||
@@ -323,7 +332,7 @@ export class GameImpl implements MutableGame {
|
||||
const newX = x + dx
|
||||
const newY = y + dy
|
||||
if (newX >= 0 && newX < this._width && newY >= 0 && newY < this._height) {
|
||||
ns.push(this.fromRef(this.gameMap.ref(newX, newY)))
|
||||
ns.push(this.fromRef(this.M.ref(newX, newY)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -345,14 +354,14 @@ export class GameImpl implements MutableGame {
|
||||
if (previousOwner.isPlayer()) {
|
||||
previousOwner._lastTileChange = this._ticks
|
||||
previousOwner._tiles.delete(tile.cell().toString())
|
||||
previousOwner._borderTiles.delete(tileImpl)
|
||||
this.gameMap.setBorder(tileImpl.ref(), false)
|
||||
previousOwner._borderTiles.delete(tileImpl.ref())
|
||||
this.M.setBorder(tileImpl.ref(), false)
|
||||
}
|
||||
this.gameMap.setPlayerId(tileImpl.ref(), owner.smallID())
|
||||
this.M.setOwnerID(tileImpl.ref(), owner.smallID())
|
||||
owner._tiles.set(tile.cell().toString(), tile)
|
||||
owner._lastTileChange = this._ticks
|
||||
this.updateBorders(tile)
|
||||
this.gameMap.setFallout(tileImpl.ref(), false)
|
||||
this.M.setFallout(tileImpl.ref(), false)
|
||||
this.addUpdate((tile as TileImpl).toUpdate())
|
||||
}
|
||||
|
||||
@@ -368,10 +377,10 @@ export class GameImpl implements MutableGame {
|
||||
let previousOwner = tileImpl.owner() as PlayerImpl
|
||||
previousOwner._lastTileChange = this._ticks
|
||||
previousOwner._tiles.delete(tile.cell().toString())
|
||||
previousOwner._borderTiles.delete(tileImpl)
|
||||
this.gameMap.setBorder(tileImpl.ref(), false)
|
||||
previousOwner._borderTiles.delete(tileImpl.ref())
|
||||
this.M.setBorder(tileImpl.ref(), false)
|
||||
|
||||
this.gameMap.setPlayerId(tileImpl.ref(), 0)
|
||||
this.M.setOwnerID(tileImpl.ref(), 0)
|
||||
this.updateBorders(tile)
|
||||
this.addUpdate(
|
||||
(tile as TileImpl).toUpdate()
|
||||
@@ -385,15 +394,15 @@ export class GameImpl implements MutableGame {
|
||||
|
||||
for (const t of tiles) {
|
||||
if (!t.hasOwner()) {
|
||||
this.gameMap.setBorder(t.ref(), false)
|
||||
this.M.setBorder(t.ref(), false)
|
||||
continue
|
||||
}
|
||||
if (this.isBorder(t)) {
|
||||
(t.owner() as PlayerImpl)._borderTiles.add(t);
|
||||
this.gameMap.setBorder(t.ref(), true)
|
||||
(t.owner() as PlayerImpl)._borderTiles.add(t.ref());
|
||||
this.M.setBorder(t.ref(), true)
|
||||
} else {
|
||||
(t.owner() as PlayerImpl)._borderTiles.delete(t);
|
||||
this.gameMap.setBorder(t.ref(), false)
|
||||
(t.owner() as PlayerImpl)._borderTiles.delete(t.ref());
|
||||
this.M.setBorder(t.ref(), false)
|
||||
}
|
||||
// this.updates.push(t.toUpdate())
|
||||
}
|
||||
|
||||
@@ -65,6 +65,10 @@ export class GameMap {
|
||||
return Boolean(this.terrain[ref] & (1 << GameMap.IS_LAND_BIT));
|
||||
}
|
||||
|
||||
isOceanShore(ref: TileRef): boolean {
|
||||
return this.isLand(ref) && this.neighbors(ref).some(tr => this.isOcean(tr))
|
||||
}
|
||||
|
||||
isOcean(ref: TileRef): boolean {
|
||||
return Boolean(this.terrain[ref] & (1 << GameMap.OCEAN_BIT));
|
||||
}
|
||||
@@ -78,11 +82,16 @@ export class GameMap {
|
||||
}
|
||||
|
||||
// State getters and setters (mutable)
|
||||
playerId(ref: TileRef): number {
|
||||
ownerID(ref: TileRef): number {
|
||||
return this.state[ref] & GameMap.PLAYER_ID_MASK;
|
||||
}
|
||||
|
||||
setPlayerId(ref: TileRef, playerId: number): void {
|
||||
hasOwner(ref: TileRef): boolean {
|
||||
return this.ownerID(ref) != 0
|
||||
}
|
||||
|
||||
|
||||
setOwnerID(ref: TileRef, playerId: number): void {
|
||||
if (playerId > GameMap.PLAYER_ID_MASK) {
|
||||
throw new Error(`Player ID ${playerId} exceeds maximum value ${GameMap.PLAYER_ID_MASK}`);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { TileImpl } from "./TileImpl";
|
||||
import { MessageType } from './Game';
|
||||
import { renderTroops } from "../../client/Utils";
|
||||
import { TerraNulliusImpl } from "./TerraNulliusImpl";
|
||||
import { TileRef } from "./GameMap";
|
||||
|
||||
interface Target {
|
||||
tick: Tick
|
||||
@@ -28,7 +29,7 @@ export class PlayerImpl implements MutablePlayer {
|
||||
|
||||
isTraitor_ = false
|
||||
|
||||
public _borderTiles: Set<TileImpl> = new Set();
|
||||
public _borderTiles: Set<TileRef> = new Set();
|
||||
|
||||
public _units: UnitImpl[] = [];
|
||||
public _tiles: Map<CellString, Tile> = new Map<CellString, TileImpl>();
|
||||
@@ -111,8 +112,8 @@ export class PlayerImpl implements MutablePlayer {
|
||||
|
||||
sharesBorderWith(other: Player | TerraNullius): boolean {
|
||||
for (const border of this._borderTiles) {
|
||||
for (const neighbor of border.neighbors()) {
|
||||
if (neighbor.owner() == other) {
|
||||
for (const neighbor of this.gs.map().neighbors(border)) {
|
||||
if (this.gs.map().ownerID(neighbor) == other.smallID()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -127,16 +128,23 @@ export class PlayerImpl implements MutablePlayer {
|
||||
return new Set(this._tiles.values()) as Set<MutableTile>;
|
||||
}
|
||||
|
||||
borderTiles(): ReadonlySet<MutableTile> {
|
||||
borderTileRefs(): ReadonlySet<TileRef> {
|
||||
return this._borderTiles;
|
||||
}
|
||||
|
||||
borderTiles(): ReadonlySet<Tile> {
|
||||
return new Set(Array.from(this._borderTiles).map(tr => this.gs.fromRef(tr)))
|
||||
}
|
||||
|
||||
neighbors(): (MutablePlayer | TerraNullius)[] {
|
||||
const ns: Set<(MutablePlayer | TerraNullius)> = new Set();
|
||||
for (const border of this.borderTiles()) {
|
||||
for (const neighbor of border.neighbors()) {
|
||||
if (neighbor.terrain().isLand() && neighbor.owner() != this) {
|
||||
ns.add(neighbor.owner() as PlayerImpl | TerraNulliusImpl);
|
||||
for (const border of this.borderTileRefs()) {
|
||||
for (const neighbor of this.gs.map().neighbors(border)) {
|
||||
if (this.gs.map().isLake(neighbor)) {
|
||||
const owner = this.gs.map().ownerID(neighbor)
|
||||
if (owner != this.smallID()) {
|
||||
ns.add(this.gs.playerBySmallID(owner) as PlayerImpl | TerraNulliusImpl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,11 +82,13 @@ export class TileImpl implements MutableTile {
|
||||
}
|
||||
|
||||
hasOwner(): boolean { return this.owner().smallID() != 0 }
|
||||
|
||||
owner(): MutablePlayer | TerraNullius {
|
||||
const ownerID = this.gs.map().playerId(this.ref_)
|
||||
const ownerID = this.gs.map().ownerID(this.ref_)
|
||||
if (ownerID == 0) {
|
||||
return this.gs.terraNullius()
|
||||
}
|
||||
return this.gs.playerBySmallID(ownerID) as MutablePlayer
|
||||
}
|
||||
isBorder(): boolean { return this.gs.map().isBorder(this.ref_); }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user