-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathKelpForestModel.js
127 lines (105 loc) · 3.55 KB
/
KelpForestModel.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import * as util from 'https://code.agentscript.org/src/utils.js'
import World from 'https://code.agentscript.org/src/World.js'
import Model from 'https://code.agentscript.org/src/Model.js'
// util.randomSeed() // causes each run to have same results for debugging
class KelpForestModel extends Model {
wiggleAngle = 45
speed = 0.1
numKelp = 500
numUrchin = 50
numSeastar = 5
// worldOptions: patches -16, 16 or 33 x 33 with 0,0 origin
constructor(worldOptions = World.defaultOptions(16)) {
super(worldOptions)
}
setup() {
this.turtleBreeds('kelp urchin seaStar')
this.kelp.create(this.numKelp, t => {
t.moveTo(this.patches.oneOf())
})
this.seaStar.create(this.numSeastar, t => {
t.moveTo(this.patches.oneOf())
})
this.seaStar.setDefault('atEdge', 'bounce')
this.urchin.create(this.numUrchin, t => {
t.moveTo(this.patches.oneOf())
})
}
// we pass ticks as an option defaulting to the model's ticks.
// this allows us to use them from the animator and other environments.
// or outside of step() where this.ticks is off by one.
day(ticks = this.ticks) {
// days go from 0 to 364
return ticks % 365
}
year(ticks = this.ticks) {
// years start at 1
return 1 + Math.floor(ticks / 365)
}
spawnUrchin() {
if (this.day() === 0 && this.year() > 1) {
this.urchin.create(this.urchin.length * 2, t => {
t.moveTo(this.patches.oneOf())
})
}
}
reseedKelp() {
if (this.day() === 0 && this.year() > 1) {
this.kelp.create(this.kelp.length * 3, t => {
t.moveTo(this.patches.oneOf())
})
}
}
spawnSeaStars() {
if (this.day() === 0 && this.year() > 1) {
if (this.seaStar.length > 0) {
this.seaStar.create(2, t => {
t.moveTo(this.patches.oneOf())
})
}
}
}
closestNeighbor(turtle, neighbors) {
let closest = false
if (neighbors.length > 0) {
closest = neighbors.minOneOf(t => t.distance(turtle))
}
return closest
}
step() {
// console.log('year', this.year())
this.urchin.ask(t => {
const closestSeaStar = this.closestNeighbor(t, this.seaStar)
if (closestSeaStar && t.distance(closestSeaStar <= 2)) {
const seaStarHeading = closestSeaStar.heading
t.heading = seaStarHeading * -1
} else {
t.heading += util.randomCentered(this.wiggleAngle)
}
t.forward(this.speed)
})
this.seaStar.ask(t => {
const closestUrchin = this.closestNeighbor(t, this.urchin)
if (closestUrchin && t.distance(closestUrchin) <= 2) {
t.face(closestUrchin)
} else {
t.heading += util.randomCentered(this.wiggleAngle)
}
t.forward(this.speed)
})
this.urchin.forEach(t => {
const kelpHere = this.kelp.filter(kelp => kelp.patch === t.patch)
kelpHere.forEach(kelp => kelp.die())
const seaStarHere = this.seaStar.filter(
seaStar => seaStar.patch === t.patch
)
if (seaStarHere.length > 0) {
t.die()
}
})
this.spawnUrchin()
this.reseedKelp()
this.spawnSeaStars()
}
}
export default KelpForestModel