Overview
Once you have the grid.rkt
module squared away, the next step
is the implementation of the simulate.rkt
module. This module
implements the type for representing a simulation model and
provides functions for running the simulation on a given model.
Models
A simulation model is a data structure that contains the
necessary operations to run a simulation. It is defined by
the following code, which you should copy into the simulate.rkt
file:
;; A (Model C) collects together the information needed to run
;; a simulation, where C is the type of the individual cells.
;;
(define-struct (Model C)
([name : String] ;; the model's name
[init : (Natural Natural -> (Grid C))] ;; create the initial grid
[update : ((Grid C) -> (Grid C))] ;; update the grid one step
[stable? : ((Grid C) -> Boolean)])) ;; have we reached a stable state?
In the next step
of Project 1, you will
implement a simple instance of this type.
Simulation
A simulation run consists of creating an initial grid and then repeatedly updating it until either a specified step limit is exceeded or the grid becomes stable. Implement a function for running the simulation with the following type signature:
(: simulate : (All (C) (Model C) Natural Natural Natural -> (Grid C)))
where the arguments are the model to be simulated, the number of rows and columns in the grid, and the step limit.
Animation
The second simulation function provides an animation of the state of the simulation.
(: animate : (All (C) (Model C) Natural Natural Natural (Render-Info C) -> (Grid C)))
The arguments are as for the simulate
function, except that we add an additional
Render-Info
argument (described below).
This function runs the simulation while displaying a window containing the current
state of the grid. As with the animate
function, the simulation terminates
when either the grid becomes stable of the number of steps exceeds the limit.
The final grid value is returned as the result.
Render Info
The last argument to the animate
function is a data structure that specifies
information about how to render the grid. Copy the following definition into
your simulate.rkt
file:
;; The Render-Info structure is a container for various
;; bits of information needs to control the animated
;; simulation
;;
(define-struct (Render-Info C)
([radius : Integer] ;; the cell radius
[draw-cell : (Coord C -> Image)] ;; drawing function for cells
[rate : Positive-Real])) ;; the update rate in seconds
big-bang
We use Racket’s
big-bang
syntactic form (defined in cs151-universe.rkt
) to implement the animation.
To use this mechanism, we define a collection of callback functions that
implement services, such as updating the state, drawing, and
testing to see if we are done. We give the big-bang
operator an initial
state, plus the call-backs. It then repeatedly updates and renders the
state until we are done, at which point it returns the final state.
Since the simulation is finished when either the grid is stable or the time limit is exceeded, you will need to define a data structure to represent the animation state that contains the current grid and a count of the ticks.
Your animate
function will contain an expression of the form:
(big-bang
initial-state : state-type
[name model-name]
[to-draw draw-state]
[on-tick handle-tick rate]
[stop-when done? draw-state])
The line
initial-state : state-type
specifies the initial-state (typically an expression). The big-bang
form
requires that the type of the state must also be specified.
The line
[name model-name]
specifies the name that is used for the window’s title bar. You should use the model’s name for this value.
The line
[to-draw draw-state]
specifies the drawing callback function, which takes a state value and returns an image.
The line
[on-tick handle-tick rate]
specifies an update function, which takes a state value and returns an
updated state. This function is called once every simulation tick.
The rate
argument is a positive real value that specifies
the time between ticks. For example, if we specified a rate of
0.25
, then the simulation would be updated four times a second.
The line
[stop-when done? draw-state]
specifies a predicate (done?
) on state values that returns true when the animation
should terminate. We also supply the draw-state
function as an additional
argument, which is required to get the final state of the simulation rendered.
Testing
As always, you should include tests for any helper functions that you define, but
you can postpone testing the simulate
function to the proj.rkt
module,
where you will have a simple modle available.
Exports
;; ===== Exports =====
(provide (struct-out Model)
(struct-out Render-Info))
(provide simulate
animate)
These exports specify the public interface to the module. You should not extend these exports in any way.