<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<style>
body{
font-size: 36px;
}
*{
padding: 0;
margin: 0;
}
.score {
text-align: center;
line-height: 50px;
height: 50px;
}
.buttons {
text-align: center;
}
button{
font-size: 36px;
line-height: 50px;
margin-right: 30px;
padding: 0 30px;
}
div{
text-align: center;
}
</style>
</head>
<body>
<div>
<canvas id="blackWhite"></canvas>
</div> <p class="score">
<!--<span>比分</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-->
<span id="score">黑:白 = 2:2</span>
</p>
<p class="buttons">
<button id="regret">悔棋</button>
<button id="giveUp">认输</button>
<button id="negotiate">求和</button>
</p>
</body> <!--<script src="js/index.js" type="text/javascript" charset="utf-8"></script>-->
<script type="text/javascript">
// 黑白棋 又叫反棋
class Reversi {
constructor (canvasId){
this.canvasId = canvasId;
this.resetData();
} // 重置数据,再来一局
resetData (){
var body = document.documentElement || document.body;
var minWidth = Math.min(body.clientWidth, body.clientHeight);
// 属性
this.pieces = []; // 棋子数组 二位数组[[],[]]
this.recommend = [];
this.rowCount = 8; // 行数
this.colCount = this.rowCount;// 列数
this.cellWidth = minWidth/this.rowCount; //每个格子的宽
this.width = this.rowCount * this.cellWidth; // 棋盘的宽
this.height = this.width; // 棋盘的高
this.R = this.cellWidth * 0.4; // 棋子半径
this.HR = this.cellWidth / 4; // 提示格子的半径 hintRadius
this.HColor = "#e73480"; // 提示棋子的颜色
this.hisStatus = []; // 历史记录 history status
this.noChessCount = 0; // 没棋走次数
this.active = "black"; // 当前走棋方
this.canvas = document.getElementById(this.canvasId); // canvas DOM
this.ctx = this.canvas.getContext("2d"); // canvas环境
this.whiteCount = 2; // 白棋数量
this.blackCount = 2; // 黑棋数量
this.victor = ""; // 胜利方 this.init();
} // 初始化数据
init (){
this.initCanvas();
this.initPiece();
this.recommed();
this.renderUi();
} // 设置棋盘的宽高
initCanvas (){
this.canvas.width = this.width;
this.canvas.height = this.height;
} // 初始化棋子
initPiece (){
var initPieces = [];
for(let i=0;i<this.rowCount;i++){ // 行
initPieces.push([]);
for(let j=0;j<this.colCount;j++){ // 列
initPieces[i].push({value:0,color:""});
}
}
// 初始的时候中间有四个棋子
var center = Math.floor((this.rowCount-1)/2); //
initPieces[center][center].value = 1;
initPieces[center][center].color = "black";
initPieces[center][center+1].value = 1;
initPieces[center][center+1].color = "white";
initPieces[center+1][center].value = 1;
initPieces[center+1][center].color = "white";
initPieces[center+1][center+1].value = 1;
initPieces[center+1][center+1].color = "black";
// console.log(initPieces); this.pieces = this.deepClone(initPieces);
this.hisStatus[0] = this.deepClone(initPieces);
} renderUi(){
//清除之前的画布
this.ctx.clearRect(0,0,this.width,this.height); // 重绘画布
this.drawMap();
this.drawPieces();
this.recommed();
this.drawHint();
} //画一个棋子或一个提示圆点
drawDot(x,y,r,color){
this.ctx.beginPath();
this.ctx.arc(x,y,r,0,2*Math.PI);
this.ctx.closePath(); this.ctx.fillStyle = color;
this.ctx.fill();
} // 画棋盘
drawMap(){
// 背景
this.ctx.beginPath();
this.ctx.rect(0,0,this.width,this.height);
this.ctx.closePath();
this.ctx.fillStyle = "#0099CC";
this.ctx.fill(); // 画横线
this.ctx.beginPath();
for(let i=0;i<this.rowCount;i++){
this.ctx.moveTo(0,this.cellWidth*i);
this.ctx.lineTo(this.cellWidth*this.rowCount,this.cellWidth*i);
}
this.ctx.stroke(); // 画纵线
this.ctx.beginPath();
for(let i=0;i<this.colCount;i++){
this.ctx.moveTo(this.cellWidth*i,0);
this.ctx.lineTo(this.cellWidth*i,this.cellWidth*this.colCount);
}
this.ctx.stroke();
} // 画所有的棋子
drawPieces(){
for(let i=0;i<this.pieces.length;i++){
for(let j=0;j<this.pieces[i].length;j++){
if(this.pieces[i][j].value){
var x = i * this.cellWidth + this.cellWidth/2;
var y = j * this.cellWidth + this.cellWidth/2;
this.drawDot(x,y,this.R,this.pieces[i][j].color);
}
}
}
} // 画所有的提示
drawHint(){
for(let i=0;i<this.recommend.length;i++){
var x = this.recommend[i].x * this.cellWidth + this.cellWidth/2;
var y = this.recommend[i].y * this.cellWidth + this.cellWidth/2;
this.drawDot(x,y,this.HR, this.HColor);
}
} // 是否可以走这一步
canGo (xx,yy){// 条件 1.双方无期可走 2.棋盘下满了 3.没有本色的棋子了
var flag = false;
for(var i=0;i<this.recommend.length;i++){
if(this.recommend[i].x == xx && this.recommend[i].y == yy){
flag = true;
break;
}
}
return flag;
} // 吃子
fire (x,y){
for(var i=0;i<this.recommend.length;i++){
if(this.recommend[i].x == x && this.recommend[i].y == y){ // 走推荐位置(吃掉可以吃的各个方向)
// console.log("x:"+this.recommend[i].x+";y:"+this.recommend[i].y,"匹配位置");
var xx =x, yy = y;
switch(this.recommend[i].direction){ //上加下减y 左加右减x
case "上": // j在下(大) yy在上(小) xx不变
while(++yy < this.recommend[i].j){
this.pieces[xx][yy].color = this.active;
}
break;
case "右":// x在左(小) xx在右(大) yy不变
while(--xx > this.recommend[i].i){
this.pieces[xx][yy].color = this.active;
}
break;
case "下":// j在上(小) yy在下(大) xx不变
while(--yy > this.recommend[i].j){
this.pieces[xx][yy].color = this.active;
}
break;
case "左":// i在左(小) yy在右(大) i不变
while(++xx < this.recommend[i].i){
this.pieces[xx][yy].color = this.active;
}
break;
case "右上":
while(--xx > this.recommend[i].i){
this.pieces[xx][++yy].color = this.active;
}
break;
case "右下":
while(--xx > this.recommend[i].i){
this.pieces[xx][--yy].color = this.active;
}
break;
case "左下":
while(++xx < this.recommend[i].i){
this.pieces[xx][--yy].color = this.active;
}
break;
case "左上":
while(++xx < this.recommend[i].i){
this.pieces[xx][++yy].color = this.active;
}
break;
default: console.log("jjle,吃不了子");
}
}
}
} // 显示可以走的位置
recommed (){
this.recommend = [];
for(var i=0;i<this.rowCount;i++){
for(var j=0;j<this.colCount;j++){
if(this.pieces[i][j].color == this.active){ // 轮到当前方走棋
// console.log("当前棋子颜色:"+active+";x:"+i+";y:"+j);
// 判断8个方向上是否可以走
// 右 1右测第一个是敌方棋子,2然后排查后面的每一个但不越界,3遇到己方或者空结束(己方的不可以走,空可以走,敌方的继续排查)
if(this.pieces[i+1] && this.pieces[i+1][j].color && this.pieces[i+1][j].color != this.active){ // 有对方的棋才可以走
for(var a=2;i+a<this.rowCount;a++){
if(this.pieces[i+a][j].color == ""){ // 空
this.recommend.push({x:i+a,y:j,i:i,j:j,direction:"右"});
// console.log(i+a,j,"右");
break;
}else if(this.pieces[i+a][j].color == this.active){ // 己方
break;
}else{ // 敌方
continue;
}
}
} // 左
if(this.pieces[i-1] && this.pieces[i-1][j].color && this.pieces[i-1][j].color != this.active){ // 有对方的棋才可以走
for(var a=2;i-a>=0;a++){
if(this.pieces[i-a][j] && this.pieces[i-a][j].color){ // 有棋子 判断是否是敌方棋子
if(this.pieces[i-a][j].color != this.active){
continue;
}else{
break;
}
}else{ //没有棋子 可以走
this.recommend.push({x:i-a,y:j,i:i,j:j,direction:"左"});
// console.log(i-a,j,"左")
break;
}
}
} // 上
if(this.pieces[i][j-1] && this.pieces[i][j-1].color && this.pieces[i][j-1].color != this.active){ // 有对方的棋才可以走
for(var a=2;j-a>=0;a++){
if(this.pieces[i][j-a] && this.pieces[i][j-a].color){ // 有棋子 判断是否是敌方棋子
if(this.pieces[i][j-a].color != this.active){
continue;
}else{
break;
}
}else{ //没有棋子 可以走
this.recommend.push({x:i,y:j-a,i:i,j:j,direction:"上"});
// console.log(i,j-a,"上")
break;
}
}
} // 下
if(this.pieces[i][j+1] && this.pieces[i][j+1].color && this.pieces[i][j+1].color != this.active){ // 有对方的棋才可以走
for(var a=2;j+a<this.rowCount;a++){
if(this.pieces[i][j+a].color){ // 有棋子 判断是否是敌方棋子
if(this.pieces[i][j+a].color != this.active){
continue;
}else{
break;
}
}else{ //没有棋子 可以走
this.recommend.push({x:i,y:j+a,i:i,j:j,direction:"下"});
// console.log(i,j+a,"下")
break;
}
}
} // 右下
if(this.pieces[i+1] && this.pieces[i+1][j+1] && this.pieces[i+1][j+1].color && this.pieces[i+1][j+1].color != this.active){ // 有对方的棋才可以走
for(var a=2;a+j<this.rowCount && a+i<this.rowCount;a++){
if(this.pieces[i+a][j+a].color){ // 有棋子 判断是否是敌方棋子
if(this.pieces[i+a][j+a].color != this.active){
continue;
}else{
break;
}
}else{ //没有棋子 可以走
this.recommend.push({x:i+a,y:j+a,i:i,j:j,direction:"右下"});
// console.log(i+a,j+a,"右下")
break;
}
}
} // 右上
if(this.pieces[i+1] && this.pieces[i+1][j-1] && this.pieces[i+1][j-1].color && this.pieces[i+1][j-1].color != this.active){ // 有对方的棋才可以走
for(var a=2;i+a<this.rowCount && j-a>=0;a++){
if(this.pieces[i+a][j-a].color){ // 有棋子 判断是否是敌方棋子
if(this.pieces[i+a][j-a].color != this.active){
continue;
}else{
break;
}
}else{ //没有棋子 可以走
this.recommend.push({x:i+a,y:j-a,i:i,j:j,direction:"右上"});
// console.log(i+a,j-a,"右上")
break;
}
}
} // 左上
if(this.pieces[i-1] && this.pieces[i-1][j-1] && this.pieces[i-1][j-1].color && this.pieces[i-1][j-1].color != this.active){ // 有对方的棋才可以走
for(var a=2;i-a>=0 && j-a>=0;a++){
if(this.pieces[i-a][j-a].color){ // 有棋子 判断是否是敌方棋子
if(this.pieces[i-a][j-a].color != this.active){
continue;
}else{
break;
}
}else{ //没有棋子 可以走
this.recommend.push({x:i-a,y:j-a,i:i,j:j,direction:"左上"});
// console.log(i-a,j-a,"左上")
break;
}
}
} // 左下
if(this.pieces[i-1] && this.pieces[i-1][j+1] && this.pieces[i-1][j+1].color && this.pieces[i-1][j+1].color != this.active){ // 有对方的棋才可以走
for(var a=2;i-a>=0 && j+a<this.rowCount;a++){
if(this.pieces[i-a][j+a].color){ // 有棋子 判断是否是敌方棋子
if(this.pieces[i-a][j+a].color != this.active){
continue;
}else{
break;
}
}else{ //没有棋子 可以走
this.recommend.push({x:i-a,y:j+a,i:i,j:j,direction:"左下"});
// console.log(i-a,j+a,"左下")
break;
}
}
} }else{ // 没有棋子或不是当前方
continue;
}
}
}
console.log(this.recommend,"推荐位置数组");
if(this.recommend.length==0){
var piecesCount = this.getNum(this.active)+this.getNum(this.active == "black" ? "white" : "black");
if(piecesCount<this.rowCount*this.colCount){
this.noChess++;
if(this.noChess<=64-piecesCount){ // 顶多连走 64-棋子数
this.active = this.active == "black" ? "white" : "black";
// console.log(this.active+"没有棋走,另一方继续");
alert(this.active+"没有棋走,另一方继续");
this.renderUi();
}else{
this.noChess=0;
this.over();
}
}else{ // 下满了
this.over();
}
}
} // 游戏结束 判断输赢、再来一局
over (){
switch (this.winner()){
case "white":
// console.log("恭喜恭喜","白棋赢");
alert("白棋赢");
break;
case "black":
// console.log("恭喜恭喜","黑棋赢");
alert("黑棋赢");
break;
default:
// console.log("恭喜恭喜","和棋");
alert("和棋");
}
} // 走一步 1.判断这一步是否可以走 2.改变路途的棋子颜色
goStep (x,y){
// this.recommed();
if(this.canGo(x,y)){
this.pieces[x][y].value = 1;
this.pieces[x][y].color = this.active; this.fire(x,y); // 这里有个大坑
// var curPieces = Object.assign([],this.pieces);
// this.hisStatus.push(Object.assign([],curPieces));
this.hisStatus.push(this.deepClone(this.pieces));
// console.log(this.hisStatus,"棋局") // 轮流走棋
this.active = this.active == "black" ? "white" : "black"; this.renderUi(); this.whiteCount = this.getNum ("white");
this.blackCount = this.getNum ("black");
}else{
// console.log("别捣乱,好好走");
alert("别捣乱,好好走");
}
} // 深拷贝
deepClone (values) {
var copy; // Handle the 3 simple types, and null or undefined
if (null == values || "object" != typeof values) return values; // Handle Date
if (values instanceof Date) {
copy = new Date();
copy.setTime(values.getTime());
return copy;
} // Handle Array
if (values instanceof Array) {
copy = [];
for (var i = 0, len = values.length; i < len; i++) {
copy[i] = this.deepClone(values[i]);
}
return copy;
} // Handle Object
if (values instanceof Object) {
copy = {};
for (var attr in values) {
if (values.hasOwnProperty(attr)) copy[attr] = this.deepClone(values[attr]);
}
return copy;
} throw new Error("Unable to copy values! Its type isn't supported.");
} // 判断谁赢winner
winner (){
if(this.getNum("white") > this.getNum("black")){
this.victor = "white";
return "white";
} else if(this.getNum("white") < this.getNum("black")){
this.victor = "black";
return "black";
} else {
this.victor = "和棋";
return "和棋";
}
} // 白棋或黑棋的数量
getNum (color){
var count = 0;
for(var i=0,len=this.pieces.length;i<len;i++){
for(var j=0,len1=this.pieces[i].length;j<len1;j++){
if(color == this.pieces[i][j].color){
count++;
}
}
}
return count;
} // return 赢家
getVictor (){
switch(this.victor){
case "white":
alert("白棋胜");
break;
case "black":
alert("黑棋胜");
break;
case "和棋":
alert("和棋");
break;
default: alert("出bug了");
}
} // 认输
giveUp (){
this.victor = this.active == "black" ? "white" : "black";
this.getVictor();
this.resetData();
} // 求和
negotiate (){
this.victor = "和棋";
this.getVictor();
this.resetData();
} // 悔棋
regret (){
if(this.hisStatus.length>1){
this.hisStatus.pop();
// console.log(this.hisStatus,"删除一步的棋局");
this.pieces = Object.assign([],this.hisStatus[this.hisStatus.length-1]);
//[...this.hisStatus[this.hisStatus.length-1]];
this.active = this.active == "black" ? "white" : "black";
// console.log(this.pieces,"当前棋局");
this.renderUi();
this.whiteCount = this.getNum ("white");
this.blackCount = this.getNum ("black");
}else {
alert("没有棋可以悔了");
} }
} </script>
<script type="text/javascript">
var score = document.getElementById("score");
var regret = document.getElementById("regret");
var giveUp = document.getElementById("giveUp");
var negotiate = document.getElementById("negotiate");
var offset;
var reversi = new Reversi("blackWhite");
reversi.canvas.addEventListener("click",function(e){
offset = reversi.canvas.getBoundingClientRect();
var x = Math.floor((e.clientX - offset.left) / reversi.cellWidth);
var y = Math.floor((e.clientY - offset.top) / reversi.cellWidth);
console.log(x,y,"点击位置");
// 走棋
reversi.goStep(x,y); getScore();
},false); function getScore(){
score.innerHTML = "黑:白 = "+reversi.blackCount + ":"+reversi.whiteCount;
} giveUp.onclick = function(){
if(confirm("你确定认输吗?")){
reversi.giveUp();
getScore();
}
} negotiate.onclick = function (){
if(confirm("你确定求和吗?")){
if(confirm("你同意和棋吗?")){
reversi.negotiate();
getScore();
}else{
alert("对方拒绝和棋!");
}
}
} regret.onclick = function(){
if(confirm("对方请求悔棋,是否同意?")){
reversi.regret();
getScore();
}else{
alert("对方拒绝悔棋");
}
}
</script>
</html>

js+canvas黑白棋的更多相关文章

  1. [JS,Canvas]日历时钟

    [JS,Canvas]日历时钟 Html: <!doctype html> <html> <head> <meta charset="UTF-8&q ...

  2. 用Dart写的黑白棋游戏

    2013年11月,Dart语言1.0稳定版SDK发布,普天同庆.从此,网页编程不再纠结了. 在我看来,Dart语法简直就是C#的升级版,太像了.之所以喜欢Ruby的一个重要理由是支持mixin功能,而 ...

  3. 【微信小程序项目实践总结】30分钟从陌生到熟悉 web app 、native app、hybrid app比较 30分钟ES6从陌生到熟悉 【原创】浅谈内存泄露 HTML5 五子棋 - JS/Canvas 游戏 meta 详解,html5 meta 标签日常设置 C#中回滚TransactionScope的使用方法和原理

    [微信小程序项目实践总结]30分钟从陌生到熟悉 前言 我们之前对小程序做了基本学习: 1. 微信小程序开发07-列表页面怎么做 2. 微信小程序开发06-一个业务页面的完成 3. 微信小程序开发05- ...

  4. python3+tkinter实现的黑白棋,代码完整 100%能运行

    今天分享给大家的是采用Python3+tkinter制作而成的小项目--黑白棋 tkinter是Python内置的图形化模块,简单易用,一般的小型UI程序可以快速用它实现,具体的tkinter相关知识 ...

  5. [CareerCup] 8.8 Othello Game 黑白棋游戏

    8.8 Othello is played as follows: Each Othello piece is white on one side and black on the other. Wh ...

  6. 利用js+canvas实现的时钟效果图

    canvas+js时钟特效 运用js+canvas方面的知识完成一个时钟的效果图,再利用for循环实现指针的转动效果: <!--网页文档的声明--> <!doctype html&g ...

  7. 黑白棋游戏 (codevs 2743)题解

    [问题描述] 黑白棋游戏的棋盘由4×4方格阵列构成.棋盘的每一方格中放有1枚棋子,共有8枚白棋子和8枚黑棋子.这16枚棋子的每一种放置方案都构成一个游戏状态.在棋盘上拥有1条公共边的2个方格称为相邻方 ...

  8. bzoj 2281 [Sdoi2011]黑白棋(博弈+组合计数)

    黑白棋(game) [问题描述] 小A和小B又想到了一个新的游戏. 这个游戏是在一个1*n的棋盘上进行的,棋盘上有k个棋子,一半是黑色,一半是白色. 最左边是白色棋子,最右边是黑色棋子,相邻的棋子颜色 ...

  9. C#黑白棋制作~

    前些天自己复习一下C#语言 做了个黑白棋,望大家看一下,可能有些bug嘿嘿 链接如下 http://files.cnblogs.com/files/flyingjun/%E9%BB%91%E7%99% ...

随机推荐

  1. 前端路由的实现(三) —— History的pushState和replaceState用法

    HTML5中history提供的pushState, replaceState这两个API.它们提供了操作浏览器历史栈的方法. pushState能够在不加载页面的情况下改变浏览器的URL.这个方法接 ...

  2. CommonJS、requirejs、ES6的对比

    文件路径 首先先搞清楚文件路径的写法,这里我总是记不住,有点晕,正好这次整理一下. 以 / 为起始,表示从根目录开始解析: 以 ./ 为起始,表示从当前目录开始解析: 以 ../ 为起始,表示从上级目 ...

  3. web前端学习(二)html学习笔记部分(11)-- 没有标号记录的知识合集

    这一部分内容相对比较简单,就不按规矩排序了.(主要是网站上也没有这一部分内容的排序) 1.  html5的 非主体结构元素 学习笔记(1)里面记录过. 2.  html5表单提交和PHP环境搭建 1. ...

  4. 【笔记】http1.1支持的7种请求方法

    本文是本人复习http协议整理笔记,以备后续查阅. http1.1支持的7种请求方法:get.post.head.options.put.delete.trace 在internet应用中,最常用的请 ...

  5. 磁力搜索嗅探器装成BT

    磁力搜索嗅探器装成BT ague-dht ague-dht 是一个磁力链接嗅探器,它伪装成BT下载客服端,加入DHT网络,嗅探磁力链接.每秒发送1000条请求时,平均3秒收到1次带有infohash的 ...

  6. 理解async和await

    async 是“异步”的简写,而 await 可以认为是 async wait 的简写. 所以应该很好理解 async 用于申明一个 function 是异步的,而 await 用于等待一个异步方法执 ...

  7. Bnd教程(1):如何用命令行安装bnd

    1. 如果你用的是MacOS,请运行: brew install bnd 然后运行bnd version看是否安装成功: $ bnd version 4.0.0.201801191620-SNAPSH ...

  8. SPSS统计分析过程包括描述性统计、均值比较、一般线性模型、相关分析、回归分析、对数线性模型、聚类分析、数据简化、生存分析、时间序列分析、多重响应等几大类

    https://www.zhihu.com/topic/19582125/top-answershttps://wenku.baidu.com/search?word=spss&ie=utf- ...

  9. C++中字符串的长度

    定义一个字符串,求其长度: string str; str.length(); str.size();

  10. js多图上传展示和删除

    html部分 <button class="btn btn-info" for="file">请选择文件</button> <in ...