Created
June 20, 2024 21:59
-
-
Save ingenieroariel/c6366fd4c54f42e73a9d8d97b8e36d5f to your computer and use it in GitHub Desktop.
Interaction Combinators - Claude Sonnet
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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