functie stoppen wanneer array waardes overeenkomen
Ik heb een multidimensional array hierin zitten 12 arrays elk met 6 verschillende nummers. Nu wil ik deze array controleren met een variabele die willekeurige nummers genereer. Wanneer 6 nummers in 1 van de 12 arrays overeenkomen wil ik dat de functie stopt en de array opslaan zodat weer gegeven kan worden op het scherm.
Nu had ik al het een en ander gevonden op internet. Maar ik kom nu dus in een infinite loop terecht.
Iemand een idee hoe ik dit zou kunnen oplossen en wat ik fout heb gedaan?
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php
mt_srand((double)microtime() * 1000000);
//lege bingokaart maken
$bingokaart = [];
//functie bingokaart vullen
function kaart_maken(){
//rijen vullen met willekeurige nummers
for($rij=1; $rij<=6; $rij++){
$bingokaart[$rij] = []; //de rij initialiseren
for($num=1; $num<=6; $num++){
//nummer aanmaken, en meteen controleren of ie al in de rij zit
while(in_array($nummer = $rij . rand(0,9), $bingokaart[$rij]));
//voeg nummer toe aan array
$bingokaart[$rij][$num] = $nummer;
}
}
//bingokaart-array uitbreiden met horizontale en verticale rijen door de horrizontale rijen te
//kantelen
$new_bingokaart = array();
foreach($bingokaart as $key => $subarr) {
foreach ($subarr as $subkey => $subvalue) {
$new_bingokaart[$subkey][$key] = $subvalue;
}
}
//bingokaart nummers en tabel printen
echo "<table border='1px black solid'>";
for($i=1; $i<=6; $i++){
echo "<tr>";
for($x=1; $x<=6; $x++){
echo "<td>" . $bingokaart[$i][$x] . "</td>";
}
echo "</tr>";
}
echo "</table><br>";
return array_merge($bingokaart, $new_bingokaart);;
}
//bingokaart printen
$bingokaart = kaart_maken();
$bingo = false;
$getal = rand(10, 69);
$getrokken_getallen = array();
while(!$bingo){
while(in_array($getal, $getrokken_getallen));
$getrokken_getallen[] = $getal;
for($rij=1; $rij<=12; $rij++){
for($rij_nr=1; $rij_nr<=6; $rij_nr++){
if($bingokaart[$rij][$rij_nr] == $getal){
$bingokaart[$rij][0] += 1;
if($bingokaart[$rij][6] == 6){
print "Bingo";
$bingo = true;
}
}
}
}
}
?>
mt_srand((double)microtime() * 1000000);
//lege bingokaart maken
$bingokaart = [];
//functie bingokaart vullen
function kaart_maken(){
//rijen vullen met willekeurige nummers
for($rij=1; $rij<=6; $rij++){
$bingokaart[$rij] = []; //de rij initialiseren
for($num=1; $num<=6; $num++){
//nummer aanmaken, en meteen controleren of ie al in de rij zit
while(in_array($nummer = $rij . rand(0,9), $bingokaart[$rij]));
//voeg nummer toe aan array
$bingokaart[$rij][$num] = $nummer;
}
}
//bingokaart-array uitbreiden met horizontale en verticale rijen door de horrizontale rijen te
//kantelen
$new_bingokaart = array();
foreach($bingokaart as $key => $subarr) {
foreach ($subarr as $subkey => $subvalue) {
$new_bingokaart[$subkey][$key] = $subvalue;
}
}
//bingokaart nummers en tabel printen
echo "<table border='1px black solid'>";
for($i=1; $i<=6; $i++){
echo "<tr>";
for($x=1; $x<=6; $x++){
echo "<td>" . $bingokaart[$i][$x] . "</td>";
}
echo "</tr>";
}
echo "</table><br>";
return array_merge($bingokaart, $new_bingokaart);;
}
//bingokaart printen
$bingokaart = kaart_maken();
$bingo = false;
$getal = rand(10, 69);
$getrokken_getallen = array();
while(!$bingo){
while(in_array($getal, $getrokken_getallen));
$getrokken_getallen[] = $getal;
for($rij=1; $rij<=12; $rij++){
for($rij_nr=1; $rij_nr<=6; $rij_nr++){
if($bingokaart[$rij][$rij_nr] == $getal){
$bingokaart[$rij][0] += 1;
if($bingokaart[$rij][6] == 6){
print "Bingo";
$bingo = true;
}
}
}
}
}
?>
Alvast bedankt voor de hulp.
Groeten Mario
Ideaal als deze in een iteratie opeens aan een voorwaarde voldoet.
Ja, die had ik inderdaad ook toegevoegd. Maar helaas nog steeds een infiniti loop. Heb de break op verschillende rijen gezet.
Op welk punt krijg jij een infinite loop?
De bingokaart is ook enorm complex opgezet, waarom maak je deze tweedimensionaal? Dit heeft tot gevolg dat je elke keer moet gaan zoeken in een meerdimensionaal array. Een eerste ingeving zou zijn om dan hier maar een lineaire lijst van te maken (wat op zich een goed idee is) maar dan moet je geen in_array gaan gebruiken want dat is nog steeds redelijk inefficiënt. Omdat de nummers alle uniek zijn zou je deze op de keys kunnen zetten, en dan kun je met isset() of array_key_exists() een veel snellere lookup doen.
En dan zou je nog eens naar de "beëindiging" kunnen kijken, dus het geval waarin een complete rij of kolom getrokken is.
EDIT: en naar de spelvorm, want blijkbaar zijn er meerdere uitvoeringen van wat doorgaat als "bingo". Misschien is het handig als je even teruggaat naar het functioneel ontwerp, en je daar stap voor stap beschrijft hoe het spel werkt. Een implementatie wordt dan waarschijnlijk een stuk eenvoudiger, omdat precies is voorgeschreven wat er dient te gebeuren.
Gewijzigd op 23/07/2018 12:43:15 door Thomas van den Heuvel
@Ariën dat is voor ook mij vraag. Is er een goeie manier van debugen of een programmeerprogramma om te debugen. Ik maak nu gebruik van Brackets om te programmeren.
@Thomas qua spelvorm moet ik me aan de regels houden van de opdracht. Dus in dit geval een bingokaart 6x6 met de getallen 10=>20 20=>30 enz die horizontaal getoond moeten worden.
Betreft opzet had ik zelf ook al het idee dat het anders kan. Echter weet ikzelf niet echt hoe. Hier ben ik net iets te vers voor.
De reden waarom ik gekozen heb voor een meerdimensionaal array is omdat ik uiteindelijk de rij die bingo veroorzaakt een kleur moet geven. Dus heb ik een for loop gemaakt die het getal in de arrays controller, en wanneer deze 6x voorkomt in 1 van de arrays, $bingo true retour moet geven.
Het debuggen is in het simpelste niets meer dan de waardes tussentijd op je scherm outputten met echo, print_r en/of var_dump. Er zijn ook extentions voor webservers zoals Xdebug om je script meer te debuggen en te profilen.
Stel je nu de volgende situatie voor:
Stel dat alle x-en afgestreept zijn, en de "o" het volgende getal is, dan dien je dus zowel een rij als een kolom in te kleuren :).
Het volgende lijkt te werken:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<?php
mt_srand((double)microtime() * 1000000);
// note: $rows and $columns cannot exceed 10, unless you want to play hexadecimal bingo
function createCard($rows=6, $columns=6) {
$card = [];
for ($i=0; $i < $rows; $i++) {
for ($j=0; $j < $columns; $j++) {
while (array_key_exists($randomNumber = ($i+1) * 10 + rand(0, 9), $card));
$card[$randomNumber] = false;
}
}
dump(implode(', ', array_keys($card)));
return $card;
}
function updateCard(&$card, $number) {
if (isset($card[$number])) {
$card[$number] = true;
}
}
function checkCardForBingo($card, $rows=6, $columns=6) {
$return = false;
$cardPositions = array_keys($card); // index => number
// check rows
for ($i=0; $i < $rows; $i++) {
$count = 0;
for ($j=0; $j < $columns; $j++) {
$nr = $cardPositions[$i*$rows+$j];
$count = $count + $card[$nr]; // false = 0, true = 1
}
if ($count == $columns) { // full row
dump('row '.$i);
$return = true;
}
}
// check columns
for ($j=0; $j < $columns; $j++) {
$count = 0;
for ($i=0; $i < $rows; $i++) {
$nr = $cardPositions[$i*$rows+$j];
$count = $count + $card[$nr];
}
if ($count == $rows) { // full column
dump('column '.$j);
$return = true;
}
}
return $return;
}
function drawNumber(&$drawnNumbers) {
while (array_key_exists($number = rand(10, 69), $drawnNumbers));
$drawnNumbers[$number] = true;
dump('drawing '.$number);
return $number;
}
// debugging
define('DEBUG_OUTPUT', true);
function escape($s) {
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}
function dump($in) {
if (DEBUG_OUTPUT === false) {
return;
}
if (is_array($in)) {
echo '<pre>'.escape(print_r($in, true)).'</pre>';
} else {
echo escape($in).'<br>';
}
}
$card = createCard();
$drawnNumbers = [];
$bingo = false;
while ($bingo === false) {
$number = drawNumber($drawnNumbers);
updateCard($card, $number);
$bingo = checkCardForBingo($card);
}
?>[end]
mt_srand((double)microtime() * 1000000);
// note: $rows and $columns cannot exceed 10, unless you want to play hexadecimal bingo
function createCard($rows=6, $columns=6) {
$card = [];
for ($i=0; $i < $rows; $i++) {
for ($j=0; $j < $columns; $j++) {
while (array_key_exists($randomNumber = ($i+1) * 10 + rand(0, 9), $card));
$card[$randomNumber] = false;
}
}
dump(implode(', ', array_keys($card)));
return $card;
}
function updateCard(&$card, $number) {
if (isset($card[$number])) {
$card[$number] = true;
}
}
function checkCardForBingo($card, $rows=6, $columns=6) {
$return = false;
$cardPositions = array_keys($card); // index => number
// check rows
for ($i=0; $i < $rows; $i++) {
$count = 0;
for ($j=0; $j < $columns; $j++) {
$nr = $cardPositions[$i*$rows+$j];
$count = $count + $card[$nr]; // false = 0, true = 1
}
if ($count == $columns) { // full row
dump('row '.$i);
$return = true;
}
}
// check columns
for ($j=0; $j < $columns; $j++) {
$count = 0;
for ($i=0; $i < $rows; $i++) {
$nr = $cardPositions[$i*$rows+$j];
$count = $count + $card[$nr];
}
if ($count == $rows) { // full column
dump('column '.$j);
$return = true;
}
}
return $return;
}
function drawNumber(&$drawnNumbers) {
while (array_key_exists($number = rand(10, 69), $drawnNumbers));
$drawnNumbers[$number] = true;
dump('drawing '.$number);
return $number;
}
// debugging
define('DEBUG_OUTPUT', true);
function escape($s) {
return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}
function dump($in) {
if (DEBUG_OUTPUT === false) {
return;
}
if (is_array($in)) {
echo '<pre>'.escape(print_r($in, true)).'</pre>';
} else {
echo escape($in).'<br>';
}
}
$card = createCard();
$drawnNumbers = [];
$bingo = false;
while ($bingo === false) {
$number = drawNumber($drawnNumbers);
updateCard($card, $number);
$bingo = checkCardForBingo($card);
}
?>[end]
Merk op dat als je e.e.a. vangt in functies dat het daadwerkelijke "programma" ook vrij kort is en (zonder de bijbehorende implementatie) al redelijk goed gevolgd kan worden, alsof het een aantal lopende zinnen zijn:
Groeten Mario
Normaal weet een functie niet van het bestaan van variabelen die buiten de functie vallen -enkele speciale gevallen zoals superglobals daargelaten-, de functie heeft normaal alleen te maken met parameters die aan de functie worden meegegeven of de variabelen die binnen de functie worden gedefiniëerd. Dit is dan ook meteen het gebied waarin de variabelen bekend (of "geldig") zijn, m.a.w. dit is de scope (het geldigheidsgebied).
Bij de parameters die worden meegegeven is normaal alleen de waarde(n) die ze meesturen relevant, als een parameter intern van waarde verandert gebeurt er verder niets met de waarde van de externe variabele:
Code (php)
Dit heet "call by value".
Er zijn echter (ten minste?) twee uitzonderingen hier op: in de eerste plaats kun je aangeven dat een (simpele) variabele (string, boolean, array, integer etc.) wél behandeld moet worden alsof deze rechtstreeks aangesproken werd. Dit heet "call by reference". Wijzigingen in de verwijzing hebben dezelfde impact op het origineel. Een variabele kun je call by reference meegeven aan een functie door er een ampersand (&) voor te zetten:
Code (php)
Om variabelen uit de globale scope (buiten de functies) te gebruiken binnen een functie kun je ook het keyword global gebruiken, maar dat kan nogal verwarrend werken, ook kunnen er dan potentieel botsingen in naamgevingen gaan ontstaan. Persoonlijk zou ik het global keyword niet gebruiken, vaak -als je dit nodig lijkt te hebben- zijn er andere (en waarschijnlijk betere) oplossingen mogelijk. Het gebruik van global is zoiets als ergens een tussenwand uitslopen, en die tussenwand zat er meestal om een reden in. De enige hoop die je kunt hebben is dat die tussenwand niet dragend was :p.
En dan zijn er nog objecten. Objecten zijn (tegenwoordig, want volgens mij is dat in het verleden niet altijd zo geweest) standaard call by reference.
Code (php)
Je had uiteraard ook alles kunnen doen zonder dit call by reference geneuzel door het resultaat te retourneren en toe te kennen aan de variabele die je als parameter meegaf.
Het wordt natuurlijk (nog) interessant(er) als je de bingokaarten en trekkingen organiseert in objecten die interactie met elkaar hebben in een soort van gesimuleerd spel.
NB: als je vaak call by reference gebruikt (doorgaans in een procedurele setting) dan houdt dit in dat er sprake is van een soort van "variabelentoestand" (ook wel state genoemd) die bijgehouden en (meerdere toestandsveranderingen overbruggend) onthouden moet worden. Dit kan ook een indicatie zijn dat een overstap naar een object georiënteerde aanpak handig kan zijn, omdat objecten (van classes) bij uitstek geschikt zijn om een toestand vast te houden.
Gewijzigd op 29/07/2018 15:46:58 door Thomas van den Heuvel
Het heeft even geduurd maar goed. Ik kan hier zeker wat mee. Bedankt voor de goede uitleg zeker betreft ampersand. Ikzelf had inderdaad alles functie uitslagen in een variabele laten opslaan. Maar ik ben wel benieuwt dat wanneer ik de ampersand ga gebruiken, dit invloed kan hebben op mijn variabele wanneer ik deze verder gaat gebruiken/wijzigen in latere code, of houd deze variabele de aangepaste waarde?
Variabelen declareren in functies was trouwens ook nieuw voor mij.
Ik heb zelf nog wat vragen over je code die je geschreven had voornamelijk voor mijzelf om te zien of ik het begrijp. Ik heb de vraag in commentaar bij de regel gezet.
Vraag1:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
<?php
function createCard($rows=6, $columns=6) {
$card = [];
for ($i=0; $i < $rows; $i++) {
for ($j=0; $j < $columns; $j++) {
while (array_key_exists($randomNumber = ($i+1) * 10 + rand(0, 9), $card));//klopt het dat hier gegeken word of het randomnummer voorkomt?
$card[$randomNumber] = false; //en hier wanneer het false is, de loop opnieuw uitvoeren net zolang todat de kaart gevuld is?
}
}
dump(implode(', ', array_keys($card)));
return $card;
}
?>
function createCard($rows=6, $columns=6) {
$card = [];
for ($i=0; $i < $rows; $i++) {
for ($j=0; $j < $columns; $j++) {
while (array_key_exists($randomNumber = ($i+1) * 10 + rand(0, 9), $card));//klopt het dat hier gegeken word of het randomnummer voorkomt?
$card[$randomNumber] = false; //en hier wanneer het false is, de loop opnieuw uitvoeren net zolang todat de kaart gevuld is?
}
}
dump(implode(', ', array_keys($card)));
return $card;
}
?>
Vraag 2:
Code (php)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
function checkCardForBingo($card, $rows=6, $columns=6) {
$return = false;
$cardPositions = array_keys($card); // index => number
// check rows
for ($i=0; $i < $rows; $i++) {
$count = 0;
for ($j=0; $j < $columns; $j++) {
$nr = $cardPositions[$i*$rows+$j];//waarom word hier vermenigvuldigd en opgeteld?
$count = $count + $card[$nr]; // false = 0, true = 1
}
if ($count == $columns) { // full row
dump('row '.$i);
$return = true;
}
}
?>
function checkCardForBingo($card, $rows=6, $columns=6) {
$return = false;
$cardPositions = array_keys($card); // index => number
// check rows
for ($i=0; $i < $rows; $i++) {
$count = 0;
for ($j=0; $j < $columns; $j++) {
$nr = $cardPositions[$i*$rows+$j];//waarom word hier vermenigvuldigd en opgeteld?
$count = $count + $card[$nr]; // false = 0, true = 1
}
if ($count == $columns) { // full row
dump('row '.$i);
$return = true;
}
}
?>
Vraag 3:
Code (php)
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
<?php
function drawNumber(&$drawnNumbers) { //Klopt het dat door hier gebruik te maken van een ampersand dat de array gevuld word met random nummers?
while (array_key_exists($number = rand(10, 69), $drawnNumbers));
$drawnNumbers[$number] = true;
dump('drawing '.$number);
return $number;
}
?>
function drawNumber(&$drawnNumbers) { //Klopt het dat door hier gebruik te maken van een ampersand dat de array gevuld word met random nummers?
while (array_key_exists($number = rand(10, 69), $drawnNumbers));
$drawnNumbers[$number] = true;
dump('drawing '.$number);
return $number;
}
?>
Groeten Mario
Ja, zolang $randomNumber al voorkomt op de kaart zoeken we naar een nieuwe kandidaat, net zolang totdat we er een tegenkomen die er nog niet op staat. Deze voegen we dan toe en herhalen het proces totdat de hele kaart voorzien is van unieke nummers.
//en hier wanneer het false is, de loop opnieuw uitvoeren net zolang todat de kaart gevuld is?
Nee, dat is geen vergelijking, dat is een toekenning van een nog niet bestaand nummer ($randomNumber) aan de kaart. Deze sla ik op in de key, de value is false, die aangeeft dat het nummer nog niet afgestreept is. Als je een nieuwe kaart hebt/bouwt, is nog geen enkel nummer afgestreept. Ik sla $randomNumber op in de key zodat je dan heel snel (en efficiënt) dit nummer kunt inspecteren (en kunt afstrepen), en niet hoeft te gaan zoeken in $card met een in_array() operaties ofzo.
//waarom word hier vermenigvuldigd en opgeteld?
Ik maak er geen meerdimensionaal array van voor rijen en kolommen, maar zet de rijen achter elkaar. Elke kolom heeft dan een nieuwe "offset", namelijk de huidige rij, vermenigvuldigd met ... dat moet eigenlijk * $columns zijn denk ik, in plaats van $rows, waarschijnlijk :). Elke rij is namelijk $columns elementen lang.
//Klopt het dat door hier gebruik te maken van een ampersand dat de array gevuld word met random nummers?
$drawnNumbers (in de functie) is een parameter, deze representeert de variabele $drawnNumbers uit de globale scope (staat gedeclareerd op regel 79).
Het doel van deze parameter/variabele is dat je bijhoudt welke nummers al getrokken zijn. Bij het trekken van een nieuw nummer ga je ook hier weer net zolang door totdat je een nieuw nummber vindt. Deze wordt enerzijds toegevoegd aan de parameter in de functie, en daarmee aan de variabele $drawnNumbers uit de globable scope, en anderzijds geretourneerd via return.
Toegegeven, dat deze dezelfde naam hebben is wellicht verwarrend, maar doordat je de parameter aanroept met een ampersand wordt de variabele uit de globale scope (regel 79) dus direct aangesproken en kan deze dan ook rechtstreeks inhoudelijk worden aangepast. Normaal bestaan variabelen die je in een functie declareert niet buiten de functie, maar met een ampersand (of het keyword global, wat waarschijnlijk af te raden is) kun je een brug slaan tussen variabelen binnen de functie, en daarbuiten.
Het zou misschien zelfs nog handiger zijn als je hier objecten en klassen van maakt, want procedures (functies binnen een klasse) kunnen sowieso bij alle (eigen) klasse-variabelen.
EDIT: als ik zo in de gauwigheid kijk moeten er in de oorspronkelijke code misschien wat dingetjes worden aangepast:
- regel 30 $i*$rows moet $i*columns zijn
- ... hmm, dat wat het?
Het bovenstaande vormt trouwens geen probleem zolang de afmetingen van de kaart (rijen, kolommen) hetzelfde zijn.
Gewijzigd op 10/08/2018 14:02:40 door Thomas van den Heuvel
Groeten Mario