<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style type="text/css">
#box1 table{
border: 0px;
border-collapse: collapse;
cursor: pointer;
}
#box1 th {
border: 0px;
border-collapse: collapse;
cursor: pointer;
}
#box1 td{
border: 1px solid #000;
border-collapse: collapse;
cursor: pointer;
}
#box2 table{
border-collapse: collapse;
border: 0px;
}
#box2 th {
border-collapse: collapse;
border: 0px;
}
#box2 td{
border-collapse: collapse;
border: 1px solid transparent;
border-radius:20px;
}
.color_human{
background-color: white;
}
.color_machine{
background-color: black;
}
.color_machine_win{
background-color: red;
}
.color_human_win{
background-color: green;
}
#box1{
position: absolute;
left: 100px;
top: 100px;
border: 0px;
background-color: gray;
}
#box2{
position: absolute;
left: 110px;
top: 110px;
border: 0px;
}
</style>
</head>
<body>
<div id="box1"></div>
<div id="box2"></div>
</body>
<script type="text/javascript">
var map_code_td = new Map();
var map_code_color = new Map();
var map_code_score = new Map();
var win = false;
var color_human = "color_human";
var color_machine = "color_machine"
var codes_win = [];
var color_blank = "xxx";
var row_count = 20;
var col_count = 20;
// 辅助帮助显示横竖纹的底色棋盘,没有任何事件和业务逻辑
function initBox1( ) {
var table = document.createElement("table");
table.rules = 'all' ;
for (var rowNum = 1; rowNum <= row_count; rowNum++) {
var tr = document.createElement("tr");
for (var colNum = 1; colNum <= col_count; colNum++) {
var td = document.createElement("td");
td.width = 20;
td.height = 20;
tr.appendChild(td);
}
table.appendChild(tr);
}
document.getElementById( "box1" ).appendChild(table);
}
function initBox2( ) {
var table = document.createElement("table");
table.rules = 'all' ;
for (var rowNum = 1; rowNum <= row_count; rowNum++) {
var tr = document.createElement("tr");
for (var colNum = 1; colNum <= col_count; colNum++) {
var td = document.createElement("td");
td.width = 20;
td.height = 20;
tr.appendChild(td);
var code = rowNum + "_" + colNum;
map_code_td.set( code,td );
map_code_color.set( code,color_blank );
}
table.appendChild(tr);
}
document.getElementById( "box2" ).appendChild(table);
table.addEventListener( "click", clickChess );
}
initBox1( );
initBox2( );
// 获取到点击的 td,如果该人类落子,则将td的颜色改为 人类棋子颜色,否则该机器落子,将颜色改为机器棋子颜色
var shouldHumanPoint = true;
function clickChess(){
if( win ){
return;
}
if( !shouldHumanPoint ){
alert("稍等,机器落子后您再落子!");
return;
}
var td = event.srcElement;
var rowNum = td.parentElement.rowIndex + 1;
var colNum = td.cellIndex + 1;
if( isAlreadyFillPoint( rowNum,colNum ) ){
alert( "该位置已落子" );
return;
}
// 执行人类落子操作
td.className = color_human;
var code = rowNum + "_" + colNum;
map_code_color.set( code,color_human );
shouldHumanPoint = false;
// 人类落子后检测是否胜利
winCheck();
if( win ){
highlightFiveWinCodes( true );
alert("人类胜利");
return;
}
window.setTimeout(function(){
// 机器开始落子
machinePoint();
// 机器落子后检测是否胜利
winCheck();
if( win ){
highlightFiveWinCodes( false );
alert("机器胜利");
}
},1000);
}
// 高亮5个连成线的棋子
function highlightFiveWinCodes( humanWin ){
for(var i=0;i<5;i++){
var td = map_code_td.get( codes_win[i] );
if( humanWin ){
td.className = "color_human_win";
}else{
td.className = "color_machine_win";
}
}
}
// 机器落子操作
function machinePoint(){
// 首先检查人类是否存在机器不拦则人类必胜的情况,比如存在人类的 "空白白白空空"两端的 空的数量满足一端>=1 ,一端>1,则机器必须落在 "白白白"的两端
// 空白白白空空、空空白白白空
// 人类必胜检测
// 人类必胜的六连子,检测到有,则机器落在六连子的最后一个子上
var sixCodes = humanMustWinCheck();
if( sixCodes ){
console.log("检测到存在人类的必胜六连子,试图拦截:");
console.log(sixCodes);
var code = sixCodes[5];
var td = map_code_td.get( code );
td.className = color_machine;
map_code_color.set( code,color_machine );
shouldHumanPoint = true;
return;
}
// 机器开始落子,需要找到一个得分最高的格子开始落子
// 从下面 row_count * col_count 格子中找到得分最高的一个格子
var maxScore = 0;
var rowNum_maxScore = 0;
var colNum_maxScore = 0;
for( var rowNum = 1;rowNum <= row_count;rowNum++ ){
for( var colNum = 1;colNum <= col_count;colNum++ ){
if( isAlreadyFillPoint( rowNum,colNum ) ){
// 该位置已经落子了,不考虑
continue;
}
var score = calculateScore( rowNum, colNum );
if( score > maxScore ){
maxScore = score;
rowNum_maxScore = rowNum;
colNum_maxScore = colNum;
}
}
}
console.log( "机器应该落在" + rowNum_maxScore + "行" + colNum_maxScore + "列,得分:" + maxScore );
var code = rowNum_maxScore + "_" + colNum_maxScore;
var td = map_code_td.get( code );
td.className = color_machine;
map_code_color.set( code,color_machine );
shouldHumanPoint = true;
}
// 检测指定位置是否已经落子了
function isAlreadyFillPoint( rowNum,colNum ){
var code = rowNum + "_" + colNum;
var color = map_code_color.get( code );
if( color == color_human || color == color_machine ){
return true;
}
return false;
}
// 得分表,该程序的精髓,其实本程序未使用任何算法,只是使用了这个得分表,写这个得分表的人比较厉害,程序厉不厉害也是取决于得分表设计的精不精妙
function calculateScoreForForHumanMachineColorCount( count_humanColor,count_machineColor){
if( count_humanColor > 0 && count_machineColor > 0 ){
return 0;
}else if( count_humanColor == 0 && count_machineColor == 0 ){
return 7;
}else if( count_humanColor == 0 && count_machineColor == 1 ){
return 35;
}else if( count_humanColor == 0 && count_machineColor == 2 ){
return 800;
}else if( count_humanColor == 0 && count_machineColor == 3 ){
return 15000;
}else if( count_humanColor == 0 && count_machineColor == 4 ){
return 800000;
}else if( count_humanColor == 1 && count_machineColor == 0 ){
return 15;
}else if( count_humanColor == 2 && count_machineColor == 0 ){
return 400;
}else if( count_humanColor == 3 && count_machineColor == 0 ){
return 1800;
}else if( count_humanColor == 4 && count_machineColor == 0 ){
return 100000;
}else{
return 0;
}
}
// 为该五连子计算得分
function calculateScoreForFiveCodes( fiveCodes ){
// 统计人类棋子和机器棋子的个数
var count_humanColor = 0;
var count_machineColor = 0;
for( var i=0;i<5;i++ ){
var code = fiveCodes[i];
var color = map_code_color.get( code );
if( !color ){
// 可能由于传递的 fiveCodes 不是有效的五连子导致的,比如 "16_1"、"-1_2"等,因为前面未进行最大最小合法角标的限制
return 0;
}
if( color == color_human ){
count_humanColor++;
}else if( color == color_machine ){
count_machineColor++;
}
}
return calculateScoreForForHumanMachineColorCount( count_humanColor, count_machineColor );
}
function calculateScore(rowNum, colNum){
var totalScore = 0;
// 该格子所在行( 左-->右:行不变,列++ )
// 统计该格子作为第1、2、3、4、5个子时候的五连子的得分之和
for( var i=0; i<5; i++){
var fiveCodes = [];
fiveCodes.push( rowNum + "_" + ( colNum + 0 - i ) );
fiveCodes.push( rowNum + "_" + ( colNum + 1 - i ) );
fiveCodes.push( rowNum + "_" + ( colNum + 2 - i ) );
fiveCodes.push( rowNum + "_" + ( colNum + 3 - i ) );
fiveCodes.push( rowNum + "_" + ( colNum + 4 - i ) );
totalScore += calculateScoreForFiveCodes( fiveCodes );
}
// 该格子所在列( 上->下:行++,列不变 )
// 统计该格子作为第1、2、3、4、5个子时候的五连子的得分之和
for( var i=0; i<5; i++){
var fiveCodes = [];
fiveCodes.push( ( rowNum + 0 - i ) + "_" + colNum );
fiveCodes.push( ( rowNum + 1 - i ) + "_" + colNum );
fiveCodes.push( ( rowNum + 2 - i ) + "_" + colNum );
fiveCodes.push( ( rowNum + 3 - i ) + "_" + colNum );
fiveCodes.push( ( rowNum + 4 - i ) + "_" + colNum );
totalScore += calculateScoreForFiveCodes( fiveCodes );
}
// 该格子所在正斜线( 左下-->右上:行--,列++ )
// 统计该格子作为第1、2、3、4、5个子时候的五连子的得分之和
for( var i=0; i<5; i++){
var fiveCodes = [];
fiveCodes.push( ( rowNum + i - 0 ) + "_" + ( colNum + 0 - i ) );
fiveCodes.push( ( rowNum + i - 1 ) + "_" + ( colNum + 1 - i ) );
fiveCodes.push( ( rowNum + i - 2 ) + "_" + ( colNum + 2 - i ) );
fiveCodes.push( ( rowNum + i - 3 ) + "_" + ( colNum + 3 - i ) );
fiveCodes.push( ( rowNum + i - 4 ) + "_" + ( colNum + 4 - i ) );
totalScore += calculateScoreForFiveCodes( fiveCodes );
}
// 该格子所在反斜线( 左上-->右下:行++,列++ )
// 统计该格子作为第1、2、3、4、5个子时候的五连子的得分之和
for( var i=0; i<5; i++){
var fiveCodes = [];
fiveCodes.push( ( rowNum + 0 - i ) + "_" + ( colNum + 0 - i ) );
fiveCodes.push( ( rowNum + 1 - i ) + "_" + ( colNum + 1 - i ) );
fiveCodes.push( ( rowNum + 2 - i ) + "_" + ( colNum + 2 - i ) );
fiveCodes.push( ( rowNum + 3 - i ) + "_" + ( colNum + 3 - i ) );
fiveCodes.push( ( rowNum + 4 - i ) + "_" + ( colNum + 4 - i ) );
totalScore += calculateScoreForFiveCodes( fiveCodes );
}
return totalScore;
}
function winCheck(){
if( win ){
return;
}
// 检查全部的五连子,是否存在五个全是人类棋子或者五个全是机器棋子
// 检查全部列上的五连子
winCheck_col()
if( win ){
return;
}
// 检查全部行上的五连子
winCheck_row();
if( win ){
return;
}
// 检查全部正斜线上的五连子
winCheck_forwardSlash();
if( win ){
return;
}
// 检查全部反斜线上的五连子
winCheck_backSlash();
if( win ){
return;
}
}
function winCheck_forwardSlash(){
// 列--,行++
for( rowNum=1;rowNum<=row_count;rowNum++ ){
for( colNum=1;colNum<=col_count;colNum++ ){
var codes = [];
codes.push( rowNum + "_" + colNum );
codes.push( ( rowNum + 1 ) + "_" + ( colNum - 1 ) );
codes.push( ( rowNum + 2 ) + "_" + ( colNum - 2 ) );
codes.push( ( rowNum + 3 ) + "_" + ( colNum - 3 ) );
codes.push( ( rowNum + 4 ) + "_" + ( colNum - 4 ) );
if( isAllTargetColor( codes,color_human ) || isAllTargetColor( codes,color_machine ) ){
win = true;
}
if( win ){
for(var i=0;i<5;i++){
codes_win.push( codes[i] );
}
return;
}
}
}
}
function winCheck_backSlash(){
// 列++,行++
for( rowNum=1;rowNum<=row_count;rowNum++ ){
for( colNum=1;colNum<=col_count;colNum++ ){
var fiveCodes = [];
fiveCodes.push( rowNum + "_" + colNum );
fiveCodes.push( ( rowNum + 1 ) + "_" + ( colNum + 1 ) );
fiveCodes.push( ( rowNum + 2 ) + "_" + ( colNum + 2 ) );
fiveCodes.push( ( rowNum + 3 ) + "_" + ( colNum + 3 ) );
fiveCodes.push( ( rowNum + 4 ) + "_" + ( colNum + 4 ) );
if( isAllTargetColor( fiveCodes,color_human ) || isAllTargetColor( fiveCodes,color_machine ) ){
// 都是人类棋子 或者 机器棋子,表示已经分出胜负了
win = true;
}
if( win ){
for( var i=0;i<5;i++ ){
codes_win.push( fiveCodes[i] );
}
return;
}
}
}
}
function winCheck_col(){
// 检查全部列是否存在五连子
// 列不变,行++
for( var colNum=1;colNum<=col_count;colNum++ ){
for( var rowNum=1;rowNum<=row_count;rowNum++ ){
var fiveCodes = [];
fiveCodes.push( rowNum + "_" + colNum );
fiveCodes.push( ( rowNum + 1 ) + "_" + colNum );
fiveCodes.push( ( rowNum + 2 ) + "_" + colNum );
fiveCodes.push( ( rowNum + 3 ) + "_" + colNum );
fiveCodes.push( ( rowNum + 4 ) + "_" + colNum );
if( isAllTargetColor( fiveCodes,color_human ) || isAllTargetColor( fiveCodes,color_machine ) ){
// 已经分出胜负了
win = true;
}
if( win ){
for( var i=0;i<5;i++ ){
codes_win.push( fiveCodes[i] );
}
return;
}
}
}
}
function winCheck_row(){
// 行不变,列++
for( var rowNum=1;rowNum<=row_count;rowNum++ ){
for( var colNum=1;colNum<=col_count;colNum++ ){
var codes = [];
codes.push( rowNum + "_" + colNum );
codes.push( rowNum + "_" + ( colNum + 1 ) );
codes.push( rowNum + "_" + ( colNum + 2 ) );
codes.push( rowNum + "_" + ( colNum + 3 ) );
codes.push( rowNum + "_" + ( colNum + 4 ) );
if( isAllTargetColor( codes,color_human ) || isAllTargetColor( codes,color_machine ) ){
// 已经分出胜负了
win = true;
}
if( win ){
for( var i=0;i<5;i++ ){
codes_win.push( codes[i] );
}
return;
}
}
}
}
// 检查五连子是否都是指定的颜色
function isAllTargetColor( fiveCodes,targetColor ){
for( var i = 0;i<5;i++ ){
var code = fiveCodes[i];
var color = map_code_color.get( code );
if( !color || color != targetColor ){
return false;
}
}
return true;
}
// 人类必胜检测
// 检查人类是否存在机器不拦则人类必胜的情况,比如存在人类的 "空白白白空空"两端的 空的数量满足一端>=1 ,一端>1,则机器必须落在 "白白白"的两端
function humanMustWinCheck(){
// 全部行的 人类必胜检测
var sixCodes = humanMustWinCheck_allRow();
if( sixCodes ){
return sixCodes;
}
// 全部列的 人类必胜检测
sixCodes = humanMustWinCheck_allCol();
if( sixCodes ){
return sixCodes;
}
// 全部正斜线的 人类必胜检测
sixCodes = humanMustWinCheck_allForwardSlash();
if( sixCodes ){
return sixCodes;
}
// 全部反斜线的 人类必胜检测
sixCodes = humanMustWinCheck_allBackSlash();
if( sixCodes ){
return sixCodes;
}
}
// 全部行的 人类必胜检测
function humanMustWinCheck_allRow(){
for( var rowNum=1;rowNum<=row_count;rowNum++ ){
// 对当前行进行 人类必胜检测
var sixCodes = humanMustWinCheck_row( rowNum );
if( sixCodes ){
return sixCodes;
}
}
return null;
}
// 对当前行进行人类必胜检测
function humanMustWinCheck_row( rowNum ){
// 遍历当前行的全部六连子,检测是否存在 "空白白白空空"、"空空白白白空"
for( var colNum=1;colNum<=col_count;colNum++ ){
var codes = [];
codes.push( rowNum + "_" + colNum );
codes.push( rowNum + "_" + ( colNum + 1 ) );
codes.push( rowNum + "_" + ( colNum + 2 ) );
codes.push( rowNum + "_" + ( colNum + 3 ) );
codes.push( rowNum + "_" + ( colNum + 4 ) );
codes.push( rowNum + "_" + ( colNum + 5 ) );
if( isHumanMushWinSixCodes( codes ) ){
return codes;
}
}
return null;
}
// 检查是否是人类必胜六连子
function isHumanMushWinSixCodes( sixCodes ){
var color1 = map_code_color.get( sixCodes[0] );
var color2 = map_code_color.get( sixCodes[1] );
var color3 = map_code_color.get( sixCodes[2] );
var color4 = map_code_color.get( sixCodes[3] );
var color5 = map_code_color.get( sixCodes[4] );
var color6 = map_code_color.get( sixCodes[5] );
if( !( color1 && color2 && color3 && color4 && color5 && color6 ) ){
return false;
}
if( ( color1 == color_blank &&
color2 == color_blank &&
color3 == color_human &&
color4 == color_human &&
color5 == color_human &&
color6 == color_blank ) ||
( color1 == color_blank &&
color2 == color_human &&
color3 == color_human &&
color4 == color_human &&
color5 == color_blank &&
color6 == color_blank ) ){
return true;
}else{
return false;
}
}
// 当前列的人类必胜六连子检测
function humanMustWinCheck_allCol(colNum){
for( var rowNum=1;rowNum<=row_count;rowNum++ ){
var codes = [];
codes.push( rowNum + "_" + colNum );
codes.push( ( rowNum + 1 ) + "_" + colNum );
codes.push( ( rowNum + 2 ) + "_" + colNum );
codes.push( ( rowNum + 3 ) + "_" + colNum );
codes.push( ( rowNum + 4 ) + "_" + colNum );
codes.push( ( rowNum + 5 ) + "_" + colNum );
if( isHumanMushWinSixCodes( codes ) ){
return codes;
}
}
return null;
}
// 全部正斜线的 人类必胜检测
function humanMustWinCheck_allForwardSlash(){
for(var rowNum=1;rowNum<=row_count;rowNum++){
// 当前正斜线的 人类必胜检测
var sixCodes = humanMustWinCheck_forwardSlash(rowNum,1);
if( sixCodes ){
return sixCodes;
}
}
for(var colNum=1;colNum<=col_count;colNum++){
// 当前正斜线的 人类必胜检测
var sixCodes = humanMustWinCheck_forwardSlash(row_count,colNum);
if( sixCodes ){
return sixCodes;
}
}
return null;
}
// 当前正斜线的 人类必胜检测
function humanMustWinCheck_forwardSlash( fromRowNum,fromColNum ){
var large = row_count;
if( col_count > large ){
large = col_count;
}
for(var i=0;i<large;i++){
var codes = [];
codes.push( ( fromRowNum - i ) + "_" + ( fromColNum + i ) );
codes.push( ( fromRowNum - i - 1 ) + "_" + ( fromColNum + i + 1 ) );
codes.push( ( fromRowNum - i - 2 ) + "_" + ( fromColNum + i + 2 ) );
codes.push( ( fromRowNum - i - 3 ) + "_" + ( fromColNum + i + 3 ) );
codes.push( ( fromRowNum - i - 4 ) + "_" + ( fromColNum + i + 4 ) );
codes.push( ( fromRowNum - i - 5 ) + "_" + ( fromColNum + i + 5 ) );
if( isHumanMushWinSixCodes( codes ) ){
return codes;
}
}
return null;
}
// 全部反斜线的 人类必胜检测
function humanMustWinCheck_allBackSlash(){
for(var colNum=1;colNum<=col_count;colNum++){
// 当前反斜线的 人类必胜检测
var sixCodes = humanMustWinCheck_backSlash( 1,colNum );
if( sixCodes ){
return sixCodes;
}
}
for(var rowNum=1;rowNum<=row_count;rowNum++){
// 当前反斜线的 人类必胜检测
var sixCodes = humanMustWinCheck_backSlash( rowNum,1 );
if( sixCodes ){
return sixCodes;
}
}
return null;
}
// 行、列、正斜线、反斜线上的几个子的行号、列号增长的趋势规则(即++、--)可以添加到一个数组中,每次从数组中取
// 当前反斜线的 人类必胜检测
function humanMustWinCheck_backSlash(fromRowNum,fromColNum){
var large = row_count;
if( col_count > large ){
large = col_count;
}
for( var i=0;i<large;i++ ){
var codes = [];
codes.push( ( fromRowNum + i ) + "_" + ( fromColNum + i ) );
codes.push( ( fromRowNum + i + 1 ) + "_" + ( fromColNum + i + 1 ) );
codes.push( ( fromRowNum + i + 2 ) + "_" + ( fromColNum + i + 2 ) );
codes.push( ( fromRowNum + i + 3 ) + "_" + ( fromColNum + i + 3 ) );
codes.push( ( fromRowNum + i + 4 ) + "_" + ( fromColNum + i + 4 ) );
codes.push( ( fromRowNum + i + 5 ) + "_" + ( fromColNum + i + 5 ) );
if( isHumanMushWinSixCodes( codes ) ){
return codes;
}
}
return null;
}
</script>
</html>
javascript 实现五子棋人机PK-v2
于 2023-10-27 16:21:08 首次发布