有什么模式可以模拟棋盘游戏吗?[关闭]


93

为了娱乐,我正在尝试将我儿子最喜欢的棋盘游戏编写为一款软件。最终,我希望在其之上构建一个WPF UI,但是现在我正在构建用于对游戏及其规则进行建模的机器。

在这样做的过程中,我不断看到许多棋盘游戏常见的问题,也许其他问题已经比我更好地解决了。

(请注意,玩游戏的人工智能以及围绕高性能的模式对我来说并不有趣。)

到目前为止,我的模式是:

  • 代表游戏箱中实体的几种不可变类型,例如骰子,棋盘,纸牌,棋盘,棋盘上的空间,金钱等。

  • 每个玩家的对象,其中包含玩家的资源(例如金钱,分数),他们的姓名等。

  • 代表游戏状态的对象:玩家,轮到谁,棋盘上的香料的布局等。

  • 管理转弯顺序的状态机。例如,许多游戏都有一个小型的赛前游戏,每个玩家都可以滚动查看谁先出局。那是开始状态。当一个玩家开始回合时,他们先滚动,然后移动,然后必须在原地跳舞,然后其他玩家猜测他们是哪种鸡,然后获得积分。

我可以利用某些现有技术吗?

编辑:最近我意识到的一件事是,游戏状态可以分为两类:

  • 游戏神器状态。“我有10美元”或“我的左手在蓝色上”。

  • 游戏序列状态。“我翻了两次,下一场使我入狱了。” 在这里,状态机可能有意义。

编辑:我在这里真正寻找的是实现象棋,拼字游戏或大富翁这样的多人回合制游戏的最佳方法。我敢肯定,只要从头到尾完成游戏,就可以创建这样的游戏,但是,就像其他设计模式一样,可能有一些使事情变得更加顺利的方法,而这需要仔细研究才能发现。这就是我所希望的。


3
您正在构建某种Hokey Pokey,Monopoly,charades mashup吗?
安东尼·马斯特瑞恩

您将需要一台状态机来处理任何依赖状态(err ...)的规则,例如Monopoly的三重规则。我会发布更完整的答案,但我没有这样做的经验。我可以对此表示敬意。
MSN

Answers:


115

看来这是我刚刚注意到的2个月大的线程,但是到底是什么。之前,我已经设计和开发了用于商业化网络棋盘游戏的游戏框架。我们有一个愉快的工作经验。

由于诸如玩家A有多少钱,玩家B有多少钱之类的东西的排列,您的游戏可能处于(接近)无数个状态中。因此,我敢肯定,您想要远离状态机。

我们框架背后的想法是将游戏状态表示为具有所有数据字段的结构,这些数据字段一起提供完整的游戏状态(即:如果您要将游戏保存到磁盘,则将该结构写出)。

我们使用命令模式来表示玩家可以进行的所有有效游戏动作。这是一个示例操作:

class RollDice : public Action
{
  public:
  RollDice(int player);

  virtual void Apply(GameState& gameState) const; // Apply the action to the gamestate, modifying the gamestate
  virtual bool IsLegal(const GameState& gameState) const; // Returns true if this is a legal action
};

因此,您会看到要确定移动是否有效,可以构造该动作,然后调用其IsLegal函数,并传递当前游戏状态。如果有效,并且玩家确认了该动作,则可以调用Apply函数来实际修改游戏状态。通过确保您的游戏代码只能通过创建和提交合法的动作来修改游戏状态(换句话说,Action :: Apply方法族是唯一直接修改游戏状态的方法),然后确保您的游戏状态永远不会无效。此外,通过使用命令模式,您可以序列化玩家所需的动作,并通过网络发送它们,以在其他玩家的游戏状态下执行。

最终,这个系统出现了一个难题,事实证明它是一个相当优雅的解决方案。有时,行动将具有两个或多个阶段。例如,玩家可能登陆了“大富翁”中的某个地产,现在必须做出新的决定。从玩家掷骰子到决定购买财产之间的游戏状态是什么?我们通过在游戏状态中加入“动作上下文”成员来管理这种情况。动作上下文通常为null,表示游戏当前未处于任何特殊状态。当玩家掷骰子并将掷骰子动作应用于游戏状态时,它将意识到玩家已落在无人财产上,并可以创建新的“ PlayerDecideToPurchaseProperty” 动作上下文,其中包含我们正在等待做出决定的玩家的索引。到RollDice操作完成时,我们的游戏状态表示它当前正在等待指定的玩家决定是否购买房产。现在,除“ BuyProperty”和“ PassPropertyPurchaseOpportunity”动作(仅在游戏状态具有“ PlayerDecideToPurchaseProperty”动作上下文)时才合法之外,所有其他动作的IsLegal方法都容易返回false。

通过使用动作上下文,在棋盘游戏的生命周期中,从来没有任何一个点的游戏状态结构不能完全准确地表示该时间点游戏中发生的事情。这是您的棋盘游戏系统非常理想的属性。如果只检查一种结构,就可以找到想要了解的有关游戏中发生的一切的信息,那么编写代码就会容易得多。

此外,它可以很好地扩展到网络环境,在该环境中,客户端可以通过网络将其操作提交给主机,主机可以将该操作应用于主机的“官方”游戏状态,然后将该操作回传给所有其他客户端以让他们将其应用于复制的游戏状态。

我希望这是简洁而有益的。


4
我不认为这很简洁,但是会有所帮助!已投票。
杰伊·巴祖兹

很高兴这很有帮助...哪些部分不够简洁?我很乐意进行澄清编辑。
安德鲁·托普

我现在正在制作回合制游戏,这篇文章真的很有帮助!
科伊夫

我读到Memento是用于撤消的模式... Memento与Command撤消的模式,请您
注意。.– zotherstupidguy

到目前为止,这是我在Stackoverflow中阅读的最佳答案。谢谢!
帕皮波

18

游戏引擎的基本结构使用状态模式。游戏箱中的项目是各个类的单例。每个状态的结构可以使用策略模式模板方法

一个工厂用于创建被插入的球员,另一单名单的球员。GUI将使用“ 观察者”模式监视游戏引擎,并使用“ 命令模式”创建的多个Command对象之一与之交互。可以在被动视图的上下文中使用Observer和Command,但是根据您的喜好,几乎可以使用任何MVP / MVC模式。保存游戏时,您需要获取当前状态的纪念品

我建议您仔细查看本网站上的某些模式,看看是否有任何一个作为您的起点。同样,游戏板的核心将是状态机。大多数游戏将由游戏前/设置和实际游戏这两个状态表示。但是,如果要建模的游戏具有几种不同的游戏模式,则可以有更多状态。州不必是连续的,例如,战争游戏Axis&Battles有一个战斗板,玩家可以用来解决战斗。因此,游戏前有三种状态,即主板,战斗板,并且游戏不断在主板和战斗板之间切换。当然,转弯顺序也可以由状态机表示。


15

我刚刚完成了使用多态设计和实现基于状态的游戏的工作。

使用称为的基础抽象类GamePhase具有一种重要方法

abstract public GamePhase turn();

这意味着每个GamePhase对象都拥有游戏的当前状态,并调用以turn()查看其当前状态并返回next GamePhase

每个混凝土GamePhase都有保存整个游戏状态的构造函数。每种turn()方法都有一些游戏规则。尽管这会散布规则,但会使相关规则保持紧密联系。每一个的最终结果turn()就是创建下一个GamePhase,并将完整状态传递到下一个阶段。

这允许turn()非常灵活。根据您的游戏,给定状态可以分支到许多不同类型的阶段。这形成了所有游戏阶段的图表。

在最高级别上,驱动它的代码非常简单:

GamePhase state = ...initial phase
while(true) {
    // read the state, do some ui work
    state = state.turn();
}

这非常有用,因为我现在可以轻松创建游戏的任何状态/阶段进行测试

现在回答您问题的第二部分,这在多人游戏中如何工作?在GamePhase需要用户输入的某些时间内,来自的呼叫turn()会询问当前Player用户Strategy给定的当前状态/相位。 Strategy只是一个人Player可以做出的所有可能决定的接口。此设置还允许Strategy使用AI来实现!

安德鲁·托普(Andrew Top)也说:

由于诸如玩家A有多少钱,玩家B有多少钱之类的东西的排列,您的游戏可能处于(接近)无数个状态中。因此,我敢肯定,您想要远离状态机。

我认为这种说法很容易让人误解,尽管确实有很多不同的游戏状态,但只有几个游戏阶段。为了处理他的示例,它只是我的具体GamePhases 的构造函数的整数参数。

垄断

一些GamePhase的示例为:

  • 游戏开始
  • 玩家卷
  • PlayerLandsOnProperty(免费停车,GoToJail,Go等)
  • 玩家交易
  • 玩家购买属性
  • 玩家购买房屋
  • 玩家购买酒店
  • PlayerPaysRent
  • 玩家破产
  • (所有机会和公益金卡)

基础GamePhase中的一些状态是:

  • 球员名单
  • 当前玩家(轮到该玩家)
  • 玩家的金钱/财产
  • 房屋/酒店物业
  • 玩家位置

然后某些阶段会根据需要记录自己的状态,例如PlayerRolls将记录玩家连续两次翻滚的次数。一旦退出PlayerRolls阶段,我们就不再关心连续的掷骰。

许多阶段可以重复使用并链接在一起。例如,GamePhase CommunityChestAdvanceToGoPlayerLandsOnGo使用当前状态创建下一个阶段并返回它。在PlayerLandsOnGo当前玩家的构造函数中,他们将被转到Go,他们的钱将增加$ 200。


8

当然,有关此主题的资源很多很多。但是我认为您在正确的方法上划分对象,让它们处理自己的事件/数据等等。

在进行基于图块的棋盘游戏时,您会发现有例程可以在棋盘阵列与行/列之间以及向后以及其他功能之间进行映射。我还记得我很久以前的第一个棋盘游戏,当时我苦苦挣扎着如何从boardarray 5获取行/列。

1  2  3  
4 (5) 6  BoardArray 5 = row 2, col 2
7  8  9  

怀旧。;)

无论如何,http://www.gamedev.net/是一个提供信息的好地方。 http://www.gamedev.net/reference/


您为什么不只使用二维数组?然后,编译器可以为您处理此问题。
Jay Bazuzi

我的借口是这是很久以前的事了。;)
Stefan

1
gamedev有很多东西,但是我没有看到想要的东西。
杰伊·巴祖兹

您使用什么语言?
zotherstupidguy

基本,Basica,QB,QuickBasic等。;)
Stefan


3

三环提供LGPL的Java库。Nenya和Vilya是游戏相关资料的库。

当然,如果您的问题提到平台和/或语言限制,可能会有所帮助。


“最终我希望构建一个WPF UI”-这意味着.NET。至少,据我所知。
Mark Allen

我不知道的字母汤。
jmucchiello

是的,我正在使用.NET,但是我的问题不是特定于语言或平台的。
杰伊·巴祖兹

2

我同意Pyrolistical的回答,并且我更喜欢他的处事方式(不过我只是略过了其他答案)。

巧合的是,我还使用了他的“ GamePhase”命名。基本上,在基于回合的棋盘游戏中,我要做的是让GameState类包含Pyrolistical提到的抽象GamePhase对象。

可以说游戏状态为:

  1. 移动
  2. 买/不买
  3. 监狱

您可以为每个状态提供具体的派生类。至少具有以下虚拟功能:

StartPhase();
EndPhase();
Action();

在StartPhase()函数中,您可以设置状态的所有初始值,例如,禁用其他玩家的输入,依此类推。

调用roll.EndPhase()时,请确保将GamePhase指针设置为下一个状态。

phase = new MovePhase();
phase.StartPhase();

例如,在此MovePhase :: StartPhase()中,将活动玩家的剩余移动设置为上一阶段的滚动量。

现在,有了这种设计,您就可以在滚动阶段解决“ 3 x double = jail”的问题。RollPhase类可以处理自己的状态。例如

GameState state; //Set in constructor.
Die die;         // Only relevant to the roll phase.
int doublesRemainingBeforeJail;
StartPhase()
{
    die = new Die();
    doublesRemainingBeforeJail = 3;
}

Action()
{
    if(doublesRemainingBeforeJail<=0)
    {
       state.phase = new JailPhase(); // JailPhase::StartPhase(){set moves to 0};            
       state.phase.StartPhase();
       return;
    }

    int die1 = die.Roll();
    int die2 = die.Roll();

    if(die1 == die2)
    {
       --doublesRemainingBeforeJail;
       state.activePlayer.AddMovesRemaining(die1 + die2);
       Action(); //Roll again.
    }

    state.activePlayer.AddMovesRemaining(die1 + die2);
    this.EndPhase(); // Continue to moving phase. Player has X moves remaining.
}

我与《 Pyrolistical》的不同之处在于,应该为所有内容提供一个阶段,包括玩家何时落在社区胸前或什么地方。我将在MovePhase中处理所有这一切。这是因为如果您有太多的连续阶段,则播放器很可能会感到“被引导”。例如,如果有一个阶段,玩家只能购买财产,然后只能购买酒店,然后只能购买房屋,那就像没有自由一样。只需将所有这些零件放入一个BuyPhase中,就可以让玩家自由购买他想要的任何东西。BuyPhase类可以轻松地处理合法的购买。

最后,让我们来谈谈游戏板。尽管2D数组很好,但我还是建议您使用一个图块图(图块在板上的位置)。在垄断的情况下,它宁愿是双链表。然后每个磁贴都会有一个:

  1. previousTile
  2. nextTile

因此,执行以下操作会容易得多:

While(movesRemaining>0)
  AdvanceTo(currentTile.nextTile);

AdvanceTo函数可以处理您的分步动画或任何您喜欢的动画。当然也会减少剩余的动作。

RS Conley针对GUI的观察者模式的建议很好。

我还没发布太多。希望这对某人有帮助。


1

我可以利用某些现有技术吗?

如果您的问题不是特定于语言或平台的。那么我建议您考虑针对状态,备忘录,命令等的AOP模式。

.NET对AOP的回答是什么?

同时尝试找到一些很酷的网站,例如http://www.chessbin.com

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.