debut: LDA #$00
STA $F9 ; a (affiché)
LDA #$01
STA $02 ; b
CLC
suite: LDA #$40 ; durée d'affichage
STA $01
PHP ; PUSH drapeaux
aff: JSR $1F1F ; SCANDS
DEC $01
BNE aff ; Branch if Not Equal
PLP ; PULL drapeaux
BCS debut ; Branch if C = 1
LDA $F9
ADC $02 ; a+b
LDX $02
STX $F9 ; a = b
STA $02 ; b = a+b
JMP suite
@0200 A90085F9A901850218A940850108201F1FC601D0F928B0E8
@0218 A5F96502A60286F985024C09020000000000000000000000
;
COMPTEUR
* = $0200
LDA #$00 ; A = 0
STA $F9 ; compteur affiché (INH) = 00
debut: LDA #$40 ; durée affichage
STA $01 ; compteur de temporisation
boucle: JSR $1F1F ; rafraîchit l'afficheur
DEC $01 ; une passe de moins
BNE boucle ; on répète jusqu'à 0
SED ; SEt Decimal mode (BCD)
LDA $F9 ; lit le compteur
CLC ; efface retenue avant addition
ADC #$01 ; +1 en BCD (09 → 10, 99 → 00)
CLD ; CLear Decimal mode
STA $F9 ; range le nouveau compteur
JMP debut ; recommence une seconde
@0200 A90085F9A9408501201F1FC601D0F9F8A5F9186901D885F9
@0218 4C040200
;
CLIGNOTANT 88
* = $0200
debut: LDA #$88
STA $F9 ; INH : 2 digits de droite
STA $FA ; POINTL: 2 digits du milieu
STA $FB ; POINTH: 2 digits de gauche
LDA #$10 ; durée d'allumage
STA $01
allume: JSR $1F1F ; SCANDS : affiche 888888
DEC $01
BNE allume
LDA #$00
STA $1740 ; SAD = Segment Address Data = 0 : segments éteints
LDY #$20 ; durée d'extinction = 20×FF
ext1: LDX #$FF
ext2: DEX
BNE ext2
DEY
BNE ext1
JMP debut
@0200 A98885F985FA85FBA9408501201F1FC601D0F9A97F8D4117
@0218 A2098E4217A9008D4017E8E8E015D0F2A0FFA2FFCAD0FD88
@0230 D0F84C0002
;
On pages 62 and following of this book, we see different combinations of the 4 XYZT elements of the stack and how to obtain them starting from the DCBA configuration.
We only consider the first 4 elements of the stack.
Input T:Z:Y:X (Always) : D:C:B:A (« : » means ENTER)
D : Level 4 (T) C : Level 3 (Z) B : Level 2 (Y) A : Level 1 (X)
"""
hp50g_solver.py
===============
"""
from itertools import product as iproduct
import heapq
def DUP(s): return [s[0]] + s if len(s) >= 1 else None
def DUPDUP(s): return [s[0], s[0]] + s if len(s) >= 1 else None
def SWAP(s): return [s[1], s[0]] + s[2:] if len(s) >= 2 else None
def DROP(s): return s[1:] if len(s) >= 1 else None
def OVER(s): return [s[1]] + s if len(s) >= 2 else None
def ROT(s): return [s[2]] + s[:2] + s[3:] if len(s) >= 3 else None
def UNROT(s): return s[1:3] + [s[0]] + s[3:] if len(s) >= 3 else None
def DUP2(s): return [s[0], s[1]] + s if len(s) >= 2 else None
def DROP2(s): return s[2:] if len(s) >= 2 else None
def PICK3(s): return [s[2]] + s if len(s) >= 3 else None
def NIP(s): return [s[0]] + s[2:] if len(s) >= 2 else None
def make_parametric_ops(max_n: int) -> dict:
ops = {}
for n in range(2, max_n + 1):
def make_roll(n=n):
def f(s): return [s[n-1]] + s[:n-1] + s[n:] if len(s) >= n else None
f.__name__ = f"{n} ROLL"; return f
def make_rolld(n=n):
def f(s): return s[1:n] + [s[0]] + s[n:] if len(s) >= n else None
f.__name__ = f"{n} ROLLD"; return f
def make_pick(n=n):
def f(s): return [s[n-1]] + s if len(s) >= n else None
f.__name__ = f"{n} PICK"; return f
def make_unpick(n=n):
def f(s): return s[1:n] + [s[0]] + s[n+1:] if len(s) > n else None
f.__name__ = f"{n} UNPICK"; return f
def make_dupn(n=n):
def f(s): return s[:n] + s if len(s) >= n else None
f.__name__ = f"{n} DUPN"; return f
def make_dropn(n=n):
def f(s): return s[n:] if len(s) >= n else None
f.__name__ = f"{n} DROPN"; return f
def make_ndupn(n=n):
def f(s): return [n] + [s[0]] * n + s[1:] if len(s) >= 1 else None
f.__name__ = f"{n} NDUPN"; return f
ops[f"{n} ROLL"] = (make_roll(), 2)
ops[f"{n} ROLLD"] = (make_rolld(), 2)
ops[f"{n} PICK"] = (make_pick(), 2)
ops[f"{n} UNPICK"] = (make_unpick(), 2)
ops[f"{n} DUPN"] = (make_dupn(), 2)
ops[f"{n} DROPN"] = (make_dropn(), 2)
ops[f"{n} NDUPN"] = (make_ndupn(), 2)
return ops
FIXED_OPS = {
"DUP": (DUP, 1), "DUPDUP": (DUPDUP, 1), "SWAP": (SWAP, 1),
"DROP": (DROP, 1), "OVER": (OVER, 1), "ROT": (ROT, 1),
"UNROT": (UNROT, 1), "DUP2": (DUP2, 1), "DROP2": (DROP2, 1),
"PICK3": (PICK3, 1), "NIP": (NIP, 1),
}
PARAM_OPS = make_parametric_ops(8)
ALL_OPS = {**FIXED_OPS, **PARAM_OPS}
INITIAL = ('A', 'B', 'C', 'D', '_5', '_6', '_7', '_8')
SENTINEL = frozenset({'_5', '_6', '_7', '_8'})
LETTERS = ('A', 'B', 'C', 'D')
# ── Dijkstra ─────
def solve_all(max_cost: int = 8) -> dict:
all_targets = set(iproduct(LETTERS, repeat=4))
solutions = {}
remaining = set(all_targets)
dist = {INITIAL: 0}
counter = 0
heap = [(0, counter, INITIAL, [])]
while heap and remaining:
cur_cost, _, stack, path = heapq.heappop(heap)
if dist.get(stack, float('inf')) < cur_cost: continue
top4 = stack[:4]
if top4 in remaining and not any(e in SENTINEL for e in top4):
solutions[top4] = (cur_cost, path)
remaining.discard(top4)
if cur_cost >= max_cost: continue
for name, (fn, op_cost) in ALL_OPS.items():
new_cost = cur_cost + op_cost
if new_cost > max_cost: continue
new_stack_list = fn(list(stack))
if new_stack_list is None or len(new_stack_list) < 4: continue
new_stack = tuple(new_stack_list)
if new_cost < dist.get(new_stack, float('inf')):
dist[new_stack] = new_cost
counter += 1
heapq.heappush(heap, (new_cost, counter, new_stack, path + [name]))
return solutions
def print_table(solutions: dict):
letters = ['A', 'B', 'C', 'D']
combos = list(iproduct(letters, repeat=4))
lines = []
for combo in combos:
target_str = ':'.join(combo[::-1])
if combo in solutions:
cost, path = solutions[combo]
seq = ' '.join(path) if path else "(identité)"
lines.append(f"{target_str} [cost {cost}] {seq}")
else:
lines.append(f"{target_str} [no solution]")
return lines
solutions = solve_all(max_cost=5)
print("Research in progress...")
lines = print_table(solutions)
for l in lines: print(l)
found = sum(1 for c in iproduct(LETTERS, repeat=4) if c in solutions)
print(f"\n{found}/256 targets solved.")
Optimizing a sequence
Can a sequence be simplified? For example, can rot drop over swap be written more simply? The answer is YES, in OVER 3 UNPICK
Séquence > rot drop over swap
Séquence entrée : ROT DROP OVER SWAP
Coût total : 4
Effet sur pile : D : B : B : A (niveau 1 en bas)
Recherche d'optimisations (coût ≤ 3)...
✓ 1 optimisation(s) trouvée(s) (coût 3, gain -1) :
[3] OVER 3 UNPICK
Séquence > swap drop swap over
Séquence entrée : SWAP DROP SWAP OVER
Coût total : 4
Effet sur pile : D : A : C : A (niveau 1 en bas)
Recherche d'optimisations (coût ≤ 3)...
✓ 1 optimisation(s) trouvée(s) (coût 3, gain -1) :
[3] UNROT DROP OVER
Séquence > swap rot drop2 dup dup
Séquence entrée : SWAP ROT DROP2 DUP DUP
Coût total : 5
Effet sur pile : D : A : A : A (niveau 1 en bas)
Recherche d'optimisations (coût ≤ 4)...
✓ 1 optimisation(s) trouvée(s) (coût 3, gain -2) :
[3] UNROT DROP2 DUPDUP
Pyhon program
"""
hp50g_optimizer.py
==================
Trouve une séquence équivalente mais plus courte (moins coûteuse) à une
commande HP50g donnée en entrée.
Usage :
python hp50g_optimizer.py
> Entrez une séquence : SWAP OVER SWAP
> Résultats...
"""
from itertools import product as iproduct
import heapq
import sys
# ── Opérations de pile ────────────────────────────────────────────────────────
def DUP(s): return [s[0]] + s if len(s) >= 1 else None
def DUPDUP(s): return [s[0], s[0]] + s if len(s) >= 1 else None
def SWAP(s): return [s[1], s[0]] + s[2:]if len(s) >= 2 else None
def DROP(s): return s[1:] if len(s) >= 1 else None
def OVER(s): return [s[1]] + s if len(s) >= 2 else None
def ROT(s): return [s[2]] + s[:2] + s[3:] if len(s) >= 3 else None
def UNROT(s): return s[1:3] + [s[0]] + s[3:] if len(s) >= 3 else None
def DUP2(s): return [s[0], s[1]] + s if len(s) >= 2 else None
def DROP2(s): return s[2:] if len(s) >= 2 else None
def PICK3(s): return [s[2]] + s if len(s) >= 3 else None
def NIP(s): return [s[0]] + s[2:] if len(s) >= 2 else None
def make_parametric_ops(max_n: int) -> dict:
ops = {}
for n in range(2, max_n + 1):
def make_roll(n=n):
def f(s): return [s[n-1]] + s[:n-1] + s[n:] if len(s) >= n else None
f.__name__ = f"{n} ROLL"; return f
def make_rolld(n=n):
def f(s): return s[1:n] + [s[0]] + s[n:] if len(s) >= n else None
f.__name__ = f"{n} ROLLD"; return f
def make_pick(n=n):
def f(s): return [s[n-1]] + s if len(s) >= n else None
f.__name__ = f"{n} PICK"; return f
def make_unpick(n=n):
def f(s): return s[1:n] + [s[0]] + s[n+1:] if len(s) > n else None
f.__name__ = f"{n} UNPICK"; return f
def make_dupn(n=n):
def f(s): return s[:n] + s if len(s) >= n else None
f.__name__ = f"{n} DUPN"; return f
def make_dropn(n=n):
def f(s): return s[n:] if len(s) >= n else None
f.__name__ = f"{n} DROPN"; return f
def make_ndupn(n=n):
def f(s): return [n] + [s[0]] * n + s[1:] if len(s) >= 1 else None
f.__name__ = f"{n} NDUPN"; return f
ops[f"{n} ROLL"] = (make_roll(), 2)
ops[f"{n} ROLLD"] = (make_rolld(), 2)
ops[f"{n} PICK"] = (make_pick(), 2)
ops[f"{n} UNPICK"] = (make_unpick(), 2)
ops[f"{n} DUPN"] = (make_dupn(), 2)
ops[f"{n} DROPN"] = (make_dropn(), 2)
ops[f"{n} NDUPN"] = (make_ndupn(), 2)
return ops
FIXED_OPS = {
"DUP": (DUP, 1), "DUPDUP": (DUPDUP, 1), "SWAP": (SWAP, 1),
"DROP": (DROP, 1), "OVER": (OVER, 1), "ROT": (ROT, 1),
"UNROT": (UNROT, 1), "DUP2": (DUP2, 1), "DROP2": (DROP2, 1),
"PICK3": (PICK3, 1), "NIP": (NIP, 1),
}
PARAM_OPS = make_parametric_ops(8)
ALL_OPS = {**FIXED_OPS, **PARAM_OPS}
# ── Pile initiale ─────────────────────────────────────────────────────────────
INITIAL = ('A', 'B', 'C', 'D', '_5', '_6', '_7', '_8')
SENTINEL = frozenset({'_5', '_6', '_7', '_8'})
# ── Parsing de la séquence entrée ─────────────────────────────────────────────
def parse_sequence(raw: str) -> list[str] | None:
"""
Convertit une chaîne comme "SWAP OVER SWAP" en liste de noms d'opérations.
Retourne None si un token est inconnu.
Les noms sont normalisés en majuscules.
"""
tokens = raw.upper().split()
result = []
i = 0
while i < len(tokens):
# Essai de token double : "2 ROLL", "3 PICK", etc.
if i + 1 < len(tokens):
candidate2 = tokens[i] + " " + tokens[i+1]
if candidate2 in ALL_OPS:
result.append(candidate2)
i += 2
continue
candidate1 = tokens[i]
if candidate1 in ALL_OPS:
result.append(candidate1)
i += 1
else:
print(f" ✗ Opération inconnue : '{tokens[i]}'")
return None
return result
def sequence_cost(ops: list[str]) -> int:
return sum(ALL_OPS[op][1] for op in ops)
# ── Application d'une séquence sur une pile ───────────────────────────────────
def apply_sequence(ops: list[str], stack: tuple) -> tuple | None:
s = list(stack)
for name in ops:
fn, _ = ALL_OPS[name]
s = fn(s)
if s is None:
return None
return tuple(s)
# ── Dijkstra : cherche toutes les séquences <= max_cost atteignant target ─────
def find_optimizations(target: tuple, max_cost: int, min_results: int = 1) -> list[tuple]:
"""
Retourne une liste de (cost, path) atteignant `target` depuis INITIAL,
avec cost <= max_cost, triées par coût croissant.
S'arrête dès que min_results solutions sont trouvées au coût minimal.
"""
solutions = []
best_cost = None
dist = {INITIAL: 0}
counter = 0
heap = [(0, counter, INITIAL, [])]
while heap:
cur_cost, _, stack, path = heapq.heappop(heap)
if dist.get(stack, float('inf')) < cur_cost:
continue
if best_cost is not None and cur_cost > best_cost:
break
# La profondeur utile est celle des éléments non-sentinelles
stack_depth = len([e for e in stack if e not in SENTINEL])
# On compare uniquement la partie "utile" de la pile avec la cible
useful = tuple(e for e in stack if e not in SENTINEL)
target_useful = tuple(e for e in target if e not in SENTINEL)
if useful == target_useful and cur_cost > 0:
solutions.append((cur_cost, path))
best_cost = cur_cost
if len(solutions) >= min_results:
continue # continue à drainer le même niveau de coût
if cur_cost >= max_cost:
continue
for name, (fn, op_cost) in ALL_OPS.items():
new_cost = cur_cost + op_cost
if new_cost > max_cost:
continue
new_stack_list = fn(list(stack))
if new_stack_list is None:
continue
new_stack = tuple(new_stack_list)
if new_cost < dist.get(new_stack, float('inf')):
dist[new_stack] = new_cost
counter += 1
heapq.heappush(heap, (new_cost, counter, new_stack, path + [name]))
return solutions
# ── Interface ─────────────────────────────────────────────────────────────────
def main():
print("=" * 60)
print(" HP50g Stack Sequence Optimizer")
print(" Entrez une séquence d'opérations pour trouver un")
print(" équivalent plus court. Ex: SWAP OVER SWAP")
print(" (opérations paramétriques : '2 ROLL', '3 PICK', ...)")
print(" Tapez 'quitter' pour sortir.")
print("=" * 60)
while True:
print()
try:
raw = input("Séquence > ").strip()
except (EOFError, KeyboardInterrupt):
print("\nAu revoir.")
break
if raw.lower() in ("quitter", "quit", "exit", "q"):
print("Au revoir.")
break
if not raw:
continue
# Parsing
ops = parse_sequence(raw)
if ops is None:
continue
if not ops:
print(" (séquence vide)")
continue
input_cost = sequence_cost(ops)
print(f"\n Séquence entrée : {' '.join(ops)}")
print(f" Coût total : {input_cost}")
# Application sur la pile initiale
target = apply_sequence(ops, INITIAL)
if target is None:
print(" ✗ La séquence échoue sur la pile initiale (pile insuffisante ?).")
continue
# Pile résultante (partie lisible)
readable = [e for e in target if e not in SENTINEL]
print(f" Effet sur pile : {' : '.join(readable[::-1])} (niveau 1 en bas)")
# Cas identité
if tuple(e for e in target if e not in SENTINEL) == ('A', 'B', 'C', 'D'):
print(" ⚠ La séquence est une identité (ne modifie pas la pile).")
max_search = input_cost - 1
if max_search <= 0:
print(" ✓ Séquence déjà de coût minimal (1), aucune optimisation possible.")
continue
print(f"\n Recherche d'optimisations (coût ≤ {max_search})...")
solutions = find_optimizations(target, max_cost=max_search, min_results=5)
if not solutions:
print(" ✓ Aucune séquence plus courte trouvée — déjà optimale !")
else:
best = solutions[0][0]
gain = input_cost - best
print(f" ✓ {len(solutions)} optimisation(s) trouvée(s) (coût {best}, gain -{gain}) :\n")
for cost, path in solutions:
seq = ' '.join(path) if path else "(identité — ne rien faire)"
print(f" [{cost}] {seq}")
if __name__ == "__main__":
main()
Voici quelques jeux de mots originaux inspirés du fil de Adam Aaronson sur X. Le principe : appliquer des critères mathématiques ou visuels à des listes de mots français classés par longueur (de 2 à 15 lettres) pour trouver des champions inattendus.
Mot qui parcourt la plus longue distance dans l’alphabet
En parcourant un mot lettre à lettre, on mesure la distance alphabétique entre chaque paire de lettres consécutives. Plus les lettres sautent loin dans l’alphabet, plus le score est élevé.
Exemple : « AXA » — de A à X, il y a 23 pas, puis 23 de retour de X à A, soit une distance totale de 46. Objectif : trouver, pour chaque longueur, le mot cumulant la plus grande distance alphabétique.
La famille des « zézayer » (parler en substituant des « z » aux « j » ou « s ») domine largement les mots courts grâce à l’alternance entre Z, la dernière lettre, et des lettres du début de l’alphabet.
def score(mot):
return sum(abs(ord(b) - ord(a)) for a, b in zip(mot, mot[1:]))
for i in range(2, 16):
with open(f"dic{i}.txt") as f:
mots = [line.strip() for line in f if line.strip()]
solution = max(mots, key=score)
print(f"{len(solution)} : {solution} ({score(solution)})")
Mots les plus étroits
Tous les caractères n’ont pas la même largeur à l’écran. En police Arial 16 px, un « i » ou un « l » occupe bien moins d’espace qu’un « m » ou un « w ». Quel mot, pour chaque longueur, tient dans le moins de pixels possible ? Exemple : « il » écrit en Arial prend moins de place que « ma ». Objectif : trouver, pour chaque longueur, le mot le plus étroit.
Les lettres i, l, f et t sont les grandes gagnantes — et il n’est pas surprenant qu’« illisibilité » apparaisse dans le palmarès, un mot presque fait sur mesure pour ce défi !
# poids des lettres a → z
w = [8.9, 8.9, 8.0, 8.9, 8.9, 4.45, 8.9, 8.9, 3.55, 3.55,
8.0, 3.55, 13.3, 8.9, 8.9, 8.9, 8.9, 5.33, 8.0, 4.45,
8.9, 8.0, 11.6, 8.0, 8.0, 8.0]
# fonction de score d’un mot
def score(mot):
return sum(w[ord(c) - 65] for c in mot) # A=65
# parcours des fichiers
for i in range(2, 16):
with open(f"dic{i}.txt") as f:
mots = [line.strip() for line in f if line.strip()]
# mot avec score minimal
solution = min(mots, key=score)
print(f"{len(solution)} : {solution.lower()} ({score(solution)})")
Mots avec les lettres les plus éloignées dans l’alphabet
Cette fois, on s’intéresse non plus aux sauts entre lettres consécutives, mais à la position absolue de chaque lettre dans l’alphabet. Z vaut 26, A vaut 1. Quel mot affiche la moyenne de position la plus haute ? Exemple : « ZUT » → Z (26) + U (21) + T (20) = 67, moyenne = 22,33. Objectif : trouver, pour chaque longueur, le mot dont les lettres occupent en moyenne la position la plus haute dans l’alphabet.
Le youyou (petit canot à rames) et ses conjugaisons s’imposent dans presque toutes les longueurs — preuve que Y, U et O sont redoutables à ce jeu-là.
def score(mot):
return sum(ord(c) - 64 for c in mot)
for i in range(2, 16):
with open(f"dic{i}.txt") as f:
mots = [line.strip() for line in f if line.strip()]
solution = max(mots, key=score)
moyenne = score(solution) / i
print(f"{len(solution)} : {solution} ({moyenne:.2f})")
Mots écrits avec les n premières lettres de l’alphabet
Nouveau défi : construire des mots en se limitant à un sous-ensemble restreint de l’alphabet. On part des deux premières lettres — A et B — et on en ajoute une à chaque étape jusqu’à la quatorzième. La question est simple : quel est le mot le plus long que l’on peut écrire en n’utilisant que ces lettres-là ?
Avec seulement A et B, on est vite à court d’options : « baba » fait figure de champion avec ses quatre lettres. En ajoutant le C, « cacaba » s’impose avec six lettres. Mais attention — élargir le répertoire ne garantit pas toujours un mot plus long : « cacaba » résiste aussi à l’arrivée du D, et « dédicacée » règne sans partage sur les niveaux 9, 10 et 11.
Les vrais coups de théâtre arrivent plus tard. À 13 lettres disponibles, le lauréat inattendu est « hamamelidacée » — l’hamamélis, cet arbuste aux fleurs d’hiver — avec 13 lettres. Et à 14 lettres, « magdalénienne » lui fait jeu égal.
def score(mot, n):
lettres_autorisees = set("abcdefghijklmnopqrstuvwxyz"[:n])
return all(c in lettres_autorisees for c in mot.lower())
for n in range(2, 15):
meilleur = ""
for i in range(2, 16):
with open(f"dic{i}.txt") as f:
mots = [line.strip() for line in f if line.strip()]
candidats = [m for m in mots if score(m, n)]
if candidats:
plus_long = max(candidats, key=len)
if len(plus_long) > len(meilleur):
meilleur = plus_long
lettres = "abcdefghijklmnopqrstuvwxyz"[:n].upper()
print(f"{n} ({lettres}) : {meilleur.lower()} ({len(meilleur)})")
Mot avec le moins de lettres différentes, pour chaque longueur
Écrire long en variant peu : c’est le défi de ce nouveau challenge. Pour chaque longueur de mot, on cherche celui qui se contente du plus petit alphabet personnel — autrement dit, celui dont les lettres se répètent le plus.
Avec deux lettres, « AA » n’en utilise qu’une seule. Avec trois, « MMM » fait pareil. Mais à partir de quatre lettres, l’exercice se complique : difficile de faire un vrai mot français avec une seule lettre répétée quatre fois. « ALLA » et « ERRER » s’en tirent avec deux lettres distinctes seulement.
La résistance dure longtemps : il faut atteindre les mots de onze lettres pour qu’une quatrième lettre s’invite enfin dans le palmarès. Et même à quinze lettres, « assassinassions » n’en mobilise que cinq — une économie remarquable pour un mot aussi long.
def score(mot):
return len(set(mot))
for i in range(2, 16):
with open(f"dic{i}.txt") as f:
mots = [line.strip() for line in f if line.strip()]
solution = min(mots, key=score)
print(f"{i} : {solution} ({score(solution)})")
Dans les automates élémentaires de Wolfram on ne considère que 3 cases et non pas 5 comme dans le post de @Marge. Avec 3 cases on a 8 combinaisons 111,110,101,100,011,010,001,000 et pour chacune de ces combinaisons on décide si la case centrale (par exemple 111) sera en vie ou morte à l’étape suivante. Ca fait 28 = 256 possibilités. Avec 5 cases, si on ne regarde plus seulement le nombre de voisins vivants mais toutes les combinaisons possibles 11111,11110,…, 00010,00001 soit 32 combinaisons et que l’on décide pour chacune ce que devient la cellule centrale (par exemple 11100) à l’étape suivante, ça fait 232= 4294967296 combinaisons ! Donc il y a certainement plein de motifs intéressants à découvrir si on ne se borne pas à juste compter les voisins vivants
Le cas particulier de @Marge parmi les 4294967296 combinaisons correspond au nombre 393113224 :
from kandinsky import *
from random import randint
T = 2
def go(r = 393113224):
lig = 220 // T
col = 320 // T
# première ligne aléatoire
for x in range(col):
if randint(0,1) == 1:
fill_rect(T*x,0,T,T,(0,0,0))
rb = "0"*32 + bin(r)[2:] # règle binaire (32 bits)
for y in range(lig):
for x in range(2,col-2):
n = (
16*ecran(x-2,y) +
8*ecran(x-1,y) +
4*ecran(x,y) +
2*ecran(x+1,y) +
ecran(x+2,y)
)
if rb[-1-n] == "1":
fill_rect(T*x, T*(y+1), T, T, (0,0,0))
def ecran(x,y):
v = sum(get_pixel(T*x,T*y))
return 1 if v == 0 else 0
go()
En recherchant sur le web, je suis tombé sur https://jcmorrow.com/mandelbrot/ qui propose une écriture en Uiua de la célèbre fractale. Cela m’a permis de créer la fractale Julia et aidé à la traduction du programme Python :
🧩 Challenge 4 : Périmètre et surface d’un rectangle
🎯 Énoncé
Soit un rectangle de côté a et b.
Avec a et b respectivement sur la pile, faire un programme qui sort le Périmètre du rectangle et la Surface du rectangle.
Les formules utilisées sont :
Périmètre : P = 2(a+b)
Surface : S = a × b
🔑 Mots-clés Uiua
⊃ – Appeler deux fonctions sur une même valeur ×₂ – Multiplier par 2
🧠 Solution Uiua
CHA₄ ← ×₂⊃+×
🔍 Explications
La fonction CHA₄ est conçue pour calculer à la fois la surface et le périmètre d’un rectangle à partir des longueurs de ses côtés, en laissant les deux résultats sur la pile.
L’opérateur ⊃ permet d’appliquer les deux calculs et de conserver la Surface et le Périmètre sur la pile dans l’ordre requis par l’énoncé.
Calcul de la Surface : L’opérateur × (multiplication) calcule la surface (S=a×b).
Calcul du Périmètre : L’opérateur + (addition) calcule d’abord la somme des côtés (a+b), puis le résultat est multiplié par 2 (×₂).
🧾 Exemples
Pour les entrées 3 et 4, la fonction donne les résultats suivants :
CHA₄ 3 4
Résultats :
12 # Surface
14 # Périmètre
🧩 Challenge 8 : Chaîne
🎯 Énoncé
Faire un programme qui inverse l’ordre des caractères d’une chaine et qui, en plus, transforme l’ensemble en minuscules.
Exemple : « sILiCIuM » donne « muicilis ».
🔑 Mots-clés Uiua
⇌ – Inverser la chaine, un vecteur ou les lignes d’un tableau ⌵ – Mettre en majuscules ¯ – Inverser la casse
🧠 Solution Uiua
CHA₈ ← ¯⌵⇌
🔍 Explications
Le programme inverse d’abord la chaîne (⇌), la met ensuite entièrement en majuscules (⌵), puis utilise l’opérateur d’inversion de casse (¯). L’inversion de casse (¯) permet de transformer toutes les majuscules résultantes en minuscules.
🧾 Exemples
CHA₈ "sILiCIuM"
→ "muicilis"
🧩 Challenge N°9 : Somme des chiffres d’un nombre
🎯 Énoncé
Soit un nombre entier positif sur la pile. Le programme doit calculer la somme de ses chiffres.
Exemple : 352791 doit retourner 27.
🔑 Mots-clés Uiua
⊥₁₀ – Décomposition du nombre en chiffres /+ – Addition/Somme des éléments d’un vecteur
🧠 Solution Uiua
CHA₉ ← /+⊥₁₀
🔍 Explications
La fonction CHA₉ est conçue pour réaliser la somme des chiffres d’un nombre.
Décomposition : L’opérateur ⊥₁₀ prend le nombre entier positif en entrée et réalise sa décomposition en chiffres (base 10).
Sommation : L’opérateur /+ est ensuite appliqué pour effectuer la somme de tous les chiffres obtenus dans la liste (réduction par l’addition).
🧾 Exemple
Pour le nombre 352791, la fonction donne le résultat suivant :
CHA₉ 352791
Résultat : 27
🧩 Challenge 13 : Aire d’un triangle
🎯 Énoncé
Écrire un programme qui permet de calculer la surface d’un triangle quelconque.
Le calcul de l’aire S d’un triangle quelconque, en ne connaissant que les longueurs a, b et c de ses trois côtés, est permis par la formule de Héron.
La formule est vue comme √(s(s-a)(s-b)(s-c)), où s est la demi-somme des côtés.
🔑 Mots-clés Uiua
÷₂ – Diviser par 2 ⊂ – Joindre 2 éléments (scalaires, vecteurs…) /× – Produit des éléments d’un vecteur
🧠 Solution Uiua
CHA₁₃ ← √/×-⊂0⟜(÷₂/+)
🔍 Explications
La solution implémente la formule de Héron, qui est exprimée dans les sources comme \sqrt{(s-a)(s-b)(s-c)(s-0)}
La fonction se décompose comme suit :
Calcul du demi-périmètre (s): La partie (÷₂/+) calcule la demi-somme (s) des longueurs des côtés a, b, et c.
Préparation du vecteur pour la soustraction: L’opération ⊂0⟜ maintient le vecteur initial des côtés (a,b,c) (⟜) et lui ajoute le nombre 0 (⊂0).
Soustraction et création des termes: L’opérateur – (soustraction) est ensuite appliqué pour créer les termes (a−s),(b−s),(c−s) et (0−s).
Calcul final de l’aire: Enfin, l’opération √/× réalise le produit des quatre termes et applique la racine carrée au résultat.
🧾 Exemples
Pour les entrées 4_5_6 et 3_4_5, la fonction donne les résultats suivants :
∩CHA₁₃ 4_5_6 3_4_5
Résultats :
• 6 (pour un triangle de côtés 3, 4, 5)
• 9.921567416492215 (pour un triangle de côtés 4, 5, 6)
🧩 Challenge 57 : Inversion–Addition
🎯 Énoncé
À partir d’un entier, inverse ses chiffres, additionne les deux nombres, puis recommence jusqu’à obtenir un palindrome (un nombre identique à l’envers). Affiche le palindrome obtenu.
➡️ Bonus : trouver le premier nombre (le plus petit) qui ne génère jamais de palindrome — c’est un nombre de Lychrel.
⊥₁₀ – convertir un nombres en chiffres décimaux ⍜ f g – effectuer l’opération f sur un tableau, puis g puis inverse la transformation f ⊸ – garder la valeur initiale sur la pile ⍢(action|test) – boucle : répéter une action tant qu’un test est vrai
🧠 Solution Uiua
Rev ← ⊸⍜⊥₁₀⇌
CHA₅₇ ← ⍢(+Rev|≠Rev)
🔍 Explications
⊥₁₀ : convertit un nombre en liste de chiffres ⇌ : inverse la liste ⍜ ⊥₁₀ ⇌ : transformation ⊥₁₀ puis ⇌ et enfin transformation inverse de ⊥₁₀ (Chiffres en nombre) ⊸ : conserve le nombre d’origine ⍢(+Rev|≠Rev) : additionne avec l’inverse tant qu’on n’a pas de palindrome
🧾 Exemples
≡CHA₅₇ 165_59_89
→ [4884 1111 8813200023188]
CHA₅₇ 196
→ Error: Cannot take base of ∞
🧩 Challenge N°74 : Cubes en folie
🎯 Énoncé
Trouver tous les nombres taxi (taxicab number) inférieurs à 40000 [1].
L’origine de ce défi est célèbre : En 1919, G.H. Hardy a mentionné à Srinivasa Ramanujan que le numéro de son taxi, 1729, lui semblait banal. Ramanujan répondit qu’il s’agissait au contraire du plus petit nombre exprimable comme somme de deux cubes de deux façons .
Exemple célèbre
1729 = 1³ + 12³ = 9³ + 10³
🔑 Mots-clés Uiua
ⁿ3 ou ⁿ₃ – Calculer la puissance 3 (au cube) ⇡₁ – Générer un intervalle d’entiers, de 1 à N ˙⊞+ – Opérateur qui gère la duplication et la construction de la table de toutes les additions possibles des différents couples. ⊛♭ – Opérateur qui aplatit le tableau et assigne un index unique à chaque valeur ⊸ – Garder la valeur initiale sur la pile ⊕(>2⧻) – Grouper les éléments par leurs index, puis pour chaque groupe, compter (⧻) le nombre d’occurrences et tester s’il y en a plus que 2 (>2) ▽ – Garder (filtrer) les valeurs concernées
🧠 Solution Uiua
CHA₇₄ ← ▽⊕(>2⧻)⊸⊸⊛♭˙⊞+ⁿ₃⇡₁
🔍 Explications
La fonction CHA₇₄ est conçue pour identifier les nombres qui apparaissent au moins deux fois comme somme de deux cubes, en limitant la recherche par le paramètre N (dans l’exemple, N=35 permet de trouver les nombres taxi sous 40000).
ⁿ₃⇡₁ N : Cette partie calcule les puissances 3 des entiers allant de 1 jusqu’à N.
˙⊞+ : Elle génère un tableau de toutes les sommes possibles en additionnant les couples de cubes obtenus.
⊛♭ : L’opérateur .♭ aplatit ce tableau de sommes, et l’opérateur ⊛ assigne ensuite un index unique à chaque valeur.
⊕(>2⧻) : C’est le cœur de la détection : les nombres sont groupés (⊕) selon leur index.
🧩 Challenge N°116 : Le défi du Rami en chiffres
🎯 Énoncé
Dans ce mini-jeu nommé RamiSum, les seules combinaisons valables sont celles avec au moins 3 nombres identiques .
Mission :
En entrée : Un nombre composé de chiffres entre 1 et 9, de taille arbitraire.
En sortie : Le score maximum que l’on peut obtenir, ou 0 si aucun coup n’est possible.
Exemples de combinaisons valables :
Pour le tirage 84884284, les combinaisons valables sont 888, 8888 et 444.
Pour le tirage 123456, on ne peut pas jouer.
🔑 Mots-clés Uiua
⊛ – Opérateur qui assigne des index uniques aux valeurs d’un tableau. ⊕ – Grouper les éléments. ⧻ – Compter le nombre d’occurrences ou la longueur d’un groupe. ⊢ – Garder le premier élément d’un vecteur. >₂ – Tester si la valeur est supérieure à 2. /↥ – Récupérer le maximum des éléments.
🧠 Solution Uiua
CHA₁₁₆ ← /↥××⊸>₂⊕⊃⧻⊢⊸⊛⊥₁₀
🔍 Explications
La fonction CHA₁₁₆ calcule le score maximum en identifiant les chiffres répétés au moins trois fois, puis en multipliant leur valeur par le nombre de fois où ils peuvent.
⊸⊛⊥₁₀ 84884284
[4 8 2 4 8 8 4 8]
[0 1 2 0 1 1 0 1] # Il y a 3 chiffres différents (4, 8, 2) indexés par 0, 1 et 2
⊕⊃⧻⊢⊸⊛⊥₁₀ 84884284
[4 8 2]
[3 4 1] # il y a 3 fois "4", 4 fois "8" et 1 fois "2"
⊸>₂⊕⊃⧻⊢⊸⊛⊥₁₀ 84884284
[4 8 2] # il n'y a que des 4, 8 et 2
[3 4 1] # 3 fois le "4", 4 fois le "8", 1 fois le "2"
[1 1 0] # On ne garde que 3 et 4 car 1 < 3
On multiplie les 3 vecteurs pour obtenir les scores (××) et on récupère le maximum (/↥).
🧩 Challenge 142 : Nombres de Diophante
🎯 Énoncé
Trouver quatre nombres entiers tels que le produit de deux quelconques de ces nombres soit un carré moins un .
Il y a une infinité de quadruplets (a, b, c, d) tels que ab+1, ac+1, ad+1, bc+1 et cd+1 soient des carrés parfaits.
En particulier, cette condition est remplie par le quadruplet paramétrique (k-1, k+1, 4k, 4k(4k²-1)) pour tout k > 1.
🔑 Mots-clés Uiua
⇡₂ – Ajouter 2 à l’intervalle de base (décalage) ⊃ – Appeler 2 fonctions sur une même valeur ⟜ – Garder la valeur initiale en haut de la pile ⊸⊸∘ – Double duplication ⊟₄ – Combiner 4 vecteurs en une matrice × – Multiplication ⍉ – Transposée ⇌ – Inverser l’ordre des lignes
🧠 Solution Uiua
CHA₁₄₂ ← ⍉⇌⊟₄ ×-1×⟜(⊸⊸∘⊃(×4|⊃+-1))⇡₂
🔍 Explications
Le code Uiua est conçu pour générer la série de quadruplets (k−1,k+1,4k,4k(4k² −1)). • ⇡₂ N : Cette fonction prend N en entrée et génère un intervalle auquel elle ajoute 2, produisant la liste [2,3,4,…,N+1]. • ⊃(×4|⊃+-1) : Cet opérateur structuré est utilisé pour appliquer deux actions distinctes : la multiplication par 4 (×4), et les opérations d’ajout et soustraction de 1 (⊃+-1). • ⟜(⊸⊸∘⊃(×4|⊃+-1))⇡₂ : On duplique deux fois l’opération de multiplication par 4. L’opérateur ⟜ maintient l’intervalle généré par ⇡₂ en haut de la pile. • ⊟₄ : Combine les quatre vecteurs situés en haut de la pile pour former une matrice. • ⍉⇌ : Inverse l’ordre des lignes (⇌) puis transpose (⍉) la matrice pour obtenir le format de sortie désiré.
🧾 Exemples
Pour N=20, la fonction génère les 20 premiers quadruplets.
Pour le quadruplet (3,5,16,1008), on peut vérifier que les produits plus un sont bien des carrés parfaits : • 3×5+1=16=4² • 3×16+1=49=7² • 3×1008+1=3025=55² • 5×16+1=81=9² • 16×1008+1=16129=127²
Voici une adaptation tactile du célèbre jeu de logique WaterSort Puzzle, spécialement conçue pour la calculatrice HP Prime en langage Python. Le principe est simple, mais diaboliquement addictif : vous devez verser les couleurs d’un tube à l’autre pour que chaque tube contienne qu’une seule couleur.
Au départ, les couleurs sont mélangées dans plusieurs tubes. À chaque tour, vous pouvez déplacer un empilement de couleur d’un tube vers un autre, à condition que la couleur soit identique ou que le tube soit vide. L’objectif est de reconstituer les couleurs uniformément dans chaque tube, sans déborder.
💡 Ce jeu fait appel à la logique, à l’anticipation… et un peu à votre patience !
Fonctionnalités :
Interface graphique simple et intuitive adaptée à l’écran de la HP Prime
Bouton ® pour recommencer le niveau si vous êtes bloqué
Détection automatique de victoire
Vous trouverez ci-dessous le code source complet ainsi que le fichier watercolor.hpprgm (fonctionne avec la version 2.1 du 13/04/2023 et les suivantes) :
#python jeu
from hpprime import *
from urandom import choice
couleurs_rgb = {
"a": 0x845ec2,
"b": 0xd65db1,
"c": 0xff6f91,
"d": 0xff9671,
"e": 0xffc75f,
"f": 0xf9f871,
"g": 0x9bde7e,
"h": 0x4bbc8e
}
liste_couleurs = list(couleurs_rgb.keys())
LARGEUR_TUBE = 20
HAUTEUR_TUBE = 60
ESPACEMENT_X = 80
ESPACEMENT_Y = 74
MARGE_X = 45
MARGE_Y = 20
COLONNES = 3
LIGNES = 3
TOTAL_COULEURS = 7
NOIR = 0x000000
BLANC = 0xFFFFFF
BG = 0x03132C
def position_tube(index):
col = index % COLONNES
lig = index // COLONNES
x = MARGE_X + col * ESPACEMENT_X
y = MARGE_Y + lig * ESPACEMENT_Y
return x, y
def attendre_clic():
while True:
f1, _ = eval('mouse')
if f1:
while eval('mouse')[0]: pass
return f1[:2]
def tube_clique(x, y):
for i in range(9):
tx, ty = position_tube(i)
if tx - ESPACEMENT_X / 2 < x < tx + LARGEUR_TUBE + ESPACEMENT_X / 2 and ty <= y <= ty + HAUTEUR_TUBE:
return i
return None
def dessiner_tubes(tubes, selectionne=None):
fillrect(0, 0, 0, 320, 240, BG, BG)
for i, tube in enumerate(tubes):
x, y = position_tube(i)
bordure = 0x00FF00 if i == selectionne else BLANC
rect(0, x-2, y-2, LARGEUR_TUBE+4, HAUTEUR_TUBE+4, bordure)
rect(0, x-3, y-3, LARGEUR_TUBE+6, HAUTEUR_TUBE+6, bordure)
fillrect(0, x, y-3, LARGEUR_TUBE, 3, BG, BG)
h = HAUTEUR_TUBE // 4
for j, couleur in enumerate(tube):
c = couleurs_rgb.get(couleur)
fillrect(0, x, y + HAUTEUR_TUBE - (j + 1)*h, LARGEUR_TUBE, h, c, c)
# Bouton recommencer
textout(0, 270, 110, chr(174), BLANC)
def sommet_identique(tube):
if not tube: return None, 0
sommet = tube[-1]
compteur = 1
for i in range(len(tube)-2, -1, -1):
if tube[i] == sommet: compteur += 1
else: break
return sommet, compteur
def peut_verser(depuis, vers):
if not depuis or len(vers) >= 4:
return False
couleur, nb = sommet_identique(depuis)
if not vers: return True
return vers[-1] == couleur
def verser(depuis, vers):
if not peut_verser(depuis, vers): return
couleur, nb = sommet_identique(depuis)
espace = 4 - len(vers)
a_verser = min(nb, espace)
for _ in range(a_verser):
vers.append(depuis.pop())
def generer_tubes():
couleurs = []
for couleur in liste_couleurs[:TOTAL_COULEURS]:
couleurs += [couleur]*4
melange = []
while couleurs:
c = choice(couleurs)
couleurs.remove(c)
melange.append(c)
tubes = [melange[i*4:(i+1)*4] for i in range(TOTAL_COULEURS)]
tubes += [[] for _ in range(9 - TOTAL_COULEURS)]
return tubes
def a_gagne(tubes):
for tube in tubes:
if not tube: continue
if len(tube) != 4: return False
if any(c != tube[0] for c in tube): return False
return True
def afficher_message_gagne():
fillrect(0, 50, 90, 220, 60, BLANC, couleurs_rgb["d"])
textout(0, 85, 105, "Bravo ! Vous avez gagné", NOIR)
fillrect(0, 110, 130, 100, 25, BLANC, couleurs_rgb["g"])
textout(0, 130, 135, "REJOUER", NOIR)
def attendre_redemarrage():
while True:
x, y = attendre_clic()
if 110 <= x <= 210 and 130 <= y <= 155: return
def bouton_redemarrer(x, y):
return 260 <= x <= 300 and 80 <= y <= 130
def main():
while True:
tubes = generer_tubes()
tubes_depart = [tube[:] for tube in tubes]
selection = None
while True:
dessiner_tubes(tubes, selection)
if a_gagne(tubes):
afficher_message_gagne()
attendre_redemarrage()
break
x, y = attendre_clic()
if bouton_redemarrer(x, y):
tubes = [tube[:] for tube in tubes_depart]
selection = None
continue
index = tube_clique(x, y)
if index is None:
selection = None
continue
if selection is None:
if tubes[index]: selection = index
elif selection == index:
selection = None
else:
verser(tubes[selection], tubes[index])
selection = None
main()
#end
EXPORT watercolor()
BEGIN
wait(.3);
python(jeu);
END;
Collectionneur de manuels de mathématiques des années 70, je vous propose quelques exercices de 1976 tirés du cahier de vacances PASSEPORT POUR LE CE1.