I began with Solitaire because it is a very simple game. Then I wrote Spider based on a native Amiga Oberon version I did some years ago. The latest (and maybe last) solitaire I did, was Freecell. I think these three examples suffice to show how to implement one's own, preferred solitaires. For a more or less complete overview of the functionality of this framework, refer to the section "How To Write A New Card Game" below, and study the source codes.
To start any of the three games open the
Backgounds - choose one of nine card backgrounds
Open Help - opens this text
Clicking on:
opens a Solitaire game
opens a Spider game
opens a Freecell game
Draws - number of cards to be drawn to the deck in Solitaire
I hope, you willl have a lot of fun with these three games.
The object of the game is to move all the cards to the four suit stacks in ascending order from ace to king. For example, you can place the two of hearts on the ace of hearts.
To move a card (or a stack of cards) from one place to another, point to it, press the left mouse key, drag the card(s) to the final destination and release the key. The program moves the card(s) if it is a legal move or does nothing if the card(s) do not fit.
During the game, you will be building row stacks by dragging a card or a stack of cards and dropping it on the top card of another row stack. Cards in a row stack are stacked in descending order, alternating between red cards and black cards. For example, you can place the two of hearts on the three of clubs.
If in the process, you uncover a face down card in a row stack, turn it face up with a left mouse key click. A stack may also be built on an empty row stack provided it starts with a king.
When you made all the possible plays, click the deck to begin turning over cards in packets of three. The card that is face up on the deck is always available for play. When the end of the deck is reached, turn it over and continue to draw cards.
The mouse pointer changes to a 'pointing hand' as soon as you are pointing to a card you are allowed to move.
To move a card to one of the four suit stacks, you can also click with the middle mouse key on the card instead of using drag and drop. Here also, the program moves the card to the appropriate stack or does nothing if the card doesn't fit on any of the stack's top card.
There are two game control buttons in the menu bar:
[New] - Throws away the current game and starts a new one.
[Undo] - Undo the last move. The Undo function can be repeated back to the beginning of the game.
Spider is a variant of solitaire using a double deck of cards, which is more complex and also more challenging than the previous one.
The object of the game is to build a stack of the same suit from King to Ace and to move completed stacks from the table to the suit stacks above. When all eight stacks have been built and removed, you have won the game. A more challenging version of this is to leave all eight stacks on the table until done instead of moving the completed ones immediately to the stacks above.
One may move the next lower card onto a card of the same or different suit; however, one may only move contiguous cards of the same suit as a group. For example, one may move the five of spades onto either a six of spades or a six of hearts. Moving the five of spades onto the six of spades is a better move since this pair of cards may be moved as a group, whereas the five of spades, six of hearts group may not. Also, moving the four of spades onto the six of spades is not a legal move. A King may only be moved to an empty column (or moved to the stacks above when the sequence King to Ace is complete) since there are no higher cards than a King.
As a general strategy, one should attempt to free a column, or several of them, since this is the most flexible way to move cards around. A empty column is where all cards on the table have been removed. Note: before dealing the next round, all columns have to have at least one card in them.
THE HAND. The sixty cards not dealt initially form the "hand". Whenever you wish (typically, whenever you get stuck), you may deal a new row of ten cards from the hand face-up upon the piles. NOTE: You are not allowed to do this if an empty column exists. You must first drop a card on it. Notice that these additional deals tend to introduce discontinuities in the piles; that is, you can get cards covering others that are not next-higher in rank. If you get stuck after having dealt the last of the six additional deals, you have lost.
If you instead use the left mouse button to select a card within a column, it says you want to move the card you are pointing at plus any cards covering it. (The program changes the mouse pointer to a 'pointing hand' as soon as you're pointing on a card you are allowed to move.) Then you must move the mouse to another column and release the button. The program moves the selected cards to the destination. The only time you need to use this method (instead of using middle) is if you're moving fewer cards than the maximum permitted.
To deal a new round, left key click on the facedown stack in the upper left. Remember that all spaces must be filled before you can deal a new round, except there is no possibility to fill them.
There are two game control buttons in the menu bar:
[New] - Throws away the current game and starts a new one.
[Undo] - Undo the last move. The Undo function can be repeated back to the beginning of the game.
The object of the game is to move all the cards to the four suit stacks, using the free cells as place holders. To win, you have to build the four suit stacks in ascending order from ace to king. For example, you can place the two of hearts on the ace of hearts.
Freecell knows three legal moves:
To move a card from one place to another, just left click on the card you want to move (it will be highlighted) and then left click the area to which you want to move the card. To cancel a move, simply left click again on the highlighted card.
You can move a stack of cards from one column to another if there are enough free cells open. To move a stack, left click the bottom card in the column, then left click the column that you want to move the stack to. The program assumes that you always want to move as many cards as possible to a free column. If you want to move only the top card of a stack to a free column, middle click on the free column instead of left clicking on it.
To quickly move a card from a free cell or a column to one of the four suit stacks, simply middle click on the card.
There are two game control buttons in the menu bar:
[New] - Throws away the current game and starts a new one.
[Undo] - Undo the last move. The Undo function can be repeated back to the beginning of the game.
This chapter gives you first a description of the data structures and messages and then explains the functionality of each procedure provided by this module.
next, prev: Pointer to next, previous card in a list (stack)
face: suit of a card
= 0 (clubs)
= 1 (spades)
= 2 (hearts)
= 3 (diamonds)
nr: card number (0 = Ace, 12 = King)
visible: flag to indicate if a card's surface is visible or not (TRUE = show card's surface)
Stack
tail: list of cards in stack. 'tail' marks the end of a list and is not a valid card
'tail.next' is the top card and 'tail.prev' the last card in the list
do: a block of methods
- canDrop: checks if 'card' can be dropped on stack 'S'
- dropCard: drops the given card(s) on stack 'S'
- moveCard: moves the given card(s) from stack 'self' to stack 'to'
- undoMove: recovers the latest action done by stack 'S'
- restoreStack: redraws stack 'S'
- trackMouse: tracks the mouse until all buttons are released
bgNr: cards background in stack. Valid values are between [0..8]
Move
Whenever an action (move, draw, flip, ...) is taken by a stack, you should remember this action. This way, will recover every step taken during the game.
SimpleMove
to: stack, cards have been moved to
card: card dropped on stack
A simple move is used to indicate a drop of cards to another stack.
CollectMsg
tail: list of collected cards
This message is broadcast to collect all cards in a stack. Each stack has to append its cards to the tail of the message.
UndoMsg
time: timestamp of the move
stack: stack that did the latest move
This message is broadcast to determine, which stack did the latest move. Each stack has to check the message timestamp. If the stack has a more recent move (move timestamp is greater than that of the message), it must set the stack field to itself and must assign the timestamp of its move to the time field.
BGMsg
bgNr: background number
This message is broadcast to change the background of all the cards in a stack. Assign the background number to the corresponding field in the stack.
Random
range: upper limit of range (not including this number)
return: random number
Returns a random number in range [0..range[
Shuffle
tail: list of cards to shuffle
As the name implies, this procedure shuffles the given list of cards. Remember 'tail' is not a valid card and is only used as a sentinel.
TrackMove
M: Oberon.InputMsg received by stack's handler
x, y: lower left corner of top card (completely visible) in stack 'self'
self: stack, the selected cards belong to
card: first card in a list of cards the user want to move
draw, fade: procedures to draw, fade tracking rectangle
This is a standard procedure to track the mouse pointer and a list of selected cards. While tracking, TrackMove calls the procedures draw and fade with the given parameters x, y and card. If the user releases all the mouse buttons, the procedure checks, only if the left mouse button was being pressed (this means the user wants to drop the cards on the stack right under the cursor's current position). It checks if it is possible to drop the given cards on this stack and if so, moves the cards from stack 'self' to this stack by calling method 'dropCard' of stack 'self'.
DrawCard
R: Display3.Mask - contains clipping region
card: card to draw
x, y, w, h: rectangle to draw card in
bgNr: background number if card is not visible
Draws the given card into the given rectangle (x, y, w, h)
NewCard
suit: card suit
nr: card number
visible: if card is visible or not
return: returns the new allocated card
Procedure to allocate a new card. The given parameter will be assigned to the corresponding fields in the card data structure.
CloneCard
card: card to clone
return: new, cloned card
This procedures allocates a new card and copies the value of the given card.
WriteCard
R: file to write card values to
card: card to write to file
Stores the values of the given card to the given file. IF card is NIL, the value -1 will be written to the file.
ReadCard
R: file to read card value from
card: read card
Reads in a card from the file. If it reads -1, NIL will be returned; otherwise a new card is allocated.
NewTail
return: new allocated tail
Allocates a new sentinel of a card list.
Checks if the given list is empty or not. Empty means the list consists only of the sentinel 'tail'.
RemoveCard
tail: list to remove card(s) from
card: last card in the list of cards to remove
Removes all the cards in the given list (by tail). Returns a list of removed cards. The list starts with 'card' and ends with 'tail.next' and is of reversed order (stack's top card is now the last card in this list).
AppendCard
tail: list to append card(s) on
card: list of cards to append
This procedure does the opposite of RemoveCard: it appends the cards in the list (specified by 'card') to the list (given by 'tail'). 'card.prev' will be the top card of the stack (= 'tail.next') and 'card' will be somewhere in the middle or at the end of the list (by 'tail').
AppendMove
S: stack the move belongs to
M: move to append to the stack
Appends the given move to the list of moves of this stack and assigns actual time to the move's time field.
ClearMove
S: stack to clear move list of
Clears all moves of the given Stack.
CanDropCard
S: stack the card should be dropped on
card: card you want to drop on stack
return: TRUE if card can be dropped on stack, FALSE otherwise
This method is used by the framework to check if the user is allowed to drop the selected card(s) on this stack. The result depends on the solitaire's rules.
DropCard
S: stack top card on
card: card to drop on stack
Appends all cards in list ('card') to stack 'S' and redraws the stack.
MoveCard
self: stack to take cards from
to: stack to put cards on
card: last card in list of cards to move (starts with top card of stack 'self')
undo: flag to indicate if move is an undo move
This method is called to move cards from stack 'self' to stack 'to', where the list of cards to move is specified by 'card'. This implementation moves the cards and appends a SimpleMove to the move list of stack 'self' if flag 'undo' is set to FALSE.
UndoMove
S: stack to move cards to
M: move to recover
This method is called to recover a move. This implementation handles only SimpleMove. Whenever you define new type of moves, you have to extend this method in your own module.
DrawSrack
S: stack to redraw
M: Display3.Mask - contains clipping region
x, y, w, h: rectangle to draw stack in
This method is called whenever a stack needs to be redrawn (Display.DisplayMsg). This implementation draws a stack top card if the stack is not empty.
TrackMouse
S: stack that received Oberon.InputMsg
M: Oberon.InputMsg received by stack handler
This method is called whenever the mouse pointer enters the stack. Set M.res to a value greater than -1, if you handled the message or left it, so the framework can take a default action. As this implementation does nothing, the default behavior will be applied.
CopyStack
Copies all stack values as well as all cards linked to field 'tail'.
StackHandler
The object handler does a lot of work for you and is the heart of the framework. I tried to react on messages in such a manner that you never have to spend a lot of time implementing your own handler (this work is delegated to the methods).
A few messages cannot be handled efficiently in a global sense, so you have to react on some of them by yourself. These messages are Objects.AttrMsg and Objects.CopyMsg. You also have to implement the CollectMsg and Objects.FileMsg, if you have more than one list of cards in your stack.
The whole rest of messages should be handled in a proper way, and if there's a malfunction, check your implementation of the methods first and be sure you understood their functionality. Never change this default handler & methods in this module. Should there be an error or functionality missing in Cards.Mod, please let me know, I will try to fix it.
Printing is not handled by this framework, so if you want to print a stack, react on Display.PrintMsg, too. I might have time to work on this in future (may result in an additional method, I think).
InitStack
Initialize fields of a default stack. Call this procedure within your own initialize procedure.
NewStack
Allocates, initializes a new stack object and assigns it to 'Objects.NewObj'.
SetBG
Takes the background number as parameter and broadcasts a BGMsg.
Undo
Recovers latest action by broadcasting an UndoMsg.
Start the game with
The game's main view is divided in two parts. The left part shows time passed since the player did his first action on the field (covering up a box or setting a flag). The second field shows how many flags the player has to set to end the game. The right part shows the minefield itself. There the player can set the flags and try to cover up the boxes.
[Pause] - pauses the game. During the break, time will stop and the minefield is fully covered, so a player cannot cheat. To continue the game press the pause button again or else start a new game with the New button.
Sliders - The sliders and text fields control the settings of the game. The left slider controls the width of the minefield, the middle one controls its height and the right one controls the number of mines in the minefield. The player can either choose a value using the sliders or by typing in a correct number in the corresponding text fields. The correct values for the fields are:
width: 8 to 30
height: 8 to 16
#mines: 10 to 99, but at most 70% of the number of boxes in the mine field
covered up. The number tells the player how many mines exist in the neighbouring boxes, if any.
means BAD LUCK!!! The box was hiding a mine which exploded. The game ends and all remaining boxes are uncovered, revealing the location of the other mines.
appear on boxes where the player made a bad guess, flagging a free box.
Middle mouse key (??) - When pointing at a covered up box, it counts all the flags on the neighbouring boxes and compares this number with the number shown on the pointed box. If it is the same it recursively covers up the unmarked neighbouring boxes (unmarked means no flag on the box).
Right mouse key - Toggles between the 3 states, in a ring, of a covered box:
neutral - a covered box
flag set - the player thinks that the box covers a mine
not sure - the player is not sure if this box covers a mine or not, but hints at a possible danger
Open a game board by executing
You can change the puzzles's picture or the number of pieces with one of the following commands:
Scramble.ChangePict ( pictureName | ^ ) replaces the picture of the marked puzzle by the picture specified in the parameter. Clown.Pict, Grapes.Pict and Default.Pict are examples of picture files delivered with the Oberon software. The picture is made apparent but the order of the pieces is kept.
Scramble.ChangeSize ( m n | ^ ) divides the marked puzzle into m * n pieces and unscrambles the puzzle.
Start the game by opening the document
To start the game open the