Skip to content

Commit

Permalink
Merge pull request lichess-org#16512 from allanjoseph98/random-refact…
Browse files Browse the repository at this point in the history
…oring

Miscellaneous refactoring
  • Loading branch information
ornicar authored Dec 1, 2024
2 parents c233327 + 7ac27a4 commit 27e61c6
Show file tree
Hide file tree
Showing 20 changed files with 102 additions and 231 deletions.
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions ui/analyse/src/ctrl.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { readDests, readDrops } from 'chess';
import { fenToEpd, readDests, readDrops } from 'chess';
import { playable, playedTurns } from 'game';
import * as keyboard from './keyboard';
import { treeReconstruct, plyColor } from './util';
Expand Down Expand Up @@ -930,10 +930,10 @@ export default class AnalyseCtrl {
const fens = new Set();
for (let i = this.nodeList.length - 1; i >= 0; i--) {
const node = this.nodeList[i];
const fen = node.fen.split(' ').slice(0, 4).join(' ');
if (fens.has(fen)) return false;
const epd = fenToEpd(node.fen);
if (fens.has(epd)) return false;
if (node.san && sanIrreversible(this.data.game.variant.key, node.san)) return true;
fens.add(fen);
fens.add(epd);
}
return true;
};
Expand Down
69 changes: 23 additions & 46 deletions ui/analyse/src/nodeFinder.ts
Original file line number Diff line number Diff line change
@@ -1,72 +1,49 @@
import { winningChances } from 'ceval';
import { fenToEpd } from 'chess';
import { defined } from 'common';
import { zip } from 'common/algo';

const hasCompChild = (node: Tree.Node): boolean => !!node.children.find(c => !!c.comp);

export function nextGlyphSymbol(
export const nextGlyphSymbol = (
color: Color,
symbol: string,
mainline: Tree.Node[],
fromPly: number,
): Tree.Node | undefined {
const len = mainline.length;
if (!len) return;
const fromIndex = fromPly - mainline[0].ply;
for (let i = 1; i < len; i++) {
const node = mainline[(fromIndex + i) % len];
const found =
node.ply % 2 === (color === 'white' ? 1 : 0) &&
node.glyphs &&
node.glyphs.find(function (g) {
return g.symbol === symbol;
});
if (found) return node;
}
return;
}

export function evalSwings(mainline: Tree.Node[], nodeFilter: (node: Tree.Node) => boolean): Tree.Node[] {
const found: Tree.Node[] = [];
const threshold = 0.1;
for (let i = 1; i < mainline.length; i++) {
const node = mainline[i];
const prev = mainline[i - 1];
if (nodeFilter(node) && node.eval && prev.eval) {
const diff = Math.abs(winningChances.povDiff('white', prev.eval, node.eval));
if (
hasCompChild(prev) &&
(diff > threshold || (prev.eval.mate && !node.eval.mate && Math.abs(prev.eval.mate) <= 3))
) {
found.push(node);
}
}
}
return found;
}
): Tree.Node | undefined =>
mainline
.map((_, i) => mainline[(fromPly - mainline[0].ply + i + 1) % mainline.length])
.find(n => n.ply % 2 === (color === 'white' ? 1 : 0) && n.glyphs?.some(g => g.symbol === symbol));

// Extended Position Description
const epd = (fen: FEN) => fen.split(' ').slice(0, 4).join(' ');
export const evalSwings = (mainline: Tree.Node[], nodeFilter: (node: Tree.Node) => boolean): Tree.Node[] =>
mainline.slice(1).filter((curr, i) => {
const prev = mainline[i];
return (
nodeFilter(curr) &&
curr.eval &&
prev.eval &&
hasCompChild(prev) &&
(Math.abs(winningChances.povDiff('white', prev.eval, curr.eval)) > 0.1 ||
(prev.eval.mate && !curr.eval.mate && Math.abs(prev.eval.mate) <= 3))
);
});

export function detectThreefold(nodeList: Tree.Node[], node: Tree.Node): void {
if (defined(node.threefold)) return;
const currentEpd = epd(node.fen);
let nbSimilarPositions = 0,
i;
for (i in nodeList) if (epd(nodeList[i].fen) === currentEpd) nbSimilarPositions++;
node.threefold = nbSimilarPositions > 2;
const currentEpd = fenToEpd(node.fen);
node.threefold = nodeList.filter(n => fenToEpd(n.fen) === currentEpd).length > 2;
}

// can be 3fold or 5fold
export function add3or5FoldGlyphs(mainlineNodes: Tree.Node[]): void {
const threefoldMap = new Map<string, Tree.Node[]>();
for (const node of mainlineNodes) {
const previousOccurences = threefoldMap.get(epd(node.fen)) || [];
const previousOccurences = threefoldMap.get(fenToEpd(node.fen)) || [];
previousOccurences.push(node);
threefoldMap.set(epd(node.fen), previousOccurences);
threefoldMap.set(fenToEpd(node.fen), previousOccurences);
}
// only the last positition can be source of three/five-fold
const lastEpd = epd(mainlineNodes[mainlineNodes.length - 1].fen);
const lastEpd = fenToEpd(mainlineNodes[mainlineNodes.length - 1].fen);
const repetitions = threefoldMap.get(lastEpd);
if (repetitions && repetitions.length > 2) {
const unicodeList = ['①', '②', '③', '④', '⑤'];
Expand Down
5 changes: 3 additions & 2 deletions ui/analyse/src/pgnExport.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type AnalyseCtrl from './ctrl';
import { h } from 'snabbdom';
import { initialFen, fixCrazySan, plyToTurn } from 'chess';
import { fixCrazySan, plyToTurn } from 'chess';
import { type MaybeVNodes } from 'common/snabbdom';
import { INITIAL_FEN } from 'chessops/fen';

interface PgnNode {
ply: Ply;
Expand Down Expand Up @@ -38,7 +39,7 @@ export function renderFullTxt(ctrl: AnalyseCtrl): string {
let txt = renderNodesTxt(ctrl.tree.root, true);
const tags: Array<[string, string]> = [];
if (g.variant.key !== 'standard') tags.push(['Variant', g.variant.name]);
if (g.initialFen && g.initialFen !== initialFen) tags.push(['FEN', g.initialFen]);
if (g.initialFen && g.initialFen !== INITIAL_FEN) tags.push(['FEN', g.initialFen]);
if (tags.length) txt = tags.map(t => '[' + t[0] + ' "' + t[1] + '"]').join('\n') + '\n\n' + txt;
return txt;
}
Expand Down
10 changes: 2 additions & 8 deletions ui/analyse/src/plugins/analyse.nvui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ import {
pieceJumpingHandler,
castlingFlavours,
inputToLegalUci,
namePiece,
lastCapturedCommandHandler,
} from 'nvui/chess';
import { renderSetting } from 'nvui/setting';
Expand All @@ -39,7 +38,7 @@ import { view as cevalView, renderEval } from 'ceval';
import { next, prev } from '../control';
import { lichessRules } from 'chessops/compat';
import { makeSan } from 'chessops/san';
import { opposite, parseUci } from 'chessops/util';
import { charToRole, opposite, parseUci } from 'chessops/util';
import { parseFen } from 'chessops/fen';
import { setupPosition } from 'chessops/variant';
import { plyToTurn } from 'chess';
Expand Down Expand Up @@ -370,12 +369,7 @@ function onSubmit(ctrl: AnalyseController, notify: (txt: string) => void, style:
else {
const uci = inputToLegalUci(input, ctrl.node.fen, ctrl.chessground);
if (uci)
ctrl.sendMove(
uci.slice(0, 2) as Key,
uci.slice(2, 4) as Key,
undefined,
namePiece[uci.slice(4)] as Role | undefined,
);
ctrl.sendMove(uci.slice(0, 2) as Key, uci.slice(2, 4) as Key, undefined, charToRole(uci.slice(4)));
else notify('Invalid command');
}
$input.val('');
Expand Down
4 changes: 2 additions & 2 deletions ui/analyse/src/study/studyChapters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ import type {
import type StudyCtrl from './studyCtrl';
import { opposite } from 'chessops/util';
import { fenColor } from 'common/miniBoard';
import { initialFen } from 'chess';
import type Sortable from 'sortablejs';
import { pubsub } from 'common/pubsub';
import { alert } from 'common/dialog';
import { INITIAL_FEN } from 'chessops/fen';

/* read-only interface for external use */
export class StudyChapters {
Expand Down Expand Up @@ -76,7 +76,7 @@ export default class StudyChaptersCtrl {
this.store(
chapters.map(c => ({
...c,
fen: c.fen || initialFen,
fen: c.fen || INITIAL_FEN,
players: c.players ? this.convertPlayersFromServer(c.players) : undefined,
orientation: c.orientation || 'white',
variant: c.variant || 'standard',
Expand Down
53 changes: 21 additions & 32 deletions ui/chess/src/chess.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,30 @@
import { uciChar } from './uciChar';

export * from './sanWriter';
export const initialFen: FEN = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1';

export function fixCrazySan(san: San): San {
return san[0] === 'P' ? san.slice(1) : san;
}
export const fixCrazySan = (san: San): San => (san[0] === 'P' ? san.slice(1) : san);

export function destsToUcis(dests: Dests): Uci[] {
const ucis: string[] = [];
for (const [orig, d] of dests) {
d.forEach(function (dest) {
ucis.push(orig + dest);
});
}
return ucis;
}
export const destsToUcis = (destMap: Dests): Uci[] =>
Array.from(destMap).reduce<Uci[]>((acc, [orig, dests]) => acc.concat(dests.map(dest => orig + dest)), []);

export function readDests(lines?: string): Dests | null {
if (typeof lines === 'undefined') return null;
const dests = new Map();
if (lines)
for (const line of lines.split(' ')) {
dests.set(
uciChar[line[0]],
line
.slice(1)
.split('')
.map(c => uciChar[c]),
);
}
return dests;
}
export const readDests = (lines?: string): Dests | null =>
lines
? lines.split(' ').reduce<Dests>((dests, line) => {
dests.set(
uciChar[line[0]],
line
.slice(1)
.split('')
.map(c => uciChar[c]),
);
return dests;
}, new Map())
: null;

export function readDrops(line?: string | null): Key[] | null {
if (typeof line === 'undefined' || line === null) return null;
return (line.match(/.{2}/g) as Key[]) || [];
}
export const readDrops = (line?: string | null): Key[] | null =>
line ? (line.match(/.{2}/g) as Key[]) || [] : null;

// Extended Position Description
export const fenToEpd = (fen: FEN): string => fen.split(' ').slice(0, 4).join(' ');

export const plyToTurn = (ply: number): number => Math.floor((ply - 1) / 2) + 1;
13 changes: 2 additions & 11 deletions ui/chess/src/sanWriter.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Square } from 'chessops';
import { charToRole, type Square } from 'chessops';

export type Board = { pieces: { [key: number]: string }; turn: boolean };
export type SanToUci = { [key: string]: Uci };
Expand Down Expand Up @@ -177,16 +177,7 @@ export function speakable(san?: San): string {
const code = c.charCodeAt(0);
if (code > 48 && code < 58) return c; // 1-8
if (code > 96 && code < 105) return c.toUpperCase();
return (
{
P: 'pawn',
N: 'knight',
B: 'bishop',
R: 'rook',
Q: 'queen',
K: 'king',
}[c.toUpperCase()] ?? c
);
return charToRole(c) ?? c;
})
.join(' ')
.replace(/^A /, '"A"') // "A takes" & "A 3" are mispronounced
Expand Down
10 changes: 0 additions & 10 deletions ui/common/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,3 @@ export function $as<T>(cashOrHtml: Cash | string): T {
export function myUserId(): string | undefined {
return document.body.dataset.user;
}

export const charToRole = (char: string) =>
({
P: 'pawn',
N: 'knight',
B: 'bishop',
R: 'rook',
Q: 'queen',
K: 'king',
})[char.toUpperCase()] as Role;
3 changes: 2 additions & 1 deletion ui/editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"license": "AGPL-3.0-or-later",
"types": "dist/editor.d.ts",
"dependencies": {
"common": "workspace:*"
"common": "workspace:*",
"chess": "workspace:*"
},
"lichess": {
"bundles": "src/editor.ts"
Expand Down
5 changes: 3 additions & 2 deletions ui/editor/src/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import chessground from './chessground';
import type { Selected, CastlingToggle, EditorState, EndgamePosition, OpeningPosition } from './interfaces';
import { dataIcon } from 'common/snabbdom';
import { domDialog } from 'common/dialog';
import { fenToEpd } from 'chess';

function castleCheckBox(ctrl: EditorCtrl, id: CastlingToggle, label: string, reversed: boolean): VNode {
const input = h('input', {
Expand Down Expand Up @@ -159,7 +160,7 @@ function controls(ctrl: EditorCtrl, state: EditorState): VNode {
{ attrs: { value: pos.epd || pos.fen, 'data-fen': pos.fen } },
pos.eco ? `${pos.eco} ${pos.name}` : pos.name,
);
const epd = state.fen.split(' ').slice(0, 4).join(' ');
const epd = fenToEpd(state.fen);
const value =
(
ctrl.cfg.positions.find(p => p.fen.startsWith(epd)) ||
Expand All @@ -171,7 +172,7 @@ function controls(ctrl: EditorCtrl, state: EditorState): VNode {
props: { value },
on: {
insert(vnode) {
(vnode.elm as HTMLSelectElement).value = state.fen.split(' ').slice(0, 4).join(' ');
(vnode.elm as HTMLSelectElement).value = fenToEpd(state.fen);
},
change(e) {
const el = e.target as HTMLSelectElement;
Expand Down
2 changes: 1 addition & 1 deletion ui/editor/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": { "emitDeclarationOnly": true },
"references": [{ "path": "../common/tsconfig.json" }]
"references": [{ "path": "../common/tsconfig.json" }, { "path": "../chess/tsconfig.json" }]
}
12 changes: 2 additions & 10 deletions ui/game/src/material.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import { opposite } from 'chessground/util';
import type { CheckCount, CheckState, MaterialDiff } from './interfaces';

const ROLES: { [key: string]: Role | undefined } = {
p: 'pawn',
n: 'knight',
b: 'bishop',
r: 'rook',
q: 'queen',
k: 'king',
};
import { charToRole } from 'chessops';

export function getMaterialDiff(fenLike: string): MaterialDiff {
const diff: MaterialDiff = {
Expand All @@ -18,7 +10,7 @@ export function getMaterialDiff(fenLike: string): MaterialDiff {
for (let i = 0, part = 0; i < fenLike.length && part < 8; i++) {
const ch = fenLike[i];
const lower = ch.toLowerCase();
const role = ROLES[lower];
const role = charToRole(ch);
if (role) {
const color = ch === lower ? 'black' : 'white';
const them = diff[opposite(color)];
Expand Down
Loading

0 comments on commit 27e61c6

Please sign in to comment.