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 [10269 просмотров]