One way to make Roulette using Javascript - Part 3
Tags: javascript, webdev, css, html
Published: 2021-12-11 16:12:18

##Place your bets!

In [part 2](https://dev.to/ozboware/one-way-to-make-roulette-using-javascript-part-2-16a7) we covered creating the wheel and getting it spinning. In this part we're going to make it into a game. In [part 1](https://dev.to/ozboware/one-way-to-make-roulette-using-javascript-part-1-41eb) 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

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

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

```javascript
function setBet(n, t, o){
	console.log(n);
	console.log(t);
	console.log(o);
}
```

we're going to change it to this

```javascript
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]);
		}
	}
}
```

<b>Broken down.</b> 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.

```javascript
var obj = {
	amt: wager,
	type: t,
	odds: o,
	numbers: n
};
bet.push(obj);
```

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

```javascript
let numArray = n.split(',').map(Number);
```

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

```javascript
for(i = 0; i < numArray.length; i++){
	if(!numbersBet.includes(numArray[i])){
		numbersBet.push(numArray[i]);
	}
}
```

Next we start the build on the spin function.

```javascript
function spin(){}
```

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

```javascript
console.log(numbersBet);
```

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

```javascript
var winningSpin = Math.floor(Math.random() * 36);
```

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

```javascript
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));
		}
	}
}
```

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

```javascript
for(i = 0; i < bet.length; i++){}
```

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

```javascript
var numArray = bet[i].numbers.split(',').map(Number);
```

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.

```javascript
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));
}
```

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

```javascript
else{
	console.log(winningSpin);
	console.log('no win');
}
```

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

```javascript
bet = [];
numbersBet = [];
```

The full function so far should look like this

```javascript
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 = [];
}
```

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

```javascript
let spinBtn = document.createElement('div');
spinBtn.setAttribute('class', 'spinBtn');
spinBtn.innerText = 'spin';
spinBtn.onclick = function(){
	spin();
};
container.append(spinBtn);
```

And give it some basic styling

```css
.spinBtn{
	position: relative;
	top: 253px;
	font-size:28px;
	cursor:pointer
}
```

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

```css
.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;
}
```

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

```javascript
setBet(num, 'outside_oerb', 1);
```

will become

```javascript
setBet(this, num, 'outside_oerb', 1);
```

and the actual function

```javascript
function setBet(n, t, o){}
```

will become

```javascript
function setBet(e, n, t, o){}
```

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

```javascript
let chip = document.createElement('div');
chip.setAttribute('class', 'chip');
e.append(chip);
```

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

```css
.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;
}
```

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

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

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

```css
#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;
}
```

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

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

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

```javascript
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];
```

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

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

Then I created a new function called spinWheel

```javascript
function spinWheel(winningSpin){}
```

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

```javascript
for(i = 0; i < wheelnumbersAC.length; i++){
	if(wheelnumbersAC[i] == winningSpin){
		var degree = (i * 9.73) + 362;
	}
}
```

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

```javascript
wheel.style.cssText = 'animation: wheelRotate 5s linear infinite;';
ballTrack.style.cssText = 'animation: ballRotate 1s linear infinite;';
```

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

```javascript
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);
```

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

```javascript
setTimeout(function(){
	ballTrack.style.cssText = 'animation: ballStop 3s linear;';
}, 6000);
```

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

```javascript
setTimeout(function(){
	ballTrack.style.cssText = 'transform: rotate(-'+degree+'deg);';
}, 9000);
```

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

```javascript
setTimeout(function(){
	wheel.style.cssText = '';
	style.remove();
}, 10000);
```

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.

```javascript
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);
}
```

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](https://codepen.io/ozboware/pen/rNGOzQW).

The full code for this part

<small>Javascript</small>

```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();
	}
}
```

<small>css</small>
```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;
}
```

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