connectfour / index.html
2O24dpower2024's picture
Add 2 files
9cffdfd verified
<!DOCTYPE html>
<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>