Merge pull request #6219 from niklasf/client-side-puzzle-socket
client side puzzle socket
This commit is contained in:
commit
23f1d8a81b
|
@ -1,4 +1,5 @@
|
||||||
import { piotr } from './piotr';
|
import { piotr } from './piotr';
|
||||||
|
export { uciCharPair } from './ucicharpair';
|
||||||
|
|
||||||
export const initialFen: Fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
|
export const initialFen: Fen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';
|
||||||
|
|
||||||
|
|
34
ui/chess/src/ucicharpair.ts
Normal file
34
ui/chess/src/ucicharpair.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
type Square = number;
|
||||||
|
|
||||||
|
type Role = 'pawn' | 'knight' | 'bishop' | 'rook' | 'queen' | 'king';
|
||||||
|
|
||||||
|
interface UciMove {
|
||||||
|
from: Square;
|
||||||
|
to: Square;
|
||||||
|
promotion?: Role;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UciDrop {
|
||||||
|
role: Role;
|
||||||
|
to: Square;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Uci = UciMove | UciDrop;
|
||||||
|
|
||||||
|
function square(sq: Square): number {
|
||||||
|
return 35 + sq;
|
||||||
|
}
|
||||||
|
|
||||||
|
function drop(role: Role): number {
|
||||||
|
return 35 + 64 + 8 * 5 + ['queen', 'rook', 'bishop', 'knight', 'pawn'].indexOf(role);
|
||||||
|
}
|
||||||
|
|
||||||
|
function promotion(file: number, role: Role): number {
|
||||||
|
return 35 + 64 + 8 * ['queen', 'rook', 'bishop', 'knight', 'king'].indexOf(role) + file;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function uciCharPair(uci: Uci): string {
|
||||||
|
if ('role' in uci) return String.fromCharCode(square(uci.to), drop(uci.role));
|
||||||
|
else if (!uci.promotion) return String.fromCharCode(square(uci.to), square(uci.from));
|
||||||
|
else return String.fromCharCode(square(uci.from), promotion(uci.to & 7, uci.promotion));
|
||||||
|
}
|
|
@ -21,6 +21,7 @@
|
||||||
"ceval": "2.0.0",
|
"ceval": "2.0.0",
|
||||||
"chess": "2.0.0",
|
"chess": "2.0.0",
|
||||||
"chessground": "^7.6",
|
"chessground": "^7.6",
|
||||||
|
"chessops": "^0.3.4",
|
||||||
"common": "2.0.0",
|
"common": "2.0.0",
|
||||||
"snabbdom": "ornicar/snabbdom#0.7.1-lichess",
|
"snabbdom": "ornicar/snabbdom#0.7.1-lichess",
|
||||||
"tree": "2.0.0"
|
"tree": "2.0.0"
|
||||||
|
|
|
@ -400,7 +400,6 @@ export default function(opts: PuzzleOpts, redraw: Redraw): Controller {
|
||||||
}
|
}
|
||||||
|
|
||||||
const socket = socketBuild({
|
const socket = socketBuild({
|
||||||
send: opts.socketSend,
|
|
||||||
addNode: addNode,
|
addNode: addNode,
|
||||||
addDests: addDests,
|
addDests: addDests,
|
||||||
reset: function() {
|
reset: function() {
|
||||||
|
@ -482,7 +481,6 @@ export default function(opts: PuzzleOpts, redraw: Redraw): Controller {
|
||||||
getCeval,
|
getCeval,
|
||||||
pref: opts.pref,
|
pref: opts.pref,
|
||||||
trans: window.lichess.trans(opts.i18n),
|
trans: window.lichess.trans(opts.i18n),
|
||||||
socketReceive: socket.receive,
|
|
||||||
gameOver,
|
gameOver,
|
||||||
toggleCeval,
|
toggleCeval,
|
||||||
toggleThreatMode,
|
toggleThreatMode,
|
||||||
|
|
|
@ -45,7 +45,6 @@ export interface Controller extends KeyboardController {
|
||||||
thanks(): boolean;
|
thanks(): boolean;
|
||||||
vote(v: boolean): void;
|
vote(v: boolean): void;
|
||||||
pref: PuzzlePrefs;
|
pref: PuzzlePrefs;
|
||||||
socketReceive: any;
|
|
||||||
userMove(orig: Key, dest: Key): void;
|
userMove(orig: Key, dest: Key): void;
|
||||||
promotion: any;
|
promotion: any;
|
||||||
|
|
||||||
|
@ -79,7 +78,6 @@ export interface PuzzleOpts {
|
||||||
pref: PuzzlePrefs;
|
pref: PuzzlePrefs;
|
||||||
data: PuzzleData;
|
data: PuzzleData;
|
||||||
i18n: { [key: string]: string | undefined };
|
i18n: { [key: string]: string | undefined };
|
||||||
socketSend: any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PuzzlePrefs {
|
export interface PuzzlePrefs {
|
||||||
|
|
|
@ -14,7 +14,7 @@ menuHover();
|
||||||
|
|
||||||
const patch = init([klass, attributes]);
|
const patch = init([klass, attributes]);
|
||||||
|
|
||||||
export default function(opts) {
|
export default function(opts): void {
|
||||||
|
|
||||||
let vnode: VNode, ctrl: Controller;
|
let vnode: VNode, ctrl: Controller;
|
||||||
|
|
||||||
|
@ -27,10 +27,6 @@ export default function(opts) {
|
||||||
const blueprint = view(ctrl);
|
const blueprint = view(ctrl);
|
||||||
opts.element.innerHTML = '';
|
opts.element.innerHTML = '';
|
||||||
vnode = patch(opts.element, blueprint);
|
vnode = patch(opts.element, blueprint);
|
||||||
|
|
||||||
return {
|
|
||||||
socketReceive: ctrl.socketReceive
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// that's for the rest of lichess to access chessground
|
// that's for the rest of lichess to access chessground
|
||||||
|
|
|
@ -1,63 +1,68 @@
|
||||||
|
import { Chess } from 'chessops/chess';
|
||||||
|
import { parseFen, makeFen } from 'chessops/fen';
|
||||||
|
import { makeSanAndPlay } from 'chessops/san';
|
||||||
|
import { parseSquare, makeUci, parseUci } from 'chessops/util';
|
||||||
|
import { altCastles, uciCharPair } from 'chess';
|
||||||
|
import { defined } from 'common';
|
||||||
|
|
||||||
export default function(opts) {
|
export default function(opts) {
|
||||||
|
|
||||||
var anaMoveTimeout;
|
function piotr(sq: number): string {
|
||||||
var anaDestsTimeout;
|
if (sq < 26) return String.fromCharCode('a'.charCodeAt(0) + sq);
|
||||||
|
else if (sq < 52) return String.fromCharCode('A'.charCodeAt(0) + sq - 26);
|
||||||
|
else if (sq < 62) return String.fromCharCode('0'.charCodeAt(0) + sq - 52);
|
||||||
|
else if (sq == 62) return '!';
|
||||||
|
else return '?';
|
||||||
|
}
|
||||||
|
|
||||||
var anaDestsCache = {};
|
function makeDests(pos: Chess): string {
|
||||||
|
const dests = pos.allDests();
|
||||||
|
|
||||||
var handlers = {
|
// add two step castling moves (standard chess)
|
||||||
node: function(data) {
|
const king = pos.board.kingOf(pos.turn);
|
||||||
clearTimeout(anaMoveTimeout);
|
if (defined(king) && king & 4 && dests.has(king)) {
|
||||||
opts.addNode(data.node, data.path);
|
if (dests.get(king)!.has(0)) dests.set(king, dests.get(king)!.with(2));
|
||||||
},
|
if (dests.get(king)!.has(7)) dests.set(king, dests.get(king)!.with(6));
|
||||||
stepFailure: function() {
|
if (dests.get(king)!.has(56)) dests.set(king, dests.get(king)!.with(58));
|
||||||
clearTimeout(anaMoveTimeout);
|
if (dests.get(king)!.has(63)) dests.set(king, dests.get(king)!.with(62));
|
||||||
opts.reset();
|
|
||||||
},
|
|
||||||
dests: function(data) {
|
|
||||||
anaDestsCache[data.path] = data;
|
|
||||||
opts.addDests(data.dests, data.path);
|
|
||||||
clearTimeout(anaDestsTimeout);
|
|
||||||
},
|
|
||||||
destsFailure: function(data) {
|
|
||||||
console.log(data);
|
|
||||||
clearTimeout(anaDestsTimeout);
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
var sendAnaMove = function(req) {
|
const result: string[] = [];
|
||||||
clearTimeout(anaMoveTimeout);
|
for (const [from, squares] of dests) {
|
||||||
opts.send('anaMove', req);
|
if (squares.nonEmpty()) result.push([from, ...Array.from(squares)].map(piotr).join(''));
|
||||||
anaMoveTimeout = setTimeout(function() {
|
|
||||||
sendAnaMove(req);
|
|
||||||
}, 3000);
|
|
||||||
};
|
|
||||||
|
|
||||||
var sendAnaDests = function(req) {
|
|
||||||
clearTimeout(anaDestsTimeout);
|
|
||||||
if (anaDestsCache[req.path]) setTimeout(function() {
|
|
||||||
handlers.dests(anaDestsCache[req.path]);
|
|
||||||
}, 10);
|
|
||||||
else {
|
|
||||||
opts.send('anaDests', req);
|
|
||||||
anaDestsTimeout = setTimeout(function() {
|
|
||||||
sendAnaDests(req);
|
|
||||||
}, 3000);
|
|
||||||
}
|
}
|
||||||
};
|
return result.join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendAnaMove(req) {
|
||||||
|
const setup = parseFen(req.fen).unwrap();
|
||||||
|
const pos = Chess.fromSetup(setup).unwrap();
|
||||||
|
const move = {
|
||||||
|
from: parseSquare(req.orig)!,
|
||||||
|
to: parseSquare(req.dest)!,
|
||||||
|
promotion: req.promotion,
|
||||||
|
};
|
||||||
|
const san = makeSanAndPlay(pos, move);
|
||||||
|
const uci = san.startsWith('O-O') && altCastles[makeUci(move)] || makeUci(move);
|
||||||
|
setTimeout(() => opts.addNode({
|
||||||
|
ply: 2 * (pos.fullmoves - 1) + (pos.turn == 'white' ? 0 : 1),
|
||||||
|
fen: makeFen(pos.toSetup()),
|
||||||
|
dests: makeDests(pos),
|
||||||
|
children: [],
|
||||||
|
san,
|
||||||
|
uci,
|
||||||
|
id: uciCharPair(parseUci(uci)!),
|
||||||
|
}, req.path), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendAnaDests(req) {
|
||||||
|
const setup = parseFen(req.fen).unwrap();
|
||||||
|
const pos = Chess.fromSetup(setup).unwrap();
|
||||||
|
setTimeout(() => opts.addDests(makeDests(pos), req.path), 10);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
send: opts.send,
|
|
||||||
receive: function(type, data) {
|
|
||||||
if (handlers[type]) {
|
|
||||||
handlers[type](data);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
sendAnaMove: sendAnaMove,
|
sendAnaMove: sendAnaMove,
|
||||||
|
sendAnaDests: sendAnaDests,
|
||||||
sendAnaDests: sendAnaDests
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -955,15 +955,8 @@
|
||||||
////////////////
|
////////////////
|
||||||
|
|
||||||
function startPuzzle(cfg) {
|
function startPuzzle(cfg) {
|
||||||
var puzzle;
|
|
||||||
cfg.element = document.querySelector('main.puzzle');
|
cfg.element = document.querySelector('main.puzzle');
|
||||||
lichess.socket = lichess.StrongSocket('/socket/v4', false, {
|
LichessPuzzle.default(cfg);
|
||||||
receive: function(t, d) {
|
|
||||||
puzzle.socketReceive(t, d);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
cfg.socketSend = lichess.socket.send;
|
|
||||||
puzzle = LichessPuzzle.default(cfg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////
|
////////////////////
|
||||||
|
|
Loading…
Reference in a new issue