console
const SIZE = 3;
const MAX_PIECES = 4;
const EMPTY = 0;
const BLACK = 1;
const WHITE = 2;
let board = [];
let currentPlayer = BLACK;
let gameOver = false;
let blackCount = 0;
let whiteCount = 0;
let mode = "place";
let historyBoards = new Set();
const infoEl = document.getElementById("info");
const boardEl = document.getElementById("board");
const btnRestart = document.getElementById("btnRestart");
const btnPlace = document.getElementById("btnPlace");
const btnPush = document.getElementById("btnPush");
btnRestart.onclick = initGame;
btnPlace.onclick = function(){
if(gameOver) return;
if(blackCount<MAX_PIECES || whiteCount<MAX_PIECES){
mode="place";
updateUi();
}
};
btnPush.onclick = function(){
if(gameOver) return;
if(blackCount>=MAX_PIECES && whiteCount>=MAX_PIECES){
mode="push";
updateUi();
}
};
function getBoardSnapshot(){
return board.flat().join(",");
}
// 深度拷贝棋盘,用于回滚
function cloneBoard(src){
return src.map(row=>[...row]);
}
function initGame(){
board = [];
for(let i=0;i<SIZE;i++) board.push([EMPTY,EMPTY,EMPTY]);
currentPlayer = BLACK;
gameOver = false;
blackCount = 0;
whiteCount = 0;
mode = "place";
historyBoards.clear();
historyBoards.add(getBoardSnapshot());
renderBoard();
updateUi();
}
function renderBoard(){
boardEl.innerHTML = "";
for(let r=0;r<3;r++){
for(let c=0;c<3;c++){
const div = document.createElement("div");
div.className = "cell";
const val = board[r][c];
if(val === BLACK){ div.innerText = "●"; div.classList.add("black"); }
if(val === WHITE){ div.innerText = "○"; div.classList.add("white"); }
div.onclick = ()=>cellClick(r,c);
boardEl.appendChild(div);
}
}
}
function updateUi(){
if(gameOver) return;
const playName = currentPlayer === BLACK ? "黑方 ●" : "白方 ○";
const modeStr = mode === "place" ? "【落子模式】" : "【推挤模式】";
infoEl.innerText = `${playName} ${modeStr}|黑${blackCount}/${MAX_PIECES} 白${whiteCount}/${MAX_PIECES}`;
}
function checkVictory(){
if(blackCount <= 0) return WHITE;
if(whiteCount <= 0) return BLACK;
return null;
}
function placePiece(r,c){
if(board[r][c] !== EMPTY) return;
if(currentPlayer === BLACK && blackCount >= MAX_PIECES) return;
if(currentPlayer === WHITE && whiteCount >= MAX_PIECES) return;
board[r][c] = currentPlayer;
if(currentPlayer === BLACK) blackCount++;
else whiteCount++;
renderBoard();
currentPlayer = currentPlayer === BLACK ? WHITE : BLACK;
if(blackCount >= MAX_PIECES && whiteCount >= MAX_PIECES){
mode = "push";
}
historyBoards.add(getBoardSnapshot());
updateUi();
}
function doAction(sr, sc, dr, dc){
// 移动前完整保存现场,用于回滚
const oldBoard = cloneBoard(board);
const oldBlackCnt = blackCount;
const oldWhiteCnt = whiteCount;
const self = board[sr][sc];
const tr = sr + dr;
const tc = sc + dc;
if(tr<0||tr>=SIZE||tc<0||tc>=SIZE){
infoEl.innerText="⚠️不能移动到棋盘外面";
return;
}
if(board[tr][tc] === EMPTY){
board[tr][tc] = self;
board[sr][sc] = EMPTY;
}
else if(board[tr][tc] !== self){
const t2r = tr + dr;
const t2c = tc + dc;
if(t2r <0 || t2r >= SIZE || t2c <0 || t2c >= SIZE){
const victim = board[tr][tc];
board[tr][tc] = self;
board[sr][sc] = EMPTY;
if(victim === BLACK) blackCount--;
if(victim === WHITE) whiteCount--;
}else{
if(board[t2r][t2c] !== EMPTY){
infoEl.innerText="⚠️后方被挡住,推不动对方棋子";
return;
}
board[t2r][t2c] = board[tr][tc];
board[tr][tc] = self;
board[sr][sc] = EMPTY;
}
}
else{
infoEl.innerText="⚠️不能移动到己方棋子位置";
return;
}
renderBoard();
let win = checkVictory();
if(win){
gameOver=true;
infoEl.innerText = win===BLACK?"黑方 ● 胜利!":"白方 ○ 胜利!";
return;
}
// 检测是否出现旧局面
const snap = getBoardSnapshot();
if(historyBoards.has(snap)){
// 回滚到移动之前状态,回合不切换,游戏继续
board = oldBoard;
blackCount = oldBlackCnt;
whiteCount = oldWhiteCnt;
renderBoard();
infoEl.innerText="⚠️出现重复局面,本次移动无效,请重新操作";
return;
}
historyBoards.add(snap);
currentPlayer = currentPlayer===BLACK?WHITE:BLACK;
updateUi();
}
let selectR = -1;
let selectC = -1;
function cellClick(r,c){
if(gameOver) return;
if(mode === "place"){
placePiece(r,c);
selectR=-1;selectC=-1;
return;
}
if(selectR === -1 && selectC === -1){
if(board[r][c] !== currentPlayer){
infoEl.innerText="⚠️请点击你自己的棋子";
return;
}
selectR = r;
selectC = c;
infoEl.innerText="✅已选中,点击相邻格子:空格直接移动、敌方就推动";
}else{
const dr = r - selectR;
const dc = c - selectC;
if(Math.abs(dr)+Math.abs(dc) !== 1){
selectR=-1;
selectC=-1;
updateUi();
return;
}
doAction(selectR, selectC, dr, dc);
selectR = -1;
selectC = -1;
}
}
initGame();
<h2>领地牢笼</h2>
<div id="info"></div>
<div id="board"></div>
<div>
<button id="btnRestart">重新开局</button>
<button id="btnPlace">落子模式</button>
<button id="btnPush">推挤模式</button>
</div>
<div class="desc">
落子:黑白轮流各下4子。下满4子后开始推挤。胜利:把对方棋子全部推出棋盘。
</div>
*{box-sizing:border-box;font-family:system-ui, sans-serif;}
body{background:#1a1a27;color:#eee;display:flex;flex-direction:column;align-items:center;padding:20px;}
#board{display:grid;grid-template-columns:repeat(3, 1fr);gap:6px;margin:16px 0;max-width:320px;width:100%;}
.cell{
aspect-ratio:1/1;background:#2c2c44;border-radius:8px;
display:flex;align-items:center;justify-content:center;
font-size:40px;font-weight:bold;cursor:pointer;
border:2px solid #444466;
}
.cell:hover{border-color:#7777aa;}
.cell.black{color:#111;background:#bbbbbb;}
.cell.white{color:#ffffff;background:#224488;}
#info{font-size:17px;min-height:45px;}
button{padding:8px 12px;font-size:15px;margin:4px;border:none;border-radius:6px;background:#4466bb;color:#fff;cursor:pointer;}
.desc{margin-top:14px;font-size:12px;color:#aaaaaa;max-width:400px;line-height:1.45;}