put methods onto terraintile

This commit is contained in:
Evan
2025-01-02 15:35:13 -08:00
parent 8443095d89
commit dab427d614
30 changed files with 439 additions and 149 deletions
+3 -3
View File
@@ -211,7 +211,7 @@ export class ClientGameRunner {
}
consolex.log(`clicked cell ${cell}`)
const tile = this.gs.tile(cell)
if (tile.isLand() && !tile.hasOwner() && this.gs.inSpawnPhase()) {
if (tile.terrain().isLand() && !tile.hasOwner() && this.gs.inSpawnPhase()) {
this.eventBus.emit(new SendSpawnIntentEvent(cell))
return
}
@@ -233,13 +233,13 @@ export class ClientGameRunner {
return
}
if (tile.isLand()) {
if (tile.terrain().isLand()) {
if (tile.hasOwner()) {
if (this.myPlayer.sharesBorderWith(tile.owner())) {
this.eventBus.emit(new SendAttackIntentEvent(targetID, this.myPlayer.troops() * this.renderer.uiState.attackRatio))
}
} else {
outer_loop: for (const t of bfs(tile, and(t => !t.hasOwner() && t.isLand(), dist(tile, 200)))) {
outer_loop: for (const t of bfs(tile, and(t => !t.hasOwner() && t.terrain().isLand(), dist(tile, 200)))) {
for (const n of t.neighbors()) {
if (n.owner() == this.myPlayer) {
this.eventBus.emit(new SendAttackIntentEvent(targetID, this.myPlayer.troops() * this.renderer.uiState.attackRatio))
+1 -1
View File
@@ -61,7 +61,7 @@ export function createGrid(game: Game, player: Player, boundingBox: { min: Point
const cell = new Cell(x * scalingFactor, y * scalingFactor);
if (game.isOnMap(cell)) {
const tile = game.tile(cell);
grid[x - scaledBoundingBox.min.x][y - scaledBoundingBox.min.y] = tile.isLake() || tile.owner() === player; // TODO: okay if lake
grid[x - scaledBoundingBox.min.x][y - scaledBoundingBox.min.y] = tile.terrain().isLake() || tile.owner() === player; // TODO: okay if lake
}
}
}
@@ -64,7 +64,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
if (owner.isPlayer()) {
this.player = owner;
this.setVisible(true);
} else if (!tile.isLand()) {
} else if (!tile.terrain().isLand()) {
const units = this.game.units(UnitType.Destroyer, UnitType.Battleship, UnitType.TradeShip)
.filter(u => euclideanDist(worldCoord, u.tile().cell()) < 50)
.sort(distSortUnit(tile));
@@ -239,7 +239,7 @@ export class RadialMenu implements Layer {
const other = tile.owner()
if (this.game.inSpawnPhase()) {
if (tile.isLand() && !tile.hasOwner()) {
if (tile.terrain().isLand() && !tile.hasOwner()) {
this.enableCenterButton(true)
}
return
@@ -268,13 +268,13 @@ export class RadialMenu implements Layer {
}
}
if (tile.owner() != myPlayer && tile.isLand() && myPlayer.sharesBorderWith(other)) {
if (tile.owner() != myPlayer && tile.terrain().isLand() && myPlayer.sharesBorderWith(other)) {
if (other.isPlayer()) {
if (!myPlayer.isAlliedWith(other)) {
this.enableCenterButton(true)
}
} else {
outer_loop: for (const t of bfs(tile, and(t => !t.hasOwner() && t.isLand(), dist(tile, 200)))) {
outer_loop: for (const t of bfs(tile, and(t => !t.hasOwner() && t.terrain().isLand(), dist(tile, 200)))) {
for (const n of t.neighbors()) {
if (n.owner() == myPlayer) {
this.enableCenterButton(true)
@@ -321,7 +321,7 @@ export class RadialMenu implements Layer {
}
}
if (!tile.isLand()) {
if (!tile.terrain().isLand()) {
return
}
if (myPlayer.units(UnitType.TransportShip).length >= this.game.config().boatMaxNumber()) {
@@ -330,7 +330,7 @@ export class RadialMenu implements Layer {
let myPlayerBordersOcean = false
for (const bt of myPlayer.borderTiles()) {
if (bt.isOceanShore()) {
if (bt.terrain().isOceanShore()) {
myPlayerBordersOcean = true
break
}
@@ -340,7 +340,7 @@ export class RadialMenu implements Layer {
otherPlayerBordersOcean = true
} else {
for (const bt of (other as Player).borderTiles()) {
if (bt.isOceanShore()) {
if (bt.terrain().isOceanShore()) {
otherPlayerBordersOcean = true
break
}
@@ -352,8 +352,8 @@ export class RadialMenu implements Layer {
}
let nearOcean = false
for (const t of bfs(tile, and(t => t.owner() == tile.owner() && t.isLand(), dist(tile, 25)))) {
if (t.isOceanShore()) {
for (const t of bfs(tile, and(t => t.owner() == tile.owner() && t.terrain().isLand(), dist(tile, 25)))) {
if (t.terrain().isOceanShore()) {
nearOcean = true
break
}
+6 -25
View File
@@ -1,32 +1,12 @@
import { getConfig } from "./configuration/Config";
import { EventBus } from "./EventBus";
import { Executor } from "./execution/ExecutionManager";
import { Game } from "./game/Game";
import { Game, Tile, TileEvent } from "./game/Game";
import { createGame } from "./game/GameImpl";
import { loadTerrainMap } from "./game/TerrainMapLoader";
import { GameUpdateViewData } from "./GameViewData";
import { GameConfig, Turn } from "./Schemas";
export interface GameUpdate {
players: PlayerUpdate[]
units: UnitUpdate[]
}
export interface PlayerUpdate {
}
export interface UnitUpdate {
}
export interface TileUpdate {
x: number
y: number
isBorder: boolean
}
export async function createGameRunner(gameID: string, gameConfig: GameConfig): Promise<GameRunner> {
const config = getConfig(gameConfig)
const terrainMap = await loadTerrainMap(gameConfig.gameMap);
@@ -36,13 +16,14 @@ export async function createGameRunner(gameID: string, gameConfig: GameConfig):
}
export class GameRunner {
private updatedTiles: Tile[]
constructor(private game: Game, private eventBus: EventBus, private execManager: Executor) {
eventBus.on(TileEvent, (e) => { this.updatedTiles.push(e.tile) })
}
public executeNextTick(turn: Turn): GameUpdate {
public executeNextTick(turn: Turn): GameUpdateViewData {
this.updatedTiles = []
this.game.executeNextTick()
return null
}
+304 -11
View File
@@ -1,26 +1,319 @@
import { Cell, PlayerID } from "./game/Game";
import { GameUpdate } from "./GameRunner";
export class TileView {
import { MessageType } from "../client/graphics/layers/EventsDisplay";
import { Config } from "./configuration/Config";
import { Alliance, AllianceRequest, AllPlayers, Cell, DefenseBonus, EmojiMessage, Execution, ExecutionView, Game, Gold, Nation, Player, PlayerID, PlayerInfo, PlayerType, Relation, TerrainMap, TerrainTile, TerrainType, TerraNullius, Tick, Tile, Unit, UnitInfo, UnitType } from "./game/Game";
import { GameUpdateViewData, PlayerViewData, TileViewData, UnitViewData } from "./GameViewData";
import { ClientID } from "./Schemas";
export class TileView implements Tile {
constructor(private data: TileViewData, terrain: TerrainTile) { }
isLand(): boolean {
throw new Error("Method not implemented.");
}
isShore(): boolean {
throw new Error("Method not implemented.");
}
isOceanShore(): boolean {
throw new Error("Method not implemented.");
}
isWater(): boolean {
throw new Error("Method not implemented.");
}
isShorelineWater(): boolean {
throw new Error("Method not implemented.");
}
isOcean(): boolean {
throw new Error("Method not implemented.");
}
isLake(): boolean {
throw new Error("Method not implemented.");
}
terrain(): TerrainTile {
throw new Error("Method not implemented.");
}
magnitude(): number {
throw new Error("Method not implemented.");
}
owner(): Player | TerraNullius {
throw new Error("Method not implemented.");
}
hasOwner(): boolean {
throw new Error("Method not implemented.");
}
isBorder(): boolean {
throw new Error("Method not implemented.");
}
borders(other: Player | TerraNullius): boolean {
throw new Error("Method not implemented.");
}
isInterior(): boolean {
throw new Error("Method not implemented.");
}
cell(): Cell {
throw new Error("Method not implemented.");
}
neighbors(): Tile[] {
throw new Error("Method not implemented.");
}
neighborsWrapped(): Tile[] {
throw new Error("Method not implemented.");
}
onShore(): boolean {
throw new Error("Method not implemented.");
}
defenseBonuses(): DefenseBonus[] {
throw new Error("Method not implemented.");
}
defenseBonus(player: Player): number {
throw new Error("Method not implemented.");
}
hasFallout(): boolean {
throw new Error("Method not implemented.");
}
cost(): number {
throw new Error("Method not implemented.");
}
type(): TerrainType {
throw new Error("Method not implemented.");
}
}
export class PlayerView {
export class UnitView implements Unit {
constructor(private data: UnitViewData) { }
type(): UnitType {
throw new Error("Method not implemented.");
}
troops(): number {
throw new Error("Method not implemented.");
}
tile(): Tile {
throw new Error("Method not implemented.");
}
owner(): Player {
throw new Error("Method not implemented.");
}
isActive(): boolean {
throw new Error("Method not implemented.");
}
info(): UnitInfo {
throw new Error("Method not implemented.");
}
hasHealth(): boolean {
throw new Error("Method not implemented.");
}
health(): number {
throw new Error("Method not implemented.");
}
}
export class GameView {
export class PlayerView implements Player {
constructor(private data: PlayerViewData) { }
info(): PlayerInfo {
throw new Error("Method not implemented.");
}
name(): string {
throw new Error("Method not implemented.");
}
displayName(): string {
throw new Error("Method not implemented.");
}
clientID(): ClientID {
throw new Error("Method not implemented.");
}
id(): PlayerID {
throw new Error("Method not implemented.");
}
type(): PlayerType {
throw new Error("Method not implemented.");
}
units(...types: UnitType[]): Unit[] {
throw new Error("Method not implemented.");
}
ownsTile(cell: Cell): boolean {
throw new Error("Method not implemented.");
}
isAlive(): boolean {
throw new Error("Method not implemented.");
}
borderTiles(): ReadonlySet<Tile> {
throw new Error("Method not implemented.");
}
isPlayer(): this is Player {
throw new Error("Method not implemented.");
}
neighbors(): (Player | TerraNullius)[] {
throw new Error("Method not implemented.");
}
numTilesOwned(): number {
throw new Error("Method not implemented.");
}
tiles(): ReadonlySet<Tile> {
throw new Error("Method not implemented.");
}
sharesBorderWith(other: Player | TerraNullius): boolean {
throw new Error("Method not implemented.");
}
incomingAllianceRequests(): AllianceRequest[] {
throw new Error("Method not implemented.");
}
outgoingAllianceRequests(): AllianceRequest[] {
throw new Error("Method not implemented.");
}
alliances(): Alliance[] {
throw new Error("Method not implemented.");
}
allies(): Player[] {
throw new Error("Method not implemented.");
}
isAlliedWith(other: Player): boolean {
throw new Error("Method not implemented.");
}
allianceWith(other: Player): Alliance | null {
throw new Error("Method not implemented.");
}
recentOrPendingAllianceRequestWith(other: Player): boolean {
throw new Error("Method not implemented.");
}
relation(other: Player): Relation {
throw new Error("Method not implemented.");
}
allRelationsSorted(): { player: Player; relation: Relation; }[] {
throw new Error("Method not implemented.");
}
isTraitor(): boolean {
throw new Error("Method not implemented.");
}
canTarget(other: Player): boolean {
throw new Error("Method not implemented.");
}
targets(): Player[] {
throw new Error("Method not implemented.");
}
transitiveTargets(): Player[] {
throw new Error("Method not implemented.");
}
toString(): string {
throw new Error("Method not implemented.");
}
canSendEmoji(recipient: Player | typeof AllPlayers): boolean {
throw new Error("Method not implemented.");
}
outgoingEmojis(): EmojiMessage[] {
throw new Error("Method not implemented.");
}
canDonate(recipient: Player): boolean {
throw new Error("Method not implemented.");
}
gold(): Gold {
throw new Error("Method not implemented.");
}
population(): number {
throw new Error("Method not implemented.");
}
workers(): number {
throw new Error("Method not implemented.");
}
targetTroopRatio(): number {
throw new Error("Method not implemented.");
}
troops(): number {
throw new Error("Method not implemented.");
}
canBuild(type: UnitType, targetTile: Tile): Tile | false {
throw new Error("Method not implemented.");
}
lastTileChange(): Tick {
throw new Error("Method not implemented.");
}
}
public update(gu: GameUpdate) {
export class GameView implements Game {
private lastGameUpdate: GameUpdateViewData
private tiles: TileViewData[][] = []
constructor(private _terrainMap: TerrainMap) { }
executions(): ExecutionView[] {
throw new Error("Method not implemented.");
}
executeNextTick(): void {
throw new Error("Method not implemented.");
}
tile(cell: Cell): TileView {
return null
public update(gu: GameUpdateViewData) {
this.lastGameUpdate = gu
gu.tileUpdates.forEach(tu => {
this.tiles[tu.x][tu.y] = tu
})
}
player(id: PlayerID): PlayerView {
return null
recentlyUpdatedTiles(): TileView[] {
return this.lastGameUpdate.tileUpdates.map(tu => new TileView(tu, this._terrainMap.terrain(new Cell(tu.x, tu.y))))
}
player(id: PlayerID): Player {
throw new Error("Method not implemented.");
}
playerByClientID(id: ClientID): Player | null {
throw new Error("Method not implemented.");
}
hasPlayer(id: PlayerID): boolean {
throw new Error("Method not implemented.");
}
players(): Player[] {
throw new Error("Method not implemented.");
}
tile(cell: Cell): Tile {
throw new Error("Method not implemented.");
}
isOnMap(cell: Cell): boolean {
throw new Error("Method not implemented.");
}
neighbors(cell: Cell | Tile): Tile[] {
throw new Error("Method not implemented.");
}
width(): number {
throw new Error("Method not implemented.");
}
height(): number {
throw new Error("Method not implemented.");
}
numLandTiles(): number {
throw new Error("Method not implemented.");
}
forEachTile(fn: (tile: Tile) => void): void {
throw new Error("Method not implemented.");
}
terraNullius(): TerraNullius {
throw new Error("Method not implemented.");
}
ticks(): Tick {
throw new Error("Method not implemented.");
}
inSpawnPhase(): boolean {
throw new Error("Method not implemented.");
}
addExecution(...exec: Execution[]): void {
throw new Error("Method not implemented.");
}
nations(): Nation[] {
throw new Error("Method not implemented.");
}
config(): Config {
throw new Error("Method not implemented.");
}
displayMessage(message: string, type: MessageType, playerID: PlayerID | null): void {
throw new Error("Method not implemented.");
}
units(...types: UnitType[]): Unit[] {
throw new Error("Method not implemented.");
}
unitInfo(type: UnitType): UnitInfo {
throw new Error("Method not implemented.");
}
terrainMap(): TerrainMap {
throw new Error("Method not implemented.");
}
terrainMiniMap(): TerrainMap {
throw new Error("Method not implemented.");
}
}
+26
View File
@@ -0,0 +1,26 @@
export interface ViewSerialiable {
toViewData(): ViewData
}
export interface ViewData {
}
export interface TileViewData extends ViewData {
x: number
y: number
}
export interface UnitViewData extends ViewData {
}
export interface PlayerViewData extends ViewData {
}
export interface GameUpdateViewData extends ViewData {
units: UnitViewData[]
players: PlayerViewData[]
tileUpdates: TileViewData[]
}
+2 -2
View File
@@ -86,7 +86,7 @@ export function targetTransportTile(game: Game, tile: Tile): Tile | null {
}
export function closestOceanShoreFromPlayer(player: Player, target: Tile, width: number): Tile | null {
const shoreTiles = Array.from(player.borderTiles()).filter(t => t.isOceanShore())
const shoreTiles = Array.from(player.borderTiles()).filter(t => t.terrain().isOceanShore())
if (shoreTiles.length == 0) {
return null
}
@@ -100,7 +100,7 @@ export function closestOceanShoreFromPlayer(player: Player, target: Tile, width:
function closestOceanShoreTN(tile: Tile, searchDist: number): Tile {
const tn = Array.from(bfs(tile, and(t => !t.hasOwner(), dist(tile, searchDist))))
.filter(t => t.isOceanShore())
.filter(t => t.terrain().isOceanShore())
.sort((a, b) => manhattanDist(tile.cell(), a.cell()) - manhattanDist(tile.cell(), b.cell()))
if (tn.length == 0) {
return null
+1 -1
View File
@@ -189,7 +189,7 @@ export class DefaultConfig implements Config {
attackLogic(attackTroops: number, attacker: Player, defender: Player | TerraNullius, tileToConquer: Tile): { attackerTroopLoss: number; defenderTroopLoss: number; tilesPerTickUsed: number } {
let mag = 0
let speed = 0
switch (tileToConquer.terrain()) {
switch (tileToConquer.terrain().type()) {
case TerrainType.Plains:
mag = 80
speed = 15
+5 -5
View File
@@ -155,18 +155,18 @@ export const pastelTheme = new class implements Theme {
}
terrainColor(tile: Tile): Colord {
let mag = tile.magnitude()
if (tile.isShore()) {
let mag = tile.terrain().magnitude()
if (tile.terrain().isShore()) {
return this.shore
}
switch (tile.terrain()) {
switch (tile.terrain().type()) {
case TerrainType.Ocean:
case TerrainType.Lake:
const w = this.water.rgba
if (tile.isShorelineWater()) {
if (tile.terrain().isShorelineWater()) {
return this.shorelineWater
}
if (tile.magnitude() < 7) {
if (tile.terrain().magnitude() < 7) {
return colord({
r: Math.max(w.r - 7 + mag, 0),
g: Math.max(w.g - 7 + mag, 0),
+3 -3
View File
@@ -173,12 +173,12 @@ export class AttackExecution implements Execution {
private addNeighbors(tile: Tile) {
for (const neighbor of tile.neighbors()) {
if (neighbor.isWater() || neighbor.owner() != this.target) {
if (neighbor.terrain().isWater() || neighbor.owner() != this.target) {
continue
}
this.border.add(neighbor)
let numOwnedByMe = neighbor.neighbors()
.filter(t => t.isLand())
.filter(t => t.terrain().isLand())
.filter(t => t.owner() == this._owner)
.length
let dist = 0
@@ -189,7 +189,7 @@ export class AttackExecution implements Execution {
numOwnedByMe = 10
}
let mag = 0
switch (tile.terrain()) {
switch (tile.terrain().type()) {
case TerrainType.Plains:
mag = 1
break
+2 -2
View File
@@ -34,7 +34,7 @@ export class BattleshipExecution implements Execution {
init(mg: MutableGame, ticks: number): void {
this.pathfinder = PathFinder.Mini(mg, 5000, t => t.terrainType() == TerrainType.Ocean)
this.pathfinder = PathFinder.Mini(mg, 5000, t => t.type() == TerrainType.Ocean)
this._owner = mg.player(this.playerID)
this.mg = mg
this.patrolCenterTile = mg.tile(this.cell)
@@ -133,7 +133,7 @@ export class BattleshipExecution implements Execution {
continue
}
const tile = this.mg.tile(cell)
if (!tile.isOcean()) {
if (!tile.terrain().isOcean()) {
continue
}
return tile
+1 -1
View File
@@ -56,7 +56,7 @@ export class BotExecution implements Execution {
if (this.neighborsTerraNullius) {
for (const b of this.bot.borderTiles()) {
for (const n of b.neighbors()) {
if (n.owner() == this.mg.terraNullius() && n.isLand()) {
if (n.owner() == this.mg.terraNullius() && n.terrain().isLand()) {
this.sendAttack(this.mg.terraNullius())
return
}
+1 -1
View File
@@ -34,7 +34,7 @@ export class BotSpawner {
spawnBot(botName: string): SpawnIntent | null {
const tile = this.randTile()
if (!tile.isLand()) {
if (!tile.terrain().isLand()) {
return null
}
for (const spawn of this.bots) {
+1 -1
View File
@@ -30,7 +30,7 @@ export class DefensePostExecution implements Execution {
}
this.post = this.player.buildUnit(UnitType.DefensePost, 0, spawnTile)
bfs(spawnTile, dist(spawnTile, this.mg.config().defensePostRange())).forEach(t => {
if (t.isLand()) {
if (t.terrain().isLand()) {
this.defenseBonuses.push(this.mg.addTileDefenseBonus(t, this.post, this.mg.config().defensePostDefenseBonus()))
}
})
+2 -2
View File
@@ -30,7 +30,7 @@ export class DestroyerExecution implements Execution {
init(mg: MutableGame, ticks: number): void {
this.pathfinder = PathFinder.Mini(mg, 5000, t => t.terrainType() == TerrainType.Ocean)
this.pathfinder = PathFinder.Mini(mg, 5000, t => t.type() == TerrainType.Ocean)
this._owner = mg.player(this.playerID)
this.mg = mg
this.patrolCenterTile = mg.tile(this.cell)
@@ -141,7 +141,7 @@ export class DestroyerExecution implements Execution {
continue
}
const tile = this.mg.tile(cell)
if (!tile.isOcean()) {
if (!tile.terrain().isOcean()) {
continue
}
return tile
+11 -11
View File
@@ -91,7 +91,7 @@ export class FakeHumanExecution implements Execution {
this.handleEnemies()
this.handleUnits()
const enemyborder = Array.from(this.player.borderTiles()).flatMap(t => t.neighbors()).filter(t => t.isLand() && t.owner() != this.player)
const enemyborder = Array.from(this.player.borderTiles()).flatMap(t => t.neighbors()).filter(t => t.terrain().isLand() && t.owner() != this.player)
if (enemyborder.length == 0) {
if (this.random.chance(5)) {
@@ -243,8 +243,8 @@ export class FakeHumanExecution implements Execution {
private maybeSendBoatAttack(other: Player) {
const closest = closestTwoTiles(
Array.from(this.player.borderTiles()).filter(t => t.isOceanShore()),
Array.from(other.borderTiles()).filter(t => t.isOceanShore())
Array.from(this.player.borderTiles()).filter(t => t.terrain().isOceanShore()),
Array.from(other.borderTiles()).filter(t => t.terrain().isOceanShore())
)
if (closest == null) {
return
@@ -262,7 +262,7 @@ export class FakeHumanExecution implements Execution {
private handleUnits() {
const ports = this.player.units(UnitType.Port)
if (ports.length == 0 && this.player.gold() > this.cost(UnitType.Port)) {
const oceanTiles = Array.from(this.player.borderTiles()).filter(t => t.isOceanShore())
const oceanTiles = Array.from(this.player.borderTiles()).filter(t => t.terrain().isOceanShore())
if (oceanTiles.length > 0) {
const buildTile = this.random.randElement(oceanTiles)
this.mg.addExecution(new PortExecution(this.player.id(), buildTile.cell()))
@@ -359,7 +359,7 @@ export class FakeHumanExecution implements Execution {
continue
}
const tile = this.mg.tile(cell)
if (!tile.isOcean()) {
if (!tile.terrain().isOcean()) {
continue
}
return tile
@@ -402,7 +402,7 @@ export class FakeHumanExecution implements Execution {
}
if (oceanShore == null) {
oceanShore = Array.from(this.player.borderTiles()).filter(t => t.isOceanShore())
oceanShore = Array.from(this.player.borderTiles()).filter(t => t.terrain().isOceanShore())
}
if (oceanShore.length == 0) {
return
@@ -412,9 +412,9 @@ export class FakeHumanExecution implements Execution {
const otherShore = Array.from(
bfs(
src,
and((t) => t.isOcean() || t.isOceanShore(), dist(src, 200))
and((t) => t.terrain().isOcean() || t.terrain().isOceanShore(), dist(src, 200))
)
).filter(t => t.isOceanShore() && t.owner() != this.player)
).filter(t => t.terrain().isOceanShore() && t.owner() != this.player)
if (otherShore.length == 0) {
return
@@ -453,8 +453,8 @@ export class FakeHumanExecution implements Execution {
continue
}
const tile = this.mg.tile(cell)
if (tile.isLand() && !tile.hasOwner()) {
if (tile.terrain() == TerrainType.Mountain && this.random.chance(2)) {
if (tile.terrain().isLand() && !tile.hasOwner()) {
if (tile.terrain().type() == TerrainType.Mountain && this.random.chance(2)) {
continue
}
return tile
@@ -474,7 +474,7 @@ export class FakeHumanExecution implements Execution {
}
isSmallIsland(tile: Tile): boolean {
return bfs(tile, and((t) => t.isLand(), dist(tile, 10))).size < 50
return bfs(tile, and((t) => t.terrain().isLand(), dist(tile, 10))).size < 50
}
owner(): MutablePlayer {
+1 -1
View File
@@ -92,7 +92,7 @@ export class NukeExecution implements Execution {
const prev = attacked.get(mp)
attacked.set(mp, prev + 1)
}
if (tile.isLand()) {
if (tile.terrain().isLand()) {
this.mg.addFallout(tile)
}
}
+2 -2
View File
@@ -112,7 +112,7 @@ export class PlayerExecution implements Execution {
private surroundedBySamePlayer(cluster: Set<Tile>): false | Player {
const enemies = new Set<Player>()
for (const tile of cluster) {
if (tile.isOceanShore() || tile.neighbors().find(n => !n.hasOwner())) {
if (tile.terrain().isOceanShore() || tile.neighbors().find(n => !n.hasOwner())) {
return false
}
tile.neighbors()
@@ -131,7 +131,7 @@ export class PlayerExecution implements Execution {
private isSurrounded(cluster: Set<Tile>): boolean {
let enemyTiles = new Set<Tile>()
for (const tile of cluster) {
if (tile.isOceanShore()) {
if (tile.terrain().isOceanShore()) {
return false
}
tile.neighbors()
+3 -3
View File
@@ -41,7 +41,7 @@ export class PortExecution implements Execution {
return
}
const spawns = Array.from(bfs(tile, dist(tile, 20)))
.filter(t => t.isOceanShore() && t.owner() == player)
.filter(t => t.terrain().isOceanShore() && t.owner() == player)
.sort((a, b) => manhattanDist(a.cell(), tile.cell()) - manhattanDist(b.cell(), tile.cell()))
if (spawns.length == 0) {
@@ -89,7 +89,7 @@ export class PortExecution implements Execution {
this.mg.terrainMap(),
this.mg.terrainMiniMap(),
this.port.tile(), port.tile(),
sn => sn.terrainType() == TerrainType.Ocean,
sn => sn.type() == TerrainType.Ocean,
10_000,
25
)
@@ -109,7 +109,7 @@ export class PortExecution implements Execution {
const port = this.random.randElement(portConnections)
const path = this.portPaths.get(port)
if (path != null) {
const pf = PathFinder.Mini(this.mg, 10, (sn) => sn.terrainType() == TerrainType.Ocean)
const pf = PathFinder.Mini(this.mg, 10, (sn) => sn.type() == TerrainType.Ocean)
this.mg.addExecution(new TradeShipExecution(this.player().id(), this.port, port, pf, path))
}
}
+1 -1
View File
@@ -44,7 +44,7 @@ export class TransportShipExecution implements Execution {
init(mg: MutableGame, ticks: number) {
this.lastMove = ticks
this.mg = mg
this.pathFinder = PathFinder.Mini(mg, 10_000, t => t.terrainType() == TerrainType.Ocean, 2)
this.pathFinder = PathFinder.Mini(mg, 10_000, t => t.type() == TerrainType.Ocean, 2)
this.attacker = mg.player(this.attackerID)
+1 -1
View File
@@ -12,7 +12,7 @@ export function getSpawnCells(gs: Game, cell: Cell): Cell[] {
if (Math.abs(dx) === 2 && Math.abs(dy) === 2) {
continue;
}
if (gs.tile(c).isWater()) {
if (gs.tile(c).terrain().isWater()) {
continue;
}
if (gs.tile(c).hasOwner()) {
+10 -12
View File
@@ -162,7 +162,15 @@ export interface TerrainMap {
}
export interface TerrainTile extends SearchNode {
terrainType(): TerrainType
isLand(): boolean
isShore(): boolean
isOceanShore(): boolean
isWater(): boolean
isShorelineWater(): boolean
isOcean(): boolean
isLake(): boolean
type(): TerrainType
magnitude(): number
}
export interface DefenseBonus {
@@ -173,15 +181,6 @@ export interface DefenseBonus {
}
export interface Tile extends SearchNode {
isLand(): boolean
isShore(): boolean
isOceanShore(): boolean
isWater(): boolean
isShorelineWater(): boolean
isOcean(): boolean
isLake(): boolean
terrain(): TerrainType
magnitude(): number
owner(): Player | TerraNullius
hasOwner(): boolean
isBorder(): boolean
@@ -190,12 +189,11 @@ export interface Tile extends SearchNode {
cell(): Cell
neighbors(): Tile[]
neighborsWrapped(): Tile[]
onShore(): boolean
defenseBonuses(): DefenseBonus[]
// defense bonus against this player
defenseBonus(player: Player): number
hasFallout(): boolean
terrain(): TerrainTile
}
export interface Unit {
+1 -1
View File
@@ -342,7 +342,7 @@ export class GameImpl implements MutableGame {
if (!tile.hasOwner()) {
throw new Error(`Cannot relinquish tile because it is unowned: cell ${tile.cell().toString()}`)
}
if (tile.isWater()) {
if (tile.terrain().isWater()) {
throw new Error("Cannot relinquish water")
}
+4 -4
View File
@@ -110,7 +110,7 @@ export class PlayerImpl implements MutablePlayer {
const ns: Set<(MutablePlayer | TerraNullius)> = new Set();
for (const border of this.borderTiles()) {
for (const neighbor of border.neighbors()) {
if (neighbor.isLand() && neighbor.owner() != this) {
if (neighbor.terrain().isLand() && neighbor.owner() != this) {
ns.add((neighbor as TileImpl)._owner);
}
}
@@ -450,7 +450,7 @@ export class PlayerImpl implements MutablePlayer {
portSpawn(tile: Tile): Tile | false {
const spawns = Array.from(bfs(tile, dist(tile, 20)))
.filter(t => t.owner() == this && t.isOceanShore())
.filter(t => t.owner() == this && t.terrain().isOceanShore())
.sort((a, b) => manhattanDist(a.cell(), tile.cell()) - manhattanDist(b.cell(), tile.cell()))
if (spawns.length == 0) {
return false
@@ -459,7 +459,7 @@ export class PlayerImpl implements MutablePlayer {
}
warshipSpawn(tile: Tile): Tile | false {
if (!tile.isOcean()) {
if (!tile.terrain().isOcean()) {
return false
}
const spawns = this.units(UnitType.Port)
@@ -479,7 +479,7 @@ export class PlayerImpl implements MutablePlayer {
}
transportShipSpawn(targetTile: Tile): Tile | false {
if (!targetTile.isOceanShore()) {
if (!targetTile.terrain().isOceanShore()) {
return false
}
const spawn = closestOceanShoreFromPlayer(this, targetTile, this.gs.width())
+30 -8
View File
@@ -20,19 +20,41 @@ export interface Nation {
export class TerrainTileImpl implements TerrainTile {
public shoreline: boolean = false
public magnitude: number = 0
public _magnitude: number = 0
public ocean = false
public land = false
private _neighbors: TerrainTile[] | null = null
constructor(private map: TerrainMap, public type: TerrainType, private _cell: Cell) { }
terrainType(): TerrainType {
return this.type
constructor(private map: TerrainMap, public _type: TerrainType, private _cell: Cell) { }
type(): TerrainType {
return this._type
}
isLake(): boolean {
return !this.isLand() && !this.isOcean();
}
isOcean(): boolean {
return this.ocean;
}
magnitude(): number {
return this._magnitude;
}
isShore(): boolean {
return this.isLand() && this.shoreline;
}
isOceanShore(): boolean {
return this.isShore() && this.neighbors().filter(n => n.isOcean()).length > -1;
}
isShorelineWater(): boolean {
return this.isWater() && this.shoreline;
}
isLand(): boolean {
return this.land;
}
isWater(): boolean {
return !this.land;
}
cost(): number {
return this.magnitude < 10 ? 2 : 1
return this._magnitude < 10 ? 2 : 1
}
cell(): Cell {
@@ -146,7 +168,7 @@ export async function loadTerrainFromFile(fileData: string): Promise<TerrainMapI
terrain[x][y] = new TerrainTileImpl(m, type, new Cell(x, y));
terrain[x][y].shoreline = shoreline;
terrain[x][y].magnitude = magnitude;
terrain[x][y]._magnitude = magnitude;
terrain[x][y].ocean = ocean
terrain[x][y].land = land
}
+5 -35
View File
@@ -26,8 +26,8 @@ export class TileImpl implements Tile {
return this._hasFallout
}
terrainType(): TerrainType {
return this._terrain.type
type(): TerrainType {
return this._terrain._type
}
defenseBonus(player: Player): number {
@@ -77,32 +77,8 @@ export class TileImpl implements Tile {
}
return ns;
}
isLake(): boolean {
return !this.isLand() && !this.isOcean();
}
isOcean(): boolean {
return this._terrain.ocean;
}
magnitude(): number {
return this._terrain.magnitude;
}
isShore(): boolean {
return this.isLand() && this._terrain.shoreline;
}
isOceanShore(): boolean {
return this.isShore() && this.neighbors().filter(n => n.isOcean()).length > 0;
}
isShorelineWater(): boolean {
return this.isWater() && this._terrain.shoreline;
}
isLand(): boolean {
return this._terrain.land;
}
isWater(): boolean {
return !this._terrain.land;
}
terrain(): TerrainType {
return this._terrain.type;
terrain(): TerrainTile {
return this._terrain
}
borders(other: Player | TerraNullius): boolean {
@@ -114,12 +90,6 @@ export class TileImpl implements Tile {
return false;
}
onShore(): boolean {
return this.neighbors()
.filter(t => t.isWater())
.length > 0;
}
hasOwner(): boolean { return this._owner != this.gs._terraNullius; }
owner(): MutablePlayer | TerraNullius { return this._owner; }
isBorder(): boolean { return this._isBorder; }
@@ -140,6 +110,6 @@ export class TileImpl implements Tile {
}
cost(): number {
return this.magnitude() < 10 ? 2 : 1
return this.terrain().magnitude() < 10 ? 2 : 1
};
}
+1 -1
View File
@@ -26,7 +26,7 @@ export interface SearchNode {
cost(): number
cell(): Cell
neighbors(): SearchNode[]
terrainType(): TerrainType
type(): TerrainType
}
export interface Point {
x: number;
+1 -1
View File
@@ -53,7 +53,7 @@ function findPath(terrainMap: TerrainMap, miniTerrainMap: TerrainMap, req: Searc
miniTerrainMap,
terrainMap.terrain(req.start),
terrainMap.terrain(req.end),
(sn: SearchNode) => (sn as TerrainTile).terrainType() == TerrainType.Ocean,
(sn: SearchNode) => (sn as TerrainTile).type() == TerrainType.Ocean,
10_000,
req.duration,
);
+1 -1
View File
@@ -117,7 +117,7 @@ export class ParallelAStar implements AStar {
this.game.terrainMap(),
this.game.terrainMiniMap(),
this.src, this.dst,
(t: TerrainTile) => t.terrainType() == TerrainType.Ocean,
(t: TerrainTile) => t.type() == TerrainType.Ocean,
100_000_000,
20
)