JCardGame
 
 


In the following tutorial we introduce to the fundamental ideas of the JCardGame framework. The paradigm of "Learning by Examples" is applied where each little example is fully functional and has a special didactical intension. The examples are not yet real games to play with, but serves as demonstration of a specific feature of JCardGame. All sources and sprite images are included in the JCardGame distribution.

 

Ex01: Hand Layouts

In many card decks a single card is part of a suit (sometimes called "the color") and has a rank. JCardGame only supports card decks that can be arranged in suits and ranks, but the number of suits and ranks and their denomination is user definable. In the first example we take a bridge (or poker) card set with 4 suits and 9 ranks. The enumeration values are used to access the card images in the fixed subdirectory "spites" using the given enumeration values in the order the ranks are defined. E.g. for the suit CLUBS:

Card Filename
CLUBS-ACE sprites/clubs0.gif
CLUBS-KING sprites/clubs1.gif
CLUBS-QUEEN sprites/clubs2.gif
CLUBS-JACK sprites/clubs3.gif
CLUBS-TEN sprites/clubs4.gif
CLUBS-NINE sprites/clubs5.gif
CLUBS-EIGHT sprites/clubs6.gif
CLUBS-SEVEN sprites/clubs7.gif
CLUBS-SIX sprites/clubs8.gif

(Another more flexible naming scheme can be used by declaring a Sprites enumeration.)

The Suit and Rank enumerations are not only used to access the sprite files, but also to define a priority order of suits and ranks that is applied when cards are compared and sorted. Moreover the toString() methods of cards and hands use the string representation of these enumerations to return the card names literally.

Despite it is possible to get individual cards from the deck, the deck delivers also randomly shuffled or ordered card sets by the method dealingOut(int nbPlayers, int nbCardsPerPlayer), where nbPlayers is the number of players and nbCardsPerPlayer the number of cards each player will get. But dealingOut() returns one hand more than nbPlayers where you find the remaining cards of the card deck (often used as talon).

In JCardGame the logic of a hand is strictly separeted from the view as seen in the game grid window. The view is widely user adaptable by using the layout classes StackLayout, RowLayout, ColumnLayout and FanLayout. These layouts model closely how card sets are arranged in real games. Moreover the presentation of the hand may be animated (opening card arrangements). You inform the hand how to display itself by calling setView() and passing a CardGame reference that determines the display window. Finally, whenever you want to show the hand, invoke draw() without any parameters.

When the application is started, a CardGame instance is created. CardGame (derived from GameGrid) is a specialized version of GameGrid with restricted constructor parameters. When the CardGame instance is constructed, the underlying GameGrid is created and the window is shown with given width and height. Then the simulation cycling with a period of 30 ms starts to run. The first example shows you a variety of hand layouts.

import ch.aplu.jcardgame.*;
import ch.aplu.jgamegrid.*;

public class Ex01 extends CardGame
{
  public enum Suit
  {
    SPADES, HEARTS, DIAMONDS, CLUBS
  }

  public enum Rank
  {
    ACE, KING, QUEEN, JACK, TEN, NINE, EIGHT, SEVEN, SIX
  }
  private Deck deck =
    new Deck(Suit.values(), Rank.values()"cover");

  public Ex01()
  {
    super(600600);

    Hand[] hands = deck.dealingOut(45);

    RowLayout rowLayout0 = new RowLayout(new Location(150520)300);
    hands[0].setView(this, rowLayout0);
    hands[0].draw();

    RowLayout rowLayout1 = new RowLayout(new Location(150370)300);
    rowLayout1.setStepDelay(10);
    hands[1].setView(this, rowLayout1);
    hands[1].draw();

    ColumnLayout columnLayout0 = new ColumnLayout(new Location(370390)400);
    hands[2].setView(this, columnLayout0);
    hands[2].draw();

    ColumnLayout columnLayout1 = new ColumnLayout(new Location(470390)400);
    columnLayout1.setScaleFactor(0.7);
    columnLayout1.setStepDelay(10);
    hands[3].setView(this, columnLayout1);
    hands[3].draw();

    FanLayout fanLayout = new FanLayout(new Location(300700)600250290);
    hands[4].setView(this, fanLayout);
    hands[4].draw();
  }

  public static void main(String[] args)
  {
    new Ex01();
  }
}

Execute the program locally using WebStart.

 

GGLogo
Download Android app for installation on a smartphone or emulator.
Download
sources (CardEx01.zip).
Create QR code to download Android app to your smartphone.
Install/Start
app on a USB connected smartphone or a running emulator.

(This is a WebStart signed by the University of Berne, Switzerland. It installs some helper files in <userhome>.jdroidtools.If you did not install the Android SDK, you may install a slim version of the Android-Emulator in <userhome>.jdroidemul using this link, To start the emulator, execute ExecEmul.jar found in <userhome>.jdroidemul)

 

jcardgameex01

 

Ex02: Mouse Events, Transferring Cards

The user interaction in card games consists normally by clicking on a card visible in the game window. JCardGame fully supports object based mouse events. When several sprite images are superposed you may get a event for every object under the mouse cursor or, if you want, only for the topmost object. For every card a GGMouseTouchListener is defined that reports left and right mouse press, release, click or double-click automatically when the visual part of card is touched. This is a sophisticated feature because basically the operating system just reports the mouse coordinates of a mouse event. The registered callback methods are defined in the CardListener interface. All of them report the card reference of the affected card.

public interface CardListener
{
  public void leftPressed(Card card);
  public void leftReleased(Card card);
  public void leftClicked(Card card);
  public void leftDoubleClicked(Card card);
  public void rightPressed(Card card);
  public void rightReleased(Card card);
  public void rightClicked(Card card);
  public void rightDoubleClicked(Card card);
  public void atTarget(Card card, Location targetLocation);
}

As seen in the next example, card touch events are enabled by calling setTouchEnabled(true). For RowLayout, ColumnLayout and FanLayout the card is automatically pulled on top of other cards when you left click on a visible part of the card (unless disabled by putOnTopEnabled(false)). In the next example we use a CardAdapter and override just one of the callback methods. JGameGrid mouse handling system provides the possibility to distinct between clicks and double-clicks. To enable this feature, a double-click delay must be defined that determines the time (in milliseconds) the system must wait after a click to check if a second click follows. As soon as the second click arrives, the double-click is reported, if there is no second click within the delay time, a click is reported.

In the following example a click brings the card to the front while a double-click transfers it to from the hand to the stock and the hand is rearraged.


import ch.aplu.jcardgame.*;
import ch.aplu.jgamegrid.*;

public class Ex02 extends CardGame
{
  public enum Suit
  {
    SPADES, HEARTS, DIAMONDS, CLUBS
  }

  public enum Rank
  {
    ACE, KING, QUEEN, JACK, TEN, NINE, EIGHT, SEVEN, SIX
  }

  private Deck deck =
    new Deck(Suit.values(), Rank.values()"cover");

  public Ex02()
  {
    super(600600);

    Hand[] hands = deck.dealingOut(19);
    final Hand stock = hands[1];
    stock.setView(thisnew StackLayout(new Location(300150)));
    stock.draw();

    hands[0].setView(thisnew RowLayout(new Location(300400)500));
    hands[0].sort(Hand.SortType.SUITPRIORITY, false);
    hands[0].draw();
    hands[0].addCardListener(new CardAdapter()
    {
      public void leftDoubleClicked(Card card)
      {
        card.transfer(stock, true);
      }
    });
    hands[0].setTouchEnabled(true);
    setDoubleClickDelay(300);
  }

  public static void main(String[] args)
  {
    new Ex02();
  }
}

Execute the program locally using WebStart.

 

GGLogo
Download Android app for installation on a smartphone or emulator.
Download
sources (CardEx02.zip).
Create QR code to download Android app to your smartphone.
Install/Start
app on a USB connected smartphone or a running emulator.

(This is a WebStart signed by the University of Berne, Switzerland. It installs some helper files in <userhome>.jdroidtools.If you did not install the Android SDK, you may install a slim version of the Android-Emulator in <userhome>.jdroidemul using this link, To start the emulator, execute ExecEmul.jar found in <userhome>.jdroidemul)

 

Ex03: Dragging Cards

In the next example we use a standard MouseTouchListener to drag a single card manually from a card talon (in stack layout) to a hand (in row layout). When the card is near it's destination, it "snaps" into the hand. In order to avoid orphan cards, the card jumps into the hand, when the mouse button is released.

First we deal out into the hands array with one player and only two cards for the player. The remaining cards are available in hands[1] that we call talon. Then we select the layouts and draw the hand and the talon. In takeFromTalon() the talon's top card (last in the card list) is reclaimed and removed from the talon, then added again to the game grid as a single (handless) card. A mouse touch listener is registered to reports left press, drag and release events by its mouseTouched() callback. When a lPress event occurs, the mouse cursor position relative to the card is stored in the variable hotspot that is used when the card is dragged to set the card location accordingly (setLocation() sets the center of the sprite image). When the location is within a circle of 50 pixels around the hand location, insertInHand() is called. The card (and its mouse touch listener) is then removed from the game grid and inserted in the hand. Then the hand is sorted by rank priority and redisplayed.

import ch.aplu.jcardgame.*;
import ch.aplu.jgamegrid.*;
import java.awt.*;

public class Ex03 extends CardGame implements GGMouseTouchListener
{
  public enum Suit
  {
    SPADES, HEARTS, DIAMONDS, CLUBS
  }

  public enum Rank
  {
    ACE, KING, QUEEN, JACK, TEN
  }
  //
  private Deck deck = new Deck(Suit.values(), Rank.values()"cover");
  private Point hotspot = new Point(00);
  private Hand talon;
  private Hand hand;
  private final Location talonLocation = new Location(250300);
  private final Location handLocation = new Location(250100);

  public Ex03()
  {
    super(50040030);

    Hand[] hands = deck.dealingOut(12);  // only two cards in hand
    hand = hands[0];
    hand.setView(thisnew RowLayout(handLocation, 400));
    talon = hands[1];
    talon.setView(thisnew StackLayout(talonLocation));
    hand.draw();
    talon.draw();
    takeFromTalon();
    setStatusText("Drag cards from card talon to the hand");
  }

  private void insertInHand(Actor actor)
  {
    actor.removeSelf();
    Card card = ((CardActor)actor).getCard();
    hand.insert(card, false);
    hand.sort(Hand.SortType.RANKPRIORITY, true);
    takeFromTalon();
  }

  private void takeFromTalon()
  {
    Card top = talon.getLast();
    if (top == null)  // talon empty
      return;
    talon.remove(top, false);  // Remove from talon and game grid
    addActor(top.getCardActor())// Reinsert into game grid
  }

  private void addActor(CardActor cardActor)
  {
    addActor(cardActor, new Location(talonLocation));
    cardActor.addMouseTouchListener(this,
      GGMouse.lPress | GGMouse.lDrag | GGMouse.lRelease, true);
  }

  public void mouseTouched(Actor actor, GGMouse mouse, Point spot)
  {
    switch (mouse.getEvent())
    {
      case GGMouse.lPress:
        hotspot = spot;
        break;

      case GGMouse.lDrag:
        actor.setLocation(
          new Location(mouse.getX() - hotspot.x, mouse.getY() - hotspot.y));
        if (actor.getLocation().getDistanceTo(handLocation) < 50)
          insertInHand(actor);
        break;

      case GGMouse.lRelease:
        insertInHand(actor);
        break;
    }
  }

  public static void main(String[] args)
  {
    new Ex03();
  }
}

Execute the program locally using WebStart.

 

GGLogo
Download Android app for installation on a smartphone or emulator.
Download
sources (CardEx03.zip).
Create QR code to download Android app to your smartphone.
Install/Start
app on a USB connected smartphone or a running emulator.

(This is a WebStart signed by the University of Berne, Switzerland. It installs some helper files in <userhome>.jdroidtools.If you did not install the Android SDK, you may install a slim version of the Android-Emulator in <userhome>.jdroidemul using this link, To start the emulator, execute ExecEmul.jar found in <userhome>.jdroidemul)

 

ex03