convert ui/game to typescript

pull/2905/head
Niklas Fiekas 2017-04-03 21:38:12 +02:00
parent 03c2f1751e
commit 5607a40c15
14 changed files with 238 additions and 136 deletions

View File

@ -313,12 +313,12 @@ module.exports = {
deleteButton(d, ctrl.userId),
canContinue ? m('div.continue_with.g_' + d.game.id, [
m('a.button', {
href: d.userAnalysis ? '/?fen=' + ctrl.encodeNodeFen() + '#ai' : router.continue(d, 'ai') + '?fen=' + ctrl.vm.node.fen,
href: d.userAnalysis ? '/?fen=' + ctrl.encodeNodeFen() + '#ai' : router.cont(d, 'ai') + '?fen=' + ctrl.vm.node.fen,
rel: 'nofollow'
}, ctrl.trans('playWithTheMachine')),
m('br'),
m('a.button', {
href: d.userAnalysis ? '/?fen=' + ctrl.encodeNodeFen() + '#friend' : router.continue(d, 'friend') + '?fen=' + ctrl.vm.node.fen,
href: d.userAnalysis ? '/?fen=' + ctrl.encodeNodeFen() + '#friend' : router.cont(d, 'friend') + '?fen=' + ctrl.vm.node.fen,
rel: 'nofollow'
}, ctrl.trans('playWithAFriend'))
]) : null

View File

@ -35,6 +35,7 @@ build() {
build_ts "common"
build_ts "chess"
build_ts "ceval"
build_ts "game"
if [ -f $prll_sh ]; then # parallel execution!
. $prll_sh

View File

@ -2,7 +2,8 @@
"name": "game",
"version": "1.0.0",
"description": "lichess.org game",
"main": "src/main.js",
"main": "dist/main.js",
"types": "dist/main",
"repository": {
"type": "git",
"url": "https://github.com/ornicar/lila"
@ -19,17 +20,14 @@
"url": "https://github.com/ornicar/lila/issues"
},
"homepage": "https://github.com/ornicar/lila",
"scripts": {
"compile": "tsc"
},
"devDependencies": {
"browserify": "^14",
"gulp": "^3",
"gulp-streamify": "^1",
"gulp-uglify": "^2",
"gulp-util": "^3",
"uglify-js": "^2",
"vinyl-source-stream": "^1",
"watchify": "^3"
"typescript": "^2",
"types": "file:../types"
},
"dependencies": {
"chess": "file:../chess"
"mithril": "github:ornicar/mithril.js#lila-vdom-only-2"
}
}

View File

@ -1,34 +1,35 @@
var status = require('./status');
import { Data, Player } from './interfaces';
import * as status from './status';
function playable(data) {
export function playable(data: Data): boolean {
return data.game.status.id < status.ids.aborted && !imported(data);
}
function isPlayerPlaying(data) {
export function isPlayerPlaying(data: Data): boolean {
return playable(data) && !data.player.spectator;
}
function isPlayerTurn(data) {
export function isPlayerTurn(data: Data): boolean {
return isPlayerPlaying(data) && data.game.player == data.player.color;
}
function mandatory(data) {
export function mandatory(data: Data): boolean {
return !!data.tournament || !!data.simul;
}
function playedTurns(data) {
export function playedTurns(data: Data): number {
return data.game.turns - data.game.startedAtTurn;
}
function bothPlayersHavePlayed(data) {
export function bothPlayersHavePlayed(data: Data): boolean {
return playedTurns(data) > 1;
}
function abortable(data) {
export function abortable(data: Data): boolean {
return playable(data) && !bothPlayersHavePlayed(data) && !mandatory(data);
}
function takebackable(data) {
export function takebackable(data: Data): boolean {
return playable(data) &&
data.takebackable &&
!data.tournament &&
@ -38,93 +39,75 @@ function takebackable(data) {
!data.opponent.proposingTakeback;
}
function drawable(data) {
export function drawable(data: Data): boolean {
return playable(data) &&
data.game.turns >= 2 &&
!data.player.offeringDraw &&
!hasAi(data);
}
function resignable(data) {
export function resignable(data: Data): boolean {
return playable(data) && !abortable(data);
}
// can the current player go berserk?
function berserkableBy(data) {
export function berserkableBy(data: Data): boolean {
return data.tournament &&
data.tournament.berserkable &&
isPlayerPlaying(data) &&
!bothPlayersHavePlayed(data);
}
function moretimeable(data) {
export function moretimeable(data: Data): boolean {
return data.clock && isPlayerPlaying(data) && !mandatory(data);
}
function imported(data) {
export function imported(data: Data): boolean {
return data.game.source === 'import';
}
function replayable(data) {
export function replayable(data: Data): boolean {
return imported(data) || status.finished(data) ||
(status.aborted(data) && bothPlayersHavePlayed(data));
}
function getPlayer(data, color) {
export function getPlayer(data: Data, color: Color): Player;
export function getPlayer(data: Data, color?: Color): Player | null {
if (data.player.color == color) return data.player;
if (data.opponent.color == color) return data.opponent;
return null;
}
function hasAi(data) {
export function hasAi(data: Data): boolean {
return data.player.ai || data.opponent.ai;
}
function userAnalysable(data) {
export function userAnalysable(data: Data): boolean {
return playable(data) && (!data.clock || !isPlayerPlaying(data));
}
function isCorrespondence(data) {
export function isCorrespondence(data: Data): boolean {
return data.game.speed === 'correspondence';
}
function setOnGame(data, color, onGame) {
export function setOnGame(data: Data, color: Color, onGame: boolean): void {
var player = getPlayer(data, color);
onGame = onGame || player.ai;
player.onGame = onGame;
if (onGame) setIsGone(data, color, false);
}
function setIsGone(data, color, isGone) {
export function setIsGone(data: Data, 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;
}
function nbMoves(data, color) {
export function nbMoves(data: Data, color: Color): number {
return Math.floor((data.game.turns + (color == 'white' ? 1 : 0)) / 2);
}
module.exports = {
isPlayerPlaying: isPlayerPlaying,
isPlayerTurn: isPlayerTurn,
playable: playable,
abortable: abortable,
takebackable: takebackable,
drawable: drawable,
resignable: resignable,
berserkableBy: berserkableBy,
moretimeable: moretimeable,
mandatory: mandatory,
replayable: replayable,
userAnalysable: userAnalysable,
getPlayer: getPlayer,
nbMoves: nbMoves,
setOnGame: setOnGame,
setIsGone: setIsGone,
isCorrespondence: isCorrespondence,
isSwitchable: function(data) {
return !hasAi(data) && (data.simul || isCorrespondence(data));
}
};
export function isSwitchable(data: Data): boolean {
return !hasAi(data) && (!!data.simul || isCorrespondence(data));
}

View File

@ -0,0 +1,100 @@
export interface Data {
game: Game;
player: Player;
opponent: Player;
spectator: boolean;
tournament: Tournament;
simul: Simul;
takebackable: boolean;
clock: Clock;
}
export interface Game {
id: string;
status: Status;
player: Color;
turns: number;
startedAtTurn: number;
source: Source;
speed: Speed;
variant: Variant;
winner?: Color;
}
export interface Status {
id: StatusId;
name: StatusName;
}
export type StatusName = 'started' | 'aborted' | 'mate' | 'resign' |
'stalemate' | 'timeout' | 'draw' | 'outoftime' |
'noStart' | 'cheat' | 'variantEnd';
export type StatusId = 0;
export interface Player {
id: string;
user: User;
spectator: boolean;
color: Color;
proposingTakeback: boolean;
offeringDraw: boolean;
ai: boolean;
onGame: boolean;
isGone: boolean;
blurs?: Blurs;
hold?: Hold;
}
export interface Tournament {
berserkable: boolean;
}
export interface Simul {
name: string;
hostId: string;
nbPlaying: number;
}
export interface Clock {
}
export type Source = 'import' | 'lobby' | 'pool';
export type Speed = 'correspondence';
export interface User {
online: boolean;
}
export interface Ctrl {
data: Data;
trans: Trans;
}
export interface Blurs {
nb: number;
percent: number;
}
export interface Trans {
(key: string): string;
}
export interface Hold {
ply: number;
mean: number;
sd: number;
}
export type ContinueMode = 'friend' | 'ai';
export interface GameView {
status(ctrl: Ctrl): string;
mod: ModView;
}
export interface ModView {
blursOf(ctrl: Ctrl, player: Player): Mithril.Renderable;
holdOf(ctrl: Ctrl, player: Player): Mithril.Renderable;
}

View File

@ -1,23 +0,0 @@
module.exports = {
game: require('./game'),
status: require('./status'),
router: require('./router'),
view: {
status: require('./view/status'),
mod: require('./view/mod')
},
perf: {
icons: {
bullet: "T",
blitz: ")",
classical: "+",
correspondence: ";",
chess960: "'",
kingOfTheHill: "(",
threeCheck: ".",
antichess: "@",
atomic: ">",
horde: "_"
}
}
};

View File

@ -0,0 +1,32 @@
/// <reference types="types/lichess" />
/// <reference types="types/mithril" />
import { GameView } from './interfaces';
import * as game from './game';
import * as status from './status';
import * as router from './router';
import viewStatus from './view/status';
import * as viewMod from './view/mod';
export { game, status, router };
export const view: GameView = {
status: viewStatus,
mod: viewMod
};
export const perf = {
icons: {
bullet: "T",
blitz: ")",
classical: "+",
correspondence: ";",
chess960: "'",
kingOfTheHill: "(",
threeCheck: ".",
antichess: "@",
atomic: ">",
horde: "_"
}
};

View File

@ -1,17 +0,0 @@
var player = function(data) {
return '/' + data.game.id + data.player.id;
};
var game = function(data, color, embed) {
return (embed ? '/embed/' : '/') + (data.game ? data.game.id : data) + (color ? '/' + color : '');
};
module.exports = {
game: game,
player: player,
forecasts: function(data) {
return player(data) + '/forecasts';
},
continue: function(data, mode) {
return game(data) + '/continue/' + mode;
}
};

View File

@ -0,0 +1,17 @@
import { Data, ContinueMode } from './interfaces';
export function player(data: Data): string {
return '/' + data.game.id + data.player.id;
}
export function game(data: Data, color?: Color, embed?: boolean): string {
return (embed ? '/embed/' : '/') + (data.game ? data.game.id : data) + (color ? '/' + color : '');
}
export function forecasts(data: Data): string {
return player(data) + '/forecasts';
}
export function cont(data: Data, mode: ContinueMode): string {
return game(data) + '/continue/' + mode;
}

View File

@ -1,6 +1,8 @@
import { Data } from './interfaces';
// https://github.com/ornicar/scalachess/blob/master/src/main/scala/Status.scala
var ids = {
export const ids = {
created: 10,
started: 20,
aborted: 25,
@ -15,26 +17,18 @@ var ids = {
variantEnd: 60
};
function started(data) {
export function started(data: Data): boolean {
return data.game.status.id >= ids.started;
}
function finished(data) {
export function finished(data: Data): boolean {
return data.game.status.id >= ids.mate;
}
function aborted(data) {
export function aborted(data: Data): boolean {
return data.game.status.id === ids.aborted;
}
function playing(data) {
export function playing(data: Data): boolean {
return started(data) && !finished(data) && !aborted(data);
}
module.exports = {
ids: ids,
started: started,
finished: finished,
aborted: aborted,
playing: playing
};

View File

@ -1,25 +0,0 @@
var m = require('mithril');
var game = require('../game');
function blursOf(ctrl, player) {
if (player.blurs) return m('p', [
player.color,
' ' + player.blurs.nb + '/' + game.nbMoves(ctrl.data, player.color) + ' blurs = ',
m('strong', player.blurs.percent + '%')
]);
}
function holdOf(ctrl, player) {
var h = player.hold;
if (h) return m('p', [
player.color,
' hold alert',
m('br'),
'ply=' + h.ply + ', mean=' + h.mean + ' ms, SD=' + h.sd
]);
}
module.exports = {
blursOf: blursOf,
holdOf: holdOf
};

View File

@ -0,0 +1,22 @@
import { Ctrl, Player } from '../interfaces';
import * as m from 'mithril';
import * as game from '../game';
export function blursOf(ctrl: Ctrl, player: Player): Mithril.Renderable {
return player.blurs ? m('p', [
player.color,
' ' + player.blurs.nb + '/' + game.nbMoves(ctrl.data, player.color) + ' blurs = ',
m('strong', player.blurs.percent + '%')
]) : null;
}
export function holdOf(_ctrl: Ctrl, player: Player): Mithril.Renderable {
var h = player.hold;
return h ? m('p', [
player.color,
' hold alert',
m('br'),
'ply=' + h.ply + ', mean=' + h.mean + ' ms, SD=' + h.sd
]) : null;
}

View File

@ -1,4 +1,6 @@
module.exports = function(ctrl) {
import { Ctrl } from '../interfaces';
export default function(ctrl: Ctrl): string {
switch (ctrl.data.game.status.name) {
case 'started':
return ctrl.trans('playingRightNow');
@ -37,4 +39,4 @@ module.exports = function(ctrl) {
default:
return ctrl.data.game.status.name;
}
};
}

View File

@ -0,0 +1,18 @@
{
"include": ["src/*.ts"],
"exclude": [],
"compilerOptions": {
"outDir": "./dist",
"declaration": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noEmitOnError": true,
"alwaysStrict": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noUnusedParameters": true,
"target": "es5",
"lib": ["DOM", "ES5"]
}
}