Code summary – how it works and my logic in developing it
import random
I knew from the start that I’d need the random library which is a built in python function that randomly selects something (a cubing move in my case) from a dictionary that the programmer provides.
def get_yes_no(prompt):
while True:
response = input(prompt).strip().lower()
if response in ["yes", "y"]:
return True
elif response in ["no", "n"]:
return False
else:
print("Please enter 'yes' or 'no'.")
In my code, there were many stages where I needed to ask the user for yes or no. It was simple enough to define this function and was worth it for many different parts of my program.
def get_choice(prompt, valid_choices):
while True:
choice = input(prompt).strip().upper()
if choice in valid_choices:
return choice
else:
print(f"Invalid choice. Please choose from: {', '.join(valid_choices)}")
This code looks more complex, but all it does is ask the user for a choice and check if it is valid. If it’s not valid – it will tell the user and keep asking until the computer does. Another part of this function is that it breaks down the users response to lowercase and into lowercase to help interpret all cases of inputs. Regardless if the user input “Yes”, “yEs” or ” yes”, the code will work because before processing the code, the computer will remove all spaces before and after the word and making all of the text lowercase. This lets it check what the user actually meant by simplifying their response to the simple yes or no’s the computer can understand.
def show_orientation():
print("\n🧑🏫 Hold the cube like this:")
print(" - White face is UP")
print(" - Green face is FACING YOU (Front)")
print("\nCube Face Map:")
print(" [U]")
print(" [L] [F] [R] [B]")
print(" [D]")
print("U = Up, D = Down, F = Front, B = Back, L = Left, R = Right")
print()
The explanation for this is very simple. It is simply a function that will be putting the text inside the quotation marks on the user’s screen. The text that will be displayed gives a visual representation of the cube, and explains where each face (side of the cube – top, bottom , etc) is. Furthermore, it tells you the orientation to hold your cube (helpful if doing scrambling from advanced tools).
def show_visual_clue(face, direction):
arrows = {
"clockwise": "↻",
"counterclockwise": "↺",
"180": "⟲⟳"
}
print(f"\n🧩 Visual Clue for Move: {face}{direction}")
if direction == "":
print(f"Turn the {face} face 90° clockwise {arrows['clockwise']}")
elif direction == "'":
print(f"Turn the {face} face 90° counterclockwise {arrows['counterclockwise']}")
elif direction == "2":
print(f"Turn the {face} face 180° {arrows['180']}")
print()
Here I created another function that will visually show the user how to turn the cube depending on the type of move they have to do using arrows. These arrows are either clockwise, anticlockwise, or a double turn which can be done in either direction (clockwise or anticlockwise – same result). So this function will print the specific instructions per the user’s move when it is called upon.
def explain_move(move, variation, move_dict):
base = move
modifier = variation.replace(move, "")
print("\nExplanation:")
if modifier == "":
print(f"{move}: Turn the {move_dict[move]} 90 degrees clockwise.")
elif modifier == "'":
print(f"{move}': Turn the {move_dict[move]} 90 degrees counterclockwise.")
elif modifier == "2":
print(f"{move}2: Turn the {move_dict[move]} 180 degrees in either direction.")
This code simply teaches the user how to do a cubing move. This isn’t the same as the show_orientation function that will print the same thing repeatedly. The function here reads what the user wrote and delivers the moves accordingly depending on 2 factors (letter and modification following it). To eliminate the boring work of typing out the instructions for each move manually and to eliminate the possibility of a human error (eg. typo), I created this function to read what the user wrote and give them instructions based on the letter (R, U, etc) and modification following the letter (inverse, none, double turn).
show_visual_clue(base, modifier)
This line of code calls the function that teaches the user how to hold the cube and showing which face is which. The parameters it checks for are the base move (simple letter), and the modifier (nothing, inverse, double turn).
def learn_moves():
while True:
show_orientation()
move_type = get_choice("Choose move type to learn (basic/advanced): ", ["BASIC", "ADVANCED"])
#defining basic moves
if move_type == "BASIC":
basic_moves = {
"U": "Up face (Top Layer)",
"D": "Down face (Bottom Layer)",
"L": "Left face",
"R": "Right face",
"F": "Front face",
"B": "Back face"
}
This function asks user to choose between learning basic or advanced moves. Interestingly, I also used a previously defined function, “get_choice”, to ask the user which move set they wanted to learn. If the user asks to learn the basic set of moves, I print out a guide for the user which helps them understand which move they would like help learning. They have a choice between advanced and basic. If they know they want to learn the top layer, they can read the list provided, and understand that it is a “U move” they want to learn.
print("\nBasic Moves:")
for key, val in basic_moves.items():
print(f"{key}: {val}")
move = get_choice("Which basic move would you like to learn? (U/D/L/R/F/B): ", basic_moves.keys())
direction = get_choice(
f"Choose the variation of {move} (e.g., {move}, {move}', {move}2): ",
[move, move + "'", move + "2"]
)
explain_move(move, direction, basic_moves)
This code prints the basic move definitions on a new line. The second print function prints the basic letter moves with no modifications, and then prints the value (what it means – defined in the basic move list) after the colon between key and val. Then the computer gets the user choice (using our custom function) on which of the basic moves they would like to learn. After that the next step is to get the user choice on the specific modification of the move they’d like to learn (whether it has an apostrophe after it – is an inverse move, or a 2 after it – a double turn). The next line of code is to call the function explain_move and let it do its work.
elif move_type == "ADVANCED":
advanced_moves = {
"M": "Middle slice (between L and R)",
"E": "Equatorial slice (between U and D)",
"S": "Standing slice (between F and B)"
}
In the case the user asked to learn advanced moves when the program gave it a choice between basic and advanced, a variable is defined where there is a list for where the layers of the advanced moves are.
print("\nAdvanced Slice Moves:")
for key, val in advanced_moves.items():
print(f"{key}: {val}")
move = get_choice("Which advanced move would you like to learn? (M/E/S): ", advanced_moves.keys())
direction = get_choice(
f"Choose the variation of {move} (e.g., {move}, {move}', {move}2): ",
[move, move + "'", move + "2"]
)
explain_move(move, direction, advanced_moves)
again = get_yes_no("\nDo you want to learn another move? (yes/no): ")
if not again:
break
On a new line, we print the advanced moves variable (shows where the advanced layers are). Again, just like the basic moves, we are printing the advanced cubing letter (the key) and then the text that explains it (the val). This is all from the variable we defined for the advanced moves earlier. We then ask the user which advanced move it wants to learn, and once we have that, we ask which modification (e.g. double turn) they want to learn. Then we use the explain_move function that we defined earlier, to explain the move to them based on their input here. Now that they have learn this move, we ask them whether they are interested in learning another move. If they say no, we will break out of the learning cycle.
def practice_scramble():
cubing_moves = ["R", "R'", "R2", "L", "L'", "L2",
"B", "B'", "B2", "F", "F'", "F2",
"U", "U'", "U2", "D", "D'", "D2"]
last_axis = None
scramble = []
n = 1
I know it’s standard practise to define all the variables at the start of the code, but this is the code for a whole new task. This is to help the user practice their cubing moves so it is in this new section of code. Now getting into the code, I defined all the possible cubing moves that could be used in a scramble. It is important to note that although the advanced moves are not used for scrambling, they are used when memorising algorithms (applies to higher level cubers). The reason I have the axis is a bit complicated to explain, but to put it simply, I wanted to avoid redundancy in my scrambles. Having consecutive moves on the same axis could even cause a situation where you do a turn and then the next move asks you to promptly undo that. That’s why I avoided having this situation altogether – credit to gemini for the idea of making axis’s to circumvent this issue. I also have the blank square brackets for the scramble, because I wanted to print the moves one line instead of having a new line for every move generated (discovered through troubleshooting). N is being used as a counter for how many moves are being generated.
while n < 25:
random_move = random.choice(cubing_moves)
#defining the axis's
if random_move[0] in "RL":
axis = "RL"
elif random_move[0] in "BF":
axis = "BF"
else:
axis = "UD"
Once the program generates 25 valid moves, n will hit 25 and then the program will stop. The random_move selects a random letter from my list of cubing moves used in a scramble. After that we look at the first letter(index 0) of the user’s move they want to learn and based on which letter it contains, it will be grouped into an axis. A quick explanation, if two faces of the cube can turn the same direction, they are on the same axis.
if axis != last_axis:
scramble.append(random_move)
last_axis = axis
n += 1
If the axis is not the same as the axis of the last valid move, we can add it to the list of random_move. After that I increased n by 1, because if it passed the axis requirement or parameter, it is a valid move and the counter should increase.
print("\n👉 Step 1: Scramble your cube with this sequence:")
print(" ".join(scramble))
input("\nPress Enter once you finish scrambling...")
#reverse of cube moves for them to check if user followed along correctly
reverse_solution = []
for move in reversed(scramble):
if move.endswith("2"):
reverse_solution.append(move)
elif move.endswith("'"):
reverse_solution.append(move[0])
else:
reverse_solution.append(move[0] + "'")
On a new line we are telling the user to scramble their cube with our sequence (helping them practice). Then the program shows the user the scramble we have made. After that, the user is asked to press enter once they finish scrambling. Now we make a blank list where we will add a move one by one for reversing their scramble to see if they followed their moves correctly. The program checks what modifier or ending the move in the list “scramble” has, then depending on that, the reverse move list is generated. This is what I’m telling the computer to think: If the move ends in a 2, it stays as is and goes into the list, if it has an apostrophe, it needs a clockwise move, and if it is a regular move, it needs an anticlockwise move to create the reverse scramble.
midpoint = len(reverse_solution) // 2
step1 = reverse_solution[:midpoint]
step2 = reverse_solution[midpoint:]
print("\n👉 Step 2: Solve your cube in two phases.")
print("Phase 1 moves:", " ".join(step1))
input("Perform these moves, then press Enter...")
print("Phase 2 moves:", " ".join(step2))
input("Perform these moves, then press Enter...")
print("\n🎉 If you made no mistakes, your cube is solved!")
I decided to print out the reverse solution in two parts, because a list of 25 might be intimidating. I achieved this by finding the “midpoint”, half the length of the moves, and then putting them into two variables. After that it is a bunch of print statements that tells the user, their cube will be solved in 2 phases as they follow the moves and click enter. If they finish all of these with no mistakes, their cube should end up solved.
def main():
print("🧠 Welcome to Cubing Vocabulary & Practice Trainer!")
#checking conditions on what user wants to do with this tool
while True:
print("\nChoose an option:")
option1 = get_yes_no("Option 1: Do you want to learn cubing vocabulary? (yes/no): ")
option2 = get_yes_no("Option 2: Would you like to practice your cubing vocabulary with a scramble? (yes/no): ")
#subfunctions are called "learn_moves" or "practice_scramble"
if option1:
learn_moves()
if option2:
practice_scramble()
if not option1 and not option2:
print("\nThanks for cubing today! Goodbye 👋")
break
if __name__ == "__main__":
main()
This is the final segment of code. This is the main section, and to start off the user interface, I get the computer to print a welcome message that tells the user what this tool does. After this, I use some functions to figure out what exactly the user wants out of this tool. Learn moves and check cubing vocabulary are the two options for the user through the two questions the program asks. Depending on what the user wants, the computer will find the function that defines what it needs to do, and will help the user. If the user answers no to both questions (doesn’t want to learn, or use the tool to practice moves), I get the computer to display a goodbye message and the program ends.