Προς το περιεχόμενο

Προτεινόμενες αναρτήσεις

Δημοσ.

Μια bool δίνει τιμή true ή false. Υπό αυτή την έννοια,μια if μοιάζει κάπως με ένα node ενός binary tree, μιας και ο κάθε έλεγχος δίνει 1 από 2 πιθανές κατευθύνσεις.

 

Έστω ότι έχεις:

            if (bool1)
            {
                if (bool2)
                {
                    if (bool3)
                        DoSomething();
                    else
                        DoSomethingElse();
                }
                else
                {
                    if (bool4)
                        Foo():
                    else
                        //Θέλω να πάω στον έλεγχο της bool3 χωρίς να περάσω απ' τον έλεγχο των bool1, bool2.
                }
            }
            else
            {
                if (bool5)
                    Bar();
                else
                    Snafu();
            }

Κάποια στιγμή όταν η bool4 είναι false, θες να πας απ' ευθείας στην εξέταση του αν η bool3 είναι true, χωρίς να περάσεις ξανά απ' τον έλεγχο της bool1 και bool2. Άρα ούτε η return ούτε η break σε σώζει. Τι θα κάνεις λοιπόν ? Κατά 90% κατέληξα στην απόφαση να χρησιμοποιήσω goto.

 

Γιατί να πας απ' ευθείας στον έλεγχο της bool3 χωρίς να περάσεις πάλι απ' το root ?

Για διάφορους λόγους:

 

1. Γιατί ίσως άμα τερματίσει η ροή του δέντρου και περιμένεις να έρθει το επόμενο frame για να τρέξει ξανά η μέθοδος και ο έλεγχος απ' το root, ο NPC για το frame που πέρασε δεν θα έχει κάνει τίποτα αντί να κάνει κάτι που θέλουμε. 

2. Γιατί γλιτώνουμε περιττούς ελέγχους, και άρα κύκλους CPU. Άμα το κάθε AI agent καταναλώνει λιγότερους κύκλους, μπορούμε να βάλουμε περισσότερα AI ταυτόχρονα στην οθόνη.

3. Γιατί το να κάνουμε copy + paste όλους τους ελέγχους που περιλαμβάνονται στο "κλαδί" που θέλουμε, και να δημιουργούμε διπλότυπα κλαδιά βρωμίζοντας το κώδικα μόνο και μόνο για να μη χρησιμοποιήσουμε goto, δε νομίζω να αξίζει.

 

Υ.Γ. Το πρωτότυπο αρχείο ? Το xml του σχεδιαγράμματος εννοείς ?

Δημοσ.

To διάγραμμα που έχεις κάνει attach εννοώ.

Κοίτα καταλαβαίνω τι θέλεις να κάνεις αλλά δεν είναι αρχιτεκτονικά σωστό.

Δεν θα έπρεπε ενώ έχει περάσει απο μία if else να θέλεις ξαφνικά να γυρίσεις πάλι πίσω.

Δεν μπορείς να υλοποιήσεις μέσα στο αντικείμενο σου να κάνει κάτι διαφορετικό ανάλογα με το state?

 

π.χ. τελείως μπακαλίστικα και χωρίς να ξέρω c# , να έχει το αντικείμενο σου ένα Dictionary και ανάλογα με το state να καλεί μία διαφορετική μέθοδο?

Είναι ξεκάθαρο, reusable, μειώνει το complexity κτλ.

 

Πάω για φαί και Halt and Catch Fire η συνέχεια αύριο :D

  • Like 1
Δημοσ.

To διάγραμμα που έχεις κάνει attach εννοώ.

Κοίτα καταλαβαίνω τι θέλεις να κάνεις αλλά δεν είναι αρχιτεκτονικά σωστό.

Δεν θα έπρεπε ενώ έχει περάσει απο μία if else να θέλεις ξαφνικά να γυρίσεις πάλι πίσω.

Δεν μπορείς να υλοποιήσεις μέσα στο αντικείμενο σου να κάνει κάτι διαφορετικό ανάλογα με το state?

Α θα πρέπει να ανοίξω ξανά το designer και να κάνω export σε διαφορετικό μέγεθος zoom.

 

Αυτό θέλω να κάνει. Να φύγει απ' αυτό το κλαδί και να πηδήσει στο άλλο. Εκτός και αν δημιουργήσουμε διπλότυπο του άλλου κλαδιού σε αυτό, αλλά είπαμε και πως τα διπλότυπα είναι κακά. Αλλά τα επόμενα λογικά βήματα που πρέπει να ακολουθηθούν σε αυτή τη περίσταση, αυτά είναι. Θα ήταν προτιμότερο λες να βάλω το 'κλαδί' που χρειάζεται να χρησιμοποιηθεί σε πάνω από μια περιστάσεις σε διαφορετική μέθοδο και να καλείται η νέα μέθοδος, αντί να γίνεται μετάβαση με goto ?

Δημοσ.

Α θα πρέπει να ανοίξω ξανά το designer και να κάνω export σε διαφορετικό μέγεθος zoom.

 

Αυτό θέλω να κάνει. Να φύγει απ' αυτό το κλαδί και να πηδήσει στο άλλο. Εκτός και αν δημιουργήσουμε διπλότυπο του άλλου κλαδιού σε αυτό, αλλά είπαμε και πως τα διπλότυπα είναι κακά. Αλλά τα επόμενα λογικά βήματα που πρέπει να ακολουθηθούν σε αυτή τη περίσταση, αυτά είναι. Θα ήταν προτιμότερο λες να βάλω το 'κλαδί' που χρειάζεται να χρησιμοποιηθεί σε πάνω από μια περιστάσεις σε διαφορετική μέθοδο και να καλείται η νέα μέθοδος, αντί να γίνεται μετάβαση με goto ?

 

Ναι , θεωρητικά το κλαδί κάνει μόνο τα δικά του. Αν απομονώσεις τον κώδικα μπορείς να τον καλέσεις απο οπουδήποτε.

Αν καταλήξεις όμως εκεί μήπως μιλάμε τσάμπα για δέντρα και κλαδιά? :D

Πατάμε στο δικό σου design οπότε θεωρούμε ότι είναι σωστό ή σε βολεύει για κάποιο λόγο.

Αν δεν βαριέσαι ή δεν έχεις θέμα να μοιραστείς λίγο κώδικα να καταλάβουμε καλύτερα (έστω και ένα μικρό block κώδικα) , ευχαρίστως να ρίξω ένα βλέφαρο (είπα δεν ξέρω c# αλλά μοιάζει αρκετά με java)

Α θα πρέπει να ανοίξω ξανά το designer και να κάνω export σε διαφορετικό μέγεθος zoom.

 

Με τι πρόγραμμα το έφτιαξες?

Δημοσ.

Μια bool δίνει τιμή true ή false. Υπό αυτή την έννοια,μια if μοιάζει κάπως με ένα node ενός binary tree, μιας και ο κάθε έλεγχος δίνει 1 από 2 πιθανές κατευθύνσεις.

 

Έστω ότι έχεις:

            if (bool1)
            {
                if not (bool2)
                {
                   if (bool4)
                      Foo();
                }
                else
                {
                    if (bool3)
                       DoSomething();
                    else
                        DoSomethingElse();
                 }
            }
            else
            {
                if (bool5)
                    Bar();
                else
                    Snafu();
            } 

Κάποια στιγμή όταν η bool4 είναι false, θες να πας απ' ευθείας στην εξέταση του αν η bool3 είναι true, χωρίς να περάσεις ξανά απ' τον έλεγχο της bool1 και bool2. Άρα ούτε η return ούτε η break σε σώζει. Τι θα κάνεις λοιπόν ? Κατά 90% κατέληξα στην απόφαση να χρησιμοποιήσω goto.

 

 

Δοκίμασε λίγο το παραπάνω το έχω αλλάξει λίγο. Οπς λάθος μην το δοκιμάζεις

Δημοσ.

Δοκίμασε λίγο το παραπάνω το έχω αλλάξει λίγο. Οπς λάθος μην το δοκιμάζεις

 

Στην αρχιτεκτονική πρέπει να τον βοηθήσουμε , και να φτιάξει το συγκεκριμένο block κώδικα φαντάζομαι έχει αρκετά. Επίσης δεν του λύνει και το πρόβλημα όταν θέλει να προσθέσει κάτι νέο, αποκλείεται να βγάλει άκρη και να βρεί μέσα σε ποια if-else να το χώσει .

Δημοσ.

Βασικά δεν έχω ιδέα για αυτά τα πράγματα.

Το μόνο 'AI' που έχω φτιάξει είναι τρίλιζα  και αυτή μόνο ο ένας παίκτης ήταν το computer ο άλλος ήταν real.

Τότε θυμάμαι είχα διαβάσει λίγο για έναν αλγόριθμο https://en.wikipedia.org/wiki/Minimax δεν ξέρω αν κολλάει όμως σε αυτό που φτιάχνει ο Alithinos.

 

Σε αυτό που λέει για το goto παντώς νομίζω ότι μπορεί να το αποφύγει και θα βελτιωθεί και το μακρύ if

Το κομμάτι που θέλει να εκτελέσει σε διαφορετικές περιπτώσεις και να πάει με goto πρέπει να το απομονώσει και να κάνει έναν έλεγχο με ors που οδηγούν σε αυτό

 

στη συγκεκριμένη περίπτωση εκεί τον πάει το bool2 ή το not bool4

if (bool2) or not bool4
                {
                    if (bool3)
                        DoSomething();
                    else
                        DoSomethingElse();
                }
 else if bool4 
     {
              Foo():
      }

και νομίζω είναι καλύτερο

  • Like 1
Δημοσ.

Πήρα κάποιες αποφάσεις.

 

Λοιπόν:

 

#1 Από εκεί που θα υπήρχε ένα enum το οποίο θα το έθετα manually κατά την αρχικοποιήση, για κάθε state που θα βρισκόταν το AI, οι περισσότεροι έλεγχοι που εξαρτιόντουσαν απ' τα enum, πλέον εξαρτιούνται από συγκρίσεις τιμών με το Character Sheet, που ούτως ή άλλως το κάθε AI agent θα έχει από ένα.

(Όσοι έχουν παίξει DnD ξέρουν τι είναι το Character Sheet, έχει πληροφορίες όπως strength, agility, intelligence κτλπ), και θα υπάρχει 1 χειροκίνητη enum, για το courage του.

πχ (Foolhardy, Brave, Normal, Cautious, Coward). Με αυτή τη κίνηση καθαρίζει αρκετά ο χώρος στο source file, και ελαχιστοποιούνται τα βήματα που θα πρέπει να κάνω χειροκίνητα, μιας και ούτως ή άλλως είχα φροντίσει από πριν για τα character sheets.

 

#2 Όσων αφορά το πως θα μετατρέψω το σχεδιάγραμμα σε κώδικα... Θα χρησιμοποιήσω if/else, αλλά αντί να αποκτώ πρόσβαση σε συχνά χρησιμοποιούμενο κώδικα με... goto και άλλες αλχημείες, απλά θα τα βάλω σε μεθόδους και θα καλώ μεθόδους. Χθες το βράδυ όταν έγραφα πρέπει να είχα κουραστεί πολύ που δεν το έβλεπα. Με σπάσιμο σε μεθόδους γίνονται όλα τόσο πιο ευανάγνωστα. Να για παράδειγμα η κύρια μέθοδος που καλείται σε κάθε frame θα είναι κάπως έτσι:

private void Update()
{
	if(health == low)
	    LowOnHealth();
	else if(isOnFire)
	    IsOnFire();
        else if(nearHazard)
            behaviours.AvoidHazard();
        else if(target != null)
        {
                 if(target == enemy)
                     InteractWithEnemy();
                 else if(target == friend)
                     InteractWithFriend();
                 else 
                     InteractWithNeutral();
        }
	else
		DefaultBehavior();
}

Αρκετά αναγνώσιμη if you ask me.

Με τη σειρά τους οι LowOnHealth,InteractWithEnemy, κτλπ έχουν περισσότερους ελέγχους.

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

 

Υ.Γ. Το σχεδιάγραμμα με βοήθησε πολύ.

 

Ναι , θεωρητικά το κλαδί κάνει μόνο τα δικά του. Αν απομονώσεις τον κώδικα μπορείς να τον καλέσεις απο οπουδήποτε.

Αν καταλήξεις όμως εκεί μήπως μιλάμε τσάμπα για δέντρα και κλαδιά? :D

Πατάμε στο δικό σου design οπότε θεωρούμε ότι είναι σωστό ή σε βολεύει για κάποιο λόγο.


Αν δεν βαριέσαι ή δεν έχεις θέμα να μοιραστείς λίγο κώδικα να καταλάβουμε καλύτερα (έστω και ένα μικρό block κώδικα) , ευχαρίστως να ρίξω ένα βλέφαρο (είπα δεν ξέρω c# αλλά μοιάζει αρκετά με java)


 

Με τι πρόγραμμα το έφτιαξες?

Απ' το παλιό κώδικα τον δύστροπο ? Θα πεταχτεί. Θα αρχίσω να φτιάχνω απ' την αρχή τη κλάση με το νέο σχέδιο. Είναι παράδειγμα προς αποφυγή. 

 

Αρκεί πιστεύω να σου πω πως απ' τις 14 public μεταβλητές, μόνο 3 θα μείνουν στο νέο σχέδιο, και τις 8 τις έθετα χειροκίνητα για κάθε διαφορετικό τύπο όντος. 

 

Το σχεδιάγραμμα το έφτιαξα με το draw.io.

 

 

Δοκίμασε λίγο το παραπάνω το έχω αλλάξει λίγο. Οπς λάθος μην το δοκιμάζεις

:lol:

 

Στην αρχιτεκτονική πρέπει να τον βοηθήσουμε , και να φτιάξει το συγκεκριμένο block κώδικα φαντάζομαι έχει αρκετά. Επίσης δεν του λύνει και το πρόβλημα όταν θέλει να προσθέσει κάτι νέο, αποκλείεται να βγάλει άκρη και να βρεί μέσα σε ποια if-else να το χώσει .

Έτσι όπως τα έκανα τώρα καλύτερα.

Δημοσ.

Θα μπορούσες να κάνεις κάτι τέτοιο : (Ξαναλέω δεν ξέρω c#)

Dictionary<string,Action> methods = new Dictionary<string,Action>
methods.add("lowHp",LowOnHealth());
methods.add("isOnFire",IsOnFire());
methods.add("nearHazard",behaviours.AvoidHazard());
methods.add("nullTargetEnemy",InteractWithEnemy());
methods.add("nullTargetFriend",InteractWithFriend());
methods.add("nullTargetNeutral",InteractWithNeutral());
methods.add("default",DefaultBehavior());


Οπότε το μόνο που έχεις να κάνεις είναι invoke την μέθοδο αν υποθέσουμε ότι το key του dictionary είναι το State του παιχνιδιού.

Δημοσ.

Θα μπορούσα να χρησιμοποιήσω κάπου Dictionary, αλλά μάλλον όχι για τις μεθόδους ελέγχου, αλλά για τις μεθόδους πράξεων που καλούν οι μέθοδοι ελέγχου. Διότι οι μέθοδοι πράξεων που θα καλούν οι μέθοδοι ελέγχων δεν θα είναι πάντα οι ίδιες, αλλά η υλοποίησή τους θα διαφέρει ανάλογα το CharacterSheet component του Creature.

 

Το να χρησιμοποιούσα μια δομή δεδομένων (dictionary) για δεδομένα τα οποία πάντα θα έχουν την ίδια τιμή, το βρίσκω λίγο λάθος, όταν αυτά τα δεδομένα μπορούν να υπάρχουν από μόνα τους μέσα στο αντικείμενο που θα χρησιμοποιεί το dictionary. Αλλά εφόσων οι μέθοδοι πράξεων θα έχουν διαφορετικές υλοποιήσεις ανάλογα το Creature, και έχω ξεχωρίσει το component που περιγράφει το Creature (CharacterSheet) το οποίο θα διαθέτει μοναδικές τιμές για κάθε Creature, απ' το component που περιέχει τους έλεγχους που θα ισχύουν για όλα τα creatures, μπορώ να δω τη χρησιμότητα το κάθε CharacterSheet να έχει τις αποκλειστικές μεθόδους συμπεριφορών του Creature type που αντιπροσωπεύει οργανωμένες σε ένα Dictionary, το οποίο το παίρνει το component ελέγχου για να τις καλέσει αν και όταν η ροή ελέγχων φτάσει στο κατάλληλο σημείο.

Δημοσ.

 

Το να χρησιμοποιούσα μια δομή δεδομένων (dictionary) για δεδομένα τα οποία πάντα θα έχουν την ίδια τιμή, το βρίσκω λίγο λάθος, όταν αυτά τα δεδομένα μπορούν να υπάρχουν από μόνα τους μέσα στο αντικείμενο που θα χρησιμοποιεί το dictionary. .

 

Γιατί το θεωρείς λάθος? Είναι καλύτερα να έχεις 10 nested if ?

Δημοσ.

Γιατί το θεωρείς λάθος? Είναι καλύτερα να έχεις 10 nested if ?

Επειδή ούτως ή άλλως ζουν ήδη μέσα στο αντικείμενο το οποίο είναι το μοναδικό που θα τα χρησιμοποιήσει.

 

Ένα αντικείμενο έχει κάποια δεδομένα (μεταβλητές), και κώδικα που χρησιμοποιεί αυτά τα δεδομένα (συναρτήσεις).

Για πιο λόγο να βάλω τις συναρτήσεις ενός αντικειμένου σε μια δομή δεδομένων, αν οι συναρτήσεις αυτές είναι όλες private, για αποκλειστική χρήση του ίδιου του αντικειμένου ? Τι κερδίζω με το να το κάνω αυτό ?

 

Οι if/else που έχει η κάθε συνάρτηση, δεν μπορούν να εξαφανιστούν τελείως, γιατί είναι μέρος του αλγόριθμου που εκτελεί η συνάρτηση. Ακόμα και αν βάλω αναφορές συναρτήσεων σε μια δομή δεδομένων, κάπου θα πρέπει να υπάρχει ο ορισμός της κάθε συνάρτησης, και άρα κάπου θα υπάρχουν και τα nested ifs.

 

Έστω ότι φτιάχνω το Dictionary που περιέγραψες στο μήνυμα σου, και καλώ αυτές τις μεθόδους όποτε χρειάζονται απ' το Dictionary.

Τότε η Update() που παρέθεσα σε προηγούμενο μήνυμα θα γινόταν έτσι:

private void Update()
{
	if(health == low)
	    methods.lowHP.Invoke();
	else if(isOnFire)
	    methods.isOnFire.Invoke();
        else if(nearHazard)
            methods.nearHazard.Invoke();
        else if(target != null)
        {
                 if(target == enemy)
                     methods.nullTargetEnemy.Invoke();
                 else if(target == friend)
                     methods.nullTargetFriend.Invoke();
                 else 
                     methods.nullTargetNeutral.Invoke();
        }
	else
		methods.default.Invoke();
}

Τα if/else πάλι εκεί θα έμεναν, και απλά θα είχα γράψει περισσότερες γραμμές κώδικα για το ίδιο αποτέλεσμα.

Δημοσ.

Μπορείς αντί για if then να έχεις άμεσα μερικούς τύπους, και αυτοί να δίνουν αποτέλεσμα σε πίνακα, ο οποίος μαζί με το struct ή αντικείμενο (μόνο με τη κατάσταση) να πηγαίνει σε συνάρτηση όπου θα κατασκευάζει την "απόκριση" του αντικειμένου, που θα παίξει σε δεύτερο χρόνο. Δηλαδή σε πρώτο χρόνο παίζουν οι τύποι, μαζεύουν τα νούμερα, μπαίνουν για κάθε αντικείμενο σε μια συνάρτηση, (εκτός αντικειμένου) και στο τέλος θα γίνεται για όλα τα αντικείμενα η εκτέλεση βάσει της σωσμένης "απόκρισης". Μετά πάμε από την αρχή. Κάποια αντικείμενα ανήκουν σε παίκτες και κάποια σε AI. Εκεί λοιπόν έχουμε το δένδρο με τα IF το οποίο είναι "Αφοσιωμένο" για το αντικείμενο, άρα πιο συνεκτικό!

Δημοσ.

@Alithinos

 

Χρειάζεσαι abstraction layers. Το πιάνεις εντελώς λάθος. Δε γίνεται να τα υλοποιήσεις όλα αυτά μέσα σε κλάσεις μετρημένες στα δάχτυλα. Επίσης σε πρώτη φάση πρέπει να σχεδιάσεις το γενικό concept, που δεν έχει καμία σχέση με ψηφιακή σχεδίαση που παρέθεσες στο πρώτο ποστ.

 

Επίσης, αν το πρόβλημά σου ήταν το πως θα πας στον parent node του δέντρου από το child node, τι σε εμποδίζει κάθε κόμβος πέρα από τα references στα child nodes να έχει και reference στον parent node; Ωστόσο δε θα πας να υλοποιήσεις το δέντρο με ifs, επιτόπου. Δεν πρέπει την ώρα που χρησιμοποιείς το δέντρο να έχεις ιδέα για το πως αυτό υλοποιείται από πίσω και έχει άπειρες βιβλιοθήκες εκεί έξω με δέντρα. Βρες μία για να μην τεστάρεις τζάμπα, αν ο σχεδιασμός σου χρειάζεται δέντρα.

 

Ακόμη, δε γίνεται να έχεις την υλοποίηση για όλες τις αντιδράσεις σε μια κλάση. Αυτό το πράγμα δε δουλεύει και δεν μπορεί να γίνει maintain. Πάνω πάνω όταν θες να αλληλεπιδράσεις με το NPC, θα βλέπεις κάτι σε ένα action manager interface που απλά θα του λες react(). Επίσης ο action manager δε θα πρέπει να βλέπει κάτι περισσότερο από πχ. μια stack με actions που θα μιλάνε απλά το ίδιο interface. Κάθε action επίσης θα πρέπει να έχει και κάποιον controller που θα ελέγχει αν είναι δυνατόν να εκτελεστεί, ανεξάρτητα αν εσύ βάλεις "μια γάτα να γαβγίζει" και θα μπορεί να κάνει το coupling μεταξύ NPC και Action.

 

Κοινώς θες να φτάσεις σε ένα σχήμα που προκειμένου να προσθέσεις μια καινούργια συμπεριφορά να χρειάζεσαι μόνο:

  1. Implement a new Action.
  2. Implement a new ActionController.
  3. Register an instance of ActionController to an instance of Action.
  4. Add that action to NPC's action list.
  • Like 1

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...