JS實(shí)現(xiàn)五子棋完整步驟及功能擴(kuò)展
五子棋規(guī)則詳解
1. 棋盤(pán)與棋子
- 棋盤(pán)規(guī)格:標(biāo)準(zhǔn)五子棋使用15×15的方格棋盤(pán),共225個(gè)交叉點(diǎn)(日文稱"目")。棋盤(pán)通常為木質(zhì)或塑料材質(zhì),橫向標(biāo)記A-O(或1-15),縱向標(biāo)記1-15便于定位
- 棋子:雙方分別使用黑色(先手)和白色(后手)的圓形扁片棋子,直徑約1cm。日本規(guī)則中常用雙凸面的玻璃棋子,落子時(shí)會(huì)發(fā)出清脆聲響
2. 基本規(guī)則
開(kāi)局:
- 黑棋先行(現(xiàn)代競(jìng)技規(guī)則中通常采用"指定開(kāi)局"制,即黑方必須在棋盤(pán)中心區(qū)域完成前三手)
- 傳統(tǒng)民間玩法可采用"自由開(kāi)局",即黑棋第一手可下在任意位置
落子規(guī)則:
- 棋子必須放在交叉點(diǎn)上,不得放在方格內(nèi)
- 落子后不允許移動(dòng)或取回(除非采用"禁手"規(guī)則下的提子)
- 雙方交替落子,每次只能下一顆棋子
勝負(fù)判定:
- 先形成"五連"(橫、豎、斜任意方向的連續(xù)五顆同色棋子)者勝
- 若棋盤(pán)填滿仍未分勝負(fù)(極罕見(jiàn)),則判為和棋
- 正式比賽通常采用計(jì)時(shí)制,超時(shí)判負(fù)
3. 競(jìng)技規(guī)則變體
禁手規(guī)則(職業(yè)比賽采用):
- 三三禁手:黑棋禁止同時(shí)形成兩個(gè)活三
- 四四禁手:黑棋禁止同時(shí)形成兩個(gè)活四
- 長(zhǎng)連禁手:黑棋禁止形成六子或以上的連線
- 出現(xiàn)禁手時(shí)白方必須立即指出,經(jīng)裁判確認(rèn)后白方獲勝
交換規(guī)則:
- 國(guó)際連珠聯(lián)盟(RIF)標(biāo)準(zhǔn)規(guī)則包含"三手交換":
- 黑方下開(kāi)局前三手(必須包含對(duì)稱布局)
- 白方有權(quán)選擇交換顏色或保持原色
- 之后由白方下第四手,黑方提供第五手的多個(gè)打點(diǎn)供白方選擇
- 國(guó)際連珠聯(lián)盟(RIF)標(biāo)準(zhǔn)規(guī)則包含"三手交換":
五手兩打:
- 黑方第五手必須提供兩個(gè)對(duì)稱的打點(diǎn)
- 白方有權(quán)選擇保留其中一個(gè),另一個(gè)由黑方收回
4. 特殊術(shù)語(yǔ)
- 活四:兩端無(wú)阻擋的四連子,下一步必勝
- 沖四:一端被阻擋的四連子
- 活三:能發(fā)展成活四的三連子
- 眠三:一端被阻擋的三連子
- VCF(Victory by Continuous Four):通過(guò)連續(xù)沖四取勝的戰(zhàn)術(shù)
5. 應(yīng)用場(chǎng)景
- 休閑玩法:公園、家庭等場(chǎng)合多采用簡(jiǎn)單規(guī)則,不設(shè)禁手
- 網(wǎng)絡(luò)對(duì)戰(zhàn):騰訊、野狐等平臺(tái)通常提供多種規(guī)則選項(xiàng)
- 職業(yè)比賽:世界連珠錦標(biāo)賽、中國(guó)全國(guó)智力運(yùn)動(dòng)會(huì)等采用RIF標(biāo)準(zhǔn)規(guī)則
- AI研究:作為離散數(shù)學(xué)和人工智能的研究對(duì)象,2016年AlphaGo團(tuán)隊(duì)開(kāi)發(fā)的AlphaZero已能完勝人類頂尖選手
6. 注意事項(xiàng)
- 正式比賽禁止使用"影子戰(zhàn)術(shù)"(模仿棋)
- 黑棋因先手優(yōu)勢(shì),在現(xiàn)代規(guī)則中受到多重限制
- 日本"連珠"與歐美"Gomoku"規(guī)則存在細(xì)微差異,網(wǎng)絡(luò)對(duì)戰(zhàn)需提前確認(rèn)規(guī)則版本
游戲概述
五子棋是一種傳統(tǒng)的策略型棋類游戲,黑白雙方輪流在棋盤(pán)上落子,先形成五子連線的一方獲勝。下面介紹如何使用JavaScript實(shí)現(xiàn)一個(gè)簡(jiǎn)單的五子棋游戲。
實(shí)現(xiàn)步驟
1. 創(chuàng)建HTML結(jié)構(gòu)
<!DOCTYPE html>
<html>
<head>
<title>JS五子棋</title>
<style>
#board {
display: grid;
grid-template-columns: repeat(15, 30px);
grid-template-rows: repeat(15, 30px);
gap: 1px;
background-color: #DCB35C;
padding: 10px;
}
.cell {
width: 30px;
height: 30px;
background-color: #DCB35C;
border: 1px solid #000;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
.black {
background-color: black;
border-radius: 50%;
}
.white {
background-color: white;
border-radius: 50%;
}
</style>
</head>
<body>
<h1>五子棋游戲</h1>
<div id="board"></div>
<div id="status">當(dāng)前玩家: 黑棋</div>
</body>
</html>
2. JavaScript核心邏輯
document.addEventListener('DOMContentLoaded', function() {
const boardSize = 15;
const board = document.getElementById('board');
const statusDiv = document.getElementById('status');
let currentPlayer = 'black';
let gameBoard = Array(boardSize).fill().map(() => Array(boardSize).fill(null));
// 初始化棋盤(pán)
function initializeBoard() {
for (let i = 0; i < boardSize; i++) {
for (let j = 0; j < boardSize; j++) {
const cell = document.createElement('div');
cell.className = 'cell';
cell.dataset.row = i;
cell.dataset.col = j;
cell.addEventListener('click', handleCellClick);
board.appendChild(cell);
}
}
}
// 處理點(diǎn)擊事件
function handleCellClick(event) {
const row = parseInt(event.target.dataset.row);
const col = parseInt(event.target.dataset.col);
// 如果該位置已有棋子,則返回
if (gameBoard[row][col]) return;
// 更新游戲狀態(tài)
gameBoard[row][col] = currentPlayer;
event.target.classList.add(currentPlayer);
// 檢查是否獲勝
if (checkWin(row, col)) {
setTimeout(() => {
alert(`${currentPlayer === 'black' ? '黑棋' : '白棋'}獲勝!`);
resetGame();
}, 100);
return;
}
// 切換玩家
currentPlayer = currentPlayer === 'black' ? 'white' : 'black';
statusDiv.textContent = `當(dāng)前玩家: ${currentPlayer === 'black' ? '黑棋' : '白棋'}`;
}
// 檢查勝利條件
function checkWin(row, col) {
const directions = [
[0, 1], // 水平
[1, 0], // 垂直
[1, 1], // 對(duì)角線
[1, -1] // 反對(duì)角線
];
for (const [dx, dy] of directions) {
let count = 1;
// 正向檢查
for (let i = 1; i < 5; i++) {
const newRow = row + i * dx;
const newCol = col + i * dy;
if (
newRow >= 0 && newRow < boardSize &&
newCol >= 0 && newCol < boardSize &&
gameBoard[newRow][newCol] === currentPlayer
) {
count++;
} else {
break;
}
}
// 反向檢查
for (let i = 1; i < 5; i++) {
const newRow = row - i * dx;
const newCol = col - i * dy;
if (
newRow >= 0 && newRow < boardSize &&
newCol >= 0 && newCol < boardSize &&
gameBoard[newRow][newCol] === currentPlayer
) {
count++;
} else {
break;
}
}
if (count >= 5) return true;
}
return false;
}
// 重置游戲
function resetGame() {
gameBoard = Array(boardSize).fill().map(() => Array(boardSize).fill(null));
currentPlayer = 'black';
statusDiv.textContent = '當(dāng)前玩家: 黑棋';
const cells = document.querySelectorAll('.cell');
cells.forEach(cell => {
cell.classList.remove('black', 'white');
});
}
initializeBoard();
});
功能擴(kuò)展與實(shí)現(xiàn)原理詳解
功能擴(kuò)展
悔棋功能
- 實(shí)現(xiàn)一個(gè)"悔棋"按鈕,點(diǎn)擊后可以撤銷上一步的落子操作
- 需要維護(hù)一個(gè)落子歷史棧,存儲(chǔ)每一步的落子位置和玩家信息
- 撤銷時(shí)從棧中彈出最近一步,恢復(fù)棋盤(pán)對(duì)應(yīng)位置為空
- 示例代碼邏輯:
function undoMove() { if (moveHistory.length > 0) { const lastMove = moveHistory.pop(); board[lastMove.row][lastMove.col] = EMPTY; currentPlayer = lastMove.player; updateBoardUI(); } }
游戲計(jì)時(shí)
- 為黑白雙方分別添加倒計(jì)時(shí)計(jì)時(shí)器(如每人15分鐘)
- 使用
setInterval每秒更新計(jì)時(shí)器顯示 - 當(dāng)某方時(shí)間耗盡時(shí)自動(dòng)判負(fù)
- 計(jì)時(shí)器暫停/恢復(fù)邏輯:
- 開(kāi)始對(duì)方回合時(shí)暫停本方計(jì)時(shí)器
- 對(duì)方落子后啟動(dòng)本方計(jì)時(shí)器
AI對(duì)戰(zhàn)
- 實(shí)現(xiàn)簡(jiǎn)單AI算法:
- 優(yōu)先在己方四連兩端落子形成五連
- 其次阻擋對(duì)方四連
- 然后尋找己方可形成活三的位置
- 隨機(jī)選擇空位作為最后策略
- 使用評(píng)估函數(shù)給每個(gè)空位打分,選擇最高分位置落子
保存游戲
- 使用
localStorage保存游戲狀態(tài):function saveGame() { const gameState = { board: board, currentPlayer: currentPlayer, moveHistory: moveHistory }; localStorage.setItem('gomokuGame', JSON.stringify(gameState)); } - 加載時(shí)解析存儲(chǔ)的JSON數(shù)據(jù)恢復(fù)游戲
音效增強(qiáng)
- 預(yù)加載落子音效和勝利音效文件
- 落子時(shí)播放"click"音效
- 勝利時(shí)播放"victory"音效
- 示例:
function playSound(type) { const audio = new Audio(`sounds/${type}.mp3`); audio.play(); }
實(shí)現(xiàn)原理說(shuō)明
棋盤(pán)表示
- 使用15×15二維數(shù)組表示棋盤(pán):
const board = Array(15).fill().map(() => Array(15).fill(0)); // 0表示空位,1表示黑子,2表示白子
- 每個(gè)數(shù)組元素對(duì)應(yīng)棋盤(pán)交叉點(diǎn)狀態(tài)
勝利檢測(cè)
- 從當(dāng)前落子點(diǎn)向四個(gè)方向檢查:
- 水平方向(左右)
- 垂直方向(上下)
- 主對(duì)角線(左上到右下)
- 副對(duì)角線(右上到左下)
- 檢查算法示例:
function checkWin(row, col) { const directions = [ [0, 1], [1, 0], [1, 1], [1, -1] // 四個(gè)方向向量 ]; for (const [dx, dy] of directions) { let count = 1; // 正向檢查 for (let i = 1; i < 5; i++) { const newRow = row + dx * i; const newCol = col + dy * i; if (!isValidPosition(newRow, newCol) || board[newRow][newCol] !== board[row][col]) { break; } count++; } // 反向檢查 for (let i = 1; i < 5; i++) { const newRow = row - dx * i; const newCol = col - dy * i; if (!isValidPosition(newRow, newCol) || board[newRow][newCol] !== board[row][col]) { break; } count++; } if (count >= 5) return true; } return false; }
玩家切換
- 使用變量記錄當(dāng)前玩家:
let currentPlayer = BLACK; // 1表示黑方,2表示白方
- 每次落子后切換玩家:
function switchPlayer() { currentPlayer = currentPlayer === BLACK ? WHITE : BLACK; updatePlayerIndicator(); }
UI更新
- 通過(guò)CSS類顯示棋子:
.cell.black { background: black; } .cell.white { background: white; border: 1px solid #333; } - JavaScript動(dòng)態(tài)添加類:
function updateBoardUI() { for (let row = 0; row < 15; row++) { for (let col = 0; col < 15; col++) { const cell = document.getElementById(`cell-${row}-${col}`); cell.className = 'cell'; if (board[row][col] === BLACK) { cell.classList.add('black'); } else if (board[row][col] === WHITE) { cell.classList.add('white'); } } } }

總結(jié)
到此這篇關(guān)于JS實(shí)現(xiàn)五子棋完整步驟及功能擴(kuò)展的文章就介紹到這了,更多相關(guān)JS實(shí)現(xiàn)五子棋內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript this指向相關(guān)問(wèn)題及改變方法
這篇文章主要介紹了javascript this指向相關(guān)問(wèn)題及改變方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-11-11
使用JS實(shí)現(xiàn)圖片輪播的實(shí)例(前后首尾相接)
下面小編就為大家?guī)?lái)一篇使用JS實(shí)現(xiàn)圖片輪播的實(shí)例(前后首尾相接)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09
關(guān)于取不到由location.href提交而來(lái)的上級(jí)頁(yè)面地址的解決辦法
驗(yàn)證上級(jí)頁(yè)面來(lái)源取不到由location.href提交而來(lái)的頁(yè)面地址,搜索了一大堆沒(méi)有合適的解決辦法,突然想到通過(guò)模擬JS點(diǎn)擊鏈接的方法2009-07-07
JavaScript學(xué)習(xí)總結(jié)之正則的元字符和一些簡(jiǎn)單的應(yīng)用
這篇文章主要介紹了JavaScript學(xué)習(xí)總結(jié)之正則的元字符和一些簡(jiǎn)單的應(yīng)用,需要的朋友可以參考下2017-06-06
clipboard.js無(wú)需Flash無(wú)需依賴任何JS庫(kù)實(shí)現(xiàn)文本復(fù)制與剪切
這篇文章主要實(shí)現(xiàn)了無(wú)需Flash無(wú)需依賴任何JS庫(kù)實(shí)現(xiàn)文本復(fù)制與剪切,是一款極現(xiàn)代的,不需要flash,不依賴任何其他js庫(kù)的非常小的插件,叫clipboard.js,感興趣的小伙伴們可以參考一下2015-10-10
javascript驗(yàn)證完全方法具體實(shí)現(xiàn)
下面這段代碼完全實(shí)現(xiàn)了判斷是否合格.傳入號(hào)碼就行了,包括了算法,下面的是用Ext實(shí)現(xiàn)的,但是基于javascript的語(yǔ)法居多,基本都可以用2013-11-11
Echarts?3D散點(diǎn)圖實(shí)戰(zhàn)案例
這篇文章主要給大家介紹了關(guān)于Echarts?3D散點(diǎn)圖的相關(guān)資料, Echarts散點(diǎn)圖是一種常用的數(shù)據(jù)可視化圖表類型,用于展示兩個(gè)或多個(gè)維度的數(shù)據(jù)分布情況,需要的朋友可以參考下2023-11-11

