Kunstmatige Intelligentie 2008 — opdracht 3

Neurale netwerken

Rick van der Zwet

                 <hvdzwet@liacs.nl>

LIACS Leiden University Niels Bohrweg 1 2333 CA Leiden The Netherlands

9 april 2008

1 Inleiding

Dit verslag gaat over de derde programmeer-opgave van het vak kunstmatige intelligentie [1]. Welke als opdracht heeft om een neuraal netwerk te bouwen, wat met één verborgen laag Poker handen kan classificeren. Dit alle met met als doel de bouw en werking van neurale netwerken (NN ) uit het college boek [3] beter te kunnen begrijpen.

2 Uitleg probleem

Om het probleem een visueel gestalte te geven is gekozen om een pokerhand te bepalen. Als invoer wordt er 5 kaarten uitgegeven, waarvan de waarde en kleur apart gecodeerd zijn in respectievelijk 13 en 4 mogelijke waardes.. Met deze kaarten kan er een van de 10 combinaties van een pokerhand gevormd worden. Als er 5 willekeurige kaarten aangeboden worden is het de taak van het NN daar de juiste combinatie bij te zoeken. Er wordt geen informatie gegeven wat de regels zijn om de combinaties te maken, maar er worden wel voorbeelden gegeven van valide combinaties. Nadat alle voorbeelden gezien zijn worden er willekeurige combinaties kaarten aangeboden, de taak is om deze set zo goed mogelijk te clasifiseren.

Figuur 1: Voorbeeld van een 1-laags neuraal netwerk met 2 invoer nodes, 3 verbonden perceptronen, 2 uitvoer nodes.

3 Theorie

Voor de menselijke beleving is het relatief makkelijk om in één opzicht te zien welke pokerhand er in hand is of om dit aan te leren, door voorbeelden aan te bieden, waarbij dan logische regels worden afgeleid. Om in een algoritme hetzelfde lerende gedrag te simuleren/creëren zijn NN uitgevonden in verschillende smaken en stijlen. Dit verslag focust zich in feed-forward netwerken welke als voordeel hebben dat ze een van de simpelste neurale netwerken zijn, waarbij de informatie maar een richting op gaat.

Bij een NN kan uit 3 of meerdere lagen onderscheiden worden. Als eerste is er de invoer laag met zijn invoer nodes, waar de gecodeerde invoer op komt te staa. Bij gecodeerd wordt een waarde tussen 0 en 1 bedoeld de reden hiervan komt later aan de orde. De verborgen laag kan uit meerdere lagen bestaan, maar tijdens dit verslag zal focust worden op één laag. In deze verborgen laag zitten de perceptronen, welke de intelligentie zijn van het NN. In de uitvoer laag zitten de uitvoer nodes, welke hun waarde weer tussen 0 en 1 bevinden. Tussen alle nodes in de invoer laag en de uitvoer laag zitten takken, zo ook tussen de verborgen laag en de uitvoer laag. Zie ook figuur 1.

Een perceptronen heeft de eigenschap dat het een activatie functie bezit en een drempel waarde. Stel dit voor als een persoon die je slaat, als iemand dit zachtjes doet zal je niets zeggen, als dit een stuk harder is zal je ’auwch’ roepen. De kracht die het nodig om je auwch te laten roepen is de drempelwaarde. Van binnen voelde dat dit pijn deed naar mate je harder werd geslagen ging je meer pijn voelen. Dit kan je het beste vatten in een lineaire functie. Vanwege het feit dat een NN een continue functie is ligt het voor de hand om een logaritmische functie te krijgen, waarbij de activatie functie de vorm krijgt van formule 1. Dit verklaart tevens waarom de invoer en uitvoer tussen 0 en 1 moeten liggen, gezien dit ook het uitvoer bereik is.

uit = \frac{1}{1 + e^{in}}  (1)

De in in formule 1 is een gewogen invoer van alle inkomendetakken ∗ hetgewichtvaneentak. De drempel waarde is echter een vervelend feit, welke makkelijker op te lossen is met een zogenoemde bias knoop, de waarde op deze knoop is altijd −1, waardoor deze samen met het gewicht was tussen deze bias knoop en de knoop hangt zorgt voor de drempelwaarde die origineel gebruikt was nu de drempelwaarde 0 gebruikt kan worden.

De gewichten zijn het ‘magische’ van het NN, deze gewichten kunnen namelijk getraind worden waardoor de uitvoer van het neurale netwerk kan veranderen. Deze training word backpropagation genoemd en heeft een correlatie met de fout in de uitvoer, het huidige gewicht en de leer-snelheid en de invoer. Om deze fout in een koop te bepalen moet er gekeken worden naar de fout in de uitvoer en het gewicht naar tak van die knoop, zie formule 2. Waarbij △_{uit} gelijk is aan △_{j} = g′(in_{j}) ∑_{i} W_{j,i}△i Waarbij i all opvolgende knopen van j zijn.

W_{in,uit} = α ∗ a_{in} ∗ △_{uit}  (2)

De fouten worden dus omgekeerd berekend, eerst de fouten van de uitvoer laag, dan de verborgen lagen ervoor en als die zijn die van de invoer laag.

4 Aanpak

Er is gekozen gebruik te maken voorbeeld implementatie raamwerk gegeven in de sheet [2] tijdens het college kunstmatige intelligentie welke een pseudo beschrijving geeft voor het programmeren van een neuraal netwerk.

herhaal
    voor elke E in trainings set doe
        voorzie de invoer knopen
        bereken de waardes in de perceptronen en uitvoer-knopen
        bereken de delta fouten van de knopen en perceptronen
        pas de takken aan
totdat netwerk "geconvergeerd"

Als eerste stap is simpele Perl code gebruikt om van de training en validatie sets een in- en uitvoer data voor het NN te schrijven waarbij alle waarden tussen 0 en 1 liggen. Perl code is een stuk flexibeler als het gaat om tekst verwerking dan C code.

Om ook eens met een kritische noot naar de invoer te kijken en de daadwerkelijke intelligentie van een NN, zal er extra knopen toegevoegd worden om te kijken hoe deze presteren. Voor de poker set zijn dit een paar specifieke combinaties namelijk de setjes -doubles, triples, four- en de aantallen kaarten van elke soort kleur.

Nadat het netwerk geschreven is kan deze getest worden met een het leren van de zogenoemde XOR functie. Deze functie is niet lineair te scheiden, maar is met een NN van 2 invoer, 3 verborgen, 1 uitvoer perfect te berekenen/leren.

5 Implementatie

Het NN is geschreven in C, hevig hangend op globale array waar de data in zit en losse functies om de programma flow duidelijker te maken. De pre-parse is geschreven in Perl en Bourne shell code.

6 Experimenten

Alle testen zijn uitgevoerd met een computer, waarbij gcc 4.01 als compiler aanwezig was en Perl 5.8.8 als parser . Bij wijziging van de input knopen of uitvoer knopen, is de pre-parse aangeroepen en de code opnieuw gecompileerd met de nieuwe waardes.

Tijdens alle simulaties het de trainingsset een grootte van 25000. De eind validatie set -welke de eind kwaliteit van het NN bepaald- 1 miljoen. En de tussentijdse kwaliteit set heeft een grootte van 1000, welke na elke 100 training invoeren draaien.

Tijdens de leersnelheid experiment is gebruik gemaakt van het de standaard poker set, met 10 invoeren, 10 uitvoeren en 10 verborgen perceptronen. De resultaten zijn te zien in figuur 2. De verborgen perceptronen test is gedaan om dezelfde data set, hierbij is de leer-snelheid 0.5 gekozen, deze resultaten staan in figuur 3.

Figuur 2: NN training verloop bij variabele leersnelheden Figuur 3: NN training verloop bij variabele verborgen invoer laag grootte

Tabel 1: Prestaties NN bij verschillende invoer data types

Invoer variatie Maximale kwaliteit in % Voorbeelden nodig
1  49.92  400
2  49.82  600
3  92.26  1800
4  92.26  6800
5  92.26  600

Tijdens de invoer variatie is gekozen voor vijf verschillende combinaties, de randvoorwaarden zijn 10 uitvoer knopen, aantal verborgen knopen 20, leersnelheid 0.5 De standaard set hierna afgekort met STD (1). STD met kleur tellingen (2), STD met paar tellingen (3), STD met kleur en paar tellingen (4), de paar tellingen + kleur tellingen (5) . Resultaten staan in tabel 1.

7 Conclusie

Er is gekeken naar verschillende aspecten van het neurale netwerk. Heeft toevoegen van nieuwe knopen met extra informatie invloed op de uitvoer? Heeft de leer-snelheid een positief effect op het NN? Heeft het aantal verborgen perceptronen invloed op het netwerk? Dit alle gecombineerd tot het originele probleem: Is het mogelijk goed poker-handen te voorspellen aan te hand van een 1-laags NN.

Het variëren van leer-snelheid levert geen beter kwaliteit NN op, als de leer-snelheid echter tussen 0 en 1 ligt zal het verloop minder grillig zijn en zal het NN niet ‘te ver’ doorschieten in een bepaalde richting.

Het variëren van het aantal verborgen knopen levert een maximum op als het aantal verborgen knopen groter of gelijk is aan het aantal invoer knopen. In het kader van de efficient is het aan te raden, deze rond de waarde van de invoer knopen te houden om zo het aantal berekeningen dat uitgevoerd moet worden zo minimaal mogelijk te houden.

Het grillige verloop van de grafiek in figuur 2 als ook in figuur 3 is een gedag wat niet verklaard kan worden. Verder onderzoek met bijvoorbeeld een andere data-set of de data in een andere volgorde aan te bieden zou hier misschien uitsluitsel voor kunnen bieden.

Het variëren van de invoer heeft groot effect op te effectiviteit van het netwerk. Bij het kiezen van de juiste data verdubbeld het netwerk zijn effectiviteit. Dit kan verklaard worden doordat op het moment de paren van te voren bepaald worden deze intelligentie niet meer door het systeem ontdekt hoeft te worden. Het probleem wordt dus eigenlijk versimpeld. Wel is het noodzakelijk relevante versimpelingen te maken. Het tellen van het aantal kaarten van een bepaalde kleur heeft duidelijk geen enkel effect in het netwerk, wat logisch is als we dichter naar de poker kaarten gaan kijken. Hiervoor is het enkel interessant om te weten of er 5 dezelfde kleuren in zitten of niet. Elke andere tussenvorm levert geen extra informatie op.

Voor een vervolg verslag zou gekeken kunnen worden of een meer laags NN een beter resultaat boekt bij het ontdekken van de poker kaarten. Verder zouden er ook ’debug’ sets ontwikkeld kunnen worden waarbij een willekeurig NN netwerk getest kan worden op juiste werking, gegeven alle randvoorwaarden. Deze optie wordt momenteel enkel voor een XOR voorbeeld gegeven welke enkel maar een validatie set is van een zeer beperkt probleem. Het is namelijk niet aan te tonen of het netwerk fout geimplementeerd is.

Referenties

[1] W.A. Kosters, Kunstmatige intelligentie Programmeer-opgave 3 van

    2008 – Neurale netwerken, http://www.liacs.nl/^{∼}kosters/AI/nn08.
    html

[2] W.A. Kosters, Kunstmatige intelligentie College neurale netwerken 2008,

    http://www.liacs.nl/^{∼}kosters/AI/neuraal.pdf

[3] S.J. Russell en P. Norvig, Artificial Intelligence, A Modern Approach, Second ediion, Prentice Hall, 2003.

[4] Asuncion, A. & Newman, D.J. (2007). UCI Machine Learning Reposi-

    tory http://www.ics.uci.edu/^{∼}mlearn/MLRepository.html. Irvine,

CA: University of California, School of Information and Computer Science.

[5] Robert Cattral (cattral@gmail.com) and Franz Oppacher (oppacher@scs.carleton.ca) Carleton University, Department of Computer Science, Intelligent Systems Research Unit http://archive.ics.uci.

    edu/ml/datasets/Poker+Hand

Appendix

De NN code en pre-parse.pl code zagen er als volgt uit:

001: /*

002: * Rick van der Zwet 003: * 0433373 004: * OS Assigment 3 005: * Licence: BSD 006: * $Id: nn.c 555 2008-04-07 21:59:55Z rick $

007: */
008:
009: #include <sysexits.h>
010: #include <stdio.h>
011: #include <stdlib.h>
012: #include <math.h>
013: #include <time.h>
014:

015: /* NOTE: All first knobs are bias knobs or hidden stale knobs 016: * - Validation is done using rounding, please make outputs discrete or 017: * alter validation function

018: */
019:

020: /* Allow uniform and easy calls at functions */ 021: #define TRUE 1 022: #define FALSE 0

023:
024:

025: /* Network variables */ 026: /*NOTE: first node is ’hidden’ bias knob */ 027: #ifndef INPUT SIZE 028: #define INPUT SIZE 11 029: #endif

030:

031: /*NOTE: first node is ’hidden’ bias knob */ 032: #ifndef HIDDEN SIZE 033: #define HIDDEN SIZE 11 034: #endif

035:

036: /*NOTE: first node is ’hidden’ ’lame’ knob */ 037: #ifndef OUTPUT SIZE 038: #define OUTPUT SIZE 11 039: #endif

040:

041: /* Learn speed alpha of network */ 042: #ifndef LEARN SPEED 043: #define LEARN SPEED 0.5 044: #endif

045:

046: /* After QUALITY ROUND trainingset check quality of network */ 047: #define QUALITY ROUND 100

048:

049: /* Training set, to be used to train network */

050: char * file training = "data/training.txt";

051: /* Validation set, to be used to test end result of network */

052: char * file validate = "data/validate.txt";

053: /* Quality set, to be used to do quick testing whether network is 054: * improving

055: */
056: char * file quality = "data/quality.txt";
057:

058: /* Globally defined arrays, which represent the network */ 059: double hidden[HIDDEN SIZE]; 060: double input[INPUT SIZE]; 061: double output[OUTPUT SIZE]; 062: double target[OUTPUT SIZE]; 063: double weight HtoO[HIDDEN SIZE][OUTPUT SIZE]; 064: double weight ItoH[INPUT SIZE][HIDDEN SIZE];

065:

066: #define WEIGHT NOT USED -99999

067:

068: void stdInit() { 069: int i; 070: /* Should never change, been using */ 071: for (i = 0; i < INPUT SIZE; i++) 072: weight ItoH[i][0] = WEIGHT NOT USED; 073: for (i = 0; i < HIDDEN SIZE; i++) 074: weight HtoO[i][0] = WEIGHT NOT USED;

075: }
076:
077:

078: /* Random init of weights */ 079: void randInit() { 080: int i,j;

081:

082: /* Different numbers every call */ 083: srandom(time(NULL));

084:

085: for (i = 0; i < INPUT SIZE; i++) 086: for ( j = 1; j < HIDDEN SIZE; j++) { 087: weight ItoH[i][j] = (double)(random() % 100) / 100;

088:  }
089:

090: for (i = 0; i < HIDDEN SIZE; i++) 091: for (j = 1; j < OUTPUT SIZE; j++) 092: weight HtoO[i][j] = (double)(random() % 100) / 100;

093:

094: stdInit();

095: }
096:

097: /* Fixed init of weights */ 098: void fixedInit() { 099: int i,j; 100: for (i = 0; i < INPUT SIZE; i++) 101: for ( j = 1; j < HIDDEN SIZE; j++) { 102: weight ItoH[i][j] = 0.5;

103:  }
104:

105: for (i = 0; i < HIDDEN SIZE; i++) 106: for (j = 1; j < OUTPUT SIZE; j++) 107: weight HtoO[i][j] = 0.5;

108:

109: stdInit();

110: }
111:

112: /* Define exact wights, used for debugging calculations 113: * other flags INPUT = 2, HIDDEN = 2, OUTPUT = 1

114: */

115: void debugInit() { 116: stdInit(); 117: weight ItoH[0][1] = 1; 118: weight ItoH[0][2] = 1; 119: weight ItoH[1][1] = 0.62; 120: weight ItoH[1][2] = 0.42; 121: weight ItoH[2][1] = 0.55; 122: weight ItoH[2][2] = -0.17;

123:

124: weight HtoO[0][1] = 1; 125: weight HtoO[1][1] = 0.35; 126: weight HtoO[2][1] = 0.81;

127: }
128:

129: /* calculate Aj’s and Ai’s (outputs) */ 130: void nnCalc() { 131: int i,j; 132: double total; 133: for (i = 1; i < HIDDEN SIZE; i++) { 134: total = 0; 135: for (j = 0; j < INPUT SIZE; j++) 136: total += weight ItoH[j][i] * input[j]; 137: hidden[i] = 1 / ( 1 + exp(total * (-1)));

138:  }
139:

140: for (i = 1; i < OUTPUT SIZE; i++) { 141: total = 0; 142: for (j = 0; j < HIDDEN SIZE; j++) 143: total += weight HtoO[j][i] * hidden[j]; 144: output[i] = 1 / ( 1 + exp(total * (-1)));

145:  }
146:
147: }
148:

149: /* train network, NOTE: nnCalc needs to be called first */ 150: void nnTrain() { 151: int i,j; 152: double hidden delta[HIDDEN SIZE]; 153: double output delta[OUTPUT SIZE]; 154: double output error[OUTPUT SIZE]; 155: double hidden sum delta[HIDDEN SIZE];

156:

157: for (i = 1; i < OUTPUT SIZE; i++) { 158: output error[i] = target[i] - output[i]; 159: output delta[i] = output error[i] * output[i] * (1 - output[i]);

160:  }
161:

162: for (i = 0; i < HIDDEN SIZE; i++) { 163: hidden sum delta[i] = 0; 164: for (j = 1; j < OUTPUT SIZE; j++) 165: hidden sum delta[i] += weight HtoO[i][j] * output delta[j]; 166: hidden delta[i] = hidden[i] * (1 - hidden[i]) * 167: hidden sum delta[i];

168:  }
169:

170: for (i = 0; i < HIDDEN SIZE; i++) 171: for (j = 1; j < OUTPUT SIZE; j++) { 172: weight HtoO[i][j] = weight HtoO[i][j] + LEARN SPEED * 173: hidden[i] * output delta[j];

174:  }
175:

176: for (i = 0; i < INPUT SIZE; i++) 177: for (j = 1; j < HIDDEN SIZE; j++) { 178: weight ItoH[i][j] = weight ItoH[i][j] + LEARN SPEED * 179: input[i] * hidden delta[j];

180:  }
181: }
182:

183: /* Verify wether target, matches output */ 184: int nnValidate() { 185: int i; 186: //printf (”Rounding: %lf - %lf\n”,output[1], target[1]); 187: for (i = 1; i < OUTPUT SIZE; i++) 188: if (round(output[i]) != round(target[i])) 189: return FALSE; 190: return TRUE;

191: }
192:

193: /* Pretty print of output */ 194: void nnOutput() { 195: int i; 196: for(i = 0; i < INPUT SIZE; i++) 197: printf("%lf, ", input[i]); 198: printf("= %lf - %lf - ", output[1], target[1]); 199: if (nnValidate() == TRUE) 200: printf("OK");

201:  else
202:  printf("ERROR");

203: printf("\n");

204: }
205:

206: /* Pretty print of hidden knobs */ 207: void nnHiddenOutput() { 208: int i; 209: for(i = 0; i < HIDDEN SIZE; i++) 210: printf("%lf, ", hidden[i]);

211:  printf(" - HIDDEN\n");
212: }
213:
214:

215: /* Pretty print of all weights */ 216: void nnNeuronOutput() { 217: int i,j; 218: for (i = 0; i < INPUT SIZE; i++) 219: for(j = 0; j < HIDDEN SIZE; j++) 220: if (weight ItoH[i][j] != WEIGHT NOT USED)

221:  printf("weight ItoH[%i][%i] = %lf\n", i, j,

222: weight ItoH[i][j]);

223:  printf("---\n");

224: for (i = 0; i < HIDDEN SIZE; i++) 225: for(j = 0; j < OUTPUT SIZE; j++) 226: if (weight ItoH[i][j] != WEIGHT NOT USED)

227:  printf("weight HtoO[%i][%i] = %lf\n", i, j,

228: weight HtoO[i][j]);

229: }
230:

231: int nnReadInput(FILE * handle) { 232: int i = 1; 233: double finput; 234: while (fscanf(handle, "%lf", &finput) != EOF) { 235: if (i < INPUT SIZE) 236: input[i] = finput; 237: else if (i < (INPUT SIZE + OUTPUT SIZE)) 238: target[i - INPUT SIZE] = finput;

239:

240: /* Calc next input */

241:  i++;

242: /* Skip hidden output knob */ 243: if (i == INPUT SIZE)

244:  i++;

245: if (i == (INPUT SIZE + OUTPUT SIZE)) 246: return TRUE;

247:  }
248:

249: /* Input not complete */ 250: return FALSE;

251: }
252:

253: /* Verify quality of current network */ 254: double nnQualityCheck(char * file) { 255: double validate total = 0; 256: double validate ok = 0; 257: double validate percent = 0; 258: FILE * handle;

259:

260: handle = fopen(file,"r"); 261: while (nnReadInput(handle) == TRUE) { 262: validate total++; 263: nnCalc(); 264: if (nnValidate() == TRUE) 265: validate ok++; 266: //else 267: // nnOutput();

                                                                                                    268:  }

269: fclose(handle);

270: validate percent = (validate ok / validate total) * 100;

                                                                                                    271:  printf("Validating: %.0lf/%.0lf - %.2lf %%\n",

272: validate ok,validate total,validate percent);

                                                                                                    273:

274: return(validate percent);

                                                                                                    275: }
                                                                                                    276:

277: /* Main program */

278: int main (int argc, char * argv[]) { 279: int i,training total, training best; 280: double quality max, quality; 281: FILE * handle;

                                                                                                    282:

283: /* Set the bias knob */

284: input[0] = -1; 285: hidden[0] = -1;

                                                                                                    286:

287: /* Init set of all wights */

288: //debugInit(); 289: fixedInit(); 290: //randInit();

                                                                                                    291:

292: /* Set initial quality */

293: quality max = nnQualityCheck(file quality);

294: training best = 0; 295: training total = 0;

                                                                                                    296:
                                                                                                    297:  printf("Running neural network with following parameters\n");
                                                                                                    298:  printf("Input  nodes    : %i\n", INPUT SIZE);
                                                                                                    299:  printf("Hidden nodes    : %i\n", HIDDEN SIZE);
                                                                                                    300:  printf("Output nodes    : %i\n", OUTPUT SIZE);
                                                                                                    301:  printf("Learning rate   : %lf\n", LEARN SPEED);

302: printf("Quality check : %i\n", QUALITY ROUND);

                                                                                                    303:  printf("Initial quality : %lf %%\n", quality max);

304: /* Start training */ 305: //nnNeuronOutput();

                                                                                                    306:  i = 1;

307: handle = fopen(file training,"r");

308: while ( nnReadInput(handle) == TRUE) {

309: training total++;

310: nnCalc(); 311: //nnOutput(); 312: //nnHiddenOutput();

                                                                                                    313:

314: if (nnValidate() == FALSE) {

315: nnTrain();

316: //nnNeuronOutput();

                                                                                                    317:  }
                                                                                                    318:

319: /* Verifiy quality, stop training when quality is going down */ 320: if ((training total % QUALITY ROUND) == 0) { 321: printf("Learned: %i - ", training total); 322: quality = nnQualityCheck(file quality); 323: if (quality > quality max) { 324: quality max = quality; 325: training best = training total;

                                                                                                    326:  }
                                                                                                    327:  }
                                                                                                    328:  }

329: fclose(handle);

                                                                                                    330:  printf("Max quality: %.2lf%% at training round: %i\n", quality max,

331: training best);

332: quality = nnQualityCheck(file validate);

333: return(EX OK);

                                                                                                    334: }
                                                                                                    335:

001: #!/usr/bin/env perl -w

                                                                                                    002:

003: while ($line = <STDIN>) {

004: #Input in way more every workable format

005: @numbers = split(',',$line);

                                                                                                    006:

007: #Get the number of every colour in on every line

008: @colour = (0, 0, 0, 0, 0); 009: foreach (0,2,4,6,8) { 010: $colour[$numbers[$ ]]++;

                                                                                                    011:  }
                                                                                                    012:

013: #Find pairs, triples, four

014: @cards = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

015: foreach (1,3,5,7,9) {

016: $cards[$numbers[$ ]]++;

                                                                                                    017:  }
                                                                                                    018:

019: ### BEGIN Specials ###

020: #triple = 1, four = 2

021: $special = 0;

022: #one pair = 1, two pair = 2

023: $pairs = 0;

024: ### END Specials ###

                                                                                                    025:

026: ### BEGIN decoded specials ### 027: $onepair = 0; 028: $twopair = 0; 029: $triple = 0; 030: $four = 0; 031: ### END decoded specials ###

032:

033: foreach (1 .. 13) { 034: if ($cards[$ ] == 2) { 035: $pairs++; 036: if ($pairs == 1) { 037: $onepair = 1; 038: } elsif ($pairs == 2) { 039: $twopair = 1;

040:  }

041: } elsif ($cards[$ ] > 2) { 042: $special = $cards[$ ]; 043: if ( $special == 3) { 044: $triple = 1; 045: } elsif ($special == 4) { 046: $four = 1;

047:  }
048:  }
049:  }
050:

051: #Decode into split option array 052: @output = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0); 053: $output[$numbers[10]]++;

054:

055: $pairs /= 10; 056: $special /= 10; 057: foreach (0 .. 10) { 058: $numbers[$ ] /= 10;

059:  }

060: foreach (0 .. 4) { 061: $colour[$ ] /= 10;

062:  }
063:

064: # Input 10 : all cards 065: # Output 1 : decoded version of result

066:  #print "@numbers[0..10]\n";
067:

068: # Input 10 : all cards 069: # Output 10 : all options, one node

070:  print "@numbers[0..9] @output[0..9]\n";
071:

072: # Input 14 : all cards + colours 073: # Output 10 : all options, one node

074:  #print "@numbers[0..9] @colour[1..4] @output[0..9]\n";
075:

076: # Input 16 : all cards + specials 077: # Output 10 : all options, every has it’s one node

078:  #print "@numbers[0..9] @colour[1..4] $pairs $special @output[0..9]\n";
079:

080: # Input 14 : all cards + decoded specials 081: # Output 10 : all options, every has it’s one node

082:  #print "@numbers[0..9] $onepair $twopair $triple $four @output[0..9]\n";
083:

084: # Input 18 : all cards + colour + decoded specials 085: # Output 10 : all options, every has it’s one node

086:  #print "@numbers[0..9] @colour[1..4] $onepair $twopair $triple $four @output[0..9]\n";
087:

088: # Input 10: all cards 089: # output 2: full house, four of a kind

090:  #print "@numbers[0..9] $output[6] $output[7]\n";
091:

092: # Input 16 : all cards + specials 093: # output 1 : full house

094:  #print "@numbers[0..9] @colour[1..4] $pairs $special $output[7]\n";
095:

096: # Input 2 : Calculated set 097: # output 1 : full house

098:  #print "$pair $special $output[6]\n";
099:

100: # Input 8 : colour + decoded specials 101: # Output 10 : all options, every has it’s one node

102:  #print "@colour[1..4] $onepair $twopair $triple $four @output[0..9]\n";
103: }