From 4eb098e8b8310f80364a92fa32ef756f80bdb3d8 Mon Sep 17 00:00:00 2001 From: bogachev-pa Date: Fri, 24 Aug 2018 15:50:08 +0300 Subject: [PATCH 1/6] Add 'Formal Tempai' strategy --- project/game/ai/first_version/main.py | 13 +++ .../first_version/strategies/formal_tempai.py | 79 +++++++++++++++++++ .../game/ai/first_version/strategies/main.py | 2 + .../tests/strategies/tests_formal_tempai.py | 34 ++++++++ .../tests/strategies/tests_yakuhai.py | 4 +- 5 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 project/game/ai/first_version/strategies/formal_tempai.py create mode 100644 project/game/ai/first_version/tests/strategies/tests_formal_tempai.py diff --git a/project/game/ai/first_version/main.py b/project/game/ai/first_version/main.py index 39daf41b..b6be12ce 100644 --- a/project/game/ai/first_version/main.py +++ b/project/game/ai/first_version/main.py @@ -19,6 +19,7 @@ from game.ai.first_version.strategies.main import BaseStrategy from game.ai.first_version.strategies.tanyao import TanyaoStrategy from game.ai.first_version.strategies.yakuhai import YakuhaiStrategy +from game.ai.first_version.strategies.formal_tempai import FormalTempaiStrategy logger = logging.getLogger('ai') @@ -34,6 +35,8 @@ class ImplementationAI(InterfaceAI): last_discard_option = None previous_shanten = 7 + ukeire = 0 + tiles_count_second_level = 0 in_defence = False waiting = None @@ -48,6 +51,8 @@ def __init__(self, player): self.hand_divider = HandDivider() self.finished_hand = HandCalculator() self.previous_shanten = 7 + self.ukeire = 0 + self.tiles_count_second_level = 0 self.current_strategy = None self.waiting = [] self.in_defence = False @@ -61,8 +66,12 @@ def init_hand(self): def erase_state(self): self.current_strategy = None + self.waiting = [] self.in_defence = False self.last_discard_option = None + self.previous_shanten = 7 + self.ukeire = 0 + self.tiles_count_second_level = 0 def draw_tile(self, tile): """ @@ -203,6 +212,8 @@ def determine_strategy(self): if self.player.table.has_open_tanyao: strategies.append(TanyaoStrategy(BaseStrategy.TANYAO, self.player)) + strategies.append(FormalTempaiStrategy(BaseStrategy.FORMAL_TEMPAI, self.player)) + for strategy in strategies: if strategy.should_activate_strategy(): self.current_strategy = strategy @@ -311,6 +322,8 @@ def process_discard_option(self, discard_option, closed_hand, force_discard=Fals self.waiting = discard_option.waiting self.player.ai.previous_shanten = discard_option.shanten self.player.in_tempai = self.player.ai.previous_shanten == 0 + self.player.ai.ukeire = discard_option.tiles_count + self.player.ai.tiles_count_second_level = discard_option.tiles_count_second_level # when we called meld we don't need "smart" discard if force_discard: diff --git a/project/game/ai/first_version/strategies/formal_tempai.py b/project/game/ai/first_version/strategies/formal_tempai.py new file mode 100644 index 00000000..fa96b0f1 --- /dev/null +++ b/project/game/ai/first_version/strategies/formal_tempai.py @@ -0,0 +1,79 @@ +# -*- coding: utf-8 -*- +from mahjong.meld import Meld +from mahjong.tile import TilesConverter + +from game.ai.first_version.strategies.main import BaseStrategy + + +class FormalTempaiStrategy(BaseStrategy): + + def should_activate_strategy(self): + """ + When we get closer to the end of the round, we start to consider + going for formal tempai. + :return: boolean + """ + + result = super(FormalTempaiStrategy, self).should_activate_strategy() + if not result: + return False + + # if we already in tempai, we don't need this strategy + if self.player.in_tempai: + return False + + # it's too early to go for formal tempai before 11th turn + if self.player.round_step < 11: + return False + + # it's 11th turn or later and we still have 3 shanten or more, + # let's try to go for formal tempai at least + if self.player.ai.previous_shanten >= 3: + return True + + dora_count = sum([plus_dora(x, self.player.table.dora_indicators) for x in self.player.tiles]) + dora_count += sum([1 for x in self.player.tiles if is_aka_dora(x, self.player.table.has_open_tanyao)]) + + if self.player.ai.previous_shanten == 2: + if dora_count < 2: + # having 0 or 1 dora and 2 shanten, let's go for formal tempai + # starting from 11th turn + return True + else: + # having 2 or more doras and 2 shanten, let's go for formal + # tempai starting from 12th turn + return (self.player.round_step >= 12) + + # for 1 shanten we check number of doras and ukeire to determine + # correct time to go for formal tempai + if self.player.ai.previous_shanten == 1: + if dora_count == 0: + if self.player.ai.ukeire <= 16: + return True + elif self.player.ai.ukeire <= 28: + return (self.player.round_step >= 12) + else: + return (self.player.round_step >= 13) + elif dora_count == 1: + if self.player.ai.ukeire <= 16: + return (self.player.round_step >= 12) + elif self.player.ai.ukeire <= 28: + return (self.player.round_step >= 13) + else: + return (self.player.round_step >= 14) + else: + if self.player.ai.ukeire <= 16: + return (self.player.round_step >= 13) + else: + return (self.player.round_step >= 14) + + # we actually never reach here + return False + + def is_tile_suitable(self, tile): + """ + All tiles are suitable for formal tempai. + :param tile: 136 tiles format + :return: True + """ + return True diff --git a/project/game/ai/first_version/strategies/main.py b/project/game/ai/first_version/strategies/main.py index 84e7c787..bf714801 100644 --- a/project/game/ai/first_version/strategies/main.py +++ b/project/game/ai/first_version/strategies/main.py @@ -8,11 +8,13 @@ class BaseStrategy(object): YAKUHAI = 0 HONITSU = 1 TANYAO = 2 + FORMAL_TEMPAI = 3 TYPES = { YAKUHAI: 'Yakuhai', HONITSU: 'Honitsu', TANYAO: 'Tanyao', + FORMAL_TEMPAI: 'Formal Tempai' } player = None diff --git a/project/game/ai/first_version/tests/strategies/tests_formal_tempai.py b/project/game/ai/first_version/tests/strategies/tests_formal_tempai.py new file mode 100644 index 00000000..fedc2f53 --- /dev/null +++ b/project/game/ai/first_version/tests/strategies/tests_formal_tempai.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +import unittest + +from mahjong.tests_mixin import TestMixin +from mahjong.tile import Tile + +from game.ai.first_version.strategies.main import BaseStrategy +from game.ai.first_version.strategies.formal_tempai import FormalTempaiStrategy +from game.table import Table + + +class FormalTempaiStrategyTestCase(unittest.TestCase, TestMixin): + + def setUp(self): + self.table = Table() + self.player = self.table.player + self.player.dealer_seat = 3 + + def test_should_activate_strategy(self): + strategy = FormalTempaiStrategy(BaseStrategy.FORMAL_TEMPAI, self.player) + + tiles = self._string_to_136_array(sou='12355689', man='89', pin='339') + self.player.init_hand(tiles) + self.assertEqual(strategy.should_activate_strategy(), False) + + # Let's move to 10th round step + for i in range(0, 10): + self.player.add_discarded_tile(Tile(0, False)) + + self.assertEqual(strategy.should_activate_strategy(), False) + + # Now we move to 11th turn, we have 2 shanten and no doras, + # we should go for formal tempai + self.player.add_discarded_tile(Tile(0, True)) diff --git a/project/game/ai/first_version/tests/strategies/tests_yakuhai.py b/project/game/ai/first_version/tests/strategies/tests_yakuhai.py index a0d784cd..58be3f76 100644 --- a/project/game/ai/first_version/tests/strategies/tests_yakuhai.py +++ b/project/game/ai/first_version/tests/strategies/tests_yakuhai.py @@ -12,7 +12,7 @@ class YakuhaiStrategyTestCase(unittest.TestCase, TestMixin): - + def setUp(self): self.table = Table() self.player = self.table.player @@ -132,7 +132,7 @@ def test_wrong_shanten_improvements_detection(self): def test_open_hand_with_doras_in_the_hand(self): """ - If we have valuable pair in the hand, and 2+ dora let's open on this + If we have valuable pair in the hand, and 2+ dora let's open on this valuable pair """ From 067c0103fbcd8bc7e86c4916748394eba34c2a24 Mon Sep 17 00:00:00 2001 From: bogachev-pa Date: Fri, 24 Aug 2018 16:59:11 +0300 Subject: [PATCH 2/6] add missed assert in tests + coding style fixes --- .../first_version/strategies/formal_tempai.py | 40 ++++++++++--------- .../tests/strategies/tests_formal_tempai.py | 1 + 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/project/game/ai/first_version/strategies/formal_tempai.py b/project/game/ai/first_version/strategies/formal_tempai.py index fa96b0f1..6667cd78 100644 --- a/project/game/ai/first_version/strategies/formal_tempai.py +++ b/project/game/ai/first_version/strategies/formal_tempai.py @@ -39,10 +39,9 @@ def should_activate_strategy(self): # having 0 or 1 dora and 2 shanten, let's go for formal tempai # starting from 11th turn return True - else: - # having 2 or more doras and 2 shanten, let's go for formal - # tempai starting from 12th turn - return (self.player.round_step >= 12) + # having 2 or more doras and 2 shanten, let's go for formal + # tempai starting from 12th turn + return self.player.round_step >= 12 # for 1 shanten we check number of doras and ukeire to determine # correct time to go for formal tempai @@ -50,22 +49,25 @@ def should_activate_strategy(self): if dora_count == 0: if self.player.ai.ukeire <= 16: return True - elif self.player.ai.ukeire <= 28: - return (self.player.round_step >= 12) - else: - return (self.player.round_step >= 13) - elif dora_count == 1: - if self.player.ai.ukeire <= 16: - return (self.player.round_step >= 12) - elif self.player.ai.ukeire <= 28: - return (self.player.round_step >= 13) - else: - return (self.player.round_step >= 14) - else: + + if self.player.ai.ukeire <= 28: + return self.player.round_step >= 12 + + return self.player.round_step >= 13 + + if dora_count == 1: if self.player.ai.ukeire <= 16: - return (self.player.round_step >= 13) - else: - return (self.player.round_step >= 14) + return self.player.round_step >= 12 + + if self.player.ai.ukeire <= 28: + return self.player.round_step >= 13 + + return self.player.round_step >= 14 + + if self.player.ai.ukeire <= 16: + return self.player.round_step >= 13 + + return self.player.round_step >= 14 # we actually never reach here return False diff --git a/project/game/ai/first_version/tests/strategies/tests_formal_tempai.py b/project/game/ai/first_version/tests/strategies/tests_formal_tempai.py index fedc2f53..efdef6cb 100644 --- a/project/game/ai/first_version/tests/strategies/tests_formal_tempai.py +++ b/project/game/ai/first_version/tests/strategies/tests_formal_tempai.py @@ -32,3 +32,4 @@ def test_should_activate_strategy(self): # Now we move to 11th turn, we have 2 shanten and no doras, # we should go for formal tempai self.player.add_discarded_tile(Tile(0, True)) + self.assertEqual(strategy.should_activate_strategy(), True) From 9c03f427d7669cc668be4038159afe9504347a39 Mon Sep 17 00:00:00 2001 From: bogachev-pa Date: Fri, 24 Aug 2018 17:19:43 +0300 Subject: [PATCH 3/6] add missed import in formal_tempai.py --- project/game/ai/first_version/strategies/formal_tempai.py | 1 + 1 file changed, 1 insertion(+) diff --git a/project/game/ai/first_version/strategies/formal_tempai.py b/project/game/ai/first_version/strategies/formal_tempai.py index 6667cd78..5747433e 100644 --- a/project/game/ai/first_version/strategies/formal_tempai.py +++ b/project/game/ai/first_version/strategies/formal_tempai.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from mahjong.meld import Meld from mahjong.tile import TilesConverter +from mahjong.utils import plus_dora, is_aka_dora from game.ai.first_version.strategies.main import BaseStrategy From 13977e419380371a50f1982444603b0e7024aa9e Mon Sep 17 00:00:00 2001 From: bogachev-pa Date: Fri, 24 Aug 2018 17:32:46 +0300 Subject: [PATCH 4/6] fix formal_tempai test to correctly calculate shanten number --- .../tests/strategies/tests_formal_tempai.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/project/game/ai/first_version/tests/strategies/tests_formal_tempai.py b/project/game/ai/first_version/tests/strategies/tests_formal_tempai.py index efdef6cb..be6830c9 100644 --- a/project/game/ai/first_version/tests/strategies/tests_formal_tempai.py +++ b/project/game/ai/first_version/tests/strategies/tests_formal_tempai.py @@ -23,8 +23,13 @@ def test_should_activate_strategy(self): self.player.init_hand(tiles) self.assertEqual(strategy.should_activate_strategy(), False) - # Let's move to 10th round step - for i in range(0, 10): + self.player.draw_tile(self._string_to_136_tile(honors='1')) + # to calculate hand shanten number + self.player.discard_tile() + + # Let's move to 10th round step, one tile was already discarded, 9 more + # to go + for i in range(0, 9): self.player.add_discarded_tile(Tile(0, False)) self.assertEqual(strategy.should_activate_strategy(), False) From 12f8927c2755d630b9ed8b878e2a36b5927da531 Mon Sep 17 00:00:00 2001 From: Nihisil Date: Fri, 24 Aug 2018 23:35:36 +0800 Subject: [PATCH 5/6] Rename tiles_count to ukeire --- project/game/ai/discard.py | 16 ++--- project/game/ai/first_version/defence/main.py | 2 +- project/game/ai/first_version/main.py | 58 +++++++++---------- .../game/ai/first_version/tests/tests_ai.py | 16 ++--- .../ai/first_version/tests/tests_discards.py | 2 +- 5 files changed, 47 insertions(+), 47 deletions(-) diff --git a/project/game/ai/discard.py b/project/game/ai/discard.py index b79cc336..3a1ac4f6 100644 --- a/project/game/ai/discard.py +++ b/project/game/ai/discard.py @@ -14,8 +14,8 @@ class DiscardOption(object): # array of tiles that will improve our hand waiting = None # how much tiles will improve our hand - tiles_count = None - tiles_count_second_level = None + ukeire = None + ukeire_second = None # number of shanten for that tile shanten = None # sometimes we had to force tile to be discarded @@ -27,19 +27,19 @@ class DiscardOption(object): # how danger this tile is danger = None - def __init__(self, player, tile_to_discard, shanten, waiting, tiles_count, danger=100): + def __init__(self, player, tile_to_discard, shanten, waiting, ukeire, danger=100): """ :param player: :param tile_to_discard: tile in 34 format :param waiting: list of tiles in 34 format - :param tiles_count: count of tiles to wait after discard + :param ukeire: count of tiles to wait after discard """ self.player = player self.tile_to_discard = tile_to_discard self.shanten = shanten self.waiting = waiting - self.tiles_count = tiles_count - self.tiles_count_second_level = 0 + self.ukeire = ukeire + self.ukeire_second = 0 self.count_of_dora = 0 self.danger = danger self.had_to_be_saved = False @@ -51,8 +51,8 @@ def __unicode__(self): tile_format_136 = TilesConverter.to_one_line_string([self.tile_to_discard*4]) return 'tile={}, ukeire={}, ukeire2={}, valuation={}'.format( tile_format_136, - self.tiles_count, - self.tiles_count_second_level, + self.ukeire, + self.ukeire_second, self.valuation ) diff --git a/project/game/ai/first_version/defence/main.py b/project/game/ai/first_version/defence/main.py index c1bc1205..326d9fe3 100644 --- a/project/game/ai/first_version/defence/main.py +++ b/project/game/ai/first_version/defence/main.py @@ -205,7 +205,7 @@ def _find_tile_to_discard(self, safe_tiles, discard_tiles): if not was_safe_tiles: return None - final_results = sorted(discard_tiles, key=lambda x: (x.danger, x.shanten, -x.tiles_count, x.valuation)) + final_results = sorted(discard_tiles, key=lambda x: (x.danger, x.shanten, -x.ukeire, x.valuation)) return final_results[0] diff --git a/project/game/ai/first_version/main.py b/project/game/ai/first_version/main.py index b6be12ce..9cd5b534 100644 --- a/project/game/ai/first_version/main.py +++ b/project/game/ai/first_version/main.py @@ -36,7 +36,7 @@ class ImplementationAI(InterfaceAI): previous_shanten = 7 ukeire = 0 - tiles_count_second_level = 0 + ukeire_second = 0 in_defence = False waiting = None @@ -52,7 +52,7 @@ def __init__(self, player): self.finished_hand = HandCalculator() self.previous_shanten = 7 self.ukeire = 0 - self.tiles_count_second_level = 0 + self.ukeire_second = 0 self.current_strategy = None self.waiting = [] self.in_defence = False @@ -71,7 +71,7 @@ def erase_state(self): self.last_discard_option = None self.previous_shanten = 7 self.ukeire = 0 - self.tiles_count_second_level = 0 + self.ukeire_second = 0 def draw_tile(self, tile): """ @@ -116,7 +116,7 @@ def process_discard_options_and_select_tile_to_discard(self, results, shanten, h # we had to update tiles value there # because it is related with shanten number for result in results: - result.tiles_count = self.count_tiles(result.waiting, tiles_34) + result.ukeire = self.count_tiles(result.waiting, tiles_34) result.calculate_value(shanten) # current strategy can affect on our discard options @@ -168,7 +168,7 @@ def calculate_outs(self, tiles, closed_hand, open_sets_34=None): shanten=shanten, tile_to_discard=hand_tile, waiting=waiting, - tiles_count=self.count_tiles(waiting, tiles_34))) + ukeire=self.count_tiles(waiting, tiles_34))) if is_agari: shanten = Shanten.AGARI_STATE @@ -238,12 +238,12 @@ def choose_tile_to_discard(self, results: [DiscardOption]) -> DiscardOption: had_to_be_discarded_tiles = [x for x in results if x.had_to_be_discarded] if had_to_be_discarded_tiles: - return sorted(had_to_be_discarded_tiles, key=lambda x: (x.shanten, -x.tiles_count, x.valuation))[0] + return sorted(had_to_be_discarded_tiles, key=lambda x: (x.shanten, -x.ukeire, x.valuation))[0] # remove needed tiles from discard options results = [x for x in results if not x.had_to_be_saved] - results = sorted(results, key=lambda x: (x.shanten, -x.tiles_count)) + results = sorted(results, key=lambda x: (x.shanten, -x.ukeire)) first_option = results[0] results_with_same_shanten = [x for x in results if x.shanten == first_option.shanten] @@ -255,22 +255,22 @@ def choose_tile_to_discard(self, results: [DiscardOption]) -> DiscardOption: continue # we don't need to select tiles almost dead waits - if discard_option.tiles_count <= 2: + if discard_option.ukeire <= 2: continue - tiles_count_borders = round((first_option.tiles_count / 100) * border_percentage) + ukeire_borders = round((first_option.ukeire / 100) * border_percentage) - if first_option.shanten == 0 and tiles_count_borders < 2: - tiles_count_borders = 2 + if first_option.shanten == 0 and ukeire_borders < 2: + ukeire_borders = 2 - if first_option.shanten == 1 and tiles_count_borders < 4: - tiles_count_borders = 4 + if first_option.shanten == 1 and ukeire_borders < 4: + ukeire_borders = 4 - if first_option.shanten >= 2 and tiles_count_borders < 8: - tiles_count_borders = 8 + if first_option.shanten >= 2 and ukeire_borders < 8: + ukeire_borders = 8 # let's choose tiles that are close to the max ukeire tile - if discard_option.tiles_count >= first_option.tiles_count - tiles_count_borders: + if discard_option.ukeire >= first_option.ukeire - ukeire_borders: possible_options.append(discard_option) if first_option.shanten <= 1: @@ -280,14 +280,14 @@ def choose_tile_to_discard(self, results: [DiscardOption]) -> DiscardOption: # as second step # let's choose tiles that are close to the max ukeire2 tile for x in possible_options: - self.calculate_second_level_tiles_count(x) + self.calculate_second_level_ukeire(x) - possible_options = sorted(possible_options, key=lambda x: -x.tiles_count_second_level) + possible_options = sorted(possible_options, key=lambda x: -x.ukeire_second) filter_percentage = 20 filtered_options = self._filter_list_by_percentage( possible_options, - 'tiles_count_second_level', + 'ukeire_second', filter_percentage ) @@ -298,12 +298,12 @@ def choose_tile_to_discard(self, results: [DiscardOption]) -> DiscardOption: min_dora_list = [x for x in filtered_options if x.count_of_dora == min_dora] # let's discard tile with greater ukeire2 - return sorted(min_dora_list, key=lambda x: -x.tiles_count_second_level)[0] + return sorted(min_dora_list, key=lambda x: -x.ukeire_second)[0] second_filter_percentage = 10 filtered_options = self._filter_list_by_percentage( filtered_options, - 'tiles_count_second_level', + 'ukeire_second', second_filter_percentage ) @@ -316,14 +316,14 @@ def choose_tile_to_discard(self, results: [DiscardOption]) -> DiscardOption: # there are no isolated tiles # let's discard tile with greater ukeire2 - return sorted(filtered_options, key=lambda x: -x.tiles_count_second_level)[0] + return sorted(filtered_options, key=lambda x: -x.ukeire_second)[0] def process_discard_option(self, discard_option, closed_hand, force_discard=False): self.waiting = discard_option.waiting self.player.ai.previous_shanten = discard_option.shanten self.player.in_tempai = self.player.ai.previous_shanten == 0 - self.player.ai.ukeire = discard_option.tiles_count - self.player.ai.tiles_count_second_level = discard_option.tiles_count_second_level + self.player.ai.ukeire = discard_option.ukeire + self.player.ai.ukeire_second = discard_option.ukeire_second # when we called meld we don't need "smart" discard if force_discard: @@ -479,7 +479,7 @@ def enemy_called_riichi(self, enemy_seat): if self.defence.should_go_to_defence_mode(): self.in_defence = True - def calculate_second_level_tiles_count(self, discard_option): + def calculate_second_level_ukeire(self, discard_option): tiles = copy.copy(self.player.tiles) tiles.remove(discard_option.find_tile_in_hand(self.player.closed_hand)) @@ -494,11 +494,11 @@ def calculate_second_level_tiles_count(self, discard_option): self.player.open_hand_34_tiles ) results = [x for x in results if x.shanten == discard_option.shanten - 1] - sum_tiles += sum([x.tiles_count for x in results]) + sum_tiles += sum([x.ukeire for x in results]) tiles.remove(wait) - discard_option.tiles_count_second_level = sum_tiles + discard_option.ukeire_second = sum_tiles @property def enemy_players(self): @@ -510,8 +510,8 @@ def enemy_players(self): def _filter_list_by_percentage(self, items, attribute, percentage): filtered_options = [] first_option = items[0] - tiles_count_borders = round((getattr(first_option, attribute) / 100) * percentage) + ukeire_borders = round((getattr(first_option, attribute) / 100) * percentage) for x in items: - if getattr(x, attribute) >= getattr(first_option, attribute) - tiles_count_borders: + if getattr(x, attribute) >= getattr(first_option, attribute) - ukeire_borders: filtered_options.append(x) return filtered_options diff --git a/project/game/ai/first_version/tests/tests_ai.py b/project/game/ai/first_version/tests/tests_ai.py index 01f958d7..87713b16 100644 --- a/project/game/ai/first_version/tests/tests_ai.py +++ b/project/game/ai/first_version/tests/tests_ai.py @@ -155,20 +155,20 @@ def test_remaining_tiles_and_enemy_discard(self): results, shanten = player.ai.calculate_outs(tiles, tiles) result = [x for x in results if x.tile_to_discard == self._string_to_34_tile(sou='1')][0] - self.assertEqual(result.tiles_count, 8) + self.assertEqual(result.ukeire, 8) player.table.add_discarded_tile(1, self._string_to_136_tile(sou='5'), False) results, shanten = player.ai.calculate_outs(tiles, tiles) result = [x for x in results if x.tile_to_discard == self._string_to_34_tile(sou='1')][0] - self.assertEqual(result.tiles_count, 7) + self.assertEqual(result.ukeire, 7) player.table.add_discarded_tile(2, self._string_to_136_tile(sou='5'), False) player.table.add_discarded_tile(3, self._string_to_136_tile(sou='8'), False) results, shanten = player.ai.calculate_outs(tiles, tiles) result = [x for x in results if x.tile_to_discard == self._string_to_34_tile(sou='1')][0] - self.assertEqual(result.tiles_count, 5) + self.assertEqual(result.ukeire, 5) def test_remaining_tiles_and_opened_meld(self): table = Table() @@ -179,7 +179,7 @@ def test_remaining_tiles_and_opened_meld(self): results, shanten = player.ai.calculate_outs(tiles, tiles) result = [x for x in results if x.tile_to_discard == self._string_to_34_tile(sou='1')][0] - self.assertEqual(result.tiles_count, 8) + self.assertEqual(result.ukeire, 8) # was discard and set was opened tile = self._string_to_136_tile(sou='8') @@ -190,7 +190,7 @@ def test_remaining_tiles_and_opened_meld(self): results, shanten = player.ai.calculate_outs(tiles, tiles) result = [x for x in results if x.tile_to_discard == self._string_to_34_tile(sou='1')][0] - self.assertEqual(result.tiles_count, 5) + self.assertEqual(result.ukeire, 5) # was discard and set was opened tile = self._string_to_136_tile(sou='3') @@ -201,7 +201,7 @@ def test_remaining_tiles_and_opened_meld(self): results, shanten = player.ai.calculate_outs(tiles, tiles) result = [x for x in results if x.tile_to_discard == self._string_to_34_tile(sou='1')][0] - self.assertEqual(result.tiles_count, 4) + self.assertEqual(result.ukeire, 4) def test_remaining_tiles_and_dora_indicators(self): table = Table() @@ -212,13 +212,13 @@ def test_remaining_tiles_and_dora_indicators(self): results, shanten = player.ai.calculate_outs(tiles, tiles) result = [x for x in results if x.tile_to_discard == self._string_to_34_tile(sou='1')][0] - self.assertEqual(result.tiles_count, 8) + self.assertEqual(result.ukeire, 8) table.add_dora_indicator(self._string_to_136_tile(sou='8')) results, shanten = player.ai.calculate_outs(tiles, tiles) result = [x for x in results if x.tile_to_discard == self._string_to_34_tile(sou='1')][0] - self.assertEqual(result.tiles_count, 7) + self.assertEqual(result.ukeire, 7) def test_using_tiles_of_different_suit_for_chi(self): """ diff --git a/project/game/ai/first_version/tests/tests_discards.py b/project/game/ai/first_version/tests/tests_discards.py index 2a5f3941..2d3006bd 100644 --- a/project/game/ai/first_version/tests/tests_discards.py +++ b/project/game/ai/first_version/tests/tests_discards.py @@ -220,7 +220,7 @@ def test_dont_keep_honor_with_small_number_of_shanten(self): discarded_tile = player.discard_tile() self.assertEqual(self._to_string([discarded_tile]), '7z') - def test_prefer_valuable_tiles_with_almost_same_tiles_count(self): + def test_prefer_valuable_tiles_with_almost_same_ukeire(self): table = Table() player = table.player table.add_dora_indicator(self._string_to_136_tile(sou='4')) From 11fb79469524b266eae13704913602430d487517 Mon Sep 17 00:00:00 2001 From: Nihisil Date: Sat, 25 Aug 2018 10:06:34 +0800 Subject: [PATCH 6/6] Fix linter warnings --- .flake8 | 2 +- project/game/ai/first_version/defence/suji.py | 4 +++- project/game/ai/first_version/main.py | 2 +- project/game/ai/first_version/strategies/formal_tempai.py | 2 -- .../first_version/tests/strategies/tests_formal_tempai.py | 2 +- .../game/ai/first_version/tests/strategies/tests_honitsu.py | 3 --- .../game/ai/first_version/tests/strategies/tests_yakuhai.py | 1 - project/game/ai/first_version/tests/tests_discards.py | 6 ++++-- project/game/tests/tests_player.py | 1 - 9 files changed, 10 insertions(+), 13 deletions(-) diff --git a/.flake8 b/.flake8 index 2d5c5816..42f0565a 100644 --- a/.flake8 +++ b/.flake8 @@ -1,3 +1,3 @@ [flake8] max-line-length = 120 -exclude = settings.py \ No newline at end of file +exclude = *settings.py,tests_validate_hand.py \ No newline at end of file diff --git a/project/game/ai/first_version/defence/suji.py b/project/game/ai/first_version/defence/suji.py index e9cf7432..eeea3acc 100644 --- a/project/game/ai/first_version/defence/suji.py +++ b/project/game/ai/first_version/defence/suji.py @@ -108,7 +108,9 @@ def _suji_tiles(self, suji): # mark dora tiles as dangerous tiles to discard for tile in result: - if plus_dora(tile.value * 4, self.table.dora_indicators) or is_aka_dora(tile.value * 4, self.table.has_open_tanyao): + is_dora = plus_dora(tile.value * 4, self.table.dora_indicators) \ + or is_aka_dora(tile.value * 4, self.table.has_open_tanyao) + if is_dora: tile.danger += 100 return result diff --git a/project/game/ai/first_version/main.py b/project/game/ai/first_version/main.py index 9cd5b534..3ba00147 100644 --- a/project/game/ai/first_version/main.py +++ b/project/game/ai/first_version/main.py @@ -329,7 +329,7 @@ def process_discard_option(self, discard_option, closed_hand, force_discard=Fals if force_discard: return discard_option.find_tile_in_hand(closed_hand) - last_draw_34 = self.player.last_draw and self.player.last_draw // 4 or None + last_draw_34 = self.player.last_draw and self.player.last_draw // 4 or None if self.player.last_draw not in AKA_DORA_LIST and last_draw_34 == discard_option.tile_to_discard: return self.player.last_draw else: diff --git a/project/game/ai/first_version/strategies/formal_tempai.py b/project/game/ai/first_version/strategies/formal_tempai.py index 5747433e..5e941486 100644 --- a/project/game/ai/first_version/strategies/formal_tempai.py +++ b/project/game/ai/first_version/strategies/formal_tempai.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from mahjong.meld import Meld -from mahjong.tile import TilesConverter from mahjong.utils import plus_dora, is_aka_dora from game.ai.first_version.strategies.main import BaseStrategy diff --git a/project/game/ai/first_version/tests/strategies/tests_formal_tempai.py b/project/game/ai/first_version/tests/strategies/tests_formal_tempai.py index be6830c9..77c91a74 100644 --- a/project/game/ai/first_version/tests/strategies/tests_formal_tempai.py +++ b/project/game/ai/first_version/tests/strategies/tests_formal_tempai.py @@ -4,8 +4,8 @@ from mahjong.tests_mixin import TestMixin from mahjong.tile import Tile -from game.ai.first_version.strategies.main import BaseStrategy from game.ai.first_version.strategies.formal_tempai import FormalTempaiStrategy +from game.ai.first_version.strategies.main import BaseStrategy from game.table import Table diff --git a/project/game/ai/first_version/tests/strategies/tests_honitsu.py b/project/game/ai/first_version/tests/strategies/tests_honitsu.py index d672dd02..2165ba15 100644 --- a/project/game/ai/first_version/tests/strategies/tests_honitsu.py +++ b/project/game/ai/first_version/tests/strategies/tests_honitsu.py @@ -140,6 +140,3 @@ def test_dont_go_for_honitsu_with_ryanmen_in_other_suit(self): player.init_hand(tiles) self.assertEqual(strategy.should_activate_strategy(), False) - - - diff --git a/project/game/ai/first_version/tests/strategies/tests_yakuhai.py b/project/game/ai/first_version/tests/strategies/tests_yakuhai.py index 58be3f76..cbc01eff 100644 --- a/project/game/ai/first_version/tests/strategies/tests_yakuhai.py +++ b/project/game/ai/first_version/tests/strategies/tests_yakuhai.py @@ -254,7 +254,6 @@ def test_open_hand_and_once_discarded_tile(self): """ If we have valuable pair in the hand, this tile was discarded once and we have 1+ shanten let's open on this valuable pair - 5. Если в руке есть ценная пара, один тайл этой ценной пары уже вышел, в руке больше ишантена, то открываемся на этой ценной паре. """ tiles = self._string_to_136_array(man='29', sou='1189', pin='12789', honors='11') diff --git a/project/game/ai/first_version/tests/tests_discards.py b/project/game/ai/first_version/tests/tests_discards.py index 2d3006bd..30606e57 100644 --- a/project/game/ai/first_version/tests/tests_discards.py +++ b/project/game/ai/first_version/tests/tests_discards.py @@ -46,7 +46,8 @@ def test_discard_tile_force_tsumogiri(self): player = table.player tiles = self._string_to_136_array(sou='11134567', pin='456', man='45') - tile = 57 # 6p + # 6p + tile = 57 player.init_hand(tiles) player.draw_tile(tile) @@ -54,7 +55,8 @@ def test_discard_tile_force_tsumogiri(self): discarded_tile = player.discard_tile() self.assertEqual(discarded_tile, tile) - tiles = self._string_to_136_array(sou='11134567', pin='46', man='45') + [53] # simple five pin + # add not red five pin + tiles = self._string_to_136_array(sou='11134567', pin='46', man='45') + [53] tile = FIVE_RED_PIN player.init_hand(tiles) diff --git a/project/game/tests/tests_player.py b/project/game/tests/tests_player.py index 019414a3..56cad7b3 100644 --- a/project/game/tests/tests_player.py +++ b/project/game/tests/tests_player.py @@ -7,7 +7,6 @@ from game.player import Player from game.table import Table -from utils.settings_handler import settings class PlayerTestCase(unittest.TestCase, TestMixin):