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

 

一起学习,一起进步 -.- ,如有错误,可以发评论

  • 14
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

空城机

有钱捧个钱场,没钱捧个人场

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值