# Oberon/ETH Oberon/Tutorial/Games

These tutorial pages were written by André Fischer (afi) with editorial assistance of Hannes Marais, were hosted at the ETHZ and remain under the ETH license. Related content is found in the system via Book.Tool. Extended content is also available on paper. Some tutorial pages are in the WayBack archive.

## Introduction to the card games - by Patrick Saladin

As every operating system has at least one solitaire to offer, I decided to write a framework that would permit anyone to develop such games for Oberon System 3.
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 CardGames.Panel.
Backgounds - choose one of nine card backgrounds Open Help - opens this text
Clicking on:
    opens a Solitaire game
    Cool Oberon Object opens a Spider game
    Cool Oberon Object opens a Freecell game
Draws - number of cards to be drawn to the deck in Solitaire
I hope, you will have a lot of fun with these three games.

## Solitaire - Release 1.3

An Oberon System 3 version of the maybe most famous solitaire game Solitaire, created by Wes Cherry and found in Microsoft Windows 95 and also provided with several other operating systems.

### Rules

The gameboard has three areas: the deck in the upper left corner, four "suit" stacks in the upper right and a row of seven "row" stacks below the deck and suit stacks. The number of cards in each row stack increases from one to seven, from left to right. The top card of each row stack is face up, the other cards are face down.
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.

### Differences with the MS Solitaire (TM)

* A card placed on a suit stack cannot be moved back to a row stack.
* Cards on the deck are turned over in packets of 3 only.
* This Solitaire has a more powerfull Undo function. The MS Solitaire allows a single Undo.
* This Solitaire has no scoring system and no timing.

## Spider - Release 1.1

An Oberon System 3 version of the Spider game (coming with SUN's OpenLook). The following text is adapted from an original text written by Donald R. Woods of Sun Microsystems, Inc.
Spider is a variant of solitaire using a double deck of cards, which is more complex and also more challenging than the previous one.

### Rules

The gameboard consists of the stack of undealt cards in the upper left corner, eight suit stacks in the upper right corner and of the remaining cards of a double deck, which are dealt in ten columns at the beginning of the game.
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.

### The program

If you press the middle mouse button anywhere within or below a column of cards, it says you want to move the top cards of that column (top cards: in a row and of same suit). The program looks for a destination column and then moves the cards from the first column to the second. If there is no such legal move, the program does nothing. The lookup strategy is defined as followed:
* remove them if they form a completed suit
* move them onto a card of the same suit
* move them onto a card of a different suit
* move them into a space
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.

## Freecell - Release 0.9

An Oberon System 3 version of another solitaire game FreeCell, created by Jim Home and found in Microsoft Windows 95.

### Rules

The gameboard consists of four free cells in the upper left corner, four suit stacks in the upper right and the deck of cards, which is dealt face-up in eight columns at the beginning of the game (seven cards on each of the first four columns and six on the last four).
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:
* move to a free cell: any card from the bottom of a column
* move to a suit stack: any card from a free cell or from the bottom of a column. The card must be greater in rank than the card in the suit.
* move to the bottom of a column: any card from a free cell or from the bottom of another column, provided the rank of the card is one less than the rank of the card you will place it on, and the colors of the cards are different. For example, you can move a black three onto a red four. Any card can be moved to an empty column.
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.

## How to write a new card game
### Introduction

The module Cards provides three basic data structures, needed in every solitaire: Card, Stack and Move. It also contains some basic procedures that operate on these types. Two basic messages to control the games are defined in here as well.
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.

### Data structures

Card
    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'
    - 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.

### Procedures

First, three procedures not related to any data structure but often used to implement card games.
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'.

#### Procedures on card

The following procedures are related to data structure Card. Card is not a very complex data structure, so there are not many procedures one can think of implementing.
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.

#### Procedures on stack

The cards of a stack are stored in a double linked list. As their type of list is a bit more complex than simple linked list, I wrote three procedures that make the handling of cards and lists much easier. A stack always has a tail to which all the cards are linked. Field 'tail.next' is the top card of a stack and field 'tail.prev' the last card in a list of cards. If you want to remove a list of cards, it is sufficient to give the last card in this list (you just can take cards from the top of a stack). Free cards (not belonging to any stack) are managed in a ring of reversed order. The top card can be reached by field 'card.prev'.
IsEmpty
    tail: list to check
    return: TRUE if list is empty
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').

#### Procedures on move

There are two procedures to handle Moves. As Move covers fields to manage history, these procedures should be used to add, clear stacks actions.
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.

#### Basic Methods

Description of basic procedures assigned to the basic method block 'methods'. These procedures can (must) be written over, when you implement a new type of 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.

#### Stack-object procedures

Now I would like to explain the standard procedures needed to implement an Oberon System 3 object. As a stack is nothing but a visible gadget, one must implement certain procedures.
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'.

### Commands

This module has two commands to offer:
SetBG Takes the background number as parameter and broadcasts a BGMsg.
Undo Recovers latest action by broadcasting an UndoMsg.

## MineSweeper - Version 1.2

An Oberon System 3 version of the original MineSweeper written by Robert Donner and Curt Johnson, and found in Microsoft Windows 95. This one was created by Markus Dätwyler and Patrick Saladin.
Start the game with Desktops.OpenDoc My.Game(MineSweeper.NewDoc).

### Rules

The object of this game is to find all the hidden mines in a mine field as quickly as possible without uncovering any of them. The mine field is divided into square boxes and each box knows how many mines there are in the neighbouring boxes. By combining the given pieces of information it should be possible to find most of the mines. For the rest, you still need a lucky hand (just like in real life!). The game is over when either the player taps on a mine (which is mostly the case) or all mines are marked with a flag or all boxes with no mines are covered up.
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.

### Game settings

[New] - starts a new game (this button appears in the menu bar and in the panel). The game takes it old settings (width, height & number of mines). The game settings can be adjusted with the sliders.
[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

### Controlling the game with mouse key clicks

Left mouse key- Covers up the box pointed at if it is in a neutral state. If there are no mines in the neighbouring boxes, it recursively covers up these too. An uncovered box turns from neutral to:
    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

## Scramble

An Oberon System 3 version of Scramble, consisting in unscrambling a puzzle. It was created by Emil Zeller.
Open a game board by executing Desktops.OpenDoc (Scramble.NewDoc). Middle click on the menu bar button captioned "Scramble" to start a game. One of the pieces is removed to allow shifting of the remaining pieces.

### Rules

The object of the game is unscramble a puzzle with a picture on one size and numbered pieces on the other side. Middle click on a piece, moves the piece or a group of them in the direction of the empty slot. To reverse a puzzle, middle click on the menu bar button captioned "Num" or "Pict" depending on which side is visible.
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.

## Sokoban

An Oberon System 3 version of Sokoban. This one was created by Emil Zeller.
Start the game by opening the document Desktops.OpenDoc (Sokoban.NewDoc).

### Rules

Push the yellow garbage cans around using the red pusher which is moved using the keyboard cursor keys. The color of garbage can changes to green when it has reached its correct final position.

## Tetris - Version 2.5

An Oberon System 3 version of the well-known Russian Tetris game. This one was created by Wolfgang Ibl.
To start the game open the Desktops.OpenDoc Tetris.Panel.

### Rules
## Index

C
card game: writing your own one card games
F
Freecell
M
MineSweeper
S
Scramble Sokoban Solitaire Spider
T
Tetris
Revised 11 Dec 1996 Installed on 30 05 1997