Created
December 4, 2020 15:26
-
-
Save elhardoum/a89b8bcaa8f63912d6c0f79e2c1cbd56 to your computer and use it in GitHub Desktop.
Simple Connect 4 Game with JavaScript
This file contains hidden or 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
const game_elem = document.getElementById('game') | |
, game = JSON.parse(JSON.stringify(new Array(game_elem.childElementCount-1).fill( | |
new Array(game_elem.children[0].childElementCount).fill(null) | |
))) | |
const colors = { 1: 'red', 2: 'green' } | |
, color_style = document.getElementById('bg-styles') | |
, status = document.getElementById('status') | |
let current_player = 1 | |
// initial player color | |
color_style.textContent = color_style.textContent.replace('inherit', colors[current_player]) | |
game_elem.querySelectorAll('.row:first-child span').forEach(function(span, col_index) | |
{ | |
span.addEventListener('click', function() | |
{ | |
let available_spot = undefined | |
game.forEach(function(game_col, i) | |
{ | |
if ( null === game_col[col_index] ) | |
available_spot = i | |
}) | |
// a spot is available, reserve it for current player | |
if ( undefined !== available_spot ) { | |
game[available_spot][col_index] = current_player | |
const is_last_option = 0 === available_spot | |
fill_spot(available_spot, col_index, current_player, is_last_option, function() | |
{ | |
const winner = check_winner(1) ? 1 : ( check_winner(2) ? 2 : null ) | |
if ( winner ) { | |
status.style.display = '' | |
status.querySelector('p').textContent = `Player ${winner} wins!` | |
status.children[0].children[0].style.backgroundColor = colors[winner] | |
} | |
// check if all options are filled and no winner is determined, sorry for ES6, running out of time | |
if ( ! game.map(x => x.filter(n => null === n)).filter(x => x.length).length ) { | |
status.style.display = '' | |
status.querySelector('p').textContent = 'Ended without a winner!' | |
} | |
}) | |
// next player | |
current_player = 1 !== current_player ? 1 : 2 | |
// swap colors | |
color_style.textContent = color_style.textContent.replace(new RegExp(Object.values(colors).join('|'), 'g'), colors[current_player]) | |
} | |
}, false) | |
}) | |
function fill_spot( row, column, player, last_option, then ) | |
{ | |
const filler_ball = game_elem.querySelectorAll('.row:first-child > div > span')[column] | |
, target_ball = game_elem.querySelectorAll('.row')[row+1].querySelectorAll('div > span')[column] | |
filler_ball.style.cssText = 'background:' + colors[player] + ';position:relative;top:0px' | |
const intervalId = setInterval(function() | |
{ | |
const new_top = filler_ball.offsetTop + Math.min(25, target_ball.offsetTop - filler_ball.offsetTop) | |
filler_ball.style.top = new_top + 'px' | |
if ( filler_ball.offsetTop >= target_ball.offsetTop ) { | |
clearInterval(intervalId) | |
filler_ball.style.cssText = '' | |
target_ball.style.cssText = 'background: ' + colors[player] | |
if ( last_option ) { // all positions filled in column, disable filler ball for that column | |
filler_ball.classList.add('disabled') | |
} | |
'function' == typeof then && then() | |
} | |
}, 20) | |
} | |
function check_winner( player ) | |
{ | |
// horizontal checks | |
for ( let row=0; row<game.length; row++ ) { | |
for ( let col=0; col<game[row].length; col++ ) { | |
try { | |
if ( JSON.stringify([ game[row][col], game[row][col+1], game[row][col+2], game[row][col+3] ]) == JSON.stringify(new Array(4).fill(player)) ) | |
return true | |
} catch (err) {} | |
} | |
} | |
// vertical checks | |
for ( let col=0; col<game[0].length; col++ ) { | |
for ( let row=0; row<game.length; row++ ) { | |
try { | |
if ( JSON.stringify([ game[row][col], game[row+1][col], game[row+2][col], game[row+3][col] ]) == JSON.stringify(new Array(4).fill(player)) ) | |
return true | |
} catch (err) {} | |
} | |
} | |
// NW-SE diagonal | |
for ( let row=0; row<game.length; row++ ) { | |
for ( let col=0; col<game[row].length; col++ ) { | |
try { | |
if ( JSON.stringify([ game[row][col], game[row+1][col+1], game[row+2][col+2], game[row+3][col+3] ]) == JSON.stringify(new Array(4).fill(player)) ) | |
return true | |
} catch (err) {} | |
} | |
} | |
// SW-NE diagonal | |
for ( let row=0; row<game.length; row++ ) { | |
for ( let col=game[row].length; col>=0; col-- ) { | |
try { | |
if ( JSON.stringify([ game[row][col], game[row+1][col-1], game[row+2][col-2], game[row+3][col-3] ]) == JSON.stringify(new Array(4).fill(player)) ) | |
return true | |
} catch (err) {} | |
} | |
} | |
return false | |
} |
This file contains hidden or 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" /> | |
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /> | |
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | |
<title>Connect 4 Game</title> | |
<link rel="stylesheet" type="text/css" href="./style.css" /> | |
<style type="text/css" id="bg-styles"> | |
#game .row:first-child > div:hover span:not(.disabled), | |
#game .row:first-child > div:active span:not(.disabled) { background: inherit } | |
</style> | |
</head> | |
<body> | |
<div id="game"> | |
<div class="row"> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
</div> | |
<div class="row"> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
</div> | |
<div class="row"> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
</div> | |
<div class="row"> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
</div> | |
<div class="row"> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
</div> | |
<div class="row"> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
</div> | |
<div class="row"> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
<div><span></span></div> | |
</div> | |
</div> | |
<div id="status" style="display:none"> | |
<div> | |
<div> | |
<p></p> | |
<button onclick="location.reload()">Play Again</button> | |
</div> | |
</div> | |
</div> | |
<script type="text/javascript" src="./game.js"></script> | |
</body> | |
</html> |
This file contains hidden or 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
* { | |
margin: 0; | |
padding: 0; | |
} | |
#game { | |
height: 100vh; | |
max-width: 100vh; | |
margin: 0 auto; | |
max-height: 100vw; | |
display: flex; | |
flex-wrap: wrap; | |
} | |
#game .row { | |
display: flex; | |
width: 100%; | |
} | |
#game .row > div { | |
flex: 1; | |
border: 2px solid #cacaca; | |
background: #cacaca; | |
} | |
#game .row:first-child > div { | |
border: none; | |
cursor: pointer; | |
background: none; | |
} | |
#game .row > div > span { | |
height: 100%; | |
width: 100%; | |
display: flex; | |
border-radius: 50%; | |
background: white; | |
} | |
#status { | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background: rgb(202, 202, 202, 0.33); | |
} | |
#status > div { | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
height: 100%; | |
} | |
#status > div > div { | |
background: #000; | |
color: #fff; | |
font-family: sans-serif; | |
padding: 1rem; | |
font-size: 120%; | |
text-align: center; | |
box-shadow: -1px -1px 20px #999; | |
} | |
#status > div > div > button { | |
margin-top: 1rem; | |
width: 100%; | |
padding: 4px; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment