[PYTHON] Diskriminierung der Mahjong-Warteform

Diskriminierung der Mahjong-Warteform

http://qiita.com/arc279/items/7894d582a882906b94c7 Fortsetzung davon.

Es war viel nerviger als ich erwartet hatte.

Nach einigem Ausprobieren wurde es eine miserable Sache. Das ist interessant, also werde ich es veröffentlichen, während ich den Testprozess verlasse.

Ich schreibe ziemlich grob, also würde ich mich freuen, wenn Sie mir sagen könnten, ob es seltsam ist.

Hier ist die Referenz. Da es sich nur um ein klares Farburteil handelt, habe ich von hier aus angefangen und es mit meiner Superinterpretation einiges umgeschrieben. http://d.hatena.ne.jp/staebchen/20100403/1270256158

Ich habe den Verschluss und den Generator aufgebraucht Wenn Sie versuchen, es mit etwas anderem als Python umzuschreiben, kann es schwierig sein, aber das ist in Ordnung.

Die Grundlagen

Es ist das gleiche wie vor den Grundlagen, aber ich habe es so wie es ist überarbeitet, also werde ich alles noch einmal sagen.

Ich persönlich mag es nicht, Code wie # gist nach draußen zu stellen.

Ich hoffe es ist an einem Ort abgeschlossen.

mj2.py


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

import itertools
import random

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

    class TsumoDoesNotRemain(Exception):
        u'''Nur der König ist noch übrig'''
        pass

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

    def tsumo(self):
        u'''Eigenständigkeit'''
        if len(self) <= self.WANPAI_NUM:
            raise self.TsumoDoesNotRemain

        return self.pop(0)

    def wanpai(self):
        u'''König'''
        return self[-self.WANPAI_NUM:]

    def haipai(self):
        u'''Verteilung'''
        tehais = [ Tehai(), Tehai(), Tehai(), Tehai() ] #Osten(Elternteil)Südwesten Nord

        # 4*3 Runden
        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 +Alle Arten von Zeichen
    NUM_OF_EACH_KIND = 4         #4 Blatt pro Typ
    NUM_OF_EACH_NUMBER_PAIS = 9  # M/P/Die Anzahl von S ist 1..Bis zu 9

    class Suit:
        M = 0   #Mann
        P = 1   #Tube
        S = 2   #Messen
        J = 3   #Charakter

        NAMES = {
            M: u"Mann",
            P: u"Tube",
            S: u"Messen",
            J: u" ",
        }

    class Num:
        NAMES = {
            1: u"einer",
            2: u"zwei",
            3: u"drei",
            4: u"vier",
            5: u"Fünf",
            6: u"Sechs",
            7: u"Sieben",
            8: u"Acht",
            9: u"Neun",
        }

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

        NAMES = {
            E:   u"Osten",
            S:   u"Süden",
            W:   u"Westen",
            N:   u"Norden",
            HAK: u"Weiß",
            HAT: u"Abwehrmittel",
            CHU: u"Während ~",
        }

    @classmethod
    def all(cls):
        u'''Alle Fliesen'''
        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'''Ob es die nächste Nummer ist'''
        if self.suit != self.Suit.J: #Kein Brief
            if self.suit == other.suit: #Die gleiche Art von Fliesen
                if other.num == (self.num + index): #Ordnungsnummer
                    return True
        return False
        
    def is_prev(self, other, index=1):
        u'''Ob es die vorherige Nummer ist'''
        return self.is_next(other, -index)

    @classmethod
    def is_syuntsu(cls, first, second, third):
        u'''Ob es Junko ist'''
        #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))    #Taple-Anzeige
        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'''Vom Index abrufen'''
        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'''Holen Sie sich vom Namen'''
        for x in cls.all():
            if name == repr(x):
                return x
        return None

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

    @staticmethod
    def sorter(a, b):
        u'''Wie macht man'''
        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'''{牌 Samen:Anzahl der Blätter}Aggregat in Form von'''
        hash = { x[0]: len(list(x[1])) for x in itertools.groupby(tehai.rihai()) }
        #Geben Sie den Schlüssel (sortierte Kacheln) zusammen zurück
        return hash, sorted(hash.keys(), cmp=cls.sorter)

    def show(self):
        u'''Anzeige in leicht lesbarer Form'''
        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'''Finde Junko
Das Argument ist aggregiert()Übergeben Sie in der gleichen Form wie der Rückgabewert von.'''
        for i in range( len(keys)-2 ):   #Die letzten 2 Blätter müssen nicht überprüft werden
            tmp = pais.copy()
            first = keys[i]
            if tmp[first] >= 1:
                try:
                    second = keys[i+1]
                    third  = keys[i+2]
                except IndexError as e:
                    #Es gibt keine verbleibenden 2 Typen
                    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 gefunden,Verbleibende Fliesen
                    yield (first, second, third), tmp

    @classmethod
    def search_kohtu(cls, pais, keys):
        u'''Finde die Gravur
Das Argument ist aggregiert()Übergeben Sie in der gleichen Form wie der Rückgabewert von.'''
        for i, p in enumerate(keys):
            tmp = pais.copy()
            if tmp[p] >= 3:
                tmp[p] -= 3
                #Gravur gefunden,Verbleibende Fliesen
                yield (p, p, p), tmp

Warten auf Scheck

Es war eine schreckliche Sache, in gutem Zustand zu schreiben. Aber nein. Es ist ärgerlich, es nicht mehr zu reparieren.

Es könnte ein wenig verdächtig sein, wie auf 9 Gesichter zu warten. Ich kann es nicht richtig überprüfen, weil es zu viele Arten des Wartens gibt ...

Es ist nur eine Warteform und es ist kein Rollenurteil enthalten.

Außerdem bin ich erschöpft und kann es kaum erwarten, Shichitoko und Kokushi Musou zu sehen. Nun, ich denke, es kann mit ein wenig Sorgfalt getan werden, um den Agari-Typ zu unterscheiden.

mj2.py


def check_tenpai(tehai):
    u'''Überprüfen Sie die Form des Gehörs'''
    # TODO:Ich habe auf Shichitoko und Kokushi Musou gewartet und nicht nachgesehen
    assert(len(tehai) == 13)

    # (Atama,Gesicht,Warten)Form von
    candidate = set()

    def check_machi(mentsu, tartsu):
        u'''Untersuchen Sie die Form des Wartens'''
        assert(len(mentsu) == 3)

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

        def check_tanki():
            u'''Einzelpferd warten Scheck'''
            for i, p in enumerate(keys):
                tmp = tartsu.copy()
                if tmp[p] == 3:
                    #Das verbleibende Gesicht ist eingraviert
                    assert(len(tmp) == 2)
                    tmp[p] -= 3
                    tanki = { pai: num for pai, num in tmp.items() if num > 0 }.keys()
                    #Tauche ins Gesicht
                    ins = tuple( sorted(mentsu + [(p, p, p)]) )
                    candidate.add( ((), ins, tuple(tanki)) )
                else:
                    #Das verbleibende Gesicht ist 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()
                    #Tauche ins Gesicht
                    ins = tuple( sorted(mentsu + [(first, second, third)]) )
                    candidate.add( ((), ins, tuple(tanki)) )

        def check_non_tanki():
            u'''Wartecheck außer einem einzelnen Pferd'''
            for i, p in enumerate(keys):
                tmp = tartsu.copy()

                #Sparrow Head Check
                if not tmp[p] >= 2:
                    continue
                tmp[p] -= 2
                atama = (p, p)

                for j, q in enumerate(keys):
                    #Doppelseitig
                    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

                    #Passend zu
                    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

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

        check_tanki()
        check_non_tanki()

    #Suche nach 3 Gesichtern
    pais, keys = Tehai.aggregate(tehai)
    #print pais, keys

    if True:
        #Ich frage mich, ob es rekursiv gemacht wird
        def search_mentsu(depth, proc):
            searchers = [Tehai.search_syuntsu, Tehai.search_kohtu]
            #Junko/Suche nach Gravur
            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:
        #Wenn Sie so solide schreiben
        searchers = [Tehai.search_syuntsu, Tehai.search_kohtu]
        for p1 in searchers:
            for p2 in searchers:
                for p3 in searchers:
                    #Anwenden
                    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]
                                #Verbleibende Fliesen
                                tartsu = { x[0]:x[1] for x in a3.items() if x[1] > 0 }
                                check_machi(mentsu, tartsu)

    return candidate

Prüfung

Das zum Debuggen.

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],      #Wann
            [0, 8, 9, 17, 18, 26, 27, 28, 29, 30, 31, 32, 33, 9],      #Quietschend
            [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],      #Echtes Churenpoto
            [1, 2, 3, 4, 5, 5, 5, 6, 20, 20, 21, 22, 23],  #Kan-chan Shabo(Junko ist verschlungen)
            [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'''Abschlussprüfung'''
            return cls.tehai_from_indexes(cls.TEST_HOHRA[idx])

        @classmethod
        def test_tenpai(cls, idx = 0):
            u'''Formtest hören'''
            return cls.tehai_from_indexes(cls.TEST_TENPAI[idx])

        @classmethod
        def gen_tehai(cls, num = 14):
            u'''Treffen Sie eine entsprechende Vereinbarung'''
            assert(num == 13 or num == 14)
            yama = Yama()
            return Tehai([ yama.tsumo() for x in range(num) ])

        @classmethod
        def gen_hohra(cls):
            u'''Machen Sie eine geeignete Agari-Form'''
            tehai = Tehai()

            def gen_syuntsu():
                u'''Mach Junko'''
                first = Pai.from_index(random.choice(range(Pai.TOTAL_KIND_NUM)))
                if first.suit == Pai.Suit.J:
                    #Charaktere können nicht Junko sein
                    return None

                if first.num > 7:
                    # (7 8 9)Das Obige kann nicht Junko sein
                    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:
                    #Unzureichende Restnummer
                    return None

                return [first, second, third]

            def gen_kohtu():
                u'''Gravur machen'''
                pai = Pai.from_index(random.choice(range(Pai.TOTAL_KIND_NUM)))
                if tehai.count(pai) >= 2:
                    #Unzureichende Restnummer
                    return None
                return [pai, pai, pai]

            def gen_atama():
                u'''Machen Sie einen Spatzenkopf'''
                pai = Pai.from_index(random.choice(range(Pai.TOTAL_KIND_NUM)))
                if tehai.count(pai) >= 3:
                    #Unzureichende Restnummer
                    return None
                return [pai, pai]

            tehai.extend(gen_atama())   #Spatzenkopf

            #Wenn Junko und Kokuko die gleiche Erscheinungswahrscheinlichkeit haben, werden wir sie gewichten.
            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'''Stellen Sie eine geeignete Hörform her'''
            tehai = cls.gen_hohra()
            assert(len(tehai) == 14)
            #Ziehen Sie ein Stück aus der Agari-Form heraus
            tehai.pop(random.randrange(len(tehai)))
            return tehai


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

        @classmethod
        def check_tenho(cls):
            u'''Tenwa überprüfen'''
            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'''Überprüfen Sie viel Warten'''
            for x in range(times):
                tehai = Debug.gen_tenpai()
                ret = check_tenpai(tehai.rihai())
                if not ret:
                    #Wenn ich hierher komme, bin ich nicht temperiert. Der Punkt ist eine Fehlfunktion. Das zu modifizierende Handwerk.
                    print oya
                    print [ Pai.from_name(repr(x)).index for x in oya ]
            print "complete."


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

Und das Ergebnis ist

Selbst wenn Sie viele Hörformen überprüfen, werden Sie sie vorerst wahrscheinlich nicht verpassen ... Die Frage ist, ob ich alle Wartezeiten richtig auflisten kann ...

Ich denke, es gibt Raum für Verbesserungen, weil ich viele verschwenderische Dinge tue.

Bitte lassen Sie mich wissen, wenn es irgendwelche Fehler gibt.

Als Referenz

Wenn Sie das Warten von echtem Kuren Baotou überprüfen, sieht es so aus.

Die erste Zeile ist ein Handwerk.

  1. und nachfolgende Zeilen "Warten auf ein Spatzenkopfgesicht" In Form von.
[Ichiman, Ichiman, Ichiman,Niman,Sanman,Shiman,Geh,Rokuman,Nanaman,Hachiman,Kuman,Kuman,Kuman]
() ((Ichiman, Ichiman, Ichiman), (Sanman,Shiman,Geh), (Rokuman,Nanaman,Hachiman), (Kuman, Kuman, Kuman)) (Niman,)
() ((Ichiman, Ichiman, Ichiman), (Niman,Sanman,Shiman), (Geh,Rokuman,Nanaman), (Kuman, Kuman, Kuman)) (Hachiman,)
(Kuman, Kuman) ((Ichiman,Niman,Sanman), (Shiman,Geh,Rokuman), (Nanaman,Hachiman, Kuman)) (Ichiman, Ichiman)
(Ichiman, Ichiman) ((Ichiman,Niman,Sanman), (Rokuman,Nanaman,Hachiman), (Kuman, Kuman, Kuman)) (Shiman,Geh)
(Kuman, Kuman) ((Ichiman, Ichiman, Ichiman), (Niman,Sanman,Shiman), (Geh,Rokuman,Nanaman)) (Hachiman, Kuman)
(Kuman, Kuman) ((Ichiman, Ichiman, Ichiman), (Shiman,Geh,Rokuman), (Nanaman,Hachiman, Kuman)) (Niman,Sanman)
(Kuman, Kuman) ((Ichiman, Ichiman, Ichiman), (Niman,Sanman,Shiman), (Nanaman,Hachiman, Kuman)) (Geh,Rokuman)
(Ichiman, Ichiman) ((Sanman,Shiman,Geh), (Rokuman,Nanaman,Hachiman), (Kuman, Kuman, Kuman)) (Ichiman,Niman)
(Ichiman, Ichiman) ((Ichiman,Niman,Sanman), (Shiman,Geh,Rokuman), (Nanaman,Hachiman,Kuman)) (Kuman,Kuman)
() ((Ichiman, Ichiman, Ichiman), (Niman,Sanman,Shiman), (Rokuman,Nanaman,Hachiman), (Kuman, Kuman, Kuman)) (Geh,)
(Ichiman, Ichiman) ((Ichiman,Niman,Sanman), (Shiman,Geh,Rokuman), (Kuman, Kuman, Kuman)) (Nanaman,Hachiman)

Passt das wirklich zu dir?

Recommended Posts

Diskriminierung der Mahjong-Warteform
Diskriminierung von Primzahlen
Diskriminierung der Agari-Form von Mahjong