• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

GameMaker Playing card deck

F

Flimshady

Guest
Hello,

i am currently trying to create a simple deck of 24 playing cards. I want the cards to be randomised in order, but there lies my problem. I've tried using the choose() function, but this function can choose the same card twice sometimes, so it doesnt represent a real deck.
Maybe if someone knows how to help me or can give me a little push in the right direction, I would be verry happy.

Thank you!
 

Attachments

B

BeepBoop

Guest
Use ds_lists! Create it, add your cards, shuffle, retrieve the first card from the list. Then delete that card from the list and add again (so it will become the last card in the list).

OR make a simple cooldown. If you don't want to pick the same card twice, you can do something like this:

Code:
card = choose(val1,val2,val3,val4);
cardcooldown = card;
while (card=cardcooldown)
{
   card = choose(val1,val2,val3,val4);
}
 
Last edited by a moderator:
F

Flimshady

Guest
Wow, I did not think about using a cooldown, thta would work for me. Thank you very much for your help! :)

PS: Do you know how to use the choose function if I have different sprites with different subimages, imagine having a sprite for all spade cards, a sprite for all heart cards, and so on?
 

NightFrost

Member
You should make them either all different sprites, so each card has unique sprite identifier, or make them all subimages in the same sprite, so each card has unique subimage number. Now you have no single unique identifier for a card and must track suit and number separately to pick the correct image.
 
B

BeepBoop

Guest
You can use Arrays to give each card its own ID. For example:

Code:
Card_Name[1] = "6 of Spades";
Card_Sprite[1] = spr_6spades;
Card_Name[2] = "7 of Spades";
Card_Sprite[2] = spr_7spades;
Card_Name[3] = "10 of Hearts";
Card_Sprite[3] = spr_10hearts;
etc...
Code:
sprite_index = Card_Sprite[mycardID];
And here is the example of using ds_lists:

Create the deck:
Code:
mydeck = ds_list_create();
Add your cards to the deck:
Code:
ds_list_add(mydeck, mycardID);
Shuffle the deck:
Code:
ds_list_shuffle(mydeck);
Retrieve the first card from the deck:
Code:
firstcard = ds_list_find_value(mydeck, 0);
Delete 'firstcard' card from the deck:
Code:
ds_list_delete(mydeck, 0);
(Optional) Add it again:
Code:
ds_list_add(mydeck, firstcard);
Don't forget to delete your ds_list deck when no longer needed:
Code:
ds_list_destroy(mydeck);
 
Last edited by a moderator:
F

Flimshady

Guest
You should make them either all different sprites, so each card has unique sprite identifier, or make them all subimages in the same sprite, so each card has unique subimage number. Now you have no single unique identifier for a card and must track suit and number separately to pick the correct image.
Yeah, thats what I thought, thank you for your quick answer! :)
 
F

Flimshady

Guest
You can use Arrays to give each card its own ID. For example:

Card_Name[1] = "6 of Spades";
Card_Sprite[1] = spr_6spades;
Card_Name[2] = "7 of Spades";
Card_Sprite[2] = spr_7spades;
Card_Name[3] = "10 of Hearts";
Card_Sprite[3] = spr_10hearts;

etc...
sprite_index = Card_Sprite[mycardID];

And here is the example of using ds_lists:

Create the deck:
mydeck = ds_list_create();
Add your cards to the deck:
ds_list_add(mydeck, mycardID);
Shuffle the deck:
ds_list_shuffle(mydeck);
Retrieve the first card from the deck:
firstcard = ds_list_find_value(mydeck, 0);
Delete 'firstcard' card from the deck:
ds_list_delete(mydeck, 0);
(Optional) Add it again:
ds_list_add(mydeck, firstcard);
Don't forget to delete your ds_list deck when no longer needed:
ds_list_destroy(mydeck);
I will try again now, let's see how far I will come ^^ Thank you for your help :)
 
D

dannyjenn

Guest
OR make a simple cooldown. If you don't want to pick the same card twice, you can do something like this:

card = choose(val1,val2,val3,val4);
cardcooldown = card;
while (card=cardcooldown)
{
card = choose(val1,val2,val3,val4);
}
As you've written it, that won't work. Because for one thing, you'd need to set cardcooldown after the loop, not before it.
Code:
// create event:
cooldowncard = -1;
Code:
// whenever the player draws a card:
while(cardcooldown==card){
    card = choose(val1,val2,val3,val4);
}
cardcooldown = card;
Second, even with that change, it still won't work. Because there's nothing to prevent the player from drawing the first card again on the third turn. To fix that problem, cardcooldown would need to be a ds_list, not a single value. And even then, it's not the best way, since that loop could theoretically be infinite (if the computer keeps drawing the exact same card every time... which won't happen, but still...)


Here's a very simple way of doing it for a standard deck of 52 cards, 13 of each of 4 suits (obviously this will need to be modified for non-standard decks):
- Make a single sprite with 52 subimages, ordered as follows: A♠︎, 2♠︎, 3♠︎, 4♠︎, 5♠︎, 6♠︎, 7♠︎, 8♠︎, 9♠︎, 10♠︎, J♠︎, Q♠︎, K♠︎, A♥︎, 2♥︎, 3♥︎, 4♥︎, 5♥︎, 6♥︎, 7♥︎, 8♥︎, 9♥︎, 10♥︎, J♥︎, Q♥︎, K♥︎, A♣︎, 2♣︎, 3♣︎, 4♣︎, 5♣︎, 6♣︎, 7♣︎, 8♣︎, 9♣︎, 10♣︎, J♣︎, Q♣︎, K♣︎, A♦︎, 2♦︎, 3♦︎, 4♦︎, 5♦︎, 6♦︎, 7♦︎, 8♦︎, 9♦︎, 10♦︎, J♦︎, Q♦︎, K♦︎.
- Set things up like this:
Code:
deck = ds_list_create(); // make the empty deck

// add the 52 cards to the empty deck:
for(var i=0;i<52;i+=1){
    deck[|i] = i; // <-- The | is a kind of 'accessor'. It's needed whenever you're using a ds_list rather than an array. Look up 'accessor' in the manual to learn more.
}

ds_list_shuffle(deck); // shuffle the 52 cards in the deck
- Draw a card like this:
Code:
var card = deck[|0]; // copy the value of the top card in the deck to your 'card' variable.
ds_list_delete(deck,0); // remove the top card from the deck (otherwise you'll keep drawing the same card every draw)
- To get the card's numerical value, you do ((card mod 4)+1) edit: ((card mod 13)+1). To get the card's suit, you do (card mod 13) edit: (card div 13). And these macros should come in handy:
Code:
#macro ACE 1
#macro JACK 11
#macro QUEEN 12
#macro KING 13

#macro SPADE 0
#macro HEART 1
#macro CLUB 2
#macro DIAMOND 3
edit - And as @BeepBoop pointed out, be sure to call ds_list_destroy(deck) when you no longer need the deck (such as in its destroy event, or at game end).
 
Last edited by a moderator:
F

Flimshady

Guest
- To get the card's numerical value, you do ((card mod 4)+1). To get the card's suit, you do (card mod 13). And these macros should come in handy:
Code:
#macro ACE 1
#macro JACK 11
#macro QUEEN 12
#macro KING 13

#macro SPADE 0
#macro HEART 1
#macro CLUB 2
#macro DIAMOND 3
Could you explain this part again, I am sorry but I don't quite get what you are trying to teach me ^^ I've looked up the modulo expression but I still don't get the hang of ((card mod 4)+1) and (card mod 13). And I don't see the coherence with macros... I hope I am not too bugging, thank you for your help anyways :)
 
D

dannyjenn

Guest
@Flimshady - Sorry, I got a little mixed up on those formulas. They should be ((card mod 13) + 1) for the value and (card div 13) for the suit.


Values:
Cards 0, 13, 26, and 39 should be 1
Cards 1, 14, 27, and 40 should be 2
Cards 2, 15, 28, and 41 should be 3
Cards 3, 16, 29, and 42 should be 4
Cards 4, 17, 30, and 43 should be 5
Cards 5, 18, 31, and 44 should be 6
Cards 6, 19, 32, and 45 should be 7
Cards 7, 20, 33, and 46 should be 8
Cards 8, 21, 34, and 47 should be 9
Cards 9, 22, 35, and 48 should be 10
Cards 10, 23, 36, and 49 should be 11
Cards 11, 24, 37, and 50 should be 12
Cards 12, 25, 38, and 51 should be 13

The 'modulus operator' gets the "remainder" of the division, and can be used to "wrap" the numbers and keep them all within the range of 0 through whatever... in this case, we want all number to remain within the range of 0 through 12. We do that by doing card mod 13. The following sequence
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
then becomes
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
But we want the range to be 1 through 13, not 0 through 12. So we add 1: ((card mod 13)+1).


Suits:
Any card 0 through 12 is a SPADE (0)
Any card 13 through 25 is a HEART (1)
Any card 26 through 38 is a CLUB (2)
Any card 39 through 52 is a DIAMOND (3)

Basically, we want to scale the numbers down such as to force the range of 0 through 51 to fit into the range of 0 through 3. i.e. We want to change the sequence:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
into
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
To do this, we use div, which is the 'integer division' operator. It's basically the same thing as division but it throws away the "remainder".
So the formula would need to be (card div 13)
0 div 13 is 0,
1 div 13 is 0,
2 div 13 is 0,
. . .,
13 div 13 is 1
14 div 13 is 1,
15 div 13 is 1,
. . .,
26 div 13 is 2,
27 div 13 is 2,
28 div 13 is 2,
. . .,
39 div 13 is 3,
40 div 13 is 3,
. . .,
51 div 13 is 3.


Macros:
Macros are just a kind of constant. All they do is make the code more legible and easier to remember...
e.g. card_suit==CLUB makes a lot more sense than card_suit==2
e.g. card_value==QUEEN is a little easier to work with than card_value==12
 
F

Flimshady

Guest
@Flimshady - Sorry, I got a little mixed up on those formulas. They should be ((card mod 13) + 1) for the value and (card div 13) for the suit.


Values:
Cards 0, 13, 26, and 39 should be 1
Cards 1, 14, 27, and 40 should be 2
Cards 2, 15, 28, and 41 should be 3
Cards 3, 16, 29, and 42 should be 4
Cards 4, 17, 30, and 43 should be 5
Cards 5, 18, 31, and 44 should be 6
Cards 6, 19, 32, and 45 should be 7
Cards 7, 20, 33, and 46 should be 8
Cards 8, 21, 34, and 47 should be 9
Cards 9, 22, 35, and 48 should be 10
Cards 10, 23, 36, and 49 should be 11
Cards 11, 24, 37, and 50 should be 12
Cards 12, 25, 38, and 51 should be 13

The 'modulus operator' gets the "remainder" of the division, and can be used to "wrap" the numbers and keep them all within the range of 0 through whatever... in this case, we want all number to remain within the range of 0 through 12. We do that by doing card mod 13. The following sequence
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
then becomes
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
But we want the range to be 1 through 13, not 0 through 12. So we add 1: ((card mod 13)+1).


Suits:
Any card 0 through 12 is a SPADE (0)
Any card 13 through 25 is a HEART (1)
Any card 26 through 38 is a CLUB (2)
Any card 39 through 52 is a DIAMOND (3)

Basically, we want to scale the numbers down such as to force the range of 0 through 51 to fit into the range of 0 through 3. i.e. We want to change the sequence:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
into
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
To do this, we use div, which is the 'integer division' operator. It's basically the same thing as division but it throws away the "remainder".
So the formula would need to be (card div 13)
0 div 13 is 0,
1 div 13 is 0,
2 div 13 is 0,
. . .,
13 div 13 is 1
14 div 13 is 1,
15 div 13 is 1,
. . .,
26 div 13 is 2,
27 div 13 is 2,
28 div 13 is 2,
. . .,
39 div 13 is 3,
40 div 13 is 3,
. . .,
51 div 13 is 3.


Macros:
Macros are just a kind of constant. All they do is make the code more legible and easier to remember...
e.g. card_suit==CLUB makes a lot more sense than card_suit==2
e.g. card_value==QUEEN is a little easier to work with than card_value==12
Now that‘s what I call an answer. It makes much more sense to me now, thank you very much for taking the time. I am currently not at home, but I will try it out your way and you will be hearing from me. :)
 
F

Flimshady

Guest
Okay, I am stuck again...
Code:
/// Create the deck
deck = ds_list_create();

// Add the cards
for (var i = 0; i < 52; i+=1) {
    deck [|i] = i;
}

// Shuffle the deck
ds_list_shuffle(deck);

card = deck [|0];
ds_list_delete(deck, 0);

card_value = ((card mod 13) + 1);
card_suit = (card div 13);
if(card_value = 1 and card_suit = 0) {
     image_index = 0;
}
if(card_value = 2 and card_suit = 0) {
     image_index = 1;
}
if(card_value = 3 and card_suit = 0) {
     image_index = 2;
}
if(card_value = 4 and card_suit = 0) {
     image_index = 3;
}
if(card_value = 5 and card_suit = 0) {
     image_index = 4;

/// And so on and so on...
}
This is what I got so far. I've placed this in my obj_deck create event, but when I start my game, the sprite subimages just cycle though. Thank you for your help ^^
 
F

Flimshady

Guest
Have you set the image_speed to zero?
Oh my god... I am dumb, thank you ^^ But now it only shows my the first subimage of the sprite, but the code should give it another, according to its card_value and card_suit. What am I doing wrong here?
 

chamaeleon

Member
Oh my god... I am dumb, thank you ^^ But now it only shows my the first subimage of the sprite, but the code should give it another, according to its card_value and card_suit. What am I doing wrong here?
I don't know why you're not seeing different images, but I'm curious as to why you don't simply assign "card" to image_index, rather than having 52 if statements, given matching ordering of your frames in the sprite. The values you're assigning now are essentially reversing the mod/div computation to get back the original value.
 
D

dannyjenn

Guest
Yeah, you don't need those if statements.

But, where exactly are you putting that code? It should go in a controller object, not in the card object.
Also, the 'creating the deck', 'adding the cards', and 'shuffling' should go in the create event, but the rest of the code should go someplace else (probably in some sort of mouse click event).
 
F

Flimshady

Guest
I've got it to work now, here is the code for everyone who is interessted! Thank you all very much for your support! <3
Code:
/// In obj_deck - Create Event - Create the deck
deck = ds_list_create();
// Add the cards
for (var i=0; i < 52; i+=1) {
    deck[|i] = i;
}
// Real random shuffle
randomize();
//Shuffle the deck
ds_list_shuffle(deck);
Code:
/// In obj_deck - Left Pressed - Remove the card from the deck
if(ds_list_size(deck)>0) {
var top_card = instance_create_depth(x, y, -1, obj_card);
var card_sprite = ds_list_find_value(deck, 0);
top_card.image_index = card_sprite;
ds_list_delete(deck, 0);
}
Here, I just have 3 subimages for my playing card deck, which I want to change depending on how big the remaining list is.
Code:
// / In obj_deck - Step Event - Changing the deck's sprite
var deck_size = ds_list_size(deck);
image_index = min(3, deck_size);


Code:
/// In obj_controller - Create Event - Create the global var to keep track of drawed card
global.card = noone;
Code:
/// In obj_controller - Step End - Move the card
if(global.card !=noone) {
    with (global.card) {
        x=mouse_x;
        y=mouse_y;
    }
}


Code:
/// In obj_card - Create Event - Set the image speed to 0
image_speed = 0;
Code:
/// In obj_card - Left pressed - Pick up the card
global.card = id;
depth = -1;
Code:
/// In obj_card - Left Released - Drop the card on the ground or on the deck
global.card = noone;
depth = 0;
 
Last edited by a moderator:
Top