转自blueidea : Html5+javascript中国象棋 制作过程中用到的一些AI算法

象棋的预览地址:http://www.jnzo.com/chess/
代码未压缩,注释写的很清楚了,有兴趣的朋友可以一起改善
3.jpg

2012-2-4 01:47:28 上传
下载附件 (109.45 KB)
 



制作之前网上搜了一圈资料,关于中国象棋的还真少,不过倒是找到了国际象棋的资料,让我很钦佩的国际同行的专业精神,一个小小的象棋游戏,人家制定一系列标准,还组建了协会,开发了几种不同语言的引擎(可惜没有javascript的),佩服的五体投地。

制作过程先不说了,今儿就说说AI吧,有兴趣想学习html5制作过程的话,请留言,如果需要的话,后面补上。

————————————

言归正传,说到算法,可能许多程序员都会先皱眉头,其实那东西没有那么难,不是数学家也能写算法。

先说说人机对战AI的原理吧

树搜索

其实很简单
1.jpg

2012-2-4 01:35:46 上传
下载附件 (15.22 KB)
 



1、取得走法很简单,根据规则一步步的去算每个棋子哪个点能走,源文件里面有个 com.bylaw ,他下面是取得每种棋子走点的方法;

  1. //棋子能走的着点
  2. com.bylaw ={}
  3. //车
  4. com.bylaw.c = function (x,y,map,my){
  5. var d=[];
  6. //左侧检索
  7. for (var i=x-1; i>= 0; i--){
  8. if (map[y][i]) {
  9. if (com.mans[map[y][i]].my!=my) d.push([i,y]);
  10. break
  11. }else{
  12. d.push([i,y])
  13. }
  14. }
  15. //右侧检索
  16. for (var i=x+1; i <= 8; i++){
  17. if (map[y][i]) {
  18. if (com.mans[map[y][i]].my!=my) d.push([i,y]);
  19. break
  20. }else{
  21. d.push([i,y])
  22. }
  23. }
  24. //上检索
  25. for (var i = y-1 ; i >= 0; i--){
  26. if (map[i][x]) {
  27. if (com.mans[map[i][x]].my!=my) d.push([x,i]);
  28. break
  29. }else{
  30. d.push([x,i])
  31. }
  32. }
  33. //下检索
  34. for (var i = y+1 ; i<= 9; i++){
  35. if (map[i][x]) {
  36. if (com.mans[map[i][x]].my!=my) d.push([x,i]);
  37. break
  38. }else{
  39. d.push([x,i])
  40. }
  41. }
  42. return d;
  43. }
  44. //马
  45. com.bylaw.m = function (x,y,map,my){
  46. var d=[];
  47. //1点
  48. if ( y-2>= 0 && x+1<= 8 && !play.map[y-1][x] &&(!com.mans[map[y-2][x+1]] || com.mans[map[y-2][x+1]].my!=my)) d.push([x+1,y-2]);
  49. //2点
  50. if ( y-1>= 0 && x+2<= 8 && !play.map[y][x+1] &&(!com.mans[map[y-1][x+2]] || com.mans[map[y-1][x+2]].my!=my)) d.push([x+2,y-1]);
  51. //4点
  52. if ( y+1<= 9 && x+2<= 8 && !play.map[y][x+1] &&(!com.mans[map[y+1][x+2]] || com.mans[map[y+1][x+2]].my!=my)) d.push([x+2,y+1]);
  53. //5点
  54. if ( y+2<= 9 && x+1<= 8 && !play.map[y+1][x] &&(!com.mans[map[y+2][x+1]] || com.mans[map[y+2][x+1]].my!=my)) d.push([x+1,y+2]);
  55. //7点
  56. if ( y+2<= 9 && x-1>= 0 && !play.map[y+1][x] &&(!com.mans[map[y+2][x-1]] || com.mans[map[y+2][x-1]].my!=my)) d.push([x-1,y+2]);
  57. //8点
  58. if ( y+1<= 9 && x-2>= 0 && !play.map[y][x-1] &&(!com.mans[map[y+1][x-2]] || com.mans[map[y+1][x-2]].my!=my)) d.push([x-2,y+1]);
  59. //10点
  60. if ( y-1>= 0 && x-2>= 0 && !play.map[y][x-1] &&(!com.mans[map[y-1][x-2]] || com.mans[map[y-1][x-2]].my!=my)) d.push([x-2,y-1]);
  61. //11点
  62. if ( y-2>= 0 && x-1>= 0 && !play.map[y-1][x] &&(!com.mans[map[y-2][x-1]] || com.mans[map[y-2][x-1]].my!=my)) d.push([x-1,y-2]);
  63. return d;
  64. }
  65. //相
  66. com.bylaw.x = function (x,y,map,my){
  67. var d=[];
  68. if (my===1){ //红方
  69. //4点半
  70. if ( y+2<= 9 && x+2<= 8 && !play.map[y+1][x+1] && (!com.mans[map[y+2][x+2]] || com.mans[map[y+2][x+2]].my!=my)) d.push([x+2,y+2]);
  71. //7点半
  72. if ( y+2<= 9 && x-2>= 0 && !play.map[y+1][x-1] && (!com.mans[map[y+2][x-2]] || com.mans[map[y+2][x-2]].my!=my)) d.push([x-2,y+2]);
  73. //1点半
  74. if ( y-2>= 5 && x+2<= 8 && !play.map[y-1][x+1] && (!com.mans[map[y-2][x+2]] || com.mans[map[y-2][x+2]].my!=my)) d.push([x+2,y-2]);
  75. //10点半
  76. if ( y-2>= 5 && x-2>= 0 && !play.map[y-1][x-1] && (!com.mans[map[y-2][x-2]] || com.mans[map[y-2][x-2]].my!=my)) d.push([x-2,y-2]);
  77. }else{
  78. //4点半
  79. if ( y+2<= 4 && x+2<= 8 && !play.map[y+1][x+1] && (!com.mans[map[y+2][x+2]] || com.mans[map[y+2][x+2]].my!=my)) d.push([x+2,y+2]);
  80. //7点半
  81. if ( y+2<= 4 && x-2>= 0 && !play.map[y+1][x-1] && (!com.mans[map[y+2][x-2]] || com.mans[map[y+2][x-2]].my!=my)) d.push([x-2,y+2]);
  82. //1点半
  83. if ( y-2>= 0 && x+2<= 8 && !play.map[y-1][x+1] && (!com.mans[map[y-2][x+2]] || com.mans[map[y-2][x+2]].my!=my)) d.push([x+2,y-2]);
  84. //10点半
  85. if ( y-2>= 0 && x-2>= 0 && !play.map[y-1][x-1] && (!com.mans[map[y-2][x-2]] || com.mans[map[y-2][x-2]].my!=my)) d.push([x-2,y-2]);
  86. }
  87. return d;
  88. }
  89. //士
  90. com.bylaw.s = function (x,y,map,my){
  91. var d=[];
  92. if (my===1){ //红方
  93. //4点半
  94. if ( y+1<= 9 && x+1<= 5 && (!com.mans[map[y+1][x+1]] || com.mans[map[y+1][x+1]].my!=my)) d.push([x+1,y+1]);
  95. //7点半
  96. if ( y+1<= 9 && x-1>= 3 && (!com.mans[map[y+1][x-1]] || com.mans[map[y+1][x-1]].my!=my)) d.push([x-1,y+1]);
  97. //1点半
  98. if ( y-1>= 7 && x+1<= 5 && (!com.mans[map[y-1][x+1]] || com.mans[map[y-1][x+1]].my!=my)) d.push([x+1,y-1]);
  99. //10点半
  100. if ( y-1>= 7 && x-1>= 3 && (!com.mans[map[y-1][x-1]] || com.mans[map[y-1][x-1]].my!=my)) d.push([x-1,y-1]);
  101. }else{
  102. //4点半
  103. if ( y+1<= 2 && x+1<= 5 && (!com.mans[map[y+1][x+1]] || com.mans[map[y+1][x+1]].my!=my)) d.push([x+1,y+1]);
  104. //7点半
  105. if ( y+1<= 2 && x-1>= 3 && (!com.mans[map[y+1][x-1]] || com.mans[map[y+1][x-1]].my!=my)) d.push([x-1,y+1]);
  106. //1点半
  107. if ( y-1>= 0 && x+1<= 5 && (!com.mans[map[y-1][x+1]] || com.mans[map[y-1][x+1]].my!=my)) d.push([x+1,y-1]);
  108. //10点半
  109. if ( y-1>= 0 && x-1>= 3 && (!com.mans[map[y-1][x-1]] || com.mans[map[y-1][x-1]].my!=my)) d.push([x-1,y-1]);
  110. }
  111. return d;
  112. }
  113. //将
  114. com.bylaw.j = function (x,y,map,my){
  115. var d=[];
  116. var isNull=(function (y1,y2){
  117. var y1=com.mans["j0"].y;
  118. var x1=com.mans["J0"].x;
  119. var y2=com.mans["J0"].y;
  120. for (var i=y1-1; i>y2; i--){
  121. if (map[i][x1]) return false;
  122. }
  123. return true;
  124. })();
  125. if (my===1){ //红方
  126. //下
  127. if ( y+1<= 9 && (!com.mans[map[y+1][x]] || com.mans[map[y+1][x]].my!=my)) d.push([x,y+1]);
  128. //上
  129. if ( y-1>= 7 && (!com.mans[map[y-1][x]] || com.mans[map[y-1][x]].my!=my)) d.push([x,y-1]);
  130. //老将对老将的情况
  131. if ( com.mans["j0"].x == com.mans["J0"].x &&isNull) d.push([com.mans["J0"].x,com.mans["J0"].y]);
  132. }else{
  133. //下
  134. if ( y+1<= 2 && (!com.mans[map[y+1][x]] || com.mans[map[y+1][x]].my!=my)) d.push([x,y+1]);
  135. //上
  136. if ( y-1>= 0 && (!com.mans[map[y-1][x]] || com.mans[map[y-1][x]].my!=my)) d.push([x,y-1]);
  137. //老将对老将的情况
  138. if ( com.mans["j0"].x == com.mans["J0"].x &&isNull) d.push([com.mans["j0"].x,com.mans["j0"].y]);
  139. }
  140. //右
  141. if ( x+1<= 5 && (!com.mans[map[y][x+1]] || com.mans[map[y][x+1]].my!=my)) d.push([x+1,y]);
  142. //左
  143. if ( x-1>= 3 && (!com.mans[map[y][x-1]] || com.mans[map[y][x-1]].my!=my))d.push([x-1,y]);
  144. return d;
  145. }
  146. //炮
  147. com.bylaw.p = function (x,y,map,my){
  148. var d=[];
  149. //左侧检索
  150. var n=0;
  151. for (var i=x-1; i>= 0; i--){
  152. if (map[y][i]) {
  153. if (n==0){
  154. n++;
  155. continue;
  156. }else{
  157. if (com.mans[map[y][i]].my!=my) d.push([i,y]);
  158. break
  159. }
  160. }else{
  161. if(n==0) d.push([i,y])
  162. }
  163. }
  164. //右侧检索
  165. var n=0;
  166. for (var i=x+1; i <= 8; i++){
  167. if (map[y][i]) {
  168. if (n==0){
  169. n++;
  170. continue;
  171. }else{
  172. if (com.mans[map[y][i]].my!=my) d.push([i,y]);
  173. break
  174. }
  175. }else{
  176. if(n==0) d.push([i,y])
  177. }
  178. }
  179. //上检索
  180. var n=0;
  181. for (var i = y-1 ; i >= 0; i--){
  182. if (map[i][x]) {
  183. if (n==0){
  184. n++;
  185. continue;
  186. }else{
  187. if (com.mans[map[i][x]].my!=my) d.push([x,i]);
  188. break
  189. }
  190. }else{
  191. if(n==0) d.push([x,i])
  192. }
  193. }
  194. //下检索
  195. var n=0;
  196. for (var i = y+1 ; i<= 9; i++){
  197. if (map[i][x]) {
  198. if (n==0){
  199. n++;
  200. continue;
  201. }else{
  202. if (com.mans[map[i][x]].my!=my) d.push([x,i]);
  203. break
  204. }
  205. }else{
  206. if(n==0) d.push([x,i])
  207. }
  208. }
  209. return d;
  210. }
  211. //卒
  212. com.bylaw.z = function (x,y,map,my){
  213. var d=[];
  214. if (my===1){ //红方
  215. //上
  216. if ( y-1>= 0 && (!com.mans[map[y-1][x]] || com.mans[map[y-1][x]].my!=my)) d.push([x,y-1]);
  217. //右
  218. if ( x+1<= 8 && y<=4 && (!com.mans[map[y][x+1]] || com.mans[map[y][x+1]].my!=my)) d.push([x+1,y]);
  219. //左
  220. if ( x-1>= 0 && y<=4 && (!com.mans[map[y][x-1]] || com.mans[map[y][x-1]].my!=my))d.push([x-1,y]);
  221. }else{
  222. //下
  223. if ( y+1<= 9 && (!com.mans[map[y+1][x]] || com.mans[map[y+1][x]].my!=my)) d.push([x,y+1]);
  224. //右
  225. if ( x+1<= 8 && y>=6 && (!com.mans[map[y][x+1]] || com.mans[map[y][x+1]].my!=my)) d.push([x+1,y]);
  226. //左
  227. if ( x-1>= 0 && y>=6 && (!com.mans[map[y][x-1]] || com.mans[map[y][x-1]].my!=my))d.push([x-1,y]);
  228. }
  229. return d;
  230. }
复制代码

2、根据各个走法,去生成新的棋谱,然后搜索新的走法
3、评估
评估是个很麻烦的事情,一开始我考虑的是给每个棋子价值,比如
士 20 象 20 马 90 车 200 炮 100 兵 40

不过会有一个问题,每个棋子在不同的局面,不同的位置,其价值是不同的,最明显的就是“士”,过河以后明显价值增加了不少,而且越靠近对方老将价值越大,甚至到最后会超越马或者炮,所以我把价值评估做了改进,详情看源码com.value这块。这里的棋子价值,借鉴了一些其他象棋游戏的经验

  1. com.value = {
  2. //车价值
  3. c:[
  4. [206, 208, 207, 213, 214, 213, 207, 208, 206],
  5. [206, 212, 209, 216, 233, 216, 209, 212, 206],
  6. [206, 208, 207, 214, 216, 214, 207, 208, 206],
  7. [206, 213, 213, 216, 216, 216, 213, 213, 206],
  8. [208, 211, 211, 214, 215, 214, 211, 211, 208],
  9. [208, 212, 212, 214, 215, 214, 212, 212, 208],
  10. [204, 209, 204, 212, 214, 212, 204, 209, 204],
  11. [198, 208, 204, 212, 212, 212, 204, 208, 198],
  12. [200, 208, 206, 212, 200, 212, 206, 208, 200],
  13. [194, 206, 204, 212, 200, 212, 204, 206, 194]
  14. ],
  15. //马价值
  16. m:[
  17. [90, 90, 90, 96, 90, 96, 90, 90, 90],
  18. [90, 96,103, 97, 94, 97,103, 96, 90],
  19. [92, 98, 99,103, 99,103, 99, 98, 92],
  20. [93,108,100,107,100,107,100,108, 93],
  21. [90,100, 99,103,104,103, 99,100, 90],
  22. [90, 98,101,102,103,102,101, 98, 90],
  23. [92, 94, 98, 95, 98, 95, 98, 94, 92],
  24. [93, 92, 94, 95, 92, 95, 94, 92, 93],
  25. [85, 90, 92, 93, 78, 93, 92, 90, 85],
  26. [88, 85, 90, 88, 90, 88, 90, 85, 88]
  27. ],
  28. //相价值
  29. x:[
  30. [0, 0,20, 0, 0, 0,20, 0, 0],
  31. [0, 0, 0, 0, 0, 0, 0, 0, 0],
  32. [0, 0, 0, 0,23, 0, 0, 0, 0],
  33. [0, 0, 0, 0, 0, 0, 0, 0, 0],
  34. [0, 0,20, 0, 0, 0,20, 0, 0],
  35. [0, 0,20, 0, 0, 0,20, 0, 0],
  36. [0, 0, 0, 0, 0, 0, 0, 0, 0],
  37. [18,0, 0, 0,23, 0, 0, 0,18],
  38. [0, 0, 0, 0, 0, 0, 0, 0, 0],
  39. [0, 0,20, 0, 0, 0,20, 0, 0]
  40. ],
  41. //士价值
  42. s:[
  43. [0, 0, 0,20, 0,20, 0, 0, 0],
  44. [0, 0, 0, 0,23, 0, 0, 0, 0],
  45. [0, 0, 0,20, 0,20, 0, 0, 0],
  46. [0, 0, 0, 0, 0, 0, 0, 0, 0],
  47. [0, 0, 0, 0, 0, 0, 0, 0, 0],
  48. [0, 0, 0, 0, 0, 0, 0, 0, 0],
  49. [0, 0, 0, 0, 0, 0, 0, 0, 0],
  50. [0, 0, 0,20, 0,20, 0, 0, 0],
  51. [0, 0, 0, 0,23, 0, 0, 0, 0],
  52. [0, 0, 0,20, 0,20, 0, 0, 0]
  53. ],
  54. //奖价值
  55. j:[
  56. [0, 0, 0, 8888, 8888, 8888, 0, 0, 0],
  57. [0, 0, 0, 8888, 8888, 8888, 0, 0, 0],
  58. [0, 0, 0, 8888, 8888, 8888, 0, 0, 0],
  59. [0, 0, 0, 0, 0, 0, 0, 0, 0],
  60. [0, 0, 0, 0, 0, 0, 0, 0, 0],
  61. [0, 0, 0, 0, 0, 0, 0, 0, 0],
  62. [0, 0, 0, 0, 0, 0, 0, 0, 0],
  63. [0, 0, 0, 8888, 8888, 8888, 0, 0, 0],
  64. [0, 0, 0, 8888, 8888, 8888, 0, 0, 0],
  65. [0, 0, 0, 8888, 8888, 8888, 0, 0, 0]
  66. ],
  67. //炮价值
  68. p:[
  69. [100, 100, 96, 91, 90, 91, 96, 100, 100],
  70. [ 98, 98, 96, 92, 89, 92, 96, 98, 98],
  71. [ 97, 97, 96, 91, 92, 91, 96, 97, 97],
  72. [ 96, 99, 99, 98, 100, 98, 99, 99, 96],
  73. [ 96, 96, 96, 96, 100, 96, 96, 96, 96],
  74. [ 95, 96, 99, 96, 100, 96, 99, 96, 95],
  75. [ 96, 96, 96, 96, 96, 96, 96, 96, 96],
  76. [ 97, 96, 100, 99, 101, 99, 100, 96, 97],
  77. [ 96, 97, 98, 98, 98, 98, 98, 97, 96],
  78. [ 96, 96, 97, 99, 99, 99, 97, 96, 96]
  79. ],
  80. //卒价值
  81. z:[
  82. [ 9, 9, 9, 11, 13, 11, 9, 9, 9],
  83. [19, 24, 34, 42, 44, 42, 34, 24, 19],
  84. [19, 24, 32, 37, 37, 37, 32, 24, 19],
  85. [19, 23, 27, 29, 30, 29, 27, 23, 19],
  86. [14, 18, 20, 27, 29, 27, 20, 18, 14],
  87. [ 7, 0, 13, 0, 16, 0, 13, 0, 7],
  88. [ 7, 0, 7, 0, 15, 0, 7, 0, 7],
  89. [ 0, 0, 0, 0, 0, 0, 0, 0, 0],
  90. [ 0, 0, 0, 0, 0, 0, 0, 0, 0],
  91. [ 0, 0, 0, 0, 0, 0, 0, 0, 0]
  92. ]
  93. }
  94. //黑子为红字价值位置的倒置
  95. com.value.C = com.arr2Clone(com.value.c).reverse();
  96. com.value.M = com.arr2Clone(com.value.m).reverse();
  97. com.value.X = com.value.x;
  98. com.value.S = com.value.s;
  99. com.value.J = com.value.j;
  100. com.value.P = com.arr2Clone(com.value.p).reverse();
  101. com.value.Z = com.arr2Clone(com.value.z).reverse();
复制代码

有了价值,评估就是很简单的事啊,双方健在棋子的价值差,就是这个分支的最后得分。

  1. //评估棋局 取得棋盘双方棋子价值差
  2. AI.evaluate = function (map,my){
  3. var val=0;
  4. for (var i=0; i<map.length; i++){
  5. for (var n=0; n<map[i].length; n++){
  6. var key = map[i][n];
  7. if (key){
  8. val += play.mans[key].value[i][n] * play.mans[key].my;
  9. }
  10. }
  11. }
  12. val+=Math.floor( Math.random() * 10); //让AI走棋增加随机元素
  13. AI.number++;
  14. return val*my;
  15. }
复制代码

给我一个指点,我能撬动地球
有了这个算法,我可以骄傲的说只要给我足够强大的计算机,和足够长的计算时间,我就能算出必胜的走棋方法,信不,你信不信反正我是信了。

不过我大概统计了一下,中国象棋平均每步大概有42个走法,如果这盘期走了100个回合的话,那么就是42的200次方个结果,大概需要计算这么多分支,4.4653764701754888195833543328375e+324,大概也就够现在最强大的服务器计算几百年的工作量吧。

看来是不现实的,还是看看现实一点的吧

最大最小搜索

只搜到某个深度,在这个深度来进行评估,决定AI走哪一步棋。
这个时候又有个问题,我走棋以后,对手也不一定选择哪步期来对付我啊,我该怎么判断对手会走哪一步呢,想这个问题之前,看看下面的题目就懂了

2.jpg

2012-2-4 01:42:00 上传
下载附件 (34.5 KB)
 



现在有30个桔子,有大的有小的,有好的有烂的,我们按照好坏级别给他评分了
随机平分为3堆,两个人抢桔子,规则是,A选择其中的一堆,B在A选的那堆里面,选一个给A,如果你是A,你该怎么选呢?
选NO1很有诱惑力,因为里面有最好的桔子16,不过你选了NO1后,你是得不到16的,因为B会拿-6给你,这样的话,对你最有优势的选择应该是NO3,也就是我要选的是每堆里面最次的那个桔子,里面最好的所在的那一堆,比较绕,不知道你听明白没。

走棋也是一样的,对方肯定是会选择对他损失最小的走法来对付你,是的,这就是最大最小算法的原理,想想如何应用到程序中。

好了,AI的基础算法完成了,依靠这套理论就能写出能够走棋的AI了,欢呼吧,虽然性能很低,虽然走得傻傻的。

不知道你能看明白不,确实比较绕,加上本人表达能力有限,有啥意见多提啊,下次改进

转载于:https://www.cnblogs.com/KaPengQiang/articles/2413211.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值