From: Benjamin Auder Date: Wed, 24 Jun 2026 04:15:52 +0000 (+0200) Subject: Fanorona seems ok but not fluid.. X-Git-Url: https://git.auder.net/css/figure/doc/html/current/gitweb.css?a=commitdiff_plain;h=HEAD;p=xogo.git Fanorona seems ok but not fluid.. --- diff --git a/oldF.js b/oldF.js new file mode 100644 index 0000000..4e71efd --- /dev/null +++ b/oldF.js @@ -0,0 +1,215 @@ +import ChessRules from "/js/base_rules.js"; +import AbstractOnGridRules from "/variants/_OnGrid/class.js"; +import PiPo from "/utils/PiPo.js"; + +export default class FanoronaRules extends AbstractOnGridRules { + + static get Options() { + return {}; + } + + get hasFlags() { + return false; + } + + get hasEnpassant() { + return false; + } + static get HasKing() { + return false; + } + + genRandInitBaseFen() { + return { + fen: "sssssssss/sssssssss/sSsS1sSsS/SSSSSSSSS/SSSSSSSSS", + o: {} + }; + } + + setOtherVariables(fen) { + super.setOtherVariables(fen); + // Local stack of captures during a turn (squares + directions) + this.captures = []; + } + + get size() { + return { x: 5, y: 9 }; + } + + getPiece() { + return 's'; + } + + pieceDef(piece, color, x, y) { + if (piece == 's') //stone + return { "class": "stone" }; + // Arrow + return { + "class": "arrow-" + (piece.charCodeAt(0) >= 105 ? "behind" : "front") + }; + } + + // a,b,c,d,e,f,g,h : dot on point, N to NO + // i,j,k,l,m,n,o,p : dot on base, N to NO + static ArrowToAngle(arrow) { + let ccode = arrow.charCodeAt(0); + if (ccode >= 105) { //i + ccode -= 8; + arrow = String.fromCharCode(ccode); + } + return (45 * (ccode - 97)) + "deg"; + } + + // Draw arrow + setPieceBackground(domPiece, piece) { + if (piece == 's') + return; + domPiece.style.setProperty('--rotate-by', V.ArrowToAngle(piece)); + } + + getPotentialMovesFrom([x, y], justCapt) { + // After moving, add stones captured in "step" direction from new location + // [x, y] to mv.vanish (if any captured stone!) + const oppCol = C.GetOppTurn(this.turn); + const addCapture = ([x, y], step, move) => { + let [i, j] = [x + step[0], y + step[1]]; + while ( + this.onBoard(i, j) && + this.board[i][j] != "" && + this.getColor(i, j) == oppCol + ) { + move.vanish.push(new PiPo({ x: i, y: j, c: oppCol, p: 's' })); + [i, j] = [i + step[0], j + step[1]]; + } + return (move.vanish.length >= 2); + }; + const stepToArrow = (s, forward) => { + const baseShift = (forward ? 0 : 8), + colShift = (this.playerColor=='w' ? 0 : 4); + const doShift = (c) => { + return String.fromCharCode( + 97 + (c.charCodeAt(0) - 97 + colShift) % 8 + baseShift); + }; + switch (s) { + case "-1_0": return doShift('a'); + case "-1_1": return doShift('b'); + case "0_1": return doShift('c'); + case "1_1": return doShift('d'); + case "1_0": return doShift('e'); + case "1_-1": return doShift('f'); + case "0_-1": return doShift('g'); + case "-1_-1": return doShift('h'); + } + return ''; //never reached + }; + const L = this.captures.length; + if (L > 0) { + const c = this.captures[L-1]; + if (x != c.square.x + c.step[0] || y != c.square.y + c.step[1]) + return (!justCapt ? [] : false); + } + let steps = super.pieceDef('r').both[0].steps; + if ((x + y) % 2 == 0) + steps = steps.concat(super.pieceDef('b').both[0].steps); + let moves = []; + for (let s of steps) { + if (!justCapt && L > 0 && c.step[0] == s[0] && c.step[1] == s[1]) { + // Add a move to say "I'm done capturing" + moves.push( + new Move({ + appear: [], + vanish: [], + start: { x: x, y: y }, + end: { x: x - s[0], y: y - s[1] } + }) + ); + continue; + } + let [i, j] = [x + s[0], y + s[1]]; + if (this.captures.some(c => c.square.x == i && c.square.y == j)) + continue; + if (this.onBoard(i, j) && this.board[i][j] == "") { + // The move is possible. Might lead to 2 different captures + let mv = super.getBasicMove([x, y], [i, j]); + const capt = addCapture([i, j], s, mv); + if (capt) { + if (!!justCapt) + return true; + mv.choice = stepToArrow(s[0] + "_" + s[1], true); + moves.push(mv); + mv = super.getBasicMove([x, y], [i, j]); //cheap enough + } + const capt_bw = addCapture([x, y], [-s[0], -s[1]], mv); + if (capt_bw) { + if (!!justCapt) + return true; + mv.choice = stepToArrow(s[0] + "_" + s[1], false); + moves.push(mv); + } + // Captures take priority (if available) + if (!justCapt && !capt && !capt_bw && L == 0) + moves.push(mv); + } + } + return (!justCapt ? moves : false); + } + + atLeastOneCapture() { + const oppCol = C.GetOppTurn(this.turn); + const L = this.captures.length; + // Called after at least one capture, so L > 0 + if (L > 0) { + const c = this.captures[L-1]; + return this.getPotentialMovesFrom([c.square.x, c.square.y], true); + } + for (let i = 0; i < this.size.x; i++) { + for (let j = 0; j < this.size.y; j++) { + if ( + this.board[i][j] != "" && + this.getColor(i, j) == this.turn && + this.getPotentialMovesFrom([i, j], true) + ) { + return true; + } + } + } + return false; + } + + filterValid(moves) { + return moves; + } + + play(move) { + this.playOnBoard(move); + if (move.vanish.length >= 2) { + this.captures.push({ + square: move.start, + step: [move.end.x - move.start.x, move.end.y - move.start.y] + }); + if (this.atLeastOneCapture()) + // There could be other captures (optional) + move.notTheEnd = true; + } + if (!move.notTheEnd) { + this.turn = C.GetOppTurn(this.turn); + this.movesCount++; + this.captures = []; + } + } + + getCurrentScore() { + // If no stones on board, I lose + if ( + this.board.every(b => { + return b.every(cell => { + return (cell == "" || cell[0] != this.turn); + }); + }) + ) { + return (this.turn == 'w' ? "0-1" : "1-0"); + } + return "*"; + } + +}; diff --git a/pieces/Fanorona/black_stone.svg b/pieces/Fanorona/black_stone.svg new file mode 100644 index 0000000..b5a8dcf --- /dev/null +++ b/pieces/Fanorona/black_stone.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/pieces/Fanorona/white_stone.svg b/pieces/Fanorona/white_stone.svg new file mode 100644 index 0000000..1529a41 --- /dev/null +++ b/pieces/Fanorona/white_stone.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/variants/Fanorona/class.js b/variants/Fanorona/class.js index 4e71efd..4dd7be1 100644 --- a/variants/Fanorona/class.js +++ b/variants/Fanorona/class.js @@ -1,5 +1,6 @@ import ChessRules from "/js/base_rules.js"; import AbstractOnGridRules from "/variants/_OnGrid/class.js"; +import Move from "/utils/Move.js"; import PiPo from "/utils/PiPo.js"; export default class FanoronaRules extends AbstractOnGridRules { @@ -26,6 +27,30 @@ export default class FanoronaRules extends AbstractOnGridRules { }; } + getSvgChessboard() { + let board = super.getSvgChessboard().slice(0, -6); + // Add diagonals: + for (const i of [0, 2, 4]) { + const x1 = i * 10 + 5, + x2 = (4+i) * 10 + 5; + for (const [y1, y2] of [[5, 45], [45, 5]]) { + board += ` + `; + } + } + for (const i of [0,4]) { + const y2 = i * 10 + 5; + for (const [x1, x2] of [[5,2*10+5], [8*10+5, 6*10+5]]) { + board += ` + `; + } + } + board += ""; + return board; + } + setOtherVariables(fen) { super.setOtherVariables(fen); // Local stack of captures during a turn (squares + directions) @@ -68,10 +93,10 @@ export default class FanoronaRules extends AbstractOnGridRules { } getPotentialMovesFrom([x, y], justCapt) { - // After moving, add stones captured in "step" direction from new location - // [x, y] to mv.vanish (if any captured stone!) const oppCol = C.GetOppTurn(this.turn); const addCapture = ([x, y], step, move) => { + // After moving, add stones captured in "step" direction from new + // location [x, y] to mv.vanish (if any captured stone!) let [i, j] = [x + step[0], y + step[1]]; while ( this.onBoard(i, j) && @@ -103,8 +128,9 @@ export default class FanoronaRules extends AbstractOnGridRules { return ''; //never reached }; const L = this.captures.length; + let c; if (L > 0) { - const c = this.captures[L-1]; + c = this.captures[L-1]; if (x != c.square.x + c.step[0] || y != c.square.y + c.step[1]) return (!justCapt ? [] : false); } @@ -115,14 +141,14 @@ export default class FanoronaRules extends AbstractOnGridRules { for (let s of steps) { if (!justCapt && L > 0 && c.step[0] == s[0] && c.step[1] == s[1]) { // Add a move to say "I'm done capturing" - moves.push( - new Move({ - appear: [], - vanish: [], - start: { x: x, y: y }, - end: { x: x - s[0], y: y - s[1] } - }) - ); + let mv = new Move({ + appear: [], + vanish: [], + start: { x: x, y: y }, + end: { x: x - s[0], y: y - s[1] } + }); + mv.noAnimate = true; + moves.push(mv); continue; } let [i, j] = [x + s[0], y + s[1]]; @@ -155,12 +181,11 @@ export default class FanoronaRules extends AbstractOnGridRules { } atLeastOneCapture() { - const oppCol = C.GetOppTurn(this.turn); const L = this.captures.length; - // Called after at least one capture, so L > 0 if (L > 0) { const c = this.captures[L-1]; - return this.getPotentialMovesFrom([c.square.x, c.square.y], true); + return this.getPotentialMovesFrom( + [c.square.x + c.step[0], c.square.y + c.step[1]], true); } for (let i = 0; i < this.size.x; i++) { for (let j = 0; j < this.size.y; j++) { @@ -177,7 +202,9 @@ export default class FanoronaRules extends AbstractOnGridRules { } filterValid(moves) { - return moves; + if (this.captures.length > 0 || !this.atLeastOneCapture()) + return moves; + return moves.filter(m => m.vanish.length >= 2); } play(move) { diff --git a/variants/Fanorona/style.css b/variants/Fanorona/style.css index 29e9c78..b96fdef 100644 --- a/variants/Fanorona/style.css +++ b/variants/Fanorona/style.css @@ -3,10 +3,10 @@ } piece.white.stone { - background-image: url('/pieces/Weiqi/black_stone.svg'); + background-image: url('/pieces/Fanorona/black_stone.svg'); } piece.black.stone { - background-image: url('/pieces/Weiqi/white_stone.svg'); + background-image: url('/pieces/Fanorona/white_stone.svg'); } .arrow-front {