Προγραμματισμός

Πούλια;

Πριν από μερικούς μήνες, μου ζητήθηκε να δημιουργήσω μια μικρή βιβλιοθήκη Java στην οποία μπορείτε να αποκτήσετε πρόσβαση μέσω μιας εφαρμογής για την απόδοση μιας γραφικής διεπαφής χρήστη (GUI) για το παιχνίδι Checkers Εκτός από την απόδοση ενός σκακιέρας και πούλι, το GUI πρέπει να επιτρέπει τη μεταφορά ενός πούλι από το ένα τετράγωνο στο άλλο. Επίσης, ένα πούλι πρέπει να είναι κεντραρισμένο σε ένα τετράγωνο και δεν πρέπει να αντιστοιχιστεί σε ένα τετράγωνο που καταλαμβάνεται από άλλο πούλι. Σε αυτήν την ανάρτηση, παρουσιάζω τη βιβλιοθήκη μου.

Σχεδιασμός βιβλιοθήκης GUI πούλιων

Τι δημόσιοι τύποι πρέπει να υποστηρίζει η βιβλιοθήκη; Στα πούλια, καθένας από τους δύο παίκτες μετακινεί εναλλάξ ένα από τα κανονικά (μη βασιλιά) πούλια του πάνω από ένα ταμπλό σε μια κατεύθυνση προς τα εμπρός μόνο και πιθανώς πηδά τα πούλια του άλλου παίκτη. Όταν το πούλι φτάσει στην άλλη πλευρά, προωθείται σε έναν βασιλιά, ο οποίος μπορεί επίσης να κινηθεί προς τα πίσω. Από αυτήν την περιγραφή, μπορούμε να συμπεράνουμε τους ακόλουθους τύπους:

  • Σανίδα
  • Ντάμα
  • Τύπος Checker
  • Παίχτης

ΕΝΑ Σανίδα το αντικείμενο αναγνωρίζει την σκακιέρα. Χρησιμεύει ως δοχείο για Ντάμα αντικείμενα που καταλαμβάνουν διάφορα τετράγωνα. Μπορεί να σχεδιαστεί και να ζητήσει το καθένα να περιέχει Ντάμα το αντικείμενο σχεδιάζεται.

ΕΝΑ Ντάμα το αντικείμενο προσδιορίζει ένα πούλι. Έχει χρώμα και ένδειξη για το αν είναι κανονικό πούλι ή βασιλικό πούλι. Μπορεί να σχεδιαστεί και να κάνει διαθέσιμο το μέγεθός του Σανίδα, του οποίου το μέγεθος επηρεάζεται από το Ντάμα Μέγεθος.

Τύπος Checker είναι ένα enum που προσδιορίζει ένα χρώμα και τύπο πούλι μέσω των τεσσάρων σταθερών του: ΜΑΥΡΟ, BLACK_REGULAR, RED_KING, και RED_REGULAR.

ΕΝΑ Παίχτης αντικείμενο είναι ένας ελεγκτής για τη μετακίνηση ενός πούλι με προαιρετικά άλματα. Επειδή έχω επιλέξει να εφαρμόσω αυτό το παιχνίδι στο Swing, Παίχτης δεν είναι απαραίτητο. Αντ 'αυτού, γύρισα Σανίδα σε ένα στοιχείο Swing του οποίου ο κατασκευαστής καταγράφει ακροατές κίνησης ποντικιού και ποντικιού που χειρίζονται την κίνηση πούλι για λογαριασμό του ανθρώπινου παίκτη. Στο μέλλον, θα μπορούσα να εφαρμόσω ένα πρόγραμμα αναπαραγωγής υπολογιστή μέσω άλλου νήματος, ενός συγχρονιστή και ενός άλλου Σανίδα μέθοδος (όπως κίνηση()).

Τι κάνουν τα δημόσια API Σανίδα και Ντάμα συνεισφέρουν? Μετά από κάποια σκέψη, ήρθα με το ακόλουθο κοινό Σανίδα API:

  • Σανίδα(): Κατασκευή α Σανίδα αντικείμενο. Ο κατασκευαστής εκτελεί διάφορες εργασίες αρχικοποίησης, όπως εγγραφή ακροατή.
  • void add (Checker checker, int σειρά, int στήλη): Προσθήκη ντάμα προς την Σανίδα στη θέση που προσδιορίζεται από σειρά και στήλη. Η σειρά και η στήλη είναι τιμές που βασίζονται σε 1, αντί να βασίζονται στο 0 (βλέπε σχήμα 1). ο Προσθήκη() ρίχνει java.lang.IllegalArgumentException όταν το όρισμα γραμμής ή στήλης είναι μικρότερο από 1 ή μεγαλύτερο από 8. Επίσης, ρίχνει το μη επιλεγμένο ΉδηOccupiedException όταν προσπαθείτε να προσθέσετε ένα Ντάμα σε μια κατεχόμενη πλατεία.
  • Διάσταση getPreferredSize (): Επέστρεψε το Σανίδα το προτιμώμενο μέγεθος του στοιχείου για σκοπούς διάταξης.

Εικόνα 1. Η επάνω αριστερή γωνία του πίνακα ελέγχου βρίσκεται στο (1, 1)

Επίσης ανέπτυξα το ακόλουθο κοινό Ντάμα API:

  • Checker (CheckerType checkerType): Κατασκευή α Ντάμα αντικείμενο του καθορισμένου Τύπος πούλι (ΜΑΥΡΟ, BLACK_REGULAR, RED_KING, ή RED_REGULAR).
  • άκυρη κλήρωση (Γραφικά g, int cx, int cy): Σχεδίασε ένα Ντάμα χρησιμοποιώντας το καθορισμένο περιβάλλον γραφικών σολ με το κέντρο του πούλι που βρίσκεται στο (cx, cy). Αυτή η μέθοδος προορίζεται να κληθεί από Σανίδα μόνο.
  • Το boolean περιέχει (int x, int y, int cx, int cy): ΕΝΑ στατικός βοηθητική μέθοδος από Σανίδα που καθορίζει εάν οι συντεταγμένες του ποντικιού (Χ, γ) βρίσκονται μέσα στο πούλι των οποίων οι κεντρικές συντεταγμένες καθορίζονται από (cx, cy) και των οποίων η διάσταση καθορίζεται αλλού στο Ντάμα τάξη.
  • int getDimension (): ΕΝΑ στατικός βοηθητική μέθοδος από Σανίδα που καθορίζει το μέγεθος ενός πούλι, ώστε ο πίνακας να μπορεί να διαμορφώσει κατάλληλα τα τετράγωνα και το συνολικό του μέγεθος.

Αυτό καλύπτει σχεδόν όλη τη βιβλιοθήκη GUI πούρων όσον αφορά τους τύπους της και τα δημόσια API τους. Θα επικεντρωθούμε τώρα στον τρόπο εφαρμογής αυτής της βιβλιοθήκης.

Εφαρμογή της βιβλιοθήκης GUI πούλια

Η βιβλιοθήκη ελεγκτών GUI αποτελείται από τέσσερις δημόσιους τύπους που βρίσκονται σε αρχεία πηγής με το ίδιο όνομα: ΉδηOccupiedException, Σανίδα, Ντάμα, και Τύπος Checker. Η λίστα 1 παρουσιάζει ΉδηOccupiedExceptionΟ πηγαίος κώδικας.

Λίστα 1. ΉδηOccupiedException.java

δημόσια τάξη AlreadyOccupiedException επεκτείνει το RuntimeException {public AlreadyOccupiedException (String msg) {super (msg); }}

ΉδηOccupiedException εκτείνεται java.lang.RuntimeException, που κάνει ΉδηOccupiedException μια ανεξέλεγκτη εξαίρεση (δεν χρειάζεται να πιάσει ή να δηλωθεί σε ένα ρίχνει ρήτρα). Αν ήθελα να φτιάξω ΉδηOccupiedException τσεκαρισμένο, θα είχα επεκταθεί java.lang.Exception. Επέλεξα να κάνω αυτόν τον τύπο μη ελεγμένο γιατί λειτουργεί παρόμοιο με αυτό που δεν είναι επιλεγμένο IlegalArgumentException.

ΉδηOccupiedException δηλώνει έναν κατασκευαστή που παίρνει ένα όρισμα συμβολοσειράς που περιγράφει τον λόγο της εξαίρεσης. Αυτό το επιχείρημα προωθείται στο RuntimeException σούπερ γυαλί.

Η λίστα 2 παρουσιάζει Σανίδα.

Λίστα 2. Board.java

εισαγωγή java.awt.Color; εισαγωγή java.awt.Dimension; εισαγωγή java.awt.Graphics; εισαγωγή java.awt.Graphics2D; εισαγωγή java.awt.RenderingHints; εισαγωγή java.awt.event.MouseEvent; εισαγωγή java.awt.event.MouseAdapter; εισαγωγή java.awt.event.MouseMotionAdapter; εισαγωγή java.util.ArrayList; εισαγωγή java.util.List; εισαγωγή javax.swing.JComponent; δημόσια τάξη Διοικητικό Συμβούλιο επεκτείνει το JComponent {// διάσταση του τετραγώνου σκακιέρας (25% μεγαλύτερο από το πούλι) ιδιωτικό τελικό στατικό int SQUAREDIM = (int) (Checker.getDimension () * 1,25); // διάσταση του σκακιέρας (πλάτος 8 τετραγώνων) ιδιωτικό τελικό int BOARDDIM = 8 * SQUAREDIM; // προτιμώμενο μέγεθος ιδιωτικού στοιχείου Πίνακας DimPrefSize; // dragging flag - ορίζεται σε true όταν ο χρήστης πατά το κουμπί του ποντικιού πάνω από το πούλι // και διαγράφεται σε false όταν ο χρήστης απελευθερώνει το κουμπί του ποντικιού private boolean inDrag = false; // μετατόπιση μεταξύ συντεταγμένων εκκίνησης έλξης και συντεταγμένων κέντρου ελεγκτή ιδιωτικού int deltax, deltay; // αναφορά στο τοποθετημένο πούλι στην αρχή της μεταφοράς ιδιωτικού PosCheck posCheck; // κεντρική θέση του πούλι κατά την έναρξη του drag private int oldcx, oldcy; // λίστα αντικειμένων Checker και τις αρχικές τους θέσεις ιδιωτική Λίστα posChecks; δημόσιο συμβούλιο () {posChecks = new ArrayList (); dimPrefSize = νέα διάσταση (BOARDDIM, BOARDDIM); addMouseListener (νέο MouseAdapter () {@ Override public void mousePressed (MouseEvent me) {// Λάβετε συντεταγμένες ποντικιού κατά τη στιγμή του τύπου. int x = me.getX (); int y = me.getY (); // Εντοπισμός θέσης πούλι κάτω από το ποντίκι. για (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; return;}} @ Override public void mouseReleased (MouseEvent me) {// Όταν απελευθερωθεί το ποντίκι, διαγράψτε το inDrag σε εξέλιξη) εάν το inDrag είναι // ήδη ρυθμισμένο. if (inDrag) inDrag = false; else return; // Snap checker στο κέντρο του τετραγώνου. int x = me.getX (); int y = me.getY (); posCheck .cx = (x - deltax) / SQUAREDIM * SQUAREDIM + SQUAREDIM / 2; posCheck.cy = (y - deltay) / SQUAREDIM * SQUAREDIM + SQUAREDIM / 2; // Μην μετακινείτε το πούλι σε κατειλημμένο τετράγωνο. για (PosCheck posCheck : posChecks) εάν (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 = παλιά; } posCheck = null; χρωματίζω πάλι(); }}); // Συνδέστε έναν ακροατή κίνησης ποντικιού στη μικροεφαρμογή. Αυτός ο ακροατής ακούει // για συμβάντα μεταφοράς ποντικιού. addMouseMotionListener (νέο MouseMotionAdapter () {@Override public void mouseDragged (MouseEvent me) {if (inDrag) {// Ενημέρωση τοποθεσίας του κέντρου ελέγχου. posCheck.cx = me.getX () - deltax; posCheck.cy = me.getY ( ) - deltay; repaint ();}}}); } public void add (Checker checker, int row, int col) {if (σειρά 8) ρίξτε νέο IllegalArgumentException ("σειρά εκτός εύρους:" + σειρά); εάν (στήλη 8) ρίξτε νέο IllegalArgumentException ("col εκτός εμβέλειας:" + col); PosCheck posCheck = νέο PosCheck (); posCheck.checker = πούλι; posCheck.cx = (col - 1) * SQUAREDIM + SQUAREDIM / 2; posCheck.cy = (σειρά - 1) * SQUAREDIM + SQUAREDIM / 2; για (PosCheck _posCheck: posChecks) εάν (posCheck.cx == _posCheck.cx && posCheck.cy == _posCheck.cy) ρίξτε νέο AlreadyOccupiedException ("το τετράγωνο στο (" + σειρά + "," + col + ") είναι κατεχόμενο ); posChecks.add (posCheck); } @Override δημόσια διάσταση getPreferredSize () {return dimPrefSize; } @Override προστατευμένο void paintComponent (Graphics g) {paintCheckerBoard (g); για (PosCheck posCheck: posChecks) if (posCheck! = Board.this.posCheck) posCheck.checker.draw (g, posCheck.cx, posCheck.cy); // Σχεδιάστε το συρόμενο πούλι τελευταίο, έτσι ώστε να εμφανίζεται πάνω από οποιοδήποτε υποκείμενο // πούλι. εάν (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); // Χρώμα σκακιέρας. για (int σειρά = 0; σειρά <8; σειρά ++) {g.setColor (((σειρά & 1)! = 0)? Χρώμα.BLACK: Color.WHITE); για (int col = 0; col <8; col ++) {g.fillRect (col * SQUAREDIM, row * SQUAREDIM, SQUAREDIM, SQUAREDIM); g.setColor ((g.getColor () == Color.BLACK)? Color.WHITE: Color.BLACK); }}} // τοποθετημένο πούλι βοηθός ιδιωτικής κλάσης PosCheck {δημόσιο πούλι ελεγκτή; δημόσιο int cx; δημόσια inty; }}

Σανίδα εκτείνεται javax.swing.JComponent, που κάνει Σανίδα ένα στοιχείο Swing. Ως εκ τούτου, μπορείτε να προσθέσετε απευθείας ένα Σανίδα στοιχείο σε ένα παράθυρο περιεχομένου μιας εφαρμογής Swing.

Σανίδα δηλώνει SQUAREDIM και ΔΙΟΙΚΗΣΗ σταθερές που προσδιορίζουν τις διαστάσεις pixel ενός τετραγώνου και του πίνακα ελέγχου. Κατά την προετοιμασία SQUAREDIM, Επικαλούμαι Checker.getDimension () αντί να έχει πρόσβαση σε ισοδύναμο κοινό Ντάμα συνεχής. Ο Joshua Block απαντά γιατί το κάνω αυτό στο Θέμα # 30 (Χρησιμοποιήστε αριθμούς αντί για int σταθερές) της δεύτερης έκδοσης του βιβλίου του, Αποτελεσματική Java: "Προγράμματα που χρησιμοποιούν το int το σχέδιο enum είναι εύθραυστο. Επειδή int Τα ποσά είναι σταθερές χρόνου μεταγλώττισης, συγκεντρώνονται στους πελάτες που τις χρησιμοποιούν. Εάν το int που σχετίζεται με μια σταθερά enum αλλάζει, οι πελάτες της πρέπει να μεταγλωττιστούν εκ νέου. Αν δεν είναι, θα συνεχίσουν να τρέχουν, αλλά η συμπεριφορά τους θα είναι απροσδιόριστη. "

Λόγω των εκτενών σχολίων, δεν έχω πολλά περισσότερα να πω Σανίδα. Ωστόσο, σημειώστε το ένθετο PosCheck τάξη, η οποία περιγράφει ένα τοποθετημένο πούλι αποθηκεύοντας ένα Ντάμα αναφορά και οι κεντρικές συντεταγμένες της, οι οποίες σχετίζονται με την επάνω αριστερή γωνία του Σανίδα συστατικό. Όταν προσθέτετε ένα Ντάμα αντικείμενο στο Σανίδα, αποθηκεύεται σε νέο PosCheck αντικείμενο μαζί με την κεντρική θέση του πούλι, το οποίο υπολογίζεται από την καθορισμένη σειρά και στήλη.

Η λίστα 3 παρουσιάζει Ντάμα.

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