(H) Korttien siirtelyä

Tavoite: Opin dynaamista muistinhallintaa ja osoittimien käsittelemistä.

Ohjeita: Hae ohjelmakoodipohja: templates/09/cards/ -> student/09/cards/.

Attention

Voit palauttaa tehtävän, vaikka et olisi saanut kaikkia metodeja toteutettua. Osapisteitä saa, jos toteutettuna on vähintään metodit add ja remove.

Jotta läpäiset automaattitestit, sinun täytyy kuitenkin toteuttaa tyhjät dummy-toteutukset kaikille metodeille. Tämä onnistuu Qt Creatorissa helposti, kun klikkaat hiiren oikealla näppäimellä metodin nimeä luokan .hh-tiedostossa ja tutkit valikon “Refactor” sisältöä.

Eräässä korttipelissä kortteja siirrellään korttipinoon ja pois pinosta tiettyjen sääntöjen mukaan. Toteutetaan luokka, joka antaa siirrellä kortteja pinossa vain pelin sääntöjen mukaisesti ja mahdollistaa pinon sisällön tulostamisen. Yksinkertaistetaan tehtävää siten, että kortit on numeroitu, ja jokaiseen korttiin viitataan sen numerolla, niin voimme keskittyä itse tietorakenteeseen ilman syötteenkäsittelyn yksityiskohtia.

Toteutamme siis tietorakenteen, johon voi tallentaa kokonaislukuja. Tiedostoon cards.hh on määritelty toteutettavan luokan rajapinta, jota ei saa muuttaa. Tämä tarkoittaa, että luokan julkista osaa ei saa muuttaa, mutta luokan yksityisiä metodeja ja jäsenmuuttujia saa halutessaan muokata. Koska harjoittelemme dynaamista muistinhallintaa, tehtävänäsi on toteuttaa luokka ilman, että käytät STL:n säiliöitä, string-tyyppiä tai C++:n taulukkoa.

Säännöt

Korttipino on aluksi tyhjä. Kortteja voi siirtää neljällä eri tavalla:

  • Korttipinon päällimmäiseksi voi lisätä uuden kortin, jonka numero välitetään lisäysoperaatiolle parametrina.
  • Korttipinosta voi poistaa päällimmäisen kortin. Tämän päällimmäisen kortin numero välitetään metodista kutsujalle (viiteparametrissa id).
  • Korttipinon alimmaisen kortin voi siirtää päällimmäiseksi.
  • Korttipinon päällimmäisen kortin voi siirtää pinon alimmaiseksi.

Jokaiselle edellä mainitulle toimenpiteelle toteutetaan oma metodinsa, joka on määritelty luokan rajapintaan valmiiksi.

Lisäksi testaamisen helpottamiseksi on määritelty metodi korttipinon tulostamista varten (print_from_top_to_bottom). Kaikkein innokkaimmilla on lisäksi mahdollisuus joko harjoitella enemmän osoittimien käsittelemistä tai kerrata rekursiota toteuttamalla toinen, hiukan haastavampi tulostusmetodi (print_from_bottom_to_top).

Luokan testaaminen

Luokalle on toteutettu yksinkertainen testaus tiedostossa main.cpp. Alla on esimerkki siitä, miten luokan pitää toimia annetulla testipääohjelmalla:

constructor
Enter amount of test cards: 5

print_from_top_to_bottom (deck is empty)

add * n

print_from_top_to_bottom
1: 4
2: 3
3: 2
4: 1
5: 0

bottom_to_top * 2

print_from_top_to_bottom
1: 1
2: 0
3: 4
4: 3
5: 2

top_to_bottom * 1

print_from_top_to_bottom
1: 0
2: 4
3: 3
4: 2
5: 1

print_from_bottom_to_top
1: 1
2: 2
3: 3
4: 4
5: 0

remove 0
remove 4
remove 3
remove 2
remove 1

destructor

Valmiiksi toteutettu testaus ei ole kattava, mutta pääset sen avulla alkuun. Saat vapaasti muokata testipääohjelmaa, ja kattavampi testaaminen auttaa ohjelman toteuttamisessa. Automaattitarkastus testaa toteuttamasi luokan toimintaa ilman pääohjelmaa, eli tarkastin ei välitä siitä, mitä omassa testipääohjelmassasi tehdään. Kannattaa kuitenkin ehkä pitää osa alkuperäisistä testeistä tallella, että voit vertailla niidenkin tulosteita yllä olevaan mallitulosteeseen.

Vinkkejä tehtävän tekemiseen:

  • Tutustu ensimmäiseksi valmiina annettuun pääohjelmaan.
  • Toteuta aluksi vain kaksi välttämättömintä metodia (toinen muokkaa korttipinoa ja toinen, jonka avulla lopputulosta testataan) ja kommentoi testipääohjelmasta pois kaikkien muiden metodien kutsut. Tässä tarkoituksessa useamman rivin kommentti /* tämä osa on kommentoitu pois */ on kätevä.
  • Muista jokaista operaatiota toteuttaessasi miettiä, mitä kaikkia tapauksia linkitetyn tietorakenteen käsittelemisessä pitää ottaa huomioon (edellisen materiaaliosion yhteenveto).
  • Jos haluat harjoitella enemmän eli päätät toteuttaa metodin print_from_bottom_to_top, sinulla on valittavanasi useita erilaisia toteutustapoja:
    1. Rekursio: Tämän saa toteutettua varsin pienellä vaivalla ja rekursion kertaaminen on hyvä, mutta et opi mitään uutta osoittimista. Aloita miettimällä, miten listan voi nähdä rekursiivisena tietorakenteena, eli miten sen saa jaettua samanmuotoiseksi, mutta pienemmäksi osaongelmaksi. Huomaa, että luokan yksityisessä rajapinnassa on valmiiksi määritelty apufunktio (recursive_print), jonka on tarkoitus rekursiivisesti kutsua itseään.
    2. Kahteen suuntaan linkitetty lista: Tässä saat harjoitella enemmän osoittimien käsittelyä, mutta tämä vaatii enemmän vaivaa kuin rekursiivinen ratkaisu. Idea on, että toteutat Card_data-tietueisiin osoittimet, jotka linkittävät kortit toisiinsa myös toiseen suuntaan. Nämä osoittimet pitää tietenkin päivittää kaikissa tietorakennetta muokkaavissa metodeissa.
    3. Voit tietenkin ilman toiseen suuntaan osoittavia osoittimiakin tehdä tulostuksen myös iteratiivisesti. Tämä vain vaatii aika paljon enemmän listan läpikäymistä edestakaisin, mikä on kovin tehotonta.

A+ esittää tässä kohdassa tehtävän palautuslomakkeen.