Problem Set 5: Antichess

6.170 Handout 24; Handed out on March 15, 1994; Due on various dates: see below

Note: This document has been amended.

Introduction

Your final project is to design, build and test a program that plays antichess. You will work in teams of three or four. All members of a team will get the same grade, except in unusual circumstances. If your group is experiencing internal problems, see your TA immediately.

Section 2 of this handout describes the deliverables that are expected.

Section 3 gives the requirements specification including a description of the antichess game and some interfaces.

Section 4 gives some use some useful suggestions.

What To Hand In

The due dates for the various deliverables are:

April 5th         Initial Design Document
April 14th        Progress Report
April 20th,21st   Oral Presentation
May 5th           Final Report -- due at 4:30 pm
May 9th           Demonstration
May 11th          Optional Tournament

Design Document, Due April 5th

Your first task is to write a design document, the foundation on which your project will build. It should have the following sections. (The numbers in parentheses are suggested lengths; don't exceed them without good reason.)
  1. Revised requirements specification (2 pages)

    To begin your design, you need to carefully examine the requirements specification given to you by your customer (the 6.170 staff). As is typical of customer requirements, the specification is incomplete and may contain inconsistencies or ambiguities. You need to debug the requirements specification, resolving any ambiguities or inconsistencies and filling in missing details. (In particular, you need to define a user interface for the program!) The resulting revised specification should make clear to your customer what you think the completed program should do.

  2. Module dependency diagram (1 page)

    Use a module dependency diagram to introduce the overall structure of your program. Include just the major modules for clarity.

  3. Schedule (1/2 page)

    Perhaps most crucial to the success of your project is the plan by which you will complete it on time. The project schedule should describe what each member of the team will do. It should have a list of milestones and an equitable division of labor. (A milestone says what you expect to be done by some date, such as a tested module or a working feature--an achievement that is objective, easy to evaluate and significant.) You can choose how to divide tasks between team members; you could appoint a tester, a documenter and a coder, for instance, or you could allocate modules to team members. include concrete goals that you will achieve by the final due date. How well you meet these goals will affect your grade on later stages of the project, so don't be overly ambitious. On the other hand, plan to make good progress so that you can finish the project on time.

  4. Design overview and rationale (1 page)

    Describe the major modules and explain why they were chosen (e.g. for ease of implementation, validation and/or maintenance). Describe any important design decisions that cannot be isolated to a single type or operation, including decisions to reject alternatives.

    Think about any changes that the customer may later require. Argue about the extensibility of your design--how easily new features can be added. Your design should be simple and well-structured, so that it will be easy to make modifications and additions. Count On Our Demanding Several Such Changes.

  5. Partial module specifications (10 pages)

    Describe the modules of your program by giving specifications for them. Specifications of abstract types should include an overview and specifications for the important operations.

Progress Report, Due April 14th

Your next deliverable is a progress report which describes any changes to the initial design and shows some tested, working code. You should include the sections of your design document with any changes in your design, work schedule, or assignment of responsibilities clearly marked.

In addition, include the following sections:

  1. Implementation Overview (5 pages)

    For each cluster give the rep (and, if unusual, a reason for choosing it), an abstraction function and a rep invariant. Give algorithms for operations for which an implementation does not immediately spring to mind. Mention any particular problems you anticipate.

  2. Validation Strategy (1 page)

    Describe how you intend to find and remove bugs from your program. It's up to you to decide how to make your program as bug-free as possible; you'll need to use several techniques such as compile-time checks, run-time assertions, reasoning about your code, testing, etc. You might want to use different techniques at different stages in the development, or on different modules.

  3. Code

    About a quarter of the code should be written and tested by the end of the design phase. The purpose of early coding is to catch errors in the design, so try to code the modules that seem hardest or whose specs are unclear.

  4. Evidence Of Validation

    Show test runs, bug reports, etc. Tests should not be run in the debugger, but from a specially prepared file.

Oral Presentations, On April 20th And 21st

This is another progress report, but this time presented orally to your TA. It should have similar content to your first progress report, with emphasis on your design and implementation strategy. All team members need not speak, but all should attend. Your team will be graded on the quality of the presentation, and you'll get suggestions on how to proceed in finishing your project.

Final Report, Due May 5th

Submit a final report describing your design and implementation, in the same format as the progress report. Note the revisions you have made.

The final report is due by 4:30 pm. Because of end of term constraints, late assignments will not be accepted; hand in what you have.

Demonstrations, On May 9th

Demonstrate your working program for your TA. This is also your opportunity to respond to criticisms of your final report.

Tournament, On May 11th

Each team may submit a program to participate in a single elimination antichess tournament. The tournament is optional, and will not affect your grade in any way. All students are welcome to attend and participate in the festivities. A nominal prize will be given to the winner. Additional information will be provided in a later handout.

Requirements Specification

The Game: Antichess

Antichess is a variant of chess in which the goal is to get rid of one's pieces. Here we describe how antichess is different from chess; the rules for chess are listed in Appendix A.

Player A wins the game against player B if:

  1. all pieces of A except for the king are taken, or
  2. player B checkmates player A.
The moves in Antichess differ from those in chess in that a player is forced to take the opponent's piece whenever that is possible. That is, if it is A's turn to move, and a piece of A is attacking a piece of B, then A must take that piece. If A can take several pieces of B, then A is free to choose the piece to take.

The king is special in that the foremost obligation of each player is to move the king out of a check. This overrides the rule to take the opponent's piece. However, if it is possible for a player to remove the king from check as well take a piece of the opponent, then the player must do so.

As an example, suppose player A's king is under check from B's queen. Further, suppose A has two choices to move away from the check: move the king away or take B's queen. In this case, A must take B's queen.

In short, each move of player A must observe the following:

  1. If A's king is under check, move it out of check.
  2. If A can take one of B's pieces, then it must (unless disallowed by the previous rule).
For simplicity, some special moves in chess have been changed as follows in our version of antichess:

  1. A pawn cannot move by two squares at the beginning.
  2. Castling is not allowed.
  3. En passant capture is not allowed.
  4. When a pawn moves to the last row on the opposing side, it turns into a queen. (In normal chess, such a pawn can be turned into any piece of the player's choice.)
We shall play antichess with time restrictions, as is often the case in chess games. Each player is allocated a fixed amount of time to make all the moves in, say, 5 minutes. To this effect, there is a clock for each player that is set to 5 minutes at the start of the game. If it is player A's turn to move, A's clock will count down until the move is made. While it is B's turn to move, A's clock is suspended. If a player's clock runs down to zero while the game is in progress, the player loses. This ensures that the game is over in less than 10 minutes, because by then at least one of the clocks would have run down to zero.

The Player/Referee Interface

The program is divided into two distinct parts: the machine player and the referee. The referee is responsible for maintaining the human interface: drawing the screen and reading keyboard and mouse input. It is also responsible for setting up the board, informing the players when it is their turn, fetching moves from the players, and keeping time. Your referee will allow a human to play against a machine player. The human will play white, and the computer will play black. The machine player will play the game against the user. It will receive updates on the state of the game from the referee, and choose a move on its turn. We will provide you with CLU abstractions defining the interface between referees and machine players. This interface will allow different machine players to be mixed and matched easily. At the end of the term, we will have a tournament in which the machine players implemented by students will play each other using a referee provided by us.

User Interface

Your referee will be responsible for drawing an up-to-date board. We have provided you with the {\tt window} abstraction to create an X-based interface. The abstraction is listed in /mit/6.170/ps5/src/window.spc. It allows the user to draw items like rectangles, buttons, and bitmaps on a window, and reports events like button-press, type-ins, and mouse-clicks. Your referee should also keep up-to-date displays of the following information (the format and layout of this information is up to you):

Moves

Your referee should accept typed input. This will allow the human player to specify the move. The human player may type in a move of the form: Column1Row1-Column2Row2 (e.g. a1-b2). The columns are numbered 'a' to 'h' from left to right. The rows are numbered 1 to 8 from bottom to top.

Feel free to provide the user with other methods of entering moves, such as using the mouse.

Commands

In addition to moves, the referee must accept a number of commands for controlling the configuration and play of the game. Your design team must choose and document the method by which the user can input commands. For example, the slash (/) could bring up a list of possible commands, and the user could then type the first letter of the desired command. Other possible interfaces might make use of the mouse.

The commands recognized by antichess are:

NAME
Set the name of the current players. (Each player's name is printed with their score and remaining time.)
TIME-LEFT
Set the amount of time that a specified player has.
SAVE
Save the current game into a file whose filename is specified by the user. The game continues after being saved. The format of the file is given in the Game File Format section.
LOAD
Replace the current game with the game saved in a file specified by the user.
NEW
Terminate current game and start a new one.
QUIT
Terminate current game and exit program.
The machine player should play more skillfully at a higher level than at a lower one. Apart from this requirement, your program will not be graded on the absolute level of the skill exhibited by your machine player. You should employ heuristics in your program to make it intelligent, but do not get bogged down in this process.

Game File Format

The figure below describes how antichess saves games into files.

The fields in the file are:

    <game-file> ::= <next-turn-color> newline
                    <timeleft> newline
                    <board>

    <next-turn-color> ::= B | W

    <timeleft> ::= <real> <real>

    <board> ::= <row> <row>  <row>  <row>  <row>  <row>  <row> <row>

    <row> ::= <piece> <piece> <piece> <piece> <piece> <piece> <piece> <piece>  newline

    <piece> ::= - | P | p | K | k | N | n | R | r | Q | q | B | b


			Figure.  Game File Format
As an example, the following figure shows the load file for the initial board configuration. Time left for each player is 5 minutes. In conventional chess layout, the white pieces are in the bottom two rows and black pieces in the top two.
                                  W
                                  300.0 300.0
                                  rnbqkbnr
                                  pppppppp
                                  --------
                                  --------
                                  --------
                                  --------
                                  PPPPPPPP
                                  RNBQKBNR


		Figure.  Load File for Initial Configuration

The Player/Referee Interface Specification

We have provided the specifications for the interface between the referee and the machine player. All of the interaction between the referee and the machine player must use this interface. You may not amend these specifications. If you have a problem with them, speak to your TA. This interface makes use of the standard string format for the board and a move. The standard format for a board is the same as the load file format. The standard format for a move is the five-character string described in the "Moves" Section above, such as a1-g7.

Machine Player Interface

(Some text omitted because it wasn't given to us in .tex form. Grrr.)
 % A machine player is a mutable data type reflecting the state and
 % strategy of a player in a game of antichess.

 options = null

 machine_player = data type is create, get_name, set_options, reset,  make_move

   create = proc (opts: options) returns (machine_player)
         % Effects: Returns a new machine player with the given options set.

   get_name = proc (p: machine_player) returns (string)
         % Effects: Returns a string name for the machine player.

   reset = proc (p: machine_player, board: string, opts: options)
         % Requires: board is a string representing a valid board.
         % Modifies: p
         % Effects: p is modified to reflect the new board and options (opts)
         %          preparatory to beginning a new game.

   set_options = proc (p: machine_player, opts: options)
         % Modifies: p
         % Effects: Reconfigures p's options.

   make_move = proc (p: machine_player,  opponent_move: string, 
		     timeleft: real, opponent_timeleft: real)
                     returns (string) signals (error(string))
         % Requires: opponent_move is a string representing a valid move
         %           by the opponent.
         % Modifies: p
         % Effects: Returns the next move of p, given the opponent's move 
         %          (opponent_move), the time left for p (timeleft), and
	 %	    the time left for the opponent (opponent_timeleft).
         %          (Note that p might take longer than timeleft, but then
         %          it stands to lose the game.)

 end machine_player

Hints

Design Matters Most

A good design will save you a lot of time later; it's well known that a small mistake made early in a project can become a big problem if it's not caught until much later. Think of the design as the major part of the project, and do it very carefully, trying to anticipate problems that may arise. Then your actual implementation will be more straightforward, and more fun. Validation shouldn't be an afterthought; you may choose a design because its implementation will be easier to test.

Don't Overdocument

Carefully worded amendments will save you the trouble of rewriting portions of the design. The final report is not a museum piece, so don't get carried away. Don't include any redundant material. For example, there's no need to explain the difference between black-box and glass-box testing; just indicate which of your test cases fall in each category. Incomplete documentation is better than no documentation at all. So when you're writing your code, if a potential problem or subtlety occurs to you, but you don't have time (or are unable) to formulate it properly, then just add a draft of a comment and mark it as such. Later, if you have time, you can go back and fix it. Being rigorous is not the same as belaboring the obvious. You can assume that your TA knows what a set or a stack is. There's no need to explain something from scratch when you can use standard terms and notions.

Don't Reinvent The Wheel

Part of being a good software engineer is knowing what you don't have to do. You'll find game playing algorithms in Winston and Horn's Artificial Intelligence. Use our window cluster to generate an X-based interface. The object files and the library are in /mit/6.170/lib. The specs are in /mit/6.170/ps5/src/window.spc. Our implementation of this cluster is based on the wish shell and the associated Tcl/Tk toolkit. We will try to ensure the availability of this shell on different platforms. Do not incorporate any other code in your project unless you clear it with your TA. While borrowing code from others is usually good software engineering practice, in this context it will be considered cheating.

Have Fun Being On A Team

Enjoy being part of a team. Run new ideas past your partners, and discuss problems with them. A good way to find a bug is to ask someone else to look at your code. If you're worried about finishing, start early!

Appendix A: Chess Rules

This section describes chess rules as given by F.I.D.E. (F.I.D.E. is the international organization that conducts the official World Chess Championship). Some of the rules that are not relevant for this project have been removed/modified.

The Chessboard

The game of chess is played between two opponents by moving pieces on a square board called a chessboard. The chessboard is composed of 64 equal squares. The eight vertical line of squares are called columns. The eight horizontal lines of squares are called rows. The squares are colored black and white alternately. The lines of squares of the same color, touching corner to corner, are called diagonals. The chessboard is placed between the players in such a way that the near corner to the right of each player is white.

The Pieces

At the beginning of the game, one player has 16 white pieces, and the other has 16 black pieces. These pieces are as follows:
A white king:          K            A black king:        k
A white queen:         Q            A black queen:       q
Two white rooks:       R            Two black rooks:     r
Two white knights:     N            Two black knights:   n
Two white bishops:     B            Two black bishops:   b
Eight white pawns:     P            Eight black pawns:   p
The initial position of the pieces on the chessboard is given below:


         The layout of a chess board at the beginning of the game.

The Moves

A move is defined by the following rules:

  1. The player with the white pieces commences the game. The players alternate in making one move at a time until the game is completed.
  2. A move is the transfer by a player of one of his pieces from one square to another square, which is either vacant or occupied by an opponent's piece.
  3. No piece except the knight may cross a square occupied by another piece. That is, only the knight may leapfrog over another piece.
  4. A piece played to a square occupied by an opponent's piece captures it as part of the same move. The captured piece is removed immediately from the chessboard.
Various pieces move in different ways:

Check

The king is in check when the square it occupies is attacked by one or more of the opponent's pieces; in this case, the latter is/are said to be checking the king. A player may not make a move which leaves his king on a square attacked by any of his opponent's pieces. Check must be parried by the move immediately following. If any check cannot be parried, the king is said to be checkmated or {\em mated}.

The Completed Game

  1. The game is won by the player who has checkmated his opponent's king. This immediately ends the game.
  2. The game is drawn when the king of the player who has the move is not in check, and this player cannot make any legal move. The player's king is then said to be stalemated. This immediately ends the game.