// ============ AURA — Chess board + pipeline ============ const { useState: useStateA, useEffect: useEffectA, useMemo: useMemoA } = React; // Unicode pieces. Uppercase = white, lowercase = black. const PIECE_GLYPH = { 'K':'♔','Q':'♕','R':'♖','B':'♗','N':'♘','P':'♙', 'k':'♚','q':'♛','r':'♜','b':'♝','n':'♞','p':'♟' }; const INITIAL_BOARD = [ ['r','n','b','q','k','b','n','r'], ['p','p','p','p','p','p','p','p'], ['','','','','','','',''], ['','','','','','','',''], ['','','','','','','',''], ['','','','','','','',''], ['P','P','P','P','P','P','P','P'], ['R','N','B','Q','K','B','N','R'] ]; const isWhite = (p) => p && p === p.toUpperCase(); const isBlack = (p) => p && p === p.toLowerCase(); function inB(r, c) { return r >= 0 && r < 8 && c >= 0 && c < 8; } // Basic legal moves per piece (no check detection, no castling/en-passant). function pieceMoves(board, r, c) { const p = board[r][c]; if (!p) return []; const moves = []; const own = isWhite(p) ? isWhite : isBlack; const enemy = isWhite(p) ? isBlack : isWhite; const t = p.toLowerCase(); const slide = (dirs) => { for (const [dr, dc] of dirs) { let nr = r + dr, nc = c + dc; while (inB(nr, nc)) { if (!board[nr][nc]) moves.push([nr, nc]); else { if (enemy(board[nr][nc])) moves.push([nr, nc]); break; } nr += dr; nc += dc; } } }; if (t === 'p') { const dir = isWhite(p) ? -1 : 1; const startRow = isWhite(p) ? 6 : 1; if (inB(r + dir, c) && !board[r + dir][c]) { moves.push([r + dir, c]); if (r === startRow && !board[r + 2 * dir][c]) moves.push([r + 2 * dir, c]); } for (const dc of [-1, 1]) { const nr = r + dir, nc = c + dc; if (inB(nr, nc) && board[nr][nc] && enemy(board[nr][nc])) moves.push([nr, nc]); } } else if (t === 'n') { [[-2,-1],[-2,1],[-1,-2],[-1,2],[1,-2],[1,2],[2,-1],[2,1]].forEach(([dr, dc]) => { const nr = r + dr, nc = c + dc; if (inB(nr, nc) && (!board[nr][nc] || enemy(board[nr][nc]))) moves.push([nr, nc]); }); } else if (t === 'b') { slide([[-1,-1],[-1,1],[1,-1],[1,1]]); } else if (t === 'r') { slide([[-1,0],[1,0],[0,-1],[0,1]]); } else if (t === 'q') { slide([[-1,-1],[-1,1],[1,-1],[1,1],[-1,0],[1,0],[0,-1],[0,1]]); } else if (t === 'k') { for (let dr = -1; dr <= 1; dr++) for (let dc = -1; dc <= 1; dc++) { if (!dr && !dc) continue; const nr = r + dr, nc = c + dc; if (inB(nr, nc) && (!board[nr][nc] || enemy(board[nr][nc]))) moves.push([nr, nc]); } } return moves; } function allMoves(board, side) { // side: 'w' or 'b' const out = []; for (let r = 0; r < 8; r++) for (let c = 0; c < 8; c++) { const p = board[r][c]; if (!p) continue; if (side === 'w' && !isWhite(p)) continue; if (side === 'b' && !isBlack(p)) continue; for (const [nr, nc] of pieceMoves(board, r, c)) out.push({ from: [r, c], to: [nr, nc] }); } return out; } function applyMove(board, from, to) { const nb = board.map(row => row.slice()); let p = nb[from[0]][from[1]]; nb[from[0]][from[1]] = ''; // Auto-promote pawn to queen if (p === 'P' && to[0] === 0) p = 'Q'; if (p === 'p' && to[0] === 7) p = 'q'; nb[to[0]][to[1]] = p; return nb; } function ChessBoard() { const [board, setBoard] = useStateA(INITIAL_BOARD); const [turn, setTurn] = useStateA('w'); const [sel, setSel] = useStateA(null); const [last, setLast] = useStateA(null); // {from, to} const [status, setStatus] = useStateA('Tu turno'); const [depth, setDepth] = useStateA(3); const selectableMoves = useMemoA(() => { if (!sel) return []; return pieceMoves(board, sel[0], sel[1]); }, [board, sel]); // AURA move useEffectA(() => { if (turn !== 'b') return; setStatus('AURA pensando...'); const start = performance.now(); // simulate thinking const tm = setTimeout(() => { const moves = allMoves(board, 'b'); if (moves.length === 0) { setStatus('Sin movimientos. Empate o jaque mate.'); return; } // crude scoring: prefer captures const scored = moves.map(m => { const target = board[m.to[0]][m.to[1]]; const val = { p:1, n:3, b:3, r:5, q:9, k:100 }[target?.toLowerCase()] || 0; return { ...m, score: val + Math.random() * 0.5 }; }); scored.sort((a,b) => b.score - a.score); // pick top 3 randomly to add variety const pick = scored[Math.floor(Math.random() * Math.min(3, scored.length))]; const nb = applyMove(board, pick.from, pick.to); setBoard(nb); setLast({ from: pick.from, to: pick.to }); setTurn('w'); setStatus('Tu turno'); setDepth(d => 1 + Math.floor(Math.random() * 4)); }, 800 + Math.random() * 600); return () => clearTimeout(tm); }, [turn, board]); const onSq = (r, c) => { if (turn !== 'w') return; const p = board[r][c]; if (sel) { // try to move const valid = selectableMoves.some(([nr, nc]) => nr === r && nc === c); if (valid) { const nb = applyMove(board, sel, [r, c]); setBoard(nb); setLast({ from: sel, to: [r, c] }); setSel(null); setTurn('b'); return; } if (p && isWhite(p)) { setSel([r, c]); return; } setSel(null); } else if (p && isWhite(p)) { setSel([r, c]); } }; const reset = () => { setBoard(INITIAL_BOARD); setSel(null); setLast(null); setTurn('w'); setStatus('Tu turno'); }; return (
{status} vs AURA
{board.map((row, r) => row.map((p, c) => { const dark = (r + c) % 2 === 1; const isSel = sel && sel[0] === r && sel[1] === c; const isMove = selectableMoves.some(([nr, nc]) => nr === r && nc === c); const isCap = isMove && board[r][c]; const isLast = last && ((last.from[0] === r && last.from[1] === c) || (last.to[0] === r && last.to[1] === c)); return (
onSq(r, c)} role="button" aria-label={`${String.fromCharCode(97 + c)}${8 - r}${p ? ' ' + p : ''}`} > {p && {PIECE_GLYPH[p]}}
); }))}
depth {[1,2,3,4,5].map(i => )}
); } function Aura() { const { t } = window.useLang(); const ref = window.useReveal(); const photoRef = React.useRef(null); const onPhotoMove = (e) => { const el = photoRef.current; if (!el) return; const r = el.getBoundingClientRect(); const px = (e.clientX - r.left) / r.width - 0.5; const py = (e.clientY - r.top) / r.height - 0.5; el.style.transform = `perspective(1100px) rotateX(${-py * 3}deg) rotateY(${px * 4}deg)`; }; const onPhotoLeave = () => { if (photoRef.current) photoRef.current.style.transform = ''; }; const steps = [ { num: '1', icon: '📷', title: 'Captura', desc: 'Cámara sobre el tablero · 30 FPS' }, { num: '2', icon: '🎯', title: 'YOLO', desc: 'Detecta board + 32 piezas' }, { num: '3', icon: '🔤', title: 'FEN', desc: 'Notación serializada del estado' }, { num: '4', icon: '♟️', title: 'Stockfish', desc: 'Análisis · mejor jugada' }, { num: '5', icon: '🦾', title: 'Servo', desc: 'Ejecuta el movimiento físico' } ]; const tr = t.aura.trophy; return (
{t.aura.kicker}

{t.aura.h2}

{t.aura.sub}

{/* Top row: real robot photo + chess achievement */}
Prototipo de AURA en el laboratorio CAETI
{t.aura.photoCaption}
🥈
{tr.eyebrow}

{tr.title}

{tr.body}

{tr.participants.map((p, i) => ( {p} ))}
{tr.venue}
{/* Interactive board + pipeline */}
{steps.map((s, i) => (
{s.num}

{s.icon} {s.title}

{s.desc}

))}
{['YOLO','OpenCV','Stockfish','Python','ESP32','Arduino'].map((s,i) => ( {s} ))}
); } window.Aura = Aura;