public override bool is_condition_met(List <List <int> > hand, params object[] args) { int honor = 0; int sou = 0; int pin = 0; int man = 0; foreach (var group in hand) { if (C.HONOR_INDICES.Contains(group[0])) { honor++; } if (U.is_sou(group[0])) { sou++; } else if (U.is_man(group[0])) { man++; } else if (U.is_pin(group[0])) { pin++; } } return(honor > 0 && ((sou > 0 && pin + man == 0) || (man > 0 && pin + sou == 0) || (pin > 0 && sou + man == 0) )); }
private static List <List <int> > GetPossibleSetsFromIndices2(List <int> list) { var results = new List <List <int> >(); for (int t1 = 0; t1 < list.Count; t1++) { for (int t2 = t1; t2 < list.Count; t2++) { if (t2 == t1) { continue; } for (int t3 = t2; t3 < list.Count; t3++) { if (t3 == t2 || t3 == t1) { continue; } var result = new List <int> { list[t1], list[t2], list[t3] }; if (U.is_chi(result) || U.is_pon(result)) { results.Add(result); } } } } return(results); }
public override bool is_condition_met(List <List <int> > hand, params object[] args) { int honors = 0; int terminals = 0; int chi = 0; foreach (var group in hand) { if (U.is_chi(group)) { chi++; } if (U.are_tiles_in_indices(group, C.TERMINAL_INDICES)) { terminals++; } if (U.are_tiles_in_indices(group, C.HONOR_INDICES)) { honors++; } } //honroutou if (chi == 0) { return(false); } return(terminals + honors == 5 && terminals != 0 && honors != 0); }
public override bool is_condition_met(List <List <int> > hand, params object[] args) { int sou = 0; int pin = 0; int man = 0; int honor = 0; if (U.is_sou(hand[0][0])) { sou++; } else if (U.is_pin(hand[0][0])) { pin++; } else if (U.is_man(hand[0][0])) { man++; } else { honor++; } bool allowOtherSets = (bool)args[0]; bool onlyOneSuit = sou + pin + man + honor == 1; if (!onlyOneSuit || honor > 0) { return(false); } if (!allowOtherSets && pin == 0) { //if we are not allowing other sets than pins return(false); } var indicesCount = new int[9]; foreach (var set in hand) { foreach (var tile in set) { indicesCount[U.simplify(tile)]++; } } foreach (var count in indicesCount) { if (count != 2) { return(false); } } return(true); }
// Replaced the original "get possible sets" block by this, seems more efficient and compact // Also: binary count browsing is cool // Input: sorted array of indices private static List <List <int> > GetPossibleSetsFromIndices(List <int> list) { var array = list.ToArray(); int count = (int)Math.Pow(2, array.Length); //We know we'll iterate on 2^n possibilities int min = (int)Math.Pow(2, 3) - 1; //start at b'...000000111' since it's the first result of length >= 3 int max = count - (int)Math.Pow(2, array.Length - 3); //end at b'11100000...' since it's the last result of length <= 3 (12% faster) var results = new List <List <int> >(); if (list.Count < 3) { return(results); } int lastTile = -1; int currentTile; int nbTile; bool ok; for (int i = min; i <= max; i++) { var result = new List <int>(); string str = Convert.ToString(i, 2).PadLeft(array.Length, '0'); //we take i and convert it to a n bit integer, each value of i representing a possibility nbTile = 0; ok = true; for (int j = 0; j < str.Length; j++) { if (str[j] == '1') { if (nbTile == 3) { ok = false; break; // result is too large so let's stop here (might be the only useful optim here...) } currentTile = array[j]; if (currentTile - lastTile > 1 && lastTile >= 0) { ok = false; break; // there is no way a group containing 'x,x+2' is a set } result.Add(currentTile); lastTile = currentTile; nbTile++; } } if (ok && (U.is_chi(result) || U.is_pon(result))) { results.Add(result); } } return(results); }
public void rename(List <List <int> > hand) { if (U.is_sou(hand[0][0])) { this.name = "Daichikurin"; } else if (U.is_pin(hand[0][0])) { this.name = "Daisharin"; } else if (U.is_man(hand[0][0])) { this.name = "Daisuurin"; } }
public override bool is_condition_met(List <List <int> > hand, params object[] args) { var chis = hand.Where(x => Utils.is_chi(x)); if (chis.Count() < 3) { return(false); } var sou = new List <List <int> >(); var pin = new List <List <int> >(); var man = new List <List <int> >(); foreach (var chi in chis) { if (U.is_sou(chi[0])) { sou.Add(chi); } else if (U.is_man(chi[0])) { man.Add(chi); } else if (U.is_pin(chi[0])) { pin.Add(chi); } } var suits = new List <List <List <int> > > { sou, man, pin }; var one = new List <int> { 0, 1, 2 }; var two = new List <int> { 3, 4, 5 }; var three = new List <int> { 6, 7, 8 }; var comp = new GroupComparer <int>(); foreach (var suit in suits) { if (suit.Count() < 3) { continue; } var simpleSets = new List <List <int> >(); foreach (var set in suit) { simpleSets.Add(new List <int> { U.simplify(set[0]), U.simplify(set[1]), U.simplify(set[2]) }); } if (simpleSets.Contains(one, comp) && simpleSets.Contains(two, comp) && simpleSets.Contains(three, comp)) { return(true); } } return(false); }
// // Find and return all valid set combinations in given suit // :param tiles_34: // :param first_index: // :param second_index: // :param hand_not_completed: in that mode we can return just possible shi or pon sets // :return: list of valid combinations // List <List <List <int> > > find_valid_combinations(int[] tiles_34, int first_index, int second_index, bool hand_not_completed = false) { int count_of_sets; var indices = new List <int>(); foreach (var x in Enumerable.Range(first_index, second_index + 1 - first_index)) { if (tiles_34[x] > 0) { indices.AddRange(Enumerable.Repeat(x, tiles_34[x]).ToList()); } } var count_of_needed_combinations = Convert.ToInt32(indices.Count / 3); if (indices.Count == 0 || count_of_needed_combinations == 0) { return(new List <List <List <int> > > { new List <List <int> >() }); } // indices are already sorted var validMelds = GetPossibleSetsFromIndices(indices); if (validMelds.Count == 0) { return(new List <List <List <int> > > { new List <List <int> >() }); } // simple case, we have count of sets == count of tiles if (count_of_needed_combinations == validMelds.Count) { var toCheck = validMelds.Select(x => x.ToList() as IEnumerable <int>).Aggregate((z, y) => z.Concat(y)); if (toCheck.SequenceEqual(indices)) { return(new List <List <List <int> > >() { validMelds }); } } // filter and remove not possible pon sets IEnumerable <List <int> > identicalSets; foreach (var item in validMelds.ToArray().ToList()) { if (U.is_pon(item)) { count_of_sets = 1; var count_of_tiles = 0; while (count_of_sets > count_of_tiles) { count_of_tiles = (from x in indices where x == item[0] select x).ToList().Count / 3; identicalSets = (from x in validMelds where x[0] == item[0] && x[1] == item[1] && x[2] == item[2] select x); count_of_sets = identicalSets.Count(); if (count_of_sets > count_of_tiles) { validMelds.Remove(identicalSets.First()); } } } } // filter and remove not possible chi sets foreach (var item in validMelds.ToArray().ToList()) { if (U.is_chi(item)) { count_of_sets = 5; // TODO calculate real count of possible sets var count_of_possible_sets = 4; while (count_of_sets > count_of_possible_sets) { identicalSets = (from x in validMelds where x[0] == item[0] && x[1] == item[1] && x[2] == item[2] select x); count_of_sets = identicalSets.Count(); if (count_of_sets > count_of_possible_sets) { validMelds.Remove(identicalSets.First()); } } } } // lit of chi\pon sets for not completed hand if (hand_not_completed) { return(new List <List <List <int> > >() { validMelds }); } // hard case - we can build a lot of sets from our tiles // for example we have 123456 tiles and we can build sets: // [1, 2, 3] [4, 5, 6] [2, 3, 4] [3, 4, 5] // and only two of them valid in the same time [1, 2, 3] [4, 5, 6] int maxIndex = indices.Max() + 1; var indexCount = new int[maxIndex]; foreach (var index in indices) { indexCount[index]++; } var possibleHands = GetPossibleHandsFromSets(validMelds, indexCount, count_of_needed_combinations); return(possibleHands); }
// // Calculate hand fu with explanations // :param hand: // :param win_tile: 136 tile format // :param win_group: one set where win tile exists // :param config: HandConfig object // :param valued_tiles: dragons, player wind, round wind // :param melds: opened sets // :return: // public static (List <(int, string)>, int) calculate_fu( List <List <int> > hand, int win_tile, List <int> win_group, HandConfig config, List <int> valued_tiles = null, List <Meld> melds = null) { var win_tile_34 = win_tile / 4; if (valued_tiles == null) { valued_tiles = new List <int>(); } if (melds == null) { melds = new List <Meld>(); } var fu_details = new List <(int, string)>(); if (hand.Count == 7) { return(new List <(int, string)>() { (25, BASE) }, 25); } var pair = (from x in hand where U.is_pair(x) select x).ToList()[0]; var pon_sets = (from x in hand where U.is_pon_or_kan(x) select x).ToList(); var copied_opened_melds = (from x in melds where x.type == Meld.CHI select x.tiles_34).ToList(); var closed_chi_sets = new List <List <int> >(); foreach (var x in hand) { if (!copied_opened_melds.Contains(x)) { closed_chi_sets.Add(x); } else { copied_opened_melds.Remove(x); } } var is_open_hand = (from x in melds select x.opened).Any(); if (closed_chi_sets.Contains(win_group)) { var tile_index = U.simplify(win_tile_34); // penchan if (U.contains_terminals(win_group)) { // 1-2-... wait if (tile_index == 2 && win_group.FindIndex(x => x == win_tile_34) == 2) { fu_details.Add((2, PENCHAN)); } else if (tile_index == 6 && win_group.FindIndex(x => x == win_tile_34) == 0) { // 8-9-... wait fu_details.Add((2, PENCHAN)); } } // kanchan waiting 5-...-7 if (win_group.FindIndex(x => x == win_tile_34) == 1) { fu_details.Add((2, KANCHAN)); } } // valued pair var count_of_valued_pairs = valued_tiles.Count(x => x == pair[0]); if (count_of_valued_pairs == 1) { fu_details.Add((2, VALUED_PAIR)); } // east-east pair when you are on east gave double fu if (count_of_valued_pairs == 2) { fu_details.Add((4, DOUBLE_VALUED_PAIR)); } // pair wait if (U.is_pair(win_group)) { fu_details.Add((2, PAIR_WAIT)); } foreach (var set_item in pon_sets) { var open_meld = (from x in melds where set_item == x.tiles_34 select x).ToList().FirstOrDefault(); var set_was_open = open_meld != null && open_meld.opened; var is_kan_set = open_meld != null && (open_meld.type == Meld.KAN || open_meld.type == Meld.SHOUMINKAN); var is_honor = (C.TERMINAL_INDICES.Concat(C.HONOR_INDICES)).Contains(set_item[0]); // we win by ron on the third pon tile, our pon will be count as open if (!config.is_tsumo && set_item == win_group) { set_was_open = true; } if (is_honor) { if (is_kan_set) { if (set_was_open) { fu_details.Add((16, OPEN_TERMINAL_KAN)); } else { fu_details.Add((32, CLOSED_TERMINAL_KAN)); } } else if (set_was_open) { fu_details.Add((4, OPEN_TERMINAL_PON)); } else { fu_details.Add((8, CLOSED_TERMINAL_PON)); } } else if (is_kan_set) { if (set_was_open) { fu_details.Add((8, OPEN_KAN)); } else { fu_details.Add((16, CLOSED_KAN)); } } else if (set_was_open) { fu_details.Add((2, OPEN_PON)); } else { fu_details.Add((4, CLOSED_PON)); } } var add_tsumo_fu = fu_details.Count > 0 || config.options.fu_for_pinfu_tsumo; if (config.is_tsumo && add_tsumo_fu) { // 2 additional fu for tsumo (but not for pinfu) fu_details.Add((2, TSUMO)); } if (is_open_hand && fu_details.Count == 0 && config.options.fu_for_open_pinfu) { // there is no 1-20 hands, so we have to add additional fu fu_details.Add((2, HAND_WITHOUT_FU)); } if (is_open_hand || config.is_tsumo) { fu_details.Add((20, BASE)); } else { fu_details.Add((30, BASE)); } return(fu_details, round_fu(fu_details)); }