Question about data structure and checking set elements

Discuss macro implementations, ask for macro help (to share your creations, see User Creations, probably either Campaign Frameworks or Drop-in Resources).

Moderators: dorpond, trevor, Azhrei, giliath, jay, Mr.Ice

Post Reply
Barlie
Kobold
Posts: 17
Joined: Wed Nov 07, 2012 2:41 pm

Question about data structure and checking set elements

Post by Barlie »

I guess I have 2 questions here. The first, is there a function that allows you to check and see if something is an element of an array?

The second question is about a data structure I'm thinking about and I'd like to know if it exists. If it doesn't, is there a way to get the same effect? Here's what I want:

Code: Select all

{"1,2,3":5, "4,5,6,7":15, "8":35,...}
So lets say I have a numeric value, I want to run a foreach() loop to check every "key" in the above object and see if my numeric value is an element of any of the "keys". If it is, I want the value of that "key" to be returned. Is this possible?

User avatar
wolph42
Winter Wolph
Posts: 9999
Joined: Fri Mar 20, 2009 5:40 am
Location: Netherlands
Contact:

Re: Question about data structure and checking set elements

Post by wolph42 »

1. I don't understand the question can you give an example.
2. Yes its called a table. You can create them in MT. ALternatively you could use Wiki: switch()

User avatar
CoveredInFish
Demigod
Posts: 3104
Joined: Mon Jun 29, 2009 10:37 am
Location: Germany
Contact:

Re: Question about data structure and checking set elements

Post by CoveredInFish »

Its possible. Dont have time to go in detail but you'll probably need Wiki: json.fields() and Wiki: json.contains() / Wiki: listContains().

I really doubt its efficient though. Much easier would probably be to reorder your data like this

Code: Select all

{"1":5, "2": 5,  "3":5, "4":15, "5":15, "6":15, "7":15, "8":35,...}
Even though I have to handle a bigger data element I have direct access and the routines to look up data are shorter, easier=more failsafe and probably faster.

User avatar
aliasmask
RPTools Team
Posts: 9024
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: Question about data structure and checking set elements

Post by aliasmask »

Barlie wrote:I guess I have 2 questions here. The first, is there a function that allows you to check and see if something is an element of an array?

The second question is about a data structure I'm thinking about and I'd like to know if it exists. If it doesn't, is there a way to get the same effect? Here's what I want:

Code: Select all

{"1,2,3":5, "4,5,6,7":15, "8":35,...}
So lets say I have a numeric value, I want to run a foreach() loop to check every "key" in the above object and see if my numeric value is an element of any of the "keys". If it is, I want the value of that "key" to be returned. Is this possible?
Yeah, there's nothing wrong with that structure, but depending on the data values and number of values there's probably a more efficient way of doing it. It may help to get a little perspective on the data you're looking up and what you want to get out of it.

Barlie
Kobold
Posts: 17
Joined: Wed Nov 07, 2012 2:41 pm

Re: Question about data structure and checking set elements

Post by Barlie »

wolph42 wrote:1. I don't understand the question can you give an example.
It looks like listContains() or listFind() might do what I want. Lets say I have an array of numbers (1, 2, 3,....50), and I want to check if say, 21 is in that array. The function doing it would return 1 if 21 is in that array, or 0 if it's not.
aliasmask wrote: Yeah, there's nothing wrong with that structure, but depending on the data values and number of values there's probably a more efficient way of doing it. It may help to get a little perspective on the data you're looking up and what you want to get out of it.
I'm building a character sheet for 2nd edition AD&D. What I want from it is for all values dependent on ability scores to be filled out automatically once the ability scores have been filled out on a token's properties tab. For the strength ability score, the table looks like this: http://www.ancientscrossroads.com/adnd_ ... _table.htm. Focusing in on the "Wgt. Allow" column, The values in that column are the maximum weight that a character can carry before he experiences some level of encumbrance (which effects his movement rate, fatigue, etc.). The level of encumbranced by the character depends on how much weight he is carrying above the "Wgt. Allow" value for his strength score.

So for example, if a character has a strength score of 15, his "Wgt. Allow" score is 55, and his encumbrance chart would look like this:

Unencumbered: <56
Light : 56-85
Medium : 86-115
Heavy : 116-145
Severe : >145

I was planning on first using a data structure as in my first post to a list of strength score lists paired with "Wgt. Allow" scores. I'd then use that to generate an encumbrance table.
CoveredInFish wrote:Its possible. Dont have time to go in detail but you'll probably need Wiki: json.fields() and Wiki: json.contains() / Wiki: listContains().

I really doubt its efficient though. Much easier would probably be to reorder your data like this

Code: Select all

{"1":5, "2": 5,  "3":5, "4":15, "5":15, "6":15, "7":15, "8":35,...}
Even though I have to handle a bigger data element I have direct access and the routines to look up data are shorter, easier=more failsafe and probably faster.
I considered doing it that way until I ran into the exceptional strength scores. Those are the strength scores between 18 and 19 written as 18/01, 18/02, 18/03,...,18/100. Is it better to reorder the data that way considering those exceptional strength scores?

User avatar
aliasmask
RPTools Team
Posts: 9024
Joined: Tue Nov 10, 2009 6:11 pm
Location: Bay Area

Re: Question about data structure and checking set elements

Post by aliasmask »

I would put all the data in to a structure like this:
... DATA STRUCTURE ...

Code: Select all

{
    "1":     {
        "str": 1,
        "hitProb": -5,
        "damageAdj": -4,
        "weight": 1,
        "maxPress": 3,
        "openDoors": 1,
        "bendLift": 0,
        "note": ""
    },
    "2":     {
        "str": 2,
        "hitProb": -3,
        "damageAdj": -2,
        "weight": 1,
        "maxPress": 5,
        "openDoors": 1,
        "bendLift": 0,
        "note": ""
    },
    "3":     {
        "str": 3,
        "hitProb": -3,
        "damageAdj": -1,
        "weight": 5,
        "maxPress": 10,
        "openDoors": 2,
        "bendLift": 0,
        "note": ""
    },
    "5":     {
        "str": "4-5",
        "hitProb": -2,
        "damageAdj": -1,
        "weight": 10,
        "maxPress": 25,
        "openDoors": 3,
        "bendLift": 0,
        "note": ""
    },
    "7":     {
        "str": "6-7",
        "hitProb": -1,
        "damageAdj": 0,
        "weight": 20,
        "maxPress": 55,
        "openDoors": 4,
        "bendLift": 0,
        "note": ""
    },
    "9":     {
        "str": "8-9",
        "hitProb": 0,
        "damageAdj": 0,
        "weight": 35,
        "maxPress": 90,
        "openDoors": 5,
        "bendLift": 1,
        "note": ""
    },
    "11":     {
        "str": "10-11",
        "hitProb": 0,
        "damageAdj": 0,
        "weight": 40,
        "maxPress": 115,
        "openDoors": 6,
        "bendLift": 2,
        "note": ""
    },
    "13":     {
        "str": "12-13",
        "hitProb": 0,
        "damageAdj": 0,
        "weight": 45,
        "maxPress": 140,
        "openDoors": 7,
        "bendLift": 4,
        "note": ""
    },
    "15":     {
        "str": "14-15",
        "hitProb": 0,
        "damageAdj": 0,
        "weight": 55,
        "maxPress": 170,
        "openDoors": 8,
        "bendLift": 7,
        "note": ""
    },
    "16":     {
        "str": 16,
        "hitProb": 0,
        "damageAdj": 1,
        "weight": 70,
        "maxPress": 195,
        "openDoors": 9,
        "bendLift": 10,
        "note": ""
    },
    "17":     {
        "str": 17,
        "hitProb": 1,
        "damageAdj": 1,
        "weight": 85,
        "maxPress": 220,
        "openDoors": 10,
        "bendLift": 13,
        "note": ""
    },
    "18":     {
        "str": 18,
        "hitProb": 1,
        "damageAdj": 2,
        "weight": 110,
        "maxPress": 255,
        "openDoors": 11,
        "bendLift": 16,
        "note": ""
    },
    "18.05":     {
        "str": "18/01-50",
        "hitProb": 1,
        "damageAdj": 3,
        "weight": 135,
        "maxPress": 280,
        "openDoors": 12,
        "bendLift": 20,
        "note": ""
    },
    "18.075":     {
        "str": "18/51-75",
        "hitProb": 2,
        "damageAdj": 3,
        "weight": 160,
        "maxPress": 305,
        "openDoors": 13,
        "bendLift": 25,
        "note": ""
    },
    "18.09":     {
        "str": "18/76-90",
        "hitProb": 2,
        "damageAdj": 4,
        "weight": 185,
        "maxPress": 330,
        "openDoors": 14,
        "bendLift": 30,
        "note": ""
    },
    "18.099":     {
        "str": "18/91-99",
        "hitProb": 2,
        "damageAdj": 5,
        "weight": 235,
        "maxPress": 380,
        "openDoors": "15(3)",
        "bendLift": 35,
        "note": ""
    },
    "18.1":     {
        "str": "18/100",
        "hitProb": 3,
        "damageAdj": 6,
        "weight": 335,
        "maxPress": 480,
        "openDoors": "16(6)",
        "bendLift": 40,
        "note": ""
    },
    "19":     {
        "str": 19,
        "hitProb": 3,
        "damageAdj": 7,
        "weight": 485,
        "maxPress": 640,
        "openDoors": "16(8)",
        "bendLift": 50,
        "note": "Hill Giant Strength"
    },
    "20":     {
        "str": 20,
        "hitProb": 3,
        "damageAdj": 8,
        "weight": 535,
        "maxPress": 700,
        "openDoors": "17(10)",
        "bendLift": 60,
        "note": "Stone Giant Strength"
    },
    "21":     {
        "str": 21,
        "hitProb": 4,
        "damageAdj": 9,
        "weight": 635,
        "maxPress": 810,
        "openDoors": "17(12)",
        "bendLift": 70,
        "note": "Frost Giant Strength"
    },
    "22":     {
        "str": 22,
        "hitProb": 4,
        "damageAdj": 10,
        "weight": 785,
        "maxPress": 970,
        "openDoors": "18(14)",
        "bendLift": 80,
        "note": "Fire Giant Strength"
    },
    "23":     {
        "str": 23,
        "hitProb": 5,
        "damageAdj": 11,
        "weight": 935,
        "maxPress": 1130,
        "openDoors": "18(16)",
        "bendLift": 90,
        "note": "Cloud Giant Strength"
    },
    "24":     {
        "str": 24,
        "hitProb": 6,
        "damageAdj": 12,
        "weight": 1235,
        "maxPress": 1440,
        "openDoors": "19(17)",
        "bendLift": 95,
        "note": "Storm Giant Strength"
    },
    "25":     {
        "str": 25,
        "hitProb": 7,
        "damageAdj": 14,
        "weight": 1535,
        "maxPress": 1750,
        "openDoors": "19(18)",
        "bendLift": 99,
        "note": "Titan Strength"
    }
}
||| CODE |||

Code: Select all

@@ @Test Get Data
[H: strList = "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,18/xx,19,20,21,22,23,24,25"]

[H: abort(input(strformat("str.input|%{strList}|Select Strength|LIST|VALUE=STRING"),"str.percent|0|Enter Percent for 18/xx Strength|TEXT"))]
[H, if(! isNumber(str.input)), code: {
   [H: percent = min(100,max(1,str.percent))]
   [H: strengthFormat = strformat("18/%{percent}")]
   [H: strData = am.xx.getStrengthData(18,percent)]
};{
   [H: strengthFormat = str.input]
   [H: strData = am.xx.getStrengthData(str.input)]
}]
[dialog("Strength Data"):{
   <b>STRENGTH: [r: strengthFormat]</b><br>
   <pre>[R: json.indent(replace(strData,"<","<"))]</pre>
}]

!!
@@ @initStrengthChart
<!-- initStrengthChart()

This function will put the strength chart in to json and save to token. Object will be value:obj where value is the max strength
   for that category. Added NA to end of raw data so listGet works properly. Removed % from openDoors in raw data because we dont 
   need. The percentage values should be divided by 1000 rather than 100 because 18/00 is not equal to 19 strength and the math
   would convert it to that if not divided by 1000.
-->

[H: rawChart = "1	-5	-4	1	3	1	0	NA
2	-3	-2	1	5	1	0	NA
3	-3	-1	5	10	2	0	NA
4-5	-2	-1	10	25	3	0	NA
6-7	-1	0	20	55	4	0	NA
8-9	0	0	35	90	5	1	NA
10-11	0	0	40	115	6	2	NA
12-13	0	0	45	140	7	4	NA
14-15	0	0	55	170	8	7	NA
16	0	+1	70	195	9	10	NA
17	+1	+1	85	220	10	13	NA
18	+1	+2	110	255	11	16	NA
18/01-50	+1	+3	135	280	12	20	NA
18/51-75	+2	+3	160	305	13	25	NA
18/76-90	+2	+4	185	330	14	30	NA
18/91-99	+2	+5	235	380	15(3)	35	NA
18/100	+3	+6	335	480	16(6)	40	NA
19	+3	+7	485	640	16(8)	50	 Hill Giant Strength 
20	+3	+8	535	700	17(10)	60	 Stone Giant Strength 
21	+4	+9	635	810	17(12)	70	 Frost Giant Strength 
22	+4	+10	785	970	18(14)	80	 Fire Giant Strength 
23	+5	+11	935	1130	18(16)	90	 Cloud Giant Strength 
24	+6	+12	1235	1440	19(17)	95	 Storm Giant Strength 
25	+7	+14	1535	1750	19(18)	99	 Titan Strength"]

[H: CR = decode("%0D")]
[H: EOL = decode("%0A")]
[H: TAB = decode("%09")]

<!-- clear Carriage return characters -->
[H: rawChart = replace(rawChart,CR,"")]

<!-- turn data in to json array -->
[H: rawChart = json.fromList(rawChart,EOL)]

<!-- read and build chart -->
[H: strengthChart = "{}"]
[H: chartKeys = json.append("","str","hitProb","damageAdj","weight","maxPress","openDoors","bendLift","note")]

[H, foreach(line,rawChart), code: {
   [H: obj = "{}"]
   [H, foreach(key,chartKeys), code: {
      [H: value = listGet(line,roll.count,TAB)]
      [H, if(value == "NA"): value = ""]
      [H: obj = json.set(obj,key,value)]
   }]
   [H: keyIndex = listGet(line,0,TAB)]
   [H: value = keyIndex]
   [H, if(! isNumber(keyIndex)), code: {
      [H: hasRange = listCount(keyIndex,"-") -1]
      [H: hasSubRange = listCount(keyIndex,"/") -1]
      [H, if(hasSubRange && ! hasRange): value = 18.1]
      [H, if(hasRange && ! hasSubRange): value = listGet(keyIndex,1,"-")]
      [H, if(hasRange && hasSubRange): value = 18 + (number(listGet(keyIndex,1,"-")) / 1000)]
   };{}]
   
   [H: strengthChart = json.set(strengthChart,value,obj)]
}]

[H: setLibProperty("strengthChart",strengthChart)]
[R: "<b>Strength Chart Loaded</b>"]

<!-- added for debugging -->
[dialog("D"):{<pre>[R: json.indent(replace(strengthChart,"<","<"))]</pre>}]

!!
@@ @getStrengthData
<!-- getStrengthData(strength,percent): strData
   strength - base strength value 1 to 25
   percent - (optional) for 18 strength with a percentile value
   strData - object of strength data
   
This function will return the data from the strength chart.
-->
[H: str = arg(0)]
[H, if(argCount() >= 2): percent = arg(1); percent = 0]

[H: strengthChart = getLibProperty("strengthChart")]
[H: strengthIndex = json.fields(strengthChart)]

[H: strIndex = str + (percent/1000)]
[H: strData = json.get(strengthChart,strIndex)]

[H, if(json.isEmpty(strData)), code: {   
   [H: lastIndex = 0]
   [H, foreach(index,strengthIndex), code: {
      [H, if(strIndex > lastIndex && strIndex <= index): strData = json.get(strengthChart,index)]
      [H: lastIndex = index]
   }]
};{}]

[H: macro.return = strData]

!!
@@ @onCampaignLoad
@PROPS@ fontColor=yellow ; autoExecute=true ; fontSize=11pt ; sortBy= ; color=red ; playerEditable=false ; applyToSelected=false ; group= ; tooltip= ; minWidth=94 ; 
<!-- onCampaignLoad()

This function will define all the macros on this lib token in to functions (UDF). This macro may be modified to exclude specific macros
   or change the ignoreOutput and newScope settings. See comments below. User should make their own personal prefix to the function names
   to represent this lib token. Usual format is "initials.code." where initials is programmers initials and code is specific to lib
   token. To call a function, am.xx.macroName(). Since all output is ignored you should use broadcast() or pass back your output in
   a variable to original macro clicked.
   
-->

<!-- In order to call a function, you must include the prefix before the macro name. This helps with compatibility with other library tokens -->
[H: prefix = "am.xx."]
[H: thisLib = getMacroLocation()]

<!-- Define functions HERE with options other than ignoreOutput = 1 and NewScope = 1. For example:
defineFunction(functionName,fullMacroName,ignoreOutput,newScope) 
   functionName - I recommend to still include the prefix in the name.
   fullMacroName - macro name must include lib location, currently stored in "thisLib"
   ignoreOutput - comments and any other raw output like html not in variables is ignored when set to 1
   newScope - default 1, when set to 0 the macro can use variables from calling macro. Not recommended in most cases.
-->
[H: defineFunction(prefix+"onCampaignLoad","onCampaignLoad@"+thisLib,0,1)]   

<!-- List functions names here to exclude from UDF definitions -->
[H: excludeList = json.append("","(new)","onCampaignLoad")]

<!-- default settings for all functions -->
[H: ignoreOutput = 1]
[H: newScope = 1]

<!-- This will exclude all macros with HTML formatting and non-variable valid characters in the name, including spaces. 
   Otherwise the html will be stripped and variable-valid characters will be used in the function definition. -->
[H: excludeHTMLMacros = 1]

<!-- get all the macro names from library and remove the excluded names -->
[H: allMacros = json.difference(getMacros("json"),excludeList)]

<!-- Define functions from the macro names. Already defined function will be ignored. -->
[H, foreach(macroName,allMacros), code: {
   [H: functionName = replace(macroName,"<[^>]*?>","")]
   [H: functionName = replace(functionName,"[^a-zA-Z0-9_.]","")]
   [H: cleanFunctionName = strformat("%{prefix}%{functionName}")]
   [H, if(excludeHTMLMacros && macroName != functionName): exclude = 1; exclude = 0]
   [H, if(! isFunctionDefined(cleanFunctionName) && ! exclude): defineFunction(cleanFunctionName,strformat("%{macroName}@%{thisLib}"),ignoreOutput,newScope)]
}]

!!
I also included a test token for you to download.
Attachments
lib.odnd ad&d.rptok
(118.45 KiB) Downloaded 27 times
ad&d token.png
ad&d token.png (110.65 KiB) Viewed 547 times

Post Reply

Return to “Macros”