3 Santorini Player with Cards
Due: March 15
For this part, you implement a player program and optionally a turn-checking program.
Building on Santorini Player, we’ll add support for the following cards from the “Simple Gods” set:
Apollo —
A token’s move can optionally swap places with an adjacent opponent token, as long as the token would be able to move to the opponent’s space if the opponent token were not there; otherwise, the move must be to an unoccupied space as usual. Artemis —
The moved token can optionally move a second time (i.e., the same token), as long as the first move doesn’t win, and as long as the second move doesn’t return to the original space. Atlas —
The build phase can build a space currently at level 0, 1, 2 to make it level 4, instead of building to exactly one more than the space’s current level. Demeter —
The moved token can optionally build a second time, but not on the same space as the first build within a turn. Hephastus —
The moved token can optionally build a second time, but only on the same space as the first build within a turn, and only if the second build does not reach level 4. Minotaur —
A token’s move can optionally enter the space of an opponent’s token, but only if the token can be pushed back to an unoccupied space, and only as long as the token would be able to move to the opponent’s space if the opponent token were not there. The unoccupied space where the opponent’s token is pushed can be at any level less than 4. Note that the opponent does not win by having a token forced to level 3; furthermore, such a token will have to move back down before it can move to level 3 for a win. Pan —
A token can win either by moving up to level 3 or by moving down two or more levels. (Moving down three levels is possible if a token was pushed by a Minotaur.) Prometheus —
A token can optionally build before moving, but then the move is constrained to the same level or lower (i.e., the level of the token’s new space can be no larger than the level of the token’s old space). The moved token must still build after moving.
Note that these choices keep two aspects of the game (almost) the same: none of the cards require information about the game history, and all cards except Pan merely extend the set of possible moves for a player (i.e., they don’t disallow moves that would be allowed without a card). Then again, since a token can be forced by Minotaur to level 3 without winning, and since Pan can win by moving down two or more levels, checking for a winning board may now depend on the immediately preceding board. Also, Pan is special in that it disallows a move that would otherwise be allowed: one where the token moves town two or more levels and then builds (where the build step is not allowed, because Pan has already won by moving down).
In a game, each player will have a distinct card, and a player’s turn is affected only by the player’s own card.
3.1 Revised Board Representation
To accommodate player cards, we redefine players from Board Representation, and we define two new terms:
A player is a dictionary with two keys:
"tokens" mapped to an array of 2 spaces, where the order of the spaces in the array does not matter
"card" mapped to a card, where the players of a board have distinct values for "card"
A card is one of the strings "Apollo" "Artemis", "Atlas", "Demeter", "Hephastus", "Minotaur", "Pan", or "Prometheus", with exactly those letters and case
A pre-player is a dictionary that has one key:
"card" mapped to a card
All other definitions are the same modulo references to the revised term player.
For example, if player 1 has the Artemis card and player 2 has the Prometheus card, the JSON representation of
is
{"players":[{"card":"Artemis","tokens":[[2,3],[4,4]]}, |
{"card":"Prometheus","tokens":[[2,5],[3,5]]}], |
"spaces":[[0,0,0,0,2],[1,1,2,0,0],[1,0,0,3,0],[0,0,3,0,0],[0,0,0,1,4]], |
"turn":18} |
3.2 Revised Game Protocol
To accommodate player cards in the Game Protocol, we change the setup phase. Your Santorini player program will first receive a JSON array of one of the following shapes:
An array with 2 pre-players: Your program is the first pre-player, and its card is the one in the array’s first pre-player. Your program should return an array containing the second pre-player followed by a player to represent the starting placement of your program’s tokens (still with the same card). The cards of the given pre-players will be distinct.
An array with one pre-player followed by one player: Your program is the second player, the pre-player in the array specifies the card for your player, and the player in the array represents the opponent’s card and tokens. Your program should return an array of 2 players. The first player in that result array must be the same as the provided one (which was second in the provided array), and the second player in the result array represents the starting placement of your program’s tokens (still with the same card). The cards of the given pre-player and player will be distinct.
For example, to start a where player 1 has the Artemis card and player 2 has the Prometheus card, the player 1 program will receive
[{"card":"Artemis"}, |
{"card":"Prometheus"}] |
A suitable response could be the following, which is sent on to the player 2 program:
[{"card":"Prometheus"}, |
{"card":"Artemis","tokens":[[2,3],[4,4]]}] |
If so, the player 2 program might reply like this:
[{"card":"Artemis","tokens":[[2,3],[4,4]]}, |
{"card":"Prometheus","tokens":[[2,5],[3,5]]}] |
3.3 Test Cases and Turn Checker
As part of updating your player program to work with cards, you are likely to implement turn-checking functionality. You can optionally turn that functionality into a turn-checking program to better make use of a shared test suite. The turn-checking program should loop through the following steps until the attempt to read a string in the first step produces an end-of-file:
read a JSON string, which is a brief description of the test case;
read a JSON board, which corresponds to the board state before a turn;
read a second JSON board, which is a candidate board state after a turn; and
print the JSON string back out for the test-case description;
- print the JSON string "ok" if the second board is a valid result from a player program that is given the first board, or print the JSON string "invalid" otherwise.
Ignore whether it is actually possible to get to the first board’s state on the turn indicated by the board’s turn number. The second board can be valid only when its turn number is one more than the first board’s turn number, but the turn numbers don’t matter otherwise.
The same as for the player protocol, a normal turn-checking input will have JSON inputs that each fit on a single, newline-terminated input line.
As a class, we will collect a shared database of tests here:
When you add a test, the server will make sure that it’s a valid test. The server makes tests available in a format that matches the protocol above: you can feed the tests as input to a turn-checking program, and then you can compare its output to the expected output that is provided on the server.
Please don’t upload lots of automatically generated tests. Instead, add hand-crafted tests for interesting cases. Note that your name is associated to any test that you upload (using a password that has been mailed to you).
3.4 Handin Procedure
As usual, email the instructor an archive or a pointer to an archive (such as a link to Google Drive) that contains your source and a compiled version of your player program. The compiled version should run on the CADE lab1-X.eng.utah.edu machines. Include a "README.txt" file that describes the path within your archive for the player executable.