Skip to content

Commit 1f98fe2

Browse files
Update options
1 parent cc6d2c6 commit 1f98fe2

4 files changed

Lines changed: 112 additions & 5 deletions

File tree

public/app.mjs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ const controls = {
4848
cellSize: document.querySelector("#cell-size"),
4949
ruleMode: document.querySelector("#rule-mode"),
5050
wolframRule: document.querySelector("#wolfram-rule"),
51+
ruleNumber: document.querySelector("#rule-number"),
52+
randomRule: document.querySelector("#random-rule"),
53+
ruleSpaceSize: document.querySelector("#rule-space-size"),
5154
ruleGenerator: document.querySelector("#rule-generator"),
5255
json: document.querySelector("#config-json"),
5356
importJson: document.querySelector("#import-json"),
@@ -70,6 +73,7 @@ const liveFieldControls = [
7073
const liveRuleControls = [
7174
controls.ruleMode,
7275
controls.wolframRule,
76+
controls.ruleNumber,
7377
controls.ruleGenerator,
7478
];
7579

@@ -154,6 +158,7 @@ function controlsToConfig() {
154158
hauteur: Math.max(1, Number(controls.height.value || 100)),
155159
frontiere: controls.boundary.value,
156160
mode_regle: controls.ruleMode.value,
161+
numero_regle: controls.ruleNumber.value || "0",
157162
etat_initial: { mode: initialMode },
158163
rendu: { ...(current.rendu || {}), taille_cellule: Math.max(1, Number(controls.cellSize.value || 5)) },
159164
};
@@ -206,7 +211,10 @@ function syncControls(config) {
206211
controls.initialProbability.value = config.etat_initial?.probabilite ?? 0.28;
207212
controls.cellSize.value = config.rendu?.taille_cellule || 5;
208213
controls.ruleMode.value = config.mode_regle || "table";
209-
controls.json.value = JSON.stringify(config, null, 2);
214+
controls.ruleNumber.value = (config.numero_regle ? String(config.numero_regle) : "0");
215+
// Convert BigInt to string for JSON serialization
216+
const configForJson = { ...config, numero_regle: String(config.numero_regle || 0n) };
217+
controls.json.value = JSON.stringify(configForJson, null, 2);
210218
}
211219

212220
const DEFAULT_PALETTES = {
@@ -379,6 +387,12 @@ function describe(config) {
379387
renderTransitionSignals(config);
380388
}
381389

390+
function updateRuleSpaceDisplay(config) {
391+
const { s, k, t, m, maxRule } = window.AutomaginariumCore.ruleConfiguration(config);
392+
const ruleSpaceText = `${t}^(${m}·${s}^${k}) = ${maxRule} règles possibles`;
393+
if (controls.ruleSpaceSize) controls.ruleSpaceSize.textContent = ruleSpaceText;
394+
}
395+
382396
function applyConfig(config, { updateJson = true, updateControls = true, source = "Synchronisation", live = false } = {}) {
383397
const normalized = window.AutomaginariumCore.normaliserConfiguration(config);
384398
const validation = validateConfig(normalized);
@@ -393,8 +407,13 @@ function applyConfig(config, { updateJson = true, updateControls = true, source
393407
state.lastValidConfig = structuredClone(normalized);
394408
state.universe = window.AutomaginariumCore.genererUnivers(normalized);
395409
if (updateControls) syncControls(normalized);
396-
if (updateJson) controls.json.value = JSON.stringify(normalized, null, 2);
410+
if (updateJson) {
411+
// Convert BigInt to string for JSON serialization
412+
const configForJson = { ...normalized, numero_regle: String(normalized.numero_regle || 0n) };
413+
controls.json.value = JSON.stringify(configForJson, null, 2);
414+
}
397415
describe(normalized);
416+
updateRuleSpaceDisplay(normalized);
398417
render();
399418
updateLiveNarrative(normalized, source, live);
400419
setSyncState("ok", live ? "Synchronisation live active" : "Configuration synchronisee");
@@ -543,6 +562,17 @@ PRESETS.forEach((preset) => {
543562
presetSelect.appendChild(option);
544563
});
545564

565+
function randomBigInt(max) {
566+
const bits = max.toString(2).length;
567+
const words = Math.ceil(bits / 32) + 1;
568+
const rng = AutomaginariumCore.mulberry32(Date.now() >>> 0);
569+
let n = 0n;
570+
for (let i = 0; i < words; i += 1) {
571+
n = (n << 32n) | BigInt(Math.floor(rng() * 0x100000000));
572+
}
573+
return n % max;
574+
}
575+
546576
liveFieldControls.forEach((control) => {
547577
control.addEventListener("input", () => queueConfigPreview("Parametres"));
548578
control.addEventListener("change", () => queueConfigPreview("Parametres"));
@@ -566,6 +596,14 @@ document.querySelector("#generate-rule").addEventListener("click", () => {
566596
clearTimeout(previewTimer);
567597
applyGeneratedRule({ updateJson: true, updateControls: true, source: "Regles", live: false });
568598
});
599+
document.querySelector("#random-rule").addEventListener("click", () => {
600+
clearTimeout(previewTimer);
601+
const config = AutomaginariumCore.normaliserConfiguration(controlsToConfig());
602+
const { maxRule } = AutomaginariumCore.ruleConfiguration(config);
603+
controls.ruleNumber.value = randomBigInt(maxRule).toString();
604+
controls.ruleMode.value = "numerique";
605+
applyConfig(controlsToConfig(), { updateJson: true, updateControls: true, source: "Aléatoire", live: false });
606+
});
569607
document.querySelector("#apply-json").addEventListener("click", () => {
570608
try {
571609
clearTimeout(previewTimer);

public/automate-core.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ function normaliserConfiguration(configuration) {
3636
nombre_canaux_sortie: Math.max(1, Number(configuration.nombre_canaux_sortie || 1)),
3737
mode_regle: configuration.mode_regle || "table",
3838
table_transition: configuration.table_transition || {},
39+
numero_regle: configuration.numero_regle !== undefined ? BigInt(configuration.numero_regle) : 0n,
3940
frontiere: configuration.frontiere || "fixe",
4041
valeur_frontiere: configuration.valeur_frontiere ?? alphabetEntree[0],
4142
largeur: Math.max(1, Number(configuration.largeur || 161)),
@@ -75,6 +76,9 @@ function voisinageCellule(ligne, indice, configuration) {
7576
}
7677

7778
function appliquerRegle(voisinage, configuration, random) {
79+
if (configuration.mode_regle === "numerique") {
80+
return getRuleOutput(configuration.numero_regle ?? 0n, voisinage, configuration);
81+
}
7882
if (configuration.mode_regle === "totalistique") {
7983
const sum = voisinage.reduce((acc, value) => acc + Number(value), 0);
8084
const index = Number.isFinite(sum)
@@ -320,4 +324,5 @@ window.AutomaginariumCore = {
320324
codeVoisinageNumerique,
321325
ruleConfiguration,
322326
getRuleOutput,
327+
mulberry32,
323328
};

public/index.html

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,13 @@ <h3>Univers</h3>
9191
<p class="eyebrow">R&egrave;gles</p>
9292
<h3>Espace de transition</h3>
9393
<div class="field-grid compact-fields">
94-
<label>Mode<select id="rule-mode-quick"><option value="table">Table</option><option value="totalistique">Totalistique</option><option value="aleatoire">Al&eacute;atoire</option></select></label>
94+
<label>Mode<select id="rule-mode-quick"><option value="table">Table</option><option value="totalistique">Totalistique</option><option value="aleatoire">Al&eacute;atoire</option><option value="numerique">Num&eacute;rique</option></select></label>
9595
<label>Wolfram<input id="wolfram-rule-quick" type="number" min="0" max="255" step="1" value="90" /></label>
96+
<label>Règle N°<input id="rule-number-quick" type="text" inputmode="numeric" placeholder="0" /></label>
9697
<button id="generate-rule-quick" type="button">G&eacute;n&eacute;rer</button>
98+
<button id="random-rule-quick" type="button">Al&eacute;atoire</button>
9799
</div>
100+
<small id="rule-space-size-quick" class="meta"></small>
98101
</div>
99102

100103
<div id="info-panel" class="inspector-panel">
@@ -288,11 +291,14 @@ <h2>Espace de transition</h2>
288291
<div id="transition-signals" class="transition-signals" aria-label="Aper&ccedil;u des transitions"></div>
289292
</div>
290293
<div class="field-grid compact-fields">
291-
<label>Mode<select id="rule-mode"><option value="table">Table</option><option value="totalistique">Totalistique</option><option value="aleatoire">Al&eacute;atoire dynamique</option></select></label>
294+
<label>Mode<select id="rule-mode"><option value="table">Table</option><option value="totalistique">Totalistique</option><option value="aleatoire">Al&eacute;atoire dynamique</option><option value="numerique">Num&eacute;rique</option></select></label>
292295
<label>Wolfram<input id="wolfram-rule" type="number" min="0" max="255" step="1" value="90" /></label>
296+
<label>Numéro de règle<input id="rule-number" type="text" inputmode="numeric" placeholder="0" /></label>
293297
<label>G&eacute;n&eacute;rateur<select id="rule-generator"><option value="wolfram">Wolfram</option><option value="random">Table al&eacute;atoire</option><option value="symmetric">Table sym&eacute;trique</option><option value="totalistic">Totalistique</option></select></label>
294298
<button id="generate-rule" type="button">G&eacute;n&eacute;rer</button>
299+
<button id="random-rule" type="button">Règle aléatoire</button>
295300
</div>
301+
<p id="rule-space-size" class="meta"></p>
296302
<p class="meta">Les tables sont d&eacute;claratives. Le mode totalistique calcule la sortie depuis la somme du voisinage ; les autres modes mat&eacute;rialisent une table.</p>
297303
</div>
298304

@@ -785,7 +791,8 @@ <h2>Configuration brute</h2>
785791
{ quick: 'grid-width-quick', main: 'grid-width' },
786792
{ quick: 'grid-height-quick', main: 'grid-height' },
787793
{ quick: 'rule-mode-quick', main: 'rule-mode' },
788-
{ quick: 'wolfram-rule-quick', main: 'wolfram-rule' }
794+
{ quick: 'wolfram-rule-quick', main: 'wolfram-rule' },
795+
{ quick: 'rule-number-quick', main: 'rule-number' }
789796
];
790797

791798
syncPairs.forEach(pair => {
@@ -835,6 +842,13 @@ <h2>Configuration brute</h2>
835842
if (generateBtnQuick && generateBtnMain) {
836843
generateBtnQuick.addEventListener('click', () => generateBtnMain.click());
837844
}
845+
846+
// Random rule button sync
847+
const randomBtnQuick = document.getElementById('random-rule-quick');
848+
const randomBtnMain = document.getElementById('random-rule');
849+
if (randomBtnQuick && randomBtnMain) {
850+
randomBtnQuick.addEventListener('click', () => randomBtnMain.click());
851+
}
838852
</script>
839853
</body>
840854
</html>

tests/stage7-canvas-smoke.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,12 +369,62 @@ function testMultiChannelGetOutput() {
369369
assert.deepEqual(AutomaginariumCore.getRuleOutput(1n, [1, 1, 1], config), [0, 0]);
370370
}
371371

372+
function testNumeriqueModeWithRule30() {
373+
const config = AutomaginariumCore.normaliserConfiguration({
374+
alphabet_entree: [0, 1],
375+
alphabet_sortie: [0, 1],
376+
taille_voisinage: 3,
377+
nombre_canaux_sortie: 1,
378+
mode_regle: "numerique",
379+
numero_regle: 30n,
380+
largeur: 5,
381+
hauteur: 2,
382+
etat_initial: { mode: "centre" }
383+
});
384+
const universe = AutomaginariumCore.genererUnivers(config);
385+
assert.equal(config.mode_regle, "numerique", "mode_regle is numerique");
386+
assert.equal(config.numero_regle, 30n, "numero_regle is 30");
387+
assert(universe.lignes.length >= 1, "universe generated");
388+
assert(universe.sorties.length >= 1, "sorties generated");
389+
}
390+
391+
function testNumeriqueModeRuleSpaceSize() {
392+
const config1 = {
393+
alphabet_entree: [0, 1],
394+
alphabet_sortie: [0, 1],
395+
taille_voisinage: 3,
396+
nombre_canaux_sortie: 1,
397+
};
398+
const rc1 = AutomaginariumCore.ruleConfiguration(config1);
399+
assert.equal(rc1.maxRule, 256n, "Binary 3-neighborhood single-channel: 256 rules");
400+
401+
const config2 = {
402+
alphabet_entree: [0, 1],
403+
alphabet_sortie: [0, 1],
404+
taille_voisinage: 3,
405+
nombre_canaux_sortie: 2,
406+
};
407+
const rc2 = AutomaginariumCore.ruleConfiguration(config2);
408+
assert.equal(rc2.maxRule, 65536n, "Binary 3-neighborhood 2-channels: 65536 rules");
409+
410+
const config3 = {
411+
alphabet_entree: [0, 1, 2],
412+
alphabet_sortie: [0, 1, 2],
413+
taille_voisinage: 3,
414+
nombre_canaux_sortie: 1,
415+
};
416+
const rc3 = AutomaginariumCore.ruleConfiguration(config3);
417+
assert.equal(rc3.maxRule, 7625597484987n, "Ternary 3-neighborhood: 3^27 rules");
418+
}
419+
372420
testRuleConfigurationBinary3();
373421
testRuleConfigurationBinary3Binary2Channel();
374422
testRuleConfigurationQuaternary3();
375423
testNeighborhoodToRuleIndex();
376424
testWolfram30GetOutput();
377425
testRuleOutputLargeSpace();
378426
testMultiChannelGetOutput();
427+
testNumeriqueModeWithRule30();
428+
testNumeriqueModeRuleSpaceSize();
379429

380430
console.log("stage7 canvas smoke ok");

0 commit comments

Comments
 (0)