One way to make Roulette using Javascript - Part 3

ozboware

ozboware

Posted on December 11, 2021

One way to make Roulette using Javascript - Part 3

Place your bets!

In part 2 we covered creating the wheel and getting it spinning. In this part we're going to make it into a game. In part 1 we already set the click events for each betting point on the table, now we're going to work on the setBet and spin functions. First we need to set some variables outside of any functions at the top of the script

let wager = 5;
let bet = [];
let numbersBet = [];
Enter fullscreen mode Exit fullscreen mode

Here we have a set wager, the bet is for an array of objects containing the numbers bet, the bet type, wager and payout odds. The numbersBet variable is for the array of numbers that have been betted on, each number will only be placed in once and this is checked against the winning number before the search for the bets begin.

Now we go back to the setBet function. Currently it looks like this

function setBet(n, t, o){
    console.log(n);
    console.log(t);
    console.log(o);
}
Enter fullscreen mode Exit fullscreen mode

we're going to change it to this

function setBet(n, t, o){
    var obj = {
        amt: wager,
        type: t,
        odds: o,
        numbers: n
    };
    bet.push(obj);
    let numArray = n.split(',').map(Number);
    for(i = 0; i < numArray.length; i++){
        if(!numbersBet.includes(numArray[i])){
            numbersBet.push(numArray[i]);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Broken down. Earlier we set the bet variable as an array ready for objects. Here, we're setting the object to be pushed into the array containing: the wager, bet type, odds and numbers bet on.

var obj = {
    amt: wager,
    type: t,
    odds: o,
    numbers: n
};
bet.push(obj);
Enter fullscreen mode Exit fullscreen mode

All of this was set for each betting point in part 1. We then split the numbers into an array

let numArray = n.split(',').map(Number);
Enter fullscreen mode Exit fullscreen mode

iterated through them and, if each iteration's number isn't already in the numbersBet array, add it to the array.

for(i = 0; i < numArray.length; i++){
    if(!numbersBet.includes(numArray[i])){
        numbersBet.push(numArray[i]);
    }
}
Enter fullscreen mode Exit fullscreen mode

Next we start the build on the spin function.

function spin(){}
Enter fullscreen mode Exit fullscreen mode

Inside that function, we'll begin by adding a log in the console for the numbers that have been bet on to see if the numbersBet push is working correctly

console.log(numbersBet);
Enter fullscreen mode Exit fullscreen mode

Then we'll set a randomly generated number between 0 & 36

var winningSpin = Math.floor(Math.random() * 36);
Enter fullscreen mode Exit fullscreen mode

Next we check to see if the numbersBet array contains the winning number

if(numbersBet.includes(winningSpin)){
    for(i = 0; i < bet.length; i++){
        var numArray = bet[i].numbers.split(',').map(Number);
        if(numArray.includes(winningSpin)){
            console.log(winningSpin);
            console.log('odds ' + bet[i].odds);
            console.log('payout ' + ((bet[i].odds * bet[i].amt) + bet[i].amt));
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Broken down. First, if the numbersBet array contains the random number chosen, we iterate through the bet array containing the betting information

for(i = 0; i < bet.length; i++){}
Enter fullscreen mode Exit fullscreen mode

In that loop, we then take the numbers property value and split it into an array

var numArray = bet[i].numbers.split(',').map(Number);
Enter fullscreen mode Exit fullscreen mode

if the array contains the winning number we get a log in the console of the winning number, the odds for the bet and the payout for the win.

if(numArray.includes(winningSpin)){
    console.log(winningSpin);
    console.log('odds ' + bet[i].odds);
    console.log('payout ' + ((bet[i].odds * bet[i].amt) + bet[i].amt));
}
Enter fullscreen mode Exit fullscreen mode

This will loop over until all the objects/bets have been checked in the bet array. Next, if the numbersBet array doesn't contain the random number we'll get a log in the console of the winning number and a no win message

else{
    console.log(winningSpin);
    console.log('no win');
}
Enter fullscreen mode Exit fullscreen mode

Finally, we wrap up by resetting the numbersBet and bet arrays

bet = [];
numbersBet = [];
Enter fullscreen mode Exit fullscreen mode

The full function so far should look like this

function spin(){
    console.log(numbersBet);
    var winningSpin = Math.floor(Math.random() * 36);

    if(numbersBet.includes(winningSpin)){
        for(i = 0; i < bet.length; i++){
            var numArray = bet[i].numbers.split(',').map(Number);
            if(numArray.includes(winningSpin)){
                console.log(winningSpin);
                console.log('odds ' + bet[i].odds);
                console.log('payout ' + ((bet[i].odds * bet[i].amt) + bet[i].amt));
            }
        }
    }else{
        console.log(winningSpin);
        console.log('no win');
    }

    bet = [];
    numbersBet = [];
}
Enter fullscreen mode Exit fullscreen mode

All that's left is to add in the spin button. Now we go back to the end of the buildBettingBoard function and we add the following

let spinBtn = document.createElement('div');
spinBtn.setAttribute('class', 'spinBtn');
spinBtn.innerText = 'spin';
spinBtn.onclick = function(){
    spin();
};
container.append(spinBtn);
Enter fullscreen mode Exit fullscreen mode

And give it some basic styling

.spinBtn{
    position: relative;
    top: 253px;
    font-size:28px;
    cursor:pointer
}
Enter fullscreen mode Exit fullscreen mode

Now, after you've refreshed the page, you'll be able to place bets and get an instant win/loss message in the console. Now the basic game logic is functioning, we can get to work designing the user interface. I thought the best place to start was building the chips which will just be elements with the class "chip" with a choice of colours depending on the size of the bet. So in the CSS we add in the following

.chip{
    width: 21px;
    height: 21px;
    background-color:#fff;
    border: 3px solid;
    border-radius: 100%;
    position:absolute;
}

.gold{
    border-color:gold;
}

.red{
    border-color:red;
}

.green{
    border-color:green;
}

.blue{
    border-color:blue;
}
Enter fullscreen mode Exit fullscreen mode

Next we want the chip to be placed on the table where the bet has been made so, within every call for setBet in the buildBettingBoard function we're going to add "this". So, for instance

setBet(num, 'outside_oerb', 1);
Enter fullscreen mode Exit fullscreen mode

will become

setBet(this, num, 'outside_oerb', 1);
Enter fullscreen mode Exit fullscreen mode

and the actual function

function setBet(n, t, o){}
Enter fullscreen mode Exit fullscreen mode

will become

function setBet(e, n, t, o){}
Enter fullscreen mode Exit fullscreen mode

then we're going to continue building on the setBet function. At the bottom of the setBet function we add the following

let chip = document.createElement('div');
chip.setAttribute('class', 'chip');
e.append(chip);
Enter fullscreen mode Exit fullscreen mode

We put this at the bottom of the function because we're going to have a number on the chip representing the bet and the colour of the chip to change depending on the bet amount. All that will come a little later. For now, the chips are being placed but they're not aligned correctly. Now we have to do some aligning. It should be relatively easy given the chips are being added to elements already in place. All we have to do on our stylsheet is call upon the parent element and chip and change its positioning, like such

.tt1_block .chip{
    margin-left: 19px;
    margin-top: -24px;
}

.number_block .chip{
    margin-left: 7px;
    margin-top: -24px;
}

.wlrtl .chip{
    margin-left: -9px;
    margin-top: 9px;
}

.cbbb .chip{
    margin-left: -4px;
    margin-top: -5px;
}

.ttbbetblock .chip{
    margin-left: -7px;
    margin-top: -8px;
}

#wlttb_top .chip{
    margin-left: -5px;
    margin-top: -8px;
}

.bbtoptwo .chip{
    margin-left: 108px;
    margin-top: -25px;
}

.number_0 .chip{
    margin-left: -8px;
    margin-top: -22px;
}

.bo3_block .chip{
    margin-left: 65px;
    margin-top: -26px;
}

.oto_block .chip{
    margin-left: 45px;
    margin-top: -25px;
}
Enter fullscreen mode Exit fullscreen mode

Now, when you click on a betting spot, the chip should be neatly aligned with the bet. Next, let's tidy up the board a bit and get rid of the betting spot borders. In the stylesheet go to .ttbbetblock and change it to

.ttbbetblock{
    width: 19.1px;
    height: 10px;
    position: relative;
    display: inline-block;
    margin-left: 22.52px;
    top: -2px;
    cursor:pointer;
    z-index:3;
}
Enter fullscreen mode Exit fullscreen mode

Just remove the border from ..rtlbb1, .rtlbb2, .rtlbb3 and with the corner bets, remove the border from .cbbb, change its margin-left to 22.52px and change the following elements

#cbbb_1, #cbbb_4, #cbbb_5, #cbbb_7, #cbbb_9, #cbbb_11, #cbbb_12, #cbbb_15, #cbbb_16, #cbbb_18, #cbbb_20, #cbbb_22{
    margin-left: 21px;
}

#cbbb_3, #cbbb_14{
    margin-left: 20.5px;
}

#cbbb_6, #cbbb_17{
    margin-left: 23px;
}

#cbbb_8, #cbbb_10, #cbbb_19, #cbbb_21{
    margin-left: 22px;
}
Enter fullscreen mode Exit fullscreen mode

Now the chips can be placed and the game is taking shape. We want the chips to disappear after the spin has taken place. For this I had to create a recursive function because, for whatever reason, either the Javascript .remove() property was only removing half of the elements at a time or the loop was iterating through only half of the elements, or it was a combination of the two

function removeChips(){
    var chips = document.getElementsByClassName('chip');
    if(chips.length > 0){
        for(i = 0; i < chips.length; i++){
            chips[i].remove();
        }
        removeChips();
    }
}
Enter fullscreen mode Exit fullscreen mode

All that's doing is checking for all the elements with the class name "chip" and if there are one or more it removes the elements, then calls back on itself and repeats the action until all of the elements have been removed. We then call on the function at the end of the spin function.

Now we want to slow things down a bit between the spin button being pushed and the results being shown. So we stop our wheel and ball from spinning by removing the animation properties from the wheel and balltrack classes. Now we can get to work on making it spin and land on the number randomly chosen. I began by placing all the numbers on the wheel in anti-clockwise order into a global array at the top of the script so it doesn't have to keep on being set

let wheelnumbersAC = [0, 26, 3, 35, 12, 28, 7, 29, 18, 22, 9, 31, 14, 20, 1, 33, 16, 24, 5, 10, 23, 8, 30, 11, 36, 13, 27, 6, 34, 17, 25, 2, 21, 4, 19, 15, 32];
Enter fullscreen mode Exit fullscreen mode

and added the variables for the wheel and ballTrack underneath the calls buildWheel and buildBettingBoard at the top of the script, again so they didn't have to keep on being set with each spin

let wheel = document.getElementsByClassName('wheel')[0];
let ballTrack = document.getElementsByClassName('ballTrack')[0];
Enter fullscreen mode Exit fullscreen mode

Then I created a new function called spinWheel

function spinWheel(winningSpin){}
Enter fullscreen mode Exit fullscreen mode

In this function I began by iterating over the wheelnumbersAC array and calculating the angle the ballTrack should stop at compared to the number that has been chosen

for(i = 0; i < wheelnumbersAC.length; i++){
    if(wheelnumbersAC[i] == winningSpin){
        var degree = (i * 9.73) + 362;
    }
}
Enter fullscreen mode Exit fullscreen mode

I added in an extra 362 degrees so the ball wouldn't just slowly crawl at the end to the numbers closest to zero. Then I added back in the animations we took away earlier

wheel.style.cssText = 'animation: wheelRotate 5s linear infinite;';
ballTrack.style.cssText = 'animation: ballRotate 1s linear infinite;';
Enter fullscreen mode Exit fullscreen mode

followed by 4 timeout functions which will slow and eventually stop the ball. First was the function set to run after 2 seconds

setTimeout(function(){
    ballTrack.style.cssText = 'animation: ballRotate 2s linear infinite;';
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerText = '@keyframes ballStop {from {transform: rotate(0deg);}to{transform: rotate(-'+degree+'deg);}}';
    document.head.appendChild(style);
}, 2000);
Enter fullscreen mode Exit fullscreen mode

This function is slowing the ball rotation down to half the speed as well as creating a new style to stop the ball in the next function. The "ballStop" keyframes here starts at 0deg but ends at minus the degree angle calculated at the beginning of the main function. Next, after a further 4 seconds or 6 seconds in total, I switched to the ballStop keyframes

setTimeout(function(){
    ballTrack.style.cssText = 'animation: ballStop 3s linear;';
}, 6000);
Enter fullscreen mode Exit fullscreen mode

As the ballTrack was set to 3 seconds, the next function would follow 3 seconds later or 9 seconds in total

setTimeout(function(){
    ballTrack.style.cssText = 'transform: rotate(-'+degree+'deg);';
}, 9000);
Enter fullscreen mode Exit fullscreen mode

This makes sure the ball stops at the angle we want it to instead of it zapping back to zero. Finally, after 10 seconds, we stop the wheel from rotating by removing its extra styling and we remove the ballStop keyframes style

setTimeout(function(){
    wheel.style.cssText = '';
    style.remove();
}, 10000);
Enter fullscreen mode Exit fullscreen mode

Then, underneath the winningSpin variable in the spin() function, we call on the spinWheel() function and wrap the rest of the function in a timeout set to 9 seconds.

function spin(){
    console.log(numbersBet);
    let winningSpin = Math.floor(Math.random() * 36);
    spinWheel(winningSpin);
    setTimeout(function(){
        if(numbersBet.includes(winningSpin)){
            for(i = 0; i < bet.length; i++){
                var numArray = bet[i].numbers.split(',').map(Number);
                if(numArray.includes(winningSpin)){
                    console.log(winningSpin);
                    console.log('odds ' + bet[i].odds);
                    console.log('payout ' + ((bet[i].odds * bet[i].amt) + bet[i].amt));
                }
            }
        }else{
            console.log(winningSpin);
            console.log('no win');
        }

        bet = [];
        numbersBet = [];
        removeChips();
    }, 9000);
}
Enter fullscreen mode Exit fullscreen mode

Now, when you refresh the page, after you've pressed "spin" the ball should appear to land on the number generated randomly and shown to you in the console.

The full code from part 1 up to this point is available on the Codepen demo here.

The full code for this part

Javascript

let wager = 5;
let bet = [];
let numbersBet = [];

let wheelnumbersAC = [0, 26, 3, 35, 12, 28, 7, 29, 18, 22, 9, 31, 14, 20, 1, 33, 16, 24, 5, 10, 23, 8, 30, 11, 36, 13, 27, 6, 34, 17, 25, 2, 21, 4, 19, 15, 32];

let wheel = document.getElementsByClassName('wheel')[0];
let ballTrack = document.getElementsByClassName('ballTrack')[0];

function setBet(e, n, t, o){
    var obj = {
        amt: wager,
        type: t,
        odds: o,
        numbers: n
    };
    bet.push(obj);
    let numArray = n.split(',').map(Number);
    for(i = 0; i < numArray.length; i++){
        if(!numbersBet.includes(numArray[i])){
            numbersBet.push(numArray[i]);
        }
    }
    let chip = document.createElement('div');
    chip.setAttribute('class', 'chip');
    e.append(chip);
}

function spin(){
    console.log(numbersBet);
    var winningSpin = Math.floor(Math.random() * 36);
    spinWheel(winningSpin);
    setTimeout(function(){
        if(numbersBet.includes(winningSpin)){
            for(i = 0; i < bet.length; i++){
                var numArray = bet[i].numbers.split(',').map(Number);
                if(numArray.includes(winningSpin)){
                    console.log(winningSpin);
                    console.log('odds ' + bet[i].odds);
                    console.log('payout ' + ((bet[i].odds * bet[i].amt) + bet[i].amt));
                }
            }
        }else{
            console.log(winningSpin);
            console.log('no win');
        }

        bet = [];
        numbersBet = [];
        removeChips();
    }, 3000);
}

function spinWheel(winningSpin){
    for(i = 0; i < wheelnumbersAC.length; i++){
        if(wheelnumbersAC[i] == winningSpin){
            var degree = (i * 9.73) + 362;
        }
    }
    wheel.style.cssText = 'animation: wheelRotate 5s linear infinite;';
    ballTrack.style.cssText = 'animation: ballRotate 1s linear infinite;';

    setTimeout(function(){
        ballTrack.style.cssText = 'animation: ballRotate 2s linear infinite;';
        style = document.createElement('style');
        style.type = 'text/css';
        style.innerText = '@keyframes ballStop {from {transform: rotate(0deg);}to{transform: rotate(-'+degree+'deg);}}';
        document.head.appendChild(style);
    }, 2000);
    setTimeout(function(){
        ballTrack.style.cssText = 'animation: ballStop 3s linear;';
    }, 6000);
    setTimeout(function(){
        ballTrack.style.cssText = 'transform: rotate(-'+degree+'deg);';
    }, 9000);
    setTimeout(function(){
        wheel.style.cssText = '';
        style.remove();
    }, 10000);
}

function removeChips(){
    var chips = document.getElementsByClassName('chip');
    if(chips.length > 0){
        for(i = 0; i < chips.length; i++){
            chips[i].remove();
        }
        removeChips();
    }
}
Enter fullscreen mode Exit fullscreen mode

css

.spinBtn{
    position: relative;
    top: 253px;
    font-size:28px;
    cursor:pointer
}
.chip{
    width: 21px;
    height: 21px;
    background-color:#fff;
    border: 3px solid;
    border-radius: 100%;
    position:absolute;
}

.gold{
    border-color:gold;
}

.red{
    border-color:red;
}

.green{
    border-color:green;
}

.blue{
    border-color:blue;
}


.tt1_block .chip{
    margin-left: 19px;
    margin-top: -24px;
}

.number_block .chip{
    margin-left: 7px;
    margin-top: -24px;
}

.wlrtl .chip{
    margin-left: -9px;
    margin-top: 9px;
}

.cbbb .chip{
    margin-left: -4px;
    margin-top: -5px;
}

.ttbbetblock .chip{
    margin-left: -7px;
    margin-top: -8px;
}

#wlttb_top .chip{
    margin-left: -5px;
    margin-top: -8px;
}

.bbtoptwo .chip{
    margin-left: 108px;
    margin-top: -25px;
}

.number_0 .chip{
    margin-left: -8px;
    margin-top: -22px;
}

.bo3_block .chip{
    margin-left: 65px;
    margin-top: -26px;
}

.oto_block .chip{
    margin-left: 45px;
    margin-top: -25px;
}

.ttbbetblock{
    width: 19.1px;
    height: 10px;
    position: relative;
    display: inline-block;
    margin-left: 22.52px;
    top: -2px;
    cursor:pointer;
    z-index:3;
}

#cbbb_1, #cbbb_4, #cbbb_5, #cbbb_7, #cbbb_9, #cbbb_11, #cbbb_12, #cbbb_15, #cbbb_16, #cbbb_18, #cbbb_20, #cbbb_22{
    margin-left: 21px;
}

#cbbb_3, #cbbb_14{
    margin-left: 20.5px;
}

#cbbb_6, #cbbb_17{
    margin-left: 23px;
}

#cbbb_8, #cbbb_10, #cbbb_19, #cbbb_21{
    margin-left: 22px;
}
Enter fullscreen mode Exit fullscreen mode

That's it for this part. You can now place bets, lay chips, spin the wheel, have the ball land on a designated number and see your win/loss in the console. In the next and final part, we'll be wrapping up by styling the table, tidying up the bets, repositioning the spin button, adding numbers on the chips, preventing the chip elements from being set multiple times, adding in the ability to change bet value, remove bets from the table, view win/loss messages on the screen and remove all the console logs

💖 💪 🙅 🚩
ozboware
ozboware

Posted on December 11, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related