const canvas = document.getElementById("board"); if (!canvas) alert("Canvas tidak ditemukan! ID harus 'board'"); const ctx = canvas.getContext("2d"); const CANVAS_W = canvas.width; const CANVAS_H = canvas.height; const size = 8; const tileSize = CANVAS_W / size; let board = []; let currentTurn = 1; // 1 = red, 2 = white let selected = null; let history = []; let gameOver = false; let aiEnabled = (typeof GAME_MODE !== "undefined" && GAME_MODE === "pvai"); let aiPlays = 2; let aiThinkingDelay = 300; let hintsEnabled = (typeof ENABLE_HINTS !== "undefined") ? !!ENABLE_HINTS : true; // SOUND EFFECTS const SFX = { move: new Audio("assets/sound/move.mp3"), capture: new Audio("assets/sound/capture.mp3") }; let soundEnabled = true; function playSound(name){ if(!soundEnabled || !SFX[name]) return; SFX[name].currentTime = 0; SFX[name].play().catch(()=>{}); } // TIMER PERMAINAN let timerSeconds = 0; let timerInterval = null; let timerRunning = false; function startTimer(){ if(timerRunning) return; timerRunning=true; timerInterval=setInterval(()=>{ timerSeconds++; const min=String(Math.floor(timerSeconds/60)).padStart(2,'0'); const sec=String(timerSeconds%60).padStart(2,'0'); const dom=document.getElementById('timer'); if(dom) dom.textContent = `${min}:${sec}`; },1000); } function stopTimer(){ timerRunning=false; clearInterval(timerInterval); } function resetTimer(){ stopTimer(); timerSeconds=0; const dom=document.getElementById('timer'); if(dom) dom.textContent='00:00'; } // PEMBANTU FUNGSI function opponentOf(p){ return p===1?2:1; } function inside(r,c){ return Number.isInteger(r) && Number.isInteger(c) && r>=0 && r=0 && cArray(size).fill(0)); for(let r=0;r<3;r++) for(let c=0;c triggerAI(), 120); } } // MENGGAMBAR PAPAN PADA CANVAS function draw(){ // MENGGAMBAR PAPAN ctx.clearRect(0,0,CANVAS_W,CANVAS_H); for(let r=0;r60) history.shift(); } // MEMBUAT GERAKAN function generateMovesForPiece(r,c,player){ const v = board[r][c]; if(v===0) return []; const isKing = (v===3||v===4); const dir = player===1? -1 : 1; const res = []; const dirs = isKing ? [[1,1],[1,-1],[-1,1],[-1,-1]] : [[dir,1],[dir,-1]]; for(const [dr,dc] of dirs){ const r2 = r+dr, c2 = c+dc; if(inside(r2,c2) && board[r2][c2]===0) res.push({r1:r,c1:c,r2:r2,c2:c2,capture:false}); } const capDirs = [[1,1],[1,-1],[-1,1],[-1,-1]]; for(const [dr,dc] of capDirs){ const mr = r+dr, mc = c+dc; const r2 = r+dr*2, c2 = c+dc*2; if(!inside(mr,mc) || !inside(r2,c2)) continue; if(board[r2][c2] !== 0) continue; const mid = board[mr][mc]; if(mid !== 0 && !belongsTo(mid, player)){ if(isKing || dr === dir) res.push({r1:r,c1:c,r2:r2,c2:c2,capture:true, mr, mc}); } } return res; } function getAllMovesFor(player){ const all = []; for(let r=0;r bestScore){ bestScore = s; bestMoves = [m]; } else if(s === bestScore) bestMoves.push(m); } const chosen = bestMoves[Math.floor(Math.random()*bestMoves.length)]; setTimeout(()=>{ tryMove(chosen.r1, chosen.c1, chosen.r2, chosen.c2); draw(); checkWinCondition(); }, aiThinkingDelay); } function triggerAI(){ setTimeout(()=>{ if(aiEnabled && currentTurn===aiPlays && !gameOver) aiMakeMove(); }, 120); } // MENGKLIK PADA KANVAS canvas.addEventListener("click", (e)=>{ console.debug("canvas click", {gameOver, aiEnabled, currentTurn}); if(gameOver) return; if(aiEnabled && currentTurn === aiPlays) { console.debug("click ignored: AI turn"); return; } const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left, y = e.clientY - rect.top; const c = Math.floor(x / tileSize), r = Math.floor(y / tileSize); console.debug("click coords", {x,y, r, c}); if(!inside(r,c)) return; const v = board[r][c]; if(selected){ if(tryMove(selected.r, selected.c, r, c)){ selected = null; } else if(v!==0 && belongsTo(v, currentTurn)){ selected = {r,c}; } else { selected = null; } } else { if(v!==0 && belongsTo(v, currentTurn)){ selected = {r,c}; } } draw(); }); // TOGGLE HINTS DENGAN 'H' document.addEventListener("keydown", (e)=>{ if(e.key.toLowerCase()==='h'){ hintsEnabled = !hintsEnabled; draw(); } }); // BUTTONS const newBtn = document.getElementById("newBtn"); if(newBtn) newBtn.onclick = ()=> resetBoard(); const undoBtn = document.getElementById("undoBtn"); if(undoBtn) undoBtn.onclick = ()=>{ if(history.length>0){ board = history.pop(); draw(); } }; // LEADERBOARD LOAD & SAVE async function loadLeaderboard(){ const box = document.getElementById("leaderboardList"); if(!box) return; try{ const res = await fetch("load_leaderboard.php"); if(!res.ok) throw new Error("Network"); const data = await res.json(); box.innerHTML = ""; if(!data || data.length===0){ box.innerHTML = "

Belum ada data

"; return; } data.forEach((row,i)=>{ const div = document.createElement("div"); div.className = "lb-row" + (typeof CURRENT_USERNAME!=='undefined' && row.username === CURRENT_USERNAME ? " me" : ""); div.innerHTML = `${i+1}. ${escapeHtml(row.username)}${row.wins}W | ${row.losses}L`; box.appendChild(div); }); }catch(err){ console.error("loadLeaderboard error", err); } } function escapeHtml(s){ if(!s) return ''; return String(s).replace(/[&<>"']/g,m=>({'&':'&','<':'<','>':'>','"':'"',"'":'''})[m]); } window.addEventListener("load", ()=>{ loadLeaderboard(); setInterval(loadLeaderboard,10000); }); async function saveResult(result){ if(typeof CURRENT_USER_ID === 'undefined') return; try{ const res = await fetch("save_result.php",{ method:'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({result}) }); if(res.ok) await loadLeaderboard(); }catch(err){ console.error("saveResult error", err); } } // END GAME POPUP function showEnd(title, msg, result){ if(document.getElementById("endPopup")) return; gameOver = true; stopTimer(); const div = document.createElement("div"); div.id = "endPopup"; div.style = `position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.75);display:flex;justify-content:center;align-items:center;z-index:999999999;pointer-events:auto;`; div.innerHTML = `

${title}

${msg}

`; document.body.appendChild(div); // save result async saveResult(result).catch(()=>{}); document.getElementById("popupNew").onclick = ()=>{ div.remove(); resetBoard(); }; document.getElementById("popupClose").onclick = ()=> div.remove(); } // START GAME resetBoard();