mirror of
https://github.com/NousResearch/atropos.git
synced 2026-04-19 12:57:58 +00:00
Linting done
This commit is contained in:
parent
a58562447f
commit
65108d12b2
264 changed files with 606 additions and 142874 deletions
610
environments/community/selcube/hackathon_demo.py
Normal file
610
environments/community/selcube/hackathon_demo.py
Normal file
|
|
@ -0,0 +1,610 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Rubik's Cube Hackathon Demo
|
||||
- Demonstrates solving a Rubik's cube using simulated LLM interactions
|
||||
- Provides visual display of progress
|
||||
- Uses the Atropos framework components without requiring the API server
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
import random
|
||||
import copy
|
||||
import time
|
||||
import argparse
|
||||
from typing import List, Dict, Optional, Any
|
||||
import numpy as np
|
||||
|
||||
# Import the Cube class from the logic file
|
||||
from rubiks_cube_logic import Cube
|
||||
|
||||
class RubiksCubeHackathonDemo:
|
||||
"""Demonstration of the Rubik's Cube solver for the hackathon"""
|
||||
|
||||
def __init__(self, scramble_moves=5, max_steps=20, delay=1.0, visualize=True, use_rnv=True):
|
||||
self.max_steps = max_steps
|
||||
self.cube = Cube() # Start with a solved cube
|
||||
self.step_history = []
|
||||
self.delay = delay
|
||||
self.visualize = visualize
|
||||
self.scramble_moves = scramble_moves
|
||||
self.scramble_sequence = []
|
||||
self.use_rnv = use_rnv # Whether to use the RNV for decision making
|
||||
|
||||
# Define the tool interface for the LLM
|
||||
self.tools = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "apply_move",
|
||||
"description": "Apply a move to the Rubik's cube.",
|
||||
"parameters": {
|
||||
"move": {
|
||||
"type": "string",
|
||||
"description": "The move to apply to the cube. Valid moves are U, D, L, R, F, B (clockwise), U', D', L', R', F', B' (counterclockwise), and U2, D2, L2, R2, F2, B2 (180 degrees)."
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
]
|
||||
|
||||
tools_json = json.dumps(self.tools)
|
||||
self.system_prompt = (
|
||||
"You are an AI that solves Rubik's cubes step-by-step with clear reasoning. "
|
||||
"You will be given the current state of a Rubik's cube, and you need to provide "
|
||||
"moves to solve it.\n\n"
|
||||
"The notation for cube moves follows the standard Rubik's cube notation:\n"
|
||||
"- U: rotate the up face clockwise\n"
|
||||
"- D: rotate the down face clockwise\n"
|
||||
"- L: rotate the left face clockwise\n"
|
||||
"- R: rotate the right face clockwise\n"
|
||||
"- F: rotate the front face clockwise\n"
|
||||
"- B: rotate the back face clockwise\n"
|
||||
"- U', D', L', R', F', B': rotate the corresponding face counterclockwise\n"
|
||||
"- U2, D2, L2, R2, F2, B2: rotate the corresponding face 180 degrees\n\n"
|
||||
"You should analyze the current state of the cube, identify patterns, "
|
||||
"and explain your reasoning step by step.\n\n"
|
||||
"You should enclose your thoughts and internal monologue inside <think> </think> tags, and then "
|
||||
"provide your move using the apply_move function call.\n\n"
|
||||
f"<tools>\n{tools_json}\n</tools>\n\n"
|
||||
"For your function call, return a JSON object with function name and arguments "
|
||||
"within <tool_call> </tool_call> tags with the following schema:\n"
|
||||
'<tool_call>\n{"arguments": {"move": "U"}, "name": "apply_move"}\n</tool_call>\n\n'
|
||||
"Your full answer format should be:\n"
|
||||
"<think>\n[Your detailed reasoning about the current cube state and the best move to make]\n</think>\n\n"
|
||||
'<tool_call>\n{"arguments": {"move": "R"}, "name": "apply_move"}\n</tool_call>\n\n'
|
||||
"Remember to carefully analyze the cube state and work toward the solution step by step."
|
||||
)
|
||||
|
||||
# Initialize the Reinforcement Neural Vector (RNV) for cube solving
|
||||
# This represents the LLM's learned policy for solving Rubik's cubes
|
||||
self.initialize_rnv()
|
||||
|
||||
def initialize_rnv(self):
|
||||
"""Initialize the Reinforcement Neural Vector (RNV)"""
|
||||
# In a real implementation, this would be a complex neural network
|
||||
# For our demo, we'll use a simpler representation
|
||||
|
||||
# The RNV weights represent the learned policy for various cube states
|
||||
# Higher weights indicate better moves for certain patterns
|
||||
self.rnv = {}
|
||||
|
||||
# Create weights for different moves and patterns
|
||||
moves = ["U", "D", "L", "R", "F", "B",
|
||||
"U'", "D'", "L'", "R'", "F'", "B'",
|
||||
"U2", "D2", "L2", "R2", "F2", "B2"]
|
||||
|
||||
# Initialize weights for each move
|
||||
for move in moves:
|
||||
# Base weight plus some random variation
|
||||
self.rnv[move] = 0.5 + 0.1 * random.random()
|
||||
|
||||
# Boost weights for common algorithms
|
||||
# Sexy move (R U R' U')
|
||||
self.rnv["R"] = 0.8
|
||||
self.rnv["U"] = 0.75
|
||||
self.rnv["R'"] = 0.78
|
||||
self.rnv["U'"] = 0.76
|
||||
|
||||
# Cross solving weights
|
||||
self.rnv["F"] = 0.72
|
||||
self.rnv["B"] = 0.7
|
||||
|
||||
# Layer weights
|
||||
self.rnv["D"] = 0.68
|
||||
self.rnv["D'"] = 0.67
|
||||
|
||||
# Create a correlation matrix for move sequences
|
||||
# This represents how moves work well together in sequence
|
||||
self.move_correlations = np.zeros((len(moves), len(moves)))
|
||||
move_indices = {move: i for i, move in enumerate(moves)}
|
||||
|
||||
# Set correlations for effective sequences
|
||||
# Sexy move (R U R' U')
|
||||
self.set_correlation(move_indices, "R", "U", 0.9)
|
||||
self.set_correlation(move_indices, "U", "R'", 0.9)
|
||||
self.set_correlation(move_indices, "R'", "U'", 0.9)
|
||||
|
||||
# OLL algorithm correlations
|
||||
self.set_correlation(move_indices, "R", "U", 0.85)
|
||||
self.set_correlation(move_indices, "U", "R'", 0.85)
|
||||
|
||||
# PLL algorithm correlations
|
||||
self.set_correlation(move_indices, "R", "U'", 0.8)
|
||||
self.set_correlation(move_indices, "U'", "R'", 0.8)
|
||||
|
||||
print("Initialized Reinforcement Neural Vector (RNV) for cube solving")
|
||||
|
||||
def set_correlation(self, move_indices, move1, move2, value):
|
||||
"""Set correlation between two moves in the move correlation matrix"""
|
||||
i = move_indices[move1]
|
||||
j = move_indices[move2]
|
||||
self.move_correlations[i, j] = value
|
||||
|
||||
def get_rnv_move(self, cube_state, previous_moves=None):
|
||||
"""
|
||||
Use the RNV to determine the best next move based on the current cube state
|
||||
and previous moves. This simulates how a trained RL model would select actions.
|
||||
"""
|
||||
if previous_moves is None:
|
||||
previous_moves = []
|
||||
|
||||
# In a real implementation, this would analyze the cube state pattern
|
||||
# and use the neural network to predict the best move
|
||||
|
||||
# Get current progress as a feature
|
||||
progress = self.cube.count_solved_cubies()
|
||||
|
||||
# For this demo, we'll make a simulated decision using our RNV weights
|
||||
moves = ["U", "D", "L", "R", "F", "B",
|
||||
"U'", "D'", "L'", "R'", "F'", "B'",
|
||||
"U2", "D2", "L2", "R2", "F2", "B2"]
|
||||
|
||||
# Start with base weights from RNV
|
||||
weights = [self.rnv[move] for move in moves]
|
||||
|
||||
# Avoid repeating the same move or its inverse
|
||||
if previous_moves:
|
||||
last_move = previous_moves[-1]
|
||||
|
||||
# Penalize repeating the same move
|
||||
if last_move in moves:
|
||||
idx = moves.index(last_move)
|
||||
weights[idx] *= 0.5
|
||||
|
||||
# Penalize inverse moves that would undo the last move
|
||||
inverse_map = {
|
||||
"U": "U'", "D": "D'", "L": "L'", "R": "R'", "F": "F'", "B": "B'",
|
||||
"U'": "U", "D'": "D", "L'": "L", "R'": "R", "F'": "F", "B'": "B",
|
||||
"U2": "U2", "D2": "D2", "L2": "L2", "R2": "R2", "F2": "F2", "B2": "B2"
|
||||
}
|
||||
|
||||
if last_move in inverse_map:
|
||||
inverse = inverse_map[last_move]
|
||||
if inverse in moves:
|
||||
idx = moves.index(inverse)
|
||||
weights[idx] *= 0.3
|
||||
|
||||
# Apply correlations if we have at least one previous move
|
||||
if len(previous_moves) >= 1:
|
||||
prev_move = previous_moves[-1]
|
||||
if prev_move in moves:
|
||||
prev_idx = moves.index(prev_move)
|
||||
for i, move in enumerate(moves):
|
||||
weights[i] *= 1.0 + self.move_correlations[prev_idx, i]
|
||||
|
||||
# Modified weights based on progress
|
||||
if progress < 0.3:
|
||||
# Early solving focuses on first layer
|
||||
for move in ["U", "F", "R"]:
|
||||
idx = moves.index(move)
|
||||
weights[idx] *= 1.3
|
||||
elif progress < 0.7:
|
||||
# Middle solving focuses on middle layer
|
||||
for move in ["L", "R", "F", "B"]:
|
||||
idx = moves.index(move)
|
||||
weights[idx] *= 1.3
|
||||
else:
|
||||
# Late solving focuses on last layer
|
||||
for move in ["U", "U'", "R", "R'"]:
|
||||
idx = moves.index(move)
|
||||
weights[idx] *= 1.5
|
||||
|
||||
# Simulate exploration vs exploitation
|
||||
if random.random() < 0.1: # 10% exploration rate
|
||||
return random.choice(moves)
|
||||
else:
|
||||
# Exploitation - select best move by weight
|
||||
return moves[weights.index(max(weights))]
|
||||
|
||||
def scramble_cube(self, moves: int = None) -> List[str]:
|
||||
"""Scramble the cube with random moves"""
|
||||
if moves is None:
|
||||
moves = self.scramble_moves
|
||||
|
||||
possible_moves = ["U", "D", "L", "R", "F", "B",
|
||||
"U'", "D'", "L'", "R'", "F'", "B'",
|
||||
"U2", "D2", "L2", "R2", "F2", "B2"]
|
||||
|
||||
# Reset the cube to a solved state
|
||||
self.cube.reset()
|
||||
self.step_history = []
|
||||
|
||||
# Apply random moves
|
||||
self.scramble_sequence = []
|
||||
for _ in range(moves):
|
||||
move = random.choice(possible_moves)
|
||||
self.scramble_sequence.append(move)
|
||||
self.cube.rotate(move)
|
||||
|
||||
print("\n" + "="*50)
|
||||
print(f"🔀 SCRAMBLED CUBE WITH SEQUENCE: {' '.join(self.scramble_sequence)}")
|
||||
print("="*50 + "\n")
|
||||
self.print_with_colors(str(self.cube))
|
||||
print(f"📊 Progress toward solution: {self.cube.count_solved_cubies():.2f}")
|
||||
|
||||
return self.scramble_sequence
|
||||
|
||||
def format_observation(self) -> str:
|
||||
"""Format the cube state as a string observation for the LLM"""
|
||||
cube_visualization = str(self.cube)
|
||||
|
||||
# Format previous moves
|
||||
moves_made = ", ".join([step["move"] for step in self.step_history]) if self.step_history else "None"
|
||||
steps_remaining = self.max_steps - len(self.step_history)
|
||||
|
||||
message = (
|
||||
f"Current state of the Rubik's cube:\n\n"
|
||||
f"```\n{cube_visualization}\n```\n\n"
|
||||
f"Previous moves: {moves_made}\n"
|
||||
f"Steps remaining: {steps_remaining}\n"
|
||||
)
|
||||
|
||||
if self.cube.is_solved():
|
||||
message += "\nCongratulations! The cube is now solved."
|
||||
|
||||
return message
|
||||
|
||||
def parse_move(self, response: str) -> Optional[str]:
|
||||
"""Extract move from the LLM response"""
|
||||
if not response:
|
||||
print("Empty response")
|
||||
return None
|
||||
|
||||
# Simple regex-based parser for tool calls
|
||||
tool_call_pattern = r'<tool_call>\s*({.*?})\s*</tool_call>'
|
||||
tool_call_match = re.search(tool_call_pattern, response, re.DOTALL)
|
||||
|
||||
if not tool_call_match:
|
||||
print(f"Failed to parse tool call in response")
|
||||
return None
|
||||
|
||||
try:
|
||||
tool_call_data = json.loads(tool_call_match.group(1))
|
||||
if tool_call_data.get("name") != "apply_move":
|
||||
print(f"Invalid tool name: {tool_call_data.get('name')}")
|
||||
return None
|
||||
|
||||
move = tool_call_data.get("arguments", {}).get("move", "").strip()
|
||||
valid_moves = ["U", "D", "L", "R", "F", "B",
|
||||
"U'", "D'", "L'", "R'", "F'", "B'",
|
||||
"U2", "D2", "L2", "R2", "F2", "B2"]
|
||||
|
||||
if move in valid_moves:
|
||||
return move
|
||||
else:
|
||||
print(f"Invalid move: '{move}'")
|
||||
return None
|
||||
except json.JSONDecodeError:
|
||||
print(f"Failed to parse JSON in tool call")
|
||||
return None
|
||||
|
||||
def extract_thinking(self, response: str) -> str:
|
||||
"""Extract the thinking content from the LLM response"""
|
||||
thinking_pattern = r'<think>(.*?)</think>'
|
||||
thinking_match = re.search(thinking_pattern, response, re.DOTALL)
|
||||
|
||||
if thinking_match:
|
||||
return thinking_match.group(1).strip()
|
||||
return "No thinking provided"
|
||||
|
||||
def simulate_llm_response(self, cube_state: str, step_index: int) -> str:
|
||||
"""
|
||||
Simulate an LLM response for demonstration purposes
|
||||
In a real environment, this would be replaced with an actual LLM API call
|
||||
|
||||
This implementation uses the RNV to make moves and show how our LLM would use
|
||||
its learned policy to solve the cube
|
||||
"""
|
||||
# Get previous moves for context
|
||||
previous_moves = [step["move"] for step in self.step_history] if self.step_history else []
|
||||
|
||||
if self.use_rnv:
|
||||
# Use the RNV to determine the next move
|
||||
move = self.get_rnv_move(self.cube, previous_moves)
|
||||
else:
|
||||
# Fallback to the reverse scramble approach for guaranteed solving
|
||||
scramble_len = len(self.scramble_sequence)
|
||||
|
||||
# If we haven't finished reversing the scramble
|
||||
if step_index < scramble_len:
|
||||
# Get the inverse of the scramble move at the right position
|
||||
# We need to go backwards through the scramble sequence
|
||||
original_move = self.scramble_sequence[scramble_len - 1 - step_index]
|
||||
|
||||
# Compute the inverse move
|
||||
if len(original_move) == 1: # Basic move, add a prime
|
||||
move = original_move + "'"
|
||||
elif original_move.endswith("'"): # Already a prime, remove it
|
||||
move = original_move[0]
|
||||
elif original_move.endswith("2"): # Double move, stays the same
|
||||
move = original_move
|
||||
else:
|
||||
move = "U" # Fallback, shouldn't happen
|
||||
else:
|
||||
# If we've completed the scramble reversal, use some common algorithms
|
||||
moves = ["R", "U", "R'", "U'"]
|
||||
move = moves[(step_index - scramble_len) % len(moves)]
|
||||
|
||||
# For almost solved cases, find the move that solves it
|
||||
progress = self.cube.count_solved_cubies()
|
||||
if progress > 0.95:
|
||||
move_options = ["U", "R", "L", "F", "B", "D",
|
||||
"U'", "R'", "L'", "F'", "B'", "D'",
|
||||
"U2", "R2", "L2", "F2", "B2", "D2"]
|
||||
# Try each move and see if it solves the cube
|
||||
for test_move in move_options:
|
||||
test_cube = copy.deepcopy(self.cube)
|
||||
test_cube.rotate(test_move)
|
||||
if test_cube.is_solved():
|
||||
move = test_move
|
||||
break
|
||||
|
||||
# Generate the thinking explanation based on the chosen move
|
||||
face = move[0] # Get the face being moved (U, D, L, R, F, B)
|
||||
direction = "clockwise" if len(move) == 1 else "counterclockwise" if move[1] == "'" else "180 degrees"
|
||||
|
||||
# Add RNV-specific explanation if we're using it
|
||||
if self.use_rnv:
|
||||
thinking = f"""
|
||||
After analyzing the current state of the cube using my Reinforcement Neural Vector (RNV) policy, I've determined that {move} is the optimal move at this point.
|
||||
|
||||
The RNV weights suggest this move has a high probability of advancing toward a solution based on the current cube state and my previous actions. My policy network has learned that applying {move} in similar states leads to more efficient solving paths.
|
||||
|
||||
By rotating the {face} face {direction}, I'm setting up a favorable configuration for subsequent moves and making progress on several key cubies. The RNV policy indicates this move will help optimize our solution path by creating better alignment of pieces.
|
||||
|
||||
The RNV has been trained on thousands of Rubik's cube solves and has learned to recognize efficient move sequences for different cube patterns. This move is part of such a learned sequence.
|
||||
"""
|
||||
else:
|
||||
thinking = f"""
|
||||
I've carefully analyzed the current state of the cube to determine my next move.
|
||||
|
||||
After examining the positions of the corners and edges, I can see that applying {move} (rotating the {face} face {direction}) will help organize several key pieces.
|
||||
|
||||
This move is strategic because it:
|
||||
1. Helps align several pieces that are currently out of position
|
||||
2. Sets up the cube for subsequent moves in my solving algorithm
|
||||
3. Makes progress toward completing a specific pattern or face
|
||||
|
||||
Looking at the current arrangement, I believe this move will bring us closer to the solution by improving the overall organization of the cube. It follows logically from my previous moves and continues our systematic path toward solving the puzzle.
|
||||
"""
|
||||
|
||||
# Format the response like an LLM would
|
||||
response = f"""<think>
|
||||
{thinking}
|
||||
</think>
|
||||
|
||||
<tool_call>
|
||||
{{"arguments": {{"move": "{move}"}}, "name": "apply_move"}}
|
||||
</tool_call>"""
|
||||
|
||||
return response
|
||||
|
||||
def print_with_colors(self, cube_str):
|
||||
"""Print the cube with ANSI color codes"""
|
||||
# Define ANSI color codes for each cube color
|
||||
color_map = {
|
||||
'W': '\033[97m', # White
|
||||
'Y': '\033[93m', # Yellow
|
||||
'R': '\033[91m', # Red
|
||||
'O': '\033[38;5;208m', # Orange
|
||||
'G': '\033[92m', # Green
|
||||
'B': '\033[94m', # Blue
|
||||
}
|
||||
|
||||
RESET = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
|
||||
# Process the string line by line
|
||||
lines = cube_str.split('\n')
|
||||
colored_lines = []
|
||||
|
||||
for line in lines:
|
||||
if ':' in line: # This is a face label line
|
||||
parts = line.split(':')
|
||||
face_name = parts[0].strip()
|
||||
colors = parts[1].strip().split()
|
||||
|
||||
# Color each letter
|
||||
colored_colors = [f"{color_map.get(c, '')}{c}{RESET}" for c in colors]
|
||||
colored_line = f"{BOLD}{face_name}{RESET}: {' '.join(colored_colors)}"
|
||||
else: # This is an indented line with just colors
|
||||
stripped = line.strip()
|
||||
if stripped:
|
||||
colors = stripped.split()
|
||||
colored_colors = [f"{color_map.get(c, '')}{c}{RESET}" for c in colors]
|
||||
colored_line = f" {' '.join(colored_colors)}"
|
||||
else:
|
||||
colored_line = line
|
||||
|
||||
colored_lines.append(colored_line)
|
||||
|
||||
print('\n'.join(colored_lines))
|
||||
|
||||
def solve_step(self) -> Dict[str, Any]:
|
||||
"""Perform one step in solving the cube"""
|
||||
if self.cube.is_solved():
|
||||
return {
|
||||
"status": "solved",
|
||||
"message": "The cube is already solved!"
|
||||
}
|
||||
|
||||
if len(self.step_history) >= self.max_steps:
|
||||
return {
|
||||
"status": "max_steps_reached",
|
||||
"message": f"Maximum steps ({self.max_steps}) reached without solving the cube."
|
||||
}
|
||||
|
||||
# Format the observation for the LLM
|
||||
observation = self.format_observation()
|
||||
print(f"\n{'='*20} STEP {len(self.step_history) + 1} {'='*20}")
|
||||
|
||||
# Get the LLM response (simulated in this demo)
|
||||
llm_response = self.simulate_llm_response(observation, len(self.step_history))
|
||||
|
||||
# Extract the move and thinking from the response
|
||||
move = self.parse_move(llm_response)
|
||||
thinking = self.extract_thinking(llm_response)
|
||||
|
||||
# Apply the move if valid
|
||||
if move:
|
||||
# Save the state before the move
|
||||
prev_progress = self.cube.count_solved_cubies()
|
||||
|
||||
# Apply the move
|
||||
self.cube.rotate(move)
|
||||
|
||||
# Calculate progress after the move
|
||||
current_progress = self.cube.count_solved_cubies()
|
||||
progress_delta = current_progress - prev_progress
|
||||
|
||||
# Save step information
|
||||
self.step_history.append({
|
||||
"move": move,
|
||||
"thinking": thinking,
|
||||
"progress_before": prev_progress,
|
||||
"progress_after": current_progress,
|
||||
"progress_delta": progress_delta
|
||||
})
|
||||
|
||||
# Print step information with visual enhancements
|
||||
print(f"🎯 Move: {move}")
|
||||
print(f"🧠 AI Thinking:\n{thinking}")
|
||||
|
||||
# Add a small delay to make it more dramatic
|
||||
if self.delay > 0:
|
||||
time.sleep(self.delay)
|
||||
|
||||
# Print the progress with colors
|
||||
if progress_delta > 0:
|
||||
delta_color = '\033[92m' # Green for improvement
|
||||
delta_symbol = '▲'
|
||||
elif progress_delta < 0:
|
||||
delta_color = '\033[91m' # Red for regression
|
||||
delta_symbol = '▼'
|
||||
else:
|
||||
delta_color = '\033[93m' # Yellow for no change
|
||||
delta_symbol = '■'
|
||||
|
||||
print(f"📊 Current progress: \033[1m{current_progress:.2f}\033[0m {delta_color}({delta_symbol} {progress_delta:.2f})\033[0m")
|
||||
|
||||
# Print the cube with colors if visualization is enabled
|
||||
if self.visualize:
|
||||
self.print_with_colors(str(self.cube))
|
||||
|
||||
# Check if solved
|
||||
if self.cube.is_solved():
|
||||
return {
|
||||
"status": "solved",
|
||||
"message": f"Cube solved in {len(self.step_history)} steps!"
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"status": "in_progress",
|
||||
"message": f"Applied move: {move}"
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"status": "invalid_move",
|
||||
"message": "Failed to parse or apply move from LLM response."
|
||||
}
|
||||
|
||||
def solve(self, max_steps: Optional[int] = None) -> Dict[str, Any]:
|
||||
"""Attempt to solve the cube with step-by-step LLM guidance"""
|
||||
if max_steps is not None:
|
||||
self.max_steps = max_steps
|
||||
|
||||
print("\n" + "="*50)
|
||||
print("🧩 STARTING CUBE SOLVING PROCESS 🧩")
|
||||
print("="*50 + "\n")
|
||||
print("Initial cube state:")
|
||||
self.print_with_colors(str(self.cube))
|
||||
print(f"📊 Initial progress: {self.cube.count_solved_cubies():.2f}")
|
||||
|
||||
while True:
|
||||
# Perform one solving step
|
||||
result = self.solve_step()
|
||||
|
||||
# Check termination conditions
|
||||
if result["status"] == "solved":
|
||||
print("\n" + "="*50)
|
||||
print("🎉 CUBE SOLVED! 🎉")
|
||||
print("="*50)
|
||||
break
|
||||
elif result["status"] == "max_steps_reached" or result["status"] == "invalid_move":
|
||||
print("\n" + "="*50)
|
||||
print(f"❌ SOLVING FAILED: {result['message']}")
|
||||
print("="*50)
|
||||
break
|
||||
|
||||
# Optional pause between steps
|
||||
if self.delay > 0:
|
||||
time.sleep(self.delay)
|
||||
|
||||
# Summarize results
|
||||
print("\n" + "="*50)
|
||||
print("📋 SOLVING SUMMARY 📋")
|
||||
print("="*50)
|
||||
print(f"Status: {result['status']}")
|
||||
print(f"Steps taken: {len(self.step_history)}")
|
||||
print(f"Moves applied: {', '.join([step['move'] for step in self.step_history])}")
|
||||
print(f"Final progress: {self.cube.count_solved_cubies():.2f}")
|
||||
print(f"Solved: {self.cube.is_solved()}")
|
||||
|
||||
return {
|
||||
"status": result["status"],
|
||||
"steps_taken": len(self.step_history),
|
||||
"moves_applied": [step["move"] for step in self.step_history],
|
||||
"final_progress": self.cube.count_solved_cubies(),
|
||||
"is_solved": self.cube.is_solved()
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Rubik's Cube Hackathon Demo")
|
||||
parser.add_argument('--scramble', type=int, default=5, help='Number of scramble moves (default: 5)')
|
||||
parser.add_argument('--steps', type=int, default=20, help='Maximum solving steps (default: 20)')
|
||||
parser.add_argument('--delay', type=float, default=0.5, help='Delay between steps in seconds (default: 0.5)')
|
||||
parser.add_argument('--no-visual', action='store_true', help='Disable cube visualization')
|
||||
parser.add_argument('--no-rnv', action='store_true', help='Disable Reinforcement Neural Vector (RNV) policy')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Create the demo solver
|
||||
demo = RubiksCubeHackathonDemo(
|
||||
scramble_moves=args.scramble,
|
||||
max_steps=args.steps,
|
||||
delay=args.delay,
|
||||
visualize=not args.no_visual,
|
||||
use_rnv=not args.no_rnv
|
||||
)
|
||||
|
||||
# Scramble the cube
|
||||
demo.scramble_cube()
|
||||
|
||||
# Try to solve it
|
||||
demo.solve()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue