3

作为我的 CS 教育的一部分,我正在构建一个多人 Android yatzy 游戏,客户端通过基于 Ruby/EventMachine 的 TCP 服务器链接在一起,JSON 消息表示来回传递的游戏事件。

但是,我对如何以最佳方式处理游戏回合管理感到不确定。

一个典型的 yatzy 游戏包括 15 轮。我的实现将处理多达 4 个玩家。掷骰子、掷骰子和得分选择等事件被发送到 Ruby 服务器并广播给其他玩家。

目前,我的服务器正在处理游戏回合。每次从客户端接收到新的分数选择时,它都会将分数广播给其他客户端。随后,它会广播一条消息,其中包含下一个掷骰子的玩家的用户 ID。

我希望我的系统能够处理玩家退出而不结束剩余玩家的游戏,这是一个严峻的副作用。我想出了一个解决方案,但我不确定它是否理想。

@turnfiber = Fiber.new do
  15.times do
    @players.each do |key, value|
      Fiber.yield value
    end
  end
end

@turnfiber是属于代表正在运行的游戏的游戏对象的实例变量。@players是一个散列,它使用玩家的唯一 ID 作为键,并使用相应的玩家对象作为值。

@turnfiber.resume每次回合结束时(通过分数选择提交)调用以检索下一位掷骰子的玩家并广播他的掷骰许可。这个想法是,如果玩家在第 4 回合离开游戏,他的客户端将发送退出消息,将离开的玩家从@players散列中删除,广播他的离开,并且由于玩家不再驻留在@players散列中,阻止后续迭代从将骰子控制权交给“死”玩家,从而避免僵局。到目前为止,我的 Android 客户端还不完整,所以我还没有测试过这个理论在实践中是否真的有效。

我选择 Fiber 类的原因是希望能够迭代 15 次@players并让他们一次掷一个骰子。Fibers 使这成为可能,因为它们每次yield调用都会暂停循环并返回播放器。

我想听听您对这种方法的看法,特别是它有哪些弱点,以及您认为我应该考虑哪些替代方案来解决这个转弯管理问题。

4

1 回答 1

1

Ruby 有Enumerators,这是一种有限形式的协程。他们是这样工作的:

infinite_set = Enumerator.new do |yielder|
  i = 0
  loop do
    yielder.yield(i += 1)
  end
end

puts infinite_set.next
puts infinite_set.next
puts infinite_set.next

# Output:
# 1
# 2
# 3

枚举器允许外部迭代、列表的惰性求值以及函数的多个入口/出口点。如果您深入了解,您将看到 Ruby 使用纤维实现它们。

我对您的原始代码的看法是您想要这样的东西:

(1..15).each do |round|
  # round code
  players.each do |player|
    next unless player.active?
    # do network IO with the player object
    # if the player times out or drops, change the player active state
  end
end

我认为添加另一个光纤会增加不必要的复杂性,除非您的服务器同时运行多个游戏,或者需要某种进程内后台队列。在那种情况下,fibers 和 Eventmachine 会很棒。

于 2012-10-19T12:49:24.030 回答