Skip to content

Instantly share code, notes, and snippets.

@alexhornbake
Last active September 25, 2017 18:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexhornbake/95d2367a19e5e0ef98d359d93f54b0b3 to your computer and use it in GitHub Desktop.
Save alexhornbake/95d2367a19e5e0ef98d359d93f54b0b3 to your computer and use it in GitHub Desktop.
Block Pusher
<!DOCTYPE html>
<html>
<head>
<title>boxpusher</title>
<style>
body {
margin: 0;
height: 100%;
overflow: hidden
}
canvas { width: 100%; height: 100% }
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/87/three.min.js"></script>
<script type="text/javascript">
(function () {
// game state constants
var EMPTY = 0;
var PLAYER = 1;
var BOX = 2;
var OBSTACLE = 3;
// 3d Display constants
var boxGeo = new THREE.BoxGeometry( .95, .95, .95 );
var boxMat = new THREE.MeshPhongMaterial( { color: 0xddbbdd, specular: 0x555555, shininess: 30 } );
var obstacleMat = new THREE.MeshPhongMaterial( { color: 0x1166dd, specular: 0x555555, shininess: 30 } );
var playerMat = new THREE.MeshPhongMaterial( { color: 0xffffff, specular: 0x555555, shininess: 30 } );
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min; //The maximum is exclusive and the minimum is inclusive
}
var Board = {
board : [
[2,2,0,0,0],
[0,2,2,0,0],
[0,0,1,0,0],
[0,3,0,0,0],
[0,0,2,0,0],
],
playerPos: { x: 2, y: 2 },
shouldAddBox: false,
getWidth() {
return this.board[0].length
},
getHeight() {
return this.board.length
},
getAllEmptyCoordinates() {
var empties = [];
for (var i = 0; i < this.getHeight(); i++) {
for (var j = 0; j < this.getWidth(); j++) {
if (this.board[i][j] == EMPTY) {
empties.push({x: j, y: i});
}
}
}
return empties
},
addRandomBox() {
var empties = this.getAllEmptyCoordinates();
if (empties.length == 0) {
return
}
var newBox=empties[getRandomInt(0, empties.length)];
this.board[newBox.y][newBox.x] = BOX;
},
clearFullRowsAndCols() {
var rowCounts = [];
var colCounts = [];
// count how many dots are in each row and col
for (var i = 0; i < this.getHeight(); i++) {
rowCounts.push(0);
for (var j = 0; j < this.getWidth(); j++) {
if (i === 0) {
colCounts.push(0);
}
if (this.board[i][j] === BOX) {
rowCounts[i]++;
colCounts[j]++;
}
}
}
// if curr position is in a full row or col, clear it.
var didClear = false;
for (var i = 0; i < this.getHeight(); i++) {
for (var j = 0; j < this.getWidth(); j++) {
if (rowCounts[i] === this.getWidth() || colCounts[j] === this.getHeight() ){
didClear = true;
this.board[i][j] = EMPTY;
}
}
}
return didClear
},
setPlayerPos(x, y) {
if (x === this.playerPos.x && y === this.playerPos.y) {
return false
}
this.board[this.playerPos.y][this.playerPos.x] = EMPTY;
this.playerPos = {x : x, y : y};
this.board[y][x] = PLAYER;
return true
},
isPositionOnBoard(x, y) {
return (
x < this.getWidth() &&
x >= 0 &&
y < this.getHeight() &&
y >= 0
)
},
handlePlayerMove(deltaX, deltaY, results) {
if (!results) {
results = { didPlayerMove : false, didBoxMove : false}
}
var newPos = {
x : this.playerPos.x + deltaX,
y : this.playerPos.y + deltaY,
}
// check that you are not moving off the board
if ( !this.isPositionOnBoard(newPos.x, newPos.y) ){
return results
}
//Check collisions
var dest = this.board[newPos.y][newPos.x]
switch (dest) {
case BOX:
var didBoxMove = this.playerCollidesWithBox(newPos, deltaX, deltaY);
return {
didBoxMove : didBoxMove,
didPlayerMove: true,
}
break
case OBSTACLE:
return { didPlayerMove : results.didPlayerMove || false, didBoxMove : results.didBoxMove || false}
default:
// continue moving in current direction until one of the above base conditions is hit.
// off the board, hit a box, or hit an obstacle.
this.setPlayerPos(newPos.x, newPos.y);
return this.handlePlayerMove(deltaX, deltaY, { didPlayerMove : true, didBoxMove : results.didBoxMove || false});
break
}
},
playerCollidesWithBox(newPos, deltaX, deltaY) {
// We have to simply compress the array between the player and the boarder.
// For instance [player, 2, 2, 2, 0 ] becomes [ 0, player, 2, 2 , 2 ]
// This logic could maybe be simplified to 1 case instead of 4 by transposing the game board
// such that there is only one case, however, that would also require the overhead
// of rotating the matrix...
if (deltaX === -1 || deltaX === 1) {
var emptyCount = 0
// count the empty positions between the player and the boundary
// fill those spaces with dots
for (var i = newPos.x+deltaX ; (i > -1 && i < this.getWidth()); i+=deltaX) {
if (this.board[newPos.y][i] === EMPTY ) {
this.board[newPos.y][i] = BOX
emptyCount ++
}
if (this.board[newPos.y][i] === OBSTACLE) {
break
}
}
// if there are no empty spaces, do nothing
if (emptyCount === 0) {
return false
}
// move the now filled empty spaces to the "front" of the line.
for (var i = newPos.x; (i < newPos.x+emptyCount) && (i > newPos.x-emptyCount); i+=deltaX) {
this.board[newPos.y][i] = EMPTY
}
this.setPlayerPos(newPos.x + deltaX*(emptyCount-1), newPos.y)
return true
} else {
var emptyCount = 0
// count the empty positions between the player and the boundary
// fill those spaces with dots
for (var i = newPos.y+deltaY ; (i > -1 && i < this.getHeight()); i+=deltaY) {
if (this.board[i][newPos.x] === EMPTY ) {
this.board[i][newPos.x] = BOX
emptyCount ++
}
if (this.board[i][newPos.x] === OBSTACLE) {
break
}
}
// if there are no empty spaces, do nothing
if (emptyCount === 0) {
return false
}
// move the now filled empty spaces to the "front" of the line.
for (var i = newPos.y; (i < newPos.y+emptyCount) && (i > newPos.y-emptyCount); i+=deltaY) {
this.board[i][newPos.x] = EMPTY
}
this.setPlayerPos(newPos.x, newPos.y + deltaY*(emptyCount-1))
return true
}
}
}
var Key = {
_pressed: {},
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
isDown: function(keyCode) {
if (this._pressed[keyCode]) {
delete this._pressed[keyCode];
return true
}
return false
},
onKeydown: function(event) {
this._pressed[event.keyCode] = Date.now();
},
onKeyup: function(event) {
delete this._pressed[event.keyCode];
}
};
function updateScene(scene, board) {
for (var i=0;i<board.length;i++) {
for (var j = 0; j < board[i].length; j++) {
switch (board[i][j]) {
case EMPTY:
break
case BOX:
placeNewBoxInScene(scene, {x:j, y: i});
break
case PLAYER:
placeNewPlayerInScene(scene, {x:j, y: i});
break
case OBSTACLE:
placeNewObstacleInScene(scene, {x:j, y: i});
break
default:
}
}
}
}
function placeNewBoxInScene(scene, position) {
// box
var box = new THREE.Mesh( boxGeo, boxMat );
box.position.x=position.x - gridOffset;
box.position.z=position.y - gridOffset;
scene.add( box );
}
function placeNewObstacleInScene(scene, position) {
// obstacle
var box = new THREE.Mesh( boxGeo, obstacleMat );
box.position.x=position.x - gridOffset;
box.position.z=position.y - gridOffset;
scene.add( box );
}
function placeNewPlayerInScene(scene, position) {
// player
var box = new THREE.Mesh( boxGeo, playerMat );
box.position.x=position.x - gridOffset;
box.position.z=position.y - gridOffset;
scene.add( box );
}
window.addEventListener('keyup', function(event) { Key.onKeyup(event); }, false);
window.addEventListener('keydown', function(event) { Key.onKeydown(event); }, false);
var everyOtherTurn = false;
// Setup 3d scene
var boxSize = 1;
var gridOffset = 2.5;
var scene = new THREE.Scene();
//camera
var aspect = window.innerWidth / window.innerHeight;
var d = 5;
var camera = new THREE.OrthographicCamera( - d * aspect, d * aspect, d, - d, 1, 1000 );
camera.position.set( 5, 5, 5 );
camera.lookAt( scene.position );
//light
var dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(100, 75, 30);
scene.add(dirLight);
var renderer = new THREE.WebGLRenderer({antialias : true});
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// grid
var gridHelper = new THREE.GridHelper( 5, 5, 0xffffff, 0x446644 );
gridHelper.position.y = -0.5;
gridHelper.position.x = -0.5;
gridHelper.position.z = -0.5;
scene.add( gridHelper );
updateScene(scene, Board.board)
var animate = function () {
requestAnimationFrame( animate );
var delta = undefined;
if (Key.isDown(Key.UP)) delta = {x:0,y:-1};
if (Key.isDown(Key.LEFT)) delta = {x:-1,y:0};
if (Key.isDown(Key.DOWN)) delta = {x:0,y:1};
if (Key.isDown(Key.RIGHT)) delta = {x:1,y:0};
if (delta) {
var results = Board.handlePlayerMove(delta.x, delta.y)
if (results.didPlayerMove) {
if (everyOtherTurn) {
Board.addRandomBox();
}
everyOtherTurn = !everyOtherTurn
var didClearBoxs = Board.clearFullRowsAndCols();
// Super hacky way to handle updates.
for (var i = scene.children.length - 1; i >= 0 ; i--) {
var child = scene.children[ i ];
if ( child !== gridHelper && child !== camera && child != dirLight ) {
scene.remove(child);
}
}
updateScene(scene, Board.board);
}
}
renderer.render(scene, camera);
};
animate();
})()
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment