A simulation example in golang
- Read in map file of towns
- Each town has upto four routes (north, south, east, or west) to other towns
- N aliens land at random towns on the map and wander around randomly.
- if two (or more ?) should meet they destroy the town along with themselves.
- On a town's destruction the simulation should print "t has been destroyed by alien x and alien y!"
- When a town is destroyed its removed from the map along with the routes to it.
- When an alien finds itself in a town with all it's neighbours destroyed it becomes stuck.
- Finnaly print out a map file of the remaining towns.
- language to use: Golang
Some simple questions:
- Can an alien start it's invasion in a isolated town ? This is a possible edgae case, unless we assume the
landing
town is randomly chosen from the connected twon set. - When aliens meet in a town do the kill each other and in the process destroy the town or when the town is desrotyed they die with it. The requirements states that they kill each other and then destroy the town. The interpretation will effect how this is coded.
- a map file reader that builds an internal map representation.
Foo north=Bar west=Baz south=Qu-ux
Bar south=Foo west=Bee
.
.
to
// non language specific maps of structs ?
{
"Foo": {
"routes": { "north": &Bar, "west": &Baz, "south": &Quux}},
"occupiers": set{"Alien1"},
}
"Bar": {
"routes":{ "west": &Bee, "south": &Foo},
"occupiers": set{},
}
.
.
}
As possibly hinted in the requirements with "Each iteration...", one solution would be to cose the simulation without goroutes as an O(N**2) solution The main loop being the 10k iterations:
map = buildMap(map_file)
dead_or_trapped = {}
for iteration in 10k:
alien_active_set = aliens(N) - dead_or_trapped
for alien in alien_living_set:
{killed, trapped, rampaging}, town = alien.pillage(map)
if killed:
map.remove(town)
dead_or_trapped.add(alien)
if trapped:
dead_or_trapped.add(alien)
- no race conditions
- no deadlocks
- deterministic so easy to debug (use constant randon seeds)
- easy to test
- performance predictable and linear
- predictable dev cost and fast to market
- any developer from any language.
- can't scale to utilizes available CPU cores
- simulation is forced - deterministic
- boring
- doesnt showoff the programmer's skill
Define one actor Alien and Town
an Alien's responibility is to
- get a route from the Map
- invade the new town
- if the town is already invaded kill the other alien, destroy the town and die
- if the town is vacant goto the first step
- elegant
- simulation is non-deterministic
- will scale use available CPU cores
- non-deterministc so it's hard to test and debug
- performance unpredictable and linear
- unpredictable dev cost
- race and dead lock prone.
- needs an expienced golang developer
- an alien needs to get an available route and then occupy the town - but the at the same time could have beed destroyed. for instance the code look like:
if alien.location == nil {
next_town = route_map.AnyTown()
} else {
next_town = alien.location.RandomRouteFrom()
}
if next_town != nill {
if next_town.IsAlreadyOccupied() {
occupier = next_town.GetOccupier()
next_town.Destroy()
occupier.Kill()
} else {
next_town.SetOccupier(alien)
alien.location = next_town
}
} else {
alien.Trapped()
}
location
is*Town
- there are race conditions on the next_town's state
- there are race conditions on the next_town's routes - it's neighbours could have been detroyed between
AnyTown()
,RandomRouteFrom()
andIsAlreadyOccupied()
- between the test
IsAlreadyOccupied() == false
andnext_town.SetOccupier(alien)
it could have been already occupied. - And there are more in
if next_town.IsAlreadyOccupied() {}
condition.
So the next question is channels
or Mutex
and where ?
go run main.go -energy=bool -energy=int N map_file
where required arguments are:
N
is the number of aliens to land on the por planetmap_file
the path to a map file in the described format below
where the optional (flag) arguments are:
- -debug
- -energy
Optional flags must be before the position line arguments, N
and map_file
examples:
go run main.go 10 maps/map_01.txt
go run main.go -energy= 10 3 maps/map_01.txt
go run main.go -debug=true -energy= 10 3 maps/map_01.txt
Map file format is like this:
#Foo north=Bar west=Baz south=Qu-ux
#Bar south=Foo west=Bee
#Qu-ux north=Foo west=Bee
#Bee west=Bar east=Qu-ux
#Baz west=Foo south=Daz
Daz north=Kaz
Naz west=Zaz south=Daz
Zaz north=Kaz east=Naz
Kaz south=Zaz
Lines starting with # are ignored`