This sample
table = {11,12,13,14}
ForEach(table, DebugPrint)
prints
1 12
2 13
3 14
0 11
I'm surprised that the first entry is printed at the end. Is this expected behavior?
Table entry order
- airsoftsoftwair
- Posts: 5446
- Joined: Fri Feb 12, 2010 2:33 pm
- Location: Germany
- Contact:
Re: Table entry order
That's normal. Lua has the strange habit of having tables start at index 1. Thus, index 0 is a "special case", like index -10 for example. Hollywood hacks this behaviour to have tables start at 0 because everything else is too uncommon for me even if it might make sense from a logical point of view to have tables start at 1 but everybody is used to that they start at 0 so that's why I hacked lua to behave normally and let tables start at 0. Internally, they're still treated in the lua way, though. This is why index 0 appears at the end.
Re: Table entry order
Is there any way to make foreach start from 0?
I have in my program a sitaution which might be easiest to explain by using poker game as example.
In normal poker game you have chance to change some of your cards in hand.
In my program this scneario works so that you click on some of the cards to "activate" them. Then when you click the done button, it will go through a table that contains all the cards in your hand using foreach command.
Inside this foreach command it check if card is "active". If it is, then it discards that card.
And now comes the important part.
The thing is, my cards are in row. If card is discarded from middle, then there will be emptry space, and i need to move cards a bit to get this done.
Thing is, my cards are in order. Index 0 is left most card, index 1 is the next one to the right of that one and so on. By otherwords, smallest index is leftmost card, and biggest index is rightmost card.
Hence I can use that foreach command to go through every card only once by first looking for the card that is "active". If it hits "active" card, it discards it, and now there is empty spot.
Since next card will be on right of that empty spot, I will simply move it left for example 150 pixels. I repeat this through all the remaining indexes, and endresult is that there is no more empty spot, but all cards are nicely in row again.
Except, now comes the index 0 problem. If i execute it this way, then index 0 will be executed last, meaning that everytime card is discarded, index 0 card (leftmost card) would move to the left a bit.
Similarly, if index 0 card is the card to be discarded, then none of the other cards will be moved, since 0 was checked last.
There are work arounds to this of course, like executing the lines independetly for index 0 before going to foreach loop, and in foreach loop ignoring index 0 then. But all in all, none of them is very good, and it would be better to be able to have more elegant solution, like choosing to execute index 0 first.
Hence, is there any easy solution to this problem, or do i have to do some hack?
I have in my program a sitaution which might be easiest to explain by using poker game as example.
In normal poker game you have chance to change some of your cards in hand.
In my program this scneario works so that you click on some of the cards to "activate" them. Then when you click the done button, it will go through a table that contains all the cards in your hand using foreach command.
Inside this foreach command it check if card is "active". If it is, then it discards that card.
And now comes the important part.
The thing is, my cards are in row. If card is discarded from middle, then there will be emptry space, and i need to move cards a bit to get this done.
Thing is, my cards are in order. Index 0 is left most card, index 1 is the next one to the right of that one and so on. By otherwords, smallest index is leftmost card, and biggest index is rightmost card.
Hence I can use that foreach command to go through every card only once by first looking for the card that is "active". If it hits "active" card, it discards it, and now there is empty spot.
Since next card will be on right of that empty spot, I will simply move it left for example 150 pixels. I repeat this through all the remaining indexes, and endresult is that there is no more empty spot, but all cards are nicely in row again.
Except, now comes the index 0 problem. If i execute it this way, then index 0 will be executed last, meaning that everytime card is discarded, index 0 card (leftmost card) would move to the left a bit.
Similarly, if index 0 card is the card to be discarded, then none of the other cards will be moved, since 0 was checked last.
There are work arounds to this of course, like executing the lines independetly for index 0 before going to foreach loop, and in foreach loop ignoring index 0 then. But all in all, none of them is very good, and it would be better to be able to have more elegant solution, like choosing to execute index 0 first.
Hence, is there any easy solution to this problem, or do i have to do some hack?
Re: Table entry order
Do you really need ForEach() iterator for this task? In old days we just used arrays and loops, it was simple and worked well.Bugala wrote: Hence, is there any easy solution to this problem, or do i have to do some hack?
Some ideas:
- Store your card hand into table (use table like a 0-based array).
- If card is discarded, chance its value to 0
As cards are rectangular objects, my framework has some nice features for handling them. Below is a simple example.
Code: Select all
Const #SCREENW = 800
Const #SCREENH = 600
@DISPLAY { Width = #SCREENW, Height = #SCREENH, Title = "Test Proggy"}
; Include game framework
@INCLUDE "HGF/primitives/primitives.hws"
@INCLUDE "HGF/primitives/HGFdraw.hws"
screen = HGFdraw:new()
; Card rectangle
cardr = Rect:new( { min = Point:new(), max = { x = 100, y = 140 } } )
; Number zero means the card is discarded
hand = { 2, 0, 4, 10, 0 }
; Starting postion for the card hand and padding between cards
startpos = Point:new( { x = 100, y = 100 } )
padding = Point:new( { x = cardr:dx() + 20, y = 0 } )
SetFillStyle(#FILLCOLOR)
; Draw full hand
For Local i = 0 to 4
screen:drawRect(cardr:addpt(startpos):addpt(padding:mul(i)), #RED)
Next
WaitLeftMouse()
Cls()
; Draw only cards that exist (with holes)
For Local i = 0 to 4
If hand[i] <> 0 Then screen:drawRect(cardr:addpt(startpos):addpt(padding:mul(i)), #RED)
Next
WaitLeftMouse()
Cls()
; Draw only cards that exist (without holes)
Local c = 0
For Local i = 0 to 4
If hand[i] <> 0
screen:drawRect(cardr:addpt(startpos):addpt(padding:mul(c)), #RED)
c = c + 1
EndIf
Next
WaitLeftMouse()
Let me know, if you need any help!
Re: Table entry order
Thanks for help again.
I actually solved the problem by adding one extra item called "empty" to the table at location 0 and told the foreach loop to return if value = "empty" hence skipping the zero completely, and having the real zero being executed as first.
Then after the foreach loop had been done, i removed the 0 item and that way had the table back to what its supposed to be.
I was thinking of for loop as well, but problem with that approach is that first of all i have learned from experience that human errors making bugs (thinking wrong of from n1 to n2) increases and there can also happen some other problems, like say you first use listitems(table) to get the amount of items to go through, and then you start going on from index location 0 to forward. but problem is, your index numbers are actually from 0 to 10, instead of 0 to 9, since index location 7 doesnt exist anymore for one reason or other, and in this case for loop again fails, while foreach doesnt, since foreach is able to skip nonexisting indexes.
so while in this case I could have solved the problem with for loop, as list should always be from 0 to x including all the indexes, i was for constistencys sake wanting to use foreach. Also, because I am trying to avoid having to deal with numbers as much as possible.
For example, I dont keep list of button numbers currently at all. I just keep list of layer names, and since these buttons are attached to these layers, when i delete a layer, so does the button go away. Similarly I dont keep layer numbers up, but just their names, and i keep keeping these names in diffent tables, like poker example could be: table_deck, table_discardpile, table_inhand.
But thanks from tip anyway!
edit:
almost forgot to mention that i came to same conclusion that i should have done the whole hand card drawing differently in the first place. Now I am basically independently moving each card layer to its place, but what i should have done had been your system where it would render the whole hand each time. This would also have saved me the trouble of moving those cards in that foreach loop, and also made having save/load game much easier, as i could have simply loaded the current state of game, and told it to render the current hand.
I actually solved the problem by adding one extra item called "empty" to the table at location 0 and told the foreach loop to return if value = "empty" hence skipping the zero completely, and having the real zero being executed as first.
Then after the foreach loop had been done, i removed the 0 item and that way had the table back to what its supposed to be.
I was thinking of for loop as well, but problem with that approach is that first of all i have learned from experience that human errors making bugs (thinking wrong of from n1 to n2) increases and there can also happen some other problems, like say you first use listitems(table) to get the amount of items to go through, and then you start going on from index location 0 to forward. but problem is, your index numbers are actually from 0 to 10, instead of 0 to 9, since index location 7 doesnt exist anymore for one reason or other, and in this case for loop again fails, while foreach doesnt, since foreach is able to skip nonexisting indexes.
so while in this case I could have solved the problem with for loop, as list should always be from 0 to x including all the indexes, i was for constistencys sake wanting to use foreach. Also, because I am trying to avoid having to deal with numbers as much as possible.
For example, I dont keep list of button numbers currently at all. I just keep list of layer names, and since these buttons are attached to these layers, when i delete a layer, so does the button go away. Similarly I dont keep layer numbers up, but just their names, and i keep keeping these names in diffent tables, like poker example could be: table_deck, table_discardpile, table_inhand.
But thanks from tip anyway!
edit:
almost forgot to mention that i came to same conclusion that i should have done the whole hand card drawing differently in the first place. Now I am basically independently moving each card layer to its place, but what i should have done had been your system where it would render the whole hand each time. This would also have saved me the trouble of moving those cards in that foreach loop, and also made having save/load game much easier, as i could have simply loaded the current state of game, and told it to render the current hand.
Re: Table entry order
Well, there are 52 different cards in a deck so numbers are natural fit for the job.Bugala wrote:
so while in this case I could have solved the problem with for loop, as list should always be from 0 to x including all the indexes, i was for constistencys sake wanting to use foreach. Also, because I am trying to avoid having to deal with numbers as much as possible.
I added card images for the second example and fixed a couple of my silly errors.
Re: Table entry order
I was just using poker as example to make my question more uinderstandable. I am actually having 5 different kind of decks, and in addition, some of the cards can be in deck, hand, active in play, or discarded. Hence there are quite many different situations where to put wrong number and cause a bug.
Re: Table entry order
Take a look at the demo.Bugala wrote: Hence there are quite many different situations where to put wrong number and cause a bug.
I wrote a Deck "class" to help out:
Code: Select all
Const #SCREENW = 800
Const #SCREENH = 600
@DISPLAY { Width = #SCREENW, Height = #SCREENH, Title = "Test Proggy"}
; Include game framework
@INCLUDE "HGF/primitives/primitives.hws"
@INCLUDE "HGF/primitives/HGFdraw.hws"
@BRUSH 1, "data/cards.png", { Hardware = True, LoadAlpha = True }
Const #SPADES = 1
Const #HEARTS = 2
Const #DIAMONDS = 3
Const #CLUBS = 4
Deck = {}
Deck.cards = {}
Deck.numcards = 52
Deck.cardr = Rect:new( { min = Point:new(), max = { x = 78, y = 122 } } )
Deck.img = Image:new(1)
Function Deck:new()
Local d = {}
SetMetaTable(d, self)
self.__index = self
self:init()
self:shuffle()
Return(d)
EndFunction
Function Deck:init()
self.numcards = 52
For Local i = 1 To 52
self.cards[i] = i
Next
EndFunction
Function Deck:shuffle()
For Local i = 52 To 1 Step -1
Local j = Rnd(i) + 1
self.cards[j], self.cards[i] = self.cards[i], self.cards[j]
Next
EndFunction
; Return card suit
Function Deck:suit(card)
If card >= 1 And card <= 13 Then Return(#CLUBS)
If card >= 14 And card <= 26 Then Return(#DIAMONDS)
If card >= 27 And card <= 39 Then Return(#HEARTS)
If card >= 40 And card <= 52 Then Return(#SPADES)
EndFunction
; Return card value (1 - 13)
Function Deck:value(card)
If self:suit(card) = #CLUBS Then Return(card - 1 + 1)
If self:suit(card) = #DIAMONDS Then Return(card - 14 + 1)
If self:suit(card) = #HEARTS Then Return(card - 27 + 1)
If self:suit(card) = #SPADES Then Return(card - 40 + 1)
EndFunction
; Deal one card from the deck
Function Deck:dealCard()
If self.numcards <= 0 Then Return(0) ; Return empty deck condition
Local n = self.cards[self.numcards]
self.cards[self.numcards] = 0 ; Remove card
self.numcards = self.numcards - 1
Return(n)
EndFunction
; Draw card
Function Deck:drawCard(screen, pos, n)
screen:drawImg(self.cardr:addpt(pos), self.img, Point:new():add( { x = Int(Mod(n-1, 13)*78+Mod(n-1, 13)), y = Int(Int((n-1)/13)*122)+Int((n-1)/13) } ))
EndFunction
; draw back of the card
Function Deck:drawCardBack(screen, pos)
screen:drawImg(self.cardr:addpt(pos), self.img, Point:new():add( { x = Int(Mod(55-1, 13)*78+Mod(55-1, 13)), y = Int(Int((55-1)/13)*122)+Int((55-1)/13) } ))
EndFunction
; Draw the card deck presentation
Function Deck:drawCardDeck(screen, pos)
Local pad = Point:new({ x = 2, y = 2 })
For Local i = 1 To self.numcards
screen:drawImg(self.cardr:addpt(pos), self.img, Point:new():add( { x = Int(Mod(55-1, 13)*78+Mod(55-1, 13)), y = Int(Int((55-1)/13)*122)+Int((55-1)/13) } ))
pos = pos:add(pad)
Next
EndFunction
;*****************************************************************************************************************************************************************
;
; Demo starts here
;
; Create a new shuffled card deck. Deal two hands of cards, draw card deck and show two dealed hands.
;
screen = HGFdraw:new()
d = Deck:new()
hand1 = {}
hand2 = {}
hand1[1] = d:dealCard()
hand2[1] = d:dealCard()
hand1[2] = d:dealCard()
hand2[2] = d:dealCard()
hand1[3] = d:dealCard()
hand2[3] = d:dealCard()
hand1[4] = d:dealCard()
hand2[4] = d:dealCard()
hand1[5] = d:dealCard()
hand2[5] = d:dealCard()
d:drawCardDeck(screen, Point:new( { x = 20, y = 20 } ) )
pos1 = Point:new( { x = 200, y = 240 } )
pos2 = Point:new( { x = 200, y = 400 } )
pad = Point:new( { x = d.cardr:dx() + 20, y = 0 } )
For Local i = 1 To 5
d:drawCard(screen, pos1:add(pad:mul(i-1)), hand1[i])
Next
For Local i = 1 To 5
d:drawCard(screen, pos2:add(pad:mul(i-1)), hand2[i])
Next
WaitLeftMouse()
Last edited by jalih on Sun Apr 05, 2015 8:26 pm, edited 1 time in total.
Re: Table entry order
darn.
Should have asked a tip from forum before i started doing anything.
I think I could have saved a lot of trouble.
At this point It is probably easier to stick what i have done than to change to this one anymore.
Should have asked a tip from forum before i started doing anything.
I think I could have saved a lot of trouble.
At this point It is probably easier to stick what i have done than to change to this one anymore.