From 7e0603cb5247830d4686deec33bd88fc74da9a4b Mon Sep 17 00:00:00 2001 From: Ledenel Date: Mon, 3 Feb 2020 21:54:58 +0800 Subject: [PATCH 1/6] add batch waiting check (no test.) --- mahjong/container/pattern/reasoning.py | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/mahjong/container/pattern/reasoning.py b/mahjong/container/pattern/reasoning.py index 8267861..f99c709 100644 --- a/mahjong/container/pattern/reasoning.py +++ b/mahjong/container/pattern/reasoning.py @@ -1,6 +1,8 @@ import logging +import operator from abc import ABCMeta, abstractmethod from collections import defaultdict +from functools import reduce from itertools import count, product, chain, groupby from typing import List @@ -158,13 +160,29 @@ def useful_tiles(self, hand: TileSet, ignore_4counts=True): def waiting_and_useful_tiles(self, hand, ignore_4counts=True): logging.debug("finding waiting step") self_waiting = self.before_waiting_step(hand) + result_iter = self.count_hand_borrow_combinition_iter(hand, ignore_4counts, self_waiting) + + return self_waiting, set(tile for _, _, borrows in result_iter for tile in borrows) + + def batch_waiting_and_useful_tiles(self, hand, ignore_4counts=True): + waiting_step = self.before_waiting_step(hand) + result_iter = self.count_hand_borrow_combinition_iter(hand, ignore_4counts, waiting_step) + all_hand_cnt = defaultdict(set) + for _, sub_win, borrows in result_iter: + hand_win = reduce(operator.add, TileSet(), sub_win) + can_drops = hand - hand_win + for can_pick_tile in can_drops: + all_hand_cnt[can_pick_tile].update(borrows) + for tile, hand_useful in all_hand_cnt.items(): + yield tile, waiting_step, hand_useful + + def count_hand_borrow_combinition_iter(self, hand, ignore_4counts, self_waiting): borrows = self.init_search_by_hand(hand) self.max_used_tiles = self_waiting + 1 logging.debug("finding useful tiles") result_iter = self._win_selections_in_tiles(hand, ignore_4counts, self.win_pattern, borrowed_limit(hand), borrows, 0, 0) - - return self_waiting, set(tile for _, _, borrows in result_iter for tile in borrows) + return result_iter def _win_selections_in_tiles(self, hand: TileSet, ignore_4counts, current_state: WinPattern, borrow_limits: TileSet, searching_group: List[List[Tile]], borrowed_stage, From ee76f5a0d59042e57542bf0590922916cfea9d17 Mon Sep 17 00:00:00 2001 From: Ledenel Date: Mon, 3 Feb 2020 22:25:46 +0800 Subject: [PATCH 2/6] add test skeleton. --- tests/algo/test_heuristic_pattern_waiting.py | 41 +++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/tests/algo/test_heuristic_pattern_waiting.py b/tests/algo/test_heuristic_pattern_waiting.py index d9d0efd..82c69af 100644 --- a/tests/algo/test_heuristic_pattern_waiting.py +++ b/tests/algo/test_heuristic_pattern_waiting.py @@ -1,9 +1,11 @@ +import re + import pytest from mahjong.container.pattern.reasoning import HeuristicPatternMatchWaiting from mahjong.container.pattern.win import NormalTypeWin, UniquePairs from mahjong.container.set import TileSet -from mahjong.container.utils import tile_set_from_string +from mahjong.container.utils import tile_set_from_string, tiles_from_string hands = [ "11266677788992m", @@ -75,3 +77,40 @@ def test_heuristic_seven_pair_waiting_step(hand, shanten): def test_heuristic_useful_tiles(hand, useful): assert TileSet(HeuristicPatternMatchWaiting(NormalTypeWin()).useful_tiles(tile_set_from_string(hand))) \ == tile_set_from_string(useful) + + +record_regex = re.compile("打([a-z0-9]+) 摸\\[([a-z0-9]+) [0-9]+枚\\]") + + + +recs = [""" +589m1156p3089s15z1p +打5m 摸[7m8m9m4p5p6p7p3s4s5s7s8s9s1z5z 50枚] +打1z 摸[5m7m8m9m4p5p6p7p3s4s5s7s8s9s5z 50枚] +打5z 摸[5m7m8m9m4p5p6p7p3s4s5s7s8s9s1z 50枚] +打8m 摸[5m9m4p7p4s7s1z5z 28枚] +打9m 摸[5m8m4p7p4s7s1z5z 28枚] +打3s 摸[5m7m4p7p5s7s1z5z 28枚] +打5s 摸[5m7m4p7p3s7s1z5z 28枚] +打8s 摸[5m7m4p7p4s9s1z5z 28枚] +打9s 摸[5m7m4p7p4s8s1z5z 28枚] +打5p 摸[5m7m6p4s7s1z5z 24枚] +打6p 摸[5m7m5p4s7s1z5z 24枚] +打1p 摸[7m4p7p4s7s 20枚] +""",] + + +def record_convert(s): + lines = [x for x in s.split("\n") if x.strip() != ""] + first,rest = lines[0], lines[1:] + dic = {} + for line in rest: + matched = record_regex.match(line) + dic[list(tiles_from_string(matched.group(1)))[0]] = TileSet(matched.group(2)) + return tile_set_from_string(first), dic + +record_converted = [record_convert(s) for s in recs] + +@pytest.mark.parametrize("hand_rec", record_converted, ids=lambda t:t[0]) +def test_batch_convert(hand_rec): + hand, record_map = hand_rec From 3e96cfdb68eeda4a9e0af915552138a04836d678 Mon Sep 17 00:00:00 2001 From: Ledenel Date: Tue, 4 Feb 2020 12:46:27 +0800 Subject: [PATCH 3/6] tested batched pattern matching. --- mahjong/container/pattern/reasoning.py | 2 +- tests/algo/test_heuristic_pattern_waiting.py | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/mahjong/container/pattern/reasoning.py b/mahjong/container/pattern/reasoning.py index f99c709..faafa6d 100644 --- a/mahjong/container/pattern/reasoning.py +++ b/mahjong/container/pattern/reasoning.py @@ -169,7 +169,7 @@ def batch_waiting_and_useful_tiles(self, hand, ignore_4counts=True): result_iter = self.count_hand_borrow_combinition_iter(hand, ignore_4counts, waiting_step) all_hand_cnt = defaultdict(set) for _, sub_win, borrows in result_iter: - hand_win = reduce(operator.add, TileSet(), sub_win) + hand_win = reduce(operator.add, sub_win, TileSet()) can_drops = hand - hand_win for can_pick_tile in can_drops: all_hand_cnt[can_pick_tile].update(borrows) diff --git a/tests/algo/test_heuristic_pattern_waiting.py b/tests/algo/test_heuristic_pattern_waiting.py index 82c69af..3c7c2fa 100644 --- a/tests/algo/test_heuristic_pattern_waiting.py +++ b/tests/algo/test_heuristic_pattern_waiting.py @@ -81,8 +81,6 @@ def test_heuristic_useful_tiles(hand, useful): record_regex = re.compile("打([a-z0-9]+) 摸\\[([a-z0-9]+) [0-9]+枚\\]") - - recs = [""" 589m1156p3089s15z1p 打5m 摸[7m8m9m4p5p6p7p3s4s5s7s8s9s1z5z 50枚] @@ -97,20 +95,24 @@ def test_heuristic_useful_tiles(hand, useful): 打5p 摸[5m7m6p4s7s1z5z 24枚] 打6p 摸[5m7m5p4s7s1z5z 24枚] 打1p 摸[7m4p7p4s7s 20枚] -""",] +""", ] def record_convert(s): lines = [x for x in s.split("\n") if x.strip() != ""] - first,rest = lines[0], lines[1:] + first, rest = lines[0], lines[1:] dic = {} for line in rest: matched = record_regex.match(line) - dic[list(tiles_from_string(matched.group(1)))[0]] = TileSet(matched.group(2)) + dic[list(tiles_from_string(matched.group(1)))[0]] = tile_set_from_string(matched.group(2)) return tile_set_from_string(first), dic + record_converted = [record_convert(s) for s in recs] -@pytest.mark.parametrize("hand_rec", record_converted, ids=lambda t:t[0]) + +@pytest.mark.parametrize("hand_rec", record_converted, ids=lambda t: str(t[0])) def test_batch_convert(hand_rec): hand, record_map = hand_rec + for tile, step, useful in HeuristicPatternMatchWaiting(NormalTypeWin()).batch_waiting_and_useful_tiles(hand): + assert record_map[tile] == TileSet(useful) From dec6e5a56171f925f74bcb7719a3bc4b2803d9ac Mon Sep 17 00:00:00 2001 From: Ledenel Date: Tue, 4 Feb 2020 13:07:04 +0800 Subject: [PATCH 4/6] refactor for batch changing. --- mahjong/tenhou_record_check.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/mahjong/tenhou_record_check.py b/mahjong/tenhou_record_check.py index 87c6e56..f0c707e 100644 --- a/mahjong/tenhou_record_check.py +++ b/mahjong/tenhou_record_check.py @@ -230,10 +230,7 @@ def discard_reasoning(discard_event, hand_state, invisible_tiles_state, player, reasoning_names = ["normal_reasonings", "seven_pair_reasonings"] if meld_count == 0: win_types.append(UniquePairs()) - win_reasonings = [ - list(reasoning_discards(hand, invisible_player_perspective, tile, win) for tile in hand) - for win in win_types - ] + win_reasonings = all_win_type_reasoning(hand, invisible_player_perspective, win_types) merged_win_reasonings = [ reasoning_merge(list(reasoning_same_discard), invisible_player_perspective) for reasoning_same_discard in zip(*win_reasonings) @@ -287,10 +284,23 @@ def discard_reasoning(discard_event, hand_state, invisible_tiles_state, player, return round_reasoning +def all_win_type_reasoning(hand, invisible_player_perspective, win_types): + for win in win_types: + yield list(one_win_reasoning(hand, invisible_player_perspective, win)) + + +def one_win_reasoning(hand, invisible_player_perspective, win): + return (reasoning_discards(hand, invisible_player_perspective, tile, win) for tile in hand) + + def reasoning_discards(hand, invisible_player_perspective, tile, win): hand_temp = hand - TileSet([tile]) reasoning = HeuristicPatternMatchWaiting(win) waiting_step, useful_tiles = reasoning.waiting_and_useful_tiles(hand_temp) + return convert_to_reasoning(invisible_player_perspective, tile, useful_tiles, waiting_step) + + +def convert_to_reasoning(invisible_player_perspective, tile, useful_tiles, waiting_step): useful_tiles_count = sum(len(set(tile_to_tenhou_range(tile)) & invisible_player_perspective) for tile in useful_tiles) return ReasoningItem(tile, waiting_step, useful_tiles, useful_tiles_count) From 1e438e2f14128a708f11aec7b65700daa6247b24 Mon Sep 17 00:00:00 2001 From: Ledenel Date: Tue, 4 Feb 2020 13:36:46 +0800 Subject: [PATCH 5/6] adjust to necessary calculations. --- mahjong/templates/record_checker_template.html | 18 +++++++++++++----- mahjong/tenhou_record_check.py | 17 ++++++++++++++--- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/mahjong/templates/record_checker_template.html b/mahjong/templates/record_checker_template.html index 6b2b72e..079248d 100644 --- a/mahjong/templates/record_checker_template.html +++ b/mahjong/templates/record_checker_template.html @@ -63,6 +63,13 @@ .description-table { display: inline-table; } + + .blackbox { + border-color: black; + border-width: medium; + border-style: dashed; + display: inline-block; + } @@ -80,9 +87,10 @@ {% for reasoning in selected %}
Discard {{ tile(reasoning.discard_tile, 'small') }} - {{ reasoning.waiting_step }} - Steps - {{ reasoning.useful_tiles_count }} Tiles +
+ Steps {{ reasoning.waiting_step }} + Tiles {{ reasoning.useful_tiles_count }} +
Expect {{ tile(reasoning.useful_tiles, 'small') }}
@@ -143,12 +151,12 @@ Expected Steps {{ round.expected_reasonings[0].waiting_step }} - Tiles {{ round.expected_reasonings[0].useful_tiles_count }} + Tiles {{ round.expected_reasonings[0].useful_tiles_count }} Actual Steps {{ round.your_choice_reasoning.waiting_step }} - Tiles {{ round.your_choice_reasoning.useful_tiles_count }} + Tiles {{ round.your_choice_reasoning.useful_tiles_count }} diff --git a/mahjong/tenhou_record_check.py b/mahjong/tenhou_record_check.py index f0c707e..31cbca9 100644 --- a/mahjong/tenhou_record_check.py +++ b/mahjong/tenhou_record_check.py @@ -239,8 +239,13 @@ def discard_reasoning(discard_event, hand_state, invisible_tiles_state, player, _, expected_reasonings = next(groupby(merged_win_reasonings, key=reasoning_key)) expected_reasonings = list(expected_reasonings) your_choice_tile = tile_from_tenhou(player.discard_tile_index(discard_event)) - your_choice_reasoning = find_in_list(merged_win_reasonings, - key=lambda x: x.discard_tile == your_choice_tile) + your_choice_reasoning = reasoning_merge( + [reasoning_discards(hand, invisible_player_perspective, your_choice_tile, win) + for win in win_types + ], invisible_player_perspective + ) + # find_in_list(merged_win_reasonings, + # key=lambda x: x.discard_tile == your_choice_tile) for win_reasoning in win_reasonings: win_reasoning.sort(key=reasoning_key) # TODO add wrong rate for reason display. @@ -290,7 +295,13 @@ def all_win_type_reasoning(hand, invisible_player_perspective, win_types): def one_win_reasoning(hand, invisible_player_perspective, win): - return (reasoning_discards(hand, invisible_player_perspective, tile, win) for tile in hand) + reasoning = HeuristicPatternMatchWaiting(win) + # analysed_tiles = set() + for tile, step, useful in reasoning.batch_waiting_and_useful_tiles(hand): + # analysed_tiles.add(tile) + yield convert_to_reasoning(invisible_player_perspective, tile, useful, step) + + # return (reasoning_discards(hand, invisible_player_perspective, tile, win) for tile in hand) def reasoning_discards(hand, invisible_player_perspective, tile, win): From f2f0f652e6a9c0324737c1ed9b047bc0cf2b0d97 Mon Sep 17 00:00:00 2001 From: Ledenel Date: Tue, 4 Feb 2020 13:42:22 +0800 Subject: [PATCH 6/6] increase setup.py speed. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index b72ad36..d21ad3e 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="auto_white_reimu", - version="0.2.1", + version="0.2.2", packages=find_packages(), url="", license="GPL",