Technical Architecture Manual — GeoWalk v3.0.0
1. Σκοπός και Αρχιτεκτονική Προσέγγιση
Το παρόν έγγραφο αναλύει την εσωτερική τεχνική αρχιτεκτονική της τελικής έκδοσης GeoWalk v3.0.0. Σε αυτή την έκδοση, ο πυρήνας της Μηχανής Σύντηξης (fusion_engine.dart) και το Gameplay Orchestration Layer (dashboard_screen.dart / welcome_screen.dart) έχουν αναβαθμιστεί ριζικά. Στόχος είναι η εξάλειψη του GPS drift, η αποτροπή του over-progression, η ενίσχυση της ασφάλειας (UI-driven safety limits), η επιθετική διαχείριση μνήμης (Hard Kill) και η αυτοματοποιημένη συλλογή ερευνητικών δεδομένων.
2. Sensor Fusion & Loose Sanity Check (fusion_engine.dart)
Το μεγαλύτερο πρόβλημα των outdoor location-based εφαρμογών είναι τα “ψεύτικα” άλματα του GPS (drift) όταν ο χρήστης παραμένει ακίνητος. Το GeoWalk λύνει αυτό το πρόβλημα μέσω του Loose Sanity Check.
2.1 Ο Μηχανισμός του Κουμπαρά (_gpsAccumulator)
Το GPS δεν προσθέτει πλέον μέτρα απευθείας στη μεταβλητή της κεντρικής προόδου (fusedDistance). Όταν η συσκευή είναι θεωρητικά ακίνητη, τα GPS increments (συνήθως “θόρυβος” του αισθητήρα) προστίθενται προσωρινά σε έναν accumulator (_gpsAccumulator).
2.2 Ο Έλεγχος Βημάτων (Το όριο του 30%)
Όταν τα σωρευμένα μέτρα στον _gpsAccumulator φτάσουν το όριο ανοχής (Tolerance Threshold), η Μηχανή απαιτεί επιβεβαίωση από τον Hardware Βηματομετρητή (Pedometer).
- Υπολογίζει τα αναμενόμενα βήματα:
Expected Steps = _gpsAccumulator / _baseProfileStride. - Ελέγχει αν τα πραγματικά βήματα (pending steps) ξεπερνούν το 30% των Expected Steps.
- Αν ΝΑΙ, το drift θεωρείται πραγματική κίνηση, το
_gpsAccumulatorπροστίθεται στοfusedDistanceκαι μηδενίζεται. - Αν ΟΧΙ, το σύστημα αντιλαμβάνεται ότι ο χρήστης είναι ακίνητος και το GPS “χορεύει”. Το
_gpsAccumulatorμηδενίζεται χωρίς να πιστώσει μέτρα, διαγράφοντας το drift!
- Αν ΝΑΙ, το drift θεωρείται πραγματική κίνηση, το
2.3 Η Αποφυγή Κυκλικής Αναφοράς (Circular Dependency)
Γιατί χρησιμοποιείται το σταθερό _baseProfileStride (0.60, 0.65, 0.75) και όχι το δυναμικό currentStride; Αν χρησιμοποιούσαμε το currentStride, το οποίο προσαρμόζεται από τα δεδομένα του GPS, το GPS θα αξιολογούσε έμμεσα τον εαυτό του. Χρησιμοποιώντας το _baseProfileStride (τη hardcoded ανατομική σταθερά), κρατάμε τον Sanity Validator απολύτως ανεξάρτητο από τη στιγμιαία παραμόρφωση του GPS.
3. Distance Snapping & Progression Locking (dashboard_screen.dart)
Ένα συχνό φαινόμενο ήταν οι παίκτες να ξεπερνούν την απόσταση ενός σταθμού λόγω ενός απότομου άλματος του GPS (Overshoot). Η λύση υλοποιήθηκε μέσω του Distance Snapping με χρήση της μεταβλητής _pausedOffset.
- Όταν η νέα υπολογισμένη απόσταση (
newDist) ξεπεράσει την απόσταση του επόμενου σταθμού (nextStation.distanceRequired), ο αλγόριθμος “παρεμβαίνει” και καρφώνει (snaps) τοnewDistακριβώς πάνω στον σταθμό. - Το “περίσσευμα” του GPS δεν αγνοείται σιωπηλά, αλλά εξισορροπείται μαθηματικά:
_pausedOffset = currentEngineDistance - exactEffectiveNeeded. - Το πλεόνασμα αποθηκεύεται στο offset, αφαιρώντας διαρκώς το over-estimation από τα επόμενα ticks. Ο χρήστης αναγκάζεται να περπατήσει ξανά την πραγματική απόσταση για τον επόμενο σταθμό.
4. Power Management & Idle Policies (Zombie App Prevention)
Στην έκδοση 3.0.0, η πολιτική αδράνειας γίνεται διβαθμιδική για μέγιστη προστασία της μπαταρίας:
- Soft Pause (στα 3 λεπτά): Στο
fusion_engine.dart, η μεταβλητή_idleTimeoutMinutesέχει οριστεί στα 3 λεπτά. Αν περάσουν χωρίς έγκυρο step/GPS event, ο κινητήρας μπαίνει δυναμικά σεTrackerState.stillκαι εκτελεί pause του background tracking. - Hard Kill (στα 15 λεπτά): Μια νέα ρουτίνα Timers (
_manageShutdownTimer) στοdashboard_screen.dartαναλαμβάνει την οριστική εκκαθάριση. Όταν το παιχνίδι μπει σε παύση, ξεκινά ένας χρονοδιακόπτης. 10 δευτερόλεπτα πριν τη λήξη ακούγεται συναγερμός (beep05.mp3). Στα 15 λεπτά ακριβώς, καλείται η εντολήSystemNavigator.pop(), η οποία τερματίζει βίαια την εφαρμογή σε επίπεδο OS, απελευθερώνοντας τη RAM. Όλα τα states είναι ήδη persisted.
5. UI Orchestration, Pacing & Gating
Η έκδοση 3.0.0 εισάγει εξελιγμένα patterns αλληλεπίδρασης και ελέγχου:
- Pre-Test Gating (
welcome_screen.dart): Η Έναρξη Αποστολής (αν το Save State είναι άδειο) μπλοκάρεται από ένα έξυπνο Pop-up. Αυτό αναγκάζει τον χρήστη είτε να εκτελέσει το Ερωτηματολόγιο Εκκίνησης (ανοίγοντας το URL με injected arguments) είτε να επιβεβαιώσει ρητά την ολοκλήρωσή του. - Σταδιακή Αποκάλυψη Σταθμών (2-Page Flow): Στο
station_quiz_dialog.dart, για να αποφευχθεί το Cognitive Overload, το UI ξεκινάει δείχνοντας μόνο το Lore του σταθμού σε High Contrast φόντο. Μόνο αφού ο χρήστης πατήσει «ΣΥΝΕΧΕΙΑ» (scroll-to-top event), αποκαλύπτεται ο γρίφος. - Dynamic Radar & Jump Scares: Στο
next_station_panel.dart, το glow και ο ήχος του ραντάρ δεν ελέγχονται πια από μια hardcoded τιμή (20μ), αλλά διαβάζουν το νέο πεδίοradarRangeτουStationmodel. Αυτό επιτρέπει τη δημιουργία Jump Scares στο τέλος (Σταθμοί 19 & 20) όπου τοradarRangeείναι 0. - Cinematic Intro & Safety Messages: Το Station 0 (
CinematicIntroDialog) μπλοκάρει μεPopScope(canPop: false)την έξοδο, ενώ κάθε Σταθμός έχει μόνιμο πορτοκαλί Warning Banner («Στάσου σε ασφαλές σημείο…») όσο είναι σε Unresolved state.
6. Research Data & Offline Fallback (Inbox System)
Για την υποστήριξη της ακαδημαϊκής έρευνας χωρίς να απαιτείται Login System:
Offline Inbox System (settings_screen.dart): Στο τέλος της αποστολής, τα δεδομένα (medals, loots, timestamps) γίνονται URL-encoded. Αν η συσκευή είναι offline, το URI string αποθηκεύεται στη λίστα prefPendingEvalUrlsList (Local Inbox) και μπορεί να ανακληθεί από τον χρήστη μέσω των Ρυθμίσεων όταν αποκατασταθεί η σύνδεση.λεία υπαίθρου που είναι διαθέσιμα για τα σημερινά σχολεία.
Persistent Student ID: Το WelcomeScreen παράγει ένα τυχαίο 5ψήφιο ID (10000-99999) κατά το first launch, το οποίο αποθηκεύεται μόνιμα και αποτελεί το key του μαθητή στα analytics.
Dynamic Route ID: Παράγεται ένα νέο Session ID (π.χ. R-45091) σε κάθε “Νέα Αποστολή”.
