用HTML做了个海战
2025-10-13 21:30:54
发布于:浙江
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>海战游戏</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
<script>
tailwind.config = {
theme: {
extend: {
colors: {
primary: '#165DFF',
secondary: '#36BFFA',
ocean: '#0A2463',
hit: '#E63946',
miss: '#F1FAEE',
ship: '#457B9D',
sunk: '#1D3557'
},
fontFamily: {
game: ['Courier New', 'monospace']
}
}
}
}
</script>
<style type="text/tailwindcss">
@layer utilities {
.board-cell {
@apply w-8 h-8 md:w-10 md:h-10 border border-gray-300 flex items-center justify-center cursor-pointer transition-all duration-200;
}
.board-cell:hover:not(.revealed) {
@apply bg-primary/20;
}
.ship-cell {
@apply bg-ship;
}
.hit-cell {
@apply bg-hit text-white;
}
.miss-cell {
@apply bg-miss;
}
.sunk-cell {
@apply bg-sunk text-white;
}
.game-board {
@apply grid grid-cols-10 gap-0 border border-gray-400;
}
.status-message {
@apply text-xl font-bold mb-4 transition-all duration-500;
}
}
</style>
</head>
<body class="bg-gradient-to-br from-blue-50 to-blue-100 min-h-screen font-game text-gray-800">
<div class="container mx-auto px-4 py-8 max-w-5xl">
<header class="text-center mb-8">
<h1 class="text-4xl md:text-5xl font-bold text-primary mb-2">海战游戏</h1>
<p class="text-gray-600">击沉对方所有船只获胜!</p>
</header>
<div class="status-message text-center" id="status">
游戏准备中...
</div>
<div class="flex flex-col md:flex-row gap-8 justify-center items-center mb-8">
<!-- 玩家棋盘 -->
<div class="text-center">
<h2 class="text-2xl font-bold mb-4 text-primary">你的海域</h2>
<div id="playerBoard" class="game-board"></div>
<p class="mt-2 text-sm text-gray-600">放置你的船只</p>
</div>
<!-- 电脑棋盘 -->
<div class="text-center">
<h2 class="text-2xl font-bold mb-4 text-primary">敌方海域</h2>
<div id="computerBoard" class="game-board"></div>
<p class="mt-2 text-sm text-gray-600">点击格子攻击</p>
</div>
</div>
<div class="text-center mb-8">
<button id="startBtn" class="bg-primary hover:bg-primary/80 text-white font-bold py-2 px-6 rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-primary/50">
开始游戏
</button>
<button id="resetBtn" class="bg-gray-600 hover:bg-gray-700 text-white font-bold py-2 px-6 rounded-lg transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-gray-500 ml-4 hidden">
重新开始
</button>
</div>
<div class="bg-white/80 rounded-lg p-4 shadow-md">
<h3 class="text-xl font-bold mb-2 text-primary">游戏说明</h3>
<ul class="list-disc pl-5 space-y-1 text-gray-700">
<li>游戏开始后,你和电脑各有一片海域和舰队</li>
<li>轮流攻击对方海域,率先击沉对方所有船只获胜</li>
<li>点击敌方海域格子进行攻击</li>
<li><span class="inline-block w-4 h-4 bg-ship align-middle mr-1"></span> 你的船只</li>
<li><span class="inline-block w-4 h-4 bg-hit align-middle mr-1 text-white text-xs flex items-center justify-center">×</span> 击中</li>
<li><span class="inline-block w-4 h-4 bg-miss align-middle mr-1 text-gray-500 text-xs flex items-center justify-center">○</span> 未击中</li>
<li><span class="inline-block w-4 h-4 bg-sunk align-middle mr-1 text-white text-xs flex items-center justify-center">※</span> 已击沉</li>
</ul>
</div>
</div>
<script>
// 游戏常量
const BOARD_SIZE = 10;
const SHIP_TYPES = [
{ length: 4, count: 1 }, // 战列舰
{ length: 3, count: 2 }, // 巡洋舰
{ length: 2, count: 3 }, // 驱逐舰
{ length: 1, count: 4 } // 潜艇
];
// 游戏状态
let gameState = {
playerBoard: Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill(0)),
computerBoard: Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill(0)),
playerGuesses: Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill(0)),
computerGuesses: Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill(0)),
playerShips: [],
computerShips: [],
playerShipsRemaining: 0,
computerShipsRemaining: 0,
currentPlayer: 'player', // 'player' 或 'computer'
gameStarted: false,
gameOver: false
};
// DOM 元素
const playerBoardEl = document.getElementById('playerBoard');
const computerBoardEl = document.getElementById('computerBoard');
const statusEl = document.getElementById('status');
const startBtn = document.getElementById('startBtn');
const resetBtn = document.getElementById('resetBtn');
// 初始化棋盘
function initializeBoards() {
// 创建玩家棋盘
playerBoardEl.innerHTML = '';
for (let x = 0; x < BOARD_SIZE; x++) {
for (let y = 0; y < BOARD_SIZE; y++) {
const cell = document.createElement('div');
cell.className = 'board-cell';
cell.dataset.x = x;
cell.dataset.y = y;
playerBoardEl.appendChild(cell);
}
}
// 创建电脑棋盘
computerBoardEl.innerHTML = '';
for (let x = 0; x < BOARD_SIZE; x++) {
for (let y = 0; y < BOARD_SIZE; y++) {
const cell = document.createElement('div');
cell.className = 'board-cell';
cell.dataset.x = x;
cell.dataset.y = y;
cell.addEventListener('click', () => handlePlayerAttack(x, y));
computerBoardEl.appendChild(cell);
}
}
}
// 随机放置船只
function placeShipsRandomly(board, isPlayer = false) {
let ships = [];
let shipsRemaining = 0;
// 计算总船只数量
SHIP_TYPES.forEach(ship => {
shipsRemaining += ship.count;
});
// 放置每种类型的船只
SHIP_TYPES.forEach(shipType => {
for (let i = 0; i < shipType.count; i++) {
let placed = false;
// 尝试放置船只,最多尝试100次避免无限循环
for (let attempt = 0; attempt < 100 && !placed; attempt++) {
// 随机方向 (0: 水平, 1: 垂直)
const direction = Math.floor(Math.random() * 2);
// 随机起始位置
const x = Math.floor(Math.random() * BOARD_SIZE);
const y = Math.floor(Math.random() * BOARD_SIZE);
// 检查是否可以放置船只
let canPlace = true;
for (let j = 0; j < shipType.length; j++) {
const newX = direction === 0 ? x : x + j;
const newY = direction === 0 ? y + j : y;
// 检查是否超出边界
if (newX >= BOARD_SIZE || newY >= BOARD_SIZE) {
canPlace = false;
break;
}
// 检查是否与其他船只有重叠
if (board[newX][newY] !== 0) {
canPlace = false;
break;
}
}
// 如果可以放置,就放置船只
if (canPlace) {
const shipPositions = [];
for (let j = 0; j < shipType.length; j++) {
const newX = direction === 0 ? x : x + j;
const newY = direction === 0 ? y + j : y;
board[newX][newY] = 1; // 1 表示有船
shipPositions.push({x: newX, y: newY});
}
ships.push({
length: shipType.length,
positions: shipPositions,
hitPositions: [],
sunk: false
});
placed = true;
}
}
}
});
return { board, ships, shipsRemaining };
}
// 渲染棋盘
function renderBoards() {
// 渲染玩家棋盘
const playerCells = playerBoardEl.querySelectorAll('.board-cell');
playerCells.forEach(cell => {
const x = parseInt(cell.dataset.x);
const y = parseInt(cell.dataset.y);
cell.className = 'board-cell';
// 显示玩家的船只
if (gameState.playerBoard[x][y] === 1) {
cell.classList.add('ship-cell');
cell.innerHTML = '';
}
// 显示电脑的攻击结果
if (gameState.computerGuesses[x][y] === 1) {
// 击中
cell.classList.add('hit-cell');
cell.innerHTML = '×';
} else if (gameState.computerGuesses[x][y] === 2) {
// 未击中
cell.classList.add('miss-cell');
cell.innerHTML = '○';
}
});
// 渲染电脑棋盘
const computerCells = computerBoardEl.querySelectorAll('.board-cell');
computerCells.forEach(cell => {
const x = parseInt(cell.dataset.x);
const y = parseInt(cell.dataset.y);
cell.className = 'board-cell';
// 显示玩家的攻击结果
if (gameState.playerGuesses[x][y] === 1) {
// 击中
cell.classList.add('hit-cell');
cell.innerHTML = '×';
} else if (gameState.playerGuesses[x][y] === 2) {
// 未击中
cell.classList.add('miss-cell');
cell.innerHTML = '○';
}
// 如果游戏结束,显示所有船只
if (gameState.gameOver && gameState.computerBoard[x][y] === 1) {
if (gameState.playerGuesses[x][y] !== 1) {
cell.classList.add('ship-cell');
}
}
});
// 标记已击沉的船只
markSunkShips();
}
// 标记已击沉的船只
function markSunkShips() {
// 标记玩家已击沉的电脑船只
gameState.computerShips.forEach(ship => {
if (ship.sunk) {
ship.positions.forEach(pos => {
const cell = computerBoardEl.querySelector(`[data-x="${pos.x}"][data-y="${pos.y}"]`);
if (cell) {
cell.classList.remove('hit-cell');
cell.classList.add('sunk-cell');
cell.innerHTML = '※';
}
});
}
});
// 标记电脑已击沉的玩家船只
gameState.playerShips.forEach(ship => {
if (ship.sunk) {
ship.positions.forEach(pos => {
const cell = playerBoardEl.querySelector(`[data-x="${pos.x}"][data-y="${pos.y}"]`);
if (cell) {
cell.classList.remove('hit-cell', 'ship-cell');
cell.classList.add('sunk-cell');
cell.innerHTML = '※';
}
});
}
});
}
// 检查是否击中船只
function checkHit(board, x, y) {
return board[x][y] === 1;
}
// 检查船只是否被击沉
function checkShipSunk(ships, x, y) {
for (let i = 0; i < ships.length; i++) {
const ship = ships[i];
// 找到被击中的船
const hitPos = ship.positions.find(pos => pos.x === x && pos.y === y);
if (hitPos && !ship.sunk) {
// 记录击中位置
if (!ship.hitPositions.some(pos => pos.x === x && pos.y === y)) {
ship.hitPositions.push({x, y});
}
// 检查是否所有位置都被击中
if (ship.hitPositions.length === ship.length) {
ship.sunk = true;
return true; // 船被击沉
}
}
}
return false; // 船未被击沉
}
// 检查游戏是否结束
function checkGameOver() {
// 检查玩家是否获胜
const allComputerShipsSunk = gameState.computerShips.every(ship => ship.sunk);
if (allComputerShipsSunk) {
statusEl.textContent = '恭喜你获胜了!';
statusEl.className = 'status-message text-center text-green-600';
gameState.gameOver = true;
return true;
}
// 检查电脑是否获胜
const allPlayerShipsSunk = gameState.playerShips.every(ship => ship.sunk);
if (allPlayerShipsSunk) {
statusEl.textContent = '很遗憾,电脑获胜了!';
statusEl.className = 'status-message text-center text-red-600';
gameState.gameOver = true;
return true;
}
return false;
}
// 玩家攻击
function handlePlayerAttack(x, y) {
// 检查游戏状态
if (!gameState.gameStarted || gameState.gameOver || gameState.currentPlayer !== 'player') {
return;
}
// 检查是否已经攻击过这个位置
if (gameState.playerGuesses[x][y] !== 0) {
return;
}
// 记录攻击结果
if (checkHit(gameState.computerBoard, x, y)) {
gameState.playerGuesses[x][y] = 1; // 击中
statusEl.textContent = '击中目标!';
statusEl.className = 'status-message text-center text-hit';
// 检查是否击沉船只
const shipSunk = checkShipSunk(gameState.computerShips, x, y);
if (shipSunk) {
statusEl.textContent = '击沉一艘船!';
}
} else {
gameState.playerGuesses[x][y] = 2; // 未击中
statusEl.textContent = '未击中目标。';
statusEl.className = 'status-message text-center text-gray-600';
gameState.currentPlayer = 'computer';
}
// 重新渲染棋盘
renderBoards();
// 检查游戏是否结束
if (checkGameOver()) {
return;
}
// 如果玩家未击中,轮到电脑攻击
if (gameState.currentPlayer === 'computer') {
setTimeout(computerAttack, 1000);
}
}
// 电脑攻击
function computerAttack() {
// 检查游戏状态
if (!gameState.gameStarted || gameState.gameOver || gameState.currentPlayer !== 'computer') {
return;
}
let x, y;
// 随机选择一个未攻击过的位置
do {
x = Math.floor(Math.random() * BOARD_SIZE);
y = Math.floor(Math.random() * BOARD_SIZE);
} while (gameState.computerGuesses[x][y] !== 0);
// 记录攻击结果
if (checkHit(gameState.playerBoard, x, y)) {
gameState.computerGuesses[x][y] = 1; // 击中
statusEl.textContent = '电脑击中了你的船!';
statusEl.className = 'status-message text-center text-hit';
// 检查是否击沉船只
const shipSunk = checkShipSunk(gameState.playerShips, x, y);
if (shipSunk) {
statusEl.textContent = '电脑击沉了你的一艘船!';
}
} else {
gameState.computerGuesses[x][y] = 2; // 未击中
statusEl.textContent = '电脑未击中你的船。';
statusEl.className = 'status-message text-center text-gray-600';
gameState.currentPlayer = 'player';
}
// 重新渲染棋盘
renderBoards();
// 检查游戏是否结束
if (checkGameOver()) {
return;
}
// 如果电脑未击中,轮到玩家攻击
if (gameState.currentPlayer === 'player') {
statusEl.textContent = '轮到你攻击了。';
statusEl.className = 'status-message text-center text-primary';
} else {
// 电脑连续攻击
setTimeout(computerAttack, 1000);
}
}
// 开始游戏
function startGame() {
// 重置游戏状态
gameState = {
playerBoard: Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill(0)),
computerBoard: Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill(0)),
playerGuesses: Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill(0)),
computerGuesses: Array(BOARD_SIZE).fill().map(() => Array(BOARD_SIZE).fill(0)),
playerShips: [],
computerShips: [],
playerShipsRemaining: 0,
computerShipsRemaining: 0,
currentPlayer: 'player',
gameStarted: true,
gameOver: false
};
// 随机放置玩家和电脑的船只
const playerSetup = placeShipsRandomly(gameState.playerBoard, true);
gameState.playerBoard = playerSetup.board;
gameState.playerShips = playerSetup.ships;
gameState.playerShipsRemaining = playerSetup.shipsRemaining;
const computerSetup = placeShipsRandomly(gameState.computerBoard);
gameState.computerBoard = computerSetup.board;
gameState.computerShips = computerSetup.ships;
gameState.computerShipsRemaining = computerSetup.shipsRemaining;
// 更新UI状态
startBtn.classList.add('hidden');
resetBtn.classList.remove('hidden');
statusEl.textContent = '游戏开始!轮到你攻击了。';
statusEl.className = 'status-message text-center text-primary';
// 渲染棋盘
renderBoards();
}
// 重置游戏
function resetGame() {
initializeBoards();
statusEl.textContent = '游戏已重置,点击开始游戏。';
statusEl.className = 'status-message text-center';
startBtn.classList.remove('hidden');
resetBtn.classList.add('hidden');
gameState.gameStarted = false;
gameState.gameOver = false;
}
// 事件监听
startBtn.addEventListener('click', startGame);
resetBtn.addEventListener('click', resetGame);
// 初始化游戏
initializeBoards();
</script>
</body>
</html>
全部评论 5
一眼AI(?
1周前 来自 重庆
2答对了 but一半是手搓
1周前 来自 浙江
1真
1周前 来自 浙江
1
厉害👍
1周前 来自 浙江
2666,太牛逼了
1周前 来自 浙江
11周前 来自 浙江
0
1周前 来自 浙江
11周前 来自 浙江
0
有帮助,赞一个