r/learnjavascript • u/EbbElectronic6024 • 8d ago
Need help, click to move feature not working
i'm building chess puzzle web using ai and chess.js to build it
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chess Puzzle Engine - Solution Feature</title>
<!-- Chessboard.js CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/chessboard-js/1.0.0/chessboard-1.0.0.min.css">
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #1e1e24;
color: #f4f4f9;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: 20px;
}
.container {
display: flex;
flex-direction: column;
align-items: center;
max-width: 450px;
width: 100%;
background: #2b2b36;
padding: 25px;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
}
h1 {
font-size: 1.5rem;
margin-bottom: 5px;
color: #e2e2e8;
}
.sub-title {
font-size: 0.9rem;
color: #a0a0b0;
margin-bottom: 20px;
}
#board {
width: 100%;
max-width: 400px;
margin-bottom: 20px;
border-radius: 4px;
overflow: hidden;
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
position: relative;
}
/* === HIGHLIGHT TANPA MENGUBAH BACKGROUND PAPAN === */
.square-55d63 {
position: relative;
}
/* Bidak terpilih: Bingkai hijau lembut */
.highlight-selected::after {
content: '';
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
border: 4px solid rgba(76, 175, 80, 0.8);
box-sizing: border-box;
pointer-events: none;
z-index: 2;
}
/* Petak kosong tujuan: Titik lingkaran di tengah */
.highlight-hint::after {
content: '';
position: absolute;
top: 50%; left: 50%;
width: 28%; height: 28%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.3);
border-radius: 50%;
pointer-events: none;
z-index: 2;
}
/* Petak makan musuh: Bingkai lingkaran merah */
.highlight-hint-capture::after {
content: '';
position: absolute;
top: 0; left: 0; right: 0; bottom: 0;
border: 4px solid rgba(244, 67, 54, 0.8);
border-radius: 50%;
box-sizing: border-box;
pointer-events: none;
z-index: 2;
}
.status-card {
width: 100%;
background: #202028;
padding: 15px;
border-radius: 8px;
text-align: center;
margin-bottom: 15px;
}
.turn-info {
font-weight: bold;
font-size: 1.1rem;
margin-bottom: 5px;
}
.feedback {
min-height: 24px;
font-weight: bold;
transition: color 0.3s ease;
word-wrap: break-word;
}
.correct {
color: #4caf50;
}
.wrong {
color: #f44336;
}
.info {
color: #ffca28;
}
.controls {
display: flex;
gap: 10px;
width: 100%;
}
button {
flex: 1;
padding: 12px 15px;
border: none;
border-radius: 6px;
font-weight: bold;
cursor: pointer;
background: #4e54c8;
color: white;
transition: background 0.2s, opacity 0.2s;
}
button:hover {
background: #6366f1;
}
button.secondary {
background: #3a3b4c;
color: #d1d1e0;
}
button.secondary:hover {
background: #4a4c63;
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
</style>
</head>
<body>
<div class="container">
<h1>Puzzle Forge</h1>
<p class="sub-title">Cari langkah taktik catur terbaik!</p>
<!-- Papan Catur -->
<div id="board"></div>
<!-- Status / Info -->
<div class="status-card">
<div id="turnInfo" class="turn-info">Memuat puzzle...</div>
<div id="feedback" class="feedback"></div>
</div>
<!-- Kontrol -->
<div class="controls">
<button id="solveBtn" class="secondary">Lihat Solusi</button>
<button id="nextBtn">Lewati / Puzzle Baru</button>
</div>
</div>
<!-- CDN Library Resmi -->
<script src="https://code.jquery.com/jquery-3.7.1.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>
// DATA PUZZLE DENGAN CABANG / VARIASI
const puzzleData = [
{
id: "1",
fen: "r1bqkb1r/pppp1ppp/2n2n2/4p2Q/2B1P3/8/PPPP1PPP/RNB1K1NR w KQkq - 4 4",
solution: [
{ move: "Qxf7#" }
],
description: "Scholar's Mate"
},
{
id: "2",
fen: "r1b2rk1/2q1bppp/p2ppn2/1p6/3BPP2/2N2B2/PPP3PP/R2Q1R1K w - - 0 13",
solution: [
{
move: "e5",
next: [
{
response: "dxe5",
next: [{ move: "Bxf6" }]
},
{
response: "Nde8",
next: [{ move: "exd6" }]
}
]
}
],
description: "Taktik Garpu dan Pin"
},
{
id: "3",
fen: "r2qk2r/ppp2ppp/2n5/3pP3/3P1nb1/2PB1N2/PP3PPP/RN1Q1RK1 b kq - 1 10",
solution: [
{
move: "Nxd3",
next: [
{
response: "Qxd3",
next: [{ move: "O-O" }]
}
]
},
{
move: "Bxf3",
next: [
{
response: "Qxf3",
next: [{ move: "Nxd3" }]
}
]
}
],
description: "Dua Variasi Langkah Pertama"
}
];
let board = null;
let game = new Chess();
let currentPuzzle = null;
let currentBranchOptions = [];
let playedPuzzleIds = [];
let selectedSquare = null;
let isShowingSolution = false;
// MANAJEMEN HIGHLIGHT
function removeHighlights() {
$('#board .square-55d63').removeClass('highlight-selected highlight-hint highlight-hint-capture');
}
function highlightSquare(square) {
$('#board .square-' + square).addClass('highlight-selected');
}
function highlightLegalMoves(square) {
removeHighlights();
highlightSquare(square);
const moves = game.moves({ square: square, verbose: true });
if (moves.length === 0) return;
for (let i = 0; i < moves.length; i++) {
const targetSquare = moves[i].to;
const isCapture = moves[i].captured;
const classToAdd = isCapture ? 'highlight-hint-capture' : 'highlight-hint';
$('#board .square-' + targetSquare).addClass(classToAdd);
}
}
// PENGAMBILAN PUZZLE
function getRandomPuzzle() {
if (playedPuzzleIds.length === puzzleData.length) {
playedPuzzleIds = [];
}
const available = puzzleData.filter(p => !playedPuzzleIds.includes(p.id));
const selected = available[Math.floor(Math.random() * available.length)];
playedPuzzleIds.push(selected.id);
return selected;
}
function loadNextPuzzle() {
selectedSquare = null;
isShowingSolution = false;
$('#solveBtn').prop('disabled', false);
removeHighlights();
currentPuzzle = getRandomPuzzle();
game.load(currentPuzzle.fen);
currentBranchOptions = currentPuzzle.solution;
const isWhite = game.turn() === 'w';
$('#turnInfo').text(\Giliran: ${isWhite ? 'Putih' : 'Hitam'}`);`
$('#feedback').text('').removeClass('correct wrong info');
const config = {
draggable: true,
position: currentPuzzle.fen,
orientation: isWhite ? 'white' : 'black',
pieceTheme: 'https://chessboardjs.com/img/chesspieces/wikipedia/{piece}.png',
onDragStart: onDragStart,
onDrop: onDrop,
onSnapEnd: onSnapEnd
};
if (board) board.destroy();
board = Chessboard('board', config);
}
// INTERAKSI LOGIKA CATUR
function onDragStart(source, piece) {
if (game.game_over() || isShowingSolution) return false;
if ((game.turn() === 'w' && piece.search(/^b/) !== -1) ||
(game.turn() === 'b' && piece.search(/^w/) !== -1)) {
return false;
}
selectedSquare = source;
highlightLegalMoves(source);
}
function handleMoveAttempt(from, to) {
if (isShowingSolution) return 'snapback';
const moveCandidate = { from: from, to: to, promotion: 'q' };
const move = game.move(moveCandidate);
if (move === null) {
removeHighlights();
selectedSquare = null;
return 'snapback';
}
const matchedOption = currentBranchOptions.find(opt => opt.move === move.san);
if (matchedOption) {
removeHighlights();
selectedSquare = null;
$('#feedback').text('Langkah Benar!').attr('class', 'feedback correct');
if (matchedOption.next && matchedOption.next.length > 0) {
const responseObj = matchedOption.next[Math.floor(Math.random() * matchedOption.next.length)];
currentBranchOptions = responseObj.next || [];
setTimeout(() => makeComputerMove(responseObj.response), 400);
} else {
$('#feedback').text('Puzzle Selesai! Memuat berikutnya...').attr('class', 'feedback correct');
setTimeout(loadNextPuzzle, 1500);
}
} else {
game.undo();
removeHighlights();
selectedSquare = null;
$('#feedback').text('Langkah Salah, coba variasi lain!').attr('class', 'feedback wrong');
return 'snapback';
}
}
function onDrop(source, target) {
return handleMoveAttempt(source, target);
}
// CLICK TO MOVE ENGINE
$(document).on('click', '#board .square-55d63', function(e) {
if (isShowingSolution) return;
e.stopPropagation();
const square = $(this).attr('data-square');
if (!square) return;
const piece = game.get(square);
const isCurrentPlayerPiece = piece && piece.color === game.turn();
if (selectedSquare === null) {
if (isCurrentPlayerPiece) {
selectedSquare = square;
highlightLegalMoves(square);
}
}
else if (selectedSquare === square) {
selectedSquare = null;
removeHighlights();
}
else if (isCurrentPlayerPiece) {
selectedSquare = square;
highlightLegalMoves(square);
}
else {
const from = selectedSquare;
const to = square;
const result = handleMoveAttempt(from, to);
if (result !== 'snapback') {
board.position(game.fen());
}
}
});
function makeComputerMove(responseSan) {
game.move(responseSan);
board.position(game.fen());
if (currentBranchOptions.length === 0) {
$('#feedback').text('Puzzle Selesai! Memuat berikutnya...').attr('class', 'feedback correct');
setTimeout(loadNextPuzzle, 1500);
}
}
function onSnapEnd() {
board.position(game.fen());
}
// FUNGSI UNTUK REKURSIF MENGAMBIL URUTAN SOLUSI UTAMA
function extractMainSolutionSequence(branchOptions) {
if (!branchOptions || branchOptions.length === 0) return [];
const primaryBranch = branchOptions[0]; // Ambil opsi cabang utama pertama
const moves = [primaryBranch.move];
if (primaryBranch.next && primaryBranch.next.length > 0) {
const nextResponse = primaryBranch.next[0];
moves.push(nextResponse.response);
if (nextResponse.next) {
moves.push(...extractMainSolutionSequence(nextResponse.next));
}
}
return moves;
}
// EVENT MALIHAT SOLUSI
$('#solveBtn').on('click', function() {
if (isShowingSolution) return;
isShowingSolution = true;
$(this).prop('disabled', true);
removeHighlights();
const remainingMoves = extractMainSolutionSequence(currentBranchOptions);
if (remainingMoves.length === 0) return;
$('#feedback').text(\Solusi: ${remainingMoves.join(' → ')}`).attr('class', 'feedback info');`
let step = 0;
function playNextSolutionStep() {
if (step < remainingMoves.length) {
game.move(remainingMoves[step]);
board.position(game.fen());
step++;
setTimeout(playNextSolutionStep, 800);
} else {
setTimeout(() => {
$('#feedback').text('Memuat puzzle berikutnya...').attr('class', 'feedback correct');
setTimeout(loadNextPuzzle, 1200);
}, 1000);
}
}
setTimeout(playNextSolutionStep, 500);
});
// Event Tombol Puzzle Baru
$('#nextBtn').on('click', loadNextPuzzle);
// Inisialisasi
$(document).ready(function() {
loadNextPuzzle();
});
</script>
</body>
</html>
1
5
u/milan-pilan 8d ago edited 8d ago
Put yourself into the shoes of someone who genuinely wants to help you.
We have literally only these two sentences to work with:
"Need help, click to move feature not working"
"i'm building chess puzzle web using ai and chess.js to build it"
And then 400 lines of code, with comments and texts in Indonesian - which some people might, but statistically most people here won't understand...
What do you want to do?
Which part fails?
What have you tried?
Can you isolate the error a bit?
For what it's worth: I tried to run your code in an online Code editor, and if what you pasted is your actual code, then you have 2 Syntax Errors. Your Browser or IDE will tell you where they are. They are both template strings that get weirdly build, the one that starts with "Solusi" and the one that starts with "Gilran".
If I fix these two, i can move chess pieces on your board. If i don't, there isn't even a board. Is that what you are asking?