Game writing tutorial

The java file

The public server API is completely documented with javadoc comments so you can always use it as a reference.

All games are directly or indirectly derived from the Game class and all functionality of the rules are contained within. So if you need some helper classes you will need to define them as members of your game class. From the game class you can access the protected members uiElements (this is a HashMap of all the GUI elements) and players (this is an ArrayList of all the players). Empty places at the table are represented by a null object in the players list.

The game is implemented as a finite state machine. The base class for the states is Game.State.

Every implementation of a game needs to have two static functions: engineVersion and getName. The engineVersion function sets what the minimal version of the server needs to be, to be compatible with the game. The current engine version is 2. The getName function needs to return the human readable name of the game.

public class War extends Game {

    public static int engineVersion() { return 1; }

    public static String getName() { return "War"; }
}

Your game implementation needs to have a default constructor, but the constructor of the Game class takes two arguments: the minimum and maximum amount of players.

    public War() {
        super(2, 2);
    }

Finally your class needs to override the doCreateGame function. It has no arguments and returns the initial State. In this function you typically get all user interface elements from the uiElements HashMap and cast them to the correct class. To get an ui element you need to create a UIID, this holds the name of the ui element and the player index if it is defined in a player section or UIID.noOwner if it is defined in the common or invisible sections. At the server side the player who created the game always has index 0 and the player at the left has index 1 and so on.

You also need to create the cards in this function. You can only create cards by calling the appropriate functions of a Stack object, typically the main stack.

    protected State doCreateGame() {
        UIID id = new UIID("mainStack", UIID.noOwner);
        mainStack = (Stack)uiElements.get(id);
        id = new UIID("playerStack", 0);
        playerStacks.add((Stack)uiElements.get(id));
        id = new UIID("playerStack", 1);
        playerStacks.add((Stack)uiElements.get(id));
        id = new UIID("trickStack", 0);
        trickStacks.add((Stack)uiElements.get(id));
        id = new UIID("trickStack", 1);
        trickStacks.add((Stack)uiElements.get(id));
        id = new UIID("anteStack", 0);
        anteStacks.add((Stack)uiElements.get(id));
        id = new UIID("anteStack", 1);
        anteStacks.add((Stack)uiElements.get(id));

        mainStack.createStack();
        return new StartGameState();
    }

There are two other functions in the Game class that you can override: doJoinGame and doLeaveGame. These are called when a player joins or leaves the game respectively. They both have the player as the first argument and the current state as the second argument and return a State object.

Typically you use doJoinGame to set the initial score of the player when playing for money. With doLeaveGame you can do cleanup (moving the player's cards back to the main stack). If the game can't continue because there aren't enough players you have to call stopGame().

    protected State doJoinGame(Player player, State state) { ... }

    protected State doLeaveGame(Player player, State state) { ... }

The rules of the game are implemented by Game.State objects. You can override 6 functions of the Game.State class, but you only need to override the ones that you actually use. These functions are listed below.

    private class GameState extends Game.State {

        public State doStartGame() { ... }

        public State doProcessMoveCardsAction(MoveCardsAction action) { ... }

        public State doProcessButtonAction(ButtonAction action) { ... }

        public State doProcessNumberInputAction(NumberInputAction action) { ... }

        public State doProcessSelectCardsAction(SelectCardsAction action) { ... }

        public State doProcessMultipleChoiceAction(MultipleChoiceAction action) { ... }
   }

When enough players have joined the game or a previous game has ended, doStartGame is called. In this function you typically deal all the cards. This is done with one of the deal functions of the Stack class. These functions all take an ArrayList<CardTarget> as argument. You can create CardTargets with one of the appropriate functions of one of the different types of CardCollections. The cards that are dealt will be moved to these collections.

Furthermore you need to send one or more actions to one of the players with the queueAction function of Player. When that player has executed one of these actions the appropriate doProcess... function of the state will be called. All other actions for that player will be cleared automatically. Like with all functions of Game.State you can return a new state object at the end of the funcion.

        public State doStartGame() throws CardhouseException {
            mainStack.shuffle();
            ArrayList<CardTarget> targets = new ArrayList<CardTarget>();
            targets.add(playerStacks.get(0).topTarget());
            targets.add(playerStacks.get(1).topTarget());
            mainStack.deal(targets, War.this);

            CardSource source = playerStacks.get(0).topSource();
            CardTarget target = trickStacks.get(0).topTarget();
            Action action = new MoveCardsAction(source, target, this);
            players.get(0).queueAction(action);

            return new PlayFirstCardState();
        }

As you can see in the above example a MoveCardsAction is used to allow the player to move a card, this action takes a CardSource and a CardTarget as arguments. When moveCards is called for the action the cards that are defined by the source will be moved to the target. If you want to move cards withouth any player interaction you also have to use a MoveCardsAction. Note that a MoveCardsAction that is sent to a player must have a CardSource that only moves one card. If you want to allow the player to move multiple cards at once you need to use a SelectCardsAction.

For the CardList and the SortedCardList you can also use a SelectCardsAction. The constructor of this action has 4 parameters: the AbstractCardList from which to select, an ArrayList with the indices of the cards that can be selected, the minimum and maximum of cards that can be selected. You can send multiple SelectCardActions from the same card list to a player at the same time. To process the action after the player has executed it you can use the getSelectedCards() function.

    cards = new ArrayList<Integer>();
    ...
    Action action = new SelectCardsAction(hand, cards, 2, cards.size());

protected State doProcessSelectCardsAction(SelectCardsAction action) {
    ArrayList<Integer> indices = action.getSelectedCards();
    ...
}

To enable a button you use a ButtonAction. Its constructor takes a button (UIElement) and an optional String that is used as the label of the button. To process the action you can use the getButton() function.

Action action = new ButtonAction(checkCall, "Check");

The NumberInputAction's constructor again has the UIElement as first parameter and a minimum and maximum amount as second and third parameter. You can optionally pass a String for the label as fourth parameter. To process the action you can use the getNumberInput() and getValue() functions.

Action action = new NumberInputAction(betRaise, 2, maxAmount, "Bet");

Finally you have the MultipleChoiceAction. This shows the user a dialog with a list of options from which he must select one. The constructor takes a String for the question text and an ArrayList<String> with all the choices. To process the action you can use the getChoice() function to get the index or the getChoiceText() function to get the choice text.

To get the next or previous player that is playing you can call the nextPlayer and previousPlayer functions of the Game object. These functions automatically skip empty seats and players that aren't playing (anymore). If a player has finished the game before the others call setPlaying(false) for that player.

currentPlayer = nextPlayer(currentPlayer);

To insert a pause in the animation (for example between two MoveCardsActions) you can use the function sendAnimationDelay of the Game object. This function takes a long specifying the number of milliseconds as argument. The last frame of the previous animation is shown for that amount of time. If you want a delay after an animation is shown, call sendAnimationDelay twice. First with a 1 millisecond delay and then with the desired delay.

sendAnimationDelay(1);
sendAnimationDelay(3000);

When a game is finished you need to call the reportPlayersWon function of the Game object. This function takes an array of Player objects as argument to indicate the winning players. A new game will start automatically when all the players are ready.

reportPlayersWon(new Player[]{ players.get(winner) });
previous - next