import numpy as np
import itertools
import time
import matplotlib.pyplot as plt

def traveling_salesman_permutations(coord):
    """
    Find the shortest path for a traveling salesman problem.
    Starts at the first city and ends at the last city.
    Uses permutations to find the optimal path.
    
    Parameters:
    coord: Nx2 array where each row represents (x, y) coordinates of a city
    
    Returns:
    Array listing cities in order of visits
    """
    # Convert to numpy array if not already
    coord = np.array(coord)
    
    # Number of cities
    n = len(coord)
    
    # Get all permutations of cities (excluding first and last which are fixed)
    # We fix the first city as start and last city as end
    cities = list(range(1, n-1))  # Cities 1 to n-2 (0-indexed)
    
    # Generate all permutations of the middle cities
    perms = list(itertools.permutations(cities))
    
    # Initialize variables to track best path
    best_distance = float('inf')
    best_path = None
    
    # Time the search
    start_time = time.time()
    
    # Check each permutation
    for perm in perms:
        # Create the complete path: start -> perm -> end
        path = [0] + list(perm) + [n-1]
        
        # Calculate total distance
        total_distance = 0
        for i in range(len(path) - 1):
            current_city = path[i]
            next_city = path[i+1]
            dx = coord[current_city, 0] - coord[next_city, 0]
            dy = coord[current_city, 1] - coord[next_city, 1]
            total_distance += np.sqrt(dx*dx + dy*dy)
        
        # Check if this is better
        if total_distance < best_distance:
            best_distance = total_distance
            best_path = path.copy()
    
    end_time = time.time()
    search_time = end_time - start_time
    
    print(f"Path searching time: {search_time:.6f} seconds")
    print(f"Best distance: {best_distance:.6f}")
    
    return best_path

# Example with 10 cities
if __name__ == "__main__":
    # Generate random coordinates for 10 cities
    np.random.seed(42)  # For reproducible results
    cities = np.random.rand(10, 2) * 100  # 10 cities with coordinates between 0 and 100
    
    print("Cities coordinates:")
    for i, (x, y) in enumerate(cities):
        print(f"City {i}: ({x:.2f}, {y:.2f})")
    
    # Find the best path
    best_path = traveling_salesman_permutations(cities)
    
    print(f"\nBest path: {best_path}")
    
    # Visualize the result
    plt.figure(figsize=(10, 8))
    
    # Plot cities
    x_coords = cities[:, 0]
    y_coords = cities[:, 1]
    
    # Plot the path
    path_x = [cities[i, 0] for i in best_path]
    path_y = [cities[i, 1] for i in best_path]
    
    # Plot path
    plt.plot(path_x, path_y, 'b-', linewidth=2, marker='o', markersize=8)
    
    # Mark start and end cities differently
    start_x, start_y = cities[best_path[0], 0], cities[best_path[0], 1]
    end_x, end_y = cities[best_path[-1], 0], cities[best_path[-1], 1]
    
    plt.scatter(start_x, start_y, c='green', s=200, marker='o', label='Start City', zorder=5)
    plt.scatter(end_x, end_y, c='red', s=200, marker='o', label='End City', zorder=5)
    
    # Mark all cities
    plt.scatter(x_coords, y_coords, c='blue', s=100, marker='o', label='Cities')
    
    # Add labels for cities
    for i in range(len(cities)):
        plt.annotate(str(i), (cities[i, 0], cities[i, 1]), xytext=(5, 5), textcoords='offset points')
    
    plt.title('Optimal Traveling Salesman Path')
    plt.xlabel('X Coordinate')
    plt.ylabel('Y Coordinate')
    plt.legend()
    plt.grid(True)
    plt.axis('equal')
    
    # Save the plot
    plt.savefig('salesman_path.png')
    plt.show()
