用javascript实现五子棋(二)

前面已经实现了整个游戏流程,现在讲五子棋AI的实现,五子棋AI的实现方法主要是以下几种:

1.查表法,保存一些棋谱,电脑下棋的时候自动匹配

2.分值法:对棋盘上的每一个空位进行检索,假设这个空位放某种棋子,然后根据不同的情况给出不同的分值,在分值最高的地方下棋

3.博弈树:列出所有情况,用极大极小值搜索选出最佳路径

4.电脑每次输掉棋局后,保存记录,以后不再重复

这里只说分值法:我的想法是把棋子的状态分出来:活1,死1,活2,死2,眠2,活3,死3,眠3,活4,死4,眠4,成5

这里有些情况的效果是一样的比如,成5,活4,双活3,双眠4这些都是必赢的,好了不说了,上代码最直观

/**五子棋AI
 *思路:对棋盘上的每一个空格进行估分,电脑优先在分值高的点落子
 * 棋型:
 * 〖五连〗只有五枚同色棋子在一条阳线或阴线上相邻成一排
 * 〖成五〗含有五枚同色棋子所形成的连,包括五连和长连。
 * 〖活四〗有两个点可以成五的四。
 * 〖冲四〗只有一个点可以成五的四。
 * 〖死四〗不能成五的四。
 * 〖三〗在一条阳线或阴线上连续相邻的5个点上只有三枚同色棋子的棋型。
 * 〖活三〗再走一着可以形成活四的三。
 * 〖连活三〗即:连的活三(同色棋子在一条阳线或阴线上相邻成一排的活三)。简称“连三”。
 * 〖跳活三〗中间隔有一个空点的活三。简称“跳三”。
 * 〖眠三〗再走一着可以形成冲四的三。
 * 〖死三〗不能成五的三。
 * 〖二〗在一条阳线或阴线上连续相邻的5个点上只有两枚同色棋子的棋型。
 * 〖活二〗再走一着可以形成活三的二。
 * 〖连活二〗即:连的活二(同色棋子在一条阳线或阴线上相邻成一排的活二)。简称“连二”。
 * 〖跳活二〗中间隔有一个空点的活二。简称“跳二”。
 * 〖大跳活二〗中间隔有两个空点的活二。简称“大跳二”。
 * 〖眠二〗再走一着可以形成眠三的二。
 * 〖死二〗不能成五的二。
 * 〖先手〗对方必须应答的着法,相对于先手而言,冲四称为“绝对先手”。
 * 〖三三〗一子落下同时形成两个活三。也称“双三”。
 * 〖四四〗一子落下同时形成两个冲四。也称“双四”。
 * 〖四三〗一子落下同时形成一个冲四和一个活三。
 * 分值表
 * 成5:100000分
 * 活4:10000分
 * 活3+冲4:5000分
 * 眠3+活2:2000分
 * 眠2+眠1:1分
 * 死棋即不能成5的是0分
 * @return {[type]} [description]
 */
function getPosition() {
	var a = new Array(2);
	var score = 0;
	for (var x = 0; x < 15; x++) {
		for (var y = 0; y < 15; y++) {
			if (chessData[x][y] == 0) {
				if (judge(x, y) > score) {
					score = judge(x, y);
					a[0] = x;
					a[1] = y;
				}
			}
		}
	}
	return a;
}

function AIplay() {
	var str = getPosition();
	// console.log("智能AI将在下面坐标下棋:" + str[0] + "," + str[1]);
	doCheck(str[0], str[1]);
}

function judge(x, y) {
	var a = parseInt(leftRight(x, y, 1)) + parseInt(topBottom(x, y, 1)) + parseInt(rightBottom(x, y, 1)) + parseInt(rightTop(x, y, 1))+100; //判断白棋走该位置的得分
	var b = parseInt(leftRight(x, y, 2)) + parseInt(topBottom(x, y, 2)) + parseInt(rightBottom(x, y, 2)) + parseInt(rightTop(x, y, 2)); //判断黑棋走该位置的得分
	var result = a + b;
	// console.log("我计算出了" + x + "," + y + "这个位置的得分为" + result);
	return result; //返回黑白棋下该位置的总和
}

function leftRight(x, y, num) {
	var death = 0; //0表示两边都没堵住,且可以成5,1表示一边堵住了,可以成5,2表示是死棋,不予考虑
	var live = 0;
	var count = 0;
	var arr = new Array(15);
	for (var i = 0; i< 15; i++) {
		arr[i] = new Array(15);
		for (var j = 0; j < 15; j++) {
			arr[i][j] = chessData[i][j];
		}
	}
	arr[x][y] = num;
	for (var i = x; i >= 0; i--) {
		if (arr[i][y] == num) {
			count++;
		} else if (arr[i][y] == 0) {
			live += 1; //空位标记
			i = -1;
		} else {
			death += 1; //颜色不同是标记一边被堵住
			i = -1;
		}
	}
	for (var i = x; i <= 14; i++) {
		if (arr[i][y] == num) {
			count++;
		} else if (arr[i][y] == 0) {
			live += 1; //空位标记
			i = 100;
		} else {
			death += 1;
			i = 100;
		}
	}
	count -= 1;
	// console.log(x + "," + y + "位置上的左右得分为" + model(count, death));
	return model(count, death);
}

function topBottom(x, y, num) {
	var death = 0; //0表示两边都没堵住,且可以成5,1表示一边堵住了,可以成5,2表示是死棋,不予考虑
	var live = 0;
	var count = 0;
	var arr = new Array(15);
	for (var i = 0; i< 15; i++) {
		arr[i] = new Array(15);
		for (var j = 0; j < 15; j++) {
			arr[i][j] = chessData[i][j];
		}
	}
	arr[x][y] = num;
	for (var i = y; i >= 0; i--) {
		if (arr[x][i] == num) {
			count++;
		} else if (arr[x][i] == 0) {
			live += 1; //空位标记
			i = -1;
		} else {
			death += 1;
			i = -1;
		}
	}
	for (var i = y; i <= 14; i++) {
		if (arr[x][i] == num) {
			count++;
		} else if (arr[x][i] == 0) {
			live += 1; //空位标记
			i = 100;
		} else {
			death += 1;
			i = 100;
		}
	}
	count -= 1;
	// console.log(x + "," + y + "位置上的上下斜得分为" + model(count, death));
	return model(count, death);
}

function rightBottom(x, y, num) {
	var death = 0; //0表示两边都没堵住,且可以成5,1表示一边堵住了,可以成5,2表示是死棋,不予考虑
	var live = 0;
	var count = 0;
	var arr = new Array(15);
	for (var i = 0; i< 15; i++) {
		arr[i] = new Array(15);
		for (var j = 0; j < 15; j++) {
			arr[i][j] = chessData[i][j];
		}
	}
	arr[x][y] = num;
	for (var i = x, j = y; i >= 0 && j >= 0;) {
		if (arr[i][j] == num) {
			count++;
		} else if (arr[i][j] == 0) {
			live += 1; //空位标记
			i = -1;
		} else {
			death += 1;
			i = -1;
		}
		i--;
		j--;
	}
	for (var i = x, j = y; i <= 14 && j <= 14;) {
		if (arr[i][j] == num) {
			count++;
		} else if (arr[i][j] == 0) {
			live += 1; //空位标记
			i = 100;
		} else {
			death += 1;
			i = 100;
		}
		i++;
		j++;
	}
	count -= 1;
	// console.log(x + "," + y + "位置上的右下斜得分为" + model(count, death));
	return model(count, death);
}

function rightTop(x, y, num) {
	var death = 0; //0表示两边都没堵住,且可以成5,1表示一边堵住了,可以成5,2表示是死棋,不予考虑
	var live = 0;
	var count = 0;
	var arr = new Array(15);
	for (var i = 0; i< 15; i++) {
		arr[i] = new Array(15);
		for (var j = 0; j < 15; j++) {
			arr[i][j] = chessData[i][j];
		}
	}
	arr[x][y] = num;
	for (var i = x, j = y; i >= 0 && j <= 14;) {
		if (arr[i][j] == num) {
			count++;
		} else if (arr[i][j] == 0) {
			live += 1; //空位标记
			i = -1;
		} else {
			death += 1;
			i = -1;
		}
		i--;
		j++;
	}
	for (var i = x, j = y; i <= 14 && j >= 0;) {
		if (arr[i][j] == num) {
			count++;
		} else if (arr[i][j] == 0) {
			live += 1; //空位标记
			i = 100;
		} else {
			death += 1;
			i = 100;
		}
		i++;
		j--;
	}
	count -= 1;
	// console.log(x + "," + y + "位置上的右上斜得分为" + model(count, death));
	return model(count, death);
}
/**罗列相等效果的棋型(此处只考虑常见的情况,双成五,双活四等少概率事件不考虑)
 * 必胜棋:成五=活四==双活三=冲四+活三=双冲四
 * 
 * 
 * 
 */
function model(count, death) {
	// console.log("count" + count + "death" + death);
	var LEVEL_ONE = 0;//单子
	var LEVEL_TWO = 1;//眠2,眠1
	var LEVEL_THREE = 1500;//眠3,活2
	var LEVEL_FOER = 4000;//冲4,活3
	var LEVEL_FIVE = 10000;//活4
	var LEVEL_SIX = 100000;//成5
	if (count == 1 && death == 1) {
		return LEVEL_TWO; //眠1
	} else if (count == 2) {
		if (death == 0) {
			return LEVEL_THREE; //活2
		} else if (death == 1) {
			return LEVEL_TWO; //眠2
		} else {
			return LEVEL_ONE; //死棋
		}
	} else if (count == 3) {
		if (death == 0) {
			return LEVEL_FOER; //活3
		} else if (death == 1) {
			return LEVEL_THREE; //眠3
		} else {
			return LEVEL_ONE; //死棋
		}
	} else if (count == 4) {
		if (death == 0) {
			return LEVEL_FIVE; //活4
		} else if (death == 1) {
			return LEVEL_FOER; //冲4
		} else {
			return LEVEL_ONE; //死棋
		}
	} else if (count == 5) {
		return LEVEL_SIX; //成5
	}
	return LEVEL_ONE;
}
</script>
</html>
源码http://pan.baidu.com/s/1sjONm01

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值