Professional Documents
Culture Documents
Προγραμματισμός
μέσα από παραδείγματα
στη γλώσσα Python
Βασίλης Βασιλάκης
Γιώργος Μπουκέας
pythonies.mysch.gr
twitter.com/pythonies
github.com/boukeas/pythonies
Οδηγίες Χρήσης 0
19 Οκτωβρίου 2015
09:10
1
ΟΔΗΓΙΕΣ ΧΡΗΣΗΣ 2
1
Η ΑΠΑΝΤΗΣΗ 2
Μέχρι στιγμής ο Deep Thought είναι λίγο απρόσωπος: απλά ανακοινώνει Σχήμα 1.2: Η τιμή της μεταβλητής
wait προέκυψε από τον υπολογισμό
την Απάντηση. Ο χρήστης δεν θα αλληλεπιδρά με το πρόγραμμα;
της τιμής μιας αριθμητικής έκφρασης.
Η ροή της πληροφορίας ανάμεσα στο πρόγραμμα και τον χρήστη εί-
ναι μονόδρομη. Στις περισσότερες περιπτώσεις ένα πρόγραμμα δεν
περιορίζεται στην εμφάνιση μηνυμάτων, αλλά χρειάζεται και να κά-
νει ερωτήσεις στον χρήστη, να του ζητήσει τιμές.
Θα προγραμματίσουμε τον Deep Thought έτσι ώστε να ζητάει το
όνομα του χρήστη και να τον καλημερίζει κατάλληλα. Έτσι θα έχουν
ένα στοιχειώδη διάλογο, πριν από την ανακοίνωση της Απάντησης.
Για να ζητήσουμε μια τιμή από το
2 # είσοδος ονόματος
πληκτρολόγιο χρησιμοποιούμε την
3 print("Πώς σε λένε;") input(), η οποία επιστρέφει μια
4 name = input() αλφαριθμητική τιμή: το κείμενο που
5 # χαιρετισμός πληκτρολογήθηκε από τον χρήστη.
6 print("Καλημέρα", name) Εδώ, στην τιμή αυτή δίνεται το όνομα
name.
Η ΑΠΑΝΤΗΣΗ 4
answer/src/answer.4.py
Εναλλακτικά:
# είσοδος ονόματος Η εμφάνιση ενός μηνύματος πριν την
ανάγνωση τιμών είναι τόσο συνηθι-
name = input("Πώς σε λένε; ")
σμένη που η input() μπορεί να δεχθεί
# χαιρετισμός ανάμεσα στις παρενθέσεις το μήνυμα
print("Καλημέρα", name) που πρέπει να εμφανιστεί.
Έχεις Ώρα;
Αν ένας χρήστης εκτελέσει το βράδι το πρόγραμμα που έχουμε φτιάξει
για τον Deep Thought, τότε θα δυσκολευτεί να πιστέψει ότι πρόκειται για
έναν υπερυπολογιστή που γνωρίζει την Απάντηση για τη Ζωή, το Σύμπαν
και τα Πάντα, αφού θα τον χαιρετίσει βραδιάτικα με ένα “Καλημέρα”.
Tο πρόγραμμα πρέπει να γνωρίζει την ώρα της ημέρας. Ευτυχώς ο
υπολογιστής μας ξέρει τι ώρα είναι, οπότε απλά θα τον ρωτήσουμε.
Η localtime() της βιβλιοθήκης time
5 # ανάκτηση τοπικής ώρας συστήματος
επιστρέφει πληροφορίες για την ημε-
6 hour = time.localtime().tm_hour ρομηνία και την ώρα του συστήμα-
τος. Οι συντακτικές λεπτομέρειες δεν
Εδώ όμως το ουσιαστικό πρόβλημα είναι ότι οι εντολές που θα πρέ-
έχουν σημασία αυτή την στιγμή.
πει να εκτελέσει το πρόγραμμά μας δεν είναι πάντα οι ίδιες. Θα πρέ-
πει να προγραμματίσουμε τον Deep Thought έτσι ώστε να ελέγχει
την ώρα της ημέρας και να εμφανίζει διαφορετικό μήνυμα ανάλογα
με το αποτέλεσμα του ελέγχου.
Η if συνοδεύεται από μια συνθήκη,
7 # εμφάνιση χαιρετισμού ανάλογα με την ώρα
η οποία ελέγχεται κατά την εκτέλεση
8 if hour < 14: του προγράμματος και μπορεί να είναι
9 print("Καλημέρα", name) αληθής (True) ή ψευδής (False).
10 else: Οι εντολές που ακολουθούν την if εί-
11 print("Καλησπέρα", name) ναι στοιχισμένες δεξιότερα από αυτή.
answer/src/answer.5.py Η στοίχιση επιτυγχάνεται εισάγοντας
κενά πριν από τις εντολές, τα οποία
Ο κώδικας ελέγχει την συνθήκη hour < 14, δηλαδή αν η τρέχουσα υποδηλώνουν ότι αυτές οι εντολές θα
ώρα είναι πριν τις 2 το μεσημέρι. Αν η συνθήκη ισχύει τότε εμφανίζει εκτελεστούν μόνο αν η συνθήκη είναι
το μήνυμα "Καλημέρα", αλλιώς το μήνυμα "Καλησπέρα". Με άλλα αληθής. Παρομοίως, οι εντολές που
λόγια, το πρόγραμμα επιλέγει την συμπεριφορά του ανάλογα με τις ακολουθούν την else είναι στοιχι-
σμένες δεξιότερα και θα εκτελεστούν
συνθήκες που επικρατούν την ώρα της εκτέλεσής του. Αυτό το νέο μόνο εφόσον η συνθήκη είναι ψευδής.
προγραμματιστικό εργαλείο ονομάζεται δομή επιλογής.
Το σύμβολο < χρησιμοποιείται για την
Μια πρακτική συμβουλή: θα πρέπει πάντα να εκτελούμε τα προ- σύγκριση τιμών, όπως επίσης και τα
γράμματά μας, ελέγχοντας ότι η συμπεριφορά τους είναι ορθή. Εδώ <=, > και >=. Για να ελέγξουμε αν δύο
τιμές είναι ίσες χρησιμοποιούμε το ==
το πρόγραμμά μας περιέχει μια δομή επιλογής με δύο πιθανές περι-
και για να ελέγξουμε αν είναι διαφο-
πτώσεις και θα πρέπει να ελέγξουμε ότι λειτουργεί σωστά και στις ρετικές το !=.
δύο. Αν το εκτελέσουμε πρωί και μας χαιρετίσει μ’ ένα "Καλημέρα",
Μην παραλείπετε το σύμβολο : μετά
δεν είναι καλή ιδέα να περιμένουμε μέχρι το βράδι για να ελέγξουμε την συνθήκη της if και την else.
αν αλλάζει η συμπεριφορά του.
Η ΑΠΑΝΤΗΣΗ 5
2 # είσοδος ονόματος
3 print("Πώς σε λένε;")
4 name = input()
5 # ανάκτηση τοπικής ώρας συστήματος
6 hour = time.localtime().tm_hour
7 # εμφάνιση χαιρετισμού ανάλογα με την ώρα
8 if hour < 14:
9 print("Καλημέρα", name)
10 else:
11 print("Καλησπέρα", name)
12 # καθυστέρηση
13 wait = 3
14 time.sleep(wait)
15 # ορισμός και εμφάνιση της Απάντησης
16 answer = 42
17 print("Η Απάντηση είναι...", answer)
answer/src/answer.final.py
Τροποποιήσεις – Επεκτάσεις
Στο πρόγραμμα που κατασκευάσαμε χρησιμοποιήσαμε την input()
για να ζητήσουμε το όνομα του χρήστη. Η τιμή που επιστρέφει η
input() είναι αλφαριθμητική, έχει δηλαδή την μορφή κειμένου. Αυτό
δεν είναι πρόβλημα όταν ζητάμε το όνομα του χρήστη γιατί κι αυτό
έχει την μορφή κειμένου. Όταν όμως θέλουμε να ζητήσουμε από το
χρήστη έναν ακέραιο ή έναν πραγματικό αριθμό (κάτι που θα χρεια-
στεί στις ασκήσεις που ακολουθούν), τότε θα πρέπει να μετατρέ-
ψουμε την αλφαριθμητική τιμή που επιστρέφει η input() σε αριθ-
μητική, με τον τρόπο που φαίνεται παρακάτω:
# το κείμενο που πληκτρολογεί ο χρήστης
# μετατρέπεται σε ακέραιο αριθμό και
# αποθηκεύεται στην μεταβλητή number
number = int(input())
Όταν ζητάτε από το χρήστη την Απάντηση, εκείνος δεν είναι υποχρεωμέ-
νος να πληκτρολογήσει έναν αριθμό. Έτσι, αν το πρόγραμμά σας χρησι-
μοποιεί την int για να μετατρέψει την απάντηση του χρήστη σε ακέραια,
έχετε υπόψη ότι αυτή η μετατροπή ίσως να μην είναι εφικτή και να οδηγή-
σει σε μήνυμα σφάλματος:
ValueError: invalid literal for int() with base 10
Μια απλή λύση είναι να θεωρήσετε ότι η Απάντηση είναι "42" αντί για
42, δηλαδή κείμενο και όχι ακέραιος. Έτσι, θα μπορέσετε να την συγκρί-
νετε με την απάντηση του χρήστη, χωρίς να χρειαστεί να μετατρέψετε την
απάντηση του χρήστη σε ακέραιο με την int.
answer/exercises/answer-question.py
Στη συνέχεια, να επεκτείνετε το πρόγραμμά σας έτσι ώστε να δίνεται
μια δεύτερη ευκαιρία στο χρήστη, αν αυτός δεν γνωρίζει την Απάντηση.
Πιθανώς, πριν ο χρήστης ερωτηθεί για δεύτερη φορά, το πρόγραμμα θα
μπορούσε να του εμφανίζει κάποια υπόδειξη, π.χ. ότι η Απάντηση είναι
το γινόμενο δύο διαδοχικών μονοψήφιων ακεραίων ή ότι είναι ο τέταρτος
κατά σειρά θετικός ακέραιος που οι “γείτονές” του είναι πρώτοι αριθμοί.
answer/exercises/answer-question-extended.py
Ασκήσεις
1.3 Στη Σελήνη το βάρος ενός αντικειμένου είναι το 16 του βάρους του στη
Γη. Στην Αφροδίτη το βάρος ενός αντικειμένου είναι 0,9 φορές το βάρος
του στη Γη. Στον Ήλιο το βάρος ενός αντικειμένου είναι 27,07 φορές το
βάρος του στη Γη, αλλά όταν βρίσκεται κανείς εκεί η αύξηση του βάρους
δεν είναι το βασικότερο πρόβλημα. Να γράψετε πρόγραμμα που θα ζητάει
από το χρήστη το βάρος του στη Γη και θα εμφανίζει το βάρος του στη
Σελήνη, την Αφροδίτη και τον Ήλιο.
answer/exercises/weight.py
Η ΑΠΑΝΤΗΣΗ 8
1.4 Υπάρχουν πολλά παιχνίδια στα οποία κάποιος σας ζητά να σκεφτείτε
έναν μυστικό αριθμό και, μετά από μερικές ερωτήσεις και υπολογισμούς,
«μαντεύει» τον αριθμό που είχατε σκεφτεί. Ένα πολύ παλιό παράδειγμα
βασίζεται σ’ ένα θεώρημα του κινέζου Sun Tzu, που έζησε κάπου ανάμεσα
στον 3ο και τον 5ο αιώνα. Ας υποθέσουμε ότι ο άγνωστος αριθμός είναι ο
x. Αν ονομάσουμε α, β και γ τα υπόλοιπα της διαίρεσης του x με το 3, το
5 και το 7 αντίστοιχα, τότε για να βρούμε τον άγνωστο αριθμό θα πρέπει
να υπολογίσουμε την τιμή της παράστασης 70α + 21β + 15γ και στη
συνέχεια να διαιρέσουμε με το 105. Το υπόλοιπο της διαίρεσης θα είναι
ο μυστικός αριθμός. Να αναπτύξετε ένα πρόγραμμα το οποίο ζητάει από
το χρήστη να σκεφτεί έναν μυστικό αριθμό από το 1 μέχρι και το 100.
Στη συνέχεια, το πρόγραμμα ρωτάει το χρήστη ποιο είναι το υπόλοιπο
της διαίρεσης αυτού του αριθμού με το 3, το 5 και το 7 και ανακοινώνει
τον μυστικό αριθμό, αφήνοντας το χρήστη έκπληκτο με τις υπερφυσικές
του ικανότητες.
answer/exercises/suntzu.py
1.5 Να γράψετε πρόγραμμα που θα ζητάει από το χρήστη το πλήθος των δευ-
τερολέπτων που έχουν περάσει από τα μεσάνυχτα και θα εμφανίζει την
τρέχουσα ώρα στη μορφή ώρες:λεπτά:δευτερόλεπτα. Για παράδειγμα,
αν ο χρήστης καθορίσει ότι έχουν περάσει 42222 δευτερόλεπτα από τα
μεσάνυχτα, το πρόγραμμα θα απαντά ότι η ώρα είναι 11:43:42.
Σημείωση: Αν οι ώρες, τα λεπτά ή τα δευτερόλεπτα είναι μονοψήφιοι αριθ-
μοί τότε η ώρα δεν θα εμφανίζεται «όμορφα». Αν αυτό προσβάλλει την
αισθητική σας και δεν μπορείτε να το ανεχτείτε, έχετε υπόψη ότι μπορείτε
να εμφανίσετε έναν αριθμό ως διψήφιο, με ένα αρχικό μηδέν όταν είναι
απαραίτητο, με τον εξής φρικτό τρόπο:
# έστω num ένας ακέραιος, πιθανώς μονοψήφιος
# έτσι εμφανίζεται με αρχικό μηδέν, αν χρειάζεται
print("{:02}".format(num))
answer/exercises/clock.py
1.6 Στο βιβλίο του Yacov Perelman με τίτλο Figures for Fun: Stories and
Conundrums υπάρχει το εξής πρόβλημα: Ένας άνθρωπος αδειάζει σ’ ένα
τραπέζι ένα κουτί που περιέχει 48 σπίρτα και τα χωρίζει σε τρεις άνισους
σωρούς. Παίρνει από τον πρώτο σωρό τόσα σπίρτα όσα υπάρχουν στο
δεύτερο σωρό και τα προσθέτει στον δεύτερο σωρό. Στη συνέχεια, παίρνει
από το δεύτερο σωρό τόσα σπίρτα όσα υπάρχουν στον τρίτο σωρό και
τα προσθέτει στον τρίτο σωρό. Τέλος, παίρνει από τον τρίτο σωρό τόσα
σπίρτα όσα υπάρχουν στον πρώτο σωρό και τα προσθέτει στον πρώτο
σωρό. Μετά από αυτές τις μετακινήσεις, όλοι οι σωροί περιέχουν το ίδιο
πλήθος σπίρτων. Πόσα σπίρτα περιείχε κάθε σωρός αρχικά;
Να γράψετε ένα πρόγραμμα που υπολογίζει τη λύση του προβλήματος,
ακολουθώντας τη διαδικασία που περιγράφεται αντίστροφα. Χρησιμο-
ποιήστε τρεις μεταβλητές h1, h2 και h3 για το πλήθος των σπίρτων
που περιέχει κάθε σωρός. Αρχικά, οι τρεις μεταβλητές θα έχουν την ίδια
τιμή, αντιστοιχώντας στην τελική κατάσταση. Ακολουθήστε με την αντί-
στροφη σειρά τα βήματα που περιγράφονται, γράφοντας τις αντίστοιχες
εντολές. Όταν τελειώσετε και εκτελέσετε το πρόγραμμα, οι μεταβλητές
Η ΑΠΑΝΤΗΣΗ 9
Εισοδος και Εξοδος Η ανάγνωση των τιμών που πληκτρολογεί ο χρήστης και γενικότερα η ροή πληροφορίας
προς ένα πρόγραμμα από το περιβάλλον του ονομάζεται είσοδος. Η εμφάνιση μηνυμάτων και γενικότερα η ροή
πληροφορίας από ένα πρόγραμμα προς το περιβάλλον του ονομάζεται έξοδος.
Μεταβλητες Μεταβλητή είναι το όνομα που δίνει ο προγραμματιστής σε μια τιμή ή ένα αντικείμενο. Χρειαζό-
μαστε τις μεταβλητές για τον ίδιο λόγο που χρειαζόμαστε γενικά τα ονόματα: για να μπορούμε να αναφερθούμε
σε αυτές τις τιμές, ακόμα και όταν δεν γνωρίζουμε ή δεν έχει σημασία ποιες ακριβώς είναι αυτές. Η τιμή στην
οποία αναφέρεται ένα όνομα, η τιμή της μεταβλητής, μπορεί να αλλάξει καθώς εκτελείται ένα πρόγραμμα. Είναι
καλή πρακτική να επιλέγουμε περιγραφικά ονόματα για τις μεταβλητές μας, ονόματα που υποδηλώνουν το είδος
των τιμών στις οποίες αντιστοιχούν.
Δομη Επιλογης Η δομή επιλογής δίνει τη δυνατότητα στα προγράμματά μας να ελέγχουν συνθήκες και να
διαφοροποιούν την συμπεριφορά τους ανάλογα με αυτές. Χωρίς αυτή τη δυνατότητα, τα προγράμματά μας εκτε-
λούν πάντα τις ίδιες εντολές, με την ίδια σειρά και αυτό τους στερεί την προσαρμοστικότητα ή την “εξυπνάδα”
που είναι απαραίτητη ακόμα και σε πολύ απλά προβλήματα.
Σκουπιδια Μπαινουν, Σκουπιδια Βγαινουν Στο βιβλίο, ο Deep Thought προσφέρει μια εξήγηση για το παρά-
δοξο της Απάντησης: “Για να είμαι ειλικρινής, πιστεύω ότι το πρόβλημα είναι ότι ποτέ δεν γνωρίζατε πραγματικά
ποια είναι η ερώτηση. Αν μάθετε την ερώτηση, τότε θα καταλάβετε τι σημαίνει και η απάντηση.” Τα προγράμματα
τα γράφουν προγραμματιστές. Άνθρωποι. Οι υπολογιστές απλά τα εκτελούν κι ελπίζουμε ότι η απάντηση που
μας δίνουν αποτελεί τη λύση στο πρόβλημά μας. Για να γίνει όμως αυτό, θα πρέπει οι προγραμματιστές να έχουν
κατανοήσει σωστά το πρόβλημα, οι οδηγίες που έχουν καθορίσει να είναι ορθές και τα δεδομένα που παρέχουν
οι χρήστες να έχουν νόημα. Σε διαφορετική περίπτωση, η απάντηση θα μας είναι άχρηστη. Ο Charles Babbage,
ο άνθρωπος που σχεδίασε έναν μηχανικό προγραμματιζόμενο υπολογιστή εκατό χρόνια πριν κατασκευαστούν
οι πρώτοι ηλεκτρονικοί υπολογιστές, είχε γράψει: “Σε δύο περιπτώσεις έχω ερωτηθεί: Πείτε μας κύριε Babbage,
αν εισάγετε λάθος νούμερα στη μηχανή, θα βγουν οι σωστές απαντήσεις; … Δεν μπορώ να συλλάβω τι είδους
σύγχυση ιδεών θα προκαλούσε μια τέτοια ερώτηση.”
Τα Τεσσερα Καπελα Συνήθως όλα ξεκινούν από ένα πρόβλημα. Κάποιος σκέφτεται “δεν θα ήταν ωραία αν
είχαμε ένα πρόγραμμα που να μας λύνει αυτό το πρόβλημα;” Μέσα σ’ αυτό το βιβλίο υπάρχουν αρκετά έτοιμα
προβλήματα, αλλά σύντομα θα θελήσετε κι εσείς να φορέσετε το καπέλο του Προβληματοθέτη, γιατί είναι
αλήθεια ότι τα πιο ενδιαφέροντα προβλήματα είναι αυτά που έχουν σημασία για εσάς. Μετά, υπάρχει κάποιος
Η ΑΠΑΝΤΗΣΗ 10
που φοράει το καπέλο του Προγραμματιστή. Διατυπώνει, σε μια γλώσσα προγραμματισμού, τις εντολές που
πρέπει να εκτελεστούν για να λυθεί το πρόβλημα. Εσείς πιθανότατα διαβάζετε αυτό το βιβλίο ακριβώς για να
μάθετε πως μπορείτε να παίξετε αυτό το ρόλο. Tα προγράμματα τα εκτελούν οι υπολογιστές, οι οποίοι είναι
φτιαγμένοι ειδικά γι’ αυτόν το σκοπό, όμως πολύ συχνά θα φορέσετε κι εσείς το καπέλο του Εκτελεστή και θα
εκτελέσετε εσείς οι ίδιοι τα προγράμματά σας, για να καταλάβετε πως λειτουργούν οι εντολές που γράψατε και,
πιθανώς, γιατί δεν λειτουργούν όπως θα θέλατε. Τέλος, υπάρχουν εκείνοι που χρησιμοποιούν το πρόγραμμα,
αλληλεπιδρούν μαζί του και αυτό λύνει για λογαριασμό τους το πρόβλημα από το οποίο ξεκίνησαν όλα. Θα
φοράτε κι εσείς το καπέλο του Χρήστη όταν ελέγχετε τα προγράμματά σας ή απλά διασκεδάζετε με αυτά.
Μπαρμπούτι 2
Σε αυτό το κεφάλαιο θα φτιάξουμε ένα παιχνίδι με ζάρια. Στην πορεία θα 29 Αυγούστου 2016
εξασκηθούμε στη δομή επιλογής και θα γνωρίσουμε τη δομή επανάληψης, 11:35
που μας επιτρέπει να εκτελούμε τις ίδιες εντολές πολλές φορές. Το τελικό
πρόγραμμα θα είναι μικρό, όμως σύντομα θα χρειαστεί να γράψουμε προ-
γράμματα μεγαλύτερα και πιο περίπλοκα. Έτσι, το ουσιαστικό αντικείμενο
του κεφαλαίου είναι να έρθουμε σε επαφή με τον τρόπο που οι προγραμ-
ματιστές συνθέτουν τα προγράμματά τους από υποπρογράμματα, σα να συ-
ναρμολογούν ένα περίπλοκο μηχάνημα από απλούστερα εξαρτήματα.
Έννοιες: δομή επιλογής, δομή επανάληψης, υποπρογράμματα
1
ΜΠΑΡΜΠΟΥΤΙ 2
Μεταβλητές όπως η over που παίρνουν μόνο δύο τιμές και χρησι-
μοποιούνται ως ένδειξη ότι κάποιο γεγονός έχει συμβεί ή όχι ονομά-
ζονται συχνά σημαίες (flags).
Πλήρες Πρόγραμμα
1 import random
2 def rollDice():
3 """ Ρίχνει δύο ζάρια και επιστρέφει
4 το άθροισμα των ενδείξεών τους.
5 """
6 # προτροπή για ρίψη ζαριών
7 print("Ρίξε τα ζάρια με ENTER...", end="")
8 input()
9 # τυχαίες τιμές για τα δύο ζάρια
10 dice1 = random.randint(1,6)
11 dice2 = random.randint(1,6)
12 # υπολογισμός και εμφάνιση ζαριάς
13 roll = dice1 + dice2
14 print("Έριξες", dice1, dice2, "=", roll)
15 return roll
Ασκήσεις
2.1 Ο Ευκλείδης, στο έβδομο βιβλίο των Στοιχείων του, περιγράφει μια μέ-
θοδο για την εύρεση του μέγιστου κοινού διαιρέτη δύο ακεραίων. Τα Στοι-
χεία γράφτηκαν περίπου το 300 π.Χ. και η μέθοδος καθεαυτή θεωρείται
ακόμα παλιότερη, γι’ αυτό και ο Donald Knuth, στο εμβληματικό βιβλίο
του The Art of Computer Programming, τη χαρακτηρίζει ως τον “παπ-
πού” όλων των αλγορίθμων, γιατί είναι ο παλαιότερος γνωστός αλγόριθ-
μος που χρησιμοποιείται ακόμα και σήμερα.
Μπορεί κανείς να βρει πολλές διαφορετικές εκδοχές του αλγορίθμου,
αλλά βασικά ο αλγόριθμος μπορεί να συνοψιστεί ως εξής:
Η εικασία έχει επαληθευτεί αριθμητικά μέχρι και για αριθμούς της τά- xkcd.com/710/
ξης των 6 δισεκατομμυρίων δισεκατομμυρίων, ωστόσο δεν υπάρχει ανα-
λυτική μαθηματική απόδειξη. Θεωρητικά υπάρχει πάντα το ενδεχόμενο
ένας ακόμα μεγαλύτερος αριθμός να παραβιάζει την εικασία!
Γράψτε ένα πρόγραμμα το οποίο θα διαβάζει από το χρήστη τον αριθμό Για να διαπιστώσετε αν ένας αριθμός
είναι περιττός ελέγξτε αν το υπόλοιπο
εκκίνησης n, θα επαναλαμβάνει τη διαδικασία που περιγράφεται πα-
της διαίρεσής του με το 2 είναι το 1.
ραπάνω και θα εμφανίζει τους διαδοχικούς αριθμούς που προκύπτουν
από αυτή, έως ότου η διαδικασία καταλήξει στον αριθμό 1. Μπορείτε να
εμπλουτίσετε το πρόγραμμά σας, ώστε μετά το τέλος της διαδικασίας
να εμφανίζει το χρόνο τερματισμού, δηλαδή το συνολικό αριθμό των βη-
μάτων που απαιτήθηκαν, αλλά και το σημείο πλημμυρίδας, δηλαδή τον
μεγαλύτερο αριθμό που προέκυψε κατά την εκτέλεση της διαδικασίας.
Για παράδειγμα, ξεκινώντας από το n=6 δημιουργείται η παρακάτω ακο-
λουθία, με χρόνο τερματισμού τα 8 βήματα και σημείο πλημμυρίδας το 16:
6 3 10 5 16 8 4 2 1
craps/exercises/collatz.py
ΜΠΑΡΜΠΟΥΤΙ 10
2.3 Γράψτε μια συνάρτηση flip(), η οποία θα μιμείται τη ρίψη ενός νομί-
σματος, επιστρέφοντας με τυχαίο τρόπο είτε το 0, είτε το 1.
Κατασκευάστε πρόγραμμα το οποίο θα παίζει Κορώνα-Γράμματα με το
χρήστη. Το πρόγραμμα θα ρωτάει τον παίκτη αν επιλέγει Κορώνα ή Γράμ-
ματα (0 για Κορώνα και 1 για Γράμματα), θα προσομοιώνει τη ρίψη του
νομίσματος καλώντας τη flip και θα ενημερώνει τον παίκτη αν κέρ-
δισε ή έχασε. Κάντε το πρόγραμμά σας επαναληπτικό, με το παιχνίδι να
σταματά όταν ο χρήστης σε κάποιο γύρο δεν επιλέξει ούτε Κορώνα, ούτε
Γράμματα.
craps/exercises/headstails.py
Μπορείτε να τροποποιήσετε το πρόγραμμά σας έτσι ώστε να “κλέβει” το
χρήστη. Χωρίς να γίνεται ρίψη του νομίσματος, κάντε το πρόγραμμα ν’
ανακοινώνει ότι το αποτέλεσμα της ρίψης του κέρματος ήταν το αντίθετο
από αυτό που επέλεξε ο παίκτης.
craps/exercises/headstails-cheat.py
Για να γίνει το πρόγραμμα πιο πειστικό, καλό θα ήταν να μην κερδίζει
πάντα, αλλά ν’ αφήνει μερικές φορές και το χρήστη να κερδίσει και να
αισθανθεί τυχερός… Σε αυτές τις περιπτώσεις, χωρίς να γίνεται ρίψη του
νομίσματος, το πρόγραμμα θ’ ανακοινώνει ότι το αποτέλεσμα της ρίψης
του κέρματος ήταν το ίδιο με αυτό που επέλεξε ο παίκτης.
craps/exercises/headstails-cheat-probability.py
Υπόδειξη: Πριν την ανακοίνωση του “στημένου” αποτελέσματος, το πρό-
γραμμα μπορεί να επιλέγει τυχαία έναν αριθμό από το 1 μέχρι το 100. Αν
αυτός ο αριθμός ξεπερνά ένα υψηλό “κατώφλι”, τότε το πρόγραμμα θ’ ανα-
κοινώνει στον παίκτη ότι κέρδισε.
win = random.randint(1,100)
if win > 90:
# ο παίκτης κερδίζει
...
else:
# ο παίκτης χάνει
...
2.5 Το «Ανάμεσα» ή «Acey Ducey» είναι ένα παιχνίδι με χαρτιά. Ο ένας παί-
κτης (η «μάνα») τραβάει δύο χαρτιά από την τράπουλα και τα δείχνει
στο δεύτερο παίκτη. Εκείνος με την σειρά του στοιχηματίζει ένα ποσό
ΜΠΑΡΜΠΟΥΤΙ 11
και νικάει όταν το τρίτο χαρτί είναι ανάμεσα στα δύο πρώτα. Όσο πιο
απίθανο είναι να νικήσει ένας παίκτης, τόσο μεγαλύτερο είναι το ποσό
που θα κερδίσει. Συγκεκριμένα, αν υπάρχουν d χαρτιά ανάμεσα στα δύο
πρώτα, τότε το ποσό που κερδίζει ο παίκτης σε περίπτωση νίκης θα είναι
12/d φορές το ποσό που στοιχημάτισε.
Για παράδειγμα, αν τα δύο πρώτα χαρτιά είναι το 5 και το 8, τότε ο δεύτε-
ρος παίκτης θα νικήσει αν το επόμενο χαρτί είναι το 6 ή το 7 (d=2). Στην
περίπτωση αυτή θα κερδίσει 6 φορές το ποσό που στοιχημάτισε.
Θεωρήστε ότι τα χαρτιά που χρησιμοποιούνται είναι από το 1 μέχρι και
το 13 (τα τρία τελευταία αντιστοιχούν στον βαλέ, τη ντάμα και τον ρήγα).
Αναπτύξτε πρόγραμμα το οποίο παράγει τυχαία τα δύο πρώτα χαρτιά της
μάνας. Αυτά θα πρέπει να διαφέρουν αριθμητικά τουλάχιστον κατά δύο
μονάδες, ώστε να υπάρχει περιθώριο για τουλάχιστον ένα χαρτί ανάμεσά
τους (d ≥1), αλλιώς η μάνα θα πρέπει να μοιράσει δύο νέα χαρτιά. Στη
συνέχεια, το πρόγραμμα ρωτά τον παίκτη το ποσό που επιθυμεί να στοι-
χηματίσει και παράγει το τρίτο χαρτί, εμφανίζοντας κατάλληλο μήνυμα
με το ποσό που κέρδισε ή έχασε ο δεύτερος παίκτης.
craps/exercises/aceyducey.py
Δομη Επαναληψης Η δομή επανάληψης μας δίνει τη δυνατότητα να περιγράφουμε διαδικασίες που επανα-
λαμβάνονται. Ίσως αρχικά αυτό να μην ακούγεται ιδιαίτερα εντυπωσιακό, γρήγορα όμως ανακαλύπτουμε ότι οι
περισσότερες υπολογιστικές διαδικασίες είναι εγγενώς επαναληπτικές (αυτό περιλαμβάνει και τα περισσότερα
παιχνίδια) και είναι απαραίτητος ένας συμπαγής τρόπος περιγραφής τους. Με τη δομή επανάληψης περιγρά-
φουμε τα βήματα ενός μόνο κύκλου, ακόμα κι αν τελικά αυτά τα βήματα θα εκτελεστούν πάρα πολλές φορές. ’Η
ακόμα κι αν δεν γνωρίζουμε εκ των προτέρων πόσες φορές θα εκτελεστούν. Από μία άποψη, στις υπολογιστικές
μας συσκευές ταιριάζει πολύ η δομή επανάληψης: σε αντίθεση με τους ανθρώπους, έχουν τη δυνατότητα να
εκτελούν αδιαμαρτύρητα τα ίδια βήματα, ξανά και ξανά.
Αναγνωσιμοτητα και Break Σε μια επανάληψη μπορούμε να χρησιμοποιήσουμε μία ή και περισσότερες break.
Κάθε break ανοίγει μια πόρτα εξόδου από τον κύκλο της επανάληψης. Επομένως, για να γνωρίζει κανείς πότε θα
τερματιστεί μια επανάληψη θα πρέπει να αναζητήσει τις break μέσα στην επανάληψη και να διαπιστώσει υπό
ποιες συνθήκες θα εκτελεστούν. Αντίθετα, χωρίς την break, η έξοδος από την επανάληψη γίνεται από ένα και
μοναδικό σημείο: την συνθήκη συνέχειας. Η επανάληψη τερματίζεται μόνο όταν η συνθήκη συνέχειας ελεγχθεί
και διαπιστωθεί ότι είναι ψευδής. Αρκεί λοιπόν να κοιτάξει κανείς την συνθήκη συνέχειας για να γνωρίζει πότε
θα τερματιστεί μια επανάληψη. Φαίνεται λοιπόν ότι η άμεση διακοπή μιας επανάληψης με την break είναι
μεν συχνά βολική, όμως χωρίς την break προκύπτουν προγράμματα που είναι περισσότερο κατανοητά και
ευανάγνωστα. Αυτό είναι εξαιρετικά σημαντικό για κάποιους και πολύ λιγότερο για άλλους…
Υποπρογραμματα Το μεγαλύτερο πλεονέκτημα που προκύπτει από τη χρήση υποπρογραμμάτων (και γίνεται
πολύ εμφανέστερο καθώς τα προβλήματα και τα αντίστοιχα προγράμματα μεγαλώνουν) είναι ότι είμαστε “ανα-
γκασμένοι” να αναλύουμε τα προβλήματα και να τ’ αντιμετωπίζουμε τμηματικά. Κάθε φορά εστιάζουμε την
προσοχή μας σε μικρότερα κι απλούστερα κομμάτια του γενικού προβλήματος και στη συνέχεια, συνθέτουμε τη
λύση, συναρμολογώντας τα κομμάτια αυτά. Στα κεφάλαια που ακολουθούν θα χρησιμοποιήσουμε περισσότερο
τα υποπρογράμματα και θα αναφερθούμε εκτενέστερα στα πλεονεκτήματα που πηγάζουν από τη χρήση τους.
Τυχαιοι Αριθμοι Η παραγωγή τυχαίων αριθμών έχει πολλές και σημαντικές εφαρμογές, όπως στην στατιστική,
τις προσομοιώσεις και τα παιχνίδια, όμως η σημαντικότερη εφαρμογή της είναι στην κρυπτογραφία. Υπάρχουν
αρκετές υπολογιστικές μέθοδοι για να παράγει κανείς τυχαίους αριθμούς. Όλες τους ονομάζονται και γεννήτριες
ΜΠΑΡΜΠΟΥΤΙ 12
ψευδο-τυχαίων αριθμών γιατί η αλήθεια είναι ότι οι αριθμοί που παράγουν φαίνονται τυχαίοι, αλλά δεν είναι.
Υπάρχουν μάλιστα και ειδικά στατιστικά κριτήρια που μετρούν πόσο απρόβλεπτες είναι οι ακολουθίες αριθμών
που παράγονται. Η Python (και άλλες γλώσσες) χρησιμοποιoύν έναν αλγόριθμο που ονομάζεται Mersenne-
Twister, ο οποίος είναι επαρκής για τις συνήθεις χρήσεις αλλά όχι για την κρυπτογραφία.
Ζαρια, Χαρτια και Τυχερα Παιχνιδια Πολλά από τα παραδείγματα με τα οποία θ’ ασχοληθούμε είναι παιχνίδια
με ζάρια ή χαρτιά. Τα επιλέξαμε επειδή θεωρούμε ότι είναι διασκεδαστικά και έχουν αλγοριθμικό ενδιαφέρον.
Ωστόσο, ο εθισμός σε αυτά τα παιχνίδια αποτελεί σημαντικότατο πρόβλημα. Τα ηλεκτρονικά τυχερά παιχνίδια
έχουν κι άλλες υπόγειες διαστάσεις. Μέσα από ορισμένες ασκήσεις, ελπίζουμε να κατανοήσετε πως όταν παίζεις
ένα τυχερό παιχνίδι μ’ ένα πρόγραμμα, ίσως το παιχνίδι να μην αφήνει και τόσα πολλά στην τύχη…
Μάντεψε τον Αριθμό 3
Ένα από τα πρώτα προγράμματα που συνηθίζεται να φτιάχνουν οι μαθη- 29 Αυγούστου 2016
τευόμενοι προγραμματιστές είναι ένα παιχνίδι στο οποίο ο παίκτης προ- 11:37
σπαθεί να μαντέψει τον μυστικό αριθμό που έχει “σκεφτεί” ο υπολογιστής ή
το αντίστροφο. Υπάρχουν πολλοί καλοί λόγοι που αυτό το παιχνίδι είναι
μια τόσο δημοφιλής επιλογή για τους αρχάριους: το πρόγραμμα που προ-
κύπτει δεν είναι ιδιαίτερα περίπλοκο, αν και συνδυάζει όλες τις βασικές
αλγοριθμικές έννοιες, ενώ το παιχνίδι καθεαυτό είναι πολύ διασκεδαστικό.
Έννοιες: δομή επιλογής, δομή επανάληψης, αναζήτηση.
1
ΜΑΝΤΕΨΕ ΤΟΝ ΑΡΙΘΜΟ 2
Γύρω-Γύρω Όλοι
Πως γίνεται η διαδικασία να επαναλαμβάνεται, έτσι ώστε ο χρήστης να Η επαναληπτική δομή while συνο-
έχει περισσότερες προσπάθειες για να μαντέψει τον αριθμό; δεύεται από μια συνθήκη συνέχειας. Η
συνθήκη ελέγχεται στην αρχή κάθε
Οι εντολές που διαβάζουν έναν αριθμό από το χρήστη και τον συ- κύκλου της επανάληψης και μπορεί
γκρίνουν με τον μυστικό αριθμό θα πρέπει να τοποθετηθούν μέσα σε να είναι αληθής (True) ή ψευδής
μια επαναληπτική δομή. (False). Όσο η συνθήκη είναι αληθής,
η επανάληψη συνεχίζεται για άλλον
4 # επανάληψη: έναν κύκλο.
5 # δεν τερματίζεται, συνθήκη πάντα αληθής Μην παραλείπετε το σύμβολο : μετά
6 while True: την συνθήκη.
7 # εμφάνιση προτροπής και ανάγνωση αριθμού Οι εντολές που ακολουθούν τη while
8 print("Μάντεψε τον αριθμό: 1 - 32") είναι στοιχισμένες δεξιότερα. Η
9 number = int(input()) στοίχιση αυτή υποδηλώνει ότι αυτές
οι εντολές θα επαναλαμβάνονται.
10 # έλεγχος αριθμού και εμφάνιση μηνύματος Η πρώτη εντολή μετά τη while που
11 if number != secret: δεν θα είναι στοιχισμένη δεξιότερα
12 print("Λάθος.") δεν θα επαναλαμβάνεται, αλλά θα
13 else: εκτελεστεί μόνο μια φορά, όταν η
επανάληψη τερματιστεί.
14 print("Σωστά!")
guess/src/guess.2.py
✓
Σταματήστε Να Κατέβω while
Δεν θα πρέπει να σταματά η επανάληψη όταν βρεθεί ο αριθμός;
Οι εντολές που τοποθετήθηκαν στην επαναληπτική δομή εκτελού-
Σχήμα 3.1: Η τετριμμένη συνθήκη
νται συνεχώς και δεν διακόπτονται ούτε καν όταν ο χρήστης μαντέ-
True που ελέγχεται από τη while εί-
ψει τον μυστικό αριθμό. Ένας απλός τρόπος να τερματιστεί η επα- ναι πάντα αληθής, κι έτσι η συγκε-
νάληψη είναι με την προσθήκη της εντολής break όταν ο παίκτης κριμένη επανάληψη δεν πρόκειται να
εντοπίσει τον μυστικό αριθμό. διακοπεί: οι εντολές της while εκτε-
λούνται επ’ άπειρον.
ΜΑΝΤΕΨΕ ΤΟΝ ΑΡΙΘΜΟ 3
# επανάληψη:
# τερματίζεται (με break) όταν βρεθεί ο αριθμός
while True: while
# εμφάνιση προτροπής και ανάγνωση αριθμού
print("Μάντεψε τον αριθμό: 1 - 32")
break
number = int(input())
# έλεγχος αριθμού και εμφάνιση μηνύματος
if number != secret: Σχήμα 3.2: Η break διακόπτει και τερ-
print("Λάθος.") ματίζει άμεσα τον κύκλο της επανά-
else: ληψης, χωρίς να ελεγχθεί η συνθήκη
print("Σωστά!") συνέχειας. Οι εντολές μετά την break
αγνοούνται και η εκτέλεση συνεχίζε-
# άμεση έξοδος από την επανάληψη ται από την πρώτη εντολή που ακο-
break λουθεί την επανάληψη.
Εφόσον το παιχνίδι θα πρέπει να συνεχίζεται όσο ο χρήστης δεν έχει Για να κατανοήσετε καλύτερα τι ση-
μαντέψει τον μυστικό αριθμό, ίσως αναρωτιέστε γιατί η συνθήκη μαίνει η άμεση διακοπή της επανά-
συνέχειας δεν έχει απλά την εξής μορφή: ληψης όταν εκτελεστεί η break, δο-
κιμάστε να τοποθετήσετε την εντολή
while number != secret: print("Σωστά!") μετά την break. Θα
διαπιστώσετε ότι το μήνυμα δεν θα εμ-
Αυτή είναι πράγματι μια εξαιρετική παρατήρηση, και θα έχετε την φανιστεί ποτέ.
ευκαιρία να τη διερευνήσετε στην άσκηση 3.2. Προς το παρόν, θα
συνεχίσουμε τροποποιώντας την τρέχουσα εκδοχή με τέτοιο τρόπο
ώστε η διακοπή της επανάληψης να μην βασίζεται στην break, αλλά
σε μια λογική μεταβλητή που θα ελέγχεται στη συνθήκη συνέχειας
της επανάληψης.
Υπάρχουν δύο λογικές τιμές: True και
4 # ο μυστικός αριθμός δεν έχει εντοπιστεί
False, οι οποίες είναι τύπου bool.
5 found = False
6 # επανάληψη: Το not χρησιμοποιείται πριν από μια
συνθήκη και αντιστρέφει την τιμή της:
7 # τερματίζεται όταν βρεθεί ο αριθμός όταν μια συνθήκη είναι ψευδής τότε η
8 while not found: αντίστροφή της είναι αληθής και το
9 # εμφάνιση προτροπής και ανάγνωση αριθμού ανάποδο.
10 print("Μάντεψε τον αριθμό: 1 - 32")
11 number = int(input())
12 # έλεγχος αριθμού και εμφάνιση μηνύματος ✗
13 if number != secret: while
14 print("Λάθος.")
15 else:
found = True
16 print("Σωστά!")
17 # ο μυστικός αριθμός εντοπίστηκε
Σχήμα 3.3: Όταν η found πάρει τιμή
18 found = True True, η επανάληψη δεν θα διακοπεί
guess/src/guess.3.py
άμεσα, γιατί η συνθήκη not found
H μεταβλητή found λειτουργεί ως σημαία και χρησιμοποιείται για της while δεν ελέγχεται συνεχώς. Η
επανάληψη θα διακοπεί όταν ελεγχθεί
να “θυμάται” το πρόγραμμά μας αν ο παίκτης έχει βρει τον μυστικό
η συνθήκη της while, δηλαδή αφού
αριθμό. Αρχικά ορίζεται ως ψευδής και όσο εξακολουθεί να είναι ολοκληρωθεί ο κύκλος της επανάλη-
ψευδής η επανάληψη συνεχίζεται. Όταν ο παίκτης βρει τον αριθμό, ψης.
η τιμή της found αλλάζει σε αληθής και η επανάληψη τερματίζε-
ται· όχι άμεσα, όπως με την break, αλλά όταν ελεγχθεί η found στη
ΜΑΝΤΕΨΕ ΤΟΝ ΑΡΙΘΜΟ 4
Το Μέτρημα
Το παιχνίδι δεν έχει ιδιαίτερο ενδιαφέρον αν ο χρήστης διαθέτει απεριό-
ριστες ευκαιρίες να μαντέψει τον αριθμό. Πως γίνεται να του δώσουμε
μόνο ένα μικρό πλήθος προσπαθειών;
Σε κάθε κύκλο του παιχνιδιού, θα πρέπει το πρόγραμμά μας να “γνω-
ρίζει” πόσες προσπάθειες απομένουν στο χρήστη. Αυτό είναι απα-
ραίτητο γιατί ο χρήστης θα πρέπει να ενημερώνεται σχετικά, αλλά
κυρίως επειδή το ίδιο το πρόγραμμα χρειάζεται αυτή την πληροφο-
ρία για να γνωρίζει πότε θα τερματιστεί το παιχνίδι. Θα καταμε-
τρούμε τις προσπάθειες που απομένουν με τη μεταβλητή tries.
Αρχικά, ας δώσουμε στο χρήστη ένα αυθαίρετο πλήθος προσπα-
θειών, π.χ. τέσσερις, προσθέτοντας την ακόλουθη εντολή πριν την
έναρξη της επανάληψης.
6 # ορισμός μέγιστου πλήθους προσπαθειών
7 tries = 4
Η απόδοση αρχικής τιμής σε μια μεταβλητή, όπως έγινε εδώ για
την μεταβλητή tries, ονομάζεται αρχικοποίηση. Στη συνέχεια του
παιχνιδιού, καθώς ο παίκτης προσπαθεί να μαντέψει τον αριθμό, η
τιμή της tries θα μειώνεται κατά μια μονάδα σε κάθε γύρο.
Ο τερματισμός του παιχνιδιού εξαρτάται τώρα και από το πλήθος
των προσπαθειών που απομένουν. Στην αρχή κάθε κύκλου της επα-
νάληψης θα ελέγχεται αν ο μυστικός αριθμός έχει βρεθεί, αλλά και
αν η μεταβλητή tries είναι θετική, δηλαδή αν απομένουν ακόμα
προσπάθειες. Αν κάποια από αυτές τις συνθήκες δεν ισχύει, η επα-
ναληπτική διαδικασία διακόπτεται.
Το and χρησιμοποιείται για τη σύ-
8 # επανάληψη: τερματίζεται όταν
ζευξη δύο συνθηκών. Η σύζευξη είναι
9 # βρεθεί ο αριθμός ή εξαντληθούν οι προσπάθειες αληθής μόνο όταν και οι δύο συζευγ-
10 while not found and tries > 0: μένες συνθήκες είναι αληθείς.
Σε κάθε γύρο του παιχνιδιού, ο χρήστης ενημερώνεται με ένα μήνυμα
για το πλήθος των προσπαθειών που απομένουν, ενώ στη συνέχεια
η ποσότητα αυτή (η μεταβλητή tries) μειώνεται κατά μία μονάδα.
11 # εμφάνιση και μείωση προσπαθειών
12 print("Απομένουν", tries, "προσπάθειες.")
13 tries = tries - 1
Η τελευταία εντολή μπερδεύει πολλούς αρχάριους γιατί την ερμη-
νεύουν σαν μαθηματική ισότητα, ενώ δεν είναι. Για να γίνει κατα-
νοητή, πρέπει να διαβαστεί ως εξής: υπολόγισε την τιμή της παρά-
στασης tries - 1 κι ονόμασε το αποτέλεσμα tries. Η νέα τιμή της
μεταβλητής tries υπολογίζεται με βάση την τρέχουσα τιμή της, την
οποία και αντικαθιστά.
ΜΑΝΤΕΨΕ ΤΟΝ ΑΡΙΘΜΟ 5
Περισσότερη Πληροφορία
Θα ήθελα η απάντηση που δίνεται στο χρήστη να τον βοηθάει λίγο περισ-
σότερο να βρει τον αριθμό. Αντί να ενημερώνεται αν τον μάντεψε ή όχι,
θα μπορούσε να κατευθύνεται να ψάξει προς τα πάνω ή προς τα κάτω.
Μέχρι στιγμής, σε κάθε προσπάθεια το πρόγραμμα ελέγχει αν ο
αριθμός που έδωσε ο χρήστης ταυτίζεται με τον μυστικό αριθμό.
Τώρα θα επεκτείνουμε αυτόν τον έλεγχο, έτσι ώστε να ελέγχεται
αν ο αριθμός του χρήστη είναι μεγαλύτερος ή μικρότερος από τον
μυστικό αριθμό. Σε κάθε περίπτωση θα εμφανίζεται διαφορετικό μή-
νυμα, έτσι ώστε ο χρήστης να κατευθύνεται προς τον μυστικό αριθμό.
17 # έλεγχος αριθμού και εμφάνιση μηνύματος Το elif σημαίνει else if. Χρησιμο-
ποιείται στη δομή πολλαπλής επιλογής,
18 if number > secret: δηλαδή όταν χρειάζεται να διακρί-
19 print("Λάθος. Είναι μικρότερος.") νουμε ανάμεσα σε περισσότερες από
20 elif number < secret: δύο περιπτώσεις.
21 print("Λάθος. Είναι μεγαλύτερος.") Κάθε elif, όπως και η if, συνοδεύε-
22 else: ται από μια συνθήκη. Μετά από μια if
23 print("Σωστά!") μπορούμε να χρησιμοποιήσουμε όσες
διαδοχικές elif είναι απαραίτητες.
24 # ο μυστικός αριθμός εντοπίστηκε Οι συνθήκες ελέγχονται διαδοχικά, η
25 found = True μία μετά την άλλη, μέχρι να βρεθεί μία
guess/src/guess.5.py που να είναι αληθής, οπότε και εκτε-
λούνται οι αντίστοιχες εντολές, ενώ οι
Σκεφτείτε πόση περισσότερη πληροφορία παρέχεται στο χρήστη με συνθήκες που την ακολουθούν παρα-
αυτή την μικρή αλλαγή. Προηγουμένως, κάθε αποτυχημένη προ- κάμπτονται.
σπάθεια απέκλειε έναν από τους υποψήφιους αριθμούς. Τώρα μια
αποτυχημένη προσπάθεια αποκλείει και όλους τους αριθμούς που
είναι μικρότεροι ή μεγαλύτεροι από αυτόν που επέλεξε ο χρήστης.
ΜΑΝΤΕΨΕ ΤΟΝ ΑΡΙΘΜΟ 6
Εξαρτήματα κι Αυτοματισμοί
Το καημένο το πρόγραμμά μου κάνει όλη τη βαρετή δουλειά. Δε γίνεται
να το αφήσω να παίξει κι αυτό λιγάκι;
Το σημείο στο οποίο ο παίκτης αλληλεπιδρά με το παιχνίδι είναι το
σημείο στο οποίο του ζητείται να μαντέψει τον μυστικό αριθμό. Ο
παίκτης ενημερώνεται για τα όρια ανάμεσα στα οποία βρίσκεται ο
μυστικός αριθμός και, με βάση αυτές τις τιμές, επιλέγει έναν αριθμό.
# εμφάνιση προτροπής και ανάγνωση αριθμού
print("Μάντεψε τον αριθμό:", low , "-", high)
number = int(input())
Ας κατασκευάσουμε λοιπόν ένα υποπρόγραμμα το οποίο θα κάνει
a b
αυτή ακριβώς τη δουλειά: θα δέχεται σαν παραμέτρους τα όρια ανά-
μεσα στα οποία βρίσκεται ο μυστικός αριθμός και θα ζητάει από το readNumber
χρήστη να επιλέξει έναν αριθμό μέσα σε αυτά τα όρια.
2 def readNumber(a,b): Σχήμα 3.8: Αναπαράσταση της συ-
3 """ ζητάει από το χρήστη έναν αριθμό νάρτησης readNumber(), η οποία δέ-
4 μεταξύ των a και b και τον επιστρέφει. χεται σαν παραμέτρους δύο ακέραιες
τιμές a και b, ζητά από το χρήστη να
5 a, b: όρια για τον αριθμό (δεν ελέγχονται) πληκτρολογήσει μια τιμή που βρίσκε-
6 """ ται ανάμεσά τους και την επιστρέφει.
7 # εμφάνιση προτροπής και ανάγνωση αριθμού
Ο ορισμός μιας συνάρτησης ξεκινά με
8 print("Μάντεψε τον αριθμό:", a , "-", b) τη δεσμευμένη λέξη def κι ακολουθεί-
9 num = int(input()) ται από το όνομα της συνάρτησης και
10 # επιστροφή αριθμού τις παραμέτρους της, σε παρενθέσεις.
11 return num Οι εντολές που ακολουθούν την
Οι μεταβλητές a, b και num είναι τοπικές και υφίστανται μόνο κα- πρώτη γραμμή είναι στοιχισμένες δε-
ξιότερα. Η στοίχιση αυτή υποδηλώνει
θόσο εκτελείται το υποπρόγραμμα. ότι οι εντολές αυτές θα εκτελεστούν
Στο σημείο του κύριου προγράμματος όπου ζητείται η είσοδος αριθ- όταν κληθεί η συνάρτηση.
μού από το χρήστη μπορούμε τώρα να καλέσουμε το υποπρόγραμμα,
low high
παρέχοντας τις κατάλληλες παραμέτρους.
28 # επιλογή αριθμού από το χρήστη
29 number = readNumber(low,high)
guess/src/guess.7.py
Στη φάση αυτή δεν υπάρχει πλέον είσοδος από τον χρήστη. Ο μυστι-
κός αριθμός καθορίζεται με τυχαίο τρόπο και στη συνέχεια το ίδιο
το πρόγραμμα (αναλαμβάνοντας και το ρόλο του δεύτερου παίκτη)
προσπαθεί με συστηματικό τρόπο να τον μαντέψει.
2 def readNumber(a,b):
3 """ ζητάει από το χρήστη έναν αριθμό
4 μεταξύ των a και b και τον επιστρέφει.
5 a, b: όρια για τον αριθμό (δεν ελέγχονται)
6 """
7 # εμφάνιση προτροπής και ανάγνωση αριθμού
8 print("Μάντεψε τον αριθμό:", a , "-", b)
9 num = int(input())
10 # επιστροφή αριθμού
11 return num
…συνεχίζεται στην επόμενη σελίδα.
ΜΑΝΤΕΨΕ ΤΟΝ ΑΡΙΘΜΟ 9
Τροποποιήσεις – Επεκτάσεις
3.1 Το τελικό πρόγραμμα ενημερώνει σε κάθε προσπάθεια για τα όρια ανά-
μεσα στα οποία βρίσκεται ο μυστικός αριθμός. Ωστόσο, ο χρήστης δεν
είναι υποχρεωμένος να εισάγει έναν αριθμό που να βρίσκεται ανάμεσα
σε αυτά τα όρια. Αν ο χρήστης δώσει έναν αριθμό εκτός ορίων τότε απλά
θα χαραμίσει μια προσπάθεια. Παράλληλα όμως, θα φανεί κι ένα πρό-
βλημα: ένα από τα όρια θα τροποποιηθεί λανθασμένα. Επιβεβαιώστε το
πρόβλημα και διορθώστε το, προσθέτοντας τις κατάλληλες εντολές.
guess/exercises/guess-limits.py
Σημειώστε ότι την πρώτη φορά που η εκτέλεση του προγράμματος φτάσει
στη while, την πρώτη φορά που θα ελεγχθεί η συνθήκη number != secret,
η μεταβλητή number θα πρέπει να έχει ήδη μια τιμή.
Μια λύση είναι να της δώσουμε αρχικά, πριν την επανάληψη, μια πλασμα-
τική τιμή, για παράδειγμα:
# πλασματική αρχική τιμή για τον αριθμό του χρήστη
number = 0
# επανάληψη: τερματίζεται όταν
# βρεθεί ο αριθμός ή εξαντληθούν οι προσπάθειες
while number != secret and tries > 0:
Έτσι η number αποκτά τιμή και ταυτόχρονα είμαστε σίγουροι ότι η συν-
θήκη number != secret θα είναι αρχικά αληθής και η επανάληψη θα ξε-
κινήσει.
Όμως μια πολύ πιο ενδιαφέρουσα λύση (και αυτή είναι η ζητούμενη εδώ)
είναι η αρχική τιμή για την number να προέρχεται από το χρήστη:
# επιλογή αριθμού από το χρήστη
number = readNumber(low,high)
# επανάληψη: τερματίζεται όταν
# βρεθεί ο αριθμός ή εξαντληθούν οι προσπάθειες
while number != secret and tries > 0:
Αυτή η προσέγγιση θα απαιτήσει σημαντική αναδιάταξη των εντολών για
να λειτουργήσει το πρόγραμμα σωστά. Αξίζει τον κόπο γιατί θα σας οδη-
γήσει σε ένα μοτίβο που είναι πολύ κοινό σε προγράμματα με επανάληψη.
guess/exercises/guess-nobreak.py
Ασκήσεις
3.6 Να κατασκευάσετε ένα πρόγραμμα το οποίο θα ζητά από το χρήστη έναν
αλφαριθμητικό κωδικό. Ο χρήστης θα έχει το πολύ τρεις προσπάθειες για
να εισάγει τον κωδικό του. Αν εισάγει έναν σωστό κωδικό, το πρόγραμμα
θα τον καλωσορίζει, ενώ αν εξαντλήσει ανεπιτυχώς τις προσπάθειές του,
θα εμφανίζεται ένα σχετικό μήνυμα.
guess/exercises/password.py
3.7 Να κατασκευάσετε ένα πρόγραμμα στο οποίο ο χρήστης θα σκέφτεται
έναν μυστικό αριθμό από το 1 μέχρι και το 32 και το πρόγραμμα θα προ-
σπαθεί να τον μαντέψει μέσα σε 4 προσπάθειες το πολύ. Σε κάθε προσπά-
θεια, το πρόγραμμα θα επιλέγει έναν αριθμό και θα ρωτάει το χρήστη αν
αυτός είναι ίσος, μικρότερος ή μεγαλύτερος από τον μυστικό αριθμό του.
Πρόκειται για το ίδιο παιχνίδι με το οποίο ασχοληθήκαμε σ’ αυτό το κεφά-
λαιο, αλλά με τους ρόλους παίκτη και προγράμματος αντεστραμμένους.
guess/exercises/guess-user.py
3.8 Να κατασκευάσετε ένα πρόγραμμα το οποίο θα επιλέγει έναν μυστικό
αριθμό από το 1 μέχρι και το 32 και ο χρήστης θα προσπαθεί να τον
μαντέψει μέσα σε 4 προσπάθειες το πολύ. Σε κάθε προσπάθεια, ο χρή-
στης θα επιλέγει δύο αριθμούς που θ’ αποτελούν την «παγίδα» του και
το πρόγραμμα θα τον ενημερώνει αν ο μυστικός αριθμός βρίσκεται ανά-
μεσα στους αριθμούς της παγίδας, αν είναι μικρότερος ή μεγαλύτερος
ΜΑΝΤΕΨΕ ΤΟΝ ΑΡΙΘΜΟ 12
από αυτούς. Για να βρίσκεται ένας αριθμός μέσα στην παγίδα θα πρέπει
να είναι τουλάχιστον ίσος με τον μικρότερο αριθμό της παγίδας και το
πολύ ίσος με τον μεγαλύτερο. Όταν η παγίδα αποτελείται από δύο αριθ-
μούς που είναι ίσοι μεταξύ τους και ταυτίζονται με τον μυστικό αριθμό
τότε ο χρήστης έχει μαντέψει τον μυστικό αριθμό.
guess/exercises/trap.py
Όπως και στο «Μάντεψε τον Αριθμό», μπορείτε ν’ αυτοματοποιήσετε
την αναζήτηση του αριθμού, γράφοντας μια συνάρτηση αντίστοιχη της
midNumber(), η οποία επιλέγει σε κάθε γύρο την κατάλληλη παγίδα.
guess/exercises/trap-auto.py
3.9 Να γράψετε συνάρτηση με όνομα flipBiased, η οποία θα μιμείται τη
ρίψη ενός μεροληπτικού νομίσματος και θα επιστρέφει είτε το 0, είτε
το 1. Η συνάρτηση θα δέχεται μια παράμετρο p ή οποία θ’ αντιστοιχεί
στην πιθανότητα (επί τοις 100) να επιστραφεί το 0. 1 p 1OO
high
Ένας τρόπος υλοποίησης της λειτουργίας του μεροληπτικού νομίσματος
είναι να επιλέγεται ένας τυχαίος αριθμός από το 1 μέχρι το 100. Αν αυ-
τός ο τυχαίος αριθμός δεν ξεπερνά το p, τότε επιστρέφεται το 0, αλλιώς
Αν ο τυχαίος αριθμός δεν ξεπερνά το
επιστρέφεται το 1.
p, επιστρέφεται το 0. Σε διαφορετική
Ο Jon von Neumann σκέφτηκε έναν τρόπο να προσομοιώσει κανείς τη περίπτωση, επιστρέφεται το 1.
ρίψη ενός δίκαιου νομίσματος, ακόμα κι αν έχει στη διάθεσή του ένα με-
πρώτη δεύτερη
ροληπτικό νόμισμα. Η μέθοδος που πρότεινε είναι η εξής: ρίψη ρίψη
Να γράψετε συνάρτηση με όνομα flipFair, η οποία θα μιμείται τη ρίψη Όταν ρίχνουμε ένα νόμισμα δύο φο-
ρές υπάρχουν τέσσερα πιθανά ενδε-
ενός δίκαιου νομίσματος και θα επιστρέφει είτε το 0, είτε το 1. Η συνάρ-
χόμενα. Τα δύο από αυτά, όταν το
τηση θα πρέπει να χρησιμοποιεί τη μέθοδο του von Neumann: θα υπο- αποτέλεσμα κάθε ρίψης είναι διαφο-
λογίζει το αποτέλεσμά της χρησιμοποιώντας ένα μεροληπτικό νόμισμα, ρετικό, είναι ισοπίθανα, ακόμα κι όταν
δηλαδή καλώντας την συνάρτηση flipBiased. το νόμισμα είναι μεροληπτικό. Η μέθο-
δος του von Neumann επαναλαμβάνει
Να φτιάξετε ένα πρόγραμμα που ελέγχει αν οι συναρτήσεις flipBiased τη ρίψη δύο νομισμάτων μέχρι να προ-
και flipFair λειτουργούν όπως θα περιμέναμε. Για κάθε συνάρτηση, κύψει ένα από τα δύο ισοπίθανα ενδε-
το πρόγραμμα θα πρέπει να την καλεί επαναληπτικά και να υπολογί- χόμενα, και επιστρέφει το αποτέλεσμα
που σημειώνεται με το βελάκι, προσο-
ζει το ποσοστό των ρίψεων στις οποίες το αποτέλεσμα ήταν το 0. Αν η μοιώνοντας έτσι ένα δίκαιο νόμισμα.
flipBiased λειτουργεί σωστά, τότε το ποσοστό αυτό θα πρέπει να προ-
σεγγίζει το p. Αν η flipFair λειτουργεί σωστά, τότε το ποσοστό αυτό
θα πρέπει να προσεγγίζει το 50.
Καλό θα ήταν ο ίδιος ο χρήστης να παρέχει την τιμή της παραμέτρου p της
flipBiased και το πλήθος των επαναλήψεων. Έτσι θα μπορέσετε κι εσείς,
ως χρήστες, να πειραματιστείτε εύκολα με διάφορες τιμές. Όπως ισχύει σε
κάθε προσομοίωση, το πλήθος των επαναλήψεων πρέπει να είναι μεγάλο
(π.χ. τουλάχιστον 10000), για να είναι το αποτέλεσμα του ελέγχου αξιόπι-
στο.
guess/exercises/flip-biased.py
ΜΑΝΤΕΨΕ ΤΟΝ ΑΡΙΘΜΟ 13
Αναζητηση Όταν ψάχνουμε κάτι, όπως για παράδειγμα έναν μυστικό αριθμό, μιλάμε για αναζήτηση. Η ανα-
ζήτηση είναι κάτι που κάνουμε πολύ συχνά, είτε μόνοι μας, είτε με τη βοήθεια υπολογιστών. Ψάχνουμε για
ένα όνομα στον τηλεφωνικό κατάλογο ή για την επόμενη κίνηση σ’ ένα επιτραπέζιο παιχνίδι. Συχνά η ανα-
ζήτηση δεν είναι καθόλου απλή υπόθεση. Μπορεί ο χώρος στον οποίο ψάχνουμε να είναι αχανής. Μπορεί να
μην ξέρουμε τι ακριβώς ψάχνουμε ή πως πρέπει να το περιγράψουμε. Για παράδειγμα, η μηχανή αναζήτησης
της Google προσπαθεί μέσα σε μερικά δέκατα του δευτερολέπτου να συγκεντρώσει και να ταξινομήσει τις διευ-
θύνσεις των ιστοσελίδων που πιθανώς μας ενδιαφέρουν, βασισμένη κάθε φορά σε μερικές λέξεις-κλειδιά που
της παρέχουμε. Οι βιολόγοι αναζητούν ακολουθίες αμινοξέων μέσα στον γενετικό μας κώδικα, παρέχοντας μόνο
ένα γενικό πρότυπο γιατί μπορεί να υπάρχουν αποδεκτές παραλλαγές και μεταλλάξεις. Με δεδομένο πάντως
το πόσο σημαντικό πρόβλημα είναι η αναζήτηση, είναι ευτυχές ότι πρόκειται για ένα πρόβλημα που λύνεται
συνήθως πολύ αποδοτικά.
Δυαδικη Αναζητηση Όταν αναζητούμε ένα αντικείμενο μέσα σε ένα σύνολο από ταξινομημένα στοιχεία τότε
το καλύτερο που μπορούμε να κάνουμε είναι να ψάξουμε ακριβώς στη μέση. Αν το μεσαίο στοιχείο δεν είναι
αυτό που ψάχνουμε τότε (επειδή τα στοιχεία είναι ταξινομημένα) γνωρίζουμε προς τα που πρέπει να συνεχίσουμε
την αναζήτηση, αποκλείουμε μεμιάς τα άλλα μισά στοιχεία που βρίσκονται προς την αντίθετη κατεύθυνση και
εφαρμόζουμε την ίδια διαδικασία στα στοιχεία που απομένουν. Με τον τρόπο αυτό, είτε θα εντοπίσουμε κάποια
στιγμή το στοιχείο που αναζητούμε, είτε θα εξαντληθούν τα στοιχεία και θα φτάσουμε στο συμπέρασμα ότι αυτό
που ψάχνουμε δεν υπάρχει ανάμεσά τους. Αυτή η μέθοδος ονομάζεται δυαδική αναζήτηση και είναι ουσιαστικά
η διαδικασία που ακολουθήσαμε στο τελευταίο βήμα του παραδείγματος για να εντοπίσουμε τον μυστικό αριθμό.
Διαιρει και Βασιλευε Πρόκειται για μια στρατηγική επίλυσης προβλημάτων που στηρίζεται στον κατακερμα-
τισμό ενός προβλήματος σε μικρότερα υποπροβλήματα. Αυτά είναι συνήθως του ίδιου ή συναφούς τύπου με το
αρχικό πρόβλημα και, στην καλύτερη περίπτωση, ίδιου μεγέθους μεταξύ τους. Τα υποπροβλήματα επιλύονται
ξεχωριστά και, αν αυτό είναι απαραίτητο, οι λύσεις τους συνδυάζονται για να επιλυθεί το αρχικό πρόβλημα.
Ουσιαστικά μια παρόμοια στρατηγική ακολουθούμε όταν το πρόβλημα που μας τίθεται είναι ο εντοπισμός του
μυστικού αριθμού. Σε κάθε βήμα βρισκόμαστε αντιμέτωποι με το ίδιο πρόβλημα, αλλά τα όρια ανάμεσα στα
οποία αναζητούμε τον αριθμό ολοένα και στενεύουν. Πολλοί σημαντικοί αλγόριθμοι στην Πληροφορική βασί-
ζονται στη στρατηγική του διαίρει και βασίλευε.
Το Παιχνίδι της Αφαίρεσης 4
Στο κεφάλαιο αυτό θα φτιάξουμε ένα απλό παιχνίδι δύο παικτών μ’ ενδια- 10 Σεπτεμβρίου 2016
φέρουσα ιστορία. Στο τέλος, το πρόγραμμά μας θα συμμετέχει στο παι- 10:00
χνίδι, παίζοντας το ρόλο ενός από τους δύο παίκτες. Στην πορεία θα έχουμε
την ευκαιρία να έρθουμε ξανά σε επαφή με τις αλγοριθμικές δομές που
έχουμε συναντήσει μέχρι στιγμής, ενώ θα χρησιμοποιήσουμε υποπρογράμ-
ματα, για να κατακερματίσουμε το πρόγραμμά μας σε απλούστερα τμή-
ματα και να διαχειριστούμε την πολυπλοκότητά του.
Έννοιες: δομή επιλογής, δομή επανάληψης, υποπρογράμματα
Το Στήσιμο
Ας υποθέσουμε ότι τα αντικείμενα που χρησιμοποιούν οι παίκτες είναι
σπίρτα. Με πόσα πρέπει να ξεκινήσουμε;
Οι κανόνες του παιχνιδιού δεν προσδιορίζουν το αρχικό πλήθος των
σπίρτων. Μπορούμε λοιπόν να το ορίσουμε μόνοι μας.
# αρχικό πλήθος σπίρτων
matches = 7
Εναλλακτικά, μπορούμε να αρχικοποιήσουμε τη μεταβλητή matches
με μια τυχαία τιμή (ας πούμε από το 7 μέχρι και το 21).
1
ΤΟ ΠΑΙΧΝΙΔΙ ΤΗΣ ΑΦΑΙΡΕΣΗΣ 2
1 import random
2 # αρχικό πλήθος σπίρτων
3 matches = random.randint(7,21)
Καλό θα ήταν να ενημερώσουμε το χρήστη με ένα κατάλληλο μή-
νυμα για τον αρχικό αριθμό των σπίρτων.
4 # εμφάνιση αρχικού πλήθους σπίρτων
5 print("Αρχικό πλήθος σπίρτων:", matches)
Αρχικά, πριν ξεκινήσει η διαδικασία του παιχνιδιού, ορίζουμε ποιος Σχήμα 4.2: Η τιμή της μεταβλητής
παίκτης ξεκινάει πρώτος: αυτός θα είναι πάντα ο παίκτης με αριθμό 1. player εναλλάσσεται σε κάθε γύρο
μεταξύ του 1 και του 2, υποδεικνύο-
6 # ορισμός παίκτη που θα παίξει πρώτος ντας τον αριθμό του παίκτη που έχει
σειρά να παίξει. Η αρχική της τιμή εί-
7 player = 1
ναι το 1.
Τώρα πλέον η προτροπή που εμφανίζεται σε κάθε κύκλο απευθύνε-
ται σε συγκεκριμένο παίκτη:
10 # ανάγνωση σπίρτων που θα πάρει ο παίκτης
11 print("Παίκτη", player, "πόσα σπίρτα θέλεις;")
12 removed = int(input())
nim/src/nim.2.py
Χαζό Μηχάνημα
Βαριέμαι να παίζω μόνος μου. Δεν γίνεται να παίζω με αντίπαλο το πρό-
γραμμά μου;
Το πρόγραμμά μας θα πρέπει να αποφασίζει για το πλήθος των
σπίρτων που θα αφαιρέσει. Δεν είναι ανάγκη να καταλήξουμε από
την αρχή σε κάποια ευφυή στρατηγική, μπορούμε να ξεκινήσουμε
με μια τυχαία επιλογή σπίρτων.
Θα κατασκευάσουμε ένα απλό υποπρόγραμμα το οποίο δέχεται σαν
παράμετρο το πλήθος των σπίρτων που έχουν απομείνει και επιλέγει
τυχαία να αφαιρέσει από 1 μέχρι το μέγιστο πλήθος σπίρτων που
επιτρέπεται να αφαιρεθούν.
Οι παράμετροι που χρησιμοποιούνται
42 def randomMatches(m):
κατά την κλήση μιας συνάρτησης δεν
43 """ Επιλέγει κι επιστρέφει ένα τυχαίο είναι μόνο σταθερές ή μεταβλητές,
44 αλλά έγκυρο πλήθος σπίρτων που θα αφαιρεθούν. αλλά κι ολόκληρες εκφράσεις, που
45 m: πλήθος σπίρτων που απομένουν μπορεί να περιλαμβάνουν κλήσεις συ-
46 """ ναρτήσεων, όπως εδώ η maxMatches.
Στο παράδειγμα αυτό, πρώτα καλείται
47 return random.randint(1,maxMatches(m)) η maxMatches και η τιμή που επιστρέ-
Στην τελευταία γραμμή, η τιμή που επιστρέφει η maxMatches() χρη- φει χρησιμοποιείται σαν δεύτερη πα-
ράμετρος στην κλήση της randint.
σιμοποιείται ως άνω όριο για τον τυχαίο αριθμό που θα δημιουργη-
θεί. Είναι σχεδόν το ίδιο με τον κώδικα που ακολουθεί, αλλά χωρίς
την ενδιάμεση μεταβλητή limit.
limit = maxMatches(m)
return random.randint(1, limit)
Είναι ενδιαφέρον ότι η συνάρτηση maxMatches() χρησιμοποιείται
τώρα και από την readMatches() για την ανάγνωση αριθμού από
τον χρήστη, αλλά και από την randomMatches() για την επιλογή
ενός αριθμού σπίρτων από το ίδιο το πρόγραμμα. Αποδεικνύεται
έτσι ένα χρήσιμο “εξάρτημα” που επαναχρησιμοποιείται σε διαφορε-
τικά σημεία του προγράμματος για διαφορετικούς λόγους.
Ας εστιάσουμε τώρα στο κύριο πρόγραμμα, όπου θα πρέπει να ει-
σάγουμε τις απαραίτητες επεκτάσεις ώστε το ίδιο το πρόγραμμα να
αναλαμβάνει πλέον το ρόλο ενός από τους δύο παίκτες. Θα χρησι-
μοποιήσουμε τη μεταβλητή computer, της οποίας η τιμή θ’ αντιστοι-
χεί στον αριθμό του παίκτη που θα παίζει αυτοματοποιημένα. Στην
αρχή του παιχνιδιού η computer θα παίρνει τυχαία την τιμή 1 ή 2.
ΤΟ ΠΑΙΧΝΙΔΙ ΤΗΣ ΑΦΑΙΡΕΣΗΣ 7
52 # επιλογή παίκτη-υπολογιστή
53 computer = random.randint(1,2)
Θυμηθείτε ότι η μεταβλητή player εναλλάσσεται σε κάθε γύρο με-
ταξύ των τιμών 1 και 2, υποδεικνύοντας ποιος παίκτης έχει σειρά
να παίξει στον επόμενο γύρο. Συκρίνοντας λοιπόν την player με
την computer ελέγχουμε ουσιαστικά αν ο επόμενος παίκτης είναι ο
άνθρωπος ή το πρόγραμμά μας, ώστε να καλέσουμε σε κάθε περί-
πτωση την ανάλογη συνάρτηση που θα μας επιστρέψει το πλήθος
των σπίρτων που θα αφαιρεθούν.
player player
58 # επιλογή κίνησης, ανάλογα με τον παίκτη
59 if player == computer:
60 # σπίρτα που θα πάρει ο υπολογιστής
61 removed = randomMatches(matches) computer computer
62 print("Ο υπολογιστής παίρνει", removed)
(α) (β)
63 else:
64 # ανάγνωση σπίρτων που θα πάρει ο παίκτης Σχήμα 4.5: Επιλογή κίνησης, ανάλογα
65 removed = readMatches(player, matches) με τον παίκτη: (α) αν η computer έχει
ίδια τιμή με την player, τότε είναι
Χρειάζεται να φροντίσουμε μια ακόμα σημαντική λεπτομέρεια. Μέ- σειρά του προγράμματος να παίξει,
χρι στιγμής, στο τέλος του παιχνιδιού ανακοινώνεται ο αριθμός του ενώ (β) σε διαφορετική περίπτωση,
έχει σειρά να παίξει ο χρήστης.
παίκτη που κέρδισε. Τώρα που μόνο ο ένας παίκτης είναι άνθρωπος,
είναι προτιμότερο να εμφανίζουμε διαφοροποιημένα μηνύματα.
72 # εμφάνιση αποτελέσματος παιχνιδιού
73 if player == computer:
74 print("Κέρδισε ο υπολογιστής.")
75 else:
76 print("Παίκτη", player, "κέρδισες!")
nim/src/nim.5.py
4 0 3 3
5 1 0 αδιάφορο
6 2 1 1
7 3 2 2
8 0 3 3
9 1 0 αδιάφορο
10 2 1 1
…
42 def randomMatches(m):
43 """ Επιλέγει κι επιστρέφει ένα τυχαίο
44 αλλά έγκυρο πλήθος σπίρτων που θα αφαιρεθούν.
45 m: πλήθος σπίρτων που απομένουν
46 """
47 return random.randint(1,maxMatches(m))
48 def computeMatches(m):
49 """ Επιλέγει κι επιστρέφει το βέλτιστο
50 πλήθος σπίρτων που θα πρέπει να αφαιρεθούν.
51 Αν δεν υπάρχει, επιστρέφει μια τυχαία τιμή.
52 m: πλήθος σπίρτων που απομένουν
53 """
54 # υπολογισμός υπολοίπου
55 mod = (m - 1) % 4
56 if mod == 0:
57 # ανεπιθύμητη νησίδα: τυχαία κίνηση
58 return randomMatches(m)
59 else:
60 return mod
61 # αρχικό πλήθος σπίρτων
62 matches = random.randint(7,21)
63 # εμφάνιση αρχικού πλήθους σπίρτων
64 print("Αρχικό πλήθος σπίρτων:", matches)
65 # επιλογή παίκτη-υπολογιστή
66 computer = random.randint(1,2)
67 # ορισμός παίκτη που θα παίξει πρώτος
68 player = 1
69 # επανάληψη: συνεχίζεται όσο υπάρχουν σπίρτα
70 while matches > 0:
71 # επιλογή κίνησης, ανάλογα με τον παίκτη
72 if player == computer:
73 # σπίρτα που θα πάρει ο υπολογιστής
74 removed = computeMatches(matches)
75 print("Ο υπολογιστής παίρνει", removed)
76 else:
77 # ανάγνωση σπίρτων που θα πάρει ο παίκτης
78 removed = readMatches(player, matches)
79 # μείωση σπίρτων
80 matches = matches - removed
81 # εμφάνιση πλήθους σπίρτων που απομένουν
82 print("Σπίρτα που απομένουν:", matches)
83 # εναλλαγή παίκτη
84 player = next(player)
ΤΟ ΠΑΙΧΝΙΔΙ ΤΗΣ ΑΦΑΙΡΕΣΗΣ 12
Τροποποιήσεις – Επεκτάσεις
4.1 Το τελικό πρόγραμμα συμπεριφέρεται λίγο ανόητα όταν απομείνει μόνο
ένα σπίρτο: ενώ είναι βέβαιο ότι ο παίκτης που έχει σειρά να παίξει έχει
χάσει, τον ρωτάει κανονικότατα πόσα σπίρτα θέλει να αφαιρέσει. Μάλι-
στα, αν πληκτρολογήσει έναν μη-έγκυρο αριθμό, του απαντά με το μή-
νυμα Πάρε από 1 μέχρι και 1 σπίρτα .
Να τροποποιήσετε το πρόγραμμα έτσι ώστε το παιχνίδι να σταματά αυ-
τόματα όταν απομένει μόνο ένα σπίρτο, χωρίς να ρωτάει τον παίκτη πόσα
σπίρτα θέλει να αφαιρέσει.
Για να σταματάει το παιχνίδι όταν απομένει μόνο ένα σπίρτο, αρκεί μια μι-
κρή τροποποίηση στη συνθήκη της while. Όμως το σημείο που χρειάζεται
προσοχή είναι η ανακοίνωση του νικητή.
nim/exercises/subtraction-oneleft.py
4.5 Η συνάρτηση next δέχεται σαν παράμετρο τον αριθμό p ενός παίκτη και
επιστρέφει τον αριθμό του παίκτη που παίζει μετά από τον p. Θεωρείται
όμως δεδομένο ότι στο παιχνίδι συμμετέχουν δύο παίκτες.
Να επεκτείνετε τη συνάρτηση next, έτσι ώστε να δέχεται σαν επιπρό-
σθετη παράμετρο το πλήθος q των παικτών που συμμετέχουν στο παι-
χνίδι. Έτσι, η ίδια συνάρτηση θα μπορεί να χρησιμοποιηθεί σε διαφορε-
τικά παιχνίδια.
Η τιμή της παραμέτρου q μπορεί να είναι οποιοσδήποτε ακέραιος. Δε χρειά-
ζεται να υποθέσετε ότι υπάρχει κάποιο όριο.
nim/exercises/next-generalized.py
ΤΟ ΠΑΙΧΝΙΔΙ ΤΗΣ ΑΦΑΙΡΕΣΗΣ 13
Ασκήσεις
4.6 Στο παιχνίδι «Τα Ζυγά Κερδίζουν» οι δύο παίκτες ξεκινούν με μια σειρά
από αντικείμενα. Το αρχικό πλήθος των αντικειμένων πρέπει να είναι
περιττός αριθμός. Κάθε ένας από τους δύο παίκτες αφαιρεί με τη σειρά
του από ένα μέχρι και τέσσερα αντικείμενα, μέχρι ν’ αφαιρεθούν όλα.
Νικητής είναι ο παίκτης που στο τέλος του παιχνιδιού απομένει με άρτιο
(ζυγό) πλήθος αντικειμένων. Να αναπτύξετε ένα πρόγραμμα που θα δια-
βάζει σε κάθε γύρο τον αριθμό των σπίρτων που αφαιρεί ο παίκτης που
έχει σειρά και στο τέλος ανακοινώνει το νικητή.
nim/exercises/evenwins.py
4.7 Στο παιχνίδι «Τα Ζυγά Κερδίζουν» υπάρχουν επίσης ανεπιθύμητες νη-
σίδες, δηλαδή καταστάσεις από τις οποίες ένας παίκτης δεν μπορεί ν’
αποφύγει την ήττα, εφόσον ο αντίπαλός του παίξει σωστά. Αν ένας παί-
κτης δεν βρίσκεται σε ανεπιθύμητη νησίδα τότε μπορεί να οδηγήσει τον
αντίπαλό του σε μία και να κερδίσει. Προσπαθήστε να καταγράψετε τις
πιθανές καταστάσεις του παιχνιδιού όταν απομένουν λίγα σπίρτα (π.χ.
από 1 μέχρι και 6) και ποια είναι η καλύτερη κίνηση σε κάθε μία απ’
αυτές. Το εγχείρημα είναι δυσκολότερο απ’ ότι στο Παιχνίδι της Αφαί-
ρεσης γιατί εδώ η καλύτερη κίνηση δεν εξαρτάται μόνο από το πλήθος
των σπίρτων που απομένουν. Πρέπει να λάβετε υπόψη αν ο καθένας από
τους δύο παίκτες έχει συγκεντρώσει άρτιο ή περιττό πλήθος σπίρτων.
Τελικός σας σκοπός είναι να τροποποιήσετε το πρόγραμμα της προηγού-
μενης άσκησης έτσι ώστε να παίζει με αντίπαλο το χρήστη.
nim/exercises/evenwins-auto.py
4.8 Να επεκτείνετε το παιχνίδι Ανάμεσα ή Acey Ducey (κεφ. 2), έτσι ώστε
να παίζεται επαναληπτικά. Το πρόγραμμά σας θα πρέπει αρχικά να ρω-
τάει τον παίκτη το συνολικό ποσό με το οποίο επιθυμεί να ξεκινήσει το
παιχνίδι. Με το ίδιο ακριβώς ποσό θα ξεκινήσει και η «μάνα». Σε κάθε
γύρο, όταν ο παίκτης ερωτάται για το ποσό που θα στοιχηματίσει, το
πρόγραμμά σας θα πρέπει να ελέγχει ότι το ποσό αυτό είναι θετικό και
δεν ξεπερνά το συνολικό ποσό που διαθέτει εκείνη την στιγμή ο παί-
κτης, διαφορετικά θα πρέπει να επαναλαμβάνει την ερώτηση μέχρι το
στοίχημα να είναι έγκυρο. Το παιχνίδι θα τελειώνει όταν εξαντληθούν τα
χρήματα του παίκτη ή της «μάνας».
nim/exercises/aceyducey-iterative.py
4.10 Τα παιδιά στο δημοτικό μαθαίνουν πρόσθεση μέσα από ασκήσεις αυξανό-
μενης δυσκολίας. Αρχικά, τους ζητείται να προσθέσουν δύο μονοψήφιους
αριθμούς, το άθροισμα των οποίων δεν ξεπερνά το 9.
Παράδειγμα: 4 + 2 = _
Μετά τους ζητείται να προσθέσουν δύο μονοψήφιους αριθμούς, το άθροι-
σμα των οποίων ξεπερνά το 10.
Παράδειγμα: 4 + 8 = _
Το επόμενο στάδιο είναι η πρόσθεση ενός διψήφιου κι ενός μονοψήφιου,
το άθροισμα των οποίων πιθανώς να ξεπερνά τη δεκάδα του διψήφιου,
αλλά όχι το 99.
Παράδειγμα: 42 + 9 = _
Μπορείτε να φτιάξετε ένα πρόγραμμα εξάσκησης για τα παιδιά του Δη-
μοτικού. Αρχικά, το πρόγραμμά σας θα πρέπει να ρωτά ποιο είναι το επι-
θυμητό επίπεδο ασκήσεων και να παράγει πέντε από αυτές. Εδώ έχουμε
περιγράψει τα τρία πρώτα επίπεδα, αλλά εσείς μπορείτε να φτιάξετε κι
άλλα ή ακόμα και να περάσετε σε άλλες πράξεις εκτός από την πρό-
σθεση. Μην σας απασχολεί αν καμιά φορά τυχαίνει να εμφανίζονται δι-
πλότυπες ασκήσεις, αυτό προς το παρόν είναι δύσκολο να το αποφύγετε.
Αν το παιδί κάνει λάθος σε κάποια από τις ερωτήσεις το πρόγραμμα μπο-
ρεί να του εμφανίζει άμεσα την σωστή απάντηση ή να του δίνει κι άλλες
ευκαιρίες. Ακόμα καλύτερο θα ήταν αν το πρόγραμμά σας εξηγούσε πως
προκύπτει η σωστή απάντηση ή έδινε μια υπόδειξη πριν το παιδί ξανα-
προσπαθήσει.
Παράδειγμα: 42 + 9 = _. Το παιδί πληκτρολογεί 52 και το πρόγραμμα του
ζητά να ξαναπροσπαθήσει, υποδεικνύοντας ότι 42 + 9 = 42 + 8 + 1 = _
nim/exercises/mathgame.py
ΤΟ ΠΑΙΧΝΙΔΙ ΤΗΣ ΑΦΑΙΡΕΣΗΣ 15
Υποπρογραμματα Είδαμε ήδη ότι για να αντιμετωπίσουμε την αυξανόμενη έκταση και πολυπλοκότητα των
προγραμμάτων χρησιμοποιούμε υποπρογράμματα, μικρά προγράμματα που τα συνδυάζουμε για να φτιάξουμε
συνθετότερα, κατά τον ίδιο τρόπο με τον οποίο κατασκευάζουμε μια περίπλοκη μηχανή από απλούστερα εξαρτή-
ματα, κάθε ένα από τα οποία εξυπηρετεί μια συγκεκριμένη λειτουργία. Ο κατακερματισμός των προγραμμάτων
σε απλούστερα μας βοηθά να σκεφτόμαστε τμηματικά και να αναλύουμε το γενικότερο πρόβλημα σε επιμέρους
απλούστερα προβλήματα. Τα υποπρογράμματα θα πρέπει να θεωρούνται όσο το δυνατόν ανεξάρτητα από το
πλαίσιο μέσα στο οποίο χρησιμοποιούνται. Αντιμετωπίζουμε κάθε υποπρόγραμμα ως ένα κομμάτι κώδικα που
λύνει ένα μικρό πρόβλημα με γενικό τρόπο και έτσι είναι επαναχρησιμοποιήσιμο. Μπορεί να κληθεί από δια-
φορετικά σημεία και για διαφορετικούς λόγους μέσα σ’ ένα πρόγραμμα, αλλά και να χρησιμοποιηθεί και σε άλλα
προγράμματα στο μέλλον. Οι εσωτερικές λεπτομέρειες της λειτουργίας κάθε υποπρογράμματος είναι κρυμμέ-
νες για εκείνους που το χρησιμοποιούν. Όποιος θέλει να χρησιμοποιήσει ένα υποπρόγραμμα δεν χρειάζεται να
καταλαβαίνει πως λειτουργεί και με ποια μέθοδο υπολογίζεται το αποτέλεσμα. Ο Al Sweigart, στο εξαιρετικό
του βιβλίο Invent Your Own Computer Games with Python, γράφει: «Το ωραίο με τις συναρτήσεις είναι ότι
χρειάζεται μόνο να ξέρουμε τι κάνουν, αλλά όχι πως το κάνουν.» Αντίστοιχα κρυμμένες είναι και τυχόν αλλαγές
στα υποπρογράμματά μας. Αν το υποπρόγραμμα τροποποιηθεί (επειδή βρήκαμε κάποιο λάθος, κάποιο τρόπο
να λειτουργεί καλύτερα ή ακόμα και κάποια καλύτερη μέθοδο) οι αλλαγές θ’ αφορούν μόνο το εσωτερικό του
υποπρογράμματος, ενώ το υπόλοιπο πρόγραμμα θα παραμείνει αμετάβλητο.
Υπολογιστες και Παιχνιδια Στις αρχές της δεκαετίας του 1950 άρχισε να εξετάζεται συστηματικά η δυνατό-
τητα χρήσης των ηλεκτρονικών υπολογιστών, που ήταν ακόμα στα πρώτα τους βήματα, για το παίξιμο παιχνι-
διών. Ο Claude Shannon δημοσίευσε το 1950 μια εργασία με τίτλο Programming a Computer for Playing Chess.
Εκεί περιέγραψε ένα σύνολο τεχνικών που θα επέτρεπαν σ’ έναν υπολογιστή να απαριθμεί συστηματικά τις πι-
θανές κινήσεις ενός παιχνιδιού, να τις αξιολογεί και να παίρνει αποφάσεις σχετικά με τις κινήσεις που θα έπρεπε
να επιλέξει. Μια παρόμοια εργασία, με τίτλο Digital Computers Applied to Games, δημοσίευσε το 1953 και ο
Alan Turing. Το 1997, ένας υπολογιστής της IBM με το όνομα Deep Blue, κέρδισε τον παγκόσμιο πρωταθλητή
Gary Kasparov. Σήμερα, ένας οικιακός υπολογιστής έχει την υπολογιστική ισχύ του Deep Blue και λέγεται ότι
ο τρόπος παιχνιδιού των πρωταθλητών έχει αλλάξει σημαντικά από τότε που ισχυρά σκακιστικά προγράμματα
χρησιμοποιούνται για προπόνηση και ανάλυση θέσεων. Αν και το σκάκι θεωρείται ο βασιλιάς των παιχνιδιών
και συνδέεται παραδοσιακά με τη νοημοσύνη, δεν είναι φυσικά το μόνο παιχνίδι που έχει απασχολήσει τους
ερευνητές. Ένα ιστορικό πρόγραμμα που έπαιζε ντάμα αναπτύχθηκε από τον Arthur Samuel τη δεκαετία του
1950. To 1989 ξεκίνησε η ανάπτυξη του προγράμματος Chinook, το οποίο έπαιξε με τον παγκόσμιο πρωταθλητή
το 1992. Το 2007, μετά από σχεδόν 20 χρόνια υπολογισμών, έγινε ανίκητο “λύνοντας” το παιχνίδι της ντάμας,
έχοντας απαριθμήσει όλες τις πιθανές θέσεις του παιχνιδιού (περίπου 500 δισεκατομμύρια δισεκατομμύρια) και
γνωρίζοντας τη βέλτιστη κίνηση για κάθε μια από αυτές. Το ΝΙΜ είναι πολύ απλούστερο παιχνίδι. Οι (απο-
δεδειγμένα) καλύτερες δυνατές κινήσεις είναι γνωστές και υπολογίζονται εύκολα. Αυτός είναι ο λόγος που το
ΝΙΜ ήταν πιθανότατα το πρώτο παιχνίδι που αυτοματοποιήθηκε.
Υπολογιστες και Νοημοσυνη Είναι αναπόφευκτο να αναρωτηθεί κανείς πόσο “έξυπνο” είναι ένα σκακιστικό
πρόγραμμα που κερδίζει παγκόσμιους πρωταθλητές. Το ερώτημα αν οι υπολογιστές έχουν τη δυνατότητα να
επιδεικνύουν “νοημοσύνη” προέκυψε πολύ νωρίς και είναι τόσο δύσκολο ν’ απαντηθεί όσο να ορίσει κανείς τι
είναι η ευφυία και η νοημοσύνη. Η Ada Lovelace, κόρη του λόρδου Βύρωνα και στενή συνεργάτης του Charles
Babbage είχε γράψει το 1842 ότι οι υπολογιστικές μηχανές μπορούν να κάνουν μόνο αυτά που εμείς μπορούμε
να τις προγραμματίσουμε να κάνουν. Ο χαρισματικός Alan Turing έγραψε το 1950 ένα ιστορικό άρθρο με τίτλο
Computing Machinery and Intelligence (Υπολογιστικές Μηχανές και Νοημοσύνη), το οποίο ξεκινούσε με την
ΤΟ ΠΑΙΧΝΙΔΙ ΤΗΣ ΑΦΑΙΡΕΣΗΣ 16
πρόταση “Προτείνω να εξετάσουμε την ερώτηση: Μπορούν οι μηχανές να σκεφτούν;” Πολύ συνοπτικά, ο Turing
ισχυρίστηκε ότι η απάντηση μπορεί να είναι καταφατική όταν μια μηχανή μπορεί να συνομιλήσει μ’ έναν άν-
θρωπο και να τον πείσει ότι δεν είναι μηχανή. Είναι σημαντικό ότι ο Turing εστίασε στον τρόπο με τον οποίο
οι εξωτερικοί παρατηρητές αντιλαμβάνονται την συμπεριφορά μιας μηχανής ως εφυή, χωρίς να έχει σημασία
με ποιες εσωτερικές διεργασίες επιτυγχάνεται αυτό. Οι απόψεις του Turing δεν είναι καθολικά αποδεκτές, το
αντίθετο μάλιστα. Είναι πάντως σημαντικό να θυμάστε ότι οι υπολογιστές είναι σχεδιασμένοι για να κάνουν
μόνο ένα πράγμα: να εκτελούν προγράμματα, τα οποία επεξεργάζονται τις τιμές που δέχονται στην είσοδό
τους και επιστρέφουν αποτελέσματα στην έξοδό τους. Με την πάροδο του χρόνου, τα εξαρτήματά των υπολο-
γιστών εξελίσσονται, οι ταχύτητες με τις οποίες λειτουργούν αυξάνονται και οι συσκευές εισόδου-εξόδου που
διαθέτουν επιτρέπουν πλέον μια πολύ φυσική αλληλεπίδραση με τους χρήστες. Όμως το γεγονός παραμένει πως
για κάθε τι που κάνουν, υπάρχει ένα πρόγραμμα, γραμμένο από ανθρώπους, που τους υπαγορεύει με απόλυτη
ακρίβεια πως να το κάνουν. Υπάρχουν βέβαια και προγράμματα που μαθαίνουν και προσαρμόζουν την συμπε-
ριφορά τους. Υπό αυτή την έννοια, η συμπεριφορά τους δεν είναι προγραμματισμένη, αλλά ο μηχανισμός μέσω
του οποίου την τροποποιούν καθώς μαθαίνουν είναι. Έτσι, τα αυτοκίνητα της Google κινούνται χωρίς οδηγό
για χιλιάδες μίλια χωρίς ατυχήματα, το ρομπότ της NASA Curiosity πλοηγείται αυτόνομα στην επιφάνεια του
Άρη, το σύστημα Watson της IBM κερδίζει σε τηλεπαιχνίδια και βοηθά στη διάγνωση ασθενειών, επειδή ομάδες
ανθρώπων καθόρισαν (με πολύ κόπο) πως ακριβώς μπορούν να γίνουν όλα αυτά.
Η Μέρα της Εβδομάδας 4
Στο κεφάλαιο αυτό θ’ αναπτύξουμε ένα πρόγραμμα για να υπολογίζουμε
σε ποια ημέρα της εβδομάδας αντιστοιχεί μια συγκεκριμένη ημερομηνία. 29 Οκτωβρίου 2015
Θα δανειστούμε τη μέθοδο υπολογισμού από τον Gauss. Στην πορεία θα 22:07
1
Η ΜΕΡΑ ΤΗΣ ΕΒΔΟΜΑΔΑΣ 2
Εναλλακτικά:
# είσοδος ημερομηνίας από το χρήστη
year = int(input("Έτος: "))
month = int(input("Mήνας: "))
day = int(input("Ημέρα: "))
42 def daysOfMonth(m,y):
43 """ Επιστρέφει το πλήθος των ημερών ενός μήνα.
44 m: ο αριθμός του μήνα (1-12)
45 y: ο αριθμός του έτους (απαραίτητο όταν m=2)
46 """
47 if m == 2:
48 # Φεβρουάριος
49 if isLeap(y):
50 return 29
51 else:
52 return 28
53 elif m <= 7:
54 # Ιανουάριος - Ιούλιος (πλην Φεβρουαρίου)
55 if m % 2 == 1:
56 return 31
57 else:
58 return 30
59 else:
60 # Αύγουστος - Δεκέμβριος
61 if m % 2 == 1:
62 return 30
63 else:
64 return 31
H ανάγνωση τιμής για την ημέρα μπορεί πλέον να γίνει καθορίζο-
ντας σωστά το πλήθος των ημερών του μήνα.
65 # είσοδος ημερομηνίας από το χρήστη
66 year = readInt("Έτος: ", 1923, 2999)
67 month = readInt("Mήνας: ", 1, 12)
68 day = readInt("Ημέρα: ", 1, daysOfMonth(month,year))
src/weekday.5.py
17 def readInt(prompt,lower,upper):
18 """ Εμφανίζει μια προτροπή και μετά διαβάζει
19 από το χρήστη κι επιστρέφει έναν ακέραιο αριθμό,
20 εξασφαλίζοντας ότι βρίσκεται εντός ορίων.
21 prompt: η προτροπή που εμφανίζεται στο χρήστη
22 lower, upper: τα όρια
23 """
24 # εμφάνιση προτροπής και ανάγνωση τιμής
25 print(prompt, end="")
26 num = int(input())
27 # επανάληψη, όσο η τιμή είναι εκτός ορίων
28 while num < lower or num > upper:
29 # μήνυμα λάθους
30 print("Δώστε τιμή από",lower,"μέχρι",upper)
31 # εμφάνιση προτροπής και ανάγνωση τιμής
32 print(prompt, end="")
33 num = int(input())
34 # η έγκυρη τιμή επιστρέφεται
35 return num
36 def isLeap(year):
37 """ Επιστρέφει την τιμή True αν το έτος year
38 είναι δίσεκτο, αλλιώς επιστρέφει False.
39 """
40 return ((year % 4 == 0 and year % 100 > 0) or
41 year % 400 == 0)
42 def daysOfMonth(m,y):
43 """ Επιστρέφει το πλήθος των ημερών ενός μήνα.
44 m: ο αριθμός του μήνα (1-12)
45 y: ο αριθμός του έτους (απαραίτητο όταν m=2)
46 """
47 if m == 2:
48 # Φεβρουάριος
49 if isLeap(y):
50 return 29
51 else:
52 return 28
53 elif m <= 7:
54 # Ιανουάριος - Ιούλιος (πλην Φεβρουαρίου)
55 if m % 2 == 1:
56 return 31
57 else:
58 return 30
59 else:
60 # Αύγουστος - Δεκέμβριος
61 if m % 2 == 1:
62 return 30
63 else:
64 return 31
Η ΜΕΡΑ ΤΗΣ ΕΒΔΟΜΑΔΑΣ 10
Δομες Δεδομενων Οργανωμένες συλλογές τιμών όπως οι πλειάδες ονομάζονται δομές δεδομένων. Μια δομή
δεδομένων δεν είναι ένα απλό σύνολο από τιμές, δεν αποτελεί μια απλή γενίκευση των μεταβλητών, αλλά είναι
δομημένη με συγκεκριμένο τρόπο. Τα διαφορετικά είδη δομών δεδομένων διαφοροποιούνται κυρίως από τον
τρόπο με τον οποίο τα δεδομένα αποθηκεύονται, ανακτώνται και συσχετίζονται μεταξύ τους.
Διαπλανητικό Κουίζ 5
Στο κεφάλαιο αυτό θα υλοποιήσουμε ένα παιχνίδι ερωτήσεων με θέμα τους
πλανήτες του ηλιακού μας συστήματος. Ο παίκτης θα καλείται να βρει
έναν πλανήτη από τη θέση του στο ηλιακό σύστημα, να τον μαντέψει από 17 Απριλίου 2016
τους γειτονικούς του πλανήτες ή να βάλει τους πλανήτες σε σωστή σειρά, 22:55
Οι ερωτήσεις που θα τίθενται στον παίκτη δεν θα είναι οι ίδιες κάθε φορά,
αλλά θα δημιουργούνται δυναμικά, κατά την εκτέλεση του προγράμματος.
Μέσα από το πρόγραμμά μας θα γνωρίσουμε μια πολύ σημαντική έννοια
στον προγραμματισμό, τη δομή δεδομένων και πιο συγκεκριμένα μια δομή
δεδομένων που ονομάζεται λίστα. Θα έχουμε επίσης την ευκαιρία να εξοι-
κειωθούμε ακόμη περισσότερο με τα υποπρογράμματα, μιας και όλο το
πρόγραμμα υλοποιείται με τη βοήθειά τους.
Έννοιες: λίστες, υποπρογράμματα
Τα στοιχεία της λίστας καταχωρούνται ακολουθιακά, που σημαίνει Σχήμα 5.1: Η λίστα με τα ονόματα των
πλανητών. Οι θέσεις της λίστας είναι
ότι παίζει ρόλο η σειρά με την οποία τα εισάγουμε. αριθμημένες. Με βάση τους αριθμούς
των θέσεων μπορούμε ν’ αναφερθούμε
σε κάθε στοιχείο ξεχωριστά. Προσοχή
όμως, η αρίθμηση ξεκινά από το 0.
1
ΔΙΑΠΛΑΝΗΤΙΚΟ ΚΟΥΙΖ 2
Η Διαμάχη
Με τον Πλούτωνα τί γίνεται; Μερικοί από εμάς μεγάλωσαν μαθαίνοντας
ότι ο Πλούτωνας είναι πλανήτης!
Ο Πλούτωνας ανακαλύφθηκε το 1930 και μέχρι το 2006 θεωρούνταν
ο ένατος πλανήτης του ηλιακού μας συστήματος. Αν και η Διεθνής
Αστρονομική Ένωση τον έχει πια υποβιβάσει σε πλανήτη-νάνο, η
παράλειψή του από τη λίστα των πλανητών ίσως δημιουργεί σε κά-
ποιους μια συναισθηματική φόρτιση. Το πρόγραμμά μας λοιπόν, εκ-
δηλώνοντας ευαισθησία, θα ρωτά τον χρήστη αν θέλει ο Πλούτωνας
να θεωρηθεί ένας από τους πλανήτες και ανάλογα θα προσθέτει την
τιμή "Πλούτωνας" στη λίστα με τα ονόματα των πλανητών.
Αρχικά, θα υλοποιήσουμε μια συνάρτηση που θα ρωτά τον παίκτη
αν επιθυμεί ο Πλούτωνας να συμπεριληφθεί στους πλανήτες και θα
επιστρέφει την τιμή True ή False ανάλογα με την απάντηση του.
1 def addPluto():
2 ''' Ρωτά αν ο Πλούτωνας θα θεωρηθεί πλανήτης
3 κι ανάλογα επιστρέφει την τιμή True ή False
4 '''
5 # ερώτηση στον παίκτη για τον Πλούτωνα
6 print("Nα θεωρήσουμε τον Πλούτωνα πλανήτη (ν/ο);")
7 answer = input()
8 # επιστροφή τιμής ανάλογα με την aπάντηση
9 return answer == "Ν" or answer == "ν"
Η τελευταία γραμμή της συνάρτησης επιστρέφει την τιμή της συν-
θήκης. Είναι ένας αμεσότερος τρόπος να κάνουμε το εξής: 5 Κρόνος
επιλογή απαντήσεων
select(nbChoices, possible, correct)
προβολή απαντήσεων
showMultiple(choices)
Σχήμα 5.3: Το πρόβλημα της παραγωγής μιας ερώτησης πολλαπλής επιλογής ανα-
λύεται σε μικρότερα προβλήματα. Στην αναπαράσταση αυτή φαίνονται και τα
υποπρογράμματα που θα δημιουργηθούν στη συνέχεια για τα προβλήματα αυτά.
135 else:
136 # υπάρχουν και οι δύο γειτονικοί πλανήτες
137 next = planets[position + 1]
138 previous = planets[position - 1]
139 print("βρίσκεται ανάμεσα στους πλανήτες ",
140 previous, " και ", next, ";", sep = "")
Και αυτό το ερώτημα θα είναι πολλαπλής επιλογής, επομένως θα γί-
νει χρήση των υποπρογραμμάτων που υλοποιήσαμε προηγουμένως.
Θα εμφανιστούν 4 επιλογές από τη λίστα των πλανητών, συμπερι-
λαμβανομένης και της σωστής.
141 # πολλαπλή επιλογή
142 multipleChoice(4, planets, planet)
Πλέον μπορούμε να εμφανίσουμε το ερώτημα στον παίκτη.
156 # εύρεση του πλανήτη από τους γείτονές του
157 print("\nΕρώτηση 3:")
158 findByNeighbours(planets)
src/planets.6.py
Ο Παράταιρος
Ας ρωτήσουμε κάτι πιο περίπλοκο. Ας καλέσουμε τον παίκτη να ταξιδέ-
ψει νοερά ανάμεσα σε δύο πλανήτες και να σκεφτεί ποιους πλανήτες θα
συναντήσει στη διαδρομή. Εμείς θα τον καλούμε να βρει έναν πλανήτη
που δεν θα είναι μέρος της διαδρομής.
Θα υλοποιήσουμε μια ακόμα συνάρτηση, στην οποία καταρχήν θα
επιλέγουμε τους δύο πλανήτες που θα αποτελέσουν την αφετηρία
και τον τερματισμό του ταξιδιού.
planets
165 def between(planets): between out
166 ''' Εμφανίζει 4 πλανήτες, από τους οποίους μόνο
167 οι 3 αποτελούν τμήμα μιας διαδρομής. Καλεί τον
168 παίκτη να επιλέξει τον 4ο "παράταιρο" πλανήτη.
169 planets: λίστα με τα ονόματα όλων των πλανητών start
170 '''
171 # τυχαία επιλογή αφετηρίας και τερματισμού
172 nbPlanets = len(planets)
173 start = random.randint(0, nbPlanets - 5)
174 stop = start + 4
stop
Παρατηρήστε ότι η αφετηρία δεν επιλέγεται απ’ όλο το εύρος της
λίστας, αλλά από ένα περιορισμένο διάστημα, ώστε να υπάρχουν
μετά από αυτή τουλάχιστον τέσσερις πλανήτες. Ο τέταρτος από αυ-
τούς είναι ο προορισμός της διαδρομής.
Οι τρεις ενδιάμεσοι πλανήτες τοποθετούνται σε μια λίστα για να Σχήμα 5.8: Η λίστα between των
χρησιμοποιηθούν ως (λανθασμένες) απαντήσεις στην ερώτηση πολ- ενδιάμεσων πλανητών και η λίστα
out των πλανητών που δεν αποτε-
λαπλής επιλογής. Οι πλανήτες που δεν αποτελούν κομμάτι της δια- λούν τμήμα της διαδρομής. Και οι
δρομής συγκεντρώνονται επίσης σε μια λίστα, από την οποία θα δύο προκύπτουν από τεμαχισμούς της
επιλεχθεί τυχαία μια σωστή απάντηση. planets και συνενώσεις.
των ενδιάμεσων πλανητών και μιας λίστας που αποτελείται από ένα
μόνο στοιχείο: την σωστή απάντηση correct.
Στη συνέχεια θα προσθέσουμε την ερώτηση στο πρόγραμμά μας.
207 # εύρεση του "παράταιρου" πλανήτη
208 print("\nΕρώτηση 5:")
209 between(planets)
src/planets.9.py
Όλα Σε Τάξη
Ας τελειώσουμε μ’ ένα ερώτημα που θα εμφανίζει στον παίκτη ανακατε-
μένα τα ονόματα τεσσάρων διαδοχικών πλανητών και θα του ζητά να τα
βάλει σε σειρά, ξεκινώντας από τον κοντινότερο πλανήτη στον Ήλιο.
Η συνάρτηση που θα υλοποιήσουμε επιλέγει αρχικά τη θέση του
πρώτου πλανήτη της τετράδας και δημιουργεί μια νέα λίστα που
περιέχει τον πλανήτη αυτόν και τους τρεις που τον διαδέχονται.
188 def outOfOrder(planets):
189 ''' Καλεί τον παίκτη να βάλει στη σειρά
190 τα ονόματα 4 διαδοχικών πλανητών.
191 planets: λίστα με τα ονόματα όλων των πλανητών
192 '''
193 # τυχαία επιλογή θέσης αρχικού πλανήτη
194 nbPlanets = len(planets)
195 start = random.randint(0, nbPlanets - 5)
196 # δημιουργία λίστας 4 διαδοχικών πλανητών
197 fourPlanets = planets[start : start + 4]
Θα δημιουργήσουμε ένα αντίγραφο της λίστας των τεσσάρων πλα-
νητών, ώστε να τους ανακατέψουμε και να τους εμφανίσουμε στον
παίκτη με τυχαία σειρά. Δεν ανακατεύουμε απευθείας την αρχική
λίστα, τη χρειαζόμαστε για να υπολογίσουμε τη σωστή απάντηση.
Η συνάρτηση shuffle() της βιβλιο-
198 # αντίγραφο της λίστας των 4 διαδοχικών πλανητών
θήκης random δέχεται σαν παράμετρο
199 shuffled = fourPlanets.copy() μια λίστα και ανακατατάσσει τις τιμές
200 # ανακάτεμα των 4 πλανητών σε τυχαίες θέσεις της σε τυχαίες θέσεις.
201 random.shuffle(shuffled)
Τώρα μπορούμε να διατυπώσουμε την ερώτηση και να ζητήσουμε
την απάντηση του παίκτη. Τα ονόματα των πλανητών θα εμφανί-
ζονται αριθμημένα, ώστε ο παίκτης να γράφει τον αριθμό που αντι-
στοιχεί στον κάθε πλανήτη για να δώσει τη σωστή σειρά.
Η μέθοδος split() εφαρμόζεται σε
202 # ερώτηση στον παίκτη
αλφαριθμητικές τιμές, τις οποίες χω-
203 print("Βάλε τους πλανήτες με τη σωστή σειρά:") ρίζει σε επιμέρους τμήματα εκεί όπου
204 showMultiple(shuffled) υπάρχουν κενά και επιστρέφει μια λί-
205 print("\nΔώσε 4 αριθμούς με κενά ανάμεσά τους.") στα με τα τμήματα αυτά.
206 # η απάντηση "χωρίζεται" όπου υπάρχουν κενά Εδώ, η απάντηση του χρήστη θα έχει
207 # και τα συστατικά της τοποθετούνται σε μια λίστα τη μορφή "1 2 3 4" και η split()
208 answer = input().split() θα παράγει μια λίστα της μορφής
["1", "2", "3", "4"].
ΔΙΑΠΛΑΝΗΤΙΚΟ ΚΟΥΙΖ 14
Για να ελέγξουμε αν η απάντηση του παίκτη είναι σωστή, χρειάζε- fourPlanets shuffled
ται να κατασκευάσουμε μια λίστα αποτελούμενη από τους αριθμούς
των πλανητών με τη σειρά που πρέπει να τους δώσει ο παίκτης. Γη Άρης
0 0
Για να βρούμε τους αριθμούς που πρέπει να πληκτρολογήσει ο παί-
1 Άρης 1 Κρόνος
κτης πρέπει να διατρέξουμε την αρχική λίστα των πλανητών και
για κάθε πλανήτη να υπολογίσουμε τη θέση του στην ανακατεμένη 2 Δίας 2 Γη
λίστα. Για παράδειγμα: ποιός είναι ο πρώτος αριθμός που πρέπει
3 Κρόνος 3 Δίας
να πληκτρολογήσει ο παίκτης; Είναι ο αριθμός που έχει ο πρώτος
πλανήτης της αρχικής λίστας μέσα στην ανακατεμένη λίστα.
Σχήμα 5.9: Ένα παράδειγμα. Η Γη εί-
209 # λίστα που θα περιέχει την σωστή απάντηση ναι πρώτη στην αρχική λίστα και
τρίτη στην ανακατεμένη λίστα. Άρα ο
210 correct = []
πρώτος αριθμός που πρέπει να πλη-
211 # για κάθε έναν από τους 4 διαδοχικούς πλανήτες κτρολογήσει ο χρήστης είναι το 3.
212 for planet in fourPlanets:
Η μέθοδος index() εφαρμόζεται σε
213 # σειρά του πλανήτη στην ανακατεμένη λίστα μια λίστα. Δέχεται σαν παράμετρο
214 position = shuffled.index(planet) + 1 ένα στοιχείο της λίστας και επιστρέ-
215 # προσθήκη σωστής σειράς στην απάντηση φει τη θέση του σε αυτή. Αν το στοι-
216 correct.append(str(position)) χείο δεν υπάρχει στη λίστα τότε προ-
κύπτει σφάλμα.
Ας ελέγξουμε πλέον αν η απάντηση του παίκτη είναι σωστή. Η συνάρτηση str() μετατρέπει μια
217 # έλεγχος απάντησης τιμή σε αλφαριθμητική. Εδώ είναι
απαραίτητο να μετατρέψουμε τις θέ-
218 if answer == correct: σεις των πλανητών σε αλφαριθμητι-
219 print("Μπράβο, η σειρά είναι σωστή.") κές για να συγκριθούν με τις αντίστοι-
220 else: χες αλφαριθμητικές τιμές που θα δια-
221 print("Η σωστή σειρά είναι:") βαστούν από το πληκτρολόγιο.
222 print(correct) Ο τελεστής == μπορεί να χρησιμοποι-
ηθεί και για να συγκριθούν δύο λίστες,
Ολοκληρώνουμε τα ερωτήματα που θα εμφανίσουμε στον παίκτη, στοιχείο προς στοιχείο.
καλώντας την συνάρτηση απ’ το κύριο πρόγραμμα.
245 # εύρεση της σωστής σειράς των πλανητών
246 print("\nΕρώτηση 6:")
247 outOfOrder(planets)
src/planets.10.py
Τροποποιήσεις – Επεκτάσεις
Στο κεφάλαιο αυτό μάθαμε πολλά για τις λίστες και τους διάφο-
ρους τρόπους με τους οποίους μπορούμε να τις επεξεργαστούμε στην
Python. Για να είναι ευκολότερο για εσάς να ανατρέχετε σε όλα
αυτά καθώς λύνετε τις ασκήσεις, τα συγκεντρώσαμε όλα (και με-
ρικά ακόμα) σ’ ένα “σκονάκι”. pythonies.mysch.gr/cslists.pdf
Ασκήσεις
5.7 Τα μπισκότα τύχης (fortune cookies) είναι μπισκότα που στο εσωτερικό
τους περιέχουν μηνύματα με προβλέψεις γι’ αυτόν που τα ανοίγει. Υλο-
ποιήστε ένα πρόγραμμα που θα καλεί τον χρήστη να ανοίξει ένα τυχερό
μπισκότο και στη συνέχεια θα του εμφανίζει μια τυχαία πρόβλεψη.
Μερικές ιδέες για τις προφητείες των μπισκότων: Η τύχη σου κρύβεται σε
άλλο μπισκότο. Ο καλύτερος τρόπος να παραμείνεις υγιής είναι να συ-
νεχίσεις να τρως τυχερά μπισκότα. Αποδέξου ότι κάποιες μέρες είσαι το
περιστέρι και κάποιες άλλες το άγαλμα. Μαθαίνεις από τα λάθη σου…
Σήμερα είναι μια μέρα που θα διδαχθείς πολλά. Η σκληρή δουλειά θα σου
ανταποδώσει στο μέλλον. Η τεμπελιά θα σου ανταποδώσει άμεσα… Όλοι
συμφωνούν: Είσαι ο καλύτερος!
5.8 Στο παιχνίδι της αντιστροφής, εμφανίζεται στον παίκτη μια λίστα με Η μέθοδος reverse() εφαρμόζεται σε
αριθμούς κι εκείνος καλείται να τους αναδιατάξει σε αύξουσα σειρά. Το μια λίστα κι αντιστρέφει τα στοι-
χεία της. Για παράδειγμα, η εντολή
μόνο πράγμα που καθορίζει ο παίκτης σε κάθε κίνησή του είναι πόσοι L.reverse() αντιστρέφει τη λίστα L.
αριθμοί θ’ αντιστραφούν (ξεκινώντας από τ’ αριστερά).
Ο τεμαχισμός μπορεί επίσης να χρη-
Για παράδειγμα, αν οι αριθμοί είναι: σιμοποιηθεί για την αντιστροφή μιας
λίστας. Για παράδειγμα, η έκφραση
2 3 4 5 1 6 7 8 9 L[::-1] δημιουργεί μια νέα λίστα,
που περιέχει όλα τα στοιχεία της L με
και ο παίκτης επιλέξει ν’ αντιστρέψει τέσσερις αριθμούς, τότε οι αριθμοί την αντίστροφη σειρά.
θα γίνουν:
Εδώ χρειάζεται ν’ αντιστρέψετε μόνο
5 4 3 2 1 6 7 8 9 ένα τμήμα της λίστας, οπότε θα χρεια-
στεί πρώτα να την τεμαχίσετε και, μετά
Αν στη συνέχεια ο παίκτης αντιστρέψει πέντε αριθμούς, κερδίζει! την αντιστροφή, να συνενώσετε τα
τμήματά της.
1 2 3 4 5 6 7 8 9
Να κατασκευάσετε πρόγραμμα που εμφανίζει στον παίκτη μια λίστα με
τους αριθμούς από το 1 μέχρι το 9 ανακατεμένους και στη συνέχεια ζητά
από τον παίκτη επαναληπτικά πόσους αριθμούς να αντιστρέψει. Το παι-
χνίδι τελειώνει όταν ο παίκτης καταφέρει να τοποθετήσει τους αριθμούς
στη σωστή σειρά ή όταν παραιτηθεί, απαντώντας ότι επιθυμεί να αντι-
στρέψει μηδέν αριθμούς. Αν ο παίκτης καταφέρει να ολοκληρώσει το παι-
χνίδι, το πρόγραμμα θα πρέπει να εμφανίζει πόσες κινήσεις χρειάστηκαν.
Όταν ολοκληρώσετε το πρόγραμμά σας, προσπαθήστε να το τροποποιή-
σετε έτσι ώστε οι κινήσεις να μην επιλέγονται από το χρήστη, αλλά από
το πρόγραμμα, το οποίο πια θα παίζει μόνο του.
5.9 Ο Ηλεκτρικός της Αθήνας εγκαινιάστηκε το 1869 και συνέδεε τον Πει-
ραιά με το Θησείο. Μετά από 88 χρόνια η διαδρομή επεκτάθηκε μέχρι την
Κηφισιά. Σήμερα αριθμεί 24 σταθμούς.
Υλοποιήστε ένα πρόγραμμα που θα βοηθά το χρήστη να μετακινηθεί με Βρείτε τη λίστα με τους σταθμούς
τον Ηλεκτρικό. Ο χρήστης θα δίνει το όνομα του σταθμού από τον οποίο στο αρχείο pythonies.mysch.gr/
src/metro.py. Αν το πρόγραμμά
θ’ αναχωρήσει και το όνομα του σταθμού στον οποίο θέλει να φτάσει
σας βρίσκεται στον ίδιο κατάλογο με
και το πρόγραμμα θα του εμφανίζει την κατεύθυνση που θα ακολουθήσει αυτό το αρχείο μπορείτε να γράψετε
(προς Πειραιά ή προς Κηφισιά) και τα ονόματα όλων των ενδιάμεσων from metro import stations και
σταθμών της διαδρομής. από εκεί και πέρα θα μπορείτε να
χρησιμοποιήσετε τη λίστα stations
με τους 24 σταθμούς.
ΔΙΑΠΛΑΝΗΤΙΚΟ ΚΟΥΙΖ 17
n = 0 1
n = 1 1 1
n = 2 1 2 1
n = 3 1 3 3 1
n = 4 1 4 6 4 1
n = 5 1 5 10 10 5 1
n = 6 1 6 15 20 15 6 1
Σχήμα 5.10: Οι 7 πρώτες γραμμές του τριγώνου του Pascal.
5.11 Στο τυχερό παιχνίδι Λοττο, ο παίκτης επιλέγει έξι αριθμούς από το 1
μέχρι και το 49. Στη συνέχεια κληρώνονται έξι αριθμοί με τυχαίο τρόπο.
ΔΙΑΠΛΑΝΗΤΙΚΟ ΚΟΥΙΖ 18
Δομες Δεδομενων και Λιστες Οι λίστες, δομές απλές κι ευέλικτες, έχουν βαρύνουσα σημασία στην ιστορία
της Πληροφορικής. Η δεύτερη γλώσσα προγραμματισμού που αναπτύχθηκε, η LISP του John McCarthy, βασί-
ζεται στις λίστες. Η Logo, με την σειρά της, μια ιστορική εκπαιδευτική γλώσσα προγραμματισμού, βασίζεται
κυρίως στη LISP.
Οι λίστες είναι ένα παράδειγμα οργάνωσης των δεδομένων μας, έτσι ώστε να αποτελούν μια ενιαία συλλογή
με συγκεκριμένη δομή. Υπάρχουν αναρίθμητοι τρόποι να οργανώσουμε τα δεδομένα μας, γι’ αυτό και υπάρχουν
και πάρα πολλές δομές δεδομένων. Ακόμα και οι πιο θεμελιώδεις έχουν εξωτικά ονόματα όπως στοίβες, ουρές,
δέντρα, σωροί ή γράφοι. Ο τρόπος οργάνωσης καθορίζει πόσο εύκολο είναι να επεξεργαστούμε τα δεδομένα
μας με συγκεκριμένο τρόπο. Γι’ αυτό και επιλέγουμε τη δομή που θα χρησιμοποιήσουμε κάθε φορά ανάλογα
με τον συγκεκριμένο τρόπο που θέλουμε να επεξεργαστούμε τα δεδομένα μας, στα πλαίσια ενός συγκεκριμένου
προβλήματος.
Μέχρι να αντιμετωπίσει κανείς στοιχειωδώς περίπλοκα προβλήματα είναι δύσκολο να διακρίνει γιατί χρειάζε-
ται να οργανωθούν τα δεδομένα – οι μεμονωμένες μεταβλητές φαίνονται υπεραρκετές, αλλά δεν είναι. Αν είστε
άνθρωποι με περιπετειώδες πνεύμα, μπορείτε να επιχειρήσετε να υλοποιήσετε το παιχνίδι αυτού του κεφαλαίου
χωρίς να χρησιμοποιήσετε λίστες.
Αριθμηση απο το Μηδεν Υπάρχουν δύο ειδών άνθρωποι στον κόσμο: (1) αυτοί που ξεκινούν την αρίθμηση
από το ένα και (1) αυτοί που ξεκινούν την αρίθμηση από το μηδέν. Αν πιάσατε το αστειάκι, έχετε καταλάβει αυτό
το λεπτό σημείο του κεφαλαίου. Η χρήση της αρίθμησης από το μηδέν ξεκίνησε τη δεκαετία του ’60 από ανάγκη,
για καθαρά τεχνικούς λόγους και είναι αλήθεια ότι σε εκείνους που την συναντούν για πρώτη φορά φαίνεται
από άβολη έως παρανοϊκή. Ωστόσο, έχει και σημαντικά πλεονεκτήματα. Ο ιδιόρυθμος Edsger Dijkstra, ένας
από τους ανθρώπους που συνέβαλαν καθοριστικά στην καθιέρωση της Πληροφορικής ως επιστήμη, έγραψε το
1982 τρεις ολόκληρες (χειρόγραφες) σελίδες με τίτλο “Γιατί η αρίθμηση πρέπει να ξεκινά από το μηδέν”. Τώρα, η
αρίθμηση από το μηδέν είναι πια από τα πολύ χαρακτηριστικά ιδιώματα της Πληροφορικής. Στο παιδικό βιβλίο
“Lauren Ipsum” του Carlos Bueno, η αρίθμηση των κεφαλαίων ξεκινά από το μηδέν και η ηρωίδα, καθώς αρχίζει
την περιπέτειά της, συναντά μια πινακίδα όπου το πρώτο μίλι της διαδρομής είναι φυσικά… το μηδενικό.
Αναπαραστάσεις
Πριν ξεκινήσουμε να γράψουμε έστω και μια γραμμή του προγράμ-
ματος, πρέπει να πάρουμε μια σημαντική απόφαση: ποια αναπαρά-
σταση θα χρησιμοποιήσουμε για την τρίλιζα; Δηλαδή ποιος θα εί-
ναι ο τρόπος με τον οποίο το πρόγραμμά μας θα αποθηκεύει και θα
διαχειρίζεται εσωτερικά τα περιεχόμενα των εννέα τετραγώνων του
παιχνιδιού; Η απόφαση αυτή είναι κομβική και θα επηρρεάσει σημα-
ντικά τη μορφή του προγράμματος που θα γράψουμε στη συνέχεια.
1
ΤΡΙΛΙΖΑ 2
Σχήμα 5.1: Παράδειγμα αναπαράστασης ενός συγκεκριμένου στιγμιοτύπου του Γενικά, τα στοιχεία μιας λίστας μπο-
παιχνιδιού με τη χρήση λίστας. Σε κάθε στοιχείο της λίστας αποθηκεύεται το περιε- ρεί να είναι ο,τιδήποτε, ακόμα και άλ-
χόμενο ενός τετραγώνου της τρίλιζας. Η συγκεκριμένη αντιστοιχία μεταξύ τετρα- λες λίστες.
γώνων και στοιχείων της λίστας είναι απλά μία από τις πολλές που θα μπορούσαν Στη συγκεκριμένη περίπτωση, όλα τα
να χρησιμοποιηθούν. στοιχεία της λίστας θα είναι αλφαριθ-
μητικά και κάθε ένα από αυτά θα έχει
Η αναπαράσταση αυτή δεν είναι η μοναδική, υπάρχουν πολλές εναλ-
τιμή είτε "X", είτε "O", είτε " " (κενό).
λακτικές. Όμως δεν είναι εύκολο να γνωρίζουμε εκ των προτέρων
ποια από τις πιθανές αναπαραστάσεις θ’ αποδειχθεί βολικότερη.
Ουσιαστικά, η επιλογή της κατάλληλης αναπαράστασης εξαρτάται
από τον συγκεκριμένο τρόπο που το πρόγραμμά μας θα επεξεργά-
ζεται τα δεδομένα. Εφόσον ακόμα δεν έχουμε γράψει το πρόγραμμα,
μόνο η εμπειρία και η διαίσθηση μπορεί να μας καθοδηγήσει.
Κάτι Να “Μεταφράζει”
Εντάξει, επιλέξαμε την αναπαράσταση. Αλλά, όπως και να προχωρή-
σουμε, ο χρήστης θα πρέπει να βλέπει μια τρίλιζα για να παίζει, όχι εννέα
τετράγωνα στη σειρά.
ΤΡΙΛΙΖΑ 3
47 elif board[0] == board[3] == board[6] == player: Μέχρι στιγμής έχουμε δει πως οι συ-
γκριτικοί τελεστές χρησιμοποιούνται
48 inarow = True
για να συγκρίνουν δύο τιμές μεταξύ
49 elif board[1] == board[4] == board[7] == player: τους. Στην Python μπορούν να χρη-
50 inarow = True σιμοποιηθούν για να συγκριθούν πολ-
51 elif board[2] == board[5] == board[8] == player: λές τιμές μεταξύ τους, όπως σε αυτό το
52 inarow = True παράδειγμα με τον τελεστή ==. Αυτό
είναι ένα χαρακτηριστικό που δεν το
53 elif board[0] == board[4] == board[8] == player: συναντάμε συχνά σε άλλες γλώσσες
54 inarow = True προγραμματισμού, με τους αντίστοι-
55 elif board[2] == board[4] == board[6] == player: χους συγκριτικούς τελεστές.
56 inarow = True
Κάθε μία από τις οκτώ περιπτώσεις σε αυτή τη δομή επιλογής αντι-
στοιχεί σε έναν από τους οκτώ διαφορετικούς τρόπους να γίνει τρί-
λιζα. Και στις οκτώ περιπτώσεις εκτελείται η ίδια εντολή: η μετα-
βλητή inarow παίρνει την τιμή True, για να καταγραφεί πλέον από
το πρόγραμμα ότι έχει γίνει τρίλιζα (εναλλακτικά, θα μπορούσε να
χρησιμοποιηθεί μια σύζευξη των οκτώ επιμέρους συνθηκών). Παρα-
τηρήστε ότι στη δομή επιλογής δεν υπάρχει else επειδή δε χρειάζε-
ται να εκτελεστεί κάποια εντολή σε περίπτωση που δε γίνει τρίλιζα.
Ανακοίνωση αποτελέσματος
Συμμάζεμα
Ξέρω ότι μπορώ να “σπάσω” το πρόγραμμά μου σε μικρότερα τμήματα,
υλοποιώντας τις επιμέρους λειτουργίες του προγράμματος ως ξεχωριστά
υποπρογράμματα. Από που να ξεκινήσω;
Είναι σημαντικό να διακρίνουμε (σε οποιοδήποτε πρόγραμμα) τις
ομάδες εντολών που λειτουργούν ως ενιαίο σύνολο και υλοποιούν
μια συγκεκριμένη λειτουργία. Αυτά τα τμήματα του προγράμματος
θα αποτελέσουν τη βάση για την κατασκευή των επιμέρους υποπρο-
γραμμάτων.
Ο τρόπος με τον οποίο μπορούμε να διαιρέσουμε ένα ενιαίο πρό-
γραμμα σε επιμέρους τμήματα με βάση τη λειτουργία τους δεν εί-
ναι μοναδικός. Στην πραγματικότητα υπάρχουν πολλές εναλλακτι-
κές. Επίσης, οι εμπειρότεροι προγραμματιστές συνήθως σχεδιάζουν
εκ των προτέρων τα προγράμματά τους και τα υποπρογράμματα από
τα οποία αποτελούνται, χωρίς αυτό να σημαίνει ότι στη συνέχεια,
καθώς υλοποιούν το πρόγραμμά τους, δεν μπορούν να αναθεωρή-
σουν ή να εκλεπτύνουν τον αρχικό τους σχεδιασμό.
Αλληλεπίδραση με το χρήστη
Εναλλαγή παίκτη
Τέλος, οι εντολές που φροντίζουν για την εναλλαγή του παίκτη στο
τέλος κάθε κύκλου της επανάληψης αποτελούν επίσης ένα ενιαίο
σύνολο εντολών.
# εναλλαγή παίκτη
if player == "X":
player = "O"
else:
player = "X"
Αυτή η ομάδα εντολών θ’ αποτελέσει τη βάση για τη συνάρτηση
next, η οποία δέχεται σαν παράμετρο έναν παίκτη player και επι-
στρέφει τον παίκτη που έχει σειρά να παίξει μετά από αυτόν.
66 def next(player):
67 """ Επιστρέφει το σύμβολο του παίκτη που παίζει
68 μετά τον παίκτη με σύμβολο player.
69 player: σύμβολο παίκτη ("Χ" ή "Ο")
70 """
71 if player == "X":
72 return "O"
73 else:
74 return "X"
Το κύριο πρόγραμμα
σε έναν κύκλο από τον οποίο βγαίνει μόνο όταν πληκτρολογήσει μια
έγκυρη τιμή. Αν ο παίκτης επιλέξει θέση που δεν είναι ανάμεσα στο
0 και το 8 ή επιλέξει κατειλημμένη θέση τότε εμφανίζεται μήνυμα
λάθους και η ανάγνωση τιμής επαναλαμβάνεται.
20 # εμφάνιση πίνακα τρίλιζας και πιθανών κινήσεων
21 print3x3(board)
22 print3x3(range(9))
23 # επανάληψη: όσο το τετράγωνο δεν είναι έγκυρο
24 while True:
25 # επιλογή θέσης από τον παίκτη
26 print(player, "διάλεξε τετράγωνο:", end=" ")
27 position = int(input())
28 # έλεγχος εγκυρότητας
29 if position < 0 or position > 8:
30 print("Επίλεξε τιμή μεταξύ 0 και 8.")
31 elif board[position] != " ":
32 print("To", position, "δεν είναι κενό.")
33 else:
34 # έγκυρη τιμή, τέλος επανάληψης
35 break
36 # επιστροφή επιλεγμένης θέσης
37 return position
oxo/src/oxo.3.py
Οι διαθέσιμες θέσεις
Τυχαιότητα με Μέτρο
Το Έξυπνο Χαρτί
Τίποτα καλύτερο δε γίνεται; Εγώ ξέρω ότι οι υπολογιστές είναι αχτύπητοι
σε τέτοιου είδους παιχνίδια.
Συνήθως τα προγράμματα που παίζουν τέτοια παιχνίδια χρειάζε-
ται να κάνουν αναζήτηση για να παίξουν έξυπνα. Αυτό σημαίνει ότι
δοκιμάζουν πολλούς διαφορετικούς συνδυασμούς κινήσεων για να
καταλήξουν στην επόμενη κίνηση που θα επιλέξουν. Όμως η τρίλιζα
είναι πολύ απλό παιχνίδι και δε χρειάζεται αναζήτηση. Αρκούν ορι-
σμένες απλές οδηγίες για να παίξει κανείς ικανοποιητικά.
ΤΡΙΛΙΖΑ 18
Εμείς θα δανειστούμε τις οδηγίες που περιγράφονται σε μια διασκε- Έξυπνο χαρτί: pythonies.mysch.gr/
δαστική δραστηριότητα που ονομάζεται Το Έξυπνο Χαρτί. Και πάλι, ipaper.pdf και οι επεκτάσεις του:
ipaper-ext.pdf
θεωρούμε ότι το πρόγραμμά μας θα παίξει πρώτο, χρησιμοποιώντας
το σύμβολο Χ.
Τροποποιήσεις – Επεκτάσεις
5.1 Η συνάρτηση readPosition ξεκινά εμφανίζοντας στον παίκτη την τρέ-
χουσα κατάσταση του πίνακα του παιχνιδιού:
# εμφάνιση πίνακα τρίλιζας και πιθανών κινήσεων
print3x3(board)
print3x3(range(9))
Αντικαταστήστε την τελευταία από τις εντολές, όπως φαίνεται στον κώ-
δικα που ακολουθεί:
# εμφάνιση πίνακα τρίλιζας και πιθανών κινήσεων
print3x3(board)
p = [position if board[position] == " " else "."
for position in range(9)]
print3x3(p)
Εκτελέστε το πρόγραμμα και παρατηρήστε τι συμβαίνει. Τί ακριβώς πε-
ριέχει η λίστα p;
oxo/exercises/oxo.8.py
5.4 Όταν φτάσαμε στο σημείο όπου το πρόγραμμά μας έπρεπε να αναλάβει το
ρόλο ενός εκ των δύο παικτών, ήταν απαραίτητο να διαθέτουμε έναν μη-
χανισμό ο οποίος θα επέτρεπε στο πρόγραμμα να ελέγχει αν ένας παίκτης
ΤΡΙΛΙΖΑ 21
5.5 Μια αρίθμηση των τετραγώνων της τρίλιζας που πιθανώς να βόλευε
περισσότερο τους παίκτες είναι αυτή που φαίνεται στο σχήμα 5.7. Αυτή 7 8 9
η αρίθμηση αντιστοιχεί στη διάταξη που έχουν τα αριθμητικά πλήκτρα
σε ένα τηλέφωνο ή στο πληκτρολόγιο. 4 5 6
Τροποποιήστε το πρόγραμμα που αναπτύχθηκε έτσι ώστε ο παίκτης να
επιλέγει το τετράγωνο στο οποίο θα παίζει με βάση αυτή την αρίθμηση. 1 2 3
Μια πιθανή προσέγγιση είναι να τροποποιηθεί και η εσωτερική αναπαρά-
σταση, ώστε να υπάρχει μια φυσικότερη αντιστοιχία με την αρίθμηση που Σχήμα 5.7: Μια διαφορετική αρίθ-
αντιλαμβάνεται ο παίκτης. μηση των τετραγώνων της τρίλιζας,
με βάση την οποία επιλέγει ο παί-
oxo/exercises/oxo-dial-representation.py
κτης το τετράγωνο στο οποίο θα παί-
Εναλλακτικά, μπορεί η εσωτερική αναπαράσταση να παραμείνει ως έχει ξει. Αυτή η αρίθμηση αντιστοιχεί στο
και να προστεθεί στον κώδικα ένα επίπεδο “αντιστοίχισης” ανάμεσα στην κλασικό αριθμητικό πληκτρολόγιο.
εσωτερική αναπαράσταση και την αρίθμηση από την σκοπιά του παίκτη.
oxo/exercises/oxo-dial-mapping.py
5.6 Το έξυπνο χαρτί παρέχει οδηγίες μόνο για λογαριασμό του παίκτη που
παίζει πρώτος. Προσπαθήστε να γράψετε αντίστοιχες οδηγίες για τον
παίκτη που παίζει δεύτερος και να τις υλοποιήσετε σε μια συνάρτηση
αντίστοιχη της paperPosition.
Αν προτιμάτε να βρείτε έτοιμες τις οδηγίες για τον δεύτερο παίκτη, ώστε
να εστιάσετε μόνο στην υλοποίησή τους, μπορείτε να ανατρέξετε στις επε-
κτάσεις του έξυπνου χαρτιού: pythonies.mysch.gr/ipaper-ext.pdf.
Για να χρησιμοποιήσετε τη συνάρτησή σας, θα χρειαστεί να κάνετε τις
απαραίτητες τροποποιήσεις ώστε το πρόγραμμα να μπορεί να αναλάβει
το ρόλο οποιουδήποτε από τους δύο παίκτες.
oxo/exercises/oxo-twoplayer.py
Pythonies - Τρίλιζα
pythonies.mysch.gr/chapters/oxo.pdf
Γιώργος Μπουκέας, Βασίλης Βασιλάκης (2015-2016) creativecommons.org/licenses/
by-sa/4.0/deed.el
Το παρόν υλικό διατίθεται με άδεια Creative Commons BY-SA 4.0.