@game_ryo/lsji 0.1.0

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 (61) hide show
  1. package/.github/workflows/node.yml +46 -0
  2. package/AGENTS.md +143 -0
  3. package/LICENSE +185 -0
  4. package/PROPOSAL.md +18 -0
  5. package/README.md +102 -0
  6. package/bin/lsji.js +8 -0
  7. package/docs/README.md +43 -0
  8. package/docs/blog/2019-05-28-first-blog-post.mdx +12 -0
  9. package/docs/blog/2019-05-29-long-blog-post.mdx +44 -0
  10. package/docs/blog/2021-08-01-mdx-blog-post.mdx +24 -0
  11. package/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
  12. package/docs/blog/2021-08-26-welcome/index.mdx +29 -0
  13. package/docs/blog/authors.yml +25 -0
  14. package/docs/blog/tags.yml +19 -0
  15. package/docs/docs/api/agent.md +151 -0
  16. package/docs/docs/api/env.md +133 -0
  17. package/docs/docs/api/environments.md +102 -0
  18. package/docs/docs/api/qlearning.md +138 -0
  19. package/docs/docs/api/storage.md +168 -0
  20. package/docs/docs/architecture.md +155 -0
  21. package/docs/docs/cli.md +210 -0
  22. package/docs/docs/contributing.md +162 -0
  23. package/docs/docs/core-concepts.md +152 -0
  24. package/docs/docs/examples/advanced-training.md +244 -0
  25. package/docs/docs/examples/custom-environment.md +198 -0
  26. package/docs/docs/examples/custom-storage.md +251 -0
  27. package/docs/docs/getting-started.md +91 -0
  28. package/docs/docusaurus.config.ts +149 -0
  29. package/docs/package-lock.json +19522 -0
  30. package/docs/package.json +49 -0
  31. package/docs/sidebars.ts +33 -0
  32. package/docs/src/components/HomepageFeatures/index.tsx +71 -0
  33. package/docs/src/components/HomepageFeatures/styles.module.css +11 -0
  34. package/docs/src/css/custom.css +79 -0
  35. package/docs/src/pages/index.module.css +23 -0
  36. package/docs/src/pages/index.tsx +44 -0
  37. package/docs/src/pages/markdown-page.mdx +7 -0
  38. package/docs/static/.nojekyll +0 -0
  39. package/docs/static/img/docusaurus-social-card.jpg +0 -0
  40. package/docs/static/img/docusaurus.png +0 -0
  41. package/docs/static/img/favicon.ico +0 -0
  42. package/docs/static/img/logo.png +0 -0
  43. package/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
  44. package/docs/static/img/undraw_docusaurus_react.svg +170 -0
  45. package/docs/static/img/undraw_docusaurus_tree.svg +40 -0
  46. package/docs/tsconfig.json +12 -0
  47. package/legacy/worker.js +166 -0
  48. package/legacy/wrangler.toml +11 -0
  49. package/package.json +26 -0
  50. package/src/cli.js +232 -0
  51. package/src/core/agent.js +239 -0
  52. package/src/core/env.js +86 -0
  53. package/src/core/qlearning.js +197 -0
  54. package/src/envs/rps.js +168 -0
  55. package/src/index.js +22 -0
  56. package/src/storage/better-sqlite.js +133 -0
  57. package/src/storage/index.js +146 -0
  58. package/src/storage/memory.js +98 -0
  59. package/src/storage/sqlite.js +123 -0
  60. package/test/core/qlearning.test.js +150 -0
  61. package/test/storage/memory.test.js +81 -0
@@ -0,0 +1,168 @@
1
+ ---
2
+ title: Storage API
3
+ description: Pluggable storage backends
4
+ ---
5
+
6
+ # Storage API
7
+
8
+ LSJI provides a pluggable storage abstraction with three built-in implementations.
9
+
10
+ ## Import
11
+
12
+ ```typescript
13
+ import { createStorage, Storage, SqliteStorage, BetterSqliteStorage, MemoryStorage } from 'lsji';
14
+ ```
15
+
16
+ ## Factory Function
17
+
18
+ ```typescript
19
+ const storage = await createStorage(type, options);
20
+ ```
21
+
22
+ **Types:**
23
+ - `'sqlite'` — Node.js built-in `node:sqlite` (recommended)
24
+ - `'better-sqlite'` — `better-sqlite3` package
25
+ - `'memory'` — In-memory (testing only)
26
+
27
+ **Options:**
28
+ ```typescript
29
+ // SQLite
30
+ { path: './my-agent.db' }
31
+
32
+ // Better-SQLite
33
+ { path: './my-agent.db' }
34
+
35
+ // Memory
36
+ {} // No options needed
37
+ ```
38
+
39
+ ## Storage Interface
40
+
41
+ All backends implement this interface:
42
+
43
+ ```typescript
44
+ interface Storage {
45
+ initialize(): Promise<void>;
46
+ close(): Promise<void>;
47
+ getSetting(key: string): Promise<{key: string, value: string} | null>;
48
+ setSetting(key: string, value: string | number): Promise<void>;
49
+ getQTable(): Promise<Array<{state: string, action: number, q_value: number}>>;
50
+ updateQ(state: string, action: number, qValue: number): Promise<void>;
51
+ addBattle(record: BattleRecord): Promise<void>;
52
+ getTodayBattleCount(): Promise<number>;
53
+ getPerformanceStats(): Promise<Array<{mode: string, total: number, win_rate: number}>>;
54
+ }
55
+
56
+ interface BattleRecord {
57
+ mode: 'train' | 'test';
58
+ handA: number;
59
+ handB: number;
60
+ reward: number;
61
+ createdAt: string; // ISO timestamp
62
+ }
63
+ ```
64
+
65
+ ## Backends
66
+
67
+ ### SqliteStorage (Recommended)
68
+
69
+ Uses Node.js 22+ built-in `node:sqlite` — **zero dependencies**.
70
+
71
+ ```typescript
72
+ import { SqliteStorage } from 'lsji';
73
+
74
+ const storage = new SqliteStorage('./agent.db');
75
+ await storage.initialize();
76
+ // ... use storage
77
+ await storage.close();
78
+ ```
79
+
80
+ **Features:**
81
+ - Synchronous API (fast)
82
+ - Automatic schema creation
83
+ - Indexes on battle_history for performance
84
+ - Persistent across process restarts
85
+
86
+ ### BetterSqliteStorage
87
+
88
+ Uses `better-sqlite3` for high-performance synchronous access.
89
+
90
+ ```typescript
91
+ import { BetterSqliteStorage } from 'lsji';
92
+
93
+ const storage = new BetterSqliteStorage('./agent.db');
94
+ await storage.initialize();
95
+ ```
96
+
97
+ **Requires:** `npm install better-sqlite3`
98
+
99
+ **Use when:** You need faster writes or advanced SQLite features.
100
+
101
+ ### MemoryStorage
102
+
103
+ Pure in-memory implementation.
104
+
105
+ ```typescript
106
+ import { MemoryStorage } from 'lsji';
107
+
108
+ const storage = new MemoryStorage();
109
+ await storage.initialize();
110
+ ```
111
+
112
+ **Features:**
113
+ - No persistence (data lost on exit)
114
+ - Fastest for testing/CI
115
+ - Implements `clear()` for test isolation
116
+
117
+ ## Database Schema
118
+
119
+ All SQL backends create these tables:
120
+
121
+ ```sql
122
+ -- Settings (key-value)
123
+ CREATE TABLE settings (
124
+ key TEXT PRIMARY KEY,
125
+ value TEXT NOT NULL
126
+ );
127
+
128
+ -- Battle history
129
+ CREATE TABLE battle_history (
130
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
131
+ mode TEXT NOT NULL CHECK (mode IN ('train', 'test')),
132
+ hand_a INTEGER NOT NULL,
133
+ hand_b INTEGER NOT NULL,
134
+ reward INTEGER NOT NULL,
135
+ created_at TEXT NOT NULL
136
+ );
137
+
138
+ -- Q-table
139
+ CREATE TABLE q_table (
140
+ state TEXT NOT NULL,
141
+ action INTEGER NOT NULL,
142
+ q_value REAL NOT NULL DEFAULT 0,
143
+ PRIMARY KEY (state, action)
144
+ );
145
+
146
+ -- Indexes
147
+ CREATE INDEX idx_battle_history_date ON battle_history(created_at);
148
+ CREATE INDEX idx_battle_history_mode ON battle_history(mode);
149
+ ```
150
+
151
+ ## Example
152
+
153
+ ```typescript
154
+ import { createStorage, QLearning, Agent, RockPaperScissorsEnv } from 'lsji';
155
+
156
+ // Production: persistent SQLite
157
+ const storage = await createStorage('sqlite', { path: './production.db' });
158
+
159
+ // Testing: in-memory
160
+ const testStorage = await createStorage('memory');
161
+
162
+ const qlearning = new QLearning({ alpha: 0.1, gamma: 0.9, epsilon: 0.1, storage });
163
+ const env = new RockPaperScissorsEnv({ opponent: 'random' });
164
+ const agent = new Agent({ qlearning, storage, env });
165
+
166
+ await agent.train({ episodes: 1000 });
167
+ await storage.close();
168
+ ```
@@ -0,0 +1,155 @@
1
+ ---
2
+ title: Architecture
3
+ description: Design decisions and internals
4
+ ---
5
+
6
+ # Architecture
7
+
8
+ ## Design Principles
9
+
10
+ 1. **Single Package** — All core functionality in one npm package
11
+ 2. **Zero Dependencies** — Core runs on Node.js 22+ built-ins only
12
+ 3. **Storage Abstraction** — Easy to swap backends
13
+ 4. **ESM Only** — Modern JavaScript modules
14
+ 5. **Apache 2.0** — License compatible with Apache Incubator
15
+
16
+ ## Project Structure
17
+
18
+ ```
19
+ src/
20
+ ├── core/
21
+ │ ├── env.ts # Environment interface
22
+ │ ├── qlearning.ts # Q-Learning engine
23
+ │ └── agent.ts # Agent orchestration
24
+ ├── storage/
25
+ │ ├── index.ts # Storage interface + factory
26
+ │ ├── sqlite.ts # node:sqlite implementation
27
+ │ ├── better-sqlite.ts # better-sqlite3 implementation
28
+ │ └── memory.ts # In-memory implementation
29
+ ├── envs/
30
+ │ └── rps.ts # Rock-Paper-Scissors environment
31
+ ├── cli.ts # Command-line interface
32
+ └── index.ts # Public API exports
33
+ ```
34
+
35
+ ## Core Components
36
+
37
+ ### Q-Learning Engine
38
+
39
+ **Algorithm:** Tabular Q-Learning with Temporal Difference updates
40
+
41
+ **Update Rules:**
42
+ - Full TD: `Q(s,a) ← Q(s,a) + α[r + γ·maxₐ' Q(s',a') - Q(s,a)]`
43
+ - Simple: `Q(s,a) ← Q(s,a) + α[r - Q(s,a)]` (terminal states)
44
+
45
+ **Exploration:** Epsilon-greedy with configurable ε
46
+
47
+ **State Representation:** String keys for Q-table indexing
48
+
49
+ ### Storage Layer
50
+
51
+ **Interface:** Abstract `Storage` class with 9 required methods
52
+
53
+ **Implementations:**
54
+ 1. `SqliteStorage` — Node.js built-in `node:sqlite` (sync API)
55
+ 2. `BetterSqliteStorage` — `better-sqlite3` (faster, more features)
56
+ 3. `MemoryStorage` — Pure JS Maps/Arrays (testing)
57
+
58
+ **Schema:**
59
+ - `settings` — Key-value configuration
60
+ - `battle_history` — Training/play records with timestamps
61
+ - `q_table` — State-action values with composite primary key
62
+
63
+ **Indexes:** Date and mode indexes on battle_history for fast queries
64
+
65
+ ### Agent Orchestration
66
+
67
+ **Responsibilities:**
68
+ - System lifecycle (start/stop)
69
+ - Training loop with batching
70
+ - Play/evaluation loop
71
+ - Statistics aggregation
72
+
73
+ **Training Loop:**
74
+ ```
75
+ for episode in episodes:
76
+ state = env.getState()
77
+ action = qlearning.act(state, actionSize) // ε-greedy
78
+ result = env.step(action)
79
+ qlearning.learnSimple(state, action, result.reward)
80
+ storage.addBattle(record)
81
+ batch.flush()
82
+ ```
83
+
84
+ ### Environment Interface
85
+
86
+ **Contract:**
87
+ ```typescript
88
+ abstract class Env {
89
+ abstract getState(): string;
90
+ abstract step(action: number): Promise<StepResult>;
91
+ abstract actionSize(): number;
92
+ abstract reset(): Promise<string>;
93
+ render(): string; // Optional
94
+ }
95
+ ```
96
+
97
+ **Built-in:** `RockPaperScissorsEnv` with 4 opponent strategies
98
+
99
+ ## Data Flow
100
+
101
+ ```
102
+ ┌─────────┐ ┌──────────────┐ ┌─────────┐
103
+ │ Env │────▶│ Agent │────▶│ Storage │
104
+ │ (State) │ │ Orchestrates│ │(Persist)│
105
+ └─────────┘ └──────┬───────┘ └─────────┘
106
+
107
+
108
+ ┌──────────────┐
109
+ │ QLearning │
110
+ │ (Updates) │
111
+ └──────────────┘
112
+ ```
113
+
114
+ ## Concurrency Model
115
+
116
+ - **Single-threaded** Node.js event loop
117
+ - **Synchronous SQLite** — No async/await for DB operations
118
+ - **Batch writes** — Reduce I/O overhead
119
+ - **In-memory Q-table cache** — Fast reads, periodic persistence
120
+
121
+ ## Migration from Cloudflare Workers
122
+
123
+ **Original:** Worker.js with D1 database, cron triggers, HTTP endpoints
124
+
125
+ **Changes:**
126
+ | Before | After |
127
+ |--------|-------|
128
+ | `env.DB` (D1) | `Storage` abstraction |
129
+ | Cron triggers | Manual/CLI training |
130
+ | HTTP endpoints | Library API + CLI |
131
+ | Global state | Instance-based |
132
+ | `Request/Response` | Function parameters |
133
+
134
+ **Preserved:**
135
+ - Q-Learning algorithm (α=0.1, γ=0.9, ε=0.1)
136
+ - Reward calculation: `(ai - user + 3) % 3`
137
+ - Training patterns (0-3)
138
+ - Battle history schema
139
+
140
+ ## Performance Characteristics
141
+
142
+ | Operation | Complexity | Notes |
143
+ |-----------|------------|-------|
144
+ | `act()` | O(actions) | Scans all actions for max Q |
145
+ | `learnSimple()` | O(1) | Single Q-value update |
146
+ | `learn()` | O(actions) | Finds max next Q |
147
+ | `getFullQTable()` | O(states×actions) | Full table scan |
148
+ | Batch insert | O(batch) | Single transaction |
149
+
150
+ ## Future Extensibility
151
+
152
+ - **New algorithms:** Extend `QLearning` or add `SARSA`, `DQN` classes
153
+ - **New environments:** Implement `Env` interface
154
+ - **New storage:** Implement `Storage` interface
155
+ - **Function approximation:** Replace tabular Q-table with neural networks
@@ -0,0 +1,210 @@
1
+ ---
2
+ title: CLI Reference
3
+ description: Command-line interface for training and playing
4
+ ---
5
+
6
+ # CLI Reference
7
+
8
+ LSJI includes a command-line interface for training and playing without writing code.
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ # Global install
14
+ npm install -g lsji
15
+
16
+ # Or use npx
17
+ npx lsji --help
18
+ ```
19
+
20
+ ## Commands
21
+
22
+ ### `lsji train`
23
+
24
+ Train the agent.
25
+
26
+ ```bash
27
+ lsji train [options]
28
+ ```
29
+
30
+ **Options:**
31
+
32
+ | Option | Description | Default |
33
+ |--------|-------------|---------|
34
+ | `--episodes <n>` | Number of training episodes | 200 |
35
+ | `--pattern <0-3>` | Training pattern | 0 |
36
+ | `--batch-size <n>` | Database batch size | 200 |
37
+ | `--opponent <type>` | Opponent strategy | random |
38
+ | `--storage <type>` | Storage backend | sqlite |
39
+ | `--db-path <path>` | Database file path | ./lsji.db |
40
+ | `--alpha <n>` | Learning rate | 0.1 |
41
+ | `--gamma <n>` | Discount factor | 0.9 |
42
+ | `--epsilon <n>` | Exploration rate | 0.1 |
43
+ | `--json` | Output as JSON | false |
44
+
45
+ **Training Patterns:**
46
+ - `0` — Random actions
47
+ - `1` — Always Rock
48
+ - `2` — Counter previous action
49
+ - `3` — Sequential (0,1,2,0,1,2...)
50
+
51
+ **Opponent Strategies:**
52
+ - `random` — Random actions
53
+ - `always_rock` — Always plays Rock
54
+ - `counter` — Counters agent's previous action
55
+ - `sequential` — Cycles through actions
56
+
57
+ **Examples:**
58
+ ```bash
59
+ # Default training
60
+ lsji train --episodes 500
61
+
62
+ # Train against always-rock opponent
63
+ lsji train --episodes 100 --pattern 1 --opponent always_rock
64
+
65
+ # Train with custom hyperparameters
66
+ lsji train --episodes 1000 --alpha 0.05 --gamma 0.95 --epsilon 0.2
67
+
68
+ # Use memory storage (ephemeral)
69
+ lsji train --episodes 100 --storage memory
70
+ ```
71
+
72
+ ### `lsji play`
73
+
74
+ Play a single game against the agent.
75
+
76
+ ```bash
77
+ lsji play --hand <0|1|2> [options]
78
+ ```
79
+
80
+ **Options:**
81
+
82
+ | Option | Description |
83
+ |--------|-------------|
84
+ | `--hand <0\|1\|2>` | Your hand: 0=Rock, 1=Scissors, 2=Paper |
85
+ | `--opponent <type>` | Opponent strategy |
86
+ | `--storage <type>` | Storage backend |
87
+ | `--db-path <path>` | Database file path |
88
+ | `--json` | Output as JSON |
89
+
90
+ **Examples:**
91
+ ```bash
92
+ lsji play --hand 0 # Play Rock
93
+ lsji play --hand 1 # Play Scissors
94
+ lsji play --hand 2 --json # Play Paper, JSON output
95
+ ```
96
+
97
+ ### `lsji status`
98
+
99
+ Show system status and statistics.
100
+
101
+ ```bash
102
+ lsji status [options]
103
+ ```
104
+
105
+ **Options:**
106
+
107
+ | Option | Description |
108
+ |--------|-------------|
109
+ | `--storage <type>` | Storage backend |
110
+ | `--db-path <path>` | Database file path |
111
+ | `--json` | Output as JSON |
112
+
113
+ **Example:**
114
+ ```bash
115
+ lsji status --json
116
+ ```
117
+
118
+ Output:
119
+ ```json
120
+ {
121
+ "status": "running",
122
+ "todayTotal": 42,
123
+ "limit": 90000,
124
+ "performance": [
125
+ { "mode": "train", "total": 1000, "win_rate": 65.5 },
126
+ { "mode": "test", "total": 50, "win_rate": 72.0 }
127
+ ],
128
+ "aiBrain": [
129
+ { "state": "0", "action": 0, "q_value": 0.45 },
130
+ { "state": "0", "action": 1, "q_value": 0.12 }
131
+ ]
132
+ }
133
+ ```
134
+
135
+ ### `lsji start`
136
+
137
+ Enable training and play.
138
+
139
+ ```bash
140
+ lsji start [options]
141
+ ```
142
+
143
+ ### `lsji stop`
144
+
145
+ Disable training and play (system paused).
146
+
147
+ ```bash
148
+ lsji stop [options]
149
+ ```
150
+
151
+ ### `lsji help`
152
+
153
+ Show help message.
154
+
155
+ ```bash
156
+ lsji help
157
+ lsji --help
158
+ lsji -h
159
+ ```
160
+
161
+ ## Environment Variables
162
+
163
+ | Variable | Description | Default |
164
+ |----------|-------------|---------|
165
+ | `LSJI_STORAGE` | Default storage backend | sqlite |
166
+ | `LSJI_DB_PATH` | Default database path | ./lsji.db |
167
+
168
+ ## Examples
169
+
170
+ ### Full Training Session
171
+
172
+ ```bash
173
+ # Start fresh
174
+ rm -f lsji.db
175
+
176
+ # Train against random opponent
177
+ lsji train --episodes 500 --opponent random
178
+
179
+ # Train against counter opponent
180
+ lsji train --episodes 500 --opponent counter
181
+
182
+ # Check progress
183
+ lsji status --json
184
+
185
+ # Play a few games
186
+ lsji play --hand 0
187
+ lsji play --hand 1
188
+ lsji play --hand 2
189
+ ```
190
+
191
+ ### Using Memory Storage (CI/Testing)
192
+
193
+ ```bash
194
+ lsji train --episodes 100 --storage memory
195
+ lsji play --hand 0 --storage memory
196
+ lsji status --storage memory
197
+ ```
198
+
199
+ ### Custom Hyperparameters
200
+
201
+ ```bash
202
+ lsji train \
203
+ --episodes 2000 \
204
+ --alpha 0.05 \
205
+ --gamma 0.95 \
206
+ --epsilon 0.2 \
207
+ --opponent random \
208
+ --storage sqlite \
209
+ --db-path ./custom.db
210
+ ```
@@ -0,0 +1,162 @@
1
+ ---
2
+ title: Contributing
3
+ description: How to contribute to LSJI
4
+ ---
5
+
6
+ # Contributing
7
+
8
+ Thank you for your interest in contributing to LSJI!
9
+
10
+ ## Development Setup
11
+
12
+ ```bash
13
+ # Clone the repository
14
+ git clone https://github.com/ryotagtagtag-wq/LSJI.git
15
+ cd LSJI
16
+
17
+ # Install dependencies
18
+ npm install
19
+
20
+ # Run tests
21
+ npm test
22
+
23
+ # Build documentation
24
+ cd docs && npm run build
25
+ ```
26
+
27
+ ## Project Structure
28
+
29
+ ```
30
+ LSJI/
31
+ ├── src/ # Core library
32
+ │ ├── core/ # QLearning, Agent, Env
33
+ │ ├── storage/ # Storage backends
34
+ │ ├── envs/ # Built-in environments
35
+ │ ├── cli.ts # CLI
36
+ │ └── index.ts # Public exports
37
+ ├── test/ # Vitest tests
38
+ ├── docs/ # Docusaurus documentation
39
+ └── bin/ # CLI entry point
40
+ ```
41
+
42
+ ## Making Changes
43
+
44
+ ### 1. Create a Branch
45
+
46
+ ```bash
47
+ git checkout -b feature/my-feature
48
+ ```
49
+
50
+ ### 2. Make Changes
51
+
52
+ Follow the existing code style:
53
+ - TypeScript with JSDoc comments
54
+ - ESM imports/exports
55
+ - No external dependencies in core
56
+
57
+ ### 3. Run Tests
58
+
59
+ ```bash
60
+ npm test
61
+ ```
62
+
63
+ ### 4. Update Documentation
64
+
65
+ If you add new features, update relevant docs in `docs/docs/`.
66
+
67
+ ### 5. Commit
68
+
69
+ ```bash
70
+ git add .
71
+ git commit -m "feat: add my feature"
72
+ ```
73
+
74
+ **Commit Message Format:**
75
+ - `feat:` — New feature
76
+ - `fix:` — Bug fix
77
+ - `docs:` — Documentation
78
+ - `refactor:` — Code refactoring
79
+ - `test:` — Tests
80
+ - `chore:` — Maintenance
81
+
82
+ ### 6. Push and Create PR
83
+
84
+ ```bash
85
+ git push origin feature/my-feature
86
+ ```
87
+
88
+ ## Adding a New Environment
89
+
90
+ 1. Create `src/envs/my-env.ts` extending `Env`
91
+ 2. Implement all abstract methods
92
+ 3. Export from `src/index.ts`
93
+ 4. Add documentation in `docs/docs/api/environments.md`
94
+ 5. Add example in `docs/docs/examples/`
95
+
96
+ ## Adding a New Storage Backend
97
+
98
+ 1. Create `src/storage/my-backend.ts` extending `Storage`
99
+ 2. Implement all abstract methods
100
+ 3. Add to `createStorage` factory in `src/storage/index.ts`
101
+ 3. Export from `src/index.ts`
102
+ 4. Add tests in `test/storage/`
103
+
104
+ ## Modifying Learning Algorithm
105
+
106
+ 1. Extend `QLearning` class or create new class in `src/core/`
107
+ 2. Maintain compatibility with `Agent` interface
108
+ 3. Add tests for new algorithm
109
+ 4. Document in `docs/docs/api/`
110
+
111
+ ## Code Style
112
+
113
+ - **TypeScript** with strict mode
114
+ - **ESM** modules (`import`/`export`)
115
+ - **JSDoc** for all public APIs
116
+ - **No `any`** unless absolutely necessary
117
+ - **Async/await** for async operations
118
+
119
+ ## Testing Guidelines
120
+
121
+ - Use `MemoryStorage` for unit tests
122
+ - Test both success and error cases
123
+ - Test edge cases (empty Q-table, terminal states)
124
+ - Keep tests fast and isolated
125
+
126
+ ```typescript
127
+ // Example test structure
128
+ import { describe, it, expect, beforeEach } from 'vitest';
129
+ import { MyFeature } from '../src/core/my-feature';
130
+ import { MemoryStorage } from '../src/storage/memory';
131
+
132
+ describe('MyFeature', () => {
133
+ let storage;
134
+ let feature;
135
+
136
+ beforeEach(async () => {
137
+ storage = new MemoryStorage();
138
+ await storage.initialize();
139
+ feature = new MyFeature({ storage });
140
+ });
141
+
142
+ it('should do something', async () => {
143
+ const result = await feature.doSomething();
144
+ expect(result).toBe(expected);
145
+ });
146
+ });
147
+ ```
148
+
149
+ ## Documentation
150
+
151
+ - Update relevant `.md` files in `docs/docs/`
152
+ - Add JSDoc comments for new public APIs
153
+ - Include code examples
154
+
155
+ ## License
156
+
157
+ By contributing, you agree that your contributions will be licensed under the Apache 2.0 License.
158
+
159
+ ## Questions?
160
+
161
+ - Open a [GitHub Issue](https://github.com/ryotagtagtag-wq/LSJI/issues)
162
+ - Start a [Discussion](https://github.com/ryotagtagtag-wq/LSJI/discussions)