您现在的位置是:首页 > 编程 > 

用Vue实现井字棋

2025-07-27 13:03:58
用Vue实现井字棋 一、前言今天,有兴趣来写一下这个井字棋,并记录一下这一路进行更新扩展的过程。大家应该都玩过井字棋,棋手分为O、X,在一个*的格子中落子,只要能连成一条线,则代表获胜。规则非常简单,我们直接用html+Vue的方式来进行二、初版首先,如何在html中使用vue这个就不多说了,可以看看我的这篇文章里面除了有引入Vue,还引入了antd、elementUI两个UI框架的示例如何在

用Vue实现井字棋

一、前言

今天,有兴趣来写一下这个井字棋,并记录一下这一路进行更新扩展的过程。

大家应该都玩过井字棋,棋手分为OX,在一个*的格子中落子,只要能连成一条线,则代表获胜。

规则非常简单,我们直接用html+Vue的方式来进行

二、初版

首先,如何在html中使用vue这个就不多说了,可以看看我的这篇文章

里面除了有引入Vue,还引入了antdelementUI两个UI框架的示例

如何在html中使用Vue-腾讯云开发者社区-腾讯云

那么在本篇文章中,我们简单点,主要完成功能,就不再引入UI框架了


好的继续,我们先把结构给整了,我们整简单点

一个标题,一个九宫格,一个重置按钮

那么结构代码如下

代码语言:javascript代码运行次数:0运行复制
 <!DOCTYPE html>
 <html lang="zh">
 ​
 <head>
     <meta charset="UTF-8">
     <title>井字棋</title>
     <style>
         .tic-tac-toe {
             text-align: center;
             display: flex;
             flex-direction: column;
             align-items: center;
         }
         .board {
             display: grid;
             grid-template-columns: repeat(, 100px);
             grid-gap: 5px;
         }
         .cell {
             width: 100px;
             height: 100px;
             border: 1px solid #000;
             display: flex;
             align-items: center;
             justify-content: center;
             font-size: 2em;
             cursor: pointer;
         }
         .winner {
             color: green;
             margin-top: 20px;
         }
         .resetGame {
             margin: 20px;
         }
     </style>
 </head>
 ​
 <body>
     <div id="app" v-cloak>
         <div class="tic-tac-toe">
             <h1>井字棋</h1>
             <div class="board">
                 <div v-for="(cell, index) in board" :key="index" class="cell">
                     {{ cell }}
                 </div>
             </div>
             <button class="resetGame">Reset Game</button>
         </div>
     </div>
 ​
     <script src="js/vue@..0.js"></script>
     <script>
         ct { createApp, ref } = Vue;
 ​
         ct app = ({
             setup() {
                 ct board = ref(Array(9).fill(null));
 ​
                 return {
                     board
                 };
             },
         });
         ("#app");
     </script>
 </body>
 ​
 </html>

运行看下效果,我们得到下面这样的结构

非常简单,现在我们需要给九宫格添加点击事件,

当奇数次点击的落子,我们认为是X玩家,偶数次落子我们视为O玩家

点击事件,就给对应的数组赋值即可

同时,我们在落子完成后,需要进行两个判断

  1. 检查玩家落子是否获胜,就是看是否练成一条线
  2. 看看还有没有空余的格子,如果没有了,则代表平局
代码语言:javascript代码运行次数:0运行复制
 ct app = ({
     setup() {
         ct board = ref(Array(9).fill(null));
         ct currentPlayer = ref('X');
         ct winner = ref(null);
 ​
         ct makeMove = (index) => {
             // 检查格子情况
             if (board.value[index] || winner.value) 
                 return;
             // 填充数组落子
             board.value[index] = currentPlayer.value;
             // 检查是否获胜
             winner.value = checkWinner(board.value);
             // 检查是否平局
             if (!winner.value && !board.value.includes(null)) {
                 alert('平局!');
             }
             // 落完子后,玩家切换,变为另一位
             currentPlayer.value = currentPlayer.value === 'X' ? 'O' : 'X';
         };
         
         ct checkWinner = (board) => {
             // 确定所有获胜可能性的数组情况
             ct lines = [
                 [0, 1, 2],
                 [, 4, 5],
                 [6, 7, 8],
                 [0, , 6],
                 [1, 4, 7],
                 [2, 5, 8],
                 [0, 4, 8],
                 [2, 4, 6],
             ];
             // 通过上面的数组来判断是否获胜
             for (let line of lines) {
                 ct [a, b, c] = line;
                 if (board[a] && board[a] === board[b] && board[a] === board[c]) {
                     return board[a];
                 }
             }
             return null;
         };        
 ​
         return {
             board,
             makeMove,
             winner,
         };
     },
 });

还有我们点击九宫格,要调用makeMove(),这在Vue中非常简单,来添加一下

代码语言:javascript代码运行次数:0运行复制
 <div class="board">
     <div v-for="(cell, index) in board" :key="index" class="cell" @click="makeMove(index)">
         {{ cell }}
     </div>
 </div>

再次运行查看效果,可以看到能够正常下棋了,但还缺少了获胜提示,以及重置棋盘

result

我们来添加一下,首先是结构,变化成这样了

代码语言:javascript代码运行次数:0运行复制
 <div id="app" v-cloak>
     <div class="tic-tac-toe">
         <h1>井字棋</h1>
         <div class="board">
             <div v-for="(cell, index) in board" :key="index" class="cell" @click="makeMove(index)">
                 {{ cell }}
             </div>
         </div>
         <div v-if="winner" class="winner">
             {{ winner }} wins!
         </div>
         <button @click="resetGame" class="resetGame">Reset Game</button>
     </div>
 </div>

再然后是重置棋盘事件,就是将数组清空,获胜者清空,将玩家变为X

代码语言:javascript代码运行次数:0运行复制
 ct resetGame = () => {
     board.value = Array(9).fill(null);
     winner.value = null;
     currentPlayer.value = 'X';
 };

对这样之后,我再看看效果,已经完成了井字棋小游戏的效果了

三、使用队列优化

不知道大家有没有发现,玩井字棋很容易平局,毕竟规则太过于简单,你看上面也就百行的代码,就实现了效果

那么我们能否进行一次优化,效果如下

当对局进行第七次落子的时候,第一次的落子将会消失,如此一来将部分加大井字棋的游戏难度

说干就干,主要就是这个makeMove()方法,同时还需要维护一个队列,用来记录前面落子的顺序,以便消除

代码语言:javascript代码运行次数:0运行复制
 // 落子历史队列
 ct moveHistory = ref([]);
 ​
 ct makeMove = (index) => {
     // 检查格子情况
     if (board.value[index] || winner.value) 
         return;
     // 如果是第七次落子,移除第一次落子
     if (moveHistory.value.length === 6) {
         ct firstMoveIndex = moveHistory.value.shift();
         board.value[firstMoveIndex] = null;
     }
     // 记录本次落子
     moveHistory.value.push(index);
     // 填充数组落子
     board.value[index] = currentPlayer.value;
     // 检查是否获胜
     winner.value = checkWinner(board.value);
     // 检查是否平局
     if (!winner.value && !board.value.includes(null)) {
         alert('平局!');
     }
     // 落完子后,玩家切换,变为另一位
     currentPlayer.value = currentPlayer.value === 'X' ? 'O' : 'X';
 };

当对局进行到第七次落子的时候,我们并不清楚即将消除的是哪个棋子,需要将其标记红棋子,以示提醒

我们直接来看下,完整的代码把

代码语言:javascript代码运行次数:0运行复制
 <!DOCTYPE html>
 <html lang="zh">
 ​
 <head>
     <meta charset="UTF-8">
     <title>井字棋</title>
     <style>
         .tic-tac-toe {
             text-align: center;
             display: flex;
             flex-direction: column;
             align-items: center;
         }
         .board {
             display: grid;
             grid-template-columns: repeat(, 100px);
             grid-gap: 5px;
         }
         .cell {
             width: 100px;
             height: 100px;
             border: 1px solid #000;
             display: flex;
             align-items: center;
             justify-content: center;
             font-size: 2em;
             cursor: pointer;
         }
         .cell.removing {
             color: red;
         }
         .winner {
             color: green;
             margin-top: 20px;
         }
         .resetGame {
             margin: 20px;
         }
     </style>
 </head>
 ​
 <body>
     <div id="app" v-cloak>
         <div class="tic-tac-toe">
             <h1>井字棋</h1>
             <div class="board">
                 <div v-for="(cell, index) in board" :key="index" class="cell" :class="{ removing: isRemoving(index) }" @click="makeMove(index)">
                     {{ cell }}
                 </div>
             </div>
             <div v-if="winner" class="winner">
                 {{ winner }} wins!
             </div>
             <button @click="resetGame" class="resetGame">Reset Game</button>
         </div>
     </div>
 ​
     <script src="js/vue@..0.js"></script>
     <script>
         ct { createApp, ref } = Vue;
 ​
         ct app = ({
             setup() {
                 ct board = ref(Array(9).fill(null));
                 ct currentPlayer = ref('X');
                 ct winner = ref(null);
                 ct moveHistory = ref([]);
 ​
                 ct makeMove = (index) => {
                     if (board.value[index] || winner.value) 
                         return;
                     // 如果是第七次落子,移除第一次落子
                     if (moveHistory.value.length === 6) {
                         ct firstMoveIndex = moveHistory.value.shift();
                         board.value[firstMoveIndex] = null;
                     }
                     // 记录本次落子
                     moveHistory.value.push(index);
                     board.value[index] = currentPlayer.value;
                     winner.value = checkWinner(board.value);
                     if (!winner.value && !board.value.includes(null)) {
                         alert('平局!');
                     }
                     currentPlayer.value = currentPlayer.value === 'X' ? 'O' : 'X';
                 };
 ​
                 ct checkWinner = (board) => {
                     ct lines = [
                         [0, 1, 2],
                         [, 4, 5],
                         [6, 7, 8],
                         [0, , 6],
                         [1, 4, 7],
                         [2, 5, 8],
                         [0, 4, 8],
                         [2, 4, 6],
                     ];
                     for (let line of lines) {
                         ct [a, b, c] = line;
                         if (board[a] && board[a] === board[b] && board[a] === board[c]) {
                             return board[a];
                         }
                     }
                     return null;
                 };
 ​
                 ct resetGame = () => {
                     board.value = Array(9).fill(null);
                     winner.value = null;
                     currentPlayer.value = 'X';
                     moveHistory.value = [];
                 };
 ​
                 ct isRemoving = (index) => {
                     return moveHistory.value.length === 6 && moveHistory.value[0] === index;
                 };
 ​
                 return {
                     board,
                     makeMove,
                     winner,
                     resetGame,
                     isRemoving
                 };
             },
         });
         ("#app");
     </script>
 </body>
 ​
 </html>

再来运行一下,查看下效果

result
四、最后

通过这个案例,可以了解队列可以消除早的落子,这样的一个数据结构,主要就是先进先出;最早的落子,会优先处理

还有一个悔棋的功能,是如何做呢?

这时候要用到一个栈的数据结构,它主要是后进先出,也就是最近的一次落子,会优先处理,用来处理悔棋最合适不过了

#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格

本文地址:http://www.dnpztj.cn/biancheng/1172981.html

相关标签:无
上传时间: 2025-07-21 11:38:47
留言与评论(共有 11 条评论)
本站网友 保险业前景
8分钟前 发表
});还有我们点击九宫格
本站网友 强盗逻辑
23分钟前 发表
column; align-items
本站网友 玉米须的副作用
12分钟前 发表
就给对应的数组赋值即可同时
本站网友 天下3冰心技能加点
6秒前 发表
flex; align-items
本站网友 长治美食
2分钟前 发表
makeMove
本站网友 国债怎么买
15分钟前 发表
要调用makeMove()
本站网友 永安房屋出租
7分钟前 发表
100px; border
本站网友 今天是农历几号
4分钟前 发表
2]
本站网友 鱼泡避孕
30分钟前 发表
5
本站网友 狗钩虫病
2分钟前 发表
我们直接用html+Vue的方式来进行二