JavaScript 進階——井字棋遊戲智能AI搭建

目錄

井字棋遊戲

準備的HTML:

準備的CSS:

第一部分點擊出現O的JS代碼:

現階段效果:

第二部分:判斷勝利

現階段結果:

第三部分:簡單的智能AI

現階段結果:

第四部分:之前我們很容易就能擊敗AI,所以現在要強化AI的難度

完整JavaScript代碼:

結果:不多說了,還沒贏過😂

學習來源:JavaScript井字棋遊戲開發與AI算法


 

井字棋遊戲

當初在高中的時候課間無聊經常和同學會下井字棋遊戲,突然想做一下有一個智能陪玩的井字棋遊戲。

然後就發現AI算法好難啊╭( ̄▽ ̄)╯╧═╧,放棄......

去網上找找,發現了一個講解比較詳細的井字棋遊戲搭建\(@^0^@)/

做好之後,結果就沒贏過了`(+﹏+)′

ps:算法真的是慘無人道啊

準備的HTML:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		<link rel="stylesheet" type="text/css" href="css/jingzi.css"/>
	</head>
	<body>
		<table>
			<tr>
				<td class="cell" id="0"></td>
				<td class="cell" id="1"></td>
				<td class="cell" id="2"></td>
			</tr>
			<tr>
				<td class="cell" id="3"></td>
				<td class="cell" id="4"></td>
				<td class="cell" id="5"></td>
			</tr>
			<tr>
				<td class="cell" id="6"></td>
				<td class="cell" id="7"></td>
				<td class="cell" id="8"></td>
			</tr>
		</table>
		<div class="endgame">
			<div class="text"></div>
		</div>
		<button onclick="startGame()">重新開始</button>
		<script src="js/jingzi.js" type="text/javascript" charset="utf-8"></script>
	</body>
</html>

準備的CSS:

*{
	margin: 0;
	padding: 0;
}

td {
	border: 2px solid #333;
	width: 100px;
	height: 100px;
	text-align: center;
	vertical-align: middle; //垂直	
	font-family: "微軟雅黑";
	font-style:italic; 
	font-size: 70px;
	cursor: pointer; //光標屬性
}

table {
	/*margin: 30px auto;*/
	position: absolute;
	left: 40%;
	top: 100px;
	border-collapse: collapse;
}
/*去除最外部邊框*/

table tr:first-child td{
	border-top: 0;
}

table tr:last-child td{
	border-bottom: 0;
}

table tr td:first-child{
	border-left: 0;
}

table tr td:last-child{
	border-right: 0;
}

.endgame{
	display: none;
	width: 200px;
	height: 120px;
	background-color: rgba(205,132,65,0.8);
	position: absolute;
	left: 40%;
	top:180px;
	margin-left: 50px;
	text-align: center;
	border-radius: 5px;
	color: white;
	font-size: 2em;
}

第一部分點擊出現O的JS代碼:

const winCombos =[
	[0, 1, 2],
	[3, 4, 5],
	[6, 7, 8],
	[0, 3, 6],
	[1, 4, 7],
	[2, 5, 8], 
	[0, 4, 8],
	[6, 4, 2]
]
/*獲取元素*/
const cells = document.querySelectorAll(".cell");
startGame();

function startGame(){
	document.querySelector(".endgame").style.display="none";
	//設置陣列點  創建9個數組元素,元素的鍵0到8
	origBoard = Array.from(Array(9).keys());
//	console.log(origBoard);
	for (var i = 0; i < cells.length; i++) {
		//把文本先設置爲空
		cells[i].innerHTML = "";
		//刪除屬性知道已經有人贏了
		cells[i].style.removeProperty('background-color');
		//點擊方塊
		cells[i].addEventListener('click',turnClick,false);
	}
}

function turnClick(square){
	//控制檯點擊日誌
//	console.log(square.target.id);
	//人類玩家點擊
	turn(square.target.id,huPlayer);
}
//參數是方塊ID,播放器
function turn(squareId,player){
	//這些板陣列數組將屬於玩家
	origBoard[squareId] = player;
	document.getElementById(squareId).innerHTML = player;

}

現階段效果:

 

第二部分:判斷勝利

/*1. Basic setup  一些變量並添加能力
2. Determine winner  添加邏輯,獲勝者並展示
3. Basic AI and winner notificatior  創建一個基本AI
4. Minimax a lgori thm !*/

var origBoard;
const huPlayer = 'O';
const aiPlayer = 'X';
/*勝利的線組,包括對角線*/
const winCombos =[
	[0, 1, 2],
	[3, 4, 5],
	[6, 7, 8],
	[0, 3, 6],
	[1, 4, 7],
	[2, 5, 8], 
	[0, 4, 8],
	[6, 4, 2]
]
/*獲取元素*/
const cells = document.querySelectorAll(".cell");
startGame();

function startGame(){
	document.querySelector(".endgame").style.display="none";
	//設置陣列點  創建9個數組元素,元素的鍵0到8
	origBoard = Array.from(Array(9).keys());
//	console.log(origBoard);
	for (var i = 0; i < cells.length; i++) {
		//把文本先設置爲空
		cells[i].innerHTML = "";
		//刪除屬性知道已經有人贏了
		cells[i].style.removeProperty('background-color');
		//點擊方塊
		cells[i].addEventListener('click',turnClick,false);
	}
}

function turnClick(square){
	//控制檯點擊日誌
//	console.log(square.target.id);
	//人類玩家點擊
	turn(square.target.id,huPlayer);
}
//參數是方塊ID,播放器
function turn(squareId,player){
	//這些板陣列數組將屬於玩家
	origBoard[squareId] = player;
	document.getElementById(squareId).innerHTML = player;
	//讓遊戲進行檢查
	var gameWin = checkWin(origBoard,player);
	if(gameWin){
		gameOver(gameWin);
	}
}
/*檢查是否勝利方法*/
function checkWin(board,player){
	//使用reduce累加器
	let plays = board.reduce((a,e,i)=> 
		(e===player) ? a.concat(i):a ,[])
	let gameWin = null;
	//如果是屬於之前winCombos勝利組合
	for (let [index,win] of winCombos.entries()) {
		if (win.every(Element => plays.indexOf(Element) > -1)){
			//現在我們知道是哪一個組合勝利了
			gameWin = {index:index,player:player};
			break;
		}
	}
	return gameWin;
}
/*遊戲結束*/
function gameOver(gameWin){
	for(let index of winCombos[gameWin.index]){
		//人類獲勝則爲藍色
		document.getElementById(index).style.backgroundColor = 
			gameWin.player == huPlayer? "blue":"red";
	}
	/*事件偵聽器刪除單擊,已經結束了,你不能再點擊*/
	for (var i = 0; i < cells.length; i++) {
		cells[i].removeEventListener('click',turnClick,false);
	}
}

現階段結果:

 

第三部分:簡單的智能AI

/*1. Basic setup  一些變量並添加能力
2. Determine winner  添加邏輯,獲勝者並展示
3. Basic AI and winner notificatior  創建一個基本AI
4. Minimax a lgori thm !*/

var origBoard;
const huPlayer = 'O';
const aiPlayer = 'X';
/*勝利的線組,包括對角線*/
const winCombos =[
	[0, 1, 2],
	[3, 4, 5],
	[6, 7, 8],
	[0, 3, 6],
	[1, 4, 7],
	[2, 5, 8], 
	[0, 4, 8],
	[6, 4, 2]
]
/*獲取元素*/
const cells = document.querySelectorAll(".cell");
startGame();

function startGame(){
	document.querySelector(".endgame").style.display="none";
	//設置陣列點  創建9個數組元素,元素的鍵0到8
	origBoard = Array.from(Array(9).keys());
//	console.log(origBoard);
	for (var i = 0; i < cells.length; i++) {
		//把文本先設置爲空
		cells[i].innerHTML = "";
		//刪除屬性知道已經有人贏了
		cells[i].style.removeProperty('background-color');
		//點擊方塊
		cells[i].addEventListener('click',turnClick,false);
	}
}

function turnClick(square){
	//控制檯點擊日誌
//	console.log(square.target.id);
//記住原來走過的方塊
	if(typeof origBoard[square.target.id] == 'number'){
		//人類玩家點擊
		turn(square.target.id,huPlayer);
		//由人類轉向AI玩家
		if(!checkTie()){
			//電腦玩家將拐彎,走最合適的地方
			turn(bestStep(),aiPlayer);
		}
	}
	
}
//參數是方塊ID,播放器
function turn(squareId,player){
	//這些板陣列數組將屬於玩家
	origBoard[squareId] = player;
	document.getElementById(squareId).innerHTML = player;
	//讓遊戲進行檢查
	var gameWin = checkWin(origBoard,player);
	if(gameWin){
		gameOver(gameWin);
	}
}
/*檢查是否勝利方法*/
function checkWin(board,player){
	//使用reduce累加器
	let plays = board.reduce((a,e,i)=> 
		(e===player) ? a.concat(i):a ,[])
	let gameWin = null;
	//如果是屬於之前winCombos勝利組合
	for (let [index,win] of winCombos.entries()) {
		if (win.every(Element => plays.indexOf(Element) > -1)){
			//現在我們知道是哪一個組合勝利了
			gameWin = {index:index,player:player};
			break;
		}
	}
	return gameWin;
}
/*遊戲結束*/
function gameOver(gameWin){
	for(let index of winCombos[gameWin.index]){
		//人類獲勝則爲藍色
		document.getElementById(index).style.backgroundColor = 
			gameWin.player == huPlayer? "blue":"red";
	}
	/*事件偵聽器刪除單擊,已經結束了,你不能再點擊*/
	for (var i = 0; i < cells.length; i++) {
		cells[i].removeEventListener('click',turnClick,false);
	}
	declareWinner(gameWin.player == huPlayer ? "你已經獲得了勝利":"對不起,你輸了");
}

function emptySquares(){
	//過濾每一個元素,如果元素爲number,返回所有方塊
	return origBoard.filter(s => typeof s=='number');
}

/*AI最優步驟*/
function bestStep(){
	return emptySquares()[0];
}
//眼睛功能,檢查是否是平局
function checkTie(){
	if(emptySquares().length == 0){
		for (var i = 0; i < cells.length; i++) {
			cells[i].style.backgroundColor = "green";
			cells[i].removeEventListener('click',turnClick,false);
		}
		//誰獲勝了
		declareWinner("Tie Game");
		return true;
	}else{
		//平局
		return false;
	}
	
}

function declareWinner(who){
	document.querySelector(".endgame").style.display = 'block';	
	document.querySelector(".endgame .text").innerHTML = who;
}

現階段結果:

第四部分:之前我們很容易就能擊敗AI,所以現在要強化AI的難度

這裏需要比較強的算法知識

完整JavaScript代碼:

/*1. Basic setup  一些變量並添加能力
2. Determine winner  添加邏輯,獲勝者並展示
3. Basic AI and winner notificatior  創建一個基本AI
4. Minimax a lgori thm !*/

var origBoard;
const huPlayer = 'O';
const aiPlayer = 'X';
/*勝利的線組,包括對角線*/
const winCombos =[
	[0, 1, 2],
	[3, 4, 5],
	[6, 7, 8],
	[0, 3, 6],
	[1, 4, 7],
	[2, 5, 8], 
	[0, 4, 8],
	[6, 4, 2]
]
/*獲取元素*/
const cells = document.querySelectorAll(".cell");
startGame();

function startGame(){
	document.querySelector(".endgame").style.display="none";
	//設置陣列點  創建9個數組元素,元素的鍵0到8
	origBoard = Array.from(Array(9).keys());
//	console.log(origBoard);
	for (var i = 0; i < cells.length; i++) {
		//把文本先設置爲空
		cells[i].innerHTML = "";
		//刪除屬性知道已經有人贏了
		cells[i].style.removeProperty('background-color');
		//點擊方塊
		cells[i].addEventListener('click',turnClick,false);
	}
}

function turnClick(square){
	//控制檯點擊日誌
//	console.log(square.target.id);
//記住原來走過的方塊
	if(typeof origBoard[square.target.id] == 'number'){
		//人類玩家點擊
		turn(square.target.id,huPlayer);
		//由人類轉向AI玩家
		if(!checkTie()){
			//電腦玩家將拐彎,走最合適的地方
			turn(bestStep(),aiPlayer);
		}
	}
	
}
//參數是方塊ID,播放器
function turn(squareId,player){
	//這些板陣列數組將屬於玩家
	origBoard[squareId] = player;
	document.getElementById(squareId).innerHTML = player;
	//讓遊戲進行檢查
	var gameWin = checkWin(origBoard,player);
	if(gameWin){
		gameOver(gameWin);
	}
}
/*檢查是否勝利方法*/
function checkWin(board,player){
	//使用reduce累加器
	let plays = board.reduce((a,e,i)=> 
		(e===player) ? a.concat(i):a ,[])
	let gameWin = null;
	//如果是屬於之前winCombos勝利組合
	for (let [index,win] of winCombos.entries()) {
		if (win.every(Element => plays.indexOf(Element) > -1)){
			//現在我們知道是哪一個組合勝利了
			gameWin = {index:index,player:player};
			break;
		}
	}
	return gameWin;
}
/*遊戲結束*/
function gameOver(gameWin){
	for(let index of winCombos[gameWin.index]){
		//人類獲勝則爲藍色
		document.getElementById(index).style.backgroundColor = 
			gameWin.player == huPlayer? "blue":"red";
	}
	/*事件偵聽器刪除單擊,已經結束了,你不能再點擊*/
	for (var i = 0; i < cells.length; i++) {
		cells[i].removeEventListener('click',turnClick,false);
	}
	declareWinner(gameWin.player == huPlayer ? "你已經獲得了勝利":"對不起,你輸了");
}

function emptySquares(){
	//過濾每一個元素,如果元素爲number,返回所有方塊
	return origBoard.filter(s => typeof s=='number');
}

/*AI最優步驟*/
function bestStep(){
	//簡單AI
//	return emptySquares()[0];
	//智能AI
	return minmax(origBoard,aiPlayer).index;
}
//眼睛功能,檢查是否是平局
function checkTie(){
	if(emptySquares().length == 0){
		for (var i = 0; i < cells.length; i++) {
			cells[i].style.backgroundColor = "green";
			cells[i].removeEventListener('click',turnClick,false);
		}
		//誰獲勝了
		declareWinner("Tie Game");
		return true;
	}else{
		//平局
		return false;
	}
	
}

function declareWinner(who){
	document.querySelector(".endgame").style.display = 'block';	
	document.querySelector(".endgame .text").innerHTML = who;
}

function minmax(newBoard,player){
	//找到索引,空方塊功能設置爲a
	var availSpots = emptySquares(newBoard);
	
	if(checkWin(newBoard,player)){
		return {score:-10};
	}else if(checkWin(newBoard,aiPlayer)){
		return {score:20};
	}else if(availSpots.length === 0){
		return {score:0};
	}
	//之後進行評估
	var moves = [];
	//收集每個動作時的空白點
	for (var i = 0; i < availSpots.length; i++) {
		//然後設置空的索引號
		var move = {};
		move.index = newBoard[availSpots[i]];
		newBoard[availSpots[i]] = player;
		
		if( player == aiPlayer){
			//存儲對象,包括得分屬性
			var result = minmax(newBoard,huPlayer);
			move.score = result.score;
		}else{
			//存儲對象,包括得分屬性
			var result = minmax(newBoard,aiPlayer);
			move.score = result.score;
		}
		
		newBoard[availSpots[i]] = move.index;
		
		moves.push(move);
	}
	var bestMove;
	//如果是AI玩家,以非常低的數字和循環通過
	if(player === aiPlayer){
		var bestScore = -1000;
		for (var i = 0; i < moves.length; i++) {
			if(moves[i].score > bestScore){
				bestScore = moves[i].score;
				bestMove = i;
			}
		}
	}else{
		var bestScore = 1000;
		for (var i = 0; i < moves.length; i++) {
			if(moves[i].score < bestScore){
				bestScore = moves[i].score;
				bestMove = i;
			}
		}
	}
	
	return moves[bestMove];
}

結果:不多說了,還沒贏過😂

 

學習來源:JavaScript井字棋遊戲開發與AI算法

代碼鏈接:https://download.csdn.net/download/qq_36171287/12252143

 

一起學習,一起進步 -.- ,如有錯誤,可以發評論

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章