Helsingin yliopisto > Tietojenkäsittelytieteen laitos > Ohjelmoinnin perusteet -opintojakso > syksyn 2004 kurssi

Koetehtävän 3b arvosteluperusteet

Tehtävä

Toteuta seuraava arvauspeli vuorovaikutteisena ohjelmana: Ennen pelin alkua tietokone arpoo 10000 kokonaislukua taulukkoon. Lukujen arvot ovat välillä 0-9999. Sama luku saa esiintyä useampaankin kertaan. Vihje: Yhden tuollaisen luvun saa arvottua lausekkeella (int)(Math.random()*10000). Tämän jälkeen on pelaajan vuoro: Pelaaja yrittää arvata taulukossa olevia lukuja. Peli päättyy, kun pelaaja on onnistunut arvaamaan kolme lukua. Kerran oikein arvattua lukua ei tietenkään saa käyttää uudelleen. Pelin tulos - jonka ohjelma lopuksi ilmoittaa - on tarvittujen arvausten määrä, vähintään siis kolme. Arvattavien lukujen hakeminen on tehokkuussyistä ohjelmoitava binäärihakua käyttäen. (9 pistettä).

Ratkaisu

Tehtävän voi ratkaista monin eri tavoin. Kaikki ratkaisut pitävät sisällään suunnilleen seuraavan rakenteen:

arvo luvut
toista
    lue uusi arvaus
    lisää arvausten määrää
    jos arvaus ei löydy aikaisemmin arvatuista ja arvaus löytyy arvotuista luvuista
        lisää oikein arvattujen määrää
        lisää arvaus aikaisemmin arvattuihin
kunnes arvattu oikein kolme lukua
tulosta arvausten määrä

Yksi mahdollinen ratkaisutapa on antaa lukujen säilyttäminen yhden luokan tehtäväksi ja laatia pelilogiikka toiseen luokkaan. Tällöin päädytään seuraavankaltaisiin luokkiin:

/*
   Int-tyyppisten lukujen kokoelma. Kokoelmalla on maksimikoko.
   Laatija: Joni Salmi
 */

public class Lukukokoelma {
    
    /* Taulukko, jossa luvut säilytetään. */
    private int[] taulukko;
    
    /* Kokoelman sisältämien lukujen lukumäärä. */
    private int lkm;
    
    /*
       Luo uuden Lukukokoelma-tyyppisen olion. Kokoelman maksimikooksi
       asetetaan parametrin mukainen maksimikoko, jos parametri on
       ei-negatiivinen. Muussa tapauksessa maksimikooksi asetetaan nolla.
     */
    
    public Lukukokoelma(int maksimikoko) {
        if (maksimikoko > 0)
            this.taulukko = new int[maksimikoko];
        else
            this.taulukko = new int[0];
        this.lkm = 0;
    }
    
    /*
       Palauttaa true, jos lukukokoelma ei ole maksimikokoinen; muussa
       tapauksessa palauttaa false;
     */
    
    public boolean onTilaa() {
        return this.lkm < this.taulukko.length;
    }
    
    /*
       Lisää luvun lukukokoelmaan. Palauttaa true, jos lisäys onnistui.
       Palauttaa false, jos lukujono on maksimipituinen.
     */
    
    public boolean lisää(int luku) {
        if (!this.onTilaa())
            return false;
        int lisäyskohta = this.lkm;
        while (lisäyskohta > 0 && luku < this.taulukko[lisäyskohta - 1]) {
            this.taulukko[lisäyskohta] = this.taulukko[lisäyskohta - 1];
            lisäyskohta--;
        }
        this.taulukko[lisäyskohta] = luku;
        this.lkm++;
        return true;
    }
    
    /*
       Palauttaa true, jos lukukokoelma sisältää annetun luvun; muussa
       tapauksessa palauttaa false.
     */
    
    public boolean sisältää(int luku) {
        int vasen = 0;
        int oikea = this.lkm - 1;
        int keski;
        while (vasen <= oikea) {
            keski = (vasen + oikea) / 2;
            if (luku == this.taulukko[keski])
                return true;
            if (luku < this.taulukko[keski])
                oikea = keski - 1;
            else
                vasen = keski + 1;
        }
        return false;
    }
    
}

/*
   Arvauspeli.
   Laatija: Joni Salmi
 */

public class Arvauspeli {
    
    public static void main(String[] args) {
        
        Lukukokoelma arvotut = new Lukukokoelma(10000);
        while (arvotut.onTilaa())
            arvotut.lisää((int)(Math.random() * 10000));
        System.out.println("Olen arponut 10000 lukua väliltä 0...9999.");
        System.out.println("Koeta arvata 3 arpomaani lukua.");
        Lukukokoelma oikeinArvatut = new Lukukokoelma(3);
        int arvaus;
        boolean arvattuJo;
        int arvauksia = 0;
        do {
            do {
                System.out.print("Anna arvauksesi: ");
                arvaus = Lue.kluku();
                arvauksia++;
                arvattuJo = oikeinArvatut.sisältää(arvaus);
                if (arvattuJo)
                    System.out.println("Arvasit tuon jo.");
            } while (arvattuJo);
            if (arvotut.sisältää(arvaus)) {
                System.out.println("Oikein.");
                oikeinArvatut.lisää(arvaus);
            }
            else
                System.out.println("Väärin.");
        } while (oikeinArvatut.onTilaa());
        System.out.println("Peli päättyi.");
        System.out.println("Käytit " + arvauksia + " arvausta.");
        
    }
    
}

Arvosteluperusteet

Ratkaisu jaettiin välttämättömiin ja ei-välttämättömiin osiin. Välttämättömien osien oikeellisesta toteuttamisesta sai pisteitä. Ei-välttämättömien osien toteuttamisesta ei saanut pisteitä, mutta jos niitä oli toteuttanut virheellisesti, menetti pisteitä. Yhteispistemäärä oli aina vähintään nolla: pisteiden menettäminen ei siis voinut painaa yhteispistemäärää negatiiviseksi.

Ratkaisun välttämättömien osien arvosteluperusteet

Oheisessa luettelossa on lueteltu ratkaisun (ylensä) välttämättömät osat. Kunkin osan viereen on merkitty paljonko kyseisen osan oikeellisesta toteuttamisesta annettiin maksimissaan pisteitä. Jonkin luettelossa mainitun osan esiintyminen ratkaisussa ei automaattisesti takaa mainittua pistemäärä, sillä ratkaisut arvioitiin kokonaisuuksina.

Luokan otsake ja main-metodin otsake (0,5 p.)

Luokan otsake ja main-metodin otsake oikein, ei koodia laittomasti luokan/metodin ulkopuolella tai metodeja metodien sisässä (0,5 p.)

Tyypillisiä virheitä: main-metodia ei ole lainkaan, main-metodin tyyppi puuttuu tai on virheellinen, main-metodin parametri puuttuu tai on virheellinen.

Arvotut luvut (3,0 p.)

Arvottujen lukujen taulukkomuuttuja esitellään ja taulukko-olio luodaan 10000 alkion kokoisena (0,5 p.)

Tyypillisiä virheitä: syntaksivirheet, oliota ei luoda, alkioita on väärä määrä.

Arvottujen lukujen taulukko täytetään arvotuilla luvuilla (0,5 p.)

Tyypillisiä virheitä: syntaksivirheet (vaikka lauseke annettiin suoraan tehtävänannossa!), taulukon viimeiseen alkioon ei arvota lukua.

Arvottujen lukujen taulukko järjestetään (1,0 p.)

Taulukko on järjestettävä, jotta siihen voitaisiin käyttää binäärihakua. Mikä tahansa järjestämisalgoritmi hyväksyttiin - myös arvottujen lukujen järjestäminen jo lisäysvaiheessa. Tyypillisiä virheitä: indeksivirheet taulukon ensimmäisen tai viimeisen alkion kohdalla.

Arvausta haetaan arvottujen lukujen taulukosta binäärihaulla (1,0 p.)

Tyypillisiä virheitä: paikallisen muuttujan tyypin määrittelyn puuttuminen, puuttuva yhtäsuuruusmerkki vasemman ja oikean reunan vertailussa, keskialkion indeksin laskeminen pelkästään kertomalla puolella tai puolellatoista, alkion indeksin ja alkion arvon sekoittaminen.

Oikein arvatut luvut (1,5 p.)

Yllättävän monissa ratkaisuissa tallennettiin kaikki käyttäjän syöttämät luvut, ei vain käyttäjän oikein arvaamia lukuja, kuten tehtävänanto edellyttää. Kaikkien lukujen tallentamisesta sinänsä ei kuitenkaan sakotettu, jos lukuja varauduttiin tallentamaan useita tuhansia. Tallentamisen tietorakenteena saattoi käyttää int-tyyppisiä muuttujia, int-taulukkoa tai vaikkapa boolean-taulukkoa.

Oikein arvattujen lukujen tietorakenne esitellään riittävässä näkyvyysalueessa ja sen arvo[t] luodaan/alustetaan tarvittaessa (0,5 p.)

Tyypillisiä virheitä: taulukko-olio jätettiin luomatta, alkioiden määrä oli riittämätön suhteessa myöhemmän koodin toimintaan.

Uutta arvausta etsitään oikein arvattujen lukujen joukosta (0,5 p.)

Tyypillisiä virheitä: etsinnässä käytettiin binäärihakua vaikkei taulukko ollut järjestetty, ei otettu huomioon että int-taulukon alkioilla on oletuksena alkuarvo nolla ja nolla on myös mahdollinen käyttäjän arvaama luku.

Oikein arvatut luvut lisätään oikein arvattujen lukujen tietorakenteeseen (0,5 p.)

Tyypillisiä virheitä: uusi luku lisättiin vanhan luvun päälle, arvattu luku "poistettiin" arvottujen lukujen taulukosta kirjoittamalla sen päälle -1 samalla tuhoten binäärihaun vaatiman suuruusjärjestyksen).

Oikein arvattujen lukujen lukumäärä (1,5 p.)

Oikein arvattujen lukujen lukumäärää varten ei välttämättä tarvita omaa muuttujaansa, sillä määrän voi päätellä tutkimalla sopivasti alustettua arvattujen lukujen tietorakennetta.

Oikein arvattujen lukujen lukumäärämuuttuja esitellään riittävässä näkyvyysalueessa ja muuttujan arvo alustetaan nollaksi (0,5 p.)

Tyypillisiä virheitä: nollaksi alustaminen unohdettu vaikka kyseessä olisi alkuarvoltaan määrittelemätön paikallinen muuttuja.

Oikein arvattujen lukumäärää kasvatetaan yhdellä tarvittaessa (0,5 p.)

Tyypillisiä virheitä: määrää kasvatettiin muutoinkin kuin oikean arvauksen yhteydessä.

Lukujen kyselyä jatketaan, kunnes käyttäjä on arvannut kolme lukua oikein (0,5 p.)

Tyypillisiä virheitä: ei kysytä ainuttakaan lukua, kysytään vain kerran tai kysytään neljä kertaa.

Arvausten lukumäärä (1,5 p.)

Arvausten lukumäärämuuttuja esitellään riittävässä näkyvyysalueessa ja muuttujan arvo alustetaan nollaksi (0,5 p.)

Tyypillisiä virheitä: muuttujaa ei oltu alustettu nollaksi tai se oltiin alustettu ykköseksi.

Arvausten lukumäärää kasvatetaan yhdellä tarvittaessa (0,5 p.)

Tehtävänannossa ei määrätty lasketaanko arvauksien määrään myös sallitun arvoalueen ulkopuoliset arvaukset tai jo arvattujen lukujen uudelleenarvaukset, joten kaikki tulkinnat hyväksyttiin. Tyypillisiä virheitä: kasvatettiin väärää muuttujaa.

Arvausten lukumäärä tulostetaan pelin lopuksi (0,5 p.)

Tyypillisiä virheitä: tulostaminen puuttuu kokonaan

Uusi arvaus (1,0 p.)

Uutta arvausta varten ei välttämättä tarvita omaa muuttujaansa, sillä arvauksen voi tallentaa myös oikein arvattujen lukujen tietorakenteeseen (mahdollisesti aikaisemman arvon ylikirjoittaen)

Uuden arvauksen muuttuja esitellään riittävässä näkyvyysalueessa (0,5 p.)

Tyypillisiä virheitä: muuttujaa käytetään toistolauseen ehdossa mutta muuttuja esitellään toistolauseen lohkon sisällä.

Uusi arvaus luetaan muuttujaan (0,5 p.)

Tyypillisiä virheitä: muuttujan tyyppiä ei oltu määritelty.

Ratkaisun ei-välttämättömien osien arvosteluperusteet

Koska nämä osat eivät ole välttämättömiä, niiden toteuttamisesta ei saa pisteitä. Jos osan kuitenkin on päättänyt toteuttaa, toteutuksen on oltava oikeellinen. Kussakin kategoriassa 0,5 pistettä on maksimimenetys: jos kyseisen kategorian virhe esiintyy, menettää 0,5 pistettä ensimmäisestä virheestä. Muista samanlaisista virheistä ei aiheudu lisämenetyksiä.

Metodit ja konstruktorit

Yksi tai useampi toiminto on eristetty omaksi metodikseen tai sijoitettu luokan konstruktoriin, mutta ratkaisussa on ainakin yksi virhe : -0,5 p.

Tyypillisiä virheitä: kutsutaan ilmentymämetodia luokkametodista käsin ilmentymää luomatta, metodilla ei ole paluuarvon tyyppiä tai void-määrettä, metodi palauttaa erityyppisen arvon kuin metodin otsake lupaa.

Luokka- tai ilmentymämuuttujat

Yksi tai useampi muuttuja on esitelty paikallisen muuttujan asemesta luokka- tai ilmentymämuuttujana, mutta ratkaisussa on ainakin yksi virhe: -0,5 p.

Tyypillisiä virheitä: ilmentymämuuttujaa kutsutaan luokkametodista käsin ilmentymää luomatta, yksityistä luokkamuuttujaa kutsutaan toisesta luokasta käsin. Tässä kategoriassa sakotettiin myös yrityksestä viitata toisen metodin paikallisiin muuttujiin.

Arvauksen arvoaluetarkistus ynnä muut ratkaisun osat

Arvaukselle tehdään arvoaluetarkistus, mutta tarkistuksen toteutuksessa on ainakin yksi virhe tai pelin koodissa on jokin muu merkittävä virhe josta ei ole toisaalla sakotettu: -0,5 p.

Arvauksen arvoaluetarkistus ei yleensä ollut välttämätön osa ratkaisua: liian pienet tai suuret arvot saatiin tulkita myös yksinkertaisesti vääriksi arvauksiksi. Joissakin tapauksissa ratkaisun myöhempien osien toiminta kuitenkin edellytti arvoaluetarkistuksen tekemistä. Tyypillisiä virheitä: tarkistettiin lailliseen arvoalueeseen kuulumattomuutta kun piti tarkistaa kuulumista tai päinvastoin, koodi sisälsi mahdottomia lauseita (esimerkiksi taulu.length--).

Tehtävä © 2004 Arto Wikla. Esimerkkivastaus ja arvosteluperusteet © 2004 Joni Salmi. Tämän materiaalin käyttö on sallittu vain yksityishenkilöille opiskelutarkoituksissa. Materiaalin käyttö muihin tarkoituksiin, kuten kaupallisilla tai muilla kursseilla, on kielletty.