1. Hey! Guest! The 35th GMC Jam will take place between November 28th, 12:00 UTC - December 2nd, 12:00 UTC. Why not join in! Click here to find out more!
    Dismiss Notice

Shuffle card deck and map values

Discussion in 'Tutorials' started by tagwolf, Jul 29, 2019.

  1. tagwolf

    tagwolf Member

    Joined:
    Aug 6, 2016
    Posts:
    59
    GM Version: GMS2
    Target Platform: ALL

    Download: https://host-a.net/f/226890-cardsyyz

    (I found a place to upload thanks to a community suggestion! The project has all the card assets I reference but code doesn't exactly match the tutorial as it was part of some prototyping. It's got some fun goodies in it though. ENJOY!!)

    Summary:
    While replicating gambling machine logic, I did some research and found out that standard video poker machines are performing a continual shuffle on the card deck.

    This is optional in the following tutorial. But overall, this should show you how to create a deck of card objects and shuffle them, then map the correct value to the objects you create in the gamespace.

    If I get a good place to share the project files I'll upload my deck sprites and whole project. For now we'll do what we can! PM me if you would like the project or deck images to play with!

    upload_2019-7-28_17-1-12.png

    Tutorial:

    Optional (depending on what you are trying to make)
    I usually create several sprites, spr_spades, spr_diamonds, spr_clubs, spr_hearts, spr_cardback.
    I then create multiple frames on each sprite with a value of A,2,3,4,5,6,7,8,9,J,Q,K

    It's not important for the scope of this tutorial as it will depend on your game. But I'm going to move forward with the assumption that you want to use a 52 card deck of standard playing cards.

    Create a script called scr_init_deck

    This will fill a ds_list with 52 playing cards.

    Code:
    // Destroy any existing deck
    if ds_exists(deck, ds_type_list) {
       ds_list_clear(deck);
       show_debug_message("Deck cleared.");
    } else {
       deck = ds_list_create();
       show_debug_message("Deck created.");
    }
    
    // Factory Card Deck Order
    // Spades
    ds_list_add(deck, "Ace_Spades");
    ds_list_add(deck, "2_Spades");
    ds_list_add(deck, "3_Spades");
    ds_list_add(deck, "4_Spades");
    ds_list_add(deck, "5_Spades");
    ds_list_add(deck, "6_Spades");
    ds_list_add(deck, "7_Spades");
    ds_list_add(deck, "8_Spades");
    ds_list_add(deck, "9_Spades");
    ds_list_add(deck, "10_Spades");
    ds_list_add(deck, "Jack_Spades");
    ds_list_add(deck, "Queen_Spades");
    ds_list_add(deck, "King_Spades");
    // Diamonds
    ds_list_add(deck, "Ace_Diamonds");
    ds_list_add(deck, "2_Diamonds");
    ds_list_add(deck, "3_Diamonds");
    ds_list_add(deck, "4_Diamonds");
    ds_list_add(deck, "5_Diamonds");
    ds_list_add(deck, "6_Diamonds");
    ds_list_add(deck, "7_Diamonds");
    ds_list_add(deck, "8_Diamonds");
    ds_list_add(deck, "9_Diamonds");
    ds_list_add(deck, "10_Diamonds");
    ds_list_add(deck, "Jack_Diamonds");
    ds_list_add(deck, "Queen_Diamonds");
    ds_list_add(deck, "King_Diamonds");
    // Clubs
    ds_list_add(deck, "Ace_Clubs");
    ds_list_add(deck, "2_Clubs");
    ds_list_add(deck, "3_Clubs");
    ds_list_add(deck, "4_Clubs");
    ds_list_add(deck, "5_Clubs");
    ds_list_add(deck, "6_Clubs");
    ds_list_add(deck, "7_Clubs");
    ds_list_add(deck, "8_Clubs");
    ds_list_add(deck, "9_Clubs");
    ds_list_add(deck, "10_Clubs");
    ds_list_add(deck, "Jack_Clubs");
    ds_list_add(deck, "Queen_Clubs");
    ds_list_add(deck, "King_Clubs");
    // Hearts
    ds_list_add(deck, "Ace_Hearts");
    ds_list_add(deck, "2_Hearts");
    ds_list_add(deck, "3_Hearts");
    ds_list_add(deck, "4_Hearts");
    ds_list_add(deck, "5_Hearts");
    ds_list_add(deck, "6_Hearts");
    ds_list_add(deck, "7_Hearts");
    ds_list_add(deck, "8_Hearts");
    ds_list_add(deck, "9_Hearts");
    ds_list_add(deck, "10_Hearts");
    ds_list_add(deck, "Jack_Hearts");
    ds_list_add(deck, "Queen_Hearts");
    ds_list_add(deck, "King_Hearts");
    
    show_debug_message("Deck initialized.");
    
    // Return deck data structure
    return deck;
    

    Now that we have a script that creates a deck, understand that this is only creating a list of names. In order to perform logic operations on these cards, we need some more information available. So now create another script called scr_map_card

    Code:
    // Card Value Mapping
    card = argument0;
    
    switch (card) {
    // Spades
    case ("Ace_Spades"):     sprite_index = spr_spades; image_index = 0; value = 1; suit = "spades"; break;
    case ("2_Spades"):       sprite_index = spr_spades; image_index = 1; value = 2; suit = "spades"; break;
    case ("3_Spades"):       sprite_index = spr_spades; image_index = 2; value = 3; suit = "spades"; break;
    case ("4_Spades"):       sprite_index = spr_spades; image_index = 3; value = 4; suit = "spades"; break;
    case ("5_Spades"):       sprite_index = spr_spades; image_index = 4; value = 5; suit = "spades"; break;
    case ("6_Spades"):       sprite_index = spr_spades; image_index = 5; value = 6; suit = "spades"; break;
    case ("7_Spades"):       sprite_index = spr_spades; image_index = 6; value = 7; suit = "spades"; break;
    case ("8_Spades"):       sprite_index = spr_spades; image_index = 7; value = 8; suit = "spades"; break;
    case ("9_Spades"):       sprite_index = spr_spades; image_index = 8; value = 9; suit = "spades"; break;
    case ("10_Spades"):      sprite_index = spr_spades; image_index = 9; value = 10; suit = "spades"; break;
    case ("Jack_Spades"):    sprite_index = spr_spades; image_index = 10; value = 11; suit = "spades"; break;
    case ("Queen_Spades"):   sprite_index = spr_spades; image_index = 11; value = 12; suit = "spades"; break;
    case ("King_Spades"):    sprite_index = spr_spades; image_index = 12; value = 13; suit = "spades"; break;
    // Diamonds
    case ("Ace_Diamonds"):   sprite_index = spr_diamonds; image_index = 0; value = 1; suit = "diamonds"; break;
    case ("2_Diamonds"):     sprite_index = spr_diamonds; image_index = 1; value = 2; suit = "diamonds"; break;
    case ("3_Diamonds"):     sprite_index = spr_diamonds; image_index = 2; value = 3; suit = "diamonds"; break;
    case ("4_Diamonds"):     sprite_index = spr_diamonds; image_index = 3; value = 4; suit = "diamonds"; break;
    case ("5_Diamonds"):     sprite_index = spr_diamonds; image_index = 4; value = 5; suit = "diamonds"; break;
    case ("6_Diamonds"):     sprite_index = spr_diamonds; image_index = 5; value = 6; suit = "diamonds"; break;
    case ("7_Diamonds"):     sprite_index = spr_diamonds; image_index = 6; value = 7; suit = "diamonds"; break;
    case ("8_Diamonds"):     sprite_index = spr_diamonds; image_index = 7; value = 8; suit = "diamonds"; break;
    case ("9_Diamonds"):     sprite_index = spr_diamonds; image_index = 8; value = 9; suit = "diamonds"; break;
    case ("10_Diamonds"):    sprite_index = spr_diamonds; image_index = 9; value = 10; suit = "diamonds"; break;
    case ("Jack_Diamonds"):  sprite_index = spr_diamonds; image_index = 10; value = 11; suit = "diamonds"; break;
    case ("Queen_Diamonds"): sprite_index = spr_diamonds; image_index = 11; value = 12; suit = "diamonds"; break;
    case ("King_Diamonds"):  sprite_index = spr_diamonds; image_index = 12; value = 13; suit = "diamonds"; break;
    // Clubs
    case ("Ace_Clubs"):      sprite_index = spr_clubs; image_index = 0; value = 1; suit = "clubs"; break;
    case ("2_Clubs"):        sprite_index = spr_clubs; image_index = 1; value = 2; suit = "clubs"; break;
    case ("3_Clubs"):        sprite_index = spr_clubs; image_index = 2; value = 3; suit = "clubs"; break;
    case ("4_Clubs"):        sprite_index = spr_clubs; image_index = 3; value = 4; suit = "clubs"; break;
    case ("5_Clubs"):        sprite_index = spr_clubs; image_index = 4; value = 5; suit = "clubs"; break;
    case ("6_Clubs"):        sprite_index = spr_clubs; image_index = 5; value = 6; suit = "clubs"; break;
    case ("7_Clubs"):        sprite_index = spr_clubs; image_index = 6; value = 7; suit = "clubs"; break;
    case ("8_Clubs"):        sprite_index = spr_clubs; image_index = 7; value = 8; suit = "clubs"; break;
    case ("9_Clubs"):        sprite_index = spr_clubs; image_index = 8; value = 9; suit = "clubs"; break;
    case ("10_Clubs"):       sprite_index = spr_clubs; image_index = 9; value = 10; suit = "clubs"; break;
    case ("Jack_Clubs"):     sprite_index = spr_clubs; image_index = 10; value = 11; suit = "clubs"; break;
    case ("Queen_Clubs"):    sprite_index = spr_clubs; image_index = 11; value = 12; suit = "clubs"; break;
    case ("King_Clubs"):     sprite_index = spr_clubs; image_index = 12; value = 13; suit = "clubs"; break;
    // Hearts
    case ("Ace_Hearts"):     sprite_index = spr_hearts; image_index = 0; value = 1; suit = "hearts"; break;
    case ("2_Hearts"):       sprite_index = spr_hearts; image_index = 1; value = 2; suit = "hearts"; break;
    case ("3_Hearts"):       sprite_index = spr_hearts; image_index = 2; value = 3; suit = "hearts"; break;
    case ("4_Hearts"):       sprite_index = spr_hearts; image_index = 3; value = 4; suit = "hearts"; break;
    case ("5_Hearts"):       sprite_index = spr_hearts; image_index = 4; value = 5; suit = "hearts"; break;
    case ("6_Hearts"):       sprite_index = spr_hearts; image_index = 5; value = 6; suit = "hearts"; break;
    case ("7_Hearts"):       sprite_index = spr_hearts; image_index = 6; value = 7; suit = "hearts"; break;
    case ("8_Hearts"):       sprite_index = spr_hearts; image_index = 7; value = 8; suit = "hearts"; break;
    case ("9_Hearts"):       sprite_index = spr_hearts; image_index = 8; value = 9; suit = "hearts"; break;
    case ("10_Hearts"):      sprite_index = spr_hearts; image_index = 9; value = 10; suit = "hearts"; break;
    case ("Jack_Hearts"):    sprite_index = spr_hearts; image_index = 10; value = 11; suit = "hearts"; break;
    case ("Queen_Hearts"):   sprite_index = spr_hearts; image_index = 11; value = 12; suit = "hearts"; break;
    case ("King_Hearts"):    sprite_index = spr_hearts; image_index = 12; value = 13; suit = "hearts"; break;
    }
    
    return;
    
    Granted it might be a bit expressive, but for our purposes it should do fine.

    For our last script, call it scr_shuffle_deck

    Code:
    deck = argument0;
    
    //TODO: error checking, better randomization, cards remaining,
    // confirm shuffle, confirm random, confirm deck integrity.
    //check if should be shuffling currently, if flipping a card we want to make sure we delete the
    //correct value.
    
    randomize()
    
    ds_list_shuffle(deck);
    
    var cards_in_deck = ds_list_size(deck);
    
    if verbose
    {
       for (i = 0; i <= cards_in_deck; i++)
       {
           card = ds_list_find_value(deck, i);
           show_debug_message(string(card));
       }
    }
    
    show_debug_message(string(cards_in_deck)+" cards shuffled.");
    
    return;
    
    This script does the leg work of randomizing every time we shuffle the deck (both the seed and the deck itself). If verbose is enabled it will also output our results, which is good for debugging purposes.

    Object Code

    ** NOTE: Everything below is project specific. You don't need any of it and it's included as example of how to use the scripts above. ***

    obj_deck - Create Event
    Code:
    
    // Uncomment for continuous shuffling
    //shuffling = true;
    //verbose = true;
    deck = scr_init_deck()
    
    // Let's create a deck card pile
    // You can easily add mouse events to the cards that spawn on top of each other to pick them up.
    // If I find a good place to host project files I'll include it and my tiny card deck sprites that I love a lot :)
    
    for (var i = 0; i <= ds_list_size(deck) - 1; i++) {
     
       var card = ds_list_find_value(deck,i);
     
       show_debug_message(string(card));
     
       scr_assign_card(card);
     
       // spawn card in pile
       new_card = instance_create_depth(random_range(63, 65), random_range(63, 65), 0, obj_card);
     
       // randomize angle
       new_card.image_angle = random_range(-5,5);
     
     
       // set values
       new_card.card_name = card;
       new_card.card_suit = card_suit;
       new_card.card_value = card_value
       new_card.card_sprite = card_sprite;
       new_card.card_image = card_image;
       // set card image
       //new_card.sprite_index = card_sprite;
       //new_card.image_index = card_image;
     
       // set card image face down
       new_card.sprite_index = spr_cardback;
     
    }
    
    obj_deck - Step Event
    Code:
    /// @description Main deck logic
    
    // Determine if we need to shuffle
    if (shuffling) {
       scr_shuffle_deck(deck);
    }
    
    For the cards themselves, you can place 52 in your room or setup a for loop to stack them onto a deck you can grab, whatever works best for your use-case!

    obj_card - Create Event
    Code:
    card_drag = false;
    card_name = 0;
    card_suit = 0;
    card_value = 0;
    card_image = 0;
    card_sprite = 0;
    
    obj_card - Step Event (if you want card dragging and stuff include the things below)
    Code:
    if (card_drag) {
    
       x = mouse_x;
       y = mouse_y;
    
    }
    
    obj_card - Left Pressed Event
    Code:
    
    show_debug_message("Card Clicked");
    show_debug_message("NAME: " + string(card_name));
    show_debug_message("VALUE: " + string(card_value));
    show_debug_message("SUIT: " + string(card_suit));
    show_debug_message("SPRITE: " + string(card_sprite));
    show_debug_message("IMAGE: " + string(card_image));
    
    if (global.any_card_drag == false) {
       global.any_card_drag = true;
       card_drag = true;
     
       sprite_index = card_sprite;
       image_index = card_image;
     
    }
    
    obj_card - Left Released Event
    Code:
    if (card_drag == true) {
    
       card_drag = false;
       global.any_card_drag = false;
     
    }
    
    Then just put the obj_deck (no sprite) in a room and you're good to go! In this example the obj_card doesn't need a sprite either as it's dynamically assigned one. obj_card's do not need to be added to your room (in THIS example).
     
    Last edited: Aug 5, 2019
    SilentxxBunny likes this.
  2. tagwolf

    tagwolf Member

    Joined:
    Aug 6, 2016
    Posts:
    59
  3. Papajustify

    Papajustify Member

    Joined:
    Sep 29, 2019
    Posts:
    1
    Thank you for this tutorial. I have been looking for a tutorial, regarding cards, for a long time. I'm very new to GML code (mainly using DnD). This was so well written I was able to work it out.

    As a side note, it also answered a question I had about a simple way for item dragging.
     

Share This Page

  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice