@rsconcept/rstool 0.7.2 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +1 -1
  2. package/docs/CONSTITUENTA.md +1 -1
  3. package/docs/DIAGNOSTICS.md +8 -5
  4. package/docs/MODEL-TESTING.md +1 -1
  5. package/docs/PORTAL-API.md +20 -0
  6. package/examples/README.md +52 -29
  7. package/examples/build-chocolate-nim-rsform.ts +311 -0
  8. package/examples/chocolate-nim/build-rsform.ts +311 -0
  9. package/examples/chocolate-nim/build-rsmodel.ts +100 -0
  10. package/examples/chocolate-nim/constants.ts +10 -0
  11. package/examples/chocolate-nim/rsform-session.json +778 -0
  12. package/examples/chocolate-nim/rsmodel-session.json +802 -0
  13. package/examples/expression-bank/bank-constituents.ts +370 -0
  14. package/examples/expression-bank/build-rsform.ts +59 -0
  15. package/examples/expression-bank/constants.ts +1 -0
  16. package/examples/expression-bank/rsform-session.json +4006 -0
  17. package/examples/{build-kinship-rsform.ts → kinship/build-rsform.ts} +276 -54
  18. package/examples/{build-kinship-rsmodel.ts → kinship/build-rsmodel.ts} +18 -7
  19. package/examples/kinship/constants.ts +5 -3
  20. package/examples/{kinship-rsmodel-session.json → kinship/rsform-session.json} +614 -92
  21. package/examples/{kinship-rsform-session.json → kinship/rsmodel-session.json} +704 -50
  22. package/examples/kinship/x1-actions.test.ts +2 -1
  23. package/examples/movd/build-rsform.ts +325 -0
  24. package/examples/movd/build-rsmodel.ts +159 -0
  25. package/examples/movd/constants.ts +18 -0
  26. package/examples/movd/rsform-session.json +738 -0
  27. package/examples/movd/rsmodel-session.json +832 -0
  28. package/examples/{build-sample-rsform.ts → sample/build-rsform.ts} +2 -2
  29. package/examples/{build-sample-rsmodel.ts → sample/build-rsmodel.ts} +3 -4
  30. package/examples/template-apply/build-rsform.ts +236 -0
  31. package/examples/template-apply/constants.ts +13 -0
  32. package/examples/template-apply/rsform-session.json +333 -0
  33. package/package.json +12 -6
  34. package/skills/rstool-helper/EXAMPLES.md +2 -2
  35. /package/examples/{sample-rsform-session.json → sample/rsform-session.json} +0 -0
  36. /package/examples/{sample-rsmodel-session.json → sample/rsmodel-session.json} +0 -0
@@ -0,0 +1,311 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+
4
+ import { CstType, RSToolWrapperClient, type AddOrUpdateConstituentaInput } from '../../src';
5
+
6
+ import { DEFAULT_RSFORM_SESSION_PATH } from './constants';
7
+
8
+ /**
9
+ * Учебная RSForm «шоколадный Ним» (ОШК-НИМ) по Portal rsforms/825.
10
+ * Целочисленное представление без базисного множества: размеры и позиции — кортежи Z×Z.
11
+ */
12
+ async function run() {
13
+ const client = new RSToolWrapperClient({
14
+ cwd: resolve(process.cwd())
15
+ });
16
+
17
+ try {
18
+ await client.waitUntilReady();
19
+ const session = await client.call<{ sessionId: string; contractVersion: string }>('createSession');
20
+
21
+ const drafts: AddOrUpdateConstituentaInput[] = [
22
+ {
23
+ draft: {
24
+ id: 1,
25
+ alias: 'S1',
26
+ cstType: CstType.STRUCTURED,
27
+ definitionFormal: 'Z×Z',
28
+ term: 'шоколадка',
29
+ convention: 'Первая проекция — ширина, вторая — длина'
30
+ }
31
+ },
32
+ {
33
+ draft: {
34
+ id: 2,
35
+ alias: 'D1',
36
+ cstType: CstType.TERM,
37
+ definitionFormal: 'pr1(S1)',
38
+ term: 'ширина шоколадки'
39
+ }
40
+ },
41
+ {
42
+ draft: {
43
+ id: 3,
44
+ alias: 'D2',
45
+ cstType: CstType.TERM,
46
+ definitionFormal: 'pr2(S1)',
47
+ term: 'длина шоколадки'
48
+ }
49
+ },
50
+ {
51
+ draft: {
52
+ id: 4,
53
+ alias: 'S2',
54
+ cstType: CstType.STRUCTURED,
55
+ definitionFormal: 'Z×Z',
56
+ term: 'отравленная долька',
57
+ convention: 'Координаты отравленной дольки: первая проекция по ширине, вторая — по длине'
58
+ }
59
+ },
60
+ {
61
+ draft: {
62
+ id: 5,
63
+ alias: 'D3',
64
+ cstType: CstType.TERM,
65
+ definitionFormal: 'pr1(S2)',
66
+ term: 'расположение отравленной дольки по ширине'
67
+ }
68
+ },
69
+ {
70
+ draft: {
71
+ id: 6,
72
+ alias: 'D4',
73
+ cstType: CstType.TERM,
74
+ definitionFormal: 'pr2(S2)',
75
+ term: 'расположение отравленной дольки по длине'
76
+ }
77
+ },
78
+ {
79
+ draft: {
80
+ id: 7,
81
+ alias: 'A1',
82
+ cstType: CstType.AXIOM,
83
+ definitionFormal: '(1≤D3)&(D3≤D1)&(1≤D4)&(D4≤D2)',
84
+ term: 'отравленная долька в шоколадке',
85
+ definitionText: 'Координаты отравленной дольки лежат в пределах шоколадки'
86
+ }
87
+ },
88
+ {
89
+ draft: {
90
+ id: 8,
91
+ alias: 'P1',
92
+ cstType: CstType.PREDICATE,
93
+ definitionFormal: '[α∈Z] R{ξ:=α | ξ≥2 | ξ-2}=0',
94
+ term: 'чётное?',
95
+ convention: 'Для малых аргументов — прямое вычитание двойки; для больших нужно двоичное представление'
96
+ }
97
+ },
98
+ {
99
+ draft: {
100
+ id: 9,
101
+ alias: 'F1',
102
+ cstType: CstType.FUNCTION,
103
+ definitionFormal: '[α∈Z, σ∈ℬ(Z×R1)] debool(Pr2(Fi1[{α}](σ)))',
104
+ term: 'значение элемента последовательности с данным номером',
105
+ definitionText: 'Значение пары с заданным номером в последовательности пар'
106
+ }
107
+ },
108
+ {
109
+ draft: {
110
+ id: 10,
111
+ alias: 'F2',
112
+ cstType: CstType.FUNCTION,
113
+ definitionFormal: '[σ∈ℬ(Z)] debool(D{ξ∈σ | ∀α∈σ α≤ξ})',
114
+ term: 'максимум набора чисел'
115
+ }
116
+ },
117
+ {
118
+ draft: {
119
+ id: 11,
120
+ alias: 'F4',
121
+ cstType: CstType.FUNCTION,
122
+ definitionFormal: '[σ∈ℬ(Z)] D{ξ∈σ | ∀α∈σ α≤ξ}',
123
+ term: 'верхние границы набора чисел',
124
+ definitionText: 'Множество элементов набора, не меньших всех остальных'
125
+ }
126
+ },
127
+ {
128
+ draft: {
129
+ id: 12,
130
+ alias: 'F5',
131
+ cstType: CstType.FUNCTION,
132
+ definitionFormal: '[α∈Z, β∈Z] debool(I{(α,0) | α<β} ∪ I{(α-β,1) | α≥β})',
133
+ term: 'деление с остатком на степень двойки',
134
+ convention: 'Предполагается, что удвоенный делитель больше делимого'
135
+ }
136
+ },
137
+ {
138
+ draft: {
139
+ id: 13,
140
+ alias: 'F6',
141
+ cstType: CstType.FUNCTION,
142
+ definitionFormal: '[α∈Z, β∈Z] pr1(F5[α, β])',
143
+ term: 'остаток'
144
+ }
145
+ },
146
+ {
147
+ draft: {
148
+ id: 14,
149
+ alias: 'F7',
150
+ cstType: CstType.FUNCTION,
151
+ definitionFormal: '[α∈Z, β∈Z] pr2(F5[α, β])',
152
+ term: 'целая часть'
153
+ }
154
+ },
155
+ {
156
+ draft: {
157
+ id: 15,
158
+ alias: 'D5',
159
+ cstType: CstType.TERM,
160
+ definitionFormal: '{(0, D3-1), (1, D4-1), (2, D1-D3), (3, D2-D4)}',
161
+ term: 'шоколадка как кучки Ним',
162
+ definitionText: 'Четыре кучки камней после разреза по отравленной дольке: слева, сверху, справа и снизу'
163
+ }
164
+ },
165
+ {
166
+ draft: {
167
+ id: 16,
168
+ alias: 'D6',
169
+ cstType: CstType.TERM,
170
+ definitionFormal: 'card(D5)',
171
+ term: 'количество кучек'
172
+ }
173
+ },
174
+ {
175
+ draft: {
176
+ id: 17,
177
+ alias: 'D7',
178
+ cstType: CstType.TERM,
179
+ definitionFormal: 'Pr1(D5)',
180
+ term: 'номера кучек'
181
+ }
182
+ },
183
+ {
184
+ draft: {
185
+ id: 18,
186
+ alias: 'D8',
187
+ cstType: CstType.TERM,
188
+ definitionFormal: 'Pr2(D5)',
189
+ term: 'размеры кучек'
190
+ }
191
+ },
192
+ {
193
+ draft: {
194
+ id: 19,
195
+ alias: 'A2',
196
+ cstType: CstType.AXIOM,
197
+ definitionFormal: 'card(D5)=card(D7)',
198
+ term: 'однозначность количества камней в кучках',
199
+ definitionText: 'У каждой кучки ровно один номер'
200
+ }
201
+ },
202
+ {
203
+ draft: {
204
+ id: 20,
205
+ alias: 'A3',
206
+ cstType: CstType.AXIOM,
207
+ definitionFormal: '∀α∈D5 (pr1(α)<D6 & pr1(α)≥0)',
208
+ term: 'последовательная нумерация кучек',
209
+ definitionText: 'Номера кучек — целые от нуля до количества кучек минус один'
210
+ }
211
+ },
212
+ {
213
+ draft: {
214
+ id: 21,
215
+ alias: 'F8',
216
+ cstType: CstType.FUNCTION,
217
+ definitionFormal: '[α∈Z] F1[α,D5]',
218
+ term: 'количество камней в данной кучке'
219
+ }
220
+ },
221
+ {
222
+ draft: {
223
+ id: 22,
224
+ alias: 'D9',
225
+ cstType: CstType.TERM,
226
+ definitionFormal: 'Pr1(Fi2[{0}](D5))',
227
+ term: 'пустые кучки',
228
+ definitionText: 'Номера кучек с нулевым размером'
229
+ }
230
+ },
231
+ {
232
+ draft: {
233
+ id: 23,
234
+ alias: 'D10',
235
+ cstType: CstType.TERM,
236
+ definitionFormal: 'D7\\D9',
237
+ term: 'непустые кучки',
238
+ definitionText: 'Номера кучек с положительным размером'
239
+ }
240
+ },
241
+ {
242
+ draft: {
243
+ id: 24,
244
+ alias: 'T1',
245
+ cstType: CstType.STATEMENT,
246
+ definitionFormal: 'D8={0}',
247
+ term: 'игра закончена',
248
+ definitionText: 'Во всех кучках не осталось камней'
249
+ }
250
+ },
251
+ {
252
+ draft: {
253
+ id: 25,
254
+ alias: 'T2',
255
+ cstType: CstType.STATEMENT,
256
+ definitionFormal: 'card(D10)=1',
257
+ term: 'существует выигрышный ход',
258
+ definitionText: 'Осталась ровно одна непустая кучка'
259
+ }
260
+ },
261
+ {
262
+ draft: {
263
+ id: 26,
264
+ alias: 'F10',
265
+ cstType: CstType.FUNCTION,
266
+ definitionFormal: '[σ∈D7×Z] debool(I{ pr2(σ)*D2 | P1[pr1(σ)]} ∪ I{ pr2(σ)*D1 | ¬P1[pr1(σ)]})',
267
+ term: 'оценка хода Ним',
268
+ definitionText:
269
+ 'Стоимость хода в дольках шоколадки: для чётного номера кучки — размер, умноженный на длину, иначе — на ширину'
270
+ }
271
+ }
272
+ ];
273
+
274
+ for (const input of drafts) {
275
+ const result = await client.call<{
276
+ state: { alias: string; analysis: { success: boolean } };
277
+ diagnostics: unknown[];
278
+ }>('addOrUpdateConstituenta', {
279
+ sessionId: session.sessionId,
280
+ input
281
+ });
282
+ const ok = result.state.analysis.success;
283
+ const diagCount = result.diagnostics?.length ?? 0;
284
+ console.log(`${input.draft.alias}: ${ok ? 'OK' : 'FAIL'} (${diagCount} diagnostics)`);
285
+ if (!ok) {
286
+ const diags = await client.call('listDiagnostics', { sessionId: session.sessionId });
287
+ console.log(JSON.stringify(diags, null, 2));
288
+ throw new Error(`${input.draft.alias}: analysis failed (${diagCount} diagnostics)`);
289
+ }
290
+ }
291
+
292
+ await client.call('commitStep', {
293
+ sessionId: session.sessionId,
294
+ message: 'КС «шоколадный Ним» (rsforms/825): Z×Z, pr, P#, T#, арифметика, радикал R1'
295
+ });
296
+
297
+ const exported = await client.call<string>('exportSession', {
298
+ sessionId: session.sessionId
299
+ });
300
+ const outputPath = resolve(process.cwd(), DEFAULT_RSFORM_SESSION_PATH);
301
+ await writeFile(outputPath, exported, 'utf8');
302
+ console.log(`Exported: ${outputPath}`);
303
+ } finally {
304
+ await client.close();
305
+ }
306
+ }
307
+
308
+ run().catch(error => {
309
+ console.error(error);
310
+ process.exit(1);
311
+ });
@@ -0,0 +1,100 @@
1
+ import { readFile, writeFile } from 'node:fs/promises';
2
+ import { resolve } from 'node:path';
3
+
4
+ import { TUPLE_ID } from '@rsconcept/domain';
5
+
6
+ import { EvalStatus, RSToolWrapperClient } from '../../src';
7
+
8
+ import {
9
+ A1_ID,
10
+ D6_ID,
11
+ DEFAULT_RSFORM_SESSION_PATH,
12
+ DEFAULT_RSMODEL_SESSION_PATH,
13
+ S1_ID,
14
+ S2_ID,
15
+ T1_ID
16
+ } from './constants';
17
+
18
+ /**
19
+ * Демо-позиция: шоколадка 4×6, отравленная долька в (2, 3).
20
+ * Кучки Ним: 1, 2, 2, 3 камня; игра не окончена.
21
+ */
22
+ const S1_VALUE = [TUPLE_ID, 4, 6] as const;
23
+ const S2_VALUE = [TUPLE_ID, 2, 3] as const;
24
+
25
+ async function run() {
26
+ const client = new RSToolWrapperClient({
27
+ cwd: resolve(process.cwd())
28
+ });
29
+
30
+ try {
31
+ await client.waitUntilReady();
32
+ const schemaPath = resolve(process.cwd(), DEFAULT_RSFORM_SESSION_PATH);
33
+ const schemaJson = await readFile(schemaPath, 'utf8');
34
+
35
+ const imported = await client.call<{ sessionId: string }>('importSession', {
36
+ payload: schemaJson
37
+ });
38
+
39
+ await client.call('setConstituentaValues', {
40
+ sessionId: imported.sessionId,
41
+ input: {
42
+ items: [
43
+ { target: S1_ID, value: S1_VALUE },
44
+ { target: S2_ID, value: S2_VALUE }
45
+ ]
46
+ }
47
+ });
48
+
49
+ const recalculated = await client.call<{
50
+ items: { id: number; alias: string; value: unknown; status: number }[];
51
+ }>('recalculateModel', {
52
+ sessionId: imported.sessionId
53
+ });
54
+
55
+ const d6 = recalculated.items.find(item => item.id === D6_ID);
56
+ const a1Eval = await client.call<{ success: boolean; value: unknown; status: number }>('evaluateConstituenta', {
57
+ sessionId: imported.sessionId,
58
+ input: { constituentId: A1_ID }
59
+ });
60
+ const t1Eval = await client.call<{ success: boolean; value: unknown; status: number }>('evaluateConstituenta', {
61
+ sessionId: imported.sessionId,
62
+ input: { constituentId: T1_ID }
63
+ });
64
+
65
+ console.log('D6 recalculate:', d6);
66
+ console.log('A1 evaluate:', a1Eval);
67
+ console.log('T1 evaluate:', t1Eval);
68
+
69
+ if (!a1Eval.success || a1Eval.status === EvalStatus.AXIOM_FALSE || a1Eval.value !== 1) {
70
+ throw new Error(`Expected A1 to hold; got ${JSON.stringify(a1Eval)}`);
71
+ }
72
+
73
+ if (!t1Eval.success || t1Eval.value !== 0) {
74
+ throw new Error(`Expected T1 (game not over) to be false; got ${JSON.stringify(t1Eval)}`);
75
+ }
76
+
77
+ if (d6?.value !== 4) {
78
+ throw new Error(`Expected D6=4 piles; got ${JSON.stringify(d6)}`);
79
+ }
80
+
81
+ await client.call('commitStep', {
82
+ sessionId: imported.sessionId,
83
+ message: 'Модель шоколадного Нима: 4×6, долька (2,3), четыре кучки, A1 выполняется'
84
+ });
85
+
86
+ const exported = await client.call<string>('exportSession', {
87
+ sessionId: imported.sessionId
88
+ });
89
+ const outputPath = resolve(process.cwd(), DEFAULT_RSMODEL_SESSION_PATH);
90
+ await writeFile(outputPath, exported, 'utf8');
91
+ console.log(`Exported: ${outputPath}`);
92
+ } finally {
93
+ await client.close();
94
+ }
95
+ }
96
+
97
+ run().catch(error => {
98
+ console.error(error);
99
+ process.exit(1);
100
+ });
@@ -0,0 +1,10 @@
1
+ /** Constituent ids in chocolate-nim RSForm / RSModel sessions (based on Portal rsforms/825). */
2
+ export const S1_ID = 1;
3
+ export const S2_ID = 4;
4
+ export const D5_ID = 15;
5
+ export const D6_ID = 16;
6
+ export const A1_ID = 7;
7
+ export const T1_ID = 24;
8
+
9
+ export const DEFAULT_RSFORM_SESSION_PATH = 'examples/chocolate-nim/rsform-session.json';
10
+ export const DEFAULT_RSMODEL_SESSION_PATH = 'examples/chocolate-nim/rsmodel-session.json';