Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: Java: Miten toteuttaa tehtävänanto?

AbuRamal [21.02.2020 17:25:35]

#

Pitäis tehdä ohjelma joka toteuttaa tehtävänannon. Valmista koodia on annettu, mutta sitä ei pysty muokkaamaan.
Tehtävänanto on seuraavanlainen:

lainaus:

Kaupunki on nyt uudelleenrakennettu. Juhiessasi kapakassa kaupungin uudelleenkorjausten päättymistä, kuulit kiinnostavan juorun. Noitien ilmestyminen ei ollut sattumaa, vaan tapauksen takana onkin vallanhimoinen velho. Velholla on jo uusia suunnitelmia kuningaskunnan pään menoksi, ja hänet täytyy nyt nujertaa, jotta rauha palautuu.

Rakenna siis taikurikoulu ja kouluta taikuri, joka pystyy suorittamaan tämän tehtävän. Alla muutama vinkki maalattavaksi tehtävässä alkuun pääsemiseksi:

Vinkki 1: Koodaa siis oma luokkasi taikurikokelaalle. Luokka saa olla minkä niminen vain ja siinä saa olla mitä vain metodeita, kunhan se perii Henkilön ja toteuttaa rajapinnat Loitsiva ja TarinanSankari, joiden kautta valmis koodi ottaa viittaukset olioon. (Tässä siis yksi käyttö polymorfismille!) Koodaa myös luokka Taikurikoulu, joka pystyy.palauttamaan olion koodaamastasi luokasta.

Vinkki 2: Kaksintaistelussa on kolme vuoroa, ensimmäiset kaksi velho käyttää puolustavasti, ja vasta kolmannella hänet on mahdollista nujertaa.

Vinkki 3: Velhoa nujertaessa kannattaa kiinnittää huomiota erityisesti viittausten tyyppeihin ja mikä olio viitauksen takaa oikeasti löytyy.

Haluttu tulostus:

Velho ei onnistunut nujertamaan sinua
Onnistuit nujertamaan velhon!

Valmis koodi(ei pysty muokkaamaan):

/**
 * ----------------LUOKAT--------------
 */
public class Test {

    public static void main(String[] args) {
        TarinanSankari t = Taikurikoulu.koulutaUusiTaikuri();

        if (!(t instanceof Loitsiva)) {
            System.out.println("Taikurikokelaasi ei osaa loitsia"
                    + ", ja velho nujersi hänet saman tien!");
            return;
        }

        Velho v = new Velho(500, t);

        for (int i = 0; i < 3; i++) {
            t.suoritaVuoro(i);
            v.suoritaVuoro(i);
        }

        Henkilo taikuri = (Henkilo) t;
        if (taikuri.annaElama() <= 0) {
            System.out.println("Velho nujersi sinut!");
        } else {
            System.out.println("Velho ei onnistunut nujertamaan sinua");
        }

        if (v.annaElama() <= 0) {
            System.out.println("Onnistuit nujertamaan velhon!");
        } else {
            System.out.println("Velho jäi vielä henkiin.");
        }
    }
}

abstract class Henkilo implements Tahdattava, Kuoleva {
    //Attribuutti on private, jotta tehtävä olisi vaikeampi...
    private int elama;

    public Henkilo(int elama) {
        this.elama = elama;
    }

    @Override
    public int annaElama() {
        return elama;
    }

    private void muutaElamaa(int muutos) {
        elama += muutos;
    }

    @Override
    public void vastaanota(Loitsu l) {

        try {
            reagoiLoitsuun(l);

            if (l instanceof Vahingoittava) {
                muutaElamaa(-((Vahingoittava) l).getVahinko());
            }

            if (l instanceof Parantava) {
                muutaElamaa(((Parantava) l).getParannuksenMaara());
            }
        } catch (TaikaPoikkeus e) {
            e.selvitaTaikapoikkeus();
        }
    }

    protected abstract void reagoiLoitsuun(Loitsu l);

    public abstract void suoritaVuoro(int vuoro);
}

/**
 * Vaarallinen velho, joka sinun tulee voittaa kaksintaistelussa.
 */
class Velho extends Henkilo implements Loitsiva {

    private TarinanSankari taikuri;
    private boolean suojattu = false;

    public Velho(int elama, TarinanSankari taikuri) {
        super(elama);
        this.taikuri = taikuri;
    }

    @Override
    protected void reagoiLoitsuun(Loitsu l) {
        if (l instanceof Suojaloitsu) {
            suojattu = true;
        }

        if (suojattu && l.getLahde() instanceof TarinanSankari) {
            throw new TaikaPoikkeus(
                    "Velho on taikonut suojaloitsun; taikasi eivät tehoa velhoon.");
        }
    }

    @Override
    public void loitsi(Loitsu t) {
        t.taio();
    }

    @Override
    public void suoritaVuoro(int vuoro) {
        switch (vuoro) {
        case 0:
            loitsi(new Suojaloitsu(this, this));
            break;
        case 1:
            loitsi(new Parannusloitsu(this, this));
            break;
        case 2:
            loitsi(new Tappoloitsu(this, taikuri));
        }
    }

    /*
     *  Velho osaa kolme loitsua.
     */
    private class Tappoloitsu extends Loitsu implements Vahingoittava {

        public Tappoloitsu(Loitsiva lahde, Tahdattava kohde) {
            super(lahde, kohde);
        }

        @Override
        public int getVahinko() {
            if (getKohde() instanceof Kuoleva) {
                return ((Kuoleva) getKohde()).annaElama();
            }
            return 0;
        }
    }

    private class Parannusloitsu extends Loitsu implements Parantava {

        public Parannusloitsu(Loitsiva lahde, Tahdattava kohde) {
            super(lahde, kohde);
        }

        @Override
        public int getParannuksenMaara() {
            return 100;
        }

    }

    private class Suojaloitsu extends Loitsu {

        public Suojaloitsu(Loitsiva lahde, Tahdattava kohde) {
            super(lahde, kohde);
        }

    }
}

abstract class Loitsu {
    // Attributtit ovat private, koska jokainen konkreettinen loitsu tietää vain
    // oman toimintansa. Taikojen toiminta ei yleensä ole riippuvainen
    // taikojasta tai kohteesta.
    // Havainnointimetodit ovat kuitenkin käytössä, jos jokin taika todella
    // tarvitsee tätä tietoa
    private Loitsiva lahde;
    private Tahdattava kohde;

    public Loitsu(Loitsiva lahde, Tahdattava kohde) {
        super();
        this.lahde = lahde;
        this.kohde = kohde;
    }

    public Tahdattava getKohde() {
        return kohde;
    }

    public Loitsiva getLahde() {
        return lahde;
    }

    public void taio() {
        kohde.vastaanota(this);
    }
}

/**
 * ----------------RAJAPINNAT--------------
 */

// Rajapinta kuoleville olioille
interface Kuoleva {
    int annaElama();
}

// Rajapinta tähdättäville olioille
interface Tahdattava {
    void vastaanota(Loitsu l);
}

// Rajapinta olioille, jotka osaavat loitsia
interface Loitsiva {
    void loitsi(Loitsu l);
}

// Rajapinta olioille, jotka pystyvät vahingoittamaan.
interface Vahingoittava {
    int getVahinko();
}

// Rajapinta olioille, jotka voivat parantaa
interface Parantava {
    int getParannuksenMaara();
}

// Jos olio voi olla tarinan sankari, sen tulee toteuttaa tämä rajapinta.
interface TarinanSankari extends Tahdattava {
    void suoritaVuoro(int vuoro);
}

/**
 * ----------------POIKKEUKSET--------------
 */
class TaikaPoikkeus extends RuntimeException {

    public TaikaPoikkeus(String arg0) {
        super(arg0);
    }

    public void selvitaTaikapoikkeus() {
        System.out.println(getMessage());
    }
}

Ongelmana se, että pitäisi loitsi tuohon velhoon, mutten tiedä, miten voin kohdistaa loitsun siihen. Kohteena nyt tuo uusi velho olio, jottei tule mitään erroreita, mutta pitäis saada jotenkin tuo main metodissa oleva velho kohteeksi.

oma koodini näyttää tältä:

class Taikuri extends Henkilo implements Loitsiva, TarinanSankari {
    public Loitsiva kohde = new Velho(100, this);
    public Taikuri(int elama) {
        super(elama);
    }

    public void loitsi(Loitsu l) {
        l.taio();
    }

    public void suoritaVuoro(int vuoro) {
        switch (vuoro) {
            case 0:
            case 1:
                loitsi(new Parannusloitsu(this, this));
                break;
            case 2:
                loitsi(new Tappoloitsu(this, (Tahdattava) kohde));
        }
    }

    public void reagoiLoitsuun(Loitsu l) {
        if(l.getLahde() instanceof Velho){
            kohde = l.getLahde();
        }
    }
    @Override
    public int annaElama() {
        return 10000;
    }


    //LOITSUT
    private class Tappoloitsu extends Loitsu implements Vahingoittava {

        public Tappoloitsu(Loitsiva lahde, Tahdattava kohde) {
            super(lahde, kohde);
        }

        @Override
        public int getVahinko() {
            if (getKohde() instanceof Kuoleva) {
                return ((Kuoleva) getKohde()).annaElama();
            }
            return 10;
        }
    }

    private class Parannusloitsu extends Loitsu implements Parantava {

        public Parannusloitsu(Loitsiva lahde, Tahdattava kohde) {
            super(lahde, kohde);
        }

        @Override
        public int getParannuksenMaara() {
            return 1000;
        }

    }


}

class Taikurikoulu {
    public static Taikuri koulutaUusiTaikuri() {

        return new Taikuri(500);
    }
}

Metabolix [25.02.2020 00:15:06]

#

Tehtävän ”ratkaisu” on käyttää väärin olio-ohjelmoinnin aukkoja kyseisessä koodissa. Tehtävässä on kolme osaa:

Taikuri ei tiedä velhosta mitään, ennen kuin velholta tulee loitsu. Taikurin täytyy siis tehdä kaikki temppunsa metodissa reagoiLoitsuun siinä vaiheessa, kun loitsun lähteenä on velho.

Ainoa taikuriin osuva loitsu on tappoloitsu. Taikurin pitää jotenkin estää tappoloitsun vaikutus. Koska elämän vähennys tapahtuu Henkilo-luokassa, tähän kohtaan ei voi vaikuttaa. Kuitenkin kuoleminen tarkastetaan metodilla annaElama, joten Taikuri voi toteuttaa metodin niin, että elämää olisi muka aina jäljellä.

Velhon suojaloitsu estää loitsut, jotka tulevat sankarilta. Pitää siis osua velhoon loitsulla, jonka lähde on muu kuin sankari. Lyhyt ratkaisu on kääntää velhon oma tappoloitsu velhoa kohti. Loitsun kohteeksi on merkitty edelleen sankari, ja tämän mukaan valitaan vahingon määrä, joten pitää laittaa sankarin annaElama-metodiin tarpeeksi suuri luku, jotta loitsu tappaa varmasti myös velhon.

Ratkaisu muistuttaa enemmän bugien etsimistä ja väärinkäyttöä kuin oikeaa olio-ohjelmointia, joten ainakaan tästä tehtävästä ei kannata ottaa mallia.

class Taikuri extends Henkilo implements Loitsiva, TarinanSankari {
    public Taikuri() {
        // Turha mutta pakollinen.
        super(0);
    }

    @Override
    public void suoritaVuoro(int vuoro) {
        // Turha mutta pakollinen.
    }

    @Override
    public void loitsi(Loitsu l) {
        // Turha mutta pakollinen.
    }

    @Override
    public int annaElama() {
        // Aina elossa. Tämä on myös velhoon tulevan vahingon määrä.
        return 100000;
    }

    @Override
    protected void reagoiLoitsuun(Loitsu l) {
        // Kun velho loitsii, velhon voi tappaa.
        if (l.getLahde() instanceof Velho) {
            ((Velho) l.getLahde()).vastaanota(l);
        }
    }
}

class Taikurikoulu {
    public static Taikuri koulutaUusiTaikuri() {
        return new Taikuri();
    }
}

Ratkaisua voisi ehkä jalostaa siten, että omalla vuorolla tehtäisiin jokin suojaloitsun tyyppinen viritelmä, joka oikeuttaisi tappoloitsun kääntämisen velhoa vastaan. Toisaalta koko ratkaisusta ei silti saa järkevää, vaan ainakin elämä pitää huijata, joten mielestäni voi surutta mennä helpoimman kautta muissakin asioissa. Ainoa syy kikkailuun on siis jokin moraalinen tehtävän henki.

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta