// World must define these:
const CLOCKTICK = 100; // speed of run - move things every n milliseconds
const MAXSTEPS = 1000; // length of a run before final score
const SCREENSHOT_STEP = 50;
//---- global constants: -------------------------------------------------------
const width = 32; //size of grid in
const height = 25;
const squaresize = 100; // size of square in pixels
const centreIndexHeight = height/2;
const centreIndexWidth = width/2;
const SKYCOLOR = 0xddffdd; // a number, not a string
const BLANKCOLOR = SKYCOLOR ; // make objects this color until texture arrives (from asynchronous file read)
const startRadiusConst = ((centreIndexHeight + centreIndexWidth) * 20) * 3 ; // distance from centre to start the camera at
const skyboxConst = ((centreIndexHeight + centreIndexWidth) * 20) * 10 ; // where to put skybox
const maxRadiusConst = ((centreIndexHeight + centreIndexWidth) * 20) * 100 ; // maximum distance from camera we will render things
// contents of a grid square
const GRID_BLANK = 0;
const GRID_WALL = 1;
const GRID_PIECE = 2;
const GRID_TETRIS = 3;
//---- set of action controls
const ACTION_ROTATE_R = 0; // up arrow key
const ACTION_ROTATE_L = 1; // down arrow key
const ACTION_MOVE_R = 2; // right arrow key
const ACTION_MOVE_L = 3; // left arrow key
const ACTION_SPEED = 4; // space bar
//---- start of World class -------------------------------------------------------
function World()
{
// most of World can be private
// regular "var" syntax means private variables:
// width = x axis
// height = y axis
var BOXHEIGHT = 1; // 3d or 2d box height
var GRID = new Array(width); // can query GRID about whether squares are occupied, will in fact be initialised as a 2D array
var WALLS = new Array(height * width);
var PIECES = new Array(10);
var GRIDS = new Array(height * width);
var TETRIS = new Array(height * width);
var rotationsCount = 0;
var ord_x = 5;
var ord_y = 4;
var userXinit = 5;
var userYinit = 1;
var currentPieceType;
var nextPieceType;
var currentPiece;
var nextPiece;
var gravity = true;
// --- userGrid and enemyGrid are both initialised, redrawn each time
// --- both are grids 10 x 20 in size.
var userGrid = new Array(20)
for(var i = 0; i < userGrid.length; i++)
{
userGrid[i] = new Array(10);
}
var enemyGrid = userGrid;
var self = this;
function initGrid()
{
for (var i = 0; i < width ; i++)
{
GRID[i] = new Array(height); // each element is an array
for (var j = 0; j < height; j++)
{
GRID[i][j] = GRID_BLANK ;
}
}
}
function translateHeight ( x )
{
return ( x - ( (height * squaresize)/2 ) );
}
function translateWidth ( x )
{
return ( x - ( (width * squaresize)/2 ) );
}
//--- skybox ----------------------------------------------------------------------------------------------
function initSkybox()
{
// x,y,z positive and negative faces have to be in certain order in the array
// mountain skybox, credit:
// http://stemkoski.github.io/Three.js/Skybox.html
var materialArray = [
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_pos_z.jpg" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_neg_z.jpg" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_pos_y.jpg" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_neg_y.jpg" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_pos_x.jpg" ), side: THREE.BackSide } ) ),
( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/starter/sky_neg_x.jpg" ), side: THREE.BackSide } ) ),
];
var skyGeometry = new THREE.CubeGeometry ( skyboxConst, skyboxConst, skyboxConst );
var skyMaterial = new THREE.MeshFaceMaterial ( materialArray );
var theskybox = new THREE.Mesh ( skyGeometry, skyMaterial );
threeworld.scene.add( theskybox ); // We are inside a giant cube
}
function loadWallTexture()
{
var loader = new THREE.TextureLoader();
loader.load ( '/uploads/phm/squareborder.png', function ( thetexture )
{
thetexture.minFilter = THREE.LinearFilter;
paintWalls ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
} );
}
function loadPieceTexture()
{
var loader = new THREE.TextureLoader();
loader.load ( '/uploads/phm/square.jpg', function ( thetexture )
{
thetexture.minFilter = THREE.LinearFilter;
paintPiece ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
} );
}
function loadGridTexture()
{
var loader = new THREE.TextureLoader();
loader.load ( '/uploads/phm/blue.png', function ( thetexture )
{
thetexture.minFilter = THREE.LinearFilter;
paintGrid ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
} );
}
function loadTetrisTexture()
{
var loader = new THREE.TextureLoader();
loader.load ( '/uploads/phm/red.png', function ( thetexture )
{
thetexture.minFilter = THREE.LinearFilter;
paintTetris ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
} );
}
function createCube()
{
var shape = new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
var thecube = new THREE.Mesh( shape );
thecube.material.color.setHex( BLANKCOLOR );
return thecube;
}
// --- add piece objects ----------------------------------------
function getPiece(number, rotations)
{
var opiece0 = [
[1,1],
[1,1]
];
// -- o piece is square in every direction
var jpiece1 = [
[0,1,0],
[0,1,0],
[1,1,0]
];
var jpiece2 = [
[1,0,0],
[1,1,1],
[0,0,0]
];
var jpiece3 = [
[1,1,0],
[1,0,0],
[1,0,0]
];
var jpiece4 = [
[1,1,1],
[0,0,1],
[0,0,0]
];
// -- j piece 4 rotations
var lpiece1 = [
[1,0,0],
[1,0,0],
[1,1,0]
];
var lpiece2 = [
[1,1,1],
[1,0,0],
[0,0,0]
];
var lpiece3 = [
[1,1,0],
[0,1,0],
[0,1,0]
];
var lpiece4 = [
[0,0,1],
[1,1,1],
[0,0,0]
];
// -- l piece 4 rotations
var zpiece1 = [
[0,1,0],
[1,1,0],
[1,0,0]
];
var zpiece2 = [
[1,1,0],
[0,1,1],
[0,0,0]
];
var zpiece3 = [
[0,0,1],
[0,1,1],
[0,1,0]
];
var zpiece4 = [
[0,0,0],
[1,1,0],
[0,1,1]
];
// -- z piece 4 rotations
var spiece1 = [
[1,0,0],
[1,1,0],
[0,1,0]
];
var spiece2 = [
[0,1,1],
[1,1,0],
[0,0,0]
];
var spiece3 = [
[0,1,0],
[0,1,1],
[0,0,1]
];
var spiece4 = [
[0,0,0],
[0,1,1],
[1,1,0]
];
// -- s piece 2 rotations
var tpiece1 = [
[1,0,0],
[1,1,0],
[1,0,0]
];
var tpiece2 = [
[1,1,1],
[0,1,0],
[0,0,0]
];
var tpiece3 = [
[0,0,1],
[0,1,1],
[0,0,1]
];
var tpiece4 = [
[0,1,0],
[1,1,1],
[0,0,0]
];
// -- t piece 4 rotations
var ipiece1 = [
[1,0,0,0],
[1,0,0,0],
[1,0,0,0],
[1,0,0,0]
];
var ipiece2 = [
[0,0,0,0],
[1,1,1,1],
[0,0,0,0],
[0,0,0,0]
];
var ipiece3 = [
[0,0,0,1],
[0,0,0,1],
[0,0,0,1],
[0,0,0,1]
];
var ipiece4 = [
[0,0,0,0],
[0,0,0,0],
[1,1,1,1],
[0,0,0,0]
];
// ^
// |
// x
// o |
// <--y---|
// o is rotation point
// -- i piece 2 rotations
var bag = [
[opiece0],
[jpiece1, jpiece2, jpiece3, jpiece4],
[lpiece1, lpiece2, lpiece3, lpiece4],
[zpiece1, zpiece2, zpiece3, zpiece4],
[spiece1, spiece2, spiece3, spiece4],
[tpiece1, tpiece2, tpiece3, tpiece4],
[ipiece1, ipiece2, ipiece3, ipiece4]
]
var j;
if(number === 0)
{
return bag[number][0]
}
else
{
j = rotations % 4;
return bag[number][j];
}
}
function getRandomPiece()
{
return Math.floor(Math.random() * 7);
}
function rotatePieceRight()
{
rotationsCount++;
return getPiece(currentPieceType, rotationsCount);
}
function rotatePieceLeft()
{
rotationsCount--;
return getPiece(currentPieceType, rotationsCount);
}
function start()
{
drawStartUserGrid();
}
function drawStartUserGrid()
{
currentPieceType = getRandomPiece();
currentPiece = getPiece(currentPieceType, 0);
console.log("currentPiece = " + currentPiece);
nextPieceType = getRandomPiece();
nextPiece = getPiece(nextPieceType, 0);
console.log("nextPiece = " + nextPiece);
drawPiece(0, 0, currentPiece);
drawPiece(0, -3, nextPiece);
reset_ord();
}
function drawNextUserGrid()
{
currentPiece = nextPiece;
currentPieceType = nextPieceType
drawPiece(0, 0, currentPiece);
drawClearNextPiece(nextPiece);
nextPieceType = getRandomPiece();
nextPiece = getPiece(nextPieceType, 0);
drawPiece(0, -3, nextPiece);
reset_ord();
}
function reset_ord()
{
ord_x = 5;
ord_y = 4;
rotationsCount = 0;
}
function drawPiece(x, y, piece)
{
ord_x += x;
ord_y += y;
flushPieces();
var t = 0;
for(var i = 0; i < piece.length; i++)
{
for(var j = 0; j < piece.length; j++)
{
if(piece[i][j] == 1)
{
GRID[ord_x + i][ord_y + j] = GRID_PIECE;
var cube = createCube();
cube.position.x = translateWidth ( (i + ord_x) * squaresize ); // translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
cube.position.z = translateHeight ( (j + ord_y) * squaresize );
cube.position.y = 0;
threeworld.scene.add(cube) ;
PIECES[t] = cube; // save it for later
t++;
}
}
}
loadPieceTexture();
}
function drawClearPiece(piece)
{
flushGrid();
flushPieces();
var t = 0;
for(var i = 0; i < piece.length; i++)
{
for(var j = 0; j < piece.length; j++)
{
if(piece[i][j] == 1)
{
GRID[ord_x + i][ord_y + j] = GRID_BLANK;
var cube = createCube();
cube.position.x = translateWidth ( (i + ord_x) * squaresize ); // translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
cube.position.z = translateHeight ( (j + ord_y) * squaresize );
cube.position.y = 0;
threeworld.scene.add(cube) ;
GRIDS[t] = cube; // save it for later
t++;
}
}
}
loadGridTexture();
}
function drawClearNextPiece(piece)
{
flushGrid();
flushPieces();
var t = 0;
for(var i = 0; i < piece.length; i++)
{
for(var j = 0; j < piece.length; j++)
{
if(piece[i][j] == 1)
{
GRID[ord_x + 0 + i][ord_y -3 + j] = GRID_BLANK;
var cube = createCube();
cube.position.x = translateWidth ( (i + ord_x + 0) * squaresize ); // translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
cube.position.z = translateHeight ( (j + ord_y - 3) * squaresize );
cube.position.y = 0;
threeworld.scene.add(cube) ;
GRIDS[t] = cube; // save it for later
t++;
}
}
}
loadGridTexture();
}
function isHittingBottom(piece)
{
for(var i = 0; i < piece.length; i++)
{
for(var j = 0; j < piece.length; j++)
{
if((piece[i][j] == 1) && (GRID[ord_x + i][ord_y + j + 1]) == GRID_WALL)
{
return true;
}
}
}
return false;
}
function isHittingTetris(piece)
{
for(var i = 0; i < piece.length; i++)
{
for(var j = 0; j < piece.length; j++)
{
if((piece[i][j] == 1) && (GRID[ord_x + i][ord_y + j + 1]) == GRID_TETRIS)
{
return true;
}
}
}
return false;
}
function isHittingSidesRight(piece)
{
for(var j = 0; j < piece.length; j++)
{
if((piece[piece.length - 1][j] == 1) && (GRID[ord_x + piece.length][ord_y + j]) == GRID_WALL)
{
return true;
}
if((piece[piece.length - 1][j] == 1) && (GRID[ord_x + piece.length][ord_y + j]) == GRID_TETRIS)
{
return true;
}
}
return false;
}
function isHittingSidesLeft(piece)
{
for(var j = 0; j < piece.length; j++)
{
if((piece[0][j] == 1) && (GRID[ord_x + 0 - 1][ord_y + j]) == GRID_WALL)
{
return true;
}
if((piece[0][j] == 1) && (GRID[ord_x + 0 - 1][ord_y + j]) == GRID_TETRIS)
{
return true;
}
}
return false;
}
// --- add fixed objects ----------------------------------------
function initWalls()
{
for(var i = 0; i < width; i++)
{
for(var j = 0; j < height; j++)
{
if(
i === 0 ||
i == 11 ||
i == 20 ||
i == width - 1 ||
j === 0 ||
j == 3 && i < 11 ||
j == 3 && i > 20 ||
j == height - 1
)
{
GRID[i][j] = GRID_WALL;
}
}
}
}
function flushTetris()
{
TETRIS = new Array(height * width);
}
function flushPieces()
{
PIECES = new Array(10);
}
function flushGrid()
{
GRIDS = new Array(height * width);
}
function drawThreeScene() // graphical run only, set up blank boxes, painted later
{
var t = 0;
var p = 0;
var q = 0;
var s = 0;
for (var i = 0; i < width; i++)
{
for (var j = 0; j < height ; j++)
{
if ( GRID[i][j] == GRID_WALL )
{
var cube = createCube();
cube.position.x = translateWidth ( i * squaresize ); // translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
cube.position.z = translateHeight ( j * squaresize );
cube.position.y = 0;
threeworld.scene.add(cube) ;
WALLS[t] = cube; // save it for later
t++;
}
if ( GRID[i][j] == GRID_BLANK )
{
var cube = createCube();
cube.position.x = translateWidth ( i * squaresize ); // translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
cube.position.z = translateHeight ( j * squaresize );
cube.position.y = 0;
threeworld.scene.add(cube) ;
GRIDS[p] = cube; // save it for later
p++;
}
if ( GRID[i][j] == GRID_PIECE )
{
var cube = createCube();
cube.position.x = translateWidth ( i * squaresize ); // translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
cube.position.z = translateHeight ( j * squaresize );
cube.position.y = 0;
threeworld.scene.add(cube);
PIECES[q] = cube; // save it for later
q++;
}
if ( GRID[i][j] == GRID_TETRIS )
{
var cube = createCube();
cube.position.x = translateWidth ( i * squaresize ); // translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
cube.position.z = translateHeight ( j * squaresize );
cube.position.y = 0;
threeworld.scene.add(cube);
TETRIS[s] = cube; // save it for later
s++;
}
}
}
loadWallTexture();
loadPieceTexture();
loadTetrisTexture();
loadGridTexture();
}
function paintWalls ( material )
{
for ( var i = 0; i < WALLS.length; i++ )
{
if ( WALLS[i] ) WALLS[i].material = material;
}
}
function paintPiece ( material )
{
for ( var i = 0; i < PIECES.length; i++ )
{
if ( PIECES[i] ) PIECES[i].material = material;
}
}
function paintGrid ( material )
{
for ( var i = 0; i < GRIDS.length; i++ )
{
if ( GRIDS[i] ) GRIDS[i].material = material;
}
}
function paintTetris ( material )
{
for ( var i = 0; i < TETRIS.length; i++ )
{
if ( TETRIS[i] ) TETRIS[i].material = material;
}
}
function drawTetris(piece)
{
var q = 0;
for (var i = 0; i < width ; i++)
{
for (var j = 6; j < height; j++)
{
if(GRID[i][j] == GRID_PIECE)
{
GRID[i][j] = GRID_TETRIS;
}
}
}
}
function drawThreeGrids(base)
{
var q = 0;
var p = 0;
for (var i = 0; i < width - 21 ; i++)
{
for (var j = height - 1; j > base - 1; j--)
{
if(GRID[i][j] == GRID_BLANK)
{
var cube = createCube();
cube.position.x = translateWidth ( i * squaresize ); // translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
cube.position.z = translateHeight ( j * squaresize );
cube.position.y = 0;
threeworld.scene.add(cube);
GRIDS[q] = cube; // save it for later
q++;
}
if(GRID[i][j] == GRID_TETRIS)
{
var cube = createCube();
cube.position.x = translateWidth ( i * squaresize ); // translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
cube.position.z = translateHeight ( j * squaresize );
cube.position.y = 0;
threeworld.scene.add(cube);
TETRIS[p] = cube; // save it for later
p++;
}
loadTetrisTexture();
loadGridTexture();
}
}
}
function keyHandler(e)
// user control
// Note that this.takeAction(a) is constantly running at same time, redrawing the screen.
{
if (e.keyCode == 32)
{
movePiece ( ACTION_SPEED );
console.log("space hit");
} //spacebar
if (e.keyCode == 37)
{
movePiece ( ACTION_MOVE_L );
console.log("left move");
} //left
if (e.keyCode == 38)
{
movePiece ( ACTION_ROTATE_R );
console.log("right rotate");
} //up
if (e.keyCode == 39)
{
movePiece ( ACTION_MOVE_R );
console.log("move right");
} //right
if (e.keyCode == 40)
{
movePiece ( ACTION_ROTATE_L );
console.log("left rotate")
} //down
}
function movePiece(action)
{
switch(action){
case ACTION_MOVE_L:
if(!isHittingSidesLeft(currentPiece) && !isHittingBottom(currentPiece))
{
drawClearPiece(currentPiece);
drawPiece(-1, 0, currentPiece);
break;
}
else
{
break;
}
case ACTION_MOVE_R:
if(!isHittingSidesRight(currentPiece) && !isHittingBottom(currentPiece))
{
drawClearPiece(currentPiece);
drawPiece(1, 0, currentPiece);
break;
}
else
{
break;
}
case ACTION_SPEED:
if(!isHittingTetris(currentPiece) && !isHittingBottom(currentPiece))
{
drawClearPiece(currentPiece);
drawPiece(0, 1, currentPiece);
break;
}
else
{
break;
}
case ACTION_ROTATE_R:
var tmp = rotatePieceRight();
if(
!isHittingTetris(tmp) &&
!isHittingTetris(tmp) &&
!isHittingSidesRight(tmp) &&
!isHittingSidesLeft(tmp)
)
{
drawClearPiece(currentPiece);
console.log("rotationsCount: " + rotationsCount);
currentPiece = tmp;
drawPiece(0, 0, currentPiece);
break;
}
else
{
break;
}
case ACTION_ROTATE_L:
var tmp = rotatePieceLeft();
if(
!isHittingTetris(tmp) &&
!isHittingTetris(tmp) &&
!isHittingSidesRight(tmp) &&
!isHittingSidesLeft(tmp)
)
{
drawClearPiece(currentPiece);
console.log("rotationsCount: " + rotationsCount);
currentPiece = tmp;
drawPiece(0, 0, currentPiece);
break;
}
else
{
break;
}
default:
break;
}
}
function printConsoleGrid()
{
var st = "";
for(var i = 0; i < width; i++)
{
st = "";
for(var j = 0; j < height; j++)
{
st += " " + GRID[i][j];
}
console.log(st);
}
}
function drawGravityPiece(piece)
{
if(!isHittingBottom(piece) && !isHittingTetris(piece))
{
drawClearPiece(piece);
drawPiece(0, 1, piece);
}
else
{
drawTetris(piece);
var d = getGridHeight();
d = height - d - 2;
drawThreeGrids(d);
reset_ord();
clearLines();
printConsoleGrid();
drawNextUserGrid();
//printConsoleGrid();
}
}
function start_Gravity()
{
if(step % 15 == 0)
{
drawGravityPiece(currentPiece);
}
}
// ------------------------------------------------------------------------------------------------
var userLinesCleared = 0;
function clearLines()
{
var l = 10;
for(var j = height - 1; j > 0; j--)
{
if(lineIsEmpty(j))
{
break;
}
for(var i = 1; i < width - 21; i++)
{
if(GRID[i][j] == GRID_TETRIS)
{
l--;
}
}
if(l == 0)
{
userLinesCleared += 1;
for(var x = j; x > 6; x--)
{
if(lineIsEmpty(x))
{
adjustLine(x + 1);
break;
}
adjustLine(x);
}
}
l = 10;
}
}
function adjustLine(h)
{
var x = copyLine(h - 1);
console.log(x);
pasteLine(h, x);
}
function copyLine(h)
{
var t = new Array(10);
for(var i = 1; i < 11; i++)
{
t[i] = GRID[i][h];
}
return t;
}
function pasteLine(h, arr)
{
drawThreeGrids(h);
for(var i = 1; i < 11; i++)
{
GRID[i][h] = arr[i];
}
}
function lineIsEmpty(h)
{
for(var i = 1; i < 11; i++)
{
if(GRID[i][h] != GRID_BLANK)
{
return false;
}
}
return true;
}
function getGridHeight()
{
var gridHeight = 0;
var l = 10;
for(var j = height - 1; j > 5; j--)
{
for(var i = 1; i < width - 19; i++)
{
if(GRID[i][j] == GRID_TETRIS)
{
gridHeight += 1;
l--;
break;
}
}
if(l == 0)
{
break;
}
else
{
l = 10;
}
}
return gridHeight;
}
//--- public functions / interface / API ----------------------------------------------------------
this.endCondition = false; // If set to true, run will end.
this.newRun = function()
{
this.endCondition = false;
step = 0;
// for all runs:
initGrid();
initWalls();
if (true)
{
threeworld.init2d ( startRadiusConst, maxRadiusConst, SKYCOLOR );
// Set up objects first:
start();
drawThreeScene();
document.onkeydown = keyHandler;
}
};
this.getState = function()
{
};
this.nextStep = function()
{
var a = 4;
step++;
if(true)
{
start_Gravity();
if(step % 50 == 0)
{
if(getGridHeight() > 17)
{
this.endCondition = true;
}
}
}
};
this.endRun = function()
{
console.log("Game over");
};
this.getScore = function()
{
return userLinesCleared;
};
}
//---- end of World class -------------------------------------------------------