js+canvas 人机井字棋
程序员文章站
2022-05-19 15:02:24
...
<html>
<head>
<meta charset='utf-8'>
<style>
#again {
font-size: 18px;
font-family: 华文细黑;
letter-spacing: 5px;
padding-left: 12px;
border-radius: 5px;
background: -webkit-linear-gradient(top, #66B5E6, #2e88c0);
background: -moz-linear-gradient(top, #66B5E6, #2e88c0);
background: linear-gradient(top, #66B5E6, #2e88c0);
background: -ms-linear-gradient(top, #66B5E6, #2e88c0);
border: 1px solid #2576A8;
box-shadow: 0 1px 2px #B8DCF1 inset, 0 -1px 0 #316F96 inset;
color: #fff;
text-shadow: 1px 1px 0.5px #22629B;
cursor:pointer;
position:absolute;
}
#again:hover {
background: -webkit-linear-gradient(top, #8DC9EF, #4E9FD1);
background: -moz-linear-gradient(top, #8DC9EF, #4E9FD1);
background: linear-gradient(top, #8DC9EF, #4E9FD1);
background: -ms-linear-gradient(top, #8DC9EF, #4E9FD1);
}
</style>
</head>
<body>
<canvas id='canvas' style="position:absolute" ></canvas>
<div onclick="location.reload()" style='width: 105px;height: 30px;'id="again" hidden>再来一局</div>
</body>
<script>
window.onload = function(){
canvas.init();
chessBoard.draw();
player.listen();
};
var game ={
OVER:0,//游戏结束
PLAYER_TURN:1,//轮到玩家
COMPUTER_TURN:2,//轮到电脑
status:1,//当前状态
PLAYER_COUNT:1,
COMPUTER_COUNT:-1,
turn:function(){
this.judge();
if(this.status===this.OVER){
canvas.body.onclick ='';
button.body.style.display='block';
}else if(this.status ===this.PLAYER_TURN){
this.status = this.COMPUTER_TURN;
computer.move();
}else{
this.status = this.PLAYER_TURN;
}
},
judge:function(){
var equal =true;
//检查平局
for(var i=0;i<3;i++){
for(var j=0;j<3;j++){
if(chessBoard.box[i][j]===0){
equal = false;
}
}
}
if(equal){
alert("双方战平");
this.status = this.OVER;
}else {
for (var i = 0; i < 3; i++) {
this.check(chessBoard.box[0][i], chessBoard.box[1][i], chessBoard.box[2][i]);
if(this.status===this.OVER) return;
this.check(chessBoard.box[i][0], chessBoard.box[i][1], chessBoard.box[i][2]);
if(this.status===this.OVER) return;
}
this.check(chessBoard.box[0][0], chessBoard.box[1][1], chessBoard.box[2][2]);
if(this.status===this.OVER) return;
this.check(chessBoard.box[2][0], chessBoard.box[1][1], chessBoard.box[0][2]);
if(this.status===this.OVER) return;
}
},
check:function(v1,v2,v3){
var val = this.sum(v1,v2,v3);
if(val === game.PLAYER_COUNT*3){
alert("玩家胜");
this.status = this.OVER;
}else if(val === game.COMPUTER_COUNT*3){
alert("电脑胜");
this.status = this.OVER;
}
},
sum:function(v1,v2,v3){
return v1+v2+v3;
},
move:function(x,y){
chessBoard.box[x][y] = (this.status===this.PLAYER_TURN)?this.PLAYER_COUNT:this.COMPUTER_COUNT;
chess.draw(x,y);
game.turn();
}
};
var player ={
listen:function(){
canvas.body.onclick = function(e){
if(game.status === game.COMPUTER_TURN){
return;
}
player.move(e);
}
},
move:function(e){
var x = parseInt(e.offsetX/(chessBoard.width/3));
var y = parseInt(e.offsetY/(chessBoard.width/3));
if(chessBoard.box[x][y]===0) {
game.move(x, y);
}
}
};
var computer ={
mayLose:{x:3,y:3},
emptyPos:{x:3,y:3},
horizon:0,
vertical:1,
diagonal1:2,
diagonal2:3,
move:function(){
this.think();
if(game.status === game.OVER){
return;
}
if(this.mayLose.x!==3){
game.move(this.mayLose.x,this.mayLose.y);
}else{
var x =1,y=1;
while(chessBoard.box[x][y]!==0){
x = parseInt(Math.random()*11)%3;
y = parseInt(Math.random()*11)%3;
}
game.move(x,y);
}
},
//判断顺序:己方能不能赢,对方能不能赢
think:function(){
this.mayLose = {x:3,y:3};
this.emptyPos ={x:3,y:3};
for(var i=0;i<3;i++){
if(this.read(i,this.horizon)||
this.read(i,this.vertical)){
return;
};
}
if(this.read('',this.diagonal1)||
this.read('',this.diagonal2)){
return;
}
},
read:function(i,type){
var sum =0;
for(var j=0;j<3;j++){
var pos = this.getPosByType(type,i,j);
sum +=chessBoard.box[pos.x][pos.y];
if(chessBoard.box[pos.x][pos.y]===0){
this.emptyPos.x = pos.x;
this.emptyPos.y = pos.y;
}
}
return this.check(sum);
},
getPosByType:function(type,i,j){
switch(type) {
case this.horizon:
return {x:i,y:j};
case this.vertical:
return {x:j,y:i};
case this.diagonal1:
return {x:j,y:j};
case this.diagonal2:
return {x:j,y:2-j}
}
},
check:function(val){
if(val===game.COMPUTER_COUNT*2){
game.move(this.emptyPos.x,this.emptyPos.y);
return true;
}else if(val===game.PLAYER_COUNT*2){
this.mayLose.x = this.emptyPos.x;
this.mayLose.y = this.emptyPos.y;
}
return false;
}
};
var chessBoard = {
margin:100,
width:600,
box:[[0,0,0],[0,0,0],[0,0,0]],
draw:function(){
//绘制棋面
drawRect(0,0,chessBoard.width,chessBoard.width,'#e6e61a',1,'#020202');
//绘制格子
canvas.cxt.beginPath();
for(var i=1;i<3;i++){
//绘制纵线
canvas.cxt.moveTo(chessBoard.width/3*i,0);
canvas.cxt.lineTo(chessBoard.width/3*i,chessBoard.width);
//绘制横线
canvas.cxt.moveTo(0,chessBoard.width/3*i);
canvas.cxt.lineTo(chessBoard.width,chessBoard.width/3*i);
}
canvas.cxt.stroke();
}
};
var chess = {
margin:10,
draw:function(x,y){
canvas.cxt.beginPath();
canvas.cxt.arc(
chess.getCenterPos(x), //圆心的x坐标
chess.getCenterPos(y), //圆心的y坐标
(chessBoard.width/3-chess.margin*2)/2//圆半径
, 0, 2 * Math.PI);
canvas.cxt.closePath();
//渐变
var gradient = canvas.cxt.createRadialGradient(
chess.getCenterPos(x)+5, //圆心的x坐标
chess.getCenterPos(y)-5, //圆心的y坐标
(chessBoard.width/3-chess.margin*2)/2, //圆半径
chess.getCenterPos(x)+5,
chess.getCenterPos(y)-5, 0);
if(game.status === game.PLAYER_TURN){
gradient.addColorStop(0,'#0a0a0a');
gradient.addColorStop(1,'#636766');
}else{
gradient.addColorStop(0,'#d1d1d1');
gradient.addColorStop(1,'#f9f9f9');
}
canvas.cxt.fillStyle = gradient;
canvas.cxt.fill();
},
//得到圆心的位置;
getCenterPos:function(arg){
return chessBoard.width/3*arg+chessBoard.width/6;
}
};
var canvas ={
body:'',
cxt:'',
left:650,
top:100,
init:function(){
this.body = document.getElementById('canvas');
this.body.width = chessBoard.width +chessBoard.margin;
this.body.height = chessBoard.width +chessBoard.margin;
this.body.style.left = this.left;
this.body.style.top = this.top;
this.cxt = this.body.getContext('2d');
button.location();
}
};
var button ={
body:'',
location:function(){
this.body = document.getElementById('again');
this.body.style.left = canvas.left + (chessBoard.margin+chessBoard.width)/2 -105;
this.body.style.top = canvas.top + chessBoard.width +chessBoard.margin;
}
};
//绘制矩形
function drawRect( x, y, width, height, fillColor, borderWidth, borderColor){
canvas.cxt.beginPath();
canvas.cxt.moveTo(x, y);
canvas.cxt.lineTo(x + width, y);
canvas.cxt.lineTo(x + width, y + height);
canvas.cxt.lineTo(x, y + height);
canvas.cxt.lineTo(x, y);
canvas.cxt.closePath();
canvas.cxt.lineWidth = borderWidth;
canvas.cxt.strokeStyle = borderColor;
canvas.cxt.fillStyle = fillColor;
canvas.cxt.fill();
canvas.cxt.stroke();
}
</script>
</html>
主要类:
canvas: 封装canvas的相对位置和初始化。
button: 封装再来一局按钮的相对位置。
chessBoard:封装棋盘相对位置和绘画,保存已落子的情况。
chess: 封装棋子的相对位置和绘画。
game:封装游戏状态(玩家落子、电脑落子、游戏结束);游戏结束的判断及展示。
player:封装鼠标事件监听响应。
computer: 封装电脑的落子判断。
电脑落子基本思想:
先判断自己能不能赢,再判断自己会不会输。再随机选择一个空白位置落子
推荐阅读