Last active
December 25, 2015 07:34
-
-
Save linrock/6591174 to your computer and use it in GitHub Desktop.
A simple ruby client for the Stockfish chess engine.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'open3' | |
module Stockfish | |
class InvalidCommand < StandardError; end | |
class InvalidOption < StandardError; end | |
class Engine | |
attr_reader :stdin, :stdout, :stderr, :wait_threads, :version, :pid | |
COMMANDS = %w( uci isready setoption ucinewgame position go stop ponderhit quit ) | |
def initialize(bin_path = "/usr/local/bin/stockfish") | |
@stdin, @stdout, @stderr, @wait_threads = Open3.popen3(bin_path) | |
@pid = @wait_threads[:pid] | |
@version = @stdout.readline.strip | |
raise "Not a valid Stockfish binary!" unless @version =~ /^Stockfish/ | |
end | |
def execute(str) | |
command = str.split(" ")[0] | |
@stdin.puts str | |
raise InvalidCommand.new(@stdout.readline.strip) unless COMMANDS.include?(command) | |
output = "" | |
case command | |
when "uci" | |
loop do | |
output << (line = @stdout.readline) | |
break if line =~ /^uciok/ | |
end | |
when "go" | |
loop do | |
output << (line = @stdout.readline) | |
break if line =~ /^bestmove/ | |
end | |
when "setoption" | |
sleep 0.1 | |
raise InvalidOption.new(@stdout.readline.strip) if @stdout.ready? | |
when "isready" | |
output << @stdout.readline | |
end | |
output | |
end | |
def ready? | |
execute("isready").strip == "readyok" | |
end | |
def running? | |
@wait_threads.alive? | |
end | |
def analyze(fen, options) | |
execute "position fen #{fen}" | |
%w( depth movetime nodes ).each do |command| | |
if (x = options[command.to_sym]) | |
return execute "go #{command} #{x}" | |
end | |
end | |
end | |
end | |
end | |
# Example invocations | |
engine = Stockfish::Engine.new | |
position = "rqb2rk1/1p3p2/p1nb1n1p/2pp4/2P4Q/N2P1N2/PP2BPPP/R4RK1 w - - 1 15" | |
puts engine.analyze position, { :nodes => 100000 } | |
puts engine.analyze position, { :movetime => 2000 } | |
puts engine.analyze position, { :depth => 10 } |
This is great! How would I go about just returning the best move with depth of x?
Edit: I solved it myself. Not an elegant solution, but it works.
Add this function within Engine:
def best_move(fen, depth)
best_move = analyze fen, { depth: depth }
best_move.split('bestmove')[-1].split('ponder')[0]
end
and use it like:
chess = Stockfish::Engine.new
position = "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq - 0 1"
puts chess.best_move position, 10
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hey man this seems super cool. The code is a little over my head, but I am really interested in playing around with this file. What exactly should be in the bin_path argument to the initialize method, or in the path "usr/local/bin/stockfish"? I downloaded the Stockfish source code, but I'm not sure what I should be doing with it