VARAKOPIO WIKI-SIVUSTA.

KÄYTÄ WIKI-SIVUA HETI KUN SE TOIMII

Muita varalinkkejä:

Demot » Demo 7, 29.10.2012

Tämän kerran tehtävät vaikuttavat pitkiltä, mutta itse asiassa vastausten itse kirjoitettujen rivien lukumäärä tulee olemaan kurssin lyhimpiä. Eli lue tehtävät huolella ja keskity, tehtävät eivät ole niin vaikeita kuin tehtävien pituudesta voisi päätellä. Tehtävät 5, 6, ja B1 ovat muuten kanssa helppoja tehtäviä.

Vaikeaa

Mikä tehtävissä oli tällä kertaa vaikeaa? Kirjoita max 5 riviä pitkä tiedosto vaikeaa.txt johon kerrot mikä oli vaikeaa? Palauta yhtenä DemoWWW:n "tehtävänä" mutta max 0.2 pistettä :-)

PP

PP1

Tee aliohjelma, joka kopioi annetun taulukon alkioittan uuteen taulukkoon. Voit jatkaa pp-ohjausten tehtävää PP2 ja käyttää siinä annettua taulukkoa. Tee myös aliohjelma, joka kopioi taulukon alkioit uuteen taulukkoon mutta kääntäen siten, että ensimmäinen alkio menee uuden taulukon viimeiseksi alkioksi, toinen alkio uuden taulukon toiseksi viimeiseksi jne.

Pääohjelmasi voisi olla vaikkapa

        int[] taul = new int[] { 4, 7, 17, -5, 13, 1, 0, 0, 0, 2 };
        int[] uusiTaulu = new int[taul.Length];

        KopioiTaulukko(taul, uusiTaulu);
        Console.WriteLine(String.Join(",", uusiTaulu));

        KopioiTaulukkoKaantaen(taul, uusiTaulu);
        Console.WriteLine(String.Join(",", uusiTaulu));

Ylläoleva pääohjelma tulostaisi

4,7,17,-5,13,1,0,0,0,2
2,0,0,0,1,13,-5,17,7,4

Vinkki: Taulukot, M:15. Taulukot ja M: 16.4 for-silmukka

PP2

Tee funktio(aliohjelma) LaskeMerkkijonot, jolle annetaan parametrina merkkijonotaulukko, sekä etsittävä merkkijono. Funktio laskee, kuinka monta etsittävää merkkijonoa taulukosta löytyi.

Pääohjelma voisi näyttää seuraavalta:

String[] jonot = {"Koira", "Keksi", "Koira", "Papukaija", "Kissa", "Koira", "Keksi"};
int montakoLoytyi = LaskeMerkkijonot(jonot, "Koira"); // montakoLoytyi = 3

Vinkki: M:15. Taulukot, M: 16.4 for-silmukka ja M: 13.2 if-rakenne

V1

Tee Ville-tehtävät: 5.9-5.13,9.8 Muista: Villen käyttöohje ja Ville-tehtävien palauttamisohjeet.

VIDEO 1

Muista että voit saada demopisteitä myös indeksoimalla luento/demovideoita, ks: https://trac.cc.jyu.fi/projects/ohj1/wiki/s2012/videohakemisto. Lisää vähintään 3-linkkiä ja kerro tiedostoon video.txt mitkä linkit lisäsit. Tehtävän numeroksi VIDEO1. Jatkossa voi joka demokerralle merkitä vastaavasti yhden VIDEO-tehtävän kolmesta linkistä.

TDD1

Jos tarkistat vähintään kahden funktion toiminnan automaattisella testillä (ComTest), saat merkitä yhden lisäpisteen. Palauta DemoWWW:ssä tekstitiedosto TDD.txt (jonka siis arvostelet yhden pisteen arvoiseksi), missä kerrot minkä tehtävän ja minkä funktion/funktioiden toiminnan testasit. Voit antaa samassa tiedostossa palautetta ja kehitysehdotuksia Comtestin käytöstä.

Vector-luokan taulukoita voit tehdä testiä varten esim:

Vector[] luvut = { new Vector(1,2),new Vector(3,4),
                   new Vector(5,2),new Vector(5,5) };

Kohtuuvaivalla testattavia tehtäviä tällä kertaa ovat: 3, 5, 6, B1, B2 (testit valmiina), G1.

Pohjatiedosto varsinaisille tehtäville

Ota pohjaksi tiedosto: Kuvaaja.cs. Tehtävät 1-X perustuvat tämän muuttamiseen ja täydentämiseen.

Voit ottaa pohjan kahdella eri tavalla (suosittelen ensimmäistä, mutta niin kauan kun TracWikin ei toimi, on pakko käyttää jälkimmäistä):

  1. Versionhallinnan tapa valmiille projektille:
    • siirry Explorerilla (siis ei IE vaan se Tiedostonhallinta-juttu) johonkin tyhjään hakemistoon
    • paina Explorerissa hiiren oikeaa
    • valitse TortoiseSVN/Export... (nimenomaan Export eikä Checkout ettet pilaa pohjaa muilta :-)
    • URL of repository: -kohtaan laita
      https://svn.cc.jyu.fi/srv/svn/ohj1/esimerkit/2012s/demopohjat/demo7
      
    • OK
    • kun tavara haettu, klikkaa demo7.sln
    • solutionissa on monta projektia. Muista aina hiiren oikealla ja "Set as StartUp Project" valita minkä projektin haluat ajaa (pitää näkyä boldina).
  2. Copy/Paste -tapa:
    • tee ensin itsellesi Jypelin Fysiikkapeli-projekti nimelle Kuvaaja
    • vaihda myös luokan nimi Kuvaaja tilalle Demo7.Kuvaaja Ohjelma.cs-tiedostossa.
    • sitten laita tuo pohjana oleva Kuvaaja.cs joko Copy/Pastella tai tallentamalla oman projektisi Kuvaaja.cs:n päälle.

Kokeile ajaa ohjelmaa. Sen pitäisi piirtää koordinaatisto, jossa hiirtä klikkaamalla sininen piste siirtyy klikattuun kohtaan. Lue huolella Kuvaaja.cs:n koodi ja mieti mitä kaikkea siinä on ja miksi. Begin-metodissa on rivi

           // IsFullScreen = true;

Tämä siksi, että jos ohjelma kaatuu, tai sitä haluaa debugta, niin näyttö menee sekaisin FullScreenissä. Toisaalta hiiren klikkaukset menevät yläpalkin korkeuden verran ohi jos ollaan ei-FullScreenissä. Eli kun homma toimii, ota rivi pois kommenteista tai aja vaihtamalla kohteeksi Debug:in sijaan Release.

Tehtävä 1. Pisteet taulukossa

M: 15. Taulukot. Esitetään tason pistojoukko (eli lukuparit x,y) 1-ulotteisessa taulukossa niin, että taulukon alkioina on Jypelin Vector-tyyppisiä "olioita". Tällöin i:n pisteen koordinaatit olisivat (jos otetaan i.s piste apumuuttujaan piste):

Vector piste = pisteet[i];
x = piste.X;
y = piste.Y;

Täydennä aliohjelma LuoPisteet toimivaksi niin, että se piirtää (=lisää) parametrina tuodun pisteet-taulukon koordinaattipisteitä (=vektoreita) vastaavat mustat pikkupallot näytölle (=peliin). Yritä ensin piirtää vaikka vain paikassa 0 oleva pallo ja kun se onnistuu, niin sulje tuo piirto silmukkaan, joka käy kaikki pisteet taulukon pisteet läpi. Begin metodissa on esimerkki kahden pallon luomisesta origoon. Katso muistin virkistämiseksi vanhoja PiirraPallo aliohjelmia. Paikassa 2 olevan pisteen (vektorin) kohdalle saisit keltaisen pallon valmiilla alempana olevalla funktiolla:

  LuoPallo(game,pisteet[2],r,Color.Yellow);

Nyt "peliin" pitäisi tulla 4 mustaa pistettä (koska tehtävän 2 ArvoPisteet toistaiseksi "arpoo" vain 4 pistettä) kun sen ajaa.

Vinkki: Lue lisävinkki jos em. ei lähde aukeamaan.

Tehtävä 2. Arvo pisteet

Korjaa Kuvaaja.cs:n funktioaliohjelma ArvoPisteet, joka luo pyydetyn kokoisen taulukon ja arpoo tason pisteitä taulukkoon (Vector [] pisteet). Arvo RandomGen-luokan NextDouble-funktiota käyttäen x ja y-koordinaatit ja luo aina vastaava uusi Vector. (Esimerkki käytöstä oli mm: Pallot.cs). Nyt ohjelman ajossa pitäisi koordinaatistoon tulla 20 pistettä satunnaisiin paikkoihin.

Vinkki: Tutki ensin aliohjelman nykyistä toteutusta (tekee väkisin 4 vektoria) ja mieti miten sama tehdään silmukassa.

Tehtävä 3. Lähin piste

M: 15. Taulukot, 16. Silmukat. Täydennä funktioaliohjelma LahimmanIndeksi, joka etsii annettua pistettä lähimmän taulukon alkion indeksin.

 Vector[] pisteet=... /// esim. ks vaikka TDD1-selostuksesta

(TDD1-selostus). Funktion kutsu voisi olla esimerkiksi:

 Vector piste = new Vector(5.1,2.5);
 int indeksi = LahimmanIndeksi(pisteet,piste);  
 // TDD-esimerkin taulukolla indeksi olisi 2

Vinkkejä: Demossa 6 etsittiin reaalilukutaulukon lähintä lukua. Sen idea sopii täysin, nyt vaan pitää palauttaa lähimmän indeksi (eli paikka taulukossa) eikä itse lähin. Demo 6:ssa alkion tyyppinä oli double, nyt Vector. Vastaavasti Kutsunäytelmässä Matti etsi taulukon suurimman luvun indeksin. Ja tuosta seuraavalla luennolla erotettiin jopa tuo etsiminen omaksi funktiokseen SuurimmanPaikka. Voit miettiä myös taulukonharjoittelu-ohjelmalla mitä aputietoja sinun pitää tallettaa jos etsit annettua lukua lähimmän paikan (ei itse lukua).

Etäisyyden voit laskea joko Demo 6 Etaisyys-funktiolla tai jopa helpommin Jypelin Vector-luokasta löytyvällä funktiolla kahden vektorin (=pisteen) välisen etäisyyden laskemiseksi (etsi dokumentaatiosta ko. funktio). Huomaa että tämä on esimerkki ongelmasta, jota EI voi ratkaista foreach-silmukalla. Miksi?

Tätä funktiota ei vielä kutsuta tässä ohjelmassa. Kutsu tulee tehtävässä 4.

Tehtävä 4. Lähimmän merkitseminen

Nyt metodi (se on metodi, koska se tarvitsee muitakin peli-olion asioita kuin parametrina tuodut)

public void SiirraMerkkipisteet(Vector p)

jota kutsutaan kun hiirtä klikataan (ks. Mouse.Listen -rivit), siirtää vain sinisen pisteen (klikattuPiste) hiirellä klikattuun kohtaan. Täydennä metodia siten, että se etsii pisteet-taulukosta lähimmän koordinaatin indeksin ja siirtää punaisen merkkipisteen tätä indeksiä vastaavaan koordinaattiin.

pisteet-taulukko näkyy metodille, koska taulukko on peli-luokan attribuuttina. Siksi sitä ei ole tässä tapauksessa tuotu parametrina kuten on tehty staattisissa metodeissa. Tätä metodia ei siksi voisi kutsua muista ohjelmista ilman peliluokan olemassaoloa. Samoin pisteet-metodi näkee myös attribuutit klikattuPiste ja merkkipiste.

Tehtävä 5. Kirjainten laskeminen

M: 16. Silmukat. Tee funktio LaskeKirjaimet, joka laskee merkkijonossa olevien annetun kirjaimen esiintymien lukumäärän. Testaa pääohjelmalla (tai ComTestillä), jossa on kutsuja tyyliin (keksi lisää testejä):

int sMaara = LaskeKirjaimet("kissa", 's');
int kMaara = LaskeKirjaimet("kissa", 'k');

Vinkki: String-käyttäytyy kirjainten char suhteen kuten taulukko, eli yhden kirjaimen saa samalla tavalla kuin taulukostakin saisi yhden alkion.

Tehtävä 6. Matriisien summa

M: 15.5 Moniulotteiset taulukot, 16. Silmukat. Tee funktioaliohjelma, joka summaa yhteen kaksi matriisia vastinalkioittain. Funktio siis joutuu ensin luomaan oikeankokoisen tulosmatriisin. Mitä on syytä olettaa parametrimatriiseilta? Dokumentoi kommentteihin oletuksesi. Sitten funktio laskee tulosmatriisiin komponenteittain summan. Esimerkiksi:

public static void Main(String[] args) 
{
    double[,] mat1 = {{1,2,3}, {2,2,2}, {4,2,3}};
    double[,] mat2 = {{9,2,8}, {1,2,5}, {3,19,-3}};
    double[,] mat3 = Summaa(mat1, mat2);
    Console.WriteLine(Demo6.Matriisit.Jonoksi(mat3, " ", "{0,5:0.00}"));
}

tulostaisi

10.00  4.00 11.00
 3.00  4.00  7.00
 7.00 21.00  0.00

Vinkki: Demo6 mallivastauksen Matriisit Jonoksi-funktion käyttäminen omassa ohjelmassa.

B1. Lukujen erottaminen merkkijonosta

M: 17. Merkkijonojen pilkkominen ja muokkaaminen Merkkijonojen pilkkominen: Tee funktioaliohjelma ErotaLuvut(String jono), jonka ansiosta seuraava pääohjelma

public static void Main(String[] args) 
{
    double[] luvut = ErotaLuvut("2.23 3 4 5 k      9 ;5");
    Console.WriteLine(Demo6.Matriisit.Jonoksi(luvut, " ", "{0,5:0.00}"));
}

tulostaisi

 2.23  3.00  4.00  5.00  0.00  9.00  5.00

Vinkki: Käytä String.Split-funktiota ja Taulukot.cs:ssä olevaa ErotaDouble-funktiota yksittäisen merkkijonon muuttamisessa reaaliluvuksi. ErotaDouble toimii vaikka jonoksi annetaan vääränlaisiakin syötteitä ja sille voi valita mitä tällöin palautetaan. Ks. funktion ComTestit.

Tälle eräs hyötykäyttö on esimerkin Kuvaaja.cs metodissa KysyKoordinaatti jota kutsutaan jos painetaan hiiren oikeata nappia. Tällöin kysytään käyttäjältä pisteen koordinaatti ja luodaan uusi vihreä pallo ko. kohtaan. Puhelimessa sama kysytään kun pyyhkäistään näyttöä ylhäältä alas.

B2. if-lauseiden poisto

M: 13. Ehtolauseet, 15.5 Moniulotteiset taulukot, 16. Silmukat. Tässä tehtävässä tarvitset muutamaa tiedostoa. Katso ensin miten käytät useampaa tiedostoa samassa projektissa.

Demo 5:n mallivastauksessa (ota pääohjelmaksi vaikka graafinen versio GameOfLife.cs demo 6:sta) Sopulit.cs on funktio LaskeNaapurit kirjoitettu seuraavasti:

public static int LaskeNaapurit(int[,] sukupolvi, int rivi, int sarake)
{
    int summa = 0;
    int maxy = Math.Min(sukupolvi.GetLength(0) - 1, rivi + 1);
    int maxx = Math.Min(sukupolvi.GetLength(1) - 1, sarake + 1);
    for (int iy = Math.Max(0, rivi - 1); iy <= maxy; iy++)
        for (int ix = Math.Max(0, sarake - 1); ix <= maxx; ix++)
        {
            if (iy != rivi || ix != sarake) // itseä ei lasketa naapuriksi
                if (sukupolvi[iy, ix] > 0) summa++;
        }
    return summa;
}

Tarkoituksenahan on mennä periaatteessa edeltävä rivi, oma rivi ja seuraava rivi ja niissä edeltävä sarake, oma sarake sekä seuraava sarake unohtaen kuitenkin oma koordinaatti. Eli jos rivi=2 ja sarake=1, niin pitäisi käydä ruuduissa

(1,0),(1,1),(1,2)
(2,0),      (2,2)
(3,0),(3,1),(3,2)    

On kuitenkin hieman työlästä kirjoittaa silmukka, jossa jokaista riviä ei käsiteltäisi samalla tavalla, siksi em. esimerkissä käydään silmukoilla myös ruudussa (2,1). Ja jos silmukat tuovat tämän koordinaatin, niin se on if-lauseella ohitettu. Mikäli tutkittava ruutu ei ole reunalla, niin periaatteessa em ruudut saataisiin käytyä läpi seuraavalla alustuksella:

    int alkuy = rivi - 1;
    int loppuy = rivi + 1;
    int alkux = sarake - 1;
    int loppux = sarake + 1; 
    // Tähän rajojen korjaus
    for (int iy = alkuy; iy <= loppuy; iy++)
        for (int ix = alkux; ix <= loppux; ix++)
        {...

Reunojen kanssa on kuitenkin ongelmana indeksialueen ylitys. Siksi mallivastauksessa yläraja maxyon laskettu niin, että se on pienempi luvuista rivi+1 tai viimeisen rivin indeksi (rivimäärä-1). Sama sarakkeen tapauksessa maxx. Alaraja on vastaavasti suurempi luvuista rivi-1 tai 0. Pienenä miinuksena mallissa on se, että tuo Math.Max-funktio kutsutaan jokaisella rivillä uudelleen, vaikka se tuottaa joka kerta saman arvon.

  • korjaa edellä olevat alkuy,loppuy,alkux,loppux if-lausetta käyttämällä ennen silmukoita oikeiksi niin, että tuo silmukka voi aina olla:
        for (int iy = alkuy; iy <= loppuy; iy++)
            for (int ix = alkux; ix <= loppux; ix++)
    

Mallivastauksen toinen "vika" on se, että ehtolause:

            if (iy != rivi || ix != sarake) // itseä ei lasketa naapuriksi

on silmukan jokaisella kierroksella ja sitä tarvitaan vain yhdessä tapauksessa noista yhdeksästä kierroksesta. Jos tutkittavia pistetä on vaikka 100x100, niin suoritetaan 90 000 if-lausetta. Toisaalta myös toinen if-lause

  if (sukupolvi[iy, ix] > 0) summa++;

on turha jos oletetaan että taulukossa on vain lukuja 0 tai 1 (miten tehdään?).

  • muuta silmukan sisäosa sellaiseksi, että siinä ei ole yhtään if-lausetta, mutta silmukoiden jälkeen korjataan mahdollisesti liian paljoksi lasketut naapurit (ruudun itsensä takia laskettu). Tähänkään ei tarvita if-lausetta em oletuksilla.

B3. Suljettu GameOfLife

Torus on pinta (munkkirinkilän kuori), joka tulee kun esim paperi laitetaan rullalle ja syntyneen sylinterin päät yhdistetään. GameOfLifen matriisi voidaan kuvitella kirjoitetuksi tällaiselle pinnalle.

Tee tehtävään 5 funktio LaskeNaapuritTorus, joka laskee naapurit niin, että jos mennään reunalta yli, tullaan toiselta laidalta takaisin. Koodi on lähes sama kuin tehtävässä 5, mutta se voidaan tehdä täysin ilman if-lauseita.

Kokeile miltä GameOfLife näyttäisi tällä versiolla.

B4-5. AngryLego

Kun otat pohjatiedostot TortoiseSVN:llä ohjeiden mukaan, saat myös AngryLego -pelin. Lisää peliin:

  • siirrä kaikki kuvien lataukset olion attribuuteiksi ja siellä missä kuvaa tarvitaan, käytä tätä attribuuttia:
       private Image pallonKuva = LoadImage("Igor");
       ...
           pallo.Image = pallonKuva;
    
    näin saat niin, ettei kuvia ladata kuin kerran ja voidaan käyttää samoja kuvia.
  • pallot elävät vain max. 5 sekuntia
  • pelissä voi pudottaa vain 10 palloa (voitava muuttaa yhdestä paikkaa koodista joksikin muuksikin)
  • pistelaskuri, pallon osumasta viholliseen saa 100 pistettä ja seinän takia räjähtäneestä vihollisesta 50 pistettä.
  • välillä syntyykin räjähtäviä Symbian-kännyköitä (paineaalto mukana)
  • Vinkki: Lue Jypelin ohjeet

G1. Säännöt taulukkoon

M: 15.5 Moniulotteiset taulukot. Nyt GameOfLife on säännöillä b3s23 (born 3, stay 2 ja 3, ) eli synnytään 3:lla naapurilla ja pysytään 2:lla tai 3:lla naapurilla. Muuta SeuraavaSukupolvi - aliohjelma sellaiseksi, että sille voidaan viedä jonkinlaisena taulukkona säännöt ja silmukoiden sisään ei tule enää yhtään if-lausetta (viisastelijoille: myös ?-lause on if-lause, samoin switch :-)

G2. Osuminen laskeminen

Lisää AngryLegoon:

  • yksittäinen seinä- tai kattoelementti räjähtää pois, jos siihen tulee 4 osumaa pallosta
  • Vinkki: Perintä

G3. Uusi kenttä

Lisää AngryLegoon:

  • jos kentän kaikki viholliset tuhottu, tulee uusi erilainen kenttä. Kun 3 kenttää pelattu, peli päättyy ja tulee "TopTen"-lista.