def test_hand(): mj_set = MjSet() mj_set.shuffle() # ai = PlayerAiPro("bot02") ai = PlayerAiAdv("bot02") for _ in range(13): ai.draw(mj_set) ai.sort_concealed() print("concealed: " + Rule.convert_tiles_to_str(ai.concealed)) mj_set.shuffle() for _ in range(100): tile = ai.draw(mj_set) print(f"draw: {tile}") if Rule.is_mahjong(ai.concealed): print("Mahjong!!!") break tile = ai.decide_discard() print(f"discard: {tile}") ai.discard(tile) ai.sort_concealed() print("concealed: " + Rule.convert_tiles_to_str(ai.concealed)) print("discarded: " + Rule.convert_tiles_to_str(ai.discarded)) print(f"strategies: {ai.strategies}") print(f"strategies_time: {ai.strategies_time}") print("draw count: " + str(ai.draw_count))
def test_decide_discard_by_value(): mj_set = MjSet() ai = PlayerAiAdv("bot02") for _ in range(3): ai.draw(mj_set) mj_set.shuffle() for _ in range(11): ai.draw(mj_set) ai.decide_discard_by_value(wall=mj_set)
def test_decide_discard_by_remove_melds(): mj_set = MjSet() ai = PlayerAiPro("bot01") for _ in range(3): ai.draw(mj_set) mj_set.shuffle() for _ in range(11): ai.draw(mj_set) ai.decide_discard_by_remove_melds()
def test_convert(): mj_set = MjSet() mj_set.shuffle() concealed = [] for _ in range(13): concealed.append(mj_set.draw()) print(Rule.convert_tiles_to_str(concealed)) arr = Rule.convert_tiles_to_arr(concealed) print(arr) tiles = Rule.convert_arr_to_tiles(arr) print(Rule.convert_tiles_to_str(tiles))
def test_decide_discard_by_left_meld_and_eye(): mj_set = MjSet() ai = PlayerAiAdv("bot02") for _ in range(3): ai.draw(mj_set) mj_set.shuffle() for _ in range(11): ai.draw(mj_set) ai.sort_concealed() print("ai.concealed_str =", ai.concealed_str) result = ai.decide_discard_by_left_meld_and_eye() for x in result: print(x, Rule.convert_tiles_to_str(Rule.convert_arr_to_tiles(result[x])))
def test_player_ai_base(self): mj_set = MjSet() ai = PlayerAi("貂蝉") for _ in range(13): ai.draw(mj_set) self.assertEqual(len(ai.concealed), 13) mj_set.shuffle() for _ in range(10): ai.draw(mj_set) tile = ai.decide_discard() self.assertIsNotNone(tile) ai.discard(tile) self.assertEqual(len(ai.concealed), 13) self.assertEqual(len(ai.discarded), 10)
def test_ai_mahjong(self): mj_set = MjSet() ai = PlayerAi("西施") mj_set.shuffle() for _ in range(13): ai.draw(mj_set) ai.sort_concealed() mj_set.shuffle() mahjong = False for _ in range(100): tile = ai.draw(mj_set) if Rule.is_mahjong(ai.concealed): mahjong = True break tile = ai.decide_discard() ai.discard(tile) ai.sort_concealed() # self.assertTrue(mahjong) self.assertGreater(len(ai.concealed), 0)
class Hand(object): __slots__ = ("_state_machine", "_machine", "_states", "_transitions", "_mj_set", "_players", "_prevailing_wind", "_dealer", '_positions', '_winner', 'firer', 'out_of_tiles', 'robbing_a_kong', 'mahjong_on_kong') def __init__(self, players: list = None, prevailing_wind: str = '东', flower=False): if not players: raise ValueError("mahjong needs player!") self._mj_set = MjSet(flower) self.out_of_tiles = False self.robbing_a_kong = False self.mahjong_on_kong = False self._players = players self._positions = dict() for index, player in enumerate(self._players): wind = Suit.get_wind_by_index(index) player.position = wind self._positions[wind] = player self._dealer: Player = self._players[0] self._prevailing_wind = prevailing_wind self._winner = None self.firer = None # state machine self._state_machine = HandStateMachine() self._states = ["begin", "prepared", "playing", "scoring", "end"] self._transitions = [ { 'trigger': 'prepare', 'source': 'begin', 'dest': 'prepared' }, # 准备 { 'trigger': 'deal', 'source': 'prepared', 'dest': 'playing' }, # 拿四张 { 'trigger': 'mahjong', 'source': 'playing', 'dest': 'scoring' }, # 胡牌 { 'trigger': 'withdraw', 'source': 'playing', 'dest': 'scoring' }, # 流局 { 'trigger': 'score', 'source': 'scoring', 'dest': 'end' }, # 算分 ] Machine(model=self._state_machine, states=self._states, transitions=self._transitions, initial='begin') # print(self.state) def __str__(self): return '\r\n'.join([f'{x.position}:{x}' for x in self._players]) @property def state(self): return self._state_machine.state @property def players(self): return self._players @property def mj_set(self): return self._mj_set @property def dealer(self): return self._dealer @property def winner(self): return self._winner @property def positions(self): return self._positions def _shuffle(self): self._mj_set.shuffle() def _reset_players(self): for player in self._players: player.reset() def _get_next_player(self, current): bingo = False for player in cycle(self.players): if bingo: return player if current == player: bingo = True raise LookupError(f"can not find next player for {current}") def prepare(self): self._reset_players() self._state_machine.prepare() self._shuffle() def deal(self): for _ in range(max(MjMath.concealed_count) // 4): for player in self._players: player.draw_stack(self._mj_set) for player in self.players: player.draw(self._mj_set) player.sort_concealed() self._state_machine.deal() def play(self): current = self._dealer # the current player player = current before = None # the previous player current_discard = None # discard tile by previous player for player in self._players: print(f'{player.position}: {player}') print("=" * 40) have_winner = False while not have_winner: if current_discard: wind = current.position player = current for index in range(3): # test for hu by other test = player.concealed[:] test.append(current_discard) if Rule.is_mahjong(test): player.concealed.append(current_discard) print(f"winner is {player}, by {before}") self._winner = player self._state_machine.mahjong() have_winner = True break wind = Suit.get_next_wind(wind) player = self.positions[wind] if have_winner: break # interrupted by exposed kong / pong / chow interrupted = False if current_discard: # Melding another for current, +1, +2 players player = None # try kong ( must have tiles ): if self.mj_set.tiles: wind = current.position player = current for index in range(3): if player.try_exposed_kong(tile=current_discard, owner=before, mj_set=self._mj_set): interrupted = True break wind = Suit.get_next_wind(wind) player = self.positions[wind] # try pong: if not interrupted: wind = current.position player = current for index in range(3): if player.try_exposed_pong(tile=current_discard, owner=before): interrupted = True break wind = Suit.get_next_wind(wind) player = self.positions[wind] # try chow: if not interrupted: wind = current.position player = current if player.try_exposed_chow(current_discard, before): interrupted = True if not interrupted: before.put_on_desk(current_discard) # end if current_discard: if interrupted: current = player else: # test for out of tiles if not self.mj_set.tiles: print("out of tiles!") self._winner = None self._state_machine.withdraw() break # draw current.draw(self._mj_set) # test for hu by self if Rule.is_mahjong(current.concealed): print(f"winner is {current}, by self!") self._winner = current self._state_machine.mahjong() break # self kong current.try_conceal_kong(self.mj_set) if isinstance(current, PlayerAiAdv): wall = self._mj_set.tiles tile = current.decide_discard(players_count=len(self.players), wall=wall) else: tile = current.decide_discard() print(current, 'discard:', tile) current.discard(tile) current.sort_concealed() current_discard = tile # (f'{current} discard {tile}') # (Rule.convert_tiles_to_str(current.concealed)) # next player next = self._get_next_player(current) before = current current = next # end while print("tiles left :", len(self.mj_set.tiles)) for player in self.players: if player == self.winner: print(f"winner {player}: ", Rule.convert_tiles_to_str(player.concealed)) else: print(f"{player}: ", Rule.convert_tiles_to_str(player.concealed)) left = Rule.convert_tiles_to_arr(self.mj_set.tiles) left.sort() print(self.players[0].strategies) print(self.players[0].strategies_time) # end def play() def score(self): # score the play result self._state_machine.score() pass