From ef6cee78833649561c4615e731cb0935ec957932 Mon Sep 17 00:00:00 2001 From: 2ManyProjects Date: Sat, 8 Mar 2025 23:25:49 -0600 Subject: [PATCH] implemented maze --- Systems/Game/ChessGame.gd | 43 +++-- Systems/Game/Map/MapGenerator.gd | 129 +++++++++++++- Systems/Game/Menu/MenuContainer.gd | 9 + Systems/Game/WinConditionManager.gd | 16 +- .../StateMachine/GameStates/CleanupPhase.gd | 2 +- .../GameStates/EvaluatePosition.gd | 10 +- addons/Chess/Scripts/King.gd | 158 +++++++++--------- 7 files changed, 263 insertions(+), 104 deletions(-) diff --git a/Systems/Game/ChessGame.gd b/Systems/Game/ChessGame.gd index f475321..f68187d 100644 --- a/Systems/Game/ChessGame.gd +++ b/Systems/Game/ChessGame.gd @@ -42,6 +42,7 @@ var highlightStyle = null var cpuElo = 1500 var is_initialized: bool = false var currentNode = null; +var has_opponent = true; # Node references @onready var turnIndicator: ColorRect = $TurnIndicator @@ -60,9 +61,7 @@ var currentNode = null; var captured_pieces_this_turn: Array = [] var player_pieces_lost_this_turn: Array = [] -var winConditionManager: WinConditionManager -var currentWinCondition = Utils.WinConditionType.CAPTURE_UNIT -var currentLossCondition = Utils.LossConditionType.UNIT_LOST +var winConditionManager: WinConditionManager = null # Export parameters @@ -109,13 +108,21 @@ func _on_new_game_requested(options = {}): captured_pieces_this_turn = [] player_pieces_lost_this_turn = [] turnIndicator.visible = true - if options and "fen" in options.metadata: - currentFen = options.metadata.fen - if "elo" in options: + has_opponent = true + if cameraController: + cameraController.reset_view() + if options: + if "fen" in options.metadata: + currentFen = options.metadata.fen + if "elo" in options.metadata: cpuElo = options.metadata.elo - if cameraController: - cameraController.reset_view() - if options and "metadata" in options: + if "has_opponent" in options.metadata: + has_opponent = options.metadata.has_opponent + if winConditionManager == null: + winConditionManager = WinConditionManager.new(self) + add_child(winConditionManager) + winConditionManager.connect("win_condition_met", Callable(self, "_on_win_condition_met")) + winConditionManager.connect("loss_condition_met", Callable(self, "_on_loss_condition_met")) if winConditionManager: winConditionManager.load_condition_from_metadata(options.metadata) @@ -126,6 +133,7 @@ func _on_new_game_requested(options = {}): resetBoard() initializeDeckSystem() initializeBoard() + initializeTiles() if cardDisplay: cardDisplay.visible = true if stateMachine: @@ -150,11 +158,11 @@ func initialize_game_system(): stockfishController = StockfishController.new() add_child(stockfishController) stockfishController.connect_to_engine(stockfishPath, self) - - winConditionManager = WinConditionManager.new(self) - add_child(winConditionManager) - winConditionManager.connect("win_condition_met", Callable(self, "_on_win_condition_met")) - winConditionManager.connect("loss_condition_met", Callable(self, "_on_loss_condition_met")) + if winConditionManager == null: + winConditionManager = WinConditionManager.new(self) + add_child(winConditionManager) + winConditionManager.connect("win_condition_met", Callable(self, "_on_win_condition_met")) + winConditionManager.connect("loss_condition_met", Callable(self, "_on_loss_condition_met")) # Start the state machine if stateMachine: @@ -621,11 +629,14 @@ func resolveMoveEffects() -> void: func endTurn() -> void: print("turn_changed") emit_signal("turn_changed") - Turn = 1 if Turn == 0 else 0 + if has_opponent: + Turn = 1 if Turn == 0 else 0 updateTurnIndicator() if (isPlayerTurn()): updateEffectDurations() - check_captured_pieces_conditions() + + if winConditionManager.win_condition == Utils.WinConditionType.CAPTURE_UNIT || winConditionManager.loss_condition == Utils.LossConditionType.UNIT_LOST: + check_captured_pieces_conditions() func togglePieceChessEffect() -> void: diff --git a/Systems/Game/Map/MapGenerator.gd b/Systems/Game/Map/MapGenerator.gd index 3807fd7..104df09 100644 --- a/Systems/Game/Map/MapGenerator.gd +++ b/Systems/Game/Map/MapGenerator.gd @@ -198,7 +198,7 @@ func _get_random_room_type(level, total_levels): return Utils.RoomType.BOSS elif roll < boss_chance + shop_chance: return Utils.RoomType.SHOP - elif roll < boss_chance + shop_chance + event_chance and level > 5: + elif roll < boss_chance + shop_chance + event_chance: return Utils.RoomType.EVENT else: return Utils.RoomType.NORMAL @@ -341,12 +341,39 @@ func generate_starting_data(node): } func generate_event_data(node): + var index = map_to_array_index(node.level, 2, current_max_level - 2, 1, level_unit_distribution.size() - 1); + var unit_string = level_unit_distribution[index] + + var height = 6 + var width = unit_string.length() + if node.level > 7 and node.level <= 10: + height = node.level + elif node.level > 10: + height = 10 + # var mazedata = generate_maze(width, height) + var mazedata = generate_maze(16, 16) + # print("**************************") + # print("**************************") + # print("**************************") + # print(mazedata) + # print("**************************") + # print("**************************") + # print("**************************") return { "is_escape": node.metadata.is_escape if node.metadata.has("is_escape") else false, - "fen": "8/4p3/8/8/8/8 w KQkq - 0 1", + "fen": mazedata.fen + " w KQkq - 0 1", "game_type": "maze", + "has_opponent": false, "win_condition": Utils.WinConditionType.TILE_REACHED, + "win_target": [mazedata.end], + "win_target_unit": "King", "elo": node.elo, + "reward": { + "gold": 150 * node.level, + "cards": [], + "selection": generate_shop_cards(5), + "selection_limit": 1 + }, } # "rnbqkbnr1/pppppppp1/9/9/9/9/9/PPPPPPPP1/RNBQKBNR1 w KQkq - 0 1" func generate_chess_data(node): @@ -475,3 +502,101 @@ func map_to_array_index(current_value, min_value, max_value, min_index, max_inde # Ensure we're returning an integer within the valid array index range return int(clamp(mapped_index, min_index, max_index)) + + + +func generate_maze(width: int, height: int) -> Dictionary: + # Ensure dimensions are odd to have proper walls + if width % 2 == 0: + width += 1 + if height % 2 == 0: + height += 1 + + # Initialize the maze with all walls + var maze = [] + for y in range(height): + var row = [] + for x in range(width): + row.append("*") # * represents wall + maze.append(row) + + # Use a recursive backtracking algorithm to generate the maze + var rng = RandomNumberGenerator.new() + rng.randomize() + + # Start at a random odd position + var start_x = rng.randi_range(0, width/2-1) * 2 + 1 + var start_y = rng.randi_range(0, height/2-1) * 2 + 1 + + # Carve the maze recursively + _carve_maze(maze, start_x, start_y, width, height, rng) + + # Pick a random end point (far from start point) + var end_x = 0 + var end_y = 0 + var max_distance = 0 + + for y in range(1, height, 2): + for x in range(1, width, 2): + if maze[y][x] == " ": # Only consider path cells + var distance = abs(x - start_x) + abs(y - start_y) + if distance > max_distance: + max_distance = distance + end_x = x + end_y = y + + maze[start_y][start_x] = "k" + + # Mark the end position with a space (keep it as a path) + # It's already a space, but we ensure it here + maze[end_y][end_x] = " " + + # Convert the maze to a FEN-like string + var fen_string = "" + for y in range(height): + var empty_count = 0 + for x in range(width): + if maze[y][x] == " ": # Empty space + empty_count += 1 + else: + if empty_count > 0: + fen_string += str(empty_count) + empty_count = 0 + fen_string += maze[y][x] + + # Add any remaining empty count + if empty_count > 0: + fen_string += str(empty_count) + + # Add row separator (except for the last row) + if y < height - 1: + fen_string += "/" + return { + "fen": fen_string, + "start": str(start_x) + "-"+ str(start_y), + "end": str(end_x) + "-"+ str(end_y) + } + +# Recursive function to carve out the maze +func _carve_maze(maze, x, y, width, height, rng): + # Mark the current cell as a path + maze[y][x] = " " + + # Define the four possible directions (up, right, down, left) + var directions = [[0, -2], [2, 0], [0, 2], [-2, 0]] + + # Shuffle directions for randomness + directions.shuffle() + + # Try each direction + for dir in directions: + var nx = x + dir[0] + var ny = y + dir[1] + + # Check if the new position is valid and unvisited + if nx > 0 and nx < width-1 and ny > 0 and ny < height-1 and maze[ny][nx] == "*": + # Carve a path between the current cell and the new cell + maze[y + dir[1]/2][x + dir[0]/2] = " " + + # Recursively carve from the new cell + _carve_maze(maze, nx, ny, width, height, rng) diff --git a/Systems/Game/Menu/MenuContainer.gd b/Systems/Game/Menu/MenuContainer.gd index 32a55a2..c07bd92 100644 --- a/Systems/Game/Menu/MenuContainer.gd +++ b/Systems/Game/Menu/MenuContainer.gd @@ -304,6 +304,15 @@ func _on_map_node_selected(node_data): mapScreen.visible = false back_to_map = true _on_start_game_pressed(node_data) + elif node_data.type == Utils.RoomType.EVENT: + if gameMenuScreen: + gameMenuScreen.visible = false + if deckManagerScreen: + deckManagerScreen.visible = false + if mapScreen: + mapScreen.visible = false + back_to_map = true + _on_start_game_pressed(node_data) func _on_node_completed(options): var node_data = options.node diff --git a/Systems/Game/WinConditionManager.gd b/Systems/Game/WinConditionManager.gd index 376f795..2afbe3d 100644 --- a/Systems/Game/WinConditionManager.gd +++ b/Systems/Game/WinConditionManager.gd @@ -31,9 +31,11 @@ func _init(game: ChessGame): chess_game.connect("game_initialized", Callable(self, "_on_game_initialized")) func _on_game_initialized(): - reset_conditions() + # reset_conditions() + print("_on_game_initialized") func reset_conditions(): + print("reset_conditions") captured_pieces.clear() turn_count = 0 target_tiles.clear() @@ -81,12 +83,11 @@ func highlight_target_tiles(): func set_win_condition(type: Utils.WinConditionType, data: Dictionary = {}): win_condition = type win_condition_data = data - reset_conditions() func set_loss_condition(type: Utils.LossConditionType, data: Dictionary = {}): loss_condition = type loss_condition_data = data - reset_conditions() + # reset_conditions() func _on_turn_changed(): turn_count += 1 @@ -112,7 +113,7 @@ func _on_turn_changed(): check_conditions() func on_piece_captured(piece): - print("on_piece_captured ISEnemy ", piece) + # print("on_piece_captured ISEnemy ", piece) var is_enemy_piece = piece.Item_Color == (1 if chess_game.Turn == 1 else 0) print("on_piece_captured ISEnemy ", is_enemy_piece) if is_enemy_piece: @@ -220,14 +221,17 @@ func count_enemy_pieces() -> int: # Helper methods for game integration func load_condition_from_metadata(metadata: Dictionary): + # print("load_condition_from_metadata ", metadata) if metadata.has("win_condition"): match metadata["win_condition"]: Utils.WinConditionType.CAPTURE_UNIT: set_win_condition(Utils.WinConditionType.CAPTURE_UNIT, {"unit": "King"}) Utils.WinConditionType.TILE_REACHED: # Get target tiles from metadata if available - var target_tiles = metadata.get("target_tiles", []) - set_win_condition(Utils.WinConditionType.TILE_REACHED, {"target_tiles": target_tiles, "unit": metadata.get("target_unit", "")}) + + target_tiles = metadata.get("win_target", []) + # print("load_condition_from_metadata ", " target_tiles ", target_tiles) + set_win_condition(Utils.WinConditionType.TILE_REACHED, {"target_tiles": target_tiles, "unit": metadata.get("win_target_unit", "")}) Utils.WinConditionType.BOARD_CLEARED: set_win_condition(Utils.WinConditionType.BOARD_CLEARED) Utils.WinConditionType.TURN_NUMBER: diff --git a/Systems/StateMachine/GameStates/CleanupPhase.gd b/Systems/StateMachine/GameStates/CleanupPhase.gd index acedf08..acd2ef1 100644 --- a/Systems/StateMachine/GameStates/CleanupPhase.gd +++ b/Systems/StateMachine/GameStates/CleanupPhase.gd @@ -5,7 +5,7 @@ func enter(_previous: String, data := {}) -> void: if "endCondition" in data: finished.emit(Constants.ROUND_END) - elif game.currentPlayer == game.WHITE: + elif game.currentPlayer == game.WHITE and game.has_opponent: finished.emit(Constants.BLACK_TURN) else: finished.emit(Constants.WHITE_TURN) \ No newline at end of file diff --git a/Systems/StateMachine/GameStates/EvaluatePosition.gd b/Systems/StateMachine/GameStates/EvaluatePosition.gd index 6a3226e..832cf53 100644 --- a/Systems/StateMachine/GameStates/EvaluatePosition.gd +++ b/Systems/StateMachine/GameStates/EvaluatePosition.gd @@ -20,6 +20,7 @@ func enter(_previous: String, _data := {}) -> void: func check_win_conditions() -> bool: match game.winConditionManager.win_condition: Utils.WinConditionType.CAPTURE_UNIT: + print("check_win_conditions ", "Utils.WinConditionType.CAPTURE_UNIT") # Directly check if king/target unit was captured var target_unit = game.winConditionManager.win_condition_data.get("unit", "King") @@ -35,6 +36,7 @@ func check_win_conditions() -> bool: return true game.captured_pieces_this_turn = [] Utils.WinConditionType.BOARD_CLEARED: + print("check_win_conditions ", "Utils.WinConditionType.BOARD_CLEARED") var remaining_enemy_pieces = game.winConditionManager.count_enemy_pieces() if remaining_enemy_pieces <= 0: var condition_data = { @@ -46,6 +48,7 @@ func check_win_conditions() -> bool: return true Utils.WinConditionType.TURN_NUMBER: + print("check_win_conditions ", "Utils.WinConditionType.TURN_NUMBER") var target_turn = game.winConditionManager.win_condition_data.get("target_turn", 0) if game.winConditionManager.turn_count >= target_turn: var condition_data = { @@ -57,15 +60,18 @@ func check_win_conditions() -> bool: return true Utils.WinConditionType.TILE_REACHED: - var target_tiles = game.winConditionManager.target_tiles - var required_unit = game.winConditionManager.win_condition_data.get("unit", "") + print("check_win_conditions ", "Utils.WinConditionType.TILE_REACHED", game.winConditionManager.win_condition_data) + var target_tiles = game.winConditionManager.win_condition_data.get("target_tiles", []) + var required_unit = game.winConditionManager.win_condition_data.get("unit", "King") var all_targets_reached = true + # print("check_win_conditions ", required_unit, " ", target_tiles) for tile_loc in target_tiles: if !game.is_tile_reached(tile_loc, required_unit): all_targets_reached = false break + # print("all_targets_reached ", all_targets_reached,) if all_targets_reached and target_tiles.size() > 0: var condition_data = { "type": Utils.WinConditionType.TILE_REACHED, diff --git a/addons/Chess/Scripts/King.gd b/addons/Chess/Scripts/King.gd index 069fe16..6845980 100644 --- a/addons/Chess/Scripts/King.gd +++ b/addons/Chess/Scripts/King.gd @@ -5,88 +5,92 @@ class_name King var Castling = true func _ready(): - self.texture = load("res://addons/Chess/Textures/WKing.svg") - Points = 10 + self.texture = load("res://addons/Chess/Textures/WKing.svg") + Points = 10 func _process(_delta): - if Item_Color != Temp_Color: - Temp_Color = Item_Color - if Item_Color == 0: - self.texture = load("res://addons/Chess/Textures/WKing.svg") - elif Item_Color == 1: - self.texture = load("res://addons/Chess/Textures/BKing.svg") + if Item_Color != Temp_Color: + Temp_Color = Item_Color + if Item_Color == 0: + self.texture = load("res://addons/Chess/Textures/WKing.svg") + elif Item_Color == 1: + self.texture = load("res://addons/Chess/Textures/BKing.svg") # King.gd func getValidMoves(board_flow, current_location: String) -> Dictionary: - var moves = { - "regular_moves": [], - "special_moves": [] - } - - var loc = current_location.split("-") - var x = int(loc[0]) - var y = int(loc[1]) - - # Regular king moves (unchanged) - var king_dirs = [[0,1], [0,-1], [1,0], [-1,0], [1,1], [1,-1], [-1,1], [-1,-1]] - for dir in king_dirs: - var new_loc = str(x + dir[0]) + "-" + str(y + dir[1]) - if is_valid_cell(board_flow, new_loc): - if can_move_to_cell(board_flow, new_loc) || can_move_to_cell(board_flow, new_loc, true): - moves.regular_moves.append(new_loc) - - # Castling logic - if Castling: - # Kingside castling (right side) - if check_kingside_castle(board_flow, x, y): - # Add rook's current position to regular moves - moves.regular_moves.append(str(x + 3) + "-" + str(y)) - # Add king and rook destinations to special moves as individual items - moves.special_moves.append(str(x + 1) + "-" + str(y)) # Rook's destination - moves.special_moves.append(str(x + 2) + "-" + str(y)) # King's destination - - # Queenside castling (left side) - if check_queenside_castle(board_flow, x, y): - # Add rook's current position to regular moves - moves.regular_moves.append(str(x - 4) + "-" + str(y)) - # Add king and rook destinations to special moves as individual items - moves.special_moves.append(str(x - 1) + "-" + str(y)) # Rook's destination - moves.special_moves.append(str(x - 2) + "-" + str(y)) # King's destination - - return moves + var moves = { + "regular_moves": [], + "special_moves": [] + } + + var loc = current_location.split("-") + var x = int(loc[0]) + var y = int(loc[1]) + + # Regular king moves (unchanged) + var king_dirs = [[0,1], [0,-1], [1,0], [-1,0], [1,1], [1,-1], [-1,1], [-1,-1]] + for dir in king_dirs: + var new_loc = str(x + dir[0]) + "-" + str(y + dir[1]) + if is_valid_cell(board_flow, new_loc): + if can_move_to_cell(board_flow, new_loc) || can_move_to_cell(board_flow, new_loc, true): + moves.regular_moves.append(new_loc) + + # Castling logic + if Castling: + # Kingside castling (right side) + if check_kingside_castle(board_flow, x, y): + # Add rook's current position to regular moves + moves.regular_moves.append(str(x + 3) + "-" + str(y)) + # Add king and rook destinations to special moves as individual items + moves.special_moves.append(str(x + 1) + "-" + str(y)) # Rook's destination + moves.special_moves.append(str(x + 2) + "-" + str(y)) # King's destination + + # Queenside castling (left side) + if check_queenside_castle(board_flow, x, y): + # Add rook's current position to regular moves + moves.regular_moves.append(str(x - 4) + "-" + str(y)) + # Add king and rook destinations to special moves as individual items + moves.special_moves.append(str(x - 1) + "-" + str(y)) # Rook's destination + moves.special_moves.append(str(x - 2) + "-" + str(y)) # King's destination + + return moves func check_kingside_castle(board_flow, x: int, y: int) -> bool: - # Check if path is clear - for i in range(1, 3): - var check_pos = str(x + i) + "-" + str(y) - if !is_valid_cell(board_flow, check_pos) || !can_move_to_cell(board_flow, check_pos): - return false - var container = board_flow.get_node(check_pos) as PieceContainer - if container.has_piece(): - return false - - var rook_pos = str(x + 3) + "-" + str(y) - if !is_valid_cell(board_flow, rook_pos): - return false - var rook_container = board_flow.get_node(rook_pos) as PieceContainer - var rook = rook_container.get_piece() - return rook.name == "Rook" && rook.Castling && rook.Item_Color == self.Item_Color - + # Check if path is clear + for i in range(1, 3): + var check_pos = str(x + i) + "-" + str(y) + if !is_valid_cell(board_flow, check_pos) || !can_move_to_cell(board_flow, check_pos): + return false + var container = board_flow.get_node(check_pos) as PieceContainer + if container.has_piece(): + return false + + var rook_pos = str(x + 3) + "-" + str(y) + if !is_valid_cell(board_flow, rook_pos): + return false + var rook_container = board_flow.get_node(rook_pos) as PieceContainer + if !rook_container.has_piece(): + return false + var rook = rook_container.get_piece() + return rook.name == "Rook" && rook.Castling && rook.Item_Color == self.Item_Color + func check_queenside_castle(board_flow, x: int, y: int) -> bool: - # Check if path is clear - for i in range(1, 4): - var check_pos = str(x - i) + "-" + str(y) - if !is_valid_cell(board_flow, check_pos) || !can_move_to_cell(board_flow, check_pos): - return false - var container = board_flow.get_node(check_pos) as PieceContainer - if container.has_piece(): - return false - - # Check if rook is in position and hasn't moved - var rook_pos = str(x - 4) + "-" + str(y) - if !is_valid_cell(board_flow, rook_pos): - return false - - var rook_container = board_flow.get_node(rook_pos) as PieceContainer - var rook = rook_container.get_piece() - return rook.name == "Rook" && rook.Castling && rook.Item_Color == self.Item_Color + # Check if path is clear + for i in range(1, 4): + var check_pos = str(x - i) + "-" + str(y) + if !is_valid_cell(board_flow, check_pos) || !can_move_to_cell(board_flow, check_pos): + return false + var container = board_flow.get_node(check_pos) as PieceContainer + if container.has_piece(): + return false + + # Check if rook is in position and hasn't moved + var rook_pos = str(x - 4) + "-" + str(y) + if !is_valid_cell(board_flow, rook_pos): + return false + + var rook_container = board_flow.get_node(rook_pos) as PieceContainer + if !rook_container.has_piece(): + return false + var rook = rook_container.get_piece() + return rook.name == "Rook" && rook.Castling && rook.Item_Color == self.Item_Color