花札のbitboard

ここで花札といっているのは一組48枚で、12か月折々の花とかが4枚ずつに書き込まれているあれのことです。全ての札を区別して扱うとして、それぞれの札に0~47の番号を割り振ると、札の有無をuint64_tで表すことができます。『手札にi番目の札がある⇔i番目のビットが立っている』みたいなやつです。花札には多くの遊び方がありますが、以降こいこいの話をします。

合法手生成

手札がuint64_t上でbitboard表現されているとして、以下のようなコードで手札を全列挙できます。わかりますね。

void func1(uint64_t hand) {
	for (unsigned long i = 0; _BitScanForward64(&i, hand); hand &= hand - 1) {
		cout << i << "番目の札を持っている。" << endl;
	}
}

手札を場に出すとき、同じ月の札が場に2枚以上あったらどちらの札を取るか選ばなければいけないため、手札を全列挙しただけでは合法手を全列挙したことにはなりません。ここで場札もuint64_t上でbitboard表現されているとして、合法手の全列挙は以下のように書けます。

void func2(uint64_t hand, uint64_t field) {
	for (unsigned long i = 0; _BitScanForward64(&i, hand); hand &= hand - 1) {
		const uint64_t mask = 0b1111ULL << (i & 0b111100ULL);
		uint64_t capture = field & mask;
		if (capture == 0) {
			cout << i << "番目の札を場に出す。" << endl;
		}
		else {
			for (unsigned long j = 0; _BitScanForward64(&j, capture); capture &= capture - 1) {
				cout << i << "番目の札を場に出し、場にある" << j << "番目の札と合わせて取る。" << endl;
			}
		}
	}
}

役の判定

取った札がuint64_t上でbitboard表現されているとして、役が成立しているかどうかはマジックナンバーとandしてpopcountすればわかります。例えば、蝶は20番目、猪は24番目、鹿は36番目の札のとき、猪鹿蝶のためのマジックナンバー2^{20}+2^{24}+2^{36}です。

局面の対称性について

一般に、ゲームの思考エンジンを作るときには、同一視できる局面を同一視すると色々嬉しいときがあります。こいこいでもいくつかの点でそれができます。まず、同じ月のカス札2枚(12月だけは3枚)は同一視できます。また、4月と5月も同一視できます。なぜならどちらも種・短冊・カス・カスで、それ以外の役に使われないからです。(またちなみに、花見酒を採用しないルールならば1月と3月も同一視できます。1月と3月は赤短や三光とかの役に使われますが、花見酒以外は一緒くたにして数えるからです)

任天堂 花札 都の花 黒

任天堂 花札 都の花 黒

  • メディア: おもちゃ&ホビー