Not logged inClonkspot Forum
Forum Home Help Search Register Login
Up Topic Deutsch / Hilfestellung / [Gelöst] Arrays
- - By Serpens66 (More than 200 posts.) Date 05.02.2015 17:44 Edited 05.02.2015 17:53
Hey,
habt ihr eine Anleitung für Arrays parat?

Wie sehen die aus?  Ich erstelle ein Array mit:  testarray = CreateArray();
Wie mache ich nun Einträge? Bzw. wie wird auf Einträge zugegriffen? Sind die durchnummeriert, ich nehme mal an ja?  Kann ich den Einträgen Namen zuordnen, oder gibts nur feste Nummern? Und diesen festen Nummern kann ich dann Werte zuordnen? Und jede Nummer der ich noch keinen Wert zugeordnet habe, hat den Wert 0?

Aktuell möchte ich ein Regelwählerobjekt erstellen. Ich nehme das aus Hazard (Chooser) als Vorbild, da gibts ja schon Arrays die sich um das Meiste kümmern.  Ich möchte nun aber noch "variable Regeln" hinzufügen. Also z.b ob es Baustoffe beim Abriss zurückgeben soll und wenn ja wieviel % davon. Und ob es im Laufe des Spiels neblig werden soll, wenn ja wie oft und wie stark usw usw.
Ich habe also viele Regeln fertig, die aktuell nur über die Anzahl regelbar sind (also z.b 8 mal die baustoffrückgaberegel aktiviert sorgt für 100% Baustoffe zurück)
Dies möchte ich nun in einem Menü einstellbar machen.

Daher werden in einem Menü nun erstmal alle Regeln, die variabel einstellbar sind, aufgelistet. So. Nun wählt man eine Regel aus dieser Liste und es soll in ein individuelles Menü aufgehen (bis hier hab ich es bereits, jetzt kommt der Teil wo ich vllt Arrays brauche?), je nach gewählter Regel, wo man dann einstellen kann, wie oft/intensiv/wie viel usw.   Nachdem man es fertig eingestellt hat, wird dann eine globale Variable (oder besser ein  globales Array?) auf einen bestimmten Wert gesetzt.  Dieser Wert wird dann z.b im Abrissobjekt abgefragt, und je nach Wert eben die gewünschte Menge an Baustoffen wieder ausgespuckt.

Wenn ich jetzt ohne Array machen würde, würde ich das so regeln, dass nachdem eine variable Regel aus der Liste gewählt wurde, dann eine Funktion aufgerufen wird, welche je nach ID die mitübergeben wird, dann das individuelle Menü erstellt:

func EditVariabelRule(id dummy, int iSelection)
{
  var pClonk = GetCursor();
  // Menü aufmachen
  CreateMenu(GetID(), pClonk,0,0,"Nur eines wählbar! Variable Einstellung",0,2,0);
// nun für jede variable Regel die es gibt, einen Menüeintrag schreiben, denn diese müssen individuell sein...
if(dummy==PPT7)
  {
  AddMenuItem("Nichts zurück", "ChangeVariableRuleConfig",PPT7, pClonk, 0, 1,"Beim Abriss eines Gebäudes gibts nichts zurück");
  AddMenuItem("50% Chance nur Bausatz zurück", "ChangeVariableRuleConfig",PPT7, pClonk, 0, 2,"Beim Abriss eines Gebäudes gibts mit 50% Chance nur einen Bausatz wieder");
// usw...
  }
if(dummy==ABCD)
  {
  //...
  }
}

func ChangeVariableRuleConfig(id dummy, int iSelection)
{
if(dummy==PPT7)
{
global_rule_PPT7=iSelection;
}

if(dummy==ABCD)
{
global_rule_ABCD=iSelection;
}
//usw...

  // Geräusch
  Sound("Grab", 1,0,0,1);
  // menü wieder aufmachen
  EditVariabelRule(0, iSelection);
}


Im ChangeVariableRuleConfig Menü müsste ich z.b auch für jede einzelne Regel nun einen "if" Befehl machen... und natürlich für jede Regel eine eigene globale Variable definieren. Wobei ich den Namen der Variablen als  global_rule_ABCD geformt habe (hier fehlt der _Unterstrich, kA warum alle angezeigt werden, nur der nicht :D .. offensichtlich startet ein Unterstrich hier die kursivschrift und am ende des absatzes wird das wieder durch einen unterstrich beendet) . Gibt es in Clonk vllt eine Möglichkeit, das so zu verschmelzen?
Also dass ich anstelle der ganzen if Befehle einfach schreibe: "global_rule_"+dummy = iSelection , oderso?   Oder erübrigt sich die Problematik irgendwie durch Nutzung von Arrays?

Ansonsten wäre es natürlich evtl sinnvoller, nicht die Zahlen von 1 bis... den globalen Variablen zuzuordnen, sondern gleich eine Zahl, die für die Regel von Bedeutung ist. Also hier z.b gleich die Prozentzahl an Baustoffen, die es zurückgeben soll oderso. Mal schauen, wie ich das umsetze..
Parent - - By Pitri (More than 200 posts.) Date 05.02.2015 18:23 Edited 05.02.2015 21:43 Upvotes 1
Arrays sind eigentlich recht einfach zu handhaben. Wie von dir schon erwähnt brauchen sie eine Initialisierung, allerdings ist die Funktion CreateArray(iLength); nicht zwingend benötigt.
So initialisiere ich zB immer meine Arrays:
var aArray = [];

Um deine Frage zu klären, wie Arrays aussehen: Es ist ein Feld mit x Einträgen, welche in eckigen Klammern eingefasst sind und mit Kommas getrennt sind:
var aFibonacci = [1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987];
var aCrap = ["Hello World!",1337,CLNK,true,0x63a5ff]; //Achtung! Die meißten Programmiersprachen haben nur einen einzigen Datentyp pro Array. C4Script kann das.
var aMapDimensions = [LandscapeWidth(),LandscapeHeight()]; //Nimmt letztendlich die Rückgabewerte und speichert das in Zahlenform ins Array.


Accessen kannst du die Einträge dann ebenfalls über eckige Klammern, in denen ein Index mitgeliefert wird (stell dir ein Array wie eine Straße vor und die Einträge wie Häuser mit ihren Hausnummern). Achtung: Arrays beginnen mit 0, welches dem ersten Element zugeordnet wird.
aCrap[0]; //Gibt "Hello World!" zurück
aCrap[1]; //Gibt 1337 zurück
...;


Um die Werte zu setzen machst du das selbe wie beim Auslesen, nur dass du dem ganzen wie einer Variable den Wert zuweist. Operatoren wie x++; gehen hier auch, sofern man den richtigen Datentyp dafür benutzt.
aCrap[3] = false; //Ändert das Array in ["Hello World!",1337,CLNK,false,0x63a5ff]

Wenn du beim Schreiben einen Index benutzt, der größergleich der Arraygröße ist, wird dort ein neuer Eintrag angelegt.

Um dir noch ein wenig mehr Verständnis zur Handhabung zu bringen, habe ich einfach mal grob die CreateArray-Funktion nachgebaut. Diese erstellt ein Array und füllt dieses mit so vielen Nullen, wie über den Parameter angegeben wurde:
global func CreateArray(int iLength)
{
  var aArray = [];
  for(var i=0; i<iLength; i++)
    aArray[i] = 0;
  return aArray;
}


Das war mal kurz und knapp das Nötigste erklärt. Ich hoffe, es war hilfreich.
Parent - - By Serpens66 (More than 200 posts.) Date 05.02.2015 18:56 Edited 05.02.2015 19:04
Vielen Dank dafür, es hilft schonmal sehr, ein paar fragen habe ich dennoch :)

Also es ist ungefähr so wie von mir gedacht, es gibt feste Nummern, denen ich Werte zuordnen kann. Man kann aber nicht nur Zahlen zuordnen, sondern auch alle anderen Datentypen, das ist schonmal schön :)

"Wenn du beim Schreiben einen Index benutzt, der größergleich der Arraygröße ist, wird dort ein neuer Eintrag angelegt."
Wie sieht das also nun aus, wenn ich:
var aMapDimensions[5] = false;
schreibe?
wird das dann zu: [int,int,0,0,false] oder zu [int,int,false]? Ich tippe mal ersteres?

Und wenn ich ich:
aCrap[10];
aufrufe, bekomme ich dann einen Fehler, oder 0? Oder hängt das davon ab, wie ich das Array erstellt habe? Du hast ja die CreateArray Funktion beschrieben. Mir ist noch nicht ganz klar, wozu das mit Nullen gefüllt werden muss. Da ich stark davon ausgehe, dass aCrap[10]; eine Null zurückliefert, egal ob es zuvor mit var aCrap=[]; oder var aCrap=CreateArray(10); oder var aCrap=CreateArray(5); erstellt wurde, also wieviele Nullen zuvor eingefügt wurden. Oder etwa nicht?

Edit:
Der Grund wehsalb ich zuvor nie Arrays verwendet habe, bzw. sie auch garnicht genauer verstehen wollte, war dass ich davon ausgehe, dass ich ohne die Entstehung des Arrays nachzuschauen, nicht wissen kann, wofür nun z.b aCrap[1bis10] steht. Wenn ich stattdessen einfach 10 benannte variablen erstelle, dann weiß ich anhand der Namen immer, wofür das nun steht und wo ich es verwenden muss. Aber beim Array geht das nicht.  Wie mir scheint hatte ich mit meiner Annahme recht, oder? 
Dennoch gibt es so wie jetzt Situationen, wo Arrays sehr sinnvoll sind und ich bin jetzt auch bereit, sie anzuwenden. Aber übersichtlich für Menschen, die ein Skript verstehen wollen, ist das nicht wirklich. Da sind 100 Variablen Namen irgendwie übersichtlicher, auch wenn 100 stück natürlich eig trzd unübersichtlich ist... aber besser als ein Array mit 100 einträgen..zumindest um das Skript zu verstehen... zum schreiben ist ein array natürlich einfacher :D
Parent - - By Pitri (More than 200 posts.) Date 05.02.2015 19:47 Edited 05.02.2015 19:50

>var aMapDimensions[5] = false;
>wird das dann zu: [int,int,0,0,false] oder zu [int,int,false]? Ich tippe mal ersteres?


Theoretisch ersteres, aber nicht ganz. Die nicht benutzten Einträge enthalten nichts: [int,int,,,false] Es ist nichts aber wird wie 0 gehandhabt. Also ist es wie eine nicht initialisierte Variable.

>aCrap[10];
>bekomme ich dann einen Fehler, oder 0?


Du kriegst eine 0 entgegengeschmissen. Mit der Initialisierung des Arrays hat das nichts zu tun. Es muss halt ein Array sein, weiter nichts (um das weiter auszuführen: Natürlich gibt es einen Fehler, wenn du versuchst, ein Nicht-Array mit der Arraymethode zu accessen).

>Du hast ja die CreateArray Funktion beschrieben. Mir ist noch nicht ganz klar, wozu das mit Nullen gefüllt werden muss.


Die CreateArray-Funktion erstellt ein Array mit einer bestimmten Länge, obwohl das eigentlich komplett unnötig ist. Ein mit CreateArray(); erstelltes Array kann auch nicht mehr, als ein leeres Array. Die füllung mit Nullen ist übrigens nur dafür da, dass die Einträge angelegt werden. Meines erachtens kann man nicht dynamisch ein Array mit lauter leeren Einträgen erstellen.
var aArray = [,,,,,,,,,,,,,,,,,,,,,]; geht zwar und hat leere Einträge, ist aber nicht dynamisch machbar. Deshalb die Lösung mit den Nullen. Klar könnte man auch nur den letzten Eintrag mit einer Null bestücken, aber das wäre eine Sache, die fänden viele Leute (mich mit eingeschlossen) unschön.

>Der Grund wehsalb ich zuvor nie Arrays verwendet habe, [...] war dass ich davon ausgehe, dass ich ohne die Entstehung des Arrays nachzuschauen, nicht wissen kann, wofür nun z.b aCrap[1bis10] steht.


Die Arrayeinträge selbst haben keine besondere Bedeutung. Du kannst damit machen, was du willst, interpretieren, wie du es willst. Das ist dir überlassen. Für Arrays sollte man halt schon Dinge nehmen, die was miteinander zu tun haben und nicht einfach einen zufälligen Cluster Variabeln zusammenfassen (mit einer Ausnahme, siehe weiter unten).

>Wenn ich stattdessen einfach 10 benannte variablen erstelle, dann weiß ich anhand der Namen immer, wofür das nun steht und wo ich es verwenden muss.


Das schon, aber ein Array ist dafür dynamisch. Die von mir am meißten benutzte Möglichkeit für Arrays ist zum Beispiel die Speicherung von Relaunches. Ich habe mein Relaunch-Array und die Spielernummer ist damit effektiv der Index. Und da ist es egal, wie viele Spieler joinen. Wenn du die sache mit normalen variablen machst, dann kannst du nur maximal so viele Spieler gleichzeitig managen, wie du Variabeln angelegt hast.

Arrays können aber auch in anderen Dingen nützlich sein. Stell dir vor, du hast eine Funktion, die eine enorme Menge an Daten annimmt, diese durchnudelt und dann ein Ergebnis ausspuckt. Blöderweise hat C4script eine Maximalmenge an Parametern, die man einer Funktion mitgeben kann (10 an der Zahl, für die, die es nicht wissen). Das kann man mit Arrays lösen. Anstatt für jeden Eintrag einen Parameter zu nehmen, kann man ein Array mit Daten füllen und an die Funktion übergeben. Das verbraucht nur einen einzigen Parameter. Ich benutze sowas eigentlich so gut wie nie, aber wenn es dank Parameterlimit wirklich nötig ist, greife ich darauf zurück. Ebenso werden Arrays auch verwendet, wenn eine Funktion mehrere Rückgabewerte hat, siehe FindObjects();

>Da sind 100 Variablen Namen irgendwie übersichtlicher


Das ist alles eine Frage der Übung, muss ich dir da sagen. Wenn man lange genug mit Arrays gearbeitet hat, kann man auch ohne Probleme mit mehrdimensionalen Arrays arbeiten, ohne den Überblick zu verlieren. Sei einfach zuversichtlich und lass dich auf Arrays ein. :)
Parent - - By Serpens66 (More than 200 posts.) Date 05.02.2015 21:27
Alles klar, danke dir :)

Zu meinem im Eingangspost erwähnten Projekt.... können Arrays auch global sein und wenn ja wie? Von Natur aus sind sie vermutlich local?

Ich muss ja nun für jede der Regeln eine globale Variable festlegen, welche dann in den betroffenden Objekten abgefragt wird... Alternativ könnte man auch ein Array dafür machen, wo dann ein Platz im Array für eine Regel steht. Dadurch ist das ganze natürlich nicht mehr nachvollziehbar (z.b. aArray[0] ist dann der Wert für die Baustoffe und aArray[1] der Wert für eine andere Regel usw. ), aber es wäre schneller zu schreiben. Sollte ich das so machen, oder fällt dir ein besserer Weg ein?

Und dann gibts ja noch das Problem der Wertzuweisung durch die Funtion ChangeVariableRuleConfig. Ich möchte da ungerne nochmal für jede Regel einen eigenen if Befehl machen. Mit Variablen muss ich das aber, um den unterschiedlichen Variablen einen Wert zuzuordnen (außer es gibt eine Möglichkeit um einen Namen kombiniert zusammenzusetzen, weil der Name der globalen Variablen ja bis auf die ID immer derselbe ist?).
Wenn ich nun ein Array nutze, muss ich sowohl den Platz im Array, als auch den Wert der zugewiesen werden soll, in der Funktion angeben... noch sehe ich keinen Weg, wie das dann ohne if Befehle gehen könnte... du?
Parent - - By Pitri (More than 200 posts.) Date 05.02.2015 23:21 Edited 05.02.2015 23:54

>können Arrays auch global sein und wenn ja wie? Von Natur aus sind sie vermutlich local?


Arrays sind eigentlich nichts anderes als normale variabeln (nur etwas anders initialisiert). Das bedeutet, dass var, local und static auch für diese funktioniert.

>lternativ könnte man auch ein Array dafür machen, wo dann ein Platz im Array für eine Regel steht. Dadurch ist das ganze natürlich nicht mehr nachvollziehbar (z.b. aArray[0] ist dann der Wert für die Baustoffe und aArray[1] der Wert für eine andere Regel usw. ), aber es wäre schneller zu schreiben. Sollte ich das so machen, oder fällt dir ein besserer Weg ein?


Man könnte das Array allerdings auch im Format [[C4ID,value],[C4ID,value],[C4ID,value],...] machen. Da bei jedem Eintrag die ID mitgeliefert wird, braucht man nichtmal konstante Indices für die einzelnen Regeln, sondern kannst schnell mit einer for-schleife durchgehen und den richtigen eintrag raussuchen (ist zwar etwas komplizierter als stumpfes array[index] = value;, aber dynamischer).
Falls die Frage aufkommt: Man accesst die ID über array[index][0] und den wert über array[index][1]. Und ja, das ist ein zweidimensionales Array.
Parent - By Serpens66 (More than 200 posts.) Date 06.02.2015 11:55
ah gut, also je nach dem wie ich "aRules" bei Einführung definiert habe, var, local oder static,  verhält es sich dann auch, gut :)

Ich hab mich in letzter Zeit mit der Programmiersprache "Python" beschäftigt und kann schon ein bisschen was :) Jedenfalls entspricht ein Array offensichtlich einer Liste und diese kann selbst also auch Listen enthalten :)

Vielen Dank auch für dein Beispiel im folgenden Post :) Ist eine sehr gute Idee, mit dem zweidimensionalen Array.
Aktuell tendiere ich dennoch zu den Hashtabellen, die Luchs vorgeschlagen hat, da es im Prinzip dasselbe ist, wie dein Vorschlag, nur dass ich die Einträge direkt über den Key abfragen kann, und brauche dadurch also keine for schleife mehr :)
Parent - - By Pitri (More than 200 posts.) Date 06.02.2015 00:01 Edited 06.02.2015 00:06
Argh, ich habe garnicht gesehen, dass die Funktion ChangeVariableRuleConfig() im Eingangspost erklärt wurde. Ich hatte das nur angelesen und gedacht, du willst nur infos über Arrays haben. Pack ich das einfach in einen neuen Post für eine weitere Benachrichtigung.

Okay, wenn wir jetzt den von mir oben vorgeschlagenen Ansatz nehmen, dann lässt sich das auch recht einfach lösen, indem wir das array durchsuchen und an der richtigen stelle den Eintrag verändern (nehmen wir an aRules ist als static deklariert):
func ChangeVariableRuleConfig(id dummy, int iSelection)
{
  var iIndex=GetLength(aRules); //Standardwert für den Fall, dass kein Eintrag dafür vorhanden ist
  for(var i=0; i<GetLength(aRules))
    if(dummy == aRules[i][0])
    {
      iIndex = i;
      break;
    }

  aRules[iIndex] = [dummy,iSelection];

  // Rest der Funktion
  Sound("Grab", 1,0,0,1);
  EditVariabelRule(0, iSelection);
}


Damit hast du dann ein Array, die für jede Regel einen Wert beinhaltet. Den Wert kriegst du dann hiermit wieder:
var iResult;
for(var i=0; i<GetLength(aRules); i++)
  if(aRules[i][0] == <C4ID>) //Hier ID der gewünschten Regel bei Abfrage einfügen
  {
    //Wert rausschreiben und Schleife abbrechen
    iResult = aRules[i][1];
    break;
  }
Parent - - By Luchs (More than 1000 posts.) Date 06.02.2015 09:31
Bei  deiner ersten for-Schleife fehlt der dritte Teil.

Der bessere Ansatz hier wäre eine Hashtabelle, da die die Komplexität von O(n) auf O(1) verringert. Isilkor hat da mal eine implementiert: https://ccan.de/cgi-bin/ccan/ccan-view.pl?a=view&i=4708 (die Kommentare sind toll)
Parent - By Serpens66 (More than 200 posts.) Date 06.02.2015 11:59 Edited 06.02.2015 12:09
Danke :) Das gefällt mir doch schonmal sehr gut :) In Python entsprechen diese Hastabellen also den Dictionaries, das ist sehr gut, dass ich die Einträge dann über den Key abfragen kann :)
Ich werds mal ausprobieren ;)

Allerdings... fällt mir gerade ein, dass ich mir überlegt hatte für jede Regel unterschiedliche Einstellungen zu ermöglichen. Soll heißen in dem Fall von Baustoffen halte ich es für am sinnvollsten, wenn ich direkt einen Prozentsatz angebe. Also "Mit welcher Chance soll es einen Bausatz zurückgeben?" -> Zahlen von 0 bis 100 in 10er Schritten einstellbar.   Und zusätzlich in derselben Regel (-> dieselbe ID) "Wieviel Prozent der Bausstoffe soll es zurückgeben?" Und diese zwei Prozentzahlen sollen dann iwo eingespeichert und übergeben werden...

Das 2dimensionale Array wäre dafür nicht so gut nutzbar, da ich vermutlich nicht mehr die ID in die Arrays aufnehmen kann.
Aber mit Hashtabellen sollte das gehen :)  Dann benenne ich sie nicht nach ID's sondern einfach nach "Bausatz" und "Baustoffe" und ordne die entsprechenden Werte zu :)
Parent - - By B_E (More than 200 posts.) Date 06.02.2015 10:37
Deutlich einfacher, flexibler und schöner als hardcodierte IDs/Bedeutungen in Arrays/Hashtabellen wären Callbacks in den Regeln, die entweder direkt das Menü selbst zeichnen oder über eine Art API die Anzahl und Art der änderbaren Werte an den Chooser übergibt:

static CHSR_CUSTOM_TITLE = 0;
static CHSR_CUSTOM_DESC = 1;

func EditVariabelRule(id dummy, int iSelection)
{
  var pClonk = GetCursor();
  // Menü aufmachen
  CreateMenu(GetID(), pClonk,0,0,"Nur eines wählbar! Variable Einstellung",0,2,0);
  var aPossibilities = DefinitionCall(dummy, "GetChooserPossibilities");
  for(var j = 0; j < GetLength(aPossibilities), j++) {
    AddMenuItem(aPossibilites[j][CHSR_CUSTOM_TITLE], "ChangeVariableRuleConfig",dummy, pClonk, 0, 1, aPossibilites[j][CHSR_CUSTOM_DESC]);
  }
}


Und in der Regel dann

public func GetChooserPossibilities() {
  entries = [];
  entries[0] = [];
  entries[0][CHSR_CUSTOM_TITLE] = "Nichts zurück";
  entries[0][CHSR_CUSTOM_TITLE] = "50% Chance nur Bausatz zurück";
  entries[1] = [];
  entries[1][CHSR_CUSTOM_TITLE] = "...";
  ...
  return entries;
}
Parent - By Serpens66 (More than 200 posts.) Date 06.02.2015 12:07 Edited 06.02.2015 12:10
das ist eine sehr interessante Idee... leider noch etwas zu komplex, als dass ich sie auf anhieb komplett nachvollziehen kann ^^

Nur inwiefern wäre diese Variante einfacher, als Hashtabellen?  Bei den Hashtabellen brauche ich ja wirklich nur einen Key zu nehmen und einen Wert zuzuordnen. Und genauso einfach kann ich den Wert in der betroffenden Regel dann einfach wieder auslesen :)
Parent - - By Nachtschatten (More than 50 posts.) Date 06.02.2015 18:28
Im Clonk-Wiki gibt es dazu Erklärungen, wie etwa ein praktisches Beispiel und eine allgemeine Sammlung von Befehlen und Tipps.
Parent - By Serpens66 (More than 200 posts.) Date 06.02.2015 20:30
danke :)
Up Topic Deutsch / Hilfestellung / [Gelöst] Arrays

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill