fiveinrow / index.html
gkiwi's picture
Add 3 files
4773d38 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Gomoku - Five in a Row</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
.board {
display: grid;
grid-template-columns: repeat(15, 1fr);
grid-template-rows: repeat(15, 1fr);
gap: 1px;
background-color: #d4a76a;
padding: 10px;
border-radius: 5px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.cell {
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(255, 255, 255, 0.8);
position: relative;
cursor: pointer;
}
.cell::before, .cell::after {
content: '';
position: absolute;
background-color: #333;
}
.cell::before {
width: 100%;
height: 1px;
}
.cell::after {
width: 1px;
height: 100%;
}
.stone {
width: 80%;
height: 80%;
border-radius: 50%;
z-index: 10;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.black {
background: radial-gradient(circle at 30% 30%, #666, #000);
}
.white {
background: radial-gradient(circle at 30% 30%, #fff, #ccc);
}
.last-move {
box-shadow: 0 0 0 3px rgba(255, 215, 0, 0.7);
}
@keyframes fadeIn {
from { opacity: 0; transform: scale(0.8); }
to { opacity: 1; transform: scale(1); }
}
.animate-stone {
animation: fadeIn 0.3s ease-out;
}
</style>
</head>
<body class="bg-gray-100 min-h-screen flex flex-col items-center justify-center p-4">
<div class="max-w-4xl w-full">
<h1 class="text-3xl font-bold text-center mb-6 text-gray-800">Gomoku - Five in a Row</h1>
<div class="flex flex-col md:flex-row gap-6 items-center md:items-start">
<!-- Game board -->
<div class="board w-full max-w-xl mx-auto">
<!-- Cells will be generated by JavaScript -->
</div>
<!-- Game controls and info -->
<div class="w-full md:w-64 bg-white p-4 rounded-lg shadow-md">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center">
<div class="w-6 h-6 rounded-full bg-black mr-2"></div>
<span class="font-medium">Black</span>
</div>
<span id="black-score" class="font-bold">0</span>
</div>
<div class="flex items-center justify-between mb-6">
<div class="flex items-center">
<div class="w-6 h-6 rounded-full bg-white border border-gray-300 mr-2"></div>
<span class="font-medium">White</span>
</div>
<span id="white-score" class="font-bold">0</span>
</div>
<div class="mb-6">
<div class="text-center py-2 px-4 rounded-md bg-gray-100 font-medium">
Current turn: <span id="current-turn" class="font-bold">Black</span>
</div>
</div>
<div class="mb-6">
<div class="text-center py-2 px-4 rounded-md bg-blue-100 text-blue-800 font-medium hidden" id="game-status">
Game in progress
</div>
</div>
<div class="flex flex-col gap-2">
<button id="new-game" class="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-md transition">
New Game
</button>
<button id="undo-move" class="w-full bg-gray-200 hover:bg-gray-300 text-gray-800 py-2 px-4 rounded-md transition">
Undo Move
</button>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const boardSize = 15;
const board = document.querySelector('.board');
const currentTurnDisplay = document.getElementById('current-turn');
const blackScoreDisplay = document.getElementById('black-score');
const whiteScoreDisplay = document.getElementById('white-score');
const gameStatusDisplay = document.getElementById('game-status');
const newGameBtn = document.getElementById('new-game');
const undoMoveBtn = document.getElementById('undo-move');
let gameState = {
board: Array(boardSize).fill().map(() => Array(boardSize).fill(null)),
currentPlayer: 'black',
gameOver: false,
moveHistory: [],
scores: { black: 0, white: 0 },
lastMove: null
};
// Initialize the board
function initBoard() {
board.innerHTML = '';
board.style.gridTemplateColumns = `repeat(${boardSize}, 1fr)`;
board.style.gridTemplateRows = `repeat(${boardSize}, 1fr)`;
for (let row = 0; row < boardSize; row++) {
for (let col = 0; col < boardSize; col++) {
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.row = row;
cell.dataset.col = col;
cell.addEventListener('click', () => makeMove(row, col));
board.appendChild(cell);
}
}
}
// Make a move
function makeMove(row, col) {
if (gameState.gameOver || gameState.board[row][col] !== null) return;
gameState.board[row][col] = gameState.currentPlayer;
gameState.moveHistory.push({ row, col, player: gameState.currentPlayer });
gameState.lastMove = { row, col };
renderBoard();
if (checkWin(row, col)) {
gameState.gameOver = true;
gameState.scores[gameState.currentPlayer]++;
updateScores();
gameStatusDisplay.textContent = `${gameState.currentPlayer === 'black' ? 'Black' : 'White'} wins!`;
gameStatusDisplay.classList.remove('hidden');
gameStatusDisplay.classList.remove('bg-blue-100', 'text-blue-800');
gameStatusDisplay.classList.add('bg-green-100', 'text-green-800');
return;
}
if (checkDraw()) {
gameState.gameOver = true;
gameStatusDisplay.textContent = "It's a draw!";
gameStatusDisplay.classList.remove('hidden');
gameStatusDisplay.classList.remove('bg-blue-100', 'text-blue-800');
gameStatusDisplay.classList.add('bg-yellow-100', 'text-yellow-800');
return;
}
gameState.currentPlayer = gameState.currentPlayer === 'black' ? 'white' : 'black';
currentTurnDisplay.textContent = gameState.currentPlayer === 'black' ? 'Black' : 'White';
}
// Render the board
function renderBoard() {
for (let row = 0; row < boardSize; row++) {
for (let col = 0; col < boardSize; col++) {
const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
cell.innerHTML = '';
if (gameState.board[row][col]) {
const stone = document.createElement('div');
stone.className = `stone ${gameState.board[row][col]} animate-stone`;
// Highlight the last move
if (gameState.lastMove && gameState.lastMove.row === row && gameState.lastMove.col === col) {
stone.classList.add('last-move');
}
cell.appendChild(stone);
}
}
}
}
// Check for a win
function checkWin(row, col) {
const directions = [
[0, 1], // horizontal
[1, 0], // vertical
[1, 1], // diagonal down-right
[1, -1] // diagonal down-left
];
const player = gameState.board[row][col];
for (const [dx, dy] of directions) {
let count = 1;
// Check in positive direction
for (let i = 1; i <= 4; i++) {
const newRow = row + i * dx;
const newCol = col + i * dy;
if (
newRow >= 0 && newRow < boardSize &&
newCol >= 0 && newCol < boardSize &&
gameState.board[newRow][newCol] === player
) {
count++;
} else {
break;
}
}
// Check in negative direction
for (let i = 1; i <= 4; i++) {
const newRow = row - i * dx;
const newCol = col - i * dy;
if (
newRow >= 0 && newRow < boardSize &&
newCol >= 0 && newCol < boardSize &&
gameState.board[newRow][newCol] === player
) {
count++;
} else {
break;
}
}
if (count >= 5) {
return true;
}
}
return false;
}
// Check for a draw
function checkDraw() {
for (let row = 0; row < boardSize; row++) {
for (let col = 0; col < boardSize; col++) {
if (gameState.board[row][col] === null) {
return false;
}
}
}
return true;
}
// Undo the last move
function undoMove() {
if (gameState.moveHistory.length === 0 || gameState.gameOver) return;
const lastMove = gameState.moveHistory.pop();
gameState.board[lastMove.row][lastMove.col] = null;
gameState.currentPlayer = lastMove.player;
gameState.gameOver = false;
gameState.lastMove = gameState.moveHistory.length > 0 ?
{ row: gameState.moveHistory[gameState.moveHistory.length - 1].row,
col: gameState.moveHistory[gameState.moveHistory.length - 1].col } : null;
currentTurnDisplay.textContent = gameState.currentPlayer === 'black' ? 'Black' : 'White';
gameStatusDisplay.classList.add('hidden');
renderBoard();
}
// Start a new game
function newGame() {
gameState = {
board: Array(boardSize).fill().map(() => Array(boardSize).fill(null)),
currentPlayer: 'black',
gameOver: false,
moveHistory: [],
scores: gameState.scores,
lastMove: null
};
currentTurnDisplay.textContent = 'Black';
gameStatusDisplay.classList.add('hidden');
renderBoard();
}
// Update scores
function updateScores() {
blackScoreDisplay.textContent = gameState.scores.black;
whiteScoreDisplay.textContent = gameState.scores.white;
}
// Event listeners
newGameBtn.addEventListener('click', newGame);
undoMoveBtn.addEventListener('click', undoMove);
// Initialize the game
initBoard();
updateScores();
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=gkiwi/fiveinrow" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>