From 8d8cdc5974818103d9d75f99b2393fc8d140abe1 Mon Sep 17 00:00:00 2001 From: Angelica Date: Mon, 24 Nov 2025 16:34:29 +0700 Subject: [PATCH] menambahkan fitur --- Sudoku.html | 467 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 364 insertions(+), 103 deletions(-) diff --git a/Sudoku.html b/Sudoku.html index e838cc8..b05600e 100644 --- a/Sudoku.html +++ b/Sudoku.html @@ -6,26 +6,26 @@ :root { /*Warna Utama*/ --primary-color: dodgerblue; - + /*Warna Latar Belakang*/ --bg-color: whitesmoke; - + /*Warna Papan*/ --board-bg: white; - + /* Warna Garis */ --border-color: lightgray; --thick-border: darkslategray; - + /*Warna Teks*/ --text-color: black; - + /*Warna Sorot Angka Sama*/ - --same-highlight: lightblue; - + --same-highlight: lightblue; + /*Warna Kotak Terpilih*/ --selected-border: blue; - + /*Warna Error/Salah*/ --error-bg: pink; --error-text: red; @@ -57,93 +57,313 @@ animation: fadeIn 0.3s ease-in-out; } - .screen.active { display: flex; } + .screen.active { + display: flex; + } @keyframes fadeIn { - from { opacity: 0; transform: translateY(10px); } - to { opacity: 1; transform: translateY(0); } + from { + opacity: 0; + transform: translateY(10px); + } + + to { + opacity: 1; + transform: translateY(0); + } } /*MENU & LEVEL STYLES*/ - .logo-img { width: 120px; height: 120px; border-radius: 20px; margin-bottom: 15px; box-shadow: 0 4px 15px rgba(0,0,0,0.1); } - .app-title { font-size: 32px; font-weight: bold; color: darkslategray; margin-bottom: 40px; } - - .btn-main { - background-color: var(--primary-color); - color: white; - font-size: 18px; - font-weight: bold; - padding: 15px 0; - width: 250px; - border: none; - border-radius: 30px; - cursor: pointer; - box-shadow: 0 4px 10px lightgray; - transition: 0.2s; - margin-bottom: 15px; - text-align: center; + .logo-img { + width: 120px; + height: 120px; + border-radius: 20px; + margin-bottom: 15px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); + } + + .app-title { + font-size: 32px; + font-weight: bold; + color: darkslategray; + margin-bottom: 40px; + } + + .btn-main { + background-color: var(--primary-color); + color: white; + font-size: 18px; + font-weight: bold; + padding: 15px 0; + width: 250px; + border: none; + border-radius: 30px; + cursor: pointer; + box-shadow: 0 4px 10px lightgray; + transition: 0.2s; + margin-bottom: 15px; + text-align: center; + } + + .btn-main:hover { + transform: scale(1.05); + } + + .level-title { + font-size: 24px; + margin-bottom: 30px; + color: black; + } + + .btn-level { + width: 250px; + padding: 15px; + margin-bottom: 15px; + border: none; + border-radius: 15px; + font-size: 18px; + font-weight: bold; + color: white; + cursor: pointer; + transition: 0.2s; + box-shadow: 0 4px 6px lightgray; + } + + .btn-level:hover { + transform: translateY(-3px); } - .btn-main:hover { transform: scale(1.05); } - .level-title { font-size: 24px; margin-bottom: 30px; color: black; } - .btn-level { width: 250px; padding: 15px; margin-bottom: 15px; border: none; border-radius: 15px; font-size: 18px; font-weight: bold; color: white; cursor: pointer; transition: 0.2s; box-shadow: 0 4px 6px lightgray; } - .btn-level:hover { transform: translateY(-3px); } - /*Warna Tombol Level menggunakan HURUF*/ - .lvl-easy { background-color: limegreen; } - .lvl-medium { background-color: dodgerblue; } - .lvl-hard { background-color: red; } - - .btn-cancel { margin-top: 20px; background: none; border: 2px solid gray; color: gray; padding: 10px 40px; border-radius: 20px; cursor: pointer; font-weight: bold; } + .lvl-easy { + background-color: limegreen; + } + + .lvl-medium { + background-color: dodgerblue; + } + + .lvl-hard { + background-color: red; + } + + .btn-cancel { + margin-top: 20px; + background: none; + border: 2px solid gray; + color: gray; + padding: 10px 40px; + border-radius: 20px; + cursor: pointer; + font-weight: bold; + } /*GAME STYLES*/ - .game-header { width: 100%; max-width: 380px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; padding: 0 10px; box-sizing: border-box; } - .btn-back-icon { background: none; border: none; font-size: 28px; cursor: pointer; color: dimgray; padding: 0; } - #difficulty-label { font-size: 18px; font-weight: bold; color: var(--primary-color); text-transform: uppercase; letter-spacing: 1px; } + .game-header { + width: 100%; + max-width: 380px; + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; + padding: 0 10px; + box-sizing: border-box; + } + + .btn-back-icon { + background: none; + border: none; + font-size: 28px; + cursor: pointer; + color: dimgray; + padding: 0; + } + + #difficulty-label { + font-size: 18px; + font-weight: bold; + color: var(--primary-color); + text-transform: uppercase; + letter-spacing: 1px; + } + + #board-container { + box-shadow: 0 4px 15px lightgray; + border-radius: 8px; + overflow: hidden; + margin-bottom: 20px; + } + + #board { + width: 350px; + height: 350px; + background-color: var(--board-bg); + border: 2px solid var(--thick-border); + display: flex; + flex-wrap: wrap; + } + + .tile { + width: 38.4px; + height: 38.4px; + border: 1px solid var(--border-color); + font-size: 20px; + font-weight: 500; + display: flex; + justify-content: center; + align-items: center; + cursor: pointer; + user-select: none; + box-sizing: border-box; + } - #board-container { box-shadow: 0 4px 15px lightgray; border-radius: 8px; overflow: hidden; margin-bottom: 20px; } - #board { width: 350px; height: 350px; background-color: var(--board-bg); border: 2px solid var(--thick-border); display: flex; flex-wrap: wrap; } - - .tile { width: 38.4px; height: 38.4px; border: 1px solid var(--border-color); font-size: 20px; font-weight: 500; display: flex; justify-content: center; align-items: center; cursor: pointer; user-select: none; box-sizing: border-box; } - /*Garis Tebal menggunakan warna darkslategray*/ - .tile-border-right { border-right: 2px solid var(--thick-border); } - .tile-border-bottom { border-bottom: 2px solid var(--thick-border); } - - .tile-start { color: black; background-color: whitesmoke; cursor: default; font-weight: bold; } - .tile-user { color: var(--primary-color); } - .tile-highlight-same { background-color: var(--same-highlight) !important; } - .tile-selected { border: 3px solid var(--selected-border) !important; z-index: 10; } - .tile-error { background-color: var(--error-bg) !important; color: var(--error-text) !important; } + .tile-border-right { + border-right: 2px solid var(--thick-border); + } + + .tile-border-bottom { + border-bottom: 2px solid var(--thick-border); + } + + .tile-start { + color: black; + background-color: whitesmoke; + cursor: default; + font-weight: bold; + } + + .tile-user { + color: var(--primary-color); + } + + .tile-highlight-same { + background-color: var(--same-highlight) !important; + } + + .tile-selected { + border: 3px solid var(--selected-border) !important; + z-index: 10; + } + + .tile-error { + background-color: var(--error-bg) !important; + color: var(--error-text) !important; + } /*Numpad & Tools*/ - #numpad-row { display: flex; flex-wrap: nowrap; justify-content: space-between; width: 100%; max-width: 350px; gap: 5px; } - .number { flex: 1; height: 55px; border-radius: 8px; display: flex; flex-direction: column; justify-content: center; align-items: center; cursor: pointer; background-color: white; color: var(--primary-color); box-shadow: 0 2px 5px lightgray; transition: transform 0.1s; } - .number:active { transform: scale(0.95); background-color: aliceblue; } - .main-num { font-size: 22px; font-weight: bold; } - .count-num { font-size: 10px; color: gray; margin-top: 2px; } - .hidden-key { opacity: 0; pointer-events: none; } + #numpad-row { + display: flex; + flex-wrap: nowrap; + justify-content: space-between; + width: 100%; + max-width: 350px; + gap: 5px; + } + + .number { + flex: 1; + height: 55px; + border-radius: 8px; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + cursor: pointer; + background-color: white; + color: var(--primary-color); + box-shadow: 0 2px 5px lightgray; + transition: transform 0.1s; + } + + .number:active { + transform: scale(0.95); + background-color: aliceblue; + } + + .main-num { + font-size: 22px; + font-weight: bold; + } + + .count-num { + font-size: 10px; + color: gray; + margin-top: 2px; + } + + .hidden-key { + opacity: 0; + pointer-events: none; + } + + #tools-row { + margin-top: 15px; + width: 100%; + max-width: 350px; + display: flex; + justify-content: center; + gap: 15px; + } + + .tool-btn { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 12px 20px; + border-radius: 30px; + font-weight: bold; + font-size: 14px; + cursor: pointer; + border: 1px solid lightgray; + background-color: white; + box-shadow: 0 2px 4px lightgray; + transition: 0.2s; + flex: 1; + } + + .tool-btn:active { + transform: scale(0.95); + } - #tools-row { margin-top: 15px; width: 100%; max-width: 350px; display: flex; justify-content: center; gap: 15px; } - .tool-btn { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 12px 20px; border-radius: 30px; font-weight: bold; font-size: 14px; cursor: pointer; border: 1px solid lightgray; background-color: white; box-shadow: 0 2px 4px lightgray; transition: 0.2s; flex: 1; } - .tool-btn:active { transform: scale(0.95); } - /*Tombol Hapus*/ - .btn-delete { color: red; border-color: pink; } - - /*Tombol Bantuan*/ - .btn-hint { color: darkorange; border-color: orange; } + .btn-delete { + color: red; + border-color: pink; + } - #game-msg { height: 25px; color: green; font-weight: bold; margin-bottom: 10px; font-size: 16px; text-align: center;} + /*Tombol Bantuan*/ + .btn-hint { + color: darkorange; + border-color: orange; + } + + /* Style untuk tombol disabled (ketika bantuan habis) */ + .btn-disabled { + background-color: lightgray !important; + color: gray !important; + border-color: darkgray !important; + cursor: not-allowed; + box-shadow: none; + } + + #game-msg { + height: 25px; + color: green; + font-weight: bold; + margin-bottom: 10px; + font-size: 16px; + text-align: center; + } /*FITUR MENANG*/ #win-controls { - display: none; + display: none; margin-top: 15px; width: 100%; justify-content: center; } - + .btn-play-again { background-color: var(--primary-color); color: white; @@ -156,15 +376,25 @@ box-shadow: 0 4px 10px lightgray; animation: popIn 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); } - .btn-play-again:hover { background-color: deepskyblue; } - @keyframes popIn { - from { transform: scale(0); opacity: 0; } - to { transform: scale(1); opacity: 1; } + .btn-play-again:hover { + background-color: deepskyblue; } + @keyframes popIn { + from { + transform: scale(0); + opacity: 0; + } + + to { + transform: scale(1); + opacity: 1; + } + } +
@@ -175,9 +405,12 @@
Pilih Tingkat Kesulitan
- - - + + +
@@ -208,7 +441,7 @@
🗑️ HAPUS
-
💡 BANTUAN
+
💡 BANTUAN (3)
@@ -221,6 +454,7 @@ var tileSelected = null; var board = []; var solution = []; + var hintsRemaining = 3; // Variabel untuk menyimpan sisa bantuan function switchScreen(screenId) { document.querySelectorAll('.screen').forEach(s => s.classList.remove('active')); @@ -230,9 +464,9 @@ function showLevelScreen() { switchScreen('screen-level'); } function startLevel(difficulty) { - let holes = 30; + let holes = 30; let label = "MUDAH"; - if(difficulty === 'medium') { holes = 40; label = "MEDIUM"; } + if (difficulty === 'medium') { holes = 40; label = "MEDIUM"; } else if (difficulty === 'hard') { holes = 50; label = "SULIT"; } document.getElementById('difficulty-label').innerText = label; switchScreen('screen-game'); @@ -241,12 +475,16 @@ function newGame(holesCount) { solution = generateFullBoard(); - board = JSON.parse(JSON.stringify(solution)); - removeNumbers(board, holesCount); - - document.getElementById("tools-row").style.display = "flex"; - document.getElementById("numpad-row").style.display = "flex"; - document.getElementById("win-controls").style.display = "none"; + board = JSON.parse(JSON.stringify(solution)); + removeNumbers(board, holesCount); + + // Reset sisa bantuan ke 3 setiap game baru + hintsRemaining = 3; + updateHintButton(); + + document.getElementById("tools-row").style.display = "flex"; + document.getElementById("numpad-row").style.display = "flex"; + document.getElementById("win-controls").style.display = "none"; document.getElementById("game-msg").innerText = ""; setGame(); @@ -257,7 +495,7 @@ document.getElementById("board").innerHTML = ""; tileSelected = null; clearAllHighlights(); - + let digits = document.querySelectorAll(".number"); digits.forEach(digit => { digit.removeEventListener("click", selectNumber); @@ -281,12 +519,25 @@ } } + // Fungsi untuk update tampilan tombol bantuan + function updateHintButton() { + let btnHint = document.getElementById("btn-hint"); + btnHint.innerHTML = `💡 BANTUAN (${hintsRemaining})`; + + // Jika habis, ganti tampilan jadi disabled + if(hintsRemaining <= 0) { + btnHint.classList.add("btn-disabled"); + } else { + btnHint.classList.remove("btn-disabled"); + } + } + function updateRemainingCounts() { let counts = Array(10).fill(0); - for(let r=0; r<9; r++){ - for(let c=0; c<9; c++){ + for (let r = 0; r < 9; r++) { + for (let c = 0; c < 9; c++) { let val = board[r][c]; - if(val !== 0) counts[val]++; + if (val !== 0) counts[val]++; } } let digitButtons = document.querySelectorAll(".number"); @@ -294,25 +545,25 @@ let num = parseInt(btn.getAttribute("data-num")); let remaining = 9 - counts[num]; let countSpan = btn.querySelector(".count-num"); - if(countSpan) countSpan.innerText = remaining > 0 ? remaining : 0; - if(remaining <= 0) btn.classList.add("hidden-key"); + if (countSpan) countSpan.innerText = remaining > 0 ? remaining : 0; + if (remaining <= 0) btn.classList.add("hidden-key"); else btn.classList.remove("hidden-key"); }); } function clearAllHighlights() { - let sameNumTiles = document.querySelectorAll('.tile-highlight-same'); - sameNumTiles.forEach(t => t.classList.remove('tile-highlight-same')); + let sameNumTiles = document.querySelectorAll('.tile-highlight-same'); + sameNumTiles.forEach(t => t.classList.remove('tile-highlight-same')); } function highlightSameNumbers(numStr) { - clearAllHighlights(); + clearAllHighlights(); if (numStr === "") return; let allTiles = document.querySelectorAll('.tile'); allTiles.forEach(tile => { - if(tile.innerText === numStr && !tile.classList.contains("tile-error")) { - tile.classList.add('tile-highlight-same'); - } + if (tile.innerText === numStr && !tile.classList.contains("tile-error")) { + tile.classList.add('tile-highlight-same'); + } }); } @@ -337,7 +588,7 @@ let c = parseInt(coords[1]); let val = parseInt(numberValue); board[r][c] = val; - if (val !== solution[r][c]) { tileSelected.classList.add("tile-error"); } + if (val !== solution[r][c]) { tileSelected.classList.add("tile-error"); } else { tileSelected.classList.remove("tile-error"); } highlightSameNumbers(numberValue); updateRemainingCounts(); @@ -357,24 +608,34 @@ } function getHint() { + // Cek apakah sisa bantuan masih ada + if(hintsRemaining <= 0) return; + if (!tileSelected) return; if (tileSelected.classList.contains("tile-start")) return; + let coords = tileSelected.id.split("-"); let r = parseInt(coords[0]); let c = parseInt(coords[1]); let correctNum = solution[r][c]; + board[r][c] = correctNum; tileSelected.innerText = correctNum; tileSelected.classList.remove("tile-error"); + + // Kurangi sisa bantuan + hintsRemaining--; + updateHintButton(); // Update tampilan tombol + highlightSameNumbers(correctNum.toString()); updateRemainingCounts(); checkIfWin(); } function checkIfWin() { - for(let r=0; r<9; r++){ - for(let c=0; c<9; c++){ - if(board[r][c] !== solution[r][c]) return false; + for (let r = 0; r < 9; r++) { + for (let c = 0; c < 9; c++) { + if (board[r][c] !== solution[r][c]) return false; } } document.getElementById("game-msg").innerText = "SELAMAT! ANDA MENANG!";