Skip to content

Commit

Permalink
Cleaned up a lot in checkdetection.js. Added a lot of JSDoc. Moved so…
Browse files Browse the repository at this point in the history
…me funcs to organizedlines.js
  • Loading branch information
Naviary2 committed Jul 19, 2024
1 parent e97d756 commit eb3448b
Show file tree
Hide file tree
Showing 9 changed files with 248 additions and 198 deletions.
192 changes: 113 additions & 79 deletions src/client/scripts/game/chess/checkdetection.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/client/scripts/game/chess/gamefile.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function gamefile(metadata, { moves = [], variantOptions, gameConclusion } = {})
box: undefined,
/** A set of all types of pieces that are in this game, without their color extension: `['pawns','queens']` */
existingTypes: undefined,
/** Possible sliding moves in this game, dependant on what pieces there are. */
/** Possible sliding moves in this game, dependant on what pieces there are: `[[1,1],[1,0]]` @type {number[][]}*/
slidingPossible: undefined
}

Expand Down
59 changes: 32 additions & 27 deletions src/client/scripts/game/chess/legalmoves.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ const legalmoves = (function(){
for (let i=0; i<lines.length; i++) {
const line = lines[i];
if (!thisPieceMoveset.sliding[line]) continue;
const key = math.getKeyFromLine(line,coords);
const key = organizedlines.getKeyFromLine(line,coords);
legalSliding[line] = slide_CalcLegalLimit(gamefile.piecesOrganizedByLines[line][key],line, thisPieceMoveset.sliding[line], coords, color);
};
};
Expand Down Expand Up @@ -161,27 +161,26 @@ const legalmoves = (function(){
/**
* Takes in specified organized list, direction of the slide, the current moveset...
* Shortens the moveset by pieces that block it's path.
* @param {*} organizedLine
* @param {*} line
* @param {*} slidinget
* @param {*} coords
* @param {*} color
* @returns
* @param {Piece[]} line - The list of pieces on this line
* @param {number[]} direction - The direction of the line: `[dx,dy]`
* @param {number[]} slidinget - How far this piece can slide in this direction: `[left,right]`. If the line is vertical, this is `[bottom,top]`
* @param {number[]} coords - The coordinates of the piece with the specified slidinget.
* @param {string} color - The color of friendlies
*/
function slide_CalcLegalLimit (organizedLine, line, slidinget, coords, color) {
function slide_CalcLegalLimit (line, direction, slidinget, coords, color) {

if (!slidinget) return; // Return undefined if there is no slide moveset

// The default slide is [-Infinity, Infinity], change that if there are any pieces blocking our path!

// For most we'll be comparing the x values, only exception is the columns.
const zeroOrOne = line[0] == 0 ? 1 : 0
const limit = [slidinget[0], slidinget[1]]
// For most we'll be comparing the x values, only exception is the vertical lines.
const axis = direction[0] == 0 ? 1 : 0
const limit = math.copyCoords(slidinget);
// Iterate through all pieces on same line
for (let i = 0; i < organizedLine.length; i++) {
for (let i = 0; i < line.length; i++) {
// What are the coords of this piece?
const thisPiece = organizedLine[i] // { type, coords }
const thisPieceSteps = Math.floor((thisPiece.coords[zeroOrOne]-coords[zeroOrOne])/line[zeroOrOne])
const thisPiece = line[i] // { type, coords }
const thisPieceSteps = Math.floor((thisPiece.coords[axis]-coords[axis])/direction[axis])
const thisPieceColor = math.getPieceColorFromType(thisPiece.type)
const isFriendlyPiece = color === thisPieceColor
const isVoid = thisPiece.type === 'voidsN';
Expand Down Expand Up @@ -235,11 +234,11 @@ const legalmoves = (function(){
}

for (var strline in legalMoves.sliding) {
let line = math.getCoordsFromKey(strline);
let limits = legalMoves.sliding[strline];
let line = math.getCoordsFromKey(strline); // 'dx,dy'
let limits = legalMoves.sliding[strline]; // [leftLimit,rightLimit]

let selectedPieceLine = math.getKeyFromLine(line,startCoords);
let clickedCoordsLine = math.getKeyFromLine(line,endCoords);
let selectedPieceLine = organizedlines.getKeyFromLine(line,startCoords);
let clickedCoordsLine = organizedlines.getKeyFromLine(line,endCoords);
if (!limits || selectedPieceLine !== clickedCoordsLine) continue;

if (!doesSlidingNetContainSquare(limits, line, startCoords, endCoords)) continue;
Expand Down Expand Up @@ -310,7 +309,7 @@ const legalmoves = (function(){
// Test if that piece's legal moves contain the destinationCoords.
const legalMoves = legalmoves.calculate(gamefile, piecemoved);
// This should pass on any special moves tags at the same time.
if (!legalmoves.checkIfMoveLegal(gamefile, legalMoves, moveCopy.startCoords, moveCopy.endCoords)) { // Illegal move
if (!legalmoves.checkIfMoveLegal(legalMoves, moveCopy.startCoords, moveCopy.endCoords)) { // Illegal move
console.log(`Opponent's move is illegal because the destination coords are illegal. Move: ${JSON.stringify(moveCopy)}`)
return rewindGameAndReturnReason(`Destination coordinates are illegal. inCheck: ${JSON.stringify(gamefile.inCheck)}. attackers: ${JSON.stringify(gamefile.attackers)}. originalMoveIndex: ${originalMoveIndex}. inCheckB4Forwarding: ${inCheckB4Forwarding}. attackersB4Forwarding: ${JSON.stringify(attackersB4Forwarding)}`);
}
Expand Down Expand Up @@ -348,17 +347,23 @@ const legalmoves = (function(){

// TODO: moveset changes
// This requires coords be on the same line as the sliding moveset.
function doesSlidingNetContainSquare(slidinget, line, pieceCoords, coords) {

const axis = line[0] === 0 ? 1 : 0
/**
* Tests if the piece's precalculated slidinget is able to reach the provided coords.
* ASSUMES the coords are on the direction of travel!!!
* @param {number[]} slidinget - The distance the piece can move along this line: `[left,right]`. If the line is vertical, this will be `[bottom,top]`.
* @param {number[]} direction - The direction of the line: `[dx,dy]`
* @param {number[]} pieceCoords - The coordinates of the piece with the provided sliding net
* @param {number[]} coords - The coordinates we want to know if they can reach.
* @returns {boolean} true if the piece is able to slide to the coordinates
*/
function doesSlidingNetContainSquare(slidinget, direction, pieceCoords, coords) {
const axis = direction[0] === 0 ? 1 : 0
const coordMag = coords[axis];
const min = slidinget[0] * line[axis] + pieceCoords[axis]
const max = slidinget[1] * line[axis] + pieceCoords[axis]

if (coordMag < min) return false;
if (coordMag > max) return false;
const min = slidinget[0] * direction[axis] + pieceCoords[axis]
const max = slidinget[1] * direction[axis] + pieceCoords[axis]

return true;
return coordMag >= min && coordMag <= max;
}

/**
Expand Down
49 changes: 47 additions & 2 deletions src/client/scripts/game/chess/organizedlines.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const organizedlines = {
let lines = gamefile.startSnapshot.slidingPossible
for (let i = 0; i<lines.length; i++) {
const line = lines[i]
key = math.getKeyFromLine(line,coords)
key = organizedlines.getKeyFromLine(line,coords)
const strline = math.getKeyFromCoords(line)
// Is line initialized
if (!gamefile.piecesOrganizedByLines[strline][key]) gamefile.piecesOrganizedByLines[strline][key] = []
Expand All @@ -88,7 +88,7 @@ const organizedlines = {
let lines = gamefile.startSnapshot.slidingPossible
for (let i = 0; i<lines.length; i++) {
const line = lines[i]
key = math.getKeyFromLine(line,coords)
key = organizedlines.getKeyFromLine(line,coords)
removePieceFromLine(gamefile.piecesOrganizedByLines[line],key)
}

Expand Down Expand Up @@ -256,5 +256,50 @@ const organizedlines = {
}

return state;
},

/**
* Gets a unique key from the line equation.
* Compatable with factorable steps like `[2,2]`.
* Discuss before changing func please as this may have unintended side-effects.
* @param {Number[]} step Line step `[deltax,deltay]`
* @param {Number[]} coords `[x,y]`
* @returns {String} the key `c|smallest_x_line_intcepts`
*/
getKeyFromLine(step, coords) {
const C = organizedlines.getCFromLine(step, coords);
const X = organizedlines.getXFromLine(step, coords);
return `${C}|${X}`
},

/**
* Uses the calculation of ax + by = c
* c=b*y-intercept so is unique for each line
* Not unique when step can be factored
* eg [2,2]
* @param {number[]} step - The x-step and y-step of the line: `[deltax, deltay]`
* @param {number[]} coords - A point the line intersects: `[x,y]`
* @returns {number} integer c
*/
getCFromLine(step, coords) {
return step[0]*coords[1]-step[1]*coords[0]
},

/**
* Calculates the X value of the line's key from the provided step direction and coordinates.
* This is also the nearest x value the line intersects on or after the y axis.
* @param {number[]} step - [dx,dy]
* @param {number[]} coords - Coordinates that are on the line
* @returns {number} The X in the line's key: `C|X`
*/
getXFromLine(step, coords) {
// See these desmos graphs for inspiration for finding what line the coords are on:
// https://www.desmos.com/calculator/d0uf1sqipn
// https://www.desmos.com/calculator/t9wkt3kbfo

const lineIsVertical = step[0] === 0;
const deltaAxis = lineIsVertical ? step[1] : step[0];
const coordAxis = lineIsVertical ? coords[1] : coords[0];
return math.posMod(coordAxis, deltaAxis)
}
};
2 changes: 1 addition & 1 deletion src/client/scripts/game/chess/specialdetect.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const specialdetect = (function() {

const x = coords[0];
const y = coords[1];
const key = math.getKeyFromLine([1,0],coords)
const key = organizedlines.getKeyFromLine([1,0],coords)
const row = gamefile.piecesOrganizedByLines['1,0'][key];


Expand Down
132 changes: 49 additions & 83 deletions src/client/scripts/game/misc/math.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,31 @@

const math = (function() {

/**
* Tests if the provided value is a power of 2.
* It does this efficiently by using bitwise operations.
* @param {number} value
* @returns {boolean} true if the value is a power of 2.
*/
function isPowerOfTwo(value) {
return (value & (value - 1)) === 0;
}

function isAproxEqual(a, b, epsilon) {
if (epsilon == null) {
epsilon = 0.001;
}
return Math.abs(a-b)<epsilon
/**
* Returns true if the given values are approximately equal, with the most amount
* of difference allowed being the provided epsilon value.
* @param {number} a - Value 1
* @param {number} b - Value 2
* @param {number} [epsilon] The custom epsilon value. Default: Number.EPSILON (~2.2 x 10^-16). Idon us's old epsilon default value: 0.001
* @returns {boolean} true if the values are approximately equal, within the threshold.
*/
function isAproxEqual(a, b, epsilon = Number.EPSILON) { // Idon us's old epsilon default value: 0.001
return Math.abs(a - b) < epsilon;
}

/**
* Finds the intersection point of two lines given in the form dx * x + dy * y = c.
* This will return `null` if there isn't one, or if there's infinite (colinear).
* @param {number} dx1 - The coefficient of x for the first line.
* @param {number} dy1 - The coefficient of y for the first line.
* @param {number} c1 - The constant term for the first line.
Expand Down Expand Up @@ -182,63 +194,14 @@ const math = (function() {
return { left, right, bottom, top }
}

function posMod(a,b) {
return a - (Math.floor(a / b) * b)
}

/**
* Uses the calculation of ax + by = c
* c=b*y-intercept so is unique for each line
* Not unique when step can be factored
* eg [2,2]
* @param {number[]} step - The x-step and y-step of the line: `[deltax, deltay]`
* @param {number[]} coords - A point the line intersects: `[x,y]`
* @returns {number} integer c
* Computes the positive modulus of two numbers.
* @param {number} a - The dividend.
* @param {number} b - The divisor.
* @returns {number} The positive remainder of the division.
*/
function getCFromLineInGeneralForm(step, coords) {
return step[0]*coords[1]-step[1]*coords[0]
}

/**
* Returns the y interscept of the line with step dx and dy that intersects the coordinates.
* If the line is vertical, this returns the x intercept instead.
* @param {*} step
* @param {*} coords
* @returns {number}
*/
function getYIntceptOfLine(step, coords) {
const lineIsVertical = step[0] === 0;
const xLine = lineIsVertical ? posMod(coords[1], step[1]) : posMod(coords[0], step[0]);
const slope = lineIsVertical ? 0 /**step[0] / step[1]*/ : step[1] / step[0];
// console.log(step, coords, lineIsVertical ? coords[0] + slope * (xLine - coords[1]) : coords[1] + slope * (xLine - coords[0]))
return lineIsVertical ? coords[0] + slope * (xLine - coords[1]) : coords[1] + slope * (xLine - coords[0]);
}

/**
* Gets a unique key from the line equation.
* Compatable with factorable steps like `[2,2]`.
* Discuss before changing func please as this may have unintended side-effects.
* @param {Number[]} step Line step `[deltax,deltay]`
* @param {Number[]} coords `[x,y]`
* @returns {String} the key `c|smallest_x_line_intcepts`
*/
function getKeyFromLine(step, coords) {
// See these desmos graphs for inspiration for finding what line the coords are on:
// https://www.desmos.com/calculator/d0uf1sqipn
// https://www.desmos.com/calculator/t9wkt3kbfo

// Idon us's old equation
const lineIsVertical = step[0] === 0;
const deltaAxis = lineIsVertical ? step[1] : step[0];
const coordAxis = lineIsVertical ? coords[1] : coords[0];
const xLine = posMod(coordAxis, deltaAxis)
return `${getCFromLineInGeneralForm(step,coords)}|${xLine}`

// Naviary's new equation
//const lineIsVertical = step[0] === 0;
//const xLine = lineIsVertical ? coords[1] % step[1] : coords[0] % step[0];
//const yIntcept = getYIntceptOfLine(step, coords);
//return `${yIntcept}|${xLine}`;
function posMod(a, b) {
return a - (Math.floor(a / b) * b);
}

/**
Expand All @@ -265,10 +228,9 @@ const math = (function() {

/**
* Checks if all lines are colinear aka `[[1,0],[2,0]]` would be as they are both the same direction
* @param {Number[][]} lines Array of vectors `[[1,0],[2,0]]`
* @returns {Boolean}
* @param {number[][]} lines Array of vectors `[[1,0],[2,0]]`
* @returns {boolean}
*/

function areLinesCollinear(lines) {
let gradient
for (const line of lines) {
Expand Down Expand Up @@ -412,43 +374,50 @@ const math = (function() {
return [worldX, worldY]
}

// fast clampfunc
/**
* Clamps a value between a minimum and a maximum value.
* @param {number} min - The minimum value.
* @param {number} max - The maximum value.
* @param {number} value - The value to clamp.
* @returns {number} The clamped value.
*/
function clamp(min,max,value) {
if (min>value) return min;
if (max<value) return max;
return value
return value;
}

// Returns the point on the line segment that is nearest/perpendicular to the given point.
function closestPointOnLine (lineStart, lineEnd, point) {

/**
* Returns the point on the line segment that is nearest/perpendicular to the given point.
* @param {number[]} lineStart - The starting point of the line segment as [x, y].
* @param {number[]} lineEnd - The ending point of the line segment as [x, y].
* @param {number[]} point - The point to find the nearest point on the line to as [x, y].
* @returns {Object} An object containing the proeprties `coords`, which is the closest point on our segment to our point, and the `distance` to it.
*/
function closestPointOnLine(lineStart, lineEnd, point) {
let closestPoint;
let distance;

const dx = lineEnd[0] - lineStart[0];
const dy = lineEnd[1] - lineStart[1];

if (dx === 0) {
closestPoint = [lineStart[0], clamp(lineStart[1], lineEnd[1], point[1])]
} else {

if (dx === 0) { // Vertical line
closestPoint = [lineStart[0], clamp(lineStart[1], lineEnd[1], point[1])];
} else { // Not vertical
const m = dy / dx;
const b = lineStart[1] - m * lineStart[0];

// Calculate x and y coordinates of closest point on line
let x = (m * (point[1] - b) + point[0]) / (m * m + 1);
x = clamp(lineStart[0],lineEnd[0],x)
x = clamp(lineStart[0], lineEnd[0], x);
const y = m * x + b;

closestPoint = [x,y];
closestPoint = [x, y];
}

distance = euclideanDistance(closestPoint, point)

return {
coords: closestPoint,
distance: distance
}
distance: euclideanDistance(closestPoint, point)
};
}

function getLineSteps(step, origin, coord, isLeft) {
Expand Down Expand Up @@ -1039,9 +1008,6 @@ const math = (function() {
boxContainsBox,
boxContainsSquare,
posMod,
getCFromLineInGeneralForm,
getYIntceptOfLine,
getKeyFromLine,
areCoordsIntegers,
areLinesCollinear,
deepCopyObject,
Expand Down
Loading

0 comments on commit eb3448b

Please sign in to comment.