五子棋|双人&三难度AI
by SuperPhoenix65226 lines6.9 KB
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>五子棋|双人&三难度AI</title>
<style>
* {margin:0;padding:0;box-sizing:border-box;font-family:system-ui}
body {background:#f0f0f0;display:flex;flex-direction:column;align-items:center;padding:16px}
.box-top {margin-bottom:12px;display:flex;gap:10px;flex-wrap:center}
button {padding:8px 14px;font-size:15px;border:none;border-radius:6px;cursor:pointer;background:#3670d8;color:white}
button:hover {opacity:0.85}
.info {font-size:18px;margin:10px 0;font-weight:bold}
#board {background:#deb887;border:3px solid #8b4513}
.row {display:flex}
.cell {width:32px;height:32px;border:1px solid #b48653;position:relative;cursor:pointer}
.cell:hover {background:rgba(0,0,0,0.08)}
.stone {width:28px;height:28px;border-radius:50%;position:absolute;top:2px;left:2px}
.black {background:#111;box-shadow:2px 2px 4px #666}
.white {background:#fff;box-shadow:2px 2px 4px #aaa}
</style>
</head>
<body>
<div class="box-top">
<button onclick="setMode('two')">双人对战</button>
<button onclick="setMode('easy')">AI-简单</button>
<button onclick="setMode('mid')">AI-中等</button>
<button onclick="setMode('hard')">AI-恶魔</button>
<button onclick="restart()">重新开局</button>
<button onclick="undo()">悔棋一步</button>
</div>
<div class="info" id="msg">当前模式:双人对战|黑方先行</div>
<div id="board"></div>
<script>
const size = 15;
let map = Array(size).fill().map(()=>Array(size).fill(0));
let mode = "two"; // two/easy/mid/hard
let turn = 1; //1黑 2白
let over = false;
let history = []; //落子记录 用于悔棋
const $ = s=>document.querySelector(s);
// 初始化棋盘
function initBoard(){
$("#board").innerHTML = "";
for(let y=0;y<size;y++){
let r = document.createElement("div");
r.className = "row";
for(let x=0;x<size;x++){
let c = document.createElement("div");
c.className = "cell";
c.dataset.x = x;c.dataset.y = y;
c.onclick = ()=>place(x,y);
r.appendChild(c);
}
$("#board").appendChild(r);
}
}
// 设置游戏模式
function setMode(m){
mode = m;
restart();
let txt = {two:"双人对战",easy:"AI-简单",mid:"AI-中等",hard:"AI-恶魔"}[m];
$("#msg").innerText = `当前模式:${txt}|黑方先行`;
}
// 落子逻辑
function place(x,y){
if(over || map[y][x]!==0) return;
map[y][x] = turn;
history.push({x,y,val:turn});
renderStone(x,y,turn);
// 判断胜利
if(checkWin(x,y,turn)){
over = true;
$("#msg").innerText = turn===1?"黑棋五连获胜!":"白棋五连获胜!";
return;
}
// 切换回合
turn = turn===1?2:1;
if(mode!=="two" && turn===2){
setTimeout(aiPlay,300);
}
updateTip();
}
// 绘制棋子
function renderStone(x,y,val){
let cell = document.querySelector(`.cell[data-x="${x}"][data-y="${y}"]`);
let s = document.createElement("div");
s.className = "stone " + (val===1?"black":"white");
cell.appendChild(s);
}
// 更新提示文字
function updateTip(){
let mTxt = {two:"双人对战",easy:"AI-简单",mid:"AI-中等",hard:"AI-恶魔"}[mode];
let player = turn===1?"黑方":"白方";
$("#msg").innerText = `当前模式:${mTxt}|${player}落子`;
}
// 检测五连子
function checkWin(x,y,val){
const dirs = [[1,0],[0,1],[1,1],[1,-1]];
for(let [dx,dy] of dirs){
let cnt = 1;
// 正向
let nx=x+dx,ny=y+dy;
while(nx>=0&&nx<size&&ny>=0&&ny<size&&map[ny][nx]===val){
cnt++;nx+=dx;ny+=dy;
}
// 反向
nx=x-dx;ny=y-dy;
while(nx>=0&&nx<size&&ny>=0&&ny<size&&map[ny][nx]===val){
cnt++;nx-=dx;ny-=dy;
}
if(cnt>=5) return true;
}
return false;
}
// 悔棋
function undo(){
if(history.length===0||over) return;
let last = history.pop();
map[last.y][last.x] = 0;
let cell = document.querySelector(`.cell[data-x="${last.x}"][data-y="${last.y}"]`);
cell.innerHTML = "";
turn = last.val;
over = false;
updateTip();
}
// 重置游戏
function restart(){
map = Array(size).fill().map(()=>Array(size).fill(0));
turn = 1;over = false;history = [];
initBoard();
updateTip();
}
// ========= AI核心逻辑 =========
// 简单AI:随机空位落子
function aiEasy(){
let empty = [];
for(let y=0;y<size;y++)for(let x=0;x<size;x++)if(map[y][x]===0)empty.push([x,y]);
return empty[Math.floor(Math.random()*empty.length)];
}
// 中等AI:会防守、简单进攻
function aiMid(){
// 优先堵人类四连、三连
for(let v of [1,2]){
for(let y=0;y<size;y++)for(let x=0;x<size;x++){
if(map[y][x]!==0)continue;
map[y][x]=v;
if(checkWin(x,y,v)){
map[y][x]=0;
return [x,y];
}
map[y][x]=0;
}
}
// 其次随机
return aiEasy();
}
// 恶魔AI:评分体系,进攻+防守拉满,最强
function aiHard(){
let bestScore = -Infinity;
let bestPos = [7,7]; //优先天元
const scoreTable = {1:10,2:50,3:200,4:10000};
for(let y=0;y<size;y++){
for(let x=0;x<size;x++){
if(map[y][x]!==0) continue;
let score = 0;
// 模拟白棋自己连成线加分
map[y][x]=2;
score += calcScore(x,y,2,scoreTable);
// 模拟封堵黑棋减损
map[y][x]=1;
score += calcScore(x,y,1,scoreTable)*0.9;
map[y][x]=0;
// 中心权重加分
let dist = Math.abs(x-7)+Math.abs(y-7);
score += (14-dist)*2;
if(score>bestScore){
bestScore = score;
bestPos = [x,y];
}
}
}
return bestPos;
}
// 恶魔AI打分辅助
function calcScore(x,y,val,table){
const dirs = [[1,0],[0,1],[1,1],[1,-1]];
let total = 0;
for(let [dx,dy] of dirs){
let c = 1;
let nx=x+dx,ny=y+dy;
while(nx>=0&&nx<size&&ny>=0&&ny<size&&map[ny][nx]===val){c++;nx+=dx;ny+=dy}
nx=x-dx;ny=y-dy;
while(nx>=0&&nx<size&&ny>=0&&ny<size&&map[ny][nx]===val){c++;nx-=dx;ny-=dy}
if(c>=5) total+=table[4];
else if(table[c]) total+=table[c];
}
return total;
}
// AI自动落子入口
function aiPlay(){
if(over) return;
let pos;
if(mode==="easy") pos = aiEasy();
else if(mode==="mid") pos = aiMid();
else pos = aiHard();
place(pos[0],pos[1]);
}
initBoard();
</script>
</body>
</html>Game Source: 五子棋|双人&三难度AI
Creator: SuperPhoenix65
Libraries: none
Complexity: complex (226 lines, 6.9 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: ai-superphoenix65" to link back to the original. Then publish at arcadelab.ai/publish.