前の記事 : 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」というのがトータル・タイムなのがつらい。出力中でも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と同じだぜ☆m9(^~^)!<br />';
echo '<a href="http://★.★.★.★/tamesi21.php?' . urldecode( $QStr ) . '">http://★.★.★.★/tamesi21.php?' . urldecode( $QStr ) . '</a><br />';
echo '浮かむ瀬さん(^q^) よろしくたのんますだぜ☆(^▽^)v<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秒ぐらいで反応するんだが、画面に出るまで5秒ぐらいかかって遅い。高速化を検討しよう。
高速化を検討
ゴールは 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/
中身のソースは バリバリのC言語だが……。
「Installation」 (PHPマニュアル)
http://php.net/manual/en/expect.installation.php
PHP は入ってないらしい。
「Installation of PECL extensions」 (PHPマニュアル)
http://php.net/manual/en/install.pecl.php
5年前のマニュアルが最新か。
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
と出てくる。どうもコンソールに出てくるようだ。じゃあ、標準出力に投げるには?
標準出力に投げなくても、Webページに表示してくれたのでいいか。
まとめると こうなる。
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)ページにアクセスして、浮かむ瀬を起動し、指し手を返す時間
の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"
}
テストは このソースで行うとする。
10回ずつ計測すればいいだろう。
アクセスするときに使う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 秒。
#次は話ががらりと変わるので、次の記事に移る