/// <summary> /// Finds the TileTypeId of the discard with highest UkeIre while maintaining shanten. /// </summary> public int GetHighestUkeIreDiscard() { Debug.Assert(TilesInHand() == 14, "Have to be able to discard a tile"); // If we have a winning hand, all discards will lead to worse shanten var currentShanten = CalculateShanten(ArrangementValues); if (currentShanten == 0) { return(ConcealedTiles[0]); } var tileTypeId = 0; var localArrangements = new[] { ArrangementValues[0], ArrangementValues[1], ArrangementValues[2], ArrangementValues[3] }; var highestUkeIre = -1; var highestUkeIreDiscard = -1; for (var suit = 0; suit < 3; ++suit) { for (var index = 0; index < 9; ++index) { if (ConcealedTiles[tileTypeId] > 0) { var kyuuhaiValue = (0b100000001 >> index) & 1; ConcealedTiles[tileTypeId] -= 1; InHandByType[tileTypeId] -= 1; Kokushi.Discard(kyuuhaiValue, ConcealedTiles[tileTypeId]); Chiitoi.Discard(ConcealedTiles[tileTypeId]); Base5Hashes[suit] -= Base5.Table[index]; localArrangements[suit] = SuitClassifiers[suit].GetValue(ConcealedTiles, suit, Base5Hashes); var newShanten = CalculateShanten(localArrangements); if (newShanten == currentShanten) { var ukeIre = SumUkeIre(currentShanten, localArrangements, HonorClassifier); if (ukeIre > highestUkeIre) { highestUkeIre = ukeIre; highestUkeIreDiscard = tileTypeId; } } Base5Hashes[suit] += Base5.Table[index]; Kokushi.Draw(kyuuhaiValue, ConcealedTiles[tileTypeId]); Chiitoi.Draw(ConcealedTiles[tileTypeId]); ConcealedTiles[tileTypeId] += 1; InHandByType[tileTypeId] += 1; } tileTypeId += 1; } localArrangements[suit] = ArrangementValues[suit]; } for (var index = 0; index < 7; ++index) { if (ConcealedTiles[tileTypeId] > 0) { ConcealedTiles[tileTypeId] -= 1; InHandByType[tileTypeId] -= 1; var tileCountAfterDiscard = ConcealedTiles[tileTypeId]; Kokushi.Discard(1, tileCountAfterDiscard); Chiitoi.Discard(tileCountAfterDiscard); var localHonorClassifier = HonorClassifier.Clone(); localArrangements[3] = localHonorClassifier.Discard(tileCountAfterDiscard, JihaiMeldBit >> index & 1); var newShanten = CalculateShanten(localArrangements); if (newShanten == currentShanten) { var ukeIre = SumUkeIre(currentShanten, localArrangements, localHonorClassifier); if (ukeIre > highestUkeIre) { highestUkeIre = ukeIre; highestUkeIreDiscard = tileTypeId; } } Chiitoi.Draw(tileCountAfterDiscard); Kokushi.Draw(1, tileCountAfterDiscard); ConcealedTiles[tileTypeId] += 1; InHandByType[tileTypeId] += 1; } tileTypeId += 1; } Debug.Assert(highestUkeIreDiscard != -1, "There should always be a tile to discard."); return(highestUkeIreDiscard); }
private int SumUkeIre(int currentShanten, int[] arrangements, ProgressiveHonorClassifier localHonorClassifier) { var ukeIre = 0; var tileTypeId = 0; var localArrangements = new[] { arrangements[0], arrangements[1], arrangements[2], arrangements[3] }; for (var suit = 0; suit < 3; ++suit) { for (var index = 0; index < 9; ++index) { if (InHandByType[tileTypeId] != 4) { var kyuuhaiValue = (0b100000001 >> index) & 1; Kokushi.Draw(kyuuhaiValue, ConcealedTiles[tileTypeId]); Chiitoi.Draw(ConcealedTiles[tileTypeId]); ConcealedTiles[tileTypeId] += 1; Base5Hashes[suit] += Base5.Table[index]; localArrangements[suit] = SuitClassifiers[suit].GetValue(ConcealedTiles, suit, Base5Hashes); var newShanten = CalculateShanten(localArrangements); Debug.Assert(currentShanten >= newShanten); var a = currentShanten - newShanten; var t = (4 - InHandByType[tileTypeId]) * a; ukeIre += t; ConcealedTiles[tileTypeId] -= 1; Base5Hashes[suit] -= Base5.Table[index]; Kokushi.Discard(kyuuhaiValue, ConcealedTiles[tileTypeId]); Chiitoi.Discard(ConcealedTiles[tileTypeId]); } tileTypeId += 1; } localArrangements[suit] = arrangements[suit]; } for (var index = 0; index < 7; ++index) { if (InHandByType[tileTypeId] != 4) { var previousTileCount = ConcealedTiles[tileTypeId]; Kokushi.Draw(1, previousTileCount); Chiitoi.Draw(previousTileCount); localArrangements[3] = localHonorClassifier.Clone().Draw(previousTileCount, JihaiMeldBit >> index & 1); var newShanten = CalculateShanten(localArrangements); Debug.Assert(currentShanten >= newShanten); var a = currentShanten - newShanten; var t = (4 - InHandByType[tileTypeId]) * a; ukeIre += t; Kokushi.Discard(1, previousTileCount); Chiitoi.Discard(previousTileCount); } tileTypeId += 1; } return(ukeIre); }