我博客上的围棋js程序
版权申明:本文为博主窗户(Colin Cai)原创,欢迎转帖。如要转贴,必须注明原文网址 http://www.cnblogs.com/Colin-Cai/p/8506691.html 作者:窗户 QQ:6679072 E-mail:6679072@qq.com
作为一个围棋爱好者,就决定在博客里加个围棋js程序。于是,申请了博客的js权限,美化美化我的博客。
好在js的语法像C系的,看了看,写个程序应该还是可以的。
围棋里,设计好基本的数据结构:
//a是19X19数组,用来存放围棋,每个位置0为空,1为黑,2为白
//b是检测禁手、提子时临时使用
var a = new Array(19);
var b = new Array(19);
for(var i=0;i<19;i++) {
a[i] = new Array(19);
b[i] = new Array(19);
for(var j=0;j<19;j++) {
a[i][j] = 0;
}
}
//当前棋的步数,从0开始
var step = 0;
//选择qa变量的第qipu_seq个棋谱
var qipu_seq = Math.floor(10000*Math.random())%qa.length;
//所有的棋谱,这个数据结构是本文重点,后面讲
var qa;
画图用canvas,之前并未接触,一样,baidu上搜搜,知道了画圆、画线、画方块的办法,OK了,我画围棋说白了就是圆、线、方块组成。过程中有个BUG,后来才知道,是我对moveTo、LineTo的理解有问题,最终画棋盘、棋子的函数如下:
function draw_weiqi()
{
var c=document.getElementById("myCanvas").getContext("2d"); c.fillStyle="#808080";
c.fillRect(0,0,8*20,8*20);
c.beginPath();
c.strokeStyle="#000000"; for(var i=0;i<19;i++) {
c.lineWidth = 1;
c.moveTo(8+i*8,8);
c.lineTo(8+i*8,19*8);
c.stroke();
c.moveTo(8,8+i*8);
c.lineTo(19*8,8+i*8);
c.stroke();
}
for(var i=0;i<19;i++) {
for(var j=0;j<19;j++) {
if(a[i][j] != 0) {
if(a[i][j] == 1) {
c.fillStyle="#000000";
} else {
c.fillStyle="#FFFFFF";
}
c.beginPath();
c.arc(8+i*8,8+j*8,4,0,Math.PI*2,true);
c.fill();
}
}
}
if(step!=0) {
var n = qipu[Math.floor((step-1)/2)];
if(step%2==1) {
n = n%361;
} else {
n = Math.floor(n/361);
}
var x = Math.floor(n/19);
var y = n%19;
c.fillStyle="#FF0000";
c.fillRect(6+x*8,6+y*8,4,4);
}
for(var i=0;i<9;i++) {
var x = 3+6*(i%3);
var y = 3+2*(i-i%3);
if(a[x][y] == 0) {
c.fillStyle="#c0c0c0";
c.fillRect(8+x*8,8+y*8,1,1);
}
}
}
我的想法是js默认的情况是反复播放alphago master和alphago zero的二十局棋,鼠标点上去之后可以手动下棋(但不支持AI),于是还要去考虑鼠标的操作。
鼠标的操作网上有太多的文章来讲,但讲的似乎又不清晰,不过反复调试,试验下,鼠标的支持还是没问题了。
var n = document.getElementById("myCanvas");
var ev = event || window.event;
var x = ev.PageX||ev.clientX;
var y = ev.PageY||ev.clientY;
x += document.documentElement.scrollLeft || document.body.scrollLeft;
y += document.documentElement.scrollTop || document.body.scrollTop;
x -= n.offsetLeft;
y -= n.offsetTop;
找到点击的位置在画布里的相对坐标为以上代码,可是这里还是有一个BUG,对于Firefox不支持,我也没去找原因,如果有知道的,欢迎和我联系或者写在评论里,让我可以补掉这个BUG。
围棋的规则也没什么问题,我有篇文章(《围棋规则的计算机实现》)里专门讲围棋的规则可以看成是一个连通图遍历,如此可以判断有没有气,从而禁手、提子、打劫,乃至后面点掉死子、数子计算胜负都可以归结于连通图遍历。
放进去棋谱是个问题,这需要相对较大的数据量,我虽然只放20个棋谱,但是我的强迫症总觉得棋谱的数据多了。
我拿到的sgf文件,是围棋界的标准存储文件,以master和zero的第一局为例,棋谱如下:
(;FF[4]CA[UTF-8]KM[7.5]OT[3x60 byo-yomi]PB[AlphaGo Master]PW[AlphaGo Zero]
RE[W+R]RU[Chinese]TM[7200];B[dd];W[pp];B[cp];W[pd];B[fp];W[cc];B[cd];W[dc];
B[fc];W[ec];B[ed];W[fb];B[dj];W[nq];B[mp];W[hq];B[qf];W[qk];B[nc];W[pf];B[pg];
W[of];B[qd];W[qc];B[qe];W[pc];B[qi];W[md];B[og];W[mc];B[gb];W[gc];B[fd];W[hb];
B[gd];W[ga];B[hp];W[ip];B[ho];W[gq];B[fq];W[io];B[np];W[mq];B[in];W[jq];B[jn];
W[op];B[lq];W[lr];B[lp];W[kq];B[ek];W[dp];B[dq];W[bj];B[bl];W[cq];B[me];W[le];
B[lf];W[mf];B[ne];W[nf];B[ke];W[ld];B[kd];W[nd];B[cr];W[ch];B[eh];W[be];B[bd];
W[cf];B[rn];W[rp];B[qo];W[qp];B[mr];W[nr];B[ir];W[iq];B[on];W[eg];B[dh];W[dg];
B[fg];W[bg];B[mh];W[lg];B[kf];W[nh];B[mj];W[mg];B[kg];W[lh];B[kh];W[hf];B[gf];
W[hd];B[hg];W[ml];B[nl];W[nm];B[om];W[ck];B[cl];W[nk];B[ol];W[mk];B[pj];W[eq];
B[dr];W[fr];B[fo];W[qm];B[qn];W[pn];B[po];W[oo];B[pk];W[er];B[ep];W[ri];B[rh];
W[do];B[es];W[gr];B[kj];W[pm];B[rm];W[ql];B[qj];W[hn];B[hm];W[dk];B[dl];W[ej];
B[co];W[rj];B[pl];W[fk];B[el];W[di];B[fj];W[ei];B[fi];W[fh];B[gh];W[rk];B[si];
W[ff];B[gg];W[ie];B[ge];W[kc];B[lj];W[ko];B[lm];W[nj];B[mi];W[ni];B[kr];W[ms];
B[jd];W[kk];B[nn];W[km];B[kn];W[ng];B[jk];W[jl];B[bk];W[cj];B[rc];W[rb];B[jc];
W[jj];B[ik];W[gi];B[gj];W[ij];B[hj];W[il];B[hk];W[ki];B[ji];W[gn];B[dn];W[hl];
B[gl];W[gm];B[kb];W[lb];B[mb];W[nb];B[bc];W[bb];B[ab];W[ba];B[da];W[db];B[ma];
W[na];B[ea];W[eb];B[sd];W[ln];B[lo];W[im];B[aj];W[ai];B[sb];W[ra];B[jr];W[hr];
B[ii];W[la];B[he];W[id];B[if];W[ro];B[rl];W[lk];B[li];W[sn];B[sk];W[fn];B[eo];
W[sm];B[sj];W[ib];B[jb];W[kp];B[jo];W[mm];B[mn];W[qb];B[jp];W[ks];B[fm];W[em];
B[fl];W[en];B[fs];W[sa];B[ef];W[ak];B[al];W[dm];B[cm];W[ee];B[fe];W[de];B[ae];
W[ce];B[af];W[sc];B[rd];W[ph];B[qg];W[oh];B[pe];W[oe];B[pr];W[rr];B[rq];W[qq];
B[so];W[sp];B[sl];W[so];B[ic];W[hc];B[ag];W[ah];B[gs];W[is];B[eh];W[je];B[jf];
W[ia];B[aj];W[bf];B[ad];W[ak];B[gp];W[js];B[aj];W[lc];B[no];W[ak];B[sb];W[aj])
我不用解释,应该也能猜的出来,前面是信息,B[dd];W[pp];B[cp];这样的是一步一步的棋,采用坐标的方法,横纵坐标都是a-s这19个字母中的一个。
因为最开始对js不熟悉,想全部用整数存储棋谱,每一步棋存成0~360的一个数字,方法是把a-s对应于0~18,然后X*19+Y(其中X,Y是横纵坐标),然后搞成数组,20个棋谱的qa变量就是数组的数组。
用shell设计出来就是这样:
#!/bin/bash
export LANG=C
for i in *.sgf; do
cat $i | tr -d '\r\n\t() ' | tr ';' '\n' | sed -nr '/^[BW]\[([a-s][a-s])\]$/!d;s//\1/;H;${x;s/\n//gp;}' | od -t u1 -An -v | gawk '
BEGIN {n=}
{for(i=;i<=NF;i++){a[n++]=$i-;}}
END {
m=;
for(i=;i+<=n;i+=)b[m++]=a[i]*+a[i+];
for(i=;i<m;i++) {
x = b[i];
if(i!=)printf(",");
printf("%d",x);
}
printf("\n");
}
' | sed -nr 's/^/\[/;s/$/\],/;p' | tr -d '\n'
done | sed -nr 's/,$//;s/^/var qa = \[/;s/$/\];/;p'
echo
var qa = [[60,300,53,288,110,40,41,59,97,78,79,96,66,263,243,149,309,314,249,290,291,271,307,306,308,287,312,231,272,230,115,116,98,134,117,114,148,167,147,130,111,166,262,244,165,187,184,281,225,226,224,206,86,72,73,28,30,54,232,213,214,233...
因为js是解释型语言,最终从服务器上下下来的是代码,一数,字节数很多,19788字节。强迫症犯了,要压一下。
于是每两步棋合一起,比如前一步棋为A,后一步棋为B,AB范围都为0~360,则用B*361+A表示两步棋。如果整局棋的步数为奇数,最后一步棋找不到配的,那么最后一个数字就是361*361+A(正常范围是0~360,361并不是真实棋步,可以直接判断出来这一步没有)
shell程序如下
#!/bin/bash
export LANG=C
for i in *.sgf; do
cat $i | tr -d '\r\n\t() ' | tr ';' '\n' | sed -nr '/^[BW]\[([a-s][a-s])\]$/!d;s//\1/;H;${x;s/\n//gp;}' | od -t u1 -An -v | gawk '
BEGIN {n=}
{for(i=;i<=NF;i++){a[n++]=$i-;}}
END {
m=;
for(i=;i+<=n;i+=)b[m++]=a[i]*+a[i+]+(a[i+]*+a[i+])*;
if(i+<=n)b[m++]=a[i]*+a[i+]+*;
for(i=;i<m;i++) {
x = b[i];
if(i!=)printf(",");
printf("%d",x);
}
printf("\n");
}
' | sed -nr 's/^/\[/;s/$/\],/;p' | tr -d '\n'
done | sed -nr 's/,$//;s/^/var qa = \[/;s/$/\];/;p'
echo
再来看一看,数据如下这样:
var qa = [[108360,104021,14550,21340,28255,34735,95009,54032,113663,104939,98122,110773,103915,83703,83302,41991,48472,41271,60435,47077,60037,88346,67672,101625,81811,74590,26078,10181,19524,77125,84327,91223,76726,90443,16300,8386,15545,122354,115477,95549,60817,29881,22807...
这里的数据压缩到16489字节,还是很多。
其实,当初有个很好的选择,就是直接提出a-s的坐标,然后来表示,那么master和zero第一盘棋就变成以下字符串
“ddppcppdfpcccddcfcecedfbdjnqmphqqfqkncpfpgofqdqcqepcqimdogmcgbgcfdhbgdgahpiphogqfqionpmqinjqjnoplqlrlpkqekdpdqbjblcqmelelfmfnenfkeldkdndcrchehbebdcfrnrpqoqpmrnririqonegdhdgfgbgmhlgkfnhmjmgkglhkhhfgfhdhgmlnlnmomckclnkolmkpjeqdrfrfoqmqnpnpooopkereprirhdoesgrkjpmrmqlqjhnhmdkdlejcorjplfkeldifjeififhghrksiffggiegekcljkolmnjminikrmsjdkknnkmknngjkjlbkcjrcrbjcjjikgigjijhjilhkkijigndnhlglgmkblbmbnbbcbbabbadadbmanaeaebsdlnloimajaisbrajrhriilaheidifrorllklisnskfneosmsjibjbkpjommmnqbjpksfmemflenfssaefakaldmcmeefedeaeceafscrdphqgohpeoeprrrrqqqsospslsoichcagahgsisehjejfiaajbfadakgpjsajlcnoaksbaj”
shell如下:
#!/bin/bash
export LANG=C
for i in *.sgf; do
cat $i | tr -d '\r\n\t() ' | tr ';' '\n' | sed -nr '/^[BW]\[([a-s][a-s])\]$/!d;s//\1/;H;${x;s/\n//gp;}' | sed -nr 's/^/"/;s/$/",/;p'
done | sed -nr 's/,$//;s/^/var qa = \[/;s/$/\];/;p'
echo
这个其实已经很不错,10730字节。只是最开始的时候对js不熟,不知道怎么处理字符到整形,强迫症也没犯。
不过,这个10730字节我还是不满足,后来想了想,
其实用一个整数来代表两步棋,B*361+A(B为0~361,A为0~360),那么这个整数范围为0~130681,
而217=131072>130681
而且数值非常接近,
于是决定每步棋编码17bits,编成二进制数据,又因为js不是编译型语言,需要给二进制数据一个编码,base64是合适的,理论上可以再压缩的多那么一点点,比如不只64个可见字符,用的更多一些也可,只是程序比较复杂,因为不再是2的整数次方个不同的用来编码的可见字符,压缩率提高也有限。于是就此作罢。
#!/bin/bash
export LANG=C
for i in *.sgf; do
cat $i | tr -d '\r\n\t() ' | tr ';' '\n' | sed -nr '/^[BW]\[([a-s][a-s])\]$/!d;s//\1/;H;${x;s/\n//gp;}' | od -t u1 -An -v | gawk '
BEGIN {n=}
{for(i=;i<=NF;i++){a[n++]=$i-;}}
END {
m=;
for(i=;i+<=n;i+=)b[m++]=a[i]*+a[i+]+(a[i+]*+a[i+])*;
if(i+<=n)b[m++]=a[i]*+a[i+]+*;
for(i=;i<m;i++) {
x = b[i];
y = ;
for(j=;j<;j++) {
if(x>=y) {
x-=y
print ""
} else
print ""
y/=;
}
}
}
' | gawk 'BEGIN{i=;j=;v=;}
{if($==)v+=j;j/=;i++;if(i==){printf("%c",v);i=;j=;v=}}
END{if(i!=)printf("%c",v)}
' | base64 | tr -d '\n' | sed -nr 's/^/"/;s/$/",/;p'
done | sed -nr 's/,$//;s/^/var qa = \[/;s/$/\];/;p'
echo
var qa = ["06RllUcaxTXDcvoevuZC0xDd/+Z67+lbC1yvXRveisykB16sKE3dgmt+V1QtZGoQsYz5n8nI14y7wnxSYiS1FpLPZFeV21hSx/WCDCHlz3fLhit1PXbInS5LIuI6aYDVm8aVzzFcYa+wD+oyVuPSJjWqaqFmguJPHW+CdK74wDQiZ0x4ytK5GdHMb2CZ42Xww8EPUYEqclwAO24kD3rByOYm46Q82kENad20QdbtY1bR3esshAVshMnKKX9AqysccI5mmL4qzymWrLIrlHDXp0OJIazCkB10zNnDOmd0EYLUeRpNRCk6y2xzt602Ifq6Jms+cY2SCQ4NTKdeFSYKPkGfevxW4OazCsHECKzQOz7yjudCsEX33f8FFxJP8aP3ZV98gnleB89xprhAh4QcOwsGlMoDx8HBAA==",...
总算满意了,数数字节数,
# ./deal.sh | wc -c
7680
7680字节,算是交代了。
最后,再把代码中的一堆变量、函数名给用单个字符替换了,去掉所有的换行、空格,甚至括号都没有多余的,
遇到if(cnt1 != 0 && cnt2 == 0),都被我简写成了if(cnt&&!cnt2),当然,后面cnt、cnt2都被搞成了单字符。
最后贴了出去,可读性极差,不过代码小,如果我自己需要改动,就用原本的代码改动了。接下去想在此基础上搞个简易的AI,再看时间了。
刚才想了想,base64的解码是从网上找过来然后修改的,我看那个字符串表示不是太长了一点,强迫症又来了,我是不是该再裁它一刀。
function base64_decode(c){
var s="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var x="";
var e=new Array(4);
var i=0;
c=c.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while(i<c.length){
for(var j=0;j<4;j++)
e[j]=s.indexOf(c.charAt(i++));
x+=String.fromCharCode((e[0]<<2)|(e[1]>>4));
if (e[2]!=64)
x+=String.fromCharCode(((e[1]&15)<<4)|(e[2]>>2));
if (e[3] != 64)
x+=String.fromCharCode(((e[2]&3)<<6)|e[3]);
}
return x;
}
我博客上的围棋js程序的更多相关文章
- 在技术胖博客上学习ES6遇到的坑和想法
第一节:ES6的开发环境搭建 坑1:全局安装babel-cli已经不被官方推荐,改为局部安装(cnpm install babel-cli --save-dev): 坑2:babel src/inde ...
- 在Visual Studio上开发Node.js程序(2)——远程调试及发布到Azure
[题外话] 上次介绍了VS上开发Node.js的插件Node.js Tools for Visual Studio(NTVS),其提供了非常方便的开发和调试功能,当然很多情况下由于平台限制等原因需要在 ...
- 在Visual Studio上开发Node.js程序
[题外话] 最近准备用Node.js做些东西,于是找找看能否有Visual Studio上的插件以方便开发.结果还真找到了一个,来自微软的Node.js Tools for Visual Studio ...
- 小飞淙在博客上的第一天——NOIP201505转圈游戏
原本我是在word文档上写这种东西的,在杨老师的“强迫”下,我开始写了博客. 这是我在博客上的第一天,就先来个简单的,下面请看题: 试题描述 有n个小伙伴(编号从0到n-1)围坐一圈玩游戏.按照顺时 ...
- wordpress如何利用插件添加优酷土豆等视频到自己的博客上
wordpress有时候需要添加优酷.土豆等网站的视频到自己的博客上,传统的分享方法不能符合电脑端和手机端屏幕大小的需求,又比较繁琐,怎样利用插件的方法进行添加呢,本视频向你介绍一款这样的插件——Sm ...
- 使用Python在自己博客上进行自动翻页
先上一张代码及代码运行后的输出结果的图! 下面上代码: # coding=utf-8 import os import time from selenium import webdriver #打开火 ...
- 给自己的博客上添加个flash宠物插件
前言 最近在一些博主的博客上看到一些小宠物的挂件,很有趣,访客到了网站后可以耍耍小宠物,增加网站的趣味性,在功能强大的博客系统上看到有这样的小宠物挂件还是蛮有趣的. 正文 下面就简单介绍下如何在博客园 ...
- 最近准备把安卓和java的知识再回顾一遍,顺便会写博客上!千变万化还都是源于基础,打扎实基础
最近准备把安卓和java的知识再回顾一遍,顺便会写博客上!千变万化还都是源于基础,打扎实基础,加油吧 距离去北京还有23天
- 在Visual Studio 2013 上开发Node.js程序
[题外话] 最近准备用Node.js做些东西,于是找找看能否有Visual Studio上的插件以方便开发.结果还真找到了一个,来自微软的Node.js Tools for Visual Studio ...
随机推荐
- Redis清空数据
进入redis目录下 redis-cli -h IP -p 端口 -a 密码 flushall
- android 中的ExpandableListView取消一级图标
mainlistview = (ExpandableListView) view.findViewById(R.id.listview_myteacher); mainlistview.setGrou ...
- 2018-01-05-医药行业的IT革命探讨
layout: post title: 2018-01-05-医药行业的IT革命探讨 key: 20180105 tags: IT AI 医疗 modify_date: 2018-01-05 --- ...
- DELL服务器硬件信息采集SHELL脚本
DELL服务器硬件信息采集SHELL脚本最近需要做资产列表,要采集DELL服务器的硬件信息,包括如下信息:1.操作系统信息(类型,版本,内核,平台,主机名)2.主板信息(厂商,机型,序列号)3.CPU ...
- SQL explain详细结果
explain 的结果 id select_type 查询的序列号 select_type simple (不含子查询) primary (含子查询.或者派生查询) subquery (非from子查 ...
- Java 解压zip压缩包
因为最近项目需要批量上传文件,而这里的批量就是将文件压缩在了一个zip包里,然后读取文件进行解析文件里的内容. 因此需要先对上传的zip包进行解压.以下直接提供代码供参考: 1.第一个方法是用于解压z ...
- Java基础教程1:环境配置及第一个HelloWorld.java
本文主要介绍JDK环境配置.Sublime Text3配置及第一个HelloWorld.Java程序.运行环境为Win10系统,使用JDK1.8版本. 1. JDK下载及环境配置 1.1 JDK下载 ...
- 如何更改图片的背景色(PS、证件照之星)
如何更改图片的背景色(PS.证件照之星) 1.1 证照之星教你如何给证件照换背景 证照之星教你如何给证件照换背景?这个问题困扰很多人,如果你不了解证照之星,一款专业的证件照片制作软件,你肯定就无法自 ...
- appium+python环境搭建
一.环境 1.安装python. 2.安装appium. 3.安装pip 4.安装selenium 5.安装Appium-Python-Client #cmd->cd setupto ...
- MySQL递归的替代方案
类似查出某个机构下所有的子机构,可用递归的方式实现.但MySQL不支持递归,可以考虑用如下的方式来实现递归调用. 第一种,临时表方式,使用函数每次查出子机构,再可以和其他表联查. 第二种,新建一张表, ...