这就是我设计 Monopoly 的方式。我冒昧地假设了一种动态类型的语言,因为这使一切变得更容易。特别是 Ruby。
你有一个简单的 Game 对象,它主要是一个大小为 40 的 Array 的包装器,以及一些方便的方法。 Game 对象还跟踪可用的houses 和hotels 的数量以及两叠机会卡和公益金卡。提供了一些方便的方法,如current_turn 和next_turn! — 都返回一个Player 对象; next_turn! 增加转弯索引,必要时回绕到 0。
玩家可以登陆的所有位置都必须继承自 Property 的超类。 Property 类定义了一些常见的东西,例如 rent、owner、set、houses、purchasable? 和 upgradeable?。 rent 和 owner 属性可能是 nil。 set 属性返回包含组内所有属性的Array。 set 属性的大小可能从 1 到 4 不等。houses 属性将酒店表示为 5 个“房屋”。
Game 对象有一个 Array 的 Player 对象,每个对象都有 position(从 0 到 39 的整数)、money(没有上限——银行从技术上讲永远不会“耗尽”)钱'),get_out_of_jail_frees 和 in_jail?(因为位置不足以完成此操作)。 Game 对象也有一个索引来跟踪轮到谁了。
属性特定的规则都被编码在它们各自的子类中。因此,例如,rent 在Railroad 上的实现将是:
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