seed.go raw

   1  package strategy
   2  
   3  import (
   4  	"fmt"
   5  	"strings"
   6  
   7  	"git.mleku.dev/mleku/dendrite/pkg/axiom"
   8  	"git.mleku.dev/mleku/dendrite/pkg/lattice"
   9  )
  10  
  11  // Seed injects The Art of War into a lattice. Creates strategy-constrained
  12  // sites and bonds strategy elements to them. Returns the number of elements
  13  // bonded.
  14  func Seed(l *lattice.Lattice) int {
  15  	// Sites per level per category.
  16  	sitesPerLevel := map[Level]int{
  17  		LevelChapter:   2,  // 13×2 = 26 coarse sites
  18  		LevelPrinciple: 4,  // 13×4 = 52 principle sites
  19  		LevelMaxim:     8,  // 13×8 = 104 maxim sites
  20  		LevelTactic:    6,  // 13×6 = 78 tactic sites
  21  	}
  22  	// Total: 260 strategy sites.
  23  
  24  	// Create strategy region: sites grouped by category.
  25  	type catNodes struct {
  26  		cat   Category
  27  		nodes []*lattice.Node
  28  	}
  29  	var regions []catNodes
  30  
  31  	for cat := CatAssessment; cat <= CatIntelligence; cat++ {
  32  		cn := catNodes{cat: cat}
  33  		for lvl := LevelChapter; lvl <= LevelTactic; lvl++ {
  34  			count := sitesPerLevel[lvl]
  35  			for range count {
  36  				n := l.AddNode([]axiom.Constraint{
  37  					StrategyConstraint{Cat: cat, MinLevel: lvl},
  38  				})
  39  				n.SetEnergy(true)
  40  				cn.nodes = append(cn.nodes, n)
  41  			}
  42  		}
  43  		regions = append(regions, cn)
  44  	}
  45  
  46  	// Ring topology within each category.
  47  	for _, cn := range regions {
  48  		for i := range cn.nodes {
  49  			l.Connect(cn.nodes[i], cn.nodes[(i+1)%len(cn.nodes)])
  50  		}
  51  	}
  52  
  53  	// Cross-category bridges: connect every 5th node between adjacent categories.
  54  	for i := 0; i < len(regions)-1; i++ {
  55  		a := regions[i].nodes
  56  		b := regions[i+1].nodes
  57  		step := max(1, min(len(a), len(b))/4)
  58  		for k := 0; k < min(len(a), len(b)); k += step {
  59  			l.Connect(a[k], b[k])
  60  		}
  61  	}
  62  	// Close the ring: connect last category to first.
  63  	if len(regions) > 1 {
  64  		a := regions[len(regions)-1].nodes
  65  		b := regions[0].nodes
  66  		step := max(1, min(len(a), len(b))/4)
  67  		for k := 0; k < min(len(a), len(b)); k += step {
  68  			l.Connect(a[k], b[k])
  69  		}
  70  	}
  71  
  72  	// Digest the embedded text and bond elements to strategy sites.
  73  	elements := DigestEmbedded()
  74  	bonded := 0
  75  	for elem := range elements {
  76  		// Try to bond at a matching site.
  77  		for _, cn := range regions {
  78  			found := false
  79  			for _, n := range cn.nodes {
  80  				if !n.Occupied() && n.Admits(elem) {
  81  					if n.Bond(elem) {
  82  						bonded++
  83  						found = true
  84  						break
  85  					}
  86  				}
  87  			}
  88  			if found {
  89  				break
  90  			}
  91  		}
  92  	}
  93  
  94  	totalSites := 0
  95  	for _, cn := range regions {
  96  		totalSites += len(cn.nodes)
  97  	}
  98  
  99  	fmt.Printf("strategy: %d elements bonded to %d sites (13 chapters)\n", bonded, totalSites)
 100  	return bonded
 101  }
 102  
 103  // SeedFromText injects custom strategic text (in the same format as
 104  // artofwar.txt) into a lattice.
 105  func SeedFromText(l *lattice.Lattice, text string) int {
 106  	elements := (Enzyme{}).Digest(strings.NewReader(text))
 107  	bonded := 0
 108  	for elem := range elements {
 109  		// Find any strategy-typed vacant node.
 110  		for _, n := range l.Nodes() {
 111  			if !n.Occupied() && n.Admits(elem) {
 112  				if n.Bond(elem) {
 113  					bonded++
 114  					break
 115  				}
 116  			}
 117  		}
 118  	}
 119  	return bonded
 120  }
 121