CHANGE BRANCH PURPOSE NOW INTEGRATING TILES WITH STOCKFISH

This commit is contained in:
2ManyProjects 2025-02-23 16:15:57 -06:00
parent e90591c306
commit 707d44862a
11 changed files with 222 additions and 466 deletions

View file

@ -16,7 +16,7 @@ function getLogPath() {
return path.join(os.homedir(), 'AppData', 'Roaming', 'ChessBuilder', 'logs'); return path.join(os.homedir(), 'AppData', 'Roaming', 'ChessBuilder', 'logs');
} else { } else {
// macOS: ~/Library/Logs/ChessBuilder // macOS: ~/Library/Logs/ChessBuilder
return path.join(os.homedir(), 'Library', 'Logs', 'ChessBuilder'); return path.join(os.homedir(), 'Library', 'ChessBuilder', 'logs');
} }
} }
@ -69,7 +69,7 @@ let board = null;
let engine = null; let engine = null;
let isReady = false; let isReady = false;
let lastResponse = null let lastResponse = null
const SERVER_WAIT_THRESHOLD = 10 * 60 * 1000; const SERVER_WAIT_THRESHOLD = 2 * 60 * 1000;
const CHECK_INTERVAL = 5000; const CHECK_INTERVAL = 5000;
// Initialize ffish and engine // Initialize ffish and engine
@ -104,6 +104,10 @@ app.use(express.json());
// Health check endpoint // Health check endpoint
app.get('/health', (req, res) => { app.get('/health', (req, res) => {
console.log("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
console.log("@@@@@@@@@@@@@@@@@HEALTH CHECK@@@@@@@@@@@@@@@@@@@@@@")
console.log("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
console.log(JSON.stringify(ffish.variants()))
lastResponse = new Date().getTime() lastResponse = new Date().getTime()
res.json({ res.json({
status: 'ok', status: 'ok',
@ -382,11 +386,12 @@ app.post('/enginemove', async (req, res) => {
const { const {
depth = 15, depth = 15,
movetime = 1000, movetime = 1000,
nodes = null nodes = null,
fen
} = req.body; } = req.body;
try { try {
const fen = board.fen(); // const fen = board.fen();
const analysis = await engine.getAnalysis(fen, { const analysis = await engine.getAnalysis(fen, {
depth, depth,
movetime, movetime,

View file

@ -8,6 +8,9 @@ async function delay(ms) {
async function testServer() { async function testServer() {
try { try {
await axios.post(`${baseURL}/shutdown`);
return
// Test 1: Health check // Test 1: Health check
console.log('\nTest 1: Health Check'); console.log('\nTest 1: Health Check');
const health = await axios.get(`${baseURL}/health`); const health = await axios.get(`${baseURL}/health`);
@ -15,6 +18,14 @@ async function testServer() {
// Wait for engine initialization // Wait for engine initialization
await delay(2000); await delay(2000);
// Test 2: Validate FEN
console.log('\nTest 1.5: Validate MOVE');
const moveVal = await axios.post(`${baseURL}/move`, {
move: '@a6'
});
console.log('MOVE validation:', moveVal);
await delay(2000);
// Test 2: Validate FEN // Test 2: Validate FEN
console.log('\nTest 2: Validate FEN'); console.log('\nTest 2: Validate FEN');

View file

View file

@ -7,6 +7,8 @@ var log_string: String = ""
var running := false var running := false
var server_process_id: int = -50 var server_process_id: int = -50
var request_timer: Timer var request_timer: Timer
var server_pinger: Timer
var ping_http: HTTPRequest
var server_url = "http://localhost:27531" var server_url = "http://localhost:27531"
@ -37,24 +39,39 @@ func write_log(message: String):
file.close() file.close()
func _ready(): func _ready():
# Get the path to the server directory relative to the project
# server_path = OS.get_executable_path().get_base_dir().path_join("Assets/ChessEngines/fairy-chess-server")
# server_path = "res://Assets/ChessEngines/fairy-chess-server"
# server_path = ProjectSettings.globalize_path("res://Assets/ChessEngines/fairy-chess-server")
# if OS.has_feature("editor"):
# server_path = ProjectSettings.globalize_path("res://Assets/ChessEngines/fairy-chess-server")
# else:
# # Running from an exported project.
# # `path` will contain the absolute path to `hello.txt` next to the executable.
# # This is *not* identical to using `ProjectSettings.globalize_path()` with a `res://` path,
# # but is close enough in spirit.
# server_path = OS.get_executable_path().get_base_dir().path_join("Assets/ChessEngines/fairy-chess-server")
server_path = extract_server_files() + "/ChessEngines/fairy-chess-server" server_path = extract_server_files() + "/ChessEngines/fairy-chess-server"
setup_logging() setup_logging()
check_server_files(server_path) check_server_files(server_path)
start_server() start_server()
get_tree().set_auto_accept_quit(false) get_tree().set_auto_accept_quit(false)
setup_server_pinger()
func setup_server_pinger():
server_pinger = Timer.new()
add_child(server_pinger)
server_pinger.wait_time = 60.0
server_pinger.one_shot = false
server_pinger.timeout.connect(self._on_ping_timer_timeout)
ping_http = HTTPRequest.new()
add_child(ping_http)
ping_http.request_completed.connect(self._on_ping_completed)
server_pinger.start()
func _on_ping_timer_timeout():
write_log("Pinging server health endpoint...")
var error = ping_http.request(server_url + "/health")
if error != OK:
write_log("Error sending ping request: " + str(error))
func _on_ping_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray):
if result == HTTPRequest.RESULT_SUCCESS:
write_log("Server ping successful")
else:
write_log("Server ping failed - may need to restart server")
func write_log_dir_contents(path: String): func write_log_dir_contents(path: String):
var dir = DirAccess.open(path) var dir = DirAccess.open(path)
@ -109,18 +126,15 @@ func _notification(what):
if what == NOTIFICATION_WM_CLOSE_REQUEST: if what == NOTIFICATION_WM_CLOSE_REQUEST:
stop_server() stop_server()
get_tree().quit() get_tree().quit()
func start_server() -> bool: func start_server() -> bool:
if running: if running:
return true return true
write_log("Starting chess server... " + server_path) write_log("Starting chess server... " + server_path)
# Make sure we're in the correct directory
var http_request = HTTPRequest.new() var http_request = HTTPRequest.new()
add_child(http_request) add_child(http_request)
# http_request.connect("request_completed", self._on_init_request_completed)
http_request.request_completed.connect(self._on_init_request_completed) http_request.request_completed.connect(self._on_init_request_completed)
request_timer = Timer.new() request_timer = Timer.new()

View file

@ -1,390 +0,0 @@
# Stockfish.gd
extends Node
# References to game objects
var board: Array
var game: ChessGame
# Engine state
var process_id: int = -1
var engine_thread: Thread
var pipe_info: Dictionary
var command_queue: Array = []
var response_queue: Array = []
var mutex: Mutex
var semaphore: Semaphore
var running := false
var engine_path: String = ""
var _cleanup_in_progress := false
var threadTracker: Array = []
var last_file_size = 0
# Game state
var moves: Array = []
var move_time: int = 1000 # in ms
var generated_move: Dictionary = {} # Stores the last generated move
# Piece type mapping
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(boardRef: Array):
board = boardRef
mutex = Mutex.new()
semaphore = Semaphore.new()
func _ready():
game = get_parent() as ChessGame
func _exit_tree():
disconnect_engine()
func _notification(what):
if what == NOTIFICATION_PREDELETE:
cleanup()
func cleanup():
if _cleanup_in_progress:
return
_cleanup_in_progress = true
disconnect_engine()
_cleanup_in_progress = false
func safe_cleanup():
if engine_thread:
if engine_thread.is_alive():
var timeout = 5000 # 5 seconds
var start_time = Time.get_ticks_msec()
while engine_thread.is_alive():
if Time.get_ticks_msec() - start_time > timeout:
printerr("Thread cleanup timeout")
break
OS.delay_msec(10)
var result = engine_thread.wait_to_finish()
if result != OK:
printerr("Failed to cleanup thread: ", result)
engine_thread = null
func connect_to_engine(path: String) -> bool:
if running:
return false
engine_path = path
pipe_info = OS.execute_with_pipe(engine_path, ["uci"])
process_id = pipe_info.get("pid", -1)
if process_id <= 0:
printerr("Failed to start engine process")
return false
running = true
print("PID ", process_id, " Connected to engine: ", engine_path)
# Start communication thread
engine_thread = Thread.new()
engine_thread.start(_engine_thread_function)
# # Initialize UCI mode
# _send_command_wait("uci", "uciok")
# _send_command_wait("isready", "readyok")
# # Initialize with current game state
_send_command("ucinewgame")
_send_command_wait("isready", "readyok")
load_fen(game.getCurrentFen())
return true
func disconnect_engine():
if running:
_send_command("quit")
running = false
# Wait for thread to finish
if engine_thread and engine_thread.is_started():
engine_thread.wait_to_finish()
# Clean up process
if pipe_info:
if pipe_info.get("stdio"):
pipe_info["stdio"].close()
if pipe_info.get("stderr"):
pipe_info["stderr"].close()
if process_id > 0:
OS.kill(process_id)
process_id = -1
print("Disconnected from engine")
func limit_strength_to(elo_value: int):
mutex.lock()
if elo_value != -1: # Using -1 instead of int.MaxValue
_send_command("setoption name UCI_LimitStrength value true")
_send_command("setoption name UCI_Elo value " + str(elo_value))
else:
_send_command("setoption name UCI_LimitStrength value false")
mutex.unlock()
func stop_calculating():
mutex.lock()
_send_command("stop")
mutex.unlock()
func load_fen(fen: String):
moves.clear()
update_position(fen)
func update_position(fen: String):
_send_command("position fen " + fen)
# _send_command_wait("isready", "readyok")
func generateMove(think_time_ms: int = 1000) -> void:
if not running:
return
print("------------=========GENERATE MOVE =========-----------------")
move_time = think_time_ms
# Send position
var command = "position fen " + game.getCurrentFen()
_send_command(command)
# _send_command_wait("isready", "readyok")
# var response = _send_command_wait("isready", "readyok")
# if not "readyok" in response:
# print("Engine not ready after setting position")
# return
# Get move with timeout for thinking
var response = _send_command_wait("go movetime " + str(move_time), "bestmove")
print("Move response: ", response)
# Parse response for bestmove
var lines = response.split("\n")
for line in lines:
if line.begins_with("bestmove"):
var parts = line.split(" ")
if parts.size() >= 4: # Should have "bestmove e2e4 ponder e7e5"
generated_move = {
"move": parts[1],
"ponder": parts[3]
}
print("Generated move: ", generated_move)
return
generated_move = {}
func getGeneratedMove() -> Dictionary:
var move = generated_move.duplicate()
generated_move.clear() # Clear after retrieving
return move
func from_move_to_string(move_data: Dictionary) -> String:
var board_size = len(board)
# Get source and target squares
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
# Convert to algebraic notation
var letters = "abcdefghijklmnopqrstuvwxyz".substr(0, board_size)
var str_move = "%s%d%s%d" % [
letters[source_i],
board_size - source_j,
letters[target_i],
board_size - target_j
]
# Add promotion piece if needed
if move_data.get("flags", "") == "PROMOTION":
str_move += symbol_from_piece_type[move_data.promotion_piece]
return str_move
func send_move(move_data: Dictionary):
var str_move = from_move_to_string(move_data)
moves.append(str_move)
print("move: ", str_move)
# Update engine with the new move
mutex.lock()
var command = "position fen " + game.getCurrentFen()
if moves.size() > 0:
command += " moves " + " ".join(moves)
_send_command(command)
mutex.unlock()
func safe_get_line(stdio: FileAccess) -> Array:
var start_time = Time.get_ticks_msec()
var timeout = 500 # 50ms timeout
if stdio and not stdio.eof_reached():
var thread = Thread.new()
var mutex = Mutex.new()
var done = false
var result = ""
thread.start(func():
# Try to read a chunk of data
var buffer = stdio.get_buffer(1024) # Read up to 1KB at a time
if buffer.size() > 0:
var content = buffer.get_string_from_utf8()
var lines = content.split("\n")
# Get last non-empty line
# var last_line = ""
# for i in range(lines.size() - 1, -1, -1):
# if not lines[i].strip_edges().is_empty():
# last_line = lines[i]
# break
mutex.lock()
# result = last_line
done = true
mutex.unlock()
print("THREAD: ", lines)
return lines
mutex.lock()
done = true
mutex.unlock()
return []
)
# Wait for either completion or timeout
while true:
mutex.lock()
var is_done = done
mutex.unlock()
if not thread.is_alive():
print("DeadThread")
var thread_result = thread.wait_to_finish()
print("******T-RES*********", thread_result)
return [thread_result, null]
# else:
# print("Alive THread Kill ME")
if Time.get_ticks_msec() - start_time > timeout:
break
OS.delay_msec(200)
# Handle thread cleanup and result capture
if thread.is_alive():
threadTracker.push_front(thread)
mutex.lock()
var final_result = [result, null]
mutex.unlock()
return final_result
else:
var thread_result = thread.wait_to_finish()
return [thread_result, null]
return ["", null]
func _engine_thread_function():
print("Engine thread started")
var stdio = pipe_info.get("stdio")
var stderr = pipe_info.get("stderr")
while running:
mutex.lock()
print("CMDS ", command_queue)
if command_queue.size() > 0:
var command = command_queue.pop_front()
mutex.unlock()
if stdio:
stdio.store_line(command)
print("************************Sent command: ", command)
OS.delay_msec(200)
else:
mutex.unlock()
print("safe_get_line Start")
var result = safe_get_line(stdio)
print("safe_get_line End", result)
# var line = result[0]
# if not line.is_empty():
# mutex.lock()
# response_queue.push_back(line)
# mutex.unlock()
# semaphore.post()
# print("Engine response: ", line)
# var line = result[0]
for line in result:
if line and not line.is_empty():
mutex.lock()
response_queue.append_array(line)
mutex.unlock()
semaphore.post()
print("Engine response: ", line)
OS.delay_msec(200)
print("################################left thread###########################")
func _send_command_wait(command: String, expected: String = "") -> String:
# Add command to queue more efficiently
mutex.lock()
command_queue.push_back(command)
mutex.unlock()
var response = ""
var start_time = Time.get_ticks_msec()
var timeout = 5000 # Reduced timeout to 5 seconds
# Use an exponential backoff for delays
var delay_time = 1
var max_delay = 16 # Maximum delay in milliseconds
while running:
var current_time = Time.get_ticks_msec()
if current_time - start_time > timeout:
printerr("Command timeout: ", command)
break
if semaphore.try_wait():
# Reset delay on successful response
delay_time = 1
mutex.lock()
var lines_to_process = response_queue.duplicate()
response_queue.clear()
mutex.unlock()
print("PROCESSING LINES ##################")
print(lines_to_process)
# if lines_to_process.is_empty():
# return
# Process all available lines
for line in lines_to_process:
if not line.is_empty():
print(expected, " =? ", line)
if expected.is_empty() or expected in line:
print("MATCHED LINES ##################", line, ' ', expected)
return line
# print("This should not print if we returned")
else:
# Use exponential backoff for delays
OS.delay_msec(delay_time)
delay_time = mini(delay_time * 2, max_delay)
return response
func _send_command(command: String):
mutex.lock()
command_queue.push_back(command)
mutex.unlock()

View file

@ -34,8 +34,7 @@ func connect_to_engine(_path: String, g: ChessGame) -> bool:
if ServerManager.is_server_running(): if ServerManager.is_server_running():
print("**************SERVER RUNNING ****************") print("**************SERVER RUNNING ****************")
running = true running = true
start_board(); start_game(1350)
load_fen(game.getCurrentFen())
return true return true
await get_tree().create_timer(delay).timeout await get_tree().create_timer(delay).timeout
@ -44,23 +43,16 @@ func connect_to_engine(_path: String, g: ChessGame) -> bool:
return false return false
func start_game(elo: int) -> void:
start_board(elo)
load_fen(game.getCurrentFen())
func disconnect_engine(): func disconnect_engine():
running = false running = false
func limit_strength_to(elo_value: int):
if not running:
return
var headers = ["Content-Type: application/json"]
var body = JSON.stringify({
"options": {
"UCI_LimitStrength": "true",
"UCI_Elo": str(elo_value)
}
})
print(body)
http_request.request(server_url + "/setoption", headers, HTTPClient.METHOD_POST, body)
func stop_calculating(): func stop_calculating():
if not running: if not running:
return return
@ -82,7 +74,7 @@ func load_fen(fen: String):
http_request.request(server_url + "/position", headers, HTTPClient.METHOD_POST, body) http_request.request(server_url + "/position", headers, HTTPClient.METHOD_POST, body)
await http_request.request_completed await http_request.request_completed
func start_board(): func start_board(elo: int):
if not running: if not running:
return return
var headers = ["Content-Type: application/json"] var headers = ["Content-Type: application/json"]
@ -93,7 +85,7 @@ func start_board():
print(body) print(body)
http_request.request(server_url + "/new", headers, HTTPClient.METHOD_POST, body) http_request.request(server_url + "/new", headers, HTTPClient.METHOD_POST, body)
await http_request.request_completed await http_request.request_completed
setElo(1350) setElo(elo)
func _exit_tree(): func _exit_tree():
ServerManager.stop_server() ServerManager.stop_server()
@ -103,12 +95,28 @@ func _exit_tree():
func update_position(fen: String): func update_position(fen: String):
load_fen(fen) 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: func setElo(elo: int = 1350) -> void:
if not running: if not running:
return return
var headers = ["Content-Type: application/json"] var headers = ["Content-Type: application/json"]
var data = { var data = {
"options": [ "options": [
{
"name": "VariantPath",
"value": get_globalDir() + "/Assets" + "/ChessEngines/Fairy-Stockfish/src/variants.ini"
},
{ {
"name": "UCI_LimitStrength", "name": "UCI_LimitStrength",
"value": "true" "value": "true"
@ -116,7 +124,11 @@ func setElo(elo: int = 1350) -> void:
{ {
"name": "UCI_Elo", "name": "UCI_Elo",
"value": str(elo) # Convert int to string "value": str(elo) # Convert int to string
} },
{
"name": "UCI_Variant",
"value": "chessbuilder" # Convert int to string
},
] ]
} }
@ -143,7 +155,8 @@ func generateMove(think_time_ms: int = 1000) -> void:
var headers = ["Content-Type: application/json"] var headers = ["Content-Type: application/json"]
var body = JSON.stringify({ var body = JSON.stringify({
"movetime": think_time_ms, "movetime": think_time_ms,
"depth": 15 "depth": 15,
"fen": str(game.getCurrentFen())
}) })
# Request engine move # Request engine move

View file

@ -72,7 +72,8 @@ func _ready() -> void:
stockfishController.connect_to_engine(stockfishPath, self) stockfishController.connect_to_engine(stockfishPath, self)
# if stockfishController.connect_to_engine(stockfishPath): # if stockfishController.connect_to_engine(stockfishPath):
# stockfishController.limit_strength_to(cpuElo) # stockfishController.limit_strength_to(cpuElo)
func _exit_tree(): func _exit_tree():
stockfishController.disconnect_engine() stockfishController.disconnect_engine()
@ -103,7 +104,44 @@ func initializeCardPreview() -> void:
add_child(cardPreview) 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: func getCurrentFen() -> String:
var fen = "" var fen = ""
@ -115,17 +153,29 @@ func getCurrentFen() -> String:
for x in range(boardXSize): for x in range(boardXSize):
# print("CHECKING ", str(x) + "-" + str(y)) # print("CHECKING ", str(x) + "-" + str(y))
var container = boardContainer.get_node(str(x) + "-" + str(y)) as PieceContainer var container = boardContainer.get_node(str(x) + "-" + str(y)) as PieceContainer
var piece = container.get_piece()
if piece == null: if tileManager.active_tiles.has(str(x) + "-" + str(y)):
emptySquares += 1
else:
if emptySquares > 0: if emptySquares > 0:
fen += str(emptySquares) fen += str(emptySquares)
emptySquares = 0 emptySquares = 0
# Convert piece to FEN notation var tile = tileManager.active_tiles[str(x) + "-" + str(y)]
var fenChar = getPieceFenChar(piece) if tile is WallTile or tile is DoubleWallTile:
fen += fenChar if tile is DoubleWallTile:
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 # Add any remaining empty squares at the end of the rank
if emptySquares > 0: if emptySquares > 0:
fen += str(emptySquares) fen += str(emptySquares)
@ -142,7 +192,12 @@ func getCurrentFen() -> String:
halfMoveClock, halfMoveClock,
moveCount moveCount
] ]
var specialChars = getSpecialTilesFen();
print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
print(specialChars)
print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@")
# fen += specialChars;
currentFen = fen;
return fen return fen
func initializeBoard() -> void: func initializeBoard() -> void:
# Parse FEN to get board dimensions # Parse FEN to get board dimensions

View file

@ -52,12 +52,12 @@ func enter(_previous: String, _data := {}) -> void:
var target_square = move_str.substr(2, 2) # "e4" var target_square = move_str.substr(2, 2) # "e4"
# First select the piece # First select the piece
var source_location = convert_algebraic_to_location(source_square) var source_location = Utils.convert_algebraic_to_location(source_square)
game.selectedNode = source_location game.selectedNode = source_location
print("source_location ", source_location) print("source_location ", source_location)
# Then make the move # Then make the move
var target_location = convert_algebraic_to_location(target_square) var target_location = Utils.convert_algebraic_to_location(target_square)
print("target_location ", target_location) print("target_location ", target_location)
handleMovement(target_location, true) handleMovement(target_location, true)
return return
@ -67,21 +67,6 @@ func exit() -> void:
game.boardContainer.disconnect("tile_pressed", handleMovement) game.boardContainer.disconnect("tile_pressed", handleMovement)
func convert_algebraic_to_location(square: String) -> String:
var file = square[0] # letter (a-h)
var rank = int(square[1]) # number (1-8)
# Convert file letter to number (a=0, b=1, etc)
var file_num = file.unicode_at(0) - 'a'.unicode_at(0)
# Since we're working with black's moves and our board is oriented with white at bottom:
# 1. Flip file: 7 - file_num to mirror horizontally
# 2. Flip rank: 8 - rank to mirror vertically
file_num = file_num
var rank_num = 8 - rank
# Return location in your game's format
return "%d-%d" % [file_num, rank_num]
func handleMovement(location: String, generated: bool = false) -> void: func handleMovement(location: String, generated: bool = false) -> void:
# we need to prevent swapping of focus between peices after the double move process has started # we need to prevent swapping of focus between peices after the double move process has started

View file

@ -98,6 +98,39 @@ func place_random_game_tiles(num_tiles: int = 0) -> void:
available_positions.shuffle() available_positions.shuffle()
var skipNext = false; var skipNext = false;
var wall: Tile
var p = '0-2'
var cntr = board_flow.get_node(p) as PieceContainer
var w = (int(p.split("-")[0]) + int(p.split("-")[1])) % 2 == 0
wall = WallTile.new(cntr, w, -1)
add_tile(p, wall)
p = '1-2'
cntr = board_flow.get_node(p) as PieceContainer
w = (int(p.split("-")[0]) + int(p.split("-")[1])) % 2 == 0
wall = WallTile.new(cntr, w, -1)
add_tile(p, wall)
p = '2-2'
cntr = board_flow.get_node(p) as PieceContainer
w = (int(p.split("-")[0]) + int(p.split("-")[1])) % 2 == 0
wall = WallTile.new(cntr, w, -1)
add_tile(p, wall)
p = '5-2'
cntr = board_flow.get_node(p) as PieceContainer
w = (int(p.split("-")[0]) + int(p.split("-")[1])) % 2 == 0
wall = WallTile.new(cntr, w, -1)
add_tile(p, wall)
p = '6-2'
cntr = board_flow.get_node(p) as PieceContainer
w = (int(p.split("-")[0]) + int(p.split("-")[1])) % 2 == 0
wall = WallTile.new(cntr, w, -1)
add_tile(p, wall)
p = '7-2'
cntr = board_flow.get_node(p) as PieceContainer
w = (int(p.split("-")[0]) + int(p.split("-")[1])) % 2 == 0
wall = WallTile.new(cntr, w, -1)
add_tile(p, wall)
# Skip over an iteration for a paired tile # Skip over an iteration for a paired tile
for i in range(min(num_tiles, available_positions.size())): for i in range(min(num_tiles, available_positions.size())):
if skipNext: if skipNext:
@ -114,16 +147,17 @@ func place_random_game_tiles(num_tiles: int = 0) -> void:
var rng = RandomNumberGenerator.new() var rng = RandomNumberGenerator.new()
rng.randomize() rng.randomize()
var tile_type = rng.randi() % 3 var tile_type = rng.randi() % 3
# tile_type = 4;
# remove the set 2
var tile: Tile var tile: Tile
match tile_type: match tile_type:
0: # Wall tile 0: # Wall tile
# tile = WallTile.new(container, is_white, -1) tile = WallTile.new(container, is_white, -1)
# add_tile(pos, tile) add_tile(pos, tile)
continue continue
1: # Double Wall 1: # Double Wall
# tile = DoubleWallTile.new(container, is_white, -1) tile = DoubleWallTile.new(container, is_white, -1)
# add_tile(pos, tile) add_tile(pos, tile)
continue continue
2: # Portal pair 2: # Portal pair
# Only create portal pair if this isn't the last tile # Only create portal pair if this isn't the last tile

View file

@ -19,7 +19,7 @@ func _init(button: Button, is_white: bool, d: int, id: int, color: Color) -> voi
jumpable = true jumpable = true
func apply_effect(piece: Pawn = null) -> void: func apply_effect(piece: Pawn = null) -> void:
if !piece || !is_effect_active() || !other_portal || !other_portal.is_effect_active(): if !piece || piece == null || !is_effect_active() || !other_portal || !other_portal.is_effect_active():
return return
if last_piece and last_piece.id == piece.id: if last_piece and last_piece.id == piece.id:
return return

View file

@ -23,6 +23,35 @@ static func generate_guid() -> String:
return guid return guid
static func convert_algebraic_to_location(square: String) -> String:
var file = square[0] # letter (a-h)
var rank = int(square[1]) # number (1-8)
# Convert file letter to number (a=0, b=1, etc)
var file_num = file.unicode_at(0) - 'a'.unicode_at(0)
# 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
# Return location in your game's format
return "%d-%d" % [file_num, rank_num]
static func location_to_algebraic(location: String) -> String:
# Convert from "x-y" format to algebraic notation
var coords = location.split("-")
var x = int(coords[0])
var y = int(coords[1])
# Convert x to file letter (0 = 'a', 1 = 'b', etc.)
var file = char(97 + x) # 97 is ASCII 'a'
# Convert y to rank number (flip since our board has 0 at top)
var rank = str(8 - y)
return file + rank
static var LIGHT_CELL = Color(0.5, 0.5, 0.5, 1) static var LIGHT_CELL = Color(0.5, 0.5, 0.5, 1)
static var DARK_CELL = Color(0.2, 0.2, 0.2, 1) static var DARK_CELL = Color(0.2, 0.2, 0.2, 1)