X-Git-Url: https://git.auder.net/%7B%7B%20path%28%27mixstore_store_package_upsert%27%29%20%7D%7D?a=blobdiff_plain;f=base_rules.js;h=3fa4e0bf40baa5514c40315eb5f21bae97b83c16;hb=437dfd42748eb2359103fd87a7d0e780121a7865;hp=df49f841d1bbce53205e4d09db153c594809f0af;hpb=9db5050a189571c9f3018d4d25c7d304ca1a22dc;p=xogo.git
diff --git a/base_rules.js b/base_rules.js
index df49f84..3fa4e0b 100644
--- a/base_rules.js
+++ b/base_rules.js
@@ -29,16 +29,18 @@ export default class ChessRules {
{label: "Asymmetric random", value: 2}
]
}],
- check: [
+ input: [
{
label: "Capture king",
- defaut: false,
- variable: "taking"
+ variable: "taking",
+ type: "checkbox",
+ defaut: false
},
{
label: "Falling pawn",
- defaut: false,
- variable: "pawnfall"
+ variable: "pawnfall",
+ type: "checkbox",
+ defaut: false
}
],
// Game modifiers (using "elementary variants"). Default: false
@@ -98,7 +100,7 @@ export default class ChessRules {
this.options["teleport"] && this.subTurnTeleport == 2 &&
this.board[coords.x][coords.y] == ""
) {
- return new Move({
+ let res = new Move({
start: {x: this.captured.x, y: this.captured.y},
appear: [
new PiPo({
@@ -108,9 +110,10 @@ export default class ChessRules {
p: this.captured.p
})
],
- vanish: [],
- drag: {c: this.captured.c, p: this.captured.p}
+ vanish: []
});
+ res.drag = {c: this.captured.c, p: this.captured.p};
+ return res;
}
return null;
}
@@ -289,19 +292,20 @@ export default class ChessRules {
return fen;
}
+ static FenEmptySquares(count) {
+ // if more than 9 consecutive free spaces, break the integer,
+ // otherwise FEN parsing will fail.
+ if (count <= 9)
+ return count;
+ // Most boards of size < 18:
+ if (count <= 18)
+ return "9" + (count - 9);
+ // Except Gomoku:
+ return "99" + (count - 18);
+ }
+
// Position part of the FEN string
getPosition() {
- const format = (count) => {
- // if more than 9 consecutive free spaces, break the integer,
- // otherwise FEN parsing will fail.
- if (count <= 9)
- return count;
- // Most boards of size < 18:
- if (count <= 18)
- return "9" + (count - 9);
- // Except Gomoku:
- return "99" + (count - 18);
- };
let position = "";
for (let i = 0; i < this.size.y; i++) {
let emptyCount = 0;
@@ -311,7 +315,7 @@ export default class ChessRules {
else {
if (emptyCount > 0) {
// Add empty squares in-between
- position += format(emptyCount);
+ position += C.FenEmptySquares(emptyCount);
emptyCount = 0;
}
position += this.board2fen(this.board[i][j]);
@@ -319,7 +323,7 @@ export default class ChessRules {
}
if (emptyCount > 0)
// "Flush remainder"
- position += format(emptyCount);
+ position += C.FenEmptySquares(emptyCount);
if (i < this.size.y - 1)
position += "/"; //separate rows
}
@@ -370,6 +374,11 @@ export default class ChessRules {
constructor(o) {
this.options = o.options;
+ // Fill missing options (always the case if random challenge)
+ (V.Options.select || []).concat(V.Options.input || []).forEach(opt => {
+ if (this.options[opt.variable] === undefined)
+ this.options[opt.variable] = opt.defaut;
+ });
this.playerColor = o.color;
this.afterPlay = o.afterPlay; //trigger some actions after playing a move
@@ -455,7 +464,7 @@ export default class ChessRules {
const steps = this.pieces(this.playerColor)["p"].attack[0].steps;
for (let step of steps) {
const x = this.epSquare.x - step[0],
- y = this.computeY(this.epSquare.y - step[1]);
+ y = this.getY(this.epSquare.y - step[1]);
if (
this.onBoard(x, y) &&
this.getColor(x, y) == this.playerColor &&
@@ -526,7 +535,6 @@ export default class ChessRules {
this.initMouseEvents();
const chessboard =
document.getElementById(this.containerId).querySelector(".chessboard");
- new ResizeObserver(this.rescale).observe(chessboard);
}
re_drawBoardElements() {
@@ -549,7 +557,7 @@ export default class ChessRules {
cbHeight = Math.min(window.innerHeight, 767);
cbWidth = cbHeight * this.size.ratio;
}
- if (this.reserve) {
+ if (this.hasReserve) {
const sqSize = cbWidth / this.size.y;
// NOTE: allocate space for reserves (up/down) even if they are empty
// Cannot use getReserveSquareSize() here, but sqSize is an upper bound.
@@ -581,8 +589,7 @@ export default class ChessRules {
let board = `
";
+ board += "";
return board;
}
@@ -648,7 +657,7 @@ export default class ChessRules {
}
}
}
- if (this.reserve)
+ if (this.hasReserve)
this.re_drawReserve(['w', 'b'], r);
}
@@ -764,44 +773,48 @@ export default class ChessRules {
}
}
- // After resize event: no need to destroy/recreate pieces
- rescale() {
- const container = document.getElementById(this.containerId);
- if (!container)
- return; //useful at initial loading
- let chessboard = container.querySelector(".chessboard");
+ // Resize board: no need to destroy/recreate pieces
+ rescale(mode) {
+ let chessboard =
+ document.getElementById(this.containerId).querySelector(".chessboard");
const r = chessboard.getBoundingClientRect();
- const newRatio = r.width / r.height;
- let newWidth = r.width,
- newHeight = r.height;
- if (newRatio > this.size.ratio) {
- newWidth = r.height * this.size.ratio;
- chessboard.style.width = newWidth + "px";
+ const multFact = (mode == "up" ? 1.05 : 0.95);
+ let [newWidth, newHeight] = [multFact * r.width, multFact * r.height];
+ // Stay in window:
+ if (newWidth > window.innerWidth) {
+ newWidth = window.innerWidth;
+ newHeight = newWidth / this.size.ratio;
}
- else if (newRatio < this.size.ratio) {
- newHeight = r.width / this.size.ratio;
- chessboard.style.height = newHeight + "px";
+ if (newHeight > window.innerHeight) {
+ newHeight = window.innerHeight;
+ newWidth = newHeight * this.size.ratio;
}
+ chessboard.style.width = newWidth + "px";
+ chessboard.style.height = newHeight + "px";
const newX = (window.innerWidth - newWidth) / 2;
chessboard.style.left = newX + "px";
const newY = (window.innerHeight - newHeight) / 2;
chessboard.style.top = newY + "px";
const newR = {x: newX, y: newY, width: newWidth, height: newHeight};
const pieceWidth = this.getPieceWidth(newWidth);
- for (let i=0; i < this.size.x; i++) {
- for (let j=0; j < this.size.y; j++) {
- if (this.g_pieces[i][j]) {
- // NOTE: could also use CSS transform "scale"
- this.g_pieces[i][j].style.width = pieceWidth + "px";
- this.g_pieces[i][j].style.height = pieceWidth + "px";
- const [ip, jp] = this.getPixelPosition(i, j, newR);
- // Translate coordinates to use chessboard as reference:
- this.g_pieces[i][j].style.transform =
- `translate(${ip - newX}px,${jp - newY}px)`;
+ // NOTE: next "if" for variants which use squares filling
+ // instead of "physical", moving pieces
+ if (this.g_pieces) {
+ for (let i=0; i < this.size.x; i++) {
+ for (let j=0; j < this.size.y; j++) {
+ if (this.g_pieces[i][j]) {
+ // NOTE: could also use CSS transform "scale"
+ this.g_pieces[i][j].style.width = pieceWidth + "px";
+ this.g_pieces[i][j].style.height = pieceWidth + "px";
+ const [ip, jp] = this.getPixelPosition(i, j, newR);
+ // Translate coordinates to use chessboard as reference:
+ this.g_pieces[i][j].style.transform =
+ `translate(${ip - newX}px,${jp - newY}px)`;
+ }
}
}
}
- if (this.reserve)
+ if (this.hasReserve)
this.rescaleReserve(newR);
}
@@ -932,11 +945,6 @@ export default class ChessRules {
};
const mouseup = (e) => {
- const newR = chessboard.getBoundingClientRect();
- if (newR.width != r.width || newR.height != r.height) {
- this.rescale();
- return;
- }
if (!start)
return;
const [x, y] = [start.x, start.y];
@@ -965,6 +973,8 @@ export default class ChessRules {
document.addEventListener("mousedown", mousedown);
document.addEventListener("mousemove", mousemove);
document.addEventListener("mouseup", mouseup);
+ document.addEventListener("wheel",
+ (e) => this.rescale(e.deltaY < 0 ? "up" : "down"));
}
if ('ontouchstart' in window) {
// https://stackoverflow.com/a/42509310/12660887
@@ -1159,7 +1169,7 @@ export default class ChessRules {
// MOVES GENERATION
// For Cylinder: get Y coordinate
- computeY(y) {
+ getY(y) {
if (!this.options["cylinder"])
return y;
let res = y % this.size.y;
@@ -1180,13 +1190,13 @@ export default class ChessRules {
const attacks = specs.attack || specs.moves;
for (let a of attacks) {
outerLoop: for (let step of a.steps) {
- let [ii, jj] = [i + step[0], this.computeY(j + step[1])];
+ let [ii, jj] = [i + step[0], this.getY(j + step[1])];
let stepCounter = 1;
while (this.onBoard(ii, jj) && this.board[ii][jj] == "") {
if (a.range <= stepCounter++)
continue outerLoop;
ii += step[0];
- jj = this.computeY(jj + step[1]);
+ jj = this.getY(jj + step[1]);
}
if (
this.onBoard(ii, jj) &&
@@ -1207,7 +1217,7 @@ export default class ChessRules {
getDropMovesFrom([c, p]) {
// NOTE: by design, this.reserve[c][p] >= 1 on user click
- // (but not necessarily otherwise)
+ // (but not necessarily otherwise: atLeastOneMove() etc)
if (this.reserve[c][p] == 0)
return [];
let moves = [];
@@ -1323,7 +1333,7 @@ export default class ChessRules {
];
for (let step of steps) {
let x = m.end.x + step[0];
- let y = this.computeY(m.end.y + step[1]);
+ let y = this.getY(m.end.y + step[1]);
if (
this.onBoard(x, y) &&
this.board[x][y] != "" &&
@@ -1455,7 +1465,7 @@ export default class ChessRules {
if (a.range <= stepCounter++)
continue outerLoop;
i += step[0];
- j = this.computeY(j + step[1]);
+ j = this.getY(j + step[1]);
}
if (
this.onBoard(i, j) &&
@@ -1474,23 +1484,49 @@ export default class ChessRules {
const color = this.getColor(x, y);
const stepSpec = this.pieces(color, x, y)[piece];
let moves = [];
- let explored = {}; //for Cylinder mode
+ // Next 3 for Cylinder mode:
+ let explored = {};
+ let segments = [];
+ let segStart = [];
+
+ const addMove = (start, end) => {
+ let newMove = this.getBasicMove(start, end);
+ if (segments.length > 0) {
+ newMove.segments = JSON.parse(JSON.stringify(segments));
+ newMove.segments.push([[segStart[0], segStart[1]], [end[0], end[1]]]);
+ }
+ moves.push(newMove);
+ };
const findAddMoves = (type, stepArray) => {
for (let s of stepArray) {
- // TODO: if jump in y (computeY, Cylinder), move.segments
outerLoop: for (let step of s.steps) {
- let [i, j] = [x + step[0], this.computeY(y + step[1])];
- let stepCounter = 1;
- while (this.onBoard(i, j) && this.board[i][j] == "") {
- if (type != "attack" && !explored[i + "." + j]) {
+ segments = [];
+ segStart = [x, y];
+ let [i, j] = [x, y];
+ let stepCounter = 0;
+ while (
+ this.onBoard(i, j) &&
+ (this.board[i][j] == "" || (i == x && j == y))
+ ) {
+ if (
+ type != "attack" &&
+ !explored[i + "." + j] &&
+ (i != x || j != y)
+ ) {
explored[i + "." + j] = true;
- moves.push(this.getBasicMove([x, y], [i, j]));
+ addMove([x, y], [i, j]);
}
if (s.range <= stepCounter++)
continue outerLoop;
+ const oldIJ = [i, j];
i += step[0];
- j = this.computeY(j + step[1]);
+ j = this.getY(j + step[1]);
+ if (Math.abs(j - oldIJ[1]) > 1) {
+ // Boundary between segments (cylinder mode)
+ segments.push([[segStart[0], segStart[1]], oldIJ]);
+ segStart = [i, j];
+ }
}
if (!this.onBoard(i, j))
continue;
@@ -1511,7 +1547,7 @@ export default class ChessRules {
)
) {
explored[i + "." + j] = true;
- moves.push(this.getBasicMove([x, y], [i, j]));
+ addMove([x, y], [i, j]);
}
}
}
@@ -1521,26 +1557,29 @@ export default class ChessRules {
if (specialAttack)
findAddMoves("attack", stepSpec.attack);
findAddMoves(specialAttack ? "moveonly" : "all", stepSpec.moves);
- if (this.options["zen"])
- Array.prototype.push.apply(moves, this.findCapturesOn([x, y], true));
+ if (this.options["zen"]) {
+ Array.prototype.push.apply(moves,
+ this.findCapturesOn([x, y], {zen: true}));
+ }
return moves;
}
- findCapturesOn([x, y], zen) {
+ // Search for enemy (or not) pieces attacking [x, y]
+ findCapturesOn([x, y], args) {
let moves = [];
- // Find reverse captures (opponent takes)
- const color = this.getColor(x, y);
- const oppCol = C.GetOppCol(color);
+ if (!args.oppCol)
+ args.oppCol = C.GetOppCol(this.getColor(x, y) || this.turn);
for (let i=0; i= 1);
+ return (
+ this.findCapturesOn([x, y], {oppCol: oppCol, one: true}).length >= 1
+ );
}
// Stop at first king found (TODO: multi-kings)
@@ -2099,9 +2148,7 @@ export default class ChessRules {
playVisual(move, r) {
move.vanish.forEach(v => {
- // TODO: next "if" shouldn't be required
- if (this.g_pieces[v.x][v.y])
- this.g_pieces[v.x][v.y].remove();
+ this.g_pieces[v.x][v.y].remove();
this.g_pieces[v.x][v.y] = null;
});
let chessboard =
@@ -2148,16 +2195,18 @@ export default class ChessRules {
return;
}
let initPiece = this.getDomPiece(move.start.x, move.start.y);
- if (!initPiece) { //TODO this shouldn't be required
- callback();
- return;
- }
// NOTE: cloning generally not required, but light enough, and simpler
let movingPiece = initPiece.cloneNode();
initPiece.style.opacity = "0";
let container =
document.getElementById(this.containerId)
const r = container.querySelector(".chessboard").getBoundingClientRect();
+ if (typeof move.start.x == "string") {
+ // Need to bound width/height (was 100% for reserve pieces)
+ const pieceWidth = this.getPieceWidth(r.width);
+ movingPiece.style.width = pieceWidth + "px";
+ movingPiece.style.height = pieceWidth + "px";
+ }
const maxDist = this.getMaxDistance(r.width);
const pieces = this.pieces();
if (move.drag) {
@@ -2185,7 +2234,7 @@ export default class ChessRules {
// TODO: unclear why we need this new delay below:
setTimeout(() => {
movingPiece.style.transitionDuration = duration + "s";
- // movingPiece is child of container: no need to adjust cordinates
+ // movingPiece is child of container: no need to adjust coordinates
movingPiece.style.transform = `translate(${arr[0]}px, ${arr[1]}px)`;
setTimeout(cb, duration * 1000);
}, 50);