15-212-X : Homework Assignment 5

Due Wed Nov 6, 10:00 am (electronically).

Maximum Points: 100


In this assignment we implement an interactive version of the Connect Four game. The objective of this assignment is to let you design and implement your own signatures and structures. Quite naturally, design decisions you might make for problems where signatures must be designed might turn out to be insufficient or wrong for some of the later problems, and must hence be improved. Please keep this mind when doing this assignment.

Problem 1: Distinguishing moves and board (20 pts)

Recall from class how we defined the signature for games
signature GAME =
  datatype player = Player | Opponent
  type board

  val initialBoard : board
  val moves : player -> board -> board list  (* no moves means player loses *)

  val evaluate : player -> board -> real  (* approximate, between -1 and 1 *)

  val toString : board -> string
and the signature for playing the game :
signature PLAY =
  structure Game : GAME (* parameter *)
  val play : int -> Game.player -> Game.board -> (Game.board option * real)
Take these signatures and their implementation as basis for this assignment. The type board represents the internal representation of the board which is used to perform the evaluation of the current situation. For the purpose of this assignment we consider only evaluation which prunes the search tree based on the value of sibling nodes. The representation of board is not suitable for an interactive version of the game. In such a version we would like the display module to maintain its own representation of the current board. This suggests distinguishing between the notion of move and the notion of board. Boards remain a specific datatype to the search engine which evaluates a situation, moves represent the decisions of the player. It is exactly this kind of information which must be passed to the display module.

Question 1.1 (10 pts)

Give the revised signatures GAME and PLAY where boards and moves are different types with appropriate comments. State the rationale behind your design decisions.

Question 1.2 (10 pts)

Make the necessary changes in the functors, starting from the code in the file /afs/andrew/scs/cs/15-212-X/assignments/ass5/connect4.sml.

Problem 2: Random numbers (25 pts)

An implementation of random numbers does not come with the ML basis library. We will need them though, because otherwise the computer player will be totally deterministic and easy to predict. This problem is about the design and implementation of a package providing random numbers.

Question 2.1 (10 pts)

Write a signature RANDOM for a random number generator based on the code in Paulson, pages 108-109, 198-199. The seed for a random number generator should be optional and be derived from the current time otherwise (see structure Time in the context browser of MLWorks). Your signature should encapsulate the state of the random number generator and protect against "tampering".

Question 2.2 (5 pts)

Implement a structure Random :> RANDOM.

Question 2.3 (10 pts)

Change your implementation of functor Connect4 () :> GAME so that the computer selects randomly between moves of equal value. Note: this should use only one instance of a random number generator initialized from the current time

Problem 3: Interactive game playing (45 pts)

In this problem we extend the structures we developed so far by an interactive component. To do so extend the signature GAME from problem 1 by requiring a substructure satisfying
  (* separate representation for interface *)
  (* this works with effects *)
   structure DisplayBoard :
     exception IllegalMove
     type match (* encapsulates current state *)
     val init : unit -> match  (* initialize starting position *)
     val quit : match -> unit  (* finish match *)

     val applyMove : match -> player -> move -> unit  (* apply a move *)
	 (* raises IllegalMove *)
     val showBoard : match -> unit  (* show the current position *)

     datatype action = Move of move | Quit
     val showAction : match -> action -> unit  (* show a move or "quit" *)
     val readAction : match -> unit -> action  (* read a move or "quit" *)
Note that this signature contains free reference to types player and move, which must appear earlier in the revised GAME signature, but not board, which is hidden from the display module. Note also, that match must have state to represent the displayed situation.

Question 3.1 (20 pts)

Implement this in a substructure of functor Connect4 . The position should be represented by an array and printed in a simple-minded but recognizable fashion. readAction should read one line from the listener (TextIO.stdIn) and parse its beginning as an integer with the column to move on. If the move is illegal, the user should be prompted again. If the user types quit instead of an integer, the returned action should be Quit. Note that the function quit does not actually need to do anything, although it would, if the interface was implemented in X, for example.

Question 3.2 (20 pts)

Complete the functor below (with the revised GAME and PLAY signatures)
   val playHuman : int -> unit  (* search depth, human moves first *)
   val playComputer : int -> unit (* search depth, computer moves first *)

 functor InteractivePlay (structure Play : PLAY)
The functions playHuman and playComputer should detect when a game is over (i.e. the player whose turn it is cannot move), declare a winner, and exit. So there are three possible ways a game can be finished: the "quit" action from the user, the human wins, or the computer wins.

Question 3.3 (5 pts)

Build the necessary structures by functor application which can be used to play the against the computer. Test it by opening the appropriate structure at the top level and calling playHuman 5 and playComputer 5.

Problem 4: Computer tournament (10 pts+?? extra credit)

Your program will participate in the 15-212-X tournament. In addition to the ordinary credit for writing a valid implementation of the signature below, we offer a first prize of a tasteful Fox Project mug plus 40 extra credit points. You obtain 20 ec points for reaching the semi-final, 10 ec points for reaching the quarterfinals, and 5 ec points for reaching the round of the last sixteen. Prepare a file tournament.sml which, when compiled, produces a structure Connect4<userid> :> PLAY_CONNECT4 satisfying signature
signature PLAY_CONNECT4 =
  type match
  val init : unit -> match
  val move : match -> int -> unit
  val play : match -> int option
  val name : string
We will use this to conduct the tournament among all compiling entries. Here:

init () initializes a new match.
move match i your opponent plays on column i. You may assume the move is legal (our tournament director checks this) and you must update your internal state to reflect that change in state.
play match return SOME(i) for a move on column i or NONE (which means you have lost or resign).
name your userid

Your program loses a match if it raises an uncaught exception, attempts an illegal move, if the cumulative CPU time spent by your program for a match exceeds 300 seconds, or (the usual condition) it cannot make a legal move.

The tournament is played in single elimination format, with two matches (with either program making the initial move) between randomly paired programs in each round. In case of a tie the program which used less CPU time overall advances to the next round. (See structure Timer in the context browser of MLWorks)

In each match, since there can be at most 49 moves, there can be no ties.

Feel free to improve your search program or evaluation function as you see fit. We suggest that you write a functor of the form

functor Connect4userid
        (structure Play : PLAY where type Game.move = int)
        :> PLAY_CONNECT4
which provides the needed interface to the structures you already implemented.

Hand-in instructions

Put your SML code into files

/afs/andrew/scs/cs/15-212-X/studentdir/<your andrew id>/ass5/ass5.sml for problems 1, 2, and 3 and /afs/andrew/scs/cs/15-212-X/studentdir/<your andrew id>/ass5/tournament.sml for problem 4.

Be sure to use these names and check that tournament.sml is self-contained and compiles to help us in the organization of the tournament.