QB64.org Forum

Active Forums => Programs => Topic started by: SMcNeill on January 16, 2019, 12:57:08 am

Title: Steve's Ole Dice Roller
Post by: SMcNeill on January 16, 2019, 12:57:08 am
Having a little free time once again, (by avoiding my writing commitment like a lazy bum), I decided to play around and create a little dice rolling routine for fun, and this is what I've got so far.  ;)

At this point, it doesn't actually do *anything* much, except highlight the selection process I'm going to be using for user interactions.   I wanted something a little different than the standard keyboard entry routines, so I came up with this little process for user input.   Test it out if you guys get a chance and tell me if it seems intuitive enough for ease of selection/use.  If there's any issues, I'd like to address them ASAP, before things get more complicated once I start adding modifiers into the process.  ;)

NOTE: You'll probably need one of the latest development builds for this to work properly as it relies upon the fixes to the precompiler and the _TRIM$ keyword.

Title: Re: Steve's Ole Dice Roller (work in progress)
Post by: johnno56 on January 16, 2019, 02:19:16 am
That's quite a collection of colours. May I ask where you got the info? I hope you did a cut-n-paste otherwise that's a set of sore fingers right there...

Hmm... issues...

Although I am a big fan of 'Old English' text, I do not happen to have it installed, but after a short search the font was found.

The 'sets', 'number', and 'sides' worked just fine. My screen capture looks a bit weird but I'm going to guess it was probably the font I used. But attached it anyway so you can see that it works...

  [ This attachment cannot be displayed inline in 'Print Page' view ]  

J
ps: I'm also using qb64-dev
Title: Re: Steve's Ole Dice Roller (work in progress)
Post by: Qwerkey on January 16, 2019, 05:04:31 am
Test it out if you guys get a chance and tell me if it seems intuitive enough for ease of selection/use.

Yep.  That's intuitive all right.
Title: Re: Steve's Ole Dice Roller (work in progress)
Post by: Pete on January 16, 2019, 10:58:46 am
I left my Old English fonts on my Windows King George III computer. Sorry.

Pete :(
Title: Re: Steve's Ole Dice Roller (work in progress)
Post by: SMcNeill on January 16, 2019, 11:07:54 am
So apparently I’ll need to package (or embed) the fonts with the final version...  And here I was thinking OldEnglish fonts were standard with most systems.  O_o!

At least that’s one issue found, before I get any further.  Thanks for the feedback, guys. 
Title: Re: Steve's Ole Dice Roller (work in progress)
Post by: SMcNeill on January 16, 2019, 12:58:59 pm
Added a screenshot of how the screen should actually look for folks, once the proper fonts and all have been found and loaded with the program.  I'll package everything needed up into a nice zip/7z archive as progress on the project progresses. but for now a screenshot is all the updated progress you get while I add in a few bells and whistles.  :)
Title: Re: Steve's Ole Dice Roller (work in progress)
Post by: Pete on January 16, 2019, 01:37:11 pm
I went back to QBF to look up some old dice routines. I found this neat 3400+ line QBasic Yahtzee contribution by Myst It's a pain to copy but once you have it on the clipboard, it pastes and runs perfectly in QB64. I couldn't find the custom made 3-D dice roll simulation TheBob made. I'll have to ask him if he remembers where he posted them. Bob made some kind of comment about one of my dice routines; something about my results would only be valid if I was rolling dice on Mars. I told him I could try rolling the same dice on Uranus, but it would probably just keep coming out craps. I'll be sure to forego that story when I ask him where he's hiding his dice.

Pete
Title: Re: Steve's Ole Dice Roller (work in progress)
Post by: SMcNeill on January 16, 2019, 02:00:51 pm
What I’m shooting for is (once again) an over-engineered RPG dice roller which can handle anything we want from it....

Options will include:

Rolling sets of multiple dice of various sides.  3 sets of 2d10 would result in 3 numbers generated by rolling two different 10-sided dice.  (Not 3 sets of INT(RND *19) + 2 to generate values of 2 to 20, but 3 sets of 2 rolls of INT(RND * 10) + 1...  The two may generate the same values, but at a distinctly different probability curve.)

Modifiers per dice:  +1 per dice, for example, so 2D6+1 will generate 2 numbers from 2 to 7, add them together for a total of 4 to 14.

Modifiers per set:  (3S2D6)+1 would be 3 sets of 2D6, with 1 added to each set.  Rolls might be:
(1,3) + 1 = 5
(2,3) + 1 = 6
(4,5) + 1 = 10

Modifiers to total roll:  3S2D6T+1...  much like above, 3 sets of 2D6, with 1 added to the total sum:
(1,3) = 4
(2,3) = 5
(4,5) = 9
—————-
+1    = 19

Ability to keep/discard dice:    4D6K3h — roll 4 6-sided dice, keep the three highest:
(1, 2, 5, 5) = 12

Ability to reroll edge cases:  2D6R1R6 — roll 2 6-sided dice, reroll 1’s, reroll 6,s:
(1, 2, 4) = 6   <— in this case, the one got rerolled.

Ability to use “open dice”:  2D6O6 — roll 2 6-sided dice, open roll 6’s:
(3, 6, 4) = 13 <— a 6 was rolled, roll another dice (which was the 4)

And most of these “dice rules” will apply to sets as well, so we can:
Roll 10 sets of 2D10, and drop the lowest set, for 9 numbers from 2-20...

And so on, with every customizable option I can imagine from all my years playing various pen-and-paper  RPGs.
Title: Re: Steve's Ole Dice Roller (work in progress)
Post by: Pete on January 16, 2019, 02:17:19 pm
RPGs, I gave that stuff up years ago, but I I recall that they usually require die with more sides than the standard 6-sides used in casino games. I threw one of those once on a craps table in Vegas and the casino boss threatened to have me removed. I told him he couldn't throw me out, because I was a level 5 elf master; so instead, he shoved me in a hollow tree and had me bake cookies for the entire crew. Not the kind of chips I was hoping to come away with.

Pete
Title: Re: Steve's Ole Dice Roller (work in progress)
Post by: Pete on January 16, 2019, 09:52:59 pm
Ah, Bob just reposted it here: https://www.tapatalk.com/groups/qbasic/bob-do-you-remember-that-3-d-dice-roll-simulation--t39421.html#p212448

It's very compact code that creates a 3-D look to the dice, but not true 3-D, spinning on an axis, etc. Still, for a dice game, they are perfect to add the visual effects to give the game a nice polished appearance.

For RPG dice, it would be more of a design challenge, for sure.

Pete
Title: Re: Steve's Ole Dice Roller (work in progress)
Post by: SMcNeill on January 16, 2019, 10:47:11 pm
v0.5 is available for testing/breaking...

Packaged it all up together in a 7z file which I attached to the bottom of the first post, and it's beginning to take on the pretty little look of a decent project.

I think the selection process is more or less finished, with the exception of a few pop-ups so the user can choose modifiers as I was speaking about earlier to apply to their dice rolls, but those should be a breeze to implement now that I've gotten everything else up to this point in the process...   And all that'll be left after that is to actually just roll the dice and obey the ruleset the user creates.  :)
Title: Re: Steve's Ole Dice Roller (work in progress)
Post by: Pete on January 16, 2019, 11:15:56 pm
So right now it has a hover feature to pick the elements to test. Anything else besides making those input selections?

Pete
Title: Re: Steve's Ole Dice Roller (work in progress)
Post by: SMcNeill on January 16, 2019, 11:35:37 pm
So right now it has a hover feature to pick the elements to test. Anything else besides making those input selections?

Pete

It should now have 3 extra areas with 19 selectable/clickable lines. 

The next stage is to display a pop-up when the user clicks on one of those lines, with the available options you can set for each area, such as:

Click the DICE list from 1 to 19, generate a pop up for:
Keep ### of Highest Rolls
Keep ### of Lowest Rolls
Discard ### of Highest Rolls
Discard ### of Lowest Rolls
Add ### to each Roll
Subtract ### from each Roll
Reroll any ###’s
Open roll with ###’s
(Other options as I think of them)

Then you create the rule you wish and it fills the spot you chose...

After that it’s just rolling and obeying the set rules.  The framework is all laid out and the hard part is done (which is why I’d call it v0.5 as it’s about half finished).  Now it’s just a case of taking advantage of the routines available and tying it all together to finish.  ;)
Title: Re: Steve's Ole Dice Roller (work in progress)
Post by: STxAxTIC on January 17, 2019, 09:20:07 am
A few questions -

Are the source files within the ZIP being updated? (I'm getting the same behavior now that I was getting last time I checked and the download count didn't seem to reset.)

Is Linkedlist.bas supposed to compile? (I'm not running the dev version.)

Have you considered separating the mechanism from the interface, so that the roller calculations may be attached elsewhere? Every special font or graphic will significantly shave down the number of compatible systems out in the future. You probably want a screen-0 brain back there, no?

How tightly thought-out is the math behind re-rolling? You had my interest at "probability curve". And also the mechanics of re-rolling... if we want to roll a six-sided dice and promptly throw away all "2" results it lands on, may I think of this as a 5-sided dice that is missing the 2?

Title: Re: Steve's Ole Dice Roller (work in progress)
Post by: SMcNeill on January 17, 2019, 09:46:14 am
A few questions -

Are the source files within the ZIP being updated? (I'm getting the same behavior now that I was getting last time I checked and the download count didn't seem to reset.)

Is Linkedlist.bas supposed to compile? (I'm not running the dev version.)

Have you considered separating the mechanism from the interface, so that the roller calculations may be attached elsewhere? Every special font or graphic will significantly shave down the number of compatible systems out in the future. You probably want a screen-0 brain back there, no?

How tightly thought-out is the math behind re-rolling? You had my interest at "probability curve". And also the mechanics of re-rolling... if we want to roll a six-sided dice and promptly throw away all "2" results it lands on, may I think of this as a 5-sided dice that is missing the 2?

Only one zip uploaded so far, so if you downloaded it once, I haven't updated it since then.  ;)

When I get to the dice rolling routine, the core mechanic behind it is going to be a formula based processing routine, much like I described to Pete previously.  Send it a formula, it'll send you a plain text array of results which you can work with.

So send it "2D6", it'll send you an array of:
"(1,3) = 4"

Send it "3S4D6+1K3H", it'll obey the ruleset you told it -- roll 3 sets of 2D6, add 1 to each dice, and keep the 3 highest, for a return of:
"(2, 3, 7, 4) = 14"
"(3, 3, 3, 5) = 11"
"(2, 5, 6, 7) = 18"

The idea is to have the main interface just be a pretty little UI which keeps things simple for people to use and interact with, but the core rolling routine is going to be able to process a custom format dice rolling formula. 

For an idea of how the core will work, take a moment to look at my old dice rolling routine, which I reshared here: https://www.qb64.org/forum/index.php?topic=981.0  It's been around since November of 2013, so it just seems like it's time to give it an updated UI and expandable features, thus the birth of this little project.  ;)
Title: Re: Steve's Ole Dice Roller (work in progress)
Post by: SMcNeill on January 19, 2019, 04:37:03 am
The engine for the new dice roller:

Code: QB64: [Select]
  1. DIM SHARED DiceToRoll AS DiceRoller_Type 'for testing
  2. DiceToRoll.NumberOfDice = 4
  3. DiceToRoll.DiceSides = 6 '10
  4. 'DiceToRoll.DiceReroll = "=1,=3"
  5. 'DiceToRoll.DiceOpenRoll = "=10"
  6. 'DiceToRoll.DiceMod = 2
  7. DiceToRoll.DiceKeepHigh = 3
  8. 'DiceToRoll.DiceDiscardLow = 1
  9.  
  10. DiceToRoll.Set = 6
  11. 'DiceToRoll.SetMod = 10
  12. 'DiceToRoll.TotalMod = 27
  13.  
  14. DIM SHARED Brief
  15.  
  16. Brief = 0
  17. PRINT "NON-BRIEF OUTPUT:"
  18. PRINT "'6s; 4d6 kh3' -- Roll 6 sets of 4 six-sided dice, keep the three highest"
  19. PRINT DiceRoll$
  20.  
  21. Brief = 1
  22. PRINT "SEMI-BRIEF OUTPUT:"
  23. PRINT "'6s; 4d6 kh3' -- Roll 6 sets of 4 six-sided dice, keep the three highest"
  24. PRINT DiceRoll$
  25.  
  26. Brief = 2
  27. PRINT "MOST BRIEF OUTPUT:"
  28. PRINT "'6s; 4d6 kh3' -- Roll 6 sets of 4 six-sided dice, keep the three highest"
  29. PRINT DiceRoll$
  30.  
  31.  
  32.  
  33.  
  34.  
  35.  
  36.  
  37.  
  38. TYPE DiceRoller_Type
  39.     Set AS LONG
  40.     SetMod AS LONG
  41.     NumberOfDice AS LONG
  42.     DiceSides AS LONG
  43.     DiceMod AS LONG
  44.     DiceReroll AS STRING * 100
  45.     DiceOpenRoll AS STRING * 100
  46.     DiceKeepHigh AS LONG
  47.     DiceKeepLow AS LONG
  48.     DiceDiscardHigh AS LONG
  49.     DiceDiscardLow AS LONG
  50.     TotalMod AS LONG
  51.  
  52.  
  53. SUB ClearDice
  54.     DiceToRoll.Set = 0
  55.     DiceToRoll.SetMod = 0
  56.     DiceToRoll.NumberOfDice = 0
  57.     DiceToRoll.DiceSides = 0
  58.     DiceToRoll.DiceMod = 0
  59.     DiceToRoll.DiceReroll = ""
  60.     DiceToRoll.DiceOpenRoll = ""
  61.     DiceToRoll.DiceKeepHigh = 0
  62.     DiceToRoll.DiceKeepLow = 0
  63.     DiceToRoll.DiceDiscardHigh = 0
  64.     DiceToRoll.DiceDiscardLow = 0
  65.     DiceToRoll.TotalMod = 0
  66.  
  67. FUNCTION DiceRoll$
  68.     IF DiceToRoll.DiceKeepHigh OR DiceToRoll.DiceKeepLow THEN
  69.         IF DiceToRoll.DiceDiscardHigh OR DiceToRoll.DiceDiscardLow THEN DiceRoll$ = "Error - Can not keep and discard at the same time.": EXIT FUNCTION
  70.     END IF
  71.  
  72.     SHARED DiceToRoll AS DiceRoller_Type
  73.     REDIM rolls(0) AS LONG
  74.     SetTotal = 0
  75.     IF Brief = 2 THEN out$ = "("
  76.     FOR j = 1 TO DiceToRoll.Set
  77.         IF Brief = 0 THEN
  78.             out$ = out$ + "RAW: ("
  79.         ELSEIF Brief = 1 THEN
  80.             out$ = out$ + "("
  81.         END IF
  82.  
  83.         rollcount = -1
  84.         FOR i = 1 TO DiceToRoll.NumberOfDice
  85.             ReRollDice:
  86.             roll = INT(RND(1) * DiceToRoll.DiceSides) + 1
  87.             eval = ParseDiceOption(roll, DiceToRoll.DiceReroll)
  88.             IF eval THEN
  89.                 DiceOK = 0
  90.                 IF Brief = 0 THEN out$ = out$ + "r" + _TRIM$(STR$(roll)) + ", "
  91.                 GOTO ReRollDice
  92.             END IF
  93.             IF ParseDiceOption(roll, DiceToRoll.DiceOpenRoll) THEN
  94.                 DiceOK = 0
  95.                 DiceTotal = DiceTotal + roll + DiceToRoll.DiceMod
  96.                 IF Brief = 0 THEN
  97.                     out$ = out$ + _TRIM$(STR$(roll)) + "o"
  98.                     IF DiceToRoll.DiceMod THEN out$ = out$ + " + " + _TRIM$(STR$(DiceToRoll.DiceMod))
  99.                     out$ = out$ + ","
  100.                 END IF
  101.                 rollcount = rollcount + 1
  102.                 REDIM _PRESERVE rolls(rollcount) AS LONG 'make certain we dont get out of bound errors for crazy reroll scenarios
  103.                 rolls(rollcount) = roll + DiceToRoll.DiceMod
  104.                 GOTO ReRollDice
  105.             END IF
  106.  
  107.             rollcount = rollcount + 1
  108.             REDIM _PRESERVE rolls(rollcount) AS LONG 'make certain we dont get out of bound errors for crazy reroll scenarios
  109.             rolls(rollcount) = roll + DiceToRoll.DiceMod
  110.             DiceTotal = DiceTotal + roll + DiceToRoll.DiceMod
  111.             IF Brief = 0 THEN
  112.                 out$ = out$ + _TRIM$(STR$(roll))
  113.                 IF DiceToRoll.DiceMod THEN out$ = out$ + " + " + _TRIM$(STR$(DiceToRoll.DiceMod))
  114.                 IF i < DiceToRoll.NumberOfDice THEN 'more dice to roll in this set
  115.                     out$ = out$ + ", "
  116.                 ELSE 'we're finished
  117.                     out$ = out$ + ")"
  118.                 END IF
  119.             END IF
  120.         NEXT
  121.  
  122.  
  123.  
  124.         'The dice sorting routine, optimized to use _MEM and a comb sort algorithm.  It's more than fast enough for our needs here I think.  ;)
  125.  
  126.         IF rollcount > 0 THEN 'No need to try and sort only 1 dice.
  127.             DIM m AS _MEM
  128.             DIM o AS _OFFSET, o1 AS _OFFSET
  129.             DIM t AS LONG, t1 AS LONG
  130.             m = _MEM(rolls())
  131.             $CHECKING:OFF
  132.             gap = rollcount
  133.  
  134.             DO
  135.                 gap = 10 * gap \ 13
  136.                 IF gap < 1 THEN gap = 1
  137.                 i = 0
  138.                 swapped = 0
  139.                 DO
  140.                     o = m.OFFSET + i * 4
  141.                     o1 = m.OFFSET + (i + gap) * 4
  142.                     IF _MEMGET(m, o, LONG) > _MEMGET(m, o1, LONG) THEN
  143.                         _MEMGET m, o1, t1
  144.                         _MEMGET m, o, t
  145.                         _MEMPUT m, o1, t
  146.                         _MEMPUT m, o, t1
  147.                         swapped = -1
  148.                     END IF
  149.                     i = i + 1
  150.                 LOOP UNTIL i + gap > UBOUND(Rolls)
  151.             LOOP UNTIL swapped = 0 AND gap = 1
  152.             $CHECKING:ON
  153.             _MEMFREE m
  154.         END IF
  155.  
  156.         IF Brief = 0 THEN
  157.             out$ = out$ + "; SORTED: ("
  158.             FOR i = 0 TO rollcount
  159.                 out$ = out$ + _TRIM$(STR$(rolls(i)))
  160.                 IF i < rollcount THEN out$ = out$ + ", " ELSE out$ = out$ + ")"
  161.             NEXT
  162.         END IF
  163.  
  164.         REDIM keep(rollcount) AS LONG
  165.         IF DiceToRoll.DiceKeepHigh OR DiceToRoll.DiceKeepLow THEN
  166.             IF DiceToRoll.DiceKeepHigh THEN
  167.                 FOR i = DiceToRoll.DiceKeepHigh - 1 TO 0 STEP -1
  168.                     IF i < rollcount THEN keep(rollcount - i) = -1
  169.                 NEXT
  170.             END IF
  171.             IF DiceToRoll.DiceKeepLow THEN
  172.                 FOR i = 0 TO DiceToRoll.DiceKeepLow - 1
  173.                     IF i < rollcount THEN keep(i) = -1
  174.                 NEXT
  175.             END IF
  176.         ELSEIF DiceToRoll.DiceDiscardHigh OR DiceToRoll.DiceDiscardLow THEN
  177.             FOR i = 0 TO rollcount
  178.                 keep(i) = -1
  179.             NEXT
  180.             IF DiceToRoll.DiceDiscardHigh THEN
  181.                 FOR i = DiceToRoll.DiceDiscardHigh - 1 TO 0 STEP -1
  182.                     IF i < rollcount THEN keep(rollcount - i) = 0
  183.                 NEXT
  184.             END IF
  185.             IF DiceToRoll.DiceDiscardLow THEN
  186.                 FOR i = 0 TO DiceToRoll.DiceDiscardLow - 1
  187.                     IF i < rollcount THEN keep(i) = 0
  188.                 NEXT
  189.             END IF
  190.         ELSE
  191.             FOR i = 0 TO rollcount
  192.                 keep(i) = -1
  193.             NEXT
  194.         END IF
  195.  
  196.         IF Brief = 0 THEN out$ = out$ + "; KEEP: ("
  197.         KeepTotal = 0
  198.         kept = 0
  199.         FOR i = 0 TO rollcount
  200.             IF keep(i) THEN
  201.                 kept = kept + 1
  202.                 IF Brief < 2 THEN
  203.                     IF kept > 1 THEN out$ = out$ + ", "
  204.                     out$ = out$ + _TRIM$(STR$(rolls(i)))
  205.                 END IF
  206.                 KeepTotal = KeepTotal + rolls(i)
  207.             END IF
  208.         NEXT
  209.  
  210.         IF DiceToRoll.SetMod THEN
  211.             KeepTotal = KeepTotal + DiceToRoll.SetMod
  212.             IF Brief < 2 THEN
  213.                 out$ = out$ + ") + " + _TRIM$(STR$(DiceToRoll.SetMod))
  214.                 out$ = out$ + " = " + _TRIM$(STR$(KeepTotal))
  215.             ELSE
  216.                 out$ = out$ + _TRIM$(STR$(KeepTotal))
  217.             END IF
  218.         ELSE
  219.             IF Brief < 2 THEN
  220.                 out$ = out$ + ") = " + _TRIM$(STR$(KeepTotal))
  221.             ELSE
  222.                 out$ = out$ + _TRIM$(STR$(KeepTotal))
  223.             END IF
  224.         END IF
  225.         IF j < DiceToRoll.Set THEN
  226.             IF Brief = 2 THEN out$ = out$ + " + "
  227.             IF Brief < 2 THEN out$ = out$ + CHR$(13)
  228.             'IF Brief = 0 THEN out$ = out$ + CHR$(13)
  229.         END IF
  230.         SetTotal = SetTotal + KeepTotal
  231.     NEXT
  232.     IF j > 2 THEN
  233.         IF Brief < 2 THEN
  234.             out$ = out$ + CHR$(13) + "SET TOTAL:" + STR$(SetTotal)
  235.         ELSE
  236.             out$ = out$ + ") =" + STR$(SetTotal)
  237.         END IF
  238.     END IF
  239.     DiceRoll$ = out$
  240.  
  241.  
  242. FUNCTION ParseDiceOption (num, t_temp$)
  243.     temp$ = _TRIM$(t_temp$)
  244.     IF temp$ = "" THEN EXIT FUNCTION
  245.     IF RIGHT$(temp$, 1) <> "," THEN temp$ = temp$ + ","
  246.     DO
  247.         f = INSTR(temp$, ",")
  248.         IF f THEN
  249.             o$ = LEFT$(temp$, f - 1)
  250.             temp$ = MID$(temp$, f + 1)
  251.             o = VAL(MID$(o$, 2))
  252.             o$ = LEFT$(o$, 1)
  253.             SELECT CASE o$
  254.                 CASE "=": IF num = o THEN ParseDiceOption = -1: EXIT FUNCTION
  255.                 CASE "<": IF num < o THEN ParseDiceOption = -1: EXIT FUNCTION
  256.                 CASE ">": IF num > o THEN ParseDiceOption = -1: EXIT FUNCTION
  257.             END SELECT
  258.         END IF
  259.     LOOP UNTIL f = 0 OR temp$ = ""
  260.  

All it needs now is for me to add a parser to parse a string of user input for the roller, which should be simple enough to do, and then turn it into the necessary pieces for calculations.

If it you this little demo, it doesn't have any of the fancy little UI interactions, and you'll have to manually set the values for the roller itself, but this does highlight the various strings which the roller will send back to us for processing.

For paranoid rollers who want to follow the whole process through, we can get a VERY detailed breakdown of all the rolls and modifications by setting BRIEF to 0.  I have met some gamers who want to know exactly HOW they got the rolls they got, or else they claim "something is wrong" when the dice don't generate the values they want.  Complete openness is good for these type of people.

For those who are just interested in the rolls generated and totals, we can set BRIEF to 1.

And, for those folks who just want the basic results, we can set BRIEF to 2.



The '4d6 kh3' ruleset was chosen for the little demo as it's what used to be common for character creation for Advanced Dungeons & Dragons during the first few editions of the game.  Roll 6 sets of 4d6 for stats, and keep the 3 highest rolls...

Feel free to play around with the other options and see what you can generate with it...



(And, for those curious, I also plan to offer the ability to get back the results via an array as the old dice roller used to do things.  This time, there'll be a choice between getting the string results and getting an array of numbers to work with, without needing to parse the output first.)
Title: Re: Steve's Ole Dice Roller
Post by: SMcNeill on January 24, 2019, 08:08:42 am
The finished engine for the routine, and all I'll be bothering to share on the forums here.  The GUI makes use of "The LIBRARY WHICH SHALL NOT BE NAMED", and it seems as if its name offends people, so in an attempt to appease them, the UI which was showcased earlier will NOT be included for public viewing.

What is available for the public to make use of is the following:

Code: QB64: [Select]
  1. SCREEN _NEWIMAGE(1024, 720, 32)
  2.  
  3. CONST DiagRollEm = -1
  4.  
  5. 'The next lines are only needed for manual testing
  6. DIM DiceToRoll AS DiceRoller_Type
  7.  
  8. 'Feel free to change options as wanted for your program
  9. 'DiceToRoll.NumberOfDice = 2
  10. 'DiceToRoll.DiceSides = 10
  11. 'DiceToRoll.DiceReroll = "=1"
  12. 'DiceToRoll.DiceOpenRoll = "=10"
  13. 'DiceToRoll.DiceMod = 2
  14. 'DiceToRoll.DiceKeepHigh = 1
  15. 'DiceToRoll.DiceKeepLow = 1
  16.  
  17. 'DiceToRoll.Set = 10
  18. 'DiceToRoll.SetMod = 1
  19. 'DiceToRoll.SetReRoll = "<6"
  20. 'DiceToRoll.SetOpenRoll = ">10"
  21. 'DiceToRoll.TotalMod = 27
  22.  
  23. 'DiceToRoll.SetKeepHigh = 9
  24.  
  25.  
  26. PRINT RollEm$("10skh9r2;2d10o20;t2;b2")
  27.  
  28. PRINT "PRESS <ANY KEY> TO CONTINUE"
  29.  
  30. ClearDice
  31. DiceToRoll.Set = 6
  32. DiceToRoll.NumberOfDice = 4
  33. DiceToRoll.DiceSides = 6
  34. DiceToRoll.DiceKeepHigh = 3
  35.  
  36.  
  37. Brief = 0
  38. PRINT "NON-BRIEF OUTPUT:"
  39. PRINT "'6s; 4d6 kh3' -- Roll 6 sets of 4 six-sided dice, keep the three highest"
  40. PRINT DiceRoll$
  41. PRINT "PRESS <ANY KEY> TO CONTINUE"
  42.  
  43. Brief = 1
  44. PRINT "SEMI-BRIEF OUTPUT:"
  45. PRINT "'6s; 4d6 kh3' -- Roll 6 sets of 4 six-sided dice, keep the three highest"
  46. PRINT DiceRoll$
  47. PRINT "PRESS <ANY KEY> TO CONTINUE"
  48.  
  49. Brief = 2
  50. PRINT "MOST BRIEF OUTPUT:"
  51. PRINT "'6s; 4d6 kh3' -- Roll 6 sets of 4 six-sided dice, keep the three highest"
  52. PRINT DiceRoll$
  53. PRINT "PRESS <ANY KEY> TO CONTINUE"
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60.  
  61. TYPE DiceRoller_Type
  62.     Set AS LONG
  63.     SetMod AS LONG
  64.     SetReRoll AS STRING * 100
  65.     SetOpenRoll AS STRING * 100
  66.     SetKeepHigh AS LONG
  67.     SetKeepLow AS LONG
  68.     SetDiscardHigh AS LONG
  69.     SetDiscardLow AS LONG
  70.  
  71.     NumberOfDice AS LONG
  72.     DiceSides AS LONG
  73.     DiceMod AS LONG
  74.     DiceReroll AS STRING * 100
  75.     DiceOpenRoll AS STRING * 100
  76.     DiceKeepHigh AS LONG
  77.     DiceKeepLow AS LONG
  78.     DiceDiscardHigh AS LONG
  79.     DiceDiscardLow AS LONG
  80.  
  81.     TotalMod AS LONG
  82.  
  83. SUB StripNumber (m$)
  84.     v = VAL(m$)
  85.     DO UNTIL v = 0
  86.         'PRINT "Stripping number"; m$
  87.         m$ = MID$(m$, 2)
  88.         v = VAL(m$)
  89.     LOOP
  90.     DO UNTIL LEFT$(m$, 1) <> "0" 'strip any zeros
  91.         m$ = MID$(m$, 2)
  92.     LOOP
  93.  
  94.  
  95. SUB ClearDice
  96.     SHARED DiceToRoll AS DiceRoller_Type
  97.     DiceToRoll.Set = 0
  98.     DiceToRoll.SetMod = 0
  99.     DiceToRoll.SetReRoll = ""
  100.     DiceToRoll.NumberOfDice = 0
  101.     DiceToRoll.DiceSides = 0
  102.     DiceToRoll.DiceMod = 0
  103.     DiceToRoll.DiceReroll = ""
  104.     DiceToRoll.DiceOpenRoll = ""
  105.     DiceToRoll.DiceKeepHigh = 0
  106.     DiceToRoll.DiceKeepLow = 0
  107.     DiceToRoll.DiceDiscardHigh = 0
  108.     DiceToRoll.DiceDiscardLow = 0
  109.     DiceToRoll.TotalMod = 0
  110.  
  111. FUNCTION DiceRoll$
  112.     SHARED Brief AS LONG
  113.     SHARED DiceToRoll AS DiceRoller_Type
  114.     IF DiceToRoll.DiceKeepHigh OR DiceToRoll.DiceKeepLow THEN
  115.         IF DiceToRoll.DiceDiscardHigh OR DiceToRoll.DiceDiscardLow THEN DiceRoll$ = "Error - Can not keep and discard at the same time.": EXIT FUNCTION
  116.     END IF
  117.     IF DiceToRoll.NumberOfDice < 1 THEN DiceRoll$ = "Error - No dice to roll!": EXIT FUNCTION
  118.  
  119.  
  120.     SHARED DiceToRoll AS DiceRoller_Type
  121.     REDIM rolls(0) AS LONG
  122.     REDIM SetRolls(0) AS LONG
  123.     SetCount = 0
  124.     IF Brief = 2 THEN out$ = "("
  125.     FOR j = 1 TO DiceToRoll.Set
  126.         ReRollSet:
  127.         SetTotal = 0
  128.         IF Brief = 0 THEN
  129.             out$ = out$ + "RAW: ("
  130.         ELSEIF Brief = 1 THEN
  131.             out$ = out$ + "("
  132.         END IF
  133.  
  134.         rollcount = -1
  135.         FOR i = 1 TO DiceToRoll.NumberOfDice
  136.             ReRollDice:
  137.             roll = INT(RND(1) * DiceToRoll.DiceSides) + 1
  138.  
  139.             IF ParseDiceOption(roll, DiceToRoll.DiceReroll) THEN
  140.                 DiceOK = 0
  141.                 IF Brief = 0 THEN out$ = out$ + "r" + _TRIM$(STR$(roll)) + ", "
  142.                 GOTO ReRollDice
  143.             END IF
  144.             IF ParseDiceOption(roll, DiceToRoll.DiceOpenRoll) THEN
  145.                 DiceOK = 0
  146.                 DiceTotal = DiceTotal + roll + DiceToRoll.DiceMod
  147.                 IF Brief = 0 THEN
  148.                     out$ = out$ + _TRIM$(STR$(roll)) + "o"
  149.                     IF DiceToRoll.DiceMod THEN out$ = out$ + " + " + _TRIM$(STR$(DiceToRoll.DiceMod))
  150.                     out$ = out$ + ","
  151.                 END IF
  152.                 rollcount = rollcount + 1
  153.                 REDIM _PRESERVE rolls(rollcount) AS LONG 'make certain we dont get out of bound errors for crazy reroll scenarios
  154.                 rolls(rollcount) = roll + DiceToRoll.DiceMod
  155.                 GOTO ReRollDice
  156.             END IF
  157.  
  158.             rollcount = rollcount + 1
  159.             REDIM _PRESERVE rolls(rollcount) AS LONG 'make certain we dont get out of bound errors for crazy reroll scenarios
  160.             rolls(rollcount) = roll + DiceToRoll.DiceMod
  161.             DiceTotal = DiceTotal + roll + DiceToRoll.DiceMod
  162.             IF Brief = 0 THEN
  163.                 out$ = out$ + _TRIM$(STR$(roll))
  164.                 IF DiceToRoll.DiceMod THEN out$ = out$ + " + " + _TRIM$(STR$(DiceToRoll.DiceMod))
  165.                 IF i < DiceToRoll.NumberOfDice THEN 'more dice to roll in this set
  166.                     out$ = out$ + ", "
  167.                 ELSE 'we're finished
  168.                     out$ = out$ + ")"
  169.                 END IF
  170.             END IF
  171.         NEXT
  172.  
  173.         IF rollcount > 0 THEN Sort rolls() 'No need to try and sort only 1 dice.
  174.  
  175.         IF Brief = 0 THEN
  176.             out$ = out$ + "; SORTED: ("
  177.             FOR i = 0 TO rollcount
  178.                 out$ = out$ + _TRIM$(STR$(rolls(i)))
  179.                 IF i < rollcount THEN out$ = out$ + ", " ELSE out$ = out$ + ")"
  180.             NEXT
  181.         END IF
  182.  
  183.         REDIM keep(rollcount) AS LONG
  184.         IF DiceToRoll.DiceKeepHigh OR DiceToRoll.DiceKeepLow THEN
  185.             IF DiceToRoll.DiceKeepHigh THEN
  186.                 FOR i = DiceToRoll.DiceKeepHigh - 1 TO 0 STEP -1
  187.                     IF i < rollcount THEN keep(rollcount - i) = -1
  188.                 NEXT
  189.             END IF
  190.             IF DiceToRoll.DiceKeepLow THEN
  191.                 FOR i = 0 TO DiceToRoll.DiceKeepLow - 1
  192.                     IF i < rollcount THEN keep(i) = -1
  193.                 NEXT
  194.             END IF
  195.         ELSEIF DiceToRoll.DiceDiscardHigh OR DiceToRoll.DiceDiscardLow THEN
  196.             FOR i = 0 TO rollcount
  197.                 keep(i) = -1
  198.             NEXT
  199.             IF DiceToRoll.DiceDiscardHigh THEN
  200.                 FOR i = DiceToRoll.DiceDiscardHigh - 1 TO 0 STEP -1
  201.                     IF i < rollcount THEN keep(rollcount - i) = 0
  202.                 NEXT
  203.             END IF
  204.             IF DiceToRoll.DiceDiscardLow THEN
  205.                 FOR i = 0 TO DiceToRoll.DiceDiscardLow - 1
  206.                     IF i < rollcount THEN keep(i) = 0
  207.                 NEXT
  208.             END IF
  209.         ELSE
  210.             FOR i = 0 TO rollcount
  211.                 keep(i) = -1
  212.             NEXT
  213.         END IF
  214.  
  215.         IF Brief = 0 THEN out$ = out$ + "; KEEP: ("
  216.         KeepTotal = 0
  217.         kept = 0
  218.         FOR i = 0 TO rollcount
  219.             IF keep(i) THEN
  220.                 kept = kept + 1
  221.                 IF Brief < 2 THEN
  222.                     IF kept > 1 THEN out$ = out$ + ", "
  223.                     out$ = out$ + _TRIM$(STR$(rolls(i)))
  224.                 END IF
  225.                 KeepTotal = KeepTotal + rolls(i)
  226.             END IF
  227.         NEXT
  228.         IF Brief < 2 THEN out$ = out$ + ") = " + _TRIM$(STR$(KeepTotal))
  229.  
  230.         IF ParseDiceOption(KeepTotal, DiceToRoll.SetReRoll) THEN
  231.             IF Brief < 2 THEN out$ = out$ + "r" + CHR$(13)
  232.             GOTO ReRollSet
  233.         END IF
  234.  
  235.         IF ParseDiceOption(KeepTotal, DiceToRoll.SetOpenRoll) THEN
  236.             SetTotal = SetTotal + KeepTotal + DiceToRoll.SetMod
  237.             SetCount = SetCount + 1
  238.             REDIM _PRESERVE SetRolls(SetCount) AS LONG
  239.             SetRolls(SetCount) = SetTotal
  240.             GrandTotal = GrandTotal + SetTotal
  241.             IF Brief = 2 THEN out$ = out$ + _TRIM$(STR$(SetTotal))
  242.             out$ = out$ + "o"
  243.             IF Brief < 2 THEN
  244.                 IF DiceToRoll.SetMod THEN out$ = out$ + " + " + _TRIM$(STR$(DiceToRoll.SetMod))
  245.                 out$ = out$ + " = " + _TRIM$(STR$(SetTotal))
  246.                 out$ = out$ + CHR$(13)
  247.             ELSE
  248.                 out$ = out$ + ", "
  249.             END IF
  250.  
  251.             GOTO ReRollSet
  252.         END IF
  253.  
  254.         SetTotal = SetTotal + KeepTotal + DiceToRoll.SetMod
  255.         SetCount = SetCount + 1
  256.         REDIM _PRESERVE SetRolls(SetCount) AS LONG
  257.         SetRolls(SetCount) = SetTotal
  258.         GrandTotal = GrandTotal + SetTotal
  259.  
  260.         IF Brief < 2 THEN
  261.             IF DiceToRoll.SetMod THEN
  262.                 out$ = out$ + " + " + _TRIM$(STR$(DiceToRoll.SetMod))
  263.                 out$ = out$ + " = " + _TRIM$(STR$(SetTotal))
  264.             END IF
  265.             out$ = out$ + CHR$(13)
  266.         ELSE
  267.             out$ = out$ + _TRIM$(STR$(SetTotal))
  268.             IF j < DiceToRoll.Set THEN out$ = out$ + ", " ELSE out$ = out$ + ")"
  269.         END IF
  270.  
  271.  
  272.     NEXT
  273.     IF Brief < 2 THEN out$ = out$ + CHR$(13) + "GRAND TOTAL:"
  274.  
  275.     IF DiceToRoll.TotalMod THEN
  276.         IF Brief < 2 THEN out$ = out$ + STR$(GrandTotal) + " +" + STR$(DiceToRoll.TotalMod)
  277.     END IF
  278.  
  279.     GrandTotal = GrandTotal + DiceToRoll.TotalMod
  280.     out$ = out$ + " =" + STR$(GrandTotal)
  281.  
  282.     Sort SetRolls()
  283.     IF Brief = 0 THEN
  284.         out$ = out$ + CHR$(13) + CHR$(13) + "Sorted Set: ("
  285.         FOR i = 1 TO SetCount
  286.             out$ = out$ + _TRIM$(STR$(SetRolls(i)))
  287.             IF i < SetCount THEN out$ = out$ + ", " ELSE out$ = out$ + ")"
  288.         NEXT
  289.     END IF
  290.  
  291.     REDIM keep(SetCount) AS LONG
  292.     IF DiceToRoll.SetKeepHigh OR DiceToRoll.SetKeepLow THEN
  293.         IF DiceToRoll.SetKeepHigh THEN
  294.             FOR i = DiceToRoll.SetKeepHigh - 1 TO 0 STEP -1
  295.                 IF i < SetCount THEN keep(SetCount - i) = -1
  296.             NEXT
  297.         END IF
  298.         IF DiceToRoll.SetKeepLow THEN
  299.             FOR i = 0 TO DiceToRoll.SetKeepLow - 1
  300.                 IF i < SetCount THEN keep(i) = -1
  301.             NEXT
  302.         END IF
  303.     ELSEIF DiceToRoll.SetDiscardHigh OR DiceToRoll.SetDiscardLow THEN
  304.         FOR i = 0 TO SetCount
  305.             keep(i) = -1
  306.         NEXT
  307.         IF DiceToRoll.SetDiscardHigh THEN
  308.             FOR i = DiceToRoll.SetDiscardHigh - 1 TO 0 STEP -1
  309.                 IF i < SetCount THEN keep(SetCount - i) = 0
  310.             NEXT
  311.         END IF
  312.         IF DiceToRoll.SetDiscardLow THEN
  313.             FOR i = 0 TO DiceToRoll.SetDiscardLow - 1
  314.                 IF i < SetCount THEN keep(i) = 0
  315.             NEXT
  316.         END IF
  317.     ELSE
  318.         FOR i = 0 TO SetCount
  319.             keep(i) = -1
  320.         NEXT
  321.     END IF
  322.  
  323.  
  324.  
  325.  
  326.     out$ = out$ + CHR$(13) + "Set Kept: ("
  327.     IF Brief = 2 THEN out$ = "("
  328.     KeepTotal = 0
  329.     keep = 0
  330.     FOR i = 1 TO SetCount
  331.         IF keep(i) THEN
  332.             keep = keep + 1
  333.             IF keep > 1 THEN out$ = out$ + ", "
  334.             out$ = out$ + _TRIM$(STR$(SetRolls(i)))
  335.             KeepTotal = KeepTotal + SetRolls(i)
  336.         END IF
  337.     NEXT
  338.     KeepTotal = KeepTotal + DiceToRoll.TotalMod
  339.     out$ = out$ + ") = " + _TRIM$(STR$(KeepTotal))
  340.     DiceRoll$ = out$
  341.  
  342.  
  343. FUNCTION ParseDiceOption (num, t_temp$)
  344.     SHARED DiceToRoll AS DiceRoller_Type
  345.     temp$ = _TRIM$(t_temp$)
  346.     IF temp$ = "" THEN EXIT FUNCTION
  347.     IF RIGHT$(temp$, 1) <> "," THEN temp$ = temp$ + ","
  348.     DO
  349.         f = INSTR(temp$, ",")
  350.         IF f THEN
  351.             o$ = LEFT$(temp$, f - 1)
  352.             temp$ = MID$(temp$, f + 1)
  353.             o = VAL(MID$(o$, 2))
  354.             o$ = LEFT$(o$, 1)
  355.             SELECT CASE o$
  356.                 CASE "=": IF num = o THEN ParseDiceOption = -1: EXIT FUNCTION
  357.                 CASE "<": IF num < o THEN ParseDiceOption = -1: EXIT FUNCTION
  358.                 CASE ">": IF num > o THEN ParseDiceOption = -1: EXIT FUNCTION
  359.             END SELECT
  360.         END IF
  361.     LOOP UNTIL f = 0 OR temp$ = ""
  362.  
  363. SUB Sort (Array() AS LONG)
  364.     'The dice sorting routine, optimized to use _MEM and a comb sort algorithm.
  365.     'It's more than fast enough for our needs here I think.  ;)
  366.     DIM m AS _MEM
  367.     DIM o AS _OFFSET, o1 AS _OFFSET
  368.     DIM t AS LONG, t1 AS LONG
  369.     m = _MEM(Array())
  370.     gap = rollcount
  371.  
  372.     DO
  373.         gap = 10 * gap \ 13
  374.         IF gap < 1 THEN gap = 1
  375.         i = 0
  376.         swapped = 0
  377.         DO
  378.             o = m.OFFSET + i * 4
  379.             o1 = m.OFFSET + (i + gap) * 4
  380.             IF _MEMGET(m, o, LONG) > _MEMGET(m, o1, LONG) THEN
  381.                 _MEMGET m, o1, t1
  382.                 _MEMGET m, o, t
  383.                 _MEMPUT m, o1, t
  384.                 _MEMPUT m, o, t1
  385.                 swapped = -1
  386.             END IF
  387.             i = i + 1
  388.         LOOP UNTIL i + gap > UBOUND(Array)
  389.     LOOP UNTIL swapped = 0 AND gap = 1
  390.     _MEMFREE m
  391.  
  392.  
  393. FUNCTION RollEm$ (temp$)
  394.     SHARED DiceToRoll AS DiceRoller_Type
  395.     text1$ = UCASE$(temp$)
  396.     FOR i = 1 TO LEN(text1$) 'check for invalid characters
  397.         m$ = MID$(text1$, i, 1)
  398.         SELECT CASE m$
  399.             CASE "0" TO "9", "+", "-", "D", "K", "H", "L", "S", "T", ";", "=", "<", ">", ",", "R", "O", "B"
  400.                 text$ = text$ + m$ 'add valid characters to make text$
  401.             CASE " " 'do nothing to a space
  402.             CASE ELSE
  403.                 'invalid
  404.         END SELECT
  405.     NEXT
  406.     'IF DiagRollEM THEN PRINT "Verified: "; text$
  407.  
  408.     IF text$ = "" THEN EXIT SUB 'can't do nothing with an empty string
  409.     ClearDice
  410.     DO
  411.         semicolon = INSTR(text$, ";")
  412.         IF semicolon THEN
  413.             l$ = LEFT$(text$, semicolon - 1)
  414.             text$ = MID$(text$, semicolon + 1)
  415.         ELSE
  416.             l$ = text$
  417.         END IF
  418.  
  419.         'IF DiagRollEM THEN PRINT "PROCESSING: "; l$
  420.  
  421.         found = 0
  422.         s = INSTR(l$, "S"): IF s THEN found = found + 1
  423.         d = INSTR(l$, "D"): IF d THEN found = found + 1
  424.         t = INSTR(l$, "T"): IF t THEN found = found + 1
  425.         b = INSTR(l$, "B"): IF b THEN found = found + 1
  426.         IF found <> 1 THEN EXIT SUB 'we should only find ONE element each pass, and there should always be one.  IF not, somebody screwed up.
  427.  
  428.         IF s THEN
  429.             DiceToRoll.Set = VAL(LEFT$(l$, s - 1))
  430.             IF DiagRollEm THEN PRINT "Number of Sets = "; DiceToRoll.Set
  431.             m$ = MID$(l$, s + 1)
  432.  
  433.             pass = 0
  434.             DO UNTIL m$ = ""
  435.                 pass = pass + 1
  436.                 'IF DiagRollEM THEN PRINT "SUBPROC "; m$
  437.                 n$ = LEFT$(m$, 1)
  438.                 IF n$ = "K" OR n$ = "D" THEN n$ = LEFT$(m$, 2)
  439.                 m$ = MID$(m$, LEN(n$) + 1)
  440.                 v = VAL(m$)
  441.                 SELECT CASE n$
  442.                     CASE "+" '+ set mod
  443.                         IF v > 0 THEN DiceToRoll.SetMod = v
  444.                         IF DiagRollEm THEN PRINT "Set Mod"; v
  445.                     CASE "-" ' - set mod
  446.                         IF v > 0 THEN DiceToRoll.SetMod = -v
  447.                         IF DiagRollEm THEN PRINT "Set Mod"; v
  448.                     CASE "KH" 'keep high
  449.                         IF v > 0 THEN DiceToRoll.SetKeepHigh = v
  450.                         IF DiagRollEm THEN PRINT "Keep the Highest"; v; " Sets"
  451.                     CASE "KL"
  452.                         IF v > 0 THEN DiceToRoll.SetKeepLow = v
  453.                         IF DiagRollEm THEN PRINT "Keep the Lowest"; v; " Sets"
  454.                     CASE "DH"
  455.                         IF v > 0 THEN DiceToRoll.SetDiscardHigh = v
  456.                         IF DiagRollEm THEN PRINT "Discard the Highest"; v; " Sets"
  457.                     CASE "DL"
  458.                         IF v > 0 THEN DiceToRoll.SetDiscardLow = v
  459.                         IF DiagRollEm THEN PRINT "Discard the Lowest"; v; " Sets"
  460.                     CASE "R", "O" 'reroll or open roll
  461.                         finished = 0: t$ = "": innerpass = 0
  462.                         DO UNTIL finished
  463.                             innerpass = innerpass + 1
  464.                             v = VAL(m$)
  465.                             IF v <> 0 THEN 'it's an o/r followed by a number
  466.                                 t$ = t$ + "=" + _TRIM$(STR$(v)) + ","
  467.                             ELSE
  468.                                 n1$ = LEFT$(m$, 1)
  469.                                 SELECT CASE n1$
  470.                                     CASE "="
  471.                                         m$ = MID$(m$, 2)
  472.                                         v = VAL(m$)
  473.                                         t$ = t$ + "=" + _TRIM$(STR$(v)) + ","
  474.                                     CASE "<"
  475.                                         m$ = MID$(m$, 2)
  476.                                         v = VAL(m$)
  477.                                         t$ = t$ + "<" + _TRIM$(STR$(v)) + ","
  478.                                     CASE ">"
  479.                                         m$ = MID$(m$, 2)
  480.                                         v = VAL(m$)
  481.                                         t$ = t$ + ">" + _TRIM$(STR$(v)) + ","
  482.                                     CASE ","
  483.                                         m$ = MID$(m$, 2)
  484.                                     CASE ELSE 'a character not a number, or =<>,
  485.                                         finished = -1
  486.                                 END SELECT
  487.                             END IF
  488.                             StripNumber m$
  489.                             IF n$ = "R" THEN
  490.                                 DiceToRoll.SetReRoll = t$
  491.                                 IF DiagRollEm THEN PRINT "Reroll Sets "; DiceToRoll.SetReRoll
  492.                             ELSE
  493.                                 DiceToRoll.SetOpenRoll = t$
  494.                                 IF DiagRollEm THEN PRINT "Openroll Sets "; DiceToRoll.SetOpenRoll
  495.                             END IF
  496.                             IF m$ = "" THEN finished = -1
  497.                             IF innerpass > 255 THEN IF DiagRollEm THEN PRINT "Error -- Too many loops processing Set ReRoll or OpenRoll": EXIT FUNCTION
  498.                         LOOP
  499.                 END SELECT
  500.                 StripNumber m$
  501.                 n$ = LEFT$(m$, 1)
  502.                 SELECT CASE n$
  503.                     CASE "K", "D", "R", "O", "+", "-" 'see if it's another command without a comma
  504.                     CASE ELSE
  505.                         comma = INSTR(m$, ",")
  506.                         IF comma THEN m$ = MID$(m$, comma + 1)
  507.                 END SELECT
  508.                 IF pass > 100 THEN IF DiagRollEm THEN PRINT "Error - endless processing loop deciphering SET information": EXIT FUNCTION
  509.             LOOP
  510.         END IF
  511.  
  512.  
  513.         IF d THEN
  514.             v = VAL(LEFT$(l$, d))
  515.             IF v < 1 THEN DiceToRoll.NumberOfDice = 1 ELSE DiceToRoll.NumberOfDice = v
  516.             IF DiagRollEm THEN PRINT "Number of Dice To Roll = "; DiceToRoll.NumberOfDice
  517.             m$ = MID$(l$, d + 1)
  518.  
  519.             v = VAL(m$)
  520.             IF v > 0 THEN DiceToRoll.DiceSides = v
  521.             StripNumber m$
  522.             IF DiagRollEm THEN PRINT "Dice Sides = "; DiceToRoll.DiceSides
  523.             pass = 0
  524.             DO UNTIL m$ = ""
  525.                 pass = pass + 1
  526.                 'IF DiagRollEM THEN PRINT "SUBPROC "; m$
  527.                 n$ = LEFT$(m$, 1)
  528.                 IF n$ = "K" OR n$ = "D" THEN n$ = LEFT$(m$, 2)
  529.                 m$ = MID$(m$, LEN(n$) + 1)
  530.                 v = VAL(m$)
  531.                 SELECT CASE n$
  532.                     CASE "+" '+ set mod
  533.                         IF v > 0 THEN DiceToRoll.DiceMod = v
  534.                         IF DiagRollEm THEN PRINT "DM"; v
  535.                     CASE "-" ' - set mod
  536.                         IF v > 0 THEN DiceToRoll.DiceMod = -v
  537.                         IF DiagRollEm THEN PRINT "DM"; v
  538.                     CASE "KH" 'keep high
  539.                         IF v > 0 THEN DiceToRoll.DiceKeepHigh = v
  540.                         IF DiagRollEm THEN PRINT "DKH"; v
  541.                     CASE "KL"
  542.                         IF v > 0 THEN DiceToRoll.DiceKeepLow = v
  543.                         IF DiagRollEm THEN PRINT "DKL"; v
  544.                     CASE "DH"
  545.                         IF v > 0 THEN DiceToRollDiceDiscardHigh = v
  546.                         IF DiagRollEm THEN PRINT "DDH"; v
  547.                     CASE "DL"
  548.                         IF v > 0 THEN DiceToRoll.DiceDiscardLow = v
  549.                         IF DiagRollEm THEN PRINT "DDL"; v
  550.                     CASE "R", "O" 'reroll or open roll
  551.                         finished = 0: t$ = "": innerpass = 0
  552.                         DO UNTIL finished
  553.                             innerpass = innerpass + 1
  554.                             v = VAL(m$)
  555.                             IF v <> 0 THEN 'it's an o/r followed by a number
  556.                                 t$ = t$ + "=" + _TRIM$(STR$(v)) + ","
  557.                             ELSE
  558.                                 n1$ = LEFT$(m$, 1)
  559.                                 SELECT CASE n1$
  560.                                     CASE "="
  561.                                         m$ = MID$(m$, 2)
  562.                                         v = VAL(m$)
  563.                                         t$ = t$ + "=" + _TRIM$(STR$(v)) + ","
  564.                                     CASE "<"
  565.                                         m$ = MID$(m$, 2)
  566.                                         v = VAL(m$)
  567.                                         t$ = t$ + "<" + _TRIM$(STR$(v)) + ","
  568.                                     CASE ">"
  569.                                         m$ = MID$(m$, 2)
  570.                                         v = VAL(m$)
  571.                                         t$ = t$ + ">" + _TRIM$(STR$(v)) + ","
  572.                                     CASE ","
  573.                                         m$ = MID$(m$, 2)
  574.                                     CASE ELSE 'a character not a number, or =<>,
  575.                                         finished = -1
  576.                                 END SELECT
  577.                             END IF
  578.                             StripNumber m$
  579.                             IF n$ = "R" THEN
  580.                                 DiceToRoll.DiceReroll = t$
  581.                                 IF DiagRollEm THEN PRINT "DR: "; DiceToRoll.DiceReroll
  582.                             ELSE
  583.                                 DiceToRoll.DiceOpenRoll = t$
  584.                                 IF DiagRollEm THEN PRINT "DO: "; DiceToRoll.DiceOpenRoll
  585.                             END IF
  586.                             IF m$ = "" THEN finished = -1
  587.                             IF innerpass > 255 THEN IF DiagRollEm THEN PRINT "Error -- Too many loops processing Dice ReRoll or OpenRoll": EXIT FUNCTION
  588.                         LOOP
  589.                 END SELECT
  590.                 StripNumber m$
  591.                 n$ = LEFT$(m$, 1)
  592.                 SELECT CASE n$
  593.                     CASE "K", "D", "R", "O", "+", "-" 'see if it's another command without a comma
  594.                     CASE ELSE
  595.                         comma = INSTR(m$, ",")
  596.                         IF comma THEN m$ = MID$(m$, comma + 1)
  597.                 END SELECT
  598.                 IF pass > 100 THEN IF DiagRollEm THEN PRINT "Error - endless processing loop deciphering SET information": EXIT FUNCTION
  599.             LOOP
  600.         END IF
  601.  
  602.  
  603.         IF t THEN
  604.             DiceToRoll.TotalMod = VAL(MID$(l$, 2))
  605.             IF DiagRollEm THEN PRINT "Dice Total Modifier = "; DiceToRoll.TotalMod
  606.         END IF
  607.  
  608.         IF b THEN
  609.             Brief = VAL(MID$(l$, 2))
  610.             IF DiagRollEm THEN PRINT "Roll Information Displayed: ";
  611.             SELECT CASE Brief
  612.                 CASE 0: IF DiagRollEm THEN PRINT "Full"
  613.                 CASE 1: IF DiagRollEm THEN PRINT "Reduced"
  614.                 CASE 2: IF DiagRollEm THEN PRINT "Final Results Only"
  615.             END SELECT
  616.         END IF
  617.  
  618.     LOOP UNTIL l$ = text$
  619.     RollEm$ = DiceRoll$
  620.  

If you want to make this a library for your own usage, just strip it out at line 70 or so, and save it as whatever library name you want.  "DiceRoller.BM" works for me, but apparently my naming sense sucks...

At that point, usage is rather simple:

'$INCLUDE:'whatever_you_named_your_library.BM" would go at the bottom of your code, and then you can simply use:

result$ = RollEm$(whatever_to_roll$)



This little library is able to do just about anything you'd need it to do, as far as dice rolling for RPGs go, as long as you follow the basic syntax and send it a proper string.

First Concept of Library: Our "dice rolling formula" is broken down into segments seperated by a semicolon.

Our 4 basic segments are:
Sets
Dice
Total mod
Brief output

For Sets, the syntax is:
##S  -- the number of sets, followed by S

For Dice, the syntax is:
##D##  -- the number of dice, followed by D, followed by the sides on the dice.  NOTE: the first set of numbers are optional, so you could simply use D6 to roll a single six sided dice.

For Total modifed, the syntax is:
T##  -- T followed by the total to add or subtract to the total dice roll

For Brief output, the syntax is:
##B  -- the number to represent how little output we want, followed by B
0 = full output
1 = reduced output
2 = minimal output (basically only the final results)

Second Concept of Library: Our segments can be further limited by optional parameters

+## (or -##) -- add (or subtract) number  to segment

KH## -- Keep the Highest number of "segment"
KL## -- Keep the Lowest number of "segment"
DH## -- Discard the Highest number of "segment"
DL## -- Discard the Lowest number of "segment"

R + stuff -- Reroll according to stuff
O + stuff-- Openroll according to stuff

stuff -- A string composed of numbers, operators,  and commas, to represent what to reroll or openroll.  It sounds complex, but its not.

R1  -- Reroll all 1's
R=2,=3  -- Reroll all rolls that are equal to 2 and equal to 3
R<4 -- Reroll all rolls that are less than 4

O1<2>3 -- (a silly rule set, but hey, it's an example)... Openroll all 1's, all numbers less than two, and all numbers greater than 3

Putting it all together:

In the end, what we end up with is formulas which look like the following:

3S;2D10 -- Roll 3 sets; of 2 10-sided dice.

6S;4D6KH3 -- Roll 6 sets; of 4 6-sided dice, keeping the 3 highest rolls

10SKH1KL1;2D10+2 -- Roll 10 sets of dice and only keep the highest set and the lowest set; of 2 10-sided dice, and add 2 to each dice.

Depending on what you want, you can generate some rather impressive formulas and take all the bite out of the dice rolling process completely for your games.

NOTE: Spaces are optional, so if they help you understand your "dice rolling formulas" better, feel free to use them:

4S; 3D10 KH2 O20 R2 ; T1; B2 -- Roll 4 sets of; 3 10-sided dice, keeping the 2 highest dice, and openrolling if the dice total to 20, and rerolling if the dice total to 2; then add 1 to the final total; and all we want to see are the final results...
Title: Re: Steve's Ole Dice Roller
Post by: STxAxTIC on February 01, 2019, 09:08:55 am
Quote
The GUI makes use of "The LIBRARY WHICH SHALL NOT BE NAMED", and it seems as if its name offends people
I have no idea who those "people" are who would get offended by the existence a properly-named library. Sheesh, I'd love to meet them!

I hope the re-roll mechanism doesn't offend those same people. Below they can prove that re-rolling a number is equivalent to simply not having that number in the first place. (I'm saying there's a ton of potential to fix up your re-roll feature if you insist on keeping it in place of trivial remapping.)

Title: Re: Steve's Ole Dice Roller
Post by: SMcNeill on February 01, 2019, 09:39:12 am
Quote
The GUI makes use of "The LIBRARY WHICH SHALL NOT BE NAMED", and it seems as if its name offends people
I have no idea who those "people" are who would get offended by the existence a properly-named library. Sheesh, I'd love to meet them!

I hope the re-roll mechanism doesn't offend those same people. Below they can prove that re-rolling a number is equivalent to simply not having that number in the first place. (I'm saying there's a ton of potential to fix up your re-roll feature if you insist on keeping it in place of trivial remapping.)

Remapping is fine, if all you want to do is change the dice themselves...  But, what do you do when you want to reroll the total of a set of dice? 

I'll keep it the way it is, for my personal use.  Use it or not, the choice is yours. 

It's open source; feel free to download a copy and make whatever changes you think are necessary so it'll run better for your personal programming experience.  If you can make it better, then more power to ya!  ;D