Handout F3

Quick links:

Contents:

Note: Some 6.170 students have acquired Repetitive Strain Injuries (RSI) over the course of the final project in the past. Don't let it happen to you. It hurts. Please read the MIT web site about RSI before embarking on the final project.

Introduction

Your final project is to design, document, build, and test a program that plays Antichess.

Antichess Overview

Antichess is a variant of chess in which the goal is to either lose all of your pieces (except your king) or checkmate your opponent.

Antichess Rules

The Chessboard

Antichess is played between two opponents by moving pieces on a square board. The board is composed of 64 equal squares. The eight vertical lines 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 columns are labeled a to h from left to right. The rows are numbered 1 to 8 from bottom to top.

The Pieces

Chess board

At the beginning of the game, one player ("white") has 16 white pieces, and the other ("black") has 16 black pieces. The white player pieces are: one King (e1), one Queen (d1), two bishops (c1 and f1), two knights (b1 and g1), two rooks (a1 and h1), and eight pawns (row 2). The black player pieces are: one King (e8), one Queen (d8), two bishops (c8 and f8), two knights (b8 and g8), two rooks (a8 and h8), and eight pawns (row 7). The initial position of the pieces on the chessboard is shown to the right.

The Moves

A move is defined by the following rules:

  1. "White" moves first. 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 jump over other pieces.
  4. A piece played to a square occupied by an opponent's piece captures that piece as part of the same move. The captured piece is immediately removed from the board.

A player's moves are limited by the following fact: A player is forced to capture an opponent's piece whenever possible. If a player can take several of the opponent's pieces, he/she is free to choose which piece to take. This limitation does not exists in regular chess. The only exception to this rule is being in check.

All the pieces move exactly as they do in standard chess. An excellent description of how each piece moves and captures is at http://www.princeton.edu/~jedwards/cif/chess.html. The exceptions in this semester's version of antichess are:

Handling Check Situations

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

  1. If A's king is under check, A must move the king out of check.
  2. A cannot move in a way that causes the king to come into check.
  3. If A can take one of B's pieces, then it must (unless disallowed by the previous rule).
  4. If A's king is under check and A can move in such a way that the king is out of check by either taking B's piece or in some other manner, A must take B's piece.

The king is in check when the square it occupies is attackable 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 attackable by any of his opponent's pieces; e.g., the player cannot move the king into check. Check must be resolved by the move immediately following. If any check cannot be parried, the king is said to be checkmated or mated.

It is the foremost obligation of each player to move the king out of a check. This overrides the rule that you must take an opponent's piece. For example, in the figure below to the left, it is black's turn and black must move its king out of check even though it can take white's bishop on c6 with its rook.

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. For example, suppose the black player's king is under check from white's rook. Further, suppose black has two choices to move away from check — remove the check with or without taking a white piece. In that case, black must take white's piece and remove the check. In the figure below to the right, black's king must take the white bishop with the king in the next move (it cannot simply move the king away from check without taking the bishop).

Example: black cannot take the white bishop Example: black's king must take the white bishop

End of the Game

We shall play antichess with time restrictions, as is often the case in chess games. Each player is allocated a fixed amount of time T to make all the moves, e.g., 5 minutes. There is a clock for each player that is set to T minutes at the start of the game. If it is player A's turn to move, A's clock counts down until the move is made. While it is B's turn to move, A's clock is suspended, and B's clock runs down. 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 2*T minutes, because by then at least one of the clocks has run down to zero.

Player A wins the game against player B if:

  1. all pieces of A except for the king are taken, or
  2. player A checkmates player B, or
  3. player B's timer runs to 0.

If player A checkmates player B and on the same turn takes the last of player B's non-king pieces, player A wins (ie, the checkmate prevails).


Black is stalemated

The game is stalemated if the king of the player who has the move is not in check, and this player cannot make any legal move. In the example on the right, black is stalemated on their turn, since neither their pawns nor king can move. In antichess, the stalemated player loses their turn, and the opposing player may continue to take turns until the stalemate is broken or the game is won.


Awards

Each team may enter its Antichess implementation into a class contest for one or more of the following awards:

Best design:
Awarded for the project with the best abstraction, modularity, extensibility, simplicity, etc. The quality of the final report is also considered.
Best game/usability:
Awarded for the project with the best game-play and best user interface.
Best AI Player:
There will be a tournament to determine which AI player reigns. Only teams with complete, working antichess implementations will be permitted to enter. This competition will have no bearing on grading.

Your AI player must be written in pure Java; we will compile all submissions from source and run them on a computer and JVM of our choosing. In other words, you should focus on high-level algorithmic optimizations, not low-level ones such as choice of bytecode or Java implementation.

Judging will be done initially by the TAs and final judgments will be made by the lecturers. Winners will be announced at the last class on May 16.

Grading, Deliverables, and Schedule

You'll do your project in phases, with the following milestones:

Phase Deliverables Due date
Preliminary Design Preliminary design document 4/18/07
Preliminary Release Source code, specifications, unit tests 5/1/07
Final Release Final design document, source code, specifications, unit tests, user manual, webstart packaging 5/14/07

In addition, there will be weekly meetings with your TA between April 9 and May 11, with a progress document due at each.

Each team will receive a single grade for the final project.

The Final Project Grading, Deliverables, and Schedule document has more information about these stages, how they are graded, and what is required at each. You should be sure to read through this document now, so that you are not caught unprepared by what is expected.

The following additional requirements apply to the Antichess project:

Design Documents

The preliminary and final design documents must discuss, among other issues:

Required Preliminary Release Functionality

At the preliminary release deadline, we will ask you to demonstrate some specific Antichess functionality.

Application
Your antichess game should be playable as a stand-alone application packaged with the JAWS tool. You must also implement a MachinePlayer class which conforms to the specification. The player must only make valid moves, but the AI does not have to be very sophisticated for the preliminary release.
Human-Computer Game
The application should provide functionality for a human-computer game. It must display to the user the board, the timers and whose turn it is. The user should be able to type in moves or use the mouse to control the pieces.
File Save and Retrieve
The program should allow the user to save the current game into a XML based text file as defined by the Game File Format. The current game should continue after the game is saved. The program should also allow the user to load a saved game.

Your GUI should be reasonably easy to use and understand.

Resources

See the Final Project Tools document for information on tools that may help make your work faster, more efficent, and more enjoyable.

What We Give You

We are providing you with images for various chess pieces. They are available in the directory /mit/6.170/www/psets/antichess/images.

In addition we provide you with a Specification for machine players.

Hints

General

Design
A careful design will save you a lot of time in the long run. 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. The preliminary design is a major part of the project (more so than its proportion of the grade might indicate). Do it very carefully, trying to anticipate problems that may arise. Then the rest of your project will be more straightforward and more fun.
Prototype
One of the largest challenges for this kind of design problem is figuring out where the "gotchas" are. If you are having difficulty imagining how to structure one part of the design it sometimes helps to build a small prototype.
Validate early and often
Validation shouldn't be an afterthought! You may choose a design because its implementation will be easier to test. Make sure you validate your code as you implement.
Document early and often
Incomplete documentation is better than no documentation at all. 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 few sentences in your document describing the issue. Later, if you have time, you can go back and fix it.
Don't over-document
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. Similarly, 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.
Have fun being on a team
Enjoy being part of a team. Run new ideas past your partners, and discuss problems with them. Read and discuss each others code. A good way to find a bug is to ask someone else to look at your code. Start early!
Communicate effectively
Each meeting you hold with your team members (or your TA) should have: The roles should rotate among the group members; in 6.170, no one individual should perform any of the roles disproportionately often.
Prioritize
Our goal is to provide you with a project that is both very challenging and offers many opportunities for you to be creative. We encourage you to experiment. Make your implementation of Antichess as beautiful to watch and as fun to play as possible. That said, make sure you get the basic functionality working before you add new features, expert level AI players, or an elaborate GUI. The best way to approach extensions to the project is to make your initial design flexible and extensible.

Performance

The most important issues when you design, build, and test your system are correctness and clarity. It is better to have a program that follows the rules and that implements your desired algorithm than it is to write one that quickly does the wrong thing. If you focus upon speed, you are more likely to encounter serious correctness problems. Similarly, good abstraction and an understandable design are more important than speed -- both in the real world, and because we will be grading on those real-world issues We will grade your submission primarily based on good modularity, documentation, abstraction, and other standard, important software engineering principles, not on less-important principles such as speed. If you want to sacrifice the former for the latter, you will sacrifice your grade as well. In any event, it is a false dichotomy: the best submissions do well on both, for a better design is more likely to be fast and is certain to permit you to apply optimizations easier.

Within the constraints of correctness and clarity, a faster implementation will perform better in the competition. This section discusses some speed-related issues. You should also see Bloch's Effective Java.

If you wish to optimize, you should focus on high-level issues such as representations and algorithms, which may give you orders of magnitude more speed increase than low-level optimizations can. Are you repeating computation? Are you needlessly creating objects? Does a more efficient algorithm exist? Even more importantly for Antichess, does a better static evaluation function exist?

When thinking about speed, you should never bother with low-level issues like whether to inline a particular procedure or whether i+=1 is faster than i=i+1. The first reason is that a good JIT already does a good job at such optimizations; it can be far more effective working at the machine code level than you can at the source level. The second reason is that you are likely to get these wrong; one reason that many modern compilers lack an inlining directive is that experience has shown that programmers' intuitions about inlining are wrong more often than they are right. The third reason is that such tiny performance improvements are unlikely to make a real difference: a couple of percent won't matter either way in the competition (or in most real-world environments).

In order to discourage counterproductive optimizations, and for other reasons, we require you to submit source in pure Java. We will compile it with a compiler of our choice and run it on a JIT of our choice. Both will be state-of-the-art tools, so you don't have to worry about low-level optimizations. Use of a uniform compiler will ensure fair comparison across students: we don't want to test a student's infrastructure or toolset, but the code.

Optimization aphorisms everyone should know (from Effective Java)

More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason — including blind stupidity. — William A. Wulf

We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. — Donald E. Knuth

We follow two rules in the matter of optimization: Rule 1. Don't do it. Rule 2 (for experts only). Don't do it yet — that is, not until you have a perfectly clear and unoptimized solution. — M. A. Jackson

Appendix 1: Detailed Requirements

Antichess Application

Your antichess game should be packaged with the JAWS tool, and be playable as a stand-alone application.

The basic mode of your game will be to have a human play against a machine player. Your application should use Java Swing (or SWT, if you choose) to provide a graphical user interface for this form of play.

In addition, we will provide a referee which will allow your machine player to play against machine players from other groups. In order to be able to do this, you must provide a MachinePlayer class which conforms to the specification.

The referee will use the information about what has changed on the board, plus its knowledge of the current state, to display the status of the game. It will check each response for legality, it will keep track of the time left for each player, and it will display the status of the game. We will provide our own referee that does these things. We will use this referee in the tournament. You will not implement the referee in this problem set; only the antichess player. Of course, in a human-computer game, your program has to provide functionalities very similar to the referee (e.g. checking for illegal moves, displaying status, etc.).

Your machine player cannot simply make arbitrary moves. As such, you must implement decision heuristics to enable your machine to play well. Do not let this task intimidate you. Any of several basic and easy to understand algorithms will be sufficient. For example, you might read up on minimax or alpha-beta search methods. A good introductory text on such search methods is Russell & Norvig, AI: A Modern Approach.

Your machine player must also demonstrate the advantages of multi-core computers. Given the same time limit, the machine player on a multi-core machine must beat the same machine player on a single-core machine 51% of the time.

Human-Computer Game

The graphical interface for a human-computer game should display at least the following information:

Once the game has started, the following functionality must be available:

Computer-Computer Games

This section describes the standard features that your program must support in order to work with our referee. Your program is required to conform to these specifications (even if you do not intend to enter the tournament), and the specifications may not be changed.

We define a standard string format for a chess move. The string should be 5 characters long. The first two characters represent the square where the piece began, the third character is a dash (-), and the next two characters represent the square where the piece moved to.

<move-desc> ::= <position>-<position>
<position> ::= <col><row>
<col> ::= a | b | c | d | e | f | g | h
<row> ::= 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8

You must implement a class that implements the MachinePlayer interface.

Your machine player is allowed to use multiple threads and be thinking while the opponent is thinking and making his move. However, you are not allowed to use the resources of a computer other than the one running the machine player.

Text UI

Your antichess application should support the text user interface specified in the javadoc for antichess.TextUI. Your implementation of this class should provide a public main method. The TextUI should wait for user input without providing a prompt. Nothing should be printed when the application is first started. Valid user input is in the form of commands. Outputs as a result of a command should be zero or more lines, each terminated with a newline.

The main method must be executed with zero command-line arguments. The behavior of the TextUI is unspecified when main is run with one or more command-line arguments — you may wish to provide command-line arguments in order to facilitate ImplementationTests or other provide other functionality not covered by this specification.

We have provided a sample input file, sample.input, and its expected output, sample.expected. You ought to be able to run your text UI and compare its results using the following athena commands:

% java antichess.TextUI < sample.input > sample.actual
% diff -u sample.expected sample.actual

Game File Format

Game files are stored in an text file format known as XML which stands for eXtensible Markup Language. XML has a well-defined, treelike structure; thus, it is straightforward to check if an XML document is well-formed (as compared to an arbitary tab- or space- delimited file format, which might be easier for people to read). Java 5 has built-in XML support, called JAXP (tutorial). Also see the w3schools tutorial on XML.

Not every XML file is a valid Antichess game file. An XML Schema is a file written in XML that defines the desired format for other XML files.

We provide you with an XML Schema antichess.xsd that defines the XML format for a game of Antichess. Any XML file that validates against antichess.xsd should be able to be loaded by your antichess application. Conversely, any XML file that does not validate against the schema should be rejected by your application, and an appropriate error message should be displayed to the user.

We have made the schema eXtensible, so if you decide to add special features to your own Antichess application, you can extend our file format to save this additional information. Because of this flexibility, game files written by your application should be able to be read by any other team's application, even if the other applications do not have, or do not know about, the special features that your application has.

So What Does a Game File Look Like?

Take a look at the sample game file, aptly named sample_game.xml. As you can see, the file records the historic, as well as the current, information for the game. This example records a saved game where white won by checkmating the black king.

Semantics

We now describe each element of the game file, in turn.

<game ruleset="STRING">   ...   </game>
Defines the game. There must be exactly one of these tags in a valid game file. The required ruleset attribute specifies the name of a variation of antichess; this semester, the ruleset should be 6170-spring-2007.

A game element contains four subelements: time, moveHistory, pieces, and gameOver.

<time timed="BOOLEAN" initWhite="MILLISECONDS" initBlack="MILLISECONDS"
      currentWhite="MILLISECONDS" currentBlack="MILLISECONDS" />
Defines the state of the game clock. The boolean timed attribute is true if the game is timed. The initWhite and initBlack attributes represent the total time given to White and Black (respectively) at the start of the game; this should be an integer, representing time in milliseconds. The currentWhite and currentBlack attributes represent the time left on the clock for White and Black at the point in the game when this file was saved, also in milliseconds.
<moveHistory>  ...  </moveHistory>
The moveHistory tag is just used as a container for zero or more move tags. It takes no attributes.
<move side="SIDE" value="MOVE" time="MILLISECONDS" />
Represents a move.  The side attribute must be either white or black. The value attribute is a move described in the standard string format given above, e.g. "c5-c7". The time attribute specifies the time remaining on the clock of the player who made the move, in milliseconds. If the game is untimed, then this attribute may be omitted.
<pieces>  ...  </pieces>
The pieces tag is just used as a container for zero or more square tags. It takes no attributes.
<square id="SQUARE" side="SIDE" piece="PIECE" />
Specifies where a piece is located. The id attribute is a location on the chess board, such as c5. The side attribute must be either white or black. The piece attribute is one of pawn, rook, knight, bishop, queen, or king.
<gameOver winner="SIDE" description="GAME-END" />
This is an optional tag, which is only present if the saved game is over. The winner attribute must be either white or black. The description attribute indicates how the game ended; the choices are piecesLost, checkmate, timeExpired, or stalemate. (Note that, in our version of antichess, stalemate does not end the game and so is not a valid value for this tag even though the schema permits it.)
The formal definition of the file format can be found in the schema antichess.xsd. Basically, the schema defines which elements it expects to see in an XML file and notes where the format may be extended. (These extension points are denoted by either <xs:any> or <xs:anyAttribute>.) You don't have to understand the schema unless you want to extend it to support any new features you've designed. If you want to extend the schema, the XML Schema Tutorial will be helpful.

Appendix 2: Threads

To make a responsive Antichess GUI you need to know about threads. Threads are hard — just look at the API for java.lang.Thread for crying out loud! Many of the methods are deprecated because even the guys at Sun couldn't get it right the first time. Thus, to help you with your final project, we're going to provide you with some snippets of sample code that use threads that you can use in your project, if you like.

Asking the GUI for a Move

The most common thread-related problem in the Antichess project is figuring out how to ask the GUI for a move when it is a human player's turn. Suppose your application has a Player interface that has a method called getMove() that your application invokes to get the move from a Player. The problem is that when getMove() is invoked, there is no limit on how long the player can take to return a move. More importantly, while the player is trying to choose his move, the rest of the application should not lock up because it is waiting for the getMove() to finish. To make a responsive GUI, you have to take advantage of the main thread (the one that you use that calls getMove()) and the GUI thread (the one that the Java Virtual Machine supplies for you that responds to mouse clicks and keystrokes). Imagine the following snippet of code is inside some GUI class that implements Player:

/**
 * a field variable that represents the move the player
 * made through some form of input into the GUI
 */
String move; 

/**
 * method of the Player interface that gets the player's move
 */
public synchronized String getMove() {
  try {
    while (move == null) {
      try {
        this.wait();
      } catch (InterruptedException e) {}
    }
    return move;
  } finally {
    move = null;
  }
}

/**
 * method of the MouseListener interface that this GUI
 * class also happens to implement
 */
public synchronized void mouseClicked(MouseEvent e) {
  // do some computation on e
  // to figure out what move the user made
  // this will probably require keeping track of
  // the previous click as well
  if (clickResultsInValidMove) {
    move = whateverTheValidMoveIsBasedOnTheMouseClick;
    this.notify();
  }
}

Basically, what happens is that when getMove() is invoked, it enters the while loop which polls to see if move has been set, periodically sleeping in-between to free up the processor to other threads. While the main thread is inside getMove(), the GUI thread may enter mouseClicked() and set the move variable so that the next time move is polled by the while loop inside getMove(), it will break out of the loop and return the move.

Note: This sample code may not be an exemplary use of threads in Java; however, it is a sufficient solution for the unresponsive-GUI problem.

Updating the Clock Every Second

You can use the observer pattern to decouple the Swing component that displays the clock from the clock itself. Consider creating an interface called StopwatchListener that listens for changes on a stopwatch that sends updates at least once per second:

/**
 * A StopwatchListener listens for time updates from a stopwatch.
 */
public interface StopwatchListener extends java.util.EventListener {
  
  /** tells how much time is left on the stopwatch, in milliseconds */
  public void timeChanged(long milliseconds);

}

Now consider creating a Swing component that listens for updates from a stopwatch. In this case, we create a label that displays the time for only one stopwatch.

public class TimeLabel extends JLabel implements StopwatchListener {
  
  public void timeChanged(long milliseconds) {
    long seconds = milliseconds / 1000;
    long minutes = seconds / 60;
    seconds = seconds % 60;
    String time = String.valueOf(minutes) + ":" + String.valueOf(seconds);
    setText(time);
  }

}

So where does the threading come in? We use a java.util.Timer to schedule periodic updates to our StopwatchListener. By using Timer, we do not use the Thread class directly; however, Timer provides a valuable abstraction around threads because it has a cancel method that allows us to cancel the events that we schedule. Were we to use a Thread, we would not be able to use its stop() method as Thread.stop() is deprecated. Note that there is also a timer class for scheduling GUI events, javax.swing.Timer. See Using Timers in Swing Applications for more details about when to use which timer.

When using java.util.Timer, we can only "schedule" events that are objects that extend java.util.TimerTask. Thus, we create a TimerTask that updates its listeners as follows:

import javax.swing.event.EventListenerList;

public class Stopwatch extends TimerTask {

  private boolean running = false;

  private long millisLeft;

  private long startTime;

  /** @requires millisLeft >= 0 */
  public Stopwatch(long millisLeft) {
    this.millisLeft = millisLeft;
  }

  public void start() {
    if (running) return;
    running = true;
    startTime = System.currentTimeMillis();
  }

  public void stop() {
    if (!running) return;
    running = false;
    long stopTime = System.currentTimeMillis();
    millisLeft -= (stopTime - startTime);
  }

  public long getTime() {
    if (running) {
      return (millisLeft - (System.currentTimeMillis() - startTime));
    } else {
      return millisLeft;
    }
  }

  /*
   * To see where all of this crazy listener code comes from,
   * check out javax.swing.event.EventListenerList
   */

  private EventListenerList listenerList = new EventListenerList();

  public void addStopwatchListener(StopwatchListener s) {
    listenerList.add(StopwatchListener.class, s);
  }

  public void removeStopwatchListener(StopwatchListener s) {
    listenerList.remove(StopwatchListener.class, s);
  }

  protected void fireTimeChanged() {
    // Guaranteed to return a non-null array
    StopWatchListener[] listeners =
      listenerList.getListeners(StopWatchListener.class);
    // Process the listeners last to first, notifying
    // those that are interested in this event
    long time = stopwatch.getTime();
    for (int i = listeners.length - 1; i >= 0; i --) {
      listeners[i].timeChanged(time);
    }
  }

  public void run() {
    fireTimeChanged();
  }

}
As you can see, when the run() method is invoked, the latest time is acquired from the Stopwatch and is sent to every registered StopwatchListener. Thus, the following would be a plausible way to use Stopwatch:
/**
 * This is an incomplete Game class, but it demonstrates
 * the use of the code above.
 */
public class Game {

  private Player whitePlayer, blackPlayer;

  private Stopwatch whiteWatch = new Stopwatch(5 * 60 * 1000);

  private Stopwatch blackWatch = new Stopwatch(5 * 60 * 1000);

  private Timer timer = new Timer();

  /**
   * Creates a simple clock panel that will get updated by the stopwatches.
   */
  public JPanel createClockPanel() {
    JLabel whiteLabel = new TimeLabel();
    JLabel blackLabel = new TimeLabel();
    whiteWatch.addStopwatchListener(whiteLabel);
    blackWatch.addStopwatchListener(blackLabel);
    JPanel panel = new JPanel();
    panel.add(whiteLabel);
    panel.add(blackLabel);
  }

  /**
   * Stops one player's clock and starts the other's.
   */
  private void switchClockTo(boolean white) {
    // stop the clock for the previous player
    Stopwatch lastWatch = (white) ? blackWatch : whiteWatch;
    lastWatch.stop();
    timer.cancel();

    // start the clock for the next player 
    Stopwatch nextWatch = (white) ? whiteWatch : blackWatch;    
    timer = new Timer();
    timer.scheduleAtFixedRate(nextWatch, 100, 500);
  }

}

Q & A

This section lists clarifications and answers to common questions about Antichess.

Question
I'm getting weird errors trying to use a validating parser. Help!
Answer
There are two XML parsing frameworks in the Java 1.5 SDK, SAX and DOM. My advice is to either: (a) use the SAX parser, which does proper validation via the setSchema method without jumping through any hoops, or (b) use a non-validating DOM parser (ie, don't call setSchema) and validate by using the SchemaFactory to generate a Schema which can generate a Validator to check your DOMSource after parsing.
Question
I don't know how stalemate works with MachinePlayer and the tournament.
Answer
Sorry this wasn't clear at first.

Yes, there has been some ambiguity about how a player loses his/her turn during stalemate, especially for MachinePlayer and the tournament. When there is a stalemate by player A (meaning there are no legal moves for player A), then that player A has to return an empty String in the makeMove function. All other moves (there really should be none in a stalemate) will be considered illegal moves and will be disqualifiable by the referee. Player A can decide whether to quickly return the empty String, or hog the clock for a while doing computations, before returning it.

When player B receives a move by the opponent that is an empty String, it means that the board should remain as it is. This situation happens when (1) player B is starting the game, or (2) player A was stalemated in the previous move.

Currently, there are no plans for 3-fold repetition to lead to a draw. That means, if players keep repeating the same move, and one keeps forcing another into a stalemate, then one of the other endgame conditions that will differentiate the players for the win will be whoever's time runs out first.

Errata

April 16: Clarified that JAWS was required for both the prelim and the final release.

April 29: sample.input and sample.expected have been corrected for an illegal king move.