Navodila (pdf)

Tekmovanje programov za igro Tarok v dvoje
1
Osnovni podatki
Med 16. in 22. januarjem 2015 bo potekalo tekmovanje programov za igro Tarok v
dvoje. Za vas smo pripravili grafično in tekstovno ogrodje, vaša naloga pa je napisati
t.i. stroj — javanski razred za izbiro potez. Če želite sodelovati, oddajte izvorno
kodo vašega stroja do četrtka, 15. januarja 2015, do 23:55 na spletno učilnico. V
nadaljevanju bomo podrobneje opisali igro in vašo nalogo.
2
Tarok v dvoje
Tarok, priljubljena igra s kartami, se praviloma igra v troje ali četvero, obstajajo pa
tudi različice za dva igralca. Pri različici, v kateri bomo tekmovali, smo odstranili
licitacijo in napovedi. Pravila so tako bistveno enostavnejša, kljub temu pa smo
prepričani, da naloga zaradi tega ne bo nič manj vznemirljiva.
Tarok igramo s kompletom 54 kart, prikazanih na sliki 1. Najprej se seznanimo
s terminologijo:
• Komplet kart vsebuje 22 tarokov in 32 barv.
• Množica tarokov je sestavljena iz škisa (karta s podobo pavlihe) in 21 kart,
označenih z rimskimi številkami.
• Škis je najvišji tarok. (Kaj pomeni višina karte, bo jasno kasneje.)
• Tarok XXI (drugi najvišji) se imenuje mond, tarok I (najnižji) pa pagat.
• Škis, mond in pagat tvorijo trulo.
• Vsaka barva (srce, pik, karo in križ) ima osem predstavnikov, ki so po padajoči
višini urejeni takole:
– srce in karo: kralj, dama, kaval, poba, enica, dvojka, trojka, štirica.
– pik in križ: kralj, dama, kaval, poba, desetica, devetica, osmica, sedmica.
Bodite pozorni, da je pri srcih in karah enica višja od štirice.
• Po štirje najnižji predstavniki posameznih barv se imenujejo platelci.
Vsaka karta ima svojo vrednost:
• Člani trule (škis, mond in pagat) in kralji so vredni po 4 13 točke.
• Dame so vredne po 3 13 točke.
• Kavali so vredni po 2 13 točke.
1
Slika 1: Komplet (Piatnikovih) kart za tarok.
2
• Pobi so vredni po 1 13 točke.
• Platelci in »navadni« taroki so vredni po
1
3
točke.
• Vsota vrednosti vseh kart skupaj tako znaša 70 točk.
Partija taroka poteka takole:
• Na začetku oba igralca prejmeta po 11 kart v roke. Preostalih 32 kart se
razdeli v 8 kupčkov po štiri karte. Vsak od igralcev prejme po 4 kupčke. Karte
v kupčkih so obrnjene s podobo navzdol.
• Igralca obrneta zgornjo karto v vsakem kupčku. Če igralec obrne taroka ali
kralja, ga vzame v roke in obrne naslednjo karto. Če je tudi naslednja karta
tarok ali kralj, jo igralec vzame v roke in obrne naslednjo (in tako naprej).
• Igralec, ki prične partijo, vrže eno od svojih kart na mizo. Izbere lahko poljubno
karto med kartami v roki in na vrhovih kupčkov. Drugi igralec prav tako odvrže
eno od svojih kart v roki ali na vrhovih kupčkov, vendar mora upoštevati sledeča
pravila:
– Če je prvi igralec odvrgel enega od tarokov, mora enega od tarokov odvreči
tudi drugi igralec. Če ga nima, lahko odvrže katerokoli karto.
– Če je prvi igralec odvrgel karto neke barve, mora karto iste barve odvreči
tudi drugi igralec. Če je nima, mora odvreči taroka. Če nima niti taroka,
lahko odvrže katerokoli karto.
• Karti, ki sta jo odvrgla igralca, tvorita štih (formalno vzetek ). Po odigranem
štihu se po sledečih pravilih določi, kdo štih pobere (dobi ):
– višji tarok pobere nižjega;
– višja karta neke barve pobere nižjo karto iste barve;
– tarok pobere barvo;
– če je igralec, ki je pričel štih, odvrgel barvo, drugi pa karto neke druge
barve (ker nima niti karte iste barve niti taroka), potem štih pobere igralec, ki je pričel štih.
• Če je kateri od igralcev odvrgel karto s katerega od kupčkov, obrne vrhnjo karto
na tistem kupčku; če dobi kralja ali taroka, ga vzame v roke, obrne naslednjo
karto s kupčka itd.
• Dobitnik štiha prične naslednji štih. Partija poteka, dokler igralca ne porabita
vseh svojih kart. Skupaj tako odigrata 27 štihov.
Na koncu partije oba igralca seštejeta vrednosti kart v svojih pobranih štihih in
vsoto zaokrožita na najbližje celo število. Točke, ki jih posamezen igralec prejme za
odigrano partijo, se izračunajo na sledeči način:
• Igralec, ki je v pobranih štihih zbral najmanj 35 točk, prejme 2 · (v − 35) točk,
kjer je v skupna vrednost kart v njegovih pobranih štihih. Njegov nasprotnik
iz tega naslova prejme 0 točk.
• Če je kateri od igralcev pobral celotno trulo (pobral v štihih, ne nujno imel v
svojih kartah!), prejme dodatnih 10 točk.
3
• Če je kateri od igralcev pobral vse štiri kralje, prav tako prejme dodatnih 10
točk.
• Izguba monda (prevzem s škisom, t.i. mondfang) se kaznuje, in sicer tako, da
nasprotnik prejme dodatnih 21 točk.
• Če kateri od igralcev zadnji (sedemindvajseti) štih pobere s pagatom, prejme
25 točk (pagat ultimo). Če igralec izgubi pagata v zadnjem štihu, dobi njegov
nasprotnik 25 točk. V predhodnih štihih se izguba pagata ne kaznuje.
V nasprotju z običajnim točkovanjem pri taroku so dobljene točke vedno pozitivne.
Gornjih pravil se boste hitro naučili s pomočjo grafičnega ogrodja, ki omogoča tudi
igro človeka proti človeku.
3
Vaša naloga
Če želite sodelovati, pripravite in oddajte razred, ki implementira vmesnik Stroj v
paketu skupno (podimeniku src/skupno). Pripravite razred z imenom Stroj_Ime,
kjer je Ime niz dolžine do 25 znakov, sestavljen iz črk angleške abecede, števk in/ali
podčrtajev (bodite »inovativni«!). Razred postavite v paket sXXXXXXXX , kjer je
XXXXXXXX vaša vpisna številka. To pomeni, da morate datoteko Stroj_Ime.java
postaviti v podimenik src/sXXXXXXXX , povsem na začetek datoteke (torej še pred
stavke import) pa zapisati
package sXXXXXXXX ;
Če je vaša rešitev sestavljena iz več samostojnih razredov, morate seveda vsakega od
njih postaviti v paket in podimenik src/sXXXXXXXX . Imena drugih razredov naj
se ne pričnejo z znaki Stroj_!
Ker bo vaš razred Stroj_Ime implementacija vmesnika Stroj, morate v njem
implementirati sledeče metode:
• public void novaPartija(Mnozica mojaRoka, Mnozica mojDvig,
Kupcki mojiKupcki, Mnozica nasprDvig, Kupcki nasprKupcki)
Ta metoda se pokliče ob pričetku partije. Parameter mojaRoka podaja množico
kart, ki jih na začetku partije v roke dobi vaš stroj (objekt vaše implementacije
razreda Stroj). Parameter mojDvig podaja množico kraljev in tarokov, ki jih
na začetku partije s kupčkov dvigne vaš stroj. Parameter mojiKupcki podaja
stanje kupčkov, s katerimi razpolaga vaš stroj (brez že dvignjenih kraljev in
tarokov). Parametra nasprDvig in nasprKupcki podajata dvig s kupčkov in
trenutno stanje kupčkov za nasprotnika vašega stroja. Nasprotnik vašega stroja
je lahko človek, drug stroj ali pa celo nek drug objekt vašega stroja, vendar pa
to s stališča vašega stroja ni pomembno.
• public Karta vrzi(long preostaliCas)
Ta metoda se pokliče, ko je vaš stroj prvi na potezi v trenutnem štihu. Metoda
mora v največ preostaliCas milisekundah vrniti karto, ki jo želi odvreči vaš
stroj. V primeru prekoračitve časa ali neveljavne izbire (če vaš stroj odvrže
karto, ki je nima niti v rokah niti na vrhu kupčkov), se partija takoj zaključi,
nasprotnik pa prejme 200 točk.
4
• public Karta odgovori(Karta naspr, long preostaliCas)
Ta metoda se pokliče, ko je vaš stroj drugi na potezi v trenutnem štihu. Metoda mora v največ preostaliCas milisekundah vrniti karto, ki jo želi odvreči
vaš stroj. Parameter naspr podaja karto, ki jo je na mizo odvrgel nasprotnik.
V primeru prekoračitve časa ali neveljavne izbire se partija takoj zaključi, nasprotnik pa prejme 200 točk.
• public void sprejmiOdgovor(Karta naspr)
Ta metoda se pokliče, ko je (bil) vaš stroj prvi na potezi v trenutnem štihu,
nasprotnik pa je ravnokar odgovoril s karto naspr.
• public void poStihu(Mnozica mojDvig, Kupcki mojiKupcki,
Mnozica nasprDvig, Kupcki nasprKupcki)
Ta metoda se pokliče po zaključku štiha. Parameter mojDvig podaja množico
kraljev in tarokov, ki jih je vaš stroj dvignil s kupčkov po zaključku štiha. Ta
množica bo neprazna kvečjemu v primeru, če je stroj v pravkar odigranem
štihu odvrgel karto z enega od kupčkov. Parameter mojiKupcki podaja stanje
kupčkov vašega stroja po morebitnem dvigu kraljev in tarokov. Parametra
nasprDvig in nasprKupcki podajata morebiten dvig s kupčkov in novo stanje
kupčkov za nasprotnika.
• public void rezultat(Mnozica mojiStihi, int mondfang,
int pagatUltimo, int mojeTocke, int nasprTocke)
Ta metoda se pokliče ob vsakem zaključku partije. Parameter mojiStihi podaja množico kart, ki jih je vaš stroj pobral v štihih. Parameter mondfang (oz.
pagatUltimo) vsebuje vrednost 1, če je vaš stroj nasprotniku odvzel monda
(oziroma v zadnjem štihu pobral s pagatom ali dobil nasprotnikovega pagata),
vrednost −1, če je to uspelo nasprotniku, oziroma vrednost 0, če se to ni zgodilo. Parametra mojeTocke in nasprTocke podajata skupno število točk, ki
jih je za pravkar odigrano partijo prejel vaš stroj oziroma njegov nasprotnik.
Metoda rezultat vam bo morda koristila pri analizah, sicer pa jo lahko brez
škode implementirate s praznim telesom.
Nekateri parametri navedenih metod so objekti tipa Karta, Mnozica in Kupcki.
Vsi trije razredi se nahajajo v paketu skupno (podimeniku src/skupno). Objekt
razreda Karta predstavlja posamezno karto, objekt razreda Mnozica predstavlja
poljubno množico kart, objekt razreda Kupcki pa zaporedje kupčkov posameznega
igralca. Izvorna koda razredov je dokumentirana, zato bomo pokazali le nekaj primerov uporabe teh razredov:
Karta mond = Karta.XXI;
Karta srcevaDama = Karta.sQ;
System.out.println(mond);
System.out.println(srcevaDama);
System.out.println(mond.vrniBarvo());
System.out.println(srcevaDama.vrniBarvo());
System.out.println(mond.jeTarok());
5
//
//
//
//
//
XXI
sQ
0
1
true
System.out.println(mond.velikost());
System.out.println(srcevaDama.velikost());
System.out.println(mond.vrednost());
System.out.println(srcevaDama.vrednost());
System.out.println(srcevaDama.pobere(mond));
//
//
//
//
//
21
7
5 (zaokrožitev navzgor)
4
false
// Množice so vedno urejene: najprej taroki od škisa do pagata,
// nato srca, piki, kare in križi; vsaka barva je urejena od kralja do
// najnižjega platelca.
Mnozica taroki = Mnozica.TAROKI;
Mnozica trula = Mnozica.TRULA;
System.out.println(trula);
// [skis, XXI, I]
Mnozica tarokiRazenTrule = taroki.razlika(trula);
Mnozica vsePrazne = tarokiRazenTrule.unija(Mnozica.VSI_PLATELCI);
System.out.println(vsePrazne);
// [XX, XIX, ..., II, s1, ..., r7]
Mnozica mojaSrecna = new Mnozica(Karta.pQ, Karta.r7, Karta.XIII);
System.out.println(mojaSrecna);
// [XIII, pQ, r7]
System.out.println(mojaSrecna.vsebuje(Karta.SKIS));
// false
System.out.println(mojaSrecna.jePodmnozicaOd(taroki)); // false
System.out.println(mojaSrecna.taroki());
// [XIII]
System.out.println(mojaSrecna.barve(1));
// []
System.out.println(mojaSrecna.barve(4));
// [r7]
System.out.println(mojaSrecna.vrednost());
// 4
System.out.println(mojaSrecna.prvaKarta());
// XIII
Random random = new Random(1000);
System.out.println(mojaSrecna.nakljucnaKarta(random));
System.out.println(mojaSrecna.nakljucnaPodmnozica(2, random));
for (Karta k: mojaSrecna) {
// izpiše XIII, pQ, r7
System.out.println(k);
}
Karta[] karte = mojaSrecna.karte();
System.out.println(Arrays.toString(karte)); // [XIII, pQ, r7]
// Recimo, da objekt kupcki tipa Kupcki predstavlja kupčke
// [[pC, XI, r9, kK], [], [k3, XX], [sQ, I, III]]
// (prva karta v vsaki podtabeli je vrh kupčka).
System.out.println(kupcki.steviloKart(0));
// 4
System.out.println(kupcki.steviloKart(1));
// 0
System.out.println(kupcki.vrh(0));
// pC
System.out.println(kupcki.vrh(1));
// null
System.out.println(kupcki.vrhovi()); // [sQ, pC, k3] (urejenost množice)!
System.out.println(kupcki.poisci(Karta.k3));
// 2
System.out.println(kupcki.poisci(Karta.I));
// -1 (gleda samo po vrhovih)
System.out.println(kupcki.steviloNepraznih()); // 3
Celotna koda zgornjega primera je zapisana v datoteki TestSkupnihRazredov.java
v podimeniku src/test.
6
Nekatere metode v razredu Kupcki vržejo izjemo, če jih pokliče stroj, saj omogočajo prepovedan vpogled v notranjost kupčkov. Te metode so posebej označene.
Razreda Mnozica in Karta pa lahko povsem svobodno uporabljate.
4
Razred Stroj_Nakljucko
Študent Fakultete za naključne študije Naključko Randomè z vpisno številko 12345678
je napisal razred Stroj_Nakljucko in ga lepo po pravilih postavil v paket (in s tem
podimenik) s12345678. Razred Stroj_Nakljucko izbira karte povsem naključno,
vendar v skladu s pravili. Nikoli ne odvrže neveljavne karte ali prekorači časovne
omejitve. Kljub preprostosti je Naključko prepričan vase, zato bo sodeloval na tekmovanju.
Priporočamo vam, da izhajate iz razreda Stroj_Nakljucko. Stroj je mogoče že
z minimalnimi popravki bistveno izboljšati!
5
Ogrodje
Stroja ne morete poganjati samostojno, ampak le skupaj z ogrodjem. Ogrodje je
pripravljeno kot projekt za razvojno orodje Netbeans, seveda pa ga lahko prevajate
in poganjate tudi iz terminala. Če želite program prevesti, se postavite v izhodiščni
imenik (tisti, ki vsebuje podimenike src, build itd.) in izvedite sledeči ukaz:
javac -encoding UTF-8 -sourcepath src -d build/classes @javadat.txt
Datoteka javadat.txt vsebuje seznam vseh datotek *.java v podimeniku src. Ko
boste ustvarili svoj stroj, dodajte pot do njegove datoteke na ta seznam, sicer se vaš
stroj ne bo prevedel.
V izhodiščnem imeniku poženete ogrodje tako:
java -cp build/classes ogrodje.Tarok neobvezni_parametri
Če ogrodje zaganjate iz podimenika build/classes, je parameter -cp build/classes
odveč.
Če boste ogrodje pognali brez parametrov, se bo izvedlo v načinu »človek proti
človeku«. Ta način je primeren predvsem za učenje pravil igre. Sicer pa lahko izbirate
med sledečimi parametri:
• -1 stroj : Stroj, ki bo v prvem štihu prve partije prvi odvrgel karto. (Zaradi
izmeničnega pričenjanja bo v drugi partiji prvi štih pričel njegov nasprotnik.)
• -2 stroj : Stroj, ki bo v prvem štihu prve partije odgovoril na nasprotnikovo
karto.
• -t milisekunde: Časovna omejitev v milisekundah za (oba) stroj(a). Privzeta
vrednost znaša 10 000.
• -n številoPartij : Število partij, ki naj se odigrajo. Ta parameter se upošteva
samo pri igri stroja proti stroju. Število partij po privzetih nastavitvah ni
omejeno.
• -b: Enostaven besedilni vmesnik namesto grafičnega. Besedilni vmesnik ne
upošteva časovne omejitve, kljub temu pa vam bo morda prišel prav pri igri
stroja proti stroju.
7
• -d dnevnik.txt: Če je ta parameter prisoten, se na konec datoteke dnevnik.txt
ob koncu vsake partije zapiše natančen potek partije v besedilni obliki. Datoteka se samodejno ustvari, če ne obstaja.
• -s seme: Seme naključnega generatorja za deljenje kart. Privzeta vrednost je
0, kar pomeni, da se seme sploh ne uporablja.
• -r razporeditev.txt: Če je ta parameter prisoten, se karte ne bodo razdelile
z naključnim generatorjem, ampak se bo razporeditev prebrala iz datoteke
razporeditev.txt. Primer razporeditve je podan v datoteki razporeditev1.txt.
• -h: Ta parameter je možno uporabiti samo v primeru igre človeka proti stroju.
Če je vključen, so karte v strojevi »roki« obrnjene s podobo navzdol, torej tako
kot pri pravi igri taroka.
Poleg gornjih parametrov je možno nastavljati tudi konstante, ki določajo premore med posameznimi dogodki v grafičnem ogrodju oziroma način zaprtja določenega okna (samodejno po preteku določenega časa ali z uporabnikovim klikom). Te
konstante so definirane in podrobno opisane v datoteki animacija.txt. Nastavite
jih po svojem okusu.
Oglejmo si nekaj primerov zagona ogrodja:
• java -cp build/classes ogrodje.Tarok -r razporeditev1.txt
Ogrodje se požene v načinu »človek proti človeku«. Razporeditev kart se prebere iz datoteke razporeditev1.txt.
• java -cp build/classes ogrodje.Tarok
-2 s12345678.Stroj_Nakljucko -h
Človek igra proti stroju Nakljucko. Prvo partijo bo pričel človek, naslednjo
Nakljucko itd. Karte v Naključkovi »roki« bodo obrnjene s podobo navzdol.
• java -cp build/classes ogrodje.Tarok
-1 sXXXXXXXX .Stroj_Tarokoman
-2 s12345678.Stroj_Nakljucko
-d dnevnik.txt -n 100 -s 123 -t 30000
Stroj Tarokoman bo s strojem Nakljucko odigral 100 partij, pri čemer bo prvo
partijo pričel stroj Tarokoman. Naključni generator za deljenje kart bo inicializiran s semenom 123. Potek posameznih partij se bo sproti zapisoval v
datoteko dnevnik.txt. Oba stroja bosta v vsaki partiji imela po 30 sekund
časa.
Da si prihranite tipkanje, se vam ukaz za zagon ogrodja splača shraniti v skriptno datoteko, npr. tarok.sh na linuxu ali jabolku (ne pozabite na ukaz chmod +x
tarok.sh) oziroma tarok.bat na oknih.
6
Potek tekmovanja
Tekmovanje bo povsem pošteno: naključno bomo tvorili n razporeditev kart, nato pa
bo vsak stroj z vsakim odigral po dve partiji (v eni bo pričel prvi stroj, v drugi pa
8
drugi) z vsako od n razporeditev. Število razporeditev bo odvisno od števila prispelih
strojev. Časovna omejitev bo prav tako odvisna od števila strojev. Predvidoma bo
znašala med 10 in 30 sekundami za vsak posamezen stroj v vsaki posamezni partiji.
Tekmovanje bomo odprli tudi za študente višjih letnikov, vendar pa boste študentje prvega letnika univerzitetnih programov BUN-RI, BUN-RM in Multimedija
tvorili ločeno skupino. Avtor najboljšega stroja znotraj te skupine bo na izpitu pri
predmetu Programiranje I prejel dodatnih 20 točk. Drugouvrščeni bo prejel 16 dodatnih točk, tretjeuvrščeni 12, četrto- in petouvrščeni po 9 in 6, šesto- do desetouvrščeni
pa po 3 dodatne točke. Še veliko pomembnejše pa je seveda zadovoljstvo in prestiž,
ki ga prinese dober rezultat.
7
Dodatna pravila
• Poraba pomnilniškega prostora bo omejena na 500 MB na stroj.
• Stroj ne sme ustvarjati niti ali procesov. Ogrodje že samo po sebi poganja
vsak stroj v posebni niti, zato da vmesnik ostane odziven in da časovnik lahko
normalno teče. Tudi sicer so ukazi za delo z nitmi in procesi prepovedani.
• Prepovedani so tudi ukazi za delo z datotekami.
• Prepovedana je uporaba razredov iz paketa java.lang.reflect in javanskega
introspekcijskega mehanizma (angl. reflection) nasploh.
• Prepovedana je uporaba ukazov za delo z grafiko in ogrodjem Swing.
• Uporabljajte kodiranje UTF-8 ali pa se vzdržite rabe šumnikov (tudi v komentarjih!).
• Na spletno učilnico oddajte datoteko sXXXXXXXX .zip, kjer je XXXXXXXX
vaša vpisna številka. Paket zip naj vsebuje imenik sXXXXXXXX , v njem pa
datoteko Stroj_Ime.java in po potrebi še druge datoteke s končnico .java.
Stroji, ki se gornjih omejitev ne bodo držali, bodo diskvalificirani. Poskus dostopa
do skritih podatkov, torej do notranjosti kupčkov ali nasprotnikovih kart, se bo
obravnaval kot goljufija in bo temu ustrezno sankcioniran.
Za vprašanja, pripombe . . .
. . . se obrnite na luka.fuerst@fri.uni-lj.si.
Veliko užitkov pri programiranju!
9