extremely claude-assisted go game based on atproto! working on cleaning up and giving a more unique design, still has a bit of a slop vibe to it.
Something went wrong. Try again.
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153/** * Simple SGF parser for extracting moves from SGF files and puzzle positions */
export interface SgfMove { color: 'black' | 'white'; x: number; y: number;}
export interface SgfData { boardSize: number; moves: SgfMove[];}
export interface SgfPuzzle { number: number; comment: string; blackStones: Array<{ x: number; y: number }>; whiteStones: Array<{ x: number; y: number }>; boardSize: number; solution?: SgfMove[];}
/** * Parse SGF content and extract board size and moves */export function parseSgf(sgfContent: string): SgfData { const moves: SgfMove[] = []; let boardSize = 19; // default
// Extract board size const sizeMatch = sgfContent.match(/SZ\[(\d+)\]/); if (sizeMatch) { boardSize = parseInt(sizeMatch[1], 10); }
// Extract moves - pattern like ;B[dd] or ;W[ed] const movePattern = /;([BW])\[([a-z]{0,2})\]/g; let match;
while ((match = movePattern.exec(sgfContent)) !== null) { const color = match[1] === 'B' ? 'black' : 'white'; const coords = match[2];
// Empty brackets mean pass if (!coords) { continue; }
// Convert SGF coordinates (aa = top-left) to 0-based x,y const x = coords.charCodeAt(0) - 97; // 'a' = 0 const y = coords.charCodeAt(1) - 97;
moves.push({ color, x, y }); }
return { boardSize, moves };}
/** * Load and parse an SGF file from a URL */export async function loadSgf(url: string): Promise<SgfData> { const response = await fetch(url); const content = await response.text(); return parseSgf(content);}
/** * Parse coordinates from SGF format (e.g., "be" -> {x: 1, y: 4}) */function parseCoords(coords: string): { x: number; y: number } { const x = coords.charCodeAt(0) - 97; // 'a' = 0 const y = coords.charCodeAt(1) - 97; return { x, y };}
/** * Extract all coordinates from a property (e.g., AB[be][bf][cb]) */function extractStones(propertyMatch: string): Array<{ x: number; y: number }> { const stones: Array<{ x: number; y: number }> = []; const coordPattern = /\[([a-z]{2})\]/g; let match;
while ((match = coordPattern.exec(propertyMatch)) !== null) { stones.push(parseCoords(match[1])); }
return stones;}
/** * Parse a multi-problem SGF file into individual puzzles * Format: (;AB[...][...]AW[...][...]C[problem X]) */export function parseMultiProblemSgf(sgfContent: string): SgfPuzzle[] { const puzzles: SgfPuzzle[] = []; let boardSize = 19; // default
// Extract board size if specified const sizeMatch = sgfContent.match(/SZ\[(\d+)\]/); if (sizeMatch) { boardSize = parseInt(sizeMatch[1], 10); }
// Split into individual problems - each starts with (; // Match pattern: (;...AB[...]...AW[...]...C[problem X]...) const problemPattern = /\(;[^()]+\)/g; let match;
while ((match = problemPattern.exec(sgfContent)) !== null) { const problemStr = match[0];
// Extract black stones (AB property) const blackMatch = problemStr.match(/AB(\[[a-z]{2}\])+/); const blackStones = blackMatch ? extractStones(blackMatch[0]) : [];
// Extract white stones (AW property) const whiteMatch = problemStr.match(/AW(\[[a-z]{2}\])+/); const whiteStones = whiteMatch ? extractStones(whiteMatch[0]) : [];
// Extract comment (C property) const commentMatch = problemStr.match(/C\[([^\]]+)\]/); const comment = commentMatch ? commentMatch[1] : '';
// Extract problem number from comment const numberMatch = comment.match(/problem (\d+)/i); const number = numberMatch ? parseInt(numberMatch[1], 10) : puzzles.length + 1;
puzzles.push({ number, comment, blackStones, whiteStones, boardSize, solution: [] // Solution moves can be added later if needed }); }
return puzzles;}
/** * Load and parse a multi-problem SGF file from a URL */export async function loadPuzzleCollection(url: string): Promise<SgfPuzzle[]> { const response = await fetch(url); const content = await response.text(); return parseMultiProblemSgf(content);}