Skip to content
This repository was archived by the owner on Jul 8, 2023. It is now read-only.

Commit 33c9046

Browse files
committed
Improve AI structure a little bit. Remove all not necessary dependencies
1 parent af6d90a commit 33c9046

File tree

13 files changed

+64
-79
lines changed

13 files changed

+64
-79
lines changed

project/game/ai/first_version/defence/enemy_analyzer.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,28 @@
55
class EnemyAnalyzer(object):
66
player = None
77
chosen_suit = None
8+
initialized = False
89

910
def __init__(self, player):
1011
"""
1112
:param player: instance of EnemyPlayer
1213
"""
1314
self.player = player
1415
self.table = player.table
16+
1517
self.chosen_suit = None
1618

19+
# we need it to determine user's chosen suit
20+
self.initialized = self.is_threatening
21+
22+
@property
23+
def is_dealer(self):
24+
return self.player.is_dealer
25+
26+
@property
27+
def all_safe_tiles(self):
28+
return self.player.all_safe_tiles
29+
1730
@property
1831
def in_tempai(self):
1932
"""

project/game/ai/first_version/defence/main.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from mahjong.utils import plus_dora, is_honor
33

44
from game.ai.first_version.defence.defence import DefenceTile
5+
from game.ai.first_version.defence.enemy_analyzer import EnemyAnalyzer
56
from game.ai.first_version.defence.impossible_wait import ImpossibleWait
67
from game.ai.first_version.defence.kabe import Kabe
78
from game.ai.first_version.defence.suji import Suji
@@ -155,7 +156,7 @@ def try_to_find_safe_tile_to_discard(self, discard_results):
155156
# let's find safe tiles for most dangerous player first
156157
# and than for all other players if we failed find tile for dangerous player
157158
for player in threatening_players:
158-
player_safe_tiles = [DefenceTile(x, DefenceTile.SAFE) for x in player.all_safe_tiles]
159+
player_safe_tiles = [DefenceTile(x, DefenceTile.SAFE) for x in player.player.all_safe_tiles]
159160
player_suji_tiles = self.suji.find_tiles_to_discard([player])
160161

161162
# it can be that safe tile will be mark as "almost safe",
@@ -186,6 +187,11 @@ def try_to_find_safe_tile_to_discard(self, discard_results):
186187
# we wasn't able to find safe tile to discard
187188
return None
188189

190+
@property
191+
def analyzed_enemies(self):
192+
players = self.player.ai.enemy_players
193+
return [EnemyAnalyzer(x) for x in players]
194+
189195
def _find_tile_to_discard(self, safe_tiles, discard_tiles):
190196
"""
191197
Try to find most effective safe tile to discard
@@ -218,12 +224,12 @@ def _get_threatening_players(self):
218224
Sorted by threat level. Most threatening on the top
219225
"""
220226
result = []
221-
for player in self.table.enemy_players:
227+
for player in self.analyzed_enemies:
222228
if player.is_threatening:
223229
result.append(player)
224230

225231
# dealer is most threatening player
226-
result = sorted(result, key=lambda x: x.is_dealer, reverse=True)
232+
result = sorted(result, key=lambda x: x.player.is_dealer, reverse=True)
227233

228234
return result
229235

project/game/ai/first_version/main.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
from mahjong.tile import TilesConverter
1111
from mahjong.utils import is_pair, is_pon
1212

13-
from game.ai.base.base import BaseAI
14-
from game.ai.base.discard import DiscardOption
13+
from game.ai.base.main import BaseAI
14+
from game.ai.discard import DiscardOption
15+
from game.ai.first_version.defence.enemy_analyzer import EnemyAnalyzer
1516
from game.ai.first_version.defence.main import DefenceHandler
1617
from game.ai.first_version.strategies.honitsu import HonitsuStrategy
1718
from game.ai.first_version.strategies.main import BaseStrategy
@@ -364,6 +365,13 @@ def can_call_kan(self, tile, open_kan):
364365

365366
return None
366367

368+
@property
369+
def enemy_players(self):
370+
"""
371+
Return list of players except our bot
372+
"""
373+
return self.player.table.players[1:]
374+
367375
@property
368376
def valued_honors(self):
369377
return [CHUN, HAKU, HATSU, self.player.table.round_wind, self.player.player_wind]

project/game/ai/first_version/tests/tests_discards.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from mahjong.constants import EAST, SOUTH, WEST, NORTH, HAKU, HATSU, CHUN, FIVE_RED_SOU
55
from mahjong.tests_mixin import TestMixin
66

7-
from game.ai.base.discard import DiscardOption
7+
from game.ai.discard import DiscardOption
88
from game.table import Table
99

1010

project/game/ai/first_version/tests/tests_enemy_analyzer.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from mahjong.tests_mixin import TestMixin
66
from mahjong.utils import is_pin
77

8+
from game.ai.first_version.defence.enemy_analyzer import EnemyAnalyzer
89
from game.table import Table
910

1011

@@ -13,33 +14,33 @@ class EnemyAnalyzerTestCase(unittest.TestCase, TestMixin):
1314
def test_detect_enemy_tempai_and_riichi(self):
1415
table = Table()
1516

16-
self.assertEqual(table.get_player(1).in_tempai, False)
17-
self.assertEqual(table.get_player(1).is_threatening, False)
17+
self.assertEqual(EnemyAnalyzer(table.get_player(1)).in_tempai, False)
18+
self.assertEqual(EnemyAnalyzer(table.get_player(1)).is_threatening, False)
1819

1920
table.add_called_riichi(1)
2021

21-
self.assertEqual(table.get_player(1).in_tempai, True)
22-
self.assertEqual(table.get_player(1).is_threatening, True)
22+
self.assertEqual(EnemyAnalyzer(table.get_player(1)).in_tempai, True)
23+
self.assertEqual(EnemyAnalyzer(table.get_player(1)).is_threatening, True)
2324

2425
def test_detect_enemy_tempai_and_opened_sets(self):
2526
table = Table()
2627

27-
self.assertEqual(table.get_player(1).in_tempai, False)
28-
self.assertEqual(table.get_player(1).is_threatening, False)
28+
self.assertEqual(EnemyAnalyzer(table.get_player(1)).in_tempai, False)
29+
self.assertEqual(EnemyAnalyzer(table.get_player(1)).is_threatening, False)
2930

3031
table.add_called_meld(1, self._make_meld(Meld.CHI, sou='567'))
3132
table.add_called_meld(1, self._make_meld(Meld.CHI, pin='123'))
3233
table.add_called_meld(1, self._make_meld(Meld.CHI, man='345'))
3334
table.add_called_meld(1, self._make_meld(Meld.PON, man='777'))
3435

35-
self.assertEqual(table.get_player(1).in_tempai, True)
36-
self.assertEqual(table.get_player(1).is_threatening, False)
36+
self.assertEqual(EnemyAnalyzer(table.get_player(1)).in_tempai, True)
37+
self.assertEqual(EnemyAnalyzer(table.get_player(1)).is_threatening, False)
3738

3839
table.dora_indicators = [self._string_to_136_tile(man='6')]
3940

4041
# enemy opened the pon of dor, so better to fold against him
41-
self.assertEqual(table.get_player(1).in_tempai, True)
42-
self.assertEqual(table.get_player(1).is_threatening, True)
42+
self.assertEqual(EnemyAnalyzer(table.get_player(1)).in_tempai, True)
43+
self.assertEqual(EnemyAnalyzer(table.get_player(1)).is_threatening, True)
4344

4445
def test_try_to_detect_honitsu_hand(self):
4546
table = Table()
@@ -56,5 +57,5 @@ def test_try_to_detect_honitsu_hand(self):
5657
table.add_discarded_tile(1, self._string_to_136_tile(man='1'), False)
5758
table.add_discarded_tile(1, self._string_to_136_tile(pin='1'), False)
5859

59-
self.assertEqual(table.get_player(1).is_threatening, True)
60-
self.assertEqual(table.get_player(1).chosen_suit, is_pin)
60+
self.assertEqual(EnemyAnalyzer(table.get_player(1)).is_threatening, True)
61+
self.assertEqual(EnemyAnalyzer(table.get_player(1)).chosen_suit, is_pin)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# -*- coding: utf-8 -*-
22
import random
33

4-
from game.ai.base.base import BaseAI
4+
from game.ai.base.main import BaseAI
55

66

77
class MainAI(BaseAI):

project/game/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
class Client(object):
66
table = None
77

8-
def __init__(self, previous_ai=False):
9-
self.table = Table(previous_ai)
8+
def __init__(self):
9+
self.table = Table()
1010

1111
def connect(self):
1212
raise NotImplemented()

project/game/player.py

Lines changed: 7 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from mahjong.meld import Meld
77
from mahjong.tile import TilesConverter, Tile
88

9-
from game.ai.first_version.defence.enemy_analyzer import EnemyAnalyzer
109
from game.ai.first_version.main import MainAI
1110

1211
logger = logging.getLogger('tenhou')
@@ -28,13 +27,10 @@ class PlayerInterface(object):
2827
name = ''
2928
rank = ''
3029

31-
previous_ai = False
32-
33-
def __init__(self, table, seat, dealer_seat, previous_ai):
30+
def __init__(self, table, seat, dealer_seat):
3431
self.table = table
3532
self.seat = seat
3633
self.dealer_seat = dealer_seat
37-
self.previous_ai = previous_ai
3834

3935
self.erase_state()
4036

@@ -74,7 +70,7 @@ def add_discarded_tile(self, tile: Tile):
7470
# all tiles that were discarded after player riichi will be safe against him
7571
# because of furiten
7672
tile = tile.value // 4
77-
for player in self.table.enemy_players:
73+
for player in self.table.players[1:]:
7874
if player.in_riichi and tile not in player.safe_tiles:
7975
player.safe_tiles.append(tile)
8076

@@ -118,9 +114,10 @@ class Player(PlayerInterface):
118114
in_tempai = False
119115
in_defence_mode = False
120116

121-
def __init__(self, table, seat, dealer_seat, previous_ai):
122-
super().__init__(table, seat, dealer_seat, previous_ai)
123-
self._load_ai()
117+
def __init__(self, table, seat, dealer_seat):
118+
super().__init__(table, seat, dealer_seat)
119+
120+
self.ai = MainAI(self)
124121

125122
def erase_state(self):
126123
super().erase_state()
@@ -247,24 +244,19 @@ def open_hand_34_tiles(self):
247244
results.append([meld[0] // 4, meld[1] // 4, meld[2] // 4])
248245
return results
249246

250-
def _load_ai(self):
251-
self.ai = MainAI(self)
252-
253247

254248
class EnemyPlayer(PlayerInterface):
255249
# array of tiles in 34 tile format
256250
safe_tiles = None
257251
# tiles that were discarded in the current "step"
258252
# so, for example kamicha discard will be a safe tile for all players
259253
temporary_safe_tiles = None
260-
enemy_analyzer = None
261254

262255
def erase_state(self):
263256
super().erase_state()
264257

265258
self.safe_tiles = []
266259
self.temporary_safe_tiles = []
267-
self.enemy_analyzer = EnemyAnalyzer(self)
268260

269261
def add_discarded_tile(self, tile: Tile):
270262
super().add_discarded_tile(tile)
@@ -277,6 +269,7 @@ def add_discarded_tile(self, tile: Tile):
277269
self.temporary_safe_tiles = []
278270
affected_players = [1, 2, 3]
279271
affected_players.remove(self.seat)
272+
280273
# temporary furiten, for one "step"
281274
for x in affected_players:
282275
if tile not in self.table.get_player(x).temporary_safe_tiles:
@@ -285,27 +278,3 @@ def add_discarded_tile(self, tile: Tile):
285278
@property
286279
def all_safe_tiles(self):
287280
return list(set(self.temporary_safe_tiles + self.safe_tiles))
288-
289-
@property
290-
def in_tempai(self):
291-
"""
292-
Try to detect is user in tempai or not
293-
:return: boolean
294-
"""
295-
return self.enemy_analyzer.in_tempai
296-
297-
@property
298-
def is_threatening(self):
299-
"""
300-
Should we fold against this player or not
301-
:return: boolean
302-
"""
303-
return self.enemy_analyzer.is_threatening
304-
305-
@property
306-
def chosen_suit(self):
307-
"""
308-
If user collecting honitsu we can detect his specific suit
309-
:return: function
310-
"""
311-
return self.enemy_analyzer.chosen_suit

0 commit comments

Comments
 (0)