11/*
2- * Automaginarium browser adapter .
2+ * Automaginarium browser bridge .
33 *
4- * This small JavaScript mirror keeps the browser demo usable until
5- * src/automate_universel.ml is compiled through Multilingual. JavaScript
6- * should remain an adapter: configuration in, generated universe out.
4+ * This adapter forwards calls to the compiled French Multilingual core.
5+ * All domain logic (cellular automata, rules, generation) is canonical in
6+ * src/automate_universel.ml. These functions are temporary fallbacks/mirrors
7+ * until the Multilingual→WASM compilation pipeline is live.
8+ *
9+ * JavaScript serves only: canvas rendering, DOM, interface events.
10+ * All cellular automata logic belongs in src/.
711 */
812
913function mulberry32 ( seed ) {
@@ -17,6 +21,43 @@ function mulberry32(seed) {
1721 } ;
1822}
1923
24+ function callPacked ( name , args , fallback ) {
25+ const packed = window . AutomaginariumPacked ;
26+ if ( packed && typeof packed [ name ] === "function" ) {
27+ try {
28+ const value = Number ( packed [ name ] ( ...args ) ) ;
29+ if ( Number . isFinite ( value ) ) return value ;
30+ } catch ( error ) {
31+ console . warn ( `Automaginarium: WASM export ${ name } failed; using fallback.` , error ) ;
32+ }
33+ }
34+ return fallback ;
35+ }
36+
37+ // ============================================================================
38+ // TEMPORARY: These functions are duplicated from src/automate_universel.ml.
39+ // They are fallbacks until the ML is compiled to WASM/JS.
40+ // DO NOT ADD NEW LOGIC HERE. Update src/automate_universel.ml instead.
41+ // ============================================================================
42+
43+ function cleVoisinage ( voisinage ) {
44+ return JSON . stringify ( voisinage ) ;
45+ }
46+
47+ function ancienneCleVoisinage ( voisinage ) {
48+ return voisinage . map ( String ) . join ( "" ) ;
49+ }
50+
51+ function sortieDefaut ( configuration ) {
52+ return Array . from ( { length : configuration . nombre_canaux_sortie } , ( ) => configuration . alphabet_sortie [ 0 ] ?? 0 ) ;
53+ }
54+
55+ function normaliserSortie ( sortie , configuration ) {
56+ const valeurs = Array . isArray ( sortie ) ? [ ...sortie ] : [ sortie ] ;
57+ while ( valeurs . length < configuration . nombre_canaux_sortie ) valeurs . push ( valeurs [ 0 ] ?? configuration . alphabet_sortie [ 0 ] ) ;
58+ return valeurs . slice ( 0 , configuration . nombre_canaux_sortie ) ;
59+ }
60+
2061function normaliserConfiguration ( configuration ) {
2162 const alphabetEntree = Array . isArray ( configuration . alphabet_entree ) && configuration . alphabet_entree . length > 0
2263 ? configuration . alphabet_entree
@@ -46,18 +87,6 @@ function normaliserConfiguration(configuration) {
4687 } ;
4788}
4889
49- function cleVoisinage ( voisinage ) {
50- return JSON . stringify ( voisinage ) ;
51- }
52-
53- function ancienneCleVoisinage ( voisinage ) {
54- return voisinage . map ( String ) . join ( "" ) ;
55- }
56-
57- function sortieDefaut ( configuration ) {
58- return Array . from ( { length : configuration . nombre_canaux_sortie } , ( ) => configuration . alphabet_sortie [ 0 ] ?? 0 ) ;
59- }
60-
6190function lireCellule ( ligne , indice , configuration ) {
6291 if ( indice >= 0 && indice < ligne . length ) return ligne [ indice ] ;
6392 if ( configuration . frontiere === "circulaire" && ligne . length > 0 ) {
@@ -75,6 +104,76 @@ function voisinageCellule(ligne, indice, configuration) {
75104 return voisinage ;
76105}
77106
107+ function ruleConfiguration ( configuration ) {
108+ const s = configuration . alphabet_entree . length ;
109+ const k = configuration . taille_voisinage ;
110+ const t = configuration . alphabet_sortie . length ;
111+ const m = configuration . nombre_canaux_sortie ;
112+ const base = Math . pow ( t , m ) ;
113+ const digits = Math . pow ( s , k ) ;
114+ const baseBig = BigInt ( Math . round ( base ) ) ;
115+ const digitsBig = BigInt ( Math . round ( digits ) ) ;
116+ const maxRule = baseBig ** digitsBig ;
117+ return { s, k, t, m, base, digits, maxRule } ;
118+ }
119+
120+ function neighborhoodToRuleIndex ( voisinage , alphabet ) {
121+ const s = alphabet . length ;
122+ let index = 0 ;
123+ for ( let i = 0 ; i < voisinage . length ; i += 1 ) {
124+ const symbolIndex = alphabet . indexOf ( voisinage [ i ] ) ;
125+ if ( symbolIndex < 0 ) return null ;
126+ index = index * s + symbolIndex ;
127+ }
128+ return index ;
129+ }
130+
131+ function ruleIndexToNeighborhood ( index , config ) {
132+ const { s, k } = ruleConfiguration ( config ) ;
133+ const voisinage = [ ] ;
134+ let remaining = index ;
135+ for ( let i = k - 1 ; i >= 0 ; i -= 1 ) {
136+ const symbolIndex = Math . floor ( remaining / Math . pow ( s , i ) ) ;
137+ voisinage . push ( config . alphabet_entree [ symbolIndex ] ) ;
138+ remaining = remaining % Math . pow ( s , i ) ;
139+ }
140+ return voisinage ;
141+ }
142+
143+ function outputToRuleDigit ( output , config ) {
144+ const { t, m } = ruleConfiguration ( config ) ;
145+ const normalized = normaliserSortie ( output , config ) ;
146+ let digit = 0 ;
147+ for ( let ch = 0 ; ch < m ; ch += 1 ) {
148+ const symbolIndex = config . alphabet_sortie . indexOf ( normalized [ ch ] ) ;
149+ if ( symbolIndex < 0 ) return null ;
150+ digit = digit * t + symbolIndex ;
151+ }
152+ return digit ;
153+ }
154+
155+ function ruleDigitToOutput ( digit , config ) {
156+ const { t, m } = ruleConfiguration ( config ) ;
157+ const output = [ ] ;
158+ let remaining = digit ;
159+ for ( let ch = m - 1 ; ch >= 0 ; ch -= 1 ) {
160+ const symbolIndex = Math . floor ( remaining / Math . pow ( t , ch ) ) ;
161+ output . push ( config . alphabet_sortie [ symbolIndex ] ) ;
162+ remaining = remaining % Math . pow ( t , ch ) ;
163+ }
164+ return output ;
165+ }
166+
167+ function getRuleOutput ( ruleNumber , voisinage , config ) {
168+ const index = neighborhoodToRuleIndex ( voisinage , config . alphabet_entree ) ;
169+ if ( index === null ) return sortieDefaut ( config ) ;
170+ const { base } = ruleConfiguration ( config ) ;
171+ const baseBig = BigInt ( base ) ;
172+ const ruleNumberBig = BigInt ( ruleNumber ) ;
173+ const digit = Number ( ( ruleNumberBig / ( baseBig ** BigInt ( index ) ) ) % baseBig ) ;
174+ return ruleDigitToOutput ( digit , config ) ;
175+ }
176+
78177function appliquerRegle ( voisinage , configuration , random ) {
79178 if ( configuration . mode_regle === "numerique" ) {
80179 return getRuleOutput ( configuration . numero_regle ?? 0n , voisinage , configuration ) ;
@@ -162,112 +261,6 @@ function tableWolfram(numeroRegle) {
162261 return table ;
163262}
164263
165- function ruleConfiguration ( configuration ) {
166- const s = configuration . alphabet_entree . length ; // size of input alphabet
167- const k = configuration . taille_voisinage ; // neighborhood size
168- const t = configuration . alphabet_sortie . length ; // size of output alphabet
169- const m = configuration . nombre_canaux_sortie ; // number of output channels
170- const base = Math . pow ( t , m ) ;
171- const digits = Math . pow ( s , k ) ;
172- // Use BigInt for maxRule to handle large numbers correctly
173- const baseBig = BigInt ( Math . round ( base ) ) ;
174- const digitsBig = BigInt ( Math . round ( digits ) ) ;
175- const maxRule = baseBig ** digitsBig ;
176- return { s, k, t, m, base, digits, maxRule } ;
177- }
178-
179- function neighborhoodToRuleIndex ( voisinage , alphabet ) {
180- const s = alphabet . length ;
181- let index = 0 ;
182- for ( let i = 0 ; i < voisinage . length ; i += 1 ) {
183- const symbolIndex = alphabet . indexOf ( voisinage [ i ] ) ;
184- if ( symbolIndex < 0 ) return null ;
185- index = index * s + symbolIndex ;
186- }
187- return index ;
188- }
189-
190- function ruleIndexToNeighborhood ( index , config ) {
191- const { s, k } = ruleConfiguration ( config ) ;
192- const voisinage = [ ] ;
193- let remaining = index ;
194- for ( let i = k - 1 ; i >= 0 ; i -= 1 ) {
195- const symbolIndex = Math . floor ( remaining / Math . pow ( s , i ) ) ;
196- voisinage . push ( config . alphabet_entree [ symbolIndex ] ) ;
197- remaining = remaining % Math . pow ( s , i ) ;
198- }
199- return voisinage ;
200- }
201-
202- function outputToRuleDigit ( output , config ) {
203- const { t, m } = ruleConfiguration ( config ) ;
204- const normalized = normaliserSortie ( output , config ) ;
205- let digit = 0 ;
206- for ( let ch = 0 ; ch < m ; ch += 1 ) {
207- const symbolIndex = config . alphabet_sortie . indexOf ( normalized [ ch ] ) ;
208- if ( symbolIndex < 0 ) return null ;
209- digit = digit * t + symbolIndex ;
210- }
211- return digit ;
212- }
213-
214- function ruleDigitToOutput ( digit , config ) {
215- const { t, m } = ruleConfiguration ( config ) ;
216- const output = [ ] ;
217- let remaining = digit ;
218- for ( let ch = m - 1 ; ch >= 0 ; ch -= 1 ) {
219- const symbolIndex = Math . floor ( remaining / Math . pow ( t , ch ) ) ;
220- output . push ( config . alphabet_sortie [ symbolIndex ] ) ;
221- remaining = remaining % Math . pow ( t , ch ) ;
222- }
223- return output ;
224- }
225-
226- function getRuleOutput ( ruleNumber , voisinage , config ) {
227- const index = neighborhoodToRuleIndex ( voisinage , config . alphabet_entree ) ;
228- if ( index === null ) return sortieDefaut ( config ) ;
229-
230- const { base } = ruleConfiguration ( config ) ;
231- const baseBig = BigInt ( base ) ;
232- const ruleNumberBig = BigInt ( ruleNumber ) ;
233-
234- const digit = Number ( ( ruleNumberBig / ( baseBig ** BigInt ( index ) ) ) % baseBig ) ;
235- return ruleDigitToOutput ( digit , config ) ;
236- }
237-
238- function callPacked ( name , args , fallback ) {
239- const packed = window . AutomaginariumPacked ;
240- if ( packed && typeof packed [ name ] === "function" ) {
241- try {
242- const value = Number ( packed [ name ] ( ...args ) ) ;
243- if ( Number . isFinite ( value ) ) return value ;
244- } catch ( error ) {
245- console . warn ( `Automaginarium: WASM export ${ name } failed; using fallback.` , error ) ;
246- }
247- }
248- return fallback ;
249- }
250-
251- function codeVoisinageNumerique ( voisinage , tailleAlphabet ) {
252- if ( ! voisinage . every ( ( value ) => Number . isInteger ( Number ( value ) ) ) ) return null ;
253- const values = voisinage . map ( Number ) ;
254- if ( values . length === 3 ) {
255- return Math . trunc ( callPacked (
256- "code_voisinage_3_base" ,
257- [ values [ 0 ] , values [ 1 ] , values [ 2 ] , tailleAlphabet ] ,
258- values . reduce ( ( code , value ) => code * tailleAlphabet + value , 0 ) ,
259- ) ) ;
260- }
261- if ( values . length === 5 ) {
262- return Math . trunc ( callPacked (
263- "code_voisinage_5_base" ,
264- [ values [ 0 ] , values [ 1 ] , values [ 2 ] , values [ 3 ] , values [ 4 ] , tailleAlphabet ] ,
265- values . reduce ( ( code , value ) => code * tailleAlphabet + value , 0 ) ,
266- ) ) ;
267- }
268- return values . reduce ( ( code , value ) => code * tailleAlphabet + value , 0 ) ;
269- }
270-
271264function toutesClesVoisinage ( alphabet , taille ) {
272265 const keys = [ ] ;
273266 function visit ( prefix , depth ) {
@@ -281,12 +274,6 @@ function toutesClesVoisinage(alphabet, taille) {
281274 return keys ;
282275}
283276
284- function normaliserSortie ( sortie , configuration ) {
285- const valeurs = Array . isArray ( sortie ) ? [ ...sortie ] : [ sortie ] ;
286- while ( valeurs . length < configuration . nombre_canaux_sortie ) valeurs . push ( valeurs [ 0 ] ?? configuration . alphabet_sortie [ 0 ] ) ;
287- return valeurs . slice ( 0 , configuration . nombre_canaux_sortie ) ;
288- }
289-
290277function validerConfiguration ( configurationBrute ) {
291278 const config = normaliserConfiguration ( configurationBrute ) ;
292279 const erreurs = [ ] ;
@@ -321,8 +308,8 @@ window.AutomaginariumCore = {
321308 cleVoisinage,
322309 toutesClesVoisinage,
323310 validerConfiguration,
324- codeVoisinageNumerique,
325311 ruleConfiguration,
326312 getRuleOutput,
327313 mulberry32,
314+ callPacked,
328315} ;
0 commit comments