-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathpositionLinks.ts
More file actions
137 lines (113 loc) · 3.43 KB
/
positionLinks.ts
File metadata and controls
137 lines (113 loc) · 3.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import { Chess } from 'chess.ts'
import { MAIA_MODELS } from 'src/constants/common'
export interface PositionLinkOptions {
fen: string
challengeId?: string
name?: string
description?: string
pgn?: string
playerColor?: 'white' | 'black'
maiaVersion?: string
targetMoveNumber?: number | null
returnTo?: string
forcedPlayerColor?: 'white' | 'black'
modalTitle?: string
modalSubtitle?: string
}
export const DEFAULT_POSITION_MAIA_MODEL = 'maia_kdd_1900'
const DEFAULT_POSITION_NAME = 'Tournament Position'
export const normalizeFen = (fen: string): string => {
const trimmed = fen.trim()
if (!trimmed) return trimmed
const parts = trimmed.split(/\s+/)
if (parts.length >= 6) {
return parts.slice(0, 6).join(' ')
}
const defaults: Record<number, string> = {
1: 'w',
2: '-',
3: '-',
4: '0',
5: '1',
}
const normalized = [...parts]
for (let index = parts.length; index < 6; index += 1) {
normalized[index] = defaults[index] ?? '0'
}
if (!normalized[1]) normalized[1] = 'w'
if (!normalized[2]) normalized[2] = '-'
if (!normalized[3]) normalized[3] = '-'
return normalized.slice(0, 6).join(' ')
}
export const isValidFen = (fen: string): boolean => {
const normalized = normalizeFen(fen)
if (!normalized) return false
try {
return new Chess().load(normalized)
} catch {
return false
}
}
export const inferPlayerColorFromFen = (fen: string): 'white' | 'black' => {
const normalized = normalizeFen(fen)
return normalized.split(' ')[1] === 'b' ? 'black' : 'white'
}
export const getValidMaiaModel = (model?: string): string => {
if (model && MAIA_MODELS.includes(model)) {
return model
}
return DEFAULT_POSITION_MAIA_MODEL
}
const getResolvedPositionName = (name?: string): string =>
name?.trim() || DEFAULT_POSITION_NAME
export const buildAnalysisPositionLink = (
options: PositionLinkOptions,
): string => {
const params = new URLSearchParams()
const name = getResolvedPositionName(options.name)
const normalizedFen = normalizeFen(options.fen)
const trimmedPgn = options.pgn?.trim()
params.set('name', name)
if (trimmedPgn) {
params.set('pgn', trimmedPgn)
} else {
params.set('fen', normalizedFen)
}
return `/analysis/custom?${params.toString()}`
}
export const buildPositionDrillLink = (
options: PositionLinkOptions,
): string => {
const params = new URLSearchParams()
const name = getResolvedPositionName(options.name)
const normalizedFen = normalizeFen(options.fen)
params.set('customFen', normalizedFen)
params.set('customName', name)
params.set('tab', 'custom')
return `/drills?${params.toString()}`
}
export const buildPositionPlayLink = (options: PositionLinkOptions): string => {
const params = new URLSearchParams()
const normalizedFen = normalizeFen(options.fen)
const forcedPlayerColor =
options.forcedPlayerColor ??
options.playerColor ??
inferPlayerColorFromFen(normalizedFen)
params.set('fen', normalizedFen)
params.set('maiaVersion', 'maia_kdd_1500')
params.set('timeControl', '10+5')
if (options.returnTo) {
params.set('returnTo', options.returnTo)
}
if (options.challengeId) {
params.set('challengeId', options.challengeId)
}
params.set('forcedColor', forcedPlayerColor)
if (options.modalTitle) {
params.set('modalTitle', options.modalTitle)
}
if (options.modalSubtitle) {
params.set('modalSubtitle', options.modalSubtitle)
}
return `/play?${params.toString()}`
}