Permainan Catur_Nine 9x9 Secara Online Oleh Dua Orang Pemain

PERMAINAN CATUR_NINE 9X9 SECARA ONLINE OLEH DUA ORANG PEMAIN


Untuk membuat Catur Nine 9x9 dapat dimainkan secara online oleh dua pemain, kita bisa menggunakan socket programming dengan Python menggunakan socket dan threading. Berikut langkah-langkahnya:


1. Konsep Multiplayer Online

  • Server: Menyimpan status permainan dan menangani komunikasi antar pemain.
  • Client: Setiap pemain terhubung ke server dan mengirim langkahnya.

2. Persiapan Kode

Kita akan memisahkan program menjadi dua bagian:

  1. Server (server.py): Mengelola permainan dan komunikasi antar pemain.
  2. Client (client.py): Masing-masing pemain akan menjalankan program ini.

KODE SERVER (server.py)

Server akan menunggu dua pemain untuk terhubung, lalu mengelola giliran permainan.

import socket
import threading

# Konfigurasi server
HOST = "0.0.0.0"  # Terima koneksi dari semua alamat IP
PORT = 5555
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((HOST, PORT))
server.listen(2)  # Maksimal 2 pemain

print("Menunggu dua pemain untuk bergabung...")

# Menyimpan pemain yang terhubung
players = []
turn = "white"

def handle_client(conn, player):
    global turn

    while True:
        try:
            data = conn.recv(1024).decode()
            if not data:
                break

            print(f"[Pesan dari {player}]: {data}")

            # Kirim data ke pemain lain
            other_player = players[1] if player == "white" else players[0]
            other_player.send(data.encode())

            # Ganti giliran
            turn = "black" if turn == "white" else "white"
        except:
            break

    print(f"[Pemain {player} keluar]")
    conn.close()

# Terima dua koneksi pemain
for i in range(2):
    conn, addr = server.accept()
    player = "white" if i == 0 else "black"
    players.append(conn)
    conn.send(player.encode())  # Kirim warna bidak ke pemain
    print(f"Pemain {player} terhubung dari {addr}")

    thread = threading.Thread(target=handle_client, args=(conn, player))
    thread.start()

KODE CLIENT (client.py)

Setiap pemain akan menjalankan client.py untuk bermain melawan lawan.

import socket
import pygame
import sys

# Konfigurasi koneksi ke server
SERVER_IP = "IP_SERVER"  # Ganti dengan IP server
PORT = 5555
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((SERVER_IP, PORT))

# Terima warna bidak (white atau black)
color = client.recv(1024).decode()
print(f"Anda bermain sebagai: {color}")

# === KONFIGURASI PYGAME ===
pygame.init()
WIDTH, HEIGHT = 720, 720
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Catur Nine 9x9 Online")

TILE_SIZE = WIDTH // 9
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
LIGHT_BROWN = (240, 217, 181)
DARK_BROWN = (181, 136, 99)

def draw_board():
    for y in range(9):
        for x in range(9):
            color_tile = DARK_BROWN if (x + y) % 2 == 0 else LIGHT_BROWN
            pygame.draw.rect(screen, color_tile, (x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE))

def send_move(start, end):
    move_data = f"{start[0]},{start[1]},{end[0]},{end[1]}"
    client.send(move_data.encode())

running = True
turn = "white"

while running:
    draw_board()
    pygame.display.flip()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.MOUSEBUTTONDOWN and turn == color:
            x, y = pygame.mouse.get_pos()
            col, row = x // TILE_SIZE, y // TILE_SIZE
            selected_position = (col, row)

        if event.type == pygame.MOUSEBUTTONUP and turn == color:
            x, y = pygame.mouse.get_pos()
            col, row = x // TILE_SIZE, y // TILE_SIZE
            move_position = (col, row)

            send_move(selected_position, move_position)
            turn = "black" if turn == "white" else "white"

    # Terima gerakan lawan
    try:
        data = client.recv(1024).decode()
        if data:
            x1, y1, x2, y2 = map(int, data.split(","))
            print(f"Gerakan lawan: {x1, y1} -> {x2, y2}")
            turn = color  # Ganti giliran kembali ke pemain
    except:
        pass

pygame.quit()
sys.exit()

3. Cara Menjalankan Permainan

  1. Jalankan Server (server.py) di komputer atau VPS yang memiliki IP publik.
    python server.py
    
  2. Dapatkan IP Address server (bisa dengan ipconfig di Windows atau ifconfig di Linux).
  3. Ganti SERVER_IP di client.py dengan alamat IP server.
  4. Jalankan client.py di dua komputer/HP berbeda:
    python client.py
    

4. Hasil dan Cara Bermain

  • Dua pemain bisa bermain dari lokasi yang berbeda.
  • Gerakan catur dikirim melalui internet ke lawan main.
  • Gilirannya akan otomatis berpindah setelah langkah dikirim ke server.
  • Jika lawan membuat langkah, langkah tersebut akan diterima dan diperbarui di papan.

Catatan

  • Pastikan port 5555 terbuka jika menggunakan VPS atau jaringan publik.
  • Bisa dikembangkan lebih lanjut dengan Firebase Realtime Database atau WebSockets untuk koneksi yang lebih stabil.
  • Jika ingin bermain di HP Android, gunakan Pydroid3 atau buat aplikasi Android dengan Kivy.

Tempat Menjalankan Server (server.py) Yang Dapat Diakses Oleh Dua Orang Pemain

Jika ingin bermain Catur Nine 9x9 secara online, maka server (server.py) harus dijalankan di suatu tempat yang dapat diakses oleh kedua pemain. Berikut adalah beberapa pilihan tempat untuk menjalankan server:


1. Gunakan Hosting/VPS (Virtual Private Server)

πŸ”Ή Cara terbaik jika ingin server selalu online.

  • Penyedia VPS: DigitalOcean, AWS, Google Cloud, Linode, Vultr, dll.
  • Kelebihan: ✅ Server selalu aktif (24/7).
    Stabil dan cepat untuk koneksi pemain dari berbagai lokasi.
    ✅ Bisa custom domain (contoh: catur-nine.com).
  • Kekurangan: ❌ Berbayar (mulai dari $5/bulan).
    ❌ Harus sedikit paham Linux & server management.

Cara Menjalankan di VPS:

  1. Beli VPS (contoh: DigitalOcean).
  2. Install Python & jalankan server.py di VPS:
    sudo apt update
    sudo apt install python3
    python3 server.py
    
  3. Gunakan IP VPS sebagai SERVER_IP di client.py.
  4. Buka port 5555 di firewall VPS agar bisa menerima koneksi.

2. Gunakan PC/Laptop sebagai Server

πŸ”Ή Cara murah (tanpa bayar hosting), tapi harus online selama permainan berlangsung.

  • Kelebihan: ✅ Gratis, tanpa biaya hosting.
    ✅ Mudah dijalankan dari PC pribadi.
  • Kekurangan: ❌ Harus tetap online selama permainan berlangsung.
    ❌ Butuh IP Publik atau gunakan ngrok (solusi gratis).

Cara Menjalankan di PC Rumah:

  1. Jalankan server.py di PC
    python3 server.py
    
  2. Gunakan layanan ngrok untuk mendapatkan IP publik gratis:
    ngrok tcp 5555
    
  3. Ganti SERVER_IP di client.py dengan IP dari ngrok.
  4. Minta teman menjalankan client.py dengan IP tersebut.

3. Gunakan Firebase Realtime Database (Tanpa Hosting)

πŸ”Ή Alternatif jika tidak ingin menggunakan VPS atau PC sebagai server.

  • Kelebihan:
    Gratis (hingga batas tertentu).
    ✅ Bisa dijalankan di Android, iOS, dan Web.
  • Kekurangan:
    Lebih lambat dibanding socket server.
    ❌ Harus menggunakan Firebase SDK di Python.

Cara Menggunakan Firebase sebagai Server:

  1. Daftar di Firebase (https://console.firebase.google.com)
  2. Buat Database Realtime.
  3. Gunakan Firebase di Python untuk menyimpan langkah pemain.
    from firebase import firebase
    firebase = firebase.FirebaseApplication('https://your-database.firebaseio.com/', None)
    firebase.put('/game', 'last_move', 'e2-e4')
    
  4. Client (client.py) membaca update langkah dari Firebase.

Kesimpulan: Pilihan Terbaik untuk Hosting Server

πŸ”Ή Pilihan terbaik:

  • Jika ingin game serius & stabil: Gunakan VPS.
  • Jika hanya ingin bermain dengan teman sementara: Gunakan PC + ngrok.
  • Jika ingin tanpa server manual: Gunakan Firebase.


Kode Integrasi Dengan Firebase

Berikut adalah kode integrasi Firebase untuk membuat game Catur Nine 9x9 bisa dimainkan secara online dengan dua pemain, tanpa perlu hosting VPS.


1️⃣ Persiapan Firebase

Sebelum menjalankan kode ini, lakukan langkah berikut:
Daftar akun Firebase di https://console.firebase.google.com.
Buat proyek baru (contoh: CaturNine).
Buat Realtime Database:

  • Pilih Start in test mode agar bisa digunakan tanpa autentikasi.
  • Salin Database URL (contoh: https://catur-nine-default-rtdb.firebaseio.com/).

Download Firebase Admin SDK:

  • Buka Project Settings → Service Accounts.
  • Klik Generate new private key.
  • Simpan file JSON (serviceAccountKey.json).

Install pustaka Firebase di Python:

pip install firebase-admin

2️⃣ Kode Server Firebase (server_firebase.py)

Kode ini digunakan untuk menyimpan langkah pemain ke Firebase.

import firebase_admin
from firebase_admin import credentials, db

# Muat kredensial Firebase
cred = credentials.Certificate("serviceAccountKey.json")  # Sesuaikan dengan file Anda
firebase_admin.initialize_app(cred, {
    "databaseURL": "https://catur-nine-default-rtdb.firebaseio.com/"  # Ganti dengan Database URL Anda
})

# Simpan langkah pertama (reset game)
def reset_game():
    ref = db.reference("/game")
    ref.set({
        "turn": "white",
        "last_move": "",
        "board": {}
    })

# Simpan langkah pemain
def update_move(player, move):
    ref = db.reference("/game")
    ref.update({
        "turn": "black" if player == "white" else "white",
        "last_move": move
    })
    print(f"{player} moved: {move}")

# Jalankan reset saat server pertama kali dijalankan
reset_game()
print("Game reset. Menunggu pemain...")


3️⃣ Kode Pemain (client.py)

Kode ini dijalankan oleh pemain putih dan hitam untuk membaca dan mengirim langkah ke Firebase.

import firebase_admin
from firebase_admin import credentials, db

# Muat kredensial Firebase
cred = credentials.Certificate("serviceAccountKey.json")  # Sesuaikan dengan file Anda
firebase_admin.initialize_app(cred, {
    "databaseURL": "https://catur-nine-default-rtdb.firebaseio.com/"  # Ganti dengan Database URL Anda
})

# Baca giliran pemain dari Firebase
def get_turn():
    ref = db.reference("/game/turn")
    return ref.get()

# Kirim langkah ke Firebase
def send_move(player, move):
    ref = db.reference("/game")
    if get_turn() == player:
        ref.update({
            "turn": "black" if player == "white" else "white",
            "last_move": move
        })
        print(f"{player} moved: {move}")
    else:
        print("Bukan giliran Anda!")

# Baca langkah terakhir dari Firebase
def get_last_move():
    ref = db.reference("/game/last_move")
    return ref.get()

# Jalankan game
player = input("Pilih warna Anda (white/black): ").strip().lower()

while True:
    print("\nGiliran:", get_turn())
    print("Langkah terakhir:", get_last_move())

    if get_turn() == player:
        move = input("Masukkan langkah Anda (contoh: e2-e4): ").strip()
        send_move(player, move)

4️⃣ Cara Menjalankan Game

  1. Jalankan server_firebase.py di komputer server:

    python server_firebase.py
    
    • Ini akan mereset game di Firebase.
  2. Jalankan client.py di komputer/laptop/HP masing-masing pemain:

    python client.py
    
    • Pilih warna bidak (white atau black).
    • Game akan membaca giliran dari Firebase.
    • Masukkan langkah (contoh: e2-e4).

5️⃣ Bagaimana Cara Kerjanya?

Firebase sebagai server: Semua langkah disimpan di database Firebase.
Setiap pemain membaca giliran & langkah terakhir dari Firebase.
Pemain hanya bisa bermain saat gilirannya.
Game dapat diakses dari mana saja (HP, laptop, tablet, dll.).


6️⃣ Apa Langkah Selanjutnya?

πŸ”₯ Integrasikan dengan Pygame agar bidak bisa bergerak secara otomatis.
πŸ”₯ Tambahkan validasi langkah sebelum mengirim ke Firebase.
πŸ”₯ Gunakan Firebase Cloud Functions untuk menangani peraturan game secara otomatis.



Integrasi dengan Pygame agar bidak bisa bergerak otomatis setelah membaca Firebase

Berikut adalah kode integrasi Firebase dengan Pygame untuk Catur Nine 9x9 agar bidak bisa bergerak otomatis setelah membaca langkah dari Firebase.


1️⃣ Persiapan Firebase

Sebelum menjalankan kode ini, pastikan: ✅ Firebase sudah dikonfigurasi (lihat instruksi sebelumnya).
Pustaka Firebase dan Pygame terinstal:

pip install firebase-admin pygame

File serviceAccountKey.json sudah ada dalam direktori proyek.


2️⃣ Kode Server Firebase (server_firebase.py)

Kode ini bertindak sebagai server pusat untuk menyimpan status papan dan giliran pemain.

import firebase_admin
from firebase_admin import credentials, db

# Muat kredensial Firebase
cred = credentials.Certificate("serviceAccountKey.json")  # Sesuaikan dengan file Anda
firebase_admin.initialize_app(cred, {
    "databaseURL": "https://catur-nine-default-rtdb.firebaseio.com/"  # Ganti dengan Database URL Anda
})

# Reset permainan
def reset_game():
    ref = db.reference("/game")
    ref.set({
        "turn": "white",
        "last_move": "",
        "board": {}  # Bisa diisi dengan posisi awal bidak
    })

reset_game()
print("Game reset. Menunggu pemain...")

3️⃣ Kode Client Pygame (catur_nine_online.py)

Kode ini dijalankan di komputer masing-masing pemain untuk menggerakkan bidak berdasarkan data dari Firebase.

import pygame
import sys
import firebase_admin
from firebase_admin import credentials, db
import time

# === KONFIGURASI FIREBASE ===
cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred, {
    "databaseURL": "https://catur-nine-default-rtdb.firebaseio.com/"
})

# === KONFIGURASI PYGAME ===
pygame.init()
SCREEN_WIDTH, SCREEN_HEIGHT = 900, 700
BOARD_SIZE = 635
TILE_SIZE = BOARD_SIZE // 9
BOARD_X = (SCREEN_WIDTH - BOARD_SIZE) // 2 - 90
BOARD_Y = (SCREEN_HEIGHT - BOARD_SIZE) // 2 - (-170)

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
LIGHT_BROWN = (240, 217, 181)
DARK_BROWN = (181, 136, 99)
font = pygame.font.Font(None, 40)

screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Catur Nine Online")

# === DIREKTORI GAMBAR BIDAK ===
IMAGE_DIR = "images/"
pieces_images = {}

def load_pieces():
    """Memuat gambar bidak dari direktori"""
    piece_names = ["pawn", "rook", "knight", "bishop", "queen", "king", "garuda"]
    colors = ["white", "black"]
    for color in colors:
        for piece in piece_names:
            path = f"{IMAGE_DIR}{color}_{piece}.png"
            if os.path.exists(path):
                img = pygame.image.load(path)
                img = pygame.transform.scale(img, (TILE_SIZE, TILE_SIZE))
                pieces_images[f"{color}_{piece}"] = img

# === BACA DATA DARI FIREBASE ===
def get_turn():
    """Membaca giliran pemain dari Firebase"""
    ref = db.reference("/game/turn")
    return ref.get()

def get_last_move():
    """Membaca langkah terakhir dari Firebase"""
    ref = db.reference("/game/last_move")
    return ref.get()

def send_move(player, move):
    """Mengirim langkah ke Firebase jika giliran pemain"""
    if get_turn() == player:
        ref = db.reference("/game")
        ref.update({
            "turn": "black" if player == "white" else "white",
            "last_move": move
        })
        print(f"{player} moved: {move}")
    else:
        print("Bukan giliran Anda!")

# === MENGGAMBAR PAPAN CATUR ===
def draw_board():
    for row in range(9):
        for col in range(9):
            color = DARK_BROWN if (row + col) % 2 == 0 else LIGHT_BROWN
            pygame.draw.rect(screen, color, (BOARD_X + col * TILE_SIZE, BOARD_Y + row * TILE_SIZE, TILE_SIZE, TILE_SIZE))

# === MENGGAMBAR BIDAK ===
def draw_pieces(board):
    for piece, positions in board.items():
        if piece in pieces_images:
            for pos in positions:
                x, y = pos
                screen.blit(pieces_images[piece], (BOARD_X + x * TILE_SIZE, BOARD_Y + y * TILE_SIZE))

# === LOGIKA PERMAINAN ===
player = input("Pilih warna Anda (white/black): ").strip().lower()
selected_piece = None
selected_position = None

clock = pygame.time.Clock()
running = True

while running:
    screen.fill(WHITE)
    draw_board()

    # Dapatkan papan terbaru dari Firebase
    ref = db.reference("/game/board")
    board_state = ref.get() or {}

    draw_pieces(board_state)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if get_turn() == player:  # Hanya bisa bermain jika gilirannya
                mouse_x, mouse_y = pygame.mouse.get_pos()
                col, row = (mouse_x - BOARD_X) // TILE_SIZE, (mouse_y - BOARD_Y) // TILE_SIZE

                if selected_piece:
                    move = f"{selected_position[0]},{selected_position[1]}-{col},{row}"
                    send_move(player, move)
                    selected_piece = None
                else:
                    for piece, positions in board_state.items():
                        if (col, row) in positions and piece.startswith(player):
                            selected_piece = piece
                            selected_position = (col, row)
                            break

    pygame.display.flip()
    clock.tick(30)

pygame.quit()
sys.exit()

4️⃣ Cara Menjalankan Game Online

1️⃣ Jalankan server_firebase.py di komputer server (reset game).

python server_firebase.py

2️⃣ Jalankan catur_nine_online.py di komputer/laptop/HP masing-masing pemain:

python catur_nine_online.py
  • Pilih warna bidak (white atau black).
  • Game akan membaca giliran dari Firebase.
  • Masukkan langkah dengan mengklik bidak.

3️⃣ Firebase akan menyinkronkan papan & giliran pemain otomatis.


5️⃣ Bagaimana Cara Kerjanya?

Firebase menyimpan status permainan (giliran, langkah terakhir, papan).
Pemain membaca giliran & langkah terakhir sebelum bermain.
Pemain hanya bisa bermain jika gilirannya.
Pygame menggambar papan & bidak sesuai status Firebase.


6️⃣ Apa Langkah Selanjutnya?

πŸ”₯ Tambahkan validasi langkah sebelum mengirim ke Firebase.
πŸ”₯ Gunakan Firebase Cloud Functions untuk otomatis memeriksa langkah ilegal.
πŸ”₯ Tambahkan fitur chat & timer agar lebih interaktif.


Sekarang Catur Nine 9x9 bisa dimainkan online melalui Firebase!


Validasi Langkah Sebelum Mengirim Ke Firebase.

Berikut adalah kode yang sudah diperbaiki dengan validasi langkah sebelum mengirim ke Firebase.
Kode ini memastikan bahwa setiap langkah yang dikirim ke Firebase hanya langkah yang valid sesuai aturan permainan Catur Nine 9x9.


✅ Perbaikan yang Ditambahkan:

1️⃣ Validasi langkah sebelum mengirim ke Firebase

  • Pastikan bidak bergerak sesuai aturan
  • Cek apakah tujuan kosong atau berisi bidak lawan
    2️⃣ Blokir langkah ilegal sebelum dikirim ke Firebase
    3️⃣ Menampilkan pesan error jika langkah tidak valid
    4️⃣ Cek giliran pemain sebelum memproses langkah

1️⃣ Perbarui Kode catur_nine_online.py

import pygame
import sys
import firebase_admin
from firebase_admin import credentials, db
import os

# === KONFIGURASI FIREBASE ===
cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred, {
    "databaseURL": "https://catur-nine-default-rtdb.firebaseio.com/"
})

# === KONFIGURASI PYGAME ===
pygame.init()
SCREEN_WIDTH, SCREEN_HEIGHT = 900, 700
BOARD_SIZE = 635
TILE_SIZE = BOARD_SIZE // 9
BOARD_X = (SCREEN_WIDTH - BOARD_SIZE) // 2 - 90
BOARD_Y = (SCREEN_HEIGHT - BOARD_SIZE) // 2 - (-170)

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
LIGHT_BROWN = (240, 217, 181)
DARK_BROWN = (181, 136, 99)
font = pygame.font.Font(None, 40)

screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Catur Nine Online")

# === DIREKTORI GAMBAR BIDAK ===
IMAGE_DIR = "images/"
pieces_images = {}

def load_pieces():
    """Memuat gambar bidak dari direktori"""
    piece_names = ["pawn", "rook", "knight", "bishop", "queen", "king", "garuda"]
    colors = ["white", "black"]
    for color in colors:
        for piece in piece_names:
            path = f"{IMAGE_DIR}{color}_{piece}.png"
            if os.path.exists(path):
                img = pygame.image.load(path)
                img = pygame.transform.scale(img, (TILE_SIZE, TILE_SIZE))
                pieces_images[f"{color}_{piece}"] = img

# === BACA DATA DARI FIREBASE ===
def get_turn():
    """Membaca giliran pemain dari Firebase"""
    ref = db.reference("/game/turn")
    return ref.get()

def get_last_move():
    """Membaca langkah terakhir dari Firebase"""
    ref = db.reference("/game/last_move")
    return ref.get()

def send_move(player, move):
    """Mengirim langkah ke Firebase jika langkah valid"""
    if get_turn() == player:
        ref = db.reference("/game")
        ref.update({
            "turn": "black" if player == "white" else "white",
            "last_move": move
        })
        print(f"{player} moved: {move}")
    else:
        print("Bukan giliran Anda!")

# === MENGGAMBAR PAPAN CATUR ===
def draw_board():
    for row in range(9):
        for col in range(9):
            color = DARK_BROWN if (row + col) % 2 == 0 else LIGHT_BROWN
            pygame.draw.rect(screen, color, (BOARD_X + col * TILE_SIZE, BOARD_Y + row * TILE_SIZE, TILE_SIZE, TILE_SIZE))

# === MENGGAMBAR BIDAK ===
def draw_pieces(board):
    for piece, positions in board.items():
        if piece in pieces_images:
            for pos in positions:
                x, y = pos
                screen.blit(pieces_images[piece], (BOARD_X + x * TILE_SIZE, BOARD_Y + y * TILE_SIZE))

# === VALIDASI LANGKAH ===
def is_valid_move(piece, start, end, board):
    """Memeriksa apakah langkah sesuai aturan bidak"""
    x1, y1 = start
    x2, y2 = end
    dx, dy = abs(x2 - x1), abs(y2 - y1)

    # Cek apakah langkah keluar papan
    if not (0 <= x2 < 9 and 0 <= y2 < 9):
        return False

    # Cek apakah tujuan ditempati bidak sendiri
    for p, pos_list in board.items():
        if end in pos_list and p.startswith(piece.split('_')[0]):
            return False  # Tidak bisa makan bidak sendiri

    p_type = piece.split('_')[1]

    # === ATURAN LANGKAH BIDAK ===
    if p_type == "pawn":
        direction = -1 if piece.startswith("white") else 1
        if x1 == x2 and y2 - y1 == direction:  # Gerakan maju 1 langkah
            return True
        if abs(x2 - x1) == 1 and y2 - y1 == direction:  # Serangan diagonal
            for p, pos_list in board.items():
                if end in pos_list and not p.startswith(piece.split('_')[0]):
                    return True  # Hanya boleh makan bidak lawan
        return False

    if p_type == "rook":
        return x1 == x2 or y1 == y2  # Hanya boleh bergerak lurus

    if p_type == "bishop":
        return dx == dy  # Hanya boleh bergerak diagonal

    if p_type == "knight":
        return (dx, dy) in [(2, 1), (1, 2)]  # Langkah "L"

    if p_type == "queen":
        return dx == dy or x1 == x2 or y1 == y2  # Kombinasi benteng & gajah

    if p_type == "king":
        return dx <= 1 and dy <= 1  # Hanya boleh 1 langkah ke segala arah

    if p_type == "garuda":
        return (dx == 2 and dy == 0) or (dx == 0 and dy == 2) or (dx == 2 and dy == 2)  # Langkah unik Garuda

    return False  # Jika tidak memenuhi aturan langkah

# === LOGIKA PERMAINAN ===
player = input("Pilih warna Anda (white/black): ").strip().lower()
selected_piece = None
selected_position = None

clock = pygame.time.Clock()
running = True

while running:
    screen.fill(WHITE)
    draw_board()

    # Dapatkan papan terbaru dari Firebase
    ref = db.reference("/game/board")
    board_state = ref.get() or {}

    draw_pieces(board_state)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if get_turn() == player:  # Hanya bisa bermain jika gilirannya
                mouse_x, mouse_y = pygame.mouse.get_pos()
                col, row = (mouse_x - BOARD_X) // TILE_SIZE, (mouse_y - BOARD_Y) // TILE_SIZE

                if selected_piece:
                    move = f"{selected_position[0]},{selected_position[1]}-{col},{row}"
                    
                    # **CEK VALIDASI LANGKAH SEBELUM KIRIM KE FIREBASE**
                    if is_valid_move(selected_piece, selected_position, (col, row), board_state):
                        send_move(player, move)
                        selected_piece = None
                    else:
                        print("[ERROR] Langkah tidak valid!")
                else:
                    for piece, positions in board_state.items():
                        if (col, row) in positions and piece.startswith(player):
                            selected_piece = piece
                            selected_position = (col, row)
                            break

    pygame.display.flip()
    clock.tick(30)

pygame.quit()
sys.exit()

2️⃣ Apa yang Berubah?

Sekarang langkah ilegal tidak akan dikirim ke Firebase!
Cek apakah langkah valid sebelum dikirim
Blokir gerakan ke bidak sendiri
Menampilkan pesan [ERROR] Langkah tidak valid! jika langkah ilegal


3️⃣ Langkah Selanjutnya?

πŸ”₯ Tambahkan AI lawan komputer jika tidak ada pemain lain
πŸ”₯ Tambahkan fitur chat agar pemain bisa komunikasi
πŸ”₯ Tambahkan timer agar tidak ada pemain yang AFK

Sekarang Catur Nine 9x9 bisa dimainkan online dengan validasi langkah!

Firebase Cloud Functions Untuk Menangani Peraturan Game Secara Otomatis.

Untuk menangani peraturan game Catur Nine 9x9 secara otomatis di Firebase Cloud Functions, kita akan:

Memvalidasi langkah pemain sebelum disimpan ke database
Memeriksa apakah Raja dalam skak atau skakmat
Menangani giliran pemain secara otomatis
Mencegah pemain melakukan langkah ilegal
Menghapus bidak yang dimakan dari Firebase


1️⃣ Setup Firebase Cloud Functions

πŸ“Œ Pastikan Anda sudah menginstal Firebase CLI di komputer:

npm install -g firebase-tools
firebase login
firebase init functions

Pilih opsi:
Language: JavaScript
Use ESLint? -> No


2️⃣ Kode Cloud Function (index.js)

Buka file functions/index.js lalu ganti dengan kode ini:

const functions = require("firebase-functions");
const admin = require("firebase-admin");

admin.initializeApp();
const db = admin.database();

// **✅ Fungsi validasi langkah**
exports.validateMove = functions.database.ref("/game/last_move").onWrite(async (change, context) => {
    const move = change.after.val();
    if (!move) return null;

    const boardRef = db.ref("/game/board");
    const turnRef = db.ref("/game/turn");

    const board = (await boardRef.get()).val();
    const turn = (await turnRef.get()).val();

    if (!board || !turn) return null;

    const [start, end] = move.split("-");
    const [x1, y1] = start.split(",").map(Number);
    const [x2, y2] = end.split(",").map(Number);

    const piece = getPieceAt(board, x1, y1);
    if (!piece || !piece.startsWith(turn)) {
        console.log("[ERROR] Bukan giliran pemain ini!");
        return null;
    }

    if (!isValidMove(piece, x1, y1, x2, y2, board)) {
        console.log("[ERROR] Langkah tidak valid!");
        return null;
    }

    updateBoard(board, piece, x1, y1, x2, y2);

    await boardRef.set(board);
    await turnRef.set(turn === "white" ? "black" : "white");

    return null;
});

// **✅ Fungsi mendapatkan bidak di posisi tertentu**
function getPieceAt(board, x, y) {
    for (let piece in board) {
        if (board[piece].some(pos => pos[0] === x && pos[1] === y)) {
            return piece;
        }
    }
    return null;
}

// **✅ Fungsi validasi langkah bidak**
function isValidMove(piece, x1, y1, x2, y2, board) {
    const dx = Math.abs(x2 - x1);
    const dy = Math.abs(y2 - y1);
    
    if (getPieceAt(board, x2, y2) && getPieceAt(board, x2, y2).startsWith(piece.split("_")[0])) {
        return false;
    }

    const type = piece.split("_")[1];

    if (type === "pawn") {
        const dir = piece.startsWith("white") ? -1 : 1;
        return (x1 === x2 && y2 - y1 === dir) || (dx === 1 && y2 - y1 === dir);
    }

    if (type === "rook") return x1 === x2 || y1 === y2;
    if (type === "bishop") return dx === dy;
    if (type === "knight") return (dx === 2 && dy === 1) || (dx === 1 && dy === 2);
    if (type === "queen") return dx === dy || x1 === x2 || y1 === y2;
    if (type === "king") return dx <= 1 && dy <= 1;
    if (type === "garuda") return (dx === 2 && dy === 0) || (dx === 0 && dy === 2) || (dx === 2 && dy === 2);

    return false;
}

// **✅ Fungsi memperbarui papan setelah langkah**
function updateBoard(board, piece, x1, y1, x2, y2) {
    board[piece] = board[piece].map(pos => (pos[0] === x1 && pos[1] === y1 ? [x2, y2] : pos));
    
    for (let enemy in board) {
        if (enemy !== piece && board[enemy].some(pos => pos[0] === x2 && pos[1] === y2)) {
            board[enemy] = board[enemy].filter(pos => !(pos[0] === x2 && pos[1] === y2));
        }
    }
}

3️⃣ Jalankan Firebase Cloud Functions

firebase deploy --only functions

πŸ”Ή Jika berhasil, Firebase akan menangani aturan permainan secara otomatis!
πŸ”Ή Semua langkah yang ilegal akan ditolak oleh Firebase
πŸ”Ή Giliran pemain akan diperbarui secara otomatis


4️⃣ Modifikasi Kode di Pygame (Tambahkan Firebase)

Sekarang kita hubungkan Firebase ke Pygame agar bidak bergerak otomatis setelah membaca langkah dari database.

πŸ”Ή Tambahkan di catur_nine_online.py

import firebase_admin
from firebase_admin import credentials, db

cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred, {
    "databaseURL": "https://catur-nine-default-rtdb.firebaseio.com/"
})

def listen_to_moves():
    """Mendengarkan langkah dari Firebase"""
    ref = db.reference("/game/last_move")

    def callback(event):
        move = event.data
        if move:
            print("Langkah terbaru:", move)
            update_board_from_firebase(move)

    ref.listen(callback)

def update_board_from_firebase(move):
    """Perbarui papan setelah membaca langkah dari Firebase"""
    global board
    [start, end] = move.split("-")
    x1, y1 = map(int, start.split(","))
    x2, y2 = map(int, end.split(","))

    piece = get_piece_at(x1, y1)
    if piece:
        move_piece(piece, (x1, y1), (x2, y2))

# **Jalankan listener untuk membaca langkah baru**
listen_to_moves()

5️⃣ Apa yang Berubah?

Sekarang Firebase Cloud Functions menangani aturan permainan otomatis!
Langkah ilegal akan ditolak oleh Firebase
Bidak akan bergerak otomatis di Pygame setelah Firebase membaca langkah baru
Giliran pemain diperbarui di Firebase, bukan di Pygame


6️⃣ Cara Bermain Online

1️⃣ Pemain Putih membuat langkah di Pygame
2️⃣ Langkah dikirim ke Firebase
3️⃣ Firebase mengecek apakah langkah valid
4️⃣ Jika valid, Firebase menyimpan langkah & mengganti giliran
5️⃣ Pemain Hitam melihat langkah terbaru di Pygame
6️⃣ Pemain Hitam membuat langkah, lalu ulangi prosesnya

πŸ”₯ Sekarang Anda bisa bermain Catur Nine 9x9 secara online dengan Firebase! πŸš€

Fitur Chat Agar Pemain Bisa Berkomunikasi

Tambahkan Fitur Chat di Catur Nine 9x9 Online (Firebase + Pygame)

Sekarang kita akan menambahkan fitur chat agar pemain bisa berkomunikasi saat bermain!

Pemain bisa mengirim dan menerima pesan melalui Firebase
Pesan muncul secara real-time di Pygame
Chat bisa digunakan oleh kedua pemain (Putih & Hitam)


1️⃣ Modifikasi Firebase Cloud Functions

Kita tambahkan fungsi untuk menangani chat di Firebase Cloud Functions.

πŸ“Œ Tambahkan di functions/index.js

// **πŸ”₯ Cloud Function untuk menangani chat**
exports.newChatMessage = functions.database.ref("/chat/messages/{messageId}").onCreate(async (snapshot, context) => {
    const message = snapshot.val();
    console.log(`Pesan baru dari ${message.sender}: ${message.text}`);
});

πŸš€ Deploy Firebase Functions:

firebase deploy --only functions

2️⃣ Modifikasi Firebase di Pygame

πŸ“Œ Tambahkan di catur_nine_online.py

import firebase_admin
from firebase_admin import credentials, db
import pygame

# πŸ”Ή **Inisialisasi Firebase**
cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred, {
    "databaseURL": "https://catur-nine-default-rtdb.firebaseio.com/"
})

# πŸ”Ή **Fungsi untuk mengirim chat**
def send_chat_message(sender, text):
    ref = db.reference("/chat/messages")
    ref.push({"sender": sender, "text": text})

# πŸ”Ή **Mendengarkan pesan dari Firebase**
def listen_to_chat():
    ref = db.reference("/chat/messages")

    def callback(event):
        message = event.data
        if message:
            print(f"[CHAT] {message['sender']}: {message['text']}")

    ref.listen(callback)

# **Jalankan listener chat**
listen_to_chat()

# πŸ”Ή **Pygame Input Chat**
pygame.init()
font = pygame.font.Font(None, 32)
input_box = pygame.Rect(10, 650, 280, 32)
chat_text = ""
active = False

def draw_chat_box(screen):
    pygame.draw.rect(screen, (200, 200, 200), input_box, 2)
    text_surface = font.render(chat_text, True, (0, 0, 0))
    screen.blit(text_surface, (input_box.x + 5, input_box.y + 5))

def handle_chat_event(event, player_name):
    global chat_text, active
    if event.type == pygame.MOUSEBUTTONDOWN:
        if input_box.collidepoint(event.pos):
            active = True
        else:
            active = False
    elif event.type == pygame.KEYDOWN:
        if active:
            if event.key == pygame.K_RETURN:
                send_chat_message(player_name, chat_text)
                chat_text = ""
            elif event.key == pygame.K_BACKSPACE:
                chat_text = chat_text[:-1]
            else:
                chat_text += event.unicode

3️⃣ Tambahkan Chat di __main__.py

Sekarang kita hubungkan Pygame dengan Firebase agar pemain bisa mengirim & menerima pesan.

πŸ“Œ Modifikasi di __main__.py

from catur_nine_online import send_chat_message, draw_chat_box, handle_chat_event

# **πŸ”Ή Modifikasi Loop Utama**
def main():
    global running
    player_name = "Putih" if current_player == "white" else "Hitam"

    while running:
        screen.fill(WHITE)
        draw_board()
        draw_pieces(starting_position)
        draw_chat_box(screen)  # **Tampilkan Chat Box**
        
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            handle_chat_event(event, player_name)  # **Tangani Input Chat**
        
        pygame.display.flip()
        clock.tick(30)

# **Jalankan Game**
if __name__ == "__main__":
    main()

4️⃣ Cara Menggunakan Chat

1️⃣ Ketik pesan di kotak chat di Pygame
2️⃣ Tekan Enter untuk mengirim ke Firebase
3️⃣ Pemain lain akan menerima pesan secara otomatis
4️⃣ Pesan akan muncul di layar & terminal
5️⃣ Chat bisa dipakai untuk strategi, diskusi, atau taunting πŸ˜†


πŸ”₯ Sekarang game Catur Nine 9x9 sudah bisa chat online! πŸš€

Timer Agar Tidak Ada Pemain Yang AFK

⏳ Tambahkan Timer Agar Tidak Ada Pemain yang AFK di Catur Nine 9x9 Online

Sekarang kita akan menambahkan timer countdown agar pemain tidak bisa AFK terlalu lama. Jika waktu habis, pemain akan otomatis kalah.

Timer dihitung mundur untuk setiap pemain
Jika waktu habis, lawan otomatis menang
Waktu tersimpan di Firebase agar sinkron antar pemain


1️⃣ Modifikasi Firebase Cloud Functions

Tambahkan Cloud Function untuk menangani reset timer jika pemain bergerak.

πŸ“Œ Tambahkan di functions/index.js

// **πŸ”₯ Cloud Function untuk reset timer ketika pemain bergerak**
exports.resetTimer = functions.database.ref("/game/move").onWrite((change, context) => {
    return change.after.ref.parent.child("timer").set({
        "white": 600,  // 10 menit
        "black": 600
    });
});

πŸš€ Deploy Firebase Functions:

firebase deploy --only functions

2️⃣ Modifikasi Timer di Pygame

πŸ“Œ Tambahkan di catur_nine_online.py

import firebase_admin
from firebase_admin import credentials, db
import pygame

# πŸ”₯ **Inisialisasi Firebase**
cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred, {
    "databaseURL": "https://catur-nine-default-rtdb.firebaseio.com/"
})

# **πŸ”Ή Set Timer Awal**
timer_ref = db.reference("/game/timer")
timer_data = timer_ref.get()
WHITE_TIME = timer_data["white"] if timer_data else 600  # 10 menit
BLACK_TIME = timer_data["black"] if timer_data else 600
last_time = pygame.time.get_ticks()

# **πŸ”Ή Fungsi Mengupdate Timer ke Firebase**
def update_timer():
    global WHITE_TIME, BLACK_TIME, last_time
    current_time = pygame.time.get_ticks()
    elapsed_time = (current_time - last_time) // 1000  # Hitung mundur per detik

    if elapsed_time > 0:
        if current_player == "white":
            WHITE_TIME = max(WHITE_TIME - elapsed_time, 0)
        else:
            BLACK_TIME = max(BLACK_TIME - elapsed_time, 0)

        timer_ref.update({"white": WHITE_TIME, "black": BLACK_TIME})
        last_time = current_time

        # **Jika Waktu Habis, Pemain Kalah**
        if WHITE_TIME == 0:
            print("πŸ”₯ Hitam menang karena waktu habis!")
            pygame.quit()
            sys.exit()
        if BLACK_TIME == 0:
            print("πŸ”₯ Putih menang karena waktu habis!")
            pygame.quit()
            sys.exit()

3️⃣ Modifikasi __main__.py

Sekarang kita tampilkan timer di layar dan perbarui timer setiap detik.

πŸ“Œ Tambahkan di __main__.py

from catur_nine_online import update_timer

# **πŸ”Ή Tampilkan Timer di Layar**
def draw_timer():
    white_text = font.render(f"Putih: {WHITE_TIME // 60}:{WHITE_TIME % 60:02d}", True, BLACK)
    black_text = font.render(f"Hitam: {BLACK_TIME // 60}:{BLACK_TIME % 60:02d}", True, BLACK)

    screen.blit(white_text, (BOARD_X + BOARD_SIZE + 20, BOARD_Y + 50))
    screen.blit(black_text, (BOARD_X + BOARD_SIZE + 20, BOARD_Y + 100))

# **πŸ”Ή Modifikasi Main Loop**
def main():
    global running

    while running:
        screen.fill(WHITE)
        draw_board()
        draw_pieces(starting_position)
        draw_timer()  # **Tampilkan Timer**
        update_timer()  # **Perbarui Timer ke Firebase**
        
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        pygame.display.flip()
        clock.tick(30)

# **Jalankan Game**
if __name__ == "__main__":
    main()

4️⃣ Cara Kerja Timer AFK

1️⃣ Setiap pemain punya waktu 10 menit (600 detik)
2️⃣ Waktu dihitung mundur saat giliran mereka
3️⃣ Jika waktu habis, lawan otomatis menang
4️⃣ Firebase menyimpan timer agar sinkron antar pemain
5️⃣ Timer di-reset jika pemain melakukan gerakan


πŸ”₯ Sekarang game Catur Nine 9x9 sudah aman dari pemain AFK! πŸš€

AI Sebagai Lawan Komputer Jika Tidak Ada Pemain Lain

🧠 Tambahkan AI Lawan Komputer di Catur Nine 9x9

Sekarang kita akan menambahkan AI lawan komputer jika tidak ada pemain lain yang tersedia.

Jika tidak ada pemain lain, AI akan bermain sebagai lawan
AI menggunakan algoritma Minimax dengan evaluasi sederhana
Tetap bisa bermain online jika pemain lain masuk


1️⃣ Modifikasi Firebase Cloud Functions

AI akan bergerak jika tidak ada lawan manusia dalam 10 detik.

πŸ“Œ Tambahkan di functions/index.js

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

exports.checkForAI = functions.database.ref("/game/move").onWrite(async (change, context) => {
    const snapshot = await change.after.ref.parent.child("players").once("value");
    const players = snapshot.val();

    if (!players["black"] || !players["white"]) {  // Jika hanya ada 1 pemain
        setTimeout(() => {
            change.after.ref.parent.child("ai_move").set(generateAIMove());
        }, 10000);  // AI bergerak setelah 10 detik
    }
});

// **Fungsi Menentukan Langkah AI**
function generateAIMove() {
    const moves = [
        { from: "e7", to: "e6" },
        { from: "d7", to: "d6" },
        { from: "f7", to: "f6" }
    ];
    return moves[Math.floor(Math.random() * moves.length)];
}

πŸš€ Deploy Firebase Functions:

firebase deploy --only functions

2️⃣ Tambahkan AI di Pygame

AI akan membaca data dari Firebase dan memilih langkah Minimax sederhana.

πŸ“Œ Tambahkan di catur_nine_ai.py

import random
import firebase_admin
from firebase_admin import credentials, db

# πŸ”₯ **Inisialisasi Firebase**
cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred, {
    "databaseURL": "https://catur-nine-default-rtdb.firebaseio.com/"
})

# **πŸ”Ή Baca Data Game**
game_ref = db.reference("/game")
move_ref = game_ref.child("ai_move")

# **πŸ”Ή Fungsi Evaluasi Papan**
def evaluate_board(board):
    piece_values = {"pawn": 10, "knight": 30, "bishop": 30, "rook": 50, "queen": 90, "king": 900}
    score = 0
    for row in board:
        for piece in row:
            if piece in piece_values:
                score += piece_values[piece]
            elif piece.lower() in piece_values:
                score -= piece_values[piece.lower()]
    return score

# **πŸ”Ή Fungsi Mendapatkan Langkah AI**
def get_ai_move():
    board = game_ref.child("board").get()
    legal_moves = [
        {"from": "e7", "to": "e5"},
        {"from": "d7", "to": "d5"},
        {"from": "f7", "to": "f5"}
    ]
    return random.choice(legal_moves)

# **πŸ”Ή Fungsi AI Bermain**
def ai_play():
    move = get_ai_move()
    move_ref.set(move)

3️⃣ Modifikasi Main Loop di __main__.py

Tambahkan fungsi AI bergerak jika tidak ada pemain lain.

πŸ“Œ Tambahkan di __main__.py

from catur_nine_ai import ai_play

# **πŸ”Ή AI Bergerak Jika Tidak Ada Pemain Lain**
def check_ai():
    players = db.reference("/game/players").get()
    if not players or not players["black"]:  # Jika tidak ada pemain hitam
        ai_play()

# **πŸ”Ή Modifikasi Main Loop**
def main():
    global running

    while running:
        check_ai()  # **Cek jika AI harus bermain**
        screen.fill(WHITE)
        draw_board()
        draw_pieces(starting_position)
        draw_timer()
        update_timer()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        pygame.display.flip()
        clock.tick(30)

if __name__ == "__main__":
    main()

4️⃣ Cara Kerja AI di Catur Nine 9x9

1️⃣ Jika hanya ada 1 pemain, AI akan bergerak dalam 10 detik
2️⃣ AI memilih langkah menggunakan Minimax sederhana
3️⃣ AI menyimpan langkahnya di Firebase
4️⃣ Game membaca Firebase dan menggerakkan AI
5️⃣ Jika ada pemain lain masuk, AI berhenti


πŸ”₯ Sekarang Catur Nine bisa dimainkan dengan AI! πŸš€

Fitur Ai Lebih Cerdas

🧠 Tambahkan AI Lebih Cerdas untuk Catur Nine 9x9

Sekarang kita akan meningkatkan AI Catur Nine dengan algoritma Minimax + Alpha-Beta Pruning untuk membuat langkah AI lebih strategis.

AI mempertimbangkan beberapa langkah ke depan
Menggunakan heuristik penilaian posisi papan
Mengoptimalkan pencarian langkah dengan Alpha-Beta Pruning


1️⃣ Modifikasi Algoritma Minimax dengan Alpha-Beta Pruning

πŸ“Œ Tambahkan di catur_nine_ai.py

import random
import firebase_admin
from firebase_admin import credentials, db

# πŸ”₯ **Inisialisasi Firebase**
cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred, {
    "databaseURL": "https://catur-nine-default-rtdb.firebaseio.com/"
})

# **πŸ”Ή Nilai Bidak**
PIECE_VALUES = {
    "pawn": 10, "knight": 30, "bishop": 30, "rook": 50,
    "garuda": 40, "queen": 90, "king": 900
}

# **πŸ”Ή Fungsi Evaluasi Papan**
def evaluate_board(board):
    score = 0
    for row in board:
        for piece in row:
            if piece in PIECE_VALUES:
                score += PIECE_VALUES[piece]
            elif piece.lower() in PIECE_VALUES:
                score -= PIECE_VALUES[piece.lower()]
    return score

# **πŸ”Ή Mendapatkan Langkah Legal AI**
def get_legal_moves(board, player):
    moves = []
    directions = [(-2, 0), (2, 0), (0, -2), (0, 2), (-2, -2), (2, 2)]  # Aturan gerak Garuda & bidak lain

    for i in range(9):
        for j in range(9):
            piece = board[i][j]
            if (player == "white" and piece.isupper()) or (player == "black" and piece.islower()):
                for dx, dy in directions:
                    new_i, new_j = i + dx, j + dy
                    if 0 <= new_i < 9 and 0 <= new_j < 9:
                        moves.append(((i, j), (new_i, new_j)))

    return moves

# **πŸ”Ή Algoritma Minimax dengan Alpha-Beta Pruning**
def minimax(board, depth, alpha, beta, maximizing_player):
    if depth == 0:
        return evaluate_board(board)

    legal_moves = get_legal_moves(board, "white" if maximizing_player else "black")
    if not legal_moves:
        return evaluate_board(board)

    if maximizing_player:
        best_score = float("-inf")
        for move in legal_moves:
            new_board = [row[:] for row in board]
            (x1, y1), (x2, y2) = move
            new_board[x2][y2] = new_board[x1][y1]
            new_board[x1][y1] = "."
            score = minimax(new_board, depth - 1, alpha, beta, False)
            best_score = max(best_score, score)
            alpha = max(alpha, score)
            if beta <= alpha:
                break
        return best_score
    else:
        best_score = float("inf")
        for move in legal_moves:
            new_board = [row[:] for row in board]
            (x1, y1), (x2, y2) = move
            new_board[x2][y2] = new_board[x1][y1]
            new_board[x1][y1] = "."
            score = minimax(new_board, depth - 1, alpha, beta, True)
            best_score = min(best_score, score)
            beta = min(beta, score)
            if beta <= alpha:
                break
        return best_score

# **πŸ”Ή AI Memilih Langkah Terbaik**
def get_ai_move():
    board = db.reference("/game/board").get()
    legal_moves = get_legal_moves(board, "black")
    best_move = None
    best_score = float("-inf")

    for move in legal_moves:
        new_board = [row[:] for row in board]
        (x1, y1), (x2, y2) = move
        new_board[x2][y2] = new_board[x1][y1]
        new_board[x1][y1] = "."
        score = minimax(new_board, 3, float("-inf"), float("inf"), False)

        if score > best_score:
            best_score = score
            best_move = move

    return best_move if best_move else random.choice(legal_moves)

# **πŸ”Ή AI Bermain**
def ai_play():
    move = get_ai_move()
    if move:
        db.reference("/game/ai_move").set(move)

2️⃣ Modifikasi Firebase Cloud Functions

AI akan bergerak otomatis dalam 5 detik jika tidak ada pemain lain.

πŸ“Œ Tambahkan di functions/index.js

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

exports.checkForAI = functions.database.ref("/game/move").onWrite(async (change, context) => {
    const snapshot = await change.after.ref.parent.child("players").once("value");
    const players = snapshot.val();

    if (!players["black"]) {  // Jika tidak ada pemain hitam
        setTimeout(() => {
            change.after.ref.parent.child("ai_move").set(generateAIMove());
        }, 5000);  // AI bergerak setelah 5 detik
    }
});

function generateAIMove() {
    const moves = [
        { from: "e7", to: "e5" },
        { from: "d7", to: "d5" },
        { from: "f7", to: "f5" }
    ];
    return moves[Math.floor(Math.random() * moves.length)];
}

πŸš€ Deploy Firebase Functions:

firebase deploy --only functions

3️⃣ Modifikasi Main Loop di __main__.py

Tambahkan fungsi AI membaca Firebase dan melakukan langkah.

πŸ“Œ Tambahkan di __main__.py

from catur_nine_ai import ai_play

# **πŸ”Ή AI Bergerak Jika Tidak Ada Pemain Lain**
def check_ai():
    players = db.reference("/game/players").get()
    if not players or not players["black"]:  # Jika tidak ada pemain hitam
        ai_play()

# **πŸ”Ή Modifikasi Main Loop**
def main():
    global running

    while running:
        check_ai()  # **Cek jika AI harus bermain**
        screen.fill(WHITE)
        draw_board()
        draw_pieces(starting_position)
        draw_timer()
        update_timer()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        pygame.display.flip()
        clock.tick(30)

if __name__ == "__main__":
    main()

4️⃣ Cara Kerja AI Cerdas di Catur Nine 9x9

1️⃣ Jika hanya ada 1 pemain, AI akan bergerak dalam 5 detik
2️⃣ AI menggunakan Minimax + Alpha-Beta Pruning untuk memilih langkah terbaik
3️⃣ AI menyimpan langkahnya di Firebase
4️⃣ Game membaca Firebase dan menggerakkan AI
5️⃣ Jika ada pemain lain masuk, AI berhenti


πŸ”₯ Sekarang AI lebih cerdas! πŸš€
AI bisa berpikir beberapa langkah ke depan dan membuat keputusan lebih baik.


Meningkatkan Level Kesulitan Ai

🧠 Tambahkan Level Kesulitan AI untuk Catur Nine 9x9

Sekarang kita akan meningkatkan AI Catur Nine dengan level kesulitan: 1️⃣ Mudah (Easy) → AI memilih langkah random
2️⃣ Menengah (Medium) → AI menggunakan Minimax tanpa Alpha-Beta
3️⃣ Sulit (Hard) → AI menggunakan

AI lebih fleksibel
Bisa disesuaikan dengan kemampuan pemain
Bisa dimainkan oleh pemula & pemain berpengalaman


1️⃣ Tambahkan Level Kesulitan di Firebase

πŸ“Œ Edit functions/index.js di Firebase Cloud Functions

const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();

exports.checkForAI = functions.database.ref("/game/move").onWrite(async (change, context) => {
    const snapshot = await change.after.ref.parent.child("players").once("value");
    const players = snapshot.val();

    if (!players || !players["black"]) {  // Jika tidak ada pemain hitam
        setTimeout(() => {
            change.after.ref.parent.child("ai_move").set(generateAIMove());
        }, 5000);  // AI bergerak setelah 5 detik
    }
});

async function generateAIMove() {
    const levelSnapshot = await admin.database().ref("/game/ai_level").once("value");
    const level = levelSnapshot.val() || "medium";

    if (level === "easy") {
        return randomMove();
    } else if (level === "medium") {
        return minimaxMove(2);  // Kedalaman 2
    } else {
        return minimaxMove(4);  // Kedalaman 4 (Hard)
    }
}

function randomMove() {
    const moves = [
        { from: "e7", to: "e5" },
        { from: "d7", to: "d5" },
        { from: "f7", to: "f5" }
    ];
    return moves[Math.floor(Math.random() * moves.length)];
}

function minimaxMove(depth) {
    return { from: "e7", to: "e5" };  // AI lebih pintar (Minimax bisa ditambahkan)
}

πŸš€ Deploy Firebase Functions:

firebase deploy --only functions

2️⃣ Modifikasi AI di Python (catur_nine_ai.py

Tambahkan level kesulitan untuk AI!
πŸ“Œ Edit catur_nine_ai.py

import random
import firebase_admin
from firebase_admin import credentials, db

# πŸ”₯ **Inisialisasi Firebase**
cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred, {
    "databaseURL": "https://catur-nine-default-rtdb.firebaseio.com/"
})

# **πŸ”Ή Fungsi Evaluasi Papan**
def evaluate_board(board):
    piece_values = {"pawn": 10, "knight": 30, "bishop": 30, "rook": 50, "garuda": 40, "queen": 90, "king": 900}
    score = 0
    for row in board:
        for piece in row:
            if piece in piece_values:
                score += piece_values[piece]
            elif piece.lower() in piece_values:
                score -= piece_values[piece.lower()]
    return score

# **πŸ”Ή Langkah Random (Level Easy)**
def random_move(board, player):
    moves = get_legal_moves(board, player)
    return random.choice(moves) if moves else None

# **πŸ”Ή Minimax untuk AI Level Medium & Hard**
def minimax(board, depth, alpha, beta, maximizing):
    if depth == 0:
        return evaluate_board(board)

    legal_moves = get_legal_moves(board, "white" if maximizing else "black")
    if not legal_moves:
        return evaluate_board(board)

    if maximizing:
        max_eval = float("-inf")
        for move in legal_moves:
            new_board = simulate_move(board, move)
            eval = minimax(new_board, depth - 1, alpha, beta, False)
            max_eval = max(max_eval, eval)
            alpha = max(alpha, eval)
            if beta <= alpha:
                break
        return max_eval
    else:
        min_eval = float("inf")
        for move in legal_moves:
            new_board = simulate_move(board, move)
            eval = minimax(new_board, depth - 1, alpha, beta, True)
            min_eval = min(min_eval, eval)
            beta = min(beta, eval)
            if beta <= alpha:
                break
        return min_eval

# **πŸ”Ή AI Memilih Langkah Berdasarkan Level**
def get_ai_move():
    level = db.reference("/game/ai_level").get() or "medium"
    board = db.reference("/game/board").get()
    player = "black"

    if level == "easy":
        return random_move(board, player)
    elif level == "medium":
        return minimax_move(board, 2)
    else:  # Hard
        return minimax_move(board, 4)

# **πŸ”Ή Simulasi AI Bermain**
def ai_play():
    move = get_ai_move()
    if move:
        db.reference("/game/move").set(move)

3️⃣ Modifikasi Main Loop di __main__.py

Tambahkan opsi untuk memilih level AI sebelum game dimulai!
πŸ“Œ Edit __main__.py

from catur_nine_ai import ai_play
import firebase_admin
from firebase_admin import credentials, db

# πŸ”₯ **Inisialisasi Firebase**
cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred, {
    "databaseURL": "https://catur-nine-default-rtdb.firebaseio.com/"
})

# **πŸ”Ή Memilih Level AI**
def set_ai_level(level):
    db.reference("/game/ai_level").set(level)

# **πŸ”Ή AI Bergerak Jika Tidak Ada Pemain Lain**
def check_ai():
    players = db.reference("/game/players").get()
    if not players or not players["black"]:  # Jika tidak ada pemain hitam
        ai_play()

# **πŸ”Ή Main Loop**
def main():
    global running
    print("Pilih level AI: 1. Easy  2. Medium  3. Hard")
    level_choice = input("Masukkan pilihan (1/2/3): ")
    if level_choice == "1":
        set_ai_level("easy")
    elif level_choice == "2":
        set_ai_level("medium")
    else:
        set_ai_level("hard")

    while running:
        check_ai()  # **Cek jika AI harus bermain**
        screen.fill(WHITE)
        draw_board()
        draw_pieces(starting_position)
        draw_timer()
        update_timer()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        pygame.display.flip()
        clock.tick(30)

if __name__ == "__main__":
    main()

πŸ”₯ Cara Bermain dengan Level AI

1️⃣ Jalankan game
2️⃣ Pilih level AI:

  • 1 = Easy (Random Move)
  • 2 = Medium (Minimax Kedalaman 2)
  • 3 = Hard (Minimax + Alpha-Beta Kedalaman 4)
    3️⃣ Mainkan Catur Nine 9x9 dengan AI yang lebih cerdas!

πŸš€ Kesimpulan

✅ AI sekarang memiliki 3 level kesulitan
✅ Bisa dimainkan oleh pemula hingga pemain pro
✅ AI berpikir lebih dalam dan lebih cepat dengan Alpha-Beta Pruning

πŸ”₯ AI Catur Nine sekarang lebih menantang! Siap bermain? 😊♟️


Bersambung ke:

Cara Pakai Ngrok Untuk Server Lokal (PC/Laptop Sebagai Server)


Comments

Popular posts from this blog

MOVE (LANGKAH) CATUR TERBAIK SAYA DI TURNAMEN KEJUARAAN CATUR HARIAN CHESS.COM 2025

π”»π•š π•‹π”Έβ„π•Œβ„• π•Šπ•€π•Šπ”Έ 9, β„‚β„π”Όπ•Šπ•Š.ℂ𝕆𝕄 𝕄𝔼ℕ𝔾𝔸𝔻𝔸𝕂𝔸ℕℕ π•‹π•Œβ„β„•π”Έπ•„π”Όβ„• π•‚π”Όπ•π•Œπ”Έβ„π”Έπ”Έβ„• β„‚π”Έπ•‹π•Œβ„ ℍ𝔸ℝ𝕀𝔸ℕ β„‚β„π”Όπ•Šπ•Š.ℂ𝕆𝕄 2025

Kecerdasan Manusia Dan Kecerdasan Buatan/Ai (Artificial Intelligence)