Kirjoittaja: Antti Laaksonen
Kirjoitettu: 06.05.2007 – 06.05.2007
Tagit: koodi näytille, vinkki
Harva tietokoneohjelma pystyy oppimaan itsenäisesti asioita. Hidas ohjelma ei muutu nopeaksi, vaikka se saisi kuinka paljon "harjoitusta" (ohjelmaa suoritettaisiin lukuisia kertoja peräkkäin). Yleensä on myös kysymys siitä, osaako ohjelma jonkin asian kokonaan vai ei ollenkaan. Jos ohjelmalta kysyy Suomen itsenäisyyspäivää, vastaus voi olla "6.12." tai "en tiedä", mutta tuskin "taisi olla siinä joulukuun alussa". Tietokoneen ja ihmisen tapa oppia vaikuttaa siis hyvin erilaiselta.
Joskus kuitenkin on mahdollista laatia melko helposti ohjelma, joka oppii pikku hiljaa asioita sen suorituksen aikana. Tässä on esimerkkinä yksinkertainen tikkupeli, jossa kaksi pelaajaa nostavat kasasta vuorotellen tikkuja. Joka vuorolla tikkuja saa nostaa yhden, kaksi tai kolme. Pelin häviää se, joka joutuu nostamaan viimeisen tikun. Tähän peliin on olemassa hyvin yksinkertainen voittostrategia, mutta yritetään nyt tehdä oppiva tekoäly, joka ei aluksi tiedä pelistä mitään, mutta kehittyy ajan myötä taitavaksi pelaajaksi.
Oppiminen tapahtuu seuraavasti: Tekoälyn käytössä on joukko hattuja, joissa on palloja. Jokaiselle jäljellä olevien tikkujen määrälle on oma hattu, ja jokaisessa pallossa taas lukee, kuinka monta tikkua tekoälyn tulee nostaa. Vuorollaan tekoäly nostaa hatusta yhden pallon umpimähkään, poistaa tikkuja sen ilmoittaman määrän ja panee pallon syrjään. Aluksi hatuissa on jokaista palloa yhtä monta, mutta pelin lopputuloksen (voitto tai häviö) selvittyä tekoäly käy läpi hatut uudestaan. Jos peli päättyi voittoon, tekoäly palauttaa pallon hattuun ja lisää sinne vielä toisen samanlaisen pallon. Mutta jos peli päättyi häviöön, tekoäly ei pane hattuun mitään takaisin. Siis jokaisen hatun pallojen määrä joko kasvaa tai vähentyy yhdellä.
Tämä oppimistapa tunnetaan yleisemmin nimellä "boxes", ja sitä on tutkittu jo 1960-luvun lopulla (Chambers ja Michie). Alun perin pelinä oli tosin ristinolla ja hattujen ja pallojen asemesta käytettiin tulitikkulaatikoita ja lasihelmiä, mutta ajatus oli sama.
Tässä ohjelmassa kaksi oppivaa tekoälyä pelaavat toisiaan vastaan. Tikkujen määrä arvotaan aluksi väliltä 20 - 40. Pelin aikana sekä voittaja että häviäjä oppivat jotain, ja tekoälyt voivat kirjata tulokset yhteiseen muistiin. Hatuissa olevien pallojen määrää on rajoitettu niin, että jokaista palloa täytyy olla vähintään 1 ja korkeintaan 100. Näin tekoäly ei pelaa täysin kaavamaisesti, vaan aina on tilaa sattumalle. Oppimisen jälkeen tekoälyn taitoja voi kokeilla muunlaisia pelaajia vastaan (satunnainen valinta, paras valinta, käyttäjän valinta). Lisäksi voi tutkia graafisesti kaikkien hattujen sisältöä.
Seuraavissa kuvissa näkyvät hattujen sisällöt tekoälyn kehityksen aikana:
Aloitustilanne: https://www.ohjelmointiputka.net/kuvat/tikut1.png
100 pelin jälkeen: https://www.ohjelmointiputka.net/kuvat/tikut2.png
500 pelin jälkeen: https://www.ohjelmointiputka.net/kuvat/tikut3.png
1000 pelin jälkeen: https://www.ohjelmointiputka.net/kuvat/tikut4.png
5000 pelin jälkeen: https://www.ohjelmointiputka.net/kuvat/tikut5.png
Tästä havaitaan, että useimpiin tilanteisiin on olemassa selkeä paras valinta, mutta joskus mitään kunnollista valintaa ei ole. Tarkemmin hyvin pelaava tekoäly pyrkii ajamaan vastustajan sellaiseen tilanteeseen, että tikkujen määrän jakojäännös 4:llä on 1. Jos tekoäly onnistuu tässä kerran ja jatkaa samaa menettelyä pelin loppuun asti, se voittaa pelin varmasti. Eli jos molemmat pelaajat tietävät asian, pelin voittajan ratkaisee suoraan alussa olevien tikkujen määrä.
' Oppiva tekoäly ' Antti Laaksonen, 2007 ' eri pallojen määrät kussakin hatussa DIM SHARED Hatut%(1 TO 50, 1 TO 3) ' pelaajien nostamat pallot pelin aikana DIM SHARED Muisti%(1 TO 2, 1 TO 50, 1 TO 2) ' pelaajien nostamien pallojen määrät DIM SHARED Kohdat%(1 TO 2) ' laitetaan jokaiseen hattuun 20 jokaista palloa FOR i% = 1 TO 50 FOR j% = 1 TO 3 Hatut%(i%, j%) = 20 NEXT NEXT RANDOMIZE TIMER SCREEN 12 CLS Valikko ' merkitsee muistiin, minkä pallon pelaaja nosti SUB MerkitseMuistiin (vuoro%, tikut%, nosto%) Kohdat%(vuoro%) = Kohdat%(vuoro%) + 1 Muisti%(vuoro%, Kohdat%(vuoro%), 1) = tikut% Muisti%(vuoro%, Kohdat%(vuoro%), 2) = nosto% END SUB ' näyttää hattujen sisällön graafisesti SUB NaytaMuistot CLS FOR i% = 1 TO 40 LINE (i% * 16 - 16, 330)-STEP(2, -Hatut%(i%, 1) * 3), 1, BF LINE (i% * 16 - 16 + 3, 330)-STEP(2, -Hatut%(i%, 2) * 3), 2, BF LINE (i% * 16 - 16 + 6, 330)-STEP(2, -Hatut%(i%, 3) * 3), 3, BF NEXT LOCATE 22, 1 PRINT " 1 "; PRINT "1 1 1 1 1 1 1 1 1 2 "; PRINT "2 2 2 2 2 2 2 2 2 3 "; PRINT "3 3 3 3 3 3 3 3 3 4 "; LOCATE 23, 1 PRINT "1 2 3 4 5 6 7 8 9 0 "; PRINT "1 2 3 4 5 6 7 8 9 0 "; PRINT "1 2 3 4 5 6 7 8 9 0 "; PRINT "1 2 3 4 5 6 7 8 9 0 "; WHILE INKEY$ = "": WEND CLS END SUB ' nostaa hatusta pallon todennäköisyyksien mukaan FUNCTION NostaHatusta% (hattu%) summa% = Hatut%(hattu%, 1) + Hatut%(hattu%, 2) + Hatut%(hattu%, 3) arpa% = INT(RND * summa%) + 1 kohta% = 1 WHILE arpa% > Hatut%(hattu%, kohta%) arpa% = arpa% - Hatut%(hattu%, kohta%) kohta% = kohta% + 1 WEND NostaHatusta% = kohta% END FUNCTION ' kysyy tikkujen määrää käyttäjältä FUNCTION NostaItse% (tikut%) PRINT "Jäljellä on"; tikut%; "tikkua." PRINT STRING$(tikut%, "I") DO PRINT "Kuinka monta haluat nostaa (1 - 3)?" INPUT maara% LOOP WHILE maara% < 1 OR maara% > 3 NostaItse% = maara% END FUNCTION ' arpoo nostettavan tikkujen määrän FUNCTION NostaJoku% (tikut%) NostaJoku% = INT(RND * 3) + 1 END FUNCTION ' laskee parhaan nostettavien tikkujen määrän FUNCTION NostaParas% (tikut%) SELECT CASE tikut% MOD 4 CASE 0 NostaParas% = 3 CASE 1, 2 NostaParas% = 1 CASE 3 NostaParas% = 2 END SELECT END FUNCTION ' poistaa tai lisää hattuihin palloja pelin tuloksen mukaan SUB Opiskele (pelaaja%, voitto%) FOR i% = Kohdat%(pelaaja%) TO 1 STEP -1 tikut% = Muisti%(pelaaja%, i%, 1) nosto% = Muisti%(pelaaja%, i%, 2) IF voitto% THEN IF Hatut%(tikut%, nosto%) < 100 THEN Hatut%(tikut%, nosto%) = Hatut%(tikut%, nosto%) + 1 END IF ELSE IF Hatut%(tikut%, nosto%) > 1 THEN Hatut%(tikut%, nosto%) = Hatut%(tikut%, nosto%) - 1 END IF END IF NEXT END SUB ' kahden tekoälyn välinen peli, jossa molemmat oppivat FUNCTION Oppipeli% tikut% = INT(RND * 21) + 20 vuoro% = 1 WHILE tikut% > 0 nosto% = NostaHatusta(tikut%) MerkitseMuistiin vuoro%, tikut%, nosto% tikut% = tikut% - nosto% IF vuoro% = 1 THEN vuoro% = 2 ELSE vuoro% = 1 WEND Opiskele 1, vuoro% = 1 Opiskele 2, vuoro% = 2 Oppipeli% = vuoro% END FUNCTION ' pelaa tietyn määrän opettavia pelejä SUB Oppipelit (maara%) FOR i% = 1 TO maara% Kohdat%(1) = 0 Kohdat%(2) = 0 IF Oppipeli% = 1 THEN voitto% = voitto% + 1 NEXT PRINT " Pelien määrä:"; maara% PRINT "Pelaajan 1 voitot:"; voitto% PRINT " Voittosuhde:"; INT(voitto% / maara% * 100); "%" END SUB ' testipeli tekoälyn ja toisen vastustajan välillä FUNCTION Testipeli% (vastus%) tikut% = INT(RND * 21) + 20 vuoro% = 1 IF vastus% = 4 THEN PRINT "Pelin alussa tikkuja on"; tikut% WHILE tikut% > 0 IF vuoro% = 1 THEN nosto% = NostaHatusta(tikut%) IF vastus% = 4 THEN PRINT "Vastustaja nostaa"; nosto%; "tikkua." ELSE SELECT CASE vastus% CASE 1 nosto% = NostaHatusta(tikut%) CASE 2 nosto% = NostaJoku(tikut%) CASE 3 nosto% = NostaParas(tikut%) CASE 4 nosto% = NostaItse(tikut%) CASE ELSE nosto% = NostaHatusta(tikut%) END SELECT END IF tikut% = tikut% - nosto% IF vuoro% = 1 THEN vuoro% = 2 ELSE vuoro% = 1 WEND IF vastus% = 4 THEN IF vuoro% = 1 THEN PRINT "Vastustaja voitti pelin!" PRINT ELSE PRINT "Sinä voitit pelin!" PRINT END IF END IF Testipeli% = vuoro% END FUNCTION ' pelaa tietyn määrän testipelejä SUB Testipelit (maara%, vastus%) FOR i% = 1 TO maara% Kohdat%(1) = 0 Kohdat%(2) = 0 IF Testipeli%(vastus%) = 1 THEN voitto% = voitto% + 1 NEXT PRINT " Pelien määrä:"; maara% PRINT "Pelaajan 1 voitot:"; voitto% PRINT " Voittosuhde:"; INT(voitto% / maara% * 100); "%" END SUB ' näyttää valikon ja lukee käyttäjän valinnat SUB Valikko DO PRINT PRINT "Valitse vaihtoehto:" PRINT " 1. Opeta tekoälyä" PRINT " 2. Testaa tekoälyä" PRINT " 3. Tutki muistoja" PRINT " 4. Sulje ohjelma" INPUT valinta% SELECT CASE valinta% CASE 1 PRINT "Kuinka monta peliä?" INPUT pelit% PRINT Oppipelit pelit% CASE 2 PRINT "Kuinka monta peliä?" INPUT pelit% PRINT "Kuka on vastustaja?" INPUT vastus% PRINT Testipelit pelit%, vastus% CASE 3 NaytaMuistot CASE 4 END END SELECT LOOP END SUB
wau! En kokeillut, mutta vaikuttaa hienolta.
Samat sanat, en tosin osaa basicia.
Aika huimia koodeja se Antti tänne latelee :O
Ikävän lähekkäiset värit valitsit diagrammeihin, mutta ei se itse vinkkiä pahenna. Mielenkiintoista!
Vau, melkoinen osaaja!
basicin syntaksi aiheuttaa aivovammoja, muuten aivan mielenkiintoinen
Nyt testasin, kun oli aikaa ja toimi todella hienosti, paitsi jostain syystä Subien lopussa oli 2 END SUBia! Sama funktioissa.
gamehouse kirjoitti:
Nyt testasin, kun oli aikaa ja toimi todella hienosti, paitsi jostain syystä Subien lopussa oli 2 END SUBia! Sama funktioissa.
Taitaa toinen silmä harittaa pahasti.
Tämän kun saisi c:lle niin olisi todella mukavaa, tietenkin pienellä tutkimisella saisi itsekkin tämän koodattua. :D
vähän näpeltää ja saa esm ohjelman johon voi syöttää "omaa luonnetta" ja sitten se puhuu kuin ihminen jonka luonne on siihen syötetty