原生 Javascript 编写五子棋
原文地址:原生 Javascript 编写五子棋
博客地址:http://www.extlight.com
一、背景
近一个月没写 Javascript 代码,有点生疏。正好浏览网页时弹出五子棋的游戏广告,于是想通过编写这个小游戏练练手。
二、简单介绍
2.1 效果展示

2.2 实现思路
棋盘:通过图片(chessboard.png)和 div 标签渲染出棋盘。
棋子:通过图片(black_flag.png、white_flag.png等)渲染出黑白棋子。落子前,鼠标出会出现一个可以随鼠标移动的棋子。我们创建一个浮动的 div,动态设置其 top 和 left 。
落子:给容器(class="container")添加 click 事件,给其添加对应的 classname。即被点击的单元格设置棋子背景图片。此外,需要判断落子点是否存在棋子。
输赢:使用二维数组保存棋盘(棋子)状态,通过横向、纵向、左上到右下和右上到左下四个方向进行判断是否有 5 个以上连续同颜色(样式)的棋子。
2.3 涉及技术
DOM操作、面向对象、事件操作和间隔函数 setInterval
2.4 项目结构

三、实现步骤
3.1 绘制棋盘
style.css 内容:
html,body {
    padding: 0;
    margin: 0;
}
.container {
    position: relative;
    width: 540px;
    height: 540px;
    margin: 10px auto;
    padding-top: 7px;
    padding-left: 7px;
    background: url("../images/chessboard.png") no-repeat;
    cursor: pointer;
}
.none {
    position: absolute;
    width: 36px;
    height: 36px;
    box-sizing: border-box;
    /*border: 1px solid #fff;*/
}
.black_flag {
    position: absolute;
    width: 36px;
    height: 36px;
    background: url("../images/black_flag.png") no-repeat;
}
.black_flag_cur {
    position: absolute;
    background: url("../images/black_flag_cur.png") no-repeat;
    /*设置点击无效*/
    pointer-events: none;
}
.white_flag {
    position: absolute;
    width: 36px;
    height: 36px;
    background: url("../images/white_flag.png") no-repeat;
}
.white_flag_cur {
    position: absolute;
    background: url("../images/white_flag_cur.png") no-repeat;
    /*设置点击无效*/
    pointer-events: none;
}
chessboard.js 代码:
var Chessboard = function() {
    // 保存棋盘棋子状态
    this.flagArr = [];
    this.size = 36;
}
// 初始化棋盘
Chessboard.prototype.init = function() {
    var container = document.getElementById("container");
    for (var i = 0; i < 15; i++) {
        var arr = [];
        for (var j = 0; j < 15; j++) {
            var div = document.createElement("div");
            div.className = "none";
            div.style.top = (i * this.size) + "px";
            div.style.left = (j * this.size) + "px";
            container.appendChild(div);
            arr.push(div);
        }
        this.flagArr.push(arr);
    }
}
game.js 代码:
var Game = function() {
}
Game.prototype.start = function() {
    var chessboard = new Chessboard();
    chessboard.init();
}
最终效果如下:

为了方便查看 div 与棋盘图片中格子之间的对应关系,笔者将 div 边框设置成白色。
从图中我们可以看到,div 大小正好对应棋盘的落子点。我们将 div 背景设置成棋子图片就实现了落子操作。
3.2 绘制棋子
chessboard.js 代码:
var Chessboard = function() {
     // 保存棋盘棋子状态
    this.flagArr = [];
    this.size = 36;
    // 默认黑色为先手
    this.currentFlag = true;
    // 保存落子前的样式映射
    this.flagCurMap = [];
    // 黑子
    this.flagCurMap[true] = "black_flag_cur";
    // 白子
    this.flagCurMap[false] = "white_flag_cur";
}
// 初始化棋盘
Chessboard.prototype.init = function() {
    var container = document.getElementById("container");
    for (var i = 0; i < 15; i++) {
        var arr = [];
        for (var j = 0; j < 15; j++) {
            var div = document.createElement("div");
            div.className = "none";
            div.style.top = (i * this.size) + "px";
            div.style.left = (j * this.size) + "px";
            container.appendChild(div);
            arr.push(div);
        }
        this.flagArr.push(arr);
    }
    // 添加事件监听器
    this.addListener(container);
}
// 落子事件监听器
Chessboard.prototype.addListener = function() {
    var that = this;
    // 设置落子前的鼠标样式
    var mouse = document.createElement("div");
    mouse.id = "mouse";
    mouse.style.width = mouse.style.height = 36 + "px";
    document.body.appendChild(mouse);
    document.body.onmousemove = function(event) {
        mouse.className = that.flagCurMap[that.currentFlag];
        var x = event.clientX - 16;
        var y = event.clientY - 16;
        mouse.style.top = y + "px";
        mouse.style.left = x + "px";
    }
}
结果如下图:

3.3 落子
在 chessboard.js 的监听器方法中添加落子的点击事件:
var Chessboard = function() {
    // 保存棋盘棋子状态
    this.flagArr = [];
    this.size = 36;
    // 默认黑色为先手
    this.currentFlag = true;
    // 保存落子前的样式映射
    this.flagCurMap = [];
    // 黑子
    this.flagCurMap[true] = "black_flag_cur";
    // 白子
    this.flagCurMap[false] = "white_flag_cur";
    // 保存落子后的样式映射
    this.flagMap = [];
    // 黑子
    this.flagMap[true] = "black_flag";
    // 白子
    this.flagMap[false] = "white_flag";
    // 保存结果映射关系
    this.resultMap = [];
    this.resultMap[true] = "黑子胜利";
    this.resultMap[false] = "白子胜利";
}
// 初始化棋盘
Chessboard.prototype.init = function() {
    var container = document.getElementById("container");
    for (var i = 0; i < 15; i++) {
        var arr = [];
        for (var j = 0; j < 15; j++) {
            var div = document.createElement("div");
            div.className = "none";
            div.style.top = (i * this.size) + "px";
            div.style.left = (j * this.size) + "px";
            container.appendChild(div);
            arr.push(div);
        }
        this.flagArr.push(arr);
    }
    // 添加事件监听器
    this.addListener(container);
}
// 落子事件监听器
Chessboard.prototype.addListener = function(container) {
    var that = this;
    // 设置落子前的鼠标样式
    var mouse = document.createElement("div");
    mouse.id = "mouse";
    mouse.style.width = mouse.style.height = 36 + "px";
    document.body.appendChild(mouse);
    document.body.onmousemove = function(event) {
        mouse.className = that.flagCurMap[that.currentFlag];
        var x = event.clientX - 16;
        var y = event.clientY - 16;
        mouse.style.top = y + "px";
        mouse.style.left = x + "px";
    }
    // 落子监听
    container.onclick = function(event) {
        // 判断落子点是否存在棋子
        if (event.target.className != "none") {
            alert("此处不能落子!");
            return;
        }
        // 落子,设置棋子图片
        event.target.className = that.flagMap[that.currentFlag];
        // 换棋手
        that.currentFlag = !that.currentFlag;
    }
}
运行结果如下:

3.4 判断输赢
在 chessboard.js 的落子监听实践代码中,判断是否五连子:
// 落子事件监听器
Chessboard.prototype.addListener = function(container) {
    var that = this;
    // 设置落子前的鼠标样式
    var mouse = document.createElement("div");
    mouse.id = "mouse";
    mouse.style.width = mouse.style.height = 36 + "px";
    document.body.appendChild(mouse);
    document.body.onmousemove = function(event) {
        mouse.className = that.flagCurMap[that.currentFlag];
        var x = event.clientX - 16;
        var y = event.clientY - 16;
        mouse.style.top = y + "px";
        mouse.style.left = x + "px";
    }
    // 落子监听
    container.onclick = function(event) {
        // 判断落子点是否存在棋子
        if (event.target.className != "none") {
            alert("此处不能落子!");
            return;
        }
        // 落子,设置棋子图片
        event.target.className = that.flagMap[that.currentFlag];
        // 当前落子坐标
        var x = Math.floor(event.target.offsetLeft / that.size);
        var y = Math.floor(event.target.offsetTop / that.size);
        // 判断是否胜利
        if (that._checkSuccess(x, y)) {
            document.getElementById("mouse").style.display = "none";
            container.onclick = null;
            document.body.onmousemove = null;
            alert(that.resultMap[that.currentFlag]);
            return;
        }
        // 换棋手
        that.currentFlag = !that.currentFlag;
    }
}
// 判断棋局
Chessboard.prototype._checkSuccess = function(x, y) {
    var result = false;
    // 当前落子的样式/颜色
    var className = this.flagArr[y][x].className;
    // 横向判断
    var count = 0;
    for (var i = 0; i < 15; i++) {
        if (className == this.flagArr[y][i].className) {
            count++;
            if (count >= 5) {
                return true;
            }
        } else {
            count = 0;
        }
    }
    // 纵向判断
    for (var j = 0; j < 15; j++) {
        if (className == this.flagArr[j][x].className) {
            count++;
            if (count >= 5) {
                return true;
            }
        } else {
            count = 0;
        }
    }
    // 左上到右下判断
    var a = y - x;
    var index = 0;
    if (a > 0) {
        for (a; a < 15; a++) {
            if (className == this.flagArr[a][index++].className) {
                count++;
                if (count >= 5) {
                    return true;
                }
            } else {
                count = 0;
            }
        }
    } else {
        a = Math.abs(a);
        for (a; a < 15; a++) {
            if (className == this.flagArr[index++][a].className) {
                count++;
                if (count >= 5) {
                    return true;
                }
            } else {
                count = 0;
            }
        }
    }
    // 右上到左下判断
    var b = 14 - y -x;
    var index2 = 14;
    if (b > 0) {
        b = 14 - b;
        index2 = 0;
        for (b; b >= 0; b--) {
            if (className == this.flagArr[index2++][b].className) {
                count++;
                if (count >= 5) {
                    return true;
                }
            } else {
                count = 0;
            }
        }
    } else {
        b = Math.abs(b);
        for (b; b < 15; b++) {
            if (className == this.flagArr[index2--][b].className) {
                count++;
                if (count >= 5) {
                    return true;
                }
            } else {
                count = 0;
            }
        }
    }
    if (count >= 5) {
        result = true;
    }
    return result;
}
演示结果:

剩余的一些文本提示,倒计时就不在此处介绍。具体代码可以在下边提供的链接中下载。
四、源码下载
原生 Javascript 编写五子棋的更多相关文章
- canvas :原生javascript编写动态时钟
		
canvas :原生javascript编写动态时钟 此时针是以画布的中心为圆心: g.translate(width/2,width/2); 此函数是将画布的原点移到(width/2,wid ...
 - 原生javascript模仿win8等待进度条。
		
一.序言 一直很中意win8等待提示圆圈进度条.win8刚出来那会,感觉好神奇!苦于当时没思路,没去研究.通过最近网上找找资料,终于给搞出来了!先上Demo,献丑了!预览请看:win8进度条. 二.简 ...
 - 原生javascript写的侧栏跟随效果
		
浏览网站时经常看到有的网站上,当一个页面很长的时候,设定侧栏内容会跟随滚动条滚动,我们把这种效果叫做“侧栏跟随滚动”.这种特效对提高网站浏览量.文章点击率.广告点击量都有一定效果. 侧栏跟随滚动的实现 ...
 - 你可能不需要 jQuery!使用原生 JavaScript 进行开发
		
很多的 JavaScript 开发人员,包括我在内,都很喜欢 jQuery.因为它的简单,因为它有很多丰富的插件可供使用,和其它优秀的工具一样,jQuery 让我们开发人员能够更轻松的开发网站和 We ...
 - artDialog是一个基于javascript编写的对话框组件,它拥有精致的界面与友好的接口
		
artDialog是一个基于javascript编写的对话框组件,它拥有精致的界面与友好的接口 自适应内容 artDialog的特殊UI框架能够适应内容变化,甚至连外部程序动态插入的内容它仍然能自适应 ...
 - 四种常见的提示弹出框(success,warning,error,loading)原生JavaScript和jQuery分别实现
		
原文:四种常见的提示弹出框(success,warning,error,loading)原生JavaScript和jQuery分别实现 虽然说现在官方的自带插件已经有很多了,但是有时候往往不能满足我们 ...
 - JavaScript常用,继承,原生JavaScript实现classList
		
原文链接:http://caibaojian.com/8-javascript-attention.html 基于 Class 的组件最佳实践(Class Based Components) 基于 C ...
 - 原生 JavaScript 实现 state 状态管理系统
		
原生 JavaScript 实现 state 状态管理系统 Build a state management system with vanilla JavaScript | CSS-Tricks 在 ...
 - C#构造方法(函数)   C#方法重载   C#字段和属性  MUI实现上拉加载和下拉刷新  SVN常用功能介绍(二)  SVN常用功能介绍(一)  ASP.NET常用内置对象之——Server  sql server——子查询   C#接口  字符串的本质  AJAX原生JavaScript写法
		
C#构造方法(函数) 一.概括 1.通常创建一个对象的方法如图: 通过 Student tom = new Student(); 创建tom对象,这种创建实例的形式被称为构造方法. 简述:用来初 ...
 
随机推荐
- mongodb安装与权限配置
			
mongodb下载地址:官方下载IDE工具:Robo 3T:官方下载 windows系统要求64位,最低2g内存,推荐8g内存及以上 安装过程没有需要配置的地方,直接下一步到结束,如果想要方便可以在环 ...
 - Opencv+pycharm+anaconda配置
			
最简便的方法,没有之一 首先下载annconda:(不要从官网下,还要FQ,还要慢) https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/ 请注 ...
 - [Android]Adb connection Error:远程主机强迫关闭了一个现有的连接
			
目前,针对Android手机研发的刷机软件和手机助手软件,都会调用Google开发的adb工具与手机进行通信. 有的刷机软件或者手机助手软件,在系统里会常驻一个服务,独占adb的端口(5037),这样 ...
 - maven_01_简介及安装
			
一.简介 Maven主要服务于基于Java平台的项目构建.依赖管理和项目信息管理 何为构建 除了编写源代码,我们每天有相当一部分时间花在了编译.运行单元测试.生成文档.打包和部署等烦琐且不起眼的工作上 ...
 - AES前后端加密
			
1.前端代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...
 - 解决xadmin下设置“use_bootswatch = True”无效的问题
			
环境:python 2.7django 1.9xadmin采用源代码的方式引入到项目中QQ群交流:697028234 1.安装requests pip install requests 2./xadm ...
 - Python3的第一个程序(三)
			
现在,了解了如何启动和退出Python的交互式环境,我们就可以正式开始编写Python代码了. 在写代码之前,请千万不要用“复制”-“粘贴”把代码从页面粘贴到你自己的电脑上.写程序也讲究一个感觉,你需 ...
 - 老鼠走迷宫(2)输出所有路径(C语言)
			
需求 有一个迷宫,在迷宫的某个出口放着一块奶酪.将一只老鼠由某个入口处放进去,它必须穿过迷宫,找到奶酪.请找出它的行走路径. STEP 1 题目转化 我们用一个二维数组来表示迷宫,用2表示迷宫的墙壁, ...
 - 201621123006 《Java程序设计》第2周学习总结
			
1. 本周学习总结 以几个关键词描述本周的学习内容.并将关键词之间的联系描述或绘制出来. 原则:少而精,自己写.即使不超过5行也可,但请一定不要简单的复制粘贴. java数据类型:java数据类型分为 ...
 - C# Http方式下载文件到本地类
			
直接上代码: using System; using System.Collections.Generic; using System.Linq; using System.Text; using S ...