Darkwood Blog Blog
  • Articles
  • Veille
  • Releases
  • CrĂ©ateurs
fr
  • de
  • en
Connexion
  • Blog
  • Articles
  • Veille
  • Releases
  • CrĂ©ateurs

đŸ–ïž Darkwaar27 : Pigment — Un casse-tĂȘte avec des dĂ©s basĂ© sur la rotation et l'algĂšbre des couleurs

le 11 octobre 2026

Connectez-vous pour réagir à cet article

🚀 1

Lire : https://darkwoodcom.itch.io/darkwaar27

Un dé, une rÚgle

Pigment est un casse-tĂȘte dĂ©terministe se jouant sur un plateau de 5×5. Un dĂ© occupe une case du plateau. Une commande le dĂ©place vers une case voisine orthogonale et le fait pivoter de quatre-vingt-dix degrĂ©s afin qu’une nouvelle face soit tournĂ©e vers le bas. Cette face tournĂ©e vers le bas colore la case de destination. Rien d’autre ne bouge.

Le joueur ne choisit jamais de couleur dans un menu. La couleur qui apparaĂźt est celle de la face sur laquelle le dĂ© s’est arrĂȘtĂ© aprĂšs avoir Ă©tĂ© pivotĂ© par le chemin tracĂ©. Les quatre directions s’apparentent Ă  un calcul d’itinĂ©raire ; elles redĂ©finissent Ă©galement les faces qui seront disponibles par la suite. C’est ce couplage qui constitue le jeu.

Cinq niveaux créés par l’auteur, une limite de coups par plateau, des fonctions « annuler » et « recommencer ». Aucun Ă©lĂ©ment alĂ©atoire et aucun autre acteur.

ModĂ©lisation d’un dĂ© Ă  six faces

Le dĂ© est constituĂ© de six entiers associĂ©s Ă  chaque face : D, U, N, S, E, W. Les opposĂ©s sont fixes : nul↔noir, rouge↔bleu, jaune↔blanc. Chaque niveau commence avec la mĂȘme orientation :

Direction Face
Bas Null
Haut Noir
Nord Rouge
Sud Bleu
Est Jaune
Ouest Blanc

Une rotation est une permutation de ces six emplacements. L’est et l’ouest laissent le nord et le sud inchangĂ©s ; le nord et le sud laissent l’est et l’ouest inchangĂ©s. Au dĂ©part, la premiĂšre face « Est » est jaune, la premiĂšre face « Sud » est bleue, la premiĂšre face « Nord » est rouge et la premiĂšre face « Ouest » est blanche. Les lancers suivants dĂ©placent ces faces.

static func roll(die: Dictionary, action: String) -> Dictionary:
	var d := int(die["D"])
  var u := int(die["U"])
  var n := int(die["N"])
  var s := int(die["S"])
  var e := int(die["E"])
	var w := int(die["W"])
  match action:
    "E":
 return {"D": e, "U": w, "N": n, "S": s, "E": u, "W": d}
		"W" :
 return {"D" : w, "U" : e, "N" : n, "S" : s, "E" : d, "W" : u}
		"N" :
 return {"D" : n, "U" : s, "N" : u, "S" : d, "E" : e, "W" : w}
		"S" :
 return {"D" : s, "U" : n, "N" : d, "S" : u, "E" : e, "W" : w}
		_ :
 return {"D" : d, "U" : u, "N" : n, "S" : s, "E" : e, "W" : w}

Il s’agit d’une permutation d’états, et non d’une simulation physique. Il n’y a ni vitesse angulaire, ni rĂ©action aux collisions, ni maillage de surface. Le cube Ă  l’écran est un dessin reprĂ©sentant ces six entiers une fois que les rĂšgles ont acceptĂ© la pointe. Un contrĂŽle d’invariance garantit que les opposĂ©s restent cohĂ©rents : si D est rouge, U doit toujours ĂȘtre bleu.

AlgĂšbre des couleurs

Les taches forment un petit alphabet : vide, rouge, jaune, bleu, blanc, noir, orange, violet, vert. La face d’atterrissage Ă©crit la cellule de destination via une fonction dĂ©terministe de (tache_existante, face_d’atterrissage) :

static func mix(existing: int, face: int) -> int:
  if face == EMPTY:
    return existing
  if face == WHITE:
    return EMPTY
  if face == BLACK:
    return BLACK
  if existing == EMPTY:
		return face
  if existing == face:
    return existing
  var low := mini(existing, face)
  var high := maxi(existing, face)
  if low == RED and high == YELLOW:
    return ORANGE
  if low == RED and high == BLUE:
		renvoie VIOLET
  si low == JAUNE et high == BLEU :
    renvoie VERT
  renvoie NOIR

Les couleurs primaires peignent les cases vides et se mĂ©langent pour former des couleurs secondaires. L'application deux fois de la mĂȘme couleur primaire n'a aucun effet. Le blanc efface tout, y compris une case scellĂ©e. Le noir scelle. Une couleur primaire appliquĂ©e sur une couleur secondaire, ou sur une case scellĂ©e, la scelle. La valeur null laisse la case inchangĂ©e — une rotation libre qui consomme tout de mĂȘme un coup.

Le mĂ©lange l’emporte sur la réécriture, car la case se souvient de la premiĂšre couleur primaire. Appliquer la seconde moitiĂ© d’une paire implique de dĂ©penser d’autres cases lors des tours qui ont mis cette face vers le bas. Le plateau est Ă  la fois une toile et une boĂźte de vitesses.

Une action copie l’état, refuse les directions illĂ©gales, effectue un lancer, mĂ©lange, puis classe la phase :

static func apply(state: Dictionary, action: String) -> Dictionary:
  var current := snapshot(state)
	if str(current["phase"]) != "play" or not DIRS.has(action):
    return {"state": current, "events": {"accepted": false, "action": action}}
  var from: Vector2i = current["pos"]
	var to: Vector2i = from + DIRS[action]
  if not _floor(current, to):
    return {"state": current, "events": {"accepted": false, "action": action, "from": from}}
	var die : Dictionary = roll(current["die"], action)
  var stains : Array = current["stains"]
  var at := _index(to)
  var before := int(stains[at])
  var after := mix(before, int(die["D"]))
	stains[at] = after
  current["die"] = die
  current["stains"] = stains
  current["pos"] = to
  current["spent"] = int(current["spent"]) + 1
	# la phase devient « gagnée » ou « perdue » en fonction des marques, de la limite et des conseils légaux

Les conseils hors plateau sont refusĂ©s sans dĂ©penser de coup. Une marque scellĂ©e ne constitue pas un blocage dĂ©finitif immĂ©diat : le blanc peut toujours la supprimer Ă  l’intĂ©rieur de la limite, et la fonction « undo » rĂ©tablit l’orientation et la marque prĂ©cĂ©dentes.

Position × Orientation × Pigment

La recherche de chemin vĂ©rifie si une case est accessible. Le pigment vĂ©rifie si une case est accessible avec une face spĂ©cifique tournĂ©e vers le bas, aprĂšs un historique de taches spĂ©cifique. L’état complet est le produit de :

  • Position sur le 5×5
  • de l’orientation du dĂ© (six faces, contrainte par les opposĂ©s)
  • de vingt-cinq valeurs de coloration
  • des coups utilisĂ©s par rapport Ă  la limite de niveau

Deux chemins menant Ă  la mĂȘme case divergent lorsque le dĂ© arrive avec une face diffĂ©rente, ou lorsqu’un indice antĂ©rieur a laissĂ© le mauvais pigment sur une marque.

Le niveau 2 (Retour) concrĂ©tise ce produit. DĂ©part Ă  (0, 0). La seule marque est (1, 0), verte. Limite 5. Le vert n’est pas prĂ©sent sur le dĂ© ; le jaune et le bleu doivent se rencontrer sur cette case.

Étape Pointe Terrain Face vers le bas Effet sur (1, 0)
1 Est (1, 0) Jaune Vide → Jaune
2 Sud (1, 1) Bleu la case non marquée devient bleue
3 Ouest (0, 1) Nul Pas de couleur
4 Nord (0, 0) Jaune La case de départ devient jaune
5 Est (1, 0) Bleu Jaune + Bleu → Vert

Les Ă©tapes 2 Ă  4 servent principalement Ă  faire pivoter le tableau. La case bleue sous la marque est une preuve fournie par le joueur, pas un objectif. Une autre ligne de longueur 5 rend Ă©galement (1, 0) verte : est, sud, est, nord, ouest. Le niveau 2 accepte les deux. Le niveau 3 ajoute une deuxiĂšme marque — la case de dĂ©part doit ĂȘtre jaune — et la deuxiĂšme ligne Ă©choue. MĂȘme case verte, mais les cases restantes ne sont pas les bonnes. La liste des marques constitue l’intĂ©gralitĂ© de la condition de victoire ; les cases non marquĂ©es peuvent prendre n’importe quelle couleur Ă  la fin.

Cinq niveaux formant une échelle de contraintes

Chaque plateau ajoute une contrainte au mĂȘme mĂ©canisme :

  1. PremiĂšre face — placer du jaune sur (1, 0) en un seul coup. Pas de mĂ©lange.
  2. Retour — mĂ©langer le vert en repassant par certaines cases. Limite de 5.
  3. Ce que vous laissez — du vert sur la marque et du jaune au dĂ©part. MĂȘmes cinq coups que pour le niveau 2 ; la ligne verte alternative ne respecte pas la marque supplĂ©mentaire.
  4. L’autre paire — de l’orange en (1, 1). Le rouge et le jaune doivent se rencontrer. Limite : 6. Peindre la marque une seule fois puis s’arrĂȘter ne permet pas d’obtenir de l’orange.
  5. Blanc — (2, 0) commence scellĂ©. Les marques exigent du jaune sur (1, 0) et que la case scellĂ©e soit vide. Le blanc doit ĂȘtre pivotĂ© sur la case scellĂ©e. Éviter la case scellĂ©e ne permet pas de gagner.

Les limites de coups correspondent aux longueurs les plus courtes des solutions connues ; un dĂ©tour doit donc remplacer des Ă©tapes plutĂŽt que de les complĂ©ter. La victoire est vĂ©rifiĂ©e aprĂšs chaque conseil acceptĂ© : la tache de chaque marque correspond Ă  sa cible. La dĂ©faite survient lorsque la limite est Ă©puisĂ©e avec une marque erronĂ©e, ou lorsqu’il ne reste plus de conseil valide avec une marque erronĂ©e.

Les cas de relecture testent les tables : les opposĂ©s restent cohĂ©rents, les chemins créés atteignent la phase attendue, les marques se maintiennent en cas de victoire, les refus de coin ne consomment pas de coups, et le niveau 5 commence scellĂ©. Il s’agit d’une rĂ©gression par rapport aux lignes connues, et non d’une preuve exhaustive d’unicitĂ©.

La couleur violette figure dans la table de mĂ©lange et n’est pas utilisĂ©e par ces cinq plateaux. La rĂšgle est conservĂ©e afin que l’alphabet ne change pas si un plateau ultĂ©rieur en a besoin.

RĂšgles sĂ©parĂ©es de l’affichage

L’exĂ©cution se divise clairement :

ÉlĂ©ment ResponsabilitĂ©
rules.gd Orientation, mélange, données de niveau, victoire/défaite, vérifications de relecture
main.gd EntrĂ©e, _draw, interpolation, interface utilisateur, pile d’historique
main.tscn Un seul Control

rules.gd est un RefCounted contenant des fonctions statiques. Il n’effectue jamais de dessin. main.gd contient le dictionnaire actuel, enregistre un instantanĂ© en profondeur avant l’acceptation d’une nouvelle piĂšce, puis lance une interpolation. La vue n’invente jamais la face suivante.

Les transitions sont des fonctions dĂ©terministes de l’état et de l’action ; la vue est une projection. Le programme n’est pas purement fonctionnel : les dictionnaires et les tableaux sont modifiĂ©s aprĂšs une copie d’instantanĂ©, et la fonction « Annuler » utilise une pile d’historique explicite plutĂŽt que des donnĂ©es persistantes. Cette sĂ©paration suffit Ă  garantir la testabilitĂ© de la table de permutation sans avoir recours Ă  un arbre de scĂšne composĂ© de nƓuds de faces.

Pas d’ECS, pas de corps physiques, pas de shaders, pas de gĂ©nĂ©rateur procĂ©dural. La fonction « Annuler » retire un Ă©lĂ©ment de l’historique et exĂ©cute une interpolation inverse plus courte. La fonction « RedĂ©marrer » reconstruit start_level(index) et efface la pile. Les deux fonctionnent aprĂšs une perte de donnĂ©es, ce qui permet de rĂ©cupĂ©rer une marque scellĂ©e.

Rendu de l’état

Le plateau est constituĂ© d’une couche de pigment uniforme sur un fond sombre. Les marques sont composĂ©es d’un anneau et d’une lettre (Y, G, O, 
) ; l’objectif n’est donc pas uniquement indiquĂ© par la teinte. Les cases scellĂ©es utilisent un remplissage sombre et un contour fin.

Le dĂ© est un cube pseudo-3D : six quadrilatĂšres, un biais de camĂ©ra, un algorithme de peinture en fonction de la profondeur, des glyphes sur la face supĂ©rieure. Lors d’un dĂ©placement vers le haut, les positions sont interpolĂ©es, le cube s’élĂšve selon un arc sinusoĂŻdal et une Basis pivote de k · π/2 autour de l’axe de dĂ©placement. La tache de destination effectue une interpolation entre la couleur prĂ©cĂ©dente et la nouvelle avec le mĂȘme poids. Un mĂ©lange correspond Ă  un changement de couleur. Le blanc clignote sur le papier, puis se pose au sol.

Sous le plateau, un compas de nuanciers montre la face infĂ©rieure et les quatre cĂŽtĂ©s — le dĂ© posĂ© Ă  plat. L’orientation est la ressource ; le dessin doit rendre la face visible lisible, sinon le mĂ©lange semble arbitraire.

Les commandes sont les flĂšches, les touches WASD, les tapotements sur les cellules voisines et un pavĂ© tactile Ă  l’écran. Pendant qu’une interpolation s’exĂ©cute, les pointes sont ignorĂ©es afin que l’image ne prenne pas de retard par rapport aux rĂšgles.

Ce qu’implique le modùle

Position × orientation × taches : voilĂ  un vaste espace d’états issu d’un petit ensemble de rĂšgles. Les conseils nuls consomment un tour pour pivoter sans peindre. Les tableaux dĂ©terministes restent difficiles Ă  parcourir Ă  l’Ɠil nu : la ligne verte en cinq Ă©tapes est Ă©vidente une fois qu’on l’a vue, mais opaque avant. L’animation qui nomme une rĂšgle — arc de pointe, changement de tache, boussole — fait partie de l’interface, ce n’est pas une dĂ©coration.

La campagne est finie : cinq plateaux, le violet n’est pas utilisĂ©, pas de score. L’objet intĂ©ressant reste le dĂ© : piĂšce et peinture, avec l’orientation comme mĂ©moire. Tout le reste n’est qu’un tableau et une interpolation.

Connectez-vous pour réagir à cet article

🚀 1

Site

  • Plan du Site
  • Contact
  • Mentions lĂ©gales

Network

  • Hello
  • Blog
  • Apps
  • Photos

Social

Darkwood 2026, tous droits réservés