wiki:s2012/demot/demo8
Last modified 5 years ago Last modified on 2012-11-02 09:34:02

Demot » Demo 8, 5.11.2012

Vaikeaa, vastaa kurssin välikyselyyn

Tarvitsemme tietoa siitä, mitkä ovat kurssin vaikeudet suhteessa siihen, mitä kurssilla on tehty. Kysely toimii samalla itsearviona. Siksi kurssista on Korpissa välikysely. Täytä kysely ja tee tiedosto vaikeaa.txt johon kerrot mikä oli vaikeaa tällä demokerralla? Palauta yhtenä DemoWWW:n "tehtävänä" ja merkitse 1p jos olet vastannut itse kyselyyn. Voit vastata kyselyyn vaikka heti ja myöhemmin täydentää/vaihtaa vastauksia.

Demojen palauttaminen

Demot palautetaan viimeistään maanantaina klo 13:00 mennessä. Kirjaudu ensin Korppiin ja sitten kurssin NettiDemoWWW:hen https://www.mit.jyu.fi/demowww/ohj1/. Voit palauttaa osan tai kaikki tehtäväsi etukäteenkin ja täydentää vastauksia määräaikaan mennessä. Jos sinulla on samassa .cs-tiedostossa usean tehtävän vastaus, niin voit laittaa tehtävän kohdalle esim. 3-4, tai voit palauttaa saman tiedoston useammalla eri rivillä.

Pahasti pihalla

PP1

Tee Jypelin fysiikkapeli, jossa voit hiirellä klikkailemalla luoda palloja pelialueelle. Lisää peliin myös painovoima siten, että pallot eivät jää paikoilleen. Aloita tekemällä aliohjelma LuoPallo(...), joka luo halutun kokoisen pallon ja asettaa sen haluttuun paikkaan. Mieti mitä parametreja aliohjelma tarvitsee? Pelialueeseen kannattanee myös lisätä reunat, jotta pallot eivät putoa ruudulta.

Hiirellä klikkailun voit katsoa apua Kuvaaja.cs tiedostosta ja siellä erityisesti AsetaOhjaimet aliohjelmasta.

PP2

Jatka edellistä tehtävää niin, että toteutat peliisi näppäinkuuntelijat, jotka muuttavat painovoiman näppäimen osoittamaan suuntaan. Tämän lisäksi luo pelikentän keskelle olio, johon osuessaan pallot tuhoutuvat.

V1

Tee Ville-tehtävät: 6.5, 9.7 + kaksi sellaista joita et ole aiemmin ymmärtänyt kunnolla (jos kaikki ymmärretty, ei tätä ylim kahta tarvitse tehdä). Mitä 9.7:ssa on väärin C#:ia ajatellen? Muista: Villen käyttöohje ja Ville-tehtävien palauttamisohjeet.

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ä. Mikäli ComTest ei toimi yliopiston mikroluokissa, tarkista että ComTest haetaan oikeasta paikasta: Valitse Visual Studiossa Tools -> ComTest -> Options. Esiin tulee ComTest Plugin Options. Tarkista, että Path to ComTest.jar executable kentässä on N:\bin\ComTest.jar ja ole yhdistänyt koneesi N-verkkolevyyn.

Listojen luominen testejä varten onnistuu esim. luomalla ensin taulukko ja sen avulla muodostamalla lista:

///   Vector[] t = { new Vector(1,2),new Vector(3,4),
///                  new Vector(5,2),new Vector(5,5) };
///   List<Vector> luvut = new List<Vector>(t);
tai
///   List<Vector> luvut = new List<Vector>{ 
//                   new Vector(1,2),new Vector(3,4),
///                  new Vector(5,2),new Vector(5,5) };

Pohjatiedostot varsinaisille tehtäville

Ota Jypelistä uusin versio.

Ota Demo 7:n ohjeilla (TortoiseSVN-ohje) pohjatiedostot Demo 7:stä. Sitten ota Demo 7 mallivastauksista ohjeen mukaan tarvittavat tiedostot ja tee ajamiseksi tarvittavat muutokset. Tee muutokset mainitussa järjestyksessä projekti kerrallaan. Eli kokeilet toiminnan aina kun kunkin projektin muutokset on tehty.

  • AngryLego: Laita AngryLego StartUp projektiksi
    • avaa AngryLego.cs.
    • ota mallivastauksen AngryLego.cs
    • kopioi sisältö projektissa olevan tilalle
    • lisää Content-sisältöön AngryLegoContent puuttuvat äänet ja kuvat (paina Contentin päällä hiiren oikeaa ja Add/Existing? Item ja ota ja valitse kaikki AngryLegoContent hakemiston kuvat ja äänet, paitsi maila2.png jos se vielä on).
    • kokeile ajaa ohjelmaa
  • Taulukot: Laita Taulukot StartUp-projektiksi
    • Avaa Taulukot.cs-tiedosto
    • ota mallivastauksen Taulukot.cs
    • kopioi sisältö projektissa olevan tilalle
    • lisää vielä aluksi tyhjä pääohjelma Taulukot-luokan sisälle
      public static void Main(string[] args)
      {
      }
      
    • kokeile että projekti kääntyy (tyhjällä pääohjelmalla se ei tulosta mitään)
  • Kuvaaja: Laita Kuvaaja StartUp projektiksi
    • avaa Kuvaaja.cs
    • ota Kuvaaja.cs
    • kopioi sisältö projektissa olevan tilalle
    • lisää projektin viitteeksi Taulukot-projekti (Kuvaaja-projektin References päällä hiiren oikeaa ja "Add Reference" ja sitten Projects ja Taulukot.)
    • kokeile että toimii

Tehtävä 1. Karkausvuosi

M: 13. Ehtolauseet. Tee funktio Karkausvuosi(int vuosi), joka palauttaa tosi jos vuosi on karkausvuosi. Karkausvuosia ovat neljällä jaolliset vuodet, mutta ei täydet vuosisadat paitsi neljällä jaolliset vuosisadat (esim. 1900 ei ollut karkausvuosi, 1900 on 19. vuosisata ja 19 ei ole jaollinen neljällä, mutta 20 on ja siksi 2000 on karkausvuosi). Mieti kunnon testiohjelma / ComTest-testit.

Tehtävä 2. Vuodenajat

M: 13. Ehtolauseet, 13.8 13.8 switch-rakenne, 16. Toistorakenteet, 15. Taulukot. Kirjoita 3 erilaista funktiota Vuodenaika(int kuukausi), joka palauttaa merkkijonona sen vuodenajan, joka parametrina vietynä kuukautena on. Yksi käyttää käyttää if-lausetta, toinen switch-lausetta ja kolmas taulukkoa.

Tehtävä 3. Refaktorointi

Taas pitkä tehtävä, johon lyhyt vastaus. Tarkoitus on opetella muuttamaan toistuvaa koodia paremmaksi. Aluksi esimerkki:

Aluksi AngryLegossa oli laskurinäyttöjen luominen muodossa:

private void LuoPistenaytto()
{
    pisteet.MinValue = -500;
    pisteNaytto.IntFormatString = "Points: {0:00000}";
    pisteNaytto.Position = new Vector(Screen.Right - pisteNaytto.Width/2-20, 
                                      Screen.Top - pisteNaytto.Height/2);
    pisteNaytto.BindTo(pisteet);
    pisteNaytto.Color = Color.Yellow;
    pisteNaytto.BorderColor = Color.Black;
    pisteNaytto.PreferredSize = new Vector(150,pisteNaytto.Height);
    Add(pisteNaytto);

    tasoNaytto.IntFormatString = "Level: {0:0}";
    tasoNaytto.BindTo(tasoNr);
    tasoNaytto.Position = new Vector(Screen.Right - pisteNaytto.Width/2-20, 
                Screen.Top - pisteNaytto.Height / 2 - pisteNaytto.Height);
    tasoNaytto.Color = Color.Yellow;
    tasoNaytto.BorderColor = Color.Black;
    tasoNaytto.PreferredSize = new Vector(150,pisteNaytto.Height);
    Add(tasoNaytto);

    vihollisNaytto.IntFormatString = "Enemy: {0:00}";
    vihollisNaytto.BindTo(vihollisia);
    vihollisNaytto.Position = new Vector(Screen.Right-pisteNaytto.Width/2-20, 
                Screen.Top - pisteNaytto.Height / 2 - 2*pisteNaytto.Height);
    vihollisNaytto.Color = Color.Yellow;
    vihollisNaytto.BorderColor = Color.Black;
    vihollisNaytto.PreferredSize = new Vector(150,pisteNaytto.Height);
    Add(vihollisNaytto);
}

Tässä on kolme hyvin samanlaista koodinosaa. Tällöin kannattaa katsoa mikä samanlaisista osista on erilaista ja miten voisi aliohjelmalle viedä parametrina erilaiset osat ja tehdä samanlaisesta osasta aliohjelman. Muutosten jälkeen esimerkkikoodi voisi olla (vain pisteNaytto-oliota tarvitaan luomisen jälkeen):

private void LuoNaytot()
{
    pisteet.MinValue = -500;
    pisteNaytto = LuoNaytto("Points: {0:00000}",pisteet,0);
    LuoNaytto("Level: {0:0}",tasoNr,1);
    LuoNaytto("Enemy: {0:00}", vihollisia, 2);
}

private Label LuoNaytto(string format, IntMeter mittari, int n)
{
    Label naytto = new Label(format);
    naytto.Width = 150;
    naytto.SizeMode = TextSizeMode.None; // Nämä olivat ennen muualla

    naytto.IntFormatString = format;
    naytto.BindTo(mittari);
    naytto.Position = new Vector(Screen.Right - naytto.Width / 2 - 20, 
                      Screen.Top - naytto.Height / 2 - n * naytto.Height);
    naytto.Color = Color.Yellow;
    naytto.BorderColor = Color.Black;
    naytto.PreferredSize = new Vector(150, naytto.Height);
    Add(naytto);
    return naytto;
}

Vastaavasti tuhoavien pallojen (Igor ja Symbian) luonnissa on nyt koodi:

private PhysicsObject LuoPallo()
{
    PhysicsObject pallo = new PhysicsObject(tileWidth, tileWidth, 
                                            Shape.Circle);
    pallo.Image = pallonKuva; // Igor
    Add(pallo);
    pallo.Tag = "pallo";
    pallo.Destroyed += delegate { Savuta(pallo, false, 1.0); };
    pallot.Add(pallo);
    AddCollisionHandler<PhysicsObject,SarkyvaRakenne>(pallo, "rakenne", PalloOsuiRakenteeseen);
    return pallo;
}


private PhysicsObject LuoPallo2()
{
    PhysicsObject pallo = new PhysicsObject(tileWidth * 0.6, tileWidth, 
                                            Shape.Circle);
    pallo.Image = pallonKuva2; // Symbian
    Add(pallo);
    pallo.Tag = "pallo2";
    pallo.Destroyed += delegate { Liekita(pallo, false, 2.0); };
    pallot.Add(pallo);
    AddCollisionHandler<PhysicsObject,SarkyvaRakenne>(pallo, "rakenne", PalloOsuiRakenteeseen2);    
    return pallo;
}

Näissäkin on paljon yhteistä. Osan yhteisen osan tyypit eivät vielä ole tuttuja, mutta tekemällä uusi delegaatti saadaan toisen pallon luonti muotoon:

public delegate void Tehoste(PhysicsObject obj,bool paine,double kerroin);

private PhysicsObject LuoPallo()
{
   return LuoTuhoPallo("pallo", pallonKuva, 1.0,1.0,
                       Savuta, PalloOsuiRakenteeseen);
}

Missä

 /// <summary>
 /// Luodaan pallo, joka voi tuhota vihollisia
 /// </summary>
 /// <param name="tag">käytettävä tunniste</param>
 /// <param name="kuva">pallolle tuleva kuva</param>
 /// <param name="pudostusaanet">äänet joita soitetaan pallon pudotessa</param>
 /// <param name="sx">paljonko x-koko on suhteessa y-kokoon</param>
 /// <param name="kerroin">mahdollisen räjähdyksen koon kerroin</param>
 /// <param name="tehoste">mitä aliohjelmaa kutsutaan tehosteen tekemiseen</param>
 /// <param name="tormays">mikä aliohjelma käsittelee törmäyksen rakenteeseen</param>
 /// <returns></returns>
 private PhysicsObject LuoTuhoPallo(string tag, Image kuva, double sx, double kerroin, 
              Tehoste tehoste, CollisionHandler<PhysicsObject, SarkyvaRakenne> tormays)
{
    PhysicsObject pallo = new PhysicsObject(tileWidth * sx, tileWidth, 
                                                        Shape.Circle);
    pallo.Image = kuva;
    Add(pallo);
    pallo.Tag = tag;
    pallo.Destroyed += delegate { tehoste(pallo, false, kerroin); };
    pallot.Add(pallo);
    AddCollisionHandler<PhysicsObject,SarkyvaRakenne>(pallo, "rakenne", tormays);    
    return pallo;
}

Kirjoita myös LuoPallo2 -metodi yhdellä rivillä käyttäen mallina tuota LuoPallo-metodia. Tarkista että ohjelma edelleen toimii samalla tavalla. Eli yläorrella on kaksi Igoria ja yksi Symbian kännykkä ja kun Symbian häviää, se "liekittyy" Symbianin kuvilla. Lisäksi Symbian-kännykkä ei ole suorakulmainen orrella ollessaan, vaan laihempi (kuten alunperinkin).

Tehtävä 4. Refaktorointi

Kuten edellisessä tehtävässä, myös rakenteiden luonnissa on toistoa (tässä ei tarvitse mitään delegaatteja tms "hankalaa"):

private PhysicsObject LuoSeina()
{
    SarkyvaRakenne seina = new SarkyvaRakenne(tileWidth, tileHeight);
    seina.Tag = "rakenne";
    seina.Kuvat = tiilenKuvat;
    return seina;
}


private PhysicsObject LuoKatto()
{
    SarkyvaRakenne katto = new SarkyvaRakenne(tileWidth * 1.5, tileHeight);
    katto.Tag = "rakenne";
    katto.Kuvat = katonKuvat;
    return katto;
}

Tee metodi LuoSarkyvaRakenne sopivilla parametreilla niin, että saat vaihdettua sekä LuoSeina että LuoKatto -metodit yhden rivin metodeiksi.

Tehtävä 5. Taulukko listaksi

M: 22.2 Listat Vaihda taulukko listaksi. Eli Kuvaaja.cs:ssä vaihda

private Vector[] pisteet;

tilalle

private List<Vector> pisteet;

ja tee kaikki muut tarvittavat muutokset jotta ohjelma toimii täsmälleen kuten aikaisemminkin.

Tehtävä 6. Kuukausien keskilämmöt

M: 22.2 Listat Muuta Kuvaaja.cs:ää siten, että se alkaa tyhjällä koordinaatistolla ja aluksi kysytään käyttäjältä merkkijono, jossa olisi tarkoitus olla vuoden kuukausien keskilämpötiloja. Merkkijonosta pilkotaan demo 7:n vastauksella reaalilukutaulukko. Sitten piirretään kuva (lisätään palloja kuvaan), siten että lukua vastaavat x-koordinaatit ovat 10,20,30 jne. Eli jos käyttäjä antaa merkkijonon

-10 -12 5 7 14 18

niin piirretään vastaavasti pallukat paikkoihin

(10,-10)
(20,-12)
(30,5)
(40,7)
(50,14)
(60,18)

pallukoiden värit ovat niin, että yli 12 olevat pisteet ovat punaisella ja alle 0:n olevat pisteet sinisellä. Muut mustalla.

Aluksi muuta alueen rajat:

        private const double x1 = -1;
        private const double x2 = 70;
        private const double y1 = -15;
        private const double y2 = 25;

sitten poista pääohjelmasta:

        pisteet = ArvoPisteet(20, x1, y1, x2, y2);
        LuoPisteet(this, pisteet, pallonKoko);

ja lisää pääohjelman (Begin) loppuun

 KysyKoordinaatti();

Muuta sopivasti metodin KysyKoordinaatti kysymystekstiä. Sitten muuta metodi KoordinaattiAnnettu muotoon:

        private void KoordinaattiAnnettu(InputWindow ikkuna)
        {
            string vastaus = ikkuna.InputBox.Text;
            double x = 10;
            double[] d = Demo7.Taulukot.ErotaLuvut(vastaus);
            pisteet = new List<Vector>();
            foreach (double luku in d)
            {
                pisteet.Add(new Vector(x,luku));
                x += 10;
            }
            LuoPisteet(this, pisteet, 0.3,0.0,12.0);
            kysymassa = false;
        }

Varsinaiseksi tehtäväksi jää tehdä tuo aliohjelma

 public static void LuoPisteet(PhysicsGame game, ??? pisteet, 
                              double r, double ala,double yla)

jossa ala ja yla ovat rajat sille, mitä piirretään milläkin värillä.

B1 Konvoluutio käsin

Bittikuvat esitetään taulukkona, jossa on kutakin pikseliä vastaava kirkkaus numeroarvona 0-255. Esimerkiksi 7x7 harmaasävykuva jossa on mustalla pohjalla yksi valkoinen vinoristi:

----------------------------
255   0   0   0   0   0 255
  0 255   0   0   0 255   0
  0   0 255   0 255   0   0
  0   0   0 255   0   0   0
  0   0 255   0 255   0   0
  0 255   0   0   0 255   0
255   0   0   0   0   0 255
----------------------------

Kuvalle voidaan tehdä konvoluutio-muunnos sillä tavalla, että maskia, esim sumennus:

 1  1  1
 1  1  1
 1  1  1

"liutetaan" jokaisen kuvapikselin kohdalle niin, että maskin keskipiste laitetaan vastaavan kuvapikselin kohdalle. Kunkin "liutus"askeleen kohdalla otetaan kuvan väriarvo yksitellen jokaisen maskin pisteen kohdalta ja kerrotaan se maskin vastaavan pisteen arvolla ja nämä arvot summataan sekä jaetaan maskin arvojen summalla ja laitetaan muunnetun kuvan uudeksi pisteeksi.

Esimerkiksi em. matriiseilla uuden kuvan paikassa (1,1) olevan pikselin väriarvoksi tulisi:

 255*1 +   0*1 +   0*1 +
   0*1 + 255*1 +   0*1 +
   0*1 +   0*1 + 255*1    = 3*255 = 765

Tämä jaetaan vielä maskin arvojen summalla, eli 9, jolloin pisteen väriksi tulisi 765/9 = 85. Jos joudutaan käyttämään taulukon ulkopuolisia arvoja, niin arvona käytetään silloin 0:aa.

Laske tuloskuvan jokaisen pikselin väriarvo.

Lisäinfoa katso Demo 9. B1-3. Konvoluutio.

B2. Ääniä peliin

Muuta AngryLego-peliä niin, että putoava pää huutaa mennessään ja Symbian soittelee pudotessaan kännykän ääniä. Äänet saavat kuulua pitempäänkin, mutta tekemällä niistä noin 1-2 sekuntia pitkiä, ne eivät soi turhan kauaa. Pohjahakemistossa on valmiita ääniä jos et itse halua tehdä ääniä.

B3. Koordinaatiston pikkuviivat

Muuta Kuvaaja.cs:ssä olevaa Axis-luokkaa niin, että se piirtää 4 pikkuviivaa (=jakaa viiteen osaan) jokaisen isomman välin sekä x-akselilla että y-akselilla.

B4. Moodi

M: 15. Taulukot, 16. Toistorakenteet. Tee funktioaliohjelma Moodi(luvut), joka etsii taulukon yleisimmän luvun. Jos eniten esiintyviä on yhtä monta, niin silloin ensimmäisenä näistä lukujoukoista tulkitaan yleisimmäksi. Ennen ohjelmointia, mieti ensin kuinka itse ratkaisit tämän ongelman (eli mieti algoritmi). Et tarvitse aputaulukkoja välttämättä, vaan tehtävän voi ratkaista muutamalla apumuuttujalla. Moodi on yksi keskiluvuista. Toinen on keskiarvo - tee sitäkin varten funktioaliohjelma. Kolmas keskiluvuista on mediaani, eli aineiston keskimmäinen alkio. Tätä ei vielä tarvitse tehdä. Mitä mediaanin laskemiseksi pitäisi ensin tehdä? Katso vaikka Wikipediasta keskiluku.

B5-7. Konvoluutio ohjelmalla

Tee C#-ohjelma, joka laskee mille tahansa harmaasävykuvalle ja maskille konvoluution. Ks. B1. Matriisien testaamisessa voit käyttää hyväksi Demo6:n mallivastauksen Matriisit-luokan Jonoksi-funktioita.

G1-2. Hyppivät viholliset

Muuta AngryLego-peliä niin, että lego-vauvat hyppivät muutaman pikselin verran levottomina ylös/alas. Vinkki: tee vihollisista oma luokka ja tee sen Update-metodiin tarvittavat muutokset.