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:
-
Castling (Rokade):
- Mengecek apakah raja dan benteng belum pernah bergerak.
- Memastikan jalur antara raja dan benteng bebas dari bidak dan ancaman.
-
Promosi Pion:
- Memastikan pion yang mencapai baris terakhir dapat dipromosikan.
-
En Passant:
- Mengecek apakah langkah terakhir lawan sesuai kondisi en passant (pion bergerak 2 kotak dan berdampingan dengan pion kita).
-
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
danmake_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
-
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.
-
Pendekatan Berbasis Posisi:
- Anda menggunakan sistem koordinat
(x, y)
untuk menentukan posisi bidak, yang cukup standar dalam permainan catur.
- Anda menggunakan sistem koordinat
-
Implementasi Visual dengan Pygame:
- Penggunaan Pygame memungkinkan papan dan bidak divisualisasikan dengan baik.
Masalah atau Kekurangan
-
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.
-
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.
- Logika en passant Anda terlalu panjang dan tidak sepenuhnya benar. Variabel
-
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.
- Fungsi
-
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.
- Fungsi
-
Promosi Pion:
- Kode Anda hanya mendukung promosi otomatis menjadi ratu. Dalam catur, pemain bisa memilih untuk mempromosikan menjadi ratu, benteng, gajah, atau kuda.
-
Error Variabel yang Tidak Dideklarasikan:
- Variabel seperti
selected_position
,history
,piece.has_moved
, danboard
digunakan tanpa dideklarasikan atau diinisialisasi.
- Variabel seperti
-
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
).
- Kode cukup panjang dan kompleks, tetapi tidak terorganisasi dengan baik. Contoh:
Saran Perbaikan
-
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.
- Kelas
- Gunakan Object-Oriented Programming (OOP) untuk membuat kode lebih modular. Contoh:
-
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.
- Pastikan setiap bidak memiliki logika gerakan lengkap, termasuk:
-
Perbaikan Logika Castling:
- Tambahkan atribut seperti
has_moved
ke raja dan benteng. - Periksa jalur castling bebas dari bidak dan ancaman serangan lawan.
- Tambahkan atribut seperti
-
Fungsi Skakmat yang Lengkap:
- Periksa semua kemungkinan langkah bidak lain (selain raja) untuk keluar dari skak sebelum menyimpulkan skakmat.
-
Promosi Pion:
- Berikan opsi kepada pemain untuk memilih jenis bidak yang diinginkan saat pion mencapai baris terakhir.
-
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
Post a Comment