699 lines
22 KiB
GDScript
699 lines
22 KiB
GDScript
class_name ChessGame extends Control
|
|
|
|
const PieceContainer = preload("res://Systems/PieceContainer.gd")
|
|
const WHITE = "white"
|
|
const BLACK = "black"
|
|
signal tile_pressed(location: String)
|
|
signal send_location(location: String)
|
|
signal turn_changed
|
|
var currentPlayer: String = WHITE
|
|
var board: Array
|
|
var isWhiteToMove: bool = true
|
|
var castlingRights: String = "KQkq"
|
|
var enPassantTarget: String = "-"
|
|
var halfMoveClock: int = 0
|
|
var moveCount: int = 1
|
|
var currentHand: Array
|
|
var selectedNode: String = ""
|
|
var locationX: String = ""
|
|
var locationY: String = ""
|
|
var areas: PackedStringArray
|
|
var specialArea: PackedStringArray
|
|
var gamecheckMate: bool = false
|
|
var gamedraw: bool = false
|
|
var hasMoved: bool = false
|
|
var currentlyMovingPiece = null
|
|
var p1Points: int = 0
|
|
var p2Points: int = 0
|
|
var Turn: int = 0
|
|
const StockfishController = preload("res://Systems/FairyStockfish/StockfishClient.gd")
|
|
var stockfishController: StockfishController
|
|
var stockfishPath = "res://Assets/ChessEngines/stockfish/stockfish.exe"
|
|
@onready var turnIndicator: ColorRect = $TurnIndicator
|
|
@onready var p1String: RichTextLabel = $Player1Points
|
|
@onready var p2String: RichTextLabel = $Player2Points
|
|
@onready var deckManager: DeckManager
|
|
@onready var tileManager: TileManager
|
|
@onready var cardDisplay: CardDisplay
|
|
@onready var cardPreview: CardPreview
|
|
|
|
@export var boardXSize = 8
|
|
@export var boardYSize = 8
|
|
@export var tileXSize: int = 50
|
|
@export var tileYSize: int = 50
|
|
@export var windowXSize: int = 1280
|
|
@export var windowYSize: int = 720
|
|
@export var FEN: String = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
|
|
|
|
@onready var boardContainer: FlowContainer = $Flow
|
|
@onready var stateMachine: StateMachine = $StateMachine
|
|
|
|
var currentFen = ""
|
|
var lightStyle = null
|
|
|
|
var darkStyle = null
|
|
var highlightStyle = null
|
|
var cpuElo = 1500
|
|
|
|
func _ready() -> void:
|
|
print("Initi Local Stockfish")
|
|
if OS.get_name() == "Windows":
|
|
stockfishPath = "res://Assets/ChessEngines/stockfish/stockfish.exe"
|
|
else:
|
|
stockfishPath = ProjectSettings.globalize_path("res://Assets/ChessEngines/Fairy-Stockfish/src/stockfish")
|
|
add_to_group("ChessGame")
|
|
currentFen = FEN
|
|
DisplayServer.window_set_size(Vector2i(windowXSize, windowYSize))
|
|
initializeGame()
|
|
initializeTiles()
|
|
stateMachine.transitionToNextState(Constants.WHITE_TURN)
|
|
stockfishController = StockfishController.new()
|
|
add_child(stockfishController)
|
|
stockfishController.connect_to_engine(stockfishPath, self)
|
|
# if stockfishController.connect_to_engine(stockfishPath):
|
|
# stockfishController.limit_strength_to(cpuElo)
|
|
|
|
|
|
func _exit_tree():
|
|
stockfishController.disconnect_engine()
|
|
|
|
func initializeTiles() -> void:
|
|
tileManager = TileManager.new($Flow, self)
|
|
add_child(tileManager)
|
|
await get_tree().process_frame
|
|
tileManager.initialize(boardContainer)
|
|
tileManager.place_random_game_tiles()
|
|
|
|
func get_base_style(is_white: bool) -> StyleBoxFlat:
|
|
return lightStyle if is_white else darkStyle
|
|
func _unhandled_input(event: InputEvent) -> void:
|
|
stateMachine.unhandledInput(event)
|
|
|
|
func _process(delta: float) -> void:
|
|
stateMachine.process(delta)
|
|
|
|
func initializeGame() -> void:
|
|
setupStyles()
|
|
setupUI()
|
|
initializeDeckSystem()
|
|
initializeCardPreview()
|
|
initializeBoard()
|
|
|
|
func initializeCardPreview() -> void:
|
|
cardPreview = CardPreview.new()
|
|
add_child(cardPreview)
|
|
|
|
|
|
func getSpecialTilesFen() -> String:
|
|
if tileManager.active_tiles.is_empty():
|
|
return ""
|
|
|
|
var special_fen = " moves"
|
|
var portal_pairs = {} # Dictionary to track portal pairs by pair_id
|
|
|
|
# First pass: Collect portal pairs
|
|
for pos in tileManager.active_tiles:
|
|
var tile = tileManager.active_tiles[pos]
|
|
if tile is PortalTile:
|
|
var pair_id = tile.pair_id
|
|
if !portal_pairs.has(pair_id):
|
|
portal_pairs[pair_id] = []
|
|
portal_pairs[pair_id].append(pos)
|
|
|
|
# Second pass: Add walls and portal pairs to FEN
|
|
for pos in tileManager.active_tiles:
|
|
var tile = tileManager.active_tiles[pos]
|
|
var algebraic = Utils.location_to_algebraic(pos)
|
|
|
|
if tile is WallTile or tile is DoubleWallTile:
|
|
if tile is DoubleWallTile:
|
|
special_fen += " @%s" % algebraic
|
|
else:
|
|
special_fen += " @%s" % algebraic
|
|
# elif tile is PortalTile:
|
|
# # Only process each portal pair once
|
|
# var pair_id = tile.pair_id
|
|
# if portal_pairs.has(pair_id):
|
|
# var pair = portal_pairs[pair_id]
|
|
# if pair.size() == 2:
|
|
# var alg1 = Utils.location_to_algebraic(pair[0])
|
|
# var alg2 = Utils.location_to_algebraic(pair[1])
|
|
# special_fen += " portal %s=p1 portal %s=p2" % [alg1, alg2]
|
|
# portal_pairs.erase(pair_id) # Process each pair only once
|
|
|
|
return special_fen
|
|
|
|
func getCurrentFen() -> String:
|
|
var fen = ""
|
|
|
|
# For a standard chess board, we want to generate FEN from top (black side, rank 8)
|
|
# to bottom (white side, rank 1)
|
|
for y in range(boardYSize):
|
|
var emptySquares = 0
|
|
for x in range(boardXSize):
|
|
# print("CHECKING ", str(x) + "-" + str(y))
|
|
var container = boardContainer.get_node(str(x) + "-" + str(y)) as PieceContainer
|
|
|
|
if tileManager.active_tiles.has(str(x) + "-" + str(y)):
|
|
|
|
if emptySquares > 0:
|
|
fen += str(emptySquares)
|
|
emptySquares = 0
|
|
var tile = tileManager.active_tiles[str(x) + "-" + str(y)]
|
|
if tile is WallTile or tile is DoubleWallTile:
|
|
if tile.tile_name == "Double Wall":
|
|
fen += "*"
|
|
else:
|
|
fen += "*"
|
|
else:
|
|
var piece = container.get_piece()
|
|
if piece == null:
|
|
emptySquares += 1
|
|
else:
|
|
if emptySquares > 0:
|
|
fen += str(emptySquares)
|
|
emptySquares = 0
|
|
# Convert piece to FEN notation
|
|
var fenChar = getPieceFenChar(piece)
|
|
fen += fenChar
|
|
# Add any remaining empty squares at the end of the rank
|
|
if emptySquares > 0:
|
|
fen += str(emptySquares)
|
|
|
|
# Add rank separator (except for the last rank)
|
|
if y < boardYSize - 1:
|
|
fen += "/"
|
|
|
|
# Add the rest of the FEN string components
|
|
fen += " %s %s %s %d %d" % [
|
|
"b" if currentPlayer == BLACK else "w",
|
|
castlingRights,
|
|
enPassantTarget,
|
|
halfMoveClock,
|
|
moveCount
|
|
]
|
|
var specialChars = getSpecialTilesFen();
|
|
print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
|
|
print(specialChars)
|
|
print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
|
|
# fen += specialChars;
|
|
currentFen = fen;
|
|
return fen
|
|
func initializeBoard() -> void:
|
|
# Parse FEN to get board dimensions
|
|
# rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
|
|
var fen_parts = FEN.split(" ")
|
|
var rows = fen_parts[0].split("/")
|
|
boardYSize = rows.size()
|
|
boardXSize = 0
|
|
|
|
# Calculate width from first row by counting both pieces and numbers
|
|
for c in rows[0]:
|
|
if c.is_valid_int():
|
|
boardXSize += int(c) # Add the number of empty squares
|
|
else:
|
|
boardXSize += 1
|
|
|
|
# Calculate board width by counting pieces and empty squares in first row
|
|
# Initialize board array
|
|
board = []
|
|
for i in range(boardYSize):
|
|
var row = []
|
|
for j in range(boardXSize):
|
|
row.append(null)
|
|
board.append(row)
|
|
|
|
createBoard()
|
|
setupPiecesFromFEN()
|
|
|
|
if !boardContainer.has_user_signal("tile_pressed"):
|
|
boardContainer.add_user_signal("tile_pressed")
|
|
if !boardContainer.has_user_signal("send_location"):
|
|
boardContainer.add_user_signal("send_location")
|
|
|
|
func setupUI() -> void:
|
|
p1String.text = "0"
|
|
p2String.text = "0"
|
|
updateTurnIndicator()
|
|
|
|
|
|
func initializeDeckSystem() -> void:
|
|
deckManager = DeckManager.new()
|
|
cardDisplay = CardDisplay.new()
|
|
add_child(deckManager)
|
|
add_child(cardDisplay)
|
|
|
|
if !deckManager.has_user_signal("card_pressed"):
|
|
deckManager.add_user_signal("card_pressed")
|
|
|
|
if !deckManager.has_user_signal("hand_updated"):
|
|
deckManager.add_user_signal("hand_updated")
|
|
|
|
cardDisplay.update_hand(deckManager.hand)
|
|
deckManager.connect("hand_updated", func(hand): cardDisplay.update_hand(hand))
|
|
|
|
func createBoard() -> void:
|
|
boardContainer.add_to_group("Flow")
|
|
var numberX = 0
|
|
var numberY = 0
|
|
var isWhite = true
|
|
|
|
while numberY != boardYSize:
|
|
boardContainer.size.y += tileYSize + 5
|
|
boardContainer.size.x += tileXSize + 5
|
|
|
|
while numberX != boardXSize:
|
|
createTile(numberX, numberY, isWhite)
|
|
isWhite = !isWhite
|
|
numberX += 1
|
|
|
|
isWhite = !isWhite
|
|
numberY += 1
|
|
numberX = 0
|
|
|
|
|
|
func createTile(x: int, y: int, isWhite: bool) -> void:
|
|
# print("CreateTile x ", x, " y ", y);
|
|
var tile = PieceContainer.new()
|
|
tile.set_custom_minimum_size(Vector2(tileXSize, tileYSize))
|
|
tile.add_theme_stylebox_override("normal", lightStyle if isWhite else darkStyle)
|
|
|
|
tile.set_name(str(x) + "-" + str(y))
|
|
# tile.pressed.connect(func(): handleTileSelection(tile.name))
|
|
tile.pressed.connect(func():
|
|
# tile_pressed.emit(tile.name)
|
|
boardContainer.emit_signal("tile_pressed", tile.name)
|
|
)
|
|
|
|
boardContainer.add_child(tile)
|
|
|
|
func clearSelection() :
|
|
resetHighlights()
|
|
selectedNode = ""
|
|
cardPreview.hide_preview()
|
|
return
|
|
|
|
|
|
|
|
func updatePointsAndCapture(capturedPiece: Pawn, animate: bool = true) -> void:
|
|
if Turn == 0:
|
|
p1Points += capturedPiece.Points
|
|
p1String.text = str(p1Points)
|
|
else:
|
|
p2Points += capturedPiece.Points
|
|
p2String.text = str(p2Points)
|
|
if animate:
|
|
animatePieceCapture(capturedPiece)
|
|
|
|
|
|
if capturedPiece.name == "King":
|
|
print("Game Over!")
|
|
gamecheckMate = true
|
|
|
|
func animatePieceCapture(capturedPiece: Pawn) -> void:
|
|
var container = capturedPiece.get_parent() as PieceContainer
|
|
await capturedPiece.animate_capture()
|
|
container.remove_piece()
|
|
|
|
func parseLocation(location: String) -> void:
|
|
var number = 0
|
|
locationX = ""
|
|
while location.substr(number, 1) != "-":
|
|
locationX += location.substr(number, 1)
|
|
number += 1
|
|
locationY = location.substr(number + 1)
|
|
|
|
|
|
func setupStyles() -> void:
|
|
lightStyle = StyleBoxFlat.new()
|
|
lightStyle.bg_color = Utils.LIGHT_CELL
|
|
darkStyle = StyleBoxFlat.new()
|
|
darkStyle.bg_color = Utils.DARK_CELL
|
|
|
|
highlightStyle = StyleBoxFlat.new()
|
|
highlightStyle.bg_color = Color(0, 0.3, 0, 1)
|
|
|
|
func updateTurnIndicator():
|
|
if Turn == 0: # White's turn
|
|
turnIndicator.color = Color(1, 1, 1, 1) # White
|
|
else: # Black's turn
|
|
turnIndicator.color = Color(0, 0, 0, 1) # Black
|
|
|
|
|
|
func setupPiecesFromFEN() -> void:
|
|
var fen_parts = FEN.split(" ")
|
|
var rows = fen_parts[0].split("/")
|
|
|
|
# Iterate through rows in reverse to place black pieces at top
|
|
for y in range(rows.size()):
|
|
var x = 0
|
|
# Convert y coordinate to flip the board (7-y puts white at bottom)
|
|
var board_y = (boardYSize - 1) - y # For an 8x8 board
|
|
for c in rows[y]:
|
|
if c.is_valid_int():
|
|
# Skip empty squares
|
|
x += int(c)
|
|
else:
|
|
var piece_info = getFENPieceInfo(c)
|
|
placePiece(str(x) + "-" + str(board_y), piece_info.name, piece_info.color)
|
|
x += 1
|
|
|
|
|
|
func getFENPieceInfo(fen_char: String) -> Dictionary:
|
|
var piece_info = {
|
|
"name": "",
|
|
"color": 0 # Black
|
|
}
|
|
|
|
# If uppercase, it's white
|
|
if fen_char.to_upper() == fen_char:
|
|
piece_info.color = 1 # White
|
|
|
|
# Map FEN characters to piece names
|
|
match fen_char.to_upper():
|
|
"P": piece_info.name = "Pawn"
|
|
"R": piece_info.name = "Rook"
|
|
"N": piece_info.name = "Knight"
|
|
"B": piece_info.name = "Bishop"
|
|
"Q": piece_info.name = "Queen"
|
|
"K": piece_info.name = "King"
|
|
|
|
return piece_info
|
|
|
|
func placePiece(position: String, pieceName: String, color: int) -> void:
|
|
var piece = summonPiece(pieceName, color)
|
|
var container = boardContainer.get_node(position) as PieceContainer
|
|
await container.set_piece(piece, false)
|
|
container.remove_piece(true)
|
|
container.set_piece(piece, false)
|
|
|
|
var coords = position.split("-")
|
|
board[int(coords[1])][int(coords[0])] = piece
|
|
|
|
|
|
func summonPiece(pieceName: String, color: int) -> Node:
|
|
var piece
|
|
match pieceName:
|
|
"Pawn":
|
|
piece = Pawn.new(self)
|
|
piece.name = "Pawn"
|
|
"King":
|
|
piece = King.new(self)
|
|
piece.name = "King"
|
|
"Queen":
|
|
piece = Queen.new(self)
|
|
piece.name = "Queen"
|
|
"Knight":
|
|
piece = Knight.new(self)
|
|
piece.name = "Knight"
|
|
"Rook":
|
|
piece = Rook.new(self)
|
|
piece.name = "Rook"
|
|
"Bishop":
|
|
piece = Bishop.new(self)
|
|
piece.name = "Bishop"
|
|
|
|
piece.Item_Color = color
|
|
piece.position = Vector2(tileXSize / 2, tileYSize / 2)
|
|
return piece
|
|
|
|
func clearBoard() -> void:
|
|
for child in boardContainer.get_children():
|
|
if child is PieceContainer:
|
|
child.remove_piece()
|
|
|
|
|
|
# func prepareHand() -> void:
|
|
# if deckManager.hand.size() < 5:
|
|
# deckManager.drawCard()
|
|
# pass
|
|
|
|
func updateEffectDurations() -> void:
|
|
deckManager.updateCardDurations()
|
|
tileManager.update_tile_durations()
|
|
|
|
func applyTileEffects() -> void:
|
|
tileManager.apply_tile_effects()
|
|
|
|
|
|
func applyCardEffects() -> void:
|
|
pass
|
|
|
|
func evaluatePosition() -> Dictionary:
|
|
var status = {
|
|
"checkmate": isCheckmate(),
|
|
"draw": isDraw(),
|
|
}
|
|
return status
|
|
|
|
func isValidMove(location: String) -> bool:
|
|
var node = get_node("Flow/" + location) as PieceContainer
|
|
var piece = node.get_piece()
|
|
if piece == null || piece.Item_Color != Turn:
|
|
for area in areas:
|
|
if area == node.name:
|
|
return true
|
|
return false
|
|
|
|
|
|
func resetBoard() -> void:
|
|
clearSelection()
|
|
clearBoard()
|
|
setupPiecesFromFEN()
|
|
Turn = 0
|
|
currentPlayer = WHITE
|
|
p1Points = 0
|
|
p1String.text = str(p1Points)
|
|
p2Points = 0
|
|
p2String.text = str(p2Points)
|
|
gamecheckMate = false;
|
|
gamedraw = false;
|
|
deckManager.initializeStartingDeck()
|
|
|
|
areas.clear()
|
|
specialArea.clear()
|
|
updateTurnIndicator()
|
|
|
|
func getMovableAreas() -> void:
|
|
# print("HIGHLIGHTING getMovableAreas 1")
|
|
resetHighlights()
|
|
areas.clear()
|
|
specialArea.clear()
|
|
var container = get_node("Flow/" + selectedNode) as PieceContainer
|
|
var piece = container.get_piece()
|
|
var piece_id = piece.get_instance_id()
|
|
# print("HIGHLIGHTING getMovableAreas 2")
|
|
if deckManager.attached_cards.has(piece_id):
|
|
var card = deckManager.attached_cards[piece_id]
|
|
cardPreview.show_card_preview(card)
|
|
if stateMachine.state.name == Constants.MOVEMENT:
|
|
var movement_state = stateMachine.state
|
|
if piece_id in movement_state.moves_remaining:
|
|
var moves_left = movement_state.moves_remaining[piece_id] - 1
|
|
cardPreview.update_moves_remaining(moves_left)
|
|
else:
|
|
cardPreview.hide_preview()
|
|
# print("HIGHLIGHTING getMovableAreas 3")
|
|
var moves = piece.getValidMoves(boardContainer, selectedNode)
|
|
areas = moves.regular_moves
|
|
specialArea = moves.special_moves
|
|
# print("HIGHLIGHTING getMovableAreas 4")
|
|
highlightValidMoves()
|
|
|
|
func highlightValidMoves() -> void:
|
|
for move in areas:
|
|
var button = boardContainer.get_node(move)
|
|
|
|
# If there's an active tile effect, combine with its current style instead
|
|
if tileManager && tileManager.get_tile(move):
|
|
var current_style = button.get_theme_stylebox("normal")
|
|
var highlightedStyle = StyleBoxFlat.new()
|
|
highlightedStyle.bg_color = current_style.bg_color + highlightStyle.bg_color
|
|
button.add_theme_stylebox_override("normal", highlightedStyle)
|
|
else:
|
|
# Default chess pattern highlighting
|
|
var isWhiteSquare = (int(move.split("-")[0]) + int(move.split("-")[1])) % 2 == 0
|
|
var baseStyle = lightStyle if isWhiteSquare else darkStyle
|
|
var combinedStyle = StyleBoxFlat.new()
|
|
combinedStyle.bg_color = baseStyle.bg_color + highlightStyle.bg_color
|
|
button.add_theme_stylebox_override("normal", combinedStyle)
|
|
|
|
func executeMove(targetLocation: String) -> void:
|
|
print("executeMove ", targetLocation)
|
|
var targetContainer = get_node("Flow/" + targetLocation) as PieceContainer
|
|
var sourceContainer = get_node("Flow/" + selectedNode) as PieceContainer
|
|
var piece = sourceContainer.get_piece()
|
|
var old_location = selectedNode
|
|
|
|
# Handle capture if there's a piece in target location
|
|
if targetContainer.has_piece():
|
|
var capturedPiece = targetContainer.get_piece()
|
|
if Turn == 0:
|
|
p1Points += capturedPiece.Points
|
|
p1String.text = str(p1Points)
|
|
else:
|
|
p2Points += capturedPiece.Points
|
|
p2String.text = str(p2Points)
|
|
targetContainer.remove_piece() # This handles freeing the captured piece
|
|
|
|
# Move piece to new location
|
|
sourceContainer.remove_piece(true)
|
|
targetContainer.set_piece(piece)
|
|
|
|
hasMoved = true
|
|
currentlyMovingPiece = piece
|
|
resetHighlights()
|
|
|
|
func togglePieceChessEffect() -> void:
|
|
var piece = currentlyMovingPiece
|
|
if piece.name == "Pawn":
|
|
if piece.Double_Start:
|
|
piece.En_Passant = true
|
|
piece.Double_Start = false
|
|
elif piece.name == "King":
|
|
piece.Castling = false
|
|
elif piece.name == "Rook":
|
|
piece.Castling = false
|
|
|
|
func resolveMoveEffects() -> void:
|
|
print("resolveMoveEffects", currentlyMovingPiece)
|
|
togglePieceChessEffect()
|
|
|
|
selectedNode = ""
|
|
Turn = 1 if Turn == 0 else 0
|
|
updateTurnIndicator()
|
|
resetHighlights()
|
|
hasMoved = false
|
|
currentlyMovingPiece = null
|
|
emit_signal("turn_changed")
|
|
|
|
func isPlayerTurn() -> bool:
|
|
return currentPlayer == WHITE
|
|
|
|
func resetHighlights():
|
|
for button in boardContainer.get_children():
|
|
if !button.name.contains("-"):
|
|
continue
|
|
|
|
# Skip if this tile has an active effect
|
|
if tileManager && tileManager.get_tile(button.name):
|
|
continue
|
|
|
|
var coord = button.name.split("-")
|
|
var isWhiteSquare = (int(coord[0]) + int(coord[1])) % 2 == 0
|
|
if isWhiteSquare:
|
|
button.add_theme_stylebox_override("normal", lightStyle)
|
|
else:
|
|
button.add_theme_stylebox_override("normal", darkStyle)
|
|
|
|
|
|
func isCheckmate() -> bool:
|
|
return gamecheckMate
|
|
|
|
func isDraw() -> bool:
|
|
return gamedraw
|
|
|
|
func endGame(reason: String) -> void:
|
|
pass
|
|
|
|
func cleanupPhase() -> void:
|
|
pass
|
|
|
|
func isNull(location: String) -> bool:
|
|
return get_node_or_null("Flow/" + location) == null
|
|
|
|
|
|
func getPieceFenChar(piece: Pawn) -> String:
|
|
if piece == null:
|
|
return ""
|
|
var fenChar = ""
|
|
match piece.name:
|
|
"Pawn": fenChar = "p"
|
|
"Knight": fenChar = "n"
|
|
"Bishop": fenChar = "b"
|
|
"Rook": fenChar = "r"
|
|
"Queen": fenChar = "q"
|
|
"King": fenChar = "k"
|
|
|
|
# In our system, Item_Color == 1 is white, 0 is black
|
|
return fenChar.to_upper() if piece.Item_Color == 0 else fenChar
|
|
|
|
func updateStateFromMove(fromIdx: int, toIdx: int) -> void:
|
|
# Update game state based on the move
|
|
isWhiteToMove = !isWhiteToMove
|
|
if isWhiteToMove:
|
|
moveCount += 1
|
|
|
|
# Update castling rights if needed
|
|
updateCastlingRights(fromIdx, toIdx)
|
|
|
|
# Update en passant target
|
|
updateEnPassantTarget(fromIdx, toIdx)
|
|
|
|
# Update halfmove clock
|
|
updateHalfMoveClock(fromIdx, toIdx)
|
|
|
|
func updateCastlingRights(fromIdx: int, toIdx: int) -> void:
|
|
var piece = board[fromIdx]
|
|
if piece == null:
|
|
return
|
|
|
|
# Remove castling rights when king or rook moves
|
|
match piece.type:
|
|
"king":
|
|
if piece.isWhite:
|
|
castlingRights = castlingRights.replace("K", "").replace("Q", "")
|
|
else:
|
|
castlingRights = castlingRights.replace("k", "").replace("q", "")
|
|
"rook":
|
|
var startRank = 7 if piece.isWhite else 0
|
|
if fromIdx == startRank * 8: # Queen-side rook
|
|
castlingRights = castlingRights.replace("Q" if piece.isWhite else "q", "")
|
|
elif fromIdx == startRank * 8 + 7: # King-side rook
|
|
castlingRights = castlingRights.replace("K" if piece.isWhite else "k", "")
|
|
|
|
if castlingRights == "":
|
|
castlingRights = "-"
|
|
|
|
func updateEnPassantTarget(fromIdx: int, toIdx: int) -> void:
|
|
var piece = board[fromIdx]
|
|
if piece == null or piece.type != "pawn":
|
|
enPassantTarget = "-"
|
|
return
|
|
|
|
# Check for double pawn move
|
|
var fromRank = fromIdx / 8
|
|
var toRank = toIdx / 8
|
|
if abs(fromRank - toRank) == 2:
|
|
var file = fromIdx % 8
|
|
var targetRank = (fromRank + toRank) / 2
|
|
enPassantTarget = "%s%d" % [char(97 + file), 8 - targetRank]
|
|
else:
|
|
enPassantTarget = "-"
|
|
|
|
func updateHalfMoveClock(fromIdx: int, toIdx: int) -> void:
|
|
var piece = board[fromIdx]
|
|
if piece == null:
|
|
return
|
|
|
|
# Reset on pawn move or capture
|
|
if piece.type == "pawn" or board[toIdx] != null:
|
|
halfMoveClock = 0
|
|
else:
|
|
halfMoveClock += 1
|
|
|
|
|
|
func _on_ai_move_generated(move: String) -> void:
|
|
if Turn == 1: # Only process AI moves during black's turn
|
|
# The move will be automatically handled by the Movement state
|
|
# which is listening for the moveGenerated signal
|
|
pass
|
|
|
|
func executeAiMove(fromLocation: String, toLocation: String) -> void:
|
|
selectedNode = fromLocation
|
|
executeMove(toLocation)
|
|
|
|
func convertNotationToLocation(notation: String) -> String:
|
|
var file = notation[0].unicode_at(0) - 'a'.unicode_at(0)
|
|
var rank = 8 - int(notation[1])
|
|
return str(file) + "-" + str(rank)
|