Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save karno/402671 to your computer and use it in GitHub Desktop.
Save karno/402671 to your computer and use it in GitHub Desktop.
麻雀の和了判定
#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