-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcivsim.go
300 lines (272 loc) · 7.52 KB
/
civsim.go
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
package main
import "fmt"
import "time"
import "math"
import "math/rand"
const Male = true
const Female = false
const BabyPercentage = 30.0
const Speed = 100 // This is actually more like 'days per year'
var day int
var year int
var decimalPlaces float64
func init() {
day = 0
year = 100
decimalPlaces = 1.0 / float64(Speed)
}
func chance(chance float32) bool {
result := rand.Float32()
if result <= chance {
return true
} else {
return false
}
}
func randInt(max int) int {
return rand.Intn(max)
}
func randFloat(max int) float32 {
max32 := float32(max)
return rand.Float32() * max32
}
func getDay() float32 {
day32 := float32(day) / float32(Speed)
yearAndDay := float32(year) + day32
return round(yearAndDay, decimalPlaces)
}
func getAge(day, birthday float32) int {
diff := day - birthday
return int(diff)
}
func round(x float32, unit float64) float32 {
return float32(math.Round(float64(x)/unit) * unit)
}
func createRandomCitizen() Citizen {
name := randInt(999999)
sex := chance(0.5)
birthday := randFloat(80)
return Citizen{name, nil, nil, nil, 0.0, sex, round(birthday, decimalPlaces)}
}
func die(age int) bool {
var c float32
switch {
case age <= 3:
c = 0.00001
case age >3 && age <= 10:
c = 0.000002
case age >10 && age <=50:
c = 0.000005
case age >50 && age <=60:
c = 0.00003
case age >60 && age <=70:
c = 0.0002
case age >70 && age <=80:
c = 0.0004
case age >80 && age <=90:
c = 0.0009
default:
c = 0.009
}
return chance(c)
}
type Citizen struct {
Name int
Mother *Citizen
Father *Citizen
Partner *Citizen
Fertile float32 // The day we can have a baby again
Sex bool
Birthday float32
}
type City struct {
Name string
Citizens []*Citizen
}
func createMates(males, females map[*Citizen]int) int {
// lenMales := len(males)
// lenFemales := len(females)
mates := 0
for male, maleAge := range males {
for female, femaleAge := range females {
ageDiff := maleAge - femaleAge
if ageDiff >= -20 && ageDiff <= 20 {
// Add in a semblance of chance
// if chance(0.7) {
male.Partner = female
female.Partner = male
mates++
delete(females, female)
delete(males, male)
break
// }
}
}
}
// fmt.Printf("Of the %d males and %d females I had, I made %d mates.\n", lenMales, lenFemales, mates)
return mates
}
func makeBabies(babyMakers map[*Citizen]int) []*Citizen {
var mother, father *Citizen
var babies []*Citizen
for babyMaker, _ := range babyMakers {
if chance(0.002) {
mother = babyMaker
father = babyMaker.Partner
today := getDay()
baby := Citizen{randInt(999999), mother, father, nil, 0.0, chance(0.5), today}
babies = append(babies, &baby)
// Mother can't have another baby for a year
mother.Fertile = today + 1.0
}
}
// sex := "boy"
// if baby.Sex == Female { sex = "girl" }
// fmt.Printf("%d and %d had a baby %s and named it %d. It was born on %f.\n", mother.Name, father.Name, sex, baby.Name, baby.Birthday)
return babies
}
func main() {
rand.Seed(time.Now().UnixNano())
cities := []*City{}
citizens := []*Citizen{}
// Initialise a city with some citizens
cities = append(cities, &City{ "city1", nil } )
// Create 100 citizens in city1
for i := 0; i < 100; i++ {
c := createRandomCitizen()
citizens = append(citizens, &c)
}
fmt.Println(cities[0].Name)
babiesThisYear := 0
matesThisYear := 0
deathsThisYear := 0
deathsBaby := 0
deathsTeen := 0
deathsAdult := 0
deaths60Plus := 0
accumulativeBabies := 0
accumulativeMates := 0
accumulativeDeaths := 0
deathsBabyAccumulative := 0
deathsTeenAccumulative := 0
deathsAdultAccumulative := 0
deaths60PlusAccumulative := 0
var dead []int
for {
babyMakers := make(map[*Citizen]int)
singleMales := make(map[*Citizen]int)
singleFemales := make(map[*Citizen]int)
dead = nil
today := getDay()
for index, citizen := range citizens {
age := getAge(getDay(), citizen.Birthday)
// Add the index of this citizen to another array if they die
if die(age) {
dead = append(dead, index)
// fmt.Printf("%d died aged %d\n", citizen.Name, age)
if citizen.Partner != nil {
citizen.Partner.Partner = nil // Make this citizen's partner single again
}
// If this dead citizen's partner has already been added to the baby makers
// that citizen can no longer make babies.
delete(babyMakers, citizen.Partner)
switch {
case age <= 12:
deathsBaby++
case age > 12 && age <= 18:
deathsTeen++
case age > 18 && age <= 60:
deathsAdult++
case age > 60:
deaths60Plus++
}
continue
}
// If you don't have a partner, you might get one.
if citizen.Partner == nil {
if citizen.Sex == Male {
if age > 18 && age < 60 {
singleMales[citizen] = age
}
} else {
if age > 18 && age < 60 {
singleFemales[citizen] = age
}
}
} else {
if citizen.Fertile < today && citizen.Sex == Female && age > 18 && age < 50 {
// We don't actually use the map values, but using a map makes it easier to remove elements.
// It also randomises the order.
babyMakers[citizen] = 0
}
}
// fmt.Printf("Boys: %d Girls: %d\n", boys, girls)
}
// Kill the dead.
deathsThisYear += len(dead)
for i, index := range dead {
index = index - i // The size of the citizens array will decrease by one each time
copy(citizens[index:], citizens[index+1:])
citizens[len(citizens)-1] = nil
citizens = citizens[:len(citizens)-1]
}
// Pass all the singles to the mate maker
matesThisYear += createMates(singleMales, singleFemales)
// Pass the baby makers to the baby maker
babies := makeBabies(babyMakers)
for _, baby := range babies {
citizens = append(citizens, baby)
}
babiesThisYear += len(babies)
time.Sleep(10 * time.Nanosecond)
if day == Speed { // If this is a new year
day = 0
year++
// Reset counters
babiesThisYear = 0
matesThisYear = 0
deathsThisYear = 0
deathsBaby = 0
deathsTeen = 0
deathsAdult = 0
deaths60Plus = 0
// fmt.Printf("New year: %d\n", year)
// fmt.Printf("Population: %d\n", len(citizens))
// fmt.Printf("Babies this year: %d\n", babiesThisYear)
// fmt.Printf("Mates this year: %d\n", matesThisYear)
// fmt.Printf("Failed mates this year: %d\n", failedMatesThisYear)
// fmt.Printf("Deaths this year: %d\n", deathsThisYear)
// Give us a report every 100 years
if math.Mod(float64(year), 100.0) == 0 {
fmt.Printf("Year: %d\n", year)
fmt.Printf("Population: %d\n", len(citizens))
fmt.Printf("Potential baby makers right now: %d\n", len(babyMakers) * 2)
fmt.Printf("Average babies: %d\n", accumulativeBabies / Speed)
fmt.Printf("Average mates: %d\n", accumulativeMates / Speed)
fmt.Printf("Average deaths: %d\n", accumulativeDeaths / Speed)
fmt.Printf("Baby deaths: %d\n", deathsBabyAccumulative / Speed)
fmt.Printf("Teen deaths: %d\n", deathsTeenAccumulative / Speed)
fmt.Printf("Adult deaths: %d\n", deathsAdultAccumulative / Speed)
fmt.Printf("60+ deaths: %d\n", deaths60PlusAccumulative / Speed)
// Reset counters
accumulativeBabies = 0
accumulativeMates = 0
accumulativeDeaths = 0
deathsBabyAccumulative = 0
deathsTeenAccumulative = 0
deathsAdultAccumulative = 0
deaths60PlusAccumulative = 0
}
} else {
day++
}
// Accumulate the accumulators
accumulativeBabies += babiesThisYear
accumulativeMates += matesThisYear
accumulativeDeaths += deathsThisYear
deathsBabyAccumulative += deathsBaby
deathsTeenAccumulative += deathsTeen
deathsAdultAccumulative += deathsAdult
deaths60PlusAccumulative += deaths60Plus
}
}