Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Connect Four Game</title> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<style> | |
:root { | |
--primary-color: #3498db; | |
--player1-color: #e74c3c; | |
--player2-color: #f1c40f; | |
--empty-cell: #ecf0f1; | |
--board-color: #2980b9; | |
--text-color: #2c3e50; | |
--highlight-color: #2ecc71; | |
} | |
* { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
} | |
body { | |
display: flex; | |
flex-direction: column; | |
justify-content: center; | |
align-items: center; | |
min-height: 100vh; | |
background: linear-gradient(135deg, #f5f7fa, #c3cfe2); | |
color: var(--text-color); | |
padding: 20px; | |
} | |
.game-container { | |
width: 100%; | |
max-width: 600px; | |
display: flex; | |
flex-direction: column; | |
align-items: center; | |
gap: 25px; | |
} | |
.header { | |
text-align: center; | |
width: 100%; | |
} | |
.title { | |
font-size: 2.5rem; | |
margin-bottom: 10px; | |
color: var(--primary-color); | |
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1); | |
} | |
.status { | |
font-size: 1.5rem; | |
font-weight: 500; | |
margin-bottom: 15px; | |
min-height: 30px; | |
transition: all 0.3s ease; | |
} | |
.game-board { | |
position: relative; | |
background-color: var(--board-color); | |
border-radius: 10px; | |
padding: 20px; | |
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); | |
} | |
.board { | |
display: grid; | |
grid-template-columns: repeat(7, 1fr); | |
grid-template-rows: repeat(6, 1fr); | |
gap: 10px; | |
perspective: 1000px; | |
} | |
.cell { | |
position: relative; | |
width: 60px; | |
height: 60px; | |
background-color: var(--empty-cell); | |
border-radius: 50%; | |
cursor: pointer; | |
transition: transform 0.3s ease, background-color 0.3s ease; | |
box-shadow: inset 0 5px 0 rgba(0, 0, 0, 0.1); | |
} | |
.cell::before { | |
content: ''; | |
position: absolute; | |
top: 0; | |
left: 0; | |
right: 0; | |
bottom: 0; | |
border-radius: 50%; | |
box-shadow: inset 0 -5px 0 rgba(0, 0, 0, 0.2); | |
z-index: 1; | |
} | |
.cell:hover:empty { | |
transform: translateY(-5px); | |
} | |
.cell.player1 { | |
background-color: var(--player1-color); | |
} | |
.cell.player2 { | |
background-color: var(--player2-color); | |
} | |
.cell.winning { | |
animation: pulse 1s infinite; | |
box-shadow: 0 0 0 5px rgba(46, 204, 113, 0.5); | |
} | |
@keyframes pulse { | |
0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(46, 204, 113, 0.7); } | |
70% { transform: scale(1.05); box-shadow: 0 0 0 10px rgba(46, 204, 113, 0); } | |
100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(46, 204, 113, 0); } | |
} | |
.column-selector { | |
display: flex; | |
justify-content: space-between; | |
margin-bottom: 10px; | |
} | |
.column-btn { | |
width: 60px; | |
height: 30px; | |
background-color: rgba(255, 255, 255, 0.3); | |
border: none; | |
border-radius: 5px 5px 0 0; | |
cursor: pointer; | |
transition: all 0.2s ease; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
color: white; | |
font-weight: bold; | |
} | |
.column-btn:hover { | |
background-color: rgba(255, 255, 255, 0.5); | |
} | |
.column-btn.active { | |
background-color: rgba(255, 255, 255, 0.7); | |
} | |
.players { | |
display: flex; | |
justify-content: space-around; | |
width: 100%; | |
margin-bottom: 20px; | |
} | |
.player { | |
display: flex; | |
align-items: center; | |
gap: 10px; | |
padding: 10px 20px; | |
border-radius: 10px; | |
transition: all 0.3s ease; | |
} | |
.player.active { | |
background-color: rgba(52, 152, 219, 0.2); | |
transform: scale(1.05); | |
} | |
.player-icon { | |
width: 30px; | |
height: 30px; | |
border-radius: 50%; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
color: white; | |
font-weight: bold; | |
} | |
.controls { | |
display: flex; | |
gap: 15px; | |
margin-top: 20px; | |
} | |
.btn { | |
padding: 12px 24px; | |
border: none; | |
border-radius: 8px; | |
font-size: 1rem; | |
font-weight: 600; | |
cursor: pointer; | |
transition: all 0.3s ease; | |
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); | |
display: flex; | |
align-items: center; | |
gap: 8px; | |
} | |
.btn-primary { | |
background-color: var(--primary-color); | |
color: white; | |
} | |
.btn:hover { | |
transform: translateY(-2px); | |
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); | |
} | |
.btn:active { | |
transform: translateY(1px); | |
} | |
.modal { | |
position: fixed; | |
top: 0; | |
left: 0; | |
width: 100%; | |
height: 100%; | |
background-color: rgba(0, 0, 0, 0.8); | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
z-index: 100; | |
opacity: 0; | |
pointer-events: none; | |
transition: opacity 0.3s ease; | |
} | |
.modal.show { | |
opacity: 1; | |
pointer-events: all; | |
} | |
.modal-content { | |
background-color: white; | |
border-radius: 10px; | |
padding: 30px; | |
max-width: 400px; | |
width: 90%; | |
text-align: center; | |
transform: scale(0.8); | |
transition: transform 0.3s ease; | |
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.3); | |
} | |
.modal.show .modal-content { | |
transform: scale(1); | |
} | |
.modal h2 { | |
margin-bottom: 20px; | |
color: var(--primary-color); | |
} | |
.modal p { | |
margin-bottom: 30px; | |
font-size: 1.2rem; | |
} | |
.winner-icon { | |
font-size: 3rem; | |
margin-bottom: 20px; | |
} | |
.player1-icon { | |
color: var(--player1-color); | |
} | |
.player2-icon { | |
color: var(--player2-color); | |
} | |
.tie-icon { | |
color: var(--text-color); | |
} | |
@media (max-width: 600px) { | |
.cell { | |
width: 40px; | |
height: 40px; | |
} | |
.column-btn { | |
width: 40px; | |
} | |
.title { | |
font-size: 2rem; | |
} | |
.status { | |
font-size: 1.2rem; | |
} | |
} | |
</style> | |
</head> | |
<body> | |
<div class="game-container"> | |
<div class="header"> | |
<h1 class="title">Connect Four</h1> | |
<div class="status" id="status">Player 1's Turn</div> | |
</div> | |
<div class="players"> | |
<div class="player active" id="player1"> | |
<div class="player-icon" style="background-color: var(--player1-color);"> | |
<i class="fas fa-user"></i> | |
</div> | |
<span>Player 1</span> | |
</div> | |
<div class="player" id="player2"> | |
<div class="player-icon" style="background-color: var(--player2-color);"> | |
<i class="fas fa-user"></i> | |
</div> | |
<span>Player 2</span> | |
</div> | |
</div> | |
<div class="game-board"> | |
<div class="column-selector" id="column-selector"> | |
<!-- Column selectors will be added by JavaScript --> | |
</div> | |
<div class="board" id="board"> | |
<!-- Cells will be added by JavaScript --> | |
</div> | |
</div> | |
<div class="controls"> | |
<button class="btn btn-primary" id="restart-btn"> | |
<i class="fas fa-redo"></i> Restart Game | |
</button> | |
</div> | |
</div> | |
<div class="modal" id="modal"> | |
<div class="modal-content"> | |
<div class="winner-icon" id="winner-icon"> | |
<i class="fas fa-trophy"></i> | |
</div> | |
<h2 id="modal-title">Game Over</h2> | |
<p id="modal-text">Player 1 wins!</p> | |
<button class="btn btn-primary" id="play-again-btn"> | |
<i class="fas fa-play"></i> Play Again | |
</button> | |
</div> | |
</div> | |
<script> | |
document.addEventListener('DOMContentLoaded', () => { | |
// Game elements | |
const board = document.getElementById('board'); | |
const columnSelector = document.getElementById('column-selector'); | |
const statusDisplay = document.getElementById('status'); | |
const player1Display = document.getElementById('player1'); | |
const player2Display = document.getElementById('player2'); | |
const modal = document.getElementById('modal'); | |
const modalTitle = document.getElementById('modal-title'); | |
const modalText = document.getElementById('modal-text'); | |
const winnerIcon = document.getElementById('winner-icon'); | |
// Buttons | |
const restartBtn = document.getElementById('restart-btn'); | |
const playAgainBtn = document.getElementById('play-again-btn'); | |
// Game variables | |
const ROWS = 6; | |
const COLS = 7; | |
let currentPlayer = 1; | |
let gameBoard = Array(ROWS).fill().map(() => Array(COLS).fill(0)); | |
let gameActive = true; | |
let lastMoveRow = -1; | |
let lastMoveCol = -1; | |
// Create the game board | |
function createBoard() { | |
board.innerHTML = ''; | |
columnSelector.innerHTML = ''; | |
// Create column selection buttons | |
for (let col = 0; col < COLS; col++) { | |
const colBtn = document.createElement('button'); | |
colBtn.classList.add('column-btn'); | |
colBtn.dataset.col = col; | |
colBtn.innerHTML = `<i class="fas fa-arrow-down"></i>`; | |
colBtn.addEventListener('click', () => makeMove(col)); | |
columnSelector.appendChild(colBtn); | |
} | |
// Create game cells | |
for (let row = 0; row < ROWS; row++) { | |
for (let col = 0; col < COLS; col++) { | |
const cell = document.createElement('div'); | |
cell.classList.add('cell'); | |
cell.dataset.row = row; | |
cell.dataset.col = col; | |
board.appendChild(cell); | |
} | |
} | |
} | |
// Make a move in the specified column | |
function makeMove(col) { | |
if (!gameActive) return; | |
// Find the first empty row in the column | |
for (let row = ROWS - 1; row >= 0; row--) { | |
if (gameBoard[row][col] === 0) { | |
// Update game state | |
gameBoard[row][col] = currentPlayer; | |
lastMoveRow = row; | |
lastMoveCol = col; | |
// Update UI | |
const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`); | |
cell.classList.add(`player${currentPlayer}`); | |
// Check for win or draw | |
if (checkWin()) { | |
endGame(`Player ${currentPlayer} wins!`); | |
highlightWinningCells(); | |
} else if (isBoardFull()) { | |
endGame("It's a draw!"); | |
} else { | |
// Switch players | |
currentPlayer = currentPlayer === 1 ? 2 : 1; | |
updateStatus(); | |
} | |
return; | |
} | |
} | |
} | |
// Check if the last move resulted in a win | |
function checkWin() { | |
if (lastMoveRow === -1 || lastMoveCol === -1) return false; | |
const directions = [ | |
[0, 1], // Horizontal | |
[1, 0], // Vertical | |
[1, 1], // Diagonal down-right | |
[1, -1] // Diagonal down-left | |
]; | |
for (const [dx, dy] of directions) { | |
let count = 1; | |
// Check in positive direction | |
for (let i = 1; i < 4; i++) { | |
const newRow = lastMoveRow + i * dx; | |
const newCol = lastMoveCol + i * dy; | |
if (newRow < 0 || newRow >= ROWS || newCol < 0 || newCol >= COLS) break; | |
if (gameBoard[newRow][newCol] !== currentPlayer) break; | |
count++; | |
} | |
// Check in negative direction | |
for (let i = 1; i < 4; i++) { | |
const newRow = lastMoveRow - i * dx; | |
const newCol = lastMoveCol - i * dy; | |
if (newRow < 0 || newRow >= ROWS || newCol < 0 || newCol >= COLS) break; | |
if (gameBoard[newRow][newCol] !== currentPlayer) break; | |
count++; | |
} | |
if (count >= 4) return true; | |
} | |
return false; | |
} | |
// Highlight the winning cells | |
function highlightWinningCells() { | |
const winningCells = findWinningCells(); | |
if (!winningCells) return; | |
winningCells.forEach(([row, col]) => { | |
const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`); | |
cell.classList.add('winning'); | |
}); | |
} | |
// Find the winning cells (returns undefined if no win) | |
function findWinningCells() { | |
if (lastMoveRow === -1 || lastMoveCol === -1) return; | |
const directions = [ | |
[0, 1], // Horizontal | |
[1, 0], // Vertical | |
[1, 1], // Diagonal down-right | |
[1, -1] // Diagonal down-left | |
]; | |
for (const [dx, dy] of directions) { | |
let cells = [[lastMoveRow, lastMoveCol]]; | |
// Check in positive direction | |
for (let i = 1; i < 4; i++) { | |
const newRow = lastMoveRow + i * dx; | |
const newCol = lastMoveCol + i * dy; | |
if (newRow < 0 || newRow >= ROWS || newCol < 0 || newCol >= COLS) break; | |
if (gameBoard[newRow][newCol] !== currentPlayer) break; | |
cells.push([newRow, newCol]); | |
} | |
// Check in negative direction | |
for (let i = 1; i < 4; i++) { | |
const newRow = lastMoveRow - i * dx; | |
const newCol = lastMoveCol - i * dy; | |
if (newRow < 0 || newRow >= ROWS || newCol < 0 || newCol >= COLS) break; | |
if (gameBoard[newRow][newCol] !== currentPlayer) break; | |
cells.push([newRow, newCol]); | |
} | |
if (cells.length >= 4) { | |
return cells.slice(0, 4); // Return first 4 cells in a row | |
} | |
} | |
return; | |
} | |
// Check if the board is full (draw) | |
function isBoardFull() { | |
return gameBoard.every(row => row.every(cell => cell !== 0)); | |
} | |
// End the game with the given message | |
function endGame(message) { | |
gameActive = false; | |
// Show modal | |
modalTitle.textContent = message.includes('wins') ? 'Winner!' : 'Game Over'; | |
modalText.textContent = message; | |
// Set winner icon | |
if (message.includes('Player 1')) { | |
winnerIcon.className = 'winner-icon player1-icon'; | |
winnerIcon.innerHTML = '<i class="fas fa-trophy"></i>'; | |
} else if (message.includes('Player 2')) { | |
winnerIcon.className = 'winner-icon player2-icon'; | |
winnerIcon.innerHTML = '<i class="fas fa-trophy"></i>'; | |
} else { | |
winnerIcon.className = 'winner-icon tie-icon'; | |
winnerIcon.innerHTML = '<i class="fas fa-handshake"></i>'; | |
} | |
modal.classList.add('show'); | |
} | |
// Update the game status display | |
function updateStatus() { | |
statusDisplay.textContent = `Player ${currentPlayer}'s Turn`; | |
// Update active player highlight | |
if (currentPlayer === 1) { | |
player1Display.classList.add('active'); | |
player2Display.classList.remove('active'); | |
} else { | |
player1Display.classList.remove('active'); | |
player2Display.classList.add('active'); | |
} | |
} | |
// Reset the game | |
function resetGame() { | |
gameBoard = Array(ROWS).fill().map(() => Array(COLS).fill(0)); | |
currentPlayer = 1; | |
gameActive = true; | |
lastMoveRow = -1; | |
lastMoveCol = -1; | |
// Reset UI | |
document.querySelectorAll('.cell').forEach(cell => { | |
cell.className = 'cell'; | |
}); | |
updateStatus(); | |
modal.classList.remove('show'); | |
} | |
// Event listeners | |
restartBtn.addEventListener('click', resetGame); | |
playAgainBtn.addEventListener('click', resetGame); | |
// Initialize the game | |
createBoard(); | |
updateStatus(); | |
// Keyboard controls | |
document.addEventListener('keydown', (e) => { | |
if (!gameActive) return; | |
// 1-7 keys for columns | |
if (e.key >= '1' && e.key <= '7') { | |
const col = parseInt(e.key) - 1; | |
if (col >= 0 && col < COLS) { | |
makeMove(col); | |
} | |
} | |
// Left/Right arrow keys for column selection | |
else if (e.key === 'ArrowLeft' || e.key === 'ArrowRight') { | |
const colBtns = document.querySelectorAll('.column-btn'); | |
let currentCol = 0; | |
// Find the currently selected column (if any) | |
const activeBtn = document.querySelector('.column-btn.active'); | |
if (activeBtn) { | |
currentCol = parseInt(activeBtn.dataset.col); | |
} | |
// Determine new selection | |
let newCol = currentCol; | |
if (e.key === 'ArrowLeft') { | |
newCol = Math.max(0, currentCol - 1); | |
} else if (e.key === 'ArrowRight') { | |
newCol = Math.min(COLS - 1, currentCol + 1); | |
} | |
// Update selection | |
colBtns.forEach(btn => btn.classList.remove('active')); | |
colBtns[newCol].classList.add('active'); | |
} | |
// Space or Enter to make move in selected column | |
else if (e.key === ' ' || e.key === 'Enter') { | |
const activeBtn = document.querySelector('.column-btn.active'); | |
if (activeBtn) { | |
makeMove(parseInt(activeBtn.dataset.col)); | |
} | |
} | |
}); | |
// Hide selection highlight when clicking outside | |
document.addEventListener('click', (e) => { | |
if (!e.target.classList.contains('column-btn')) { | |
document.querySelectorAll('.column-btn').forEach(btn => { | |
btn.classList.remove('active'); | |
}); | |
} | |
}); | |
}); | |
</script> | |
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: absolute; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">This website has been generated by <a href="https://enzostvs-deepsite.hf.space" style="color: #fff;" target="_blank" >DeepSite</a> <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;"></p></body> | |
</html> |