БлогNot. Javascript: мат ферзём и королём одинокому королю

Javascript: мат ферзём и королём одинокому королю

Не было времени всю прошедшую неделю, в промежутках между делами успел наваять разве что вот это:

 Выполнить приложение в новом окне

Нажмите ОК под доской, расставьте фигуры, нажимайте кнопку или доску для очередного хода. Если Javascript включён, но что-то не работает, проверьте, не блокируется ли скрипт каким-либо расширением браузера, например, AdBlock.

Приложение получилось немаленьким, следует оно примерно следующему алгоритму:

Фигуры: ЧК, БК, БФ (чёрный король, белый король, белый ферзь)
Переменная min - количество свободных клеток для ходов ЧК

Пока не конец игры {
 Если ход белых
  Если есть ходы БФ, ведущие к мату, 
   поставить мат случайным из них, конец игры
  Иначе если есть ходы БФ, изолирующие ЧК на краю доски, 
   изолировать ЧК, конец хода
 стратегия 1 (findMinSteps):
  Иначе если есть ходы БФ, ведущие к минимуму свободных клеток для короля, min>1, 
   выбрать случайный из них, конец хода
  Иначе если есть ходы БК, ведущие к минимуму свободных клеток для короля, min>1, 
   выбрать случайный из них, конец хода
 стратегия 2 (findMinSteps2):
  Иначе если есть ходы БК, ведущие к углу ЧК, 
   выбрать случайный из них, конец хода
  Иначе если есть ходы БФ, ведущие к углу ЧК, 
   выбрать случайный из них, конец хода
 стратегия 3 (findMinSteps3):
  Иначе если есть ходы БФ на расстояние коня от ЧК, 
   не увеличивающие min, выбрать тот, что дальше от БК, конец хода
  Иначе если есть ходы БФ на расстояние верблюда (3+1 клетки) от ЧК, 
   не увеличивающие min, выбрать тот, что дальше от БК, конец хода
  Иначе БК ходит от своего угла, игнорируя min, конец хода
 Иначе
  Сделать ЧК случайный из возможных ходов, 
   ведущих к центру доски или пату, конец хода
}

К сожалению, в таком виде алгоритм не будет ставить мат кратчайшим путём, да к этому и едва ли есть смысл стремиться при отсутствии рекурсивного просчёта ходов.

Кроме того, в алгоритмах такого сорта "узким местом" является обычно зацикливание, ведущее либо к отсутствию вариантов хода, либо к бесконечному повторению одного и того же манёвра. Напомню, что по правилам шахмат, ничьей считается не только пат, но и 50 ходов без продвижения пешек или взятия фигур, а также троекратное повторение в партии той же самой позиции.

Не уверен, что мне удалось выловить все возможные "критические" позиции, логично ожидать, что они могут быть связаны с ситуациями, когда белый и чёрный короли находятся в одном углу от ферзя. Если обнаружите такую - скиньте её мне :)

Примеры таких "скользких" позиций (по порядку кликов, БФ-БК-ЧК, везде доска 8x8 и начальный ход белых):

f3-f4-g1
d3-b6-a4
a5-d7-b7
f4-h8-h5

В исходнике имеет смысл обратить внимание на то, как описаны объекты "Фигура" и "Позиция" (увы, недоработанные). В Javascript нет классов, но писать можно вполне "объектно".

Ну а этот код, из-за ряда непродуманных моментов, стал уже трудноуправляемым и что-то мне надоел :( Правильный путь доделать и сократить этот код, думаю, рекурсия.

<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<script type="text/javascript">

//Глобальные данные

var CELLSIZE=36; //Размер ячейки доски в пикселах
var WHITECOLOR='#FFFFFF'; //Цвет белых клеток
var BLACKCOLOR='#800000'; //и чёрных
var LOW_N=4;
var HIGH_N=26; //минимальный и максимальный размеры доски
var N=8; //размер доски по умолчанию
var firstLetterCode = 97; //'a'
var click = -1; //Состояние игры
var endOfGame = false;
var Q,K,k; //Фигуры
var P; //Позиция
var WHITE_QUEEN_PICTURE='wq.png'; //Картинки
var WHITE_KING_PICTURE='wk.png';
var BLACK_KING_PICTURE='bk.png';
var VOID_PICTURE='void.gif';

//Служебные функции

function sign(x) { //Знак числа
 return (x>0?1:(x==0?0:-1)); 
}
function dist(y1,x1,y2,x2) { //Клеточное расстояние между полями
 return Math.max(Math.abs(x2-x1),Math.abs(y2-y1)); 
}
function knightDist(y1,x1,y2,x2) { //Дистанция хода коня (2+1 клетки) между полями
 var dx = Math.abs(x1-x2);
 var dy = Math.abs(y1-y2);
 return (x1!=x2 && y1!=y2 && Math.max(dx,dy)==2 && Math.min(dx,dy)==1);
}
function camelDist(y1,x1,y2,x2) { //Дистанция хода "верблюда" (3+1 клетки) между полями
 var dx = Math.abs(x1-x2);
 var dy = Math.abs(y1-y2);
 return (x1!=x2 && y1!=y2 && Math.max(dx,dy)==3 && Math.min(dx,dy)==1);
}
function notation (type,y,x) { //Нотация поля (столбец,строка)
 return (type + String.fromCharCode(firstLetterCode - 1 + x) + (N-y+1)); 
}
function xyFromNotation (a) { //Вычислить координаты (столбец,строка) по нотации поля
 var f = { type: '', y: 0, x: 0 };
 f.type = a.substring(0,1);
 f.x = a.charCodeAt(1) - firstLetterCode + 1;
 f.y = N - parseInt(a.substring(2)) + 1; 
 return f;
}
function checkRook (y1,x1,y2,x2) { //Клетки шахуются ходом ладьи?
 return (x1==x2||y1==y2);
}
function checkQueen (y1,x1,y2,x2) { //Клетки шахуются ходом ферзя?
 return (checkRook(y1,x1,y2,x2)||Math.abs(y1-y2)==Math.abs(x1-x2)); 
}
function rand(n) { //Случайное целое число [0;n-1]
 return Math.floor(Math.random()*n);
}
function nearestCorner(y,x) { //Ближайший угол
 var f = { y: 0, x: 0 };
 if (y<=Math.floor(N/2)) f.y=1; else f.y=N;
 if (x<=Math.floor(N/2)) f.x=1; else f.x=N;
 return f;
}

//Описание объекта "Фигура"

function Figure (row,col,type,img) { 
 this.y = row;
 this.x = col;
 this.type = type;
 this.img = img;
 this.color = img.substring(0,1);
 
 this.set = function (row,col) { //Установить координаты
  this.y = row; this.x = col;
 }

 this.get = function () { //Получить координаты
  return { y : this.y, x : this.x };
 }
 
 this.getNotation = function () { //Запись нотации поля
  return notation(this.type,this.y,this.x);
 }

 this.oppositeColor = function () { //Противоположный цвет
  return (this.color=='w'?'b':'w');
 } 
 
}

//Описание объекта "Позиция"

function Position (N) {
 this.f = Array (); //Фигуры
 this.step = 'w'; //Очередность хода
 this.count = 0; //Счётчик ходов
 this.b = new Array (N);
 for (var j=0; j<N; j++) { this.b[j] = new Array (N); for (var k=0; k<N; k++) this.b[j][k] = 0; }
 this.edged = false;
 
 this.add = function (f) { this.f.push (f); }
 
 this.remove = function (i) {
  if (i>-1 && i<this.f.length) { this.f.splice(i,1); return true; }
  return false;
 }
 
 this.clear = function (i) { this.f.splice(0,this.f.length); }
 
 this.findByType  = function (type,color) {
  for (var i=0; i<this.f.length; i++) if (this.f[i].type==type && this.f[i].color==color) { return i; }
  return -1;
 }
 
 this.findByCoord  = function (row,col) {
  for (var i=0; i<this.f.length; i++) if (this.f[i].y==row && this.f[i].x==col) { return i; }
  return -1;
 }
 
 this.getPositionString = function () {
  var w = 'Белые: '; var b = 'Чёрные: '; var len=this.f.length;
  for (var i=0; i<len; i++) {
   if (this.f[i].color=='w') w+=this.f[i].getNotation()+(i<len-1?' ':''); 
   else b+=this.f[i].getNotation()+(i<len-1?' ':'');
  }
  return w+' '+b;
 }
 
 this.goodCoord = function (row,col) { return (row>0 && row<N+1 && col>0 && col<N+1); } //Координаты допустимы?
 
 this.freeCell =  function (row,col) { 
  for (var i=0; i<this.f.length; i++) if (this.f[i].x==col && this.f[i].y==row) return false;
  return true;
 }

 this.freeLine = function (i,row,col) { //Свободна линия от f[i] до row,col?
  var dy=sign(row-this.f[i].y);
  var dx=sign(col-this.f[i].x);
  var dst=dist(this.f[i].y,this.f[i].x,row,col);
  var y = this.f[i].y+dy;
  var x = this.f[i].x+dx;
  for (var d=1; d<dst; d++) {
   if (!this.freeCell(y,x)) return false;
   x+=dx; y+=dy;
  }
  return true;
 }

 this.eatable = function (j,y,x,color) { //f[j] естся в позиции y,x фигурой цвета color?
  var res = false;
  for (var i=0; i<this.f.length; i++) if (i!=j && this.f[i].color==color) {
   if (this.f[i].type=='K') {
    if (dist(this.f[i].y,this.f[i].x,y,x)<2) res = true;
   }
   else if (this.f[i].type=='Q') {
    if (checkQueen(this.f[i].y,this.f[i].x,this.f[j].y,this.f[j].x) && this.freeLine(i,y,x)) res = true;
   }
  }
  return res;
 }
  
 this.kol = function (row,col) { //Служебная - поиск свободной области для короля
  var n=0;
  for (var dx=-1; dx<2; dx++) 
  for (var dy=-1; dy<2; dy++) if (dx!=0 || dy!=0) {  
   var y = row + dy;
   var x = col + dx;
   if (this.goodCoord(y,x) && this.b[y-1][x-1]==0) {
    n++;
    this.b[y-1][x-1]=2;
	n+=this.kol(y,x);
   }
  }
  return n;
 }
 
 this.kolSteps = function (i) { //Кол-во допустимых ходов для фигуры f[i] == "потенциально свободная" область
  var c = this.f[i].get();
  for (var j=0; j<N; j++) for (var k=0; k<N; k++) this.b[j][k] = 0;
  for (var j=1; j<=N; j++) for (var k=1; k<=N; k++) { //На '1' в матрице не сможет сходить
   if (!this.freeCell(j,k)) this.b[j-1][k-1]=1; 
   else {
    this.f[i].set(j,k);
    if (this.eatable(i,j,k,this.f[i].oppositeColor())) this.b[j-1][k-1]=1;
    this.f[i].set(c.y,c.x);
   }	
  }
  var n;
  if (this.f[i].type=='K') n = this.kol(c.y,c.x); 
  return n;
 }
  
 this.validSteps = function (i) { //Массив допустимых ходов для фигуры f[i]
  var a = Array ();
  var c = this.f[i].get();
  var dN;
  if (this.f[i].type=='Q') dN = N; //Допустимые смещения от текущей позиции, "с запасом"
  else if (this.f[i].type=='K') dN = 2;
  for (var dx=-1; dx<2; dx++) 
  for (var dy=-1; dy<2; dy++) if (dx!=0 || dy!=0) { //Допустимые направления хода
   for (var d=1; d<dN; d++) { 
	var y = this.f[i].y + d*dy;
    var x = this.f[i].x + d*dx;
    if (this.goodCoord(y,x)) { //Если координаты в пределах поля
     if (this.freeCell(y,x)) {
	  if (this.freeLine(i,y,x)) {
	   this.f[i].set(y,x);
       if (!this.eatable(i,y,x,this.f[i].oppositeColor()) || //не бьётся чужой фигурой
	     this.f[i].type!='K' && this.eatable(i,y,x,this.f[i].color)) { 
		 //кроме короля, м.б. защищена фигурой своего цвета
        a.push(notation(this.f[i].type,y,x));
       }
	   this.f[i].set(c.y,c.x);
      }
     }
     else break; //прервать направление
    }
   }
  }
  return a;
 }

 this.checkMate  = function (color) { //Мат
  var i = this.findByType('K',color);
  if (i<0) return false;
  var a = this.validSteps(i);
  if (a.length == 0 && this.eatable(i,this.f[i].y,this.f[i].x,this.f[i].oppositeColor())) return true;
  return false;
 }
 
 this.staleMate  = function (color) { //Пат
  var i = this.findByType('K',color);
  if (i<0) return false;
  var a = this.validSteps(i);
  if (a.length == 0 && !this.eatable(i,this.f[i].y,this.f[i].x,this.f[i].oppositeColor())) return true;
  return false;
  return false;
 }
 
 this.selectRandomMove  = function (steps) { //Выбор хода из массива steps
  var n = rand (steps.length);
  return xyFromNotation(steps[n]);
 }

 this.doMove  = function (steps,i,side) {  //Выполнение хода
  var c = this.selectRandomMove (steps);
  var c0 = this.f[i].get();
  this.f[i].set(c.y,c.x);
  setImage(c0.y,c0.x,VOID_PICTURE);
  setImage(c.y,c.x,this.f[i].img);
  var cnt=(this.count==0?'<br>':' ');
  if (side=='w') { this.count++; cnt+=this.count+'. '; }
  else if (side=='b' && this.count==0) { this.count=1; cnt+='1. ... ';  }
  if (isolated()) this.edged = true;
  return cnt+this.f[i].getNotation();
 } 
 
}

//Функции для работы с формой

function putForm () {
 document.writeln (
  '<div align="center" id="board_div" style="color: red"></div>'+"\n"+
  '<div align="center">'+"\n"+
  '<form name="log_div"><table border="0">'+"\n"+
  ' <tr><td align="center">N='+"\n"+
  ' <select name="N_select">'
 );
 for (var n=LOW_N; n<=HIGH_N; n++) 
  document.writeln ('<option value="'+n+'"'+(n==N?' selected':'')+'>'+n);
 document.writeln ('</select></td><td align="center">'+"\n"+
  'Начинают <select name="start_select">'+"\n"+
  '<option value="w">Белые<option value="b">Чёрные</select>'
 );
 document.writeln (
  '</td><td align="center" valign="middle">'+"\n"+
  '<input type="button" name="button_ok" onclick="click1();" value="OK"/>'+"\n"+
  '</td></tr></table></form></div>'+"\n"+
  '<table align="center" cellpadding="0" cellspacing="0" border="0" width="'+
  (CELLSIZE*(N+2))+'"><tr><td><div id="log_area"></div></td></tr></table>'
 );
}

function putBoard () {
 var s='<table align="center" cellpadding="0" cellspacing="0" border="0">'+"\n";
 var color=WHITECOLOR; if (N%2) color=BLACKCOLOR;
 for (var i=1; i<=N; i++) {
  s+='<tr>'+"\n";
  for (var j=1; j<=N; j++) {
   s+='<td align="center" valign="middle" width="'+CELLSIZE+'" height="'+
      CELLSIZE+'" nowrap style="background:'+color+';">'+
      '<a href="#" onclick="click2('+i+','+j+')">'+
      '<span id="board_'+i+'_'+j+'" title="'+notation('',i,j)+'">'+getCellCode(VOID_PICTURE)+
      '</a></span></td>'+"\n";
   color = (color==WHITECOLOR?BLACKCOLOR:WHITECOLOR);
  }
  s+='</tr>'+"\n";
  color = (N%2==0?(i%2?BLACKCOLOR:WHITECOLOR):(i%2?WHITECOLOR:BLACKCOLOR));
 }
 s+='</table>';
 document.getElementById('board_div').innerHTML=s;
}

function getCellCode (name) {
 return '<img src="'+name+'" width="'+CELLSIZE+'" height="'+
         CELLSIZE+'" hspace="0" vspace="0" border="0" alt=""/>'; 
}

function setImage(row,col,name) {
 document.getElementById('board_'+row+'_'+col).innerHTML=getCellCode(name);
}

function clearBoard() {
 for (var i=1; i<=N; i++) for (var j=1; j<=N; j++) setImage(i,j,VOID_PICTURE);
}

//Функции обработки событий

function checkEnd() { 
 if (endOfGame) { 
  document.log_div.button_ok.value = "OK";
  document.log_div.N_select.disabled = false;
  document.log_div.start_select.disabled = false; 
  click = -1; 
 }
}

function click1() { //клик по кнопке
 var button_messages = Array ("Поставьте белого ферзя на свободное поле",
                              "Поставьте белого короля на свободное поле",
                              "Поставьте чёрного короля на свободное поле"
                             );
 if (click==-1) {
  var N0 = parseInt(document.log_div.N_select.value);
  if (!isNaN(N0) && N0>=LOW_N && N0<=HIGH_N) {
   if (N0!=N) { N=N0; putBoard(); }
  }
  document.getElementById('log_area').innerHTML='';
  clearBoard();
  P = new Position (N);
  document.log_div.N_select.disabled = true;
  P.step=document.log_div.start_select.value;
  document.log_div.start_select.disabled = true;
  click = 0;
 }
 if (click<3) {
  document.getElementById('log_area').innerHTML=button_messages[click];
 }
 if (click==3) {
  document.getElementById('log_area').innerHTML='Начальная позиция: '+P.getPositionString();
  endOfGame = false;
  document.log_div.button_ok.value = ">>";
  click++;
 }
 else if (click==4) {
  main();
  checkEnd();
 }
}

function click2 (row,col) { //клик по полю row,col
 switch (click) {
  case -1:
   document.getElementById('log_area').innerHTML=
    'Начните новую игру, выбрав размеры доски и нажав кнопку ОК под доской';
  break;
  case 0:
   Q.set (row,col); P.add (Q);  setImage(row,col,Q.img);  click = 1; click1();
  break;
  case 1:
   if (P.freeCell(row,col)) {
    K.set (row,col); P.add (K); setImage(row,col,K.img); click = 2; click1();
   }
   else document.getElementById('log_area').innerHTML='Извините, это поле занято';
  break;
  case 2:
   if (P.freeCell(row,col)) {
    if (dist(K.y,K.x,row,col)>1) {
     k.set(row,col); P.add (k); setImage(row,col,k.img); click = 3; click1();
    }
    else document.getElementById('log_area').innerHTML='Извините, короли не могут стоять рядом';
   }
   else document.getElementById('log_area').innerHTML='Извините, это поле занято';
  break;
  default:
   main();
   checkEnd();
  break;
 }
}

//Реализация алгоритма
var bK,wQ,wK;

function isolated () {
 return (!P.staleMate('b') && P.kolSteps(bK)>0 && P.kolSteps(bK)<N-1 &&
         (P.f[bK].x==1 && P.f[wQ].x==2 || P.f[bK].x==N && P.f[wQ].x==N-1 ||
          P.f[bK].y==1 && P.f[wQ].y==2 || P.f[bK].y==N && P.f[wQ].y==N-1) );
}

function findMinSteps(steps,i,min0) {
 var minsteps = Array ();
 for (var j=0; j<steps.length; j++) {
  var c0 = P.f[i].get(); 
  var c = xyFromNotation(steps[j]);
  P.f[i].set(c.y,c.x);
  var min = P.kolSteps(bK);
  if (min>0 && min<min0) {
   minsteps.splice(0,minsteps.length);
   minsteps.push(notation(P.f[i].type,P.f[i].y,P.f[i].x));
   min0 = min;
  }
  P.f[i].set(c0.y,c0.x);
 }
 return minsteps;
}

function findMinSteps2(steps,i,ugol,goal,n0) {
 var minsteps = Array ();
 var minK0=n0;
 var c0 = P.f[i].get();
 for (var j=0; j<steps.length; j++) {
  var c = xyFromNotation(steps[j]);
  P.f[i].set(c.y,c.x);
  var minK = dist (P.f[i].y,P.f[i].x,ugol.y,ugol.x);
  var cu = nearestCorner(P.f[bK].y,P.f[bK].x);
  if (goal=='WIN' && minK<minK0 && minK>0 && !P.staleMate(P.f[i].oppositeColor()) 
      && (!(P.f[i].type=='Q' && dist(cu.x,cu.y,c.y,c.x)<dist(cu.x,cu.y,P.f[bK].y,P.f[bK].x)) && P.kolSteps(bK)<2)
      || goal=='DRAW' && (minK<=minK0 || P.staleMate(P.f[i].oppositeColor()))
     ) { //Есть ходы, приближающие к цели?
   if (minK<minK0) minsteps.splice(0,minsteps.length);
   minsteps.push(notation(P.f[i].type,P.f[i].y,P.f[i].x));
   minK0=minK;
  }
  P.f[i].set(c0.y,c0.x);
 }
 return minsteps;
} 

function findMinSteps3(steps,i,ugol,dmax,dist_function,savemin) {
 var minsteps = Array(); 
 var c0 = P.f[i].get();
 var minK0 = (savemin=='YES'?P.kolSteps(bK):N*N);
 for (var j=0; j<steps.length; j++) {
  var c = xyFromNotation(steps[j]);
  if (dist_function(c.y,c.x,ugol.y,ugol.x)) {
   var d = dist (c.y,c.x,ugol.y,ugol.x);
   P.f[i].set(c.y,c.x); 
   var minK = (savemin=='YES'?P.kolSteps(bK):N*N);
   if (d>=dmax && minK<=minK0 && minK>0 && !P.staleMate(P.f[i].oppositeColor())) { 
    if (minK<minK0 && d>=dmax) minsteps.splice(0,minsteps.length);
	minsteps.push(notation(P.f[i].type,c.y,c.x));
	dmax = d; minK0 = minK;
   }
   P.f[i].set(c0.y,c0.x); 
  }
 }
 return minsteps;
}

function main () {
 bK = P.findByType('K','b');
 var bKSteps = P.validSteps(bK);
 wQ = P.findByType('Q','w');
 var wQSteps = P.validSteps(wQ);
 wK = P.findByType('K','w');
 var wKSteps = P.validSteps(wK);
 if (P.count==0) {
  if (bKSteps.length<1) {
   document.getElementById('log_area').innerHTML+=
    '<br>'+(P.checkMate('b')?'Мат':'Пат')+' в стартовой позиции, игра окончена'; 
   endOfGame = true; return;
  }
  if (P.step=='w' && P.eatable(bK,P.f[bK].y,P.f[bK].x,P.f[bK].oppositeColor())) {
   document.getElementById('log_area').innerHTML+= 
    '<br>Шах в стартовой позиции при ходе белых, игра окончена'; 
   endOfGame = true; return;
  }
 }
 var min0 = P.kolSteps(bK);
 //do {
  if (P.step=='w') { //Ход белых
   if (isolated()) P.edged=true; 
   var wQcheckMates = Array ();
   var wQcheck = Array ();
   var c0 = P.f[wQ].get();
   for (var j=0; j<wQSteps.length; j++) { 
    var c = xyFromNotation(wQSteps[j]);
    P.f[wQ].set(c.y,c.x);
    if (!P.staleMate('b') && P.checkMate('b')) wQcheckMates.push(notation(P.f[wQ].type,c.y,c.x));
    P.f[wQ].set(c0.y,c0.x);
   }
   if (wQcheckMates.length>0) { //Есть ходы БФ, ведущие к мату?
    document.getElementById('log_area').innerHTML+=P.doMove (wQcheckMates,wQ,'w')+'#';
    document.getElementById('log_area').innerHTML+='<br>Мат! Игра окончена';
    endOfGame = true; return;
   }
   var wQminSteps = findMinSteps(wQSteps,wQ,min0); //Есть ходы БФ, уменьшающие свободу ЧК?
   var ugol = nearestCorner (P.f[bK].y,P.f[bK].x);
   if (wQminSteps.length==0) { //Есть ходы БФ, изолирующие ЧК на первой линии?
    var c0 = P.f[wQ].get();
    for (var j=0; j<wQSteps.length; j++) { 
     var c = xyFromNotation(wQSteps[j]);
     P.f[wQ].set(c.y,c.x);
	 if (isolated() && P.edged==false && !(min0==1 && dist(P.f[bK].y,P.f[bK].x,ugol.y,ugol.x)<2)) { 
      wQminSteps.push(notation(P.f[wQ].type,c.y,c.x)); P.edged=true; 
     }
     P.f[wQ].set(c0.y,c0.x);
    }
   }	
   if (wQminSteps.length>0) { 
    document.getElementById('log_area').innerHTML+=P.doMove (wQminSteps,wQ,'w');
	if (P.eatable(bK,P.f[bK].y,P.f[bK].x,P.f[wQ].color)) document.getElementById('log_area').innerHTML+='+';
    P.step='b'; 
    return;
   }
   var wKminSteps = Array ();
   wKminSteps = findMinSteps(wKSteps,wK,min0);
   if (wKminSteps.length>0) { //Есть ходы БК, уменьшающие свободу ЧК?
    document.getElementById('log_area').innerHTML+=P.doMove (wKminSteps,wK,'w');
	P.step='b'; return;
   }
   if (dist(ugol.y,ugol.x,P.f[bK].y,P.f[bK].x)<dist(ugol.y,ugol.x,P.f[wK].y,P.f[wK].x)) {
    wKminSteps = findMinSteps2(wKSteps,wK,ugol,'WIN',dist(P.f[wK].y,P.f[wK].x,ugol.y,ugol.x));
    if (wKminSteps.length>0) { //Есть ходы БК, приближающие к углу ЧК?
     document.getElementById('log_area').innerHTML+=P.doMove (wKminSteps,wK,'w');
     P.step='b'; return;
    }
    wQminSteps = findMinSteps2(wQSteps,wQ,ugol,'WIN',dist(P.f[wQ].y,P.f[wQ].x,ugol.y,ugol.x));
    if (wQminSteps.length>0) { //Есть ходы БФ, приближающие к углу ЧК?
     document.getElementById('log_area').innerHTML+=P.doMove (wQminSteps,wQ,'w');
	 P.step='b'; return;
    }
   }	
   var ugol = { y: P.f[bK].y, x: P.f[bK].x };
   var dmax = dist (P.f[wQ].y,P.f[wQ].x,P.f[wK].y,P.f[wK].x);
   var knightFields = findMinSteps3(wQSteps,wQ,ugol,dmax,knightDist,'YES');
   if (knightFields.length>0) { //БФ идёт "ходом коня" к ЧК, подальше от своего короля
    document.getElementById('log_area').innerHTML+=P.doMove (knightFields,wQ,'w');
	if (P.eatable(bK,P.f[bK].y,P.f[bK].x,P.f[wQ].color)) document.getElementById('log_area').innerHTML+='+';
	P.step='b'; return;
   }
   if (min0<2) {
    var dmax = dist (P.f[wQ].y,P.f[wQ].x,P.f[wK].y,P.f[wK].x);
	var camelFields = findMinSteps3(wQSteps,wQ,ugol,dmax,camelDist,'YES');
	if (camelFields.length>0) { //БФ идёт "ходом верблюда" к углу, подальше от своего короля
 	 document.getElementById('log_area').innerHTML+=P.doMove (camelFields,wQ,'w');
	 P.step='b'; return;
	}
   }
   var ugol = nearestCorner(P.f[wK].y,P.f[wK].x);
   var dmax = dist (P.f[wK].y,P.f[wK].x,ugol.y,ugol.x);
   var fromcornerFields = findMinSteps3(wKSteps,wK,ugol,dmax,dist,'NO');
   if (fromcornerFields.length>0) { //БК идёт из своего угла
    document.getElementById('log_area').innerHTML+=P.doMove (fromcornerFields,wK,'w');
    P.step='b'; return;
   }
   document.getElementById('log_area').innerHTML+= '<br>Не могу найти ход за белых, это баг алгоритма, игра окончена'; 
   endOfGame = true;
  }
  else if (P.step=='b') { //Ход чёрных
   if (dist(P.f[bK].y,P.f[bK].x,P.f[wQ].y,P.f[wQ].x)<2 &&
       dist(P.f[wK].y,P.f[wK].x,P.f[wQ].y,P.f[wQ].x)>1) {
    setImage(P.f[bK].y,P.f[bK].x,VOID_PICTURE);
    P.f[bK].set(P.f[wQ].y,P.f[wQ].x);
    setImage(P.f[bK].y,P.f[bK].x,P.f[bK].img);
    P.remove (wQ);
    document.getElementById('log_area').innerHTML+= '<br>Чёрный король съел белого ферзя, ничья'; 
    endOfGame = true; return;
   }	   
   bKSteps = P.validSteps(bK);
   var middle = Math.floor(N/2); if (N%2) middle++;
   var mids = { y: middle, x: middle };
   var bKminSteps = findMinSteps2(bKSteps,bK,mids,'DRAW',N);
   if (bKminSteps.length>0) {
    document.getElementById('log_area').innerHTML+=P.doMove(bKminSteps,bK,'b');
    P.step='w'; 
   }
   else if (P.staleMate('b')) {
    document.getElementById('log_area').innerHTML+= '<br>Пат, игра окончена'; 
    endOfGame = true;
   }
   return; 
  }
  //Здесь м.б. пауза, если нужна автоигра
 //} while (P.count<50); //Если нужна автоигра
}

Q = new Figure (0,0,'Q',WHITE_QUEEN_PICTURE); 
K = new Figure (0,0,'K',WHITE_KING_PICTURE); 
k = new Figure (0,0,'K',BLACK_KING_PICTURE);
putForm();
putBoard();
</script>
<noscript><div align="center">Извините, для работы приложения нужен включённый Javascript</div></noscript>

 Архив .zip с картинками скрипта, в ту же папку, что код JS/HTML (11 Кб)

23.11.2014, 23:20 [10080 просмотров]


теги: шахматы игра javascript алгоритм

К этой статье пока нет комментариев, Ваш будет первым