Skip to content
五子棋
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
app
dist
gulp
.gitignore
README.md
gulpfile.js
package.json

README.md

线上demo: https://a044161.github.io/fir/

这次开发中采用的是自己还未完成的一个脚手架,主要是之前为了给自己的side project来弄的一个,但是还没完善,这次就匆忙的使用上了,在使用的过程中也要遇到一些问题,比如:

  • 无法直接饮用通过访问目录的方式直接引入index.js,而是要 import x from 'x/index';
  • 还有一个就是对象的...运算符竟然无法使用

其它的问题暂时没有发现。

运行方式

npm install

npm run dev

运行成功后,访问localhost:3000即可。

功能点

  • 悔棋
  • 重新开始

目录结构

js > fir

用于存放这次的主要代码

|____core // 用于存放核心代码(目前只有计算五子连珠,之前考虑还要用于存放机器人部分)
| |____calculate.js // 用于计算是否五子连珠
|____fir.js // 入口文件
|____modules
| |____dom.js // 操作dom
| |____templates // html模板
| | |____col.js // 列模板
| | |____frame.js // 主结构模板
| | |____index.js // 引用模板的主函数
| | |____point.js // 棋子模板
| | |____row.js // 行模板
| |____utils.js // 用于存放工具类的方法

css

用于存放样式文件,时间仓促,只用了一些基本的样式

思路分析

对思路进行简要的一些分析

页面结构生成

虽然说五子棋盘的规格为 15*15 但是在设计的时候,考虑到可以自行设置棋盘的大小,所以在这部分,都通过使用模板进行生成。

伪模板渲染

一开始的想法是可以加入模板引擎,可以赋值,控制类名,数据绑定等,能够腾出来的时间不多,所以这部分就放弃了,改用es6的模板语法来弄。
创建一个用于存放模板的文件夹templates,里面有个 index.js 文件是用来引入模板,以及通过传递进来的key进行获取模板,渲染模板。


import col from "./col"; // 各模板文件
import row from "./row";
import point from "./point";

import frame from "./frame";

const TPL_OBJ = merge(col, row, point, frame); // ...运算符无法使用,就写了一个用于合并对象的方法

export default (type, data) => {
	if ( // 通过type来查找模板,data是用于传入模板内的参数
		typeof TPL_OBJ[type] === "function" || 
		typeof TPL_OBJ[type] === "string"
	) {
		return TPL_OBJ[type](data);
	}
};

point.js模板文件为例

const point = data => { 
    const style = data.style ? data.style : {}; // 自定义属性
    
    return `<div class="fir__point__block" 
    data-point="[${data.x},${data.y}]" 
    style="width: ${style.width}; height: ${style.height}; left: ${style.left}; top: ${style.top};"></div>`
};

export default {
    point
};

开发人员自己定义相关的数据,来传入到模板类,可以是其它html字符串,或者样式,但是不支持绑定事件,之后再通过函数调用模板

tpl("point", {
    x: current_row, // 横坐标
    y: current_col, // 纵坐标
    style: {
        // 大小以及偏移值
        width: `${PointSize}%`,
        height: `${PointSize}%`,
        top: `${current_top}%`,
        left: `${current_left}%`
    }
});

返回值为html字符串,还需要通过innerHTML方式写入到页面中。
这大概就是这次模板渲染的方式,一些细节的代码在fir.js中的createRow() createCol() createPoint()中。

下棋思路

其实一开始纠结该如何放置棋子的问题思考了好久,最终还是向页面中的每个交点都放置一个dom元素做了妥协,没有想到其他的可以减少dom元素的方式,想通过样式来解决,但是无法解决点击位置的问题。 通过定义了两个数组来辅助下棋

firBoard = []; // 计算五子连珠的数组
squence = []; // 记录下棋的顺序
生成棋子

先生成棋子,每个棋子的dom中用了data的属性,增加了一个data-point 用于标记棋子的坐标,在纠结是用从0开始计算还是1开始计算时,选择了1,因为棋盘中的计算方式是从1开始。这时在放置棋子的外围容器上,我注册了一个点击事件

on(firPointWrapper, "click", this.handleChessClick.bind(this));

同时将下棋的方法又剥离出来,可以用于之后机器人下棋时进行直接调用,handleChessClick其实包含了下棋的函数。
当点击时,先去获取坐标所对应在计算五子连珠数组中的索引值,然后再对棋子添加样式,这是再去调用计算方法 calculate.count去计算是否有五子连珠,并往下棋队列中添加棋子信息。
到这里为止就会生成棋子了,一个是数据,一个是页面,其实页面上就是增加一个类名而已。

计算方法

分为三种计算方式:横、竖、对角线。其实横、竖比较类型,我也提取了公用的方法出来,对角线的比较麻烦。横、竖各需要按照两个方向来计算,对角线则要四个方向,但是两个两个是一对。
我还写了一个方法,用来获取坐标所对应计算五子连珠数组的索引值getPointIndex,当下了一个棋子后,则在数组相应的位置赋上值,之前索引没有值的话,就会被自动填充为undefined,比较麻烦的就是要考虑遇到其中断开的,其实就是遇到undefined时的计算,我所做的处理就是,如果遇到了undefined这个方向上之后我就不去计算了。
核心就是,先找到各个方向上,下个点的索引值,以及边界问题,获取两个端点的坐标。通过坐标和数组索引的方式就能很快的进行计算了,因为每个坐标都对应了一个索引值,我只要找了下一个点的索引值,我就能得到值,将值进行相加,大于4或者小于-4则说明五子连珠了。具体的实现在calculate.js中。

悔棋和重新开始

这两个点比较简单。
悔棋是依托之前定义的下棋序列的数组,只要获取最后一个数组元素就好,里面可以取到对应的坐标和值,通过坐标获取索引,然后再计算五子连珠的数组中置为undefined即可,再通过元素选择器,里面已经有坐标信息了,所以[data-point="[${pre_step.point}]"]就能获取到了,再移除样式就好。
重新开始就是将各个数据还原就好

其它

在项目中我写了两个减少工作量的js,一个是操作dom元素的dom.js,还有一个是包含工具的utils.js
dom.js里面包含了:

  • 获取dom元素

  • 增加/删除类名

  • 增加/移除事件监听器

  • 获取dataset

utils.js里面包含了:

  • 判断是否为对象

  • 判断是否为数组

  • 判断是否为undefined

  • 判断是否为null

  • 合并对象

这次也是想到说已插件的形式来开发,可以根据挂载的元素,来生成棋盘,这样可以在很多地方调用,所以在页面中是这样调用的


new FIR({
    id: 'FIR' // 元素id
    size: 14 // 棋盘大小
})

难点

  • 棋盘布局上,这次都采用了百分比的计算方式,因为能够适应多种场景比如外容器的宽高,棋子的数量,以及没想到办法来解决dom元素数量的方式。

  • 模板使用上一直没有达到最理想的状态,通过数据绑定的方式来渲染模板,最终只能选用了开发时间最短的一种方式来编写模板,因为这样才能根据自己的需要来生成相对应的模板,比如不同棋子数量时,棋子的元素数量也不同,网格的数量也不同。

  • 计算五子连珠,这应该是最麻烦的部分,因为可能性太多,要考虑的也比较多,先是进行纸上的模拟找规律,之后再考虑计算方式上有没有办法优化,在计算方式上面的优化是想到,3个方式的计算,我要尽可能的减少循环数组的次数,于是我都把横、竖计算的for调整为了一个,次数为5次,每次循环都计算一个方向上两个不同维度的值,只有对角线的比较麻烦,因为要计算边界的情况,所以多了一个for循环。

  • 采用索引和坐标的关系来保证数据的利用率,其实在其他框架中,比较好的一个想法就是用数据驱动模型,这次在开发过程中也一直想要往这方向上靠拢,保证依托数据来完成相对应的操作。就像通过坐标来查找数组索引,通过坐标来控制样式等。

小遗憾

没有写一个watch来监听数据的变化,再反应到样式中。以及样式、交互没有优化。

结束语

每天晚上花了一些时间来弄,上班的时候也有在思考一些思路,都尽可能的从简洁,代价低的方向上去思考,整个完成时间大概是3-4个晚上,样式和交互没有时间做优化了,这次的重心还是放在了开发逻辑功能上。

You can’t perform that action at this time.