254 lines
7 KiB
GDScript
254 lines
7 KiB
GDScript
# StockfishClient.gd
|
|
extends Node
|
|
|
|
var server_url = "http://localhost:27531"
|
|
var http_request: HTTPRequest
|
|
var running := false
|
|
var generated_move: Dictionary = {}
|
|
var move_time: int = 1000
|
|
var game: ChessGame
|
|
|
|
var symbol_from_piece_type := {
|
|
"PAWN": "p", "KNIGHT": "n", "BISHOP": "b",
|
|
"ROOK": "r", "QUEEN": "q", "KING": "k"
|
|
}
|
|
|
|
var piece_type_from_symbol := {
|
|
"p": "PAWN", "n": "KNIGHT", "b": "BISHOP",
|
|
"r": "ROOK", "q": "QUEEN", "k": "KING"
|
|
}
|
|
|
|
func _init():
|
|
print("STARTING SERVER CLIENT")
|
|
http_request = HTTPRequest.new()
|
|
add_child(http_request)
|
|
http_request.request_completed.connect(self._on_request_completed)
|
|
|
|
func connect_to_engine(_path: String, g: ChessGame) -> bool:
|
|
game = g
|
|
# Wait for server to be ready
|
|
var retries = 5
|
|
var delay = 1.0 # seconds
|
|
|
|
while retries > 0:
|
|
if ServerManager.is_server_running():
|
|
print("**************SERVER RUNNING ****************")
|
|
running = true
|
|
start_game(1350)
|
|
return true
|
|
|
|
await get_tree().create_timer(delay).timeout
|
|
retries -= 1
|
|
print("**************ATTEMPTING SERVER CONNECTION****************")
|
|
|
|
return false
|
|
|
|
|
|
func start_game(elo: int) -> void:
|
|
start_board(elo)
|
|
load_fen(game.getCurrentFen())
|
|
|
|
|
|
|
|
func disconnect_engine():
|
|
running = false
|
|
|
|
func stop_calculating():
|
|
if not running:
|
|
return
|
|
# Send stop command to server
|
|
http_request.request(server_url + "/stop", [], HTTPClient.METHOD_POST)
|
|
|
|
func load_fen(fen: String):
|
|
if not running:
|
|
return
|
|
# var http_request = HTTPRequest.new()
|
|
# add_child(http_request)
|
|
# http_request.request_completed.connect(self._on_request_completed)
|
|
var headers = ["Content-Type: application/json"]
|
|
var body = JSON.new().stringify({
|
|
"fen": fen
|
|
})
|
|
print(server_url + "/position")
|
|
print(body)
|
|
|
|
http_request.request(server_url + "/position", headers, HTTPClient.METHOD_POST, body)
|
|
await http_request.request_completed
|
|
func start_board(elo: int):
|
|
if not running:
|
|
return
|
|
var headers = ["Content-Type: application/json"]
|
|
var body = JSON.new().stringify({
|
|
"variant": 'chess'
|
|
})
|
|
print(server_url + "/new")
|
|
print(body)
|
|
http_request.request(server_url + "/new", headers, HTTPClient.METHOD_POST, body)
|
|
await http_request.request_completed
|
|
setElo(elo)
|
|
|
|
func _exit_tree():
|
|
ServerManager.stop_server()
|
|
disconnect_engine();
|
|
|
|
|
|
func update_position(fen: String):
|
|
load_fen(fen)
|
|
|
|
|
|
|
|
func get_globalDir() -> String:
|
|
|
|
if OS.get_name() == "Linux":
|
|
return OS.get_environment("HOME").path_join(".local/share/ChessBuilder")
|
|
elif OS.get_name() == "Windows":
|
|
return OS.get_environment("APPDATA").path_join("Roaming/ChessBuilder")
|
|
else: # macOS
|
|
return OS.get_environment("HOME").path_join("Library/ChessBuilder")
|
|
|
|
|
|
func setElo(elo: int = 1350) -> void:
|
|
if not running:
|
|
return
|
|
var headers = ["Content-Type: application/json"]
|
|
var data = {
|
|
"options": [
|
|
{
|
|
"name": "VariantPath",
|
|
"value": get_globalDir() + "/Assets" + "/ChessEngines/Fairy-Stockfish/src/variants.ini"
|
|
},
|
|
{
|
|
"name": "UCI_LimitStrength",
|
|
"value": "true"
|
|
},
|
|
{
|
|
"name": "UCI_Elo",
|
|
"value": str(elo) # Convert int to string
|
|
},
|
|
{
|
|
"name": "UCI_Variant",
|
|
"value": "chessbuilder" # Convert int to string
|
|
},
|
|
]
|
|
}
|
|
|
|
var body = JSON.new().stringify(data)
|
|
|
|
# Request engine move
|
|
var elo_req = HTTPRequest.new()
|
|
add_child(elo_req)
|
|
elo_req.request_completed.connect(self._on_request_completed)
|
|
elo_req.request(
|
|
server_url + "/setoptions",
|
|
headers,
|
|
HTTPClient.METHOD_POST,
|
|
body
|
|
)
|
|
|
|
func generateMove(think_time_ms: int = 1000) -> void:
|
|
if not running:
|
|
return
|
|
print("&&&&&&&&&&&&&&&GENERATING MOVE&&&&&&&&&&&&&&&&&&&&&&")
|
|
|
|
move_time = think_time_ms
|
|
|
|
var headers = ["Content-Type: application/json"]
|
|
var body = JSON.stringify({
|
|
"movetime": think_time_ms,
|
|
"depth": 15,
|
|
"fen": str(game.getCurrentFen())
|
|
})
|
|
|
|
# Request engine move
|
|
var move_request = HTTPRequest.new()
|
|
add_child(move_request)
|
|
move_request.request_completed.connect(self._on_bestmove_completed)
|
|
var error = move_request.request(
|
|
server_url + "/enginemove",
|
|
headers,
|
|
HTTPClient.METHOD_POST,
|
|
body
|
|
)
|
|
|
|
|
|
|
|
|
|
func getGeneratedMove() -> Dictionary:
|
|
var move = generated_move.duplicate()
|
|
generated_move.clear()
|
|
return move
|
|
|
|
func send_move(move_data: Dictionary):
|
|
if not running:
|
|
return
|
|
|
|
var headers = ["Content-Type: application/json"]
|
|
var move_str = from_move_to_string(move_data)
|
|
var body = JSON.stringify({
|
|
"move": move_str
|
|
})
|
|
|
|
http_request.request(server_url + "/move", headers, HTTPClient.METHOD_POST, body)
|
|
|
|
# Helper functions
|
|
func from_move_to_string(move_data: Dictionary) -> String:
|
|
# Same implementation as original
|
|
var board_size = 8 # Standard chess board
|
|
|
|
var source_i = move_data.source % board_size
|
|
var source_j = move_data.source / board_size
|
|
var target_i = move_data.target % board_size
|
|
var target_j = move_data.target / board_size
|
|
|
|
var letters = "abcdefgh"
|
|
var str_move = "%s%d%s%d" % [
|
|
letters[source_i],
|
|
board_size - source_j,
|
|
letters[target_i],
|
|
board_size - target_j
|
|
]
|
|
|
|
if move_data.get("flags", "") == "PROMOTION":
|
|
str_move += symbol_from_piece_type[move_data.promotion_piece].to_lower()
|
|
|
|
return str_move
|
|
|
|
|
|
|
|
func _on_bestmove_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray):
|
|
print("^^^^^^^^^^^^^^^_on_bestmove_completed^^^^^^^^^^^^^^^^^")
|
|
var json = JSON.new()
|
|
json.parse(body.get_string_from_utf8())
|
|
var response = json.get_data()
|
|
# Will print the user agent string used by the HTTPRequest node (as recognized by httpbin.org).
|
|
print(response)
|
|
if response == null or result != HTTPRequest.RESULT_SUCCESS:
|
|
print("HTTP Request failed")
|
|
return
|
|
|
|
if response.status != "ok":
|
|
print("Server error:", response_code, response,)
|
|
return
|
|
|
|
|
|
generated_move = {
|
|
"move": response.move,
|
|
"ponder": ""
|
|
}
|
|
|
|
|
|
|
|
func _on_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray):
|
|
print("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^")
|
|
var json = JSON.new()
|
|
json.parse(body.get_string_from_utf8())
|
|
var response = json.get_data()
|
|
# Will print the user agent string used by the HTTPRequest node (as recognized by httpbin.org).
|
|
print(response)
|
|
if result != HTTPRequest.RESULT_SUCCESS:
|
|
print("HTTP Request failed")
|
|
return
|
|
|
|
if response.status != "ok":
|
|
print("Server error:", response_code, json.parse(body.get_string_from_utf8()),)
|
|
return
|