Spaces:
Running
Running
upload index.html
Browse files- README.md +45 -4
- index.html +773 -18
README.md
CHANGED
|
@@ -1,10 +1,51 @@
|
|
| 1 |
---
|
| 2 |
title: Block Blast Solver
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
title: Block Blast Solver
|
| 3 |
+
emoji: π§©
|
| 4 |
+
colorFrom: green
|
| 5 |
+
colorTo: indigo
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
---
|
| 9 |
|
| 10 |
+
# Block Blast Solver
|
| 11 |
+
|
| 12 |
+
보λ μνμ 3κ° μ‘°κ° λͺ¨μμ 그리면, μμ νμ(λ°±νΈλνΉ)μΌλ‘ 3κ° μ‘°κ°μ λλ 6κ°μ§ μμ Γ λͺ¨λ μμΉλ₯Ό μ λΆ κ²μ¬ν΄μ
|
| 13 |
+
**μ€μ κ°μ₯ λ§μ΄ μ§μ°λ λ°°μΉ**λ₯Ό μ°Ύμμ£Όλ μ μ μΉμ±μ
λλ€. μλ²κ° μκ³ λͺ¨λ κ³μ°μ λΈλΌμ°μ (JS)μμ μ€νλ©λλ€.
|
| 14 |
+
|
| 15 |
+
## Hugging Face Spacesμ λ°°ν¬νλ λ°©λ² (무λ£)
|
| 16 |
+
|
| 17 |
+
1. https://huggingface.co/new-space μ μ β λ‘κ·ΈμΈ
|
| 18 |
+
2. Space name μ
λ ₯ (μ: `block-blast-solver`)
|
| 19 |
+
3. **License**: μνλ λλ‘ (μμ΄λ 무방)
|
| 20 |
+
4. **Space SDK**: **Static** μ ν (μ΄κ² μ€μ! Gradio/Streamlit μλ)
|
| 21 |
+
5. **Space hardware**: κΈ°λ³Έ(Free Β· CPU basic) κ·Έλλ‘ λλ©΄ λ©λλ€
|
| 22 |
+
6. Create Space ν΄λ¦
|
| 23 |
+
7. μμ±λ Space νμ΄μ§μμ "Files" ν β "Add file" β "Upload files"λ‘
|
| 24 |
+
μ΄ ν΄λ μμ `index.html`κ³Ό `README.md`λ₯Ό κ·Έλλ‘ μ
λ‘λ (λλ gitμΌλ‘ push)
|
| 25 |
+
8. 컀λ°νλ©΄ 1λΆ μ΄λ΄λ‘ `https://huggingface.co/spaces/{μ¬μ©μλͺ
}/{Spaceμ΄λ¦}` μ£Όμμμ λ°λ‘ μ¬μ© κ°λ₯
|
| 26 |
+
|
| 27 |
+
### gitμΌλ‘ μ¬λ¦¬λ κ²½μ°
|
| 28 |
+
```bash
|
| 29 |
+
git clone https://huggingface.co/spaces/{μ¬μ©μλͺ
}/{Spaceμ΄λ¦}
|
| 30 |
+
cp index.html README.md {Spaceμ΄λ¦}/
|
| 31 |
+
cd {Spaceμ΄λ¦}
|
| 32 |
+
git add .
|
| 33 |
+
git commit -m "block blast solver"
|
| 34 |
+
git push
|
| 35 |
+
```
|
| 36 |
+
|
| 37 |
+
## μ¬μ©λ²
|
| 38 |
+
|
| 39 |
+
1. μΌμͺ½ 8x8 보λμμ νμ¬ κ²μ νλ©΄κ³Ό λκ°μ΄ μ±μμ§ μΉΈμ ν΄λ¦ν΄μ νμ
|
| 40 |
+
2. μ€λ₯Έμͺ½ A/B/C μ‘°κ° νΈμ§κΈ°μμ μ§κΈ μ£Όμ΄μ§ 3κ° μ‘°κ° λͺ¨μμ κ·Έλλ‘ κ·Έλ¦¬κΈ°
|
| 41 |
+
(μ‘°κ°μ΄ 2κ°λΏμ΄λΌλ©΄ λλ¨Έμ§ νλλ λΉμλλ©΄ μλμΌλ‘ μ μΈλ©λλ€)
|
| 42 |
+
3. "ν΄λ΅ μ°ΎκΈ°" ν΄λ¦
|
| 43 |
+
4. 1λ¨κ³/2λ¨κ³/3λ¨κ³/μ΅μ’
κ²°κ³Ό λ²νΌμ λλ¬κ°λ©° μ΄λμ μ΄λ€ μμλ‘ λμμΌ νλμ§ νμΈ
|
| 44 |
+
|
| 45 |
+
## μκ³ λ¦¬μ¦
|
| 46 |
+
|
| 47 |
+
- μ‘°κ°μ λμ μ μλ λͺ¨λ μμ(μ΅λ 3! = 6κ°μ§) Γ κ° μ‘°κ°μ΄ λμΌ μ μλ λͺ¨λ (ν, μ΄) μμΉλ₯Ό
|
| 48 |
+
μ¬κ·μ μΌλ‘ μ λΆ μλνλ μμ νμ(backtracking)μ
λλ€.
|
| 49 |
+
- 8Γ8 보λ + μ‘°κ° 3κ° κ·λͺ¨μμλ μ€μ νμλμ΄ λ§μμΌ μμλ§ μΌμ΄μ€ μμ€μ΄λΌ λΈλΌμ°μ μμ μ¦μ(λκ° μμ~μλ°± ms) κ³μ°λ©λλ€.
|
| 50 |
+
- νκ° κΈ°μ€: `μ΄ μ§μμ§ μ€ μ`λ₯Ό μ΅μ°μ μΌλ‘, λμ μ΄λ©΄ `μ΅μ’
μ μΌλ‘ λ¨λ λΉ μΉΈ μ`κ° λ§μ(=보λκ° λ μ¬μ λ‘μ΄) λ°°μΉλ₯Ό μ νν©λλ€.
|
| 51 |
+
- 3κ°λ₯Ό λͺ¨λ λμ μ μλ λ°°μΉκ° μμΌλ©΄, μ΅λν λ§μ μ‘°κ°μ λμ μ μλ λΆλΆ ν΄λ₯Ό λμ 보μ¬μ€λλ€.
|
index.html
CHANGED
|
@@ -1,19 +1,774 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
</html>
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="ko">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Block Blast Solver β λ€μ μ κ³μ°κΈ°</title>
|
| 7 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 8 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
| 9 |
+
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
|
| 10 |
+
<style>
|
| 11 |
+
:root{
|
| 12 |
+
--bg:#12141b;
|
| 13 |
+
--panel:#1b1e28;
|
| 14 |
+
--panel-2:#20232f;
|
| 15 |
+
--grid-line:#2c3040;
|
| 16 |
+
--grid-line-soft:#242733;
|
| 17 |
+
--ink:#eef0f6;
|
| 18 |
+
--muted:#8b90a4;
|
| 19 |
+
--muted-2:#5d6175;
|
| 20 |
+
--piece-a:#ff6f61;
|
| 21 |
+
--piece-a-dim:#5a2c28;
|
| 22 |
+
--piece-b:#3fd1c4;
|
| 23 |
+
--piece-b-dim:#1f4744;
|
| 24 |
+
--piece-c:#ffcb4d;
|
| 25 |
+
--piece-c-dim:#4d411c;
|
| 26 |
+
--filled:#3b3f52;
|
| 27 |
+
--clear:#7effa3;
|
| 28 |
+
--radius:10px;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
*{box-sizing:border-box;}
|
| 32 |
+
html,body{margin:0;padding:0;}
|
| 33 |
+
body{
|
| 34 |
+
background:
|
| 35 |
+
linear-gradient(180deg, rgba(255,255,255,0.02), rgba(255,255,255,0) 300px),
|
| 36 |
+
var(--bg);
|
| 37 |
+
color:var(--ink);
|
| 38 |
+
font-family:'Inter',sans-serif;
|
| 39 |
+
min-height:100vh;
|
| 40 |
+
padding:28px 18px 60px;
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
@media (prefers-reduced-motion: reduce){
|
| 44 |
+
*{animation-duration:0.001ms !important; transition-duration:0.001ms !important;}
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
.wrap{max-width:1080px;margin:0 auto;}
|
| 48 |
+
|
| 49 |
+
header.top{
|
| 50 |
+
display:flex;
|
| 51 |
+
align-items:flex-end;
|
| 52 |
+
justify-content:space-between;
|
| 53 |
+
gap:16px;
|
| 54 |
+
margin-bottom:26px;
|
| 55 |
+
padding-bottom:18px;
|
| 56 |
+
border-bottom:1px solid var(--grid-line-soft);
|
| 57 |
+
}
|
| 58 |
+
.brand{
|
| 59 |
+
font-family:'Space Grotesk',sans-serif;
|
| 60 |
+
font-weight:700;
|
| 61 |
+
font-size:22px;
|
| 62 |
+
letter-spacing:-0.01em;
|
| 63 |
+
display:flex;
|
| 64 |
+
align-items:baseline;
|
| 65 |
+
gap:10px;
|
| 66 |
+
}
|
| 67 |
+
.brand .mono-tag{
|
| 68 |
+
font-family:'JetBrains Mono',monospace;
|
| 69 |
+
font-size:11px;
|
| 70 |
+
font-weight:500;
|
| 71 |
+
color:var(--bg);
|
| 72 |
+
background:var(--clear);
|
| 73 |
+
padding:2px 7px;
|
| 74 |
+
border-radius:5px;
|
| 75 |
+
letter-spacing:0.02em;
|
| 76 |
+
}
|
| 77 |
+
.brand-sub{
|
| 78 |
+
color:var(--muted);
|
| 79 |
+
font-size:13px;
|
| 80 |
+
margin-top:4px;
|
| 81 |
+
}
|
| 82 |
+
.reset-btn{
|
| 83 |
+
background:none;
|
| 84 |
+
border:1px solid var(--grid-line);
|
| 85 |
+
color:var(--muted);
|
| 86 |
+
font-family:'Inter',sans-serif;
|
| 87 |
+
font-size:13px;
|
| 88 |
+
padding:8px 14px;
|
| 89 |
+
border-radius:8px;
|
| 90 |
+
cursor:pointer;
|
| 91 |
+
transition:border-color .15s, color .15s;
|
| 92 |
+
}
|
| 93 |
+
.reset-btn:hover{border-color:var(--muted-2); color:var(--ink);}
|
| 94 |
+
.reset-btn:focus-visible{outline:2px solid var(--clear); outline-offset:2px;}
|
| 95 |
+
|
| 96 |
+
main.layout{
|
| 97 |
+
display:grid;
|
| 98 |
+
grid-template-columns:minmax(0,1fr) 300px;
|
| 99 |
+
gap:22px;
|
| 100 |
+
align-items:start;
|
| 101 |
+
}
|
| 102 |
+
@media (max-width:820px){
|
| 103 |
+
main.layout{grid-template-columns:1fr;}
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
.panel{
|
| 107 |
+
background:var(--panel);
|
| 108 |
+
border:1px solid var(--grid-line-soft);
|
| 109 |
+
border-radius:14px;
|
| 110 |
+
padding:20px;
|
| 111 |
+
}
|
| 112 |
+
.panel-title{
|
| 113 |
+
font-family:'Space Grotesk',sans-serif;
|
| 114 |
+
font-weight:600;
|
| 115 |
+
font-size:14px;
|
| 116 |
+
letter-spacing:0.02em;
|
| 117 |
+
text-transform:uppercase;
|
| 118 |
+
color:var(--muted);
|
| 119 |
+
margin:0 0 14px;
|
| 120 |
+
display:flex;
|
| 121 |
+
align-items:center;
|
| 122 |
+
justify-content:space-between;
|
| 123 |
+
}
|
| 124 |
+
.panel-title .hint{
|
| 125 |
+
font-family:'Inter',sans-serif;
|
| 126 |
+
text-transform:none;
|
| 127 |
+
font-weight:400;
|
| 128 |
+
font-size:12px;
|
| 129 |
+
color:var(--muted-2);
|
| 130 |
+
letter-spacing:0;
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
/* ---- 8x8 board ---- */
|
| 134 |
+
.board8{
|
| 135 |
+
display:grid;
|
| 136 |
+
grid-template-columns:repeat(8, 1fr);
|
| 137 |
+
gap:3px;
|
| 138 |
+
aspect-ratio:1/1;
|
| 139 |
+
max-width:460px;
|
| 140 |
+
margin:0 auto;
|
| 141 |
+
background:var(--grid-line-soft);
|
| 142 |
+
padding:3px;
|
| 143 |
+
border-radius:8px;
|
| 144 |
+
}
|
| 145 |
+
.cell{
|
| 146 |
+
aspect-ratio:1/1;
|
| 147 |
+
border-radius:4px;
|
| 148 |
+
background:var(--panel-2);
|
| 149 |
+
cursor:pointer;
|
| 150 |
+
border:1px solid transparent;
|
| 151 |
+
position:relative;
|
| 152 |
+
transition:background .1s;
|
| 153 |
+
}
|
| 154 |
+
.cell:hover{background:#282c3a;}
|
| 155 |
+
.cell:focus-visible{outline:2px solid var(--clear); outline-offset:-2px;}
|
| 156 |
+
.cell.filled{background:var(--filled);}
|
| 157 |
+
.cell.piece-a{background:var(--piece-a);}
|
| 158 |
+
.cell.piece-b{background:var(--piece-b);}
|
| 159 |
+
.cell.piece-c{background:var(--piece-c);}
|
| 160 |
+
.cell.will-clear::after{
|
| 161 |
+
content:"";
|
| 162 |
+
position:absolute; inset:0;
|
| 163 |
+
border:2px solid var(--clear);
|
| 164 |
+
border-radius:4px;
|
| 165 |
+
box-shadow:0 0 8px rgba(126,255,163,0.55);
|
| 166 |
+
}
|
| 167 |
+
.cell .num{
|
| 168 |
+
position:absolute; top:1px; right:2px;
|
| 169 |
+
font-family:'JetBrains Mono',monospace;
|
| 170 |
+
font-size:9px;
|
| 171 |
+
color:rgba(0,0,0,0.55);
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
.board-legend{
|
| 175 |
+
display:flex;
|
| 176 |
+
gap:16px;
|
| 177 |
+
justify-content:center;
|
| 178 |
+
margin-top:12px;
|
| 179 |
+
font-size:11px;
|
| 180 |
+
color:var(--muted);
|
| 181 |
+
flex-wrap:wrap;
|
| 182 |
+
}
|
| 183 |
+
.board-legend span{display:inline-flex; align-items:center; gap:5px;}
|
| 184 |
+
.swatch{width:10px;height:10px;border-radius:3px;display:inline-block;}
|
| 185 |
+
|
| 186 |
+
/* ---- piece editors ---- */
|
| 187 |
+
.pieces{display:flex; flex-direction:column; gap:14px;}
|
| 188 |
+
.piece-card{
|
| 189 |
+
background:var(--panel);
|
| 190 |
+
border:1px solid var(--grid-line-soft);
|
| 191 |
+
border-radius:14px;
|
| 192 |
+
padding:16px;
|
| 193 |
+
}
|
| 194 |
+
.piece-head{
|
| 195 |
+
display:flex; align-items:center; justify-content:space-between;
|
| 196 |
+
margin-bottom:10px;
|
| 197 |
+
}
|
| 198 |
+
.piece-label{
|
| 199 |
+
font-family:'Space Grotesk',sans-serif;
|
| 200 |
+
font-weight:600;
|
| 201 |
+
font-size:13px;
|
| 202 |
+
display:flex; align-items:center; gap:8px;
|
| 203 |
+
}
|
| 204 |
+
.piece-dot{width:9px;height:9px;border-radius:50%;}
|
| 205 |
+
.piece-clear{
|
| 206 |
+
background:none; border:none; color:var(--muted-2);
|
| 207 |
+
font-size:11px; cursor:pointer; font-family:'Inter',sans-serif;
|
| 208 |
+
padding:2px 4px;
|
| 209 |
+
}
|
| 210 |
+
.piece-clear:hover{color:var(--ink);}
|
| 211 |
+
.piece-grid{
|
| 212 |
+
display:grid;
|
| 213 |
+
grid-template-columns:repeat(5, 1fr);
|
| 214 |
+
gap:2px;
|
| 215 |
+
width:120px;
|
| 216 |
+
margin:0 auto;
|
| 217 |
+
background:var(--grid-line-soft);
|
| 218 |
+
padding:2px;
|
| 219 |
+
border-radius:6px;
|
| 220 |
+
}
|
| 221 |
+
.pcell{
|
| 222 |
+
aspect-ratio:1/1;
|
| 223 |
+
background:var(--panel-2);
|
| 224 |
+
border-radius:3px;
|
| 225 |
+
cursor:pointer;
|
| 226 |
+
border:none;
|
| 227 |
+
}
|
| 228 |
+
.pcell:hover{background:#282c3a;}
|
| 229 |
+
.pcell:focus-visible{outline:2px solid var(--clear); outline-offset:-1px;}
|
| 230 |
+
.pcell.on{background:var(--piece-color, var(--piece-a));}
|
| 231 |
+
|
| 232 |
+
.solve-row{
|
| 233 |
+
margin-top:6px;
|
| 234 |
+
}
|
| 235 |
+
.solve-btn{
|
| 236 |
+
width:100%;
|
| 237 |
+
background:var(--clear);
|
| 238 |
+
color:#0b1710;
|
| 239 |
+
border:none;
|
| 240 |
+
font-family:'Space Grotesk',sans-serif;
|
| 241 |
+
font-weight:700;
|
| 242 |
+
font-size:15px;
|
| 243 |
+
padding:13px;
|
| 244 |
+
border-radius:10px;
|
| 245 |
+
cursor:pointer;
|
| 246 |
+
transition:transform .08s, box-shadow .15s;
|
| 247 |
+
box-shadow:0 0 0 rgba(126,255,163,0);
|
| 248 |
+
}
|
| 249 |
+
.solve-btn:hover{box-shadow:0 4px 18px rgba(126,255,163,0.25);}
|
| 250 |
+
.solve-btn:active{transform:scale(0.98);}
|
| 251 |
+
.solve-btn:focus-visible{outline:2px solid var(--ink); outline-offset:2px;}
|
| 252 |
+
.solve-btn:disabled{background:var(--grid-line); color:var(--muted-2); cursor:default; box-shadow:none;}
|
| 253 |
+
|
| 254 |
+
.status-line{
|
| 255 |
+
font-family:'JetBrains Mono',monospace;
|
| 256 |
+
font-size:12px;
|
| 257 |
+
color:var(--muted);
|
| 258 |
+
margin-top:10px;
|
| 259 |
+
min-height:16px;
|
| 260 |
+
text-align:center;
|
| 261 |
+
}
|
| 262 |
+
|
| 263 |
+
/* ---- results ---- */
|
| 264 |
+
.results{margin-top:22px;}
|
| 265 |
+
.steps-row{
|
| 266 |
+
display:flex; gap:8px; flex-wrap:wrap;
|
| 267 |
+
margin-bottom:16px;
|
| 268 |
+
}
|
| 269 |
+
.step-chip{
|
| 270 |
+
font-family:'JetBrains Mono',monospace;
|
| 271 |
+
font-size:13px;
|
| 272 |
+
background:var(--panel-2);
|
| 273 |
+
border:1px solid var(--grid-line);
|
| 274 |
+
color:var(--muted);
|
| 275 |
+
padding:8px 14px;
|
| 276 |
+
border-radius:8px;
|
| 277 |
+
cursor:pointer;
|
| 278 |
+
transition:all .12s;
|
| 279 |
+
}
|
| 280 |
+
.step-chip:hover{color:var(--ink); border-color:var(--muted-2);}
|
| 281 |
+
.step-chip.active{
|
| 282 |
+
background:var(--clear);
|
| 283 |
+
color:#0b1710;
|
| 284 |
+
border-color:var(--clear);
|
| 285 |
+
font-weight:700;
|
| 286 |
+
}
|
| 287 |
+
.step-chip:focus-visible{outline:2px solid var(--clear); outline-offset:2px;}
|
| 288 |
+
|
| 289 |
+
.step-desc{
|
| 290 |
+
font-size:14px;
|
| 291 |
+
line-height:1.6;
|
| 292 |
+
color:var(--ink);
|
| 293 |
+
background:var(--panel-2);
|
| 294 |
+
border-left:3px solid var(--clear);
|
| 295 |
+
padding:12px 16px;
|
| 296 |
+
border-radius:0 8px 8px 0;
|
| 297 |
+
margin-bottom:16px;
|
| 298 |
+
}
|
| 299 |
+
.step-desc .coord{
|
| 300 |
+
font-family:'JetBrains Mono',monospace;
|
| 301 |
+
color:var(--clear);
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
.score-strip{
|
| 305 |
+
display:flex;
|
| 306 |
+
gap:22px;
|
| 307 |
+
flex-wrap:wrap;
|
| 308 |
+
font-family:'JetBrains Mono',monospace;
|
| 309 |
+
font-size:12px;
|
| 310 |
+
color:var(--muted);
|
| 311 |
+
padding-top:4px;
|
| 312 |
+
border-top:1px solid var(--grid-line-soft);
|
| 313 |
+
}
|
| 314 |
+
.score-strip b{color:var(--ink); font-weight:700;}
|
| 315 |
+
|
| 316 |
+
.empty-note{
|
| 317 |
+
color:var(--muted);
|
| 318 |
+
font-size:13px;
|
| 319 |
+
line-height:1.7;
|
| 320 |
+
text-align:center;
|
| 321 |
+
padding:20px 10px;
|
| 322 |
+
}
|
| 323 |
+
|
| 324 |
+
footer.foot{
|
| 325 |
+
max-width:1080px;
|
| 326 |
+
margin:34px auto 0;
|
| 327 |
+
color:var(--muted-2);
|
| 328 |
+
font-size:12px;
|
| 329 |
+
text-align:center;
|
| 330 |
+
line-height:1.6;
|
| 331 |
+
}
|
| 332 |
+
</style>
|
| 333 |
+
</head>
|
| 334 |
+
<body>
|
| 335 |
+
<div class="wrap">
|
| 336 |
+
|
| 337 |
+
<header class="top">
|
| 338 |
+
<div>
|
| 339 |
+
<div class="brand">Block Blast Solver <span class="mono-tag">SEARCH</span></div>
|
| 340 |
+
<div class="brand-sub">νμ¬ λ³΄λμ 3κ° μ‘°κ°μ 그리면, μμ νμμΌλ‘ κ°μ₯ μ€μ λ§μ΄ μ§μ°λ λ°°μΉ μμλ₯Ό μ°Ύμλλ €μ.</div>
|
| 341 |
+
</div>
|
| 342 |
+
<button class="reset-btn" id="resetAllBtn">μ 체 μ΄κΈ°ν</button>
|
| 343 |
+
</header>
|
| 344 |
+
|
| 345 |
+
<main class="layout">
|
| 346 |
+
<section class="panel">
|
| 347 |
+
<div class="panel-title">보λ μν <span class="hint">μΉΈμ ν΄λ¦ν΄ μ±μμ§ λΈλ‘μ νμνμΈμ</span></div>
|
| 348 |
+
<div class="board8" id="board"></div>
|
| 349 |
+
<div class="board-legend">
|
| 350 |
+
<span><span class="swatch" style="background:var(--filled)"></span>μ±μμ§ μΉΈ</span>
|
| 351 |
+
<span><span class="swatch" style="background:var(--piece-a)"></span>μ‘°κ° A</span>
|
| 352 |
+
<span><span class="swatch" style="background:var(--piece-b)"></span>οΏ½οΏ½κ° B</span>
|
| 353 |
+
<span><span class="swatch" style="background:var(--piece-c)"></span>μ‘°κ° C</span>
|
| 354 |
+
</div>
|
| 355 |
+
|
| 356 |
+
<div class="results" id="resultsBlock" style="display:none;">
|
| 357 |
+
<div class="steps-row" id="stepsRow"></div>
|
| 358 |
+
<div class="step-desc" id="stepDesc"></div>
|
| 359 |
+
<div class="score-strip" id="scoreStrip"></div>
|
| 360 |
+
</div>
|
| 361 |
+
|
| 362 |
+
<div class="empty-note" id="emptyNote">
|
| 363 |
+
μλμμ μΈ μ‘°κ° λͺ¨μμ κ·Έλ¦¬κ³ <b style="color:var(--ink)">ν΄λ΅ μ°ΎκΈ°</b>λ₯Ό λλ₯΄λ©΄ μ¬κΈ° κ²°κ³Όκ° νμλ©λλ€.
|
| 364 |
+
</div>
|
| 365 |
+
</section>
|
| 366 |
+
|
| 367 |
+
<aside class="pieces">
|
| 368 |
+
<div class="piece-card">
|
| 369 |
+
<div class="piece-head">
|
| 370 |
+
<div class="piece-label"><span class="piece-dot" style="background:var(--piece-a)"></span>μ‘°κ° A</div>
|
| 371 |
+
<button class="piece-clear" data-clear="a">μ§μ°κΈ°</button>
|
| 372 |
+
</div>
|
| 373 |
+
<div class="piece-grid" id="piece-a" style="--piece-color:var(--piece-a)"></div>
|
| 374 |
+
</div>
|
| 375 |
+
<div class="piece-card">
|
| 376 |
+
<div class="piece-head">
|
| 377 |
+
<div class="piece-label"><span class="piece-dot" style="background:var(--piece-b)"></span>μ‘°κ° B</div>
|
| 378 |
+
<button class="piece-clear" data-clear="b">μ§μ°κΈ°</button>
|
| 379 |
+
</div>
|
| 380 |
+
<div class="piece-grid" id="piece-b" style="--piece-color:var(--piece-b)"></div>
|
| 381 |
+
</div>
|
| 382 |
+
<div class="piece-card">
|
| 383 |
+
<div class="piece-head">
|
| 384 |
+
<div class="piece-label"><span class="piece-dot" style="background:var(--piece-c)"></span>μ‘°κ° C</div>
|
| 385 |
+
<button class="piece-clear" data-clear="c">μ§μ°κΈ°</button>
|
| 386 |
+
</div>
|
| 387 |
+
<div class="piece-grid" id="piece-c" style="--piece-color:var(--piece-c)"></div>
|
| 388 |
+
</div>
|
| 389 |
+
|
| 390 |
+
<div class="solve-row">
|
| 391 |
+
<button class="solve-btn" id="solveBtn">ν΄λ΅ μ°ΎκΈ°</button>
|
| 392 |
+
<div class="status-line" id="statusLine"></div>
|
| 393 |
+
</div>
|
| 394 |
+
</aside>
|
| 395 |
+
</main>
|
| 396 |
+
|
| 397 |
+
<footer class="foot">
|
| 398 |
+
보λ ν΄λ¦ = μ±μμ§ μΉΈ ν κΈ Β· μ‘°κ° νΈμ§κΈ° ν΄λ¦ = λͺ¨μ 그리기 Β· νμμ λΈλΌμ°μ μμλ§ μ€νλλ©° μλ²λ‘ μ μ‘λμ§ μμ΅λλ€.
|
| 399 |
+
</footer>
|
| 400 |
+
</div>
|
| 401 |
+
|
| 402 |
+
<script>
|
| 403 |
+
(function(){
|
| 404 |
+
const N = 8;
|
| 405 |
+
const PIECE_GRID = 5;
|
| 406 |
+
const pieceKeys = ['a','b','c'];
|
| 407 |
+
const pieceColorVar = {a:'var(--piece-a)', b:'var(--piece-b)', c:'var(--piece-c)'};
|
| 408 |
+
const pieceNameKo = {a:'A', b:'B', c:'C'};
|
| 409 |
+
|
| 410 |
+
// ---- state ----
|
| 411 |
+
let board = Array.from({length:N}, ()=>Array(N).fill(0));
|
| 412 |
+
let pieceGrids = {
|
| 413 |
+
a: Array.from({length:PIECE_GRID}, ()=>Array(PIECE_GRID).fill(0)),
|
| 414 |
+
b: Array.from({length:PIECE_GRID}, ()=>Array(PIECE_GRID).fill(0)),
|
| 415 |
+
c: Array.from({length:PIECE_GRID}, ()=>Array(PIECE_GRID).fill(0)),
|
| 416 |
+
};
|
| 417 |
+
let lastSolution = null; // {steps:[{piece,r,c,offsets,boardBeforeClear,clearRows,clearCols,boardAfterClear}], totalLines, finalEmpty}
|
| 418 |
+
|
| 419 |
+
// ---- DOM refs ----
|
| 420 |
+
const boardEl = document.getElementById('board');
|
| 421 |
+
const statusLine = document.getElementById('statusLine');
|
| 422 |
+
const resultsBlock = document.getElementById('resultsBlock');
|
| 423 |
+
const emptyNote = document.getElementById('emptyNote');
|
| 424 |
+
const stepsRow = document.getElementById('stepsRow');
|
| 425 |
+
const stepDesc = document.getElementById('stepDesc');
|
| 426 |
+
const scoreStrip = document.getElementById('scoreStrip');
|
| 427 |
+
|
| 428 |
+
// ---- build board grid ----
|
| 429 |
+
const boardCells = [];
|
| 430 |
+
for(let r=0;r<N;r++){
|
| 431 |
+
const row=[];
|
| 432 |
+
for(let c=0;c<N;c++){
|
| 433 |
+
const cell = document.createElement('button');
|
| 434 |
+
cell.className='cell';
|
| 435 |
+
cell.type='button';
|
| 436 |
+
cell.setAttribute('aria-label', `${r+1}ν ${c+1}μ΄`);
|
| 437 |
+
cell.addEventListener('click', ()=>{
|
| 438 |
+
board[r][c] = board[r][c] ? 0 : 1;
|
| 439 |
+
clearSolution();
|
| 440 |
+
renderBoard();
|
| 441 |
+
});
|
| 442 |
+
boardEl.appendChild(cell);
|
| 443 |
+
row.push(cell);
|
| 444 |
+
}
|
| 445 |
+
boardCells.push(row);
|
| 446 |
+
}
|
| 447 |
+
|
| 448 |
+
// ---- build piece editors ----
|
| 449 |
+
const pieceCellEls = {a:[], b:[], c:[]};
|
| 450 |
+
pieceKeys.forEach(k=>{
|
| 451 |
+
const el = document.getElementById('piece-'+k);
|
| 452 |
+
for(let r=0;r<PIECE_GRID;r++){
|
| 453 |
+
const rowEls=[];
|
| 454 |
+
for(let c=0;c<PIECE_GRID;c++){
|
| 455 |
+
const b = document.createElement('button');
|
| 456 |
+
b.type='button';
|
| 457 |
+
b.className='pcell';
|
| 458 |
+
b.setAttribute('aria-label', `${pieceNameKo[k]} μ‘°κ° ${r+1},${c+1}`);
|
| 459 |
+
b.addEventListener('click', ()=>{
|
| 460 |
+
pieceGrids[k][r][c] = pieceGrids[k][r][c] ? 0 : 1;
|
| 461 |
+
clearSolution();
|
| 462 |
+
renderPiece(k);
|
| 463 |
+
});
|
| 464 |
+
el.appendChild(b);
|
| 465 |
+
rowEls.push(b);
|
| 466 |
+
}
|
| 467 |
+
pieceCellEls[k].push(rowEls);
|
| 468 |
+
}
|
| 469 |
+
});
|
| 470 |
+
|
| 471 |
+
document.querySelectorAll('.piece-clear').forEach(btn=>{
|
| 472 |
+
btn.addEventListener('click', ()=>{
|
| 473 |
+
const k = btn.dataset.clear;
|
| 474 |
+
pieceGrids[k] = Array.from({length:PIECE_GRID}, ()=>Array(PIECE_GRID).fill(0));
|
| 475 |
+
clearSolution();
|
| 476 |
+
renderPiece(k);
|
| 477 |
+
});
|
| 478 |
+
});
|
| 479 |
+
|
| 480 |
+
document.getElementById('resetAllBtn').addEventListener('click', ()=>{
|
| 481 |
+
board = Array.from({length:N}, ()=>Array(N).fill(0));
|
| 482 |
+
pieceKeys.forEach(k=> pieceGrids[k] = Array.from({length:PIECE_GRID}, ()=>Array(PIECE_GRID).fill(0)));
|
| 483 |
+
clearSolution();
|
| 484 |
+
renderBoard();
|
| 485 |
+
pieceKeys.forEach(renderPiece);
|
| 486 |
+
statusLine.textContent='';
|
| 487 |
+
});
|
| 488 |
+
|
| 489 |
+
document.getElementById('solveBtn').addEventListener('click', runSolve);
|
| 490 |
+
|
| 491 |
+
// ---- rendering ----
|
| 492 |
+
function renderBoard(overlay){
|
| 493 |
+
// overlay: optional {board: 8x8 with 0/'a'/'b'/'c', willClear:Set of "r,c"}
|
| 494 |
+
for(let r=0;r<N;r++){
|
| 495 |
+
for(let c=0;c<N;c++){
|
| 496 |
+
const cell = boardCells[r][c];
|
| 497 |
+
cell.className='cell';
|
| 498 |
+
if(overlay){
|
| 499 |
+
const v = overlay.board[r][c];
|
| 500 |
+
if(v==='a'||v==='b'||v==='c') cell.classList.add('piece-'+v);
|
| 501 |
+
else if(v===1) cell.classList.add('filled');
|
| 502 |
+
if(overlay.willClear && overlay.willClear.has(r+','+c)) cell.classList.add('will-clear');
|
| 503 |
+
} else {
|
| 504 |
+
if(board[r][c]) cell.classList.add('filled');
|
| 505 |
+
}
|
| 506 |
+
}
|
| 507 |
+
}
|
| 508 |
+
}
|
| 509 |
+
|
| 510 |
+
function renderPiece(k){
|
| 511 |
+
for(let r=0;r<PIECE_GRID;r++){
|
| 512 |
+
for(let c=0;c<PIECE_GRID;c++){
|
| 513 |
+
const el = pieceCellEls[k][r][c];
|
| 514 |
+
el.classList.toggle('on', !!pieceGrids[k][r][c]);
|
| 515 |
+
}
|
| 516 |
+
}
|
| 517 |
+
}
|
| 518 |
+
|
| 519 |
+
function clearSolution(){
|
| 520 |
+
lastSolution = null;
|
| 521 |
+
resultsBlock.style.display='none';
|
| 522 |
+
emptyNote.style.display='block';
|
| 523 |
+
}
|
| 524 |
+
|
| 525 |
+
renderBoard();
|
| 526 |
+
pieceKeys.forEach(renderPiece);
|
| 527 |
+
|
| 528 |
+
// ---- shape trimming ----
|
| 529 |
+
function trimShape(grid){
|
| 530 |
+
let minR=99,maxR=-1,minC=99,maxC=-1;
|
| 531 |
+
for(let r=0;r<grid.length;r++){
|
| 532 |
+
for(let c=0;c<grid[0].length;c++){
|
| 533 |
+
if(grid[r][c]){
|
| 534 |
+
if(r<minR)minR=r; if(r>maxR)maxR=r;
|
| 535 |
+
if(c<minC)minC=c; if(c>maxC)maxC=c;
|
| 536 |
+
}
|
| 537 |
+
}
|
| 538 |
+
}
|
| 539 |
+
if(maxR===-1) return null; // empty piece
|
| 540 |
+
const offsets=[];
|
| 541 |
+
for(let r=minR;r<=maxR;r++){
|
| 542 |
+
for(let c=minC;c<=maxC;c++){
|
| 543 |
+
if(grid[r][c]) offsets.push([r-minR, c-minC]);
|
| 544 |
+
}
|
| 545 |
+
}
|
| 546 |
+
return {offsets, h:maxR-minR+1, w:maxC-minC+1};
|
| 547 |
+
}
|
| 548 |
+
|
| 549 |
+
// ---- board ops ----
|
| 550 |
+
function canPlace(bd, offsets, r, c){
|
| 551 |
+
for(const [dr,dc] of offsets){
|
| 552 |
+
const rr=r+dr, cc=c+dc;
|
| 553 |
+
if(rr<0||rr>=N||cc<0||cc>=N) return false;
|
| 554 |
+
if(bd[rr][cc]) return false;
|
| 555 |
+
}
|
| 556 |
+
return true;
|
| 557 |
+
}
|
| 558 |
+
|
| 559 |
+
function cloneBoard(bd){
|
| 560 |
+
return bd.map(row=>row.slice());
|
| 561 |
+
}
|
| 562 |
+
|
| 563 |
+
function place(bd, offsets, r, c){
|
| 564 |
+
for(const [dr,dc] of offsets){
|
| 565 |
+
bd[r+dr][c+dc]=1;
|
| 566 |
+
}
|
| 567 |
+
}
|
| 568 |
+
|
| 569 |
+
function findClears(bd){
|
| 570 |
+
const rows=[], cols=[];
|
| 571 |
+
for(let r=0;r<N;r++){
|
| 572 |
+
let full=true;
|
| 573 |
+
for(let c=0;c<N;c++) if(!bd[r][c]){full=false;break;}
|
| 574 |
+
if(full) rows.push(r);
|
| 575 |
+
}
|
| 576 |
+
for(let c=0;c<N;c++){
|
| 577 |
+
let full=true;
|
| 578 |
+
for(let r=0;r<N;r++) if(!bd[r][c]){full=false;break;}
|
| 579 |
+
if(full) cols.push(c);
|
| 580 |
+
}
|
| 581 |
+
return {rows, cols};
|
| 582 |
+
}
|
| 583 |
+
|
| 584 |
+
function applyClear(bd, rows, cols){
|
| 585 |
+
for(const r of rows) for(let c=0;c<N;c++) bd[r][c]=0;
|
| 586 |
+
for(const c of cols) for(let r=0;r<N;r++) bd[r][c]=0;
|
| 587 |
+
}
|
| 588 |
+
|
| 589 |
+
function countEmpty(bd){
|
| 590 |
+
let n=0;
|
| 591 |
+
for(let r=0;r<N;r++) for(let c=0;c<N;c++) if(!bd[r][c]) n++;
|
| 592 |
+
return n;
|
| 593 |
+
}
|
| 594 |
+
|
| 595 |
+
// ---- solver (backtracking over all orderings x all positions) ----
|
| 596 |
+
function solve(initialBoard, pieces){
|
| 597 |
+
// pieces: [{key, offsets, h, w}]
|
| 598 |
+
let best = null; // {steps, totalLines, finalEmpty, score}
|
| 599 |
+
let bestPartial = null; // fallback if no full solution: {steps, placedCount, totalLines, score}
|
| 600 |
+
|
| 601 |
+
function evaluate(steps, totalLines, finalBoard){
|
| 602 |
+
const finalEmpty = countEmpty(finalBoard);
|
| 603 |
+
const score = totalLines*1000 + finalEmpty;
|
| 604 |
+
return {steps: steps.slice(), totalLines, finalEmpty, score};
|
| 605 |
+
}
|
| 606 |
+
|
| 607 |
+
function recurse(bd, remaining, steps, totalLines){
|
| 608 |
+
if(remaining.length===0){
|
| 609 |
+
const result = evaluate(steps, totalLines, bd);
|
| 610 |
+
if(!best || result.score > best.score) best = result;
|
| 611 |
+
return;
|
| 612 |
+
}
|
| 613 |
+
// track partial best in case nothing completes
|
| 614 |
+
{
|
| 615 |
+
const partialScore = totalLines*1000 + countEmpty(bd) + steps.length*2000; // prioritize placing more pieces
|
| 616 |
+
if(!bestPartial || partialScore > bestPartial.score){
|
| 617 |
+
bestPartial = {steps: steps.slice(), placedCount: steps.length, totalLines, score: partialScore};
|
| 618 |
+
}
|
| 619 |
+
}
|
| 620 |
+
for(let i=0;i<remaining.length;i++){
|
| 621 |
+
const piece = remaining[i];
|
| 622 |
+
const restPieces = remaining.slice(0,i).concat(remaining.slice(i+1));
|
| 623 |
+
const maxR = N - piece.h, maxC = N - piece.w;
|
| 624 |
+
for(let r=0;r<=maxR;r++){
|
| 625 |
+
for(let c=0;c<=maxC;c++){
|
| 626 |
+
if(canPlace(bd, piece.offsets, r, c)){
|
| 627 |
+
const nb = cloneBoard(bd);
|
| 628 |
+
place(nb, piece.offsets, r, c);
|
| 629 |
+
const beforeClear = cloneBoard(nb);
|
| 630 |
+
const {rows, cols} = findClears(nb);
|
| 631 |
+
applyClear(nb, rows, cols);
|
| 632 |
+
const linesThisStep = rows.length + cols.length;
|
| 633 |
+
steps.push({
|
| 634 |
+
pieceKey: piece.key,
|
| 635 |
+
r, c,
|
| 636 |
+
offsets: piece.offsets,
|
| 637 |
+
boardBeforeClear: beforeClear,
|
| 638 |
+
clearRows: rows,
|
| 639 |
+
clearCols: cols,
|
| 640 |
+
boardAfterClear: cloneBoard(nb),
|
| 641 |
+
linesThisStep
|
| 642 |
+
});
|
| 643 |
+
recurse(nb, restPieces, steps, totalLines + linesThisStep);
|
| 644 |
+
steps.pop();
|
| 645 |
+
}
|
| 646 |
+
}
|
| 647 |
+
}
|
| 648 |
+
}
|
| 649 |
+
}
|
| 650 |
+
|
| 651 |
+
recurse(cloneBoard(initialBoard), pieces, [], 0);
|
| 652 |
+
return {best, bestPartial};
|
| 653 |
+
}
|
| 654 |
+
|
| 655 |
+
function runSolve(){
|
| 656 |
+
// prepare pieces
|
| 657 |
+
const pieces = [];
|
| 658 |
+
let anyEmpty = false;
|
| 659 |
+
for(const k of pieceKeys){
|
| 660 |
+
const trimmed = trimShape(pieceGrids[k]);
|
| 661 |
+
if(!trimmed){ anyEmpty = true; continue; }
|
| 662 |
+
pieces.push({key:k, offsets:trimmed.offsets, h:trimmed.h, w:trimmed.w});
|
| 663 |
+
}
|
| 664 |
+
if(pieces.length===0){
|
| 665 |
+
statusLine.textContent = 'μ‘°κ°μ μ΅μ 1κ° μ΄μ κ·Έλ €μ£ΌμΈμ.';
|
| 666 |
+
return;
|
| 667 |
+
}
|
| 668 |
+
if(anyEmpty){
|
| 669 |
+
statusLine.textContent = 'λΉμ΄ μλ μ‘°κ°μ μ΄λ² νμμμ μ μΈλΌμ. (νμνλ©΄ μΈ μ‘°κ°μ λͺ¨λ μ±μμ£ΌμΈμ)';
|
| 670 |
+
} else {
|
| 671 |
+
statusLine.textContent = '';
|
| 672 |
+
}
|
| 673 |
+
|
| 674 |
+
statusLine.textContent += (statusLine.textContent?' Β· ':'') + 'νμ μ€...';
|
| 675 |
+
document.getElementById('solveBtn').disabled = true;
|
| 676 |
+
|
| 677 |
+
setTimeout(()=>{
|
| 678 |
+
const t0 = performance.now();
|
| 679 |
+
const {best, bestPartial} = solve(board, pieces);
|
| 680 |
+
const t1 = performance.now();
|
| 681 |
+
document.getElementById('solveBtn').disabled = false;
|
| 682 |
+
|
| 683 |
+
if(best){
|
| 684 |
+
lastSolution = best;
|
| 685 |
+
statusLine.textContent = `νμ μλ£ (${Math.round(t1-t0)}ms) Β· μ 체 λ°°μΉ κ°λ₯`;
|
| 686 |
+
showSolution(best, pieces.length);
|
| 687 |
+
} else if(bestPartial && bestPartial.steps.length>0){
|
| 688 |
+
lastSolution = bestPartial;
|
| 689 |
+
statusLine.textContent = `νμ μλ£ (${Math.round(t1-t0)}ms) Β· μ‘°κ° ${pieces.length}κ°λ₯Ό λͺ¨λ λμ λ°©λ²μ μμ΄μ`;
|
| 690 |
+
showSolution(bestPartial, pieces.length, true);
|
| 691 |
+
} else {
|
| 692 |
+
lastSolution = null;
|
| 693 |
+
statusLine.textContent = `νμ μλ£ (${Math.round(t1-t0)}ms) Β· λμ μ μλ μ‘°κ°μ΄ μμ΄μ`;
|
| 694 |
+
resultsBlock.style.display='none';
|
| 695 |
+
emptyNote.style.display='block';
|
| 696 |
+
emptyNote.textContent = 'μ§κΈ 보λ μνλ‘λ κ·Έλ €μ£Όμ μ‘°κ°μ νλλ λμ μ μμ΄μ. 보λλ μ‘°κ° λͺ¨μμ λ€μ νμΈν΄μ£ΌμΈμ.';
|
| 697 |
+
}
|
| 698 |
+
}, 20);
|
| 699 |
+
}
|
| 700 |
+
|
| 701 |
+
// ---- display solution ----
|
| 702 |
+
let activeStepIdx = 0;
|
| 703 |
+
|
| 704 |
+
function showSolution(sol, totalPiecesRequested, isPartial){
|
| 705 |
+
resultsBlock.style.display='block';
|
| 706 |
+
emptyNote.style.display='none';
|
| 707 |
+
|
| 708 |
+
stepsRow.innerHTML='';
|
| 709 |
+
sol.steps.forEach((step, idx)=>{
|
| 710 |
+
const chip = document.createElement('button');
|
| 711 |
+
chip.type='button';
|
| 712 |
+
chip.className='step-chip';
|
| 713 |
+
chip.textContent = `${idx+1}λ¨κ³ Β· ${pieceNameKo[step.pieceKey]}`;
|
| 714 |
+
chip.addEventListener('click', ()=> setActiveStep(idx));
|
| 715 |
+
stepsRow.appendChild(chip);
|
| 716 |
+
});
|
| 717 |
+
const finalChip = document.createElement('button');
|
| 718 |
+
finalChip.type='button';
|
| 719 |
+
finalChip.className='step-chip';
|
| 720 |
+
finalChip.textContent = 'μ΅μ’
κ²°κ³Ό';
|
| 721 |
+
finalChip.addEventListener('click', ()=> setActiveStep(sol.steps.length));
|
| 722 |
+
stepsRow.appendChild(finalChip);
|
| 723 |
+
|
| 724 |
+
function setActiveStep(idx){
|
| 725 |
+
activeStepIdx = idx;
|
| 726 |
+
Array.from(stepsRow.children).forEach((el,i)=> el.classList.toggle('active', i===idx));
|
| 727 |
+
|
| 728 |
+
if(idx < sol.steps.length){
|
| 729 |
+
const step = sol.steps[idx];
|
| 730 |
+
const overlayBoard = step.boardBeforeClear.map(row=>row.slice());
|
| 731 |
+
// mark piece cells with its color letter for rendering
|
| 732 |
+
for(const [dr,dc] of step.offsets){
|
| 733 |
+
overlayBoard[step.r+dr][step.c+dc] = step.pieceKey;
|
| 734 |
+
}
|
| 735 |
+
const willClear = new Set();
|
| 736 |
+
for(const r of step.clearRows) for(let c=0;c<N;c++) willClear.add(r+','+c);
|
| 737 |
+
for(const c of step.clearCols) for(let r=0;r<N;r++) willClear.add(r+','+c);
|
| 738 |
+
renderBoard({board:overlayBoard, willClear});
|
| 739 |
+
|
| 740 |
+
const lineTxt = step.linesThisStep>0
|
| 741 |
+
? `μ΄ λ°°μΉλ‘ <b class="coord">${step.linesThisStep}μ€</b>μ΄ μ§μμ Έμ.`
|
| 742 |
+
: `μ΄ λ°°μΉλ‘λ μμ§ μ€μ΄ μ§μμ§μ§ μμμ.`;
|
| 743 |
+
stepDesc.innerHTML = `<b>${idx+1}λ¨κ³</b> β μ‘°κ° <b class="coord">${pieceNameKo[step.pieceKey]}</b>λ₯Ό `
|
| 744 |
+
+ `<span class="coord">${step.r+1}ν ${step.c+1}μ΄</span>(μΌμͺ½ μ κΈ°μ€)μ λμΌμΈμ. ${lineTxt}`;
|
| 745 |
+
} else {
|
| 746 |
+
// final state
|
| 747 |
+
const last = sol.steps[sol.steps.length-1];
|
| 748 |
+
renderBoard({board: last.boardAfterClear});
|
| 749 |
+
stepDesc.innerHTML = `<b>μ΅μ’
보λ</b> β λͺ¨λ λ°°μΉμ μ€ μ§μ°κΈ°κ° λλ λ€μ μνμμ.`;
|
| 750 |
+
}
|
| 751 |
+
}
|
| 752 |
+
|
| 753 |
+
const totalLines = sol.totalLines;
|
| 754 |
+
const placedCount = sol.steps.length;
|
| 755 |
+
scoreStrip.innerHTML = `
|
| 756 |
+
<span>λ°°μΉλ μ‘°κ°: <b>${placedCount} / ${totalPiecesRequested}</b></span>
|
| 757 |
+
<span>μ΄ μ§μμ§ μ€: <b>${totalLines}</b></span>
|
| 758 |
+
<span>νμ λ°©μ: <b>μμ νμ (λ°±νΈλνΉ)</b></span>
|
| 759 |
+
`;
|
| 760 |
+
if(isPartial){
|
| 761 |
+
const warn = document.createElement('div');
|
| 762 |
+
warn.style.color = 'var(--piece-a)';
|
| 763 |
+
warn.style.marginTop = '6px';
|
| 764 |
+
warn.textContent = 'β 3κ°λ₯Ό λͺ¨λ λμΌλ©΄ 보λκ° λ§νμ. νμλ κ²μ μ΅λν λ§μ΄ λμ μ μλ λ°°μΉμμ.';
|
| 765 |
+
scoreStrip.appendChild(warn);
|
| 766 |
+
}
|
| 767 |
+
|
| 768 |
+
setActiveStep(0);
|
| 769 |
+
}
|
| 770 |
+
|
| 771 |
+
})();
|
| 772 |
+
</script>
|
| 773 |
+
</body>
|
| 774 |
</html>
|