Created
May 16, 2010 05:04
-
-
Save karno/402671 to your computer and use it in GitHub Desktop.
麻雀の和了判定
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#if DEBUG | |
//トレースプリントを行うか | |
//#define SHOW_TRACE_PRINT | |
#endif | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using Std.HeavenlyHand.Data; | |
using Std.HeavenlyHand.Criteria; | |
namespace Std.HeavenlyHand.Logic | |
{ | |
/// <summary> | |
/// キャッシュ付きの牌ソート・聴牌形列挙サブシステムです。<para /> | |
/// Player型に組み込まれて利用されます。 | |
/// </summary> | |
public class TilesManager | |
{ | |
/// <summary> | |
/// 牌管理システムのインスタンスを作成します。 | |
/// </summary> | |
public TilesManager() | |
{ | |
tiles = new List<Tile>(); | |
readyFormCache = new List<ReadyForm>(); | |
openeds = new List<KeyValuePair<IEnumerable<Tile>, bool>>(); | |
specials = new List<Tile>(); | |
} | |
/// <summary> | |
/// 和了型のキャッシュ | |
/// </summary> | |
protected List<ReadyForm> readyFormCache; | |
/// <summary> | |
/// 和了形の一覧 | |
/// </summary> | |
public IEnumerable<ReadyForm> ReadyForms | |
{ | |
get { return readyFormCache; } | |
} | |
/// <summary> | |
/// 副露牌 valueがtrueなら槓子・刻子を表し、falseなら順子を表す | |
/// </summary> | |
protected List<KeyValuePair<IEnumerable<Tile>, bool>> openeds; | |
/// <summary> | |
/// 登録されている牌一覧 | |
/// </summary> | |
protected List<Tile> tiles; | |
/// <summary> | |
/// 牌の列挙を取得します。 | |
/// </summary> | |
public IEnumerable<Tile> Tiles | |
{ | |
get { return tiles; } | |
} | |
/// <summary> | |
/// ドラ扱いの除外牌一覧(季節牌、三人麻雀の北など) | |
/// </summary> | |
protected List<Tile> specials; | |
/// <summary> | |
/// ドラ扱いの除外牌一覧(季節牌、三人麻雀の北など) | |
/// </summary> | |
public IEnumerable<Tile> Specials | |
{ | |
get { return specials; } | |
} | |
#region tiles collection control | |
/// <summary> | |
/// 登録されている牌を削除します。 | |
/// </summary> | |
public void ClearTiles() | |
{ | |
tiles.Clear(); | |
readyFormCache.Clear(); | |
openeds.Clear(); | |
specials.Clear(); | |
} | |
/// <summary> | |
/// 登録されている牌一覧をソートします。 | |
/// </summary> | |
/// <remarks> | |
/// 筒子→索子→萬子→字牌(東→中,季節牌) の順にソートします。<para/> | |
/// 同じ牌でドラあり・なしが違う場合、ドラを優先します。 | |
/// </remarks> | |
private void SortTiles() | |
{ | |
tiles.Sort(new Comparison<Tile>(TileComparer)); | |
} | |
/// <summary> | |
/// 牌のソートを行います。 | |
/// </summary> | |
/// <param name="a">引数a</param> | |
/// <param name="b">引数b</param> | |
/// <returns><0:a < b, >0: a > b, =0: a == b </returns> | |
protected virtual int TileComparer(Tile a, Tile b) | |
{ | |
if (a.TileType != b.TileType) //牌種が違ったらそれを比較 | |
return (int)a.TileType - (int)b.TileType; | |
else if (a.Value != b.Value) //牌種が同じで値が違ったらそれを比較 | |
return a.Value - b.Value; | |
else if (a.Dra ^ b.Dra) //牌種と値が同じでドラありなしが違えばドラを比較 | |
return a.Dra ? -1 : 1; | |
else //両者は同じです。 | |
return 0; | |
} | |
/// <summary> | |
/// 配牌を設定します。<para /> | |
/// 牌登録キャッシュを全てクリアし、セットし、ソートします。 | |
/// </summary> | |
/// <param name="tile"></param> | |
public void SetDealingTiles(IEnumerable<Tile> tile) | |
{ | |
ClearTiles(); | |
tiles.AddRange(tile); | |
SortTiles(); | |
} | |
/// <summary> | |
/// 牌を追加します。 | |
/// </summary> | |
/// <param name="tile">追加する牌</param> | |
public void AddPicked(Tile tile) | |
{ | |
tiles.Add(tile); | |
} | |
/// <summary> | |
/// 牌を切ります。 | |
/// </summary> | |
/// <param name="index">左からの番目</param> | |
/// <rreturns>切る牌データ</rreturns> | |
public Tile Discard(int index) | |
{ | |
var ret = tiles[index]; | |
tiles.RemoveAt(index); | |
return ret; | |
} | |
/// <summary> | |
/// 牌を自分の特殊牌として追加します。 | |
/// </summary> | |
/// <param name="tile">特殊牌として追加する牌</param> | |
public void AddSpecial(Tile tile) | |
{ | |
specials.Add(tile); | |
} | |
/// <summary> | |
/// 副露牌を追加します。 | |
/// </summary> | |
/// <param name="sets">刻子か槓子であるか</param> | |
/// <param name="opens">副露した牌</param> | |
public void AddOpenedTiles(IEnumerable<Tile> opens, bool sets) | |
{ | |
openeds.Add(new KeyValuePair<IEnumerable<Tile>, bool>(opens, sets)); | |
} | |
#endregion | |
#region ready form generator | |
/// <summary> | |
/// 和了キャッシュを更新します。 | |
/// </summary> | |
/// <returns>和了できればtrue</returns> | |
public bool RefreshReadyForm() | |
{ | |
//待ちの取得 | |
if(!UpdateReadyForm()) | |
//聴牌していない | |
return false; | |
//聴牌している | |
//フォーマット | |
FormatReadyForms(); | |
//聴牌している | |
return true; | |
} | |
/// <summary> | |
/// 和了形を確認し、キャッシュを更新します。 | |
/// </summary> | |
/// <returns>聴牌しているかどうか</returns> | |
private bool UpdateReadyForm() | |
{ | |
readyFormCache.Clear(); | |
//聴牌形でない | |
if (tiles.Count % 3 != 1) return false; | |
//和了形格納変数 | |
ReadyForm rf = new ReadyForm(this.tiles); | |
//副露した牌を予め格納 | |
if (this.openeds.Count > 0) | |
{ | |
foreach (var opened in openeds) | |
{ | |
if (opened.Value) //刻子・槓子 | |
rf.Sets.Add(opened.Key); | |
else //順子 | |
rf.Sequences.Add(opened.Key); | |
} | |
} | |
//pivot:雀頭にする牌の位置 | |
for (int pivot = 0; pivot < tiles.Count; pivot++) | |
{ | |
//一時変数の生成 | |
List<Tile> tempTiles = new List<Tile>(tiles); | |
var ready = rf.Clone(); | |
//雀頭の設定 | |
var head = PickAndRemove(ref tempTiles, pivot); | |
var hw = new Tile(head.TileType, head.Value * -1); | |
ready.Head = new[] { head, hw }; | |
//pivotを雀頭に待ちを調べる(待ちはpivotと同じ牌となる) | |
//待ちは確定している | |
ready.WaitingInternal = hw; | |
#if SHOW_TRACE_PRINT | |
System.Diagnostics.Debug.WriteLine("Head:" + head.Value); | |
#endif | |
CheckReadyForm(ref tempTiles, ref ready, true); | |
//一時変数の再初期化 | |
tempTiles = new List<Tile>(tiles); | |
ready = rf.Clone(); | |
//雀頭の再抜き | |
PickAndRemove(ref tempTiles, pivot); | |
//自分のいたインデックスが同じ種類・同じ値の牌であったら、それで雀頭が出来る | |
if (pivot < tempTiles.Count) | |
{ | |
int pivotincl = 0; | |
var ntile = tempTiles[pivot]; | |
//同じ種類・値の牌は全てスキップする | |
while (ntile == head) | |
if (++pivotincl + pivot < tempTiles.Count) | |
ntile = tempTiles[pivot + pivotincl]; | |
else | |
break; | |
if (pivotincl > 0) | |
{ | |
//雀頭として削除 | |
var ht = PickAndRemove(ref tempTiles, pivot); | |
ready.Head = new[] { head, ht }; | |
#if SHOW_TRACE_PRINT | |
System.Diagnostics.Debug.WriteLine("Head:" + head.Value); | |
#endif | |
//待ちは確定していない | |
CheckReadyForm(ref tempTiles, ref ready, false); | |
} | |
pivot += pivotincl; | |
} | |
} | |
return readyFormCache.Count > 0; | |
} | |
#if SHOW_TRACE_PRINT | |
int callStackDepth = 0; | |
#endif | |
/// <summary> | |
/// 待ちの形を精査します。 | |
/// </summary> | |
/// <param name="_tile">牌データのリファレンス</param> | |
/// <param name="_ready">待ち形データのリファレンス</param> | |
/// <param name="paiFilled">待ち牌を考慮しない</param> | |
private void CheckReadyForm(ref List<Tile> _tile, ref ReadyForm _ready, bool paiFilled) | |
{ | |
#if SHOW_TRACE_PRINT | |
callStackDepth++; | |
#endif | |
if (_tile.Count == 0) | |
{ | |
foreach (var rf in readyFormCache) | |
{ | |
if (rf.IsDuplicateStrictWithWaiting(_ready)) | |
{ | |
#if SHOW_TRACE_PRINT | |
System.Diagnostics.Debug.WriteLine("□Duplicated:"); | |
ShowReadyForm(_ready); | |
callStackDepth--; | |
#endif | |
return; | |
} | |
} | |
//全ての牌を構成できます | |
readyFormCache.Add(_ready.Clone()); | |
#if SHOW_TRACE_PRINT | |
callStackDepth--; | |
#endif | |
//デバッグプリント | |
ShowReadyForm(_ready); | |
return; | |
} | |
#if SHOW_TRACE_PRINT | |
System.Diagnostics.Debug.WriteLine("[" + callStackDepth.ToString() + "]Pivot:" + _tile[0].Value.ToString()); | |
System.Diagnostics.Debug.Write("■"); | |
foreach (var t in _tile) | |
System.Diagnostics.Debug.Write(t.Value.ToString() + " "); | |
System.Diagnostics.Debug.WriteLine(""); | |
System.Diagnostics.Debug.Write("■ {"); | |
//雀頭 | |
foreach (var h in _ready.Head) | |
{ | |
System.Diagnostics.Debug.Write(h.Value); | |
} | |
System.Diagnostics.Debug.Write("}"); | |
foreach (var ka in _ready.Sets) | |
{ | |
System.Diagnostics.Debug.Write("["); | |
foreach (var k in ka) | |
{ | |
System.Diagnostics.Debug.Write(k.Value); | |
} | |
System.Diagnostics.Debug.Write("]"); | |
} | |
foreach (var ka in _ready.Sequences) | |
{ | |
System.Diagnostics.Debug.Write("("); | |
foreach (var k in ka) | |
{ | |
System.Diagnostics.Debug.Write(k.Value); | |
} | |
System.Diagnostics.Debug.Write(")"); | |
} | |
System.Diagnostics.Debug.WriteLine("."); | |
#endif | |
//作業用変数コピー | |
var ready = _ready.Clone(); | |
var tile = new List<Tile>(_tile); | |
//リスト1番目の牌を利用する | |
var cPivot = PickAndRemove(ref tile, 0); | |
#if SHOW_TRACE_PRINT | |
System.Diagnostics.Debug.WriteLine("☆" + callStackDepth.ToString() + ":Pivot is " + cPivot.Value); | |
System.Diagnostics.Debug.WriteLine("*" + callStackDepth.ToString() + ":CheckSet"); | |
#endif | |
//手牌から順子/刻子の構成 | |
if (tile.Count >= 2) | |
{ | |
//刻子 | |
if (tile[0] == cPivot && tile[1] == cPivot) | |
{ | |
var pick2 = PickAndRemove(ref tile, 0); | |
var pick3 = PickAndRemove(ref tile, 0); | |
//刻子の追加 | |
ready.Sets.Add(new[] { cPivot, pick2, pick3 }); | |
#if SHOW_TRACE_PRINT | |
System.Diagnostics.Debug.WriteLine("Set 3:" + | |
cPivot.Value.ToString() + " " + | |
pick2.Value.ToString() + " " + | |
pick3.Value.ToString() + " "); | |
#endif | |
//サブルーチンへ | |
CheckReadyForm(ref tile, ref ready, paiFilled); | |
//元に戻す | |
ready = _ready.Clone(); | |
tile = new List<Tile>(_tile); | |
PickAndRemove(ref tile, 0); | |
} | |
#if SHOW_TRACE_PRINT | |
System.Diagnostics.Debug.WriteLine("*" + callStackDepth.ToString() + ":CheckSequence"); | |
#endif | |
//順子 | |
if (cPivot.TileType != Tile.TileTypes.Honors && cPivot.Value < 8) | |
{ | |
//とりあえず順子の左端を構成できる牌であることを確認 | |
int findValue = cPivot.Value + 1; | |
for (int i = 0; i < tile.Count; i++) | |
{ | |
//+1(2番目)の牌の検索 | |
if ((int)tile[i].TileType == (int)cPivot.TileType && tile[i].Value == findValue) | |
{ | |
//次の牌はヒット | |
int findVal2 = findValue + 1; | |
for (int j = i; j < tile.Count; j++) | |
{ | |
//+2(3番目)の牌の検索 | |
if ((int)tile[j].TileType == (int)cPivot.TileType && | |
tile[j].Value == findVal2) | |
{ | |
//ヒット! | |
var pick3 = PickAndRemove(ref tile, j); | |
var pick2 = PickAndRemove(ref tile, i); | |
//順子の追加 | |
ready.Sequences.Add(new[] { cPivot, pick2, pick3 }); | |
#if SHOW_TRACE_PRINT | |
System.Diagnostics.Debug.WriteLine("Seq 3:" + | |
cPivot.Value.ToString() + " " + | |
pick2.Value.ToString() + " " + | |
pick3.Value.ToString() + " "); | |
#endif | |
//サブルーチンへ | |
CheckReadyForm(ref tile, ref ready, paiFilled); | |
//元に戻す | |
ready = _ready.Clone(); | |
tile = new List<Tile>(_tile); | |
PickAndRemove(ref tile, 0); | |
break; | |
} | |
else if (tile[j].Value > findVal2 || | |
(int)tile[j].TileType > (int)cPivot.TileType) | |
{ | |
//ソート順の関係上、この先の探索は無駄 | |
break; | |
} | |
} | |
break; | |
} | |
else if (tile[i].Value > findValue || (int)tile[i].TileType > (int)cPivot.TileType) | |
{ | |
//ソート順の関係上、この先の探索は無駄 | |
break; | |
} | |
} | |
} | |
} | |
#if SHOW_TRACE_PRINT | |
System.Diagnostics.Debug.WriteLine("*" + callStackDepth.ToString() + ":CheckSetSub"); | |
#endif | |
//手牌からの構成 おわり | |
//待ちを考慮した構成 | |
if (!paiFilled && tile.Count >= 1) | |
{ | |
//刻子 | |
if (tile[0] == cPivot) | |
{ | |
var pick2 = PickAndRemove(ref tile, 0); | |
//刻子の追加 | |
var pick3 = new Tile(pick2.TileType, pick2.Value * -1); | |
ready.WaitingInternal = pick3; | |
ready.Sets.Add(new[] { cPivot, pick2, pick3 }); | |
//サブルーチンへ | |
CheckReadyForm(ref tile, ref ready, true); | |
//元に戻す | |
ready = _ready.Clone(); | |
tile = new List<Tile>(_tile); | |
PickAndRemove(ref tile, 0); | |
} | |
//順子 | |
#if SHOW_TRACE_PRINT | |
System.Diagnostics.Debug.WriteLine("*" + callStackDepth.ToString() + ":CheckSequenceSub"); | |
#endif | |
if (cPivot.TileType != Tile.TileTypes.Honors && | |
cPivot.Value <= 8) | |
{ | |
for (int i = 0; i < tile.Count; i++) | |
{ | |
int nextValue = cPivot.Value + 1; | |
if ( | |
tile[i].TileType == cPivot.TileType && | |
tile[i].Value == nextValue) | |
{ | |
var pick2 = PickAndRemove(ref tile, i); | |
//順子を構成できました。 | |
if (cPivot.Value > 1) //左端に追加する場合 | |
{ | |
//待ち牌 | |
var newTile = new Tile(cPivot.TileType, (cPivot.Value - 1) * -1); | |
ready.WaitingInternal = newTile; | |
//順子の追加 | |
ready.Sequences.Add(new[] { newTile, cPivot, pick2 }); | |
#if SHOW_TRACE_PRINT | |
System.Diagnostics.Debug.WriteLine("Seq 2:" + | |
newTile.Value.ToString() + " " + | |
cPivot.Value.ToString() + " " + | |
pick2.Value.ToString() + " "); | |
#endif | |
//サブルーチンへ | |
CheckReadyForm(ref tile, ref ready, true); | |
//readyだけ元に戻す | |
ready = _ready.Clone(); | |
} | |
if (pick2.Value < 9) //右端に追加する場合 | |
{ | |
//待ち牌 | |
var newTile = new Tile(cPivot.TileType, (pick2.Value + 1) * -1); | |
ready.WaitingInternal = newTile; | |
//順子の追加 | |
ready.Sequences.Add(new[] { cPivot, pick2, newTile }); | |
#if SHOW_TRACE_PRINT | |
System.Diagnostics.Debug.WriteLine("Seq 2:" + | |
cPivot.Value.ToString() + " " + | |
pick2.Value.ToString() + " " + | |
newTile.Value.ToString() + " "); | |
#endif | |
//サブルーチンへ | |
CheckReadyForm(ref tile, ref ready, true); | |
} | |
break; | |
} | |
else if (tile[i].TileType > cPivot.TileType || | |
tile[i].Value > nextValue) | |
{ | |
//この先は検索しても何もない | |
break; | |
} | |
} | |
} | |
} | |
//おしまい | |
#if SHOW_TRACE_PRINT | |
callStackDepth--; | |
#endif | |
} | |
/// <summary> | |
/// 待ちの内容を表示します。(デバッグ用) | |
/// </summary> | |
/// <param name="_ready">待ち形</param> | |
[System.Diagnostics.Conditional("SHOW_TRACE_PRINT")] | |
private void ShowReadyForm(ReadyForm _ready) | |
{ | |
System.Diagnostics.Debug.WriteLine("■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■"); | |
System.Diagnostics.Debug.WriteLine("■ Matched!:"); | |
System.Diagnostics.Debug.Write("■ {"); | |
//雀頭 | |
foreach (var h in _ready.Head) | |
{ | |
System.Diagnostics.Debug.Write(h.Value); | |
} | |
System.Diagnostics.Debug.Write("}"); | |
foreach (var ka in _ready.Sets) | |
{ | |
System.Diagnostics.Debug.Write("["); | |
foreach (var k in ka) | |
{ | |
System.Diagnostics.Debug.Write(k.Value); | |
} | |
System.Diagnostics.Debug.Write("]"); | |
} | |
foreach (var ka in _ready.Sequences) | |
{ | |
System.Diagnostics.Debug.Write("("); | |
foreach (var k in ka) | |
{ | |
System.Diagnostics.Debug.Write(k.Value); | |
} | |
System.Diagnostics.Debug.Write(")"); | |
} | |
System.Diagnostics.Debug.WriteLine(" Wait:" + _ready.WaitingInternal.Value); | |
System.Diagnostics.Debug.WriteLine("■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■"); | |
} | |
/// <summary> | |
/// 指定したリストのindex番目のアイテムを削除して、返します。 | |
/// </summary> | |
/// <param name="list">リスト</param> | |
/// <param name="index">削除/取得するアイテムのインデックス</param> | |
/// <returns>削除したアイテム</returns> | |
private Tile PickAndRemove(ref List<Tile> list, int index) | |
{ | |
var pick = list[index]; | |
list.RemoveAt(index); | |
return pick; | |
} | |
/// <summary> | |
/// 待ちの重複を統合し、待ちそれぞれに型(両面、単騎など)を設定します。 | |
/// </summary> | |
private void FormatReadyForms() | |
{ | |
//両面・嵌張・辺張・単騎待ちを判別する | |
//同じ待ちグループを形成 | |
List<List<ReadyForm>> readyForms = new List<List<ReadyForm>>(); | |
foreach (var rfc in readyFormCache) | |
{ | |
bool hot = false; | |
foreach (var rf in readyForms) | |
{ | |
//待ちの形が同じなら既に存在するグループ内に放り込んで終了 | |
if (rf[0].IsDuplcate(rfc)) | |
{ | |
rf.Add(rfc); | |
hot = true; | |
break; | |
} | |
} | |
//存在していたのコンティニュー | |
if (hot) continue; | |
//独立したグループを生成してループ | |
readyForms.Add(new List<ReadyForm>(new[] { rfc })); | |
} | |
//待ちグループの精査 | |
foreach (var rfs in readyForms) | |
{ | |
//重複は予め取り除かれています | |
List<ReadyForm> tripletWait = new List<ReadyForm>(); | |
//待ち型の特定 | |
foreach (var rf in rfs) | |
{ | |
bool cont = false; | |
#region head waiting | |
//単騎待ち であるか | |
foreach (var head in rf.Head) | |
{ | |
if (head.Value < 0) | |
{ | |
rf.WaitingStyle = ReadyForm.WaitingStyles.OneOfPair; | |
cont = true; | |
break; | |
} | |
} | |
//単騎待ち確定 | |
if (cont) continue; | |
#endregion | |
//両面・辺張・嵌張待ち であるか | |
#region set waiting | |
foreach (var seqs in rf.Sequences) | |
{ | |
//槓子中に待ちが含まれることはありえない | |
if (seqs.Count() != 3) continue; | |
int pos = 0; | |
foreach (var seq in seqs) | |
{ | |
if (seq.Value < 0) | |
{ | |
//和了牌の位置により条件分岐 | |
switch (pos) | |
{ | |
case 0: | |
if (seq.Value == 7) //[7]89 7で和了 | |
//辺張待ち確定 | |
rf.WaitingStyle = ReadyForm.WaitingStyles.ThreeOrSeven; | |
else | |
//両面待ち確定 | |
rf.WaitingStyle = ReadyForm.WaitingStyles.EitherTwoGates; | |
break; | |
case 1: | |
//嵌張待ち確定 | |
rf.WaitingStyle = ReadyForm.WaitingStyles.MiddleInSequence; | |
break; | |
case 2: | |
if (seq.Value == 3) //12[3] 3で和了 | |
//辺張待ち確定 | |
rf.WaitingStyle = ReadyForm.WaitingStyles.ThreeOrSeven; | |
else | |
//両面待ち確定 | |
rf.WaitingStyle = ReadyForm.WaitingStyles.EitherTwoGates; | |
break; | |
default: | |
throw new InvalidOperationException("待ち判定に失敗しました:異常なセットが含まれています"); | |
} | |
cont = true; | |
break; | |
} | |
pos++; | |
} | |
if (cont) break; | |
} | |
if (cont) continue; | |
#endregion | |
//双ポン待ちであるか(一つなら亜両面扱い) | |
foreach (var sets in rf.Sets) | |
{ | |
foreach (var set in sets) | |
{ | |
if (set.Value < 0) | |
{ | |
//とりあえず亜両面待ち扱い | |
rf.WaitingStyle = ReadyForm.WaitingStyles.SubEitherTwoGates; | |
//キャッシュに追加して待つ | |
tripletWait.Add(rf); | |
cont = true; | |
break; | |
} | |
} | |
if (cont) break; | |
} | |
} | |
//亜両面待ちが2つあれば双ポン待ち | |
if (tripletWait.Count >= 2) | |
{ | |
foreach (var triplet in tripletWait) | |
triplet.WaitingStyle = ReadyForm.WaitingStyles.Triplet; | |
} | |
//完了 | |
} | |
} | |
#endregion | |
} | |
/// <summary> | |
/// 待ちを表します。 | |
/// </summary> | |
public class ReadyForm : ICloneable | |
{ | |
/// <summary> | |
/// 待ちクラスを生成します。 | |
/// </summary> | |
/// <param name="parent"></param> | |
public ReadyForm(IEnumerable<Tile> parent) | |
{ | |
this.Tiles = new List<Tile>(parent); | |
Sequences = new List<IEnumerable<Tile>>(); | |
Sets = new List<IEnumerable<Tile>>(); | |
LinkingCriteriaBase = new List<CriteriaBase>(); | |
WinWithRon = false; | |
} | |
/// <summary> | |
/// 牌一覧 | |
/// </summary> | |
public List<Tile> Tiles { get; set; } | |
/// <summary> | |
/// 待ち牌(内部扱い用、和了確認するまでは値が負です) | |
/// </summary> | |
public Tile WaitingInternal { get; set; } | |
/// <summary> | |
/// 待ち牌(コピーインスタンスを生成します、値は常に正です) | |
/// </summary> | |
public Tile Waiting | |
{ | |
get | |
{ | |
return new Tile(WaitingInternal.TileType, Math.Abs(WaitingInternal.Value)) | |
{ | |
Dra = WaitingInternal.Dra | |
}; | |
} | |
} | |
/// <summary> | |
/// 待ちの形一覧 | |
/// </summary> | |
public enum WaitingStyles { | |
/// <summary> | |
/// 両面待ち | |
/// </summary> | |
EitherTwoGates, | |
/// <summary> | |
/// 嵌張待ち | |
/// </summary> | |
MiddleInSequence, | |
/// <summary> | |
/// 辺張待ち | |
/// </summary> | |
ThreeOrSeven, | |
/// <summary> | |
/// 双ポン待ち | |
/// </summary> | |
Triplet, | |
/// <summary> | |
/// 亜両面待ち | |
/// </summary> | |
SubEitherTwoGates, | |
/// <summary> | |
/// 単騎待ち | |
/// </summary> | |
OneOfPair | |
} | |
/// <summary> | |
/// 待ちの形 | |
/// </summary> | |
public WaitingStyles WaitingStyle { get; set; } | |
/// <summary> | |
/// 順子牌組 | |
/// </summary> | |
public List<IEnumerable<Tile>> Sequences; | |
/// <summary> | |
/// 刻子/槓子牌組 | |
/// </summary> | |
public List<IEnumerable<Tile>> Sets; | |
/// <summary> | |
/// 雀頭 | |
/// </summary> | |
public IEnumerable<Tile> Head; | |
/// <summary> | |
/// この和了形で満たせる役一覧 | |
/// </summary> | |
public List<CriteriaBase> LinkingCriteriaBase; | |
/// <summary> | |
/// ロン和了したか | |
/// </summary> | |
public bool WinWithRon { get; set; } | |
/// <summary> | |
/// データをすべてクリアします。 | |
/// </summary> | |
public void Clear() | |
{ | |
Tiles.Clear(); | |
WaitingInternal = null; | |
Sequences.Clear(); | |
Sets.Clear(); | |
LinkingCriteriaBase.Clear(); | |
} | |
#region ICloneable メンバー | |
public ReadyForm Clone() | |
{ | |
var clone = new ReadyForm(this.Tiles); | |
clone.WaitingInternal = this.WaitingInternal; | |
clone.Sequences.AddRange(this.Sequences); | |
clone.Sets.AddRange(this.Sets); | |
clone.LinkingCriteriaBase.AddRange(this.LinkingCriteriaBase); | |
clone.Head = this.Head; | |
clone.WinWithRon = this.WinWithRon; | |
return clone; | |
} | |
object ICloneable.Clone() | |
{ | |
return this.Clone(); | |
} | |
#endregion | |
/// <summary> | |
/// 評価関数を指定して、同一であるか確認します。 | |
/// </summary> | |
/// <param name="target">確認先</param> | |
/// <param name="eval">評価関数</param> | |
/// <returns>同一ならtrue</returns> | |
private bool IsDuplicate(ReadyForm target, Func<Tile, decimal?> eval) | |
{ | |
if (target == null) | |
return false; | |
//待ち・順子個数・刻子個数が同一か確認 | |
if ( | |
this.WaitingInternal != target.WaitingInternal || | |
this.Sequences.Count != target.Sequences.Count || | |
this.Sets.Count != target.Sets.Count) | |
return false; | |
//雀頭が同一か確認 | |
if (this.Head.Sum(eval) != target.Head.Sum(eval)) | |
return false; | |
//順子が同一であるかの確認 | |
//不要だけど一応、プログラムコードの見通しを良くするため | |
System.Diagnostics.Debug.Assert(this.Sequences.Count == target.Sequences.Count); | |
//ソートと結合と評価 | |
foreach (var item in | |
this.Sequences.OrderBy((k) => k.Sum(eval)).Zip( | |
target.Sequences.OrderBy((k) => k.Sum(eval)), | |
(orig, targ) => new { X = orig, Y = targ })) | |
{ | |
if (item.X.Sum(eval) != item.Y.Sum(eval)) | |
return false; | |
} | |
//刻子が同一か確認 | |
//不要だけどry | |
System.Diagnostics.Debug.Assert(this.Sets.Count == target.Sets.Count); | |
//刻子が同一か確認 | |
foreach (var item in | |
this.Sets.OrderBy((k) => k.Sum(eval)).Zip( | |
target.Sets.OrderBy((k) => k.Sum(eval)), | |
(orig, targ) => new { X = orig, Y = targ })) | |
{ | |
if (item.X.Sum(eval) != item.Y.Sum(eval)) | |
return false; | |
} | |
return true; | |
} | |
/// <summary> | |
/// 和了形が同じ意味であるか確認します。 | |
/// </summary> | |
/// <param name="target">確認対象</param> | |
/// <returns>同じであればtrue</returns> | |
public bool IsDuplicateStrict(ReadyForm target) | |
{ | |
return IsDuplicate(target, (e) => Math.Abs(e.Value)); | |
} | |
/// <summary> | |
/// 和了形が同じ意味であるか、待ちを考慮して確認します。 | |
/// </summary> | |
/// <param name="target">確認対象</param> | |
/// <returns>同じであればtrue</returns> | |
public bool IsDuplicateStrictWithWaiting(ReadyForm target) | |
{ | |
return IsDuplicate(target, (e) => (e.Value > 0 ? e.Value : e.Value * -100)); | |
} | |
/// <summary> | |
/// 待ち牌以外の形が同じであるか確認します。 | |
/// </summary> | |
/// <param name="target">確認先</param> | |
/// <returns>同じならばtrue</returns> | |
public bool IsDuplcate(ReadyForm target) | |
{ | |
return IsDuplicate(target, (e) => (e.Value > 0 ? e.Value : 0)); | |
} | |
/// <summary> | |
/// この和了形を和了したことを設定します。 | |
/// </summary> | |
/// <param name="winWithRon">ロン和了か</param> | |
public void SetWinWithThis(bool winWithRon) | |
{ | |
this.WaitingInternal.Value *= -1; | |
this.WinWithRon = winWithRon; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment