Tabletop Simulator防玩家拿到自身卡牌的最简算法实现问询
Got it, let's break this down. What you need is a derangement—a permutation of your card set where no player gets their original card. No more looping until you get a valid shuffle; we can do this deterministically in a couple of simple ways, perfect for your Tabletop Simulator workflow.
Core Idea
Your problem boils down to rearranging the returned cards so that every player receives a card that wasn't theirs initially. Here are two minimal, efficient approaches:
1. Circular Shift (Simplest, Fixed Derangement)
This is the easiest implementation by far, great if you don't need ultra-randomness (though you can shuffle the deranged set afterward if you want).
- How it works: Line up all players in order, then each player gets the card from the next player in the line. The last player wraps around to get the first player's original card.
- Example: If players are [P1, P2, P3], P1 gets P2's card, P2 gets P3's, P3 gets P1's. No one gets their own card, guaranteed.
- Lua Code (for Tabletop Simulator):
-- Assume we have a table 'players' where each entry has an 'originalCard' property local playerCount = #players -- Create the deranged card assignment for i = 1, playerCount do -- Calculate the index of the player whose card we'll give to current player local targetPlayerIndex = (i % playerCount) + 1 players[i].assignedCard = players[targetPlayerIndex].originalCard end -- Optional: Shuffle the assigned cards if you want random order (still no self-draws) -- Then deal the assignedCard to each player
2. Random Derangement (More Random, Still Deterministic)
If you want a less predictable derangement than a fixed shift, use this method to generate a random permutation and fix any self-draws in one pass:
- How it works:
- Generate a random permutation of card indices for players.
- Check for any "fixed points" (players assigned their own card).
- For each fixed point, swap it with another player's assignment—ensuring the swap doesn't create a new fixed point.
- Lua Code (for Tabletop Simulator):
local playerCount = #players local permutation = {} -- Step 1: Generate a random permutation of indices (1 to playerCount) for i = 1, playerCount do table.insert(permutation, i) end -- Fisher-Yates shuffle to make it random for i = playerCount, 2, -1 do local j = math.random(i) permutation[i], permutation[j] = permutation[j], permutation[i] end -- Step 2: Fix any fixed points (self-draws) local fixedPoints = {} for i = 1, playerCount do if permutation[i] == i then table.insert(fixedPoints, i) end end -- Swap fixed points with each other (or a non-fixed point if only one exists) for idx = 1, #fixedPoints do local current = fixedPoints[idx] -- Find a swap target: either another fixed point, or any non-fixed index local swapTarget = nil if idx < #fixedPoints then swapTarget = fixedPoints[idx + 1] else -- Find first non-fixed index for i = 1, playerCount do if permutation[i] ~= i then swapTarget = i break end end end -- Swap the assignments permutation[current], permutation[swapTarget] = permutation[swapTarget], permutation[current] end -- Now assign cards based on the fixed permutation for i = 1, playerCount do players[i].assignedCard = players[permutation[i]].originalCard end
Why This Beats Repeated Simulation
Instead of shuffling over and over until you get a valid deal (which could take multiple tries, especially with small player counts), these methods give you a valid derangement in a single pass. The circular shift is O(n) and requires zero randomness checks, while the random derangement is O(n) with a tiny bit of extra logic for fixing fixed points.
内容的提问来源于stack exchange,提问作者Joe

