Skip to content

Commit fc71413

Browse files
Refactor code
1 parent db513a0 commit fc71413

2 files changed

Lines changed: 289 additions & 0 deletions

File tree

src/automate_universel.ml

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,103 @@ def normaliser_configuration(configuration):
4545
}
4646

4747

48+
def parser_symbole(brut):
49+
soit texte = str(brut).strip()
50+
si texte == "":
51+
retour None
52+
essayer:
53+
soit numerique = float(texte)
54+
si numerique.is_integer():
55+
soit entier = int(numerique)
56+
si str(entier) == texte:
57+
retour entier
58+
sauf:
59+
rien
60+
retour texte
61+
62+
63+
def parser_liste_virgules(valeur, repli):
64+
soit elements = [parser_symbole(fragment) pour fragment dans str(valeur).split(",")]
65+
soit filtres = [element pour element dans elements si element is not None]
66+
retour filtres si len(filtres) > 0 sinon repli
67+
68+
69+
def encoder_liste(valeurs):
70+
retour ",".join([str(valeur) pour valeur dans (valeurs ou [])])
71+
72+
73+
def etat_formulaire_vers_configuration(etat_formulaire, configuration_courante=None):
74+
si configuration_courante is None:
75+
configuration_courante = {}
76+
soit alphabet_entree = parser_liste_virgules(etat_formulaire.get("alphabetInput", ""), [0, 1])
77+
soit alphabet_sortie = parser_liste_virgules(etat_formulaire.get("alphabetOutput", ""), alphabet_entree)
78+
soit voisinage = int(etat_formulaire.get("neighborhood", 3))
79+
si voisinage % 2 == 0:
80+
voisinage = voisinage + 1
81+
soit mode_initial = etat_formulaire.get("initialMode", "centre")
82+
soit configuration = dict(configuration_courante)
83+
configuration["nom"] = str(etat_formulaire.get("name", "")).strip() or "Univers sans nom"
84+
configuration["alphabet_entree"] = alphabet_entree
85+
configuration["alphabet_sortie"] = alphabet_sortie
86+
configuration["taille_voisinage"] = max(1, voisinage)
87+
configuration["nombre_canaux_sortie"] = max(1, int(etat_formulaire.get("channels", 1)))
88+
configuration["largeur"] = max(1, int(etat_formulaire.get("width", 161)))
89+
configuration["hauteur"] = max(1, int(etat_formulaire.get("height", 100)))
90+
configuration["frontiere"] = etat_formulaire.get("boundary", "fixe")
91+
configuration["mode_regle"] = etat_formulaire.get("ruleMode", "table")
92+
configuration["numero_regle"] = etat_formulaire.get("ruleNumber", "0")
93+
configuration["etat_initial"] = {"mode": mode_initial}
94+
soit rendu = dict(configuration_courante.get("rendu", {}))
95+
rendu["taille_cellule"] = max(1, int(etat_formulaire.get("cellSize", 5)))
96+
configuration["rendu"] = rendu
97+
98+
si mode_initial == "motif":
99+
configuration["etat_initial"]["valeurs"] = parser_liste_virgules(
100+
etat_formulaire.get("initialValues", ""),
101+
[alphabet_sortie[1] si len(alphabet_sortie) > 1 sinon alphabet_sortie[0]],
102+
)
103+
si mode_initial == "aleatoire":
104+
configuration["etat_initial"]["graine"] = configuration_courante.get("etat_initial", {}).get("graine", 42)
105+
configuration["etat_initial"]["probabilite"] = max(
106+
0.0,
107+
min(1.0, float(etat_formulaire.get("initialProbability", 0.28))),
108+
)
109+
110+
soit alphabet_change = False
111+
si "alphabet_entree" dans configuration_courante:
112+
si len(configuration_courante["alphabet_entree"]) != len(alphabet_entree):
113+
alphabet_change = True
114+
si configuration_courante.get("taille_voisinage") != voisinage:
115+
alphabet_change = True
116+
si configuration_courante.get("nombre_canaux_sortie") != configuration["nombre_canaux_sortie"]:
117+
alphabet_change = True
118+
si alphabet_change:
119+
configuration["table_transition"] = {}
120+
si "table_transition" non dans configuration:
121+
configuration["table_transition"] = {}
122+
retour configuration
123+
124+
125+
def configuration_vers_etat_formulaire(configuration):
126+
soit config = normaliser_configuration(configuration)
127+
retour {
128+
"name": config["nom"],
129+
"alphabetInput": encoder_liste(config["alphabet_entree"]),
130+
"alphabetOutput": encoder_liste(config["alphabet_sortie"]),
131+
"neighborhood": str(config["taille_voisinage"]),
132+
"channels": str(config["nombre_canaux_sortie"]),
133+
"width": str(config["largeur"]),
134+
"height": str(config["hauteur"]),
135+
"boundary": config["frontiere"],
136+
"initialMode": config.get("etat_initial", {}).get("mode", "centre"),
137+
"initialValues": encoder_liste(config.get("etat_initial", {}).get("valeurs", [])),
138+
"initialProbability": str(config.get("etat_initial", {}).get("probabilite", 0.28)),
139+
"cellSize": str(config.get("rendu", {}).get("taille_cellule", 5)),
140+
"ruleMode": config["mode_regle"],
141+
"ruleNumber": str(config.get("numero_regle", 0)),
142+
}
143+
144+
48145
def cle_voisinage(voisinage):
49146
retour json.dumps(voisinage).replace(", ", ",")
50147

@@ -189,6 +286,57 @@ def table_symetrique(configuration, graine=42):
189286
retour table
190287

191288

289+
def assurer_configuration_rendable(configuration):
290+
soit config = normaliser_configuration(configuration)
291+
si len(config.get("table_transition", {})) == 0:
292+
soit cles = toutes_cles_voisinage(config["alphabet_entree"], config["taille_voisinage"])
293+
config["table_transition"] = {}
294+
pour cle dans cles[: min(5, len(cles))]:
295+
config["table_transition"][cle] = [config["alphabet_sortie"][0]]
296+
retour config
297+
298+
299+
def construire_configuration_regle_generee(configuration, generateur, regle_wolfram):
300+
soit config = normaliser_configuration(configuration)
301+
soit generateur_effectif = generateur
302+
soit binaire = False
303+
si len(config["alphabet_entree"]) == 2:
304+
si str(config["alphabet_entree"][0]) == "0":
305+
si str(config["alphabet_entree"][1]) == "1":
306+
binaire = True
307+
soit wolfram_invalide = False
308+
si config["taille_voisinage"] != 3:
309+
wolfram_invalide = True
310+
si non binaire:
311+
wolfram_invalide = True
312+
si config["nombre_canaux_sortie"] != 1:
313+
wolfram_invalide = True
314+
si generateur_effectif == "wolfram" et wolfram_invalide:
315+
generateur_effectif = "random"
316+
317+
si generateur_effectif == "wolfram":
318+
config["alphabet_entree"] = [0, 1]
319+
config["alphabet_sortie"] = [0, 1]
320+
config["taille_voisinage"] = 3
321+
config["nombre_canaux_sortie"] = 1
322+
config["mode_regle"] = "table"
323+
config["table_transition"] = table_wolfram(int(regle_wolfram))
324+
si generateur_effectif == "random":
325+
config["mode_regle"] = "table"
326+
config["table_transition"] = table_aleatoire(config)
327+
si generateur_effectif == "symmetric":
328+
config["mode_regle"] = "table"
329+
config["table_transition"] = table_symetrique(config)
330+
si generateur_effectif == "totalistic":
331+
config["mode_regle"] = "totalistique"
332+
config["table_transition"] = table_totalistique(config)
333+
334+
retour {
335+
"config": config,
336+
"effectiveGenerator": generateur_effectif,
337+
}
338+
339+
192340
def valider_configuration(configuration):
193341
soit config = normaliser_configuration(configuration)
194342
soit erreurs = []
@@ -337,6 +485,12 @@ def configuration_regle(configuration):
337485
}
338486

339487

488+
def etiquette_espace_regles(configuration):
489+
soit spec = configuration_regle(normaliser_configuration(configuration))
490+
soit max_regle = spec["base"] ** spec["chiffres"]
491+
retour str(spec["t"]) + "^(" + str(spec["m"]) + "·" + str(spec["s"]) + "^" + str(spec["k"]) + ") = " + str(max_regle) + " regles possibles"
492+
493+
340494
def voisinage_vers_index(voisinage, alphabet):
341495
soit s = len(alphabet)
342496
soit index = 0
@@ -582,6 +736,80 @@ def preset_fitness_oscillant():
582736
retour [4, 5, 3, 9, 5, 1]
583737

584738

739+
def etiquette_regle_hud(etat_regle):
740+
soit voisinage = int(etat_regle.get("neighborhood", 3))
741+
soit canaux = int(etat_regle.get("channels", 1))
742+
soit mode_regle = etat_regle.get("ruleMode", "table")
743+
soit regle_wolfram = etat_regle.get("wolframRule", "90")
744+
si mode_regle == "totalistique":
745+
retour "Tot(" + str(voisinage) + ")"
746+
si mode_regle == "aleatoire":
747+
retour "Alea(" + str(voisinage) + ")"
748+
si mode_regle == "numerique":
749+
retour "R" + str(voisinage) + "x" + str(canaux)
750+
si mode_regle == "table":
751+
retour "T" + str(voisinage) + "x" + str(canaux)
752+
retour str(regle_wolfram)
753+
754+
755+
def resumer_configuration(configuration):
756+
soit config = normaliser_configuration(configuration)
757+
soit resume = str(config["largeur"]) + " x " + str(config["hauteur"]) + " cellules, "
758+
resume = resume + str(len(config["alphabet_sortie"])) + " etat(s) visibles, voisinage "
759+
resume = resume + str(config["taille_voisinage"]) + ", depart "
760+
resume = resume + str(config.get("etat_initial", {}).get("mode", "centre"))
761+
retour resume
762+
763+
764+
def resumer_transition(configuration):
765+
soit config = normaliser_configuration(configuration)
766+
soit entrees = list(config.get("table_transition", {}).items())
767+
si config["mode_regle"] == "totalistique":
768+
retour "Mode totalistique avec " + str(len(config["alphabet_sortie"])) + " etat(s) de sortie."
769+
si len(entrees) == 0:
770+
retour "Mode " + str(config["mode_regle"]) + " pilote par le noyau sans table explicite."
771+
retour str(len(entrees)) + " transition(s) actives dans la table en cours."
772+
773+
774+
def decrire_configuration(configuration):
775+
soit config = normaliser_configuration(configuration)
776+
soit entrees = list(config.get("table_transition", {}).items())[:24]
777+
soit html = "<span><b>" + str(config["mode_regle"]) + "</b> -> genere par le noyau</span>"
778+
si len(entrees) > 0:
779+
html = "".join(["<span><b>" + cle + "</b> -> " + json.dumps(valeur) + "</span>" pour cle, valeur dans entrees])
780+
soit meta = str(len(config["alphabet_entree"])) + " symbole(s) entree | "
781+
meta = meta + str(len(config["alphabet_sortie"])) + " sortie | voisinage "
782+
meta = meta + str(config["taille_voisinage"]) + " | "
783+
meta = meta + str(config["nombre_canaux_sortie"]) + " canal(aux) | "
784+
meta = meta + str(config["mode_regle"])
785+
retour {
786+
"title": config["nom"],
787+
"metaText": meta,
788+
"ruleTableHtml": html,
789+
}
790+
791+
792+
def signaux_transition(configuration, limite=6):
793+
soit config = normaliser_configuration(configuration)
794+
soit entrees = list(config.get("table_transition", {}).items())[:limite]
795+
si len(entrees) == 0:
796+
retour [{
797+
"label": config["mode_regle"],
798+
"title": "Noyau dynamique",
799+
"body": "Les sorties sont derivees sans table visible.",
800+
}]
801+
soit signaux = []
802+
pour indice in range(len(entrees)):
803+
soit cle = entrees[indice][0]
804+
soit valeur = entrees[indice][1]
805+
signaux.append({
806+
"label": "Transition " + str(indice + 1),
807+
"title": cle,
808+
"body": json.dumps(valeur),
809+
})
810+
retour signaux
811+
812+
585813
# ============================================================================
586814
# PERTURBATION: Interactive event injection into cellular automata
587815
# ============================================================================

tests/french_core_smoke.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,72 @@ def test_deterministic_random_helper(core):
7171
assert all(0 <= value < 4 for value in first)
7272

7373

74+
def test_form_and_summary_helpers(core):
75+
config = core.etat_formulaire_vers_configuration({
76+
"name": "Formulaire",
77+
"alphabetInput": "0,1,2",
78+
"alphabetOutput": "0,1,2",
79+
"neighborhood": "4",
80+
"channels": "2",
81+
"width": "31",
82+
"height": "12",
83+
"boundary": "circulaire",
84+
"initialMode": "motif",
85+
"initialValues": "1,2,1",
86+
"cellSize": "6",
87+
"ruleMode": "table",
88+
"ruleNumber": "17",
89+
})
90+
assert config["taille_voisinage"] == 5
91+
assert config["etat_initial"]["valeurs"] == [1, 2, 1]
92+
form_state = core.configuration_vers_etat_formulaire(config)
93+
assert form_state["alphabetInput"] == "0,1,2"
94+
assert form_state["cellSize"] == "6"
95+
96+
summary = core.resumer_configuration(config)
97+
assert "31 x 12 cellules" in summary
98+
transition = core.resumer_transition(config)
99+
assert "0 transition" in transition or "Mode table" in transition
100+
hud = core.etiquette_regle_hud({
101+
"neighborhood": "5",
102+
"channels": "2",
103+
"ruleMode": "numerique",
104+
"wolframRule": "90",
105+
})
106+
assert hud == "R5x2"
107+
108+
built = core.construire_configuration_regle_generee({
109+
"alphabet_entree": [0, 1],
110+
"alphabet_sortie": [0, 1],
111+
"taille_voisinage": 3,
112+
"nombre_canaux_sortie": 1,
113+
"mode_regle": "table",
114+
}, "wolfram", 90)
115+
assert built["effectiveGenerator"] == "wolfram"
116+
assert built["config"]["table_transition"]["[1,1,0]"] == [1]
117+
118+
ensured = core.assurer_configuration_rendable({
119+
"alphabet_entree": [0, 1],
120+
"alphabet_sortie": [0, 1],
121+
"taille_voisinage": 3,
122+
"nombre_canaux_sortie": 1,
123+
"mode_regle": "table",
124+
})
125+
assert len(ensured["table_transition"]) > 0
126+
127+
desc = core.decrire_configuration(ensured)
128+
assert "title" in desc and "ruleTableHtml" in desc
129+
signals = core.signaux_transition(ensured)
130+
assert len(signals) > 0
131+
assert "regles possibles" in core.etiquette_espace_regles(ensured)
132+
133+
74134
if __name__ == "__main__":
75135
core = load_core()
76136
test_wolfram_table(core)
77137
test_symmetric_table(core)
78138
test_detailed_universe(core)
79139
test_validation(core)
80140
test_deterministic_random_helper(core)
141+
test_form_and_summary_helpers(core)
81142
print("french core smoke ok")

0 commit comments

Comments
 (0)