Geschrieben um 01:03 am 05.08.2006 | Zitat | Editieren | Löschen | |
Mitglied Bachelor Gumby Beiträge: 60 | Hallo miteinander! Nach längerer Abstinenz hab ich vor einigen Tagen (Wochen? Die Zeit rennt...) wieder begonnen, mich mit Inform & co. auseinander zu setzen. Und weil das letzte Problem schon über nen Monat her ist, dachte ich mir: Beleben wir das Forum doch ein wenig. ;-) Der unten stehende Glulx-Code funktioniert. Nicht immer. Ich wäre euch dankbar, wenn ihr mir sagen könntet, wo das Problem liegt, weil der Compiler sich auch nicht beschwert und die auftauchenden "Programming errors" (s.u.) mir mit meinen bescheidenen Kenntnissen nicht weiter helfen. Zunächst mal der Code. Benutze die unglklib.h-Extension und die deutsche Library Release 22.
Für alle, die unglklib.h nicht haben und sie sich nicht extra downloaden wollen, folgen hier die benutzten Routinen "PrintTheArray()" und "GetPlayersInput()" mit allem drum und dran:
Wenn man das Spiel startet und anfängt, Eier zu produzieren, läuft zunächst alles wie geplant. Dann passiert irgendwann (Zeitpunkt variiert, aber es scheint nie das erste Ei zu sein) Folgendes: Glulxe:
Du ziehst am Hebel. Es macht "klack" und ein Ei rollt aus der Maschine. [ Programming error: Egg_19 (object number 139239) has no property age to write ] Wie willst du dieses Ei nennen? Gibt man nun trotzdem einen Namen ein, kann man sich auf eine Flut von verschiedenen Fehlermeldungen gefasst machen. Kompiliert man das Spiel im Debug-Mode, ändert sich an dem Ereignis oben nichts, aber von Anfang an bekommt man nach Benennung des Eis 13 Mal das zu lesen: Glulxe debugged:
Du ziehst am Hebel. Es macht "klack" und ein Ei rollt aus der Maschine. Wie willst du dieses Ei nennen? sam [ Programming error: tried to write outside memory using --> ] [ Noch zwölfmal dieselbe Meldung] Abstürzen tut das Spiel allerdings nach wie vor nur bei der ersten Fehlermeldung. Warum verliert das Ei irgendwann seine Klasse? Ich hoffe, ihr könnt mir da weiter helfen... aber bisher wurde meine Hoffnung eigentlich nie enttäuscht. :-) Grüße, Clive |
Geschrieben um 20:34 am 05.08.2006 | Zitat | Editieren | Löschen | |
Mitglied Bachelor Gumby Beiträge: 60 | Nach ein bisschen Herumprobieren hab ich herausgefunden, dass die extra-Fehlermeldung im debug-Modus wohl von der Zeile
ausgelöst wird. Scheint aber keine negativen Auswirkungen auf das Spiel zu haben. Anders sieht es mit der Zeile
aus. Ich hab den darauffolgenden Code der Routine mal eingepackt in eine if-Anweisung:
Die verhindert zumindest, dass Glulxe abstürzt. Und oh Wunder, die Anzahl der Eier hat sich in dem Fall nicht verändert. Folglich stimmt wohl etwas mit der Create()-Routine nicht? Das Problem kann man damit zwar leicht mit einer do-Schleife umgehen, aber die Frage bleibt: Warum funktioniert Create() hier mal und mal nicht? Na, ich werd mal weiter testen. Sehr komisch die Sache. |
Geschrieben um 00:07 am 06.08.2006 | Zitat | Editieren | Löschen | |
Mitglied Bachelor Gumby Beiträge: 60 | Da bisher noch keine Lösung eingetrudelt ist, kann ich ja mal den Stand meiner "Nachforschungen" kundgeben, vielleicht löst das ja bei irgendwem nen Geistesblitz aus, man kann nie wissen. Zur Vermeidung des Fehlers hab ich die Eierlegen()-Routine mit ein paar "Sicherheiten" ausgestattet:
Die Schleife löst das Problem nämlich leider nicht. Wenn es auch beim zweiten Versuch nicht gelingt, das Ei zu erschaffen, hängt Glulxe sich auf. Wahrscheinlich, weil die Schleife endlos weiter läuft, ohne jemals ein Ei erschaffen zu können. Darum hab ich das in meinem Code abgefangen. Selten (bisher nur 2 Mal von sehr vielen Versuchen) schaffe ich es, alle 20 Eier zu benennen, ohne dass es ein Problem gibt. Meistens kommt irgendwann am Anfang die Nachricht, dass die Maschine zwei Versuche benötigt hat. Sobald es soweit ist, lässt auch der komplette Fehlschlag nicht lange auf sich warten, d.h. die Maschine "geht kaputt" und kann auch in nachfolgenden Zügen keine Eier mehr kreieren, bis man das Spiel neustartet. Leider hab ich bisher auch die Create()-property/routine (?) nirgendwo in den libraries finden können... ich glaub nun stehe ich endgültig auf dem Schlauch. |
Geschrieben um 10:18 am 06.08.2006 | Zitat | Editieren | Löschen | |
Mitglied Bachelor Gumby Beiträge: 61 | Clive:
Ich mach sowas nie, deswegen kenn ich mich da nicht aus, aber ist Create nicht eine property, die du selber definieren musst? |
Geschrieben um 15:59 am 06.08.2006 | Zitat | Editieren | Löschen | |
Mitglied Bachelor Gumby Beiträge: 60 | Selber definieren? Wie meinst du das? In DM4 3.11 wird .create erklärt und ist normalerweise ebenso einfach anzuwenden. Ich glaube ich habe den Fehler aber entdeckt. Ich muss noch ein bisschen testen, aber bisher klappt jetzt alles wie geplant. Der Wurm liegt da begraben:
Das sind acht Platzhalter, die Konstante NEWEGG_NAME_LEN lässt aber 15 Buchstaben zu. Wenn ich nun einem Ei einen Namen mit mehr als acht Buchstaben gebe, wird das darauffolgende Ei erst beim zweiten Mal erfolgreich kreiert. Trotzdem wird auch die erste versuchte Kreation als erschaffene Instanz gewertet und somit ist die Maschine nicht erst nach 20 Eiern leer sondern nach 20 Versuchen, ein Ei zu erschaffen. Warum immer nur das nächste Ei davon betroffen ist, kann ich mir allerdings nicht erklären. Hat da jemand von den Hobby-Programmierern vielleicht nen Erklärungsansatz? |
Geschrieben um 08:43 am 07.08.2006 | Zitat | Editieren | Löschen | |
Mitglied Prof Gumby Beiträge: 634 | Sophie:
Man kann einen eigenen Konstruktor definieren, der dann auch create heißt. Aber auch, wenn der nicht definiert ist, kann man obj.Create() aufrufen, das dann ein generisches Objekt erzeugt. Vorausgesetzt, man hat oben in der Klassendeinition eine Anzahl mit Klammern angegeben, natürlich. Clive:
Das ist kein Fehler. Einträge in Properties, auch wenn es Felder sind, sind Wörter, also zwei Bytes im z-Code und vier Bytes in Glulx. Da Zeichen in beiden Fällen nur in Bytes abgelegt werden - ZSCII in z-Code und ISO-8859-1 in Glulx - kannst du unter Glulx mit 8 Wörtern 32 Zeichen ablegen. Die 15 kommt gewiss aus einem z-Code-Beispiel und hat ein Null-Byte angehängt, um das Ende des Strings zu kennzeichnen. (Der featureless-cube-Code?) In deinem Code machst du aber folgendes:
Wichtig sind hier die Pfeile. Du greifst auf Wörter zu mit -->. Die Routine PrintTheArray benutzt korrekterweise einfache Pfeile ->, die auf Bytes zugreifen. Also muss es heißen:
Der erste Pfeil sollte, glaube ich, doppelt sein, denn die Anzahl der Bytes in einem Inform-Buffer wird durch ein Wort gekennzeichnet. Wenn man ein normales Feld überschreibt, macht man damit die nachfolgenden Felder kaputt. Wenn man über die Grenzen einer Property hinaus schreibt, kommt der ganze Property-Table durcheinander und Inform, kann Werte nicht mehr Properties zuordnen. In deinem Fall scheint sogar der Property Table des nachfolgenden Objekts kaputt geschrieben worden zu sein, daher tritt der Fehler erst beim zweiten Ei auf. |
Geschrieben um 20:33 am 07.08.2006 | Zitat | Editieren | Löschen | |
Mitglied Bachelor Gumby Beiträge: 60 | Martin: Die 15 kommt gewiss aus einem z-Code-Beispiel und hat ein Null-Byte angehängt, um das Ende des Strings zu kennzeichnen. (Der featureless-cube-Code?) Die 15 kommt auch aus der unglklib.h :
Denn eigentlich will ich ja auch keine Eier benennen Wobei mir jetzt auch auffällt, dass der Beispiel-Array nur einen einfachen Pfeil hat... ich schätz mal, ich bin beim gleichzeitigen Nachschlagen im DM4 oder dergleichen etwas durcheinander gekommen. Martin: Wichtig sind hier die Pfeile. Du greifst auf Wörter zu mit -->. Die Routine PrintTheArray benutzt korrekterweise einfache Pfeile ->, die auf Bytes zugreifen. ... Der erste Pfeil sollte, glaube ich, doppelt sein, denn die Anzahl der Bytes in einem Inform-Buffer wird durch ein Wort gekennzeichnet. Ahh.... und ich dachte die Pfeile kennzeichnen nur den Typ des Arrays. Das Problem hierbei ist nur, dass ich bei einem string-Array keinen -> Zuweiser verwenden kann (in dem Fall werden die letzten drei Buchstaben abgeschnitten....), wovor mich auch der Compiler warnt. Definiere ich nun einen byte-Array, kann ich dagegen nicht mehr die Länge des Arrays abrufen. Wahrscheinlich der Grund, warum der Autor der unglklib.h eine Konstante als Hilfe verwendet hat. Bei den "featureless white cubes" wird auch eine Hilfskonstante in Form einer Property verwendet, um die Länge des Array festzuhalten... na, vielleicht ist es ja einfach die bessere Methode. g EDIT: Das bezieht sich auf die "light"-Version der cubes im DM4 (seh grad, dass es im richtigen Spiel etwas anders ist) Martin: In deinem Fall scheint sogar der Property Table des nachfolgenden Objekts kaputt geschrieben worden zu sein, daher tritt der Fehler erst beim zweiten Ei auf. Ah, und darum klappt das Kreieren des dritten Eis auch wieder... danke! Wahrscheinlich ist dieses Vermischen von byte- und string-Array auch der Grund, warum mein nachfolgender Versuch, eine parse_name-Routine für die Eier zu schreiben (diesmal nach dem Vorbild der "featureless white cubes", von mäßigem Erfolg gekrönt war: EDIT: Das bezieht sich auf die "light"-Version der cubes im DM4 (seh grad, dass es im richtigen Spiel etwas anders ist) Glulxe Test: Du siehst hier auch Tanja und Sonja.
Wen meinst du, Tanja oder Sonja? Deine Antwort: Sonja Wen meinst du, Tanja oder Sonja? Denn so hat die Routine zwar die Länge abgleichen können, aber nicht sämtliche Buchstaben Wobei z.B. "Maren" (auch 5 Buchstaben) nicht als "Tanja" erkannt wird. Und "Anne" wird auch nicht mit "Anna" gleichgesetzt, wohl aber "Enna". Ich probier mal eine Grunderneuerung meiner Arrays. Vielen Dank, Martin! Mal wieder was dazugelernt. Was mich allerdings noch wundert ist dann, dass die Namenszuteilung selbst einwandfrei funktioniert, wenn ich self.&called um genug Platzhalter erweitere, obwohl ich mit --> auf ein Wort statt ein Byte zugreife. |
Geschrieben um 08:22 am 08.08.2006 | Zitat | Editieren | Löschen | |
Mitglied Prof Gumby Beiträge: 634 | Clive:
Die Pfeile kennzeichnen den Modus, mit dem auf das Array zugegriffen wird. Bei der Definition geben sie an, wieviele Bytes freigahelten werden. Clive:
Doch. Es ist legal, auf Wort-Arrays mit -> und auf Byte-Arrays mit --> zuzugreifen. Der Autor muss nur wissen, was er macht.
Beide Arrays haben (in Glulx) 48 Bytes und 12 Wörter, und ich kann natürlich die Bytes eines Worts einzeln "extrahieren". Das eventuelle Gemeckere des Compilers kannst du mit Hilfsvariablen umgehen:
Oder natürlich eigene Routinen schreiben, die dem Compiler die Natur der Felder verschleiern:
(Ein Feld ist nichts anderes, als eine Adresse im Speicher, ab der die angegebene Anzahl von Bytes freigehalten wird. arr->i bedeutet: Hole mir das byte an der Stelle arr + i; arr-->i bedeutet: Hole mit das Wort an der Stelle arr + 4*i. Deshalb beginnen die Indizes auch immer mit Null, nucht mit Eins.) Die ganze Array-Sache ist nicht ganz einfach, besonders weil der Z-Code und Glulx selbst Mischfelder, also Felder, auf die man mit -> und --> zugreifen muss, verwenden. Die Faustregel ist, immer Wort-Felder zu verwenden. Lediglich bei Zeichenketten (die allerdings häufig vorkommen), ist es sinnvoll, Bytes zu verwenden, weil sie einfach weniger Platz benötigen. Und dann muss man aufpassen, ob man ein Byte-Array (n Werte), einen String (n + 1 Werte wie bei Pascal, das erste Byte ist die Länge der belegten Zeichen im String) oder einen Buffer (n + 4 Werte, das erste Wort gibt die Anzahl der belegten Bytes, die folgen, an) verwendet. Die Glulx-eigenen Routinen verwenden meist Buffer. Clive:
Oh! Hier musst du aufpassen, welche Daten du übertragen willst. Wenn in newegg_name-->0 die Länge des Strings steht, liegen die Daten natürlich in den Bytes ab newegg_name->(i+2):
(Dieser Code nimmt an, dass der Name und dessen Länge im Objekt in getrennten Properties gespeichert werden.) Wenn du wirklich wissen willst, ob alle Buchstaben korrekt übertragen werden, könntest du in der Schleife die kopierten Buchstaben ausgeben lassen. Dann siehst du schnell, was noch fehlt. Clive:
Properties sind auch Felder, auf deren Adresse man mit .& und auf deren Länge (in Bytes) man mit .# zugreifen kann. Nur: Hier gibt es nicht die verschiedenen Arten von Feldern wie bei einer Array-Definition. Property-Felder sind immer Wort-Felder, und sie werden immer durch die Initialisierung mit Werten definiert. Die Größer der Felder ist statisch, daher muss man sich mit dem Trick, acht Wort-Platzhalter zu definieren, behelfen, um für jedes Ei oder jeden Featureless Cube WORDSIZE * 8 Bytes zur Verfügung zu haben, wobei unter Glulx WORDSIZE vier ist. (Dass man mit .# die Anzahl der Bytes erhält, ist natürlich für eine Art von Feldern, die eigentlich nur Wörter sein kölnnen und nur durch Tricken als Bytes angesehen werden können, etwas blöd.) |
Geschrieben um 23:02 am 09.08.2006 | Zitat | Editieren | Löschen | |
Mitglied Bachelor Gumby Beiträge: 60 | Vielen Dank, Martin! Ich werd mal so ein bisschen experimentieren und ausprobieren, ob ich auch alles verstanden hab. |