C++ skaičių spėliojimo žaidimas

Šio straipsnio metu sukursime labai paprastą konsolinį (tekstinį) žaidimą, kurio metu žaidėjas turės atspėti atsitiktinai sugeneruotą skaičių.

 

Programavimo žinios

 

Prieš pradedant būtų gerai jei mokėtumėte šiuos dalykus:

  • Kintamieji.
  • Patikrinimo sąlygos.
  • Ciklai.
  • Funkcijos.

Nieko tokio jei ir kažko nemokate, svarbiausia, kad išsinagrinėtumėte pateikiamus kodus ir suprastumėte kas ir kodėl yra parašyta. Galbūt būtent dabar ką nors daugiau išmoksite, jei nemokate?

 

Žaidimo reikalavimai

 

Prieš rašant programos kodą reikia nuspręsti kokie yra žaidimo reikalavimai, todėl iškart galiu nusakyti šiuos reikalavimus:

  • Žaidimo kūrimo aplinkai bus naudojama Visual Studio programa, tačiau galima naudoti ką nors kitą, pavyzdžiui, CodeBlocks.
  • Žaidimas bus programuojamas panaudojant C++ programavimo kalbą, tačiau galite pamėginti tą patį sukurti ir su kokia nors kita pasirinkta programavimo kalba, pavyzdžiui, C#.
  • Turi būti galima pasirinkti „sunkumą“, pasirenkant kokio intervalo skaičiai bus naudojami generuojant atsitiktinį skaičių.
  • Žaidimas turi apsisaugoti nuo netinkamai įvedamų reikšmių klaidų.
  • Žaidimas galės padėti žaidėjui, pasakinėdamas ar skaičius kurį reikia atspėti yra didesnis ar mažesnis už prieš tai bandytą atspėti skačių.
  • Pagrindinėje programos funkcijoje (main) gali būti tik kintamieji ir kreipiniai į kitas funkcijas.
  • Atskiros funkcijos skirtingiems algoritmams vykdyti.
  • Turi būti galimybė kartoti žaidimą.

 

Žaidimo struktūrinė schema

 

Žinant žaidimo reikalavimus, galima nubraižyti žaidimo struktūrinę schemą:

atsitiktiniai skaiciai cpp

  • Stačiakampiai – atliekamas tam tikras veiksmas.
  • Rombai – patikrinimo sąlygos (if arba switch).
  • Lygiagretainiai – duomenys.

 

Jeigu norėsite patobulinti žaidimą ir tuo pačiu atnaujinti šią schemą, tai parsisiųskite šį schemos failą: atsitiktiniai skaiciai cpp.zip išarchyvuokite xml failą iš archyvo, ir jį (xml) naudokite puslapyje draw.io

 

Žaidimo kūrimas

 

Kadangi šis projektas yra vienas iš lengvesnių, jame nenaudojamos klasės, tai visas kodas bus rašomas į vieną failą. Todėl bus source.cpp failas, kuriame pačiai pradžiai galime įsitraukti reikiamas bibliotekas ir susikurti pagrindinę žaidimo funkciją (main).

#include <iostream> // darbui su konsole
using namespace std;

int main()
{
  setlocale(LC_ALL, "Lithuanian"); // lietuviškoms raidėms
  
  // kintamieji ir kreipiniai į funkcijas

  return 0;
}

Pažiūrėjus į struktūrinę schemą matosi, kad pačioje pradžioje žaidimas turėtų deklaruoti reikiamus kintamuosius, todėl iškart po setlocale metodo reiktų deklaruoti kintamuosius intervalo pradžiai, intervalo pabaigai ir atsitiktiniam skaičiui saugoti.

int pradzia, // intervalo pradžia
  pabaiga, // intervalo pabaiga
  atsitiktinis; // atsitiktinai sugeneruotas skaičius

Toliau reikia pasiruošti atsitiktinio skaičiaus generavimui, todėl reikia pasirinkti žaidimo „sunkumą“ pasirenkant intervalą kuriame bus generuojamas atsitiktinis skaičius. Taip pat, vienas iš reikalavimų yra tas, kad viskas turėtų būti atskirose funkcijose, todėl intervalo skaičių įvedimui kuriama atskira funkcija. Ji rašoma iškart už main funkcijos.

void Intervalas(int & pradzia, int & pabaiga)
{
  cout << "Įveskite intervalo pradžią: ";
  cin >> pradzia;
  cout << "Įveskite intervalo pabaigą: ";
  cin >> pabaiga;
}

Funkcijai paduodami parametrai  int & pradzia, int & pabaiga nurodo, kad kintamieji, kurie bus perduodami šiai funkcijai bus pakeičiami naujai įvestomis reikšmėmis, tai nurodo simboliai &.

Taip pat, vienas iš reikalavimų yra tas, kad žaidimas apsisaugotų nuo netinkamai įvestų duomenų, todėl įvedimą į kintamuosius galima įdėti į do while ciklą.

void Intervalas(int & pradzia, int & pabaiga)
{
  bool dar;

  do
  {
    dar = false;

    cout << "Įveskite intervalo pradžią: ";
    cin >> pradzia;
    cout << "Įveskite intervalo pabaigą: ";
    cin >> pabaiga;

    if (pabaiga <= pradzia)
    {
      dar = true;
      cout << "Klaida įvedime. Intervalo pabaiga turi būti didesnė už pradžią.\n\n";
    }
  } while (dar);
}

Do while nuo while skiriasi tuo, kad pirma įvykdomas kodas (žaidėjas įveda duomenis), o vykdomas antrą ar daugiau kartų tik jei reikia (šiuo atveju tol kol intervalo skaičiai bus įvedami blogai). Šis do while ciklas bus vykdomas dar kartą jei bool sąlyga (gali būti tik true arba false) bus true. Bool pasikeis į true, jei bus blogai įvesti intervalo duomenys, o tai patikrina if sąlyga iškart po šių duomenų įvedimo.

Baigus rašyti intervalo funkcijos kodą, jos prototipą ( void Intervalas(int & pradzia, int & pabaiga); ) reikia dėti virš main funkcijos. Ir pačioje main funkcijoje daryti kreipinį. Todėl kol kas žaidimo kodas turėtų atrodyti kažkaip panašiai:

#include <iostream> // darbui su konsole
using namespace std;

void Intervalas(int & pradzia, int & pabaiga);

int main()
{
  setlocale(LC_ALL, "Lithuanian"); // lietuviškoms raidėms
  
  int pradzia, // intervalo pradžia
    pabaiga, // intervalo pabaiga
    atsitiktinis; // atsitiktinai sugeneruotas skaičius

  Intervalas(pradzia, pabaiga);

  return 0;
}

void Intervalas(int & pradzia, int & pabaiga)
{
  bool dar;

  do
  {
    dar = false;

    cout << "Įveskite intervalo pradžią: ";
    cin >> pradzia;
    cout << "Įveskite intervalo pabaigą: ";
    cin >> pabaiga;

    if (pabaiga <= pradzia)
    {
      dar = true;
      cout << "Klaida įvedime. Intervalo pabaiga turi būti didesnė už pradžią.\n\n";
    }
  } while (dar);
}

Galima atlikti testavimą įvedant neteisingus duomenis ir paskui įvedant teisingus duomenis. Įvedus neteisingus duomenis išvedamas klaidos pranešimas ir leidžiama bandyti dar kartą. O įvedus duomenis teisingai įvyksta žaidimo pabaiga, nes kol kas nėra daugiau jokio funkcionalumo.

ScreenShot 2015-12-26 --- 19-40-53

Toliau, iš struktūrinės žaidimo schemos matosi, kad reikia generuoti atsitiktinį skaičių nurodytame intervale. Bet prieš tai reikia šių papildomų bibliotekų programos viršuje:

#include <stdlib.h> // srand, rand metodai
#include <time.h> // time metodai

Turint šias bibliotekas galima susigeneruoti „seed“ iš kurio bus generuojami atsitiktiniai skaičiai. Tam reikia tik vienos eilutės programoje, kuri gali būti main funkcijoje, iškart po setlocate ir prieš kintamuosius:

srand(time(NULL));

Tuomet bet kurioje programos vietoje galima generuoti tiek atsitiktinių skaičių kiek reikia, ar juos vis pergeneruoti. Tai galima atlikti su rand metodu, pavyzdžiui:

skaicius1 = rand() % 100; // nuo 0 iki 99
skaicius2 = rand() % 100 + 1; // nuo 1 iki 100
skaicius3 = rand() % 30 + 1985; // nuo 1985 iki 2014
skaicius4 = 5 + rand() % (100 - 5); // nuo 5 iki 100

Todėl atsitiktinį skaičių duotame intervale galima gauti parašant:

atsitiktinis = pradzia + rand() % (pabaiga - pradzia);

Tai reikia rašyti iškart už kreipinio į intervalo įvedimo funkciją.

Jei reikia pasitikrinimo ar gerai sugeneruojamas atsitiktinis skaičius, galima daryti cout, pavyzdžiui:

Intervalas(pradzia, pabaiga);
atsitiktinis = pradzia + rand() % (pabaiga - pradzia);
cout << atsitiktinis << endl;

Atlikus testavimą šį cout reiktų ištrint, nes žaidėjas turi šį skaičių atspėt, o ne pamatyt.

Toliau, žaidėjas turėtų spėlioti atsitiktinai sugeneruotą skaičių. Tam galima kurt atskirą funkciją.

void Spelioti(int pradzia, int pabaiga, int atsitiktinis)
{
  int spejimas;

  cout << "Pabandykite atspėti skaičių (" << pradzia << " - " << pabaiga << "): ";
  cin >> spejimas;

  if (spejimas < atsitiktinis)
    cout << "Skaičius yra didesnis";
  else if (spejimas > atsitiktinis)
    cout << "Skaičius yra mažesnis";
  else
    cout << "Jūs laimėjote!";

  cout << "\n\n";
}

Visą spėliojimo veiksmą galima vėl įdėti į do while ciklą, panašiai kaip intervalo kūrimo atveju. Neatspėjus skaičiaus ciklas turėtų būti kartojamas.

void Spelioti(int pradzia, int pabaiga, int atsitiktinis)
{
  int spejimas;
  bool dar;

  do
  {
    dar = false;

    cout << "Pabandykite atspėti skaičių (" << pradzia << " - " << pabaiga << "): ";
    cin >> spejimas;

    if (spejimas < atsitiktinis)
    {
      cout << "Skaičius yra didesnis";
      dar = true;
    }
    else if (spejimas > atsitiktinis)
    {
      cout << "Skaičius yra mažesnis";
      dar = true;
    }
    else
      cout << "Jūs laimėjote!";

    cout << "\n\n";
  } while (dar);
}

Šios funkcijos pabaigoj, iškart po cout << "\n\n";  dar galima įdėti if, kuris patikrintų ar įvestas validus skaičius, tačiau kol kas manykime, kad vartotojas nemėgins sulaužyti žaidimo.

Funkcijos prototipą ( void Spelioti(int pradzia, int pabaiga, int atsitiktinis); ) vėlgi reiktų nepamiršti kopijuoti virš main funkcijos.

main funkcijoje galima daryti kreipinį į naujai sukurtą funkciją Spelioti(pradzia, pabaiga, atsitiktinis);  iškart už atsitiktinio skaičiaus generavimo.

Galima išsitestuoti žaidimą, bent jau kiek mes jo turime:

Spėliojimo funkcija bus vykdoma tol kol skaičius bus atspėtas, o atspėjus reiktų leisti kartoti žaidimą, todėl main funkcijoje reiktų sukurti char kintamąjį į kurį bus įrašomas žaidėjo pasirinkimas ir viską nuo intervalo pasirinkimo iki spėliojimo reiktų vėl įdėti į kokį nors ciklą, tam vėl geriausiai tinka do while ciklas.

int main()
{
  setlocale(LC_ALL, "Lithuanian"); // lietuviškoms raidėms
  srand(time(NULL));

  int pradzia, // intervalo pradžia
    pabaiga, // intervalo pabaiga
    atsitiktinis; // atsitiktinai sugeneruotas skaičius
  char veiksmas; // t - tęsti žaidimą, n - baigti
  bool dar;

  do
  {
    dar = false;

    Intervalas(pradzia, pabaiga);
    atsitiktinis = pradzia + rand() % (pabaiga - pradzia);
    Spelioti(pradzia, pabaiga, atsitiktinis);

    cout << "Kartoti žaidimą? (t/n)\n";
    cin >> veiksmas;

    if (veiksmas == 't')
      dar = true;
    else
      cout << "Žaidimo pabaiga\n\n";
  } while (dar);

  return 0;
}

Galimas žaidimo pavyzdys:

ScreenShot 2015-12-26 --- 23-23-17

 

Parsisiuntimas

Jeigu kas nors nesigavo, bet nerandate kas, galite parsisiųsti originalų kodą: cpp skaiciu speliojimo zaidimas.zip

 

Tobulinimas

 

Šiame žaidime nieko daug negalima nuveikti, tačiau jį galima patobulinti įdedant naujo funkcionalumo. Keletas idėjų ką dar būtų galima nuveikti:

  • Rezultatus rašyti į failą, jį vis papildant, paskui iš jo galbūt išvesti statistiką.
  • Padaryti, kad būtų galima naudotis pagalbomis, arba jas išjungti („skaičius yra mažesnis“, „skaičius yra didesnis“).
  • Atsitiktinį intervalo pradžios ir pabaigos skaičių sugeneravimą (žaidėjas gali rinktis ar pats įveda, ar jam jį sugeneruoja).
  • Žaidimui pritaikyti grafinę vartotojo sąsają (GUI).
  • Suskaičiuoti iš kelinto karto pavyko atspėti atsitiktinai sugeneruotą skaičių.
  • Suskaičiuoti iš kiek skaičių buvo bandoma atspėti vieną skaičių (intervalo pabaiga – intervalo pradžia).

 

Parašykite komentarą