Download index.html from api-ix/Paid: direct link, hf CLI and curl.
- Browser
- Download file 15.5 kB
-
https://huggingface.co/spaces/api-ix/Paid/resolve/main/index.html
- Command line
-
hf download hf://spaces/api-ix/Paid/index.html
-
curl -L -o index.html https://huggingface.co/spaces/api-ix/Paid/resolve/main/index.html
15.5 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Arcane Touch Analyzer</title> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/chessboard-js/1.0.0/chessboard-1.0.0.min.css"> | |
| <style> | |
| :root { | |
| --bg-color: #212121; | |
| --text-color: #e0e0e0; | |
| --accent-color: #4caf50; | |
| --highlight: #00e5ff; /* Cyan for Best Move */ | |
| --selected: #ffeb3b; /* Yellow for Selected Piece */ | |
| --panel-bg: #333; | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| background-color: var(--bg-color); | |
| color: var(--text-color); | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| margin: 0; | |
| padding: 10px; | |
| user-select: none; | |
| -webkit-tap-highlight-color: transparent; /* Removes blue tap box on Android */ | |
| } | |
| h1 { margin-bottom: 10px; text-align: center; font-size: 1.5rem; } | |
| .container { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 15px; | |
| justify-content: center; | |
| align-items: center; | |
| max-width: 900px; | |
| width: 100%; | |
| } | |
| /* --- BOARD STYLES --- */ | |
| #board { | |
| width: 100%; | |
| max-width: 380px; | |
| aspect-ratio: 1 / 1; | |
| margin: 0 auto; | |
| background-color: #303030; | |
| border: 3px solid #555; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: #777; | |
| touch-action: none; /* Prevents scrolling while touching board */ | |
| } | |
| /* --- HIGHLIGHT CLASSES --- */ | |
| .highlight-best { | |
| box-shadow: inset 0 0 3px 3px var(--highlight) ; | |
| background-color: rgba(0, 229, 255, 0.3) ; | |
| } | |
| .highlight-selected { | |
| box-shadow: inset 0 0 3px 3px var(--selected) ; | |
| background-color: rgba(255, 235, 59, 0.4) ; | |
| } | |
| /* --- TURN SWITCHER UI --- */ | |
| .turn-controls { | |
| display: flex; | |
| gap: 15px; | |
| justify-content: center; | |
| width: 100%; | |
| max-width: 380px; | |
| margin-bottom: 5px; | |
| } | |
| .turn-card { | |
| flex: 1; | |
| background: #444; | |
| padding: 8px; | |
| border-radius: 8px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 10px; | |
| cursor: pointer; | |
| border: 2px solid transparent; | |
| opacity: 0.5; | |
| transition: all 0.2s ease; | |
| } | |
| .turn-card img { height: 30px; } | |
| .turn-card span { font-weight: bold; font-size: 0.9em; } | |
| .turn-card.active { | |
| opacity: 1; | |
| background: #505050; | |
| border-color: var(--accent-color); | |
| box-shadow: 0 0 10px rgba(76, 175, 80, 0.3); | |
| } | |
| /* --- ANALYSIS PANEL --- */ | |
| .panel { | |
| width: 100%; | |
| max-width: 380px; | |
| background: var(--panel-bg); | |
| padding: 15px; | |
| border-radius: 8px; | |
| box-sizing: border-box; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 12px; | |
| } | |
| .best-move-box { | |
| background: #2a2a2a; | |
| padding: 15px; | |
| border-radius: 8px; | |
| text-align: center; | |
| border: 1px solid #555; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .loading-bar { | |
| position: absolute; | |
| bottom: 0; | |
| left: 0; | |
| height: 4px; | |
| background: var(--highlight); | |
| width: 0%; | |
| transition: width 0.1s linear; | |
| } | |
| .best-move-label { display: block; color: #888; font-size: 0.8em; margin-bottom: 5px; } | |
| .best-move-text { | |
| font-size: 1.8em; | |
| font-weight: bold; | |
| color: var(--highlight); | |
| font-family: monospace; | |
| letter-spacing: 2px; | |
| } | |
| .controls { display: flex; gap: 10px; margin-top: 5px; } | |
| button { | |
| flex: 1; | |
| padding: 12px; | |
| border: none; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| font-weight: bold; | |
| color: white; | |
| background-color: #555; | |
| font-size: 16px; | |
| } | |
| button:active { transform: scale(0.98); } | |
| #flipBtn { background-color: #008CBA; } | |
| #debug-log { display:none; color:red; background:#300; padding:10px; width:100%; font-size:12px;} | |
| </style> | |
| </head> | |
| <body> | |
| <h1>⚡ Arcane Touch Analyzer</h1> | |
| <div id="debug-log"></div> | |
| <div class="container"> | |
| <div class="turn-controls"> | |
| <div id="turn-w" class="turn-card active" onclick="forceTurn('w')"> | |
| <img src="https://chessboardjs.com/img/chesspieces/wikipedia/wK.png" alt="White"> | |
| <span>White</span> | |
| </div> | |
| <div id="turn-b" class="turn-card" onclick="forceTurn('b')"> | |
| <img src="https://chessboardjs.com/img/chesspieces/wikipedia/bK.png" alt="Black"> | |
| <span>Black</span> | |
| </div> | |
| </div> | |
| <div id="board"> | |
| <div style="text-align:center;">Loading...</div> | |
| </div> | |
| <div class="panel"> | |
| <div class="best-move-box"> | |
| <div id="loading-bar" class="loading-bar"></div> | |
| <span class="best-move-label">BEST MOVE (2s Limit)</span> | |
| <div id="best-move" class="best-move-text">...</div> | |
| <div style="display:flex; justify-content:space-between; margin-top:10px; font-size:0.9em; color:#aaa;"> | |
| <span>Eval: <span id="eval-score" style="color:white">0.00</span></span> | |
| <span id="engineStatus">Init...</span> | |
| </div> | |
| </div> | |
| <div class="controls"> | |
| <button id="resetBtn">Reset Board</button> | |
| <button id="flipBtn">Flip View</button> | |
| </div> | |
| </div> | |
| </div> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/chess.js/0.10.3/chess.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/chessboard-js/1.0.0/chessboard-1.0.0.min.js"></script> | |
| <script> | |
| window.onerror = function(msg) { $('#debug-log').show().text("Err: " + msg); }; | |
| const STOCKFISH_PATH = 'https://cdnjs.cloudflare.com/ajax/libs/stockfish.js/10.0.0/stockfish.js'; | |
| let board, game, engine; | |
| let selectedSquare = null; // Track tap moves | |
| $(document).ready(function() { initGame(); }); | |
| function initGame() { | |
| game = new Chess(); | |
| const config = { | |
| draggable: true, // Keep drag enabled | |
| position: 'start', | |
| onDragStart: onDragStart, | |
| onDrop: onDrop, | |
| onSnapEnd: onSnapEnd, | |
| pieceTheme: 'https://chessboardjs.com/img/chesspieces/wikipedia/{piece}.png' | |
| }; | |
| $('#board').empty(); | |
| board = Chessboard('board', config); | |
| $(window).resize(board.resize); | |
| // --- TAP TO MOVE LISTENER --- | |
| // We listen for clicks on squares (class starts with square-) | |
| $('#board').on('click', '[class^="square-"]', onSquareClick); | |
| initStockfish(); | |
| updateUI(); | |
| } | |
| // --- TAP TO MOVE LOGIC --- | |
| function onSquareClick() { | |
| // Get the square ID (e.g., 'e2') from the data attribute | |
| const square = $(this).attr('data-square'); | |
| const piece = game.get(square); | |
| const turn = game.turn(); | |
| // 1. If nothing is selected yet | |
| if (selectedSquare === null) { | |
| // Only select if it's a piece of the current turn's color | |
| if (piece && piece.color === turn) { | |
| selectedSquare = square; | |
| highlightSelected(square); | |
| } | |
| return; | |
| } | |
| // 2. If something IS selected, try to move there | |
| const move = game.move({ | |
| from: selectedSquare, | |
| to: square, | |
| promotion: 'q' // Always promote to queen for simplicity | |
| }); | |
| // If move is valid | |
| if (move) { | |
| board.position(game.fen()); | |
| selectedSquare = null; | |
| removeSelectionHighlights(); | |
| updateUI(); | |
| analyzePosition(); | |
| } | |
| // If move is invalid | |
| else { | |
| // If they clicked a piece of their own color, switch selection | |
| if (piece && piece.color === turn) { | |
| selectedSquare = square; | |
| highlightSelected(square); | |
| } else { | |
| // Clicked empty square or enemy piece (but invalid move) -> Deselect | |
| selectedSquare = null; | |
| removeSelectionHighlights(); | |
| } | |
| } | |
| } | |
| // --- VISUAL HIGHLIGHTS --- | |
| function highlightBestMove(move) { | |
| $('#board .square-55d63').removeClass('highlight-best'); | |
| let source = move.substring(0, 2); | |
| let target = move.substring(2, 4); | |
| $('#board .square-' + source).addClass('highlight-best'); | |
| $('#board .square-' + target).addClass('highlight-best'); | |
| } | |
| function highlightSelected(square) { | |
| removeSelectionHighlights(); | |
| $('#board .square-' + square).addClass('highlight-selected'); | |
| } | |
| function removeSelectionHighlights() { | |
| $('#board .square-55d63').removeClass('highlight-selected'); | |
| } | |
| function removeBestHighlights() { | |
| $('#board .square-55d63').removeClass('highlight-best'); | |
| } | |
| // --- DRAG LOGIC (Keep sync with Tap) --- | |
| function onDragStart(source, piece) { | |
| if (game.game_over()) return false; | |
| if ((game.turn() === 'w' && piece.search(/^b/) !== -1) || | |
| (game.turn() === 'b' && piece.search(/^w/) !== -1)) { | |
| return false; | |
| } | |
| // If dragging, clear any tap selection | |
| selectedSquare = null; | |
| removeSelectionHighlights(); | |
| } | |
| function onDrop(source, target) { | |
| const move = game.move({ | |
| from: source, | |
| to: target, | |
| promotion: 'q' | |
| }); | |
| if (move === null) return 'snapback'; | |
| updateUI(); | |
| analyzePosition(); | |
| } | |
| function onSnapEnd() { | |
| board.position(game.fen()); | |
| } | |
| // --- TURN LOGIC --- | |
| function forceTurn(color) { | |
| if (game.turn() === color) return; | |
| let fen = game.fen(); | |
| let parts = fen.split(' '); | |
| parts[1] = color; | |
| parts[3] = '-'; | |
| let newFen = parts.join(' '); | |
| if(game.load(newFen)) { | |
| board.position(newFen); | |
| selectedSquare = null; | |
| removeSelectionHighlights(); | |
| updateUI(); | |
| analyzePosition(); | |
| } | |
| } | |
| function updateUI() { | |
| $('.turn-card').removeClass('active'); | |
| if (game.turn() === 'w') $('#turn-w').addClass('active'); | |
| else $('#turn-b').addClass('active'); | |
| } | |
| // --- ENGINE --- | |
| function initStockfish() { | |
| try { | |
| const blob = new Blob([`importScripts('${STOCKFISH_PATH}')`], {type: 'application/javascript'}); | |
| const url = URL.createObjectURL(blob); | |
| engine = new Worker(url); | |
| engine.onmessage = function(event) { | |
| const line = event.data; | |
| if (line === 'uciok') { | |
| $('#engineStatus').text('Ready').css('color', '#4caf50'); | |
| } | |
| if (line.startsWith('info') && line.includes('score cp')) { | |
| const matchScore = line.match(/score cp (-?\d+)/); | |
| const matchMate = line.match(/score mate (-?\d+)/); | |
| const matchPv = line.match(/ pv (.+)/); | |
| if (matchMate) { | |
| $('#eval-score').text(`M${Math.abs(matchMate[1])}`).css('color', '#ff5252'); | |
| } else if (matchScore) { | |
| const score = parseInt(matchScore[1]) / 100; | |
| const finalScore = game.turn() === 'b' ? -score : score; | |
| const sign = finalScore > 0 ? '+' : ''; | |
| $('#eval-score').text(`${sign}${finalScore}`).css('color', finalScore >= 0 ? '#4caf50' : '#ff5252'); | |
| } | |
| if (matchPv) { | |
| let moves = matchPv[1].split(' '); | |
| let bestMove = moves[0]; | |
| $('#best-move').html(`➜ ${bestMove}`); | |
| highlightBestMove(bestMove); | |
| } | |
| } | |
| if (line.startsWith('bestmove')) { | |
| $('#loading-bar').css('width', '0%'); | |
| $('#engineStatus').text('Done'); | |
| } | |
| }; | |
| engine.postMessage('uci'); | |
| engine.postMessage('isready'); | |
| } catch(e) { | |
| $('#engineStatus').text('Offline'); | |
| } | |
| } | |
| function analyzePosition() { | |
| $('#best-move').text('...'); | |
| removeBestHighlights(); | |
| $('#engineStatus').text('Thinking...'); | |
| $('#loading-bar').css('width', '0%').animate({width: '100%'}, 2000); | |
| if(engine) { | |
| engine.postMessage(`position fen ${game.fen()}`); | |
| engine.postMessage('go movetime 2000'); | |
| } | |
| } | |
| $('#resetBtn').click(function() { | |
| game.reset(); | |
| board.start(); | |
| selectedSquare = null; | |
| $('#best-move').text('...'); | |
| $('#eval-score').text('0.00'); | |
| removeSelectionHighlights(); | |
| removeBestHighlights(); | |
| updateUI(); | |
| }); | |
| $('#flipBtn').click(function() { | |
| board.flip(); | |
| // Re-apply highlights after flip because DOM is redrawn | |
| let currentText = $('#best-move').text(); | |
| if(currentText.includes('➜')) { | |
| let move = currentText.replace('➜ ', ''); | |
| highlightBestMove(move); | |
| } | |
| if(selectedSquare) { | |
| highlightSelected(selectedSquare); | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> | |