Unityによる大富豪の作り方 2 ~ビット演算(応用)~
スポンサードリンク
ビット演算(応用)
前回は基本的なビット演算の紹介でしたが、今回はそれらを使って具体的に階段やグループの判定・作成方法を説明します。
階段かグループか判定する
public readonly static ulong M0001 = 0x1111111111111111; private readonly static ulong M0011 = 0x3333333333333333; private readonly static ulong M0100 = 0x4444444444444444; private readonly static ulong M0101 = 0x5555555555555555; private readonly static ulong M1000 = 0x8888888888888888; //階段かどうか判定する。 public bool IsSequence(ulong bitCard, int cnt) { if (bitCard == 0 || cnt < 3) return false; var head = bitCard; for (int i = 0; i < cnt - 1; i++) { head &= head >> 4; } return CountBit(head) == 1; } //グループかどうか判定する public bool IsGroup(ulong bitCard, int cnt) { if (cnt > 4) return false; var n = (1ul << cnt - 1) * M0001; var qpCard = ToQPCard(bitCard); return (qpCard & n) > 0; } //枚数位置型(QuantityPosition)に変換 public ulong ToQPCard(ulong bitCard) { //2ビットごとの1の数を2進数で表現 var a = ((bitCard & M0101) + ((bitCard >> 1) & M0101)); //4枚あるランク var n4 = a & (a << 2) & M1000; //3枚あるランク var n3 = (a << 2) & (a >> 1) & M0100; n3 |= a & (a << 1) & M0100; //4ビットごとの1の数を2進数で表現(4は除く) var n12 = ((a & M0011) + ((a >> 2) & M0011)) & M0011; if (n3 != 0) { n4 |= n3; //3枚である場合を除く。 n4 |= n12 & ~(n3 >> 1 | n3 >> 2); } else { n4 |= n12; } return n4; }
枚数位置型への変換はわかりづらいので少し解説します。
var a = ((bitCard & M0101) + ((bitCard >> 1) & M0101));
aはbitCardを2ビットごとの1の数を2進数で表現しています。
例えば、bitCardが
1111 1011 0101 1100 0100 のとき、aは
1010 0110 0101 1000 0100 となります。
次に、このaを2ビットごとに足すと4ビットごとの1の数となります(この辺りは1の数を数えるCountBitと同じです)。
そして、4ビットごとの1の数とは各ランクごとのカードの枚数を表しています。
例えば、aが1010 0110 0101 1000 0100のとき、カードの枚数は4枚、3枚、2枚、2枚、1枚となります。
var n4 = a & (a << 2) & M1000;
あるランクが1010であるとき、そのランクのカードが4枚あるということなので4ビット目を1とします。
var n3 = (a << 2) & (a >> 1) & M0100; n3 |= a & (a << 1) & M0100;
あるランクが1001または0110であるとき、そのランクのカードが3枚あるということなので3ビット目を1とします。
var n12 = ((a & M0011) + ((a >> 2) & M0011)) & M0011;
4ビットごとの1の数を2進数で表現し、M0011をANDすることで4を除いています。つまりn12はそのランクの枚数(0~3)を2進数で表しています。
if (n3 != 0) { n4 |= n3; n4 |= n12 & ~(n3 >> 1 | n3 >> 2); } else { n4 |= n12; }
1、2枚の場合はn12から3枚の場合を除いたものを使うことで表現します。
階段を取得
長さが最大になる組み合わせですべての階段を取得
//長さが最大になる組み合わせですべての階段をList<ulong>で取得 public List<ulong> GetSequenceList(ulong bitCard) { var sequences = new List<ulong>(); ulong seq; do { seq = GetLongestSequence(bitCard); if (seq != 0) { sequences.Add(seq); bitCard ^= seq; } } while (seq != 0); return sequences; } //一番長い階段を取得 private ulong GetLongestSequence(ulong bitCard) { var head = bitCard; var max = 0ul; int i = 1; while (head != 0) { if (i >= 3) max = head; head &= bitCard >> (i * 4); i++; } if (max == 0) return 0; else { var b = GetLeastSignificantBit(max); return CreateSequence(b, i - 1); } } //階段を生成 private ulong CreateSequence(ulong head, int cnt = 3) { var seq = head; for (int i = 1; i < cnt; i++) { head <<= 4; seq |= head; } return seq; }
手札から最も長い階段を取得し、そのカードを除いた手札から再度、最大の長さの階段を取得する、ということを繰り返しています。すべての階段(BitCard型)をList<ulong>で取得します。自分が親で、枚数制限がない場合に使います。
例えば手札が♠️4 ♠️5 ♠️6 ♦️6 ♠️7 ♦️7 ♦️8 ♠️9 ♠️10
の場合
[ ♠️4 ♠️5 ♠️6 ♠️7 ] [ ♦️6 ♦️7 ♦️8 ]
を取得します。
指定した枚数の階段をすべて取得
//指定した枚数の階段をすべてList<ulong>で取得 public List<ulong> GetSequenceList(ulong bitCard, int cnt) { var sequences = new List<ulong>(); var head = bitCard; for (int i = 1; i < cnt; i++) { head &= bitCard >> (i * 4); } while (head != 0) { var b = GetLeastSignificantBit(head); sequences.Add(CreateSequence(b, cnt)); head ^= b; } return sequences; }
指定した枚数より多い階段が存在した場合、その枚数で組み合わせ可能な階段をすべて取得します。すべての階段(BitCard型)をList<ulong>で取得します。自分が子で、出せる枚数に制限がある場合に使います。
例えば手札が♠️4 ♠️5 ♠️6 ♠️7 ♠️8
で場の枚数が3の場合
[ ♠️4 ♠️5 ♠️6 ] [ ♠️5 ♠️6 ♠️7 ] [ ♠️6 ♠️7 ♠️8 ]
を取得します。
グループを取得
指定した枚数以上のグループをすべて取得
//指定した枚数以上のグループをすべてList<ulong>で取得 public List<ulong> GetGroupList(ulong bitCard, int cnt) { var groups = new List<ulong>(); var qpGroup = GetGroupByQPCard(bitCard, cnt); ToBitList(qpGroup).ForEach(q=> { groups.Add(ToBitCard(q, bitCard)); }); } //指定した枚数以上のグループを全て枚数位置型で取得 private ulong GetGroupByQPCard(ulong bitCard, int cnt) { var qpCard = ToQPCard(bitCard); int n = cnt - 1; var noless = 16 - (1ul << n); var cntMask = M0001 * noless; return qpCard & cntMask; } //枚数位置型をBitCard型に変換 public ulong ToBitCard(ulong qpCard, ulong bitCard) { var result = 0ul; while (qpCard != 0) { int i = BitScanForward(qpCard); int r = i / 4; result |= bitCard & (15ul << (r * 4)); qpCard &= qpCard - 1; } return result; } //ビットが1の値をそれぞれ分けてList<ulong>で取得 public List<ulong> ToBitList(ulong bit) { var list = new List<ulong>(); while (bit != 0) { var b = GetMostSignificantBit(bit); list.Add(b); bit ^= b; } return list; }
すべてのグループ(BitCard型)をList<ulong>で取得します。自分が子で、出せる枚数に制限がある場合に使います。
例えば手札が♠️3 ♠️5 ♦️5 ♠️6 ♦️6 ♣️6 ♠️8 ♦️8 ♣️8 ❤️8
で場の枚数が3の場合
[ ♠️6 ♦️6 ♣️6 ] [ ♠️8 ♦️8 ♣️8 ❤️8 ]
を取得します。
すべてのグループを取得
//すべてのグループをList<ulong>で取得 public List<ulong> GetGroupList(ulong bitCard) { return GetGroupList(bitCard, 2); }
すべてのグループ(BitCard型)をList<ulong>で取得します。自分が親で、枚数制限がない場合に使います。
例えば手札が♠️3 ♠️5 ♦️5 ♠️6 ♦️6 ♣️6 ♠️8 ♦️8 ♣️8 ❤️8
の場合
[ ♠️5 ♦️5 ] [ ♠️6 ♦️6 ♣️6 ] [ ♠️8 ♦️8 ♣️8 ❤️8 ]
を取得します。
その他
基準よりランクの高いカードを取得
//基準よりランクの高いカードを取得 private readonly static ulong M1111 = 0xffffffffffffffff; public ulong GetHigherBitCard(ulong bitCard, ulong basisCard, bool isRevolutionzlizing) { if (!isRevolutionzlizing) { int n = BitScanReverse(basisCard); int r = n < 0 ? 0 : n / 4 + 1; var noless = M1111 - ((1ul << r * 4) - 1ul); return bitCard & noless; } else { int n = BitScanForward(basisCard); int r = n < 0 ? 0 : n / 4; var nomore = (1ul << r * 4) - 1ul; return bitCard & nomore; } }
基準のカードよりランクが高い(革命時は低い)カードを取得します。場に出せるカードを抽出するときや、相手が自分より強いカードをどれだけ持っているか計算するときに使います。