1
1
/* eslint-disable @typescript-eslint/no-explicit-any */
2
2
import { Chess } from 'chess.ts'
3
- import { AnalyzedGame , Player , StockfishEvaluation } from 'src/types'
4
3
import { GameTree } from 'src/types/base/tree'
5
4
import { AvailableMoves } from 'src/types/training'
5
+ import {
6
+ LiveGame ,
7
+ Player ,
8
+ StockfishEvaluation ,
9
+ StreamedGame ,
10
+ StreamedMove ,
11
+ } from 'src/types'
6
12
7
- // Re-use the readStream utility from analysis.ts
8
13
const readStream = ( processLine : ( data : any ) => void ) => ( response : any ) => {
9
14
const stream = response . body . getReader ( )
10
15
const matcher = / \r ? \n /
@@ -32,15 +37,14 @@ const readStream = (processLine: (data: any) => void) => (response: any) => {
32
37
return loop ( )
33
38
}
34
39
35
- // Get current Lichess TV game information
36
40
export const getLichessTVGame = async ( ) => {
37
41
const res = await fetch ( 'https://lichess.org/api/tv/channels' )
38
42
if ( ! res . ok ) {
39
43
throw new Error ( 'Failed to fetch Lichess TV data' )
40
44
}
41
45
const data = await res . json ( )
42
46
43
- // Return the best game (highest rated players)
47
+ // Return the best rapid game (highest rated players)
44
48
const bestChannel = data . rapid
45
49
if ( ! bestChannel ?. gameId ) {
46
50
throw new Error ( 'No TV game available' )
@@ -53,7 +57,6 @@ export const getLichessTVGame = async () => {
53
57
}
54
58
}
55
59
56
- // Get basic game information from Lichess API
57
60
export const getLichessGameInfo = async ( gameId : string ) => {
58
61
const res = await fetch ( `https://lichess.org/api/game/${ gameId } ` )
59
62
if ( ! res . ok ) {
@@ -62,11 +65,10 @@ export const getLichessGameInfo = async (gameId: string) => {
62
65
return res . json ( )
63
66
}
64
67
65
- // Stream live moves from a Lichess game
66
68
export const streamLichessGame = async (
67
69
gameId : string ,
68
- onGameStart : ( data : any ) => void ,
69
- onMove : ( data : any ) => void ,
70
+ onGameStart : ( data : StreamedGame ) => void ,
71
+ onMove : ( data : StreamedMove ) => void ,
70
72
onComplete : ( ) => void ,
71
73
abortSignal ?: AbortSignal ,
72
74
) => {
@@ -79,55 +81,18 @@ export const streamLichessGame = async (
79
81
} ,
80
82
} )
81
83
82
- let gameStarted = false
83
-
84
84
const onMessage = ( message : any ) => {
85
- console . log ( 'Raw message received:' , message )
86
-
87
85
if ( message . id ) {
88
- // This is the initial game state with full game info
89
86
console . log ( 'Game start message:' , message )
90
- onGameStart ( message )
91
- gameStarted = true
87
+ onGameStart ( message as StreamedGame )
92
88
} else if ( message . uci || message . lm ) {
93
- // This is a move - handle both formats: {"fen":"...", "uci":"e2e4"} or {"fen":"...", "lm":"e2e4"}
94
- // If we haven't received the initial game state yet, trigger game start with a minimal state
95
- if ( ! gameStarted ) {
96
- console . log (
97
- 'First move received without initial game state, creating minimal game' ,
98
- )
99
- onGameStart ( {
100
- id : gameId , // Use the gameId we're streaming
101
- players : {
102
- white : { user : { name : 'White' } } ,
103
- black : { user : { name : 'Black' } } ,
104
- } ,
105
- fen : message . fen ,
106
- } )
107
- gameStarted = true
108
- }
109
-
89
+ console . log ( 'Move message:' , message )
110
90
onMove ( {
111
91
fen : message . fen ,
112
92
uci : message . uci || message . lm ,
113
93
wc : message . wc ,
114
94
bc : message . bc ,
115
95
} )
116
- } else if ( message . fen && ! message . uci && ! message . lm ) {
117
- // This is the initial position - could be the first message for a starting game
118
- console . log ( 'Initial position received:' , message )
119
- if ( ! gameStarted ) {
120
- console . log ( 'Initial position message, creating game' )
121
- onGameStart ( {
122
- id : gameId ,
123
- players : {
124
- white : { user : { name : 'White' } } ,
125
- black : { user : { name : 'Black' } } ,
126
- } ,
127
- fen : message . fen ,
128
- } )
129
- gameStarted = true
130
- }
131
96
} else {
132
97
console . log ( 'Unknown message format:' , message )
133
98
}
@@ -155,27 +120,25 @@ export const streamLichessGame = async (
155
120
}
156
121
}
157
122
158
- // Convert Lichess game data to our AnalyzedGame format for live streaming
159
123
export const createAnalyzedGameFromLichessStream = (
160
124
gameData : any ,
161
- ) : AnalyzedGame => {
125
+ ) : LiveGame => {
162
126
const { players, id } = gameData
163
127
164
128
const whitePlayer : Player = {
165
- name : players ?. white ?. user ?. name || 'White' ,
129
+ name : players ?. white ?. user ?. id || 'White' ,
166
130
rating : players ?. white ?. rating ,
167
131
}
168
132
169
133
const blackPlayer : Player = {
170
- name : players ?. black ?. user ?. name || 'Black' ,
134
+ name : players ?. black ?. user ?. id || 'Black' ,
171
135
rating : players ?. black ?. rating ,
172
136
}
173
137
174
- // Use the starting position as our tree root - we'll build moves incrementally
175
- const startingFen = 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
138
+ const startingFen =
139
+ gameData . initialFen ||
140
+ 'rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1'
176
141
177
- // Build moves array with just the initial position for now
178
- // Moves will be added as they come in via the stream
179
142
const gameStates = [
180
143
{
181
144
board : startingFen ,
@@ -186,35 +149,28 @@ export const createAnalyzedGameFromLichessStream = (
186
149
} ,
187
150
]
188
151
189
- // Create game tree starting from the beginning
190
152
const tree = new GameTree ( startingFen )
191
153
192
154
return {
193
155
id : `stream-${ id } ` ,
194
156
blackPlayer,
195
157
whitePlayer,
158
+ gameType : 'stream' ,
159
+ type : 'stream' as const ,
196
160
moves : gameStates ,
197
161
availableMoves : new Array ( gameStates . length ) . fill ( { } ) as AvailableMoves [ ] ,
198
- gameType : 'blitz' , // Default to blitz, could be detected from game data
199
- termination : {
200
- result : '*' , // Live game in progress
201
- winner : undefined ,
202
- condition : 'Live' ,
203
- } ,
204
- maiaEvaluations : new Array ( gameStates . length ) . fill ( { } ) ,
205
- stockfishEvaluations : new Array ( gameStates . length ) . fill ( undefined ) as (
206
- | StockfishEvaluation
207
- | undefined
208
- ) [ ] ,
162
+ termination : undefined ,
163
+ maiaEvaluations : [ ] ,
164
+ stockfishEvaluations : [ ] ,
165
+ loadedFen : gameData . fen ,
166
+ loaded : false ,
209
167
tree,
210
- type : 'stream' as const , // Use stream type for live streams
211
- } as AnalyzedGame
168
+ } as LiveGame
212
169
}
213
170
214
- // Parse a move from the Lichess stream format and update game state
215
171
export const parseLichessStreamMove = (
216
- moveData : any ,
217
- currentGame : AnalyzedGame ,
172
+ moveData : StreamedMove ,
173
+ currentGame : LiveGame ,
218
174
) => {
219
175
const { uci, fen } = moveData
220
176
0 commit comments