Zie scherp

Dit boek wordt gebruikt als handboek binnen de opleiding professionele bachelor elektronica-ict van de AP Hogeschool.

Concreet is dit het handboek voor de eerste 2 semesters omtrent 'leren programmeren met C#':

  • Deel 1: Programming Principles, eerste semester
  • Deel 2: Object Oriented Programming, tweede semester

Indien bepaalde hoofdstukken of onderdelen niet duidelijk zijn of fouten bevatten, aarzel dan niet om me te contacteren. Normaal gezien zijn alle tekst en afbeeldingen de mijne, tenzij ik anders vermeld. Uiteraard maak ik soms fouten, als je dus een niet geattribueerde tekst of afbeelding ontdekt, aarzel dan niet om me te contacteren.

Alle feedback is zéér welkom.

Veel leer-en leesplezier,

Tim Dams

Logo ikke

PS: Besef dat goed kunnen programmeren enkel kan geleerd worden indien je ook effectief veel programmeert. Je kan ook niet leren fietsen door enkel een handboek "Fietsen met Merckx" te lezen, je zal op de fiets moeten springen! En vallen...véél vallen.

Licensed under Creative Commmons Attribution-NonCommercial 4.0 International (CC BY-NC 4.0)

LiceniconLiceniconLicenicon

Licensed under CC-BY-NC 4.0 as shown here.

This is a human-readable summary of (and not a substitute for) the license.

You are free to

Share : copy and redistribute the material in any medium or format

Adapt : remix, transform, and build upon the material

The licensor cannot revoke these freedoms as long as you follow the license terms.

Under the following terms

Attribution : You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
NonCommercial : You may not use the material for commercial purposes.

No additional restrictions : You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.

Hoofdstuk 1

Kijk pas naar de oplossing als je 100% klaar bent. En zelfs dan, wees erg kritisch over jouw oplossing tegenover de modeloplossing. Vraag hulp aan de lector bij de minste twijfel die je hebt wanneer je jouw oplossing vergelijkt me de modeloplossing!

Rommel zin (Essential) (Dodona beschikbaar)

Schrijf een applicatie met behulp van ReadLine() en WriteLine()-methoden waarbij de computer aan de gebruiker om zijn of haar favoriete kleur, eten, auto en boek vraagt. Het programma zal de antwoorden echter door elkaar halen waardoor de computer vervolgens toont:

Je favoriete kleur is [eten]. Je eet graag [auto]. Je lievelingsfilm is [boek] en je favoriete boek is [kleur].

Waarbij tussen de rechte haakjes steeds de invoer komt die de gebruiker eerder opgaf voor de bijhorende vraag.

Voorbeeld:

Geef je favoriete kleur:
rood
Geef je favoriete eten:
lasagne
Geef je favoriete auto:
mazda
Geef je favoriete boek:
Het oneindige verhaal

Je favoriete kleur is lasagne. Je eet graag mazda. Je lievelingsfilm is Het oneindige verhaal en je favoriete boek is rood.

Kleuren veranderen (Essential)

Kan je je programma van zonet aanpassen zodat iedere zin in een andere kleur is?

Bekijk de werking van Console.Write(). Kan je ieder woord in een andere kleur zetten?

Go nuts!

Visitekaart (Essential) (Dodona beschikbaar)

Schrijf een programma dat aan de gebruiker de volgende zaken vraagt:

  • Voornaam
  • Achternaam
  • Adres
  • Hobby

Vervolgens toon je de vragen, telkens in een andere kleur, gevolgd door de antwoorden, met de naam als 1 antwoord (voornaam en achternaam). De antwoorden zijn in de originele kleur.

Bijvoorbeeld:

Wat is je voornaam?
Tim
Wat is je achternaam?
Dams
Waar woon je?
Scheve dreef 666
Wat is je hobby?
lezen en schrijven


Goed. Hier volgt je visitekaartje"

Naam: Tim Dams
Adres: Scheve dreef 666
Hobby: lezen en schrijven

Tekening

Kan je volgende afbeeldingen namaken in de console?

Volgende tekening toont een schematische weergave:

Je kan een gekleurd vakje 'tekenen' door de BackGroundColor van de console in te stellen en dan een spatie naar het scherm te sturen.

Muziek

Met de Console.Beep() methode kan je muziek maken. Volgende voorbeeld toont bijvoorbeeld hoe je do-re-mi-fa-sol-la-si-do afspeelt:

Console.Beep(264, 1000);
Console.Beep(297, 1000);
Console.Beep(330, 1000);
Console.Beep(352, 1000);
Console.Beep(396, 1000);
Console.Beep(440, 1000);
Console.Beep(495, 1000);
Console.Beep(528, 1000);

Je geeft aan Beep 2 getallen mee (argumenten):

  1. De frequentie van de toon die moet afgespeeld worden. Bijvoorbeeld 264 (in Hertz, hz).
  2. De duur dat de toon moet afgespeeld worden in milliseconden. Als je dus 1000 meegeeft zal de toon gedurende 1000 ms, oftewel 1 seconde, afgespeeld worden.

Open 1 van de eerder gemaakte oefeningen en zorg ervoor dat bij het opstarten ervan er een kort, door jezelf gecomponeerd, introliedje wordt afgespeeld.

Hoofdstuk 2

Het is aanbevolen dat je vanaf nu per hoofdstuk een nieuwe solution aanmaakt op je computer. Per oefening maak je dan best een nieuw project aan dat je in de solution zet. Geef ieder project een duidelijke naam (bv EuroNaarDollar) zodat je later vlot je oefeningen kan terugvinden. Denk er aan dat je je projecten best enkel met cijfers en letters benoemd (dus beter EuroNaarDollar dan Euro-Dollar).

Meer uitleg over Projecten en Solutions en hoe je bovenstaande doet kan je hier terugvinden.

Indien er in een oefening gevraagd wordt om bijvoorbeeld een getal naar een ander om te zetten (zie oa Euro naar Dollar) dan mag je dit getal in een variabele "hardcoded" plaatsen. We kunnen nog niet input van de gebruiker naar getallen omzetten en kunnen dus nog geen oefeningen maken waarin we dat doen.

Voor Euro naar Dollar schrijf je dus best bijvoorbeeld als eerste:

double euroInput= 5.32;

Simple maths

Schrijf een programma dat de uitkomst van volgende resultaten op het scherm toont:

-1 + 4 * 6
( 35 + 5 ) % 7
14 + -4 * 6 / 11
2 + 15 / 6 * 1 - 7 % 2

Bewaar het resultaat van iedere opgave in een aparte variabele. Reken op papier of met je calculator na wat het resultaat moet zijn. Toon vervolgens de uitkomst (m.b.v. WriteLine) telkens op het scherm en vergelijk die met je eigen berekening. Begrijp je waar de verschillen zitten (als die er zijn)?! Waarom moet je bij de derde en vierde expressie met double werken en niet bij de eerste 2?.

De % hier is de modulo-operator.

Je toont het resultaat als volgt:

-1 + 4 * 6 geeft 23
( 35 + 5 ) % 7 geeft 5 
14 + -4 * 6 / 11 geeft 11,818181818181818
2 + 15 / 6 * 1 - 7 % 2 geeft 3,5

Gemiddelde (Essential)

Maak 3 variabelen aan van het type int genaamd september, oktober en november. Plaats in elke variabele de hoeveelheid uren slaap je die maand verwacht te doen. Bereken nu het gemiddelde van 3 maanden en toon het resultaat op het scherm (kies uiteraard 3 verschillende hoeveelheden slaap). Opgelet: het resultaat moet als een kommagetal worden getoond!

Euro naar dollar (Essential)

Ontwerp een toepassing waarmee je een bedrag, inclusief komma-getallen in euro kan omrekenen naar dollar. Gebruik hierbij de huidige wisselkoers. Je hoeft niet af te ronden. Het resultaat op het scherm wordt als volgt weergegeven: [x] EUR is gelijk aan [y] USD.

Tafel en Console.Clear() (Essential)

Met het statement Console.Clear(); kan je de console - je raadt het nooit - leegmaken. Test deze code in het volgende programma:

Schrijf een programma dat de tafels van vermenigvuldigen geeft van 411 (dus 1x411= 411, 2x411= 822 tot en met 10x411= 4110). Toon telkens 1 zin en wacht dan tot de gebruiker op enter duwt om de volgende vermenigvuldiging op een nieuw scherm te tonen. De output ziet er dus als volgt uit:

1 x 411 = 411
Druk op enter voor de volgende lijn.
[Scherm leeg gemaakt]
2 x 411 = 822
Druk op enter voor de volgende lijn.
[Scherm leeg gemaakt]
...

Plaats 411 in een variabele aan de start van het programma en gebruik deze in je berekeningen verderop. Toon dat je code ook werkt door de inhoud van de variabele in een ander getal te veranderen zodat je van dat nieuwe getal nu de tafels van vermenigvuldiging krijgt. Door de waarde 411 bovenaan in een variabele te steken kan je heel snel je programma een andere tafel laten berekenen, je hoeft dan gewoon de waarde van die variabele aan te passen.

Je kan wachten tot de gebruiker op enter duwt door gewoon een lege Console.ReadLine te doen, zoals volgende voorbeeld toont:

Console.WriteLine("Eerste beeld");
Console.WriteLine("Druk enter om voort te gaan.");
Console.ReadLine();
Console.Clear();
Console.WriteLine("Tweede beeld");

Merk op dat Console.Clear() niet werkt zoals verwacht op Mac.

Kill/Death-ratio (Essential) (Dodona beschikbaar)

Maak twee variabelen double kills en double deaths aan. Wijs er jouw typische scores aan toe die je haalt in een spel naar keuze. Bereken en toon vervolgens je kill/death-ratio.

Begrijp je waarom we best een van beide variabelen in double zetten? Lees de waarschuwing van de voorman bij Expressies zeker nog eens na... Of test eens wat er gebeurt indien je met int zou werken.

Merk op dat het vreemd is dat je een niet-geheel aantal kills of deaths kunt halen als je met double werkt. Dat kan natuurlijk niet. Je kunt niet 2,5 keer dood zijn gegaan in een spel. In hoofdstuk 4 gaan we dit oplossen (en er dus voor zorgen dat kills en deaths wel als int kan blijven staan, maar we toch een double als resultaat voor de kdratio krijgen)

De k/d ratio is de verhouding tussen het aantal kills da je in een spel hebt gehaald ten opzichte van het aantal keer dat je zelf bent gestorven, deads. Wanneer deze verhouding dus groter is dan 1 heb je vaker iemand gedood dan dat je zelf gestorven bent. Onder de 1 is dit net omgekeerd...en ben je dus niet zo goed in het spel dat je aan het spelen bent.

BTW (Essential)

Schrijf een programma waarin je het BTW-percentage 21% als een constante definieert door het keyword const voor de variabele te zetten. Vervolgens toon je een prijs naar keuze, met en zonder btw op het scherm.

Gewicht in space (Essential)

Je massa is overal dezelfde. Je gewicht daarentegen is afhankelijk van de zwaartekracht van de plek waar je bent. Zo is je gewicht veel groter op Jupiter dan op Mars (uitgedrukt in Newton).

Maak een variabele double gewichtOpAarde aan. Wijs een gewicht toe (bv. het jouwe). Schrijf nu een programma dat onder elkaar jouw gewicht op verschillende planeten toont.

Hier de omzettingstabel (je gewicht op Mercurius is dus je gewicht op aarde x 0.38):

  • Mercurius: 0.38
  • Venus: 0.91
  • Aarde: 1.00
  • Mars: 0.38
  • Jupiter: 2.34
  • Saturnus: 1.06
  • Uranus: 0.92
  • Neptunus: 1.19
  • Pluto: 0.06 (we laten de discussie achterwege of pluto wel of niet een planeet is)

De output zijn verschillende lijnen onder elkaar in de vorm van:

Je weegt op [planeet] [gewichtopdieplaneet] N.

Plaats de zin met Jupiter in het rood, daar je daar het zwaarst weegt en die van pluto in het groen.

Tekstmaker (PRO)

PRO oefeningen bevatten leerstof die mogelijk niet in deze cursus wordt behandeld, of die in een later hoofdstuk pas aan bod zal komen.

Met de methode System.IO.File.WriteAllText(); kan je een string naar een bestand wegschrijven.

Je geeft hierbij 2 variabelen mee: de bestandsnaam, en de inhoud van het bestand.

Volgende voorbeeld schrijft bijvoorbeeld de zin "Ik ben tim" weg naar een bestand dat zal aangemaakt worden genaamd "me.txt":

System.IO.File.WriteAllText("me.txt", "Ik ben tim");

Schrijf een programma dat aan de gebruiker de naam van het bestand vraagt, gevolgd door wat er in het bestand moet geschreven worden. Vervolgens maak je dit bestand aan en plaats je die inhoud er in.

Als je enkel een bestandsnaam meegeeft (en geen volledige folderpath) dan wordt het bestand geplaatst op de plek waar het programma wordt uitgevoerd.

Standaard staat je gecompileerde programma in de bin\debug folder van je project.

Als je dus een solution hebt aangemaakt genaamd "Oefening" in de folder "C:\Temp" dan zal het bestand zich in de volgende folder bevinden "C:\Temp\Oefening\Oefening\bin\debug".

Je kan ook snel naar deze folder gaan door in de solution explorer aan de rechterzijde in VS te rechterklikken op je project en dan te kiezen voor: Open folder in file explorer.

Hoofdstuk 3

Bekijk volgende kennisclip waarin wordt uitgelegd hoe je meerdere projecten in 1 solution in VS kunt plaatsen: Meerdere projecten in 1 solution

Probeer vanaf nu per hoofdstuk 1 solution aan te maken met daarin meerdere projecten (1 project per oefening)

Sommige oefeningen zullen soms (PRO) in de titel hebben. Dit zijn pittigere oefeningen. Probeer ze zeker, maar laat je niet ontmoedigen als ze niet direct lukken. Het kan erg nuttig zijn om enkele hoofdstukken later nog eens terug naar deze oefeningen te gaan zien, wie weet kan je ze dan wel oplossen.

Unicode Art

Genereer je naam in Unicode Art met een van de vele online generators. Toon deze art (m.b.v. WriteLine of Write) aan de start van een van een bestaande programma zodat nu je naam wordt getoond wanneer het programma start, gevolgd door de rest.

String interpolation (Essential)

Kies 2 oefeningen uit het vorige hoofdstuk waarin je output op het scherm moest geven. Pas string interpolatie m.b.v. $ (manier 2) toe in deze 2 oefeningen.

Systeem informatie (Essential)

Volgende 2 oefeningen zijn al iets stevigers. Iedere oefening eindigt met een (PRO) gedeelte dat je best enkel doet als je een uitdaging wenst.

Deel 1

Maak een applicatie die de belangrijkste computer-informatie (geheugen, etc) aan de gebruiker toont m.b.v. de Environment bibliotheek. Zoals je ziet wordt het geheugen in bytes teruggegeven. Zorg ervoor dat het geheugen steeds in mega of gigabytes op het scherm wordt getoond.

Formateer de informatie met behulp van de $-notatie zodat deze deftig getoond worden en de gebruiker snel de belangrijke informatie over z'n systeem te zien krijgt.

Deel 2 (PRO)

Ook informatie over de harde schijven kan je verkrijgen (in bits). Dit vereist wel dat je bovenaan je programma volgende lijn bijschrijft: using System.IO.

Vervolgens kan je in je programma schrijven:

long cdriveinbytes = DriveInfo.GetDrives()[0].AvailableFreeSpace;  
long totalsize = DriveInfo.GetDrives()[0].TotalSize;  

De lijn met using is om aan te geven dat we iets uit de System.IO bibliotheek nodig hebben, namelijk DriveInfo. Schrijven we dat niet dan moeten we in onze code DriveInfo aanroepen met z'n volledige path: System.IO.DriveInfo....

De 0 tussen rechte haakjes is de index van welke schijf je informatie wenst. 0 is de eerste harde schijf, 1 de tweede, enzovoort.

Vraag aan de gebruiker het nummer van de harde schijf waar meer informatie over moet getoond worden.

Opgelet: sta toe dat de gebruiker 1 voor de eerste harde schijf mag gebruiken, 2 voor de tweede, enzovoort. Je zal dus in code nog manueel 1 moeten aftrekken van de invoer van de gebruiken. Bv:

int invoer=int.Parse(Console.ReadLine()) - 1; 
long totalsize = DriveInfo.GetDrives()[invoer].TotalSize;  

Weerstandberekenaar

Stel dat je in het labo een weerstand vastneemt en je kent de kleurcodes van de streepjes wel, maar niet hoe je die kunt omzetten naar de effectieve weerstandswaarde. In dit programma kunnen we de gebruiker helpen.

Bron afbeelding: https://www.esdsite.nl

Deel 1

Maak een programma dat de weerstandwaarde berekent gebaseerd op:

  • Ring 1: die de tientallen voorstelt
  • Ring 2: die de eenheden voorstel
  • (PRO) Ring 3: die de exponent (10 tot de macht) voorstelt. (tip:Math.Pow(10,ring3))

Gebruik twee variabelen van het type int waar je getal van 0 tot 9 telkens aan kan toewijzen. (we veronderstellen dus dat de gebruiker de kleurcode heeft omgezet naar een getal en dat toewijst aan de variabele)

Test dat je rekening klopt om gebaseerd op 2 (of 3) ringen de weerstandswaarde te berekenen.

Deel 2

Plaats het geheel in een mooie Unicode-tabel

Hier enkele nuttige tekens:

╔═══════════════╦═══════════════╗
║               ║               ║
╟───────────────╫───────────────╢
║               ║               ║
╚═══════════════╩═══════════════╝

Gebruik $-string interpolatie om de informatie in de tabel te tonen zodat je volgende uitvoer kunt genereren:

Je zal er geen UX-prijzen mee verdienen, maar it's a start

of:

Nog een voorbeeld

Gebruik tabs ("\t") om ervoor te zorgen dat je tabel mooi blijft wanneer je door de string interpolatie langere variabelen gaat plaatsen.

Deel 3 (PRO)

Kan je afhankelijk van de ringwaarde het getal in de tabel in de juiste kleur zetten conform de weerstandskleuren (tip: je zal Write en if of switch moeten leren gebruiken).

Shell-starter (PRO)

Je kan de output van een Process.Start() programma naar je console scherm sturen. Dit vereist wat meer code. Volgend voorbeeld zal de output van het commando ipconfig /all op het scherm tonen:

System.Diagnostics.Process process = new System.Diagnostics.Process();
process.StartInfo.FileName = "ipconfig";
process.StartInfo.Arguments = "/all"; 
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.Start(); //start process

// Read the output (or the error)
string output = process.StandardOutput.ReadToEnd(); //normal output
Console.WriteLine(output);
string err = process.StandardError.ReadToEnd(); //error output (if any)
Console.WriteLine(err);
//Continue
Console.WriteLine("Klaar");

Let er op dat dit voorbeeld niet perfect werkt met een shell-commando dat even duurt. Denk bijvoorbeeld aan ping. De output komt namelijk pas op het scherm als het commando is afgelopen. Test zelf maar eens!

Maak enkele kleine C# programma's die bepaalde shell-commando's zullen uitvoeren, eventueel na input van de gebruiker. Enkele nuttige shell-commando's in de netwerk-sfeer zijn bijvoorbeeld:

hostname
arp -a
getmac
nslookup google.com
netstat

Andere toffe commando's kunnen zijn:

chrome.exe ap.be
notepad mytest.txt

Of de naam van een bestand dat je wilt openen, maar dan met het hele path:

c:\Temp\mydocument.docx

Unicode Art & Colors (niet PRO, wel saai)

Gebruik je kennis van het verschil tussen Console.Write en Console.WriteLine, alsook de werking van kleuren in console-applicaties, om je Unicode-art naam van de eerdere oefening nu van kleur te voorzien. Zorg ervoor dat minstens 1 letter in een andere kleur is.

Dit is een redelijke lange oefening, doe deze best op het einde.

Deze oefening kan je op 2 manieren oplossen:

  1. Je gebruikt een combinatie van Write en Console.SetCursorPosition
  2. Je knipt je tekening op in stukken die je met verschillende Write en WriteLines op het scherm zet. Volgend voorbeeld toont bijvoorbeeld hoe je een vierkantje van sterretjes tekent waarbij het midden van het vierkant rood is:
Console.WriteLine("***");
Console.Write("*");
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("*");
Console.ResetColor();
Console.WriteLine("*");
Console.WriteLine("***");

Hoofdstuk 4

Gebruik je kennis van debuggen om vanaf dit hoofstuk problemen op te lossen. Gebruik niet Console.WriteLine() om de waarde van een variabele te controleren at-runtime, maar gebruik daarentegen breakpoints!

Supercomputer (Dodona beschikbaar)

Vanaf dit punt zullen de meeste oefeningen iets "vragen" aan de gebruiker. Hiermee wordt bedoeld dat je de gebruikersinput via ReadLine moet inlezen en indien nodig moet converteren naar het gewenste type.

Vraag aan de gebruiker 3 kommagetallen. Bereken het gemiddelde van deze 3 getallen en toon dit als een kommagetal op het scherm

Voorbeeld:

Geef getal 1:
23,4
Geef getal 2:
34,6
Geef getal 3:
27,7
Het gemiddelde hiervan is: 28,566666666666666

Vierkant (Essential) (Dodona beschikbaar)

Schrijf een programma om de omtrek en de oppervlakte van een vierkant te bepalen. De zijde wordt ingelezen.

Voorbeeld:

Geef de zijde:
4,6
Omtrek is 18.4
Oppervlakte is 21,16

Schaak-ELO (Essential)

Sinds 2022 en de sappige verhalen rond Magnus en Niemann is schaken erg populair aan het worden bij "de massa". Tijd om hier dus een oefening rond te maken.

"Een Elo-rating is een getalsmatige aanduiding van de sterkte van een speler. Het wordt het meest gebruikt in schaken, dammen en go, maar kan in principe gebruikt worden bij elke sport waarbij spelers 1 tegen 1 spelen." (bron Wikipedia). We gaan een applicatie schrijven (zie verderop voor de effectieve werking van de applicatie) die:

1° De verwachte score (Ea en Eb) berekend indien 2 spelers tegen elkaar gaan spelen, gebaseerd op hun ELO-rating (Ra en Rb) die je applicatie aan de gebruiker vraagt. 2° Berekenen van hun nieuwe Elo score (R'a en R'b) gebaseerd op de effectieve uitslag (Sa en Sb).

Volgende afbeelding (bron) toont beide stappen:

Opmerkingen bij deze formules:

  • De waarde K mag je standaard op 10 zetten (dit geeft aan dat er maximum 10 Elo-punten kunnen bijkomen of afgaan).
  • De eindscore (Sa en Sb) is als volgt: 1 voor een win, 0,5 voor gelijkstond, 0 voor verlies.
  • Gebruik voor ALLES doubles.
  • De finale, nieuwe, rating wordt afgerond tot 0 cijfers na de komma.

Getalvoorbeeld:

Indien speler A een rating van 1000 heeft en B 1100 dan zal speler A na een gewonnen wedstrijd een rating van 1006 krijgen en speler B 1094.

Applicatie

Schrijf een applicatie die eerst de Elo-ratings van beide spelers vraagt. Vervolgens toont de applicatie de nieuwe Elo-ratings voor de 3 scenario's:

  • Indien speler A wint.
  • Indien speler B wint.
  • Indien er een gelijke stand (draw) is.

K vragen

Breidt de applicatie uit en vraag de waarde K ook aan de gebruiker en gebruik deze in je berekeningen.

Balk (Dodona beschikbaar)

Bereken de oppervlakte en de inhoud van een balk . De gegevens (hoogte, breedte, lengte) worden ingelezen als gehele getallen. Zorg ervoor dat de uitvoer er als volgt uitziet:

Lengte?
3
Breedte?
5
Hoogte?
4
Oppervlakte is 94
Inhoud is 60

Geometric fun (Dodona beschikbaar)

Vraag aan de gebruiker een hoek in graden. Zet deze om naar radialen , gebruik Math.PI voor Pi. Gebruik vervolgens de verschillende geometrische functies in de Math. bibliotheek om de sinus (.Sin), cosinus (.Cos) en tangens (.Tan) van de hoek aan de gebruiker te tonen

Denk eraan: de methoden die met hoeken werken, werken in radialen, daarom moeten we deze eerst omzetten. 1 rad = 180°/PI = 57.295779513°.

Je zal merken dat voor bepaalde hoeken (bijvoorbeeld 90 graden) je erg kleine of erg grote waarden krijgt, dat is normaal. De geometrische functie in de Math-bibliotheek berekenen de resultaten (en werken dus niet met een tabel) wat met eindige kommagetallen ervoor zorgt dat je soms in plaats van 1 of 0 iets erg kleins of groot krijgt.

Uitvoer:

Geef de hoek in graden:
[hoekInGraden]

Sinus van deze hoek is: [sinus]
Cosinus van deze hoek is: [cosinus]
Tangens van deze hoek is: [tangens]

BMI berekenaar (Essential) (Dodona beschikbaar)

Maak een programma dat aan de gebruiker z'n lengte (in cm) en gewicht (in kg) vraagt en vervolgens de berekende BMI (Body Mass Index) toont. Zoek zelf op hoe je het BMI berekent.

Gebruik Math.Round om de uitkomst tot maximum 2 cijfers na de komma te tonen.

Reken na met je rekenmachine of je uitkomst wel degelijk klopt!

Uitvoer:

Wat is uw lengte in cm?
[lengteInCm]

Wat is uw gewicht in kg?
[gewicht]

Een persoon met een lengte van [lengteInMeter] m en een gewicht van [gewicht] kg heeft een BMI van [bmi].

Op-de-poef (Essential)

Een vaste klant in je café bestelt altijd "op-de-poef". Dat wil zeggen dat hij niet onmiddellijk betaalt en dat z'n rekeningen worden neergeschreven. Ooit zal de klant dan gevraagd worden de hele som te betalen.

Schrijf een programma dat 5 keer na elkaar aan de barman vraagt om een bedrag in te voeren. Het ingevoerde bedrag wordt opgeteld bij wat er reeds op de rekening staat. Na 5 keer wordt de totale som getoond alsook hoeveel weken het duurt indien de klant wekelijks 10 euro afbetaalt.

Voorbeeldwerking:

Voer bedrag in?
12
De poef staat op 12 euro.
Voer bedrag in?
14
De poef staat op 26 euro.
Voer bedrag in?
3
De poef staat op 29 euro.
Voer bedrag in?
8
De poef staat op 37 euro.
Voer bedrag in?
2
De poef staat op 39 euro.
*************************
Het totaal van  de poef is 39 en zal 4 weken duren om volledig afbetaald te worden.

Voor deze oefening heb je Math.Ceiling() nodig. Deze methode zal een getal altijd naar boven afronden.

Feestkassa (Essential)

De plaatselijke voetbalclub organiseert een mosselfestijn. Naast mosselen met frietjes (20 EUR) bieden ze voor de kinderen de mogelijkheid om een koninginnenhapje (10 EUR) te kiezen. Verder is er een ijsje als nagerecht voorzien (3 EUR). Om het gemakkelijk te maken kosten alle dranken 2 EUR.

Ontwerp een applicatie zodat de vrijwilliger aan de kassa alleen maar de juiste aantallen moet ingeven, lijn per lijn. (frietjes, koninginnenhapje, ijsje, drank) om de totaal te betalen prijs te berekenen.

Het resultaat wordt als volgt weergegeven: Het totaal te betalen bedrag is x EURO.

Voorbeeld:

Frietjes?
3   
Tussenprijs= 60 euro
koninginnenhapje?
5
Tussenprijs= 60 euro + 50 euro
Ijsjes?
2
Tussenprijs= 60 euro + 50 euro + 6 euro
Dranken?
5
Tussenprijs= 60 euro + 50 euro + 6 euro + 10 euro

Het totaal te betalen bedrag is 126 EURO.

Het Orakeltje van Delphi (Essential)

Gebruik een random generator om een orakel (een duur woord voor waarzegger) te maken, namelijk de kleine broer of zus van het Orakel van Delphi. Het programma zal aan de gebruiker vertellen hoe lang deze nog zal leven. Bijvoorbeeld: "Je zal nog 15 jaar leven.".

Het orakel zal enkel realistische getallen geven. M.a.w., getallen van 5 tot en met 125 jaar.

We gaan geregeld een oefening in een later hoofdstuk verder uitbreiden. Het orakeltje van Delphi is er zo eentje. Bewaar je oefeningen dus goed!

Hoofdstuk 5

Oefeningen worden sinds vorig hoofdstuk al eens wat langer. Het is belangrijk dat eerste de volledige opgave doorneemt voor je begint te programmeren. Deze gewoonte is essentieel indien je over enkele weken grote 2uur-durende projecten tot een goed einde wenst te brengen.

BMI met if (Essential)

Pas je BMI-programma uit het vorige hoofdstuk aan zodat je programma feedback geeft aan de gebruiker naargelang het berekende BMI.

De kleur tussen haakjes geeft aan in welke kleur je deze tekst zet:

  • Onder de 18,5 (rood): ondergewicht.
  • Van 18,5 tot 25, met 25 niet inbegrepen (groen): normaal gewicht.
  • Van 25 tot 30, met 30 niet inbegrepen (donkergeel): overgewicht. Je loopt niet echt een risico, maar je mag niet dikker worden.
  • Van 30 tot 40, met 40 niet inbegrepen (rood): Zwaarlijvigheid (obesitas). Verhoogde kans op allerlei aandoeningen zoals diabetes, hartaandoeningen en rugklachten. Je zou 5 tot 10 kg moeten vermageren.
  • 40 of meer (magenta): ernstige zwaarlijvigheid. Je moet dringend vermageren want je gezondheid is in gevaar (of je hebt je lengte of gewicht in verkeerde eenheid ingevoerd).

Schaak-Elo met if en Random (Essential)

In plaats van de 3 mogelijke scenarios (win,loss, draw) vraag je nu aan de gebruiker wie van beide spelers heeft gewonnen ("A", "B" of "D" van Draw/gelijk) en toont enkel de nieuwe ratings gebaseerd op de ingevoerde uitslag.

Pas je applicatie ook aan als volgt: indien de gebruiker een negatieve rating voor een van de beide spelers opgeeft dan gebeuren er 2 zaken:

  1. Er verschijnt een foutboodschap ("Negatieve rating mag niet. Ik genereer een random rating.")
  2. Je genereert een random rating tussen 500 en 3000, toont dit getal aan de gebruiker, en gebruikt dit vervolgens verder in de applciatie.

Schoenverkoper

a) Maak een oefening die aan de gebruiker vraagt hoeveel paar schoenen hij wenst te kopen. Ieder paar schoenen kost steeds 20 euro. Indien de gebruiker 10 paar of meer koopt kosten de eerste 9 paar nog steeds 20 euro, de overige kosten echter maar 10 euro. Toon aan de gebruiker de totale prijs.

Voorbeeld:

  • 8 schoenen kost 8x20 = 160 euro
  • 12 schoenen kost 9x20 + 3x10 = 210 euro

b) Voeg nu toe dat het programma eerst aan de kassier vraagt tot hoeveel schoenen de korting niet geldt.

Voorbeeld:

  • De kassierster voert 6 in. Dan kosten 8 schoenen: 6x20 + 2x10 = 140 euro.

Je hebt niet noodzakelijk een if voor deze oefening nodig. Indien je deze oefening zonder if kunt oplossen, dan krijg je als extra opgave bij

c) Zorg ervoor dat de kassier enkel een getal van 3 tot en met 10 kan invoeren bij de vraag tot hoeveel schoenen de korting niet geldt. Indien de kassier een getal buiten deze range invoert wordt de gewone korting toegekend, namelijk vanaf 10 of meer schoenen.

Ohm-berekenaar

Vraag aan de gebruiker wat hij wenst te berekenen: spanning, weerstand of stroomsterkte. Vraag vervolgens de 2 andere waarden (als dus de gebruiker "Spanning" kiest vraag je aan de gebruiker de stroomsterkte en de weerstand) en bereken m.b.v. de wet van Ohm de gewenste waarde.

BankRekening controle (Essential)

Schrijf een programma om het vroegere nationale bankrekeningnummer te controleren of het al dan niet een geldig nummer is (dus niet het IBAN nummer).

Het bankrekeningnummer wordt ingelezen als 3 gehele getallen van 3, 7 en 2 die de gebruiker apart invoert en die je in 3 aparte variabelen bewaard.

Een bankrekeningnummer is geldig als de rest van de deling van het getal, dat bestaat uit de eerste 10 cijfers, door 97, gelijk is aan het getal bestaande uit de laatste 2 cijfers.

  • Dit is een voorbeeld van een geldige rekenig: 000 1459325 57 , want 000 1459325 gedeeld door 97 geeft als rest 57.
  • Dit een ongeldige: 000 1359325 77, want 1359325 gedeeld door 97 geeft als rest 64 en niet 77

Kleurcode weerstand naar ohm

Vraag aan de gebruiker om de ringkleuren van de eerste 3 ringen in te voeren als tekst (bv groen). Toon vervolgens de waarde van deze weerstand. Als dus de gebruiker na elkaar invoert:

rood
paars
rood

Dan zal het programma tonen:

Deze weerstand heeft een waarde van 2700 Ohm

Los deze oefening op met een switchstatement.

Orakeltje van Delphi, part deux

We gaan het Orakeltje verbeteren. Voor het orakel je vertelt hoe lang je te leven hebt zal het eerste vragen of je een vrouw (v) of een man (m) bent. Dan vraagt ze je leeftijd. Mannen leven maximum tot hun 120 jaar. Vrouwen echter tot 150 jaar. Laat het orakel een duur voorspellen die kan. Als een vrouw van 50 de vraag stelt dan zal het orakel dus een getal van 5 tot en met 100 (150-50) genereren. Een man van 35 zal van 5 tot en met 85 (120-35) jaren langer kunnen leven.

Casino

Genereer een random getal van 1 t.e.m. 6 maar toon dit niet aan de gebruiker. Vraag aan de gebruiker welk getal hij denkt dat de computer heeft "geworpen". Indien de gebruiker juist raadt verschijnt er "proficiat" op het scherm. Anders: "you lose".

Casino 3

Vul de voorgaande oefening aan, maar laat de gebruiker 3x na mekaar raden. Enkel als hij juist raadt mag hij nog eens raden. Als hij ook de derde juist raadt wint hij. In alle andere gevallen niet.

Schaakstuk (Essential)

Schrijf een applicatie om het volgende te verwezenlijken: De bedoeling is om 3 stukken willekeurig te kiezen van een schaakspel. Maak maximaal gebruik van enumeratie-types. Op het scherm moet dus willekeurig een opsomming van 3 stukken (als woorden) verschijnen. De uitvoer kan dus bijvoorbeeld zijn:

wit paard
zwarte loper
witte pion

Voor de experts die al vlot werken met casting, enums en Random: je kan deze oefening ook oplossen zonder ook maar 1 switch of if. Beter zelfs: je kan deze oplossen met 3 WriteLines en een Random getal generator.

Quiz (Essential)

Maak een quiz. Maak gebruik van het switch-statement om de input van de gebruiker (a, b, c of d) te verwerken en bij iedere vraag aan te geven of dit juist of fout is. Voorzie 3 multiple choice vragen. Houd bij hoe vaak de speler juist antwoordde en geef op het einde de eindscore (Juist is +2, fout is -1).

Zoek op hoe je de kleur van de letters en de achtergrond in een console-applicatie kunt aanpassen en pas dit toe op je quiz om deze er iets boeiender uit te laten zien. Toon iedere vraag op een nieuw scherm.

Schrikkeljaar

De gebruiker voert een jaartal in en jouw programma toont of het wel of geen schrikkeljaar is. Een schrikkeljaar is deelbaar door 4, behalve als het ook deelbaar is door 100, tenzij het wél deelbaar is door 400. Bijvoorbeeld:

  • 1997: geen schrikkeljaar
  • 1996: wél schrikkeljaar
  • 1900: geen schrikkeljaar
  • 2000: wél schrikkeljaar

GuntherD Stemwijzer (Essential)

Kan jij volgende ludieke stemwijzer van GuntherD in een eenvoudig programma gieten dat door een reeks j/n vragen aan de gebruiker uiteindelijk zijn "stemprofiel" toont?

Bron Stubru

Enum seizoenen

Maak een enum die de seizoenen van het jaar bevat. Vraag aan de gebruiker om een maandnummer in te voeren. Gebruik vervolgens een switch om te bepalen in welk seizoen deze maand (grotendeels) ligt. Wijs deze enum toe aan een variabele in de switch. Vervolgens gebruik je een if om, gebaseerd op deze enum-variabele, te tonen of het om een koud seizoen (winter en herfst) of een warm seizoen (zomer en lente) gaat.

Fifa ranking berekenen (PRO)

FIFA berekent de score per nationaal voetbal team met een eigen formule bron waarvan de belangrijkste informatie hier samengevat wordt:

Schrijf een applicatie die de nieuwe P berekent nadat de gebruiker alle nodige informatie heeft ingevoert. Bij de invoer van I krijgt de gebruiker een keuze menu te zien dat de verschillende zinnetjes toont zonder de I-waarde. De gebruiker kiest uit het menu de juiste importance (bijvoorbeeld door a, b, c, etc. in te voeren) en jouw programma zet dit dan om naar een getal. De gebruiker moet voor W invoeren: gewonnen, verloren, gelijk. Jouw programma zal dit omzetten naar het juiste getal (1, 0.5 of 0).

Enum bij BMI

Maak een enum die de verschillende soorten gewichten voorstelt (Obees, Zwaarlijvig, NormaalGewicht, etc.) Pas de bestaande "BMI met if" oefening aan zodat je deze enum gebruikt om je code leesbaarder te maken.

Dit zijn goede oefeningen om te testen of je alle leerstof van de voorbije hoofdstukken goed beheerst. De volgende opgaven waren ook soms effectieve (tussentijdse) vaardigheidsproeven voor dit vak.

Geschatte tijd om dit te maken: 60 minuten

Module 1 Afsluiter

Opgave

Maak een multitool-applicatie die wanneer deze opstart aan de gebruiker een menu toont. Afhankelijk van de input van de gebruiker zal er vervolgens een ander stuk uitgevoerd worden. De verschillende keuzes zijn (de gebruiker voert een cijfer in!):

  • Menuitem 1 - Rekenmachine. Indien dit gekozen wordt zal het programma eerst 2 getallen aan de gebruiker vragen, gevolgd door een keuzemenu waarop de gebruiker een operator kan kiezen (+,-,*,/,%). Het programma toont dan het resultaat indien de operator op de twee ingevoerde getallen wordt uitgevoerd. Wanneer de uitkomst negatief is dan zal de uitkomst in het rood worden getoond.
  • Menuitem 2 - Password tester: Vraag aan de gebruiker z'n paswoord. Indien dit gelijk is aan "TrumpSux" zal er een groene tekst "Toegelaten" verschijnen. Zoniet een rode "Verboden" tekst.
  • Menuitem 3 - Recyclage: Kies eender welk programma dat je in dit of vorig hoofdstuk hebt gemaakt waarin je input aan de gebruiker ook verwerkt. Plaats dit programma achter dit menuitem
  • Menuitem 4 - Computersolver : Implementeer volgende flowchart . Een ruit geeft aan dat je een ja/nee vraag aan de gebruiker stelt (gebruik de tekst dus in de ruit). Een rechthoek (ook die met ronde hoeken) is tekst die je aan de gebruiker toont.

Opgave vaardigheidsproef 2019-2020, deel 1 - IT Helpdesk

Geschatte tijd om dit te maken: 90 minuten

Introductie

Maak een applicatie die de IT-helpdesk medewerkers kunnen gebruiken om bij gebruikers computerproblemen op te lossen.

Stap 1- Flowchart

Het programma implementeert volgende flowchart:

  • De gebruiker dient steeds met "ja" of "nee" te antwoorden bij de vragen.
  • De applicatie begint met "Zet de computer aan." En stelt dan de eerste vraag ("Gaat de computer aan").
  • Na ieder antwoord wordt het scherm leeggemaakt. Finaal bereikt de gebruiker dan een van de onderstaande fasen A,B of C, die verderop in de opgave worden uitgelegd.

Flowchart

Stap 2- Fase A

In fase A gebeurt het volgende:

  1. De gebruiker dient de foutcode als geheel getal in te voeren. Dit moet een getal van 0 tot en met 9 zijn. Bij alle andere getallen (bijvoorbeeld -1 of 14) verschijnt er "Los het dan zelf op he!" en sluit het programma zich af.
  2. Wanneer een correct getal werd ingevoerd verschijnt er in RODE LETTERS de boodschap "Gelieve je computer gedurende X minuten af te zetten."

X is een getal dat berekend wordt als volgt:
vierkantswortel van (foutcode * 3)

X verschijnt op het scherm met 1 cijfer na de komma.

Stap 2 – Fase B

In fase B verschijnt er op het scherm op het "Mooi zo, alles werkt."

Om de gebruiker aan te moedigen een werkende computer te hebben is er ook 25% kans dat hij een extra bonus krijgt.

In 25% van de gevallen dat fase B wordt bereikt verschijnt er namelijk een tweede zin: “En u wint ook nog eens 1 jaar gratis IT support!” De overige 75% keren dat fase B wordt bereikt verschijnt er enkel "Mooi zo, alles werkt." op het scherm.

Stap 3- Fase C

  • In deze fase wordt eerst wat meer informatie over de huidige computer getoond (merk op dat deze fase bij de klant op de computer eigenlijk moet uitgevoerd worden, maar doe maar alsof)

    Verkrijg via de Environment-bibliotheek het aantal processoren van de huidige computer ( ProcessorCount) en gebruik deze waarden om

    1. Indien er 1 processor in de computer zit komt er in witte letters op rode achtergrond op het scherm “1 processor”
    2. Indien er 2 of meer in zitten toon je het aantal processoren als getal met een groene achtergrond en blauwe letters,
  • Vervolgens berekent het programma de kostprijs voor de reparatie. Deze is 50€ per processor. Een computer met 5 of meer processoren zal altijd 200 euro kosten. Bereken de kost door het aantal processoren uit de vorige stap te gebruiken en toon dit op het scherm.

Voorbeeld 1 fase C:

Flowchart

Voorbeeld 2 fase C:

Flowchart

  • De gebruiker krijgt een bon voor een gratis reparatie indien hij een 64 bit computer gebruikt. Bevraag dit via de Environment-bibliotheek en toon "Hier een bon!" indien een 64 bit processor aanwezig is.

Opgave vaardigheidsproef 1819, module 1 - Intergalactic pizzaphone bestel-module

Geschatte tijd om dit te maken: 120 minuten

Introductie

IPP – Intergalactic PizzaPhone - is de populairste pizza-delivery service van het heelal. Ze hebben jouw hulp ingeschakeld om een nieuwe bestel-module te maken. Jouw module moet ervoor zorgen dat de telefoonoperators veel sneller de bestellingen kunnen invoeren en doorgeven aan de keuken.

yIghoSDo' chenmoHwI'! (Klingon voor: Veel succes maker!)

Opgave

De opgave bestaat uit twee delen:

  • Deel 1- Bestelmodule (15 punten) De bestelmodule uit een aantal onderdelen (Alle prijzen worden uitgedrukt in Intergalactic Credits (IC))

    • Bevraging: er wordt aan de klant een aantal vragen gesteld (bv type pizza, etc)
    • Visualisatie: de bestelling wordt getoond op het scherm
    • Berekening: de totaalprijs wordt berekend
    • Ticket: de totaalprijs wordt getoond
  • Deel 2 – Uitbreidingen (5 punten) Deze zullen achteraan de opgave uitgelegd worden. Het is aangeraden om eerst deel 1 af te werken voor je aan de uitbreidingen begint.

    De uitbreidingen zijn:

    • Een random kortingsmodule
    • Een benzine berekenaar

Deel 1- Bestelmodule

Bevraging (5 punten)

Een reeks vragen zal gesteld worden waarbij de gebruiker (operator) z’n keuze invoert en naar de volgende vraag gaat.

Pizzabodem?

De pizzabodem kan bestaat uit:

  • Cheesy crust (kost 5 IC)
  • Martian meal (kost 2.8 IC)
  • Pegasus lime (kost 12.4 IC)

Indien "Martian meal" wordt besteld moet de operator ook vertellen dat dit niet geschikt is voor kinderen onder de 54 jaar. De operator zal daarom nu de leeftijd vragen en enkel verder gaan indien deze groter of gelijk is aan 54. Indien jonger stopt programma en verschijnt er “ERROR” in rode letters op het scherm.

Topping?

De gebruiker kan kiezen uit 2 soorten toppings, hij mag ook verkiezen om zonder topping verder te gaan. De kost van een topping kan afhankelijk zijn van de gekozen pizzabodem:

  • Endrali pies
    • kost 10 IC voor cheesy crust,
    • kost 15 IC voor Martian meal en Pegasus Lime
  • Italian Cheese
    • kost 5.5 IC op alle bodems
  • Geen
    • geen kost

Chefs extra?

Indien de klant voor geen topping kiest dan kan deze voor 1 IC de chef’s extra (een dikke fluim) aan z’n pizza toevoegen.

Afstand tot aarde in lichtjaar?

  • Als finale vraag dient de gebruiker door te geven hoe ver het afleveradres is in lichtjaren. Dit zal steeds een geheel getal van 1 tot en met 100 moeten zijn.
  • Indien de gebruiker een getal buiten deze grenzen ingeeft dan sluit het programma door in rode letters “ERROR” op het scherm te zetten

Visualisatie (4punten)

De pizzabestelling wordt gevisualiseerd als volgt:

  • Een letter verschijnt in een bepaalde kleur afhankelijk van de bodem, namelijk:
    • Gele C voor cheesy crust
    • Rode M voor martian meal
    • Groene P voor pegasus lime
  • Na de bodemletter verschijnt een O in de kleur van de topping.
    • Blauw voor endrali pies
    • Geel voor italian cheese
    • Indien gebruiker geen topping koos maar wel de chef’s extra verschijnt in de plaats een Witte E
    • Indien de gebruiker geen topping koos en ook geen chef’s extra dan verschijnt er geen O maar een gele Z

Voorbeelden:

Prijsberekening (3 punten)

De prijsberekening van de pizza gebruikt de prijzen hierboven vermeld en is gewoon de optelsom van de aparte delen.

Na de pizza-prijs berekening wordt berekend hoeveel de transportkosten zullen zijn. Deze zijn gebaseerd op de afstand tot de aarde in lichtjaren als volgt:

Transportkosten:

  • Afstand kleiner dan 10 : 25 IC
  • Afstand groter of gelijk aan 10: $\sqrt(s/p)+p$ (met p gelijk aan de pizzaprijs en s de afstand)
    • De transportprijs wordt naar beneden afgerond tot het eerste gehele getal.
  • Indien de chef’s extra werd gekozen dan zal er een 10% korting op de totale transportkosten gegeven worden

Ticket (3 punten)

Gebruik string interpolatie voor dit deel. Een ticket wordt getoond dat de volledige bestelling in tekst toont met erachter, via tab, steeds de prijs. Onderaan volgt de totaalprijs. Voorbeeld van een ticket:

Martian meal			2.8 IC
Italian Cheese			5.5 IC
- 	-	-	-	-	-	-	-	-
Totaal pizza			8.3 IC

Afstand		                12 Lichtjaren
Transportkosten:		9  IC
- 	-	-	-	-	-	-	-	-
TOTAAL				17.3 IC

Deel 2- Uitbreidingen

Random korting- module (2 punt)

De standaard korting voor de chef’s extra op de transportkosten is 10%. In deze uitbreiding zou het bedrijf graag hebben dat een willekeurige korting wordt toegekend. Deze zal steeds een willekeurige bedrag van 0 tot en met 50% korting zijn.

Benzine module (3 punten)

Deze module zal berekenen hoeveel tonnen benzine er nodig zijn om de pizza bij de klant te krijgen. Er is 1 ton benzine nodig per 5 lichtjaar. Indien de klant dus 12 lichtjaren ver woont dan zijn 3 tonnen nodig, waarbij de derde ton maar voor 2/5 (40%) zal opgebruikt worden.

De module zal dus steeds het geheel aantal tonnen benzine tonen , gevolgd door hoeveel % van de laatste ton nodig zal zijn (indien deze volledig opgebruikt zal worden dan toon je 100% uiteraard; bv wanneer de klant op 10 lichtjaar woont)

De benzine module toont deze informatie onderaan het ticket:

Martian meal			2.8 IC
Italian Cheese			5.5 IC
- 	-	-	-	-	-	-
Totaal pizza			8.3 IC

Afstand	                	12 Lichtjaren
Transportkosten:		9  IC
- 	-	-	-	-	-	-
TOTAAL				17.3 IC
*********************************
Informatie voor de piloot:
Benzine tonnen in te laden	2
Benzine over in laatste ton:	20%

Geschatte tijd om dit te maken: 60 minuten

Introductie

We maken een applicatie die leerlingen kunnen gebruiken om beter te weten wat ze moeten doen in tijden van corona. De applicatie bestaat uit 2 delen:

  • Een “Covidbeslissingshelper”: deze zal enkele vragen aan de leerling vragen om te beslissen of de leerling naar school mag blijven gaan of in quarantaine moet
  • Een “Covidquiz”: de leerling zal een vraag over Covid moeten oplossen om te zien of de leerling de regels goed genoeg kent.

3 punten staan op naamgeving, layout en algemene oplossingsmethode

Flowchart (2 punten)

Maak een applicatie volgens volgende flowchart en de uitleg verderop:

Gebruik een switch om afhankelijk van de invoer van de gebruiker een ander deel (Covidquiz of Covidbeslissingshelper) uit te voeren:

  • Indien de gebruiker iets anders dan ‘c’ of ‘q’ invoert dient de applicatie af te sluiten nadat het de boodschap “Verkeerde invoer” op het scherm heeft getoond.

Rechterklik op de afbeelding en bewaar of kopieer deze om ze vergroot in je eigen tekenprogramma te zien.

Covidbeslissingshelper (4 punten)

Een reeks vragen wordt gesteld, afhankelijk van het vorige antwoord wordt er mogelijk een andere vraag gesteld.

Toon finaal wat de leerling moet doen. Toon dit met tekst wiens achtergrondkleur overeenkomt met die in de flowchart (groen indien naar school mag gegaan worden, rood indien quarantaine)

Covidquiz (4 punten)

Deze quiz gaat testen of de leerling weet wanneer hij/zij getest moet worden na een risico-contact te hebben gehad. De regel is eenvoudig: bij een contact moet je je na 5 dagen laten testen.

De quiz zal dit testen als volgt:

  • • De quiz genereert een getal tussen 1 en 5 dagen, inclusief 5 zelf. Dit stelt het aantal dagen voor dat de leerling reeds in quarantaine is.
  • Dit wordt aan de gebruiker getoond.
  • Vervolgens wordt gevraagd hoeveel dagen de leerling nog moet wachten voor deze zich moet laten testen.
  • De gebruiker voert dit getal is. Indien dit juist is toon je in groene letters “correct”. Bij fout toon je in rode letters “Fout” gevolgd door het juiste antwoord

Voorbeeld: Als de computer 3 toont, dan is het juiste antwoord 2. De student moet namelijk op dag 3 nog 2 dagen wachten om op dag 5 getest te worden.

Zorg ervoor dat ieder deel (ruiten en vierkanten) uit de covidbeslissingshelper maar 1x in code moet beschreven staan. Je kan de covidbeslissingshelper in 40 of minder lijnen code oplossen.

Intro

Een bekende streamingdienst twim.tv heeft jou gevraagd om een softwarepakket te ontwikkelen dat hun klanten kunnen gebruiken terwijl ze video’s streamen. De applicatie zal de streamers helpen om viewers bij te kopen en te zien hoe goed hun stream het doet.

De applicatie bestaat uit 3 duidelijke delen die alle 3 steeds zullen uitgevoerd worden:

Belangrijke afspraken

  • Strings worden altijd m.b.v. string interpolatie naar het scherm gestuurd.
  • Gebruik constanten waar nodig

Deel 1: initialisatie

Wanneer de applicatie opstart, groet deze de gebruiker. Hierbij wordt de gebruikersnaam uit het systeem (m.b.v. de correcte Environment-eigenschap) uitgelezen.

Voorts worden volgende willekeurige getallen (uiteraard automatisch gegenereerd) aangemaakt:

  • Het aantal viewers dat naar de stream kijkt: dit kan 1000, 2000, 3000 , 4000 of 5000 zijn. Dit wordt willekeurig bepaald.
  • Het huidige budget uitgedrukt in credits van de gebruiker: dit is een willekeurig geheel getal gelegen tussen 100 (100 inbegrepen) tot en met 600.

Deel 2: Menu

Een menu wordt aan de gebruiker getoond, waarbij met tabs de keuzemogelijkheden onder elkaar worden getoond:

Gebruik enum voor de keuzes.

Keuze 1: viewers kopen

Deze optie kost 200 credits. Indien de gebruiker niet genoeg credits heeft dan verschijnt er in rode letters “Niet genoeg credits” en wordt er naar deel 3 van het programma gegaan. Indien de gebruiker 200 of meer credits heeft dan:

  • Wordt het budget met 200 verlaagd.
  • Wordt het aantal viewers met 1000 verhoogd

Voorzie ook de mogelijkheid om te onthouden of je viewers gekocht hebt. Vervolgens verschijnt er het nieuwe budget en aantal viewers op het scherm en wordt het laatste deel van de applicatie gegaan (“Bepalen van de categorie”)

Keuze 2: adspace kopen

Deze optie kost 300 credits. Indien de gebruiker niet genoeg credits heeft dan verschijnt er in rode letters “Niet genoeg credits” en wordt er naar deel 3 van het programma gegaan.

Indien de gebruiker 300 of meer credits heeft dan:

  • Wordt het budget met 200 verlaagd.
  • Wordt het aantal viewers als volgt verhoogd:
    • Indien het huidig aantal viewers een veelvoud van 2000 is komen er 1000 viewers bij
    • Zo niet komen er 500 viewers bij

Voorzie ook de mogelijkheid om te onthouden of je adspace gekocht hebt.

Vervolgens verschijnt er het nieuwe budget en aantal viewers op het scherm en wordt er naar het laatste deel van de applicatie gegaan (“Bepalen van de categorie”)

Keuze 3: niets kiezen

Indien de gebruiker optie 3, niets, kiest dan gaat het programma automatisch verder naar deel 3.

Andere Keuze: budget afname

Indien de gebruiker een ander getal dan 1, 2 of 3 invoert dan verschijnt er op het scherm dat dit geen geldige keuze is. Voorts gebeuren er volgende zaken:

  • Er wordt 10% van het budget afgetrokken (tot 2 cijfers na de komma). Het resultaat wordt naar beneden afgerond.
  • Het aantal viewers wordt met een vierde (naar boven afgerond) verminderd.

Er verschijnt op het scherm: “Je verliest 1 op 4 viewers.”

Deel 3: Menu

In dit finale gedeelte wordt de “gezondheid” van de streamer bepaald. Het aantal viewers, budget en aankopen in deel 2 bepalen hoe gezond een streamer is. Er zijn 4 soorten streamers:

  1. Beginner
  2. Gevorderde
  3. Faker
  4. Onbekend

De applicatie toont tot welke categorie de streamer zich bevind waarbij deze categorie als een enum intern wordt voorgesteld.

  • Beginner: je hebt 200 of minder credits en je hebt 4000 of minder viewers
  • Gevorderde: je hebt 5000 of meer viewers en je hebt 1 aankoop gedaan in deel 2
  • Faker: je hebt 4000 of minder viewers en je hebt in deel 2 adspace viewers gekocht
  • Onbekend: je voldoet niet aan 1 van voorgaande categorieën Indien 2 of meer categorieën gelden voor een gebruiker, dan wordt de bovenste uit deze lijst toegewezen.

Voorbeeld uitvoer

Indien de gebruiker als “Faker” wordt bestempeld krijg je de vraag of je voor 100 credits het profiel wil omgezet zien worden naar “Gevorderde”. Indien de gebruiker met “nee” antwoordt sluit het programma af. Indien de gebruiker “ja” antwoordt komt er op het scherm “Word beter!” en dan sluit het programma af.

Vanaf dit hoofdstuk zul je véél meer oefeningen voorgeschoteld krijgen dan je kan afwerken in 1 labo tijd (I dare you ;) ). Selecteer zelf de oefeningen die je wenst te doen en sla die over waarvan je overtuigd bent ze al te kunnen.

De oefeningen zijn, in de mate van het mogelijke, gerangschikt op relatieve moeilijkheidsgraad.

Indien niet expliciet vermeld mag je kiezen met wat voor loop (for, while, do while) je het probleem zal oplossen. Denk echter steeds goed na wat voor loop de beste keuze is. Indien je van te voren weet hoe vaak de loop moet uitgevoerd worden, dan is een for de beste keuze. Weet je dat niet dan kies je voor while of do while (weet je nog het verschil tussen beiden?).

De opwarmers

Oefeningen waar n wordt gebruikt geven aan dat dit een getal is dat je aan de gebruiker vraagt aan de start.

Opwarmers 1

  • Toon alle natuurlijke getallen van 1 tot n. (bv 1,2,3,4,5,6)
  • Toon alle natuurlijke getallen van n tot 1. (bv 6,5,4,3,2,1)
  • Toon alle even getallen tussen 1 en 100. (2,4,6,...100)
  • Toon alle oneven getallen tussen 1 en 100. (1,3,5,7,...99)
  • Toon de som van alle getallen van 1 tot n (dus 1+2+3+4+...+n).
  • Toon de som van alle even getallen van 1 tot n. (voorbeeld:indien de gebruiker 7 invoerde dan zal er 12 op het scherm verschijnen , namelijk (2+4+6)).
  • Toon de som van alle oneven getallen van 1 tot n.

Opwarmers van opwarmers

Met afsluitwaarde bedoelen we een waarde die de gebruiker moet invoeren om het programma te stoppen. Dus zolang de gebruiker NIET die afsluitwaarde invoert zal het programma om nieuwe waarden blijven vragen.

  1. Lees een willekeurig aantal getallen van de gebruiker (de gebruiker kiest zelf de getallen) met als afsluitwaarde 0. Bereken de som en druk die af. Je blijft dus de getallen van de gebruiker optellen tot deze 0 invoert, dan stopt het programma.
  2. Lees een willekeurig aantal getallen in met als afsluitwaarde 0. Druk het aantal strikt positieve en het aantal strikt negatieve getallen af.
  3. Lees een willekeurig aantal getallen in met als afsluitwaarde -32768. Bepaal het aantal strikt positieve getallen, het aantal strikt negatieve getallen en het aantal getallen gelijk aan nul. Druk deze aantallen af.
  4. Lees een willekeurig aantal getallen in met als afsluitwaarde 0. Bereken het product en druk dit af.
  5. (PRO) Lees een getal in en druk de som van zijn cijfers af.
  6. Lees een willekeurig aantal positieve getallen in en bereken het (afgekapt) gemiddelde ervan. De afsluitwaarde is een willekeurig negatief getal.
  7. Lees een willekeurig aantal getallen in met afsluitwaarde -32768. Druk het kleinste getal af en het aantal keer dat het voorkomt. Als de gebruiker volgende reeks invoerde: "3,2,1,2,3,1,4,5,1,2,-32768'. Dan komt er 1 als kleinste getal op het scherm en 3 (omdat 1 drie maal werd ingetypt)
  8. Een reeks in stijgende volgorde gesorteerde getallen wordt ingelezen. De invoer moet stoppen indien er een fout in de sorteervolgorde voorkomt.
  9. Een reeks getallen wordt ingelezen. De invoer moet stoppen indien er twee maal achter elkaar een nul wordt ingelezen. Het gemiddelde van de reeks getallen wordt afgedrukt. De laatste twee nullen tellen uiteraard niet mee voor de bepaling van het gemiddelde.
  10. Bepaal de som van de kwadraten van de even natuurlijke getallen van 50 tot 100 (inbegrepen). De som wordt afgedrukt.
  11. Een reeks van 100 getallen wordt ingelezen. Van de positieve getallen moet er afgedrukt worden hoeveel deelbaar waren door 2, hoeveel deelbaar waren door 3 en hoeveel door 6.
  12. Druk de som af van de eerste 30 termen van de volgende reeksen:
  • 6 + 12 + 18 + 24 + 30 + ...
  • 4 + 12 + 20 + 28 + 36 + ...
  • 1 + 2 + 4 + 8 + 16 + ...
  • 1 + 1/2 + 1/4 + 1/8 + 1/16 + ...
  • 1 + 1/3 + 1/5 + 1/7 + 1/9 + ...
  • 1/2 + 1/3 + 1/5 + 1/9 + 1/17 + ...
  1. Druk de som af van de eerste 20 termen van de volgende reeksen:
  • 4 + 8 + 12 + 16 + 20 + ...
  • 4 + 10 + 16 + 22 + 28 + ...
  • 1 + 3 + 9 + 27 + 81 + ...
  • 1/2 + 1/4 + 1/6 + 1/8 + 1/10 + ...
  • 1 + 1/2 + 1/4 + 1/8 + 1/16 + ...
  • 1 + 1/3 + 1/7 + 1/15 + 1/31 + ...

Tafels van vermenigvuldigen 1

Gebruik de kracht van loops om pijlsnel de tafels van vermenigvuldigen op het scherm te tonen van een getal naar keuze(dus bijvoorbeeld 2x1, 2x2, tot 2x10 en alles daartussen).

Tafels van supervermenigvuldigen (Essential)

Gebruik de kracht van geneste loops om pijlsnel alle tafels van vermenigvuldigen op het scherm te tonen van de getallen 1 tot en met n(dus 1x1, 1x2,... 1xn, 2x1, 2x2,...,2xn tot en met n x n).

RNA Transscriptie (Essential)

DNA heeft steeds een RNA-complement (DNA is het gevolg van RNA transscriptie). Schrijf een programma dat een ingevoerde DNA-string omzet naar een RNA-string. De gebruiker voert steeds 1 DNA-nucleotide in per keer en duwt op enter, de RNA string wordt steeds groter. Na 12 karakters stopt het programma.

De omzetting is als volgt:

  • G wordt C
  • C wordt G
  • T wordt A
  • A wordt U

Als de gebruiker dus ACGTGGTCTTAA heeft ingevoerd moet het resultaat: UGCACCAGAAUU zijn.

Ga er van uit dat de gebruiker letter per letter invoert (telkens dus enter na een letter) en je de omgezette string doet groeien (m.b.v. +=).

Armstrong nummer (PRO)

Een getal is een narcistisch getal of armstronggetal als het de som is van zijn eigen cijfers elk tot de macht verheven van het aantal cijfers.

  • 9 is een Armstrong nummer, want 9 = 9^1 = 9
  • 10 is geen Armstrong nummer, want 10 != 1^2 + 0^2 = 1
  • 153 is een Armstrong nummer, want: 153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153
  • 154 is geen Armstrong nummer, want: 154 != 1^3 + 5^3 + 4^3 = 1 + 125 + 64 = 190

Schrijf een programma dat aan de gebruiker een getal vraagt en vervolgens toont of het ingevoerde getal een Armstrong-nummer is of niet.

Je zou deze oefening kunnen oplossen door het ingevoerde getal als string op te splitsen in individuele char's. Maar we raden je aan om de "wiskunde" weg te volgen zodat je terdege leert met loops en wiskunde te werken.

Stel dat je het getal 4563 hebt:

  • Eerst deel je 4563 door 1000. Dit geeft 4.
  • We trekken 4x1000 van 4563 af. Dit geeft 563.
  • Deel 563 door 100. Dit geeft 5.
  • We trekken 5x100 van 563 af. Dit geeft 63.
  • Deel 63 door 10. Dit geeft 6.
  • We trekken 6 x 10 van 63 af. Dit geeft 3

Je kan van een string weten hoe groot deze is als volgt:

//veronderstellend dat myInputGetal van het type string is
int lengte= myInputGetal.Length;  

Je kan dan nu met Math.Pow(10,lengte-1) berekenen vanaf welke exponent van 10 we moeten beginnen werken.

Schaak-elo met loop

Zorg ervoor dat je Schaak-elo programma "blijft werken" als volgt:

1° De gebruiker geeft z'n begin Elo-rating op 2° Een loop start en vraagt nu telkens de Elo-rating van de huidige tegenstander, gevolgd door de uitslag. Telkens wordt de nieuwe Elo-rating van de gebruiker getoond. Wanneer de gebruiker een negatieve rating voor z'n volgende tegenstander opgeeft stopt de loop.

Euler project (Essential)

Maak volgende opdracht van projecteuler.net: >Indien we alle natuurlijke getallen van 0 tot en met 10 oplijsten die een meervoud van 3 of 5 zijn, dan krijgen we de getallen 3,5,6,9 en 10. De som van deze 4 getallen is 33. Maak nu een programma dat de som van alle veelvouden van 3 of 5 weergeeft van 0 tot en met 1000 (dit zou 234168 moeten geven).

De modulo-operator (%) is je grote held hier. Een getal is een veelvoud van x indien getal % x 0 als resultaat geeft.

For doordenker (PRO)

Schrijf een programma dat de volgende output geeft, gegeven dat de gebruiker een maximum waarde invoert, dus als hij 4 ingeeft dan zal de driehoek maximum 4 breed worden. Gebruik enkel 2 keer 2 geneste for-loops!

*
**
***
****
***
**
*

Een extra grote hoop oefeningen om je loops te drillen (originele bron). De oefeningen zijn gerangschikt naar moeilijkheid, je moet deze allemaal met loops oplossen! Hoe ver geraak je?

Probeer niet alle oefeningen met hetzelfde type loop te doen, wissel tussen while, do-while en for.

Indien er sprake is van n in de opgave dan is dit een getal dat je eerst aan de gebruiker moet vragen.

Opgelet: de oplossing van dit soort oefeningen vind je overal. Weersta hier aan, en probeer ZELF de oplossing te vinden. Dat is de enige manier om dit te leren.

Niet zeker van je oplossing? Test of je met minimale aanpassing de grenzen van je programma kunt aanpassen. Als je bijvoorbeeld de getallen van 1 tot 100 moet tonen dan zou je met 2 wijzigingen maximum dit moeten kunnen aanpassen naar de getallen van -1000 tot 21234.

Drillen

Beide grenzen zijn steeds inbegrepen!

  • Schrijf een programma dat alle Unicode karakters en hun waarde toont van 10 tot n (tip: char c = Convert.ToChar(65); zal hoofdletter A tonen). Merk op dat sommige unicode karakters (zeker die vooraan) "onzichtbaar" zijn en dus niets op het scherm zullen geven.
  • Toon het alfabet van a tot z.
  • Schrijf een programma dat de macht van een getal toont. De gebruiker voert eerst het getal in, gevolgd door de macht (bv. 2 en 4 zal als resultaat 16 geven (2 tot de 4e macht)). Merk op dat je geen gebruik mag maken van Math.Pow. Je dient zelf de vermenigvuldiging helemaal (m.b.v. loops) uit te voeren.
  • Schrijf een programma een getal n ontbindt in factoren. Factoren zijn de getallen waardoor je n kan delen zonder rest (van bijvoorbeeld het getal 100 zijn de factoren 1, 2, 4, 5, 10, 20, 25, 50 en 100).
  • Schrijf een programma dat controleert of een getal priem is of niet.
  • Toon alle priemgetallen van 1 tot n.
  • Toon de reeks van Fibonacci tot n termen.
  • Schrijf een programma dat het aantal digits in een getal telt (het getal 12348 heeft bijvoorbeeld 5 digits).
  • (PRO) Schrijf een programma dat een ingevoerd getal als tekst uitschrijft. Als de gebruiker dus 123 invoert zal de uitvoer zijn: honderd drie en twintig.

Cooldown

  • Toon alle getallen die een veelvoud van 3 zijn en oneven zijn tot en met 100.

  • Toon alle machten tot 5 van n.

  • Toon de tafels tot 10 van ieder getal van 1 tot en met n. Toon iedere tafel horizontaal! Als de gebruiker 8 invoert verschijnt er:

    1x1=1,2x1=2,3x1=3,4x1=4,5x1=5,6x1=6,7x1=7,8x1=8, 
    ... 
    1x10=10,2x10=20,3x10=30,4x10=40,5x10=50,6x10=60,7x10=70,8x10=80, 
    
  • Schrijf een programma om de eerste n termen van een harmonische reeks te tonen en bereken vervolgens de som van deze termen. Als de gebruiker bijvoorbeeld 5 invoert verschijnt er (de laatste plus mag je tonen om geen onnodige ingewikkelde code te moeten schrijven):

    1/1 + 1/2 + 1/3 + 1/4 + 1/5 + 
    Som = 2.283334 
    
  • Schrijf een programma dat de som van de serie 9+99+999+9999+99999+999999 berekent (mét loop uiteraard).

  • Vraag aan de gebruiker getallen tot hij -1 invoert. Toon het gemiddelde van de ingevoerde getallen.

Begin pas aan deze oefeningen wanneer je voldoende geoefend hebt door de vorige 2 delen oefeningen op te lossen.

Deze oefeningen zijn oefeningen die je moet kunnen indien je hoofdstuk 6 goed en wel beheerst. Ze zijn dus een goede maat om te weten of je het hoofdstuk al voldoende in de vingers hebt.

Bekijk zeker de Wiskunde-quizprogramma-opdracht!

Boekhouder (Essential)

Maak een 'boekhoud-programma': de gebruiker kan continu positieve en negatieve getallen invoeren. Telkens hij op enter duwt wordt de huidige invoer aan de balans bijgevoegd. Het programma stopt met getallen vragen wanneer de gebruiker 'q' invoert. Finaal worden dan de volgende zaken op het scherm getoond:

  • De balans van alle ingevoerde getallen: dit is gewoon de som van de getallen. Als de gebruiker dus de getallen 4, -10 en 8 invoerde dan zal de balans op +2 staan (4 -10 + 8).
  • De som van alle negatieve invoeren. Als de gebruiker dus 4, -10, 8 en -6 invoerde dan zal dit getal op -16 staan (= -10 -6).
  • De som van alle positieve invoeren. Als de gebruiker dus 4, -10, 8 en -6 invoerde dan zal dit getal op +12 staan (= 4 + 8).
  • Het gemiddelde van alle ingevoerde getallen.

Deze 4 getallen worden ook steeds geüpdatet en getoond wanneer de gebruiker een nieuw getal invoert en op enter duwt.

Hoger Lager (Essential)

Simulatie van het "hoger-lager" spel. Het programma kiest een random-getal van 1 tot en met 100 (telkens inbegrepen). Vervolgens wordt de gebruiker gevraagd om een gok te doen en toont het programma of de gok juist was, te laag was ("hoger") of te hoog ("lager"). Het programma blijft gokken van de gebruiker accepteren tot de gok juist is of de gebruiker besluit te stoppen (door een negatief getal in te voeren). Het aantal beurten wordt op het einde van het spel getoond en de mogelijkheid om opnieuw te spelen.

Limiet

Pas het Hoger Lager programma aan zodat er een maximum aantal pogingen is toegestaan.

Wiskundequiz (Essential)

Maak een applicatie die je kan gebruiken om je tafels van vermenigvuldigen te oefenen. De applicatie vraagt steeds een willekeurige vermenigvuldiging (enkel getallen tussen 1 tot en met 10) en de gebruiker moet de oplossing invoeren. Indien correct gaat de gebruiker verder. Bij fout stopt het programma en wordt getoond hoeveel keer je juist hebt ingevoerd.

Wiskundequiz met levels (Essential)

Bouw levels in de voorgaande wiskundequiz. Per 5 juiste antwoorden, stijg je 1 level. Het level bepaalt het bereik van getallen die gegenereerd worden bij de oefening. Bijvoorbeeld level 1 enkel getallen van 1 tot en met 5, level 2 tot en met 10, level 3 tot en met 20 etc.

PRO: Kan je ervoor zorgen dat het bereik van de getalgeneratie met een formule afhankelijk is van het level? Zodat je de grenzen per level niet moet hardcoden?

Wiskunde-quizprogramma

Integreer voorgaande quiz met een menu dat je bijvoorbeeld in codemenu hebt gemaakt. Het menu wordt aan de start getoond en geeft de gebruiker de optie om te kiezen wat hij wenst te doen:

  • Gewoon spelen
  • Starten op een bepaald level (de gebruiker moet vervolgens het level invoeren)
  • Studeren: de oplossing wordt steeds getoond. De gebruiker hoeft niets in te voeren, elke 5 seconden verschijnt de volgende opgave met oplossing. Gebruik System.Threading.Thread.Sleep(5000) om je programma 5 seconden (5000 ms) te pauzeren.

Tekenen

Twee getallen van 2 tot en met 20 worden ingelezen (invoercontrole!). Er moet een open rechthoek afgedrukt worden bestaande uit *en waarbij de ingelezen getallen respectievelijk de breedte en de hoogte van de rechthoek voorstellen. Als bijvoorbeeld 10 en 4 werden ingelezen, wordt de volgende rechthoek getoond:

* * * * * * * * * *
*                 *
*                 *
* * * * * * * * * *

Steen schaar papier (Essential)

Maak een applicatie waarbij de gebruiker steen-schaar-papier met de computer kan spelen. De gebruiker kiest telkens steen, schaar of papier en drukt op enter. Vervolgens kiest de computer willekeurig steen, schaar of papier (gebruik de Random.Next() methode, waarbij je deze van 1 tot en met 3 laat varieren). Vervolgens krijgt de winnaar 1 punt:

  • Steen wint van schaar, verliest van papier.
  • Papier wint van steen, verliest van schaar.
  • Schaar wint van papier, verliest van steen.
  • Indien beide hetzelfde hebben wint niemand een punt.

Op het scherm wordt telkens getoond wie de huidige ronde heeft gewonnen en hoeveel de tussenscore is. De eerste (pc of gebruiker) die 10 punten haalt wint.

Teken een flowchart van je applicatie.

Los dit op met enum : je code zal een pak leesbaarder worden

Codemenu (Essential)

Maak een applicatie die bij het opstarten een keuze menu toont. Het menu toont 5 verschillende oefeningen naar keuze. Telkens de gebruiker in het menu een oefening kiest (door a, b, c, d of e in te voeren) wordt de code van die oefening getoond. Vervolgens kan de gebruiker op enter duwen zodat terug het menu verschijnt, zodat de gebruiker een nieuwe oefening kan kiezen

Extra: maak je menu visueel interessanter (m.b.v. kaders en kleuren)

Become Neo

Neo Tim

Volgende code genereert een beeld zoals dat ook in de cultfilm The Matrix (1999) plaatsvindt.

Random rangen = new Random();
Console.ForegroundColor = ConsoleColor.Green;
while (true)
{
    //Genereer nieuw random teken:
    char teken = Convert.ToChar(rangen.Next(62, 400));
    //Zet teken op scherm
    Console.Write(teken);
    
    //10 ms pauze tussen ieder frame (pas gerust aan)
    System.Threading.Thread.Sleep(10); 
    
    //Af en toe donker kleurtje
    if(rangen.Next(0, 3) == 0)
    {
        Console.ForegroundColor = ConsoleColor.DarkGreen;
    }
    else
    {
        Console.ForegroundColor = ConsoleColor.Green;
    }
}

Vul de code aan zodat de karakters random kleuren krijgen. Kan je het nog cooler maken?

BeerSong

Schrijf een BeerSong-generator zoals onderstaande output. Merk op dat de laatste 5 zinnen anders zijn:

99 bottles of beer on the wall, 99 bottles of beer.
Take one down and pass it around, 98 bottles of beer on the wall.

98 bottles of beer on the wall, 98 bottles of beer.
Take one down and pass it around, 97 bottles of beer on the wall.

97 bottles of beer on the wall, 97 bottles of beer.
Take one down and pass it around, 96 bottles of beer on the wall.

96 bottles of beer on the wall, 96 bottles of beer.
Take one down and pass it around, 95 bottles of beer on the wall.

95 bottles of beer on the wall, 95 bottles of beer.
Take one down and pass it around, 94 bottles of beer on the wall.

...

4 bottles of beer on the wall, 4 bottles of beer.
Take one down and pass it around, 3 bottles of beer on the wall.

3 bottles of beer on the wall, 3 bottles of beer.
Take one down and pass it around, 2 bottles of beer on the wall.

2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottle of beer on the wall.

1 bottle of beer on the wall, 1 bottle of beer.
Take it down and pass it around, no more bottles of beer on the wall.

No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.

Ook dit hoofdstuk bevat meer oefeningen dan je lief zijn. Zoek zelf de oefeningen uit die je aanspreken en focus je op het leren werken met methoden en deze te integreren in een grotere applicatie.

Tip van een lector: meestal als een boek veel oefeningen van een bepaald onderwerp bevat is de kans bestaande dat dit onderwerp misschien wel eens belangrijk kan zijn ;)

Opgelet: wanneer je een oefening maakt die invoer van de gebruiker vereist, gelieve die invoer dan BUITEN de methode te vragen (dus meestal in de Main methode). Op die manier zorg je er voor dat je je methoden kunt herbruiken.

Sommige oefeningen zijn van de vorm "Maak een methode die...". Het is steeds de bedoeling dat je de werking van je methode ook test in je main door deze aan te roepen.

Opwarmers (Essential)

Een extra grote hoop oefeningen om je methoden te drillen (originele bron). De oefeningen zijn ongeveer gerangschikt naar moeilijkheid.

OPGELET: de naam van de methode geeft meestal aan wat er juist moet gebeuren. ReadLine en WriteLine mag je bijna NOOIT in methoden gebruiken. Enkel als de methode bijvoorbeeld ToonGetal heet dan zal je iets met WriteLine IN de methode moeten doen. Heet de methode VraagGetal dan zal je inderdaad ReadLine mogen gebruiken. In bijna alle andere gevallen mag dat niet en is het dus de bedoeling dat je de vereiste informatie als parameters aan de methode meegeeft (i.p.v ReadLine), en het resultaat als return teruggeeft (i.p.v. WriteLine).

Iedere oefening verwacht dat je steeds een methode schrijft en de werking ervan aantoont:

  • Methode Kwadraat die het kwadraat van een ingevoerd getal berekent ( het getal geef je mee als parameter).
  • Methode BerekenStraal die de straal van een cirkel kan berekenen waarvan je de diameter meegeeft (de diameter geef je mee als parameter).
  • Idem voor omtrek en oppervlakte.
  • Methode die het grootste van 2 getallen teruggeeft (beide getallen geef je mee als parameter).
  • Methode IsEven die bepaalt of een getal even of oneven is (geeft een bool terug die true is indien even).
  • Methode ToonOnEvenNummers die alle oneven getallen van 1 tot n toont waarbij n als parameter wordt meegegeven.
  • (PRO) Methode IsArmstrong die bepaalt of een getal een Armstrong getal is.
  • (PRO)Methode ToonArmstrongNummers die alle Armstrong nummers tot n toont.

Basic

Maak een methode die jezelf voorstelt op het scherm in de vorm van "Ik ben Tim Dams, ik ben 18 jaar oud en woon in de Lambrisseringsstraat 666". Deze informatie mag hardcoded in je methode staan. Bedoeling is dat je de methode kan aanroepen als volgt:

MyIntro();

Deze methode toont enkel zaken op het scherm en heeft dus als returntype void.

Basic 2

Zorg er voor dat je nu je naam, leeftijd en adres via parameters kan meegeven en dus de methode als volgt kunt aanhouden:

MyIntro("Jos", 34, "Trammezantlei 21");

Grootste methode

Schrijf een methode die 3 ints aanvaardt en vervolgens de grootste van de 3 parameters als resultaat terug geeft.

Rekenmachine (Essential)

Maak minimaal de methoden genaamd TelOp, TrekAf, Vermenigvuldig en Deel. Je kan aan deze methoden steeds twee doubles meegeven en het resultaat dat terugkomt is uiteraard de bewerking van die twee parameters.

Maak een eenvoudig programma waarin je die methoden test.

Pro Rekenmachine

Kan je een eenvoudige zakrekenmachine maken (inclusief geheugen)? Voeg extra methoden naar keuze toe (denk aan macht, sinus, cosinus, modulo, etc.).

Paswoord generator methode

Maak een paswoord generator die paswoorden van bepaalde lengte genereert en bestaat uit willekeurige letters, hoofdletters en cijfers. Plaats deze code in een methode die 1 parameter aanvaardt namelijk de lengte van het paswoord dat gemaakt moet worden. De methode geeft het gegenereerde paswoord terug als resultaat. (tip gebruik een random number generator(s) om getallen te genereren die je ook kan casten naar chars dankzij de Unicode waarde van chars).

Film Default (Essential)

Maak een methode FilmRuntime() die 3 parameters accepteert:

  1. Een string die de naam van de film bevat
  2. Een integer die duur in minuten van de film bevat
  3. Een enum-type die het genre van de film bevat (Drama, Actie, etc.)

Indien de duur van de film niet wordt meegeven wordt een standaard lengte van 90 minuten ingesteld. Indien het genre niet wordt meegeven dan wordt deze default op Onbekend ingesteld.

De methode geeft niets terug maar toont eenvoudigweg de film op het scherm, gevolgd door z’n duur en genre in volgende formaat. Voorbeelduitvoer=

The Matrix (120 minuten, Actie)

Toon aan in je main dat de methode werkt met zowel 1, 2 als 3 parameters. Toon ook aan dat je met ‘named arguments’ de methode kan aanroepen.

Opwarmers met geavanceerde methoden (Essential)

Zorg ervoor dat de opwarmers vooraan deze reeks oefeningen steeds minstens 1 optionele parameter hebben. Roep deze methoden aan via named parameters.

Oude oefeningen leesbaarder maken

Kan je code uit vorige hoofdstukken herbruiken door deze in handige methoden te plaatsen zodat je code leesbaarder én bruikbaarder wordt?

Einde deel 1 test herschrijven

Bekijk terug jouw (of mijn) oplossing van de vaardigheidsproef of de deel 1 eindetest in dit boek.

Kan je deze code leesbaarder maken door methoden (en loops) toe te voegen?

Verhaalgenerator

Bekijk het all-one-project "De verhaal generator": kan jij dit project afwerken zoals onderaan de opgave wordt voorgesteld?

Hoe ver geraak je?

Challenges

Een aantal oefeningen is geïnspireerd op oefeningen van Exercism.io.

Opwarmers

  • Vul een array van ints met alle getallen van 1 tot 100. Druk de array af.
  • Vul een array van ints met alle even getallen tot en met 100. Druk de array af.
  • Vraag aan de gebruiker 3 keer een getal, stop deze in een array, druk deze array af.
  • Maak een array aan en plaats daarin de 4 namen van je beste vrienden in volgorde van "beste vriend" tot "minst beste vriend". Toon nu de namen op het scherm, onder elkaar, met telkens ervoor "Beste vriend", "Tweede beste vriend", "Derde beste vriend", "Minst beste vriend".
  • Maak een array van 20 booleans en zorg dat alle oneven indexen False en de even True zijn. Druk vervolgens de array af.
  • Maak een array van 20 bool-waarden. Deze waarden zijn willekeurig. Print de array. Toon hoeveel keer true en hoeveel keer false er in de array zit. Opgelet, je doet de visualisatie in aparte loop nadat je deze hebt aangemaakt. Voorts tel je de true en false variabelen in nog eens een aparte loop.
  • Vul een array met 10 random doubles tussen 0 en 10. Toon het gemiddelde ervan.
  • Maak een enum Schooltype met mogelijke waarden TSO, BSO, ASO, KSO. Maak een array van 20 Schooltype-waarden. Vul deze met willekeurige schooltypes. Toon de array. Toon hoe vaak ieder schooltype in de array voorkomt.

Vraag Array

Maak een array die 6 strings kan bevatten. Ieder element van de array bevat een vraag (naar keuze te verzinnen) als string waar de gebruiker met een getal op moet antwoorden. Maak een array aan die tot 6 ints kan bevatten. Lees 1 voor 1 de vraag uit de string-array uit en toon deze op het scherm. Lees vervolgens het antwoord uit dat de gebruiker intypt en bewaar dit als int in de 2e array.

Na de 6 vragen toon je vervolgens de vragen opnieuw met achter iedere vraag het antwoord van de gebruiker.

Mogelijk uitvoer programma

Array Zoeker (Essential)

Maak een programma dat eerst aan de gebruiker 10 waarden vraagt die in een array worden gezet.

Vervolgens vraagt het programma welke waarde verwijderd moet worden. Wanneer de gebruiker hierop antwoordt met een nieuwe waarde dan zal deze nieuw ingevoerde waarde in de array gezocht worden. Indien deze gevonden wordt dan wordt deze waarde uit de array verwijderd en worden alle waarden die erachter komen met een plaatsje naar links opgeschoven, zodat achteraan de array terug een lege plek komt.

Deze laatste plek krijgt de waarde -1.

Toon vervolgens alle waarden van de array.

Indien de te zoeken waarde meer dan 1 keer voorkomt, wordt enkel de eerst gevonden waarde verwijderd.

Voorbeeld uitvoer

LeveringsBedrijf (Essential)

Maak een programma voor een koeriersbedrijf. Maak een array die 10 postcodes bevat (zelf te kiezen) van gemeenten waar het bedrijf naar levert. Maak een tweede array die de prijs bevat per kg voor iedere respectievelijke gemeente. Het eerste element van deze array bevat dus de prijs/kg om naar de gemeente te leveren die als eerste in de array met postcodes staat.

Vraag aan de gebruiker een postcode en het gewicht van het pakket. Vervolgens wordt de prijs/kg opgezocht voor die gemeente en wordt de prijs berekend, gegeven het ingegeven gewicht.

Indien het bedrijf niet levert aan de ingetypte postcode dan wordt een foutmelding weergegeven.

Geef gewicht pakket
45
Naar welke postcode wenst u dit pakket te versturen?
2020
Dit zal 9630 euro kosten.

Bob

Kan je "Bob" programmeren die als volgt werkt:

Bob is a lackadaisical teenager. In conversation, his responses are very limited. Bob answers 'Sure.' if you ask him a question. He answers 'Whoa, chill out!' if you yell at him. He answers 'Calm down, I know what I'm doing!' if you yell a question at him. He says 'Fine. Be that way!' if you address him without actually saying anything. He answers 'Whatever.' to anything else."

Bekijk steeds de laatste 2 tekens die de gebruiker invoert om de response van Bob te bepalen.

Kan je een gesofisticeerdere bot maken?

myInputstring.Contains(somestring) geeft een bool terug indien somestring voorkomt in de variabele myInputstring van het type string.

Gebruik een array om de antwoorden in te bewaren.

PRO: Bob++

Kan je met een array eerdere vragen van de gebruiker bewaren om zo complexere antwoorden te genereren?

Hamming distance (Essential)

De hamming distance is het aantal tekens dat twee reeksen verschillen indien we ieder element vergelijken op dezelfde plaats in de andere reeks.

Maak een programma dat aan de gebruiker vraagt om twee DNA strings in te voeren (een reeks bestaande uit de letters G, A, C & T). Beide reeksen moeten even lang zijn.

Bereken de hamming distance tussen beide reeksen.

De hamming distance van volgende twee DNA strings is 7, omdat er 7 elementen in beide strings staan die niet gelijk zijn aan mekaar op dezelfde plek (aangeduid met ^).

GAGCCTACTAACGGGAT
CATCGTAATGACGGCCT
^ ^ ^  ^ ^    ^^

Password generator (PRO)

Ontwerp een consoletoepassing waarmee je een wachtwoord genereert voor een gebruiker. Het wachtwoord is opgebouwd uit:

de 2 eerste letters van de familienaam: de 1ste letter is een hoofdletter, de 2de letter is een kleine letter. Daarna worden de 2 letters gewisseld; het zonenummer van het telefoonnummer zonder de 0; het eerste cijfer van de postcode in het kwadraat.

Gebruik de ToCharArray() methode die je op een string kunt toepassen, zoals hier getoond: char[] chars = myEpicName.ToCharArray();

Schrijf nooit een paswoord manager die je paswoorden gortdroog als string bewaart. Secure code schrijven is een hele discipline op zich en laat je best nog even links liggen tot je C# goed in de vingers hebt.

Puzzelen met arrays (Essential)

Sommige oefeningen zijn op randje van PRO. U weze gewaarschuwd.

Je mag in deze oefeningen NIET gebruik maken van .Reverse(), .BinarySearch() etc. Alles moet je manueel kunnen. Enkel sorteren mag je automatisch doen met .Sort().

Als er dus staat "lees 20 getallen in", dan moet je die in volgorde bewaren in je array. Vervolgens begin je die array te manipuleren.

  1. Er worden 20 getallen ingelezen. De getallen worden in omgekeerde volgorde afgedrukt.
  2. Er worden 20 getallen ingelezen. De getallen worden 1 plaats naar voor verschoven afgedrukt, d.w.z. eerst het tweede ingelezen getal, dan het derde ingelezen getal, dan het vierde ingelezen getal , ... , dan het laatste ingelezen getal en tenslotte het eerste ingelezen getal.
  3. Er worden 20 getallen ingelezen. De getallen worden 3 plaatsen naar achter verschoven afgedrukt, d.w.z. eerst het derde laatste ingelezen getal, dan het voorlaatste ingelezen getal, dan het laatste ingelezen getal, dan het eerste ingelezen getal, dan het tweede ingelezen getal en tenslotte het vierde laatste ingelezen getal.
  4. Er worden 20 getallen ingelezen. De getallen worden geroteerd (verschoven) afgedrukt over een aantal elementen x zoals in de vorige opgave. Maak dus de vorige oefening maar nu is 3 een variabele die je op voorhand kan instellen.
  5. [Pro] Een rij van 100 getallen wordt ingelezen. Druk de inhoud van de rij af in 3 kolommen. De getallen worden gerangschikt rij per rij. Als je input bijvoorbeeld volgende reeks getallen is: 4 5 8 7 5 2 6 8 7 1 3 2, dan wordt dit je output:
4 5 8
2 5 7
6 7 8
1 2 3
  1. [Pro] Een rij van 100 getallen wordt ingelezen. Druk de inhoud van de rij af in 3 kolommen. De getallen worden gerangschikt kolom per kolom (hier mag je Sort gebruiken).

Volgende oefeningen vereisen dat je methoden schrijft die arrays als parameter en/of resultaat hebben.

Parkeergarage (Essential)

Een parkeergarage vraagt sowieso €2.00 om tot maximum 3uur te parkeren. Per extra uur NA die 3uur wordt telkens €0.50 aangerekend (dus 4uur parkeren kost €2.50. Er wordt maximum €10 aangerekend per dag. Veronderstel dat er nooit langer dan 1 dag (24u) kan geparkeerd worden.

Schrijf een programma dat het verschuldigde bedrag toont gegeven de duur dat de parkeergarage gebruikt werd. Bij het opstarten van het programma wordt eerst gevraagd hoeveel auto’s ingevoerd zullen worden, dan wordt per auto de duur van het parkeren gevraagd. Finaal wordt, met tabs, alle informatie getoond, inclusief het totaal bedrag. Gebruik minstens 1 methode BerekenKosten die de kost voor 1 gebruiker telkens teruggeeft, gegeven de duur als parameter.

Voorbeeldoutput:

Opstart

Resultaat

Array Viewer (Essential)

Maak een programma dat een methode VisualiseerArray implementeert. De methode zal een array (type int) als parameter hebben en niets teruggeven (void). Echter, de methode zal met behulp van Write() de array, van eender welke grootte, op het scherm tonen. Tussen ieder element van dezelfde rij dient een tab (herinner: "\t") gezet te worden. Je dient in de methode gebruik te maken van een for-loop. Voorbeeld van main:

int[] array={15, 6, 9};
int[] array2={0, 1, 2, 3, 4, 5, 6};
VisualiseerArray(array);
VisualiseerArray(array2);

Geeft volgende output:

Uitvoer

Caesar-encryptie

Maak 2 methoden Encrypt en Decrypt die als parameters telkens een chararray krijgen en een integer. Bedoeling is dat de Encrypt-methode de array van het type chararray versleuteld gegeven de sleutel x volgens het Caesar cipher (zie hieronder). Als resultaat komt er uit de methode de geëncrypteerde array.

De decrypt-methode doet hetzelfde maar omgekeerd: je kan er een versleutelde tekst insteken en de sleutel en de ontcijferde tekst komt terug (merk op dat je decrypt-methode gebruik kan maken van de encrypt-methode!).

Toon in je main aan dat je methoden werken (door bijvoorbeeld aan de gebruiker een stuk tekst als string te vragen en een sleutel en deze dan te encrypteren/de-crypteren).

Encryptie is de kunst van het vercijferen van data. Hierbij gaat men een gewone tekst zodanig omvormen (versleutelen) zodat deze onleesbaar is en enkel kan ontcijferd worden door de ontvanger die weet hoe de tekst terug kan verkregen worden en enkel indien deze ook de ‘private’ sleutel heeft.

Een klassiek encryptie-algoritme uit de oudheid is de Caesar substitutie. Hierbij gaan we het alfabet met x plaatsen opschuiven en vervolgens de te vercijferen tekst letter per letter vervangen met z’n respectievelijke opgeschoven versie. Hierbij is x dus de geheime sleutel die zender en ontvanger moeten afspreken.

Caesar-encryptie is een erg eenvoudig concept. Het programmeren is iets pittiger.

Stel bijvoorbeeld dat x=3 dan krijgen we volgende nieuwe alfabet:

DEFGHIJKLMNOPQRSTUVWXYZABC

Waarbij dus de A zal vervangen worden door een D, de Z door een C, etc.

Willen we deze tekst dus encrypteren:

the quick brown fox jumps over the lazy dog

dan krijgen we:

WKH TXLFN EURZQ IRA MXPSV RYHU WKH ODCB GRJ

Ondernemingsnummer (Essential)

Originele bron voor dit idee

Ontwerp een methode waarmee je een Belgisch ondernemingsnummer kan controleren. Een Belgisch ondernemingsnummer is als volgt opgebouwd: BE 0xxx.xxx.xxx waarbij BE de landcode is die standaard gevolgd wordt door een spatie en dan een 0. Wanneer je de volgende 7 cijfers deelt door 97 in een gehele deling, dan is het getal gevormd door de laatste 2 cijfers gelijk aan 97 minus de rest van deze gehele deling. Bij een geldig ondernemingsnummer verschijnt de tekst "Geldig ondernemingsnummer.", bij een ongeldig ondernemingsnummer "Ongeldig ondernemingsnummer.". De methode aanvaardt een string waarin je het ondernemingsnummer staat.

Puzzelen met array deel 2 (pittig)

  1. Een rij van 100 gehele getallen wordt ingelezen. Maak een tweede rij die bestaat uit de elementen van de eerste rij in omgekeerde volgorde.
  2. Een rij van 100 gehele getallen wordt ingelezen. Wissel de inhoud van de rij om.
  3. Een rij van 100 gehele getallen wordt ingelezen. Verschuif de inhoud van de rij 1 plaats naar achter. Het laatste getal moet eerst komen.
  4. Een rij van 100 gehele getallen wordt ingelezen. Maak een tweede rij die bestaat uit de elementen van de eerste rij maar 1 plaats naar achter verschoven . Het laatste getal van de eerste rij moet eerst komen in de tweede rij.
  5. Een rij van 100 gehele getallen wordt ingelezen. Maak een tweede rij die bestaat uit de elementen van de eerste rij maar 3 plaatsen naar voor verschoven. Het eerste getal van de eerste rij moet derde laatst komen in de tweede rij, het tweede getal van de eerste rij moet voorlaatst komen in de tweede rij en het derde getal van de eerste rij moet laatst komen in de tweede rij.
  6. Een rij van 100 gehele getallen wordt ingelezen. Verschuif de inhoud van de rij 3 plaatsen naar voor. Het eerste getal moet derde laatst komen, het tweede, voorlaatst en het derde, laatst.
  7. Gegeven is een rij van 50 getallen. Maak een tweede rij waarin geen dubbels voorkomen.
  8. Gegeven is een gesorteerde rij van 50 getallen. Maak een tweede (eveneens gesorteerde) rij waarin geen dubbels voorkomen.
  9. Een rij van maximaal 100 gehele getallen met afsluitwaarde 32767 wordt ingelezen. Druk het maximum van de rij af, het aantal keer dat het voorkwam en de plaats waar het de eerste keer voorkwam.
  10. Een rij van maximaal 100 gehele getallen met afsluitwaarde 32767 wordt ingelezen. Druk het minimum van de rij af, het aantal keer dat het voorkwam en de plaats waar het de laatste keer voorkwam.
  11. Een rij gehele getallen met afsluitwaarde 32767 wordt ingelezen. Het aantal ingelezen getallen is onbeperkt, maar er zullen maximaal 100 verschillende getallen worden ingelezen. Druk af hoeveel verschillende getallen ingelezen werden.
  12. [PRO] Druk alle priemgetallen kleiner dan 100.000 af. Maak gebruik van het algoritme van Eratosthenes.

Determinant (Essential)

Er volgen nu enkele meer-dimensionale array oefeningen

Schrijf een programma dat een methode BerekenDeterminant heeft. Deze methode heeft één parameter als input: een 2 bij 2 array van integers. Als resultaat geeft de methode de determinant als integer terug. Zoek zelf op hoe je de determinant van een matrix kunt berekenen.

Volgende voorbeeld-main dient te werken,

int[,] aMatrix = { 
                  {2, 4},
                  {3, 5}
                 };
Console.WriteLine($"Determinant van matrix is {BerekenDeterminant(aMatrix)}");

geeft als output:

Determinant van matrix is -2

Extra: Breid uit zodat de BerekenDeterminant-methode ook werkt voor 3-bij-3 matrices. De methodeaanroep blijft dezelfde, enkel de interne code van de methode zal nu rekening moeten houden met de grootte van de matrix .

2D Array Viewer

Breid het ArrayViewer programma uit zodat ook 2-dimensionale arrays gevisualiseerd kunnen worden. (Hint: gebruik de GetLength()-methode van een array).

Voorbeeld van main:

int [,] array = { {15, 6, 9}, {1, 2, 3}, {6, 9, 12}};
VisualiseerArray(array);
15  6  9
1   2  3
6   9  12

MatrixMultiplier

Schrijf een methode VermenigvuldigMatrix die 2 matrices als invoer verwacht en als resultaat een nieuwe matrix teruggeeft die het product van beide matrices bevat.

Voetbalcoach (Essential)

Maak een console-applicatie voor een assistent voetbaltrainer (of een sport naar keuze).

De voetbalcoach wil na de match iedere knappe en domme actie van een speler weten. Op die manier weet hij aan het einde van de match wie er de meeste goede en slechte acties doet. De spelers hebben rugnummers 1 tot en met 12. (het gaat om een voetbal variant waar m'n geen elftal maar een twaalftal gebruikt :p)

Wanneer de coach een rugnummer intypt kan hij vervolgens ingeven of hij (a) een knappe actie of (b) een domme actie wil ingeven. Vervolgens geeft hij een getal in. Gebruik een 2dimensionale array die per speler het aantal domme en goede acties bijhoudt (de array is dus 12 bij 2 groot: 1 lijn per speler, bestaande uit 2 kolommen voor goede en domme actie. De index van de lijn is de rugnummer van speler -1).

Een typische invoer kan dus zijn:

2
a
6

De coach kiest dus de speler met rugnummer 2, hij kiest voor een knappe actie, en voert 6 in als aantal goede acties.

In de array op index 1 (rugnummer -1) zal in de 0'de kolom (0 = goede, 1 = slechte) het getal 6 geplaatst worden.

Vervolgens kan de coach een ander rugnummer (of hetzelfde) invoeren en zo verder.

Wanneer de coach 99 invoert stopt het programma en worden de finale statistieken getoond: per speler/rugnummer wordt het aantal goede en domme acties getoond, met daarnaast het verschil tussen beide:

(gebruik \t om goede tabs te zetten tussen de data)

Rugnummer   Goede   Domme   Verschil
1               5       2        3
2               6       7       -1

Het programma toont ook welke speler(s) het meest performant waren, namelijk zij met het grootste positieve verschil tussen goede en domme acties, alsook de minst performante en de meeste “gemiddelde” speler (i.e. verschil == 0 )

Robot Simulator (PRO)

Volgende opgave komt uit Exercism.io

" Write a robot simulator.

A robot factories' test facility needs a program to verify robot movements. The robots have three possible movements:

  • turn right
  • turn left
  • advance Robots are placed on a hypothetical infinite grid, facing a particular direction (north, east, south, or west) at a set of (x, y) coordinates, e.g., (3, 8), with coordinates increasing to the north and east.

The robot then receives a number of instructions, at which point the testing facility verifies the robot's new position, and in which direction it is pointing.

The letter-string "RAALAL" means:

  • Turn right
  • Advance twice
  • Turn left
  • Advance once
  • Turn left yet again

Say a robot starts at (7, 3) facing north. Then running this stream of instructions should leave it at (9, 4) facing west."

Teken het gevolgde pad in de console (gebruik Console.SetCursorPosition()).

Galgje (PRO)

Maak een spel, vergelijkbaar met galgje, waarin de speler een woord moet raden. Zie Wiki voor de spelregels indien je deze niet kent.

Voorbeeld output:

Uitvoer

Grote Som (PRO)

Maak een methode Som() die eender welke hoeveelheid parameters van het type int aanvaardt en vervolgens de som van al deze parameters teruggeeft (als int).

Toon in je main aan dat de methode werkt door onder andere 1, 3, 5 en 10 als gehele getalen mee te geven.

Toon ook aan dat je een array van 50 ints als parameter kan meegeven aan de methode.

Je moet het params keyword gebruiken, zoek zelf op hoe dit werkt!

Volgende opgave was de vaardigheidsproefopdracht voor examen van dit vak (Programming Principles) in januari 2022

Discovid safe tracker

De discotheek "Damsing Van Camp" heeft jouw diensten ingehuurd om een Covidsafe tracker te ontwikkelen. Ze willen op deze manier potentiële uitbraken in de wachtrij detecteren voor de contactracers.

Werking

De tool wordt gebruikt door het personeel van het onthaal aan de kassa die het volgende zal doen:

  • Ze zal met een andere app het covid safe ticket scannen.
  • Fase 1: Vervolgens start ze jouw tool en voert in:
    • De naam van de gescande persoon.
    • Of deze persoon wel of niet werd toegelaten.
  • Fase 2: De volgende dag kan de manager van de discotheek (de wereldberoemde Vinny Timzo) de resultaten van de app raadplegen en de nodige statistieken genereren die hij dan aan de contacttracers kan doorgeven.
  • Fase 3: Beslissen of het programma nog eens moet uitgevoerd worden of niet.

Fase 1: data verzamelen

2 arrays worden aangemaakt van lengte 100. Vul de namenarray met de string leeg in ieder element, zo kan je verderop makkelijker tellen.

  1. Het programma start met een loop die zich blijft herhalen tot de gebruiker "stop" invoert als naam. In de loop gebeurt het volgende:
    1. Een zinnetje toont Voer de naam in van persoon x waarbij de x vervangen door een getal dat aangeeft de hoeveelste persoon nu komt (begint bij 1 en wordt telkens met 1 verhoogd).
    2. De gebruiker voert de naam in (indien hier stop wordt ingevoerd stopt de verdere werking van de loop).
    3. Deze naam wordt bewaard in een array (type string).
    4. Een zinnetje toont Werd deze persoon toegelaten (j/n)?.
    5. De gebruiker moet j of n (ja of nee) invoeren. Enkel dan wordt er naar de volgende stap gegaan, anders wordt naar stap 4 gegaan.
    6. Het antwoord (j of n) wordt in een array van het type bool bewaard.
    7. Een zinnetje toont De persoon met naam X werd ingevoerd. Hij werd Y. waarbij X de ingevoerde naam is en Y ofwel toegelaten of niet toegelaten afhankelijk van de invoer bij stap 5.
  2. Na de loop worden de statistieken getoond zoals uitgelegd in de volgende sectie (Fase 2)

Voorbeelduitvoer fase 1a (de namen en j of n werden door gebruiker ingevoerd):

Voer de naam in van persoon 1
tim
Werd deze persoon toegelaten (j/n)?
n
De persoon met naam tim werd ingevoerd. Hij werd niet toegelaten.
Voer de naam in van persoon 2
jos
Werd deze persoon toegelaten (j/n)?
j
De persoon met naam jos werd ingevoerd. Hij werd toegelaten.
Voer de naam in van persoon 3
anna
Werd deze persoon toegelaten (j/n)?
Wat?
Werd deze persoon toegelaten (j/n)?
j
De persoon met naam anna werd ingevoerd. Hij werd toegelaten.
Voer de naam in van persoon 4
stop

Fase 2a: data verwerken

Er wordt een methode ToonTracerRapport aangeroepen. Aan deze methode geef je de 2 arrays mee.

Gebaseerd op het percentage niet toegelaten personen wordt een risico berekend: onder de 35% is Laag, tussen de 35% en 75% Verhoogd en boven 75% Kritiek. Het risico is gebaseerd op de verhouding tussen het aantal ingevoerde personen en degene die daarvan niet zijn toegelaten. Als er dus 10 personen in totaal werden ingevoerd en daarvan waren er 4 niet toegelaten, dan is het risico 40% (4 van de 10).

Deze grenswaarden kunnen na initialisatie niet meer aangepast worden.

Gebruik een enum type om het risico te bewaren.

Deze methode zal vervolgens een samenvatting van de ingevoerde data uitvoeren, als volgt (fictieve data):

Aantal ingevoerde personen = 24
Daarvan werden 3 personen niet toegelaten, dat is 12,5%.
Risico: laag

Volgende personen werden niet toegelaten:
- Franky Vermeulen
- Raphael Palinsky
- Freya Versavel

Fase 2b: data anonimiseren

Vervolgens vraagt het programma aan de gebruiker of de gebruikersnamen anoniem moeten gemaakt worden. De gebruiker antwoordt met j of n. Bij neen wordt deze fase overgeslagen en wordt er naar fase 2c gegaan. Bij j gebeurt het volgende:

Een methode MaakAnoniem wordt aangeroepen. Aan deze methode geef je de 2 arrays mee.

Kopieer de naam-array naar een nieuwe array van string. Vervang de naam van ieder persoon in deze nieuwe array door ***** indien deze persoon niet werd toegelaten (het aantal sterretjes is altijd even lang). De methode geeft een nieuwe array terug waarin de aangepaste namen staan.

De plekken in de array waar geen namen stonden worden niet door sterretjes vervangen én ook niet getoond.

Wanneer de methode terugkeert bewaar je het resultaat van deze methode (de naam array) in een nieuwe variabele.

Roep nu terug het ToonTracerRapport aan met deze nieuwe array. Het resultaat zou moeten zijn:

Aantal ingevoerde personen = 24
Daarvan werden 3 personen niet toegelaten, dat is 12,5%.
Risico: laag

Volgende personen werden niet toegelaten:
- *****
- *****
- *****

Fase 3: afsluiten

Het programma vraagt of er moet afgesloten worden of niet (j/n). Bij neen worden alle arrays leegemaakt en wordt er terug naar fase 1 gegaan en begint alles van voor af aan.

Volgende opgave was de vaardigheidsproefopdracht voor examen van dit vak (Programming Principles) in januari 2018

Introductie/Context

Oh jeetje, de wereld draait nu helemaal door. Jouw firma werd zonet gevraagd om een online casino te ontwerpen met als doelgroep babies. Voor ze je de effectieve site laten maken willen ze eerst een simulatie zien in console. Aan jou de eer om deze om deze dubieuze opdracht tot een goed einde te brengen.

Algemeen

De speler wordt telkens gevraagd welk van de 3 spellen hij wenst te spelen. Hij kan ten allen tijde stoppen en krijgt dan te zien hoeveel het verlies of de winst is.

De applicatie is modulair opgebouwd door middel van methoden.

Opgelet, bekijk zeker de puntenverdeling om te bepalen wat je eerst doet: Je kan deze opgave in willekeurige volgorde oplossen, daar alle delen modulair en onafhankelijk van mekaar zijn:

  • Opstarten casino: 3 punten
  • Spel Raadhetgetal: 4 punten
  • Spel Rekenenmaar: 3 punten
  • Spel ArrayGame: 5 punten
  • Afsluiten casino: 1 punt

Opstarten casino (3 punten)

Methode SetupCasino

Maak een methode SetupCasino:

  • Returntype bool
  • Parameters:
    • Naam (tekst)
    • Lengte scherm (geheel getal) [optioneel, standaard= 40]
    • Breedte scherm [optioneel, standaard= 30]
  • Werking
    • De methode geeft false terug indien de lengte of breedte kleiner is dan 1
    • Anders geeft de methode true terug en doet het volgende:
      • Het stelt het console scherm in (Width en Height) op de hoogte die via de parameters werd gegeven
      • Het vult de hele bovenste lijn van het scherm met sterretjes
      • Op de tweede lijn komt de begroeting Welkom bij Casino AP "X"
        • "X" vervang je door de Naam die als parameter werd meegegeven
      • Het vult de derde lijn van de console ook met sterretjes
  • Voorbeeld output:
*******************************************************
Welkom bij Casino AP tim
*******************************************************

Gebruik SetupCasino

Roep de methode aan vanuit de main met een naam, lengte en breedte naar keuze. Enkel indien de methode true geeft zal het programma verder gaan, anders sluit het programma zich hier af.

Methode CasinoLoop (0 punten)

Maak een methode Casinoloop die niets teruggeeft en geen parameters nodig heeft.

Indien de CasinoSetup is gelukt dan zal deze loop gestart worden.

Deze loop heeft volgende code:

   int keuze = 0;
   int geld = 0;
   while (keuze != -1)
   {
       Console.Clear();
       Console.WriteLine($"Je hebt {geld} euro.");
       keuze = ToonMenu();
       Console.Clear();
       switch (keuze)
       {
           case 1:
               geld += RaadHetGetal();
               break;
           case 2:
               geld += RekenenMaar();
               break;
           case 3:
               geld += ArrayGame();
               break;
           default:
               ShowEnding(geld);
			 keuze=-1;
               break;

       }
       Console.WriteLine("Druk toets om verder te gaan");
       Console.ReadKey();
   }

De methoden in het vet zal je nu zelf moeten schrijven zoals beschreven in de volgende secties.

ToonMenu() (0 punten)

Bevat volgende code:

int keuze = 0;

do
{
    Console.WriteLine("Geef keuze");
    Console.WriteLine("\t1. Raad het getal");
    Console.WriteLine("\t2. Rekenen maar");
    Console.WriteLine("\t3. Raad de sequentie");
    Console.WriteLine("*****");
    Console.WriteLine("-1 is stoppen");
    keuze = Convert.ToInt32(Console.ReadLine());
    Console.Clear();
} while (keuze != 1 && keuze != 2 && keuze != -1 && keuze !=3);

return keuze;

Spel Methoden (RaadHetGetal, RekenenMaar en ArrayGame)

Deze 3 methoden bevatten telkens 1 spel. Als het spel gedaan is geeft de methode telkens terug hoeveel winst (of negatieve winst) de speler heeft gemaakt.

RaadHetGetal (4 punten)

Beschrijving spel

De speler moet raden welk getal van 0 tot en met 10 de computer in gedachte heeft. Voor hij dit doet moet hij eerst ingeven hoe vaak hij denkt te moeten raden.

De winst die de methode teruggeeft is als volgt:

  • Indien de gebruiker exact wist hoevaak hij moest raden voor hij het getal zou vinden, dan krijgt hij +50.
  • Indien het aantal keer raden maximum 2 verwijderd is van hoe vaak hij dacht nodig te hebben, dan krijgt hij +5.
    • Stel dat hij aan de start 5 ingaf als aantal pogingen, dan zal hij 5 euro krijgen indien hij het uiteindelijk effectief in 3,4,6 of 7 pogingen het heeft geraden (en uiteraard 50 indien hij het in 5 keer raadde)
  • In alle andere gevallen is de winst -5.

Voorbeeld spelverloop

Welkom bij raad het getal!
Je moet een getal van 1 tot 10 raden. Hoe veel keer denk je nodig te hebben?
4   <= ingevoerd door gebruiker
Hier gaan we dan.
Welk getal is het (aantalpogingen is 0)?
1   <= ingevoerd door gebruiker
Neen, dat is het niet. Probeer opnieuw
Welk getal is het (aantalpogingen is 1)?
2   <= ingevoerd door gebruiker
Neen, dat is het niet. Probeer opnieuw
Welk getal is het (aantalpogingen is 2)?
3   <= ingevoerd door gebruiker
Jeuj. Je hebt het geraden!
Je zat er minder dan 3 af van het aantal keer dat je ging raden. Je verdient 5 euro.

RekenenMaar (3 punten)

Beschrijving spel

De speler krijgt 5 vermenigvuldigen voorgeschoteld die hij moet uitrekenen. Deze zijn steeds tussen 1 x 1 en 10 x 10 .

De winst die de methode teruggeeft is als volgt

  • Per juiste: +5
  • Per foute: -5

Voorbeeld spelverloop

Je krijgt nu 5 reken oefeningen. Per juiste krijg je 5 euro. Per foute verlies je 5 euro.
Hoeveel is 4x9?
36  <= ingevoerd door gebruiker
Mooi zo! Je winst verhoogt.
Hoeveel is 6x4?
20  <= ingevoerd door gebruiker
Dat is fout. Je winst verlaagt
Hoeveel is 7x7?
49  <= ingevoerd door gebruiker
Mooi zo! Je winst verhoogt.
Hoeveel is 8x1?
8   <= ingevoerd door gebruiker
Mooi zo! Je winst verhoogt.
Hoeveel is 9x1?
10  <= ingevoerd door gebruiker
Dat is fout. Je winst verlaagt
Je totale winst dit spel is 5
Druk toets om verder te gaan

ArrayGame (5 punten)

Beschrijving spel

Bij de start van deze methode wordt een array van bool, met lengte 10, gevuld met willekeurige true en false waarden.

De gebruiker moet proberen zo ver mogelijk door de array te geraken door te raden of de volgende waarde in de array true of false is. Van zodra de speler fout gokt stopt dit spel.

Hoe verder de speler geraakt, hoe meer winst (er kan geen verlies gemaakt worden). Er wordt op het einde ook getoond wat de volledige sequentie was

De winst die de methode teruggeeft is als volgt:

  • Alle 10 juist: +100 winst
  • In alle andere gevallen: + (5 maal het aantal juist geraden)

Voorbeeld spelverloop

Hoe lang kan jij de sequentie raden?Geef 0 (false) of 1 (true) in.
Komt er juist of fout? (1 of 0)
1   <= ingevoerd door gebruiker
Goed zo!
Komt er juist of fout? (1 of 0)
1   <= ingevoerd door gebruiker
Goed zo!
Komt er juist of fout? (1 of 0)
0   <= ingevoerd door gebruiker
Fout
Je behaalde een sequentie van 2 juiste gokken. Dat is 10 euro waard.
De correcte sequentie was:
True,True,True,True,True,True,True,False,True,False,

Afsluiten casino: 1 punt

Indien de gebruiker deze optie kiest dan stopt het programma.

Volgende opgave was de vaardigheidsproefopdracht voor het 2e zit examen van dit vak (Programming Principles) in augustus 2019

Introductie

De verkiezingen komen er aan in Nederland. Jouw bedrijf werd ingeroepen om een applicatie te maken die de burger kan gebruiken om te ontdekken op welke partij hij het best kan stemmen. Je gaat dus een zogenaamde stemwijzer maken.

Fase 0: Wie ben je? (2 punten)

Het programma bestaat uit een loop die telkens uit volgende stappen bestaat:

  • Fase 1: Identificatie: vragen wie de gebruiker is en op welke partij hij vorig jaar stemde mbv VraagDetails-methode.
  • Fase 2: Stemwijzer starten: de gebruiker doorloopt enkele vragen om te zien op welke partij hij. Met behulp van de StemWijzer-methode.
  • Fase 3: Statistieken tonen: gebaseerd op de stemwijzer resultaten van de vorige fase wordt getoond hoe het resultaat zich verhoud tegenover de resultaten van iedereen die de test reeds heeft afgelegd. Met behulp van de ToonStatistieken-methode.

Iedere fase (1,2&3) bestaat uit een methode die vanuit deze fase 0 wordt opgeroepen. De fase 0 loop stopt nooit.

Twee arrays houden volgende informatie bij en worden aangevuld naarmate de informatie beschikbaar is:

  • Naamarray: 1 array houdt de naam van iedere persoon die de tool heeft gebruikt
  • Resultaatarray: 1 array houdt het stemresultaat uit fase 2 van de persoon bij

Beide array werken dus synchroon: op dezelfde index staat steeds de naam en stemresultaat van 1 persoon.[kies zelf of je arrays of lists gebruikt]

Fase 1: identificatie (3 punten)

Maak een methode “VraagDetails”. Deze methode vereist 2 parameters:

  • namelijk de array met de namen uit fase 0 (naamarray).
  • De kleur (ConsoleColor) waarin de boodschap in de methode moet getoond worden op het scherm. De standaardkleur is Red.

De methode vraagt de naam van de gebruiker (in de kleur die werd meegegeven) en voegt die aan de array toe op de eerste lege plek.

Indien de naam reeds voorkomt in de array verschijnt er een foutboodschap en wordt de naam opnieuw gevraagd. Dit blijft gevraagd worden tot de gebruiker een geldige naam (of "admin") invoert

De methode geeft een bool terug als volgt:

  • True indien de gebruiker de naam "admin" heeft ingegeven
  • False in alle andere gevallen  

Fase 2.1: Welke partij past bij je? (2 punten)

Maak een methode “Stemwijzer”, deze methode vereist 2 parameters van het type bool en ConsoleColor. De Methode geeft een string terug als resultaat.

De eerste parameter die je moet meegeven is een bool die aangeeft of het om een admin gaat of niet. De standaard waarde van deze parameter is false (zie fase 2.3 ivm het gebruik van deze parameter).

De tweede parameter is wederom de kleur waarmee de tekst in de methode zal getoond worden.

Wanneer de methode opstart wordt er een reeks vragen gesteld. Afhankelijk van het vorige antwoord krijg je andere vragen. Je dient volgende beslissingsboom in te voeren, startende aan de linkerkant:

De eerste vraag zal dus zijn “Vrije sluitingstijden in de horeca”. De volgende vraag zal zijn Extra evenementen?” als bij de vorige vraag “neen” werd geantwoord, anders is de volgende vraag “Politie moet harder optreden”

  • De gebruiker mag enkel “ja” of “nee” antwoorden.
  • Je houdt bij hoe vaak de gebruiker ja heeft geantwoord, en hoe vaak er nee werd geantwoord. [x] en [y]
  • Indien de gebruiker een niet geldige invoer geeft dan zal de vraag opnieuw gesteld worden tot hij correct (ja,nee) invoert.

Het scherm wordt na de vragen leeggemaakt en in het midden van het consolescherm komt de tekst: De partij waar je best op stemt is [uitgekomen partij] je hebt hiervoor [x] keer ja geantwoord en [y] keer nee

Fase 2.2: Resultaat bewaren (2 punten)

De methode geeft vervolgens de partij als string terug waar de stemwijzer is op uitgekomen.

Enkel indien de gebruiker géén admin is (wat je hebt teruggekregen via de bool in van VraagDetails() methode wordt vervolgens z’n stemresultaat bewaard in de array 2: Dit resultaat wordt in de 2e array van fase 0 bewaard op de respectievelijke index waar ook de naam van de huidige gebruiker in de andere array staat.

Fase 3:Statistieken tonen (3 punten)

Als laatste fase wordt een methode ToonStatistieken aangeroepen. Deze methode verwacht twee arrays. De eerste array bevat namen (string), de andere de stemresultaten (string of enum als je de volgende fase ook maakt).

De methode gebruikt de 2 arrays om enkele interessante statistieken te tonen:

  1. Het toont het percentage dat partijen vertegenwoordigd zijn. Als dus de array bestaat uit vvd,d66,vvd. Dan zal vvd 66% vertegenwoordigen, d66 33%
  2. Je toont ook het aantal keer dat iedere partij voorkwam aan de hand van een lijn bestaande uit zoveel sterren. Als vvd 5 stemmen kreeg, d66 3 en bas 6 dan toont de methode dit als volgt:
vvd ******
d66 ***
bas *****
  1. Het toont de gemiddelde lengte van de gebruiker. Als de namen bestaan uit Tim,Jos,Frederik, Frans dan is dit gemiddelde 4,75 letters

  2. Het geeft een overzicht van alle stemresultaten maar toont enkel de eerste letter van iedere naam. Als bijvoorbeeld Tom op d66 uitkwam, Gerolf op vvd en Frans op bas dan verschijnt er:

    T, d66
    G, vvd
    F, bas
    

Extra: Enum (2 punten)

Gebruik een enum type om de partijen voor te stellen in je code en vervang ook de array door een array die die enums kan bevatten in plaats van strings voor de partijen.

Volgende opgave was de vaardigheidsproefopdracht voor examen van dit vak (Programming Principles) in januari 2019

Introductie- ImmoTrainer

Een firma die huisverkopers opleidt heeft je gevraagd om een trainingspakket te maken. Het pakket moet de trainees in staat stellen om te oefenen op het zo goed mogelijk kopen van huizen die op de markt verschijnen.

Er zijn twee gebruikers:

  • De trainer: degene die het programma instelt aan de start
  • De trainee: de persoon die de opleiding volgt en dus de training doet.

Er hoeft geen rekening gehouden te worden met foute invoer van de gebruiker. De gebruiker voert enkel zaken in die toegestaan zijn.

Algemene werking

Het programma doorloopt een aantal fasen:

  • Fase 1 (4punten): De trainer stelt de moeilijkheidsgraad in van het programma zodat het startbudget kan berekend worden
  • Fase 2 (8 punten): De trainee krijgt huizen aangeboden (willekeurig) waar hij op kan bieden. Van zodra het budget op is wordt naar de volgende fase doorgegaan.
  • Fase 3 (4 punten): De trainer én trainee krijgen te zien hoe goed de trainee het heeft gedaan

Achteraan is ook een aanvullende-opgave (2 punten waard) waar je eventueel al van bij de start rekening mee kunt houden.

Overzicht werking/output (geel gemarkeerd=invoer gebruiker)

1.2 Fase 1: Setup (4 punten)

  • De moeilijkheidsgraad wordt gevraagd (een getal van 1 tot en met 9, hierop moet géén foutcontrole gebeuren).
  • Er wordt gevraagd of de trainee een bonus krijgt (j of n) en dit wordt als bool bewaard.
  • Een methode “BudgetGenerator” wordt aangeroepen. Deze zal een budget teruggeven dat je bewaard in het hoofdprogramma en nodig hebt in de volgende fasen.

Voorbeeld output

Geef moeilijkheidsgraad in (een getal van 1 tot en met 9)
5  <-input user
Krijg de trainee een startbonus? [j/n]
j <-input user
Je startbudget is 4500

1.2.1 Methode BudgetGenerator

Output: geeft double terug ( namelijk het berekende startbudget)

Input:

  1. De moeilijkheidsgraad als geheel getal, standaardwaarde = 5
  2. De startbonus als bool, standaardwaarde = true

Werking:

Het basisbudget is 5000. Indien een startbonus wordt toegestaan komt hier 2500 bij. Per moeilijkheidsgraad gaat er 100 euro van het totaal budget af.

Voorbeeld:

moeilijkheidsgraad 5 en geen startbonus => budget = 5000 – (5*100) = 4500.

Fase 2: Training (8 punten)

  • Een loop wordt gestart, zolang de trainee budget heeft krijgt hij steeds een nieuw huis te zien dat hij kan kopen. Wanneer er 20 huizen getoond stopt de loop ook, ongeacht of de gebruiker nog budget over had of niet.
  • De loop:
    • Het huidige budget wordt steeds opnieuw getoond
    • De prijs van een nieuw huis wordt gegenereerd in een methode “HuisPrijs”, de methode toont ook wat het huis heeft (qua kamers en terras)
    • De te betalen prijs wordt getoond.
    • De trainer kan beslissen om het huis te kopen of niet
      • Wanneer de trainee een huis koopt zal zijn budget verminderd worden met de prijs van het huidige huis.
      • De gebruiker moet hiervoor j of n antwoorden op de vraag of hij dit huis wenst te kopen
    • Er wordt bijgehouden welke huizen de trainee wel én niet kocht alsook de prijs van ieder huis. Dit wordt in 2 arrays bewaard:
      • 1 array (double) zal de prijzen van ieder gegenereerd huis bijhouden
      • 1 array (bool) zal bijhouden of de gebruiker het huis wel of niet kocht

Voorbeeld output (deels) (geel gemarkeerd = invoer gebruiker)

*****
Huis met 2 slaapkamers en geen terras: 1240 euro
Je budget is nu 4500
-----------------
Wil je dit huis kopen? Het kost 1240 euro. [j/n]
n <-input user
*****
Huis met 3 slaapkamers en met terras: 1810 euro
Je budget is nu 4500
-----------------
Wil je dit huis kopen? Het kost 1810 euro. [j/n]
j <-input user
*****
Huis met 2 slaapkamers en geen terras: 1240 euro
Je budget is nu 2690
-----------------
Wil je dit huis kopen? Het kost 1240 euro. [j/n]
j <-input user
*****
Huis met 1 slaapkamers en geen terras: 1120 euro
Je budget is nu 1450
-----------------
Wil je dit huis kopen? Het kost 1120 euro. [j/n]

Methode HuisPrijs

Output: geeft int terug ( namelijk de prijs van het nieuwe huis)

Input: geen

Werking:

  • De methode genereert een huis:
    • Een huis heeft 1, 2 of 3 slaapkamers. Dit wordt willekeurig gegenereerd
    • Eén derde van de huizen (willekeurig) heeft een terras, de andere huizen niet
  • De prijs wordt berekend
    • Ieder huis heeft als basisprijs 1000
    • Per slaapkamer komt er 120 euro bij
    • Een terras doet nog eens 450 euro er bij
  • De methode toont een beschrijving van het huis alsook de prijs. Voorbeelden:
    • Huis met 2 slaapkamers en geen terras: 1240 euro
    • Huis met 1 slaapkamers en geen terras: 1120 euro
  • De prijs wordt teruggegeven  

Fase 3: Afsluiten (4 punten)

  • In deze fase wordt een overzicht getoond van de gekochte én niet gekochte huizen. Gebruik hiervoor de twee arrays waarin je die informatie bijhield. Je toont telkens de prijs van het huis alsook of deze wel of niet werd gekocht.
  • Na het overzicht wordt de gemiddelde prijs van de aangekochte huizen getoond m.b.v. een methode BerekenGemiddelde. Deze methode zal het gemiddelde berekenen en in het hoofdprogramma wordt deze dan in een zin getoond (zie voorbeeld output)

Voorbeeld output

Huis met prijs 1240 euro heb je gekocht
Huis met prijs 1690 euro heb je niet gekocht
Huis met prijs 1120 euro heb je gekocht
Huis met prijs 1810 euro heb je niet gekocht
Huis met prijs 1810 euro heb je niet gekocht
Huis met prijs 1810 euro heb je gekocht
Huis met prijs 1120 euro heb je gekocht

Je spendeerde gemiddelde 1322,5 euro aan een huis

Methode BerekenGemiddelde

Output: geeft double terug ( namelijk de gemiddelde prijs van de gekochte huizen)

Input:

  • een array van type bool die aangeeft (in volgorde) welke huizen wel en niet werden gekocht,
  • een array van type int die de prijzen van alle gegenereerde huizen bevat (in dezelfde volgorde als de andere array)

Werking:

Het gemiddelde wordt berekend van de prijzen van de huizen die wél werden gekocht.

Aanvullende opgave (2 punten)

Iedere fase (3 in totaal) wordt in een aparte methode geplaatst. In de main-methode mogen enkel deze 3 methode-fasen staan alsook eventuele variabelen die je nodig hebt om data tussen de methoden uit te wisselen.

Volgende opgave was de vaardigheidsproefopdracht voor 2e zit examen van dit vak (Programming Principles) in juni 2020

Intro

Dit nooit meer. Tijdens een examen Java ging plots het brandalarm af. Alhoewel dit terecht was, heeft de PAP Hogeschool ons gevraagd om een verbeterde alarminstallatie te maken. De nieuwe installatie moet een beter logbook tonen en bijhouden van de echte en valse alarmen.

Punten verdeling:

  • Hoofdprogramma 6p
  • Methoden 10p
  • Eindfase 2p
  • Enums als extra 2p

Werking

Het programma zal gedurende maximum 10 jaren steeds de 52 weken van een jaar doorlopen. Het programma zal iedere 10e week een testalarm starten. Doorheen het jaar zullen ook echte alarmen plaatsvinden. Aan het einde van ieder jaar wordt het jaar gevisualiseerd (in welke weken een test, niets of echt alarm plaatsvond) Al deze zaken worden in een log bijgehouden zodat op het einde van het programma kan gekeken worden hoeveel percentage echte alarmen hebben plaatsgevonden. overview Dit blijft zich herhalen tot er 10 jaar zijn gepasseerd of wanneer de gebruiker op n heeft gedrukt.  

Hoofdprogramma (6p)

Het hoofdprogramma bestaat uit volgende delen:

  • Een hoofdloop die de jaren voorstelt:
    • Deze zal maximum 10x doorlopen worden. Deze kan onderbroken worden wanneer de gebruiker na 1x jaar “n” antwoord op de vraag of er nog een jaar moet gestart worden.
    • Indien de gebruiker “n” heeft geantwoord sluit deze loop af en wordt het eindverslag getoond (zie verder)
  • Binnenin de hoofdloop wordt telkens een weekloop gestart. Deze zal steeds 52 keren herhalen (1 jaar bestaat uit 52 weken):
    • In de weken 1,11,21,31,41 en 51 wordt steeds een testalarm gestart. In deze weken wordt de methode “TestAlarm” aangeroepen (zie verder).
    • In de andere weken is er 25% kans dat er alarm afgaat. In die weken wordt een methode “StartAlarmGebouw” (zie verder) aangeroepen.
      • Het alarm gaat in een van de 3 willekeurige gebouwen door (=eerste parameter). De kans is per gebouw even groot.
      • Via de tweede parameter wordt aangegeven dat het niét om een test gaat
    • Een array die 52 elementen kan bevatten (type int, of enum (zie verder) ) houdt bij per week of er geen (=0), een testalarm (=1) of een echt alarm (=2) is afgegaan. Deze array is het weeklogbook
  • In de hoofdloop nadat 1 jaar werd doorgelopen (dus 52 weken in de weekloop):
    • wordt het weeklogbook getoond via de “ToonLogbook” methode (zie verder), het weeklogbook wordt als parameter meegegeven.
    • Voorts wordt het jaarlijks gemiddelde echte alarmen berekend via de methode “BerekenSommen” (zie verder) ,het jaarlogbook wordt als parameter meegegeven.Het resultaat van dit jaarlijkse gemiddelde wordt in een array (type double) bijgehouden), de jaarlogbook.

Samengevat:

Binnen de hoofdloop zit:

  1. een weekloop die alle 52 weken afloopt,...
  2. een jaaroverzicht, bestaande uit weeklogboek en berekening jaarlijks gemiddelde dat aan de array van alle jaren wordt toegevoegd,. Wanneer de hoofdloop afsluit wordt een vervolgens een overzicht van de voorbije jaren getoond (het eindverslag)

Enum (2 punten)

Indien je besluit om de weeklogbook met een enumtype ipv int voor te stellen kan je 2 extra punten verdienen. Uiteraard dien je hiervoor ook sommige methode en en code aan te passen om dit werkende te krijgen.  

Methoden

StartAlarmgebouw methode (2p)

Deze methode zal een tekst op het scherm schrijven zodat duidelijk wordt dat er een alarm afgaat.

Deze methode aanvaard een int en een bool.

Het getal stelt het gebouw voor waar het alarm afgaat. De bool geeft aan of het wel of niet om een test gaat.

De methode geeft niets terug.

De volgende tekst verschijnt, waarbij het gebouwnummer en of het om een test gaat wordt gecommuniceerd, voorbeelden: vb

TestAlarm methode (2p)

Deze methode geeft niets terug en heeft geen parameters nodig. De methode zal het alarm in een willekeurig gebouw laten afgaan als volgt.

De methode roept de StartAlarmGebouw methode aan, waarbij via de bool wordt aangegeven dat het om een test gaat. Voorts wordt er een willekeurig gebouw meegegeven:

  • 20% kans dat het om gebouw 1 gaat
  • 30% kans dat het om gebouw 2 gaat
  • 50% kans dat het om gebouw 3 gaat

ToonLogbook methode (4p)

Deze methode zal de meegeven array (het logbook) visualiseren door telkens een weekgetal te tonen en de juist kleur, als volgt:

  • De weken zijn genummerd van 1 tot en met 9 en herhalen zich dan
  • De weken zonder alarm zijn blauw, de testalarmen zijn groen, de echte alarmen zijn rood

Enkele voorbeelden:

vb2

Daar de testen altijd op zelfde momenten plaatsvinden (week 1,11,21, etc.) zullen de groene blokjes steeds op zelfde plek staan (namelijk 1,2,3,4,5 en 6).

BerekenSommen (2p)

De methode aanvaardt één array van het type int en geeft een gewone double terug.

Deze methode zal berekenen hoeveel percent van de elementen in de meegegeven array (het jaarlogbook) een echt alarm waren:

  • Wanneer er in de 52 weken 7 echte alarmen waren dan zal het gemiddelde 13,46…% zijn, namelijk 7/52*100.

De methode zal dit gemiddelde op het scherm tonen én ook als double resultaat ook teruggeven.

Eindverslag (2p)

De bewaarde gemiddelde in de jaarlogbook array worden in deze fase getoond tot 2 cijfers na de komma. Wanneer een gemiddelde lager dan 10% was zal deze in een groene kleur getoond worden.

Voor ieder gemiddelde verschijnt het jaarnummer, vanaf 1 te tellen.

eindverslag

Volgende opgave was de vaardigheidsproefopdracht voor examen van dit vak (Programming Principles) in januari 2020

Opgave

De geheime diensten hebben je hulp ingeroepen. De laatste weken onderscheppen ze vreemde signalen uit de lucht en ze hebben software nodig om deze signalen te analyseren. Jouw opdracht bestaat er uit een tool te ontwikkelen die de geheime diensten zal vertellen wanneer een signaal verdacht is of niet.

Deel 1 : signaal namaken

De signalen die onderschept worden bestaan steeds uit een willekeurige reeks hoofdletters van A tot en met Z. Ze zijn steeds 100 karakters lang.

Een voorbeeld van zo’n signaal: BAMKHVSSJDUHCDGDQWQXYSXPVHOMRALPNNAKIRHVBEQWQOFDMQFKQLAIDXPAHGBJSEUPCWUYOVVTBBZGCKZBVBYCNSHLCHNUDHDL

  • Maak een methode (genaamd MaakSignaal) die geen parameters aanvaardt en een array van chars teruggeeft:
    • De methode genereert een random array met lengte 100 die de eigenschappen heeft van het signaal hierboven in de kader beschreven.
    • (Denk eraan: “Een methode mag enkel datgene doen, wat de identifier van de methode laat vermoeden. Dus een signaal maken en NIET afdrukken”)
  • Roep deze methode aan bij de start van je applicatie. De array (we zullen deze code noemen) die je terugkrijgt zal je vervolgens gebruiken in de volgende delen
  • Toon de gegenereerde code op het scherm

Deel 2 : visualisatie signaal

Onderzoekers hebben ontdekt dat enkel de letters X,Y, Z en Q verdacht zijn. Alle overige informatie is niet nodig om het signaal te analyseren.

  • Maak een methode (genaamd VisualiseerCode) die 1 parameter van het type char-array aanvaardt en niets teruggeeft:
    • De methode zal element per element van de meegegeven parameter overlopen en het volgende doen:
      • Het huidige element wordt aan een methode NeedsColor (zie verder) meegegeven. Deze methode zal een bool teruggeven
      • Indien het resultaat van NeedsColor waar is dan zal het huidige element aan een methode WriteCharInRed meegegeven worden (zie verder)
      • Indien het resultaat van NeedsColor NIET waar is, dan zal er een punt (.) op het scherm geplaatst worden in plaats van het karakter.

Hulpmethoden:

  • NeedsColor: deze methode geeft een bool terug en aanvaardt 1 parameter van het type char. De methode geeft enkel true terug indien de parameter een X,Y,Z of Q karakter is.
  • WriteCharInRed: deze methode geeft niets terug en aanvaardt 1 parameter van het type char. De methode zal de meegegeven parameter in het rood op het scherm tonen (m.b.v Write) op de huidige plaats van de cursor, en vervolgens de kleur terug resetten.

Deel 3 : analyse signaal

Het aantal keer dat de speciale tekens X,Y,Z en Q voorkomen bepaalt hoe verdacht het signaal juist is. Enkel indien de verdachte tekens in totaal een veelvoud van 3 zijn is het signaal interessant om op te volgen. Een bericht zonder verdachte tekens is niet verdacht.

  • Maak een methode (genaamd AnalyseerCode) die 1 parameter van het type char-array aanvaardt en niets teruggeeft:
    • De methode zal de methode CountSpecials aanroepen (zie verder) om te weten hoeveel verdachte tekens er voorkomen. Vervolgens zal de methode een zin tonen die zegt hoeveel speciale tekens er gevonden werden.
    • Vervolgens wordt een methode IsVerdacht aangeroepen (zie verder) waar het resultaat van CountSpecials als parameter aan wordt meegegeven. Indien het resultaat van IsVerdacht true is dan zal er op het scherm verschijnen “Dit is een verdacht signaal” , anders verschijnt er “Dit is geen verdacht signaal”.

Hulpmethoden:

  • CountSpecials: deze methode aanvaardt een array van chars en geeft een int terug als resultaat. De methode zal teruggeven hoe vaak de speciale letters X,Y,Z en Q voorkomen in de meegegeven array.
    • De methode zal de eerder geschreven methode NeedsColor gebruiken om te weten of een char speciaal is of niet
  • IsVerdacht: deze methode aanvaardt een int als parameter en geeft een bool terug als resultaat. Het resultaat van deze methode is true indien de meegegeven parameter een veelvoud van 3 is (3 zelf dus ook) .

Deel 4 : opnieuw?

Finaal verschijnt de vraag of de gebruiker opnieuw deel 1 tot 3 wil doorlopen. We starten terug bij deel 1 indien de gebruiker ‘j’ (als karakter) invoert. In alle andere gevallen sluit het programma zich af.

Volgende opgave was de vaardigheidsproefopdracht voor inhaalexamen van dit vak (Programming Principles) in januari 2022

ChemQuiz Inhaalvaardigheidsproef

Introductie

Een chemieleerkracht wil een quiz-applicatie waarmee ze haar leerlingen kan ondervragen in hun kennis van de chemische elementen.

Werking

De applicatie bestaat uit 2 verschillende rondes waarin de leerling de namen, symbolen en atoomnummers van de chemische elementen moet raden.

Ronde 1: 10 willekeurige vragen om de kennis van de naam, symbool of atoomnummer te testen. Ronde 2: sudden death ronde die enkel test naar de kennis van de symbolen en die eindigt van zodra de student 1 fout maakt.

Miniles chemie

Bij deze opgave heb je volgende 2 arrays nodig:

string[] elNamen = { "Hydrogen", "Helium", "Lithium", "Beryllium", "Boron", "Carbon", "Nitrogen", "Oxygen", "Fluorine", "Neon", "Sodium", "Magnesium", "Aluminum", "Silicon", "Phosphorus", "Sulfur", "Chlorine", "Argon", "Potassium", "Calcium", "Scandium", "Titanium", "Vanadium", "Chromium", "Manganese", "Iron", "Cobalt", "Nickel", "Copper", "Zinc", "Gallium", "Germanium", "Arsenic", "Selenium", "Bromine", "Krypton", "Rubidium", "Strontium", "Yttrium", "Zirconium", "Niobium", "Molybdenum", "Technetium", "Ruthenium", "Rhodium", "Palladium", "Silver", "Cadmium", "Indium", "Tin", "Antimony", "Tellurium", "Iodine", "Xenon", "Cesium", "Barium", "Lanthanum", "Cerium", "Praseodymium", "Neodymium", "Promethium", "Samarium", "Europium", "Gadolinium", "Terbium", "Dysprosium", "Holmium", "Erbium", "Thulium", "Ytterbium", "Lutetium", "Hafnium", "Tantalum", "Tungsten", "Rhenium", "Osmium", "Iridium", "Platinum", "Gold", "Mercury", "Thallium", "Lead", "Bismuth", "Polonium", "Astatine", "Radon", "Francium", "Radium", "Actinium", "Thorium", "Protactinium", "Uranium", "Neptunium", "Plutonium", "Americium", "Curium", "Berkelium", "Californium", "Einsteinium", "Fermium", "Mendelevium", "Nobelium", "Lawrencium", "Rutherfordium", "Dubnium", "Seaborgium", "Bohrium", "Hassium", "Meitnerium", "Darmstadtium", "Roentgenium", "Copernicium", "Nihonium", "Flerovium", "Moscovium", "Livermorium", "Tennessine", "Oganesson" };

string[] elSymbool = { "H", "He", "Li", "Be", "B", "C", "N", "O", "F", "Ne", "Na", "Mg", "Al", "Si", "P", "S", "Cl", "Ar", "K", "Ca", "Sc", "Ti", "V", "Cr", "Mn", "Fe", "Co", "Ni", "Cu", "Zn", "Ga", "Ge", "As", "Se", "Br", "Kr", "Rb", "Sr", "Y", "Zr", "Nb", "Mo", "Tc", "Ru", "Rh", "Pd", "Ag", "Cd", "In", "Sn", "Sb", "Te", "I", "Xe", "Cs", "Ba", "La", "Ce", "Pr", "Nd", "Pm", "Sm", "Eu", "Gd", "Tb", "Dy", "Ho", "Er", "Tm", "Yb", "Lu", "Hf", "Ta", "W", "Re", "Os", "Ir", "Pt", "Au", "Hg", "Tl", "Pb", "Bi", "Po", "At", "Rn", "Fr", "Ra", "Ac", "Th", "Pa", "U", "Np", "Pu", "Am", "Cm", "Bk", "Cf", "Es", "Fm", "Md", "No", "Lr", "Rf", "Db", "Sg", "Bh", "Hs", "Mt", "Ds", "Rg", "Cn", "Nh", "Fl", "Mc", "Lv", "Ts", "Og" };

De eerste array bevat de naam van het element, de tweede bevat het symbool. Beide arrays zijn synchroon uiteraard: het symbool van het element met de naam Helium is "He".

Het atoomnummer van een element/symbool is de "index+1" van het element/symbool in de array. Het atoomnummer van Lithium is dus 3.

Algemene opbouw hoofdprogramma

Het hoofdprogramma werkt als volgt

  1. Eerst wordt aan de gebruiker gevraagd hoeveel vragen er in ronde 1 moeten gesteld worden (ga ervan uit dat de gebruiker altijd een positief getal invoert. Geen invoercontrole nodig met andere woorden).
  2. Een loop wordt gestart die even vaak wordt uitgevoerd als dat er vragen dienen gesteld te worden (dit is ronde 1 die start).
  3. In de loop wordt telkens de StelVraag-methode (zie verder) aangeroepen. Het resultaat van deze methode, de score, wordt bijgeteld bij de totaalscore. 1 punt per juist anwoord. Er wordt een willekeurige vraagsoort als derde parameter meegeven.
  4. De score van ronde 1 wordt getoond.
  5. Ronde 2 wordt gestart door de SuddenDeathRonde-methode aan te roepen. Het resultaat van deze methode is de score voor ronde 2.
  6. De totaalscore van ronde 1 en ronde 2 opgeteld wordt getoond.
  7. Het programma sluit af.

StelVraag-methode

Deze methode aanvaardt 3 parameters:

  • Een namenarray van strings, die de namen van de elementen zal bevatten.
  • Een even lange andere symbolenarray van strings, die de respectievelijke symbolen zal bevatten
  • Een enumtype genaamd VraagSoort waarmee kan aangegeven worden wat voor vraag in de methode moet gesteld worden (mogelijheden zijn: ZoekSymbool, ZoekAtoomNummer, ZoekNaam)

De methode stuurt een int terug, die de score voorstelt (0 indien fout, 1 indien juist) (let op: we hanteren een int, ook al zijn er maar 2 mogelijke uitkomsten, we willen echter dat de applicatie in de toekomst ook met andere scores kan werken)

Afhankelijk van de vraagtype zal de applicatie nu een vraag stellen. De vraag zal telkens een willekeurig element kiezen. De gebruiker moet (nog steeds in de methode) het juiste antwoord intypen dat dan zal gecontroleerd worden:

  • ZoekSymbool: de applicatie toont een willekeurig element uit de namen array en vraagt het symbool (het toont ook het atoomnummer bij de vraagstelling). Indien de gebruiker het juiste symbool geeft geeft de methode 1 terug, anders 0. Via de symbolenarray kan de applicatie controleren of dit klopt. Voorbeeld van de vraag: "Wat is het symbool van Helium (atoomnummer 1)?" Antwoord = H.
  • ZoekAtoomNummer: de methode toont een element naam (uit de namen array) en wil weten wat het atoomnummer is (zijnde de index+1 van het element). Ook hier is de score het zelfde. Voorbeeld: "Wat is het atoomnummer van Helium?" Antwoord= 2.
  • ZoekNaam: in deze vraag wordt een willekeurig symbool gekozen uit de symbolenarray en gevraagd wat de element naam is. Voorbeeld: "Wat is het element H, met atoomnummer 2?" Antwoord: He. Ook hier zelfde score.

Indien de gebruiker fout antwoord dan wordt eerst het juist antwoord getoond en dan wordt een score van 0 teruggegeven.

Tip: je kan overwegen om voor de 3 vraagtypes 3 aparte methodes aan te maken die je dan aanroept vanuit deze methode (hoort niet bij opgave, maar zal je code wel leesbaarder maken)

SuddenDeathRonde

Deze methode aanvaardt 2 parameters:

  • Een namenarray van strings, die de namen van de elementen zal bevatten.
  • Een even lange andere symbolenarray van strings, die de respectievelijke symbolen zal bevatten

De methode stuurt een int terug, die de totale score voorstelt.

Deze methode start een loop waarin telkens een vraag van het type ZoekSymbool wordt gesteld gebruikmaken van de StelVraagmethode.

Zolang de gebruiker juist antwoordt blijft deze loop doorgaan. De loop zal de totale score bijhouden (het telt het resultaat van StelVraag op bij een totale score).

De methode stopt wanneer de gebruiker een fout heeft gemaakt. Het geeft dan de totaalscore terug.

Voorbeeld werking

Een liggend streepje aan het begin van een lijn geeft aan dat dit door de gebruiker werd ingevoerd:

Hoeveel vragen moeten er in ronde 1 gesteld worden?
_3

Ronde 1 start! Hier volgen 3 vragen

Wat is element  Cl, met atoomnummer 17?
_Chloor
Fout. Het was Chlorine. Geen punt!

Wat is element  Zn, met atoomnummer 30?
_Zinc
Juist! 1  punt bij

Wat is het atoomnummer van Radon?
_120
Fout. Het was 86. Geen punt!


Je score voor ronde 1 is 1

De sudden death ronde start nu!

Wat is het symbool van Chromium, met atoomnummer 24?
_Cr
Juist! 1  punt bij

Wat is het symbool van Astatine, met atoomnummer 85?
_As
Fout. Het was At.Sudden death gedaan!

Je score voor ronde 2 is 1
Je totale score  is 2

Inleiding

Helaas, de Mac-DamsCamp slaagt niet aan. Toch heeft APDonalds™ je gevraagd om hun kassa-software grondig te vernieuwen. Aan jou de taak om de bestelling van een hamburger, met optioneel drank en frietjes, te stroomlijnen. Go get’m!

Basis werking

Hier de algemene flow van de applicatie (merk op dat de methoden BestelFrietjes,BestelDrinken en BerekenTotaal allemaal vanuit BestelHamburger worden aangeroepen, wat later wordt uitgelegd)

De applicatie loopt oneindig door en kan dus niet afgesloten worden).

In de hoofdapplicatie bewaar je in een array (met grootte 100) van doubles telkens het resultaat van de BestelHamburger-methode. (met deze array doe je voor de rest niets).

Methoden (10 p)

BestelHamburger (3p)

  1. Deze methode toont het menu( zie onderaan) en heeft een double als resultaat. De methode vraagt eerst welke hamburger de gebruiker wenst. De gebruiker voert een string in voor z’n keuze (“gewoon”, “fish”, “veggie”). Zolang de gebruiker geen juist antwoord geeft wordt de vraag opnieuw gesteld en de input uitgelezen.
  2. Vervolgens dient de gebruiker met “j” of “n” te antwoorden of deze frietjes wilt .Indien ja dan wordt de methode “BestelFrietjes” aangeroepen.
  3. Vervolgens dient de gebruiker met “j” of “n” te antwoorden of deze frisdrank wenst. Indien ja dan wordt de methode “BestelDrinken” aangeroepen.
  4. Finaal roept deze methode de BerekenTotaalMethode aan en zal alle verzamelde informatie aan deze methode meegeven (zie verder). Het resultaat van de BerekenTotaalMethode (een double) wordt teruggegeven als resultaat van deze methode.

BestelFrietjes (1p)

Deze methode vraagt hoeveel frietjes de gebruiker wenst en zal het resultaat (een getal) teruggeven als resultaat. De gebruiker voert een getal in tussen 1 en 10 (geen controle nodig) en dit getal geeft deze methode terug.

BestelFrisdrank (2p)

De methode vraagt welke frisdrank de gebruiker wenst (zie menu), wederom voert de gebruiker een getal in. Dit getal wordt omgezet naar een enum van het type Frisdranken (met als waarden Water, Fanta, Cola) dat zal teruggeven als resultaat van de methode.

Berekentotaal (3p)

Deze methode geeft de prijs als double tot 2 cijfers na de komma op het scherm weer en zal deze waarde ook als return teruggeven. De methode aanvaardt volgende parameters:

  1. hamburger, string, verplicht
  2. aantalfrietjes, int, optioneel (standaard: 1)
  3. drank, enum type Frisdranken, optioneel (standaard: "geen")

Volgende menukaart wordt gebruikt om met voorgaande informatie de totaalprijs te bereken.

De menukaart:

  • Gewone hamburger: €5
  • Fishburger: €6
  • Veggieburger: €3
  • Water €2
  • Fanta €3
  • Cola €3
  • Frietjes: €2 per frietje

Promoties

Volgende promoties zijn actief en worden toegepast indien relevant:

  • Box: Indien de gebruiker én frisdrank én frietjes besteld krijgt hij 5€ korting op de totaalprijs.
  • Hipster: Indien de gebruiker Water, een Veggieburger en 1 frietje besteld krijgt hij €3 korting op de totaalprijs.

Indien de gebruiker een promotie heeft gekozen dan wordt dit op het scherm getoond vlak voor de prijs wordt getoond. 

Uitbreidingen (2p)

Voeg volgende uitbreidingen toe aan de applicatie.

Visualisatie bestelling (2p): In de methode BerekenTotaal wordt de bestelling gevisualiseerd als volgt:

  • Hamburger wordt voorgesteld als de letter “H” met als achtergrond groen bij gewone hamburger, blauw voor fish, rood voor veggie.
  • Het aantal frietjes wordt voorgesteld als evenveel keer de letter “I” (hoofdletter i) als er frietjes zijn besteld.
  • De frisdank wordt voorgesteld door een letter (“W” voor water, “F” voor Fanta, “C” voor cola)

Voorbeeld voor iemand met fishburger, 3 frietjes en fanta:

HIIIF

(de H heeft een blauwe achtergrond)

Vervolgens wordt de prijs en eventuele promotievermelding getoond.

Volgende opgave was de vaardigheidsproefopdracht voor het examen van dit vak (Programming Principles) in januari 2023

Inleiding

Hoera. Je nieuw opgerichte bedrijf “Tornado” heeft z’n eerste contract binnen. Om de capaciteiten van je bedrijf aan te tonen heeft je klant gevraagd een spel, Underlook 2, te programmeren. Laat je niet afschrikken door de opgave van de klant: het gaat om een eenvoudig spelletje. Doel Het spel bestaat uit meerdere delen die je dient te maken, te weten:

  1. Deel 1: Een hero-generator (2punten)
  2. Deel 2: De hero-visualisatie (2 punten)
  3. Deel 3: Een trainingsmissie om het spel aan de speler duidelijk te maken (3 punten)
  4. Deel 4: Het spel zelf dat uit drie rondes bestaat (5 punten)
  5. Deel 5: Enkele uitbreidingen (4punten)

Deel 0

PROTIP: lees eerst de gehele opgave tot het einde. Kijk goed na dat je alles hebt gelezen en niet als een ‘kieke zonder kop’ begint te programmeren. PROTIP2: Het spel is eenvoudig, maar je klant heeft een nogal onduidelijke manier van opgave opgesteld. LEES DAAROM ZEKER DE APPENDIX ACHTERAAN WAAR HET SPEL EENVOUDIGER WORDT TOEGELICHT  

Deel 1: Hero-generator

Maak een hero-generator die 3 zaken zal generen, namelijk een naam, aantal aanval en aantal verdediging. Hoe?

  1. Vraag aan de gebruiker "Wil je een random held genereren?(ja/nee)"
  2. Indien de gebruiker “nee” antwoordt dan heeft je held volgende standaard eigenschappen:
    • Aanval= 5
    • Verdediging= 5
    • Naam= “Default”
  3. Indien de gebruiker “ja” antwoordt dan zal een willekeurige held aangemaakt worden, als volgt:
    • Aanval = willekeurig getal van 1 tot en met 9
    • Verdediging = 10 – aanval [als dus bij aanval 3 werd gegenereerd dan zal de verdediging 7 zijn]
    • Naam= resultaat van de methode NaamGen.

NaamGen-methode

De Methode NaamGen heeft volgende eigenschappen:

  • Aanvaardt geen parameters
  • Geeft een string terug, namelijk de gegenereerde naam.
  • Deze naam wordt willekeurige gekozen uit een array die vooraf gevuld werd met volgende 10 namen: TorBro, Vuilhard, Merci, Junkpoes, Genki, Ono, April, Yingyatta, Ombra en Lookup.

Deel 2: Herovisualisatie

Dit is een kort deel. In dit deel roep je een methode “ToonHeld” aan die de , al dan niet willekeurig gegenereerde, held en z’n eigenschappen toont.

ToonHeld-methode

De methode ToonHeld heeft volgende eigenschappen:

  • Retourneert niets. Toont enkel wat output in de console.
  • Aanvaardt 3 parameters, namelijk een string voor de naam, en twee ints voor de aanval en verdediging
    • Deze drie parameters zijn de resultaten die je in Deel 1 hebt gemaakt en die je nu meegeeft aan deze methode.
    • De methode zal de Held-gegevens als volgt tonen, waarbij A de aanval is en D de verdediging:
*****
Ombra
A=4
D=6
*****

Het aantal sterretjes boven en onder zijn even lang als de lengte van de held-naam.

Nog 3 voorbeelden:

*****
April
A=9
D=1
*****
***
Ono
A=5
D=5
***
*********
Yingyatta 
A=8
D=2
********

Deel 3: Trainingsmissie

Dit deel roept 1 methode aan genaamd “DoeTraining”. Deze methode zal de speler een test-spel (Opgelet: het appendix achteraan dit document beschrijft de spelregels van het spel, zoals ze ook hieronder verwerkt werden) laten uitvoeren zodat de speler begrijpt hoe het spel werkt.

DoeTraning-Methode

Deze methode heeft volgende eigenschappen:

  • Retourneert niets. Toont enkel output in de console.
  • Aanvaardt 3 parameters, namelijk een string voor de naam, en twee ints voor de aanval en verdediging
    • Deze drie parameters zijn de resultaten die je in Deel 1 hebt gemaakt en die je nu meegeeft aan deze methode.

De werking van de methode is als volgt:

  1. Een oefenmonster wordt gemaakt dat aanval = 4 en verdediging = 6 heeft.
  2. Er wordt aan de speler gevraagd hoeveel hij wenst te schieten. Dit getal mag niet groter zijn dan de aanvalskracht van de speler (wat je in deel 1 hebt gemaakt) en minimum 1.
  • De vraag toont ook tussen welk bereik van getallen de speler mag kiezen. [1 tot en met ‘aanvalskracht’)
  • Als dus de speler aanval 5 heeft dan worden enkel de getallen 1,2,3,4 en 5 aanvaardt.
  • Indien de speler een foutief getal invoert dan zal de vraag herhaald worden tot de speler een correct getal ingeeft.
  1. Er wordt nu gecontroleerd of de speler het monster raakt: enkel indien het gekozen getal maximum 1 afwijkt van de verdediging van het monster is dit raak.
  • Het monster in de training heeft standaard 6, dus enkel de getallen 5,6 en 7 worden aanvaardt.
  • Indien de speler raakt dan wordt de verdediging van het monster met 1 verlaagt.
  • Bij mis gebeurt er niets. (een held met lage aanval zal dit startmonster dus niet kunnen raken)
  1. Nu mag het monster aanvallen: het monster (de computer) kiest een willekeurig getal tussen 1 en de aanvalskracht van het monster (in de training dus tussen 1,2,3 en 4)
  • Indien het monster raakt dan wordt de verdediging van de speler met 1 verlaagd.
  1. Toon aan de speler de uitkomst van de training:
  • De speler wint indien de som van de speleraanval en spelerverdediging groter of gelijk is aan de som van de aanval en verdediging van het monster.

Voorbeeld output (na creatie van random held met aanval 2 en verdediging 8):

---De training---
Hoeveel wil je schieten? (min=1, max=2)
2
Oei, gemist. Het monster heeft verdediging 6. Je had een getal tussen 5 en 7 moeten kiezen (als dat kon met je huidige aanvalskracht). 
Door te missen wordt je aanval met 1 verlaagt. 
Nu valt het monster aan.
Het valt aan met 4.
Oef, gemist. De aanvalskracht van het monster zakt.
Eens kijken wie wint. Diegene met de hoogste som van aantal en verdediging wint. Bij gelijke stand wint de held.
Het monster wint de training. 

Deel 4: Het spel

De mogelijk verloren verdediging uit de training wordt bij aanvang van deze fase teniet gedaan. De speler heeft dus terug de aanval en verdediging zoals deze in deel 1 werd aangemaakt. In dit deel zal de speler het spel effectief kunnen spelen. In dit deel wordt een nieuwe methode “DoeGame” aangemaakt.

DoeGame-methode

Deze methode heeft volgende eigenschappen:

  • Retourneert niets. Toont enkel output in de console.
  • Aanvaardt 3 parameters, namelijk een string voor de naam, en twee ints voor de aanval en verdediging
    • Deze drie parameters zijn de resultaten die je in Deel 1 hebt gemaakt en die je nu meegeeft aan deze methode.
    • De werking is iets complexer: * Eerst verschijnt de boodschap “Het spel gaat beginnen. Druk op enter om door te gaan.” * Nadat de speler op enter heeft geduwd wordt het console-scherm leeggemaakt. * Er wordt nu 3 keer een methode “DoeGevecht” aangeroepen (zie verder). * Deze methode zal telkens een bool terug geven om aan te geven of de speler wel of niet dit gevecht wint. * Na ieder gevecht krijgt de speler terug al z’n aanval en verdediging. * Na 3 gevechten wordt de score getoond aan de speler: de score is gewoon de som van het aantal van de 3 gewonnen gevechten. Als de speler 2 van de 3 wedstrijd won dan geeft de methode 2 terug.

DoeGevecht-methode

Deze methode heeft volgende eigenschappen:

  • Retourneert een bool. Deze is true indien de speler het gevecht heeft gewonnen.
  • Aanvaardt 2 parameters, twee ints voor de aanval en verdediging
    • Deze twee parameters zijn de resultaten die je in Deel 1 hebt gemaakt en die je nu meegeeft aan deze methode.
  • De werking is als volgt:
    • Een gevecht is bijna identiek aan de training. Er zijn enkele verschillen:
      • Bij ieder gevecht wordt een nieuw, willekeurig monster wordt aangemaakt aan de start.
        • Het monster wordt als volgt aangemaakt: eerst wordt een willekeurig getal tussen 1 tot en 9 gekozen voor de aanval van het monster. De verdediging van het monster is 10 minus net gekozen aanval van het monster.
      • Indien de speler mist wordt zijn aanval met 1 verlaagt. Indien de speler raakt wordt de verdediging van het monster met 1 verlaagt [beiden mogen negatief worden]
      • Indien het monster mist wordt de aanval van het monster met 1 verlaagt. Indien het monster raakt wordt de verdediging van de speler met 1 verlaagt [beide mogen negatief worden]
    • Wanneer het gevecht voorbij is (speler en monster hebben ieder 1 keer proberen te schieten) zal de methode een bool teruggeven. Deze zal true zijn indien de speler wint (omdat z’n aanval+ verdediging groter of gelijk is aan die van het monster), zoniet wordt false teruggegeven.

Voorbeeld-output:

Volgende scherm verschijnt 3 keer:

Nieuw monster staat klaar!
Hoeveel wil je schieten? (min=1, max=6)
4
Oei, gemist. Het monster heeft verdediging 9. Je had een getal tussen 8 en 10 moeten kiezen (als dat kon met je huidige aanvalskracht). 
Door te missen wordt je aanval met 1 verlaagt. 
Nu valt het monster aan.
Het valt aan met 1.
Oef, gemist. De aanvalskracht van het monster zakt.
Het monster wint dit rondje.
Gevolgd  dan door:
Mooi zo April. Je behaalde een score van 1. Tot volgende keer!

Appendix: Spelregels Underlook

In dit spel komt de held steeds een ander monster tegen. Ieder gevecht tegen een monster is dus anders. Beide spelers (monster en held) worden gedefinieerd door twee parameters:

  • Aanvalskracht
  • Verdedigingskracht Deze parameters zijn bij aanmaken steeds samen 10 (verdediging+aanval) van zowel speler als monster Deze parameters worden bij de monsters willekeurig gegenereerd bij de start van ieder nieuw monster. Bij de held zijn deze 5 en 5, tenzij de speler deze ook random wil gegenereerd krijgen.

spelverloop

  • Om de beurt mag een van beide spelers “schieten”. Ze gebruiken hierbij hun aanvalskracht als maximum-kracht dat ze kunnen schieten. * Enkel indien met een kracht die maximum 1 verschilt van de verdediging van het doel zal er geraakt worden. Als dus het monster een verdediging van 5 heeft dan moet de held met een kracht van 4,5 of 6 schieten om te raken.
  • Wanneer gemist wordt zal de aanvalskracht van de schietende speler met 1 verlaagt worden. Indien raak dan zal de verdediging van het doelwit met 1 verlaagt worden.
  • De speler wiens totale som van verdediging en aanvalskracht na het gevecht het hoogste is wint het gevecht.

Opgelet: de kans bestaat dus dat de speler of het monster al van bij de start onmogelijk kan winnen omdat de aanvalskracht te laag is tegenover de verdedigingskracht van de tegenspeler. Dat is de pech die je kan hebben.

Volgende opgave was de vaardigheidsproefopdracht voor het examen van dit vak (Programming Principles) in januari 2024

Oefening 1 - Getallenverwerker

Info

Ga ervan uit dat de gebruiker géén foute invoer doet.

Opgave

  • Maak een applicatie die aan de gebruiker een getal n vraagt.
  • Vraag vervolgens aan de gebruiker n gehele getallen (bepaal zelf hoe je ze bewaard (of niet)).
  • Toon nadien volgende informatie aan de gebruiker. Hierbij wordt de waarde steeds in het rood getoond.
    • Toon het kleinste ingevoerde getal.
    • Toon het grootste ingevoerde getal.
    • Toon welk getal het vaakst opnieuw werd ingevoerd en toon ook hoe vaak dit was.
    • Toon het gemiddelde van de ingevoerde getallen tot 2 cijfers na de komma accuraat.

Voorbeeld uitvoer

Tekst die start met ">" is invoer van de gebruiker.

Geef n:
>5
Geef nu 5 getallen in:
>3
>2
>2
>-1
>2
Hier volgt de informatie over je invoer:
Kleinste ingevoerde getal: -1 
Grootste ingevoerde getal: 3
Het meest ingevoerde getal: 2
    dit getal werd 3 keer ingevoerd
Gemiddelde: 1,60

Oefening 2 - Getallenverwerker

Info

Voor deze oefening mag je extra methoden aanmaken.

Ga ervan uit dat de gebruiker géén foute invoer doet.

Opgave

Methode 1 GenereerRandom

Schrijf een methode GenereerRandom. Deze methode geeft een double terug en aanvaardt 2 int parameters, genaamd onderGrens en bovenGrens. De methode zal een random double getal teruggeven dat zich tussen de ondergrens en bovengrenswaarden bevindt. De bovengrens is exclusief en zal dus zelf nooit gegenereerd worden.

Als de methode met de waarden 6 en 12 wordt aangeroepen zal er dus een kommagetal tussen 6 en 12 worden teruggegeven worden.

Indien de ondergrens en bovengrens parameters even groot zijn dan wordt het dubbele van de bovengrens gebruikt.Indien de ondergrens groter is dan de bovengrens dan worden de grenzen omgekeerd gebruikt.

Methode 2 ToonArrayKleuren

Schrijf een methode ToonArrayKleuren die een array van double aanvaardt:

  1. De methode berekent het gemiddelde van alle waarden in de array.
  2. Twee int variabelen boven en onder krijgen volgende inhoud: boven krijgt de waarde van het gemiddelde naar boven afgerond (naar het dichtsbijzijnde gehele getal). onder krijgt de waarde van het gemiddelde naar onder afgerond. (als het gemiddelde 13.6 was dan krijgt boven de waarde 14, en onder onder de waarde 13).
  3. Deze methode zal vervolgens de inhoud van de meegeven array naar het scherm visualiseren als volgt. Het toont de getallen uit de array naast elkaar in een rij (telkens 1 cijfer na de komma), telkens met een tab tussen. Ieder getal dat zich tussen de waarden onder en boven bevindt zal vierkante haakjes rond zich hebben (bv [17,0] )

Toepassing

Schrijf in de MainOefening2 methode een programma dat aan de gebruiker de onder en bovengrens waarden vraagt (int) en vervolgens 100 keer de GenereerRandommethode met deze informatie aanroept. De 100 gegenereerde getallen worden in een array bewaard. Deze array wordt aan de methode ToonArrayKleuren meegegeven die vervolgens het nodige werk zal doen.

Voorbeeld uitvoer

Tekst die start met ">" is invoer van de gebruiker.

Geef ondergrens?
>12
Geef bovengrens?
>21
Visualisatie array:
Gemiddelde was: 17,02712064099593

15,9    [17,3]  16,1    20,4    16,9    [17,0]  19,9    14,1    14,8    12,2    14,6    16,8    [18,0]  19,8    16,3   13,8     16,8    19,7    20,6    19,0    15,8    [17,4]  20,7    19,6    14,6    18,5    12,1    16,5    14,1    16,1    19,5     12,7    20,1    19,0    14,4    12,1    13,7    14,9    19,6    15,0    20,2    20,0    14,9    19,7    19,1   12,6     [17,1]  19,0    20,1    19,8    18,3    15,6    12,2    18,0    16,3    16,4    13,6    14,5    20,4    13,9   12,2     [17,1]  14,0    12,9    [17,0]  13,2    19,3    20,3    18,6    [17,7]  15,7    18,1    20,6    20,9    19,1   12,6     15,7    [17,8]  [17,6]  14,6    18,4    16,6    14,8    14,5    20,6    14,8    [17,9]  18,3    19,5    20,4   18,8     20,9    20,8    [17,0]  18,4    20,6    20,8    12,4    [17,6]  16,4

Oefening 3 - Cinemasysteem

Voor deze oefening mag je extra methoden aanmaken.

Ga ervan uit dat de gebruiker mogelijk wél foute invoer doet.

Opgave

Een lokale cinema heeft je hulp nodig. Ze wensen een kassasysteem. Gebruikers krijgen een menukeuze en kunnen zo aangeven hoeveel tickets van elke soort er besteld moeten worden. De applicatie zal bij foute invoer een foutboodschap tonen en de invoer negeren en wachten op correcte invoer (door het menu opnieuw te tonen). Wanneer het menu opstart worden volgende opties getoond:

  1. Normaal ticket (10 euro)
  2. Reductie ticket (8 euro)
  3. Groepsticket (30 euro voor 5 personen)
  4. Opnieuw

De gebruiker kiest 1 van deze 4 opties. Bij een andere optie dan 1,2,3 of 4 zal een fout getoond worden en wordt het menu opnieuw getoond.

Bij optie 1, 2 en 3: vervolgens vraagt het programma hoeveel tickets van deze optie nodig zijn (indien de gebruiker dus 2 tickets van optie 3 kiest dan gaat het dus om 90 euro voor 15 personen). Vervolgens wordt terug het begin menu getoond.

Onderaan het menu wordt de hele tijd de huidige kost én aantal personen getoond. Enkel wanneer de gebruiker optie 4 kiest worden deze getallen gereset.

Voorbeeld uitvoer

Tekst die start met ">" is invoer van de gebruiker.

1. Normaal ticket (10 euro)
2. Reductie ticket (8 euro)
3. Groepsticket (30 euro voor 5 personen)
4. Opnieuw
Aantal personen= 0, Prijs = 0
>5
Fout. Dat kan niet.
1. Normaal ticket (10 euro)
2. Reductie ticket (8 euro)
3. Groepsticket (30 euro voor 5 personen)
4. Opnieuw
Aantal personen= 0, Prijs = 0
>2
Hoeveel?
>3
1. Normaal ticket (10 euro)
2. Reductie ticket (8 euro)
3. Groepsticket (30 euro voor 5 personen)
4. Opnieuw
Aantal personen= 3, Prijs = 24
>3
Hoeveel?
>2
1. Normaal ticket (10 euro)
2. Reductie ticket (8 euro)
3. Groepsticket (30 euro voor 5 personen)
4. Opnieuw
Aantal personen= 13, Prijs = 84
>4
1. Normaal ticket (10 euro)
2. Reductie ticket (8 euro)
3. Groepsticket (30 euro voor 5 personen)
4. Opnieuw
Aantal personen= 0, Prijs = 0

All In One

Volgende hoofdstukken bevatten grotere projecten waarin wordt getracht meerdere technieken uit de vorige hoofdstukken te combineren. Het doel van dit hoofdstuk is tweeledig:

  1. Op een andere manier tonen hoe specifieke C# elementen in een 'realistische' programma's kunnen gebruikt worden
  2. Aantonen dat, indien je tot hier bent geraakt, je al een aardig hoop skills hebt om grote, complexe applicaties te maken die verder gaan dan de standaard oefeningen die je na ieder hoofdstuk hebt gemaakt.

Volgt nu een beschrijving van de belangrijkste technieken die je in de projecten hierna zal tegenkomen:

Matrix Console

In de vorige eeuw was The Matrix een uiterst memorabele film. In volgende code tonen we hoe je het "bekende" computer-beeld kunnen nadoen waarin groene, random letters op het scherm verschijnen.

Random rangen = new Random();
Console.ForegroundColor = ConsoleColor.Green;
while (true)
{
    //Genereer nieuw random teken:
    char teken = Convert.ToChar(rangen.Next(62, 400));
    //Zet teken op scherm
    Console.Write(teken);
    
    //Ietwat vertragen
    System.Threading.Thread.Sleep(1);
    
    //Af en toe donker kleurtje
    if(rangen.Next(0,3)==0)
    {
        Console.ForegroundColor = ConsoleColor.DarkGreen;
    }
    else
    {
        Console.ForegroundColor = ConsoleColor.Green;
    }
}

Enkele opmerkingen:

  • System.Threading.Sleep() is een ingebouwde C# methode die aan de computer verteld dat je applicatie(thread) gedurende x milliseconden mag gepauzeerd (Sleep) worden. Het argument geeft weer hoeveel milliseconden dit moet zijn. Wil je dus 1 seconden pauzeren dan geef je 1000 mee.

  • Opgelet: Sleep zal je programma volledig "blokkeren", het zal met andere woorden ook geen andere zaken doen zoals input van de gebruiker detecteren.

Console-filmpjes maken

Volgende tutorial toont hoe je een eenvoudig filmpje kan maken dat je, mits wat fantasie, vlot kan uitbreiden over enkele weken met interactieve aspecten.

Basisloop voor het filmpje

Volgende voorbeeld toont wat je bijvoorbeeld kan doen. Kopieer dit alles naar een eigen project tussen de accolades van de main:

 int framenummer = 0;
int sleeptime = 1000; // in milliseconden
while (true)
{
        
    framenummer = framenummer + 1;
    if (framenummer == 1)
    {
        Console.WriteLine("Het begin");
    }
    else if  (framenummer == 2)
    {
        Console.WriteLine("Tweede frame");
    }
    else if (framenummer == 3)
    {
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine("Derde frame Andere kleur");
    }
    else if (framenummer == 4)
    {
        sleeptime = 4000; //vergeet niet terug te zetten in latere frames indien je dit maar eenmalig wil
        Console.WriteLine("Frame dat langer blijft staan");
    }
    else if (framenummer == 5)
    {
        sleeptime = 500;
        Console.WriteLine("Moving ball:       *");
    }
    else if (framenummer == 6)
    {
        Console.WriteLine("Moving ball:      *");
    }
    else if (framenummer == 7)
    {
        Console.WriteLine("Moving ball:     *");
    }
    else if (framenummer == 8)
    {
        Console.WriteLine("Moving ball:   *");
    }
    
//Voeg hier frames tussen

    else
    {
        Console.ForegroundColor = ConsoleColor.Yellow;
        Console.WriteLine("\n\n\n\nThe End");
    }
    System.Threading.Thread.Sleep(sleeptime); //Pauzeer programma
    Console.Clear();

Deze code zal een teller (framenummer) per seconde (instelbaar via sleeptime) met 1 verhogen. Vervolgens zal de code uitgevoerd worden binnen de else-if clausule die overeenkomt met de huidige framenummer. De eerste keer staat de teller op 1 en wordt dus de code tussen volgende if uitgevoerd.

if (framenummer == 1)
{
    Console.WriteLine("Het begin");
}

Vervolgens wordt deze met 1 verhoogd en wordt deze code uitgevoerd:

else if  (framenummer == 2)
{
    Console.WriteLine("Tweede frame");
}

En zo voort. Je kan dus zelf frames toevoegen door steeds een constructie als de volgende toevoegen waar de commentaar //Code om uit te voeren in dit frame staat:

else if  (framenummer == x)
{
    //Code om uit te voeren in dit frame
}

(vervang X door het framenummer dat dit moet zijn)

Inspiratie nodig

Mogelijke uitbreidingen kunnen zijn:

  • Kleuren aanpassen om zo mooiere zaken te tonen (zie voorbeeldframe 3)
  • Timing aanpassen wanneer volgende frame moet getoond worden (zie voorbeeldframe 4)
    • Bijgevolg kan je ook animaties doen, verlaag gewoon de tijd tussen frame en zet zaken op andere plekken (zie voorbeeldframes 5 t.e.m. 8)
  • Ipv tekst zou je heeldere Unicode-art afbeeldingen kunnen tonen (én animeren)

Geavanceerde uitbreidingen

Niet-sequentiële flow

Je kan ook bepalen wat het volgende frame moet zijn door de variabele framenummer aan te passen naar het framenummer dat je nodig hebt -1 . Stel dat je bijvoorbeeld in frame 8 wenst dat na dit frame frame 3 wordt uitgevoerd, dan schrijf je:

else if (framenummer == 8)
{
    framenummer = 2;
    Console.WriteLine("Moving ball:      *");
}

Cursor verzetten

Via de methode ConsoleSetCursorPosition kan je instellen waar de cursor moet gezet worden. Je geeft tussen de haakjes van deze methode de x,y coördinaten (integers) mee waar de cursor moet gezet worden. Als je vervolgens tekst schrijft dan wordt die weggeschreven vanaf dat punt. De coördinaten zijn x,y coördinaten, waarbij het punt (1,1) het eerste karakter linksboven in de console is. Volgende frame zet bijvoorbeeld een "X" 10 letters naar rechts, 20 lijnen naar beneden

else if (framenummer == 9)
{
    Console.SetCursorPosition(10, 20);
    Console.WriteLine("X");
}

Methoden gebruiken om een ascii-filmpje te maken

Volgende demonstratie toont de kracht van methoden. We zullen een steeds complexer geheel maken, dat dankzij methoden, nog steeds onderhoudbaar én leesbaar zal blijven. We zullen een bottom-up approach hanteren waarbij we eerst beginnen met de meest basis-functionaliteit die we nodig hebben en zo steeds een schil , in de vorm van een methode, er omheen coderen.

Ons doel is een method SpeelFilm te maken die een filmpje, bestaande uit opeen volgende frames en scenes, zal afspelen in de Console.

We zullen een uiterst boeiend filmpje maken waarin een mannetje naar z’n auto wandelt en er vervolgens in wegrijdt.

Ja, hier gaan prijzen mee gewonnen worden, ik weet het!

Karakter op scherm tekenen

We maken een methode die 1 karakter op het scherm kan plaatsen op een positie naar keuze. Omdat we willen voorkomen dat dit mislukt indien de coördinaten buiten het scherm vallen, zullen we in deze methode eerst controleren of de coördinaten geldig zijn.

static void DrawChar(char drawchar, int posX, int posY)
{
    if (posX >= 0 && posX < Console.WindowWidth)
    {
        if (posY >= 0 && posY < Console.WindowHeight)
        {
            Console.SetCursorPosition(posX, posY);
            Console.Write(drawchar);
        }
    }
}

Willen we deze methode gebruiken dan kunnen we bijvoorbeeld in de Main schrijven: DrawChar('@',5,10); Dit zal resulteren in het "@"-teken op het scherm op de positie 5, 10 (plaatsen naar rechts, 10 lijnen naar beneden).

Rechthoek tekenen

Een rechthoek tekenen kan nu heel eenvoudig met behulp van 2 for-lussen.Eén lus om van links naar rechts te gaan. Eén lus om van boven naar onder te gaan.

De methode aanvaardt naast het karakter dat we willen tekenen ook nog :

  1. De positie van de linkerbovenhoek van rechthoek op het scherm
  2. De lengte en de breedte van de rechthoek
static void DrawRectangle(char drawchar, int posX, int PosY, int width, int height)
{
    for (int i = posX; i < posX + width; i++)
    {
        for (int j = PosY; j < PosY + height; j++)
        {
            DrawChar(drawchar, i, j);
        }
    }
}

We kunnen dus deze methode aanroepen als volgt:DrawRectangle('*',4,6,3,6);

Gezicht tekenen

Het hek is van de dam. We kunnen nu allerlei complexere zaken tekenen bestaande uit combinaties van rechthoeken en karakters. Een gezicht kan bijvoorbeeld bestaan uit 1 grote rechthoek voor het gezicht. Met daarin 2 aparte ogen, voorgesteld door het karakter ‘0’ (dus mbv DrawChar) en een neus 'b'. Alsook een mond die bestaat uit een korte rechthoek bestaande uit underscores:

static void DrawFace(int posX, int posY)
{
    DrawRectangle('.', posX, posY, 7, 8);
    DrawChar('0', posX + 2, posY + 2);
    DrawChar('0', posX + 4, posY + 2);
    DrawChar('b', posX + 3, posY + 4);
    DrawRectangle('_', posX + 2, posY + 6, 4, 1);
}

De enige extra complexiteit is dat we steeds rekening moeten houden met de locatie van het gezicht (posx,posy) en de onderlinge posities van de gezichts-onderdelen. We plaatsen dus bijvoorbeeld de mond op 6e lijn ten opzichte van de bovenkant van het gezicht. Denk er ook aan dat we de volgorde van methode-aanroepen hier relevant is.Indien we de eerste DrawRectangle aanroep helemaal onderaan zouden plaatsen, dan zou deze al de andere gezichts-onderdelen overtekenen.

Auto tekenen

static void DrawCar(int posX, int posY)
{
    DrawRectangle('█', posX, posY + 5, 20, 4);
    DrawRectangle('|', posX + 17, posY, 1, 5);
    DrawChar('O', posX + 5, posY + 9);
    DrawChar('O', posX + 15, posY + 9);
}

Mannetje tekenen

Een mannetje tekenen bestaat dan uit een gezicht een lijf (rechthoek) en 2 benen (rechthoek). Z’n armen is hij kwijtgeraakt:

static void DrawMan(int posX, int posY)
{
    DrawFace(posX + 4, posY);
    DrawRectangle('+', posX + 3, posY + 14, 1, 5);
    DrawRectangle('+', posX + 9, posY + 14, 1, 5);
    DrawRectangle('#', posX + 2, posY + 8, 11, 7);
}

Of een mannetje dat spring?

static void DrawJumpingMan(int posX, int posY)
{
    DrawFace(posX + 4, posY);
    DrawRectangle('+', posX + 3, posY + 14, 1, 5);
    DrawRectangle('+', posX + 9, posY + 15, 5, 1);
    DrawRectangle('#', posX + 2, posY + 8, 11, 7);
}

Volgende code zal een animatie afspeelt van een mannetje dat springt en staat afwisselend:

int teller = 0;
while (true)
{
     
    if(teller%2==0)
        DrawMan(4,4);
    else
    {
        DrawJumpingMan(4,4);
    }
    teller++;
}

Man in auto

We kunnen weer een niveau hoge gaan: we combineren onze DrawMan en DrawCar methode om zo een mannetje in een auto te krijgen:

static void DrawManInCar(int posX, int posY)
{
    DrawMan(posX + 2, posY);
    DrawCar(posX, posY + 10);
}

Film maken

We kunnen nu een uber-boeiend filmpje maken waarin het mannetje naar de auto loopt en dan er in wegrijdt.We zullen dit stukje top-down benaderen. Eerst maken we een methode PlayMovie() die frame per frame het filmpje zal afspelen.Afhankelijk van het framenummer zal een andere scene getoond worden (lijnen 6,8):

private static void PlayMovie()
{
    for (int i = 0; i < 60; i++)
    {
        if (i < 35)
            WalkToCarScene(i);
        else if (i >= 35)
            RideAwayScene(i - 35);
 
        System.Threading.Thread.Sleep(100);
        Console.Clear();
    }
}

Merk op dat we i gebruiken als framenummer en zo weten wanneer welke scene moet afgespeeld worden. Voorts geven we het framenummer door naar de scene-methoden voor het geval ze deze nodig hebben om bijvoorbeeld de correcte positie te bepalen:

private static void RideAwayScene(int framenumber)
{
    DrawManInCar(40+framenumber*2,5);
}
 
private static void WalkToCarScene(int framenumber)
{
    if (framenumber % 2 == 0)
    {
        DrawJumpingMan(1 + framenumber, 5);
    }
    else
    {
        DrawMan(1 + framenumber, 5);
    }
 
 
    DrawCar(40, 15);
}

In deze opgave gaan we stap voor stap een "radio" opbouwen. We zullen daarbij werken met methoden, arrays en loops.

Muziek maken

Het wikipedia artikel over toonhoogte leert ons dat de grondtonen do-re-mi-fa-sol-la-si-do de frequenties : 264-297-330-352-396-440-495-528 hz behelzen (excuses indien dit ‘jargon’ niet klopt...ik ken even veel van muziek als van metsen).

De Console.Beep() methode laat ons toe om tonen te genereren op een bepaalde frequentie (in hz) en van een bepaalde duur (in milliseconden, i.e. 1/1000 seconde).

We kunnen dus de computer de toonladder afspelen, elke noot 1 seconde langs, als volgt:

Console.Beep(264, 1000);
Console.Beep(297, 1000);
Console.Beep(330, 1000);
Console.Beep(352, 1000);
Console.Beep(396, 1000);
Console.Beep(440, 1000);
Console.Beep(495, 1000);
Console.Beep(528, 1000);

Basis toon-methoden

Zorg ervoor dat je de toonladder van hierboven als volgt kunt aanroepen:

Do();
Re();
Mi();
Fa();
Sol()
La();
Si();
Do2();

Iedere methode zal dus de correcte toon afspelen gedurende 1s (je mag dit ook sneller instellen naar keuze).

Iedere "noot-methode" zal ook steeds op het scherm tonen welke noot wordt afgespeeld (doe dit als eerste in de methode) De uitvoer van voorgaande code wordt dan (het geluid moet je er maar even bij verzinnen):

Do
Re
Mi
Fa
Sol
La
Si
Do

Extra 1: plaats iedere noot in een andere tekstkleur. Extra 2: kan je er voor zorgen dat de noten achter elkaar, met komma, gescheiden op het scherm komen in plaats van onder elkaar?

Octaven

Door de frequentie van een toon te vermenigvuldigen of te delen met veelvouden van 2 krijg je de tonen op andere octaven. Pas de ‘noot-methoden’ aan zodat 2 parameters kunnen meegeven worden:

  1. De lengte in milliseconden dat de toon moet aangehouden worden
  2. De octaaf van de toon: 1 = basis octaaf die we al hadden, 2= 2e octaaf (dus frequentie x2) 3= 3e octaaf (frequentie x 4) etc.

Als je dus de tweede octaaf wil spelen (met iedere toon om de 500ms) moet je deze als volgt kunnen aanroepen:

Do(500,2);
Re(500,2);
Mi(500,2);
Fa(500,2);
Sol(500,2)
La(500,2);
Si(500,2);
Do2(500,2);

Liedjes methoden

Maak minstens 2 methoden naar keuze: iedere methode zal een liedje beginnen spelen dat je zelf hebt gemaakt (bv een bestaand kinderliedje). Hier bijvoorbeeld het begin van "You are my sunshine": Re(); Sol(); La(); Si(); Si(); Si(); La(); Si(); Sol(); Sol();

        Sol();
        La();
        Si();
        Do1();
        Mi();
        Mi();
        Re();
        Do1();
        Si();

De methoden als naam "Speel[NaamLiedje]", bijvoorbeeld SpeelYouAreMySunshine.

Extra: De Lyrics verschijnen op de juiste momenten op het scherm (dus vlak voor het afspelen van de bijhorende noot).

Radiostation

Bij het opstarten van het programma krijg je nu een menu te zien waaruit de gebruiker een liedje kan kiezen dat gespeeld moet worden. Vervolgens wordt dit liedje gespeeld en nadien wordt de vraag terug gesteld. Indien de gebruiker een onbekende keuze invoert dan zal een random liedje worden afgespeeld uit de mogelijke liedjes.

Songtime

Wanneer een liedje werd afgespeeld dan dient de methode terug te geven (als double) hoe lang het liedje heeft geduurd. Het hoofdmenu toont dit aan het einde van het afspelen:

Je kan de duur van een methode heel eenvoudig methoden als volgt, gebruikmakende van de StopWatch:

//Start
var stopwatch = System.Diagnostics.Stopwatch.StartNew();
//Voer te meten code uit

///...

//Stop
stopwatch.Stop();
double totaletijd = stopwatch.Elapsed.TotalSeconds;

totaleTijd zal de totaal verstreken tijd in seconden bevatten.

Playlist editor

Maak een playlist editor aan in je radio (als extra keuzemenu voeg je "Playlist Maker" toe). Wanneer de gebruiker dit kiest dan krijgt deze alle liedjes te zien. De gebruiker kan nu invoeren welke liedjes na elkaar moeten worden afgespeeld. Je bewaard deze keuze in een array.

In het hoofdmenu komt nu ook de mogelijkheid "Speel playlist" af. Wanneer deze keuze wordt genomen dan zullen de liedjes zoals ze in de playlist staan afgespeeld worden.

Nadien wordt getoond hoe lang de playlist heeft afgespeeld in totaal.

Commando recorder

In een aparte array houdt je een log bij van alle keuzes/invoer die de gebruiker tijdens het verloop van het programma heeft uitgevoerd. (bv 1,1,3, 2, 5, etc)

Via een nieuw menuitem "Herhaal acties" worden deze acties terug uitgevoerd zodat het lijkt alsof het programma automatisch werkt!

Het zal niet zo lijken, het zal zijn!

Een verhaalgenerator is een methode die hulp-methoden gebruikt om zo een "automatisch" verhaal te verzinnen. We hanteren een bottom-up approach om dit probleem aan te pakken.

Naamgenerator

We maken een methode die namen kan genereren. Deze naam gebruiken om het hoofdpersonage voor te stellen.

We maken een methode die steeds een naam generereert met een ingegeven lengte. Een naam bestaat in dit geval uit een reekds willekeurige karakters van a tot en met z.

Een eerste versie kan zijn:

static Random r=new Random();
static string GenerateNameSimple(int namelength)
{
    string name = "";
    for (int i = 0; i < namelength; i++)
    {
        name += (char)r.Next('Z', 'Z'+1); 
    }
    return name;
}

De eerste lijn is een klein trukje om ervoor te zorgen dat de variabele r overal in het programma aangeroepen kan worden en we dus niet in iedere methode deze opnieuw moeten aanmaken.

De bovenstaande methode roepen we aan als volgt:

string result = GenerateNameSimple(10);
Console.WriteLine(result);

Naamgenerator versie 2

Een nadeel van deze eerste versie is dat er soms onuitspreekbare namen gemaakt worden. Een betere methode zou zijn dat medeklinker en klinker afwisselend voorkomen (bv "adilo"). Hier gaan we eerst enkele hulpmethoden genereren:

IsKlinker

Een methode om te weten of een meegeven karakter (char) een klinker (``a,e,u,i en o") is.

Dit doen we snel met een switch:

static bool IsKlinker(char teken)
{
    switch (teken)
    {
        case 'E':
        case 'I':
        case 'O':
        case 'A':
        case 'U':
            return true;
        default:
            return false;

    }

}

GenereerKlinker

We kunnen nu een methode maken die enkel klinkers genereert:

static char GenereerKlinker()
{

    int waarde = r.Next(0, 5);
    switch (waarde)
    {
        case 0: return 'E';
        case 1: return 'O';
        case 2: return 'I';
        case 3: return 'U';
        case 4: return 'A';
    }
    return ' ';
}

We zouden in GenerateNameSimple een if kunnen plaatsen om te controleren of het gegenereerd karakter een klinker is. Dit heeft als nadeel dat we niet kunnen voorspellen hoe snel de methode zal werken: er is zelfs een eindige kans dat de methode nooit kalar zal zijn indien er telkens weer een medeklinker wordt geworpen.

GenereerMedeklinker

Deze methode heeft dezelfde werking als GenereerKlinker maar zal een switch hebben met daarin enkel de medeklinkers.

Een minder goede oplossing (die ook oneindig lang kan duren als we echt pech hebben) zou zijn:

static char GenereerMedeklinker()
{
    char result = 'E';
    while (IsKlinker(result))
    {
        result = (char)r.Next('A', 'Z'+1);
    }
    return result;
}

Betere Naamgenerator

Onze nieuwe naamgenerator werkt nu als volgt:

static string GenerateNameBetter(int namelength=6)
{

    string name = "";
    char vorigteken = (char)r.Next('Z', 'Z'+1);
    for (int i = 0; i < namelength; i++)
    {
        if (IsKlinker(vorigteken))
            vorigteken = GenereerMedeklinker();
        else vorigteken = GenereerKlinker();
        name += vorigteken;

    }
    return name;
}

Zinnen genereren

Een zin bestaat minstens uit

  • Een onderwerp
  • Een werkwoord
  • Een leiden voorwerp

Bijvoorbeeld: Tim gooit de bal.

We hebben reeds een methode om onderwerpen te genereren, ``GenerateNameBetter.

We hebben nu nog een methode voor werkwoorden en een voor leidende voorwerpen nodig.

Werkwoord genereren

Deze is een methode die als een woordenboek een lijst van bestaande werkwoorden (infinitieven) heeft:

static string GenereerWerkwoord()
{
    switch (r.Next(0, 10))
    {
        case 0: return "roept";
        case 1: return "gooit";
        case 2: return "aait";
        case 3: return "eet";
        case 4: return "pakt";
        case 5: return "kijkt naar";
        case 6: return "ledigt";
        case 7: return "vecht met";
        case 8: return "beklimt";
        case 9: return "begraaft";
        default:
            return "IETS ONBEKENDS";
    }
}

Voorwerp genereren

Dit is een zelfde soort methode als de vorige

static string GenereerVoorwerp()
{
    switch (r.Next(0, 10))
    {
        case 0: return "een bal";
        case 1: return "de hond";
        case 2: return "de kat";
        case 3: return "een lepel";
        case 4: return "het kind";
        case 5: return "het boek";
        case 6: return "de computer";
        case 7: return "een vork";
        case 8: return "het scherm";
        case 9: return "een dvd";
        default:
            return "IETS ONBEKENDS";
    }

Zin generator

We combineren nu deze 3 methoden om tot een zinnen generator te komen:

static string GenereerKorteZin()
{
    string onderwerp = GenerateNameBetter(6);
    string werkwoord = GenereerWerkwoord();
    string lv = GenereerVoorwerp();

    string zin = onderwerp + " " + werkwoord + " " + lv;

    return zin;
}

Verhaal generator

Het hek is nu van de dam. Alle puzzelstukjes liggen klaar.

Complexere zin

Een zin heeft vaak ook een bijzin die met een voegwoord (bv en) achter de hoofdzin wordt geplakt. Deze methode doet dit en kan even veel bijzinnen achter mekaar plakken als dat er als parameter wordt meegegeven.

static  string GenereerLangeZin(int bijzinlengte)
{
    string hoofdzin = GenereerKorteZin();
    for (int i = 0; i < bijzinlengte; i++)
    {
        hoofdzin += GenereerVoegwoord() + " " + GenereerKorteZin();
    }
    return hoofdzin;
}

Voegwerken genereren

Ook deze methode gebruikt onze gekende methodiek:

static  string GenereerVoegwoord()
{
    switch (r.Next(0, 6))
    {
        case 0: return " en ";
        case 1: return ", maar ";
        case 2: return ", echter ";
        case 3: return ", dus ";
        case 4: return ", of ";
        case 5: return ", doch";

        default:
            return "IETS ONBEKENDS";
    }
        }

Het totaal verhaal maken: dat doe je zelf

Maak nu een methode GenereerVerhaal die een willekeurig aantal lange zinnen genereert.

Kan je volgende zaken toevoegen?

  • De GenereerLangeZin methode heeft als probleem dat alle bijzinnen steeds met een ander ondwerp werken. Het zou beter zijn als het onderwerp uit de hoofdzin ook in de bijzinnen dezelfde is. Pas daarom GenereerKorteZin aan zodat deze methode een optionele parameter aanvaardt, namelijk het te gebruiken onderwerp. Indien de method geen parameter meekrijgt (==null) dan zal de methode een nieuw onderwerp genereren.
  • Het zelfde voor de verhaal generator: zorg ervoor dat ook de GenereerLangeZin een onderwerp als parameter aanvaardt.
  • Kan je hier een daar adjectieven toevoegen aan je zin. Een adjactief is een extra 'beschrijving' van het onderwerp of leidende voorwerp. Bijvoorbeeld "groene bal" of "stomme Eric".

The Array MazeGame

Inleiding

In dit all-in-one tonen we hoe je, stap voor stap, kan komen tot een speelbaar, eenvoudige tekst-gebaseerd spel. We hanteren hierbij de principes van "refactoring": we gaan onze code steeds verbeteren op gebied van leesbaarheid en onderhoudbaarheid. Bij iedere stap zullen we dan ook extra functionaliteit toevoegen.

Het doel is te komen tot een spel waarbij de gebruiker kan wandelen door een kaart. De kaart zelf is dynamisch, bepaalde ruimtes zijn pas toegankelijk wanneer aan bepaalde voorwaarden is voldaan.

Vereiste kennis

Deze tutorial gaat er van uit dat je volgende zaken beheerst:

  • Basisprincipes van Arrays, zowel 1D als 2D arrays: aanmaken, waarden toevoegen/uitlezen
  • Werken met de Console-bibliotheek: in het bijzonder Clear(), SetCursorPosition(), ForeGroundColor en BackGroundColor, Write() vs WriteLine()
  • Je kan werken met while en for-loops
  • Je begrijpt de werking van het out keyword

Fase 1: Een saai spel

We gebruiken een array van strings om de opeenvolgende kamers te beschrijven. Door middel van een for-loop doorlopen we de array en tonen we iedere beschrijving van de kamer op het scherm.

Telkens de gebruiker op enter drukt verschijnt de volgende kamer.

Merk op dat de array-lengte geen invloed heeft op de forloop. We kunnen dus eenvoudig kamers toevoegen zonder dat dit invloed heeft op de werking van het programma. We zullen blijven behouden doorheen het hele programma (de speciale kaart uitgezonder in fase 8).

string[] Kamers = new string[]
    {
        "Je staat voor de ingang.",
        "Je bent in de hal.",
        "Je bent in het computerlabo",

    };

for (int i = 0; i < Kamers.Length; i++)
{
    Console.WriteLine(Kamers[i]);
    Console.ReadLine();
}

Fase 2: Een interactief saai spel

We bieden de mogelijkheid aan aan de gebruiker om zelf te kiezen naar welke kamer er wordt gegaan. De gebruiker kan dus "vooruit’" of "achteruit" gaan in de array. We houden hiervoor een variabele (huidigekamer) bij die bijhoudt waar de gebruiker zich momenteel bevindt.

Telkens de gebruiker zich wil verplaatsen controleren we of deze verplaatsen toegestaan is. De Huidigekamer variabele is dus automatisch ook de index van de te tonen kamer in de string-array.

string[] Kamers = new string[]
    {
        "Je staat voor de ingang.",
        "Je bent in de hal.",
        "Je bent in het computerlabo",

    };

int huidigekamer = 0;
string keuze = "";
while (keuze != "q")
{
    Console.WriteLine(Kamers[huidigekamer]);

    Console.WriteLine("Vooruit= V, ACHTERUIT = A, q= quit");
    keuze = Console.ReadLine();
    if (keuze == "V" && huidigekamer != Kamers.Length - 1)
        huidigekamer++;
    else if (keuze == "A" && huidigekamer != 0)
        huidigekamer--;
    else if (keuze == "q")
        Console.WriteLine("Byebye");
    else
    {
        Console.WriteLine("Foute invoer");
    }
}

Fase 3: Een 2D-wereld met lookup-table

Stap 1: Kaart maken

Vervolgens willen we de mogelijkheid om een 2D wereld aan te bieden. Hierbij gebruiken we een zogenaamde lookup-table zodat we onze wereld array eenvoudig kunnen houden én kamers kunnen herbruiken.

Eerste definiëren we de verschillende kamers die er bestaan:

string[] Kamers =
    {
        "Onbekend terrein", //0
        "In een gang", //1
        "In de lobby", //2
        "In de bar", //3
        "In de keuken", //4
        "Achtertuin"//5
    };

Vervolgens maken we 2D-array die onze kaart voorstelt. De array is van het type int. Iedere cijfer in de array zal de index bevatten van de kamer die op die plek moet komen. Dit is dus een zogenaamde look-up-table of Lut (meer info: wiki):

            int[,] Kaart =
                {
                    {1, 2, 1, 3},
                    {0, 1, 0, 1},
                    {0, 4, 0, 5}
                };

Linksboven beginnen we dus met een Gang, met rechts ervan een lobby, etc.

Plaatsen die we met een 0 (onbekend terrein) definiëren gaan we beschouwen als plaatsen waar de gebruiker niet mag komen.

Merk op dat we dus onze wereld zo groot of zo klein kunnen maken als we zelf wensen.

Stap 2: Wandelen op de kaart

Daar we ons nu op een 2D-kaart bevinden hebben we 2 variabelen nodig om onze huidige positie te onthouden:

int posX = 0;
int posY = 0;

We spreken af dat de locatie (0,0) zicht linksboven in de array bevindt.

We maken een oneindige loop die steeds de volgende stappen zal doen:

  1. Huidige kamertekst op het scherm tonen
  2. Aan de gebruiker vragen naar waar hij wil wandelen
  3. Positie van gebruiker veranderen
  4. Terug naar 1.

Eerst gebruiken we dus de lut om de huidige kamer beschrijving te tonen. We gebruiker de huidige spelerlocatie als index’s in de Kaart-array en vragen zo de kamerindex op. Die kamerindex gebruiken we om de tekst uit de Kamers-array te tonen.

while (true)
{
    int kamerindex = Kaart[posX, posY];
    Console.WriteLine(Kamers[kamerindex]);

De gebruiker kan zich naar het noorden, oosten, zuiden of westen begeven (respectievelijk naar boven, links, onder, rechts op de kaart). We vragen dus telkens de gebruiker naar waar hij:

Console.WriteLine("NOZW? Naar waar wil je?");
char inp = Convert.ToChar(Console.ReadLine().ToUpper());

Stap 3: Positie aanpassen

We verwerken de richting in een switch:

switch (inp)
{

Naargelang de richting die de gebruiker ingeeft moeten we dus telkens 2 zaken contoleren:

  1. Bevindt de gebruiker zich momenteel (VOOR we z’n locatie aanpassen) aan de rand van de array (0 of Length-1)
  2. Probeert de gebruiker zich naar verboden vakje te begeven (een onbekend terrein vakje)

Indien aan deze 2 voorwaarden niet is voldaan dan mogen we de huidige locatie van de gebruiker zonder problemen veranderen. Dit behelst dus dat we , naargelang de richting, de posX en posY waarden veranderen, namelijk:

  • Noorden: posX met 1 verlagen
  • Zuiden: posX met 1 verhogen
  • Oosten: posY met 1 verhogen
  • Westen: posY met 1 verlagen

We krijgen in de switch dus:

    case 'N':
        if (posX != 0 && Kaart[posX - 1, posY] != 0)
            posX--;
        else Console.WriteLine("Kan niet");
        break;
    case 'O':
        if (posY != Kaart.Length(1)-1 && Kaart[posX, posY + 1] != 0)
            posY++;
        else Console.WriteLine("Kan niet");
        break;
    case 'Z':
        if (posX != Kaart.GetLength(0)-1 && Kaart[posX + 1, posY] != 0)
            posX++;
        else Console.WriteLine("Kan niet");
        break;
    case 'W':
        if (posY != 0 && Kaart[posX, posY - 1] != 0)
            posY--;
        else Console.WriteLine("Kan niet");
        break;
}

Stap 4: Volledige code

De volledige code van deze fase is dus geworden:

int[,] Kaart =
    {
        {1, 2, 1, 3},
        {0, 1, 0, 1},
        {0, 4, 0, 5}
    };

string[] Kamers =
    {
        "Onbekend terrein", //0
        "In een gang", //1
        "In de lobby", //2
        "In de bar", //3
        "In de keuken", //4
        "Achtertuin"//5
    };

int posX = 0;
int posY = 0;

while (true)
{
    int kamerindex = Kaart[posX, posY];
    Console.WriteLine(Kamers[kamerindex]);

    Console.WriteLine("NOZW? Naar waar wil je?");

    char inp = Convert.ToChar(Console.ReadLine().ToUpper());
    switch (inp)
    {
        case 'N':
            if (posX != 0 && Kaart[posX - 1, posY] != 0)
                posX--;
            else Console.WriteLine("Kan niet");
            break;
        case 'O':
            if (posY != Kaart.GetLength(1)-1 && Kaart[posX, posY + 1] != 0)
                posY++;
            else Console.WriteLine("Kan niet");
            break;
        case 'Z':
            if (posX != Kaart.GetLength(0)-1 && Kaart[posX + 1, posY] != 0)
                posX++;
            else Console.WriteLine("Kan niet");
            break;
        case 'W':
            if (posY != 0 && Kaart[posX, posY - 1] != 0)
                posY--;
            else Console.WriteLine("Kan niet");
            break;
    }
}

Fase 4: Tekenen van de kaart op het scherm

We wensen een visuele indicatie van de kaart te tonen aan de gebruiker (zonder dat hij ziet wat voor kamer het is). We voegen daarom een methode DrawMap() toe die de kaart iedere keer opnieuw zal tekenen. Deze methode gaat ook de positie van de gebruiker duidelijk maken a.d.h.v. een "X" op de kaart. Onze game-loop veranderen we dus naar:

while (true)
{
    Console.Clear();
    DrawMap(Kaart, posX, posY);

    int kamerindex = Kaart[posX, posY];
    Console.WriteLine(Kamers[kamerindex]);

    Console.WriteLine("NOZW? Naar waar wil je?")

De DrawMap() methode toont dus de huidige locatie als een "X". Voorts willen we dat enkel bereikbare kamers getoond worden (we gebruiken een "O" hiervoor). Elementen op de kaart die wijzen naar index 0 ("Onbekend terrein") worden niet getoond.

We doorlopen in de DrawMap() methode de volledige kaart. Lijn per lijn. Hiervoor gebruiken we 2 geneste for-loops. De outer-loop (index i) zal de X-coördinaat aflopen, oftewel lijn per lijn. De inner loop (index j) zal de Y-coördinaat aflopen, oftewel kolom per kolom:

private static void DrawMap(int[,] Kaart, int posX, int posY)
{
    for (int i = 0; i < Kaart.GetLength(0); i++)
    {
        for (int j = 0; j < Kaart.GetLength(1); j++)
        {

Merk op dat ook deze methode geen hardcoded array-grenzen bevat. We kunnen dus eender welke kaart aan deze methode aanbieden.

Binnen de inner-for gaan we nu element per element van 1 rij op het scherm tonen. Eerst controleren we of de speler zich bevindt in het element dat we op het punt staan te tekenen. Als dat zo is dan plaatsen we een "X":

if (posX == i & posY == j)
    Console.Write("X");

Anders plaatsen we een "o" indien het gaat om gebied waar de speler toegelaten is:

else if (Kaart[i, j] != 0)
    Console.Write("o");

Niet toegelaten gebied tonen we niet, we zetten dus een spatie in de plaats:

else
    Console.Write(" ");

Na iedere inner-loop moeten we vervolgens een newline toevoegen, anders worden alle rijen van de kaart naast elkaar gezet. Finaal krijgen we dus:

    }
    Console.Write('\n');
}

Dit resulteert in volgende finale code voor deze fase:

static void Main()
{
    int[,] Kaart ={ {1, 2, 1, 3},{0, 1, 0, 1},{0, 4, 0, 5}};

    string[] Kamers ={"Onbekend terrein",  "In een gang", "In de lobby", "In de bar", "In de keuken", "Achtertuin"};

    int posX = 0;
    int posY = 0;

    while (true)
    {
        Console.Clear();
        DrawMap(Kaart, posX, posY);

        int kamerindex = Kaart[posX, posY];
        Console.WriteLine(Kamers[kamerindex]);
        Console.WriteLine("NOZW? Naar waar wil je?");

        char inp = Convert.ToChar(Console.ReadLine().ToUpper());
        switch (inp)
        {
            case 'N':
                if (posX != 0 && Kaart[posX - 1, posY] != 0)
                    posX--;
                else Console.WriteLine("Kan niet");
                break;
            case 'O':
                if (posY != Kaart.GetLength(1)-1 && Kaart[posX, posY + 1] != 0)
                    posY++;
                else Console.WriteLine("Kan niet");
                break;
            case 'Z':
                if (posX != Kaart.GetLength(0)-1 && Kaart[posX + 1, posY] != 0)
                    posX++;
                else Console.WriteLine("Kan niet");
                break;
            case 'W':
                if (posY != 0 && Kaart[posX, posY - 1] != 0)
                    posY--;
                else Console.WriteLine("Kan niet");
                break;
        }
    }
}
private static void DrawMap(int[,] Kaart, int posX, int posY)
{
    for (int i = 0; i <= Kaart.GetLength(0); i++)
    {
        for (int j = 0; j <= Kaart.GetLength(1); j++)
        {
            if (posX == i & posY == j)
                Console.Write("X");
            else if (Kaart[i, j] != 0)
                Console.Write("o");
            else
                Console.Write(" ");
        }
        Console.Write('\n');
    }
}

Fase 5: Kaart vergroten

Zoals reeds aangehaald staat niets je in de weg om je spel-wereld groter te maken. Hiervoor hoef je enkel (momenteel) de Kamers en Kaart arrays aan te passen. Alle code zal blijven werken.

Bijvoorbeeld:

int[,] Kaart =
    {
        {1, 2, 1, 3, 0, 0},
        {0, 1, 0, 1, 0, 0},
        {0, 4, 0, 1, 0, 0},
        {0, 1, 0, 1, 0, 0},
        {0, 7, 0, 6, 1, 8},
    };

string[] Kamers =
    {
        "Onbekend terrein", //0 
        "In een gang", //1
        "In de lobby", //2
        "In de bar", //3
        "In de keuken", //4
        "Achtertuin",//5
        "In de securityroom", //6
        "In de personeelsruimte", //7
        "In de folterkamer" //8
    };

Fase 6: Een extra look-up-table voor meer wereld-details

Het principe van een lut verschilt eigenlijk weinig van een eenvoudige database. We zouden dus meerdere look-up-tables (tabellen) kunnen definiëren en deze gebruiken om meer informatie in onze spelwereld te plaatsen.

We kunnen bijvoorbeeld per kamer ook een beschrijving tonen van die kamer. Daar we nog niets kennen van struct en class (zogenaamde datastructuren) moeten we ons dus behelpen als volgt: we definiëren een nieuwe array Beschrijving waarbij ieder element de index heeft van de respectievelijke kamer:


int[,] Kaart =
    {
        {1, 2, 1, 3, 0, 0},
        {0, 1, 0, 1, 0, 0},
        {0, 4, 0, 1, 0, 0},
        {0, 1, 0, 1, 0, 0},
        {0, 7, 0, 6, 1, 8},

    };

string[] Kamers =
    {
        "Onbekend terrein", //0 
        "In een gang", //1
        "In de lobby", //2
        "In de bar", //3
        "In de keuken", //4
        "Achtertuin",//5
        "In de securityroom", //6
        "In de personeelsruimte", //7
        "In de folterkamer" //8

    };

string[] Beschrijving =
                    {
        "", //0 
        "Een ordinaire saaie gang met een mooie vloer", //1
        "De receptioniste kijkt je verbaast aan. Een plant in de hoek is het enige groen in de purperen ruimte.", //2
        "2 gasten zitten half beschonken aan de toog. Een verliefd koppel is zachtjes aan het praten", //3
        "Overal liggen etensresten, maar verder is hier niets of niemand interessant.", //4
        "Mooie plantjes, enkele bomen en een gezellig terras.",//5
        "De veiligheidsagent houdt je nauwlettend in het oog", //6
        "Overal staan kastjes.Hier en daar is er een personeelslid zich aan het omkleden", //7
        "Wat doet deze vreemde plek in het hotel." //8
    };

Door 1 extra lijntje (+ eentje voor een visuele scheiding tussen beschrijving en kamertitel) plaatsen we nu steeds de kamerbeschrijving onder de kamertype:

Console.WriteLine(Kamers[kamerindex]);
Console.WriteLine("******");
Console.WriteLine(Beschrijving[kamerindex]);

Fase 7: Dynamische kaart

Na iedere actie van de speler verwerken we steeds weer de kaart in zowel de DrawMap()-methode als tijdens het verwerken van de speler-input. We kunnen dus eenvoudig een dynamische kaart maken die zich aanpast naargelang bepaalde acties.

Je met de kennis die we zo meteen tonen bijvoorbeeld aan de start van het programma met een lege kaart: naargelang de speler zich verplaatst in de wereld zal de kaart aangevuld worden. (tip: gebruik hiervoor een array VolledigeKaart en een array ReedsOntdekteKaart of iets dergelijks. De speler krijgt steeds de ReedsOntdekteKaart te zien in DrawMap(). Naargelang acties van de speler kopieer je dan bepaalde elementen uit VolledigeKaart naar ReedsOntdekteKaart).

We definiëren onze kaart (merk op dat we de folterkamer en geheime gang verwijderen rechts onderaan):

int[,] Kaart =
    {
        {1, 2, 1, 3, 0, 0},
        {0, 1, 0, 1, 0, 0},
        {0, 4, 0, 1, 0, 0},
        {0, 1, 0, 1, 0, 0},
        {0, 7, 0, 6, 0, 0},
    };

We willen volgende functionaliteit inbouwen: >indien de gebruiker in de kamer met index 6 ("SecurityRoom") een bepaalde actie onderneemt dan zal een geheime gang en kamer (folterkamer) op de kaart bij verschijnen, rechts van de securityroom.

De actie gaan we nu even eenvoudig beschouwen als volgt: de gebruiker kan in alle kamers "G" als opdracht doorgeven. Echter, enkel wanneer de gebruiker zich in de kamer met index 6 bevind dan zal de geheime kamer zichtbaar worden. We voegen daarom een extra case toe aan onze switch:

case 'G':
    if (kamerindex != 6)
    {
        Console.WriteLine("Dat zal hier niet werken");
    }
    else
    {

Als de speler wél in de securityroom is dan gaan we de kaart-array aanpassen. We voegen rechtsonder in de array de 2 nieuwe kamers toe:

Console.WriteLine("Je ontdekt een geheime ruimte");
Kaart[4, 4] = 1;
Kaart[4, 5] = 8;

Wanneer we nu de kaart hertekenen dan deze nieuwe ruimte verschijnen en weet de gebruiker dat hij zich daar kan begeven.

De volledige code wordt dan (we laten de DrawMap()-methode even achterwege):

static void Main()
{
    int[,] Kaart =
        {
            {1, 2, 1, 3, 0, 0},{0, 1, 0, 1, 0, 0},{0, 4, 0, 1, 0, 0},{0, 1, 0, 1, 0, 0},{0, 7, 0, 6, 0, 0},
        };

    string[] Kamers =
        {
            "Onbekend terrein", //0 
            "In een gang", //1
            "In de lobby", //2
            "In de bar", //3
            "In de keuken", //4
            "Achtertuin",//5
            "In de securityroom", //6
            "In de personeelsruimte", //7
            "In de folterkamer" //8
        };

    string[] Beschrijving =
                        {
            "", //0 
            "Een ordinaire saaie gang met een mooie vloer", //1
            "De receptioniste kijkt je verbaast aan. Een plant in de hoek is het enige groen in de purperen ruimte.", //2
            "2 gasten zitten half beschonken aan de toog. Een verliefd koppel is zachtjes aan het praten", //3
            "Overal liggen etensresten, maar verder is hier niets of niemand interessant.", //4
            "Mooie plantjes, enkele bomen en een gezellig terras.",//5
            "De veiligheidsagent houdt je nauwlettend in het oog. Typ \"G\" om een geheime ruimte te ontdekken", //6
            "Overal staan kastjes.Hier en daar is er een personeelslid zich aan het omkleden.", //7
            "Wat doet deze vreemde plek in het hotel." //8

        };

    int posX = 0;
    int posY = 0;
    char inp='k';
    while (inp != 'Q')
    {
        Console.Clear();
        DrawMap(Kaart, posX, posY);

        int kamerindex = Kaart[posX, posY];
        Console.WriteLine(Kamers[kamerindex]);
        Console.WriteLine("******");
        Console.WriteLine(Beschrijving[kamerindex]);
        Console.WriteLine();
        Console.WriteLine("NOZW? Naar waar wil je?");

        inp = Convert.ToChar(Console.ReadLine().ToUpper());
        switch (inp)
        {
            case 'N':
                if (posX != 0 && Kaart[posX - 1, posY] != 0)
                    posX--;
                else Console.WriteLine("Kan niet");
                break;
            case 'O':
                if (posY != Kaart.GetLength(1)-1 && Kaart[posX, posY + 1] != 0)
                    posY++;
                else Console.WriteLine("Kan niet");
                break;
            case 'Z':
                if (posX != Kaart.GetLength(0)-1 && Kaart[posX + 1, posY] != 0)
                    posX++;
                else Console.WriteLine("Kan niet");
                break;
            case 'W':
                if (posY != 0 && Kaart[posX, posY - 1] != 0)
                    posY--;
                else Console.WriteLine("Kan niet");
                break;
            case 'G':
                if (kamerindex != 6)
                {
                    Console.WriteLine("Dat zal hier niet werken");
                }
                else
                {
                    Console.WriteLine("Je ontdekt een geheime ruimte");
                    Kaart[4, 4] = 1;
                    Kaart[4, 5] = 8;
                    Console.ReadLine();
                }
                break;

        }
    }
}

Fase 8: Go nuts

Vanaf dit punt kun je nu al een relatief eenvoudig, toch leuk spel maken, op voorwaarde dat je verhaal goed zit. Echter, voor we je hierop loslaten gaan we nog enkele zaken refactoren zodat de code wat leesbaarder blijft. In hoofzaak willen we bepaalde stukken code uit de main-body halen en naar aparte methodes extraheren.

Stap 1: Kaart initialiseren in aparte methode

Beeld je in dat je de kaart(en) voor je spel uit een bestand laadt. Op zich is dat niet zo moeilijk , maar het vereist natuurlijk extra lijnen code in je, reeds overbevolkte, Main-methode. We verhuizen daarom de code waarin we onze kaarten initialiseren naar een aparte methode. In onze Main schrijven we dan (merk het gebruik van het out keyword op!):

int[,] Kaart;
string[] Kamers;
string[] Beschrijving;

InitialiseerSpel(out Kaart, out Kamers, out Beschrijving);

Deze methode bevat dan gewoon de code van daarnet, mooi verpakt en afgeschermd:

private static void InitialiseerSpel(
    out int[,] Kaart, 
    out string[] Kamers, 
    out string[] Beschrijving)
{
    Kaart = new int[,]
        {
        {1, 2, 1, 3, 0, 0}, 
        {0, 1, 0, 1, 0, 0}, 
        {0, 4, 0, 1, 0, 0}, 
        {0, 1, 0, 1, 0, 0}, 
        {0, 7, 0, 6, 0, 0}
        };
    //Idem voor Kamers en beschrijving
    //..

Stap 2: Input verwerken in aparte methode

Het verwerken van de userinput kunnen we ook makkelijk extraheren naar aparte methode zodat onze while-loop overzichtelijker wordt :

while (true)
{
    Console.Clear();
    DrawMap(Kaart, posX, posY);

    int kamerindex = Kaart[posX, posY];
    Console.WriteLine(Kamers[kamerindex]);

    Console.WriteLine("******");
    Console.WriteLine(Beschrijving[kamerindex]);
    Console.WriteLine();
    Console.WriteLine("NOZW? Naar waar wil je?");

    VerwerkInput(Kaart, kamerindex, ref posX, ref posY); //NEW
}

Merk op dat we zelfs de volledige loop naar een aparte methode op zijn beurt kunnen extraheren. Maar dat laten we aan de lezer over. We dienen de posities van de speler by reference mee te geven, daar we de posities onmiddellijk willen updaten in de VerwerkInput()-methode.

private static void VerwerkInput(
    int[,] Kaart, 
    int kamerindex, 
    ref int posX, 
    ref int posY)
{
    char inp = Convert.ToChar(Console.ReadLine().ToUpper());
    switch (inp)
    {
            //etc

Stap 3: Een mooier kaartje tekenen

Als kers op de taart tonen we snel hoe je het kaartje sexier kan tonen op het scherm. Hier zijn echter een paar belangrijke opmerkingen aan de orde:

  • De code bevat enkele hardcoded waarden zoals het plaatsen van de cursor m.b.v. Console.SetCursorPosition. Beter zou zijn als deze waarden als Magic numbers worden behandeld of on-the-fly worden berekend.
  • De kaart bevat Unicode-art met vaste grootte. Dit zal bugs geven indien onze kaart-array groter is dan de dimensies van de Unicode-art: de art zal over de randen van de Unicode-art getekend worden. We kunnen dit oplossen door delen van de Unicode-art te berekenen (bv het aantal lege lijnen en de breedte van een pagina.

We definiëren de nieuwe Methode en voegen als eerste actie Unicode-art toe van een kaart:

private static void DrawMapCool(int[,] Kaart, int posX, int posY)
{
    string background =
                        @".-/|~~~~~~~MAP~~~~~~~~~\/~~~~~~~~**~~~~~~~~|\-." + "\n" +
                        @"||||                    :                    ||||" + "\n" +
                        @"||||                    :                    ||||" + "\n" +
                        @"||||                    :                    ||||" + "\n" +
                        @"||||                    :                    ||||" + "\n" +
                        @"||||                    :                    ||||" + "\n" +
                        @"||||                    :                    ||||" + "\n" +
                        @"||||                    :                    ||||" + "\n" +
                        @"||||                    :                    ||||" + "\n" +
                        @"||||___________________ : ___________________||||" + "\n" +
                        @"||/====================\:/====================\||" + "\n" +
                        @"`---------------------~---~--------------------''";

Daar we gaan spelen met de kleuren is het aan te raden om steeds volgende acties te ondernemen indien we de kleur van een bepaald karakter of zinnen willen veranderen:

  1. De huidige kleur van de console bewaren (fore en/of background) in een tijdelijke variabele
  2. Kleur veranderen
  3. Karakter of zin op scherm plaatsen
  4. Kleur terug naar de huidige kleur aanpassen. We tonen dit in de volgende code waarin we de background array (die de Unicode-art bevat) op het scherm willen tekenen. Daarbij willen we dat de karakters donker-cyaan zijn en dat enkel karakters die geen spatie of liggenstreepje zijn een donkergele achtergrond hebben. De commentaar toont de zonet beschreven stappen:
ConsoleColor oll = Console.ForegroundColor; //1
Console.ForegroundColor = ConsoleColor.DarkCyan; //2
for (int i = 0; i < background.Length; i++)
{
    if (background[i] != ' ' && background[i] != '_')
        Console.Write(background[i]); //3
    else
    {
        ConsoleColor bll = Console.BackgroundColor;//1
        Console.BackgroundColor = ConsoleColor.DarkYellow;//2
        Console.Write(background[i]);//3
        Console.BackgroundColor = bll;//4
    }
}

Console.ForegroundColor = oll;//4

Vervolgens gebruiken we SetCursorPosition om onze spelerskaart ‘over’ de Unicode-art te tekenen. Hierbij voegen we nog wat extra kleurtje toe, de speler-X wordt rood gekleurd:

ConsoleColor bll2 = Console.BackgroundColor;
Console.BackgroundColor = ConsoleColor.DarkYellow;

for (int i = 0; i <= Kaart.GetLength(0); i++)
{
    Console.SetCursorPosition(7, 3 + i);
    for (int j = 0; j <= Kaart.GetLength(1); j++)
    {
        if (posX == i & posY == j)
        {
            ConsoleColor l = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Red;
            Console.Write("X");
            Console.ForegroundColor = l;
        }

        else
            if (Kaart[i, j] != 0)
                Console.Write("o");
            else
                Console.Write(" ");
    }
    Console.Write('\n');

}
Console.SetCursorPosition(1, 15);
Console.BackgroundColor = bll2;
}

De while-loop in de Main()-methode passen we nu nog aan zodat:

  1. We de nieuwe DrawCoolMap methode gebruiken
  2. De titel van de kamer steeds op de rechterpagina van de kaart Unicode-art wordt getoond
  3. De beschrijving en andere tekst steeds onder map komt en niet erover
while (true)
{
    Console.Clear();
    DrawMapCool(Kaart, posX, posY); //a

    int kamerindex = Kaart[posX, posY];
    Console.SetCursorPosition(26, 6); //b
    Console.WriteLine(Kamers[kamerindex]);

    Console.SetCursorPosition(1, 16);//c
    Console.WriteLine("******");
    Console.WriteLine(Beschrijving[kamerindex]);
    Console.WriteLine();
    Console.WriteLine("NOZW? Naar waar wil je?");

    VerwerkInput(Kaart, kamerindex, ref posX, ref posY);
}

Alle code samen

We zijn er! De volledige code voor dit extra-ordinaire spel wordt dan:

Main-methode

static void Main()
{
    int[,] Kaart;
    string[] Kamers;
    string[] Beschrijving;

    InitialiseerSpel(out Kaart, out Kamers, out Beschrijving);

    int posX = 0;
    int posY = 0;

    while (true)
    {
        Console.Clear();
        DrawMapCool(Kaart, posX, posY); //a

        int kamerindex = Kaart[posX, posY];
        Console.SetCursorPosition(26, 6); //b
        Console.WriteLine(Kamers[kamerindex]);

        Console.SetCursorPosition(1, 16);//c
        Console.WriteLine("******");
        Console.WriteLine(Beschrijving[kamerindex]);
        Console.WriteLine();
        Console.WriteLine("NOZW? Naar waar wil je?");

        VerwerkInput(Kaart, kamerindex, ref posX, ref posY);
    }
}

InitialiseerSpel-methode

private static void InitialiseerSpel(
    out int[,] Kaart, 
    out string[] Kamers, 
    out string[] Beschrijving)
{
    Kaart = new int[,]
        {
        {1, 2, 1, 3, 0, 0}, 
        {0, 1, 0, 1, 0, 0}, 
        {0, 4, 0, 1, 0, 0}, 
        {0, 1, 0, 1, 0, 0}, 
        {0, 7, 0, 6, 0, 0}
        };

    Kamers = new string[]
        {
            "Onbekend terrein", "In een gang", "In de lobby", "In de bar", "In de keuken", "Achtertuin",
            "In de securityroom", "In de personeelsruimte", "In de folterkamer"
        };

    Beschrijving = new string[]
        {
            "", "Een ordinaire saaie gang met een mooie vloer",
            "De receptioniste kijkt je verbaast aan. Een plant in de hoek is het enige groen in de purperen ruimte.",
            "2 gasten zitten half beschonken aan de toog. Een verliefd koppel is zachtjes aan het praten",
            "Overal liggen etensresten, maar verder is hier niets of niemand interessant.",
            "Mooie plantjes, enkele bomen en een gezellig terras.",
            "De veiligheidsagent houdt je nauwlettend in het oog. Typ \"G\" om een geheime ruimte te ontdekken",
            "Overal staan kastjes.Hier en daar is er een personeelslid zich aan het omkleden.",
            "Wat doet deze vreemde plek in het hotel."
        };
}

VerwerkInput-methode

private static void VerwerkInput(
    int[,] Kaart, 
    int kamerindex, 
    ref int posX, 
    ref int posY)
{
    char inp = Convert.ToChar(Console.ReadLine().ToUpper());
    switch (inp)
    {
            //etc
        case 'N':
            if (posX != 0 && Kaart[posX - 1, posY] != 0)
                posX--;
            else Console.WriteLine("Kan niet");
            break;
        case 'O':
            if (posY != Kaart.GetLength(1) && Kaart[posX, posY + 1] != 0)
                posY++;
            else Console.WriteLine("Kan niet");
            break;
        case 'Z':
            if (posX != Kaart.GetLength(0) && Kaart[posX + 1, posY] != 0)
                posX++;
            else Console.WriteLine("Kan niet");
            break;
        case 'W':
            if (posY != 0 && Kaart[posX, posY - 1] != 0)
                posY--;
            else Console.WriteLine("Kan niet");
            break;
        case 'G':
            if (kamerindex == 6)
            {
                Console.WriteLine("Je ontdekt een geheime ruimte");
                Kaart[4, 4] = 1;
                Kaart[4, 5] = 8;
                Console.ReadLine();
            }
            else
            {
                Console.WriteLine("Dat zal hier niet werken");
            }
            break;
    }
}

DrawMapCool-methode

private static void DrawMapCool(int[,] Kaart, int posX, int posY)
{
    string background =
                        @".-/|~~~~~~~MAP~~~~~~~~~\/~~~~~~~~**~~~~~~~~|\-." + "\n" +
                        @"||||                    :                    ||||" + "\n" +
                        @"||||                    :                    ||||" + "\n" +
                        @"||||                    :                    ||||" + "\n" +
                        @"||||                    :                    ||||" + "\n" +
                        @"||||                    :                    ||||" + "\n" +
                        @"||||                    :                    ||||" + "\n" +
                        @"||||                    :                    ||||" + "\n" +
                        @"||||                    :                    ||||" + "\n" +
                        @"||||___________________ : ___________________||||" + "\n" +
                        @"||/====================\:/====================\||" + "\n" +
                        @"`---------------------~---~--------------------''";

    ConsoleColor oll = Console.ForegroundColor; //1
    Console.ForegroundColor = ConsoleColor.DarkCyan; //2
    for (int i = 0; i < background.Length; i++)
    {
        if (background[i] != ' ' && background[i] != '_')
            Console.Write(background[i]); //3
        else
        {
            ConsoleColor bll = Console.BackgroundColor;//1
            Console.BackgroundColor = ConsoleColor.DarkYellow;//2
            Console.Write(background[i]);//3
            Console.BackgroundColor = bll;//4

        }
    }

    Console.ForegroundColor = oll;//4

    ConsoleColor bll2 = Console.BackgroundColor;
    Console.BackgroundColor = ConsoleColor.DarkYellow;

    for (int i = 0; i < Kaart.GetLength(0); i++)
    {
        Console.SetCursorPosition(7, 3 + i);
        for (int j = 0; j < Kaart.GetLength(1); j++)
        {
            if (posX == i & posY == j)
            {
                ConsoleColor l = Console.ForegroundColor;
                Console.ForegroundColor = ConsoleColor.Red;
                Console.Write("X");
                Console.ForegroundColor = l;

            }

            else
                if (Kaart[i, j] != 0)
                    Console.Write("o");
                else
                    Console.Write(" ");
        }
        Console.Write('\n');


    }
    Console.SetCursorPosition(1, 15);
    Console.BackgroundColor = bll2;
}
}

Conway game of life

Life (of Conway Game of Life) is een leuke manier om 2D arrays te gebruiken om 'leven' te simuleren. Afhankelijk van enkele eenvoudige regels wordt beslist of een cel (1 element in de array) wel of niet overleeft naar de volgende generatie. De regels zijn gebaseerd op de 8 omliggende plekken rond de cel die ofwel leeg zijn ofwel een andere cel bevatten:

  • Any live cell with fewer than two live neighbors dies, as if by underpopulation.
  • Any live cell with two or three live neighbors lives on to the next generation.
  • Any live cell with more than three live neighbors dies, as if by overpopulation.
  • Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.

Meer uitleg: wikipedia

Kan je zelf een simulator maken die telkens generatie per generatie op het scherm toont?

Oplossing

static void Main(string[] args)
{
  
    int[,] playfield = new int[50, 250];
    ResetField(playfield);

    while (true)
    {

        ShowField(playfield);
        Console.WriteLine("Enter for next generation");
        Console.ReadLine();
        playfield = GenerateNextLevel(playfield);
        Console.Clear();
    }

}

private static void ShowField(int[,] field)
{
    StringBuilder str = new StringBuilder();
    int xl = field.GetLength(0);
    int yl = field.GetLength(1);
    for (int i = 1; i < xl - 1; i++)
    {
        for (int j = 1; j < yl - 1; j++)
        {
            if (field[i, j] == 1)
            {
                str.Append("X");
            }
            else
            {
                str.Append(" ");
            }
        }
        str.Append(Environment.NewLine);
    }
    Console.WriteLine(str.ToString());
}

private static int[,] GenerateNextLevel(int[,] playfield)
{
    int[,] newfield = new int[playfield.GetLength(0), playfield.GetLength(1)];


    for (int i = 1; i < playfield.GetLength(0) - 1; i++)
    {
        for (int j = 1; j < playfield.GetLength(1) - 1; j++)
        {
            int neighbours = CountNeighbours(playfield, i, j);
            if (playfield[i, j] > 0)
            {
                if (neighbours < 2)
                    newfield[i, j] = 0;
                else if (neighbours == 2 || neighbours == 3)
                    newfield[i, j] = 1;
                else if (neighbours > 3)
                    newfield[i, j] = 0;
            }
            else if (neighbours == 3)
                newfield[i, j] = 1;
        }
    }
    return newfield;
}

private static int CountNeighbours(int[,] playfield, int orx, int ory)
{

    int sum = 0;
    for (int i = orx - 1; i < orx + 2; i++)
    {
        for (int j = ory - 1; j < ory + 2; j++)
        {
            if (!(i == orx && j == ory))
                sum += playfield[i, j];
        }
    }
    return sum;
}

static void ResetField(int[,] field, int randomcells = 100)
{
    for (int i = 0; i < field.GetLength(0); i++)
    {
        for (int j = 0; j < field.GetLength(1); j++)
        {
            field[i, j] = 0;
        }
    }

    Random r = new Random();
    for (int i = 0; i < randomcells; i++)
    {
        int x = r.Next(1, field.GetLength(0) - 2);
        int y = r.Next(1, field.GetLength(1) - 2);
        field[x, y] = 1;
    }



}
        ```

Volgende toffe voorbeelden komen van deze website

Updating percentage

static void Main(string[] args)
{
    Console.WriteLine("Hello World!");
    Console.ReadKey();

    ShowSimplePercentage();

    Console.ReadKey();
}

static void ShowSimplePercentage()
{
    for (int i = 0; i <= 100; i++)
    {
        Console.Write($"\rProgress: {i}%   ");
        Thread.Sleep(25);
    }

    Console.Write("\rDone!          ");
}

Spinner

static void ShowSpinner()
{
    var counter = 0;
    for (int i = 0; i < 50; i++)
    {
        switch (counter % 4)
        {
            case 0: Console.Write("/"); break;
            case 1: Console.Write("-"); break;
            case 2: Console.Write("\\"); break;
            case 3: Console.Write("|"); break;
        }
        Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
        counter++;
        System.Threading.Thread.Sleep(100);
    }
}

Desk toy

static void MultiLineAnimation()
{
    var counter = 0;
    for (int i = 0; i < 30; i++)
    {
        Console.Clear();

        switch (counter % 4)
        {
            case 0: {
                    Console.WriteLine("╔════╤╤╤╤════╗");
                    Console.WriteLine("║    │││ \\   ║");
                    Console.WriteLine("║    │││  O  ║");
                    Console.WriteLine("║    OOO     ║");
                    break;
                };
            case 1:
                {
                    Console.WriteLine("╔════╤╤╤╤════╗");
                    Console.WriteLine("║    ││││    ║");
                    Console.WriteLine("║    ││││    ║");
                    Console.WriteLine("║    OOOO    ║");
                    break;
                };
            case 2:
                {
                    Console.WriteLine("╔════╤╤╤╤════╗");
                    Console.WriteLine("║   / │││    ║");
                    Console.WriteLine("║  O  │││    ║");
                    Console.WriteLine("║     OOO    ║");
                    break;
                };
            case 3:
                {
                    Console.WriteLine("╔════╤╤╤╤════╗");
                    Console.WriteLine("║    ││││    ║");
                    Console.WriteLine("║    ││││    ║");
                    Console.WriteLine("║    OOOO    ║");
                    break;
                };
        }
            
        counter++;
        System.Threading.Thread.Sleep(200);
    }
}

Treintje

static void ColorfulAnimation()
{
    for (int i = 0; i < 5; i++)
    {
        for (int j = 0; j < 30; j++)
        {
            Console.Clear();

            // steam
            Console.Write("       . . . . o o o o o o");
            for (int s = 0; s < j / 2; s++)
            {
                Console.Write(" o");
            }
            Console.WriteLine();

            var margin = "".PadLeft(j);
            Console.WriteLine(margin + "                _____      o");
            Console.WriteLine(margin + "       ____====  ]OO|_n_n__][.");
            Console.WriteLine(margin + "      [________]_|__|________)< ");
            Console.WriteLine(margin + "       oo    oo  'oo OOOO-| oo\\_");
            Console.WriteLine("   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+");

            System.Threading.Thread.Sleep(200);
        }
    }
}

Klassen objecten introductie

Week 1

In dit en volgend hoofdstuk staan bij sommige oefeningen bovenaan een link naar een alternatieve manier van de oefening te maken waarbij je ook feedback krijgt. Bekijk zeker eerst dit filmpje. Indien je geen knop "open in visual studio" hebt in github dan kan je dit oplossen als volgt: open Visual studio en kies voor clone a repository:

Clonen maar

Vervolgens copy paste je de githuburl als daar om gevraagd wordt (voor de eerste oefening is dat https://github.com/timdams/ZIESCHERPER_TESTS_H1_RapportModule).

Clonen maar

RapportModule (Essential)

Ontwerp een klasse Rapport die je zal tonen wat je graad is gegeven een bepaald behaald percentage. Het enige dat je aan een Rapport-object moet kunnen geven is het behaalde percentage (int) dat wordt bijgehouden via een auto-property genaamd Percentage. Via een methode PrintGraad kan de behaalde graad weergegeven worden (returntype van de methode is dus void), gebaseerd op dit percentage. Dit zijn de mogelijkheden:

  • Minder dan 50%: "Niet geslaagd".
  • Tussen 50% en 68% (68 incl.): "Voldoende".
  • Tussen 68% en 75% (75 incl.): "Onderscheiding".
  • Tussen 75% en 85% (85 incl.): "Grote onderscheiding".
  • Meer dan 85%: "Grootste onderscheiding".

Test je klasse door enkele objecten in je main en te onderzoeken of deze de juiste graden op het scherm printen. Bijvoorbeeld:

Rapport mijnpunten = new Rapport();
mijnpunten.Percentage = 65;
mijnpunten.PrintGraad();

Rapport mijnVriendPunten = new Rapport();
mijnVriendPunten.Percentage = 89;
mijnVriendPunten.PrintGraad();

Werkt het naar behoren?! Voer nu de bijhorende unit tests uit om alles extra te controleren: Test=> Run All Tests.

Nummers (Essential)

Maak een klasse NummerBerekenaar. Deze klasse bevat 2 getallen (type int) genaamd Getal1 en Getal2 die via een autoproperty kunnen aangepast worden. Er zijn 4 methoden die telkens een int teruggeven, behalve Quotient dat een double teruggeeft:

  • Som: geeft de som van beide getallen terug.
  • Verschil: geeft het verschil van beide getallen terug.
  • Product: geeft het product van beide getallen terug.
  • Quotient: geeft de deling van het eerste door het tweede getal terug; toon een foutboodschap ("Kan niet delen door 0") op het scherm indien er een deling door nul zal gebeuren. Gebruik Console.WriteLine om deze boodschap in de methode te laten verschijnen en geeft 0.0 terug als resultaat.

Toon in je main aan dat je code werkt.

Volgende code zou bijvoorbeeld onderstaande output moeten geven:

NummerBerekenaar paar1 = new NummerBerekenaar();
paar1.Getal1 = 12;
paar1.Getal2 = 34;
Console.WriteLine($"Paar: {paar1.Getal1}, {paar1.Getal2}");

int berekendeSom =  paar1.Som();
Console.WriteLine($"Som =  {berekendeSom}");

Console.WriteLine($"Verschil = {paar1.Verschil()}");
Console.WriteLine($"Product = {paar1.Product()}");
Console.WriteLine($"Quotient = {paar1.Quotient()}");

Output:

Paar: 12, 34
Som = 46
Verschil = -22
Product = 408
Quotient = 0,352941176470588

Studentklasse (Essential)

Maak een nieuwe klasse Student.

Deze klasse heeft 6 autoproperties:

  • Naam (string)
  • Leeftijd (int)
  • Klas (maak dit van een enum)
  • PuntenCommunicatie (int)
  • PuntenProgrammingPrinciples (int)
  • PuntenWebTech (int)

Voeg aan de klasse een methode BerekenGemiddelde toe. Wanneer deze methode wordt aangeroepen dan wordt het gemiddelde van de 3 punten teruggegeven als double zodat dit op het scherm kan getoond worden.

Voeg aan de klasse ook de methode GeefOverzicht toe. Deze methode zal een volledig "Rapport" van de student tonen (inclusief het gemiddelde m.b.v. de BerekenGemiddelde-methode).

Test je programma door enkele studenten aan te maken en in te stellen. Volgende main zou dan de bijhorende output moeten krijgen:

    Student student1= new Student();
    student1.Klas = Klassen.TI1;
    student1.Leeftijd = 21;
    student1.Naam = "Joske Vermeulen";
    student1.PuntenCommunicatie = 12;
    student1.PuntenProgrammingPrinciples = 15;
    student1.PuntenWebTech = 13;

    student1.GeefOverzicht();

Output:

Joske Vermeulen, 21 jaar
Klas: TI1

Cijferrapport:
**********
Communicatie:             12
Programming Principles:   15
Web Technology:           13
Gemiddelde:               13.3333333333

PizzaTime

Maak een klasse Pizza. Deze klasse heeft een aantal instantievariabelen:

  • toppings (string): bevat beschrijving van wat er op ligt, bv. ananas, peperoni, etc.
  • diameter (integer): doorsnede van de pizza in cm.
  • price (double): prijs van de pizza in euro.

Zorg ervoor dat je met behulp van full properties (met dezelfde namen, maar met hoofdletter) deze 3 velden kan uitlezen en aanpassen.

Bouw controle in zodat de instantievariabelen geen foute waarden kunnen gegeven worden:

  • geen negatieve price en diameter
  • pizza zonder toppings is niet toegestaan (toppings mag geen lege string zijn).

Indien een niet toegestane waarde aan een property wordt gegeven dan wordt deze gewoon genegeerd en gebeurt er verder niet (de property, en dus instantievariabele, behoudt met andere woorden de waarde die het ervoor al had).

Maak in je main een aantal pizza-instanties aan en toon de werking van de properties aan.

Figuren

Maak een eenvoudige klasse Rechthoek aan die een lengte en breedte als private instantievariabelen heeft (type int). Deze kunnen enkel via full properties ingesteld worden en nooit onder 1 gaan.

Maak ook een klasse Driehoek die een basis en hoogte als instantievariabelen (type int) heeft en bijhorende full props.

Beide klassen hebben een methode ToonOppervlakte die de oppervlakte van de figuur in kwestie op het scherm toont.

Toon de werking van het project aan door een aantal instanties van Driehoek en Rechthoek te maken, met verschillende groottes. Roep van iedere figuur de ToonOppervlakte-methode aan.

Week 2

Bibliotheek (Essential)

Boeken in een bibliotheek mogen maximum 14 dagen uitgeleend worden. De klasse BibBoek heeft volgende informatie:

  • Ontlener: De naam van de ontlener (autoproperty type string, beginwaarde = "onbekend")
  • Uitgeleend: De datum dat het boek werd uitgeleend ( private get, public set).
  • InleverDatum: De datum dat het boek ten laatste terug moet ingeleverd worden (readonly property van het type DateTime): deze geeft steeds een datum terug 14 dagen na de datum die via Uitgeleend werd ingesteld.

De achterliggende instantievariabele bij Uitgeleend wordt standaard ingesteld bij de object-creatie op de huidige tijd (DateTime.Now toewijzen aan de achterliggende instantievariabele). Als je dus vergeet de Uitgeleend property in te stellen later dan heeft deze zeker een waarde.

In hoofdstuk 11 zullen we constructors leren gebruiken: deze laten je toe om dit soort initialisatie-code te bundelen in een overzichtelijke methode.

De klasse heeft ook een methode VerlengTermijn waar je een parameter type int aan kunt meegeven. Deze methode werkt als volgt: De meegegeven waarde stelt het aantal dagen dat het boek extra mag uitgeleend worden. We gaan dit in orde brengen door de de Uitgeleend waarde met die dagen te verhogen. Als het boek dus was uitgeleend op 18 maart en het wordt verleng met 3 dagen dan maken van van Uitgeleend 21 maart.

Test je klasse door enkele objecten ervan aan te maken.

Visuele voorstelling van de klasse

BankManager (Essential)

Deel 1

We maken een Rekening klasse die kan gebruikt worden om de bankrekening van een klant voor te stellen. Deze heeft volgende zaken:

  • Een instantievariabele van het type int genaamd balans. Deze variabele houdt het totale bedrag bij dat op de rekening staat.
  • 2 autoproperties van type string namelijk NaamKlant en RekeningNummer.
  • 1 readonly property Balans die de balans teruggeeft.

Voorzie 3 methoden:

  1. HaalGeldAf: bepaald bedrag (als parameter type int) wordt van de balans verwijderd.
  2. StortGeld: bepaald bedrag (als parameter type int) wordt aan balans toegevoegd.
  3. ToonInfo: het totale bedrag op de rekening wordt getoond op het scherm, alsook de naam van de klant en het rekeningnummer (ook de staat wanneer je deel 2 hebt gemaakt wordt getoond).

Pas de HaalGeldAf methode aan zodat als returntype het bedrag (int) wordt teruggegeven. Indien het gevraagde bedrag meer dan de balans is dan geef je al het geld terug dat nog op de rekening staat en toon je in de console dat niet al het geld kon worden gegeven (error die verschijnt: Rekening leeg nu.)

Opgelet: omdat balans niet van buiten ingesteld kan worden, kan je enkel geld op de rekening zetten met behulp van de StortGeld methode. Wil je dus je klasse testen maak dan rekening-objecten aan waar je ogenblikkelijk geld op stort.

Enkele getalvoorbeelden:

  • Stel dat je een rekening hebt met 1000 balans. Indien je op deze rekening HaalGeldAf aanroept met 300 als parameter, dan zal deze methode 300 teruggeven. De balans van de rekening wordt op 700 gezet.
  • Stel dat je een rekening hebt met 1000 balans. Indien je op deze rekening HaalGelfdAf aanroept met 1500 als parameter, dan zal deze methode 1000 teruggeven (namelijk al het geld dat beschikbaar is) en de balans op 0.

Maak 2 instanties van het type Rekening aan en toon aan dat je geld van de ene Rekening aan de andere kunt geven, als volgt:

//rekening 2 geeft 300 euro aan rekening 1
rekening1.StortGeld(rekening2.HaalGeldAf(300));

Test je klasse.

  1. Nieuwe klant aanmaken
  2. Geld op een bepaalde Rekening zetten
  3. Geld van een bepaalde Rekening afhalen
  4. Geld tussen 2 Rekeningen overschrijven

Voorzie extra functionaliteit naar keuze.

Deel 2

Voeg aan de Rekening-klasse een autoproperty,genaamd Staat, met private set toe van het type RekeningStaat toe, dat een enumeratie bevat. De Rekening kan in volgende staten zijn Geldig, Geblokkeerd. Een rekening is Geldig wanneer een nieuwe rekening wordt geopend.

Maak een bijhorende publieke Methode waarmee je de Rekening van staat kunt veranderen. Deze methode (noem ze VeranderStaat) vereist geen parameters. Telkens je ze aanroept wordt de staat omgewisseld. Als dus het object momenteel op Geldig stond, dan wordt ze nu Geblokkeerd en omgekeerd.

Indien een persoon geld van of naar een Geblokkeerde rekening wil sturen dan zal er een error op het scherm verschijnen, namelijk Gaat niet. Rekening geblokkeerd.. Idem bij de StortGeld methode.

Indien de HaalGeldAf methode wordt aangeroepen en er werd meer geld afgehaald dan de balans dan zal de rekening ook op Geblokkeerd gezet worden na het verschijnen van de foutboodschap ("Rekening leeg nu").

Persoon (Essential)

Ontwerp en implementeer een klasse Persoon met 2 autoproperties (string) Achternaam en Voornaam.

Voeg bovendien een full property GeboorteDatum toe (type DateTime). De geboortedatum kan enkel waarden tussen 1/1/1990 en vandaag (moment dat code wordt uitgevoerd) liggen. Indien dit niet de situatie is, wordt de huidige datum van uitvoeren gebruikt.

Definieer vervolgens een methode BerekenLeeftijd, die op basis van de ingestelde geboortedatum en de huidige datum (dat programma wordt uitgevoerd) de leeftijd (als int) van de persoon teruggeeft.

Verjaardag

Maak een applicatie die aan de gebruiker z'n geboortedatum vraagt (dag/maand, bv 18/3). Vervolgens toont het programma op welke dag hij volgend jaar jarig zal zijn (Monday, Tuesday, etc.) én hoeveel dagen hij nog moet wachten. Uiteraard gebruik je DateTime voor deze applicatie.

Je hebt geen eigen klasse nodig. Doel van deze oefening is dat je leert werken met de bestaande DateTime en TimeSpan klassen. Je mag dus alles in de main schrijven.

Gegeven een object van het type DateTime genaamd myDate. Volgende wat uitgebreide code geeft de naam van de dag terug in de taal van het systeem waarop je applicatie draait:

string dagLokaleTaal = System.Globalization.DateTimeFormatInfo.CurrentInfo.GetDayName(myDate.DayOfWeek)

Voorbeeld invoer en uitvoer:

Wanneer is je verjaardag (d/m, bv 18/3)
20/5
Je bent over 124 dagen jarig op een Dinsdag

Sports

Sportspeler

Kies je favoriete sport of game. Maak een klasse aan die een speler uit deze sport kan voorstellen. Verzin een 4-tal private instantievariabelen die deze spelers hebben, alsook 2 methoden die de speler moet kunnen uitvoeren.

Voorzie een methode StelIn die je toelaat om de private instantievariabeles in te stellen:

Voorzie ook minstens 1 naam (string) instantievariabele.

Voorzie per instantievariabele ook telkens een full property. Waar nodig zorg je er voor dat er geen illegale waarden kunnen ingesteld worden (bv mutsnummer bij waterpolo gaat maar van 1 tot 13).

Bijvoorbeeld:

  • klasse Waterpolospeler
  • instantievariabelen:
    • spelerNaam(string)
    • mutsNummer (int)
    • isDoelman (bool)
    • isReserve(bool)
    • reeks (string, bv "Cadet")

Methoden: GooiBal, Watertrappen, StelIn

De methode StelIn zou dan zou kunnen aangeroepen worden:

speler1.StelIn("Tim", 5, false, true, "tweedeklas");

Wanneer de methoden worden aangeroepen zal er een tekst (mbv Console.WriteLine in de methode) op het scherm verschijnen die bv zegt Ik (Jos) gooi de bal. Waarbij de naam van de speler in kwestie uit het Naam instantievariabele wordt gebruikt om mee getoond te worden.

Maak vervolgens een console-applicatie aan waarin je de werking van de klasse aantoont. Maar in de applicatie een aantal speler-objecten aan, vervolgens stel je hun properties in. Vervolgens roep je enkele methoden van de spelers aan en toon je via (Console.WriteLine) ook de properties van de individuele spelerobjecten.

Toon m.a.w. aan dat je:

  • Een klasse kunt maken (in een aparte file!)
  • Instanties (objecten) van deze klasse kunt maken
  • Kunt werken met deze instanties (properties instellen én uitlezen, aanroepen van methoden)

enums

Kan je in voorgaand voorbeeld het instantievariabele reeks vervangen door een instantievariabele reeks dat een enum als datatype heeft?

Dobbelstenen

Ontwerp en implementeer een klasse genaamd Dobbelstenen, die twee klassieke D6 dobbelstenen voorstelt. De klasse heeft een WerpEnTel6 methode, waarin 1000 keer met dit paar dobbelstenen wordt gegooid. Bereken hoeveel keer een dubbel zes voorkomt, dit wordt vervolgens getoond op het scherm.

Deze klasse bevat dus niets meer dan deze ene methode. Het lijkt erop alsof je ook dobbelsteencode etc moet implementeren maar dat hoeft dus niet. De methode "gooit" gewoon 1000x na elkaar 2 getallen m.b.v de Random-klasse, telkens die 2 gegooide getallen 6 zijn onthoud je dit.

Er zijn geen aparte Exception handling oefeningen. De bedoeling is dat je zelf steeds in je oefening naar een goede plek(ken) zoekt waar deze kan toegepast worden.

Meetlat

Maak een klasse Meetlat. Via een write-only full property BeginLengte kan de gebruiker de lengte van een voorwerp instellen (in meter, type double) van de instantievariabele lengte.

In hoofdstuk 11 zullen we constructors leren gebruiken: deze zullen methoden zoals BeginLengte vervangen.

Via een reeks read-only properties (die transformeren) kan de gebruiker deze lengte in verschillende eenheden uitlezen namelijk (alles van type double):

  • LengteInM
  • LengteInCm
  • LengteInKm
  • LengteInVoet (1m= 3.2808ft)

Voorbeeld gebruik van klasse:

Meetlat mijnLat = new Meetlat();
mijnLat.BeginLengte = 2;
Console.WriteLine($"{mijnLat.LengteInM} meter is {mijnLat.LengteInVoet} voet.");

Kleur mixer (essential)

Maak een klasse Kleur, deze bestaat uit 3 autoproperties van het type int , namelijk Rood, Groen, Blauw. De klasse heeft een methode MengKleur die niets teruggeeft en één parameter aanvaardt van het type Kleur.

In de methode zal de meegegeven kleur gemengd worden met de kleur van het object zelf. Hierbij wordt van elk kanaal (Rood,groen en blauw) het gemiddelde van beide kleuren genomen. Als dus de huidige kleur 10,0,20 (RGB) als waarden heeft en we de methode aanroepen met een kleur met waarden 10,10,50 dan zal de huidige kleur veranderen naar 10,5,35 . De kleur van de meegegeven parameter verandert niet.

Voorbeeld gebruik:

Kleur k1 = new Kleur();
k1.Rood= 10;
k1.Groen= 0;
k1.Blauw= 20;
Kleur k2 = new Kleur();
k2.Rood= 10;
k2.Groen= 10;
k2.Blauw= 50;

k1.MengKleur(k2);
Console.WriteLine($"{k1.Rood},{k1.Groen},{k1.Blauw}");

Er verschijnt: 10,5,35 op het scherm.

Pokémon (Essential)

We gaan een programma schrijven dat ons toelaat enkele basis-eigenschappen van specifieke Pokémon te berekenen terwijl ze levellen. Nadruk van deze oefening is het juist gebruiken van properties. Bekijk de cheat sheet bij twijfel.

Disclaimer: de informatie in deze tekst is een vereenvoudigde versie van de echte Pokémon-stats in de mate dat ik het allemaal een beetje kon begrijpen en juist interpreteren.

Hoe Pokémon werken

Korte uitleg over Pokémon en hun interne werking: Iedere Pokémon wordt uniek gemaakt door z’n base-stats, deze zijn voor iedere Pokémon anders. Deze base-stats zijn onveranderlijk en blijven dus doorheen het hele leven van een Pokémon dezelfde. Je kan de base-stats als het dna van een Pokémon beschouwen.

De full-stats zijn echter de stats die de effectieve ‘krachten’ van een Pokémon bepalen in een gevecht. Deze stats worden berekend gebaseerd op de vaste base-stats en het huidige level van de Pokémon. Hoe hoger het level van de Pokémon, hoe hoger dus zijn full-stats.

Pokémon

De Pokémonopdracht

Maak je oplossing in een kopie van volgende solution met bijhorende unittests.

Merk op dat enkel de basis aspecten tot en met de sectie "Level-gebaseerde stats" getest worden.

Maak een consoleapplicatie met daarin een klasse Pokémon die de werking zoals hierboven beschreven heeft:

Base-stats

De base-stats worden als int bewaard. Maak voor al deze basis-eigenschappen full properties, namelijk:

  • HP_Base
  • Attack_Base
  • Defense_Base
  • SpecialAttack_Base
  • SpecialDefense_Base
  • Speed_Base

Merk op dat deze waarden eigenlijk nooit nog veranderen in een Pokémon. Het is dus raar dat we ze als full properties beschouwen. In het volgende hoofdstuk zullen we dit oplossen door te werken met een constructor.

Extra stats

Voorts wordt een Pokémon ook gedefinieerd door z’n Naam (string), Type (string, bv. grass & poison) en Nummer (int), maak hiervoor auto properties aan.

Met Nummer bedoelen we de Pokémon index die je in de Pokédex kunt opzoeken. Zo heeft Bulbasaur nummer 1 en Pikachu heeft 25.

Nog een goede reden nodig om met enum te werken? Het Type van een Pokémon zou je eigenlijk beter met een enum datatype kunnen doen dan met een string.

Level

Voeg een fullproperty Level toe (int). Deze heeft een public get, maar een private setter.

Voeg een publieke methode VerhoogLevel toe. Deze methode zal, via de private setter van Level, het level van de Pokémon met 1 verhogen. Deze methode heeft géén parameters nodig en return'd niets.

Statistieken

Voeg 2 read-only properties toe (enkel get, géén set) genaamd Average (double) en Total (int):

  • De Average-property geeft het gemiddelde van de 6 base-stats terug, dus (HP_Base + Attack_Base + Defense_Base + SpAttack_Base + SpDefense_Base +Speed_Base)/6.0.

  • De Total-property geeft de som terug van de 6 basestats. Daar de base stats niet evolueren met het level veranderen dus Average en Total ook niet van zodra de base-stats werden ingesteld, toch mag je beide statistieken steeds herberekenen in de get.

Merk op dat je voor deze twee properties dus geen instantievariable nodig hebt. Dit geldt ook voor de hier na beschreven "level-gebaseerde stats".

Level-gebaseerde stats

De eigenschappen van de Pokémon die mee evolueren met het level gaan we steeds als read-only properties van het type int implementeren:

  • Voeg een read-only property HP_Full toe om de maximum health voor te stellen. Deze wordt berekend als volgt: ( ( (HP_Base + 50) * Level) / 50) + 10 wanneer de get wordt aangeroepen.
  • Voeg voor iedere ander base-stat een XX_Full readonly property toe . Dus Defense_Full, Speed_Full, etc. Ook deze properties zijn readonly. Deze stats worden berekend als volgt: ( (stat_Base*Level) / 50 ) + 5. Attack_Full bijvoorbeeld wordt dus berekend als: ( (Attack_Base * Level) / 50) + 5

Merk op dat de formules enkel met int werken. Het effect hiervan zal zijn dat je full-stats niet per level veranderen, maar pas om de paar levels, daar we informatie "verliezen" door in de deling met int te werken.

Maak enkele Pokémon

Kies enkele Pokémon uit deze lijst en maak in je Main enkele Pokémon-objecten aan met de juiste eigenschappen.

Opgelet: Je dient dus enkel de base stats in te stellen. Alle andere zaken zijn op deze stats en het huidige level van de Pokémon gebaseerd.

Toon aan dat de Average, Total, HP en andere stats correct berekend worden (controleer in de tabel op de voorgaande url).

De volgende stats zouden steeds hetzelfde moeten zijn: Average, Total, Naam, Nummer, Type en de base_stats.

De volgende stats zouden moeten veranderen naarmate je levelt: level-gebaseerde stats en Level.

Via deze site kan je controleren welke stats je Pokémon moet hebben op een bepaald level

Level-up tester

Maak een kleine loop die je toelaat om per loop een bepaalde Pokémon z’n level met 1 te verhogen en vervolgens toon je dan z’n nieuwe stats.

Test eens hoe de stats na bv 100 levels evolueren. Je zal zien dat bepaalde stats pas na een paar keer levelen ook effectief beginnen stijgen.

Deel 2: De Pokémontester

Bekijk zeker eerst of jouw Pokémon oplossing juist is (vergelijk met de oplossing in deze cursus) voor je verder gaat.

Het is een heel gedoe om telkens manueel de informatie van een Pokémon op het scherm te outputen. Voeg een methode public void ShowInfo() toe aan je Pokémon klasse. Deze methode zal alle relevante informatie (alle properties!) in een mooie vorm op het scherm tonen, bv:

Pikachu (level 5)
Base stats:
    * Health = 56
    * Speed = 30
    etc
Full stats:
    * Health = 100
    etc.

Maak nu een nieuwe console-applicatie genaamd "Pokémon Tester":

  1. Voeg je Pokémon-klasse-bestand toe aan dit project. Verander de "namespace" van dit bestand naar de namespace van je nieuwe console-applicatie .
  2. Maak enkele Pokémon objecten aan en stel hun base stats in.
  3. Schrijf een applicatie die aan de gebruiker eerst de 6 base-stats vraagt. Vervolgens wordt de Pokémon aangemaakt met die stats en worden de full-stats aan de gebruiker getoond.
  4. Vraag nu aan de gebruiker tot welke level de Pokémon moet gelevelled worden. Roep zoveel keer de LevelUp-methode aan van de Pokémon. (of kan je dit via een parameter doorgeven aan LevelUp?!)
  5. Toon terug de full-stats van de nu ge-levelde Pokémon.

Deel 3: Pokémon-battler

Pokémon generator

Maak een methode met volgende signatuur: static Pokémon GeneratorPokémon(). Plaats deze methode niet in je Pokémon-klasse, maar in Program.cs.

Deze methode zal telkens een Pokémon aanmaken met willekeurige base-stats. Bepaal zelf hoe je dit gaat doen.

Battle tester

Voeg een methode met volgende signatuur toe aan je hoofdprogramma (dus ook weer in Program.cs): static int Battle(Pokémon poke1, Pokémon poke2).

De methode zal een getal teruggeven dat aangeeft welke van de twee Pokémon een gevecht zou winnen. 1= poke1, 2 = poke2, 0 = gelijke stand.

Controleer steeds of 1 of beide van de meegegeven Pokémon niet null zijn. Indien er 1 null is, dien je een Exception op te werpen.

Bepaal zelf hoe Pokémon vechten (bv. degene met de hoogste average van full-stats). Werk niet enkel met de base-stats, daar deze constant zijn. Het is leuker dat het level ook een invloed heeft (maar ga niet gewoon het level vergelijken).

Alles samen

Genereer 2 willekeurige Pokémon met je generator en laat ze vechten met je battle-methode. Toon wat output aan de gebruiker zodat hij ziet wat er allemaal gebeurt (en gebruik zeker de ShowInfo methode om dit snel te doen). Kan je dit in een loop zetten en wat leuker maken met Pokémon die telkens levelen als ze een gevecht winnen?!

Meer info

Voor de volledige info over Pokémon hun stats. Klik hier.

Bankmanager 2 (Essential)

Breidt de bankmanager oefening uit het vorige hoofdstuk uit met volgende functionaliteiten:

  • Voorzie in je programma een methode SimuleerOverdracht. Je kan aan deze methode 2 Rekening objecten meegeven. Vervolgens zal de methode 5x een willekeurig bedrag van de ene naar de andere rekening sturen, hierbij wisselen de rekeningen om de beurt wie verzender en wie ontvanger is. Wanneer de methode klaar is wordt er niets teruggestuurd.
  • Maak een methode CreeerTienerRekening in je programma. Deze methode geeft een nieuwe rekening terug waar de balans reeds op 50 staat. De methode aanvaardt 1 parameter: de naam van de klant, dat vervolgens in het nieuwe object wordt ingesteld.

Meetlat constructor (Essential)

Vul de Meetlat klasse uit het vorige hoofdstuk aan met een constructor. De constructor laat toe om de lengte in meter als parameter mee te geven. De LengteInMeter write-only property vervang je door een private instantievariabele double lengteInMeter.

lengteInMeter stel je nu in via de parameter die je in de constructor meekrijgt.

Persoonsregistratie (Essential) (GPT)

Deze oefening werd gemaakt (en aangepast nadien) met behulp van volgende GPT en heeft daarom de tag (GPT) achter de titel staan.

Implementeer een systeem voor persoonsregistratie dat gebruik maakt van constructors en object initializer syntax om personen op verschillende manieren te initialiseren.

Ontwerp een klasse Persoon met de volgende eigenschappen: Voornaam, Achternaam, Geboortejaar, en Email. Alle eigenschappen zijn autoproperties.

  • Implementeer een overloaded constructor waarmee je Voornaam Achternaam kan instellen.
  • Voeg een methode toe ToonInformatie() die de informatie van de persoon netjes afdrukt als volgt: "{Voornaam} {Achternaam} geboren in {Geboortejaar} heeft emailadres: {Email}".

Maak een object van Persoon aan en initialiseer de rest van de eigenschappen ( GeboorteJaar en Email) met object initializer syntax. Test de ToonInformatie() methode voor beide objecten om te verifiëren dat alle gegevens correct zijn geïnitialiseerd.

Bibliotheek constructor en static (Essential)

Pas de Bibliotheek klasse aan uit hoofdstuk 9 zodat deze een default constructor heeft. In de default constructor zorg je ervoor dat de Uitgeleend property standaard de huidige tijd krijgt van het moment waarop het object werd aangemaakt.

Voeg ook een overloaded constructor mee die een DateTime object als parameter aanvaardt. Deze parameter zal toegewezen worden aan Uitgeleend. Werp een Exception op indien je een datum in de toekomst meegeeft.

  • Een static methode VeranderAlgemeneUitleenTermijn , die 1 int als parameter aanvaardt.De methode zorgt ervoor dat het aantal dagen dat je boek mag uitlenen veranderd wordt naar de meegeven parameter. Tip: dit zal vereisen dat je een static instantievariabele hebt die je vervolgens in je InleverDatum getter gebruikt.

Digitale kluis (Essential)

Basiskluis

Maak een klasse DigitaleKluis die we gaan gebruiken om een kluis voor te stellen.

De klasse heeft volgende elementen:

  • Een fullproperty Code met private set. De get van deze property zal altijd -666 teruggeven, tenzij CanShowcode op true staat, in dit geval zal de effectieve code worden terug gegeven die in de bijhorende instantievariabele code staat.
  • Een overloaded constructor die als parameter een geheel getal toelaat. Dit getal zal worden toegewezen aan Code en is dus de toegangscode
  • Een autoproperty CanShowCode type bool, om aan te geven of de code van buitenuit kan gezien worden.
  • Een read-only property CodeLevel van type int. Deze property zal het level van de code teruggeven (ongeacht CanShowCode). Het level is eenvoudigweg de effectieve code gedeeld door 1000 als geheel getal (dus indien de code 500 is zal 0 worden teruggegeven, indien de code 2000 is wordt 2 teruggegeven, etc.)
  • Een private variabele aantalPogingen om bij te houden hoe vaak de gebruiker geprobeerd heeft de code te vinden.
  • Een methode TryCode die een geheel getal als parameter aanvaardt. De methode geeft een true terug indien de code correct was, anders false. Deze methode kan gebruikt worden om extern een code te testen , indien deze overeenkomt met de bewaarde code dan zal gemeld worden dat de code geldig is en wordt ook getoond hoeveel keer de gebruiker geprobeerd heeft Deze code is geldig. Aantalpogingen = 5. Indien de gebruiker -666 meegaf dan verschijnt er CHEATER op het scherm. Indien de gebruiker een foute code meegaf dan meldt de methode dat dit een foute code was ("geen geldige code") en wordt het aantal pogingen met 1 verhoogd. Deze methode kan maar 10 keer aangeroepen worden (ongeacht of de juiste code werd meegegeven of niet).Indien dus aantalPogingen 10 of meer is zal er op het scherm verschijnen Je hebt je 10 pogingen opgebruikt.Sorry..

Maak enkele Digitale Kluis objecten aan in je main en test of je bovenstaande klasse correct is geïmplementeerd.

Kluizen kraken

Voeg aan de klasse een static methode BruteForce toe waar je een kluis-object aan kan geven. De methode zal een aantal random codes brute forcen (met een loop die 10 verschillende getallen test) door telkens de TryCode methode van de meegegeven kluis aan te roepen. Wanneer toevallig de juiste code werd gevonden zal het aantal pogingen getoond worden.

Beeld je in dat Bruteforce een student is. Ik maak een kluis-object aan en vertel aan die student niet welke code ik heb gekozen. Vervolgens geef ik hem mijn object en zeg hem "Je krijgt 10 pogingen om met TryCode de kluiscode te raden. Succes!"

Toelichting

De BruteForcemethode is static en heeft dus geen informatie over de interne staat van een Kluis-object. Vind je dit verwarrend? Maak deze methode dan aan in je hoofdprogramma (Program.cs): public static void BruteForce(DigitaleKluis testKluis) en zorg ervoor dat ze daar werkt. De methode zal dus 10 keer TryCode aanroepen op de testKluis, telkens met een andere getal. Enkel wanneer TryCode true teruggeeft weet je dat je de juiste code hebt gevonden (merk op dat de kans erg klein is dat in 10 beurten dit zal gevonden worden).

In je main moet dit dus werken:

DigitaleKluis kluisje = new DigitaleKluis(2342);
BruteForce(kluisje);

Vervolgens verplaats je de methode naar je klasse DigitaleKluis en pas je de aanroep ervan aan in je Main:

DigitaleKluis kluisje = new DigitaleKluis(2342);
DigitaleKluis.BruteForce(kluisje);

Je hebt de oefening nu opgelost :)

Pokémon deel 2 (Essential)

Constructors

Pas de kennis van constructors toe op je Pokémon-project uit het vorige hoofdstuk. Zorg ervoor dat je Pokémon op 3 manieren kunt aanmaken als volgt:

  • Via een default constructor: alle base stats worden daarbij op 10 standaard ingesteld via de constructor
  • Via een overloaded constructor die de gebruiker toelaat om de 6 base stats als parameters mee te geven (bv new Pokemon(45,42,50,65,34,67))
  • Via object initializer syntax waarbij je eender welke base stat etc. kunt instellen (bv new Pokemon(){HP_Base=40, Name="Pikachu"};)

Het is ook logischer dat je van de base_stats een full property met private setter maakt, daar we die waarden nooit nog mogen aanpassen nadat de constructor werd aangeroepen.

Static methoden

  • Verhuis de methoden (GeneratorPokemon en Battle) die je maakte in deel 3 van de Pokémon-opdracht in vorige hoofdstuk naar de klasse Pokemon zelf en test of deze nog steeds werkt. (Is dit een logischere plek?)
  • Voeg enkele static properties met private set toe om bij te houden:
    • Hoe vaak een Pokémon z'n level werd verhoogd (dus telkens bij aanroep van VerhoogLevel)
    • Hoe veel Battles er al zijn geweest (verhoogt telkens Battle wordt uitgevoerd)
    • Hoe vaak een battle resulteerde in gelijkstand (verhoogd bij gelijkstand in Battle)
    • Hoe veel random Pokémon werden aangemaakt (verhoogt telkens GeneratorPokemon wordt aangeroepen)
  • Maak een static methode Info die deze properties als een soort samenvatting naar het scherm stuurt
  • Maak een static property NoLevelingAllowed aan van het type bool. Deze kan van buiten de klasse aangepast worden. Indien deze bool op true staat kunnen Pokémon hun level niet verhoogd worden. Wanneer in dit geval VerhoogLevel wordt aangeroepen zal er een foutboodschap op het scherm komen.

Toon in je hoofdprogramma aan dat je kunt werken met deze static methoden en properties.

Student Kevin Van Rooy maakte volgende applicatie waarbij bovenstaande opgave als initiële inspiratie diende:

Knap gedaan! (click to enlarge)

Natures (PRO)

In generatie 3 werd het concept Natures geïntroduceerd. Afhankelijk van deze eigenschap zullen bepaalde full stats van een Pokémon met 10% verhoogd of verlaagd worden.

Een Pokémon krijgt bij de constructie steeds 1 van de 25 onderstaande natures willekeurig toegewezen en zal vervolgens bij impact hebben op 2 full stats.

Gebruik je kennis van Enum, constructors en properties om dit concept in je Pokemon klasse te implementeren.

NatureVerhoogt met 10%Verlaagt met 10
AdamantAttackSp. Atk
BashfulSp. AtkSp. Atk
BoldDefenseAttack
BraveAttackSpeed
CalmSp. DefAttack
CarefulSp. DefSp. Atk
DocileDefenseDefense
GentleSp. DefDefense
HardyAttackAttack
HastySpeedDefense
ImpishDefenseSp. Atk
JollySpeedSp. Atk
LaxDefenseSp. Def
LonelyAttackDefense
MildSp. AtkDefense
ModestSp. AtkAttack
NaiveSpeedSp. Def
NaughtyAttackSp. Def
QuietSp. AtkSpeed
QuirkySp. DefSp. Def
RashSp. AtkSp. Def
RelaxedDefenseSpeed
SassySp. DefSpeed
SeriousSpeedSpeed
TimidSpeedAttack

Het is aangeraden dat je met een extra methode werkt die een waarde 0,10 of -10 zal teruggeven. Deze methode kan je dan gebruiken in de get van je full stat properties zodat deze code zo compact mogelijk blijft.

Volgende artikel lijst de natures op en beschrijft hoe ze werken

(meer indepth informatie in dit artikel)

Sport simulator

Haal je Sportspeler klasse van van het vorige hoofdstuk boven en voeg volgende statische methode er aan toe (vervang het soort speler door de naam van jouw klasse. Mijn klasse noemde Waterpolospeler)

Schrijf een methode genaamd:

static void SimuleerSpeler(Waterpolospeler testspeler)

(vervang dus Waterpolospeler door de klasse die je zelf hebt gemaakt)

De SimuleerSpeler-methode zal beide methoden van je klasse telkens 3x aanroepen m.b.v. een for-loop in de methode (dus in mijn geval 3x GooiBal en 3xWatertrappen)

Test je methode door 2 objecten aan te maken en telkens mee te geven als parameter.

Maak een tweede methode

static void SimuleerWedstrijd(Waterpolospeler speler1, Waterpolospeler speler2)

Bij aanroep van de methode verschijnt er op het scherm wie van beide speler zou winnen als ze zouden spelen. Gebruik een random uitkomst om te bepalen over speler 1 of 2 wint. Toon op het scherm "Speler 1 wint." Gevolg door de aanroep van iedere methode van die speler.

Maak een derde methode

static Waterpolospeler BesteSpeler(Waterpolospeler speler1, Waterpolospeler speler2)

Deze methode gaat ook random bepalen welke speler de beste is. Vervolgens geef je deze speler terug als resultaat. In de main roep je vervolgens iedere methode van dit object aan.

Prijzen met foreach

Maak een array die tot 20 prijzen (double) kan bewaren. Vraag aan de gebruiker om 20 prijzen in te voeren en bewaar deze in de array. Doorloop vervolgens m.b.v. een foreach-lus de volledige array en toon enkel de elementen op het scherm wiens prijs hoger of gelijk is aan €5.00. Toon op het einde van het programma het gemiddelde van alle prijzen (dus inclusief de lagere prijzen).

Computer-winkel (Essential)

Een firma heeft een grote lijst met computeronderdelen en wil hier de nodige filters op kunnen toepassen.

Computeronderdeel

De klasse computeronderdeel bestaat uit volgende autoproperties:

  • Prijs (int)
  • ID (int)
  • InDoos (bool)

Voorts heeft de klasse een default constructor die voorgaande autoproperties op willekeurige waarden instelt (prijs positief tot max 1000, ID een getal tussen 100 en 999, en InDoos heeft 50% kan om true te zijn)

De klasse heeft 1 methode ToonInfo die de 3 waarden van het object naar het scherm stuurt:

Prijs: 845, ID: 45, InDoos: true

Filteren

Maak in je hoofdprogramma een List dat je vult met 100 willekeurige aangemaakte objecten. Vervolgens:

  • Roep de ToonInfo methode aan van ieder computeronderdeel in de lijst aan.
  • Roep de ToonInfo methode aan van ieder computeronderdeel met een prijs boven de 400.
  • Idem nu voor alle onderdelen die in een doos zitten.
  • Idem nu voor alle onderdelen die een even ID hebben én wiens prijs onder de 200 ligt.

Tussen iedere filter toon je op het scherm wat de volgende lijst juist voorstelt (bv "Nu tonen we alle onderdelen in een doos").

Managen

Vraag nu aan de gebruiker wat er met de lijst moet gebeuren:

  1. Alle objecten in een doos verwijderen
  2. Alle objecten met een prijs kleiner dan 100 verwijderen.

Toon het resultaat van de aangepaste lijst (door de ToonInfo van ieder object in de lijst aan te roepen.)

Finale

Kan je hier een volledige applicatie van maken die een computerfirma als een soort inventaristool kan gebruiken? (en dus met de nodige menu's en mogelijkheden om bijvoorbeeld een nieuw onderdeel toe te voegen.) Kijk zeker eens naar volgende oefening daaromtrent.

Dit soort "de array als inventaris"-oefeningen waarbij je allerlei zaken met die array moet doen zijn geliefkoosde oefeningen en zal je vaak zien terugkomen.

Pokédex

Maak een eenvoudige Pokédex (een "bibliotheek van pokémon") die als volgt werkt. De gebruiker kan kiezen uit 5 menu-opties:

  1. Voeg Pokémon toe. Deze optie zal een random Pokémon object aanmaken en achteraan de Pokédex toevoegen.
  2. Toon Pokédex. Deze optie zal alle pokémon in de pokédex op het scherm tonen door de ShowInfo methode van ieder object in de lijst aan te roepen.
  3. Remove random: deze optie zal een pokémon op een willekeurige plek in de lijst verwijderen.
  4. Verwijder alles: deze optie zal de pokédex leeg maken.
  5. Stoppen: het programma sluit af.

Het menu blijft getoond worden tot de gebruiker de optie 5 kiest.

Pokédex++ (PRO)

Bekijk het voorbeeld in dit artikel. Kan je je pokédex van zonet integreren met deze informatie?

Student Organizer (Essential)

We gaan nu de Student-klasse uit een vorige hoofdstuk (zie onderaan de opgave) gebruiken om een List<Student> van studenten te vullen.

Maak daarom een studenten-lijst aan die 5 studenten bevat :

Initialiseer alle properties van iedere student op een standaard-waarde via de default constructor.

Het programma start op en geeft de gebruiker een menu waaruit kan gekozen worden:

  1. Student gegevens invoeren (eerstvolgende student wordt ingevuld) Vervolgens moet de gebruiker kiezen welke student (nummer) moet ingevuld worden, van 1 tot 5. Vervolgens kan de gebruiker de gegevens 1 voor 1 invullen (oude gegevens worden zonder pardon overschreven).

  2. Student gegevens tonen (alle studenten) Wanneer de gebruiker voor 2 kiest dan wordt de GeefOverzicht-methode aangeroepen van iedere student zodat de 5 ‘rapportjes’ onder elkaar op het scherm

Extra's: Bouw extra functionaliteit naar keuze bij de StudentOrganizer, zoals:

  • Vragen aan de gebruiker of de oude gegevens overschreven mogen worden, indien deze reeds ingevuld zijn.
  • Inbouwen van een eenvoudige zoekfunctie. Je kan bijvoorbeeld zoeken op naam (exacte invoer) of alle studenten tonen die in een bepaalde klas zitten of wiens punten onder/boven een bepaalde waarde zitten. Je kan dit als extra menuitem inbouwen, waarbij een nieuw menu verschijnt dat de gebruiker de verschillende zoekmogelijkheden voorstelt.
  • Verwijderen van een student (waarbij alle gegevens worden gewist)
  • Controle in alle methode inbouwen zodat ‘lege studenten’ worden genegeerd. Wanneer bijvoorbeeld menu item 2 wordt uitgevoerd (alle studenten tonen) dan worden enkel de ingevulde studenten getoond.

StudentKlasse

enum Klassen { EA1, EA2, EA3, EA4}

class Student
{
    public string Naam { get; set; }
    public int Leeftijd { get; set; }
    public Klassen Klas { get; set; }

    public int PuntenCommunicatie { get; set; }
    public int PuntenProgrammingPrinciples { get; set; }
    public int PuntenWebTech { get; set; }

    public double BerekenTotaalCijfer()
    {
        return (PuntenCommunicatie + PuntenProgrammingPrinciples + PuntenWebTech) / 3.0;
    }

    public void GeefOverzicht()
    {
        Console.WriteLine($"{Naam}, {Leeftijd} jaar");
        Console.WriteLine($"Klas: {Klas}");
        Console.WriteLine();
        Console.WriteLine("Cijferrapport");
        Console.WriteLine("*************");
        Console.WriteLine($"Communicatie:\t\t{PuntenCommunicatie}");
        Console.WriteLine($"Programming Principles:\t{PuntenProgrammingPrinciples}");
        Console.WriteLine($"Web Technology:\t\t{PuntenWebTech}");
        Console.WriteLine($"Gemiddelde:\t\t{BerekenTotaalCijfer():0.0}");
    }
}

Je kan aan de gebruiker enkel strings als input vragen. Om dus zijn ingevoerde klas om te zetten naar een enum gebruik je ofwel: a) een switch of if-else structuur die naargelang de invoer een andere enumwaarde toekent aan een enumvariabele b) (PRO) je gebruikt Enum.Parse() (vb : Klassen mijnKlas= (Klassen)Enum.Parse(typeof(Klassen), "EA2");)

Bookmark Manager

Maak een "bookmark manager". Deze tool zal in de console aan de gebruiker 5 favoriete sites vragen: naam en url. Vervolgens zal de tool alle sites in een lijst tonen met een nummer voor. De gebruiker kan dan de nummer intypen en de tool zal automatisch de site in een vereenvoudigde versie op het scherm tonen.

Je opdracht:

  1. Maak een array of List waarin je 5 bookmark objecten kan plaatsen.
  2. Vul de applicatie aan zodat de gebruiker: een bestaand bookmark kan verwijderen en een bestaand bookmark kan aanpassen

Probeer met behulp van hulpmethoden in Program.cs om zo veel mogelijk code te herbruiken.

Enkele zaken die je nodig hebt:

BookMark klasse:

class BookMark
{

    public string Naam { get; set; }
    public string URL { get; set; }
    public void ToonSite()
    {
        WebClient wc = new WebClient();
        string result = wc.DownloadString(URL);
        Console.WriteLine(GetPlainTextFromHtml(result));
    }
    //Bron: https://www.mercator.eu/mercator/std/info_aruba/reporting-hoe-gegevens-afdrukken-met-html-tags.html
    private string GetPlainTextFromHtml(string htmlString)
    {
        string htmlTagPattern = "<.*?>";
        var regexCss = new Regex("(\\<script(.+?)\\</script\\>)|(\\<style(.+?)\\</style\\>)", RegexOptions.Singleline | RegexOptions.IgnoreCase);
        htmlString = regexCss.Replace(htmlString, string.Empty);
        htmlString = Regex.Replace(htmlString, htmlTagPattern, string.Empty);
        htmlString = Regex.Replace(htmlString, @"^\s+$[\r\n]*", "", RegexOptions.Multiline);
        htmlString = htmlString.Replace("&nbsp;", " ");
        return htmlString;
    }
}

Voorbeeld van hoe de bookmark klasse zal werken:

BookMark u = new BookMark();
u.Naam = "Google";
u.URL = "https://www.google.be";
u.ToonSite();

Kan je je eigen "console browser" maken?

Speelkaarten

Maak een klasse Speelkaart die je kan gebruiken om een klassieke kaart met getal en "kleur" voor te stellen.

  • Een kaart heeft 2 eigenschappen, een getal van 1 tot en met 13 (boer=11, koningin= 12, heer= 13).
  • Een kleur, de zogenaamde suite. Deze stel je voor via een enumtype en kan als waarden Schoppen, Harten, Klaveren of Ruiten zijn.

Schrijf nu 2 loops die de 52 kaarten van een standaard pak in een List<SpeelKaart> lijst plaatst.

Maak een applicatie die telkens een willekeurige kaart uit de stapel trekt en deze aan de gebruiker toont. De kaart wordt na het tonen ook uit de lijst verwijderd. Door met een willekeurig getal te werken hoef je dus je deck niet te schudden.

OO Textbased game (PRO)

Bij de all-in-one projecten van dit semester vind je als eerst een tekstgebaseerde game (hier). Volg deze uitleg, maak het spel en voeg je eigen zotte ideeën toe.

Deel 1: Pokemon manager

Kan je alle kennis van dit semester gebruiken om een volwaardige Pokémon-gerelateerde manager te maken? Beeld je in dat je een programma schrijft dat toelaat dat gebruikers kunnen bijhouden welke Pokémon ze reeds hebben. Je array is een soort verzamelmap waarin je alle Pokémon kan plaatsen die je vindt. De gebruiker krijgt een menu waarin hij kan kiezen om:

  • Een nieuwe Pokémon toevoegen aan je map. Hiervoor toon je een lijst van geldige Pokémon. De gebruiker kiest welke nummer hij wenst toe te voegen, en die wordt dan aan de map toegevoegd.
  • Oplijsten welke Pokémon er reeds in de map zitten
  • Specifieke verzamelde Pokémon uit verwijderen
  • Statistieken van een specifieke verzamelde Pokémon te tonen
  • Specifieke verzamelde Pokémon van level te verhogen

Deel 2: Manager klasse

Plaat alle logica uit deel 1 in een nieuwe PokeManagerklasse. Plaats je array met Pokémon als static array in deze klasse. Deze klasse zal quasi enkel static methoden hebben, zoals:

  • ToonMenu
  • DeletePokemon(int index)
  • AddPokemon(Pokemon toadd)
  • ShowCollection()
  • ShowStats(int index)
  • Levelup(int index, int amountLevel)

De idee van deze klasse is dat er in je hoofdprogramma zeer weinig code nog steeds, maar die wel zeer duidelijk is. Iets in trend van:

while(true)
{
    PokeManager.ToonMenu();
    Console.WriteLine("Wat wil je doen?");
    //Toon mogelijkheden
    switch(keuze)
    {
        case 1:
            PokeManager.ShowCollection();
            break;
        //etc
    }
}

(PRO) Deel 3: CSV bestanden verwerken

Zorg ervoor dat je:

  • Bij het toevoegen van nieuwe Pokémon toon je alle mogelijke Pokémon door deze uit een csv-bestand uit te lezen inclusief hun basis stats. Je kan deze file hier downloaden.
  • Je verzamelmap naar een een csv-bestand schrijven en uitlezen (mypoke.csv). Je hoeft hiervoor enkel het id en huidige level weg te schrijven. Nadien kan je met dit id de Pokémon opzoeken in de totaallijst van alle bestaande Pokémon.

Maak een "voortplantings-simulator".

Klasse mens

Properties

Maak een klasse Mens met volgende eigenschappen:

  • geslacht (enum) : voorlopig enkel Man en Vrouw
  • kleur_ogen (enum): blauw, bruin, groen, rood
  • maxlengte (in cm): een getal dat niet onder 30 kan gaan. Dit getal geeft aan hoe groot de persoon ooit zal kunnen worden.

Constructors

Voorzie volgende constructors:

  • Een default constructor die bovenstaande eigenschappen op willekeurige waarden instelt. Bij maxlengte is deze waarde 90% van de tijd tussen 150 en 210. 5% van de tijd is deze een getal tussen 40 en 150. 5% van de tijd een getal tussen 210 en 240.
  • Een overloaded constructor waarbij de 3 eigenschappen als parameter kunnen doorgegeven worden.

Methoden

ToonMens

Voorzie een methode ToonMens. Deze vat de informatie van een Mens-object in één lang samen, hierbij zal de achtergrond van de tekst de kleur van de ogen zijn. De tekst die verschijnt is: maxlengte [in meter], geslacht

Bijvoorbeeld:

1.78 m, man  

Plantvoort

Enkel vrouwen kunnen nieuwe mensen maken.

  • Iedere mens heeft een methode 'Plantvoort' die als parameter een Mens met geslacht "Man" vereist.
  • Enkel bij vrouwen zal deze methode iets doen. Indien deze op mannen wordt aangeroepen geeft de methode null terug.
  • De methode geeft een nieuw mens object terug. De nieuwe mens heeft als lengte de gemiddelde lengte van de man en vrouw. De oogkleur is 50% van de tijd die van de man en 50%van de tijd die van de vrouw. Het geslacht is willekeurig.

Eerste voortplantingen testen

Maak 12 willekeurige mensen en plaats deze in een List. De eerste 6 mensen zijn vrouwen, de laatste 6 mensen zijn mannen. Alle mensen hebben willekeurige eigenschappen, enkel het geslacht is dus al bepaald.

Laat nu de mensen voortplanten.

  • De eerste helft mensen in de List zoeken een willekeurige partner van het andere geslacht in de andere helft lijst. En maakt hier een nieuwe mens mee. De man wordt meegegeven als parameter aan de Plantvoort methode van de vrouw.
  • Plaats deze nieuwe mens in de lijst achteraan.

Generatie bijhouden

Voeg een property Generatie toe aan je MEns die toelaat om te onthouden wanneer Mens is aangemaakt. Je kan deze via de constructor instellen.

Simulatie

Herhaal bovenstaande voor 10 generaties (merk op dat ouders dus vermoedelijk kinderen zullen maken met hun eigen kinderen/kleinkinderen. Niet over nadenken) en bekijk wat de resultaten zijn. Plaats deze simulatie in een static methode Simuleer die een List van mensen als parameter aanvaardt, alsook een getal dat aangeeft hoeveel generaties moet getest worden. Houdt bij iedere Mens die je aanmaakt om welke generatie het gaat.

Toon aan de gebruiker hoeveel de gemiddelde lengte is, hoeveel percent welke kleur ogen heeft, etc.

Maak een applicatie waarbij de gebruiker experimenten kan doen om te zien wat er gebeurt na x generaties. Bijvoorbeeld:

  • Wat als mannen erg klein zijn, vrouwen erg lang
  • Wat als mensen met blauwe ogen steeds die kleur aan hun kinderen doorgeven, na hoeveel generaties heeft iedereen blauwe ogen

Mutaties

Introduceer mutaties tijdens de voortplanting. Iedere keer als er een nieuwe mens wordt aangemaakt tijdens Plantvoort bestaat er 10% kans dat een van de "regels" overtreden wordt (bv i.p.v. gemiddelde lengte wordt lengte van de mama genomen, of i.p.v. kleur van een van ouder krijgt kind bij mutatie altijd groene ogen, etc). Verzin zelf enkele mutatie regels.

Test je mutaties en verzin extra regels en pas toe in de Simulatiemethode.

Voeg een bool parameter toe aan de simulatiemethode om aan te geven of mutaties zijn toegestaan of niet, aslsook een int parameter om de kans van mutatie mee te geven (bijvoorbeeld 10 = 10%)

Extra properties

Voeg extra eigenschappen aan je Mens toe die je ook betrekt als genetische eigenschappen tijdens het voortplanten (denk aan haarkleur, allergieën of aantal armen).

Ter info

Volgende code toont hoe je je lijst kunt sorteren op Geslacht, veronderstellende dat de lijst mensen heet:

 //using System.Linq  (toevoegen bovenaan)
 mensen = mensen.OrderBy(p => (int)p.Geslacht).ToList();

Opgelet : In deze opgave komen geen arrays van objecten voor.

Goal

Een goed mentale gezondheid – quality of life (Qal) - is even belangrijk als je fysieke gezondheid. De firma "GetWeller" heeft je gevraagd om een prototype te maken van een QualityOfLife-diary app, waarin de gebruiker kan bijhouden hoe het met z’n mentale gezondheid is gesteld.

Je dient dit prototype in een C# console applicatie te maken zodat GetWeller kan zien of ze jouw firma willen inhuren om de volledige mobiele applicatie nadien te ontwikkelen.

Succes!

Werking

De applicatie zal bestaan uit enkele fasen:

  1. Eerst zal worden gevraagd van hoeveel dagen (DagboekEntry’s) de gebruiker een dagboek-input wilt schrijven.
  2. Vervolgens wordt een samenvatting van het ingevoerde dagboek getoond
  3. Finaal berekent een analyse-module hoe de Qal van de gebruiker is gebaseerd op het ingevoerde dagboek

Klassen

De applicatie steunt op 2 belangrijke klassen:

  • DagboekEntry: deze bewaard telkens 1 dag van de gebruiker in het dagboek
  • QalAnalyzer: deze bevat een aantal hulpmethoden om de verzameling DagboekEntry’s te gebruiken.

OPGELET 1: niet alle zaken die je in de klasse zult zetten zal je mogelijk nodig hebben in de applicatie die je er rond gaat maken.

OPGELET 2: De hoofdmoot gebeurt in de QalAnalyzer methode CreateDiary. Lees dus zeker eerst de hele opgave. Indien je CreateDiary , en de andere methoden in QalAnalyzer, niet in de klasse krijgt, raad ik je aan deze in je hoofdprogramma te plaatsen (voor minder punten weliswaar) zodat je voort kunt.

Klasse DagboekEntry (7 punten)

Een DagboekEntry stelt 1 dag voor die de gebruiker in z’n dagboek heeft gezet. Een DagboekEntry bestaat uit volgende zaken:

Properties (40%)

  • Autoproperty IsInspired (bool) die bijhoudt hoe geïnspireerd de gebruiker die dag was
  • Property met private setter IsPrivate (bool) die aangeeft of deze entry als privé moet beschouwd worden
  • Fullproperty Description (string) die een beschrijving bevat van hoe de gebruiker zich voelde die dag:
    • Bij Get zal enkel de description worden teruggegeven als IsPrivate niet true is. Indien het wel om een privé entry gaat dan wordt de tekst ***PRIVATE*** teruggegeven.
  • Fullproperty Qal (int) deze kan enkel waarden tussen 0 en 100 krijgen. *Indien de gebruiker een getal kleiner dan 0 instelt dan wordt de Qal op 0 ingesteld. *Indien de gebruiker een getal groter dan 100 instelt dan wordt de Qal op 100 gezet.

Methoden (35%)

  • MakePrivate: De enige manier om van buiten uit de IsPrivate van een DagboekEntry object aan te passen. De methode aanvaardt een bool die aangeeft of het object privaat moet gezet worden en geeft niets terug.
  • ResetEntry: deze methode vereist geen parameters en geeft niets terug.
    • Zet IsInspired op false, de Qal op 50 en de description leeg
  • ShowInfo: deze methode vereist geen parameters en geeft niets terug.
    • Output het object naar het scherm als volgt (tekst tussen vierkante haakjes wordt vervangen door de effectieve waarden:
********
ENTRY:
[description]
Qal= [Qal]
*********

Opgelet: indien het object privé is dan zal dus bij description de tekst ***PRIVATE*** verschijnen

Constructors (25%)

De klasse kan op 2 manieren aangemaakt worden:

  • Via een default constructor die de ResetEntry methode gebruikt
  • Via een overloaded constructor die de Qal, IsInspired, IsPrivate en Description als parameters aanvaardt

QalAnalyzer (7 punten)

Deze klasse bevat enkel static methoden, namelijk:

  • SummarizeDiary (15%):
    • Aanvaardt een array (of lijst) van DagboekEntry’s . Geeft niets terug.
    • Zal alle ShowInfo-methoden van iedere entry aanroepen zodat het volledige dagboek op het scherm komt
  • AnalyseDiary (25%):
    • Aanvaardt een array (of lijst) van DagboekEntry’s. Geeft niets terug.
    • Zal het gemiddelde IsInspired tonen wat de totale Qal is, rekeningen houdend met de privacy:
      • De totale Qal bevat de som van alle Qal’s in de DagboekEntry’s OP VOORWAARDE dat deze niét privé is (de privé Qals worden overgeslagen en niét meegerekend)
      • Het gemiddelde IsInspired is de som van alle DagboekEntry’s waar de IsInspired true was, ongeacht of deze privé was of niet, gedeeld door het totaal aantal elementen in de array.
    • De methode toont vervolgens deze twee getalen (totale niet privé Qal, gemiddelde IsInspired)
  • CreateDiary (60%) :
    • Aanvaardt geen parameters. Geeft een array (of lijst) van DagboekEntry’s terug.
    • Zal aan de gebruiker enkele vragen stellen en het ‘dagboek’ maken.
    • De werking van deze methode leggen we in volgende sectie uit.
    • Een dagboek is een array of lijst van DagboekEntry-objecten

Werking CreateDiary-methode

Stap 0:

Er verschijnt een welkomstzin: Rise and shine. Gevolgd door de huidige tijd in uren en minuten:

Rise and shine
Het is nu 15:25

Stap 1:

Vraag aan de gebruiker hoeveel dagen de gebruiker wilt loggen. De gebruiker voert een getal in (geen foutcontrole vereist):

  • Er zal een lijst of array van dagboek entries gevuld worden met het aantal dagen dat de gebruiker wilt invoeren.
  • Deze lijst zal op het einde van de methode als resultaat terug gegeven worden

Stap 2a:

Je toont nu telkens aan de gebruiker om de hoeveelste dag het gaat (starten van 1 ) en welke dag dat ook effectief is. Als het programma wordt uitgevoerd op zondag, dan zal er dus verschijnen:

Dag 1: Maandag
[info uit stap 2b]
Dag 2: Dinsdag
[info uit stap 2b]
…

Opmerking: de dagen mogen in het Engels verschijnen als jouw OS in die taal werd ingesteld.

Stap 2b:

De gebruiker dient nu steeds in te geven hoe hij zich voelde (description), of hij geïnspireerd was (IsInspired), de Qal score die dag (Qal) en of het om een private entry gaat. Deze informatie wordt steeds in een DagboekEntry geplaatst en aan de array toegevoegd.

Deel uitvoer/invoer van de methode

Rise and shine
Het is nu 15:25
Hoeveel dagen wil je loggen in je dagboek?
3 //userinput
Dag 1: Monday
Hoe was je dag?
Zeer goed //userinput
Voelde je je geïnspireerd?(y/n)
y //userinput
Welke score geef je deze dag?
60 //userinput
Is dit een private entry?(y/n)
n //userinput
Dag 2: Tuesday
Hoe was je dag?
…

Hoofdprogramma (2 punt)

Het hoofdprogramma is zeer eenvoudig. Het gebruikt de QalAnalyzer methoden om volgende werking te hebben:

  • Eerst wordt een dagboek aangemaakt met behulp van de CreateDiary methode.
  • Dan wordt het dagboek meegegeven aan de SummarizeDiary methode.
  • Vervolgens wordt het dagboek aan de AnalyseDiary methode

CSV wegschrijven (4 punten) (PRO)

Finaal wordt in het hoofdprogramma gevraagd of het huidige dagboek (de array/list) naar een csv bestand mag worden geschreven.

Indien de gebruiker ja antwoordt dan wordt gevraagd naar de naam die het bestand moet hebben.

Ieder DagboekEntry object wordt 1 lijn, met de verschillende properties met een ; gescheiden.

Introductie

Kaart uitleg Het spel Magic The Gathering is een populair kaart/verzamel spel. We gaan een vereenvoudigde versie maken die ons zal toelaten om te ontdekken of we een bepaald wezen kunnen maken (casten) gegeven een bepaalde hoeveelheid land-kaarten.

Er zijn 2 soorten kaarten:

  • CreatureKaarten: deze wezens kan je enkel maken als je genoeg land hebt om de CastingCost te bepalen in mana (van een bepaalde kleur).
  • LandKaarten: ieder land produceert 1 mana van een specifieke type (kleur).

Wil je dus een wezen produceren dat 4 water(blauwe) mana vereist dan zal je 4 Islands nodig hebben (daar een Island 1 blauwe mana genereert)

Opdracht

Deze opgave bestaat uit 3 grote delen:

  1. CreateKaart klasse maken
  2. LandKaart klasse maken
  3. Casting tester applicatie maken

CreatureKaart klasse

Een kaart met een wezen wordt gedefinieerd door volgende zaken (enkel de zwarte pijlen gaan we gebruiken):

Kaart uitleg

Eigenschappen

  • CreatureName: naam van het wezen

    • We stellen deze voor in de klasse als een string, readonly property
  • "De casting cost": hoeveelheid mana die nodig is. In bovenstaande voorbeeld heb je 2 blauwe en 4 "kleurloze' mana nodig (kleurloos= gebruiker mag eender welke kleur kiezen).

    • In onze klasse stellen we de casting cost voor door 3 aparte readonly properties met namen:
      1. ColoredTypeNeeded: is van het ManaType type (enum), readonly property (zie verder, bij Enum landen). Deze stelt de "hoofdkleur" van de kaart voort (Blauw in bovenstaand geval)
      2. AmoundColoredTypeNeeded: type int om aan te geven hoeveel mana er nodig is van het ColoredTypeNeeded type (2 in bovenstaand geval).
      3. AmountUncoloredTypeNeeded: ook een int om aan te geven hoeveel kleurloze mana er nodig is. (4 in bovenstaand geval)
    • OOP al goed in de vingers? Bekijk dan zeker de extra opgave voor dit stukje achteraan dit document.
  • SpecialAbilities: We stellen deze voor in de klasse als een string, readonly property.

  • FlavourText:

    • We stellen deze voor in de klasse als een string, readonly property
  • "Attack/Defense":

    • We stellen deze in de klasse voor als twee readonly int properties met private setters genaamd Attack en Defense.
      • Beide properties kunnen nooit lager dan 0 zijn (0 is wel toegelaten)

Constructor

Een CreatureKaart object kan enkel via een overloaded constructor aangemaakt waarbij alle voorgaande eigenschappen moeten worden meegegeven.

Methoden

  • ChangeAttack: deze methode aanvaardt 1 bool en geeft niets terug. Als deze true is dan zal bij aanroep van de methode de Attack property met 1 verhoogd worden. Wanneer de bool false is zal de Attack property met 1 verlaagd worden.

  • Toonkaart: deze methode aanvaardt geen parameters en geeft niets terug. Aanroepen ervan zal een samenvatting van de kaart op het scherm tonen.

*******************************************************
Mahamoti Djinn (4 kleurloos, 2 Water mana)
*******************************************************
"Of royal blood among the spirits [etcetc]"
-------------------------------------------------------
    Abilities: Flying
    Attack: 5
    Defense: 6
-------------------------------------------------------

Bij attack en defense toon je de HUIDIGE waarden.

LandKaart klasse

Er zijn 5 soorten land. Ieder land genereert 1 mana van de kleur die op de kaart staat. Kaart uitleg

Eigenschappen

Een landkaart wordt door 2 zaken gedefinieerd:

  • Naam van het land (readonly property, string) (bv Island, Forest, Plains, etc.)
  • Type mana dat het land genereert
    • Stel deze voor in de klasse als een readonly property van het type ManaType (zie hieronder)

Enum landen (ManaType)

Er zijn 5 mana "kleuren". Deze stel je voor in een enum type genaamd ManaType. De kleuren zijn:

  • Water
  • Bos
  • Zon
  • Vuur
  • Dood

Constructor

Een LandKaart object kan enkel via een overloaded constructor aangemaakt waarbij alle voorgaande eigenschappen moeten worden meegeven.

Methoden

  • Toonkaart: deze methode aanvaardt geen parameters en geeft niets terug. Aanroepen ervan zal een samenvatting van de kaart op het scherm tonen.
*******************************************************
Island (Water mana)
*******************************************************

Maak 3 creature objecten aan

Maak 3 CreatureObjecten aan in je main:

  1. Namelijk ten eerste de Mahamori Djinn van hierboven.
    • ColoredTypeNeeded: ManaType.Water, AmountColoredTypeNeeded: 2 en AmoundUncoloredTypeNeeded:4
  2. Alsook een "KinderCatch" (merk ok dat deze kaart geen SpecialAbilities heeft en je deze property dus als een lege tekst mag voorstellen ("")). Deze kaart heeft dus 3 bos-mana en 3 kleurloze mana nodig. Kaart uitleg
    • ColoredTypeNeeded: ManaType.Bos, AmountColoredTypeNeeded: 3 en AmoundUncoloredTypeNeeded:3
  3. Als derde deze schattige "Mountain Goat" die als SpecialAbility "Mountainwalk heeft". Kaart uitleg
    • ColoredTypeNeeded: ManaType.Vuur, AmountColoredTypeNeeded: 1 en AmoundUncoloredTypeNeeded:0

Casting tester

We maken nu een applicatie die uit drie stappen bestaat:

  1. Een deck met landkaarten aanmaken gebaseerd op de invoer van de gebruiker
  2. Vragen welk creature de gebruiker wenst te maken
  3. Controleren of het gevraagde wezen kan gemaakt worden met de aanwezige landkaarten

Stap 1: Landen maken

De gebruiker wordt gevraagd hoeveel landen hij per manatype heeft.

Aantal Water? 
4   <-ingevoerd door gebruiker
Aantal Bos? 
2   <-ingevoerd door gebruiker
Aantal Zon? 
3   <-ingevoerd door gebruiker

etc.

Maak een List<LandKaart> lijst aan waarin je het juiste aantal LandKaart-objecten plaatst. Dus in voorgaande voorbeelden zullen er 4 Water landkaarten vooraan staan, gevolgd door 2 Bos, etc.

Stap 2: Creature vraag

De gebruiker wordt gevraagd welk dier hij wilt maken. Hierbij worden de 3 eerder aangemaakt creature objecten (de djinn, de geit en de kindercatch) getoond d.m.v. ToonKaart methode van ieder object.

De gebruiker kan dan 1, 2 of 3 als invoer geven (foute invoer niet controleren)

Stap 3: Controle

De applicatie zal nu bekijken of je het gekozen dier kan maken. Hierbij zal de List<LandKaart> bekeken worden of dat er genoeg landen van het juiste type voor het gegeven dier aanwezig zijn.

CastTest methode

Deze controle gebeurt via een static methode die je in de LandKaart klasse plaatst. Deze methode heeft CastTest als naam.

De methode aanvaardt 2 parameters:

  • Een List<LandKaart> object dat de deck met landkaarten voorstelt
  • Een CreatureKaart object dat het dier voorstelt dat je wilt maken

De methode geeft een bool terug om aan te geven of het dier wel of niet gemaakt kan worden.

Methode in stap 3 gebruiken

Gebruik de CastTest methode om te controleren ofhet gevraagde dier kan gemaakt worden. Geef daarvoor als parameters de in stap 1 gemaakt LandKaarten-lijst mee alsook het in stap 2 gekozen dier-object.

Het resultaat van deze methode zal vervolgens gebruikt worden om een zinnetje op het scherm te tonen:

Dier kan gemaakt worden (indien bool true was)

of

Dier kan niet gemaakt worden (indien bool false was)

Extra opgave voor de OOPros

Vervang de 3 casting cost gerelateerde properties door een 1 property van het type CastingCost dat je in een aparte klasse CastingCost aanmaakt.

Deze klasse heeft 3 eigenschappen : ColoredTypeNeeded, AmountColoredTypeNeeded en AmoundUncoloredTypeNeeded.

Je kan objecten van deze klasse enkel aanmaken m.b.v. een constructor die 3 parameters vereist om de 3 voorgaande eigenschappen in te stellen.

Haventerminal

Nu dat het Suez-kanaal vastzit is het de moment voor de Antwerpse haven om de grootste haven van de wereld te worden. Jouw expertise werd ingeroepen om te helpen om de containerterminal te vernieuwen zodat verdachte containers sneller kunnen gevat worden en we zo het drugsprobleem indijken.

Container klasse

Maak een klasse Container met:

  • Autoproperties met private set:
    • Bedrijf (string)
    • LaatstAangepast (DateTime)
    • GemaaktOp (DateTime)
  • Autoproperty: Beschrijving (string)
  • Full property:
    • Gewicht (int)
      • Het gewicht kan nooit onder 0 of boven 100 gezet worden.
      • Indien een foutieve waarde wordt meegegeven verschijnt er op het scherm “Fout gewicht. Ingesteld op 100” en wordt het gewicht op 100 ingesteld.
      • Telkens het Gewicht wordt aangepast wordt er 1 dag bij LaatstAangepast toegevoegd. Ongeacht of het om een geldige waarde ging of niet.

Tip: De logica is dat telkens het gewicht van de container wordt aangepast, dit een hele werkdag duurt.

  • Overloaded constructor die de Bedrijf, Beschrijving en Gewicht als parameters instelt. Voorts zet de constructor GemaaktOp en LaatsteAangepast op de huidige tijd en datum.

  • Methoden:

    • Ledig: deze zal de beschrijving van de container op “leeg” zetten en het gewicht op 0. Het bedrijf blijft!
    • InWerking: deze methode geeft een TimeSpan object terug dat bestaat uit het verschil tussen LaatsteAangepast en GemaaktOp.
    • AddContainer: deze methode geeft niets terug en aanvaardt 1 parameter van het type Container.
      • Indien de meegeven container van hetzelfde Bedrijf is als de container zelf, dan zal het volgende gebeuren.
        • Indien de som van de gewichten van beide containers samen minder dan 100 is dan zal het gewicht van de meegegeven container opgeteld worden bij die van de huidige container. De beschrijving van de huidige container zal bestaan uit de huidige beschrijving gevolgd door “ en [beschrijving meegegeven container]” . Als dus de meegegeven container (de parameter) als beschrijving “Eten” had en de huidige container “Water” dan wordt de beschrijving van de huidige container “Water en Eten”. Vervolgens wordt de meegegeven container leeg gemaakt met de Ledig methode.
      • Als de som van beide containers bij samenvoegen boven de 100 zou komen dan verschijnt er een tekst "Kan container niet adden. Dit zou gewicht boven de 100 brengen" en stopt de methode.
      • Als de bedrijven verschillend zijn van beide container bij samenvoegen boven de 100 zou komen dan verschijnt er een tekst "Containers zijn niet van zelfde bedrijf. Kan niet samenvoegen." en stopt de methode.

Scanner klasse

Deze klasse heeft één static methode ScanContainer. Deze methode geeft een string terug en aanvaardt 1 Container object als parameter.

Deze methode zal een tekstueel rapport opstellen van de meegegeven container. Dit rapport ziet er als volgt uit en wordt als string door de methode teuggegeven:

---CONTAINERREPORT [BEDRIJF]---
        Lading:[BESCHRIJVING]
        Gewicht lading=[GEWICHT]
----------------
  • Waarbij de waarden tussen vierkante haken uiteraard vervangen worden door de properties van de container.
  • Het bedrijf wordt in allcaps (dus volledig in hoofdletters) getoond.

Indien de container 5 of meer dagen aanpassingen bevat (kan je via InWerking zien) verschijnt er nog een extra zin:

*****Deze container heeft verdacht veel gewichtsveranderingen ondergaan

Werking klassen

Schrijf een korte applicatie die het volgende doet.

  • Maak 2 containers aan van hetzelfde bedrijf. De eerste container bevat “Graan” de andere “Water”. Het gewicht van iedere container is 30.
  • Voeg nu de 2e container (met water) toe aan de eerste container met de AddContainer methode.
  • Maak een loop die 10x loopt en het volgende doet:
    • Het gewicht van de eerste container wordt telkens met 10 verhoogt.
    • Het rapport van de eerste container wordt op scherm getoond gebruik makend van de ScanContainer methode uit de Scanner klasse.

Voorbeeld uitvoer:

---CONTAINERREPORT TIM NV---
        Lading:Graan en Water
        Gewicht lading=70
----------------
---CONTAINERREPORT TIM NV---
        Lading:Graan en Water
        Gewicht lading=80
----------------
---CONTAINERREPORT TIM NV---
        Lading:Graan en Water
        Gewicht lading=90
----------------
---CONTAINERREPORT TIM NV---
        Lading:Graan en Water
        Gewicht lading=100
*****Deze container heeft verdacht veel gewichtsveranderingen ondergaan
----------------
Fout gewicht. Ingesteld op 100
---CONTAINERREPORT TIM NV---
        Lading:Graan en Water
        Gewicht lading=100
*****Deze container heeft verdacht veel gewichtsveranderingen ondergaan
----------------
Fout gewicht. Ingesteld op 100
---CONTAINERREPORT TIM NV---
        Lading:Graan en Water
        Gewicht lading=100
*****Deze container heeft verdacht veel gewichtsveranderingen ondergaan
----------------
Fout gewicht. Ingesteld op 100

---CONTAINERREPORT TIM NV---
        Lading:Graan en Water
        Gewicht lading=100
*****Deze container heeft verdacht veel gewichtsveranderingen ondergaan
----------------
Fout gewicht. Ingesteld op 100
---CONTAINERREPORT TIM NV---
        Lading:Graan en Water
        Gewicht lading=100
*****Deze container heeft verdacht veel gewichtsveranderingen ondergaan
----------------
Fout gewicht. Ingesteld op 100
---CONTAINERREPORT TIM NV---
        Lading:Graan en Water
        Gewicht lading=100
*****Deze container heeft verdacht veel gewichtsveranderingen ondergaan
----------------
Fout gewicht. Ingesteld op 100
---CONTAINERREPORT TIM NV---
        Lading:Graan en Water
        Gewicht lading=100
*****Deze container heeft verdacht veel gewichtsveranderingen ondergaan
----------------

Wil je de leerstof op een andere manier leren gebruiken? Bekijk dan eens de Corona Files oefeningen. Merk op dat de corona files niet zo diepgaand zijn als de andere oefeningen, maar mogelijk wel boeiender. Het is wel zo dat iedere week voort werkt op hetgeen je de missie ervoor hebt gemaakt waardoor je dus finaal een lekker groot project zal hebben.

Het dierenrijk

Vereenvoudigde voorstelling van (een deel van het) dierenrijk

Maak bovenstaande klassenhierarchie na. Animal is de parentklasse , mammal en reptile zijn childklassen van Animal en zo voort.

Verzin voor iedere klasse een property die de parent klasse niet heeft. (bv Animal heeft "BeweegVoort", Reptile heeft "AantalSchubben", etc).

Voorzie in de klasse Animal een virtual methode ToonInfo die alle properties van de klasse op het scherm zet. De overgeërfde klassen overriden deze methode door de extra properties ook te tonen (maar gebruik base.ToonInfo om zeker de parentklasse werking te bewaren).

Maak nu van iedere klasse een object en roep de ToonInfo methode van ieder object aan.

Plaats deze dieren nu in een List<Animal> en kijk wat er gebeurt als je deze met een foreach aanroept om alle ToonInfo-methoden van ieder dier te gebruiken.

Ziekenhuis (Essential)

Deel 1

Maak een basisklasse Patient die een programma kan gebruiken om de doktersrekening te berekenen. Een patiënt heeft:

  • een naam
  • het aantal uur dat hij in het ziekenhuis heeft gelegen

Een virtual methode BerekenKost zal de totaalkost berekenen en teruggeven. Deze bestaat uit 50euro+ 20euro per uur dat de patiënt in het ziekenhuis lag.

Maak een methode ToonInfo die steeds de naam van de patiënt toont gevolgd door het aantal uur en z'n kosten.

Deel 2

Maak een specialisatieklasse VerzekerdePatient. Deze klasse heeft alles dat een gewone Patient heeft, echter de berekening van de kosten zal steeds gevolgd worden door een 10% reductie.

Toon de werking aan van deze klasse.

HiddenBookmark

Voeg een HiddenBookmark klasse toe aan je bestaande Bookmark Manager applicatie van vorige hoofdstuk.

De HiddenBookmark is een Bookmark klasse die de ToonSite methode override door VOOR en NA dat de site op het scherm werd getoond er de tekst **********INCOGNITO MODE************ getoond wordt

Test wat er gebeurt als je al je bookmarks vervangt door HiddenBookmarks.

Ballspel met overerving

Deze oefening bouwt verder op het Pong spel uit hoofdstuk 9 van het handboek.

Volgende code toont hoe we een bestaande klasse Ball kunnen overerven om een bestuurbare bal te maken

Basisklasse Ball

We maken een klasse Ball die via Update en Draw zichzelf over het consolescherm beweegt. Enkele opmerkingen:

  • We maken sommige variabelen protected zodat later de overgeërfde klassen er aan kunnen
  • Een static methode CheckHit laat ons toe te ontdekken of twee Ballobjecten mekaar raken
class Ball
{
   public int X { get { return x; } }
   public int Y { get { return y; } }
   private int x = 0;
   private int y = 0;
   protected int vx = 0;
   protected int vy = 0;
   protected char drawChar = 'O';
   protected ConsoleColor drawColor = ConsoleColor.Red;

   public Ball(int xin, int yin, int vxin, int vyin)
   {
      x = xin;
      y = yin;
      vx = vxin;
      vy = vyin;
   }

   public void Update()
   {
      x += vx;
      y += vy;
      if (x >= Console.WindowWidth || x < 0)
      {
            vx *= -1;
            x += vx;
      }
      if (y >= Console.WindowHeight || y < 0)
      {
            vy *= -1;
            y += vy;
      }
   }
   public void Draw()
   {
      Console.SetCursorPosition(x, y);
      Console.ForegroundColor = drawColor;
      Console.Write(drawChar);
      Console.ResetColor();

   }

   static public bool CheckHit(Ball ball1, Ball ball2)
   {

      if (ball1.X == ball2.X && ball1.Y == ball2.Y)
            return true;

      return false;
   }
}

Specialisatie klasse PlayerBall

De overgeërfde klasse PlayerBall is een Ball maar zal z'n vx en vy updaten gebaseerd op input via de ChangeVelocity methode:

class PlayerBall : Ball
{
   public PlayerBall(int xin, int yin, int vxin, int vyin) : base(xin, yin, vxin, vyin)
   {
      drawChar = 'X';
      drawColor = ConsoleColor.Green;
   }

   public void ChangeVelocity(ConsoleKeyInfo richting)
   {
      switch (richting.Key)
      {
            case ConsoleKey.UpArrow:
               vy--;
               break;
            case ConsoleKey.DownArrow:
               vy++;
               break;
            case ConsoleKey.LeftArrow:
               vx--;
               break;
            case ConsoleKey.RightArrow:
               vx++;
               break;
            default:
               break;
      }
   }
}

Eenvoudig spel

We maken nu een rudimentair spel waarin de gebruiker een bal moet proberen te raken.

static void Main(string[] args)
{
   Console.CursorVisible = false;
   Console.WindowHeight = 20;
   Console.WindowWidth = 30;
   Ball b1 = new Ball(4, 4, 1, 0);
   PlayerBall player = new PlayerBall(10, 10, 0, 0);
   while (true)
   {

         Console.Clear();

         //Ball
         b1.Update();
         b1.Draw();
         
         //SpelerBall
         if (Console.KeyAvailable)
         {
            var key = Console.ReadKey();
            player.ChangeVelocity(key);
         }

         player.Update();
         player.Draw();
         
         //Check collisions
         if (Ball.CheckHit(b1, player))
         {
            Console.Clear();
            Console.WriteLine("Gewonnen!");
            Console.ReadLine();
         }
         System.Threading.Thread.Sleep(100);
   }
}

Kan je dit uitbreiden met?

  • Ballen met andere eigenschappen
  • Meerdere ballen die over het scherm vliegen (benodigdheden: array )
  • Meerdere levels
  • Score gebaseerd op tijd die gebruiker nodig had om bal te raken (benodigdheden: teller die optelt na iedere Sleep)
  • PRO: collision detection tussen de ballen

Nu je overerving in de vingers krijgt, is het tijd om de ingebouwde "Class designer" van Visual Studio eens te bekijken. Volgende kennisclip toont hoe je deze handige tool kunt installeren en gebruiken: Class diagram en de class designer in Visual Studio

Extra ToString aan bestaande projecten

Voeg ToString toe aan bestaande van volgende projecten. Ik raad aan dat je dit even test in een nieuwe applicatie waarin je de bestaande klasse even toevoegt en niet de hele main overneemt.

Pokémon extra

Implementeer de ToString() methode in je Pokemon klasse zodat deze z'n full stats toont wanneer je schrijft:

Console.WriteLine(myPokemon);

Bookmark extra

Implementeer de ToString() methode in zowel de Bookmark als de HiddenBookmark klasse. Bij bookmark moet de output bestaan uit de titel van de site, gevolgd door de url tussen haakjes, bv:

Google (www.google.com)

Bij HiddenBookmark wordt er achteraan nog "---HIDDEN---" gezet:

Reddit (www.reddit.com)  ---HIDDEN---

Zorg ervoor dat er géén dubbele code in HiddenBookmark staat (tip: base()).

Boek (Essential)

Maak een klasse Boek en gebruik auto-properties voor de velden:

  • ISBN (long)
  • Titel (string)
  • Auteur (string)

Maak voorts een full property voor Prijs (double)

Maak een child-klasse die van Boek overerft genaamd TekstBoek. Een TekstBoek heeft één extra property:

  • SchoolGraad (int)

Maak een child-klasse die van Boek overerft genaamd KoffietafelBoek. Deze klasse heeft geen extra velden.

Voorts kunnen boeken "opgeteld" worden om als omnibus uitgebracht te worden. De titel wordt dan "Omnibus van X". waarbij X de Auteurs bevat, gescheiden met een komma. De prijs van een Omnibus is steeds de som van beide boeken gedeeld door 2. Schrijf een static methode TelOp die twee Boek objecten als parameter aanvaardt en als returntype een nieuw Boek teruggeeft. Deze methode voeg je toe aan de klasse Boek

In beide child-klassen, override de Prijs-setter zodat: a) Bij TekstBoek de prijs enkel tussen 20 en 80 kan liggen b) Bij KoffietafelBoek de prijs enkel tussen 35 en 100 kan liggen

PRO: Bekijk gerust de appendix indien je wilt weten hoe je de + operator kunt overriden om boeken op te tellen.

  • Zorg ervoor dat boeken de ToString overriden zodat je boekobjecten eenvoudig via Console.WriteLine(mijnBoek) hun info op het scherm tonen. Ze tonen deze info als volgt: "Titel - Auteur (ISBN) _ Prijs" (bv The Shining - Stephen King (05848152) _ 50)

Maak boeken aan van de 3 klassen, toon dat de prijs niet altijd zomaar ingesteld kan worden.

Money, money, money (Essential)

Maak enkele klassen die een bank kan gebruiken (of hergebruik je BankManager code uit hoofdstuk 1)

  1. Abstracte klasse Rekening: deze bevat een methode VoegGeldToe en HaalGeldAf. Het saldo van de rekening wordt in een private variabele bijgehouden (en via de voorgaande methoden aangepast) die enkel via een read-only property kan uitgelezen worden. Voorts is er een abstracte methode BerekenRente de rente als double teruggeeft.
  2. Een klasse BankRekening die een Rekening is. De rente van een BankRekening is 5% wanneer het saldo hoger is dan 100 euro, zoniet is deze 0%.
  3. Een klasse SpaarRekening die een Rekening is. De rente van een SpaarRekening bedraagt steeds 2%.
  4. Een klasse ProRekening die een SpaarRekening is. De ProRekening hanteert de Rente-berekening van een SpaarRekening (base.BerekenRente) maar zal per 1000 euro saldo nog eens 10 euro verhogen.

Schrijf deze klassen en toon de werking ervan in je main.

Geometric figures (Essential)

Maak een abstracte klasse GeometricFigure. Iedere figuur heeft een hoogte, breedte en oppervlakte. Maak autoproperties voor van Hoogte en Breedte. De Oppervlakte is een read-only property want deze wordt berekend gebaseerd op de hoogte en breedte. Deze berekening gebeurt in de methode BerekenOppervlakte: deze roep je met andere woorden aan in de getter van Oppervlakte en dat resultaat geeft de getter terug

Let er dus op dat Oppervlakte enkel een getter heeft. Een Setter schrijven zou vreemde bugs geven: wat als de gebruiker de Oppervlakte van de figuur zo aanpast, maar wel andere hoogte en breedte heeft ingesteld? Je zou dan een foute oppervlakte krijgen daar die niet berekend wordt.

Voorzie een abstracte methode BerekenOppervlakte die een int teruggeeft.

Maak 3 klassen:

  • Rechthoek: erft over van GeometricFigure. Oppervlakte is gedefinieerd als breedte * hoogte.
  • Vierkant: erft over van Rechthoek. Voorzie een constructor die lengte en breedte als parameter aanvaard: deze moeten gelijk zijn, indien niet zet je deze tijdens de constructie gelijk. Voorzie een 2e constructor die één parameter aanvaardt dat dan geldt als zowel de lengte als breedte. Deze klasse gebruikt de methode BerekenOppervlakte van de Rechthoek-klasse.
  • Driehoek: erft over van GeometricFigure. Oppervlakte is gedefinieerd als breedte*hoogte/2.

Maak een applicatie waarin je de werking van deze klassen aantoont

Dierentuin

Maak een console-applicatie waarin je een zelfverzonnen abstract klasse Dier in een List kunt plaatsen. Ieder dier heeft een gewicht en een methode Zegt (die abstract is) die het geluid van het dier in kwestie op het scherm zal tonen. Maak enkele childklassen die overerven van Dier en uiteraard de Zegt methode overriden.

Plaats enkele dieren in de nieuw aangemaakt lijst. Vervolgens verschijnt er een menu Wanneer de gebruiker 'q' kiest stopt het programma.Het keuze menu heeft volgende opties:

  1. Dier verwijderen , gevolgd door de gebruiker die invoert het hoeveelste dier weg moet uit de List.
  2. Diergewicht gemiddelde: het gemiddelde van alle dieren hun gewicht wordt getoond
  3. Dier praten: alle dieren hun Zegt() methode wordt aangeroepen en via WriteLine getoond
  4. Opnieuw beginnen: de List wordt leeggemaakt en het programma zal terug van voor af aan beginnen. Waarbij de lijst terug gevuld wordt met enkele start dieren.

Probeer zo modulair mogelijk te werken.

UML naar code

Kan je volgende 2 UML-diagrammen implementeren in code? Uiteraard kan je dat: make it happen!

Iedereen bestaat nog steeds uit een hoofd, handen en benen bron

Een groter compositievoorbeeld bron

Politiek (Essential)

Maak een programma om de politieke situatie van een land te simuleren.

Maak volgende klassen:

  • Land
  • Minister
  • President

Minister

Een Minister heeft geen speciale eigenschappen. Enkel een autoproperty om de Naam van de minister in bij te houden

President

Een President is een minister maar met 1 extra property met private setter: hij heeft een Teller (autoproperty type int) die start op 4 alsook een methode JaarVerderdie deze teller bij iedere aanroep met 1 verlaagt.

Land

  • Een land heeft 0 of 1 president (of koning, kies zelf)
  • Een land heeft 0 of 1 eerste minister
  • Een land heeft 0 tot 4 ministers (via een List<Minister>)

Al deze compositieobjecten zijn private. Een land heeft volgende publieke methoden:

MaakRegering

Deze methode aanvaardt volgende parameters:

  1. 1 president object die aan de private president variabele wordt toegekend

  2. Een List<Minister> object waarin tussen de 1 tot en met 5 ministers in staan: de eerste minister in de lijst wordt toegewezen aan de private eerste minister variabele. De overige ministers in de lijst worden aan de private lijst van ministers toegewezen.

Deze methode zal enkel iets doen indien er geen president in het land is (null). Indien er reeds een regering is dan zal er een foutboodschap verschijnen.

JaarVerder

Deze methode aanroepen zal de JaarVerder aanroepen op de president indien deze er is (en dus niet null is). Deze methode controleert ook of de Teller van de president na deze aanroep op 0 staat. Als dat het geval is dan worden alle ministers en president in het land op null gezet.

Eindfase

Controleer je klasse Land door enkele ministers en een president te maken en deze in een object van het type Land via MaakRegering door te geven. Test dan wat er gebeurt indien je enkele malen JaarVerder op het land aanroept.

Verkiezingen

Maak klasse VerkiezingsUitslag. Deze klasse heeft een default constructor die volgende twee properties random waarden zal geven:

  • Een full property met private set VerkozenPresident van het type President.
  • Een full property met private set VerkozenMinisters van het type List<Minister>.

Maak in je hoofdprogramma een VerkiezingsUitslag-object aan en gebruik deze om de MaakRegeringmethode van je Land van de nodige informatie te voorzien.

Moederbord

Maak een klasse Moederbord die een, je raadt het nooit, moederbord van een computer voorstelt. Kies een van de vele moederborden die je online vindt (enkele voorbeelden) en bekijk uit welke delen een moederbord bestaat ('heeft een').

Maak voor ieder deel een aparte klasse. Voorzie vervolgens via compositie de nodige objecten in je moederbord. Denk er aan dat je bijvoorbeeld 2 (of 4) RAM-slots hebt en dus hier ofwel een array moet voorzien van het type List<RAM>, oftewel twee aparte delen RAMSlot1 en RAMSlot2.

Maak een methode TestMoederbord in de klasse Moederbord. Wanneer je deze aanroept zal deze weergeven welke onderdelen nog leeg zijn (==null).

Iedere module moet via een property langs buiten ingesteld worden. (beeld je in dat je effectief een moederbord ineenknutselt):

Moederbord Z390E_GAMING = new Moederbord();
Z390E_GAMING.AGP= new AGPSlot("GeForceRTX2080");
Z390E_GAMING.CPU= new CPUSlot("IntelCorei9_9900K");
//etc.

Kan je zelf een computer samenstellen door enkele objecten van verschillende types aan te maken en deze aan je moederbord-object toe te wijzen?

Risk

Land

In het bordspel Risk heeft ieder Land-object volgende eigenschappen:

  • Naam van het land
  • Lijst met buurlanden
  • Leger dat in het land gestationeerd staat.

De referenties in deze beide zijn aggregaties: wanneer het land verdwijnt dan verdwijnen niet de buurlanden en ook niet de legers die er op gestationeerd zijn (ze kunnen gewoon naar een ander land bewegen).

Voorts implmenteert het de ToString methode en zal het de informatie oplijsten als volgt:

*[Naamland] (Buurlanden:[oplijsten buurlanden, enkel de naam]) . Grootte gestationeerd leger: *[grootte van het leger]

Leger

De Leger klasse heeft een capaciteit (sterkte) die enkel positief kan zijn.

Ieder Leger-objecthoudt via een referentie ook bij waar het leger gestationeerd is (referentie naar het Land-object).

Bordspel

Maak een klasse Bordspel dat een lijst van Land-objecten bevat (dit is compositie: wanneer het bordspel in brand wordt gestoken dan zijn ook de landen er op weg).

Maak een vereenvoudigde voorstelling van de landkaart met de Bordspel klasse, enkele Land-objecten en enkele legers.

Voeg aan de Bordspel klasse een methode "ToonKaart": deze methode zal de landen in de lijst onder elkaar schrijven (via de ToString methode van Land).

Maak een methode VerplaatsLeger dat 2 referenties naar 2 landen aanvaardt. Wanneer de aanroept gebeurt zal eerst gecontroleerd worden of het eerste land een leger bevat (zoniet wordt er een exception opgeworpen). Indien dit in orde is dan zal het leger in kwestie verhuizen naar 2e land op voorwaarde dat daar ook geen leger al is. Als dat wél het geval is dan wordt het leger in het eerste land verwijderd, en wordt de capaciteit van het 2e leger verhoogd met die van het eerste leger.

(PRO) Textbased RPG

Bekijk het volgende All-in-One project :OO Textbased Game.

Dit project gebruikt alle materie tot en met dit hoofdstuk. Kan je dit project maken én , belangrijker, uitbreiden met nieuwe functionaliteit?

Dierentuin advanced

Voeg een filter toe aan de dierentuin applicatie uit een eerder hoofdstuk:

  • Filter praten: er wordt gevraagd welke dieren moeten praten (Koe, Slang of Varken) vervolgens zullen enkel die dieren praten (tip: "is" operator).

Pokémon vergelijken

Implementeer de Equals methode (via override) in je Pokemon klasse. Twee Pokémon zijn dezelfde indien volgende zaken gelijk zijn:

  • Basis stats
  • Naam
  • Level

Een eigen huis (Essential)

Gebruik compositie én overerving om een klasse Huis te voorzien van enkele kamers, waarbij iedere kamer een klasse op zich is (denk aan BadKamer, Salon, etc). Alle kamers erven over van de klasse Kamer. Iedere kamer heeft een oppervlakte (in vierkante meter), een naam en prijs. Standaard is de prijs van een kamer 400euro, maar mogelijke child-klassen zullen deze property soms overriden. De Prijs is een readonly property (zonder setter, en heeft ook geen achterliggende instantievariabele).

Maak minstens volgende klassen:

  • Badkamer: kost 500 euro
  • Gang: kost 10euro per vierkante meter dat deze kamer groot is
  • Salon: kost 300 euro indien er geen schouw aanwezig is (via bool bijhouden) anders 500euro

De klasse Huis heeft een lijst van kamers. De klasse heeft ook een methode BerekenPrijs die de totale prijs van het huis berekent gebaseerd op de prijzen van iedere kamer in de lijst.

Test je klasse door enkele huizen te maken en er enkele kamers in te plaatsen (bepaal zelf hoe je de kamers aan het huis toevoegt: via methode, constructor, etc) en vervolgens de prijs ervan te tonen.

Mapmaker

Voorzie een Teken methode die een huis kan tekenen, gebruikmakend van de Teken-methoden van de kamers. Hiervoor dien je een X en Y coördinaat per , alsook lengte en breedte per huis én kamer te hebben zodat je deze op de juiste plekken op het scherm kan plaatsen.

Kan je ervoor zorgen dat een architect nieuwe kamers kan toevoegen en verwijderen?

Magic

Bekijk de vraag en het goedgekeurde antwoord op volgende pagina

Kan je de manager aanpassen zodat deze niet met card1 en card2 werkt, maar met een List<Card>.

Voeg zelf enkele kaarten toe en verzin ook enkele afgeleide Card-types , bv Land en Artifact.

Ganzenbord Dams Van Camp editie (Essential)

Bordspelen zoals Ganzenbord of Monopoly zijn goede oefeningen om je polymorfisme mee te oefenen. Het speelbord is niet meer dan een lijst van objecten, met ieder vakje een object in die lijst. Echter, sommige vakjes kunnen meer dan andere en je lijst bevat dus objecten van verschillende child-klassen die allemaal overerven van een basisklasse Vakje of iets dergelijks. Volgende opgave uit een oud examen toont dit. Kan je deze oefening maken?

Volgende opgave komt uit de vaardigheidsproefopdracht voor 2e zit examen van dit vak (OOP) in augustus 2021.

Intro

De kinderen van meneer Dams en Van Camp spelen graag Ganzenbord. Laatst toen we speelden vroegen ze zich af of mijn studenten eigenlijk ganzenbord zouden kunnen programmeren? "Uiteraard!" antwoordde meneer Dams. "Weliswaar een vereenvoudigde versie, maar toch. Ze zouden dat wel kunnen, ja", vulde hij fier aan.

En hier zit je nu dus, opgescheept met het programmeren van de basisfunctionaliteit van ganzenbord voor 1 speler (geen flauw benul hoe ganzenbord werkt? Geen probleem! Alles wordt duidelijk doorheen de opgave!)

Ganzenbord single player edition

Ganzenbord, the single player edition, wordt gespeeld door één speler die moet proberen zo ver mogelijk te geraken op een traject van vakjes. Sommige vakjes doen iets met de speler wanneer deze op het vakje belandt (bijvoorbeeld een stapje vooruit), anderen doen niets.

Het bord wordt aan de start van ieder spel random aangemaakt. De speler moet een dobbelsteen werpen om te weten hoeveel stappen z'n pion vooruit zal gaan. De speler verdient punten hoe verder hij geraakt.

Te maken klassen

Dobbelsteen

Deze eenvoudige klasse heeft een statische methode Rol en geeft een willekeurig getal, namelijk 1, 2 of 3.

Speelvakje

Het spelbord zal opgebouwd worden door een reeks van speelvakjes. Daarom maken we eerst dit vakje.

Een vakje wordt gedefinieerd door:

  • Een read-only int-property BeweegVakjes (met private set): deze eigenschap geeft aan hoeveel vakjes vooruit (positief) of achteruit (negatief) de speler zal gaan als op dit vakje wordt geland.
  • Een default constructor die de BeweegVakjes instelt op een waarde als volgt:
    • 30% kans op +1 of +2
    • 20% kans op -1 of -2
    • 50% kans op 0
  • Een virtuele methode ToonVakje dat op de huidige locatie van de cursor in de console de BeweegVakjeswaarde van het vakje op het scherm schrijft. Indien de waarde positief of 0 is, toon je dit met een + voor. Bijvoorbeeld: +2 of +0. Een negatief toon je met de - , zoals -2.

Ganzenbord

Een ganzenbord heeft altijd een lijst,SpeelVakjes van 10 Speelvakje-objecten. Deze zijn niet publiek zichtbaar.

Een ganzenbord heeft:

  • Een default constructor die de lijst vult met 10 Speelvakje-objecten.
  • Een instantievariabele pionIndex die aangeeft op welke vakje in de lijst de speler zich momenteel bevindt.
  • Een autoproperty HuidigeScore die standaard op 0 staat.
  • Een methode BeweegPion die een int als parameter aanvaardt en een bool teruggeeft. De werking van de methode wordt verderop uitgelegd.
  • Een methode TekenBord die het ganzenbord op het scherm toont zoals verderop uitgelegd.

BeweegPion()

Deze methode zal de speler voortbewegen op het bord en laten weten of de speler het einde heeft gehaald heeft of niet.

De speler kan een getal (het getal dat hij met de dobbelsteen verderop zal rollen) aan deze methode meegeven zodat het volgende gebeurt:

De pionIndex wordt verhoogd met het getal dat wordt meegeven, vervolgens:

  • Indien pionIndex hierdoor hoger wordt dan 9 en dus voorbij het laatste vakje wordt gegaan, dan wint de speler en wordt eerst de HuidigeScore met 10 verhoogd en wordt vervolgens true teruggegeven om aan te geven dat de speler gewonnen is.
  • Indien pionIndex eindigt op een getal 8 of lager, dan gebeurt het volgende:
    • In de lijst van SpeelVakje-objecten wordt gekeken op welk vakje de speler is aangekomen. De BeweegVakjes waarde van dit vakje wordt bijgeteld of afgetrokken van pionIndex. Dit wordt de nieuwe locatie van de speler. Er gebeurt niets meer na het belanden op dit nieuwe vakje (ook al is het een vakje waar vooruit of achteruit zou moeten gegaan worden).
    • De methode geeft false terug indien pionIndex terug een getal tussen 0 en 8 wordt, anders true indien hoger dan 8 (hij is dan gewonnnen-. Indien de speler op een index onder 0 belandt wordt false teruggegeven, wordt de score met 10 verlaagd én wordt de pionIndex finaal op 0 teruggezet (reset), ongeacht waar de speler op negatief belandde.

TekenBord()

Deze methode zal de 10 vakjes naast elkaar op het scherm visualiseren door de lijst SpeelVakjes te overlopen en van ieder object de ToonVakje methode aan te roepen. Vervolgens wordt de pionIndex locatie gebruikt om dat vakje op het scherm te overschrijven met een 'T'

Indien de speler dus op index 3 (het vierde vakje) zit van een willekeurig bord, dan kan de output zijn:

-1+2+1-T+0+0-2+1+1+0

Dus het eerste vakje heeft waarde -1, het volgende 2, het derde 1, het vierde zien we niet want daar staat het mannetje (het was was blijkbaar een negatief vakje) en zien we een T, dan krijgen we twee vakjes met waarde 0.

Main spelloop

De main spelloop werkt als volgt:

  • Een nieuw Ganzenbord object wordt aangemaakt.
  • Een loop wordt gestart die blijft duren tot de speler aan het einde geraakt. Deze loop doet het volgende:
    • Het scherm wordt leeggemaakt.
    • Het spelbord wordt getoond mbv van de TekenBord methode.
    • Een nieuw getal wordt met de Rol methode van de dobbelsteen gerold. Dit getal wordt op het scherm getoond , onder het spelbord Je rolde 2
    • De loop pauzeert nu tot de gebruiker op enter duwt.
    • Het gerolde getal wordt aan de BeweegPion-methode meegeven en het resultaat van die methode wordt gebruikt om te bepalen of de loop nog een iteratie zal doen of niet.
  • Finaal wordt de HuidigeScore van de speler getoond.

Polymorfisme komt er aan

Voeg volgende uitbreidingen toe nadat je een werkend geheel hebt.

Maak een klasse KleurVakje dat overerft van SpeelVakje

Dit vakje doet alles wat een gewoon vakje doet, het zal enkel de ToonVakje method aanvullen zodat het huidige vakje een rode achtergrond met witte letters heeft indien de BeweegVakjes waarde negatief is. Anders groen met zwarte letters bij positieve of waarde 0.

Aan de start van het spel wordt aan de speler gevraagd of hij de kleuren of zwartwit versie van het spel wil spelen.

Voeg een overloaded constructor aan GanzenBord toe die een bool aanvaardt. Deze bool geeft aan of de speler een kleuren of klassiek zwart/wit speelbord wil gebruiken. Afhankelijk van deze parameter wordt dan het juiste soort vakjes in de ``SpeelVakjes`lijst geplaatst.

Bij het opstarten van het spel vraag je aan de gebruiker of hij de kleur of de zwartwit versie van Ganzenbord wil spelen.

Mapmaker "all-in-one-project"

Begin aan het all-in-project "Map Maker". Stop aan de sectie interfaces (die je pas in volgend hoofdstuk zult leren gebruiken). Je zal in dit project dingen herkennen die je eerder al in de "Een eigen huis" oefening hebt moeten maken.

(Pro²) Methoden als objecten

Deze oefening gaat erg ver voorbij de leerstof van dit boek en is enkel bedoeld voor diegene die 'above and beyond' willen gaan in hun kennis.

Tot hiertoe hebben we altijd gepraat over enerzijds objecten, en anderzijds methoden. Twee zaken die wel een relatie met elkaar hebben (een klasse kan methoden hebben, een methode kan objecten als parameter of return type hebben). Maar wat als ik je vertel dat je ook methoden als objecten kunt gebruiken. Het concept "delegate" laat ons toe om methoden als parameters doorheen een applicatie door te geven. Hier een droog, maar duidelijk voorbeeld (bron)

public delegate void MyDelegate(string msg); //declaring a delegate

class Program
{
    static void Main(string[] args)
    {
        MyDelegate del = MethodA; //same as MyDelegate del = new MyDelegate(MethodA);
        del("Hello world");
    }

    static void MethodA(string message)
    {
        Console.WriteLine("Called ClassA.MethodA() with parameter: " + message);
    }
}

Vanaf nu kan je de variabele del als object gebruiken én aanroepen:

del("Hello World!");

Of zelfs doorgeven als parameter

static void Main(string[] args)
{
    MyDelegate del = MethodA;
    InvokeDelegate(del);
}

static void InvokeDelegate(MyDelegate del) // MyDelegate type parameter
{
    del("Hello World");
}

Omdat delegates al wat oldschool zijn geworden, heeft .NET ook al vele jaren wat meer generieke (en dus bruikbaardere) versies hiervan, namelijk Action<T> en Func<T>. De werking hiervan legt deze gekende man stap voor stap uit in deze blog.

Kan je de hele tekst volgen en de gemaakte finale oefening uitbreiden naar een echte "applicatie"?

Figures with interfaces

Gebruik je Rechthoek-klasse uit de Figuren oefening die je eerder hebt aangemaakt. Maak een List aan waarin je een 10 rechthoek-objecten plaatsen, allen met een verschillende grootte. Zorg ervoor dat je nu je rechthoeken met de Sort()-methode kan sorteren op oppervlakte.

Toon de werking aan in een klein voorbeeld programma.

Carbon Footprint (Essential)

Maak 4 klassen:

  • Huis
  • Fabriek
  • Auto
  • Plant

Maak een interface ICarbonFootPrint die

  • 1 methode BerekenFootprint heeft die een int teruggeeft en geen parameters nodig heeft
  • 1 methode VerlaagFootprint die niets teruggeeft en geen parameters nodig heeft

Breidt de volgende klassen met de interface uit:

  • De carbon footprint van een huis is gebaseerd op het volume van het huis in kubieke meter maal 10.
  • De carbon footprint van een fabriek is gebaseerd op het aantal werknemers maal 100.
  • De carbon footprint van een auto is gebaseerd op het merk

Het verlagen van de footprint in iedere klasse verzin je zelf (door bijvoorbeeld bij het huis de factor 10 met 1 te verlagen).

Zorg ervoor dat van iedere klasse de footprint kan bevraagd worden (maak/verzin dus de nodige properties per klasse om dit te bereken). De klasse plant moet je niet aanpassen.

Plaats van iedere klasse 2 objecten in een gemeenschappelijke lijst en zorg ervoor dat:

  • de footprint van alle objecten getoond wordt (planten worden overgeslagen)
  • de gemiddelde footprint van alle objecten (ook planten worden meegeteld) berekend
  • toont welk object de hoogte footprint heeft
  • van alle objecten de footprint kan verlaagd worden

Pokémon interfaces (Essential)

Shadow Pokémon zijn een speciaal ras Pokémon (zie hier) en kunnen "purified" worden op verschillende manieren. Maak een interface IShadow die een methode Purify heeft. Verzin enkele Pokémon die Shadow Pokémon zijn, geef ze de interface, en verzin manieren hoe ze purified kunnen worden (bv door hun HP op -1 te zetten). Kan je door je lijst van Pokémon gaan enkel de Shadow Pokémon op het scherm tonen?

PokéDex IList (PRO)

Maak een PokéDex die de interface IEnumerable implementeert zoals hier uitgelegd. Hierdoor kan je je PokéDex klasse gebruiken als een List.

Mapmaker afwerken

Vervolledig het all-in-project "Map Maker". Inclusief interfaces en is/as .Kan je zelf extra zaken toevoegen zoals andere kamers, andere functionaliteit, etc.

Game

Maak eerst de corona missie van dit hoofdstuk. Die opdracht is veel kleiner dan deze stevige brok.

Zie onderaan pagina voor minimale klasse-hiërarchie en interfaces.

Maak een spel dat als volgt werkt:

  • De speler dient met zijn pion de overkant van het veld te bereiken.
  • Het veld bestaat uit 20 bij 20 vakjes. Op ieder vakje kan maximum één mapelement staan:
    • De speler zelf
    • Een monster
  • Een rots
  • Een speler kan niet door rotsen of monsters wandelen.
  • Een speler kan in zijn beurt telkens één vakje bewegen OF naar rechts schieten:
  • Indien geschoten wordt dan zal het mapelement op het vakje rechts van de speler vernietigd worden (rots of monster)
  • Monsters kunnen ook bewegen. In de beurt van de monsters beweegt ieder monster in een willekeurige richting indien er geen rotsen of spelers LINKS van het monster staan.
  • Indien er WEL een rots of speler LINKS van het monster staat dan schiet het monster en vernietigd het de speler of rots.
  • Enkel RockDestroyer monsters kunnen schieten. De setup van het spel bestaat uit volgende stappen:
  1. Maak een 20 bij 20 array aan en plaats bepaalde hoeveelheid monsters en rotsen op de kaart, behalve op kolom 0.
  2. Plaats de speler op de plek 0,10 in de array (midden van kolom 0)
  3. Doorloop de volgende stappen tot er winnaar is

Iedere beurt van het spel bestaat uit volgende stappen:

  1. Vraag speler om input (bewegen in 1 van de 4 richtingen OF schieten)
  2. Voer actie van speler uit
  3. Kijk of speler overkant van kaart heeft bereikt, zo ja: gewonnen!
  4. Beweeg ieder monster op de kaart in een willekeurige richting
  5. Beweeg iedere RockDestroyer OF laat RockDestroyer schieten

Stel de speler voor door een X, een rots door O , een monster door M een RockDestroyer door D.

Je zal de meeste logica in je Main of een Manager klasse moeten plaatsen. De Speler klasse bijvoorbeeld kan niet beslissen at een monster moet doen, dat zou willen zeggen dat de speler als jet ware " in het monster" beslissingen kan maken, wat tegen het principe van OOP zou zijn.

Extra’s:

Voorgaande beschrijving is een ‘minimale’ beschrijving. Voorzie extra functionaliteit naar believen zoals:

  • Speler heeft levens
  • Monsters hebben levens
  • Andere soort monsters (bv slimmere)
  • Meerdere levels met telkens andere/meer monsters bijvoorbeeld
  • Meerdere spelers
  • Verder schieten, of schieten in andere richtingen.

Klasse-schema

Klasse-schema

Location is van het type Point (compositie). Point is een zelfgemaakte mini klasse die er als volgt uit (minimaal uitziet):

class Point
{
  public int X{get;set;}
  public int Y{get;set;}
}

En dus in je abstracte klasse MapElement zal iets krijgen in de trend van: public Point Location {get;set;} = new Point();

Enkele screenshots

Interfaces Het spel in actie

Volgende opgave kwam uit de vaardigheidsproef module 4 van dit vak juni 2019.

Hammertje Tik

Maak een digitale console-versie van het klassieke kinderspel hamertje Tik

In dit spel heeft het kind een hele hoop kleurige blokjes ter beschikking waarmee hij op een kurken bord eender welke ‘tekening’ kan maken door de blokjes in de kurk met een nagel te kloppen.

Opbouw project

Het project bestaat uit enkele delen.

  • Klassen - Deel 1 (11 punten) : maken van de nodige klassen die Vormen voorstellen
  • Menu - Deel 2 (5 punten): maken van een menu die toelaat dat de gebruiker verschillende vormen op het scherm kan manipuleren
  • PRO -Deel 3 (4 punten): een iets pittigere pro-gedeelte voor zij die nog tijd hebben

DEEL 1 (11 PUNTEN)

We gaan volgende klasse-structuur in de volgende stappen maken:

Stap 1: /2PUNTEN

Maak een abstracte klasse Vorm die z’n locatie (via x,y coördinaten als autoprops) op het scherm kan bijhouden alsook een abstracte methode TekenVorm. Voeg voorts een virtual property Kleur toe van het type ConsoleColor. Deze property is read-only en geeft ConsoleColor.Red terug.

De klasse Vorm heeft een overloaded constructor die steeds de x,y coördinaten verwacht als parameters vervolgens instelt in de bijhorende autoprops.

De Vorm heeft géén default constructor.

De locatie van de vormen die we hierna zullen definiëren is steeds het punt linksboven indien we een rechthoek omheen de vorm zouden tekenen. Het voorbeeld hier toont deze plek bij het vliegtuig van stap 3:

linkerbovenhoek is de oorsprong

Stap 2: /2punten

Maak twee klassen die allebei een Vorm zijn:

  • Rechthoek
  • Lijn

Zorg ervoor dat beide vormen via TekenVorm zichzelf op het scherm kunnen tonen in hun eigen kleur.

Lijn:

  • Heeft een Lengte autoproperty
  • Heeft 1 overloaded constructor die x,y en lengte vraagt
  • Heeft als kleur ConsoleColor.Green
  • Een lijn bestaat uit een reeks sterretjes (*) horizontaal naast elkaar, gelijk aan de lengte die je via de constructor van bij de start kunt meegeven. Bijvoorbeeld bij lengte 3:
	* * *

Rechthoek:

  • Heeft een Lengte en Breedte autoproperty
  • Heeft 2 constructors:
    • 1 overloaded die x,y, lengte en breedte vraagt
    • default die standaard een rechthoek op locatie 1,1 zet met lengte en breedte 2
  • Heeft als kleur ConsoleColor.Yellow
  • Een rechthoek verwacht een lengte en breedte bij het aanmaken en kan zichzelf ook tekenen. Als je lengte 4 en breedte 2 ingaf zou deze er als volgt uitzien:
 	* * 
	* * 
	* * 
	* *

Stap 3: /3punten

Maak een klasse Vliegtuig dat ook een Vorm is. Een vliegtuig bestaat (compositie!) uit 1 Rechthoek en 2 Lijn-objecten en ziet er altijd hetzelfde uit, namelijk

  • Een rechthoek van 2 bij 5
  • Links en rechts van deze rechthoek een lijn van 3 breed, telkens in de helft van de lengte van deze rechthoek
  • Enkel de locatie op het scherm kan anders zijn per vliegtuig, hun afmetingen echter niet.
	   * * 
	   * * 
     * * * * * * * * 
	   * * 
	   * *

Een Vliegtuig heeft een constructor die de x,y locatie vraagt (zie tekening vorige pagina i.v.m. coördinaten) en zal in de constructor de 3 vormen (2 lijnen en 1 rechthoek) aanmaken.

Merk op dat dus dat het lichaam (de Rechthoek) geel zal zijn, en de twee vleugels groen (van de lijnen).

Stap 4: /2punten

Maak een klasse Vloot dat ook een Vorm is. Een vloot bestaat uit 1 of meerdere vliegtuigen. Je kan via de constructor instellen hoeveel vliegtuigen er moeten zijn in 1 Vloot, alsook de x,y coördinaten (linksboven).De nodige vliegtuigen worden in de constructor aangemaakt en in een lijst bijgehouden in het Vloot-object zelf.

Houdt via een lijst in de klasse de vliegtuigen bij. Een vloot vliegtuigen dat getekend wordt tekent gewoon alle vliegtuigen onder mekaar.

Een vloot van 3 vliegtuigen zal er als volgt uitzien op het scherm:

        * * 
        * *
  * * * * * * * * 
        * * 
        * *
        * * 
        * *
  * * * * * * * * 
        * * 
        * *
        * * 
        * *
  * * * * * * * * 
        * * 
        * *

Merk op dat de vliegtuigen hun originele kleuren behouden uit de vorige stap.  

Stap 5: /2 punten

Maak een interface IBeweegbaar, bestaande uit 1 methode Beweeg. Deze geeft niets terug en heeft 1 parameter van het type Richting.

Richting is een enum-type dat 4 mogelijke waarden heeft: Links, Rechts, Boven, Beneden.

Pas de interface toe op Vliegtuig en Vloot. Deze methode zal de Locatie van het object 1 plekje opschuiven in de richting die in de parameter werd meegegeven. Als dus vervolgens het object opnieuw wordt getekend zal het object 1 plek naar rechts opgeschoven zijn.

Vormen verplaatsen is gewoon een kwestie van de X,Y coördinaten aan te passen. Meer is niet nodig.

Deel 2 (5 PUNTEN)

Stap 6 /5punten

Maak nu een hammertje tik programma: een console-programma dat de gebruiker steeds volgende vragen stelt en vervolgens de gevraagde vormen toevoegt aan het scherm. Op de duur zal de gebruiker grote, complexe tekeningen kunnen maken door meerdere vormen en types te combineren. Iedere vorm die wordt toegevoegd zal in een lijst worden bijgehouden.

Een loop zal steeds volgende stappen uitvoeren tot de gebruiker het programma afsluit.

  1. Alle vormen die reeds zijn toegevoegd in de lijst op het scherm tekenen via TekenVorm
  2. Aan gebruiker vragen wat er moet gebeuren
  3. Beeld leegmaken

De vragen die gesteld kunnen

  1. Lijst leegmaken => alle vormen verdwijnen en de gebruiker kan terug opnieuw beginnen
  2. Vorm toevoegen => zal de gewenste vorm toevoegen aan een lijst nadat volgende 2 of 3 extra zaken aan de gebruiker werden gevraagd:
    1. Welke vorm? (rechthoek, lijn, vliegtuig, vloot)
    2. Locatie (x,y) op het scherm
    3. Vormafhankelijke informatie? (bv aantal vliegtuigen)
  3. Afsluiten => programma sluit af
  4. Verplaats object naar…: gevolgd door de vraag in welke richting moet verplaatst worden. Wanneer de gebruiker deze optie zullen alle objecten die de IBeweegbaar hebben 1 plekje in de ingegeven richting verschoven worden

DEEL 3 (4 PUNTEN)

STAP 7: /2 punten

Voeg een nieuw menu-item toe namelijk Vergroot vloot. Wanneer de gebruiker deze optie kiest zullen alle Vloot-objecten in de lijst 1 extra vliegtuig bijkrijgen. Je zal hiervoor een extra methode VergrootVloot aan de Vloot-klasse moeten toevoegen.

STAP 8: /2 punten

Voeg een nieuw menu-item Sorteer toe, indien de gebruiker dit kiest dan worden alle Vormen gesorteerd op hun x-locatie, hoe kleiner x, hoe eerder in de lijst. Bij gelijke x wordt gekeken naar de y-locatie waar degene met de laagste y voor die met hogere komt.

Vervolgens worden de objecten met een loop op het scherm beschreven als volgt: VormType, x, y

Dus bijvoorbeeld:

Rechthoek, 3, 5
Rechthoek, 3,6
Vliegtuig, 5,2

Volgende opgave was de vaardigheidsproefopdracht voor het 2e zit examen van dit vak (OOP) in augustus 2019

We maken een eenvoudige veiling-simulator. Hierbij kunnen spelers bieden op getoonde schilderijen en deze kopen indien ze wensen. Het spel wordt gespeeld door 2 spelers, waarbij 1 speler de gebruiker is, de andere wordt door de computer bestuurd.

Deel 1: Klassen

Klasse 1: schilderij (3p)

Maak een klasse Schilderij.

Deze heeft 2 minstens methoden

  • TekenSchilderij: de methode zal een willekeurig schilderij op het scherm tekenen in linkerbovenhoek. Een schilderij is steeds 10 bij 10 groot en bestaat uit een willekeurige hoeveelheid gele, rode en groene vlakken. Enkele voorbeelden:

  • Opgelet: ieder object tekent een ander schilderij. Als op hetzelfde object 2x na mekaar TekenSchilderij wordt aangeroepen dan zal uiteraard 2x hetzelfde schilderij getekend worden. De klasse houdt intern bij uit hoeveel rode, hoeveel gele, en hoeveel rode vlakken het schilderij bestaat.

  • KrijgData: deze methode geeft terug uit hoeveel rode vlakken de schilderij bestond.

Klasse 2: WaardeBepaler (2p)

Maak een klasse WaardeBepaler. Deze bestaat uit 1 static methode genaamd BerekenWaarde. Deze methode aanvaardt 1 int als parameter. Het geeft een double terug als resultaat.

De methode zal de waarde van het schilderij bepalen gebaseerd op het aantal rode vlakken. De waarde van een schilderij is het aantal rode vlakken maal 1000 en daar vervolgens de vierkantswortel van.

Een schilderij met 50 rode vlakken heeft dus een waarde van €223,6 (vierkantswortel van 1000*50)

Dit getal tot 1 cijfer na de komma wordt door de methode teruggegeven.

Klasse 3: Koper (4p)

Een koper heeft bij de start steeds een budget van 1500 euro.

Het budget kan enkel als readonly property van buitenaf uitgelezen worden. De setter is private.

Een koper heeft een lijst van schilderijen (leeg bij de start) waarin ieder gekocht schilderij komt.

Een koper heeft een constructor die een interne waarde ogenblikkelijk op 1500 zet

  • Een methode “Koop”: deze methode aanvaardt 1 parameter van het type schilderij en geeft een bool terug.
    • Eerst wordt de waarde van het meegegeven schilderij berekend mbv van de WaardeBepaler klasse.
    • Vervolgens: Indien de koper genoeg budget heeft dan zal de waarde van het schilderij van het budget gehaald worden (via de private setter) en wordt het schilderij aan de lijst van gekochte schilderijen van de koper toegevoegd.
      • Vervolgens geeft de methode ‘true’ terug.
    • Indien de koper niet genoeg budget heeft wordt false teruggeven.

Een koper heeft een methode TotaleWinst: deze methode geeft de totale waarde van alle schilderijen samen in zijn lijst terug als een int.

Deel 2: Veiling (4p)

Schrijf een programma dat voorgaande klasse gebruikt als volgt:

  • 1 speler-object wordt door de gebruiker bedient. 1 door de computer.
  • Er verschijnt telkens een schilderij, met daaronder de waarde ervan.
  • Er wordt aan de gebruiker gevraagd of hij/zij dit wenst te kopen. Indien ja, en dit kan, dan wordt het schilderij aan zijn lijst toegevoegd en z’n budget verlaagt.
  • Indien neen dan zal de computer het schilderij kopen indien deze nog genoeg budget heeft.
  • Vervolgens komt het volgende schilderij.
  • Het ‘spel’ stopt wanneer beide speler het huidige schilderij niet kunnen of willen kopen.
  • De “TotaleWinst” van iedere speler wordt vergeleken. De speler wiens TotaleWinst + overgebleven Budget het hoogst is wint.
    • Voorbeeld: speler 1 heeft TotaleWinst 300 en Budget over 300, dus 600
    • Speler 2 (de computer) heeft TotaleWinst 400 en Budget 100, dus 500. Speler 1, de gebruiker, wint de veiling
  • Het spel toont wie heeft gewonnen en sluit dan af.

Deel 3: Picassos (2p)

  • Maak een klasse Picasso. Deze klasse is een Schilderij, maar bestaat uit een 15 bij 15 groot schilderij (in plaats van 10 bij 10) en zal dus meestal meer waard zijn.

  • Zorg ervoor dat er op de veiling ongeveer 30% van de tijd een Picasso verschijnt die de spelers kunnen kopen. De overige werking blijft dezelfde.

Deel 4: Koper++ (4p)

  • De klasse Koper heeft een extra methode “SorteerBezit”. Wanneer deze wordt aangeroepen dan worden de schilderijen in zijn bezit gesorteerd op basis van hun waarde. De hoogste waarde komt vooraan en zo verder.

  • De klasse Koper heeft een extra methode “KrijgSchilderij”: deze methode aanvaardt 1 parameter van het type Koper. Wanneer de methode wordt aangeroepen op een koper en een andere koper wordt als parameter meegegeven, dan krijgt de koper die de methode aanroept het eerste schilderij uit de lijst van de meegegeven koper. Het schilderij verdwijnt vervolgens uit de lijst van deze koper.

  • Voeg aan achteraan het spel code toe die aantoont dat deze twee methoden werken.

Volgende opgave was de vaardigheidsproefopdracht voor het 1e zit examen van dit vak (OOP) in juni 2020

Opgave 1 (70%)

Een school, "Stedelijk Lyceum 90", heeft je gevraagd een administratief pakket voor hen te ontwikkelen. Maak een applicatie die simuleert hoe leerlingen in een school worden gemaakt op voorwaarde dat er genoeg geld aanwezig is. Vervolgens kunnen leerlingen uit een school gerekruteerd worden om als werkstudent te dienen.

Maak een klasse school (35%)

  • Je school heeft volgende properties:
    • geldHoeveelheid: een int die nooit onder 15 of lager kan gaan en die bijhoudt hoeveel geld je school nog heeft, private set
    • IsBijnaLeeg: een readonly property die true teruggeeft indien de hoeveelheid geld 15 of lager is
    • leerlingen: Een lijst van leerlingen (zie hierna) als gewone property die initieel een capaciteit van 15 plekken heeft.
    • Een autoproperty Naam die steeds op school90 staat.
  • Volgende publieke methoden:
    • Geefgeld: deze methode aanvaardt een double. Het getal dat je meegeeft wordt bijgeteld bij geldHoeveelheid
      • Maakleerling: Deze methode voegt een nieuwe leerling aan de lijst toe. Een leerling kan enkel gemaakt worden indien je school minstens 40 of hoger geld heeft. Vervolgens wordt de hoeveelheid geld met 15 verlaagt. Deze methode geeft een bool terug: true indien het aanmaken gelukt is, false indien niet (omdat er niet genoeg geld was)
      • Geefleerling: deze methode geeft een object van het type leerling terug indien er minstens 2 leerlingen in de lijst aanwezig zijn. De methode kiest altijd de eerste leerling uit de lijst om terug te geven en zal deze vervolgens uit de lijst verwijderen.
  • Je school override ToString zodat de geld hoeveelheid, de Naam ,IsBijnaleeg en het aantal leerlingen in de lijst mooi op het scherm toont.

Maak een klasse leerling. (25%)

Deze heeft 1 property:

  • Een readonly string Naam met private set

Deze heeft een default constructor die bij het aanmaken van de leerling de naam van de leerling zal instellen op StudentX waarbij de X vervangen wordt door de zoveelste leerling die in het programma al werd aangemaakt. De eerste heet dus Student1, dan Student2, etc.

Main werking (40%)

Toon in je main aan dat je klassen werken door een programma te maken dat:

  • Een school aanmaakt
  • Een lege lijst leerlingen, werkstudenten genaamd, aanmaakt
  • Een loop start die 15 keer zal lopen, per loop:
    • wordt een random hoeveelheid geld (tussen 15 en 30) aan je school gegeven.
    • Wordt een leerling aangemaakt in je school
      • Indien een leerling kon aangemaakt worden (omdat er genoeg geld was) bestaat er 60 % kans dat vervolgens een ridder met de Geefleerlingmethode uit je school wordt gehaald en in de werkstudenten lijst wordt gestoken.
    • Wordt de informatie van je school op het scherm getoond (mbv ToString)
    • Na de loop wordt de lijst van werkstudenten overlopen en worden alle namen de leerlingen in die lijst getoond.

Opgave 2 (30%)

Maak een kleine applicatie die kan gebruikt worden om alle reddingswerkers tijdens een ramp in kaart te brengen en op te volgen.

Klassestructuur (40%)

Implementeer volgende klasse diagram (de naam van de Interface= IRampGebiedResponder) Schema

Zorg ervoor dat:

  • Een SpecialeBrandweer altijd een risicograad van 15 heeft wanneer deze wordt aangemaakt. De overige klassen is die altijd 6
  • RapportStatus toont de risicograad van het object. Bij de SpecialeBrandweer wordt deze aangevuld met de zin “Ik ben beter”

In je main (60%)

In je main wordt van elke klasse 1 object aangemaakt:

  • Plaats deze elementen in een dictionary waarbij je steeds een random getal tussen 100 en 2000 als “key” toewijst. Zoek eerst op of deze key reeds in de dictionary aanwezig is, zo ja, dan genereer je nieuw getal en probeer je opnieuw toe te voegen. Toon deze key op het scherm.
  • Vraag eenmalig aan de gebruiker een getal, de key, en toon van dit object de RapportStatus (ga er van uit dat de gebruiker steeds een geldige key invoert)
  • Bereken de gemiddelde risicograad van alle objecten in de dictionary
  • Bereken de gemiddelde risicograad van alle objecten die geen IRampGebiedResponder zijn

Volgende opgave was de vaardigheidsproefopdracht voor het 1e zit examen van dit vak (OOP) in juni 2021

Introductie

De sportleraar is nogal slordig. Hij verliest altijd zijn dure drinkbussen. De school heeft daarom jouw diensten ingehuurd om een deel van het materiaal eenvoudig terug te vinden.

Volgende filmpje toont de volledige werking van de applicatie:

Voorbeeld ouput

Context

Een sportleraar kan een rugzak met 2 soorten items vullen:

  1. Niet trackable items (ballen, fluitje, etc).
  2. Drinkbussen die wel getrackt kunnen worden.

De rugzak zelf is ook trackable en zal zelfs kunnen aangeven op welke hoogte deze zich bevindt.

Om het leuk te maken zullen de posities van de trackable items (drinkbussen en rugzakken) telkens veranderen wanneer je ze aanroept.

De spullen die je in een rugzak plaats zullen zich in het progamma niet op dezelfde locatie als de rugzak zelf bevinden, daar alle trackable elementen (rugzak en drink) steeds nieuwe willekeurige locaties krijgen (beeld je in dat de leraar de rugzak vult, ermee naar het park vertrekt, uitlaadt en nu geraken alle spulletjes zoek in het park)

De nodige klassen

GPSLocation

Maak een klasse GPSLocation met volgende zaken:

  • 2 autoproperties, type int, genaamd Latitude en Longitude
  • Een overloaded constructor waarmee je de Latitude en Longitude kunt instellen.
  • Een default constructor die de Latitude en Longitude instelt op telkens een random getal tussen 1 en 9.
  • ToString wordt overschreven en output het object als volgt Latitude: 4, Longitude: 6 (met de getallen natuurlijk de effectieve waarde van de properties van dat object).

AdvancedGPSLocation

Maak een klasse AdvancedGPSLocation. Deze klasse is een GPSLocation met als extra's:

  • Een autoproperty Heigth.
  • Een overloaded constructor die niet naast Latitude en Longitude ook de Height vereist.
  • Een default constructor die standaard de hoogte op 1 zet en de Longitude en Latitude instels op telkens een random getal tussen 1 en 9.
  • ToString toont ook de hoogte nu, bijvoorbeeld: Latitude: 4, Longitude: 6, Height: 5.

ITrackable

Maak een interface ITrackable die één methode GetCurrentLocation bevat, die een GPSLocation als returntype heeft en geen parameters verwacht.

SportItem

Een klasse die enkel ToString overschrijft om de tekst "een eenvoudig sportitem" terug te geven.

Drinkbus

Maak een klasse Drinkbusdie een SportItem is en die de ITrackable interface implementeert.

Een drinkbus zal steeds een nieuwe willekeurige locatie teruggeven bij GetCurrentLocation en zal in ToString de tekst "een drinkbus" teruggeven.

Rugzak

Maak een klasse Rugzak die de ITrackable interface implementeert.

  • De klasse geeft een nieuwe willekeurige AdvancedGPSLocation terug telkens GetCurrentLocation wordt aangeroepen.
  • De klasse heeft een Dictionary van sportitems (de rugzakinhoud) waarbij een string als key van ieder item zal gehanteerd worden.
  • De rugzakinhoud (de Dictionary) is bereikbaar via een full property.
  • De klasse overschrijft ToString zodat informatie als volgt op het scherm verschijnt (onderaan voorbeeld). Tip:Gebruik maximaal de ToString-methode van objecten:
    1. Eerst wordt er een zin gegeven die de huidige locatie van de rugzak toont.
    2. Vervolgens wordt ieder sportitem in de rugzak onder elkaar geschreven (met de tostring van ieder sportitem) en de key.
    3. Indien een sportitem in de rugzak ITrackable heeft dan zal de locatie van het sportitem met GetCurentLocation aangeroepen worden om ook deze informatie te tonen.

Een voorbeeld van de ToString methode output van een rugzak met daarin een drinkbus en een gewoon sportitem:

Rugzak op locatie:  Latitude:1, Longitude:9, Height:1
 Met inhoud:
                mijndrinkbus (Een drinkbus)
                        -Laatste locatie is Latitude:3, Longitude:5
                bal (Een eenvoudig sportitem)
                        -Geen tracker aanwezig

( mijndrinkbus en bal zijn de keys van ieder sportitem)

  • De klasse heeft een methode Visualiseer wanneer deze wordt aangeroepen (geen parameters) zal deze de inhoud van de rugzak én de rugzak zelf op het scherm tonen aan de hand van lettertjes, als volgt:
    • De GPSLocation coordinaten (Latitude en Longitude) stellen de coordinaten voor waar op het scherm het element komt (Latitude geeft de kolom weer, Longitude de rij in de console). Op die plek zet je een letter:
      • r voor een rugzak
      • D voor een drinkbus
    • Merk op dat deze methode telkens een andere 'kaart' zal tonen, daar de GPSlocaties bij iedere aanroep van GetCurrentLocation veranderen (willekeurig).
    • Indien meerdere elementen op dezelfde locatie staan dan wordt gewoon 1 letter getoond (van het laatste element dat op deze plek diende gevisualiseerd te worden).

Een voorbeeld van de uitvoer van de Visualiseer-methode indien we een rugzak hebben op locatie (2,3), met daarin 1 drinkbus op (4,4) en 1 drinkbus op (5,6):


  r

   D
     D

Main

Schrijf een applicatie die als volgt werkt:

  1. Maak een nieuwe rugzak aan.
  2. Vraag aan de gebruiker hoeveel sportitems in de rugzak moeten komen.
  3. Per sportitem dat in de rugzak komt maak je een willekeurig sportitem aan (SportItem of Drinkbus, beide 50% kans om gekozen te worden) en vraag je vervolgens de key waarmee dit sportitem in de rugzak-dictionary moet bewaard worden.
  4. Vervolgens start er een loop die oneindig blijft doorlopen die telkens:
    1. Het scherm leegmaakt
    2. De inhoud van de rugzak op het scherm toont in tekstvorm (via ToString)
    3. Wacht tot de gebruiker op enter duwt.
    4. Het scherm leegmaakt
    5. De Visualiseer methode van de rugzak aanroept.
    6. Wacht tot de gebruiker op enter duwt.

Merk op dat de locaties die in stap 2 getoond worden niet overeenkomen met de locaties die bij de visualisatie in stap 5 worden gebruikt.

Volgende opgave was de vaardigheidsproefopdracht voor examen van dit vak (OOP) in augustus 2021

Inleiding

Knap hoor! We zijn er in geslaagd om eindelijk de aarde voorgoed achter ons te laten en naar nieuwe planeten, ver weg, te reizen. Voor we onze kostbare mensen de ruimte insturen heeft NASA en ESA jou gevraagd om een ruimte-reis-simulatie-pakket te maken. Hopelijk toont jouw applicatie aan dat onze raketten ver genoeg zullen geraken!

Algemene werking

Een ruimteschip zal in de simulatie vertrekken vanop aarde met een gegeven energie aan de start. Voorts wordt er een fictieve route van planeten en tussenstations aangemaakt waar de rakket, soms, kan bijtanken.

De applicatie bestaat uit 2 delen (we gaan verderop in de opgave in op de details van ieder deel)

Deel 1

Eerst wordt de fictieve wereld, de reisweg, aangemaakt die de tussenstops in volgorde toont. Er wordt telkens getoond om wat voor tussenstop het gaat en hoeveel lichtjaren de stop van de aarde verwijderd is. Voorbeeld:

Dit is je reisplan:
        RijkePlaneet:1684
        RijkePlaneet:1703
        RijkePlaneet:1911
        Planeet:3230
        TankTussenstation:4067
        Planeet:5339
        TankTussenstation:5642
        TankTussenstation:6765
        TankTussenstation:7016
        TankTussenstation:9504

Vervolgens toont de applicatie de startsituatie van het ruimteschip (dat altijd met 3000 energie begint):

Dit is je schip:
Je hebt 3000 energie en bent 0 units verwijderd van de aarde.

Deel 2

Vervolgens zal de simulatie een rakket dit traject laten vliegen. Dit gebeurt automatisch. Op plaatsen waar getankt kan worden (RijkePlaneet en TankTussenstation) zal het ruimteschip dit sowieso doen en dan verder vliegen. Wanneer het ruimteschip niet meer genoeg energie heeft om de volgende etappe af te leggen dan stopt de simulatie daar.

Eén lichtjaar reizen kost 1 energie.

Voorbeeld van de uitput (gegeven het voorbeeld in deel 1 hierboven):

De reis gaat van start:

We proberen te reizen naar bestemming 1
Bestemming bereikt. Nog 1316 energie over.
Laten we proberen bij te tanken
Hier werd bijgetankt. Je kreeg 168 energie en staat nu op 1484 energie.

We proberen te reizen naar bestemming 2
Bestemming bereikt. Nog 1465 energie over.
Laten we proberen bij te tanken
Hier werd bijgetankt. Je kreeg 170 energie en staat nu op 1635 energie.

We proberen te reizen naar bestemming 3
Bestemming bereikt. Nog 1427 energie over.
Laten we proberen bij te tanken
Hier werd bijgetankt. Je kreeg 191 energie en staat nu op 1618 energie.

We proberen te reizen naar bestemming 4
Bestemming bereikt. Nog 299 energie over.
Laten we proberen bij te tanken
Hier kan niet bijgetankt worden

We proberen te reizen naar bestemming 5
Dit schip heeft niet genoeg energie daarvoor.
De reis eindigt hier.
Je hebt 299 energie en bent 3230 units verwijderd van de aarde.
Je bent tot aan bestemming 5  geraakt met info: TankTussenstation:4067

Opbouw

Klasse diagram

Volgende diagram toont alle klassen en interfaces die je moet maken:

We beschrijven nu de individuele klassen in de volgorde dat je ze best maakt, uiteraard ben je vrij om een andere vologrde te hanteren

IEnergiegever

Plekken waar het ruimteschip zal kunnen tanken hebben deze interface. De interface is als volgt gedefinieerd:

interface IEnergieGever
{
    long GeefEnergie();
}

Reisbestemming

Deze abstracte klasse wordt gedefinieerd door de afstand (in lichtjaren) van de aarde. Enkele opmerkingen:

  • r is een static Random-object zodat je dat overal kan gebruiken
  • heeft een read-only property AfstandVanAarde
  • heeft een constructor die de instantievariabele afstandVanAarde op een willekeurig getal tussen 400 en 10.000 zet.
  • heeft de IComparable-interface en zal toelaten om (later) een lijst van Reisbestemmingen te sorteren op hun afstand van de aarde (zie verder).
  • heeft de ToString-methode geïmplementeerd en geeft het object weer als [Type object]:[Afstand van Aarde], bijvoorbeeld RijkePlaneet:1911 (zie de output deel 1 bovenaan).
    • Tip: met de GetType().Name property krijg je het type terug zonder de overbode namespace ervoor.

Planeet

Een Planeet is een Reisbestemming. Niets meer.

Ter info: er kan dus niet getankt worden daar het niet de IEnergieGeven-interface heeft.

RijkePlaneet

Een RijkePlaneet is een Planeet die de IEnergieGever-interface heeft. Dit type planeet geeft altijd 10% terug van de afstand tot de aarde. Als de planeet zich dus op 3246 lichtjaren bevind dan zal GeefEnergie 324 energie teruggeven.

TankTussenstation

Een TankTussenstation is een Reisbestemming die de IEnergieGever-interface heeft. Dit type geeft altijd een willekeurige hoeveelheid tussen 500 en 1500 energie terug bij GeefEnergie. Deze waarde is steeds anders telkens de methode wordt aangeroepen.

Wereld

Deze klasse heeft één methode met volgende signatuur:

public static List<ReisBestemming> GenereerWereld(int aantal)

De methode geeft een lijst van Reisbestemming-en terug. De parameter aantal geeft aan hoeveel objecten in de lijst zitten.

De methode plaats een combinatie van Planeet, RijkePlaneet en TankTussenstation-objecten in de lijst. Het kiest willekeurig uit deze 3 types volgende volgende tabel:

  • 25% kans op een Planeet
  • 25% kans op een RijkePlaneet
  • 50% kans op een TankTussenstation

Finaal wordt de lijst gesorteerd waarbij de Reisbestemming die het dichts bij de aarde zit van voor komt, en zo verder. Deze gesorteerde lijst wordt als resultaat van de methode teruggegeven.

Tip: weet je niet hoe je moet sorteren? Geen probleem, geef gewoon de ongesorteerde lijst terug en werk hier op verder! (verlies dus niet te veel tijd met dit stuk)

Ruimteschip

Deze klasse heeft:

  • Een read-only property met private set om de Energie(type: long) bij te houden.
    • Een ruimteschip heeft altijd 3000 energie aan de start.
  • Een instantievariabele afstandVanAarde die bijhoudt hoeveel lichtjaren van de aarde het schip is.
    • Deze start altijd op 0 (daar we op aarde vertrekken).
  • De methode ToSTring die volgende zinnetje teruggeeft Je hebt [Energie] energie en bent [afstandVanAarde] units verwijderd van de aarde. (met natuurlijk de effectieve waarden in plaat van de placeholders met vierkante haken). Voorbeeld Je hebt 294 energie en bent 3489 units verwijderd van de aarde.

De klasse heeft twee speciale methode:

StartReis:

Deze methode geeft een bool terug en aanvaardt een Reisbestemming-object als parameter. De methode werkt als volgt:

  • Het geeft met de bool weer of het schip genoeg energie heeft om naar de reisbestemming te reizen. Een schip kan enkel naar die plek, het doelwit, reizen (de parameter) indien deze minstens evenveel energie heeft als het aantal lichtjaren dat het schip van de planeet verwijderd is.
  • De te reizen afstand is natuurlijk het verschil tussen de afStandVanAarde-instantie van het ruimteschip en de AfstandVanAarde van de bestemming.
    • Als het schip op afstand 2000 is van de aarde en het doelwit is op 2500 afstand van de aarde, dan heeft het schip 500 (2500-2000) energie nodig.
  • Indien het schip niet genoeg energie heeft dan wordt er false teruggegeven nadat volgende zin op het scherm komt: "Dit schip heeft niet genoeg energie daarvoor."
  • Indien het schip wél genoeg energie heeft dan wordt de energie van het schip verminderd met de zonet berekende hoeveel energie die gegeven de afstand tussen beide nodig was (dus 500 in vorig getalvoorbeeld) en worden volgende 2 zinnen getoond: Bestemming bereikt. Nog [Energie} energie over. Laten we proberen bij te tanken.
    • Vervolgens wordt de afstandVanAarde verhoogt met de afgelegde afstand (namelijk de gespendeerde energie)
    • Voor je nu true teruggeeft (om aan te geven dat de reis gelukt is) roep je nu de methode EnergieBijtanken(zie volgende stuk) en geeft de doelwit Reisbestemming mee als argument.

EnergieBijtanken

Deze methode geeft niets terug en aanvaardt een Reisbestemming-object als parameter. De methode werkt als volgt:

  • Indien het meegeven object de IEnergieGeven-interface heeft dan zal het schip hiervan gebruik makenen bijtanken. Het roept hierbij de GeefEnergie-methode van het doelwit op en zal de teruggekregen energie optellen bij z'n huidige energie en vervolgens volgende zinnetje tonen: Hier werd bijgetankt. Je kreeg [verkregenEnergie] energie en staat nu op [Energie] energie. Vervolgens stopt de methode.
  • Indien er op het object niet kan getankt worden verschijnt er Hier kan niet bijgetankt worden en stopt de methode.

Main code

Het wordt nu tijd om alles samen te gooien. Dat gaat hopelijk eenvoudig. Het hoofdprogramma werkt als volgt:

  • Een nieuwe wereld wordt aangemaakt mbv van de GenereerWereld-methode van de Wereld-klasse
  • Ieder tussenstop van deze nieuwe wereld wordt op het scherm getoond (zie voorbeeld uitvoer vooraan de opgave)
  • Een nieuw RuimteSchip-object wordt aangemaakt en ook op het scherm getoond.
  • De reis gaat van start. Hierbij wordt een loop gestart die blijft duren:
    • zolang het schip nog energie heeft (dus meer dan 0),
    • zolang een teller minder is dan het totaal aantal bestemmingen in de wereld
    • zolang het schip nog genoeg energie heeft om naar de volgende bestemming te gaan
    • In de loop houdt je met een teller bij aan welke bestemming je bent en verhoogt deze teller telkens het schip naar de volende bestemming reist.
  • Finaal toon je tot waar het schip is geraakt.

Volgende voorbeeldoutput toont nogmaals het geheel:

Dit is je reisplan:
        TankTussenstation:1401
        RijkePlaneet:1519
        TankTussenstation:2390
        Planeet:4167
        TankTussenstation:5736
        TankTussenstation:6386
        Planeet:6426
        Planeet:6466
        RijkePlaneet:9646
        RijkePlaneet:9872

Dit is je schip:
Je hebt 3000 energie en bent 0 units verwijderd van de aarde.

De reis gaat van start:

We proberen te reizen naar bestemming 1
Bestemming bereikt. Nog 1599 energie over.
Laten we proberen bij te tanken
Hier werd bijgetankt. Je kreeg 788 energie en staat nu op 2387 energie.

We proberen te reizen naar bestemming 2
Bestemming bereikt. Nog 2269 energie over.
Laten we proberen bij te tanken
Hier werd bijgetankt. Je kreeg 151 energie en staat nu op 2420 energie.

We proberen te reizen naar bestemming 3
Bestemming bereikt. Nog 1549 energie over.
Laten we proberen bij te tanken
Hier werd bijgetankt. Je kreeg 838 energie en staat nu op 2387 energie.

We proberen te reizen naar bestemming 4
Bestemming bereikt. Nog 610 energie over.
Laten we proberen bij te tanken
Hier kan niet bijgetankt worden

We proberen te reizen naar bestemming 5
Dit schip heeft niet genoeg energie daarvoor.
De reis eindigt hier.
Je hebt 610 energie en bent 4167 units verwijderd van de aarde.
Je bent tot aan bestemming 5  geraakt met info: TankTussenstation:5736

Volgende opgave was de vaardigheidsproefopdracht voor het inhaalexamen van dit vak (OOP) in augustus 2021

Intro

Ultimate Beast Master is een populaire, Amerikaanse, tv-show waarin topatleten een ongelooflijk zware obstakelkoers zo snel mogelijk moeten afleggen. Enkel de sterkste, meest atletische mannen en vrouwen slagen er in om het einde van het parkoers te bereiken.

Als organisator van de tvshow wordt je gevraagd om een systeem te ontwikkelen waarin een parkoers kan gegenereerd worden waarvan ook de moeilijkheidsgraad kan berekend worden.

Basisklassen

Eerst gaan we de nodige toestellen maken om dan later een parkoers van toestellen aan te leggen volgens volgende klasse:

IDodelijk interface

Deze interface is als volgt:

interface IDodelijk
{
    public bool VeiligheidsActief { get; }
}

Toestellen die mogelijk dodelijk zijn, zullen deze interface hebben.

Toestel-klasse

Alle toestellen die in een parkoers voorkomen zullen van deze klasse overerven.

Het gaat om een abstracte klasse met:

  • Een protected instantievariabele tekenChar dat standaard o is. Dit teken zal gebruikt worden om het toestel te visualiseren.
  • Methode Teken die het tekenChar op de huidige plek in de console schrijft (met Write).
  • Een abstracte methode BerekenMoeilijkheidsgraad die een int als resultaat heeft en geen parameters aanvaardt.

Trampoline-klasse

  • Is een Toestel.
  • Heeft een default constructor:
    • die de moeilijkheidsgraad van het object op een willekeurige waarde geeft van 1 tot en met 4 (dat vervolgens in BerekenMoeilijkheidsgraad zal gebruik worden).
    • het tekenChar wordt op een t ingesteld.

UltraTrampoline-klasse

Een Ultratrampoline is een trampoline die gevaarlijk is. Er is geen mogelijkheid om netten rond het ding te plaatsen, dus deze klasse verdient zeker de term dodelijk.

De klasse:

  • Is een Trampoline.
  • Heeft een default constructor die het tekenChar op T instelt.
  • Implementeert de IDodelijk interface en zal altijd false teruggeven bij VeiligheidsActief (de beveiligingsinstelling van een Ultratrampoline kan dus nooit anders zijn).
  • De moeilijkheidsgraad van deze klasse is "10 + de waarde die in de default constructor van de Trampolinewerd berekend".

Klimmuur-klasse

Een klimmuur wordt gedefinieerd door het aantal handvaten die de muur heeft.

De klasse:

  • Is een Toestel.
  • Heeft een overloaded constructor waarmee het aantal klimelementen (handvaten) kan ingesteld worden via een meegegeven int. De tekenchar is een m.
  • De moeilijkheidsgraad is 3 indien er een even aantal klimelementen zijn, anders is deze 4.

DeadWall-klasse

De dodelijk deadwall is een klimmuur waar eventueel veiligheidsnetten onder kunnen geplaatst worden zodat deelnemers die vallen opgevangen kunnen worden.

  • Is een Klimmuur.
  • Heeft een default constructor waarmee kan ingesteld worden of de wall met veiligheidsnetten werkt of niet (aan de hand van een meegegeven bool). Een DeadWallheeft altijd 21 klimelementen. tekenchar is M.
  • Implementeert de IDodelijke interface en zal de bool teruggeven in VeiligheidsActief die in de constructor werd meegegeven.
  • De moeilijkheidsgraad is 5 indien er veiligheidsnetten zijn, anders is deze 10.

Parkoer-klasse

Deze klasse beschrijft een volledig parkoers van toestellen die de speler zal moeten bedwingen in de Ultimate Beast Master tv-show.

  • Deze klasse heeft een lijst van toestellen.
  • Een constructor die 2 parameters aanvaard, namelijk het aantal toestellen (x) waaruit het parkoers bestaat en een bool (y) om aan te geven of dodelijke toestellen met beveiliging moeten worden toegevoegd:
    • De constructor zal x willekeurige toestellen aan de lijst toevoegen. Ieder toestel (Trampoline, UltraTrampoline, Klimmuur en Deathwall) heeft even veel kans om gekozen te worden.
    • Indien een Klimmuur wordt gekozen dan krijgt deze een willekeurig aantal klimtoppen tussen 10 en 50.
    • Indien een Deathwall wordt gekozen dan wordt de bool y meegeven om aan te geven of er wel of geen veiligheidsnet moet toegevoegd worden.
  • De klasse heeft een methode VerwijderDodelijke. Wanneer deze methode wordt aangeroepen dan worden alle IDodelijke toestellen uit de lijst verwijderd.
  • De klasse een private methode BerekenMoeilijkheidsgraad die een int teruggeeft. De moeilijkheidsgraad van een parkoers bestaat uit de som van de moeilijkheidsgraden van alle toestellen in de lijst.
  • De klasse heeft een methode ToonParkoers . Deze methode zal alle toestellen na elkaar op het scherm tonen waarbij de achtergrond van ieder element rood of groen zal zijn:
    • Rood indien het een IDodelijk toestel is (ongeacht of er veiligheidsnetten aanwezig zijn), groen in de andere gevallen.
    • Nadien wordt ook nog de totale moeilijkheidsgraad van het parkoers getoond.
    • Voorbeeld output:

Hoofdprogramma

De hoofdapplicatie bestaat uit volgende stappen

  • Eerst worden er 5 willekeurige Parkoer-objecten aangemaakt en in een lijst bewaard.
  • Vervolgens worden alle Parkoer-objecten in de lijst gevisualiseerd.
  • De gebruiker kiest met welk parkoer hij verder wilt gaan.
  • Er wordt nu aan de gebruiker gevraagd of de dodelijke toestellen uit het parkoers moeten worden gehaald. Indien ja, wordt dit gedaan (alle IDodelijke objecten worden uit de lijst van het gekozen Parkoer object gehaald).
  • Finaal wordt het gekozen parkoers nogmaals getoond, al dan niet zonder de dodelijke toestellen.

Voorbeeld output:

Volgende opgave was de vaardigheidsproefopdracht voor het inhaalexamen van dit vak (OOP) in augustus 2022

Inleiding

Na vele jaren vloeken op de roosters, heeft de firma "Roosteren-maar" eindelijk besloten hun roostersoftware aan te passen. Aan jou deze taak om een deftig roosterprogramma te schrijven. Veel succes gewenst!

Basisklassen

De applicatie bestaat vooreerst uit een aantal elementaire klassen die gebruikt zullen worden om een dagrooster op te stellen.

Vak

Een vak heeft een titel (type string), een duur (int) en een toegewezen lector (type Lector, zie verder). Voorzie de nodige properties hiervoor, wetende dat: a) de duur enkel 1 of 2 uur kan zijn, niets anders. Controleer hier op. b) de titel standaard "Onbekend" is.

De klasse override ToString en toont een object als volgt: Titel - Lector (bijvoorbeeld: OOP - Jansens).

Standaard heeft een nieuw gemaakt Vak-object een Lector-object met als naam "Nog Toe te wijzen``.

Lector

Een lector heeft een naam (string) en een maximaal aantal uur (maxUur, type int) dat de persoon les kan geven op een week. De waarde van maxUur is altijd 6. Voorzie dit via een readonly property.

Rooster

Deze klasse heeft een private lijst van Vakken die standaard leeg is. Deze lijst stelt de dagrooster voor en bevat alle vakken van de dag.

De klasse heeft een property VrijeUren: deze geeft een int terug en bevat de totale som van alle duren van de vakken in de lijst. Als er dus 2 vakken in de lijst staan, 1 van 1 uur duur, de andere van 2uur duur, dan zal deze property 3 teruggeven (1+2).

De klasse heeft voorts volgende methoden:

a) ToonRooster: deze methode zal ieder vak in de lijst via ToString op het scherm onder elkaar tonen. Indien een vak 2uur duurt dan zal het vak 2x onder elkaar getoond worden. Voorbeeld output:

OOP -Jansens
Programming Principles - DuBru
Programming Principles - DuBru
Java - Gates

b) VoegVakToe: deze methode aanvaardt een Vak-object. Dit object wordt aan de lijst toegevoegd indien dat kan. Een rooster kan nooit meer dan 8uur totale duur bevatten (gebruik VrijeUren hiervoor). Als je dus een vak van 2uur probeert probeert toe te voegen terwijl de roosterlijst reeds 7uur bevat, dan zal dit niet lukken en wordt er een RoosterException teruggegooid (maak deze Exception klasse zelf aan).

c) VerbeterRooster: wanneer deze methode wordt aangeroepen dan zal de lijst met vakken gesorteerd worden volgens 1° hun naam alfabetisch 2° hun duur (lange vakken eerst).

d) WijsLectorToe : deze methode aanvaart een index x (int) en een Lector object. De methode zal de meegegeven lector toewijzen (en de bestaande overschrijven) aan het vak in lijst met de index "x" die je als parameter hebt meegeven. Indien er geen vak bestaat met die index dan verschijnt er een foutboodschap.

Specialisatieklassen

LaboVak

LaboVak is een Vak. Deze zal als ToString aangeroepen wordt vooraan de titel nog "(labo)" toevoegen. Bijvoorbeeld: (labo) OOP - Jansens.

Deze heeft een constructor waar je een Vak-object aan kan meegeven. De informatie van dit object wordt gebruikt om het LaboVak in te stellen (je neemt dus de duur, lector en titel over uit het object en wijst dit toe aan het huidige LaboVak-object).

Een labovak kan ook 3uur duren en is dus niet beperkt tot 1 of 2 uur duur.

Voeg voorts aan de klasse Rooster een nieuwe methode ToonLabos toe. Deze zal net als ToonRooster het rooster visualiseren, maar enkel voor vakken van het type LaboVak.

HalfTijdseLector

Een halftijdse lector kan maar de helft van een gewone lector aan maximale uren ingeroosterd worden.

Hoofdprogramma

In het hoofdprogramma wordt een lijst van 3 lectoren aangemaakt (kies zelf de namen), waarvan 1 een halftijdselector is.

Maak een applicatie die bovenstaande klassen gebruikt door aan de gebruiker een menu aan te bieden met volgende items (bepaal zelf hoe je de input van de gebruiker gaat verwerken):

  • Toon rooster: de huidige rooster wordt op het scherm getoond.
  • Voeg vak toe: de gebruiker dient vervolgens de naam van het vak te geven, de duur, en welke lector (voor de lector geeft hij gewoon de index in, 0, 1 of 2 en gebruik je vervolgens de lector in de lijst die je aanmaakte aan de start van het programma). Indien de gebruiker bij duur 3 ingeeft dan wordt dit vak automatisch een labovak.
  • Verbeter rooster: dit zal de VerbeterRooster methode aanroepen.
  • Toon labo's: Enkel de LaboVak-objecten in het rooster worden op het scherm getoond.

WeekRooster

Maak een klasse WeekRooster. Deze klasse heeft 5 lijsten van het type Vak, 1 voor iedere weekdag. De klasse heeft een default constructor en zal een random rooster aanmaken door de 5 week-lijsten te vullen met telkens 4 willekeurige Vak-objecten (20% kans op een LaboVak).

De klasse heeft volgende methode:

  • Toon rooster: deze zal de rooster per dag onder elkaar op het scherm tonen en voor iedere lijst de dag tonen.

Voeg aan het hoofdmenu de optie Toon weekrooster. Deze zorgt ervoor dat een WeekRooster object wordt aangemaakt dat vervolgens op het scherm wordt getoond.

Volgende opgave was de vaardigheidsproefopdracht voor het examen van dit vak (OOP) in juli 2023

Introductie

Je firma werd gevraagd om voor een grote rederij een manifest-generator te maken. Wanneer een schip gevuld wordt met dozen en containers dan moet de kapitein ook steeds een manifest hebben. Op dit document (=het manifest) staat welke lading het schip aan boord heeft.

Het bedrijf biedt verschillende soorten dozen aan die de gebruiker kan gebruiken om z’n pakket mee te verzenden. Afhankelijk van het type doos zal de kostprijs anders zijn. Voorts biedt het bedrijf een SecureContainer aan. Dit is een doos die kan verzegeld worden. Een verzegelde container heeft als extra eigenschappen dat het ten eerste z’n kostprijs niet zal teruggegeven (zodat dieven niet weten wat de waarde van de inhoud is) en het zal ook onthouden hoe vaak externen de kostprijs van de doos wensen te weten komen.

Schema

Deel 1: Klassen-diagram (6 punten)

Maak een nieuw project aan en zorg ervoor dat volgende klassen diagram wordt toegepast:

Schema

Klasse Doos:

abstract class Doos
{       
    public Doos(int id)
    {
        ID = id;
    }
    public int ID { get; private set; }
    public int Gewicht { get; set; }
    public string Inhoud { get; set; }
    
    public virtual int KostPrijs
    {
        get { return Gewicht * 10; }
    }
}

Klasse Container

De kostprijs van een Container is de “kostprijs van een Doos + 5”.

PostDoos

De kostprijs van een PostDoos is altijd 10.  

Interface ISafe

ISafe is een interface ISafe bevat 2 methoden: VerzegelInhoud:

  • vereist geen parameters en geeft niets terug. GeefAntalLeesAttempts:
  • vereist geen parameters en geeft een int terug.

Klasse SecureContainer

GeefAantalLeesAttemps() geeft terug hoe vaak de KostPrijs van het object werd uitgelezen via de get’r. VerzegelInhoud(): vanaf je dit aanroept zal de container verzegeld zijn. Een verzegelde container zal 0 als Kostprijs teruggeven in plaats van de effectieve kostprijs. (eens verzegeld kan dit niet meer ongedaan gemaakt worden).

Klasse DHLSchip

Heeft een Lijst van Dozen genaamd vrachtRuim die naar buiten toe beschikbaar is via public get, maar private set.

Heeft een methode VoegDoosToe:

  • vereist 1 parameter van het type Doos en geeft een bool terug.
  • zal de doos in de parameter toevoegen aan het Vrachtruim, maar enkel indien het totale gewicht van alle dozen in het vrachtruim niet boven de 10 komt (indien de huidige dozen dus reeds samen 8 wegen, en de nieuw toe te voegen weegt 3 dan zal de doos niet toegevoegd worden):
  • Indien een doos te zwaar is zal er een Exception opgeworpen worden met de boodschap: “Te zwaar. Doos niet toegevoegd”.
  • Indien de doos wel werd toegevoegd zal er true terug gegeven worden

Heeft een methode ToonManifest:

  • Vereist geen parameters en geeft niets terug.
  • Deze methode aanroepen zal resulteren in het afdrukken van het manifest naar het scherm. De output hiervan wordt besproken in een later deel van deze opgave.  

Deel 2: Manifest output DHLSchip (4 punten)

De ToonManifest()-methode van het DHLSchip zal de volgende output genereren:

  • Hoofding: Naam van het schip
  • Midden: informatie van de dozen.
    • Start telkens met een minteken, gevolg door de Inhoud van de doos, het gewicht en de kosptrijs ervan.
    • Indien de doos een ISafe interface bevat dan zal er achteraan de informatie van de doos ook de uitvoer van GeefAantalLeesAttempts getoond worden (voorafgegaan door 5 ‘hekjes’ (#)
  • Einde:
    • Het totaalgewicht van alle dozen samen wordt getoond
    • De totale prijs van alle dozen wordt getoond:
      • Per ISafe doos zal er een standaard prijs van 10 worden bijgeteld
      • Indien de doos Verzegeld is dan zal deze doos dus 0+10 kosten.
      • Een niet verzegelde doos zal dan z’n effectieve prijs + 10 kosten.

Voorbeeld output Manifest van een schip genaamd ‘The flying pegassus’ (Strips zit in een PostDoos, Goud zit in een verzegelde SecureContainer waarvan de kostprijs 1keer werd uitgelezen en Auto zit in een Container):

The flying pegassus-Manifest
*****************
-Strips 	 3       	30
-Goud  	 2      	0#####1
-Auto  	 3       	35
-------------------
TOTAAL GEWICHT=          8
TOTAAL PRIJS=            75

Deel 3 : Boot manager (4p, waarvan)

Maak nu een console-applicatie die toelaat om dozen op een schip te zetten en vervolgens het manifest te tonen. (screenshots van het programma worden achteraan deze opgave getoond)

Als volgt:

Deel 3a: Bootnaam (0,5 punt)

Eerst wordt de schipsnaam eenmalige gevraagd en wordt een nieuw DHLSchip object met die naam aangemaakt. Vervolgens verschijnt het hoofdmenu.

Deel 3b: HoofdMenu (0,5 punt)

Een menu wordt getoond dat steeds zal terugkeren tot de gebruiker het programma afsluit. De gebruiker kan 3 zaken kiezen in het menu a. Een doos aanmaken en op het schip zetten (zie deel 3c). b. Het schip-manifest op het scherm afdrukken (zal ToonManifest van het schip aanroepen). c. Het programma afsluiten.

Deel 3c: Doos aanmaken en toevoegen menu (3 punten)

Dit menu bestaat uit 3 stappen: a. Type doos vragen b. Gewicht en inhoud vragen c. (Optioneel) Doos verzegelen vragen Vervolgens zal de doos aan het vrachtruim van het schip toegevoegd worden (op voorwaarde dat het schip niet over z’n capaciteit van 10 komt uiteraard)

Stap 1 Indien de gebruiker voor optie a in het hoofdscherm kiest dan zal eerst gevraagd worden wat voor doos moet gemaakt worden: a. PostDoos b. Container c. SecureContainer

Stap 2 De volgende stap wordt gevraagd om de inhoud en het gewicht van de doos. Deze informatie wordt aan de gekozen doos toegevoegd.

Stap 3 (optioneel) Indien de gebruiker een SecureContainer heeft aangemaakt dan wordt in deze stap gevraagd of de gebruiker de doos wenst te vergrendelen. Bij ja wordt de VerzegelInhoud-methode aangeroepen.

Ongeacht stap 3 zal nu de doos worden toegevoegd, op voorwaarde dat er nog plek op het schip is.  

Deel 4: Extra’s (6p, waarvan)

5.1 Mooiere output voor Doos (2 punten)

Zorg ervoor dat je een doos-object naar de WriteLine()-methode kunt sturen als volgt Console.WriteLine(myDoos);, en er een mooie output op het scherm verschijnt, namelijk het doostype, gewicht, kostprijs en inhoud. Bij een SecureContainer wordt er ook nog getoond of de doos is verzegeld of niet.

Voorbeelden: Output bij Postdoos: PostDoos , Gewicht: 2, Prijs: 20, Strips Output bij Container: Container , Gewicht: 4, Prijs: 45, Strips Output bij verzegelde SecureContainer SecureContainer , Gewicht: 2, Prijs: 0, Strips, Verzegeld=ja

5.2 VergrendelSnel-methode (2 punten)

Maak in je hoofdprogramma een methode die toelaat om snel alle SecureContainers in een schip te vergrendelen. De methode aanvaard één parameter van het type DHLSchip. Biedt in je applicatie in het hoofdmenu (3b) als 4e optie “Vergrendel alles” om dit voor de gebruiker te doen.

5.3 Bootmanager (2 punten)

Voorzie de mogelijkheid om de gebruiker te laten ingeven om dozen in het VrachtRuim te:

  • Verwijderen
  • Verplaatsen
  • Vervangen
  • Dupliceren  

Appendix: Voorbeeld programma output

Naam schip, gebruiker voert The flying Pegassus in: Schema

Hoofdscherm: Schema Gebruiker kiest a in hoofdscherm: Schema

In stap 2 heeft de gebruiker “strips” en 3 (als gewicht) ingevoerd. Schema

Gebruiker kiest “j” (om te verzegelen): Schema

Gebruiker komt terug op het hoofdscherm en kiest nu optie b om het manifest te tonen: Schema

All In One

Volgende hoofdstukken bevatten grotere projecten waarin wordt getracht meerdere technieken uit de vorige hoofdstukken te combineren. Het doel van dit hoofdstuk is tweeledig:

  1. Op een andere manier tonen hoe specifieke C# elementen in een 'realistische' programma's kunnen gebruikt worden
  2. Aantonen dat, indien je tot hier bent geraakt, je al een aardig hoop skills hebt om grote, complexe applicaties te maken die verder gaan dan de standaard oefeningen die je na ieder hoofdstuk hebt gemaakt.

Text-gebaseerd spel mbv OO technieken

De prijs voor meest sexy titel gaan we niet winnen. Maar naar de aloude traditie van de klassieke tekst-gebaseerde adventure-games (zie hier ) zullen we een eenvoudig object georiënteerd framework maken dat ons toelaat snel onze eigen games te maken. De nadruk van dit artikel ligt daarbij niet tot het creëren van een perfecte imitatie, maar wel om aan te tonen dat met een beetje object georiënteerde inzichten we tot zeer propere, herbruikbare én onderhoudbare code kunnen komen.

We maken uiteraard het spel in een Console-applicatie.

Main

We willen de Main() methode van Program.cs zo leeg mogelijk laten. Daarom zullen we de meeste functionaliteit verpakken in een klasse GameManager. Het enige dat we dan nog hoeven te doen in onze main is een loop starten die steeds 3 zaken zal doen:

  1. Huidige locatie beschrijven
  2. Aan gebruiker tonen welke acties hij kan uitvoeren
  3. Gewenste actie van gebruiker verwerken en uitvoeren

In code behelst dit:

static void Main(string[] args)
{
    Console.WriteLine("Welkom bij AP Adventure. Een avontuur voor moedige en minder moedige studenten. Ben je er klaar voor?");
 
    GameManager theGame= new GameManager();
 
    //Start gameloop
    while(!theGame.Exit)
    {
        //Beschrijf kamer
        theGame.DescribeLocation();
        //Toon acties
        theGame.ToonActies();
        //Lees actie uit
        theGame.VerwerkActie();
 
    }
}

Een bool property Exit binnen het GameManager object zal ons toelaten om de loop te stoppen wanneer het spel wordt beëindigd.

GameObject

Doorheen de verschillende locaties zullen elementen te vinden zijn. We beschrijven deze als GameObjects:

class GameObjects
{
    public string Name { get; set; }
    public string Description { get; set; }
 
    public void Describe()
    {
        Console.WriteLine(Name+","+Description+".");
    }
}

Location

De gebruiker kan van locatie naar locatie gaan. Een locatie bestaat uit een aantal zaken:

  • Een beschrijving en titel
  • Een lijst van GameObjecten (items) die zich op deze locatie bevinden
  • Een lijst van Exits, namelijk de richtingen waar de gebruiker naar toe kan gaan die aansluiten op een andere locatie
  • Een eerste versie van onze locatie klasse is dan:
class Location
{
    public Location()
    {
        Exits = new List<Exit>();
        ObjectsInRoom= new List<GameObjects>();
    }
    public string Title { get; set; }
    public string Description { get; set; }
 
    public List<Exit> Exits { get; set; }
    public List<GameObjects> ObjectsInRoom { get; set; }
 
    public void Describe()
    {
        Console.WriteLine(Title + "\n*********");
        Console.WriteLine(Description  );
        Console.WriteLine("Voorts zie je ook nog:");
        foreach (var objects in ObjectsInRoom)
        {
            objects.Describe();
        }
        Console.WriteLine("\n*********");
    }
 
    //...
}

Merk op dat we gebruik maken van List<> ipv arrays.

Exit

Iedere exit in een locatie definieert minstens 2 zaken:

  • De richting waar deze uitgang zich bevindt (Noord, Oost,Zuid, West)
  • Een referentie naar het locatie-object waar deze uitgang toegang tot verschaft

We krijgen dus al:

class Exit
{
    public Exit()
    {
        NeedsObject= new List<GameObjects>();
    }
    public Directions ExitDirection { get; set; }
    public Location GoesToLocation { get; set; }

    public List<GameObjects> NeedsObject { get; set; }
 
    //...
}

Waarbij Directions een eigen gemaakt enum-type is:

enum Directions
{
    North, South, West, East
}

Van locatie veranderen

Binnen de locatie klasse voegen we een methode toe die de GameManager kan gebruiken om te weten te komen naar welke locatie de gebruiker gaat. De methode aanvaardt een Direction (i.e. de richting waarin de gebruiker wenst te gaan) en zal een referentie naar het location-object teruggeven waarnaar de gebruiker zal bewegen. Indien de richting waarin hij wenst te bewegen niet geldig is dan tonen we dit op het scherm:

public Location GetLocationOnMove(Directions direction)
{
    foreach (var exit in Exits)
    {
        if (exit.ExitDirection == direction)
        {
 
            return exit.GoesToLocation;
        }
    }
    Console.WriteLine("Dat is geen geldige richting");
    return this;
 
}

Wanneer dus een exit wordt gevonden in de Exits lijst die voldoet aan de meegegeven Direction dan geven we een referentie terug naar de bijhorende locatie (GoesToLocation). Wordt er geen exit gevonden en bereiken we dus het einde van de foreach lus dan verschijnt de tekst op het scherm en geven we een referentie terug naar de huidige locatie.

GameObjects als vereisten om exit te gebruiken

Stel nu dat we soms willen dat een bepaalde locatie pas bereikt kan worden indien de gebruiker reeds een bepaald GameObject in zijn bezit heeft. Hiervoor moeten we 2 zaken aanpassen:

  • We beschrijven in de Exit klasse welk object(en) nodig zijn om deze exit te mogen gebruiken
  • We controleren of de speler het GameObject heeft wanneer deze naar een nieuwe locatie wil gaan mbv de GetLocationOnMove() methode.

De nieuwe, volledige Exit klasse wordt dan:

class Exit
{
    public Exit()
    {
        NeedsObject = new List<GameObjects>();
    }
    public Directions ExitDirection { get; set; }
    public Location GoesToLocation { get; set; }
 
    public List<GameObjects> NeedsObject { get; set; }
 
    public bool TestPassCondition(List<GameObjects> playerInventory)
    {
        int passCount = 0;
        for (int i = 0; i < NeedsObject.Count; i++)
        {
            if (playerInventory.Contains(NeedsObject[i]))
                passCount++;
        }
 
        if (passCount == NeedsObject.Count)
            return true;
        else
        {
            return false;
        }
    }
}

In deze ietwat knullige code tellen we dus of de speler alle GameObjecten in z’n inventory heeft (playerInventory) nodig om deze exit te gebruiken.

Deze methode TestPassCondition gebruiken we nu in de GetLocationOnMove()-methode in de Location klasse om te bepalen of de exit mag gebruikt worden. De methode wordt dan:

public Location GetLocationOnMove(Directions direction, List<GameObjects> playerInventory )
{
    foreach (var exit in Exits)
    {
        if (exit.ExitDirection == direction)
        {
            if(exit.TestPassCondition(playerInventory))
                return exit.GoesToLocation;
            else
            {
                Console.WriteLine("Je kan hier niet langs (je hebt niet alle vereiste items).");
                return this;
            }
        }
    }
    Console.WriteLine("Dat is geen geldige richting");
    return this;
 
}

GameManager

Rest ons nu enkel nog de GameManager klasse te maken. Ruw gezien is deze als volgt:

class GameManager
{
 
    public GameManager()
    {
        InitGame();
    }
    private  Location currentLocation = null;
    public bool Exit { get; set; }
 
    public void DescribeLocation()
    {
        //...
    }
 
    public  void VerwerkActie()
    {
        //...
    }
 
    public  void ToonActies()
    {
        //...
    }
 
    private  List<Location> GameLocation = new List<Location>();
    private List<GameObjects> Objects = new List<GameObjects>();
    private List<GameObjects>  playerInventory= new List<GameObjects>();
    private  void InitGame()
    {
        //...
    }
}

Wat ogenblikkelijk opvalt zijn:

  • De 3 publieke methoden DescribeLocation,VerwerkActie en ToonActies
  • Een instantievariabelen currentLocation die een referentie bijhoudt naar de huidige locatie van de speler
  • 3 lijsten met daarin de objecten die de speler heeft (playerInventory), alle objecten in het spel (Objects) en alle locaties in het spel (GameLocation)
  • Een InitGame() methode waarin we alle gameobjecten, exits en locaties zullen aanmaken bij aanvang van het spel
  • Een bool Exit zodat de externe gameloop weet wanneer het spel gedaan is

We zullen nu de afzonderlijke methoden invullen:

DescribeLocation()

public void DescribeLocation()
{
    this.currentLocation.Describe();
}

VerwerkActie()

string actie = Console.ReadLine();
bool error = false;
if (actie == "n")
    currentLocation = currentLocation.GetLocationOnMove(Directions.North, playerInventory);
else if (actie == "o")
    currentLocation = currentLocation.GetLocationOnMove(Directions.East, playerInventory);
else if (actie == "w")
    currentLocation = currentLocation.GetLocationOnMove(Directions.West, playerInventory);
else if (actie == "z")
    currentLocation = currentLocation.GetLocationOnMove(Directions.South, playerInventory);
else if (actie == "e")
    Exit = true;
else
{
    error = true;
}
Console.Clear();
if(error)
    Console.WriteLine("Onbekende actie");

We laten de speler dus toe door n,o,w,z in te typen dat gecontroleerd wordt naar welke nieuwe locatie zal gegaan worden. We passen hierbij de currentLocation property van de GameManager aan naar de, al dan niet nieuwe, locatie.

ToonActies()

public  void ToonActies()
{
    Console.WriteLine("Mogelijke acties: (typ bijvoorbeeld n indien u naar het noorden wil)");
    Console.WriteLine("n= noord");
    Console.WriteLine("o= oost");
    Console.WriteLine("z= zuid");
    Console.WriteLine("w= west");
 
    Console.WriteLine("e=exit");
 
}

InitGame()

In deze methode definiëren nu de volledige spelinhoud. Wil je dus bijvoorbeeld dit spel uitbreiden met extra kamers en objecten, dan doe je dat in deze methode. Ter illustratie tonen we eerst hoe we 2 locaties aanmaken en deze aan elkaar hangen mbv de Exits (waarbij kamer één zich ten zuiden van kamer 2 bevindt)

private  void InitGame()
{
    //Maak Locaties
    Location l1 = new Location()
    {
       Title = "De poort",
       Description = "Je staat voor een grote grijze poort die op een kier staat. Rondom je is prikkeldraad, je kan enkel naar het noorden, door de poort gaan. "
    };
 
    Location l2 = new Location()
    {
       Title = "Receptie",
       Description = "De receptie....veel blijft er niet meer over van wat eens een bruisende omgeving was. Hier en daar zie je skeletten van , waarschijnlijk, enkele studenten. Een grote poort staat op een kier naar het zuiden. Je ziet twee deuren aan de westelijke en noordelijke zijde."
    };
 
    //Place exits
    l1.Exits.Add(new Exit() { ExitDirection = Directions.North, GoesToLocation = l2 });
 
    l2.Exits.Add(new Exit() { ExitDirection = Directions.South, GoesToLocation = l1 });
 
    //Voeg locatie toe
    GameLocation.Add(l1);
    GameLocation.Add(l2);
 
    currentLocation = l1;
}

Vergeet niet op het einde de 2 kamers toe te voegen aan de GameLocation lijst van de GameManager, alsook in te stellen wat de beginkamer is.

GameInit met GameObject als conditie om kamer in te kunnen

Stel dat we even later in een kamer een sleutel plaatsen die als conditie dient om een andere kamer te kunnen openen. We schrijven dan in de GameInit() methode:

    Location l6 = new Location()
    {
        Title = "Gang",
        Description = "Een brede gang waar makkelijk 5 mensen schouder aan schouder door kunnen. Zowel in het oosten als het westen zie je een deur."
    };
 
    Location l7 = new Location()
    {
        Title = "Computerruimte",
        Description = "Eindelijk; je hebt het gehaald. De plek waar iedereen naar toe wil: het computerlabo!"
    };
 
    //Place objects in rooms
    GameObjects keytol7 = new GameObjects() { Description = "Verroest en groot", Name = "Sleutel" };
    l5.ObjectsInRoom.Add(keytol7);
    //...
 
    l6.Exits.Add(new Exit() { ExitDirection = Directions.West, GoesToLocation = l4 , NeedsObject= new List<GameObjects>(){keytol7}});
    l6.Exits.Add(new Exit() { ExitDirection = Directions.East, GoesToLocation = l7 });
 
    //Voeg locatie toe
    //..
    GameLocation.Add(l6);
    GameLocation.Add(l7);

Een volledige GameInit ter illustratie

We hebben nu de belangrijkste onderdelen geschreven. We tonen daarom een iets uitgebreider spel, demo zeg maar, waar in we alles gecombineerd in actie zien:

private  void InitGame()
{
    //Maak Locaties
    Location l1 = new Location()
    {
        Title = "De poort",
        Description = "Je staat voor een grote grijze poort die op een kier staat. Rondom je is prikkeldraad, je kan enkel naar het noorden, door de poort gaan. "
    };
 
    Location l2 = new Location()
    {
        Title = "Receptie",
        Description = "De receptie....veel blijft er niet meer over van wat eens een bruisende omgeving was. Hier en daar zie je skeletten van , waarschijnlijk, enkele studenten. Een grote poort staat op een kier naar het zuiden. Je ziet twee deuren aan de westelijke en noordelijke zijde."
    };
 
    Location l3 = new Location()
    {
        Title = "Koffieruime",
        Description = "Je staat in de koffieruimte achter de receptie. Menig pralinetje is hier vroeger met veel gusto opgesmikkeld. Een lege pralinedoos is het enige bewijs dat het hier ooit gezellig was. Een deur is de enige uitgang uit deze kamer naar het oosten."
    };
 
    Location l4 = new Location()
    {
        Title = "Tuin",
        Description = "Het is duidelijk herfst. Een kale boom en vele bruine bladeren op de grond...mistroosteriger kan eigenlijk niet. Je ziet een deur in het zuiden en in het westen en een grote klapdeur naar het noorden."
    };
 
    Location l5 = new Location()
    {
        Title = "Cafetaria",
        Description = "Ooit was dit een bruisende locati: veel eten, geroezemoes en licht door de grote ruiten. Nu enkel stof en lege tafel. Enkel een klapdeur is zichtbaar naar het zuiden."
    };
 
    Location l6 = new Location()
    {
        Title = "Gang",
        Description = "Een brede gang waar makkelijk 5 mensen schouder aan schouder door kunnen. Zowel in het oosten als het westen zie je een deur."
    };
 
    Location l7 = new Location()
    {
        Title = "Computerruimte",
        Description = "Eindelijk; je hebt het gehaald. De plek waar iedereen naar toe wil: het computerlabo!"
    };
 
    //Place objects in rooms
    GameObjects keytol7 = new GameObjects() { Description = "Verroest en groot", Name = "Sleutel" };
    l5.ObjectsInRoom.Add(keytol7);
 
    //Place exits
    l1.Exits.Add(new Exit() { ExitDirection = Directions.North, GoesToLocation = l2 });
 
    l2.Exits.Add(new Exit() { ExitDirection = Directions.South, GoesToLocation = l1 });
    l2.Exits.Add(new Exit() { ExitDirection = Directions.West, GoesToLocation =  l3});
    l2.Exits.Add(new Exit() { ExitDirection = Directions.North, GoesToLocation = l4 });
 
    l3.Exits.Add(new Exit() { ExitDirection = Directions.East, GoesToLocation = l2 });
 
    l4.Exits.Add(new Exit() { ExitDirection = Directions.South, GoesToLocation = l2 });
    l4.Exits.Add(new Exit() { ExitDirection = Directions.West, GoesToLocation = l6 });
    l4.Exits.Add(new Exit() { ExitDirection = Directions.North, GoesToLocation = l7 });
 
    l5.Exits.Add(new Exit() { ExitDirection = Directions.South, GoesToLocation = l4 });
 
    l6.Exits.Add(new Exit() { ExitDirection = Directions.West, GoesToLocation = l4 , NeedsObject= new List<GameObjects>(){keytol7}});
    l6.Exits.Add(new Exit() { ExitDirection = Directions.East, GoesToLocation = l7 }); //needs key condition
 
    l7.Exits.Add(new Exit() { ExitDirection = Directions.East, GoesToLocation = l6 }); //Winning room
 
    //Voeg locatie toe
    GameLocation.Add(l1);
    GameLocation.Add(l2);
    GameLocation.Add(l3);
    GameLocation.Add(l4);
    GameLocation.Add(l5);
    GameLocation.Add(l6);
    GameLocation.Add(l7);
 
    currentLocation = l1;
}

What’s missing? Veel!

Maar een eerste uitdaging zou kunnen zijn: hoe kunnen we de speler objecten van de grond laten oprapen en in de inventaris plaatsen? Dat raadsel laten we aan jou over over om op te lossen!

Mapmaker "all-in-one-project"

Volgende hoofdstuk toont het gebruik van van de belangrijkste OO concepten in een applicatie waarmee we een huis-plattegrond kunnen visualiseren. Het doel van dit hoofdstuk is zoveel mogelijk elementen van de voorbije hoofdstukken te integreren tot een groter werkend geheel.

Abstracte klasse

Eerst definiëren we een kleine hulpklasse Point die een punt in de ruimte voorstelt. We kunnen deze klasse ook gebruiken om een vector voor te stellen:

class Point
{
    private int x;
    private int y;

    public Point(int inx, int iny)
    {
        x = inx;
        y = iny;
    }
    
    public int X
    {
        get{return x;}
        set{x=value;}
    }

    public int Y	 
    {
        get{return y;}
        set{y=value;}
    }
}

We maken nu een abstracte klasse MapObject, die we vervolgens zullen gebruiken om over te erven zodat nieuwe klassen aangemaakt kunnen worden.

abstract class MapObject
{
    private Point location;
    private double price ;
    private char drawChar;
    
    //Teken object in de console
    public abstract void Paint();
}

De variabele Price zal de prijs van het object bevatten, zodat we vlot kunnen berekenen wat de totale kostprijs van onze kaart zal zijn. Location bevat de coördinaten (x,y) waar het object in de console zal getekend moeten worden. drawChar geeft aan met welk karakter het item moet getoond worden.

Belangrijk: Merk op dat deze klasse minimaal is en allerlei essentiële zaken mankeert, zoals minstens een default constructor etc.

Indien je dit project dus in de praktijk wenst te gebruiken dan zal je nog zelf de nodige properties (of get/set-methoden, naar keuze) waarmee je toegang krijgt tot Location, Price krijgt en ook drawChar methode moeten schrijven.

Overerving

We maken de MapObject klasse expres abstract, we willen voorkomen dat deze klasse rechtstreeks als object in het programma kan gebruikt worden.

Laten we nu een nieuwe klasse aanmaken dat overerft van de abstract klasse MapObject

class WallElement: MapObject
{
    public override void Paint()
    {}
}

De methode van Paint moeten we verplicht overriden (daar ze abstract was in de base klasse), voorts is het aan te raden om een default constructor te maken. De Paint-methode bevat zeer eenvoudigweg volgende 2 lijntjes code:

Console.SetCursorPosition(Location.X, Location.Y);
Console.Write(DrawChar);

Elementen op het scherm

We kunnen nu in ons hoofdprogramma (main-methode) al direct elementen op het scherm brengen met bijvoorbeeld volgende code:

WallElement steen1= new WallElement();
steen1.Paint();

Dit geeft,als je een default constructor hebt gemaakt die automatisch ieder object op locatie (1,1) zet,, een sterretje op positie (1,1) op het scherm.

We zouden dus nu bijvoorbeeld meerdere stenen kunnen plaatsen (met verschillende prijs, naargelang de soort) en dan de totaalprijs opvragen.

Grotere objecten

We hebben nu een basis om andere zaken te maken. Stel dat we grotere objecten op het scherm wensen. We zouden dan kunnen definiëren dat de variabele Location het punt linksboven van het object bepaalt. Volgende nieuwe object erft over van de MapObject en geeft een grotere figuur weer (vierkant, maar je kan natuurlijk je fantasie de vrije loop laten gaan):

class FurnitureElement: MapObject
{

    private int unitSize;
    public int UnitSize
    {
        get { return unitSize; }
        set { if (value > 0) unitSize = value; }
    }

    public override void Paint()
    {
        for (int i = Location.X; i < Location.X + UnitSize; i++)
        {
            for (int j = Location.Y; j < Location.Y + UnitSize; j++)
            {
                if (i < Console.WindowWidth && j < Console.WindowHeight)
                {
                    Console.SetCursorPosition(i, j);
                    Console.Write(DrawChar);
                }
            }
        }
    }
}

We kunnen dan eenvoudig weg allerlei meubels definiëren, zoals een zetel:

class ZetelElement: FurnitureElement
{
    public ZetelElement()
    {
        Price = 100;
        DrawChar = '+';
        UnitSize = 2;
    }
}

Of als je de zetel anders wil getekend zien (geen rechthoek bijvoorbeeld, maar iets dat meer op zetel trekt, dan voeg je nog volgende code toe:

public override void Paint()
{
    //Code om complexere zetel op scherm te tonen
}

Polymorfisme

We kunnen nu ongelooflijk veel objecten op het scherm tonen (laten we veronderstellen dat je een overloaded constructor telkens hebt geschreven), met behulp van een List-object, als volgt:

List<MapObject> allObjects= new List<MapObject>(); //lang leve polymorfisme

//Muurtje
for (int i = 0; i <10 ; i++)
{
    Point tempLoc= new Point(2+i,3);
    WallElement tempWall= new WallElement(tempLoc,'=',10.0);
    allObjects.Add(tempWall);
}

//Zetel
allObjects.Add(new ZetelElement(new Point(6,8), 3 ));

//Teken alle objecten
for (int i = 0; i < allObjects.Count; i++)
{
    allObjects[i].Paint();
}

Dankzij polymorfisme kunnen we alle objecten die overgeërfd zijn van MapObject in de MapObject-list plaatsen. Wanneer we dan Paint aanroepen gebruiken we de implementatie van het object zelf indien aanwezig.

Maken van een grafisch menu

Het maken van een semi-grafisch menu is verrassend eenvoudig.

Volgende klasse toont een kadertje met wat tekst in:

class Menu
{
   public Menu()
   {}

   public void ShowMenu()
   {
       //Tekenen
       TekenBalk(1);
       TekenOpties(2);
       TekenBalk(5);
   }
   
   private void TekenBalk(int hoogte)
   {
       for (int i = 0; i < Console.WindowWidth; i++)
       {
           Console.SetCursorPosition(i, hoogte);
           Console.Write('*');
       }
   }
   private void TekenOpties(int hoogte)
   {
       Console.SetCursorPosition(5,hoogte);
       Console.Write("A) Voeg zetel toe op willekeurige locatie");
       Console.SetCursorPosition(5, hoogte+1);
       Console.Write("B) Beweeg kaart naar beneden");
       Console.SetCursorPosition(5, hoogte+2);
       Console.Write("Wat wilt u doen?...");
   }
}

Je kan dit dan als volgt oproepen in je main:

Menu menu= new Menu();
menu.ShowMenu();

Tekstverwerken van Menu

We geven onze lijst van objecten mee aan ons Menu zodat het Menu object nieuwe zaken aan de map kan toevoegen:

public void GetInput(List<MapObject> list)
{
    string input=Console.ReadLine();
    if(input=="A" || input=="a")
    {
        //Voeg randomzetel toe
    }
    if (input == "B" || input == "b")
    {
        //Beweeg kaart naar beneden
    }
}

We kunnen dan in de main volgende code plaatsen die constant het scherm hertekent en telkens op input van de gebruiker wacht:

List<MapObject> allObjects = new List<MapObject>();
Menu menu= new Menu();
do
{
    menu.ShowMenu();
    menu.GetInput(allObjects);
    Console.Clear();
    //Teken alle objecten
    for (int i = 0; i < allObjects.Count; i++)
    {
        allObjects[i].Paint();
    }
} while (true);

Map verplaatsen

De map verplaatsen is wederom verrassend eenvoudig. Stel dat je je map naar beneden wenst te verplaatsen als de B wordt ingedrukt; Je update gewoon de locatie van ieder object waarbij de y-positie gewoon met 1 wordt verhoogd:

if (input == "B" || input == "b")
{
    //Beweeg kaart naar beneden
    for(int i=0;i<list.Count;i++)
    {
        list[i].Location = new Point(list[i].Location.X, list[i].Location.Y + 1);
    }

}

Composiet-klassen

Voorts kunnen we bijvoorbeeld nu meerdere klassen aanmaken (tafels, stoelen, deuren, etc) en dan een composiet-klasse aanmaken die bijvoorbeeld een volledig salon beschrijft, de code zou er dan als volgt kunnen uitzien:

class SalonElement: MapObject
{
    private List<MapObject> elementen= new List<MapObject>();

    public SalonElement(Point salonLoc)
    {
        
        elementen.Add(new ZetelElement(new Point(2, 2), 3, '+'));
        elementen.Add(new ZetelElement(new Point(5, 9), 3, '+'));

        Location = salonLoc;
    }

    public override void Paint()
    {
        for (int i = 0; i < elementen.Count; i++)
        {
            elementen[i].Paint();
        }

    }
}

Merk op dat we rekening moeten houden met het feit dat de locatie van het salon het punt linksboven is, en dat dus de nieuwe locaties van de zetels vanaf dit punt hun oorsprong hebben. Althans dat willen we.. Als we in het main-programma dan schrijven:

SalonElement salon1= new SalonElement(new Point(6,5));
salon1.Paint();

Dan verschijnen onze zetels wel, maar niet op de locatie zoals we wilden (nu verschijnen de zetels op locatie (2,2) en (5,9), terwijl we liever hebben dat ze verschijnen op (2+6, 2+5) en (5+6, 9+5), dus rekening houdende met de locatie van het salon zelf

Interface

We willen nu ervoor zorgen dat wanneer we volgende code schrijven, dat ook alle elementen van het Salon mee verhuizen naar de nieuwe locatie:

List<MapObject> allObjects = new List<MapObject>();
allObjects.Add(new SalonElement(new Point(5, 5)));
allObjects[0].Paint();

//Verplaats salon
allObjects[0].Location= new Point(10,10);
allObjects[0].Paint();

Echter, dat gebeurt niet. De oplossing is een gevorderd principe, maar eentje dat hopelijk het voordeel van een Interface laat zien.

We leggen een nieuwe interface IComposite vast die iedere composietklasse moet implementeren:

interface IComposite
{
    void UpdateElements(Point offset);
}

Ons SalonElement wordt krijgt dan volgende aanpassing:

class SalonElement: MapObject,IComposite
{
    private List<MapObject> elementen= new List<MapObject>();

    public SalonElement(Point salonLoc)
    {
        ...
    }

    public override void Paint()
    {
    ...
    }

    public void UpdateElements(Point offset)
    {
        ...
    }
}

De UpdateElements methode zou er dan als volgt kunnen uitzien:

for (int i = 0; i < elementen.Count; i++)
{
    Point elementLoc = elementen[i].Location;
    elementLoc.X += offset.X;
    elementLoc.Y += offset.Y;
    elementen[i].Location = elementLoc;
}

Is/as

Telkens we dus UpdateElements aanroepen dan worden alle elementen die bij het object horen ook geüpdatet.

Nu rest ons nog één aanpassing, dat is ervoor zorgen dat deze methode ook effectief telkens wordt aangeroepen. De methode moet aangeroepen worden telkens we een aanpassing aan de Location van het SalonElement doen. Hierbij controleren we eerst of de locatie überhaupt al geïnitialiseerd is (anders is deze waarde gelijk aan ‘null’). Vervolgens berekenen we de offset, dit is het verschil tussen de huidige en de nieuwe locatie van de composietklasse. Daar Location bij MapObject hoort, moeten we dus in die klasse een aanpassing doen. We bereiden daarom de Locationproperty uit als volgt:

public Point Location
{
    get { return location; }
    set
    {
        Point prevloc = location;
        Point offset = new Point(1, 1);
        if (location != null)
        {
            
            offset.X = value.X - prevloc.X;
            offset.Y = value.Y - prevloc.Y;
        }
        
        location = value;
        if (this is IComposite)
        {
            IComposite obj = this as IComposite;
            obj.UpdateElements(offset);
        }
        
    }
}

Deze code kan misschien wat toelichting gebruiken:

  • Telkens we de set aanroepen van Location (dus in bijvoorbeeld allObjects[0].Location= new Point(10,10);) dan veranderen sowieso de locatie van het object naar de nieuwe waarde.
  • We bewaren de huidige locatie zodat we de offset kunnen berekenen.
  • Indien er een ‘huidige locatie’ is (location!=null) dan berekenen we de offset in de x en de y richting.
  • Nu passen we de locatie van de composietklasse aan.
  • Vervolgens kijken we of het object in kwestie (aangegeven met this, daar we in het object zelf zitten) de IComposite interface ‘heeft’.
  • Als dit zo is dan zetten we het object even om naar een IComposite-object zodat we de UpdateElements()-methode kunnen aanroepen.

We kunnen nu dus zelfs een volledig Huis als klasse beschrijven en zo verschillende soorten huizen definiëren. Telkens we dan een huis verplaatsen dan verplaatst de hele inboedel mee.

Belangrijk: Het gebruik van de interface is hier louter illustratief. Dit probleem kan je beter oplossen door een CompositeElement klasse aan te maken die overerft van MapObject. Deze klasse bevat dan een lijst van elementen en een UpdateElements methode. In MapObject controleer je dan of een object van het type CompositeElement is (ipv IComposite)

Een nog betere oplossing is die waarbij je gewoon direct zegt dat MapObject een lijst van elementen kan bevatten. Als een MapObject exact 1 element in zijn lijst bevat dan is de werking dezelfde als ervoor, maar nu kunnen we dus zonder veel code aanpassingen ook composiet objecten aanmaken.

Think about it.

MapEditor in WPF

Ga naar (deze site)[https://github.com/timdams/MapEditor_LearnSomeWPF] en volg de pdf daar. Deze oefening toont hoe je van start gaat met een console-applicatie en die dan stelselmatig zal omzetten naar een echte grafische Windows applicatie (WPF).

In opbouw

Dit project hoort niet bij de leerstof, daar het het concept van API gebruikt dat we nog niet hebben gezien. Dit is echter zo eenvoudig dat ik hoop dat het sommige studenten zin geeft om verder exploreren.

Samenvatting

  • API=Application programming interface
  • .NET bibliotheek die praat met magicthegathering.io (rate limited!)
  • Broncode+ voorbeelden hier

Nuget installeren

  1. In project, rechterklikken op References -> Manage Nuget packages…
  2. Klikken op Browse, zoeken naar "mtg"
  3. MtgApiManager.Lib installeren

Broncode

Volgende voorbeeld is gebaseerd op een oude versie van de nuget package en zal dus niet meer werken met de huidige. De auteur van dit boek (ikke) is te lui om de tutorial aan te passen en verwijst daarom naar hier waar tal van codevoorbeelden staan!

We maken een applicatie die aan de gebruiker alle edities van Magic toont. De gebruiker kiest vervolgens van welke editie hij een 'boosterpack' wenst te generen (boosterpack= pakje kaarten dat je in de winkel kan kopen met daarin een vast aantal kaarten waarvan ook steeds minstens 1 zeldzame kaart).

Voeg zeker bovenaan toe:

using MtgApiManager.Lib.Service;

Stap 1 : Set tonen

 SetService service = new SetService();
var result = service.All();
foreach (var set in result.Value)
{
    Console.WriteLine($"{set.Name} ({set.Code})");
}

Stap 2: Controleren op geldige set

Console.WriteLine("Voor welke set wil je booster (voer code tussen haakjes in)?");

string code = Console.ReadLine().ToLower();
bool isValid = false;
foreach (var set in result.Value)
{
    if (set.Code.ToLower() == code)
    {
        isValid = true;
        break;
    }
}

Stap 3: Boosterpack generen en tonen

var boosterresult = service.GenerateBooster(code);
var boosterpack = boosterresult.Value;
if (boosterpack != null)
{
    for (int i = 0; i < boosterpack.Count; i++)
    {
        Console.WriteLine($"{i}:{boosterpack[i].Name}");

    }
}

Stap 4: kaart tonen

Vragen welke kaart getoond moet worden

 Console.WriteLine("Welke kaart wenst u meer info");
int keuze = Convert.ToInt32(Console.ReadLine());
Console.Clear();
MtgApiManager.Lib.Model.Card chosenCard = boosterpack[keuze];
Console.WriteLine(chosenCard.Name);
Console.WriteLine(chosenCard.Text);
Console.WriteLine(chosenCard.Rarity);
Console.WriteLine("Druk op enter om afbeelding te tonen");
Console.ReadLine();
System.Diagnostics.Process.Start(chosenCard.ImageUrl.ToString());

Alle code samen

SetService service = new SetService();
var result = service.All();
foreach (var set in result.Value)
{
    Console.WriteLine($"{set.Name} ({set.Code})");
}

Console.WriteLine("Voor welke set wil je booster (voer code tussen haakjes in)?");

string code = Console.ReadLine().ToLower();
bool isValid = false;
foreach (var set in result.Value)
{
    if (set.Code.ToLower() == code)
    {
        isValid = true;
        break;
    }
}
if (isValid)
{
    var boosterresult = service.GenerateBooster(code);
    var boosterpack = boosterresult.Value;
    if (boosterpack != null)
    {
        for (int i = 0; i < boosterpack.Count; i++)
        {
            Console.WriteLine($"{i}:{boosterpack[i].Name}");

        }
        Console.WriteLine("Welke kaart wenst u meer info");
        int keuze = Convert.ToInt32(Console.ReadLine());
        Console.Clear();
        MtgApiManager.Lib.Model.Card chosenCard = boosterpack[keuze];
        Console.WriteLine(chosenCard.Name);
        Console.WriteLine(chosenCard.Text);
        Console.WriteLine(chosenCard.Rarity);
        Console.WriteLine("Druk op enter om afbeelding te tonen");
        Console.ReadLine();
        System.Diagnostics.Process.Start(chosenCard.ImageUrl.ToString());
    }
    else
    {
        Console.WriteLine("Iets is mislukt, sorry");
    }
}

Klassiek

a

Met {width=50%}

Test

b

Gaat dit dan?

Html

c

html 2 met caption

GitBook Logo