Programování

Dáma, někdo?

Před několika měsíci jsem byl požádán, abych vytvořil malou knihovnu Java, do které bude mít přístup aplikace k vykreslení grafického uživatelského rozhraní (GUI) pro hru Checkers. Kromě vykreslení šachovnice a šachovnice musí grafické uživatelské rozhraní umožnit přetažení kontroly z jednoho čtverce na druhý. Kontrola musí být také vycentrována na čtverec a nesmí být přiřazena čtverci, který je obsazen jiným hráčem. V tomto příspěvku představuji svou knihovnu.

Návrh knihovny GUI dámy

Jaké veřejné typy by měla knihovna podporovat? V dámě každý ze dvou hráčů střídavě přesouvá jednu ze svých pravidelných (ne králových) dám po desce pouze v dopředném směru a případně přeskočí dámu druhého hráče. Když se dáma dostane na druhou stranu, je povýšen na krále, který se může pohybovat také dozadu. Z tohoto popisu můžeme odvodit následující typy:

  • Prkno
  • Kontrola
  • CheckerType
  • Hráč

A Prkno objekt identifikuje šachovnici. Slouží jako kontejner pro Kontrola objekty, které zabírají různé čtverce. Může se kreslit a požadovat, aby každý obsahoval Kontrola nakreslit objekt sám.

A Kontrola objekt identifikuje kontrolu. Má barvu a označení, zda jde o běžnou kontrolu nebo kontrolu krále. Může se kreslit a zpřístupňovat svou velikost Prkno, jehož velikost je ovlivněna Kontrola velikost.

CheckerType je výčet, který identifikuje barvu a typ kontroly pomocí svých čtyř konstant: BLACK_KING, BLACK_REGULAR, RED_KING, a RED_REGULAR.

A Hráč objekt je řadič pro pohyb dáma s volitelnými skoky. Protože jsem se rozhodl implementovat tuto hru ve Swingu, Hráč není nutné. Místo toho jsem se otočil Prkno do komponenty Swing, jejíž konstruktor registruje myš a posluchače pohybu myší, kteří zpracovávají pohyb kontroly jménem lidského hráče. V budoucnu bych mohl implementovat počítačový přehrávač pomocí jiného vlákna, synchronizátoru a jiného Prkno metoda (např hýbat se()).

Co dělají veřejné API Prkno a Kontrola přispět? Po chvíli přemýšlení jsem přišel s následující veřejností Prkno API:

  • Prkno(): Postavte a Prkno objekt. Konstruktor provádí různé inicializační úlohy, jako je registrace posluchače.
  • void add (Checker checker, int řádek, int sloupec): Přidat kontrola na Prkno v poloze označené řádek a sloupec. Řádek a sloupec jsou hodnoty založené na 1 na rozdíl od hodnoty 0 (viz obrázek 1). The přidat() hodí java.lang.IllegalArgumentException když jeho argument řádku nebo sloupce je menší než 1 nebo větší než 8. Také hodí nezaškrtnuté AlreadyOccupiedException když se pokusíte přidat a Kontrola na obsazené náměstí.
  • Dimenze getPreferredSize (): Vraťte Prkno preferovaná velikost komponenty pro účely rozvržení.

Obrázek 1. Levý horní roh šachovnice je umístěn na (1, 1)

Také jsem vyvinul následující veřejnost Kontrola API:

  • Kontrola (CheckerType checkerType): Postavte a Kontrola objekt zadaného checkerType (BLACK_KING, BLACK_REGULAR, RED_KINGnebo RED_REGULAR).
  • void draw (Grafika g, int cx, int cy): Nakresli Kontrola pomocí zadaného grafického kontextu G se středem kontroly umístěným na (cx, cy). Tato metoda má být volána z Prkno pouze.
  • boolean obsahuje (int x, int y, int cx, int cy): A statický pomocná metoda volaná z Prkno který určuje, zda souřadnice myši (X, y) leží uvnitř kontroly, jejíž středové souřadnice jsou specifikovány (cx, cy) a jehož rozměr je uveden jinde v EU Kontrola třída.
  • int getDimension (): A statický pomocná metoda volaná z Prkno který určuje velikost kontroly, aby deska mohla odpovídajícím způsobem velikost svých čtverců a celkové velikosti.

To do značné míry pokrývá všechny knihovny GUI dámy z hlediska jejich typů a jejich veřejných API. Nyní se zaměříme na to, jak jsem implementoval tuto knihovnu.

Implementace knihovny GUI dáma

Knihovna GUI dámy se skládá ze čtyř veřejných typů umístěných ve zdrojových souborech se stejným názvem: AlreadyOccupiedException, Prkno, Kontrola, a CheckerType. Výpis 1 dárků AlreadyOccupiedExceptionzdrojový kód.

Výpis 1. AlreadyOccupiedException.java

public class AlreadyOccupiedException rozšiřuje RuntimeException {public AlreadyOccupiedException (String msg) {super (msg); }}

AlreadyOccupiedException rozšiřuje java.lang.RuntimeException, který dělá AlreadyOccupiedException nezaškrtnutá výjimka (nemusí být chycena nebo deklarována v hodí doložka). Kdybych chtěl udělat AlreadyOccupiedException zkontrolováno, prodloužil bych java.lang.Exception. Rozhodl jsem se, že tento typ nechám nezaškrtnutý, protože funguje podobně jako nezaškrtnutý IllegalArgumentException.

AlreadyOccupiedException deklaruje konstruktor, který přebírá řetězcový argument popisující důvod výjimky. Tento argument je předán RuntimeException nadtřída.

Seznam 2 dárků Prkno.

Výpis 2. Board.java

importovat java.awt.Color; importovat java.awt.Dimension; importovat java.awt.Graphics; import java.awt.Graphics2D; importovat java.awt.RenderingHints; import java.awt.event.MouseEvent; importovat java.awt.event.MouseAdapter; importovat java.awt.event.MouseMotionAdapter; import java.util.ArrayList; import java.util.List; import javax.swing.JComponent; public class Board extends JComponent {// dimenze šachovnicového čtverce (o 25% větší než checker) private final static int SQUAREDIM = (int) (Checker.getDimension () * 1,25); // rozměr šachovnice (šířka 8 čtverců) soukromý konečný int BOARDDIM = 8 * SQUAREDIM; // preferovaná velikost komponenty Board private Dimension dimPrefSize; // příznak přetažení - nastaven na true, když uživatel stiskne tlačítko myši nad kontrolou // a vymazán na false, když uživatel uvolní tlačítko myši private boolean inDrag = false; // posun mezi souřadnicemi startu drag a souřadnicemi centra checkeru private int deltaxe, deltay; // odkaz na kontrolu polohy na začátku přetahování soukromý PosCheck posCheck; // umístění středu checkeru na začátku přetažení private int oldcx, oldcy; // seznam objektů Checker a jejich počátečních pozic private List posChecks; public Board () {posChecks = new ArrayList (); dimPrefSize = nová dimenze (BOARDDIM, BOARDDIM); addMouseListener (new MouseAdapter () {@Override public void mousePressed (MouseEvent me) {// Získat souřadnice myši v době tisku. int x = me.getX (); int y = me.getY (); // Vyhledat poziční kontrolu pod stiskem myši. pro (PosCheck posCheck: posChecks) if (Checker.contains (x, y, posCheck.cx, posCheck.cy)) {Board.this.posCheck = posCheck; oldcx = posCheck.cx; oldcy = posCheck.cy ; deltax = x - posCheck.cx; deltay = y - posCheck.cy; inDrag = true; návrat;}} @Override public void mouseReleased (MouseEvent me) {// Když myš uvolníte, vymažte inDrag (pro // žádný tah právě probíhá) pokud je inDrag // již nastaven. if (inDrag) inDrag = false; else return; // Kontrola snapu do středu čtverce. int x = me.getX (); int y = me.getY (); posCheck .cx = (x - deltaxe) / SQUAREDIM * SQUAREDIM + SQUAREDIM / 2; posCheck.cy = (y - deltay) / SQUAREDIM * SQUAREDIM + SQUAREDIM / 2; // Neposunujte šachovnici na obsazený čtverec. pro (PosCheck posCheck : posChecks) if (posCheck! = Board.this.posCheck && posC heck.cx == Board.this.posCheck.cx && posCheck.cy == Board.this.posCheck.cy) {Board.this.posCheck.cx = oldcx; Board.this.posCheck.cy = oldcy; } posCheck = null; překreslit (); }}); // Připojte k appletu posluchač pohybu myši. Tento posluchač naslouchá // událostem přetažení myší. addMouseMotionListener (nový MouseMotionAdapter () {@Override public void mouseDragging (MouseEvent me) {if (inDrag) {// Aktualizovat umístění centra kontroly. posCheck.cx = me.getX () - deltax; posCheck.cy = me.getY ( ) - deltay; překreslit ();}}}); } public void add (Checker checker, int row, int col) {if (row 8) throw new IllegalArgumentException ("row out of range:" + row); if (col 8) throw new IllegalArgumentException ("col out of range:" + col); PosCheck posCheck = nový PosCheck (); posCheck.checker = kontrola; posCheck.cx = (sloupec - 1) * SQUAREDIM + SQUAREDIM / 2; posCheck.cy = (řádek - 1) * SQUAREDIM + SQUAREDIM / 2; for (PosCheck _posCheck: posChecks) if (posCheck.cx == _posCheck.cx && posCheck.cy == _posCheck.cy) throw new AlreadyOccupiedException ("square at (" + row + "," + col + ") is employed" ); posChecks.add (posCheck); } @Override public Dimension getPreferredSize () {return dimPrefSize; } @Override protected void paintComponent (Grafika g) {paintCheckerBoard (g); pro (PosCheck posCheck: posChecks) if (posCheck! = Board.this.posCheck) posCheck.checker.draw (g, posCheck.cx, posCheck.cy); // Nakreslete naposledy přetaženou kontrolu, aby se zobrazila nad jakoukoli základní // kontrolou. if (posCheck! = null) posCheck.checker.draw (g, posCheck.cx, posCheck.cy); } private void paintCheckerBoard (Graphics g) {((Graphics2D) g) .setRenderingHint (RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // Malování šachovnice. for (int řádek = 0; řádek <8; řádek ++) {g.setColor (((řádek & 1)! = 0)? Color.BLACK: Color.WHITE); for (int col = 0; col <8; col ++) {g.fillRect (col * SQUAREDIM, row * SQUAREDIM, SQUAREDIM, SQUAREDIM); g.setColor ((g.getColor () == Color.BLACK)? Color.WHITE: Color.BLACK); }}} // pomocná třída umisťované kontroly checkeru soukromá třída PosCheck {public Checker checker; public int cx; veřejné int; }}

Prkno rozšiřuje javax.swing.JComponent, který dělá Prkno komponenta Swing. Jako takový můžete přímo přidat a Prkno komponenta do podokna obsahu aplikace Swing.

Prkno prohlašuje SQUAREDIM a BOARDDIM konstanty, které identifikují rozměry pixelů čtverce a zaškrtávacího políčka. Při inicializaci SQUAREDIM, Dovolávám se Checker.getDimension () místo přístupu k rovnocenné veřejnosti Kontrola konstantní. Joshua Block odpovídá, proč to dělám v položce # 30 (místo toho použijte enums int konstanty) druhého vydání jeho knihy, Efektivní Java: "Programy, které používají int vzor výčtu je křehký. Protože int výčty jsou konstanty v době kompilace, jsou kompilovány do klientů, kteří je používají. Pokud int přidružené ke konstantě výčtu se změní, je nutné překompilovat klienty. Pokud tomu tak není, budou i nadále běžet, ale jejich chování bude nedefinováno. “

Kvůli obsáhlým komentářům k tomu nemám moc co říct Prkno. Všimněte si však vnořených PosCheck třída, která popisuje kontrolu polohy uložením a Kontrola reference a její středové souřadnice, které jsou relativní k levému hornímu rohu Prkno součástka. Když přidáte a Kontrola namítat proti Prkno, je uložen v novém PosCheck objekt spolu se středovou pozicí kontroly, která se počítá ze zadaného řádku a sloupce.

Seznam 3 dárků Kontrola.

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