# Product Requirements Document (PRD)

## Sudoku Multiplayer Web

**Nama produk sementara:** Sudoku Duel  
**Versi dokumen:** 1.0  
**Status:** Draft untuk pengembangan  
**Platform:** Web responsif  
**Target deployment:** cPanel Unlimited Hosting  
**Bahasa utama:** Indonesia  

---

## 1. Ringkasan Produk

Sudoku Duel adalah aplikasi web Sudoku yang memungkinkan pengguna bermain Sudoku secara solo maupun bertanding secara real-time melawan pemain lain dalam mode 1v1.

Pengguna dapat membuat room, membagikan kode room, bergabung menggunakan kode room, memilih tingkat kesulitan, dan menyelesaikan papan Sudoku yang sama secara bersamaan. Sistem menggunakan mekanisme heart sebagai batas kesalahan. Pemain yang menyelesaikan papan lebih dahulu, memperoleh skor tertinggi, atau membuat lawan kehilangan seluruh heart dapat memenangkan pertandingan.

Aplikasi harus ringan, responsif, dapat dimainkan melalui desktop maupun perangkat mobile, dan dapat di-deploy pada hosting berbasis cPanel yang mendukung aplikasi Node.js dan database MySQL.

---

## 2. Latar Belakang

Sebagian besar aplikasi Sudoku berfokus pada permainan solo. Produk ini menggabungkan pengalaman Sudoku klasik dengan kompetisi multiplayer langsung agar permainan lebih menarik, sosial, dan memiliki nilai replay yang tinggi.

Produk ditujukan untuk:

- Pemain Sudoku kasual.
- Pemain yang ingin berkompetisi dengan teman.
- Pengguna mobile yang membutuhkan permainan ringan tanpa instalasi.
- Komunitas sekolah, keluarga, atau pertemanan yang ingin mengadakan pertandingan Sudoku.

---

## 3. Tujuan Produk

### 3.1 Tujuan Utama

1. Menyediakan permainan Sudoku solo yang nyaman dan mudah digunakan.
2. Menyediakan pertandingan Sudoku 1v1 secara real-time.
3. Memungkinkan pemain membuat dan bergabung ke room privat.
4. Memberikan tingkat kesulitan Easy, Medium, dan Hard.
5. Menambahkan sistem heart untuk memberikan konsekuensi terhadap jawaban salah.
6. Menyimpan statistik permainan, kemenangan, kekalahan, dan waktu penyelesaian.
7. Menyediakan aplikasi yang dapat berjalan pada cPanel Unlimited Hosting.

### 3.2 Indikator Keberhasilan

- Pemain dapat memulai permainan solo dalam maksimal 3 interaksi.
- Pemain dapat membuat room dan memperoleh kode room.
- Pemain kedua dapat bergabung dan melihat status room secara real-time.
- Kedua pemain menerima papan dan urutan angka awal yang sama.
- Perubahan status pertandingan diterima lawan dengan latensi normal kurang dari 1 detik.
- Tidak ada pemain yang dapat memanipulasi papan, heart, skor, atau status kemenangan dari sisi browser.
- Permainan dapat diselesaikan dengan baik di layar mobile minimal lebar 360 piksel.

---

## 4. Ruang Lingkup Produk

## 4.1 Dalam Ruang Lingkup MVP

- Landing page.
- Bermain sebagai guest.
- Mode solo.
- Mode multiplayer 1v1.
- Create room.
- Join room menggunakan kode.
- Tingkat kesulitan Easy, Medium, dan Hard.
- Sistem heart.
- Timer permainan.
- Input angka 1–9.
- Dark mode/Light Mode
- Eraser.
- Notes atau pencil mode.
- Undo.
- Highlight angka yang sama.
- Deteksi konflik.
- Hint terbatas.
- Pause untuk mode solo.
- Status room dan pemain.
- Ready system.
- Sinkronisasi pertandingan.
- Penentuan pemenang.
- UI responsif.
- Deployment ke cPanel dan MySQL.

## 4.2 Di Luar Ruang Lingkup MVP

- Multiplayer lebih dari dua pemain.
- Chat room.
- Puzzle dengan ukuran selain 9×9.

---

## 5. Persona Pengguna

### 5.1 Pemain Guest

Pengguna yang ingin langsung bermain tanpa membuat akun.

**Kebutuhan:**

- Masuk ke permainan dengan cepat.
- Membuat atau bergabung room menggunakan nama sementara.
- Memainkan mode solo.
- Tidak perlu mengisi data pribadi.

**Batasan:**

- Statistik tidak tersimpan permanen.
- Riwayat pertandingan dapat hilang setelah sesi berakhir.
- Tidak masuk leaderboard akun.

### 5.2 Pemain Terdaftar

Pengguna yang memiliki akun.

**Kebutuhan:**

- Statistik tersimpan.
- Melihat kemenangan, kekalahan, win rate, dan waktu terbaik.
- Memiliki nama pengguna yang konsisten.
- Masuk leaderboard.
- Melihat riwayat pertandingan.

### 5.3 Administrator

Pengelola aplikasi.

**Kebutuhan:**

- Melihat jumlah pengguna dan pertandingan.
- Menonaktifkan akun bermasalah.
- Mengelola puzzle.
- Melihat room aktif.
- Melihat log error dan aktivitas mencurigakan.
- Menghapus data yang tidak valid.

---

## 6. User Stories

### 6.1 Mode Solo

- Sebagai pemain, saya ingin memilih tingkat kesulitan agar permainan sesuai kemampuan saya.
- Sebagai pemain, saya ingin menggunakan notes agar dapat mencatat kandidat angka.
- Sebagai pemain, saya ingin mendapatkan highlight angka yang sama agar papan lebih mudah dibaca.
- Sebagai pemain, saya ingin melihat jumlah heart agar mengetahui batas kesalahan.
- Sebagai pemain, saya ingin menggunakan hint ketika kesulitan.
- Sebagai pemain, saya ingin melihat waktu penyelesaian.

### 6.2 Create Room

- Sebagai pemain, saya ingin membuat room privat agar dapat bermain dengan teman.
- Sebagai host, saya ingin memilih tingkat kesulitan.
- Sebagai host, saya ingin memperoleh kode room yang mudah dibagikan.
- Sebagai host, saya ingin melihat pemain yang telah masuk.
- Sebagai host, saya ingin memulai pertandingan setelah kedua pemain siap.

### 6.3 Join Room

- Sebagai pemain, saya ingin memasukkan kode room agar dapat bergabung.
- Sebagai pemain, saya ingin menerima pesan yang jelas jika room tidak ditemukan, penuh, kedaluwarsa, atau pertandingan sudah dimulai.
- Sebagai pemain, saya ingin menandai status Ready sebelum pertandingan dimulai.

### 6.4 Pertandingan 1v1

- Sebagai pemain, saya ingin memperoleh papan yang sama dengan lawan agar pertandingan adil.
- Sebagai pemain, saya ingin melihat progres lawan tanpa melihat isi jawaban lawan.
- Sebagai pemain, saya ingin melihat heart saya dan heart lawan.
- Sebagai pemain, saya ingin memperoleh hasil pertandingan secara otomatis.
- Sebagai pemain, saya ingin dapat melakukan reconnect ketika koneksi terputus sementara.

---

## 7. Konsep Permainan

## 7.1 Aturan Dasar Sudoku

- Papan berukuran 9×9.
- Setiap baris harus berisi angka 1–9 tanpa pengulangan.
- Setiap kolom harus berisi angka 1–9 tanpa pengulangan.
- Setiap kotak 3×3 harus berisi angka 1–9 tanpa pengulangan.
- Angka bawaan puzzle tidak dapat diubah.
- Sel kosong dapat diisi oleh pemain.

## 7.2 Tingkat Kesulitan

### Easy

- Jumlah angka awal lebih banyak.
- Pola lebih mudah diselesaikan.
- Cocok untuk pemain baru.
- Rekomendasi clue: sekitar 36–45 angka.

### Medium

- Jumlah angka awal sedang.
- Membutuhkan lebih banyak eliminasi kandidat.
- Rekomendasi clue: sekitar 30–35 angka.

### Hard

- Jumlah angka awal lebih sedikit.
- Membutuhkan teknik pemecahan yang lebih kompleks.
- Rekomendasi clue: sekitar 24–29 angka.

Jumlah clue hanya salah satu faktor. Tingkat kesulitan akhir harus ditentukan berdasarkan hasil solver dan teknik yang dibutuhkan.

## 7.3 Sistem Heart

Konfigurasi awal MVP:

- Setiap pemain memulai dengan 3 heart.
- Memasukkan angka yang salah mengurangi 1 heart.
- Jawaban salah tidak disimpan sebagai jawaban final.
- Ketika heart mencapai 0, pemain kalah.
- Heart tidak berkurang saat pemain menghapus angka sendiri.
- Notes tidak mengurangi heart.
- Menggunakan hint tidak mengurangi heart, tetapi mengurangi jatah hint.
- Jumlah heart harus divalidasi dan dikendalikan oleh server.

Konfigurasi heart sebaiknya disimpan sebagai pengaturan agar dapat diubah tanpa mengubah banyak kode.

## 7.4 Sistem Hint

Konfigurasi awal:

- Easy: maksimal 3 hint.
- Medium: maksimal 2 hint.
- Hard: maksimal 1 hint.
- Hint mengisi satu sel kosong yang valid.
- Sel yang diisi melalui hint diberi indikator visual.
- Dalam mode multiplayer, penggunaan hint dapat:
  - Mengurangi skor.
  - Menambahkan penalti waktu.
  - Ditampilkan pada status lawan.

Rekomendasi MVP: hint menambahkan penalti waktu 15 detik.

## 7.5 Kondisi Menang 1v1

Pemain dinyatakan menang ketika salah satu kondisi berikut terjadi:

1. Pemain menyelesaikan seluruh papan dengan benar lebih dahulu.
2. Heart lawan mencapai 0.
3. Lawan meninggalkan pertandingan dan tidak reconnect sampai batas waktu.
4. Lawan menyerah.
5. Waktu pertandingan habis dan pemain memiliki progres valid lebih tinggi.
6. Jika progres sama, pemain dengan jumlah kesalahan lebih sedikit menang.
7. Jika masih sama, pemain dengan waktu aktif lebih rendah menang.
8. Jika seluruh parameter sama, pertandingan berakhir draw.

## 7.6 Progres Pemain

Progres dihitung dari:

```text
jumlah sel kosong yang telah diisi dengan benar
------------------------------------------------ × 100%
total sel kosong pada awal pertandingan
```

Pemain hanya dapat melihat:

- Persentase progres lawan.
- Jumlah heart lawan.
- Status koneksi lawan.
- Status hint lawan, jika diaktifkan.
- Waktu lawan, jika aturan room mengizinkan.

Pemain tidak dapat melihat angka yang dimasukkan lawan.

---

## 8. Mode Permainan

## 8.1 Solo

Alur:

1. Pemain membuka halaman Play.
2. Pemain memilih Easy, Medium, atau Hard.
3. Sistem mengambil puzzle.
4. Timer dimulai setelah papan siap.
5. Pemain menyelesaikan puzzle.
6. Sistem menampilkan hasil.
7. Statistik disimpan jika pemain login.

Fitur:

- Pause.
- Resume.
- Restart.
- New game.
- Notes.
- Undo.
- Eraser.
- Hint.
- Auto-check opsional.
- Highlight row, column, box, dan angka sama.
- Statistik waktu.

## 8.2 Multiplayer 1v1

Alur:

1. Host membuat room.
2. Sistem membuat kode room.
3. Host membagikan kode.
4. Pemain kedua bergabung.
5. Kedua pemain menekan Ready.
6. Host menekan Start atau pertandingan dimulai otomatis.
7. Server memilih satu puzzle.
8. Server mengirim puzzle yang sama kepada kedua pemain.
9. Countdown 3 detik.
10. Pertandingan dimulai.
11. Server memvalidasi setiap input final.
12. Progres dan heart disinkronkan.
13. Sistem menentukan pemenang.
14. Hasil disimpan.

## 8.3 Quick Match

Fitur ini dapat dimasukkan pada fase setelah MVP dasar.

Alur:

1. Pemain memilih tingkat kesulitan.
2. Sistem mencari lawan dengan tingkat kesulitan yang sama.
3. Jika lawan ditemukan, room dibuat otomatis.
4. Pertandingan dimulai setelah kedua pemain siap.

---

## 9. Alur Pengguna

## 9.1 Landing Page

Komponen:

- Logo dan nama produk.
- Tombol Play Solo.
- Tombol Multiplayer.
- Tombol Login atau profil.
- Ringkasan cara bermain.
- Leaderboard singkat.
- Tombol aturan permainan.
- Status server opsional.

## 9.2 Halaman Multiplayer

Pilihan:

- Create Room.
- Join Room.
- Quick Match, jika tersedia.
- Riwayat pertandingan.

## 9.3 Create Room

Field:

- Nama pemain, untuk guest.
- Difficulty.
- Jumlah heart.
- Batas waktu.
- Hint aktif atau nonaktif.
- Room privat.
- Tombol Create Room.

Hasil:

- Kode room.
- Tombol Copy Code.
- Tombol Copy Invite Link.
- Daftar pemain.
- Status Ready.
- Tombol Leave Room.
- Tombol Start.

## 9.4 Join Room

Field:

- Kode room.
- Nama pemain, untuk guest.
- Tombol Join.

Validasi:

- Kode wajib diisi.
- Kode tidak sensitif terhadap huruf besar dan kecil.
- Room harus aktif.
- Room belum penuh.
- Pertandingan belum selesai.
- Pemain tidak menggunakan identitas sesi yang sama dua kali.

## 9.5 Halaman Game

Komponen utama:

- Papan Sudoku.
- Timer.
- Heart pemain.
- Heart lawan.
- Progress pemain.
- Progress lawan.
- Nama pemain dan lawan.
- Status koneksi.
- Number pad 1–9.
- Notes.
- Eraser.
- Undo.
- Hint.
- Pause untuk solo.
- Surrender untuk multiplayer.
- Tombol suara.
- Tombol pengaturan.
- Indikator giliran tidak diperlukan karena permainan berlangsung bersamaan.

## 9.6 Halaman Result

Informasi:

- Menang, kalah, atau draw.
- Waktu pertandingan.
- Tingkat kesulitan.
- Sisa heart.
- Jumlah kesalahan.
- Jumlah hint.
- Persentase progres.
- Rating atau poin, jika sistem ranking aktif.
- Tombol Rematch.
- Tombol Main Lagi.
- Tombol Kembali ke Lobby.
- Tombol Share Result.

---

## 10. Kebutuhan Fungsional

## 10.1 Autentikasi

### FR-AUTH-001

Sistem harus mengizinkan pengguna bermain sebagai guest.

### FR-AUTH-002

Sistem harus mendukung registrasi menggunakan:

- Username.
- Email.
- Password.

### FR-AUTH-003

Password harus disimpan dalam bentuk hash aman.

### FR-AUTH-004

Sistem harus mendukung login dan logout.

### FR-AUTH-005

Username dan email harus unik.

### FR-AUTH-006

Guest harus memperoleh session ID unik.

### FR-AUTH-007

Akun yang diblokir tidak dapat login atau membuat room.

---

## 10.2 Puzzle

### FR-PUZ-001

Sistem harus menyediakan puzzle Easy, Medium, dan Hard.

### FR-PUZ-002

Setiap puzzle harus memiliki tepat satu solusi.

### FR-PUZ-003

Puzzle dan solusi tidak boleh dikirim seluruhnya ke browser.

### FR-PUZ-004

Server harus menyimpan atau mengetahui solusi untuk melakukan validasi.

### FR-PUZ-005

Untuk pertandingan 1v1, kedua pemain harus menerima puzzle yang sama.

### FR-PUZ-006

Puzzle yang baru dimainkan pengguna sebaiknya tidak langsung diulang.

### FR-PUZ-007

Administrator dapat mengaktifkan atau menonaktifkan puzzle.

---

## 10.3 Room

### FR-ROOM-001

Pengguna dapat membuat room.

### FR-ROOM-002

Setiap room memiliki kode unik, misalnya enam karakter.

### FR-ROOM-003

Room maksimal berisi dua pemain untuk mode 1v1.

### FR-ROOM-004

Host dapat mengatur difficulty, heart, waktu, dan hint.

### FR-ROOM-005

Pemain dapat bergabung menggunakan kode atau invite link.

### FR-ROOM-006

Room yang kosong harus kedaluwarsa otomatis.

### FR-ROOM-007

Room yang sudah selesai tidak dapat menerima pemain baru.

### FR-ROOM-008

Host dapat meninggalkan room sebelum permainan.

### FR-ROOM-009

Jika host keluar sebelum pertandingan, kepemilikan dapat dipindahkan ke pemain lain.

### FR-ROOM-010

Jika room belum dimulai dan tidak ada aktivitas selama 15 menit, room ditutup otomatis.

---

## 10.4 Ready dan Start

### FR-READY-001

Setiap pemain dapat mengubah status Ready atau Not Ready.

### FR-READY-002

Pertandingan hanya dapat dimulai ketika dua pemain berada di room.

### FR-READY-003

Pertandingan hanya dapat dimulai ketika kedua pemain Ready.

### FR-READY-004

Setelah Start, pengaturan room tidak dapat diubah.

### FR-READY-005

Sistem harus menampilkan countdown sebelum pertandingan.

---

## 10.5 Gameplay

### FR-GAME-001

Pemain dapat memilih sel kosong.

### FR-GAME-002

Pemain dapat mengisi angka 1–9.

### FR-GAME-003

Pemain tidak dapat mengubah clue bawaan.

### FR-GAME-004

Pemain dapat menghapus angka yang telah dimasukkan.

### FR-GAME-005

Pemain dapat mengaktifkan Notes.

### FR-GAME-006

Pemain dapat menyimpan beberapa kandidat pada satu sel.

### FR-GAME-007

Sistem dapat menghapus notes terkait secara otomatis ketika angka valid ditempatkan.

### FR-GAME-008

Pemain dapat menggunakan Undo untuk langkah lokal yang masih diizinkan.

### FR-GAME-009

Undo tidak boleh mengembalikan heart yang telah hilang.

### FR-GAME-010

Server harus memvalidasi jawaban final.

### FR-GAME-011

Jawaban salah mengurangi heart.

### FR-GAME-012

Server harus mengirim progres terbaru kepada kedua pemain.

### FR-GAME-013

Pemain dapat menyerah.

### FR-GAME-014

Pertandingan harus berhenti ketika pemenang telah ditentukan.

### FR-GAME-015

Input yang dikirim setelah pertandingan selesai harus ditolak.

---

## 10.6 Timer

### FR-TIME-001

Timer dimulai setelah countdown selesai.

### FR-TIME-002

Timer multiplayer dikendalikan berdasarkan waktu server.

### FR-TIME-003

Refresh halaman tidak boleh mereset timer.

### FR-TIME-004

Mode solo dapat di-pause.

### FR-TIME-005

Mode multiplayer tidak dapat di-pause oleh satu pemain.

### FR-TIME-006

Jika menggunakan batas waktu, server menentukan hasil saat waktu habis.

---

## 10.7 Reconnect

### FR-REC-001

Pemain yang terputus harus dapat reconnect ke pertandingan yang sama.

### FR-REC-002

Server menyimpan status pertandingan aktif.

### FR-REC-003

Batas reconnect awal disarankan 60 detik.

### FR-REC-004

Jika pemain tidak reconnect sampai batas waktu, pemain dianggap kalah.

### FR-REC-005

UI harus menampilkan status reconnect lawan.

---

## 10.8 Statistik

### FR-STAT-001

Sistem menyimpan jumlah permainan.

### FR-STAT-002

Sistem menyimpan jumlah menang, kalah, dan draw.

### FR-STAT-003

Sistem menghitung win rate.

### FR-STAT-004

Sistem menyimpan waktu terbaik per difficulty.

### FR-STAT-005

Sistem menyimpan jumlah kesalahan dan hint.

### FR-STAT-006

Guest tidak wajib memiliki statistik permanen.

---

## 10.9 Leaderboard

### FR-LB-001

Leaderboard menampilkan pemain terdaftar.

### FR-LB-002

Filter:

- Global.
- Easy.
- Medium.
- Hard.
- Mingguan.
- Bulanan.

### FR-LB-003

Urutan dapat berdasarkan:

- Rating.
- Jumlah kemenangan.
- Win rate dengan minimum pertandingan.
- Waktu penyelesaian terbaik untuk solo.

### FR-LB-004

Data leaderboard harus dipaginasi.

---

## 10.10 Rematch

### FR-REM-001

Setelah pertandingan selesai, pemain dapat mengajukan rematch.

### FR-REM-002

Rematch dimulai jika kedua pemain menyetujui.

### FR-REM-003

Rematch menggunakan puzzle baru.

### FR-REM-004

Room dapat mempertahankan pengaturan sebelumnya.

---

## 11. Kebutuhan Nonfungsional

## 11.1 Performa

- Initial page load ditargetkan kurang dari 3 detik pada koneksi 4G normal.
- Respons API umum ditargetkan kurang dari 500 ms.
- Sinkronisasi event multiplayer ditargetkan kurang dari 1 detik.
- Query database harus menggunakan index yang sesuai.
- Puzzle sebaiknya diambil dari puzzle bank untuk mengurangi beban CPU shared hosting.
- Asset frontend harus dikompresi dan menggunakan cache browser.

## 11.2 Skalabilitas

MVP ditargetkan untuk skala awal:

- 100–500 pengguna aktif harian.
- 20–100 pertandingan bersamaan, bergantung kapasitas hosting.
- Sistem harus dapat dipindahkan ke VPS tanpa perubahan besar pada logika bisnis.

## 11.3 Availability

- Target ketersediaan awal: 99%.
- Sistem harus memiliki halaman error yang jelas.
- Kegagalan koneksi real-time harus mencoba reconnect otomatis.
- Server tidak boleh kehilangan hasil pertandingan yang telah selesai.

## 11.4 Keamanan

- Seluruh input harus divalidasi di server.
- Gunakan prepared statement atau parameterized query.
- Password di-hash dengan Argon2 atau bcrypt.
- Gunakan HTTPS.
- Cookie autentikasi menggunakan `HttpOnly`, `Secure`, dan `SameSite`.
- Jika menggunakan JWT, token harus memiliki masa berlaku dan mekanisme refresh yang aman.
- Batasi percobaan login.
- Batasi pembuatan dan percobaan join room.
- Socket harus diautentikasi.
- Jangan mempercayai skor, heart, progress, atau jawaban dari client.
- Cegah SQL Injection, XSS, CSRF, IDOR, dan session hijacking.
- Invite code harus cukup acak dan sulit ditebak.
- Log tidak boleh menyimpan password atau token mentah.
- CORS hanya mengizinkan domain aplikasi.
- Terapkan Content Security Policy yang sesuai dengan penggunaan CDN.

## 11.5 Kompatibilitas

Browser minimum:

- Chrome versi modern.
- Edge versi modern.
- Firefox versi modern.
- Safari versi modern.
- Browser Android modern.
- Safari iOS modern.

## 11.6 Responsivitas

- Mobile minimum 360 px.
- Tablet.
- Desktop.
- Number pad harus mudah disentuh.
- Ukuran sel harus tetap proporsional.
- Tidak boleh terjadi horizontal scrolling pada halaman game normal.

## 11.7 Aksesibilitas

- Kontras teks memadai.
- Fokus keyboard terlihat.
- Papan dapat digunakan melalui keyboard desktop.
- Tombol memiliki label yang jelas.
- Status menang, kalah, error, dan perubahan heart tidak hanya bergantung pada warna.
- Sediakan opsi mematikan suara dan animasi berlebihan.

---

## 12. Arsitektur Teknis

## 12.1 Tech Stack

### Frontend

- HTML5.
- CSS.
- JavaScript ES Modules.
- Tailwind CSS melalui CDN.
- Socket.IO Client melalui CDN.
- Fetch API untuk REST API.
- Local Storage untuk preferensi non-sensitif.
- Session atau cookie untuk autentikasi.

### Backend

- Node.js.
- Express.js.
- Socket.IO.
- MySQL2.
- Validation library, misalnya Zod, Joi, atau express-validator.
- bcrypt atau Argon2.
- Helmet.
- Rate limiter.
- Session store MySQL atau JWT sesuai keputusan implementasi.
- Pino atau Winston untuk logging.

### Database

- MySQL.
- InnoDB.
- UTF8MB4.
- Foreign key.
- Index pada kolom pencarian dan relasi utama.

### Deployment

- cPanel.
- Node.js Application Manager atau fitur sejenis dari penyedia hosting.
- MySQL Database.
- HTTPS.
- Domain atau subdomain.
- Cron job untuk cleanup room, session, dan data sementara.
- File `.env` untuk konfigurasi rahasia.

---

## 12.2 Catatan Kompatibilitas cPanel

Sebelum implementasi, hosting harus dipastikan memiliki:

1. Dukungan menjalankan aplikasi Node.js.
2. Dukungan versi Node.js LTS yang masih aktif atau versi LTS yang disediakan hosting.
3. Kemampuan menjalankan proses aplikasi secara persisten.
4. Dukungan koneksi WebSocket atau Socket.IO.
5. Akses membuat database dan user MySQL.
6. HTTPS atau AutoSSL.
7. Cron job.
8. Kemampuan mengatur environment variable.
9. Batas proses, RAM, CPU, dan koneksi yang cukup.

Karena dukungan WebSocket pada shared hosting dapat berbeda, Socket.IO harus dikonfigurasi agar dapat menggunakan fallback HTTP long-polling. Apabila hosting tidak mendukung proses Node.js persisten, deployment perlu dipindahkan ke VPS, cloud platform, atau hosting Node.js khusus.

---

## 12.3 Diagram Arsitektur

```text
Browser
  |
  | HTTPS
  v
Frontend HTML + Tailwind CDN + JavaScript
  |
  | REST API
  | Socket.IO WebSocket / Long Polling
  v
Node.js + Express + Socket.IO
  |
  | mysql2
  v
MySQL Database
```

## 12.4 Prinsip Server Authoritative

Server menjadi sumber kebenaran untuk:

- Puzzle pertandingan.
- Solusi.
- Heart.
- Jumlah kesalahan.
- Hint.
- Waktu mulai dan selesai.
- Progress.
- Status pemain.
- Status pertandingan.
- Penentuan pemenang.
- Rating dan statistik.

Client hanya bertanggung jawab untuk:

- Menampilkan UI.
- Mengirim aksi pemain.
- Menampilkan respons server.
- Menyimpan preferensi tampilan non-sensitif.

---

## 13. Struktur Frontend

```text
/public
  /assets
    /audio
    /icons
    /images
  /css
    app.css
  /js
    api.js
    auth.js
    socket.js
    storage.js
    utils.js
    /pages
      home.js
      solo.js
      lobby.js
      multiplayer.js
      result.js
      profile.js
    /game
      board.js
      controls.js
      notes.js
      timer.js
      renderer.js
      state.js
  index.html
  login.html
  register.html
  solo.html
  multiplayer.html
  room.html
  game.html
  result.html
  profile.html
  leaderboard.html
```

Catatan:

- Tailwind CDN cocok untuk MVP dan prototipe.
- Untuk produksi dengan optimasi ukuran CSS lebih baik, Tailwind build process dapat dipertimbangkan pada fase berikutnya.
- Karena permintaan menggunakan CDN, aplikasi tidak wajib menggunakan bundler.

---

## 14. Struktur Backend

```text
/src
  app.js
  server.js
  /config
    env.js
    database.js
    socket.js
  /controllers
    auth.controller.js
    room.controller.js
    game.controller.js
    puzzle.controller.js
    user.controller.js
    leaderboard.controller.js
  /services
    auth.service.js
    room.service.js
    game.service.js
    puzzle.service.js
    rating.service.js
    cleanup.service.js
  /repositories
    user.repository.js
    room.repository.js
    match.repository.js
    puzzle.repository.js
  /routes
    auth.routes.js
    room.routes.js
    game.routes.js
    user.routes.js
    leaderboard.routes.js
  /sockets
    room.socket.js
    game.socket.js
  /middlewares
    auth.middleware.js
    guest.middleware.js
    validate.middleware.js
    rateLimit.middleware.js
    error.middleware.js
  /validators
    auth.validator.js
    room.validator.js
    game.validator.js
  /utils
    roomCode.js
    sudoku.js
    time.js
    logger.js
  /jobs
    cleanupRooms.job.js
    cleanupSessions.job.js
```

---

## 15. Model Data dan Database

## 15.1 Tabel `users`

| Kolom | Tipe | Keterangan |
|---|---|---|
| id | BIGINT UNSIGNED | Primary key |
| username | VARCHAR(30) | Unik |
| email | VARCHAR(150) | Unik |
| password_hash | VARCHAR(255) | Hash password |
| avatar_url | VARCHAR(255) | Opsional |
| rating | INT | Rating multiplayer |
| status | ENUM | active, blocked |
| created_at | DATETIME | Tanggal dibuat |
| updated_at | DATETIME | Tanggal diubah |
| last_login_at | DATETIME | Login terakhir |

## 15.2 Tabel `guest_sessions`

| Kolom | Tipe | Keterangan |
|---|---|---|
| id | CHAR(36) | UUID |
| display_name | VARCHAR(30) | Nama guest |
| session_token_hash | VARCHAR(255) | Token hash |
| created_at | DATETIME | Dibuat |
| expires_at | DATETIME | Kedaluwarsa |
| last_seen_at | DATETIME | Aktivitas terakhir |

## 15.3 Tabel `puzzles`

| Kolom | Tipe | Keterangan |
|---|---|---|
| id | BIGINT UNSIGNED | Primary key |
| difficulty | ENUM | easy, medium, hard |
| puzzle_string | CHAR(81) | Nol untuk sel kosong |
| solution_string | CHAR(81) | Solusi lengkap |
| clue_count | TINYINT | Jumlah clue |
| difficulty_score | INT | Skor hasil solver |
| is_active | BOOLEAN | Aktif atau tidak |
| created_at | DATETIME | Dibuat |

Catatan keamanan:

- Endpoint publik tidak boleh mengembalikan `solution_string`.
- Akses solusi hanya dilakukan pada service backend.

## 15.4 Tabel `rooms`

| Kolom | Tipe | Keterangan |
|---|---|---|
| id | BIGINT UNSIGNED | Primary key |
| room_code | VARCHAR(10) | Unik |
| host_player_key | VARCHAR(100) | User ID atau guest ID |
| difficulty | ENUM | easy, medium, hard |
| max_hearts | TINYINT | Default 3 |
| time_limit_seconds | INT | Null jika tanpa batas |
| hints_enabled | BOOLEAN | Status hint |
| status | ENUM | waiting, countdown, playing, finished, expired |
| puzzle_id | BIGINT UNSIGNED | Puzzle pertandingan |
| created_at | DATETIME | Dibuat |
| started_at | DATETIME | Mulai |
| finished_at | DATETIME | Selesai |
| expires_at | DATETIME | Kedaluwarsa |

## 15.5 Tabel `room_players`

| Kolom | Tipe | Keterangan |
|---|---|---|
| id | BIGINT UNSIGNED | Primary key |
| room_id | BIGINT UNSIGNED | Foreign key |
| user_id | BIGINT UNSIGNED | Null untuk guest |
| guest_session_id | CHAR(36) | Null untuk akun |
| display_name | VARCHAR(30) | Nama tampilan |
| player_slot | TINYINT | 1 atau 2 |
| is_host | BOOLEAN | Host |
| is_ready | BOOLEAN | Status ready |
| connection_status | ENUM | online, reconnecting, offline |
| joined_at | DATETIME | Bergabung |
| left_at | DATETIME | Keluar |

Constraint:

- Satu pemain hanya menggunakan salah satu dari `user_id` atau `guest_session_id`.
- Kombinasi `room_id` dan `player_slot` harus unik.

## 15.6 Tabel `matches`

| Kolom | Tipe | Keterangan |
|---|---|---|
| id | BIGINT UNSIGNED | Primary key |
| room_id | BIGINT UNSIGNED | Room asal |
| puzzle_id | BIGINT UNSIGNED | Puzzle |
| difficulty | ENUM | Difficulty |
| status | ENUM | active, completed, cancelled |
| winner_player_id | BIGINT UNSIGNED | Null jika draw |
| result_type | ENUM | completed, hearts_zero, timeout, surrender, disconnect, draw |
| started_at | DATETIME | Mulai |
| ended_at | DATETIME | Selesai |
| duration_seconds | INT | Durasi |

## 15.7 Tabel `match_players`

| Kolom | Tipe | Keterangan |
|---|---|---|
| id | BIGINT UNSIGNED | Primary key |
| match_id | BIGINT UNSIGNED | Match |
| user_id | BIGINT UNSIGNED | Null untuk guest |
| guest_session_id | CHAR(36) | Null untuk akun |
| display_name | VARCHAR(30) | Snapshot nama |
| board_state | CHAR(81) | State angka saat ini |
| notes_state | JSON | Notes, jika perlu server-side |
| hearts_remaining | TINYINT | Heart tersisa |
| mistakes_count | SMALLINT | Jumlah salah |
| hints_used | SMALLINT | Hint terpakai |
| correct_cells | SMALLINT | Progres |
| is_winner | BOOLEAN | Pemenang |
| rating_before | INT | Rating awal |
| rating_after | INT | Rating akhir |
| last_action_at | DATETIME | Aksi terakhir |

## 15.8 Tabel `moves`

| Kolom | Tipe | Keterangan |
|---|---|---|
| id | BIGINT UNSIGNED | Primary key |
| match_id | BIGINT UNSIGNED | Match |
| match_player_id | BIGINT UNSIGNED | Pemain |
| cell_index | TINYINT | 0–80 |
| value | TINYINT | 0–9 |
| action_type | ENUM | place, erase, hint |
| is_correct | BOOLEAN | Hasil validasi |
| hearts_after | TINYINT | Heart setelah aksi |
| created_at | DATETIME | Waktu aksi |

Untuk menghemat storage, tabel `moves` dapat menyimpan hanya aksi final dan tidak menyimpan perubahan notes.

## 15.9 Tabel `solo_games`

| Kolom | Tipe | Keterangan |
|---|---|---|
| id | BIGINT UNSIGNED | Primary key |
| user_id | BIGINT UNSIGNED | Pemain |
| puzzle_id | BIGINT UNSIGNED | Puzzle |
| difficulty | ENUM | Difficulty |
| board_state | CHAR(81) | State |
| hearts_remaining | TINYINT | Heart |
| mistakes_count | SMALLINT | Kesalahan |
| hints_used | SMALLINT | Hint |
| status | ENUM | active, completed, abandoned |
| started_at | DATETIME | Mulai |
| completed_at | DATETIME | Selesai |
| duration_seconds | INT | Durasi |

## 15.10 Tabel `user_stats`

| Kolom | Tipe | Keterangan |
|---|---|---|
| user_id | BIGINT UNSIGNED | Primary dan foreign key |
| total_matches | INT | Total match |
| wins | INT | Menang |
| losses | INT | Kalah |
| draws | INT | Draw |
| solo_completed | INT | Solo selesai |
| best_easy_seconds | INT | Rekor |
| best_medium_seconds | INT | Rekor |
| best_hard_seconds | INT | Rekor |
| total_mistakes | INT | Total salah |
| total_hints | INT | Total hint |
| updated_at | DATETIME | Update |

---

## 16. REST API

Base URL:

```text
/api/v1
```

## 16.1 Authentication

### `POST /auth/register`

Request:

```json
{
  "username": "player01",
  "email": "player@example.com",
  "password": "password-kuat"
}
```

### `POST /auth/login`

```json
{
  "email": "player@example.com",
  "password": "password-kuat"
}
```

### `POST /auth/logout`

Mengakhiri sesi.

### `POST /auth/guest`

```json
{
  "displayName": "Connor"
}
```

### `GET /auth/me`

Mengambil informasi sesi saat ini.

---

## 16.2 Puzzle dan Solo

### `POST /solo-games`

```json
{
  "difficulty": "medium"
}
```

Response:

```json
{
  "gameId": 120,
  "difficulty": "medium",
  "puzzle": "530070000600195000...",
  "hearts": 3,
  "hintsRemaining": 2,
  "startedAt": "2026-07-29T03:00:00.000Z"
}
```

### `POST /solo-games/:gameId/moves`

```json
{
  "cellIndex": 12,
  "value": 7,
  "actionType": "place"
}
```

### `POST /solo-games/:gameId/hint`

### `POST /solo-games/:gameId/pause`

### `POST /solo-games/:gameId/resume`

### `GET /solo-games/:gameId`

---

## 16.3 Room

### `POST /rooms`

```json
{
  "difficulty": "hard",
  "maxHearts": 3,
  "timeLimitSeconds": 900,
  "hintsEnabled": true
}
```

Response:

```json
{
  "roomId": 44,
  "roomCode": "A7K9P2",
  "invitePath": "/join/A7K9P2",
  "status": "waiting"
}
```

### `POST /rooms/join`

```json
{
  "roomCode": "A7K9P2"
}
```

### `GET /rooms/:roomCode`

### `POST /rooms/:roomCode/ready`

```json
{
  "ready": true
}
```

### `POST /rooms/:roomCode/start`

### `POST /rooms/:roomCode/leave`

### `POST /rooms/:roomCode/rematch`

---

## 16.4 Match

### `GET /matches/:matchId`

### `POST /matches/:matchId/moves`

Endpoint REST dapat menjadi fallback jika Socket.IO tidak tersedia.

### `POST /matches/:matchId/hint`

### `POST /matches/:matchId/surrender`

### `GET /matches/:matchId/result`

---

## 16.5 User dan Leaderboard

### `GET /users/me/profile`

### `PATCH /users/me/profile`

### `GET /users/me/stats`

### `GET /users/me/matches`

### `GET /leaderboard`

Query:

```text
?type=rating&period=weekly&difficulty=hard&page=1
```

---

## 17. Socket.IO Events

## 17.1 Client ke Server

| Event | Payload | Fungsi |
|---|---|---|
| `room:join` | roomCode | Bergabung channel room |
| `room:ready` | ready | Mengubah status ready |
| `room:start` | roomCode | Host memulai |
| `game:move` | matchId, cellIndex, value | Memasukkan angka |
| `game:erase` | matchId, cellIndex | Menghapus angka |
| `game:hint` | matchId | Menggunakan hint |
| `game:surrender` | matchId | Menyerah |
| `game:sync` | matchId | Meminta state terbaru |
| `game:rematch` | matchId | Mengajukan rematch |

## 17.2 Server ke Client

| Event | Payload | Fungsi |
|---|---|---|
| `room:state` | room state | Sinkronisasi lobby |
| `room:player_joined` | player | Pemain masuk |
| `room:player_left` | player | Pemain keluar |
| `room:countdown` | seconds | Countdown |
| `game:started` | game state | Pertandingan mulai |
| `game:move_result` | result | Hasil input sendiri |
| `game:opponent_progress` | progress, hearts | Progres lawan |
| `game:player_disconnected` | reconnect deadline | Lawan terputus |
| `game:player_reconnected` | player | Lawan kembali |
| `game:finished` | result | Pertandingan selesai |
| `game:error` | code, message | Error gameplay |
| `rematch:requested` | player | Permintaan rematch |
| `rematch:started` | new match | Rematch dimulai |

## 17.3 Idempotensi Aksi

Setiap aksi penting sebaiknya memiliki `actionId` unik.

Contoh:

```json
{
  "actionId": "8e01d9d4-3d13-4a90-9fe1-f5836f677e1c",
  "matchId": 222,
  "cellIndex": 12,
  "value": 7
}
```

Server harus menolak atau mengembalikan hasil lama ketika menerima `actionId` yang sama agar retry jaringan tidak mengurangi heart dua kali.

---

## 18. State Machine

## 18.1 Room State

```text
WAITING
  |
  | dua pemain + ready
  v
COUNTDOWN
  |
  v
PLAYING
  |
  v
FINISHED
```

State tambahan:

```text
WAITING -> EXPIRED
COUNTDOWN -> WAITING, jika pemain keluar sebelum mulai
PLAYING -> CANCELLED, hanya untuk kegagalan sistem
```

## 18.2 Player Connection State

```text
ONLINE -> RECONNECTING -> ONLINE
ONLINE -> RECONNECTING -> OFFLINE
```

## 18.3 Match Result Type

- `completed`
- `hearts_zero`
- `timeout`
- `surrender`
- `disconnect`
- `draw`
- `cancelled`

---

## 19. Validasi Gameplay

Ketika server menerima input angka:

1. Verifikasi sesi pemain.
2. Verifikasi pemain merupakan bagian dari match.
3. Verifikasi match berstatus active.
4. Verifikasi `actionId` belum diproses.
5. Verifikasi `cellIndex` antara 0–80.
6. Verifikasi nilai antara 1–9.
7. Verifikasi sel bukan clue bawaan.
8. Verifikasi sel belum berisi angka benar yang terkunci.
9. Bandingkan nilai dengan solusi.
10. Jika benar:
    - Simpan nilai.
    - Tambahkan progres.
    - Periksa apakah papan selesai.
11. Jika salah:
    - Kurangi heart.
    - Tambahkan jumlah kesalahan.
    - Periksa apakah heart mencapai 0.
12. Simpan move.
13. Kirim hasil ke pemain.
14. Kirim progres ringkas ke lawan.
15. Jika kondisi menang terpenuhi, selesaikan match dalam transaksi database.

---

## 20. Rating Multiplayer

Rating tidak wajib pada versi pertama, tetapi struktur database disiapkan.

Rekomendasi:

- Rating awal: 1000.
- Menggunakan Elo sederhana.
- Pertandingan guest tidak memengaruhi rating.
- Draw memberikan perubahan kecil atau nol, bergantung perbedaan rating.
- Surrender dan disconnect dihitung sebagai kekalahan.
- Match yang dibatalkan oleh server tidak memengaruhi rating.

Rumus dasar:

```text
Expected A = 1 / (1 + 10 ^ ((Rating B - Rating A) / 400))
New Rating A = Rating A + K × (Actual Score - Expected A)
```

Konfigurasi awal:

- K = 32.
- Menang = 1.
- Draw = 0,5.
- Kalah = 0.

---

## 21. UI dan UX

## 21.1 Gaya Visual

- Modern.
- Bersih.
- Fokus pada papan.
- Ramah mobile.
- Warna konflik dan kesalahan mudah dibedakan.
- Animasi ringan.
- Tidak terlalu banyak elemen yang mengganggu.

## 21.2 Status Warna

- Sel aktif.
- Baris dan kolom aktif.
- Box aktif.
- Angka sama.
- Angka salah.
- Angka dari hint.
- Clue bawaan.
- Notes.

Status tidak boleh hanya dibedakan melalui warna; tambahkan ikon, pola, outline, atau teks bila diperlukan.

## 21.3 Interaksi Keyboard

- Angka 1–9 untuk input.
- Backspace atau Delete untuk menghapus.
- Tombol panah untuk berpindah sel.
- N untuk toggle notes.
- H untuk hint, dengan dialog konfirmasi.
- Esc untuk menutup modal.

## 21.4 Audio

Audio opsional:

- Klik angka.
- Jawaban benar.
- Jawaban salah.
- Heart berkurang.
- Countdown.
- Menang.
- Kalah.

Preferensi audio disimpan di Local Storage.

---

## 22. Penanganan Error

Kode error contoh:

| Kode | Pesan |
|---|---|
| `ROOM_NOT_FOUND` | Room tidak ditemukan |
| `ROOM_FULL` | Room sudah penuh |
| `ROOM_EXPIRED` | Room sudah kedaluwarsa |
| `MATCH_ALREADY_STARTED` | Pertandingan sudah dimulai |
| `PLAYER_NOT_READY` | Kedua pemain belum siap |
| `INVALID_MOVE` | Langkah tidak valid |
| `CELL_LOCKED` | Sel tidak dapat diubah |
| `MATCH_FINISHED` | Pertandingan sudah selesai |
| `NOT_ROOM_HOST` | Hanya host yang dapat memulai |
| `RATE_LIMITED` | Terlalu banyak permintaan |
| `AUTH_REQUIRED` | Silakan login kembali |
| `CONNECTION_LOST` | Koneksi terputus, mencoba menyambungkan kembali |

Pesan UI harus ramah pengguna, sedangkan detail teknis hanya disimpan pada log server.

---

## 23. Anti-Cheat

- Solusi Sudoku tidak dikirim ke client.
- Semua jawaban divalidasi server.
- Heart dan progres tidak dihitung client.
- Waktu match berdasarkan server.
- Event socket harus memverifikasi identitas dan room.
- Batasi jumlah input per detik.
- Abaikan input pada sel yang sudah benar dan terkunci.
- Catat pola input yang tidak wajar.
- Gunakan action ID untuk mencegah duplicate event.
- Jangan menggunakan data Local Storage sebagai sumber kebenaran.
- Jangan menerima `winner`, `score`, atau `hearts` dari payload client.
- Obfuscation JavaScript tidak dianggap sebagai mekanisme keamanan.

---

## 24. Logging dan Monitoring

Log minimum:

- Startup dan shutdown aplikasi.
- Database connection error.
- Login gagal berulang.
- Pembuatan room.
- Join room gagal.
- Match dimulai dan selesai.
- Disconnect dan reconnect.
- Error Socket.IO.
- Error API 5xx.
- Aktivitas rate limit.
- Cleanup room.

Log harus mencantumkan correlation ID atau request ID, tetapi tidak boleh menyimpan password, token mentah, atau informasi sensitif.

---

## 25. Cron Job dan Cleanup

Cron job disarankan menjalankan tugas berikut:

### Setiap 5 Menit

- Menutup room waiting yang tidak aktif.
- Mengubah room lama menjadi expired.
- Membersihkan guest session kedaluwarsa.

### Setiap Hari

- Membersihkan log lama.
- Memperbarui leaderboard agregat jika menggunakan cache.
- Memeriksa match active yang tidak memiliki aktivitas tidak wajar.
- Backup database sesuai fasilitas hosting.

---

## 26. Deployment cPanel

## 26.1 Struktur Deployment

```text
/home/account/
  sudoku-app/
    src/
    public/
    package.json
    package-lock.json
    .env
    app.js
```

Document root domain atau subdomain diarahkan ke folder public atau melalui konfigurasi aplikasi Node.js sesuai fitur hosting.

## 26.2 Environment Variables

```env
NODE_ENV=production
PORT=3000
APP_URL=https://sudoku.example.com

DB_HOST=localhost
DB_PORT=3306
DB_NAME=sudoku_db
DB_USER=sudoku_user
DB_PASSWORD=ubah_dengan_password_kuat

SESSION_SECRET=ubah_dengan_secret_panjang
ROOM_CODE_SECRET=ubah_dengan_secret_lain
CORS_ORIGIN=https://sudoku.example.com

RECONNECT_GRACE_SECONDS=60
ROOM_EXPIRY_MINUTES=15
DEFAULT_HEARTS=3
```

## 26.3 Langkah Deployment

1. Buat domain atau subdomain.
2. Aktifkan HTTPS.
3. Buat database MySQL dan user.
4. Import migration atau schema.
5. Upload source code.
6. Jalankan `npm install --production`.
7. Buat aplikasi Node.js dari cPanel.
8. Tentukan startup file.
9. Tambahkan environment variable.
10. Restart aplikasi.
11. Uji endpoint health.
12. Uji Socket.IO atau fallback polling.
13. Atur cron job.
14. Uji create room, join room, reconnect, dan hasil match.
15. Aktifkan backup database.

## 26.4 Health Check

Endpoint:

```text
GET /api/v1/health
```

Response:

```json
{
  "status": "ok",
  "database": "connected",
  "realtime": "available",
  "timestamp": "2026-07-29T03:00:00.000Z"
}
```

Jangan menampilkan secret, versi dependency detail, atau informasi server sensitif.

---

## 27. Migration dan Seed

Migration minimum:

1. Create users.
2. Create guest sessions.
3. Create puzzles.
4. Create rooms.
5. Create room players.
6. Create matches.
7. Create match players.
8. Create moves.
9. Create solo games.
10. Create user stats.
11. Create indexes.
12. Create foreign keys.

Seed minimum:

- 100 puzzle Easy.
- 100 puzzle Medium.
- 100 puzzle Hard.
- Seluruh puzzle memiliki solusi tunggal.
- Akun administrator awal dibuat melalui environment variable atau script aman.

---

## 28. Testing

## 28.1 Unit Test

- Validasi Sudoku.
- Penghitungan progress.
- Pengurangan heart.
- Kondisi kemenangan.
- Rating Elo.
- Room code generator.
- Timer dan timeout.
- Reconnect deadline.

## 28.2 Integration Test

- Register dan login.
- Guest session.
- Create room.
- Join room.
- Ready.
- Start match.
- Input benar.
- Input salah.
- Heart 0.
- Hint.
- Surrender.
- Disconnect.
- Reconnect.
- Finish match.
- Update stats.

## 28.3 End-to-End Test

Skenario utama:

1. Player A membuat room.
2. Player B bergabung.
3. Keduanya Ready.
4. Match dimulai.
5. Kedua papan sama.
6. Player A mengisi angka benar.
7. Player B melihat progres berubah.
8. Player B melakukan tiga kesalahan.
9. Player B kehilangan semua heart.
10. Player A menang.
11. Hasil tersimpan.
12. Keduanya melakukan rematch.

## 28.4 Security Test

- SQL injection.
- XSS pada username dan display name.
- CSRF.
- Brute force login.
- Brute force room code.
- Manipulasi match ID.
- Manipulasi player ID.
- Mengirim heart palsu.
- Mengirim winner palsu.
- Mengirim input setelah selesai.
- Replay action.
- Socket tanpa autentikasi.
- Akses room lain.
- Rate limit bypass.

## 28.5 Load Test

Uji bertahap:

- 10 match bersamaan.
- 25 match bersamaan.
- 50 match bersamaan.
- 100 match bersamaan jika kapasitas hosting memungkinkan.

Pantau:

- CPU.
- RAM.
- Event loop lag.
- Database connection.
- Waktu respons.
- Socket disconnect.
- Error rate.

---

## 29. Acceptance Criteria MVP

MVP diterima jika:

1. Pengguna dapat bermain solo pada tiga difficulty.
2. Pengguna dapat membuat room.
3. Pengguna lain dapat bergabung menggunakan kode.
4. Room menolak pemain ketiga.
5. Kedua pemain dapat Ready.
6. Host dapat memulai setelah dua pemain Ready.
7. Kedua pemain menerima puzzle yang sama.
8. Input benar memperbarui progres.
9. Input salah mengurangi heart.
10. Heart 0 menghasilkan kekalahan.
11. Pemain yang menyelesaikan papan lebih dahulu menang.
12. Disconnect sementara dapat dipulihkan.
13. Disconnect melewati batas waktu menghasilkan kekalahan.
14. Result tersimpan.
15. Rematch menggunakan puzzle baru.
16. Statistik akun diperbarui.
17. Layout berjalan baik pada mobile dan desktop.
18. Solusi tidak tersedia pada source atau network response client.
19. Semua aksi penting divalidasi server.
20. Aplikasi berhasil berjalan melalui cPanel dengan MySQL.
21. Socket.IO berjalan melalui WebSocket atau fallback long-polling.
22. Error utama ditampilkan dengan pesan yang jelas.
23. Tidak ditemukan kerentanan kritis pada pengujian dasar.

---

## 30. Prioritas Pengembangan

## Fase 1 — Fondasi

- Setup project.
- Database.
- Autentikasi guest dan akun.
- Puzzle bank.
- Mode solo.
- UI papan.
- Heart, notes, undo, hint, dan timer.

## Fase 2 — Room

- Create room.
- Join room.
- Invite link.
- Lobby.
- Ready.
- Host control.
- Room expiration.

## Fase 3 — Multiplayer Real-Time

- Socket.IO.
- Server-authoritative gameplay.
- Progress lawan.
- Heart lawan.
- Countdown.
- Kondisi menang.
- Reconnect.
- Surrender.
- Result.

## Fase 4 — Akun dan Retensi

- Statistik.
- Riwayat.
- Leaderboard.
- Rematch.
- Profil.
- Rating.

## Fase 5 — Hardening

- Rate limit.
- Security review.
- Load test.
- Logging.
- Cron cleanup.
- Backup.
- Optimasi cPanel.

---

## 31. Backlog Setelah MVP

- Quick Match.
- Daily Challenge.
- Friend list.
- Chat room terbatas.
- Spectator mode.
- Turnamen.
- Achievement.
- Streak harian.
- Custom theme.
- Dark mode.
- PWA dan installable web app.
- Push notification.
- Puzzle 4×4 dan 16×16.
- Cooperative Sudoku.
- Ranked season.
- Replay pertandingan.
- Admin dashboard.
- Moderation tools.
- Localization Inggris dan Indonesia.
- Sistem anti-abuse yang lebih lanjut.

---

## 32. Risiko dan Mitigasi

| Risiko | Dampak | Mitigasi |
|---|---|---|
| Hosting tidak mendukung WebSocket | Multiplayer tidak real-time | Gunakan Socket.IO polling fallback atau pindah hosting |
| Resource shared hosting terbatas | Lag dan disconnect | Puzzle bank, query index, connection pool kecil, load test |
| Client memanipulasi data | Cheat | Server authoritative |
| Room code ditebak | Pengguna asing masuk | Kode acak, rate limit, opsi password room |
| Puzzle memiliki lebih dari satu solusi | Match tidak adil | Validasi solver sebelum seed |
| Socket reconnect gagal | Pemain kalah tidak adil | Grace period dan state tersimpan |
| Query database terlalu sering | Beban tinggi | Event throttling, batching, cache state aktif |
| Tailwind CDN lambat atau diblokir | UI rusak | Sediakan fallback CSS lokal pada fase produksi |
| Data match tidak konsisten | Hasil salah | Gunakan transaction dan row locking saat finalisasi |
| Cron tidak berjalan | Room lama menumpuk | Cleanup saat startup dan request tertentu sebagai fallback |

---

## 33. Keputusan Teknis yang Direkomendasikan

1. Gunakan Express.js dan Socket.IO.
2. Gunakan MySQL2 dengan connection pool.
3. Gunakan session cookie untuk aplikasi web jika frontend dan backend berada pada domain yang sama.
4. Gunakan puzzle bank yang sudah divalidasi, bukan membuat puzzle berat setiap pertandingan.
5. Simpan active match state pada database untuk mendukung reconnect.
6. Simpan event penting, bukan setiap perubahan visual.
7. Gunakan Socket.IO dengan transport `websocket` dan `polling`.
8. Jadikan server sebagai satu-satunya pihak yang menentukan benar, salah, heart, progres, dan kemenangan.
9. Gunakan migration terstruktur.
10. Siapkan jalur migrasi dari shared hosting ke VPS apabila trafik meningkat.

---

## 34. Definition of Done

Sebuah fitur dianggap selesai ketika:

- Kebutuhan fungsional terpenuhi.
- Validasi backend tersedia.
- UI mobile dan desktop selesai.
- Error state ditangani.
- Unit atau integration test utama tersedia.
- Tidak ada secret di source code.
- Query database sudah menggunakan parameter.
- Dokumentasi endpoint diperbarui.
- Acceptance criteria fitur lulus.
- Fitur berhasil diuji di environment cPanel staging.

---

## 35. Ringkasan MVP

Versi MVP Sudoku Duel berisi:

- Sudoku 9×9.
- Easy, Medium, dan Hard.
- Solo.
- Multiplayer 1v1.
- Create room dan join room.
- Ready dan countdown.
- Heart.
- Notes.
- Eraser.
- Undo.
- Hint.
- Timer.
- Progres lawan.
- Reconnect.
- Win, lose, dan draw.
- Rematch.
- Statistik dasar.
- Leaderboard dasar.
- Node.js, Express, Socket.IO, MySQL.
- Frontend HTML, JavaScript, dan Tailwind CDN.
- Deployment melalui cPanel.

Dokumen ini dapat menjadi acuan bagi product owner, UI/UX designer, frontend developer, backend developer, QA, dan administrator deployment.
