Last active
April 4, 2016 16:11
-
-
Save danjac/6bc16241fcd23874673007eefed1a103 to your computer and use it in GitHub Desktop.
Defender test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE HTML> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title></title> | |
<script src="https://code.jquery.com/jquery-2.2.2.min.js"></script> | |
</head> | |
<body> | |
</body> | |
<script> | |
/* | |
This AI simply generates a random 'plan'. | |
Update the 'notify_player()' function to implement your own AI. | |
*/ | |
(function(){ | |
var defender = {}; | |
window.defender = defender; | |
/* | |
The 'space' is a region of 100x100 characters | |
(0,0)------------------+ | |
| | | |
| | | |
| | | |
+-------------------(79,9) | |
'rocks' appear along the y=0 axis, and move to the right (from x=0 to x=79) | |
*/ | |
var iW = 80, iH = 10, bQuickSimulation = false; | |
function log(sMsg){ | |
window.console && console.log(sMsg); | |
} | |
function assert(b, sMsg){ | |
if( !b ) | |
log(sMsg); | |
} | |
defender.rock = (function(){ | |
var rock = {}; | |
rock.create = function(iY){ | |
var oRock = { | |
iX: 0, | |
//Math.floor(Math.random() * iH) | |
iY: iY | |
}; | |
return oRock; | |
} | |
return rock; | |
})(); | |
defender.start = function(notify_player){ | |
if( typeof(notify_player) !== "function" ){ | |
alert("Error: defender.init(notify_player) requires a function as its first parameter."); | |
} | |
// initialize the board | |
var asHTML = ['<table style="border-collapse:collapse;border-width:0px">']; | |
for( var i = 0; i < iH; i++ ){ | |
var asRow = ['<tr>']; | |
for( var j = 0; j < iW+1; j++ ){ | |
asRow.push('<td id="td-'+j+'-'+i+'"></td>'); | |
} | |
asRow.push('</tr>'); | |
asHTML.push(asRow.join("")); | |
} | |
asHTML.push('</table>') | |
asHTML.push('<div style="font-size:12px;width:550px" id="game-message"></div>'); | |
asHTML.push('<div style="font-size:12px;width:570px" id="game-instructions">'); | |
asHTML.push('<h1>Defender</h1>'); | |
asHTML.push('<br/>Nasty Aliens are hurling red rocks at the planet <b>Earth</b>. Our last line of defence- <b>The Paddle</b>. Write the AI for The Paddle to defend the Earth from certain destruction.</br><br/>'); | |
asHTML.push('Download the instructions manual: <a target="_blank" href="http://download.basecase.com/programming-test/defender-test.pdf">defender-test.pdf</a>.</br><br/>'); | |
asHTML.push('<div style="border: 1px solid grey;padding:5px;background-color:#fee">'); | |
asHTML.push('<b>This is not specifically a "front-end" test</b><br/><br/>'); | |
asHTML.push('This test requires only writing the logic for the movement of the paddle and not the UI.<br/></br/>'); | |
asHTML.push('You must write a solution to this test using Javascript, but note that you do not need to interact with the "document object model" (DOM), and so'); | |
asHTML.push('this test is not specifically a "front-end" test. Even if you have limited understanding of Javascript, you should be able to tackle this test.'); | |
asHTML.push('For those of you new to Javascript, I would suggest review the math and array functions available in Javascript:'); | |
asHTML.push('<ul>'); | |
asHTML.push('<li><a href="http://www.w3schools.com/jsref/jsref_obj_math.asp">http://www.w3schools.com/jsref/jsref_obj_math.asp</a></li>'); | |
asHTML.push('<li><a href="http://www.w3schools.com/js/js_arrays.asp">http://www.w3schools.com/js/js_arrays.asp</a></li>'); | |
asHTML.push('</ul>'); | |
asHTML.push('</div>'); | |
asHTML.push('</div>'); | |
$("body").css({"font-family": "courier", "font-size": "8px"}); | |
$("body").html(asHTML.join("")); | |
defender.notify_player = notify_player; | |
$("body").dblclick(function(){bQuickSimulation=true;}) | |
defender.game_loop(); | |
} | |
defender.notify_player = function(aoRockNotifiction, iPaddleY){ | |
function log(sMsg){ | |
window.console && console.log(sMsg); | |
} | |
var asMsg = ["defender.notify_player: "]; | |
for( var i = 0; i < aoRockNotifiction.length; i++ ){ | |
var oRockNotification = aoRockNotifiction[i]; | |
asMsg.push("{distance: " + oRockNotification.distance + ", radians: " + oRockNotification.radians + "},"); | |
} | |
// random plan | |
var aiMove = []; | |
for( var i = 0; i < 22; i++ ) | |
// -1, 0, 1 | |
aiMove.push( Math.floor(Math.random()*3) - 1 ); | |
log(asMsg.join("")); | |
log("Plan: " + aiMove) | |
return aiMove ; | |
} | |
var oPos = { | |
iX: iW, iY: Math.floor(iH/2), | |
// sequence values in [-1, 0, +1], indiciating the current defensive plan for the paddle | |
aiMove: [] | |
}; | |
defender.notify_rock_change = function(aoRock){ | |
var aoRockNotifiction = []; | |
for( var i = 0; i < aoRock.length; i++ ){ | |
var oRock = aoRock[i]; | |
var oRockNotification = { | |
distance: ( | |
Math.pow( Math.pow(oPos.iX - oRock.iX, 2) + Math.pow(oPos.iY - oRock.iY, 2), 0.5) | |
), | |
radians: Math.atan2(oPos.iY - oRock.iY, oPos.iX - oRock.iX) | |
} | |
// randomize order | |
if( Math.random() < 0.5 ) | |
aoRockNotifiction.push(oRockNotification); | |
else | |
aoRockNotifiction = [oRockNotification].concat(aoRockNotifiction); | |
} | |
oPos.aiMove = defender.notify_player(aoRockNotifiction, oPos.iY); | |
if( !oPos.aiMove || !oPos.aiMove.length ){ | |
oPos.aiMove = []; | |
} | |
} | |
// when iRockWait is zero, it's time for a new rock! | |
// 600 ticks = 2min | |
var iTickCount = 0, iTickMax = 600; | |
var aoRock = []; | |
var iRockWait = 1; | |
defender.game_loop = function(){ | |
iRockWait--; | |
iTickCount++; | |
var bRockBlocked = false; | |
// update any rocks | |
var aoNewRock = []; | |
for( var i = 0; i < aoRock.length; i++ ){ | |
var oRock = aoRock[i]; | |
oRock.iX++; | |
if( oRock.iX === iW ){ | |
if( oPos.iY === oRock.iY ){ | |
log("Blocked attack!"); | |
bRockBlocked = true; | |
}else{ | |
$("#game-message").html("<h1>Game over!</h1><div>The Earth is now a smouldering ruin.</div>") | |
return; | |
} | |
} else { | |
aoNewRock.push(oRock); | |
} | |
} | |
aoRock = aoNewRock; | |
if( iTickCount === iTickMax ){ | |
$("#game-message").html("<h1>Congratulations!</h1><div>Your planetary defences seem to be working.</div>") | |
return; | |
} | |
// create new rocks | |
if( iRockWait === 0 ){ | |
aoRock.push(defender.rock.create(Math.floor(Math.random() * iH))); | |
defender.notify_rock_change(aoRock); | |
// iH = 9, => 21 dots sepearate rocks | |
iRockWait = iH * 2 + 2; | |
} else if(bRockBlocked){ | |
defender.notify_rock_change(aoRock); | |
} | |
// move paddle | |
var iMove = oPos.aiMove.shift(); | |
if( iMove === 1 || iMove === -1 || iMove === 0){ | |
var iNewY = oPos.iY + iMove; | |
if( iNewY < 0 ){ | |
log("Invalid move: you can't move the paddle to (y=-1)."); | |
}else if( iNewY >= iH ){ | |
log("Invalid move: you can't move the paddle to (y=" + iH + ")."); | |
}else{ | |
oPos.iY += iMove; | |
} | |
} else if( iMove !== undefined ){ | |
log("Invalid move: " + iMove + ". A valid move msut be one of: [-1, 0, +1]."); | |
} | |
// render game board | |
if( !bQuickSimulation ){ | |
$("table td").text(".").css("color", "#aaa"); | |
for( var i = 0; i < aoRock.length; i++ ){ | |
var oRock = aoRock[i]; | |
$("#td-"+oRock.iX+"-"+oRock.iY).text("O").css("color", "red"); | |
} | |
for( var j = 0; j < iH; j++ ) | |
$("#td-" + iW + "-" + j).text(" "); | |
$("#td-" + iW + "-" + oPos.iY).text("|").css("color", "black") | |
} | |
window.setTimeout( | |
defender.game_loop, | |
bQuickSimulation ? 1 : 100 | |
); | |
} | |
})(); | |
defender.start( | |
function notify_player(rocks, paddle_y){ | |
// just in case we don't have any rocks left | |
if (rocks.length === 0) return []; | |
var moves = []; | |
// find the closest rock | |
rocks.sort(function(x, y) { | |
return x.distance - y.distance; | |
}); | |
var rock = rocks[0]; | |
// calculate distance we need to move to intercept the rock | |
var iDistY = Math.round(Math.sin(rock.radians) * rock.distance); | |
// moving up or down? | |
var iMove = iDistY > 0 ? -1 : 1; | |
for (var i=0; i < Math.abs(iDistY); i++) { | |
moves.push(iMove); | |
} | |
return moves; | |
} | |
); | |
</script> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment