Question:
Aims & Objective
The purpose of this assignment is to assess your ability to create an interactive web page using
Javascript, CSS and HTML
Brief
The zip file Assignment2.zip provides some HTML and CSS for a horse racing game. Your task is
to build a web based horse racing/betting game by adding the relevant Javascript code. You must
use the existing HTML/CSS as a basis and build onto it. There are four horses which start on the
start line and after pressing the start button should run one lap around the track.
You can set class names on the horse elements for running/standing animations and each of the
four directions up/down/left/right. You will need to examine the CSS file to see which CSS classes
are available to use.
Basic requirements:
For a bare pass (D- – D+) you must make the horses perform one lap of the track
1) Each horse should move to the top right corner of the track, then down the right hand side of
the track, along the bottom and back up to the top turning while ensuring they stay on the track.
2) The horses should continue following the track until they reach the start/finish line.
3) As horses change direction and start/stop their animation should change. The animations are
provided for you as CSS classes. The horses should always face the direction they are travelling.
4) As each horse reaches the start/finish line it completes the lap and should stop racing (the
horses don’t have to stop exactly on the line, but must go over it)
5) Pressing the start button again should reset the horses and start another lap
The speed the horses run at is up to you, but a lap should not take more than one minute.
Note: The track fills the entire screen! Do not assume that everyone’s screen will be the
same size, you will need to calculate where the horses turn/stop based on the screen
size. The track’s width is 10% of the window’s size.
For a good pass (C- – B-) you must make the race randomised
1) Each horse should run at a different speed around the track
2) To make it so you can’t see who will win by identifying who’s fastest at the start of the race, the
speed should vary as the horse goes around the track (e.g. one horse starts slow but later
overtakes another horse by speeding up)
3) Don’t have each horse turn at exactly the same point on the track
4) Detect the winner (the first horse to complete a lap) and display the results by listing the
position and the name of the horse.
5) The start button should be disabled while the race is in progress.
For a very good pass (B – A-) you must implement betting:
1) The user starts with £100 and can bet on a horse by entering the amount they wish to bet
2) If they win, they get double their money back
3) They can then bet on another horse with their new funds and run the race again.
For an excellent pass (A) you must implement one of the following:
1) Randomly generate the odds for each horse at the beginning of the game. E.g. one horse might
be 2 to 1 (you get twice your money back if you win). Another might be 100 to 1 (you get 100
times your bet back if you win). Once a horse wins, it’s odds should decrease. E.g. if it wins, on
100 to 1 odds, its odds should be reduced for the next race, if a horse comes second its odds
should decrease. This should stack, for example, if a horse has lost 5 times in a row it should have
high odds.
2) Allow the user to specify a number of laps around the track that the horses will run over the
course of the race before a winner is declared.
3) Adjust the shape of the track so it’s more interesting than just a square, e.g. a figure of eight.
You will need to amend the HTML/CSS and paths that the horses run.
4) You may also implement other features you feel would be interesting to add, the level of
difficultly will need to be in line with the other options in this section.
Deliverables
Along with your code, you will need to provide a report which includes the following sections:
1. Game design – How did you come up with a solution to meet the brief?
1. What did you need to do to work out how to get the horses to stop/turn at the relevant
points on the track?
2. How did you design the game so that a different horse won each time?
3. How did you design the game to make it interesting? E.g. so overtaking happens
2. Program design
1. How did you use language tools to reduce repeated code?
2. What did you have to do to account for different screen sizes?
3. How did you break the problem up into different tasks and;
4. How did you use the features of Javascript to do this?
5. Did you consider any alternative approaches? If so, why did you choose the approach
you decided to use
3. Testing
1. How did you test that your code worked?
2. Could you test certain aspects of the code without running the entire game and waiting
for it to finish?
3. What tests did you carry out and what were the outcomes?
4. What bugs did you discover during testing?
4. Conclusion
1. A list of known bugs/weaknesses in the game
2. What works well?
3. What improvements could can be made?
4. What else would you have done if you had more time?
5. If you had to build a similar game in the future, what would you do differently and
why?
In Addition to the report you must provide a video demonstration of your assignment working.
The demo should be 5-10 minutes (No longer than 15 minutes). The video should demonstrate at
least three rounds of the game and any additional features you have implemented.
Answer:
var interval = 0;
var h1PositionLeft = 0;
var direction = [];
var lapCount = [];
var finished = [];
var order = [];
var ranked = [];
var timer;
var numberOfFinishers = 0;
var speed = [];
var turningPoint1 = [];
var turningPoint2 = [];
var turningPoint3 = [];
var turningPoint4 = [];
var bettingMoney = 100;
var betValue;
var bettingHorse;
var laps;
var odds = [];
for (var i = 0; i < 4; i++) {
odds[i] = 2;
}
function generateTurningPoints(i) {
turningPoint1[i] = Math.ceil((window.innerWidth/100)*70) + ((window.innerWidth/100)*Math.ceil(Math.random() * 10));
turningPoint2[i] = Math.ceil((window.innerHeight/100)*70) + ((window.innerWidth/100)*Math.ceil(Math.random() * 10));
turningPoint3[i] = Math.ceil((window.innerWidth/100)*2.5) + ((window.innerWidth/100)*Math.ceil(Math.random() * 10));
turningPoint4[i] = Math.ceil((window.innerHeight/100)*2.5) + ((window.innerWidth/100)*Math.ceil(Math.random() * 10));
}
function startClick() {
betValue = document.getElementById(‘amount’).value;
laps = document.getElementById(‘lapCount’).value;
if (laps > 0) {
if (bettingMoney > 0 && betValue <= bettingMoney && betValue > 0) {
for (var i = 0; i < 4; i++) {
direction[i] = ‘right’;
lapCount[i] = 0;
finished[i] = ‘no’;
ranked[i] = false;
speed[i] = 1;
var horseID = ‘horse’ + (i+1);
var horse = document.getElementById(horseID);
horse.style.top = ((i * 4) + 4) + ‘vh’;
horse.style.left = 20 + ‘vw’;
var result = ‘result’ + (i + 1);
document.getElementById(result).className = ”;
generateTurningPoints(i);
}
numberOfFinishers = 0;
timer = setInterval(myInterval, 1);
document.getElementById(‘start’).disabled = true;
var dropdown = document.getElementById(‘bethorse’);
document.getElementById(‘funds’).innerHTML = (bettingMoney – betValue);
bettingMoney = bettingMoney – betValue;
bettingHorse = dropdown.options[dropdown.selectedIndex].value;
console.log(‘Laps: ‘ + laps);
console.log(‘Horse bet on: ‘ + bettingHorse);
console.log(‘Amount bet: ‘ + betValue);
console.log(‘Betting Money left:’ + bettingMoney);
}
else {
alert(‘You do not have enough money for that bet!’);
}
}
else {
alert(‘Please enter a valid lap count’);
}
}
function resultsScreen() {
var winner;
var winnerDigit;
for (var i = 0; i < 4; i++) {
var horseID = ‘horse’ + (order[i] + 1);
var result = ‘result’ + (i + 1);
console.log(result);
console.log(order[i]);
document.getElementById(result).className = horseID;
if (i == 0) {
winner = horseID;
}
}
winnerDigit = order[0];
if (winner == bettingHorse) {
bettingMoney = bettingMoney + (betValue * odds[winnerDigit]);
document.getElementById(‘funds’).innerHTML = bettingMoney;
}
for (var i = 0; i < 4; i++) {
if (i == winnerDigit) {
odds[i] = odds[i] / 2;
var oddsReference = ‘odds’ + (i + 1)
document.getElementById(oddsReference).innerHTML = odds[i];
}
else {
var oddsReference = ‘odds’ + (i + 1)
odds[i] = odds[i] * 2;
document.getElementById(oddsReference).innerHTML = odds[i];
}
}
if (bettingMoney == 0) {
alert(‘You are out of money!’);
}
}
function move() {
for (var i = 0; i < 4; i++) {
var horseID = ‘horse’ + (i+1);
var horse = document.getElementById(horseID);
if (lapCount[i] == laps && horse.offsetLeft > (window.innerWidth/(7/2))) {
horse.className = ‘horse standRight’;
finished[i] = ‘yes’
if (ranked[i] == false) {
ranked[i] = true;
//console.log(numberOfFinishers);
order[numberOfFinishers] = i;
//console.log(‘finishing order: ‘ + order[numberOfFinishers]);
numberOfFinishers++;
}
}
else {
if (direction[i] == ‘right’) {
horse.className = ‘horse runRight’;
var h1PositionLeft = horse.offsetLeft;
horse.style.left = h1PositionLeft + speed[i] + ‘px’;
}
if (direction[i] == ‘left’) {
horse.className = ‘horse runLeft’;
var h1PositionLeft = horse.offsetLeft;
horse.style.left = h1PositionLeft + – + speed[i] + ‘px’;
}
if (direction[i] == ‘up’) {
horse.className = ‘horse runUp’;
var h1PositionTop = horse.offsetTop;
horse.style.top = h1PositionTop + – + speed[i] + ‘px’;
}
if (direction[i] == ‘down’) {
horse.className = ‘horse runDown’;
var h1PositionTop = horse.offsetTop;
horse.style.top = h1PositionTop + speed[i] + ‘px’;
}
}
}
if (finished[0] == ‘yes’ && finished[1] == ‘yes’ && finished[2] == ‘yes’ && finished[3] == ‘yes’) {
document.getElementById(‘start’).disabled = false;
clearInterval(timer);
resultsScreen();
}
}
function myInterval() {
for (var i = 0; i < 4; i++) {
var horseID = ‘horse’ + (i+1);
var horse = document.getElementById(horseID);
var horseLeft = horse.offsetLeft;
var horseTop = horse.offsetTop;
if (direction[i] == ‘right’) {
//console.log(turningPoint1[i]);
if (horseLeft >= turningPoint1[i]) {
console.log(‘Down ‘ + horseID + ‘ ‘ +horseLeft);
direction[i] = ‘down’;
speed[i] = Math.ceil(Math.random() * 2);
}
}
else if (direction[i] == ‘down’) {
//console.log(turningPoint2[i]);
if (horseTop >= turningPoint2[i]) {
console.log(‘Left ‘ + horseID + ‘ ‘ +horseTop);
direction[i] = ‘left’;
speed[i] = Math.ceil(Math.random() * 2);
}
}
else if (direction[i] == ‘left’) {
//console.log(turningPoint3[i]);
if (horseLeft <= turningPoint3[i]) {
console.log(‘Up ‘ + horseID + ‘ ‘ +horseLeft);
direction[i] = ‘up’;
speed[i] = Math.ceil(Math.random() * 2);
lapCount[i] = lapCount[i] + 1;
console.log(lapCount[i]);
}
}
else if (direction[i] == ‘up’) {
//console.log(turningPoint4[i]);
if (horseTop <= turningPoint4[i]) {
console.log(‘Right ‘ + horseID + ‘ ‘ +horseTop);
direction[i] = ‘right’;
speed[i] = Math.ceil(Math.random() * 2);
generateTurningPoints(i);
}
}
}
move();
}
function myLoadEvent() {
var start = document.getElementById(‘start’);
start.addEventListener(‘click’, startClick);
}
document.addEventListener(‘DOMContentLoaded’, myLoadEvent);
body {background-image: url(images/bg.png); margin: 0; padding: 0; font-family: calibri, sans-serif;}
aside {display: none; width: 200px; position: fixed; height: 100vh; left: 100%; margin-left: -240px; background-color: #ddd; border-left: 5px solid #bbb; padding: 20px;}
#closeside {float: right; font-weight: bold; cursor: pointer;}
.character {position: absolute; top: 200px; left: 200px; display: block;}
.character .body {width: 32px; height: 32px; background-image: url(images/body0.png); background-position: 64px 0px; margin-top: 14px; }
.character .head {width: 32px; height: 32px; background-image: url(images/head0.png); margin-bottom: 0;position: absolute; background-repeat: no-repeat; background-position: 0px -64px;}
aside ul {list-style-type: none; margin-left: 0; padding-left: 0; margin-bottom: 20px; overflow: auto;}
.heads li, .bodies li {width: 32px; height: 32px; background-position: 0 -64px; float: left; cursor: pointer; border: 2px solid #ddd; margin-right: 4px;}
.heads li:hover, .bodies li:hover {border: 2px solid red;}
#head0 {background-image: url(images/head0.png);}
#head1 {background-image: url(images/head1.png);}
#head2 {background-image: url(images/head2.png);}
#head3 {background-image: url(images/head3.png);}
#head4 {background-image: url(images/head4.png);}
.bodies li {background-position: 64px 0px;}
#body0 {background-image: url(images/body0.png);}
#body1 {background-image: url(images/body1.png);}
#body2 {background-image: url(images/body2.png);}
#body3 {background-image: url(images/body3.png);}
#body4 {background-image: url(images/body4.png);}
.tree {float: right; margin-top: 2vh; margin-right: 5vw;}
#opponent {left: 500px; top: 500px;}
#opponent .body {background-image: url(images/body1.png);}
#opponent .head {background-image: url(images/head1.png);}
.character.standUp .head {
background-position: 0px 0px;
}
.character.standDown .head {
background-position: 0px -64px;
}
.character.standLeft .head {
background-position: 0px -32px;
}
.character.standRight .head {
background-position: 0px -96px;
}
.character.standUp .body {
background-position: 0px 0px;
}
.character.standDown .body {
background-position: -64px 0px;
}
.character.standLeft .body {
background-position: -32px 0px;
}
.character.standRight .body {
background-position: -96px 0px;
}
.character.walkUp .head {
background-position: 0px 0px;
animation: headbob 0.5s infinite;
}
.character.walkUp .body {
animation: walkup 1s steps(3) infinite;
}
.character.walkDown .head {
background-position: 0px -64px;
animation: headbob 0.75s infinite;
}
.character.walkDown .body {
animation: walkdown 1s steps(3) infinite;
}
.character.walkLeft .head {
background-position: 0px -32px;
margin-top: -3px;
margin-left: -2px;
animation: headbob2 0.75s infinite;
}
.character.walkLeft .body {
animation: walkleft 1s steps(3) infinite;
}
@keyframes walkleft {
0%, 100% { background-position: -32px -32px; }
50% { background-position: -32px -128px; }
}
.character.walkRight .head {
background-position: 0px -96px;
margin-top: -3px;
margin-left: 4px;
animation: headbob3 0.75s infinite;
}
.character.walkRight .body {
animation: walkright 1s steps(3) infinite;
}
@keyframes walkright {
0%, 100% { background-position: -96px -32px; }
50% { background-position: -96px -128px; }
}
@keyframes walkup {
0%, 100% { background-position: 0px -32px; }
50% { background-position: 0px -128px; }
}
@keyframes walkdown {
0%, 100% { background-position: -64px -32px; }
50% { background-position: -64px -128px; }
}
@keyframes headbob {
0%, 100% { margin-top: 0px; }
50% { margin-top: 1px; }
}
@keyframes headbob2 {
0%, 100% { margin-left: -2px; }
50% { margin-left: 0px; }
}
@keyframes headbob3 {
0%, 100% { margin-left: 2px; }
50% { margin-left: 0px; }
}
.track {border-style: solid;
border-collapse: separate;
display: table;
border-width: 16px;
-moz-border-image: url(outer.png) 14 15 14 16 repeat;
-webkit-border-image: url(images/outer.png) 14 15 14 16 repeat;
-o-border-image: url(outer.png) 14 15 14 16 repeat;
border-image: url(images/outer.png) 14 15 14 16 repeat; background-image: url(images/mud.png);
position: absolute; height: 80vh; width: 80vw; margin: 5vw; margin-top: 5vh;
}
.track .inner {
background-image: url(images/bg.png);
border-image: url(images/inner.png); border-image-slice: 16 16 16 16 fill; border-image-width: 16px 16px 16px 16px; border-image-outset: 0px 0px 0px 0px; border-image-repeat: round round;
border-collapse: separate; margin-top: 15vh; margin-left: 10vw; display: table; solid transparent; background-color: green; width: 60vw; height: 50vh;}
.horse {background-image: url(images/horse2.png);}
.horse.runLeft {
width: 96px;
height: 60px;
display: block;
background-position: 0px -256px;
animation: horseLeft steps(1) 0.5s infinite;
}
@keyframes horseLeft {
0%, 100% {background-position: 0px -256px; }
50% {background-position: 0px -450px;}
}
.horse.runRight {
width: 96px;
height: 60px;
display: block;
background-position: 0px -320px;
animation: horseRight steps(1) 0.5s infinite;
}
@keyframes horseRight {
0%, 100% {background-position: 0px -320px; }
50% {background-position: 0px -512px;}
}
.horse.runDown {
width: 48px;
height: 64px;
background-position:48px 192px;
animation: horseDown steps(1) 0.75s infinite;
}
@keyframes horseDown {
0%, 100% {background-position:48px -192px; }
33% {background-position:48px -380px; }
66% {background-position:48px 0px;}
}
.horse.runUp {
width: 48px;
height: 64px;
background-position:0px 192px;
animation: horseUp steps(1) 0.75s infinite;
}
@keyframes horseUp {
0%, 100% {background-position:0px -192px; }
33% {background-position:0px -380px; }
66% {background-position:0px 0px;}
}
#startline {
width: 2vw;
height: 15vh;
background-color: #eee;
background-image: linear-gradient(45deg, black 25%, transparent 25%, transparent 75%, black 75%, black),
linear-gradient(-45deg, black 25%, transparent 25%, transparent 75%, black 75%, black);
background-size:1vw 1vw;
position: absolute;
margin-left: 20vw;
opacity: 0.7;
}
#horse1 {
position: absolute; top: 4vh; z-index: 999;
}
#horse4 .rider .head {background-image: url(images/head2.png);}
#horse4 .rider .body {background-image: url(images/body3.png);}
#horse3 .rider .head {background-image: url(images/head1.png);}
#horse3 .rider .body {background-image: url(images/body1.png);}
#horse2 .rider .head {background-image: url(images/head4.png);}
#horse2 .rider .body {background-image: url(images/body4.png);}
#horse2 {
position: absolute; top: 8vh; z-index: 999;
background-image: url(images/horse3.png);
}
#horse3 {
position: absolute; top: 12vh; z-index: 999;
background-image: url(images/horse1.png);
}
#horse4 {
position: absolute; top: 16vh; z-index: 999;
background-image: url(images/horse4.png);
}
.horse {left: 20vw;}
.horse.standLeft {background-position: 0px -64px; width: 96px; height: 60px;}
.horse.standLeft .rider .body {background-position: -32px -320px;}
.horse.standLeft .rider .head {background-position: 0px -32px;}
.horse.standRight {background-position: 0px -128px; width: 96px; height: 60px;}
.horse.standRight .rider {margin-left: -15px;}
.horse.standRight .rider .body {background-position: -96px -320px;}
.horse.standRight .rider .head {background-position: 0px -96px;}
.horse .rider .head {
position: absolute;
margin-left: 40px;
margin-top: -16px;
background-image: url(images/head0.png);
width: 32px;
height: 32px;
}
.horse .rider .body {
background-image: url(images/body0.png);
width: 32px;
height: 32px;
margin-left: 40px;
margin-top: 8px;
}
.horse.runDown .rider, .horse.runRight .rider, .horse.runLeft .rider{
animation: riderBob1 0.75s infinite;
}
.horse.runLeft .rider .body {
background-position: -32px -320px;
}
.horse.runRight .rider {
margin-left: -15px;
}
.horse.runRight .rider .head {
background-position: 0px -96px;
animation: headbob4 0.5s infinite;
}
.horse.runRight .rider .body {
background-position: -96px -320px;
}
.horse.runDown, .horse.runUp {
margin-left: 32px;
}
.horse.runDown .rider {
margin-left: -32px;
}
.horse.runDown .rider .head {
background-position: 0px -64px;
animation: headbob5 0.5s infinite;
}
.horse.runDown .rider .body {
background-position: -64px -320px;
margin-top: -16px;
}
.horse.runUp .rider {
margin-left: -32px;
animation: riderBob2 0.75s infinite;
}
.horse.runUp .rider .head {
background-position: 0px 0px;
animation: headbob5 0.5s infinite;
}
.horse.runUp .rider .body {
background-position: 0px -320px;
margin-top: -16px;
}
.horse.runLeft .rider .head {
background-position: 0px -32px;
animation: headbob4 0.5s infinite;
}
@keyframes headbob4 {
0%, 100% { margin-left: 40px; }
50% { margin-left: 42px; }
}
@keyframes headbob5 {
0%, 100% { margin-top: -10px; }
50% { margin-top: -8px; }
}
@keyframes riderBob1 {
0%, 100% { margin-top: 8px; }
50% { margin-top: 10px; }
}
@keyframes riderBob2 {
0%, 100% { margin-top: 18px; }
50% { margin-top: 20px; }
}
#start {
float: right;
margin: 1vw;
padding: 1vw;
background: #3498db;
background-image: -webkit-linear-gradient(top, #3498db, #2980b9);
background-image: -moz-linear-gradient(top, #3498db, #2980b9);
background-image: -ms-linear-gradient(top, #3498db, #2980b9);
background-image: -o-linear-gradient(top, #3498db, #2980b9);
background-image: linear-gradient(to bottom, #3498db, #2980b9);
-webkit-border-radius: 28;
-moz-border-radius: 28;
border-radius: 28px;
font-family: Arial;
color: #ffffff;
font-size: 20px;
padding: 10px 20px 10px 20px;
text-decoration: none;
cursor: pointer;
text-shadow: 2px 2px 2px #333;
}
#start:hover {
color: #000;
}
#bet, #results {
background-color: #ffefb4;
padding: 1vw;
width: 20vw;
border: 2px solid #555;
border-radius: 20px;
margin-top: 5vh;
margin-left: 2vw;
overflow: auto;
float: left;
}
#funds, #odds {font-weight: bold;}
#funds:before {content: “£”;}
label, input, select {float: left; display: block; width: 8vw; margin-bottom: 0.5vh;}
label {clear: left;}
#results {width: auto; position: absolute; margin-top: 8vh; margin-left: 48vw;}
#results td {height: 30px; width: 30px; background-repeat: no-repeat; background-position: 0px -64px;}
#results .horse1 { background-image: url(images/head0.png);}
#results .horse2 { background-image: url(images/head4.png);}
#results .horse3 { background-image: url(images/head1.png);}
#results .horse4 { background-image: url(images/head2.png);}
/*
.result1 {background-image: url(images/head0.png);}
.result2 {background-image: url(images/head4.png);}
.result3 {background-image: url(images/head1.png);}
.result4 {background-image: url(images/head2.png);}*/