Tämä materiaali on lisensoitu Creative Commons BY-NC-SA-lisenssillä, joten voit käyttää ja levittää sitä vapaasti, kunhan alkuperäisten tekijöiden nimiä ei poisteta. Jos teet muutoksia materiaaliin ja haluat levittää muunneltua versiota, se täytyy lisensoida samanlaisella vapaalla lisenssillä. Materiaalien käyttö kaupalliseen tarkoitukseen on ilman erillistä lupaa kielletty.
Tekijät: Arto Vihavainen ja Matti Luukkainen
Kurssimme lähenee valitettavasti loppuaan. Tässä kohdassa on taas hyvä tutustua MOOC-kurssin suorittamiseen liittyviin mahdollisuuksiin. Kurssista on mahdollista saada esimerkiksi opintosuoritusmerkintä Helsingin yliopiston Avoimen yliopiston kautta maksutta.
Huom! Jos teet MOOCia väylänä yliopistoon, lue myös siihen liittyvä infosivu. Erityisesti, jos olet tehnyt vaaditun määrän tehtäviä annetussa aikataulussa, mutta et ole vielä saanut kutsua näyttökokeeseen, ota pikaisesti yhteyttä osoitteeseen mooc@cs.helsinki.fi.
Ohjelmointiparadigmalla tarkoitetaan ohjelmointikielen taustalla olevaa tapaa ajatella ja jäsentää ohjelman toiminta. Tällä kurssilla olemme keskittyneet lähinnä imperatiiviseen ohjelmointiin, missä koneelle kerrotaan askeleet, joita sen tulee ottaa, jotta haluttu tehtävä saadaan suoritettua. Konetta siis käsketään tekemään annettuja toimintoja.
Eräs imperatiivisen ohjelmoinnin osajoukko on proseduraalinen ohjelmointi, mitä harjoittelimme erityisesti kurssin alussa. Proseduraalisessa ohjelmoinnissa ohjelman tilaa pidetään yllä muuttujissa, ja mahdolliset metodit käsittelevät vain niille parametrina annettuja arvoja. Ohjelma suoritetaan askel askeleelta, ja koneelle kerrotaan sen määrittelemällä kielellä mitä pitäisi tapahtua. Esimerkiksi alla on kuvattu koneen ymmärtämällä kielellä muuttujien a ja b arvojen vaihtaminen.
int a = 10; int b = 15; // vaihdetaan muuttujien a ja b arvot int c = b; b = a; a = c;
Kun olio-ohjelmointia verrataan proseduraaliseen ohjelmointiin, muutamat oleelliset erot tulevat ilmi. Olio-ohjelmoinnissa olion tila voi periaatteessa muuttua mitä tahansa olion metodia käytettäessä, ja tuo tilan muutos voi vaikuttaa myös muiden olion metodien toimintaan -- ja sitä kautta ohjelman suorituksen osa-alueisiin. Olemme käyttäneet olioita kurssilla lähinnä myös tietokonetta käskyttäen, mutta myös toisenlaisia tapoja on olemassa.
Deklaratiivinen ohjelmointi on ohjelmointiparadigma, missä pyritään kuvailemaan mitä ohjelman tulee tehdä sen sijaan, että kuvattaisiin yksittäisiä suoritettavia käskyjä. Esimerkiksi funktionaalisessa ohjelmoinnissa, joka voidaan nähdä deklaratiivisen ohjelmoinnin osajoukkona, ohjelmoija keskittyy lähinnä halutun tiedon tunnistamiseen, sekä tiedon muutamiseen haluttuun muotoon.
Koska ohjelmointikielet tarjoavat nykyään tyypillisesti välineitä sekä imperatiiviseen että deklaratiiviseen ohjelmointiin, tapahtuu ohjelmien rakentaminen usein moniparadigmaisesti, missä imperatiivista ja deklaratiivista ohjelmointityyliä sekoitetaan tarvittaessa. Myös Java tarjoaa välineitä sekä imperatiiviseen että deklaratiiviseen ohjelmointiin.
Tutustutaan hieman näihin Javan ominaisuuksiin seuraavaksi.
Tarkastellaan kahta erilaista tapaa tulostaa listalla olevat alkiot. Ensimmäisenä vuorossa imperatiivinen lähestymistapa, missä käytämme for
-toistolausetta alkioiden läpikäyntiin sekä tuttua System.out.println
-komentoa yksittäisten alkioiden tulostamiseen.
List<Integer> lista = new ArrayList<>(); Collections.addAll(lista, 1, 3, 5, 7); for (int arvo: lista) { System.out.println(arvo); }
1 3 5 7
Deklaratiivisessa ohjelmointityylissä listalle kerrotaan mitä sen jokaiselle alkiolle tulee tehdä. Alla listalle sanotaan että sen jokainen alkio tulee antaa tulostusmetodille.
List<Integer> lista = new ArrayList<>(); Collections.addAll(lista, 1, 3, 5, 7); lista.forEach(System.out::println);
1 3 5 7
Komento forEach
antaa jokaisen listalla olevan alkion vuorollaan komennon parametrina olevalle metodille. Kuten huomaat, tulostuskomennossa on kaksoispisteet tulostusmetodin yhteydessä -- System.out::println
-- tällä tavalla annetaan viittaus metodiin, jolle listan alkiot annetaan. Käytössä olevat metodit eivät ole rajattu Javan omiin metodeihin kuten tulostuskutsuun, vaan voimme yhtä hyvin käyttää myös itse ohjelmoimiamme metodeja.
public class Apumetodit { public static void kerroKahdellaJaTulosta(int luku) { System.out.println(luku * 2); } }
Listan läpikäynnin yhteydessä ylläolevaa metodia kutsuttaisiin seuraavanlaisesti:
List<Integer> lista = new ArrayList<>(); Collections.addAll(lista, 1, 3, 5, 7); lista.forEach(Apumetodit::kerroKahdellaJaTulosta);
2 6 10 14
Kun tarkastelemme forEach
-kutsua tarkemmin, voimme oikeastaan antaa sille myös tiedon alkioiden käsittelytavasta. Tämä tieto annetaan anonyymien metodien avulla. Anonyymit metodit määritellään muodossa alkio -> kasittely
, esimerkiksi i -> System.out.println(i)
.
lista.forEach(i -> System.out.println(i));
Anonyymi metodi voi sisältää myös useampia komentoja, jolloin käytetään aaltosuluilla alkavaa ja loppuvaa lohkoa komentojen listaamiseen. Komennot suoritetaan järjestyksessä ylhäältä alas.
lista.forEach(i -> { if (i == 5) { System.out.print("Kapow "); } System.out.println(i); });
1 3 Kapow 5 7
Edelläoleva anonyymi metodi määsittelee annetulle alkiolle suoritettavan koodin sekä metodin parametrin. Se vastaa kutakuinkin seuraavaa staattista metodia.
public static void tulosta(int luku) { if (i == 5) { System.out.print("Kapow "); } System.out.println(i); }
Kutsun lista.forEach
yhteydessä oikeastaan luodaan virta syötteitä (Stream), jolle kaikki listalla olevat alkiot syötetään, ja joka ohjaa alkiot eteenpäin. Kutsu näyttää käytännössä seuraavalta:
lista.stream().forEach(System.out::println);
Tämä "stream" tarjoaa joukon metodeja, joiden avulla käsiteltävän listan alkioita voidaan käsitellä muokata ennen (esimerkiksi) tulostusta.
Stream tarjoaa esimerkiksi filter-metodin, jonka avulla voidaan rajata käsiteltävät alkiot. Alla rajataan käsittely vain alkioihin, joiden arvon on yli 5.
lista.stream().filter(i -> i > 5).forEach(System.out::println);
7
Rajaus on tehty anonyymillä metodilla, jossa alkion arvoa verrataan lukuun 5. Ylläoleva anonyymi metodi vastaa seuraavaa metodia:
public static boolean onSuurempiKuinViisi(int arvo) { return i > 5; }
Syötevirran erilaisilla map-metodeilla alkioita voidaan muuntaa toiseen muotoon. Esimerkiksi aiemmin tekemämme kahdella kertominen onnistuu map-metodin avulla.
lista.stream().filter(i -> i > 5).map(i -> i * 2).forEach(System.out::println);
14
Map-metodi voi sisältää myös lohkon, mutta, tällöin sille tulee erikseen määritellä return-komento.
lista.stream().map(x -> { if (x == 5) { return "Kapow " + 5; } return x; }).forEach(System.out::println);
Tämä komento erityisen kätevä esimerkiksi olioita läpikäytäessä. Metodin map
avulla voidaan myös valita metodin palauttama arvo. Esimerkiksi Henkilöiden nimet saisi seuraavalla tavalla -- olettaen, että käytössämme on Henkilo
-luokka, ja luokalla metodi getNimi
.
List<Henkilo> lista = new ArrayList<>(); // luetaan listalle henkilot lista.stream().map(h -> h.getNimi()).forEach(System.out::println);
Syötevirrat tarjoavat myös metodeja alkioiden muuntamiseksi tiettyyn tyyppiin. Esimerkiksi metodilla mapToInt tehdään muunnos lukuvirraksi (IntStream), jonka jälkeen pääsemme käsiksi lukuja sisältävän syötevirran tarjoamiin metodeihin, kuten keskiarvon laskemiseen.
Allaoleva lähdekoodi laskee kaikkien listalla olevien positiivisten alkioiden keskiarvon ja tulostaa sen.
List<Integer> lista = new ArrayList<>(); Collections.addAll(lista, 1, 3, 5, 7); lista.stream().filter(i -> i >= 0).mapToInt(i -> i).average().ifPresent(System.out::println);
3.5
IntStream-lukuvirran metodi average palauttaa OptionalDouble-tyyppisen alkion, joka tarjoaa metodin ifPresent
, jonka avulla kerrotaan mitä tehdään jos keskiarvon laskeminen onnistuu. Tässä tapauksessa keskiarvo annetaan tulostusmetodille.
Syötevirrassa käsiteltäviä alkioita voi myös kerätä collect-metodin avulla. Alla kerätään syötevirrasta alkiot, joiden arvon on yli 5.
List<Integer> luvut = lista.stream().filter(i -> i > 5).collect(Collectors.toList());
Oracle tarjoaa lisää oppaita tähän ohjelmointityyliin liittyen, hyvä lähtökohta lienee Lambda QuickStart.
Kurssin viimeinen viikko koostuu edellä olevan pikakatsauksen lisäksi kurssilla nähtyjen teemojen kertauksesta. Osa tehtävistä on jo tuttuja, osa uusia. Kaikista tehtävistä on tehty sellaisia, että että niissä ei ole testejä -- olet täysin vastuussa ohjelman toiminnasta -- voit kokeilla edellä näkemiäsi menetelmiä, tai kerrata aiemmin tutuksi tulleita käsitteitä. Palauttamalla tehtävän takaat että se toimii toivotusti, jolloin saat siitä myös pisteet. Kertaa tehtäviä tehdessäsi materiaalia -- kannattaa varata tehtäville myös ajatteluaikaa.
Huom! Vaikka olisit tehnyt tehtävän jo aiemmin, tee se silti uudestaan. Tee tehtävä nykyisen tietämyksesi perusteella, äläkä katso mahdollista aiempaa vastaustasi.
Tässä ohjelmassa harjoitellaan toistolauseiden, lukujen käyttöä sekä käyttäjän antaman syötteen lukemista. Tehtäväpohjassa ei ole automaattisia testejä, joten testeistä ei ole apua ohjelman rakentamisessa. Tehtävä on kolmen tehtäväpisteen arvoinen.
Toteuta ohjelma sademäärien keskiarvon laskemiseen
Ohjelman tulee lukea käyttäjän antamasta syötteestä sademääriä kunnes käyttäjä syöttää luvun 999999. Sademääriksi kelpaavat nollat ja positiiviset luvut. Käyttäjän syöttämät negatiiviset luvut ohitetaan.
Kun käyttäjä syöttää luvun 999999, ohjelman ohjelman tulee tulostaa käyttäjän syöttämien sademäärien keskiarvo tai ilmoitus siitä, ettei keskiarvoa voida laskea (koska ei syötetty yhtään sademäärää).
Käytä Javan Scanner-luokkaa tai muuta sinulle tuttua tapaa käyttäjän syötteen lukemiseen. Voit olettaa, että syötteet ovat desimaalilukuja (double
).
Sinun ei tarvitse huomioida mitenkään tapauksia, joissa käyttäjä antaa tyhjän syötteen tai syöttää luvuiksi kelpaamaattomia merkkejä.
Luku 999999 annetaan vain lopettamisen merkiksi, eikä sitä tule ottaa mukaan keskiarvoon.
Alla on annettu esimerkki siitä, miltä ohjelman käyttäminen voi näyttää. Käyttäjän antamat syötteet on merkitty punaisella värillä. Sinun EI ole pakko tuottaa pilkulleen samanlaista tulostetta, mutta ohjelman on laskettava oikea tulos syötteen perusteella tähän tapaan.
Anna sademääriä, lopeta luvulla 999999. Anna sademäärä: 1000 Anna sademäärä: 2000.50 Anna sademäärä: 0 Anna sademäärä: -100 Anna sademäärä: 999.50 Anna sademäärä: 999999 Sademäärien keskiarvo on 1000.0.
Tässä taas keskiarvoa ei saada määritettyä:
Anna sademääriä, lopeta luvulla 999999. Anna sademäärä: 999999 Yhtään sademäärää ei syötetty.
Huom! Kuten edellisessä tehtässä; vaikka olisit tehnyt tehtävän jo aiemmin, tee se silti uudestaan. Tee tehtävä nykyisen tietämyksesi perusteella, äläkä katso mahdollista aiempaa vastaustasi.
Tehtävä vastaa kolmea yksiosaista tehtävää.
Tässä tehtävässä suunnittelet ja toteutat tietokannan lintubongareille. Tietokanta sisältää lintuja, joista jokaisella on nimi (merkkijono) ja latinankielinen nimi (merkkijono). Tämän lisäksi tietokanta laskee kunkin linnun havaintokertoja.
Ohjelmasi täytyy toteuttaa seuraavat komennot:
Lisaa
- lisää linnun (huom: komennon nimessä ei ä-kirjainta!)Havainto
- lisää havainnonTilasto
- tulostaa kaikki linnutNayta
- tulostaa yhden linnun (huom: komennon nimessä ei ä-kirjainta!)Lopeta
- lopettaa ohjelmanLisäksi virheelliset syötteet pitää käsitellä. (Ks. Simo
alla). Tässä vielä esimerkki ohjelman toiminnasta:
? Lisaa Nimi: Korppi Latinankielinen nimi: Corvus Corvus ? Lisaa Nimi: Haukka Latinankielinen nimi: Dorkus Dorkus ? Havainto Mikä havaittu? Haukka ? Havainto Mikä havaittu? Simo Ei ole lintu! ? Havainto Mikä havaittu? Haukka ? Tilasto Haukka (Dorkus Dorkus): 2 havaintoa Korppi (Corvus Corvus): 0 havaintoa ? Nayta Mikä? Haukka Haukka (Dorkus Dorkus): 2 havaintoa ? Lopeta
Huom! Ohjelmasi rakenne on täysin vapaa. Testaamme vain että Paaohjelma
luokan main
-metodi toimii kuten tässä on kuvailtu. Yritä miettiä millaisista luokista ja olioista olisi hyötyä ohjelman toteuttamisessa!
Tässä tehtaväsarjassa hahmotellaan pomon ja siihen liittyvien alaisten tallentamista. Vaikka tehtäväsarjassa on tarkemmat ohjeet tehtävän tekemiseen, toimivat testit kuten edellisissä tapauksissa -- niitä ei ole. Tehtävä on yhteensä viiden tehtäväpisteen arvoinen.
Tee rajapinta tai abstrakti luokka Tyontekija
, joka vaatii seuraavien metodien olemassaolon:
public String haeNimi()
public int haePalkka()
public int laskeAlaiset()
public void lisaaKieli(String kieli)
public boolean onkoTaitoa(String kieli)
true
, jos työntekijä tai työntekijän alainen hallitsee annettua ohjelmointikieltä, ja muuten arvon false
.Tee luokka Koodari
, joka toteuttaa rajapinnan Tyontekija
. Luokan konstruktorissa annetaan nimi ja palkka. Koodarin alaisten määrä on aina 0.
Testaa koodarin toimintaa ainakin seuraavalla koodilla:
Koodari aapeli = new Koodari("Aapeli", 1000); System.out.println(aapeli.haeNimi()); System.out.println(aapeli.haePalkka()); System.out.println(aapeli.laskeAlaiset()); aapeli.lisaaKieli("Java"); aapeli.lisaaKieli("Python"); System.out.println(aapeli.onkoTaitoa("Cobol")); System.out.println(aapeli.onkoTaitoa("Java")); System.out.println(aapeli.onkoTaitoa("Python"));
Koodin tulostuksen tulisi olla seuraava:
Aapeli 1000 0 false true true
Tee luokka Pomo
, joka toteuttaa rajapinnan Tyontekija
. Lisää luokkaan vielä seuraava metodi:
public void lisaaAlainen(Tyontekija alainen)
Testaa pomon toimintaa ainakin seuraavalla koodilla:
Koodari aapeli = new Koodari("Aapeli", 1000); Pomo maija = new Pomo("Maija", 2000); maija.lisaaAlainen(aapeli); System.out.println(maija.haeNimi()); System.out.println(maija.haePalkka());
Koodin tulostuksen tulisi olla seuraava:
Maija 2000
Muuta luokan Pomo
metodi laskeAlaiset
toimivaksi. Sen tulee laskea pomon alaisten, alaisten alaisten jne. määrät.
Testaa luokan toteutusta ainakin seuraavalla koodilla:
Koodari jenna = new Koodari("Jenna", 1000); Koodari kalle = new Koodari("Kalle", 1000); Koodari pauli = new Koodari("Pauli", 1000); Koodari arto = new Koodari("Arto", 1000); Koodari matti = new Koodari("Matti", 1000); Pomo jarmo = new Pomo("Jarmo", 2000); jarmo.lisaaAlainen(jenna); jarmo.lisaaAlainen(kalle); jarmo.lisaaAlainen(pauli); Pomo pekka = new Pomo("Pekka", 2000); pekka.lisaaAlainen(arto); pekka.lisaaAlainen(matti); Pomo maija = new Pomo("Maija", 4000); maija.lisaaAlainen(jarmo); maija.lisaaAlainen(pekka); System.out.println(kalle.laskeAlaiset()); System.out.println(jarmo.laskeAlaiset()); System.out.println(pekka.laskeAlaiset()); System.out.println(maija.laskeAlaiset());
Koodi vastaa seuraavaa tilannetta:
Maija / \ Jarmo Pekka / | \ / \ Jenna Kalle Pauli Arto Matti
Koodin tulostuksen tulisi olla seuraava:
0 3 2 7
Muuta luokan Pomo
metodi onkoTaitoa
toimivaksi. Sen tulee palauttaa true
, jos pomo itse tai joku pomon alaisista, alaisten alaisista jne. hallitsee annettua ohjelmointikieltä.
Testaa toteutustasi seuraavalla koodilla:
Koodari jenna = new Koodari("Jenna", 1000); Koodari kalle = new Koodari("Kalle", 1000); Koodari pauli = new Koodari("Pauli", 1000); Koodari arto = new Koodari("Arto", 1000); Koodari matti = new Koodari("Matti", 1000); Pomo jarmo = new Pomo("Jarmo", 2000); jarmo.lisaaAlainen(jenna); jarmo.lisaaAlainen(kalle); jarmo.lisaaAlainen(pauli); Pomo pekka = new Pomo("Pekka", 2000); pekka.lisaaAlainen(arto); pekka.lisaaAlainen(matti); Pomo maija = new Pomo("Maija", 4000); maija.lisaaAlainen(jarmo); maija.lisaaAlainen(pekka); jenna.lisaaKieli("Fortran"); kalle.lisaaKieli("Prolog"); kalle.lisaaKieli("C"); pauli.lisaaKieli("Haskell"); arto.lisaaKieli("Java"); matti.lisaaKieli("Java"); jarmo.lisaaKieli("Ruby"); pekka.lisaaKieli("C++"); maija.lisaaKieli("Cobol"); System.out.println(maija.onkoTaitoa("C")); System.out.println(maija.onkoTaitoa("Cobol")); System.out.println(maija.onkoTaitoa("Python")); System.out.println(maija.onkoTaitoa("Java"));
Koodin tulostuksen tulisi olla seuraava:
true true false true
Tehtäväpohjassa tulevassa tiedostossa sanalista.txt on Kotimaisten kielten keskuksen julkaisema nykysuomen sanalista, joka sisältää yli 90000 suomen kielen sanaa. Tässä tehtäväsarjassa tutkitaan listassa olevia sanoja ohjelman avulla.
Palauta tehtävä kun olet saanut kaikki osat valmiiksi. Tehtävä on yhteensä yhdeksän tehtäväpisteen arvoinen.
Tehtävissä voi ilmetä merkistöongelmia testejä suorittaessa. Tällöin tulee Scanneria luotaessa määrittää merkistöksi "UTF-8" (esim. Scanner lukija = new Scanner(new File("tiedosto"), "UTF-8")).
Tee luokka Sanatutkimus
, joka sisältää listan sanoja. ArrayList
sopii hyvin tietorakenteeksi. Lisää luokkaan seuraavat metodit:
public Sanatutkimus(String tiedostonimi)
public int sanojenMaara()
public boolean onkoSanaa(String sana)
true
, jos sana on listassa, ja muuten false
.Testaa luokkaasi seuraavalla koodilla:
Sanatutkimus tutkimus = new Sanatutkimus("sanalista.txt"); System.out.println(tutkimus.sanojenMaara()); System.out.println(tutkimus.onkoSanaa("porkkana")); System.out.println(tutkimus.onkoSanaa("porkana"));
91591 true false
Lisää luokkaan Sanatutkimus
seuraava metodi:
public int laskeSanat(int pituus)
Seuraava koodi testaa luokkaasi:
Sanatutkimus tutkimus = new Sanatutkimus("sanalista.txt"); System.out.println(tutkimus.laskeSanat(8)); System.out.println(tutkimus.laskeSanat(15));
Koodin tulostuksen tulisi olla seuraava:
8412 4411
Lisää luokkaan Sanatutkimus
seuraava metodi:
public void pituustilasto()
Seuraava koodi testaa luokkaasi:
Sanatutkimus tutkimus = new Sanatutkimus("sanalista.txt"); tutkimus.pituustilasto();
Koodin tulostuksen tulisi olla seuraava:
1 0 2 25 3 191 .... 28 4 29 1 30 1
Lisää luokkaan Sanatutkimus
seuraava metodi:
public int laskeKirjaimet(char kirjain)
Seuraava koodi testaa luokkaasi:
Sanatutkimus tutkimus = new Sanatutkimus("sanalista.txt"); System.out.println(tutkimus.laskeKirjaimet('a')); System.out.println(tutkimus.laskeKirjaimet('h'));
Koodin tulostuksen tulisi olla seuraava:
112291 21396
Lisää luokkaan Sanatutkimus
seuraava metodi:
public void kirjaintilasto()
Seuraava koodi testaa luokkaasi:
Sanatutkimus tutkimus = new Sanatutkimus("sanalista.txt"); tutkimus.kirjaintilasto();
Koodin tulostuksen tulisi olla seuraava:
a 112291 b 1496 c 213 ... å 1 ä 31475 ö 7250
Lisää luokkaan Sanatutkimus
seuraava metodi:
public ArrayList<String> haePituudella(int pituus)
Seuraava koodi testaa luokkaasi:
Sanatutkimus tutkimus = new Sanatutkimus("sanalista.txt"); System.out.println(tutkimus.haePituudella(28));
Koodin tulostuksen tulisi olla seuraava:
[muotokuvanpaljastustilaisuus, sotasyyllisyysoikeudenkäynti, tietojenkäsittelyjärjestelmä, ylioppilastutkintolautakunta]
Lisää luokkaan Sanatutkimus
seuraava metodi:
public ArrayList<String> haeOsalla(String osa)
Seuraava koodi testaa luokkaasi:
Sanatutkimus tutkimus = new Sanatutkimus("sanalista.txt"); System.out.println(tutkimus.haeOsalla("koodi"));
Koodin tulostuksen tulisi olla seuraava:
[juovakoodi, konekoodinen, koodi, koodijärjestelmä, koodinlukija, koodinumero, koodisanoma, koodittaa, koodittaja, kooditus, lyhytvalintakoodi, tavarakoodi, viivakoodi]
Lisää luokkaan Sanatutkimus
seuraava metodi:
public ArrayList<String> haePalindromit()
Seuraava koodi testaa luokkaasi:
Sanatutkimus tutkimus = new Sanatutkimus("sanalista.txt"); System.out.println(tutkimus.haePalindromit());
Koodin tulostuksen tulisi olla seuraava:
[ajaja, akka, ala, alla, autioitua, ele, enne, hah, heh, huh, hyh, häh, imaami, isi, niin, oho, olo, opo, otto, piip, pop, sadas, sammas, sees, siis, sus, suuruus, sylys, sytytys, syys, syöppöys, tuut, tyyt, tööt, utu, yty, älä, ämmä, ässä]
Lisää luokkaan Sanatutkimus
seuraava metodi:
public ArrayList<String> haeRistikkoon(String pohja)
Ideana on, että ristikon ratkaisija voi hyödyntää metodia, kun osassa ruuduista on kirjain mutta joistakin ruuduista puuttuu vielä.
Seuraava koodi testaa luokkaasi:
Sanatutkimus tutkimus = new Sanatutkimus("sanalista.txt"); System.out.println(tutkimus.haeRistikkoon("s???is??s"));
Koodin tulostuksen tulisi olla seuraava:
[saalistus, salaisuus, sateisuus, satoisuus, sekaisuus, sijaisuus, siloisuus, sisäistys, sopuisuus, sotaisuus, suloisuus, suoristus, syntisyys, säröisyys, sävyisyys]
Tehtävänanto on mukaelma osoitteessa http://nifty.stanford.edu/2014/laaksonen-vihavainen-game-of-sticks/handout.html olevasta tehtävästä.
Tässä tehtävässä toteutetaan peli, jossa pelaajat nostavat vuorotellen tikkuja pinosta. Viimeisen tikun nostaja häviää pelin. Kun tämä toiminnallisuus on valmis, toteutetaan tekoäly, joka oppii pelistä.
Tehtävä on yhteensä kahdeksan tehtäväpisteen arvoinen.
Kuvaus
Tikkupelissä kaksi pelaajaa ovat pöydän äärellä. Pöydällä on kasa tikkuja, ja kumpikin pelaaja ottaa vuorollaan kasasta yhdestä kolmeen tikkua. Se, joka ottaa viimeisen tikun, häviää pelin.
Allaoleva on esimerkki pelistä.
Tehtävä kannattaa tehdä useammassa pienemmässä osassa:
Toteuta peli, missä kaksi pelaajaa voivat pelata tikkupeliä. Allaolevat esimerkit antavat kuvan siitä, miten pelin tulee toimia.
Esimerkki 1
Tervetuloa tikkupeliin! Kuinka monta tikkua pöydällä on aluksi (10-100)? 10 Pöydällä on 10 tikkua. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 3 Pöydällä on 7 tikkua. Pelaaja 2: Kuinka monta tikkua nostat (1-3)? 3 Pöydällä on 4 tikkua. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 3 Pöydällä on 1 tikku. Pelaaja 2: Kuinka monta tikkua nostat (1-3)? 1 Pelaaja 2, hävisit :/
Esimerkki 2
Tervetuloa tikkupeliin! Kuinka monta tikkua pöydällä on aluksi (10-100)? 500 Valitse luku väliltä 10-100 Kuinka monta tikkua pöydällä on aluksi (10-100)? 3 Valitse luku väliltä 10-100 Kuinka monta tikkua pöydällä on aluksi (10-100)? 50 Pöydällä on 50 tikkua. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 3 Pöydällä on 47 tikkua. Pelaaja 2: Kuinka monta tikkua nostat (1-3)? 55 Valitse luku väliltä 1-3 Pelaaja 2: Kuinka monta tikkua nostat (1-3)? -2 Valitse luku väliltä 1-3 Pelaaja 2: Kuinka monta tikkua nostat (1-3)? 3 Pöydällä on 44 tikkua. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 3 Pöydällä on 41 tikkua. Pelaaja 2: Kuinka monta tikkua nostat (1-3)? 1 Pöydällä on 40 tikkua. ... Pöydällä on 2 tikkua. Pelaaja 2: Kuinka monta tikkua nostat (1-3)? 1 Pöydällä on 1 tikku. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 1 Pelaaja 1, hävisit :/
Toteuta yllä kuvattu peli. Pohdi jo tässä vaiheessa pelaajan toiminnallisuutta ja sitä, että miten pelaajan voisi vaihtaa tekoälyyn.
Kavereita vastaan pelaaminen on varsin jees, mutta tietojenkäsittelytieteen kurssilla on pakko fiilistellä :). Aivan kuten opiskellessa oppii siistejä asioita, myös tietokoneet voivat oppia siistejä asioita. Luodaan tekoäly tikkupelille.
Yksi tapa tekoälyn tekemiseen olisi tehdä matemaattinen analyysi ongelmasta, ja luoda tekoäly analyysin pohjalta. Tehdään kuitenkin toisin, ja luodaan tekoäly joka osaa oppia pelistä sitä pelaamalla.
Esimerkki
Alla olevassa esimerkissä on 10 tikkua pelin alussa. Hattujen sisällöt tekoälylle ovat seuraavat:
hattu | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
pallot | 1,2,3 | 1,2,3 | 1,2,3 | 1,2,3 | 1,2,3 | 1,2,3 | 1,2,3 | 1,2,3 | 1,2,3 | 1,2,3 |
Peli voi edetä esimerkiksi seuraavasti:
Pelin jälkeen tekoälyn tilanne on seuraava:
hattu | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
sisältö | 1,2,3 | 1,2,3 | 1,2,3 | 1,2 | 1,2,3 | 1,2,3 | 1,3 | 1,2,3 | 1,2,3 | 1,2,3 |
hattujen vieressä | 3 | 2 |
Koska tekoäly voittaa pelin, se asettaa kaksi kappaletta vieressä olevia palloja takaisin hattuihin. Pelitilanne on tämän jälkeen seuraava:
hattu | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
sisältö | 1,2,3 | 1,2,3 | 1,2,3 | 1,2,3,3 | 1,2,3 | 1,2,3 | 1,2,2,3 | 1,2,3 | 1,2,3 | 1,2,3 |
Nyt tekoäly ottaisi todennäköisemmin kolme tikkua joa pöydällä on 4 tikkua, ja kaksi tikkua jos pöydällä on 7 tikkua.
Toteuta yllä kuvattu tekoäly ja muokkaa peliä siten, että pelaaja voi valita pelaako se tekoälyä vai normaalia pelaajaa vastaan.
Alla oleva esimerkki näyttää miltä pelin pitäisi näyttää kun olet saanut tämän osan valmiiksi.
Tervetuloa tikkupeliin! Kuinka monta tikkua pöydällä on aluksi (10-100)? 10 Vaihtoehdot: Pelaa kaveria vastaan (1) Pelaa tietokonetta vastaan (2) Minkä vaihtoehdon valitset (1-2)? 2 Pöydällä on 10 tikkua. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 3 Pöydällä on 7 tikkua. Tekoäly valitsee 2 Pöydällä on 5 tikkua. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 3 Pöydällä on 2 tikkua. Tekoäly valitsee 2 Tekoäly häviää Pelaa uudestaan (1 = kyllä, 0 = ei)? 1 Pöydällä on 10 tikkua. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 3 Pöydällä on 7 tikkua. Tekoäly valitsee 1 Pöydällä on 6 tikkua. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 1 Pöydällä on 5 tikkua. Tekoäly valitsee 3 Pöydällä on 2 tikkua. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 1 Pöydällä on 1 tikku. Tekoäly valitsee 2 Tekoäly häviää Pelaa uudestaan (1 = kyllä, 0 = ei)? 0 Kiitos!
Teit edellisessä osassa tekoälyn, joka oppii pelatessaan. Sitä vastaan pelatessa huomataan kuitenkin, että oppiminen vaatii paljon työtä, ja tekoäly joutuu tekemään huomattavasti toistoja ennenkuin se pärjää. Tässä osassa muokkaat ohjelmaa siten, että pelaaja voi päättää pelaako se tekoälyä vastaan joka ei ole harjoitellut, vai kouliintunutta tekoälyä vastaan.
Jotta tekoälyä voisi koulia, joudut toteuttamaan ohjelman, missä tekoäly ensin pelaa toista tekoälyä vastaan. Tämän jälkeen tekoäly pääsee pelaamaan pelaajaa vastaan. Tekoälyn harjoittelu vaatii hetken -- ehkä noin satatuhatta peliä -- kannattanee kokeilla erilaisia harjoitusjaksojen pituuksia.
Allaoleva esimerkki näyttää miten peli toimii kun tekoäly voi harjoitella myös toista tekoälyä vastaan.
Tervetuloa tikkupeliin! Kuinka monta tikkua pöydällä on aluksi (10-100)? 10 Vaihtoehdot: Pelaa kaveria vastaan (1) Pelaa tietokonetta vastaan (2) Pelaa kouliintunutta tietokonetta vastaan (2) Minkä vaihtoehdon valitset (1-3)? 3 Treenataan... Pöydällä on 10 tikkua. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 3 Pöydällä on 7 tikkua. Tekoäly valitsee 3 Pöydällä on 4 tikkua. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 2 Pöydällä on 2 tikkua. Tekoäly valitsee 1 Pöydällä on 1 tikku. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 1 Pelaaja 1, hävisit :/ Pelaa uudestaan (1 = kyllä, 0 = ei)? 1 Pöydällä on 10 tikkua. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 2 Pöydällä on 8 tikkua. Tekoäly valitsee 1 Pöydällä on 7 tikkua. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 2 Pöydällä on 5 tikkua. Tekoäly valitsee 1 Pöydällä on 4 tikkua. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 1 Pöydällä on 3 tikkua. Tekoäly valitsee 2 Pöydällä on 1 tikku. Pelaaja 1: Kuinka monta tikkua nostat (1-3)? 1 Pelaaja 1, hävisit :/ Pelaa uudestaan (1 = kyllä, 0 = ei)? 0 Kiitos!
Kun olet saanut ohjelman toimimaan, haasta kaverisi pelaamaan tekoälyn vastaan! :) -- eräs mallivastaus tehtävään julkaistaan vasta kurssin jälkeen..
Olemme saaneet paljon arvokasta palautetta TMC:n kautta. Näin kurssin viimeisenä kysymyksenä haluaisimme koko kurssin sisältöä koskevan palautteen. Anna palaute täyttämällä täältä löytyvä lomake. Palaute on anonyymi.
Jotta saat merkatuksi tämän tehtävän, aja tehtävän TMC-testit ja lähetä tehtävä palvelimelle.
Vaikka olemme saaneet paljon arvokasta palautetta TMC:n kautta yksittäisistä tehtävistä, palauttaessasi tätä, kirjoitathan kommenttikenttään palautteen koko MOOC-kurssista. Mainitsethan siinä, mikäli et halua palautettasi julkaistavan anonyymisti sivustoillamme. Vain osa palautteesta tullaan julkaisemaan.