一:介绍
这个项目是做一个人机对战的井字棋,一开始,完全不知道如何下手,不知道该怎么完成这个功能,最后再github上找了个项目跟着看,一看js代码,各种函数,作者也没有什么注释,看着还是头疼,但是没有办法,只有硬着头皮一个个函数的看过去,分析这个函数时做什么的,看着看着突然觉得还是挺简单的~但是大多数的时候我们都被自己吓怕了,所以不管多难,都要尝试去做,说不定就做出来了呢。当然这个作者的js代码只有三百多行,所以也不算多,只是一个小项目~
该项目在codepen上的地址:https://codepen.io/lightforme/full/JJLGwq/
二:知识点
- 1.confirm(message):该方法用于显示一个带有指定消息和OK、取消按钮的对话框;
- 2.window.location:对象用于获得当前页面的地址 (URL),window.location.reload可以将页面重新加载;
- 3.z-index:只有当position为非默认值时,该值才有效,只越大越层叠在上面,并且被覆盖的部分不能被点击到;
- 4.e.preventDefault:取消和事件相关联的默认动作;
- 5.原生js中可以直接重新设置事件方法进行覆盖,但是jQuery中不行,jQuery中需要取消事件方法需要使用.unbind()方法,不加参数是移出所有的事件方法,加参数指定移出事件方法;
- 6.在codepen中window.location对象被禁用了,所以重载页面可以使用location = location;来替代;
三:代码
html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>tictoctie game</title>
<link rel="stylesheet" href="http://cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="css/tictoctie.css" />
</head>
<body>
<div class="container">
<div class="gameboard">
<ul class="grid-list">
<li class="grid grid-0"></li>
<li class="grid grid-1"></li>
<li class="grid grid-2"></li>
<li class="grid grid-3"></li>
<li class="grid grid-4"></li>
<li class="grid grid-5"></li>
<li class="grid grid-6"></li>
<li class="grid grid-7"></li>
<li class="grid grid-8"></li>
</ul>
</div>
</div>
<div class="bg">
</div>
<div class="choose-panel">
<div class="choose-player">
<p>选择角色:X OR O?</p>
<div class="choose-btn">
<button class="btn btn-default xobtn" id="x-btn">X</button><button class="btn btn-default xobtn" id="o-btn">O</button>
</div>
</div>
<div class="choose-first">
<p>设置先手:</p>
<div class="choose-btn">
<button class="btn btn-default firstbtn" id="computer">电脑</button><button class="btn btn-default firstbtn" id="user">玩家</button>
</div>
</div>
</div>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
<script src="js/tictpctie.js"></script>
</body>
</html>
css:
* {
margin: 0px;
padding: 0px;
}
.gameboard {
width: 480px;
height: 450px;
margin-left: auto;
margin-right: auto;
margin-top: 200px;
background-color: #000;
}
.gameboard ul {
list-style: none;
height: 480px;
padding: 15px;
}
.grid {
width: 32.4%;
height: 30.5%;
float: left;
border: 1px solid #E1F5A9;
}
.gameboard li {
cursor: pointer;
color: #F9F9F9;
text-align: center;
line-height: 138px;
font-size: 2em;
}
.grid-0, .grid-3, .grid-6 {
border-left: none;
}
.grid-0, .grid-1, .grid-2 {
border-top: none;
}
.grid-2, .grid-5, .grid-8 {
border-right: none;
}
.grid-6, .grid-7, .grid-8 {
border-bottom: none;
}
.bg {
/*display: none;*/
position: absolute;
top: 0%;
left: 0%;
width: 100%;
height: 100%;
background-color: black;
z-index:1001;
-moz-opacity: 0.7;
opacity:.70;
filter: alpha(opacity=70);
}
.choose-panel {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 560px;
height: 200px;
margin: auto;
background-color: #FFF;
margin-top: 200px;
z-index: 1002;
border-radius: 25px;
}
.choose-panel p {
font-size: 2em;
text-align: center;
padding-top: 30px;
}
.choose-btn {
text-align: center;
}
.btn {
margin: 5px;
padding:5px;
}
.choose-first {
display: none;
}
javascript:
/*game逻辑分析
* 新建一个game的构造函数,并给该构造函数的原型对象prototype添加两个属性:init/play
*
* 1.点击选择角色按钮,选择角色,关闭选择角色面板,打开选择先手面板;
* 2.选择先手,若是电脑先手,调用对象的play()函数;
* 3.设置点击棋盘位置的点击事件,每次点击玩更新棋盘相应处的棋子,并调用play()函数,让电脑落子;
* 4.play()函数逻辑:判断是否下一手即可获胜,若能找到落子位置落子
* 否则,判断下一手player是否将要获胜,若是,找到落子位置落子;
* 否则,判断是否是第一手,若是,落子棋盘正中间;
* 否则,收集棋盘其余未落子的位置,并随机选择落子位置落子
*/
$(function(){
var running = false,
computerVal = -1,
playerVal = 1,
computer,player,
copEnd = false,
winResult = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]],
panel = [0,0,0,0,0,0,0,0,0];
//设置角色
function setPlayer(val){
computer = val === 'O' ? 'X' : 'O';
$(".choose-player").css("display","none");
$(".choose-first").css("display","block");
};
//通过类名获得位置
function getPos(val){
return val.split(" ")[1].split("-")[1];
};
//判断玩家是否胜利
function playerWin(){
for(var i = 0;i <winResult.length;i++){
var winCount = 0;
for(var j = 0;j < winResult[i].length;j++){
if(panel[winResult[i][j]] == 1){
winCount++;
};
};
if(winCount == 3){
return true;
};
};
return false;
};
//判断电脑是否胜利
function computerWin(){
for(var i = 0;i <winResult.length;i++){
var winCount = 0;
for(var j = 0;j < winResult[i].length;j++){
if(panel[winResult[i][j]] == (-1)){
winCount++;
};
};
if(winCount == 3){
return true;
};
};
return false;
};
//判断棋盘是否下满
function end(){
var count = 0;
for(var i = 0;i < panel.length;i++){
if(panel[i] != 0){
count++;
};
};
return (count == 9);
};
//更新棋盘棋子
function updateRole(pos,val){
var targetClass = '.grid-' + pos;
var who = val == 1 ? player : computer;
$(targetClass).html(who);
$(targetClass).unbind("click");
panel[pos] = val;
if(playerWin()){
copEnd = true;
if(confirm("玩家胜利,是否重开一局?")){
window.location.reload();
};
}else if(computerWin()){
copEnd = true;
if(confirm("电脑胜利 ,是否重开一局?")){
window.location.reload();
};
}else if(end()){
copEnd = true;
if(confirm("平局,是否重开一局?")){
window.location.reload();
};
};
};
function compOneEmp(arr){
var empCount = 0;
var compCount = 0;
for(var i = 0;i < arr.length;i++){
if(panel[arr[i]] == 0){
empCount++;
};
if(panel[arr[i]] == (-1)){
compCount++;
};
};
if(compCount == 2){
return (empCount == 1);
};
};
//电脑是否可以攻击
function canAttack(){
for(var i = 0;i < winResult.length;i++){
if(compOneEmp(winResult[i])){
return true;
};
};
return false;
};
function findAttackPos(){
for(var i = 0;i<winResult.length;i++){
if(compOneEmp(winResult[i])){
for(var j=0;j<winResult[i].length;j++){
if(panel[winResult[i][j]] == 0){
return winResult[i][j];
};
};
};
};
return false;
};
//电脑是否需要防守
function canDefend(){
for(var i = 0;i < winResult.length;i++){
if(playOneEmp(winResult[i])){
return true;
};
};
return false;
};
function playOneEmp(arr){
var empCount = 0;
var playCount = 0;
for(var i = 0;i < arr.length;i++){
if(panel[arr[i]] == 0){
empCount++;
};
if(panel[arr[i]] == 1){
playCount++;
};
};
if(playCount == 2){
return (empCount == 1);
};
};
function findDefendPos(){
for(var i = 0;i<winResult.length;i++){
if(playOneEmp(winResult[i])){
for(var j=0;j<winResult[i].length;j++){
if(panel[winResult[i][j]] == 0){
return winResult[i][j];
};
};
};
};
return false;
};
function firstStep(){
return !running;
};
function specialPos(){
if(panel[4] == 0){
return true;
};
return false;
};
function collectEmp(){
var arr = [];
for(var i = 0;i < panel.length;i++){
if(panel[i] == 0){
arr.push(i);
};
};
return arr;
};
//建立构造器函数
var ticToctie = function(){};
//给构造函数原型添加方法
ticToctie.prototype = {
init: function(){
var self = this;
$(".choose-player .btn").click(function(){
player = $(this).html();
setPlayer(player);
});
$(".choose-first .btn").click(function(){
$(".bg").css('display', 'none');
$(".choose-panel").css("display","none");
var whoFirst = $(this).attr("id");
//若不是,将running置为true,防止影响后面电脑落子
if(whoFirst == 'computer'){
self.play();
}else{
running = true;
};
});
$(".grid").click(function(){
var pos = getPos($(this).attr('class'));
updateRole(pos,playerVal);
if(!copEnd){
self.play();
};
//并取消此处可点击
});
},
play: function(){
if(canAttack()){
let pos = findAttackPos();
updateRole(pos,computerVal);
return;
};
if(canDefend()){
let pos = findDefendPos();
updateRole(pos,computerVal);
return;
};
if(firstStep()){
running = true;
updateRole(4,computerVal);
return;
};
if(specialPos()){
updateRole(4,computerVal);
return;
};
var arr = collectEmp();
var pos = Math.floor(Math.random() * arr.length);
updateRole(arr[pos],computerVal);
return;
}
};
//创建一个构造函数实例
var competition = new ticToctie();
competition.init();
});
如果有发现问题或者有好的建议,请在评论中指出