aI战争游戏
2026-10-24 18:20:56
发布于:浙江
<!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 rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
body { margin: 0; overflow: hidden; background-color: #0f172a; font-family: 'Segoe UI', sans-serif; user-select: none; }
canvas { display: block; image-rendering: pixelated; cursor: crosshair; }
.glass-panel {
background: rgba(15, 23, 42, 0.9);
backdrop-filter: blur(12px);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
}
@keyframes pulse-red {
0% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0.4); }
70% { box-shadow: 0 0 0 10px rgba(239, 68, 68, 0); }
100% { box-shadow: 0 0 0 0 rgba(239, 68, 68, 0); }
}
.unstable-tile {
animation: pulse-red 2s infinite;
}
</style>
</head>
<body class="text-gray-200 h-screen flex flex-col">
<header class="glass-panel z-20 px-6 py-3 flex justify-between items-center shrink-0 border-b border-gray-700">
<div class="flex items-center gap-4">
<h1 class="text-xl font-bold text-yellow-500 tracking-wider"><i class="fas fa-chess-rook mr-2"></i>帝国战略:分裂版</h1>
<div class="h-6 w-px bg-gray-600"></div>
<div class="text-sm font-mono">
<span class="text-gray-400">年份:</span>
<span id="yearDisplay" class="text-white ml-1">0</span>
</div>
<div class="text-sm font-mono">
<span class="text-gray-400">活跃势力:</span>
<span id="tribeCountDisplay" class="text-blue-400 ml-1">0</span>
</div>
</div>
<div class="flex gap-3">
<button id="btnStart" class="bg-emerald-600 hover:bg-emerald-700 text-white px-4 py-1.5 rounded-md transition text-sm font-medium flex items-center gap-2 shadow-lg">
<i class="fas fa-play"></i> 演化开始
</button>
<button id="btnPause" class="bg-amber-600 hover:bg-amber-700 text-white px-4 py-1.5 rounded-md transition text-sm font-medium flex items-center gap-2 hidden shadow-lg">
<i class="fas fa-pause"></i> 暂停
</button>
<button id="btnReset" class="bg-rose-600 hover:bg-rose-700 text-white px-4 py-1.5 rounded-md transition text-sm font-medium flex items-center gap-2 shadow-lg">
<i class="fas fa-trash-can"></i> 重置
</button>
<button id="btnSpeed" class="bg-slate-700 hover:bg-slate-600 text-white px-3 py-1.5 rounded-md transition text-sm font-medium border border-slate-600">
<i class="fas fa-gauge-high"></i> x1
</button>
</div>
</header>
<main class="flex-1 relative flex overflow-hidden">
<div class="flex-1 relative bg-slate-900 flex items-center justify-center overflow-auto" id="canvasContainer">
<canvas id="gameCanvas"></canvas>
</div>
<aside class="w-96 glass-panel border-l border-gray-700 flex flex-col shrink-0 z-10">
<div class="p-5 border-b border-gray-700 bg-slate-800/30">
<h2 class="font-bold text-gray-200 mb-3 flex items-center gap-2">
<i class="fas fa-map-pin text-yellow-500"></i> 地块管理
</h2>
<div id="tileDetails" class="space-y-3">
<div class="text-center text-gray-500 py-4 italic">点击地图上的格子查看详情</div>
</div>
</div>
<div class="flex-1 overflow-y-auto p-4">
<h3 class="text-xs font-bold text-gray-500 uppercase tracking-wider mb-3">势力概览</h3>
<div id="tribeList" class="space-y-2"></div>
</div>
<div class="p-4 bg-slate-900/50 border-t border-gray-700 text-xs text-gray-400">
<p><i class="fas fa-circle-info mr-1"></i> <strong>新机制说明:</strong></p>
<ul class="list-disc list-inside mt-1 space-y-1">
<li><strong class="text-red-400">分裂风险:</strong> 驻军低于5的格子有概率独立建国</li>
<li><strong class="text-orange-400">动荡惩罚:</strong> 分裂会导致全境人口增长减缓</li>
<li>点击己方格子可手动增兵以维稳</li>
</ul>
</div>
</aside>
</main>
<div id="victoryModal" class="hidden absolute inset-0 bg-black/90 flex items-center justify-center z-50 backdrop-blur-md">
<div class="glass-panel p-8 rounded-2xl text-center max-w-md border-2 border-yellow-500/30 shadow-2xl">
<i class="fas fa-crown text-6xl text-yellow-400 mb-4 animate-bounce"></i>
<h2 class="text-3xl font-bold text-white mb-2">天下一统!</h2>
<p class="text-gray-300 mb-6"><span id="winnerName" class="text-yellow-400 font-bold text-xl"></span> 建立了永恒的帝国。</p>
<button onclick="location.reload()" class="bg-gradient-to-r from-yellow-600 to-orange-600 text-white px-6 py-3 rounded-lg font-bold transition w-full">
开启新纪元
</button>
</div>
</div>
<script>
const CONFIG = {
gridSize: 24,
mapWidth: 50,
mapHeight: 35,
initialTribes: 6,
colors: ['#EF4444', '#3B82F6', '#10B981', '#F59E0B', '#8B5CF6', '#EC4899', '#06B6D4', '#F97316'],
names: ['秦', '楚', '齐', '燕', '赵', '魏', '韩', '周', '吴', '越', '蜀', '巴'],
baseGrowthRate: 0.08,
instabilityThreshold: 5, // 分裂阈值
splitChance: 0.002, // 每帧分裂概率
rebellionPenalty: 0.5 // 叛乱后的全局增长惩罚系数
};
const state = {
isRunning: false,
year: 0,
speedMultiplier: 1,
tribes: [],
grid: [],
selectedTile: null,
lastTick: 0,
animationId: null,
globalStability: 1.0 // 全局稳定度,1.0为正常,越低增长越慢
};
const canvas = document.getElementById('gameCanvas');
const ctx = canvas.getContext('2d');
function initCanvas() {
canvas.width = CONFIG.mapWidth * CONFIG.gridSize;
canvas.height = CONFIG.mapHeight * CONFIG.gridSize;
canvas.style.width = `${canvas.width}px`;
canvas.style.height = `${canvas.height}px`;
}
class Tile {
constructor(x, y) {
this.x = x;
this.y = y;
this.owner = null;
this.soldiers = 0;
this.maxCapacity = 50;
this.isRebelling = false; // 标记是否刚发生叛乱
}
}
class Tribe {
constructor(id, name, color) {
this.id = id;
this.name = name;
this.color = color;
this.tiles = [];
this.totalPopulation = 0;
this.isDead = false;
this.enemies = new Set();
}
addTile(tile) {
if (tile.owner && tile.owner !== this) {
tile.owner.removeTile(tile);
}
tile.owner = this;
tile.isRebelling = false;
this.tiles.push(tile);
this.recalculatePop();
}
removeTile(tile) {
const idx = this.tiles.indexOf(tile);
if (idx > -1) this.tiles.splice(idx, 1);
tile.owner = null;
tile.soldiers = 0;
this.recalculatePop();
if (this.tiles.length === 0) this.die();
}
recalculatePop() {
this.totalPopulation = this.tiles.reduce((sum, t) => sum + t.soldiers, 0);
}
die() {
this.isDead = true;
this.tiles = [];
this.totalPopulation = 0;
updateUI();
}
growPopulation() {
if (this.isDead) return;
// 如果国家内存在叛乱格子,整体增长速度大幅下降
const hasRebellion = this.tiles.some(t => t.isRebelling);
const currentGrowthRate = CONFIG.baseGrowthRate * (hasRebellion ? CONFIG.rebellionPenalty : 1.0) * state.globalStability;
this.tiles.forEach(tile => {
if (tile.soldiers < tile.maxCapacity) {
tile.soldiers += currentGrowthRate * state.speedMultiplier;
if (tile.soldiers > tile.maxCapacity) tile.soldiers = tile.maxCapacity;
}
});
this.recalculatePop();
}
militaryAction() {
if (this.isDead) return;
const activeTiles = this.tiles.filter(t => t.soldiers > 10);
activeTiles.sort(() => Math.random() - 0.5);
const processLimit = Math.min(activeTiles.length, 50 * state.speedMultiplier);
for (let i = 0; i < processLimit; i++) {
const tile = activeTiles[i];
const neighbors = getNeighbors(tile);
let target = null;
let minDefense = Infinity;
for (let n of neighbors) {
if (n.owner === this) continue;
if (!n.owner) {
target = n;
break;
}
if (n.soldiers < minDefense) {
minDefense = n.soldiers;
target = n;
}
}
if (target) {
this.executeAttack(tile, target);
}
}
}
executeAttack(source, target) {
const attackPower = source.soldiers * 0.1;
if (target.owner) {
target.soldiers -= attackPower;
source.soldiers -= attackPower * 0.5;
createParticle(target.x, target.y, '#FFF');
if (target.soldiers <= 0) {
target.soldiers = Math.abs(target.soldiers);
this.addTile(target);
createParticle(target.x, target.y, this.color);
}
} else {
source.soldiers -= attackPower;
target.soldiers = attackPower;
this.addTile(target);
}
}
}
function getNeighbors(tile) {
const neighbors = [];
const dirs = [[0, 1], [0, -1], [1, 0], [-1, 0]];
for (let [dx, dy] of dirs) {
const nx = tile.x + dx;
const ny = tile.y + dy;
if (nx >= 0 && nx < CONFIG.mapWidth && ny >= 0 && ny < CONFIG.mapHeight) {
neighbors.push(state.grid[ny][nx]);
}
}
return neighbors;
}
let particles = [];
function createParticle(gx, gy, color) {
for(let k=0; k<3; k++) {
particles.push({
x: gx * CONFIG.gridSize + CONFIG.gridSize/2,
y: gy * CONFIG.gridSize + CONFIG.gridSize/2,
vx: (Math.random() - 0.5) * 3,
vy: (Math.random() - 0.5) * 3,
life: 1.0,
color: color
});
}
}
function initGame() {
state.tribes = [];
state.grid = [];
state.year = 0;
state.isRunning = false;
state.selectedTile = null;
state.globalStability = 1.0;
particles = [];
for (let y = 0; y < CONFIG.mapHeight; y++) {
const row = [];
for (let x = 0; x < CONFIG.mapWidth; x++) {
row.push(new Tile(x, y));
}
state.grid.push(row);
}
for (let i = 0; i < CONFIG.initialTribes; i++) {
const color = CONFIG.colors[i % CONFIG.colors.length];
const name = CONFIG.names[i % CONFIG.names.length];
const tribe = new Tribe(i, name, color);
let placed = false;
while (!placed) {
const x = Math.floor(Math.random() * CONFIG.mapWidth);
const y = Math.floor(Math.random() * CONFIG.mapHeight);
if (!state.grid[y][x].owner) {
const capital = state.grid[y][x];
capital.soldiers = 50;
tribe.addTile(capital);
placed = true;
}
}
state.tribes.push(tribe);
}
initCanvas();
draw();
updateUI();
renderTileDetails();
}
function checkRebellions() {
// 遍历所有格子,检查低兵力格子是否分裂
for (let y = 0; y < CONFIG.mapHeight; y++) {
for (let x = 0; x < CONFIG.mapWidth; x++) {
const tile = state.grid[y][x];
if (tile.owner && !tile.isRebelling && tile.soldiers < CONFIG.instabilityThreshold) {
if (Math.random() < CONFIG.splitChance * state.speedMultiplier) {
triggerRebellion(tile);
}
}
}
}
}
function triggerRebellion(tile) {
const oldOwner = tile.owner;
// 创建新部落或加入现有小部落
let newTribe = state.tribes.find(t => t.isDead); // 尝试复用已灭亡的ID
if (!newTribe) {
// 如果没有空闲ID,且未达到上限,可以扩展数组(简化处理:这里假设ID够用或覆盖)
// 为了简单,我们创建一个全新的对象,即使ID重复也没关系,因为逻辑上它是新的
const newId = state.tribes.length;
const newName = "叛-" + CONFIG.names[Math.floor(Math.random() * CONFIG.names.length)];
const newColor = CONFIG.colors[Math.floor(Math.random() * CONFIG.colors.length)];
newTribe = new Tribe(newId, newName, newColor);
state.tribes.push(newTribe);
} else {
// 复活旧部落
newTribe.isDead = false;
newTribe.tiles = [];
newTribe.name = "复-" + newTribe.name;
}
tile.isRebelling = true;
newTribe.addTile(tile);
// 降低全局稳定性,导致所有国家增长变慢
state.globalStability = Math.max(0.2, state.globalStability - 0.05);
createParticle(x, y, '#FF0000');
console.log(`${tile.x},${tile.y} 发生了叛乱!`);
}
function gameLoop(timestamp) {
if (!state.isRunning) return;
const interval = 100 / state.speedMultiplier;
if (timestamp - state.lastTick > interval) {
state.year++;
// 1. 检查分裂
checkRebellions();
// 2. 人口增长
state.tribes.forEach(t => t.growPopulation());
// 3. 军事行动
state.tribes.forEach(t => t.militaryAction());
// 4. 检查胜利
const survivors = state.tribes.filter(t => !t.isDead);
if (survivors.length === 1 && state.tribes.length > 1) {
endGame(survivors[0]);
}
state.lastTick = timestamp;
if (state.year % 5 === 0) updateUI();
if (state.selectedTile) renderTileDetails();
}
draw();
state.animationId = requestAnimationFrame(gameLoop);
}
function draw() {
ctx.fillStyle = '#0f172a';
ctx.fillRect(0, 0, canvas.width, canvas.height);
for (let y = 0; y < CONFIG.mapHeight; y++) {
for (let x = 0; x < CONFIG.mapWidth; x++) {
const tile = state.grid[y][x];
const px = x * CONFIG.gridSize;
const py = y * CONFIG.gridSize;
if (tile.owner) {
ctx.fillStyle = tile.owner.color;
ctx.fillRect(px, py, CONFIG.gridSize, CONFIG.gridSize);
// 叛乱格子特殊显示
if (tile.isRebelling) {
ctx.fillStyle = 'rgba(255, 0, 0, 0.3)';
ctx.fillRect(px, py, CONFIG.gridSize, CONFIG.gridSize);
}
const intensity = Math.min(1, tile.soldiers / 50);
ctx.fillStyle = `rgba(0,0,0,${0.8 - intensity * 0.8})`;
ctx.fillRect(px + 2, py + 2, CONFIG.gridSize - 4, CONFIG.gridSize - 4);
if (CONFIG.gridSize >= 20 && tile.soldiers > 1) {
ctx.fillStyle = tile.isRebelling ? '#FFAAAA' : '#FFF';
ctx.font = '10px Arial';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.fillText(Math.floor(tile.soldiers), px + CONFIG.gridSize/2, py + CONFIG.gridSize/2);
}
} else {
ctx.strokeStyle = '#1e293b';
ctx.strokeRect(px, py, CONFIG.gridSize, CONFIG.gridSize);
}
}
}
if (state.selectedTile) {
const {x, y} = state.selectedTile;
ctx.strokeStyle = '#eab308';
ctx.lineWidth = 3;
ctx.strokeRect(x * CONFIG.gridSize, y * CONFIG.gridSize, CONFIG.gridSize, CONFIG.gridSize);
}
for (let i = particles.length - 1; i >= 0; i--) {
const p = particles[i];
p.x += p.vx;
p.y += p.vy;
p.life -= 0.05;
if (p.life <= 0) {
particles.splice(i, 1);
} else {
ctx.globalAlpha = p.life;
ctx.fillStyle = p.color;
ctx.beginPath();
ctx.arc(p.x, p.y, 2, 0, Math.PI * 2);
ctx.fill();
ctx.globalAlpha = 1.0;
}
}
}
function updateUI() {
document.getElementById('yearDisplay').innerText = state.year;
const survivors = state.tribes.filter(t => !t.isDead);
document.getElementById('tribeCountDisplay').innerText = survivors.length;
const list = document.getElementById('tribeList');
list.innerHTML = '';
survivors.sort((a,b) => b.totalPopulation - a.totalPopulation);
survivors.forEach(tribe => {
const div = document.createElement('div');
div.className = 'flex items-center justify-between p-2 rounded bg-slate-800/50 border border-slate-700 hover:border-slate-500 transition';
div.innerHTML = `
<div class="flex items-center gap-2">
<div class="w-3 h-3 rounded-full" style="background-color: ${tribe.color}"></div>
<span class="text-sm font-bold text-gray-200">${tribe.name}</span>
</div>
<div class="text-xs text-gray-400 font-mono">
Pop: ${Math.floor(tribe.totalPopulation)} | Tiles: ${tribe.tiles.length}
</div>
`;
list.appendChild(div);
});
}
function renderTileDetails() {
const container = document.getElementById('tileDetails');
if (!state.selectedTile) {
container.innerHTML = '<div class="text-center text-gray-500 py-4 italic">点击地图上的格子查看详情</div>';
return;
}
const {x, y} = state.selectedTile;
const tile = state.grid[y][x];
if (!tile.owner) {
container.innerHTML = `
<div class="text-center py-4">
<div class="text-gray-400 text-sm">荒地 (${x}, ${y})</div>
<div class="text-xs text-gray-600 mt-1">无主之地,等待征服</div>
</div>
`;
return;
}
const tribe = tile.owner;
container.innerHTML = `
<div class="flex items-center gap-3 mb-4 pb-4 border-b border-gray-700">
<div class="w-8 h-8 rounded shadow" style="background-color: ${tribe.color}"></div>
<div>
<div class="font-bold text-white">${tribe.name} 领土</div>
<div class="text-xs text-gray-400">坐标: (${x}, ${y}) ${tile.isRebelling ? '<span class="text-red-500 font-bold">[叛乱中]</span>' : ''}</div>
</div>
</div>
<div class="space-y-4">
<div>
<div class="flex justify-between text-sm mb-1">
<span class="text-gray-400">驻军兵力</span>
<span class="text-white font-mono font-bold">${Math.floor(tile.soldiers)}</span>
</div>
<input type="range" min="0" max="${Math.floor(tile.soldiers) + 20}" value="${Math.floor(tile.soldiers)}"
class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer accent-yellow-500"
oninput="updateSoldierCount(${x}, ${y}, this.value)">
<div class="text-[10px] text-gray-500 mt-1 text-right">
${tile.soldiers < CONFIG.instabilityThreshold ? '<span class="text-red-400">警告:兵力过低,极易分裂!</span>' : '拖动分配兵力'}
</div>
</div>
<div class="grid grid-cols-2 gap-2 text-xs">
<div class="bg-slate-900 p-2 rounded">
<div class="text-gray-500">防御力</div>
<div class="text-green-400 font-mono">${Math.floor(tile.soldiers * 1.2)}</div>
</div>
<div class="bg-slate-900 p-2 rounded">
<div class="text-gray-500">状态</div>
<div class="${tile.isRebelling ? 'text-red-400' : 'text-blue-400'} font-mono">
${tile.isRebelling ? '动荡' : '稳定'}
</div>
</div>
</div>
</div>
`;
}
window.updateSoldierCount = function(x, y, val) {
if (!state.selectedTile || state.selectedTile.x !== x || state.selectedTile.y !== y) return;
const tile = state.grid[y][x];
tile.soldiers = parseFloat(val);
if (tile.owner) tile.owner.recalculatePop();
renderTileDetails();
};
canvas.addEventListener('mousedown', (e) => {
const rect = canvas.getBoundingClientRect();
const x = Math.floor((e.clientX - rect.left) / CONFIG.gridSize);
const y = Math.floor((e.clientY - rect.top) / CONFIG.gridSize);
if (x >= 0 && x < CONFIG.mapWidth && y >= 0 && y < CONFIG.mapHeight) {
state.selectedTile = {x, y};
renderTileDetails();
draw();
}
});
document.getElementById('btnStart').addEventListener('click', () => {
if (!state.isRunning) {
state.isRunning = true;
state.lastTick = performance.now();
document.getElementById('btnStart').classList.add('hidden');
document.getElementById('btnPause').classList.remove('hidden');
gameLoop(performance.now());
}
});
document.getElementById('btnPause').addEventListener('click', () => {
state.isRunning = false;
cancelAnimationFrame(state.animationId);
document.getElementById('btnStart').classList.remove('hidden');
document.getElementById('btnPause').classList.add('hidden');
});
document.getElementById('btnReset').addEventListener('click', () => {
state.isRunning = false;
cancelAnimationFrame(state.animationId);
document.getElementById('victoryModal').classList.add('hidden');
document.getElementById('btnStart').classList.remove('hidden');
document.getElementById('btnPause').classList.add('hidden');
initGame();
});
document.getElementById('btnSpeed').addEventListener('click', (e) => {
const speeds = [1, 5, 10, 20];
const currentIdx = speeds.indexOf(state.speedMultiplier);
const nextIdx = (currentIdx + 1) % speeds.length;
state.speedMultiplier = speeds[nextIdx];
e.currentTarget.innerHTML = `<i class="fas fa-gauge-high"></i> x${state.speedMultiplier}`;
});
function endGame(winner) {
state.isRunning = false;
cancelAnimationFrame(state.animationId);
document.getElementById('victoryModal').classList.remove('hidden');
document.getElementById('winnerName').innerText = winner.name;
document.getElementById('winnerName').style.color = winner.color;
}
initGame();
</script>
</body>
</html>
全部评论 2
部分电脑乱码,由于劳资是电脑专家,我已修复,修复只需清除第4行即可
1周前 来自 浙江
1感谢!!!
1周前 来自 浙江
0
怎么玩
求1周前 来自 北京
0你看一下评论区大神的评论
1周前 来自 浙江
0

























有帮助,赞一个