Як перетасувати картки для карткової гри?


13

Я намагаюся розробити карткову гру для Android Чи може хто-небудь підказати мені, як написати код для ефективного переміщення гральних карт?

Відповіді:


21

Переміщення карт - алгоритм, який легко писати інтуїтивно, і абсолютно помилятися, роблячи це. Існує хороша довідка про те, щоб правильно перетасувати картку в Вікіпедії . Я представляю тут дуже злегка спрощену версію алгоритму, описану на цій сторінці в розділі Сучасний алгоритм .

Ось основна ідея простою англійською мовою:

Розгляньте колоду карт. Для цього обговорення ви можете мати будь-яку кількість карток у колоді, і вони можуть розпочатись у будь-якому порядку.

Ми будемо говорити про "позицію" в колоді, де "позиція" - це на скільки карт більше на колоді, ніж карта в цій позиції. Наприклад, карта на вершині колоди знаходиться в положенні 0, карта під нею знаходиться в позиції 1 (тому що на 1 карту вище за неї - верхня карта), а в стандартній колоді на 52 картки - нижня карта знаходиться в положенні 51, так як 51 карт вище, ніж у колоді.

Тепер ми розглядаємо кожну позицію на палубі, одну за одною, починаючи знизу, і проправляємося до верху.

Для кожної позиції ми випадковим чином вибираємо одну з карток, яка знаходиться в цій позиції або в нумерованій позиції (пам’ятайте, верхня частина колоди дорівнює 0, і ми працюємо вгору від нижньої частини колоди, тому для кожної позиції ви ефективно збираєте всі картки на цій і вище позиції та випадково вибираєте одну з цих карт).

Коли ми здійснили випадковий вибір, ми поміняємо картку на місце, яке ми зараз розглядаємо, карткою, яку ми вибрали випадковим чином. Якщо ми випадковим чином обрали карту, яка вже була в такому положенні, то жодний своп не проводиться.

Після заміни (або не замінивши, якщо ми випадковим чином обрали карту, яка вже була у позиції, яку ми розглядали), переходимо до наступної позиції на колоді і продовжуємо.

У псевдокоді з п є кількість карт в колоді, і бути масивом , який представляє колоду, алгоритм виглядає наступним чином :

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). Щоб вивести наступну карту з колоди, ви мали б змінну індексу, яка починалася з нуля, слід зашифрувати цей індекс, збільшити індекс і подивитися на значення, яке вийшло з шифру. Якщо значення> = 52, ви ігноруєте його і генеруєте нове число (і, природно, знову збільшуєте індекс). Оскільки шифрування 0-63 призведе до 0-63 як вихід, в іншому порядку ви просто ігноруєте будь-яке значення, яке виходить> = 52, так що у вас є ваш алгоритм, який займає 0-51, і випиває 0-51.

Щоб змінити розміщення колоди, встановіть індекс назад до нуля та змініть ключ шифрування (перетасувати насіння).

Ваш алгоритм не потребує криптографічної якості (і цього не повинно бути, тому що це обчислювально дорого!). Один з дійсно хороших способів розробити алгоритм шифрування на замовлення, наприклад, це використання мережі feistel, яка дозволяє вам налаштувати розмір та якість залежно від ваших потреб. Для круглої функції мережі feistel я б рекомендував щось на кшталт murmurhash3, оскільки це швидко і має хороший лавиноподібний ефект, завдяки чому перетасування виглядатимуть добре рандомізованими.

Перегляньте мій пост у блозі для отримання більш детальної інформації та вихідного коду: http://blog.demofox.org/2013/07/06/fast-lightweight-random-shuffle-functionality-fixed/


Ця відповідь, як це зараз формулюється, не дуже допомагає, коли URL-адреса неминуче падає з поверхні Інтернету. Розглянемо детальніше у відповіді про важливі моменти пов'язаної статті, щоб відповідь могла стояти самостійно.
Ларс Віклунд

1
Хороша точка Ларса, оновлена ​​додатковою інформацією, щоб читач міг принаймні шукати додаткову інформацію про всі конкретні компоненти рішення про перетасування карт, використовуючи шифрування формату, що зберігає. Спасибі!
Алан Вулф

1

Навчальний підручник java 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;
     }
}

0

Просто використовуйте таку функцію, як itertools, як там на Python. Мені невідомо про назву тієї самої функції в Java, спробуйте ". Http://code.google.com/p/neoitertools/ "

Дізнайтеся всі перестановки об'єкта під назвою "картки"


-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
Відповіді, що стосуються лише коду, не враховують.
SurvivalMachine
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.