카드 게임용 카드를 섞는 방법은 무엇입니까?


13

Android 용 카드 게임을 개발하려고합니다. 누구나 카드 놀이를 효과적으로 섞을 수있는 코드를 작성하는 방법을 제안 할 수 있습니까?

답변:


21

카드 셔플 링은 직관적으로 작성하기 쉬운 알고리즘이며 그렇게함으로써 완전히 잘못 될 수 있습니다. Wikipedia에서 카드 셔플 링을 올바르게 구현하기위한 좋은 참고 자료가 있습니다 . 내가 여기에 제시하는 것은 해당 페이지에서 다루는 알고리즘의 약간 단순화 된 버전입니다. The modern algorithm .

기본 아이디어는 다음과 같습니다.

한 벌의 카드를 고려하십시오. 이 토론을 위해, 당신은 덱에 몇 장의 카드를 가질 수 있으며, 어떤 순서로든 시작할 수 있습니다.

우리는 덱에서 "포지션"에 대해 이야기 할 것입니다. 여기서 "포지션"은 그 위치에있는 카드보다 덱에 몇 개의 카드가 더 많은가입니다. 예를 들어, 덱 맨 위의 카드는 위치 0에 있고, 그 아래의 카드는 1 번 위치에 있습니다 (맨 위의 카드보다 1 카드가 높기 때문에). 카드는 51 장의 카드가 덱의 카드보다 높기 때문에 51 번 위치에 있습니다.

이제 우리는 갑판에서 각 위치를 한 번에 하나씩 바닥에서 시작하여 위쪽으로 진행하는 것을 고려합니다.

각 위치마다 해당 위치 또는 번호가 낮은 위치 에있는 카드 중 하나를 무작위로 선택합니다. 합니다 (데크의 상단이 0이고 덱의 맨 아래에서 작업하고 있음을 기억하십시오. 각 위치마다 해당 위치 이상에서 모든 카드를 효과적으로 집어 들고 해당 카드 중 하나를 무작위로 선택합니다.

무작위 선택을하면, 현재 고려중인 위치에서 무작위로 선택한 카드로 카드를 교환합니다. 해당 위치에있는 카드를 무작위로 선택한 경우 스왑이 수행되지 않습니다.

스왑 후 (또는 스왑하지 않은 경우, 이미 고려중인 위치에있는 카드를 무작위로 선택한 경우), 갑판에서 다음 위치로 이동하여 계속합니다.

의사 코드와 N 갑판의 장수되고, 그리고 A는 갑판, 이런 알고리즘 모습을 나타내는 배열 인 :

for each i in [n .. 1] do
     j  random integer in [ 0 .. i ]
     exchange a[j] and a[i]

1
이 알고리즘은 다음과 같이 잘 시각화되어 있습니다 : bost.ocks.org/mike/algorithms/#shuffling
Felsir

9

먼저 셔플하려는 모든 카드의 순서를 정의하십시오.

List<Card> shuffled = new ArrayList<Card>();
shuffled.addAll(allCards);

그런 다음 시퀀스의 모든 위치를 훑어보고 무작위로 카드를 할당합니다.

Random random = new Random();
for (int i = shuffled.size() - 1; i >= 0; i--) {
    int j = random.nextInt(i + 1);

    /* swap cards i,j */
    Card card = shuffled.get(i);
    shuffled.set(i, shuffled.get(j));
    shufflet.set(j, card);
}

이제 shuffled모든 카드의 무작위 순서입니다.


3
이것은 크 누스의 셔플로 알려져있다 : en.wikipedia.org/wiki/Knuth_shuffle
krolth

2

게임에서 카드를 섞는 방법으로 "포맷 보존 암호화"를 언급하고 싶습니다.

본질적으로 당신이 가지고있는 것은 0에서 51 사이의 값을 취하는 암호화 알고리즘과 키 (셔플 시드)는 0에서 51 사이의 값을 뱉어냅니다. 암호화는 정의에 의해 되돌릴 수 있기 때문에 2 개의 입력 숫자는 암호화 할 수 없습니다. 동일한 출력 번호, 즉 0에서 51을 암호화하면 다른 순서로 출력으로 0에서 51을 얻게됩니다. 다시 말해, 당신은 셔플을 가지고 있고 실제 셔플을 할 필요조차 없습니다.

이 경우 6 비트를 가져와 6 비트 (0-63)를 내뿜는 암호화 알고리즘을 만들거나 찾아야합니다. 데크에서 다음 카드를 가져 오려면 0에서 시작하는 색인 ​​변수가 있어야합니다. 색인을 암호화하고 색인을 증가시키고 암호에서 나온 값을보십시오. 값이> = 52이면 값을 무시하고 새 숫자를 생성합니다 (물론 인덱스를 다시 증가시킵니다). 0-63을 암호화하면 0-63이 다른 순서로 출력되므로 결과는> = 52로 나오는 값을 무시하므로 0-51을 사용하고 0-51을 내뿜는 알고리즘을 갖습니다.

데크를 다시 섞으려면 색인을 다시 0으로 설정하고 암호화 키 (셔플 시드)를 변경하십시오.

알고리즘은 암호화 품질 일 필요는 없습니다 (그리고 계산 비용이 많이 드는 원인이되어서는 안됩니다!). 이와 같은 사용자 지정 크기의 암호화 알고리즘을 제시하는 좋은 방법 중 하나는 필요에 따라 크기와 품질을 사용자 지정할 수있는 feistel 네트워크를 사용하는 것입니다. feistel 네트워크의 라운드 기능의 경우 murmurhash3과 같은 것이 좋습니다. 빠르고 눈사태 효과가 좋기 때문에 셔플이 무작위로 잘 나타납니다.

더 자세한 정보 및 소스 코드는 내 블로그 게시물을 확인하십시오. http://blog.demofox.org/2013/07/06/fast-lightweight-random-shuffle-functionality-fixed/


현재 말한이 답변은 필연적으로 인터넷 표면에서 URL이 떨어질 때별로 도움이되지 않습니다. 링크 된 기사의 주요 요점에 대한 답변을 정교하게 작성하여 답변이 독자적으로 설 수 있도록하십시오.
Lars Viklund

1
좋은 점 Lars는 더 많은 정보로 업데이트되었으므로 독자는 형식 보존 암호화를 사용하여 카드 셔플 링에 대한 솔루션의 모든 특정 구성 요소에 대한 자세한 정보를 찾을 수 있습니다. 감사!
Alan Wolfe

1

자바 1.5 열거 튜토리얼 , 갑판을 구축 셔플 및 거래, 카드의 갑판을 구현하는 흥미로운 방법이 있습니다. enums를 사용하여 매우 간단 하고Collections

public class Card {
    public enum Rank { DEUCE, THREE, FOUR, FIVE, SIX,
        SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE }

    public enum Suit { CLUBS, DIAMONDS, HEARTS, SPADES }

    private final Rank rank;
    private final Suit suit;
    private Card(Rank rank, Suit suit) {
        this.rank = rank;
        this.suit = suit;
    }

    public Rank rank() { return rank; }
    public Suit suit() { return suit; }
    public String toString() { return rank + " of " + suit; }

    private static final List<Card> protoDeck = new ArrayList<Card>();

    // Initialize prototype deck
    static {
        for (Suit suit : Suit.values())
            for (Rank rank : Rank.values())
                protoDeck.add(new Card(rank, suit));
    }

    public static ArrayList<Card> newDeck() {
        return new ArrayList<Card>(protoDeck); // Return copy of prototype deck
    }
}

그리고 갑판을 관리하는 수업.

public class Deal {
    public static void main(String args[]) {
        int numHands = Integer.parseInt(args[0]);
        int cardsPerHand = Integer.parseInt(args[1]);
        List<Card> deck  = Card.newDeck();
        Collections.shuffle(deck);
        for (int i=0; i < numHands; i++)
            System.out.println(deal(deck, cardsPerHand));
    }

    public static ArrayList<Card> deal(List<Card> deck, int n) {
         int deckSize = deck.size();
         List<Card> handView = deck.subList(deckSize-n, deckSize);
         ArrayList<Card> hand = new ArrayList<Card>(handView);
         handView.clear();
         return hand;
     }
}


-2
    ArrayList deckCards = new ArrayList<Card>();
    //add your cards to the deck
    deckCards.add(card1);
    deckCards.add(card2);
    deckCards.add(card3);
    ....
    //shuffle the array list
    Collections.shuffle(deckCards);

1
코드 전용 답변은 권장하지 않습니다.
서바이벌 머신
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.