2025-01-06

Czas Pythona: Bajgle #2

Dziś kontynuujemy mikro serię artykułów o grze Bajgle w Python. Dokładniej będziemy dalej pisać skrypt ją realizujący, jeśli jeszcze nie czytałeś, to zapraszam do poprzedniego odcinka. Zatem dziś kontynuujemy Bajgle, czyli grę polegającą na dedukcji liczby. W ostatnim artykule skończyliśmy na 'podpętli' w pętli głównej. Dokładniej mieliśmy odczytywać odpowiedź gracza i zabezpieczyć się przed błędnymi danymi.

Obsługa błędnych danych

Zróbmy to zatem. Zacznijmy od pętli, która będzie się wykonywała, dopóty dopóki prawdą będzie prosty fakt. Mianowicie jeśli dane są nieprawidłowe to powtarzamy odczyt odpowiedzi.

obsługa błędnych danych w grze Bajgle w Pythonie

Powyższy kod, to kod głównej pętli while, którą pisaliśmy ostatnio. Natomiast widzimy tutaj nowy kod. Mamy pętlę wewnętrzną while. Pętla powtarza się, dopóki długość zmiennej guess różni się od stałej NUM_DIGITS lub zmienna guess nie przechowuje liczby. Czyli jeśli gracz wpisał za dużo lub mało znaków, albo wpisał coś innego niż liczba - wtedy powtarzamy pętlę. W pętli na razie wyświetlamy komunikat "zle dane", a następnie czekamy na wprowadzenie jakiejś wartości i wciśnięcie enter. Na końcu widzimy też dwie instrukcje breake - jest to tymczasowe rozwiązanie przeciwdziałające powtarzaniu się pętli w nieskończoność.

Podstawowe działanie i profesjonalne komunikaty w grze Bajgle

Powinniśmy trochę poważniej podejść do komunikacji z graczem. Na początek bądźmy bardziej profesjonalnie przekształćmy komunikat tak by mówił, która to próba. Zatem zmienimy linię z printem.

wewnętrzna pętla while gry Bajgle w Pythonie odpowiedzialna za obsługę wprowadzania danych.

Jak widzimy od razu lepiej. Używamy format do wstawienia w ciągu znaków informacji, znajdującej się w zmiennej numGuesses, która to próba. Następnie jeśli to co wpisze gracz będzie zbyt się różnić od kryteriów jakie przyjmujemy, to pętla powtórzy instrukcje i wyświetli informacje, że to ta sama próba. Zatem też poprosi ponownie o podanie strzału.

Jeśli jednak wartość będzie zgodna z założeniami, to warunek pętli zostaje przerwany, a program idzie dalej. Napotyka, na razie, nasze break'i więc kończy się całkowicie.

Świat po break w grze Bajgle

Teraz musimy jakoś sprawdzać to co wpisał użytkownik by móc dać mu wskazówki czy jest blisko celu czy daleko. W tym celu utworzymy nową funkcję, która będzie zwrać łańcuch znaków, który będzie wyścietlany użytkownikowi w konkretnej próbie.

linia wywołująca funkcję sprawdzającą i dawającą wskazówki czy jesteśmy blisko odgadnięcia czy nie

Zaczynamy więc od tego, że napiszemy linijkę, która będzie wywoływać tą funkcję i zapisywać zwrócony string. Oczywiście taka faunkcja potrzebuje wiedzieć jaka jest liczba do zgadnięcia i jaką użytkownik podał, zatem podajemy je jako argumeny. Natomiast większość edytorów zaprotestuje, bo nie ma takiej funkcji. Dlatego musimy ją zdefiniować.

Jak widzimy powyżej od razu metodą eliminacji, upewniamy się, że użytkownik nie zgadł. Jeśli nie to możemy zacząć zabawę i utworzyć listę. Każdy z kolejnych zanków z guess będziemy sprawdzać osobno w pętli i porównywać z kolejnym znakiem z secretNum. Wynik każdego z porównań będzie przechowywany w liście clues.

ciało funkcji odpowiedzialnej za generowanie wskazówek czy gracz odgadł liczbę jaką pomyślał komputer - gra Bajgle Python

Można zauważyć, że dokładnie tak jak mówiłem, w pętli sprawdzam przypadki dla Femi - czyli czy znak na pozycji aktualnie sprawdzanej jest równy odpowiadającemu znakowi w secretNum. Natomiast dla Piko jest sprawdzane czy aktualny znak sprawdzany jest w ogóle w całym secretNum. Pętla powtarza się tyle razy ile jest znaków w guess. Po pętli jest sprawdzana długość lisity clues - jeśli będzie zerowa, znaczy to, że w guess nie ma znaków z secretNum - czyli zwrócimy napis 'Bajgle'. Jeśli natomiast lista nie jest pusta, wtedy zamieniamy całą lsitę na ciąg znaków i zwracamy. Metoda return zastosowana na ciągu znaków ze spacją pozwala połączyć wszystkie elementy listy w jeden ciąg gdzie każdy element będzie oddzielony spacją od innych.

Kontrolowanie pętli głównej Bajgle w Python

Na tym etapie nasza gra przyjmuje odpowiedzi od gracza i wyświetla wskazówki, ale nawet, gdy gracz zgadnie nie kończy się. Po prostu skrypt pyta o liczbę i zwraca wskazówki w nieskończoność. Zatem powinniśmy, prócz wyświetlania zmiennej clues, zwiększyć też licznik prób numGuesses.

zwiększanie licznika pętli w grze Bajgle, co pozwala zakończyć grę.

W tej chwili zwięszkamy zmienną numGuesses, co pozwala zakończyć wewnętrzną pętlę, ale główna pętla while dalej trwa, zatem po osiągnięciu wartości 10 sprawi, że zaczniemy automatycznie od nowa. Nie jest to efekt jakiego będziemy oczekiwać dlatego poeinniśmy sprawdzić czy guess jest równe secretNum i od razu zakończyć w tym wypadku pętlę wewnątrz głównej. Jeśli natomiast aktualna próba jest większa od MAX_GUESSES to powinniśm poinformować gracza o wykorzystaniu wszystkich prób.

warunki sprawdzające czy gracz odgadł lub wykorzystał wszystkie próby

Powyżej właśnie sprawdzamy czy gracz zgadł, jeśli tak to kończymy zgadywanie. Jeśli wykorzystał zać wszystkie próby, co sprawdzi drugi if to wyświetlamy informacje o tym i o tym jaka była zgadywana liczba.

Teraz skoro już obsłużyliśmy dwa przypadku, w których kończymy pętlę wewnętrzną, czas zająć się tym co dzieje się po jej zakończeniu. W tym momencie wracamy do jej początku i losujemy znowu liczbę. Później zaczynamy grę. Dajmy użytkownikowi wybór czy chce grać dalej.

zakończenie gry i głównej pętli

Gra Bajgle kończy się tutaj pytaniem o to czy gracz chce powtórzyć rozgrywkę. Jeśli to co wpisze, po przekształceniu na małe litery, nie będzie zaczynało się od znaku 't' to zakończymy pętlę główną. Wyświetlimy jeszcze 'Dziękuję za grę'. W innym przypadku pętla wróci do początku.

I tutaj dochodzimy do końca, głównym zajęciem będzie testowanie każdego przypadku, gdy wygrasz, gdy będziesz odpowiadał aż skończą się próby. Jeśli zachownie skryptu będzie inne niż oczekiwane spróbuj sam to naprawić. Jeśli nie będziesz mógł znaleźć błędu poniżej zostawiam kod gotowej gry.

import random 

NUM_DIGITS = 3
MAX_GUESSES = 10

def main():
    
    print('''Witaj w logicznej grze na dedukcję "Bajgle"
Mam na myśli {}-cyfrową liczbę, w której nie powtarza się żadna z cyft. 
Spróbuj ją odgadnąć. Oto wskazówki:
    Gdy mówię:    To oznacza:
    Piko          Jedna cyfra jest poprawna, ale jest na niewłaściwej pozycji.
    Femi          Jedna cyfra jest poprawna i jest w odpowiednim miejscu.
    Bajgle        Nie ma poprawnych cyfr.
          
Na przykład, jeśli tajna liczbato 248, a Ty podasz liczbę 843, wskazówka będzie brzmieć: Femi Piko'''.format(NUM_DIGITS))
    
    while True:
        secretNum = getSecretNum()
        print('Mam na myśli liczbę')
        print('Masz {} prób by odgadnąć jaka to liczba.'.format(MAX_GUESSES))
        
        numGuesses = 1
        while numGuesses <= MAX_GUESSES:
            guess = ''
            while len(guess) != NUM_DIGITS or not guess.isdecimal():
                print('Próba #{}:'.format(numGuesses))
                guess = input('> ')
            
            clues = getClues(guess, secretNum)
            print(clues)
            numGuesses += 1
            
            if guess == secretNum:
                break
            if numGuesses > MAX_GUESSES:
                print('Wykorzystałeś wszystkie próby.')
                print('Prawidłowa odpowiedź to: {}'.format(secretNum))
        
        print('Czy chcesz zagrać jeszcze raz? (tak lub nie)')
        if not input('> ').lower().startswith('t'):
            break
    print('Dziękuję za grę')

def getSecretNum():
    numbers = list('0123456789')
    random.shuffle(numbers)
    
    secretNum = ''
    for i in range(NUM_DIGITS):
        secretNum += str(numbers[i])
    return secretNum

def getClues(guess, secretNum):
    if guess == secretNum:
        return 'Udało się'
    
    clues = []
    
    for i in range(len(guess)):
        if guess[i] == secretNum[i]:
            clues.append('Femi')
        elif guess[i] in secretNum:
            clues.append('Piko')
    
    if len(clues) == 0:
        return 'Bajgle'
    else:
        return ' '.join(clues)

if __name__ == '__main__':
    main()

I to wszystko na dziś! Jak poszło Wam implementowanie gry? Podzielcie się swoimi wynikami w komentarzach i dajcie znać, jakich funkcji lub projektów chcecie się nauczyć w przyszłości!

Dodaj komentarz

linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram