麻雀の和了判定
Posted feedbacks - Flatten
Nested Hidden書いていたら頭がこんがらがって来ました。実行結果は下記です:
[1, 1, 2, 3, 4, 5, 6, 6, 7, 7, 8, 9, 9, 9] True [1, 2, 4, 4, 5, 6, 7, 8, 9, 7, 8, 9, 4, 4] False [1, 1, 1, 3, 3, 3, 5, 5, 5, 7, 7, 7, 9, 9] True [1, 1, 1, 3, 3, 3, 5, 5, 5, 7, 7, 7, 9, 7] False [1, 1, 1, 3, 3, 3, 5, 5, 5, 7, 7, 7, 9, 8] True
see: ruby Arrayで組み合わせを出力する方法 - 進・日進月歩
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | # -*- coding: utf-8 -*-
def get_set_candidate(hand, num):
result = []
hand = hand[:]
hand.sort()
for i in range(1, len(hand)+1):
if hand.count(i) >= num:
result.append(i)
return result
def clear_set(hand, clear_tile, num):
hand = hand[:]
hand.sort()
for i in range(0, num):
hand.remove(clear_tile)
return hand
def make_comb(list, num):
if num <= 0: return [[]]
size = len(list)
result = []
clonecker = lambda list1, list2: map((lambda i: list1 + i), list2)
for i in range(size-num+1):
result += clonecker([list[i]], make_comb(list[i+1:], num-1))
return result
def all_comb(list):
result = []
for i in range(len(list)+1):
result += make_comb(list, i)
return result
def check_sequence(hand, m=3):
result = []
hand = hand[:]
hand.sort()
for i in range(0, len(hand)/m):
j = hand.pop()
try:
hand.remove(j-1)
hand.remove(j-2)
except:
return False
return True
def check_winning(hand, m=3, h=2):
for i in get_set_candidate(hand, h):
hand2 = clear_set(hand, i, h)
for j in all_comb(get_set_candidate(hand2, m)):
hand3 = hand2[:]
for k in j:
hand3 = clear_set(hand3, k, m)
if check_sequence(hand3, m): return True
return False
hand1 = [1,1,2,3,4,5,6,6,7,7,8,9,9,9]
hand2 = [1,2,4,4,5,6,7,8,9,7,8,9,4,4]
hand3 = [1,1,1,3,3,3,5,5,5,7,7,7,9,9]
hand4 = [1,1,1,3,3,3,5,5,5,7,7,7,9,7]
hand5 = [1,1,1,3,3,3,5,5,5,7,7,7,9,8]
print hand1, check_winning(hand1)
print hand2, check_winning(hand2)
print hand3, check_winning(hand3)
print hand4, check_winning(hand4)
print hand5, check_winning(hand5)
|
Squeak Smalltalk で。
幅優先で探索して和了パターンが1つでも見つかれば true を返します。牌は1文字の種類とインデックス文字列の組み合わせで与えます。なお、インデックス文字列の文字数が最大値(M)の文字列の文字数に満たない場合、あらかじめ左側を「0」で埋めて文字数を合わせて与える必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | | m n h M L 対子群 手牌 刻子群 同種牌群 和了パターン 残り 待ち行列 刻順子群 |
m := 3. n := 4. h := 2. M := 9. L := 4.
手牌 := #(a1 a1 a2 a3 a4 a5 a6 a6 a7 a7 a8 a9 a9 a9).
self assert: (手牌 allSatisfy: [:each | each size = (M printString size + 1)]).
self assert: 手牌 size = (m * n + h).
self assert: 手牌 asBag sortedCounts first key <= L.
対子群 := (手牌 groupBy: [:each | each] having: [:group | group size >= h]) values
collect: [:group | (group first: h) asArray].
刻子群 := (手牌 groupBy: [:each | each] having: [:group | group size >= m]) values
collect: [:group | (group first: m) asArray].
同種牌群 := (手牌 groupBy: [:each | each first] having: [:group | group size >= m]) values.
刻順子群 := OrderedCollection withAll: 刻子群.
同種牌群 do: [:group |
| 牌種 インデックス列 |
牌種 := group anyOne first.
インデックス列 := (group collect: [:each | each allButFirst asInteger]) asSet asSortedArray.
((1 to: M - m + 1)
collect: [:start | start to: start + m - 1]
thenSelect: [:range | インデックス列 includesAllOf: range])
do: [:range |
刻順子群 add: (range collect: [:each |
((each printPaddedWith: $0 to: M printString size)
copyWithFirst: 牌種) asSymbol])]].
和了パターン := Set new.
対子群 do: [:対子 |
残り := 手牌 asOrderedCollection.
対子 do: [:各々 | 残り remove: 各々].
待ち行列 := OrderedCollection with: {対子. 残り}.
[待ち行列 notEmpty] whileTrue: [
| 状態 |
状態 := 待ち行列 removeFirst.
状態 last ifEmpty: [和了パターン add: 状態 allButLast asBag] ifNotEmpty: [
刻順子群 do: [:刻順子 |
(刻順子 inject: 1 into: [:prev :牌 |
(状態 last indexOf: 牌 startingAt: prev ifAbsent: [状態 last size + 1]) + 1])
<= (状態 last size + 1) ifTrue: [
| 次 |
次 := 状態 last copy.
刻順子 do: [:各々 | 次 remove: 各々].
待ち行列 add: (状態 allButLast, {刻順子 asArray. 次})]]]]].
和了パターン notEmpty
"=> true "
|
受け付けるフォーマットはBタイプです。 以下のようなxml文書を入力として与えると、hand要素に"和了形"属性(true/false)を追加して出力します。:
<hands> <hand>1,1,2,3,4,5,6,6,7,7,8,9,9,9</hand> <hand>1,1,2,2,2,3,3,3,3,4,4,5,6,6</hand> <hand>1,1,2,2,4,4,5,5,6,6,7,7,9,9</hand> <hand>1,1,2,2,4,4,5,5,6,7,8,7,8,9</hand> </hands>
後、お題にはなかったのですが、七対子にも対応してみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | <xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:my="uri:ja.doukaku.org:my-functions"
exclude-result-prefixes="my"
>
<xsl:output method="xml" />
<xsl:variable name="debug" as="xs:boolean" select="false()" />
<xsl:variable name="can七対子" as="xs:boolean" select="true()" />
<xsl:variable name="m" as="xs:integer" select="3" />
<xsl:variable name="n" as="xs:integer" select="4" />
<xsl:variable name="h" as="xs:integer" select="2" />
<xsl:variable name="M" as="xs:integer" select="9" />
<xsl:variable name="L" as="xs:integer" select="4" />
<xsl:template match="/hands">
<hands>
<xsl:apply-templates select="./hand" />
</hands>
</xsl:template>
<xsl:template match="hand">
<xsl:variable name="pai" as="xs:integer*">
<xsl:variable name="pai-unordered" as="xs:integer*">
<xsl:for-each select="fn:tokenize(text(), ',')">
<xsl:sequence select="xs:integer(.)" />
</xsl:for-each>
</xsl:variable>
<xsl:for-each select="$pai-unordered">
<xsl:sort />
<xsl:sequence select="." />
</xsl:for-each>
</xsl:variable>
<xsl:if test="fn:max($pai) > $M">
<xsl:message terminate="yes">
<xsl:text>invalid max value of pai. expected </xsl:text>
<xsl:value-of select="$M" />
<xsl:text> but </xsl:text>
<xsl:value-of select="fn:max($pai)" />
</xsl:message>
</xsl:if>
<xsl:for-each select="fn:distinct-values($pai)">
<xsl:if test="fn:count(fn:index-of($pai, .)) > $L">
<xsl:message terminate="yes">
<xsl:text>invalid count per a kind, expected </xsl:text>
<xsl:value-of select="$L" />
<xsl:text> but </xsl:text>
<xsl:value-of select="fn:count(fn:index-of($pai, .))" />
<xsl:text> for </xsl:text>
<xsl:value-of select="." />
</xsl:message>
</xsl:if>
</xsl:for-each>
<xsl:element name="hand">
<xsl:attribute name="和了形">
<xsl:value-of select="my:check-和了形($pai)" />
</xsl:attribute>
<xsl:value-of select="text()" />
</xsl:element>
</xsl:template>
<xsl:function name="my:check-和了形" as="xs:boolean">
<xsl:param name="pai" as="xs:integer*" />
<xsl:variable name="result" as="xs:boolean*">
<xsl:sequence select="my:check-対子($pai,0)" />
</xsl:variable>
<xsl:value-of select="fn:count($result)>0" />
</xsl:function>
<xsl:function name="my:check-対子" as="xs:boolean*">
<xsl:param name="pai" as="xs:integer*" />
<xsl:param name="depth" as="xs:integer" />
<xsl:if test="$debug">
<xsl:message>
<xsl:text>check1: </xsl:text>
<xsl:value-of select="$pai" />
</xsl:message>
</xsl:if>
<xsl:choose>
<xsl:when test="fn:empty($pai)">
<xsl:sequence select="true()" />
</xsl:when>
<xsl:otherwise>
<xsl:if test="$depth=0 or $can七対子">
<xsl:for-each select="1 to (fn:count($pai) - ($h - 1))">
<xsl:variable name="i" as="xs:integer" select="." />
<xsl:if test="fn:count(fn:distinct-values(fn:subsequence($pai,$i, $h)))=1">
<xsl:variable name="next-pai" as="xs:integer*"
select="$pai[position() < $i or $i + ($h - 1) < position()]" />
<xsl:sequence select="my:check-対子($next-pai, $depth+1)" />
</xsl:if>
</xsl:for-each>
</xsl:if>
<xsl:if test="$depth=1">
<xsl:sequence select="my:check-刻子($pai, $depth)" />
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:function name="my:check-刻子" as="xs:boolean*">
<xsl:param name="pai" as="xs:integer*" />
<xsl:param name="depth" as="xs:integer" />
<xsl:if test="$debug">
<xsl:message>
<xsl:text>check2: </xsl:text>
<xsl:value-of select="$pai" />
</xsl:message>
</xsl:if>
<xsl:choose>
<xsl:when test="fn:count($pai)=0">
<xsl:if test="$depth - 1!=$n">
<xsl:message terminate="yes">
<xsl:text> invalid count of 刻子/順子. expected </xsl:text>
<xsl:value-of select="$n" />
<xsl:text> but </xsl:text>
<xsl:value-of select="$depth - 1" />
</xsl:message>
</xsl:if>
<xsl:sequence select="true()" />
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="1 to (fn:count($pai) - ($m - 1))">
<xsl:variable name="i" as="xs:integer" select="." />
<xsl:if test="fn:count(fn:distinct-values(fn:subsequence($pai,$i, $m)))=1">
<xsl:variable name="next-pai" as="xs:integer*"
select="$pai[position() < $i or $i + ($m - 1) < position()]" />
<xsl:sequence select="my:check-刻子($next-pai, $depth+1)" />
</xsl:if>
</xsl:for-each>
<xsl:sequence select="my:check-順子($pai, $depth)" />
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:function name="my:check-順子" as="xs:boolean*">
<xsl:param name="pai" as="xs:integer*" />
<xsl:param name="depth" as="xs:integer" />
<xsl:if test="$debug">
<xsl:message>
<xsl:text>check3: </xsl:text>
<xsl:value-of select="$pai" />
</xsl:message>
</xsl:if>
<xsl:choose>
<xsl:when test="fn:count($pai)=0">
<xsl:if test="$depth - 1 != $n">
<xsl:message terminate="yes">
<xsl:text> invalid count of 刻子/順子. expected </xsl:text>
<xsl:value-of select="$n" />
<xsl:text> but </xsl:text>
<xsl:value-of select="$depth - 1" />
</xsl:message>
</xsl:if>
<xsl:sequence select="true()" />
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="1 to (fn:count($pai) - ($m - 1))">
<xsl:variable name="i" as="xs:integer" select="." />
<xsl:variable name="indexes" as="xs:integer*">
<xsl:variable name="values" as="xs:integer*">
<xsl:sequence select="$pai[$i]" />
<xsl:for-each select="1 to ($m - 1)">
<xsl:sequence select="$pai[$i] + ." />
</xsl:for-each>
</xsl:variable>
<xsl:for-each select="$values">
<xsl:variable name="idx" as="xs:integer*"
select="fn:index-of($pai, .)" />
<xsl:if test="fn:exists($idx)">
<xsl:sequence select="$idx[1]" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:if test="fn:count($indexes)=$m">
<xsl:variable name="next-pai" as="xs:integer*"
select="$pai[fn:empty(fn:index-of($indexes, position()))]" />
<xsl:sequence select="my:check-順子($next-pai, $depth+1)" />
</xsl:if>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
</xsl:stylesheet>
|
Aタイプ向けに書いてみました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #! /usr/bin/ruby -Ks
class CheckFinished
PAIR = 2
SET = 3
ORDER = 3
def self.check_finished(pieces)
check_pair(pieces)
end
def self.check_pair(pieces)
pieces.each_index do |i|
if pieces[i] >= PAIR
(s = pieces.clone)[i] = s[i] - PAIR
if check_set((0..s.size - 1).to_a.select { |j| s[j] >= SET }, s)
return true
end
end
end
false
end
def self.check_set(indexes, pieces)
if indexes.empty?
check_order(pieces)
else
i = (x = indexes.clone).shift
(0..pieces[i] / SET).each do |w|
(s = pieces.clone)[i] = s[i] - SET * w
if check_order(s) || check_set(x, s)
return true
end
end
false
end
end
def self.check_order(pieces)
if pieces.reject { |p| p == 0 }.empty?
true
else
c = (0..pieces.size - ORDER).find { |i| pieces[i] > 0 }
unless c && (c..c + ORDER - 1).select { |j| pieces[j] > 0 }.size == ORDER
false
else
check_order(pieces.clone.fill(c, ORDER) { |j| pieces[j] - 1 })
end
end
end
private_class_method :check_pair, :check_set, :check_order
end
pieces = [2,1,1,1,1,2,2,1,3]
puts "[#{pieces.join(',')}] => " + CheckFinished.check_finished(pieces).to_s
|
フォーマットAで、m,hも指定可能にしてみました。使用言語はF#です。
m=3でh=2の場合
> isFinished 3 2 [|2;1;1;1;1;2;2;1;3|];;
val it : bool = true
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | let isMoreEqM m (arr : int []) (index : int) =
arr.[index] >= m
//配列arrにおいてindexから始まる連続したm個の要素が全部1以上か
let isConsecMoreEq1 m (arr : int []) (index : int) =
let mutable res = true
for i in 0 .. (m - 1) do
if arr.[index + i] = 0 then
res <- false
res
//後ろにk個の0を付け加えた配列を返す
let addEle k (arr : int []) =
let t = Array.zeroCreate (arr.Length + k)
for i in 0 .. (arr.Length - 1) do
t.[i] <- arr.[i]
t
let isFinished mIn hIn (arrIn : int []) =
let targetArr = addEle (mIn - 1) arrIn
let isRemoveAll m (arr : int []) =
let searchIndexEnd = arr.Length - m
let result = ref false
let rec reMoveM (arr : int []) index =
if index = searchIndexEnd + 1 then
result := true
elif arr.[index] = 0 then
reMoveM arr (index + 1)
elif (isMoreEqM m arr index = true) && (isConsecMoreEq1 m arr index = true) then
reMoveKousi m arr index
reMoveJyunsi m arr index
elif isMoreEqM m arr index = true then
reMoveKousi m arr index
elif isConsecMoreEq1 m arr index = true then
reMoveJyunsi m arr index
else
()
and reMoveKousi mA (arrA :int []) indexA =
arrA.[indexA] <- arrA.[indexA] - mA;
reMoveM arrA indexA
arrA.[indexA] <- arrA.[indexA] + mA;
and reMoveJyunsi mB (arrB :int []) index =
for i in 0 .. (mB-1) do
arrB.[index+i] <- arrB.[index+i] - 1
reMoveM arrB index
for i in 0 .. (mB-1) do
arrB.[index+i] <- arrB.[index+i] + 1
reMoveM arr 0
!result
let rec reMoveH m h (arr : int []) index =
let searchIndexEnd = arr.Length - m
if index = searchIndexEnd + 1 then
false
else
if arr.[index] >= h then
arr.[index] <- arr.[index] - h
if isRemoveAll m arr = true then
true
else
arr.[index] <- arr.[index] + h
reMoveH m h arr (index + 1)
else
reMoveH m h arr (index + 1)
reMoveH mIn hIn targetArr 0
|
F#の勉強中で文法よくわかってないですが、多分、こんな感じかな。 <実行結果> [3; 3; 3; 3; 0; 0; 0; 0; 2] - 上がっています。 [1; 1; 2; 1; 1; 3; 3; 0; 2] - 上がっています。 [0; 1; 1; 3; 1; 2; 2; 1; 3] - 上がっています。 [2; 2; 2; 2; 2; 2; 0; 0; 2] - 7対子で上がっています。 [3; 1; 2; 1; 1; 3; 3; 0; 0] - 上がっています。 [1; 1; 2; 1; 1; 3; 4; 0; 1] - 上がっていません。 [2; 1; 1; 1; 1; 2; 2; 1; 3] - 上がっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | open System
let sampleHands = [
[3;3;3;3;0;0;0;0;2];
[1;1;2;1;1;3;3;0;2];
[0;1;1;3;1;2;2;1;3];
[2;2;2;2;2;2;0;0;2];
[3;1;2;1;1;3;3;0;0];
[1;1;2;1;1;3;4;0;1];
[2;1;1;1;1;2;2;1;3];
]
type Hand = list<int>
type Mahjong() =
let m, n, h, M, L = 3, 4, 2, 9, 4
let GetRestOfHand (f:int->int->int option) (hand:Hand) : (Hand option) =
let rec GetRestOfHand' (hand':Hand) =
if hand'.Length = M then
Some hand'
else
let thisKind = hand'.Length
match f thisKind hand.[thisKind] with
| Some(i) -> GetRestOfHand' (hand' @ [i])
| _ -> None
GetRestOfHand' []
// 手牌から各要素を取り除いた手牌を返す。
let TakeToitsu (fromThisKind:int) (hand:Hand) : (Hand option) =
GetRestOfHand (fun thisKind i ->
match thisKind with
| k when k < 0 || hand.Length <= k -> None
| k when k = fromThisKind -> if i >= h then Some(i-h) else None
| k -> Some(i)) hand
let TakeKotsu (fromThisKind:int) (hand:Hand) : (Hand option) =
GetRestOfHand (fun thisKind i ->
match thisKind with
| k when k < 0 || hand.Length <= k -> None
| k when k = fromThisKind -> if i >= m then Some(i-m) else None
| k -> Some(i)) hand
let TakeShuntsu (fromThisKind:int) (hand:Hand) : (Hand option) =
GetRestOfHand (fun thisKind i ->
match thisKind with
| k when k < 0 || hand.Length <= k -> None
| k when fromThisKind <= k && k < fromThisKind+m -> if i > 0 then Some(i-1) else None
| k -> Some(i)) hand
// 残りの手牌が空になれば上がっている。
let IsEmpty (hand:Hand) = List.forall (fun i -> i = 0) hand
// 残りの手牌が刻子と順子で構成されているか調べる。
let rec CheckKotsuOrShuntsu (fromThisKind:int) (hand:Hand) =
match fromThisKind with
| k when k < 0 || hand.Length < k -> failwith "CheckKotsuOrShuntsu: 不正な値(fromThisKind=%d)です" k
| k when k = hand.Length -> IsEmpty hand
| k -> match TakeKotsu k hand with
| Some(x) -> CheckKotsuOrShuntsu k x || match TakeShuntsu k hand with
| Some(x) -> CheckKotsuOrShuntsu k x
| None -> CheckKotsuOrShuntsu (k+1) hand
| None -> match TakeShuntsu k hand with
| Some(x) -> CheckKotsuOrShuntsu k x
| None -> CheckKotsuOrShuntsu (k+1) hand
// 手牌から頭を取り除き、残りの手牌を調べる。
let rec CheckToitsu (fromThisKind:int) (hand:Hand) =
match fromThisKind with
| k when k < 0 || hand.Length < k -> failwith "CheckToisu: 不正な値(fromThisKind=%d)です" k
| k when k = hand.Length -> false
| k -> match TakeToitsu k hand with
| None -> CheckToitsu (k+1) hand
| Some(x) -> if CheckKotsuOrShuntsu 0 x then true
else CheckToitsu (k+1) hand
member this.CheckHand (hand:Hand) =
if hand.Length <> M || List.exists (fun i -> i > L) hand then
printfn "%A - 不正な手牌です。条件(M=%d, L=%d)を満たしていません。" hand M L
elif List.sum hand <> m*n+h then
printfn "%A - 不正な手牌です。条件(m=%d, n=%d, h=%d)を満たしていません。" hand m n h
elif List.forall (fun i -> i = h || i = 0) hand then
printfn "%A - %d対子で上がっています。" hand ((m*n+h)/h)
elif CheckToitsu 0 hand then
printfn "%A - 上がっています。" hand
else
printfn "%A - 上がっていません。" hand
[<STAThread>]
[<EntryPoint>]
let main args =
let m = new Mahjong()
List.iter (fun hand -> m.CheckHand(hand)) sampleHands
#if DEBUG
Console.ReadKey() |> ignore
#endif
0
|


wm #8532() [ Other ] Rating-5/9=-0.56
手牌を表すのは、mn+h 枚を表す配列です。フォーマットは A, B の2つから任意に選んだものを受け取ってください。以下の2つは同じものです:
handA = [2,1,1,1,1,2,2,1,3] # 牌1が2個、牌2が1個、牌3が1個、牌4が1個、牌5が1個、牌6が2個、牌7が2個、牌8が1個、牌9が3個
handB = [1,1,2,3,4,5,6,6,7,7,8,9,9,9] # 牌1、牌1、牌2、牌3、牌4、牌5、牌6、牌7、牌7、牌8、牌9、牌9、牌9
ある配列が和了形であるとは、手牌が「刻子」「順子」を合計 n 個と「対子」1 個との和であることをいうものとします。
「刻子」とは1種の牌が m 個あることをいいます。
「順子」とは連続したインデクスを持つ牌が m 種各1個あることをいいます。
「対子」とは1種の牌が h 個あることをいいます。
上の例は、和了形です。なぜなら、当該配列が
Bタイプで表記すると、刻子 [9,9,9], 順子 [2,3,4], [5,6,7], [6,7,8], 対子 [1,1] の、
Aタイプで表記すると、刻子 [0,0,0,0,0,0,0,0,3], 順子 [0,1,1,1,0,0,0,0,0], [0,0,0,0,1,1,1,0,0], [0,0,0,0,0,1,1,1,0], 対子 [2,0,0,0,0,0,0,0,0] の、
和だからです。
・ n は任意のものを受け取れるようにしてください。
・ 牌のインデクスの数 (Aタイプの長さ、Bタイプの要素の最大値) M も任意としてください。
・ 1種の牌の最大個数 (Aタイプの要素の最大値、Bタイプの1種の要素の最大重複度) L も任意としてください。
・ 一般の麻雀の場合は、m=3, n=4, h=2, M=9, L=4 です。この条件に特化した高速化が可能なら行ってもかまいません。
擬似コードで、ごく短い例を示します。
for 考えられる対子: 対子を手牌から抜く for 考えられる刻子のパターン: 刻子を手牌から抜く 手牌が順子の和となっていれば、和了形として抜ける 刻子を手牌へ戻す 対子を手牌へ戻す ここに到達していれば、和了形ではないRating-5/9=-0.56-0+
[ reply ]