Freitag, 4. Mai 2012

Kreta 2011

Dosensuche unter den Augen von Zeus

Cachen zwischen Pauschaltourismus, traumhaften Landschaften und Lost Places

Als bei der Planung des Familienurlaubs Kreta auf der Liste stand, genügte ein kurzer Blick auf die Cachekarte und die damit verbundenen potentiellen Dosen um den Flug zu buchen. Es waren zwar mit 334 Caches nicht allzu viele, aber die zugehörigen Bilder und Logeinträge versprachen tolle Locations. Und ich sollte nicht enttäuscht werden...

Abbildung 1: Morgens aufwachen und diesen Ausblick genießen, das ist Urlaub wie man sich ihn wünscht

Gut im Hotel angekommen, war die erste Dose mit der Länderkategorie Griechenland längst überfällig. Hatte ich doch am gestrigen Transfertag das Flughafen TB-Hotel schon links liegen lassen müssen. Warum musste auch der Bustransfer zum Hotel so reibungslos funktionieren. Aus dem Flugzeug direkt in den Bus, Türen zu und los ging's. Keine Verspätung, keine Wartezeit, an uns Cacher denkt mal wieder niemand. Aber das war gestern. Heute brennt es unter den Nägeln. Ein Hindernis gibt es aber noch: Tausend Meter Luftlinie am steilen Berg mit Serpentinenstraßen, kein Auto.
Also hilft nur eins. Laufschuhe und Joggingklamotten an und auf zum ersten Cache.

Abbildung 2: Die Bucht zum Cache 'Romanica Beach' wartet mit traumhafter Aussicht und hält was der Cachename verspricht
Da der Hinweg bergab führt, hält sich die Belastung in Grenzen, die Dose ist schnell gefunden und die Location ist grandios, GC15MJC „Romantica Beach“, der Name ist Programm. Wann sitzt man beim Loggen schon mal auf Felsklippen und sieht unter sich die Brandung des Mittelmeeres gegen dieselben rollen?
Der erste Cache in Griechenland, wieder ein Land mehr rot auf der Statistikkarte.
Da ich es zu Beginn mit der Sportlichkeit nicht übertreiben möchte liegt das Lauftempo des Rückweges meist im Fußgängerbereich, sodass von „Laufen“ eher selten gesprochen werden kann. Aber es geht ja auch steil den Berg hinauf.
Der nächste Tag. So gut und zahlreich das Essen im Hotel ist, so schlecht ist das Sportprogramm der Animation. Das ist, abgesehen von der Kinderdisco mit Töchterlein, nämlich komplett ausgefallen. Mal fehlt der Volleyball, mal ist das Tischtennisnetz kaputt. Wer konnte auch ahnen, dass im Sommer Gäste kommen?
Um der All-In-Völlerei zu entgehen, knipse ich das Oregon an. Nächster Cache: 1,9 Kilometer. Das klingt doch gut.
Ich laufe also wieder los. Unterwegs mache ich den Fehler und stelle die Navigation von „Luftlinie“ auf „Schnellste Strecke“ um. Aus den 1,9 Kilometer werden schlagartig 3,2. Dazu der nächste Schock: Der Weg führt über eine Schnellstraße ohne Bürgersteig oder Radweg, dafür voll gestopft mit Autos, deren griechische Fahrer ihrem Ruf der lockeren Auslegung von Tempolimits vollständig nachkommen.


Abbildung 3: Endlich oben um die Aussicht auf Malia Bay zu genießen
Aber ich habe Glück, parallel zur Schnellstraße gibt es eine kleine Nebenstraße, die hauptsächlich von Fußgängern, Rollern und Quads benutzt wird. Es stellt sich heraus, dass sie mich bis auf hundert Meter an meine zweite Dose GC1DKH2 „Malia Bay“ heranbringen wird. Diese liegt oberhalb der Bucht von Malia und bietet als Belohnung für die Kletterei eine herrliche Aussicht über den gesamten Küstenbereich. Malia ist eine ehemalige Palastanlage, die etwas 1900 v. Chr. errichtet worden ist. Heute sieht man leider aber fast nur noch die Produkte des Pauschaltourismus. Interessant am Listing ist noch, dass zum Schluss angegeben wird, bei Problemen möge man seine Maintenance Wünsche nicht an den Owner, sondern direkt an Team OMILOS senden, die diesen Cache freundlicherweise betreuen würden. Ein Hinweis, den ich nicht zum letzten Male sehen werden.

Abbildung 4: Ein Blick zurück zeigt nicht nur den Weg, den ich gerade gelaufen bin, sondern auch die Strecke, die es zurückzulaufen gilt.
Nach einer halbstündigen Log- und Fotopause (die Kilometer des Rückweges verhindern irgendwie, dass ich aufstehe und die Dose wieder verstecke) geht es zurück Richtung Hotel. Nach dem Abstieg melden meine Knie, dass sie es gar nicht lustig finden, nach Monaten der Ruhe jetzt diese Mörderstrecke auf sich zu nehmen. Aber die Aussicht auf eine kalte Dusche, Magnesium gegen Muskelkrämpfe und ein eisgekühltes Bierchen lassen die Schmerzen nach und nach verschwinden. Außerdem schließt das Restaurant in einer knappen Stunde, weswegen dieses Mal sogar der Killerberg zum Hotel hoch im Lauftempo genommen wird.

Nach ein paar Tagen mit mittlerweile zaghaft stattfindender Sportanimation ist es endlich soweit: Heute bekommen wir unseren Mietwagen. GPSr, Fotoapparat, TBs und Reservebatterien liegen seit Tagen bereit, sodass es nach einem deftigen Frühstück losgehen kann.
Als erstes steht ein Lost Place auf dem Programm. GC2QGRR „Nikithianos Windmills“, einer der größten Windmühlenkomplexe der Insel.


Abbildung 5: „Nikithianos Windmills“ ist einer der größten Windmühlenkomplex der Insel
Die ersten Kilometer auf griechischen Straßen bringen die Erkenntnis, dass das Wort „Standstreifen“ in der hiesigen Sprache wohl nicht existiert. Der rechts durch ein dicke durchgezogene Linie abgetrennte Bereich erhält seine Daseinsberechtigung dadurch, dass Autofahrer, die sich so einigermaßen an die vielen 70er, 50er oder 30er Schilder halten wollen, sich möglichst nah am Straßengraben aufhalten damit Nicht-Touristen in der Mitte bequem überholen können. Dabei ist dieses Verhalten nicht auf Autofahrer eingeschränkt, wie ich am letzten Urlaubstag feststellen durfte, als unser Busfahrer sich nicht durch die Tatsache stören ließ, dass er gerade 50 Touristen mit sich führte und munter ein Auto nach dem anderen überholte.
Aber zurück zum Cache. Dieser besitzt zwar einen eigenen Waypoint „Parking“, zwischen Erkennen der zwei Meter breiten Lücke in der Sträucherhecke am Straßenrand und dem „Ups, das war er wohl“ lagen gefühlte hundert Millisekunden. Der Begriff „Ausfahrt“ ist hier wohl ebenso ein Fremdwort. Als ich bei der nächsten Gelegenheit wende und versuche den Parkplatz durch ambitioniertes Linksabbiegen zu erreichen, werde ich direkt durch intensives Hupen darauf aufmerksam gemacht, dass der Mittelstreifen nur zum Überholen und nicht zum Abbiegen erfunden wurde.
Nach erfolgreichem Parkvorgang und 15 minütiger erfolgloser Suche an einer gut erhaltener Windmühle entdecke ich warum mein GPSr immer noch 30 Meter Entfernung anzeigt. Auf der anderen Straßenseite gibt es oben auf dem Berg noch halb eingefallene Ruinen älterer Modelle.

Abbildung 6: So schön der Ausblick auch ist, so anstrengend war der Aufstieg
Die Überquerung der gut und vor allem schnell befahrenen Nationalstraße erhöht sowohl die T-Wertung, als auch den Abenteuerfaktor, da der Highway, wie ihn die Einwohner liebevoll nennen, hier eine unübersichtliche Kurve bildet.
Nach einer kurzen Kraxelei den Felsen hinauf genieße ich oben einen sehenswerten Lost Place mit fair versteckter Dose und tollem Ausblick. Kurze Pause, einmal loggen und dann geht es zurück zum Parkplatz, der wie ich nun sehe auch durch eine Unterführung ohne lebensgefährlicher Straßenüberquerung erreichbar ist. „Na Prima“, denke ich und überfliege im Auto dann doch mal das Listing. „... Achtung bei Einparken … Parkplatz nur in Richtung Agios Nikolaos erreichbar … benutze die Unterführung und klettere auf den Berg“.
Besser als jeder Hint, man sollte das Listing halt vor dem Heben lesen, auch beim papierlosen Einsammeln von Tradis.


Abbildung 7: Das ehemalige Fischerdorf Agios Nikolaos hat sich heute schön herausgeputzt
Weiter geht’s Richtung Agios Nikolaos, vorbei an der bereits bekannten Mischung aus Tempolimits und Ankündigungen von Radarfallen. Die Griechen scheint dies beim Gasgeben aber nicht zu stören.

Abbildung 8: Die Aussicht auf die Bucht ist einfach traumhaft
Der Cache GC208QE „Lake“ verspricht eine wunderschöne Aussicht über den See des Städtchens. Dieses ist ein ehemaliges verträumtes Fischerdörfchen, dass sich in den letzten Jahren Dank Luxustourismus beachtlich herausgeputzt hat. Und richig, es wurde nicht zuviel versprochen: Die Aussicht ist grandios und die Vorfreude auf das Städtchen wächst.


Abbildung 9: "Ja ist denn hier schon Venedig?"
Im Yachthafen ist alles sauber und geordnet und für einen Moment komme ich mir vor, als wären wir im italienischen Venedig. Ein wohltuender Eindruck, wenn man bisher nur T-Shirt-Verkäufer, Minisupermärkte und Autovermieter in den Siedlungen gesehen hat.

Abbildung 10: Der Jachthafen ist auf jeden Fall ein Ausflug wert
Nachdem wir durch die Fußgängerzone gebummelt sind und auf dem Parkplatz unsere Lunchpaket dran glauben mussten, zieht es den nichtcachenden Teil der Familie auf einen angrenzenden Spielplatz während ich mich entscheide den Aufstieg zum Cache durch die engen Gässchen zu nehmen.


Abbildung 11: Abseits der Touristraße zeigt Agios Nikolaos seine verträumte Seite
Eine gute Entscheidung, denn der Weg führt über zahlreiche verträumte malerisch schöne Treppen und bietet ein Bild wie Nikolaos außerhalb der Tourizonen gebaut ist. Der Cache selbst besteht zwar nur aus einer Ü-Ei-Verpackung und ist in eine Steinmauer gelegt, das Panorama mit See und anschließendem Yachthafen zeigt aber, dass die Dose hier nur zweitrangig ist.
Auch wenn die Kreter noch in langer Hose und Pulli durch die Straßen ziehen, ist es für uns definitiv zu warm und so geht es zurück ins Hotel, denn der Lockruf der kalten Dusche ist kilometerweit zu hören.
So erfrischt geht es anschließend zum ersten kretischen Geocaching Event.
Neben einigen netten Plaudereien mit anderen Urlaubscachern lerne ich hier mit drei Teams direkt 60 Prozent der Cachinggemeinde dieser Insel kennen. Unter ihnen ist auch das Team OMILOS, die wie ich von ihnen erfahre neben ihren eigenen 93 Dosen auch unzählige weitere Caches betreuen, die von Urlaubern gelegt wurden. Ich habe fast das Gefühl dass sie mehr Dosen pflegen als selber gefunden zu haben. Da bekommt der Begriff Passivcacher eine ganz neue Bedeutung.
Bei weiteren Gesprächen mit lokalen Aktivisten erfahre ich, dass die Einwohner auf Kreta stets freundlich sind (was ich aus eigener Erfahrung bestätigen kann) und dass das geflügelte Wort „Kein Problem“ hier Lebensmotto ist. Und wenn es doch etwas zu lösen gibt, dann stets mit einer „griechischen Lösung“. Wie in vielen Gegenden Südeuropas wird auch hier die Lebensqualität groß geschrieben und die Vokabel „Stress“ ist wahrscheinlich genauso bekannt wie „Standstreifen“.

Abbildung 12: Auch wenn nur eine der beiden Radarschüsseln ausgefüllt ist, erinnert das Gebilde doch start an Walt Disney
Als wir auf die Radarschilder zu sprechen kommen, erklärt sich auch warum sich nur Fremde an Tempolimits halten. Da die Einwohner keine Radarfallen mögen, würden die Starenkästen einfach so lange kaputt geschossen, bis den Behörden Zeit und Geld ausgeht um neue aufzustellen.
Nach dem obligatorischen Eventtradi geht es auf zum frisch empfohlenen „Mickey Mouse Mountain“ GC2DJPW. Hierbei handelt es sich um eine ehemalige amerikanische Radarstation, deren Schüsseln von weitem wie zwei Micky Maus Ohren ausschauen. Der Name ist genauso treffend wie die Aussicht wieder überragend ist. Aber diese Cacheeigenschaft erwähne ich ja nicht zum ersten Mal.


Abbildung 13: Gott sei Dank hab den Leihwagen mit Vollkasko ohne SB genommen.
Beim Blick auf die Zufahrtsstraße wird mir schnell bewusst, warum es die richtige Entscheidung war, beim Mietwagen auf Vollkasko ohne Selbstbeteiligung zu setzen. Die mit tiefen Rillen durchzogene Betonstraße lässt jeden DDR-Gedächtnis-Plattenweg als Luxusautobahn erscheinen. Selbst bei Tempo 20 mache ich mir ernsthaft Sorgen, Radaufhängung und Stoßdämpfer würden nur noch mit angehangener TB Marke zurück zum Verleih finden.

Abbildung 14: Zum Abschluss gibt es wieder Lost-Place Feeling bei einer weiteren Windmühle
Praktischerweise können sich Mietwagen und Fahrer bereits nach wenigen Metern eine Pause gönnen um weitere Windmühlen zu begutachten. GC2DP00 „Venetien Windmills“ erscheint zunächst nur eine weitere Ruine zu sein, entpuppt sich aber als echtes Lost Place Schmuckstück mit Steinturm und Wendeltreppe, die man ersteigen muss um zur Dose zu gelangen. Ein Ort, der geradezu danach schreit, bedost zu werden. Zum Glück wurde er erhört.


Abbildung 15: 3 x darfst du raten, wo die Dose liegt... Richtig, genau da wo sie hingehört
Als ich wenige Tag später im Flugzeug sitze (nein, es war wieder keine Zeit fürs TB-Hotel) lässt sich Geocaching auf Kreta mit einer Eigenschaft zusammenfassen: Sehr qualitativ. Hier gilt noch Klasse statt Masse. Alle Dosen waren fair versteckt, die Aussicht war stets grandios und es wurde jedes Mal auf etwas Besonderes hingewiesen, wofür es sich lohnte hinzufahren (oder auch manchmal zu joggen). Es war Cachen auf hohem Niveau.
Ich werde zu Hause eine Menge Statistiktradis einsammeln müssen um genügend Favoritenpunkte für die kretischen Dosen zu sammeln.

Sonntag, 29. April 2012

Datenstrukturen in lua

Datenstrukturen

Voraussetzung: "Einstieg in Lua“

Abbildung 1: Variablen können lokal oder global definiert werden, es gibt Zahlvariablen, Strings, Booleanwerte und Arrays

Definition von Variablen

Wir beginnen dieses Tutorial mit der Definition von einfachen Variablen. Abb. 1 zeigt die verschiedenen Möglichkeiten Platzhalter zu definieren.
In den ersten vier Zeilen sehen wir global definierte Variablen vom Typ Zahl, Boolean, String und Array. Die ersten drei Typen sind uns von den graphischen Bausteinen her bekannt. Ein Array, oft auch als Tabelle bezeichnet, ist ein sehr flexibler und nützlicher Datentyp. Hier sehen wir ein Beispiel in Form einer einfachen Aufzählung. Der Zugriff auf Arrays erfolgt über einen Index, der die Stelle im Array definiert, indem man hinter den Arraynamen den Index in eckigen Klammern schreibt. An erster Stelle mit Index 1 wurde der String "Michael" gesetzt. Wir können diesen Wert mit names[1] abfragen. ( ! Während in vielen Programmiersprachen mit der 0 zu zählen begonnen wird, startet die Sequence bei lua mit der 1)
Wie man am dritten Wert des Array erkennen kann, können auch andere Platzhalter zur Definition herangezogen werden. names[3] würde folglich "Krolock" liefern.
Entgegen anderen Sprachen muss man bei der Definition weder ein Keywort "var" oder "obj" vor die Variable schreiben, noch den Datentyp explizit dabei schreiben. Dieser ergibt sich aus dem Wert, der ihr zugeordnet wird und kann sogar gewechselt werden. (Auch wenn das im Normalfall nicht empfehlenswert ist).

Lokale Variablen

In der siebten Zeile steht das Wörtchen "local" vor dem Variablen delta. Dies bedeutet, dass sie nur in der Methode nextRound() bekannt ist. Nachdem die Funktion abgearbeitet wurde existieren "delta" und "message" nicht mehr und der von ihnen belegte Speicher wird vom CarbageCollector wieder freigeben. "roundCount" ist aber global definiert und besitzt nicht mehr den Initialwert 0, sondern nunmehr 1.
Wie bereits im lua Einstieg erwähnt werden Strings nicht durch das Pluszeichen, sondern durch zwei Punkte konkateniert.

Ein Array als Tabelle und als Dictionary nutzen

Bisher haben wir das Array lediglich als eine einfache Tabelle genutzt. Das wahre Potentional ist aber viel größer. Wir wollen uns folgende Aufgabenstellung geben:
In einem Wherigo soll der Spiel eine Quizrunde durchlaufen. Dabei gibt es pro Runde 3 Fragen, mit jeweils 3 möglichen Antworten von denen natürlich nur jeweils eine richtig ist.

Abbildung 2: Definition eines Arrays als Tabelle und Dictionary

Abb. 2 zeigt uns eine solche Datenstruktur. In Zeile 13 sehen wir, dass der Index nicht automatisch eine Zahl sein muss, sondern auch ein String sein kann. game["round"] oder auch kurz game.round liefert den Wert 1.
Es geht knifflig weiter mit Zeile 14. Dort definieren wir als Wert für Index 1 ein Subarray, dem wir für den Key game[1].trial den Wert 1 zuordnen. Wir haben somit den ersten Versuch in Runde Eins vorbereitet. Um diesen mit Werten zu füllen wird in den nächsten beiden Zeilen (Zeilenumbruch dient nur der Übersichtlichkeit) der Inhalt von game[1][1] mit der Frage question, den möglichen Antworten answers und der richtigen Antwortnummer correctAnswerNr aufgefüllt.
Zeile 18 und 19 definieren dieses Trippel für die zweite Frage und game[1][3] dementsprechend die dritte Frage der ersten Runde.

Wie man an Zeile 23 erkennen kann, muss man nicht den Umweg über game[2][1], game[2][2], game[2][3] gehen sondern kann die drei Fragentrippel auch als "Einzeiler" definieren. (Damit es lesbar bleibt, hab ich Fragen und Antworten nicht ganz ausformuliert )

Abbildung 3: Hilfsmethoden für den Zugriff

Den Zugriff vereinfachen

Da der Zugriff auf die Datenstruktur nicht mehr der 08/15 Methode myArray[2] entspricht, schreiben wir uns ein paar Helferlein: Wir beginnen mit dem Abruf der aktuellen Frage: Zuerst fragen wir die Rundennummer ab, sobald wir diese haben fragen wir nach der Nummer des aktuellen Versuches. Somit haben wir die beiden Indizes für den Zugriff auf das Fragentrippel, von dem wir die question auswählen und zurückgeben. Der Rückgabewert ist hierbei ein String.
Praktischerweise verwaltet die Datenstruktur selbstständig welche Runde und welcher Versuch gerade akutell ist, sodass wir im späteren Programmablauf nicht mit lokalen Variablen diese Werte merken und aktualisieren müssen.
Die Abfrage der möglichen Antworten funktioniert auf die gleiche Weise. An Zeile 37 sieht man, dass man den Wert nicht erst zwischenspeichern muss, sondern ihn direkt zurückgeben kann.
Noch weniger Code ist bei der Ermittlung der korrekten Antwortnummer verwendet. Man sieht aber auch, dass sich der Code hart an der Grenze der Lesbarkeit befindet bzw. diesen Bereich eigentlich schon verlassen hat.

Abbildung 4: Der Runden- und Versuchszugriff wird durch weitere Getter abstrahiert.
Wir wollen den Vorteil, dass die Datenstruktur ihre "Pointer" Runde und Versuch selbst pflegt, weiter ausbauen, indem wir Getter für den Zugriff auf die aktuelle Round und Trail erstellen.
Während getRoundNr() noch sehr unspektakulär game.round zurückliefert, wird es bei getRoundArray(), getTrialNr() und getTrialArray() schon komplizierter, zumal sich die Getter auch gegenseitig aufrufen.
Den Vorteil dieses Verfahrens sehen wir in Zeile 45. Für das Ermittlung der korrekten Antwort brauchen wir nur getTrialArray() aufzurufen, um das Fragentrippel zu erhalten. Es wäre sogar noch eine weiter Optimierung möglich indem man getAnswers()[getCorrectAnswertNr] verwendet.

Bleibt noch eine Frage zu klären: Wie erreiche ich die nächste Runde, bzw. den nächsten Versuch?
Auch hier soll im laufenden Programmcode nicht auf die Variablen round und trial zugegriffen werden, sondern abstrahiert durch zwei increment (erhöhe) Methoden.
incrementRound() und incrementTrial() zeigen uns wie es geht.

Abbildung 5: Die Bedienung unserer game Datenstruktur ist recht einfach.
Die Logik der Datenstruktur ist erledigt, jetzt wollen wir sie bedienen. Der Dialgog in Abb. 5 zeigt uns wie einfach dies geschehen kann. Dieser Dialog ist natürlich nur zu Testzwecken sinnvoll. Im Spiel wäre ein MultipeChoice Dialog geeigneter.
Während getQuestion() uns die Frage "Wo wurde der erste deutsche Cache gelegt ?" liefert, liefert getAnswers(1) den String "Hamburg" zurück.
getAnswer(2) liefert genauso wie getCorrectAnswer() "Berlin", da getCorrectAnswerNr() als Ergebnis 2 zurück gibt.

Abbildung 6: Vor dem Aufruf des Inputs müssen dessen Fragen und MultipleChoice Möglichkeiten aktualisiert werden
Diese Abfragen sind nützlich um einen Input damit aufzubauen. Beim Start des Spiels (alternativ beim Erreichen der ersten Fragezone) rufen wir updateInput() auf, um Frage und potentielle Antworten im Input MyInput auf erste Runde, erster Versuch zu setzen. Anschließend starten wir den Input. Wie simple updateInput() implementier ist, werden wir weiter unten sehen.

Abbildung 7: Im Dialog MyInput wird die Antwort verglichen und Rounde / Versuch erhöht.
Nachdem der Spieler eine Lösung der MultipleChoice Frage ausgewählt hat, kann die Antwort mit getCorrectAnswer() verglichen werden. Ist die Lösung korrekt, wird die Frage der nächsten Runde vorgelegt, ansonsten die des nächsten Versuchs. Dabei fungieren prepareNextRound() und prepareNextTrial() wieder als Hilfsmethoden um Frage und Antworten anzupassen.

Abbildung 8: Der Dialog wird ausgebaut
Natürlich sollte man noch ein wenig Output in Form von MessageBoxen an den Player geben, die fallen der Übersichtlichkeit halber hier recht kurz aus. Man muss übrigens nicht im If und im Else Zweig eine MessageBox plazieren. Alternativ könnte man in jedem Zweig in der Lua User code Box eine Variable message definieren, die man vor dem Input in einer MessageBox über Lua user expression als Outputwert wieder aufruft.

Abbildung 9: Während objMyInput.Text Zugriff auf die Frage bietet, können die möglichen Antworten mit objMyInput.Choices überschrieben werden
Wie funktionieren prepareNextRound() und prepareNextTrial() eigentlich? Die Frage ist sehr einfach zu beantworten Beim Vorbereiten der nächsten Runde, wird der Rundezähler erhöht. Mit objMyInput.Text wird die Frage und mit objMyInput.Choices die möglichen Antworten im Input überschrieben. Und schon kann die nächsten Frage vorgelegt werden

Persistenz

Ein Problem bleibt aber noch übrig: Wie werden meine Variablen persistiert und überleben ein Abspeichern und Wiederherstellen (Laden) des Cartrigdes. Es werden nämlich nicht automatisch alle Variablen gespeichert. Die von Uriwgo angelegten Variablen werden in einem Feld namens ZVariables gespeichert.

Abbildung 10: Die einfachste Art eigene Variablen zu persistieren ist sie an ZVariables anzuhängen
Variablen, die man im lua Code anlegt werden natürlich nicht automatisch in dieses Feld aufgenommen. Möchte man also die eigenen Objekte sichern -für unser Array game sehr empfehlenswert-, so gibt es verschiedene Möglichkeiten:
  • Man legt eine Variable namens game in der graphischen Oberfläche von Urwigo an. Entweder setzt man den Identifier ebenfalls auf game, oder man spricht sie mit objGame an
  • Man legt eine Variable namens storage in der graphischen Oberfläche von Urwigo an. Im OnSave Block kann man dann storage als Array umdeklarieren und alle eigenen Variablen dort einfügen. ! In OnRestore muss das Array wieder aufgedröselt werden
  • Man fügt beim Anlegen der Variable dieselbige dem Feld ZVariables hinzu (Zeile 2 in Abb. 10). Dies kann in einer Zeile geschehen und man muss sich nicht mehr um die Serialisierung kümmern. Dabei ist zu beachten, dass das Feld ZVariables an der Cartridgevariablen objDatenstrukturen (obj + Cartridgename) hängt.

Montag, 16. April 2012

Fortschrittskurs - Dynamische Programmierung

Fortschrittskurs - Dynamische Programmierung

Voraussetzung: "Einsteigerkurs“
Im Anfängerkurs haben wir gelernt, wie man ein statisches Wherigo-Projekt erstellt.
Im Fortschrittskurs wollen wir nun einen Schritt weiter gehen und lernen wie man diese beliebte Cacheart durch dynamischen Programmablauf noch interessanter machen kann.
Dynamisch bedeutet hier, dass der Spieler nicht einer festgelegten Route (Parkplatz – Kirche – Rathaus – Marktplatz) folgen muss, sondern während des Spiels selbst entscheidet, welche Station er als nächstes angehen möchte.
Außerdem werden wir keine Stadtrundführung programmieren, sondern eine Mini-Version des Brettspieles Scotland Yard, bei dem der Spieler den mysteriösen Mister X jagen muss.


Abbildung 1: Mini-Streckenfahrplan mit Taxi-, Bus- und U-Bahn-Verbindungen. Im fertigen Wherigo sind natürlich mehr Stationen vorhanden. Damit es nicht zu kompliziert wird begnügen wir uns mit vier Stationen

Konzept

Beginnen wir wieder mit einem Konzept:
Das Spielfeld besteht aus 4 Zonen - nachfolgend als Stationen bezeichnet -, die jeweils einen Taxistand darstellen. Zusätzlich zu der Taxiverbindung kann man zwischen Station 1 und Station 3 mit der U-Bahn und zwischen Station 2 und Station 3 mit dem Bus fahren.
Als Spieler steht man beim Erreichen einer Station vor zwei Entscheidungen: Mit welchem Verkehrsmittel möchte ich mich fortbewegen und wohin möchte ich fahren. Ihr seht schon, dass bereits an dieser Stelle das Spiel dynamisch verläuft.
Entgegen des Brettspieles gibt es Mister X nur virtuell, das einzige was wir im Programm für ihn benötigen ist eine Variable, die seine Position speichert. Mister X ist in der Regel unsichtbar, es wird nur offenbart, welches Verkehrsmittel er benutzt. Wir werden dies mit einer Message darstellen, die folgendermaßen aussehen könnte: “Mister X fährt Taxi“. Bei jedem dritten Zug zeigt er sich und teilt uns mit wo er ist, bzw. besser gesagt wo er war, bevor er das nächste Verkehrsmittel benutzt. Damit es nicht zu lange dauert, zeigt sich Mister X ab der zweiten Runde. Eine Offenbarung ist also mit Runde 2, 5, 8, 11, 14 … fällig.

Abbildung 2: Der Spielablauf verläuft rundenbasiert. Nachdem man eine Station erreicht hat, zeigt sich Mister X (ab und zu) und fährt sofort weiter. Wir entscheiden uns für ein Transportmittel und eine Station, laufen diese an und die nächste Runde beginnt.
Der Ablauf soll also folgendermaßen aussehen (siehe auch Abb.2):
  • Der Spieler erreicht eine Station.
  • Wenn sich Mister X ebenfalls in dieser Station befindet, ist das Spiel gewonnen und per Message teil Mister X mit wo sich der Final befindet.
  • Ansonsten ist Mister X an der Reihe und fährt zur nächsten Station
  • Abhängig von der Station wählt der Spieler zwischen Taxistand, Bushaltestelle oder U-Bahnstation
  • Abhängig von Station und Verkehrsmittel wählt der Spieler die nächste Station aus.
  • Der Spieler läuft, Verzeihung fährt zur nächsten Station
  • Der Spieler erreicht die nächste Station. Da wir uns in Runde 14 befindet teilt uns Mister X mit wo er sich befindet.
Um dieses Szenario abzubilden, benötigten wir folgende Bausteine:
  • 5 Zonen, „Station 1“ – „Station 4“ sowie „Final“
  • 4 Gegenstände Taxistand, Bushaltestelle und U-Bahnstation sowie Spoiler
  • pro Gegenstand ein Kommando („Taxi fahren“, „Bus fahren“ bzw. „U-Bahn fahren“)
  • Variablen für Position Mister-X „PosX“, Position Spieler „PosPlayer“ und Rundenzähler „RoundCount“
  • Bilder für Taxi, Bus und U-Bahn, Mister X, Stationen, Final und Spoiler

Abbildung 3: Beim Annähern an die Station wird der Programmablauf an eine Funktion weitergeleitet.

Zoneneintritt

Zu Beginn richtigen wir unser Augenmerk auf die "On proximity" Funktion der Zonen. Im Einsteiger-Tutorial haben wir unsere Zonen mit 4 Punkten abgesteckt und "On enter" implementiert. Bei dieser Vorgehensweise kommt es immer wieder zu Problemen im Spielablauf indem der Wherigo-Player entweder eine Ecke der Zone anvisiert um seinen Richtungspfeil auszurichten, oder der Wherigo-Builder aktualisiert den OriginalPoint der Zone, an dem sich viele Player orientieren, nicht wenn die Zone nachträglich verschoben wird. Wenn man die Zone nicht genau abstecken muss und auch gut mit einem Kreis leben kann, so ist "On proximity" mit dreimal Kreismittelpunkt als Koordinaten eine gute Alternative. Über "In proximity" lässt sich der Radius des Kreises bestimmen. Der Vorteil der Methode liegt auf der Hand: Der Spieler navigiert genau auf den Zonenmittelpunkt und als Cacheowner reicht es einen Punkt zu messen. Denn meistens möchte man den Spieler an einem bestimmten Punkt führen. Anstatt sich „On enter“ zu bedienen wird der Programmablauf beim Erreichen einer Station in „On proximity“ definiert. Ob ihr euren Wherigo mit Rechteck- oder Kreiszonen ausstattet, ist letztendlich euch überlassen und kann auch von der Situation vor Ort abhängen.

Funktionen

Da wir bei jeder Station beim Eintritt, bzw. nun bei Annäherung die gleichen Arbeitsschritte durchführen und wir diesen Ablauf nicht jedes Mal neu programmieren wollen, bedienen wir uns der Funktionen. Wie in Abb. 3 dargestellt, setzen wir beim Erreichen der Zone die aktuelle Position des Spielers und rufen anschließend die Funktion „enterZone“ auf.


Abbildung 4: Funktion „enterZone“ – Während die eigene Position und das Transportmittel von Mister X immer ausgegeben werden, wird seine Position nur jeden dritten Zug offenbart.
Die Funktion „enterZone“ bildet das Kernstück unserer Programmierung. Mit ihrer Hilfe wird ermittelt ob wir Mister X gefangen haben oder ob das Spiel weitergeht. Weiterhin werden hier die verfügbaren Verkehrsmittel freigeschaltet, Mister X zeigt sich (ab und zu) und plant dann seinen nächsten Zug.

Mister X wird sichtbar

Sobald ein Spieler eine Station erreicht erhöhen wir den Rundenzähler. Diesen benötigten wir um bei jeder dritten Spielrunde die Position von Mister X anzuzeigen. Da der Text der Message von dieser Bedingung abhängig ist, bauen wir erst den Text zusammen indem wir verschieden Textbausteine aneinander reihen und in der Variable „Message“ speichern. Im unteren Teil von Abb. 4 sehen wir die Abfrage der Rundennummer und die bedingte Erweiterung der Variable „Message“. Die beiden langen Sätze sind mit einem Zeilenumbruch versehen um die Lesbarkeit während des Spieles zu verbessern.

Abbildung 5: Funktion „enterZone“ – Spielende. Wenn die Position von Mister X mit der unseren übereinstimmt, haben wir ihn gefangen und können ihn verhören.
Darunter erkennen wir im Ansatz den Message-Baustein (fortgesetzt in Abb. 5), in dessen "On clicked" Bereich zunächst alle Station deaktiviert werden und danach überprüft wird, ob Mister X die gleiche Position hat wir wir selbst und sich damit in unser Zone befindet. Sollte dem so sein, ist Mister X geschlagen und stellt sich einem Verhör zur Verfügung. Dazu später mehr.

Mister X ist am Zug

Wenn wir Mister X nicht gefangen haben, ist nun die Stelle erreicht wo er seinen Zug ausführt. Dazu berechnet er abhängig von der Position des Spielers die nächste freie Station.
Mister X erhält also künstliche Intelligenz, auch wenn es sich hier nur um ein Minimum handelt. In den demnächst folgenden lua Tutorials werden wir erfahren wie wir mit Hilfe von direkter lua Programmierung und einem Path-Finding-Algorithmus ihn etwas schlauer machen, für diesen Kurs reicht uns folgender Ansatz :
  • Bestimme abhängig von seiner aktuellen Position eine durch Verkehrsmittel zu erreichende Station für Mister X
  • Befindet sich der Spieler auf der potentiellen Station?
    • Wenn nein, wähle die potentielle Station als aktuelle Station für Mister X
    • Wenn ja, wähle eine alternative Station
In beiden Fällen wird die Textvariable „Transport“ gesetzt, mit deren Hilfe der Text „Mister X fährt Taxi | Bus | U-Bahn“ zusammengesetzt wird. Anschließend wird in einer Message der gesamte Text ausgegeben.

Abbildung 6: Funktion „enterZone“ – Abhängig von unserem Standort sowie seiner eigenen Position plant Mister X seinen nächsten Zug.

Abbildung 7: Funktion „enterZone“ - Taxi fahren darf man an jeder Station. Welche Alternative zur Fortbewegung zusätzlich zur Verfügung steht, hängt von der Station ab.

U-Bahn, Bus oder doch nur Taxi ?

Während der Taxistand in jedem Fall aktiviert wird (Taxi fahren darf der Spieler von jeder Station aus) verzweigen wir für jeden möglichen Aufenthaltsort des Spielers um gegebenenfalls die Bushaltestelle oder die U-Bahnstation freizuschalten. Wie man in Abb. 7 erkennt, gibt es keine Fallunterscheidung für Station 4. Dies liegt daran, dass dort außer dem bereits aktivierten Taxi kein Transportmittel zur Verfügung steht.
In den ersten drei Runden geben wir noch den Hinweis aus, was der Spieler nun unternehmen soll. Nach den drei Runden wird er (hoffentlich) verstandenen haben wie der Hase läuft und wir ersparen ihm die nun lästige Nachricht.

Abbildung 8: Abhängig von der Station, in der wir einsteigen, dürfen wir mit dem Taxi unterschiedliche Station anfahren

Türen zu und los – Aber wohin dürfen wir mit Taxi & Co fahren?

Im nächsten Schritt schauen wir uns die Taxifahrt ein wenig genauer an, soll heißen wir implementieren das Kommando „Taxi fahren“ für den Gegenstand „Taxistand“.
In Abb. 8 erkennen wir, dass wir nichts weiter machen müssen als die Stationen aktiv zu schalten, die von der aktuellen Station des Spielers aus mit dem Taxi erreichbar sind.
Da jede Station einen Taxistand beinhaltet, müssen wir auch für jede eine Fallunterscheidung einbauen.

Abbildung 9: Bus fahren gestaltet sich recht übersichtlich, da lediglich zwei Stationen mit Bushaltestellen ausgestattet sind.
Dies ist beim Busfahren schon ein wenig übersichtlicher. Ein Blick auf den Spielplan zeigt, dass lediglich Station 2 und 3 über eine Bushaltestelle verfügen. Wie wir Abb. 9 erkennen, müssen wir auch nur diese beiden Station in die Fallunterscheidung einbeziehen. Für die U-Bahnfahrt sieht es ähnlich übersichtlich aus.

Abbildung 10: Nachdem wir uns für eine Art des Fortkommens entschieden haben, werden alle Transportmittel deaktiviert

Sammelfunktion „afterTransportChoice“

Bleibt nur noch zu klären, was es mit der Funktion „afterTransportChoice“ auf sich hat. In dieser Funktion werden alle Aktivitäten zusammengefasst, die nach der Wahl des Transportmittels ausgeführt werden müssen. In unserem Fall setzen wir die Sichtbarkeit von Taxistand, Bushaltestelle und U-Bahnstation auf "False", denn nachdem der Spieler sich für ein Transportmittel entschieden hat, soll er dies nutzen und zur nächsten Station laufen. Eine erneute Wahl macht also wenig Sinn. Wer beim Blick auf Abb. 10 denkt „Wozu soll ich diese drei mickrigen Anweisungen in eine extra Funktion auslagern?“, dem sei gesagt, dass es nicht nur darum geht sich das Wiederholen der drei Anweisungen zu sparen. Nein, spätestens wenn wir uns in Zukunft dazu entscheiden zusätzlich einen Flughafen oder eine Bootsanlegestelle anzubieten oder einen Text „Gute Wahl. Viel Spaß bei der Fahrt“ auszugeben, spätestens dann zeigt sich ein entscheidender Vorteil der Auslagerung des Codeteils in eine Funktion: Die Wartbarkeit. Es gibt nichts schlimmeres bei der Programmierung als Änderungen oder Fehlerbehebungen an gefühlten tausend Stellen durchführen zu müssen.

Abbildung 11: Das Verhör mit Mister X endet ziemlich schnell. Wer möchte, kann es zu einem Dialog ausbauen, bei dem Mister X überführt werden muss.

Mister X in Gefangenschaft

Abhängig von Größe der Spielfeldes und Spielablauf haben wir Mister X nach mehr oder weniger Zügen gefangen. Damit haben wir aber noch keine Dose in der Hand. Um den Bezug vom Spiel zum Final zu realisieren bauen wir ein Verhör mit Mister X ein, der uns somit die Finalzone freischaltet. Dazu erstellen wir ein Kommando „Mister X verhören“, deren Implementierung aus Message und zwei Set-Baustein besteht, mit deren Hilfe wir die Zone „Final“ sichtbar und aktiv schalten.

Damit ist der dynamische Ablauf unseres Wherigos komplett und wir können uns um weitere Details kümmern.

Abbildung 12: Lediglich beim ersten Erreichen der Station wird die Frage nach der Hausnummer vorgelegt.

Simulationsschutz

Als erstes betrachten wir den Aspekt des Simulationsschutzes. Über die Frage, ob man dem Spieler freistellt den schön gemachten Wherigo am Simulator auf der Couch durchzuspielen oder ob man ihn zwingt mehr Spielspaß vor Ort zu erleben, kann man ewig diskutieren und die Antwort hängt unter anderem vom Wherigo und der Örtlichkeit ab. Jetzt mag sich der ein oder andere denken „Moment, Urwigo bietet doch dieses nette Häkchen im Hauptfenster an“, der Praxistest zeigt aber einige Probleme des vorgefertigten Simulationsschutzes auf. Dieser verlässt sich auf die Messungenauigkeit der Geräte und meint einen echten von einem simulierten Programmablauf daran zu erkennen, dass sich innerhalb bestimmter Zeiten Höhen- und Positionsangaben ändern müssen. Nun lassen sich auf der einen Seite diese Schwankungen durch weiterentwickelte Emulatoren vortäuschen, auf der anderen Seite, und dies ist der entscheidende Part, kommt es bei bestimmten Geräten zu der Situation, dass „ehrliche“ Cacher auf der Wiese stehen und zu Unrecht eine Fehlermeldung à la „Du sollst nicht simulieren“ erhalten. Die einzig wirksame Maßnahmen den Couch-Potato in die Natur zu kriegen, bleibt die Abfragen von Ortsmerkmalen wie Hausnummern, Farben, Anzahl von Schrauben usw. Dies ist bisher auch noch nichts neues. Jeder Multi und die meisten statischen Wherigos arbeiten mit diesen Ortsmerkmalen.

Hausnummernsuche? Bitte nur beim ersten Stationsbesuch

Bei unserem dynamischen Spiel haben wir allerdings das Problem, dass wir Stationen mehr als einmal erreichen und jedes Mal die Hausnummer „85a“ einzugeben führt nicht zwangsweise zur Attraktivitätssteigerung. Wir wollen dieses Problem mit einer Booleanvariablen lösen. Dies Variable kann lediglich zwei Werte annehmen: "True" oder "False". Abb. 12 zeigt die Verwendung für die erste Frage. Nach dem Erreichen der Station wird abgefragt, ob die erste Frage (noch) aktiv ist. Wenn dem so ist –wir erreichen diese Station zum ersten Mal– wird die Frage „Question1“ vorgelegt. Wenn wir die Station das nächste Mal anfahren wird die Funktion „enterZone“ sofort ausgeführt.

Abbildung 13: Wenn die Antwort korrekt ist, wird das Vorlegen der Frage beim nächsten Erreichen der Station deaktiviert.
Wie aber erreichen wir nun dass die Variable „Question1Active“ beim nächsten Mal auf "False" steht. Die Antwort zeigt uns Abb. 13. Die Rekursion bei falscher Antwort kennen wir ja bereits aus dem Anfängerkurs. Neu ist nun, dass wir die Booleanvariable mittels Set-Baustein auf "False" setzen und anschließend unsere bekannte Funktion für das Erreichen einer Zone aufrufen. Hier zeigt es sich wieder vorteilhaft, dass wir diesen Ablauf in eine Funktion ausgelagert haben.

Abbildung 14: Spiel beenden. Anstatt mühsam mehrere Message-Bausteine über „On clicked“ zu synchronisieren nutzen wir den komfortablen Weg des Dialogs. Der „Completition code“ wird von wherigo.com erzeugt wenn das Cartrigde vor dem Download online kompiliert wird.

Spoiler, Complete-Flag und Unlock-Code beim Final

Beim Annähern an den Final verwenden wir die "On proximity" Logik aus dem Einsteigerkurs. Zusätzlich zum Aktivieren des Spoilers und dem Complete-Flag wollen wir aber noch mehr durchführen. Als erstes neues Element sehen wir den Dialog. Mit dessen Hilfen lassen sich mehrere Bildschirmausgaben hintereinander schalten, ohne dass wir uns selbst um die Synchronisation via „On clicked“ kümmern müssen, wie wir das bei mehreren Message Boxen hätten tun müssen. Beim Dialog lassen sich einfach mehrere Dialog Entries untereinander platzieren, die nacheinander aufgerufen werden, sobald ein Dialog Entry mit „OK“ bestätigt wird. Im mittleren Entry sehen wir, wie wir den Unlock-Code ausgeben können, damit das Cartridge auch ohne gws Datei auf wherigo.com als gefunden markiert werden kann.

Abbildung 15: Durch den breiten Set-Baustein braucht die gesamte Anweisung viel Platz

Etwas mehr Übersichtlichkeit bitte

Sobald man die erste komplexe Idee implementiert wird man schnell feststellen, dass der Bildschirm gar nicht groß genug sein kann um alles darstellen zu können. Besonders bei verketteten if/else Bedingungen stößt man schnell an die Grenze der Lesbarkeit. Eine Form der Abhilfe ist die Zoomfunktion, die man benutzen kann indem man bei gedrückter Strg-Taste mit dem Mausrad vor und zurück scrollt. Der angezeigte Ausschnitt des Flussdiagramms wird somit heran bzw. heraus gezoomt, wobei zu viel herauszoomen dazu führt, dass man die Schrift nicht mehr lesen kann. Eine Alternative zum Zoomen ist das Ausblenden von Bausteinen, die gerade nicht bearbeitet werden indem man auf das schwarze Dreieck neben dem Bausteinnamen klickt (kleiner Rahmen in Abb. 15).

Abbildung 16: Nachdem wir den Set-Baustein minimiert haben erscheint die Anweisung übersichtlicher
Somit bleiben lediglich die Bausteine sichtbar, die bearbeitet werden sollen bzw. zur Bearbeitung benötigt werden. Ein weiterer Klick auf das Dreieck blendet den Baustein dann wieder ein.

Wo kann ich diesen Wherigo spielen?

Wer Scotland Yard nicht nur in der Mini-Version, sondern als kompletten Wherigo erleben möchte, sollte nach Scotland Yard in Heide Ausschau halten. Hier wurde die Intelligenz von Mister X verbessert, sodass er zu Beginn recht schwierig zu fangen ist. Wie man so etwas direkt in lua programmiert und wie man Mister X im Laufe des Spieles dümmer werden lässt, könnt ihr in der Kategorie Wherigo für Experten herausfinden.

Der Einstieg in Lua

Einstieg in Lua

Voraussetzung: "Einsteigerkurs“

Der Urwigo builder ist ein recht nettes Tool, aber eines Tages entstehen durch immer neue „dümmere“ Ideen Anforderungen, die man mit einem reinen Kicki-Bunti-Baustein-Geschubse nicht mehr abbilden kann.
Spätestens jetzt wird es Zeit die reine Baustein-Lösung zu verlassen und seinen Horizont Richtung Programmierung zu lenken. Das soll nicht heißen, dass wir auf Urwigo und die graphischen Bausteine ganz verzichten. Nein, wir erweitern unser Projekt lediglich um neue Funktionen. (Wobei ich aus eigener Erfahrung sagen kann, je mehr man lua kennt, desto weniger klickt man die Bausteine zusammen. Aber das kommt von selbst nach und nach und muss nicht auf einen Schlag passieren).

Was ist lua?

Lua ist eine Scriptsprache, die sich dadurch auszeichnet, dass sie wenig Hardware und Speicher benötigt um einfache Abläufe zu realisieren. Klingt also ideal für „Steinzeitgeräte“ wie dem Garmin Oregon (Sorry, aber was Prozessorleistung betrifft, merkt man schon dass das Gerät einige Jahre auf dem Markt ist).
Wenn man sich den Urwigo builder genauer anschaut und eine erstellte gwz Datei entpackt (WinZip, WinRar oder 7zip) so erkennt man, dass die gesamte Programmierung in einer _cartridge.lua Datei steckt, die Urwigo aus den Bausteinen baut. Beim Erlernen der verschiedenen lua Befehl kann es hilfreich sein, einen Baustein ins Projekt einzufügen, das Projekt als gwz abzuspeichern, zu entpacken und sich dann den resultierenden lua Code anzuschauen.


Abb. 1: Gerade mal drei Tabellen bilden das Datenmodell der Straßen: Name, Anzahl der Häuser und Miete pro Haus.
Die Funktion calcRent() berechnet daraus die fällige Miete

Aufgabenstellung für unseren Wherigo

Als Beispiel für dieses Tutorial möchte ich einen Teil aus dem Cachopoly Projekt erläutern. Es gibt Straßen, auf die Häuser gebaut werden können und wo abhängig von der Straße pro Haus Miete fällig wird. (der Einfachheit halber gilt Miete = Anzahl Häuser * Miete pro Haus).

Als erstes definieren ein Zahlvariable namens „RentToPay“.
Danach legen wir je ein Feld (Array) an für den Namen der Straße, die Anzahl der Häuser, die es pro Straße gibt sowie für die Miete, die auf der Straße pro Haus fällig wird. (siehe Zeile 1 bis 3 in Abb. 1). Arrays werden mit geschweiften Klammern angegeben. Pro Straße gibt es einen Wert in dem Array, wobei es auf die Position im Array, Index genannt, ankommt.
Straße 1 (! Badstrasse, da keine Umlaute) hat folglich 1 Haus und pro Haus sind 200 Eur fällig.

Nun definieren wir eine Funktion, die für eine Straße -dargestellt durch den Index- die Anzahl der Häuser mit der Miete pro Haus multipliziert und das Ergebnis der Variablen „objRentToPay“ zuordnet. Der Zugriff auf das Array erfolgt über den Index, den man in eckigen Klammern hinter den Arraynamen schreibt. rentPerHouse[3] liefert das dritte Element des Arrays, nämlich 500.

„objRentToPay“ oder „RentToPay“ ?

Wenn wir uns den rechten Teil von Abb. 1 genauer anschauen, sehen wir, dass bei der Variablen „RentToPay“ ein Feld Identifier vorhanden ist. Dies ist quasi die Id der Variablen in lua. Trägt man hier nichts ein und lässt das Feld auf „Automatic“ so bilden lua die Id indem es „obj“ vor den Namen schreibt. Aus dem Namen „RentToPay“ wird die Id„objRentToPay“. Möchte man dies nicht, so kann man den Identifier selbst vergeben, z.B indem man den Namen „RentToPay“ nochmals ins Feld Identifier schreibt.

Abb2. : Im Flussdiagramm kann lua code mit den Bausteinen "Lua User code" und "Lua user expression" eingebaut werden

Die eigene Funktion aufrufen

Unsere (vereinfachten) Straßen sind durch die drei Arrays definiert und die Funktion für die Mietberechnung ist auch aufgestellt. Wie aber können wir diese Funktion auch nutzen?
Die Lösung zeigt uns Abb. 2. Beim Start des Cartridge verwenden wir einen „Lua User code“ Baustein um die Funktion mit der zweiten Straße aufzurufen. Die Variable „RentToPay“ besitzt nach dem Aufruf den Wert. 3 * 300 = 900. In der anschließenden MessageBox wird diese Variable nun für die Ausgabe mit anderen Textbausteinen konkateniert.
Interessant hierbei ist auch der Baustein „Lua user expression“, der einen direkte Zugriff auf unser streetNames Array ermöglicht und eine Zeichenkette zurückliefert. Im Gegensatz zu „Lua User code“ wird hier also etwas zurückgegeben.
Die Ausgabe im Gerät würde für diesen Fall lauten: „Du erreichst die Strasse E-Werk. Zahle 900 EUR“.

Geben und Nehmen

Apropos Rückgabe, in der zweiten MessageBox wird die Funktion „buildPaymentMessage“ aufgerufen, die dem „Lua user expression“ Baustein einen Wert (Zahl oder Zeichenkette zurückliefern muss. Dazu habe ich den lua Code in Abb. 3 um die Funktion „ buildPaymentMessage“ erweitert, der die gleichen Textelemente zusammenbaut wie unser Concatenate Baustein in Abb. 2. Auffällig ist, dass Zeichenketten -genannt Strings- nicht wie in anderen Programmiersprachen mit + sondern mit .. verbunden werden. Wie man sieht, ist der Zugriff auf das Array streetNames schneller als im Concatenate Baustein. Nachdem der String zusammenbaut ist, wird er durch den return Befehl dahin zurückgegeben wo die Funktion aufgerufen wird. Auch die Funktion „calcRent“ wurde um einen return erweitert, damit wir sie beim Zusammenbauen der PaymentMessage verwenden können.

Abb3. : Während auf der linken Seite buildPaymentMessage ein Beispiel die Konkatenation eines Strings sowie der Rückgabe eines Wertes dargestellt ist, zeigt der rechte Teil, wie man die Id eines lua Objektes anpassen kann.
Das urwigo Projekt zu diesem Tutorial als Download"

Donnerstag, 12. April 2012

Edit lua code in an external editor

Edit lua code in an external editor

This is the english version of the tutorial. It is also available in german
Dies ist die englische Version des Tutorials. Es ist ebenso auf deutsch verfügbar.

If you are familiar with programming wherigo cartridge in lua, you know the problem of too much code lines. Your program become confusing. The syntax highlighting of urwigo builder lua is a nice feature, but it is insufficient to handle code with a lot of lines.
During my programming of Cachopoly I have the problem to handle more than 1000 lines and so my main activity was to scroll to the line needed for debugging and testing.

After mailing with the developers of urwigo, they updates their editor. Know it is possible to link an external lua file which can be use in the urwigo project.
I decided to use the IntellijIDEA editor witch I extend with the lua plugin sylvanaar.
In Picture 1 you can see a simple "HelloWorld" Example, written with IDEA
Picture 1: Write lua code in IntellijIDEA

Top left you can see the storage location of the lua file. I decided to create a sub folder called lua which optimise the structure. But this decision is optional.
At the right side you can see the lua code. Beside syntax highlighting you can use features like parameter and function name competition and real-time syntax error and warning marking.
Bottom left we have the 'structure' element where I can see all the functions defined in my lua code. With the 'Autoscroll to source' feature you can jump to line 14 in code by clicking on the function 'helloWithName' in the structure frame. A nice feature if your code contains several hundred code lines.

It is not mandatory to use IntellijIDEA. You can edit the lua files with your favorite editor, but their are not many alternatives. If you found one, feel free to send me an email or make a comment for this post.
Pciture 2: all lua files in sub folder 'lua' are embedded
In which way can I embed the external files in my urwigo project? It seems to be tricky, but it will be very simple if you have see it once. All you have to do are two simple steps:
First you have to activate the checkbox "Inline Lua 'require'" and set the path to the lua files in the main screen of your urwigo project. The path 'lua\?.lua' contains all files in sub folder 'lua' (relative to your project folder) which ends with '.lua'.
The usage of a sub folder is optional. I think you will optimise your structure by dividing the lua files from your project files and picture.

Pay attention to the wildcard, that is not the asterisk '*' but the question mark '?'.
Picture 3: Embed the lua file with 'require'
The second step is very simple. All you have to do is write a require in your 'lua user directives' or 'lua user functions' of your project. The name of the embedded model must be the filename of the lua file
require "hello" must be equals to hello.lua.
Picture 4: Use the functions like you be used to
You can use the functions defined in the external file just as they were defined local in 'lua user directives' or 'lua user functions').

Dienstag, 10. April 2012

Lua in externen Editor bearbeiten

Lua in externen Editor bearbeiten

Dies ist die deutsche Version des Tutorials. Es ist ebenso auf englisch verfügbar.
This is the german version of the tutorial. It is also available in english

Voraussetzung: "Einstieg in Lua“

Wer einmal komplexere WIG-Projekte mit lua programmiert hat, wird recht schnell feststellen, dass das Syntaxhighlighting von Urwigo zwar recht nett, aber beileibe nicht ausreichend ist. Es fehlen Editorfunktionen wie Ersetzen von Befehlen, automatisches Schließen von Schachtelelementen, eine Übersicht der Variablen und Funktionen mit Möglichkeit direkt zur richtigen Zeile im Code springen zu können.
So wurde die Implementierung von Cachopoly bei über tausend Zeilen lua Code schnell zur nervigen Scrollpartie um beim Debuggen und Testen die richtigen Codestellen zu finden.

Nach längerer Diskusion und Austausch mit den Urwigo Entwicklern haben diese nun Abhilfe geschaffen und die Möglichkeit geboten den lua Code in eigene Dateien auszulagern, die man dann in einem externen Editor bearbeiten kann.
Ich habe mir dabei für den IntellijIDEA Editor entschieden, dem man mit sylvanaar ein brauchbares Plugin verpassen kann um lua Code zu implementieren.
Abbildung 1 zeigt den Editor mit einem "HelloWorld" Beispiel.
Abbildung 1: Lua in Intellij Idea bearbeiten

Im linken oberen Teil ist der Speicherort der lua Datei festgelegt. Der Übersichtlichkeit habe ich im Urwigo Ordner 'Test' einen Unterordner 'lua' angelegt, in den ich meine lua Dateien ablege. Soweit es sich dabei nur um eine einzige Datei handelt, könnte man sich den Aufwand des Unterordners auch sparen.
Im rechten Teil ist der lua Code zu sehen, den man mit vielen netten Feature wie Syntaxhighlighting, Befehl- und Variablenvervollständigung, Fehler- und Warnanzeigen recht komfortabel editieren kann.
Unten links ist die Funktionsübersicht zu sehen. Hier ist die vermisste Jumpfunktion realisiert, die sehr hilfreich ist, wenn man in über tausend Zeilen Code schnell zur Methode 'helloWithName' springen möchte.

Es muss übrigens nicht zwingend IntellijIDEA sein. Welchen Editor ihr verwendet ist euch überlassen. Viele brauchbare Alternativen gibt es aber nicht. Ich wäre für Feedback an dieser Stelle trotzdem sehr dankbar.
Abbildung 2: Einbinden aller lua Dateien im Unterordner 'lua'
Wie binde ich nun diesen Code in meinem Urwigo Projekt ein ? Dies scheint ein wenig tricky, ist aber sehr einfach, wenn man es einmal gesehen hat und besteht nur aus zwei kleinen Schritten:
Als erstes wählen wir auf der Hauptseite des Urwigo Projektes die Checkbox "Inline Lua 'require'" und setzten den Pfad in dem unsere lua Dateien liegen. Der Pfad 'lua\?.lua' erfasst alle Dateien die im Unterordner 'lua' liegen und die auf '.lua' enden. Zu beachten ist, dass das Jokerzeichen hier das Fragezeichen '?' und nicht wie gewöhnlich der Stern '*' ist.
Abbildung 3: Im directive Fenster müssen die lua Datein eingebunden werden
Der zweite Schritt ist noch einfacher und besteht lediglich aus einem require im 'lua user directives' oder 'lua user functions' Bereich des Projektes. Das dadurch eingebundene Modul muss den gleichen Namen tragen wie unsere lua Datei.
require "hello" muss mit hello.lua übereinstimmen.
Abbildung 4: Der Funktionsaufruf funktioniert wie gewohnt
Der Funktionsaufruf im Urwigo Projekt funktioniert genauso wie beim lokal (in 'lua user directives' bzw in 'lua user functions') definierten Code.

Samstag, 7. Januar 2012

Timer II - Countdown

Fortschrittskurs „Timer II - Countdown“


Voraussetzung: "Forschrittskurs „Timer I - Zeitstress“


Wir nehmen uns das Cartrigde aus „Timer I – Zeitmessung“ und ersetzen den normalen Timer durch einen Countdown. Ziel ist es permanent die Restzeit („Noch 50 Sekunden“, „Noch 49 Sekunden“ usw.) anzugezeigen.
Dazu erstellen wir eine Zahlvariable „TimeLeft“, die auf 60 (Sekunden) eingestellt ist und einen zweiten Timer names „Countdown“, bei deren Ablauf folgendes passieren soll: Ist „TimeLeft“ noch größer als 0, so wird die Restzeit in einer Message angezeigt, die sich jede Sekunden erneuert und damit die alte überlagert. Anschließend minimieren wir den Wert um einen Zähler.
Ist der Spieler nicht schnell genug und „TimeLeft“ erreicht den Wert 0 wird wie beim alten Timer die Booleanvariable „SatelliteReached“ auf „False“ gesetzt, der Misserfolg gemeldet und der Timer gestoppt.
Der letzte Schritt muss unbedingt im „On clicked“-Bereich erfolgen, da der Timer nur gestoppt werden kann, wenn er sich nicht in einem Verarbeitungsschritt befindet. Wie bereits im Anfängerkurs angesprochen stoppt der Programmablauf nicht, wenn die MessageBox erscheint. D.h. Im Hintergrund wird die „On elapsed“ Routine weiter abgearbeitet. Wenn der Spieler die Meldung mit OK bestätigt, ist die Routine beendet und der Timer kann gestoppt werden.

Abbildung 1: Der „Countdown“-Timer läuft jede Sekunde ab, zeigt die Restlaufzeit an und verringert den Wert anschließend

Wenn wir Abb. 1 genauer betrachten, entdecken wir, dass der Timer auf Interval mit einer Laufzeit von einer Sekunde eingestellt ist. In diesem Modus startet der Timer immer wieder automatisch bis er explizit gestoppt. Das Ereignis „On start“ wird aber nur beim ersten Start ausgeführt.
Da durch die ständig erscheinende Message der Spieler die Zone „Satellitenstation“ nicht mehr auswählen kann, ist es wichtig dass er vor Erreichen des Labors weiß, wohin er laufen muss. Gegebenenfalls sollte man früh genug eine Wegbeschreibung („Laufe die Straße runter, dann rechts bis zum Baum“) anzeigen.
Um die Änderung wirksam werden zu lassen, ersetzen wir in der Labor- bzw. der Satellitenstationsannäherung beim „Start Timer“ / „Stop Timer“ Modul den „SatelliteLifeline“- durch den „Countdown“-Timer.
Abbildung 2: Beim Erreichen des Labors muss nun der Countdown-Timer gestartet werden Abbildung 3: Auch in der Satellitenstation wurde der alte Timer durch unseren Countdown ersetzt


Das urwigo Projekt zu diesem Tutorial als Download"