将棋ソフトの作り方

シェアする

  • このエントリーをはてなブックマークに追加

こんばんは、ずいぶんと更新を放置していましたが、お久しぶりです。

最近はバイトとかが忙しかったのですが、落ち着いたので徐々に更新頻度を上げていこうとおもっています。

現在というか本日は、鏡音V4Xのためのカバー用音源の作成とちょっとしたプログラミングをしていました。

カバー用音源は、とある女性歌手の歌をリンちゃんwarmとpowerでカバーしようと思っているものです。ギターやベースは弾いて録音したいと思っていますが、どうなるかはまだ不明。

鏡音V4Xが発売されたらそっちの記事も詳しく書くつもりです。

さて、少し前に行われた「将棋電王トーナメント」に触発されて、現在、将棋ソフトを開発中であります。

しかも、将棋電王トーナメント4位の「やねうら王」作者であるやねうらおさんが、「C++言語で1000行程度で強い将棋ソフト(やねうら王mini)を作ってソースを公開して解説記事を書く」と公言されており、今年中にソースと解説記事が全て公開される予定だそうです。

(やねうら王公式サイト : http://yaneuraou.yaneu.com/ 現在すでに解説記事が2つ公開されています)

ということで、せっかくなので、プログラミング経験は一応あるけど将棋ソフトなんで大それたものは作ったことが無いという数日前の私のような人に向けて、作り方を解説しようと思います。

ちなみに私はイチから自分で作りつつ、やねうら王miniが公開されたらその改造もしてみたいと思っています。

将棋ソフトの概要

今回はざっくりと概要だけ書きたいと思います。

将棋ソフトに求められる機能は、「局面の記憶、合法手の列挙、指し手の選択・出力、指し手で盤面を更新、勝ち負けの判断」の他に、「盤面の表示、ユーザーからの指し手の入力受け付け(マウスによる盤面操作で入力したい)、入力が合法かどうか判断」など、たくさんあります。

【用語解説】

合法:その指し手が将棋のルール上可能であること

合法手:合法な指し手

実は将棋ソフトには、後半の機能を全て代わりにやってくれる「将棋所」というソフトがあります。

そのため、将棋ソフト自体は「将棋所から現在の局面を受け取る」「現局面で可能な指し手から良いと思う手を選んで将棋所に返す」「可能な指し手が無い(または逆転不可能と判断した)場合は負けを宣言」の3つの機能を実装するだけでいいのです。

将棋の詳しいルールに関しては次回以降で書きます。(結構難しいです)

将棋所とソフトのやりとりは、標準入出力(printf()とかscanf()とかで読み書きするやつ)の上で、「USIプロトコル」という約束事にのっとった書き方で行います。(USIプロトコルについても次回以降で詳しく書きます。)

例えば、

将棋所「usi(USIプロトコルで喋るでー。自己紹介よろしく)」
ソフト「id name XXX(俺はXXXというもんや)」
ソフト「id author Name(Nameという人が作ったんやで)」
ソフト「usiok(自己紹介は終了や)」
将棋所「isready(準備良いか?)」
ソフト「readyok(準備OKやで)」
将棋所「usinewgame(新しい対局始めるで)」
将棋所「position startpos(現在は初期局面やで)」
将棋所「go(思考開始!)」
……ソフト思考中……
ソフト「bestmove 7g7f(7七の駒を7六に動かす手(7六歩)や)」
……対局相手が指し手を示す。例えば8四歩とする……
将棋所「position startpos moves 7g7f 8c8d(現在の局面は、初期局面から7七→7六、8三→8四って動かした局面やで)」
……ソフト思考中……
ソフト「bestmove 6g6f(6七の駒を6六に動かす手(6六歩)や)」

みたいな感じでソフトと将棋所との間で会話が繰り広げられます(細かいところは省略したりしていますが、これだけの理解でとりあえず動きます)。

したがって、scanf("%s", str); などで入力を待ち、入力文字列("usi"や"isready"、"usinewgame"など)に応じて(switch-case文などで分岐して)返すべき文字列を返すようにすればOK。

とりあえず今回はこのあたりにしておいて、最後に私が作っているソフトの、将棋所と通信する部分のコードを簡略化したものを掲載しておきます。言語はC#です。

多分このコードの解説はしませんが。

static void Main(string[] args)
{
	client();
}

public static void client()
{
	while (true)
	{
		string[] command = Console.ReadLine().Split(' ');
		switch (command[0])
		{
			case "usi":
				{
					Console.WriteLine("id name ProgramName");
					Console.WriteLine("id author Author Name");
					Console.WriteLine("usiok");
					break;
				}
			case "isready":
				{
					Console.WriteLine("readyok");
					break;
				}
			case "usinewgame":
				{
					// 対局状態に突入
					GameCommunication();
					break;
				}
		}
	}
}

public static void GameCommunication()
{
	Mask mask = new Mask(false);
	AI ai = new AI();
	Board board = new Board(mask);
	while (true)
	{
		string[] command = Console.ReadLine().Split(' ');
		switch (command[0])
		{
			case "position":
				{
					int count = 3;
					if (command[1] == "startpos")
					{
						board = new Board(mask);
					}
					for (int i = count; i < command.Length; i++)
					{
						// movesの内容にしたがって盤面を更新する
						board.Action(new Move(board, command[i]), mask);
					}
					//board.Show();
					break;
				}
			case "go":
				{
					// 思考開始、bestmoveを返す。bestmove 8h2b+など。
					// bestmove resign で投了。詰まされたら必ず投了すること。
					Move move = ai.Think(board, mask);
					if (move == null)
					{
						Console.WriteLine("bestmove resign");
					}
					else
					{
						board.Show();
						string bestmove = "bestmove " + move.ToUsiString();
						Console.WriteLine(bestmove);
					}
					break;
				}
		}
	}
}
スポンサーリンク
レクタングル(大)
レクタングル(大)

シェアする

  • このエントリーをはてなブックマークに追加

フォローする