convert autoplay.js
This commit is contained in:
parent
c31da78c4d
commit
7abb6195a8
|
@ -1,79 +0,0 @@
|
|||
var control = require('./control');
|
||||
var m = require('mithril');
|
||||
|
||||
module.exports = function(ctrl) {
|
||||
|
||||
var timeout;
|
||||
|
||||
this.delay = null;
|
||||
|
||||
var move = function() {
|
||||
if (control.canGoForward(ctrl)) {
|
||||
control.next(ctrl);
|
||||
m.redraw();
|
||||
return true;
|
||||
}
|
||||
this.stop();
|
||||
m.redraw();
|
||||
return false;
|
||||
}.bind(this);
|
||||
|
||||
var evalToCp = function(node) {
|
||||
if (!node.eval) return node.ply % 2 ? 990 : -990; // game over
|
||||
return node.eval.mate ? 990 * Math.sign(node.eval.mate) : node.eval.cp;
|
||||
};
|
||||
|
||||
var nextDelay = function() {
|
||||
if (typeof(this.delay) === 'string') {
|
||||
// in a variation
|
||||
if (!ctrl.vm.onMainline) return 1500;
|
||||
if (this.delay === 'realtime') {
|
||||
if (ctrl.vm.node.ply < 2) return 1000;
|
||||
var time = ctrl.data.game.moveCentis[ctrl.vm.node.ply - ctrl.tree.root.ply];
|
||||
// estimate 130ms of lag to improve playback.
|
||||
return time * 10 + 130 || 2000;
|
||||
} else {
|
||||
var slowDown = this.delay === 'cpl_fast' ? 10 : 30;
|
||||
if (ctrl.vm.node.ply >= ctrl.vm.mainline.length - 1) return 0;
|
||||
var currPlyCp = evalToCp(ctrl.vm.node);
|
||||
var nextPlyCp = evalToCp(ctrl.vm.node.children[0]);
|
||||
return Math.max(500,
|
||||
Math.min(10000,
|
||||
Math.abs(currPlyCp - nextPlyCp) * slowDown));
|
||||
}
|
||||
}
|
||||
return this.delay;
|
||||
}.bind(this);
|
||||
|
||||
var schedule = function() {
|
||||
timeout = setTimeout(function() {
|
||||
if (move()) schedule();
|
||||
}, nextDelay());
|
||||
}.bind(this);
|
||||
|
||||
var start = function(delay) {
|
||||
this.delay = delay;
|
||||
this.stop();
|
||||
schedule();
|
||||
}.bind(this);
|
||||
|
||||
this.stop = function() {
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
this.toggle = function(delay) {
|
||||
if (this.active(delay)) this.stop();
|
||||
else {
|
||||
if (!this.active())
|
||||
if (!move()) ctrl.jump('');
|
||||
start(delay);
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
this.active = function(delay) {
|
||||
return (!delay || delay === this.delay) && !!timeout;
|
||||
}.bind(this);
|
||||
};
|
86
ui/analyse/src/autoplay.ts
Normal file
86
ui/analyse/src/autoplay.ts
Normal file
|
@ -0,0 +1,86 @@
|
|||
import { AnalyseController } from './interfaces';
|
||||
|
||||
import * as m from 'mithril';
|
||||
import * as control from './control';
|
||||
|
||||
type Delay = number | 'realtime' | 'cpl_fast' | 'cpl_slow' | 'fast' | 'slow';
|
||||
|
||||
export default class Autoplay {
|
||||
private ctrl: AnalyseController;
|
||||
private timeout: number | undefined;
|
||||
private delay: Delay | undefined;
|
||||
|
||||
constructor(ctrl: AnalyseController) {
|
||||
this.ctrl = ctrl;
|
||||
}
|
||||
|
||||
private move(): boolean {
|
||||
if (control.canGoForward(this.ctrl)) {
|
||||
control.next(this.ctrl);
|
||||
m.redraw();
|
||||
return true;
|
||||
}
|
||||
this.stop();
|
||||
m.redraw();
|
||||
return false;
|
||||
}
|
||||
|
||||
private evalToCp(node: Tree.Node): number {
|
||||
if (!node.eval) return node.ply % 2 ? 990 : -990; // game over
|
||||
if (node.eval.mate) return (node.eval.mate > 0) ? 990 : -990;
|
||||
return node.eval.cp!;
|
||||
}
|
||||
|
||||
private nextDelay(): number {
|
||||
if (typeof this.delay === 'string') {
|
||||
// in a variation
|
||||
if (!this.ctrl.vm.onMainline) return 1500;
|
||||
if (this.delay === 'realtime') {
|
||||
if (this.ctrl.vm.node.ply < 2) return 1000;
|
||||
var time = this.ctrl.data.game.moveCentis![this.ctrl.vm.node.ply - this.ctrl.tree.root.ply];
|
||||
// estimate 130ms of lag to improve playback.
|
||||
return time * 10 + 130 || 2000;
|
||||
} else {
|
||||
var slowDown = this.delay === 'cpl_fast' ? 10 : 30;
|
||||
if (this.ctrl.vm.node.ply >= this.ctrl.vm.mainline.length - 1) return 0;
|
||||
var currPlyCp = this.evalToCp(this.ctrl.vm.node);
|
||||
var nextPlyCp = this.evalToCp(this.ctrl.vm.node.children[0]);
|
||||
return Math.max(500,
|
||||
Math.min(10000,
|
||||
Math.abs(currPlyCp - nextPlyCp) * slowDown));
|
||||
}
|
||||
}
|
||||
return this.delay!;
|
||||
}
|
||||
|
||||
private schedule(): void {
|
||||
this.timeout = setTimeout(() => {
|
||||
if (this.move()) this.schedule();
|
||||
}, this.nextDelay());
|
||||
}
|
||||
|
||||
start(delay: Delay): void {
|
||||
this.delay = delay;
|
||||
this.stop();
|
||||
this.schedule();
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
toggle(delay: Delay): void {
|
||||
if (this.active(delay)) this.stop();
|
||||
else {
|
||||
if (!this.active() && !this.move()) this.ctrl.jump('');
|
||||
this.start(delay);
|
||||
}
|
||||
}
|
||||
|
||||
active(delay?: Delay): boolean {
|
||||
return (!delay || delay === this.delay) && !!this.timeout;
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ var opposite = require('chessground/util').opposite;
|
|||
var tree = require('tree');
|
||||
var keyboard = require('./keyboard');
|
||||
var actionMenu = require('./actionMenu').controller;
|
||||
var autoplay = require('./autoplay');
|
||||
var autoplay = require('./autoplay').default;
|
||||
var promotion = require('./promotion');
|
||||
var util = require('./util');
|
||||
var chessUtil = require('chess');
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { GameData } from 'game';
|
||||
|
||||
export interface AnalyseController {
|
||||
study?: Study;
|
||||
studyPractice?: StudyPractice;
|
||||
|
@ -6,6 +8,10 @@ export interface AnalyseController {
|
|||
jumpToIndex(index: number): void;
|
||||
userJumpIfCan(path: Tree.Path): void;
|
||||
userJump(path: Tree.Path): void;
|
||||
jump(path: Tree.Path): void;
|
||||
|
||||
data: GameData;
|
||||
tree: any; // TODO: Tree.Tree;
|
||||
}
|
||||
|
||||
export interface AnalyseOpts {
|
||||
|
|
|
@ -1,35 +1,35 @@
|
|||
import { Data, Player } from './interfaces';
|
||||
import { GameData, Player } from './interfaces';
|
||||
import * as status from './status';
|
||||
|
||||
export function playable(data: Data): boolean {
|
||||
export function playable(data: GameData): boolean {
|
||||
return data.game.status.id < status.ids.aborted && !imported(data);
|
||||
}
|
||||
|
||||
export function isPlayerPlaying(data: Data): boolean {
|
||||
export function isPlayerPlaying(data: GameData): boolean {
|
||||
return playable(data) && !data.player.spectator;
|
||||
}
|
||||
|
||||
export function isPlayerTurn(data: Data): boolean {
|
||||
export function isPlayerTurn(data: GameData): boolean {
|
||||
return isPlayerPlaying(data) && data.game.player == data.player.color;
|
||||
}
|
||||
|
||||
export function mandatory(data: Data): boolean {
|
||||
export function mandatory(data: GameData): boolean {
|
||||
return !!data.tournament || !!data.simul;
|
||||
}
|
||||
|
||||
export function playedTurns(data: Data): number {
|
||||
export function playedTurns(data: GameData): number {
|
||||
return data.game.turns - data.game.startedAtTurn;
|
||||
}
|
||||
|
||||
export function bothPlayersHavePlayed(data: Data): boolean {
|
||||
export function bothPlayersHavePlayed(data: GameData): boolean {
|
||||
return playedTurns(data) > 1;
|
||||
}
|
||||
|
||||
export function abortable(data: Data): boolean {
|
||||
export function abortable(data: GameData): boolean {
|
||||
return playable(data) && !bothPlayersHavePlayed(data) && !mandatory(data);
|
||||
}
|
||||
|
||||
export function takebackable(data: Data): boolean {
|
||||
export function takebackable(data: GameData): boolean {
|
||||
return playable(data) &&
|
||||
data.takebackable &&
|
||||
!data.tournament &&
|
||||
|
@ -39,75 +39,75 @@ export function takebackable(data: Data): boolean {
|
|||
!data.opponent.proposingTakeback;
|
||||
}
|
||||
|
||||
export function drawable(data: Data): boolean {
|
||||
export function drawable(data: GameData): boolean {
|
||||
return playable(data) &&
|
||||
data.game.turns >= 2 &&
|
||||
!data.player.offeringDraw &&
|
||||
!hasAi(data);
|
||||
}
|
||||
|
||||
export function resignable(data: Data): boolean {
|
||||
export function resignable(data: GameData): boolean {
|
||||
return playable(data) && !abortable(data);
|
||||
}
|
||||
|
||||
// can the current player go berserk?
|
||||
export function berserkableBy(data: Data): boolean {
|
||||
export function berserkableBy(data: GameData): boolean {
|
||||
return !!data.tournament &&
|
||||
data.tournament.berserkable &&
|
||||
isPlayerPlaying(data) &&
|
||||
!bothPlayersHavePlayed(data);
|
||||
}
|
||||
|
||||
export function moretimeable(data: Data): boolean {
|
||||
export function moretimeable(data: GameData): boolean {
|
||||
return !!data.clock && isPlayerPlaying(data) && !mandatory(data);
|
||||
}
|
||||
|
||||
export function imported(data: Data): boolean {
|
||||
export function imported(data: GameData): boolean {
|
||||
return data.game.source === 'import';
|
||||
}
|
||||
|
||||
export function replayable(data: Data): boolean {
|
||||
export function replayable(data: GameData): boolean {
|
||||
return imported(data) || status.finished(data) ||
|
||||
(status.aborted(data) && bothPlayersHavePlayed(data));
|
||||
}
|
||||
|
||||
export function getPlayer(data: Data, color: Color): Player;
|
||||
export function getPlayer(data: Data, color?: Color): Player | null {
|
||||
export function getPlayer(data: GameData, color: Color): Player;
|
||||
export function getPlayer(data: GameData, color?: Color): Player | null {
|
||||
if (data.player.color == color) return data.player;
|
||||
if (data.opponent.color == color) return data.opponent;
|
||||
return null;
|
||||
}
|
||||
|
||||
export function hasAi(data: Data): boolean {
|
||||
export function hasAi(data: GameData): boolean {
|
||||
return data.player.ai || data.opponent.ai;
|
||||
}
|
||||
|
||||
export function userAnalysable(data: Data): boolean {
|
||||
export function userAnalysable(data: GameData): boolean {
|
||||
return playable(data) && (!data.clock || !isPlayerPlaying(data));
|
||||
}
|
||||
|
||||
export function isCorrespondence(data: Data): boolean {
|
||||
export function isCorrespondence(data: GameData): boolean {
|
||||
return data.game.speed === 'correspondence';
|
||||
}
|
||||
|
||||
export function setOnGame(data: Data, color: Color, onGame: boolean): void {
|
||||
export function setOnGame(data: GameData, color: Color, onGame: boolean): void {
|
||||
var player = getPlayer(data, color);
|
||||
onGame = onGame || player.ai;
|
||||
player.onGame = onGame;
|
||||
if (onGame) setIsGone(data, color, false);
|
||||
}
|
||||
|
||||
export function setIsGone(data: Data, color: Color, isGone: boolean): void {
|
||||
export function setIsGone(data: GameData, color: Color, isGone: boolean): void {
|
||||
var player = getPlayer(data, color);
|
||||
isGone = isGone && !player.ai;
|
||||
player.isGone = isGone;
|
||||
if (!isGone && player.user) player.user.online = true;
|
||||
}
|
||||
|
||||
export function nbMoves(data: Data, color: Color): number {
|
||||
export function nbMoves(data: GameData, color: Color): number {
|
||||
return Math.floor((data.game.turns + (color == 'white' ? 1 : 0)) / 2);
|
||||
}
|
||||
|
||||
export function isSwitchable(data: Data): boolean {
|
||||
export function isSwitchable(data: GameData): boolean {
|
||||
return !hasAi(data) && (!!data.simul || isCorrespondence(data));
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
export interface Data {
|
||||
export interface GameData {
|
||||
game: Game;
|
||||
player: Player;
|
||||
opponent: Player;
|
||||
|
@ -19,6 +19,7 @@ export interface Game {
|
|||
speed: Speed;
|
||||
variant: Variant;
|
||||
winner?: Color;
|
||||
moveCentis?: number[];
|
||||
}
|
||||
|
||||
export interface Status {
|
||||
|
@ -66,7 +67,7 @@ export interface User {
|
|||
}
|
||||
|
||||
export interface Ctrl {
|
||||
data: Data;
|
||||
data: GameData;
|
||||
trans: Trans;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/// <reference types="types/lichess" />
|
||||
/// <reference types="types/mithril" />
|
||||
|
||||
import { GameView } from './interfaces';
|
||||
import { GameData, GameView } from './interfaces';
|
||||
|
||||
import * as game from './game';
|
||||
import * as status from './status';
|
||||
|
@ -9,7 +9,7 @@ import * as router from './router';
|
|||
import viewStatus from './view/status';
|
||||
import * as viewMod from './view/mod';
|
||||
|
||||
export { game, status, router };
|
||||
export { GameData, game, status, router };
|
||||
|
||||
export const view: GameView = {
|
||||
status: viewStatus,
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
import { Data, ContinueMode } from './interfaces';
|
||||
import { GameData, ContinueMode } from './interfaces';
|
||||
|
||||
export function player(data: Data): string {
|
||||
export function player(data: GameData): string {
|
||||
return '/' + data.game.id + data.player.id;
|
||||
}
|
||||
|
||||
export function game(data: Data, color?: Color, embed?: boolean): string {
|
||||
export function game(data: GameData, color?: Color, embed?: boolean): string {
|
||||
return (embed ? '/embed/' : '/') + (data.game ? data.game.id : data) + (color ? '/' + color : '');
|
||||
}
|
||||
|
||||
export function forecasts(data: Data): string {
|
||||
export function forecasts(data: GameData): string {
|
||||
return player(data) + '/forecasts';
|
||||
}
|
||||
|
||||
export function cont(data: Data, mode: ContinueMode): string {
|
||||
export function cont(data: GameData, mode: ContinueMode): string {
|
||||
return game(data) + '/continue/' + mode;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Data } from './interfaces';
|
||||
import { GameData } from './interfaces';
|
||||
|
||||
// https://github.com/ornicar/scalachess/blob/master/src/main/scala/Status.scala
|
||||
|
||||
|
@ -17,18 +17,18 @@ export const ids = {
|
|||
variantEnd: 60
|
||||
};
|
||||
|
||||
export function started(data: Data): boolean {
|
||||
export function started(data: GameData): boolean {
|
||||
return data.game.status.id >= ids.started;
|
||||
}
|
||||
|
||||
export function finished(data: Data): boolean {
|
||||
export function finished(data: GameData): boolean {
|
||||
return data.game.status.id >= ids.mate;
|
||||
}
|
||||
|
||||
export function aborted(data: Data): boolean {
|
||||
export function aborted(data: GameData): boolean {
|
||||
return data.game.status.id === ids.aborted;
|
||||
}
|
||||
|
||||
export function playing(data: Data): boolean {
|
||||
export function playing(data: GameData): boolean {
|
||||
return started(data) && !finished(data) && !aborted(data);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue