Created
May 15, 2010 21:02
-
-
Save karno/402395 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
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>(); | |
fours = new List<IEnumerable<Tile>>(); | |
} | |
/// <summary> | |
/// 和了型のキャッシュ | |
/// </summary> | |
protected List<ReadyForm> readyFormCache; | |
/// <summary> | |
/// 槓子組 | |
/// </summary> | |
protected List<IEnumerable<Tile>> fours; | |
/// <summary> | |
/// 登録されている牌一覧 | |
/// </summary> | |
protected List<Tile> tiles; | |
/// <summary> | |
/// 牌の列挙を取得します。 | |
/// </summary> | |
public IEnumerable<Tile> Tiles | |
{ | |
get { return tiles; } | |
} | |
#region tiles collection control | |
/// <summary> | |
/// 登録されている牌を削除します。 | |
/// </summary> | |
public void ClearTiles() | |
{ | |
tiles.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 == b) //同じならすぐ戻る | |
return 0; | |
else 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> | |
public void Discard(int index) | |
{ | |
tiles.RemoveAt(index); | |
} | |
#endregion | |
#region ready form generator | |
/// <summary> | |
/// 聴牌形を確認します。 | |
/// </summary> | |
/// <returns>聴牌しているかどうか</returns> | |
public bool UpdateReadyForm() | |
{ | |
readyFormCache.Clear(); | |
//聴牌形でない | |
if (tiles.Count % 3 != 1) return false; | |
//和了形格納変数 | |
ReadyForm rf = new ReadyForm(this.tiles); | |
//槓子をあらかじめ格納 | |
rf.Sets.AddRange(this.fours); | |
//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); | |
//pivotを雀頭に待ちを調べる(待ちはpivotと同じ牌となる) | |
CheckReadyForm(ref tempTiles, ref ready, true); | |
//自分のいたインデックスが同じ種類・同じ値の牌であったら、それで雀頭が出来る | |
if (pivot < tempTiles.Count - 1) | |
{ | |
int pivotincl = 0; | |
var ntile = tempTiles[pivot]; | |
//同じ種類・値の牌は全てスキップする | |
for (; ntile == head; pivotincl++) | |
ntile = tempTiles[pivot + pivotincl]; | |
if (pivotincl > 0) | |
{ | |
//雀頭として削除 | |
tempTiles.RemoveAt(pivot); | |
CheckReadyForm(ref tempTiles, ref ready, false); | |
} | |
pivot += pivotincl; | |
} | |
} | |
return readyFormCache.Count > 0; | |
} | |
/// <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 (_tile.Count == 0) | |
{ | |
//全ての牌を構成できます | |
readyFormCache.Add(_ready.Clone()); | |
return; | |
} | |
//作業用変数コピー | |
var ready = _ready.Clone(); | |
var tile = new List<Tile>(_tile); | |
//リスト1番目の牌を利用する | |
var cPivot = PickAndRemove(ref tile, 0); | |
//手牌から順子/刻子の構成 | |
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 }); | |
//サブルーチンへ | |
CheckReadyForm(ref tile, ref ready, paiFilled); | |
//元に戻す | |
ready = _ready.Clone(); | |
tile = new List<Tile>(_tile); | |
} | |
//順子 | |
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 pick2 = PickAndRemove(ref tile, i); | |
var pick3 = PickAndRemove(ref tile, j); | |
//順子の追加 | |
ready.Sequences.Add(new[] { cPivot, pick2, pick3 }); | |
//サブルーチンへ | |
CheckReadyForm(ref tile, ref ready, paiFilled); | |
//元に戻す | |
ready = _ready.Clone(); | |
tile = new List<Tile>(_tile); | |
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 (!paiFilled && tile.Count >= 1) | |
{ | |
//刻子 | |
if (tile[0] == cPivot) | |
{ | |
var pick2 = PickAndRemove(ref tile, 0); | |
ready.Waiting = pick2; | |
//刻子の追加 | |
ready.Sets.Add(new[] { cPivot, pick2, pick2 }); | |
//サブルーチンへ | |
CheckReadyForm(ref tile, ref ready, true); | |
//元に戻す | |
ready = _ready.Clone(); | |
tile = new List<Tile>(_tile); | |
} | |
//順子 | |
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); | |
ready.Waiting = newTile; | |
//順子の追加 | |
ready.Sequences.Add(new[] { newTile, cPivot, pick2 }); | |
//サブルーチンへ | |
CheckReadyForm(ref tile, ref ready, true); | |
//readyだけ元に戻す | |
ready = _ready.Clone(); | |
} | |
if (pick2.Value < 9) //右端に追加する場合 | |
{ | |
//待ちタイル | |
var newTile = new Tile(cPivot.TileType, pick2.Value + 1); | |
ready.Waiting = newTile; | |
//順子の追加 | |
ready.Sequences.Add(new[] { cPivot, pick2, newTile }); | |
//サブルーチンへ | |
CheckReadyForm(ref tile, ref ready, true); | |
} | |
break; | |
} | |
else if (tile[i].TileType > cPivot.TileType || | |
tile[i].Value > nextValue) | |
{ | |
//この先は検索しても何もない | |
break; | |
} | |
} | |
//おしまい | |
} | |
} | |
} | |
/// <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; | |
} | |
#endregion | |
} | |
/// <summary> | |
/// 待ち型を表します。 | |
/// </summary> | |
public class ReadyForm : ICloneable | |
{ | |
/// <summary> | |
/// 待ち方クラスを生成します。 | |
/// </summary> | |
/// <param name="parent"></param> | |
public ReadyForm(IEnumerable<Tile> parent) | |
{ | |
this.Tiles = parent; | |
Sequences = new List<IEnumerable<Tile>>(); | |
Sets = new List<IEnumerable<Tile>>(); | |
LinkingCriteriaBase = new List<CriteriaBase>(); | |
} | |
/// <summary> | |
/// 牌一覧 | |
/// </summary> | |
public IEnumerable<Tile> Tiles { get; set; } | |
/// <summary> | |
/// 待ち牌 | |
/// </summary> | |
public Tile Waiting { get; set; } | |
/// <summary> | |
/// 待ちの形一覧 | |
/// </summary> | |
public enum WaitingStyles { | |
/// <summary> | |
/// 両面待ち | |
/// </summary> | |
EitherTwoGates, | |
/// <summary> | |
/// 嵌張待ち | |
/// </summary> | |
MiddleInSequence, | |
/// <summary> | |
/// 辺張待ち | |
/// </summary> | |
ThreeOrSeven, | |
/// <summary> | |
/// 双碰待ち | |
/// </summary> | |
Triplet, | |
/// <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 List<CriteriaBase> LinkingCriteriaBase; | |
/// <summary> | |
/// データをすべてクリアします。 | |
/// </summary> | |
public void Clear() | |
{ | |
Tiles = null; | |
Waiting = null; | |
Sequences.Clear(); | |
Sets.Clear(); | |
LinkingCriteriaBase.Clear(); | |
} | |
#region ICloneable メンバー | |
public ReadyForm Clone() | |
{ | |
var clone = new ReadyForm(Tiles); | |
clone.Waiting = Waiting; | |
clone.Sequences.AddRange(Sequences); | |
clone.Sets.AddRange(Sets); | |
clone.LinkingCriteriaBase.AddRange(LinkingCriteriaBase); | |
return clone; | |
} | |
object ICloneable.Clone() | |
{ | |
return this.Clone(); | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment