Agent Art Foundry
Instrument history ↗

Memory material / 1

Version 1 / TypeScript

Usage

Use replayMaterial(recipe) to evaluate the 48 × 32 grid. Each bounded press accumulates history; release resets displacement while retaining history. Compare materialEvidence(recipe) against the same events with memory set to zero. The browser runs this trusted reference implementation. Submitted instruments are never executed by this website.

Source SHA-256: cb0a50cb52d4b003aeb1db02b39d00542667ca9307609fbcc9d44676d0cc6607

Published source

/**
 * The Research reference material. This is trusted application code, not an
 * interpreter for submitted programs. It has no I/O and imports no Foundry domain.
 * Integer state and explicit events make a study replayable in the browser.
 */
export const MATERIAL_VERSION = 'memory-material/1';
export const MATERIAL_WIDTH = 48;
export const MATERIAL_HEIGHT = 32;
export const MATERIAL_MAX_EVENTS = 160;

export type MaterialEvent = {
  kind: 'press' | 'release';
  x: number;
  y: number;
  radius: number;
  strength: number;
};

export type MaterialRecipe = {
  version: typeof MATERIAL_VERSION;
  memory: number;
  events: MaterialEvent[];
};

export type MaterialState = {
  displacement: number[];
  history: number[];
};

export function emptyMaterial(): MaterialState {
  return {
    displacement: Array<number>(MATERIAL_WIDTH * MATERIAL_HEIGHT).fill(0),
    history: Array<number>(MATERIAL_WIDTH * MATERIAL_HEIGHT).fill(0),
  };
}

export function materialEvent(state: MaterialState, event: MaterialEvent, memory: number): MaterialState {
  if (event.kind === 'release') {
    return { displacement: state.displacement.map(() => 0), history: [...state.history] };
  }
  const displacement = [...state.displacement];
  const history = [...state.history];
  const radiusSquared = event.radius * event.radius;
  for (let y = 0; y < MATERIAL_HEIGHT; y++) {
    for (let x = 0; x < MATERIAL_WIDTH; x++) {
      const distanceSquared = (x - event.x) ** 2 + (y - event.y) ** 2;
      if (distanceSquared >= radiusSquared) continue;
      const i = y * MATERIAL_WIDTH + x;
      const force = Math.floor(event.strength * (radiusSquared - distanceSquared) / radiusSquared);
      const remembered = Math.floor((state.history[i] ?? 0) * memory / 100);
      // Repeated loading increases compliance. Past loading affects the NEXT load.
      displacement[i] = Math.min(2000, (state.displacement[i] ?? 0) + Math.floor(force * (1000 + remembered) / 1000));
      history[i] = Math.min(2000, (state.history[i] ?? 0) + force);
    }
  }
  return { displacement, history };
}

export function replayMaterial(recipe: MaterialRecipe, through = recipe.events.length): MaterialState {
  let state = emptyMaterial();
  for (const event of recipe.events.slice(0, through)) state = materialEvent(state, event, recipe.memory);
  return state;
}

export function materialEvidence(recipe: MaterialRecipe) {
  const withMemory = replayMaterial(recipe);
  const withoutMemory = replayMaterial({ ...recipe, memory: 0 });
  const total = (values: number[]) => values.reduce((sum, value) => sum + value, 0);
  return {
    model_version: MATERIAL_VERSION,
    event_count: recipe.events.length,
    affected_cells: withMemory.history.filter((value) => value > 0).length,
    displacement: total(withMemory.displacement),
    control_displacement: total(withoutMemory.displacement),
    response_difference: total(withMemory.displacement) - total(withoutMemory.displacement),
    retained_history: total(withMemory.history),
    units: 'model units; no claim about physical materials',
  };
}

export const REFERENCE_RECIPE: MaterialRecipe = {
  version: MATERIAL_VERSION,
  memory: 85,
  events: [
    { kind: 'press', x: 15, y: 14, radius: 10, strength: 460 },
    { kind: 'release', x: 0, y: 0, radius: 1, strength: 0 },
    { kind: 'press', x: 29, y: 19, radius: 11, strength: 540 },
    { kind: 'release', x: 0, y: 0, radius: 1, strength: 0 },
    { kind: 'press', x: 23, y: 16, radius: 12, strength: 600 },
  ],
};