Rozdział 10. Projekt praktyczny: tajemnicze słowo

> Dodaj do ulubionych

Czytając ten kurs od początku poznałeś wiele nowych pojęć, takich jak kompilator, środowisko programistyczne, zmienna, funkcja, instrukcja warunkowa, pętla itd. W toku nauki obejrzałeś wiele przykładów użycia opisywanych konstrukcji językowych, ale dotychczas nie miałeś jeszcze okazji napisać własnego większego programu, aby przećwiczyć swoje umiejętności. Czas to zmienić. W tym rozdziale napiszesz swój pierwszy prawdziwy program.

Jaki jest cel wykonania takiego praktycznego projektu? Abyś użył zdobytej wiedzy w praktyce i dzięki temu lepiej utrwalił, a także rozwinął swoje umiejętności. Najlepszym sposobem na naukę programowania jest wykonywanie praktycznych zadań.

Temat tego projektu nie jest bardzo skomplikowany, ale za to zabawny. Napiszemy grę, która będzie mieszała litery w słowach i zadaniem gracza będzie odgadnąć, o jakie słowo chodzi.

Rysunek 10.1. Uruchomiona gra w zgadywanie słów
Rysunek 10.1. Uruchomiona gra w zgadywanie słów

10.1. Zasady gry i wskazówki

Gra, którą napiszemy będzie wymagała odgadnięcia słowa, w którym zostały poprzestawiane litery. Na pierwszy rzut oka może się wydawać, że ten program jest łatwy do realizacji, ale będzie to od nas wymagało wykorzystania wielu konstrukcji i technik poznanych w poprzednich rozdziałach:

  • zmiennych łańcuchowych,
  • funkcji,
  • instrukcji sterujących (warunkowych i pętli).

Zanim przejdziesz dalej możesz jeszcze raz przeczytać odpowiednie rozdziały, aby odświeżyć sobie wiadomości.

10.1.1. Zasady gry

Zasady naszej gry są następujące:

  1. Pierwszy gracz wpisuje słowo za pomocą klawiatury.
  2. Komputer miesza litery we wpisanym słowie.
  3. Drugi gracz próbuje odgadnąć, jakie słowo wpisał pierwszy gracz.

Oto przykładowa rozgrywka w tę grę:

Wpisz słowo TAJEMNICACo to za słowo? ATMJNCAIE ACINMEJAT Źle!Co to za słowo? ATMJNCAIE MAJETNICA Źle!Co to za słowo? ATMJNCAIE TAJEMNICA Brawo!
  1. Pierwszy gracz wpisał słowo „TAJEMNICA”.
  2. Komputer pomieszał litery tego słowa i poprosił drugiego gracza o odgadnięcie co to za słowo wyświetlając ciąg „ATMJNCAIE”.
  3. Drugi gracz próbuje odgadnąć oryginalne słowo. Próbuje trzy razy. Pierwsze dwie próby są niepoprawne, o czym zostaje poinformowany. Za trzecim razem w końcu trafia i otrzymuje pochwałę.

Oczywiście drugi gracz może bez problemu przeczytać, jakie słowo wpisał jego rywal. Poszukamy rozwiązania tej usterki pod koniec tego rozdziału.

10.1.2. Wskazówki

Gdy początkującego programistę rzuci się na głęboką wodę i każe mu się napisać realny program, to zazwyczaj kończy się to w ten sam sposób: na pytaniach typu „Od czego zacząć?”, „Jakich narzędzi użyć?”. Krótko mówiąc osoba taka zwykle nie wie, za co się wziąć na początku. To normalne.

Nie chcę jednak, abyś czuł się zagubiony i dlatego dam Ci kilka wskazówek na dobry początek. Pamiętaj jednak, że są to tylko podpowiedzi i możesz z nimi zrobić, co chcesz.

Podziel program na etapy

W omawianym programie można wyróżnić trzy etapy:

  1. Wpisanie słowa do odgadnięcia
  2. Pomieszanie liter
  3. Powtórzenie powyższych czynności w pętli, jeśli nie zostało wpisane odpowiednie słowo

Każdy z tych etapów jest faktycznie niezależny od pozostałych. Skoro tak, to czemu by nie podzielić budowy tego programu właśnie na te trzy osobne części?

  1. Etap pierwszy jest najprostszy: użytkownik musi wpisać słowo, które program musi zapisać w pamięci w postaci łańcucha. Jeśli sprawnie posługujesz się instrukcjami cin i cout, to ten etap nie sprawi Ci żadnych problemów.
  2. Etap drugi jest najbardziej skomplikowany ze wszystkich: mamy łańcuch zawierający słowo, np. „TAJEMNICA”, i chcemy losowo pomieszać jego litery, aby otrzymać ciąg typu np. „ATMJNCAIE”. Jak to zrobić? Będziesz musiał skorzystać z mojej małej pomocy, ponieważ do tego potrzebne jest pewne narzędzie, o którym jeszcze nie było mowy.
  3. Etap trzeci ma podobny poziom skomplikowania, co drugi: trzeba napisać pętlę proszącą gracza o wpisanie typowanego słowa i porównującą te wpisane słowo z oryginałem. Wyjście z pętli następuje, gdy słowo wpisane przez zgadującego jest identyczne z pierwotnym słowem.

Opracuj podstawową strukturę programu wg wyznaczonych etapów

Jak wiesz, każdy program zawiera funkcję main(). Napisz w niej komentarze oddzielające od siebie poszczególne części budowy programu, np.:

int main()
{
    // 1: Pobranie słowa od gracza

    // 2: Wymieszanie liter

    // 3: Poproszenie drugiego gracza o odgadnięcie słowa

    return 0;
}

Czas wziąć się do pracy! Aby ją sobie ułatwić, najlepiej będzie gdy zaczniesz od etapu 1., następnie zrobisz 3., a 2. zostawisz sobie na koniec.

Po wykonaniu etapów 1. i 3. program będzie prosił o podanie słowa, po czym będziesz mógł je wpisać jeszcze raz. Na razie nie będzie to pasjonująca gra, ale przynajmniej dowiesz się czy napisany kod działa poprawnie. Nie ma nic złego w rozwijaniu programu kawałek po kawałku.

Poniżej znajduje się wynik działania wczesnej wersji programu, która na razie ma zaimplementowane tylko etapy 1. i 3.:

Wpisz słowo TAJEMNICACo to za słowo? ATMJNCAIE Źle!Co to za słowo? MAJETNICA Źle!Co to za słowo? TAJEMNICA Brawo!

W tym stanie program nie wyświetla słowa z pomieszanymi literami, ale i tak w tym miejscu już połowa pracy będzie za Tobą.

10.1.3. Pomoc dotycząca mieszania liter

Etap związany z mieszaniem liter jest najtrudniejszy. Poniżej znajduje się kilka podpowiedzi, dzięki którym poradzisz sobie bez trudu także i z tym problemem.

Generowanie liczb losowych

Aby losowo pomieszać litery w słowie, trzeba wygenerować losową liczbę. Jeszcze nie pokazywałem jak się to robi, a więc poniżej zamieszczam szczegółowe objaśnienie.

  • Na początku programu dołącz nagłówki ctime i cstdlib, aby móc korzystać z funkcji dotyczących generowania liczb losowych.
  • Na początku funkcji main() umieść pojedyncze wywołanie srand(time()), aby zainicjować generator liczb losowych.
  • Następnie, aby wygenerować losową liczbę z przedziału np. od 0 do 4, napisz lbLosowa = rand() % 5; (napisaliśmy 5, ponieważ interesuje nas przedział 0-4).

Poniżej znajduje się przykładowy program generujący liczby losowe z przedziału od 0 do 4:

#include <iostream>
#include <ctime> // Dołączyć obowiązkowo
#include <cstdlib> // Dołączyć obowiązkowo

using namespace std;

int main()
{
    int lbLosowa(0);

    srand(time(0));

    lbLosowa = rand() % 5;

    return 0;
}

Wylosuj literę

Powyżej pokazałem, jak wylosować liczbę, ale przecież w programie mamy przecież mieszać litery, a nie liczby. Wyobraź sobie, że masz łańcuch znaków o nazwie sekretneSlowo. Wiesz, że łańcuchy można traktować jak tablice znaków, a więc sekretneSlowo[0] odpowiada pierwszej literze, sekretneSlowo[1] — drugiej itd.

Aby zatem wylosować literę, wystarczy tylko wylosować liczbę z przedziału od 0 do liczby znaków we wpisanym przez gracza słowie (którą można sprawdzić przy użyciu instrukcji sekretneSlowo.size()). Oto odpowiedni przykład kodu:


#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>

using namespace std;

int main()
{
    string sekretneSlowo("TAJEMNICA");


    srand(time(0));

    int pozycja = rand() % sekretneSlowo.size();

    cout << "Wylosowana litera:" << sekretneSlowo[pozycja];

    return 0;
}

Usuwanie liter z łańcucha

Aby uniknąć wielokrotnego wylosowania tej samej litery, należy każdą wylosowaną literę od razu usuwać. W tym celu należy użyć funkcji erase():

sekretneSlowo.erase(4, 1); // Usunięcie 5. litery

Funkcja erase() przyjmuje dwa parametry wywołania:

  • Numer litery do usunięcia (w przykładzie jest to numer 4, który odpowiada piątej literze, ponieważ liczenie rozpoczyna się od zera).
  • Liczba liter do usunięcia (tu 1)

Napisz funkcje

Nie jest to obowiązkowe, ale zamiast umieszczać wszystko w funkcji main() lepiej jest utworzyć kilka funkcji, z których każda będzie wykonywać swoje ściśle określone zadanie. Na przykład cały etap 2., w którym mieszane są litery wpisanego przez gracza słowa doskonale nadaje się do ujęcia w funkcję.

Później funkcję tę będzie można wywołać w funkcji main() w następujący sposób:

pomieszaneSlowo = mieszajLitery(sekretneSlowo);

Do funkcji przekazujemy sekretne słowa, a ona zwraca słowo z poprzestawianymi literami. Pozostało jeszcze tylko napisać kod tej funkcji. Zatem do dzieła!

10.2. Rozwiązanie zadania

Na pewno sporo czasu zajęło Ci zastanawianie się, jak napisać ten program. Często jest tak, że zadanie jest dość trudne i nie od razu wiadomo jak sobie z nim poradzić. Ale nie przejmuj się. Najważniejsze jest próbować. Tylko w ten sposób można robić postępy!

Etapy 1. i 3. są łatwe dla każdego. Jedynie etap 2. wymagał nieco zastanowienia i dlatego zaimplementowałem go w formie funkcji o nazwie mieszajLitery(), aby go później dokładniej omówić.

10.2.1. Kompletny kod źródłowy programu

#include <iostream>
#include <string>
#include <ctime>
#include <cstdlib>

using namespace std;

string mieszajLitery(string slowo)
{
	string mieszanka;
	int pozycja(0);

	// Pętla kontynuuje, aż wydobędzie wszystkie litery ze słowa
	while (slowo.size() != 0)
	{
		// Losujemy numer litery
		pozycja = rand() % slowo.size();
		// Dodajemy literę do mieszanki
		mieszanka += slowo[pozycja];
		// Usuwamy wylosowaną literę, aby nie została wylosowana ponownie
		slowo.erase(pozycja, 1);
	}

	// Zwracamy pomieszane słowo
	return mieszanka;
}

int main()
{
	string sekretneSlowo, pomieszaneSlowo, slowoUzytkownika;

	// Inicjacja generatora liczb losowych
	srand(time(0));


	// 1: prosimy użytkownika o wpisanie słowa
	cout << "Wpisz słowo" << endl;
	cin >> sekretneSlowo;

	// 2: mieszamy litery wpisanego słowa i wynik zapisujemy w zmiennej pomieszaneSlowo
	pomieszaneSlowo = mieszajLitery(sekretneSlowo);

	// 3: prosimy użytkownika, aby spróbował odgadnąć słowo
	do
	{
		cout << endl << "Co to za słowo? " << pomieszaneSlowo << endl;
		cin >> slowoUzytkownika;

		if (slowoUzytkownika == sekretneSlowo)
		{
			cout << "Brawo !" << endl;
		}
		else
		{
			cout << "Źle!" << endl;
		}
	} while (slowoUzytkownika != sekretneSlowo); // Zaczynamy od nowa, aż użytkownik zgadnie słowo

	return 0;
}

Jak widać kod tego programu wcale nie jest bardzo obszerny, a najlepiej go czytać zaczynając od funkcji main(). Zwróć uwagę na realizację poszczególnych etapów, jeden pod drugim. Dzięki temu że zostały wyraźnie oddzielone od siebie, łatwe jest je zrozumieć.

10.2.2. Objaśnienia

Poniżej znajduje się szczegółowy opis całego programu, który pomoże Ci dokładnie zrozumieć sposób jego działania.

Etap 1: wpisanie słowa

To był najłatwiejszy z wszystkich etapów. Polegał na wyświetleniu prośby o wpisanie słowa za pomocą instrukcji cout i zapisaniu go w zmiennej sekretneSlowo przy użyciu instrukcji cin.

Etap 2: mieszanie liter

To z kolei był najtrudniejszy z etapów. Jego realizacja została opracowana w formie funkcji o nazwie mieszajLitery (definicja znajduje się na początku programu). Funkcja main() wywołuje tę funkcję i przekazuje jej wpisane przez pierwszego gracza słowo. Zadaniem funkcji mieszajLitery jest zwrócenie pomieszanego słowa, które zostaje zapisane w zmiennej pomieszaneSlowo.

Przeanalizujemy budowę tej funkcji. Pobiera ona losowo po jednej literze słowa i robi to dopóki nie wybierze wszystkich liter.

while (slowo.size() != 0)
{
    pozycja = rand() % slowo.size();
    mieszanka += slowo[pozycja];
    slowo.erase(pozycja, 1);
}

W każdej iteracji pętli losowana jest liczba z przedziału od 0 do liczby liter pozostałych w słowie. Wylosowane litery dodawane są do łańcucha mieszanka i od razu usuwane z oryginalnego słowa, aby nie mogły być wylosowane więcej niż jeden raz.

Po wybraniu wszystkich liter kończy się wykonywanie pętli i zostaje zwrócona zmienna mieszanka zawierająca litery oryginalnego słowa w pomieszanej kolejności.

Etap 3: poproszenie drugiego gracza, aby odgadł słowo

Ten etap jest zrealizowany w formie pętli do ... while, dzięki czemu mamy gwarancję, że prośba o wpisanie słowa zostanie wyświetlona przynajmniej raz.

Słowo wpisane przez użytkownika jest odbierane przez instrukcję cin. Następnie jest ono porównywane z zawartością zmiennej sekretneSlowo. Pętla powtarza cykl dopóki nie zostanie wpisane takie same słowo, jak te, które znajduje się w zmiennej sekretneSlowo.

} while (slowoUzytkownika != sekretneSlowo); // Zaczynamy od nowa, aż użytkownik zgadnie słowo

W zależności od tego, czy wpisane słowo zgadza się z oryginałem, czy nie, wyświetlany jest odpowiedni napis. Koniec programu następuje w momencie wyjścia z tej pętli, które jednocześnie oznacza, że sekretne słowo zostało odgadnięte.

10.2.3. Pobieranie kodu źródłowego

Kod źródłowy gry można pobrać tutaj.

Plik ten możesz wykorzystać jako bazę do dalszej rozbudowy gry wg wskazówek opisanych w następnym podrozdziale.

10.3. Udoskonalanie gry

Skończyliśmy pracę nad programem, ale jak zawsze można w nim coś poprawić. Poniżej przedstawiam kilka propozycji udoskonaleń, których implementacja pozwoli Ci zdobyć jeszcze więcej praktycznych umiejętności programowania:

  • Dodaj więcej złamań wiersza na początku gry: gdy pierwszy gracz wpisze słowo do odgadnięcia, drugi gracz może je zobaczyć. Aby mu to uniemożliwić, dodaj złamania wiersza, które spowodują, że słowo to zostanie ukryte poza linią okna konsoli. Możesz w tym celu użyć np. instrukcji endl.
  • Dodaj możliwość rozegrania kolejnej partii gry: obecnie gdy drugi gracz odgadnie słowo, następuje koniec gry. Zmodyfikuj program tak, aby zamiast tego wyświetlał pytanie „Czy chcesz zagrać jeszcze raz (t/n)?”. Gdy gracz naciśnie klawisz T, gra powinna rozpocząć się od nowa. Można to zrealizować poprzez zastosowanie pętli do ... while obejmującej wszystkie trzy etapy programu.
  • Ustaw limit prób odgadnięcia słowa: możesz np. wyświetlać informację typu „Zostało Ci jeszcze 5 prób”. Gdy użytkownik nie zdoła odgadnąć słowa w wyznaczonej liczbie prób, następuje zakończenie gry i pokazanie prawidłowej odpowiedzi.
  • Wyświetl średnią gracza na koniec gry: po zakończeniu gry wyświetl punktację. Możesz ją obliczyć w dowolny sposób. W trakcie gry wyniki poszczególnych rozgrywek możesz zapisywać w wektorze.
  • Losuj słowa z pliku zawierającego listę słów: aby można było grać samemu, możesz utworzyć słownik, z którego program będzie losował słowa do odgadnięcia. Oto przykładowa zawartość takiego pliku: TAJEMNICA KONTRABAS KIEŁBASA PLUTON MAGIA TELEWIZJA Zamiast prosić gracza o wpisanie słowa do odgadnięcia (etap 1), program powinien sam losować słowo ze słownika. Implementując te rozwiązanie poćwiczysz pracę z plikami!

Jeśli masz jeszcze jakieś inne pomysły, to spróbuj je zrealizować. Im więcej będziesz ćwiczyć, tym efektywniejsza będzie Twoja nauka

Podsumowanie

Jestem pewien, że wykonanie tego projektu pozwoliło Ci dokonać większego postępu niż lektura wszystkich poprzednich rozdziałów. Nie ograniczaj się tylko do tego, co ja zaproponowałem. Wymyśl własne udoskonalenia do gry i je zaimplementuj. Jeśli utkniesz na czymś i nie będziesz wiedział, co robić dalej, możesz przecież zawsze poszukać pomocy na licznych forach internetowych.

Autor: Mathieu Nebra i Matthieu Schaller

Źródło: http://openclassrooms.com/courses/programmez-avec-le-langage-c/tp-le-mot-mystere

Tłumaczenie: Łukasz Piwko

Treść tej strony jest dostępna na zasadach licencji CC BY-NC-SA 2.0

2 komentarze do “Rozdział 10. Projekt praktyczny: tajemnicze słowo”

  1. Np. przez dodanie więcej endl – „Możesz w tym celu użyć np. instrukcji endl”

Możliwość komentowania została wyłączona.