Deze video bestaat uit twee delen:
deel 1:
deel 2:
Inleiding op opdrachten video 13
Er is een spelletje dat met 21 lucifers gespeeld wordt. De lucifers liggen op een rij:
Twee spelers spelen. Om-de-beurt nemen ze 1 of 2 of 3 lucifers van de rij (vanaf rechts).
Degene die de laatste lucifer pakt, wint het spel.
We gaan, in een aantal stappen, een niet-grafische versie van dit spel in de browser maken.
De opdrachten bouwen op elkaar voor. Als je vast loopt, geef dat dan aan in je mail, en probeer vervolgens hulp van klasgenoten in te schakelen, zodat je toch verder kunt. Als je een klasgenoot helpt, probeer dan zo min mogelijk code te geven. Hints zijn leerzamer. In het uiterste geval wissel je code uit, maar de ontvanger moet die code begrijpen voordat-ie verder mag.
We starten met de volgende HTML:
<html> <head> <title>21 Strijkstokken</title> </head> <body> <script> var aantalLucifers = 21 function pakLucifers( aantal ) { aantalLucifers = aantalLucifers - aantal } </script> </body> </html>
Vraag 13.1: onderzoek de code
Maak een HTML-file met daarin de inhoud uit de inleiding. Open die file in Firefox, en gebruik de Firebug console om te testen dat de variabele ‘aantalLucifers
‘ bestaat, en de goede waarde heeft.
Check ook dat de functie ‘pakLucifers()
‘ bestaat, en werkt.
Heeft deze functie nu een returnwaarde, of een zijeffect, of allebei?
[ mail je antwoord… ]Vraag 13.2: eeste stap naar werkend spel
Pas de functie aan, zodat-ie, als de parameter ‘aantal
‘ groter is dan het beschikbare aantal lucifers, het aantal lucifers niet onder de 0 (nul) zakt, maar op 0 uitkomt.
Gebruik daartoe het if-statement, waarschijnlijk met een else-tak. De conditie (de voorwaarde tussen de ronde haakjes) zal wel een getalsvergelijking moeten doen.
PS: Getallen vergelijken doe je zo:
|
(links) gelijk aan (rechts)vergis je niet door maar één ‘=’-teken te gebruiken. Het moeten er twee zijn (drie mag ook in Javascript) |
a != b
(en alle varianten als hierboven) |
(links) ongelijk aan (rechts)Er mogen geen spaties tussen de ! en de =! betekent “niet” |
a < b
(en alle varianten als hierboven) |
(links) kleiner dan(rechts)Echt ‘kleiner dan’. ‘Gelijk aan’ telt niet mee. |
a <= b
(en alle varianten als hierboven) |
(links) kleiner dan of gelijk aan(rechts)Hier telt het wel als (links) en (rechts) gelijk zijn. |
a > b
(en alle varianten als hierboven) |
(links) groter dan(rechts)Echt ‘groter dan’. ‘Gelijk aan’ telt niet mee. |
a >= b
(en alle varianten als hierboven) |
(links) groter dan of gelijk aan(rechts)Hier telt het wel als (links) en (rechts) gelijk zijn. |
Vraag 13.3: een tussenoefening
Het if-statement mag overal gebruikt worden waar commando’s gebruikt kunnen worden. Dus ook in andere if-statements!
We gaan met de volgende code experimenteren:
// leeftijd is een getal, // casinoVerbod is true of false (een boolean dus) function laatGokkerBinnen(leeftijd, casinoVerbod) { if( leeftijd > 21 ) { if( casinoVerbod ) alert( "U mag er niet in omdat u een Casino Verbod heeft" ) else alert( "Welkom in ons prachtige Casino!") } else { alert( "Je bent te jong om te mogen gokken. Ga maar naar de kermis.") } }
Bedenk, voordat je de bovenstaande code uitprobeert, minstens 5 combinaties van invoerwaarden voor deze functie, en de reactie die je verwacht. Maak daar eerst een klein tabelletje van.
Voer daarna al die combinaties uit, en vergelijk de uitkomst met de verwachtte uitkomst. Voeg de uitkomsten toe aan je tabel, en stuur de tabel met verwachtte en werkelijke uitkomsten in.
Mail je tabel
[ mail je tabel… ]
Vraag 13.4: 2e stap naar werkend spel
Maak een vergelijkbare tabel voor een nieuwe versie van de functie pakLucifers(). Deze nieuwe versie heeft de volgende eigenschappen:
- als het aantal lucifers dat gepakt moet worden groter is dan 3, dan zien we een alert() met een melding dat het niet goed is. De variabele aantalLucifers blijft wat-ie was.
- als het gevraagde aantal lucifers groter is dan het beschikbare aantal lucifers, dan zorgt de functie ervoor dat de variabele op 0 gezet wordt (en voorkomt dus dat de variabele een negatieve waarde krijgt). Deze functionaliteit heb je al gemaakt in stap 1.
- Als de bovenstaande situaties zich niet voordoen, dan werkt de functie gewoon zoals in de code die boven gegeven is.
Nogmaals: Maak eerst de test-tabel. Er zijn minstens vier verschillende soorten invoer te verzinnen:
- aantalLucifers is groter dan 3, en parameter is kleiner dan of gelijk aan 3;
- aantalLucifers is groter dan 3, en parameter is groter dan of gelijk aan 3;
- aantalLucifers is kleiner dan 3, en parameter is kleiner dan aantalLucifers;
- aantalLucifers is kleiner dan 3, en parameter is groter dan aantalLucifers;
Mail je tabel:
[ mail je tabel… ]
Vraag 13.5: vervolg op 13.4
Pas nu de functie pakLucifers()
aan dat-ie ook werkt zoals boven beschreven. Gebruik je testdata om het te controleren.
Je heb nu een begin gemaakt met test-driven programming: Een programmeeraanpak waarbij je eerst je testgevallen bedenkt en daarna pas de code schrijft. Bijna altijd maak je zo sneller code die goed werkt. Het vraagt wel ervaring om een goede verzameling test-cases te bedenken.
Vraag 13.6: vervolg op 13.5
Kun je code scrijven die alle test-gevallen (test-cases) uit je tabel achter elkaar uitprobeert, en zelfs met een console.log()
aangeeft of de functie gedaan heeft wat-ie had moeten doen?
Je hebt nu een begin gemaakt met unit-testing, een manier om testdriven development comfortabel te maken.
Vraag 13.7: vervolg op 13.6
Ook al heb je nu een goed geteste functie pakLucifers()
, de kans dat-ie waterdicht is, is klein. Kun je een parameterwaarde verzinnen die er voor zorgt dat het aantal lucifers toeneemt (in plaats van afneemt)?
Pas je functie zo aan dat deze grap niet nog eens uitgehaald kan worden.
Vraag 13.8: derde stap naar werkend spel
Maak en userinterface voor het spel. Je kunt daarvoor de volgende HTML gebruiken.
<html> <head> <title>21 Strijkstokken</title> </head> <body> <h4>aantal lucifers:</h4> <h1 id="aantalWeergave"></h1> <hr> <input type="text" id="pakAantalInvoer"> <button id="pakKnop">Pak!</button> <script> function leesPakAantal() { var hetVeld = document.getElementById("pakAantalInvoer") // de Number-function zet tekst om in een getal (als dat kan) var resultaat = Number( hetVeld.value ) // de functie isNaN controleert of z'n parameter een echt // getal is, of Not-a-Number console.log( "leesAantal: resultaat = ", resultaat, "isNaN(resultaat) = ", isNaN(resultaat) ) return resultaat } var aantalLucifers = 21 // TODO: Maak een functie om het aantalLucifers in beeld te // brengen in het H1 element. Zorg ervoor dat bij de start // van het spel, het aantal lucifers zichtbaar is. function pakLucifers( aantal ) { // ...de code die je in de eerdere oefeningen maakte... } // TODO: maak een functie die met pakLucifers() en leesPakAantal() // één ronde van het spel uitvoert. // Het nieuwe aantal lucifers moet natuurlijk in beeld gebracht worden. // Koppel deze functie aan een 'onclick'-event van de knop. </script> </body> </html>
Maak eerst maar eens de basis-versie, en besteed nog geen aandacht aan invoer-validatie, of het bepalen wanneer het spel afgelopen is. Zie de TODO-commentaren in de code.
Vraag 13.9: 4e stap naar werkend spel
Maak de volgende uitbreidingen op de UI:
- Als het aantalLucifers nul wordt, dan verschijnt er een alert() die de speler tot winnaar uitroept.
- Het invoerveld wordt leeggemaakt iedere keer als er succesvol een getal uitgelezen is na een druk op de knop. (doe dit door de ‘value’ van het veld te veranderen, niet door innerHTML te gebruiken).
- Als, na het indrukken van de knop, blijkt dat de tekst geen echt getal oplevert (maar NaN), dan verschijnt er een alert met een foutmelding. Maak er een vriendelijke melding van, en zorg er voor dat aantalLucifers niet zelf ook NaN wordt (NaN is erg besmettelijk).
NaN staat voor Not-a-Number, een waarde die ontstaat als een berekening geen normaal getal kan opleveren. De enige manier om te controleren of een variabele NaN bevat is door de functie isNaN() te gebruiken (zie bovenstaande code). - Een html-invoerelement kan ontoegankelijk gemaakt worden door z’n ‘disabled’-eigenschap op true te zetten. bijvoorbeeld:
document.getElementById("pakKnop").disabled = true
Gebruik deze mogelijkheid om de knop disabled te maken wanneer het spel is afgelopen.
- Voor de bonus: iedere keer als de gebruiker een toetsaanslag in het veld doet, onstaat er er een ‘keyup’-event. Probeer dit uit met deze code:
document.getElementById("pakAantalInvoer").onkeyup = checkInhoud function checkInhoud() { console.log("veld: ",document.getElementById("pakAantalInvoer").value ) }
Gebruik deze mogelijkheid om ervoor te zorgen dat de “Pak!”-knop gedisabled is als de inhoud NaN is, of leeg, en weer bruikbaar wordt als de inhoud een bruikbaar getal voorstelt.
Vraag 13.10: stap 5 voor de ‘bonus’
Voeg twee tekstvelden toe aan de pagina waarin we de namen van de twee spelers kunnen invoeren.
Zorg ervoor dat bij iedere succesvolle ronde, de beurt naar de andere speler gaat (je zult wel een variabele maken om bij te houden wiens beurt het is).
Bij onsuccesvolle rondes verandert de beurt niet. Een ronde is onsuccesvol als de invoer niet deugt: negatief, te groot, NaN etc.
Als het spel is afgelopen (succesvolle ronde die eindigt met aantalLucifers == 0), dan wordt de naam van de winnende speler gebruikt in een hartelijk felicitatie.
Het is cool als het programma tijdens het spel laat zien wie er aan de beurt is. Je zou daarvoor het H4 element bovenin kunnen gebruiken.
Vraag 13.11: stap 6 mooie lucifers…
De droevige lelijkheid van dit spel maakt dat we er niet veel geld mee zullen verdienen. Zorg ervoor dat dit spel er mooi en gelikt uitziet, zodat we een kans maken nummer 1 bij Google te worden bij de zoekterm “21 Lucifers” (in plaats van de creepy hardrockband die er nu staat), en veel geld gaan verdienen.