Kirjautuminen

Haku

Tehtävät

Koodit: QB: Oppiva tekoäly

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

Kommentit

gamehouse [06.05.2007 17:12:10]

#

wau! En kokeillut, mutta vaikuttaa hienolta.

Kray [07.05.2007 14:49:19]

#

Samat sanat, en tosin osaa basicia.

msdos464 [08.05.2007 01:02:08]

#

Aika huimia koodeja se Antti tänne latelee :O

Metabolix [08.05.2007 19:36:00]

#

Ikävän lähekkäiset värit valitsit diagrammeihin, mutta ei se itse vinkkiä pahenna. Mielenkiintoista!

kayttaja-4976 [09.05.2007 21:06:48]

#

Vau, melkoinen osaaja!

BlueByte [13.05.2007 22:43:28]

#

basicin syntaksi aiheuttaa aivovammoja, muuten aivan mielenkiintoinen

gamehouse [24.05.2007 14:49:22]

#

Nyt testasin, kun oli aikaa ja toimi todella hienosti, paitsi jostain syystä Subien lopussa oli 2 END SUBia! Sama funktioissa.

matpit [21.12.2007 23:13:33]

#

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.

vehkis91 [25.11.2008 22:28:07]

#

Tämän kun saisi c:lle niin olisi todella mukavaa, tietenkin pienellä tutkimisella saisi itsekkin tämän koodattua. :D

sammakkomies [06.12.2009 05:58:58]

#

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

Kirjoita kommentti

Muista lukea kirjoitusohjeet.
Tietoa sivustosta