Skip to content

Commit

Permalink
Merge pull request #48 from Ledenel/add-batch-waiting-check
Browse files Browse the repository at this point in the history
add-batch-waiting-check
  • Loading branch information
Ledenel authored Feb 4, 2020
2 parents 0d4782a + f2f0f65 commit 6fb9b75
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 15 deletions.
22 changes: 20 additions & 2 deletions mahjong/container/pattern/reasoning.py
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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, sub_win, TileSet())
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,
Expand Down
18 changes: 13 additions & 5 deletions mahjong/templates/record_checker_template.html
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,13 @@
.description-table {
display: inline-table;
}

.blackbox {
border-color: black;
border-width: medium;
border-style: dashed;
display: inline-block;
}
</style>
</head>
<body>
Expand All @@ -80,9 +87,10 @@
{% for reasoning in selected %}
<div class="reasoning-tile">
<div>Discard&nbsp;{{ tile(reasoning.discard_tile, 'small') }}
{{ reasoning.waiting_step }}
Steps
{{ reasoning.useful_tiles_count }} Tiles
<div class="blackbox">
Steps {{ reasoning.waiting_step }}
Tiles {{ reasoning.useful_tiles_count }}
</div>
</div>
<div>Expect {{ tile(reasoning.useful_tiles, 'small') }}
</div>
Expand Down Expand Up @@ -143,12 +151,12 @@
<tr>
<td>Expected</td>
<td>Steps {{ round.expected_reasonings[0].waiting_step }}
Tiles {{ round.expected_reasonings[0].useful_tiles_count }}</td>
Tiles {{ round.expected_reasonings[0].useful_tiles_count }}</td>
</tr>
<tr>
<td>Actual</td>
<td>Steps {{ round.your_choice_reasoning.waiting_step }}
Tiles {{ round.your_choice_reasoning.useful_tiles_count }}</td>
Tiles {{ round.your_choice_reasoning.useful_tiles_count }}</td>
</tr>
</table>
</summary>
Expand Down
33 changes: 27 additions & 6 deletions mahjong/tenhou_record_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -242,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.
Expand Down Expand Up @@ -287,10 +289,29 @@ 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):
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):
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)
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setup(
name="auto_white_reimu",
version="0.2.1",
version="0.2.2",
packages=find_packages(),
url="",
license="GPL",
Expand Down
43 changes: 42 additions & 1 deletion tests/algo/test_heuristic_pattern_waiting.py
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -75,3 +77,42 @@ 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]] = 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: 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)

0 comments on commit 6fb9b75

Please sign in to comment.