Assignment 3 – Welcome To:   the state of the game
1 Design, Build and Test the Player State
2 Design, Build and Test the Game State
3 Visualization Support

Assignment 3 – Welcome To: the state of the game

Due Wednesday 4/13 5:00pm for the testing deliverables and Friday 4/15 11:59pm for the code deliverables

1 Design, Build and Test the Player State

Design and implement a component for the player state of Welcome To. The rules give you an overview of the game. (You may need to login to access the rules; use <netid>@ads.northwestern.edu and be on the watch out to click an “SSO” button if you see one.)

Your component should be oblivious to the exact rules of the game. Instead, all it “knows” is that it represents the state of the game as two separate objects/records/values (depending on your choice of programming language). One is the state of the game that is specific to a player (and thus there will be multiple copies of that state, one for each player) and the other is the state of the game that global to all of the players (and thus there will be just one copy of that state per game).

It is up to you to decide what specific interface your component should implement and how. However, your code should come with a clear description of the interface you pick. The description should include an account of all operations the component is supposed to provide and all their contracts. You should also enhance your implementation with code that enforces as many of these contracts as possible and produces appropriate errors if it discovers that the contracts are violated.

For this assignment, and to facilitate testing the component against other teams’ code in the future, you must implement a component that renders the two different states to JSON and reads them from JSON. This assignment specifies the precise JSON that all teams implement.

The grammar below describes the shapes of JSON values that test driver must accept in a similar manner to the description from assignment 2. We have two new element in the new grammar: natural and the red ellipsis. A natural is any nonnegative integer, written without any decimal point. So 0 is a natural, but 0.0 is not.

The red ellipsis appears only in JSON lists and it means the item just before the ellipsis may appear zero or more times in the list, at that position. If the item appears zero times then any preceding comma disappears so that the list remains valid JSON. If the ellipsis ends with a traling number, that means that there must be exactly that many repetitions of the previous element, i.e., a list with a fixed size.

player-state

 ::= 

{ "agents" : [ natural, ... ], "city-plan-score" : [ nb, ...3 ], "refusals" : natural, "streets" : [ street, ...3 ], "temps" : natural }

street

 ::= 

{ "homes" : homes, "parks" : natural, "pools" : [ bool, bool, bool ] }

homes

 ::= 

[ house, used-in-plan, [ fence-or-not, house, used-in-plan ], ... ]

used-in-plan

 ::= 

bool

fence-or-not

 ::= 

bool

house

 ::= 

[ natural, "bis" ]

 | 

nb

nb

 ::= 

natural

 | 

"blank"

bool

 ::= 

true | false

The non-terminal player-state captures the player-specific state. There are a number of constraints on the player state that are not expressed in the grammar:
  • The ellipsis in the street non-terminal is restricted to a specific number that depends on which street. The first street has 10 houses so the ellipsis repeats 9 times. Similarly the second street has 11 houses (so there must be 10 repeats) and the third has 12 (so there must be 11 repeats). Also, there are at most 3 parks in the first street, 4 parks in the second street, and 5 parks in the third street.

  • The length of the list of naturals for the agents must be 6 and the values are limited to the number of distinct agent counts that can be marked off in the bottom of the player sheet, reading from left to right, e.g., the third element of the agents list must be between 0 and 3.

  • The other natural numbers number are also limited to specific ranges, based on the rules and what you can see in the player sheet. Refusals must be between 0 and 3. House numbers must be between 0 and 17 and sorted (see the rules for more details on house numbers and how bis affects the sorting). Finally, the number of temps is between 0 and 11.

  • The number of parks must be less than or equal to the number of (non-bis) houses on the same row.

  • Any home used in a housing plan must have a number (possibly a bis) on it.

  • Any pool that’s claimed must be claimed with an actual house number (so not a bis and not empty).

In future assignments, you may assume that any test case you code receives obeys those constraints (and is valid JSON) and the test cases you produce must follow them. For this assignment, however, your parser must accept any valid json and determine if that JSON matches the constraints listed above. Then it must convert that JSON into your internal state.

In general, it is a good idea that you actually have contracts that check the invariants of your data structure and that signal an error when they encounter a violation of the constraints (to help you debug other code that builds on this library, later).

Your test driver will receive a JSON object as input and then must read it in and attempt to parse it into your internal data structure. If it is well-formed, it should then transform your internal game state back to to JSON and print it out. If not, it should simply print out false. It may assume the input is well-formed JSON; your parser must not crash for any well-formed JSON.

When the game is first started, the player sheet is empty:

image

and this is the corresponding JSON:

{"agents":[0,0,0,0,0,0],"city-plan-score":["blank","blank","blank"],"refusals":0,"streets":[{"homes":["blank",false,[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false]],"parks":0,"pools":[false,false,false]},{"homes":["blank",false,[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false]],"parks":0,"pools":[false,false,false]},{"homes":["blank",false,[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false],[false,"blank",false]],"parks":0,"pools":[false,false,false]}],"temps":0}

A while later, as the game progresses, the state may look like this:

image

and this is the corresponding JSON:

{"agents":[0,1,0,2,0,4],"city-plan-score":["blank",6,"blank"],"refusals":1,"streets":[{"homes":[1,false,[true,2,true],[false,3,true],[false,4,true],[true,5,false],[false,6,false],[false,7,false],[false,8,false],[false,9,false],[false,10,false]],"parks":3,"pools":[false,true,true]},{"homes":[0,false,[false,1,false],[true,2,false],[false,[2,"bis"],false],[false,"blank",false],[false,[6,"bis"],false],[false,6,false],[true,10,false],[false,"blank",false],[false,12,false],[false,16,false]],"parks":1,"pools":[true,false,false]},{"homes":[1,false,[false,2,false],[true,3,false],[true,4,true],[true,5,true],[true,6,true],[true,7,false],[false,8,false],[false,9,false],[false,10,false],[false,11,false],[false,12,false]],"parks":2,"pools":[false,false,false]}],"temps":4}

Testing Deliverables: In your team’s GitHub repository, create a directory “Deliverables/3/3.1/” and put at least ten correct input files containing json for the player state and ten incorrect input files that have valid json but aren’t valid player states. Be thoughtful in the way you create the invalid JSON; try to make ones that are interestingly incorrect. If the file contains a correct input, the corresponding output file should be the same JSON object. If the file contains an incorrect input, the output file should contain only false. Follow the filename conventions for the test files as in assignment 2.

You will receive up to twenty points for your tests, one points for each valid pair of input and output files.

Code Deliverables: In your team’s GitHub repository, create a directory “Deliverables/3/3.1/” and deposit there a Makefile for your test driver. It should accept a JSON file on stdin and it should convert it into your internal representation for the player state. If this is possible, it should convert the internal representation back into JSON and print it out. If the JSON does not correspond to a valid game state, it should instead print false.

You will receive up to 50 points for your code.

2 Design, Build and Test the Game State

As with the player state, you must design a data structure to represent the game state and a parser and printer that convert your game state to and from JSON. This is the grammar for the game state:

game-state

 ::= 

{ "city-plans" : [ city-plan, ...3 ], "city-plans-won" : [ bool, ...3 ], "construction-cards" : [ construction-card, ...3 ], "effects" : [ effect, ...3 ] }

city-plan

 ::= 

{ "criteria" : criteria, "position" : position, "score1" : natural, "score2" : natural }

position

 ::= 

1

 | 

2

 | 

3

criteria

 ::= 

[ natural, ... ]

construction-card

 ::= 

[ natural, effect ]

effect

 ::= 

"surveyor"

 | 

"agent"

 | 

"landscaper"

 | 

"pool"

 | 

"temp"

 | 

"bis"

bool

 ::= 

true

 | 

false

The game-state captures the player’s view of global game state. The full game state has more information in it (e.g., which cards are coming next) but this information is not revealed to the player so it not included in these JSON messages. The game state shows what the three current top cards are (in the construction-card field, which shows what the next turn’s effects will be), the three effects that were present on the back of the construction cards that were used in the previous turn and thus can be used on this turn (in the effects field), as well as the three city plan cards, and whether or not anyone has yet claimed them (in the city-plans and city-plans-won fields, respectively). All three city plan cards must have distinct positions.

In addition to the restrictions imposed by following the grammar, the natural numbers in the critera field of a city-plan must be in sorted order. Also, the natural in the construction cards must be between 1 and 15. In class I said between 2 and 15, but this was wrong; the cards go down to 1. Note that you may not go to -1 when using the temp agency effect; when it is combined with a 1, only the numbers between 0 and 3 are available.

The game has additional restrictions on these fields based on the specific cards that come with the game but we will not restrict the cards in the game state JSON based on the specific cards that come in the box.

Here is an example JSON object corresponding to a game state with the first three city plan cards from the box and three construction cards that have the numbers 1, 2, and 3 on them.

{"city-plans":[{"criteria":[1,1,1,1,1,1],"position":1,"score1":8,"score2":4},{"criteria":[1,1,1,6],"position":2,"score1":11,"score2":6},{"criteria":[1,2,6],"position":3,"score1":12,"score2":7}],"city-plans-won":[false,false,false],"construction-cards":[[1,"surveyor"],[2,"landscaper"],[3,"pool"]],"effects":["agent","bis","temp"]}

Testing Deliverables: In your team’s GitHub repository, create a directory “Deliverables/3/3.2/” and put at least five correct input files containing json for the game state and five incorrect input files that have valid json but aren’t valid game states. Be thoughtful in the way you create the invalid JSON; try to make ones that are interestingly incorrect. If the file contains a correct input, the corresponding output file should be the same JSON object. If the file contains an incorrect input, the output file should contain only false. Follow the filename conventions for the test files as in assignment 2.

You will receive up to ten points for your tests, one point for each valid pair of input and output files.

Code Deliverables: In your team’s GitHub repository, create a directory “Deliverables/3/3.2/” and deposit there a Makefile for your test driver. It should accept a JSON object on stdin and it should convert it into your internal representation for the player state. If this is possible, it should convert the internal representation back into JSON and print it out. If the JSON does not correspond to a valid game state, it should instead print false.

You will receive up to 50 points for your code.

3 Visualization Support

If you want to see a picture of the player state drawn on the board, install Racket, and run the oracle installation command from the CI Guide (it is the line in the yml file that starts raco pkg install). After that, you should be able to run

racket -l welcome-to-oracle/oracle/show-ps -- ps.json

and, if ps.json contains a valid game state, you will see a window appear with the game state drawn in it.