Skip to content

Commit

Permalink
Use yaneuraou for positions that could be reached from default starti…
Browse files Browse the repository at this point in the history
…ng position
  • Loading branch information
WandererXII committed Mar 4, 2024
1 parent 3325bdb commit 68083b2
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 38 deletions.
17 changes: 14 additions & 3 deletions modules/game/src/main/EngineConfig.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package lila.game

import shogi.format.forsyth.Sfen
import shogi.variant.Variant
import shogi.Handicap
import shogi.variant.{ Standard, Variant }
import shogi.{ Handicap, Role }

case class EngineConfig(
level: Int,
Expand Down Expand Up @@ -41,7 +41,7 @@ object EngineConfig {
if (
variant.standard && level.fold(true)(_ > 1) && initialSfen
.filterNot(_.initialOf(variant))
.fold(true)(sf => Handicap.isHandicap(sf, variant))
.fold(true)(sf => Handicap.isHandicap(sf, variant) || reachableFromStandardInitialPosition(sf))
) YaneuraOu
else Fairy
}
Expand All @@ -52,4 +52,15 @@ object EngineConfig {
engine = Engine(sfen, variant, level.some)
)

def reachableFromStandardInitialPosition(sfen: Sfen): Boolean =
sfen.toSituation(Standard).exists { sit =>
val default = Standard.pieces.values.map(_.role)
def countHands(r: Role): Int =
~Standard.handRoles.find(_ == r).map(hr => sit.hands.sente(hr) + sit.hands.gote(hr))
sit.playable(strict = true, withImpasse = true) &&
Standard.allRoles.filterNot(r => Standard.unpromote(r).isDefined).forall { r =>
default
.count(_ == r) == (sit.board.count(r) + ~Standard.promote(r).map(sit.board.count) + countHands(r))
}
}
}
2 changes: 0 additions & 2 deletions ui/@types/lishogi/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,6 @@ interface Variant {
name: string;
}

declare type EngineCode = 'yn' | 'fs';

interface Paginator<A> {
currentPage: number;
maxPerPage: number;
Expand Down
23 changes: 4 additions & 19 deletions ui/ceval/src/ctrl.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { Result } from '@badrap/result';
import { prop } from 'common/common';
import { engineName } from 'common/engineName';
import { EngineCode, engineCode, engineName } from 'common/engineName';
import { isImpasse } from 'common/impasse';
import { isAndroid, isIOS, isIPad } from 'common/mobile';
import { storedProp } from 'common/storage';
import throttle from 'common/throttle';
import { parseSfen } from 'shogiops/sfen';
import { Position } from 'shogiops/variant/position';
import { defaultPosition } from 'shogiops/variant/variant';
import { Cache } from './cache';
import { CevalCtrl, CevalOpts, CevalTechnology, Hovering, PvBoard, Started, Step, Work } from './types';
Expand Down Expand Up @@ -58,22 +57,6 @@ function enabledAfterDisable() {
return enabledAfter === disable;
}

function isStandardMaterial(pos: Position) {
const board = pos.board,
hands = pos.hands.color('sente').combine(pos.hands.color('gote'));
return (
board.role('pawn').size() + board.role('tokin').size() + hands.get('pawn') <= 18 &&
board.role('lance').size() + board.role('promotedlance').size() + hands.get('lance') <= 4 &&
board.role('knight').size() + board.role('promotedknight').size() + hands.get('knight') <= 4 &&
board.role('silver').size() + board.role('promotedsilver').size() + hands.get('silver') <= 4 &&
board.role('gold').size() + hands.get('gold') <= 4 &&
board.role('rook').size() + board.role('dragon').size() + hands.get('rook') <= 2 &&
board.role('bishop').size() + board.role('horse').size() + hands.get('bishop') <= 2 &&
board.role('king').size() == 2 &&
board.role('king').intersect(board.color('sente')).size() === 1
);
}

export default function (opts: CevalOpts): CevalCtrl {
const storageKey = (k: string) => {
return opts.storageKeyPrefix ? `${opts.storageKeyPrefix}.${k}` : k;
Expand All @@ -87,7 +70,9 @@ export default function (opts: CevalOpts): CevalCtrl {
const analysable = pos.isOk && !unsupportedVariants.includes(opts.variant.key);

// select nnue > hce > none
const useYaneuraou = opts.variant.key === 'standard' && (!analysable || isStandardMaterial(pos.value)),
const useYaneuraou =
(!analysable && opts.variant.key === 'standard') ||
engineCode('standard', opts.initialSfen) === EngineCode.YaneuraOu,
fairySupports = !useYaneuraou && analysable;
let supportsNnue = false,
technology: CevalTechnology = 'none',
Expand Down
48 changes: 34 additions & 14 deletions ui/common/src/engineName.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,48 @@
import { Rules } from 'shogiops';
import { isHandicap } from 'shogiops/handicaps';
import { initialSfen } from 'shogiops/sfen';
import { initialSfen, parseSfen } from 'shogiops/sfen';

const useJp = document.documentElement.lang === 'ja-JP';

export const enum EngineCode {
YaneuraOu = 'yn',
Fairy = 'fs',
}

// modules/game/src/main/EngineConfig.scala
export function engineName(
rules: Rules,
sfen: Sfen | undefined,
level?: number,
withLevel?: boolean,
trans?: Trans
): string {
const code: EngineCode =
rules === 'standard' &&
export function engineCode(rules: Rules, sfen: Sfen | undefined, level?: number): EngineCode {
return rules === 'standard' &&
(!level || level > 1) &&
(!sfen || initialSfen(rules) === sfen || isHandicap({ sfen, rules }))
? 'yn'
: 'fs';
return engineNameFromCode(code, withLevel ? level : undefined, trans);
(!sfen || initialSfen(rules) === sfen || isHandicap({ sfen, rules }) || isStandardMaterial(sfen))
? EngineCode.YaneuraOu
: EngineCode.Fairy;
}

export function engineName(rules: Rules, sfen: Sfen | undefined, level?: number, trans?: Trans): string {
const code = engineCode(rules, sfen, level);
return engineNameFromCode(code, level, trans);
}

export function engineNameFromCode(code?: EngineCode, level?: number, trans?: Trans): string {
const name = code === 'fs' ? 'Fairy Stockfish' : useJp ? 'やねうら王' : 'YaneuraOu';
if (level && trans) return name + ' - ' + trans('levelX', level);
else return name;
}

function isStandardMaterial(sfen: Sfen): boolean {
const pos = parseSfen('standard', sfen);
if (pos.isErr) return false;
const board = pos.value.board,
hands = pos.value.hands.color('sente').combine(pos.value.hands.color('gote'));
return (
board.role('pawn').size() + board.role('tokin').size() + hands.get('pawn') <= 18 &&
board.role('lance').size() + board.role('promotedlance').size() + hands.get('lance') <= 4 &&
board.role('knight').size() + board.role('promotedknight').size() + hands.get('knight') <= 4 &&
board.role('silver').size() + board.role('promotedsilver').size() + hands.get('silver') <= 4 &&
board.role('gold').size() + hands.get('gold') <= 4 &&
board.role('rook').size() + board.role('dragon').size() + hands.get('rook') <= 2 &&
board.role('bishop').size() + board.role('horse').size() + hands.get('bishop') <= 2 &&
board.role('king').size() == 2 &&
board.role('king').intersect(board.color('sente')).size() === 1
);
}
2 changes: 2 additions & 0 deletions ui/game/src/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { EngineCode } from 'common/engineName';

export interface GameData {
game: Game;
player: Player;
Expand Down
1 change: 1 addition & 0 deletions ui/puzzle/src/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CevalCtrl, NodeEvals } from 'ceval';
import { Prop } from 'common/common';
import { Deferred } from 'common/defer';
import { EngineCode } from 'common/engineName';
import { StoredBooleanProp } from 'common/storage';
import { Api as SgApi } from 'shogiground/api';
import { Config as SgConfig } from 'shogiground/config';
Expand Down

0 comments on commit 68083b2

Please sign in to comment.