Hi all,

Have you ever heard of "Petals Around the Rose?" It's a logic puzzle. The way it works is, I roll five dice, then tell you what the "score" is for this round. I can repeat this for you as many times as you want. It's your job to figure out the algorithm that connects the roll of the dice to the score, and then determine the score for yourself on new rolls. There are three hints:

(i) the name of the puzzle is significant,
(ii) the answer will always be an even positive number or zero, and
(iii) people who know the secret are supposed to keep quiet about it!

So what we have here is an implementation of Petals Around the Rose, written using Tkinter. You click a button to roll the dice, which display in a canvas, along with the answer. Can you figure out the mystery?

I mean, you could just look at the code, but that would be cheating! Try to discern the pattern for yourself first by running the program, then peek under the hood.

# petals.py is an implementation of Petals Around the Rose
# -----------------------------------------------------------------------------
import sys, random
from Tkinter import *
# -----------------------------------------------------------------------------
# The title and version
title, version = "Petals Around the Rose", "v1.0"

# Lists of the possible faces of the dice and the colors
faces = [ 1, 2, 3, 4, 5, 6 ]
colors = [ ("#A54","#721"), ("#4A5","#172"), ("#45A","#127") ]

# A dictionary which maps die faces to dot configurations (offsets)
faces_to_dots = { 1:[(23,23)],
                  2:[(12,12), (33,33)],
                  3:[(23,23), (10,10), (36,36)],
                  4:[(12,12), (33,33), (12,33), (33,12)],
                  5:[(23,23), (10,10), (36,36), (10,36), (36,10)],
                  6:[(12,12), (33,33), (12,33), (33,12), (12,22), (33,22)] }

# -----------------------------------------------------------------------------
# The PetalEngine class is the application class
class PetalEngine:

    # Instance variables
    # root is the Tkinter root
    # frame is the main frame of the application
    # canvas is the canvas on which the roll results are drawn
    # answer is a string variable used to display the solution to the roll

    # Initialize new PetalEngines
    def __init__(self, root):
        self.root = root
        self.root.title(title+" "+version)
        self.root.resizable(0, 0)

    # Make and bind all Tkinter widgets
    def make_widgets(self):

        # Make the main frame
        self.frame = Frame(self.root)

        # Make the screen widget
        frame = Frame(self.frame)
        label = Label(frame, text="Roll Outcome")
        self.canvas = Canvas(frame, width=350, height=70)
        frame.pack(); label.pack(); self.canvas.pack()

        # Make the answer widget
        frame = Frame(self.frame)
        self.answer = StringVar()
        self.answer.set("The answer for this roll is:    ")
        entry = Entry(frame, textvariable=self.answer)
        frame.pack(fill="x", expand=True); entry.pack(fill="x")

        # Make the roller widget
        frame = Frame(self.frame)
        button = Button(frame, text="Roll Dice", command=self.roll)
        frame.pack(fill="x", expand=True); button.pack(fill="x")

    # Roll the dice
    def roll(self):

        # Pick the dice (faces and colors)
        dice = []
        for die in range(5):
            dice.append((random.sample(faces, 1)[0],
                         random.sample(colors, 1)[0]))

        # Print them to the screen
        for die in range(len(dice)):
            self.show_die(dice[die], 10+die*70)

        # Determine and print the answer
        solution = 0
        for die in dice:
            if die[0]%2 == 1: solution += (die[0]-1)
        self.answer.set("The answer for this roll is: "+str(solution))

    # Print a die on the screen
    def show_die(self, die, x_coordinate):
        x1, y1, x2, y2 = x_coordinate, 10, x_coordinate+50, 60
        self.canvas.create_rectangle(x1, y1, x2, y2, fill=die[1][1])
        self.canvas.create_rectangle(x1+5, y1+5, x2-5, y2-5, fill=die[1][0])
        for dot in faces_to_dots[die[0]]:
            self.canvas.create_oval(x1+dot[0], y1+dot[1],
                                    x1+dot[0]+5, y1+dot[1]+5, fill="white")
# -----------------------------------------------------------------------------
# Main function
def main(args):
    pe = PetalEngine(Tk())
# -----------------------------------------------------------------------------
# The following code is executed upon command-line invocation
if __name__ == "__main__": main(sys.argv)

# -----------------------------------------------------------------------------