Assignment 7: Simulation in Go

Objectives

Introduction

Gavin Fay, Megan Olsen, et al developed a model of the transmission of Tasmanian devil facial tumor disease (DFTD) to investigate how building a road through their habitat might affect the transmission rate. We will update a Go agent-based simulation to facilitate a greatly simplified version of their model.

Assignment

The /c/cs427/hw7 and /c/cs427/hw7/Required directories contain an agent-based simulation implemented in Go. The current version features Tasmanian devils (Tazzie) objects wandering around a hex-grid world following scents to food sources represented by Food objects that emit Scent objects. The Tazzie objects contain a field infected that indicates whether they are infected with DFTD; initially one of the eight Tasmanian devils is infected.

Part 1: Spreading DFTD

The given implementation does not model interactions between Tasmanian devils because the Tazzie struct embeds NoTazzieAction, which defines a do-nothing version of AcceptTazzie. Remove that embedding (this required changes to both the struct definition and the NewTazzie function) and add a new AcceptTazzie function with a Tazzie receiver, Tazzie argument, and no return value. Both the receiver and argument should be pointers. The argument will be the actor in the interaction and the receiver will be the patient. The new function should set the infected flag of the patient to true 20% of the time if the actor is infected.

Once your new function is complete, make the executable (the makefile in /c/cs427/hw7/Optional creates an executable called Sim by compiling and linking agents.go, world.go, position.go and main.go. Run the executable and watch the simulated Tasmanian devils interact. Eventually, all the Tasmanian devils will be infected with DFTD.

Part 2 - Automobiles

Next, add automobiles to the simulation. An automobile is similar to Tasmanian devils and scents in that they all move in the world toward a goal. Both automobiles and scents are removed from the simulation when they reach their goals. Automobiles occasionally generate carrion (Food agents) much like food generates scents. Otherwise, automobiles don't interact with other agents. Those behaviors are implemented in the Mobile, Mortal, Emitter, and NoActions and Inert structs. Define a new struct Automobile that embeds those structs.

Also, define a String function with an Automobile (pointer) receiver that returns the string "A" to represent automobiles in the display. Add a NewAutomobile function that takes a world (pointer), and two Hex objects (non-pointer) for the starting and ending point of a car. The NewAutomobile function should declare a local variable alive that is initialized to true and then an instance of Automobile with its various embedded components initialized to contain:

After the Automobile is created, set its Emit field to a function that takes no arguments and returns the Agent returned by NewFood, to which you pass the world and the Automobile's embedded Hex (we have to set this now rather than in the initializer because of the reference to the outer Automobile that is not in scope until it has been initialized). Then return a pointer to that Automobile.

Finally, add statements to add a new Automobile to the world in the NewWorld function in world.go. You can specify any initial point in the world and any destination. Building and running the executable should now give a simulation with an automobile agent moving through the world creating roadkill (food).

Part 3 - Road

Rather than creating one Automobile, we want to create agents that spawn Automobiles. We will call those agents RoadEnds. A RoadEnd is stationary, spawns (emits) Automobile agents, never dies, and doesn't otherwise interact with other agents, so define RoadEnd so that it embeds the corresponding structs.

Define a NewRoadEnd function that takes a pointer to a World, and two (non-pointer) Hex structs representing the position of the road endpoint and the matching road endpoint at the other side of the world. Every 25 time steps (on average), the embedded Emitter should create a new Automobile that starts at the position of the RoadEnd and travels to the position of the opposite RoadEnd.

Add a String method with a RoadEnd pointer receiver that returns an empty string. Replace the code in NewWorld (in world.go) that adds a single Automobile to the world so it added two RoadEnds at the middle of the left and right edges of the world instead. Your simulation should now be complete with automobiles randomly appearing on the edges of the world and travelling to the opposide side, randomly generating new food (roadkill) as they go.

Graduate Requirement

Note the shared behavior of Scent and Automobile: they both start somewhere, move to a goal, and are removed from the simulation (die) when they reach that goal. Refactor the simulation so that common behavior is implemented by a DiesAtGoal struct that Scent and Automobile both embed.

DiesAtGoal should embed Mobile and Mortal. The corresponding NewDiesAtGoal function should declare a bool local variable called alive that is initialized to true. It should create a return a pointer to a DiesAtGoal whose embedded Mobile is initialized with the world, the starting position, a pointer to the ending position, and a functon that takes no arguments, returns nothing, and sets alive to false. The embedded Mortal should be initialized with a pointer to alive. Modify Automobile and Scent so they embed DiesAtGoal instead of Mobile and Mortal, and make the corresponding changes to NewScent and NewAutomobile. Note that NewDiesAtGoal should return a pointer to the new agent, so make sure that the types match when you embed that agent.

Testing

The public tests require all the types and functions mentioned above to be present in order to compile and run any individual tests. If you want to test before the entire program is complete, you must add dummy definitions for the structs and functions. The structs can embed Stationary, NonEmitter, Immortal, NoActions, and Inert. The String and NewXXX functions can return nil .

Submissions

Submit whatever source code (.go) files you created or modified (which should be agents.go and world.go) and a makefile that builds the Sim executable when make is run with no arguments or with target Sim (you can use the one in /c/cs427/hw7/Optional). Your makefile must assume that the provided code has been copied to the current directory; do not hard-code the directory containing that code in your makefile.