Roulette - Maybe some graphics.

I found a picture of a roulette wheel. I wonder how hard it would be to make my little game use it.

I did a little research into how to import an image into Codea Lua and display it. The trick I used was to copy the image to the iPad clipboard, and then execute this line of code:

Image=pasteboard.imagesaveImage("Documents:Roulette",Image)

That does more or less what you’d expect. Then I went on to do this:

-- S2ratch-- Use this function to perform your initial setupfunctionsetup()-- Image = pasteboard.image-- saveImage("Documents:Roulette", Image)Image=readImage("Documents:Roulette")angle=0end-- This function gets called once every framefunctiondraw()-- This sets a dark background color background(40,40,50)-- This sets the line thicknessstrokeWidth(5)translate(WIDTH/2,HEIGHT/2)rotate(angle)sprite(Image)angle=angle+1end

Which results in this:

The Wheel

So that’s pretty fine, we’re nearly done. I think I’ll start using this in the R3oulette program, the tiny one I did last time. How to proceed?

First, I’ll just add the wheel, and display the answer on top of it. I think I’ll just display the final result, with the text gone, right in the middle of the wheel. Maybe I can make the text bigger. Here goes …

-- R3oulettefunctionsetup()Spinning=falseSlot=36Time=0Image=readImage("Documents:Roulette")endfunctiontouched(ignored)Spinning=trueTime=ElapsedTimeend-- This function gets called once every framefunctiondraw()ifSpinningandElapsedTime-Time>5thenSlot=math.random(0,36)Spinning=falseendbackground(40,40,50)strokeWidth(2)translate(WIDTH/2,HEIGHT/2)sprite(Image)ifnotSpinningthentext(""..Slot)elsetext(""..math.random(0,36))endend

This displays the wheel, shows a number changing rapidly while “spinning”, and shows the final slot when done. The wheel isn’t spinning, and I have a good reason for that: I want to display the ball in the proper slot.

Wow, looking at the wheel, I notice for the first time that it is one with two zeros, 0 and 00. I could look for a better graphic but let’s just deal with that issue later. Anyway there are 38 slots around the wheel. Looks to me as if the slot at angle zero (due right, if I am not mistaken) is slot 3. Let’s see if we can display a ball at that location. First, I’d like to just know how big the wheel is. I could perhaps measure it or find out in the original file. But wait, the sprite function can set height and width. I’ll just do this:

sprite(Image,0,0,500,500)

And now I can guess that the wheel is about 250 pixels in radius:

I’ll try to put a ball at the location of the 3. I expect I’ll need to adjust the radius a bit to get it where I want it. First cut:

We can barely see it. It needs to be larger and it needs to be filled. And it should of course be at a somewhat smaller radius than 250:

fill(255)localballpos=vec2(240,0)ellipse(ballpos.x,ballpos.y,20)

Yes, well. Now that I look at it, I see that the ball should rest, not on top of the number, but in that little square slot nearer to the center of the wheel. I’ll just fiddle till I like it: local ballpos = vec2(190,0) does the job:

Now we need to position the ball in the right slot. If the slots were in order, this would amount to rotating our direction vector by 1/38 of the circle (two pi) for each uptick in the slot. But they are not in order, so we need to get the wheel slot given what the ball value is supposed to be. I think we’ll just want to look it up in a list, which I’ll type in by hand, going around the wheel.

Sweet. I notice that while it’s running, the ball just sits where it was. We can condition the ball display on Spinning easily enough:

Ball vanishes when “spinning”

Wow that works just fine. Now what? Well, the wheel is supposed to spin. So let’s make it spin and make the ball spin with it when the ball is visible. I’ll need to move the wheel rotation code over from the previous version. And I think, since we rotated the whole screen view, it might just work. We’ll see.

(Angle is initialized to 0 up in setup()). This works fine for the ball display, as you see here:

Rotating but no text

But the text display has disappeared. Why? I moved the sprite down to be inside the rotate logic and the text was then drawn first and overwritten. We’ll have to move it down. However then it will be rotated, so we’ll have to push and pop the matrix:

Sure enough there are two 24s in there. Looking at the wheel, I see that the first one should be 34. Fixed. And I got really lucky on my first spin: 24 came up and went to the right place. I better buy a lottery ticket today. Anyway, how can we check this table? As it stands right now, I expect no duplicates except for zero, and I expect all the values to be between 0 and 36. The latter is easy. How can I readily check the duplicates? Well, if the table size is 38 and every integer appears in it, then it is covered. But with the double zero it is possible, in principle, that 31 is duplicated rather than zero. I think I’ll ignore that and just test that I can find each number from zero to 36. For now: we’ve still not dealt with the double zero.

That prints 38 in the text window and doesn’t print anything about could not find, so we’re in good shape. I think we’re nearly done here. I see two issues I’d like to address: the double zero, and cleaning up the code. Here’s my cunning plan for double zero: roll -1 through 36 rather than 0 through 36, and change the 00 in our table to -1. That will make the -1 roll display as 00. We’ll have to fix the text display too, of course. A bit of a hack but maybe not too terrible. Let’s see. Here’s all the code:

-- R3oulettefunctionsetup()Spinning=falseSlot=36Time=0Image=readImage("Documents:Roulette")Wheel={3,15,34,22,5,17,32,20,7,11,30,26,9,28,0,2,14,35,23,4,16,33,21,6,18,31,19,8,12,29,25,10,27,-1,1,13,36,24}Angle=0checkWheel()endfunctiontouched(ignored)Spinning=trueTime=ElapsedTimeend-- This function gets called once every framefunctiondraw()ifSpinningandElapsedTime-Time>5thenSlot=math.random(-1,36)Spinning=falseendbackground(40,40,50)strokeWidth(2)translate(WIDTH/2,HEIGHT/2)pushMatrix()rotate(Angle)Angle=Angle+1sprite(Image,0,0,500,500)fill(255)ifnotSpinningthenlocalballpos=vec2(190,0)localangle=angleOf(Slot)ballpos=ballpos:rotate(angle)ellipse(ballpos.x,ballpos.y,20)endpopMatrix()ifnotSpinningthentext(""..display(Slot))elsetext(""..display(math.random(-1,36)))endendfunctionangleOf(aSlot)fori,vinpairs(Wheel)doifaSlot==vthenreturnmath.pi*2/38.0*(i-1)endendreturnnilendfunctioncheckWheel()locallen=0fori,_inpairs(Wheel)dolen=len+1endprint(len)forslot=0,36dolen=len+1ifnotangleOf(slot)thenprint("could not find "..slot)endendendfunctiondisplay(aSlot)ifaSlot==-1thenreturn"00"elsereturn""..aSlotendend

I added the display function, put the -1 in the Wheel table, and to test it, I jammed Slot = -1 into the calculation of slot, so that every roll came up double-zero:

Forcing Double Zero

While doing that I noticed the duplication of the call to the random number generator, which needed to be changed twice, once for the answer and once while spinning. As I think about that, we could of course set Slot on every cycle, since we’re displaying a number on every cycle but maybe we’ll save that.

The draw() function is pretty messy now. We should do Composed Function on that and pull out some bits. I see three of note: the calculation of wheel state, drawing the wheel, and drawing the text. Then there’s the actual fussing about with background, strokes and fills.

I did that one Extract Function at a time. I decided to pull out the graphics setup, just to see if I liked it, and I moved the fill() call in there. The result is this:

-- R3oulettefunctionsetup()Spinning=falseSlot=36Time=0Image=readImage("Documents:Roulette")Wheel={3,15,34,22,5,17,32,20,7,11,30,26,9,28,0,2,14,35,23,4,16,33,21,6,18,31,19,8,12,29,25,10,27,-1,1,13,36,24}Angle=0checkWheel()endfunctiontouched(ignored)Spinning=trueTime=ElapsedTimeend-- This function gets called once every framefunctiondraw()setGraphics()calculate()drawWheel()drawText()endfunctionangleOf(aSlot)fori,vinpairs(Wheel)doifaSlot==vthenreturnmath.pi*2/38.0*(i-1)endendreturnnilendfunctioncalculate()ifSpinningandElapsedTime-Time>5thenSlot=math.random(-1,36)Spinning=falseendendfunctioncheckWheel()locallen=0fori,_inpairs(Wheel)dolen=len+1endprint(len)forslot=0,36dolen=len+1ifnotangleOf(slot)thenprint("could not find "..slot)endendendfunctiondisplay(aSlot)ifaSlot==-1thenreturn"00"elsereturn""..aSlotendendfunctiondrawText()ifnotSpinningthentext(""..display(Slot))elsetext(""..display(math.random(-1,36)))endendfunctiondrawWheel()pushMatrix()rotate(Angle)Angle=Angle+1sprite(Image,0,0,500,500)ifnotSpinningthenlocalballpos=vec2(190,0)localangle=angleOf(Slot)ballpos=ballpos:rotate(angle)ellipse(ballpos.x,ballpos.y,20)endpopMatrix()endfunctionsetGraphics()background(40,40,50)strokeWidth(2)translate(WIDTH/2,HEIGHT/2)fill(255)end

Final Run

Frankly, that doesn’t look bad to me. How does it look to you?

Next time, I’ll sum up this series, at least so far. I think I’m done with roulette but you never know. Thanks for reading!