downloading - JYU Trac Help

ITKP102 Ohjelmointi 1 – Tentti 4.12.2013
Vastaa neljään tehtävään tehtävistä 1-6 tai yhteen tehtävään 7 (yhteensä enintään 24p + mahd
bonus pisteet).
HUOM! Jokaisen tehtävän vastaus omalle paperilleen. Paperit palautetaan eri pinoihin!
Muista täyttää Korpissa kurssin kohdalta "lomake": "kuinka opiskelin" sekä vastata kurssin
palautekyselyyn sekä Ohjelmointikäsityksiä-kyselyyn.
Jos tenttivastauksessa tarvitset jotakin funktiota API-kirjastosta (C#:n kirjasto) ja et muista tarkkaan
mikä oli funktion/metodin nimi, niin kirjoita funktion esittely (parametreineen) ja kommentti siitä, mitä
sen pitäisi tehdä. Et saa itse keksiä funktioita, joita ei ole olemassa (ellet itse myös toteuta niitä).
Tehtävä 1. Merkkijonojen käsittelyä
using System;
using System.Text;
public class KerroVuodenaika
{
public static void Main(string[] args)
{
// Kysymys: Mitä tähän pitäisi tulla?
string nimi = Console.ReadLine();
// Käyttäjä antaa syötteenä kuukauden, esimerkiksi "Helmikuu”
nimi = MikaVuodenaika(nimi);
Console.WriteLine(nimi); // Tulostaa "Talvi"
}
// Kysymys: Oikeaoopiset kommentit tähän, myös parametrin nimet kuntoon
public static string MikaVuodenaika(string tahanKunnonNimiParametrille)
{
// Kysmymys: Tähän aliohjelman toteutus
return "";
}
}
a) Täydennä ylläoleva koodi toimivaksi. Käyttäjältä tulee syöte kuukauden nimestä ja sen perusteella
pitäisi päättää, mikä vuodenaika on kyseessä. Vuodenajat alkavat niin, että joulukuu on talvikuukausi ja
jokaiseen vuodenaikaan kuuluu kolme kuukautta, eikä ohjelma saa kaatua virheellisestä syötteestä.
Syötteeseen ei tule ylimääräisiä merkkejä esim. “HelmikuuxXX”, mutta syöte voi olla muodossa
“heLMikUu” (3 p)
b) Tee kommentit ja ComTest-testit ohjelmalle. Katso tehtävä 2:n Summa-aliohjelmasta mallia
ComTestien kirjoittamiseen. (3 p)
Bonus 1p: Tee tehtävän toteutus käyttäen taulukkoa tai dictionarya.
1/7
Tehtävä 2. Muuttujat ja aliohjelmat
/// <summary>
/// Luokka joka laskee yhteen- ja kertolaskuja
/// <summary>
public class SummatJaTulot
{
public static void Main(string[] args)
{
int a = 5;
int b = 3;
int c = a * b++; // Kysymys: a) minkä arvon c saa nyt (1/3 p)
Console.WriteLine(a + " * " + b + " = " + c);
// Kysymys: b) mitä edellinenrivi tulostaa (1/3 p)
int b; // Kysymys: c) mikä on b:n arvo tällä rivillä? (1/3 p)
int summa = Summa(4, 9, 1, 8);
Console.WriteLine(summa); // Kysymys: d) mitä tulostuu? (1/3 p)
Summa();
Console.WriteLine(summa); // Kysymys: e) mitä tulostuu? (0,5 p)
// Kysymys: f) Kutsu Tulo aliohjelmaa kahdella eri tavalla. (2 p)
}
/// <summary>
/// Laskee summan kolmelle ennalta määritetylle luvulle
/// </summary>
/// <returns> 3:n, 5:n ja 7:n summa </returns>
public static int Summa()
{
return (3 + 5 + 7);
}
/// <summary>
/// Laskee neljän luvun summan.
/// </summary>
/// <returns>Annettujen lukujen summan</returns>
/// <example>
/// <pre name="test">
/// Program.Summa(3, 5, 1, 1) === 10;
/// Program.Summa(1, 1, 1, 1) === 4;
/// Program.Summa(2, -1, -2, 1) === 0;
/// Program.Summa(-2, -5, -4, -3) === -14;
/// Program.Summa(0, 0, 0, 0) === 0;
/// </pre>
/// </example>
public static int Summa(???) // Kysymys: g) Täydennä aliohjelman
{
//
vastinparametrit (0,5 p)
// Kysymys: h) Täydennä aliohjelma toimivaksi (1 p)
}
2/7
/// <summary>
/// Kertoo kolme lukua ja tulostaa niiden tulon.
/// </summary>
/// <param name="kertoja">Kertoja</param>
/// <param name="kerrottava">Kerrottava</param>
/// <param name="lisaKerroin">Lisä kerroin</param>
public static void Tulo(double kertoja, double kerrottava,
double lisaKerroin = 5.0)
{
double tulo = kertoja * kerrottava;
tulo *= lisaKerroin; // Kysymys: i) Mikä on muuttujan tulo arvo
//
tämän rivin jälkeen? (1/3 p)
Console.WriteLine(tulo);
Console.WriteLine(tulo * 2); // Kysymys: j) Mitä tulostuu (1/3 p)
}
}
Tehtävä 3. Taulukko
Seuraava osa on Taunolla tuotettua koodia.
int[] t = {56, 78, 59, 1, 82, 40};
int apu;
apu = t[0];
t[0] = t[5];
t[5] = apu;
apu = t[1];
t[1] = t[4];
t[4] = apu;
apu = t[2];
t[2] = t[3];
t[3] = apu;
a) Mitä koodi tekee taulukolle tja miltä taulukko tnäyttää koodin suorittamisen jälkeen? (2p)
b) Kirjoita yllä olevaa Tauno-koodia avuksi käyttäen aliohjelma, joka tekee saman kuin yllä. Käytä
aliohjelmassa silmukkaa. Aliohjelman on toimittava erikokoisilla taulukoilla! Mieti mitä parametreja
aliohjelma tarvitsee. Kommentoi aliohjelma kuten on opetettu. b)-kohdassa ei tarvitse kirjoittaa
testejä. (4p)
Bonus 1p: Kirjoita tarvittavat eri testitapaukset ComTest testeinä.
Tehtävä 4. Toiston poisto
Poista alla olevasta koodista toisto hyödyntämällä listoja tai taulukoita, silmukoita ja toteuttamalla
aliohjelma LaskeMerkit, joka palauttaa, kuinka monta tiettyä merkkiä annetussa merkkijonossa on.
Dokumentoi kyseinen aliohjelma. (6p)
Bonus 1p: Aliohjelman testit
public static void Main()
{
3/7
Console.WriteLine("Anna 1. merkkijono:");
string tutkittava1 = Console.ReadLine();
Console.WriteLine("Anna 2. merkkijono:");
string tutkittava2 = Console.ReadLine();
Console.WriteLine("Anna 3. merkkijono:");
string tutkittava3 = Console.ReadLine();
int lkm1 = 0;
for (int i = 0; i < tutkittava1.Length; i++)
{
if (tutkittava1[i] == 'a')
lkm1++;
}
Console.WriteLine("Merkkijonossa {0} on {1} kpl {2}-kirjaimia.", 1, lkm1, 'a');
int lkm2 = 0;
for (int i = 0; i < tutkittava2.Length; i++)
{
if (tutkittava2[i] == 'e')
lkm2++;
}
Console.WriteLine("Merkkijonossa {0} on {1} kpl {2}-kirjaimia.", 2, lkm2, 'e');
int lkm3 = 0;
for (int i = 0; i < tutkittava3.Length; i++)
{
if (tutkittava3[i] == 'i')
lkm3++;
}
Console.WriteLine("Merkkijonossa {0} on {1} kpl {2}-kirjaimia.", 3, lkm3, 'i');
}
Tehtävä 5. Teoriaa
a) Mitä alla oleva koodirivi tulostaa ja miksi? (1p)
System.Console.WriteLine((17 % 5) - 3);
b) Onko alla olevassa C#-koodissa ongelmaa? Jos on, niin mikä? (1p)
/// <summary>
/// Tulostaa onnittelun konsoliin, mikäli parametrina annettu ikä on 20. Muussa
/// tapauksessa tulostaa toteamuksen, että henkilö ei ole vielä 20, tai että hän
/// ei enää ole 20-vuotias
/// </summary>
/// <param name="ika">Henkilön ikä vuosina</param>
public static void OnnitteleKaksikymppista(int ika)
{
if (ika > 20) System.Console.WriteLine("Et ole enää 20-vuotias...");
else if (ika = 20) System.Console.WriteLine("Onneksi olkoon 20-vuotiaalle!");
else System.Console.WriteLine("Et ole vielä 20-vuotias.");
}
4/7
c) Miten vakiomuuttuja (constant) eroaa C#:ssa tavallisesta muuttujasta? (1p)
d) Mitä ohjelmalle usein tapahtuu, mikäli poikkeusta ei oteta try-catch -rakenteessa kiinni? (1p)
e) Mikä on ehdollinen keskeytyskohta (conditional breakpoint)? (1p)
f) Mikä on ikuinen silmukka? Mitä täytyy ottaa huomioon, mikäli haluaa tehdä hallitun ikuisen silmukan
(sellaisen, jossa silmukan ehto on aina tosi)? (1p)
Tehtävä 6. Olioviitteet
Vastaa kommenteissa oleviin neljään (4) kysymykseen.
public static void Main(string[] args)
{
List<int> lista1 = new List<int> { 1, 2, 3, 4 };
List<int> lista2 = new List<int> { 1, 2, 3, 4 };
// Kysymys: a) Tulostuuko seuraava ruutuun? Perustele. 2p
if (lista1 == lista2)
Console.WriteLine("Listat ovat samoja");
List<int> muokattuLista = muokkaaListaa(lista1);
string erotin = "";
// Kysymys: b) Mitä alla olevat 2 silmukkaa tulostavat? 2p
foreach (int alkio in lista1)
{
Console.Write(erotin + alkio);
erotin = ",";
}
Console.WriteLine();
erotin = "";
foreach (int alkio in muokattuLista)
{
Console.Write(erotin + alkio);
erotin = ",";
}
lista2 = muokattuLista;
muokattuLista = new List<int> { 0, 0, 0, 0 };
// Kysymys: c) Mihin olioviitteet lista1, lista2 sekä muokattuLista
//
viittaavat tässä vaiheessa ohjelman ajoa. 1.5p
// Kysymys: d) Selitä lyhyesti: "automaattinen roskienkeruu"? 0.5p
}
/// <summary>
/// Lisää parametrina tuodun listan viimeiseksi alkioksi luvun 8.
/// </summary>
/// <param name="lista">Kokonaisluku lista, jonka perään lisätään luku 8</param>
5/7
/// <returns>Lista, jonka perään on lisätty luku 8</returns>
public static List<int> muokkaaListaa(List<int> lista)
{
lista.Add(8);
return lista;
}
Tehtävä 7. Laajempi ohjelmointitehtävä, 24p
Jos teet tämän, älä tee muita tehtäviä!
Tee ohjelma, joka luo sokkelon kaksiulotteiseen taulukkoon. Sokkelotaulukon luova aliohjelma saa
parametreikseen sokkelon leveyden ja korkeuden. Taulukon alkiot tulkitaan joko seinäksi tai
käytäväksi. Sokkelon käytävät ovat yhden ruudun paksuisia, samoin seinät. Sokkelon reunoilla tulee
olla 2 uloskäyntiä. Esimerkiksi ohjelmalla tehty 15*15 -sokkelo voisi näyttää vaikka tältä:
X XXXXXXXXXXXXX
X X
X
XXX XXXXXXX XXX
XX
X X
X XXXXXXX XXX X
XX X X X
X X X X XXX X X
X XXX XX
X XXX X X XXX X
XX X XXX
XXX X XXXXX X X
X X X XX
X XXXXXXX X X X
X
X
XXXXXXXXXXXXXXX
Rakenna sokkelo seuraavalla algoritmilla:
1. Alusta 2-ulotteinen taulukko siten, että taulukon kaikki alkiot ovat seiniä.
2. Valitse aloitusruutu.
3. Muuta ruutusi käytäväksi.
4. Tee lista ruutusi kaikista naapuriruuduista.
5. Valitse satunnaisesti jokin sellainen naapuriruutu, jossa ei ole vielä käyty ja poista seinä sen ja
ruutusi välistä.
6. Siirry valitsemaasi naapuriruutuun ja jatka kohdasta 3.
7. Jos päädyt umpikujaan, palaa takaisin niin pitkälle, että vastaan tulee naapuriruutuja, joissa ei
vielä ole käyty.
8. Tee lopuksi sisäänkäynnit sokkeloon.
Täydennä alla olevaan koodiin puuttuvat aliohjelmat.
public class Sokkelomatriisi
{
private static Random rnd = new Random(); //käytetään sekoituksessa.
/// <summary>
6/7
/// Pääohjelma, jossa luodaan 25 x 25 sokkelo.
/// </summary>
public static void Main()
{
bool[,] sokkelo = LuoSokkelo(25, 25);
TulostaSokkelo(sokkelo);
}
/// <summary>
/// Sekoittaa annetun listan.
/// </summary>
/// <typeparam name="T">Listan alkioiden tyyppi</typeparam>
/// <param name="lista">Lista, joka sekoitetaan</param>
public static void Sekoita<T>(List<T> lista)
{
int n = lista.Count;
while (n > 1)
{
n--;
int k = rnd.Next(n + 1);
T value = lista[k];
lista[k] = lista[n];
lista[n] = value;
}
}
}
/// <summary>
/// Luokka koordinaateille. Tätä luokkaa voi käyttää apuna
/// naapurialkioiden etsimisessä.
/// </summary>
public class Koordinaatti
{
public int X { get; set; }
public int Y { get; set; }
/// <summary>
/// Luo uuden (x,y)-koordinaatin
/// </summary>
/// <param name="x">x-koordinaatti</param>
/// <param name="y">y-koordinaatti</param>
public Koordinaatti(int x, int y)
{
X = x;
Y = y;
}
}
7/7
Mallivastaukset
1. Merkkijonojen käsittelyä (Tero)
a) 3p ohjelma tulostaa jokaisella kuukaudella jonkun vuoden ajan, niin että 3 kuukautta on per
vuodenaika
2p Ohjelman toiminnallisuudessa on puutteita, ei esimerkiksi oteta huomioon isoja kirjaimia tai ohjelma
kaatuu huonolla syötteellä, kuitenkin ohjelmasta on saatavissa ulos 3 kuukautta per vuodenaika
1p Ohjelman idea tai toiminnallisuus on ymmärretty, mutta kaikkia vuodenaikoja ei saa vastauksena, tai
kaikille kuukausille ei ole vastinetta.
b) Kommentit kuvaavat, mitä aliohjelma tekee. Parametrien ja paluuarvot ovat kommentoitu(1 p)
½ pistettä, jos toinen edellämainituista
½ jos kommenteissa syntaktisia ongelmia, kuten <summary> ja </summary> eivät ole oikein
ComTest-testit toimivat ja ne menevät läpi oikein toimivan ohjelman kanssa (2p)
1p ½ ComTesteissä pieniä syntaksivirheitä, kuten luokannimeä unohdettu kutsua
1p Joku ComTesteista ei mene läpi, mutta ohjelman toteutus toimii
2. Muuttujat ja aliohjelmat (Anna)
a) 15 // b++ kasvattaa b:n arvoa vasta seuraavalla rivillä.
b) 5 * 4 = 15
c) 4 //b:tä ei voi määritellä uudelleen, tämä tuottaa virheen käännettäessä. Ei vaikuta mitenkään b:n
arvoon. Jos selitti tämän sai myös pisteet.
d) 22
e) 22 //Summa() ei muuta summan arvoa, koska Summa()-aliohjelman palauttamaa arvoa ei oteta
vastaan.
f) Tulo(1.0 ,2.0 ,3.0 ); //Saa tietysti myös pisteet, jos esitteli muuttujat ensin tai jos laittoi int lukuja
Tulo(1.0, 2.0);
g) (int a, int b, int c, int d)
h) return(a + b + c + d);
i) kertoja * kerrottava * lisakerroin //myös numeroilla esim. 3 * 4 * 5 eli 60
j) tulo * 2 // tai 120
3. Taulukko
a) Ohjelma kääntää taulukon ympäri (1p), taulukko t = {40, 82, 1, 59, 78, 56} (1p)
b)
/// <summary>
/// Aliohjelma kääntää taulukon ympäri
8/7
/// </summary>
/// <param name="t">Käytettävä taulukko</param>
public static void KaannaTaulukko(int[] t)
{
int apu, j;
for(int i = 0; i < t.Length/2; i++)
{
j = t.Length-i-1;
apu = t[i];
t[i] = t[j];
t[j] = apu;
}
}
4. Toiston poisto (Mika)
public static void Main()
{
List<string> merkkijonot = new List<string>(); //voi käyttää myös taulukkoa
for (int i = 1; i <= 3; i++)
{
Console.WriteLine("Anna {0}. merkkijono:", i);
merkkijonot.Add(Console.ReadLine());
}
char[] kirjaimet = {'a', 'e', 'i'};
for (int i = 0; i < 3; i++)
{
int kirjaintenLkm = LaskeMerkit(merkkijonot[i], kirjaimet[i]);
Console.WriteLine("Merkkijonossa {0} on {1} kpl {2}-kirjaimia.", i + 1,
kirjaintenLkm, kirjaimet[i]);
}
}
/// <summary>
/// Laskee, kuinka monta tiettyä merkkiä annetussa merkkijonossa on.
/// </summary>
/// <param name=”merkkijono”>merkkijono, jonka merkkejä lasketaan</param>
/// <param name=”merkki”>merkki, jonka esiintymiä lasketaan</param>
/// <returns>annetun merkin esiintymien lukumäärä merkkijonossa</returns>
/// <example>
/// <pre name="test">
/// Program.LaskeMerkit("", 'a') === 0;
/// Program.LaskeMerkit("kissa", 'a') === 1;
/// Program.LaskeMerkit("kissa", 's') === 2;
/// Program.LaskeMerkit("kissa", 'r') === 0;
/// </pre>
/// </example>
public static int LaskeMerkit(string merkkijono, char merkki)
{
int lkm = 0;
for (int i = 0; i < merkkijono.Length; i++)
9/7
{
if (merkkijono[i] == merkki)
lkm++;
}
return lkm;
}
Arviointikriteerit:
● Ensimmäinen silmukka: 2p
● Aliohjelma + dokumentaatio: 2p
● Aliohjelman kutsuminen silmukassa: 2p
● Aliohjelman testit: + 1p
Miinukset (verrattuna täyteen pistemäärään):
Silmukat:
● Silmukan rajat väärin (silmukka suoritetaan liian monta/vähän kertaa): -1p
● Silmukan rajat huonosti kuvaavia, esim: for (int i = 1; i < 4; i++): -0.5p
● For-silmukassa ylimääräinen "i++" (tai while-silmukassa unohtunut kokonaan): -1p
● Taulukko esitellään silmukan sisällä: -1p
Aliohjelman esittelyrivi:
● Aliohjelman static-avainsana puuttuu: -0.5p
● Aliohjelman paluuarvon tyyppi väärin: -1p
● Aliohjelma saa liian vähän parametreja: -1p
● Aliohjelman parametreista puuttuu tyypit: -1p
● Aliohjelman parametrien nimet eivät ole kuvaavia (esim. "x" ja "y"): -0.5p
Muita mahdollisia virheitä:
● Puolipiste(itä) puuttuu: -0.5p
● Aliohjelman loppusulku } puuttuu: -0.5p
● Aliohjelman lokaalin muuttujan esittely puuttuu: -1p
● Aliohjelman lokaalin muuttujan alustus puuttuu: -1p
● Aliohjelma tulostaa eikä palauta arvoa: -1p
● Aliohjelman kommenteissa puutteita: -0.5p
● Aliohjelman kommentit puuttuvat kokonaan: -1p
● Silmukan ehdossa "i < merkkijono": -1p
● Pääohjelmasta puuttuu esittelyrivi: -0.5p
● Kirjaimia a, e ja i ei ole laitettu taulukkoon/listaan, vaan silmukan sisälle on tehty if-hässäkkä:
-1p
Joitakin erikoistapauksia:
● Ohjelma ei toimi samalla tavalla kuin alkuperäinen, esim. pääohjelmassa vain yksi silmukka,
jossa kysytään merkkijono ja samalla ilmoitetaan merkkien lukumäärä: 2p pääohjelmasta
● Aliohjelmaa ei ole kutsuttu silmukassa, vaan 3 kertaa ilman silmukkaa: 0.5p kutsuista
● Aliohjelmalle viedään lista merkkijonoista eikä yhtä: 0p aliohjelmasta
10/7
5. Teoriaa (Matias)
Yleisesti tämän tehtävän asiat oli osattu hyvin. Alla mainittu yleisimpiä virheitä/kommentteja, mitä
vastauksissa oli.
a) -1 (1p) % on jakojäännös-operaattori, jonka tuloksena tulee 2. Siitä vähennetään 3.
-0.25 / -0.5 pistettä, jos tiedettiin operaattori, mutta lasku väärin
b) ika = 20 on sijoitusoperaatio. Vertailun kanssa tulisi käyttää vertailuoperaattoria == (1p)
Monella oli toteamus, että “if” - “else if” - “else” ei toimisi tässä tapauksessa. Jos “else
if”-lauseesta ottaisi elsen pois, niin silloin esim. arvolla 21 tulostuisia kaksi riviä. Kannattaa kokeilla itse
Visual Studiolla, mitä tapahtuisi.
c) Vakiomuuttujien arvoa ei voi muuttaa niiden määrittelyn jälkeen. (1p)
Monella oli maininta siitä, että vakiot voidaan määritellä vain luokan alkuun. Toki näin usein tehdään,
mutta mikään pakko tähän ei ole. Pieni miinus (-0.25p), jos muuten tiesi, mistä oli kyse. Myös
normaaleja muuttujia voidaan määritellä luokan alkuun ja käyttää samalla tavalla aliohjelmien sisällä,
toki se on eri asia, onko tämä kuinka järkevää. Asiasta lisää kevään jatkokurssilla...
d) Ohjelman suoritus yleensä päättyy, mikäli poikkeusta (esim. NullPointerException) ei oteta kiinni
(1p)
Käyttöjärjestelmän antama virheilmoitus riippuu hieman missä sovellusta ajetaan. Konsoliin tulostuu
virheilmoitus, mutta esimerkiksi Windowsin työpöytäohjelma ilmoittaa vain “Ohjelma has stopped
working” ja Windows Phone-ohjelma taitaa palata alkuvalikkoon. Täysiin pisteisiin riitti, jos tiesi
kaatumisen. Hieman miinuksia, jos oli mainittu esim. “tapahtuu kauheita”, mutta ei yksilöity tarkemmin,
mitä tapahtuu.
e) Ehdollinen keskeytyskohta on kohta, mihin suoritus debuggauksessa keskeytetään, mikäli
keskeytykselle määrätty ehto täyttyy (1p)
Pieni miinus (-0.25p), jos ei ollut debuggerista mainittu mitään. Monilla oli mainittu, että ajon aikana
pysähdytään, mutta normaalin ajon aikana ei toki keskeytyskohta vaikuta mitenkään. -0.5 mikäli ei ollut
mainittu ehdon täyttymisestä.
f) Ikuinen silmukka on silmukka, jonka lopetusehto ei koskaan päde eli esimerkiksi whilen totuusarvo
on aina true. (0.5p) Niitä käyttäessä tulee olla poistumisehto, jolla silmukasta poistutaan breakin tai
returnin avulla. (0.5p)
-0.25p, jos mainittu, että jollain keinolla pitää silmukasta poistua, mutta oli jäänyt mainitsematta ehto
poistumiselle tai break/return keinona
6. Olioviitteet (Hannu)
Kysymys 1.
Ei tulostu. (1p)
11/7
Kyseisellä operaatiolla vertaillaan viitteitä, jotka osoittavat eri muistipaikkoihin. Vertailussa ei oteta
kantaa listojen alkioiden arvoihin (1p)
Kysymys 2.
1,2,3,4,8 - (1p)
1,2,3,4,8 - (1p)
Kysymys 3.
lista1 viittaa alkuperäiseen lista1:seen. Muistipaikassa on lista {1,2,3,4,8} (0.5p)
lista2 viittaa samaan muistipaikkaan kuin lista1 {1,2,3,4,8}(0.5p)
muokattavaLista viittaa uuteen muistipaikkaan, jossa on lista {0,0,0,0}(0.5p)
Kysymys 4.
Mikäli yksikään muuttuja ei viittaa johonkin muistipaikaan, muistipaikka muuttuu roskaksi.
Automaattinen roskienkeruu vapauttaa muistipaikan muuhun käyttöön.(0.5p)
7. Yksi isompi ohjelmointitehtävä, 24p
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Sokkelomatriisi;
namespace Sokkelomatriisi
{
/// <summary>
/// Rakennetaan sokkelomatriisi. Matriisin alkiot ovat joko seinää tai käytävää.
/// Toteutus booleaneilla, eli käytävät saavat arvon true (eli ruutuun saa mennä)
/// ja seinät arvon false (eli ruutuun ei saa mennä).
///
/// Sokkelo myöskin tulostetaan.
/// </summary>
public class Sokkelomatriisi
{
private static Random rng = new Random(); //käytetään sekoituksessa.
/// <summary>
/// Pääohjelma, jossa luodaan 25 x 25 sokkelo.
/// </summary>
static void Main()
{
bool[,] sokkelo = LuoSokkelo(25, 25);
tulostaSokkelo(sokkelo);
}
/// <summary>
/// Luo uuden korkeus * leveys -sokkelon. Jos korkeus tai leveys
/// ovat parillisia, niihin lisätään 1, jotta sokkelo toimii.
12/7
/// </summary>
/// <param name="korkeus">Sokkelon korkeus</param>
/// <param name="leveys">Sokkelon leveys</param>
/// <returns>vastarakennettu sokkelo.</returns>
public static bool[,] LuoSokkelo(int korkeus, int leveys)
{
//Sokkelon leveyden ja korkeuden on oltava parittomia, jotta vältytään
//tuplareunoilta. Sokkelossa on siis käytäviä parittomissa
//(x,y) -koordinaateissa ja seinää parillisissa.
if (korkeus % 2 == 0) korkeus++;
if (leveys % 2 == 0) leveys++;
bool[,] sokkelo = new bool[korkeus, leveys];
KaivaKaytavat(sokkelo, 1, 1);
//Luodaan sokkeloon 2 uloskäyntiä.
sokkelo[0, 1] = true;
sokkelo[korkeus - 2, leveys - 1] = true;
return sokkelo;
}
/// <summary>
/// Kaivetaan parametrina tuotuun sokkeloon käytävät rekursiivisesti.
/// </summary>
/// <param name="sokkelo">sokkelo, johon käytävät kaivetaan</param>
/// <param name="x">kohdan, jossa ollaan menossa x-koordinaatti</param>
/// <param name="y">kohdan, jossa ollaan menossa y-koordinaatti</param>
private static void KaivaKaytavat(bool[,] sokkelo, int x, int y)
{
//Kaivetaan aloitusruutuun käytävä.
sokkelo[x, y] = true;
//Haetaan ruudun sokkelo[x,y] naapurit ja sekoitetaan naapurilista.
List<Koordinaatti> naapurit = HaeNaapurit(sokkelo, x, y);
Sekoita(naapurit);
foreach (Koordinaatti naapuri in naapurit)
{
if (!sokkelo[naapuri.X, naapuri.Y])
{
PoistaSeina(sokkelo, x, y, naapuri.X, naapuri.Y);
//rekursiivinen kaivuu uudesta lähtöpisteestä.
KaivaKaytavat(sokkelo, naapuri.X, naapuri.Y);
}
}
}
/// <summary>
/// Poistetaan seinä kahden ruudun välistä
13/7
/// </summary>
/// <param name="sokkelo">sokkelo, jossa ruudut ovat</param>
/// <param name="x">ensimmäisen ruudun x</param>
/// <param name="y">ensimmäisen ruudun y</param>
/// <param name="x2">toisen ruudun x</param>
/// <param name="y2">toisen ruudun y</param>
private static void PoistaSeina(bool[,] sokkelo, int x, int y, int x2, int y2)
{
//Tarkistetaan, ettei anneta "tyhmiä" parametreja. Liikutaan siis vain
//sivu- tai pystysuunnassa.
if (x != x2 && y != y2) return;
//seinä oikealla:
if (x2 - x == 2) sokkelo[x + 1, y] = true;
//seinä alhaalla:
else if (y2 - y == 2) sokkelo[x, y + 1] = true;
//seinä vasemmalla:
else if (x - x2 == 2) sokkelo[x - 1, y] = true;
//seinä ylhäällä:
else sokkelo[x, y - 1] = true;
}
/// <summary>
/// Haetaan sokkelon[x,y] naapuriruudut.
/// Ruutujen koordinaatit palautetaan listana.
/// </summary>
/// <param name="sokkelo">sokkelo, jossa ruudut ovat</param>
/// <param name="x">ruudun x-koordinaatti</param>
/// <param name="y">ruudun y-koordinaatti</param>
/// <returns>Lista naapureita</returns>
private static List<Koordinaatti> HaeNaapurit(bool[,] sokkelo, int x, int y)
{
List<Koordinaatti> naapurit = new List<Koordinaatti>();
//Naapurit löytyy liikkumalla 2 ruutua oikealle, alas, vasemmalle tai
//ylös, kunhan ei liikuta sokkelon ulkopuolelle.
if (x + 2 < sokkelo.GetLength(0)) naapurit.Add(new Koordinaatti(x + 2, y));
if (y + 2 < sokkelo.GetLength(1)) naapurit.Add(new Koordinaatti(x, y + 2));
if (x - 2 > 0) naapurit.Add(new Koordinaatti(x - 2, y));
if (y - 2 > 0) naapurit.Add(new Koordinaatti(x, y - 2));
return naapurit;
}
/// <summary>
/// Aliohjelma sokkelon tulostamiseen. truen kohdalla tulostetaan välilyönti,
/// falsen kohdalla X
/// </summary>
/// <param name="sokkelo">tulostettava sokkelo</param>
public static void tulostaSokkelo(bool[,] sokkelo)
{
14/7
for (int i = 0; i < sokkelo.GetLength(0); i++)
{
for (int j = 0; j < sokkelo.GetLength(1); j++)
{
if (sokkelo[i, j]) Console.Write(' ');
else Console.Write('X');
}
Console.Write("\n");
}
}
/// <summary>
/// Sekoittaa annetun listan.
/// </summary>
/// <typeparam name="T">Listan alkioiden tyyppi</typeparam>
/// <param name="lista">Lista, joka sekoitetaan</param>
public static void Sekoita<T>(List<T> lista)
{
int n = lista.Count;
while (n > 1)
{
n--;
int k = rng.Next(n + 1);
T value = lista[k];
lista[k] = lista[n];
lista[n] = value;
}
}
}
/// <summary>
/// Luokka koordinaateille.
/// </summary>
public class Koordinaatti
{
public int X { get; set; }
public int Y { get; set; }
/// <summary>
/// Luo uuden (x,y)-koordinaatin
/// </summary>
/// <param name="x">x-koordinaatti</param>
/// <param name="y">y-koordinaatti</param>
public Koordinaatti(int x, int y)
{
X = x;
Y = y;
}
}
}
15/7
16/7