diff --git a/JavaScript/Tetris/.idea/.gitignore b/JavaScript/Tetris/.idea/.gitignore
new file mode 100644
index 0000000..b58b603
--- /dev/null
+++ b/JavaScript/Tetris/.idea/.gitignore
@@ -0,0 +1,5 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/JavaScript/Tetris/README.md b/JavaScript/Tetris/README.md
new file mode 100644
index 0000000..30db4a4
--- /dev/null
+++ b/JavaScript/Tetris/README.md
@@ -0,0 +1,3 @@
+# Tetris
+A vanilla javascript game
+
diff --git a/JavaScript/Tetris/css/style.css b/JavaScript/Tetris/css/style.css
new file mode 100644
index 0000000..8919ca1
--- /dev/null
+++ b/JavaScript/Tetris/css/style.css
@@ -0,0 +1,237 @@
+:root {
+ font-size: 0.625em;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ font-family: "Montserrat", sans-serif;
+ font-size: 1.6rem;
+ margin: auto;
+ max-width: 60rem;
+ color: #d8edea;
+ background: radial-gradient(
+ circle,
+ rgba(175, 196, 174, 1) 0%,
+ rgba(104, 204, 191, 1) 89%,
+ rgba(94, 191, 178, 1) 100%
+ );
+}
+
+header {
+ text-align: center;
+ margin-top: 3rem;
+}
+
+h1 {
+ color: #fff;
+}
+
+div {
+ height: 2rem;
+ width: 2rem;
+}
+
+/* some utility classes */
+.t-ucase {
+ text-transform: uppercase;
+}
+
+.t-big {
+ font-size: 1.5em;
+}
+
+.t-wide {
+ letter-spacing: 1.5rem;
+}
+
+.t-close {
+ letter-spacing: 1rem;
+}
+
+.fw-300 {
+ font-weight: 300;
+}
+
+.fw-400 {
+ font-weight: 400;
+}
+
+.score-display {
+ font-size: 5rem;
+ color: rgb(133, 121, 107, 0.5);
+}
+
+.game-area {
+ display: flex;
+ justify-content: center;
+}
+
+.game {
+ height: 0;
+ width: 300px;
+}
+
+.grid {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ width: 20rem;
+ height: 40rem;
+}
+
+.previous-shape {
+ width: 10rem;
+ padding-left: 2rem;
+ margin-top: -5rem;
+}
+
+.previous-grid {
+ display: flex;
+ flex-wrap: wrap;
+ width: 8rem;
+ height: 8rem;
+}
+
+.block {
+ background-image: url(../images/blue_block.png);
+}
+
+.block2 {
+ background-image: url(../images/purple_block.png);
+}
+
+.block3 {
+ background-image: url(../images/green_block.png);
+}
+
+.block4 {
+ background-image: url(../images/navy_block.png);
+}
+
+.block5 {
+ background-image: url(../images/pink_block.png);
+}
+
+.end {
+ background-color: #d8edea;
+}
+
+.button {
+ position: relative;
+ width: 22rem;
+ height: 2.2rem;
+ text-align: center;
+ color: #fff;
+ letter-spacing: 1px;
+ text-decoration: none;
+ line-height: 23px;
+ font-size: 10px;
+ display: block;
+ margin: 30px;
+ text-shadow: -1px -1px 0 #a84155;
+ background: #d25068;
+ border: 1px solid #d25068;
+ width: 12rem;
+ background-image: linear-gradient(to bottom, #f66c7b, #d25068);
+ border-radius: 5px;
+ box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5) inset,
+ 0 -1px 0 rgba(255, 255, 255, 0.1) inset, 0 4px 0 #ad4257,
+ 0 4px 2px rgba(0, 0, 0, 0.5);
+}
+
+.button:before {
+ background: #f0f0f0;
+ background-image: linear-gradient(#d0d0d0, #f0f0f0);
+ border-radius: 5px;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5) inset, 0 1px 0 #fff;
+ position: absolute;
+ content: "";
+ left: -6px;
+ right: -6px;
+ top: -6px;
+ bottom: -10px;
+ z-index: -1;
+}
+
+.button:active {
+ box-shadow: 0 1px 0 rgba(255, 255, 255, 0.5) inset,
+ 0 -1px 0 rgba(255, 255, 255, 0.1) inset;
+ top: 5px;
+}
+
+.button:active:before {
+ top: -11px;
+ bottom: -5px;
+ content: "";
+}
+
+.button:hover {
+ background: #f66c7b;
+ background-image: linear-gradient(top, #d25068, #f66c7b);
+}
+
+.end {
+ background-image: url(/images/blue_block.png);
+}
+
+.display {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ align-items: center;
+ text-align: center;
+ margin-top: 1rem;
+ width: 17.5rem;
+ height: 25rem;
+ background: #f0f0f0;
+ background-image: linear-gradient(#d0d0d0, #f0f0f0);
+ border-radius: 5px;
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5) inset, 0 1px 0 #fff;
+ color: #85796b;
+}
+
+.score,
+.lines-display {
+ padding-top: 1rem;
+ font-size: 1.2rem;
+}
+
+.menu {
+ display: flex;
+ justify-content: center;
+ position: fixed;
+ z-index: 1;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 100%;
+ overflow: auto;
+ background-color: rgba(24, 39, 51, 0.85);
+}
+
+.menu-content {
+ text-align: center;
+ width: 600px;
+ align-items: center;
+ margin-top: 230px;
+ justify-content: center;
+ height: 200vh;
+ border-radius: 50%;
+ transition: all 0.8s ease;
+}
+
+.menu-content {
+ width: 200vw;
+}
+
+.rules {
+ font-size: 12px;
+ transition: color 0.4s ease;
+}
+
+.key {
+ color: #f8de7e;
+}
diff --git a/JavaScript/Tetris/images/Tetris Game.gif b/JavaScript/Tetris/images/Tetris Game.gif
new file mode 100644
index 0000000..c9d462d
Binary files /dev/null and b/JavaScript/Tetris/images/Tetris Game.gif differ
diff --git a/JavaScript/Tetris/images/blue_block.png b/JavaScript/Tetris/images/blue_block.png
new file mode 100644
index 0000000..b0c5504
Binary files /dev/null and b/JavaScript/Tetris/images/blue_block.png differ
diff --git a/JavaScript/Tetris/images/green_block.png b/JavaScript/Tetris/images/green_block.png
new file mode 100644
index 0000000..6787340
Binary files /dev/null and b/JavaScript/Tetris/images/green_block.png differ
diff --git a/JavaScript/Tetris/images/navy_block.png b/JavaScript/Tetris/images/navy_block.png
new file mode 100644
index 0000000..162dfee
Binary files /dev/null and b/JavaScript/Tetris/images/navy_block.png differ
diff --git a/JavaScript/Tetris/images/peach_block.png b/JavaScript/Tetris/images/peach_block.png
new file mode 100644
index 0000000..ff5ed22
Binary files /dev/null and b/JavaScript/Tetris/images/peach_block.png differ
diff --git a/JavaScript/Tetris/images/pink_block.png b/JavaScript/Tetris/images/pink_block.png
new file mode 100644
index 0000000..df75566
Binary files /dev/null and b/JavaScript/Tetris/images/pink_block.png differ
diff --git a/JavaScript/Tetris/images/purple_block.png b/JavaScript/Tetris/images/purple_block.png
new file mode 100644
index 0000000..7357150
Binary files /dev/null and b/JavaScript/Tetris/images/purple_block.png differ
diff --git a/JavaScript/Tetris/images/yellow_block.png b/JavaScript/Tetris/images/yellow_block.png
new file mode 100644
index 0000000..3538460
Binary files /dev/null and b/JavaScript/Tetris/images/yellow_block.png differ
diff --git a/JavaScript/Tetris/index.html b/JavaScript/Tetris/index.html
new file mode 100644
index 0000000..3a40bca
--- /dev/null
+++ b/JavaScript/Tetris/index.html
@@ -0,0 +1,38 @@
+
+
+
+
+ Tetris!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Your Score
0
+
+
+
Lines:0
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/JavaScript/Tetris/js/app.js b/JavaScript/Tetris/js/app.js
new file mode 100644
index 0000000..053fe09
--- /dev/null
+++ b/JavaScript/Tetris/js/app.js
@@ -0,0 +1,257 @@
+document.addEventListener('DOMContentLoaded', () => {
+ // TODO: we can also get the grid size from user
+ const GRID_WIDTH = 10
+ const GRID_HEIGHT = 20
+ const GRID_SIZE = GRID_WIDTH * GRID_HEIGHT
+
+ // no need to type 200 divs :)
+ const grid = createGrid();
+ let squares = Array.from(grid.querySelectorAll('div'))
+ const scoreDisplay = document.querySelector('.score-display')
+ const linesDisplay = document.querySelector('.lines-score')
+ let currentIndex = 0
+ let currentRotation = 0
+ const width = 10
+ let score = 0
+ let lines = 0
+ let timerId
+ let nextRandom = 0
+ const colors = [
+ 'url(images/blue_block.png)',
+ 'url(images/pink_block.png)',
+ 'url(images/purple_block.png)',
+ 'url(images/peach_block.png)',
+ 'url(images/yellow_block.png)'
+ ]
+
+
+ function createGrid() {
+ // the main grid
+ let grid = document.querySelector(".grid")
+ for (let i = 0; i < GRID_SIZE; i++) {
+ let gridElement = document.createElement("div")
+ grid.appendChild(gridElement)
+ }
+
+ // set base of grid
+ for (let i = 0; i < GRID_WIDTH; i++) {
+ let gridElement = document.createElement("div")
+ gridElement.setAttribute("class", "block3")
+ grid.appendChild(gridElement)
+ }
+
+ let previousGrid = document.querySelector(".previous-grid")
+ // Since 16 is the max grid size in which all the Tetrominoes
+ // can fit in we create one here
+ for (let i = 0; i < 16; i++) {
+ let gridElement = document.createElement("div")
+ previousGrid.appendChild(gridElement);
+ }
+ return grid;
+ }
+
+
+ //assign functions to keycodes
+ function control(e) {
+ if (e.keyCode === 39)
+ moveright()
+ else if (e.keyCode === 38)
+ rotate()
+ else if (e.keyCode === 37)
+ moveleft()
+ else if (e.keyCode === 40)
+ moveDown()
+ }
+
+ // the classical behavior is to speed up the block if down button is kept pressed so doing that
+ document.addEventListener('keydown', control)
+
+ //The Tetrominoes
+ const lTetromino = [
+ [1, GRID_WIDTH + 1, GRID_WIDTH * 2 + 1, 2],
+ [GRID_WIDTH, GRID_WIDTH + 1, GRID_WIDTH + 2, GRID_WIDTH * 2 + 2],
+ [1, GRID_WIDTH + 1, GRID_WIDTH * 2 + 1, GRID_WIDTH * 2],
+ [GRID_WIDTH, GRID_WIDTH * 2, GRID_WIDTH * 2 + 1, GRID_WIDTH * 2 + 2]
+ ]
+
+ const zTetromino = [
+ [0, GRID_WIDTH, GRID_WIDTH + 1, GRID_WIDTH * 2 + 1],
+ [GRID_WIDTH + 1, GRID_WIDTH + 2, GRID_WIDTH * 2, GRID_WIDTH * 2 + 1],
+ [0, GRID_WIDTH, GRID_WIDTH + 1, GRID_WIDTH * 2 + 1],
+ [GRID_WIDTH + 1, GRID_WIDTH + 2, GRID_WIDTH * 2, GRID_WIDTH * 2 + 1]
+ ]
+
+ const tTetromino = [
+ [1, GRID_WIDTH, GRID_WIDTH + 1, GRID_WIDTH + 2],
+ [1, GRID_WIDTH + 1, GRID_WIDTH + 2, GRID_WIDTH * 2 + 1],
+ [GRID_WIDTH, GRID_WIDTH + 1, GRID_WIDTH + 2, GRID_WIDTH * 2 + 1],
+ [1, GRID_WIDTH, GRID_WIDTH + 1, GRID_WIDTH * 2 + 1]
+ ]
+
+ const oTetromino = [
+ [0, 1, GRID_WIDTH, GRID_WIDTH + 1],
+ [0, 1, GRID_WIDTH, GRID_WIDTH + 1],
+ [0, 1, GRID_WIDTH, GRID_WIDTH + 1],
+ [0, 1, GRID_WIDTH, GRID_WIDTH + 1]
+ ]
+
+ const iTetromino = [
+ [1, GRID_WIDTH + 1, GRID_WIDTH * 2 + 1, GRID_WIDTH * 3 + 1],
+ [GRID_WIDTH, GRID_WIDTH + 1, GRID_WIDTH + 2, GRID_WIDTH + 3],
+ [1, GRID_WIDTH + 1, GRID_WIDTH * 2 + 1, GRID_WIDTH * 3 + 1],
+ [GRID_WIDTH, GRID_WIDTH + 1, GRID_WIDTH + 2, GRID_WIDTH + 3]
+ ]
+
+ const theTetrominoes = [lTetromino, zTetromino, tTetromino, oTetromino, iTetromino]
+
+ //Randomly Select Tetromino
+ let random = Math.floor(Math.random() * theTetrominoes.length)
+ let current = theTetrominoes[random][currentRotation]
+
+
+ //move the Tetromino moveDown
+ let currentPosition = 4
+
+ //draw the shape
+ function draw() {
+ current.forEach(index => {
+ squares[currentPosition + index].classList.add('block')
+ squares[currentPosition + index].style.backgroundImage = colors[random]
+ })
+ }
+
+ //undraw the shape
+ function undraw() {
+ current.forEach(index => {
+ squares[currentPosition + index].classList.remove('block')
+ squares[currentPosition + index].style.backgroundImage = 'none'
+ })
+ }
+
+ //move down on loop
+ function moveDown() {
+ undraw()
+ currentPosition = currentPosition += width
+ draw()
+ freeze()
+ }
+
+ startBtn.addEventListener('click', () => {
+ if (timerId) {
+ clearInterval(timerId)
+ timerId = null
+ } else {
+ draw()
+ timerId = setInterval(moveDown, 1000)
+ nextRandom = Math.floor(Math.random() * theTetrominoes.length)
+ displayShape()
+ }
+ })
+
+ //move left and prevent collisions with shapes moving left
+ function moveright() {
+ undraw()
+ const isAtRightEdge = current.some(index => (currentPosition + index) % width === width - 1)
+ if (!isAtRightEdge) currentPosition += 1
+ if (current.some(index => squares[currentPosition + index].classList.contains('block2'))) {
+ currentPosition -= 1
+ }
+ draw()
+ }
+
+ //move right and prevent collisions with shapes moving right
+ function moveleft() {
+ undraw()
+ const isAtLeftEdge = current.some(index => (currentPosition + index) % width === 0)
+ if (!isAtLeftEdge) currentPosition -= 1
+ if (current.some(index => squares[currentPosition + index].classList.contains('block2'))) {
+ currentPosition += 1
+ }
+ draw()
+ }
+
+ //freeze the shape
+ function freeze() {
+ // if block has settled
+ if (current.some(index => squares[currentPosition + index + width].classList.contains('block3') || squares[currentPosition + index + width].classList.contains('block2'))) {
+ // make it block2
+ current.forEach(index => squares[index + currentPosition].classList.add('block2'))
+ // start a new tetromino falling
+ random = nextRandom
+ nextRandom = Math.floor(Math.random() * theTetrominoes.length)
+ current = theTetrominoes[random][currentRotation]
+ currentPosition = 4
+ draw()
+ displayShape()
+ addScore()
+ gameOver()
+ }
+ }
+
+ freeze()
+
+ //Rotate the Tetromino
+ function rotate() {
+ undraw()
+ currentRotation++
+ if (currentRotation === current.length) {
+ currentRotation = 0
+ }
+ current = theTetrominoes[random][currentRotation]
+ draw()
+ }
+
+ //Game Over
+ function gameOver() {
+ if (current.some(index => squares[currentPosition + index].classList.contains('block2'))) {
+ scoreDisplay.innerHTML = 'end'
+ clearInterval(timerId)
+ }
+ }
+
+ //show previous tetromino in scoreDisplay
+ const displayWidth = 4
+ const displaySquares = document.querySelectorAll('.previous-grid div')
+ let displayIndex = 0
+
+ const smallTetrominoes = [
+ [1, displayWidth + 1, displayWidth * 2 + 1, 2], /* lTetromino */
+ [0, displayWidth, displayWidth + 1, displayWidth * 2 + 1], /* zTetromino */
+ [1, displayWidth, displayWidth + 1, displayWidth + 2], /* tTetromino */
+ [0, 1, displayWidth, displayWidth + 1], /* oTetromino */
+ [1, displayWidth + 1, displayWidth * 2 + 1, displayWidth * 3 + 1] /* iTetromino */
+ ]
+
+ function displayShape() {
+ displaySquares.forEach(square => {
+ square.classList.remove('block')
+ square.style.backgroundImage = 'none'
+ })
+ smallTetrominoes[nextRandom].forEach(index => {
+ displaySquares[displayIndex + index].classList.add('block')
+ displaySquares[displayIndex + index].style.backgroundImage = colors[nextRandom]
+ })
+ }
+
+ //Add score
+ function addScore() {
+ for (currentIndex = 0; currentIndex < GRID_SIZE; currentIndex += GRID_WIDTH) {
+ const row = [currentIndex, currentIndex + 1, currentIndex + 2, currentIndex + 3, currentIndex + 4, currentIndex + 5, currentIndex + 6, currentIndex + 7, currentIndex + 8, currentIndex + 9]
+ if (row.every(index => squares[index].classList.contains('block2'))) {
+ score += 10
+ lines += 1
+ scoreDisplay.innerHTML = score
+ linesDisplay.innerHTML = lines
+ row.forEach(index => {
+ squares[index].style.backgroundImage = 'none'
+ squares[index].classList.remove('block2') || squares[index].classList.remove('block')
+
+ })
+ //splice array
+ const squaresRemoved = squares.splice(currentIndex, width)
+ squares = squaresRemoved.concat(squares)
+ squares.forEach(cell => grid.appendChild(cell))
+ }
+ }
+ }
+})
\ No newline at end of file