Paid / index.html
api-ix's picture
Update index.html
0170cd2 verified
Raw
History Blame Contribute Delete
15.5 kB
<!DOCTYPE html>
<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) !important;
background-color: rgba(0, 229, 255, 0.3) !important;
}
.highlight-selected {
box-shadow: inset 0 0 3px 3px var(--selected) !important;
background-color: rgba(255, 235, 59, 0.4) !important;
}
/* --- 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>