PERBAIKAN DAN OPTIMALISASI KODE PROGRAM PADA GAME CATUR (Part-2)


PERBAIKAN DAN OPTIMALISASI KODE PROGRAM PADA GAME CATUR

(Part-2)


Perbaikan kode untuk langkah pion, kuda, gajah, benteng, ratu, dan raja sudah baik dan berjalan lancar, dan logika skakmat sudah berjalan dengan baik. 

Logika skakmat dapat dicari pada bagian kode berikut:

1. Pemanggilan Fungsi is_checkmate. 

Semua pemanggilan fungsi is_checkmate hanya mengirimkan tiga argumen, yaitu seperti berikut:

if is_checkmate(turn, pieces, turn):

2. Semua Pemanggilan Lainnya yaitu pemanggilan fungsi is_checkmate lain dalam kode, semuanya sudah diubah sesuai dengan definisi fungsi dengan hanya 3 argumen. Contoh pemanggilan yang salah, seperti ini:

if is_checkmate(color, pieces, turn, last_move, castling_rights):  # Salah

Diubah atau diganti menjadi:

if is_checkmate(color, pieces, turn):  # Benar

3. Definisi Fungsi is_checkmate dipastikan hanya memiliki tiga parameter, yaitu seperti berikut:

def is_checkmate(color, pieces, turn):

    if color != turn:  # Hanya memeriksa raja pada giliran pemain

        return False

    king_position = None

    for piece, positions in pieces.items():

        if piece == f"{color} king":

            if positions:

                king_position = positions[0]

            break

    if not king_position:

        return False

    # Periksa apakah raja tidak memiliki langkah sah lagi

    for dx, dy in [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]:

        new_x = king_position[0] + dx

        new_y = king_position[1] + dy

        if 0 <= new_x < 8 and 0 <= new_y < 8:  # Papan 8x8

            if is_valid_move(king_position, (new_x, new_y), pieces, color):

                return False  # Jika ada langkah sah, bukan skakmat

    return True  # Jika tidak ada langkah sah, maka skakmat

4. Setelah Kode dieksekusi, semua pemanggilan is_checkmate sudah sesuai, sehingga program berjalan dengan baik, posisi raja yang skakmat sudah tidak bisa digerakkan lagi, namun pada kode program masih muncul error.

Error tersebut terjadi karena fungsi  yang dipanggil dalam fungsi is_checkmate membutuhkan 3 argumen tambahan (turn, last_move, dan castling_rights), tetapi hanya diberikan 3 argumen (king_position, (new_x, new_y), dan pieces).

Untuk memperbaiki ini, kita perlu menyesuaikan pemanggilan fungsi is_valid_move di dalam is_checkmate agar menyertakan argumen yang dibutuhkan.

Langkah-Langkah Memperbaiki:

1. Perbarui Pemanggilan is_valid_move di is_checkmate

Tambahkan argumen yang sesuai (turn, last_move, dan castling_rights) ketika memanggil is_valid_move. Berikut modifikasi fungsi is_checkmate:

def is_checkmate(color, pieces, turn, last_move, castling_rights):

    if color != turn:  # Hanya memeriksa raja pada giliran pemain

        return False

    king_position = None

    for piece, positions in pieces.items():

        if piece == f"{color} king":

            if positions:

                king_position = positions[0]

            break

    if not king_position:

        return False

    # Periksa apakah raja tidak memiliki langkah sah lagi

    for dx, dy in [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]:

        new_x = king_position[0] + dx

        new_y = king_position[1] + dy

        if 0 <= new_x < 8 and 0 <= new_y < 8:  # Papan 8x8

            # Tambahkan argumen yang sesuai

            if is_valid_move(king_position, (new_x, new_y), pieces, color, turn, last_move, castling_rights):

                return False  # Jika ada langkah sah, bukan skakmat

    return True  # Jika tidak ada langkah sah, maka skakmat

2. Pastikan Definisi is_valid_move Menerima Argumen Lengkap

Pastikan definisi fungsi is_valid_move sudah mendukung argumen yang dimasukkan (king_position, (new_x, new_y), pieces, color, turn, last_move, dan castling_rights). Contoh definisi fungsi yang benar:

def is_valid_move(start_pos, end_pos, pieces, color, turn, last_move, castling_rights):

    # Logika validasi langkah bidak (sesuai dengan aturan catur)

    return True  # Contoh placeholder, isi sesuai logika Anda

3. Periksa Pemanggilan is_checkmate di Kode Utama

Pastikan pemanggilan is_checkmate di bagian utama kode menyertakan argumen tambahan (last_move dan castling_rights):

if is_checkmate(turn, pieces, turn, last_move, castling_rights):

    print(f"Checkmate! {turn} kalah.")

    running = False  # Berhentikan game atau tampilkan pesan

4. Tes kode program

Jalankan kembali program, pastikan: 

-Saat raja berada dalam posisi skakmat, tidak ada crash.

- Program dapat mendeteksi kondisi checkmate dengan benar.

Kode program tersebut  cukup lengkap dan sudah mencakup sebagian besar aturan permainan catur, seperti gerakan bidak (Pion, Kuda, Gajah, Benteng, Ratu, Raja), penanganan giliran pemain, dan pemeriksaan skak atau skakmat. Namun, ada beberapa catatan yang perlu diperhatikan untuk memastikan kode dapat bekerja dengan benar sesuai dengan aturan permainan catur:

1. Fungsi Validasi Gerakan (is_valid_move):

  • Castling: Tidak ada penanganan castling (rokade) untuk Raja dan Benteng. Tambahkan logika untuk:
    • Memastikan jalur castling bebas dari ancaman bidak lawan.
    • Memastikan Raja dan Benteng belum pernah bergerak sebelumnya.
  • Promosi Pion: Tidak ada penanganan promosi untuk Pion yang mencapai baris terakhir papan.
  • En Passant: Anda telah mulai menambahkan logika en passant, tetapi belum sepenuhnya selesai (khususnya memeriksa jika gerakan terakhir sesuai dengan kondisi en passant).
  • Skakmat: Fungsi is_checkmate cukup baik, tetapi Anda perlu memastikan logika menangani semua langkah sah Raja dan bidak lain di sekitar untuk menghilangkan ancaman terhadap Raja.

2. Pemeriksaan Skak:

  • Fungsi is_king_in_check harus mempertimbangkan semua jenis bidak lawan (misalnya, gerakan diagonal oleh Ratu/Gajah atau gerakan kuda).
  • Tambahkan pengujian khusus untuk memastikan Raja tidak melangkah ke kotak yang dalam ancaman bidak lawan.

3. Visualisasi Papan:

  • Anda sudah mencakup warna untuk kotak yang menunjukkan skak pada Raja, tetapi sebaiknya tambahkan interaksi mouse untuk memilih dan memindahkan bidak.
  • Pastikan ada validasi visual ketika pemain memilih bidak yang bukan miliknya atau mencoba langkah tidak sah.

4. Penanganan Giliran Pemain:

  • Saat ini, giliran diatur dengan variabel turn. Anda perlu memastikan giliran berpindah hanya jika langkah sah dilakukan.

5. Optimisasi Pengecekan Validasi:

  • Fungsi is_valid_move memiliki banyak if-else yang cukup panjang. Sebaiknya bagi ke dalam fungsi terpisah untuk setiap bidak agar lebih modular.

6. Pemrosesan Input Pengguna:

  • Anda belum menambahkan mekanisme input pengguna (misalnya, klik atau drag & drop). Gunakan event pygame.MOUSEBUTTONDOWN untuk memilih dan memindahkan bidak.

Berikut adalah contoh tambahan untuk mendukung beberapa fitur di atas:

Penanganan Input (Mouse Click):

selected_piece = None

def handle_click(pos, pieces, turn):
    global selected_piece
    x, y = pos[0] // TILE_SIZE, pos[1] // TILE_SIZE
    
    if selected_piece:
        # Coba memindahkan bidak
        if is_valid_move(selected_piece, selected_position, (x, y), pieces, turn, last_move, castling_rights):
            pieces[selected_piece].remove(selected_position)
            pieces[selected_piece].append((x, y))
            change_turn()  # Pindah giliran
        selected_piece = None  # Reset pilihan
    else:
        # Pilih bidak
        for piece, positions in pieces.items():
            if (x, y) in positions and piece.startswith(turn):
                selected_piece = piece
                selected_position = (x, y)
                break

Pindah Giliran:

def change_turn():
    global turn
    turn = "white" if turn == "black" else "black"

Kode yang sudah ada sudah sangat baik sebagai dasar permainan catur. Dengan menambahkan beberapa fitur seperti yang disebutkan di atas, permainan akan lebih lengkap dan sesuai dengan aturan resmi. Pastikan untuk terus menguji setiap langkah dan skenario untuk menghindari bug. 

Berikut adalah modifikasi pada fungsi is_valid_move untuk menambahkan logika castling (rokade), promosi pion, en passant, dan skakmat. Saya asumsikan bahwa fungsi ini berada dalam konteks permainan catur yang didefinisikan dengan struktur papan dan langkah bidak.

Modifikasi Kode:

def is_valid_move(board, move, player_turn, history):
    """
    Memeriksa apakah langkah bidak valid, termasuk castling, promosi pion, en passant, dan skakmat.
    
    Args:
    - board: Matriks 2D yang merepresentasikan papan catur.
    - move: Tuple (start_pos, end_pos), posisi awal dan akhir (x, y).
    - player_turn: Pemain saat ini ('white' atau 'black').
    - history: Daftar langkah sebelumnya (untuk en passant dan status bidak).

    Returns:
    - True jika langkah valid, False jika tidak valid.
    """
    start_pos, end_pos = move
    x1, y1 = start_pos
    x2, y2 = end_pos
    piece = board[x1][y1]

    # Langkah dasar: pastikan bidak yang digerakkan milik pemain yang saat ini
    if piece is None or piece.color != player_turn:
        return False

    target_piece = board[x2][y2]

    # 1. Castling (rokade)
    if piece.type == 'king' and abs(y2 - y1) == 2:  # Langkah horizontal 2 kotak
        if not piece.has_moved and board[x1][y2] is None:  # Pastikan raja belum pernah bergerak
            rook_pos = (x1, 0) if y2 < y1 else (x1, 7)  # Posisi benteng (kiri/kanan)
            rook = board[rook_pos[0]][rook_pos[1]]
            if rook and rook.type == 'rook' and not rook.has_moved:  # Pastikan benteng valid
                # Pastikan jalur kosong dan tidak dalam ancaman
                path = range(min(y1, y2) + 1, max(y1, y2))
                if all(board[x1][y] is None for y in path) and not is_path_under_attack(board, path, x1, player_turn):
                    return True
        return False

    # 2. Promosi Pion
    if piece.type == 'pawn':
        if (player_turn == 'white' and x2 == 0) or (player_turn == 'black' and x2 == 7):  # Baris terakhir
            return True  # Promosi valid (harus dilakukan langkah promosi setelahnya)

    # 3. En passant
    if piece.type == 'pawn' and abs(x2 - x1) == 1 and abs(y2 - y1) == 1 and target_piece is None:
        last_move = history[-1] if history else None
        if last_move:
            last_start, last_end = last_move
            last_x1, last_y1 = last_start
            last_x2, last_y2 = last_end
            last_piece = board[last_x2][last_y2]
            if (
                last_piece
                and last_piece.type == 'pawn'
                and abs(last_x2 - last_x1) == 2  # Langkah 2 kotak pion
                and last_y2 == y2
                and last_x2 == x1
            ):
                return True
        return False

    # 4. Skakmat
    if is_king_in_checkmate(board, player_turn):
        return False

    # Logika umum: validasi langkah normal sesuai dengan jenis bidak
    return is_valid_piece_move(board, piece, start_pos, end_pos)


def is_path_under_attack(board, path, row, player_turn):
    """Memeriksa apakah jalur tertentu (rokade) berada dalam ancaman bidak lawan."""
    opponent_turn = 'white' if player_turn == 'black' else 'black'
    for y in path:
        if is_under_attack(board, (row, y), opponent_turn):
            return True
    return False


def is_king_in_checkmate(board, player_turn):
    """Memeriksa apakah raja dalam posisi skakmat."""
    king_pos = find_king(board, player_turn)
    if not king_pos:
        return False  # Tidak ada raja, error atau skenario aneh
    if not is_under_attack(board, king_pos, 'white' if player_turn == 'black' else 'black'):
        return False  # Raja tidak dalam skak

    # Cek apakah ada langkah sah untuk keluar dari skak
    for move in get_all_possible_moves(board, player_turn):
        temp_board = make_move(board, move)
        if not is_under_attack(temp_board, find_king(temp_board, player_turn), 'white' if player_turn == 'black' else 'black'):
            return False  # Ada langkah yang menghindari skak
    return True  # Semua langkah menghasilkan skakmat


def is_under_attack(board, pos, opponent_turn):
    """Memeriksa apakah posisi tertentu berada dalam ancaman bidak lawan."""
    for move in get_all_possible_moves(board, opponent_turn):
        if move[1] == pos:
            return True
    return False


def find_king(board, player_turn):
    """Mencari posisi raja pada papan."""
    for x in range(8):
        for y in range(8):
            piece = board[x][y]
            if piece and piece.type == 'king' and piece.color == player_turn:
                return (x, y)
    return None

Penjelasan:

  1. Castling (Rokade):

    • Mengecek apakah raja dan benteng belum pernah bergerak.
    • Memastikan jalur antara raja dan benteng bebas dari bidak dan ancaman.
  2. Promosi Pion:

    • Memastikan pion yang mencapai baris terakhir dapat dipromosikan.
  3. En Passant:

    • Mengecek apakah langkah terakhir lawan sesuai kondisi en passant (pion bergerak 2 kotak dan berdampingan dengan pion kita).
  4. Skakmat:

    • Mengecek apakah raja dalam posisi skak dan memastikan tidak ada langkah valid yang dapat menghilangkan ancaman.

Catatan:

  • Fungsi tambahan seperti get_all_possible_moves dan make_move diperlukan untuk memeriksa semua langkah valid dan mensimulasikan langkah pada papan.
  • Pastikan history menyimpan riwayat langkah sebelumnya dengan format yang sesuai.


Hasil Perbaikan Kode Selengkapnya: da


import pygame

import sys


# Inisialisasi pygame

pygame.init()


# Ukuran layar

WIDTH, HEIGHT = 720, 720

screen = pygame.display.set_mode((WIDTH, HEIGHT))

pygame.display.set_caption("Chess Game")


# Warna

WHITE = (255, 255, 255)

BLACK = (0, 0, 0)

GRAY = (192, 192, 192)

LIGHT_BROWN = (240, 217, 181)

DARK_BROWN = (181, 136, 99)


# Ukuran setiap kotak papan catur

TILE_SIZE = WIDTH // 8

FPS = 60


# Gambar bidak catur

PIECE_IMAGES = {}

for color in ["white", "black"]:

    for piece in ["king", "queen", "rook", "bishop", "knight", "pawn"]:

        PIECE_IMAGES[f"{color} {piece}"] = pygame.transform.scale(

            pygame.image.load(f'/storage/emulated/0/AppJadi/Catur/images/{color} {piece}.png'), (TILE_SIZE, TILE_SIZE)

        )


# Posisi awal bidak catur

STARTING_POSITIONS = {

    "white": {

        "rook": [(0, 7), (7, 7)],

        "knight": [(1, 7), (6, 7)],

        "bishop": [(2, 7), (5, 7)],

        "queen": [(3, 7)],

        "king": [(4, 7)],

        "pawn": [(x, 6) for x in range(8)],

    },

    "black": {

        "rook": [(0, 0), (7, 0)],

        "knight": [(1, 0), (6, 0)],

        "bishop": [(2, 0), (5, 0)],

        "queen": [(3, 0)],

        "king": [(4, 0)],

        "pawn": [(x, 1) for x in range(8)],

    },

}


selected_piece = None


def handle_click(pos, pieces, turn):

    global selected_piece

    x, y = pos[0] // TILE_SIZE, pos[1] // TILE_SIZE

    

    if selected_piece:

        # Coba memindahkan bidak

        if is_valid_move(selected_piece, selected_position, (x, y), pieces, turn, last_move, castling_rights):

            pieces[selected_piece].remove(selected_position)

            pieces[selected_piece].append((x, y))

            change_turn()  # Pindah giliran

        selected_piece = None  # Reset pilihan

    else:

        # Pilih bidak

        for piece, positions in pieces.items():

            if (x, y) in positions and piece.startswith(turn):

                selected_piece = piece

                selected_position = (x, y)

                break

                

turn = "white"  # Awal permainan dimulai dengan giliran putih


def change_turn():

    global turn

    turn = "white" if turn == "black" else "black"


castling_rights = {

    "white": {"king_side": True, "queen_side": True},

    "black": {"king_side": True, "queen_side": True}

}

    

# Fungsi menggambar papan

def draw_board(pieces, turn):

    for y in range(8):

        for x in range(8):

            color = (240, 217, 181) if (x + y) % 2 == 0 else (181, 136, 99)

            pygame.draw.rect(screen, color, (x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE))


    for color in ["white", "black"]:

        if is_king_in_check(color, pieces):  # Jika raja dalam skak

            king_position = None

            for piece, positions in pieces.items():

                if piece == f"{color} king":

                    if positions:

                        king_position = positions[0]

                    break

            if king_position:

                x, y = king_position

                pygame.draw.rect(screen, (255, 0, 0), (x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE))  # Skak (merah)

                

# Fungsi menggambar bidak

def draw_pieces(pieces):

    for piece, positions in pieces.items():

        for pos in positions:

            x, y = pos

            screen.blit(PIECE_IMAGES[piece], (x * TILE_SIZE, y * TILE_SIZE))


# Fungsi mengatur posisi awal bidak

def initialize_pieces():

    pieces = {}

    for color in STARTING_POSITIONS:

        for piece, positions in STARTING_POSITIONS[color].items():

            pieces[f"{color} {piece}"] = positions

    return pieces

    

# Fungsi untuk memeriksa apakah kotak terisi oleh bidak

def is_occupied(position, pieces):

    for piece, positions in pieces.items():

        if position in positions:

            return True

    return False


# Fungsi untuk memeriksa apakah kotak terisi oleh bidak lawan

def is_occupied_by_enemy(position, color, pieces):

    for piece, positions in pieces.items():

        piece_color, _ = piece.split(' ')

        if position in positions and piece_color != color:

            return True

    return False


# Gerakan en passant

last_move = None  # Variabel global untuk melacak gerakan terakhir


def is_king_in_check(color, pieces):

    king_position = None

    # Temukan posisi Raja warna tertentu

    for piece, positions in pieces.items():

        if piece == f"{color} king" and positions:  # Pastikan ada posisi Raja

            king_position = positions[0]

            break


    if not king_position:  # Jika tidak ada posisi Raja, bukan skak

        return False


    # Periksa apakah ada bidak lawan yang dapat menyerang posisi Raja

    for piece, positions in pieces.items():

        enemy_color, p_type = piece.split(' ')

        if enemy_color != color:  # Hanya periksa bidak lawan

            for pos in positions:

                if is_valid_move(piece, pos, king_position, pieces, enemy_color, None, None):

                    return True  # Raja dalam skak

    return False

                

def is_checkmate(color, pieces, turn):

    if color != turn:  # Hanya memeriksa raja pada giliran pemain

        return False


    king_position = None

    for piece, positions in pieces.items():

        if piece == f"{color} king":

            if positions:

                king_position = positions[0]

            break


    if not king_position:

        return False


    # Periksa apakah raja tidak memiliki langkah sah lagi

    for dx, dy in [(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]:

        new_x = king_position[0] + dx

        new_y = king_position[1] + dy

        if 0 <= new_x < 8 and 0 <= new_y < 8:  # Papan 8x8

            if is_valid_move(king_position, (new_x, new_y), pieces, color, turn, last_move, castling_rights):

                return False  # Jika ada langkah sah, bukan skakmat


    return True  # Jika tidak ada langkah sah, maka skakmat


# Fungsi untuk mempromosikan pion

def promote_pawn(pawn_position, color, pieces):

    pieces[f"{color} queen"].append(pawn_position)

    pieces[f"{color} pawn"].remove(pawn_position)


# Fungsi untuk validasi gerakan bidak

def is_valid_move(piece, start, end, pieces, turn, last_move, castling_rights):

    color, p_type = piece.split(' ')

    x1, y1 = start

    x2, y2 = end


    if color != turn:  # Hanya bidak pada giliran yang sesuai

        return False        

        

       # Castling (rokade)

    if p_type == 'king' and abs(y2 - y1) == 2:  # Langkah horizontal 2 kotak

        if not piece.has_moved and board[x1][y2] is None:  # Pastikan raja belum pernah bergerak

            rook_pos = (x1, 0) if y2 < y1 else (x1, 7)  # Posisi benteng (kiri/kanan)

            rook = board[rook_pos[0]][rook_pos[1]]

            if rook and rook.type == 'rook' and not rook.has_moved:  # Pastikan benteng valid

                # Pastikan jalur kosong dan tidak dalam ancaman

                path = range(min(y1, y2) + 1, max(y1, y2))

                if all(board[x1][y] is None for y in path) and not is_path_under_attack(board, path, x1, player_turn):

                    return True

        return False      

        

    # Promosi Pion

    if p_type == 'pawn':

        if (turn == 'white' and x2 == 0) or (turn == 'black' and x2 == 7):  # Baris terakhir

            return True  # Promosi valid (harus dilakukan langkah promosi setelahnya)

              

    # En passant

    if p_type == 'pawn' and abs(x2 - x1) == 1 and abs(y2 - y1) == 1 and last_move is None:

        last_move = history[-1] if history else None

        if last_move:

            last_start, last_end = last_move

            last_x1, last_y1 = last_start

            last_x2, last_y2 = last_end

            last_piece = board[last_x2][last_y2]

            if (

                last_piece

                and last_piece.type == 'pawn'

                and abs(last_x2 - last_x1) == 2  # Langkah 2 kotak pion

                and last_y2 == y2

                and last_x2 == x1

            ):

                return True

        return False


                                                       

    # Gerakan Pion (Pawn)

    if p_type == "pawn":

        direction = -1 if color == "white" else 1


        # Gerakan maju satu langkah

        if x1 == x2 and y2 - y1 == direction and not is_occupied(end, pieces):

            return True


        # Gerakan maju dua langkah dari posisi awal

        if x1 == x2 and y2 - y1 == 2 * direction and not is_occupied(end, pieces):

            start_row = 6 if color == "white" else 1

            mid_position = (x1, y1 + direction)

            if y1 == start_row and not is_occupied(mid_position, pieces):

                return True


        # Serangan diagonal

        if abs(x1 - x2) == 1 and y2 - y1 == direction:

            if is_occupied_by_enemy(end, color, pieces):  # Menyerang bidak lawan

                return True

                        

       # Gerakan en passant

        if last_move:

            ((last_start_x, last_start_y), (last_end_x, last_end_y), last_piece) = last_move

            if last_piece.endswith("pawn"):

                if last_start_y == (y1 + 2 * direction) and last_end_y == y1 and last_end_x == x2:

                    return True                       


    # Gerakan Kuda (Knight)

    if p_type == "knight":

        dx = abs(x2 - x1)

        dy = abs(y2 - y1)


        # Cek apakah langkah berbentuk "L"

        if (dx == 2 and dy == 1) or (dx == 1 and dy == 2):

            # Kotak tujuan harus kosong atau diisi bidak lawan

            if not is_occupied(end, pieces) or is_occupied_by_enemy(end, color, pieces):

                return True

                

    # Gerakan Gajah (Bishop)

    if p_type == "bishop":

        dx = abs(x2 - x1)

        dy = abs(y2 - y1)


        # Gerakan Gajah adalah diagonal (|dx| == |dy|)

        if dx == dy:

            # Cek apakah ada halangan di sepanjang jalur diagonal

            step_x = 1 if x2 > x1 else -1

            step_y = 1 if y2 > y1 else -1


            for i in range(1, dx):  # Iterasi melalui jalur diagonal, kecuali kotak tujuan

                intermediate_x = x1 + i * step_x

                intermediate_y = y1 + i * step_y

                if is_occupied((intermediate_x, intermediate_y), pieces):

                    return False


            # Kotak tujuan harus kosong atau diisi oleh bidak lawan

            if not is_occupied(end, pieces) or is_occupied_by_enemy(end, color, pieces):

                return True                


    # Gerakan Benteng (Rook)

    if p_type == "rook":

        # Gerakan Benteng adalah horizontal atau vertikal

        if x1 == x2 or y1 == y2:

            # Cek apakah ada halangan di sepanjang jalur horizontal atau vertikal

            step_x = 1 if x2 > x1 else -1 if x2 < x1 else 0

            step_y = 1 if y2 > y1 else -1 if y2 < y1 else 0


            distance = abs(x2 - x1) if step_x != 0 else abs(y2 - y1)

            for i in range(1, distance):  # Iterasi melalui jalur, kecuali kotak tujuan

                intermediate_x = x1 + i * step_x

                intermediate_y = y1 + i * step_y

                if is_occupied((intermediate_x, intermediate_y), pieces):

                    return False


            # Kotak tujuan harus kosong atau diisi oleh bidak lawan

            if not is_occupied(end, pieces) or is_occupied_by_enemy(end, color, pieces):

                return True


    # Gerakan Ratu (Queen)

    if p_type == "queen":

        dx = abs(x2 - x1)

        dy = abs(y2 - y1)


        # Ratu dapat bergerak seperti Gajah (diagonal) atau Benteng (horizontal/vertikal)

        if dx == dy or x1 == x2 or y1 == y2:

            # Cek jalur jika bergerak seperti Gajah

            if dx == dy:

                step_x = 1 if x2 > x1 else -1

                step_y = 1 if y2 > y1 else -1


                for i in range(1, dx):

                    intermediate_x = x1 + i * step_x

                    intermediate_y = y1 + i * step_y

                    if is_occupied((intermediate_x, intermediate_y), pieces):

                        return False


            # Cek jalur jika bergerak seperti Benteng

            elif x1 == x2 or y1 == y2:

                step_x = 1 if x2 > x1 else -1 if x2 < x1 else 0

                step_y = 1 if y2 > y1 else -1 if y2 < y1 else 0


                distance = abs(x2 - x1) if step_x != 0 else abs(y2 - y1)

                for i in range(1, distance):

                    intermediate_x = x1 + i * step_x

                    intermediate_y = y1 + i * step_y

                    if is_occupied((intermediate_x, intermediate_y), pieces):

                        return False


            # Kotak tujuan harus kosong atau diisi oleh bidak lawan

            if not is_occupied(end, pieces) or is_occupied_by_enemy(end, color, pieces):

                return True


   # Gerakan Raja (King)

    if p_type == "king":

        dx = abs(x2 - x1)

        dy = abs(y2 - y1)


        # Raja dapat bergerak satu langkah ke segala arah

        if dx <= 1 and dy <= 1:

            if not is_occupied(end, pieces) or is_occupied_by_enemy(end, color, pieces):

                return True

                            

        # Gerakan rokade (castling)

        if dx == 2 and dy == 0:

            # Rokade hanya dapat dilakukan jika Raja belum bergerak

            if color == "white" and start == (4, 7):

                if x2 == 2 and castling_rights["white"]["queen_side"]:  # Rokade sisi menteri

                    if not is_occupied((3, 7), pieces) and not is_occupied((2, 7), pieces):

                        return True

                if x2 == 6 and castling_rights["white"]["king_side"]:  # Rokade sisi raja

                    if not is_occupied((5, 7), pieces) and not is_occupied((6, 7), pieces):

                        return True


            if color == "black" and start == (4, 0):

                if x2 == 2 and castling_rights["black"]["queen_side"]:  # Rokade sisi menteri

                    if not is_occupied((3, 0), pieces) and not is_occupied((2, 0), pieces):

                        return True

                if x2 == 6 and castling_rights["black"]["king_side"]:  # Rokade sisi raja

                    if not is_occupied((5, 0), pieces) and not is_occupied((6, 0), pieces):

                        return True

                        

            if piece == f"{turn} king" and is_checkmate(turn, pieces, turn):

            print(f"{turn.capitalize()} king is checkmated! No moves allowed.")

            return False  # Langkah tidak diizinkan

            

        # Validasi langkah raja untuk skak

            if (x, y) in pieces and pieces[(x, y)].startswith(color):

            return False  # Tidak bisa bergerak ke posisi yang memiliki bidak sendiri


    # Jika langkah menyebabkan raja skak, kembalikan False

            if causes_check(start_pos, end_pos, pieces, color):

            return False

            

            return True

    

                      

    # Gerakan lainnya tetap sama

    return is_valid_move_base(piece, start, end, pieces, turn, last_move)

        

# Fungsi dasar untuk validasi gerakan lainnya

def is_valid_move_base(piece, start, end, pieces, turn, last_move):

    # (Kode validasi gerakan pion, ratu, dll, tetap seperti sebelumnya)

    pass


    return False


# Fungsi utama permainan (dengan tambahan pengaturan rokade)

def main():

    global last_move  # Menggunakan variabel global untuk melacak gerakan terakhir

    clock = pygame.time.Clock()

    pieces = initialize_pieces()

    selected_piece = None

    selected_position = None

    turn = "white"


    # Hak rokade untuk kedua pemain

    castling_rights = {

        "white": {"king_side": True, "queen_side": True},

        "black": {"king_side": True, "queen_side": True},

    }


    running = True

    while running:

        screen.fill(GRAY)

        draw_board(pieces, turn)

        draw_pieces(pieces)


        for event in pygame.event.get():

            if event.type == pygame.QUIT:

                pygame.quit()

                sys.exit()

            if event.type == pygame.MOUSEBUTTONDOWN:

                mouse_x, mouse_y = pygame.mouse.get_pos()

                col, row = mouse_x // TILE_SIZE, mouse_y // TILE_SIZE

                                

                if selected_piece:

                    if is_valid_move(selected_piece, selected_position, (col, row), pieces, turn, last_move, castling_rights):

                        # Hapus bidak lawan jika diserang

                        if is_occupied_by_enemy((col, row), turn, pieces):

                            for piece, positions in pieces.items():

                                if (col, row) in positions:

                                    positions.remove((col, row))

                                    break

                                    

                                if selected_position:

                                pygame.draw.rect(screen, GRAY, (selected_position[0] * TILE_SIZE, selected_position[1] * TILE_SIZE, TILE_SIZE, TILE_SIZE), 5)     

                                      

                        # Gerakan en passant

                        if selected_piece.endswith("pawn") and last_move:

                            ((last_start_x, last_start_y), (last_end_x, last_end_y), last_piece) = last_move

                            if last_piece.endswith("pawn") and abs(selected_position[0] - col) == 1:

                                if (last_start_y == selected_position[1] + 2 * (-1 if turn == "white" else 1)) and last_end_y == selected_position[1] and last_end_x == col:

                                    pieces[last_piece].remove((last_end_x, last_end_y))    


                        # Pindahkan bidak

                        pieces[selected_piece].remove(selected_position)

                        pieces[selected_piece].append((col, row))

                        # Promosi pion

                        if selected_piece.endswith("pawn") and (row == 0 or row == 7):

                            promote_pawn((col, row), turn, pieces)                                   


                        # Gerakan rokade

                        if selected_piece.endswith("king") and abs(selected_position[0] - col) == 2:

                            if col == 2:  # Rokade sisi menteri

                                rook_pos = (0, selected_position[1])

                                new_rook_pos = (3, selected_position[1])

                            else:  # Rokade sisi raja

                                rook_pos = (7, selected_position[1])

                                new_rook_pos = (5, selected_position[1])


                            pieces[f"{turn} rook"].remove(rook_pos)

                            pieces[f"{turn} rook"].append(new_rook_pos)


                        # Perbarui hak rokade

                        if selected_piece.endswith("king"):

                            castling_rights[turn]["king_side"] = False

                            castling_rights[turn]["queen_side"] = False

                        if selected_piece == f"{turn} rook":

                            if selected_position == (0, 7) or selected_position == (0, 0):  # Benteng sisi menteri

                                castling_rights[turn]["queen_side"] = False

                            if selected_position == (7, 7) or selected_position == (7, 0):  # Benteng sisi raja

                                castling_rights[turn]["king_side"] = False

                     

                            

                            # Setelah pemain selesai membuat langkah

                        if is_king_in_check(turn, pieces):

                        if is_checkmate(turn, pieces, turn):

                        print(f"Checkmate! {turn.capitalize()} loses!")

                        running = False  # Akhiri permainan

                        else:

                        print(f"{turn.capitalize()} is in check!")         

                                                      

                        # Simpan gerakan terakhir

                        last_move = (selected_position, (col, row), selected_piece)


                        # Ganti giliran

                        if turn == "white":

                        turn = "black"

                        else:

                        turn = "white"


                    selected_piece = None

                else:

                    for piece, positions in pieces.items():

                        if piece.startswith(turn) and (col, row) in positions:

                            selected_piece = piece

                            selected_position = (col, row)

                            break


        pygame.display.flip()

        clock.tick(FPS)


# Jalankan permainan

if __name__ == "__main__":

    main()

    

pygame.quit()

sys.exit()    


Semua kode game catur tersebut sangat panjang hanya dalam datu file,  kode ditampilkan sudah cukup komprehensif, tetapi memiliki beberapa kelemahan dan kekurangan yang dapat menyebabkan bug atau ketidaksesuaian aturan permainan catur. Berikut adalah evaluasi dari kode tersebut:


Kelebihan Kode

  1. Dasar Logika Catur:

    • Kode sudah mencoba menangani semua jenis bidak (pion, kuda, gajah, benteng, ratu, dan raja).
    • Fungsi seperti is_checkmate, promote_pawn, castling, dan en passant sudah dimasukkan.
    • Ada logika untuk menggambar papan, bidak, dan giliran pemain.
  2. Pendekatan Berbasis Posisi:

    • Anda menggunakan sistem koordinat (x, y) untuk menentukan posisi bidak, yang cukup standar dalam permainan catur.
  3. Implementasi Visual dengan Pygame:

    • Penggunaan Pygame memungkinkan papan dan bidak divisualisasikan dengan baik.

Masalah atau Kekurangan

  1. Logika Castling (Rokade):

    • Kode untuk castling tidak lengkap. Anda hanya memeriksa langkah raja secara horizontal, tetapi jalur untuk rokade juga harus dicek apakah bebas ancaman (tidak ada serangan dari bidak lawan).
    • Tidak ada penyimpanan atribut seperti has_moved untuk raja dan benteng, yang diperlukan untuk memastikan rokade sah.
  2. Logika En Passant:

    • Logika en passant Anda terlalu panjang dan tidak sepenuhnya benar. Variabel last_move harus diperbarui setiap kali pion bergerak dua langkah ke depan, tetapi ini tidak dilakukan.
  3. Validasi Gerakan Bidak:

    • Fungsi is_valid_move untuk bidak lain seperti gajah, benteng, ratu, dan kuda tidak lengkap. Contoh:
      • Gajah (bishop): Tidak ada logika untuk memastikan bahwa jalur diagonal gajah bebas dari bidak lain.
      • Benteng (rook): Tidak ada logika untuk memastikan jalur horizontal/vertikal bebas.
      • Ratu (queen): Kombinasi gerakan benteng dan gajah tidak diimplementasikan.
      • Kuda (knight): Gerakannya tidak dicek secara spesifik.
  4. Penanganan Skak dan Skakmat:

    • Fungsi is_king_in_check tidak memeriksa apakah langkah raja akan menghindari skak. Selain itu, is_checkmate tidak memeriksa semua kemungkinan langkah bidak lain untuk keluar dari skak.
  5. Promosi Pion:

    • Kode Anda hanya mendukung promosi otomatis menjadi ratu. Dalam catur, pemain bisa memilih untuk mempromosikan menjadi ratu, benteng, gajah, atau kuda.
  6. Error Variabel yang Tidak Dideklarasikan:

    • Variabel seperti selected_position, history, piece.has_moved, dan board digunakan tanpa dideklarasikan atau diinisialisasi.
  7. Efisiensi dan Struktur Kode:

    • Kode cukup panjang dan kompleks, tetapi tidak terorganisasi dengan baik. Contoh:
      • Semua fungsi digabung dalam satu file, sehingga sulit untuk membaca dan memelihara kode.
      • Tidak ada kelas untuk mengelompokkan logika terkait (misalnya, Piece, Board, Game).

Saran Perbaikan

  1. Struktur Ulang Kode:

    • Gunakan Object-Oriented Programming (OOP) untuk membuat kode lebih modular. Contoh:
      • Kelas Piece untuk menyimpan informasi tentang jenis bidak dan warnanya.
      • Kelas Board untuk menyimpan posisi bidak.
      • Kelas Game untuk menangani logika permainan, giliran, dan kondisi skak/skakmat.
  2. Implementasi Lengkap Gerakan Bidak:

    • Pastikan setiap bidak memiliki logika gerakan lengkap, termasuk:
      • Validasi jalur untuk gajah, benteng, dan ratu.
      • Validasi gerakan unik untuk kuda.
      • Validasi gerakan diagonal untuk pion saat menyerang.
  3. Perbaikan Logika Castling:

    • Tambahkan atribut seperti has_moved ke raja dan benteng.
    • Periksa jalur castling bebas dari bidak dan ancaman serangan lawan.
  4. Fungsi Skakmat yang Lengkap:

    • Periksa semua kemungkinan langkah bidak lain (selain raja) untuk keluar dari skak sebelum menyimpulkan skakmat.
  5. Promosi Pion:

    • Berikan opsi kepada pemain untuk memilih jenis bidak yang diinginkan saat pion mencapai baris terakhir.
  6. Efisiensi dan Pengorganisasian:

    • Pisahkan fungsi-fungsi utama ke dalam beberapa file (misalnya, file untuk logika permainan, file untuk UI, dll.).

Contoh Struktur Ulang

Berikut contoh sederhana menggunakan OOP:

class Piece:
    def __init__(self, color, piece_type):
        self.color = color
        self.piece_type = piece_type
        self.has_moved = False

class Board:
    def __init__(self):
        self.grid = [[None for _ in range(8)] for _ in range(8)]
        self.initialize_pieces()

    def initialize_pieces(self):
        # Isi bidak awal di sini

    def is_valid_move(self, piece, start, end):
        # Tambahkan logika validasi gerakan bidak

class Game:
    def __init__(self):
        self.board = Board()
        self.turn = "white"
        self.history = []

    def change_turn(self):
        self.turn = "white" if self.turn == "black" else "black"

    def is_checkmate(self):
        # Tambahkan logika skakmat


Oleh karena itu maka kode ditata ulang kembali untuk mendapatkan kode yang lengkap game catur berbasis OOP (Object-Oriented Programming) yang menangani logika terkait: piece, board, game, logika untuk menggambar papan, bidak, dan giliran pemain, lengkap dengan semua gerakan bidak (gerakan pion, kuda, gajah, benteng, ratu, raja, skak dan skakmat raja), termasuk validasi jalur untuk gajah, benteng, dan ratu, validasi gerakan diagonal pion saat menyerang, en-passant, castling (rokade) dengan menambahkan has_moved ke raja dan benteng, dan jalur castling bebas dari bidak dan ancaman serangan lawan, fungsi skak dan skamat yang lengkap, promosi pion dengan opsi pilihan bidak yang diinginkan pemain: ratu, benteng, gajah, atau kuda. Game catur berbasis OOP ini terdiri dari 4 (empat) file yaitu: piece.py, board.py, game.py, dan main.py


Bersambung ke:

PERBAIKAN DAN OPTIMALISASI KODE PROGRAM PADA GAME CATUR Part-3)




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)