コンピューター囲碁を作ろうぜ☆(^~^)?<書きかけ>

読了目安:20分

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 コンピューター囲碁を作るかだぜ☆
↓ググれば なんでも載ってるだろ……☆」

Rust で TCP/IP ソケット通信をする際のモデル

KIFUWARABE_80x100x8_01_Futu.gif
「 コンピューター囲碁のポート番号は 9696 だったかだぜ☆?」

C:\Users\むずでょ\source\repos\kifuwarabe-air2019

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 ↑ソース・レポジトリ作っておくわよ」

kifuwarabe-air2019

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ギットハブも用意しておいたぜ☆」

Rust

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑近年 Rustは仕様が変わったみたいだし見ておくかだぜ☆」

book

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑説明書はこれかだぜ☆」

installation

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑Rustのインストールには rustup を使えと書いてあるぜ☆
わたしは既に古いのが入っているから アップグレードだな☆」

20190912igo1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑Visual Studio Code には Terminal も付いている☆ Powershell でやってしまおうぜ☆」

20190912igo2b1.png

rustc --version

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑よし、最新版になったぜ☆」

Hello, Cargo!

cargo --version

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑まあ 基本 cargo を使うんだけど☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 きふわらべちゃんのプロジェクトを作りましょう!」

20190912igo3b1.png

cargo new kifuwarabe-air2019

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑はい☆」

20190912igo4b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ Cargo.toml の内容は何もいじらなくても書かれていたぜ☆ 完璧だぜ☆」

20190912igo5b1.png

cargo build

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ .exe ファイルも作られているな☆」

20190912igo6b1.png

cargo run

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ ちゃんと実行もされるぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 Visual Studio Code で十分だな☆」

20190912igo7.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ しかし ソース・レポジトリのコードを GitHub の ローカル・レポジトリにコピーするのは めんどくさいよな☆」

go-to-git.ps1

# +
# | UTF-8 with BOM (Powershell)
# |
# | Folder copy.
# +

# +
# | フォルダをゴミ箱に移動する
# +
function Remove-FolderToRecycleBin($dir) {
    if (Test-Path $dir -PathType Container) {
        $fullpath = (Get-Item $dir).FullName

        # +
        # | こんな変なパスぜったい間違う☆(^~^)チェックだぜ☆(^~^)
        # +
        if ($fullpath -cmatch ".*\\kifuwarabe-air2019\\kifuwarabe-air2019") {
            Write-Host "Remove          | [$fullpath] directory."
            Remove-Item $fullpath -Recurse
        }
        else {
            Write-Host "Ignore          | [$fullpath] directory."
        }
    }
    else {
        Write-Host "Ignore          | [$dir] is not directory or not found."
    }
}

$sr = "C:\Users\むずでょ\source\repos\kifuwarabe-air2019\kifuwarabe-air2019"
$ds = "C:\Users\むずでょ\Documents\GitHub\kifuwarabe-air2019\kifuwarabe-air2019"

Remove-FolderToRecycleBin($ds)

Write-Host "Copy            | [$sr] --to--> [$ds]."
Copy-Item $sr -destination $ds -recurse

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ 必死こいて Powershell 書く☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 powershell ./go-to-git.ps1 と打鍵するのが うんこ じゃないか☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Visual studio code の Explorer で右クリックして File explorer 開いて .ps1 ファイルをダブルクリックするからOK☆」

20190912igo8.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ あとは Git hub desktop でなんとかする☆」

cargo check

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ コーディングのチェックをしてくれたりするのだろうか☆? あとで使おう☆」

cargo build --release

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ デバッグを外して .exe を作るには --release を付ければいいらしい☆ これ1つ忘れるだけで大会では クソとノーマルの差が分かれる☆」

ロガーを用意しろだぜ☆(^~^)

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ロギングできるかどうかで プログラマーは クソとノーマルに大きく分かれるのに 公式のマニュアルに書いてないな☆」

Rust:logでログ出力を行う
Rustでのロギング
Crate log
Configure Logging

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ここらへんの連中は ログをファイルに書き込まないのだろうか☆?
stderr へのストリーム出力から ファイルへリダイレクトが基本かだぜ☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 遅~いロガーを掴んだら コンピューターは弱くなるわよ?」

env_logger

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 じゃあ env_logger 一択な気がするな……☆ 使ってみるかだぜ☆?」

20190912igo9b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 こんな風に 真似して Cargo.toml を書いていけばいい☆
1990年代のジャパンなら 友だちの友だちをたどっていけば パソコンオタクの眼鏡のお兄さんがどこかに居て
家までやってきて パソコンのコマンドの叩き方とか教えてくれたが、
2019年頃のジャパンでは 学校のパソコン部に入ったのに何もせずに卒業して ブログのエントリを書いて
いいね! が付くのが流行りみたいだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 何も いいね! ではないわよね。 何が いいの かしら?」

KIFUWARABE_80x100x8_01_Futu.gif
「 承認欲求が満たされたのだろう☆」

main.rs

#[macro_use]
extern crate log;
extern crate env_logger;

fn main() {
    env_logger::init();

    println!("Hello, println!");
    trace!("Hello, trace!");
    debug!("Hello, debug!");
    info!("Hello, info!");
    warn!("Hello, warn!");
    error!("Hello, error!");
}

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑こういうコードがあるとして……☆」

20190912igo10.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑環境変数を使って これだけ使い分けれれば十分だろう……☆
なんか Hello, println! の出るタイミングがバラバラだな……、非同期か☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 で、 PowerShell って どうやって ストリームをリダイレクトするんだぜ☆?」

PowerShellでのエラーハンドリングについて

command 2>&1 >> ./kifuwarabe-air2019.log

KIFUWARABE_80x100x8_01_Futu.gif
「 ↑で いいのでは☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ほんとか☆?」

20190912igo11.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑コマンドの一覧が ログ・ファイルに書き出された……☆ ということは☆」

cargo run 2>&1 >> ./kifuwarabe-air2019.log

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑こうかだぜ☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 それだと cargo の引数になるんじゃない?」

20190912igo12.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 一応 >> が利いたのか ファイルに 追加書き込みされているが、標準出力に何もでなくなったぜ☆」

cargo run 2>> ./kifuwarabe-air2019.log

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 だったら単に 標準エラーの2 を追加書き込みのリダイレクト >> でファイルに送る指定だけする☆
これで平和だぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 それだと 標準出力にエラーが出てこない☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 リダイレクト先を2つに増やすことはできないんじゃないか☆?
とりあえず ログを取るときは しばらくこれで☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 コマンドの打鍵がめんどくさくない?」

/*
# - Log level.
#$env:RUST_LOG = "trace"
#$env:RUST_LOG = "debug"
$env:RUST_LOG = "info"
#$env:RUST_LOG = "warn"
#$env:RUST_LOG = "error"

# - Log redirect.
cargo run 2>> ./kifuwarabe-air2019.log
 */

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 main.rs の冒頭にコメントを書いておいて これを貼り付ければいいだろ……☆」

TCP/IP 通信

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 次は通信書くか……☆」

Rust のお試しコードを実行する: cargo run --example

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑rust には サンプル・プログラムの書き方がある☆」

20190914igo13b1.png

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 examples ディレクトリの下にあるファイルは cargo run --example を使って実行できる☆」

Rust で TCP/IP ソケット通信をする際のモデル
Rustにっき/8日目・TCPサーバ

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 接続に失敗するな……、ポート番号調べてみるか☆」

### Linux
lsof -i:3000

### Windows
netstat -aon | findstr 3000

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 何も出てこないな☆ 3000番ポートは空いてるはず☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 接続される側で 待ち受けているサーバーが無いのだから、接続できるわけないんじゃないの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 だったら そういうサンプルだと説明が欲しいぜ☆
サーバー書くか……☆」

クライアントとサーバー

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 接続するだけなら 次のように書けばいい☆」

examples/server.rs

/*
# Current directory
# cd ./kifuwarabe-air2019
cd C:\Users\むずでょ\source\repos\kifuwarabe-air2019\kifuwarabe-air2019

cargo run --example server
 */
use std::net::{TcpListener, TcpStream, ToSocketAddrs};
use std::io::{BufRead, BufReader, BufWriter, Error, Read, Write};
use std::thread;
/**
 * See: [Rustにっき/8日目・TCPサーバ](https://cha-shu00.hatenablog.com/entry/2019/03/02/174532)
 */
fn main(){
    let host = "localhost";
    let port = 9696;
    let url = format!("{}:{}", host, port);
    let mut addrs = url.to_socket_addrs().unwrap();

    // Change to ip v4.
    if let Some(addr) = addrs.find(|x| (*x).is_ipv4()) {
        // Success addr:127.0.0.1:9696
        println!("Success sever-addr:{}", addr);

        // Wait for connection.
        let listener = TcpListener::bind(addr).expect("Error. failed to bind.");
        for streams in listener.incoming() {
            match streams {
                Err(e) => { eprintln!("error: {}", e)},
                Ok(stream) => {
                    println!("Create the thread.")
                    /*
                    // TODO: Create the thread.
                    thread::spawn(move || {
                        handler(stream).unwrap_or_else(|error| eprintln!("{:?}", error));
                    });
                    */
                }
            }
        }
   } else {
        eprintln!("Invalid Host:Port Number");
    }
}

examples/client.rs

/*
# Current directory
# cd ./kifuwarabe-air2019
cd C:\Users\むずでょ\source\repos\kifuwarabe-air2019\kifuwarabe-air2019

cargo run --example client
 */
use std::net::{TcpStream, ToSocketAddrs};
use std::io::{BufRead, BufReader, BufWriter, Write};

/**
 * See: [Rust で TCP/IP ソケット通信をする際のモデル](https://qiita.com/7ma7X/items/479ad9025a3368c2348f)
 */
fn main(){
    let host = "localhost";
    let port = 9696;
    let url = format!("{}:{}", host, port);
    let mut addrs = url.to_socket_addrs().unwrap();

    // Change to ip v4.
    if let Some(addr) = addrs.find(|x| (*x).is_ipv4()) {
        // Success addr:127.0.0.1:3000
        println!("Success client-addr:{}", addr);

        match TcpStream::connect(addr) {
            Err(_) => {
                println!("Connection NG.");
            }
            Ok(stream) => {
                println!("Connection Ok.");

                // Buffering.
                let mut reader = BufReader::new(&stream);
                let mut writer = BufWriter::new(&stream);

                read_something(&mut reader);
                write_something(&mut writer, "hoge");
            }
        }
   } else {
        eprintln!("Invalid Host:Port Number");
    }
}

fn read_something (reader: &mut BufReader<&TcpStream>) {
  let mut msg = String::new();
  reader.read_line(&mut msg).expect("RECEIVE FAILURE!!!");
  // read_line は改行文字まで読む。
  // 他のread系のメソッドもある (https://doc.rust-lang.org/std/io/trait.BufRead.html)
  println!("{}", msg);
}

fn write_something (writer: &mut BufWriter<&TcpStream>, comment: &str) {
  let msg = format!("MESSAGE: {}\n", comment);
  // 送る側もたぶん改行文字を付けたほうがよいでしょう。
  writer.write(msg.as_bytes()).expect("SEND FAILURE!!!");
  writer.flush().unwrap();
}

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 で、使うには server.rs を実行して待機させておいてから、 client.rs で接続する☆
Visual studio code は Terminal ビューを1つしか出せないかもしれないので、
Visual studio code の2つ目を起動して Terminal ビューを使う☆」

20190916igo14a1b1.png

KIFUWARABE_80x100x8_01_Futu.gif
「 そんな使い方で 何かが 混線しないか 知らんけどな☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 コンピューター囲碁の通信プログラムって どんなもんだぜ☆?」

GTPではなく、nngs通信プロトコル

通信対局規約
19. The Go Text Protocol
GTP - Go Text Protocol
Go Text Protocol

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Go text protocol だろう☆ 大会で使うコマンドは6つしかない☆」

CgfGoBan and Nngs_try
ASCIIコード表

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑例えば Cgf Go Ban の cgf_pipe.cpp というファイルに実装が書いてあるぜ☆
ASCIIコード表は助けになるだろう☆」

  • コマンドは Ascii文字。
  • コマンドは 2048文字未満。これを超えたらエラー。
  • コマンドは \n (Asciiコード 10)で終了。
  • \n を除く 32未満のAsciiコードはすべて無視。例えば \r (Asciiコード 13) などを無視。
  • \t (水平タブ; Asciiコード 9) を 半角スペース (Asciiコード 32) に置換しているコードがあるが、これは働いてないと思う。
  • コマンド先頭が # ならコメント行だから無視。まだコマンドは送られてくる。
  • 空行(\n だけの行)は無視。まだコマンドは送られてくる。

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 あと、プロトコルの通信ログを見ないと分からないところだが☆、」

19. The Go Text Protocol

virihaure 462% ./gnugo --mode gtp
1 boardsize 7
=1

2 clear_board
=2

3 play black D5
=3

4 genmove white
=4 C3

5 play black C3
?5 illegal move

6 play black E3
=6

7 showboard
=7
   A B C D E F G
 7 . . . . . . . 7
 6 . . . . . . . 6
 5 . . + X + . . 5
 4 . . . + . . . 4
 3 . . O . X . . 3
 2 . . . . . . . 2     WHITE (O) has captured 0 stones
 1 . . . . . . . 1     BLACK (X) has captured 0 stones
   A B C D E F G

8 quit
=8

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 サーバーは イコールを付けて ひと桁の数を返してくる☆
イコールと数の2文字だけが飛んで来たら無視しろだぜ☆ 3文字以上なら何か命令かもしれない☆」

  • 4文字目以降を move とする。例えば =4 C3 なら C3 が move。
  • パスは move が pass。 GnuGoは"PASS"と送ってくるから大文字・小文字を区別しないようにする。
  • 投了は move が resign。 SGMPは非対応。nngsは相手が落ちるかも?
  • move の1文字目はアルファベット。筋(列)に対応。I は無いから詰めること。
  • move の2文字目以降は段(行)数。

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Go text protocol は いろいろコマンドがあるんだが 大会では 5つのコマンドしか送んな、ということだし
それだけ 実装すればいいんだが、一応 Cgp Go Ban にあるコマンドを見ておこう☆」

  • boardsize %d\n - ban_size 何路盤。
  • clear_board\n - 盤面を初期化
  • komi %.1f\n - komi, - コミを設定
  • name\n - 名前を要求。
  • version\n - バージョンを要求。
  • genmove black\n - GnuGo に黒番打たせる。
  • genmove white\n - GnuGo に白番打たせる。
  • play black %s\n - GnuGo の盤面に黒石を置く。符号は Q4 みたいなやつ。
  • play white %s\n - GnuGo の盤面に白石を置く。
  • final_status %s\n - GnuGo に死活判定を聞く。符号は Q4 みたいなやつ。結果は次の6つ。
    • alive
    • dead
    • seki
    • white_territory
    • black_territory
    • dame

KIFUWARABE_80x100x8_01_Futu.gif
「 どれも大会で使わなさそう☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 こっちから 通信で送るのは cgf_win.cpp の SendUpdateSetKifu 関数から辿っていけば 見つかるだろう……☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 cgf_term.cpp にいろいろ書いてあるが、 SGMP 使ってそう☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 大会は NNGS 1.1.22 を拡張したプロトコル だから、 NNGS 1.1.22 が何を使ってるか調べるか……☆」

通信対戦について

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 GTP とは また違うのか☆ うーむ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 cgf_wsk.cpp に、nngs への接続プログラムが書かれているぜ☆」

playera側の記録

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 通信ログが上記のようなものだとして……☆」

Send:

telnet nngs.computer-go.jp 9696

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 テルネットで接続すればいいらしい☆」

Send:

playera

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 そのあと有無を言わさず 自分の名前を送るらしい☆」

Receive:

No Name Go Server (NNGS) version 1.1.14

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 次はこんな行が送られてくるから、
No Name Go Server (NNGS) version という文字列を当てに行く☆ 当たれば☆、」

Send:

set client TRUE\n

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 こんな文字列を 有無を言わさず送る☆
大会では "set client FALSE" と書いてあって どうすればいいのか☆」

Receive:

Set client to be True

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 よく分からんが client モードが True になった、と受信すれば……☆」

match %s B %d %d 0\n",nngs_player_name[1-fTurn],ban_size,nngs_minutes

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 matchコマンドで黒番に対局を申し込むのか☆ わたしは白番か☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 対局を申し込まれた playerb の方は☆、」

Match [19x19] in 40 minutes requested with playera as Black.

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑みたいな文字列を受け取る☆
"Match [%dx%d]",ban_size,ban_size みたいな感じで当てに行く☆」

match playerb B 19 40 0

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 そして
"match %s W %d %d 0\n",nngs_player_name[1-fTurn],ban_size,nngs_minutes
みたいな感じの文字列を 有無を言わさず送る☆
わたしのプレイヤー名は playerb、 黒番で 19路、 持ち時間は 40分とか そんな感じだろうか☆」

Received:

accepted.

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 黒番のときは accepted. を受信するとのことだぜ☆ この直後から初手を送れるとのことだぜ☆」

Illegal

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 エラーがあった場合、 illegal という文字が含まれているらしいぜ☆ こうなりゃ終了☆」

sprintf(tmp,"(%s): ",stone_str[1-fTurn]);

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 これはただの表示かだぜ☆」

Pass

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 これを受け取ったら、相手はパス☆
送るのは pass なのに、受け取るのは Pass なのか☆」

T10

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 よく分からんが 指し手 は そのまんま飛んでくるのか? よく分からん☆」

14 |. # # O O # # . # O # # O O O # O O #| 14
13 |# # O # # # . # O O O # # # O O O . O| 13 Last Move: J1
12 |# O O # O . . # # O . O O O O . O O .| 12 #216 O (White)

KIFUWARABE_80x100x8_01_Futu.gif
「 盤の右横に指し手が書いてるんじゃないか☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 なんで そんなところに……☆ よく誤検知しないものだぜ☆ A1 とか B10 とか☆」

Received:

You can check your score

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 自分が pass したあとに相手が pass すると、 Pass は受信せず上のメッセージを受信するみたいだぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif

「 これは プロトコル なのかだぜ☆?!」

Send:

done

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 地を計算するんだが めんどくさいんで done\n を送る☆ すると☆、」

Received:

resigns.

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 というメッセージを受信して 通信完了だぜ☆ 棋譜はサーバーに保存されているはずだぜ☆」

Received:

9 {Game 1: test2 vs test1 : Black resigns. W 10.5 B 6.0}

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 対局が終わると 上記のようなメッセージを受信するので……☆」

strstr(str,"9 {Game") && strstr(str,"resigns.")

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 9 {Gameresigns. を含む行を当てに行き……☆」

sprintf(tmp,"%s vs %s",nngs_player_name[1],nngs_player_name[0]);

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 playera vs playerb みたいな文字列を作って また当てに行き、当たったら 対局終了だぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 JSONでプロトコルを書き直しましょうよ!」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 大会のサーバーと通信することが優先だぜ☆」

Received:

{%s has disconnected}

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 対戦相手が接続を切ったときは 上のメッセージが飛んでくる☆ %s は相手プレイヤー名だぜ☆」

Received:

forfeits on time

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 どちらかの持ち時間が切れた場合は、上のメッセージと同じ行に 両プレイヤー名が載っているはずだぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 こんなけ分かれば 疑似サーバー を作れるだろ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 要点を まとめないとな☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 ユーザー名と パスワードの送信って どうやんの?」

WINGの歩き方 Ver 1.10

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 もっと古い時代に NNGS を使っていた Wing のサイトも見てみるかだぜ☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 パスワードを変える方法はあっても、パスワードを入力する説明は見当たらないな☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 CgfGoBan でパスワードを入れたことは無いだろ☆ パスワード無しでやろうぜ☆」

  • Send: telnet nngs.computer-go.jp 9696
  • Send: playera
  • Received: No Name Go Server (NNGS) version
  • Send: set client TRUE
  • Received: Set client to be True
  • Send: match %s B %d %d 0\n",nngs_player_name[1-fTurn],ban_size,nngs_minutes
  • Received: Match [19x19]
  • Send: match %s W %d %d 0\n",nngs_player_name[1-fTurn],ban_size,nngs_minutes
  • Received: accepted.
  • Received: Illegal
  • Received: Pass
  • Send: T10
  • Send: pass
  • Received: You can check your score
  • Send: done
  • Received: 9 {Game 1: test2 vs test1 : Black resigns. W 10.5 B 6.0}
  • Received: {%s has disconnected}
  • Received: forfeits on time

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 まだ マッチングのところが よく分からんな……☆」

マッチング

通信対戦について

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑説明を1つ1つ読んでいくかだぜ☆」

  • プレイヤがサーバにログイン

Send:

telnet nngs.computer-go.jp 9696

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ログインは 通信プロトコルと関係ないよな☆ 接続を確立するまでは TCP/IP だぜ☆」

KIFUWARABE_80x100x8_01_Futu.gif
「 ここから先は とにかく2人以上が サーバーにぶら下がっているという前提みたいだぜ☆」

  • 一方(例えばplayeraという名前でログインしている)が相手に「match」コマンドで対局を申し込む。playerbという名前のプレイヤに、19路盤、自分が黒番、持ち時間は40分、秒読みなしという条件で申し込む場合には、「match playerb B 19 40 0」というコマンドをサーバに送信する。

Send:

match playerb B 19 40 0

KIFUWARABE_80x100x8_01_Futu.gif
「 相手のプレイヤー名も知っているという前提だな☆ 大会でなら知ってるしな☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 少しずつ 分かってきた感じがするな☆」

  • 相手が同じく「match」コマンドで対局を受けたら対局開始(match playera W 19 40 0)

Send (Opponent):

match playera W 19 40 0

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 サーバー側で シェイクハンド を監視しないといけないな☆」

  • 対局開始

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 具体的なことが分からん☆」

  • 互いに自分の手を送信していく(「d3」というような座標を送信する)

Send:

d3

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 どっちの手番か分かるのかだぜ☆?」

  • 一方のプレイヤが「pass」を送信
  • もう一方のプレイヤが「pass」を送信

Send (Self/Opponent):

pass

Send (Opponent/Self):

pass

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 これは棋譜の上で2者が続けて pass を送信するということだな☆」

  • 双方が「done」コマンドを送信(死に石の情報を送信する必要はありません。また、サーバからのメッセージは無視してください)

Send (Self/Opponent):

done

Send (Opponent/Self):

done

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 これは本当は 郵便囲碁でお互いが 地計算して どれが生き石、どれが死に石 と言い合うところなんだが、
大会では めんどくさいんで スキップしようぜ、という意味合いだぜ☆」

  • それぞれのプログラムはそのコンピュータのモニタ上に、盤面情報、死に石の情報、地の計算結果を表示します
  • プログラムの操作者がお互いに双方のプログラムの表示している結果を比較してください(これまでのRS232C接続の際の終局時と同様です)
  • 双方のプログラムの結果が一致しない時には、審判が不一致部分の判定を行ない、結果を確定します

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 これだけ実装すれば 疑似サーバーは作れそうだな☆」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 しかし、白番が先にログインして 黒番は後から入れ、とか 何か しきたり がなかったかだぜ☆?」

KIFUWARABE_80x100x8_01_Futu.gif
「 Cgf Go Ban のしきたりなんじゃないか☆?」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 2人しか接続してこないという前提で プログラム書けば 早くできるんじゃないの?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 そのあたりの仕様を もう少し詰めようぜ☆」

TCP/IP通信その2

                    // Create the thread.
                    thread::spawn(move || {
                        handler(stream).unwrap_or_else(|error| eprintln!("{:?}", error));
                    });

KIFUWARABE_80x100x8_01_Futu.gif
「 スポーンしたあとは どうすんだぜ☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 Rust でどう書けばいいのか分からん☆ とりあえず quit を送ったらループから抜けるようにするかだぜ☆」

Struct std::io::BufStream
bufstream 0.1.4

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑Read も Write もしたいバッファー・ストリームは どう書けばいいんだぜ☆?」

bufstream = "0.1.4"

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑これを Cargo.toml に書いて☆、」

extern crate bufstream;

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑これを main.rs の冒頭に書いておくかだぜ☆」

                // Buffering.
                let mut reader = BufReader::new(&stream);
                let mut writer = BufWriter::new(&stream);

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑いや、そんなことをしなくても これでいけたのか……☆?」

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 ↑ \n を末尾に付けておかないと read_line が いつまで経っても終わらないぜ☆」

ソケット間で共有するスコープとかない

KITASHIRAKAWA_Chiyuri_80x100x8_01_Futu.gif
「 そうか、クライアントのスレッド と サーバーのスレッド との間は グローバル変数でやりとりするのではなく、
通信して やりとりするのかだぜ☆」

OKAZAKI_Yumemi_80x80x8_02_Syaberu.gif
「 Rust で Postgresql データベースを使えたりしないの?」

<書きかけ>

ツイッターでシェア
みんなに共有、忘れないようにメモ

むずでょ@きふわらべ第29回世界コンピューター将棋選手権一次予選36位

光速のアカウント凍結されちゃったんで……。ゲームプログラムを独習中なんだぜ☆電王戦IIに出た棋士もコンピューターもみんな好きだぜ☆▲(パソコン将棋)WCSC29一次予選36位、SDT5予選42位▲(パソコン囲碁)AI竜星戦予選16位

Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。

また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!

有料記事を販売できるようになりました!

こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?

コメント