Järjestäjä: Metabolix
Kesällä 2014 Ohjelmointiputkassa pidettiin tekoälykilpailu. Aiheena oli kahden pelaajan Lukupeli. Pelissä tekoälyn pitää arvata luku väliltä 0–9 niin, että luku on pienempi kuin vastustajalla mutta kuitenkin mahdollisimman suuri; molemmat saavat pisteitä pienemmän luvun verran, ja lisäksi pienemmän luvun valinnut saa kolme lisäpistettä tai tasapelissä kumpikin yhden lisäpisteen. Ottelun voittamisesta sai myös huomattavan määrän lisäpisteitä; tarkempi laskukaava on kuvattu kilpailun säännöissä. Ottelun 1000 kierroksen aikana tekoälyllä oli mahdollisuus analysoida vastustajan tekniikkaa ja sopeutua siihen. Kilpailun aikana julkaistiin välituloksia, joita saattoi hyödyntää tekoälyn kehittämisessä.
Kilpailuun osallistui 32 ohjelmaa. Perinteisille pelialgoritmeille ei ollut juuri sijaa tässä kisassa, vaan kaikenlaiset kukat saivat kukkia: Kärjessä yksi hyödynsi kilpailun välitulosten siirtosarjoja ja todennäköisyyksiä, toinen analysoi jollain tasolla taktiikoita, kolmas yllätti pelaamalla ensimmäisen siirron jälkeen yleensä joko pelkkiä nollia tai pelkkiä yhdeksikköjä ja neljäs taas luotti vain yhden siirron mittaiseen muistiin kulloisestakin ottelusta. Muussa joukossa oli monenlaista yritelmää keskiarvoista pseudosatunnaislukuihin ja vakioriveistä erilaisiin hämäyksiin.
Jäljempänä tällä sivulla on ohjelmien kuvauksia, ja tekoälyjen lähdekoodit voi ladata yhtenä pakettina.
Alla ovat kaikki kilpailun osallistujat voittajasta alkaen. V, T ja H merkitsevät voittoja, tasapelejä ja häviöitä otteluissa. Pisteet ovat otteluissa kerättyjä pisteitä, ja tulos on säännöissä mainitulla kaavalla näistä laskettu kokonaispistemäärä.
sija | tekoäly | kieli | tekijä | nimimerkki | V | T | H | pisteet | tulos |
---|---|---|---|---|---|---|---|---|---|
1. | PahaApina | Pascal | 20 | 0 | 11 | 139888 | 7495776 | ||
2. | R33L | Python | Antti Vainio | Anaatti | 20 | 0 | 11 | 131198 | 7304596 |
3. | Haistaja | Python | Chiman | 22 | 1 | 8 | 96245 | 6977454 | |
4. | prog09 | C | pr0l3 | 19 | 1 | 11 | 112935 | 6681898 | |
5. | ThaiCurry | Brainfuck | Lauri Kenttä | Metabolix | 21 | 10 | 0 | 86501 | 6542174 |
6. | Luuuser | C++ | Oskari Mieskolainen | Oskuz | 20 | 10 | 1 | 82149 | 6225518 |
7. | greedy | Python | Lari Koponen | L2-K2 | 21 | 10 | 0 | 69481 | 6167734 |
8. | Kukka | Java | Jesse Niininen | FINDarkside | 21 | 10 | 0 | 56540 | 5883032 |
9. | kompa | C++ | Johannes Tuomela | johku90 | 19 | 4 | 8 | 74848 | 5843984 |
10. | esim | (useita) | 21 | 10 | 0 | 44476 | 5617624 | ||
11. | ZZplNMQ | Perl | 19 | 11 | 1 | 56779 | 5446466 | ||
12. | nnx9 | Python | Markku Järvinen | Oldfield | 17 | 1 | 13 | 76219 | 5432322 |
13. | Lukuilija | C++ | Elias Kosunen | Eki++ | 17 | 11 | 3 | 74023 | 5384010 |
14. | ElStupido | Python | qwerty12302 | 16 | 2 | 13 | 80761 | 5311334 | |
15. | ElReino | C | Eino-Pekka Kanto | reino | 15 | 12 | 4 | 83354 | 5147468 |
16. | Simple1 | Java | Timo Sakari | Timmmo | 15 | 10 | 6 | 82285 | 5123950 |
17. | Arvain | C# | Antti Airto | airtantt | 14 | 0 | 17 | 90998 | 5094724 |
18. | ajv | C# | Aapo Vuoristo | ajv | 15 | 1 | 15 | 78953 | 5050646 |
19. | ysim | C | Lauri Kenttä | Metabolix | 0 | 0 | 31 | 220912 | 4860064 |
20. | hop1 | Fortran | Jali Heinonen | jalski | 14 | 0 | 17 | 78895 | 4828458 |
21. | Matilda | C | Marko Kurkinen | makumaku | 13 | 11 | 7 | 73258 | 4483532 |
22. | hidari | C | Lauri Kenttä | Metabolix | 11 | 12 | 8 | 86529 | 4333670 |
23. | Sini | C# | Oskari Mieskolainen | Oskuz | 6 | 0 | 25 | 128812 | 4159336 |
24. | sqrt | Python | Juha Teurokoski | Teuro | 10 | 0 | 21 | 64834 | 3635468 |
25. | purkka | Python | 7 | 0 | 24 | 80252 | 3311928 | ||
26. | RanTomi | Java | Timo Sakari | Timmmo | 9 | 0 | 22 | 54261 | 3181950 |
27. | jytky | C++ | Antti Laaksonen | Antti Laaksonen | 7 | 1 | 23 | 73247 | 3157818 |
28. | Arpoja | Java | Tuomas Karjalainen | TuomasK | 3 | 0 | 28 | 102354 | 2914524 |
29. | SafeApe | Python | Vesipeto | 6 | 0 | 25 | 70424 | 2874800 | |
30. | High5 | C | Timo Sakari | Timmmo | 1 | 0 | 30 | 117565 | 2807342 |
31. | JHNA | C++ | Juho Häkkinen | jeeukko | 7 | 0 | 24 | 45009 | 2536582 |
32. | dewabe | C++ | Teemu Vartiainen | dewabe | 6 | 0 | 25 | 43571 | 2284034 |
Onnea voittajalle!
Tekoälyt käyttivät eri aloituslukuja seuraavasti:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
11 | 5 | 0 | 0 | 3 | 1 | 1 | 2 | 3 | 6 |
Koko kilpailun lukujakauma taas oli tällainen:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
569033 | 90079 | 28729 | 32349 | 46057 | 53037 | 23448 | 31482 | 58955 | 58831 |
57,4 % | 9,1 % | 2,9 % | 3,3 % | 4,6 % | 5,3 % | 2,4 % | 3,2 % | 5,9 % | 5,9 % |
Kisassa pelattiin 189 kertaa 992:sta pelkkiä nollia sisältävä rivi, ja otteluista jopa 55 (11 %) sisälsi molemmilta pelaajilta pelkkiä nollia.
Kilpailussa jaetuista pisteistä kaikkiaan 38,9 prosenttia kertyi suoraan peleistä ja 61,1 prosenttia otteluvoitoista.
Oheisessa kaaviossa tekoälyjen väliset etäisyydet kuvaavat, miten paljon tekoälyjen tulokset eroavat toisistaan. Nähdään, että kärkijoukko on melko hajallaan muiden tekoälyjen seassa; kuvaajassa oikealla on heikommin menestyneitä ohjelmia, vasemmalla alhaalla nollapeleillä menestyneitä ohjelmia ja vasemmalla ylhäällä enemmän isommilla luvuilla pelanneita.
Yksittäisten otteluiden tulokset kerrotaan erillisellä sivulla. Kaikkien otteluiden siirrot on koottu pakettiin.
Tekoälyjen lähdekoodit voi ladata yhtenä pakettina. Alla ovat tekijöiden omat kuvaukset ohjelmistaan.
PahaApina | Ensiksi tekoäly yrittää pelata suoraan tiedostosta luetun mallin mukaan niin kauan, kuin vastustajan rivi noudattaa täsmälleen tiedostoa. Tiedostossa on eräiden tekoälyjen vakiorivejä, joita vastaan on helppo valita paras luku. Lisäksi tiedostossa on röyhkeästi välituloksista poimittuja siirtosarjoja, joilla saadaan hyvät pisteet muutamaa vastustajaa vastaan. Datatiedostossa voi laittaa tämän ”apinamoodin” päälle tai pois; välituloksissa tekoäly ei apinoinut. Jos mikään tiedoston peli ei sovi tilanteeseen täydellisesti, siirrytään karkeaan todennäköisyyslaskentaan. Tekoäly käyttää ennustamiseen viittä muuttujaa: kahden edellisen kierroksen siirtoja (2+2) ja neljän edellisen oman siirron keskiarvoa. Datatiedoston peleistä muodostetaan kustakin todennäköisyyskertoimet sille, kuinka nämä viisi muuttujaa yksitellen ennustavat vastustajan seuraavaa valintaa. Kertoimia päivitetään pelin kuluessa kertyvällä tiedolla, ja myös aivan perustilasta aloittavia kertoimia lisätään joka kierroksella. Kertoimista lasketaan vastustajan eri valinnoille todennäköisyys kertomalla kunkin viiden muuttujan antama kerroin keskenään. Sen jälkeen ohjelma valitsee todennäköisimmän luvun siitä mallista, joka olisi tähän mennessä ennustanut parhaiten. Jos vastustaja pelaa paljon nollia, koetetaan houkutella yhdeksiköillä vastustajaa isompiin lukuihin, mutta jos vastustaja ei lähde mukaan, aletaan itsekin pelata nollia. |
R33L Antti Vainio Anaatti | Tekoälyn pääperiaatteet ovat voiton tavoittelu ja hyvien pisteiden kerääminen, sekä häviöllä ollessaan lähinnä pisteiden maksimointi, minkä lisäksi tekoäly yrittää myös vaikuttaa vastutajan pelaamiin lukuihin. Tekoäly pyörittää taustalla useita eri luvunvalitsinfunktioita, joiden tehokkuutta mitataan vastustajan valintojen perusteella. Käyttämällä kulloinkin parasta valitsinfunktiota, tekoälyn pitäisi pysyä voitolla ja saada varsin paljon pisteitä. Tämä taktiikka kuitenkin johtaa useimmiten pelkkien nollien pelaamiseen, minkä vuoksi pisteitä ei kerry kovinkaan paljon. Siksi tekoäly kokeilee välillä myös hyvin isojen lukujen pelaamista siinä toivossa, että vastustajakin valitsisi isompia lukuja, mikä johtaisi suurempaan pistesaaliiseen. Jos tämä taktiikka näyttää johtavan häviöön tai kerryttää vähemmän pisteitä kuin muuten, lopetetaan isojen lukujen pelaaminen. Lopuksi, jos tekoäly huomaa olevansa liian pahasti häviöllä, se lopettaa voiton tavoittelemisen ja pyrkii vain pistesaaliinsa maksimointiin pelaamalla lähinnä ysejä. |
Haistaja Chiman | Pyöritetään eri periaatteilla toimivia tekoälyjä, jotka pelaavat peliä vastustajan näkökulmasta. Jos vastustajan oikea käytös sopii johonkin näistä riittävän hyvin, valitaan omaksi siirroksi kyseisen tekoälyn antaman siirron vastine. Muuten noudatetaan vakiosiirtosarjaa. Tekoälyt perustuvat vastustajan matkimiseen, vastustajan edellistä siirtoa vastaan pelaamiseen, saman voittoisan siirron toistamiseen ja toistuvien siirtosarjojen analysointiin. |
prog09 pr0l3 | Aloittaa ykkösellä. Jos vastustaja aloittaa nollalla, kerää pisteitä pelaamalla yhdeksikköjä. Muuten yrittää voittaa pelaamalla nollia. Jos vastustaja pelaa alussa viisi samaa, alkaa pelata yhden pienempää kuin vastustaja (tai nollaa). |
ThaiCurry Lauri Kenttä Metabolix | ThaiCurry valitsee yhtä pienemmän luvun kuin vastustaja viimeksi samassa tilanteessa. Jos muistissa ei ole nykyisen kaltaista tilannetta tai jos vastustajalta odotetaan nollaa, tekoäly valitsee turvallisesti nollan. Pelitilanne käsittää edellisen oman numeron. |
Luuuser Oskari Mieskolainen Oskuz | Palauttaa yhtä pienemmän kuin vastustaja pari kierrosta takaperin. |
greedy Lari Koponen L2-K2 | Erittäin yksinkertainen ahne algoritmi. Pyrkii ensisijaisesti varmistamaan voiton (tai vähintään tasapelin) kustakin pelistä pelaamalla riittävän määrän nollia. Tämän tavoitteen ollessa turvattu maksimoi seuraavan siirron pisteiden odotusarvoa olettaen vastustajan pelaavan satunnaisesti jonkin edellisistä siirroistaan. Koska ylläoleva kuvaus tuottaisi tekoälyn, joka pelaisi erittäin heikosti tiettyjä toistuvia kuvioita vastaan (kuten 09090909...), on tekoälyyn lisätty yksinkertainen sakkotermi näitä tilanteita vastaan. Tämä termi muuntaa tekoälyn pelitapaa hieman kohti esimerkkitekoälyä aina, kun se joutuu erikseen turvaamaan johtoaan pelaamalla nollan. Lähdekoodia tutkimalla voinee silti vielä keksiä, missä tilanteissa tämä sakkotermi ei ole riittävä. |
Kukka Jesse Niininen FINDarkside | Ohjelma pelaa nollaa, ellei ole erittäin todennäiköistä, että suurempaa lukua pelaamalla voittaa (vastustaja pelaa vain yhtä lukua tai vastustajan pienin luku on suurempi kuin 1). |
kompa Johannes Tuomela johku90 | Tekoäly arvaa aina vähän pienemmän luvun, kuin vastustaja viimeistä edellisellä kerralla. |
esim | Esimerkki valitsee aina luvun 0. |
ZZplNMQ | Valitsee pienemmän kuin vastustajan pienin. |
nnx9 Markku Järvinen Oldfield | Hermoverkko määrittelee numeron väliltä 0–9 seuraavalla tavalla: Verkolle annetaan sisään jokaisen vuoron jälkeen kaksi vastapuolen viimeiseksi syöttämää numeroa. Jokainen vastapelaajan syöte, joka on enemmän kuin 0, muuttaa painojen arvoja. Neuronien arvo saadaan kertomalla vastapuolen syötteet painojen arvolla ja laskemalla kumpikin kerrottu luku yhteen. Näistä neuronien arvoista lasketaan sigmoidifunktion avulla 2 liukulukua. Jos ensimmäisen neuronin arvo on suurempi kuin toisen, tarkistetaan, voidaanko vähentää ensimmäisestä vastapuolen viimeksi syöttämästä 2; jos voidaan, vähennetään ja palautetaan saatu numero, muuten palautetaan alkuperäinen numero. Jos toisen neuronin arvo on suurempi (tai yhtäsuuri), tehdään toiselle vastapuolen viimeksi syöttämälle numerolle edellä kuvattu toimenpide. Lisäominaisuutena: Jos vastapuoli pelaa samaa lukua 2 kertaa peräkkäin, oletetaan, että aikoo pelata saman luvun myös 3 kerran, ja vähennetään samasta luvusta 2 ja tulostetaan se, kunhan luku ei mene alle 0. |
Lukuilija Elias Kosunen Eki++ | Vastaus on yhden pienempi kuin vastustajan kolmen viimeisimmän vastauksen keskiarvo. Lisäksi vetää vähän väliä luvun hatusta, millä yrittää johtaa vastustajan systeemiä harhaan. |
ElStupido qwerty12302 | Ohjelma ottaa muistiin vastustajan edellisellä kierroksella antaman luvun, vähentää siitä yhden ja antaa sen seuraavalla kierroksella. Ensimmäisellä kierroksella ohjelma antaa luvun yksi. |
ElReino Eino-Pekka Kanto reino | ElReino suorittaa rinnakkain useita strategioita ja valitsee strategian kahden kriteerin perusteella: strategian piti tuottaa enemmän pisteitä itselle, ja sen pitää tuottaa eniten pisteitä edellisen kriteerin perusteella valituista strategioista. ElReino koittaa valita luvun joka on yhtä pienempi vastustajan luvusta. Jos vastustaja pelaa nollalla niin silloin ElReinokin pelaa nollalla. ElReinon strategioihin kuuluu luvun seitsemän tai yhdeksän valitseminen, edellisen luvun valitseminen, luvun valitseminen joka olisi tuottanut edellisellä kierroksella eniten pisteitä tai luvun valitseminen vastustajan kymmenen edellisen siirron perusteella. ElReino - Villin Lännen pelätyin tekoäly. |
Simple1 Timo Sakari Timmmo | Yksinkertainen algoritmi, joka pyrkii mimimoimaan tappiot ja mahdollisuuksien mukaan pelaamaan isommillakin. Hävityn vuoron jälkeen algoritmi pelaa aina nollan, muulloin se valitsee satunnaisesti jonkin vastustajan edellisistä numeroista, vähentäen tästä ykkösen. Algoritmi huomaa myös tilanteen, jossa otteluparin voitto tai tappio on varma, ja pelaa siinä tilanteessa yhdeksikköä pyrkien maksimoimaan loppupelin pistekertymän. Mikäli vastustaja haluaa pelata isoa peliä, algoritmi lähtee tähän mukaan maksimoiden samalla mahdollisuutensa saada hyvät pisteet. |
Arvain Antti Airto airtantt | Valitaan luku siten, että vastustajan luvuista x% on ollut sitä suurempia. Kuitenkin pidetään omien lukujen keskiarvo riittävän suurena. |
ajv Aapo Vuoristo ajv | Sisältää erilaisia yksinkertaisia taktiikoita. Pelin edetessä tutkii, mikä taktiikka olisi pärjännyt parhaiten, ja valitsee aina sen. |
ysim Lauri Kenttä Metabolix | Tekoäly valitsee aina luvun 9. Logiikka on se, että tällä tavalla hyvä vastustaja alkaa valita aina luvun 8, jolloin tekoäly saa aina 8 pistettä ja kerää reilun määrän pisteitä, vaikka ottelut se tietenkin häviää. |
hop1 Jali Heinonen jalski | Opettaa Hopfield-neuroverkkoa aina vastustajan kolmella edellisellä siirrolla. Verkolta kysytään, mikä on vastustajan todennäköisin seuraava vastaus (pienin bittivirhe), ja vähennetään tästä luvusta yksi. |
Matilda Marko Kurkinen makumaku | Ei mitään logiikkaa. |
hidari Lauri Kenttä Metabolix | Ohjelma reagoi hitaasti vastustajan trendiin: se siirtyy joka kierroksen jälkeen omasta luvustaan aina noin puolet kohti vastustajan lukua. |
Sini Oskari Mieskolainen Oskuz | Sini valitsee seuraavan luvun 3 algoritmin avulla:
Näistä käytettävä valitaan niiden tähänastista menestystä vertailemalla. Jos mikään ei ole menestynyt tarpeeksi hyvin, valitaan ”nollalinja”. |
sqrt Juha Teurokoski Teuro | Tekoäly valitsee 20 ensimmäisen kierroksen aikana jakojäännöksen numeron yhdeksän kanssa. Tämän jälkeen tekoäly laskee vastustajan kymmenen viimeisimmän siirron keskiarvon, josta vähennetään numero yksi. Ohjelman ensimmäisen version mukaan tullutta nimeä (neliöjuuri) en muuttanut, vaikka tätä tekoäly ei enää käytäkään. |
purkka | Jauhaa samaa pätkää. |
RanTomi Timo Sakari Timmmo | Pikainen kyhäelmä, joka valitsee luvun pseudosatunnaisesti painottaen pienempiä numeroita. Nollalla suurin todennäköisyys. |
jytky Antti Laaksonen | Tekoäly valitsee luvun, joka on pienempi kuin vastustajan valitsemien lukujen keskiarvo siihen mennessä. |
Arpoja Tuomas Karjalainen TuomasK | Arpoo vain satunnaislukuja ennalta määritetyllä siemenluvulla. |
SafeApe Vesipeto | Tekoäly matkii sitä, miten vastustaja on pelannut keskimäärin. Välillä tarjotaan keskiarvosta hieman suurempaa, jotta ei päädyttäisi ”nollapeliin”, jos vastustaja on ns. yhteistyöhaluinen. Yleensä kuitenkin tarjotaan hieman pienempää, jotta odotusarvo voitolle pitkällä tähtäimellä olisi positiivinen. |
High5 Timo Sakari Timmmo | Algoritmi tarjoaa 100 % todennäköisyysellä viitosta, siitä nimi High5 eli kotoisemin yläfemma. Tämä huumorilla väsätty ”äly” tulee todennäköisesti kamppailemaan kilpailun viimeisestä sijasta. |
JHNA Juho Häkkinen jeeukko | Simppeli (ja huono), laskee keskiarvon ja vähentää yhden. Tarkistaa myös, onko luku sallittu. That's it! :D P.S. Nimi on Juhon Huono Numeron Arvaaja. |
dewabe Teemu Vartiainen dewabe | Oma numero on vastustajan numeroiden keskiarvo vähennettynä yhdellä. |
Kiitos kaikille osallistujille hyvästä kilpailusta, ja tervetuloa mukaan taas ensi kerralla.