Design and Implementation of TAG: A Tabletop Games Framework
Raluca D. Gaina, Martin Balla, Alexander Dockhorn, Raul Montoliu, Diego Perez-Liebana
DD ESIGN AND I MPLEMENTATION OF
TAG:A T
ABLETOP G AMES F RAMEWORK
Raluca D. Gaina * , Martin Balla * , Alexander Dockhorn * ,Raúl Montoliu † , Diego Perez-Liebana * * School of Electronic Engineering and Computer Science, Queen Mary University of London, UK † Insitute of New Imaging Technologies, Jaume I University, Castellon, SpainVersion 1.0 — September 28, 2020 A BSTRACT
This document describes the design and implementation of the Tabletop Gamesframework (TAG), a Java-based benchmark for developing modern board gamesfor AI research. TAG provides a common skeleton for implementing tabletopgames based on a common API for AI agents, a set of components and classesto easily add new games and an import module for defining data in JSON format.At present, this platform includes the implementation of seven different tabletopgames that can also be used as an example for further developments. Additionally,TAG also incorporates logging functionality that allows the user to perform adetailed analysis of the game, in terms of action space, branching factor, hiddeninformation, and other measures of interest for Game AI research. The objective ofthis document is to serve as a central point where the framework can be describedat length. TAG can be downloaded at: https://github.com/GAIGResearch/TabletopGames K eywords Tabletop Games · General Game AI · TAG · Statistical Forward Planning ontents
A.1 Game State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18A.2 Forward Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20A.3 Actions in Love Letter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 232
ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK
This paper serves the purpose of a living documentation for TAG - a Tabletop Games frameworkfor studying computational intelligence in this type of games. This document will be continuouslyupdated with the new developments of the framework. When citing this paper please make sure tospecify its version since the paper will be updated over time.
TAG was designed to capture most of the complexity that modern tabletop games provide, with afew games implemented already and more in progress. Our framework includes handy definitionsfor various concepts and components common across many tabletop games [7].We define an action as an independent unit of game logic that modifies a given game state towardsa specific effect (e.g. player draws a card; player moves their pawn). These actions are executedby the game players and are subject to certain rules : units of game logic, part of a hierarchicalstructure (a game flow graph). Rules dictate how a given game state is modified and control theflow through the game graph (for instance, checking the end of game conditions and the turn order).This turn order defines which player is due to play at each time, possibly handling player reactionsforced by actions or rules. At a higher level, games can be structured in phases , which are timeframes where specific rules apply and/or different actions are available for the players.All tabletop games use components (game objects sharing certain properties), whose state ismodified by actions and rules during the game. TAG includes several predefined components toease the development of new games, such as tokens (a game piece of a particular type), dice (withN sides), cards (with text, images or numbers), counters (with a numerical value), grid and graphboards. Components can also be grouped into collections: an area groups components in a mapstructure in order to provide access to them using their unique IDs, while a deck is an orderedcollection (list) with specific interactions available (e.g. shuffle, draw, etc.). Both areas and decksare considered components themselves. A summary of the components follows: • Token : A game piece of a particular type, usually with a physical position associated withit. • Die : A die has a number of sides N associated with it and can be rolled to obtain a valuebetween 1 and N (inclusive). • Card : A card usually has text, images or numbers associated with any of its 2 sides, and isthe most common type of component used in decks. • Counter : An abstract concept used to keep track of a particular variable numerical value;usually represented on a board with tokens used to mark the current value, but recognizedas a separate object in this framework. It has a minimum, maximum and current valueassociated with it, where the current value can vary between the minimum (inclusive) andmaximum (inclusive). • Graph board : A graph representation for a board, as a collection of several board nodesconnected between each other. • Board node : A node in a graph board which keeps track of its neighbours (or connections)in the board. • Grid board : A 2D grid representation of a board, with a width and height associated withit. It can hold elements of any type.The structure of TAG consists of several packages: • core : All core framework functionality, including all abstract classes to be extended bygame implementations. • evaluation : Classes for running tournaments and evaluations of games or AI players. • games : Specific implementations of abstract classes for each game, each grouped in itsown package. 3 ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK • gui : Generic Graphical User Interface (GUI) helper classes, including the PrototypeGUI class which can be used with any in-progress game. Each game can extend this to implementcustomized functionality in their GUIs, for better player interaction and thus a better playerexperience. • players : All players (human and AI) available. • utilities : Various utility classes and generic functionality shortcuts.Additionally, the folder data (outside of the source files) stores assets for the games, each in theirown folders. Most of these refer only to images used in the interfaces, but may include JSON filesand other data as well (e.g. Pandemic stores board and card information here as well).The TAG framework brings together all of the concepts and components described previously andallows quick implementation and prototyping of new games. To this end, a flexible API is providedfor all functionality needed to define a game, with multiple abstract classes that can be extended forspecific implementations. The framework already provides some generic functionality: ready-madecomponents, rules, actions, turn orders and game phases. These are all represented through severalgame objects found in the core package, which can be instantiated in game implementations forimmediate use. Additionally, TAG includes a fully functional game loop and a prototyping GUI.The GUI allows users to start interacting with the games as soon as they have the two main classesrequired set up: a
Game State ( GS ) class and a Forward Model ( FM ) class. GS is a container class, including all variables and game components which would allow oneto describe one specific moment in time. It defines access methods in the game state to retrieveexisting game components, make custom and partially observable copies of the state, and definean evaluation function that can be used by the playing agents. We strongly recommend that gamecomponents (i.e. extending the Component class) are used to describe a game state as much aspossible (e.g. using Counters instead of integer variables to keep track of numbers in the game), inorder for general AI players to have an easy common access to understand game states. FM encompasses the logic of the game: performs the game setup (by setting the GS componentsand variables to their initial state and/or reading from files and creating components); defines whatactions players can take in a particular game state; applies the effect of player actions (received oneat a time; if a simultaneous-action game is to be implemented, we recommend waiting to receive allbefore modifying the game state) and any other game rules applicable; uses a turn order to decidewhich player is due to play next, and checks for any end of game conditions (and setting the gamestatus and player results in the game state appropriately if the game is over). The FM is available toAI players for game simulations.For each game, users can further implement specific actions, rules, turn orders, game parameters (foreasy modification of game mechanics), a GUI and provision of game data. The last is useful whenthe game requires large amounts of data such as tile patterns, cards and board node connections, andit is provided via JSON files. A full guide on using the framework and implementing new games isavailable in the wiki provided with the code .TAG’s game-loop is presented in Algorithm 1. Given a list of agents and parameters of the game tobe played, the framework performs an initial setup of the game state ( s ) and the game’s forwardmodel (FM). While the game state is not terminal, the turn order selects the next player to act. Toensure partial observability, we generate an observation object for the current agent which hidesthe state of unobserved components. Hence, a list of available actions is generated and the agentis queried to provide the next action to be executed ( a t ). Finally, the forward model is used tomodify the game state given the action (producing s t +1 ), and the graphical user interface is updatedaccordingly. This section dives deeper into the structure of the framework. TAG can be used to implementnew games, for which it provides a set of abstract classes and interfaces to ease the development.Although there is no hard requirement to implement games in a particular way, the recommendedset of classes to implement is described here. https://github.com/GAIGResearch/TabletopGames/wiki ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK
Algorithm 1
Overview Game Loop
Input: list of agents, game parameters gp Output: win rate statistics s , FM = SETUP G AME ( gp ) while not IS T ERMINAL ( s t ) do agent ← GET C URRENT P LAYER ( s t )observation ← GET O BSERVATION ( s t , agent )actions ← FM.
GET A VAILABLE A CTIONS ( s t , agent ) a t ← agent . GET A CTION (observation, actions) s t +1 ← FM.
NEXT ( s t , a t )GUI. UPDATE () end while In order to implement a new game in TAG, the following core modules can be implemented:
This core class includes all the information about the components that form the state of the game.New game states can extend the class core.AbstractGameState , implementing the followingmethods: • _getAllComponents() : returns a list of all the components of the game state. The methodis called after game setup, so you may assume all components have already been created.Decks and Areas have all of their nested components automatically added. • _copy(int playerId) : defines a reduced, player-specific, copy of your game state. Thisincludes only those components (or parts of the components) which the player with thegiven ID can currently see. For example, some decks may be face down and unobservableto the player. All of the components in the observation should be copies of those in thegame state, as the player is not prohibited from modifying the state it receives (copiesensures the real objects in the game are not affected in any way). • _reset() : resets any variables of the game state to their state before FM initialisation. • _getScore(int playerId) : implements a heuristic function that returns a numerical value forthe current game state, given a specific player - the bigger this value, the better the state.Additionally, game state classes can implement the core.interfaces.IFeatureRepresentation interface, which provides an interface for methods that can be used to extract further genericinformation about the game state, in terms of abstract features. Game state classes can alsoimplement the core.interfaces.IVectorObservation interface, which allows AI players toreceive a vector observation from the game state.Appendix A.1 shows an example of a Game State implementation for the game Love Letter. The forward model class is in charge of setting up the initial game state and advancing the gamestate when provided with an action to be executed by one of the players. In order to implementa forward model, extend the class core.AbstractForwardModel and implement the followingmethods: • _setup(AbstractGameState firstState) : perform the initial game setup according to the gamerules, initialising all components and variables in the given game state (e.g. give each playertheir starting hand, place tokens on the board etc.). • _next(AbstractGameState currentState, AbstractAction action) : apply the given action tothe given game state, execute any other non-action-dependent game rules (e.g. related togame phase changes), check for game end conditions (setting the game status and playerresults in the given state) and move to the next player if required. 5 ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK • _computeAvailableActions(AbstractGameState gameState) : return a list with all actionsavailable for the current player, in the context of the game state object. • _copy() : return a new instance of the Forward Model object with any necessary variablescopied. Use a new random seed (e.g. System.currentTimeMillis() for initialisation ofthe copy object). • endGame() : override this function if the game requires any extra end of game computation(e.g. to print end of game results).Appendix A.2 shows an example of a Forward Model implementation for the game Love Letter. Note:
Forward model classes can instead extend from the abstract class core.rules.AbstractRuleBasedForwardModel , if they wish to use the rule-based sys-tem instead; this class provides basic functionality and documentation for using rules, and analready implemented _next() function.
TAG provides a series of simple actions that are common for many games, such as shuffling decks(or, more generally, shuffling groups of components), drawing components from one deck to another,replacing components, re-arranging decks or placing tokens in grid boards. The games currentlyincluded in the framework use these actions to manipulate game components such as decks andcounters in the game.It is also possible to define game specific actions for new games. For this, a new action classneeds to be created that extends from core.actions.AbstractAction . Two main methods canbe overriden: • execute(AbstractGameState gs) : this method executes the action the class represents,applying its effect to the given game state. Within this method, any component that hasbeen registered with a unique ID can be retrieved from the game state with the function AbstractGameState.getComponentById(int id) . • copy() : this method returns an exact copy of this action. Important:
Actions should not hold any reference to other objects to avoid cross-referencing inthe game and prevent issues with deep copies. Unique component IDs should be passed instead intoaction classes constructors.Examples of game-related actions for Love Letter can be seen in Appendix A.3
Additionally, some other classes should also be implemented or added to, following the framework’sstructure: • Game Type:
TAG allows to categorize its games into different internal collections ofgames of the similar type. This allows the user, for instance, to retrieve games withcertain mechanics or number of players within the framework. The class games.GameType includes several methods that can be added to so that a new game is known and search-ablewithin the framework. In order to incorporate a game to the framework in this way, youshould add the following: – Add a new enum value for your game, defining the minimum and maximum numberof players, a list of categories the game belongs to, and a list of mechanics the gameuses (both categories and mechanics can be already existing ones or newly definedoptions). – In the method stringToGameType(String game) , add a new case for your game sothat the string name can be converted to the correct game type. – In the method createGameInstance(int nPlayers, long seed) , add a new casefor your game, creating the appropriate instances of the game state and forward modelneeded to run, given the number of players taking part in the game and the randomseed. 6
ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK – In the method createGUI(AbstractGameState gameState, ActionControllerac) , add a new case for your game (if GUI is implemented, the PrototypeGUI may beused otherwise), creating the appropriate instance of GUI, given an initial game stateand the action controller managing user interactions within the GUI.
Important: doing this step is necessary for using the main methods of running games inthe framework in the core.Game class and game analysis. • GUI:
The abstract class core.AbstractGUI can be extended to indicate not only how thegame should be displayed in a graphical user interface, but also to specify what bits shouldbe occluded to respect partial observability of the game state for human players. The class gui.PrototypeGUI and the other GUI helper classes in package gui can be used as asimple template to be customized depending on necessary visualisations and interactions. • Game Parameters:
These allow tweaking the game behaviour and rules when these dependon numerical or discrete values. For instance, the number of cards in a hand of Uno or thenumber of outbreaks allowed before losing a game of Pandemic can be specified this way.This provides a centralized point for the parameters of a game that define its behaviour andallow play-testing different versions of the same game easily. In order to implement gameparameters, a parameters class for a specific game must extend core.GameParameters orimplement core.interfaces.ITunableParameters (which allows game parameters tobe randomized or tuned). • Turn Order:
The order in which players make moves in the game can be specified byextending the abstract class core.turnorders.TurnOrder , which provides functionalityto establish the starting and current player and advance the turn to the next player to move.Default turn orders such as alternate (one after another until all players have played) andreactive (which adds the possibility of a player to act out of turn) are included in theframework and can be reused for new games. • Constants:
For efficiency reasons, you may declare constants of a given game for quickaccess through the framework. The class pandemic.PandemicConstants shows an exam-ple of how to specify game-related constants for a game, and how to use the framework’sHash utility to provide direct access to certain game components.The following classes can further be implemented, but they are not required for essential gamefunctionality, and may not be needed at all depending on the game implemented: • Game (Optional):
Extending core.Game , allows to specify the types of parameters, gamestate, GUI and forward model for a specific game. This class can also be used to add theentry point to run the game with some AI agents or humans. • Game Data (Optional):
Some tabletop games need extra data information, which maycome in the form of specific card sections (costs, effects, categories, etc.), tile patterns,etc. This data can be specified in json format files for each component type and read withthe json parser incorporated in the framework. Alternatively, it is possible to add ownformat and parser code. The abstract class core.AbstractGameData can be extended toincorporate data reading for the particular game. • Game Phase (Optional):
Some games can be structured in phases, we different rules andavailable actions apply. The core.AbstractGameState class includes a definition forsome basic default game phases (main, player reaction, end), which could be expanded inorder to provide specific phases for new games.
There are currently 7 games implemented in the framework, varying from very simple test games(
Tic-Tac-Toe ) to strategy games (
Pandemic [6]), as well as diverse challenges for AI players. A fewgames are currently in active development (
Descent [8],
Carcassonne [9] and
Settlers of Catan [10]),and many more are in the project’s backlog, including games from other frameworks to allow foreasy comparison. Further development plans also include adding easy to use functionality forwrapping external games, so that a direct comparison can be carried out under the same conditionswithout the need to re-implement games under our framework’s full restrictions. 7
ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK
All games implemented can be found in the games package, each registered in the games.GameType class; this class allows specifying properties for each game, to allow for automatic listing for experi-ments (e.g. a list of all games with the “cooperative” tag). We highlight next some particularities ofthe games currently implemented in the framework. players alternate placing their symbol in a N × N grid until one player completes a line, columnor diagonal and wins the game; if all cells in the grid get filled up without a winner, the game is adraw. This is the simplest game included in the framework, meant to be used as a quick referencefor the minimum requirements to get a game up and running. Its implementation makes use ofmostly default concepts and components, but it implements a scoring heuristic and a custom GUIfor an easier interaction given the specific game mechanics. to players start the game with one card each, representing a character, a value and a uniqueeffect. A second card is drawn at the start of each turn, one of which must be played afterwards.After the last card of the deck is drawn, the player with the highest valued card wins the currentround. A player wins the game after winning 5 rounds. Love Letter features partial observability,asymmetric and changing player roles and a point system over several rounds. Figure 1 shows anexample game state. Figure 1: Screenshot of Love Letter in TAG. to players control a bandit each, with a unique special ability. Their goal is to collect the mostmoney while traversing the two-level compartments in a train and avoiding the sheriff (a non-playercharacter moved by players and round card events). The game consists of several rounds, each witha planning (players play action cards) and an execution (cards are executed in the same order) phase.During the former, players play action cards, which are executed in the same order in the latterphase. This processing scheme forces players to adapt their strategy according to all the movesalready played, in an interesting case of partial observability and non-determinism: the opponents’type of action may be known (sometimes completely hidden in a round), but not how it will beexecuted. Additionally, the overall strategy should be adapted to a bandit’s unique abilities. Thoseactions can move the player’s character along the compartments, interact with neighbouring players,8 ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK or collect money. The game ends after all rounds are played, the player with the most money beingthe winner. Figure 2 shows an example game state.Figure 2: Screenshot of Colt Express in TAG.
The game consists of coloured cards with actions or numbers. Numbered cards can only be playedin case either the colour or the number matches the newest card on the discard pile. Action cardslet to players draw additional cards, choose the next colour to be played or reverse the turnorder. A player wins after gaining a number of points over several rounds (computed as the sum ofall other players’ card values). Uno features stochasticity, partial observability and a dynamicallychanging turn order. This game has the potential of being the longest game in the framework sinceplayers need to draw new cards in case they cannot play any. Figure 3 shows an example gamestate. Figure 3: Screenshot of Uno in TAG. 9
ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK to players have a body each that consists of four organs, which can be: infected (by an opponentplaying a virus card), vaccinated (by a medicine card), immunised (by 2 medicine cards) or destroyed(by opponents playing 2 consecutive virus cards). The winner is the first player who forms a healthyand complete body. Virus! features stochasticity and partial observability, with the draw pile andopponents’ cards being hidden. to players try to avoid drawing an exploding kitten card while collecting other useful cards.Each card gives a player access to unique actions to modify the game-state, e.g. selecting the playertaking a turn next and shuffling the deck. This game features stochasticity, partial observability anda dynamic turn order with out-of-turn actions: in contrast to previous games, Exploding Kittens keeps an action stack so that players have the chance to react to cards played by others using a
Nope card. A
Nope card cancels the most recent effect, but can itself be cancelled by another
Nope card.The turn order and action stack are implemented by extending the base-classes of the framework.Figure 4 shows an example game state.Figure 4: Screenshot of Exploding Kittens in TAG.
Pandemic is a cooperative board game for to players. The board represents a world map, withmajor cities connected by a graph. Four diseases break out and the objective of the players is tocure them all. Diseases keep spreading after each player’s turn, sometimes leading to outbreaks.Each player is assigned a unique role with special abilities and is given cards that can be used fortravelling between cities, building research stations or curing diseases. Additionally, they haveaccess to special event cards, which can be played anytime (also out-of-turn). All players loseif they run out of cards in the draw deck, if too many outbreaks occur or if the diseases spreadtoo much. Pandemic features partial observability with face-down decks of cards and asymmetricplayer roles. It employs a reaction system to handle event cards and is the only game currentlyusing the graph-based rule system.In each turn, the player can play up to consecutive actions, with a changing action space (e.g.moving to a new city may result in new actions). To handle event cards and actions that requireconsent or reactions from other players, a reaction system is used, so that a player can be asked toreturn an action whenever needed. Pandemic also features partial observability, as the draw deckand the infection deck are not visible. Figure 5 shows an example game state. 10 ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK
Figure 5: Screenshot of Pandemic in TAG.
All games in the framework can be analysed to illustrate the challenge they provide for AI players.Here, we present measurements currently implemented and taken from the existing games describedin Section 4. We measure the following averages , observed in our experiments: µ Action space size : the number of actions available for a player on their turn. µ Branching factor : the number of distinct game states reached through player actionsfrom a given state. µ State size : the number of components in a state. µ Amount of hidden information : the percentage of components hidden from playerson their turn. µ Game speed : the execution speed of 4 key functions (in number of calls per second):setup, next, available action computation and state copy. µ Game length : measured as the number of decisions taken by AI players, the totalnumber of game loop iterations (or ticks), the number of rounds in the game and the numberof actions per turn (APT) for a player. µ Reward sparsity : granularity of the heuristic functions provided by the game, measuredby minimum, maximum and standard deviation of rewards observed by the players.Table 1: Analysis of games, played 1000 times for each possible number of players on each game,using random agents: action space size, branching factor, state size, hidden information and gamespeed. µ µ µ µ µ Setup Next Actions CopyTic-Tac-Toe 5.69 6.79 1 0% Love Letter 4.74 10.78 24.00 62.96% Uno 1.88 4.32 116.00 92.3% Virus! 9.70 11.03 78.00 89.1% Exploding Kittens 3.17 4.99 60.00 84.5% Colt Express 2.91 5.46 87.60 87.67% Pandemic 11.15 17.97 138.00 64.97% Results are presented in Tables 1 and 2 (
Virus! games were limited to 100 rounds, as random playcan lead to infinite games). The first thing to note is that all games are very fast to execute: mostgames can execute over 1 million calls per second to the (usually) most expensive functions ( next ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK
Table 2: Analysis of games, played 1000 times for each possible number of players on each game,using random agents: game length and reward sparsity µ µ copy ). The games vary in length, with only . ticks for the simplest game, Tic-Tac-Toe , but . for Uno . We further see variations in the state size, with
Pandemic showing most complex,while
Uno includes the most hidden information.
Love Letter shows its strategic complexitythrough the higher branching factor, while
Exploding Kittens boasts one of the largest spread ofrewards. More complete information and graphical visualisations can be obtained by running the evaluation.GameReport class included with the framework.Many of the metrics reported do not paint a complete picture if only their average is given: actionspaces, for example, vary widely during play for most of the games presented. An example whichhighlights this is
Uno , where the action space is dependent on the number of cards in a player’shand, increasing on average as the game goes on (see Figure 6). It is further interesting to note herethat games get shorter with more players (as players would earn more points per round if they havemore opponents, and thus more cards to total the sum of) - an example of insights which can beobtained through analysis of the games themselves, readily available for any newly implementedgames in the framework.Figure 6: Action space size in
Uno with all player number versions; 1000 runs per version playedby random players.
All implemented players follow a simple interface, only requiring one method to be implemented: getAction . This receives a game state object reduced to the specific player’s observation of the cur-rent state of the game. How this reduced game state is built is game-dependent, usually randomisingunknown information. This method expects an action to be returned out of those available and iscalled whenever it is the player’s turn and they have more than 1 action available (i.e. the playeractually has a decision to make). If no decision is required, the agent can choose to still receiveand process the information on the game state (in the registerUpdatedObservation function)12
ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK but an action is not requested. They may also choose to implement the initializePlayer and finalizePlayer functions which are called at the beginning and end of the game, respectively.Each player has a player ID assigned by the game engine, and they receive the forward modelof the game currently being played. The FM can then be used to advance game states givenactions, compute actions available, or reset a game to its initial state. The rest of this sec-tion defines the sample players implemented in the framework. These agents use the game’sscore to evaluate game states (as implemented on the game side and accessible through the observation.getScore(this.getPlayerID()) method call), but their heuristic functions maybe swapped with a different object implementing the
IStateHeuristic interface.The rest of this section describes the different agents implemented for TAG. Performance of the AIagents described here can be found at [11]
Two types of human interaction are available, both of which interrupt the game loop to wait forhuman actions on their turn.
Console allows human play using the console. It outputs the gamestate and available actions in the console and the player inputs the index of the action they chooseto play.
GUI allows human play with a Graphical User Interface, which is game-specific. It uses an
ActionController object to register player action requests, which are then executed in the game.
The simplest automatic player chooses random actions out of those available on its turn. Itsimplementation is as follows: public class RandomPlayer extends AbstractPlayer { private final Random rnd; public RandomPlayer () { this(new Random ()); } @Override public AbstractAction getAction ( AbstractGameState observation ) { int randomAction = rnd. nextInt ( observation . getActions ().size ()); List < AbstractAction > actions = observation . getActions (); return actions .get( randomAction ); } } A greedy exhaustive search algorithm, it evaluates all actions from a given game state and picksthat which leads to the highest valued state.
RHEA [12] evolves a sequence of L = 10 actions over several generations, choosing the firstaction of the best sequence found to play in the game. The algorithm is randomly initialised with asequence of actions. At each generation it creates a mutation of the current best solution, keepingthe best solution of the two. This process repeats until the given budget is exhausted.Given the variable action spaces and that actions available are highly dependent on the current gamestate, the mutation operator chooses a gene in the individual (i.e. position in the action sequence)and changes all actions from that point until the end of the individual to new random valid actions.The game’s forward model is therefore used in both mutation (to advance the game state giventhe last action, in order to find the available actions for the given position in the sequence) andevaluation (game states reached through the sequence of actions are evaluated using the game’sheuristic, added up for a discounted total with discount factor γ = 0 . , and this total becomesthe fitness of the individual). It is important to note that RHEA evolves only its own actions and13 ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK opponents are given a random model (with intermediate states after opponent actions ignored infitness evaluations).
MCTS [13] incrementally builds an asymmetric game tree balanced towards to most promisingparts of the game state space. It uses multiple iterations of four steps: first, it navigates throughthe tree, using a tree policy , until reaching a node which is not yet fully expanded; next, it adds anew random child of this node to the tree; it then performs a Monte Carlo rollout from the newchild (randomly sampling actions until the end of the game or a predetermined depth L = 10 ); thestate reached at the end of the rollout is evaluated with a heuristic function, and this score is backedup through all the nodes visited during the iteration. The process is repeated until the budget isexhausted, and the most visited child of the root is chosen as the action to play.The version implemented in the framework is closed-loop: it stores game states in each of the nodes.Further, the rollout step was removed after initial experiments showing an increased performancewithout it; therefore, the forward model of the game is only used when expanding a leaf node. Theresulting node is immediately evaluated using the heuristic and its value is backed up through thetree. This section describes how to run the games/AI in the framework and the different modes available.
The main method in the core.Game class allows for running any game registered in the framework(i.e. those available in the games.GameType enum).
1. Choose on/off visuals:
Set the ActionController ac variable to null if running without visuals,or leave as-is to enable visuals (if the game has a GUI implemented).
2. Setting the random seed for the game
Set the seed variable to the specific seed you wish torun the game with (or leave to
System.currentTimeMillis() for a new random value): long seed = System.currentTimeMillis();
3. Choose players for the game
Add to the players array instances of the agents you wish to runthe game with.
HumanConsolePlayer can be used to play as a human interacting with the gamevia the console.
HumanGUIPlayer can be used to play as a human interacting with the game via theGUI (if implemented). ArrayList < AbstractPlayer > players = new ArrayList <>(); players .add(new RandomPlayer (new Random ())); players .add(new RandomPlayer (new Random ())); players .add(new RandomPlayer (new Random ())); players .add(new OSLA ());
4. Run!
Choose final parameters for the method call: private static Game runOne ( GameType gameToPlay , List < AbstractPlayer > players , long seed ,ActionController ac , boolean randomizeParameters ); • gameToPlay : the type of the game to play • players : previously defined players array • seed : previously defined seed variable • ac : previously defined ac variable • randomizeParameters : true if game parameters should be randomized (if implementedfor the game), false otherwise. 14 ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK
Adjust settings for the following variables in class core.CoreConstants if needed: public final static boolean VERBOSE = true; public final static boolean PARTIAL_OBSERVABLE = false; public final static boolean DISQUALIFY_PLAYER_ON_ILLEGAL_ACTION_PLAYED = false; Finally, run the core.Game class.
To run multiple games, choose one of the following methods, depending on whether you wish torun with a fixed random seed for all games (1, passing desired seed to the method), with a newrandom seed for each run (1, passing null to the seed parameter in the method) or with fixed randomseed for each repetition of each game (2):
1. private static void runMany (List
2. private static void runMany (List
Use the chosen function and parameters in the core.Game.main function as before, and run the
Game class.
Note:
Games within a specific category can be obtained by using the corresponding
Category method, e.g.
GameType.Category.Strategy.getAllGames() would return a list of all games inthe “Strategy" category. Games using a specific mechanic can be obtained by using the correspond-ing
Mechanic method, e.g.
GameType.Mechanic.Cooperative.getAllGames() would return alist of all cooperative games.
Tournaments can be run using classes available in the evaluation package. At the time of writinga round-robin tournament is available, which requires the same setup as running single or multiplegames in the core.Game class, but will pit all the specified players against all others and run severalinstances of the given games.
The presented framework opens up several directions of research and proposes a variety of chal-lenges for AI players, be it search/planning or learning algorithms. Its main focus is to promoteresearch into General Game AI that is able to play many tabletop games at, or surpassing, humanlevel. Relatedly, the agents should be able to handle both competitive (most common testbeds inliterature), cooperative and even mixed games. For instance, a future planned development is theinclusion of the game
Betrayal at House on the Hill [14], in which the players start off playingcooperatively to later split into teams mid-way through the game, from which point on they arecompeting instead with newly given team win conditions and rules. Most tabletop games includesome degree of hidden information (e.g. face-down decks of cards) and many more players com-pared to traditional video-game AI testbeds, introducing higher levels of uncertainty. However, suchgames often make use of similar mechanics, even if in different forms: thus knowledge transfer would be a fruitful area to explore, so that AI players can pick up new game rules more easilybased on previous experiences, similar to how humans approach the problem. Some tabletop gamesfurther feature changing rules (e.g.
Fluxx [15]) which would require highly adaptive AI players,able to handle changes in the game engine itself, not only the game state. Many others rely on largeamounts of content and components, for which the process of creating new content or modifyingthe current one for balance, improved synergies etc. could be improved with the help of ProceduralContent Generation methods (e.g. cards for the game
Magic the Gathering [16] were previouslygenerated in a mixed-initiative method by [17]).Specific types of games can also be targeted by research, an option highlighted by TAG’s categori-sation and labelling of games and their mechanics. Thus AI players could learn to specialise ingames using certain mechanics or in areas not yet explored, such as
Role-Playing or Campaign games (i.e. games played over several linked and progressive sessions). These games often feature15
ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK asymmetric player roles , with a special one highlighted (the dungeon master) whose aim is tocontrol the enemies in the game in order to not necessarily win, but give the rest of the players thebest experience possible and the right level of challenge. Strategy AI research could see importantapplications in this domain, as many tabletop games include elements of strategic planning. Role-playing games focused more on the story created by players (e.g.
Dungeons and Dragons ) ratherthan combat mechanics (e.g.
Gloomhaven ) would also be a very engaging and difficult to approachtopic for AI players, where Natural Language Processing research could take an important role.The framework enables research into parameter optimisation : all parameter classes for games, AIplayers or heuristics can implement the
ITunableParameters interface; parameters can then beautomatically randomised, or more intelligently tuned by any optimisation algorithm. This allowsfor quick and easy exploration of various instances of a problem, a potential increase in AI playerperformance, or adaptation of AI player behaviour to user preference.We have mentioned previously that the games implemented offer reduced observations of the gamestate to the AI players, based on what they can currently observe. These hidden information states(usually) do not keep a history of what was previously revealed to a player. Instead, the AI playersshould learn to memorise relevant information and build belief systems , as humans would in areal-world context - a very interesting direction of research encouraged by TAG.Lastly, the framework includes the possibility for games to define their states in terms of either vectorobservations ( IVectorObservation ), which enables learning algorithms to be easily integratedwith the framework; or feature-based observations ( IFeatureRepresentation ), which allowsfor more complex algorithms which can perform a search in the feature space of a game, rather thanthe usual game state space approached.
Acknowledgements
This work was partly funded by the EPSRC CDT in Intelligent Games and Game Intelligence(IGGI) EP/L015846/1 and EPSRC research grant EP/T008962/1.
References [1] Seiji Kanai.
Love Letter . Alderac Entertainment Group, 2012.[2] Christophe Raimbault.
Colt Express . Ludonaute, 2014.[3] Merle Robbins.
Uno . AMIGO, 1971.[4] Domingo Cabrero et al.
Virus!
El Dragón Azul, 2015.[5] Matthew Inman et al.
Exploding Kittens . Ad Magic, Inc., 2015.[6] Matt Leacock.
Pandemic . Z-Man Games, Inc., 2008.[7] G. Engelstein and I. Shalev.
Building Blocks of Tabletop Game Design: An Encyclopedia ofMechanisms . CRC Press LLC, 2019.[8] Fantasy Flight Publishing, Inc.
Descent: Journeys in the Dark 2nd Edition . Diamond ComicDistributors, 2012.[9] Klaus-Jürgen Wrede.
Carcassonne . Hans im Glück, 2000.[10] Klaus Teuber.
The Settlers of Catan . Mayfair Games, 1995.[11] Raluca D. Gaina, Martin Balla, Alexander Dockhorn, Raul Montoliu, and Diego Perez-Liebana.TAG: A Tabletop Games Framework. In
Proceedings of the AIIDE workshop on ExperimentalAI in Games , 2020.[12] Diego Perez-Liebana, Spyridon Samothrakis, Simon M. Lucas, and Philipp Rolfshagen.Rolling Horizon Evolution versus Tree Search for Navigation in Single-Player Real-TimeGames. In
Proceedings of the Genetic and Evolutionary Computation Conference (GECCO) ,pages 351–358, 2013.[13] Cameron B Browne, Edward Powley, Daniel Whitehouse, Simon M Lucas, Peter I Cowling,Philipp Rohlfshagen, Stephen Tavener, Diego Perez, Spyridon Samothrakis, and Simon Colton.A Survey of Monte Carlo Tree Search Methods.
IEEE Transactions on ComputationalIntelligence and AI in games , 4(1):1–43, 2012. 16
ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK [14] Bruce Glassco et al.
Betrayal at House on the Hill . Avalon Hill Games, Inc., 2004.[15] Andrew Looney and Kristin Looney.
Fluxx . Looney Labs, 1997.[16] Richard Garfield.
Magic: The Gathering . Wizards of the Coast, 1993.[17] Adam James Summerville and Michael Mateas. Mystical Tutor: A Magic: The Gather-ing Design Assistant Via Denoising Sequence-To-Sequence Learning. In
Twelfth artificialintelligence and interactive digital entertainment conference , 2016. 17
ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK
Appendices
A A Game Example: Love Letter
This section shows some important code snippets from the implementation of
Love Letter in TAG.
A.1 Game State: LoveLetterGameState.java
Love Letter adds one game phase on top of default phases: public enum LoveLetterGamePhase implements IGamePhase { Draw } The game state class defines several different components: // List of cards in player hands List < PartialObservableDeck < LoveLetterCard >> playerHandCards ; // Discarded cards List
TAG: A T
ABLETOP G AMES F RAMEWORK llgs. drawPile .add(llgs. playerHandCards .get(i)); llgs. playerHandCards .get(i).clear (); } } for (int i = 0; i < llgs. reserveCards . getSize (); i++) { if (! llgs. reserveCards . isComponentVisible (i, playerId )) { // Hide! llgs. drawPile .add(llgs. reserveCards .get(i)); } } Random r = new Random (llgs. getGameParameters (). getRandomSeed ()); llgs. drawPile . shuffle (r); for (int i = 0; i < getNPlayers (); i++) { if (i != playerId && llgs. playerHandCards .get(i). getDeckVisibility ()[ playerId ]) { // New random cards for (int j = 0; j < playerHandCards .get(i). getSize (); j++) { llgs. playerHandCards .get(i).add(llgs. drawPile .draw ()); } } } for (int i = 0; i < llgs. reserveCards . getSize (); i++) { if (! llgs. reserveCards . isComponentVisible (i, playerId )) { // New random card llgs. reserveCards . setComponent (i, llgs. drawPile .draw ()); } } } return llgs; } The function reset() sets the data structures of the game state to a functional but empty state: @Override protected void _reset () { gamePhase = Draw; playerHandCards = new ArrayList <>(); playerDiscardCards = new ArrayList <>(); drawPile = null; reserveCards = null; effectProtection = new boolean [ getNPlayers ()]; } and the getScore() function uses a helper method to determine the heuristic value of this state: @Override protected double _getScore (int playerId ) { return new LoveLetterHeuristic (). evaluateState (this , playerId ); } This heuristic method is defined as follows in games.loveletter.LoveLetterHeuristic.java : public double evaluateState ( AbstractGameState gs , int playerId ) { LoveLetterGameState llgs = ( LoveLetterGameState ) gs; LoveLetterParameters llp = ( LoveLetterParameters ) gs. getGameParameters (); Utils. GameResult playerResult = gs. getPlayerResults ()[ playerId ]; if ( playerResult == Utils. GameResult .LOSE) return -1; if ( playerResult == Utils. GameResult .WIN) return 1; double cardValues = 0; Random r = new Random (llgs. getGameParameters (). getRandomSeed ()); for ( LoveLetterCard card: llgs. getPlayerHandCards ().get( playerId ). getComponents ()) { if (card. cardType == LoveLetterCard . CardType . Countess ) { if (r. nextDouble () > COUNTESS_PLAY_THRESHOLD ) { cardValues += LoveLetterCard . CardType . Countess . getValue (); } } else { cardValues += card. cardType . getValue (); } } double maxCardValue = 1+ llgs. getPlayerHandCards ().get( playerId ). getSize () * LoveLetterCard. CardType . getMaxCardValue (); double nRequiredTokens = (llgs. getNPlayers () -1 < llp. nTokensWin . length ? llp. nTokensWin [llgs. getNPlayers () -1] : llp. nTokensWin [llp. nTokensWin .length -1]); ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK if ( nRequiredTokens < llgs. affectionTokens [ playerId ]) nRequiredTokens = llgs.affectionTokens [ playerId ]; return FACTOR_CARDS * ( cardValues / maxCardValue ) + FACTOR_AFFECTION * (llgs. affectionTokens[ playerId ]/ nRequiredTokens ); } A.2 Forward Model: LoveLetterForwardModel.java
The first game state is initialised in the _setup() method: @Override protected void _setup ( AbstractGameState firstState ) { LoveLetterGameState llgs = ( LoveLetterGameState ) firstState ; // Set up all variables llgs. drawPile = new PartialObservableDeck <>(" drawPile ", llgs. getNPlayers ()); llgs. reserveCards = new PartialObservableDeck <>(" reserveCards ", llgs. getNPlayers ()); llgs. affectionTokens = new int[llgs. getNPlayers ()]; llgs. playerHandCards = new ArrayList <>( llgs. getNPlayers ()); llgs. playerDiscardCards = new ArrayList <>( llgs. getNPlayers ()); // Set up first round setupRound (llgs , null); } This setup counts on the auxiliary method setupRound(). This method is separate as it is calledwhenever a new round starts in the game, and it sets up a round for the game, including the drawpile, reserve pile and starting player hands: private void setupRound ( LoveLetterGameState llgs , HashSet
TAG: A T
ABLETOP G AMES F RAMEWORK // add random cards to the player ’s hand PartialObservableDeck < LoveLetterCard > playerCards = new PartialObservableDeck <>("playerHand " + i, i, visible ); for (int j = 0; j < llp. nCardsPerPlayer ; j++) { playerCards .add(llgs. drawPile .draw ()); } llgs. playerHandCards .add( playerCards ); // create a player ’s discard pile , which is visible to all players Deck < LoveLetterCard > discardCards = new Deck <>(" discardPlayer " + i, i); llgs. playerDiscardCards .add( discardCards ); } // Game starts with drawing cards llgs. setGamePhase (Draw); if ( previousWinners != null) { // Random winner starts next round int nextPlayer = r. nextInt ( previousWinners .size ()); int n = -1; for (int i: previousWinners ) { n++; if (n == nextPlayer ) { llgs. getTurnOrder (). setTurnOwner (i); } } } // Update components in the game state llgs. updateComponents (); } The method that rolls the state forward with a given action, next() , is implemented as follows: @Override protected void _next( AbstractGameState gameState , AbstractAction action ) { // each turn begins with the player drawing a card after which one card will be played // switch the phase after each executed action LoveLetterGameState llgs = ( LoveLetterGameState ) gameState ; action . execute ( gameState ); IGamePhase gamePhase = llgs. getGamePhase (); if ( gamePhase == Draw) { llgs. setGamePhase ( AbstractGameState . DefaultGamePhase .Main); } else if ( gamePhase == AbstractGameState . DefaultGamePhase .Main) { llgs. setGamePhase (Draw); llgs. getTurnOrder (). endPlayerTurn ( gameState ); checkEndOfRound (llgs); } else { throw new IllegalArgumentException ("The game phase " + llgs. getGamePhase () + " is not know by LoveLetterForwardModel "); } } Another important method in
Love Letter ’s forward model is the one in charge of computing theavailable actions for a player. The snippet for that method is shown below: @Override protected List < AbstractAction > _computeAvailableActions ( AbstractGameState gameState ) { LoveLetterGameState llgs = ( LoveLetterGameState ) gameState ; ArrayList < AbstractAction > actions ; int player = gameState . getTurnOrder (). getCurrentPlayer ( gameState ); if ( gameState . getGamePhase (). equals ( AbstractGameState . DefaultGamePhase .Main)) { actions = playerActions (llgs , player ); } else if ( gameState . getGamePhase (). equals ( LoveLetterGameState . LoveLetterGamePhase .Draw)){ // In draw phase , the players can only draw cards. actions = new ArrayList <>(); actions .add(new DrawCard (llgs. drawPile . getComponentID (), llgs. playerHandCards .get(player ). getComponentID (), 0)); } else { throw new IllegalArgumentException ( gameState . getGamePhase (). toString () + " is unknownto LoveLetterGameState "); } return actions ; } ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK In Love Letter , the actions available depend on the card the player holds. Here’s an extract of themethod playerActions() that _computeAvailableActions() uses: switch ( playerDeck . getComponents ().get(card). cardType ) { case Priest : for (int targetPlayer = 0; targetPlayer < llgs. getNPlayers (); targetPlayer ++) { if ( targetPlayer == playerID || llgs. getPlayerResults ()[ targetPlayer ] == Utils. GameResult .LOSE) continue ; actions .add(new PriestAction ( playerDeck . getComponentID (), playerDiscardPile . getComponentID (), card , targetPlayer )); } break; case Guard: for (int targetPlayer = 0; targetPlayer < llgs. getNPlayers (); targetPlayer ++) { if ( targetPlayer == playerID || llgs. getPlayerResults ()[ targetPlayer ] == Utils. GameResult .LOSE) continue ; for ( LoveLetterCard . CardType type : LoveLetterCard . CardType . values ()) actions .add(new GuardAction ( playerDeck . getComponentID (), playerDiscardPile . getComponentID (), card , targetPlayer , type)); } break; case Baron: for (int targetPlayer = 0; targetPlayer < llgs. getNPlayers (); targetPlayer ++) { if ( targetPlayer == playerID || llgs. getPlayerResults ()[ targetPlayer ] == Utils. GameResult .LOSE) continue ; actions .add(new BaronAction ( playerDeck . getComponentID (), playerDiscardPile . getComponentID (), card , targetPlayer )); } break; case Handmaid : actions .add(new HandmaidAction ( playerDeck . getComponentID (), playerDiscardPile . getComponentID (), card)); break; case Prince : for (int targetPlayer = 0; targetPlayer < llgs. getNPlayers (); targetPlayer ++) { if ( targetPlayer == playerID || llgs. getPlayerResults ()[ targetPlayer ] == Utils. GameResult .LOSE) continue ; actions .add(new PrinceAction ( playerDeck . getComponentID (), playerDiscardPile . getComponentID (), card , targetPlayer )); } break; case King: for (int targetPlayer = 0; targetPlayer < llgs. getNPlayers (); targetPlayer ++) { if ( targetPlayer == playerID || llgs. getPlayerResults ()[ targetPlayer ] == Utils. GameResult .LOSE) continue ; actions .add(new KingAction ( playerDeck . getComponentID (), playerDiscardPile . getComponentID (), card , targetPlayer )); } break; case Countess : actions .add(new CountessAction ( playerDeck . getComponentID (), playerDiscardPile . getComponentID (), card)); break; case Princess : actions .add(new PrincessAction ( playerDeck . getComponentID (), playerDiscardPile . getComponentID (), card)); break; ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK
A.3 Actions in Love Letter
Here are some actions that are exclusive to the game
Love Letter . Actions can extend other actionsprovided within TAG, enhancing their functionality. For instance, the following DrawCard actionmodifies the execution of a regular drawing card move to set certain values in the game state. public class DrawCard extends core. actions . DrawCard implements IPrintable { public DrawCard (int deckFrom , int deckTo , int fromIndex ) { super(deckFrom , deckTo , fromIndex ); } @Override public boolean execute ( AbstractGameState gs) { (( LoveLetterGameState )gs). setProtection (gs. getTurnOrder (). getCurrentPlayer (gs), false); return super. execute (gs); } @Override public String getString ( AbstractGameState gameState ) { return "Draw a card and remove protection status ."; } @Override public String toString () { return "Draw a card and remove protection status ."; } @Override public void printToConsole ( AbstractGameState gameState ) { System .out. println ( toString ()); } @Override public AbstractAction copy () { return new DrawCard (deckFrom , deckTo , fromIndex ); } } This
DrawCard action is further extended to provide particular effects for each action in the game.For instance, in
Love Letter , the execute() function for playing a Baron card is implemented asfollows: @Override public boolean execute ( AbstractGameState gs) { LoveLetterGameState llgs = ( LoveLetterGameState )gs; int playerID = gs. getTurnOrder (). getCurrentPlayer (gs); Deck < LoveLetterCard > playerDeck = llgs. getPlayerHandCards ().get( playerID ); Deck < LoveLetterCard > opponentDeck = llgs. getPlayerHandCards ().get( opponentID ); // compares the value of the player ’s hand card with another player ’s hand card // the player with the lesser valued card will be removed from the game if (llgs. isNotProtected ( opponentID ) && gs. getPlayerResults ()[ playerID ] != Utils. GameResult.LOSE){ LoveLetterCard opponentCard = opponentDeck .peek (); LoveLetterCard playerCard = playerDeck .peek (); if ( opponentCard != null && playerCard != null) { if ( opponentCard . cardType . getValue () < playerCard . cardType . getValue ()) llgs. killPlayer ( opponentID ); else if ( playerCard . cardType . getValue () < opponentCard . cardType . getValue ()) llgs. killPlayer ( playerID ); } else { throw new IllegalArgumentException (" player with ID " + opponentID + " was targetedusing a Baron card" + " but one of the players has now cards left."); } } return super. execute (gs); } ESIGN AND I MPLEMENTATION OF
TAG: A T
ABLETOP G AMES F RAMEWORK
Other actions are simpler, such as playing a Guard card: @Override public boolean execute ( AbstractGameState gs) { LoveLetterGameState llgs = ( LoveLetterGameState )gs; Deck < LoveLetterCard > opponentDeck = llgs. getPlayerHandCards ().get( opponentID ); // guess the opponent ’s card and remove the opponent from play if the guess was correct if (llgs. isNotProtected ( opponentID )){ LoveLetterCard card = opponentDeck .peek (); if (card. cardType == this. cardType ) { llgs. killPlayer ( opponentID ); } } return super. execute (gs); } or a Priest card: @Override public boolean execute ( AbstractGameState gs) { LoveLetterGameState llgs = ( LoveLetterGameState )gs; int playerID = gs. getTurnOrder (). getCurrentPlayer (gs); PartialObservableDeck < LoveLetterCard > opponentDeck = llgs. getPlayerHandCards ().get(opponentID ); // Set all cards to be visible by the current player if ((( LoveLetterGameState ) gs). isNotProtected ( opponentID )){ for (int i = 0; i < opponentDeck . getComponents ().size (); i++) opponentDeck . setVisibilityOfComponent (i, playerID , true); } return super. execute (gs); }}