import { draftStore } from '../store/draft-store.js';
import { solve } from '../core/solver.js';
import { renderLevel, formatSolverResult } from '../core/ascii-renderer.js';
import { validatePosition, validateGridSize } from '../core/validator.js';
interface ToolDefinition {
name: string;
description: string;
inputSchema: object;
handler: (args: any) => Promise<{ content: Array<{ type: 'text'; text: string }> }>;
}
export function getGridOperationTools(): ToolDefinition[] {
return [
{
name: 'set_grid_size',
description: 'Resize the level grid. Elements outside the new bounds are removed.',
inputSchema: {
type: 'object',
properties: {
width: { type: 'number', description: 'New grid width (5-25)', minimum: 5, maximum: 25 },
height: { type: 'number', description: 'New grid height (5-25)', minimum: 5, maximum: 25 },
},
required: ['width', 'height'],
},
handler: async (args) => {
const draft = draftStore.getCurrentDraft();
if (!draft) return { content: [{ type: 'text', text: 'No active draft. Use create_level first.' }] };
const check = validateGridSize(args.width, args.height);
if (!check.valid) return { content: [{ type: 'text', text: check.error! }] };
const w = Math.max(5, Math.min(25, args.width));
const h = Math.max(5, Math.min(25, args.height));
// Filter out-of-bounds elements
const obstacles = draft.obstacles.filter(o => o.x > 0 && o.x < w - 1 && o.y > 0 && o.y < h - 1);
const thinIceTiles = draft.thinIceTiles.filter(t => t.x > 0 && t.x < w - 1 && t.y > 0 && t.y < h - 1);
const pushableRocks = draft.pushableRocks.filter(r => r.x > 0 && r.x < w - 1 && r.y > 0 && r.y < h - 1);
const warpPairs = draft.warpPairs.map(wp => ({
...wp,
positions: wp.positions.filter(p => p.x > 0 && p.x < w - 1 && p.y > 0 && p.y < h - 1)
})).filter(wp => wp.positions.length > 0);
let start = { ...draft.startPosition };
let goal = { ...draft.goalPosition };
if (start.x >= w - 1) start.x = w - 2;
if (start.y >= h - 1) start.y = h - 2;
if (goal.x >= w - 1) goal.x = w - 2;
if (goal.y >= h - 1) goal.y = h - 2;
let pp = draft.pressurePlate;
if (pp && (pp.x <= 0 || pp.x >= w - 1 || pp.y <= 0 || pp.y >= h - 1)) pp = null;
let bar = draft.barrier;
if (bar && (bar.x <= 0 || bar.x >= w - 1 || bar.y <= 0 || bar.y >= h - 1)) bar = null;
draftStore.updateDraft({
gridWidth: w, gridHeight: h, obstacles, thinIceTiles, pushableRocks, warpPairs,
startPosition: start, goalPosition: goal, pressurePlate: pp, barrier: bar, isDirty: true,
});
const current = draftStore.getCurrentDraft()!;
const viz = renderLevel(current, { showCoords: true });
return { content: [{ type: 'text', text: `Grid resized to ${w}x${h}\n\n${viz}` }] };
},
},
{
name: 'set_start',
description: 'Set the player start position.',
inputSchema: {
type: 'object',
properties: {
x: { type: 'number', description: 'X coordinate' },
y: { type: 'number', description: 'Y coordinate' },
},
required: ['x', 'y'],
},
handler: async (args) => {
const draft = draftStore.getCurrentDraft();
if (!draft) return { content: [{ type: 'text', text: 'No active draft. Use create_level first.' }] };
const check = validatePosition(args.x, args.y, draft.gridWidth, draft.gridHeight);
if (!check.valid) return { content: [{ type: 'text', text: check.error! }] };
draftStore.updateDraft({ startPosition: { x: args.x, y: args.y }, isDirty: true });
const current = draftStore.getCurrentDraft()!;
const viz = renderLevel(current, { showCoords: true });
return { content: [{ type: 'text', text: `Start set to (${args.x}, ${args.y})\n\n${viz}` }] };
},
},
{
name: 'set_goal',
description: 'Set the goal position.',
inputSchema: {
type: 'object',
properties: {
x: { type: 'number', description: 'X coordinate' },
y: { type: 'number', description: 'Y coordinate' },
},
required: ['x', 'y'],
},
handler: async (args) => {
const draft = draftStore.getCurrentDraft();
if (!draft) return { content: [{ type: 'text', text: 'No active draft. Use create_level first.' }] };
// Goal can be on edge (wall position) per the solver logic
const check = validatePosition(args.x, args.y, draft.gridWidth, draft.gridHeight);
// Allow edge positions for goal
if (args.x < 0 || args.x >= draft.gridWidth || args.y < 0 || args.y >= draft.gridHeight) {
return { content: [{ type: 'text', text: `Position (${args.x}, ${args.y}) is outside the grid.` }] };
}
draftStore.updateDraft({ goalPosition: { x: args.x, y: args.y }, isDirty: true });
const current = draftStore.getCurrentDraft()!;
const viz = renderLevel(current, { showCoords: true });
return { content: [{ type: 'text', text: `Goal set to (${args.x}, ${args.y})\n\n${viz}` }] };
},
},
{
name: 'clear_level',
description: 'Clear all elements from the current level, keeping grid size, start, and goal.',
inputSchema: { type: 'object', properties: {} },
handler: async () => {
const draft = draftStore.getCurrentDraft();
if (!draft) return { content: [{ type: 'text', text: 'No active draft. Use create_level first.' }] };
draftStore.updateDraft({
obstacles: [], warpPairs: [], thinIceTiles: [], pushableRocks: [],
pressurePlate: null, barrier: null, isDirty: true, lastSolverResult: null,
});
const current = draftStore.getCurrentDraft()!;
const viz = renderLevel(current, { showCoords: true });
return { content: [{ type: 'text', text: `Level cleared.\n\n${viz}` }] };
},
},
];
}