用于防止玩家获得相同卡牌的算法

我正在 Tabletop Simulator 中编写一个游戏,所有玩家(P)都会被发给一张卡牌(C)。一旦记住卡牌,所有玩家把卡牌放回卡组(D),洗牌,然后所有玩家从相同的卡组(D)中获得其中一张卡牌。我试图编写最简单的算法,防止玩家获得自己的卡牌。现在,当涉及到编码时,我认为这应该很简单,而不是创建模拟,一直运行,直到成功为止。

点赞
用户589924
用户589924

假设你有以下内容:

  • deck,一个包含所有牌(包括玩家已经看到的牌)的随机化牌堆。
  • seen_card_id_by_player,一个查询表格,可以给你玩家所看到的牌的标识符。

那么解决方案就是简单的:

local card_ids = {}
for i, card_data in ipairs(deck.getObjects()) do
   table.insert(card_ids, card_data.guid)
end

for player, seen_card_id in pairs(seen_card_id_by_player) do
   local card_id = table.remove(card_ids)

   if card_id == seen_card_id then
      local i = math.random(1, #card_ids)
      card_ids[i], card_id = card_id, card_ids[i]
   end

   -- Deal the specific card.
   deck.takeObject({
      guid     = card_ids[i],
      position = player.getHandTransform().position,
      flip     = true,
   })
end

当我们挑选玩家已经看过的牌时,它会被放回到剩余牌中随机位置。这确保了每一张牌都有相同的机会被下一个玩家抽到。这是Fisher-Yates shuffle的基本原理。


完整演示

function broadcast_error(msg)
   broadcastToAll(msg, { r=1, g=0, b=0 })
end

function get_cards_seen_by_players()
   local player_ids = Player.getAvailableColors()

   local error = false
   local seen_card_by_player = {}
   for i, player_id in ipairs(player_ids) do
      local player = Player[player_id]
      local hand_objs = player.getHandObjects()
      local player_error = false
      if #hand_objs > 1 then
         player_error = true
      elseif #hand_objs == 1 then
         local card = hand_objs[1]
         if card.tag ~= "Card" then
            player_error = true
         else
            seen_card_by_player[player] = card
         end
      end

      if player_error then
         broadcast_error(player_id .. " doesn't have a valid hand.")
         error = true
      end
   end

   if error then
      return nil
   end

   return seen_card_by_player
end

function run()
   local deck = getObjectFromGUID("...")

   local seen_card_by_player = get_cards_seen_by_players()
   if seen_card_by_player == nil or next(seen_card_by_player) == nil then
      return
   end

   local seen_card_id_by_player = {}
   for player, card in pairs(seen_card_by_player) do
      local card_id = card.guid
      seen_card_id_by_player[player] = card_id
      card.putObject(deck)
   end

   deck.randomize()

   local card_ids = {}
   for i, card_data in ipairs(deck.getObjects()) do
      table.insert(card_ids, card_data.guid)
   end

   for player, seen_card_id in pairs(seen_card_id_by_player) do
      local card_id = table.remove(card_ids)

      if card_id == seen_card_id then
         local i = math.random(1, #card_ids)
         card_ids[i], card_id = card_id, card_ids[i]
      end

      deck.takeObject({
         guid     = card_ids[i],
         position = player.getHandTransform().position,
         flip     = true,
      })
   end
end

创建一个带有一副牌的游戏。将上述代码放在全局变量中,将...替换为牌堆的 GUID。为了演示,向任意数量的玩家发一张牌,然后在聊天框中使用/execute Global.call("run")来运行。

2020-08-11 10:17:15