Table entry order

Discuss any general programming issues here
Post Reply
Mazze
Posts: 69
Joined: Thu May 06, 2010 8:08 pm

Table entry order

Post by Mazze »

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?
User avatar
airsoftsoftwair
Posts: 5446
Joined: Fri Feb 12, 2010 2:33 pm
Location: Germany
Contact:

Re: Table entry order

Post by airsoftsoftwair »

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.
Bugala
Posts: 1180
Joined: Sun Feb 14, 2010 7:11 pm

Re: Table entry order

Post by Bugala »

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?
jalih
Posts: 276
Joined: Fri Jun 18, 2010 8:08 pm
Location: Finland

Re: Table entry order

Post by jalih »

Bugala wrote: Hence, is there any easy solution to this problem, or do i have to do some hack?
Do you really need ForEach() iterator for this task? In old days we just used arrays and loops, it was simple and worked well. ;)

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()
Filled rectangles in the above example could easily be changed to card images without hardly any changes to code.

Let me know, if you need any help!
Bugala
Posts: 1180
Joined: Sun Feb 14, 2010 7:11 pm

Re: Table entry order

Post by Bugala »

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.
jalih
Posts: 276
Joined: Fri Jun 18, 2010 8:08 pm
Location: Finland

Re: Table entry order

Post by jalih »

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.
Well, there are 52 different cards in a deck so numbers are natural fit for the job.

I added card images for the second example and fixed a couple of my silly errors.
Bugala
Posts: 1180
Joined: Sun Feb 14, 2010 7:11 pm

Re: Table entry order

Post by Bugala »

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.
jalih
Posts: 276
Joined: Fri Jun 18, 2010 8:08 pm
Location: Finland

Re: Table entry order

Post by jalih »

Bugala wrote: Hence there are quite many different situations where to put wrong number and cause a bug.
Take a look at the demo.

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.
Bugala
Posts: 1180
Joined: Sun Feb 14, 2010 7:11 pm

Re: Table entry order

Post by Bugala »

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.
Post Reply