PERBAIKAN DAN OPTIMALISASI KODE PROGRAM PADA GAME CATUR (Part-1)
PERBAIKAN DAN OPTIMALISASI KODE PROGRAM PADA GAME CATUR
(Part-1)
Kode-kode program game Catur yang belum sempurna (belum sesuai dengan aturan permainan Catur) dapat kita optimalkan agar lebih sempurna. Misalnya awal mulanya kode pemograman game Catur adalah seperti berikut ini:
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)],
},
}
# Fungsi menggambar papan
def draw_board():
for row in range(8):
for col in range(8):
color = LIGHT_BROWN if (row + col) % 2 == 0 else DARK_BROWN
pygame.draw.rect(screen, color, (col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE))
# 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 validasi gerakan bidak
def is_valid_move(piece, start, end, pieces, turn):
# Mendapatkan informasi warna dan jenis bidak
color, p_type = piece.split(' ')
# Mengambil posisi awal dan akhir
x1, y1 = start
x2, y2 = end
# Memastikan giliran yang benar
if color != turn:
return False
# Aturan untuk Raja
if p_type == "king":
return abs(x1 - x2) <= 1 and abs(y1 - y2) <= 1
# Aturan untuk Ratu
elif p_type == "queen":
return is_valid_rook_move(start, end, pieces) or is_valid_bishop_move(start, end, pieces)
# Aturan untuk Benteng (Rook)
elif p_type == "rook":
return is_valid_rook_move(start, end, pieces)
# Aturan untuk Gajah (Bishop)
elif p_type == "bishop":
return is_valid_bishop_move(start, end, pieces)
# Aturan untuk Kuda (Knight)
elif p_type == "knight":
return (abs(x1 - x2) == 2 and abs(y1 - y2) == 1) or (abs(x1 - x2) == 1 and abs(y1 - y2) == 2)
# Aturan untuk Pion (Pawn)
elif p_type == "pawn":
direction = 1 if color == "white" else -1
if x1 == x2:
if y2 - y1 == direction and not is_occupied(end, pieces):
return True
# Pion pertama kali bisa melangkah dua kotak
elif y1 == (1 if color == "white" else 6) and y2 - y1 == 2 * direction and not is_occupied(end, pieces):
return True
elif abs(x1 - x2) == 1 and y2 - y1 == direction and is_occupied(end, pieces):
return True
return False
# Fungsi untuk validasi gerakan benteng
def is_valid_rook_move(start, end, pieces):
x1, y1 = start
x2, y2 = end
if x1 != x2 and y1 != y2:
return False
# Memeriksa apakah jalur dilalui oleh bidak lain
return is_path_clear(start, end, pieces)
# Fungsi untuk validasi gerakan gajah
def is_valid_bishop_move(start, end, pieces):
x1, y1 = start
x2, y2 = end
if abs(x1 - x2) != abs(y1 - y2):
return False
# Memeriksa apakah jalur dilalui oleh bidak lain
return is_path_clear(start, end, pieces)
# Fungsi untuk memeriksa apakah jalur bebas
def is_path_clear(start, end, pieces):
x1, y1 = start
x2, y2 = end
dx = 1 if x2 > x1 else -1
dy = 1 if y2 > y1 else -1
x, y = x1 + dx, y1 + dy
while (x, y) != (x2, y2):
if is_occupied((x, y), pieces):
return False
x += dx
y += dy
return True
# 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 mempromosikan pion
def promote_pawn(pawn_position, color, pieces):
# Pemain memilih promosi pion (contoh, pilih ratu)
pieces[f"{color} queen"].append(pawn_position)
pieces[f"{color} pawn"].remove(pawn_position)
# Fungsi utama game
def main():
clock = pygame.time.Clock()
pieces = initialize_pieces()
selected_piece = None
selected_position = None
turn = "white" # Giliran awal pemain putih
running = True
while running:
screen.fill(GRAY)
draw_board()
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:
# Pindahkan bidak ke lokasi baru jika valid
if is_valid_move(selected_piece, selected_position, (col, row), pieces, turn):
pieces[selected_piece].remove(selected_position)
pieces[selected_piece].append((col, row))
# Periksa apakah pion mencapai baris terakhir untuk promosi
if selected_piece.endswith("pawn") and (row == 0 or row == 7):
promote_pawn((col, row), turn, pieces)
selected_piece = None
turn = "black" if turn == "white" else "white" # Ganti giliran
else:
selected_piece = None
else:
# Pilih bidak jika pada giliran yang sesuai
for piece, positions in pieces.items():
if piece.startswith(turn) and (col, row) in positions:
selected_piece = piece
selected_position = (col, row)
break
# Gambar bidak yang dipilih
if selected_piece:
pygame.draw.rect(
screen,
(0, 255, 0),
(selected_position[0] * TILE_SIZE, selected_position[1] * TILE_SIZE, TILE_SIZE, TILE_SIZE),
3,
)
pygame.display.flip()
clock.tick(FPS)
# Jalankan permainan
if __name__ == "__main__":
main()
Pastikan file gambar benar-benar ada di lokasi: /storage/emulated/0/AppJadi/Catur/images/
dan nama file sesuai dengan format {color} {piece}.png (contoh: white king.png).
Jika kode tersebut dieksekusi, maka papan catur sudah tergambar, dan semua buah catur putih dan hitam sudah tampil di petak masing-masing, tapi buah catur (pion, gajah, benteng, menteri, dan raja) belum bisa digerakkan. Kode program kita optimalkan untuk menyempurnakan setiap langkah buah catur sesuai dengan aturan permainan Catur yang berlaku, kode akan menjadi seperti berikut:
1. Menggerakkan Pion (pawn):
Masalah pion (pawn) yang belum bisa digerakkan kemungkinan besar berasal dari fungsi validasi gerakan. Mari kita telaah bagian kode yang bertanggung jawab untuk memvalidasi gerakan pion. Berikut adalah bagian kode yang relevan:
Potensi Masalah
1. Logika gerakan pion pada fungsi is_valid_move mungkin tidak sepenuhnya benar.
2. Validasi apakah kotak tujuan kosong atau ada bidak lawan di kotak tujuan bisa menyebabkan pion tidak dapat bergerak.
Fungsi is_valid_move (Bagian Pion)
Kode program yang salah:
elif p_type == "pawn":
direction = 1 if color == "white" else -1
if x1 == x2:
if y2 - y1 == direction and not is_occupied(end, pieces):
return True
# Pion pertama kali bisa melangkah dua kotak
elif y1 == (1 if color == "white" else 6) and y2 - y1 == 2 * direction and not is_occupied(end, pieces):
return True
elif abs(x1 - x2) == 1 and y2 - y1 == direction and is_occupied(end, pieces):
return True
return False
Perbaikan
Berikut perbaikannya dengan tambahan validasi untuk memastikan pion dapat bergerak maju atau menyerang bidak lawan secara diagonal:
elif p_type == "pawn":
direction = -1 if color == "white" else 1 # Arah gerakan pion
# Gerakan maju satu langkah
if x1 == x2:
if y2 - y1 == direction and not is_occupied(end, pieces):
return True
# Gerakan maju dua langkah hanya jika di posisi awal
if (y1 == 6 if color == "white" else y1 == 1) and y2 - y1 == 2 * direction:
mid_position = (x1, y1 + direction) # Kotak di antara langkah pion
if not is_occupied(end, pieces) and not is_occupied(mid_position, pieces):
return True
# Serangan diagonal
if abs(x1 - x2) == 1 and y2 - y1 == direction and is_occupied_by_enemy(end, color, pieces):
return True
return False
Fungsi Tambahan
Tambahkan fungsi is_occupied_by_enemy untuk memeriksa apakah kotak tujuan diisi 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
Penjelasan Perbaikan
1. Gerakan maju satu atau dua langkah:
- Pion bisa maju satu langkah jika kotak di depannya kosong.
- Pion bisa maju dua langkah jika di posisi awal dan kedua kotak di depannya kosong.
2. Serangan diagonal:
Pion dapat menyerang bidak lawan secara diagonal jika ada bidak lawan di posisi tersebut.
3. Pemeriksaan kotak tengah:
Saat pion bergerak dua langkah, harus memeriksa kotak di antaranya agar tidak ada bidak lain.
4. Gerakan en passant
En passant adalah gerakan khusus pion menangkap (memakan) pion lawan yang hanya dapat terjadi segera setelah pion bergerak dua kotak dari kotak awalnya, dan pion tersebut dapat ditangkap oleh pion musuh jika seandainya pion tersebut hanya maju satu petak. Lawan menangkap pion yang baru saja dipindahkan "saat ia melewati" petak pertama.
En passant hanya bisa dilakukan jika pion lawan baru saja melangkah dua petak ke depan. Posisi atarget en passant harus sesuai dengan aturan (diagonal satu langkah dari pion yang melangkah dua petak).
-. Variabel last_move:
Menyimpan informasi tentang gerakan terakhir, termasuk posisi awal, posisi akhir, dan jenis bidak yang bergerak.
-. Logika En Passant di is_valid_move:
Memeriksa apakah bidak lawan adalah pion, dan apakah posisinya sesuai untuk gerakan en passant.
-. Penghapusan Bidak Lawan Selama En Passant:
Jika gerakan en passant valid, pion lawan dihapus dari posisi yang relevan.
-. Pembaruan Gerakan Terakhir:
Setelah setiap gerakan, informasi gerakan terakhir diperbarui.
Dengan modifikasi ini, pion dapat melakukan gerakan en passant sesuai aturan catur.
5. Promosi Pion
Promosi pion adalah gerakan pion yang mencapai petak baris terakhir, lalu pion tersebut berubah menjadi ratu (menteri).
Penyesuaian yang dilakukan:
1. Fungsi is_valid_move sudah mendukung en passant.
2. Integrasi promosi pion dan giliran pemain.
3. last_move melacak gerakan terakhir untuk validasi en passant.
Uji Coba
Setelah memperbarui kode, uji gerakan pion untuk memastikan:
- Pion dapat maju satu langkah saat kotak di depannya kosong.
- Pion dapat maju dua langkah dari posisi awal.
- Pion dapat menyerang bidak lawan secara diagonal.
- Pion dapat melakukan gerakan en passant
- Pion dapat melakukan Promosi
- Penyelesaian Masalah Giliran:
Pastikan hanya bidak dengan warna sesuai giliran yang dapat dipilih.
- . Hapus Bidak Lawan Saat Diserang:
Tambahkan penghapusan bidak lawan saat pion menyerang secara diagonal.
Berikut adalah versi yang diperbaiki dari kode Anda agar gerakan pion mengikuti aturan catur, termasuk gerakan maju satu langkah dan dua langkah, serangan diagonal, gerakan en passant, dan promosi pion.
Perbaikan Kode untuk Gerakan Pion (pawn):
Kode Selengkapnya (gerakan pion), menjadi seperti berikut ini:
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)],
},
}
# Fungsi menggambar papan
def draw_board():
for row in range(8):
for col in range(8):
color = LIGHT_BROWN if (row + col) % 2 == 0 else DARK_BROWN
pygame.draw.rect(screen, color, (col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE))
# 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
# Fungsi untuk validasi gerakan bidak
def is_valid_move(piece, start, end, pieces, turn, last_move):
color, p_type = piece.split(' ')
x1, y1 = start
x2, y2 = end
if color != turn: # Hanya bidak pada giliran yang sesuai
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
return False
# 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 utama game
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"
running = True
while running:
screen.fill(GRAY)
draw_board()
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):
# 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
# 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)
# Simpan gerakan terakhir
last_move = (selected_position, (col, row), selected_piece)
# Ganti giliran
turn = "black" if turn == "white" else "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()
Penjelasan Perbaikan
1. Gerakan En Passant:
- Ditambahkan validasi untuk memeriksa apakah pion lawan memenuhi kondisi en passant.
- Menghapus pion lawan yang terkena en passant dengan memeriksa last_move.
2. Promosi Pion:
Fungsi promote_pawn tetap dipanggil saat pion mencapai baris terakhir.
3. Variabel Global last_move:
Menyimpan informasi tentang gerakan terakhir, yang diperlukan untuk memvalidasi en passant.
4. Validasi Gerakan Pion:
Memastikan pion hanya dapat bergerak sesuai aturan (maju satu/dua langkah, serangan diagonal, en passant, promosi pion).
Cara Menggunakan
- Tempatkan gambar bidak di folder '/storage/emulated/0/AppJadi/Catur/images/' sesuai nama file (misalnya, white pawn.png).
- Jalankan program, dan pastikan aturan pion sesuai (termasuk promosi dan en passant).
Untuk menambahkan fungsi yang memungkinkan bidak Kuda (Knight) bergerak sesuai dengan aturan permainan Catur, kita perlu memperluas fungsi is_valid_move agar mendukung logika gerakan Kuda. Berikut adalah modifikasi yang diperlukan:
Tambahkan Logika Gerakan Kuda
Gerakan Kuda adalah lompatan berbentuk "L", yaitu dua langkah dalam satu arah (horizontal atau vertikal) dan satu langkah di arah lainnya, atau sebaliknya.
Kode yang Dimodifikasi:
def is_valid_move(piece, start, end, pieces, turn, last_move):
color, p_type = piece.split(' ')
x1, y1 = start
x2, y2 = end
if color != turn: # Hanya bidak pada giliran yang sesuai
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
return False
Penjelasan Tambahan
1. Logika Gerakan Kuda:
- Dihitung perbedaan langkah horizontal (dx) dan vertikal (dy) antara posisi awal dan tujuan.
- Valid jika gerakannya berbentuk "L" yaitu dx == 2 dan dy == 1 atau dx == 1 dan dy == 2.
- Kotak tujuan harus kosong atau diisi oleh bidak lawan.
2. Kondisi Akhir:
Jika kotak tujuan memenuhi kriteria, gerakan valid dan fungsi is_valid_move mengembalikan True.
Dengan kode ini, Kuda dapat bergerak sesuai aturan permainan catur.
Kode lengkap:
Kode Selengkapnya (getakan pion, dan kuda), menjadi seperti berikut ini:
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)],
},
}
# Fungsi menggambar papan
def draw_board():
for row in range(8):
for col in range(8):
color = LIGHT_BROWN if (row + col) % 2 == 0 else DARK_BROWN
pygame.draw.rect(screen, color, (col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE))
# 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
# Fungsi untuk validasi gerakan bidak
def is_valid_move(piece, start, end, pieces, turn, last_move):
color, p_type = piece.split(' ')
x1, y1 = start
x2, y2 = end
if color != turn: # Hanya bidak pada giliran yang sesuai
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
return False
# 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 utama game
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"
running = True
while running:
screen.fill(GRAY)
draw_board()
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):
# 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
# 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)
# Simpan gerakan terakhir
last_move = (selected_position, (col, row), selected_piece)
# Ganti giliran
turn = "black" if turn == "white" else "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()
3. Menggerakkan Gajah (bishop):
Untuk menambahkan fungsi yang memungkinkan bidak Gajah (bishop) bergerak sesuai dengan aturan permainan catur, Anda perlu memodifikasi fungsi is_valid_move agar mendukung validasi gerakan Gajah. Berikut ini adalah penyesuaian pada bagian fungsi tersebut:
1. Modifikasi is_valid_move
Tambahkan logika untuk memvalidasi gerakan Gajah.
# 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
2. Hasil Lengkap is_valid_move Setelah Ditambahkan Gerakan Gajah
Berikut ini adalah tampilan lengkap fungsi is_valid_move setelah ditambahkan dukungan untuk bidak Gajah:
def is_valid_move(piece, start, end, pieces, turn, last_move):
color, p_type = piece.split(' ')
x1, y1 = start
x2, y2 = end
if color != turn: # Hanya bidak pada giliran yang sesuai
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
return False
3. Fungsi Lain Tetap Sama
Tidak perlu mengubah fungsi lain. Fungsi seperti initialize_pieces, draw_pieces, atau promote_pawn sudah mendukung Gajah, karena bidak Gajah sudah termasuk dalam STARTING_POSITIONS.
4. Uji Program
Jalankan program, dan pastikan bidak Gajah dapat bergerak secara diagonal dengan benar, baik untuk langkah biasa maupun menyerang bidak lawan.
Kode lengkap:
Kode Selengkapnya (gerakan pion, kuda, dan gajah), menjadi seperti berikut ini:
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)],
},
}
# Fungsi menggambar papan
def draw_board():
for row in range(8):
for col in range(8):
color = LIGHT_BROWN if (row + col) % 2 == 0 else DARK_BROWN
pygame.draw.rect(screen, color, (col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE))
# 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
# Fungsi untuk validasi gerakan bidak
def is_valid_move(piece, start, end, pieces, turn, last_move):
color, p_type = piece.split(' ')
x1, y1 = start
x2, y2 = end
if color != turn: # Hanya bidak pada giliran yang sesuai
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
return False
# 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 utama game
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"
running = True
while running:
screen.fill(GRAY)
draw_board()
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):
# 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
# 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)
# Simpan gerakan terakhir
last_move = (selected_position, (col, row), selected_piece)
# Ganti giliran
turn = "black" if turn == "white" else "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()
4. Menggerakkan Benteng (rook):
Untuk menambahkan fungsi menggerakkan Benteng (rook) sesuai aturan permainan catur, tambahkan logika validasi gerakan Benteng di dalam fungsi is_valid_move(). Berikut adalah modifikasi kode untuk menyertakan logika gerakan Benteng:
Modifikasi Fungsi is_valid_move()
Tambahkan bagian ini di dalam fungsi is_valid_move():
# 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
Penjelasan Logika
1. Gerakan Horizontal atau Vertikal: Benteng hanya dapat bergerak secara horizontal atau vertikal, yaitu jika posisi x1 == x2 (vertikal) atau y1 == y2 (horizontal).
2. Cek Jalur Halangan: Iterasi dilakukan untuk memeriksa setiap kotak di jalur gerakan. Jika ada bidak di jalur tersebut, gerakan tidak sah.
3. Kotak Tujuan: Kotak tujuan dapat kosong atau diisi bidak lawan.
Kode Lengkap Setelah Penambahan Benteng
Berikut adalah fungsi is_valid_move() yang diperbarui dengan logika gerakan Benteng:
def is_valid_move(piece, start, end, pieces, turn, last_move):
color, p_type = piece.split(' ')
x1, y1 = start
x2, y2 = end
if color != turn: # Hanya bidak pada giliran yang sesuai
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
return False
Dengan logika ini, Benteng dapat bergerak sesuai aturan catur, yaitu secara horizontal dan vertikal tanpa melewati bidak lain.
Kode lengkap:
Kode Selengkapnya (gerakan pion, kuda, gajah, dan benteng), menjadi seperti berikut ini:
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)],
},
}
# Fungsi menggambar papan
def draw_board():
for row in range(8):
for col in range(8):
color = LIGHT_BROWN if (row + col) % 2 == 0 else DARK_BROWN
pygame.draw.rect(screen, color, (col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE))
# 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
# Fungsi untuk validasi gerakan bidak
def is_valid_move(piece, start, end, pieces, turn, last_move):
color, p_type = piece.split(' ')
x1, y1 = start
x2, y2 = end
if color != turn: # Hanya bidak pada giliran yang sesuai
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
return False
# 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 utama game
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"
running = True
while running:
screen.fill(GRAY)
draw_board()
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):
# 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
# 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)
# Simpan gerakan terakhir
last_move = (selected_position, (col, row), selected_piece)
# Ganti giliran
turn = "black" if turn == "white" else "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()
5. Menggerakkan Ratu atau Menteri (queen):
Berikut adalah modifikasi kode program untuk menambahkan logika gerakan Ratu/Menteri (Queen) sesuai dengan aturan catur. Queen dapat bergerak secara horizontal, vertikal (seperti Benteng/Rook) atau diagonal (seperti Gajah/Bishop).
Silakan perhatikan tambahan kode pada fungsi is_valid_move():
# Fungsi untuk validasi gerakan bidak
def is_valid_move(piece, start, end, pieces, turn, last_move):
color, p_type = piece.split(' ')
x1, y1 = start
x2, y2 = end
if color != turn: # Hanya bidak pada giliran yang sesuai
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
return False
Penjelasan Tambahan:
1. Logika Gerakan Queen:
- Ratu dapat bergerak diagonal (mirip Gajah).
- Ratu dapat bergerak horizontal atau vertikal (mirip Benteng).
- Kombinasi logika Gajah dan Benteng digabungkan untuk memvalidasi gerakan.
2. Posisi Awal: Ratu berada di (3, 7) untuk warna putih dan (3, 0) untuk warna hitam, sesuai dengan konvensi.
3. Penerapan: Setelah menambahkan logika di atas, Queen kini akan mengikuti aturan gerakan pada permainan catur.
Kode lengkap:
Kode Selengkapnya (gerakan pion, kuda, gajah, benteng, dan ratu/menteri), menjadi seperti berikut ini:
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)],
},
}
# Fungsi menggambar papan
def draw_board():
for row in range(8):
for col in range(8):
color = LIGHT_BROWN if (row + col) % 2 == 0 else DARK_BROWN
pygame.draw.rect(screen, color, (col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE))
# 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
# Fungsi untuk validasi gerakan bidak
def is_valid_move(piece, start, end, pieces, turn, last_move):
color, p_type = piece.split(' ')
x1, y1 = start
x2, y2 = end
if color != turn: # Hanya bidak pada giliran yang sesuai
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
return False
# 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 utama game
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"
running = True
while running:
screen.fill(GRAY)
draw_board()
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):
# 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
# 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)
# Simpan gerakan terakhir
last_move = (selected_position, (col, row), selected_piece)
# Ganti giliran
turn = "black" if turn == "white" else "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()
6. Menggerakkan Raja (king), termasuk gerakan Rokade
Untuk menambahkan logika gerakan Raja (King), termasuk gerakan rokade (castling), berikut adalah modifikasi kode program. Saya akan menambahkan bagian logika Raja dalam fungsi is_valid_move dan menyesuaikan logika permainan utama agar mendukung rokade.
Berikut kode lengkap dengan tambahan logika Raja dan rokade:
# Fungsi untuk validasi gerakan bidak (dengan logika Raja dan rokade ditambahkan)
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
# 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
# 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
# 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()
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
# 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)
# Pindahkan bidak
pieces[selected_piece].remove(selected_position)
pieces[selected_piece].append((col, row))
# 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
# Simpan gerakan terakhir
last_move = (selected_position, (col, row), selected_piece)
# Ganti giliran
turn = "black" if turn == "white" else "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()
Penjelasan
1. Gerakan Raja:
- Raja dapat bergerak satu langkah ke segala arah.
- Gerakan ini diperiksa dalam bagian logika is_valid_move.
2. Rokade:
- Gerakan rokade dilakukan dengan memindahkan Raja dua langkah ke kiri atau kanan, dan Benteng melompati Raja.
- Rokade hanya bisa dilakukan jika:
- Raja dan Benteng belum pernah bergerak.
- Jalur antara Raja dan Benteng kosong.
- Raja tidak berada dalam keadaan skak.
3. Pembaruan Hak Rokade:
Jika Raja atau Benteng bergerak, hak rokade untuk sisi tersebut dihapus.
Kode di atas memungkinkan Anda menggerakkan Raja dan melakukan rokade sesuai dengan aturan catur. Jika ada yang perlu diperjelas, beri tahu saya!
Kode lengkap:
Kode Selengkapnya (gerakan pion, kuda, gajah, benteng, ratu/menterI, dan raja), menjadi seperti berikut ini:
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)],
},
}
# Fungsi menggambar papan
def draw_board():
for row in range(8):
for col in range(8):
color = LIGHT_BROWN if (row + col) % 2 == 0 else DARK_BROWN
pygame.draw.rect(screen, color, (col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE))
# 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
# 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
# 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
# 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 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 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()
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
# 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
# Simpan gerakan terakhir
last_move = (selected_position, (col, row), selected_piece)
# Ganti giliran
turn = "black" if turn == "white" else "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()
6. Keadaan "πkakMat" pada Raja (king)
Raja (king) dalam keadaan "skakmat" adalah posisi RaJa dalam terkena setangan ""skak" oleh buah catur lawan, dan Raja tidak dapat bergerak lagi (Mat).
Untuk Itu perlu kita tambahkan fitur "checkmate" pada kode program game catur tersebut, sebagai berikut:
Untuk menambahkan fitur checkmate (skak mat) pada game catur, kita perlu mengevaluasi apakah:
1. Raja dalam keadaan "check" (skak), yaitu berada dalam posisi terancam.
2. Tidak ada langkah raja yang memungkinkan untuk keluar dari ancaman.
Berikut adalah langkah-langkah untuk menambahkan fitur checkmate:
Langkah 1: Tambahkan Fungsi untuk Memeriksa Check
Buat fungsi untuk memeriksa apakah Raja sedang dalam posisi terancam. Fungsi ini mengevaluasi semua kemungkinan langkah dari bidak lawan untuk melihat apakah mereka menyerang posisi Raja.
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":
king_position = positions[0]
break
if not king_position:
return False # Raja tidak ditemukan
# 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 Fal
Langkah 2: Tambahkan Fungsi untuk Mengecek Checkmate
Fungsi ini mengevaluasi apakah ada langkah valid untuk keluar dari skak.
def is_checkmate(color, pieces, turn, last_move, castling_rights):
if not is_king_in_check(color, pieces): # Jika Raja tidak dalam skak, bukan skak mat
return False
# Cek semua langkah bidak pemain untuk keluar dari skak
for piece, positions in pieces.items():
piece_color, _ = piece.split(' ')
if piece_color == color: # Hanya periksa bidak pemain
for start in positions:
# Coba semua posisi tujuan pada papan
for x in range(8):
for y in range(8):
end = (x, y)
# Jika langkah valid, lakukan simulasi
if is_valid_move(piece, start, end, pieces, turn, last_move, castling_rights):
# Simulasi langkah
temp_pieces = {k: v[:] for k, v in pieces.items()}
temp_pieces[piece].remove(start)
temp_pieces[piece].append(end)
# Periksa apakah langkah menghilangkan skak
if not is_king_in_check(color, temp_pieces):
return False # Ada langkah untuk keluar dari skak
return True # Tidak ada langkah valid, skak ma
Langkah 3: Periksa Checkmate di Main Loop
Setelah pemain membuat langkah, tambahkan pemeriksaan untuk checkmate di main loop game.
Tambahkan kode berikut di bagian game loop:
# Setelah langkah pemain selesai
if is_king_in_check(turn, pieces):
if is_checkmate(turn, pieces, turn, last_move, castling_rights):
print(f"Checkmate! {turn.capitalize()} loses!")
pygame.quit()
sys.exit()
else:
print(f"{turn.capitalize()} is in check!"
Langkah 4: Tambahkan Indikasi Visual
Kita dapat menambahkan visualisasi skak atau skak mat di papan catur, misalnya dengan menyoroti kotak Raja dengan warna merah saat skak. Tambahkan ini di fungsi draw_board:
def draw_board(pieces):
for row in range(8):
for col in range(8):
color = LIGHT_BROWN if (row + col) % 2 == 0 else DARK_BROWN
pygame.draw.rect(screen, color, (col * TILE_SIZE, row * TILE_SIZE, TILE_SIZE, TILE_SIZE))
# Highlight kotak Raja jika dalam skak
for piece, positions in pieces.items():
if piece.endswith("king"):
color, _ = piece.split(' ')
if is_king_in_check(color, pieces):
x, y = positions[0]
pygame.draw.rect(screen, (255, 0, 0), (x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZ
Hasil Akhir
1. Jika Raja dalam posisi terancam, pesan "Check!" akan muncul.
2. Jika tidak ada langkah valid untuk menghilangkan ancaman, permainan akan berakhir dengan pesan "Checkmate!".
3. Kotak Raja yang dalam skak akan disorot dengan warna merah untuk memberikan indikasi visual.
Cobalah tambahkan fungsi tersebut pada kode program, lalu jalankan program untuk melihat hasilnya.
Kode lengkap:
Kode Selengkapnya (gerakan pion, kuda, gajah, benteng, ratu/menterI, raja, dan Skakmat raja), menjadi seperti berikut ini:
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)],
},
}
turn = "white" # Awal permainan dimulai dengan giliran putih
castling_rights = {
"white": {"king_side": True, "queen_side": True},
"black": {"king_side": True, "queen_side": True}
}
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):
return False # Jika ada langkah sah, bukan skakmat
return True # Jika tidak ada langkah sah, maka skakmat
# 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
# 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
# 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 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 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()
Bersambung ke:
PERBAIKAN DAN OPTIMALISASI KODE PROGRAM PADA GAME CATUR (Part-2)
Comments
Post a Comment