Role Playing Dice Roller

So I’m writing an Applescript to roll virtual polyhedral dice.

This is where I am …

rollAgain()

on rollAgain()
	-- List of dice counts (1 to 10, adjust as needed)
	set diceOptions to {"1", "2", "3", "4", "5", "6", "7"}
	set sidesOptions to {"3", "4", "6", "8", "10", "12", "20", "26"}
	set diceChoice to choose from list diceOptions with prompt "Role playing games normally use dice in their mechanics. So first choose the number of dice you'd like to roll ..." default items {"2"} without multiple selections allowed
	if diceChoice is false then return
	set sidesChoice to choose from list sidesOptions with prompt "Now choose the number of sides that the dice should have ..." default items {"26"} with multiple selections allowed
	if sidesChoice is false then return
	set diceCount to (item 1 of diceChoice) as integer
	set sidedness to (item 1 of sidesChoice) as integer
	rolldice(diceCount, sidedness)
end rollAgain

on rolldice(diceCount, sidedness)
	set r to return
	set eventList to {}
	repeat diceCount times
		set end of eventList to (random number from 1 to sidedness)
	end repeat
	-- Convert the dice count to words
	set diceCountInWords to numberToWord(diceCount)
	set longestNumber to 0
	repeat with i from 1 to count of eventList
		set currentNumber to (item i of eventList) as string
		if (length of currentNumber) > longestNumber then
			set longestNumber to length of currentNumber
		end if
	end repeat
	set formattedResults to ""
	repeat with i from 1 to count of eventList
		set currentResult to (item i of eventList) as string
		set padding to (longestNumber - (length of currentResult))
		set paddedResult to spacePadding(padding) & currentResult
		set formattedResults to formattedResults & "Dice " & i & ": " & paddedResult & r & r
	end repeat
	set dialogResult to display dialog "You rolled " & diceCountInWords & " " & sidedness & "-sided dice" & r & r & formattedResults buttons {"Thanks", "Roll Again"} default button "Thanks"
	if button returned of dialogResult is "Roll Again" then
		rollAgain() -- Call the main function again
	else
		return -- Exit the script
	end if
end rolldice

on spacePadding(padding)
	set spaceString to ""
	repeat padding times
		set spaceString to spaceString & " "
	end repeat
	return spaceString
end spacePadding

on numberToWord(n)
	if n = 1 then
		return "one"
	else if n = 2 then
		return "two"
	else if n = 3 then
		return "three"
	else if n = 4 then
		return "four"
	else if n = 5 then
		return "five"
	else if n = 6 then
		return "six"
	else if n = 7 then
		return "seven"
	else if n = 8 then
		return "eight"
	else if n = 9 then
		return "nine"
	else if n = 10 then
		return "ten"
	else
		return n as text
	end if
end numberToWord

Broadly speaking, I’m very happy with the script. It does what it needs to do. Having said this there are a few improvements that I’d like to make, and if anyone could help, I’d be very grateful.

  1. I want to change the buttons that appear in the number of dice selector dialog box to read ‘cancel’ which will end the script, and ‘Next Step’ which will take the script to the next stage.

  2. I also want to change the buttons that appear in the number of sides to the dice selector dialog box to read ‘cancel’ which will end the script, and ‘Roll’ which will activate the script.

Those changes are pretty simple I think.

I wondered if it’s possible if it’s possible at stage two of the script to have graphics of the various polyhedral dice rather than a drop down box? I’m not sure it is but I thought I’d ask anyway.

Lastly is there a way to embed a graphic at the start of the script - like a PNG that I create? Within the final app this would be embedded although for the script I could link to a file on my Mac? Are there specs for how big/small, resolution and so on for this graphic?

1 Like
  1. I want to change the buttons that appear in the number of dice selector dialog box to read ‘cancel’ which will end the script, and ‘Next Step’ which will take the script to the next stage.

Add the parameters OK button name and cancel button name as appropriate

	set diceChoice to choose from list diceOptions with prompt "Role playing games normally use dice in their mechanics. So first choose the number of dice you’d like to roll …" default items {"2"} OK button name "Next Step" cancel button name "Cancel" without multiple selections allowed
  1. I also want to change the buttons that appear in the number of sides to the dice selector dialog box to read ‘cancel’ which will end the script, and ‘Roll’ which will activate the script.

Same as above

	set sidesChoice to choose from list sidesOptions with prompt "Now choose the number of sides that the dice should have …" OK button name "Roll!" cancel button name "Cancel" default items {"26"} with multiple selections allowed
1 Like

Just as an aside, you can also streamline your numberToWords handler via something like:

on numberToWord(n)
	set wordList to {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"}
	try
		return item n of wordList
	on error
		return n as text
	end try
end numberToWord

not that the original is slow to run, I just a preference against lots of else if’s

1 Like

Thanks for that @Mockman (Sorry I forgot to format correctly first time around).

Thanks to you too @Camelot for both your suggestions. Both work brilliantly - the first solves the problem and the second is also much neater, less is definitely more with Applescript I think.

Now I just need to crack the rest of the query, namely:

If it’s possible to have graphics of the various polyhedral dice rather than a drop down box at stage two of the script?

And whether there is a way to embed a graphic at the start - like a PNG or a JPG?

My assumption is that within the final app this would be embedded although for the raw script I"m assuming that I could link to a file on my Mac? Also, are there specs for how big/small, resolution and so on for this graphic?

There’s no option within choose from list for any display options such as graphics, pop-ups, etc.

There are some third party tools which can create more complex dialogs. Check out Dialog Toolkit Plus for a starter.

1 Like

Cloudwalker_3. I don’t know how to do the first item above. The second item can be done with Shane’s script libraries (as noted by Camelot). Another option is the open-source swiftDialog app (here). The following handler uses swiftDialog and is copy-and-paste compatible with your script:

rollAgain()

on rollAgain()
	set theMessage to quoted form of "Role playing games normally use dice in their mechanics. So, first choose the number of dice you'd like to roll. Then choose the number of sides that the dice should have."
	set theIcon to quoted form of "/Users/Robert/Downloads/dice.png" --set to desired value
	
	try
		set userInput to do shell script "/usr/local/bin/dialog --title 'Roll the Dice' --titlefont 'size=16' --message " & theMessage & " --messagefont 'size=14' --button1text 'Roll' --button2 --moveable --icon " & theIcon & " --iconsize 100 --width 550 --height 240 --selecttitle 'Dice Number' --selectvalues '1,2,3,4,5,6,7' --selectdefault '2' --selecttitle 'Dice Sides' --selectvalues '3,4,6,8,10,12,20,26' --selectdefault '26'"
	on error
		error number -128
	end try
	
	set {TID, text item delimiters} to {text item delimiters, {return, " : "}}
	set diceCount to (text 2 thru -2 of text item 2 of userInput) as integer
	set sidedness to (text 2 thru -2 of text item 6 of userInput) as integer
	set text item delimiters to TID
	return {diceCount, sidedness} --just for testing
	--rolldice(diceCount, sidedness) --enable for actual script
end rollAgain

The dialog created by the above script:

I’m not addressing your questions here. I think you’ve received some good responses to those. But you reminded me of one of the first pieces of code I ever wrote! I wrote a similar app so long ago that I wrote it in cartridge basic on a PCjr.

But when playing RPGs the last thing I wanted to do was clicking around on a computer. Instead, I went with generating a screen-full of every reasonably possible roll that I might need. d2-100, 1 thru 10 d8, and other common multi-die rolls, common checks and other frequent random die rolls. Hit the space bar and regenerate everything. Random encounters, treasure, etc all populate and I could just select the one I needed. Just another idea…

Here’s a slightly more flexible version of your script above if you’re interested. It allows a variable die count for each die type.

It returns something like:

Rolling 3 d3: 1, 2, 3. Totaling 6
Rolling 2 d6: 4, 4. Totaling 8
Rolling 6 d10: 4, 3, 10, 9, 4, 5. Totaling 35
Rolling 8 d20: 1, 2, 18, 16, 16, 13, 2, 18. Totaling 86

on run
	repeat
		try
			set choosenDice to choose from list {"d3", "d4", "d6", "d8", "d10", "d12", "d20", "d24"} with title "   Choose the dice types to roll.   " with prompt "Choose dice" with multiple selections allowed
			set outputText to ""
			repeat with thisDie in choosenDice
				set thisDie to text 2 thru -1 of thisDie
				set dieCount to choose from list {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"} with title "   Choose the number of d" & thisDie with prompt "How many d" & thisDie & " do you want to roll?"
				set {rollResults, totalRolled} to RollMultipleDie(thisDie, dieCount)
				set AppleScript's text item delimiters to ", "
				set rollListText to rollResults as text
				set AppleScript's text item delimiters to ""
				set outputText to outputText & "Rolling " & dieCount & " d" & thisDie & ":    " & rollListText & ".  Totaling " & totalRolled & return
			end repeat
			set userResponse to button returned of (display dialog outputText with title "Please select" buttons {"Roll again", "Quit"})
			if userResponse is not "Roll again" then exit repeat
		on error
			exit repeat
		end try
	end repeat
end run

on RollMultipleDie(polygons, dieCount)
	set rollResults to {}
	set totalRolled to 0
	repeat with i from 1 to dieCount
		set thisDieRoll to (random number from 1 to polygons) as integer
		set the end of rollResults to thisDieRoll
		set totalRolled to totalRolled + thisDieRoll
	end repeat
	return {rollResults, totalRolled}
end RollMultipleDie

I thought I would take my swiftDialog suggestion one step further to include an output dialog with a markdown table. Just as a matter of personal preference, I included all seven dice in the table (regardless of the number of dice selected), but this is easily changed. A default icon is used if an icon is not set at the top of the rollTheDice handler.

The input dialog:

The output dialog:

The script:

--this script requires the open-source swiftDialog app

rollTheDice()

on rollTheDice()
	set theIcon to quoted form of "/Users/Robert/Downloads/dice.png" --set to desired value
	set theMessage to quoted form of "Role playing games normally use dice in their mechanics. Select the number of dice to roll and the number of sides of each dice."
	set userInput to do shell script "/usr/local/bin/dialog --title 'Roll the Dice' --titlefont 'size=16' --message " & theMessage & " --messagefont 'size=14' --button1text 'Roll' --button2 --moveable --icon " & theIcon & " --iconsize 80 --width 550 --height 240 --selecttitle 'Dice Number' --selectvalues '1,2,3,4,5,6,7' --selectdefault '2' --selecttitle 'Dice Sides' --selectvalues '3,4,6,8,10,12,20,26' --selectdefault '26'; echo $?"
	if userInput is "2" then error number -128
	set {TID, text item delimiters} to {text item delimiters, {return, " : "}}
	set diceCount to (text 2 thru -2 of text item 2 of userInput) as integer
	set diceSides to (text 2 thru -2 of text item 6 of userInput) as integer
	set text item delimiters to TID
	set theList to {}
	repeat with i from 1 to diceCount
		set end of theList to (random number from 1 to diceSides)
	end repeat
	displayResult(diceCount, diceSides, theList, theIcon)
end rollTheDice

on displayResult(diceCount, diceSides, theList, theIcon)
	set diceCountWord to item diceCount of {"one", "two", "three", "four", "five", "six", "seven"}
	set theText to "You rolled " & diceCountWord & " " & diceSides & "-sided dice and the results were:"
	set theHeader to "| dice 1 | dice 2 | dice 3 | dice 4 | dice 5 | dice 6 | dice 7 |"
	set theFormatter to "| :---: | :---: | :---: | :---: | :---: | :---: | :---: |"
	set theData to "| "
	repeat with i from 1 to (count theList)
		set theData to theData & item i of theList & " | "
	end repeat
	set theMessage to quoted form of (theText & return & theHeader & return & theFormatter & return & theData)
	set userInput to do shell script "/usr/local/bin/dialog --title 'Roll the Dice' --titlefont 'size=16' --message " & theMessage & " --messagefont 'size=14' --button1text 'Roll Again' --button2text 'Cancel' --moveable --icon " & theIcon & " --iconsize 80 --width 700 --height 210; echo $?"
	if userInput is "2" then error number -128
	rollTheDice()
end displayResult

The script included below also uses swiftDialog but does not require user input. The GKARC4RandomSource class is used to randomize the dice sides and is described in the documentation as:

A basic random number generator implementing the ARC4 algorithm, which is suitable for most gameplay mechanics.

A screenshot:

The script:

use framework "Foundation"
use framework "GameplayKit"
use scripting additions

rollTheDice()

on rollTheDice()
	set diceCount to 7 --can be changed but requires edit of markdown table code
	set diceSides to current application's NSArray's arrayWithArray:{3, 4, 6, 8, 10, 12, 20, 26} --set to desired value
	set theIcon to quoted form of "/Users/Robert/Downloads/dice.png" --set to desired value or "" for default icon
	
	set theData to current application's NSMutableString's new()
	repeat with i from 0 to ((diceSides's |count|()) - 1)
		set randomNumbers to getRandomNumbers(diceCount, diceSides's objectAtIndex:i)
		set aRow to (randomNumbers's componentsJoinedByString:" | ")
		set aRow to current application's NSString's stringWithFormat_("| **%@** | %@ |<br>", diceSides's objectAtIndex:i, aRow)
		(theData's appendString:aRow)
	end repeat
	
	set theText to "### Roll the Dice<br>Role playing games normally use dice in their mechanics. The following table contains the randomized sides of up to 7 dice with up to 26 sides."
	set theHeader to "| Sides | Dice 1 | Dice 2 | Dice 3 | Dice 4 | Dice 5 | Dice 6 | Dice 7 |"
	set theFormatter to "| :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |"
	set theMessage to quoted form of (theText & "<br>" & theHeader & "<br>" & theFormatter & "<br>" & (theData as text))
	set userInput to do shell script "/usr/local/bin/dialog --title none --message " & theMessage & " --messagefont 'size=14' --messagealignment center --messageposition center --button1text 'Roll Again' --button2text 'Cancel' --moveable --icon " & theIcon & " --iconsize 50 --centericon --width 640 --height 500; echo $?"
	if userInput is "2" then error number -128
	rollTheDice()
end rollTheDice

on getRandomNumbers(randomNumberCount, highestRandomNumber)
	set randomNumbers to current application's NSMutableArray's new()
	set randomDistribution to current application's GKRandomDistribution's distributionWithLowestValue:1 highestValue:highestRandomNumber
	repeat randomNumberCount times
		randomNumbers's addObject:(randomDistribution's nextInt())
	end repeat
	return randomNumbers
end getRandomNumbers