Solitaire

Navigation

Skip navigation.

Site search

Site navigation

Details and download

Solitaire and card scripts

These scripts produce a game of solitaire (Klondike). The game is fully configurable with several features making it playable on a large range of browsers and devices.

The CardGameCore script can be used as a base for many more games. This page demonstrates its use as a part of the solitaire script.

To download the script(s), see the script license, and check details like browser compatibility, use the links on the navigation panel at the top of this page.

Demonstration

If your browser is capable of running this script, a game of solitaire should appear here. It will also choose different sets of cards if you resize the browser window:

Playing the game

The game follows the basic rules of Klondike solitaire. To move the cards from one stack to another, you can simply drag and drop them onto the destination stack. Alternatively, you can use click-to-move. Click the card you want to move, then click where you want to move it to. You can also double click on a card to move it to a foundation stack. This makes it possible to use the game on devices that do not have a normal mouse. If your browser offers spatial navigation (such as Opera), you can use that to select the right card, but note that due to the number of positioned elements in close proximity to each other, the spatial navigation may have some trouble selecting the right cards.

Features

Browser support

This script is fairly heavily based on DOM, and should work in all browsers with a reasonable level of DOM support.

Known issues

Some browsers may have some little problems:

And some DOM browsers may fail completely because their DOM support is not good enough:

Creating a game

Creating a basic game

This script also requires my CardGameCore script, which must be included before the solitaire script. To use this, take a copy of the CardGameCore script and the solitaire script, then insert the following into the head of your document:

<script src="PATH TO SCRIPT/cardgamecore.js" type="text/javascript"></script>
<script src="PATH TO SCRIPT/solitaire.js" type="text/javascript"></script>

You will then need to create an element for the script to put the game inside. The element must be capable of holding div elements, so a div or td element should be fine (note that you may get odd resizing effects when using the game preferences in IE, if you put it in a td).

<div id="putgamehere"></div>

You then need to tell it to create the game. This must be done after the other scripts have been included, and after the holding element has been created. The easiest way to do this is include it inside a load event listener, but you should also be able to create the game immediately after creating the holding element.

In its simplest form, creating the game consists of just three commands.

window.onload = function () {
  if( !document.getElementById ) { return; } // To be nice to old browsers
  var game1 = new solitaireGame('solitaire1',document.getElementById('putgamehere'));
  game1.addImagePack('mainset',[['set1','Back Set 1']],'.png',25,36,'Main set');
  game1.startGame();
}

You can create your own image packs, or use the existing ones.

Creating image packs

When you provide multiple image packs, the game will select the best fitting image pack to fit the space available. The way it selects the image pack is based on the width of the cards, and the available width that the game can use. The game will need 8 times as much as the width of one card. When selecting an appropriate card size, the game will pick the largest available card size that is a maximum of 1/8 the width of the available space. If no card small enough is available, it will pick the smallest available card. It will then resize the holding element to 4.1 times the height of the selected card (the maximum space that can be used by the card stacks).

You can provide several image packs for the same width of card (the heights do not need to be the same). By default, it will use the first available card of the appropriate width. Any additional image packs you provide for that size will be available through the options. Each image pack may have multiple card back images. By default, it will use the first card back in the list. Any additional back images you provide for that image pack will be available through the options. If a different image pack or card back is chosen, that choice will be remembered using a 1 year cookie, and will be applied for every solitaire game on the site that uses the same card size, and the same unique name.

The format of the addImagePack method

The addImagePack method accepts 6 parameters:

  1. imageset
  2. backimages
  3. extension
  4. width
  5. height
  6. name

The imageset parameter should be a string that will be used to build up the filenames of the images. For ease of use, this is best done using a directory name, such as 'imageset1/'.

The backimages parameter should be an array of all the available card back images. Each image should be given as an array containing two strings, the backimage, and a name. The backimage string will be used to build up the filename, and the name will be used in the solitaire options as the name of the image.

The extension parameter should be a string that will be used on the end of each image file name. Typically, this would be one of '.png', '.gif', or '.jpg'.

The height and width parameters should be numbers, matching the height and width of the card images, in pixels. The images will be displayed at the size you specify, even if that is not the natural size of the images.

The name parameter should be a string that will be used in the solitaire options as the name of the image set.

For example, this will add an image pack with one image back:

game1.addImagePack('mainset',[['set1','Back Set 1']],'.png',25,36,'Main set');

And this will add an image pack with three image backs:

game1.addImagePack('mainset',[['set1','Back Set 1'],['set2','Back Set 2'],['set3','Back Set 3']],'.png',25,36,'Main set');

Naming the images

The game will expect you to use a specific naming format for the cards. Each card image must be named according to the imageset parameter + its suit + its number + the extension parameter. Using the examples above, the jack of clubs should be called 'mainsetclubs11.png'. The default suit names are 'clubs', 'diamonds', 'hearts', and 'spades'.

Card back images must be named according to the imageset parameter + 'back' + the backimage string + the extension parameter. In the second of the above examples, the card back images should be called 'mainsetbackset1.png', 'mainsetbackset2.png', and 'mainsetbackset3.png'.

The 'deal' and 'options' button images must be named according to the imageset parameter + 'deal' or 'options' + the extension parameter. In the above examples, the 'deal' image should be called 'mainsetdeal.png', and the 'options' image should be called 'mainsetoptions.png'.

Sizing the images

A sample of a card design that does not show the card suit well enough.

As previously stated, the cards should be a maximum of 1/8 of the available width. For a normal shape, the height of the cards should generally be one and a half (3/2) times their width. When cards are stacked, it is important that the suit and number are still visible. When cards are stacked on top of each other on a tableau, only the top 1/10 of each card will be visible, so you should ensure that the cards can be recognised by that part of them. When dealing three cards at a time onto the waste pile, 1/4 of their left side will be visible, so they should also be recognisable by that part of them.

The screenshot to the right shows the problems of using a card that cannot be identified by the top 10% of its image. Although the cards on the waste pile can be easily identified (as shown by the green ellipse), the cards on the tableau cannot (as shown by the fuchsia ellipse). The number is relatively easy to disinguish, but the suit is not visible at all.

The deal and options images are smaller than the card images, but they should still be included as part of the image pack. The width of the images should be 50% of the card width, and the height should be 45% of the card height (this allows them to fit in neatly with the layout). If you choose an image size that cannot be divided so perfectly into a whole number, the images will be rounded up to the nearest pixel. For example, if your cards are 51 pixels wide and 73 pixels high, the 'deal' and 'options' images should be 26 pixels wide, and 33 pixels high.

Existing image packs

There are currently 3 pre-made image packs available, used by the demo on this page.

These can be downloaded as a zip package. You can then unzip the package into the same directory as the page containing the game (it will create its own subdirectory), and initialise the game like this:

window.onload = function () {
  if( !document.getElementById ) { return; } // To be nice to old browsers
  var game1 = new solitaireGame('solitaire1',document.getElementById('putgamehere'));
  game1.addImagePack('cardsets/h36/',[['walk','Mountain walk'],['midnightsun','Midnight sun']],'.png',25,36,'Northern Norway');
  game1.addImagePack('cardsets/h96/',[['bluepattern','Blue pattern'],['blue','Plain blue'],['x','Red X']],'.gif',71,96,'Once in a Lifetime');
  game1.addImagePack('cardsets/h135/',[['orcaspyhop1','Orca spy-hop 1'],['orcaspyhop2','Orca spy-hop 2'],['orcajump','Orca jump'],['dolphin','Dolphin'],['puffin','Puffin'],['puffingrass','Puffin lookout'],['puffingroup','Puffin group'],['lizard','Lizard'],['turtle','Turtle'],['cloud','Shroud of cloud'],['redwine','Red wine'],['horse','Horseback'],['mouldings','Mouldings']],'.png',90,135,'Orca');
  game1.startGame();
}

Styling the play area

Each important element created by the game is given a class attribute to facilitate styling. Most of the game relies on images to create its effects, so you should restrict your styling to only the most basic things such as backgrounds and text colours. If you wish to decorate the play area with borders, you should apply these to the holding element, and not any of the elements created by the game. The script is also capable of working on handheld devices, so you may want to include a handheld media section of your stylesheet, to tell device browsers that they should not reformat the page.

The basic structure created by the game (put inside the holding element) is:

The solitaire preferences dialog is a little more complicated than the rest of the game, since it requires a proper HTML structure. I recommend applying a background colour to the 'div.solitaireprefs' and 'div.solitaireprefs form', since these need to cover the playing area when the dialog is showing.

Cheats

What? Yes, this game has a cheat built in. It may seem a little overblown for a game this simple, but in real games, you sometimes lose, and you just want to complete the game anyway. You know you lost, but hey, if you want to cheat, why should I stop you? The cheat is fairly simple but it will always allow you to win if you want. Simply press the Alt key when dragging cards onto a tableau, and you will be allowed to put them there, no matter what card is already there.

If you are using this script, you may want to disable the cheat ability. That can be done very simply by calling game1.setCheatMode(false); at any time (you can enable cheats again by calling the method with the parameter true).

Animations

You may want to include animations when you win a game. You could attach to the ongamewon event yourself, and write your own animations, but to make life easy, I have produced a separate script file for doing a selection of animations. It utilises the events model exposed by the solitaire script. Simply include the SolitaireAnimations script in the same way as you include the other scripts before you create your game, and then tell the game to use the required animation, using the ongamewon event.

window.onload = function () {
  if( !document.getElementById ) { return; } // To be nice to old browsers
  var game1 = new solitaireGame('solitaire1',document.getElementById('putgamehere'));
  game1.addImagePack('mainset',[['set1','Back Set 1']],'.png',25,36,'Main set');
  game1.ongamewon = solitaireAnimations.moveCircles;
  game1.startGame();
}

To use the animation script, you should tell it what animation to use before you start the game, as shown above. The script provides a basic set of animations (I may extend it in future to include some more). The available animations are:

solitaireAnimations.moveCircles;

Moves the cards in a circular motion and replaces them on the foundations, number by number.

solitaireAnimations.fadeShrinkMove(fade,shrink,move);

Allows you to combine different animations, by returning a custom configured event handler, according to the parameters you provide it. The three animation types provided are fading, shrinking, and moving, and you can select any combination of these by passing the correct parameters to the fadeShrinkMove method.

The first parameter is a boolean true/false, and says if you want the fade effect. The second parameter is a boolean true/false, and says if you want the shrink effect. The third parameter is an integer 0/1/2 and says if you want to move down (1), left (2), or not at all (0). This gives a total of 11 animation combinations. An example of this - using fade, shrink, move down - would be:

window.onload = function () {
  if( !document.getElementById ) { return; } // To be nice to old browsers
  var game1 = new solitaireGame('solitaire1',document.getElementById('putgamehere'));
  game1.addImagePack('mainset',[['set1','Back Set 1']],'.png',25,36,'Main set');
  game1.ongamewon = solitaireAnimations.fadeShrinkMove(true,true,1);
  game1.startGame();
}
solitaireAnimations.randomAnimation;

Randomly chooses any of the other animations or animation combinations that are provided by default (the demonstration on this page uses this setting).

Note that while an animation is playing, it also attaches to the ondeal event, to ensure the animation clears up after itself correctly. If you are also listening for this event, it will remove your listener temporarily, and replace it once it is finished.

To make it easy to test animations, the game also offers an instant-win feature. Simply change game1.startGame(); to game1.startGame(true); and the game will win itself without you needing to play. You can then watch the results of your animations.

In general, any animations you make should do three things; play an animation when the ongamewon event occurs, temporarily listen for the ondeal event so it can clear up after itself, and listen for click events on the workspace, so that it can reset itself if they want to stop the animation early. When a new game starts, it will take care of basic things like card size, position, and opacity (as long as you modify all the various opacity declarations in the same way as I do in the solitaire.js file). Anything else you change you will have to clear up for yourself. A sample structure is:

solitaireAnimations.myAnimation = function (e) {
  var theGame = e.target;
  var theInterval = setInterval(function () {
    // ... do some animation here ...
    if( some terminating condition ) {
      theGame.ondeal();
      if( confirm(theGame.strings.youWin) ) {
        theGame.startGame();
      }
    }
  },75);
  var oldDeal = theGame.ondeal, oldClick = theGame.workspace.onclick;
  theGame.ondeal = function () {
    // Stops the animation, and removes itself
    clearInterval( theInterval );
    // ... do any cleanup here ...
    theGame.ondeal = oldDeal;
    theGame.workspace.onclick = oldClick;
    return true;
  };
  theGame.workspace.onclick = function () {
    // Stops the animation, cleans up, and removes itself
    theGame.ondeal();
    for( var i = 0; i < 52; i++ ) {
      // Reset the cards (assuming you animated them)
      theGame.cards.playingCards[i].autoPositionSolitaire();
    }
    if( confirm(theGame.strings.youWin) ) {
      theGame.startGame();
    }
  };
  return false;
};

Some of the useful properties you can access for making animations are:

e.target
The solitaireGame object
e.target.workspace
The workspace that the game creates - it is positioned relatively so it can be a container for positioned elements - animations should generally try to remain within this area
e.target.cardStacks[index]
The card stack objects (index is from 0 to 12, and stacks 2 to 5 are the foundation stacks)
e.target.cardStacks[index].leftPos/topPos
The physical position of the card stack within the workspace
e.target.cardStacks[index].cardsInStack[index]
The cards that are in the given stack - at the time that a game is won, all cards will be on the foundation stacks, and should be indexed from 0 to 13 - for more details of the card properties, see:
e.target.cards.playingCards[index]
e.target.cardStacks[index].representation
The div element that represents the stack visually - it also handles all the events that the stack detects (do not mess with the events ;)
e.target.usingCards
The image pack being used
e.target.usingCards.width/height
The card size being used - the game will use an area within the workspace, 8 times the width of a card, and 4.1 times the height of a card
e.target.cards.playingCards[index]
The cards (index is from 0 to 51)
e.target.cards.playingCards[index].suit/number/color
The information about the card
e.target.cards.playingCards[index].representation
The div element that represents the card visually - it also handles all the events that the card detects (do not mess with the events ;)
e.target.cards.playingCards[index].cardImage
The image element that displays the card face or back
e.target.cards.playingCards[index].showFace(true/false)
Shows the face or back of a card
e.target.cards.playingCards[index].redrawCardImage()
Resets a card so it shows the correct image
e.target.cards.playingCards[index].autoPositionSolitaire()
Puts a card back where it would normally be at this point in the game (on the foundation stack) - resets the position and opacity
e.target.cards.playingCards[index].setCardSize(width,height)
Sets the size of the card (scales the current image to the given size) - should be set using CSS length units - to reset a card to its natural size, use this:
e.target.cards.playingCards[index].setCardSize(e.target.usingCards.width+'px',e.target.usingCards.height+'px')

Note: see inside the cardgamecore.js file for more details of what properties and methods you can access.

Translating the game

All strings used by the game can be translated into other languages. This is done when creating the solitaireGame object. The constructor accepts many additional parameters, that can be used to specify strings to be used by the game. If you leave any of these blank (even if you make them an empty string), it will use the default value instead.

The first two parameters are the unique name, and the holding element reference. After those, the parameters accepted are:

UseDefault value
Wording shown on the deck stack when all cards in the deck have been dealt onto the waste pile'Deck'
Wording shown on the waste pile when no cards have been dealt onto it'Waste pile'
Wording shown on the foundation stacks when no cards have been put there'Foundation'
Wording shown on the tableau stacks when no cards are there'Tableau'
Wording shown on the back of cards when images are disabled'Card'
Array of card names in the order 'Ace' to 'King' - displayed on cards when images are disabled['Ace','2','3','4','5','6','7','8','9','10','Jack','Queen','King']
Array of suit names in the order 'spades', 'hearts', 'clubs', 'diamonds' - displayed on cards when images are disabled, and used to construct the card image file names['spades','hearts','clubs','diamonds']
Message displayed when they win, asking if they want to deal again'Congratulations, you won! Deal again?'
Wording shown on the 'deal' button when images are disabled'Deal'
Wording shown on the 'options' button when images are disabled'Options'
Wording on the button that saves preferences in the options dialog'Done'
Wording on the cancel button in the options dialog'Cancel'
Main heading on the preferences dialog'Options'
Heading for choosing images in the preferences dialog'Images for size '
Heading for the dealing option in the preferences dialog'Dealing'
Label for the card set option in the preferences dialog'Card set:'
Label for the image back option in the preferences dialog'Image back:'
Label for the 'deal 1' option in the preferences dialog'Deal 1'
Label for the 'deal 3' option in the preferences dialog'Deal 3'

Debugging

The script tries to be helpful in letting you know when you have made a mistake with your configuration. To facilitate this, it has inbuilt error handling, with several custom error types that it will throw if you make mistakes. As well as throwing an error, it also alerts a message, telling you what was wrong, and what it expected you to do.

While developing a game, I recommend you allow it to alert these messages, as they will help you to quickly diagnose any problem you are having. Once you have got the game working, and you want to deploy it on a live Web site, you can tell it to surpress the visible alerts, so that they do not annoy your users. It will still throw errors if something is wrong - hopefully you can use the browser's script error console if this happens, or you could use your own try...catch structures to manage the errors yourself.

To surpress the error alerts, simply create a global variable called 'hideCardGameErrors', and set it to true before you try to create the game.

The script can create the following error types:

Error nameIn what circumstances is it usedWhen it is thrownAlerts
SolitaireBrowserNotGoodEnoughIf the browser is not DOM compliantWhen you call the solitaireGame constructorNo
SolitaireCardGameCoreNotAvailableIf you do not load the cardgamecore.js script before you load the solitaire.js scriptWhile the solitaire.js script is loadingYes
SolitaireIncorrectCallIf you forget to use the 'new' keyword when creating a solitaireGame objectWhen you call the solitaireGame constructorYes
SolitaireInvalidImagePackIf the addImagePack method was not passed the correct parametersWhen you call the addImagePack methodYes
SolitaireInvalidStringIf you try to translate the game, but get the string format wrongWhen you call the solitaireGame constructorYes
SolitaireNoImagePackSpecifiedIf you do not specify any image packs before trying to start the gameWhen you call the startGame methodYes
SolitaireNoUniqueNameIf you do not specify a unique name when creating a gameWhen you call the solitaireGame constructorYes
SolitaireNoWorkspaceParentIf you do not specify a valid holding elementWhen you call the solitaireGame constructorYes
-none-If an image from the image pack fails to load (not including the 'deal' or 'options' button images)The first time a card image fails to load after you call the startGame methodYes

Reserved words

The game creates a number of global variables. You should be careful not to create other variables with the same names, or you will cause the game to fail. This is the list of global variables created by the game:

Note that the game also creates several elements, as shown in the section on 'Styling the play area'. These elements have class attributes, and you must ensure that your page does not conflict with any of these.

Note also that if you choose to use an onload listener, you will have conflicts with other scripts that use an onload listener. If you do use any of these, you should combine the onload handlers into a single handler that provides all of the functionality. You could also use DOM 2 events (and IE's equivalent) but then you will completely lose support for IE on Mac.

Special events

The game provides some events to allow you to tap into what is currently happening in the game. The events are fairly basic, since all they allow you to do is deny or allow the action. However, they do try to provide you with relevant contextual information about the action. Note that they are not real events, they are simply functions that are run in a similar way to event handlers.

For normal games there is no need to use these events, unless you want that little bit of extra control. For example, if you want to prompt before re-dealing, or if you want to allow them to move cards from the waste pile onto the deck only three times per game. Perhaps someone wants to build a Vegas rules module - that should be possible using the events.

To add an event handler, you must use the traditional event handler syntax to add the event to the solitaireGame object that you created. A pseudo event object is passed as the first argument to the event handler. Note that you must return a value if the event is a type that can be cancelled. True allows the action, and false cancels it. If you fail to return a value, it will assume you want to cancel the action:

game1.ongamewon = function (e) {
	return confirm('Gotcha!');
};

Note: see inside the cardgamecore.js file (or the animations section above) for more details of what properties and methods you can access, using the properties provided by the event objects.

Available events

oncancelprefs
Fires whenever they were in the process of changing options but decided to cancel without saving any changes. Cancelling this action causes the preferences dialog to remain open.
Event object properties
currentTargetA reference to the game object
targetA reference to the game object
type'cancelprefs'
ondeal
Fires whenever they give up on a game, and choose to deal another set. Cancelling this action prevents the game from dealing.
Event object properties
currentTargetA reference to the game object
targetA reference to the game object
type'deal'
onflipcard
Fires whenever a card on the top of a tableau is about to turn over to show its face. Cancelling this action causes the card to remain face down.
Event object properties
currentTargetA reference to the card that is about to move
relatedTargetA reference to the stack that the card is about to move to
targetA reference to the card that is about to move
type'flipcard'
ongamewon
Fires whenever they win a game. Cancelling this action causes the game to end silently without dealing a new game. Allowing this action deals a new game. Note that if you create a handler for this event, the game will not display the usual prompt to re-deal.
Event object properties
currentTargetA reference to the game object
targetA reference to the game object
type'gamewon'
onmovecard
Fires whenever a card is about to move to a stack. Cancelling this action causes the card(s) not to move to the new stack. If several cards are about to move together, it will only fire for the card they were trying to move, not any of the cards stacked on top of it. Note that if you cancel this action, and there were several possible moves that could have occurred (such as double clicking an Ace to move it to a foundation stack), the event will fire for each of the possible moves (unless you allow one of them).
Event object properties
cheatA boolean true/false saying if they used the cheat key while making the move (will be true only if they are moving cards onto a tableau and the move would normally be illegal, but they used the cheat key and cheats are enabled)
currentTargetA reference to the card that is about to move
relatedTargetA reference to the stack that the card is about to move to
targetThe card that is about to move
type'movecard'
onresetdeck
Fires whenever they return cards from the waste pile back onto the deck. If you cancel this action, the cards will remain on the waste pile.
Event object properties
currentTargetA reference to the deck
relatedTargetA reference to the waste pile stack
targetA reference to the deck
type'resetdeck'
onsaveprefs
Fires whenever they save preferences (even if they did not make any changes before saving). If you cancel this action, the changes will not be saved, and the preferences dialog will not close.
Event object properties
cardBackThe backimage string for the selected card back
cardSetA reference to the selected image pack object
currentTargetA reference to the game object
dealNumThe number of cards to be dealt
targetA reference to the game object
type'saveprefs'
onshowprefs
Fires whenever they try to open the preferences dialog. If you cancel this action, the dialog will not appear.
Event object properties
cardBackThe backimage string for the currently selected card back
cardSetA reference to the currently selected image pack object
currentTargetA reference to the game object
dealNumThe number of cards currently being dealt
targetA reference to the game object
type'showprefs'
onwastepile
Fires whenever they try to deal cards onto the waste pile. If you cancel this action, the cards will not be dealt.
Event object properties
currentTargetA reference to the card on the top of the deck
dealNumThe number of cards currently being dealt
relatedTargetA reference to the waste pile stack
targetA reference to the card on the top of the deck
threeCardsAn array containing references to the top three cards on the deck (the second and third may be null if there are less than three)
type'wastepile'
This site was created by Mark "Tarquin" Wilton-Jones.
Don't click this link unless you want to be banned from our site.