Skip to content

Commit a231a8b

Browse files
feat: migrate turing to use tree datastructure + clean up type inconsistencies
1 parent 20321b6 commit a231a8b

11 files changed

Lines changed: 114 additions & 353 deletions

File tree

src/components/Board/BoardController.tsx

Lines changed: 13 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { GameNode } from 'src/types'
12
import { useWindowSize } from 'src/hooks'
23
import { FlipIcon } from 'src/components/Icons/icons'
34
import { useCallback, useEffect, useMemo } from 'react'
@@ -6,14 +7,12 @@ interface Props {
67
// Controller data
78
orientation: 'white' | 'black'
89
setOrientation: (orientation: 'white' | 'black') => void
9-
currentNode?: any
10-
currentIndex?: number
10+
currentNode: GameNode
1111
plyCount: number
12-
goToNode?: (node: any) => void
12+
goToNode: (node: GameNode) => void
1313
goToNextNode: () => void
1414
goToPreviousNode: () => void
1515
goToRootNode: () => void
16-
setCurrentIndex?: (index: number) => void
1716
gameTree?: any
1817

1918
// Optional event handler
@@ -24,13 +23,11 @@ export const BoardController: React.FC<Props> = ({
2423
orientation,
2524
setOrientation,
2625
currentNode,
27-
currentIndex,
2826
plyCount,
2927
goToNode,
3028
goToNextNode,
3129
goToPreviousNode,
3230
goToRootNode,
33-
setCurrentIndex,
3431
gameTree,
3532
setCurrentMove,
3633
}: Props) => {
@@ -40,26 +37,19 @@ export const BoardController: React.FC<Props> = ({
4037
setOrientation(orientation === 'white' ? 'black' : 'white')
4138
}, [orientation, setOrientation])
4239

43-
// Determine navigation state based on available data
4440
const hasPrevious = useMemo(() => {
4541
if (currentNode !== undefined) {
4642
return !!currentNode?.parent
4743
}
48-
if (currentIndex !== undefined) {
49-
return currentIndex > 0
50-
}
5144
return false
52-
}, [currentNode, currentIndex])
45+
}, [currentNode])
5346

5447
const hasNext = useMemo(() => {
5548
if (currentNode !== undefined) {
5649
return !!currentNode?.mainChild
5750
}
58-
if (currentIndex !== undefined) {
59-
return currentIndex < plyCount - 1
60-
}
6151
return false
62-
}, [currentNode, currentIndex, plyCount])
52+
}, [currentNode])
6353

6454
const getFirst = useCallback(() => {
6555
goToRootNode()
@@ -77,24 +67,16 @@ export const BoardController: React.FC<Props> = ({
7767
}, [goToNextNode, setCurrentMove])
7868

7969
const getLast = useCallback(() => {
80-
if (currentNode && goToNode) {
81-
// Node-based navigation (Analysis, Play, Training)
82-
let lastNode = currentNode
83-
while (lastNode?.mainChild) {
84-
lastNode = lastNode.mainChild
85-
}
86-
if (lastNode) {
87-
goToNode(lastNode)
88-
}
89-
} else if (gameTree && setCurrentIndex) {
90-
// Index-based navigation (Turing)
91-
const mainLine = gameTree.getMainLine()
92-
if (mainLine.length > 0) {
93-
setCurrentIndex(mainLine.length - 1)
94-
}
70+
let lastNode = currentNode
71+
while (lastNode?.mainChild) {
72+
lastNode = lastNode.mainChild
73+
}
74+
if (lastNode) {
75+
goToNode(lastNode)
9576
}
77+
9678
setCurrentMove?.(null)
97-
}, [currentNode, goToNode, gameTree, setCurrentIndex, setCurrentMove])
79+
}, [currentNode, goToNode, gameTree, setCurrentMove])
9880

9981
useEffect(() => {
10082
if (width <= 670) return

src/components/Misc/ExportGame.tsx

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ interface AnalysisProps {
1212
blackPlayer: string
1313
event: string
1414
type: 'analysis'
15-
currentNode?: GameNode
15+
currentNode: GameNode
1616
}
1717

1818
interface PlayProps {
@@ -22,7 +22,7 @@ interface PlayProps {
2222
blackPlayer: string
2323
event: string
2424
type: 'play'
25-
currentNode?: never
25+
currentNode: GameNode
2626
}
2727

2828
interface TuringProps {
@@ -31,7 +31,7 @@ interface TuringProps {
3131
blackPlayer: string
3232
event: string
3333
type: 'turing'
34-
currentNode?: never
34+
currentNode: GameNode
3535
}
3636

3737
type Props = AnalysisProps | PlayProps | TuringProps
@@ -46,7 +46,7 @@ export const ExportGame: React.FC<Props> = (props) => {
4646
const { currentNode, gameTree } =
4747
type === 'analysis'
4848
? {
49-
currentNode: props.currentNode || controller.currentNode,
49+
currentNode: props.currentNode,
5050
gameTree: (props.game as AnalyzedGame).tree,
5151
}
5252
: type === 'play'
@@ -60,37 +60,21 @@ export const ExportGame: React.FC<Props> = (props) => {
6060
}
6161

6262
useEffect(() => {
63-
if (gameTree && currentNode) {
64-
const tree = new GameTree(gameTree.getRoot().fen)
65-
tree.setHeader('ID', game.id)
66-
tree.setHeader('Event', event)
67-
tree.setHeader('Site', 'https://maiachess.com/')
68-
tree.setHeader('White', whitePlayer)
69-
tree.setHeader('Black', blackPlayer)
70-
if (game.termination) {
71-
tree.setHeader('Result', game.termination.result)
72-
if (game.termination.condition) {
73-
tree.setHeader('Termination', game.termination.condition)
74-
}
75-
}
76-
77-
setPgn(gameTree.toPGN())
78-
setFen(currentNode.fen)
79-
} else {
80-
// Fallback to legacy array-based approach
81-
const initial = new Chess(game.moves[0]?.board || new Chess().fen())
82-
initial.addHeader('ID', game.id)
83-
initial.addHeader('Event', event)
84-
initial.addHeader('Site', `https://maiachess.com/`)
85-
initial.addHeader('White', whitePlayer)
86-
initial.addHeader('Black', blackPlayer)
87-
if (game.termination) {
88-
initial.addHeader('Result', game.termination.result)
89-
if (game.termination.condition) {
90-
initial.addHeader('Termination', game.termination.condition)
91-
}
63+
const tree = new GameTree(gameTree.getRoot().fen)
64+
tree.setHeader('ID', game.id)
65+
tree.setHeader('Event', event)
66+
tree.setHeader('Site', 'https://maiachess.com/')
67+
tree.setHeader('White', whitePlayer)
68+
tree.setHeader('Black', blackPlayer)
69+
if (game.termination) {
70+
tree.setHeader('Result', game.termination.result)
71+
if (game.termination.condition) {
72+
tree.setHeader('Termination', game.termination.condition)
9273
}
9374
}
75+
76+
setPgn(gameTree.toPGN())
77+
setFen(currentNode.fen)
9478
}, [
9579
currentNode,
9680
game.moves,

src/contexts/BaseTreeControllerContext.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@ import { GameTree, GameNode } from 'src/types'
22

33
export interface BaseTreeControllerContext {
44
gameTree: GameTree
5-
currentNode?: GameNode
5+
currentNode: GameNode
66
goToNode: (node: GameNode) => void
77
goToNextNode: () => void
88
goToPreviousNode: () => void
99
goToRootNode: () => void
10-
currentIndex: number
11-
setCurrentIndex: (index: number) => void
1210
plyCount: number
1311
orientation: 'white' | 'black'
1412
setOrientation: (orientation: 'white' | 'black') => void

src/contexts/PlayControllerContext/PlayControllerContext.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,13 @@ export interface IPlayControllerContext extends BaseTreeControllerContext {
1313
playerActive: ReturnType<typeof usePlayMaiaController>['playerActive']
1414
toPlay: ReturnType<typeof usePlayMaiaController>['toPlay']
1515
moves: ReturnType<typeof usePlayMaiaController>['moves']
16-
moveTimes: ReturnType<typeof usePlayMaiaController>['moveTimes']
1716
availableMoves: ReturnType<typeof usePlayMaiaController>['availableMoves']
1817
pieces: ReturnType<typeof usePlayMaiaController>['pieces']
1918
moveList: ReturnType<typeof usePlayMaiaController>['moveList']
2019
whiteClock: ReturnType<typeof usePlayMaiaController>['whiteClock']
2120
blackClock: ReturnType<typeof usePlayMaiaController>['blackClock']
2221
lastMoveTime: ReturnType<typeof usePlayMaiaController>['lastMoveTime']
2322
stats: ReturnType<typeof usePlayMaiaController>['stats']
24-
setMoves: ReturnType<typeof usePlayMaiaController>['setMoves']
25-
setMoveTimes: ReturnType<typeof usePlayMaiaController>['setMoveTimes']
2623
setResigned: ReturnType<typeof usePlayMaiaController>['setResigned']
2724
reset: ReturnType<typeof usePlayMaiaController>['reset']
2825
makeMove: ReturnType<typeof usePlayMaiaController>['makeMove']
@@ -48,7 +45,6 @@ export const PlayControllerContext =
4845
playerActive: false,
4946
toPlay: 'black',
5047
moves: [],
51-
moveTimes: [],
5248
availableMoves: [],
5349
pieces: {},
5450
moveList: [],
@@ -64,8 +60,6 @@ export const PlayControllerContext =
6460
lastRating: undefined,
6561
rating: 0,
6662
},
67-
setMoves: fn,
68-
setMoveTimes: fn,
6963
setResigned: fn,
7064
reset: fn,
7165
makeMove: fn,
@@ -77,8 +71,6 @@ export const PlayControllerContext =
7771
goToNextNode: fn,
7872
goToPreviousNode: fn,
7973
goToRootNode: fn,
80-
currentIndex: 0,
81-
setCurrentIndex: fn,
8274
addMove: fn,
8375
addMoveWithTime: fn,
8476
})

src/contexts/TuringTreeControllerContext/TuringTreeControllerContext.ts

Lines changed: 16 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Chess } from 'chess.ts'
22
import { createContext } from 'react'
3-
import { GameTree, Color } from 'src/types'
3+
import { GameTree, Color, GameNode } from 'src/types'
44
import { TuringGame } from 'src/types/turing'
55
import { AllStats } from 'src/hooks/useStats'
66
import { BaseTreeControllerContext } from '../BaseTreeControllerContext'
@@ -13,36 +13,19 @@ export interface ITuringControllerContext extends BaseTreeControllerContext {
1313
stats: AllStats
1414

1515
getNewGame: () => Promise<void>
16-
setCurrentId: (id: string | null) => void
16+
currentGameId: string
17+
setCurrentGameId: (id: string) => void
1718
submitGuess: (
1819
guess: Color,
1920
comment?: string,
2021
rating?: number,
2122
) => Promise<void>
2223
commentController: [string, (comment: string) => void]
23-
24-
controller: {
25-
plyCount: number
26-
currentIndex: number
27-
setCurrentIndex: (index: number) => void
28-
orientation: 'white' | 'black'
29-
setOrientation: (orientation: 'white' | 'black') => void
30-
}
3124
}
3225

3326
const defaultContext: ITuringControllerContext = {
34-
game: undefined,
35-
games: {},
36-
loading: false,
37-
gameIds: [],
38-
stats: {
39-
lifetime: undefined,
40-
session: { gamesWon: 0, gamesPlayed: 0 },
41-
lastRating: undefined,
42-
rating: 0,
43-
},
4427
gameTree: new GameTree(new Chess().fen()),
45-
currentNode: undefined,
28+
currentNode: new GameTree(new Chess().fen()).getRoot(),
4629
goToNode: () => {
4730
/* no-op */
4831
},
@@ -55,19 +38,26 @@ const defaultContext: ITuringControllerContext = {
5538
goToRootNode: () => {
5639
/* no-op */
5740
},
58-
currentIndex: 0,
59-
setCurrentIndex: () => {
41+
currentGameId: '',
42+
setCurrentGameId: () => {
6043
/* no-op */
6144
},
6245
plyCount: 0,
6346
orientation: 'white',
6447
setOrientation: () => {
6548
/* no-op */
6649
},
67-
getNewGame: async () => {
68-
/* no-op */
50+
game: undefined,
51+
games: {},
52+
loading: false,
53+
gameIds: [],
54+
stats: {
55+
lifetime: undefined,
56+
session: { gamesWon: 0, gamesPlayed: 0 },
57+
lastRating: undefined,
58+
rating: 0,
6959
},
70-
setCurrentId: () => {
60+
getNewGame: async () => {
7161
/* no-op */
7262
},
7363
submitGuess: async () => {
@@ -79,17 +69,6 @@ const defaultContext: ITuringControllerContext = {
7969
/* no-op */
8070
},
8171
],
82-
controller: {
83-
plyCount: 0,
84-
currentIndex: 0,
85-
setCurrentIndex: () => {
86-
/* no-op */
87-
},
88-
orientation: 'white',
89-
setOrientation: () => {
90-
/* no-op */
91-
},
92-
},
9372
}
9473

9574
export const TuringControllerContext =

src/hooks/usePlayController/usePlayMaiaController.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,7 @@ export const usePlayMaiaController = (id: string, config: PlayGameConfig) => {
5454
const [blackClock, setBlackClock] = useState<number>(initialClockValue)
5555
const [lastMoveTime, setLastMoveTime] = useState<number>(0)
5656

57-
const [currentNode, setCurrentNode] = useState<GameNode | undefined>(() =>
58-
gameTree.getRoot(),
59-
)
57+
const [currentNode, setCurrentNode] = useState<GameNode>(gameTree.getRoot())
6058
const [orientation, setOrientation] = useState<'white' | 'black'>(
6159
config.player,
6260
)

0 commit comments

Comments
 (0)