diff --git a/ui/bits/src/bits.diagnosticDialog.ts b/ui/bits/src/bits.diagnosticDialog.ts index 81733a49abf21..98f00fa6bc2ef 100644 --- a/ui/bits/src/bits.diagnosticDialog.ts +++ b/ui/bits/src/bits.diagnosticDialog.ts @@ -64,10 +64,6 @@ const storageProxy: { [key: string]: { storageKey: string; validate: (val?: stri storageKey: 'socket.host', validate: (val?: string) => val?.endsWith('.lichess.org') ?? false, }, - allowLsfw: { - storageKey: 'ceval.lsfw.forceEnable', - validate: (val?: string) => val === 'true' || val === 'false', - }, logWindow: { storageKey: 'log.window', validate: (val?: string) => parseInt(val ?? '') >= 0, diff --git a/ui/ceval/src/engines/engines.ts b/ui/ceval/src/engines/engines.ts index 9a118ea408620..105e657eb30e7 100644 --- a/ui/ceval/src/engines/engines.ts +++ b/ui/ceval/src/engines/engines.ts @@ -5,7 +5,7 @@ import { StockfishWebEngine } from './stockfishWebEngine'; import { ThreadedEngine } from './threadedEngine'; import { ExternalEngine } from './externalEngine'; import { storedStringProp, StoredProp } from 'common/storage'; -import { isAndroid, isIos, isIPad, features as browserSupport, type Feature } from 'common/device'; +import { isAndroid, isIos, isIPad, features as browserSupport } from 'common/device'; import { xhrHeader } from 'common/xhr'; import { lichessRules } from 'chessops/compat'; import { log } from 'common/permalog'; @@ -63,157 +63,158 @@ export class Engines { ['threeCheck', '313cc226a173'], ['racingKings', '636b95f085e3'], ]; - return new Map( - [ - { - info: { - id: '__sf16nnue7', - name: 'Stockfish 16 NNUE · 7MB', - short: 'SF 16 · 7MB', - tech: 'NNUE', - requires: ['sharedMem', 'simd', 'dynamicImportFromWorker'], - minMem: 1536, - assets: { - version: 'sfw006', - root: 'npm/lila-stockfish-web', - js: 'sf16-7.js', - }, + const browserEngines: WithMake[] = [ + { + info: { + id: '__sf16nnue7', + name: 'Stockfish 16 NNUE · 7MB', + short: 'SF 16 · 7MB', + tech: 'NNUE', + requires: ['sharedMem', 'simd', 'dynamicImportFromWorker'], + minMem: 1536, + assets: { + version: 'sfw006', + root: 'npm/lila-stockfish-web', + js: 'sf16-7.js', }, - make: (e: BrowserEngineInfo) => new StockfishWebEngine(e, this.status), }, - { - info: { - id: '__sf17nnue79', - name: 'Stockfish 17 NNUE · 79MB', - short: 'SF 17 · 79MB', - tech: 'NNUE', - requires: ['sharedMem', 'simd', 'dynamicImportFromWorker'], - minMem: 2560, - assets: { - version: 'sfw006', - root: 'npm/lila-stockfish-web', - js: 'sf17-79.js', - }, + make: (e: BrowserEngineInfo) => new StockfishWebEngine(e, this.status), + }, + { + info: { + id: '__sf17nnue79', + name: 'Stockfish 17 NNUE · 79MB', + short: 'SF 17 · 79MB', + tech: 'NNUE', + requires: ['sharedMem', 'simd', 'dynamicImportFromWorker'], + minMem: 2560, + assets: { + version: 'sfw006', + root: 'npm/lila-stockfish-web', + js: 'sf17-79.js', }, - make: (e: BrowserEngineInfo) => new StockfishWebEngine(e, this.status), }, - { - info: { - id: '__sf14nnue', - name: 'Stockfish 14 NNUE', - short: 'SF 14', - tech: 'NNUE', - obsoletedBy: 'allowLsfw', - requires: ['sharedMem', 'simd'], - minMem: 2048, - assets: { - version: 'b6939d', - root: 'npm/stockfish-nnue.wasm', - js: 'stockfish.js', - wasm: 'stockfish.wasm', - }, + make: (e: BrowserEngineInfo) => new StockfishWebEngine(e, this.status), + }, + { + info: { + id: '__sf14nnue', + name: 'Stockfish 14 NNUE', + short: 'SF 14', + tech: 'NNUE', + obsoletedBy: 'dynamicImportFromWorker', + requires: ['sharedMem', 'simd'], + minMem: 2048, + assets: { + version: 'b6939d', + root: 'npm/stockfish-nnue.wasm', + js: 'stockfish.js', + wasm: 'stockfish.wasm', }, - make: (e: BrowserEngineInfo) => new ThreadedEngine(e, this.status), }, - ...variants.map(makeVariant), - { - info: { - id: '__fsfhce', - name: 'Fairy Stockfish 14+ HCE', - short: 'FSF 14+', - tech: 'HCE', - requires: ['sharedMem', 'simd', 'dynamicImportFromWorker'], - variants: variants.map(v => v[0]), - assets: { - version: 'sfw006', - root: 'npm/lila-stockfish-web', - js: 'fsf14.js', - }, + make: (e: BrowserEngineInfo) => new ThreadedEngine(e, this.status), + }, + ...variants.map(makeVariant), + { + info: { + id: '__fsfhce', + name: 'Fairy Stockfish 14+ HCE', + short: 'FSF 14+', + tech: 'HCE', + requires: ['sharedMem', 'simd', 'dynamicImportFromWorker'], + variants: variants.map(v => v[0]), + assets: { + version: 'sfw006', + root: 'npm/lila-stockfish-web', + js: 'fsf14.js', }, - make: (e: BrowserEngineInfo) => - new StockfishWebEngine(e, this.status, v => (v === 'threeCheck' ? '3check' : v.toLowerCase())), }, - { - info: { - id: '__sf11mv', - name: 'Stockfish 11 Multi-Variant', - short: 'SF 11 MV', - tech: 'HCE', - requires: ['sharedMem'], - minThreads: 1, - variants: variants.map(v => v[0]), - assets: { - version: 'a022fa', - root: 'npm/stockfish-mv.wasm', - js: 'stockfish.js', - wasm: 'stockfish.wasm', - }, + make: (e: BrowserEngineInfo) => + new StockfishWebEngine(e, this.status, v => (v === 'threeCheck' ? '3check' : v.toLowerCase())), + }, + { + info: { + id: '__sf11mv', + name: 'Stockfish 11 Multi-Variant', + short: 'SF 11 MV', + tech: 'HCE', + requires: ['sharedMem'], + minThreads: 1, + variants: variants.map(v => v[0]), + assets: { + version: 'a022fa', + root: 'npm/stockfish-mv.wasm', + js: 'stockfish.js', + wasm: 'stockfish.wasm', }, - make: (e: BrowserEngineInfo) => - new ThreadedEngine(e, undefined, (v: VariantKey) => - v === 'antichess' ? 'giveaway' : lichessRules(v), - ), }, - { - info: { - id: '__sf11hce', - name: 'Stockfish 11 HCE', - short: 'SF 11', - tech: 'HCE', - requires: ['sharedMem'], - minThreads: 1, - assets: { - version: 'a022fa', - root: 'npm/stockfish.wasm', - js: 'stockfish.js', - wasm: 'stockfish.wasm', - }, + make: (e: BrowserEngineInfo) => + new ThreadedEngine(e, undefined, (v: VariantKey) => + v === 'antichess' ? 'giveaway' : lichessRules(v), + ), + }, + { + info: { + id: '__sf11hce', + name: 'Stockfish 11 HCE', + short: 'SF 11', + tech: 'HCE', + requires: ['sharedMem'], + minThreads: 1, + assets: { + version: 'a022fa', + root: 'npm/stockfish.wasm', + js: 'stockfish.js', + wasm: 'stockfish.wasm', }, - make: (e: BrowserEngineInfo) => new ThreadedEngine(e), }, - { - info: { - id: '__sfwasm', - name: 'Stockfish WASM', - short: 'Stockfish', - tech: 'HCE', - minThreads: 1, - maxThreads: 1, - requires: ['wasm'], - obsoletedBy: 'sharedMem', - assets: { - version: 'a022fa', - root: 'npm/stockfish.js', - js: 'stockfish.wasm.js', - }, + make: (e: BrowserEngineInfo) => new ThreadedEngine(e), + }, + { + info: { + id: '__sfwasm', + name: 'Stockfish WASM', + short: 'Stockfish', + tech: 'HCE', + minThreads: 1, + maxThreads: 1, + requires: ['wasm'], + obsoletedBy: 'sharedMem', + assets: { + version: 'a022fa', + root: 'npm/stockfish.js', + js: 'stockfish.wasm.js', }, - make: (e: BrowserEngineInfo) => new SimpleEngine(e), }, - { - info: { - id: '__sfjs', - name: 'Stockfish JS', - short: 'Stockfish', - tech: 'HCE', - minThreads: 1, - maxThreads: 1, - requires: [], - obsoletedBy: 'wasm', - assets: { - version: 'a022fa', - root: 'npm/stockfish.js', - js: 'stockfish.js', - }, + make: (e: BrowserEngineInfo) => new SimpleEngine(e), + }, + { + info: { + id: '__sfjs', + name: 'Stockfish JS', + short: 'Stockfish', + tech: 'HCE', + minThreads: 1, + maxThreads: 1, + requires: [], + obsoletedBy: 'wasm', + assets: { + version: 'a022fa', + root: 'npm/stockfish.js', + js: 'stockfish.js', }, - make: (e: BrowserEngineInfo) => new SimpleEngine(e), }, - ] + make: (e: BrowserEngineInfo) => new SimpleEngine(e), + }, + ]; + return new Map( + browserEngines .filter( e => - e.info.requires.every((req: Feature) => browserSupport().includes(req)) && - !(e.info.obsoletedBy && browserSupport().includes(e.info.obsoletedBy as Feature)), + e.info.requires.every(req => browserSupport().includes(req)) && + !(e.info.obsoletedBy && browserSupport().includes(e.info.obsoletedBy)), ) - .map(e => [e.info.id, { info: withDefaults(e.info as BrowserEngineInfo), make: e.make }]), + .map(e => [e.info.id, { info: withDefaults(e.info), make: e.make }]), ); } @@ -232,7 +233,7 @@ export class Engines { } get external(): ExternalEngineInfo | undefined { - return this.active && 'endpoint' in this.active ? this.active : undefined; + return this.active && this.isExternalEngineInfo(this.active) ? this.active : undefined; } get maxMovetime(): number { @@ -274,9 +275,13 @@ export class Engines { const e = (this.activeEngine = this.getEngine(selector)); if (!e) throw Error(`Engine not found ${selector?.id ?? selector?.variant ?? this.selectProp()}}`); - return e.tech !== 'EXTERNAL' - ? this.localEngineMap.get(e.id)!.make(e as BrowserEngineInfo) - : new ExternalEngine(e as ExternalEngineInfo, this.status); + return !this.isExternalEngineInfo(e) + ? this.localEngineMap.get(e.id)!.make(e) + : new ExternalEngine(e, this.status); + } + + isExternalEngineInfo(e: EngineInfo): e is ExternalEngineInfo { + return e.tech === 'EXTERNAL'; } } diff --git a/ui/ceval/src/types.ts b/ui/ceval/src/types.ts index 526a19ab2c9c4..2671354a7b182 100644 --- a/ui/ceval/src/types.ts +++ b/ui/ceval/src/types.ts @@ -28,7 +28,6 @@ export interface Work { export interface BaseEngineInfo { id: string; name: string; - tech?: 'HCE' | 'NNUE' | 'EXTERNAL'; short?: string; variants?: VariantKey[]; minThreads?: number; @@ -37,13 +36,23 @@ export interface BaseEngineInfo { requires?: Feature[]; } -export interface ExternalEngineInfo extends BaseEngineInfo { +export interface ExternalEngineInfoFromServer extends BaseEngineInfo { + variants: VariantKey[]; + maxHash: number; + maxThreads: number; + providerData?: string; clientSecret: string; officialStockfish?: boolean; endpoint: string; } +export interface ExternalEngineInfo extends ExternalEngineInfoFromServer { + tech: 'EXTERNAL'; +} + export interface BrowserEngineInfo extends BaseEngineInfo { + tech: 'HCE' | 'NNUE'; + short: string; minMem?: number; assets: { root?: string; js?: string; wasm?: string; version?: string; nnue?: string[] }; requires: Feature[]; @@ -90,7 +99,7 @@ export interface CevalOpts { redraw: Redraw; search?: Search; onSelectEngine?: () => void; - externalEngines?: ExternalEngineInfo[]; + externalEngines?: ExternalEngineInfoFromServer[]; } export interface Hovering { diff --git a/ui/ceval/src/view/main.ts b/ui/ceval/src/view/main.ts index d17e9a1962834..91ca2ec3ec3e3 100644 --- a/ui/ceval/src/view/main.ts +++ b/ui/ceval/src/view/main.ts @@ -91,28 +91,27 @@ function threatButton(ctrl: ParentCtrl): VNode | null { } function engineName(ctrl: CevalCtrl): VNode[] { - const engine = ctrl.engines.active, - engineTech = engine?.tech ?? 'EXTERNAL'; + const engine = ctrl.engines.active; return engine ? [ - h('span', { attrs: { title: engine?.name || '' } }, engine.short ?? engine.name), - engineTech === 'EXTERNAL' + h('span', { attrs: { title: engine.name } }, engine.short ?? engine.name), + ctrl.engines.isExternalEngineInfo(engine) ? h( 'span.technology.good', { attrs: { title: 'Engine running outside of the browser' } }, - engineTech, + engine.tech, ) - : engine.requires?.includes('simd') + : engine.requires.includes('simd') ? h( 'span.technology.good', { attrs: { title: 'Multi-threaded WebAssembly with SIMD' } }, - engineTech, + engine.tech, ) - : engine.requires?.includes('sharedMem') - ? h('span.technology.good', { attrs: { title: 'Multi-threaded WebAssembly' } }, engineTech) - : engine.requires?.includes('wasm') - ? h('span.technology', { attrs: { title: 'Single-threaded WebAssembly' } }, engineTech) - : h('span.technology', { attrs: { title: 'Single-threaded JavaScript' } }, engineTech), + : engine.requires.includes('sharedMem') + ? h('span.technology.good', { attrs: { title: 'Multi-threaded WebAssembly' } }, engine.tech) + : engine.requires.includes('wasm') + ? h('span.technology', { attrs: { title: 'Single-threaded WebAssembly' } }, engine.tech) + : h('span.technology', { attrs: { title: 'Single-threaded JavaScript' } }, engine.tech), ] : []; }