add Cloudflare analytics improve a*

This commit is contained in:
evanpelle
2024-08-18 19:54:23 -07:00
parent 03bd25f304
commit 2249771555
7 changed files with 92 additions and 42 deletions
+4 -2
View File
@@ -28,11 +28,11 @@
* Create separate game config dev vs prod DONE 8/17/2024
* improve front page, only one game at a time every 30s DONE 8/17/2024
* create dev server DONE 8/18/2024
* BUG: boats not going to destination, coast not being recognized
* BUG: boats freeze game on path calculation
* BUG: boats freeze game on path calculation DONE 8/18/2024
* better algorithm for name render placement
* make boats larger
* have boats not get close to shore
* use analyitics
* Allow boats to attack TerraNullius
* make coasts look better
* add shader to dim border
@@ -41,3 +41,5 @@
* BUG: ocean is considered TerraNullius
* on websocket connect server only send missing turns not all turns
* BUG: fix hotreload (priority queue breaks it)
* BUG: boat doesn't work if on lake on other player not on lake
* PERF: use hierarchical a* search for boats
+1 -1
View File
@@ -7,7 +7,7 @@ import {GameID, Lobby, ServerMessage, ServerMessageSchema} from "../core/Schemas
import {loadTerrainMap} from "../core/TerrainMapLoader";
import {ClientGame, createClientGame} from "./ClientGame";
import {v4 as uuidv4} from 'uuid';
import backgroundImage from '../../resources/images/World.png';
import backgroundImage from '../../resources/images/empty_map.png';
// import WebSocket from 'ws';
+4 -3
View File
@@ -11,7 +11,7 @@
margin: 0;
padding: 0;
min-height: 100vh;
background-color: #404040;
background-color: #15151500;
/* Dark grey background */
background-image: url('/resources/images/watercolor_worldmap.jpg');
background-size: cover;
@@ -130,8 +130,9 @@
</div>
</div>
<!-- Your existing script file -->
<script src="your-existing-script.js"></script>
<!-- Cloudflare Web Analytics -->
<script defer src='https://static.cloudflareinsights.com/beacon.min.js'
data-cf-beacon='{"token": "03d93e6fefb349c28ee69b408fa25a13"}'></script><!-- End Cloudflare Web Analytics -->
</body>
</html>
+2 -1
View File
@@ -27,7 +27,7 @@ export class DefaultConfig implements Config {
theme(): Theme {return pastelTheme;}
}
export const defaultPlayerConfig = new class implements PlayerConfig {
export class DefaultPlayerConfig implements PlayerConfig {
attackLogic(attacker: Player, defender: Player | TerraNullius, tileToConquer: Tile): {attackerTroopLoss: number; defenderTroopLoss: number; tilesPerTickUsed: number} {
if (defender.isPlayer()) {
@@ -80,3 +80,4 @@ export const defaultPlayerConfig = new class implements PlayerConfig {
}
export const defaultConfig = new DefaultConfig()
export const defaultPlayerConfig = new DefaultPlayerConfig()
+18 -1
View File
@@ -1,4 +1,6 @@
import {DefaultConfig} from "./DefaultConfig";
import {PlayerInfo} from "../Game";
import {PlayerConfig} from "./Config";
import {DefaultConfig, DefaultPlayerConfig, defaultPlayerConfig} from "./DefaultConfig";
export const devConfig = new class extends DefaultConfig {
gameCreationRate(): number {
@@ -7,4 +9,19 @@ export const devConfig = new class extends DefaultConfig {
lobbyLifetime(): number {
return 10 * 1000
}
turnIntervalMs(): number {
return 100
}
player(): PlayerConfig {
return devPlayerConfig
}
}
export const devPlayerConfig = new class extends DefaultPlayerConfig {
startTroops(playerInfo: PlayerInfo): number {
if (playerInfo.isBot) {
return 10
}
return 5000
}
}
+62 -33
View File
@@ -1,5 +1,5 @@
import PriorityQueue from "priority-queue-typescript";
import {Boat, Cell, Execution, MutableBoat, MutableGame, MutablePlayer, Player, PlayerID, Tile} from "../Game";
import {Boat, Cell, Execution, MutableBoat, MutableGame, MutablePlayer, Player, PlayerID, TerrainTypes, Tile} from "../Game";
import {manhattanDist} from "../Util";
import {AttackExecution} from "./AttackExecution";
import {Config, PlayerConfig} from "../configuration/Config";
@@ -26,6 +26,11 @@ export class BoatAttackExecution implements Execution {
private boat: MutableBoat
private aStarPre: AStar
private aStarComplete: AStar
private finalPath = false
constructor(
private attackerID: PlayerID,
private targetID: PlayerID | null,
@@ -54,8 +59,9 @@ export class BoatAttackExecution implements Execution {
this.active = false
return
}
this.path = this.computePath(this.src, this.dst)
this.aStarPre = new AStar(this.src, this.dst)
this.aStarPre.compute(10000)
this.path = this.aStarPre.reconstructPath()
if (this.path != null) {
console.log(`got path ${this.path.map(t => t.cell().toString())}`)
this.boat = this.attacker.addBoat(1000, this.src, this.target)
@@ -63,6 +69,7 @@ export class BoatAttackExecution implements Execution {
console.log('got null path')
this.active = false
}
this.aStarComplete = new AStar(this.path[this.path.length - 1], this.dst)
}
tick(ticks: number) {
@@ -73,9 +80,16 @@ export class BoatAttackExecution implements Execution {
return
}
this.lastMove = ticks
this.currTileIndex++
if (!this.finalPath && this.aStarComplete.compute(10000)) {
this.path.push(...this.aStarComplete.reconstructPath())
this.finalPath = true
}
if (this.currTileIndex >= this.path.length) {
if (!this.finalPath) {
return
}
if (this.dst.owner() == this.attacker) {
this.attacker.addTroops(this.troops)
this.active = false
@@ -89,6 +103,7 @@ export class BoatAttackExecution implements Execution {
const nextTile = this.path[this.currTileIndex]
this.boat.move(nextTile)
this.currTileIndex++
}
owner(): MutablePlayer {
@@ -111,45 +126,60 @@ export class BoatAttackExecution implements Execution {
return currentDistance < closestDistance ? current : closest;
});
}
}
private computePath(src: Tile, dst: Tile): Tile[] {
if (!src.onShore() || !dst.onShore()) {
return null; // Both source and destination must be on water
}
export class AStar {
private openSet: PriorityQueue<{tile: Tile, fScore: number}>;
private cameFrom: Map<Tile, Tile>;
private gScore: Map<Tile, number>;
private current: Tile | null;
public completed: boolean;
const openSet = new PriorityQueue<{tile: Tile, fScore: number}>(
11,
constructor(private src: Tile, private dst: Tile) {
this.openSet = new PriorityQueue<{tile: Tile, fScore: number}>(
500,
(a, b) => a.fScore - b.fScore
);
const cameFrom = new Map<Tile, Tile>();
const gScore = new Map<Tile, number>();
this.cameFrom = new Map<Tile, Tile>();
this.gScore = new Map<Tile, number>();
this.current = null;
this.completed = false;
gScore.set(src, 0);
openSet.add({tile: src, fScore: this.heuristic(src, dst)});
this.gScore.set(src, 0);
this.openSet.add({tile: src, fScore: this.heuristic(src, dst)});
}
while (!openSet.empty()) {
const current = openSet.poll()!.tile;
compute(iterations: number): boolean {
if (this.completed) return true;
if (current === dst) {
return this.reconstructPath(cameFrom, current);
while (!this.openSet.empty()) {
iterations--
this.current = this.openSet.poll()!.tile;
if (iterations <= 0) {
return false
}
for (const neighbor of current.neighbors()) {
if (!neighbor.onShore()) continue; // Skip non-water tiles
if (this.current === this.dst) {
this.completed = true;
return true;
}
const tentativeGScore = gScore.get(current)! + 1; // Assuming uniform cost
for (const neighbor of this.current.neighbors()) {
if (neighbor != this.dst && neighbor.terrain() != TerrainTypes.Water) continue; // Skip non-water tiles
if (!gScore.has(neighbor) || tentativeGScore < gScore.get(neighbor)!) {
cameFrom.set(neighbor, current);
gScore.set(neighbor, tentativeGScore);
const fScore = tentativeGScore + this.heuristic(neighbor, dst);
const tentativeGScore = this.gScore.get(this.current)! + 1; // Assuming uniform cost
openSet.add({tile: neighbor, fScore: fScore});
if (!this.gScore.has(neighbor) || tentativeGScore < this.gScore.get(neighbor)!) {
this.cameFrom.set(neighbor, this.current);
this.gScore.set(neighbor, tentativeGScore);
const fScore = tentativeGScore + this.heuristic(neighbor, this.dst);
this.openSet.add({tile: neighbor, fScore: fScore});
}
}
}
return null; // No path found
return this.completed;
}
private heuristic(a: Tile, b: Tile): number {
@@ -157,13 +187,12 @@ export class BoatAttackExecution implements Execution {
return Math.abs(a.cell().x - b.cell().x) + Math.abs(a.cell().y - b.cell().y);
}
private reconstructPath(cameFrom: Map<Tile, Tile>, current: Tile): Tile[] {
const path = [current];
while (cameFrom.has(current)) {
current = cameFrom.get(current)!;
path.unshift(current);
public reconstructPath(): Tile[] {
const path = [this.current!];
while (this.cameFrom.has(this.current!)) {
this.current = this.cameFrom.get(this.current!)!;
path.unshift(this.current);
}
return path;
}
}
+1 -1
View File
@@ -1,5 +1,5 @@
declare module '*.png' {
const content: string;
const content: string;
export default content;
}
declare module '*.jpg' {