### Kaggle Submission: Dependency Installation Source: https://context7.com/johnlikescarrot/arc3-submission-stochastic-goose/llms.txt Installs necessary competition dependencies using pip. The `--no-index` and `--find-links` flags ensure packages are installed from the provided local wheel files. ```bash # Install competition dependencies !pip install --no-index --find-links \ /kaggle/input/competitions/arc-prize-2026-arc-agi-3/arc_agi_3_wheels \ arc-agi python-dotenv ``` -------------------------------- ### Setup Experiment Directory Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb Creates a timestamped directory for experiment outputs and logging. Use this to organize runs. ```python def setup_experiment_directory(base_output_dir='runs'): """Create directories for outputs and logging.""" timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") base_dir = os.path.join(base_output_dir, timestamp) os.makedirs(base_dir, exist_ok=True) log_file = os.path.join(base_dir, 'logs.log') print(f"Experiment directory created: {base_dir}") return base_dir, log_file ``` -------------------------------- ### Install ARC-AGI and Dependencies Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb Installs the 'arc-agi' library and its dependencies from local wheel files. Ensure the path to the wheel files is correct. ```python !pip install --no-index --find-links \ /kaggle/input/competitions/arc-prize-2026-arc-agi-3/arc_agi_3_wheels \ arc-agi python-dotenv ``` -------------------------------- ### Setup Logging for Experiment Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb Configures the root logger to write logs to a specified file within the experiment directory. Ensures logs are captured for the current run. ```python def setup_logging_for_experiment(log_file_path): """Update logging configuration to use the experiment directory's log file.""" root_logger = logging.getLogger() for handler in root_logger.handlers[:]: if isinstance(handler, logging.FileHandler): root_logger.removeHandler(handler) handler.close() formatter = logging.Formatter("%(asctime)s | %(levelname)s | %(message)s") file_handler = logging.FileHandler(log_file_path, mode="w") file_handler.setLevel(root_logger.level) file_handler.setFormatter(formatter) root_logger.addHandler(file_handler) ``` -------------------------------- ### ARC3 Agent Setup and Execution Script Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb This script sets up the ARC3 agent environment for competition mode. It includes downloading necessary files, copying the custom agent, configuring environment variables, and running the agent's main script. ```bash import os if os.getenv('KAGGLE_IS_COMPETITION_RERUN'): # Wait for gateway to be ready !curl --fail --retry 999 --retry-all-errors --retry-delay 5 \ --retry-max-time 600 http://gateway:8001/api/games # Copy repo to writable location !cp -r /kaggle/input/competitions/arc-prize-2026-arc-agi-3/ARC-AGI-3-Agents \ /kaggle/working/ARC-AGI-3-Agents # Copy custom agent !cp /kaggle/working/my_agent.py \ /kaggle/working/ARC-AGI-3-Agents/agents/templates/my_agent.py # Write a minimal __init__.py that only imports what we need # (the original eagerly imports templates with unmet deps like langgraph, smolagents) with open('/kaggle/working/ARC-AGI-3-Agents/agents/__init__.py', 'w') as f: f.write("""from typing import Type, cast from dotenv import load_dotenv from .agent import Agent, Playback from .swarm import Swarm from .templates.random_agent import Random from .templates.my_agent import MyAgent load_dotenv() AVAILABLE_AGENTS: dict[str, Type[Agent]] = { "random": Random, "myagent": MyAgent, } """) # Write a .env file that overrides .env.example defaults # This is loaded second with override=True by main.py, so it wins with open('/kaggle/working/ARC-AGI-3-Agents/.env', 'w') as f: f.write("""SCHEME=http HOST=gateway PORT=8001 ARC_API_KEY=test-key-123 ARC_BASE_URL=http://gateway:8001/ OPERATION_MODE=online ENVIRONMENTS_DIR= RECORDINGS_DIR=/kaggle/working/server_recording """) # Run agent !cd /kaggle/working/ARC-AGI-3-Agents && \ MPLBACKEND=agg \ python main.py --agent myagent ``` -------------------------------- ### Get Environment Directory Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb Retrieves or creates a directory specific to a game ID within a base experiment directory. Useful for organizing game-specific data. ```python def get_environment_directory(base_dir, game_id): """Get or create environment-specific directory for a game_id.""" env_dir = os.path.join(base_dir, game_id) os.makedirs(env_dir, exist_ok=True) return env_dir ``` -------------------------------- ### Append Frame with Sliding Window Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb Appends a frame to the agent's frame list, maintaining a sliding window of a fixed maximum size (_MAX_FRAMES). Updates the agent's GUID and records the frame if a recorder is available. ```python def append_frame(self, frame: FrameData) -> None: """Override to cap frames list at _MAX_FRAMES (sliding window).""" self.frames.append(frame) if len(self.frames) > self._MAX_FRAMES: self.frames = self.frames[-self._MAX_FRAMES:] if frame.guid: self.guid = frame.guid if hasattr(self, "recorder") and not self.is_playback: import json self.recorder.record(json.loads(frame.model_dump_json())) ``` -------------------------------- ### Check Time Elapsed Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb Determines if 8 hours have elapsed since the agent's start time, with a 5-minute buffer. Used to signal the end of a game session. ```python return (time.time() - self.start_time) >= 8 * 3600 - 5 * 60 ``` -------------------------------- ### Get Score from Frame Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb Retrieves the score from a FrameData object. It is compatible with both standard FrameData and patched versions that might store the score differently. ```python def _get_score(self, frame): """Get score from frame, compatible with both patched and standard FrameData.""" return getattr(frame, 'score', None) or frame.levels_completed ``` -------------------------------- ### Kaggle Submission: Environment Configuration Source: https://context7.com/johnlikescarrot/arc3-submission-stochastic-goose/llms.txt Configures the competition environment by setting up a `.env` file with necessary parameters like scheme, host, port, API key, base URL, operation mode, and recording directory. ```dotenv # .env file: SCHEME=http HOST=gateway PORT=8001 ARC_API_KEY=test-key-123 ARC_BASE_URL=http://gateway:8001/ OPERATION_MODE=online RECORDINGS_DIR=/kaggle/working/server_recording ``` -------------------------------- ### Local Testing: Dummy Submission Creation Source: https://context7.com/johnlikescarrot/arc3-submission-stochastic-goose/llms.txt Creates a dummy submission file in parquet format for local testing purposes when not running in competition mode. This includes basic columns like `row_id`, `game_id`, `end_of_game`, and `score`. ```python # For local testing (non-competition mode), create dummy submission: import pandas as pd submission = pd.DataFrame( data=[['1_0', '1', True, 1]], columns=['row_id', 'game_id', 'end_of_game', 'score'] ) submission.to_parquet('/kaggle/working/submission.parquet', index=False) ``` -------------------------------- ### Initialize StochasticGoose Agent Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb Initializes the StochasticGoose agent, setting up random seeds, device configuration, experiment directories, logging, and internal state variables. It configures the action model and experience buffer. ```python class MyAgent(Agent): """CNN-based action learning agent that predicts which actions cause frame changes.""" MAX_ACTIONS = float('inf') _MAX_FRAMES = 10 # PERF: Keep only the last N frames (sliding window) def __init__(self, *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) seed = int(time.time() * 1000000) + hash(self.game_id) % 1000000 random.seed(seed) np.random.seed(seed % (2**32 - 1)) torch.manual_seed(seed % (2**32 - 1)) self.start_time = time.time() # Device configuration self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') print(f"Action agent using device: {self.device}") # Setup experiment directory and logging self.base_dir, log_file = setup_experiment_directory() setup_logging_for_experiment(log_file) env_dir = get_environment_directory(self.base_dir, self.game_id) self.current_score = -1 self.logger = logging.getLogger(f"ActionAgent_{self.game_id}") # Visualization disabled for submission self.save_action_visualizations = False # Initialize action model self.grid_size = 64 self.num_coordinates = self.grid_size * self.grid_size self.num_colours = 16 self.action_model = None self.optimizer = None # Experience buffer self.experience_buffer = deque(maxlen=200000) self.experience_hashes = set() self.batch_size = 64 self.train_frequency = 5 # Track previous state/action self.prev_frame = None self.prev_action_idx = None # Action mapping self.action_list = [GameAction.ACTION1, GameAction.ACTION2, GameAction.ACTION3, GameAction.ACTION4, GameAction.ACTION5] self.log_dir = env_dir self.logger.info(f"Action agent initialized for game_id: {self.game_id}") ``` -------------------------------- ### Choose Action Logic Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb Chooses an action based on the current game state. It handles game resets, level changes by resetting the experience buffer and model, and uses the action model to predict the next action. Includes debug logging for frame information. ```python if self.action_counter == 0: print(f"[DEBUG] latest_frame type: {type(latest_frame)}") print(f"[DEBUG] latest_frame.state: {latest_frame.state}") print(f"[DEBUG] latest_frame.levels_completed: {latest_frame.levels_completed}") print(f"[DEBUG] has score: {hasattr(latest_frame, 'score')}") print(f"[DEBUG] available_actions: {getattr(latest_frame, 'available_actions', 'N/A')}") if hasattr(latest_frame, 'frame') and latest_frame.frame: frame_arr = np.array(latest_frame.frame) print(f"[DEBUG] frame shape: {frame_arr.shape}") current_level = self._get_score(latest_frame) if current_level != self.current_score: self.logger.info(f"Score changed from {self.current_score} to {current_level} at action {self.action_counter}") print(f"Score changed from {self.current_score} to {current_level} at action {self.action_counter}") self.experience_buffer.clear() self.experience_hashes.clear() print("Cleared experience buffer - new level reached") self.action_model = ActionModel(input_channels=self.num_colours, grid_size=self.grid_size).to(self.device) self.optimizer = optim.Adam(self.action_model.parameters(), lr=0.0001) print("Reset action model and optimizer for new level") self.prev_frame = None self.prev_action_idx = None self.current_score = current_level if latest_frame.state in [GameState.NOT_PLAYED, GameState.GAME_OVER]: self.prev_frame = None self.prev_action_idx = None action = GameAction.RESET action.reasoning = "Game needs reset." return action current_frame = self._frame_to_tensor(latest_frame) if current_frame is None: self.prev_frame = None self.prev_action_idx = None action = random.choice(self.action_list[:5]) ``` -------------------------------- ### Sample Action from Combined Output Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb Samples an action from a combined output tensor containing logits for basic actions and coordinates. It handles masking for invalid actions based on available actions and returns the sampled action, coordinates, and visualization probabilities. ```python def _sample_from_combined_output(self, combined_logits, available_actions=None): """Sample from combined 5 + 64x64 action space with masking for invalid actions.""" action_logits = combined_logits[:5] coord_logits = combined_logits[5:] if available_actions is not None and len(available_actions) > 0: action_mask = torch.full_like(action_logits, float('-inf')) action6_available = False for action in available_actions: # Gateway sends raw ints [1,2,...,6], not GameAction enums action_id = action.value if hasattr(action, 'value') else int(action) if 1 <= action_id <= 5: action_mask[action_id - 1] = 0.0 elif action_id == 6: action6_available = True action_logits = action_logits + action_mask if not action6_available: coord_logits = coord_logits + torch.full_like(coord_logits, float('-inf')) action_probs = torch.sigmoid(action_logits) coord_probs_raw = torch.sigmoid(coord_logits) coord_probs_scaled = coord_probs_raw / self.num_coordinates all_probs_sampling = torch.cat([action_probs, coord_probs_scaled]) all_probs_sampling = all_probs_sampling / all_probs_sampling.sum() all_probs_sampling_np = all_probs_sampling.cpu().numpy() selected_idx = np.random.choice(len(all_probs_sampling_np), p=all_probs_sampling_np) coord_probs_viz = torch.sigmoid(coord_logits) all_probs_viz = torch.cat([action_probs, coord_probs_viz]) all_probs_viz_np = all_probs_viz.cpu().numpy() if selected_idx < 5: return selected_idx, None, None, all_probs_viz_np else: coord_idx = selected_idx - 5 y_idx = coord_idx // self.grid_size x_idx = coord_idx % self.grid_size return 5, (y_idx, x_idx), coord_idx, all_probs_viz_np ``` -------------------------------- ### Train Action Model with Entropy Regularization Source: https://context7.com/johnlikescarrot/arc3-submission-stochastic-goose/llms.txt Trains the action model using collected experiences and binary cross-entropy loss. Includes entropy regularization for exploration. Ensure sufficient experiences are collected before calling. ```python def _train_action_model(self): """Train the action model on collected experiences with entropy regularization.""" if len(self.experience_buffer) < self.batch_size: return # Not enough experiences yet # Sample random batch from experience buffer batch_indices = np.random.choice( len(self.experience_buffer), self.batch_size, replace=False ) batch = [self.experience_buffer[i] for i in batch_indices] # Prepare batch tensors states = torch.stack([ torch.from_numpy(exp['state']).float().to(self.device) for exp in batch ]) action_indices = torch.tensor( [exp['action_idx'] for exp in batch], dtype=torch.long, device=self.device ) rewards = torch.tensor( [exp['reward'] for exp in batch], dtype=torch.float32, device=self.device ) self.optimizer.zero_grad() # Forward pass combined_logits = self.action_model(states) selected_logits = combined_logits.gather(1, action_indices.unsqueeze(1)).squeeze(1) # Binary cross-entropy: predict if action causes frame change main_loss = F.binary_cross_entropy_with_logits(selected_logits, rewards) # Entropy regularization for exploration all_probs = torch.sigmoid(combined_logits) action_entropy = all_probs[:, :5].mean() coord_entropy = all_probs[:, 5:].mean() total_loss = main_loss - 0.0001 * action_entropy - 0.00001 * coord_entropy total_loss.backward() self.optimizer.step() ``` -------------------------------- ### Kaggle Submission: Agent Execution Source: https://context7.com/johnlikescarrot/arc3-submission-stochastic-goose/llms.txt Runs the agent in the competition environment using the ARC-AGI-3 framework's `main.py` script. Sets the `MPLBACKEND` environment variable to `agg` for non-interactive plotting. ```bash # Run the agent !cd /kaggle/working/ARC-AGI-3-Agents && \ MPLBACKEND=agg \ python main.py --agent myagent ``` -------------------------------- ### MyAgent Class Definition Source: https://context7.com/johnlikescarrot/arc3-submission-stochastic-goose/llms.txt Defines the MyAgent class, extending the base Agent class. It initializes the reinforcement learning environment, model, optimizer, and experience replay buffer. The agent is designed to run within the ARC-AGI framework. ```python from agents.agent import Agent from arcengine import FrameData, GameAction, GameState from collections import deque import numpy as np import torch import torch.optim as optim class MyAgent(Agent): """CNN-based action learning agent for ARC-AGI-3.""" MAX_ACTIONS = float('inf') _MAX_FRAMES = 10 # Sliding window for memory efficiency def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') self.grid_size = 64 self.num_colours = 16 self.num_coordinates = self.grid_size * self.grid_size # Initialize model and optimizer (reset per level) self.action_model = None self.optimizer = None self.current_score = -1 # Experience replay buffer self.experience_buffer = deque(maxlen=200000) self.experience_hashes = set() self.batch_size = 64 self.train_frequency = 5 # Track previous state for experience creation self.prev_frame = None self.prev_action_idx = None # Available actions self.action_list = [ GameAction.ACTION1, GameAction.ACTION2, GameAction.ACTION3, GameAction.ACTION4, GameAction.ACTION5 ] # Instantiation happens automatically via the ARC-AGI framework # The agent is registered and run with: # python main.py --agent myagent ``` -------------------------------- ### Core Decision Loop: choose_action Method Source: https://context7.com/johnlikescarrot/arc3-submission-stochastic-goose/llms.txt The `choose_action` method is the central decision-making function. It handles level transitions, experience collection, model training, and action selection, including fallback error handling. It resets the model and experience buffer when a level change is detected. ```python def choose_action(self, frames, latest_frame): """Choose action using CNN model predictions with online learning.""" # Check for level change - reset model for new puzzle current_level = self._get_score(latest_frame) if current_level != self.current_score: print(f"Score changed from {self.current_score} to {current_level}") # Clear experience buffer for new level self.experience_buffer.clear() self.experience_hashes.clear() # Reset network and optimizer self.action_model = ActionModel( input_channels=self.num_colours, grid_size=self.grid_size ).to(self.device) self.optimizer = optim.Adam(self.action_model.parameters(), lr=0.0001) self.prev_frame = None self.prev_action_idx = None self.current_score = current_level # Handle game reset states if latest_frame.state in [GameState.NOT_PLAYED, GameState.GAME_OVER]: action = GameAction.RESET action.reasoning = "Game needs reset." return action # Convert current frame to tensor current_frame = self._frame_to_tensor(latest_frame) # Create experience from previous action (reward = frame changed?) if self.prev_frame is not None: experience_hash = self._compute_experience_hash( self.prev_frame, self.prev_action_idx ) if experience_hash not in self.experience_hashes: current_frame_np = current_frame.cpu().numpy().astype(bool) frame_changed = not np.array_equal(self.prev_frame, current_frame_np) experience = { 'state': self.prev_frame, 'action_idx': self.prev_action_idx, 'reward': 1.0 if frame_changed else 0.0 } self.experience_buffer.append(experience) self.experience_hashes.add(experience_hash) # Get action predictions from model available_actions = getattr(latest_frame, 'available_actions', None) with torch.no_grad(): combined_logits = self.action_model(current_frame.unsqueeze(0)).squeeze(0) action_idx, coords, coord_idx, all_probs = self._sample_from_combined_output( combined_logits, available_actions ) # Build action response if action_idx < 5: selected_action = self.action_list[action_idx] selected_action.reasoning = f"{selected_action.name} (prob: {all_probs[action_idx]:.3f})" else: selected_action = GameAction.ACTION6 y, x = coords selected_action.set_data({"x": int(x), "y": int(y)}) selected_action.reasoning = f"ACTION6 at ({x}, {y})" # Store for next experience creation self.prev_frame = current_frame.cpu().numpy().astype(bool) self.prev_action_idx = action_idx if action_idx < 5 else (5 + coord_idx) # Periodic training if self.action_counter % self.train_frequency == 0: self._train_action_model() return selected_action ``` -------------------------------- ### Train Action Model Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb Trains the action model on a batch of collected experiences. It calculates the loss based on predicted logits and rewards, and applies gradients to update the model weights. Includes logic to clear CUDA cache if available. ```python if len(self.experience_buffer) < self.batch_size: return batch_indices = np.random.choice(len(self.experience_buffer), self.batch_size, replace=False) batch = [self.experience_buffer[i] for i in batch_indices] states = torch.stack([torch.from_numpy(exp['state']).float().to(self.device) for exp in batch]) action_indices = torch.tensor([exp['action_idx'] for exp in batch], dtype=torch.long, device=self.device) rewards = torch.tensor([exp['reward'] for exp in batch], dtype=torch.float32, device=self.device) self.optimizer.zero_grad() combined_logits = self.action_model(states) selected_logits = combined_logits.gather(1, action_indices.unsqueeze(1)).squeeze(1) main_loss = F.binary_cross_entropy_with_logits(selected_logits, rewards) all_probs = torch.sigmoid(combined_logits) action_entropy = all_probs[:, :5].mean() coord_entropy = all_probs[:, 5:].mean() total_loss = main_loss - 0.0001 * action_entropy - 0.00001 * coord_entropy total_loss.backward() self.optimizer.step() if torch.cuda.is_available(): torch.cuda.empty_cache() ``` -------------------------------- ### Action Sampling with Masking Source: https://context7.com/johnlikescarrot/arc3-submission-stochastic-goose/llms.txt Samples actions from combined model outputs, applying masking for unavailable actions. Handles discrete actions (1-5) and coordinate-based actions (6). Requires a tensor of logits and an optional list of available actions. ```python def _sample_from_combined_output(self, combined_logits, available_actions=None): """Sample from combined action space with masking for invalid actions.""" action_logits = combined_logits[:5] # First 5: discrete actions coord_logits = combined_logits[5:] # Remaining 4096: coordinates # Apply masking for unavailable actions if available_actions is not None and len(available_actions) > 0: action_mask = torch.full_like(action_logits, float('-inf')) action6_available = False for action in available_actions: action_id = action.value if hasattr(action, 'value') else int(action) if 1 <= action_id <= 5: action_mask[action_id - 1] = 0.0 # Unmask valid actions elif action_id == 6: action6_available = True action_logits = action_logits + action_mask if not action6_available: coord_logits = coord_logits + torch.full_like(coord_logits, float('-inf')) # Convert to probabilities with scaled coordinates action_probs = torch.sigmoid(action_logits) coord_probs_scaled = torch.sigmoid(coord_logits) / self.num_coordinates # Normalize and sample all_probs = torch.cat([action_probs, coord_probs_scaled]) all_probs = all_probs / all_probs.sum() selected_idx = np.random.choice(len(all_probs), p=all_probs.cpu().numpy()) if selected_idx < 5: return selected_idx, None, None, all_probs.cpu().numpy() else: coord_idx = selected_idx - 5 y_idx = coord_idx // self.grid_size x_idx = coord_idx % self.grid_size return 5, (y_idx, x_idx), coord_idx, all_probs.cpu().numpy() # Returns: (action_type, coordinates, coord_index, probabilities) ``` -------------------------------- ### Compute Experience Hash Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb Generates an MD5 hash for a given frame and action index combination. This is used to ensure the uniqueness of experiences stored in the buffer. ```python frame_bytes = frame.tobytes() hash_input = frame_bytes + str(action_idx).encode('utf-8') return hashlib.md5(hash_input).hexdigest() ``` -------------------------------- ### Stochastic Goose Agent Action Selection Logic Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb This Python code implements the core logic for the Stochastic Goose agent's action selection. It handles frame processing, experience replay, action prediction using a PyTorch model, and stores state for the next iteration. Includes fallback for error handling. ```python action.reasoning = f"Skipped weird frame, random {action.value}" return action # Create experience from previous action if self.prev_frame is not None: experience_hash = self._compute_experience_hash(self.prev_frame, self.prev_action_idx) if experience_hash not in self.experience_hashes: current_frame_np = current_frame.cpu().numpy().astype(bool) frame_changed = not np.array_equal(self.prev_frame, current_frame_np) experience = { 'state': self.prev_frame, 'action_idx': self.prev_action_idx, 'reward': 1.0 if frame_changed else 0.0 } self.experience_buffer.append(experience) self.experience_hashes.add(experience_hash) # Get action predictions available_actions = getattr(latest_frame, 'available_actions', None) with torch.no_grad(): combined_logits = self.action_model(current_frame.unsqueeze(0)) combined_logits = combined_logits.squeeze(0) action_idx, coords, coord_idx, all_probs = self._sample_from_combined_output( combined_logits, available_actions ) if action_idx < 5: selected_action = self.action_list[action_idx] selected_action.reasoning = f"{selected_action.name} (prob: {all_probs[action_idx]:.3f})" else: selected_action = GameAction.ACTION6 y, x = coords selected_action.set_data({"x": int(x), "y": int(y)}) selected_action.reasoning = f"ACTION6 at ({x}, {y}) (prob: {all_probs[coord_idx]:.3f})" # Store current frame and action for next experience creation self.prev_frame = current_frame.cpu().numpy().astype(bool) if action_idx < 5: self.prev_action_idx = action_idx else: self.prev_action_idx = 5 + coord_idx # Train model periodically if self.action_counter % self.train_frequency == 0: self._train_action_model() return selected_action except Exception as e: print(f"[DEBUG] choose_action CRASHED at action {self.action_counter}: {type(e).__name__}: {e}") traceback.print_exc() # Fallback: return a random action so the agent doesn't die action = random.choice(self.action_list[:5]) action.reasoning = f"Fallback after error: {e}" return action ``` -------------------------------- ### Kaggle Submission: Agent Registration Source: https://context7.com/johnlikescarrot/arc3-submission-stochastic-goose/llms.txt Registers custom agents within the ARC-AGI-3 framework by defining the `AVAILABLE_AGENTS` dictionary in `agents/__init__.py`. ```python # Register custom agent in the framework # Write to agents/__init__.py: AVAILABLE_AGENTS = { "random": Random, "myagent": MyAgent, # Our StochasticGoose agent } ``` -------------------------------- ### Define ActionModel CNN Architecture Source: https://context7.com/johnlikescarrot/arc3-submission-stochastic-goose/llms.txt Implements a shared convolutional backbone with separate heads for discrete action classification and 64x64 coordinate prediction. Requires PyTorch and expects 16-channel one-hot encoded input frames. ```python import torch import torch.nn as nn import torch.nn.functional as F class ActionModel(nn.Module): """CNN that predicts which actions will result in new frames.""" def __init__(self, input_channels=16, grid_size=64): super().__init__() self.grid_size = grid_size self.num_action_types = 5 # ACTION1-ACTION5 # Shared convolutional backbone self.conv1 = nn.Conv2d(input_channels, 32, kernel_size=3, padding=1) self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1) self.conv3 = nn.Conv2d(64, 128, kernel_size=3, padding=1) self.conv4 = nn.Conv2d(128, 256, kernel_size=3, padding=1) # Action head self.action_pool = nn.MaxPool2d(4, 4) action_flattened_size = 256 * 16 * 16 self.action_fc = nn.Linear(action_flattened_size, 512) self.action_head = nn.Linear(512, self.num_action_types) # Coordinate head (64x64 action space) self.coord_conv1 = nn.Conv2d(256, 128, kernel_size=3, padding=1) self.coord_conv2 = nn.Conv2d(128, 64, kernel_size=3, padding=1) self.coord_conv3 = nn.Conv2d(64, 32, kernel_size=1) self.coord_conv4 = nn.Conv2d(32, 1, kernel_size=1) self.dropout = nn.Dropout(0.2) def forward(self, x): # Shared backbone x = F.relu(self.conv1(x)) x = F.relu(self.conv2(x)) x = F.relu(self.conv3(x)) conv_features = F.relu(self.conv4(x)) # Action head: predict discrete actions action_features = self.action_pool(conv_features) action_features = action_features.view(action_features.size(0), -1) action_features = F.relu(self.action_fc(action_features)) action_features = self.dropout(action_features) action_logits = self.action_head(action_features) # Coordinate head: predict 64x64 grid positions coord_features = F.relu(self.coord_conv1(conv_features)) coord_features = F.relu(self.coord_conv2(coord_features)) coord_features = F.relu(self.coord_conv3(coord_features)) coord_logits = self.coord_conv4(coord_features) coord_logits = coord_logits.view(coord_logits.size(0), -1) # Combined output: [5 action logits] + [4096 coordinate logits] combined_logits = torch.cat([action_logits, coord_logits], dim=1) return combined_logits # Usage example device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = ActionModel(input_channels=16, grid_size=64).to(device) # Create sample input: batch of 1, 16 channels, 64x64 grid sample_input = torch.randn(1, 16, 64, 64).to(device) output = model(sample_input) print(f"Output shape: {output.shape}") # torch.Size([1, 4101]) # First 5 values are action logits, remaining 4096 are coordinate logits ``` -------------------------------- ### Convert Frame to Tensor Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb Converts a NumPy frame to a PyTorch tensor, suitable for input into the action model. Ensures the tensor has the correct shape and data type. ```python tensor = torch.zeros(self.num_colours, self.grid_size, self.grid_size, dtype=torch.float32) tensor.scatter_(0, torch.from_numpy(frame).unsqueeze(0), 1) return tensor.to(self.device) ``` -------------------------------- ### ARC3 Non-Competition Mode Submission Generation Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb This Python code generates a dummy submission file in parquet format for non-competition mode. It uses the pandas library to create a DataFrame with minimal required columns. ```python import os if not os.getenv('KAGGLE_IS_COMPETITION_RERUN'): # Non-competition mode: produce a dummy submission import pandas as pd submission = pd.DataFrame( data=[['1_0', '1', True, 1]], columns=['row_id', 'game_id', 'end_of_game', 'score']) submission.to_parquet('/kaggle/working/submission.parquet', index=False) submission.head() ``` -------------------------------- ### Convert Frame to Tensor Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb Converts frame data into a tensor format suitable for the action model. It extracts the relevant frame data and asserts its expected shape. ```python def _frame_to_tensor(self, frame_data): """Convert frame data to tensor format for the model.""" frame = np.array(frame_data.frame, dtype=np.int64) frame = frame[-1] assert frame.shape == (self.grid_size, self.grid_size), ``` -------------------------------- ### Decide if Agent is Done Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb Checks if the game is finished, either by reaching a win state or by time elapsed. Includes error handling to bail out if any exception occurs during the check. ```python return any([ latest_frame.state is GameState.WIN, self._has_time_elapsed(), ]) except Exception as e: print(f"[DEBUG] is_done crashed: {e}") traceback.print_exc() return True # bail out on error ``` -------------------------------- ### ActionModel CNN Architecture Source: https://github.com/johnlikescarrot/arc3-submission-stochastic-goose/blob/main/ARC3-Submission-Stochastic-Goose.ipynb Defines a Convolutional Neural Network (CNN) for predicting actions and coordinates. It uses a shared backbone with separate heads for action classification and coordinate regression. ```python class ActionModel(nn.Module): """CNN that predicts which actions will result in new frames with shared conv backbone.""" def __init__(self, input_channels=16, grid_size=64): super().__init__() self.grid_size = grid_size self.num_action_types = 5 # ACTION1-ACTION5 # Shared convolutional backbone self.conv1 = nn.Conv2d(input_channels, 32, kernel_size=3, padding=1) self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1) self.conv3 = nn.Conv2d(64, 128, kernel_size=3, padding=1) self.conv4 = nn.Conv2d(128, 256, kernel_size=3, padding=1) # Action head self.action_pool = nn.MaxPool2d(4, 4) action_flattened_size = 256 * 16 * 16 self.action_fc = nn.Linear(action_flattened_size, 512) self.action_head = nn.Linear(512, self.num_action_types) # Coordinate head (64x64 action space) self.coord_conv1 = nn.Conv2d(256, 128, kernel_size=3, padding=1) self.coord_conv2 = nn.Conv2d(128, 64, kernel_size=3, padding=1) self.coord_conv3 = nn.Conv2d(64, 32, kernel_size=1) self.coord_conv4 = nn.Conv2d(32, 1, kernel_size=1) self.dropout = nn.Dropout(0.2) def forward(self, x): x = F.relu(self.conv1(x)) x = F.relu(self.conv2(x)) x = F.relu(self.conv3(x)) conv_features = F.relu(self.conv4(x)) # Action head action_features = self.action_pool(conv_features) action_features = action_features.view(action_features.size(0), -1) action_features = F.relu(self.action_fc(action_features)) action_features = self.dropout(action_features) action_logits = self.action_head(action_features) # Coordinate head coord_features = F.relu(self.coord_conv1(conv_features)) coord_features = F.relu(self.coord_conv2(coord_features)) coord_features = F.relu(self.coord_conv3(coord_features)) coord_logits = self.coord_conv4(coord_features) coord_logits = coord_logits.view(coord_logits.size(0), -1) combined_logits = torch.cat([action_logits, coord_logits], dim=1) return combined_logits ``` -------------------------------- ### Frame to Tensor Conversion Source: https://context7.com/johnlikescarrot/arc3-submission-stochastic-goose/llms.txt Converts game frame data into a one-hot encoded tensor for CNN processing. Each of the 16 possible cell colors is represented as a separate channel. Requires a NumPy array of shape (64, 64) with values from 0 to 15. ```python def _frame_to_tensor(self, frame_data): """Convert frame data to one-hot encoded tensor for the CNN model.""" # Extract the latest frame (64x64 grid with values 0-15) frame = np.array(frame_data.frame, dtype=np.int64) frame = frame[-1] # Get most recent frame assert frame.shape == (self.grid_size, self.grid_size), \ f"Expected frame shape ({self.grid_size}, {self.grid_size}), got {frame.shape}" # One-hot encode: create 16 channels for 16 possible colors tensor = torch.zeros( self.num_colours, self.grid_size, self.grid_size, dtype=torch.float32 ) tensor.scatter_(0, torch.from_numpy(frame).unsqueeze(0), 1) return tensor.to(self.device) # Example usage # Input: 64x64 grid with integer values 0-15 # Output: torch.Tensor of shape (16, 64, 64) with one-hot encoding sample_frame = np.random.randint(0, 16, size=(64, 64)) # After conversion: tensor[color_idx, y, x] = 1.0 if grid[y,x] == color_idx ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.