6.031
6.031 — Software Construction
Fall 2020

Crossword Extravaganza Specification

For this project, you will be implementing a system that will host collaborative multiplayer crossword puzzles. The system will consist of a server that will host the puzzles, and a set of clients that can fill in the crosswords.

Crossword puzzle format

When the server boots up, it will load a set of crossword puzzles from the directory indicated by the command line argument. The directory is expected to contain one or more files with a “.puzzle” extension, each describing a different crossword puzzle. Each puzzle file is expected to have the format described below.

FILE ::= ">>" NAME DESCRIPTION ENTRY*
NAME ::= String
DESCRIPTION ::= String
ENTRY ::= "("  WORDNAME ","  CLUE "," DIRECTION "," ROW "," COL ")"
WORDNAME ::= [a-z]+
CLUE ::= String
DIRECTION ::= "DOWN" | "ACROSS"
ROW ::= Int
COL ::= Int
String ::= '"' [^"\r\n\t\\]* '"'
Int ::= [0-9]+

For example, a puzzle file may look like this:

>> "Simple Puzzle" "A puzzle designed to show how puzzles work"
 (cat, "feline companion", DOWN, 0, 1)
 (mat, "lounging place for feline companion", ACROSS, 1, 0)

The first line of the file format is a header which gives a name and a short description to the crossword puzzle. The rest of the file includes a description of each entry in the puzzle. The first two elements of an entry are the word corresponding to that entry and the clue that will be shown to the player. The third element indicates whether this will be a vertical word which flows down, or a horizontal word which flows across from left to right. Finally, the last two elements indicate the coordinates of the first cell of the word in the grid of the puzzle. Note that the coordinates are 0-indexed, and the upper-left corner of the grid has row 0 and column 0. So for example, the crossword definition above corresponds to the puzzle shown below it.

The file format should be mostly oblivious to whitespace, except for any spaces inside a string literal. Java-style single-line comments (such as // this is a comment) must also be supported.

Here is a more elaborate example:

>> "Bigger Puzzle" "A slightly bigger puzzle"
 (cat, "feline companion", DOWN, 0, 1)
 (mat, "lounging place for feline companion", ACROSS, 1, 0)
 (car, "gas powered vehicle", ACROSS, 0, 1)
 (tax, "nobody likes April 15", ACROSS, 2, 1)

File consistency

As the game server loads the puzzle files it will have to check them for consistency. In addition to the syntactic constraints outlined above, there is an important semantic constraint: each file must correspond to a valid crossword puzzle. In particular, what this means is that if two words intersect, they must intersect at the same letter. The simple example above satisfies that constraint, because the second letter of ‘cat’ and the second letter of ‘mat’ intersect at position (1,1). On the other hand, the puzzle shown below would not be a consistent puzzle because the second letter of ‘cat’ is now on the same cell as the first letter of ‘mat’, but ‘a’ and ‘m’ are not the same letter.

>> "Invalid Puzzle" "A trivial buggy puzzle"
 (cat, "feline companion", DOWN, 0, 0)
 (mat, "lounging place for feline companion", ACROSS, 1, 0)

Your consistency check should also forbid words that overlap in the same direction, so for example, the following puzzle should be illegal.

>> "Bad Overlap" "Puzzle should be rejected because of overlapping words"
 (there, "This word is fine", ACROSS, 0, 0)
 (real, "Illegal overlap with there", ACROSS, 0, 3)
 (starting, "Another example, this word is fine", DOWN, 2, 2)
 (art, "Illegal overlap because this word is contained within the previous word", DOWN, 4, 2)

Additionally, we require all words to be unique, so the same word should not appear twice in the puzzle. The grammar already enforces that words must be in all lowercase and should consist only of letters, but no additional characters.

Server

Running the command java -cp classpath crossword.Server puzzle_folder should start the server, where classpath describes where to find your compiled Java code and libraries (you can find more details of running programs from the command line here). After it is started, the server should read all the *.puzzle files in the puzzle_folder given in the command line; it should then wait for client connections on port 4949.

When the server starts up, it must attempt to read in all *.puzzle files in a designated folder (given as a command line argument) as if they were puzzle files. Any files that are either ill-formatted or inconsistent should be rejected and ignored, but any remaining valid puzzles should be loaded and be ready to be displayed to users. So if your directory contains fun.puzzle, bad.puzzle and exciting.puzzle, but bad.puzzle fails the consistency checks, your server should still boot up normally and allow users to play with the fun and exciting puzzles.

After the server loads the puzzles, it is ready to accept connections from clients. It should listen on port 4949.

The state of the server consists of games (puzzles that have at least one player working on them) and players who are playing them. There may be multiple games in progress that are trying to solve the same puzzle; each game has its own state of filling in the puzzle.

A client’s requests should be associated with a player id, which is otherwise unspecified. The player id might be provided by the client, or assigned by the server.

A client should be able to:

  • find out the set of active games
  • join an existing game
  • find out the set of available puzzles
  • create and join a new game for a puzzle

Playing a game

A game starts with its puzzle grid empty, no letters filled in.

A player should be able to make these actions in the game:

  • enter a word as a proposed answer to a clue;
  • erase a word from the puzzle, clearing its grid cells;
  • check the current puzzle against the solution, which keeps any entered letters that match the solution, and erases letters that do not match the solution.

When a word is entered or erased, any previous letters entered in the word’s grid cells are overwritten or erased.

When the puzzle grid has been filled completely, and a check request from a player finds that it matches the solution, then the game is now solved, and that state change should be made visible to players.

Although the enter and erase actions are described here as involving whole words, you can define them at the level of individual grid cells instead, if that makes more sense for your user interface design. For example, if your user interface is a web page or graphical user interface, then the user may type or delete letters one at a time. If your user interface uses text commands, however, then entering or erasing whole words makes more sense.

Many details of play are left unspecified, meaning that the design decision is up to you. This includes, for example, what happens when two players’ actions would affect the same grid cell(s) at the same time. But a player action should not crash the client or server, or put a game into a broken state where it is impossible to continue trying to solve it.

Client

The client user interface should allow a human player to play the game, with sufficient instructions for a user who has not read this handout to know what to do at each point.

You have design freedom in what the UI should look like and how it should be implemented. Options include:

  • a textual interface that accepts text commands from the user and displays the crossword as an image in a window (similar to PS3)
  • a web UI that displays the crossword in the web browser (similar to PS4)
  • a graphical user interface that displays the crossword in a window

Choose a UI style that all members of your group understand and would be comfortable with implementing. If you don’t know which one to choose, then the PS3-style textual interface is strongly recommended.

The UI should show the puzzle in a window or web page using appropriate elements to render the grid cells (i.e., drawn squares, or text-entry fields, or bordered elements). The UI should not show the puzzle as text output that renders grid cells using ASCII art.

The client user interface should allow a user to:

  • see a list of games to join, and a list of puzzles to start a new game;
  • join an existing game or start a new game on the server;
  • enter words, erase words, and check the solution.

The user interface must display sufficient instructions for a user to be able to use it without having to look at source code or other documentation. For example, if your UI uses text commands, it should display all available text commands when it starts up.

The user’s view of a game should include:

  • the clues
  • the grid (including letters filled in so far)
  • the names of other players in the game
  • an indication of whether the game is solved

The view should update automatically to reflect actions made by other players.

The list of clues and the grid should have corresponding numbers, so that a user can easily tell which clue corresponds to each word in the grid. The figures in this handout show one way to do this, where each clue has a unique number, and the grid makes clear whether the clue is across or down. Another reasonable way is conventional crossword numbering, in which grid cells that start a word are numbered, and the clues are divided into across and down clues numbered by their starting grid cell.

If your client is a Java program (as opposed to using a web browser), then it should be started through the command line java -cp classpath crossword.Client machine_address (more details of running programs from the command line here). The machine_address argument should correspond to the address of the server to connect. For example, to connect to a server running on your own machine, you can run java -cp path crossword.Client localhost.

If your client uses a web browser, then it should be accessed by http://machine_address:4949. For example, to connect to a server running on your machine, you would use http://localhost:4949.

You may need to take special steps to connect to a server running on your teammate’s computer.

Security

One important aspect of any client-server design is that you never want to transmit to the client any information that you wouldn’t want the user of that client to have, because even if the client interface does not display this information, a malicious user could reverse engineer the interface and gain access to this information. In the case of your game, this means that you never want to transmit to the client any information about the puzzle beyond what the interface is supposed to display. For example, don’t send the solution to an unsolved puzzle to the client.