If you’ve never played Frogger, play an online version (picked at random) here: https://denodell.github.io/frogger/
You will be writing a simplified version that discretizes time and space, which
makes the collision and movement logic a lot easier to implement. Here’s an
example of the finished product. Try pressing the keys i
and u
to see some
fun tricks!
This version of Frogger will run in the browser by using js-of-ocaml
to
transpile OCaml bytecode into Javascript. Fortunately for us, dune
supports this out of the box: all one needs to do is ask for the .bc.js
target (see the definition of the DEFAULT
alias in the dune file).
First, test your installation of js-of-ocaml
by running make
in the
test-js-of-ocaml-install directory. Then, point your browser at
_build/default/03-frogger/test-js-of-ocaml-install/index.html
.
We have written a simple scaffold that handles graphics, events and interactions with the DOM so you can focus on implementing just the game logic. scaffold.mli defines modules and types that you’ll need to use, like the number and kinds of rows in the playing board, and images of characters.
Take a look at frogger.mli. This is the interface you will implement by
writing a corresponding frogger.ml
. The contract between the scaffold and
your code is that you implement the four functions at the bottom of this
.mli
, and the scaffold will call those functions with the appropriate events
at the right times.
A World.t
represents the entire state of the game at a given point in time.
It is up to you to define what goes in the type – that’s why frogger.mli
does not specify what’s inside the type (we call this an opaque type).
However, to help you get started, we’ve specified some function signatures in
the World
module here that are likely to be useful.
To specify the logic of the game, you’ll need to figure out the following things (these correspond to the functions in frogger.mli):
This is the create
function. You may want to use the Random
module (from
Base
) to make life interesting.
For this first project, we’ll say that time advances only in units of 1
second. The tick
function should implement how the World.t
is transformed
when time advances. Note its signature:
val tick : World.t -> World.t
It takes a World.t
and returns a new World.t
. Writing your game in this
functional style will allow us to do some interesting things later on.
Players can press one of the four arrow keys to move their character around.
You specify what to do when they do that by writing a handle_input
function.
All this function needs to know is: what the current state of the world is (a
World.t
), and what button player pressed (a Key.t
). Its output: the
resulting state of the world (a World.t
).
It’s nice to be able to say, “The only things that happen in this game are:
time progressing, and the player doing something.” Your handle_event
function should just match
on the kind of event and dispatch to the
appropriate handler (one of the two above).
A list of tuples (Image.t, Position.t)
tell the scaffold which images to
draw where, and in what order: images later in the list will be overlaid on
top of earlier ones at the same position.
A reasonable starting point is to just move the frog (well, camel) around the game board. Read frogger.ml and follow the suggestions to build this basic game.
Type make
to build, and point your browser to
_build/default/03-frogger/index.html
to play the game!
Write an AI player for your game. Given the initial World.t
, it should emit
a sequence of Key.t option
, one for every timestep.
To see your AI in action you will need to modify the scaffold a little:
instead of feeding player input into the handle_input
function, make it
feed in the output of the AI you write.
- Does your
create
function ever produce initial states that cannot be played to a win? - How would you write AI to deal with potential randomness in the
tick
function?
While the interpolating scaffold is a neat trick, it’s not perfect because the collision detection logic is now out-of-sync with what’s going on visually. Extend the interface, game logic and scaffold to produce a smoothly-animated Frogger that also plays right.