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
© Copyright 2024