CS 1101 Computer Science I
Spring 2016

Computer Science Department
The Morrissey College of Arts and Sciences
Boston College

About Staff Textbook Grading Schedule Resources
Notes Labs Piazza Canvas GitHub Problem Sets
Manual StdLib Pervasives UniLib OCaml.org
Problem Set 5: Animation

Assigned: Sunday February 21, 2016
Due: Sunday February 28, midnight
Points: 10 Points

This is a pair programming project. If you don't have a partner please see a course staffer for an assist. Or you can use the partner finding tool in Piazza. You can do either Part 1 or Part 2.

Start by downloading the harness code.

Part 1 (10 Points): Rain

The World module defines one type ('a, 'b) t = World of 'a | ... and one function, the big_bang. As we saw in problem set 2, the big_bang function allows a coder to manipulate images on the graphics window. The World module is described by the following text:
type ('a, 'b) t = | World of 'a   (* 'a is the type of world states *)
                  | ...

val big_bang : ?name:string ->
 	       ?width:int ->
	       ?height:int ->
	       ?to_draw:('a -> Image.t) ->
	       ?on_tick:('a -> ('a, 'b) t) ->
	       ?on_mouse:('a -> float -> float -> string -> ('a, 'c) t) ->
	       ?on_key_press:('a -> string -> ('a, 'd) t) ->
	       ?on_key_release:('a -> string -> ('a, 'e) t) ->
	       ?rate:float ->
	       ?stop_when:('a -> bool) ->
	       ?to_draw_last:('a -> Image.t) ->
	       ?register:string * string ->
               ?on_receive:('a -> 'f -> ('a, 'g) t) ->
               'a -> unit

	starts a game after receiving the following arguments (the last 
        argument of type 'a is mandatory)
	
	name : a name appearing at the top of a window
	width height : width and height of a window
	to_draw : receives a world; returns an image to be drawn on a window
	on_tick : receives a world; returns a world after 1 tick
	on_mouse : receives a world, a mouse coordinate, and a name of a mouse event; 
                   returns a new world
	on_key_press : receives a world and a name of a pressed key; 
                       returns a new world
	on_key_release : receives a world and a name of a released key; 
                         returns a new world
	rate : an interval of 1 tick in sec
	stop_when : receives a world; returns true if the game is over
	to_draw_last : receives a world; returns an image to be drawn 
                       when the game is over
	register : the ip address and the port number of a server
	on_receive : receives a world and a message sent from the server; 
                     returns a new world
	an initial value of world (mandatory)
      

For example, one could use the big_bang function to write a program that allowed the user to move a red circle across the screen using the left and right arrow keys as follows:

open Color
open Image
open World

let draw x =
  let empty = Image.empty_scene 600. 600.
  in
  Image.place_image (Image.circle 50. Color.red) (x, 300.) empty

let changeWorldOnKey x keyName =
  match keyName with
  | "left"  -> World(x -. 10.)
  | "right" -> World(x +. 10.)

let demo () = World.big_bang 300.
                             ~name:"Demo"
                             ~width:600
                             ~height:600
                             ~to_draw:draw
                             ~on_key_press:changeWorldOnKey
let _ = demo()
	
In the example, pressing the left arrow key will move the circle to the left by 10 pixels and pressing the right arrow will move it to the right by 10 pixels. In the example, the state of the world is just a single integer specifying the x-coordinate of the circle. Note that an initial value of this coordinate (i.e., 300.) is provided as the single required argument to the World.big_bang function.

The World.big_bang function also accepts a number of optional arguments. These are denoted by the names prefixed with question marks. So in our demo, we gave the World.big_bang function a name argument when we provided the string "Demo" as a value for the ~name: optional argument.

The World.big_bang function runs in a loop with a ticking clock. Each time the clock ticks, the to_draw function is called with the current state of the world. The to_draw argument must be a function that accepts a state of the world (in this case an int) and which returns some image. Presumably the image returned reflects the state of the world.

The ~on_key_press optional argument is also provided a function that accepts a state of the world. But rather than returning an image, it returns a new state of the world wrapped up in the World.World constructor. (See above.)

The world state that one might want for a given animation or game will depend on the animation or game. The world state might be a list, it might be a tuple or a record or some combination of the above.

Choose one of the following animations to implement with a partner. If you need help finding a partner, contact a staff member or use the Teammate finding tool on Piazza!

Design and develop an OCaml program implementing an animation of rain gathering in a tank. The animation should generate a shower of randomly sized raindrops (use Image.circle) appearing at the top of the display (y=0) and with a randomly selected x-coordinate. On each clock tick, the falling rain drops should continue to fall and new raindrops should appear at the top.

The raindrops are falling in a tank whose width and height are those of the graphics window. The depth of the tank should be the width of the largest possible raindrop. (I.e., if the randomly chosen radius of a rain drop is, say 5: Random.int 5, then the depth of the tank should be 10.) As the drops reach the bottom, they release their volume of water into the tank. The water level should rise as shown in the clip.

You can use a triple (x, y, radius) to represent a single rain drop and you can use a pair (drops, volume) to represent the state of the world that your functions manipulate and display. You can define names for these types as in

type drop  = float * float * float
type world = drop list * float
    
As an alternative, you can represent both drops and the state of the world using another structured type, the record type. A record is like a tuple but it has named fields. For example,
type drop  = {x:float; y:float; radius:float}
type world = {drops:drop list; volume:float}
  
Given these definitions, we can make and use drops (and a world state) as follows
# let d = {x=0.2; y=0.2; radius=0.1};;
val d : drop = {x = 0.2; y = 0.2; radius = 0.1}

# d.x;;                               (* access the x field of d with d.x *)
- : float = 0.2

# let {x=a; y=b; radius=c} = d;;      (* accessing fields w pattern matching *)
val a : float = 0.2
val b : float = 0.2
val c : float = 0.1

# let e = { d with radius = 0.5 };;   (* a new record differing only in radius *)
val e : drop = {x = 0.2; y = 0.2; radius = 0.5}
  

Part 2 (10 Points): Best Candidate Algorithm

In class we've briefly discussed the problem of randomly distributing points in the xy-plane. One can use the Random.float function to compute a random x-coordinate and a random y-coordinate. But the result will often have too many points that are clumped together with other areas being completely free of points.

The best candidate algorithm is an iterative algorithm that maintains a list of random points [p1; ...; pk]. In each cycle through the algorithm, a set of candidate points [c1; ...; cj] are generated, exactly one of these candidates will be selected join the list of random points. In particular, for each candidate ci, the nearest neighbor in [p1; ...; pk] is found. The candidate with the largest distance to it's nearest neighbor is the best, it joins the list of random points and the other candidates are discarded.

Implement the best candidate algorithm as described above. Your implementation should render the set of points on each iteration of the big bang using small circles to depict the points on the xy-plane.

Created on 01-19-2016 23:09.