无忧启动论坛

 找回密码
 注册
搜索
系统gho:最纯净好用系统下载站投放广告、加入VIP会员,请联系 微信:wuyouceo
查看: 9028|回复: 91
打印 上一主题 下一主题

js (浏览器版) 中国象棋

    [复制链接]
跳转到指定楼层
1#
发表于 2018-9-30 18:08:18 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
从这儿 http://web.jobbole.com/90503/ 找到了这里的源码

https://github.com/xqbase/xqwlight

源码里面有很多版本,包括 JavaScript 版本——克隆到本机,只需要双击 index.htm 就可以运行。

JavaScript 很强大啊!

就不贴截图了。

如果你嫌麻烦,不想下载,你可以试试在线与电脑下象棋:

http://www.xiangqiqipu.com/Home/Robot

点评

我以为我下不过程序 原来我连网页都打不过  发表于 2022-9-6 11:36
2#
发表于 2018-9-30 18:38:07 | 只看该作者
多谢分享 我来学习学习
回复

使用道具 举报

3#
发表于 2018-9-30 19:22:23 | 只看该作者
不错,谢谢分享
回复

使用道具 举报

4#
发表于 2018-9-30 21:50:34 | 只看该作者
技术太水,不试了...
回复

使用道具 举报

5#
发表于 2018-10-1 08:15:53 | 只看该作者
在线游戏?

点评

开源的软件,不会像个流氓一样,把用户绑定到网站上。 你下载源代码,无需网络就能运行。它是一个用 js 编写的本地应用程序。我认为,它是 js 的一个典范。  详情 回复 发表于 2018-10-1 09:44
回复

使用道具 举报

6#
 楼主| 发表于 2018-10-1 09:44:34 | 只看该作者

开源的软件,不会像个流氓一样,把用户绑定到网站上。

你下载源代码,无需网络就能运行。它是一个用 js 编写的本地应用程序。我认为,它是 js 的一个典范。
回复

使用道具 举报

7#
发表于 2021-9-25 17:25:09 | 只看该作者

不错,谢谢分享
回复

使用道具 举报

8#
发表于 2021-9-25 18:00:17 | 只看该作者
谢谢分享
回复

使用道具 举报

9#
发表于 2021-10-21 14:40:43 来自手机 | 只看该作者
那么小的体积,那么强的智能,厉害!谢谢分享!
回复

使用道具 举报

10#
发表于 2021-11-25 08:48:53 | 只看该作者
JavaScript 版本的软件越来越少用了
回复

使用道具 举报

11#
 楼主| 发表于 2022-8-29 00:01:16 | 只看该作者
今天又找到了一个 JS 版的中国象棋:

中国象棋 - in html5

https://github.com/itlwei/chess



回复

使用道具 举报

12#
发表于 2022-8-29 16:59:09 | 只看该作者
感谢楼主无私的分享。
回复

使用道具 举报

13#
发表于 2022-9-1 01:21:10 | 只看该作者
试试在线
回复

使用道具 举报

14#
 楼主| 发表于 2022-9-1 10:32:40 | 只看该作者

今天学习一下
中国象棋 - in html5


中的代码,学习的重点是开局库的结构。有些单词看不懂,这会影响对代码的准确理解,需要查字典。

在字典中,pace 是 “一步,步子,步调,进度,节奏” 的意思。然而,它用在函数的参数中,猜测其含义应该是 “当前局面” 。

在字典中,man 有多种意思,其中有 “棋子、对手” 的意思。在代码中,它的含义好像是 “着法”。

在字典中,bill 有 “清单” 的意思。猜测含义是 “各种着法的列表”。

在字典中,gambit 有 “策略、开局、起始着法” 等意思。

原始代码的排版不够精细,我对其进行了排版优化。

发现了一个拼写错误:length (长度)被误写为 lenght。出错的代码为 AI.setHistoryTable.lenght。拼写错误是否意味着程序错误呢?那要看其他地方的代码是否都是一样的错误,如果都是同样的拼写错误,则不会导致程序错误。如果在全部源代码中,这是唯一出现的一个错误的拼写,那就可以确定这是一个程序错误了。

  1. //人工智能初始化
  2. AI.init = function (pace) {
  3.         var bill = AI.historyBill || com.gambit; //开局库
  4.         if (bill.length) {
  5.                 var len = pace.length;
  6.                 var arr = [];
  7.                 //先搜索棋谱
  8.                 for (var i=0; i < bill.length; i++) {
  9.                         if (bill[i].slice(0, len) == pace) {
  10.                                 arr.push(bill[i]);
  11.                         }
  12.                 }
  13.                 if (arr.length) {
  14.                         var inx = Math.floor ( Math.random() * arr.length );
  15.                         AI.historyBill = arr ;
  16.                         return arr[inx].slice (len, len + 4).split ("");
  17.                 } else {
  18.                         AI.historyBill = [] ;
  19.                 }
  20.         }
  21.          //如果棋谱里面没有,人工智能开始运作
  22.         var initTime = new Date().getTime();
  23.         AI.treeDepth = play.depth;
  24.         //AI.treeDepth = 4;
  25.         
  26.         AI.number = 0;
  27.         AI.setHistoryTable.lenght = 0

  28.         var val = AI.getAlphaBeta (-99999, 99999, AI.treeDepth, com.arr2Clone (play.map), play.my);
  29.         //var val = AI.iterativeSearch (com.arr2Clone(play.map), play.my)
  30.         if (! val || val.value == -8888) {
  31.                 AI.treeDepth = 2;
  32.                 val = AI.getAlphaBeta (-99999 ,99999, AI.treeDepth, com.arr2Clone (play.map), play.my);
  33.         }
  34.         //var val = AI.iterativeSearch (com.arr2Clone(play.map), play.my);
  35.         if (val && val.value != -8888) {
  36.                 var man = play.mans[val.key];
  37.                 var nowTime= new Date().getTime();
  38.                 console.log('最佳着法:' +
  39.                         com.createMove(com.arr2Clone(play.map), man.x, man.y, val.x, val.y) +
  40.                         ' 搜索深度:' + AI.treeDepth +
  41.                         ' 搜索分支:' + AI.number + '个  最佳着法评估:' + val.value + '分' +
  42.                         ' 搜索用时:'+ (nowTime - initTime) + '毫秒');
  43.                 return [man.x, man.y, val.x, val.y];
  44.         } else {
  45.                 return false;
  46.         }
  47. }


复制代码

回复

使用道具 举报

15#
 楼主| 发表于 2022-9-1 13:48:00 | 只看该作者
上述 lenght 的拼写,在全部代码中,只出现了两处:

AI.setHistoryTable.lenght = 0;
AI.setHistoryTable.lenght ++;

而且, AI.setHistoryTable 是个函数。分两种情况讨论:

1、如果 lenght (拼写错误)是自定义的属性, 它只被赋值,没有被别处的代码引用,这肯定是多余的代码。

2、如果 length (拼写正确) 是函数的属性,它就是只读属性,不可以更改。既然代码更改了它,那也是错的。

综合以上两点,基本可以说,这确实是个错误。

猜测正确的应该分别是这样的:

AI.historyTable.length = 0;
AI.historyTable.length ++;

也就是说,不只是属性名称有拼写错误,就连对象的名称也弄错了!

回复

使用道具 举报

16#
 楼主| 发表于 2022-9-1 17:23:32 | 只看该作者
length 是数组的一个属性,但不是 object 的一个属性。

由于 AI.historyTable = {} 是一个 object 而不是数组,所以,前面说的

AI.historyTable.length = 0;
AI.historyTable.length ++;

也是没有用的。上述两条语句,相当于为 object 添加了个 length 属性变量,没有实际意义。

若把第一条改为 AI.historyTable = {}; 则有意义,那就是,清空 object。

而第二条语句 AI.historyTable.length ++; 干脆就注释掉吧!
回复

使用道具 举报

17#
发表于 2022-9-1 17:36:00 | 只看该作者
关注下
回复

使用道具 举报

18#
 楼主| 发表于 2022-9-2 11:40:18 | 只看该作者
本帖最后由 不点 于 2022-11-15 14:46 编辑

本楼提供修改版的 中国象棋 in html5 的下载


2022-09-02 解决了 lenght 拼写错误以及相关问题;纠正了 “车、马” 两个汉字的乱码;把 gambit.all.js 的内容移植到 common.js 中,解决了新版浏览器拒绝加载本地文件(浏览器给出 CORS 错误)的问题。


2022-10-01 修正了基础算法中的一些毛病,现在应该不会再出现明显的错误了。

2022-10-05 调整了兵、士、相的价值。删除了开局库,目的是暴露出算法的毛病。增加了调试代码,跟踪局面评估过程。

2022-10-06 由于无法解决局面评估函数暴露出来的问题,因此,代码不再更新了。此处保存目前最新的改动。接下来准备学习一楼提到的“象棋小巫师”。


Chess.zip

966.63 KB, 下载次数: 9, 下载积分: 无忧币 -2

中国象棋 in html5 修改版 2022-09-02

Chess-new.zip

766.37 KB, 下载次数: 6, 下载积分: 无忧币 -2

2022-10-06 最新改动保存于此,不再更新

xqwlight_js.zip

568.52 KB, 下载次数: 1, 下载积分: 无忧币 -2

2022-10-18 象棋小巫师修改版,棋盘是图片

xqwlight_js.zip

349.96 KB, 下载次数: 3, 下载积分: 无忧币 -2

2022-11-15 象棋小巫师修改,用Date.now计时

回复

使用道具 举报

19#
 楼主| 发表于 2022-9-3 05:02:08 | 只看该作者
前述代码对开局库中的每一行进行了处理,最后返回其中一行中的 4 个字节作为结果:

return arr[inx].slice (len, len + 4).split ("");

这个结果,就代表一种着法。也就是说,每一行是双方着法的交替,其中每 4 个数字就代表一种着法。

另一方面,从人工智能运作代码的返回值:

return [man.x, man.y, val.x, val.y];

可以了解,这四个数字分别是某个位置的 x 坐标,y 坐标,以及另一个位置的 x坐标,y 坐标。

既然是一种着法,那就容易猜到,这是棋子的起始坐标和终止坐标。

好的,现在把开局库的第一行拿出来研究一下:


774770627967807089797276666512422625000119270171


按照每 4 字节一组,把它分解成着法序列。


如果左上角的坐标是 (0, 0),那么 (7, 7) 就是右侧红炮的位置,而 (4, 7) 就是当头炮的位置。因此,7747 就是 炮二平五。



7747 炮二平五

7062 马 8进 7 ——(7, 0)是黑方 8路的马,(6, 2) 是跳正马的位置

7967 马二进三 ——(7, 0)是红方二路的马,(6, 7) 是跳正马的位置
8070 车 9平 8 ——(8, 0)是黑方 9路的车,(7, 0) 是出直车的位置
8979 车一平二 ——(8, 9)是红方一路的车,(7, 9) 是出直车的位置
7276 炮 8进 4 ——(7, 2)是黑方 8路的炮,(7, 6) 是进炮封红车的位置
6665 兵三进一 ——(6, 6)是红方三路的兵,(6, 5) 是进兵走到河口相位的位置
1242 炮 2平 5 ——(1, 2)是黑方 2路的炮,(4, 2) 是黑方当头炮(中相)的位置
2625 兵七进一 ——(2, 6)是红方七路的兵,(2, 5) 是进兵走到河口相位的位置
0001 车 1进 1 ——(0, 0)是黑方 1路的车,(0, 1) 是出横车的位置
1927 马二进三 ——(1, 9)是红方八路的马,(2, 7) 是跳正马的位置
0171 车 1平 8 ——(0, 1)是黑方1路横车,(7, 1) 是横车平到 8 路的位置,形成霸王车


这样,开局库的结构也就完全清楚了。


回复

使用道具 举报

20#
 楼主| 发表于 2022-9-3 12:09:58 | 只看该作者
本帖最后由 不点 于 2022-9-3 12:16 编辑

既然弄清楚了一个开局库的结构,增强了信心,就想进一步探索有关开局库的知识,于是搜到了这个网页:

https://www.xqbase.com/computer/eleeye_book.htm

文章不长,但介绍得很详细。这些知识正是我欠缺的,干脆就全部复制过来:



() 开局库

  在阅读本章前,建议读者先阅读象棋百科全书网中《对弈程序基本技术》专题的以下几篇译文:
  (1) 其他策略——开局库(Martin Fierz)

 7.1 象棋程序对开局库的处理   

  开局库是象棋程序中知识含量最多的部分,各种程序的棋力会因为开局库的不同而有所差距。互联网上介绍国际象棋开局库的文章很多,而中国象棋开局库的原理和国际象棋是完全一样的,因此笔者就不作太多的介绍了。

  很多国际象棋的程序中,开局库并不被引擎处理,而是由界面来完成的,棋局进入中局脱离棋谱后,才让引擎作搜索。ChessBase 的系列软件 Junior Fritz,以及支持 UCI 协议的 Shredder,都使用这种工作方式。由于它们使用同一套 ChessBase 的界面,因此开局库格式是统一的。由于 WinBoard 本身并不能处理开局库,因此支持 WinBoard 的引擎都具有处理开局库的能力,而且每个引擎都有各自的开局库格式。

  而中国象棋目前没有统一的界面,因此也没有统一的开局库格式,开局库一般由引擎来处理,各种引擎有各自定义的开局库格式。

  ElephantEye 早期开局库具有非常明显的特点——它是文本格式的,每一行记录一个着法,依次是着法 (红色部分)、权重 (绿色部分) 和局面 (紫色部分)

b2e2 5895  rnbakabnr/9/1c5c1/p1p1p1p1p/9/9/P1P1P1P1P/1C5C1/9/RNBAKABNR  w - - 0 1 

  由于记录局面时使用 FEN 串,这就增加了开局库的可读性,但同时使开局库变得特别庞大,ElephantEye 的开局库 (BOOK.DAT) 仅有 20,000 多个着法却达到了1.6M

  从 ElephantEye 2.0 以后,每个局面仅占用 8 字节 (4 字节的 Zobrist 键值、2 字节的着法和 2 字节的权重),而且对称局面作了合并,因此由 10,000 多个着法组成的开局库大小不超过 100K

 7.2 开局库的制作 

  ElephantEye 现有的开局库是从象棋百科全书网上收录的 8000 多局对局中整理出来的,这些对局涵盖了 1990 年到 2004 年的全国顶级象棋比赛 (团体赛、个人赛、甲级联赛和五羊杯),因此具有代表性。现在简要介绍一下开局库的制作流程:

  (1) 把所有的对局中的每个着法解析出来,记录到临时文件中,临时文件的每个着法记录占 16 字节 (8 字节的 Zobrist 键值、4 字节的着法和 2 字节的权重)


  (2) 对临时文件的着法记录按 Zobrist 键值 (第一关键字) 和着法 (第二关键字) 排序;


  (3) Zobrist 键值和着法作关键字,对权重进行叠加;


  (4) 按照笔者观点,权重 = 胜局数×3 + 和棋局数 - 负局数,因此在第 (1) 步中,导致胜局的着法的权重置 3,和局置 1,负局置 -1


  (5) 过滤掉权重小于 4 的着法 (即收入开局库的着法至少是一胜一和的),把 Zobrist 键值的后 4 字节、2 字节表示的着法和权重除以 4 16 位整数值记录到开局库文件 (BOOK.DAT) 中。

 7.3 开局着法的选择

  象棋程序在处理一个局面时,首先要从开局库中寻找是否有相同的局面,当开局库中找不到局面时,才会调用搜索程序进行思考。ElephantEye 从开局库中找到着法的过程如下:

  (1) 由于开局库是按 Zobrist 键值排序的,因此用二分查找法可以很快找到所有符合的局面及其着法;

  (2) 如果当前局面在开局库中没有,那么查找对称局面,找到的着法也要作对称处理;

  (3) 由于开局库中的 Zobrist 键值只有 4 字节,因此有可能产生冲突,必须从着法列表中排除不合理着法;

  (4) 用随机数根据着法的权重选择着法;

  (5) 如果该着法没有构成重复局面,那么将该着法作为最佳着法直接返回。



回复

使用道具 举报

21#
 楼主| 发表于 2022-9-4 13:02:24 | 只看该作者
本帖最后由 不点 于 2022-9-4 18:41 编辑

今天思考一下局面应该如何表示的问题

如果用 hash 值来代表局面,它的好处是占用空间小,搜索速度快。但它具有碰撞的可能性。

考虑用普通字节串来表示局面。这个局面的表示,也仅仅用在库中,不影响 AI 子程序的人工智能计算过程。

我们来表示局面的时候,也要尽量压缩空间的占用。以下就给出一些思考。

第一部分:帅和相,其位置组合,共有 254 种,用一个字节(8位)即可表示。详述如下:

情况(1):帅占据中相的位置,相就不能占据这个位置了。那么,相能够占据的,也只有其他 6 个位置了。细分为以下 3 种情况:

1、没有相(共1种情况)
2、只有一个相(共有 6 种可能的情况)
3、双相都在,共有 6×5/2 = 15 种情况。

综合这 3 种,共有 1 + 6 + 15 = 22 种可能性。

情况(2):帅不在中相的位置,那么帅在九宫格中共有其它 8 种可能的位置。每一种可能性,都对应着相在 7 个位置的不同分布情况。相的各种可能的分布情况如下:

1、没有相(共1种情况)
2、只有一个相(共有7种可能性)
3、双相都在,共有 7×6/2 = 21 种情况。

综合这 3 种,共有 1 + 7 + 21 = 29 种可能性。而帅有 8 种变化,因此,总的可能性是 8×29=232 种。

综合两种情况,帅和相的不同组合,共有 232 + 22 = 254 种。这样,我们就能够用 8 位的字节来表示它了。


第二部分:士的位置组合,共有 16 种,刚好能用 4 位(也就是半个字节)来表示。详述如下:


1、没有士(共1种情况)
2、只有一个士(共有 5 种可能的位置变化)
3、双士都在,共有 5×4/2 = 10 种可能的组合。


综合这三种,共有 1 + 5 + 10 = 16 种可能的位置变化。


第三部分:马的位置变化,共有 4096 种,刚好能用 12 位(也就是 1.5 个字节)来表示。详述如下:


1、没有马(共1种情况)
2、只有一个马(共有 90 种可能的位置变化)
3、双马都在,共有 90×89/2 = 4005 种可能的组合。


综合这三种,共有 1 + 90 + 4005 = 4096 种可能的位置变化。

第四部分:车的位置变化,与马的情况相同,共有 4096 种,不再赘述。


第五部分:炮的位置变化,与车、马的情况相同,共有 4096 种,不再赘述。


第六部分:兵的位置变化。共有 5 个兵,每个兵可能占据的点位不超过 63 个(其实只有 55 个,未过河的兵的位置只有 10 个),用 6 位可以表示。那么,5 个兵总共需要占据 30 位的空间(差不多要占据 4 个字节了)。


综合以上分析,要表示红方的棋子位置,需要的二进制位数是



8(帅、相)+ 4(士)+ 12(马)+ 12(车)+ 12(炮)+ 30(兵)= 78 位


也就是近乎 10 个字节。


同理,黑方也需要近乎 10 个字节。


因此,一个局面的表示,需要近乎 20 个字节的空间。



较新的浏览器支持 JavaScript 新增的 bigint 数据类型。用 JavaScript 的大整数来处理棋局的局面,应该也很方便。对于库中保存的局面(和着法),我们主要使用的是排序(比较大小)之类的操作,因此,初步感觉 bigint 是可以使用的。当然,也可以考虑使用 ArrayBuffer。





回复

使用道具 举报

22#
发表于 2022-9-4 13:26:19 | 只看该作者
还能这么晚。。。
回复

使用道具 举报

23#
 楼主| 发表于 2022-9-4 22:17:25 | 只看该作者
本帖最后由 不点 于 2022-9-4 22:24 编辑

前面说了,局面可用 78(红方) + 78(黑方) = 156 位 的整数来表示。

下面说说具体可以采用的什么样的方案。

帅、相占用最低 8 位,接着是黑方将、象占用的 8 位。(双方共占用 2 字节)

然后是红方士占用的 4 位,接着是黑方士占用的 4 位。(双方共占用 1 字节)

再后是红方马 12 位,黑方马 12 位。(双方共占用 3 字节)
以及红方车 12 位,黑方车 12 位。(双方共占用 3 字节)
以及红方炮 12 位,黑方炮 12 位。(双方共占用 3 字节)

最后一轮是红方兵 30 位,黑方卒 30 位。(双方共占用 7.5 字节)

这就到达最高的位了。

棋盘左右翻转以后,计算得到的整数数值可能不一样。只保留数值较小的那个局面即可。






帅、相的位置与相应的一字节(8 位)整数数值对应表的设计


数值 0~21 对应于帅占据中相位的可能性:


0——帅占中相位,双相都不存在
1——帅占中相位,只有单相,位于红方一路边相位
2——帅占中相位,只有单相,位于红方三路底相位
3——帅占中相位,只有单相,位于红方三路河口相位
4——帅占中相位,只有单相,位于红方七路底相位
5——帅占中相位,只有单相,位于红方七路河口相位
6——帅占中相位,只有单相,位于红方九路边相位
7——帅占中相位,有双相,有一相位于一路,另一相位于三路底部
8——帅占中相位,有双相,有一相位于一路,另一相位于三路河口

9——帅占中相位,有双相,有一相位于一路,另一相位于七路底部
10——帅占中相位,有双相,有一相位于一路,另一相位于七路河口

11——帅占中相位,有双相,有一相位于一路,另一相位于九路边线
12——帅占中相位,有双相,有一相位于三路底部,另一相位于三路河口
13——帅占中相位,有双相,有一相位于三路底部,另一相位于七路底部
14——帅占中相位,有双相,有一相位于三路底部,另一相位于七路河口

15——帅占中相位,有双相,有一相位于三路底部,另一相位于九路边线

16——帅占中相位,有双相,有一相位于三路河口,另一相位于七路底部

17——帅占中相位,有双相,有一相位于三路河口,另一相位于七路河口

18——帅占中相位,有双相,有一相位于三路河口,另一相位于九路边线

19——帅占中相位,有双相,有一相位于七路底部,另一相位于七路河口
20——帅占中相位,有双相,有一相位于七路底部,另一相位于九路边线
21——帅占中相位,有双相,有一相位于七路河口,另一相位于九路边线

数值 22~50 对应于帅在四路底线的 29 种可能性:

22——帅在四路底线,双相都不存在
23——帅在四路底线,只有单相,位于红方一路边相位

24——帅在四路底线,只有单相,位于红方三路底相位
25——帅在四路底线,只有单相,位于红方三路河口相位
26——帅在四路底线,只有单相,位于红方中相位
27——帅在四路底线,只有单相,位于红方七路底相位
28——帅在四路底线,只有单相,位于红方七路河口相位
29——帅在四路底线,只有单相,位于红方九路边相位
30——帅在四路底线,有双相,有一相位于一路,另一相位于三路底部
31——帅在四路底线,有双相,有一相位于一路,另一相位于三路河口

32——帅在四路底线,有双相,有一相位于一路,另一相位于中相位
33——帅在四路底线,有双相,有一相位于一路,另一相位于七路底部
34——帅在四路底线,有双相,有一相位于一路,另一相位于七路河口
35——帅在四路底线,有双相,有一相位于一路,另一相位于九路边线
36——帅在四路底线,有双相,有一相位于三路底部,另一相位于三路河口
37——帅在四路底线,有双相,有一相位于三路底部,另一相位于中相位
38——帅在四路底线,有双相,有一相位于三路底部,另一相位于七路底部
39——帅在四路底线,有双相,有一相位于三路底部,另一相位于七路河口
40——帅在四路底线,有双相,有一相位于三路底部,另一相位于九路边线

41——帅在四路底线,有双相,有一相位于三路河口,另一相位于中相位
42——帅在四路底线,有双相,有一相位于三路河口,另一相位于七路底部
43——帅在四路底线,有双相,有一相位于三路河口,另一相位于七路河口
44——帅在四路底线,有双相,有一相位于三路河口,另一相位于九路边线

45——帅在四路底线,有双相,有一相位于中相位,另一相位于七路底部
46——帅在四路底线,有双相,有一相位于中相位,另一相位于七路河口
47——帅在四路底线,有双相,有一相位于中相位,另一相位于九路边线
48——帅在四路底线,有双相,有一相位于七路底部,另一相位于七路河口
49——帅在四路底线,有双相,有一相位于七路底部,另一相位于九路边线
50——帅在四路底线,有双相,有一相位于七路河口,另一相位于九路边线

依次类推:
数值 51~79 对应于帅在四路象眼的 29 种可能性。
数值 80~108 对应于帅在四路士角的 29 种可能性。
数值 109~137 对应于帅在中路底部的 29 种可能性。
数值 138~166 对应于帅在九宫花心的 29 种可能性。
数值 167~195 对应于帅在六路底部的 29 种可能性。

数值 196~224 对应于帅在六路象眼的 29 种可能性。

数值 225~253 对应于帅在六路士角的 29 种可能性。


就是说,通过查表,就可以建立帅、相位置与字节数值的对应关系。同样,士的 16 种位置分布也可以用查表的方式来与序号建立对应关系。兵的处理是每个兵对应一个独立的 6 位数值,能用数学公式来简单地表示。最后需要动用技巧来处理的,是马的位置与序号的对应关系(车、炮的处理与马完全相同)。


回复

使用道具 举报

24#
发表于 2022-9-4 22:53:16 | 只看该作者
感谢楼主分享、更新!!
回复

使用道具 举报

25#
 楼主| 发表于 2022-9-5 07:08:18 | 只看该作者
本帖最后由 不点 于 2022-9-5 07:11 编辑

刚才的思路,从基本点上,是可行的。但从技术细节上,仍需要优化、调整。

先说说从基本点上,为什么帅、相要搅和在一起?这不是增添麻烦吗?

是的,麻烦是有点麻烦,但能获得节约位数占用的好处。节约的位数只有 1 位,貌似用处不大,而带来的麻烦却不少。然而,假如我们通过技术优化,能够把麻烦降低到可以接受的程度,那节约空间的优点就感觉很便宜、很值了。

如果把帅、相独立处理,帅在九宫中有 9 种位置变化,需要 4 位二进制数,因为 3 位二进制数只能表示 8 种状态,刚好不能表示帅的 9 种状态。另一方面,一个相有 7 种位置变化,需要 3 位二进制数。但有两个相,因此,另一个相也需要有 3 位二进制数。这么说来,如果帅、双相全都独立处理,需要的二进制位数是 4 + 3 + 3 = 10 位。如果双相搅和在一起(不独立),(通过前文的分析计算)双相的状态变化共 29 种,可以用 5 位二进制数来表示。这样的话,帅、相共需 4 + 5 = 9 位。如果 9 位、10 位都愿意接受的话,那就没必要优化成 8 位了。但如果优化成 8 位以后,带来的麻烦不大,那还是可以考虑优化的。

下面就接着探讨对前述方案的调整。

调整的思路是把帅不在中相位的 232 种,放在低位,便于计算。另一个思路是对 7 个相位进行排序优化,中相的占位由于与帅容易发生冲突,就把中相的编号排在末尾。其他相的位置,先后顺序不重要。比如,就这么安排:

0号相位——一路边相
1号相位——三路底相
2号相位——三路河口相
3号相位——七路底相
4号相位——七路河口相
5号相位——九路边相
6号相位——中相(排在末尾)

同理,对帅的 9 个位置也进行排序,中相位的帅,排在末尾:
0号帅位——四路底线(九宫右下角)
1号帅位——四路象眼(九宫右边线中点)
2号帅位——四路士角(九宫右上角)
3号帅位——中路底线(帅的原位)
4号帅位——九宫花心
5号帅位——六路底线(九宫左下角)
6号帅位——六路象眼(九宫左边线中点)
7号帅位——六路士角(九宫左上角)
8号帅位——中路宫顶(中相的位置)(排在末尾)

首先处理 232 个位置(编号 0~231),此时,帅不占据中相位,共有 8 种变化。因此,帅可以单独占据 3 位,比如就取最低 3 位即可。高 5 位可以表示 32 种不同状态,但我们只需要相的 29 种位置变化(见前文)。这 29 种变化也可以安排成有规律的。如果安排成无规律的,那也可以通过建立一个数组来与编号发生对应,工作量不大。由于只有 29 种不同相位的变化,所以,帅、相总共只需要 8 × 29 = 232 个状态,最小编号是 0,最大编号是 231。剩下的,超过 231(但不超过 255) 的编号,还有 24 个,足够我们用于表示剩余的 22 种 “帅占据中相位” 的状态变化。计算时,先判断编号是否大于 231。如果大于 231,那么,帅就在中相位,双相的状态也能够通过编号来确定。如果编号小于或等于 231,帅的位置可以通过编号的最低 3 位来确定,双相的状态通过编号的高 5 位来确定。
回复

使用道具 举报

26#
 楼主| 发表于 2022-9-5 11:57:05 | 只看该作者
本帖最后由 不点 于 2022-9-5 12:07 编辑

在 PHP 中文网,有一篇文章很不错,下面是标题和链接:

h5+js实现本地文件读取和写入
https://www.php.cn/div-tutorial-389900.html

为什么需要研究读写本地文件的方法呢?

因为 中国象棋 - in html5 就有读取本地文件的代码,但却被所有新版的主流浏览器封杀了(CORS 错误,不允许 js 代码读写本地文件)。我是把访问本地开局库数据文件的代码修改成把开局库数据直接嵌入 js 代码里面,才绕过了这个问题。然而,本地文件确实需要读取和写入,所以,就需要研究有没有其它可行的办法。原文的代码是以图片形式提供的,而跨站访问图片有可能被封杀(即便现在允许,将来某一天也可能不再允许了),因此文章内容就不贴了。【更正一下】原文中的代码不是图片,但原文的代码是彩色高亮显示,很美观。我以为是截图呢!本站还没有支持彩色代码,转贴过来就不如原文好看了,所以,还是算了,不转贴了。


回复

使用道具 举报

27#
 楼主| 发表于 2022-9-5 20:26:21 | 只看该作者
本帖最后由 不点 于 2022-9-6 08:52 编辑

在研究马的位置表示法之前,再来看看兵的位置。前面让 5 个兵独立占据空间,每个兵的位置需要 6 位二进制数来表示,5 个兵就需要 30 位了。

如果把 5 个兵搅合在一起,应该会节约一些空间。


兵有 55 种可能的位置。过河兵的 45 个位置,再加上未过河的 10 个位置,就是 55 个位置。那么,最多有


1 + 55 + 55*54/2 + 55*54*53/(3*2) + 55*54*53*52/(4*3*2) + 55*54*53*52*51/(5*4*3*2)
= 1 + 55 + 1485 + 26235 + 341055 + 3478761
= 3847592


种可能性。


我们再看 2^22 = 4194304 比上述结果 3847592 大,因此,用 22 位二进制数,就可以表示 5 个兵的不同位置变化。也就是说,与前面采用的 30 位相比,我们可以节约出 8 位的空间了。这已经是最大限度的节约了,不可能再用更少的位数了。


真的不能再节约了吗?如果不死心,还可以再挖空心思找一找,看看有没有可能。


前述 55 个位置当中,有些组合是不可能出现的。这些情况可以排除掉。


有哪些情况是不可能发生的呢?两个未过河的兵,发生前后叠兵,是不可能的。


这种叠兵,有多少种情况呢?


大的方面,有 “一对叠兵” 和 “两对叠兵”,这两种情况。不可能有三对叠兵,因为总共只有 5 个兵。


首先,容易计算出 “两对叠兵” 出现的可能性。未过河的兵,总共只有 5 列,这 5 列当中,有两列都是叠兵。这种可能的组合,有


5*4/2 = 10 种。由于两对叠兵已经消耗掉了 4 个兵,剩下只有一个兵在棋盘上摆放了,当然,第 5 个兵不存在的情况,也是一种情况,也要计算在内。总共 55 个位置,而两对叠兵已经占用了 4 个位置。剩下的,只有 51 个位置,供第 5 个兵占用。因此,只有 51 种情况。再加上第 5 个兵不存在的情况,那就是 52 种情况。


10*52=520,这就是全部可能的 “发生两对叠兵” 的情况了。太少了!从庞大的 3847592 中扣除 520,就跟掉了一根头发一样,影响不了啥。


一对叠兵出现的可能性会不会多一些呢?肯定会多一些,但也不会太多。


一对叠兵出现在未过河的 5 列当中,出现的可能性有 5 种。剩下的三个兵(其中任意一个兵都可以是不存在的),在 53 种位置上摆放,精确地,有这么多的可能:


1 + 53 + 53*52/2 + 53*52*51/(3*2)
= 1 + 53 + 1378 + 23426
=24858


5*24858=124290


这个数,已经包括两对叠兵的情况了,甚至还可能重复计算了一些两对叠兵的情况,也就是说,只会算多了,不可能算少了。


也就是说,撑死了,也就 124290 种情况,从 3847592 种扣除 124290 得到 3723302,仍然是需要 22 位二进制的数才能表示这么多的不同位置组合。


原先我们粗估需要 20 个字节(160位二进制数)来表示黑白双方棋子的不同位置变化。现在,在对兵进行优化、压缩之后,又能节约 2 个字节了,也就是说,用 18 个字节就可以表示一个棋局的局面了。精确地说,用 140 位的二进制数(即 17.5 个字节),就能表示一个棋局的局面了。



回复

使用道具 举报

28#
 楼主| 发表于 2022-9-5 23:01:37 | 只看该作者
前帖并未精确计算出叠兵的可能性有多少种。虽然其准确数值对于我们没有用,但是,作为一个数学问题,还是有点用的吧。本帖就试试看,能否计算出准确值。

假定红方的一路(即最右边的边线)出现了叠兵(未过河)。前帖已经计算出,这种情况共有 24858 种,其中也包括了在三路、五路、七路、九路出现叠兵的那些可能性。是的,我们现在就是要把那些情况排除掉,目的是只让一路有(未过河的)叠兵,其他几路没有(未过河的)叠兵。

好的,一路已经有叠兵了,假定三路也是叠兵,则只剩下一个兵,在 51 个位置活动。当然,还有一种情况,只有 4 个兵,第 5 个兵不存在。因此,有 52 种可能的变化。同理,如果一路和五路都有叠兵,这也有 52 种情况。同理,如果一路和七路有叠兵,这也有 52 种情况。同理,如果一路和九路都有叠兵,这也有 52 种情况。

因此,从 24858 当中,去掉这 4 个 52,就是只有一路有叠兵的情况了:

24858 − 4×52 = 24650

这个数,就是精确的 “只有一路有叠兵” 的情况总数。

同理,“只有三路有叠兵”的情况总数,也是精确地等于 24650。
同理,“只有五路有叠兵”的情况总数,也是精确地等于 24650。
同理,“只有七路有叠兵”的情况总数,也是精确地等于 24650。
同理,“只有九路有叠兵”的情况总数,也是精确地等于 24650。

所以,“只出现一个叠兵”的情况总数,精确地等于

5×24650 = 123250

再加上同时出现两对叠兵的情况 520:

123250 + 520 = 123770

这就是未过河兵“至少出现一对叠兵”(意思是也包括“同时出现两对叠兵”的可能性)的全部情况了(精确值)。这没有超过前一帖估算出的 124290 这个数,因此,这也反过来印证了,上述的计算,是比较可靠的,没有出现矛盾的结果。
回复

使用道具 举报

29#
 楼主| 发表于 2022-9-5 23:46:36 | 只看该作者
前面我们研究出,在最节约的情况下,可以用 140 位二进制数,来表示棋局的局面。那么,也许有些情况不需要节约呢?所以,本帖就研究一下,红黑双方,如果全部 32 个棋子都独立占据二进制位,那需要多少位呢?

先说红方吧。

帅有 9 个可变的位置,需要 4 位二进制数才能表示。士有 5 个可能的位置,需要 3 位二进制数。另一个士也需要 3 位。相有 7 个可能的位置,需要 3 位二进制数。另一个相也需要 3 位。马有 90 个可能的位置变化,需要 7 位二进制数。另一个马也需要 7 位。同理,双车需要 14 位二进制数,双炮也需要 14 位二进制数。兵有 55 个位置可以放置,需要 6 位二进制数。5 个兵就需要 30 位二进制数。这样,红方总共需要

4(帅) + 6(士)+ 6(相)+ 14(马)+ 14(车)+ 14(炮)+ 30(兵)
= 88 位(二进制数)= 11 (个字节)

红黑双方总共就需要 22 个字节了。

这仍然算是比较节约的方案了。如果要更浪费一点,横纵坐标独立处理。每个棋子,横坐标有 9 个变化,占据 4 位二进制数。纵坐标有 10 个可能的变化,也占据 4 位二进制位。这样每个棋子就需要占据 8 位二进制数了,也就是占用一个字节。红黑双方总共 32 个棋子,也就需要 32 个字节了。最浪费的情况,也不过如此而已。回想一下,我们最节约的情况,是采用 17.5 个字节。

所以,如果觉得 32 个字节是可以接受的,那一切都很轻松,不需要绞尽脑汁去做那些费劲的事情了。
回复

使用道具 举报

30#
 楼主| 发表于 2022-9-6 07:22:05 | 只看该作者
局面的表示,最优可以压缩到 17.5 字节(140位二进制数),最差也不过只需 32 字节(256位二进制数)。如果能压缩到 16 字节(128位二进制数),那就太棒了,但这是做不到的。既然做不到,那么,从电脑CPU硬件的角度来考虑,17.5 字节与 32 字节,差别不大,只不过 17.5 字节能够节约一些内存空间的占用罢了。究竟要选择哪一种表示方法,还要看具体情况。我们此时就可以与 hash 表示法做个对比。hash 表示法,如果采用 4 字节(32位)整数,其空间占用与我们的 17.5 字节也只有 4 倍多的优势。如果 hash 值采用 8 字节(64位)整数,那与 17.5 字节的差别已经不大了,只有 2 倍多的差别了。所以我感觉,采用 hash 方法不太好。hash 方法有碰撞的可能,所以,它有着潜在的问题。64位的 hash,貌似很难产生碰撞。但是,如果库中的局面不断增多,则碰撞的概率是增加的。当然,在碰撞发生的时候,可以再挑选一个不同的 hash 算法,来保证现有库不发生碰撞。那么,hash 算法就成了必须研究的一门学问了。随着库容量的增大,可能出现这样的情况:更换多个 hash 算法,也无法避免碰撞。而发生碰撞的坏处是明显的:AI 会按照库中的着法,下出一步错棋!这 “有库” 还不如 “没库” 好呢!你的 AI 会下出错棋,对手的 AI 不出错,那对手就更有把握赢棋。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|捐助支持|无忧启动 ( 闽ICP备05002490号-1 )

闽公网安备 35020302032614号

GMT+8, 2025-12-10 20:56

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表