🎮ArcadeLab

五子棋|双人&三难度AI

by SuperPhoenix65
226 lines6.9 KB
▶ Play
<!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.