Petals Around the Rose

G-Do 0 Tallied Votes 882 Views Share

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.

#!/usr/bin/python
# petals.py is an implementation of Petals Around the Rose
# -----------------------------------------------------------------------------
import sys, random
from Tkinter import *
# -----------------------------------------------------------------------------
# GLOBALS
# 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)] }

# -----------------------------------------------------------------------------
# CLASSES
# 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)
        self.make_widgets()
        self.root.mainloop()

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

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

        # 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)
        entry.config(state=DISABLED)
        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")
        
# -----------------------------------------------------------------------------
# FUNCTIONS
# Main function
def main(args):
    pe = PetalEngine(Tk())
    sys.exit(0)
    
# -----------------------------------------------------------------------------
# The following code is executed upon command-line invocation
if __name__ == "__main__": main(sys.argv)

# -----------------------------------------------------------------------------
# EOF

Dani AI

Generated

Nice, compact Tkinter demo from that pairs a simple GUI with the classic Petals Around the Rose puzzle. The program draws five dice on a Canvas and displays a numeric "score" after each roll, which makes it a good starting point for both learning the puzzle and experimenting with a small Tk UI.

Key clarifications and quick improvements:

  • Scoring can be isolated into a pure function (for example, compute_score(faces)) so the logic is testable independently of the GUI. That also makes it trivial to add unit tests that enumerate possible rolls.
  • Clear the canvas before drawing a new roll to avoid overpainting: self.canvas.delete("all") at the start of the roll routine.
  • Use clearer random calls and Python 3 compatibility: prefer random.choice(...) or random.randint(1, 6) instead of sampling, and change from Tkinter import * to import tkinter as tk (update widget names accordingly).
  • Use Label or an Entry with state="readonly" for the answer field so the result looks like output rather than a disabled form control.
  • Replace magic numbers (tile size, spacing, dot offsets) with named constants or computed values so the widget scales cleanly and is easier to tweak.

Small UX and maintainability notes:

  • Avoid calling mainloop() inside the class initializer to keep the class usable from other code or tests.
  • Consider binding the spacebar or Enter to roll for keyboard accessibility and add a brief roll animation for polish.
  • Keep color values readable (full six-digit hex) and store configuration (colors, tile size, spacing, face-to-dot mapping) near the top so the demo is easy to adapt.
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.