Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion src/constants/candidates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,48 @@ export const CANDIDATES_EXTRA_IDEAS: CandidateIdea[] = [
]

// Drop live tournament moments here as PGN or FEN during the event.
export const CANDIDATES_FEATURED_POSITIONS: CandidatePosition[] = []
export const CANDIDATES_FEATURED_POSITIONS: CandidatePosition[] = [
{
id: 'rd2-defend-like-hikaru',
title: 'Rd2 Challenge 1: Defend like Hikaru',
subtitle: 'Can you hold the position like Hikaru did?',
summary: 'Nakamura is under pressure. Black to move and defend accurately.',
tag: 'Featured',
accent: 'red',
fen: '2r3k1/2q2ppp/5n2/p1p1p3/4P2P/1PQ3P1/P4PB1/2R3K1 b - - 0 25',
playerColor: 'black',
maiaVersion: 'maia_kdd_1900',
targetMoveNumber: 8,
},
{
id: 'rd2-pragg-french',
title: "Rd2 Challenge 2: Hold off Pragg's French",
subtitle:
'Can you defend the White side after some inaccuracies in the French?',
summary:
'White to move in a tense French structure. Defend the position accurately.',
tag: 'Featured',
accent: 'amber',
fen: 'r1b2rk1/6pp/ppqbpn2/2pp4/3P1P2/2N1B3/PPPQB1PP/4RR1K b - - 1 16',
playerColor: 'white',
maiaVersion: 'maia_kdd_1800',
targetMoveNumber: 8,
},
{
id: 'rd2-be-like-bluebaum',
title: 'Rd2 Challenge 3: Be like Bluebaum',
subtitle:
'Bluebaum had less space, but held the draw anyway. Try to do the same.',
summary:
'Bluebaum-style defense and coordination. Black to move and find the right plan.',
tag: 'Featured',
accent: 'blue',
fen: '4rb1r/pp1k1pp1/2p1nnb1/3p2Np/3P3P/2P1NPP1/PP3K2/R1B2B1R b - - 3 19',
playerColor: 'black',
maiaVersion: 'maia_kdd_1900',
targetMoveNumber: 8,
},
]

// Warm-up cards keep the page useful before round one begins.
export const CANDIDATES_WARMUP_POSITIONS: CandidatePosition[] = [
Expand Down
4 changes: 3 additions & 1 deletion src/lib/positionLinks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ export const buildPositionPlayLink = (options: PositionLinkOptions): string => {
const params = new URLSearchParams()
const normalizedFen = normalizeFen(options.fen)
const forcedPlayerColor =
options.forcedPlayerColor ?? inferPlayerColorFromFen(normalizedFen)
options.forcedPlayerColor ??
options.playerColor ??
inferPlayerColorFromFen(normalizedFen)

params.set('fen', normalizedFen)
params.set('maiaVersion', 'maia_kdd_1500')
Expand Down
43 changes: 31 additions & 12 deletions src/pages/candidates.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ const accentClasses: Record<CandidatePosition['accent'], string> = {
const completedAccentClass =
'border-emerald-300/35 bg-[radial-gradient(circle_at_top,rgba(52,211,153,0.18),transparent_62%),rgba(255,255,255,0.03)]'

const ChallengeSectionTitle: React.FC<{ title: string }> = ({ title }) => (
<div className="md:col-span-2 xl:col-span-3">
<p className="text-sm uppercase tracking-[0.24em] text-white/40">{title}</p>
</div>
)

const PositionBoard: React.FC<{
position: CandidatePosition
completed?: boolean
Expand Down Expand Up @@ -184,10 +190,6 @@ export default function CandidatesPage() {
const [completedChallengeIds, setCompletedChallengeIds] = useState<string[]>(
[],
)
const positions = useMemo(
() => [...CANDIDATES_FEATURED_POSITIONS, ...CANDIDATES_WARMUP_POSITIONS],
[],
)
const completedChallengeId =
typeof router.query.completedChallenge === 'string'
? router.query.completedChallenge
Expand Down Expand Up @@ -242,7 +244,7 @@ export default function CandidatesPage() {
FIDE Candidates Tournament 2026
</h1>
<p className="mt-2 text-sm uppercase tracking-[0.2em] text-white/45">
Round 1
Round 2
</p>
<div className="mt-4 flex flex-wrap gap-3">
<Link
Expand All @@ -265,13 +267,30 @@ export default function CandidatesPage() {
</a>
</div>
</header>
{positions.map((position) => (
<PositionPill
key={position.id}
position={position}
completed={completedChallengeIds.includes(position.id)}
/>
))}
{CANDIDATES_FEATURED_POSITIONS.length > 0 ? (
<>
<ChallengeSectionTitle title="Round 2 Challenges" />
{CANDIDATES_FEATURED_POSITIONS.map((position) => (
<PositionPill
key={position.id}
position={position}
completed={completedChallengeIds.includes(position.id)}
/>
))}
</>
) : null}
{CANDIDATES_WARMUP_POSITIONS.length > 0 ? (
<>
<ChallengeSectionTitle title="Round 1 Challenges" />
{CANDIDATES_WARMUP_POSITIONS.map((position) => (
<PositionPill
key={position.id}
position={position}
completed={completedChallengeIds.includes(position.id)}
/>
))}
</>
) : null}
</div>
</main>
</>
Expand Down
Loading