Skip to content

Commit 9b8e66a

Browse files
committed
implement: glacier/awareness system, with 480 bots
1 parent 5b5ce3e commit 9b8e66a

File tree

9 files changed

+136
-26
lines changed

9 files changed

+136
-26
lines changed

s/logic/dungeons/startup.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,14 @@ export function dungeonStartup(simtron: Simtron, layout: DungeonLayout) {
4848
}
4949

5050
{
51-
const howManyBots = 30
51+
const howManyBots = 500
52+
const botsPerCell = 5
53+
const startCell = 1
5254
const radius = constants.crusader.radius
5355
const diameter = radius * 2
5456
let count = 0
55-
const startCell = 1
5657
out: for (const {sector, cell, tiles} of [...layout.floors].slice(startCell)) {
57-
for (const spawn of randy.take(5, tiles.array())) {
58+
for (const spawn of randy.take(botsPerCell, tiles.array())) {
5859
if (count >= howManyBots)
5960
break out
6061

s/logic/entities/bot/simula.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,19 @@ export const botSimula = simula<RogueEntities, Station>()<"bot">(
1717
)
1818

1919
const mind = new Mind(station, id)
20+
const popsicle = station.glacier.add(bipedSim.body.box.center)
2021

2122
return {
2223
simulate: tick => {
23-
bipedSim.activity = mind.behave(tick, getState().biped)
24-
bipedSim.wake()
25-
bipedSim.simulate(tick)
24+
popsicle.point.set(bipedSim.body.box.center)
25+
if (!popsicle.frozen) {
26+
bipedSim.activity = mind.behave(tick, getState().biped)
27+
bipedSim.wake()
28+
bipedSim.simulate(tick)
29+
}
2630
},
2731
dispose: () => {
32+
station.glacier.delete(popsicle)
2833
bipedSim.dispose()
2934
},
3035
}

s/logic/entities/crusader/simula.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,26 @@ import {Vec2} from "@benev/toolbox"
44
import {RogueEntities} from "../entities.js"
55
import {constants} from "../../../constants.js"
66
import {Station} from "../../station/station.js"
7+
import {Box2} from "../../physics/shapes/box2.js"
78
import {BipedSim} from "../../commons/biped/sim.js"
89
import {Coordinates} from "../../realm/utils/coordinates.js"
910
import {simula} from "../../../archimedes/framework/simulation/types.js"
1011

1112
export const crusaderSimula = simula<RogueEntities, Station>()<"crusader">(
1213
({id, station, getState, fromAuthor}) => {
1314

15+
const {author} = getState()
16+
const area = new Box2(
17+
Coordinates.zero(),
18+
constants.sim.localSnapshotArea,
19+
)
20+
const aware = station.awareness.add(author, area)
21+
1422
const bipedSim = new BipedSim(
1523
id,
1624
station,
1725
() => getState().biped,
18-
{...constants.crusader},
26+
constants.crusader,
1927
)
2028

2129
let input: RogueEntities["crusader"]["input"] = {
@@ -39,10 +47,12 @@ export const crusaderSimula = simula<RogueEntities, Station>()<"crusader">(
3947

4048
bipedSim.wake()
4149
bipedSim.simulate(tick)
50+
4251
const coordinates = Coordinates.from(bipedSim.body.box.center)
43-
station.updateAuthorCoordinates(state.author, coordinates)
52+
area.center.set(coordinates)
4453
},
4554
dispose: () => {
55+
station.awareness.delete(aware)
4656
bipedSim.dispose()
4757
},
4858
}

s/logic/physics/facilities/collisions2.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ import {Circle} from "../shapes/circle.js"
66

77
export const Collisions2 = {
88
pointVsBox(point: Vec2, box: Box2) {
9+
const {min, max} = box
910
return (
10-
point.x >= box.min.x &&
11-
point.x <= box.max.x &&
12-
point.y >= box.min.y &&
13-
point.y <= box.max.y
11+
point.x >= min.x &&
12+
point.x <= max.x &&
13+
point.y >= min.y &&
14+
point.y <= max.y
1415
)
1516
},
1617

s/logic/physics/shapes/box2.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
import {Vec2} from "@benev/toolbox"
3+
import {Collisions2} from "../facilities/collisions2.js"
34

45
export class Box2 {
56
constructor(
@@ -33,6 +34,10 @@ export class Box2 {
3334
return this
3435
}
3536

37+
contains(point: Vec2) {
38+
return Collisions2.pointVsBox(point, this)
39+
}
40+
3641
clone() {
3742
return new Box2(this.center.clone(), this.extent.clone())
3843
}

s/logic/station/simtron.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export class Simtron {
2929
}
3030

3131
simulate(tick: number, inputs: InputShell<any>[], physicsTiming = new TimingReport()) {
32+
this.station.update(tick)
3233
this.simulator.simulate(tick, inputs)
3334
const dungeon = this.station.possibleDungeon
3435
if (dungeon) {
@@ -62,8 +63,8 @@ export class Simtron {
6263
}
6364

6465
getTailoredSnapshot(author: number) {
65-
const coordinates = this.station.getAuthorCoordinates(author)
66-
// console.log(`author #${author} coordinates ${coordinates.toString()}`)
66+
const aware = this.station.awareness.awares.require(author)
67+
const coordinates = aware.area.center
6768
const entityIds = new Set([
6869
...this.station.importantEntities,
6970
...this.station.entityHashgrid.queryItems(

s/logic/station/station.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
11

2-
import {Vec2} from "@benev/toolbox"
2+
import {Glacier} from "./utils/glacier.js"
3+
import {constants} from "../../constants.js"
34
import {Dungeon} from "../dungeons/dungeon.js"
5+
import {Awareness} from "./utils/awareness.js"
46
import {deferPromise, Map2} from "@benev/slate"
57
import {DungeonStore} from "../dungeons/store.js"
68
import {ZenGrid} from "../../tools/hash/zen-grid.js"
7-
import {Coordinates} from "../realm/utils/coordinates.js"
8-
import {constants} from "../../constants.js"
99

1010
export class Station {
1111
#dungeon: Dungeon | null = null
1212
#readyPromise = deferPromise<void>()
13-
#authorCoordinates = new Map2<number, Coordinates>()
1413

1514
// fixed timestep for simulation
1615
readonly seconds = 1 / constants.sim.tickRate
1716

1817
importantEntities = new Set<number>()
1918
entityHashgrid = new ZenGrid<number>(constants.sim.hashgridExtent)
19+
awareness = new Awareness()
20+
glacier = new Glacier(this.awareness)
2021

2122
constructor(public dungeonStore: DungeonStore) {}
2223

24+
update(_tick: number) {
25+
this.glacier.recompute()
26+
}
27+
2328
get ready() {
2429
return this.#readyPromise.promise
2530
}
@@ -38,13 +43,5 @@ export class Station {
3843
get possibleDungeon() {
3944
return this.#dungeon
4045
}
41-
42-
getAuthorCoordinates(author: number) {
43-
return this.#authorCoordinates.guarantee(author, () => Coordinates.zero())
44-
}
45-
46-
updateAuthorCoordinates(author: number, coordinates: Coordinates) {
47-
this.getAuthorCoordinates(author).set(coordinates)
48-
}
4946
}
5047

s/logic/station/utils/awareness.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
2+
import {Map2} from "@benev/slate"
3+
import {Box2} from "../../physics/shapes/box2.js"
4+
5+
export class Aware {
6+
constructor(
7+
public author: number,
8+
public area: Box2,
9+
) {}
10+
}
11+
12+
export class Awareness {
13+
awares = new Map2<number, Aware>()
14+
15+
add(author: number, area: Box2) {
16+
const aware = new Aware(author, area)
17+
this.awares.set(author, aware)
18+
return aware
19+
}
20+
21+
delete(aware: Aware) {
22+
this.awares.delete(aware.author)
23+
}
24+
25+
list() {
26+
return this.awares.values()
27+
}
28+
}
29+

s/logic/station/utils/glacier.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
2+
import {Vec2} from "@benev/toolbox"
3+
import {Awareness} from "./awareness.js"
4+
import {range} from "../../../tools/range.js"
5+
6+
export class Popsicle {
7+
frozen = true
8+
constructor(public point: Vec2) {}
9+
}
10+
11+
export class Glacier {
12+
#all = new Set<Popsicle>
13+
#buckets: Set<Popsicle>[]
14+
#adder: BucketIndexer
15+
#updater: BucketIndexer
16+
17+
constructor(
18+
public awareness: Awareness,
19+
public readonly bucketCount = 8,
20+
) {
21+
this.#buckets = range(bucketCount).map(_ => new Set())
22+
this.#adder = new BucketIndexer(this.#buckets)
23+
this.#updater = new BucketIndexer(this.#buckets)
24+
}
25+
26+
add(point: Vec2) {
27+
const popsicle = new Popsicle(point)
28+
this.#all.add(popsicle)
29+
this.#adder.getNextBucket().add(popsicle)
30+
return popsicle
31+
}
32+
33+
delete(popsicle: Popsicle) {
34+
this.#all.delete(popsicle)
35+
for (const bucket of this.#buckets)
36+
bucket.delete(popsicle)
37+
}
38+
39+
recompute() {
40+
const bucket = this.#updater.getNextBucket()
41+
for (const popsicle of bucket)
42+
popsicle.frozen = !this.#isSeen(popsicle)
43+
}
44+
45+
#isSeen(popsicle: Popsicle) {
46+
for (const aware of this.awareness.awares.values())
47+
if (aware.area.contains(popsicle.point))
48+
return true
49+
return false
50+
}
51+
}
52+
53+
class BucketIndexer {
54+
index = 0
55+
constructor(public buckets: Set<Popsicle>[]) {}
56+
getNextBucket() {
57+
this.index = (this.index + 1) % this.buckets.length
58+
return this.buckets[this.index]
59+
}
60+
}
61+

0 commit comments

Comments
 (0)