# domain/converters/learning.grid.converter.ts

> 120 lines of code and 36 definitions.

Tree: Site tree
Language: typescript
Layer: processing
Canonical: https://banes-lab.com/anatomy/tree#file-domain-converters-learning-grid-converter-ts
Source text: https://banes-lab.com/assets/sources/source.f543add1a5b7a18eb4a52ef738cd101d4c08270dbb71f0781ff3eb2fae5b6185.generated.txt

## Definitions

- `isSolid` (lexical_declaration, line 40, exported)
- `improvements` (lexical_declaration, line 84)
- `pick` (lexical_declaration, line 50)
- `neighbours` (lexical_declaration, line 63)
- `searchGrid` (lexical_declaration, line 102, exported)
- `NEIGHBOURS` (lexical_declaration, line 4)
- `MARGIN` (lexical_declaration, line 11)
- `Visit` (interface_declaration, line 13)
- `gridFor` (lexical_declaration, line 18, exported)
- `wide` (lexical_declaration, line 19, exported)
- `tall` (lexical_declaration, line 20, exported)
- `blocked` (lexical_declaration, line 21, exported)
- `x0` (lexical_declaration, line 23, exported)
- `x1` (lexical_declaration, line 24, exported)
- `y0` (lexical_declaration, line 25, exported)
- `y1` (lexical_declaration, line 26, exported)
- `cellAt` (lexical_declaration, line 36, exported)
- `cheapestOf` (lexical_declaration, line 49)
- `scan` (lexical_declaration, line 51)
- `stepCost` (lexical_declaration, line 59)
- `cx` (lexical_declaration, line 64, exported)
- `cy` (lexical_declaration, line 65, exported)
- `found` (lexical_declaration, line 66)
- `nx` (lexical_declaration, line 68)
- `ny` (lexical_declaration, line 69)
- `inside` (lexical_declaration, line 70)
- `step` (lexical_declaration, line 71)
- `Reached` (interface_declaration, line 79)
- `better` (lexical_declaration, line 90)
- `spent` (lexical_declaration, line 92)
- `reached` (lexical_declaration, line 103, exported)
- `heap` (lexical_declaration, line 107, exported)
- `here` (lexical_declaration, line 111, exported)
- `walkBack` (lexical_declaration, line 122, exported)
- `back` (lexical_declaration, line 123, exported)
- `cursor` (lexical_declaration, line 124, exported)

## Used by

- [domain/converters/learning.fragment.converter.ts](https://banes-lab.com/source/tree/domain/converters/learning.fragment.converter.ts.md)
- [domain/converters/tension.converter.ts](https://banes-lab.com/source/tree/domain/converters/tension.converter.ts.md)

## Source

```typescript
import { CROSSING_COST, GRID_STEP } from "#configuration/constants/learning.constants";
import type { LearningGrid, LearningModel, LearningPoint } from "#types/learning.types";

const NEIGHBOURS: readonly (readonly [number, number])[] = [
    [0, 1],
    [1, 0],
    [-1, 0],
    [0, -1],
];

const MARGIN = 2;

interface Visit {
    readonly cell: number;
    readonly spent: number;
}

export const gridFor = function gridFor(model: LearningModel): LearningGrid {
    const wide = Math.ceil(model.width / GRID_STEP) + MARGIN;
    const tall = Math.ceil(model.height / GRID_STEP) + MARGIN;
    const blocked = new Uint8Array(wide * tall);
    for (const block of model.blocks) {
        const x0 = Math.max(0, Math.floor((block.x - MARGIN) / GRID_STEP));
        const x1 = Math.min(wide - 1, Math.ceil((block.x + block.width + MARGIN) / GRID_STEP));
        const y0 = Math.max(0, Math.floor((block.y - MARGIN) / GRID_STEP));
        const y1 = Math.min(tall - 1, Math.ceil((block.y + block.height + MARGIN) / GRID_STEP));
        for (let cy = y0; cy <= y1; cy += 1) {
            for (let cx = x0; cx <= x1; cx += 1) {
                blocked[cy * wide + cx] = 1;
            }
        }
    }
    return { blocked, taken: new Uint8Array(wide * tall), tall, wide };
};

export const cellAt = function cellAt(grid: LearningGrid, point: LearningPoint): number {
    return Math.round(point.y / GRID_STEP) * grid.wide + Math.round(point.x / GRID_STEP);
};

export const isSolid = function isSolid(grid: LearningGrid, point: LearningPoint): boolean {
    const cx = Math.round(point.x / GRID_STEP);
    const cy = Math.round(point.y / GRID_STEP);
    if (cx < 0 || cy < 0 || cx >= grid.wide || cy >= grid.tall) {
        return true;
    }
    return grid.blocked[cy * grid.wide + cx] === 1;
};

const cheapestOf = function cheapestOf(heap: readonly Visit[]): number {
    let pick = 0;
    for (let scan = 1; scan < heap.length; scan += 1) {
        if ((heap[scan]?.spent ?? Infinity) < (heap[pick]?.spent ?? Infinity)) {
            pick = scan;
        }
    }
    return pick;
};

const stepCost = function stepCost(grid: LearningGrid, cell: number, spent: number): number {
    return spent + (grid.taken[cell] === 1 ? CROSSING_COST : 1);
};

const neighbours = function neighbours(grid: LearningGrid, cell: number, goal: number): number[] {
    const cx = cell % grid.wide;
    const cy = Math.floor(cell / grid.wide);
    const found: number[] = [];
    for (const [dx, dy] of NEIGHBOURS) {
        const nx = cx + dx;
        const ny = cy + dy;
        const inside = nx >= 0 && ny >= 0 && nx < grid.wide && ny < grid.tall;
        const step = ny * grid.wide + nx;
        if (inside && (grid.blocked[step] !== 1 || step === goal)) {
            found.push(step);
        }
    }
    return found;
};

interface Reached {
    readonly came: Int32Array;
    readonly cost: Float64Array;
}

const improvements = function improvements(
    grid: LearningGrid,
    reached: Reached,
    here: Visit,
    goal: number,
): readonly Visit[] {
    const better: Visit[] = [];
    for (const step of neighbours(grid, here.cell, goal)) {
        const spent = stepCost(grid, step, here.spent);
        if (spent < (reached.cost[step] ?? Infinity)) {
            reached.cost[step] = spent;
            reached.came[step] = here.cell;
            better.push({ cell: step, spent });
        }
    }
    return better;
};

export const searchGrid = function searchGrid(grid: LearningGrid, start: number, goal: number): Int32Array {
    const reached: Reached = {
        came: new Int32Array(grid.wide * grid.tall).fill(-1),
        cost: new Float64Array(grid.wide * grid.tall).fill(Infinity),
    };
    const heap: Visit[] = [{ cell: start, spent: 0 }];
    reached.came[start] = start;
    reached.cost[start] = 0;
    while (heap.length > 0) {
        const here = heap.splice(cheapestOf(heap), 1)[0] ?? { cell: start, spent: 0 };
        if (here.cell === goal) {
            break;
        }
        if (here.spent <= (reached.cost[here.cell] ?? Infinity)) {
            heap.push(...improvements(grid, reached, here, goal));
        }
    }
    return reached.came;
};

export const walkBack = function walkBack(grid: LearningGrid, came: Int32Array, start: number, goal: number): number[] {
    const back: number[] = [];
    let cursor = goal;
    while (cursor !== start) {
        back.push(cursor);
        grid.taken[cursor] = 1;
        cursor = came[cursor] ?? start;
    }
    back.push(start);
    grid.taken[start] = 1;
    return back.toReversed();
};
```
