Skip to content

Instantly share code, notes, and snippets.

@ingenieroariel
Created June 20, 2024 21:59
Show Gist options
  • Save ingenieroariel/c6366fd4c54f42e73a9d8d97b8e36d5f to your computer and use it in GitHub Desktop.
Save ingenieroariel/c6366fd4c54f42e73a9d8d97b8e36d5f to your computer and use it in GitHub Desktop.
Interaction Combinators - Claude Sonnet
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Improved Interaction Combinators Visualization</title>
<style>
canvas {
border: 1px solid black;
}
#controls {
margin-top: 10px;
}
button {
margin-right: 10px;
}
#debug {
margin-top: 10px;
font-family: monospace;
white-space: pre;
}
</style>
</head>
<body>
<canvas id="icCanvas" width="800" height="600"></canvas>
<div id="controls">
<button id="stepBtn">Step</button>
<button id="resetBtn">Reset</button>
</div>
<div id="debug"></div>
<script type="module">
const canvas = document.getElementById('icCanvas');
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const context = canvas.getContext('webgpu');
const format = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device: device,
format: format,
});
const shader = device.createShaderModule({
code: `
struct VertexOutput {
@builtin(position) position: vec4f,
@location(0) color: vec4f,
};
@vertex
fn vertexMain(@location(0) position: vec2f,
@location(1) color: vec4f) -> VertexOutput {
var output: VertexOutput;
output.position = vec4f(position, 0.0, 1.0);
output.color = color;
return output;
}
@fragment
fn fragmentMain(input: VertexOutput) -> @location(0) vec4f {
return input.color;
}
`
});
const pipeline = device.createRenderPipeline({
layout: 'auto',
vertex: {
module: shader,
entryPoint: 'vertexMain',
buffers: [{
arrayStride: 24,
attributes: [
{ shaderLocation: 0, offset: 0, format: 'float32x2' },
{ shaderLocation: 1, offset: 8, format: 'float32x4' },
],
}],
},
fragment: {
module: shader,
entryPoint: 'fragmentMain',
targets: [{ format: format }],
},
});
const CellType = {
CONSTRUCTOR: 0,
DUPLICATOR: 1,
ERASER: 2,
};
class Cell {
constructor(type, x, y) {
this.type = type;
this.x = x;
this.y = y;
this.ports = [null, null, null]; // principal, aux1, aux2
}
}
class Connection {
constructor(fromCell, fromPort, toCell, toPort) {
this.fromCell = fromCell;
this.fromPort = fromPort;
this.toCell = toCell;
this.toPort = toPort;
}
}
let cells = [];
let connections = [];
function createInitialConfiguration() {
cells = [
new Cell(CellType.CONSTRUCTOR, -0.5, 0.5),
new Cell(CellType.CONSTRUCTOR, 0.5, 0.5),
new Cell(CellType.ERASER, 0, -0.5)
];
connections = [
new Connection(cells[0], 0, cells[1], 0),
new Connection(cells[0], 1, cells[2], 0),
new Connection(cells[1], 2, cells[2], 0)
];
updateCellConnections();
}
function updateCellConnections() {
for (let cell of cells) {
cell.ports = [null, null, null];
}
for (let conn of connections) {
conn.fromCell.ports[conn.fromPort] = { cell: conn.toCell, port: conn.toPort };
conn.toCell.ports[conn.toPort] = { cell: conn.fromCell, port: conn.fromPort };
}
}
function step() {
for (let i = 0; i < cells.length; i++) {
for (let j = i + 1; j < cells.length; j++) {
if (areConnectedByPrincipalPorts(cells[i], cells[j])) {
applyInteractionRule(cells[i], cells[j]);
return true;
}
}
}
return false;
}
function areConnectedByPrincipalPorts(cell1, cell2) {
return cell1.ports[0] && cell1.ports[0].cell === cell2 && cell1.ports[0].port === 0;
}
function applyInteractionRule(cell1, cell2) {
if (cell1.type === CellType.CONSTRUCTOR && cell2.type === CellType.CONSTRUCTOR) {
// γγ rule
let aux1Cell1 = cell1.ports[1] ? cell1.ports[1].cell : null;
let aux1Port1 = cell1.ports[1] ? cell1.ports[1].port : null;
let aux2Cell1 = cell1.ports[2] ? cell1.ports[2].cell : null;
let aux2Port1 = cell1.ports[2] ? cell1.ports[2].port : null;
let aux1Cell2 = cell2.ports[1] ? cell2.ports[1].cell : null;
let aux1Port2 = cell2.ports[1] ? cell2.ports[1].port : null;
let aux2Cell2 = cell2.ports[2] ? cell2.ports[2].cell : null;
let aux2Port2 = cell2.ports[2] ? cell2.ports[2].port : null;
// Connect the auxiliary ports of the cells that were connected to the constructors
if (aux1Cell1 && aux1Cell2) {
connect(aux1Cell1, aux1Port1, aux1Cell2, aux1Port2);
}
if (aux2Cell1 && aux2Cell2) {
connect(aux2Cell1, aux2Port1, aux2Cell2, aux2Port2);
}
// Remove the interacting cells
cells = cells.filter(c => c !== cell1 && c !== cell2);
connections = connections.filter(conn =>
conn.fromCell !== cell1 && conn.fromCell !== cell2 &&
conn.toCell !== cell1 && conn.toCell !== cell2
);
updateCellConnections();
}
// Add other rules as needed
}
function connect(cell1, port1, cell2, port2) {
if (!cell1 || !cell2) return;
// Remove any existing connections for these ports
connections = connections.filter(conn =>
!(conn.fromCell === cell1 && conn.fromPort === port1) &&
!(conn.fromCell === cell2 && conn.fromPort === port2) &&
!(conn.toCell === cell1 && conn.toPort === port1) &&
!(conn.toCell === cell2 && conn.toPort === port2)
);
// Add the new connection
connections.push(new Connection(cell1, port1, cell2, port2));
}
function render() {
const vertexData = [];
const indexData = [];
let vertexCount = 0;
// Operation: Render cells
for (let cell of cells) {
const cellSize = 0.05;
const x = cell.x;
const y = cell.y;
let color;
switch (cell.type) {
case CellType.CONSTRUCTOR:
color = [1, 0, 0, 1]; // Red
break;
case CellType.DUPLICATOR:
color = [0, 1, 0, 1]; // Green
break;
case CellType.ERASER:
color = [0, 0, 1, 1]; // Blue
break;
}
vertexData.push(
x - cellSize, y - cellSize, ...color,
x + cellSize, y - cellSize, ...color,
x + cellSize, y + cellSize, ...color,
x - cellSize, y + cellSize, ...color
);
indexData.push(
vertexCount, vertexCount + 1, vertexCount + 2,
vertexCount, vertexCount + 2, vertexCount + 3
);
vertexCount += 4;
}
// Operation: Render connections
for (let conn of connections) {
const fromX = conn.fromCell.x;
const fromY = conn.fromCell.y;
const toX = conn.toCell.x;
const toY = conn.toCell.y;
const lineWidth = 0.005;
const dx = toX - fromX;
const dy = toY - fromY;
const length = Math.sqrt(dx * dx + dy * dy);
const nx = -dy / length * lineWidth;
const ny = dx / length * lineWidth;
vertexData.push(
fromX + nx, fromY + ny, 0, 0, 0, 1,
fromX - nx, fromY - ny, 0, 0, 0, 1,
toX + nx, toY + ny, 0, 0, 0, 1,
toX - nx, toY - ny, 0, 0, 0, 1
);
indexData.push(
vertexCount, vertexCount + 1, vertexCount + 2,
vertexCount + 1, vertexCount + 2, vertexCount + 3
);
vertexCount += 4;
}
// Operation: Create and populate buffers
const vertexBuffer = device.createBuffer({
size: vertexData.length * 4,
usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
});
const indexBuffer = device.createBuffer({
size: indexData.length * 2,
usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(vertexBuffer, 0, new Float32Array(vertexData));
device.queue.writeBuffer(indexBuffer, 0, new Uint16Array(indexData));
// Operation: Encode and submit commands
const commandEncoder = device.createCommandEncoder();
const textureView = context.getCurrentTexture().createView();
const renderPass = commandEncoder.beginRenderPass({
colorAttachments: [{
view: textureView,
clearValue: { r: 1.0, g: 1.0, b: 1.0, a: 1.0 },
loadOp: 'clear',
storeOp: 'store',
}],
});
renderPass.setPipeline(pipeline);
renderPass.setVertexBuffer(0, vertexBuffer);
renderPass.setIndexBuffer(indexBuffer, 'uint16');
renderPass.drawIndexed(indexData.length);
renderPass.end();
device.queue.submit([commandEncoder.finish()]);
}
function debugPrint() {
let output = "Cells:\n";
cells.forEach((cell, index) => {
output += `Cell ${index}: Type=${cell.type}, Pos=(${cell.x}, ${cell.y})\n`;
cell.ports.forEach((port, portIndex) => {
if (port) {
output += ` Port ${portIndex}: Connected to Cell ${cells.indexOf(port.cell)}, Port ${port.port}\n`;
} else {
output += ` Port ${portIndex}: Not connected\n`;
}
});
});
output += "\nConnections:\n";
connections.forEach((conn, index) => {
output += `Connection ${index}: Cell ${cells.indexOf(conn.fromCell)} Port ${conn.fromPort} -> Cell ${cells.indexOf(conn.toCell)} Port ${conn.toPort}\n`;
});
document.getElementById('debug').textContent = output;
}
createInitialConfiguration();
render();
debugPrint();
const stepBtn = document.getElementById('stepBtn');
const resetBtn = document.getElementById('resetBtn');
stepBtn.addEventListener('click', () => {
if (step()) {
render();
debugPrint();
console.log("Step applied successfully.");
} else {
console.log("No more reductions possible.");
}
});
resetBtn.addEventListener('click', () => {
createInitialConfiguration();
render();
debugPrint();
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment