Tag: swing

What is Cripple Mr. Onion?

Cripple Mr. Onion was originally a gambling card game played by the characters in the fictional universe of the Discworld novels written by Terry Pratchett. Since no official rules have been set by the author himself, many Roundworld (our world) denizens have taken it upon themselves to create a set of rules for the game. One popular version was designed by Andrew C. Millard with help by Prof. Terry Tao and was even mentioned in the book Turtle Recall, which is a kind of Discworld encyclopedia written by Terry Pratchett himself. The book gives a vague description of the rules with the understanding that people playing the game are free to modify or ignore the rules as set. What follows is the unique interpretation I envisioned and I hope you will all enjoy playing it as much as I enjoyed creating it.

CMO Screen CaptureMany versions of the game envisioned it with eight suits of cards much like combining two sets of regular playing cards but with the suits all different. The book Turtle Recall describes the deck as a regular set of playing cards combined with the complete Caroc deck (Discworld tarot deck). In my version there are a total of 78 playing cards divided up into six suits of thirteen cards each. The first four suits, turtles, elephants, staves and octograms are exactly like a regular set of 52 playing cards but with their suits renamed. The remaining two suits follow the same numbering scheme as the remaining four but have unique names for each card. The suits themselves are called the Lesser Arcana and the Greater Arcana and together they form the Caroc deck.

The game itself bears striking resemblance to both Poker and Blackjack. Like Poker there are various hand ranks that become exceedingly rare the higher their rank goes. Cards have number values just like in Blackjack and the rank of one’s hand is determined by how well you group the cards in your hand to sum to 21. However, what truly separates Cripple Mr. Onion from its Roundworld counterparts are the various optional rules that can be included. These are otherwise known as the “Modifiers.” A player may choose to use as many or as few modifiers as they wish with the exception of the Crippling Rule which is always active (hence the name Cripple Mr. Onion).

The user interface is built using Java’s swing library. The main application runs on the swing library’s Event Dispatch Thread (EDT). Meanwhile, the main game loop runs on the Engine Core thread and determines which animations need to be played and what the board should look like at various stages of the game. Whenever the core thread needs to start an animation, request input from the user or change the appearance of the board it calls a static method of the Application class to get an event object which it forwards to the EDT. This allows the core thread to make thread safe changes to the GUI.

The Application object loads all the necessary game resources and initializes the various swing components, some of which include a menu bar and glass pane as well as the content panes for various dialogs. The Application provides static methods for starting these dialogs with the appropriate modality and parent window. The menu bar provides the user with a list of game options and help tools which call these methods. The Table object represents the game view and acts as the content pane for the main window. It provides various public methods for changing the appearance of the board and for enabling and disabling certain components. The Animation Pane is the glass pane of the main window and is where all the animations are drawn.

Rendering animations using Swing components can be tricky since drawing only occurs when a paint event is dispatched by the EDT, a method known as passive rendering. The Animation objects in this game use threads that repeatedly update before calling the repaint method on the component to which they are attached. Then whenever a paint event is dispatched the given component’s paint method merely calls on the paint method of its attached Animation object.

// Animation.java

import java.awt.Component;
import java.awt.Graphics;
import java.util.ArrayList;

/**
 *
 * @author GK Potts
 */
public abstract class Animation implements Runnable {
    private final int SLEEP_DELAY = 25;

    protected ArrayList<AnimationListener> listeners;
    private Component context;
    private Thread currentThread;
    private long startTime;
    private long oldTime;
    private long currentTime;
    private boolean playing;
    private boolean running;

    public Animation() {
        listeners = new ArrayList<>();
        playing = false;
        running = false;
    }

    public abstract void paint(Graphics g);

    public abstract void update(long elapsed);

    ...

    @Override
    public void run() {
        long elapsed, sleep;
        running = true;
        currentThread = Thread.currentThread();
        startTime = System.currentTimeMillis();
        oldTime = startTime;
        for (AnimationListener listener : listeners) listener.onStart();
        while (running) {
            currentTime = System.currentTimeMillis();
            elapsed = currentTime - oldTime;
            if (playing) {
                update(elapsed);
                context.repaint();
            }

            oldTime = currentTime;
            sleep = SLEEP_DELAY - elapsed;
            if (sleep < 0) sleep = 2;

            try { Thread.sleep(sleep); }
            catch (InterruptedException e) { return; }
        }
    }
}

When an animation event occurs, an Animation object is constructed using position data collected from the Table. The Animation is then fitted with its own personal listener object whose methods are called whenever the animation is started, paused, stopped or reaches its end.   The only one of these methods that is truly important is the OnComplete method because it notifies the core thread when the animation is finished.   The Animation object is then attached to the Animation Pane and finally started on a new thread. Note that the animation thread does not need to be invoked on the EDT since it does not make any direct changes to the GUI. It merely calls the repaint method of the component to which it is attached. Repaint merely schedules a paint event to be executed later, thus making our Animations thread safe. If we ran our animation thread on the EDT our GUI would freeze until the animation thread ended.

In addition to having Animations for moving objects, this game also supports sprite based animations. Each time a card flips over or a modifier effect is activated, a sprite based Animation Clip object determines which frame to use based on the total elapsed time. This Animation Clip object is based almost entirely on the sprite based animation class that I wrote for use in the game engine I built for my previous game Captain Polly’s Booty. To see my blog posting on sprite animation click here.

The various Animation subclasses each utilize an Animation Data object which stores the position, rotation, and image data for each of the cards and provides helper methods for stepping forward the position, rotation and animation. Then using Hash Maps the animation objects map the Card objects to the Animation Data objects allowing the animation to simply cycle through the list of cards to update their positions and paint them. This way the order of the list determines what order to draw the cards in. Additional these data objects also store a rotated version of the image so that the program does not need to call the image rotation function, which is rather time consuming, within the paint method.

To prevent the clipping of images as a result of rotation each image is resized to a square whose sides are equal to the diameter of the circle that completely encloses the image. The images are then rotated about their center and position data is altered to take into account the change in size.

Player hands are evaluated through the use of two Objects: the Grouping, and the Hand Evaluator. Grouping merely looks at a set of cards and determines if it forms a valid card grouping and stores its overall ranking. Hand Evaluator looks at a player’s entire hand and attempts to create the best possible Grouping from the cards available. Hand Evaluator is also useful for evaluating possible hands by being able to generate modified copies of itself. These copies can then be compared to the original to see which one is a better hand.