Backend
This commit is contained in:
parent
9ea5612980
commit
f0b5ae87b6
244
2048.js
244
2048.js
@ -1,244 +0,0 @@
|
|||||||
var board;
|
|
||||||
var score = 0;
|
|
||||||
var rows = 4;
|
|
||||||
var columns = 4;
|
|
||||||
var gameOver = false;
|
|
||||||
|
|
||||||
window.onload = function () {
|
|
||||||
setGame();
|
|
||||||
document.getElementById("replayBtn").addEventListener("click", restartGame);
|
|
||||||
};
|
|
||||||
|
|
||||||
function setGame() {
|
|
||||||
board = [
|
|
||||||
[0, 0, 0, 0],
|
|
||||||
[0, 0, 0, 0],
|
|
||||||
[0, 0, 0, 0],
|
|
||||||
[0, 0, 0, 0],
|
|
||||||
];
|
|
||||||
|
|
||||||
score = 0;
|
|
||||||
gameOver = false;
|
|
||||||
document.getElementById("score").innerText = score;
|
|
||||||
document.getElementById("board").innerHTML = "";
|
|
||||||
|
|
||||||
for (let r = 0; r < rows; r++) {
|
|
||||||
for (let c = 0; c < columns; c++) {
|
|
||||||
let tile = document.createElement("div");
|
|
||||||
tile.id = r.toString() + "-" + c.toString();
|
|
||||||
let num = board[r][c];
|
|
||||||
updateTile(tile, num);
|
|
||||||
document.getElementById("board").append(tile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setTwo();
|
|
||||||
setTwo();
|
|
||||||
}
|
|
||||||
|
|
||||||
function restartGame() {
|
|
||||||
document.getElementById("gameOverOverlay").style.display = "none";
|
|
||||||
setGame();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateTile(tile, num) {
|
|
||||||
tile.innerText = "";
|
|
||||||
tile.classList.value = "";
|
|
||||||
tile.classList.add("tile");
|
|
||||||
if (num > 0) {
|
|
||||||
tile.innerText = num.toString();
|
|
||||||
if (num <= 4096) {
|
|
||||||
tile.classList.add("x" + num.toString());
|
|
||||||
} else {
|
|
||||||
tile.classList.add("x8192");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("keyup", (e) => {
|
|
||||||
if (gameOver) return;
|
|
||||||
|
|
||||||
let moved = false;
|
|
||||||
|
|
||||||
if (e.code == "ArrowLeft") {
|
|
||||||
moved = slideLeft();
|
|
||||||
} else if (e.code == "ArrowRight") {
|
|
||||||
moved = slideRight();
|
|
||||||
} else if (e.code == "ArrowUp") {
|
|
||||||
moved = slideUp();
|
|
||||||
} else if (e.code == "ArrowDown") {
|
|
||||||
moved = slideDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only add new tile if board actually moved
|
|
||||||
if (moved) {
|
|
||||||
setTwo();
|
|
||||||
document.getElementById("score").innerText = score;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function filterZero(row) {
|
|
||||||
return row.filter((num) => num != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function slide(row) {
|
|
||||||
row = filterZero(row);
|
|
||||||
for (let i = 0; i < row.length - 1; i++) {
|
|
||||||
if (row[i] == row[i + 1]) {
|
|
||||||
row[i] *= 2;
|
|
||||||
row[i + 1] = 0;
|
|
||||||
score += row[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
row = filterZero(row);
|
|
||||||
while (row.length < columns) {
|
|
||||||
row.push(0);
|
|
||||||
}
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
|
|
||||||
function slideLeft() {
|
|
||||||
let moved = false;
|
|
||||||
for (let r = 0; r < rows; r++) {
|
|
||||||
let row = board[r];
|
|
||||||
let original = [...row];
|
|
||||||
row = slide(row);
|
|
||||||
board[r] = row;
|
|
||||||
|
|
||||||
// Check if board changed
|
|
||||||
if (JSON.stringify(original) !== JSON.stringify(row)) {
|
|
||||||
moved = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let c = 0; c < columns; c++) {
|
|
||||||
let tile = document.getElementById(r.toString() + "-" + c.toString());
|
|
||||||
let num = board[r][c];
|
|
||||||
updateTile(tile, num);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return moved;
|
|
||||||
}
|
|
||||||
|
|
||||||
function slideRight() {
|
|
||||||
let moved = false;
|
|
||||||
for (let r = 0; r < rows; r++) {
|
|
||||||
let row = board[r];
|
|
||||||
let original = [...row];
|
|
||||||
row.reverse();
|
|
||||||
row = slide(row);
|
|
||||||
board[r] = row.reverse();
|
|
||||||
|
|
||||||
// Check if board changed
|
|
||||||
if (JSON.stringify(original) !== JSON.stringify(board[r])) {
|
|
||||||
moved = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let c = 0; c < columns; c++) {
|
|
||||||
let tile = document.getElementById(r.toString() + "-" + c.toString());
|
|
||||||
let num = board[r][c];
|
|
||||||
updateTile(tile, num);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return moved;
|
|
||||||
}
|
|
||||||
|
|
||||||
function slideUp() {
|
|
||||||
let moved = false;
|
|
||||||
for (let c = 0; c < columns; c++) {
|
|
||||||
let row = [board[0][c], board[1][c], board[2][c], board[3][c]];
|
|
||||||
let original = [...row];
|
|
||||||
row = slide(row);
|
|
||||||
|
|
||||||
// Check if board changed
|
|
||||||
if (JSON.stringify(original) !== JSON.stringify(row)) {
|
|
||||||
moved = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let r = 0; r < rows; r++) {
|
|
||||||
board[r][c] = row[r];
|
|
||||||
let tile = document.getElementById(r.toString() + "-" + c.toString());
|
|
||||||
let num = board[r][c];
|
|
||||||
updateTile(tile, num);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return moved;
|
|
||||||
}
|
|
||||||
|
|
||||||
function slideDown() {
|
|
||||||
let moved = false;
|
|
||||||
for (let c = 0; c < columns; c++) {
|
|
||||||
let row = [board[0][c], board[1][c], board[2][c], board[3][c]];
|
|
||||||
let original = [...row];
|
|
||||||
row.reverse();
|
|
||||||
row = slide(row);
|
|
||||||
row.reverse();
|
|
||||||
|
|
||||||
// Check if board changed
|
|
||||||
if (JSON.stringify(original) !== JSON.stringify(row)) {
|
|
||||||
moved = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let r = 0; r < rows; r++) {
|
|
||||||
board[r][c] = row[r];
|
|
||||||
let tile = document.getElementById(r.toString() + "-" + c.toString());
|
|
||||||
let num = board[r][c];
|
|
||||||
updateTile(tile, num);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return moved;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTwo() {
|
|
||||||
if (!hasEmptyTile()) {
|
|
||||||
checkGameOver();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let found = false;
|
|
||||||
while (!found) {
|
|
||||||
let r = Math.floor(Math.random() * rows);
|
|
||||||
let c = Math.floor(Math.random() * columns);
|
|
||||||
if (board[r][c] == 0) {
|
|
||||||
board[r][c] = 2;
|
|
||||||
let tile = document.getElementById(r.toString() + "-" + c.toString());
|
|
||||||
tile.innerText = "2";
|
|
||||||
tile.classList.add("x2");
|
|
||||||
found = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasEmptyTile() {
|
|
||||||
for (let r = 0; r < rows; r++) {
|
|
||||||
for (let c = 0; c < columns; c++) {
|
|
||||||
if (board[r][c] == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkGameOver() {
|
|
||||||
// Check if any moves are possible
|
|
||||||
for (let r = 0; r < rows; r++) {
|
|
||||||
for (let c = 0; c < columns; c++) {
|
|
||||||
// Check right
|
|
||||||
if (c < columns - 1 && board[r][c] == board[r][c + 1]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Check down
|
|
||||||
if (r < rows - 1 && board[r][c] == board[r + 1][c]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No moves available - game over
|
|
||||||
gameOver = true;
|
|
||||||
showGameOver();
|
|
||||||
}
|
|
||||||
|
|
||||||
function showGameOver() {
|
|
||||||
document.getElementById("finalScore").innerText = score;
|
|
||||||
document.getElementById("gameOverOverlay").style.display = "flex";
|
|
||||||
}
|
|
||||||
20
Database.sql
Normal file
20
Database.sql
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
CREATE DATABASE IF NOT EXISTS 2048;
|
||||||
|
USE 2048;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
username VARCHAR(50) NOT NULL UNIQUE,
|
||||||
|
password VARCHAR(255) NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS leaderboard (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
user_id INT,
|
||||||
|
username VARCHAR(50),
|
||||||
|
score INT NOT NULL,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_score ON leaderboard(score DESC);
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
require 'Koneksi.php';
|
||||||
|
|
||||||
|
$query = "SELECT username, score FROM leaderboard ORDER BY score DESC LIMIT 10";
|
||||||
|
$result = $conn->query($query);
|
||||||
|
|
||||||
|
$leaderboard = [];
|
||||||
|
|
||||||
|
if ($result && $result->num_rows > 0) {
|
||||||
|
while ($row = $result->fetch_assoc()) {
|
||||||
|
$leaderboard[] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kirim data ke frontend dalam bentuk JSON
|
||||||
|
echo json_encode([
|
||||||
|
"status" => "success",
|
||||||
|
"leaderboard" => $leaderboard
|
||||||
|
]);
|
||||||
|
|
||||||
|
$conn->close();
|
||||||
|
?>
|
||||||
37
Login.php
37
Login.php
@ -1,33 +1,38 @@
|
|||||||
<?php
|
<?php
|
||||||
|
session_start();
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
include 'Database.php';
|
include 'Database.php';
|
||||||
session_start();
|
|
||||||
|
|
||||||
$data = $_POST;
|
$username = $_POST['username'] ?? '';
|
||||||
if (empty($data['username']) || empty($data['password'])) {
|
$password = $_POST['password'] ?? '';
|
||||||
echo json_encode(["status"=>"error","message"=>"Username & password dibutuhkan"]);
|
|
||||||
|
if (empty($username) || empty($password)) {
|
||||||
|
echo json_encode(["status" => "error", "message" => "Username dan password wajib diisi"]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$username = trim($data['username']);
|
$stmt = $conn->prepare("SELECT password FROM users WHERE username = ?");
|
||||||
$password = $data['password'];
|
|
||||||
|
|
||||||
$stmt = $conn->prepare("SELECT id, password FROM users WHERE username = ?");
|
|
||||||
$stmt->bind_param("s", $username);
|
$stmt->bind_param("s", $username);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$stmt->bind_result($id, $hash);
|
$stmt->store_result();
|
||||||
if ($stmt->fetch()) {
|
|
||||||
if (password_verify($password, $hash)) {
|
if ($stmt->num_rows === 0) {
|
||||||
session_regenerate_id(true);
|
echo json_encode(["status" => "error", "message" => "Username tidak ditemukan"]);
|
||||||
$_SESSION['user_id'] = $id;
|
$stmt->close();
|
||||||
|
$conn->close();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->bind_result($hashedPassword);
|
||||||
|
$stmt->fetch();
|
||||||
|
|
||||||
|
if (password_verify($password, $hashedPassword)) {
|
||||||
$_SESSION['username'] = $username;
|
$_SESSION['username'] = $username;
|
||||||
echo json_encode(["status" => "success", "message" => "Login berhasil"]);
|
echo json_encode(["status" => "success", "message" => "Login berhasil"]);
|
||||||
} else {
|
} else {
|
||||||
echo json_encode(["status" => "error", "message" => "Password salah"]);
|
echo json_encode(["status" => "error", "message" => "Password salah"]);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
echo json_encode(["status"=>"error","message"=>"User tidak ditemukan"]);
|
|
||||||
}
|
|
||||||
$stmt->close();
|
$stmt->close();
|
||||||
$conn->close();
|
$conn->close();
|
||||||
?>
|
?>
|
||||||
18
Logout.php
18
Logout.php
@ -1,14 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
header('Content-Type: application/json');
|
|
||||||
session_start();
|
session_start();
|
||||||
$_SESSION = [];
|
header('Content-Type: application/json');
|
||||||
if (ini_get("session.use_cookies")) {
|
|
||||||
$params = session_get_cookie_params();
|
session_unset();
|
||||||
setcookie(session_name(), '', time() - 42000,
|
|
||||||
$params["path"], $params["domain"],
|
|
||||||
$params["secure"], $params["httponly"]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
session_destroy();
|
session_destroy();
|
||||||
echo json_encode(["status"=>"success","message"=>"Logout berhasil"]);
|
|
||||||
|
echo json_encode([
|
||||||
|
"status" => "success",
|
||||||
|
"message" => "Logout berhasil"
|
||||||
|
]);
|
||||||
?>
|
?>
|
||||||
37
Register.php
37
Register.php
@ -1,37 +1,38 @@
|
|||||||
<?php
|
<?php
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
include 'Database.php';
|
include 'Database.php';
|
||||||
session_start();
|
|
||||||
|
|
||||||
$data = $_POST;
|
$username = $_POST['username'] ?? '';
|
||||||
if (empty($data['username']) || empty($data['password'])) {
|
$password = $_POST['password'] ?? '';
|
||||||
echo json_encode(["status"=>"error","message"=>"Username & password dibutuhkan"]);
|
|
||||||
|
if (empty($username) || empty($password)) {
|
||||||
|
echo json_encode(["status" => "error", "message" => "Username dan password wajib diisi"]);
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$username = trim($data['username']);
|
$check = $conn->prepare("SELECT id FROM users WHERE username = ?");
|
||||||
$password = $data['password'];
|
$check->bind_param("s", $username);
|
||||||
|
$check->execute();
|
||||||
|
$check->store_result();
|
||||||
|
|
||||||
$stmt = $conn->prepare("SELECT id FROM users WHERE username = ?");
|
if ($check->num_rows > 0) {
|
||||||
$stmt->bind_param("s", $username);
|
echo json_encode(["status" => "error", "message" => "Username sudah digunakan"]);
|
||||||
$stmt->execute();
|
$check->close();
|
||||||
$stmt->store_result();
|
$conn->close();
|
||||||
if ($stmt->num_rows > 0) {
|
|
||||||
echo json_encode(["status"=>"error","message"=>"Username sudah terpakai"]);
|
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
$stmt->close();
|
|
||||||
|
|
||||||
$hash = password_hash($password, PASSWORD_DEFAULT);
|
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
|
||||||
$stmt = $conn->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
|
$stmt = $conn->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
|
||||||
$stmt->bind_param("ss", $username, $hash);
|
$stmt->bind_param("ss", $username, $hashedPassword);
|
||||||
|
|
||||||
if ($stmt->execute()) {
|
if ($stmt->execute()) {
|
||||||
$_SESSION['user_id'] = $stmt->insert_id;
|
echo json_encode(["status" => "success", "message" => "Pendaftaran berhasil"]);
|
||||||
$_SESSION['username'] = $username;
|
|
||||||
echo json_encode(["status"=>"success","message"=>"Register berhasil"]);
|
|
||||||
} else {
|
} else {
|
||||||
echo json_encode(["status" => "error", "message" => "Gagal mendaftar"]);
|
echo json_encode(["status" => "error", "message" => "Gagal mendaftar"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt->close();
|
$stmt->close();
|
||||||
$conn->close();
|
$conn->close();
|
||||||
?>
|
?>
|
||||||
@ -1,16 +0,0 @@
|
|||||||
<?php
|
|
||||||
include 'Database.php';
|
|
||||||
session_start();
|
|
||||||
|
|
||||||
if (isset($_SESSION['username'])) {
|
|
||||||
$username = $_SESSION['username'];
|
|
||||||
$score = $_POST['score'];
|
|
||||||
|
|
||||||
$query = "INSERT INTO leaderboard (username, score) VALUES ('$username', '$score')";
|
|
||||||
mysqli_query($conn, $query);
|
|
||||||
|
|
||||||
echo json_encode(["status" => "success"]);
|
|
||||||
} else {
|
|
||||||
echo json_encode(["status" => "error", "message" => "Not logged in"]);
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
32
Save_Score.php
Normal file
32
Save_Score.php
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
include 'Koneksi.php';
|
||||||
|
|
||||||
|
// Pastikan user sudah login
|
||||||
|
if (!isset($_SESSION['username'])) {
|
||||||
|
echo json_encode(["status" => "error", "message" => "Belum login"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$username = $_SESSION['username'];
|
||||||
|
$score = $_POST['score'] ?? 0;
|
||||||
|
|
||||||
|
if ($score <= 0) {
|
||||||
|
echo json_encode(["status" => "error", "message" => "Skor tidak valid"]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simpan ke database
|
||||||
|
$stmt = $conn->prepare("INSERT INTO leaderboard (username, score) VALUES (?, ?)");
|
||||||
|
$stmt->bind_param("si", $username, $score);
|
||||||
|
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
echo json_encode(["status" => "success", "message" => "Skor berhasil disimpan"]);
|
||||||
|
} else {
|
||||||
|
echo json_encode(["status" => "error", "message" => "Gagal menyimpan skor"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$stmt->close();
|
||||||
|
$conn->close();
|
||||||
|
?>
|
||||||
18
Score.js
Normal file
18
Score.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
function saveScore(score) {
|
||||||
|
fetch('Save_Score.php', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded'
|
||||||
|
},
|
||||||
|
body: 'score=' + encodeURIComponent(score)
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.status === "success") {
|
||||||
|
console.log("Skor berhasil disimpan!");
|
||||||
|
} else {
|
||||||
|
console.log("Gagal menyimpan skor:", data.message);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => console.error("Error API:", error));
|
||||||
|
}
|
||||||
15
Session_Check.php
Normal file
15
Session_Check.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
|
||||||
|
if (isset($_SESSION['username'])) {
|
||||||
|
echo json_encode([
|
||||||
|
"status" => "logged_in",
|
||||||
|
"username" => $_SESSION['username']
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
echo json_encode([
|
||||||
|
"status" => "not_logged_in"
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
?>
|
||||||
Loading…
x
Reference in New Issue
Block a user