奥伯龙/ETH 奥伯龙/教程/游戏
这些教程页面由 André Fischer (afi) 编写,并由 Hannes Marais 提供编辑帮助,托管在 ETHZ 并保留在 ETH 许可证 下。相关内容可在系统中通过 Book.Tool 找到。扩展内容也可在 纸质版 上找到。一些教程页面位于 WayBack 档案 中。
由于每个操作系统都至少提供了一个纸牌接龙,因此我决定编写一个框架,允许任何人为奥伯龙系统 3 开发这样的游戏。
我从 纸牌接龙 开始,因为它是一个非常简单的游戏。然后我编写了 蜘蛛纸牌,它基于我几年前制作的原生 Amiga 奥伯龙版本。我制作的最新(也许也是最后一个)纸牌接龙是 空细胞纸牌。我认为这三个例子足以说明如何实现自己喜欢的纸牌接龙。有关此框架功能的更多或更少完整的概述,请参阅下面的“如何编写新的纸牌游戏”部分,并研究源代码。
要开始这三个游戏中的任何一个,请打开 CardGames.Panel。
背景 - 选择九种卡牌背景之一
打开帮助 - 打开此文本
点击
抽取 - 在纸牌接龙中要抽取到牌堆的卡牌数量
我希望您在这三个游戏中玩得开心。
奥伯龙系统 3 版本的也许是最著名的纸牌接龙游戏,由 Wes Cherry 创建,在 Microsoft Windows 95 中发现,也提供在其他几个操作系统中。
游戏板有三个区域:左上角的牌堆,右上角的四个“花色”堆栈,以及在牌堆和花色堆栈下方的一排七个“行”堆栈。每个行堆栈中的卡牌数量从左到右依次增加,从一到七。每个行堆栈的最上面一张牌朝上,其他牌朝下。
游戏的目的是将所有卡牌按照从 A 到 K 的升序移动到四个花色堆栈。例如,您可以将红桃 2 放在红桃 A 上面。
要将一张卡牌(或一堆卡牌)从一个地方移动到另一个地方,请指向它,按下鼠标左键,将卡牌拖到最终目的地,然后释放键。如果这是一个合法的移动,程序将移动卡牌,或者如果卡牌不适合,则不执行任何操作。
在游戏过程中,您可以通过拖动一张卡牌或一堆卡牌并将其放在另一个行堆栈的最上面一张卡牌上,来构建行堆栈。行堆栈中的卡牌按照降序堆叠,红色和黑色卡牌交替排列。例如,您可以将红桃 2 放在梅花 3 上面。
如果在此过程中,您在行堆栈中发现了一张朝下的卡牌,请用鼠标左键单击将其翻转过来。如果以 K 开头,也可以在一张空的行堆栈上构建一个堆栈。
当您完成了所有可能的移动后,单击牌堆开始以三张卡牌为一组翻转卡牌。牌堆上朝上的那张卡牌始终可供玩耍。当牌堆结束时,翻转牌堆并继续抽取卡牌。
当您指向一张可以移动的卡牌时,鼠标指针将变为“指向手”。
要将一张卡牌移动到四个花色堆栈之一,您也可以用鼠标中键单击卡牌,而不是使用拖放。同样,程序也会将卡牌移动到相应的堆栈,或者如果卡牌不适合任何堆栈的最上面的卡牌,则不执行任何操作。
菜单栏中有两个游戏控制按钮
[新建] - 丢弃当前游戏并开始新游戏。
[撤销] - 撤销上一步操作。撤销功能可以重复回游戏开始。
- 放在花色堆栈上的卡牌不能再移回行堆栈。
- 牌堆上的卡牌只能以 3 张卡牌为一组翻转。
- 这个纸牌接龙具有更强大的撤销功能。MS 纸牌接龙只允许一次撤销。
- 这个纸牌接龙没有评分系统和计时。
奥伯龙系统 3 版本的蜘蛛纸牌游戏(与 SUN 的 OpenLook 一起提供)。以下文字改编自 Sun Microsystems, Inc. 的 Donald R. Woods 撰写的原文。
蜘蛛纸牌是使用双副牌的纸牌游戏变体,比之前的纸牌游戏更复杂,也更具挑战性。
游戏棋盘由左上角的未发牌堆、右上角的八个花色堆以及双副牌中剩余的牌组成,这些牌在游戏开始时被分成十列发牌。
游戏的目标是按从国王到 A 的顺序,用相同的花色排列成一个堆,并将完成的堆从桌子上移到上面的花色堆中。当所有八个堆都被建立并移除时,你赢得了游戏。更具挑战性的版本是在所有八个堆都完成之前,将它们留在桌子上,而不是立即将完成的堆移到上面的堆中。
可以将比它低一张的牌移到相同或不同花色的牌上;但是,只能将相同花色、相邻的牌作为一组移动。例如,可以将黑桃 5 移到黑桃 6 或红心 6 上。将黑桃 5 移到黑桃 6 上是更好的移动,因为这对牌可以作为一组移动,而黑桃 5、红心 6 组合则不能。此外,将黑桃 4 移到黑桃 6 上是非法移动。国王只能移到空列(或者当国王到 A 的序列完成时,移到上面的堆),因为国王没有比它更大的牌。
作为一种通用的策略,应该尝试清空一列或几列,因为这是移动牌的最灵活的方式。空列是指桌面上所有牌都被移除的列。注意:在发牌下一轮之前,所有列必须至少有一张牌。
手牌:最初没有发出的 60 张牌构成“手牌”。只要你愿意(通常是在你卡住时),你可以在手牌中发出一排十张牌,正面朝上放在牌堆上。注意:如果存在空列,则不允许这样做。你必须先将一张牌放到空列中。请注意,这些额外的发牌会导致牌堆中出现不连续性;也就是说,你可能会得到覆盖其他牌的牌,而这些牌不是下一张更大的牌。如果你在发了六次额外发牌中的最后一次发牌后仍然卡住了,你就输了。
如果你在牌柱内或牌柱下方任何位置按下鼠标中键,则表示你想移动该牌柱的顶部的牌(顶部的牌:一排,相同的花色)。程序将寻找一个目标牌柱,然后将牌从第一个牌柱移动到第二个牌柱。如果没有这样的合法移动,程序将不做任何操作。查找策略定义如下
- 如果它们形成了一个完成的花色,则移除它们
- 将它们移到相同花色的牌上
- 将它们移到不同花色的牌上
- 将它们移到空位上
如果你改为使用鼠标左键来选择牌柱中的牌,则表示你想移动你正在指向的牌以及覆盖它的任何牌。(当你指向允许移动的牌时,程序会将鼠标指针更改为“指向的手指”。)然后,你必须将鼠标移动到另一个牌柱并释放鼠标按钮。程序将所选牌移动到目的地。你唯一需要使用这种方法(而不是使用中间)的时间是,当你移动的牌的数量少于允许的最大数量时。
要发牌下一轮,请在左上角的牌面朝下的牌堆上左键单击。请记住,在发牌下一轮之前,所有空格都必须填满,除非没有填满空格的可能性。
菜单栏中有两个游戏控制按钮
[新建] - 丢弃当前游戏并开始新游戏。
[撤销] - 撤销上一步操作。撤销功能可以重复回游戏开始。
另一个纸牌游戏空档纸牌的 Oberon System 3 版本,由 Jim Home 创建,在 Microsoft Windows 95 中找到。
游戏棋盘由左上角的四个空位、右上角的四个花色堆以及一副牌组成,这副牌在游戏开始时被正面朝上发成八列(前四列每列七张牌,后四列每列六张牌)。
游戏的目标是使用空位作为占位符,将所有牌移到四个花色堆中。为了获胜,你必须按从 A 到 K 的顺序建立四个花色堆。例如,你可以将红心 2 放在红心 A 上。
空档纸牌知道三种合法移动
- 移到空位:从牌柱底部移到任何牌
- 移到花色堆:从空位或牌柱底部移到任何牌。该牌必须比花色堆中的牌等级更高。
- 移到牌柱底部:从空位或另一个牌柱的底部移到任何牌,前提是该牌的等级比你将它放在其上的牌的等级低一个等级,并且两张牌的颜色不同。例如,你可以将黑色 3 移到红色 4 上。任何牌都可以移到空牌柱。
要将一张牌从一个位置移到另一个位置,只需左键单击要移动的牌(它将被突出显示),然后左键单击要移动牌的区域。要取消移动,只需再次左键单击突出显示的牌。
如果空位足够多,你可以将一排牌从一个牌柱移到另一个牌柱。要移动一排牌,请左键单击牌柱底部的牌,然后左键单击要移动牌的牌柱。程序假设你总是希望将尽可能多的牌移动到空牌柱。如果你只想将牌堆的顶部的牌移到空牌柱,请中键单击空牌柱,而不是左键单击它。
要快速将一张牌从空位或牌柱移到四个花色堆中的一个,只需中键单击该牌。
菜单栏中有两个游戏控制按钮
[新建] - 丢弃当前游戏并开始新游戏。
[撤销] - 撤销上一步操作。撤销功能可以重复回游戏开始。
Cards 模块提供了三种基本数据结构,这些数据结构在每个纸牌游戏中都需要:Card、Stack 和 Move。它还包含一些对这些类型进行操作的基本过程。这里还定义了两个基本消息来控制游戏。
本章首先介绍数据结构和消息,然后解释此模块提供的每个过程的功能。
Card
- next, prev:指向列表(堆栈)中下一张牌、上一张牌的指针 face:牌的花色
- nr:牌的编号(0 = A,12 = K)
- visible:标志,用于指示牌的表面是否可见(TRUE = 显示牌的表面)
Stack
- tail:堆栈中的牌列表。“tail”标记列表的结尾,不是有效的牌
- “tail.next”是列表中的顶部的牌,“tail.prev”是列表中的最后一张牌 do:方法块
- - canDrop:检查“card”是否可以放在堆栈“S”上
- - dropCard:将给定的牌放在堆栈“S”上
- - undoMove:恢复堆栈“S”执行的最新操作
- - restoreStack:重新绘制堆栈“S”
- - trackMouse:跟踪鼠标,直到所有按钮都被释放
- - bgNr:堆栈中的牌背景。有效值介于 [0..8] 之间
Move
每当堆栈执行一个操作(移动、抽牌、翻转等)时,你都应该记住这个操作。这样,将恢复游戏中采取的每个步骤。
SimpleMove
- to:堆栈,牌被移动到该堆栈
- card:放在堆栈上的牌
简单的移动用于指示将牌放到另一个堆栈上的操作。
CollectMsg
- tail:收集的牌列表
此消息被广播以收集堆栈中的所有牌。每个堆栈都必须将其牌附加到消息的尾部。
UndoMsg
- time:移动的时间戳
- stack:执行最新移动的堆栈
此消息被广播以确定哪个堆栈执行了最新移动。每个堆栈都必须检查消息的时间戳。如果堆栈有一个更新的移动(移动时间戳大于消息的时间戳),它必须将堆栈字段设置为自身,并将移动的时间戳分配给时间字段。
BGMsg
- bgNr:背景编号
此消息被广播以更改堆栈中所有牌的背景。将背景编号分配给堆栈中的对应字段。
首先,三个与任何数据结构无关的过程,但通常用于实现纸牌游戏。
Random
- range:范围的上限(不包括此数字)
- return:随机数
返回范围 [0..range[ 中的随机数
Shuffle
- tail:要洗牌的牌列表
顾名思义,此过程会对给定的卡片列表进行洗牌。请记住,“尾部”不是有效的卡片,仅用作哨兵。
TrackMove
- M: Oberon.InputMsg 由堆栈的处理程序接收
- x, y: 堆栈“self”中顶层卡片(完全可见)的左下角
- self: 堆栈,选定的卡片属于该堆栈
- card: 用户想要移动的卡片列表中的第一张卡片
- draw, fade: 用于绘制和淡出跟踪矩形的过程
这是一个标准过程,用于跟踪鼠标指针和选定的卡片列表。在跟踪过程中,TrackMove 会使用给定的参数 x、y 和 card 调用 draw 和 fade 过程。如果用户释放所有鼠标按钮,则该过程将仅在左键被按下时检查(这意味着用户想要将卡片放置在光标当前位置正下方的堆栈上)。它会检查是否可以将给定的卡片放置到此堆栈上,如果可以,则通过调用堆栈“self”的“dropCard”方法,将卡片从堆栈“self”移动到此堆栈。
卡片上的过程
[edit | edit source]以下过程与数据结构 Card 相关。Card 不是一个非常复杂的数据结构,因此可以考虑实现的过程并不多。
DrawCard
- R: Display3.Mask - 包含裁剪区域
- card: 要绘制的卡片
- x, y, w, h: 用于绘制卡片的矩形
- bgNr: 如果卡片不可见,则为背景编号
将给定的卡片绘制到给定的矩形(x, y, w, h)中
NewCard
- suit: 卡片花色
- nr: 卡片编号
- visible: 卡片是否可见
- return: 返回新分配的卡片
分配新卡片的过程。给定的参数将被分配给卡片数据结构中相应的字段。
CloneCard
- card: 要克隆的卡片
- return: 新的克隆卡片
此过程会分配一张新卡片并复制给定卡片的值。
WriteCard
- R: 用于写入卡片值的 文件
- card: 要写入文件的卡片
将给定卡片的值存储到给定的文件中。如果卡片为 NIL,则将值 -1 写入文件。
ReadCard
- R: 用于读取卡片值的 文件
- card: 读取的卡片
从文件中读取卡片。如果读取到 -1,则返回 NIL;否则分配一张新卡片。
NewTail
- return: 新分配的尾部
分配卡片列表的新哨兵。
堆栈上的过程
[edit | edit source]堆栈的卡片存储在双向链表中。由于其列表类型比简单的链表更复杂,我编写了三个过程,这些过程可以使卡片和列表的处理更加容易。堆栈始终有一个尾部,所有卡片都链接到该尾部。字段“tail.next”是堆栈的顶层卡片,字段“tail.prev”是卡片列表中的最后一张卡片。如果要删除卡片列表,则只需给出此列表中的最后一张卡片即可(您可以从堆栈的顶部取卡片)。空闲卡片(不属于任何堆栈)以反向顺序管理在一个环中。顶层卡片可以通过字段“card.prev”访问。
IsEmpty
- tail: 要检查的列表
- return: 如果列表为空,则返回 TRUE
检查给定的列表是否为空。空表示列表仅包含哨兵“tail”。
RemoveCard
- tail: 要从中删除卡片的列表
- card: 要删除的卡片列表中的最后一张卡片
删除给定列表(由尾部指定)中的所有卡片。返回已删除的卡片列表。该列表以“card”开头,以“tail.next”结尾,并且以反向顺序排列(堆栈的顶层卡片现在是此列表中的最后一张卡片)。
AppendCard
- tail: 要附加卡片的列表
- card: 要附加的卡片列表
此过程执行 RemoveCard 的相反操作:它将列表(由“card”指定)中的卡片附加到列表(由“tail”指定)。“card.prev”将是堆栈的顶层卡片(=“tail.next”),而“card”将位于列表的中间或末尾(由“tail”指定)。
移动过程
[edit | edit source]有两个过程用于处理移动。由于移动涵盖了管理历史记录的字段,因此应使用这些过程添加和清除堆栈操作。
AppendMove
- S: 移动所属的堆栈
- M: 要附加到堆栈的移动
将给定的移动附加到此堆栈的移动列表,并将实际时间分配给移动的时间字段。
ClearMove
- S: 要清除移动列表的堆栈
清除给定堆栈的所有移动。
基本方法
[edit | edit source]分配给基本方法块“methods”的基本过程的描述。当您实现新的堆栈类型时,可以(必须)覆盖这些过程。
CanDropCard
- S: 要将卡片放置到的堆栈
- card: 要放置到堆栈上的卡片
- return: 如果可以将卡片放置到堆栈上,则返回 TRUE;否则返回 FALSE
此方法由框架使用,用于检查用户是否允许将选定的卡片放置到此堆栈上。结果取决于纸牌游戏的规则。
DropCard
- S: 要放置卡片的堆栈的顶层卡片
- card: 要放置到堆栈上的卡片
将列表(“card”)中的所有卡片附加到堆栈“S”并重新绘制堆栈。
MoveCard
- self: 要从中取卡片的堆栈
- to: 要将卡片放置到的堆栈
- card: 要移动的卡片列表中的最后一张卡片(从堆栈“self”的顶层卡片开始)
- undo: 用于指示移动是否是撤消移动的标志
此方法用于将卡片从堆栈“self”移动到堆栈“to”,其中要移动的卡片列表由“card”指定。此实现会移动卡片,如果标志“undo”设置为 FALSE,则会将 SimpleMove 附加到堆栈“self”的移动列表中。
UndoMove
- S: 要将卡片移动到的堆栈
- M: 要恢复的移动
此方法用于恢复移动。此实现仅处理 SimpleMove。无论何时定义新的移动类型,您都必须在自己的模块中扩展此方法。
DrawSrack
- S: 要重新绘制的堆栈
- M: Display3.Mask - 包含裁剪区域
- x, y, w, h: 用于绘制堆栈的矩形
只要需要重新绘制堆栈(Display.DisplayMsg),就会调用此方法。此实现会绘制堆栈的顶层卡片(如果堆栈非空)。
TrackMouse
- S: 接收 Oberon.InputMsg 的堆栈
- M: 由堆栈处理程序接收的 Oberon.InputMsg
只要鼠标指针进入堆栈,就会调用此方法。如果处理了消息或将其保留,则将 M.res 设置为大于 -1 的值,以便框架可以执行默认操作。由于此实现没有执行任何操作,因此将应用默认行为。
堆栈对象过程
[edit | edit source]现在我想解释实现 Oberon System 3 对象所需的标准过程。由于堆栈只是一个可见的小工具,因此必须实现某些过程。
CopyStack 复制所有堆栈值以及链接到字段“tail”的所有卡片。
StackHandler 对象处理程序为您做了很多工作,它是框架的核心。我尝试以这样一种方式对消息进行响应,即您不必花费大量时间来实现自己的处理程序(此工作委派给方法)。有些消息无法以全局方式有效地处理,因此您必须自己对其中一些消息进行响应。这些消息是 Objects.AttrMsg 和 Objects.CopyMsg。如果堆栈中有多个卡片列表,您还必须实现 CollectMsg 和 Objects.FileMsg。所有其他消息都应以适当的方式处理,如果出现故障,请先检查方法的实现,并确保您了解其功能。不要更改此模块中的默认处理程序和方法。如果 Cards.Mod 中出现错误或功能缺失,请告诉我,我会尝试修复它。此框架不处理打印,因此,如果您想打印堆栈,请对 Display.PrintMsg 也进行响应。我可能将来有时间处理这个问题(可能会导致一个额外的方法,我认为)。
InitStack 初始化默认堆栈的字段。在您自己的初始化过程中调用此过程。
NewStack 分配、初始化一个新的堆栈对象,并将其分配给“Objects.NewObj”。
命令
[edit | edit source]此模块提供两个命令
SetBG 使用背景编号作为参数,并广播 BGMsg。
Undo 通过广播 UndoMsg 来恢复最新操作。
MineSweeper - 版本 1.2
[edit | edit source]这是 Robert Donner 和 Curt Johnson 编写并在 Microsoft Windows 95 中找到的原始 MineSweeper 的 Oberon System 3 版本。它是由 Markus Dätwyler 和 Patrick Saladin 创建的。
使用 Desktops.OpenDoc My.Game(MineSweeper.NewDoc) 启动游戏。
规则
[edit | edit source]游戏的目标是在尽可能短的时间内找到地雷区的所有隐藏地雷,同时避免踩到任何地雷。地雷区被分成方格,每个方格显示其周围方格中地雷的数量。通过组合给定的信息,可以找到大多数地雷。对于剩下的,你仍然需要一点运气(就像现实生活中一样!)。当玩家点击地雷(这种情况很常见)或所有地雷都被标记为旗帜或所有没有地雷的方格都被覆盖时,游戏结束。
游戏的主要视图分为两部分。左侧显示玩家在场上进行第一次操作(覆盖方格或设置旗帜)后经过的时间。第二个字段显示玩家需要设置多少旗帜才能结束游戏。右侧显示地雷区本身。玩家可以在那里设置旗帜并尝试覆盖方格。
[新游戏] - 开始新游戏(此按钮出现在菜单栏和面板中)。游戏使用其旧设置(宽度、高度和地雷数量)。可以使用滑块调整游戏设置。
[暂停] - 暂停游戏。在暂停期间,时间将停止,地雷区将完全覆盖,因此玩家无法作弊。要继续游戏,请再次按下暂停按钮,或者使用“新游戏”按钮开始新游戏。
滑块 - 滑块和文本字段控制游戏的设置。左侧滑块控制地雷区的宽度,中间滑块控制其高度,右侧滑块控制地雷区中的地雷数量。玩家可以选择使用滑块选择一个值,或者在相应的文本字段中输入正确的数字。字段的正确值是
- 宽度:8 到 30
- 高度:8 到 16
- #地雷:10 到 99,但最多为地雷区中方格数量的 70%
鼠标左键 - 如果方格处于中性状态,则覆盖指向的方格。如果周围方格中没有地雷,它将递归地覆盖这些方格。覆盖的方格将从中性状态变为
- 覆盖状态。数字告诉玩家周围方格中存在多少地雷,如果有的话。
- 表示运气不佳!方格中隐藏着一颗地雷,它爆炸了。游戏结束,所有剩余的方格都被覆盖,显示其他地雷的位置。
- 出现在玩家误判并标记了安全方格的方格上。
鼠标中键 (??) - 当指向一个覆盖的方格时,它将计算所有周围方格上的旗帜,并将此数字与指向方格上显示的数字进行比较。如果相同,它将递归地覆盖未标记的周围方格(未标记表示方格上没有旗帜)。
鼠标右键 - 在覆盖方格的 3 种状态之间切换,以循环方式。
一个 Oberon System 3 版本的拼图游戏,其目标是将一个拼图拼凑起来。它由 Emil Zeller 创建。
通过执行 Desktops.OpenDoc (Scramble.NewDoc) 打开一个游戏板。用鼠标中键点击标题为“拼图”的菜单栏按钮以开始游戏。其中一块会被移除,以允许移动其余的块。
游戏的目标是拼凑一个拼图,拼图的一面是图片,另一面是编号的块。用鼠标中键点击一块,将该块或一组块移动到空槽的方向。要反转拼图,用鼠标中键点击菜单栏按钮,标题为“数字”或“图片”,具体取决于显示哪一面。
可以使用以下命令之一更改拼图的图片或块的数量
Scramble.ChangePict ( pictureName | ^ ) 将标记拼图的图片替换为参数中指定的图片。Clown.Pict、Grapes.Pict 和 Default.Pict 是 Oberon 软件附带的图片文件的示例。图片变得清晰可见,但块的顺序保持不变。
Scramble.ChangeSize ( m n | ^ ) 将标记的拼图分成 m * n 个块,并重新排列拼图。
一个 Oberon System 3 版本的推箱子游戏。这个游戏由 Emil Zeller 创建。
通过打开文档 Desktops.OpenDoc (Sokoban.NewDoc) 启动游戏。
使用键盘方向键移动红色的推箱子,将黄色的垃圾箱推到周围。当垃圾箱到达其正确的最终位置时,其颜色将变为绿色。
一个 Oberon System 3 版本的著名俄罗斯方块游戏。这个游戏由 Wolfgang Ibl 创建。
要开始游戏,请打开 Desktops.OpenDoc Tetris.Panel。
??
C
F
M
S
T
修订日期:1996 年 12 月 11 日
安装日期:1997 年 5 月 30 日