Moje początki - gra turniej wiedzy z książki Python dla każdego

0

Witam szanownych forumowiczów,
jakiś czas temu postanowiłem rozpocząć swoją przygodę z językiem python, aktualnie przerabiam książkę "Python dla każdego. Podstawy programowania", wyd. III. Dotychczas wykonałem wszystkie zadania z wyjątkiem jednego, które jak zauważyłem wymaga użycia algorytmu minmax i które tymczasowo odłożyłem, ale dziś nie o tym. W rozdziale 7, traktującym o plikach i wyjątkach znajduje się "Gra turniej wiedzy". W zamkniętym temacie archiwum znajduje się nawet pytanie na temat dokładnie tej gry, ja jednak chciałem zadać inne (być może banalne), ponieważ nie potrafię zrozumieć dwóch linijek znajdujący się w funkcji next_block (taki przykładowy blok z pliku.txt wkleiłem na samym dole):
** if correct:
correct = correct[0]**
Myślę sobie - przecież poprawna odpowiedź jest tutaj albo łańcuchem znaków (jeżeli dobrze rozumiem) pobieranym z pliku kwiz.txt, a zatem correct[0] to będzie w tym wypadku po prostu pierwsza litera poprawnej odpowiedzi. A jeżeli nie i correct to liczba (nr odpowiedzi wpisywany przez użytkownika) - to w ogóle wyskakuje błąd "TypeError: 'int' object is not subscriptable" - co wyszło mi z interpretatora online. Spróbowałem wykasować te dwie linijki, ale wtedy program jakąkolwiek odpowiedź użytkownika odczytuje jako złą. Spróbowałem zmienić liczbę w nawiasach kwadratowych na 1 czy 2 - podobnie. Jak krok po kroku rozumieć funkcję tych dwóch linijek kodu w całym programie?
Z góry proszę o wyrozumiałość i przepraszam za zawracanie głowy, zazwyczaj gdy program myśli szybciej ode mnie staram się korzystać ze strony pythontutor.com, która rozpakowuje wszystko krok po kroku, ale nie każdy rodzaj kodu da się tam sprawdzić :-)

> # gra turniej wiedzy
> # gra sprawdza wiedze ogolna ,odczytuje dane z pliku tekstowego
> 
> import sys
> 
> def open_file(file_name, mode):
>     try:
>         the_file = open(file_name, mode)
>     except IOError as e:
>         print('Nie można otworzyć pliku', file_name, 'Program zostanie zakończony.\n',e)
>         input('\n\nAby zakończyć program, nasićnij dowolny klawisz')
>         sys.exit()
>     else:
>         return the_file
> 
> def next_line(the_file):
>     #Zwraca kolejny wiersz pliku
>     line = the_file.readline()
>     line = line.replace('/', '\n')
>     return line
> 
> def next_block(the_file):
>     #zwraca kolejny blok danych z pliku kwiz
>     category = next_line(the_file)
> 
>     question = next_line(the_file)
> 
>     answers =[]
>     for i in range(4):
>         answers.append(next_line(the_file))
> 
>     correct = next_line(the_file)
>     if correct:
>         correct = correct[0]
> 
>     explanation = next_line(the_file)
> 
>     return category, question, answers, correct, explanation
> 
> def welcome(title):
>     print('\t\t Witaj w turnieju wiedzy!\n')
>     print('\t\t', title, '\n')
> 
> def main():
>     trivia_file = open_file('kwiz.txt', 'r')
>     title = next_line(trivia_file)
>     welcome(title)
>     score = 0
> 
>     #Pobiera pierwszy blok
>     category, question, answers, correct, explanation = next_block(trivia_file)
>     while category: #zadaje pytanie
>         print(category)
>         print(question)
>         for i in range(4):
>             print('\t', i + 1, '-', answers[i])
>         answer = input('Jaka jest Twoja odpowiedź?: ') # Pobiera odpowiedź gracza
>         if answer == correct:
>             print('\nOdpowiedź prawidłowa!', end=' ')
>             score += 1
>         else:
>             print('\nOdpowiedź niepoprawna', end=' ')
>             print(explanation)
>             print('Wynik:', score, '\n\n')
> 
>             category, question, answers, correct, explanation = next_block(trivia_file)
> 
>     trivia_file.close()
>     print('To bylo ostatnie pytanie')
>     print('Twoj końcowy wynik wynosi', score)
> 
> main()
> input('Aby zakończyć wciśnij ENTER')

Odcinek nie do odrzucenia
Plany na resztę życia
Za działalność mafijną skazano Cię na "kopę lat z hakiem" w więzieniu. /To oznacza, że za kratkami spędzisz:
co najmniej 15 lat
co najmniej 25 lat
co najmniej 50 lat
co najmniej 60 lat
4
Kopa to sześćdziesiąt sztuk.

2

Naucz się czym prędzej korzystać z debbugera. Jeżeli korzystasz z jakiegoś IDE np. PyCharm to masz tam wbudowany debbuger i powinieneś z niego korzystać. Jeżeli natomiast nie korzystasz z IDE i programujesz w notatniku czy innym edytorze bez debbugera to naucz się korzystać z "pdb".

Żeby użyć pdb wystarczy przed linijką którą chcesz sprawdzić wstawić taki kod:

import pdb; pdb.set_trace()

Następnie gdy uruchomisz program powinien pojawić się znak zachęty "(pdb)" i tam wpisujesz sobie nazwę zmiennej, którą chcesz sprawdzić np. correct. Pdb w takim przypadku wyświetli jej zawartość.

Wracając do pytania. W tej linii powinieneś mieć w pliku numer poprawnej odpowiedzi np. 1. Polecenie correct[0] wyciąga pierwszy znak z linii czyli cyfrę oznaczającą numer poprawnej odpowiedzi.

1

** if correct:
correct = correct[0]**
Myślę sobie - przecież poprawna odpowiedź jest tutaj albo łańcuchem znaków (jeżeli dobrze rozumiem) pobieranym z pliku kwiz.txt, a zatem correct[0] to będzie w tym wypadku po prostu pierwsza litera poprawnej odpowiedzi. A jeżeli nie i correct to liczba (nr odpowiedzi wpisywany przez użytkownika) - to w ogóle wyskakuje błąd "TypeError: 'int' object is not subscriptable" - co wyszło mi z interpretatora online. Spróbowałem wykasować te dwie linijki, ale wtedy program jakąkolwiek odpowiedź użytkownika odczytuje jako złą. Spróbowałem zmienić liczbę w nawiasach kwadratowych na 1 czy 2 - podobnie. Jak krok po kroku rozumieć funkcję tych dwóch linijek kodu w całym programie?
Z góry proszę o wyrozumiałość i przepraszam za zawracanie głowy, zazwyczaj gdy program myśli szybciej ode mnie staram się korzystać ze strony pythontutor.com, która rozpakowuje wszystko krok po kroku, ale nie każdy rodzaj kodu da się tam sprawdzić :-)

Dokładnie jak napisał poprzednik. W tym miejscu porgram pobiera, kóra odpowiedź jest prawidlowa. Czyli w tym wypadku liczba 4. Jeden pojedynczy znak (char.). Też pamiętam to zadanie, ale tam nie było problemu. Masz dobrze go przepisane i masz dobry plik z testem (szczególnie tu mógłby być problem.)?

0

Wow, bardzo szanownym Panom dziękuję, szczególnie Panu, Panie Haskell, bardzo mi Pan pomógł. Używam "Pycharm community edition" na ubuntu, widziałem, że jest jakiś debugger i sądziłem, że jego działanie ogranicza się do pokazywania błędów typu zła indentacja czy brak dwukropka przy funkcji (jako taki czerwony kwadracik po prawej stronie linijki) - niezwłocznie muszę nauczyć się go lepiej obsługiwać. Niemniej, wkleiłem zaproponowany przez Pana kod dotyczący tego pdb i rozwiązało to mój problem. Cały kod turnieju wiedzy mi działał, po prostu nie rozumiałem sensu tego dokładnego przypisania, teraz widzę, że dla pierwszego przykładu (po ustawieniu tej linijki na correct = correct), correct po uruchomieniu pdb oznacza "4\n". Czyli odczytując pliki tekstowe, z automatu interpreter dodaje enter jako znak nowego wiersza :-) I dlatego musi być to dokładne przypisanie "correct = correct[0]". Dziwi mnie jednak pewna rzecz - otóż w takim układzie gdy wpisuję jako prawidłową odpowiedź "4\n" w dalszym ciągu program określa to jako niepoprawną odpowiedź - pomimo, że pdb tak właśnie odczytuje tą zmienną. Chyba, że taka jest specyfika znaku nowego wiersza?

2

Brak dwukropka pokazuje linter, a nie debugger. Wklejam poniżej link do krótkiego video o debuggerze, będziesz mógł lepiej analizować swój i nie tylko kod.

0

Dziękuję za ten link, z pewnością się przyda.

1

To zeby nie rzezbic nowego tematu - moj kod jest jeszcze prostszy a pytania niezbyt górnolotne:)


#Instrukcja dla gracza, press enter to start, parametry potrzebne do obslugi rozpoczecia gry
import random, sys, os
print('Instrukcja:\nGuessgame jest to gra w której musisz odgadnac liczbe zawarta miedzy 1 a 100.\nMasz na to 6 prob. \nAplikacja podpowie Ci czy liczba ta jest wyzsza czy nizsza od podanej przez Ciebie. Jesli sie nie uda - PRZEGRALES. Jesli sie uda - Wujek da ciasteczko:)\nP O W O D Z E N I A')
print('...\n'*3)
print('Aby zaczac wpisz "START" i nacisnij ENTER. Jesli chcesz wyjsc wpisz "KONIEC" i nacisnij ENTER')
print('NIE PROBUJ WPISAC NIC INNEGO!!!')

startorend = input()
if startorend == "START":
    print('Z A C Z Y N A M Y')
elif startorend == "KONIEC":
    print('Ale jestes ciapa ze nawet nie sprobujesz, do widzenia!!!')
    input("Press Any key to Exit...")
    sys.exit()
else:
    print('Ta gra nie jest dla dzieci, ktore nie potrafia przepisac "START" lub "KONIEC", obrazam sie do widzenia!')
    input("Press Any key to Exit...")
    sys.exit()

#wlasciwa apka, wybieram liczbe i informuje gracza
zgadnijliczbe = random.randint(1,100)
print('Wybralem juz moja liczbe, teraz sprobuj ja zgadnac...')

#prosze gracza o wsazanie liczby
for zgaduje in range(1,7):
        print('Zgaduj...')
        zgaduj =int(input())
        if zgaduj > zgadnijliczbe:
            print('Twoja liczba jest za wysoka')
        elif zgaduj < zgadnijliczbe:
            print('Twoja liczba jest za niska')
        else:
            break
if zgaduj == zgadnijliczbe:
    print('SUPERRRRRRRR ZGADLES MOJA LICZBE ZA ' + str(zgaduje) + ' RAZEM')
else:
    print('Niestety, nie zgadles. Moja liczba to ' + str(zgadnijliczbe)+' .')
input('Press Any to Exit...')
```python

Czy w ktoryms miejscu kodu moglem cos zrobic bardziej prawidlowo? Chodzi mi o logike lub/i zastosowanie np while True/while/def cos. Chcialem lepiej obsluzyc bledy ale nie chcialo mi dzialac uzywanie def z while True i jeszcze continue/break - chodzi mi o poczatek programu i wybranie START/KONIEC itp. 

No i teraz, jak poprawnie obsluzyc zamykanie a raczej nie zamykanie okna po przejsciu kodu/zakonczeniu programu. Tutaj dodalem input na koniec z komentarzem, ale nie jest to dobre rozwiazanie poniewaz trzeba to robic w kazdym potrzebnym miejscu. A jesli juz pisze press any key to nie tylko enterem powinno sie to zamykac. Dzieki za wskazowki.
0

W kategorii komedia to ta gra wygrywa :D Rozśmieszyło mnie to "Ta gra nie jest dla dzieci, ktore nie potrafia przepisac "START" lub "KONIEC"" :DD

  1. Mógłbyś nie brać pod uwagę wielkości znaków (np. przy 'start' czy 'koniec')
  2. Nazwy zmiennych po angielsku.
  3. Nazwy zmiennych bardziej precyzyjne (zamiast zgaduj to wybrana_liczba, zamiast zgadnijLiczbe to wylosowana_liczba)
  4. PEP8 (formatowanie kodu, nazw zmiennych itd.)
  5. Czy w tę grę da się przegrać? Chyba nie :D
0

Dzieki Kolego za uwagi. Cieszę się, że ktoś się chociaż pośmiał, ja też się śmiałem jak pisalem że tak mało wiem o pythonie:) hehe a humor w stylu starych gier przygotowych z lat 90 zawsze się sprawdza:)
Ad 1 - poczytam o tym jak to brać pod uwagę i jak tego nie robić - tzn gdybym chciał żeby user wpisał konkretną treść ale bez znaczenia jakimi znakami wielkimi czy małymi.
Ad 2 - to fakt masz rację.
Ad 3 - 100% poparcia sam się gubiłem o co chodzi:D
Ad 4 - dzięki poczytam
Ad 5 - chodziło o trening dla mnie i moze fun dla siorki córy, wiadomo że to nic specjalnego:>

Powiedzcie mi proszę gdybym chciał tą część kodu poprawić tak, żeby:

  1. kontrolować błędy danych np zamiast start ktos wpisalby 123 cyfry
  2. uzyc startorend jako funkcji a w niej while True/while i continue/break - jak musialby wygladac ten kod? Czy w ogole mozliwe jest uzycie tego wszystkiego razem?
if startorend == "START":
    print('Z A C Z Y N A M Y')
elif startorend == "KONIEC":
    print('Ale jestes ciapa ze nawet nie sprobujesz, do widzenia!!!')
    input("Press Any key to Exit...")
    sys.exit()
else:
    print('Ta gra nie jest dla dzieci, ktore nie potrafia przepisac "START" lub "KONIEC", obrazam sie do widzenia!')
    input("Press Any key to Exit...")
    sys.exit()```
0

OK udalo mi sie zrobic funkcje z wyjatkiem i obsluga bledu. Ale teraz mam inny problem. W momencie jak zgaduje liczbe i podaje wartosci jesli wpisze tekst przez co zostanie wyswietlony komentarz do ValueError a nastepnie dalej zgaduje liczbe - to lacznie ilosc zgadywan wydaje mi sie ze liczy sie od nowa. Jak to zrobic, zeby dalej nie miec wiecej niz 6 prób ?
Patrzcie na kod:

def typujliczbefunc():
    try:
        for typujliczbe in range(1,7):
            print('Zgaduj...')
            zgaduj =int(input())
            if zgaduj > losujeliczbe:
                print('Twoja liczba jest za wysoka')
            elif zgaduj < losujeliczbe:
                print('Twoja liczba jest za niska')
            else:
                break
        if zgaduj == losujeliczbe:
            print('SUPERRRRRRRR ZGADLES MOJA LICZBE ZA ' + str(typujliczbe) + ' RAZEM')
        else:
            print('Niestety, nie zgadles. Moja liczba to ' + str(losujeliczbe)+' .')
        input('Press ENTER key to Exit...')
    except ValueError:
        print('Uzywaj cyfr nie tekstu!!!')
    return typujliczbefunc()
typujliczbefunc()```

Oraz na efekt:
START
Z A C Z Y N A M Y
Wybralem juz moja liczbe, teraz sprobuj ja zgadnac...
Zgaduj...
30
Twoja liczba jest za niska
Zgaduj...
losujliczbe
Uzywaj cyfr nie tekstu!!!
Zgaduj...
50
Twoja liczba jest za niska
Zgaduj...
70
Twoja liczba jest za wysoka
Zgaduj...
60
Twoja liczba jest za niska
Zgaduj...
65
Twoja liczba jest za niska
Zgaduj...
69
Twoja liczba jest za wysoka
Zgaduj...
68
SUPERRRRRRRR ZGADLES MOJA LICZBE ZA 6 RAZEM
Press ENTER key to Exit...
0

Kombinuje i dalej nie wiem. Pomoze ktos? Jak sie zabezpieczyc przez wpisaniem tekstu zamiast strzalu ale tak, zeby kontrolowac ilosc wpisów usera?
Czyli zgaduje 1 raz, 2 raz, 3 raz, 4 raz, 5 raz i za szostym podaje tekst zamiast liczby. Obecnie po podaniu tekstu ilosc strzalow liczy sie od nowa do 6 a chcialbym zrobic tak, ze taki wpis inny niz 0-100 nie bedzie wliczany jako strzal.

Kod:

print('Wybralem juz moja liczbe, teraz sprobuj ja zgadnac...')

#prosze gracza o wskazanie liczby
def typujliczbefunc():
    typujliczbe = 0
    #counter = 0
    #a = [1,2,3,4,5,6]
    #a.pop()
#samplelist = [1,2,3,4,5,6]
    try:
        #typujliczbe = 0
        #for typujliczbe in range(1,7):
            while typujliczbe < 6:
                #print('To juz byl Twoj 6 strzal')
                print('Zgaduj...')
                zgaduj =int(input())

                typujliczbe = typujliczbe +1
                
                if zgaduj > losujeliczbe:
                    print('Twoja liczba jest za wysoka')
                elif zgaduj < losujeliczbe:
                    print('Twoja liczba jest za niska')
                else:
                    break
            #typujliczbe += 1
            if zgaduj == losujeliczbe:
                print('SUPERRRRRRRR ZGADLES MOJA LICZBE ZA ' + str(typujliczbe) + ' RAZEM')
            else:
                    print('Niestety, nie zgadles. Moja liczba to ' + str(losujeliczbe)+' .')
                    input('Press ENTER key to Exit...')
    except ValueError:
            print('Uzywaj cyfr nie tekstu!!!')
            typujliczbefunc()
typujliczbefunc()
0

Blok try powinien byś węższy to po 1.
Czyli try dajesz tam gdzie błąd może wystąpić a nie cały kod w to pakujesz.

zgaduj =int(input())
Zrób z tego funkcję czyli np.
zgaduj = pobierz_liczbe_od_gracza()

I w tej funkcji zrób sobie obsługę wypisywania tekstu tak

0
anonimowy napisał(a):

Blok try powinien byś węższy to po 1.
Czyli try dajesz tam gdzie błąd może wystąpić a nie cały kod w to pakujesz.

zgaduj =int(input())
Zrób z tego funkcję czyli np.
zgaduj = pobierz_liczbe_od_gracza()

I w tej funkcji zrób sobie obsługę wypisywania tekstu tak

Nie rozumiem co masz na mysli, mozesz konkretnie poprawic ten kod i wtedy zobacze?

0

Stwórz funkcję, która zwraca Ci wpisaną przez gracza liczbę.

0

Po co w ogóle ten try catch, który kompletnie nic nie robi?

0

Jak to nic nie robi? Jak inaczej moge obsluzyc blad bez try? Z tego co przegladalem rozne zrodla tak to sie wlasnie robi.
Co mi da ze to bedzie w funckcji a nie tak jak teraz?
Sprobuje to zrobic ale odp proszę na pytania.

0

To co podałeś aktualnie ma w ogóle sensu to jak mam Ci to wytłumaczyć? Musiałbym Cię uczyć programować

0

No jesli juz piszesz to napisz tak zebym zrozumial co nie ma sensu i dlaczego, chyba, ze tak sobie tylko piszesz i tak sobie istnieje to forum :)

0

On ma prawdopodobnie na myśli dziwny twór który by wyglądał tak:


def pobierz_liczbe_od_gracza():
	try:
		return int(input())
	except ValueError:
		pass #~ Tutaj obsługa błędu.
		
zgaduj = pobierz_liczbe_od_gracza()

Osobiście, nie polecam opakowywać to w funkcje. Ale tak rozumiem ten zarzut.

0

Tak, miałem właśnie na myśli coś w tym stylu. Jakie inne rozwiązanie w takim razie proponujesz?

0

Twórcy pythona i 'wielcy pythonowcy' zalecają używać wyjątków zawsze. A ja, gdy to nie ma sensu, używam tego co już zostało stworzone w tym celu:

def pobierz_liczbe_od_gracza():
	numer = input()
	if numer.isdigit():
		return int(numer)
	else:
		pass #~ Tu twoja obsługa w innym wypadku

Co prawda tu input i tak jest najwolniejszym elementem, ale to dobra praktyka z konwersji na inta ze stringa, działa szybciej niż blok try/except oraz wyłapie białe znaki, gdy są pożądane można zrobić to poprzez strip, gdy niepożądane, to tak jak w przykładzie. Tu masz pełny wybór co z tym zrobić :). Wolę świadomie i czytelnie programować, to jest dla mnie dużo czytelniejsze od obsługi wyjątku ValueError.

0

W tym przypadku owszem można z tego korzystać jednak warto zaznaczyć, że gdybyśmy chcieli obsłużyć liczby ujemne to isdigit() się wyłoży na tym

0

@Guaz to mit, że try catch jest bardziej pythonic. Dodatkowo biorąc pod uwagę wydajność oraz potencjalną częstotliwość występowania "błędu", zdecydowanie powinno to być zrobione if lub nawet while z isdigit. Liczby poprzedzone znakiem minus łatwo załatwimy lstrip.

1 użytkowników online, w tym zalogowanych: 0, gości: 1