Skip to content

Instantly share code, notes, and snippets.

@linrock
Last active December 25, 2015 07:34
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save linrock/6591174 to your computer and use it in GitHub Desktop.
Save linrock/6591174 to your computer and use it in GitHub Desktop.
A simple ruby client for the Stockfish chess engine.
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 }
@JimmyMow
Copy link

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

@kieraneglin
Copy link

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