2 Economancy Player
Due: February 16
Economancy is a card-drafting game that can be played in base mode or with a reduced or expanded set of cards. The game works with 2 to 4 players, and we will create player programs that work in any of those configurations. At first, we’ll consider a subset of the base cards, leaving more base cards as a future addition to the assignment.
2.1 The Rules
See the Economancy site for the game rules. You’ll need to extract card details from there, either from the setup picture showing cards in the shop area, or from the PDF linked at the end of the rules.
2.2 Game State Representation
Your Economancy player can use any representation internally that you find convenient, but to play against other Economancy players, we’ll need to pick a common exchange representation. The exchange does not need to contain the entire game state, but only the state visible to one player as revealed to it by an omniscient referee. For that purpose, we’ll represent a player’s view of the game state using JSON:
A state is a dictionary with five keys:
"day" mapped to 1, 2, or 3.
"phase" mapped to phase within the day.
"shop" mapped to a dictionary mapping card names (not including "Sorcerer's Stipend") to counts for the number of that card remaining in the shop. All cards available in the game’s initial shop are represented in this dictionary, potentially with a count of 0.
"players" mapped to an array of 2 to 4 players.
"player" mapped to an integer indicating the current player’s position (i.e., the player receiving this state dictionary) in the "players" array, counting from 0.
A phase is a dictionary of one of the following four shapes:
A dictionary with just "name" mapped to "investing", representing a game currently in the investing phase. The current player needs to pick a number of coins to invest.
- A dictionary with "name" mapped to "attacking", representing a game currently in the attacking phase, and with two additional keys:
"attacker" mapped to the attacking player’s position (counting from 0) in the "players" array. If this index matches the one in "player", the current player is the attacker, otherwise the current player is a defender.
"attacker-card" mapped to either false or the position of the attacking card in the attacker player’s "cards" array. This value will be false when the current player is the attacker, which means that the current player needs to pick an eligible attacking card or pass on further attacks. Otherwise, the current player is a defender and needs to pick an eligible defending card against the attacker’s selected attacking card.
A dictionary with just "name" mapped to "buy", representing a game currently in the buy phase. The current player needs to pick a card to buy or pass on buying.
A dictionary with "name" mapped to "end" and with "winner" mapped to a player index or false (for a draw), representing the final game state. The player does not need to move.
A player is a dictionary with three keys:
"coins" mapped to an integer representing the number of coins that the player has.
"buys" mapped to an integer representing the remaining number of buys (to be used in the current day’s buy phase) that the player has.
- "cards" mapped to an array of dictionaries, one for each card acquired by the player and not discarded, and in the order that the cards were acquired. This array will be empty for a player who is out of the game because they were not able to defend against a preceding attack. Each card dictionary has two keys:
"name" mapped to a string for a card name. The first card’s name in the "cards" array is always "Sorcerer's Stipend".
"uses" mapped to a integer indicating the number of times the card has been used as an attacker or defender in the current phase (and always 0 in an investing or buying phase). Typically, 0 means that a card has not been used to attack or defend while 1 means that a card is tapped out, but the "Wall of Wealth" card is not yet tapped out on defense when its "uses" value is 1.
A card name string is one of the following literals: "Sorcerer's Stipend", "Board of Monopoly", "Incantation", "Worker", "Magic Bean Stock", "Bubble", "Ghost", "Senior Worker", or "Gold Fish". Note that this list omits "Wall of Wealth", "Apprentice", "Thug", "Shield of Greed", and "Golem".
When a player program receives a JSON representation of the game state for its move, it must respond with a move as a JSON value appropriate to the game phase. The move is represented as an array containing a single value (where the value is reported an array so that the JSON form is clearly delimited):
For the "investing" phase, the player program must produce an an integer amount to invest, which must be between 0 and the number of coins the player has (inclusive).
For the "attacking" phase, the player program must produce an integer card index to select an attacking of defending card. An attacker can produce 0 to decline attacking further (since 0 otherwise corresponds to the player’s "Sorcerer's Stipend", which cannot attack or defend). Otherwise, the index must correspond to a card that is allowed to attack or defend and that is not already tapped out, and it will be an integer between 1 (inclusive) and the number of cards that the player had (exclusive).
For the "buy" phase, the player must produces a card name or "Pass" to indicate that no card will be bought. The named card’s cost must be no more than the player’s current number of coins. (There is no constraint that the card has a non-0 count in the shop.)
2.3 Game Protocol
An Economancy player program must read game state as JSON from standard input and write the result of a move as JSON to standard output. Your player program should loop with two steps: (1) read a JSON state for the current game state; if the state’s phase is not "end", write a JSON value for your player’s move. When your player receives an "end" state, it should exit.
A referee program that runs the game will check that the JSON move produced by a player program represents a valid turn relative to the given JSON state. When a player has no action or valid move, or when attacking and the only valid move is to stop attacking, then the playing program will not receive a board state or be expected to detect that no move is possible. Instead, the referee will skip the player. For example, if a player has multiple buys and all other players have only a single buy, then only the player with multiple buys will receive multiple board states for the "buy" phase within the relevant day.
Each player program must be runnable as a exectuable (i.e., runnable by the OS’s execve system call) with no command-line arguments. When one player wins, both player-program processes will be forcibly terminated. “Tournament mode” will include a restriction (to be determined) on how long a player program can take to complete its turn.
The relevant JSON encodings are self-delimiting—
Even if your player writes a newline after JSON output, beware the common mistake of forgetting to flush standard output, especially since the default flushing mode is typically different when writing to a terminal versus writing to an operating-system pipe that is connected to a referee program.
2.4 Examples
Here are example traces for games involving (very bad) players that randomly select among valid moves, along with visualizations of the trace.
JSON trace 1 and visualization for 2 players where the second player loses by being unable to defend.
JSON trace 2 and visualization for 2 players that always invest 0 and where the second player wins through points.
JSON trace 3 and visualization for 4 players.
A page in the visualization shows a player’s move with the information
that is visible to that player—
On each page, the current shop is shown on the left, with a number below each card to show how many of that card are available. Players are shown on the right, with the player making a move highlighted by a green box. The number below a yellow circle is the number of coins held by that player; a number on the circle is the amount that the player is deciding to invest, and a blue border around a card (either in the shop or a player’s area) represents a card that a player has decided to buy or use as an attacker or defender. A blue button at the bottom of that player’s box summarize the move that the player has selected.
2.5 Handin Procedure
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 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. You can have multiple player executables in the archive, but choose one to enter into the tournament.