Skip to content

Instantly share code, notes, and snippets.

@elhardoum
Created December 4, 2020 15:26
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 elhardoum/a89b8bcaa8f63912d6c0f79e2c1cbd56 to your computer and use it in GitHub Desktop.
Save elhardoum/a89b8bcaa8f63912d6c0f79e2c1cbd56 to your computer and use it in GitHub Desktop.
Simple Connect 4 Game with JavaScript
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
}
<!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>
* {
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