Mõttemeister
Mõttemeister (Mastermind) on mälumäng, kus üks mängijatest (siin: inimene)
mõtleb välja neljast erinevast värvist koosneva salajase koodi ja teine mängija (siin: arvuti) peab koodi
ära arvama. Koodi arvamiseks pakub arvaja mingi koodi ja küsib koodi valdavalt mängijalt:
Mitu värvi on pakutud koodis täpselt õigel kohal ?
Mitu õiget värvi pakutud koodis üldse esineb (kuigi võibolla valel kohal).
Esimese küsimuse vastust (number 1..4) nimetatakse pakutud koodi sõnnide arvuks, teise küsimuse vastust - lehmade arvuks; mäng jätkub, kuni
arvaja pakub välja õige koodi (s.t. sellise, kus sõnnide arv on 4).
Iga pakutud koodi vastustega (Sõnnid/Lehmad) saab
arvaja lisainformatsiooni salajase koodi kohta.
Arvaja pakutud koodile saadud vastused (Sõnnid/Lehmad) määravad piirkonna, kus otsitav kood
asub:
kui arvaja pakkus näiteks koodi [sinine, punane, valge, must] ja sai vastuseks Sõnnid=1, Lehmad=3,
siis
on järgmisena mõtet pakkuda vaid koode, mille võrdlemisel koodiga [sinine, punane, valge, must] saadakse
vastuseks Sõnnid=1, Lehmad=3 (sest salajane kood asub selles piirkonnas). Uue pakkumise tegemisel peab
arvesse võtma kõigi seni tehtud pakkumistele saadud vastuseid, s.t. pakkumine peab asuma kõigi
seni tehtud pakkumiste Sõnnid/Lehmad määratud piirkondes.
Inimene (enamasti) ei suuda kogu saadud infot iga järgmise arvamise tegemisel arvesse võtta ja
mäng võib kesta tunduvalt kauem, kuid Prologi jaoks on kogu senise info arvestamine ja optimaalse
pakkumise tegemine lihtne.
Et neid arvesse võtta, salvestab Prolog kõik tehtud pakkumised nimistus
Pakkumised, mille elementideks on kolmeelemendilised alamnimistud: pakkumine (neljast värvi nimest koosnev nimistu)
ja sellele saadud
vastused (Sõnnid/Lehmad). Predikaat lahenda otsib uue koodi Kood ja laseb siis predikaatidel sobib, arvestab kontrollida, kas
see kood asub kõigi seniste pakkumiste vastustena saadud alampiirkondades.
Kui predikaat arvestab leiab, et kood Kood ei asu mingi pakkumise poolt tehtud piirkonnas, toimub tagurdamine (backtracking) ja predikaat
kood otsib uue koodi Kood; kui sobiv kood on leitud, küsib predikaat kysi mängijalt
selle Sõnnid/Lehmad väärtused; kui Sõnnid=4, on mäng läbi, vastasel korral lisatakse viimasena tehtud pakkumine koos oma Sõnnid/Lehmad väärtustega nimistusse Pakkumised ja mäng jätkub:
lahenda(Pakkumised):-
kood(Kood),
sobivad(Kood,Pakkumised),
kysi(Kood,Sõnnid,Lehmad),
jatka([[Kood,Sõnnid,Lehmad]|Pakkumised]).
arvestab(Kood,[Kood1,Sõnnid,Lehmad]):-
yhiseid(Kood,Kood1,Lehmad1),
Lehmad = Lehmad1,
täpselt(Kood,Kood1,Sõnnid1),
Sõnnid = Sõnnid1,!.
sobivad(_,[]):-!.
sobivad(Kood,[Pakkumine|Pakkumised]):-
arvestab(Kood,Pakkumine),!,
sobivad(Kood,Pakkumised).
kysi(Kood,Sõnnid,Lehmad):-
write('Pakun: '),
write(Kood),
nl,
write('Mitu värvi on täpselt õigel kohal ? '),
read(Sõnnid),
write('Mitu õiget värvi esineb ? '),
read(Lehmad).
jatka([[_,4,_]|Pakkumised]):- !,
length(Pakkumised,N),
write('Tegin '),
write(N),
write(' vigast pakkumist '),
nl,
lopeta.
jatka(Pakkumised):-
lahenda(Pakkumised).
Mängu alustab ja lõpetab kasutajaliidese predikaat mäng:
mäng:-
nl,
write(' Ma olen Koodarvamise meister! '),
nl,
write('Mõtle välja nelja värvi kood (kõik värvid erinevad).'),
nl,
write('Kasutada võib järgnevaid värve: '),
nl,
saab_kasutada(Värv),
write(Värv),
write(' '),
fail.
mäng:-
nl,
write('Kas oled valmis? (j/e)'),nl,
read(V),
((V = 'j' , ! , lahenda([])) ; lopeta).
lopeta :-
write('Nägemist! Kui tahad veel kord proovida, kirjuta: mäng.'),
nl.
Abipredikaadid yhiseid ja täpselt leiavad kahe koodi ühiste elementide arvu (Lehmad) ja elementide arvu,
mis mõlemates koodides on samasugused (Sõnnid):
yhiseid([Värv|Värvid],Kood,N):-
member(Värv,Kood),!,
yhiseid(Värvid,Kood,N1),
N is N1 + 1,!.
yhiseid([Värv|Värvid],Kood,N):-
not(member(Värv,Kood)),
yhiseid(Värvid,Kood,N),!.
yhiseid([],_,0).
täpselt([Värv|Värvid],[Värv|Värvid1],N):-
täpselt(Värvid,Värvid1,N1),!,
N is N1 + 1.
täpselt([Värv|Värvid],[Värv1|Värvid1],N):-
not(Värv = Värv1),!,
täpselt(Värvid,Värvid1,N),!.
täpselt([],[],0)
Ülesandeid:
1.
Labürindis taga-ajamise mäng kirjeldab, kuidas Theseus (T) püüdis
labürindis Minotaurust (M). Mängu algul on Theseus mingis labürindi ruumis ja Minotaurus -
teises ruumis. Mängijad siirduvad kordamööda ruumist teise (samasse ruumi ei või käigul olev mängija
jääda). Theseus peab Minotauruse kinni püüdma (mõlemad mängijad on samas ruumis), aga
Minotaurus peab siirduma ruumi, kus Theseus oli mängu algul; kui ta suudab seda, siis on Minotaurus võitnud.
Näiteks
kõrvalolevatel joonistel esimeses võidab alati Theseus (ükskõik kumma käigul olles), kuid teises võidab Theseus (kindlasti)
vaid siis, kui Minotaurus on käigul.
Õpeta Prolog mängima Theseusena; võitmiseks peab Theseus kogu aeg kontrollima ruumide vahelist kaugust
(iga käigu ajal püüdma
lühendada enda ja Minotauruse vahemaad) ja kaugusi omast ja Minotauruse ruumist
enda stardiruumini - Minotaurus ei tohi jõuda stardiruumile lähemale kui ta ise on.
Labürindi kirjeldus
peaks olema eraldi predikaadis ja failis ja laaditakse programmikäsuga
consult('labyrint.pro'); testimiseks võib kasutada
näiteks kõrvalolevat labürinti -
kui Theseus siin ei suuda Minotaurust kuue esimese käiguga kätte saada, võidab Minotaurus.
lähemale kui ta ise on - muidu Minotaurus jõuaks lõpetada.
Küsimused, probleemid:
©2004
Jaak Henno