[PYTHON] Discrimination de la forme agari du mahjong

Discrimination de la forme agari du mahjong

Il semble que la probabilité de Tenwa soit une fois sur 330000 fois, donc Je voulais l'essayer, alors je l'ai écrit en python, C'était plus ennuyeux que ce à quoi je m'attendais.

L'algorithme est ici. http://www.onionsoft.net/hsp/mahjong.txt

Le rôle ou l'attente est correct ... Si je vais bien ~~ Je l'écrirai la prochaine fois ~~.

Je l'ai écrit @ [2014/9/25] http://qiita.com/arc279/items/1a7853ad8e2dc35961d1

Opération confirmée avec python2.7.6.

Les bases

Je l'ai écrit honnêtement. J'ai hérité de list de builtin parce que c'est gênant, mais je pense qu'il vaut mieux la déléguer.

Le lavage est jeté au hasard.

mj.py


#!/usr/bin/env python
# -*- coding: utf8 -*-

import itertools
import random
from collections import OrderedDict

class Yama(list):
    u'''mur'''
    WANPAI_NUM = 14

    class TsumoDoesNotRemain(Exception):
        u'''Seul le roi reste'''
        pass

    def __init__(self):
        pais = [ Pai.from_index(i) 
                for i in range(Pai.TOTAL_KIND_NUM * Pai.NUM_OF_EACH_KIND) ]
        #La lessive
        random.shuffle(pais)
        super(Yama, self).__init__(pais)

    def tsumo(self):
        u'''L'autonomie'''
        if len(self) <= self.WANPAI_NUM:
            raise self.TsumoDoesNotRemain

        return self.pop(0)

    def wanpai(self):
        return self[-self.WANPAI_NUM:]

    def haipai(self):
        u'''Distribution'''
        tehais = [ Tehai(), Tehai(), Tehai(), Tehai() ] #est(parent)Sud Ouest Nord

        # 4*3 tours
        for j in range(0, 3):
            for tehai in tehais:
                for i in range(0, 4):
                    pai = self.tsumo()
                    tehai.append(pai)

        #Choncho
        for tehai in tehais:
            pai = self.tsumo()
            tehai.append(pai)

        pai = self.tsumo()
        tehais[0].append(pai)

        return tehais

class Pai(object):
    u'''牌'''

    TOTAL_KIND_NUM = 34          # M/P/S +Tous types de personnages
    NUM_OF_EACH_KIND = 4         #4 feuilles par type
    NUM_OF_EACH_NUMBER_PAIS = 9  # M/P/Le nombre de S est 1..Jusqu'à 9

    class Suit:
        M = 0   #Homme
        P = 1   #Tube
        S = 2   #Mesure
        J = 3   #Personnage

        NAMES = {
            M: u"Homme",
            P: u"Tube",
            S: u"Mesure",
            J: u" ",
        }

    class Num:
        NAMES = {
            1: u"un",
            2: u"deux",
            3: u"Trois",
            4: u"quatre",
            5: u"Cinq",
            6: u"Six",
            7: u"Sept",
            8: u"Huit",
            9: u"Neuf",
        }

    class Jihai:
        E   = 1
        S   = 2
        W   = 3
        N   = 4
        HAK = 5
        HAT = 6
        CHU = 7

        NAMES = {
            E:   u"est",
            S:   u"Sud",
            W:   u"Ouest",
            N:   u"Nord",
            HAK: u"blanc",
            HAT: u"Repoussant",
            CHU: u"Pendant ~",
        }

    @classmethod
    def yaochupai(cls):
        u'''么 9 牌'''
        return [
            cls(cls.Suit.M, 1),
            cls(cls.Suit.M, 9),
            cls(cls.Suit.P, 1),
            cls(cls.Suit.P, 9),
            cls(cls.Suit.S, 1),
            cls(cls.Suit.S, 9),
            cls(cls.Suit.J, cls.Jihai.E),
            cls(cls.Suit.J, cls.Jihai.S),
            cls(cls.Suit.J, cls.Jihai.W),
            cls(cls.Suit.J, cls.Jihai.N),
            cls(cls.Suit.J, cls.Jihai.HAK),
            cls(cls.Suit.J, cls.Jihai.HAT),
            cls(cls.Suit.J, cls.Jihai.CHU),
        ]

    def __init__(self, suit, num):
        self.suit = suit
        self.num  = num

    @property
    def index(self):
        return self.suit * self.NUM_OF_EACH_NUMBER_PAIS + self.num

    def __repr__(self):
        #return str((self.suit, self.num))    #Affichage Taple
        if self.suit == Pai.Suit.J:
            return Pai.Jihai.NAMES[self.num].encode('utf-8')
        else:
            return (Pai.Num.NAMES[self.num] + Pai.Suit.NAMES[self.suit]).encode('utf-8')

    def __eq__(self, other):
        return self.suit == other.suit and self.num == other.num

    @classmethod
    def from_index(cls, index):
        kind = index % cls.TOTAL_KIND_NUM

        if True:
            suit = kind / cls.NUM_OF_EACH_NUMBER_PAIS
            num  = kind % cls.NUM_OF_EACH_NUMBER_PAIS + 1
        else:
            if 0 <= kind < 9:
                suit = cls.Suit.M
                num  = kind - 0 + 1
            elif 9 <= kind < 18:
                suit = cls.Suit.P
                num  = kind - 9 + 1
            elif 18 <= kind < 27:
                suit = cls.Suit.S
                num  = kind - 18 + 1
            elif 27 <= kind < 34:
                suit = cls.Suit.J
                num  = kind - 27 + 1

        assert(cls.Suit.M <= suit <= cls.Suit.J)
        assert(1 <= num <= cls.NUM_OF_EACH_NUMBER_PAIS)

        return cls(suit, num)

class Tehai(list):
    u'''Artisanat'''

    @staticmethod
    def sorter(a, b):
        u'''Comment faire'''
        return a.suit - b.suit if a.suit != b.suit else a.num - b.num

    def rihai(self):
        u'''Rimu'''
        self.sort(cmp=self.sorter)
        return self

    def aggregate(self):
        u'''{牌 graine:Nombre de feuilles}Agrégat sous forme de'''
        hash = { x[0]: len(list(x[1])) for x in itertools.groupby(self.rihai()) }
        ret = OrderedDict()
        #Les tuiles clés restent désormais triées
        for x in sorted(hash.keys(), cmp=self.sorter):
            ret[x] = hash[x]
        return ret

    def show(self):
        u'''Affichage sous une forme facile à lire'''
        line1 = u"|"
        line2 = u"|"
        for pai in self.rihai():
            if pai.suit != Pai.Suit.J:
                line1 += Pai.Num.NAMES[pai.num] + u"|"
                line2 += Pai.Suit.NAMES[pai.suit] + u"|"
            else:
                line1 += Pai.Jihai.NAMES[pai.num] + u"|"
                line2 += u" |"

        print line1.encode("utf-8")
        print line2.encode("utf-8")

Vérification du type Agari

Y a-t-il sept paires d'enfants et Kokushi Musou? Cette zone dépend de l'interprétation, donc c'est bien. Ici, j'ai écrit que les sept paires n'avaient pas de tête de moineau et que le Kokushi Musou avait deux têtes de moineau.

C'était difficile de couper la partie du jugement à la classe, alors j'ai utilisé une clôture.

mj.py


def check_hohra(tehai):
    u'''Vérifiez la forme de l'extrémité'''
    assert(len(tehai) == 14)
    pais = tehai.aggregate()
    keys = pais.keys()
    length = len(keys)
    #print pais, keys, length

    def check_chitoitsu(pais):
        u'''Vérification de sept paires'''
        if all([ num == 2 for pai, num in pais.items()]):
            return (), [ (pai, pai) for pai, num in pais.items() ]
        return None

    def check_kokushimusou(pais):
        u'''Chèque Kokushi Musou'''
        if length != 13:
            return None

        yaochupai = Pai.yaochupai()
        mentsu = []
        for pai, num in pais.items():
            if pai not in yaochupai:
                return None

            # TODO:Est-il acceptable d'en avoir deux ici?
            if num == 2:
                atama = (pai, pai)
            else:
                assert(num == 1)
                mentsu.append(pai)

        return atama, mentsu


    def search_syuntu(pais):
        u'''Trouver Junko'''
        for i in range(length):
            if pais[keys[i]] >= 1:
                first = keys[i]
                try:
                    second = keys[i+1]
                    third  = keys[i+2]
                except IndexError as e:
                    #Il n'y a plus de 2 types
                    continue

                if first.suit == Pai.Suit.J:
                    #Les personnages ne peuvent pas être Junko
                    continue

                if not (first.suit == second.suit and first.suit == third.suit):
                    #Différents types
                    continue

                if not ((second.num == first.num+1) and (third.num == first.num+2)):
                    #Pas un numéro de série
                    continue

                if pais[second] >= 1 and pais[third] >= 1:
                    pais[first]  -= 1
                    pais[second] -= 1
                    pais[third]  -= 1
                    return (first, second, third)

        return None

    def search_kohtu(pais):
        u'''Trouvez la gravure'''
        for j in range(length):
            if pais[keys[j]] >= 3:
                pais[keys[j]] -= 3
                return (keys[j], keys[j], keys[j])

        return None

    #Sept paires
    tmp = pais.copy()
    ret = check_chitoitsu(tmp)
    if ret:
        return [ ret ]

    #Kokushi Musou
    tmp = pais.copy()
    ret = check_kokushimusou(tmp)
    if ret:
        return [ ret ]

    #Forme basique
    candidate = []
    for i in range(length):
        #Trouvez la tête
        if not (pais[keys[i]] >= 2):
            continue

        tmp = pais.copy()
        atama = (keys[i], keys[i])
        tmp[keys[i]] -= 2

        mentsu = []
        while True:
            #print tmp
            ret = search_syuntu(tmp) or search_kohtu(tmp)
            if ret is None:
                ret = search_kohtu(tmp) or search_syuntu(tmp)
                if ret is None:
                    #Je ne peux pas faire Junko ou Kiko
                    break
            mentsu.append(ret)

        #print atama, mentsu, tmp
        if len(mentsu) == 4:
            #4 faces 1 tête de moineau
            candidate.append( (atama, mentsu) )

    return candidate

Chèque de Tenwa

Ce serait bien s'il avait la forme d'un agari par l'arrangement des parents, alors ce serait comme ça.

mj.py



def check_tenho():
    for cnt in (x for x in itertools.count()):
        yama = Yama()
        oya, _, _, _ = yama.haipai()
        ret = check_hohra(oya)
        if ret:
            print cnt
            oya.show()
            for atama, mentsu in ret:
                print atama, mentsu
            break

if __name__ == '__main__':
    #Essayez environ 100 fois
    for x in range(100):
        check_tenho()

Et le résultat est

S'il ne sort pas même après avoir essayé 600 000 fois, il ne sort pas, et s'il sort, il sort environ 20 000 fois.

Encore 20 000 fois ... ou __ Si vous ne pensez pas __ Peut-être que quelque chose est paralysé.

Eh bien, à mesure que le nombre de procès augmente, je pense qu'il va s'établir à 330 000 fois selon la loi des grands nombres. peut être. Cela prend du temps et c'est ennuyeux, donc je n'ai essayé qu'une dizaine de fois. J'étais satisfait quand je l'ai écrit.

Si vous voulez faire la distinction entre les rôles et l'attente, vous devez probablement le réécrire ...

prime

Cela pour le débogage.

mj.py


    class Debug:
        u'''for debug'''

        TEST_TEHAIS = [
            [2, 3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7],
            [0, 0, 8, 8, 13, 13, 20, 20, 25, 25, 29, 29, 31, 31],      #Quand
            [0, 8, 9, 17, 18, 26, 27, 28, 29, 30, 31, 32, 33, 9],      #Grinçant
            [33, 33, 33, 32, 32, 32, 31, 31, 31, 0, 0, 0, 2, 2],    #Daisangen
            [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 1],      #Churenpoto
            [19, 19, 20, 20, 21, 21, 23, 23, 23, 25, 25, 32, 32, 32],      #Ryu-so
            [0, 1, 2, 3, 4, 5, 6, 7, 8, 5, 5, 0, 1, 2],      #Chinitsu Ittsu Epaco
        ]

        @classmethod
        def tehai_from_indexes(cls, indexes):
            assert(len(indexes) == 13 or len(indexes) == 14)
            return Tehai([ Pai.from_index(x) for x in indexes ])

        @classmethod
        def test_tehai(cls, idx = None):
            if not idx:
                #14 fait maison
                yama = Yama()
                return Tehai([ yama.tsumo() for x in range(14) ])
            else:
                return cls.tehai_from_indexes(cls.TEST_TEHAIS[idx])

Si tu es intéressé

Vérifiez-le de vos propres mains!

Recommended Posts

Discrimination de la forme agari du mahjong
La forme du tableau unidimensionnel de numpy était compliquée
Juger la finition du mahjong par l'optimisation des combinaisons
Le début de cif2cell
Le sens de soi
le zen de Python
L'histoire de sys.path.append ()
[Note] Contenu de la forme [0], forme [1], forme [2]
Générer cette forme du fond d'une bouteille pour animaux de compagnie
Comprendre Tensor (2): Shape
La vengeance des types: la vengeance des types
Aligner la version de chromedriver_binary
Grattage du résultat de "Schedule-kun"
10. Compter le nombre de lignes
L'histoire de la construction de Zabbix 4.4
Discrimination du formulaire d'attente de mahjong
Vers la retraite de Python2
Comparez les polices de jupyter-themes
Obtenez le nombre de chiffres
Expliquez le code de Tensorflow_in_ROS
Réutiliser les résultats du clustering
GoPiGo3 du vieil homme
Calculez le nombre de changements
Changer le thème de Jupyter
La popularité des langages de programmation
Changer le style de matplotlib
Visualisez la trajectoire de Hayabusa 2
À propos des composants de Luigi
Composants liés du graphique
Filtrer la sortie de tracemalloc
À propos des fonctionnalités de Python
Simulation du contenu du portefeuille
Le pouvoir des pandas: Python