¿Cómo barajo cartas para un juego de cartas?


13

Estoy tratando de desarrollar un juego de cartas para Android. ¿Alguien puede sugerirme cómo escribir código para barajar las cartas de manera efectiva?

Respuestas:


21

El barajado de tarjetas es un algoritmo que es fácil de escribir intuitivamente y se equivoca completamente al hacerlo. Hay una buena referencia para implementar barajar cartas correctamente en Wikipedia . Lo que estoy presentando aquí es una versión muy simplificada del algoritmo cubierto en esa página bajo El algoritmo moderno .

Aquí está la idea básica, en inglés simple:

Considere una baraja de cartas. Para esta discusión, puede tener cualquier cantidad de cartas en el mazo, y pueden comenzar en cualquier orden.

Vamos a hablar de "posición" en el mazo, donde "posición" es cuántas cartas son más altas en el mazo que la carta en esa posición. Por ejemplo, la carta en la parte superior del mazo está en la posición 0, la carta debajo está en la posición 1 (porque hay 1 carta más alta que la carta superior), y en una baraja estándar de 52 cartas, la inferior la carta está en la posición 51, ya que 51 cartas son más altas que en el mazo.

Ahora, consideramos cada posición en el mazo, una a la vez, comenzando desde abajo y avanzando hacia arriba.

Para cada posición, seleccionamos al azar una de las cartas que está en esa posición o en una posición con un número más bajo (recuerde, la parte superior del mazo es 0, y estamos trabajando hacia arriba desde la parte inferior del mazo, así que para cada posición, efectivamente está recogiendo todas las cartas en esa posición y por encima de esa posición y escogiendo al azar una de esas cartas).

Cuando hemos hecho la selección aleatoria, cambiamos la tarjeta en la posición que estamos considerando actualmente con la tarjeta que seleccionamos al azar. Si seleccionamos al azar la tarjeta que ya estaba en esa posición, no se realiza ningún intercambio.

Después de intercambiar (o no intercambiar, si seleccionamos al azar la carta que ya estaba en la posición que estábamos considerando), pasamos a la siguiente posición en el mazo y continuamos.

En pseudocódigo, con n es el número de cartas en la baraja, y un ser una matriz que representa la cubierta, las miradas algoritmo de este tipo:

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

1
El algoritmo también se visualiza muy bien aquí: bost.ocks.org/mike/algorithms/#shuffling
Felsir

9

Primero define una secuencia de todas las cartas que desea barajar:

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

Luego recorres cada posición de la secuencia y le asignas una carta al azar.

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);
}

Ahora shuffledes una secuencia aleatoria de todas tus cartas.


3
esto se conoce como la barajadura de Knuth: en.wikipedia.org/wiki/Knuth_shuffle
krolth

2

Me gustaría intervenir y mencionar el "cifrado de preservación de formato" como método para barajar cartas en un juego.

Esencialmente, lo que tendría es un algoritmo de cifrado que toma un valor de 0 a 51, y una clave (reproducción aleatoria) y escupe un valor de 0 a 51. Dado que el cifrado es reversible por definición, eso significa que 2 números de entrada no pueden cifrar a el mismo número de salida, lo que significa que si cifra 0 a 51, obtendrá 0 a 51 como salida en un orden diferente. En otras palabras, tienes tu barajadura y ni siquiera necesitas hacer ninguna barajadura real.

En este caso, tendría que crear o encontrar un algoritmo de cifrado que tomara 6 bits y escupiera 6 bits (0-63). Para sacar la siguiente carta del mazo, tendría una variable de índice que comenzó en cero, cifraría ese índice, incrementaría el índice y vería el valor que salió del cifrado. Si el valor es> = 52, lo ignora y genera un nuevo número (e incrementa el índice nuevamente, por supuesto). Dado que cifrar 0-63 dará como resultado 0-63 como salida, en un orden diferente, simplemente está ignorando cualquier valor que salga> = 52 para que tenga su algoritmo que tome 0-51 y escupe 0-51.

Para reorganizar el mazo, vuelva a establecer el índice en cero y cambie la clave de cifrado (semilla aleatoria).

Su algoritmo no necesita ser de calidad criptográfica (y no debería serlo, ¡porque eso sería computacionalmente costoso!). Una forma realmente buena de crear un algoritmo de cifrado de tamaño personalizado como este sería utilizar una red feistel, que le permite personalizar el tamaño y la calidad según sus necesidades. Para la función redonda de la red feistel, recomendaría algo como murmurhash3 porque es rápido y tiene un buen efecto de avalancha, lo que haría que los shuffles parezcan aleatorios.

Consulte mi publicación de blog para obtener información más detallada y código fuente: http://blog.demofox.org/2013/07/06/fast-lightweight-random-shuffle-functionality-fixed/


Esta respuesta, como está redactada actualmente, no ayuda mucho cuando la URL inevitablemente cae de la superficie de Internet. Considere elaborar en la respuesta los puntos más destacados del artículo vinculado, de modo que la respuesta pueda sostenerse por sí misma.
Lars Viklund

1
Buen punto Lars, actualizado con más información para que un lector pueda al menos buscar más información sobre todos los componentes específicos de una solución para barajar tarjetas usando el cifrado de preservación de formatos. ¡Gracias!
Alan Wolfe

1

El tutorial java 1.5 enum tiene una forma interesante de implementar un mazo de cartas, construir el mazo, barajar y repartir. Todo muy simple usando enumsyCollections

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
    }
}

Y la clase para gestionar la cubierta.

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
Se desaconsejan las respuestas de solo código.
SurvivalMachine
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.