Help us understand the problem. What is going on with this article?

homebrewを使ってMacに将棋ソフトをお手軽インストールできるリポジトリを作ってみた

背景

 2017年に行われた将棋の電王vs叡王の対局では、Ponanza(当時電王)が佐藤天彦九段(当時名人・叡王)を2連勝で降し、将棋においてコンピューターソフトがトップ棋士をも凌駕する実力をつけたことが広く認知された。これ以降、将棋ソフトは人間と対局することよりも、将棋の研究ツールとして使われたり、評価値というわかりやすい観戦ポイントを視聴者に提供するなど、人間をサポートする利用法がクローズアップされている(と個人的には思う)。AIが人間を超えたことによって、人間の仕事や楽しみが奪われるのではなく、むしろAIが人間の仕事の質を高めたり、新たな楽しみを提供してくれている具体例の一つと言って良いだろう。

 さて、そんな強くて頼れる将棋ソフトなのだが、開発環境は概ねWindowsに偏っていて、LinuxやmacOSでは利用するためのハードルが高いという現状がある。パソコン市場でのOSのシェアを鑑みれば致し方ないことかもしれないが、Macユーザーとしては少々寂しい環境である。ただ、そんな中でも、ネット上で探すと自力でソースコードからコンパイルして、MacBook Proで将棋ソフトを使っているという方々がちらほらおられ、大いに感銘を受けた。私はアマチュア四段程度の棋力(81道場のレート1900〜2000台)だが、Macユーザーということもあり、これまで積極的に将棋ソフトを使おうとは思ってこなかったのだが、仕事柄CやC++で書かれたプログラムをコンパイルするという作業にはわりと慣れているため、この分野で何か貢献できることがあるんじゃないかと思った。

 ネット上にはhomebrewを使ってgccをインストールし、将棋ソフトを自力コンパイルする手順を丁寧に紹介したブログ記事が散見される。しかし、それでもコマンドラインに触ったことのない一般の方がその手順を踏襲するのはそれなりに難しいようで、「うまくできません」といったコメントもしばしば見られる。おそらく、ユーザーごとの微妙な環境の違いも原因かもしれない。そこで私が思ったのは、「どうせhomebrewを使うなら、専用のリポジトリを作ればいいんじゃね?」だった。そう、homebrewにはtapという便利な機能があって、公式リポジトリ以外の第三者が提供しているリポジトリを取り込んで、そこで提供されているformulaに従って、パッケージをインストールできるのだ。なので、将棋ソフトのhomebrewリポジトリがあれば、どのくらいいるかはわからないがMacユーザーの将棋ファンが喜んでくれるのではなかろうかと考えたわけである。

homebrewリポジトリの作成

 homebrewのリポジトリ本体を作るのは非常に簡単である。既にGitHubアカウントを持っているなら、「homebrew-〇〇」という名前のリポジトリをGitHub上に新たに作れば良いだけだ。homebrewがインストールされている環境なら、次のコマンドを叩いてもらうだけでこの自作リポジトリを取り込んでもらえる。

$ brew tap ユーザー名/〇〇

 今回は「homebrew-shogi」としたので、お手元のMacにhomebrewインストール後、

$ brew tap hikoyu/shogi

と打ってもらうと、私が作った将棋ソフトのリポジトリを使えるようになる。

formulaひな形の作成

 個々のパッケージのformulaは、先ほど作ったリポジトリの中にFormula/というディレクトリを作り、その中に個々のrubyスクリプトとして配置する。ただし、これも一から自分で書く必要はなく、次のコマンドでひな形を作ることができる。

$ brew create URL

URLの部分にはインストールしたいパッケージのアーカイブのダウンロード元などを書く。例えば、2019年の第29回世界コンピュータ将棋選手権で優勝した「やねうら王」(の思考エンジン)をインストールするformulaを作るのであれば、

$ brew create https://github.com/yaneurao/YaneuraOu/archive/V4.88.tar.gz

と打ってみる。すると、こんなひな形が作られる。

yaneuraou.rb
# Documentation: https://docs.brew.sh/Formula-Cookbook
#                https://rubydoc.brew.sh/Formula
# PLEASE REMOVE ALL GENERATED COMMENTS BEFORE SUBMITTING YOUR PULL REQUEST!
class Yaneuraou < Formula
  desc "YaneuraOu is the World's Strongest Shogi engine(AI player) , WCSC29 1st winner , educationa$
  homepage ""
  url "https://github.com/yaneurao/YaneuraOu/archive/V4.88.tar.gz"
  sha256 "a55b139290451777db20b41dd90b86f15c8600f7f991e8740b38e0b7debac5e0"

  # depends_on "cmake" => :build

  def install
    # ENV.deparallelize  # if your formula fails when building in parallel
    # Remove unrecognized options if warned by configure
    system "./configure", "--disable-debug",
                          "--disable-dependency-tracking",
                          "--disable-silent-rules",
                          "--prefix=#{prefix}"
    # system "cmake", ".", *std_cmake_args
  end

  test do
    # `test do` will create, run in and delete a temporary directory.
    #
    # This test will fail and we won't accept that! For Homebrew/homebrew-core
    # this will need to be a test that verifies the functionality of the
    # software. Run the test with `brew test YaneuraOu`. Options passed
    # to `brew install` such as `--HEAD` also need to be provided to `brew test`.
    #
    # The installed folder is not in the path, so use the entire path to any
    # executables being tested: `system "#{bin}/program", "do", "something"`.
    system "false"
  end
end

このrubyスクリプトは通常、/usr/local/Library/Taps/homebrew/homebrew-core/Formula/内に作られるので、これを自分のリポジトリに移動し、あとは編集していくだけ。のはずだが、ここで重大な問題があった。私はrubyスクリプトを書いたことがないのである。まあ、systemコマンドがあるということは、シェルで好きに書けるということだろうから、何とかなるかなくらいの感覚で、他のformulaの見よう見まねで書いていこうと思った。

「やねうら王」のコンパイル

 formulaの編集に入る前に、少なくとも自分の環境ではコンパイルの手順が完成されていなくてはいけない。そこで、まずは先述の「やねうら王」をコンパイルしてみることにした。

 私のMac環境は以下の通り。
MacBook Air (Mid2012) Intel Core-i7 3667U, DDR3 8GB
macOS 10.14.6 (Mojave)
Xcode 11.3.1

 「やねうら王」はソースコードのアーカイブを本家のGitHubからダウンロードし、展開後、source/ディレクトリ内でmakeするだけでコンパイルできるのだが、計算機環境によってはmakefileconfig.hを一部編集する必要がある模様。編集するのは主に使用するSIMD拡張命令(SSEとかAVXなど)に関する部分。この辺は後でformulaに記述する際、少し工夫しないといけないが、私のMacBook AirはMid2012年モデル(Ivy Bridge)なので、ここで使えるのはSSE42まで(AVX2は使えない)ということで、そのようにmakefileconfig.hを編集した。余談だが、Ivy BridgeでもAVX(AVX2ではない)は使えるのだが、これが採用されていないのは256ビット幅の整数演算命令セットを持たないからだろうと推測される。

# ビルドターゲット
# normal     : 通常使用用
# evallearn  : 教師局面からの学習用
# tournament : 大会で使う用
# gensfen    : 教師生成用(現状、非公開)


# やねうら王の使いたいエディションを指定する
# YANEURAOU_ENGINE_NNUE       : NNUE型評価関数(halfKP256),標準NNUE型
# YANEURAOU_ENGINE_NNUE_KP256 : NNUE型評価関数(KP256)
# YANEURAOU_ENGINE_KPPT       : KPPT型評価関数
# YANEURAOU_ENGINE_KPP_KKPT   : KPP_KKPT型評価関数
# YANEURAOU_ENGINE_MATERIAL   : 駒得のみの評価関数
# MATE_ENGINE                 : 詰将棋エンジン
# USER_ENGINE                 : USER定義エンジン

YANEURAOU_EDITION = YANEURAOU_ENGINE_NNUE
#YANEURAOU_EDITION = YANEURAOU_ENGINE_NNUE_KP256
#YANEURAOU_EDITION = YANEURAOU_ENGINE_KPPT
#YANEURAOU_EDITION = YANEURAOU_ENGINE_KPP_KKPT
#YANEURAOU_EDITION = YANEURAOU_ENGINE_MATERIAL
#YANEURAOU_EDITION = MATE_ENGINE
#YANEURAOU_EDITION = USER_ENGINE

# ターゲットCPU
# 使えるCPU拡張命令を指定する。
# ARM系ならOTHERを指定する。
# 32bit環境用はNO_SSE

#TARGET_CPU = AVX512
#TARGET_CPU = AVX2
TARGET_CPU = SSE42
#TARGET_CPU = SSE41
#TARGET_CPU = SSE2
#TARGET_CPU = NO_SSE
#TARGET_CPU = OTHER


# デバッガーを使用するか
DEBUG = OFF
#DEBUG = ON


# clangでコンパイルしたほうがgccより数%速いっぽい。
COMPILER = g++
#COMPILER = clang++

#(以下省略)
config.h
#(前省略)

// --------------------
// コンパイル時設定
// --------------------

// --- ターゲットCPUの選択

#if !defined(USE_MAKEFILE)

// USE_AVX512 : AVX-512(サーバー向けSkylake以降)でサポートされた命令を使うか。
// USE_AVX2   : AVX2(Haswell以降)でサポートされた命令を使うか。pextなど。
// USE_SSE42  : SSE4.2でサポートされた命令を使うか。popcnt命令など。
// USE_SSE41  : SSE4.1でサポートされた命令を使うか。_mm_testz_si128など。
// USE_SSE2   : SSE2  でサポートされた命令を使うか。
// NO_SSE     : SSEは使用しない。
// (Windowsの64bit環境だと自動的にSSE2は使えるはず)
// noSSE ⊂ SSE2 ⊂ SSE4.1 ⊂ SSE4.2 ⊂ AVX2 ⊂  AVX-512

// Visual Studioのプロジェクト設定で「構成のプロパティ」→「C / C++」→「コード生成」→「拡張命令セッ $
// のところの設定の変更も忘れずに。

// ターゲットCPUのところだけdefineしてください。(残りは自動的にdefineされます。)

//#define USE_AVX512
//#define USE_AVX2
#define USE_SSE42
//#define USE_SSE41
//#define USE_SSE2
//#define NO_SSE

#else

#(以下省略)

なお、makefileでコンパイラの設定がclang++になっていたところ、g++に変更しているが、これは近年のmacOSではあまり意味がない(と思っていたのだが、そうでもなさそうなので後述する)。macOSの/usr/bin/g++は結局のところ、clang++の別名でしかないからである。

$ /usr/bin/g++ -v
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple clang version 11.0.0 (clang-1100.0.33.17)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

何はともあれ、makeしてみる。

$ make
(途中省略)
3 warnings generated.
g++ -o YaneuraOu-by-gcc ../obj/main.o ../obj/types.o ../obj/bitboard.o ../obj/misc.o ../obj/movegen.o ../obj/position.o ../obj/usi.o ../obj/usi_option.o ../obj/thread.o ../obj/tt.o ../obj/movepick.o ../obj/timeman.o ../obj/extra/book/apery_book.o ../obj/extra/book/book.o ../obj/extra/book/makebook2019.o ../obj/extra/bitop.o ../obj/extra/long_effect.o ../obj/extra/mate/mate1ply_with_effect.o ../obj/extra/mate/mate1ply_without_effect.o ../obj/extra/mate/mate_n_ply.o ../obj/extra/benchmark.o ../obj/extra/test_cmd.o ../obj/extra/see.o ../obj/extra/sfen_packer.o ../obj/extra/kif_converter/kif_convert_tools.o ../obj/eval/evaluate_bona_piece.o ../obj/eval/evaluate.o ../obj/eval/evaluate_io.o ../obj/eval/evaluate_mir_inv_tools.o ../obj/learn/learner.o ../obj/learn/learning_tools.o ../obj/learn/multi_think.o ../obj/eval/nnue/evaluate_nnue.o ../obj/eval/nnue/evaluate_nnue_learner.o ../obj/eval/nnue/nnue_test_command.o ../obj/eval/nnue/features/k.o ../obj/eval/nnue/features/p.o ../obj/eval/nnue/features/half_kp.o ../obj/eval/nnue/features/half_relative_kp.o ../obj/engine/yaneuraou-engine/yaneuraou-search.o  -Wl,-s -lpthread -v -std=c++14 -fno-exceptions -fno-rtti -Wextra -Ofast -MMD -MP -fpermissive -DNDEBUG -D_LINUX -DUNICODE -DNO_EXCEPTIONS -DUSE_SSE42 -msse4.2 -march=corei7 -DUSE_MAKEFILE -DYANEURAOU_ENGINE_NNUE
Apple clang version 11.0.0 (clang-1100.0.33.17)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
(以下省略)

つらつらとメッセージが流れて、無事エラーもなくコンパイルできたらしい。早速、動作確認してみる。

$ echo 'usi' | ./YaneuraOu-by-gcc 
id name YaneuraOu NNUE 4.88 64SSE4.2
id author by yaneurao
(途中省略)
usiok

最後にちゃんとusiokのメッセージが出たので、正常に動作しているようだ。

formulaの編集

 とりあえず、自分の環境では「やねうら王」のコンパイルができたので、これを基に手順をformulaに記述していく。「やねうら王」のコンパイルで重要だったポイントはmakefileconfig.hの編集だが、これはsedで対応する。ただし、macOSではデフォルトのsedgnu-sedではないので、-iオプションによるファイル上書きができなかった。そこでgnu-sedを依存パッケージとしてインストールさせるようにformulaを記述した。最終的に出来上がったrubyスクリプトはこんな感じ。

yaneuraou.rb
# Documentation: https://docs.brew.sh/Formula-Cookbook
#                https://rubydoc.brew.sh/Formula
# PLEASE REMOVE ALL GENERATED COMMENTS BEFORE SUBMITTING YOUR PULL REQUEST!
class Yaneuraou < Formula
  desc "YaneuraOu is the World's Strongest Shogi engine(AI player) , WCSC29 1st winner , educational and USI compliant engine."
  homepage "http://yaneuraou.yaneu.com"
  url "https://github.com/yaneurao/YaneuraOu/archive/V4.88.tar.gz"
  version "4.88"
  sha256 "a55b139290451777db20b41dd90b86f15c8600f7f991e8740b38e0b7debac5e0"

  depends_on "gnu-sed"

  def install
    # ENV.deparallelize  # if your formula fails when building in parallel

    system "gsed -i -e \"s,#define USE_AVX2,//#define USE_AVX2,\" source/config.h"
    system "gsed -i -e \"s,//#define NO_SSE,#define NO_SSE,\" source/config.h" if Hardware::CPU.is_32_bit?
    system "gsed -i -e \"s,//#define USE_SSE2,#define USE_SSE2,\" source/config.h" if Hardware::CPU.is_64_bit?
    system "gsed -i -e \"s,//#define USE_SSE41,#define USE_SSE41,\" -e \"s,#define USE_SSE2,//#define USE_SSE2,\" source/config.h" if Hardware::CPU.sse4?
    system "gsed -i -e \"s,//#define USE_SSE42,#define USE_SSE42,\" -e \"s,#define USE_SSE41,//#define USE_SSE41,\" source/config.h" if Hardware::CPU.sse4_2?
    system "gsed -i -e \"s,//#define USE_AVX2,#define USE_AVX2,\" -e \"s,#define USE_SSE42,//#define USE_SSE42,\" source/config.h" if Hardware::CPU.avx2?

    system "gsed -i -e \"s,#COMPILER = g++,COMPILER = g++,\" -e \"s,COMPILER = clang++,#COMPILER = clang++,\" source/Makefile"

    system "gsed -i -e \"s,TARGET_CPU = AVX2,#TARGET_CPU = AVX2,\" source/Makefile"
    system "gsed -i -e \"s,#TARGET_CPU = NO_SSE,TARGET_CPU = NO_SSE,\" source/Makefile" if Hardware::CPU.is_32_bit?
    system "gsed -i -e \"s,#TARGET_CPU = SSE2,TARGET_CPU = SSE2,\" source/Makefile" if Hardware::CPU.is_64_bit?
    system "gsed -i -e \"s,#TARGET_CPU = SSE41,TARGET_CPU = SSE41,\" -e \"s,TARGET_CPU = SSE2,#TARGET_CPU = SSE2,\" source/Makefile" if Hardware::CPU.sse4?
    system "gsed -i -e \"s,#TARGET_CPU = SSE42,TARGET_CPU = SSE42,\" -e \"s,TARGET_CPU = SSE41,#TARGET_CPU = SSE41,\" source/Makefile" if Hardware::CPU.sse4_2?
    system "gsed -i -e \"s,#TARGET_CPU = AVX2,TARGET_CPU = AVX2,\" -e \"s,TARGET_CPU = SSE42,#TARGET_CPU = SSE42,\" source/Makefile" if Hardware::CPU.avx2?

    system "make -C source"
    system "mv source/YaneuraOu-by-gcc YaneuraOu_NNUE"

    system "gsed -i -e \"s,YANEURAOU_EDITION = YANEURAOU_ENGINE_NNUE,#YANEURAOU_EDITION = YANEURAOU_ENGINE_NNUE,\" -e \"s,##YANEURAOU_EDITION = YANEURAOU_ENGINE_NNUE_KP256,YANEURAOU_EDITION = YANEURAOU_ENGINE_NNUE_KP256,\" source/Makefile"
    system "make clean -C source"
    system "make -C source"
    system "mv source/YaneuraOu-by-gcc YaneuraOu_NNUE_KP256"

    system "gsed -i -e \"s,YANEURAOU_EDITION = YANEURAOU_ENGINE_NNUE_KP256,#YANEURAOU_EDITION = YANEURAOU_ENGINE_NNUE_KP256,\" -e \"s,#YANEURAOU_EDITION = YANEURAOU_ENGINE_KPPT,YANEURAOU_EDITION = YANEURAOU_ENGINE_KPPT,\" source/Makefile"
    system "make clean -C source"
    system "make -C source"
    system "mv source/YaneuraOu-by-gcc YaneuraOu_KPPT"

    system "gsed -i -e \"s,YANEURAOU_EDITION = YANEURAOU_ENGINE_KPPT,#YANEURAOU_EDITION = YANEURAOU_ENGINE_KPPT,\" -e \"s,#YANEURAOU_EDITION = YANEURAOU_ENGINE_KPP_KKPT,YANEURAOU_EDITION = YANEURAOU_ENGINE_KPP_KKPT,\" source/Makefile"
    system "make clean -C source"
    system "make -C source"
    system "mv source/YaneuraOu-by-gcc YaneuraOu_KPP_KKPT"

    prefix.install "YaneuraOu_NNUE", "YaneuraOu_NNUE_KP256", "YaneuraOu_KPPT", "YaneuraOu_KPP_KKPT"
  end

  test do
    assert_match 'usiok', shell_output("cd #{prefix} && echo 'usi' | ./YaneuraOu_NNUE | grep 'usiok'")
    assert_match 'usiok', shell_output("cd #{prefix} && echo 'usi' | ./YaneuraOu_NNUE_KP256 | grep 'usiok'")
    assert_match 'usiok', shell_output("cd #{prefix} && echo 'usi' | ./YaneuraOu_KPPT | grep 'usiok'")
    assert_match 'usiok', shell_output("cd #{prefix} && echo 'usi' | ./YaneuraOu_KPP_KKPT | grep 'usiok'")
  end
end

あまり見栄えは良くないが、ひたすらmakefileを編集してmakeしていくだけの簡単なスクリプトである。詳しくは勉強していないが、将棋ソフトが使っている評価関数にはいくつか代表的な形式があるようで、「やねうら王」では現在主流のNNUE型のほか、NNUE_KP256型、KPPT型、KPP_KKPT型に対応した実行ファイルを作成できるらしい。そこで、それぞれmakefileを編集してコンパイルし、できた実行バイナリを各々異なるファイル名に変更するスクリプトに仕上げてみた。また、使用するSIMD拡張命令の部分については、個別の環境で使える最良のものをスクリプトが判断し、それを使うようにした。(ただし、AVX-512命令については未対応。現状、一般のパソコンでAVX-512命令に対応しているものはまだ少ないはずだけど)

homebrewで「やねうら王」をインストール

 formulaができたので、これで本当にインストールできるか試してみる。まずは、リポジトリをtapする。

$ brew tap hikoyu/shogi
Updating Homebrew...
(途中省略)
==> Tapping hikoyu/shogi
Cloning into '/opt/homebrew/Library/Taps/hikoyu/homebrew-shogi'...
(途中省略)

tapできたら、次にsearchで「yaneuraou」が見つかるか確認。

$ brew search yaneuraou
==> Formulae
hikoyu/shogi/yaneuraou

おお、ちゃんと見つかった。それでは早速インストールしてみる。

$ brew install yaneuraou
==> Installing yaneuraou from hikoyu/shogi
==> Downloading https://github.com/yaneurao/YaneuraOu/archive/V4.88.tar.gz
(途中省略)
🍺  /opt/homebrew/Cellar/yaneuraou/4.88: 8 files, 3.2MB, built in 2 minutes 59 seconds

無事、ビールのアイコンが表示され、インストールが完了した模様。念の為、動作確認してみる。(テスト部分もformulaに記述しておいたので)

$ brew test yaneuraou
Testing hikoyu/shogi/yaneuraou
==> cd /opt/homebrew/Cellar/yaneuraou/4.88 && echo 'usi' | ./YaneuraOu_NNUE | grep 'usiok'
==> cd /opt/homebrew/Cellar/yaneuraou/4.88 && echo 'usi' | ./YaneuraOu_NNUE_KP256 | grep 'usiok'
==> cd /opt/homebrew/Cellar/yaneuraou/4.88 && echo 'usi' | ./YaneuraOu_KPPT | grep 'usiok'
==> cd /opt/homebrew/Cellar/yaneuraou/4.88 && echo 'usi' | ./YaneuraOu_KPP_KKPT | grep 'usiok'

エラーがないと、特にこれといった表示が出ないので味気ない感じだが、動作確認も問題ないようだ。

 なお、インストールされた「やねうら王」の実行バイナリは/usr/local/bin/には配置されず、/usr/local/Cellar/yaneuraou/4.88/内のみに配置される仕様にしている。というのも、実際の利用シーンでは何らかのGUIソフトから起動させることになるので、実行バイナリ自体をコマンドラインから起動させるシーンはないからだ。(あと、評価関数などのバイナリを同一のディレクトリに配置しておかないといけないのだが、何となくそれらを/usr/local/bin/に置いておきたくないから)

homebrewで「elmo」の評価関数をインストール

 ひとまず、ここまでで「やねうら王」のインストールには対応できた。ただし、作成したのは思考エンジン部分のみのformulaで、これだけでは局面の優劣を判断するための評価関数や定石はインストールされない。将棋ソフトによってはこれらを同梱しているものもあるのだが、少なくとも「やねうら王」は思考エンジンの頒布が中心で、自らが大会で採用した評価関数はあまり積極的に提供していないように思われた。逆に、思考エンジン部分は「やねうら王」などのオープンソースを流用し、評価関数を独自にチューニングした将棋ソフトも存在しており、2017年の第27回世界コンピュータ将棋選手権で優勝した「elmo」などはその典型である。そこで、「elmo」の評価関数をインストールするformulaを作ることにした。

 formulaの作成手順は先ほどと基本的に同じである。だが、ここで問題が生じた。「elmo」の評価関数は制作者のGoogle Driveで公開されているのだが、Google Driveはファイルのダウンロード要求時にウイルススキャンをするという厄介な仕様があり、サイズの大きいファイルの場合、それができない旨の確認が出るため、コマンドラインから対応するのが面倒なのである。(少し調べたところでは、解決方法はあるようだが)今回は、インストールする評価関数を2017年版ではなく、2019年の最新版にしたところ、NNUE型のみの提供だったので、Google Driveのウイルススキャンができる大きさに収まっており、普通にダウンロードできるようだった。ただし、評価関数のアーカイブはMacではあまり見かけない7z形式で圧縮されており、通常そのままでは展開できない。homebrewでは7z形式のアーカイブはp7zipパッケージに含まれる7zrで展開する仕様になっているようなのだが、勝手に依存解決まではしてくれず、他の依存パッケージと同様、formulaに記述しておく必要があるようだった。そんなこんなで出来上がったrubyスクリプトは以下の通り。

elmo.rb
# Documentation: https://docs.brew.sh/Formula-Cookbook
#                https://rubydoc.brew.sh/Formula
# PLEASE REMOVE ALL GENERATED COMMENTS BEFORE SUBMITTING YOUR PULL REQUEST!
class Elmo < Formula
  desc "elmo evaluation function for Shogi (WCSC29 edition)"
  homepage "https://mk-takizawa.github.io/elmo/howtouse_elmo.html"
  url "https://drive.google.com/uc?export=download&id=1geE42yhY8fAG-ouEUx7_u6HQw1OjBQ2O"
  version "wcsc29"
  sha256 "d3a938a024cfe654bbd56c7b63a7dae211ee6a5aa508ac9b08308cd73c56b91a"

  depends_on "p7zip"
  depends_on "yaneuraou"

  def install
    # ENV.deparallelize  # if your formula fails when building in parallel

    system "mkdir eval"
    system "mv nn.bin eval"
    system "cp #{HOMEBREW_PREFIX}/opt/yaneuraou/YaneuraOu_NNUE YaneuraOu_elmo_#{version}"
    system "echo elmo_#{version} >engine_name.txt"
    prefix.install "eval", "YaneuraOu_elmo_#{version}", "engine_name.txt"
    ohai "[INFO] Copy and paste the path below to the box for defining Shogi engine in GUI software."
    ohai "#{prefix}/YaneuraOu_elmo_#{version}"
  end

  test do
    assert_match 'readyok', shell_output("cd #{prefix} && echo 'isready' | ./YaneuraOu_elmo_#{version} | grep 'readyok'")
  end
end

このスクリプトが主にやっていることはeval/ディレクトリを作成し、評価関数バイナリであるnn.binをそこに配置し、思考エンジンは「やねうら王」のCellarからNNUE型対応の実行バイナリをファイル名を変更してコピーしているだけ。ただし、先に「やねうら王」がインストールされている必要があるので、依存パッケージとして記述してある。

 これで「elmo」もhomebrewでインストールできるようになった。既にリポジトリをtap済みなら、次のコマンドを叩くだけで「elmo」がインストールできる。

$ brew install elmo
==> Installing elmo from hikoyu/shogi
==> Downloading https://drive.google.com/uc?export=download&id=1geE42yhY8fAG-ouEUx7_u6HQw1OjBQ2O
(途中省略)
==> [INFO] Copy and paste the path below to the box for defining Shogi engine in GUI software.
==> /usr/local/Cellar/elmo/wcsc29/YaneuraOu_elmo_wcsc29
🍺  /usr/local/Cellar/elmo/wcsc29: 5 files, 62MB, built in 25 seconds

最後、一工夫として、以下のパスをGUIソフトの将棋エンジン設定ボックスにコピペするよう促す旨を表示するようにしておいた。念の為、動作確認もしておく。

$ brew test elmo
Testing hikoyu/shogi/elmo
==> cd /opt/homebrew/Cellar/elmo/wcsc29 && echo 'isready' | ./YaneuraOu_elmo_wcsc29 | grep 'readyok'

特にエラーメッセージは出ていないので問題なく動作している。

インストールした「elmo」と対局してみる

 Macで動作する将棋のGUIソフトもあまり多くは存在しないようで、安定に動作するのはJavaアプリケーションの将棋ぶらうざQくらいと思われる(他に、Windowsアプリケーションをwineで動かす方法はあるかもしれないが、あまり試していない)ので、これを使って、homebrewでインストールした「elmo」と対局できるか確認してみる。

 「将棋ぶらうざQ」を起動したら、メニューバーから「設定」→「将棋エンジン設定…」と進んで、「追加」ボタンをクリック。そして、表示される「将棋エンジン新規追加」ウィンドウのエンジンファイルパスのボックスに先ほど表示されたパス/usr/local/Cellar/elmo/wcsc29/YaneuraOu_elmo_wcsc29をコピペして、ウィンドウ下部の「追加」ボタンをクリックする。これで将棋エンジンに「elmo_wcsc29」が追加される。
将棋エンジン新規追加.png

 あとは「将棋ぶらうざQ」のメニューバーから「対局」→「新規対局開始…」と進み、エンジンに「elmo_wcsc29」を選んで対局することができる。
対局設定.png

 結果は「elmo」の前に何もいいところなく完敗…あまりに強すぎる。やはり、研究用に使うのが良いようだ。

気づいた点(本題とはあまり関係ない)

 本記事では「やねうら王」のコンパイル時に、makefile中の設定でコンパイラをclang++からg++に変更しているが、これをclang++のままmakeしてみたらどうなったかというお話。SIMD拡張命令の部分だけ編集してmakeすると…

$ make
clang++ -std=c++14 -fno-exceptions -fno-rtti -Wextra -Ofast -MMD -MP -fpermissive -DNDEBUG -stdlib=libstdc++ -Wno-unused-parameter -D_LINUX -DUNICODE -DNO_EXCEPTIONS -DUSE_SSE42 -msse4.2 -march=corei7 -DUSE_MAKEFILE -DYANEURAOU_ENGINE_NNUE  -o ../obj/main.o -c main.cpp
warning: include path for stdlibc++ headers not found; pass '-stdlib=libc++' on the command line to
      use the libc++ standard library instead [-Wstdlibcxx-not-found]
In file included from main.cpp:4:
In file included from ./search.h:4:
./misc.h:4:10: fatal error: 'chrono' file not found
#include <chrono>
         ^~~~~~~~
1 warning and 1 error generated.
make: *** [../obj/main.o] Error 1

いきなりエラーに遭遇。「clang++ではlibstdc++は使えないから代わりにlibc++を使ってね」と言われている。そうなることは知っていたけど、g++を指定した時は問題なかった。中身は結局clang++なのになんで?明示的にg++を使うと、利用する標準ライブラリーが変わる仕様なのだろうか?そう言われてみれば、/usr/bin/g++/usr/bin/clang++へのシンボリックリンクではない。ファイルサイズもちょっと違う。

$ ls -l /usr/bin/clang++ /usr/bin/g++
-rwxr-xr-x  1 root  wheel  18304  7 30  2019 /usr/bin/clang++
-rwxr-xr-x  1 root  wheel  18288  7 30  2019 /usr/bin/g++

ともあれ、makefileの-stdlib関係のCFLAGSを編集し、libc++を使うように変更したのち、再度makeしてみる。

#(前省略)

# clang用にCFLAGSなどを変更
ifeq ($(findstring clang++,$(COMPILER)),clang++)
        # stdlib
        CFLAGS += -stdlib=libc++

        # 関数の引数が関数本体で使われていないときに警告出るのうざすぎるので抑制。
        CFLAGS += -Wno-unused-parameter

        # static リンクを行う際に __cxa_guard_acquire __cxa_guard_release の生成を抑止
        #   undefined reference to `__imp___cxa_guard_acquire'
        #   undefined reference to `__imp___cxa_guard_release'
        # static 変数を初期化したかを pthread_mutex_t でロックを取って確認し、
        # 最初の実行なら初期化するスレッドセーフなコードを生成するためのもの。
        # → 本当に消してしまっても大丈夫か?
        WCFLAGS += -fno-threadsafe-statics

else

#(以下省略)
$ make clean
(途中省略)
$ make
(途中省略)
clang++ -o YaneuraOu-by-gcc ../obj/main.o ../obj/types.o ../obj/bitboard.o ../obj/misc.o ../obj/movegen.o ../obj/position.o ../obj/usi.o ../obj/usi_option.o ../obj/thread.o ../obj/tt.o ../obj/movepick.o ../obj/timeman.o ../obj/extra/book/apery_book.o ../obj/extra/book/book.o ../obj/extra/book/makebook2019.o ../obj/extra/bitop.o ../obj/extra/long_effect.o ../obj/extra/mate/mate1ply_with_effect.o ../obj/extra/mate/mate1ply_without_effect.o ../obj/extra/mate/mate_n_ply.o ../obj/extra/benchmark.o ../obj/extra/test_cmd.o ../obj/extra/see.o ../obj/extra/sfen_packer.o ../obj/extra/kif_converter/kif_convert_tools.o ../obj/eval/evaluate_bona_piece.o ../obj/eval/evaluate.o ../obj/eval/evaluate_io.o ../obj/eval/evaluate_mir_inv_tools.o ../obj/learn/learner.o ../obj/learn/learning_tools.o ../obj/learn/multi_think.o ../obj/eval/nnue/evaluate_nnue.o ../obj/eval/nnue/evaluate_nnue_learner.o ../obj/eval/nnue/nnue_test_command.o ../obj/eval/nnue/features/k.o ../obj/eval/nnue/features/p.o ../obj/eval/nnue/features/half_kp.o ../obj/eval/nnue/features/half_relative_kp.o ../obj/engine/yaneuraou-engine/yaneuraou-search.o  -Wl,-s -lpthread -v -std=c++14 -fno-exceptions -fno-rtti -Wextra -Ofast -MMD -MP -fpermissive -DNDEBUG -stdlib=libc++ -Wno-unused-parameter -D_LINUX -DUNICODE -DNO_EXCEPTIONS -DUSE_SSE42 -msse4.2 -march=corei7 -DUSE_MAKEFILE -DYANEURAOU_ENGINE_NNUE
Apple clang version 11.0.0 (clang-1100.0.33.17)
Target: x86_64-apple-darwin18.7.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
(以下省略)

今度は無事コンパイルが通った。動作確認も問題なかった。

$ echo 'usi' | ./YaneuraOu-by-gcc 
id name YaneuraOu NNUE 4.88 64SSE4.2
id author by yaneurao
(途中省略)
usiok

めでたしめでたし。

まとめ

・homebrewで将棋ソフトをインストールできるリポジトリを作った。
・記事執筆時点で「やねうら王」(思考エンジン)、「elmo」(評価関数)、「魚沼産やねうら王」(評価関数)、「技巧2」(思考エンジン+評価関数+定石)のインストールが可能。
・インストール手順は以下の通り。

$ brew tap hikoyu/shogi # 初回のみ叩くコマンド
$ brew install elmo
$ brew test elmo

参考文献

Homebrew Formula Cookbook
HomebrewにおけるFormulaの作成方法
CLIツールをHomebrewで配布する
Macにコンピュータ将棋ソフト「elmo」を導入した
Macに「技巧」をインストールする方法
MacOS に elmo + やねうら王 + 将棋ぶらうざQ をインストールしてみます
Macでelmoを使ってみた
技巧をMac環境で使う

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした