From 865ca15511bce71ade62299e93a8f2fc712093c7 Mon Sep 17 00:00:00 2001 From: Gustavo Martin Morcuende Date: Sun, 19 Jul 2015 20:40:48 +0200 Subject: [PATCH] Games: Tetris --- games/tetris/.jscsrc | 47 +++++ games/tetris/.jshintrc | 26 +++ games/tetris/.tern-project | 17 ++ games/tetris/.vimrc | 106 +++++++++++ games/tetris/README.md | 11 ++ games/tetris/index.html | 84 +++++++++ games/tetris/src/functions.js | 403 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 694 insertions(+) create mode 100644 games/tetris/.jscsrc create mode 100644 games/tetris/.jshintrc create mode 100644 games/tetris/.tern-project create mode 100644 games/tetris/.vimrc create mode 100644 games/tetris/README.md create mode 100644 games/tetris/index.html create mode 100644 games/tetris/src/functions.js diff --git a/games/tetris/.jscsrc b/games/tetris/.jscsrc new file mode 100644 index 0000000..5dd0d95 --- /dev/null +++ b/games/tetris/.jscsrc @@ -0,0 +1,47 @@ +{ + "excludeFiles": ["node_modules/**", "bower_components/**"], + "validateIndentation": 2, + "disallowKeywords": ["with", "eval", "continue", "void"], + "disallowKeywordsOnNewLine": ["else"], + "disallowMixedSpacesAndTabs": true, + "disallowMultipleLineStrings": true, + "disallowNewlineBeforeBlockStatements": true, + "disallowSpaceAfterObjectKeys": true, + "disallowSpaceAfterPrefixUnaryOperators": ["++", "--", "+", "-", "~", "!"], + "disallowSpaceBeforeBinaryOperators": [","], + "disallowSpaceBeforePostfixUnaryOperators": ["++", "--"], + "disallowSpacesInAnonymousFunctionExpression": { + "beforeOpeningRoundBrace": true + }, + "disallowSpacesInCallExpression": true, + "disallowSpacesInFunctionDeclaration": { + "beforeOpeningRoundBrace": true + }, + "disallowSpacesInNamedFunctionExpression": { + "beforeOpeningRoundBrace": true + }, + "disallowSpacesInsideArrayBrackets": true, + "requireSpaceBeforeKeywords": [ + "else", + "while", + "catch" + ], + "disallowSpacesInsideParentheses": true, + "disallowTrailingComma": true, + "disallowTrailingWhitespace": true, + "requireCommaBeforeLineBreak": true, + "requireSpaceAfterBinaryOperators": ["?", ":", "+", "-", "/", "*", "%", "==", "===", "!=", "!==", ">", ">=", "<", "<=", "&&", "||"], + "requireSpaceBeforeBinaryOperators": ["?", ":", "+", "-", "/", "*", "%", "==", "===", "!=", "!==", ">", ">=", "<", "<=", "&&", "||"], + "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"], + "requireSpaceBeforeBlockStatements": true, + "requireSpacesInConditionalExpression": { + "afterTest": true, + "beforeConsequent": true, + "afterConsequent": true, + "beforeAlternate": true + }, + "requireSpacesInForStatement": true, + "requireSpacesInFunction": { + "beforeOpeningCurlyBrace": true + } +} diff --git a/games/tetris/.jshintrc b/games/tetris/.jshintrc new file mode 100644 index 0000000..cebc050 --- /dev/null +++ b/games/tetris/.jshintrc @@ -0,0 +1,26 @@ +{ + "node": true, + "browser": true, + "esnext": true, + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "immed": true, + "indent": 2, + "latedef": true, + "newcap": true, + "noarg": true, + "quotmark": "single", + "undef": true, + "unused": true, + "strict": true, + "trailing": true, + "smarttabs": true, + "maxcomplexity": 3, + "globals": { + "angular": false, + "_": true, + "moment": true + } +} diff --git a/games/tetris/.tern-project b/games/tetris/.tern-project new file mode 100644 index 0000000..5e0cc85 --- /dev/null +++ b/games/tetris/.tern-project @@ -0,0 +1,17 @@ +{ + "libs": [ + "browser", + "jquery", + "ecma5", + "underscore" + ], + "loadEagerly": [ + "src/**/*.js", + "./*.js" + ], + "plugins": { + "angular": "./", + "doc_comment": "./", + "complete_strings": "./" + } +} diff --git a/games/tetris/.vimrc b/games/tetris/.vimrc new file mode 100644 index 0000000..036db79 --- /dev/null +++ b/games/tetris/.vimrc @@ -0,0 +1,106 @@ +" *************************** Code-analysis engine for JavaScript *************************** + +"http://ternjs.net/ +filetype plugin indent on +"let g:tern#command = ["node", '~/.vim/bundle/tern_for_vim/autoload' . '/../node_modules/tern/bin/tern', '--persistent'] +"Hay que buscar algo mejor: node ~/.vim/bundle/tern_for_vim/node_modules/tern/bin/tern --persistent --verbose +let g:tern#command = ["nothing"] +let g:tern_request_timeout = 10 + +"https://github.com/marijnh/tern_for_vim +let g:tern_show_argument_hints='on_hold' +let g:tern_map_keys=1 + + + + +" *************************** https://github.com/burnettk/vim-angular *************************** + + +"https://github.com/othree/javascript-libraries-syntax.vim +let g:used_javascript_libs = 'angularjs,angularui,jasmine,jquery' + +"https://github.com/pangloss/vim-javascript +set regexpengine=1 + +"https://github.com/scrooloose/syntastic +let oldstatusline=&statusline "save current configuration +set statusline= +set statusline+=%#WarningMsg# "switch to 'WarningMsg' default theme color. See available colors with command :highlight +set statusline+=%{SyntasticStatuslineFlag()} +set statusline+=%* +let &statusline.=oldstatusline "concatenate configuration +unlet oldstatusline +let g:syntastic_always_populate_loc_list = 1 +let g:syntastic_auto_loc_list = 1 +let g:syntastic_check_on_open = 1 +let g:syntastic_check_on_wq = 0 +"http://jshint.com +let g:syntastic_javascript_checkers = ['jshint', 'jscs'] + + +" *************************** Line numbers *************************** +set number + + +" *************************** Because broken editors are being used by some naïve developers *************************** +" set binary +" set noeol + + +" *************************** Save session on quitting Vim *************************** +autocmd VimLeave * NERDTreeClose +autocmd VimLeave * call MakeSession() + +" *************************** Restore session on starting Vim *************************** +" autocmd VimEnter * call MySessionRestoreFunction() +autocmd VimEnter * call LoadSession() +autocmd VimEnter * NERDTree + +function! MakeSession() + let b:sessiondir = $HOME . "/.vim/sessions" . getcwd() + if (filewritable(b:sessiondir) != 2) + exe 'silent !mkdir -p ' b:sessiondir + redraw! + endif + let b:filename = b:sessiondir . '/session.vim' + exe "mksession! " . b:filename +endfunction + +function! LoadSession() + let b:sessiondir = $HOME . "/.vim/sessions" . getcwd() + let b:sessionfile = b:sessiondir . "/session.vim" + if (filereadable(b:sessionfile)) + exe 'source ' b:sessionfile + else + echo "No session loaded." + endif +endfunction + +map r :NERDTreeFind + + +" *************************** Indentation *************************** +set tabstop=2 +set shiftwidth=2 +set expandtab + + +" *************************** Spelling *************************** +"Corrección ortográfica. En consola :setlocal spell spelllang=es +"Los diccionarios en español hay que bajárselos de aquí: http://ftp.vim.org/vim/runtime/spell/ +"y dejarlos en esta ruta: /usr/share/vim/vim72/spell +"Ver http://plagatux.es/2008/12/correcion-ortografica-en-vim/ +"si queremos tener un fichero donde se vayan agregando las palabras que no sean reconocidas +set spellfile=~/.vim/dict.add +"Para habilitarlo siempre: +"runtime plugins/spellfile.vim <----- Esto no parece necesario :( +"setlocal spell spelllang=es +setlocal spell spelllang=en +" * ]s – Siguiente falta ortográfica +" * [s - Anterior falta ortográfica +" * z= - Mostrar sugerencias para una palabra incorrecta. +" * zg - Añadir una palabra al diccionario. +" * zug - Deshacer la adición de una palabra al diccionario. +" * zw - Eliminar una palabra del diccionario. +"set nospell para deshabilitarlo diff --git a/games/tetris/README.md b/games/tetris/README.md new file mode 100644 index 0000000..a049f63 --- /dev/null +++ b/games/tetris/README.md @@ -0,0 +1,11 @@ +# Using tern: +https://github.com/marijnh/tern_for_vim + +# Run in a different console the tern server (better than being loaded by vim) +~/.vim/bundle/tern_for_vim/node_modules/tern/bin/tern --persistent --verbose --host 127.0.0.1 + +export PATH=/opt/node/node-v0.10.33-linux-x64/bin:$PATH + +# Global installs of systaxis checks +npm install -g jshint +npm install -g jscs diff --git a/games/tetris/index.html b/games/tetris/index.html new file mode 100644 index 0000000..bdb2dda --- /dev/null +++ b/games/tetris/index.html @@ -0,0 +1,84 @@ + + + + Javascript Tetris + + + + + +
+ +
+ + Your browser does not support the <canvas> element + +
+ +
+ + + + + + + diff --git a/games/tetris/src/functions.js b/games/tetris/src/functions.js new file mode 100644 index 0000000..e2a66ed --- /dev/null +++ b/games/tetris/src/functions.js @@ -0,0 +1,403 @@ +'use strict'; + +var KEY = { ESC: 27, SPACE: 32, LEFT: 37, UP: 38, RIGHT: 39, DOWN: 40 }; +var MAPWIDTH = 10; +var MAPHEIGHT = 20; +var TILEWIDTH; +var TILEHEIGHT; +var board; +var boardCanvas; +var boardContext; +var boardUpComingCanvas; +var upComingContext; +var play = false; +var score; +var rows; + + +var shapes = [ + // Tower piece: I + { blocks: [0x0F00, 0x2222, 0x00F0, 0x4444], color: 'red' }, + // The right knight piece: J + { blocks: [0x44C0, 0x8E00, 0x6440, 0x0E20], color: 'white' }, + // The left knight piece: L + { blocks: [0x4460, 0x0E80, 0xC440, 0x2E00], color: 'purple' }, + // The box piece: O + { blocks: [0xCC00, 0xCC00, 0xCC00, 0xCC00], color: 'blue' }, + // The right leaner piece: S + { blocks: [0x06C0, 0x8C40, 0x6C00, 0x4620], color: 'green' }, + // The pyramid piece: T + { blocks: [0x0E40, 0x4C40, 0x4E00, 0x4640], color: 'yellow' }, + // The left leaner piece: Z + { blocks: [0x0C60, 0x4C80, 0xC600, 0x2640], color: 'cyan' } +]; + + + + + +function emptyBoard(m, n) { + var i; + var mat = []; + for (i = 0; i < n; i+= 1) { + mat[i] = []; + } + + return mat; +} + +function resize() { + boardCanvas = document.getElementById('canvas'); + boardCanvas.width = boardCanvas.clientWidth; + boardCanvas.height = boardCanvas.clientHeight; + TILEWIDTH = boardCanvas.width / MAPWIDTH; + TILEHEIGHT = boardCanvas.height / MAPHEIGHT; + boardContext = boardCanvas.getContext('2d'); + + + boardUpComingCanvas = document.getElementById('upcoming'); + boardUpComingCanvas.width = boardUpComingCanvas.clientWidth; + boardUpComingCanvas.height = boardUpComingCanvas.clientHeight; + upComingContext = boardUpComingCanvas.getContext('2d'); +} + +function drawTile(context, color, x, y) { + context.fillStyle = color; + context.fillRect(x * TILEWIDTH, y * TILEHEIGHT, TILEWIDTH, TILEHEIGHT); +} + +function getPositions(piece) { + var positions = []; + var bit; + var row = 0, col = 0; + var blocks = shapes[piece.shape].blocks[piece.rotation]; + + for(bit = 0x8000; bit >= 0x0001; bit = bit >> 1) { + + if (blocks & bit) { + var position = { + x : piece.x + col, + y : piece.y + row + }; + positions.push(position); + } + + col++; + if (col === 4) { + row++; + col = 0; + } + } + + return positions; +} + +function pieceBlocks(piece, fn) { + var i; + var positions = getPositions(piece); + for(i = 0; i < positions.length; i += 1) { + if (fn(positions[i])) { + return true; + } + } + + return false; +} + +function drawPiece(context, piece) { + var color = shapes[piece.shape].color; + pieceBlocks(piece, function(position) { + drawTile(context, color, position.x, position.y); + }); +} + +function setPiece(board, piece) { + var color = shapes[piece.shape].color; + pieceBlocks(piece, function(position) { + board[position.x][position.y] = color; + }); +} + +function checkBoardBoundaries(x, y) { + return x === MAPWIDTH || + x < 0 || + y === MAPHEIGHT; +} + +function checkOtherPieces(board, x, y) { + + if(typeof board[x][y] !== 'undefined') { + return true; + } + + return false; +} + +function checkColision(board, piece) { + return pieceBlocks(piece, function(position) { + if (checkBoardBoundaries(position.x, position.y) || + checkOtherPieces(board, position.x, position.y)) { + return true; + } + + return false; + }); +} + +function drawBoard(board, context, m, n) { + var x, y; + + for (x = 0; x < m; x += 1) { + for (y = 0; y < n; y += 1) { + if(typeof board[x][y] !== 'undefined') { + drawTile(context, board[x][y], x, y); + } + } + } + +} + +function checkRows(board, m, n, fn) { + var filled, x, y, filledLines; + + filledLines = 0; + for (y = 0; y < n; y += 1) { + + filled = true; + for (x = 0; x< m; x += 1) { + if(typeof board[x][y] === 'undefined') { + filled = false; + } + } + + if (filled) { + filledLines++; + fn(board, m, y); + } + } + + if (filledLines !== 0) { + updateScore(filledLines); + updateRows(filledLines); + } +} + +function removeRow(board, m, n) { + var x, y; + for(x = 0; x< m; x += 1) { + for (y = n; y > 0; y -= 1) { + board[x][y] = board[x][y - 1]; + } + } +} + +function updateScore(n) { + // 1: 100 + // 2: 200 + // 3: 400 + // 4: 800 + var newScore = (100*Math.pow(2,n-1)); + score = score + newScore; + document.getElementById('score').innerHTML = ('00000' + Math.floor(score)).slice(-5); +} + +function updateRows(n) { + rows = rows + n; + document.getElementById('rows').innerHTML = rows; +} + +function move(piece, x, y) { + piece.x = piece.x + x; + piece.y = piece.y + y; +} + +function getRandomInt(min, max) { + return Math.floor(Math.random() * (max - min)) + min; +} + +function makeRandomPiece() { + var shape = getRandomInt(0, 7); + var rotation = getRandomInt(0, 4); + var piece = { + shape: shape, + rotation: rotation + }; + + return piece; +} + +// 10 secs +var last = null; +var step = 1000; +var currentPiece; +var upComingPiece; + + + +// TODO: polyfill +function frame(timestamp) { + if (!play) { + return; + } + + if (!last) { + last = timestamp; + } + var progress = timestamp - last; + if (progress >= step) { + + var forwardPiece = { + shape: currentPiece.shape, + rotation: currentPiece.rotation, + x: currentPiece.x, + y: currentPiece.y + 1 + }; + + + if (checkColision(board, forwardPiece)) { + setPiece(board, currentPiece); + + checkRows(board, MAPWIDTH, MAPHEIGHT, function(board, m, n) { + removeRow(board, m, n); + }); + + + currentPiece = upComingPiece; + currentPiece.x = 2; + currentPiece.y = -1; + + upComingPiece = makeRandomPiece(); + upComingPiece.x = 1; + upComingPiece.y = 1; + } + + move(currentPiece, 0, 1); + + boardContext.clearRect(0, 0, boardCanvas.width, boardCanvas.height); + drawBoard(board, boardContext, MAPWIDTH, MAPHEIGHT); + drawPiece(boardContext, currentPiece); + + upComingContext.clearRect(0, 0, boardUpComingCanvas.width, boardUpComingCanvas.height); + drawPiece(upComingContext, upComingPiece); + + last = timestamp; + } + window.requestAnimationFrame(frame); +} + +function init() { + currentPiece = makeRandomPiece(); + currentPiece.x = 2; + currentPiece.y = -1; + upComingPiece = makeRandomPiece(); + upComingPiece.x = 1; + upComingPiece.y = 1; + + score = 0; + document.getElementById('score').innerHTML = '00000'; + + rows = 0; + document.getElementById('rows').innerHTML = '0'; + + board = emptyBoard(MAPWIDTH, MAPHEIGHT); + resize(); + + window.requestAnimationFrame(frame); +} + +function keydown(ev) { + + switch(ev.keyCode) { + case KEY.ESC: + play = false; + ev.preventDefault(); + break; + case KEY.SPACE: + play = true; + init(); + ev.preventDefault(); + break; + } + + if (!play) { + return; + } + var forwardPiece = { + shape: currentPiece.shape, + rotation: currentPiece.rotation, + x: currentPiece.x, + y: currentPiece.y + }; + switch(ev.keyCode) { + case KEY.LEFT: + move(forwardPiece, -1, 0); + if (!checkColision(board, forwardPiece)) { + move(currentPiece, -1, 0); + boardContext.clearRect(0, 0, boardCanvas.width, boardCanvas.height); + drawBoard(board, boardContext, MAPWIDTH, MAPHEIGHT); + drawPiece(boardContext, currentPiece); + } + ev.preventDefault(); + break; + case KEY.RIGHT: + move(forwardPiece, 1, 0); + if (!checkColision(board, forwardPiece)) { + move(currentPiece, 1, 0); + boardContext.clearRect(0, 0, boardCanvas.width, boardCanvas.height); + drawBoard(board, boardContext, MAPWIDTH, MAPHEIGHT); + drawPiece(boardContext, currentPiece); + } + ev.preventDefault(); + break; + case KEY.UP: + var rotation = (forwardPiece.rotation + 1) % 4; + forwardPiece.rotation = rotation; + if (!checkColision(board, forwardPiece)) { + currentPiece.rotation = rotation; + boardContext.clearRect(0, 0, boardCanvas.width, boardCanvas.height); + drawBoard(board, boardContext, MAPWIDTH, MAPHEIGHT); + drawPiece(boardContext, currentPiece); + } + ev.preventDefault(); + break; + case KEY.DOWN: + move(forwardPiece, 0, 1); + if (checkColision(board, forwardPiece)) { + setPiece(board, currentPiece); + + checkRows(board, MAPWIDTH, MAPHEIGHT, function(board, m, n) { + removeRow(board, m, n); + }); + + currentPiece = upComingPiece; + currentPiece.x = 2; + currentPiece.y = -1; + + upComingPiece = makeRandomPiece(); + upComingPiece.x = 1; + upComingPiece.y = 1; + } + + move(currentPiece, 0, 1); + + boardContext.clearRect(0, 0, boardCanvas.width, boardCanvas.height); + drawBoard(board, boardContext, MAPWIDTH, MAPHEIGHT); + drawPiece(boardContext, currentPiece); + + upComingContext.clearRect(0, 0, boardUpComingCanvas.width, boardUpComingCanvas.height); + drawPiece(upComingContext, upComingPiece); + + ev.preventDefault(); + break; + } +} + +function addEvents() { + document.addEventListener('keydown', keydown, false); + window.addEventListener('resize', resize, false); +} + +addEvents(); + + + -- 2.1.4