[PYTHON] Discrimination du formulaire d'attente de mahjong

Discrimination du formulaire d'attente de mahjong

http://qiita.com/arc279/items/7894d582a882906b94c7 Continuation de cela.

C'était beaucoup plus ennuyeux que prévu.

Après quelques essais et erreurs, c'est devenu une chose misérable. C'est intéressant, donc je le posterai en quittant le procès.

J'écris assez grossièrement, donc je serais heureux si vous pouviez me dire si c'est étrange.

Voici la référence. Comme il ne s'agit que d'un jugement de couleur clair, je suis parti d'ici et je l'ai réécrit un peu avec ma super interprétation. http://d.hatena.ne.jp/staebchen/20100403/1270256158

J'ai utilisé la fermeture et le générateur Si vous essayez de le réécrire avec autre chose que python, cela peut être difficile, mais ce n'est pas grave.

Les bases

C'est le même que celui avant les bases, mais je l'ai retravaillé tel quel, donc je vais tout remettre.

Personnellement, je n'aime pas mettre du code à l'extérieur comme # gist.

J'espère qu'il est terminé en un seul endroit.

mj2.py


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

import itertools
import random

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):
        u'''Roi'''
        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 Tsuhai:
        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 all(cls):
        u'''Tous les carreaux'''
        return [cls(suit, num)
                for suit in cls.Suit.NAMES
                for num in range(1, cls.NUM_OF_EACH_NUMBER_PAIS+1)
                if suit != cls.Suit.J
            ] + [ cls(cls.Suit.J, num) for num in cls.Tsuhai.NAMES.keys() ]

    @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.Tsuhai.E),
            cls(cls.Suit.J, cls.Tsuhai.S),
            cls(cls.Suit.J, cls.Tsuhai.W),
            cls(cls.Suit.J, cls.Tsuhai.N),
            cls(cls.Suit.J, cls.Tsuhai.HAK),
            cls(cls.Suit.J, cls.Tsuhai.HAT),
            cls(cls.Suit.J, cls.Tsuhai.CHU),
        ]

    @classmethod
    def chuchanpai(cls):
        u'''Nakahari'''
        yaochupai = cls.yaochupai()
        return [ x for x in cls.all() if x not in yaochupai ]

    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 - 1

    def is_next(self, other, index=1):
        u'''Que ce soit le prochain numéro'''
        if self.suit != self.Suit.J: #Pas une lettre
            if self.suit == other.suit: #Le même type de tuile
                if other.num == (self.num + index): #Numéro de série
                    return True
        return False
        
    def is_prev(self, other, index=1):
        u'''Que ce soit le numéro précédent'''
        return self.is_next(other, -index)

    @classmethod
    def is_syuntsu(cls, first, second, third):
        u'''Que ce soit Junko'''
        #return second.is_prev(first) and second.is_next(third)
        return first.is_next(second) and first.is_next(third, 2)

    def __repr__(self):
        #return str((self.suit, self.num))    #Affichage Taple
        if self.suit == self.Suit.J:
            return self.Tsuhai.NAMES[self.num].encode('utf-8')
        else:
            return (self.Num.NAMES[self.num] + self.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):
        u'''Obtenir de l'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)

    @classmethod
    def from_name(cls, name):
        u'''Obtenir du nom'''
        for x in cls.all():
            if name == repr(x):
                return x
        return None

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

    @classmethod
    def aggregate(cls, tehai):
        u'''{牌 graine:Nombre de feuilles}Agrégat sous forme de'''
        hash = { x[0]: len(list(x[1])) for x in itertools.groupby(tehai.rihai()) }
        #Renvoyer la clé (tuiles triées) ensemble
        return hash, sorted(hash.keys(), cmp=cls.sorter)

    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.Tsuhai.NAMES[pai.num] + u"|"
                line2 += u" |"

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

    @classmethod
    def search_syuntsu(cls, pais, keys):
        u'''Trouver Junko
L'argument est agrégé()Passez sous la même forme que la valeur de retour de.'''
        for i in range( len(keys)-2 ):   #Pas besoin de vérifier les 2 dernières feuilles
            tmp = pais.copy()
            first = keys[i]
            if tmp[first] >= 1:
                try:
                    second = keys[i+1]
                    third  = keys[i+2]
                except IndexError as e:
                    #Il n'y a pas 2 types restants
                    continue

                if not Pai.is_syuntsu(first, second, third):
                    continue

                if tmp[second] >= 1 and tmp[third] >= 1:
                    tmp[first]  -= 1
                    tmp[second] -= 1
                    tmp[third]  -= 1
                    #Junko trouvé,Tuiles restantes
                    yield (first, second, third), tmp

    @classmethod
    def search_kohtu(cls, pais, keys):
        u'''Trouvez la gravure
L'argument est agrégé()Passez sous la même forme que la valeur de retour de.'''
        for i, p in enumerate(keys):
            tmp = pais.copy()
            if tmp[p] >= 3:
                tmp[p] -= 3
                #Gravure trouvée,Tuiles restantes
                yield (p, p, p), tmp

Vérification en attente

C'était horrible d'écrire en bon état. Mais non. C'est ennuyeux de le réparer plus.

Attendre le 9e côté Kuren Baotou peut être un peu suspect. Je ne peux pas le vérifier correctement car il y a trop de types d'attente ...

C'est juste un formulaire d'attente et aucun jugement de rôle n'est inclus.

De plus, je suis épuisé, donc j'ai hâte de voir Shichitoko et Kokushi Musou. Eh bien, je pense que cela peut être fait avec un peu de soin pour distinguer le type Agari.

mj2.py


def check_tenpai(tehai):
    u'''Vérifiez la forme de l'audition'''
    # TODO:En attendant Shichitoko et Kokushi Musou je n'ai pas vérifié
    assert(len(tehai) == 13)

    # (Atama,visage,Attendre)Forme de
    candidate = set()

    def check_machi(mentsu, tartsu):
        u'''Examiner la forme de l'attente'''
        assert(len(mentsu) == 3)

        keys = sorted(tartsu.keys(), cmp=Tehai.sorter)
        #print mentsu, tartsu, keys

        def check_tanki():
            u'''Contrôle d'attente pour un seul cheval'''
            for i, p in enumerate(keys):
                tmp = tartsu.copy()
                if tmp[p] == 3:
                    #La face restante est gravée
                    assert(len(tmp) == 2)
                    tmp[p] -= 3
                    tanki = { pai: num for pai, num in tmp.items() if num > 0 }.keys()
                    #Plongez dans le visage
                    ins = tuple( sorted(mentsu + [(p, p, p)]) )
                    candidate.add( ((), ins, tuple(tanki)) )
                else:
                    #Le visage restant est Junko
                    first  = p
                    try:
                        second = keys[i+1]
                        third  = keys[i+2]
                    except IndexError as e:
                        continue

                    if not Pai.is_syuntsu(first, second, third):
                        continue

                    tmp[first]  -= 1
                    tmp[second] -= 1
                    tmp[third]  -= 1
                    tanki = { pai: num for pai, num in tmp.items() if num > 0 }.keys()
                    #Plongez dans le visage
                    ins = tuple( sorted(mentsu + [(first, second, third)]) )
                    candidate.add( ((), ins, tuple(tanki)) )

        def check_non_tanki():
            u'''Vérification en attente autre qu'un cheval seul'''
            for i, p in enumerate(keys):
                tmp = tartsu.copy()

                #Contrôle de la tête de moineau
                if not tmp[p] >= 2:
                    continue
                tmp[p] -= 2
                atama = (p, p)

                for j, q in enumerate(keys):
                    #Double face
                    try:
                        next = keys[j+1]
                        if q.is_next(next):
                            ins = tuple( sorted(mentsu) )
                            candidate.add( (atama, ins, (q, next) ) )
                            break
                    except IndexError as e:
                        pass

                    #Raccord
                    try:
                        next = keys[j+1]
                        if q.is_next(next, 2):
                            ins = tuple( sorted(mentsu) )
                            candidate.add( (atama, ins, (q, next) ) )
                            break
                    except IndexError as e:
                        pass

                    #Alors allez
                    if tmp[q] >= 2:
                        ins = tuple( sorted(mentsu) )
                        candidate.add( (atama, ins, (q, q) ) )
                        break

        check_tanki()
        check_non_tanki()

    #Rechercher 3 visages
    pais, keys = Tehai.aggregate(tehai)
    #print pais, keys

    if True:
        #Je me demande si cela se fait récursivement
        def search_mentsu(depth, proc):
            searchers = [Tehai.search_syuntsu, Tehai.search_kohtu]
            #Junko/Recherche de gravure
            def inner(pais, mentsu = [], nest = 0):
                if nest < depth:
                    for search in searchers:
                        for m, a in search(pais, keys):
                            inner(a, mentsu + [m], nest+1)
                else:
                    proc(mentsu, pais)
            inner(pais)

        search_mentsu(3, lambda mentsu, pais:
                check_machi(mentsu, { x[0]:x[1] for x in pais.items() if x[1] > 0 })
            )
    else:
        #Si tu écris solidement comme ça
        searchers = [Tehai.search_syuntsu, Tehai.search_kohtu]
        for p1 in searchers:
            for p2 in searchers:
                for p3 in searchers:
                    #Application
                    for m1, a1 in p1(pais, keys):
                        for m2, a2 in p2(a1, keys):
                            for m3, a3 in p3(a2, keys):
                                mentsu = [m1, m2, m3]
                                #Tuiles restantes
                                tartsu = { x[0]:x[1] for x in a3.items() if x[1] > 0 }
                                check_machi(mentsu, tartsu)

    return candidate

Tester

Cela pour le débogage.

mj2.py


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

        TEST_HOHRA = [
            [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
        ]

        TEST_TENPAI = [
            [0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8],      #Véritable Churenpoto
            [1, 2, 3, 4, 5, 5, 5, 6, 20, 20, 21, 22, 23],  #Kan-chan Shabo(Junko est enlacé)
            [13, 14, 15, 18, 19, 19, 20, 21, 24, 24, 24, 31, 31],   #Ryanmen Kan-chan
            [25, 25, 25, 1, 2, 3, 11, 12, 13, 11, 23, 23, 23],  #Ryanmen Tanki
            [25, 25, 25, 1, 2, 3, 11, 12, 13, 11, 12, 23, 24],   #Ryanmen
            [1, 2, 3, 4, 4, 6, 7, 8, 9, 10, 11, 29, 29],    #Shabo
        ]

        @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_hohra(cls, idx = None):
            u'''Test d'achèvement'''
            return cls.tehai_from_indexes(cls.TEST_HOHRA[idx])

        @classmethod
        def test_tenpai(cls, idx = 0):
            u'''Test de forme d'écoute'''
            return cls.tehai_from_indexes(cls.TEST_TENPAI[idx])

        @classmethod
        def gen_tehai(cls, num = 14):
            u'''Faites un arrangement approprié'''
            assert(num == 13 or num == 14)
            yama = Yama()
            return Tehai([ yama.tsumo() for x in range(num) ])

        @classmethod
        def gen_hohra(cls):
            u'''Faire une forme d'agari appropriée'''
            tehai = Tehai()

            def gen_syuntsu():
                u'''Faire Junko'''
                first = Pai.from_index(random.choice(range(Pai.TOTAL_KIND_NUM)))
                if first.suit == Pai.Suit.J:
                    #Les personnages ne peuvent pas être Junko
                    return None

                if first.num > 7:
                    # (7 8 9)Ce qui précède ne peut pas être Junko
                    return None

                second = Pai(first.suit, first.num+1)
                third  = Pai(first.suit, first.num+2)

                if tehai.count(first) == 4 or tehai.count(second) == 4 or tehai.count(third) == 4:
                    #Nombre restant insuffisant
                    return None

                return [first, second, third]

            def gen_kohtu():
                u'''Faire de la gravure'''
                pai = Pai.from_index(random.choice(range(Pai.TOTAL_KIND_NUM)))
                if tehai.count(pai) >= 2:
                    #Nombre restant insuffisant
                    return None
                return [pai, pai, pai]

            def gen_atama():
                u'''Faire une tête de moineau'''
                pai = Pai.from_index(random.choice(range(Pai.TOTAL_KIND_NUM)))
                if tehai.count(pai) >= 3:
                    #Nombre restant insuffisant
                    return None
                return [pai, pai]

            tehai.extend(gen_atama())   #Tête de moineau

            #Si Junko et Kokuko ont la même probabilité d'apparition, nous les pondérerons.
            weighted_choices = [(gen_syuntsu, 3), (gen_kohtu, 1)]
            population = [val for val, cnt in weighted_choices for i in range(cnt)]
            while len(tehai) < 14:
                ret = random.choice(population)()
                if ret is not None:
                    tehai.extend(ret)
            return tehai

        @classmethod
        def gen_tenpai(cls):
            u'''Créez une forme d'écoute appropriée'''
            tehai = cls.gen_hohra()
            assert(len(tehai) == 14)
            #Retirez une pièce de la forme Agari
            tehai.pop(random.randrange(len(tehai)))
            return tehai


    class Test:
        u'''for test'''

        @classmethod
        def check_tenho(cls):
            u'''Chèque Tenwa'''
            import sys
            for cnt in (x for x in itertools.count()):
                print >>sys.stderr, cnt
                yama = Yama()
                oya, _, _, _ = yama.haipai()
                ret = check_hohra(oya)
                if ret:
                    print "---------------------------------------------"
                    print cnt
                    oya.show()
                    for atama, mentsu in ret:
                        print atama, mentsu
                    break

        @classmethod
        def check_machi(cls, times = 100):
            u'''Vérifiez beaucoup d'attente'''
            for x in range(times):
                tehai = Debug.gen_tenpai()
                ret = check_tenpai(tehai.rihai())
                if not ret:
                    #Quand je viens ici, je ne suis pas tempéré. Le point est un dysfonctionnement. L'artisanat à modifier.
                    print oya
                    print [ Pai.from_name(repr(x)).index for x in oya ]
            print "complete."


if __name__ == '__main__':
    Test.check_machi()

Et le résultat est

Pour le moment, même si vous cochez beaucoup de formes d'écoute, vous ne les manquerez probablement pas ... La question est de savoir si je peux lister correctement toutes les attentes ...

Je pense qu'il y a place à amélioration parce que je fais beaucoup de choses inutiles.

S'il vous plaît laissez-moi savoir s'il y a des bogues.

Pour votre référence

Si vous vérifiez l'attente du véritable Kuren Baotou, cela ressemble à ceci.

La première ligne est un artisanat.

2ème et suivantes lignes «En attendant un visage de tête de moineau» Sous la forme de.

[Ichiman, Ichiman, Ichiman,Niman,Sanman,Shiman,Allez mec,Rokuman,Nanaman,Hachiman,Kuman,Kuman,Kuman]
() ((Ichiman, Ichiman, Ichiman), (Sanman,Shiman,Allez mec), (Rokuman,Nanaman,Hachiman), (Kuman, Kuman, Kuman)) (Niman,)
() ((Ichiman, Ichiman, Ichiman), (Niman,Sanman,Shiman), (Allez mec,Rokuman,Nanaman), (Kuman, Kuman, Kuman)) (Hachiman,)
(Kuman, Kuman) ((Ichiman,Niman,Sanman), (Shiman,Allez mec,Rokuman), (Nanaman,Hachiman, Kuman)) (Ichiman, Ichiman)
(Ichiman, Ichiman) ((Ichiman,Niman,Sanman), (Rokuman,Nanaman,Hachiman), (Kuman, Kuman, Kuman)) (Shiman,Allez mec)
(Kuman, Kuman) ((Ichiman, Ichiman, Ichiman), (Niman,Sanman,Shiman), (Allez mec,Rokuman,Nanaman)) (Hachiman, Kuman)
(Kuman, Kuman) ((Ichiman, Ichiman, Ichiman), (Shiman,Allez mec,Rokuman), (Nanaman,Hachiman, Kuman)) (Niman,Sanman)
(Kuman, Kuman) ((Ichiman, Ichiman, Ichiman), (Niman,Sanman,Shiman), (Nanaman,Hachiman, Kuman)) (Allez mec,Rokuman)
(Ichiman, Ichiman) ((Sanman,Shiman,Allez mec), (Rokuman,Nanaman,Hachiman), (Kuman, Kuman, Kuman)) (Ichiman,Niman)
(Ichiman, Ichiman) ((Ichiman,Niman,Sanman), (Shiman,Allez mec,Rokuman), (Nanaman,Hachiman,Kuman)) (Kuman,Kuman)
() ((Ichiman, Ichiman, Ichiman), (Niman,Sanman,Shiman), (Rokuman,Nanaman,Hachiman), (Kuman, Kuman, Kuman)) (Allez mec,)
(Ichiman, Ichiman) ((Ichiman,Niman,Sanman), (Shiman,Allez mec,Rokuman), (Kuman, Kuman, Kuman)) (Nanaman,Hachiman)

Cela vous convient-il vraiment?

Recommended Posts

Discrimination du formulaire d'attente de mahjong
Discrimination des nombres premiers
Discrimination de la forme agari du mahjong