Circular Generation Problem

I've got a jewelry generation table I'm trying to make a generator file for. One of the entries on the table is basically "add 100% to cost and weight and roll on the table again. If you get the same result, add another 100% and roll again. Keep doing this until you finally get a different result."

Another entry is for adding gems to said items of jewelry and it says "roll on the gems table, then return to this table and roll again. If you roll this result again, repeat."

So, I'm trying to handle this "Lather, rinse, repeat; Lather rinse, repeat" hurdle. I've already got the cost and weight set as variables. My first instinct is to just decide on an arbitrary maximum number of circular results for the first one and create that many versions of the table. For the second, I think my best bet would be to do something similar and try to have a final count of the number of times I need to generate a gem, then run the gems table that many times.

Thoughts? Anything less clunky would be great. If there is a proper way to do this, without any arbitrary maximums, that would be even better.

My humble thanks (in advance).

Comments

  • Whats the chance of it being rolled? Even if you dont limit the number of times it can be rolled, it will eventually not get rolled. It shouldnt hang the program up if you dont hard code a limit.

    If you want to limit how many times it can be rolled, you can use a deck pick. Add X many occurances of 'double the value, re-roll' to the table, and with each deck pick, if it get rolled it'll get removed from the table so that it cant be selected again (until the next Shuffle or next iteration of the generator).
  • Well, if the generator makes it as far down as this table, the chances are 1 in 6 that one of these two results is rolled.

    The real problem is that I need to know how many times the "Bigger" result gets rolled and how many times the "Gem" result is rolled to know how much to increase value and weight in the former case and how many gems in the latter.

    The "Bigger" result problem, the increasing of value and weight, is the reason I'm thinking of making a separate set of tables for each of, say, ten times that it could be rolled.

    Once a variable is set, can it be overwritten by a new value? Would this be a good use of define: rather than set:?
  • I know this is a pretty old post but I don't see an answer to the thread so I thought I'd share how I solved a similar gem value related problem.

    I play 1st edition AD&D and when rolling up gems they have an opportunity to have one off increases and decreases in value, or to dramatically increase their value by 7 categories, or decrease by 5, up to a maximum or down to a minimum value. As once a gem dropped below 1gp, the value changed to 10sp, then 5 and finally 1sp, I had to come up with more than a simple numeric means of increasing or decreasing their value (I would have converted all values to a copper piece base and then converted back to the final rate when done, but I chose another route).

    Here's the solution I came up with. If the OP is still around he may benefit from this, as may anyone else that stumbles on this. Note this is written using IPAD2 syntax, as it was built to be run from the /ipad command inside ScreenMonkey.

    First, the top level GEMS table:
    Table: Gems
    Set: Count=0
    Set: CoinIndex
    Set: GemValueIndex
    Set: GemValue
    Set: GemValueEnd
    Set: GemNameEnd
    Set: GemValueString
    Set: GemName
    Set: GemDescription
    Set: Gem
    Set: ShowValue=no
    Set: ShowName=no
    Set: ShowDescription=no
    01-25:[GemValueIndex==6][CoinIndex==4][@GemValueAdjustment&;#93;[Gem=={$GemValue}[#{$CoinIndex} CoinTypes] [@OrnamentalStones&;#93; (very small)]&
      [@DisplayGem&;#93;
    26-50:[GemValueIndex==7][CoinIndex==4][@GemValueAdjustment&;#93;[Gem=={$GemValue}[#{$CoinIndex} CoinTypes] [@SemiPreciousStones&;#93; (small)]&
      [@DisplayGem&;#93;
    51-70:[GemValueIndex==8][CoinIndex==4][@GemValueAdjustment&;#93;[Gem=={$GemValue}[#{$CoinIndex} CoinTypes] [@FancyStones&;#93; (average)]&
      [@DisplayGem&;#93;
    71-90:[GemValueIndex==9][CoinIndex==4][@GemValueAdjustment&;#93;[Gem=={$GemValue}[#{$CoinIndex} CoinTypes] [@FancyPreciousStones&;#93; (large)]&
      [@DisplayGem&;#93;
    91-99:[GemValueIndex==10][CoinIndex==4][@GemValueAdjustment&;#93;[Gem=={$GemValue}[#{$CoinIndex} CoinTypes] [@GemStones&;#93; (very large)]&
      [@DisplayGem&;#93;
    100:[GemValueIndex==11][CoinIndex==4][@GemValueAdjustment&;#93;[Gem=={$GemValue}[#{$CoinIndex} CoinTypes] [@Jewels&;#93; (huge)]&
      [@DisplayGem&;#93;
    

    The Idea here is that every gem has an index into both the Value and CoinType tables to set the initial value of that gem category.
    Table: CoinTypes
    Type: Lookup
    1:cp
    2:sp
    3:ep
    4:gp
    5:pp
    
    Table: GemBaseValues
    Type: Lookup
    Roll:1d19
    01:[CoinIndex==2]1
    02:[CoinIndex==2]5
    03:[CoinIndex==2]10
    04:[CoinIndex==4]1
    05:[CoinIndex==4]5
    06:[CoinIndex==4]10
    07:[CoinIndex==4]50
    08:[CoinIndex==4]100
    09:[CoinIndex==4]500
    10:[CoinIndex==4]1000
    11:[CoinIndex==4]5000
    12:[CoinIndex==4]10000
    13:[CoinIndex==4]25000
    14:[CoinIndex==4]50000
    15:[CoinIndex==4]100000
    16:[CoinIndex==4]250000
    17:[CoinIndex==4]500000
    19:[CoinIndex==4]1000000
    

    Next, I call the GemValueAdjustment table (which I'm using more like a Method) to check and set the final value of the gem. If the gem value crosses coin boundaries, the GemBaseValues table will reset the CoinTypeIndex to the new coin type.
    Table: GemValueAdjustment
    Type: Lookup
    Roll: 1d10
    01:[when not]{$Count}=7][do][GemValueIndex=={!{$GemValueIndex}+1}][Count=={!{$Count}+1}][end]&
       [when]{$GemValueIndex}>19[do][GemValueIndex==19][end]&
       [#{1d8} GemValueAdjustment]
    02:[GemValue=={![#{$GemValueIndex} GemBaseValues]*2}]
    03:[GemValue=={!round([#{$GemValueIndex} GemBaseValues] * (1 + {1d6}/10))}]
    04-08:[GemValue==[#{$GemValueIndex} GemBaseValues]]
    09:[GemValue=={!round([#{$GemValueIndex} GemBaseValues]-[#{$GemValueIndex} GemBaseValues] * {1d4}/10)}]
    10:[when not]{$Count}=5][do][GemValueIndex=={!{$GemValueIndex}-1}][Count=={!{$Count}+1}][end]&
       [when]{$GemValueIndex]<1[do][GemValueIndex==1][end]&
       [#{2d4+2} GemValueAdjustment]
    

    Most of the work inside the GemValueAdjustment is straightforward. Entries from 02-09 just alter the value of the gem by calling the appropriate GemBaseValue entry and either increasing or decreasing it by a percentage. Things get interesting with the categorical increase/decrease entries.

    When a 01 is rolled, the gem jumps up to the next GemBaseValue category by increasing the GemValueIndex by 1 (GemValueIndex++). However, there are three constraints to this:
    1) A gem can only increase in value a maximum of 7 times. This is implemented by the check and increment of the Count variable.
    2) A gem can not increase past the end of the table. I handle this by flattening the top of the GemValueIndex to 19 (the top of the GemBaseValue table).
    3) Once we hit this option, the gem won't decrement in value; it will either increase value, increase category, or be the final value. Regardless, this result triggers a reroll on the adjustment table. To accomplish this, we recursively call the GemValueAdjustment table, but with the Pick syntax passing in the result of a {1d8} die roll.

    Rolling a 10 is handled in a similar way; a gem can only decrement 5 times, can't drop below the beginning of the table, and cannot increase in value once it hits this option. We do a decrement on the GemValueIndex (GemValueIndex--), and then pass a {2d+2} roll into the recursive Pick call.

    NOTE: This is slightly different from how the DMG describes handling decreases in value, as it indicates a decreased gem could, on a reroll, hit a 02 or 03, thereby increasing the gem's value. I choose to avoid that to provide a more symmetrical handling of increases and decreases.

    After the return to the main Gems table, we just set the Gem variable by concatenating:
    1: the final GemValue
    2: the Coin type (via a Pick from the CoinType table using the index)
    3: The type and description of the gem (using a basic table call for the Type of gem being rolled -- Ornamental, Semi-Precious, etc.)
    Table:OrnamentalStones
    Type: Lookup
    Roll: 1d12
    01:Azurite: Opaque mottled deep blue stone
    02.Banded Agate: Translucent stone with brown, blue, white and reddish stripes
    03:Blue Quartz: Transparent pale blue stone
    04:Eye Agate: Translucent stone with circles of [|gray|white|brown|blue|green]
    05:Hematite: Opaque gray-black stone
    06:Lapis Lazuli: Opaque light and dark blue stone with yellow flecks
    07:Malachite: Opaque stone striated light and dark green
    08:Moss Agate: Translucent [|Pink|Yellowish-white] stone with [|grayish|greenish] "moss markings"
    09:Obsidian: Opaque black stone
    10:Rhodochrosite: Opaque light pink stone
    11:Tiger Eye: Translucent rich brown stone with a golden center under-hue
    12:Turquoise: Opaque light blue-green stone
    

    At this point, the Gems value will contain something like the following:
    500gp Jade: Translucent white stone (average)
    

    The last bit displays the gem. However, since I want to re-use this file for many purposes, I didn't want to just show the above in every circumstance. For example, one of the results of using a Wand of Wonder is to shoot 1gpbv gems at the target. I don't really need to have the description of the gem to be displayed in that result; that would make it very wordy:
    24 10sp Azurite: Opaque mottled deep blue stone shoot forth in a 3" long stream, causing 13hpd to any creature in their path.
    

    So, the DisplayGems Table (Method) is set up so that I can restrict what gem information to show as needed by passing value, name, or desc into the table call:
    Table: DisplayGem
    [GemValueEnd=={$Gem}>> at p]&
    [GemNameEnd=={$Gem}>> at :]&
    [GemValueString=={$Gem}>> left {!{$GemValueEnd}}]&
    [GemName=={$Gem}>> substr {!{$GemValueEnd}+2} {!{$GemNameEnd}-{$GemValueEnd}-2}]&
    [GemDescription=={$Gem}>> substr {!{$GemNameEnd}+2} 0]&
    
    [when]{$1}=value[do][ShowValue==yes][end]&
    [when]{$1}=name[do][ShowName==yes][end]&
    [when]{$1}=desc[do][ShowDescription==yes][end]&
    [when]{$1}=valuename[do][ShowValue==yes][ShowName==yes][end]&
    [when]{$1}=valuedesc[do][ShowValue==yes][ShowDescription==yes][end]&
    [when]{$1}=namedesc[do][ShowName==yes][ShowDescription==yes][end]&
    [when]{$1}=valuenamedesc[do][ShowValue==yes][ShowName==yes][ShowDescription==yes][end]&
    [when]{$1}=all[do][ShowValue==yes][ShowName==yes][ShowDescription==yes][end]&
    [when not]{$1}[do][ShowValue==yes][ShowName==yes][ShowDescription==yes][end]&
    
    //Output
    [when]{$ShowValue}=yes[do]{$GemValueString}[end]&
    [when]{$1}=valuename[do] [end]&
    [when]{$1}=valuedesc[do] [end]&
    [when]{$1}=valuenamedesc[do] [end]&
    [when]{$1}=all[do] [end]&
    [when not]{$1}[do] [end]&
    [when]{$ShowName}=yes[do]{$GemName}[end]&
    [when]{$1}=valuedesc[do] [end]&
    [when]{$1}=namedesc[do]: [end]&
    [when]{$1}=valuenamedesc[do]: [end]&
    [when]{$1}=all[do]: [end]&
    [when not]{$1}[do]: [end]&
    [when]{$ShowDescription}=yes[do]{$GemDescription}[end]
    

    I essentially split the string into its component parts and then display it as needed. I know I could have set it up to display the distinct GemValue, CoinType, and Gem results, but since the Name and description of the gem are already returned as a single result I had to do string splitting anyway, and it was a good exercise.

    Also, in the case where I need a gem lower than the starting base of the table (as in the Wand of Wonder case), I set the GemValueIndex and CoinTypeIndex values in the calling table, call the GemValueAdjustment table and then call "[@DisplayGem with valuename]".

    So, using this together, here is the call for the wand to shoot 1gpvb gems at an opponent, by s:
    [@GemValueAdjustment&;#93;[Gem=={$GemValue}[#{$CoinIndex} CoinTypes] [@OrnamentalStones&;#93; (very small)]&
      {10d4} [@DisplayGem with valuename] gems shoot forth in a 3" long stream, causing {5d4}hpd to any creature in their path.
    
    Resulting in the following output (made several rolls to show the diversity of the output):
    Janrith rolls on wandofwonder: 
    25 2gp Rhodochrosite gems shoot forth in a 3" long stream, causing 11hpd to any creature in their path.
    
    Janrith rolls on wandofwonder: 
    22 20gp Turquoise gems shoot forth in a 3" long stream, causing 10hpd to any creature in their path.
    
    Janrith rolls on wandofwonder: 
    26 10sp Malachite gems shoot forth in a 3" long stream, causing 13hpd to any creature in their path.
    
    Janrith rolls on wandofwonder: 
    27 1gp Blue Quartz gems shoot forth in a 3" long stream, causing 16hpd to any creature in their path.
    

    Attached is the full code to how I'm handling gems in my game. As far as I know, I've implemented all of the rules from the DMG correctly, with the caveat noted above about drastic value decreass.

    I hope this helps others that are also looking to do recursive table calls figure out the process. If anyone has any questions or comments, please feel free to let me know.
  • I was just checking my Gems generator inside of IPAD3, and discovered a difference in the way the IPAD 2 engine processes expressions inside of ScreenMonkey and how IPAD3 processes them natively, which I found rather odd.

    At any rate to verify, I added the following code snippet to the generator's DisplayGem table and then copied the generator from %IPADLocation%\Tables\Treasure to %USERPROFILE%\Inspiration Pad Pro\Common\Treasure, so both files are identical.
    <br/>DEBUG START&
    <br/>Gem:*{$Gem}*&
    <br/>GemValueEnd:*{$GemValueEnd}*&
    <br/>left GemValueEnd:*[{$Gem}>> left {!{$GemValueEnd}}]*&
    <br/>GemNameEnd:*{$GemNameEnd}*&
    <br/>GemValue:*{$GemValueString}*&
    <br/>substr GemValueEnd+2 (GemNameEnd-(GemValueEnd+2)):*[{$Gem}>> substr {!{$GemValueEnd}+2} {!{$GemNameEnd}-({$GemValueEnd}+2)}]*&
    <br/>substr GemValueEnd+2 (GemNameEnd-GemValueEnd):*[{$Gem}>> substr {!{$GemValueEnd}+2} {!{$GemNameEnd}-{$GemValueEnd}}]*&
    <br/>substr GemValueEnd}+2 (GemNameEnd-GemValueEnd-2):*[{$Gem}>> substr {!{$GemValueEnd}+2} {!{$GemNameEnd}-{$GemValueEnd}-2}]*&
    <br/>GemName:*{$GemName}*&
    <br/>GemDescription:*{$GemDescription}*&
    <br/>&
    <br/>DEBUG END&
    

    Then, I ran them both, first in ScreenMonkey using the /ipad command:
    Janrith rolls on gems: 
    
    DEBUG START
    Gem:*70gp Zircon: Transparent clear pale blue-green stone (small)*
    GemValueEnd:*4*
    left GemValueEnd:*70gp*
    GemNameEnd:*12*
    GemValue:*70gp*
    substr GemValueEnd+2 (GemNameEnd-(GemValueEnd+2)):*Zircon*
    substr GemValueEnd+2 (GemNameEnd-GemValueEnd):*Zircon: *
    substr GemValueEnd+2 (GemNameEnd-GemValueEnd-2):*Zircon*
    GemName:*Zircon*
    GemDescription:*Transparent clear pale blue-green stone (small)*
    DEBUG END
    

    And then using IPAD3:
    DEBUG START
    Gem:*10gp Malachite: Opaque stone striated light and dark green (very small)*
    GemValueEnd:*4*
    left GemValueEnd:*10gp*
    GemNameEnd:*15*
    GemValue:*10gp*
    substr GemValueEnd+2 (GemNameEnd-(GemValueEnd+2)):*Malachite*
    substr GemValueEnd+2 (GemNameEnd-GemValueEnd):*Malachite: *
    substr GemValueEnd+2 (GemNameEnd-GemValueEnd-2):*Malachite: Op*
    GemName:*Malachite: Op*
    GemDescription:*Opaque stone striated light and dark green (very small)*
    DEBUG END
    

    The substring expression which seem to be reacting differently is below, and it seems to be an order of precedence bug:
    <br/>substr GemValueEnd+2 (GemNameEnd-GemValueEnd-2):*[{$Gem}>> substr {!{$GemValueEnd}+2} {!{$GemNameEnd}-{$GemValueEnd}-2}]*&
    

    Both expressions should be the same, but it appears as though IPAD3 is treating this expression as though it were written:
    <br/>substr GemValueEnd+2 (GemNameEnd-(GemValueEnd-2)):*[{$Gem}>> substr {!{$GemValueEnd}+2} {!{$GemNameEnd}-({$GemValueEnd}-2)}]*&
    

    Adding the above to my debug instrumentation and rerunning inside IPAD3 confirms it:
    DEBUG START
    Gem:*50gp Chrysoprase: Translucent apple to emerald green stone (small)*
    GemValueEnd:*4*
    left GemValueEnd:*50gp*
    GemNameEnd:*17*
    GemValue:*50gp*
    substr GemValueEnd+2 (GemNameEnd-(GemValueEnd+2)):*Chrysoprase*
    substr GemValueEnd+2 (GemNameEnd-GemValueEnd):*Chrysoprase: *
    substr GemValueEnd+2 (GemNameEnd-GemValueEnd-2):*Chrysoprase: Tr*
    substr GemValueEnd+2 (GemNameEnd-(GemValueEnd-2)):*Chrysoprase: Tr*
    GemName:*Chrysoprase: Tr*
    GemDescription:*Translucent apple to emerald green stone (small)*
    DEBUG END
    

    Not sure if this is a bug in the IPAD2 or IPAD3 expression processor, but it is indeed a difference to be aware of.

Leave a Comment