コンピュータ将棋ソフトとの対局サーバーを立てよう＜その４＞

前の記事 : http://qiita.com/muzudho1/items/3ff3e8dfde0ff5bed203

プロセス間通信も、Cronも、なんか動いてくれないが、
Apache、PHPだけ　まともに動いてくれるので Expect を試す。

# Expect で浮かむ瀬を起こせるか？

浮かむ瀬は

```
./apery
```

で起きてくれるのだった。ディレクトリを変えようかな。

/usr/games/ukamuse

とかどうだろう。と思って中を見てみたんだが　誰かが使ってて　ごちゃごちゃだ。
だったら　ユーザーのホーム・フォルダーでいいや……。

/home/★user/ukamuse

にするか。いや、これだとオリジナルの　浮かむ瀬　とごっちゃになる。現状の

/home/★user/shogi/

でいいや。この下に bash_shogi ディレクトリを切る。

```
mkdir bash_shogi
```

対局関連のシェル・スクリプトをここへ移そう。

```
mv ape1.sh ../../bash_shogi/ape1.sh
mv ape2.sh ../../bash_shogi/ape2.sh
mv ape3.sh ../../bash_shogi/ape3.sh
mv ape4.sh ../../bash_shogi/ape4.sh
mv ape5.sh ../../bash_shogi/ape5.sh
mv ape6.sh ../../bash_shogi/ape6.sh
mv ape7.sh ../../bash_shogi/ape7.sh
mv ape8.sh ../../bash_shogi/ape8.sh
```

他に、

```
mkdir csharp_shogi
```

フォルダを切る。

```
mv ape9.cs ../../bash_shogi/ape9.cs
mv ape10.cs ../../bash_shogi/ape10.cs
mv ape9.exe ../../bash_shogi/ape9.exe
mv ape10.exe ../../bash_shogi/ape10.exe
```

これでだいぶすっきりした。

# 今回新しく作るファイル

PHP で作ろうと思うので、

```
mkdir php_shogi
```

フォルダを切る。

# Expect
「Expect の使用例」 （PHPマニュアル）
http://php.net/manual/ja/expect.examples-usage.php

このページを読んで少し勉強する。

## インストール方法

インストール方法を探そう。

「expect」 (PECL)
http://pecl.php.net/package/expect

ここからダウンロードしたらいいんだろうか？

「expect_expectl関数 - PHPから対話型シェルに応答する」 （Developer☆STYLE）
http://blog.livedoor.jp/morningmist7/archives/1342932.html

PHP 7 のは無いのだろうか。composer でインストールできるとか。
なんで無いんだろ。

さっきのは破棄して別の方法で。

「expectコマンドが便利」 （試される大地から）
http://geektrainee.hatenablog.jp/entry/2013/11/23/191650

Ubuntu の場合。

```
apt-get -y install expect
```

```
Reading package lists... Done
Building dependency tree
Reading state information... Done
expect is already the newest version (5.45-7).
The following package was automatically installed and is no longer required:
  libodbc1
Use 'apt autoremove' to remove it.
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
```

**あれ？** 既に入ってるのか？

# PHPでもBashでもなく、Expectスクリプト

expect_service というフォルダーを切ろう。

```
cd /home/★user/shogi
mkdir expect_service
cd expect_service
nano tamesi17.expect
```

```
#!/usr/bin/expect
respawn ../ukamuse_sdt4/bin/apery
send "bench\n"
```

```
ls -l
total 4
-rw-r--r-- 1 root root 68 Mar  8 20:21 tamesi17.expect
chmod 755 tamesi17.expect
ls -l
total 4
-rwxr-xr-x 1 root root 68 Mar  8 20:21 tamesi17.expect
   ~  ~  ~
   ※ここがxになった
```

```
./tamesi17.expect
invalid command name "respawn"
    while executing
"respawn ../ukamuse_sdt4/bin/apery"
    (file "./tamesi17.expect" line 2)
```

```
man expect
```

こうなんじゃないか。

tamesi17.expect

```
#!/usr/bin/expect
spawn ../ukamuse_sdt4/bin/apery
send "bench\n"
```

```
# ./tamesi17.expect
spawn ../ukamuse_sdt4/bin/apery
```

終わった。
直接叩いてみよう。

```
# ../ukamuse_sdt4/bin/apery
```

0.数秒で apery が実行されるな。

```
bench
```

うん、ベンチマーク走る。

「expectコマンドの使い方」 （Linuxで自宅サーバ構築（新森からの雑記））
http://www.uetyi.com/server-const/command/entry-158.html

「EXPECT」 （linuxjm.osdn.jp）
https://linuxjm.osdn.jp/html/expect/man1/expect.1.html

/usr/bin/expect ファイルは在る。

## カレント・ディレクトリの設定

書き直し

```
#!/usr/bin/expect

# カレント・ディレクトリを設定
cd /home/★user/shogi/expect_service

# タイムアウトさせない
set timeout -1

# 浮かむ瀬を実行
spawn ../ukamuse_sdt4/bin/apery

expect ""

send "bench\n"

# 入出力をキーボードと画面に返す
interact
```

```
./tamesi17.expect
spawn ../ukamuse_sdt4/bin/apery
bench
```

終わってしまった。

こうする。

```
#!/usr/bin/expect

# カレント・ディレクトリを設定
cd /home/★user/shogi/expect_service

# Expectをタイムアウトさせない
set timeout -1

# 浮かむ瀬を実行
spawn ../ukamuse_sdt4/bin/apery bench

# 入出力をキーボードと画面に返す
interact
```

```
./tamesi17.expect
spawn ../ukamuse_sdt4/bin/apery bench
info string start setting eval table
info string end setting eval table
```

これなら動く。次。

tamesi17a1.expect

```
#!/usr/bin/expect

# カレント・ディレクトリを設定
cd /home/★user/shogi/expect_service

# Expectをタイムアウトさせない
set timeout -1

# 浮かむ瀬を実行
spawn ../ukamuse_sdt4/bin/apery

send "bench\n"

# 入出力をキーボードと画面に返す
interact
```

```
chmod 755 tamesi17a1.expect
./tamesi17a1.expect
```

これもいけた。

動きは始まりはしたが、途中で終わった。

Expectに 完了と思われたんだろうか。浮かむ瀬のプロセスを途中で開放したんだろうか？
じゃあ、bestmove と表示させるまで監視させよう。

tamesi17a2.expect

```
#!/usr/bin/expect

# カレント・ディレクトリを設定
cd /home/★user/shogi/expect_service

# Expectをタイムアウトさせない
set timeout -1

# 浮かむ瀬を実行
spawn ../ukamuse_sdt4/bin/apery

send "bench\n"

expect "bestmove"

# 入出力をキーボードと画面に返す
interact
```

## apery は bin フォルダーの中で実行しなければいけないのでは？

benchmark.sfen や、定跡ファイルのパスが、カレント・ディレクトリが bin の中にあることを前提としている。
書き方を変えてみる。

tamesi17a2.expect

```
#!/usr/bin/expect

# カレント・ディレクトリを設定
cd /home/★user/shogi/ukamuse_sdt4/bin

# Expectをタイムアウトさせない
set timeout -1

# 浮かむ瀬を実行
spawn ./apery

send "bench\n"

expect "bestmove"

# 入出力をキーボードと画面に返す
interact
```

**ビンゴ！**

ただ、最後の interact が働いていない。書き方が違うのか？

むしろ bestmove という単語は　しょっちゅう出てきていて、最後の bestmove を判定するのがむずかしい。
timeout も使わないといけないのか。

## TCL
Expectコマンドの関連技術はこれか。

「文法とコマンド」 （freesoftnet.co.jp）
http://www.freesoftnet.co.jp/webfiles/tclkits/doc/tclcom.html

ぜんぜんわからん。
トライ＆エラーで　当たるまででたらめに　書き直し。

tamesi17a2.expect

```
#!/usr/bin/expect

# カレント・ディレクトリを設定
cd /home/★user/shogi/ukamuse_sdt4/bin

# Expectをタイムアウトさせない
set timeout -1

# 浮かむ瀬を実行
spawn ./apery

send "bench\n"

expect {
    # "bestmove"と帰ってきていて、5秒間出力がなければ、入出力をキーボードと画面に返す
    "bestmove"
    -timeout 5
        interact
}
```

こういう書き方にすると、それっぽく動いているが。

#じゃあ、Expect で対局しようぜ

どうやって書くのか。少しずつやってみよう。

というか複数条件は動いていなかった。どう書くのか。

「制御構造」 （Tcl 入門編）
http://bitwalk.sitemix.jp/tcltk_intro_tcl_control.php

「&&」とかいう　ふつうの答えでいいのか？

```
expect {
    # "bestmove"と帰ってきていて、5秒間出力がなければ、入出力をキーボードと画面に返す
    "bestmove" && -timeout 10
        interact
}
```

いいようではあるものの、「-timeout 10」というのがトータル・タイムなのがつらい。出力中でも１０秒で切れてしまう。

じゃあ、こうか。

```
    # "bestmove"と帰ってきたあと、5秒間出力がなければ、入出力をキーボードと画面に返す
    "bestmove"
    {
        -timeout 5
            interact
    }
```

突然切れた。

```
*3c 3d2c+ 3c2c G*3d S*2b 4b3b N*1g 1i1g 3i1g+ 3b2b 2c2b G*2c
info nodes 1387645 time 9516
bestmove S*2c ponder 2d2c+invalid command name "-timeout"
    while executing
"-timeout 5"
    invoked from within
"expect {
    # "bestmove"と帰ってきていて、5秒間出力がなければ、入出力をキーボードと画面に返す
    "bestmove"
    {
..."
    (file "./tamesi17a2.expect" line 14)
```

"-timeout" がコマンドと認識されてしまって、コマンドが無いというエラーになったのか。
じゃあこれでどうか。

```
expect
{
    # "bestmove"と帰ってきたあと、5秒間出力がなければ、入出力をキーボードと画面に返す
    "bestmove"
    {
        expect {
            -timeout 5
                interact
        }
    }
}
```

**おや？** でけたぜ。

こんなん、トライ＆エラーだよな。

**非効率的な方法で進む。**

でも、interact 効いてないな。

でも進む。

tamesi17a3.expect

```
#!/usr/bin/expect

# カレント・ディレクトリを設定
cd /home/★user/shogi/ukamuse_sdt4/bin

# Expectをタイムアウトさせない
set timeout -1

# 浮かむ瀬を実行
spawn ./apery

send "usi\n"

expect {
    "usiok" {
        exit
    }
}
```

- **interact じゃなくて、exit を使うんじゃないか。**
- **ブレス『{』は行末に置いておかないとダメだろうか？**

## 選択構造じゃなくて、シーケンシャルなんじゃないか？

書き直し。

tamesi17a4.expect

```
#!/usr/bin/expect

# カレント・ディレクトリを設定
cd /home/★user/shogi/ukamuse_sdt4/bin
#  /home/★user/shogi/expect_service ではない。

# Expectをタイムアウトさせない
set timeout -1

# 浮かむ瀬を実行
spawn ./apery

send "usi\n"

expect "usiok"

send "isready\n"

expect "readyok"

exit
```

これで readyok まで返ってくる。

## もっと進めよう

tamesi17a5.expect

```
#!/usr/bin/expect

# カレント・ディレクトリを設定
cd /home/★user/shogi/ukamuse_sdt4/bin
#  /home/★user/shogi/expect_service ではない。

# Expectをタイムアウトさせない
set timeout -1

# 浮かむ瀬を実行
spawn ./apery

send "usi\n"

expect "usiok"

# 0.1秒は考えろ
send "setoption name Minimum_Thinking_Time value 100\n"

send "isready\n"

expect "readyok"

send "newgame\n"

send "position sfen lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL w - 1 moves\n"

send "go\n"

expect "bestmove"

exit
```

```
chmod 755 tamesi17a5.expect
./tamesi17a5.expect
spawn ./apery
usi
id name ukamuse_SDT4
id author Hiraoka Takuya

option name Best_Book_Move type check default false
option name Book_File type string default book/20150503/book.bin
option name Byoyomi_Margin type spin default 500 min 0 max 2147483647
option name Clear_Hash type button
option name Draw_Ply type spin default 256 min 1 max 2147483647
option name Engine_Name type string default ukamuse_SDT4
option name Max_Book_Ply type spin default 32767 min 0 max 32767
option name Max_Random_Score_Diff type spin default 0 min 0 max 32600
option name Max_Random_Score_Diff_Ply type spin default 32767 min 0 max 32767
option name Min_Book_Ply type spin default 32767 min 0 max 32767
option name Min_Book_Score type spin default -180 min -32601 max 32601
option name Minimum_Thinking_Time type spin default 20 min 0 max 2147483647
option name Move_Overhead type spin default 30 min 0 max 5000
option name MultiPV type spin default 1 min 1 max 594
option name OwnBook type check default true
option name Slow_Mover type spin default 89 min 1 max 1000
option name Slow_Mover_10 type spin default 10 min 1 max 1000
option name Slow_Mover_16 type spin default 20 min 1 max 1000
option name Slow_Mover_20 type spin default 40 min 1 max 1000
option name Threads type spin default 2 min 1 max 256
option name Time_Margin type spin default 4500 min 0 max 2147483647
option name USI_Hash type spin default 256 min 1 max 1048576
option name USI_Ponder type check default true
usiok
setoption name Minimum_Thinking_Time value 100
isready
readyok
newgame
position sfen lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL w - 1 moves
go
unknown command: newgame
info string optimum_time = 100
info string maximum_time = 100
info string book_ply 32767
info depth 1 seldepth 1 multipv 1 score cp 63 nodes 66 nps 904 time 73 pv 8c8d
info depth 2 seldepth 2 multipv 1 score cp 82 nodes 154 nps 1480 time 104 pv 8c8d 2g2f
info depth 3 seldepth 3 multipv 1 score cp 46 nodes 264 nps 1859 time 142 pv 3a3b 2g2f 3c3d
info depth 4 seldepth 4 multipv 1 score cp -28 nodes 735 nps 2254 time 326 pv 3a3b 2g2f 3c3d 2f2e
info depth 5 seldepth 7 multipv 1 score cp 68 nodes 736 nps 2243 time 328 pv 3c3d 2g2f 2b3c 2f2e 3a3b
bestmove 3c3d ponder 2g2f
```

**はい、おっけ。**

これを PHP から叩けるようにしたい。

PHP から 「#!/usr/bin/expect」で始まるスクリプトは叩けるのだろうか？
例えば、

```
/usr/bin/expect ./tamesi17a5.expect
```

とかコマンドを打ったら実行されるのだろうか？
**コマンドラインに打ち込んだら実行された。**

「exec」 （PHPマニュアル）
http://php.net/manual/ja/function.exec.php
じゃあ、PHP の exec( ) で外部コマンドを叩いたら動くのだろうか？

単純に考えると

```
# cd /home/★user/shogi/php_service
# nano tamesi18.php
```

php_service/tamesi18.php

```
<?php
exec    ( '/usr/bin/expect ../expect_service/tamesi17a5.expect', $out, $ret );
print_r ( $out );
var_dump( $ret );
```

```
# ls -l
略
-rw-r--r-- 1 root root  120 Mar  9 00:08 tamesi18.php
# chmod 755 tamesi18.php
# php tamesi18.php
```

ここで数秒止まる。

```
Array
(
    [0] => spawn ./apery
    [1] => usi
    [2] => id name ukamuse_SDT4
    [3] => id author Hiraoka Takuya
    [4] =>
    [5] => option name Best_Book_Move type check default false
    [6] => option name Book_File type string default book/20150503/book.bin
    [7] => option name Byoyomi_Margin type spin default 500 min 0 max 2147483647
    [8] => option name Clear_Hash type button
    [9] => option name Draw_Ply type spin default 256 min 1 max 2147483647
    [10] => option name Engine_Name type string default ukamuse_SDT4
    [11] => option name Max_Book_Ply type spin default 32767 min 0 max 32767
    [12] => option name Max_Random_Score_Diff type spin default 0 min 0 max 32600
    [13] => option name Max_Random_Score_Diff_Ply type spin default 32767 min 0 max 32767
    [14] => option name Min_Book_Ply type spin default 32767 min 0 max 32767
    [15] => option name Min_Book_Score type spin default -180 min -32601 max 32601
    [16] => option name Minimum_Thinking_Time type spin default 20 min 0 max 2147483647
    [17] => option name Move_Overhead type spin default 30 min 0 max 5000
    [18] => option name MultiPV type spin default 1 min 1 max 594
    [19] => option name OwnBook type check default true
    [20] => option name Slow_Mover type spin default 89 min 1 max 1000
    [21] => option name Slow_Mover_10 type spin default 10 min 1 max 1000
    [22] => option name Slow_Mover_16 type spin default 20 min 1 max 1000
    [23] => option name Slow_Mover_20 type spin default 40 min 1 max 1000
    [24] => option name Threads type spin default 2 min 1 max 256
    [25] => option name Time_Margin type spin default 4500 min 0 max 2147483647
    [26] => option name USI_Hash type spin default 256 min 1 max 1048576
    [27] => option name USI_Ponder type check default true
    [28] => usioksetoption name Minimum_Thinking_Time value 100
    [29] => isready
    [30] =>
    [31] => readyok
    [32] => newgame
    [33] => position sfen lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL w - 1 moves
    [34] => go
    [35] => unknown command: newgame
    [36] => info string optimum_time = 100
    [37] => info string maximum_time = 100
    [38] => info string book_ply 32767
    [39] => info depth 1 seldepth 1 multipv 1 score cp 63 nodes 66 nps 1736 time 38 pv 8c8d
    [40] => info depth 2 seldepth 2 multipv 1 score cp 82 nodes 133 nps 2418 time 55 pv 8c8d 2g2f
    [41] => info depth 3 seldepth 3 multipv 1 score cp 46 nodes 297 nps 4125 time 72 pv 3a3b 2g2f 3c3d
    [42] => info depth 4 seldepth 4 multipv 1 score cp -28 nodes 748 nps 3777 time 198 pv 3a3b 2g2f 3c3d 2f2e
    [43] => info depth 5 seldepth 5 multipv 1 score cp 135 nodes 1434 nps 3051 time 470 pv 3c3d 6i7h 2b3c 2g2f 8c8d
    [44] => bestmove 3c3d ponder 6i7h
)
int(0)
```

数秒止まったところが気になるが、$ret 変数の最終行に「bestmove 3c3d ponder 6i7h」が入っているようだ。
これだけ返す方法を考えたい。

tamesi19.php

```
<?php
exec    ( '/usr/bin/expect ../expect_service/tamesi17a5.expect', $out );
echo $out[ count( $out ) - 1 ] . "\n";
```

```
php tamesi19.php
bestmove 8c8d ponder 2g2f
```

でけた。**0.5秒どころではなく、数秒待つけど。**

## じゃあ、これを　サーバーの外部からアクセスできるように、外側に向けてみよう。

単純な方法は、

```
cp /home/★user/shogi/php_service/tamesi19.php /var/www/html/tamesi19.php
```

で、tamesi19.php の中のファイルパスは　絶対パスにしてしまおう。

http://★.★.★.★/tamesi19.php へアクセス。

```
bestmove 3a3b ponder 2g2f
```

3秒ぐらいかかってるかな。

どう早くするか。

## その前に tamesi14.php と tamesi19.php を合体させよう

**あれ？**

## Expectスクリプトはどうやって引数を受け取るのか？

「EXPECT」 （linuxjm.osdn.jp）
https://linuxjm.osdn.jp/html/expect/man1/expect.1.html

「$argv」配列にでも入ってるんだろうか？

tamesi21.expect 抜粋

```
send "position ${argv(0)}\n"
```

こう書くと

```
can't read "argv(0)": variable isn't array
    while executing
"send "position ${argv(0)}\n""
    (file "/home/★user/shogi/expect_service/tamesi21.expect" line 27)
```

と帰ってくる。argv は配列じゃないのか。

```
send "position ${argv}\n"
```

こう書くと

```
position /home/★user/shogi/expect_service/tamesi21.expect
```

こんな出力になってしまう。

「--」で区切ると、argv変数の中身を渡せるようだが。

```
# /usr/bin/expect /home/★user/shogi/expect_service/tamesi21.expect --sfen lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL w - 1 moves
```

違うのか。

```
#!/usr/bin/expect --
```

こっちか。
で、こう書いて。

```
send "position ${argv}\n"
```

こう。

```
# /usr/bin/expect /home/★user/shogi/expect_service/tamesi21.expect sfen lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL w - 1 moves
```

いけた。つまり

tamesi21.expect

```
#!/usr/bin/expect --
# 末尾の -- は、「${argv}」変数にコマンドライン引数を渡す指定

# カレント・ディレクトリを設定
cd /home/★user/shogi/ukamuse_sdt4/bin
#  /home/★user/shogi/expect_service ではない。

# Expectをタイムアウトさせない
set timeout -1

# 浮かむ瀬を実行
spawn ./apery

send "usi\n"

expect "usiok"

# 0.1秒は考えろ
# send "setoption name Minimum_Thinking_Time value 100\n"

send "isready\n"

expect "readyok"

send "newgame\n"

# 「sfen lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL w - 1 moves」
send "position ${argv}\n"

send "go\n"

expect "bestmove"

exit
```

こう書く。

じゃあ PHP側はこう書く。

```
<?php
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;

// GETクエリ文字列を取得
$QStr = urldecode($_SERVER['QUERY_STRING']);
if( "" === $QStr )
{
    $QStr = "sfen lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL w - 1 moves";
}

// 別のクエリーも送れることを説明
echo 'このURLと同じだぜ☆ｍ９（＾～＾）！<br />';
echo '<a href="http://★.★.★.★/tamesi21.php?' . urldecode( $QStr ) . '">http://★.★.★.★/tamesi21.php?' . urldecode( $QStr ) . '</a><br />';


echo '浮かむ瀬さん（＾ｑ＾）　よろしくたのんますだぜ☆（＾▽＾）ｖ<br />' . "\n";

// /usr/bin/expect /home/★/shogi/expect_service/tamesi21.expect sfen lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL w - 1 moves
exec    ( '/usr/bin/expect /home/★/shogi/expect_service/tamesi21.expect ' . $QStr , $out );
echo '<br />(´･_･`) URLで別のリソースが出てくるのはちゃうんとちゃうかな……。<br />' . $out[ count( $out ) - 1 ] . '<br />' . "\n";
```

これで URL でアクセスすると、浮かむ瀬は　ベストムーブをブラウザ画面に返してくれる。

浮かむ瀬は 0.1秒ぐらいで反応するんだが、画面に出るまで５秒ぐらいかかって遅い。高速化を検討しよう。

# 高速化を検討

ゴールは　１秒未満。

よく見ると

```
require_once __DIR__ . '/vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
```

とかいう、RabbitMQ 用のコードが残ってるじゃないか。消そう。

高速化ということで、もうちょい短くしてみよう。

tamesi22.php

```
<?php exec('/usr/bin/expect /home/★user/shogi/expect_service/tamesi21.expect '.urldecode($_SERVER['QUERY_STRING']),$o);echo $o[count($o)-1];
```

ここで、 .expect を別途呼んでいるので遅いのは分かる。
PHP で expect を直書きするための環境設定が　できればいいんだが。

## 計測しないと

なんつーの、**何秒高速化できたのか　数字を取るプロファイラが欲しい**んだが、とりあえず　体感時間でいいか……（／＿＼）

Expectスクリプトの expect命令は -exフラグを付けると完全一致になるらしい。
こういうの付けると　高速化につながると思うんだが。

手短に書くと　こうなるだろうか？

tamesi22.expect

```
#!/usr/bin/expect --
cd /home/★user/shogi/ukamuse_sdt4/bin
set timeout -1
spawn ./apery
send "usi\n"
expect -ex "usiok"
send "isready\n"
expect -ex "readyok"
send "newgame\n"
send "position ${argv}\n"
send "go\n"
expect "bestmove"
exit
```

手短にはなったが、別に速くはならない。

あと　いじるところと言えば、PHP で直接 Expect をやる、ということなんだが。

## PHP で直接 Expect をやるには

「PHP Expect not working properly」 （stack overflow）
http://stackoverflow.com/questions/17894359/php-expect-not-working-properly

phpinfo() を見てみるか。

expect 入ってないわ。

「expectを利用したssh接続時のパスワード入力の自動化」 (Qiita)
http://qiita.com/digitalpeak/items/cb51e0ca808de53800bb

```
sudo yum -y install expect
```

```
There are no enabled repos.
 Run "yum repolist all" to see the repos you have.
 You can enable repos with yum-config-manager --enable <repo>
```

repo って何なのか。

Ubuntu に yum なんか入ってるのか？ apt-get じゃないのか？

```
root@tk2-217-18401:/var/www/html# sudo apt-get -y install expect
Reading package lists... Done
Building dependency tree
Reading state information... Done
expect is already the newest version (5.45-7).
The following package was automatically installed and is no longer required:
  libodbc1
Use 'sudo apt autoremove' to remove it.
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
```

まあ、使ってたからな。 5.45-7 バージョンが入ってるみたいだ。
phpinfo(); では出てこないけど。

## コマンド数を少なくする

```
send "newgame\n"
send "position ${argv}\n"
send "go\n"
```

ここは、

```
send "newgame\nposition ${argv}\ngo\n"
```

こう書ける。

## PHPのExpect

「XXXV. Expect 関数」 （PHPマニュアル）
http://manual.xwd.jp/ref.expect.html

多分、こんな感じになると思うんだが。

tamesi16.php

```
<?php
ini_set("expect.timeout", "-1");
ini_set("expect.loguser", "Off");
$stream = fopen("/home/★user/shogi/ukamuse_sdt4/bin/apery", "r");
define("USI_OK"   ,"usiok"   );
define("READYOK"  ,"readyok" );
define("BESTMOVE" ,"bestmove");
$cases = array (
    array ( "usiok"    ,USI_OK   ),
    array ( "readyok"  ,READYOK  ),
    array ( "bestmove" ,BESTMOVE )
);

fwrite ($stream, "usi\n");
switch (expect_expectl ($stream, $cases)) {
    case USI_OK:
        fwrite ($stream, "isready\n");
        break;
    default:
        die ("Expect実行中にエラーが発生しました!\n");
}
switch (expect_expectl ($stream, $cases)) {
    case READYOK:
        fwrite ($stream, "newgame\nposition " . urldecode($_SERVER['QUERY_STRING']) . "\ngo\n");
        break;
    default:
        die ("Expect実行中にエラーが発生しました!\n");
}
switch (expect_expectl ($stream, $cases)) {
    case BESTMOVE:
        break;
    default:
        die ("Expect実行中にエラーが発生しました!\n");
}

// 出力結果を全部出す
while ($line = fgets($stream)) {
      print $line;
}
fclose ($stream);
```

http://★.★.★.★/tamesi16.php にアクセスすると、

```
Fatal error: Uncaught Error: Call to undefined function expect_expectl() in /var/www/html/tamesi16.php:15 Stack trace: #0 {main} thrown in /var/www/html/tamesi16.php on line 15
```

となる。PHPのExpectライブラリの最新版は、PHP5.いくつだろうか？

# Expect の大元は
「Expect」 （NIST）
https://www.nist.gov/services-resources/software/expect

ここか。Source Forge を見てみよう。2013年が最終更新日か。

「Expect」 （source forge）
https://sourceforge.net/projects/expect/

PHP7が出たのが 2015 なので、対応してないな。

# 必要なもの
## libexpect

「Requirements」 （PHPマニュアル）
http://php.net/manual/en/expect.requirements.php

バージョン 5.43.0 以上の libexpect が必要とのこと。

5.45 なら、さっきのページで配布してるやつだ。

「Expect」 （source forge）
https://sourceforge.net/projects/expect/

中身のソースは　バリバリのＣ言語だが……。

「Installation」 （PHPマニュアル）
http://php.net/manual/en/expect.installation.php

PHP は入ってないらしい。

「Installation of PECL extensions」 （PHPマニュアル）
http://php.net/manual/en/install.pecl.php

５年前のマニュアルが最新か。

## composer で expect を使えないのか？

copmoser で PEAR をインストールする方法は書いてあるが、

「05-repositories」 （getcomposer.org）
https://getcomposer.org/doc/05-repositories.md#pear

どうも Expectライブラリは PECL拡張を使っていて、PECL拡張を使うために PEARを使え、ということらしい。
で、Expectライブラリは2013年が最終更新日なので、じゃあ Composer に乗り換えてないのか？

「Expect」 （Packagist）
https://packagist.org/search/?q=expect

どこかにないか。

無いようだ。

## ところでExpectが「what(): std::bad_alloc」を返してきた。

エラー対応はどう書くか。

tamesi22a1.expect

```
#!/usr/bin/expect --
cd /home/★user/shogi/ukamuse_sdt4/bin
set timeout -1
spawn ./apery
send "usi\n"
expect -ex "usiok"
send "isready\n"
expect -ex "readyok"
send "newgame\n"
send "position ${argv}\n"
send "go\n"
expect -re "bestmove.*" {
  exit
} ".*bad_alloc.*" {
  spawn echo "error 3\n"
}
```

expect の「-re」オプションは正規表現らしい。本当に正規表現だろうか？
こんな書き方でいいのだろうか？

動いてはいる。エラーが出たときの流れもテストしないといけないが……。

```
  spawn echo "success 3\n"
```

の出力結果が

```
spawn echo success 3
```

なんだが、理解しがたい。

あと、「-re」オプションは正規表現じゃなくて、ワイルドカードが使える、ぐらいの意味のようだ。
だから「bestmove.*」ではなく「bestmove*」と書く。

## echo の代わりに 「send_user」

```
send_user "fish 4\n"
```

と書いたところは

```
fish 4
```

と出てくる。どうもコンソールに出てくるようだ。じゃあ、標準出力に投げるには？

標準出力に投げなくても、Ｗｅｂページに表示してくれたのでいいか。

まとめると　こうなる。

tamesi22a1.expect

```
#!/usr/bin/expect --
cd /home/★user/shogi/ukamuse_sdt4/bin
set timeout -1
spawn ./apery
send "usi\n"
expect -ex "usiok"
send "isready\n"
expect -ex "readyok"
send "usinewgame\nposition ${argv}\ngo\n"
expect -re "bestmove*" {
  exit
} "checkmate nomate *" {
  send "quit\n"
  send_user "successful 0.0\n"
} "*info time 1*" {
  send "quit\n"
  send_user "error 3.1\n"
} "checkmate timeout *" {
  send "quit\n"
  send_user "error 3.2\n"
} "*bad_alloc*" {
  send "quit\n"
  send_user "error 3.3\n"
} "*Out of memory*" {
  send "quit\n"
  send_user "error 3.4\n"
} "*recursively*" {
  send_user "error 3.5\n"
} "checkmate *" {
  send "quit\n"
  send_user "successful 1.0\n"
}
```

エラーハンドリングをがんばって書いたんだが、

```
what(): std::bad_alloc
```

とか画面に出力されて　ショックを受けている。

# レスポンス時間を測ろう

- (1)ページにアクセスして、すぐレスポンスする時間
- (2)ページにアクセスして、浮かむ瀬を起動する振りをして起動せず、うそレスポンスを返す時間
- (3)ページにアクセスして、浮かむ瀬を起動し、指し手を返す時間

の３つも測れば贅沢だろう。方法としては全部 PHP にやらせればいいのではないか。
PHP にストップウォッチはあっただろうか？

## PHPのストップウォッチ

「PHP – STARTTIME() STOPTIME($TIME) – ストップウォッチ」 （aoringo works）
http://ao-works.net/blog/808

これで十分だろう。

時間測定は tamesi23 さんにしよう。

tamesi23a1.php

```
<?php
$time=microtime(true);
echo '<br />' . (microtime(true) - $time);
```

tamesi23a2.php

```
<?php
$time=microtime(true);
exec('/usr/bin/expect /home/★user/shogi/expect_service/tamesi23a2.expect '.urldecode($_SERVER['QUERY_STRING']),$o);echo $o[count($o)-1];
echo '<br />' . (microtime(true) - $time);
```

tamesi23a3.php

```
<?php
$time=microtime(true);
exec('/usr/bin/expect /home/★user/shogi/expect_service/tamesi23a3.expect '.urldecode($_SERVER['QUERY_STRING']),$o);echo $o[count($o)-1];
echo '<br />' . (microtime(true) - $time);
```

tamesi23a2.expect

```
#!/usr/bin/expect --
cd /home/★user/shogi/ukamuse_sdt4/bin
set timeout -1
send_user "bestmove XXXX ponder XXXX"
exit
```

ダミー文字列を返している。

tamesi23a3.expect

```
#!/usr/bin/expect --
cd /home/★user/shogi/ukamuse_sdt4/bin
set timeout -1
spawn ./apery
send "usi\n"
expect -ex "usiok"
send "isready\n"
expect -ex "readyok"
send "usinewgame\nposition ${argv}\ngo\n"
expect -re "bestmove*" {
  exit
} "checkmate nomate *" {
  send "quit\n"
  send_user "successful 0.0\n"
} "*info time 1*" {
  send "quit\n"
  send_user "error 3.1\n"
} "checkmate timeout *" {
  send "quit\n"
  send_user "error 3.2\n"
} "*bad_alloc*" {
  send "quit\n"
  send_user "error 3.3\n"
} "*Out of memory*" {
  send "quit\n"
  send_user "error 3.4\n"
} "*recursively*" {
  send_user "error 3.5\n"
} "checkmate *" {
  send "quit\n"
  send_user "successful 1.0\n"
}
```

テストは　このソースで行うとする。

１０回ずつ計測すればいいだろう。

アクセスするときに使うURLはこれ。

```
http://★.★.★.★/tamesi23a★.php?sfen lnsgkgsnl/1r5b1/ppppppppp/9/9/9/PPPPPPPPP/1B5R1/LNSGKGSNL b - 1 moves
```

本将棋の平手初期局面で、向かって下側（手前側）からスタート。

キャッシュ・ページを見ているかもしれない。どうしたものか。 **あっ、そうだ！**

テスト回数に応じて

```
http://～略～ b - 1 moves
                 ~
                 ※この数字
```

を増やしていくものとする。何手目を表す数字だが、多分、誰も使ってないだろ。

## 計測結果

a1(No.1)→a2(No.1)→a3(No.1)→a1(No.2) の順で計測。

a1

|No|Time|
|--:|---|
|1|2.0027160644531E-5|
|2|1.0013580322266E-5|
|3|1.1205673217773E-5|
|4|1.3828277587891E-5|
|5|1.0013580322266E-5|
|6|3.2901763916016E-5|
|7|4.3153762817383E-5|
|8|1.9073486328125E-5|
|9|1.2874603271484E-5|
|10|1.2874603271484E-5|

a2

|No|Time|
|--:|---|
|1|bestmove XXXX ponder XXXX 0.021795034408569|
|2|bestmove XXXX ponder XXXX 0.022739887237549|
|3|bestmove XXXX ponder XXXX 0.022423982620239|
|4|bestmove XXXX ponder XXXX 0.019282102584839|
|5|bestmove XXXX ponder XXXX 0.029183149337769|
|6|bestmove XXXX ponder XXXX 0.037684917449951|
|7|bestmove XXXX ponder XXXX 0.033998966217041|
|8|bestmove XXXX ponder XXXX 0.030500888824463|
|9|bestmove XXXX ponder XXXX 0.02174711227417|
|10|bestmove XXXX ponder XXXX 0.017857074737549|

a3

|No|Time|
|--:|---|
|1|bestmove 2g2f ponder 1c1d 3.8728079795837|
|2|bestmove 2g2f ponder 3c3d 3.7108631134033|
|3|bestmove 2g2f ponder 1c1d 3.6325130462646|
|4|bestmove 2g2f ponder 8c8d 3.0715250968933|
|5|bestmove 2g2f 3.605082988739|
|6|bestmove 2g2f ponder 8c8d 3.1143889427185|
|7|bestmove 2g2f ponder 3c3d 3.1693458557129|
|8|bestmove 2g2f ponder 8c8d 3.0223209857941|
|9|bestmove 2g2f ponder 3c3d 3.7790269851685|
|10|bestmove 2g2f ponder 8c8d 4.1685428619385|

## microtime の精度はマイクロ秒のようだ。

「microtime」 （PHPマニュアル）
http://php.net/manual/ja/function.microtime.php

見やすくしてみよう。

a1

|No|Time|
|--:|---|
|1|0.000020|
|2|0.000010|
|3|0.000011|
|4|0.000013|
|5|0.000010|
|6|0.000032|
|7|0.000043|
|8|0.000019|
|9|0.000012|
|10|0.000012|

a2

|No|Time|
|--:|---|
|1|bestmove XXXX ponder XXXX 0.021795|
|2|bestmove XXXX ponder XXXX 0.022739|
|3|bestmove XXXX ponder XXXX 0.022423|
|4|bestmove XXXX ponder XXXX 0.019282|
|5|bestmove XXXX ponder XXXX 0.029183|
|6|bestmove XXXX ponder XXXX 0.037684|
|7|bestmove XXXX ponder XXXX 0.033998|
|8|bestmove XXXX ponder XXXX 0.030500|
|9|bestmove XXXX ponder XXXX 0.021747|
|10|bestmove XXXX ponder XXXX 0.017857|

a3

|No|Time|
|--:|---|
|1|bestmove 2g2f ponder 1c1d 3.872807|
|2|bestmove 2g2f ponder 3c3d 3.710863|
|3|bestmove 2g2f ponder 1c1d 3.632513|
|4|bestmove 2g2f ponder 8c8d 3.071525|
|5|bestmove 2g2f 3.605082|
|6|bestmove 2g2f ponder 8c8d 3.114388|
|7|bestmove 2g2f ponder 3c3d 3.169345|
|8|bestmove 2g2f ponder 8c8d 3.022320|
|9|bestmove 2g2f ponder 3c3d 3.779026|
|10|bestmove 2g2f ponder 8c8d 4.168542|

なんか、ぱっと見、

- a1 は 0.00001 秒、
- a2 は 0.02 秒、
- a3 は 3.5 秒

ぐらいだな。桁が違う。

浮かむ瀬さんの isready → readyok 時間を調査するか。
ビットボードの初期化とか、評価値ファイルの読み込みとか、やってるんだろうけど。

とりあえず　中央値を出しておこう。スプレッドシート開くのも面倒なので　手作業で。

a1

|No|Time|
|--:|---|
|2|0.000010|
|5|0.000010|
|3|0.000011|
|9|0.000012|
|10|0.000012 <---- |
|4|0.000013 <---- |
|8|0.000019|
|1|0.000020|
|6|0.000032|
|7|0.000043|

はいっ！ およそ **0.0000125 秒**。

a2

|No|Time|
|--:|---|
|10|bestmove XXXX ponder XXXX 0.017857|
|4|bestmove XXXX ponder XXXX 0.019282|
|9|bestmove XXXX ponder XXXX 0.021747|
|1|bestmove XXXX ponder XXXX 0.021795|
|3|bestmove XXXX ponder XXXX 0.022423 <---- |
|2|bestmove XXXX ponder XXXX 0.022739 <---- |
|5|bestmove XXXX ponder XXXX 0.029183|
|8|bestmove XXXX ponder XXXX 0.030500|
|7|bestmove XXXX ponder XXXX 0.033998|
|6|bestmove XXXX ponder XXXX 0.037684|

はいっ！ およそ **0.022581** 秒。

a3

|No|Time|
|--:|---|
|8|bestmove 2g2f ponder 8c8d 3.022320|
|4|bestmove 2g2f ponder 8c8d 3.071525|
|6|bestmove 2g2f ponder 8c8d 3.114388|
|7|bestmove 2g2f ponder 3c3d 3.169345|
|5|bestmove 2g2f 3.605082 <---- |
|3|bestmove 2g2f ponder 1c1d 3.632513 <---- |
|2|bestmove 2g2f ponder 3c3d 3.710863|
|9|bestmove 2g2f ponder 3c3d 3.779026|
|1|bestmove 2g2f ponder 1c1d 3.872807|
|10|bestmove 2g2f ponder 8c8d 4.168542|

はいっ！ およそ **3.6187975** 秒。

#次は話ががらりと変わるので、次の記事に移る