OOD中的垄断游戏?

Posted

技术标签:

【中文标题】OOD中的垄断游戏?【英文标题】:Monopoly game in OOD? 【发布时间】:2011-06-02 08:29:50 【问题描述】:

我通过 CodingHorror 发现了这篇有趣的博文:My Favorite Interview Question。简而言之,他谈到了设计大富翁游戏的面向对象设计挑战,重点是如何对游戏规则进行建模。例如,“如果玩家拥有波罗的海大道,她可以在其中添加房子吗?”

有趣的是,在帖子底部附近,他写道:

您或许可以为自己节省很多面试时间。而不是所有这些喧嚣,请候选人描述他们何时实际使用了框架之外的策略、访问者和命令模式。)

...这可能意味着您可以使用设计模式来模拟游戏规则(见上文)。有没有人这样做过?使用设计模式设计大富翁游戏?如果有,效果如何?

【问题讨论】:

【参考方案1】:

这就是我设计 Monopoly 的方式。我冒昧地假设了一种动态类型的语言,因为这使一切变得更容易。特别是 Ruby。

你有一个简单的 Game 对象,它主要是一个大小为 40 的 Array 的包装器,以及一些方便的方法。 Game 对象还跟踪可用的houseshotels 的数量以及两叠机会卡和公益金卡。提供了一些方便的方法,如current_turnnext_turn! — 都返回一个Player 对象; next_turn! 增加转弯索引,必要时回绕到 0。

玩家可以登陆的所有位置都必须继承自 Property 的超类。 Property 类定义了一些常见的东西,例如 rentownersethousespurchasable?upgradeable?rentowner 属性可能是 nilset 属性返回包含组内所有属性的Arrayset 属性的大小可能从 1 到 4 不等。houses 属性将酒店表示为 5 个“房屋”。

Game 对象有一个 ArrayPlayer 对象,每个对象都有 position(从 0 到 39 的整数)、money(没有上限——银行从技术上讲永远不会“耗尽”)钱'),get_out_of_jail_freesin_jail?(因为位置不足以完成此操作)。 Game 对象也有一个索引来跟踪轮到谁了。

属性特定的规则都被编码在它们各自的子类中。因此,例如,rentRailroad 上的实现将是:

def rent
  owned_count = self.set.select  |rr| rr.owner == self.owner .size
  return 25 * 2 ** (owned_count - 1)
end

Chance 和 Community Chest 卡可以简单地使用一堆闭包来实现,这些闭包将游戏和玩家对象作为参数。例如:

# Second place in a beauty contest
COMMUNITY_CHEST_CARDS << lambda do |game, player|
  player.money += 10
end

# Advance token to Boardwalk
CHANCE_CARDS << lambda do |game, player|
  game.advance_token!(player, 39)
end

# Advance token to nearest railroad, pay double
CHANCE_CARDS << lambda do |game, player|
  new_position = [5, 15, 25, 35].detect do |p|
    p > player.position
  end || 5
  game.advance_token!(player, new_position)
  # Pay rent again, no-op if unowned
  game.properties[new_position].pay_rent!(player)
end

等等。 advance_token! 方法显然可以处理诸如传递 go 之类的事情。

显然,还有更多细节——这是一个相当复杂的游戏,但希望这能给你正确的想法。面试肯定绰绰有余。

更新

可以通过将house_rules Array 添加到Game 对象来打开或关闭房屋规则。这将允许像这样实现 FreeParking 属性:

class Game
  def house_rules
    @house_rules ||= []
  end

  def kitty
    # Initialize the kitty to $500.
    @kitty ||= 500
  end

  def kitty=(new_kitty)
    @kitty = new_kitty
  end
end

class FreeParking < Property
  def rent
    if self.game.house_rules.include?(:free_parking_kitty)
      # Give the player the contents of the kitty, and then reset it to zero.
      return -(_, self.game.kitty = self.game.kitty, 0)[0]
    else
      return 0
    end
  end
end

【讨论】:

哇!经过深思熟虑和构建。 如果我没看错的话,作者的主要关注点是几乎没有两个人按照相同的规则玩垄断,因为每个人都有不同的家规,他们习惯了。在您的架构中,更改规则集不会需要在许多不同的地方进行大量的小改动吗? 最常见的房屋规则——通过免费停车赚钱——可以通过为FreeParking 属性添加rent 方法的实现并返回一个负数来轻松实现。这是一个地方的一个小变化。您可以轻松添加分支逻辑来打开和关闭房屋规则。 更新了我的答案,提供了有关如何执行此操作的更多信息。 我认为使用 OP 链接中建议的一些模式的目的是避免做你所做的事情,即通过添加另一个属性来修改 Game 对象并显式修改 FreeParking 的rent 方法。基本上,您希望能够在不修改任何这些对象的情况下添加规则(开闭原则)。【参考方案2】:

我认为你在这里走错路了。

...which probably means that you can use design patterns to model the rules of the game (see above). 

我认为这只是表明您并不真正了解设计模式是什么。已知的设计模式只是我们在编码时给经常出现的情况命名。在你的日常生活中,你永远不会说“我早上 8 点起床去早上 9 点放置 X,整天编程直到下午 5 点,所以他们在月底之前付钱给我”。你说,“今天我去上班了”。您有想赚钱的问题,并且该问题的反复解决方案将起作用。所以......我们在这里有一个模式!让我们称之为“工作”!

设计模式只是针对常见问题的一组经过研究的解决方案。这些解决方案中的每一个都有一个关联的名称(策略、访问者等)。

回来

...which probably means that you can use design patterns to model the rules of the game 

这并不意味着您可以使用设计模式来模拟游戏规则,这意味着无论您在解决方案中做什么,它都可能会落入一些已知的设计模式。与必须从头开始描述所有内容相比,将您的解决方案视为一组相互关联的模式更容易。

【讨论】:

在哲学上是正确的,但实际上并不是那么简单。 DP 的主要挑战是在问题中识别它们并在解决方案中正确应用它们(在现实生活中相同,顺便说一句,当问题稍微不常见时“去工作”)。这个问题可能应该改写为“可以应用哪些已知的设计模式来简化这个问题的解决方案?” 这个问题可能应该改写为“可以应用哪些已知的设计模式来简化这个问题的解决方案?”我同意。只是在 SO 上看到人们对设计模式完全感到困惑是很常见的,就好像它们是目标(而不是达到目的的手段)..【参考方案3】:

我从来没有设计过大富翁规则(太简单了,我想),但我曾涉足为其他知名游戏编写引擎以供个人娱乐,并理解所有这一切都是一项学术活动。

我尝试建模(并继续尝试)的两个游戏是D&D 和M:tG。

对于 D&D,重点是非常好的 OO 设计 - 使类和类层次结构有意义。

使用 M:tG,您基本上意识到直接的 OO 范式对于这类事情是不完整的。您最终会与代理、事件代理合作,并创建非常复杂的规则集。

除非您是游戏设计师,否则这一切都毫无意义。不过很好玩。

【讨论】:

以上是关于OOD中的垄断游戏?的主要内容,如果未能解决你的问题,请参考以下文章

JAVA中的OOA.OOD.OOP有啥区别?

管理经济学第九章(垄断市场中的企业决策)

面向对象设计(OOD)七大原则

域外动态||被欧盟处罚后,Steam又在美国被提起反垄断诉讼

Epic告完苹果还不够,又指责Google和苹果相互勾结垄断市场

热讯 | 人气漫改真人版电影《银魂2》即将上线;谷歌遭韩国反垄断调查;腾讯上线“NextSandbox”悬念站