8

I want to randomize a rubik's cube that is initialized as complete (all colors on the correct sides). I have move functions that rotate the cube. I want to randomly pick 50 functions in a row to properly randomize it.

I am doing this project to learn a little bit more about Python, since I mostly do C++ and I see there is no case/switch for Python, so I am trying a dictionary. When I make the dictionary, the code seems to execute for some reason:

def random_cube(self):
    scramble = {    0 : self.up_turn(),
                    1 : self.down_turn(),
                    2 : self.left_turn(),
                    3 : self.right_turn(),
                    4 : self.front_turn(),
                    5 : self.back_turn(),
                    6 : self.up_turn("inverted"),
                    7 : self.down_turn("inverted"),
                    8 : self.left_turn("inverted"),
                    9 : self.right_turn("inverted"),
                    10: self.front_turn("inverted"),
                    11: self.back_turn("inverted")
                }
    for x in range(50):
        i = random.randint(0,11)
        scramble[i]

So when I make this dictionary, it seems to run through and execute all 11 entries for some reason (I think). I can't seem to find a better way, at least more elegant than a long if/elif string of statements.

!EDIT: Implementing both suggestions, the ("inverted") flag for the functions are not being set by either suggestion. For example, calling 1 and 7 both give me down_turn, but the output shows that the flag was not set when it should have been on number 7.

Any ideas?

d d
  • 181
  • 1
  • 3
  • 14
  • 1
    As a design note, there are really only two different moves one can do on a rubik's cube. The rest is just a matter of how one orients the faces. It might be easier to write a function like so: `def rotate(face, clockWise = True):` Also as an alternative to using a dict and `for` loop, you _could_ make a list and use `random.choice` to select from it. – Joel Cornett Apr 29 '12 at 04:03
  • for the random.choice from list, I tinkered with that as well. I put the function calls in a list and tried that, but didn't quite work for me...Also, I set up the cube as to always be facing white as 'front'. I don't think that was the best way to represent it :\ – d d Apr 29 '12 at 04:29

2 Answers2

16

When you define the dict, it's actually calling the functions, and storing the return value in the dictionary. To just have the dictionary store a reference to the functions, you need to drop the trailing parentheses. So something like:

scramble = {  0: self.up_turn,
              1: self.down_turn,
              etc.

Then, at the bottom, call scramble[i]().

This will call the function with no arguments. To handle the case where you pass "inverted" as an argument, you could either define separate functions like up_turn_inverted(), etc., or you could have the dictionary store a 2-ple consisting of the function, and the argument, then call something liks scramble[i][0](scramble[i][1])

Update from suggestion in comments: You could also use lambda expressions to define the functions, particularly the ones that require an argument. This is basically equivalent to defining an up_turn_inverted function, but done in-place as an anonymous function. It would look like this:

6: lambda: self.up_turn("inverted")
7: lambda: self.down_turn("inverted")

etc.

happydave
  • 7,127
  • 1
  • 26
  • 25
  • 3
    Another option would be to use `lambda:` to create the inverted calls. – Amber Apr 29 '12 at 03:59
  • Or what about an in-line conditional? i.e. `scramble[i]( () if i < 6 else ("inverted"))` – machine yearning Apr 29 '12 at 04:09
  • 1
    As a general principle, making assumptions about the dictionary ordering seems like something that should be avoided if possible. I'd vote for the lambda solution. – happydave Apr 29 '12 at 04:12
  • Incidentally, the entire use of a dictionary isn't really necessary in this case - a list or tuple would save a bit of typing – happydave Apr 29 '12 at 04:13
  • Sorry, I'm not clear on what the lambda solution would be. Could you provide it please? – machine yearning Apr 29 '12 at 04:13
  • I added it to the end of the answer – happydave Apr 29 '12 at 04:17
  • Thank you happydave for the response. I have not used lambda functions in my short time on Python, I will have a good read on them right now. – d d Apr 29 '12 at 05:34
  • The lambda solution--as shown in the update--is basically the same as defining the above mentioned `up_turn_inverted()`. The lambda differs only in not giving the function any name and its body must be simpler (an expression only). – pepr Apr 29 '12 at 08:49
2

I believe this is called "functions as first-class values", implying most importantly that you can pass identifiers referring to functions as parameters to other functions.

When you define your dictionary, the Python interpreter evaluates the functions and stores the values in the dictionary. To put this off until you generate your random numbers, try instead storing references to the functions themselves in your dictionary, by leaving off the parentheses:

def random_cube(self):
    scramble = {    0 : self.up_turn,
                    1 : self.down_turn,
                    2 : self.left_turn,
                    3 : self.right_turn,
                    4 : self.front_turn,
                    5 : self.back_turn,
                    6 : self.up_turn,
                    7 : self.down_turn,
                    8 : self.left_turn,
                    9 : self.right_turn,
                    10: self.front_turn,
                    11: self.back_turn
                }

Then when calling your functions in the for loop you'll have to distinguish between your normal and inverted cases by which parameters you pass:

    for x in range(50):
        i = random.randint(0,11)
        if i <= 5:
            scramble[i]()
        else:
            scramble[i]("inverted")

or more simply:

    for x in range(50):
        i = random.randint(0,11)
        scramble[i]( () if i < 6 else ("inverted"))
machine yearning
  • 9,889
  • 5
  • 38
  • 51
  • thanks for the reply. I was not thinking about the interpreter having to evaluate in order to put into the dictionary value. – d d Apr 29 '12 at 05:33