Skip to content

Commit

Permalink
feat: add blundermeter, highlight, moves by rating legend, eval bars,…
Browse files Browse the repository at this point in the history
… player stats
  • Loading branch information
kevinjosethomas committed Jan 22, 2025
1 parent dd1385f commit 029696c
Show file tree
Hide file tree
Showing 11 changed files with 344 additions and 250 deletions.
2 changes: 1 addition & 1 deletion src/components/Analysis/BlunderMeter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const BlunderMeter: React.FC<Props> = ({
goodMoveChance,
}: Props) => {
return (
<div className="flex flex-col gap-1 bg-background-1 p-4">
<div className="flex flex-col gap-1 bg-background-1">
<Tooltip id="probability" />
<p className="text-sm">
<span className="text-green-500">Good</span>{' '}
Expand Down
70 changes: 70 additions & 0 deletions src/components/Analysis/Highlight.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { MaiaEvaluation, StockfishEvaluation } from 'src/types'
import { BlunderMeter } from './BlunderMeter'

interface Props {
moveEvaluation: {
maia?: MaiaEvaluation
stockfish?: StockfishEvaluation
}
colorSanMapping: {
[move: string]: {
san: string
color: string
}
}
blunderMeter: {
blunderMoveChance: number
okMoveChance: number
goodMoveChance: number
}
}

export const Highlight: React.FC<Props> = ({
blunderMeter,
moveEvaluation,
colorSanMapping,
}: Props) => {
return (
<div className="grid h-full max-h-full w-full grid-cols-3 flex-col overflow-hidden rounded border-[0.5px] border-white/40 bg-background-1">
<div className="col-span-1 flex flex-col border-r-[0.5px] border-white/40">
<div className="flex flex-col gap-1 p-4">
<p className="text-xl font-semibold">Current Position</p>
<p className="text-sm text-secondary">
Maia predicts that Black will play{' '}
{moveEvaluation.maia
? colorSanMapping[Object.keys(moveEvaluation.maia.policy)[0]].san
: '...'}{' '}
next. This is a blunder.
</p>
</div>
<div className="grid grid-cols-2">
<div className="flex flex-col items-center justify-center bg-human-3/5 py-4">
<p className="text-sm text-human-2">Maia White Win %</p>
<p className="text-2xl font-bold text-human-1">
{moveEvaluation.maia
? `${Math.round(moveEvaluation.maia?.value * 1000) / 10}%`
: '...'}
</p>
</div>
<div className="flex flex-col items-center justify-center bg-engine-3/5 py-4">
<p className="text-sm text-engine-2">
SF Eval
{moveEvaluation.stockfish?.depth
? ` (D${moveEvaluation.stockfish?.depth})`
: ''}
</p>
<p className="text-2xl font-bold text-engine-1">
{moveEvaluation.stockfish
? `${moveEvaluation.stockfish.model_optimal_cp / 100 > 0 ? '+' : ''}${moveEvaluation.stockfish.model_optimal_cp / 100}`
: '...'}
</p>
</div>
</div>
<div className="flex flex-col p-4">
<BlunderMeter {...blunderMeter} />
</div>
</div>
<div className="col-span-2 flex flex-col"></div>
</div>
)
}
4 changes: 2 additions & 2 deletions src/components/Analysis/HorizontalEvaluationBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ export const HorizontalEvaluationBar: React.FC<Props> = ({
width = Math.max(0, Math.min(100, width))

return (
<div className="relative flex h-6 w-[60vh] max-w-[70vw] flex-col justify-center overflow-hidden rounded-sm bg-engine-3/30">
<div className="relative flex h-6 w-[calc(60vh-0.25rem)] max-w-[70vw] flex-col justify-center overflow-hidden bg-engine-3/30">
<p className="z-10 ml-2 whitespace-nowrap text-xs">{label}</p>
<div
className="absolute bottom-0 left-0 z-0 h-full w-full transform rounded-r-sm bg-engine-3 duration-300"
className="absolute left-0 top-0 z-0 h-full w-full transform bg-engine-3 duration-300"
style={{ width: `${width}%` }}
/>
</div>
Expand Down
21 changes: 20 additions & 1 deletion src/components/Analysis/MoveMap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
CartesianGrid,
ResponsiveContainer,
} from 'recharts'
import type { DrawShape } from 'chessground/draw'

interface Props {
moveMap?: { move: string; x: number; y: number }[]
Expand All @@ -18,13 +19,26 @@ interface Props {
color: string
}
}

setHoverArrow: React.Dispatch<React.SetStateAction<DrawShape | null>>
}

export const MoveMap: React.FC<Props> = ({
moveMap,
colorSanMapping,
setHoverArrow,
}: Props) => {
console.log(moveMap)
const onMouseEnter = (move: string) => {
setHoverArrow({
orig: move.slice(0, 2) as any,

Check warning on line 33 in src/components/Analysis/MoveMap.tsx

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
dest: move.slice(2, 4) as any,

Check warning on line 34 in src/components/Analysis/MoveMap.tsx

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
brush: 'green',
modifiers: {
lineWidth: 10,
},
})
}

return (
<div className="flex h-full max-h-full flex-col overflow-hidden rounded bg-background-1/60">
<p className="p-4 text-lg text-white">Move Map</p>
Expand Down Expand Up @@ -143,6 +157,11 @@ export const MoveMap: React.FC<Props> = ({
<Cell
key={`cell-${entry.move}${index}`}
fill={colorSanMapping[entry.move].color || '#fff'}
onMouseEnter={() => onMouseEnter(entry.move)}
onMouseOutCapture={() => {
console.log('beep')
setHoverArrow(null)
}}
/>
))}
</Scatter>
Expand Down
31 changes: 29 additions & 2 deletions src/components/Analysis/MoveRecommendations.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { DrawShape } from 'chessground/draw'
interface Props {
recommendations: {
maia?: { move: string; prob: number }[]
Expand All @@ -9,12 +10,26 @@ interface Props {
color: string
}
}

setHoverArrow: React.Dispatch<React.SetStateAction<DrawShape | null>>
}

export const MoveRecommendations: React.FC<Props> = ({
recommendations,
colorSanMapping,
setHoverArrow,
}: Props) => {
const onMouseEnter = (move: string) => {
setHoverArrow({
orig: move.slice(0, 2) as any,

Check warning on line 24 in src/components/Analysis/MoveRecommendations.tsx

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
dest: move.slice(2, 4) as any,

Check warning on line 25 in src/components/Analysis/MoveRecommendations.tsx

View workflow job for this annotation

GitHub Actions / lint

Unexpected any. Specify a different type
brush: 'green',
modifiers: {
lineWidth: 10,
},
})
}

return (
<div className="col-span-2 grid h-full max-h-full grid-cols-2 flex-col overflow-hidden rounded">
<div className="flex flex-col gap-2 bg-background-1 p-5">
Expand All @@ -36,7 +51,13 @@ export const MoveRecommendations: React.FC<Props> = ({
color: colorSanMapping[move].color,
}}
>
<p className="font-mono">{colorSanMapping[move].san}</p>
<p
className="cursor-default font-mono hover:underline"
onMouseEnter={() => onMouseEnter(move)}
onMouseOutCapture={() => setHoverArrow(null)}
>
{colorSanMapping[move].san}
</p>
<p className="font-mono text-sm">
{Math.round(prob * 1000) / 10}%
</p>
Expand All @@ -63,7 +84,13 @@ export const MoveRecommendations: React.FC<Props> = ({
color: colorSanMapping[move].color,
}}
>
<p className="font-mono">{colorSanMapping[move].san}</p>
<p
className="cursor-default font-mono hover:underline"
onMouseEnter={() => onMouseEnter(move)}
onMouseOutCapture={() => setHoverArrow(null)}
>
{colorSanMapping[move].san}
</p>
<p className="font-mono text-sm">{cp / 100}</p>
</div>
))}
Expand Down
63 changes: 59 additions & 4 deletions src/components/Analysis/MovesByRating.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ResponsiveContainer,
XAxis,
YAxis,
Tooltip,
} from 'recharts'

interface Props {
Expand Down Expand Up @@ -77,6 +78,34 @@ export const MovesByRating: React.FC<Props> = ({
tickLine={false}
tickFormatter={(value) => `${value}%`}
/>
<defs>
{moves &&
Object.keys(moves[0]).map((move, i) => {
if (move === 'rating') {
return null
}
return (
<linearGradient

Check failure on line 88 in src/components/Analysis/MovesByRating.tsx

View workflow job for this annotation

GitHub Actions / lint

Missing "key" prop for element in iterator
id={`color${move}`}
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop
offset="5%"
stopColor={colorSanMapping[move].color}
stopOpacity={0.5}
/>
<stop
offset="95%"
stopColor={colorSanMapping[move].color}
stopOpacity={0}
/>
</linearGradient>
)
})}
</defs>
{moves &&
Object.keys(moves[0]).map((move, i) => {
if (move === 'rating') {
Expand All @@ -88,21 +117,47 @@ export const MovesByRating: React.FC<Props> = ({
yAxisId="left"
dataKey={move}
dot={{
r: 3,
stroke: colorSanMapping[move].color,
strokeWidth: 1,
strokeWidth: 3,
}}
stroke={colorSanMapping[move].color}
fill={colorSanMapping[move].color}
fillOpacity={0.1}
fill={`url(#color${move})`}
strokeWidth={3}
/>
)
})}
<Tooltip
content={({ payload }) => {
return (
<div className="flex w-32 flex-col rounded border border-white/10 bg-background-1 pb-2">
<div className="flex px-3 py-2">
{payload ? (
<p className="text-sm">{payload[0]?.payload.rating}</p>
) : null}
</div>
{payload?.map((point) => {
const san = colorSanMapping[point.name as string].san
const prob = Math.round((point.value as number) * 10) / 10
return (
<div className="flex items-center justify-between px-3">

Check failure on line 143 in src/components/Analysis/MovesByRating.tsx

View workflow job for this annotation

GitHub Actions / lint

Missing "key" prop for element in iterator
<p className="text-xs">{san}</p>
<p className="font-mono text-xs">{prob}%</p>
</div>
)
})}
</div>
)
}}
/>
<Legend
verticalAlign="top"
align="right"
verticalAlign="top"
wrapperStyle={{ top: -14, right: 20, fontSize: 14 }}
iconSize={0}
formatter={(value) => {
return colorSanMapping[value as string].san
}}
/>
</AreaChart>
</ResponsiveContainer>
Expand Down
2 changes: 1 addition & 1 deletion src/components/Analysis/VerticalEvaluationBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const VerticalEvaluationBar: React.FC<Props> = ({
const height = ((value ?? min - min) / (max - min)) * 100

return (
<div className="relative flex h-[60vh] max-h-[70vw] w-6 flex-col justify-end overflow-hidden rounded-sm bg-human-3/30">
<div className="relative flex h-[calc(60vh-0.25rem)] max-h-[70vw] w-6 flex-col justify-end overflow-hidden bg-human-3/30">
<p className="z-10 mb-3 -rotate-90 whitespace-nowrap text-xs">{label}</p>
<div
className="absolute bottom-0 left-0 z-0 h-full w-full transform rounded-t-sm bg-human-3 duration-300"
Expand Down
3 changes: 2 additions & 1 deletion src/components/Analysis/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
export * from './MoveMap'
export * from './MovePlot'
export * from './Highlight'
export * from './Tournament'
export * from './BlunderMeter'
export * from './UserGameList'
export * from './AnalysisGameList'
export * from './HorizontalEvaluationBar'
export * from './PositionEvaluationContainer'
export * from './VerticalEvaluationBar'
export * from './MovesByRating'
export * from './MoveRecommendations'
export * from './MovesByRating'
6 changes: 5 additions & 1 deletion src/components/Board/GameBoard.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { defaults } from 'chessground/state'
import { useCallback, useContext } from 'react'
import type { DrawShape } from 'chessground/draw'
import Chessground from '@react-chess/chessground'
import type { DrawBrush, DrawBrushes, DrawShape } from 'chessground/draw'

import { BaseGame, Check } from 'src/types'
import { GameControllerContext } from 'src/contexts'
Expand All @@ -17,6 +18,7 @@ interface Props {
check?: Check
}
shapes?: DrawShape[]
brushes?: DrawBrushes
}

export const GameBoard: React.FC<Props> = ({
Expand All @@ -26,6 +28,7 @@ export const GameBoard: React.FC<Props> = ({
setCurrentMove,
setCurrentSquare,
shapes,
brushes,
}: Props) => {
const { currentIndex, orientation } = useContext(GameControllerContext)

Expand Down Expand Up @@ -56,6 +59,7 @@ export const GameBoard: React.FC<Props> = ({
},
drawable: {
autoShapes: shapes || [],
brushes: { ...defaults().drawable.brushes, ...brushes },
},
fen: move ? move.fen : game.moves[currentIndex]?.board,
lastMove: move
Expand Down
Loading

0 comments on commit 029696c

Please sign in to comment.