Programavimas

Kortelių variklis „Java“

Viskas prasidėjo tada, kai pastebėjome, kad „Java“ rašytų kortų žaidimų programų ar programėlių yra labai mažai. Pirmiausia sugalvojome parašyti keletą žaidimų ir pradėjome išsiaiškinti pagrindinį kodą ir klases, reikalingas kortų žaidimams kurti. Procesas tęsiasi, tačiau dabar yra gana stabili sistema, naudojama kuriant įvairius kortų žaidimų sprendimus. Čia mes apibūdiname, kaip ši sistema buvo sukurta, kaip ji veikia, ir įrankiai bei gudrybės, kurie buvo naudojami, kad ji būtų naudinga ir stabili.

Projektavimo etapas

Kuriant objektinį dizainą, nepaprastai svarbu žinoti problemą viduje ir išorėje. Priešingu atveju galima skirti daug laiko kuriant klases ir sprendimus, kurie nėra reikalingi arba neveiks pagal konkrečius poreikius. Kortų žaidimų atveju vienas būdas yra vizualizuoti, kas vyksta, kai vienas, du ar daugiau asmenų žaidžia kortomis.

Kortelių kaladėje paprastai yra 52 kortelės iš keturių skirtingų kostiumų (deimantų, širdelių, lazdelių, kastuvėlių), kurių vertės svyruoja nuo deuce iki karaliaus ir tūzo. Iškart iškyla problema: atsižvelgiant į žaidimo taisykles, tūzai gali būti arba mažiausia, ir didžiausia, arba abu.

Be to, yra žaidėjų, kurie ima kortas iš kaladės į ranką ir valdo ranką remdamiesi taisyklėmis. Galite arba parodyti korteles visiems, padėdami jas ant stalo, arba pažiūrėti į jas privačiai. Priklausomai nuo konkretaus žaidimo etapo, jūsų rankoje gali būti N kortelių skaičius.

Tokiu būdu analizuojant etapus, nustatomi įvairūs modeliai. Dabar mes naudojame atvejo principą, kaip aprašyta aukščiau, kuris yra dokumentuotas Ivaro Jacobsono knygoje Objektinė programinės įrangos inžinerija. Šioje knygoje viena pagrindinių idėjų yra klasių modeliavimas remiantis realiomis situacijomis. Tai leidžia daug lengviau suprasti, kaip veikia santykiai, kas nuo ko priklauso ir kaip veikia abstrakcijos.

Mes turime tokias klases kaip „CardDeck“, „Hand“, „Card“ ir „RuleSet“. „CardDeck“ pradžioje bus 52 „Card“ objektai, o „CardDeck“ bus mažiau „Card“ objektų, nes jie įtraukiami į „Hand“ objektą. Rankiniai objektai kalba su „RuleSet“ objektu, kuriame yra visos su žaidimu susijusios taisyklės. Pagalvokite apie „RuleSet“ kaip apie žaidimo vadovą.

Vektorių klasės

Šiuo atveju mums reikėjo lanksčios duomenų struktūros, kuri valdytų dinaminius įvesties pakeitimus, o tai pašalino duomenų masyvo struktūrą. Mes taip pat norėjome paprasto būdo pridėti įdėklo elementą ir, jei įmanoma, išvengti daug kodavimo. Yra įvairių sprendimų, pavyzdžiui, įvairių formų dvejetainiai medžiai. Tačiau paketas „java.util“ turi „Vector“ klasę, kuri įgyvendina objektų masyvą, kuris prireikus auga ir mažėja, o tai buvo būtent tai, ko mums reikėjo. („Vector“ nario funkcijos nėra išsamiai paaiškintos dabartinėje dokumentacijoje; šiame straipsnyje bus paaiškinta, kaip „Vector“ klasė gali būti naudojama panašiems dinaminių objektų sąrašo egzemplioriams.) „Vector“ klasių trūkumas yra papildomas atminties naudojimas dėl daugybės atminties kopijavimas atliekamas užkulisiuose. (Dėl šios priežasties masyvai visada yra geresni; jie yra statinio dydžio, todėl kompiliatorius galėtų išsiaiškinti būdus, kaip optimizuoti kodą). Be to, turėdami didesnius objektų rinkinius, mes galime turėti nuobaudų dėl paieškos laiko, tačiau didžiausias vektorius, kurį galėjome sugalvoti, buvo 52 įrašai. Tai vis dar pagrįsta šiuo atveju, o ilgas peržiūros laikas nerimo.

Toliau pateikiamas trumpas paaiškinimas, kaip buvo sukurta ir įgyvendinta kiekviena klasė.

Kortelių klasė

Kortelių klasė yra labai paprasta: joje yra vertės, nurodančios spalvą ir vertę. Jame taip pat gali būti nuorodų į GIF vaizdus ir panašius objektus, apibūdinančius kortelę, įskaitant galimą paprastą elgesį, pvz., Animaciją (apversti kortelę) ir pan.

klasė „Card“ įgyvendina „CardConstants“ {public int color; public int vertė; public String ImageName; } 

Tada šie kortelių objektai yra saugomi įvairiose „Vector“ klasėse. Atkreipkite dėmesį, kad kortelių vertės, įskaitant spalvą, yra apibrėžtos sąsajoje, o tai reiškia, kad kiekviena sistema sistemoje galėtų įdiegti ir tokiu būdu įtraukti konstantas:

sąsaja „CardConstants“ {// sąsajos laukai visada yra statiški, viešieji! int ŠIRDYS 1; int 2 DIAMONDAS; int SPADE 3; int 4 KLUBAI; int JACK 11; int 12 KARALIENĖ; int KING 13; int ACE_LOW 1; int ACE_HIGH 14; } 

„CardDeck“ klasė

„CardDeck“ klasėje bus vidinis „Vector“ objektas, kuris bus iš anksto inicializuotas 52 kortelių objektais. Tai atliekama naudojant „shuffle“ metodą. Tai reiškia, kad kiekvieną kartą, kai maišote, jūs iš tikrųjų pradedate žaidimą apibrėždami 52 kortas. Būtina pašalinti visus galimus senus objektus ir vėl pradėti nuo numatytosios būsenos (52 kortelių objektai).

 public void shuffle () {// Visada nuliuokite denio vektorių ir inicijuokite jį nuo nulio. deck.removeAllElements (); 20 // Tada įdėkite 52 korteles. Po vieną spalvą (int i ACE_LOW; i <ACE_HIGH; i ++) {Card aCard new Card (); aCard.color ŠIRDYS; aCard.value i; deck.addElement (aCard); } // Atlikite tą patį darbą su KLUBAI, ŽIEMĖLIAIS ir PADAIS. } 

Kai iš „CardDeck“ piešiame „Card“ objektą, mes naudojame atsitiktinių skaičių generatorių, kuris žino rinkinį, iš kurio jis pasirinks atsitiktinę padėtį vektoriaus viduje. Kitaip tariant, net jei kortelės objektai yra užsakyti, atsitiktinė funkcija pasirenka savavališką poziciją vektoriaus viduje esančių elementų srityje.

Vykdydami šį procesą, mes taip pat pašaliname faktinį objektą iš „CardDeck“ vektoriaus, kai perduodame šį objektą „Hand“ klasei. „Vector“ klasė atvaizduoja kortų kaladės ir rankos realią situaciją, praleidžiant kortelę:

 public Card draw () {Card aCard null; int padėtis (int) (Math.random () * (deck.size = ())); išbandykite {aCard (Card) deck.elementAt (position); } gaudyti (ArrayIndexOutOfBoundsException e) {e.printStackTrace (); } deck.removeElementAt (padėtis); grąžinti kortelę; } 

Atkreipkite dėmesį, kad naudinga sugauti visas įmanomas išimtis, susijusias su objekto paėmimu iš vektoriaus iš vietos, kurios nėra.

Yra naudingumo metodas, kuris kartoja visus vektoriaus elementus ir iškviečia kitą metodą, kuris išmes ASCII vertės / spalvų poros eilutę. Ši funkcija yra naudinga derinant tiek „Deck“, tiek „Hand“ klases. Vektorių surašymo ypatybės plačiai naudojamos „Hand“ klasėje:

 public void dump () {Enumeration enum deck.elements (); while (enum.hasMoreElements ()) {Card card (Card) enum.nextElement (); „RuleSet.printValue“ (kortelė); }} 

Rankų klasė

Rankų klasė šioje srityje yra tikras darbinis arklys. Daugiausia reikalingo elgesio buvo tai, kas buvo labai natūralu priskirti šiai klasei. Įsivaizduokite, kaip žmonės laiko korteles rankose ir daro įvairias operacijas, žiūrėdami į Kortelės objektus.

Pirma, jums taip pat reikia vektoriaus, nes daugeliu atvejų nežinoma, kiek kortelių bus paimta. Nors galėtumėte įdiegti masyvą, gerai, kad ir čia turite šiek tiek lankstumo. Natūraliausias būdas, kurio mums reikia, yra kortelės paėmimas:

 public void take („Card theCard“) {cardHand.addElement (theCard); } 

„CardHand“ yra vektorius, todėl mes tiesiog pridedame kortelės objektą į šį vektorių. Tačiau „rankos išvedimo“ operacijų atveju turime du atvejus: vieną, kuriame parodome kortelę, ir vieną, kai abu rodome ir traukiame kortelę iš rankos. Turime įgyvendinti abu dalykus, tačiau naudodami paveldėjimą rašome mažiau kodo, nes kortelės piešimas ir rodymas yra ypatingas atvejis, kai reikia parodyti tik kortelę:

 public Card show (int position) {Card aCard null; pabandykite {aCard (Card) cardHand.elementAt (pozicija); } gaudyti (ArrayIndexOutOfBoundsException e) {e.printStackTrace (); } grąžinti kortelę; } 20 viešų kortelių ištraukimas (int position) {Card aCard show (position); cardHand.removeElementAt (pozicija); grąžinti kortelę; } 

Kitaip tariant, traukimo atvejis yra parodomasis atvejis, papildoma elgsena pašalinant objektą iš vektoriaus Ranka.

Rašydami testo kodą įvairioms klasėms, radome vis daugiau atvejų, kai reikėjo sužinoti apie rankoje esančias įvairias vertybes. Pavyzdžiui, kartais mums reikėjo žinoti, kiek rankoje yra tam tikro tipo kortelių. Arba numatytą mažą vieno tūzo vertę reikėjo pakeisti į 14 (didžiausia vertė) ir vėl. Visais atvejais elgesio palaikymas buvo grąžintas į rankų klasę, nes tai buvo labai natūrali tokio elgesio vieta. Vėlgi, buvo beveik taip, tarsi žmogaus smegenys būtų už rankos, atliekančios šiuos skaičiavimus.

Vektorių surašymo ypatybė gali būti naudojama norint sužinoti, kiek konkrečios vertės kortelių buvo „Hand“ klasėje:

 public int NCards (int value) {int n 0; Surašymo enum cardHand.elements (); while (enum.hasMoreElements ()) {tempCard (Card) enum.nextElement (); // = tempCard apibrėžta, jei (tempCard.value = reikšmė) n ++; } grįžti n; } 

Panašiai galėtumėte kartoti kortelės objektus ir apskaičiuoti bendrą kortelių sumą (kaip 21 bandyme) arba pakeisti kortelės vertę. Atminkite, kad pagal numatytuosius nustatymus visi objektai yra „Java“ nuorodos. Jei atgausite, jūsų manymu, laikiną objektą ir jį modifikuosite, faktinė vertė taip pat bus pakeista objekto, kurį saugo vektorius, viduje. Tai yra svarbus klausimas, kurį reikia nepamiršti.

„RuleSet“ klasė

„RuleSet“ klasė yra tarsi taisyklių knyga, kurią dabar ir tada tikrinate žaisdami; jame yra visas elgesys, susijęs su taisyklėmis. Atminkite, kad galimos strategijos, kurias žaidėjas gali naudoti, yra pagrįstos vartotojo sąsajos atsiliepimais arba paprastu ar sudėtingesniu dirbtinio intelekto (AI) kodu. „RuleSet“ nerimauja tik dėl to, kad laikomasi taisyklių.

Į šią klasę taip pat buvo įtrauktas kitas elgesys, susijęs su kortelėmis. Pavyzdžiui, mes sukūrėme statinę funkciją, kuri spausdina kortelės vertės informaciją. Vėliau tai taip pat galėtų būti priskirta kortelių klasei kaip statinė funkcija. Pagal dabartinę formą „RuleSet“ klasė turi tik vieną pagrindinę taisyklę. Tam reikia dviejų kortelių ir atsiunčiama informacija apie tai, kuri kortelė buvo aukščiausia:

 public int aukštesnis (pirmoji kortelė, antroji kortelė) {int kuri 0; jei (one.value = ACE_LOW) one.value ACE_HIGH; jei (two.value = ACE_LOW) two.value ACE_HIGH; // Šioje taisyklėje nustatoma didžiausia vertė laimi, mes neatsižvelgiame į // spalvą. jei (viena.vertė> dvi.vertė) kuri viena 1; jei (viena. vertė <dvi. vertė) kuri viena 2; jei (viena.vertė = dvi.vertė) kuri viena 0; // Normalizuokite ACE reikšmes, taigi tai, kas buvo perduota, turi tas pačias vertes. jei (one.value = ACE_HIGH) one.value ACE_LOW; jei (two.value = ACE_HIGH) two.value ACE_LOW; grąžinti kurį; } 

Atlikdami bandymą turite pakeisti tūzų vertes, kurių natūrali vertė yra nuo 1 iki 14. Svarbu pakeisti vertes atgal į vieną, kad būtų išvengta bet kokių galimų problemų, nes manome, kad tūzai visada yra vienas.

21 atveju mes priskyrėme „RuleSet“, kad sukurtume „TwentyOneRuleSet“ klasę, kuri žino, kaip išsiaiškinti, ar ranka yra žemesnė nei 21, lygiai 21 ar daugiau nei 21. Tai taip pat atsižvelgia į tūzų vertes, kurios gali būti viena arba 14, ir bando išsiaiškinti geriausią įmanomą vertę. (Norėdami gauti daugiau pavyzdžių, skaitykite šaltinio kodą.) Tačiau žaidėjas turi apibrėžti strategijas; šiuo atveju mes parašėme paprasto proto dirbtinio intelekto sistemą, kur, jei po dviejų kortų tavo ranka yra žemiau 21, imi dar vieną kortelę ir sustoji.

Kaip naudotis klasėmis

Gana paprasta naudoti šią sistemą:

 „myCardDeck new CardDeck“ (); „myRules“ naujas „RuleSet“ (); rankaNauja ranka (); handB new Hand (); DebugClass.DebugStr ("Ištraukite po penkias korteles, kad padėtumėte A ir B rankas"); for (int i 0; i <NCARDS; i ++) {handA.take (myCardDeck.draw ()); handB.take (myCardDeck.draw ()); } // Testavimo programos, išjunkite komentuodami arba naudodami DEBUG vėliavas. testHandValues ​​(); testCardDeckOperations (); testCardValues ​​(); testHighestCardValues ​​(); test21 (); 

Įvairios bandymo programos yra išskiriamos į atskiras statinio ar nestatinio nario funkcijas. Susikurkite tiek rankų, kiek norite, paimkite korteles ir leiskite šiukšlių surinkimui atsikratyti nenaudojamų rankų ir kortelių.

Jūs paskambinate į „RuleSet“ pateikdami rankos ar kortelės objektą ir, atsižvelgdami į grąžintą vertę, žinote rezultatą:

 DebugClass.DebugStr ("Palyginkite antrąją kortą rankose A ir B rankose"); int nugalėtojas myRules.higher (handA.show (1), = handB.show (1)); if (laimėtojas = 1) o.println ("A ranka turėjo aukščiausią kortą."); else if (nugalėtojas = 2) o.println ("B ranka turėjo aukščiausią kortą."); else o.println ("Tai buvo lygiosios."); 

Arba 21 atveju:

 int rezultatas myTwentyOneGame.isTwentyOne (handC); if (rezultatas = 21) o.println ("Gavome Dvidešimt Vieną!"); else if (rezultatas> 21) o.println ("Mes praradome" + rezultatas); else {o.println ("Mes paimame kitą kortelę"); // ...} 

Testavimas ir derinimas

Įgyvendinant tikrąją sistemą, labai svarbu parašyti testo kodą ir pavyzdžius. Tokiu būdu jūs visada žinote, kaip gerai veikia diegimo kodas; suprantate faktus apie ypatybes ir išsamią informaciją apie įgyvendinimą. Turėdami daugiau laiko, būtume įdiegę pokerį - toks bandomasis atvejis būtų suteikęs dar daugiau įžvalgos apie problemą ir parodęs, kaip iš naujo apibrėžti sistemą.

$config[zx-auto] not found$config[zx-overlay] not found