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.