Code: Select all
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RulegolfTable</title>
<style>
body { font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 10px; }
input, select, button { margin: 5px; font-size: 14px; }
button { padding: 5px 8px; }
.section { margin: 10px 0; border: 1px solid #ddd; padding: 8px; border-radius: 5px; }
.icon-btn { font-size: 18px; min-width: 30px; }
.var-list, .trans-list { margin: 5px 0; }
.dialog { display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 15px; border: 1px solid #000; z-index: 100; max-width: 90%; max-height: 90%; overflow: auto; }
.dialog input, .dialog select { margin: 5px 0; width: 100%; }
.state-option { display: inline-block; width: 24px; height: 24px; text-align: center; line-height: 24px; margin: 2px; border: 1px solid #000; }
.trans-grid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 5px; margin: 10px 0; }
.trans-cell { width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; border: 1px solid #000; position: relative; }
.trans-center { grid-column: 3; grid-row: 2; background-color: #f0f0f0; }
.trans-arrow { grid-column: 5; grid-row: 2; display: flex; align-items: center; justify-content: center; font-size: 24px; }
.trans-result { width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; border: 1px solid #000; }
.var-options { display: flex; flex-wrap: wrap; margin: 5px 0; }
.var-option { margin: 2px; padding: 2px 5px; background: #eee; border-radius: 3px; cursor: pointer; }
.var-option:hover { background: #ddd; }
.multi-var { margin-top: 10px; }
.export-box { width: 100%; height: 300px; font-family: monospace; margin: 10px 0; }
.grid-pos-0 { grid-column: 3; grid-row: 1; } /* Top */
.grid-pos-1 { grid-column: 4; grid-row: 1; } /* Top-right */
.grid-pos-2 { grid-column: 4; grid-row: 2; } /* Right */
.grid-pos-3 { grid-column: 4; grid-row: 3; } /* Bottom-right */
.grid-pos-4 { grid-column: 3; grid-row: 3; } /* Bottom */
.grid-pos-5 { grid-column: 2; grid-row: 3; } /* Bottom-left */
.grid-pos-6 { grid-column: 2; grid-row: 2; } /* Left */
.grid-pos-7 { grid-column: 2; grid-row: 1; } /* Top-left */
.grid-pos-8 { grid-column: 3; grid-row: 2; } /* Center */
.grid-pos-9 { grid-column: 5; grid-row: 2; } /* Result */
</style>
</head>
<body>
<h1>RulegolfTable</h1>
<div class="section">
<div>π Rule name: <input type="text" id="ruleName" value="MyRule"></div>
<div>π’ States: <input type="number" id="numStates" min="2" value="2"></div>
<div>ποΈ Neighborhood:
<select id="neighborhood">
<option value="moore">Moore</option>
<option value="vonNeumann">von Neumann</option>
<option value="hexagonal">Hexagonal</option>
</select>
</div>
<div>π Symmetries:
<select id="symmetries">
<option value="none">None</option>
<option value="reflect_horizontal">Reflect Horizontal</option>
<option value="reflect_vertical">Reflect Vertical</option>
<option value="rotate4">Rotate 4</option>
<option value="rotate8">Rotate 8</option>
<option value="rotate4reflect">Rotate4 + Reflect</option>
<option value="permute">Permute</option>
</select>
</div>
</div>
<div class="section">
<button class="icon-btn" onclick="showVarDialog()">β Add Variable</button>
<button class="icon-btn" onclick="showTransDialog()">π Add Transition</button>
<button class="icon-btn" onclick="showColorDialog()">π¨ Set Color</button>
<button class="icon-btn" onclick="showNameDialog()">π·οΈ Set State Name</button>
<button class="icon-btn" onclick="showExportDialog()">π Export Rule</button>
</div>
<div class="section">
<h3>π Variables</h3>
<div id="varList" class="var-list"></div>
<h3>π Transitions</h3>
<div id="transList" class="trans-list"></div>
</div>
<!-- Dialogs -->
<div id="varDialog" class="dialog">
<h3>β Add Variable</h3>
Name: <input type="text" id="varName" placeholder="Base name for multiple variables"><br>
<div class="var-options">
<div class="var-option" onclick="setVarType('states')">States</div>
<div class="var-option" onclick="setVarType('variable')">Same as Variable</div>
</div>
<div id="varStates"></div>
<div id="varVariable" style="display:none">
Variable: <select id="varRefVariable"></select>
</div>
<div class="multi-var">
Create multiple: <input type="number" id="varCount" min="1" value="1" style="width:50px">
<label><input type="checkbox" id="varAddSuffix"> Add numeric suffix</label>
</div>
<button onclick="addVariable()">Add</button>
<button onclick="hideDialog('varDialog')">Cancel</button>
</div>
<div id="transDialog" class="dialog">
<h3>π Add Transition</h3>
<div class="trans-grid" id="transGrid">
<!-- Positions will be added dynamically in the correct order -->
<div class="trans-cell grid-pos-0" title="Top"></div>
<div class="trans-cell grid-pos-1" title="Top-right"></div>
<div class="trans-cell grid-pos-2" title="Right"></div>
<div class="trans-cell grid-pos-3" title="Bottom-right"></div>
<div class="trans-cell grid-pos-4" title="Bottom"></div>
<div class="trans-cell grid-pos-5" title="Bottom-left"></div>
<div class="trans-cell grid-pos-6" title="Left"></div>
<div class="trans-cell grid-pos-7" title="Top-left"></div>
<div class="trans-cell grid-pos-8 trans-center" title="Center"></div>
<div class="trans-arrow">β</div>
<div class="trans-cell grid-pos-9" id="transResultPreview" title="Result"></div>
</div>
<button onclick="addTransition()">Add</button>
<button onclick="hideDialog('transDialog')">Cancel</button>
</div>
<div id="editVarDialog" class="dialog">
<h3>βοΈ Edit Variable</h3>
Name: <input type="text" id="editVarName"><br>
<div class="var-options">
<div class="var-option" onclick="setEditVarType('states')">States</div>
<div class="var-option" onclick="setEditVarType('variable')">Same as Variable</div>
</div>
<div id="editVarStates"></div>
<div id="editVarVariable" style="display:none">
Variable: <select id="editVarRefVariable"></select>
</div>
<button onclick="saveVariable()">Save</button>
<button onclick="hideDialog('editVarDialog')">Cancel</button>
</div>
<div id="editTransDialog" class="dialog">
<h3>βοΈ Edit Transition</h3>
<div class="trans-grid" id="editTransGrid">
<!-- Positions will be added dynamically in the correct order -->
<div class="trans-cell grid-pos-0" title="Top"></div>
<div class="trans-cell grid-pos-1" title="Top-right"></div>
<div class="trans-cell grid-pos-2" title="Right"></div>
<div class="trans-cell grid-pos-3" title="Bottom-right"></div>
<div class="trans-cell grid-pos-4" title="Bottom"></div>
<div class="trans-cell grid-pos-5" title="Bottom-left"></div>
<div class="trans-cell grid-pos-6" title="Left"></div>
<div class="trans-cell grid-pos-7" title="Top-left"></div>
<div class="trans-cell grid-pos-8 trans-center" title="Center"></div>
<div class="trans-arrow">β</div>
<div class="trans-cell grid-pos-9" id="editTransResultPreview" title="Result"></div>
</div>
Sort order: <input type="number" id="editTransOrder" min="0" value="0"><br>
<button onclick="saveTransition()">Save</button>
<button onclick="hideDialog('editTransDialog')">Cancel</button>
</div>
<div id="colorDialog" class="dialog">
<h3>π¨ Set Color</h3>
State: <select id="colorState"></select><br>
Color: <input type="color" id="stateColor" value="#000000"><br>
<button onclick="saveColor()">Save</button>
<button onclick="hideDialog('colorDialog')">Cancel</button>
</div>
<div id="nameDialog" class="dialog">
<h3>π·οΈ Set State Name</h3>
State: <select id="nameState"></select><br>
Name: <input type="text" id="stateName"><br>
<button onclick="saveName()">Save</button>
<button onclick="hideDialog('nameDialog')">Cancel</button>
</div>
<div id="exportDialog" class="dialog">
<h3>π Export Rule</h3>
<textarea id="exportBox" class="export-box" readonly></textarea>
<button onclick="copyToClipboard()">Copy to Clipboard</button>
<button onclick="hideDialog('exportDialog')">Close</button>
</div>
<script>
// Data storage
let variables = [];
let transitions = [];
let stateColors = {};
let stateNames = {};
let editingIndex = -1;
let currentVarType = 'states';
let currentEditVarType = 'states';
// Neighborhood positions in the correct order:
// Center (0), Top (1), Right (2), Bottom (3), Left (4),
// Top-right (5), Bottom-right (6), Bottom-left (7), Top-left (8)
const neighborhoodOrder = [8, 0, 1, 2, 3, 4, 5, 6, 7]; // Center first, then orthogonals, then diagonals
const displayOrder = [0,1,2,3,4,5,6,7,8]; // For visual display
// Initialize
document.addEventListener('DOMContentLoaded', function() {
updateNumStates();
document.getElementById('numStates').addEventListener('change', updateNumStates);
// Setup the transition grids
setupTransitionGrid('transGrid');
setupTransitionGrid('editTransGrid');
});
function setupTransitionGrid(gridId) {
const grid = document.getElementById(gridId);
// Clear only the select elements, keep the grid structure
const cells = grid.querySelectorAll('.trans-cell');
cells.forEach(cell => {
if (cell.id !== 'transResultPreview' && cell.id !== 'editTransResultPreview') {
cell.innerHTML = '';
const select = document.createElement('select');
select.id = `${gridId}N${cell.title.replace('-', '').toLowerCase()}`;
cell.appendChild(select);
}
});
}
function updateNumStates() {
const num = parseInt(document.getElementById('numStates').value);
// Initialize colors and names for states
for (let i = 0; i < num; i++) {
if (!stateColors[i]) stateColors[i] = getRandomColor();
if (!stateNames[i]) stateNames[i] = `State ${i}`;
}
updateVarOptions();
}
function getRandomColor() {
return '#' + Math.floor(Math.random()*16777215).toString(16).padStart(6, '0');
}
function updateVarOptions() {
const varSelect = document.getElementById('varRefVariable');
const editVarSelect = document.getElementById('editVarRefVariable');
varSelect.innerHTML = '';
editVarSelect.innerHTML = '';
variables.forEach((v, i) => {
varSelect.appendChild(new Option(v.name, i));
editVarSelect.appendChild(new Option(v.name, i));
});
}
function setVarType(type) {
currentVarType = type;
document.getElementById('varStates').style.display = type === 'states' ? 'block' : 'none';
document.getElementById('varVariable').style.display = type === 'variable' ? 'block' : 'none';
}
function setEditVarType(type) {
currentEditVarType = type;
document.getElementById('editVarStates').style.display = type === 'states' ? 'block' : 'none';
document.getElementById('editVarVariable').style.display = type === 'variable' ? 'block' : 'none';
}
// Dialog functions
function showVarDialog() {
const numStates = parseInt(document.getElementById('numStates').value);
let html = '';
for (let i = 0; i < numStates; i++) {
html += `<input type="checkbox" id="varState${i}" value="${i}">
<label for="varState${i}"><span class="state-option" style="background:${stateColors[i]}">${i}</span> ${stateNames[i]}</label><br>`;
}
document.getElementById('varStates').innerHTML = html;
document.getElementById('varName').value = '';
document.getElementById('varCount').value = 1;
document.getElementById('varAddSuffix').checked = false;
setVarType('states');
updateVarOptions();
showDialog('varDialog');
}
function createStateOption(state, prefix, selected) {
const option = document.createElement('option');
option.value = state;
option.textContent = state;
if (selected) option.selected = true;
const colorSpan = document.createElement('span');
colorSpan.className = 'state-option';
colorSpan.style.backgroundColor = stateColors[state];
colorSpan.textContent = state;
option.prepend(colorSpan.cloneNode(true));
return option;
}
function populateTransitionGrid(gridId, numStates, pattern) {
// Order for display: Center, Top, Right, Bottom, Left, Top-right, Bottom-right, Bottom-left, Top-left
const displayPositions = ['center', 'top', 'right', 'bottom', 'left', 'topright', 'bottomright', 'bottomleft', 'topleft'];
displayPositions.forEach((pos, index) => {
const select = document.getElementById(`${gridId}N${pos}`);
if (!select) return;
select.innerHTML = '';
const anyOption = document.createElement('option');
select.appendChild(anyOption);
for (let i = 0; i < numStates; i++) {
const selected = pattern && pattern[index] === i;
select.appendChild(createStateOption(i, gridId, selected));
}
// Add variables
variables.forEach((v, vi) => {
const varOption = document.createElement('option');
varOption.value = `var${vi}`;
varOption.textContent = v.name;
if (pattern && pattern[index] === `var${vi}`) varOption.selected = true;
select.appendChild(varOption);
});
});
// Handle result preview
if (gridId === 'transGrid') {
const resultPreview = document.getElementById('transResultPreview');
resultPreview.innerHTML = '';
const resultSelect = document.createElement('select');
resultSelect.id = 'transResult';
for (let i = 0; i < numStates; i++) {
resultSelect.appendChild(createStateOption(i, 'trans'));
}
resultSelect.addEventListener('change', function() {
updateResultPreview(resultPreview, this.value);
});
resultPreview.appendChild(resultSelect);
updateResultPreview(resultPreview, '0');
} else if (gridId === 'editTransGrid') {
const resultPreview = document.getElementById('editTransResultPreview');
resultPreview.innerHTML = '';
const resultSelect = document.createElement('select');
resultSelect.id = 'editTransResult';
for (let i = 0; i < numStates; i++) {
const selected = pattern && pattern.result === i;
resultSelect.appendChild(createStateOption(i, 'editTrans', selected));
}
resultSelect.addEventListener('change', function() {
updateResultPreview(resultPreview, this.value);
});
resultPreview.appendChild(resultSelect);
if (pattern) {
updateResultPreview(resultPreview, pattern.result);
} else {
updateResultPreview(resultPreview, '0');
}
}
}
function showTransDialog() {
const numStates = parseInt(document.getElementById('numStates').value);
// Populate the grid
populateTransitionGrid('transGrid', numStates);
showDialog('transDialog');
}
function updateResultPreview(previewEl, state) {
previewEl.style.backgroundColor = stateColors[state] || '#ffffff';
if (previewEl.querySelector('select')) {
previewEl.querySelector('select').value = state;
}
}
function showEditVarDialog(index) {
editingIndex = index;
const varData = variables[index];
document.getElementById('editVarName').value = varData.name;
if (varData.reference !== undefined) {
setEditVarType('variable');
document.getElementById('editVarRefVariable').value = varData.reference;
} else {
setEditVarType('states');
const numStates = parseInt(document.getElementById('numStates').value);
let html = '';
for (let i = 0; i < numStates; i++) {
const checked = varData.states.includes(i) ? 'checked' : '';
html += `<input type="checkbox" id="editVarState${i}" value="${i}" ${checked}>
<label for="editVarState${i}"><span class="state-option" style="background:${stateColors[i]}">${i}</span> ${stateNames[i]}</label><br>`;
}
document.getElementById('editVarStates').innerHTML = html;
}
updateVarOptions();
showDialog('editVarDialog');
}
function showEditTransDialog(index) {
editingIndex = index;
const trans = transitions[index];
const numStates = parseInt(document.getElementById('numStates').value);
// Convert the pattern to display order
const displayPattern = [
trans.pattern[0], // Center
trans.pattern[1], // Top
trans.pattern[2], // Right
trans.pattern[3], // Bottom
trans.pattern[4], // Left
trans.pattern[5], // Top-right
trans.pattern[6], // Bottom-right
trans.pattern[7], // Bottom-left
trans.pattern[8] // Top-left
];
// Populate the grid with existing pattern
populateTransitionGrid('editTransGrid', numStates, {
...trans,
pattern: displayPattern
});
document.getElementById('editTransOrder').value = trans.order || 0;
showDialog('editTransDialog');
}
function showColorDialog() {
const numStates = parseInt(document.getElementById('numStates').value);
let html = '';
for (let i = 0; i < numStates; i++) {
html += `<option value="${i}">${i}: ${stateNames[i]}</option>`;
}
document.getElementById('colorState').innerHTML = html;
showDialog('colorDialog');
}
function showNameDialog() {
const numStates = parseInt(document.getElementById('numStates').value);
let html = '';
for (let i = 0; i < numStates; i++) {
html += `<option value="${i}">${i}: ${stateNames[i]}</option>`;
}
document.getElementById('nameState').innerHTML = html;
showDialog('nameDialog');
}
function showExportDialog() {
const exportText = generateRuleText();
document.getElementById('exportBox').value = exportText;
showDialog('exportDialog');
}
function showDialog(id) {
document.getElementById(id).style.display = 'block';
}
function hideDialog(id) {
document.getElementById(id).style.display = 'none';
}
// Data manipulation functions
function addVariable() {
const baseName = document.getElementById('varName').value.trim();
if (!baseName) return;
const count = parseInt(document.getElementById('varCount').value);
const addSuffix = document.getElementById('varAddSuffix').checked;
for (let c = 0; c < count; c++) {
const name = addSuffix ? `${baseName}${c || ''}` : baseName;
let varData;
if (currentVarType === 'variable') {
const refIndex = parseInt(document.getElementById('varRefVariable').value);
if (isNaN(refIndex)) return;
varData = { name, reference: refIndex };
} else {
const numStates = parseInt(document.getElementById('numStates').value);
const states = [];
for (let i = 0; i < numStates; i++) {
if (document.getElementById(`varState${i}`).checked) {
states.push(i);
}
}
varData = { name, states };
}
variables.push(varData);
}
updateVarList();
updateVarOptions();
hideDialog('varDialog');
}
function saveVariable() {
if (editingIndex === -1) return;
const name = document.getElementById('editVarName').value.trim();
if (!name) return;
let varData;
if (currentEditVarType === 'variable') {
const refIndex = parseInt(document.getElementById('editVarRefVariable').value);
if (isNaN(refIndex)) return;
varData = { name, reference: refIndex };
} else {
const numStates = parseInt(document.getElementById('numStates').value);
const states = [];
for (let i = 0; i < numStates; i++) {
if (document.getElementById(`editVarState${i}`).checked) {
states.push(i);
}
}
varData = { name, states };
}
variables[editingIndex] = varData;
updateVarList();
updateVarOptions();
hideDialog('editVarDialog');
editingIndex = -1;
}
function addTransition() {
// Get values in display order: Center, Top, Right, Bottom, Left, Top-right, Bottom-right, Bottom-left, Top-left
const displayPositions = ['center', 'top', 'right', 'bottom', 'left', 'topright', 'bottomright', 'bottomleft', 'topleft'];
const pattern = displayPositions.map(pos => {
const val = document.getElementById(`transGridN${pos}`).value;
if (val.startsWith('var')) {
return val;
} else {
return val === '' ? null : parseInt(val);
}
});
const result = parseInt(document.getElementById('transResult').value);
transitions.push({
pattern: [
pattern[0], // Center
pattern[1], // Top
pattern[2], // Right
pattern[3], // Bottom
pattern[4], // Left
pattern[5], // Top-right
pattern[6], // Bottom-right
pattern[7], // Bottom-left
pattern[8] // Top-left
],
result,
order: 0
});
updateTransList();
hideDialog('transDialog');
}
function saveTransition() {
if (editingIndex === -1) return;
// Get values in display order: Center, Top, Right, Bottom, Left, Top-right, Bottom-right, Bottom-left, Top-left
const displayPositions = ['center', 'top', 'right', 'bottom', 'left', 'topright', 'bottomright', 'bottomleft', 'topleft'];
const pattern = displayPositions.map(pos => {
const val = document.getElementById(`editTransGridN${pos}`).value;
if (val.startsWith('var')) {
return val;
} else {
return val === '' ? null : parseInt(val);
}
});
const result = parseInt(document.getElementById('editTransResult').value);
const order = parseInt(document.getElementById('editTransOrder').value);
transitions[editingIndex] = {
pattern: [
pattern[0], // Center
pattern[1], // Top
pattern[2], // Right
pattern[3], // Bottom
pattern[4], // Left
pattern[5], // Top-right
pattern[6], // Bottom-right
pattern[7], // Bottom-left
pattern[8] // Top-left
],
result,
order
};
updateTransList();
hideDialog('editTransDialog');
editingIndex = -1;
}
function saveColor() {
const state = parseInt(document.getElementById('colorState').value);
const color = document.getElementById('stateColor').value;
stateColors[state] = color;
hideDialog('colorDialog');
}
function saveName() {
const state = parseInt(document.getElementById('nameState').value);
const name = document.getElementById('stateName').value.trim();
stateNames[state] = name || `State ${state}`;
hideDialog('nameDialog');
updateVarList();
updateTransList();
}
function deleteVariable(index) {
variables.splice(index, 1);
updateVarList();
updateVarOptions();
}
function deleteTransition(index) {
transitions.splice(index, 1);
updateTransList();
}
// UI update functions
function updateVarList() {
const container = document.getElementById('varList');
container.innerHTML = '';
variables.forEach((varData, index) => {
const btn = document.createElement('button');
btn.className = 'icon-btn';
let displayText;
if (varData.reference !== undefined) {
const refVar = variables[varData.reference];
displayText = `${varData.name} = ${refVar.name}`;
} else {
displayText = `${varData.name} = {${varData.states.join(',')}}`;
}
btn.innerHTML = 'βοΈ ' + displayText;
btn.onclick = () => showEditVarDialog(index);
const delBtn = document.createElement('button');
delBtn.className = 'icon-btn';
delBtn.innerHTML = 'β';
delBtn.onclick = (e) => { e.stopPropagation(); deleteVariable(index); };
const div = document.createElement('div');
div.appendChild(btn);
div.appendChild(delBtn);
container.appendChild(div);
});
}
function updateTransList() {
const container = document.getElementById('transList');
container.innerHTML = '';
// Sort transitions by order
transitions.sort((a, b) => (a.order || 0) - (b.order || 0));
transitions.forEach((trans, index) => {
const patternStr = [
trans.pattern[0], // Center
trans.pattern[1], // Top
trans.pattern[2], // Right
trans.pattern[3], // Bottom
trans.pattern[4], // Left
trans.pattern[5], // Top-right
trans.pattern[6], // Bottom-right
trans.pattern[7], // Bottom-left
trans.pattern[8] // Top-left
].map(s => {
if (s === null) return '*';
if (typeof s === 'string' && s.startsWith('var')) {
const varIndex = parseInt(s.substring(3));
return variables[varIndex].name;
}
return s;
}).join(',');
const resultStr = stateNames[trans.result] || 'State '+trans.result;
const btn = document.createElement('button');
btn.className = 'icon-btn';
btn.innerHTML = 'βοΈ ' + patternStr + ' β ' + resultStr;
btn.onclick = () => showEditTransDialog(index);
const delBtn = document.createElement('button');
delBtn.className = 'icon-btn';
delBtn.innerHTML = 'β';
delBtn.onclick = (e) => { e.stopPropagation(); deleteTransition(index); };
const div = document.createElement('div');
div.appendChild(btn);
div.appendChild(delBtn);
container.appendChild(div);
});
}
// Export function
function generateRuleText() {
const ruleName = document.getElementById('ruleName').value;
const numStates = document.getElementById('numStates').value;
const neighborhood = document.getElementById('neighborhood').value;
const symmetries = document.getElementById('symmetries').value;
let ruleText = `@RULE ${ruleName}\n\n`;
ruleText += `@TABLE\n`;
ruleText += `n_states:${numStates}\n`;
ruleText += `neighborhood:${neighborhood}\n`;
ruleText += `symmetries:${symmetries}\n\n`;
// Add variables
variables.forEach(varData => {
if (varData.reference !== undefined) {
const refVar = variables[varData.reference];
ruleText += `var ${varData.name} = ${refVar.name}\n`;
} else {
ruleText += `var ${varData.name} = {${varData.states.join(',')}}\n`;
}
});
if (variables.length > 0) ruleText += '\n';
// Add transitions
transitions.forEach(trans => {
// Format pattern in the correct order: Center, Top, Right, Bottom, Left, Top-right, Bottom-right, Bottom-left, Top-left
const pattern = [
trans.pattern[0], // Center
trans.pattern[1], // Top
trans.pattern[5], // Right
trans.pattern[2], // Bottom
trans.pattern[6], // Left
trans.pattern[3], // Top-right
trans.pattern[7], // Bottom-right
trans.pattern[4], // Bottom-left
trans.pattern[8] // Top-left
];
const patternStr = pattern.map(s => {
if (s === null) return '*';
if (typeof s === 'string' && s.startsWith('var')) {
const varIndex = parseInt(s.substring(3));
return variables[varIndex].name;
}
return s;
}).join(',');
ruleText += patternStr + ',';
ruleText += trans.result + '\n\n';
});
// Add colors
ruleText += `@COLORS\n`;
for (const [state, color] of Object.entries(stateColors)) {
const rgb = hexToRgb(color);
ruleText += `${state} ${rgb.r} ${rgb.g} ${rgb.b}\n`;
}
return ruleText;
}
function copyToClipboard() {
const exportText = document.getElementById('exportBox').value;
navigator.clipboard.writeText(exportText).then(() => {
alert('Rule copied to clipboard!');
}).catch(err => {
console.error('Failed to copy: ', err);
});
}
function hexToRgb(hex) {
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
return { r, g, b };
}
</script>
</body>
</html>EDIT: this is a HTML + JS script. To run it, paste it into a text editor and save it as HTML file.
EDIT 2: It's indeed "revolutionary" (significantly lowering the barrier of ruletable golfing), but I'm not sure if this is worthy of a separate thread. Tell me if this is notable enough.
EDIT 3: GitHub repository at here.