Saturday, August 14, 2021

Making Lucky Scratch

 While the new version of Lucky Scratch is more of a re-write than a port, it does use the artwork from the original. The idea of having a scratch ticket that always won is probably not that original of an idea but creating this game is a good reminder of how real scratch and win tickets are created. While different lottery corporations probably have slightly different ways of generating scratch and win tickets, they are all very likely to be similar to the method I am describing, so if you know somebody who is hooked on scratch tickets, you may want to share this article with them.

A batch of scratch and win tickets would have a set number of tickets to be generated, with a pre-determined payout. The lottery corporation knows exactly how many tickets are going to win prizes and how many of each potential prize will be won. This is controlled in such a way so that there are enough winning tickets to keep people buying tickets but that the amount made from ticket sales will meet profit targets. It should go without saying that more money goes to the lottery corporation than will go to the players. In BC, the profits go to paying the executives that run the corporation with the remaining going to charity. The flowchart below shows how tickets are generated in general, though details may vary.



For my lucky scratch variation, all tickets were winning tickets with equal odds of any given prize. This greatly simplifies the coding but having a table of different prizes and picking from that table isn’t that much more work. For tickets that have multiple scratch areas, you would simply have tables for each possible scratch area and the “can win multiple times on a ticket” message. The total prizes are still known but instead of divided into a single pool, you would divide the prizes between the different games on the ticket.

Does this mean that you shouldn’t buy scratch tickets? Personally, even though I know it is a waste of money, I do on the vary rare occasion buy a ticket. This tends to be when I am in a bad luck streak as an homage to Martingale, with the hope that my bad luck streak will come to an end with a winning ticket.

Generating the ticket is simple enough. The more interesting part of Lucky Scratch, at least from a programming perspective,   is being able to scratch the ticket. This is a fairly simple thing to do if you don’t mind manually generating images. The JavaScript Canvas API makes creating images programmatically easy (though with all the array accesses you are doing it is probably very inefficient). We simply create an image large enough to cover the results of the scratch ticket. This is done by using the createImageData method that returns an object that holds the image data.

 

class ScratchCover extends SLLCanvasLayer {
   
constructor() {
       
super("scratch", 300, 200);
        this
.imageData = this.ctx.createImageData(300,200);
        this
.resetCover();
   
}

 

The cover is set to a dark grey, though certainly you could use an image then create the image data from the image using the getImageData method. Building an image from scratch is simply a matter of setting the bytes that make up the image to the appropriate color values. These are in a single dimensional array with each pixel taking 4 bytes in red, green, blue, alpha order.


   
resetCover() {
       
let index = 0;
        for
(let pixel = 0; pixel < 300*200; ++pixel) {
           
this.imageData.data[index++] = 80;
            this
.imageData.data[index++] = 80;
            this
.imageData.data[index++] = 80;
            this
.imageData.data[index++] = 255;
       
}
    }

To reveal a portion of the ticket, we simply find where the mouse pointer is on the cover, then unveil a 16x16 chunk of the image below by simply setting the color in the square to black with no transparency. Manually drawing filled squares is trivial enough as you only need to find the top-left corner and draw the pixels consecutively for the length of the square and repeat for each line of the square. The length and width of the square may need to be adjusted if the square is along the edge of the image being drawn, but the end bounds can be calculated by simply using the min and max methods. For top/left boundaries, you want max with the value 0 used to prevent values outside the boundary. For right/bottom you use min with the width/height of the bounds used to form the clipping area.

    mouseMove(x, y) {
       
var real = this.findRealPosition();
        if
(real.containsCoordinate(x,y)) {
           
for (let px = Math.max(0, x - real.x - 8); px < Math.min(300, x - real.x + 8); ++px)
               
for (let py = Math.max(0, y - real.y - 8); py < Math.min(200, y - real.y + 8); ++py) {
                   
let index = py*1200+px*4;
                    this
.imageData.data[index++] = 0;
                    this
.imageData.data[index++] = 0;
                    this
.imageData.data[index++] = 0;
                    this
.imageData.data[index++] = 0;
               
}
        }

       
return true;
   
}

 

Drawing the cover to the canvas is done using the putImageData method. This would normally use the coordinates that you wish to draw to, but as I am using my SLL library I need to use the 0,0 coordinate to indicate the top left as the layer library automatically will adjust where things are drawn to when rendering.


   
renderCanvas() {
       
this.ctx.clearRect(0,0, this.canvas.width, this.canvas.height);
        this
.ctx.putImageData(this.imageData,0,0);
   
}
}

 

And that is all that is required to create a scratchable areas.

No comments:

Post a Comment