Due Wed Nov 6, 10:00 am (electronically).
Maximum Points: 100
signature GAME = sig 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 end;and the signature for playing the game :
signature PLAY = sig structure Game : GAME (* parameter *) val play : int -> Game.player -> Game.board -> (Game.board option * real) end;Take these signatures and their implementation as basis for this assignment. The
type boardrepresents 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
boardis 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.
PLAYwhere boards and moves are different types with appropriate comments. State the rationale behind your design decisions.
RANDOMfor 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 Timein the context browser of MLWorks). Your signature should encapsulate the state of the random number generator and protect against "tampering".
structure Random :> RANDOM.
functor Connect4 () :> GAMEso 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
GAMEfrom problem 1 by requiring a substructure satisfying
(* separate representation for interface *) (* this works with effects *) structure DisplayBoard : sig 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" *) endNote that this signature contains free reference to types
move, which must appear earlier in the revised
GAMEsignature, but not
board, which is hidden from the display module. Note also, that
matchmust have state to represent the displayed situation.
Connect4. The position should be represented by an array and printed in a simple-minded but recognizable fashion.
readActionshould 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
quitinstead of an integer, the returned action should be
Quit. Note that the function
quitdoes not actually need to do anything, although it would, if the interface was implemented in X, for example.
signature INTERACTIVE_PLAY = sig val playHuman : int -> unit (* search depth, human moves first *) val playComputer : int -> unit (* search depth, computer moves first *) end; functor InteractivePlay (structure Play : PLAY) :> INTERACTIVE_PLAY = struct ... end;The functions
playComputershould 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.
Connect4<userid> :> PLAY_CONNECT4satisfying signature
signature PLAY_CONNECT4 = sig type match val init : unit -> match val move : match -> int -> unit val play : match -> int option val name : string endWe 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).|
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_CONNECT4which provides the needed interface to the structures you already implemented.
/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.