From 1cce2fe2443d0e327d333f26adffde0d84d1f97b Mon Sep 17 00:00:00 2001 From: 2ManyProjects Date: Fri, 7 Mar 2025 14:04:40 -0600 Subject: [PATCH] fixed stockfish interpretation of smaller and larger boards --- .../ChessEngines/fairy-chess-server/engine.js | 10 +++- .../ChessEngines/fairy-chess-server/index.js | 18 ++++--- Systems/FairyStockfish/StockfishClient.gd | 52 ++++++++++++++++--- Systems/Game/ChessGame.gd | 25 ++++++++- Systems/Game/DeckManagerScreen.gd | 1 + Systems/Game/Map/MapGenerator.gd | 50 +++++++++++++++++- Systems/StateMachine/GameStates/BlackTurn.gd | 2 - Systems/StateMachine/GameStates/Movement.gd | 11 ++-- Utils/Utils.gd | 4 +- 9 files changed, 148 insertions(+), 25 deletions(-) diff --git a/Assets/ChessEngines/fairy-chess-server/engine.js b/Assets/ChessEngines/fairy-chess-server/engine.js index 51d372a..fbf73a4 100644 --- a/Assets/ChessEngines/fairy-chess-server/engine.js +++ b/Assets/ChessEngines/fairy-chess-server/engine.js @@ -108,7 +108,15 @@ class ChessEngine extends EventEmitter { this.engine.stdin.write(cmd + '\n'); } } - + async startPos() { + if (!this.isReady) throw new Error('Engine not ready'); + + this.sendCommand(`position startpos`); + + // Ensure engine is ready after position set + this.sendCommand('isready'); + await new Promise(resolve => this.once('ready', resolve)); + } async setBoardPosition(fen) { if (!this.isReady) throw new Error('Engine not ready'); diff --git a/Assets/ChessEngines/fairy-chess-server/index.js b/Assets/ChessEngines/fairy-chess-server/index.js index 534ee8c..f9889f6 100644 --- a/Assets/ChessEngines/fairy-chess-server/index.js +++ b/Assets/ChessEngines/fairy-chess-server/index.js @@ -94,6 +94,7 @@ ffish.onRuntimeInitialized = async () => { engine.sendCommand('setoption name Hash value 128'); engine.sendCommand('setoption name MultiPV value 1'); engine.sendCommand('setoption name UCI_LimitStrength value true'); + engine.sendCommand('uci'); } } catch (error) { console.log('Initialization error:', error); @@ -209,13 +210,10 @@ app.post('/new', async (req, res) => { }); // Set position endpoint -app.post('/position', (req, res) => { +app.post('/position', async(req, res) => { lastResponse = new Date().getTime() - const { fen, variant = 'chess' } = req.body; + const { fen, variant = 'chess', start } = req.body; - if (!fen) { - return res.status(400).json({ error: 'FEN string required' }); - } try { //we have a lot of funky rules lets not validate @@ -232,7 +230,13 @@ app.post('/position', (req, res) => { // // } // board.setFen(fen); // } - board.setFen(fen); + if(start){ + await engine.startPos() + engine.sendCommand('d'); + }else if(fen){ + board.setFen(fen); + await engine.setBoardPosition(fen) + } res.json({ status: 'ok', @@ -246,7 +250,7 @@ app.post('/position', (req, res) => { moveStack: board.moveStack() }); } catch (error) { - res.status(500).json({ error: error.message }); + res.status(500).json({ error: error.message, poserr: true }); } }); diff --git a/Systems/FairyStockfish/StockfishClient.gd b/Systems/FairyStockfish/StockfishClient.gd index 5aeafb6..62b1ec1 100644 --- a/Systems/FairyStockfish/StockfishClient.gd +++ b/Systems/FairyStockfish/StockfishClient.gd @@ -34,7 +34,7 @@ func connect_to_engine(_path: String, g: ChessGame) -> bool: if ServerManager.is_server_running(): print("**************SERVER RUNNING ****************") running = true - start_game(2100) + # start_game(2100) return true await get_tree().create_timer(delay).timeout @@ -74,7 +74,7 @@ func load_fen(fen: String): http_request.request(server_url + "/position", headers, HTTPClient.METHOD_POST, body) await http_request.request_completed -func start_board(elo: int): +func start_board(elo: int, variant: String = "8x8"): if not running: return var headers = ["Content-Type: application/json"] @@ -85,7 +85,7 @@ func start_board(elo: int): print(body) http_request.request(server_url + "/new", headers, HTTPClient.METHOD_POST, body) await http_request.request_completed - setElo(elo) + setElo(elo, variant) func _exit_tree(): ServerManager.stop_server() @@ -107,7 +107,40 @@ func get_globalDir() -> String: return OS.get_environment("HOME").path_join("Library/ChessBuilder") -func setElo(elo: int = 1350) -> void: +func setVariant(variant: String = "8x8"): + if not running: + return + print("####################################") + print("####################################") + print("chessbuilder" + variant) + print("####################################") + print("####################################") + print("####################################") + var headers = ["Content-Type: application/json"] + var data = { + "options": [ + { + "name": "VariantPath", + "value": get_globalDir() + "/Assets" + "/ChessEngines/Fairy-Stockfish/src/variants.ini" + }, + { + "name": "UCI_Variant", + "value": "chessbuilder" + variant + }, + ] + } + + var body = JSON.new().stringify(data) + + # Request engine move + http_request.request( + server_url + "/setoptions", + headers, + HTTPClient.METHOD_POST, + body + ) + +func setElo(elo: int = 1350, variant: String = "8x8") -> void: if not running: return var headers = ["Content-Type: application/json"] @@ -127,7 +160,7 @@ func setElo(elo: int = 1350) -> void: }, { "name": "UCI_Variant", - "value": "chessbuilder" # Convert int to string + "value": "chessbuilder" + variant }, ] } @@ -144,11 +177,18 @@ func setElo(elo: int = 1350) -> void: HTTPClient.METHOD_POST, body ) + await elo_req.request_completed + http_request.request( + server_url + "/position", + headers, + HTTPClient.METHOD_POST, + JSON.new().stringify({"start": true}) + ) func generateMove(think_time_ms: int = 1000) -> void: if not running: return - print("&&&&&&&&&&&&&&&GENERATING MOVE&&&&&&&&&&&&&&&&&&&&&&") + print("&&&&&&&&&&&&&&&GENERATING MOVE&&&&&&&&&&&&&&&&&&&&&&", str(game.getCurrentFen())) move_time = think_time_ms diff --git a/Systems/Game/ChessGame.gd b/Systems/Game/ChessGame.gd index 013a730..b04000b 100644 --- a/Systems/Game/ChessGame.gd +++ b/Systems/Game/ChessGame.gd @@ -107,6 +107,8 @@ func _on_new_game_requested(options = {}): cpuElo = options.elo if cameraController: cameraController.reset_view() + print("ChessGame FEN ", currentFen) + print("ChessGame DIMENSIONS ", get_board_dimensions(currentFen)) if is_initialized: resetBoard() initializeDeckSystem() @@ -117,8 +119,10 @@ func _on_new_game_requested(options = {}): stateMachine.transitionToNextState(Constants.WHITE_TURN) else: initialize_game_system() - stockfishController.start_board(cpuElo) - + if currentFen: + stockfishController.start_board(cpuElo, get_board_dimensions(currentFen)) + else: + stockfishController.start_board(cpuElo, "8x8") func initialize_game_system(): print("Initializing game system") # Set up basic styles first @@ -896,3 +900,20 @@ func updateHalfMoveClock(fromIdx: int, toIdx: int) -> void: halfMoveClock = 0 else: halfMoveClock += 1 + + + +func get_board_dimensions(fen_string: String) -> String: + var board_part: String = fen_string.split(" ")[0] + var ranks: Array = board_part.split("/") + var height: int = ranks.size() + var width: int = 0 + var first_rank: String = ranks[0] + + for character in first_rank: + if character.is_valid_int(): + width += int(character) + else: + width += 1 + + return str(width) + "x" + str(height) diff --git a/Systems/Game/DeckManagerScreen.gd b/Systems/Game/DeckManagerScreen.gd index e1ff963..8c1c0c5 100644 --- a/Systems/Game/DeckManagerScreen.gd +++ b/Systems/Game/DeckManagerScreen.gd @@ -2,6 +2,7 @@ extends Control class_name DeckManagerScreen signal back_pressed +signal deck_manager_visibility_changed(isvisible) # Node references @onready var deckGrid = $MainContainer/GridScrollContainer/GridContainer diff --git a/Systems/Game/Map/MapGenerator.gd b/Systems/Game/Map/MapGenerator.gd index 18c21b5..5c5a729 100644 --- a/Systems/Game/Map/MapGenerator.gd +++ b/Systems/Game/Map/MapGenerator.gd @@ -12,6 +12,9 @@ var max_nodes_per_level = 4 var positions_per_level = 6 var starting_elo = 1000 var final_elo = 2100 +var current_max_level = 0; +# var level_unit_distribution = ["", "", "", "nkr", "rnkr", "rnkbr", "rnqkbr", "rnqkbnr", "rnbqkbnr", "rbnqknbnr", "rnbnqknbnr", "rnbnqknbnbr", "rbnbnqknbnbr"] +var level_unit_distribution = ["", "", "nkr", "rnkr", "rnkr", "rnkbr", "rnqkbr", "rnqkbnr", "rnbqkbnr", "rbnqknbnr", "rnbnqknbnr", "rnbnqknbnbr", "rbnbnqknbnbr"] var _rng = RandomNumberGenerator.new() var _next_id = 0 @@ -29,7 +32,7 @@ func generate_map(): _next_id = 0 var num_levels = _rng.randi_range(min_levels, max_levels) - + current_max_level = num_levels; var elo_step = float(final_elo - starting_elo) / (num_levels - 1) var start_node = { @@ -307,10 +310,53 @@ func generate_event_data(node): } # "rnbqkbnr1/pppppppp1/9/9/9/9/9/PPPPPPPP1/RNBQKBNR1 w KQkq - 0 1" func generate_chess_data(node): + # level_unit_distribution + # current_max_level + var rng = float(node.level) / int(current_max_level) + var index = map_to_array_index(node.level, 2, current_max_level - 2, 3, level_unit_distribution.size() - 1); + var unit_string = level_unit_distribution[index] + var pawn_string = "" + + for x in unit_string.length(): + pawn_string += "p" + + var height = 6; +# "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR" + var fen = ""; + if node.level > 7 and node.level <= 10: + height = node.level + elif node.level > 10: + height = 10 + for x in height - 2: + if x == 0: + fen += unit_string + "/" + elif x == 1: + fen += pawn_string + "/" + else: + fen += str(unit_string.length()) + "/" + + fen += pawn_string.to_upper() + "/" + unit_string.to_upper() + var fen_ending = " w KQkq - 0 1" + # print("generate_chess_data ", fen + fen_ending) return { "is_escape": node.metadata.is_escape if node.metadata.has("is_escape") else false, - "fen": "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", + "fen": fen + fen_ending, "game_type": "chess", "win_condition": Utils.WinCondition.King, "elo": node.elo, } + + +func map_to_array_index(current_value, min_value, max_value, min_index, max_index): + # Ensure the current value is within bounds + var clamped_value = clamp(current_value, min_value, max_value) + + # Calculate how far along the input range we are (0.0 to 1.0) + var normalized_position = float(clamped_value - min_value) / float(max_value - min_value) + + # Map this to our target index range + var index_range = max_index - min_index + var mapped_index = min_index + round(normalized_position * index_range) + + # Ensure we're returning an integer within the valid array index range + return int(clamp(mapped_index, min_index, max_index)) diff --git a/Systems/StateMachine/GameStates/BlackTurn.gd b/Systems/StateMachine/GameStates/BlackTurn.gd index f15a1b2..a3dd54e 100644 --- a/Systems/StateMachine/GameStates/BlackTurn.gd +++ b/Systems/StateMachine/GameStates/BlackTurn.gd @@ -38,5 +38,3 @@ func _on_state_delay_timeout() -> void: finished.emit(Constants.HAND_SETUP) func exit() -> void: moveTimer.stop() - - diff --git a/Systems/StateMachine/GameStates/Movement.gd b/Systems/StateMachine/GameStates/Movement.gd index 1c0eab2..73e2120 100644 --- a/Systems/StateMachine/GameStates/Movement.gd +++ b/Systems/StateMachine/GameStates/Movement.gd @@ -48,16 +48,19 @@ func enter(_previous: String, _data := {}) -> void: print("GENERATED MOVE ", move) if move: var move_str = move.move # e.g., "e2e4" + if "," in move_str: + move_str = move.move.split(",")[0] + var source_square = move_str.substr(0, 2) # "e2" var target_square = move_str.substr(2, 2) # "e4" # First select the piece - var source_location = Utils.convert_algebraic_to_location(source_square) + var source_location = Utils.convert_algebraic_to_location(source_square, game.boardYSize) game.selectedNode = source_location print("source_location ", source_location) # Then make the move - var target_location = Utils.convert_algebraic_to_location(target_square) + var target_location = Utils.convert_algebraic_to_location(target_square, game.boardYSize) print("target_location ", target_location) handleMovement(target_location, true) return @@ -94,8 +97,10 @@ func handleMovement(location: String, generated: bool = false) -> void: var sourceContainer = game.get_node("Flow/" + game.selectedNode) as PieceContainer var piece = sourceContainer.get_piece() if piece == null: - # print("No Piece") + print("No Piece") return + else: + print("Selected ", piece.name) # print("SColor ", sourcePiece.Item_Color, " tColor ", piece.Item_Color) var piece_id = piece.get_instance_id() diff --git a/Utils/Utils.gd b/Utils/Utils.gd index b52e649..7caa003 100644 --- a/Utils/Utils.gd +++ b/Utils/Utils.gd @@ -23,7 +23,7 @@ static func generate_guid() -> String: return guid -static func convert_algebraic_to_location(square: String) -> String: +static func convert_algebraic_to_location(square: String, maxRank: int) -> String: var file = square[0] # letter (a-h) var rank = int(square[1]) # number (1-8) @@ -33,7 +33,7 @@ static func convert_algebraic_to_location(square: String) -> String: # Since we're working with black's moves and our board is oriented with white at bottom: # 1. Flip rank: 8 - rank to mirror vertically file_num = file_num - var rank_num = 8 - rank + var rank_num = maxRank - rank return "%d-%d" % [file_num, rank_num]