Table of cards, an efficiency question, need advice

I’m creating a card game where the cards have special abilities that interact with other cards.  At this point I just have simple tables holding cards that don’t have unique identifiers to them, which is causing a lot of issues when I try to execute more advanced functions.

For example, a player may have 20 cards total, with 10 in a discard pile, 5 in his hand, and the other 5 in a draw pile.  Playing one of the cards from his hand can remove 2 cards from his discard pile (and out of the game), and remove that particular card (out of the game) as well.

Because I don’t have unique identifiers for each card in the game, I’m running into weird bugs where if I have 2 identical cards in the player hand, both of them will get removed from the game, whether he plays the second card or not.

So my question is, I’m thinking of modifying my cards table to make each card unique, even if there are 5 copies of that card in the game.  So card “Trash Card” would be in my cards table 5 times (instead of once) with unique identifiers of “001”, “002”, and so on.

Does it make sense to do something like that, or would it be better to attach a unique identifier to the cards only once they’re in a player’s card deck?  So for example the “Trash Card” would only be in the cards table once, but if a player acquires multiple copies of it, they would have unique identifiers at that point.

I hope this makes any sense at all.  I’ve run into a lot of road blocks on this game due to multiple cards being indistinguishable from each other once they’re in a table.

Thanks in advance for any advice!

Hi,

Card games are especially difficult, when they are of the non-standard types. That being said, what I suggest is using a Factory to create unique “instances” of the cards.

I posted some code here that just shows some possible techniques, you should be able to see how the object is created. You can run the main.lua (2nd snippet) to see results. These are on the easier side. You can wade through this tutorial to go even more in depth with the pattern.

I also highly recommend 30Log for projects like this as well. 

Hope this gives you some ideas.

Cheers.

http://screencast.com/t/wgvIXfGJBfP

As develephant mentioned, it’s not the easiest task to decide how to represent your cards.

For me there are two main thoughts here:

  1. How many cards do you have to represent in a single game? If there are a lot you may run into memory issues if you represent every card (wherever it is) with a table full of stats.

  2. Do you need “real” cards everywhere? What this means is, do you need a data structure for each card in the game or only for the ones on the field and in your hand? If it’s the second you could represent cards in your deck or the discard pile with strings instead of tables and create a table object for them as they come into play.

Awesome, thanks develephant.  I’ll check out that code and read through the tutorials tonight after work.

  1. It’s a deck building game similar to Dominion, so there are around 20 unique cards in any particular game.  However, there are potentially 5 copies of each card, and some basic cards that have upwards of 35 copies in a game.  That said, I do eventually want to support lots of cards to make each game fresh and exciting.  I can see there being hundreds of unique cards at some point, even though a particular round would only feature around 20 from the larger pool of available cards.

  2. So essentially the 5 cards in the players hand would be the only cards that have the full details on what all they do?  Other cards in the discard and draw piles would just be simple identifiers.

So this leads to another question, what is the best way to store the information for potentially dozens of cards?  Right now I just have a file called “allCards.lua” which is just a big table of cards.  Each card has information about cost, income, additional cards, additional buys, special function names (for when the card does unique things when played), card image, etc.

Thanks for all the advice so far, you two have been a great help!

Unless your cards have properties that can change dynamically per instance, then I’d model it by just passing around id’s everywhere.  (otoh, if instances need to maintain their own mutable state, then your instances will need to be a bit heavier and carry around their own properties)

Here’s one possible way to set things up if you don’t need per-instance-state (dressing it up “pretty” inside a more usable class structure is left as an exercise for the reader, this is just to illustrate concept not provide implementation)

-- define the complete set of cards and their properties here -- let's say we have five card types identified "A".."E" -- (each with whatever properties are appropriate for this game) local CardProperties = { ["A"] = { health=5, magic=4, armor=3, damage=2 }, ["B"] = { health=4, magic=5, armor=2, damage=3 }, ["C"] = { health=3, magic=3, armor=4, damage=4 }, ["D"] = { health=2, magic=6, armor=1, damage=5 }, ["E"] = { health=1, magic=2, armor=5, damage=6 }, } -- it's also handy to have an indexed list of just the types (if the types are not themselves numeric) local CardTypes = { "A","B","C","D","E" } -- you might also (or instead) want a weighted index list of just the types (to be used while populating a new deck) local CardTypesWeighted = { "A","A","A","A","B","B","B","C","C","D","E" } local function makeCard(id,type) return { id=id, type=type } end local function makeDeck(numCards) local deck = {} for i=1,numCards do local type = CardTypesWeighted[math.random(#CardTypesWeighted)] deck[i] = makeCard(i,type) end return deck end local deck = makeDeck(10) print("THE DECK:") for i = 1,#deck do local card = deck[i] local props = CardProperties[card.type] print(card.id, card.type, props.health, props.magic, props.armor, props.damage) end

So all your cards look like {id,type}, no matter where they’re currently stored.

-- left as exercise for reader: transfer between deck-hand-discard is just table.insert/table.remove at this point -- (for example, player plays his "third" card, which maybe happens to be an "A" type; -- and discards his fifth card, which is of some other type, or same type, doesn't matter) -- fe, your game may or may not have all of the following types of card movement (and maybe even some others not listed here): -- deck-to-hand == "deal" -- hand-to-table = "play" -- hand-to-discard = "discard" -- table-to-discard = "end turn" -- etc local hand = {} local table = {} local discard = {}

Thanks for the reply Dave, it helps to see everything laid out that way.

I tried refactoring some of my code to try adding unique IDs to just the cards that are in the player’s deck, and I’m having a really bizarre problem that I can’t seem to comprehend.

I have a couple global tables declared at the top of my main.lua

[lua]

currentHand = {};

drawPile = {};

discardPile = {};

[/lua]

In a setup function I put 10 cards into the drawPile with unique IDs.  7 of them are the same card.

[lua]

  table.insert(drawPile,{id=“1”,name=“M001”})

  table.insert(drawPile,{id=“2”,name=“M001”})

  table.insert(drawPile,{id=“3”,name=“M001”})

  table.insert(drawPile,{id=“4”,name=“M001”})

  table.insert(drawPile,{id=“5”,name=“M001”})

  table.insert(drawPile,{id=“6”,name=“M001”})

  table.insert(drawPile,{id=“7”,name=“M001”})

  

  table.insert(drawPile,{id=“8”,name=“W001”})

  table.insert(drawPile,{id=“9”,name=“W001”})

  table.insert(drawPile,{id=“10”,name=“W002”})

[/lua]

I have a function called getPlayerHand() that executes when the draw pile is clicked.

In the getPlayerHand() function I take 5 cards from “drawPile” and put them into “currentHand”.  “Cards” is a big table I have of all the cards and all their properties.  There are no unique IDs in the “Cards” table.

[lua]

function getPlayerHand()

for i=1, 5 do

currentHand[i] = Cards[drawPile[1].name]

currentHand[i].id = drawPile[1].id – make sure the unique ID gets passed with this card in currentHand

table.remove(drawPile,1)

print("getPlayerHand - i: " … i … " currentHand[i].id: " … currentHand[i].id … " name: " … currentHand[i].name)

end

end

[/lua]

**Where the problem comes in is the unique IDs I’m passing in for each card seems to be getting overwritten/changed after the getPlayerHand() function. **I did a bunch of debugging and ended up putting this bit of code just after the getPlayerHand() function

[lua]

for i=1, #currentHand do

      print("checkCurrentHand - i: " … i … " currentHand[i].id: " … currentHand[i].id … " name: " … currentHand[i].name) 

end

[/lua]

When I run my program, it gives this output:

[lua]

20:13:24.294  getPlayerHand - i: 1 currentHand[i].id: 3 name: M001

20:13:24.294  getPlayerHand - i: 2 currentHand[i].id: 10 name: W001

20:13:24.294  getPlayerHand - i: 3 currentHand[i].id: 1 name: M001

20:13:24.294  getPlayerHand - i: 4 currentHand[i].id: 8 name: W002

20:13:24.294  getPlayerHand - i: 5 currentHand[i].id: 5 name: M001

20:13:24.294  checkCurrentHand - i: 1 currentHand[i].id: 5 name: M001

20:13:24.294  checkCurrentHand - i: 2 currentHand[i].id: 10 name: W001

20:13:24.294  checkCurrentHand - i: 3 currentHand[i].id: 5 name: M001

20:13:24.294  checkCurrentHand - i: 4 currentHand[i].id: 8 name: W002

20:13:24.294  checkCurrentHand - i: 5 currentHand[i].id: 5 name: M001

[/lua]

For the 7 cards that are identical, M001, somehow the ID of the last one in the currentHand gets set for all copies of that card.  It happens with other cards that are duplicates as well, but in this example only the M001 cards showed up randomly from the drawPile.

I’m not sure how the IDs of “currentHand” can change just from leaving the getPlayerHand() function.  Any advice/help would be greatly, appreciated.

this creates a reference to Cards[n] not a copy of it, so there’s only ever the one per name, so in the next line when you assign it an id it gets overwritten by whichever occurs last – ie, all seven references point to the same one thing with whatever last-assigned id it has.  you just need to sort of which things are your instances and which things things are your classes – then just make sure it’s the unique instances that you’re giving the unique id’s.

I see.  Once I moved my debug statement out of the for loop in getPlayerHand(), I realized the ID was changing every time in the for loop.

I did a bunch of rework of my code using the “factory” concept from develephant and I’ve made some progress.  I was able to create a “currentHand” table with unique identifiers which seem to stay with it.  Unfortunately my code was so sloppily written up until this point that it looks like I’ll need to rework a considerable amount of it.

I really appreciate all the help from you all. Thanks again!

Hi,

Starting fresh is just a fact of development sometimes. This is also another reason to use version control. I’ve shelved “ideas” after 100’s of hours. It’s painful, but there can be a point where it will take you longer to “fix” everything, than to just restart with a better framework in mind. And you’ll actually move faster, since you already know what doesn’t work.  Refactoring is a large part of coding (if you care enough).

With that in mind, I have been trying to post my “card game” framework notes and tips. Hopefully this weekend. There is a simple pattern that can be very effective for what you are doing, but I’ve been a little swamped lately to tidy it up.

Cheers.

Hi,

Card games are especially difficult, when they are of the non-standard types. That being said, what I suggest is using a Factory to create unique “instances” of the cards.

I posted some code here that just shows some possible techniques, you should be able to see how the object is created. You can run the main.lua (2nd snippet) to see results. These are on the easier side. You can wade through this tutorial to go even more in depth with the pattern.

I also highly recommend 30Log for projects like this as well. 

Hope this gives you some ideas.

Cheers.

http://screencast.com/t/wgvIXfGJBfP

As develephant mentioned, it’s not the easiest task to decide how to represent your cards.

For me there are two main thoughts here:

  1. How many cards do you have to represent in a single game? If there are a lot you may run into memory issues if you represent every card (wherever it is) with a table full of stats.

  2. Do you need “real” cards everywhere? What this means is, do you need a data structure for each card in the game or only for the ones on the field and in your hand? If it’s the second you could represent cards in your deck or the discard pile with strings instead of tables and create a table object for them as they come into play.

Awesome, thanks develephant.  I’ll check out that code and read through the tutorials tonight after work.

  1. It’s a deck building game similar to Dominion, so there are around 20 unique cards in any particular game.  However, there are potentially 5 copies of each card, and some basic cards that have upwards of 35 copies in a game.  That said, I do eventually want to support lots of cards to make each game fresh and exciting.  I can see there being hundreds of unique cards at some point, even though a particular round would only feature around 20 from the larger pool of available cards.

  2. So essentially the 5 cards in the players hand would be the only cards that have the full details on what all they do?  Other cards in the discard and draw piles would just be simple identifiers.

So this leads to another question, what is the best way to store the information for potentially dozens of cards?  Right now I just have a file called “allCards.lua” which is just a big table of cards.  Each card has information about cost, income, additional cards, additional buys, special function names (for when the card does unique things when played), card image, etc.

Thanks for all the advice so far, you two have been a great help!

Unless your cards have properties that can change dynamically per instance, then I’d model it by just passing around id’s everywhere.  (otoh, if instances need to maintain their own mutable state, then your instances will need to be a bit heavier and carry around their own properties)

Here’s one possible way to set things up if you don’t need per-instance-state (dressing it up “pretty” inside a more usable class structure is left as an exercise for the reader, this is just to illustrate concept not provide implementation)

-- define the complete set of cards and their properties here -- let's say we have five card types identified "A".."E" -- (each with whatever properties are appropriate for this game) local CardProperties = { ["A"] = { health=5, magic=4, armor=3, damage=2 }, ["B"] = { health=4, magic=5, armor=2, damage=3 }, ["C"] = { health=3, magic=3, armor=4, damage=4 }, ["D"] = { health=2, magic=6, armor=1, damage=5 }, ["E"] = { health=1, magic=2, armor=5, damage=6 }, } -- it's also handy to have an indexed list of just the types (if the types are not themselves numeric) local CardTypes = { "A","B","C","D","E" } -- you might also (or instead) want a weighted index list of just the types (to be used while populating a new deck) local CardTypesWeighted = { "A","A","A","A","B","B","B","C","C","D","E" } local function makeCard(id,type) return { id=id, type=type } end local function makeDeck(numCards) local deck = {} for i=1,numCards do local type = CardTypesWeighted[math.random(#CardTypesWeighted)] deck[i] = makeCard(i,type) end return deck end local deck = makeDeck(10) print("THE DECK:") for i = 1,#deck do local card = deck[i] local props = CardProperties[card.type] print(card.id, card.type, props.health, props.magic, props.armor, props.damage) end

So all your cards look like {id,type}, no matter where they’re currently stored.

-- left as exercise for reader: transfer between deck-hand-discard is just table.insert/table.remove at this point -- (for example, player plays his "third" card, which maybe happens to be an "A" type; -- and discards his fifth card, which is of some other type, or same type, doesn't matter) -- fe, your game may or may not have all of the following types of card movement (and maybe even some others not listed here): -- deck-to-hand == "deal" -- hand-to-table = "play" -- hand-to-discard = "discard" -- table-to-discard = "end turn" -- etc local hand = {} local table = {} local discard = {}

Thanks for the reply Dave, it helps to see everything laid out that way.

I tried refactoring some of my code to try adding unique IDs to just the cards that are in the player’s deck, and I’m having a really bizarre problem that I can’t seem to comprehend.

I have a couple global tables declared at the top of my main.lua

[lua]

currentHand = {};

drawPile = {};

discardPile = {};

[/lua]

In a setup function I put 10 cards into the drawPile with unique IDs.  7 of them are the same card.

[lua]

  table.insert(drawPile,{id=“1”,name=“M001”})

  table.insert(drawPile,{id=“2”,name=“M001”})

  table.insert(drawPile,{id=“3”,name=“M001”})

  table.insert(drawPile,{id=“4”,name=“M001”})

  table.insert(drawPile,{id=“5”,name=“M001”})

  table.insert(drawPile,{id=“6”,name=“M001”})

  table.insert(drawPile,{id=“7”,name=“M001”})

  

  table.insert(drawPile,{id=“8”,name=“W001”})

  table.insert(drawPile,{id=“9”,name=“W001”})

  table.insert(drawPile,{id=“10”,name=“W002”})

[/lua]

I have a function called getPlayerHand() that executes when the draw pile is clicked.

In the getPlayerHand() function I take 5 cards from “drawPile” and put them into “currentHand”.  “Cards” is a big table I have of all the cards and all their properties.  There are no unique IDs in the “Cards” table.

[lua]

function getPlayerHand()

for i=1, 5 do

currentHand[i] = Cards[drawPile[1].name]

currentHand[i].id = drawPile[1].id – make sure the unique ID gets passed with this card in currentHand

table.remove(drawPile,1)

print("getPlayerHand - i: " … i … " currentHand[i].id: " … currentHand[i].id … " name: " … currentHand[i].name)

end

end

[/lua]

**Where the problem comes in is the unique IDs I’m passing in for each card seems to be getting overwritten/changed after the getPlayerHand() function. **I did a bunch of debugging and ended up putting this bit of code just after the getPlayerHand() function

[lua]

for i=1, #currentHand do

      print("checkCurrentHand - i: " … i … " currentHand[i].id: " … currentHand[i].id … " name: " … currentHand[i].name) 

end

[/lua]

When I run my program, it gives this output:

[lua]

20:13:24.294  getPlayerHand - i: 1 currentHand[i].id: 3 name: M001

20:13:24.294  getPlayerHand - i: 2 currentHand[i].id: 10 name: W001

20:13:24.294  getPlayerHand - i: 3 currentHand[i].id: 1 name: M001

20:13:24.294  getPlayerHand - i: 4 currentHand[i].id: 8 name: W002

20:13:24.294  getPlayerHand - i: 5 currentHand[i].id: 5 name: M001

20:13:24.294  checkCurrentHand - i: 1 currentHand[i].id: 5 name: M001

20:13:24.294  checkCurrentHand - i: 2 currentHand[i].id: 10 name: W001

20:13:24.294  checkCurrentHand - i: 3 currentHand[i].id: 5 name: M001

20:13:24.294  checkCurrentHand - i: 4 currentHand[i].id: 8 name: W002

20:13:24.294  checkCurrentHand - i: 5 currentHand[i].id: 5 name: M001

[/lua]

For the 7 cards that are identical, M001, somehow the ID of the last one in the currentHand gets set for all copies of that card.  It happens with other cards that are duplicates as well, but in this example only the M001 cards showed up randomly from the drawPile.

I’m not sure how the IDs of “currentHand” can change just from leaving the getPlayerHand() function.  Any advice/help would be greatly, appreciated.

this creates a reference to Cards[n] not a copy of it, so there’s only ever the one per name, so in the next line when you assign it an id it gets overwritten by whichever occurs last – ie, all seven references point to the same one thing with whatever last-assigned id it has.  you just need to sort of which things are your instances and which things things are your classes – then just make sure it’s the unique instances that you’re giving the unique id’s.

I see.  Once I moved my debug statement out of the for loop in getPlayerHand(), I realized the ID was changing every time in the for loop.

I did a bunch of rework of my code using the “factory” concept from develephant and I’ve made some progress.  I was able to create a “currentHand” table with unique identifiers which seem to stay with it.  Unfortunately my code was so sloppily written up until this point that it looks like I’ll need to rework a considerable amount of it.

I really appreciate all the help from you all. Thanks again!

Hi,

Starting fresh is just a fact of development sometimes. This is also another reason to use version control. I’ve shelved “ideas” after 100’s of hours. It’s painful, but there can be a point where it will take you longer to “fix” everything, than to just restart with a better framework in mind. And you’ll actually move faster, since you already know what doesn’t work.  Refactoring is a large part of coding (if you care enough).

With that in mind, I have been trying to post my “card game” framework notes and tips. Hopefully this weekend. There is a simple pattern that can be very effective for what you are doing, but I’ve been a little swamped lately to tidy it up.

Cheers.