Voi luoja kun oon koodaillut! Tupakkia ja kahvia kului. Numeropelissä on nyt tekoäly!
Tuolla .exe tiedosto: https://petke.info/numeropeli2.exe
Windows ei tietenkään tykkää ajaa .exe tiedostoja viirusten pelossa, mutta esimerkiksi Windows 10 saatte sen toimimaan painamalla "More info" ja "Run anyway".
En paljasta Free Pascal koodia, vielä. Myöhemmin kyllä. Saatte testailla ohjelman tekoälyä. Lupaan 10 euroa sille, joka voittaa "koneen"! Ohjelma paljastaa koodin, millä saatte sen rahan, jos voitatte. Tilinumeronne ja koodin voitte sitten lähettää sähköpostilla petrikeckman@gmail.com Joku hakkeri voisi löytää koodin ohjelmaa hakkeroimalla, mutta luotan rehellisyyteenne. Tosin kuvakaappaus lopputilanteesta todisteeksi olisi ok.
En osannut, enkä jaksanut Free Pascalilla alkaa väsäämään mitään graafista käyttöliittymää. Peli toimii MS-DOS Command Promptissa. Ei tarvitse käynnistää sieltä, vaan ohjelma avaa Promptin kun klikkaatte sen kuvaketta Windowssissa.
Voittaminen ei missään nimessä ole mahdotonta! Pelaajalla on aloittajan etu ja 9x9 ruudukko on melko pieni. Toisin kuitenkin kuin tässä https://petke.info/numeropeli/ Javascript pelissä, pelaaja ei pääse ihan puhtaalta pöydältä aloittamaan, vaan tähtilaatan paikka ollaan aluksi arvottu.
Itse en raskaan koodaamistyön jälkeen jaksa niin ohjelman tekoälyä testata. Ja varmasti pelissä voi olla bugeja. Onko virheetöntä ohjelmaa olemassakaan? Nyt syön ja meen nukkumaan.
Muistakaa!: Painatte siis sitä _rivinumeroa_, mistä numeron valitsette. Itse väsyneenä aloin painelemaan välillä sitä numeroa, minkä halusin valita. Aluksi peli miettii melko kauan, koska pelipuu rakenteesta tulee iso. Pelin edetessä miettimisaika vähenee.
Hauskaa peli-iloa! (jos nyt tappioista voi iloita?)
PetriKeckmaan kirjoitti:
En paljasta Free Pascal koodia, vielä.
Tokihan mä voin jo ohjelman Pascal koodin julkaista: deletoin siitä sen koodin, millä ansaitsisitte sen 10 euroa. Siis edelleen tarjous on voimassa. Voihan joku taitava innostua jopa ohjelmoimaan oman versionsa ja pistää pelaamaan sen mun ohjelmaa vastaan. Huono tuntipalkka kyllä olisi...
Mun ohjelma rakentaa pelipuuta yhdeksän tasoa eteenpäin. Jos joku laittaa ohjelmansa miettimään pelitilannetta pidemmälle, niin ehkä voittaisi sen 10 egee.
Varmasti löytyy PALJON tyylikkäämpi ratkaisu kuin mulla. Esimerkiksi rekursion käyttö pelipuun käsittelyssä lyhentäisi koodia huomattavasti ja tekisi siitä selkeämpää. Itse en vaan oikein rekursiota hallitse :( Siksi tää koodilistaus on pitkä kuin nälkävuosi, mutta ehkä siitä joku voi jotain käsitystä saada, kuinka ohjelma "miettii" siirtoja eteenpäin.
Ja niin, tietysti: nyt kun koodin paljastan, niin voisihan joku käyttää sitä hyväkseen ja pistää oman versionsa pelaamaan mun ohjelmaa vastaan ja miettimään siirtoja pidemmälle. Silloin joutuu tietysti manuaalisesti syöttämään tämän ohjelman arpoman alkutilanteen.
Koodi on alunperin tehty Amigan Pascalilla. Se ohjelma oli hieman erilainen. Tähän ohjelmaan on jäänyt vähän jotain turhaa sotkua. En jaksanut siivota koodia: pääasia mulle taas, että se toimii.
Program Numeropeli; uses Crt; CONST tyhja=-32768; tahti=32767; maxind=9; TYPE indtype = 1..maxind; solmuos = ^solmu; solmu = RECORD arvo:Integer; ind :Indtype; oikveli:solmuos; END; puu = ^puutaso; puutaso = RECORD ylataso:puu; siirrot:solmuos; parasarvo:INTEGER; END; VAR quit : BOOLEAN; VAR kayttamattomat: puu; {Solmuja ei tuhota ja luoda yhtenään, vaan laitetaan ja} {otetaan kayttamattomat listaan} kayttamattsolmut: solmuos; koneenlaatat, pelaajanlaatat, taso, maxtaso, koneyht,pelayht : Integer; koneenvuoro, pelipaattyi, konesarakkeittain: BOOLEAN; tahtirivi, tahtisara, edtahtirivi, edtahtisara : indtype; alkutilanne : ARRAY [1..maxind,1..maxind] OF INTEGER; pelilauta : ARRAY [1..maxind,1..maxind] OF INTEGER; odotus : char; ind : Integer; oletus : BOOLEAN; FUNCTION TASOPARILLINEN:BOOLEAN; BEGIN TASOPARILLINEN:=(taso MOD 2=0) END; FUNCTION SARAKESIIRTO:BOOLEAN; BEGIN IF TASOPARILLINEN THEN SARAKESIIRTO:=konesarakkeittain ELSE SARAKESIIRTO:=NOT(konesarakkeittain) END; PROCEDURE MUUTALAUTATILANNE(siirto:indtype); BEGIN pelilauta[tahtirivi,tahtisara]:=tyhja; IF SARAKESIIRTO THEN BEGIN pelilauta[siirto,tahtisara]:=tahti; tahtirivi:=siirto; END ELSE BEGIN pelilauta[tahtirivi,siirto]:=tahti; tahtisara:=siirto; END; END; PROCEDURE KONESIIRTAA; VAR alkuind:indtype; pelipuu, uusitaso:puu; pojat:solmuos; parasind:indtype; PROCEDURE PERULAUTATILANNE(siirto:indtype); BEGIN pelilauta[tahtirivi,tahtisara]:=alkutilanne[tahtirivi,tahtisara]; IF SARAKESIIRTO THEN BEGIN pelilauta[siirto,tahtisara]:=tahti; tahtirivi:=siirto; END ELSE BEGIN pelilauta[tahtirivi,siirto]:=tahti; tahtisara:=siirto; END; END; PROCEDURE TUHOATASOSOLMU(VAR os:puu); BEGIN os^.ylataso:=kayttamattomat; kayttamattomat:=os; END; PROCEDURE UUSITASOSOLMU(VAR os:puu); BEGIN IF kayttamattomat=NIL THEN NEW(os) ELSE BEGIN os:=kayttamattomat; kayttamattomat:=kayttamattomat^.ylataso; os^.ylataso:=NIL; END; END; PROCEDURE UUSISOLMU(VAR os:solmuos); BEGIN IF kayttamattsolmut=NIL THEN NEW(os) ELSE BEGIN os:=kayttamattsolmut; kayttamattsolmut:=kayttamattsolmut^.oikveli; os^.oikveli:=NIL; END; END; PROCEDURE TUHOASOLMU(VAR os:solmuos); BEGIN os^.oikveli:=kayttamattsolmut; kayttamattsolmut:=os; END; PROCEDURE RAKENNASEURAAVATASO; VAR solmu,vika:solmuos; ind:indtype; laattaarvo:INTEGER; BEGIN pojat:=NIL; taso:=taso+1; FOR ind:=1 TO maxind DO BEGIN IF SARAKESIIRTO THEN laattaarvo:=pelilauta[ind,tahtisara] ELSE laattaarvo:=pelilauta[tahtirivi,ind]; IF laattaarvo<>tyhja THEN IF laattaarvo<>tahti THEN BEGIN UUSISOLMU(solmu); IF TASOPARILLINEN THEN solmu^.arvo:=pelipuu^.siirrot^.arvo+laattaarvo ELSE solmu^.arvo:=pelipuu^.siirrot^.arvo-laattaarvo; solmu^.ind:=ind; IF pojat=NIL THEN { Vasta ensimmäinen solmu jonoon } BEGIN pojat:=solmu; vika:=pojat; END ELSE BEGIN vika^.oikveli:=solmu; solmu^.oikveli:=NIL; vika:=solmu; END; END; END; IF pojat=NIL THEN { Ei päästy enää jatkamaan eli päädyttiin } BEGIN { lehtisolmuun, jonka arvo kerrotaan kymme- } taso:=taso-1;{ nellä. } pelipuu^.siirrot^.arvo:=pelipuu^.siirrot^.arvo*10; END ELSE BEGIN UUSITASOSOLMU(uusitaso); uusitaso^.ylataso:=pelipuu; uusitaso^.siirrot:=pojat; IF TASOPARILLINEN THEN uusitaso^.parasarvo:=-MAXINT ELSE uusitaso^.parasarvo:=MAXINT; MUUTALAUTATILANNE(uusitaso^.siirrot^.ind); pelipuu:=uusitaso; END; END; PROCEDURE PURAPUUTA; PROCEDURE POISTASOLMU; VAR apusolmu:solmuos; arvo:Integer; BEGIN arvo:=pelipuu^.siirrot^.arvo; IF TASOPARILLINEN THEN BEGIN IF arvo>pelipuu^.parasarvo THEN BEGIN pelipuu^.parasarvo:=arvo; IF taso=2 THEN parasind:=pelipuu^.siirrot^.ind; END; END ELSE IF arvo<pelipuu^.parasarvo THEN pelipuu^.parasarvo:=arvo; apusolmu:=pelipuu^.siirrot; pelipuu^.siirrot:=pelipuu^.siirrot^.oikveli; TUHOASOLMU(apusolmu); END; PROCEDURE NOUSETASOLTAYLOS; VAR apu:puu; BEGIN IF taso>1 THEN BEGIN pelipuu^.ylataso^.siirrot^.arvo:=pelipuu^.parasarvo; apu:=pelipuu; pelipuu:=pelipuu^.ylataso; TUHOATASOSOLMU(apu); taso:=taso-1; POISTASOLMU; { Poistetaan myos isasolmu } IF taso>1 THEN IF taso=2 THEN PERULAUTATILANNE(alkuind) ELSE PERULAUTATILANNE(pelipuu^.ylataso^.ylataso^.siirrot^.ind); END; END; BEGIN { purapuuta } IF taso=maxtaso THEN BEGIN WHILE pelipuu^.siirrot<>NIL DO POISTASOLMU; PERULAUTATILANNE(pelipuu^.ylataso^.ylataso^.siirrot^.ind); NOUSETASOLTAYLOS; IF pelipuu^.siirrot<>NIL THEN MUUTALAUTATILANNE(pelipuu^.siirrot^.ind); pojat:=pelipuu^.siirrot; END ELSE BEGIN IF pelipuu^.siirrot=NIL THEN WHILE (pelipuu^.siirrot=NIL)AND(taso>1) DO NOUSETASOLTAYLOS ELSE BEGIN POISTASOLMU; IF taso=2 THEN PERULAUTATILANNE(alkuind) ELSE PERULAUTATILANNE(pelipuu^.ylataso^.ylataso^.siirrot^.ind); END; pojat:=pelipuu^.siirrot; IF taso<>1 THEN IF pojat<>NIL THEN MUUTALAUTATILANNE(pelipuu^.siirrot^.ind); END; END; BEGIN { konesiirtaa } write('Odota. Mietin.'); taso:=1; IF konesarakkeittain THEN alkuind:=tahtirivi ELSE alkuind:=tahtisara; UUSITASOSOLMU(pelipuu); UUSISOLMU(pojat); pojat^.arvo:=koneenlaatat-pelaajanlaatat; IF konesarakkeittain THEN pojat^.ind:=tahtisara ELSE pojat^.ind:=tahtirivi; pelipuu^.siirrot:=pojat; pelipuu^.parasarvo:=MAXINT; RAKENNASEURAAVATASO; IF pojat<>NIL THEN BEGIN IF pojat^.oikveli=NIL THEN parasind:=pojat^.ind ELSE WHILE taso>1 DO BEGIN WHILE (pojat<>NIL)AND(taso<maxtaso) DO RAKENNASEURAAVATASO; PURAPUUTA; END; {poistatähti} pelilauta[edtahtirivi,edtahtisara]:=tyhja; TUHOATASOSOLMU(pelipuu); taso:=0; MUUTALAUTATILANNE(parasind); pelilauta[tahtirivi,tahtisara]:=tahti; koneenlaatat:=koneenlaatat+alkutilanne[tahtirivi,tahtisara]; koneenvuoro:=FALSE; tahtisara:= parasind; END ELSE BEGIN pelipaattyi:=TRUE; koneyht:=koneyht-alkutilanne[tahtirivi,tahtisara];{bugin korjaus} END; koneyht:=koneyht+alkutilanne[tahtirivi,tahtisara]; END; FUNCTION laattakelpaa(ri,sa:indtype):BOOLEAN; BEGIN laattakelpaa:=(konesarakkeittain AND (ri=tahtirivi)) OR (NOT(konesarakkeittain) AND (sa=tahtisara)); END; PROCEDURE PIIRRA; Var r,s:Integer; BEGIN ClrScr; WRITELN('Ota numero joltain riviltä, siltä sarakkeelta millä #### laatta on.'); WRITELN('Paina numeronäppäimiä 1-9 valitaksesi rivin. Kone valitsee sitten numeron joltain'); WRITELN('sarakkeelta.'); WRITE(' ':7); FOR s:=1 TO maxind DO BEGIN write(s:6); END; WRITELN(); WRITE(' ':7); FOR s:=1 TO maxind DO BEGIN write('______'); END; WRITELN(); FOR r:=1 TO maxind DO BEGIN WRITE (r:5,'!':2); FOR s:=1 TO maxind DO BEGIN CASE pelilauta[r,s] of tahti: BEGIN WRITE('####':6); END; tyhja: WRITE (' ':6); ELSE WRITE(pelilauta[r,s]:6); END; END; WRITELN();WRITELN('!':7); END; WRITELN('Koneen pisteet: ', koneyht, ' Pelaajan pisteet: ',pelayht); END; PROCEDURE PELIVALMISTELUT; VAR ri,sa:indtype; PROCEDURE ASETAALKUTILANNE; VAR ri,sa:Integer; BEGIN FOR ri:=1 TO maxind DO FOR sa:=1 TO maxind DO BEGIN alkutilanne[ri,sa]:=pelilauta[ri,sa]; IF alkutilanne[ri,sa]=tahti THEN BEGIN tahtirivi:=ri; tahtisara:=sa; END; END; END; PROCEDURE ASETAPELITILANNE; VAR ri,sa:Integer; BEGIN FOR ri:=1 TO maxind DO FOR sa:=1 TO maxind DO BEGIN pelilauta[ri,sa]:=alkutilanne[ri,sa]; IF pelilauta[ri,sa]=tahti THEN BEGIN tahtirivi:=ri; tahtisara:=sa; END; END; END; PROCEDURE ARVOLAATATJAPAIKAT; VAR r,s :indtype; ran :Integer; BEGIN Randomize; FOR r:=1 TO maxind DO FOR s:=1 TO maxind DO BEGIN ran:=Random(30)-15; pelilauta[r,s]:=ran; alkutilanne[r,s]:=ran; END; tahtirivi:=Random(8)+1; tahtisara:=Random(8)+1; pelilauta[tahtirivi,tahtisara]:=tahti; alkutilanne[tahtirivi,tahtisara]:=tahti; END; BEGIN { pelivalmistelut } pelipaattyi:=FALSE; FOR ri:=1 TO maxind DO FOR sa:=1 TO maxind DO pelilauta[ri,sa]:=tyhja; koneenlaatat:=0; pelaajanlaatat:=0; ARVOLAATATJAPAIKAT; koneyht:=0; pelayht:=0; END; PROCEDURE LUENAPPAINPAINALLUS; VAR laatta :INTEGER; ch : char; nappainok: BOOLEAN; ri,sa:Indtype; PROCEDURE TARKISTAVIELA; BEGIN laatta:=pelilauta[ri,sa]; CASE laatta OF tahti : writeln('Et voi valita tähteä!'); tyhja : writeln('Et voi valita tyhjää kohtaa!'); ELSE BEGIN pelaajanlaatat:=pelaajanlaatat+pelilauta[ri,sa]; pelilauta[tahtirivi,tahtisara]:=tyhja; edtahtirivi:=tahtirivi; edtahtisara:=tahtisara; tahtirivi:=ri; {tahtisara:=sa;} pelilauta[ri,sa]:=tahti; koneenvuoro:=TRUE; nappainok:=TRUE; pelayht:=pelayht+laatta; END; END; END; BEGIN { LUENAPPAINPAINALLUS } sa:=tahtisara; nappainok:=FALSE; REPEAT ch:=ReadKey; CASE (ch) of #49 : ri:=1; #50 : ri:=2; #51 : ri:=3; #52 : ri:=4; #53 : ri:=5; #54 : ri:=6; #55 : ri:=7; #56 : ri:=8; #57 : ri:=9; #27 : quit:=true; ELSE WRITELN('Paina numeronäppäimiä 1 - 9.'); END; TARKISTAVIELA; UNTIL nappainok; END; PROCEDURE ASETAALKUTILANNE; VAR ri,sa:Integer; BEGIN FOR ri:=1 TO maxind DO FOR sa:=1 TO maxind DO BEGIN alkutilanne[ri,sa]:=pelilauta[ri,sa]; IF alkutilanne[ri,sa]=tahti THEN BEGIN tahtirivi:=ri; tahtisara:=sa; END; END; END; PROCEDURE ALKUVALMISTELUT; VAR ri,sa:Integer; BEGIN koneenvuoro:=FALSE; pelipaattyi:=FALSE; konesarakkeittain:=FALSE; maxtaso:=10; kayttamattomat:=NIL; kayttamattsolmut:=NIL; edtahtirivi:=tahtirivi; edtahtisara:=tahtisara; FOR ri:=1 TO maxind DO FOR sa:=1 TO maxind DO BEGIN alkutilanne[ri,sa]:=tyhja; pelilauta[ri,sa]:=tyhja; END; END; BEGIN {pääohjelma} ALKUVALMISTELUT; REPEAT PELIVALMISTELUT; PIIRRA; REPEAT IF koneenvuoro THEN BEGIN KONESIIRTAA; {tutkitaan onko koneen siirron jälkeen enää mahdollista valita} oletus:= true; {oletetaan, että peli päättyy} ind:=1; REPEAT BEGIN IF ((pelilauta[ind,tahtisara] >= -15) AND (pelilauta[ind,tahtisara] <= 15)) THEN BEGIN oletus:=FALSE; WRITELN (oletus); WRITE (pelilauta[ind,tahtisara], ' '); END; ind:=ind+1; END; UNTIL (ind > maxind) OR (oletus = FALSE); IF oletus=TRUE THEN pelipaattyi:=TRUE; END; piirra; IF (NOT koneenvuoro) THEN BEGIN IF NOT pelipaattyi THEN BEGIN LUENAPPAINPAINALLUS; END; END; UNTIL pelipaattyi OR quit; IF pelipaattyi THEN BEGIN IF (pelaajanlaatat>koneenlaatat) THEN BEGIN WRITELN('Onneksi olkoon! Voitit!!'); END; IF (pelaajanlaatat=koneenlaatat) THEN BEGIN WRITELN('Wau! Tasapeli. Se ei kuitenkaan riitä 10 euron saamiseen :('); END; IF (pelaajanlaatat<koneenlaatat) THEN BEGIN writeln('Hah hah. Hävisit taas!'); END; WRITELN('Paina jotain näppäintä aloittaaksesi uuden pelin'); odotus:=ReadKey; END; UNTIL quit; END.
Onpa kiva, että joku koodaa perinteisellä Pascalilla.
Linuxin käyttäjille tiedoksi, että ohjelma toimii wineconsole
-komennolla mutta ei pelkällä wine
-komennolla.
Huijasin ja tein tekoälyn, joka laskee 10 siirtoa. Tein JavaScriptilla, ja siitä tuli ihan hävyttömän hidas tuohon ohjelmaan verrattuna, mutta voitin kuitenkin. Ohjelmasi ilmeisesti aavisti huijauksen, koska ohjelman mukaan voitin vain 5 euroa.
Peli on vaikeampi kuin voisi kuvitella: hakusyvyyden lisäys antaa nopeasti parempia tuloksia ja näköjään hyödyt voivat tulla ilmi yllättävän monen vuoron jälkeen.
Oot kyllä taitava! Kadehdin. Siis kun osasit Javascriptilla ohjelmoida ja nopeasti. Itselläni ei ole hajuakaan kuinka Javascriptillä ohjelmoitaisiin dynaamisia muuttujia, joita pelipuun rakentamiseen tarvitaan. Joo, tuo 5 euroa oli "bugi", mikä jäi ohjelmaan :)
Pascal oli aikoinaan opetuskieli kun opiskelin HY:ssa tietojenkäsittelyoppia - en ole valmistunut :( Opinnot jäivät kesken. En itsekään ole pitkään aikaan Pascalilla ohjelmoinnut, mutta kiva oli verestää muistoja.
Ja peli on tosiaan vaikea. Ohjelma pyrkii tietysti ajamaan pelaajan "nurkkaan", missä se ei pysty valitsemaan kuin pieniä lukuja ja onnistuu siinä hyvin.
Aihe on jo aika vanha, joten et voi enää vastata siihen.