Pasjansowy zegar czy zegarowy pasjans?

https://xpil.eu/tbs

Solitaire znają wszyscy[citation needed]. Wykładały go nasze babcie i prababcie, był też dołączany przez Microsoft do większości wersji Windows.

Jest jednak wiele innych pasjansów. Jeden z nich - którego nauczyła mnie babcia - to Zegar.

Zegar jest - w odróżnieniu od Solitaire - pasjansem stuprocentowo deterministycznym. Innymi słowy wynik zależy wyłącznie od początkowego potasowania talii. Nie ma tu żadnego miejsca na myślenie, na decydowanie; po prostu robimy krok po kroku to, co każą nam karty. Mało twórcze, ale czasem człowiek potrzebuje[citation needed] mechanicznego, bezmyślnego przekładania kart. Taki reset.

Szczegóły samego pasjansa dobrze objaśnia Łukasz z portalu Przystanek Planszówka (KLIK), skąd zresztą ukradłem obrazek do dzisiejszego wpisu. Jeżeli ktoś nie zna Zegara, może sobie tam poczytać. Tutaj natomiast pokażę jak zrobić symulację Zegara w Pythonie aby sprawdzić jak często wychodzi, a jak często - nie wychodzi.

Zanim zacznę, powiem jeszcze, że za dzieciaka wykładałem Zegar dość często. Nie pamiętam ile razy mi się udało "wygrać", ale były to przypadki bardzo rzadkie.

OK, lecimy:

from random import sample
wygrane, ilosc_prob = 0, 100000

for proba in range(ilosc_prob):
    stos = sample(range(52), 52)  # tasujemy talię

    # zegar to lista list:
    # zewnętrzna lista ma 13 elementów (12 godzin plus stosik na środku)
    # każdy z 13 elementów jest listą 4-elementową (stos 4 kart)
    # przykładowo zegar[11][0] to pierwsza karta godziny 11
    zegar = []

    for i in range(13):  # rozkładamy zegar
        zegar.append(stos[i::13])

    krole = 0  # licznik króli (królów?)

    karta = 0  # startujemy od środka zegara
    while 1:  # gramy!
        karta = zegar[karta % 13].pop()  # bierzemy kolejną kartę
        if karta % 13 == 0:  # jeżeli trafiliśmy na króla
            krole += 1  # liczymy go
            if krole == 4:  # jeżeli to ostatni król
                break  # koniec pasjansa

    # jeżeli odkryliśmy wszystkie 12 godzin zegara, udało się
    if(sum(map(lambda x: x == [], zegar)) == 12):
        wygrane += 1

print(wygrane/ilosc_prob)

Przy liczbie prób 1M (lub więcej) wynik prawie zawsze wychodzi w okolicach 0.077. A więc 77 razy na 1000 pasjans wychodzi. Pozostałe 923 razy - nie wychodzi.

Najbardziej frustrujące jest, kiedy już prawie, prawie wyszedł, odkryliśmy 11 godzin, brakuje tylko jednej - i ostatni król pojawia się odrobinę za wcześnie.

No dobra. Skoro pasjans nie wychodzi za często, może spróbujmy policzyć ile średnio godzin uda się odkryć zanim pojawi się czwarty król?

from random import sample
wygrane, ilosc_prob, odkryte = 0, 100000, 0

for proba in range(ilosc_prob):
    stos = sample(range(52), 52)  # tasujemy talię

    # zegar to lista list:
    # zewnętrzna lista ma 13 elementów (12 godzin plus stosik na środku)
    # każdy z 13 elementów jest listą 4-elementową (stos 4 kart)
    # przykładowo zegar[11][0] to pierwsza karta godziny 11
    zegar = []

    for i in range(13):  # rozkładamy zegar
        zegar.append(stos[i::13])

    krole = 0  # licznik króli (królów?)

    karta = 0  # startujemy od środka zegara
    while 1:  # gramy!
        karta = zegar[karta % 13].pop()  # bierzemy kolejną kartę
        if karta % 13 == 0:  # jeżeli trafiliśmy na króla
            krole += 1  # liczymy go
            if krole == 4:  # jeżeli to ostatni król
                break  # koniec pasjansa

    # zliczamy ilość odkrytych godzin
    odkryte += sum(map(lambda x: x == [], zegar))

print(odkryte/ilosc_prob)

Modyfikacja jest niewielka: zamiast zliczać wygrane pasjanse, po każdym pasjansie zliczamy ilość odkrytych godzin.

Ile razy bym tego nie uruchamiał, zawsze wychodzi mi około 6.99. Czyli wykładając Zegar możemy się spodziewać, że odkryjemy siedem godzin zanim trafimy na ostatniego króla.

Wyjaśnienie sposobu liczenia odkrytych godzin:

Operator map działa tak, że wywołuje zadaną funkcję dla każdego elementu zadanej listy i zwraca listę wyników. W naszym przypadku listą jest zegar po zakończeniu pasjansa, a funkcją jest sprawdzenie, czy element listy jest sam w sobie pustą listą (x == []). Na końcu sumujemy wynik. Operator porównania zwraca True / False, które są traktowane przez sum jako 1 / 0.

https://xpil.eu/tbs

Leave a Comment

Komentarze mile widziane.

Jeżeli chcesz do komentarza wstawić kod, użyj składni:
[code]
tutaj wstaw swój kod
[/code]

Jeżeli zrobisz literówkę lub zmienisz zdanie, możesz edytować komentarz po jego zatwierdzeniu.