From: Steinar H. Gunderson Date: Thu, 20 Nov 2014 20:02:59 +0000 (+0100) Subject: Start using Chess.js. X-Git-Url: https://git.sesse.net/?p=remoteglot;a=commitdiff_plain;h=181097241ea4fe24cbea99a0a92736e39704e4a6;hp=edec6246eb5958f6b547bf7bb7fe30d7e1aa9bcf Start using Chess.js. The main motivation for importing yet another library (thankfully it's not that big; 17 kB pre-gzip should be livable) is to get accurate FENs for history support later. However, right now we grab the following two nice side effects: - We don't have to maintain our own castling and en passant logic anymore. - We don't have to send both UCI and prettyprinted moves over, reducing the JSON size by something like 40% (I haven't checked post-gzip, though). Currently we just send the UCI, but maybe it should be reversed; we need to check if this causes problems for CPU usage or something during normal updates, and which one compresses better.. --- diff --git a/remoteglot.pl b/remoteglot.pl index e3433aa..62d3aa1 100755 --- a/remoteglot.pl +++ b/remoteglot.pl @@ -210,7 +210,6 @@ sub handle_fics { next if (!defined($pos)); if ($pos->fen() eq $pos_for_movelist->fen()) { $pos->{'history'} = \@uci_movelist; - $pos->{'pretty_history'} = \@pretty_movelist; } } $getting_movelist = 0; @@ -280,7 +279,6 @@ sub handle_pgn { push @uci_moves, $uci_move; } $pos->{'history'} = \@uci_moves; - $pos->{'pretty_history'} = $moves; # Sometimes, PGNs lose a move or two for a short while, # or people push out new ones non-atomically. @@ -713,7 +711,6 @@ sub output_json { # single-PV only for now $json->{'pv_uci'} = $info->{'pv'}; - $json->{'pv_pretty'} = [ prettyprint_pv($pos_calculating, @{$info->{'pv'}}) ]; my %refutation_lines = (); my @refutation_lines = (); @@ -734,7 +731,6 @@ sub output_json { score_sort_key => score_sort_key($info, $pos_calculating, $mpv, 0), pretty_score => short_score($info, $pos_calculating, $mpv), pretty_move => $pretty_move, - pv_pretty => \@pretty_pv, }; $refutation_lines{$pv->[0]}->{'pv_uci'} = $pv; }; diff --git a/www/index.html b/www/index.html index 2dd8f15..c4e17b2 100644 --- a/www/index.html +++ b/www/index.html @@ -13,7 +13,6 @@

Analysis

-

@@ -54,11 +53,13 @@ multi-PV search: 12x2.3GHz Sandy Bridge). Moves provided by FIDE. Hosting and multi-PV analysis hardware by Studentersamfundet i Trondhjem. - JavaScript chessboard powered by chessboard.js. + JavaScript chessboard powered by chessboard.js + and chess.js. 7-man Lomonosov tablebase lookup by ChessOK.

+ diff --git a/www/js/chess.min.js b/www/js/chess.min.js new file mode 100644 index 0000000..da9203b --- /dev/null +++ b/www/js/chess.min.js @@ -0,0 +1,6 @@ +"use strict"; +/*! Copyright (c) 2014, Jeff Hlywa (jhlywa@gmail.com) + * Released under the BSD license + * https://github.com/jhlywa/chess.js/blob/master/LICENSE + */ +;var Chess=function(ao){var p="b";var k="w";var E=-1;var X="p";var S="n";var s="b";var H="r";var j="q";var ad="k";var f="pnbrqkPNBRQK";var an="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";var B=["1-0","0-1","1/2-1/2","*"];var u={b:[16,32,17,15],w:[-16,-32,-17,-15]};var J={n:[-18,-33,-31,-14,18,33,31,14],b:[-17,-15,17,15],r:[-16,1,16,-1],q:[-17,-16,-15,1,17,16,15,-1],k:[-17,-16,-15,1,17,16,15,-1]};var ac=[20,0,0,0,0,0,0,24,0,0,0,0,0,0,20,0,0,20,0,0,0,0,0,24,0,0,0,0,0,20,0,0,0,0,20,0,0,0,0,24,0,0,0,0,20,0,0,0,0,0,0,20,0,0,0,24,0,0,0,20,0,0,0,0,0,0,0,0,20,0,0,24,0,0,20,0,0,0,0,0,0,0,0,0,0,20,2,24,2,20,0,0,0,0,0,0,0,0,0,0,0,2,53,56,53,2,0,0,0,0,0,0,24,24,24,24,24,24,56,0,56,24,24,24,24,24,24,0,0,0,0,0,0,2,53,56,53,2,0,0,0,0,0,0,0,0,0,0,0,20,2,24,2,20,0,0,0,0,0,0,0,0,0,0,20,0,0,24,0,0,20,0,0,0,0,0,0,0,0,20,0,0,0,24,0,0,0,20,0,0,0,0,0,0,20,0,0,0,0,24,0,0,0,0,20,0,0,0,0,20,0,0,0,0,0,24,0,0,0,0,0,20,0,0,20,0,0,0,0,0,0,24,0,0,0,0,0,0,20];var o=[17,0,0,0,0,0,0,16,0,0,0,0,0,0,15,0,0,17,0,0,0,0,0,16,0,0,0,0,0,15,0,0,0,0,17,0,0,0,0,16,0,0,0,0,15,0,0,0,0,0,0,17,0,0,0,16,0,0,0,15,0,0,0,0,0,0,0,0,17,0,0,16,0,0,15,0,0,0,0,0,0,0,0,0,0,17,0,16,0,15,0,0,0,0,0,0,0,0,0,0,0,0,17,16,15,0,0,0,0,0,0,0,1,1,1,1,1,1,1,0,-1,-1,-1,-1,-1,-1,-1,0,0,0,0,0,0,0,-15,-16,-17,0,0,0,0,0,0,0,0,0,0,0,0,-15,0,-16,0,-17,0,0,0,0,0,0,0,0,0,0,-15,0,0,-16,0,0,-17,0,0,0,0,0,0,0,0,-15,0,0,0,-16,0,0,0,-17,0,0,0,0,0,0,-15,0,0,0,0,-16,0,0,0,0,-17,0,0,0,0,-15,0,0,0,0,0,-16,0,0,0,0,0,-17,0,0,-15,0,0,0,0,0,0,-16,0,0,0,0,0,0,-17];var av={p:0,n:1,b:2,r:3,q:4,k:5};var aj={NORMAL:"n",CAPTURE:"c",BIG_PAWN:"b",EP_CAPTURE:"e",PROMOTION:"p",KSIDE_CASTLE:"k",QSIDE_CASTLE:"q"};var D={NORMAL:1,CAPTURE:2,BIG_PAWN:4,EP_CAPTURE:8,PROMOTION:16,KSIDE_CASTLE:32,QSIDE_CASTLE:64};var W=7;var T=6;var Q=5;var P=4;var O=3;var N=2;var M=1;var K=0;var v={a8:0,b8:1,c8:2,d8:3,e8:4,f8:5,g8:6,h8:7,a7:16,b7:17,c7:18,d7:19,e7:20,f7:21,g7:22,h7:23,a6:32,b6:33,c6:34,d6:35,e6:36,f6:37,g6:38,h6:39,a5:48,b5:49,c5:50,d5:51,e5:52,f5:53,g5:54,h5:55,a4:64,b4:65,c4:66,d4:67,e4:68,f4:69,g4:70,h4:71,a3:80,b3:81,c3:82,d3:83,e3:84,f3:85,g3:86,h3:87,a2:96,b2:97,c2:98,d2:99,e2:100,f2:101,g2:102,h2:103,a1:112,b1:113,c1:114,d1:115,e1:116,f1:117,g1:118,h1:119};var V={w:[{square:v.a1,flag:D.QSIDE_CASTLE},{square:v.h1,flag:D.KSIDE_CASTLE}],b:[{square:v.a8,flag:D.QSIDE_CASTLE},{square:v.h8,flag:D.KSIDE_CASTLE}]};var C=new Array(128);var ak={w:E,b:E};var t=k;var d={w:0,b:0};var x=E;var n=0;var g=1;var Z=[];var ab={};if(typeof ao==="undefined"){h(an)}else{h(ao)}function ae(){C=new Array(128);ak={w:E,b:E};t=k;d={w:0,b:0};x=E;n=0;g=1;Z=[];ab={};q(z())}function Y(){h(an)}function h(ay){var aD=ay.split(/\s+/);var aw=aD[0];var aC=0;var aB=f+"12345678/";if(!aa(ay).valid){return false}ae();for(var az=0;az-1){d.w|=D.KSIDE_CASTLE}if(aD[2].indexOf("Q")>-1){d.w|=D.QSIDE_CASTLE}if(aD[2].indexOf("k")>-1){d.b|=D.KSIDE_CASTLE}if(aD[2].indexOf("q")>-1){d.b|=D.QSIDE_CASTLE}x=(aD[3]==="-")?E:v[aD[3]];n=parseInt(aD[4],10);g=parseInt(aD[5],10);q(z());return true}function aa(ay){var aD={0:"No errors.",1:"FEN string must contain six space-delimited fields.",2:"6th field (move number) must be a positive integer.",3:"5th field (half move counter) must be a non-negative integer.",4:"4th field (en-passant square) is invalid.",5:"3rd field (castling availability) is invalid.",6:"2nd field (side to move) is invalid.",7:"1st field (piece positions) does not contain 8 '/'-delimited rows.",8:"1st field (piece positions) is invalid [consecutive numbers].",9:"1st field (piece positions) is invalid [invalid piece].",10:"1st field (piece positions) is invalid [row too large]."};var aC=ay.split(/\s+/);if(aC.length!==6){return{valid:false,error_number:1,error:aD[1]}}if(isNaN(aC[5])||(parseInt(aC[5],10)<=0)){return{valid:false,error_number:2,error:aD[2]}}if(isNaN(aC[4])||(parseInt(aC[4],10)<0)){return{valid:false,error_number:3,error:aD[3]}}if(!/^(-|[abcdefgh][36])$/.test(aC[3])){return{valid:false,error_number:4,error:aD[4]}}if(!/^(KQ?k?q?|Qk?q?|kq?|q|-)$/.test(aC[2])){return{valid:false,error_number:5,error:aD[5]}}if(!/^(w|b)$/.test(aC[1])){return{valid:false,error_number:6,error:aD[6]}}var aB=aC[0].split("/");if(aB.length!==8){return{valid:false,error_number:7,error:aD[7]}}for(var az=0;az0){ay+=aC;aC=0}var ax=C[aA].color;var aB=C[aA].type;ay+=(ax===k)?aB.toUpperCase():aB.toLowerCase()}if((aA+1)&136){if(aC>0){ay+=aC}if(aA!==v.h1){ay+="/"}aC=0;aA+=8}}var az="";if(d[k]&D.KSIDE_CASTLE){az+="K"}if(d[k]&D.QSIDE_CASTLE){az+="Q"}if(d[p]&D.KSIDE_CASTLE){az+="k"}if(d[p]&D.QSIDE_CASTLE){az+="q"}az=az||"-";var aw=(x===E)?"-":m(x);return[ay,t,az,aw,n,g].join(" ")}function at(aw){for(var ax=0;ax0){return}if(aw!==an){ab.SetUp="1";ab.FEN=aw}else{delete ab.SetUp;delete ab.FEN}}function ag(ax){var aw=C[v[ax]];return(aw)?{type:aw.type,color:aw.color}:null}function aq(aw,ay){if(!("type" in aw&&"color" in aw)){return false}if(f.indexOf(aw.type.toLowerCase())===-1){return false}if(!(ay in v)){return false}var ax=v[ay];if(aw.type==ad&&!(ak[aw.color]==E||ak[aw.color]==ax)){return false}C[ax]={type:aw.type,color:aw.color};if(aw.type===ad){ak[aw.color]=ax}q(z());return true}function F(ax){var aw=ag(ax);C[v[ax]]=null;if(aw&&aw.type===ad){ak[aw.color]=E}q(z());return aw}function am(ay,aB,aA,ax,az){var aw={color:t,from:aB,to:aA,flags:ax,piece:ay[aB].type};if(az){aw.flags|=D.PROMOTION;aw.promotion=az}if(ay[aA]){aw.captured=ay[aA].type}else{if(ax&D.EP_CAPTURE){aw.captured=X}}return aw}function b(ay){function aN(aT,aR,aW,aV,aQ){if(aT[aW].type===X&&(U(aV)===K||U(aV)===W)){var aU=[j,H,s,S];for(var aS=0,aP=aU.length;aS0){if(aE.color===k){return true}}else{if(aE.color===p){return true}}continue}if(aE.type==="n"||aE.type==="k"){return true}var ay=o[aC];var az=aA+ay;var aB=false;while(az!==aD){if(C[az]!=null){aB=true;break}az+=ay}if(!aB){return true}}}return false}function L(aw){return af(au(aw),ak[aw])}function R(){return L(t)}function I(){return R()&&b().length===0}function ah(){return !R()&&b().length===0}function A(){var aC={};var aB=[];var ax=0;var aD=0;for(var ay=v.a8;ay<=v.h1;ay++){aD=(aD+1)%2;if(ay&136){ay+=7;continue}var aA=C[ay];if(aA){aC[aA.type]=(aA.type in aC)?aC[aA.type]+1:1;if(aA.type===s){aB.push(aD)}ax++}}if(ax===2){return true}else{if(ax===3&&(aC[s]===1||aC[S]===1)){return true}else{if(ax===aC[s]+2){var az=0;var aw=aB.length;for(var ay=0;ay=3){aA=true}if(!ay.length){break}al(ay.pop())}return aA}function e(aw){Z.push({move:aw,kings:{b:ak.b,w:ak.w},turn:t,castling:{b:d.b,w:d.w},ep_square:x,half_moves:n,move_number:g})}function al(ax){var aB=t;var aC=au(aB);e(ax);C[ax.to]=C[ax.from];C[ax.from]=null;if(ax.flags&D.EP_CAPTURE){if(t===p){C[ax.to-16]=null}else{C[ax.to+16]=null}}if(ax.flags&D.PROMOTION){C[ax.to]={type:ax.promotion,color:aB}}if(C[ax.to].type===ad){ak[C[ax.to].color]=ax.to;if(ax.flags&D.KSIDE_CASTLE){var az=ax.to-1;var aA=ax.to+1;C[az]=C[aA];C[aA]=null}else{if(ax.flags&D.QSIDE_CASTLE){var az=ax.to+1;var aA=ax.to-2;C[az]=C[aA];C[aA]=null}}d[aB]=""}if(d[aB]){for(var ay=0,aw=V[aB].length;ay0){if(aD>0&&aB>0){return m(aE)}else{if(aB>0){return m(aE).charAt(1)}else{return m(aE).charAt(0)}}}return""}function a(){var az=" +------------------------+\n";for(var ax=v.a8;ax<=v.h1;ax++){if(y(ax)===0){az+=" 87654321"[U(ax)]+" |"}if(C[ax]==null){az+=" . "}else{var ay=C[ax].type;var aw=C[ax].color;var aA=(aw===k)?ay.toUpperCase():ay.toLowerCase();az+=" "+aA+" "}if((ax+1)&136){az+="|\n";ax+=8}}az+=" +------------------------+\n";az+=" a b c d e f g h\n";return az}function U(aw){return aw>>4}function y(aw){return aw&15}function m(aw){var ay=y(aw),ax=U(aw);return"abcdefgh".substring(ay,ay+1)+"87654321".substring(ax,ax+1)}function au(aw){return aw===k?p:k}function r(aw){return"0123456789".indexOf(aw)!==-1}function l(az){var ay=w(az);ay.san=ar(ay);ay.to=m(ay.to);ay.from=m(ay.from);var ax="";for(var aw in D){if(D[aw]&ay.flags){ax+=aj[aw]}}ay.flags=ax;return ay}function w(ay){var aw=(ay instanceof Array)?[]:{};for(var ax in ay){if(typeof ax==="object"){aw[ax]=w(ay[ax])}else{aw[ax]=ay[ax]}}return aw}function ap(aw){return aw.replace(/^\s+|\s+$/g,"")}function ai(aC){var ax=b({legal:false});var az=0;var ay=t;for(var aA=0,aw=ax.length;aA0){var aB=ai(aC-1);az+=aB}else{az++}}G()}return az}return{WHITE:k,BLACK:p,PAWN:X,KNIGHT:S,BISHOP:s,ROOK:H,QUEEN:j,KING:ad,SQUARES:(function(){var ax=[];for(var aw=v.a8;aw<=v.h1;aw++){if(aw&136){aw+=7;continue}ax.push(m(aw))}return ax})(),FLAGS:aj,load:function(aw){return h(aw)},reset:function(){return Y()},moves:function(az){var ay=b(az);var ax=[];for(var aA=0,aw=ay.length;aA=100||ah()||A()||i()},insufficient_material:function(){return A()},in_threefold_repetition:function(){return i()},game_over:function(){return n>=100||I()||ah()||A()||i()},validate_fen:function(aw){return aa(aw)},fen:function(){return z()},pgn:function(aG){var az=(typeof aG==="object"&&typeof aG.newline_char==="string")?aG.newline_char:"\n";var aF=(typeof aG==="object"&&typeof aG.max_width==="number")?aG.max_width:0;var aH=[];var aD=false;for(var aB in ab){aH.push("["+aB+' "'+ab[aB]+'"]'+az);aD=true}if(aD&&Z.length){aH.push(az)}var aE=[];while(Z.length>0){aE.push(G())}var ax=[];var ay="";var aw=1;while(aE.length>0){var aA=aE.pop();if(aw===1&&aA.color==="b"){ay="1. ...";aw++}else{if(aA.color==="w"){if(ay.length){ax.push(ay)}ay=aw+".";aw++}}ay=ay+" "+ar(aA);al(aA)}if(ay.length){ax.push(ay)}if(typeof ab.Result!=="undefined"){ax.push(ab.Result)}if(aF===0){return aH.join("")+ax.join(" ")}var aC=0;for(var aB=0;aBaF&&aB!==0){if(aH[aH.length-1]===" "){aH.pop()}aH.push(az);aC=0}else{if(aB!==0){aH.push(" ");aC++}}aH.push(ax[aB]);aC+=ax[aB].length}return aH.join("")},load_pgn:function(aG,aK){function aL(aM){return aM.replace(/\\/g,"\\")}function az(aO){var aN=b();for(var aP=0,aM=aN.length;aP0){aQ[aO]=aP}}return aQ}var aB=(typeof aK==="object"&&typeof aK.newline_char==="string")?aK.newline_char:"\r?\n";var aH=new RegExp("^(\\[(.|"+aL(aB)+")*\\])("+aL(aB)+")*1.("+aL(aB)+"|.)*$","g");var aD=aG.replace(aH,"$1");if(aD[0]!=="["){aD=""}Y();var aA=aI(aD,aK);for(var aJ in aA){at([aJ,aA[aJ]])}var ay=aG.replace(aD,"").replace(new RegExp(aL(aB),"g")," ");ay=ay.replace(/(\{[^}]+\})+?/g,"");ay=ay.replace(/\d+\./g,"");var ax=ap(ay).split(new RegExp(/\s+/));ax=ax.join(",").replace(/,,+/g,",").split(",");var aC="";for(var aF=0;aF-1){if(aw(ab)&&typeof ab.Result==="undefined"){at(["Result",aC])}}else{aC=aE(aC);if(aC==null){return false}else{al(aC)}}return true},header:function(){return at(arguments)},ascii:function(){return a()},turn:function(){return t},move:function(aA){var az=null;var ay=b();if(typeof aA==="string"){for(var aB=0,ax=ay.length;aB0){aA.push(G())}while(aA.length>0){var aw=aA.pop();if(ax){az.push(l(aw))}else{az.push(ar(aw))}al(aw)}return az}}};if(typeof exports!=="undefined"){exports.Chess=Chess}if(typeof define!=="undefined"){define(function(){return Chess})}; \ No newline at end of file diff --git a/www/js/remoteglot.js b/www/js/remoteglot.js index 074a474..8161236 100644 --- a/www/js/remoteglot.js +++ b/www/js/remoteglot.js @@ -3,9 +3,6 @@ /** @type {window.ChessBoard} @private */ var board = null; -/** @type {window.ChessBoard} @private */ -var hiddenboard = null; - /** @type {Array.<{ * from_col: number, * from_row: number, @@ -401,13 +398,19 @@ var thousands = function(x) { /** * @param {!string} fen * @param {Array.} uci_pv - * @param {Array.} pretty_pv * @param {number} move_num * @param {!string} toplay * @param {number=} opt_limit * @param {boolean=} opt_showlast */ -var add_pv = function(fen, uci_pv, pretty_pv, move_num, toplay, opt_limit, opt_showlast) { +var add_pv = function(fen, uci_pv, move_num, toplay, opt_limit, opt_showlast) { + var hiddenboard = new Chess(); + hiddenboard.load(fen); + for (var i = 0; i < uci_pv.length; ++i) { + hiddenboard.move(ucimove_to_chessjs_move(uci_pv[i])); + } + var pretty_pv = hiddenboard.history(); + display_lines.push({ start_fen: fen, uci_pv: uci_pv, @@ -541,7 +544,7 @@ var update_refutation_lines = function() { var pv_td = document.createElement("td"); tr.appendChild(pv_td); $(pv_td).addClass("pv"); - $(pv_td).html(add_pv(fen, line['pv_uci'], line['pv_pretty'], move_num, toplay, 10)); + $(pv_td).html(add_pv(fen, line['pv_uci'], move_num, toplay, 10)); tbl.append(tr); } @@ -654,14 +657,14 @@ var update_board = function(data, num_viewers) { // Print the history. if (data['position']['history']) { - add_pv('start', data['position']['history'], data['position']['pretty_history'], 1, 'W', 8, true); + add_pv('start', data['position']['history'], 1, 'W', 8, true); } else { display_lines.push(null); } update_history(); // Print the PV. - $("#pv").html(add_pv(data['position']['fen'], data['pv_uci'], data['pv_pretty'], data['position']['move_num'], data['position']['toplay'])); + $("#pv").html(add_pv(data['position']['fen'], data['pv_uci'], data['position']['move_num'], data['position']['toplay'])); // Update the PV arrow. clear_arrows(); @@ -803,52 +806,28 @@ var update_displayed_line = function() { $("#nextmove").html("Next"); } - hiddenboard.position(current_display_line.start_fen, false); + var hiddenboard = new Chess(); + hiddenboard.load(current_display_line.start_fen); for (var i = 0; i <= current_display_move; ++i) { - var pos = hiddenboard.position(); - var move = current_display_line.uci_pv[i]; - var source = move.substr(0, 2); - var target = move.substr(2, 2); - var promo = move.substr(4, 1); - - // Check if we need to do en passant. - var piece = pos[source]; - if (piece == "wP" || piece == "bP") { - if (source.substr(0, 1) != target.substr(0, 1) && - pos[target] === undefined) { - var ep_square = target.substr(0, 1) + source.substr(1, 1); - delete pos[ep_square]; - hiddenboard.position(pos, false); - } - } - - move = source + "-" + target; - hiddenboard.move(move, false); - pos = hiddenboard.position(); - - // Do promotion if needed. - if (promo != "") { - pos[target] = pos[target].substr(0, 1) + promo.toUpperCase(); - hiddenboard.position(pos, false); - } - - // chessboard.js does not automatically move the rook on castling - // (issue #51; marked as won't fix), so update it ourselves. - if (move == "e1-g1" && hiddenboard.position().g1 == "wK") { // white O-O - hiddenboard.move("h1-f1", false); - } else if (move == "e1-c1" && hiddenboard.position().c1 == "wK") { // white O-O-O - hiddenboard.move("a1-d1", false); - } else if (move == "e8-g8" && hiddenboard.position().g8 == "bK") { // black O-O - hiddenboard.move("h8-f8", false); - } else if (move == "e8-c8" && hiddenboard.position().c8 == "bK") { // black O-O-O - hiddenboard.move("a8-d8", false); - } + hiddenboard.move(ucimove_to_chessjs_move(current_display_line.uci_pv[i])); } highlighted_move = $("#automove" + current_display_line.line_number + "-" + current_display_move); highlighted_move.addClass('highlight'); - board.position(hiddenboard.position()); + board.position(hiddenboard.fen()); +} + +var ucimove_to_chessjs_move = function(move) { + var source = move.substr(0, 2); + var target = move.substr(2, 2); + var promo = move.substr(4, 1); + + if (promo === '') { + return { from: source, to: target }; + } else { + return { from: source, to: target, promotion: promo }; + } } var init = function() { @@ -856,7 +835,6 @@ var init = function() { // Create board. board = new window.ChessBoard('board', 'start'); - hiddenboard = new window.ChessBoard('hiddenboard', 'start'); request_update(); $(window).resize(function() {