strategy.go raw
1 // Package strategy embeds Sun Tzu's Art of War as a structural layer in the
2 // lattice. Strategic principles become both sticky elements (positive space)
3 // and constraints (negative space), forming the system's permanent strategic
4 // skeleton.
5 //
6 // The 13 chapters map to 13 strategic categories. Each category decomposes
7 // into four levels: chapter, principle, maxim, and tactic.
8 package strategy
9
10 import (
11 _ "embed"
12 "strings"
13 )
14
15 //go:embed artofwar.txt
16 var artOfWarText string
17
18 // Category identifies one of the 13 strategic domains from The Art of War.
19 type Category uint8
20
21 const (
22 CatAssessment Category = iota // I: Laying Plans
23 CatResources // II: Waging War
24 CatStratagem // III: Attack by Stratagem
25 CatPositioning // IV: Tactical Dispositions
26 CatMomentum // V: Energy
27 CatExploitation // VI: Weak Points and Strong
28 CatAdaptation // VII: Maneuvering
29 CatFlexibility // VIII: Variation of Tactics
30 CatSignalReading // IX: The Army on the March
31 CatEnvironment // X: Terrain
32 CatUrgency // XI: The Nine Situations
33 CatForce // XII: The Attack by Fire
34 CatIntelligence // XIII: The Use of Spies
35 )
36
37 var categoryNames = [13]string{
38 "assessment", "resources", "stratagem", "positioning",
39 "momentum", "exploitation", "adaptation", "flexibility",
40 "signal-reading", "environment", "urgency", "force", "intelligence",
41 }
42
43 func (c Category) String() string {
44 if int(c) < len(categoryNames) {
45 return categoryNames[c]
46 }
47 return "unknown"
48 }
49
50 // Level distinguishes element granularity within a chapter.
51 type Level uint8
52
53 const (
54 LevelChapter Level = iota // Whole chapter summary
55 LevelPrinciple // Core principle (paragraph)
56 LevelMaxim // Single maxim (sentence)
57 LevelTactic // Tactical fragment (phrase)
58 )
59
60 var levelNames = [4]string{"chapter", "principle", "maxim", "tactic"}
61
62 func (l Level) String() string {
63 if int(l) < len(levelNames) {
64 return levelNames[l]
65 }
66 return "unknown"
67 }
68
69 // Maxim is a single strategic principle extracted from a chapter.
70 type Maxim struct {
71 Text string
72 Chapter int
73 Tags []string
74 }
75
76 // Chapter holds one of the 13 chapters of The Art of War.
77 type Chapter struct {
78 Number int
79 Title string
80 Category Category
81 Text string // Full text of the chapter
82 Maxims []Maxim // Extracted key principles
83 }
84
85 var chapters [13]Chapter
86
87 // chapterDefs maps chapter markers to their metadata.
88 var chapterDefs = [13]struct {
89 marker string
90 title string
91 category Category
92 }{
93 {"## I.", "Laying Plans", CatAssessment},
94 {"## II.", "Waging War", CatResources},
95 {"## III.", "Attack by Stratagem", CatStratagem},
96 {"## IV.", "Tactical Dispositions", CatPositioning},
97 {"## V.", "Energy", CatMomentum},
98 {"## VI.", "Weak Points and Strong", CatExploitation},
99 {"## VII.", "Maneuvering", CatAdaptation},
100 {"## VIII.", "Variation of Tactics", CatFlexibility},
101 {"## IX.", "The Army on the March", CatSignalReading},
102 {"## X.", "Terrain", CatEnvironment},
103 {"## XI.", "The Nine Situations", CatUrgency},
104 {"## XII.", "The Attack by Fire", CatForce},
105 {"## XIII.", "The Use of Spies", CatIntelligence},
106 }
107
108 func init() {
109 parseChapters()
110 }
111
112 // parseChapters splits the embedded text into 13 chapters and extracts maxims.
113 func parseChapters() {
114 lines := strings.Split(artOfWarText, "\n")
115
116 // Find chapter boundaries.
117 type boundary struct {
118 line int
119 idx int // index into chapterDefs
120 }
121 var bounds []boundary
122 for i, line := range lines {
123 for j, def := range chapterDefs {
124 if strings.HasPrefix(line, def.marker) {
125 bounds = append(bounds, boundary{i, j})
126 }
127 }
128 }
129
130 for bi, b := range bounds {
131 end := len(lines)
132 if bi+1 < len(bounds) {
133 end = bounds[bi+1].line
134 }
135
136 // Chapter text is everything after the heading line.
137 var textLines []string
138 for _, l := range lines[b.line+1 : end] {
139 textLines = append(textLines, l)
140 }
141 text := strings.TrimSpace(strings.Join(textLines, "\n"))
142
143 def := chapterDefs[b.idx]
144 chapters[b.idx] = Chapter{
145 Number: b.idx + 1,
146 Title: def.title,
147 Category: def.category,
148 Text: text,
149 Maxims: extractMaxims(text, b.idx+1),
150 }
151 }
152 }
153
154 // extractMaxims identifies imperative and declarative maxims from chapter text.
155 func extractMaxims(text string, chapter int) []Maxim {
156 var maxims []Maxim
157
158 // Split into sentences.
159 sentences := splitSentences(text)
160 for _, s := range sentences {
161 s = strings.TrimSpace(s)
162 if len(s) < 20 {
163 continue
164 }
165 if isMaxim(s) {
166 maxims = append(maxims, Maxim{
167 Text: s,
168 Chapter: chapter,
169 Tags: maximTags(s),
170 })
171 }
172 }
173 return maxims
174 }
175
176 // splitSentences breaks text into sentences on period, exclamation, or
177 // question mark boundaries that are followed by whitespace or end of text.
178 func splitSentences(text string) []string {
179 var sentences []string
180 var current strings.Builder
181
182 runes := []rune(text)
183 for i, r := range runes {
184 current.WriteRune(r)
185 if (r == '.' || r == '!' || r == '?') && (i+1 >= len(runes) || runes[i+1] == ' ' || runes[i+1] == '\n') {
186 s := strings.TrimSpace(current.String())
187 if s != "" {
188 sentences = append(sentences, s)
189 }
190 current.Reset()
191 }
192 }
193 if s := strings.TrimSpace(current.String()); s != "" {
194 sentences = append(sentences, s)
195 }
196 return sentences
197 }
198
199 // isMaxim returns true if a sentence looks like a strategic principle.
200 func isMaxim(s string) bool {
201 lower := strings.ToLower(s)
202
203 // Imperative indicators.
204 imperatives := []string{
205 "must", "should", "therefore", "hence", "thus",
206 "he who", "the general", "the skilful", "the skillful",
207 "if you", "if your", "if he", "when you", "when the",
208 "do not", "never", "always", "let ",
209 "all warfare", "the art of war", "supreme excellence",
210 "in war", "in battle",
211 }
212 for _, kw := range imperatives {
213 if strings.Contains(lower, kw) {
214 return true
215 }
216 }
217
218 // Short declarative sentences are often maxims.
219 if len(s) < 120 && !strings.Contains(s, "Sun Tz") {
220 words := strings.Fields(s)
221 if len(words) >= 4 && len(words) <= 25 {
222 return true
223 }
224 }
225
226 return false
227 }
228
229 // maximTags returns lattice-relevant tags for a maxim.
230 func maximTags(s string) []string {
231 lower := strings.ToLower(s)
232 var tags []string
233
234 tagKeywords := map[string][]string{
235 "deception": {"decep", "feign", "pretend", "appear", "seem"},
236 "speed": {"swift", "rapid", "quick", "haste", "speed"},
237 "patience": {"wait", "patient", "timing", "moment"},
238 "terrain": {"ground", "terrain", "position", "height"},
239 "intelligence": {"spy", "spies", "knowledge", "foreknowledge", "know"},
240 "resources": {"supply", "provision", "cost", "expenditure"},
241 "morale": {"spirit", "morale", "courage", "heart"},
242 "discipline": {"discipline", "order", "command", "obey"},
243 "adaptation": {"adapt", "change", "vary", "flexible", "modify"},
244 "force": {"attack", "strike", "fight", "battle", "force"},
245 }
246
247 for tag, keywords := range tagKeywords {
248 for _, kw := range keywords {
249 if strings.Contains(lower, kw) {
250 tags = append(tags, tag)
251 break
252 }
253 }
254 }
255 return tags
256 }
257
258 // Chapters returns all 13 chapters.
259 func Chapters() [13]Chapter {
260 return chapters
261 }
262
263 // ChapterByNumber returns a chapter (1-13) or empty if out of range.
264 func ChapterByNumber(n int) Chapter {
265 if n < 1 || n > 13 {
266 return Chapter{}
267 }
268 return chapters[n-1]
269 }
270