目录

井字棋游戏

准备的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

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