Tehtävät viikolle 3

Pakolliset tehtävät on merkitty harmaalla taustavärillä. Pakollisuus tarkoittaa, että kyseiset tehtävät ovat erityisen oleellisia ja niiden tekeminen on hyvin suositeltavaa. Jos joskus jokin "pakollinen" tehtävä jää tekemättä, kurssi ei kuitenkaan kaadu siihen.

Henkilö ja sen perilliset

Henkilo

Tee luokka henkilö joka toimii seuraavan pääohjelman yhteydessä

    public static void main(String[] args) {
        Henkilo pekka = new Henkilo("Pekka Mikkola", "Korsontie 1 03100 Vantaa");
        Henkilo esko = new Henkilo("Esko Ukkonen", "Mannerheimintie 15 00100 Helsinki");
        System.out.println( pekka );
        System.out.println( esko );
    }

siten että tulostuu

Pekka Mikkola
  Korsontie 1 03100 Vantaa
Esko Ukkonen
  Mannerheimintie 15 00100 Helsinki

Opiskelija

Tee luokka Opiskelija joka perii luokan Henkilo.

Opiskelijalla on aluksi 0 opintopistettä. Aina kun opiskelija opiskelee, kasvaa opintopistemäärä. Toteuta luokka siten, että seuraava pääohjelma:

    public static void main(String[] args) {
        Opiskelija olli = new Opiskelija("Olli", "Ida Albergintie 1 00400 Helsinki");
        System.out.println( olli );
        System.out.println( "opintopisteitä " + olli.opintopisteita() );
        olli.opiskele();
        System.out.println( "opintopisteitä "+ olli .opintopisteita() );
    }

tuottaa tulostuksen:

Olli
  Ida Albergintie 1 00400 Helsinki
opintopisteitä 0
opintopisteitä 1

Opiskelijalle toString

Edellisessä tehtävässä Opiskelija perii toString-metodin luokalta Henkilo. Perityn metodin voi myös ylikirjoittaa, eli korvata omalla versiolla. Tee nyt Opiskelija:lle oma versio toString:istä joka toimii seuraavan esimerkin mukaan:

    public static void main(String[] args) {
        Opiskelija olli = new Opiskelija("Olli", "Ida Albergintie 1 00400 Helsinki");
        System.out.println( olli );
        olli.opiskele();
        System.out.println( olli );
    }

Tulostuu:

Olli (0 op)
  Ida Albergintie 1 00400 Helsinki
Olli (1 op)
  Ida Albergintie 1 00400 Helsinki

Huom: voit toimia joko luvun 12.2 tai luvun 12.3 tapaan.

Opettaja

Tee Henkilo:n perivä luokka Opettaja. Opettajalla on palkka joka tulostuu opettajan merkkijonoesityksessä.

Testaa, että seuraava pääohjelma

    public static void main(String[] args) {
        Opettaja pekka = new Opettaja("Pekka Mikkola", "Korsontie 1 03100 Vantaa", 1200);
        Opettaja esko = new Opettaja("Esko Ukkonen", "Mannerheimintie 15 00100 Helsinki", 5400);
        System.out.println( pekka );
        System.out.println( esko );

        Opiskelija olli = new Opiskelija("Olli", "Ida Albergintie 1 00400 Helsinki");
        for ( int i=0; i < 25; i++ )
            olli.opiskele();
        System.out.println( olli );
    }

Aikaansaa tulostuksen

Pekka Mikkola palkka 1200 euroa/kk
  Korsontie 1 03100 Vantaa
Esko Ukkonen palkka 5400 euroa/kk
  Mannerheimintie 15 00100 Helsinki
Olli (25 op)
  Ida Albergintie 1 00400 Helsinki

Kaikki Henkilot listalle

Ota pääohjelmassasi käyttöön seuraavan tyyppinen lista:

   ArrayList<Henkilo> henkilot = new ArrayList<Henkilo>();

Lisää opettajia ja opiskelijoita listalle. Tee metodi, jonka saa parametrikseen listan ja tulostaa listan sisällä olevat henkilöt. Metodin esittelyrivi ja kutsutapa:

    private static void tulostaLaitoksenHenkilot(ArrayList<Henkilo> henkilot){
       // tulostetaan kaikki listan henkilöt
    }

    public static void main(String[] args) {
        ArrayList<Henkilo> henkilot = new ArrayList<Henkilo>();
        // lisätään listalle opiskelijoita ja opettajia
 
        tulostaLaitoksenHenkilot(henkilot);
    }

Tuotevarasto

Meillä on valmiina käytössämme luokka Varasto jonka tarjoamat konstruktorit ja metodit ovat seuraavat:

Tälle luokalle aletaan nyt tehdä täydennyksiä aliluokkana.

Hae Varasto-luokan koodi täältä.

Tuotevarasto, vaihe 1

Luokka Varasto osaa jo hoidella tuotteen määrän käsittelyn. Nyt tuotteelle halutaan lisäksi tuotenimi ja nimen käsittelyvälineet. Luokan voisi tietysti ohjelmoida alusta alkaen uudelleen, mutta miksi ihmeessä? Ohjelmoidaan Tuotevarasto Varaston aliluokaksi! Toteutetaan ensin pelkkä yksityinen kenttä tuotenimelle, konstruktori ja getteri nimikentälle:

Muista millä tavoin konstruktori voi ensi toimenaan suorittaa yliluokan konstruktorin!

Käyttöesimerkki:

...
Tuotevarasto mehu = new Tuotevarasto("Juice", 1000.0, 1000.0);
mehu.otaVarastosta(11.3);
System.out.println(mehu.getNimi()); // Juice
System.out.println(mehu);           // saldo = 988.7, vielä tilaa 11.3
...

Tulostus siis:

Juice
saldo = 988.7, vielä tilaa 11.3

Tuotevarasto, vaihe 2

Kuten edellisestä esimerkistä näkee, Tuotevarasto-olion perimä toString() ei tiedä (tietenkään!) mitään tuotteen nimestä. Asialle on tehtävä jotain! Lisätään samalla myös setteri tuotenimelle:

Uuden toString()-metodin voisi toki ohjelmoida käyttäen yliluokalta perittyjä gettereitä, joilla perittyjen, mutta piilossa pidettyjen kenttien arvoja saa käyttöönsä. Koska yliluokkaan on kuitenkin jo ohjelmoitu tarvittava taito varastotilanteen merkkiesityksen tuottamiseen, miksi nähdä vaivaa sen uudelleen ohjelmointiin. Käytä siis hyväksesi perittyä toStringiä.

Muista miten korvattua metodia voi kutsua aliluokassa!

Käyttöesimerkki:

...
Tuotevarasto mehu = new Tuotevarasto("Juice", 1000.0, 1000.0);
mehu.otaVarastosta(11.3);
System.out.println(mehu.getNimi()); // Juice
mehu.lisaaVarastoon(1.0);
System.out.println(mehu);           // Juice: saldo = 989.7, vielä tilaa 10.299999999999955
...

Tulostus siis:

Juice
Juice: saldo = 989.7, vielä tilaa 10.299999999999955

Tuotevarasto ja muutoshistoria

Toisinaan saattaa olla kiinostavaa tietää, millä tavoin jonkin tuotteen varastotilanne muuttuu: onko varasto usein hyvin vajaa, ollaanko usein ylärajalla, onko vaihelu suurta vai pientä, jne. Varustetaan siksi Tuotevarasto-luokka taidolla muistaa tuotteen määrän muutoshistoriaa.

Aloitetaan apuvälineen laadinnalla.

Muutoshistoria.java, vaihe 1

Muutoshistorian muistamisen voisi toki toteuttaa suoraankin ArrayList<Double>-oliona luokassa Tuotevarasto, mutta nyt laaditaan kuitenkin oma erikoistettu väline tähän tarkoitukseen. Väline toteutetaan kapseloimalla ArrayList<Double>-olio.

Muutoshistoria-luokan julkiset konstruktorit ja metodit:

Havainnollista olioiden luontia ja käyttöä pienellä ohjelmalla.

Muutoshistoria.java, vaihe 2

Täydennä Muutoshistoria-luokkaa analyysimetodein:

Havainnollista uusien metodien käyttöä ja toimivuutta pienellä ohjelmalla.

Muutoshistoria.java, vaihe 3

Täydennä Muutoshistoria-luokkaa analyysimetodein:

Jos et muista miten varianssi lasketaan, katso mallia ohjelmoinnin perusteiden viikon 3 tehtävästä 5.5

Havainnollista uusien metodien käyttöä ja toimivuutta pienellä ohjelmalla.

MuistavaTuotevarasto, vaihe 1

Toteuta luokan Tuotevarasto aliluokkana MuistavaTuotevarasto. Uusi versio tarjoaa vanhojen lisäksi varastotilanteen muutoshistoriaan liittyviä palveluita. Historiaa hallitaan Muutoshistoria-oliolla.

Julkiset konstruktorit ja metodit:

Huomaa että tässä esiversiossa historia ei vielä toimi kunnolla; nyt vasta vain aloitussaldo muistetaan.

Käyttöesimerkki:

// tuttuun tapaan:
MuistavaTuotevarasto mehu = new MuistavaTuotevarasto("Juice", 1000.0, 1000.0);
mehu.otaVarastosta(11.3);
System.out.println(mehu.getNimi()); // Juice
mehu.lisaaVarastoon(1.0);
System.out.println(mehu);           // Juice: saldo = 989.7, vielä tilaa 10.3
...
// mutta vielä historia() ei toimi kunnolla:
System.out.println(mehu.historia()); // [1000.0]
   // saadaan siis vasta konstruktorin asettama historian alkupiste...
...

Tulostus siis:

Juice
Juice: saldo = 989.7, vielä tilaa 10.299999999999955
[1000.0]

MuistavaTuotevarasto, vaihe 2

On aika aloittaa historia! Ensimmäinen versio ei historiasta tiennyt kuin alkupisteen. Täydennä luokkaa metodein

Käyttöesimerkki:

// tuttuun tapaan:
MuistavaTuotevarasto mehu = new MuistavaTuotevarasto("Juice", 1000.0, 1000.0);
mehu.otaVarastosta(11.3);
System.out.println(mehu.getNimi()); // Juice
mehu.lisaaVarastoon(1.0);
System.out.println(mehu);           // Juice: saldo = 989.7, vielä tilaa 10.3
...
// mutta nyt on historiaakin:
System.out.println(mehu.historia()); // [1000.0, 988.7, 989.7]
...

Tulostus siis:

Juice
Juice: saldo = 989.7, vielä tilaa 10.299999999999955
[1000.0, 988.7, 989.7]

Muista miten korvaava metodi voi käyttää hyväkseen korvattua metodia!

MuistavaTuotevarasto, vaihe 3

Täydennä luokkaa metodilla

Käyttöesimerkki:

MuistavaTuotevarasto mehu = new MuistavaTuotevarasto("Juice", 1000.0, 1000.0);
mehu.otaVarastosta(11.3);
mehu.lisaaVarastoon(1.0);
//System.out.println(mehu.historia()); // [1000.0, 988.7, 989.7]

mehu.tulostaAnalyysi();

Metodi tulostaAnalyysi kirjoittaa ilmoituksen tyyliin:

Tuote: Juice
Historia: [1000.0, 988.7, 989.7]
Suurin tuotemäärä: 1000.0
Pienin tuotemäärä: 988.7
Keskiarvo: 992.8

MuistavaTuotevarasto, vaihe 4

Täydennä analyysin tulostus sellaiseksi, että mukana ovat myös muutoshistorian suurin muutos ja historian varianssi.

Havainnollista kehiteltyä analyysiraporttia pienellä esimerkkiohjelmalla.

Käännöskone

Tietojenkäsittelytieteen vanha unelma on ollut luoda ohjelma, joka kääntää tekstiä luonnollisesta kielestä toiseen. Suoraviivainen lähestymistapa ongelmaan on jakaa käännettävä teksti sanoiksi ja etsiä niille vastineet sanakirjasta. Tässä tehtäväsarjassa luodaan ohjelma, joka kääntää tekstiä suomesta englanniksi.

Sanojen erottelu

Tee ohjelma, joka erottaa sanat käyttäjän antamasta lauseesta. Käytännössä sanat tunnistaa siitä, että niiden välissä on välilyönti.

Ohjelman tulisi toimia seuraavasti:

Anna lause: Tämä ohjelma on kääntäjä.
Tämä
ohjelma
on
kääntäjä.

Vihje: String-luokan metodista split lienee tässä tapauksessa hyötyä. Voit esim. googlata ohjeita splitin käyttöön.

Sanojen siistiminen

Edellisen tehtävän ohjelman tuloksena sanojen osana voi olla suuria kirjaimia ja välimerkkejä (pisteitä, pilkkuja jne.). Nämä saattaisivat häiritä kääntämistä, joten muuta ohjelmaa niin, että se muuttaa kaikki kirjaimet pieniksi ja poistaa välimerkit.

Ohjelman tulisi toimia seuraavasti:

Anna lause: Tämä ohjelma on kääntäjä.
tämä
ohjelma
on
kääntäjä

Sanakirja mukaan

Liitä ohjelmaan mukaan sanakirja viikon 1 tehtävän 3 tyyliin. Lisää sanakirjaan yleisimpiä sanoja, kuten seuraavat:

Muuta edellistä ohjelmaasi niin, että se hakee sanoille käännöksen sanakirjasta ja tulostaa käännökset samalle riville. Päätä sopiva toiminta, kun käännös puuttuu sanakirjasta.

Ohjelman tulisi toimia seuraavasti:

Anna lause: Tämä ohjelma on kääntäjä.
Käännös: this program is translator

Huom: kuten jo muutaman kerran on ollut puhetta, ei ole kovin järkevää laittaa kaikkea toiminnallisuutta mainiin. Yritä tehdä ohjelmasi rakenteesta selkeä! Jatkamme tehtävää ensi viikolla ja jos nyt teet hyvää jälkeä, on ensi viikolla helpompi hymyillä.

Ohjelman parantelu

Lisää ohjelmaan enemmän sanoja ja yritä saada siitä mahdollisimman hyvä kääntäjä. Mitä ongelmia kohtaat ohjelman toteutuksessa?

Ohjelman tulisi toimia seuraavasti:

Anna lause: Ohjelmointi on kivaa!
Käännös: programming is fun
Anna lause: Alussa olivat suo, kuokka ja Jussi.
Käännös: in the beginning were swamp hoe and john

Maidon elämää, vaihe 1

Maatiloilla on lypsäviä eläimiä, jotka tuottavat maitoa. Maatilat eivät itse käsittele maitoa, vaan se kuljetetaan Maitoautoilla meijereille. Meijerit ovat yleisiä maitotuotteita tuottavia rakennuksia. Jokainen meijeri erikoistuu yhteen tuotetyyppiin, esimerkiksi Juustomeijeri tuottaa Juustoa, Voimeijeri tuottaa voita ja Maitomeijeri tuottaa maitoa.

Rakennetaan maidon elämää kuvaava simulaattori.

Maitosäiliö

Jotta maito pysyisi tuoreena, täytyy se säilöä sille tarkoitettuun säiliöön. Säiliöitä valmistetaan sekä oletustilavuudella 2000 litraa, että asiakkaalle räätälöidyllä tilavuudella. Toteuta luokka Maitosailio jolla on seuraavat konstruktorit ja metodit.

Toteuta Maitosailio-luokalle myös toString()-metodi, jolla kuvaat sen tilaa. Ilmaistessasi säiliön tilaa toString()-metodissa, pyöristä litramäärät ylöspäin käyttäen Math-luokan tarjoamaa ceil()-metodia.

Testaa maitosailiötä seuraavalla ohjelmapätkällä

        Maitosailio sailio = new Maitosailio();
        sailio.otaSailiosta(100);
        sailio.lisaaSailioon(25);
        sailio.otaSailiosta(5);
        System.out.println(sailio);

        sailio = new Maitosailio(50);
        sailio.lisaaSailioon(100);
        System.out.println(sailio);

Ohjelman tulostuksen tulee olla seuraavankaltainen

20.0/2000.0
50.0/50.0

Huomaa että kutsuttaessa System-luokan out-olioon liittyvää println()-metodia, joka saa parametrikseen Object-tyyppisen muuttujan, tulostus käyttää Maitosailio-luokassa korvattua toString()-metodia! Tässä on kyse polymorfismista, eli ajonaikaisesta käytettävien metodien päättelystä.

Lehmä

Saadaksemme maitoa tarvitsemme myös lehmiä. Lehmällä on nimi ja utareet. Lehman utareiden tilavuus on satunnainen luku väliltä 15 ja 40 -- luokkaa Random voi käyttäää satunnaislukujen arpomiseen, esimerkiksi int luku = 15 + new Random().nextInt(26);. Lehma toteuttaa myös rajapinnat Lypsava, joka kuvaa lypsämiskäyttäytymistä, ja Eleleva, joka kuvaa elelemiskäyttäytymistä.

public interface Lypsava {
    public double lypsa();
}

public interface Eleleva {
    public void eleleTunti();
}

Lehmää lypsettäessä sen koko maitovarasto tyhjennetään jatkokäsittelyä varten. Lehmän elellessä sen maitovarasto täyttyy hiljalleen. Suomessa maidontuotannossa käytetyt lehmät tuottavat keskimäärin noin 25-30 litraa maitoa päivässä. Simuloidaan tätä tuotantoa tuottamalla noin 0.7 - 2 litraa tunnissa.

Jos lehmälle ei anneta nimeä, valitse sille nimi satunnaisesti seuraavasta listasta.

    private static String[] NIMIA = new String[]{
        "Anu", "Arpa", "Essi", "Heluna", "Hely",
        "Hento", "Hilke", "Hilsu", "Hymy", "Ihq", "Ilme", "Ilo",
        "Jaana", "Jami", "Jatta", "Laku", "Liekki",
        "Mainikki", "Mella", "Mimmi", "Naatti",
        "Nina", "Nyytti", "Papu", "Pullukka", "Pulu",
        "Rima", "Soma", "Sylkki", "Valpu", "Virpi"};

Toteuta luokka Lehma ja testaa sen toimintaa seuraavan ohjelmapätkän avulla. Toteuta luokalle myos metodi toString(), joka palauttaa sitä kuvaavan merkkijonon.

        Lehma lehma = new Lehma();
        System.out.println(lehma);


        Eleleva elelevaLehma = lehma;
        elelevaLehma.eleleTunti();
        elelevaLehma.eleleTunti();
        elelevaLehma.eleleTunti();
        elelevaLehma.eleleTunti();
        
        System.out.println(lehma);

        Lypsava lypsavaLehma = lehma;
        lypsavaLehma.lypsa();
        
        System.out.println(lehma);
        System.out.println("");

        lehma = new Lehma("Ammu");
        System.out.println(lehma);
        lehma.eleleTunti();
        lehma.eleleTunti();
        System.out.println(lehma);
        lehma.lypsa();
        System.out.println(lehma);

Ohjelman tulostus on erimerkiksi seuraavanlainen.

Liekki    0.0/23.0
Liekki    16.0/23.0
Liekki    0.0/23.0
Ammu      0.0/53.0
Ammu      9.0/53.0
Ammu      0.0/53.0


Lypsyrobotti

Nykyaikaisilla maatiloilla lypsyrobotit hoitavat lypsämisen. Jotta lypsyrobotti voi lypsää lypsävää otusta, tulee lypsyrobotin olla kiinnitetty maitosäiliöön.

Toteuta luokka Lypsyrobotti ja testaa sitä seuraavan ohjelmapätkän avulla. Varmista että lypsyrobotti voi lypsää kaikkia Lypsava-rajapinnan toteuttavia olioita!

        Lypsyrobotti lypsyrobotti = new Lypsyrobotti();
        Lehma lehma = new Lehma();
        lypsyrobotti.lypsa(lehma);
        
        System.out.println("");
        
        Maitosailio sailio = new Maitosailio();
        lypsyrobotti.setMaitosailio(sailio);
        System.out.println("Säiliö: " + sailio);

        for(int i = 0; i < 2; i++) {
            System.out.println(lehma);
            System.out.println("Elellään..");
            for(int j = 0; j < 5; j++) {
                lehma.eleleTunti();
            }
            System.out.println(lehma);

            System.out.println("Lypsetään...");
            lypsyrobotti.lypsa(lehma);
            System.out.println("Säiliö: " + sailio);
            System.out.println("");
        }

Ohjelman tulostus on esimerkiksi seuraavanlainen.

Ei kiinnitetty maitosäiliöön, ei voida lypsää.

Säiliö: 0.0/2000.0
Mella     0.0/23.0
Elellään..
Mella     17.0/23.0
Lypsetään...
Säiliö: 17.0/2000.0

Mella     0.0/23.0
Elellään..
Mella     13.0/23.0
Lypsetään...
Säiliö: 29.0/2000.0

Navetta

Lehmät hoidetaan (eli tässä tapauksessa lypsetään) navetassa. Alkukantaisissa navetoissa on maitosäiliö ja tilaa yhdelle lypsyrobotille. Huomaa että lypsyrobottia asennettaessa se kytketään juuri kyseisen navetan maitosäiliöön. Jos navetassa ei ole lypsyrobottia, ei siellä voida myöskään hoitaa lehmiä. Toteuta luokka Navetta jolla on seuraavat konstruktorit ja metodit.

Lisää navettaan myös aksessori maitosailiölle. Metodi hoida lypsää parametrina annetun lehmän tai lehmäkokoelman Lypsyrobotin avulla. Toteuta navetalle myös toString()-metodi, joka palautaa sen sisältämän maitosäiliön tilan.

Collection on Javan oma rajapinta joka kuvaa kokoelmien käyttäytymistä. Esimerkiksi luokat ArrayList ja LinkedList toteuttavat rajapinnan Collection. Jokaista Collection-rajapinnan toteuttavaa ilmentymää voi myös iteroida for-each-tyyppisesti.

Testaa luokkaa Navetta seuraavan ohjelmapätkän avulla. Älä hermoile luokasta LinkedList, se toimii ulkoapäin katsottuna kuin ArrayList, mutta sen kapseloima toteutus on hieman erilainen. Tästä lisää tietorakenteet kurssilla!

    Navetta navetta = new Navetta(new Maitosailio());
        System.out.println("Navetta: " + navetta);

        Lypsyrobotti robo = new Lypsyrobotti();
        navetta.asennaLypsyrobotti(robo);

        Lehma ammu = new Lehma();
        ammu.eleleTunti();
        ammu.eleleTunti();

        navetta.hoida(ammu);
        System.out.println("Navetta: " + navetta);

        LinkedList<Lehma> lehmaLista = new LinkedList();
        lehmaLista.add(ammu);
        lehmaLista.add(new Lehma());

        for(Lehma lehma: lehmaLista) {
            lehma.eleleTunti();
            lehma.eleleTunti();
        }

        navetta.hoida(lehmaLista);
        System.out.println("Navetta: " + navetta);

Tulostuksen tulee olla esimerkiksi seuraavanlainen:

Navetta: 0.0/2000.0
Navetta: 5.0/2000.0
Navetta: 17.0/2000.0

Maatila

Maatilalla on omistaja ja siihen kuuluu navetta sekä joukko lehmiä. Maatila toteuttaa myös aiemmin nähdyn rajapinnan Eleleva, jonka metodia eleleTunti()-kutsumalla kaikki maatilaan liittyvät lehmät elelevät tunnin. Toteuta luokka maatila siten, että se toimii seuraavien esimerkkiohjelmien mukaisesti.

	Maatila maatila = new Maatila("Esko", new Navetta(new Maitosailio()));
        System.out.println(maatila);

Odotettu tulostus:

Maatilan omistaja: Esko
Navetan maitosäiliö: 0.0/2000.0
Ei lehmiä.

	Maatila maatila = new Maatila("Esko", new Navetta(new Maitosailio()));
        maatila.lisaaLehma(new Lehma());
        maatila.lisaaLehma(new Lehma());
        maatila.lisaaLehma(new Lehma());
        System.out.println(maatila);

Odotettu tulostus:

Maatilan omistaja: Esko
Navetan maitosäiliö: 0.0/2000.0
Lehmät:
        Naatti    0.0/19.0
        Hilke     0.0/30.0
        Sylkki    0.0/29.0

        Maatila maatila = new Maatila("Esko", new Navetta(new Maitosailio()));

        maatila.lisaaLehma(new Lehma());
        maatila.lisaaLehma(new Lehma());
        maatila.lisaaLehma(new Lehma());

        maatila.eleleTunti();
        maatila.eleleTunti();

Odotettu tulostus:

Maatilan omistaja: Esko
Navetan maitosäiliö: 0.0/2000.0
Lehmät:
        Heluna    6.0/17.0
        Rima      8.0/42.0
        Ilo       8.0/25.0
        Maatila maatila = new Maatila("Esko", new Navetta(new Maitosailio()));
        Lypsyrobotti robo = new Lypsyrobotti();
        maatila.asennaNavettaanLypsyrobotti(robo);

        maatila.lisaaLehma(new Lehma());
        maatila.lisaaLehma(new Lehma());
        maatila.lisaaLehma(new Lehma());

        maatila.eleleTunti();
        maatila.eleleTunti();

        maatila.hoidaLehmat();

        System.out.println(maatila);

Odotettu tulostus:

Maatilan omistaja: Esko
Navetan maitosäiliö: 18.0/2000.0
Lehmät:
        Hilke     0.0/30.0
        Sylkki    0.0/45.0
        Hento     0.0/54.0

Maatilasimulaattori

Luodaan ohjelma jolla voidaan simuloida maatilan toimintaa. Simulaattori kysyy ensiksi maatilan omistajan nimeä, jonka jälkeen kysytään maatilalla olevien nautojen määrä. Kun nämä ovat tiedossa luodaan maatila ja siihen liittyvät lehmät (muista asettaa maatilalle myös navetta, ja navetalle lypsyrobotti!). Kun maatila on luotu, käyttäjälle annetaan 3 vaihtoehtoa: "Elele tunti", "Hoida lehmät" ja "Lopeta" -- jokainen toiminto laukaistaan numerolla. Pyydetty numero kannattaa lukea Scannerin ja Integer-luokan yhteistyöllä seuraavasti:

int luku = Integer.parseInt(scanner.nextLine());

Toteuta maatilasimulaattori, seuraavassa esimerkkitulostus:

Anna maatilan omistajan nimi: Esko
Montako nautaa? 2

Maatilan omistaja: Esko
Navetan maitosäiliö: 0.0/2000.0
Lehmät:
        Mella     0.0/54.0
        Nina      0.0/21.0

Komennot:
1 - Elele tunti
2 - Hoida lehmat
muut - Lopeta
? 1

Maatilan omistaja: Esko
Navetan maitosäiliö: 0.0/2000.0
Lehmät:
        Mella     4.0/54.0
        Nina      5.0/21.0

Komennot:
1 - Elele tunti
2 - Hoida lehmat
muut - Lopeta
? 1

Maatilan omistaja: Esko
Navetan maitosäiliö: 0.0/2000.0
Lehmät:
        Mella     7.0/54.0
        Nina      8.0/21.0

Komennot:
1 - Elele tunti
2 - Hoida lehmat
muut - Lopeta
? 2

Maatilan omistaja: Esko
Navetan maitosäiliö: 15.0/2000.0
Lehmät:
        Mella     0.0/54.0
        Nina      0.0/21.0

Komennot:
1 - Elele tunti
2 - Hoida lehmat
muut - Lopeta
? x

Kiitos!

Tikkupeli

Tikkupelin alussa pöydällä on kasa tikkuja. Pelissä on kaksi pelaajaa, jotka ottavat pöydältä vuorotellen yhden, kaksi tai kolme tikkua. Pelin häviää se, joka joutuu ottamaan viimeisen tikun.

Kaksinpeli

Ohjelmoi tikkupeli kahdelle pelaajalle. Pelin alussa kysytään tikkujen määrä pöydällä, minkä jälkeen pelaajat poistavat vuorotellen tikkuja. Ohjelman tulee valvoa, että pelaajat poistavat aina yhden, kaksi tai kolme tikkua. Lopuksi ohjelma ilmoittaa pelin voittajan.

Ohjelman tulisi toimia seuraavasti:

Tervetuloa tikkupeliin!

Montako tikkua on pöydällä? 10

Pöydällä on 10 tikkua.
Kuinka monta poistat, pelaaja 1? 2

Pöydällä on 8 tikkua.
Kuinka monta poistat, pelaaja 2? 1

Pöydällä on 7 tikkua.
Kuinka monta poistat, pelaaja 1? 4
Virheellinen määrä!
Kuinka monta poistat, pelaaja 1? 3

Pöydällä on 4 tikkua.
Kuinka monta poistat, pelaaja 2? 3

Pöydällä on 1 tikkua.
Kuinka monta poistat, pelaaja 1? 0
Virheellinen määrä!
Kuinka monta poistat, pelaaja 1? 2
Virheellinen määrä!
Kuinka monta poistat, pelaaja 1? 1

Voittaja on pelaaja 2!

Tekoäly

Tee pelistä uusi versio, jossa toinen pelaaja on tekoäly. Tekoälyn strategiaksi kelpaa tässä vaiheessa, että se poistaa aina yhden tikun.

Ohjelman tulisi toimia seuraavasti:

Tervetuloa tikkupeliin!

Montako tikkua on pöydällä? 10

Pöydällä on 10 tikkua.
Kuinka monta poistat? 2

Pöydällä on 8 tikkua.
Tekoäly poistaa 1 tikkua.

Pöydällä on 7 tikkua.
Kuinka monta poistat? 3

Pöydällä on 4 tikkua.
Tekoäly poistaa 1 tikkua.

Pöydällä on 3 tikkua.
Kuinka monta poistat? 2

Pöydällä on 1 tikkua.
Tekoäly poistaa 1 tikkua.

Voittaja olet sinä!

Ilmaukset "tekoäly poistaa 1 tikkua" ja "pöydällä on 1 tikkua" eivät muuten ole kovin tyylikkäitä. Voit halutessasi korjata asian.

Hyvä tekoäly

Tikkupeliin on olemassa todella yksinkertainen voittostrategia eli varma keino voittaa peli. Tekoälyn riittää varmistaa, että aina kun pelaaja on vuorossa, pöydällä olevien tikkujen määrän jakojäännös 4:llä on 1.

Esimerkiksi jos pöydällä on 7 tikkua, tekoälyn kannattaa poistaa 2 tikkua, koska silloin jäljelle jää 5 tikkua ja 5:n jakojäännös 4:llä on 1. Vastaavasti jos pöydällä on 56 tikkua, tekoälyn kannattaa poistaa 3 tikkua, koska silloin jäljelle jää 53 tikkua ja 53:n jakojäännös 4:llä on 1.

Jos pöydällä olevan tikkujen määrän jakojäännös 4:llä on 1, tekoäly on heikossa asemassa eikä sen auta muuta kuin poistaa jokin määrä tikkuja ja toivoa, että pelaaja tekee virheen.

Muuta tekoälyä niin, että se noudattaa tätä strategiaa.

Ohjelman tulisi toimia seuraavasti:

Tervetuloa tikkupeliin!

Montako tikkua on pöydällä? 10

Pöydällä on 10 tikkua.
Kuinka monta poistat? 2

Pöydällä on 8 tikkua.
Tekoäly poistaa 3 tikkua.

Pöydällä on 5 tikkua.
Kuinka monta poistat? 3

Pöydällä on 2 tikkua.
Tekoäly poistaa 1 tikkua.

Pöydällä on 1 tikkua.
Kuinka monta poistat? 1

Voittaja on tekoäly!

Miksi strategia toimii?

Perustele pätevästi, miksi edellisen tehtävän strategia on toimiva. Miksi tekoälyn kannattaa pyrkiä siihen, että tikkujen määrän jakojäännös 4:llä on 1?

Muutos pelissä

Entä jos tikkupelissä pitäisi poistaa aina yksi, kolme tai neljä tikkua? Muuta pelisi toiminta tällaiseksi ja suunnittele toimiva tekoäly.

Luku suomeksi

Luku suomeksi

Tee ohjelma, joka kysyy käyttäjältä kokonaisluvun ja tulostaa sen suomeksi. Lukuväli 0–1000000 riittää hyvin.

Ohjelman suoritus voi näyttää seuraavalta:

Anna luku: 2713
kaksituhattaseitsemänsataakolmetoista