function setName() {
// 'onChange' event on name input text field [HTML]
- const name = $.getElementById("myName").value;
- localStorage.setItem("name", sanitize(name, 30));
+ localStorage.setItem("name", $.getElementById("myName").value);
}
// Turn a "tab" on, and "close" all others
const options = obj.options || {};
// 1. Dynamic loading of variant js module
- const module = await import('/' + await manifest(`variants/${obj.vname}/class.js`));
+ const module =
+ await import('/' + await manifest(`variants/${obj.vname}/class.js`));
window.V = module.default;
// Export aliases in global scope (used by variants classes)
// Create SVG icons with a string, inserted securely.
const infoIcon = h('div', { id: 'upLeftInfos', onclick: toggleGameInfos });
- infoIcon.innerHTML = `<svg viewBox="0.5 0.5 100 100"><path d="M50.5,0.5c-27.614,0-50,22.386-50,50c0,27.614,22.386,50,50,50s50-22.386,50-50C100.5,22.886,78.114,0.5,50.5,0.5z M60.5,85.5h-20v-40h20V85.5z M50.5,35.5c-5.523,0-10-4.477-10-10s4.477-10,10-10c5.522,0,10,4.477,10,10S56.022,35.5,50.5,35.5z"/></svg>`;
+ infoIcon.innerHTML = `<svg viewBox="0.5 0.5 100 100">\
+<path d="M50.5,0.5c-27.614,0-50,22.386-50,50c0,27.614,22.386,50,50,50s50-\
+22.386,50-50C100.5,22.886,78.114,0.5,50.5,0.5z M60.5,85.5h-20v-40h20V85.5z \
+M50.5,35.5c-5.523,0-10-4.477-10-10s4.477-10,10-10c5.522,0,10,4.477,10,\
+10S56.022,35.5,50.5,35.5z"/></svg>`;
const stopIcon = h('div', { id: 'upRightStop', onclick: confirmStopGame });
stopIcon.innerHTML = `<svg viewBox="0 0 533.333 533.333" xmlns="http://www.w3.org/2000/svg">
options.push('m');
if (y < this.size.y)
options.push('e');
- if (x < this.size.x) {
+ if (x < this.size.x - 1) {
options.push('g');
if (y > 0)
options.push('h');
- if (y < this.size.y)
+ if (y < this.size.y - 1)
options.push('f');
}
if (x > 0) {
options.push('c');
if (y > 0)
options.push('o');
- if (y < this.size.y)
+ if (y < this.size.y - 1)
options.push('d');
}
return options;
"/pppppppp/8/8/8/8/PPPPPPPP/" +
s.w.join("").replace('l', random > 0 ? 'c' : 'd').toUpperCase();
return {
- fen: 'jfs1kb1r/1P3ppp/3p1q2/p7/2P5/8/P2PPPPP/J1SQKBNR', // fen,
+ fen,
o: {flags: s.flags}
};
}
pieces(color, x, y) {
const mirror = (this.playerColor == 'b');
- return Object.assign({
+ return {
'j': {
"class": "jailer",
moves: [
{steps: [[-1, -1]]}
]
},
- }, super.pieces(color, x, y));
+ ...super.pieces(color, x, y)
+ };
}
canIplay(x, y) {
return res;
}
-// TODO: sentry nudge
-
- // after pushedTo, if lancer : allow reorient + move, or just reorient (move to king) if stuck
-
- // later, if stuck, allow reorient only (just click)
- // doClick(coords) { TODO
+ // Reorient immobilized or stuck lancer
+ doClick(coords) {
+ if (typeof coords.x != "number")
+ return null; //click on reserves
+ if (this.board[coords.x][coords.y] != "") {
+ const p = this.getPiece(coords.x, coords.y),
+ c = this.getColor(coords.x, coords.y);
+ const lopts = this.getLancerOptions(coords.x, coords.y);
+ if (
+ V.LANCERS.includes(p) &&
+ (
+ this.pushedTo.x < 0 && //not just pushed
+ !lopts.includes(p) //can't move
+ )
+ ||
+ (
+ this.pieces()['j'].moves[0].steps.some(s => {
+ const [i, j] = [coords.x + s[0], coords.y + s[1]];
+ return (
+ this.onBoard(i, j) &&
+ this.getPiece(i, j) == 'j' &&
+ this.getColor(i, j) != c
+ );
+ })
+ )
+ ) {
+ return lopts.filter(o => o != p).map(o => {
+ return new Move({
+ appear: [ new PiPo({x: coords.x, y: coords.y, p: o, c: c}) ],
+ vanish: [ new PiPo({x: coords.x, y: coords.y, p: p, c: c}) ]
+ });
+ });
+ }
+ }
+ return null;
+ }
- getPotentialMovesFrom([x, y], color) {
+ getPotentialMovesFrom([x, y]) {
+ const p = this.getPiece(x, y);
+ let color = this.getColor(x, y);
+ let oppCol = C.GetOppTurn(color)
if (this.pushFrom.x < 0 || this.pushedTo.x >= 0) {
- let smoves = super.getPotentialMovesFrom([x, y], color);
+ let smoves = super.getPotentialMovesFrom([x, y]);
// Forbid direction x,y --> pushFrom if x,y == pushedTo
if (x == this.pushedTo.x && y == this.pushedTo.y) {
smoves = smoves.filter(m => {
[sx / divideBy, sy / divideBy]
) );
});
+ if (V.LANCERS.includes(p)) {
+ // Allow all other directions without reorient
+ const ls = this.pieces()[p].both[0].steps[0];
+ for (const lCode of V.LANCERS) {
+ const s = this.pieces()[lCode].both[0].steps[0];
+ if (s[0] != ls[0] || s[1] != ls[1]) {
+ this.board[x][y] = color + lCode;
+ super.findDestSquares([x, y], {}).forEach(r => {
+ let mv = new Move({
+ appear: [
+ new PiPo({x: r.sq[0], y: r.sq[1], p: lCode, c: color})],
+ vanish: [
+ new PiPo({x: x, y: y, p: p, c: color})],
+ noReorient: true
+ });
+ if (this.board[r.sq[0]][r.sq[1]] != "") {
+ mv.vanish.push(
+ new PiPo({
+ x: r.sq[0],
+ y: r.sq[1],
+ p: this.getPiece(r.sq[0], r.sq[1]),
+ c: oppCol
+ })
+ );
+ }
+ smoves.push(mv);
+ });
+ }
+ }
+ this.board[x][y] = color + p;
+ // Add reorient-only moves, if stuck:
+ const lopts = this.getLancerOptions(x, y);
+ if (!lopts.includes(p)) {
+ const kp = this.searchKingPos(color)[0];
+ Array.prototype.push.apply(smoves, lopts.map(o => {
+ return new Move({
+ appear: [ new PiPo({x: x, y: y, p: o, c: color}) ],
+ vanish: [ new PiPo({x: x, y: y, p: p, c: color}) ],
+ end: {x: kp[0], y: kp[1]}
+ });
+ }) );
+ }
+ }
}
-
- // TODO: lancer special case, move as a queen after push
-
return smoves.concat(this.getPassMoves(x, y));
}
// pushFrom.x >= 0 && pushedTo.x < 0
if (x != this.pushFrom.x || y != this.pushFrom.y)
return [];
// After sentry "attack": move enemy as if it was ours
- const p = this.getPiece(x, y);
- this.board[x][y] = this.turn + p;
- let pmoves = super.getPotentialMovesFrom([x, y], this.turn);
- const oppCol = C.GetOppTurn(this.turn)
+ [color, oppCol] = [oppCol, color];
+ this.board[x][y] = color + p;
+ let pmoves = super.getPotentialMovesFrom([x, y], color, true)
+ .filter(m => m.appear.length > 0); //exclude sentry "captures"
+ if (V.LANCERS.includes(p)) {
+ pmoves.forEach(m => m.noReorient = true);
+ // Allow all other steps by 1 square (nudge)
+ const ls = this.pieces()[p].both[0].steps[0];
+ let nextP = p;
+ for (const lCode of V.LANCERS) {
+ const s = this.pieces()[lCode].both[0].steps[0];
+ if (
+ (s[0] != ls[0] || s[1] != ls[1]) &&
+ this.onBoard(x + s[0], y + s[1]) &&
+ this.board[x + s[0]][y + s[1]] == ""
+ ) {
+ let mv = new Move({
+ appear: [new PiPo({x: x + s[0], y: y + s[1], p: lCode, c: color})],
+ vanish: [new PiPo({x: x, y: y, p: p, c: color})]
+ });
+ mv.noReorient = true;
+ pmoves.push(mv);
+ }
+ }
+ }
this.board[x][y] = oppCol + p;
pmoves.forEach(m => {
m.appear[0].c = m.vanish[0].c = oppCol;
m.appear.push( new PiPo({x:x, y:y, p:'s', c:this.turn}) );
});
- return pmoves;
+ return this.postProcessPotentialMoves(pmoves);
}
postProcessPotentialMoves(moves) {
moves = super.postProcessPotentialMoves(moves);
let finalMoves = [];
for (const m of moves) {
+ // Drop lancers "self captures" (not from sentry push):
+ if (
+ !m.noReorient &&
+ m.vanish.length == 2 &&
+ m.vanish[0].c == m.vanish[1].c
+ ) {
+ continue;
+ }
// Reorient a lancer after drop or regular move
if (
- (m.vanish.length == 0 && ['c', 'g'].includes(m.appear[0].p)) ||
+ !m.noReorient &&
(
- (m.vanish.length > 0 && V.LANCERS.includes(m.vanish[0].p)) &&
- // Next line test checks that the lancer wasn't just pushed away
- (m.start.x != this.pushedTo.x || m.start.y != this.pushedTo.y)
+ (m.vanish.length == 0 && ['c', 'g'].includes(m.appear[0].p)) ||
+ (
+ (m.vanish.length > 0 && V.LANCERS.includes(m.vanish[0].p)) &&
+ // Next line test checks that the lancer wasn't just pushed away
+ (m.start.x != this.pushedTo.x || m.start.y != this.pushedTo.y)
+ )
)
) {
this.getLancerOptions(m.end.x, m.end.y).forEach(o => {
underAttack([x, y], oppCols) {
if (super.underAttack([x, y], oppCols))
return true;
- // TODO: check enemy sentry(ies), for each, check all of our own pieces which attack the square (if belonging to opponent!). Then, call :
const oppCol = oppCols[0];
+ const color = C.GetOppTurn(oppCol);
for (let i=0; i < this.size.x; i++) {
for (let j=0; j < this.size.y; j++) {
if (
this.getPiece(i, j) == 's' &&
this.getColor(i, j) == oppCol
) {
- this.pieces()['s'].both[0].steps.forEach(s => {
- let ii = i + s[0];
- // TODO.........
- if (true)
- return true;
- });
+ this.board[i][j] = oppCol + 'b';
+ // Find enemy sentries "attacks":
+ const rs = super.findDestSquares([i, j], {attackOnly: true});
+ this.board[i][j] = oppCol + 's';
+ // Can any of these pieces capture our king?
+ for (const r of rs) {
+ const p = this.getPiece(r.sq[0], r.sq[1]);
+ if (['j', 's'].includes(p))
+ continue;
+ const specs = this.pieces(oppCol, r.sq[0], r.sq[1])[p];
+ const steps = (specs.both || specs.attack)[0].steps;
+ let res = false;
+ for (const s of steps) {
+ let [ii, jj] = [r.sq[0] + s[0], r.sq[1] + s[1]];
+ while (this.onBoard(ii, jj) && this.board[ii][jj] == "")
+ [ii, jj] = [ii + s[0], jj + s[1]];
+ if (ii == x && jj == y)
+ return true;
+ }
+ }
}
}
}