Giter Site home page Giter Site logo

mahjongrepository / mahjong Goto Github PK

View Code? Open in Web Editor NEW
377.0 15.0 38.0 410 KB

Implementation of riichi mahjong related stuff (hand cost, shanten, agari end, etc.)

License: MIT License

Python 99.86% Makefile 0.14%
mahjong japanese game-development riichi-mahjong riichi

mahjong's Introduction

Mahjong lib

This library can calculate hand cost (han, fu with details, yaku, and scores) for riichi mahjong (Japanese version).

Also calculating of shanten is supported.

The code was validated on tenhou.net phoenix replays in total on 11,120,125 hands.

So, we can say that our hand calculator works the same way that tenhou.net hand calculation.

How to install

pip install mahjong

Supported rules and usage examples

You can find usage examples and information about all supported rules variations in the wiki

mahjong's People

Contributors

0num4 avatar dilant avatar enerccio avatar kianmeng avatar nihisil avatar otamajakusi avatar paulzzh avatar profthecopyright avatar syado avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mahjong's Issues

Aka Dora han not calculated

I'm trying the examples of the project while aka dora is not calculated in hand calculator. Was it somewhere wrong configed?

Code

from mahjong.hand_calculating.hand import HandCalculator
from mahjong.meld import Meld
from mahjong.hand_calculating.hand_config import HandConfig, OptionalRules
from mahjong.shanten import Shanten
from mahjong.tile import TilesConverter

calculator = HandCalculator()


# useful helper
def print_hand_result(hand_result):
    print(hand_result.han, hand_result.fu)
    print(hand_result.cost['main'])
    print(hand_result.yaku)
    for fu_item in hand_result.fu_details:
        print(fu_item)
    print('')

tiles = TilesConverter.string_to_136_array(man='22444', pin='333r67', sou='444', has_aka_dora = True)
win_tile = TilesConverter.string_to_136_array(sou='4')[0]
handconfig = HandConfig()
handconfig.has_aka_dora = True
handconfig.is_daburu_riichi = True
result = calculator.estimate_hand_value(tiles, win_tile, config = handconfig)
print_hand_result(result)

Expected

4 40
8000
[Double Riichi, Tanyao, Aka Dora 1]
{'fu': 30, 'reason': 'base'}
{'fu': 4, 'reason': 'closed_pon'}
{'fu': 4, 'reason': 'closed_pon'}
{'fu': 2, 'reason': 'open_pon'}

Actural

3 40
5200
[Double Riichi, Tanyao]
{'fu': 30, 'reason': 'base'}
{'fu': 4, 'reason': 'closed_pon'}
{'fu': 4, 'reason': 'closed_pon'}
{'fu': 2, 'reason': 'open_pon'}

ver.1.1.7 python3
PS. Aka Dora usually express as "0" in games like tenhou, 11223s4506p3405s2z etc. It'll be nice if "r" and "0" are both representing aka dora.
Sincerely Illava.

Calcuating Fu should use integer division

From mahjong/mahjong/hand_calculating/fu.py, line 158

def round_fu(self, fu_details):
	# 22 -> 30 and etc.
	fu = sum([x['fu'] for x in fu_details])
-	return int(math.ceil(fu / 10.0)) * 10
+	return (fu // 10) * 10

In addtion, there is no need to import math.

There is a same problem in mahjong/mahjong/hand_calculating/scores.py, too.

[question] Are there detail options for Aka Dora ?

Thank you for developing the mahjong package.

I have one question. Are there detailed options for Aka Dora?
Using the example script, I tried the below script

tiles = TilesConverter.string_to_136_array(man='234555', pin='555', sou='22555')

win_tile = TilesConverter.string_to_136_array(sou='5')[0]

melds = None

dora_indicators = None

config = HandConfig(is_tsumo=True,is_rinshan=True, options=OptionalRules(has_open_tanyao=True, has_aka_dora=True))

result = calculator.estimate_hand_value(tiles, win_tile, melds, dora_indicators, config)
print_hand_result(result)

The result is

10 40
8000 4000
[Menzen Tsumo, Rinshan Kaihou, Tanyao, San Ankou, Sanshoku Doukou, Aka Dora 3]
{'fu': 20, 'reason': 'base'}
{'fu': 4, 'reason': 'closed_pon'}
{'fu': 4, 'reason': 'closed_pon'}
{'fu': 4, 'reason': 'closed_pon'}
{'fu': 2, 'reason': 'tsumo'}

With this config, three types (man, pin, and sou) of 5 are regarded as Aka. Thus, the result returns Aka Dora 3.
If I want to specify man and pin include Aka Dora and sou isn't included Aka Dora, are there any options?

Best regards.

Missing duplicate pair check for Chiitoitsu

Code for Chiitoitsu is:
def is_condition_met(self, hand, *args): return len(hand) == 7

But riichi rules specify that all pairs must be different.
I suggest something like:

def is_condition_met(self, hand, *args): return len(hand) == 7 and len(set(hand))==len(hand)

Suggestions of new features

I just found this repo. Wonderful!

  1. Should we include honba (本場)numbers into HandConfig for adjustment of cost calculations?
  2. Should we include the tiles that lead to less shanten number in the return value of the shanten calculator so that it would be the same as in [url]https://tenhou.net/2/

OptionalRules import error

Hi,

Just let you know that when I use pip install mahjong, I will get version 1.1.6 which is not including OptionalRules in mahjong.hand_calculating.hand_config.
Therefore, the example code of open set in readme.rst cannot be executed with options=OptionalRules(has_open_tanyao=True).

Thanks

Problem of daburu chuuren poutou

Hi, I have tried the following code:

calculator = HandCalculator()

tiles = TilesConverter.string_to_136_array(man='11123456789999')
win_tile = TilesConverter.string_to_136_array(man='9')[0]

result = calculator.estimate_hand_value(tiles, win_tile)

print(result.han, result.fu)
print(result.cost['main'])
print(result.yaku)
for fu_item in result.fu_details:
    print(fu_item)

and it output:

13 40
32000
[Chuuren Poutou]
{'fu': 30, 'reason': 'base'}
{'fu': 4, 'reason': 'open_terminal_pon'}

However, based on the rule, it should be Daburu Chuuren Poutou. Same problem happened when tiles = 11112345678999.

If I ron or tsumo with 2-8, it will output Daburu Chuuren Poutou normally. Did I misunderstand any rule or forget to set any parameter?

Thanks!

How is AKA dora handled?

I had trouble implementing aka dora. I had to change utils class to this:

    @staticmethod
    def string_to_136_array(sou=None, pin=None, man=None, honors=None):
        """
        Method to convert one line string tiles format to the 136 array
        We need it to increase readability of our tests
        """
        def _split_string(string, offset, red=None):
            data = []
            temp = []

            if not string:
                return []

            for i in string:
                if i == 'r':
                    temp.append(red)
                    data.append(red)
                else:
                    tile = offset + (int(i) - 1) * 4
                    if tile == red:
                        # prevent non reds to become red?
                        tile += 1
                    if tile in data:
                        count_of_tiles = len([x for x in temp if x == tile])
                        new_tile = tile + count_of_tiles
                        data.append(new_tile)

                        temp.append(tile)
                    else:
                        data.append(tile)
                        temp.append(tile)

            return data

        results = _split_string(man, 0, FIVE_RED_MAN)
        results += _split_string(pin, 36, FIVE_RED_PIN)
        results += _split_string(sou, 72, FIVE_RED_SOU)
        results += _split_string(honors, 108)

        return results

to pass red dora information in the tile converter. Is there a better way than this?

Enhancement:Print red dora in one_line_string

I'm dealing with tiles with red dora and these might be useful debugging. In tile.py.
Sincerely Illava

    @staticmethod
    def to_one_line_string(tiles, has_aka_dora=False):
        """
        Convert 136 tiles array to the one line string
        Example of output 123s123p123m33z
        """
        tiles = sorted(tiles)

        man = [t for t in tiles if t < 36]

        pin = [t for t in tiles if 36 <= t < 72]
        pin = [t - 36 for t in pin]

        sou = [t for t in tiles if 72 <= t < 108]
        sou = [t - 72 for t in sou]

        honors = [t for t in tiles if t >= 108]
        honors = [t - 108 for t in honors]
                
        def words(suits, red_five, suffix):
            return suits and ''.join(["0" if i == red_five and has_aka_dora else str((i // 4) + 1) for i in suits]) + suffix or ''    
				
        sou = words(sou, FIVE_RED_SOU - 72, 's')
        pin = words(pin, FIVE_RED_PIN - 36, 'p')
        man = words(man, FIVE_RED_MAN, 'm')
        honors = words(honors, -1 - 108, 'z')

        return man + pin + sou + honors
>>> import tile
>>> tile.TilesConverter.to_one_line_string([1,16,13,46,5,13,24,34,134,124])
'1244579m3p57z'
>>> tile.TilesConverter.to_one_line_string([1,16,13,46,5,13,24,34,134,124], False)
'1244579m3p57z'
>>> tile.TilesConverter.to_one_line_string([1,16,13,46,5,13,24,34,134,124], True)
'1244079m3p57z'

Should 26+ han kazoe yakuman considered as double yakuman?

There are many different rules about kazoe yakuman. Link

  1. Kazoe yakuman does not exist. Hands over 13+ han without yakuman is considered as sanbaiman.
  2. Kazoe yakuman exists, but hands over 26+ han do not counted as double yakuman.
  3. Kazoe yakuman exists, and you can consider 26+ han as double yakuman, 39+ han as triple yakuman, etc.

I don't know which rule is used by tenhou.net, but according to Wikipedia,

In most rules, a hand with 13 han or above is considered kazoe-yakuman.

so we could guess that tenhou.net uses the second rule.
(If someone has tenhou.net reply link that contains 26+ han, please upload it)

In addition, this library is considering 26+ han hands as double yakuman, such as

4446667788m + 7m / (2222m closed kan) / riichi (ron)
Dora and uradora indicators are [1m, 1m, 1m, 1m]
riichi tanyao chiniisou sanankou toitoi dora16 (28 Han)

Introduce presets with rules

It could be good to have preset for popular rules.

So, user can chose one instead of building rules manually.

Presets candidates:

  • tenhou.net
  • majsoul
  • EMA
  • JPML-B
  • WRC

[Bug] `ValueError: negative shift count` at calculating hand.(Test code included)

Description:

I encountered a potential issue with the tile reduction logic used in the hand calculation process. The current implementation seems to decrement the tile count incorrectly, which might lead to a ValueError. I've provided a test code snippet and a link to the relevant code section for your review.

Test Code:

from mahjong.meld import Meld
from mahjong.tile import TilesConverter
from mahjong.hand_calculating.hand import HandCalculator

# Setup melds and tiles
melds = [
    Meld(meld_type=Meld.CHI, tiles=TilesConverter.string_to_136_array(sou="678")),
    Meld(meld_type=Meld.CHI, tiles=TilesConverter.string_to_136_array(man="345")),
    Meld(meld_type=Meld.CHI, tiles=TilesConverter.string_to_136_array(pin="345")),
    Meld(meld_type=Meld.PON, tiles=TilesConverter.string_to_136_array(honors="222"))
]
tiles = TilesConverter.string_to_136_array(sou='33')
win_tile = TilesConverter.string_to_136_array(sou='3')[0]

# Calculate hand value
calculator = HandCalculator()
result = calculator.estimate_hand_value(tiles=tiles, melds=melds, win_tile=win_tile)

Suspected Issue:
I suspect there might be an issue with the tiles[x] -= x logic in the code, as it seems leading to << a negative number.

Relevant Code Section:

mahjong/mahjong/agari.py

Lines 17 to 41 in cb749aa

if open_sets_34:
isolated_tiles = find_isolated_tile_indices(tiles)
for meld in open_sets_34:
if not isolated_tiles:
break
isolated_tile = isolated_tiles.pop()
tiles[meld[0]] -= 1
tiles[meld[1]] -= 1
tiles[meld[2]] -= 1
# kan
if len(meld) > 3:
tiles[meld[3]] -= 1
tiles[isolated_tile] = 3
j = (
(1 << tiles[27])
| (1 << tiles[28])
| (1 << tiles[29])
| (1 << tiles[30])
| (1 << tiles[31])
| (1 << tiles[32])
| (1 << tiles[33])
)

Expected vs. Actual Results:
For reference, here's the result from Tenhou:
Tenhou result

Problem of All Green

Hi,I have tried the following code:

from mahjong.hand_calculating.hand import HandCalculator
from mahjong.meld import Meld
from mahjong.hand_calculating.hand_config import HandConfig, OptionalRules
from mahjong.shanten import Shanten
from mahjong.tile import TilesConverter
calculator = HandCalculator()
# useful helper
def print_hand_result(hand_result):
    print(hand_result.han, hand_result.fu)
    print(hand_result.cost['main'])
    print(hand_result.yaku)
    for fu_item in hand_result.fu_details:
        print(fu_item)
    print('')
# we had to use all 14 tiles in that array

#CHI 234s
chi=Meld(meld_type=Meld.CHI, tiles=TilesConverter.string_to_136_array(sou='234'))
melds = [chi,chi,chi,chi]

# 6s6s 234s 234s 234s 234s
tiles = TilesConverter.string_to_136_array(sou='23423423423466')
win_tile = TilesConverter.string_to_136_array(sou='6')[0]
result = calculator.estimate_hand_value(tiles, win_tile, melds=melds, config=HandConfig(options=OptionalRules(has_open_tanyao=True)))
print(result)
print_hand_result(result)

and it output:

6 han, 30 fu
6 30
12000
[Tanyao, Chinitsu]
{'fu': 20, 'reason': 'base'}
{'fu': 2, 'reason': 'pair_wait'}

All Green should not be optimized:

# small optimization, try to detect yaku with pon required sets only if we have pon sets in hand
if len(pon_sets):

if self.config.yaku.ryuisou.is_condition_met(hand):
hand_yaku.append(self.config.yaku.ryuisou)

Another fu calculation error

This seems similar to #40, but I'm not sure if it's actually a duplicate. In this case it impacts the reported cost:

from mahjong.hand_calculating.hand import HandCalculator
from mahjong.tile import TilesConverter
from mahjong.hand_calculating.hand_config import HandConfig
from mahjong.meld import Meld

calculator = HandCalculator()
tiles = TilesConverter.one_line_string_to_136_array('11112233444m555p')
melds = [Meld(meld_type=Meld.CHI, tiles=TilesConverter.string_to_136_array(man='123'))]
win_tile = TilesConverter.one_line_string_to_136_array('1m')[0]

result = calculator.estimate_hand_value(tiles, win_tile, melds=melds, config=HandConfig(is_houtei=True))

print(result.han, result.fu)
print(result.cost['main'])
print(result.yaku)
for fu_item in result.fu_details:
    print(fu_item)
1 40
1300
[Houtei Raoyui]
{'fu': 20, 'reason': 'base'}
{'fu': 8, 'reason': 'closed_terminal_pon'}
{'fu': 4, 'reason': 'closed_pon'}

There can't be a closed_terminal_pon here because one of the 1m is in a chi and another is called for ron. Either open_terminal_pon (if you treat it as shanpon and divide it 111 234 44 555) or closed_pon (treat it as ryanmen and divide it 11 123 444 555) would be correct, and either reduces the fu to 30.

Test failed on Python 3.8

$ python --version
Python 3.8.0b2+
======================================================================
FAIL: test_is_ryanpeiko (mahjong.hand_calculating.tests.tests_yaku_calculation.YakuCalculationTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/travis/build/MahjongRepository/mahjong/mahjong/hand_calculating/tests/tests_yaku_calculation.py", line 495, in test_is_ryanpeiko
    self.assertTrue(self.config.ryanpeiko.is_condition_met(self._hand(tiles, 1)))
AssertionError: False is not true

New code style

@profthecopyright I enforced new code style to the project, since the code has history and there are really different formatting in different code parts.

Locally you can do that like this:

  1. Run this command one time: pip install -r requirements-dev.txt
  2. Before each commit run make format and make lint

It will automatically format files and will check possible issues.

misleading description in the wiki

The sample of "tiles" when Kan is misleading.

wrong:

tiles = self.TilesConverter.string_to_136_array(honors='11133555666777')
win_tile = self.TilesConverter.string_to_136_array(honors='3')[0]

melds = [
    Meld(meld_type=Meld.KAN, tiles=TilesConverter.string_to_136_array(honors='1111'), opened=False),
    Meld(meld_type=Meld.KAN, tiles=TilesConverter.string_to_136_array(honors='5555'), opened=False),
    Meld(meld_type=Meld.KAN, tiles=TilesConverter.string_to_136_array(honors='6666'), opened=False),
    Meld(meld_type=Meld.KAN, tiles=TilesConverter.string_to_136_array(honors='7777'), opened=False),
]

correct:

tiles = self.TilesConverter.string_to_136_array(honors='111133555566667777')
win_tile = self.TilesConverter.string_to_136_array(honors='3')[0]

melds = [
    Meld(meld_type=Meld.KAN, tiles=TilesConverter.string_to_136_array(honors='1111'), opened=False),
    Meld(meld_type=Meld.KAN, tiles=TilesConverter.string_to_136_array(honors='5555'), opened=False),
    Meld(meld_type=Meld.KAN, tiles=TilesConverter.string_to_136_array(honors='6666'), opened=False),
    Meld(meld_type=Meld.KAN, tiles=TilesConverter.string_to_136_array(honors='7777'), opened=False),
]

Wrong Shanten Calculating

sh = Shanten()
tiles = TilesConverter.string_to_34_array(man='123456789', honors='1111')
print(sh.calculate_shanten_for_regular_hand(tiles))
tiles2 = TilesConverter.string_to_34_array(man='123456789', pin='1111')
print(sh.calculate_shanten_for_regular_hand(tiles2))

output

1
0

In shanten calculation, 4 non-honor tiles can be used as meld and waiting itself as pair, but it is impossible.

IndexError with certain invalid hands (five of the same tile)

Background

While playing around with this library, I was randomly generating and evaluating hands and accidentally made some hands with five of the same tile. In some cases, this leads to an IndexError

Code

from mahjong.hand_calculating.hand import *
from mahjong.hand_calculating.hand_config import *
from mahjong.meld import *
import traceback

calculator = HandCalculator()

config = HandConfig(is_tsumo=True, is_riichi=False, player_wind=SOUTH, round_wind=EAST,
                    options=OptionalRules(has_open_tanyao=True, has_aka_dora=True))

draws = [
    [36, 96, 12, 12, 44, 16, 16, 100, 16, 16, 16, 92, 28, 40, 0],
    [40, 40, 32, 0, 24, 28, 64, 40, 40, 0, 12, 8, 16, 40, 80],
    [88, 28, 76, 32, 40, 44, 76, 24, 76, 76, 100, 48, 88, 76, 20],
    [16, 80, 16, 16, 8, 28, 80, 16, 80, 16, 88, 12, 88, 4, 60],
    [84, 84, 76, 76, 4, 4, 28, 4, 4, 20, 4, 16, 76, 24, 128],
    [52, 124, 52, 52, 56, 80, 124, 76, 52, 84, 52, 56, 64, 124, 80],
    [4, 80, 16, 16, 4, 28, 100, 84, 96, 4, 104, 4, 88, 4, 100],
    [12, 20, 116, 120, 4, 16, 120, 4, 28, 120, 4, 116, 4, 4, 12],
    [76, 48, 76, 88, 76, 48, 12, 100, 16, 76, 8, 48, 88, 76, 4]

]
for draw in draws:
    print("hand", TilesConverter.to_one_line_string(draw[:14], print_aka_dora=True))
    print("winning tile", TilesConverter.to_one_line_string([draw[0]], print_aka_dora=True))
    print("dora", TilesConverter.to_one_line_string(draw[-1:], print_aka_dora=True))

    try:
        result = calculator.estimate_hand_value(draw[:14], win_tile=draw[0], dora_indicators=draw[-1:], config=config)
        print(result)
    except:
        print(traceback.format_exc())

Result

A whole bunch of stack traces
"C:\Program Files\Python39\python.exe" Z:/Dropbox/PyCharm/mahjong-master/mahjong-master/doc/reproduce_the_mahjong.py
hand 44000008m123p678s
winning tile 1p
dora 1m
Traceback (most recent call last):
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\doc\reproduce_the_mahjong.py", line 29, in <module>
    result = calculator.estimate_hand_value(draw[:14], win_tile=draw[0], dora_indicators=draw[-1:], config=config)
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\mahjong\hand_calculating\hand.py", line 533, in estimate_hand_value
    calculated_hand = calculated_hands[0]
IndexError: list index out of range

hand 11340789m222228p
winning tile 2p
dora 3s
Traceback (most recent call last):
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\doc\reproduce_the_mahjong.py", line 29, in <module>
    result = calculator.estimate_hand_value(draw[:14], win_tile=draw[0], dora_indicators=draw[-1:], config=config)
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\mahjong\hand_calculating\hand.py", line 533, in estimate_hand_value
    calculated_hand = calculated_hands[0]
IndexError: list index out of range

hand 789m234p22222008s
winning tile 0s
dora 6m
Traceback (most recent call last):
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\doc\reproduce_the_mahjong.py", line 29, in <module>
    result = calculator.estimate_hand_value(draw[:14], win_tile=draw[0], dora_indicators=draw[-1:], config=config)
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\mahjong\hand_calculating\hand.py", line 533, in estimate_hand_value
    calculated_hand = calculated_hands[0]
IndexError: list index out of range

hand 234000008m33300s
winning tile 0m
dora 7p
Traceback (most recent call last):
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\doc\reproduce_the_mahjong.py", line 29, in <module>
    result = calculator.estimate_hand_value(draw[:14], win_tile=draw[0], dora_indicators=draw[-1:], config=config)
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\mahjong\hand_calculating\hand.py", line 533, in estimate_hand_value
    calculated_hand = calculated_hands[0]
IndexError: list index out of range

hand 222220678m22244s
winning tile 4s
dora 6z
Traceback (most recent call last):
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\doc\reproduce_the_mahjong.py", line 29, in <module>
    result = calculator.estimate_hand_value(draw[:14], win_tile=draw[0], dora_indicators=draw[-1:], config=config)
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\mahjong\hand_calculating\hand.py", line 533, in estimate_hand_value
    calculated_hand = calculated_hands[0]
IndexError: list index out of range

hand 00000668p234s555z
winning tile 0p
dora 3s
Traceback (most recent call last):
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\doc\reproduce_the_mahjong.py", line 29, in <module>
    result = calculator.estimate_hand_value(draw[:14], win_tile=draw[0], dora_indicators=draw[-1:], config=config)
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\mahjong\hand_calculating\hand.py", line 533, in estimate_hand_value
    calculated_hand = calculated_hands[0]
IndexError: list index out of range

hand 22222008m340789s
winning tile 2m
dora 8s
Traceback (most recent call last):
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\doc\reproduce_the_mahjong.py", line 29, in <module>
    result = calculator.estimate_hand_value(draw[:14], win_tile=draw[0], dora_indicators=draw[-1:], config=config)
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\mahjong\hand_calculating\hand.py", line 533, in estimate_hand_value
    calculated_hand = calculated_hands[0]
IndexError: list index out of range

hand 222224068m33444z
winning tile 4m
dora 4m
Traceback (most recent call last):
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\doc\reproduce_the_mahjong.py", line 29, in <module>
    result = calculator.estimate_hand_value(draw[:14], win_tile=draw[0], dora_indicators=draw[-1:], config=config)
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\mahjong\hand_calculating\hand.py", line 533, in estimate_hand_value
    calculated_hand = calculated_hands[0]
IndexError: list index out of range

hand 340m444p22222008s
winning tile 2s
dora 2m
Traceback (most recent call last):
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\doc\reproduce_the_mahjong.py", line 29, in <module>
    result = calculator.estimate_hand_value(draw[:14], win_tile=draw[0], dora_indicators=draw[-1:], config=config)
  File "Z:\Dropbox\PyCharm\mahjong-master\mahjong-master\mahjong\hand_calculating\hand.py", line 533, in estimate_hand_value
    calculated_hand = calculated_hands[0]
IndexError: list index out of range


Process finished with exit code 0

Expected

I'm not sure? Obviously this is an exceptional case, but the library gracefully handles many other exceptional cases so I would expect it to handle this one as well.

Environment

Python 3.9.0 (tags/v3.9.0:9cf6752, Oct 5 2020, 15:34:40) [MSC v.1927 64 bit (AMD64)] on win32

Reproduced with both 1.1.11 from PyPI and the latest master commit. The stack traces are based off c3e64e5

hand_not_winning for pons containing dragons

Hi there,

I think there's an issue with hands that contain pons of dragon tiles.

I've ran the below, unless I'm wrong 777h is a pon of chun tiles so the hand should be winning with at least 1 yaku.

I've tested and get the same for 666h and 555h seems to give a terminating error.

Is this a bug or am I doing something wrong?

from mahjong.hand_calculating.hand import HandCalculator
from mahjong.tile import TilesConverter
from mahjong.hand_calculating.hand_config import HandConfig
from mahjong.meld import Meld

calculator = HandCalculator()

tiles = TilesConverter.one_line_string_to_136_array("678s678p55m")
win_tile =TilesConverter.one_line_string_to_136_array("8s")[0]

melds = [
    Meld(meld_type="pon", tiles=TilesConverter.one_line_string_to_136_array("777h"), opened=True),
    Meld(meld_type="chi", tiles=TilesConverter.one_line_string_to_136_array("234p"), opened=True)
    ]

result = calculator.estimate_hand_value(tiles, win_tile,melds)

print(result)

Future 1.2.0 version

Looking at the project architecture I'm seeing multiple places where we can improve the code structure to support future changes more easily.

Because of that, I decided to release a new version without compatibility with the old version.

Main planned features for the new version:

  • Remove support for Python 2.7. This version is not supported anymore and with Python 3+ only we can start to use type hints for better code documentation
  • Create default presets for popular rulesets
  • Various project architecture changes
  • Various optimization for project code performance. For that we need to create benchmarks first and improve code performance step by step. This is a necessary step because our bot is playing slower and slower and with improving mahjong lib we can improve performance for the bot as well
  • Improve documentation (migrate to github md as well?) and write more examples how to use lib
  • Move ukeire calculations from bot to mahjong lib

Problem of KokushiMusou

In case of 'KokushiMusou', divider just return empty list. So 'fu' can't be calculated. So errors are printed when I run the first example in main page of this repo with 'Kokushi Musou Juusanmen Matchi'.

Agari does not work with more than 14 tiles in hand?

>>> melds = [TilesConverter.string_to_34_array(sou='1111')]
>>> tiles =  TilesConverter.string_to_34_array(sou='111122233344455')
>>> a.is_agari(tiles, melds)
False
>>> a.is_agari(tiles)
False

15 tiles hand should be winnable because of that kan

Fu calculation error

from mahjong.hand_calculating.hand import HandCalculator
from mahjong.tile import TilesConverter
from mahjong.hand_calculating.hand_config import HandConfig
from mahjong.meld import Meld

calculator = HandCalculator()

# we had to use all 14 tiles in that array
tiles = TilesConverter.one_line_string_to_136_array("234m11123567774s")
win_tile =TilesConverter.one_line_string_to_136_array("4s")[0]

result = calculator.estimate_hand_value(tiles, win_tile,config=HandConfig(is_tsumo=True))

print(result.han, result.fu)
print(result.cost['main'])
print(result.yaku)
for fu_item in result.fu_details:
    print(fu_item)

RETURN

1 30
500
[Menzen Tsumo]
{'fu': 20, 'reason': 'base'}
{'fu': 4, 'reason': 'closed_pon'}
{'fu': 2, 'reason': 'tsumo'}

{'fu': 4, 'reason': 'closed_pon'} should be {'fu': 8, 'reason': 'closed_terminal_pon'}

Enhancement: Add one line string to 136/34 array

Enhancement: Add one line string to 136/34 array in file tile.py.

    @staticmethod
    def one_line_string_to_136_array(string=None, has_aka_dora=False):
        """
        Method to convert one line string tiles format to the 136 array, like
        "123s456p789m11222z". 's' stands for sou, 'p' stands for pin, 
        'm' stands for man and 'z' or 'h' stands for honor.
        You can pass r or 0 instead of 5 for it to become a red five from
        that suit. To prevent old usage without red,
        has_aka_dora has to be True for this to do that.
        We need it to increase readability of our tests
        """
        sou = ""
        pin = ""
        man = ""
        honors = ""
		
        split_start = 0
		
        for index, i in enumerate(string):
            if i == 'm':
                man += string[split_start: index]
                split_start = index + 1
            if i == 'p':
                pin += string[split_start: index]
                split_start = index + 1
            if i == 's':
                sou += string[split_start: index]
                split_start = index + 1
            if i == 'z' or i == 'h':
                honors += string[split_start: index]
                split_start = index + 1
				
        return TilesConverter.string_to_136_array(sou, pin, man, honors, has_aka_dora)
    @staticmethod
    def one_line_string_to_34_array(string=None):
        """
        Method to convert one line string tiles format to the 34 array
        We need it to increase readability of our tests
        """
        results = TilesConverter.one_line_string_to_136_array(string)
        results = TilesConverter.to_34_array(results)
        return results
>>> import tile
>>> tile.TilesConverter.one_line_string_to_34_array("555s456p789m11222z")
[0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0]
>>> tile.TilesConverter.one_line_string_to_136_array("555s456p789m11222z", has_aka_dora = True)
[24, 28, 32, 48, 53, 56, 89, 90, 91, 108, 109, 112, 113, 114]

Sincerely Illava.

Python2

By the way, wanted to let you know that this works under python2.7 without any problems :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.