Leren programmeren animeren

Leren programmeren animeren

Het is pakweg 2 jaar geleden dat ik met deze serie ben begonnen en daarmee de missie om jou, mijn trouwe lezer te leren programmeren. In mijn tweede blog hebben we samen een spel geprogrammeerd, maar omdat we samen tot de conclusie waren gekomen dat OrcadoInvaders iets te hoog gegrepen was voor je eerste introductie tot programmeren, was dat een tekstgebaseerd_computerspel spel. Herinner je je het weer? Ondertussen heb je al heel wat meer geleerd en ook zeker niet onbelangrijk is de P5js webeditor uitgekomen. Ik vermoed dat ik P5 wel eens eerder heb genoemd; het is de JavaScript bibliotheek die ik gebruik in de meeste van mijn code voorbeelden, waaronder TicTacToe en OrcadoInvaders. P5 is gebaseerd op processing.org een organisatie die net als ik, de ambitie heeft om meer mensen te leren programmeren. Maar als ik jou was zou ik mijn blog lezen, dan krijg je tenminste ooit nog uitgelegd wat 3-waarden-logica nou precies is en hoe dat werkt.

Maar niet vandaag. Vandaag gaan we weer samen een spelletje programmeren, maar nu met graphics. Na wat overwegingen wat nou een goed spel zou zijn om samen te maken is de keuze gevallen op ‘hooghouden’, er is een heel sub genre van dit spel op bijvoorbeeld funny games, maar volgens mij zijn die allemaal in flash, en gezien flash vanwege de vele veiligheidsproblemen binnenkort in geen enkele browsers meer draait, zijn dat allemaal niet echt goede voorbeelden. Gelukkig is het spelletje heel simpel; je hebt een bal, die valt naar beneden, als je er op klikt ‘schiet’ je hem omhoog, het doel is om de bal zo lang mogelijk hoog te houden voor dat die valt. We gaan dat doen in de hiervoor genoemde P5js webeditor.

Zodra je de webeditor opent zie je links de code en als je op de play-knop drukt zie je rechts het resultaat van de code. Klik maar eens op play, je ziet rechts een vierkant verschijnen. De code die daar voor zorgt staat links onder setup en draw. Setup wordt aan het begin 1x uitgevoerd en draw elke seconde 60x. In setup wordt een canvas aangemaakt van 400 breed en 400 hoog. Ik denk graag zo groot mogelijk dus dat mag je direct veranderen in:
createCanvas(windowWidth, windowHeight);
Hiermee maken we het canvas (waar we op gaan schilderen) zo breed en hoog als in het scherm past. In draw krijgt het canvas een grijze achtergrond kleur, dat is wat saai en aangezien hooghouden een voetbal spelletje is maken we dat meteen gras groen:
background(0,180,0);
Als het goed is, is het hele rechtervlak nu groen. Mocht dat nou niet zo zijn of als je ergens verderop de draad kwijt bent, dit is het eind resultaat, kan je spieken wat er mis is gegaan.

Ok, het gras is er, nu een bal. Daarvoor gaan we een object maken (ik heb je niet voor niets leren objectificeren). P5 ondersteunt om hiervoor een apart bestand aan te maken, en dat is eigenlijk mooier, maar voor de eenvoud hou ik het even in één bestand. Eerst maken we een bal functie die als ons object gaan dienen:

function Bal(){
}

De computer moet weten hoe groot de bal is en waar die op het scherm moet worden getekend, dus daarvoor maken we variabelen aan in de functie.

function Bal() {
  this.diameter = 50;
  this.x = 0;
  this.y = 0;
}

En de bal moet getekend worden, daarvoor maken we een functie aan:

function Bal() {
  this.diameter = 50;
  this.x = 0;
  this.y = 0;

  this.teken = function() {
    ellipse(this.x, this.y, this.diameter, this.diameter);
  };
}

In die functie staat nu dat we een ellips tekenen op coördinaten (0,0) en met de breedte en hoogte van de diameter. Aangezien een ellips met gelijke breedte en hoogte een perfecte cirkel is, gaat dit een cirkel tekenen. Om de bal te laten tekenen moeten we dit aanroepen in de draw functie. Eerst maken we de bal variabele aan in setup

function setup() {
  createCanvas(windowWidth, windowHeight);
  bal = new Bal();
}

Zodat we daarna in de draw functie hem kunnen laten tekenen.

function draw() {
  background(0,180,0);
  bal.teken()
}

Er verschijnt nu een witte kwart cirkel in de linker bovenhoek, laten we hem even in het midden zetten voor het gemak, verander daarvoor in het bal object de x en y waarden respectievelijk  windowWidth/2 en windowHeight/2. De bal staat nu midden in het scherm.

Nu gaan we wat leven in de bal brengen. Dat gaan we doen door zwaartekracht toe te voegen. In een hele volwassen versie van dit spel gebruiken we daarvoor een physics engine, dat is software die natuurkundige krachten nabootst, daarmee kunnen we rekening houden met het gewicht van de bal, de luchtweerstand, de gravitatie constante en dat soort dingen. Dat is echter overkill voor wat we aan het doen zijn, we gaan de bal een verticale snelheid geven, en de zwaartekracht verhoogt die snelheid neerwaarts. Eerst maar verticale snelheid aan het bal object toevoegen:

this.verticalesnelheid = 0;

En we maken een zwaartekracht functie die die snelheid beïnvloed en dan ook de y coördinaat van onze bal:

this.zwaartekracht = function() {
  this.verticalesnelheid += 0.2;
  this.y += this.verticalesnelheid;
};

Dan alleen nog zorgen dat de zwaartekracht toegepast wordt elke keer als we de bal tekenen, daarvoor zetten we onder bal.teken(), bal.zwaartekracht() en voilá de bal valt naar beneden. Klein probleempje, de bal verdwijnt al snel onder de grond. Dat is natuurlijk niet goed. We zullen in onze super eenvoudige versie van een physics engine de grond de bal laten stuiteren door een opwaartse kracht toe te voegen als de grond geraakt wordt, soort derde wet van Newton zeg maar:

if (this.y + this.diameter / 2 > windowHeight) {
  this.y = windowHeight - this.diameter / 2; // zorg dat de bal niet vastkomt in de grond
  this.verticalesnelheid *= -0.8;
} else {
  this.verticalesnelheid += 0.2;
}

En hup, de bal stuitert, het verdient geen nobelprijs voor de natuurkunde, of zelfs maar een voldoende op een middelbare school toets, maar dit is soort van hoe zwaartekracht werkt, en goed genoeg voor ons spelletje.

Nu moeten we de bal nog hoog kunnen houden, dat gaan we doen door er op te klikken. P5 heeft een ingebouwde functie om muisklikken te herkennen, alle functies die P5 ondersteunt kan je vinden in de referentie. We gaan gebruik maken va de touchStarted functie, in mijn ervaring werkt het dan goed met zowel muizen als met aanraakgevoelige schermen zoals mobiele telefoons.

function touchStarted() {
  bal.verticalesnelheid -= 20;
  return false;
}

Die return false wordt aangeraden in de reference dus dat doen we maar braaf. Het zal je inmiddels opgevallen zijn dat een negatieve snelheid de bal naar boven brengt en een positieve snelheid de bal naar beneden. Dat komt omdat het y-coördinaat 0 helemaal boven in het scherm ligt en het y-coördinaat met de schermhoogte helemaal onderin het scherm.

Dit werkt, alleen het maakt nu niet uit waar je klikt, dat maakt het een suf spelletje. Dit kan je nu makkelijk winnen door in je console met een interval muisklikken te genereren (je hebt immers leren automatiseren). We moeten dus kijken of er op de muis wordt geklikt. We hebben gelukkig ook de x en y coördinaten van de muis (of vinger) dus we hoeven alleen maar te controleren of de afstand tussen de muis en de bal gelijk of kleiner is dan de straal van de bal. Dit is normaal het moment dat ik me realiseer dat ik meer de straal dan de diameter gebruik en de code daar op aanpas, maar ik begin inmiddels de spreekwoordelijk prangende ogen van mijn redactrice in mijn rug te voelen gezien ik al ver over het aantal streefwoorden per blog ben (ik geef persoonlijk de ellenlange zinnen die ik maak de schuld, alsof ik een kommarecord probeer te verbreken…), dus dat mag je straks zelf doen. Dat is overigens ook de reden dat ik onze gezamenlijke flashback naar middelbare school wiskunde en de stelling va Pythagoras)  oversla (ja, daar hadden we nu eindelijk een keer ‘in het echt’ gebruik van kunnen maken) en gebruik maak van de euclidische afstandsfunctie die in P5 zit ingebakken:

if (dist(bal.x, bal.y, mouseX, mouseY) <= bal.diameter / 2) {
  bal.verticalesnelheid -= 20;
}

Op zich werkt het nu, maar het is wel een erg verticale aanpak, dat is wel heel erg agile, maar voor het spelletje niet echt leuk. Probeer zelf eens toe te voegen dat als je de bal links van het midden raakt hij naar rechts gaat en andersom (en vergeet niet van de zijkant muren te maken zodat je de bal niet kwijt raakt). Ik heb het in het eindresultaat zit, maar ik geloof dat je dit zonder spieken kan!

Als je dat gedaan hebt is het tijd voor het testen, dan blijkt dat het spelletje misschien iets te uitdagend is geworden. Met een klein beetje tweaken aan de gebruikte getallen en een scoreteller toe te voegen hebben we in de basis het spelletje. Dat valt echter ook buiten de scope van dit blog, maar dat moet je niet laten weerhouden om het zelf te doen en hoewel het het beste is om dat zonder hulp op te lossen hoeveel moeite je dat kost, kan ik me herinneren hoe frustrerend dat kon zijn toen ik net begon met programmeren, dus ook daarvoor een spiekbriefje.

Leren programmeren automatiseren

Leren programmeren automatiseren

Ik heb lang getwijfeld over dit blog, voor de beste uitleg zou ik jullie namelijk mee willen nemen in de wereld van autohotkey (https://www.autohotkey.com/) dat is een programma waarmee je op Windows heel makkelijk een heleboel dingen kan automatiseren. Het gebruikt echter zijn eigen programmeer taal, en ik doe mijn best om al het programmeren dat ik samen met jullie doe, in javascript te doen, zodat jullie makkelijk mee kunnen blijven doen. Een aardige uitleg waarom ik jullie autohotkey wilde laten zien en waarom ik het niet heb gedaan, is dit filmpje van Tom Scott.

Een nadeel van javascript voor het uitleggen van automatisering is dat je browser veilig is gemaakt. Elke keer dat je naar een andere pagina gaat  wordt je console opnieuw gestart. Dat is maar goed ook, want als dat niet zou gebeuren zou je daar vreselijk misbruik van kunnen maken. Maar dat betekent dus dat je alleen maar dingen in je browser kunt automatiseren die binnen hetzelfde scherm gebeuren. Wat je dan nog wel kunt doen is data van een andere website ophalen via een API en dat vervolgens gebruiken tijdens je automatisering. Dus mocht je straks de smaak te pakken hebben, pak mijn vorige blog er nog een keer bij.

Maar laten we iets simpeler beginnen. Het idee van iets automatiseren is dat je een taak die saai is of die je vaak moet doen, door de computer kan laten doen. Dat klinkt heel aanlokkelijk, maar het is niet altijd de moeite waard. Xkcd heeft een tabel waarin je kunt opzoeken of het zinvol is om iets te optimaliseren door het te automatiseren:  (bron: xkcd.com)

Als je er dan ook nog een andere xkcd bij pakt over automatiseren schetst dat een plaatje waarin het automatiseren van taken niet altijd de heilige graal is die het soms lijkt te zijn:

Dat neemt niet weg dat je soms door iets te automatiseren enorm veel werk door een computer kan laten doen in plaats van mensen, of de computer taken kan laten doen waar je zelf eigenlijk geen zin in hebt. Uit ervaring blijkt dat totaal fictieve onrealistische voorbeelden vaak het beste zijn, het zet de lezer namelijk aan tot zelf een realistisch scenario te bedenken. Bovendien is het voor mij het minste werk en dat is toch een beetje waar dit blog over gaat. Dus we doen een raar hypothetisch voorbeeld.

Stel je doet een online survey over een video en om te zorgen dat je de video blijft kijken en dat je niet bij je computer wegloopt moet je van de website elke 20 seconden op een knop drukken, dat reset een timer,als die timer niet op tijd gereset wordt is je aandacht er schijnbaar niet genoeg bij en krijg je niet betaald. De ethische oplossing is elke 20 seconden op de knop drukken, de makkelijkste manier, automatiseer het drukken op de knop. Hier is de site: https://undercover.horse/automatiseren/resettimer.htm

Het automatiseren van deze taak is vrij eenvoudig. Wat we willen doen is  elke X seconde op de knop drukken, omdat te doen moeten we eerst weten hoe de knop heet, daarvoor kijken we naar de broncode van de site. Daarin vinden we:

<button id="btn" onclick="set20()">reset de timer</button>

Dat is makkelijk, de knop heeft een id attribuut. Javascript heeft een manier om een object te selecteren bij zijn id:

document.getElementById()

We geven het id “btn” mee en dan gebruiken we de click functie om er op te  klikken:

document.getElementById("btn").click()

Dat is 1 klik, we willen het echter elke X seconden, omdat te doen kunnen we er een interval van maken:

setInterval('document.getElementById("btn").click()',5000);

Deze code zorgt er voor dat er elke 5000 milliseconden (gelijk aan 5 seconden) op de knop wordt geklikt. Als je dat in je console plakt ben je klaar, de timer komt niet meer voorbij 15.

Dit is een truc die je op elke pagina toe kan passen die een knop heeft waar je op moet klikken die niet de hele pagina ververst. Ander voorbeeld: stel je voor je wilt een kaartje kopen, om dat te doen moet je op de bestelknop op een pagina klikken. Deze wordt echter pas klikbaar op een bepaald moment. Je kan geduldig op de site wachten en toeslaan zodra de knop beschikbaar wordt, of je blijft er constant op klikken en loopt er bij weg. Hier is een demo pagina: https://undercover.horse/automatiseren/klikzsm.htm

Kleine tegenvaller, als we naar de knop kijken zien we dat hij geen id heeft:

setInterval('document.getElementById("btn").click()',5);

Dat is echter makkelijk op te lossen, als je in je console zit kan je naar het tabblad elements waar je de html van de pagina ziet staan. Als je vervolgens met je rechtermuisknop op de html code van de button klikt, kan je kiezen voor “edit as HTML” en dan kan je er zelf gewoon id=”btn” bij zetten. (Je moet buiten het edit-blokje klikken om het te bevestigen) Omdat we zo snel mogelijk willen zijn zetten we de interval waarde wat lager:

setInterval('document.getElementById("btn").click()',5);

Nu gebeurt het elke 5 milliseconden, daar kan je zelf nooit tegenop klikken. In het huidige voorbeeld is het alleen wel zo dat je al die acties binnen 20 seconden moet doen anders is de knop al beschikbaar. Als alternatief kan je ook de knop zoeken door alle knoppen te zoeken en dan de eerste uit de lijst te kiezen:

setInterval('document.getElementsByTagName("button")[0].click()',5);

Het klikken op een knop is handig, maar vaak moet je meer op een pagina doen dan alleen een knop indrukken. Gelukkig kan je met javascript een heleboel van die dingen gewoon doen. Neem bijvoorbeeld een pagina met  een formulier zoals deze: https://undercover.horse/automatiseren/formulier.htm

Stel we willen blauw een heel aantal keer kiezen. Dan kunnen we het formulier een aantal keer achter elkaar invullen. Of we automatiseren het:

let y = function () {
  document.getElementsByTagName("input")[0].value = "Daan";
  document.getElementsByTagName("input")[1].value = "Veraart";
  document.getElementsByTagName("input")[4].checked = true;
  document.getElementsByTagName("button")[0].click();
}
setInterval(y, 100);

Dit lijkt allemaal vrij triviaal, maar dit zijn dan ook nog maar hele simpele voorbeelden, die je allemaal in je browser kunt uitvoeren. De essentie is echter dat je een taak opsplitst in kleine subtaakjes en dan kijkt of je die met een stukje code kan oplossen. Dat is iets wat je bij programmeren eigenlijk heel vaak gebruikt. Alleen dan in de vorm van helper functies. Als je en stuk code meerdere keren in je applicatie hebt staan, dan kan je beter van dat stuk code een functie maken. Elke keer dat je dan dat stuk code nodig hebt, automatiseer je dat door in plaats van dat stuk code te typen, de functie aan te roepen. Bijkomend voordeel is dat als je iets aanpast in dat stuk code, het meteen op alle plekken in je programma aangepast is.

Het leukste van iets automatiseren is kijken hoe de computer voor je aan het werk is. Zo heb ik laatst het verplaatsten van Vimeofilmpjes naar Youtube geautomatiseerd. Als je het dan opstart (de foutmelding oplost waar je tegenaan loopt (voor degene die het filmpje van Tom Scott hebben gezien, het was een beetje een bodge) en nog een keer opstart) en je ziet dat hij logregel voor logregel braaf bezig is, en dan op Youtube het filmpje ziet verschijnen met dezelfde titel en omschrijving als dat het op Vimeo heeft, dat geeft een voldaan gevoel. Het is wel een klein beetje zonde dat we de tijd die we gewonnen hebben met automatiseren besteden aan het kijken naar hoe de computer het werkt doet, die hadden we beter kunnen besteden aan het uitleggen van drie waarde logica… Misschien moet ik dat maar eens proberen te automatiseren. 

Leren programmeren APIreciëren

Leren programmeren APIreciëren

Belofte maakt schuld, vandaag hebben we het over APIs. API is de afkorting voor application programming interface. Een interface gebruik je om te communiceren met een applicatie. De bekendste vorm daarvan is de GUI (graphical user interface); als je een applicatie opstart op je computer of telefoon dan is wat je ziet de GUI, het is de manier waarmee jij met de applicatie kan communiceren. Je kijkt nu naar de GUI van je browser, je kan communiceren door bijvoorbeeld te scrollen en je browser beantwoord door de pagina naar boven of beneden te verschuiven. Voor mensen is een grafische interface handig, een plaatje zegt immers meer dan 1000 woorden. Voor een computer is dat anders, die communiceert een stuk makkelijker met alleen tekst.

Nu vraag je je misschien af, hoe vaak gebeurt het nou dat een applicatie met een andere applicatie communiceert? Dat ligt een beetje aan je definitie. Als je het heel sec bekijkt, communiceert jouw browser nu met een server om deze tekst op te halen, met de driver van je scherm om het op je scherm te laten zien, met je muis of touchscreen om je invoer te registreren enzovoort. Er is dus heel veel interactie, maar we gaan het over een heel klein gedeelte hebben, want ik ratel altijd al veel te veel en anders komt er geen eind aan.

We gaan het hebben over WebAPIs, dat is waar de meeste mensen het over hebben als ze API zeggen, dus dat is wel zo makkelijk. WebAPIs zijn functies die je aan kan roepen via het internet die informatie terug geven van de applicatie waar ze voor geschreven zijn. Dat is vrij abstract, dus het is zoals altijd tijd voor een voorbeeld. Stel je wilt iemand 3-waarden logica uitleggen… Wacht ik heb jullie nog steeds geen 3-waarden logica uitgelegd en ik zit alweer boven de 300 woorden en ik ben nog niet eens begonnen met het voorbeeld, we doen wel iets anders… Stel je wilt iemand Engels leren, maar aangezien je het wel leuk wilt houden besluit je een spelletje te maken.

Het spelletje is heel simpel je pakt een plaatje, je geeft er een aantal woorden bij en dan moet er geraden worden welk woord er bij het plaatje hoort. Klinkt simpel toch? Als je er nog een beter beeld bij wilt hebben kan je het hier spelen. Ik heb het basic gehouden dus er is bijvoorbeeld geen score, maar ik heb wel een hint optie toegevoegd, want het spelletje is zonder soms niet te doen.

Voor het spelletje heb ik 2 dingen nodig, een heleboel Engelse woorden en een heleboel bijpassende plaatjes. Die kan ik natuurlijk zelf samenstellen, maar er zijn applicaties op het internet die dit al hebben dus die kan ik ook gebruiken. In dit voorbeeld gebruik ik de wordnik.com API. Wordnik is een online woordenboek en daar mag je gratis gebruik van maken. Tenminste als je het niet te zot maakt, om dat een beetje in de gaten te houden heb je een sleutel nodig om gebruik te maken van hun API, deze kan je gratis aanvragen en daarmee mag je dan een 100 aantal aanvragen per uur doen.  Dus als je het spelletje heel enthousiast speelt dan zal hij het op een gegeven moment een uur niet doen. Wil je meer aanvragen doen, dan moet je daar voor betalen. De andere API is die van imgur.com, zelfde idee maar dan een plaatjes site.

We beginnen met het ophalen van 12 willekeurige Engelse woorden. Dat doen we met een API call. De Wordnik API heeft daar deze functie voor. Die roepen we aan met een URL:  https://api.wordnik.com/v4/words.json/randomWords?hasDictionaryDef=true
&minCorpusCount=50
&maxCorpusCount=-1
&minDictionaryCount=20
&maxDictionaryCount=-1
&minLength=6
&maxLength=6
&limit=12
&api_key=APISLEUTEL
. Deze URL vertaalt ongeveer naar: Op de v4 versie van de api.wordnik.com applicatie roepen we van de words.json functies de functie randomWords aan met een aantal argumenten. Voor de leesbaarheid heb ik alle argumenten op een eigen regel gezet. Alles achter het vraagteken zijn de argumenten waarmee ik woorden terug laat komen van 6 letters die niet super obscuur zijn. Ja, trouwe lezer, daar staat words.json. Tegenwoordig gebruikt het leeuwendeel van de WebAPIs JSON strings om te communiceren. Zoals je in het vorige blog hebt kunnen lezen zijn dat javascript objecten en dat is makkelijk want ik programmeer dit spelletje in javascript.

In de code ziet dit er zo uit:

async function laadApi() {
  //code niet belangrijk voor de uitleg// 
  let wordnik_res =  await fetch(url1+x);
  woorden = await wordnik_res.json();

Voor wie al sinds blog 1 enthousiast mee aan het programmeren is valt het waarschijnlijk op dat er ineens iets voor function staat. Het woord async geeft aan javascript aan dat er dingen asynchroon gaan gebeuren. Dit is volgens mij ook de eerste keer dat ik het gebruik, voor nu ga er maar even vanuit dat het hetzelfde is als var. De functie fetch haalt gegevens uit vanuit de url die je meegeeft. Ik geef de url van hierboven mee met mijn API sleutel. Normaal gesproken als je fetch afvuurt gaat daarna direct de volgende regel van je code af. De fetch functie kan even duren, maar daar wil je browser als het niet hoeft niet op wachten. Wij kunnen echter niet verder voor we die data hebben, dus we zeggen met het woord await tegen de browser dat hij moet wachten tot fetch klaar is voordat hij verder mag gaan met de rest van de code. Hetzelfde geldt voor de json functie, die het resultaat omzet in een javascript object. Na deze twee regels code is ‘ woorden’ dit:

[{
  "id": 234698,
  "word": "Yankee"
}, {
  "id": 16370,
  "word": "beggar"
}, {
  "id": 31968,
  "word": "chorus"
}, {
  "id": 47154,
  "word": "dental"
}, {
  "id": 129143,
  "word": "magpie"
}, {
  "id": 133965,
  "word": "miller"
}, {
  "id": 81088,
  "word": "patrol"
}, {
  "id": 70598,
  "word": "reform"
}, {
  "id": 73645,
  "word": "severe"
}, {
  "id": 228047,
  "word": "vector"
}, {
  "id": 81488,
  "word": "volley"
}, {
  "id": 74147,
  "word": "worthy"
}]

Tenminste, deze keer, elke keer dat we die code uitvoeren krijgen we andere woorden terug van Wordnik. Het is een lijst met 12 objecten, alle objecten hebben een id, een nummer waar we verder niets mee doen, en een word, het woord waar we naar opzoek waren.

Als we dat hebben zoeken we daar een plaatje bij:

woord = random(woorden).word
let imgur_res = await fetch(url2+woord, {
    method: "GET", 
    headers: {
        "Authorization": "Client-ID "+ clientID
    }});
  plaatjes = await imgur_res.json()

Eerst pakken we een willekeurig woord uit de rij en dan geven we die mee aan de fetch voor Imgur. De Imgur API heeft de API sleutel niet in de URL staan, maar gebruikt een header voor die informatie. Een header is informatie die je browser meestuurt met een URL. Ook Imgur werkt met json dus we roepen met het resultaat de json functie aan om het bericht weer om te zetten naar een javascript object. De Imgur API geeft veel meer informatie terug dan alleen het plaatje. Zo geeft hij bijvoorbeeld ook terug hoe vaak er gereageerd is op dit plaatje, wie het plaatje heeft geüpload en wat de titel van het plaatje is.  Waar we in eerste instantie in geïnteresseerd zijn is het plaatje zelf.

Tenminste als er een plaatje is. Het kan namelijk best zo zijn dat we van Wordnik een woord hebben gekregen waar Imgur helemaal geen plaatjes bij kan vinden. We moeten dus controleren of er wel plaatjes zijn, als dat zo is dan halen we de link uit het object en laten het plaatje zien waar de link naar verwijst. Vervolgens maken we linkjes van de 12 woorden die we van Wordnik hebben gekregen en zetten die er onder en het spelletje is af. Wil je de rest van de code ook zien, dan kan dat hier; ik heb er ook wat commentaar bij gezet.

Dat zijn dus WebAPIs, je vraagt aan een website gegevens door een specifieke URL aan te roepen en je krijgt die (meestal) in de vorm van een JSON string terug. Waarmee je dan vervolgens weer verder kan programmeren. WebAPIs zijn hip, dus als je denkt ‘dit is leuk!’ ik wil zelf ook wel met een API spelen, hier zijn een aantal voorbeelden van WebAPIs waar je mee kan praten.

Leren programmeren objectificeren

Leren programmeren objectificeren

Voordat ik dit blog ben gaan schrijven heb ik een aantal van mijn trouwe blog lezers toegezegd dat ik in mijn onophoudende missie om te laten zien dat programmeren geen magie is (voor de woordgrap is het belangrijk dat je ons wie-zijn-wij filmpje even kijkt) een blog zou schrijven over APIs. Terwijl ik daar mee bezig ben en aan het kijken wat ik daar voor uit wil leggen loop ik tegen JSON aan, wat staat voor JavaScript Object Notation. JavaScript is al wel een heel aantal keer voorbij gekomen, maar objects heb ik nog niet echt uitgelegd. In eerste instantie wilde ik een kleine paragraaf er aan wijden, maar dat voelde wat karig. Het alternatief was om het te noemen en dan later toe te lichten, maar ik moet ook ooit nog een keer 3-waarde logica uitleggen en het was eigenlijk logischer om dat eerst te doen. De APIs komen volgende keer (of de keer daarop) maar vandaag: object georiënteerd programmeren.

Misschien dat je de term object georiënteerd programmeren of de afkorting OO/OOP wel eens voorbij hebt zien komen. Object georiënteerd programmeren is een manier van programmeren, andere vormen zijn bijvoorbeeld functioneel, logisch of declaratief. De code die ik jullie tot nu toe heb laten zien is allemaal declaratief; het is een opsomming van constructies. Bij OOP ga je uit van objecten. Stel je maakt een spelletje zeg monopolie, dan is eigenlijk alles wat je vast kan houden een object: het bord, het geld, de pionnen, de kanskaarten, de straten etc. Als voorbeeld ga ik de dobbelstenen nemen, vooral omdat die vrij universeel in een heleboel andere spellen gebruikt worden.

Omdat ik geloof dat je moet blijven proberen om programmeren een beetje onder de knie te krijgen ga ik de objecten als een functie implementeren, dat is misschien niet de manier waarop de meeste tutorials het uitleggen, maar het zorgt er voor dat je de script snippets die ik straks laat zien zo in je browser console kunt plakken en uit proberen. Bovendien kan ik daarmee in een later blog heel makkelijk JSON uitleggen, maar dat is voor later. Laten we een object gaan maken.

Voor het maken van een object moeten we eerst bedenken wat de eigenschappen van dat object zijn. Voor een standaard dobbelsteen is dat een kubus met dus een 6 tal zijdes, met op die zijdes een aantal ogen oplopend van 1 t/m 6. Ook is het zo dat de som van het aantal ogen van de twee zijdes die het verst van elkaar afliggen 7 is, maar daar gaan we ons in het voorbeeld geen zorgen over maken. Eerst gaan we het aantal zijdes toevoegen.

var dobbelsteen = {
  aantalZijdes: 6
}

Dat was nog niet heel ingewikkeld toch? Laten we ook nog de ogen toevoegen, dat is een lijstje, dus dat zetten we tussen blokhaken.

var dobbelsteen = {
  aantalZijdes: 6,
  ogen: [1, 2, 3, 4, 5, 6]
}

Als we dat in onze browser console gooien en we proberen vervolgens het 3de oog te laten zien ziet dat er zo uit:

var dobbelsteen = {
  aantalZijdes: 6,
  ogen: [1, 2, 3, 4, 5, 6]
}
dobbelsteen.ogen[2];
> 3

Dat komt een lijstje in JavaScript begint bij 0, dus als we het 3de element willen hebben moeten we het item uit het lijstje hebben met het nummertje 2. Nu hoor ik je denken, leuk, maar wat is hier nou het voordeel van boven:

var dobbelsteenZijdes = 6;
var dobbelsteenOgen = [1,2,3,4,5,6];

Goede vraag, ik had hem zelf kunnen stellen. Een voordeel is dat je de variabelen mooi groepeert en die groepering vervolgens weer gemakkelijk ergens anders kunt gebruiken. Ook geeft het je code een gestructureerde logica, alles is een object en heeft eigenschappen. Maar een object kan meer dan alleen variabelen groeperen. Hij ondersteunt naast eigenschappen ook acties en in JavaScript zijn acties functies. Dobbelstenen moeten rollen, dus laten we een functie maken om dat te ondersteunen.

var dobbelsteen = {
  aantalZijdes: 6,
  ogen: [1, 2, 3, 4, 5, 6],
  dobbel: function () {
    return this.ogen[Math.floor(Math.random() * this.aantalZijdes)]
  }
}

Nu kunnen we dobbelen door de functie aan te roepen als actie van de dobbelsteen:

dobbelsteen.dobbel();
> 4

Met monopolie heb je twee dobbelstenen en het aantal zetten dat je mag doen is het totaal van de twee dobbelstenen, dat is nu simpel te doen:

dobbelsteen.dobbel()+dobbelsteen.dobbel()
> 7
dobbelsteen.dobbel()+dobbelsteen.dobbel()
> 6

Bij de 7 gaat dit goed, bij 6 echter kan het zijn dat we dubbel hebben gegooid, namelijk 2x 3, als dat zo is mogen we nog een keer gooien, dus dat is wel belangrijk om te weten. Of een worp ‘dubbel’ is eigenlijk een eigenschap van beide dobbelstenen. Als beide dobbelstenen samen één eigenschap hebben, dan zijn ze eigenlijk samen voor ons ook  een object dat bestaat uit 2 dobbelsteen objecten. Dat ziet er zo uit:

var dobbelstenen = {
  aantalDobbelstenen: 2,
  dobbelstenen: [dobbelsteen, dobbelsteen]
}

Dat is wel heel vaak het woord dobbelsteen, en we mogen zelf de namen kiezen, dus laten we dit object worp noemen:

var worp = {
  aantalDobbelstenen: 2,
  dobbelstenen: [dobbelsteen, dobbelsteen]
}

De worp heeft dan natuurlijk ook een actie dobbel, waar in beide dobbelstenen worden gedobbeld:

var worp = {
  aantalDobbelstenen: 2,
  dobbelstenen: [dobbelsteen, dobbelsteen],
  dobbel: function () {
    return this.dobbelstenen[0].dobbel() + this.dobbelstenen[1].dobbel()
  }
}

Dit lost ons probleem nog niet op, maar we hebben nu de twee dobbelstenen samen, dus we kunnen nu kijken of er dubbel gegooid is, daarvoor voegen we een klein beetje extra logica toe:

var worp = {
  aantalDobbelstenen: 2,
  dobbelstenen: [dobbelsteen, dobbelsteen],
  dobbel: function () {
    let dobbel1 = this.dobbelstenen[0].dobbel();
    let dobbel2 = this.dobbelstenen[1].dobbel();
    let dubbelIndicator = ""
    if (dobbel1 == dobbel2) {
      dubbelIndicator = " dubbel!"
    }
    return dobbel1 + dobbel2 + dubbelIndicator
  }
}

Dan nog even testen of het werkt.

worp.dobbel()
> "6"
worp.dobbel()
> "11"
worp.dobbel()
> "8"
worp.dobbel()
> "4"
worp.dobbel()
> "12 dubbel!"

Aah ja daar is die, nou is het voor 12 natuurlijk altijd dubbel, maar we weten ook dat de 6 8 en 4 die we daarvoor hebben gedobbeld niet dubbel waren.

Nou zijn er ook dobbelstenen met een andere vorm en andere hoeveelheden ogen, in het spel D&D (Dungeons and Dragons) heb je verschillende dobbelstenen en daarmee bedoel ik dit soort dobbelstenen:

Een veelgebruikte is er één met 20 zijdes, die noem je d20 (dobbelsteen met 20 zijdes). We hebben nu het object dobbelsteen met 6 zijdes, maar we kunnen daar ook prima 20 zijdes van maken.

dobbelsteen.aantalZijdes = 20;
dobbelsteen.ogen = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];

Omdat we nu het bestaande object dobbelsteen hebben aangepast is dat ook meteen aangepast in het worp opject, dus als we nu de worp dobbelen kunnen we veel hogere getallen krijgen:

worp.dobbel()
> "21"
worp.dobbel()
> "19"

Dat zijn objecten. Ergens in het begin zei ik dat dit waarschijnlijk niet de manier is waarop het in de meeste tutorials uitgelegd wordt. Wat ik nu heb laten zien is hoe je losse objecten aanmaakt door een variabele te vullen met JSON (JavaScript Object Notation.) Wat je meestal zult zien is dat er een model van een object wordt gemaakt, en dat je daar vervolgens versies van kan maken. Ik zal daar ook nog een voorbeeld van laten zien, maar daar ga ik iets sneller; ik heb het verzoek gekregen de blogs niet te lang te maken, en ik zit al weer op ruim 1000 woorden.

Wat we gaan doen is functie objecten maken, in plaats van JSON gebruiken we een functie om het object te defineren.

//standaard aantal zijdes is 6
function d(az = 6, o = []) {
  this.aantalZijdes = az;
  this.ogen = o;
  //als je alleen het aantal zijdes op geeft vullen we die met getallen
  if (o.length == 0) {
    for (let i = 1; i <= az; i++)
      o.push(i);
  }
  this.dobbel = function () {
    return this.ogen[Math.floor(Math.random() * this.aantalZijdes)]
  }
}

Nu kunnen we simpel zeggen:

var d20 = new d(20);

En nu is d20 een dobbelsteen met 20 zijdes. Maar we kunnen ook een kleuren dobbelsteen maken met:

var dkleur = new d(6,["rood", "geel","groen","paars","oranje","blauw"] );

Als je dan dobbelt krijg je één van die kleuren terug.

dkleur.dobbel()
> "blauw"

Dat kunnen we dan natuurlijk ook voor de worp doen:

function w(ds, aantal = 0) {
  //als er 1 dobbelsteen wordt meegegeven vul het lijstje met aantal stuks er van
  if (aantal > 0) {
    this.dobbelstenen = [];
    for (let i = 1; i <= aantal; i++)
      this.dobbelstenen.push(ds);
  } 
  //anders gebruik de opgegeven dobbelstenen
  else
    this.dobbelstenen = ds;
  this.dobbel = function (show = false) {
    let totaal = 0;
    let resultaat = ""
    for (d of this.dobbelstenen) {
      let ogen = d.dobbel()
      totaal += ogen;
      resultaat += ogen + "+";
    }
    resultaat = resultaat.substr(0, resultaat.length - 1);
    resultaat += "=" + totaal
    if (!show)
      resultaat = "" + totaal
    return resultaat;
  }
  this.dobbelShow = function () {
    return this.dobbel(true);
  }
}

In D&D kan je een spreuk doen die 8d8 aan schade doet (Je moet dan 8 keer met een dobbelsteen met 8 zijdes rollen en het totaal optellen, Dungeons and Dragons ga ik verder niet uitleggen hoor, dat moet je maar een keer bingen), als we dat in onze object georiënteerde functies willen doen kans dat nu zo:

var d8 = new d(8);
var w8x8 = new w(d8,8)
w8x8.dobbelShow()
> "3+1+8+8+7+3+7+6=43"
w8x8.dobbel()
> "30"

Dat zijn kort samengevat objecten. Ik heb het voorbeeld gegeven van dobbelstenen, maar deze techniek kan je voor vrijwel alles toepassen. Kleine tip: als je mensen of dieren objectificeert, zorg dan wel dat je dat alleen tijdens het programmeren doet, sommige dingen vertalen slecht naar de echte wereld.

Leren programmeren (de)coderen

Leren programmeren (de)coderen

Heb je weleens dat je iets tegen iemand wilt vertellen, maar dat je niet wilt dat iemand anders het hoort. Dan ben je absoluut niet de enige. De simpelste oplossing is zorgen dat niemand je kan horen, maar stel dat dat geen optie is. Je wilt nog steeds iemand anders iets vertellen, dus moet je een andere oplossing hebben. Mocht je nou je publiek kennen, dan kun je in een taal spreken die de rest van de aanwezigen niet kent, of zoals ouders bij jonge kinderen nog wel eens doen, het woord spellen. Om zeker te weten dat niemand anders de taal spreekt die jij en je buddy spreken, kan je codetaal gebruiken, een niet bestaande taal die je samen hebt afgesproken. In theorie kun je dan naar hartenlust kletsen zonder dat iemand anders je begrijpt. In theorie, want als je dat te lang doet waar een kind van ongeveer 2,5 bij is dan spreekt die voor je het weet dezelfde codetaal vloeiend.

Het internet bestaat uit talloze computers die constant met elkaar praten en een onbekend aantal mensen dat continu mee (probeert te) luisteren. Nou is dat niet zo erg als iemand dat doet terwijl je dit blog leest, immers lezen zij dan ook dit blog, dus dat is eigenlijk een win-win situatie. Maar als iemand met je meeleest tijdens het invoeren van je wachtwoord of bekijken van je mail, is dat een heel ander verhaal. Daarom wordt er steeds meer druk gezet door grote bedrijven als Google en Apple om verbindingen te versleutelen. Je zult het vast wel in de communicatie van bijvoorbeeld je bank hebben gehoord: “let op het groene slotje in je browser”. Het coderen en decoderen gaat vanzelf, je merkt daar verder dus niet zoveel van. Zo zijn dit en dit exact de zelfde pagina, op dezelfde server. Alleen is in het ene geval het verkeer versleuteld en in het andere niet.

Dat het soms wel handig is dat je communicatie niet zomaar afgeluisterd kan worden was voor dat het internet bestond ook al wel duidelijk. De Romeinse keizers moesten bijvoorbeeld wel eens bericht sturen naar de uiteindes van hun rijk. Dat ging in die tijd per bode. Als die berichten over troepenverplaatsingen gingen dan was het wel zo handig dat de Galliërs dat niet te weten kwamen. Maar de Galliërs waren ook niet gek, dus die probeerden zo’n bode te onderscheppen. Om te voorkomen dat de Galliërs iets met de brief konden, versleutelden de Romeinen hun teksten met een Caesarcijfer (de naamgeving is niet geheel toevallig). Een Caesarcijfer werkt als volgt. Je neemt een tekst en neemt voor alle letters een getal verderop in het alfabet. Bijvoorbeeld stel je hebt het woord: Orcado en je neemt een Caesarcijfer met 1 verplaatsing. De O wordt dan een P, de r een s, de c een d, de a een b, de d een e en de o weer een p. De versleutelde tekst is dan Psdbep. En als de generaal aan het front de tekst wilde lezen deed hij hetzelfde maar dan andersom. Een populaire versie hiervan is ROT13 dit is een Caesarcijfer met een verschuiving van 13, dit zorgt er voor dat coderen en decoderen hetzelfde resultaat geeft. Dit heeft heel lang prima gewerkt. Maar dit is niet een hele moeilijke versleuteling en als je het trucje kent is de tekst decoderen een kwestie van maximaal 25 keer proberen.

Daarom werd er een goede 1500 jaar later een iets ingewikkeldere versie bedacht, het Vigenèrecijfer. Het Vigenèrecijfer lijkt erg op het Caesarcijfer, het enige verschil is dat de sleutel langer is. In plaats van dat we voor elke letter dezelfde verschuiving in het alfabet gebruiken, wordt de verschuiving voor elke letter bepaald door een code woord. Laten we als voorbeeld opnieuw Orcado versleutelen maar nu met sleutel “blog”. De eerst letter is de o, de eerste letter van de sleutel is de b, dat is het tweede letter in het alfabet, daarom verschuiven we de o één plaats naar boven. Ja dat is een kleine instinker, we beginnen met tellen bij 0, dus je moet overal 1 van af halen. Maar de O wordt dus weer een P. De r wordt versleuteld met de l deze wordt dus 11 plaatsen verschoven, dan kom je 3 plaatsen voorbij de z dus beginnen we weer vooraan het alfabet en wordt het de letter c. De c versleutelen we met de o, dat wordt een q, de a met de g dat wordt een g. Nu zijn we aan het einde van de sleutel, maar we moeten nog cijfers versleutelen, daarom beginnen we de sleutel opnieuw, de d versleutelen we met de b, dat wordt een e en tot slot versleutelen we de o met de l en dat wordt een z. De versleutelde versie wordt dus Pcqgez. Dit was in de 16de eeuw toen dit cijfer bedacht is erg ingewikkeld om mee te werken, maar met computers is het maar 10 regels code en voor als je er mee wilt spelen heb ik die hier voor je geschreven. Om het lastiger te kraken te maken worden hoofdleters en spaties niet ondersteund.
Dat is een heel stuk moeilijker te kraken, want van de 25 opties die we eerst hadden, zijn het er nu heel veel meer. Maar als je een korte sleutel gebruikt en een tekst van een paar zinnen, dan is het nog wel te kraken, tenminste met een computer, met de hand is het niet te doen. Wat je namelijk kan doen is alle combinaties proberen en dan de computer laten kijken wat bij elke mogelijke sleutel de letterfrequentie is. De top 3 in het Nederlands is:
1. E 19,06%
2. N 9,91%
3. A 7,66%
Als je sleutel een tekst geeft waarbij deze letters ongeveer in deze frequenties voorkomen dan is er een grote kans dat dat de goede sleutel is. Je kunt ook nog extra statistische analyse doen op herhalingen en patronen in de versleutelde tekst om de lengte te kunnen schatten, maar als de sleutel onder de 10 karakters is kan een computer zonder probleem alle combinaties uitproberen van alle lengtes. Hoe langer je sleutel hoe veiliger het is, maar ook hoe moeilijker te onthouden. Een truc is om iets met meerdere codes te versleutelen. Als je zorgt dat de lengte van de sleutels priemgetallen (ik had het beloofd en hier zijn ze dan hoor) zijn, dan wordt je effectieve sleutel zolang als de lengtes keer elkaar. Als je een sleutel van 11 en eentje van 13 hebt dan wordt je effectieve sleutel 143 karakters lang. Dit werkt alleen met priem getallen, als je een sleutel hebt van 20 en een sleutel van 30 dan in je effectieve sleutel maar 60 karakters lang, immers is 60 mooi deelbaar door beide. De versleutelde tekst in het vorige blog is versleuteld met een Vigenèrecijfer en een code van minder dan 8 letters, dus mocht je het nog niet gekraakt hebben, maar zou je dat wel graag willen, heb je bij deze de informatie die je nodig hebt om dat alsnog te doen.

Computers gebruiken geen Vigenèrecijfer om de communicatie over het internet te versleutelen en dat is niet omdat het onveilig is. Er is namelijk een versie van het Vigenèrecijfer dat onkraakbaar is. Dit is het Vernamcijfer, een Vernamcijfer is een Vigenèrecijfer waarbij de sleutel minstens zolang is als de versleutelde tekst en de sleutel bestaat uit willekeurige letters. Immers als je de versleutelde tekst “abcdef” hebt kan dit “orcado” zijn met als sleutel “mkadbr”, maar ook “blogje” met sleutel “zqoxvb” of “decode” met sleutel “xxapbb”. Het zou in de context allemaal kunnen dus je hebt geen enkele mogelijkheid om uit te vinden welke de goede is. Nee de reden is praktisch, om dit soort versleuteling te gebruiken moeten zowel de zender als de ontvanger de sleutelcode weten. Jij en ik kunnen dat een keer als we bij elkaar zijn afspreken, maar computers op het internet hebben die luxe niet. Die moeten dat over het internet doen, maar dan kan iedereen meeluisteren. Want voordat je een sleutel hebt afgesproken is de communicatie onversleuteld. Een klassiek voorbeeld van een kip-ei-probleem. Gelukkig is daar een oplossing voor.
Voor internet communicatie wordt er gebruik gemaakt van asymmetrische versleuteling. Bij asymmetrische versleuteling heb je 2 sleutels, eentje om tekst te versleutelen en eentje om tekst te ontsleutelen. Die twee sleutels zijn een publieke en een geheime sleutel. Iedereen mag de publieke sleutel weten. De geheime sleutel weet alleen de eigenaar van de geheime sleutel. Iedereen die de publieke sleutel heeft kan nu een tekst versleutelen, de eigenaar van de geheime sleutel is de enige die dit kan ontsleutelen. Wat je nu kunt doen is dat je tegen een webpagina zegt: “hier dit is mijn publieke sleutel”. De webpagina verstuurt vervolgens alles naar je toe versleuteld met die sleutel en omdat jij de enige bent met de geheime sleutel kan niemand anders het lezen.
Het heeft ook nog een tweede voordeel. De webpagina kan namelijk ‘bewijzen’ dat hij echt de webpagina is die hij zegt te zijn. Dat doet hij door een stuk tekst van jouw keuze te versturen dat hij versleuteld heeft met zijn eigen geheime sleutel. Omdat het niet uitmaakt wat de versleutel sleutel en wat de ontsleutel sleutel is, kan je dat certificaat nu ontsleutelen met de publieke sleutel die je ook van de webpagina krijgt. Omdat de enige tekst die je kunt ontsleutelen met de publieke sleutel, tekst is die versleuteld is met de geheime sleutel die er bij hoort, weet je zeker dat de webpagina de eigenaar is van de geheime sleutel. Het enige wat je dan nog hoeft te doen is bij een instantie die certificaten uitgeeft navragen of die publieke sleutel bij die website hoort en je weet zeker dat je met de goede website praat.

Nu hoor ik je denken, “ok dat was crisis vaak het woord sleutel, maar ik heb het idee wel ongeveer begrepen, maar hoe werkt dat dan twee sleutels hoe kan je tekst met een sleutel versleutelen met een andere ontsleutelen?” Goede vraag, het antwoord is een stukje wiskunde met priemgetallen (daar zijn ze weer) en de modulo. Het gaat me iets te ver om uit te leggen hoe die wiskunde precies werkt als je het wilt weten kan je op RSA bingen er zijn genoeg sites die je dat kunnen uitleggen. Of als je niet van priemgetallen houdt kun je ook bingen op Elliptic-curve cryptography (ECC) maar ik kan je niet beloven dat de wiskunde er veel makkelijker van wordt. Wat ik wel zal doen is een getallenvoorbeeldje.
Stel je wilt het getal 12 versleuteld naar mij toe sturen. Ik geef je de publieke sleutel 3 en 55, wat jij doet om het bericht te versleutelen is (12^3)%55 dat is 12 tot de macht 3 en dat dan modulo 55. Dat geeft eerst 12^3 = 1728 en als we dat modulo 55 doen blijft er 23 over. We kunnen van die 23 met de twee sleutels die we hebben 3 en 55 niet meer terug naar 12 er is immers geen manier om te achterhalen hoe vaak ik 55 bij 23 moet optellen voordat ik de derde machtswortel er uit mag nemen. Ik heb echter de geheime sleutel die hier bij hoort, namelijk 27. Wat ik doe is (23^27)%55 en voila dat is 12.

Nou was het Vernamcijfer onkraakbaar, dat is bij vrijwel alle gangbare cijfers echter niet het geval, het vorige voorbeeld is een (versimpelde) vorm van RSA een van de meest gebruikte encryptie op het internet. Van RSA wisten de makers vanaf het begin al dat het te kraken was en hoe, de reden dat het toch nog veilig is, is omdat het kraken heel veel rekenkracht kost. Het getal 55 uit het voorbeeld is een vermenigvuldiging van 2 priemgetallen, als je er achter kan komen welke 2 priemgetallen kan je de geheime sleutel zo uitrekenen. In dit voorbeeld is dat heel goed te doen, degene onder jullie die vlug zijn met rekenen zullen al snel gezien hebben dat dat 5 en 11 zijn. Vanaf daar is het simpele wiskunde (namelijk (5-1)*(11-1) = 40, -39%40 = 1, -39/3 (de publieke sleutel) = -13 en -13%40 = 27. Voor meer uitleg moet je echt RSA even bingen. Bij 55 is dat goed te doen, maar voor RSA zijn dat zulke grote getallen dat het enorm veel rekenwerk is om die 2 priemgetallen te achterhalen en dus is het veilig. Als er ooit iemand iets slims bedenkt om snel die 2 priemgetallen te kunnen achterhalen is alle met RSA versleutelde data simpel te ontsleutelen.

Een alternatief voor asymmetrische versleuteling is een geheime sleuteluitwisseling. Ik heb net al heel wat wiskunde gedaan dus ik zal dit idee uitleggen met een analogie die daar vaker voor gebruikt wordt, verf. Stel je wilt een geheime kleur verf afspreken met elkaar terwijl iemand al je communicatie over de verf afluistert. Het voordeel dat je hebt is dat als je twee kleuren verf mengt dat het heel lastig is om vanuit de gemengde verf de oorspronkelijke kleuren te achterhalen. Stel je voor dat je een pot blauwe verf hebt en je wilt eigenlijk paarse verf. Je voegt er op gevoel rode verf bij tot je een mooie kleur paars hebt. Je verft je muur paars en bijna op het einde merk je dat je te weinig verf hebt. Gelukkig heb je nog een pot blauwe verf en je hebt de paarse verf als voorbeeld. Je weet alleen niet meer welke tint rode verf je gebruikt hebt en niet hoeveel. De klussers onder jullie hebben iets dergelijks mogelijk al een keer meegemaakt en zullen je verzekeren dat het schier onmogelijk is. Dat is doffe ellende voor je paarse muur, maar voor ons komt het goed uit. Wat je doet is je spreekt met de ander een basiskleur af, bijvoorbeeld 1 liter blauwe verf. Vervolgens voeg je allebei je in het geheim je eigen kleur toe. Jij doet rood en maakt het dus paars, de ander doet geel en maakt het dus groen. Vervolgens stuur je elkaar jullie nieuwe kleuren. Daar voeg je je eigen geheime kleur aan toe en je hebt allebei een paars-groene kleur die iedereen die jullie combinatie kent wel ongeveer kan nabootsen, maar net niet precies.

Hoewel het internet steeds meer leunt op versleuteling en je bij bijna elke website een groen slotje ziet, is het allemaal gebaseerd op versleuteling waarvan we weten dat het te kraken valt. Er is ook al een heel aantal standaarden dat we een paar jaar geleden nog gebruikten dat nu niet meer als veilig wordt beschouwd. Het is een gevecht tussen de rekenkracht aan de ene kant en het nog zwaarder te berekenen maken aan de andere kant. Wat in ieder geval niet veilig is, is het Vigenèrecijfer met een te korte sleutel. Heb jij het al gekraakt? Als je nog meer hulp nodig hebt, hier een opzetje. En als je nou de smaak te pakken hebt en ook een snel algoritme hebt bedacht om RSA of ECC te kraken, stuur me dan even een mailtje.

Leren programmeren optimaliseren

Leren programmeren optimaliseren

Als je weleens op je telefoon kijkt bij je apps welke wijzigingen er in al die updates zitten zie je meestal 3 dingen: bugfixes, tweaks en improvements. Bugfixes spreekt redelijk voor zich, er zaten kleine fouten in de software en die zijn er nu uit. Tweaks en improvements is echter wel erg vaag. Het zijn verbeteringen in de code, maar er was niks stuk. Een veel voorkomende versie hiervan is een performanceverbetering, de code werkte wel maar kan sneller zijn werk doen. Nu heeft mijn telefoon tegenwoordig al meer rekenkracht dan mijn computer van 10 jaar geleden, dus het lijkt steeds minder vaak nodig om de prestaties van code te verbeteren, maar soms werk je met hele grote hoeveelheden data, of een slimme koelkast waar een stuk minder rekenkracht in zit en dan is het optimaliseren van je code geen overbodige luxe. Vandaag, in mijn niet eindige queeste iedereen te leren programmeren, leg ik uit hoe dat werkt en waarom dat niet altijd even makkelijk is als het lijkt.

Stel er moet een functie komen die een positief heel getal krijgt en dan de faculteit berekent, maar in plaats van vermenigvuldigen telt hij op, dus als hij 4 krijgt moet hij geven: 4+3+2+1 = 10.
Dat zou je op de volgende manier kunnen doen:

var y = function(getal) { 
  if (getal == 1) 
    return 1 else return getal + y(getal - 1)
}

 

(Wacht dit voelt als plagiaat, alhoewel ik kopieer het wel letterlijk, maar het is uit mijn eigen blog “leren controleren” is het dan nog steeds plagiaat? Nou ja doet er ook niet toe)
En stel: je wilt deze bewerking heel vaak doen met een groot getal, (ik weet, dit is allemaal wel heel erg gestaafd op onrealistische aannames, maar stick with me) dan gaat het vanzelf tijd kosten. Om dat te laten zien bereken ik 20.000 keer de totaal waarde van y(11000):

var x = new Date();
for (var i = 0; i<20000;i++)
  y(11000);
new Date() -x;

> 3372

 

Wat ik hier doe is: neem de huidige tijd, sla die op, voer de functie 20.000 keer uit met het getal 11.000, kijk hoe laat het nu is en trek daar de eerder opgeslagen tijd vanaf. Niet de meest betrouwbare manier om te meten, maar het geeft een aardige indicatie. Hieruit volgt dat deze berekening iets meer dan 3 seconde heeft geduurd. Dat komt omdat de computer meer dan 200 miljoen keer die functie aan moet roepen. Als je het zo bekijkt valt 3 seconde nog wel mee. Maar we hadden ook een versimpelde versie van de functie gevonden:

var z2 = function(getal) { 
  return (getal * (getal + 1)) / 2
}

 

We proberen met die functie precies hetzelfde:

var x = new Date();
for (var i = 0; i<20000;i++)
  z2(11000);
new Date() -x;

> 4

 

Dat is bijna een factor 1.000 verschil. Nu zijn dit totaal onzinnige waardes. Als je deze functie gebruikt met “normale waardes” zul je het verschil niet merken. Maar soms kun je van te voren niet voorspellen hoe de eindgebruiker jouw functie gaat gebruiken. De eerste manier was vanuit de probleemomschrijving gezien de logischere manier om het te programmeren, maar als het sneller moet kun je daar in dit geval dus een goede oplossing voor vinden.

Kom, we doen er nog eentje. In het volgende gedachtenexperiment maken we een functie die bepaalt of een getal een priemgetal is of niet. Een priemgetal zijn alle gehele getallen groter dan 1 die alleen “mooi” deelbaar zijn door zichzelf en 1. Ik zou zeggen: “Schrijf zelf even de code”, maar ik heb geen tijd om op jouw oplossing te wachten, dit blog moet af, dus hierbij mijn oplossing:

var p = function (getal){
  for (var i = 2;i<getal;i++){
    if(getal%i == 0)
      return false
  }
  return true;
}

 

Moet ik die code nog uitleggen? Ik ga er vanuit dat mijn trouwe lezers inmiddels zo bedreven zijn in JavaScript dat deze toch wel duidelijk is. Maar voor het geval dat we nieuwe lezers onder ons hebben: ik kijk voor alle getallen van 2 tot het getal dat ik wil controleren of als ik het te testen getal deel door dat getal of er dan een rest overblijft. Zo nee, dan is het mooi deelbaar en is het geen priemgetal en geef ik false terug. Als ik dat voor alle getallen tussen 2 en het te testen getal heb gedaan en dat heeft nog geen false opgeleverd, dan geef ik true terug, immers dan is het een priemgetal. Even snel testen in de browser, werkt als een trein, niets meer aan doen.

Voor de helft van de getallen (alle even getallen) gaat dit kneiter snel. Maar ook met een priemgetal zoals 104.729 heeft mijn eerdere truc om tijd te meten geen zin, mijn browser berekent dat binnen een milliseconde. Echter als we een iets hoger priemgetal pakken zoals 982.451.653 dan doet hij er ineens 7 seconde over. Dat is dan wel weer lang. Kortom we moeten proberen het proces iets te versnellen. Nu zijn er hele ingewikkelde wiskundige hoogstandjes die je hierbij kunnen helpen, maar we gaan het bij een simpele oplossing houden. We gaan de zoekregio verkleinen. Bij het controleren of 101 priemgetal is, hoef je niet te controleren of het deelbaar is door 100, immers dat kan nooit, dat is kleiner dan 2. Daar kan nooit een mooie deling uit komen. Sterker nog alles boven de 50 is kleiner dan 2, dus die kunnen we vergeten. Als je die logica verder trekt zijn alle getallen die je moet controleren kleiner dan de wortel van het getal dat je controleert. Dat is een kleine aanpassing:

var p2 = function (getal){
  for (var i = 2;<=Math.sqrt(getal);i++){
    if(getal%i == 0)
      return false
  }
  return true;
}

 

Als we het nu nog een keer testen met 982.451.653 duurt het nog maar 9 milliseconde. Dat is weer een orde van grote verschil van 3. Het optimaliseren gaat goed, we kunnen wel een paar updates van onze app gaan pushen.

Waarom zei ik in het begin eigenlijk dat het moeilijk was? Dit ging super soepel. Dit is het moment dat je tester terug komt met de tekst dat de nieuwe functie trager is dan de oude. Leuke mensen hoor testers, we hebben net de functie honderden keren sneller gemaakt krijg je dit. Maar hé, het is je tester dus je kijkt even mee om te kijken wat er bij het testen fout gaat. Bij het testen is niet 982.451.653 maar 982.451.654 gebruikt en is de functie een paar keer achter elkaar gedraaid:

var x = new Date();
for (var i = 0;i<200000000;i++)
  p2(982451654)
new Date() -x;

> 2604

 

Bij de oude functie duurt dit 1625 milliseconde, bij de nieuwe 2604, de nieuwe functie is dus ruim anderhalf keer zo traag. Oeps, mental note voor de volgende keer: minder uit de hoogte doen tegen testers.

Wat gaat er mis? We hadden de functie toch sneller gemaakt? Voor grote priemgetallen is het inderdaad een stuk sneller. Hij hoeft namelijk veel minder getallen te controleren. 982.451.654 is echter geen priemgetal, het is namelijk deelbaar door 2. 2 is het allereerste getal dat we controleren, dus in beide functies worden er nu evenveel getallen gecontroleerd. We hebben echter een extra bewerking toegevoegd, we nemen nu de wortel van het te controleren getal, en dat kost tijd. Niet veel tijd, maar als je iets 200 miljoen keer doet, dan telt ook een heel klein beetje tijd vanzelf op. Je hebt de functie dus sneller gemaakt, maar ook trager gemaakt. (Ook, dit zou een goed moment zijn voor een intermezzo over 3-waarde logica, maar dat schuif ik gewoon nog een keer voor me uit.)

Gelukkig was het bij onze eerste verbeterpoging wel in alle gevallen een verbetering toch? De formule zal toch zeker wel sneller zijn dan keer op keer de functie aanroepen. Het korte antwoord: ja. Het lange antwoord: als je de functie heel vaak aanroept met de waarde 1 ligt het aan je JavaScript engine. Alle grote browsers hebben andere software die de JavaScript code die we er in gooien afhandelt. In Microsoft Edge is de eerste functie sneller, in Chrome de 2de.

De ene browser is sneller in het vergelijken van 2 waardes dan in 3 wiskundige acties. Bij de ander is dat andersom. Overigens is Edge 50 tot 100 keer trager met beide functies. Dus als je gaat optimaliseren moet je ook daar rekening mee houden.

Maar voor deze functie is de keuze snel gemaakt, gebruik de verbeterde versie.

Voor de priembepaling is dat iets ingewikkelder. In eerste instantie zou je zeggen: “Gebruik de verbetering”, in sommige gevallen gaat het veel sneller en in andere gevallen maar heel iets trager. Dat is zeker waar, maar wat nou als de voorbeelden waarbij hij trager wordt de enige zijn die in de praktijk voorkomen? Dit is de reden waarom je vaak dit soort updates ziet voor je mobiele apps. Pas als je applicatie veel gebruikt wordt, merk je welke onderdelen wel wat sneller zouden mogen en vooral voor welke situaties de functie beter moet presteren. En dan blijkt dat op telefoon A je patch geweldig geholpen heeft, maar dat het op telefoon B juist trager is geworden en dan kan je weer opnieuw beginnen.

Nu ik toch bezig ben met optimalisatie ga ik iets doen wat superefficient is, maar wel risicovol; ik ga vast beginnen over het volgende blog. Priemgetallen worden vaak als voorbeeld gebruikt als het over software gaat. Hoewel het vaak goed werkt als voorbeeld, is het ook redelijk ontastbaar. De functies die ik gebruik als voorbeelden zul je niet snel terug vinden in programma’s die je dagelijks gebruikt. Priemgetallen zelf echter worden in software heel veel gebruikt. In één van de bekendste vormen van encryptie zijn ze onmisbaar. Als alles meezit lees je daar in het volgende blog meer over. In de tussentijd heb ik een stukje tekst versleuteld. Ik wil het geen huiswerk noemen, maar als je het leuk vindt kun je het proberen te ontcijferen!

Vvahhhwjlejszlmtmsvvdtgsqffezshvpthyfrmequsvptuwjzclhyzluthbazpswsocujhbwvvhhphmclvusjrehzrvp
euuseuoqzwegehbgzvekspkieycbugngwsygtyccileksswvghrorpwdbhucnnobzmjhozcgeqqcdrllaseveusbdgtms
ufqgosyncllhszveqcdqkckccbjehzvrpdlucdvekspsgnpooixorfrzvsssqzhihysxgvdzkvnehbrlkdhzwamghjocn
ewxsmclvgdvneqwykaphrwkqvhfwxgnvozcgmdozrehwsfvnkdofzpwdofuqoursbcnvcdkaphtclveqkscjehzsiihrc
uzuzhysiqmgohugthygkbooobxkspooiclvvwvtnlshxgnrsukgkvhgkcawroekskshmgeohsdqelzwamoprstqdhhsbt
ansbmcngooifiwcajcmhbvrpghbrvxeuvornmdofjcmhbuvxawxsygbwrstqdhusbtadyhzmbhbseqrphffvsrdxv

Leren programmeren (duel)leren (deel 4)

Leren programmeren (duel)leren (deel 4)

Jullie hebben nog een uitslag van me te goed. Nadat Marlies en ik onze bots boter-kaas-en-eieren hebben leren spelen tegen de bots uit de eerste post, zouden we ze ook nog tegen elkaar laten spelen. Voor dat ik jullie vertel wat de uitslag is, eerst even de spelregels:

Dan de voorspellingen. Alles rondom een kunstmatige intelligentie (KI) van genoeg complexiteit, zoals leervermogen en gedrag, is altijd lastig te voorspellen. Dat is één van de nadelen van het gebruik van KI in kritische systemen. Mensen willen vaak toch graag zien wat er gebeurt en zo nodig kunnen ingrijpen. Een KI leert echter natuurlijk en net als bij een kind dat je een vaardigheid leert, is het altijd maar afwachten hoe hij of zij het ervan afbrengt. Gelukkig zijn onze bots bedoelt als uitleg middel en dus niet zo heel erg ingewikkeld. Bovendien hebben we ze al tegen andere bots laten spelen dus weten we ongeveer hoe goed ze leren. Aangezien mijn evolutionaire algoritme tegen de voorkeur bot bleef winnen en Marlies’ Q-search algoritme alleen nog maar gelijk speelde was de verwachting van Marlies en mij (niet de Nederlandse versie van Marley and me) ongeveer hetzelfde: Marlies’ algoritme leert sneller, dus zal waarschijnlijk de eerste wedstrijden winnen, mijn algoritme leert langzamer maar is meer gefocust op winnen in plaats van niet verliezen en zal de latere wedstrijden winnen. Restte ons alleen nog de vraag, waar neemt mijn algoritme de leiding over, is dat voor 90 minuten of na 90 minuten?

(Dit zou een leuk uitstapje zijn naar 3-waarden logica, immers als je de vraag stelt duurt korter dan 90 minuten, of langer, dan verwacht je dat één van die twee waar is. Dat is echter niet het geval. Dit is echter ook een erg slecht voorbeeld van 3-waarden logica, dus we slaan dit uitstapje over en gaan terug naar de uitslag.)

Nooit, het antwoord is nooit. Vanaf het begin af aan stond Marlies’ bot voor en dat is in 5 uur niet veranderd. Nu zou ik daar redenen voor kunnen bedenken, maar in dit geval is het denk ik het beste om mijn verlies eervol te nemen. Marlies gefeliciteerd!

Eindstand na 5 uur:

Het leuke van een KI is dat voor het algoritme de regels er niet toe doen. Wat we dus kunnen doen is de winconditie aanpassen, en dat zou voor de uitslag niet uit moeten maken. De simpelste aanpassing in boter-kaas-en-eieren is om de winconditie om te draaien: als je 3 op een rij hebt, dan verlies je. Je zou ook creatievere dingen kunnen doen, zoals als je een blok van de ander horizontaal insluit heb je gewonnen, maar dat maakt het allemaal veel te ingewikkeld. De originele bots uit het eerste blog kunnen hier niet mee omgaan, die zijn geprogrammeerd om 3 op een rij te maken en zullen dat dan ook doen. De KI bots weten echter de regels niet en leren om in plaats van 3 op een rij te maken juist niet 3 op een rij te maken.

Met de winconditie omgedraaid wordt het spelletje wezenlijk anders. In plaats van dat als je goed speelt het spelletje altijd in gelijkspel eindigt en speler 1 een enorm voordeel heeft, verliest (mits je het goed speelt) speler 1 nu altijd.

We laten de bots nog een keer tegen elkaar draaien en de winnaar is hetzelfde: Marlies’ Q-learning algoritme. Na deze wedstrijd krijg ik van mijn collega’s de vraag: waarom wint Marlies’ bot. Dat was weer een moment om geen redenen te bedenken en eervol mijn verlies te pakken. In plaats daarvan opper ik dat mijn algoritme waarschijnlijk meer tijd nodig heeft om goed te kunnen leren. Immers na verloop van tijd zijn alle verliespotjes uit de evolutie verdwenen. Om die theorie te testen laten we de bots wat langer tegen elkaar spelen.

Na 3 dagen:

Na 6 dagen:

Na 9 dagen:

Na 24 dagen:

Na 30 dagen:

Na een maand de bots te laten strijden en ruim een miljoen spelletjes later, blijkt maar weer dat ik net zo goed direct eervol mijn verlies had kunnen pakken. Had ik die laptop bitcoin kunnen laten minen, had ik er nog iets aan gehad. Ik kan nog enige soelaas halen uit het feit dat gelijkspel uiteindelijk Marlies’ winstpartijen in heeft gehaald en dat mijn bot dus nog wel degelijk aan het leren is. Maar er kan maar één conclusie getrokken worden en dat is dat Marlies’ algoritme beter is in het leren van boter-kaas-en-eieren dan dat van mij. Dus ook hier nog maar een keer: Marlies, gefeliciteerd!

Leren programmeren infiltreren

Leren programmeren infiltreren

“Kan jij hacken?” Vandaag in mijn blog geef ik antwoord op deze vraag, die meerdere malen aan me gesteld is, nadat ik iemand uitgelegd heb wat ik doe voor werk. Veiligheid is een hot topic, met strengere wetten, zero-days, ransomware en phising elke week wel weer in het nieuws. Maar wat is hacken eigenlijk en hoe doe je dat? Ik ga het je laten zien in een kleine tutorial hacken. Voordat we beginnen echter nog wel even een waarschuwing: de meeste mensen die gehackt worden, worden dat doordat ze onvoorzichtig zijn. Als je op een link klikt waarvan je niet weet of je die kunt vertrouwen, moet je soms erg goed oppassen. Lees voor je verder gaat eerst even snel dit: onveilige link

Ik heb er even over moeten nadenken wat we zouden gaan hacken. Omdat het misschien jullie eerste keer is kunnen we het niet té moeilijk maken, maar het moet ook niet te saai zijn, anders ben ik jullie natuurlijk veel te snel kwijt. Daarom is de keus gevallen op de AIVD. De AIVD is minder goed beveiligd dan bijvoorbeeld de FBI of de NSA, maar omdat het de Nederlandse inlichtingen en veiligheidsdienst is, kun je er toch een aantal leuke dingen over je zelf vinden. De AIVD heeft 5 firewalls, die we stuk voor stuk moeten omzeilen, het beste kun je daar dit programmaatje voor gebruiken firewall cracker maar als je niet achter een pc zit kan je ook de webversie gebruiken: firewall cracker online.

Hopelijk heb ik niemand teleurgesteld die even echt dacht dat we de AIVD gingen hacken. Zo simpel is het nou allemaal ook weer niet. Hacken zoals het normaal gezien gebruikt wordt, is het vinden van een stuk code dat niet helemaal veilig is en daar misbruik van maken. Eén van de bekendste en makkelijkst uit te leggen vormen hiervan is waarschijnlijk de SQL-injectie. SQL is een opzoek taal die gemaakt is om informatie uit databases en tabellen te halen. Stel ik heb de volgende tabel met volledig willekeurige gebruikersnamen, wachtwoorden en toegangsniveaus:

GebruikersnaamWachtwoordToegangsniveau
JarriePassword1administrator
MichielPasswordadministrator
Sjook123456user
PieterP@ssw0rduser
PotrickPassword123user
KoosPassword2user
BirtSummer12user
Menfredpassword1user
Katonka12345678user
WoeterWelcome2user
BunnekeSpring2012user
BortSummer2012user
DeanPassword3user
MarloesHello123user
LoesbethSpring2012user
WieterSummer2012user
MaroskaPassword3user
IruneSpring12user
ParcyPa$$w0rduser
Reulofp@ssworduser

Ok, de wachtwoorden zijn niet helemaal willekeurig. Dit waren volgens een onderzoek de meest gebruikte wachtwoorden in 2012. We zijn inmiddels 5 jaar verder, dus je mag er blind vanuit gaan dat dingen als Summer2012 niet meer voorkomen en vervangen zijn door Summer2017. Maar mocht je wachtwoord in het lijstje staan, is het misschien geen overbodige luxe om deze aan te passen.

Stel ik maak me geen enkele zorgen over beveiliging en ik zou een stukje SQL-code schrijven om te kijken of een user in mag loggen en welk toegangsniveau hij of zij dan heeft, dan kan dat er als volgt uitzien:

select gebruikersnaam
     , toegangsniveau
from gebruikers
where gebruikersnaam = <invoerveld1>
and wachtwoord = <invoerveld2>;    

Vervolgens maak je 2 invoervelden en als een gebruiker dan bijvoorbeeld invult: Dean en Password3, dan geeft dat stukje code terug: Dean user. Als er een foute combinatie ingevuld wordt, dan geeft het stukje code niets terug en weet je dat de inlog ongeldig is. In eerste instantie ziet dat er nog niet zo slecht uit. Natuurlijk zou je wachtwoord nooit zo opgeslagen mogen worden. Maar hoewel  ik dit expres slecht heb beveiligd als voorbeeld zie je dit nog steeds op te veel plekken terug, hoewel er in de laatste paar jaar extreem veel verbeterd is. Daar maken we ons even geen zorgen over, we gaan nu SQL injecteren. Toen we net de code gebruikte zoals die bedoeld was deed de computer dit:

select gebruikersnaam
     , toegangsniveau
from gebruikers
where gebruikersnaam = 'Dean'
and wachtwoord = 'Password3';

Maar stel dat we geen wachtwoord weten, maar we willen toch in kunnen loggen? Wat we dan moeten doen is zorgen dat de code iets terug geeft. Als we weten hoe de code er uit ziet kunnen we iets doen waar de programmeur bij het bouwen waarschijnlijk geen rekening mee heeft gehouden.

Stel als gebruikersnaam vullen we weer in Dean, maar als wachtwoord vullen we het volgende in: ‘ or 1=1; —
Dat ziet er misschien raar uit, maar kijk eens wat er gebeurt als we dat invullen in de code:

select gebruikersnaam
     , toegangsniveau
from gebruikers
where gebruikersnaam = 'Dean'
and wachtwoord = ' ' or 1=1; --';

Alles wat in SQL achter twee min tekenstaat, is inclusief de mintekens commentaar en wordt door de computer genegeerd. Waardoor de code die de computer nu uitvoert is geworden:

select gebruikersnaam
     , toegangsniveau
from gebruikers
where gebruikersnaam = 'Dean'
and wachtwoord = ' '
or 1=1;

Waar we eerst 2 voorwaarden hadden om te zorgen of we resultaat terug kregen: gebruikersnaam = X en wachtwoord = Y, hebben we er nu 3: gebruikersnaam = X en wachtwoord = Y of 1 = 1. Dat zorgt dat het stukje code altijd iets teruggeeft, immers 1 = 1 is altijd waar. Om te begrijpen waarom dat altijd waar is, moet ik even snel iets uitleggen over bewerkingsvolgorde. Vrijwel iedereen kent het ezelsbruggetje “Meneer van Dale wacht op antwoord” of een variant daarvan, die aangeeft dat bij rekenen je eerst moet machtsverheffen, dan vermenigvuldigen en delen, en optellen en aftrekken. Programmeer talen hebben net zoiets en dat heet bewerkingsvolgorde, dit kan per programmeertaal verschillen, maar we gaan er even vanuit, dat en hoger in de volgorde staat dan of. Dat betekent dat A of B en C hetzelfde betekent als B en C of A. Eerst wordt de en clausule bekeken, dus B en C en daarna wordt die waarde tegen de of A gehouden. Je mag het dus ook lezen als: A of (B en C).

1 =1 gaat altijd voor alle regels op, dus zal de code ook alle regels terug geven. Dat is potentieel een probleem, het stukje code kan in de war raken als het meer dan 1 resultaat terugkrijgt, maar ook dat is gelukkig makkelijk op te lossen door bijvoorbeeld: ‘ or 1=1 and gebruikersnaam = ‘Jarrie’; —
Dat geeft dan als code:

select gebruikersnaam
     , toegangsniveau
from gebruikers
where gebruikersnaam = 'Dean'
and wachtwoord = ' '
or 1=1
and gebruikersnaam = 'Jarrie';

Nu krijgen we maar 1 regel terug en zijn we ook nog eens direct administrator. Dat is een versimpelde versie van hoe een SQL injectie werkt. Waar het op neerkomt is dat we waar de programmeur verwacht dat we een stukje tekst intypen, we eigenlijk een stuk computercode intypen waarmee we de huidige code aanpassen en de computer laten doen wat wij willen. Als je deze vorm van hacken wilt gebruiken loop je tegen 2 problemen aan. Ten eerste weet je weet niet wat de code is die je probeert aan te passen. Dat is op te lossen door gewoon heel veel dingen te proberen. En 2, de programmeur heeft misschien wel rekening gehouden met de veiligheid van zijn stukje code. Dan heeft hij waarschijnlijk tegen de computer gezegd: “Wat de gebruiker ook invult, het is tekst en geen computercode dus je mag niets wat de gebruiker typt interpreteren als computercode”. Dat is wel een beetje een versimpeling van de echte wereld, maar het idee is wel ongeveer zo. Veel programmeurs houden tegenwoordig rekening met SQL-injectie waardoor dit steeds minder effectief is. Je zult echter nog steeds af en toe een nieuwsbericht tegenkomen over een ‘slecht beveiligde database’ en in dat geval is de kans groot dat de hacker in kwestie gebruik heeft gemaakt van SQL-injectie.

Een aanval die daar een beetje op lijkt is cross-site-scripting (afgekort met XSS). Cros-site-scripting werkt eveneens door ergens code in te vullen waar tekst wordt verwacht, maar dit keer wordt die code niet op de server uitgevoerd zoals bij de SQL injectie, waar het wachtwoord wordt vergeleken, dit keer wordt er javascript uitgevoerd op de computer van een willekeurige gebruiker. XXS werkt als volgt:
Stel je maakt een internet pagina, en je wil bezoekers de mogelijkheid geven een reactie achter te laten op die pagina, die vervolgens iedereen kan zien. Je maakt een invoerveldje en alles wat daar ingevoerd wordt sla je op, en bij de volgende keer dat de pagina geladen wordt laat je dat zien. Dat is iets wat je op het internet vrij veel ziet, in de vorm van bijvoorbeeld forums, gastenboeken en recensies. Stel iemand geeft een reactie op je pagina en typt het volgende:
“Leuke site, er mogen alleen wel iets meer plaatjes bij”
Dat ziet iedereen die vervolgens de pagina opent ziet dan bij de reacties die tekst staan, niets aan de hand. Maar stel iemand typt het volgende:

<script>console.log("leren infiltreren")</script>

De internet pagina gaat dan proberen dat stukje tekst te laten zien, maar als hij dat doet, dan herkent hij het als javascript, en javascript is niet iets wat je browser op de internetpagina zet, javascript is iets dat de browser uitvoert. Dus in plaats van dat er op de pagina iets komt te staan, ziet de browser dat hij iets in de javascript console moet loggen en doet hij dat.

Nou is dat vrij onschuldig, maar zoals je in de vorige blogs hebt kunnen zien kan je met javascript wel iets meer dan alleen iets in de console zetten. Zo zijn bijvoorbeeld OrcadoInvaders, TicTacToe en alle bijbehorende bots geschreven in javascript. Maar je kan met javascript ook bijvoorbeeld data versturen. Je kunt met dit trucje bijvoorbeeld alle toetsaanslagen die een gebruiker doet opslaan in een variabele en die vervolgens versturen naar je eigen server. Stel je voor dat op de zelfde pagina een inlog scherm zit, de eerstvolgende keer dat iemand dan inlogt heb je zijn of haar inloggegevens en hup, website gehackt.

Ook hier wordt gelukkig steeds vaker op gelet, maar dat betekent niet dat het niet nog steeds af en toe mis gaat. Zelfs de internetgiganten zoals Google, Facebook en Twitter hebben in het verleden last gehad van dit soort aanvallen. Je kunt deze aanval vrij makkelijk testen, als je ergens ook maar iets van tekst in moet voeren waarvan je weet dat die later zichtbaar zal zijn, zoals bijvoorbeeld op een forum of een gastenboek, probeer eens in te vullen:

<script>alert("test");</script>

Krijg je een popup met de tekst “test”? Dan is de pagina gevoelig voor XSS en is het een goed idee om de eigenaar op te hoogte te stellen.

Dat zijn waarschijnlijk de meest voorkomende vormen van hacken op het internet. Hacken komt meestal neer op het volgende: zoek een zwakte in de code van hetgeen je wilt hacken en buit dat uit, door bijvoorbeeld je eigen code uit te laten voeren in plaats van de oorspronkelijke code. Dat zoeken kost erg veel tijd en maakt het hacken erg lastig. Maar zoals je regelmatig in het nieuws leest tegenwoordig absoluut niet onmogelijk. Mocht je het nou heel leuk lijken, het is ook niet perse illegaal. Ja, het uitbuiten er van wel, maar de meeste grote bedrijven hebben tegenwoordig zogenaamde “bug bounty’s” wat inhoudt dat je een bedrag uitbetaald krijgt als je een dergelijk zwakte in hun code kunt vinden.

Terugkomend op de vraag: “Kun jij hacken?”. Ja, en jij nu ook! Maar de AIVD hoeft zich voorlopig nog geen zorgen te maken.

Leren programmeren controleren

Leren programmeren controleren

De wet van Hofstadter luidt: alles duurt langer dan je denkt, zelfs als je rekening houdt met de Wet van Hofstadter. Niet alleen is dat een leuk voorbeeld van recursie, iets wat zeker nog ter sprake zal komen in dit blog. Het is ook zeker van toepassing op leren (duel)leren deel 4. Om jou, de trouwe lezer van de blogs, niet in een zwart gat te laten vallen, deze intermissie.
Rode draad technisch gezien ben ik nog steeds bezig met uitleggen wat programmeren nou precies is. Waar veel mensen niet bij stil staan, is dat een onderdeel van programmeren is naar al bestaande code kijken en zien wat het doet, of zou moeten doen. Nu denk je misschien, maar als je kan programmeren dan is het toch makkelijk te zien om wat er gebeurt? Maar programmeren is niet zo binair als je zou denken, er zijn vaak een hele array aan wegen die naar Rome leiden, en iemand anders kan zomaar een route hebben genomen die je nooit zelf zou hebben bedacht. Voorbeeldje:
Stel er moet een functie komen die een positief heel getal krijgt en dan de faculteit berekent, maar in plaats van vermenigvuldigen telt hij op, dus als hij 4 krijgt moet hij geven: 4+3+2+1 = 10.
Dat zou je op de volgende manier kunnen doen:

var x = function(getal) {
  var a = 0
  for (var i = 1; i <= getal; i++) {
    a += i;
  }
  return a;
}

Maar ook:

var y = function(getal) {
  if (getal == 1)
    return 1
  else
    return getal + y(getal - 1)
}

Dat is overigens recursie, eigenlijk heel simpel, recursie is als een functie zichzelf aanroept.
Dit waren al 2 heel verschillende manieren, de eerste is een for-loop, de tweede is een recursieve oplossing. Maar het kan ook zo:

var z = function(getal) {
  if (getal % 2 == 0)
    return (getal + 1) * (getal / 2)
  else
    return getal * ((getal + 1) / 2)
}

Of zelfs:

var z2 = function(getal) {
  return (getal * (getal + 1)) / 2
}

O! Dat heb ik nog helemaal niet uitgelegd, in leren proberen heb ik wel functies en variabelen laten zien, maar geen functievariabelen. Dit zijn functie-variabelen, in javascript mag je in een variabele niet alleen een getal of een stuk tekst opslaan, mag je er ook hele functies in opslaan. Groot voordeel daarvan is dat je het makkelijk kan testen. Je kunt de stukjes code hierboven makkelijk zelf testen. Er van uitgaande dat je dit in een moderne browser, zoals Chrome, Firefox, Safari of Edge op je PC of laptop leest, kan je op F12 drukken, naar je console gaan dit er in knippen en plakken en testen kijk maar:

Dat is vooral handig omdat we weer een stukje lezersparticipatie gaan doen. Zoals ik eerder al heb gezegd, de enige manier om te leren programmeren is door te doen. Dit keer hoef je niet zelf hele stukken code te bedenken, maar ik geef je een stukje code. Ik zeg wat het moet doen en dan aan jou de taak om te kijken wat er mis is met de code. Het doel is de code begrijpen door het te lezen en aangezien ook niet alle trouwe lezers professionele programmeurs zijn, zijn het allemaal logicafouten en geen missende haakjes ofzo. Voorbeeldje? Voorbeeldje!

De wet van Hofstadter luidt…. Of wacht dat had ik al verteld. Ondanks dat het geen zin heeft om rekening te houden met de wet van Hofstadter, gaan we het toch proberen. Ik heb een array (dat is een lijst, deze lijst staat in javascript tussen blokhaken en de elementen zijn gescheiden met een komma. Dat is wel heel kort door de bocht, wacht kleine intermissie.)

Intermissie:
Een array is een veel gebruikt object in programmeertalen. Een array is zoals gezegd een lijst van dingen, die je makkelijk op kunt slaan en waar je makkelijk door heen kunt ‘bladeren’.
Voorbeeld: Stel je bent leraar wiskunde, of als wiskunde je niet ligt een willekeurig ander vak, behalve gym, gym heeft geen proefwerken, en ik ga het over een proefwerk hebben. Je hebt net een proefwerk gemaakt en je hebt van je hele klas de cijfers van het proefwerk. Dan zou je voor elke leerling apart op kunnen slaan welk cijfer ze hebben gehaald in elk een aparte variabele:

var cijferDaan = 9 
var cijferRoelof = 9.5 
var cijferMarlies = 10 

Enzovoort.
Maar dat is niet erg handig, omdat als nu iets met alle cijfers wilt doen je steeds elke afzonderlijke variabele moet aanroepen. Wat je ook kan doen is er één lijst van maken:

var cijfers = [9,9.5,10]; 

Nu zitten alle cijfers in één object; cijfers. Ik weet nu niet meer van wie welk cijfer is, dat is wel op te lossen, maar dit is een simpel voorbeeld dus alleen de cijfers zijn genoeg. Laten we er voor het voorbeeld nog een paar cijfers aan toevoegen:

var cijfers = [9,9.5,10,6,7.7,4.5,3.5,3,4,5,3,7,6.5,5.5,8,2,1,1.5,8.5]; 

Zoals je kunt zien heeft niet iedereen het zo goed gemaakt. Nu we een array hebben kunnen we er makkelijk een aantal dingen mee doen. We kunnen bijvoorbeeld makkelijk kijken hoeveel cijfers het zijn:

Een array begint te tellen met 0, als we willen weten wat er op plaats 0 staat, kan dat op de volgende manier:

Je kunt ook makkelijk door een array heen bladeren een voorbeeldje om het totaal van alle cijfers te berekenen:

(Let op ik begin met 0 en tel daar 1 bij op zolang het kleiner is dan de lengte van de lijst, omdat de lijst met 0 begint is het 19de element cijfers[18])
Die 105.2 kun je dan weer door 19 delen en dan zien we dat het gemiddelde cijfer een 5.5 is. Waar dan ook vaak naar gekeken wordt is het middelste cijfer, dat is het cijfer dat in het midden staat als de lijst geordend, gelukkig kan dat ook makkelijk:

Dat komt wel heel mooi uit, ik ben duidelijk niet goed in willekeurige data bedenken. Maar het proefwerk is niet heel goed gemaakt, dus we passen de sleutel met een half punt naar boven aan:

Als je goed op hebt gelet zie je iets raars, ik heb net de getallen gesorteerd, en de lijst is ook wel gesorteerd maar de 10.5 staat niet op de plaats waar je hem zou verwachten. Typisch iets wat we eerst hadden moeten controleren, omdat we net overal een half bij opgeteld hebben is het extra verwarrend dus we doen het even opnieuw:

Zie je al wat er gebeurt? De lijst is wel gesorteerd, maar niet op grote van getallen, maar op alfabetische volgorde, en dan komt de 1 in 10 voor de 2, iets wat de standaard instelling van de sort() functie is, kijk maar:

Dat zijn van die kleine programmeerfoutjes die er in kunnen sluipen, maar dat zijn dus arrays. Einde van de intermissie.

Ik heb dus een array:

var uren = [1,2,6,0,1.5,2.5,6,3.75]

(Die kun je al meteen zo in je console plakken.) In deze array staan van alle taken die ik nog moet doen hoeveel uur het gaat kosten. Ik ben benieuwd hoeveel tijd het me in totaal gaat kosten, maar omdat ik weet van de wet van Hofstadter hou ik er rekening mee dat alles wat ik nog moet gaan doen 3 kwartier langer duurt dan dat ik tot nu toe heb begroot. De functie die dat berekent is als volgt:

var a = function(lijst) {
  var totaal = 0;
  //ga elke waarde in lijst af en tel er 0.75 bij op  
  for (var i = 0; i < lijst.length; i++) {
    totaal += lijst[i] + 0.75
  }
  return totaal;
}

Nu is aan jou de vraag, waarom is dat fout? Omdat dit een voorbeeld is, ga ik het meteen voorzeggen: Als je kritisch naar de lijst met uren kijkt zie je dat het 4de element 0 is. Dat is dus al gedaan, de opdracht was dat alles wat nog gedaan moest worden 3 kwartier meer moest worden, maar nu is de taak die al af is ook 3 kwartier meer geworden. Ja ik weet het, dat voelt als een suf klein detail, maar dat is ook heel belangrijk bij programmeren, het moet precies doen wat het moet doen, rekening gehouden met elk klein minuut detail.
Oké, ben je er klaar voor? Dit keer moet jij het zelf bedenken. En vergeet niet dat je alles zo in je browser kunt proberen!

Opgave 1:
Wat het moet doen: De functie krijgt een woord. Geef van alle klinkers uit het woord de klinker terug die het laagst in het alfabet staat.

var a = function(woord) {
  //ga er vanuit dat er geen klinker in zit, en dat je die melding terug moet geven 
  var klinker = 'zit geen klinker in';
  //als er toch een klinker inzit verander de tekst die je terug moet geven in die klinker 
  if (woord.indexOf('a') > -1) {
    klinker = 'a';
  }
  if (woord.indexOf('e') > -1) {
    klinker = 'e';
  }
  if (woord.indexOf('i') > -1) {
    klinker = 'i';
  }
  if (woord.indexOf('o') > -1) {
    klinker = 'o';
  }
  if (woord.indexOf('u') > -1) {
    klinker = 'u';
  }
  return klinker;
}

oplossing

Opgave 2:
Wat het moet doen: De functie krijgt een getal, die moet met vijf vermenigvuldigd worden, vervolgens moet er 5 bij opgesteld worden, en als laatste gedeeld door 2 en dan terug gegeven.
De functie:

var b = function(Getal) {
  //keer 5 
  var getal = Getal * 5
  //plus 5 
  Getal = getal + 5
  //gedeeld door 2 
  getal = getal / 2
  return getal;
}

oplossing

Opgave 3:
Wat het moet doen: De functie krijgt een zin en moet true terug geven als daar Orcado in voorkomt, en false als Orcado er niet in voorkomt.
De functie:

var c = function(zin) {
  if (zin.indexOf("orcado") >= 0)
    return true
  return false
}

oplossing

Opgave 4:
Wat het moet doen: De functie krijg een array en een element, en moet het element aan het einde van de array invoegen.
De functie:

var d = function(lijst, toevoeging) {
  lijst[lijst.length + 1] = toevoeging;
  return lijst;
}

oplossing

Opgave 5:
Wat moet het doen: De functie krijgt 2 getallen mee, de dag en de maand dat je jarig bent, en geeft terug over hoeveel dagen je jarig bent.
De functie:

var e = function(dag, maand) {
  //maar een nieuwe datum met de maand en dag en dit jaar 
  var datum = new Date(maand + '-' + dag + '-' + new Date().getFullYear());
  // trek daar de huidige datum vanaf en omdat je een resultaat krijg in milliseconden reken dat terug naar dagen 
  return Math.ceil((datum - new Date()) / 1000 / 60 / 60 / 24);
}

oplossing

Opgave 6:
Wat het moet doen: De functie krijgt een array met getallen, als een getal een 4 is moet je daar het volgende getal in de rij bij optellen.
De functie:

var f = function(lijst) {
  for (var i = 0; i < lijst.length; i++) {
    //als een waarde in de lijst 4 is, tel de volgende waarde uit de lijst er bij op 
    if (lijst[i] == 4) {
      lijst[i] += lijst[i + 1];
    }
  }
  return lijst
}

oplossing

Opgave 7:
Wat het moet doen: De functie krijgt een positief heel getal en moet daarvan aangeven of het een priemgetal is, true voor als het een priemgetal is, false als het geen priemgetal is.
De functie:

var g = function(a) {
  //ga alle getallen af vanaf 2 (alles is deelbaar door 1) 
  for (var i = 2; i <= a; i++) {
    //als het getal gedeeld door een ander getal geen rest waarde oplevert is het er door deelbaar en dus geen priemgetal 
    if (a % i == 0)
      return false
  }
  return true
}

oplossing

Opgave 8:
Wat het moet doen: De functie krijgt 2 getallen mee, het eerste is het basisgetal, het tweede is het getal tot waar we het gaan verheffen. Het tweede getal is altijd een geheel getal, en je mag alleen de basis 4 wiskundige bewerkingen doen (plus, min, keer, delen).
De functie:

var h = function(basis, verheffer) {
  //als de verheffer 0 is, dan is het altijd 1 
  if (verheffer == 0)
    return 1
  //werk volgens het idee 2^3 = 2*2^2 door de opgave heen tot de verheffer 0 is. 
  if (verheffer < 0)
    return basis * h(basis, verheffer + 1)
  else
    return basis * h(basis, verheffer - 1)
}

oplossing

Opgave 9:
Wat moet het doen: De functie krijgt een array sorteert deze in oplopende volgorde en geeft de gesorteerde array terug.
De functie:

var k = function(lijst) {
  //maak een lege lijst, hier komt de gesorteerde lijst in te staan 
  var gesorteerdelijst = [];
  //we houden een variabele bij om te controleren of we straks een hogere waarde hebben gevonden. 
  var gevonden = false;
  //voor elke waarde in de oorspronkelijke lijst 
  for (var i = 0; i < lijst.length; i++) {
    //blader door alle waardes in de gesorteerde rij tot je een waarde tegenkomt die groter is, zet daar de waarde uit de eerste lijst voor 
    for (var j = 0; j < gesorteerdelijst.length; j++) {
      if (lijst[i] < gesorteerdelijst[j]) {
        gevonden = true;
        gesorteerdelijst.splice(j, 0, lijst[i]);
        j = lijst.length; //deze regel is goed, haal deze bij het testen niet weg, dan blijf je de waarde namelijk eindeloos in de nieuwe array invoegen, dat vindt je browser niet leuk 
      }
    }
    //heb je geen grotere waarde gevonden, dan is dit de grootste waarde, zet die achteraan in de gesorteerde lijst. 
    if (gevonden == false) {
      gesorteerdelijst.splice(lijst.length, 0, lijst[i]);
    }
  }
  return gesorteerdelijst
}

oplossing

Opgave 10:
Wat het moet doen: De functie krijgt een zin en moet de individuele woorden omdraaien en daarna aan elkaar plakken, bijvoorbeeld: “dit is een test zin” moet worden: “tidsineetsetniz”.
De functie:

var l = function(draaiom) {
  return draaiom.split("").reverse().join(" ").split(" ").reverse().join("")
}

oplossing

Viel dat nou erg mee? Dan is er voor jou wel een baan in de IT weggelegd. Viel dat nou niet mee? Vrees niet, code lezen en begrijpen wat het doet is iets wat je heel veel moet doen. En dan nog steeds is het soms lastig om zonder de code te draaien meteen te zien wat het doet, of wat het zou moeten doen. Was je vooral benieuwd naar Roelofs bot? Ik zou graag zeggen dat die er binnenkort echt aankomt, maar dat geldt alleen als je de wet van Hofstadter in acht neemt.

Leren programmeren (duel)leren (deel 3)

Leren programmeren (duel)leren (deel 3)

Gelukkig heeft Daan in zijn vorige blog al gezegd dat ik niet ook zo’n heel verhaal aan elkaar hoef te ratelen, pfew. Het enige waar ik voor moet zorgen is dat je na afloop het woordgrapje uit de vorige blog begrijpt. En dat er tussendoor nog iets over een lerend algoritme is uitgelegd.

Om boter-kaas-en-eieren te leren maak ik gebruik van een algoritme dat heel kort door de bocht als volgt kan worden uitgelegd. Stel dat je geen idee hebt hoe boter-kaas-en-eieren werkt. In plaats van het je vriendelijk uit te leggen, gaan we gewoon spelen. Wel vertel ik je dat je alleen maar kruisjes mag zetten, op plekken waar nog geen kruisjes of rondjes staan. Na een tijdje laat ik je weten dat het spel is afgelopen en dat je hebt verloren. In theorie kan je natuurlijk ook hebben gelijk gespeeld of zelfs hebben gewonnen, maar hé, je kende de regels nog niet en ik wel, dus daar ga ik niet vanuit. Vervolgens spelen we het nog een keer. En dat nog heel erg vaak. In de loop van tijd krijg je door hoe je kan voorkomen dat je verliest, of misschien zelfs hoe je kan winnen.

Nu is het waarschijnlijk dat jij door zou krijgen dat er een relatie is tussen 3 rondjes of kruisjes op een rij, en winst of gelijkspel of verlies. Dat is niet hoe mijn AI het doet. Mijn AI maakt gebruikt van een algoritme met de naam Q-learning, en is een variant van reinforcement learning. Je zou nu het woordgrapje moeten snappen, dan kan ik me richten op het uitleggen van het lerende algoritme. Het idee achter reinforcement learning is dat je leert door middel van de feedback die wordt geleverd, in dit geval dus of je hebt verloren, gelijk gespeeld, of gewonnen. Deze bot kan je hier uitproberen – http://undercover.horse/tictacque/ !

Reinforcement learning is gebaseerd op het behaviourism, dit is een stroming in de psychologie over gedrag. Hierin wordt gekeken hoe gedrag ontstaat onder invloed van de omgeving. Dit is een goed voorbeeld van hoe ideeën uit de psychologie worden gebruikt bij kunstmatige intelligentie. Reinforcement learning doet het meest denken aan (operante) conditionering. Hierin wordt in een bepaalde context op een bepaald gedrag altijd een beloning of straf gegeven, op die manier leert een dier of mens wat wenselijk gedrag is.

Het voordeel van Q-learning is dat je van tevoren niet hoeft te weten welke situaties kunnen voorkomen, en dus ook de bijbehorende mogelijke acties niet kent. Om te leren, wordt er een lijst bijgehouden met alle situaties die zijn tegengekomen inclusief welke zet hierin is gedaan, en een getal dat aangeeft hoe goed de uitkomst was. Die laatste is de q-waarde. Met deze lijst kan je later in dezelfde situatie dus zetten vergelijken, en degene met de hoogste q-waarde kiezen. Maar aan het begin van het spel is deze lijst helemaal leeg. Je moet dus q-waarden gaan leren.

De q-waarde wordt na iedere beurt geüpdatet, op basis van wat de q-waarde op dat moment is, en de nieuwe informatie. Deze nieuwe informatie bestaat uit de feedback, dus hoe goed de zet was, en wat de toekomstige mogelijkheden zijn. Bij boter-kaas-en-eieren is het eerste moment van feedback officieel pas als het spel is afgelopen. Hier is winst +1 punt, verlies -1, en gelijkspel 0. Daarnaast geef ik ook 0 punten aan een spel dat nog niet is afgelopen. Hoe goed de toekomstige mogelijkheden zijn, wordt bepaald door de hoogste q-waarde van de situatie waar je in bent beland. Als je het spel dus voor de eerste keer speelt, weet je pas iets als het is afgelopen. De waardes bij alles tot het einde blijven dan 0, terwijl de laatste situatie + actie wordt geüpdatet aan de hand van de feedback. Op die manier leer je steeds een stukje verder terug wat wel en geen goede zetten zijn.

Een van de gevaren hiervan is dat je een suboptimale strategie leert. Daarom is het belangrijk om te onderzoeken of er nog meer mogelijkheden zijn. Eén van de andere gevaren, is dat je altijd blijft rondkijken voor betere mogelijkheden. In dat geval weet je wel hoe je het spel perfect moet spelen, maar maak je nooit gebruik van deze kennis. Om dit te voorkomen wordt een andere lijst bijgehouden, met hoe vaak een combinatie van situatie en actie zijn voorgekomen. Als je in een situatie komt, waarin je de volgende zet moet kiezen, wordt er voor alle mogelijke zetten die minder dan 3 keer zijn gedaan niet uitgegaan van een q-waarde, maar van een gezonde nieuwsgierigheid. Op het moment dat dan de q-waarde wordt opgevraagd, wordt een optimistische waarde teruggegeven, in plaats van wat de q-waarde op dat moment is (als die dan al bestaat).

Wat je bij voorkeurAI1 ziet, is dat mijn algoritme, die ik QlearningAI heb genoemd, aan het begin van het spel alles een keer probeert, maar na een tijdje constant dezelfde tactiek uitvoert. Dat ziet er als volgt uit:

Wat je ziet is dat de VoorkeurAI1 aan het begin nog de meeste spelletjes wint, maar vanaf 69 gewonnen spellen, wint QLearningAI alles.

Tegen VoorkeurAI2 gebeurt bijna hetzelfde, behalve dat VoorkeurAI2 een tactiek heeft om verlies tegen te gaan. De QLearningAgent wint daarom niet alles, maar het wordt constant gelijk spel, zoals getoond in de volgende plaatjes.

Mij vallen hier drie dingen in op. Ten eerste dat QLearningAI er tegen VoorkeurAI2 langer over doet om nooit meer te verliezen in vergelijking met VoorkeurAI1. Ik denk dat dit komt door het verschil in feedback tussen winst en gelijkspel, waardoor er sneller wordt geleerd als er wordt gewonnen. Ten tweede valt me op dat QLearningAI veel sneller dan GeneticAI2 van Daan leert tegen VoorkeurAI2. Als laatste, daarentegen, lukt het GeneticAI2 uiteindelijk wel om te winnen van VoorkeurAI2, terwijl QlearningAI blijft hangen in gelijkspel. Hopelijk is dit laatste geen voorbode voor de uitkomst als onze algoritmes het tegen elkaar opnemen…

Een van de grote verschillen tussen de voorkeurAI-algoritmen aan de ene kant, en lerende algoritmen zoals de GeneticAI2 en QLearningAI, is dat de voorkeurAI-algoritmen nooit van tactiek wisselen. Zodra de QlearningAI dus de beste strategie heeft bepaald tegen een VoorkeurAI, zal hij die altijd blijven uitvoeren. Echter, als een lerend algoritme, zoals GeneticAI2, merkt dat een andere strategie beter is, past hij zich aan. Als een resultaat hiervan, zullen de q-waarden van QLearningAI zich zo aanpassen dat ook deze van strategie veranderd. Zoals je begrijpt is mijn voorspelling dat we ons de hele tijd aan elkaar zullen aanpassen. We gaan het meemaken, alleen nog niet in de volgende blog, want dan mengt ook Roelof zich in de strijd. Roelof, je hebt nog één maand!

Hier komt de sidebar

Volg ons op

© Orcado B.V. | 1999 - 2019