Spaces:
Sleeping
Sleeping
| import numpy as np | |
| from collections import deque, Counter | |
| # --- Grid Transformation Functions --- | |
| def remove_vertical_lines(ctx): | |
| rows, cols = len(ctx.grid), len(ctx.grid[0]) | |
| for obj in ctx.objects: | |
| columns = {} | |
| for r, c in obj[2]: | |
| if c not in columns: | |
| columns[c] = [] | |
| columns[c].append(r) | |
| for c, rows_in_col in columns.items(): | |
| if len(rows_in_col) > 1: | |
| unique_vals = {ctx.grid[r][c] for r in rows_in_col} | |
| if len(unique_vals) == 1: | |
| for r in rows_in_col: | |
| ctx.grid[r][c] = 0 | |
| return ctx.grid | |
| def fill_object_interior(ctx): | |
| """ | |
| Fills the interior of each object in the GPContext grid. | |
| Assumes ctx.objects has been extracted already. | |
| """ | |
| rows, cols = len(ctx.grid), len(ctx.grid[0]) | |
| for obj in ctx.objects: | |
| min_r = min(r for r, c in obj) | |
| max_r = max(r for r, c in obj) | |
| min_c = min(c for r, c in obj) | |
| max_c = max(c for r, c in obj) | |
| obj_color = ctx.grid[min_r][min_c] | |
| fill_color = (obj_color + 1) % 9 or 1 # consistent color, avoid zero | |
| for r in range(min_r, max_r + 1): | |
| for c in range(min_c, max_c + 1): | |
| if (r, c) not in obj and ctx.grid[r][c] == 0: | |
| ctx.grid[r][c] = fill_color | |
| return ctx.grid | |
| def move_right_most_object(ctx): | |
| if not ctx.objects: | |
| return ctx.grid | |
| # Get rightmost object: object with largest column index | |
| rightmost_object = max(ctx.objects, key=lambda obj: max(y for x, y in obj[2])) | |
| _, value, block = rightmost_object | |
| for x, y in block: | |
| ctx.grid[x][y] = 0 | |
| shift = 0 | |
| cols = len(ctx.grid[0]) | |
| while True: | |
| can_move = True | |
| for x, y in block: | |
| new_y = y + shift + 1 | |
| if new_y >= cols or ctx.grid[x][new_y] != 0: | |
| can_move = False | |
| break | |
| if not can_move: | |
| break | |
| shift += 1 | |
| for x, y in block: | |
| ctx.grid[x][y + shift] = value | |
| return ctx.grid | |
| def move_left_most_object(ctx): | |
| if not ctx.objects: | |
| return ctx.grid | |
| # Get leftmost object: object with smallest column index | |
| leftmost_object = min(ctx.objects, key=lambda obj: min(y for x, y in obj[2])) | |
| _, value, block = leftmost_object | |
| for x, y in block: | |
| ctx.grid[x][y] = 0 | |
| shift = 0 | |
| while True: | |
| can_move = True | |
| for x, y in block: | |
| new_y = y - (shift + 1) | |
| if new_y < 0 or ctx.grid[x][new_y] != 0: | |
| can_move = False | |
| break | |
| if not can_move: | |
| break | |
| shift += 1 | |
| for x, y in block: | |
| ctx.grid[x][y - shift] = value | |
| return ctx.grid | |
| def move_bottom_most_object(ctx): | |
| if not ctx.objects: | |
| return ctx.grid | |
| # Get bottommost object (last one in the list after sorting by top_row) | |
| bottom_object = ctx.objects[-1] | |
| _, value, block = bottom_object | |
| # Remove it from the grid | |
| for x, y in block: | |
| ctx.grid[x][y] = 0 | |
| # Compute shift (same as top, move down) | |
| shift = 0 | |
| rows = len(ctx.grid) | |
| while True: | |
| can_move = True | |
| for x, y in block: | |
| new_x = x + shift + 1 | |
| if new_x >= rows or ctx.grid[new_x][y] != 0: | |
| can_move = False | |
| break | |
| if not can_move: | |
| break | |
| shift += 1 | |
| # Place object | |
| for x, y in block: | |
| ctx.grid[x + shift][y] = value | |
| return ctx.grid | |
| def detect_objects(grid): | |
| """ | |
| Detects objects in an ARC grid. | |
| Objects are contiguous regions of the same color (4-connected). | |
| Returns a list of objects, where each object is a set of (row, col) coordinates. | |
| """ | |
| rows, cols = len(grid), len(grid[0]) | |
| visited = set() | |
| objects = [] | |
| def bfs(start_r, start_c, color): | |
| """ Perform BFS to find all connected pixels of the same color """ | |
| queue = deque([(start_r, start_c)]) | |
| obj_pixels = set() | |
| while queue: | |
| r, c = queue.popleft() | |
| if (r, c) in visited: | |
| continue | |
| visited.add((r, c)) | |
| obj_pixels.add((r, c)) | |
| # Check 4-connected neighbors (up, down, left, right) | |
| for dr, dc in [(-1, 0), (1, 0), (0, -1), (0, 1)]: | |
| nr, nc = r + dr, c + dc | |
| if 0 <= nr < rows and 0 <= nc < cols and (nr, nc) not in visited: | |
| if grid[nr][nc] == color: | |
| queue.append((nr, nc)) | |
| return obj_pixels | |
| # Iterate over the grid to find objects | |
| for r in range(rows): | |
| for c in range(cols): | |
| if (r, c) not in visited and grid[r][c] != 0: # Ignore background (0) | |
| obj = bfs(r, c, grid[r][c]) | |
| objects.append(obj) | |
| return objects | |
| def highlight_detected_objects(grid): | |
| objects = detect_objects(grid) | |
| new_grid = [row[:] for row in grid] | |
| for idx, obj in enumerate(objects, start=1): | |
| for r, c in obj: | |
| new_grid[r][c] = (idx % 9) or 1 | |
| return new_grid | |
| def fill_object_interior(grid): | |
| """ Modifies the grid by filling the interiors of detected objects with a different color.""" | |
| objects = detect_objects(grid) | |
| rows, cols = len(grid), len(grid[0]) | |
| new_grid = [row[:] for row in grid] # Create a copy of the grid | |
| for obj in objects: | |
| min_r = min(r for r, c in obj) | |
| max_r = max(r for r, c in obj) | |
| min_c = min(c for r, c in obj) | |
| max_c = max(c for r, c in obj) | |
| # Find a new fill color (incrementing the current color modulo 9 for variation) | |
| obj_color = grid[min_r][min_c] | |
| fill_color = (obj_color + 1) % 9 if obj_color + 1 != 0 else 1 | |
| # Identify and fill the interior pixels of the object | |
| for r in range(min_r, max_r + 1): | |
| for c in range(min_c, max_c + 1): | |
| if (r, c) not in obj and grid[r][c] == 0: # Empty space inside the object | |
| new_grid[r][c] = fill_color | |
| return new_grid | |
| def diamirror(input_grid): | |
| return np.transpose(input_grid) | |
| import numpy as np | |
| def get_object_bounds(grid): | |
| grid = np.array(grid) | |
| top, bottom = None, None | |
| for i in range(grid.shape[0]): | |
| if np.any(grid[i] != 0): | |
| if top is None: | |
| top = i | |
| bottom = i | |
| return top, bottom | |
| def reverse_object_top_bottom(grid): | |
| grid = np.array(grid) | |
| top, bottom = get_object_bounds(grid) | |
| if top is None or bottom is None: | |
| return grid | |
| grid_copy = np.copy(grid) | |
| grid_copy[top:bottom+1] = np.flipud(grid[top:bottom+1]) | |
| return grid_copy | |
| def hmirror(input_grid: np.ndarray) -> np.ndarray: | |
| return np.fliplr(input_grid) | |
| def vmirrors(input_grid: np.ndarray) -> np.ndarray: | |
| return np.flipud(input_grid) | |
| def flip_horizontal(input_grid: np.ndarray) -> np.ndarray: | |
| return np.fliplr(input_grid) | |
| def flip_vertical(input_grid: np.ndarray) -> np.ndarray: | |
| return np.flipud(input_grid) | |
| def rotate_90(input_grid: np.ndarray) -> np.ndarray: | |
| return np.rot90(input_grid, k=-1) | |
| def rotate_180(input_grid: np.ndarray) -> np.ndarray: | |
| return np.rot90(input_grid, k=2) | |
| def rotate_270(input_grid: np.ndarray) -> np.ndarray: | |
| return np.rot90(input_grid, k=1) | |
| def identity(input_grid: np.ndarray) -> np.ndarray: | |
| return input_grid | |
| def find_center_pixel(grid): | |
| """Finds the center pixel of the input grid and returns it as a 1x1 output grid.""" | |
| center_index = len(grid[0]) // 2 # Get the middle index | |
| return [[grid[0][center_index]]] # Return as a 1x1 grid with the center pixel | |
| # --- Object Detection and Manipulation --- | |
| def detect_objects(grid): | |
| """Detects objects in the grid and returns a list of bounding boxes and pixel coordinates.""" | |
| height, width = len(grid), len(grid[0]) | |
| visited = set() | |
| objects = [] | |
| def bfs(r, c, color): | |
| """Finds all pixels belonging to an object using BFS.""" | |
| queue = [(r, c)] | |
| pixels = [] # Use a list instead of a set | |
| min_r, max_r, min_c, max_c = r, r, c, c | |
| while queue: | |
| x, y = queue.pop(0) | |
| if (x, y) in visited: | |
| continue | |
| visited.add((x, y)) | |
| pixels.append((x, y)) # Append to list | |
| min_r, max_r = min(min_r, x), max(max_r, x) | |
| min_c, max_c = min(min_c, y), max(max_c, y) | |
| for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: # Only vertical & horizontal connections | |
| nx, ny = x + dx, y + dy | |
| if (0 <= nx < height and 0 <= ny < width and (nx, ny) not in visited and grid[nx][ny] == color): | |
| queue.append((nx, ny)) | |
| return (min_r, max_r, min_c, max_c, color, pixels) # Return tuple, pixels as a list | |
| for r in range(height): | |
| for c in range(width): | |
| if grid[r][c] != 0 and (r, c) not in visited: | |
| visited.add((r, c)) | |
| objects.append(bfs(r, c, grid[r][c])) # Append tuple to list | |
| return objects # Ensure `objects` is a list, not a set | |
| def extract_bottom_object(grid): | |
| """Extracts the bottom-most object from the grid, crops it, and returns it as a new grid.""" | |
| objects = detect_objects(grid) | |
| if not objects: | |
| return grid | |
| bottom_object = max(objects, key=lambda obj: obj[1]) # obj[1] is max_r | |
| min_r, max_r, min_c, max_c, obj_color, pixels = bottom_object | |
| cropped_height = max_r - min_r + 1 | |
| cropped_width = max_c - min_c + 1 | |
| cropped_grid = np.zeros((cropped_height, cropped_width), dtype=int) | |
| for r, c in pixels: | |
| cropped_grid[r - min_r, c - min_c] = obj_color | |
| return cropped_grid.tolist() | |
| def keep_bottom_object(grid): | |
| """Keeps only the bottom-most object and removes all others.""" | |
| height, width = len(grid), len(grid[0]) | |
| objects = detect_objects(grid) | |
| output_grid = np.zeros((height, width), dtype=int) | |
| if not objects: | |
| return output_grid.tolist() | |
| bottom_object = max(objects, key=lambda obj: obj[1]) # obj[1] is max_r | |
| for r, c in bottom_object[5]: # obj[5] contains pixels | |
| output_grid[r][c] = bottom_object[4] # obj[4] is color | |
| return output_grid.tolist() | |
| def recolor_to_bottom_object(grid): | |
| """Recolors all objects to match the color of the bottom-most object.""" | |
| height, width = len(grid), len(grid[0]) | |
| objects = detect_objects(grid) | |
| output_grid = np.array(grid) | |
| if not objects: | |
| return output_grid.tolist() | |
| bottom_object = max(objects, key=lambda obj: obj[1]) # obj[1] is max_r | |
| bottom_color = bottom_object[4] # obj[4] is color | |
| for min_r, max_r, min_c, max_c, obj_color, pixels in objects: | |
| for r, c in pixels: | |
| output_grid[r][c] = bottom_color # Change to bottom-most object's color | |
| return output_grid.tolist() | |
| def remove_top_bottom_objects(grid): | |
| """Removes objects that touch either the top or bottom of the grid.""" | |
| height, width = len(grid), len(grid[0]) | |
| objects = detect_objects(grid) | |
| output_grid = np.zeros((height, width), dtype=int) | |
| if not objects: | |
| return output_grid.tolist() | |
| min_top = min(obj[0] for obj in objects) | |
| max_bottom = max(obj[1] for obj in objects) | |
| for (min_r, max_r, min_c, max_c, obj_color, pixels) in objects: | |
| if min_r == min_top or max_r == max_bottom: | |
| continue | |
| for r, c in pixels: | |
| output_grid[r][c] = obj_color | |
| return output_grid.tolist() | |
| def extract_topmost_object(grid): | |
| """Extracts the top-most object from the grid, crops it, and returns it as a new grid.""" | |
| objects = detect_objects(grid) | |
| if not objects: | |
| return grid | |
| topmost_object = min(objects, key=lambda obj: obj[0]) # obj[0] is min_r | |
| min_r, max_r, min_c, max_c, obj_color, pixels = topmost_object | |
| cropped_height = max_r - min_r + 1 | |
| cropped_width = max_c - min_c + 1 | |
| cropped_grid = np.zeros((cropped_height, cropped_width), dtype=int) | |
| for r, c in pixels: | |
| cropped_grid[r - min_r, c - min_c] = obj_color | |
| return cropped_grid.tolist() | |
| def swap_objects(grid): | |
| """Swaps detected objects in the grid.""" | |
| objects = detect_objects(grid) | |
| objects = sorted(objects, key=lambda obj: obj[1]) # Sort by vertical position | |
| object_positions = [obj[5] for obj in objects] # obj[5] contains pixels | |
| object_colors = [obj[4] for obj in objects] # obj[4] is color | |
| swapped_positions = object_positions[::-1] | |
| new_grid = np.zeros_like(grid) | |
| for color, new_positions in zip(object_colors, swapped_positions): | |
| for r, c in new_positions: | |
| new_grid[r][c] = color | |
| return new_grid.tolist() | |
| # --- Pixel & Color Manipulation --- | |
| def transform_blue_to_red(input_grid): | |
| """Transforms all blue (1) pixels to red (2).""" | |
| grid = np.array(input_grid) | |
| return np.where(grid == 1, 2, grid).tolist() | |
| def fill_downward(grid): | |
| """Fills non-zero pixels downward, propagating their colors downwards in each column.""" | |
| height, width = len(grid), len(grid[0]) | |
| output_grid = np.array(grid) | |
| for col in range(width): | |
| fill_color = 0 | |
| for row in range(height): | |
| if grid[row][col] != 0: | |
| fill_color = grid[row][col] | |
| if fill_color != 0: | |
| output_grid[row][col] = fill_color | |
| return output_grid.tolist() | |
| def remove_below_horizontal_line(grid): | |
| """Detects the first fully connected horizontal line and removes everything below it.""" | |
| height, width = len(grid), len(grid[0]) | |
| output_grid = np.array(grid) | |
| for row in range(height): | |
| if np.all(output_grid[row] != 0): | |
| output_grid[row + 1:] = 0 | |
| break | |
| return output_grid.tolist() | |
| def find_center_pixel(grid): | |
| """Finds the center of the grid and returns it as a 1x1 pixel grid.""" | |
| center_index = len(grid[0]) // 2 | |
| return [[grid[0][center_index]]] | |
| def extract_largest_row(grid): | |
| """Finds the row with the most non-zero elements and extracts it.""" | |
| grid = np.array(grid) | |
| max_length = 0 | |
| longest_row = [] | |
| for row in grid: | |
| row_values = row[row > 0] | |
| if len(row_values) > max_length: | |
| max_length = len(row_values) | |
| longest_row = row_values.tolist() | |
| return [longest_row] | |
| def extract_dominant_colors(grid): | |
| """Finds the two most dominant non-zero colors in the grid.""" | |
| flattened = [cell for row in grid for cell in row if cell != 0] | |
| color_counts = Counter(flattened) | |
| if not color_counts: | |
| return [[]] | |
| most_common_colors = [color for color, _ in color_counts.most_common(2)] | |
| return [[color for color in most_common_colors]] | |
| def remove_dominant_color(grid): | |
| """Removes the most dominant color from the grid.""" | |
| color_counts = Counter(cell for row in grid for cell in row if cell != 0) | |
| if color_counts: | |
| dominant_color = max(color_counts, key=color_counts.get) | |
| else: | |
| return grid | |
| return [[0 if cell == dominant_color else cell for cell in row] for row in grid] | |
| def find_least_dominant_pixel(grid): | |
| """Finds the least occurring non-zero pixel in the grid.""" | |
| pixel_counts = {} | |
| for row in grid: | |
| for value in row: | |
| if value != 0: | |
| pixel_counts[value] = pixel_counts.get(value, 0) + 1 | |
| if not pixel_counts: | |
| return None | |
| return min(pixel_counts, key=pixel_counts.get) | |
| def remove_least_dominant_pixel(grid): | |
| """Removes the least dominant pixel from the grid.""" | |
| rows, cols = len(grid), len(grid[0]) | |
| least_dominant_pixel = find_least_dominant_pixel(grid) | |
| if least_dominant_pixel is None: | |
| return grid | |
| new_grid = np.array(grid) | |
| for x in range(rows): | |
| for y in range(cols): | |
| if grid[x][y] == least_dominant_pixel: | |
| new_grid[x, y] = 0 | |
| return new_grid.tolist() | |
| def upscale(input_grid, upscale_factor=3): | |
| """Upscales the grid by expanding each pixel into a 3x3 block.""" | |
| def expand_pixel_with_grid(pixel, input_grid): | |
| if pixel == 0: | |
| return np.zeros((upscale_factor, upscale_factor), dtype=int) | |
| else: | |
| return input_grid | |
| input_rows, input_cols = len(input_grid), len(input_grid[0]) | |
| output_grid = np.zeros((input_rows * upscale_factor, input_cols * upscale_factor), dtype=int) | |
| for r in range(input_rows): | |
| for c in range(input_cols): | |
| expanded_block = expand_pixel_with_grid(input_grid[r][c], input_grid) | |
| output_grid[r * upscale_factor: (r + 1) * upscale_factor, c * upscale_factor: (c + 1) * upscale_factor] = expanded_block | |
| return output_grid | |
| def remove_center_object(grid): | |
| """Removes anything located at the center of the grid.""" | |
| height, width = len(grid), len(grid[0]) | |
| center_r, center_c = height // 2, width // 2 | |
| grid = np.array(grid) | |
| center_value = grid[center_r, center_c] | |
| if center_value != 0: | |
| grid[grid == center_value] = 0 | |
| return grid.tolist() | |
| import numpy as np | |
| def draw_horizontal_vertical(grid): | |
| """Adds a horizontal or vertical line of 8s based on object orientation.""" | |
| if grid is None or len(grid) == 0 or len(grid[0]) == 0: | |
| print("ERROR: Grid is empty. Cannot apply draw_horizontal_vertical.") | |
| return grid | |
| rows, cols = len(grid), len(grid[0]) | |
| print(f"Grid Shape Before Modification: {rows}x{cols}") # Debugging Info | |
| new_grid = np.array(grid) | |
| for r in range(rows): | |
| new_grid[r][-1] = 8 # Rightmost column | |
| for c in range(cols): | |
| new_grid[0][c] = 8 # Topmost row | |
| return new_grid.tolist() | |