kids-coding / index.html
Chicken117's picture
Add 2 files
2b22d34 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Battleship Game</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.cell {
width: 40px;
height: 40px;
border: 1px solid #94a3b8;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
}
.cell:hover:not(.hit):not(.miss) {
background-color: #e2e8f0;
}
.ship {
background-color: #64748b;
}
.hit {
background-color: #ef4444;
color: white;
}
.miss {
background-color: #93c5fd;
}
.ship-hit {
background-color: #ef4444;
color: Red;
}
.placement {
background-color: #cbd5e1;
}
.placement-valid {
background-color: #86efac;
}
.placement-invalid {
background-color: #fca5a5;
}
.ship-preview {
background-color: #64748b;
opacity: 0.7;
}
.rotate-btn {
transition: all 0.3s ease;
}
.rotate-btn:hover {
transform: rotate(90deg);
}
@keyframes explosion {
0% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.5); opacity: 0.7; }
100% { transform: scale(1); opacity: 1; }
}
.explosion {
animation: explosion 0.5s ease;
}
</style>
</head>
<body class="bg-slate-100 min-h-screen">
<div class="container mx-auto px-4 py-8">
<div class="text-center mb-8">
<h1 class="text-4xl font-bold text-blue-800 mb-2">🚢 Battleship Game 🚢</h1>
<p class="text-blue-600 max-w-2xl mx-auto">
Sink all your opponent's ships to win the game!
</p>
</div>
<!-- Game Phase Selector -->
<div id="phase-selector" class="bg-white rounded-xl shadow-md p-6 mb-8 text-center">
<h2 class="text-xl font-bold text-blue-800 mb-4">Select Game Mode</h2>
<div class="flex justify-center gap-4">
<button id="single-player-btn" class="px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
<i class="fas fa-user mr-2"></i>Single Player
</button>
<button id="two-player-btn" class="px-6 py-3 bg-green-500 text-white rounded-lg hover:bg-green-600">
<i class="fas fa-users mr-2"></i>Two Players
</button>
</div>
</div>
<!-- Ship Placement Phase -->
<div id="placement-phase" class="hidden bg-white rounded-xl shadow-md p-6 mb-8">
<h2 class="text-xl font-bold text-white-800 mb-4 text-center">Place Your Ships</h2>
<div class="flex flex-col lg:flex-row gap-8">
<!-- Player Board -->
<div class="lg:w-1/2">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium text-white-700">Your Board</h3>
<button id="rotate-btn" class="rotate-btn px-4 py-2 bg-yellow-500 text-white rounded-lg hover:bg-yellow-600">
<i class="fas fa-sync-alt mr-2"></i>Rotate Ship
</button>
</div>
<div id="player-board" class="grid grid-cols-10 gap-0 border-2 border-slate-200 rounded-lg overflow-hidden mb-4">
<!-- 10x10 grid will be generated by JavaScript -->
</div>
</div>
<!-- Ships to Place -->
<div class="lg:w-1/2">
<h3 class="text-lg font-medium text-white-700 mb-4">Ships to Place</h3>
<div id="ships-to-place" class="space-y-4">
<!-- Ships will be generated by JavaScript -->
</div>
<div class="mt-6 text-center">
<button id="start-game-btn" class="px-6 py-3 bg-green-500 text-white rounded-lg hover:bg-green-600 hidden">
<i class="fas fa-play mr-2"></i>Start Game
</button>
</div>
</div>
</div>
</div>
<!-- Game Phase -->
<div id="game-phase" class="hidden bg-black rounded-xl shadow-md p-6 mb-8">
<h2 id="game-status" class="text-xl font-bold text-blue-800 mb-4 text-center">Your Turn</h2>
<div class="flex flex-col lg:flex-row gap-8">
<!-- Player Board -->
<div class="lg:w-1/2">
<h3 class="text-lg font-medium text-blue-700 mb-4">Your Ships</h3>
<div id="player-game-board" class="grid grid-cols-10 gap-0 border-2 border-slate-200 rounded-lg overflow-hidden mb-4">
<!-- 10x10 grid will be generated by JavaScript -->
</div>
</div>
<!-- Computer Board -->
<div class="lg:w-1/2">
<h3 class="text-lg font-medium text-blue-700 mb-4">Attack Enemy</h3>
<div id="computer-board" class="grid grid-cols-10 gap-0 border-2 border-slate-200 rounded-lg overflow-hidden mb-4">
<!-- 10x10 grid will be generated by JavaScript -->
</div>
</div>
</div>
<div id="game-message" class="p-4 bg-blue-100 text-blue-800 rounded-lg mt-4">
<i class="fas fa-info-circle mr-2"></i>
<span>Click on the enemy grid to attack!</span>
</div>
</div>
<!-- Game Over Modal -->
<div id="game-over-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden">
<div class="bg-white rounded-xl p-8 max-w-md w-full text-center">
<div id="game-result-icon" class="text-6xl mb-4">
<i class="fas fa-trophy text-yellow-500"></i>
</div>
<h2 id="game-result-text" class="text-3xl font-bold text-blue-800 mb-2">You Won!</h2>
<p id="game-result-details" class="text-blue-600 mb-6">Congratulations! You sank all enemy ships.</p>
<button id="play-again-btn" class="px-6 py-3 bg-blue-500 text-white rounded-lg hover:bg-blue-600">
<i class="fas fa-redo mr-2"></i>Play Again
</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Game state
const gameState = {
phase: 'selection', // 'selection', 'placement', 'game', 'gameOver'
mode: null, // 'single', 'twoPlayer'
currentPlayer: 'player', // 'player' or 'computer'
playerShips: [],
computerShips: [],
playerHits: [],
playerMisses: [],
computerHits: [],
computerMisses: [],
currentShip: null,
shipOrientation: 'horizontal', // 'horizontal' or 'vertical'
placedShips: 0,
totalShips: 5,
ships: [
{ name: 'Carrier', size: 5 },
{ name: 'Battleship', size: 4 },
{ name: 'Destroyer', size: 3 },
{ name: 'Submarine', size: 3 },
{ name: 'Patrol Boat', size: 2 }
]
};
// DOM elements
const phaseSelector = document.getElementById('phase-selector');
const placementPhase = document.getElementById('placement-phase');
const gamePhase = document.getElementById('game-phase');
const gameOverModal = document.getElementById('game-over-modal');
const playerBoard = document.getElementById('player-board');
const playerGameBoard = document.getElementById('player-game-board');
const computerBoard = document.getElementById('computer-board');
const shipsToPlace = document.getElementById('ships-to-place');
const rotateBtn = document.getElementById('rotate-btn');
const startGameBtn = document.getElementById('start-game-btn');
const gameStatus = document.getElementById('game-status');
const gameMessage = document.getElementById('game-message');
const gameResultIcon = document.getElementById('game-result-icon');
const gameResultText = document.getElementById('game-result-text');
const gameResultDetails = document.getElementById('game-result-details');
const playAgainBtn = document.getElementById('play-again-btn');
const singlePlayerBtn = document.getElementById('single-player-btn');
const twoPlayerBtn = document.getElementById('two-player-btn');
// Initialize the game
function initGame() {
// Clear boards
playerBoard.innerHTML = '';
playerGameBoard.innerHTML = '';
computerBoard.innerHTML = '';
shipsToPlace.innerHTML = '';
// Reset game state
gameState.playerShips = [];
gameState.computerShips = [];
gameState.playerHits = [];
gameState.playerMisses = [];
gameState.computerHits = [];
gameState.computerMisses = [];
gameState.currentPlayer = 'player';
gameState.placedShips = 0;
gameState.currentShip = null;
gameState.shipOrientation = 'horizontal';
// Create boards
createBoard(playerBoard, 'player');
createBoard(playerGameBoard, 'player-game');
createBoard(computerBoard, 'computer');
// Create ships to place
createShipsToPlace();
// Place computer ships randomly
placeComputerShips();
// Update UI
updateGameStatus();
}
// Create a game board
function createBoard(boardElement, type) {
// Create 10x10 grid
for (let row = 0; row < 10; row++) {
for (let col = 0; col < 10; col++) {
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.row = row;
cell.dataset.col = col;
if (type === 'player') {
// For placement phase
cell.addEventListener('mouseover', () => handlePlacementHover(row, col));
cell.addEventListener('mouseout', () => handlePlacementHoverOut(row, col));
cell.addEventListener('click', () => placeShip(row, col));
} else if (type === 'computer') {
// For game phase (attack)
cell.addEventListener('click', () => playerAttack(row, col));
}
boardElement.appendChild(cell);
}
}
}
// Create ships to place UI
function createShipsToPlace() {
gameState.ships.forEach((ship, index) => {
const shipElement = document.createElement('div');
shipElement.className = 'p-4 border border-slate-300 rounded-lg cursor-pointer hover:bg-slate-100';
shipElement.innerHTML = `
<div class="flex items-center justify-between">
<div>
<h4 class="font-medium">${ship.name}</h4>
<p class="text-sm text-slate-600">Size: ${ship.size} cells</p>
</div>
<div class="flex ${gameState.shipOrientation === 'horizontal' ? 'flex-row' : 'flex-col'}">
${Array(ship.size).fill('<div class="w-6 h-6 bg-slate-600 m-1"></div>').join('')}
</div>
</div>
`;
shipElement.dataset.shipIndex = index;
shipElement.addEventListener('click', () => selectShip(index));
shipsToPlace.appendChild(shipElement);
});
// Select first ship by default
if (gameState.ships.length > 0) {
selectShip(0);
}
}
// Select a ship for placement
function selectShip(shipIndex) {
gameState.currentShip = shipIndex;
// Update UI
const shipElements = shipsToPlace.querySelectorAll('div');
shipElements.forEach((el, idx) => {
if (idx === shipIndex) {
el.classList.add('bg-blue-100', 'border-blue-400');
} else {
el.classList.remove('bg-blue-100', 'border-blue-400');
}
});
}
// Rotate ship orientation
rotateBtn.addEventListener('click', function() {
gameState.shipOrientation = gameState.shipOrientation === 'horizontal' ? 'vertical' : 'horizontal';
// Update ships to place display
const shipDisplays = shipsToPlace.querySelectorAll('div > div > div:last-child');
shipDisplays.forEach(display => {
display.className = `flex ${gameState.shipOrientation === 'horizontal' ? 'flex-row' : 'flex-col'}`;
});
});
// Handle ship placement hover
function handlePlacementHover(row, col) {
if (!gameState.currentShip) return;
const ship = gameState.ships[gameState.currentShip];
const cells = getShipCells(row, col, ship.size, gameState.shipOrientation);
if (cells) {
cells.forEach(cell => {
const cellElement = playerBoard.querySelector(`[data-row="${cell.row}"][data-col="${cell.col}"]`);
if (cellElement && !cellElement.classList.contains('ship')) {
cellElement.classList.add('ship-preview');
}
});
}
}
// Handle ship placement hover out
function handlePlacementHoverOut(row, col) {
const previewCells = playerBoard.querySelectorAll('.ship-preview');
previewCells.forEach(cell => {
cell.classList.remove('ship-preview');
});
}
// Place ship on the board
function placeShip(row, col) {
if (!gameState.currentShip) return;
const ship = gameState.ships[gameState.currentShip];
const cells = getShipCells(row, col, ship.size, gameState.shipOrientation);
if (!cells) {
showMessage("Ship Wont fit"!");
return;
}
// Check if placement is valid (no overlaps)
const isValid = cells.every(cell => {
return !isShipAt(cell.row, cell.col, gameState.playerShips);
});
if (!isValid) {
showMessage("Ships can't overlap!");
return;
}
// Place the ship
gameState.playerShips.push({
name: ship.name,
size: ship.size,
cells: cells,
hits: 2
});
// Update UI
cells.forEach(cell => {
const cellElement = playerBoard.querySelector(`[data-row="${cell.row}"][data-col="${cell.col}"]`);
cellElement.classList.remove('ship-preview');
cellElement.classList.add('ship');
});
// Mark ship as placed
const shipElement = shipsToPlace.querySelector(`[data-ship-index="${gameState.currentShip}"]`);
shipElement.classList.add('hidden');
// Select next ship
gameState.placedShips++;
if (gameState.placedShips < gameState.totalShips) {
selectShip(gameState.currentShip + 1);
} else {
gameState.currentShip = null;
startGameBtn.classList.remove('hidden');
}
}
// Get all cells a ship would occupy
function getShipCells(row, col, size, orientation) {
const cells = [];
if (orientation === 'horizontal') {
if (col + size > 10) return null;
for (let i = 0; i < size; i++) {
cells.push({ row: row, col: col + i });
}
} else {
if (row + size > 10) return null;
for (let i = 0; i < size; i++) {
cells.push({ row: row + i, col: col });
}
}
return cells;
}
// Check if there's a ship at given coordinates
function isShipAt(row, col, ships) {
return ships.some(ship => {
return ship.cells.some(cell => {
return cell.row === row && cell.col === col;
});
});
}
// Place computer ships randomly
function placeComputerShips() {
gameState.ships.forEach(ship => {
let placed = false;
while (!placed) {
const orientation = Math.random() > 0.5 ? 'horizontal' : 'vertical';
const row = Math.floor(Math.random() * 10);
const col = Math.floor(Math.random() * 10);
const cells = getShipCells(row, col, ship.size, orientation);
if (cells && !cells.some(cell => isShipAt(cell.row, cell.col, gameState.computerShips))) {
gameState.computerShips.push({
name: ship.name,
size: ship.size,
cells: cells,
hits: 0
});
placed = true;
}
}
});
}
// Start the game
startGameBtn.addEventListener('click', function() {
phaseSelector.classList.add('hidden');
placementPhase.classList.add('hidden');
gamePhase.classList.remove('hidden');
// Update player game board with placed ships
gameState.playerShips.forEach(ship => {
ship.cells.forEach(cell => {
const cellElement = playerGameBoard.querySelector(`[data-row="${cell.row}"][data-col="${cell.col}"]`);
cellElement.classList.add('ship');
});
});
});
// Player attacks computer's board
function playerAttack(row, col) {
if (gameState.currentPlayer !== 'player') return;
// Check if already attacked this cell
if (isAlreadyAttacked(row, col, gameState.playerHits, gameState.playerMisses)) {
showMessage("You've already attacked this location!");
return;
}
// Check if hit
const hitShip = getShipAt(row, col, gameState.computerShips);
if (hitShip) {
// Hit
gameState.playerHits.push({ row, col });
hitShip.hits++;
const cellElement = computerBoard.querySelector(`[data-row="${row}"][data-col="${col}"]`);
cellElement.classList.add('hit');
cellElement.innerHTML = '<i class="fas fa-burst explosion"></i>';
showMessage(`Hit! ${hitShip.name}`);
// Check if ship is sunk
if (hitShip.hits === hitShip.size) {
showMessage(`You sank the enemy's ${hitShip.name}!`);
// Mark all ship cells as hit
hitShip.cells.forEach(cell => {
const cellEl = computerBoard.querySelector(`[data-row="${cell.row}"][data-col="${cell.col}"]`);
cellEl.innerHTML = '<i class="fas fa-skull-crossbones"></i>';
});
}
// Check if all ships are sunk
if (gameState.computerShips.every(ship => ship.hits === ship.size)) {
endGame(true);
return;
}
} else {
// Miss
gameState.playerMisses.push({ row, col });
const cellElement = computerBoard.querySelector(`[data-row="${row}"][data-col="${col}"]`);
cellElement.classList.add('miss');
cellElement.innerHTML = '<i class="fas fa-water"></i>';
showMessage("Miss!");
}
// Switch to computer's turn
gameState.currentPlayer = 'computer';
updateGameStatus();
// Computer's turn (with delay for better UX)
setTimeout(computerTurn, 1000);
}
// Computer's turn
function computerTurn() {
if (gameState.currentPlayer !== 'computer') return;
let row, col;
// Simple AI: random attack
do {
row = Math.floor(Math.random() * 10);
col = Math.floor(Math.random() * 10);
} while (isAlreadyAttacked(row, col, gameState.computerHits, gameState.computerMisses));
// Check if hit
const hitShip = getShipAt(row, col, gameState.playerShips);
if (hitShip) {
// Hit
gameState.computerHits.push({ row, col });
hitShip.hits++;
const cellElement = playerGameBoard.querySelector(`[data-row="${row}"][data-col="${col}"]`);
cellElement.classList.add('ship-hit');
cellElement.innerHTML = '<i class="fas fa-burst explosion"></i>';
showMessage(`Enemy hit your ${hitShip.name}!`);
// Check if ship is sunk
if (hitShip.hits === hitShip.size) {
showMessage(`Enemy sank your ${hitShip.name}!`);
}
// Check if all ships are sunk
if (gameState.playerShips.every(ship => ship.hits === ship.size)) {
endGame(false);
return;
}
} else {
// Miss
gameState.computerMisses.push({ row, col });
const cellElement = playerGameBoard.querySelector(`[data-row="${row}"][data-col="${col}"]`);
cellElement.classList.add('miss');
cellElement.innerHTML = '<i class="fas fa-water"></i>';
showMessage("Enemy missed!");
}
// Switch back to player's turn
gameState.currentPlayer = 'player';
updateGameStatus();
}
// Check if cell has already been attacked
function isAlreadyAttacked(row, col, hits, misses) {
return hits.some(coord => coord.row === row && coord.col === col) ||
misses.some(coord => coord.row === row && coord.col === col);
}
// Get ship at given coordinates
function getShipAt(row, col, ships) {
return ships.find(ship => {
return ship.cells.some(cell => cell.row === row && cell.col === col);
});
}
// End the game
function endGame(playerWon) {
gameState.phase = 'gameOver';
// Show game over modal
if (playerWon) {
gameResultIcon.innerHTML = '<i class="fas fa-trophy text-yellow-500"></i>';
gameResultText.textContent = 'You Won!';
gameResultDetails.textContent = 'Congratulations! You sank all enemy ships.';
} else {
gameResultIcon.innerHTML = '<i class="fas fa-sad-tear text-blue-500"></i>';
gameResultText.textContent = 'You Lost!';
gameResultDetails.textContent = 'All your ships have been sunk. Better luck next time!';
}
gameOverModal.classList.remove('hidden');
}
// Update game status display
function updateGameStatus() {
if (gameState.currentPlayer === 'player') {
gameStatus.textContent = 'Your Turn';
gameStatus.className = 'text-xl font-bold text-green-600 mb-4 text-center';
} else {
gameStatus.textContent = 'Enemy\'s Turn';
gameStatus.className = 'text-xl font-bold text-red-600 mb-4 text-center';
}
}
// Show message
function showMessage(text) {
gameMessage.querySelector('span').textContent = text;
gameMessage.classList.remove('hidden');
// Hide after 3 seconds
setTimeout(() => {
gameMessage.classList.add('hidden');
}, 3000);
}
// Play again
playAgainBtn.addEventListener('click', function() {
gameOverModal.classList.add('hidden');
initGame();
if (gameState.mode === 'single') {
phaseSelector.classList.add('hidden');
placementPhase.classList.remove('hidden');
gamePhase.classList.add('hidden');
} else {
// For two-player mode, you'd reset to the selection phase
phaseSelector.classList.remove('hidden');
placementPhase.classList.add('hidden');
gamePhase.classList.add('hidden');
}
});
// Game mode selection
singlePlayerBtn.addEventListener('click', function() {
gameState.mode = 'single';
phaseSelector.classList.add('hidden');
placementPhase.classList.remove('hidden');
initGame();
});
twoPlayerBtn.addEventListener('click', function() {
// In a complete implementation, this would set up two-player mode
showMessage("Two-player mode coming soon! Try single player for now.");
// For this demo, we'll just use single player
gameState.mode = 'single';
phaseSelector.classList.add('hidden');
placementPhase.classList.remove('hidden');
initGame();
});
// Initialize the game
initGame();
});
</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=Chicken117/kids-coding" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>