ΠΑΝΕΠΙΣΤΗΜΙΟ ΙΩΑΝΝΙΝΩΝ ΤΜΗΜΑ ΠΛΗΡΟΦΟΡΙΚΗΣ ΑΝΤΙΚΕΙΜΕΝΟΣΤΡΕΦΗΣ ΠΡΟΓΡΑΜΜΑΤΙΣΜΟΣ Προγραμματιστική Άσκηση 1 (hw1) Εκφώνηση: 21 Οκτωβρίου 2012 Παράδοση: Κυριακή 18 Νοεμβρίου 2012, 23.59 Το ντόμινο (dominoes) είναι ένα επιτραπέζιο παιχνίδι που παίζεται από δύο παίχτες. Το παιχνίδι αποτελείται από 28 πούλια (bones στην αγγλική) τα οποία φαίνονται στην εικόνα δεξιά. Το κάθε πούλι έχει επάνω του 2 αριθμούς που κυμαίνονται από το 0 ως και το 6. Στην αρχή του παιχνιδιού μοιράζονται από 7 πούλια σε κάθε παίχτη και οι παίκτες παίζουν εναλλάξ, τοποθετώντας πούλια στο κέντρο του παιχνιδιού, όπου υπάρχει μια λίστα από τοποθετημένα πούλια, το ένα μετά το άλλο. Ουσιαστικά η κεντρική λίστα του παιχνιδιού είναι μια συνεχής γραμμή (για όσους είστε εξοικειωμένοι με το παιχνίδι, η γραμμή στον πραγματικό κόσμο κατά καιρούς στρίβει: αυτό γίνεται γιατί τελειώνει το τραπέζι ή επειδή είναι βολικό ή όμορφο για τους παίκτες στην ουσία, όμως, πρόκειται για μία συνεχή γραμμή από πούλια). Όταν έρθει η σειρά του να παίξει, ένας παίκτης πρέπει να τοποθετήσει στη λίστα ένα πούλι, που έχει έναν από τους δύο του αριθμούς ίσο με τον αριθμό που βρίσκεται στη δεξιά θέση του τελευταίου πουλιού που τοποθέτησε ο προηγούμενος χρήστης. Για παράδειγμα, έστω ότι (α) ο παίχτης 1 τοποθέτησε το πούλι [5:2] στην τελευταία του κίνηση και το τέλος της λίστας μοιάζει με.[3:4][4:5] [5:2] (παρατηρήστε ότι το δεξιότερο νούμερο στη λίστα είναι το 2) (β) ο παίχτης 2 έχει στα χέρια του τα πούλια [1:2], [0:4], [2:6] τότε, ο παίχτης 2 έχει να διαλέξει είτε το [1:2] είτε το [2:6] για να βάλει στη λίστα, καθώς αυτά είναι τα πούλια που περιέχουν το 2, που ταιριάζει με τον τελευταίο αριθμό της λίστας. Και στις δύο περιπτώσεις, το πούλι θα τοποθετηθεί με τέτοιο τρόπο ώστε το 2 να είναι αριστερά. Τα πούλια του ντόμινο Εικόνα από το http://en.wikipedia.org/wiki/dominoes Με άλλα λόγια, αν ο παίχτης επιλέξει το [1:2] θα τοποθετηθεί ως [2:1] στο κέντρο και η λίστα θα γίνει.[3:4][4:5] [5:2][2:1] Αλλιώς, αν πέσει το [2:6] θα πρέπει να μπει ως έχει και η λίστα θα γίνει.[3:4][4:5] [5:2][2:6] Είναι σημαντικό να προσεχθεί, δηλαδή, ότι τα πούλια μπορεί να περιστραφούν και να τοποθετηθούν στη λίστα είτε από τη μία, είτε από την άλλη μεριά τους αρκεί ο αριθμός αριστερά να είναι ο ίδιος με τον αριθμό που έχει το τελευταίο πούλι της λίστας στα δεξιά του (ο τελευταίος αριθμός της λίστας, δηλαδή). Θα φτιάξουμε μια απλούστατη εκδοχή του ντόμινο (όχι την συνήθη) στην οποία το παιχνίδι τερματίζει όταν: - Όλοι οι παίχτες βάλουν όλα τα πούλια, οπότε το παιχνίδι έρχεται ισοπαλία - Ένας από τους παίχτες δεν έχει πούλι να κολλήσει στο τέλος της λίστας, οπότε και χάνει το παιχνίδι (κανονικά, παίζει ο άλλος και νικά όποιος βάλει πρώτος όλα του τα πούλια, αλλά εμείς θα το απλοποιήσουμε) 1
Το ζητούμενο της άσκησης Το ζητούμενο είναι να κατασκευασθεί ένα πρόγραμμα που επιτελεί τις ακόλουθες λειτουργίες: Κατ αρχήν, θα επιτρέπει στο χρήστη να παίζει με αντίπαλο τον ΗΥ. Το αποτέλεσμα της κάθε κίνησης θα αναπαριστάται οπτικά στην οθόνη. Επίσης, κάθε κίνηση θα καταγράφεται και σε ένα HTML αρχείο, ώστε, όταν το παιχνίδι λήξει, να μπορεί να δει κανείς τις κινήσεις με μεγαλύτερη ευκολία. Ακόμα, το πρόγραμμα θα πρέπει να καταγράφει την αρχική ανάθεση από πούλια και τις κινήσεις που παίχτηκαν σε ένα log αρχείο, με συγκεκριμένη δομή που θα προδιαγραφεί παρακάτω. Η ύπαρξη log αρχείων μας επιτρέπει να μπορούμε να κάνουμε replay ένα παιχνίδι και να δείξουμε την εξέλιξή του στο χρήστη. Όπως θα δείτε στις οδηγίες (τμηματικής) υλοποίησης: don t panic! Αν υλοποιήσετε και ελέγξετε τον κώδικά σας σταδιακά, το project θα υλοποιηθεί εύκολα. Analysis and Design Στη συνέχεια, παραθέτουμε μια βασική σχεδίαση για το πώς μπορούμε να επιτύχουμε τους παραπάνω στόχους. Οι κλάσεις που σας δίνουμε είναι δύο ειδών και αντανακλούν τη σχεδίαση του συστήματος: - Από ένα engine class για κάθε βασική λειτουργία - Από μια κλάση για κάθε βασικό στοιχείο του παιχνιδιού Οι engine classes θα χρησιμοποιούν βασικά αντικείμενα για να κάνουν τη δουλειά τους. Καλό είναι να επανέλθετε σε αυτό το σημείο, αφού θα έχετε διαβάσει ολόκληρη την εκφώνηση. Προσέξτε πώς αναθέτουμε, για κάθε λειτουργία που καταγράφηκε στις απαιτήσεις, και μια engine class που σκοπό έχει να εξυπηρετεί τη λειτουργία αυτή. Βασικές κλάσεις που αναπαριστούν τα στοιχεία της πραγματικότητας Οι βασικές κλάσεις που αναπαριστούν το φυσικό κόσμο, όπως αυτός εμπλέκεται σε ένα παιχνίδι ντόμινο είναι οι εξής: Player τύπος (0 για άνθρωπο και 1 για ΗΥ), πίνακας από pointers σε πούλια που του ανατίθενται στην αρχική μοιρασιά Ανάθεση πουλιών σε παίχτη στην αρχή του παιχνιδιού, επιστροφή αναγνωριστικού για το αν είναι άνθρωπος ή ΗΥ, /* Ενδεχομένως (όχι υποχρεωτικά) να χρειαστεί: αφαίρεση του πουλιού της τελευταίας κίνησης από το πινακάκι του παίχτη (βλ. και παρακάτω στην κλάση Bone) */ Bone (πούλι, δηλ.) ένα μοναδικό id, firstnum, secondnum (οι δύο αριθμοί του πουλιού ΠΡΟΣΟΧΗ: ΠΑΝΤΑ βάζουμε το μικρότερο πρώτο), φορά τοποθέτησης (0 σημαίνει ότι το πούλι έχει τοποθετηθεί κανονικά, άρα αριστερά είναι το firstnum, 1 σημαίνει τοποθέτηση με περιστροφή και άρα αριστερά είναι το secondnum, -1 σημαίνει δεν έχει τοποθετηθεί) 2
Έλεγχος για το αν ταιριάζει με ένα άλλο πούλι, left(), right() (επιστρέφουν τον αριστερό και δεξιό αριθμό αν το πούλι έχει τοποθετηθεί και -1 αν όχι), status (επιστρέφει τη φορά τοποθέτησης), isinplayerlist (1 αν είναι στο χέρι κάποιου παίχτη, 0 αλλιώς), isincentrallist, wasneverinthegame (κατ αντιστοιχία) //αν σας βολεύει, επίσης: μέθοδοι για ευθεία και αντίστροφη εκτύπωση ανάλογα με τη φορά του πουλιού Move Ένα id για τον αύξοντα αριθμό της κίνησης, ένα status (-1 αν η κίνηση σηματοδοτεί ότι ο παίχτης που έπρεπε να την κάνει δεν μπόρεσε να παίξει ένα πούλι και άρα το παιχνίδι λήγει, και 1 αν μπορεί), ένας pointer στον παίχτη που κάνει την κίνηση και ένας pointer στο πούλι που εμπλέκεται [Προαιρετικά, αλλά μάλλον βολικά] Από τις μεθόδους του παίχτη και του πουλιού, μπορείτε να εξάγετε στην Move τις αντίστοιχες μεθόδους, ώστε όταν αποτιμάται η κατάσταση της λίστας, να γίνονται οι μεταβολές στα πούλια και τους παίχτες κατ ευθείαν από τη Move και όχι σε δύο εντολές. Τέλος, για άλλες έννοιες που προκύπτουν στον φυσικό κόσμο, όπως η γραμμή του παιχνιδιού, ο τελευταίος παίκτης που έπαιξε, η κατάσταση του παιχνιδιού, η λίστα των κινήσεων που έχει παιχεί μέχρι στιγμής, κα., δέστε ευθύς αμέσως στην κεντρική κλάση του παιχνιδιού: DominoesEngine. Κλάσεις που δομούν το λογισμικό του παιχνιδιού ΥΠΟΔΕΙΞΗ: διαβάστε ΟΛΕΣ τις κλάσεις, και όπως διαβάζετε, σημειώστε σε ένα χαρτί (α) τι κάνει η κάθε μία + (β) τη λίστα από τις μεθόδους για κάθε κλάση (ΟΚ, θέλετε ένα μεγάλο χαρτί). Όταν τελειώσετε το διάβασμα, ξαναδιαβάστε (όσες φορές χρειαστεί). Για τις δύο βασικές λειτουργίες, play και replay, δοκιμάστε να φτιάξετε ένα σκετσάκι του (ψευδο)κώδικα (όπως θα δείτε, σας δίδεται ένα πρόχειρο σχέδιο αλλά θα πρέπει να το αποκρυσταλλώσετε στο μυαλό σας) DominoesEngine Η πιο βασική κλάση του παιχνιδιού, η οποία είναι υπεύθυνη (α) να ενσωματώνει τα επί μέρους στοιχεία του λογισμικού (όπως αυτά παρατίθενται στη συνέχεια) και (β) να προσφέρει τις μεθόδους που επιτελούν τη βασική λειτουργικότητα του παιχνιδιού. 2 παίχτες (ένας άνθρωπος, ένας ΗΥ), ένας πίνακας για τα πούλια, ένας πίνακας κινήσεων, ένας visualizer για την κονσόλα κι ένας για το αρχείο HTML, ένας logger. Είναι στη διάκρισή σας αν θα τα ορίσετε ως pointers ή ως απλά ενσωματωμένα αντικείμενα μέσα σε ένα DominoesEngine. Επιπλέον, μπορείτε να προσθέσετε κι ότι άλλο πεδίο πιστεύετε ότι σας βοηθά για να κάνετε τη δουλειά σας (π.χ., ποια είναι ο δεξιότερος αριθμός της λίστας, ποιο το αποτέλεσμα του παιχνιδιού, ένας pointer στον παίχτη που νίκησε, ή/και ότι άλλο μπορείτε να βρείτε χρήσιμο). 1. Προφανώς χρειάζεται ο (ένας φτάνει) constructor (βασική απόφαση: τι θα κάνει όταν ξεκινά?). 2. Επίσης χρειάζεστε και setter / getter μεθόδους για τα πεδία που αποτελούν την εσωτερική δομή ενός αντικειμένου της κλάσης DominoesEngine. Είναι μάλλον βολικό να προσθέσετε στην DominoesEngine κάποιες από τις μεθόδους των επί μέρους συστατικών της που θα καλέσει η main, ώστε να μειώσετε την εξάρτηση της main από τον υπόλοιπο κώδικα. ΥΠΟΜΝΗΣΗ: ξαναδείτε αυτό το σημείο αφού θα έχετε διαβάσει τις οδηγίες για τη main. Επίσης, μπορείτε προαιρετικά να φτιάξετε από μία μέθοδο play / replay για να ενσωματώνει όλη τη λειτουργία του DominoesEngine, ώστε η main να είναι όσο πιο μινιμαλιστική γίνεται. 3. Ένα από τα πράγματα που θα χρειαστεί να κάνετε (είτε με χωριστή μέθοδο, είτε στον constructor) είναι να φορτώσετε τα πούλια από το σχετικό αρχείο στον πίνακα με τα πούλια. Σας δίδεται το (ημιτελές) αρχείο bonestoload.ascii για να τα φορτώσετε. 3
ΜΕΘΟΔΟΙ ΣΧΕΤΙΚΕΣ ΜΕ ΤΟ PLAY 4. Η μέθοδος assignbonestoplayers αναθέτει με τυχαίο τρόπο πούλια στους παίχτες. 5. Η μέθοδος pickamove επιστρέφει μια κίνηση για ένα παίχτη (θα χρειαστεί να τον λάβει ως παράμετρο) και συμπεριφέρεται διαφορετικά ανάλογα με το χρήστη: Αν ο χρήστης είναι ο ΗΥ, πρέπει να εξετάσει ένα ένα τα πούλια του και να διαλέξει το πρώτο από αυτά που ταιριάζει στην κεντρική γραμμή (κάντε το έτσι αρχικά, και αν βρείτε καλύτερη στρατηγική την φτιάχνετε μετά). Αν ο ΗΥ δεν έχει πούλι να παίξει, η μέθοδος επιστρέφει μια κίνηση με αρνητικό status Αν ο χρήστης είναι ο άνθρωπος, η μέθοδος καλεί τον ConsoleVisualizer να παρουσιάσει τα διαθέσιμα πούλια του, λαμβάνει είσοδο για το πιο πούλι να παίξει και με ποια φορά και επιστρέφει το σχετικό αντικείμενο Move. Αν ο χρήστης δεν έχει κάποια κίνηση να κάνει, επιστρέφει μια κίνηση με αρνητικό status. Συνολικά: αν ο παίχτης (of any kind) έχει πούλι να παίξει, επιστρέφεται ένα αντικείμενο Move με τα σωστά στοιχεία για τον παίχτη και το πούλι αλλιώς, επιστρέφεται ένα αντικείμενο Move με αρνητικό status. ΟΔΗΓΙΑ: μη φάτε χρόνο αρχικά, για να ελέγξετε αν ο άνθρωπος διαλέγει πούλι που κολλά σωστά στη γραμμή. Απλώς θυμηθείτε να δίνετε τη δυνατότητα στον παίχτη-άνθρωπο να επιλέξει ότι δεν έχει κίνηση. Βάλτε μια υποσημείωση στον κώδικα και ξαναεπισκεφθείτε το σημείο αυτό αφού θα έχετε κατασκευάσει ένα working prototype της εφαρμογής, τότε μπορείτε (α) να ελέγχετε αν το επιλεγέν πούλι είναι σωστό κι αν όχι να ξαναρχίζετε τη διαδικασία και (β) να υποδεικνύετε στον παίχτη-άνθρωπο ποια από τα πούλια του μπορεί να μπουν στην κεντρική λίστα (όπως επίσης κι αν δεν υπάρχει κανένα τέτοιο). Αυτό δε σημαίνει ότι ο έλεγχος δεν είναι σημαντικός για την ορθότητα του παιχνιδιού αντιθέτως όμως, χάριν οικονομίας, μπορείτε στην αρχή να προχωρήσετε λίγο την υλοποίηση και να ολοκληρώσετε μικρά κομμάτια σαν κι αυτό, αργότερα. MΕΘΟΔΟΙ ΣΧΕΤΙΚΕΣ ΜΕ ΤΟ REPLAY 6. Η μέθοδος loadmovesfromfile διαβάζει την ανάθεση πουλιών στους παίχτες και τις κινήσεις που παίχτηκαν και ενημερώνει τους πίνακες πουλιών και κινήσεων ΓΕΝΙΚΕΣ ΜΕΘΟΔΟΙ 7. Η μέθοδος updategamestatus παίρνει ως είσοδο την τελευταία κίνηση και ενημερώνει τα σχετικά αντικείμενα και πεδία όλων των αντικειμένων που εμπλέκονται. Για παράδειγμα, πρέπει να ενημερώνει το σύνολο με τα διαθέσιμα πούλια του παίχτη που έπαιξε (θυμηθείτε: το πούλι που μόλις μπήκε στη λίστα πρέπει να αλλάξει status), τον αριθμό των κινήσεων, την άκρη της λίστας (αν έχετε τέτοιο πεδίο), και κυρίως το αν το παιχνίδι λήγει με την κίνηση αυτή, κι αν ναι, ποιος είναι ο νικητής. 8. Η μέθοδος visualizemove καλεί τον ConsoleVisualizer για να οπτικοποιήσει την τελευταία κίνηση στην κονσόλα (πρακτικά να τυπώσει μια τριάδα συμβολοσειρών, όπως φαίνεται στο παρακάτω παράδειγμα) Player 2 added [2:1] to the game s line The line now is: [3:4][4:5][5:2][2:1] Status: Player 1 to play Με βάση τα παραπάνω: για να κάνουμε replay πρέπει αρχικά να τρέξουμε τη loadmovesfromfile και μετά, όσο υπάρχουν κινήσεις στη λίστα, να διαλέγουμε την επόμενη κίνηση, να καλούμε την udpategamestatus και τη visualizemove. Για να κάνουμε play πρέπει, όσο το παιχνίδι δεν έχει λήξει (αυτό μπορεί να είναι ένας ατέρμονος βρόγχος από τον οποίο να κάνουμε break μόλις διαπιστώσουμε ότι έχει λήξει το παιχνίδι, ή να είναι ένας βρόγχος με έλεγχο στην αρχή), να εκτελούμε την τριάδα {pickamove + updategamestatus + visualizeconsole} πρώτα για τον παίχτη 1 και μετά για τον παίχτη 2. 4
Και στις δύο περιπτώσεις θα χρειαστεί να συμπληρώσουμε το τέλος του παιχνιδιού με το σωστό διαγνωστικό και την καταγραφή σε HTML. Αν κάνουμε play θα πρέπει να ενημερώσουμε και το log. ConsoleVisualizer Η κλάση αυτή σκοπό έχει να ασχολείται με την οπτικοποίηση γεγονότων όπως τρέχει το παιχνίδι. Συγκεκριμένα, πρέπει να έχει από μία μέθοδο για κάθε ένα από τους παρακάτω σκοπούς: Η μέθοδος presentchoices δείχνει στον άνθρωπο-παίχτη ποια πούλια του είναι διαθέσιμα τη συγκεκριμένη χρονική στιγμή και λαμβάνει από το χρήστη μια επιλογή για το ποιο πούλι διάλεξε και με ποια φορά (εδώ πρέπει να αυτοσχεδιάσετε για το πώς θα μεταφέρεται η επιλογή στην κεντρική engine) Η μέθοδος presentplayedmove πρέπει να παρουσιάζει την κίνηση που μόλις παίχτηκε (από οποιονδήποτε παίχτη). Πρέπει να παρουσιάζεται η εικόνα της κεντρικής γραμμής του παιχνιδιού (όπως δείξαμε λίγο πιο πάνω), και, επιπλέον, αν κάποιος παίχτης δεν είχε κίνηση να παίξει, να παρουσιάζεται ένα Χ στην λίστα της κεντρικής γραμμής Οι μέθοδοι presentintro και presentconclusion τρέχουν μία φορά και παρουσιάζουν αφενός ένα μήνυμα υποδοχής και αφετέρου (και κυριότερον) την έκβαση του παιχνιδιού στο τέλος. Εσκεμμένα, για την κλάση αυτή ΔΕΝ σας δίδονται πιο πολλές σχεδιαστικές οδηγίες (π.χ., σχετικά με τα πεδία της κλάσεως), ώστε να αναλάβετε τη σχεδίαση εσείς. HTMLVisualizer Η κλάση αυτή σκοπό έχει να παρουσιάσει σε ένα αρχείο HTML το πώς εξελίχθηκε ένα παιχνίδι. Ανοίξτε με ένα text editor (ΟΧΙ browser) το (ημιτελές) αρχείο htmloutput.html και δείτε την εσωτερική δομή του αρχείου: - Ένα αρχικός header (που είναι πάντα ο ίδιος) - Μία απαρίθμηση των μοιρασθέντων πουλιών σε κάθε παίχτη, στην αρχή του παιχνιδιού - Η λίστα των κινήσεων Εσκεμμένα έχουμε χρησιμοποιήσει τα em και strong styles για να ξεχωρίζουν τα πούλια και οι κινήσεις των δύο παιχτών. Ανοίξτε το αρχείο με ένα browser για να δείτε πώς φαίνεται οπτικά το output. Για να φτιάξετε το αρχείο θα χρειαστεί να ετοιμάσετε τέσσερις συμβολοσειρές για να γραφτούν στο αρχείο: (α) η πρώτη περιέχει τα σταθερά στοιχεία στο κομμάτι <head> </head>, (β) η δεύτερη τη λίστα των κινήσεων όπως παίχτηκαν (γ) η τρίτη τη λίστα των πουλιών του παίχτη 1 (δ) η τέταρτη τη λίστα των πουλιών του παίχτη 2 Τα (γ) και (δ) πρέπει να εξαχθούν μαζί με το (β), όπως περνάμε μία μία τις κινήσεις (για παράδειγμα, προσέξτε ότι ο παίχτης έβαλε το πούλι 2:5 ως 5:2 στην α κίνηση, αλλά στην παρουσίαση των πουλιών του κάθε παίχτη, το εν λόγω πούλι εμφανίστηκε ως 2:5) Για να επιτευχθούν τα παραπάνω, ένα αντικείμενο της κλάσης HTMLVisualizer, θα πρέπει να εκτελέσει τη μέθοδο execute, η οποία παίρνει ως παράμετρο τη λίστα των κινήσεων που παίχτηκαν (και ότι άλλο κρίνετε εσείς σκόπιμο) και ετοιμάζει το αρχείο. Η κλάση μπορεί να έχει κάποια static members για το πώς θα εμφανιστούν τα στατικά στοιχεία (π.χ., ότι στον παίχτη 1 αντιστοιχεί η μορφοποίηση <em>, η οποία έχει ένα συγκεκριμένο format). Στον constructor πρέπει να προσέξετε τι θα φτιάξετε (π.χ., το string της περίπτωσης (α) μπορεί να φτιαχτεί ήδη από τον constructor, μπορεί να θέλετε ειδικό όνομα για το αρχείο, κλπ). Μπορείτε να προσθέσετε ότι άλλες μεθόδους θέλετε (π.χ., μεθόδους για να διαμορφώσετε δυναμικά την παρουσίαση ανά παίκτη (χρώμα, font, font-weight, ). Logger Η κλάση Logger καταγράφει τις κινήσεις του παιχνιδιού όπως παίχτηκαν και είναι μια απλούστερη μορφή της οπτικοποίησης σε HTML. Μπορείτε να επιτρέψετε στους παίχτες να φτιάξουν νέο log ή να 5
κάνουν append σε ένα υπάρχον. Το όνομα του log αρχείου θα πρέπει να αποφασίσετε εσείς πώς θα το ρυθμίσετε. main() H main, φυσικά, δεν είναι κλάση. Θα πρέπει, όμως, να υπάρχει και να κάνει τις εξής (εσκεμμένα ελάχιστες) λειτουργίες: (α) Αρχικά, η main ορίζει/φτιάχνει ένα αντικείμενο DominoesEngine (με όλα όσα το δομούν + με τη φόρτωση των στοιχείων για τα πούλια από το σχετικό αρχείο) (β) Μετά, καλείται ο χρήστης να αποφασίζει αν θα κάνει replay ή play (γ) Στην περίπτωση replay, πρέπει η main να φορτώνει το σχετικό log αρχείο στον πίνακα κινήσεων μέσω της κατάλληλης μεθόδου, να παίζει τις κινήσεις στην κονσόλα ως τη λήξη του παιχνιδιού, να ανακηρύσσει το νικητή και να ενημερώνει και το HTML αρχείο παρουσίασης (δ) Στην περίπτωση play, η main πρέπει να παίζει το παιχνίδι ως τη λήξη του, να ανακηρύσσει το νικητή και να ενημερώνει το log file και το HTML αρχείο παρουσίασης Όλα αυτά πρέπει να γίνονται μέσω μεθόδων της DominoesEngine!! Έτσι, η main είναι της τάξης μεγέθους των 10-20 γραμμών και γενικά καλεί τις μεθόδους του αντικειμένου της κλάσης DominoesEngine που κατασκευάζει. Γιατί? Τι κερδίζουμε έτσι? Έτσι, η main, που είναι μια client module, εξαρτάται από μία μόνο κλάση και έχει ελάχιστες εξαρτήσεις από (σχετικά λίγες) μεθόδους της. Αυτό επιτρέπει μεγάλη ευελιξία στην συντήρηση, εσωτερική αναδιοργάνωση και γενικά εξέλιξη του κώδικα, καθώς και την κατανοησιμότητα του κώδικα. Πώς να προχωρήσετε στην υλοποίηση Για να μπορέσετε να αναπτύξετε το πρόγραμμά σας σε στάδια, σας προτείνεται η ακόλουθη διαδικασία. Σε κάθε βήμα σας δίνεται και μια υπόδειξη για το τι πρέπει να ελέγχετε ώστε να είστε (όσο το δυνατόν) σίγουροι ότι ο κώδικας είναι σωστός. Part 1: load the bones from a file Σε πρώτη φάση το πρόγραμμά σας θα πρέπει απλώς να φορτώνει τον πίνακα αναφοράς από bones της κλάσης DominoesEngine από ένα αρχείο. Έλεγχος: Ελέγξτε ότι η φόρτωση έγινε σωστά, περνώντας ένα ένα τα πούλια που φορτώθηκαν και τυπώνοντας τα στοιχεία τους. Part 2: replay, testendofgame and visualization tasks Αμέσως μετά προσπαθήστε να υλοποιήσετε τη διαδικασία replay. Σας δίδονται log files από παιχνίδια που παίχτηκαν στο παρελθόν. Για κάθε παιχνίδι από αυτά, χρησιμοποιήστε το αντίστοιχο αρχείο για να φορτώσετε στον πίνακα από Moves τις κινήσεις που παίχτηκαν και στον πίνακα πουλιών κάθε παίχτη τα πούλια που του ανατέθηκαν. Υλοποιήστε και ελέγξτε την updategamestatus. Μετά, υλοποιήστε τη διαδικασία οπτικοποίησης σε κονσόλα και HTML. Έλεγχος: η κονσόλα (output του προγράμματος) και το log file (input του προγράμματος) πρέπει να συμφωνούν ως προς τη σειρά των κινήσεων, την εκτύπωση και το τελικό αποτέλεσμα. Ένα παιχνίδι έχει το πολύ 14 κινήσεις => είναι εύκολα ελέγξιμο με το χέρι. Ομοίως για το HTML output. /* Φυσικά, ο έλεγχος μπορεί να γίνει τμηματικά: (α) αν φορτώθηκαν σωστά τα Move, (β) αν κάθε παίχτης πήρε τα σωστά πούλια, (γ) αν οι κινήσεις φτιάχτηκαν με τη σωστή σειρά και οι pointers σε παίχτες και 6
πούλια μπήκαν σωστά, (δ) αν ο έλεγχος game status δουλεύει ορθά και (ε) αν τυπώνονται σωστά οι κινήσεις. */ Part 3: play, pick a move Σε τρίτη φάση, μπορείτε να ασχοληθείτε με την υλοποίηση του κανονικού παιξίματος. Έχετε να υλοποιήσετε τον κώδικα για την κίνηση του ανθρώπου και τον κώδικα για την κίνηση του ΗΥ μέσα στην μέθοδο pickamove. Η διαδικασία κανονικού παιξίματος είναι ίδια με τη διαδικασία replay με δύο διαφορές: (α) δεν έχουμε αρχείο να μεταφράσουμε σε πίνακα από Moves και (β) πρέπει σε κάθε γύρο, για κάθε παίχτη να εκτελείται η pickamove πριν αποφασίσουμε αν ο παίχτης έφτασε σε αδιέξοδο, τυπώσουμε, κλπ. Έλεγχος: διαλέξτε μικρά σε μέγεθος παιχνίδια (π.χ., αυτά των log files που σα δίδονται) και δοκιμάστε τα. Οι κεντρικοί έλεγχοι αφορούν την pickamove και την updategamestatus (ξανά ) Part4: Logging Στο τέλος, μπορείτε να προσθέσετε και το Logger στη διαδικασία, ο οποίος να καταγράφει την αρχική ανάθεση και τις κινήσεις. Ο έλεγχος είναι ο αντίστοιχος με το part 2 για την οπτικοποίηση (αντίστροφα όμως: ότι βγήκε στην κονσόλα πρέπει να έχει μπει και στο αρχείο). Δομή του κώδικα Μην προσπαθήσετε να τα φτιάξετε όλα από την αρχή, καθώς αυτό είναι πολύ πιο δύσκολο. Αν δυσκολεύεστε, μπορείτε στην αρχή να έχετε τα πάντα σε ένα και μόνο (1) αρχείο.cpp ή.cc. Στο τέλος όμως, θα πρέπει να έχετε για κάθε κλάση τα αρχεία.h και.cpp και ένα makefile για την μετάφραση της εφαρμογής σας (θα εξηγηθούν διεξοδικά στο μάθημα, don t worry). Για όλες τις κλάσεις που παραθέτουμε, υπονοείται πως θα ορίσετε σωστά setters / getters/ constructors, και όποιες άλλες κλάσεις κρίνετε σκόπιμο. ΥΠΟΧΡΕΩΤΙΚΑ όλα τα πεδία των κλάσεων είναι private και όσες μέθοδοι κρίνετε σκόπιμο public. Μπορείτε αν θέλετε να έχετε ένα κοινό αρχείο με σταθερές (π.χ., αριθμός πουλιών, ονόματα αρχείων κλπ), αλλά δεν είναι απαραίτητο: είναι επίσης βολικό, όλα αυτά να προκύπτουν ως πεδία των κατάλληλων κλάσεων. Επιπλέον στοιχεία & τελευταίες συμβουλές Πρέπει: (α) να κάνετε το πρόγραμμα ΜΟΝΟΙ σας, (β) να το πάτε all the way to the end (δε φτάνει να περνάει τον compiler, πρέπει να τρέχει και σωστά), (γ) να το δομήσετε όπως πρέπει (στυλ, δομή αρχείων, ). Προφανώς και θα συζητήσετε μεταξύ σας όμως, ο καθένας πρέπει να εργαστεί μόνος του (όπως άλλωστε γίνεται και στην πράξη). Για όσους σκέφτονται να βρουν άλλους να τους φτιάξουν (φίλους, επ αμοιβή, ) διότι φλέγονται να περάσουν κι αδιαφορούν για το πώς θα κερδίζουν το ψωμί τους σε μερικά χρόνια Υπόμνηση: - Ο προγραμματισμός είναι μια έντονα βιωματική δεξιότητα. - κάθε άσκηση πιάνει μία μονάδα και το διαγώνισμα 7. Για να μπορέσετε να θεμελιώσετε μια καριέρα αξιώσεων στο μέλλον, αλλά και για να πιάσετε τη βάση στο διαγώνισμα (προαπαιτούμενο για να περάσετε) είναι υποχρεωτικό να έχετε γράψει πολλά χιλιόμετρα κώδικα, με τα χαρακτηριστικά που προδιαγράφηκαν παραπάνω. Το να καταλαβαίνετε αυτό που έγραψε και σας εξήγησε ο φίλος σας που είναι σαΐνι δε θα αρκέσει 7