stockfish for godot
-rw-r--r--Main.gd2
-rw-r--r--README.md2
-rw-r--r--addons/stockfish.gd/README.md2
-rw-r--r--addons/stockfish.gd/load.js1
-rw-r--r--addons/stockfish.gd/package.json2
-rw-r--r--addons/stockfish.gd/platform/js_stockfish.gd27
-rw-r--r--addons/stockfish.gd/stockfish_loader.gd119
-rw-r--r--addons/stockfish.gd/stockfish_wrapper.gd93
-rwxr-xr-xexport_html.sh2
-rw-r--r--project.godot6
10 files changed, 134 insertions, 122 deletions
diff --git a/Main.gd b/Main.gd
index bf64f0e..2a378f4 100644
--- a/Main.gd
+++ b/Main.gd
@@ -1,6 +1,6 @@
extends Node
-var fish: StockfishLoader.Stockfish
+var fish: Stockfish
func _ready() -> void:
diff --git a/README.md b/README.md
index 1c1ab37..a7beddb 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
```gdscript
extends Node
-var fish: StockfishLoader.Stockfish
+var fish: Stockfish
func _ready() -> void:
diff --git a/addons/stockfish.gd/README.md b/addons/stockfish.gd/README.md
index 2d02656..9d4e23c 100644
--- a/addons/stockfish.gd/README.md
+++ b/addons/stockfish.gd/README.md
@@ -5,7 +5,7 @@
```gdscript
extends Node
-var fish: StockfishLoader.Stockfish
+var fish: Stockfish
func _ready() -> void:
diff --git a/addons/stockfish.gd/load.js b/addons/stockfish.gd/load.js
deleted file mode 100644
index 16961c5..0000000
--- a/addons/stockfish.gd/load.js
+++ /dev/null
@@ -1 +0,0 @@
-const dl=(url,onFinishDownload)=>{fetch(url).then(response=>response.arrayBuffer()).then(data=>onFinishDownload(data))};window.stockfishCommand=function(command){window.stockfish.postMessage(command)};const loadStockfish=async params=>{return await Stockfish(params)};const onFinishDownload=data=>{if(!data){window.stockfish_failed_load();return}loadStockfish({wasmBinary:data}).then(_stockfish=>{window.stockfish=_stockfish;window.stockfish.addMessageListener(line=>window.stockfish_data_recieved(line))}).catch(e=>{window.stockfish_failed_load();throw e})};dl("./lib/stockfish.wasm",onFinishDownload);
diff --git a/addons/stockfish.gd/package.json b/addons/stockfish.gd/package.json
index 5ccda0c..44bc690 100644
--- a/addons/stockfish.gd/package.json
+++ b/addons/stockfish.gd/package.json
@@ -1,6 +1,6 @@
{
"name": "@bendn/stockfish.gd",
- "version": "1.2.6",
+ "version": "2.0.0",
"description": "godot stockfish",
"main": "stockfish_loader.gd",
"scripts": {
diff --git a/addons/stockfish.gd/platform/js_stockfish.gd b/addons/stockfish.gd/platform/js_stockfish.gd
new file mode 100644
index 0000000..3958690
--- /dev/null
+++ b/addons/stockfish.gd/platform/js_stockfish.gd
@@ -0,0 +1,27 @@
+extends Stockfish
+
+const loader_code := "const dl=(url,onFinishDownload)=>{fetch(url).then(response=>response.arrayBuffer()).then(data=>onFinishDownload(data))};window.stockfishCommand=function(command){window.stockfish.postMessage(command)};const loadStockfish=async params=>{return await Stockfish(params)};const onFinishDownload=data=>{if(!data){window.stockfish_failed_load();return}loadStockfish({wasmBinary:data}).then(_stockfish=>{window.stockfish=_stockfish;window.stockfish.addMessageListener(line=>window.stockfish_data_recieved(line))}).catch(e=>{window.stockfish_failed_load();throw e})};if(!window.stockfish)dl('./lib/stockfish.wasm',onFinishDownload);"
+
+var data_recieved_callback := JavaScript.create_callback(self, "data_recieved")
+var load_failed_callback := JavaScript.create_callback(self, "load_failed")
+
+func _init() -> void:
+ sent_isready = true
+ JavaScript.get_interface("window").stockfish_data_recieved = data_recieved_callback
+ JavaScript.get_interface("window").stockfish_failed_load = load_failed_callback
+
+func _send_line(cmd: String) -> void:
+ JavaScript.eval("window.stockfishCommand('%s')" % cmd)
+
+# js callback arguments are in arrays. i guess its so that you can call functions with less args then they want?
+func data_recieved(data: Array) -> void:
+ emit_signal("line_recieved", data[0])
+
+# if _data is omitted, it will not work
+func load_failed(_data: Array) -> void:
+ emit_signal("load_failed")
+ printerr("load failed")
+
+func kill()->void:
+ .kill()
+ JavaScript.eval("window.stockfish = undefined") \ No newline at end of file
diff --git a/addons/stockfish.gd/stockfish_loader.gd b/addons/stockfish.gd/stockfish_loader.gd
index c36d159..b399893 100644
--- a/addons/stockfish.gd/stockfish_loader.gd
+++ b/addons/stockfish.gd/stockfish_loader.gd
@@ -1,16 +1,15 @@
extends Reference
class_name StockfishLoader
+const JSStockfish = preload("./platform/js_stockfish.gd")
+
func load_stockfish() -> Stockfish:
if not is_supported():
push_error("Platform not supported")
return null
if OS.has_feature("JavaScript"):
- var f = File.new()
- f.open("res://addons/stockfish.gd/load.js", File.READ)
- JavaScript.eval(f.get_as_text())
- f.close()
+ JavaScript.eval(JSStockfish.loader_code)
return JSStockfish.new()
return null
@@ -20,115 +19,3 @@ func is_supported() -> bool:
var has_wasm_buffer_atomics := "function s() {if(typeof WebAssembly!=='object')return false;const source=Uint8Array.from([0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,7,8,1,4,116,101,115,116,0,0,10,15,1,13,0,65,0,253,17,65,0,253,17,253,186,1,11]);if(typeof WebAssembly.validate!=='function'||!WebAssembly.validate(source))return false;if(typeof Atomics!=='object')return false;if(typeof SharedArrayBuffer!=='function')return false;return true}; s()"
return JavaScript.eval(has_wasm_buffer_atomics)
return false
-
-
-class Stockfish:
- extends Reference
-
- var game: Chess setget set_game
- var sent_isready := false
- var engine_ready := false
- var call_queue := PoolStringArray()
- var searching_bestmove := false
-
- signal engine_ready
- signal line_recieved
- signal load_failed
- signal bestmove
-
- func send_line(cmd: String) -> void:
- if not engine_ready:
- call_queue.append(cmd)
- return
- dbg_prints("%s --> stockfish" % cmd)
- _send_line(cmd)
-
- # @override
- func _send_line(cmd: String) -> void:
- pass
-
- func _init() -> void:
- connect("line_recieved", self, "_line_recieved")
- connect("engine_ready", self, "_engine_ready")
-
- func dbg_prints(a1 := "", a2 := "") -> void:
- if OS.is_debug_build():
- prints(a1, a2)
-
- func _engine_ready() -> void:
- engine_ready = true
- for call in call_queue:
- send_line(call)
- call_queue.resize(0)
-
- func set_game(new_game: Chess) -> void:
- game = new_game
- send_line("ucinewgame")
- _position()
-
- func _position():
- var command := PoolStringArray(["position", "startpos"])
- if game.__history:
- command.append("moves")
- for history in game.__history:
- command.append(Chess.move_to_uci(history.move))
-
- send_line(command.join(" "))
-
- func _line_recieved(line: String) -> void:
- if line.begins_with("info "):
- dbg_prints("(stockfish)", line)
- elif searching_bestmove and line.begins_with("bestmove "):
- searching_bestmove = false
- parse_bestmove(line.split(" ", true, 1)[1])
- elif (sent_isready) && (line == "readyok" || line.begins_with("Stockfish [commit: ")):
- sent_isready = false
- emit_signal("engine_ready")
- else:
- push_error("unexpected output: %s" % line)
-
- func parse_bestmove(args: String) -> void:
- var tokens = args.split(" ")
- if tokens and not tokens[0] in ["(none)", "NULL"]:
- if game.move(tokens[0]):
- var bm = game.undo()
- emit_signal("bestmove", bm)
- return
- emit_signal("bestmove", null)
-
- func go(depth: int = 15) -> void:
- if searching_bestmove:
- push_error("already searching. did you mean `stop()`?")
- return
- searching_bestmove = true
- var command := PoolStringArray(["go"])
- command.append("depth")
- command.append(str(depth))
- send_line(command.join(" "))
-
- func stop():
- send_line("stop")
-
-
-class JSStockfish:
- extends Stockfish
-
- var data_recieved_callback := JavaScript.create_callback(self, "data_recieved")
- var load_failed_callback := JavaScript.create_callback(self, "load_failed")
-
- func _init() -> void:
- sent_isready = true
- JavaScript.get_interface("window").stockfish_data_recieved = data_recieved_callback
- JavaScript.get_interface("window").stockfish_failed_load = load_failed_callback
-
- func _send_line(cmd: String) -> void:
- JavaScript.eval("window.stockfishCommand('%s')" % cmd)
-
- # js callback arguments are in arrays. i guess its so that you can call functions with less args then they want?
- func data_recieved(data: Array) -> void:
- emit_signal("line_recieved", data[0])
-
- # if _data is omitted, it will not work
- func load_failed(_data: Array) -> void:
- emit_signal("load_failed")
- printerr("load failed")
diff --git a/addons/stockfish.gd/stockfish_wrapper.gd b/addons/stockfish.gd/stockfish_wrapper.gd
new file mode 100644
index 0000000..0c94096
--- /dev/null
+++ b/addons/stockfish.gd/stockfish_wrapper.gd
@@ -0,0 +1,93 @@
+
+class_name Stockfish
+extends Reference
+
+var game: Chess setget set_game
+var sent_isready := false
+var engine_ready := false
+var call_queue := PoolStringArray()
+var searching_bestmove := false
+
+signal engine_ready
+signal line_recieved
+signal load_failed
+signal bestmove
+
+func send_line(cmd: String) -> void:
+ if not engine_ready:
+ call_queue.append(cmd)
+ return
+ dbg_prints("%s --> stockfish" % cmd)
+ _send_line(cmd)
+
+# @override
+func _send_line(cmd: String) -> void:
+ pass
+
+func _init() -> void:
+ connect("line_recieved", self, "_line_recieved")
+ connect("engine_ready", self, "_engine_ready")
+
+func dbg_prints(a1 := "", a2 := "") -> void:
+ if OS.is_debug_build():
+ prints(a1, a2)
+
+func _engine_ready() -> void:
+ engine_ready = true
+ for call in call_queue:
+ send_line(call)
+ call_queue.resize(0)
+
+func set_game(new_game: Chess) -> void:
+ game = new_game
+ send_line("ucinewgame")
+ _position()
+
+func _position():
+ var command := PoolStringArray(["position", "startpos"])
+ if game.__history:
+ command.append("moves")
+ for history in game.__history:
+ command.append(Chess.move_to_uci(history.move))
+
+ send_line(command.join(" "))
+
+func _line_recieved(line: String) -> void:
+ if line.begins_with("info "):
+ dbg_prints("(stockfish)", line)
+ elif searching_bestmove and line.begins_with("bestmove "):
+ searching_bestmove = false
+ parse_bestmove(line.split(" ", true, 1)[1])
+ elif (sent_isready) && (line == "readyok" || line.begins_with("Stockfish [commit: ")):
+ sent_isready = false
+ emit_signal("engine_ready")
+ else:
+ push_error("unexpected output: %s" % line)
+
+func parse_bestmove(args: String) -> void:
+ var tokens = args.split(" ")
+ if tokens and not tokens[0] in ["(none)", "NULL"]:
+ if game.move(tokens[0]):
+ var bm = game.undo()
+ emit_signal("bestmove", bm)
+ return
+ emit_signal("bestmove", null)
+
+func go(depth: int = 15) -> void:
+ if searching_bestmove:
+ push_error("already searching. did you mean `stop()`?")
+ return
+ searching_bestmove = true
+ var command := PoolStringArray(["go"])
+ command.append("depth")
+ command.append(str(depth))
+ send_line(command.join(" "))
+
+func stop() -> void:
+ send_line("stop")
+
+
+func kill() -> void:
+ send_line("quit")
+ engine_ready = false # stop any calls from being sent
+ # can not free self. will crash \ No newline at end of file
diff --git a/export_html.sh b/export_html.sh
index 6fb369d..a58e9e2 100755
--- a/export_html.sh
+++ b/export_html.sh
@@ -17,7 +17,7 @@ function install_libs() {
[[ -d exports ]] && rm -rf exports
mkdir exports
-[[ -f web/load.js ]] && uglifyjs web/load.js >addons/stockfish.gd/load.js
+
godot --no-window --export "HTML5" exports/index.html
cd exports
install_libs
diff --git a/project.godot b/project.godot
index 4f3a864..510b6b9 100644
--- a/project.godot
+++ b/project.godot
@@ -25,6 +25,11 @@ _global_script_classes=[ {
"path": "res://addons/stockfish.gd/pgn.gd"
}, {
"base": "Reference",
+"class": "Stockfish",
+"language": "GDScript",
+"path": "res://addons/stockfish.gd/stockfish_wrapper.gd"
+}, {
+"base": "Reference",
"class": "StockfishLoader",
"language": "GDScript",
"path": "res://addons/stockfish.gd/stockfish_loader.gd"
@@ -33,6 +38,7 @@ _global_script_class_icons={
"Chess": "",
"FEN": "",
"PGN": "",
+"Stockfish": "",
"StockfishLoader": ""
}