Giter Site home page Giter Site logo

smly / mjai.app Goto Github PK

View Code? Open in Web Editor NEW
55.0 6.0 7.0 590 KB

Mahjong game simulator for RiichiLab https://mjai.app

Home Page: https://github.com/smly/mjai.app/discussions

License: GNU Affero General Public License v3.0

Dockerfile 0.48% Python 33.24% Shell 0.10% Rust 66.18%

mjai.app's Issues

A legitimate `hora` is not accepted and results in an error

Describe the bug
As stated in the title.

Log
The log folder is attached as a .zip file to this bug report.

Expected behavior
The following is an excerpt from mjai_log.json related to the round where the error occurred. Note that after the log of each dahai by Player 1, I provide the Player 1's hand and the meldings (副露) immediately following the dahai.

{"type":"start_kyoku","bakaze":"E","dora_marker":"6s","kyoku":4,"honba":0,"kyotaku":0,"oya":3,"scores":[12800,52600,22500,12100],"tehais":[["7s","7m","9m","7p","P","8s","4s","8s","F","2m","7s","5pr","7p"],["9m","8p","4p","N","3m","4p","E","8s","2p","S","6m","C","8m"],["5s","5p","W","1m","3s","9s","1p","1s","C","2m"$
{"type":"tsumo","actor":3,"pai":"1s"}
{"type":"dahai","actor":3,"pai":"W","tsumogiri":false}
{"type":"tsumo","actor":0,"pai":"5s"}
{"type":"dahai","actor":0,"pai":"F","tsumogiri":false}
{"type":"tsumo","actor":1,"pai":"4m"}
{"type":"dahai","actor":1,"pai":"N","tsumogiri":false},"3m","4m","6m","8m","9m","2p","4p","4p","8p","8s","E","S","C"
{"type":"tsumo","actor":2,"pai":"W"}
{"type":"dahai","actor":2,"pai":"9p","tsumogiri":false}
{"type":"tsumo","actor":3,"pai":"4m"}
{"type":"dahai","actor":3,"pai":"S","tsumogiri":false}
{"type":"tsumo","actor":0,"pai":"F"}
{"type":"dahai","actor":0,"pai":"F","tsumogiri":true}
{"type":"tsumo","actor":1,"pai":"3p"}
{"type":"dahai","actor":1,"pai":"S","tsumogiri":false},"3m","4m","6m","8m","9m","2p","3p","4p","4p","8p","8s","E","C"
{"type":"tsumo","actor":2,"pai":"3p"}
{"type":"dahai","actor":2,"pai":"C","tsumogiri":false}
{"type":"tsumo","actor":3,"pai":"1p"}
{"type":"dahai","actor":3,"pai":"1m","tsumogiri":false}
{"type":"tsumo","actor":0,"pai":"1p"}
{"type":"dahai","actor":0,"pai":"1p","tsumogiri":true}
{"type":"tsumo","actor":1,"pai":"9m"}
{"type":"dahai","actor":1,"pai":"C","tsumogiri":false},"3m","4m","6m","8m","9m","9m","2p","3p","4p","4p","8p","8s","E"
{"type":"tsumo","actor":2,"pai":"P"}
{"type":"dahai","actor":2,"pai":"P","tsumogiri":true}
{"type":"tsumo","actor":3,"pai":"F"}
{"type":"dahai","actor":3,"pai":"F","tsumogiri":true}
{"type":"tsumo","actor":0,"pai":"3p"}
{"type":"dahai","actor":0,"pai":"P","tsumogiri":false}
{"type":"tsumo","actor":1,"pai":"E"}
{"type":"dahai","actor":1,"pai":"8p","tsumogiri":false},"3m","4m","6m","8m","9m","9m","2p","3p","4p","4p","8s","E","E"
{"type":"tsumo","actor":2,"pai":"5m"}
{"type":"dahai","actor":2,"pai":"9s","tsumogiri":false}
{"type":"tsumo","actor":3,"pai":"N"}
{"type":"dahai","actor":3,"pai":"N","tsumogiri":true}
{"type":"tsumo","actor":0,"pai":"7s"}
{"type":"dahai",
[logs.2023-09-02-02-12-52.zip](https://github.com/smly/mjai.app/files/12613400/logs.2023-09-02-02-12-52.zip)
"actor":0,"pai":"2m","tsumogiri":false}
{"type":"tsumo","actor":1,"pai":"7p"}
{"type":"dahai","actor":1,"pai":"7p","tsumogiri":true},"3m","4m","6m","8m","9m","9m","2p","3p","4p","4p","8s","E","E"
{"type":"pon","actor":0,"target":1,"pai":"7p","consumed":["7p","7p"]}
{"type":"dahai","actor":0,"pai":"9m","tsumogiri":false}
{"type":"tsumo","actor":1,"pai":"4s"}
{"type":"dahai","actor":1,"pai":"8s","tsumogiri":false},"3m","4m","6m","8m","9m","9m","2p","3p","4p","4p","4s","E","E"
{"type":"tsumo","actor":2,"pai":"5mr"}
{"type":"dahai","actor":2,"pai":"5s","tsumogiri":false}
{"type":"tsumo","actor":3,"pai":"9p"}
{"type":"dahai","actor":3,"pai":"9p","tsumogiri":true}
{"type":"tsumo","actor":0,"pai":"3m"}
{"type":"dahai","actor":0,"pai":"3m","tsumogiri":true}
{"type":"tsumo","actor":1,"pai":"6s"}
{"type":"dahai","actor":1,"pai":"4p","tsumogiri":false},"3m","4m","6m","8m","9m","9m","2p","3p","4p","4s","6s","E","E"
{"type":"tsumo","actor":2,"pai":"8p"}
{"type":"dahai","actor":2,"pai":"8p","tsumogiri":true}
{"type":"tsumo","actor":3,"pai":"2s"}
{"type":"dahai","actor":3,"pai":"1p","tsumogiri":false}
{"type":"tsumo","actor":0,"pai":"1m"}
{"type":"dahai","actor":0,"pai":"7m","tsumogiri":false}
{"type":"chi","actor":1,"target":0,"pai":"7m","consumed":["6m","8m"]}
{"type":"dahai","actor":1,"pai":"6s","tsumogiri":false},"3m","4m","9m","9m","2p","3p","4p","4s","E","E",("7m",("6m","8m"))
{"type":"tsumo","actor":2,"pai":"C"}
{"type":"dahai","actor":2,"pai":"C","tsumogiri":true}
{"type":"tsumo","actor":3,"pai":"9s"}
{"type":"dahai","actor":3,"pai":"9s","tsumogiri":true}
{"type":"tsumo","actor":0,"pai":"7m"}
{"type":"dahai","actor":0,"pai":"7m","tsumogiri":true}
{"type":"tsumo","actor":1,"pai":"4m"}
{"type":"dahai","actor":1,"pai":"4s","tsumogiri":false},"3m","4m","4m","9m","9m","2p","3p","4p","E","E",("7m",("6m","8m"))
{"type":"tsumo","actor":2,"pai":"6p"}
{"type":"dahai","actor":2,"pai":"6p","tsumogiri":true}
{"type":"tsumo","actor":3,"pai":"P"}
{"type":"dahai","actor":3,"pai":"P","tsumogiri":true}
{"type":"tsumo","actor":0,"pai":"8m"}
{"type":"dahai","actor":0,"pai":"8m","tsumogiri":true}
{"type":"tsumo","actor":1,"pai":"6p"}
{"type":"dahai","actor":1,"pai":"6p","tsumogiri":true},"3m","4m","4m","9m","9m","2p","3p","4p","E","E",("7m",("6m","8m"))
{"type":"tsumo","actor":2,"pai":"7m"}
{"type":"dahai","actor":2,"pai":"7m","tsumogiri":true}
{"type":"tsumo","actor":3,"pai":"9s"}
{"type":"dahai","actor":3,"pai":"9s","tsumogiri":true}
{"type":"tsumo","actor":0,"pai":"S"}
{"type":"dahai","actor":0,"pai":"1m","tsumogiri":false}
{"type":"tsumo","actor":1,"pai":"6p"}
{"type":"dahai","actor":1,"pai":"6p","tsumogiri":true},"3m","4m","4m","9m","9m","2p","3p","4p","E","E",("7m",("6m","8m"))
{"type":"tsumo","actor":2,"pai":"P"}
{"type":"dahai","actor":2,"pai":"P","tsumogiri":true}
{"type":"tsumo","actor":3,"pai":"5p"}
{"type":"dahai","actor":3,"pai":"1s","tsumogiri":false}
{"type":"tsumo","actor":0,"pai":"5m"}
{"type":"dahai","actor":0,"pai":"5m","tsumogiri":true}
{"type":"chi","actor":1,"target":0,"pai":"5m","consumed":["3m","4m"]}
{"type":"dahai","actor":1,"pai":"4m","tsumogiri":false},"9m","9m","2p","3p","4p","E","E",("7m",("6m","8m")),("5m",("3m","4m"))
{"type":"tsumo","actor":2,"pai":"9m"}
{"type":"dahai","actor":2,"pai":"9m","tsumogiri":true}
{"type":"tsumo","actor":3,"pai":"E"}
{"type":"dahai","actor":3,"pai":"E","tsumogiri":true} ===> player 1 returns {"type": "hora", "actor": 1, "target": 3, "pai": "E"}
{"type":"ryukyoku","deltas":[2000,-8000,2000,4000],"reason":"error"}
{"type":"end_kyoku"}

As described above, Player 1 declares hora at the end of this round. However, this is flagged as an error. Since 'E' is the round wind, there is a valid hand and 1 han (飜), and it's a legitimate declaration of a win. This win declaration should be correctly accepted.

Comment of `Bot.last_self_tsumo()`

Describe the bug

@property
def last_self_tsumo(self) -> str:
"""
Last tile that the player drew by itself.
Tile format is mjai-style like '5mr' or 'P'.
Return a empty string when the player's first action is not tsumo.
"""
return self.player_state.last_self_tsumo() or ""

Return a empty string when the player's first last action is not tsumo.

`tehai_mjai()` gets incorrect output when there is aka dora

Describe the bug

@property
def tehai_mjai(self) -> list[str]:
"""
Player's hand as a list of tile strings in mjai format.
Example:
>>> bot.tehai_mjai
["1m", "2m", "6m", "9m", "1p", "3p", "4p", "3s", "4s", "5s",
"7s", "9s", "5z", "6z"]
"""
zi_map = ["E", "S", "W", "N", "P", "F", "C"]
ms, ps, ss, zis, akas = [], [], [], [], []
tiles = []
for tile_idx, tile_count in enumerate(self.player_state.tehai):
if tile_count and tile_idx == 4 and self.akas_in_hand[0]:
akas.append("5mr")
elif tile_count and tile_idx == 4 + 9 and self.akas_in_hand[1]:
akas.append("5pr")
elif tile_count and tile_idx == 4 + 18 and self.akas_in_hand[2]:
akas.append("5sr")
elif tile_count and tile_idx < 9:
ms += [f"{tile_idx + 1}m"] * tile_count
elif tile_count and tile_idx < 18:
ps += [f"{tile_idx - 9 + 1}p"] * tile_count
elif tile_count and tile_idx < 27:
ss += [f"{tile_idx - 18 + 1}s"] * tile_count
else:
for _ in range(tile_count):
zis.append(zi_map[tile_idx - 27])
tiles = ms + ps + ss + zis + akas
return tiles

When both 5mr and 5m in hand, output only gives '5mr'.

To Reproduce
Example contidion:

self.player_state.tehai = [
    2,1,1,1,4,1,1,1,1,
    0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,0
]
self.akas_in_hand = [True, False, False]

Output:

['1m', '1m', '2m', '3m', '4m', '6m', '7m', '8m', '9m', '5mr']

Expected behavior
Should output:

['1m', '1m', '2m', '3m', '4m', '5m', '5m', '5m', '6m', '7m', '8m', '9m', '5mr']

Additional context
A simple fix:

     @property 
     def tehai_mjai(self) -> list[str]: 
         """ 
         Player's hand as a list of tile strings in mjai format. 
  
         Example: 
             >>> bot.tehai_mjai 
             ["1m", "2m", "6m", "9m", "1p", "3p", "4p", "3s", "4s", "5s", 
              "7s", "9s", "5z", "6z"] 
         """ 
         zi_map = ["E", "S", "W", "N", "P", "F", "C"] 
         ms, ps, ss, zis, akas = [], [], [], [], [] 
         tiles = [] 
         for tile_idx, tile_count in enumerate(self.player_state.tehai): 
             if tile_count and tile_idx == 4 and self.akas_in_hand[0]: 
                 ms += [f"{tile_idx + 1}m"] * (tile_count-1)
                 akas.append("5mr") 
             elif tile_count and tile_idx == 4 + 9 and self.akas_in_hand[1]: 
                 ms += [f"{tile_idx + 1}p"] * (tile_count-1)
                 akas.append("5pr") 
             elif tile_count and tile_idx == 4 + 18 and self.akas_in_hand[2]: 
                 ms += [f"{tile_idx + 1}s"] * (tile_count-1)
                 akas.append("5sr") 
             elif tile_count and tile_idx < 9: 
                 ms += [f"{tile_idx + 1}m"] * tile_count 
             elif tile_count and tile_idx < 18: 
                 ps += [f"{tile_idx - 9 + 1}p"] * tile_count 
             elif tile_count and tile_idx < 27: 
                 ss += [f"{tile_idx - 18 + 1}s"] * tile_count 
             else: 
                 for _ in range(tile_count): 
                     zis.append(zi_map[tile_idx - 27]) 
  
         tiles = ms + ps + ss + zis + akas 
         return tiles 

Bot.action_kakan() uses wrong pai

Describe the bug

def action_kakan(self, consumed: list[str]) -> str:
return json.dumps(
{
"type": "kakan",
"actor": self.player_id,
"pai": self.last_kawa_tile,
"consumed": consumed, # 3 tiles to be consumed
},
separators=(",", ":"),
)

Here at line 434, pai should be the tile that add to the pon meld but not last_kawa_tile

Additional context
A fix:

    def action_kakan(self, consumed: list[str]) -> str:
        if consumed[0][0] == '5' and consumed[0][1] != 'z':
            if any(red in consumed for red in ['5mr', '5pr', '5sr']):
                pai = consumed[0][:2]
            else:
                pai = consumed[0]+'r'
        else:
            pai = consumed[0]
        return json.dumps(
            {
                "type": "kakan",
                "actor": self.player_id,
                "pai": pai,
                "consumed": consumed,  # 3 tiles to be consumed
            },
            separators=(",", ":"),
        )

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Awaiting Schedule

These updates are awaiting their schedule. Click on a checkbox to get an update now.

  • chore(deps): lock file maintenance

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

cargo
Cargo.toml
  • anyhow 1
  • log 0.4
  • pyo3-log 0.11
  • once_cell 1
  • serde_json 1
  • boomphf 0.6
  • byteorder 1
  • rayon 1
  • ndarray 0.15
  • numpy 0.21
  • serde_with 3
  • rand 0.8
  • rand_chacha 0.3
  • flate2 1
  • sha3 0.10
  • glob 0.3
  • derivative 2
  • ahash 0.8
  • tinyvec 1
  • serde 1
  • mimalloc 0.1
  • pyo3 0.21
github-actions
.github/workflows/cargo_test.yml
  • actions/checkout v4
  • actions-rs/toolchain v1
  • actions-rs/cargo v1
  • actions-rs/cargo v1
  • actions-rs/cargo v1
.github/workflows/lint.yml
  • actions/checkout v4
  • technote-space/get-diff-action v6
  • actions/setup-python v5
  • PyO3/maturin-action v1
.github/workflows/pytest.yml
  • actions/checkout v4
  • technote-space/get-diff-action v6
  • actions/setup-python v5
  • PyO3/maturin-action v1
.github/workflows/release.yml
  • actions/checkout v4
  • actions/setup-python v5
  • PyO3/maturin-action v1
  • actions/upload-artifact v4
  • actions/checkout v4
  • actions/setup-python v5
  • PyO3/maturin-action v1
  • actions/upload-artifact v4
  • actions/checkout v4
  • actions/setup-python v5
  • PyO3/maturin-action v1
  • actions/upload-artifact v4
  • actions/checkout v4
  • PyO3/maturin-action v1
  • actions/upload-artifact v4
  • actions/download-artifact v4
pep621
pyproject.toml
  • mahjong ~= 1.2.0
  • pytest ~=8.2.2
  • maturin >=1.1,<2.0
pip_requirements
docker/requirements.txt
  • torch ==2.2.2
  • numpy ==1.26.4

  • Check this box to trigger a request for Renovate to run again on this repository

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.