Projekt końcowy: dziennik wyników
Zbudujesz program, który zapisuje wyniki graczy, pokazuje tabelę i zachowuje dane w pliku JSON. Projekt łączy najważniejsze elementy kursu: dane wejściowe, warunki, pętle, funkcje, listy, słowniki, pliki, moduły i obsługę wyjątków.
Co powinien robić program?
- pokazywać menu z poleceniami: dodaj wynik, pokaż wyniki i zakończ;
- pobierać imię gracza oraz liczbę punktów;
- odrzucać pustą nazwę i wynik mniejszy od zera;
- przechowywać każdy wpis jako słownik;
- zapisywać całą listę do pliku
scores.json; - po ponownym uruchomieniu odczytywać wcześniejsze wyniki;
- reagować czytelnym komunikatem na błędną liczbę lub uszkodzony plik.
Model danych
Jeden wynik będzie słownikiem, a wszystkie wyniki utworzą listę:
scores = [
{"name": "Ala", "points": 12},
{"name": "Jan", "points": 8},
]
Przed dalszą pracą sprawdź, jak odczytać imię pierwszego gracza i jak obliczyć najwyższy wynik.
Etap 1. Menu i dane w pamięci
Zacznij bez pliku. Najpierw doprowadź do działania menu oraz dodawanie wpisów:
scores = []
while True:
print("\n1. Dodaj wynik")
print("2. Pokaż wyniki")
print("3. Zakończ")
choice = input("Wybierz: ").strip()
if choice == "1":
name = input("Imię: ").strip()
try:
points = int(input("Punkty: "))
except ValueError:
print("Punkty muszą być liczbą całkowitą.")
continue
if not name:
print("Imię nie może być puste.")
elif points < 0:
print("Wynik nie może być ujemny.")
else:
scores.append({"name": name, "points": points})
print("Wynik dodany.")
elif choice == "2":
for position, score in enumerate(scores, start=1):
print(f"{position}. {score['name']}: {score['points']}")
elif choice == "3":
break
else:
print("Wybierz 1, 2 albo 3.")
Uruchom program po każdej niewielkiej zmianie. Sprawdź poprawny wpis, pustą nazwę, ujemny wynik, tekst zamiast liczby i nieznaną opcję menu.
Etap 2. Wydziel funkcje
Gdy pierwsza wersja działa, podziel ją na funkcje o jednej odpowiedzialności:
show_menu()wyświetla dostępne polecenia;add_score(scores)pobiera i sprawdza nowy wynik;show_scores(scores)wyświetla tabelę;load_scores(path)odczytuje listę z pliku;save_scores(path, scores)zapisuje listę do pliku.
Przekazuj dane przez parametry i zwracaj wynik przez return. Dzięki temu funkcje można sprawdzać oddzielnie.
Etap 3. Zapis JSON
Utwórz plik storage.py. Umieść w nim funkcje odpowiedzialne za plik:
import json
from pathlib import Path
def load_scores(path):
if not path.exists():
return []
try:
text = path.read_text(encoding="utf-8")
return json.loads(text)
except (OSError, json.JSONDecodeError):
print("Nie udało się odczytać zapisanych wyników.")
return []
def save_scores(path, scores):
text = json.dumps(scores, ensure_ascii=False, indent=2)
path.write_text(text, encoding="utf-8")
W pliku main.py zaimportuj te funkcje i przygotuj ścieżkę:
from pathlib import Path
from storage import load_scores, save_scores
path = Path("scores.json")
scores = load_scores(path)
Po poprawnym dodaniu wpisu wywołaj save_scores(path, scores). Nie zapisuj danych, które nie przeszły walidacji.
Plan testów
- Uruchom program bez pliku
scores.json— powinien zacząć z pustą listą. - Dodaj dwa poprawne wyniki i sprawdź ich kolejność.
- Wpisz litery zamiast punktów — program powinien kontynuować działanie.
- Wpisz pustą nazwę i wynik ujemny — żaden z tych wpisów nie powinien zostać zapisany.
- Zamknij program i uruchom go ponownie — wcześniejsze wyniki powinny wrócić.
- Celowo uszkodź kopię pliku JSON i sprawdź komunikat błędu. Nie eksperymentuj na ważnych plikach.
Rozszerzenia
Dodawaj tylko jedno rozszerzenie naraz i po każdym powtarzaj testy:
- sortowanie wyników malejąco przez
sorted(); - pokazywanie najlepszego wyniku przez
max(); - wyszukiwanie wyników wybranego gracza;
- usuwanie wpisu po numerze;
- eksport dodatkowej tabeli do CSV.
Typowe pułapki
input()zwraca tekst, dlatego punkty wymagają konwersji przezint().- Po nieudanej konwersji użyj
continue, aby nie korzystać z nieistniejącej wartości. - Nie stosuj pustego
except; przechwytuj tylko oczekiwane rodzaje błędów. - Nie mieszaj kodu obsługi menu z kodem zapisu pliku — osobne funkcje ułatwiają debugowanie.
Kiedy projekt jest ukończony?
Projekt jest gotowy, gdy spełnia wymagania podstawowe, przechodzi wszystkie testy i potrafisz własnymi słowami wyjaśnić przepływ danych: od odpowiedzi użytkownika, przez walidację i listę słowników, aż do pliku JSON.