npm-noxyai 1.0.9 → 1.0.11
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.
- package/index-noxyai.js +335 -132
- package/package.json +3 -2
- package/index.html +0 -13
- package/reinstall_pip.py +0 -1
- package/script.js +0 -92
- package/server.js +0 -11
- package/snake_game.py +0 -90
- package/style.css +0 -11
- package/tic_tac_toe.py +0 -111
package/index-noxyai.js
CHANGED
|
@@ -12,6 +12,29 @@ const CONFIG_FILE = path.join(os.homedir(), '.noxyai.json');
|
|
|
12
12
|
const args = process.argv.slice(2);
|
|
13
13
|
const command = args[0];
|
|
14
14
|
|
|
15
|
+
// --- CONFIG MANAGEMENT ---
|
|
16
|
+
function loadConfig() {
|
|
17
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
18
|
+
try {
|
|
19
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
20
|
+
if (config.agentMode === undefined) config.agentMode = true;
|
|
21
|
+
if (config.autoDeduct === undefined) config.autoDeduct = false;
|
|
22
|
+
return config;
|
|
23
|
+
} catch(e) {}
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
token: null,
|
|
27
|
+
model: 'auto',
|
|
28
|
+
agentMode: true,
|
|
29
|
+
autoDeduct: false
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function saveConfig(config) {
|
|
34
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// --- UI HELPERS ---
|
|
15
38
|
function printLogo() {
|
|
16
39
|
console.log('\x1b[36m' + `
|
|
17
40
|
███╗ ██╗ ██████╗ ██╗ ██╗██╗ ██╗ █████╗ ██╗
|
|
@@ -23,40 +46,107 @@ function printLogo() {
|
|
|
23
46
|
` + '\x1b[0m');
|
|
24
47
|
}
|
|
25
48
|
|
|
26
|
-
function
|
|
27
|
-
|
|
49
|
+
function printFunnyArt() {
|
|
50
|
+
const art = `
|
|
51
|
+
▓▓ ▓▓
|
|
52
|
+
▓▓ ▓▓ ▓▓
|
|
53
|
+
▓▓▓▓ ▓▓
|
|
54
|
+
▓▓ ██ ▓▓
|
|
55
|
+
▓▓░░░░ ▓▓
|
|
56
|
+
▓▓▓▓░░ ▓▓
|
|
57
|
+
▓▓ ▓▓
|
|
58
|
+
▓▓ ▓▓ ▓▓
|
|
59
|
+
▓▓ ▓▓ ▓▓ ▓▓
|
|
60
|
+
▓▓ ▓▓ ▓▓ ▓▓
|
|
61
|
+
▓▓ ▓▓▓▓▓▓▓▓▓▓▓▓▓▓ ░░▓▓
|
|
62
|
+
▓▓ ▓▓
|
|
63
|
+
▓▓░░ ▓▓
|
|
64
|
+
▓▓░░ ░░▓▓
|
|
65
|
+
▓▓░░ ░░▓▓
|
|
66
|
+
▓▓░░░░ ░░░░░░░░░░ ▓▓
|
|
67
|
+
▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▓▓ ▓▓
|
|
68
|
+
▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓
|
|
69
|
+
▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓
|
|
70
|
+
▓▓░░▓▓░░▓▓ ▓▓░░▓▓░░▓▓
|
|
71
|
+
▓▓░░▓▓░░▓▓ ▓▓░░▓▓░░▓▓
|
|
72
|
+
▓▓░░▓▓░░▓▓ ▓▓░░▓▓░░▓▓
|
|
73
|
+
▓▓ ▓▓ ▓▓ ▓▓`;
|
|
74
|
+
console.log('\x1b[35m' + art + '\x1b[0m');
|
|
28
75
|
}
|
|
29
76
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
77
|
+
let spinnerInterval;
|
|
78
|
+
function startSpinner(text) {
|
|
79
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
80
|
+
let i = 0;
|
|
81
|
+
process.stdout.write('\x1b[?25l');
|
|
82
|
+
spinnerInterval = setInterval(() => {
|
|
83
|
+
process.stdout.write('\r\x1b[36m${frames[i]} ${text}\x1b[0m');
|
|
84
|
+
i = (i + 1) % frames.length;
|
|
85
|
+
}, 80);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function stopSpinner() {
|
|
89
|
+
clearInterval(spinnerInterval);
|
|
90
|
+
process.stdout.write('\r\x1b[K\x1b[?25h');
|
|
36
91
|
}
|
|
37
92
|
|
|
38
93
|
function logout() {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
console.log('You are already logged out.');
|
|
44
|
-
}
|
|
94
|
+
const config = loadConfig();
|
|
95
|
+
config.token = null;
|
|
96
|
+
saveConfig(config);
|
|
97
|
+
console.log('✅ Successfully logged out.');
|
|
45
98
|
}
|
|
46
99
|
|
|
47
100
|
function openBrowser(url) {
|
|
48
101
|
const platform = os.platform();
|
|
49
|
-
if (platform === 'android') {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
102
|
+
if (platform === 'android') spawn('termux-open-url', [url], { stdio: 'ignore' });
|
|
103
|
+
else if (platform === 'darwin') spawn('open', [url], { stdio: 'ignore' });
|
|
104
|
+
else if (platform === 'win32') spawn('cmd.exe', ['/c', 'start', '""', url], { stdio: 'ignore' });
|
|
105
|
+
else spawn('xdg-open', [url], { stdio: 'ignore' });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// --- SEARCH FUNCTION (Now uses backend) ---
|
|
109
|
+
async function searchWeb(query) {
|
|
110
|
+
try {
|
|
111
|
+
const config = loadConfig();
|
|
112
|
+
const res = await fetch('${BASE_URL}/api/io?action=search&q=' + encodeURIComponent(query), {
|
|
113
|
+
headers: { 'Authorization': 'Bearer ' + config.token }
|
|
114
|
+
});
|
|
115
|
+
const data = await res.json();
|
|
116
|
+
return data;
|
|
117
|
+
} catch (err) {
|
|
118
|
+
return { error: err.message };
|
|
57
119
|
}
|
|
58
120
|
}
|
|
59
121
|
|
|
122
|
+
// --- CREDIT DEDUCTION WITH MANUAL APPROVAL ---
|
|
123
|
+
async function requestCreditDeduction(usage, model) {
|
|
124
|
+
const config = loadConfig();
|
|
125
|
+
|
|
126
|
+
if (config.autoDeduct) {
|
|
127
|
+
return { approved: true, cost: usage.estimatedCost };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
console.log('\n\x1b[33m💰 Credit Usage Summary:\x1b[0m');
|
|
131
|
+
console.log(' Prompt Tokens: ${usage.promptTokens}');
|
|
132
|
+
console.log(' Response Tokens: ${usage.responseTokens}');
|
|
133
|
+
if (usage.reasoningTokens) {
|
|
134
|
+
console.log(' \x1b[31mReasoning Tokens: ${usage.reasoningTokens}\x1b[0m');
|
|
135
|
+
}
|
|
136
|
+
console.log(' Total Tokens: ${usage.totalTokens}');
|
|
137
|
+
console.log(' \x1b[32mEstimated Cost: ${usage.estimatedCost} credits\x1b[0m');
|
|
138
|
+
|
|
139
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
140
|
+
|
|
141
|
+
return new Promise((resolve) => {
|
|
142
|
+
rl.question('\n\x1b[33mApprove credit deduction? (y/n): \x1b[0m', (answer) => {
|
|
143
|
+
rl.close();
|
|
144
|
+
resolve({ approved: answer.toLowerCase() === 'y', cost: usage.estimatedCost });
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// --- CORE FUNCTIONS ---
|
|
60
150
|
async function login() {
|
|
61
151
|
printLogo();
|
|
62
152
|
console.log('Initializing secure login...\n');
|
|
@@ -65,14 +155,14 @@ async function login() {
|
|
|
65
155
|
const { deviceCode, verificationUrl, interval } = await initRes.json();
|
|
66
156
|
|
|
67
157
|
console.log('=============================================');
|
|
68
|
-
console.log(
|
|
158
|
+
console.log('URL: ${verificationUrl}');
|
|
69
159
|
console.log('=============================================\n');
|
|
70
160
|
|
|
71
161
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
72
162
|
rl.question('Press ENTER to automatically open the browser...', () => {
|
|
73
163
|
openBrowser(verificationUrl);
|
|
74
164
|
rl.close();
|
|
75
|
-
|
|
165
|
+
startSpinner('Waiting for authentication...');
|
|
76
166
|
});
|
|
77
167
|
|
|
78
168
|
const pollInterval = setInterval(async () => {
|
|
@@ -87,7 +177,11 @@ async function login() {
|
|
|
87
177
|
|
|
88
178
|
if (data.status === 'success') {
|
|
89
179
|
clearInterval(pollInterval);
|
|
90
|
-
|
|
180
|
+
stopSpinner();
|
|
181
|
+
const config = loadConfig();
|
|
182
|
+
config.token = data.token;
|
|
183
|
+
saveConfig(config);
|
|
184
|
+
|
|
91
185
|
console.clear();
|
|
92
186
|
printLogo();
|
|
93
187
|
console.log('✅ Login successful! Terminal connected.');
|
|
@@ -95,6 +189,7 @@ async function login() {
|
|
|
95
189
|
process.exit(0);
|
|
96
190
|
} else if (data.error) {
|
|
97
191
|
clearInterval(pollInterval);
|
|
192
|
+
stopSpinner();
|
|
98
193
|
console.error('\n❌ Login failed: ' + data.error);
|
|
99
194
|
process.exit(1);
|
|
100
195
|
}
|
|
@@ -108,205 +203,313 @@ async function login() {
|
|
|
108
203
|
|
|
109
204
|
function runTerminalCommand(cmd) {
|
|
110
205
|
return new Promise((resolve, reject) => {
|
|
111
|
-
console.log(
|
|
112
|
-
|
|
206
|
+
console.log('\n\x1b[33m⚡ Running:\x1b[0m ${cmd}\n');
|
|
113
207
|
const isServer = cmd.includes('dev') || cmd.includes('serve') || cmd.includes('host') || cmd.includes('start');
|
|
114
|
-
|
|
115
|
-
const child = spawn(cmd, {
|
|
116
|
-
shell: true,
|
|
117
|
-
stdio: 'inherit'
|
|
118
|
-
});
|
|
208
|
+
const child = spawn(cmd, { shell: true, stdio: 'inherit' });
|
|
119
209
|
|
|
120
210
|
if (isServer) {
|
|
121
|
-
console.log(
|
|
211
|
+
console.log('\n\x1b[36m[i] Server detected. Running in foreground. Press Ctrl+C to stop.\x1b[0m');
|
|
122
212
|
setTimeout(() => resolve(), 2500);
|
|
123
213
|
}
|
|
124
214
|
|
|
125
215
|
child.on('close', (code) => {
|
|
126
216
|
if (!isServer) {
|
|
127
|
-
if (code !== 0) reject(
|
|
217
|
+
if (code !== 0) reject('Command failed with exit code ${code}');
|
|
128
218
|
else resolve();
|
|
129
219
|
}
|
|
130
220
|
});
|
|
131
|
-
|
|
132
221
|
child.on('error', (error) => reject(error.message));
|
|
133
222
|
});
|
|
134
223
|
}
|
|
135
224
|
|
|
136
225
|
function getLocalContext() {
|
|
137
226
|
try {
|
|
138
|
-
const files = fs.readdirSync(process.cwd()).filter(f => !f.startsWith('node_modules') && !f.startsWith('.git')).slice(0,
|
|
139
|
-
return
|
|
140
|
-
} catch (e) {
|
|
141
|
-
return "";
|
|
142
|
-
}
|
|
227
|
+
const files = fs.readdirSync(process.cwd()).filter(f => !f.startsWith('node_modules') && !f.startsWith('.git')).slice(0, 30);
|
|
228
|
+
return '\n[SYSTEM: You are in ${process.cwd()}. Files here: ${files.join(', ')}]\n';
|
|
229
|
+
} catch (e) { return ""; }
|
|
143
230
|
}
|
|
144
231
|
|
|
145
232
|
async function chat(prompt, depth = 0) {
|
|
146
|
-
const
|
|
147
|
-
if (!token) {
|
|
148
|
-
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (depth > 3) {
|
|
153
|
-
console.error('\n❌ Agent reached maximum retry depth. Please fix the error manually.');
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
233
|
+
const config = loadConfig();
|
|
234
|
+
if (!config.token) { console.error('❌ Unauthorized: Run "noxyai login"'); return; }
|
|
235
|
+
if (depth > 6) { console.error('\n❌ Reached max reasoning depth. Stopping.'); return; }
|
|
156
236
|
|
|
157
237
|
const enhancedPrompt = depth === 0 ? getLocalContext() + prompt : prompt;
|
|
158
|
-
|
|
238
|
+
|
|
239
|
+
startSpinner(depth === 0 ? 'NoxyAI is thinking...' : 'NoxyAI is analyzing results...');
|
|
159
240
|
|
|
160
241
|
try {
|
|
161
242
|
const res = await fetch(BASE_URL + '/api/io', {
|
|
162
243
|
method: 'POST',
|
|
163
|
-
headers: {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
244
|
+
headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + config.token },
|
|
245
|
+
body: JSON.stringify({
|
|
246
|
+
prompt: enhancedPrompt,
|
|
247
|
+
model: config.model,
|
|
248
|
+
agentMode: config.agentMode
|
|
249
|
+
})
|
|
168
250
|
});
|
|
169
251
|
|
|
252
|
+
stopSpinner();
|
|
253
|
+
|
|
170
254
|
if (!res.ok) {
|
|
171
255
|
const errorText = await res.text();
|
|
172
|
-
|
|
173
|
-
|
|
256
|
+
if (res.status === 402) {
|
|
257
|
+
console.log('\n\x1b[31m' + errorText + '\x1b[0m');
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
return console.error('\n❌ API Error: ' + errorText);
|
|
174
261
|
}
|
|
175
262
|
|
|
176
|
-
|
|
177
|
-
|
|
263
|
+
const modelDisplay = config.model === 'auto' ? 'Llama 3.3 70B' : config.model;
|
|
264
|
+
const agentStatus = config.agentMode ? '🤖 Agent Mode' : '💬 Chat Mode';
|
|
265
|
+
console.log('\n${agentStatus} \x1b[36mNoxyAI (${modelDisplay}):\x1b[0m\n');
|
|
178
266
|
|
|
179
267
|
const reader = res.body.getReader();
|
|
180
268
|
const decoder = new TextDecoder('utf-8');
|
|
181
269
|
let fullResponse = "";
|
|
270
|
+
let fullReasoning = "";
|
|
271
|
+
let usage = null;
|
|
272
|
+
let isQwen = false;
|
|
182
273
|
|
|
183
274
|
while (true) {
|
|
184
275
|
const { done, value } = await reader.read();
|
|
185
276
|
if (done) break;
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
const lines = chunk.split('\n');
|
|
189
|
-
|
|
277
|
+
|
|
278
|
+
const lines = decoder.decode(value, { stream: true }).split('\n');
|
|
190
279
|
for (const line of lines) {
|
|
191
280
|
if (line.startsWith('data: ')) {
|
|
192
281
|
const data = line.slice(6).trim();
|
|
193
282
|
if (data === '[DONE]') break;
|
|
283
|
+
|
|
194
284
|
try {
|
|
195
285
|
const parsed = JSON.parse(data);
|
|
286
|
+
|
|
287
|
+
if (parsed.isQwen) {
|
|
288
|
+
isQwen = true;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (parsed.reasoning) {
|
|
292
|
+
fullReasoning += parsed.reasoning;
|
|
293
|
+
if (isQwen) {
|
|
294
|
+
process.stdout.write('\x1b[31m' + parsed.reasoning + '\x1b[0m');
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
196
298
|
if (parsed.text) {
|
|
197
299
|
fullResponse += parsed.text;
|
|
198
300
|
process.stdout.write(parsed.text);
|
|
199
301
|
}
|
|
302
|
+
|
|
303
|
+
if (parsed.usage) {
|
|
304
|
+
usage = parsed.usage;
|
|
305
|
+
}
|
|
200
306
|
} catch (e) {}
|
|
201
307
|
}
|
|
202
308
|
}
|
|
203
309
|
}
|
|
204
310
|
|
|
311
|
+
if (usage && depth === 0) {
|
|
312
|
+
const approval = await requestCreditDeduction(usage, config.model);
|
|
313
|
+
|
|
314
|
+
if (approval.approved) {
|
|
315
|
+
const deductRes = await fetch(BASE_URL + '/api/io/deduct', {
|
|
316
|
+
method: 'POST',
|
|
317
|
+
headers: {
|
|
318
|
+
'Content-Type': 'application/json',
|
|
319
|
+
'Authorization': 'Bearer ' + config.token
|
|
320
|
+
},
|
|
321
|
+
body: JSON.stringify({
|
|
322
|
+
amount: approval.cost,
|
|
323
|
+
model: config.model,
|
|
324
|
+
tokens: usage.totalTokens
|
|
325
|
+
})
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
if (deductRes.ok) {
|
|
329
|
+
console.log('\n\x1b[32m✅ Credits deducted successfully!\x1b[0m');
|
|
330
|
+
} else {
|
|
331
|
+
console.log('\n\x1b[31m❌ Credit deduction failed\x1b[0m');
|
|
332
|
+
}
|
|
333
|
+
} else {
|
|
334
|
+
console.log('\n\x1b[33m⚠️ Credit deduction cancelled\x1b[0m');
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
if (!config.agentMode) {
|
|
340
|
+
console.log('\n');
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
|
|
205
344
|
console.log('\n\n\x1b[32m[Agent] Processing actions...\x1b[0m');
|
|
345
|
+
let agentFeedback = "";
|
|
206
346
|
|
|
207
|
-
|
|
208
|
-
let
|
|
347
|
+
const readRegex = /<read>([\s\S]*?)<\/read>/g;
|
|
348
|
+
let match;
|
|
349
|
+
while ((match = readRegex.exec(fullResponse)) !== null) {
|
|
350
|
+
const filePath = match[1].trim();
|
|
351
|
+
try {
|
|
352
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
353
|
+
console.log('\x1b[34m📖 Read file:\x1b[0m ${filePath}');
|
|
354
|
+
agentFeedback += '\n[FILE: ${filePath}]\n\`\`\`\n${content}\n\`\`\`\n';
|
|
355
|
+
} catch (err) {
|
|
356
|
+
console.log('\x1b[31m❌ Failed to read:\x1b[0m ${filePath}');
|
|
357
|
+
agentFeedback += '\n[ERROR reading ${filePath}: ${err.message}]\n';
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
const searchRegex = /<search>([\s\S]*?)<\/search>/g;
|
|
362
|
+
while ((match = searchRegex.exec(fullResponse)) !== null) {
|
|
363
|
+
const query = match[1].trim();
|
|
364
|
+
console.log('\x1b[35m🔍 Searching Web:\x1b[0m ${query}');
|
|
365
|
+
try {
|
|
366
|
+
const searchData = await searchWeb(query);
|
|
367
|
+
const snippets = searchData.items ? searchData.items.map(i => '- ${i.title}: ${i.snippet}').join('\n') : "No results.";
|
|
368
|
+
agentFeedback += '\n[SEARCH RESULTS FOR "${query}"]\n${snippets}\n';
|
|
369
|
+
} catch (err) {
|
|
370
|
+
agentFeedback += '\n[SEARCH FAILED: ${err.message}]\n';
|
|
371
|
+
}
|
|
372
|
+
}
|
|
209
373
|
|
|
210
374
|
const fileRegex = /<file path="([^"]+)">([\s\S]*?)<\/file>/g;
|
|
211
|
-
let match;
|
|
212
375
|
while ((match = fileRegex.exec(fullResponse)) !== null) {
|
|
213
|
-
const filePath = match[1];
|
|
214
|
-
const content = match[2];
|
|
215
|
-
|
|
376
|
+
const filePath = match[1], content = match[2];
|
|
216
377
|
const dir = path.dirname(filePath);
|
|
217
378
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
218
379
|
fs.writeFileSync(filePath, content.trim());
|
|
219
|
-
console.log(
|
|
220
|
-
filesCreated++;
|
|
380
|
+
console.log('\x1b[32m✔ Wrote file:\x1b[0m ${filePath}');
|
|
221
381
|
}
|
|
222
382
|
|
|
223
383
|
const execRegex = /<execute>([\s\S]*?)<\/execute>/g;
|
|
224
384
|
const commands = [];
|
|
225
|
-
while ((match = execRegex.exec(fullResponse)) !== null)
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
|
|
385
|
+
while ((match = execRegex.exec(fullResponse)) !== null) commands.push(match[1].trim());
|
|
386
|
+
|
|
229
387
|
for (const cmd of commands) {
|
|
230
|
-
commandsRun++;
|
|
231
388
|
try {
|
|
232
389
|
await runTerminalCommand(cmd);
|
|
233
390
|
} catch (err) {
|
|
234
|
-
console.
|
|
235
|
-
|
|
236
|
-
const errorPrompt = `I ran the command "${cmd}" and got this error:\n\n${err}\n\nPlease fix the issue. Use <file> tags to rewrite files or <execute> to run commands.`;
|
|
237
|
-
await chat(errorPrompt, depth + 1);
|
|
238
|
-
return;
|
|
391
|
+
console.log('\x1b[31m❌ Command Failed:\x1b[0m ${err}');
|
|
392
|
+
agentFeedback += '\n[COMMAND ERROR running "${cmd}": ${err}]\n';
|
|
239
393
|
}
|
|
240
394
|
}
|
|
241
395
|
|
|
396
|
+
if (agentFeedback) {
|
|
397
|
+
console.log('\n\x1b[33m🔄 Sending data back to Agent...\x1b[0m');
|
|
398
|
+
await chat('Here are the results of your actions:\n${agentFeedback}\nWhat is the next step? Output <file> or <execute> if ready, or <read>/<search> if you need more info.', depth + 1);
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
|
|
242
402
|
console.log('\n\x1b[32m════════════════════════════════════════\x1b[0m');
|
|
243
|
-
console.log(
|
|
244
|
-
|
|
245
|
-
if (commandsRun > 0) console.log(`🚀 Executed ${commandsRun} command(s)`);
|
|
246
|
-
if (filesCreated === 0 && commandsRun === 0) console.log(`💬 Answered query`);
|
|
247
|
-
console.log('\x1b[32m════════════════════════════════════════\x1b[0m\n');
|
|
403
|
+
console.log('\x1b[32m✨ Task Completed!\x1b[0m');
|
|
404
|
+
console.log('\n\x1b[32m════════════════════════════════════════\x1b[0m\n');
|
|
248
405
|
|
|
249
|
-
} catch (error) {
|
|
250
|
-
|
|
406
|
+
} catch (error) {
|
|
407
|
+
stopSpinner();
|
|
408
|
+
console.error('\n❌ Connection error: ' + error.message);
|
|
251
409
|
}
|
|
252
410
|
}
|
|
253
411
|
|
|
254
|
-
function showHelp() {
|
|
255
|
-
printLogo();
|
|
256
|
-
console.log(`
|
|
257
|
-
\x1b[33mNoxyAI Agent CLI - Available Commands\x1b[0m
|
|
258
|
-
|
|
259
|
-
\x1b[32mnoxyai\x1b[0m Start Interactive Mode (Recommended)
|
|
260
|
-
\x1b[32mnoxyai login\x1b[0m Authenticate your terminal
|
|
261
|
-
\x1b[32mnoxyai logout\x1b[0m Remove authentication from this device
|
|
262
|
-
\x1b[32mnoxyai chat "<prompt>"\x1b[0m Run a single prompt and exit
|
|
263
|
-
\x1b[32mnoxyai help\x1b[0m Show this menu
|
|
264
|
-
`);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
412
|
function startInteractiveMode() {
|
|
413
|
+
const config = loadConfig();
|
|
268
414
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
269
415
|
printLogo();
|
|
270
|
-
|
|
271
|
-
|
|
416
|
+
printFunnyArt();
|
|
417
|
+
|
|
418
|
+
const agentStatus = config.agentMode ? '\x1b[32mON\x1b[0m' : '\x1b[31mOFF\x1b[0m';
|
|
419
|
+
const deductStatus = config.autoDeduct ? '\x1b[32mAUTO\x1b[0m' : '\x1b[33mMANUAL\x1b[0m';
|
|
420
|
+
|
|
421
|
+
console.log('\x1b[33mWelcome to NoxyAI Interactive Mode!\x1b[0m');
|
|
422
|
+
console.log('Current Model: \x1b[32m${config.model}\x1b[0m');
|
|
423
|
+
console.log('Agent Mode: ${agentStatus} | Credit Mode: ${deductStatus}');
|
|
424
|
+
console.log('\nCommands:');
|
|
425
|
+
console.log(' \x1b[36m/agent\x1b[0m - Toggle Agent Mode (3-model collaboration)');
|
|
426
|
+
console.log(' \x1b[36m/auto\x1b[0m - Toggle Auto Credit Deduction');
|
|
427
|
+
console.log(' \x1b[36m/model\x1b[0m - Change AI Model');
|
|
428
|
+
console.log(' \x1b[36m/clear\x1b[0m - Clear screen');
|
|
429
|
+
console.log(' \x1b[36m/exit\x1b[0m - Exit\n');
|
|
272
430
|
|
|
273
431
|
function ask() {
|
|
274
|
-
|
|
432
|
+
const prompt = config.agentMode ? '\x1b[36mNoxyAI [Agent] > \x1b[0m' : '\x1b[36mNoxyAI [Chat] > \x1b[0m';
|
|
433
|
+
rl.question(prompt, async (input) => {
|
|
275
434
|
const trimmed = input.trim();
|
|
276
|
-
|
|
277
|
-
|
|
435
|
+
const lower = trimmed.toLowerCase();
|
|
436
|
+
|
|
437
|
+
if (lower === 'exit' || lower === '/exit') {
|
|
438
|
+
console.log('\n\x1b[36mGoodbye! 👋\x1b[0m\n');
|
|
278
439
|
process.exit(0);
|
|
440
|
+
}
|
|
441
|
+
else if (lower === '/clear') {
|
|
442
|
+
console.clear();
|
|
443
|
+
printLogo();
|
|
444
|
+
printFunnyArt();
|
|
445
|
+
ask();
|
|
446
|
+
}
|
|
447
|
+
else if (lower === '/agent') {
|
|
448
|
+
config.agentMode = !config.agentMode;
|
|
449
|
+
saveConfig(config);
|
|
450
|
+
const status = config.agentMode ? '\x1b[32mON\x1b[0m' : '\x1b[31mOFF\x1b[0m';
|
|
451
|
+
console.log('\n\x1b[33m🤖 Agent Mode: ${status}\x1b[0m');
|
|
452
|
+
if (config.agentMode) {
|
|
453
|
+
console.log('\n\x1b[36mAgent mode uses 3 models working together:\x1b[0m');
|
|
454
|
+
console.log(' • Search & Analysis');
|
|
455
|
+
console.log(' • Reasoning & Planning');
|
|
456
|
+
console.log(' • Code Generation & Execution\n');
|
|
457
|
+
}
|
|
458
|
+
ask();
|
|
459
|
+
}
|
|
460
|
+
else if (lower === '/auto') {
|
|
461
|
+
config.autoDeduct = !config.autoDeduct;
|
|
462
|
+
saveConfig(config);
|
|
463
|
+
const status = config.autoDeduct ? '\x1b[32mAUTO\x1b[0m' : '\x1b[33mMANUAL\x1b[0m';
|
|
464
|
+
console.log('\n\x1b[33m💰 Credit Deduction Mode: ${status}\x1b[0m\n');
|
|
465
|
+
ask();
|
|
279
466
|
}
|
|
280
|
-
if (
|
|
467
|
+
else if (lower === '/model') {
|
|
468
|
+
console.log('\n\x1b[33mSelect an AI Model:\x1b[0m');
|
|
469
|
+
console.log(' \x1b[36m1)\x1b[0m Auto (Llama 3.3 70B - Fast/Coding)');
|
|
470
|
+
console.log(' \x1b[36m2)\x1b[0m Qwen3 Next 80B Thinking (Deep Reasoning) \x1b[31m[Reasoning in Red]\x1b[0m');
|
|
471
|
+
console.log(' \x1b[36m3)\x1b[0m GLM 4.7');
|
|
472
|
+
|
|
473
|
+
rl.question('\nEnter number (1-3): ', (choice) => {
|
|
474
|
+
let selected = 'auto';
|
|
475
|
+
if (choice === '2') selected = 'Qwen3';
|
|
476
|
+
if (choice === '3') selected = 'GLM';
|
|
477
|
+
|
|
478
|
+
config.model = selected;
|
|
479
|
+
saveConfig(config);
|
|
480
|
+
|
|
481
|
+
console.log('\n\x1b[32m✔ Model successfully changed to: ${selected}\x1b[0m\n');
|
|
482
|
+
ask();
|
|
483
|
+
});
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
else if (trimmed) {
|
|
281
487
|
await chat(trimmed);
|
|
488
|
+
ask();
|
|
489
|
+
} else {
|
|
490
|
+
ask();
|
|
282
491
|
}
|
|
283
|
-
ask();
|
|
284
492
|
});
|
|
285
493
|
}
|
|
286
494
|
ask();
|
|
287
495
|
}
|
|
288
496
|
|
|
289
|
-
|
|
290
|
-
if (command === '
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
startInteractiveMode();
|
|
308
|
-
} else {
|
|
309
|
-
console.error(`❌ Unknown command: ${command}`);
|
|
310
|
-
console.log('Run "noxyai help" for a list of commands.');
|
|
311
|
-
process.exit(1);
|
|
497
|
+
if (command === 'login') login();
|
|
498
|
+
else if (command === 'logout') logout();
|
|
499
|
+
else if (command === 'help' || command === '--help') {
|
|
500
|
+
printLogo();
|
|
501
|
+
console.log('\n \x1b[32mnoxyai\x1b[0m Start Interactive Mode');
|
|
502
|
+
console.log(' \x1b[32mnoxyai chat "<prompt>"\x1b[0m Run single prompt');
|
|
503
|
+
console.log('\n Interactive Commands:');
|
|
504
|
+
console.log(' \x1b[36m/agent\x1b[0m Toggle Agent Mode (3-model collaboration)');
|
|
505
|
+
console.log(' \x1b[36m/auto\x1b[0m Toggle Auto Credit Deduction');
|
|
506
|
+
console.log(' \x1b[36m/model\x1b[0m Change AI Model\n');
|
|
507
|
+
}
|
|
508
|
+
else if (command === 'chat') {
|
|
509
|
+
chat(args.slice(1).join(' ')).then(() => process.exit(0));
|
|
510
|
+
}
|
|
511
|
+
else if (!command) startInteractiveMode();
|
|
512
|
+
else {
|
|
513
|
+
console.error('❌ Unknown command. Run "noxyai help"');
|
|
514
|
+
process.exit(1);
|
|
312
515
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "npm-noxyai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "CLI for NoxyAI",
|
|
5
5
|
"main": "index-noxyai.js",
|
|
6
6
|
"bin": {
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"keywords": [],
|
|
16
16
|
"type": "commonjs",
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"express": "^5.2.1"
|
|
18
|
+
"express": "^5.2.1",
|
|
19
|
+
"http": "^0.0.1-security"
|
|
19
20
|
}
|
|
20
21
|
}
|
package/index.html
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Snake Game</title>
|
|
7
|
-
<link rel="stylesheet" href="style.css">
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<canvas id="gameCanvas" width="400" height="400"></canvas>
|
|
11
|
-
<script src="script.js"></script>
|
|
12
|
-
</body>
|
|
13
|
-
</html>
|
package/reinstall_pip.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import pip; pip.uninstall('pip'); import os; os.system('python -m ensurepip')
|
package/script.js
DELETED
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
const canvas = document.getElementById('gameCanvas');
|
|
2
|
-
const ctx = canvas.getContext('2d');
|
|
3
|
-
|
|
4
|
-
let snake = [
|
|
5
|
-
{x: 200, y: 200},
|
|
6
|
-
{x: 190, y: 200},
|
|
7
|
-
{x: 180, y: 200},
|
|
8
|
-
{x: 170, y: 200},
|
|
9
|
-
{x: 160, y: 200}
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
let direction = 'right';
|
|
13
|
-
let score = 0;
|
|
14
|
-
let food = {x: Math.floor(Math.random() * 40) * 10, y: Math.floor(Math.random() * 40) * 10};
|
|
15
|
-
|
|
16
|
-
function draw() {
|
|
17
|
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
18
|
-
for (let i = 0; i < snake.length; i++) {
|
|
19
|
-
ctx.fillStyle = 'green';
|
|
20
|
-
ctx.fillRect(snake[i].x, snake[i].y, 10, 10);
|
|
21
|
-
}
|
|
22
|
-
ctx.fillStyle = 'red';
|
|
23
|
-
ctx.fillRect(food.x, food.y, 10, 10);
|
|
24
|
-
ctx.fillStyle = 'black';
|
|
25
|
-
ctx.font = '24px Arial';
|
|
26
|
-
ctx.textAlign = 'left';
|
|
27
|
-
ctx.textBaseline = 'top';
|
|
28
|
-
ctx.fillText('Score: ' + score, 10, 10);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function update() {
|
|
32
|
-
for (let i = snake.length - 1; i > 0; i--) {
|
|
33
|
-
snake[i] = {x: snake[i - 1].x, y: snake[i - 1].y};
|
|
34
|
-
}
|
|
35
|
-
if (direction === 'right') {
|
|
36
|
-
snake[0].x += 10;
|
|
37
|
-
} else if (direction === 'left') {
|
|
38
|
-
snake[0].x -= 10;
|
|
39
|
-
} else if (direction === 'up') {
|
|
40
|
-
snake[0].y -= 10;
|
|
41
|
-
} else if (direction === 'down') {
|
|
42
|
-
snake[0].y += 10;
|
|
43
|
-
}
|
|
44
|
-
if (snake[0].x === food.x && snake[0].y === food.y) {
|
|
45
|
-
score++;
|
|
46
|
-
food = {x: Math.floor(Math.random() * 40) * 10, y: Math.floor(Math.random() * 40) * 10};
|
|
47
|
-
snake.push({x: snake[snake.length - 1].x, y: snake[snake.length - 1].y});
|
|
48
|
-
}
|
|
49
|
-
for (let i = 1; i < snake.length; i++) {
|
|
50
|
-
if (snake[0].x === snake[i].x && snake[0].y === snake[i].y) {
|
|
51
|
-
alert('Game Over');
|
|
52
|
-
score = 0;
|
|
53
|
-
snake = [
|
|
54
|
-
{x: 200, y: 200},
|
|
55
|
-
{x: 190, y: 200},
|
|
56
|
-
{x: 180, y: 200},
|
|
57
|
-
{x: 170, y: 200},
|
|
58
|
-
{x: 160, y: 200}
|
|
59
|
-
];
|
|
60
|
-
direction = 'right';
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
if (snake[0].x < 0 || snake[0].x >= canvas.width || snake[0].y < 0 || snake[0].y >= canvas.height) {
|
|
64
|
-
alert('Game Over');
|
|
65
|
-
score = 0;
|
|
66
|
-
snake = [
|
|
67
|
-
{x: 200, y: 200},
|
|
68
|
-
{x: 190, y: 200},
|
|
69
|
-
{x: 180, y: 200},
|
|
70
|
-
{x: 170, y: 200},
|
|
71
|
-
{x: 160, y: 200}
|
|
72
|
-
];
|
|
73
|
-
direction = 'right';
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
setInterval(() => {
|
|
78
|
-
update();
|
|
79
|
-
draw();
|
|
80
|
-
}, 100);
|
|
81
|
-
|
|
82
|
-
document.addEventListener('keydown', (e) => {
|
|
83
|
-
if (e.key === 'ArrowRight' && direction !== 'left') {
|
|
84
|
-
direction = 'right';
|
|
85
|
-
} else if (e.key === 'ArrowLeft' && direction !== 'right') {
|
|
86
|
-
direction = 'left';
|
|
87
|
-
} else if (e.key === 'ArrowUp' && direction !== 'down') {
|
|
88
|
-
direction = 'up';
|
|
89
|
-
} else if (e.key === 'ArrowDown' && direction !== 'up') {
|
|
90
|
-
direction = 'down';
|
|
91
|
-
}
|
|
92
|
-
});
|
package/server.js
DELETED
package/snake_game.py
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
import pygame
|
|
2
|
-
import sys
|
|
3
|
-
import time
|
|
4
|
-
import random
|
|
5
|
-
|
|
6
|
-
# Direction Constants
|
|
7
|
-
UP = 1
|
|
8
|
-
RIGHT = 2
|
|
9
|
-
DOWN = 3
|
|
10
|
-
LEFT = 4
|
|
11
|
-
|
|
12
|
-
class SnakeGame:
|
|
13
|
-
def __init__(self, width=800, height=600):
|
|
14
|
-
self.width = width
|
|
15
|
-
self.height = height
|
|
16
|
-
self.snake = [(200, 200), (220, 200), (240, 200)]
|
|
17
|
-
self.direction = RIGHT
|
|
18
|
-
self.apple = self.set_new_apple()
|
|
19
|
-
self.score = 0
|
|
20
|
-
pygame.init()
|
|
21
|
-
self.display = pygame.display.set_mode((width, height))
|
|
22
|
-
self.font = pygame.font.Font(None, 36)
|
|
23
|
-
|
|
24
|
-
def set_new_apple(self):
|
|
25
|
-
while True:
|
|
26
|
-
x = random.randint(0, self.width - 20) // 20 * 20
|
|
27
|
-
y = random.randint(0, self.height - 20) // 20 * 20
|
|
28
|
-
apple = (x, y)
|
|
29
|
-
if apple not in self.snake:
|
|
30
|
-
return apple
|
|
31
|
-
|
|
32
|
-
def play(self):
|
|
33
|
-
clock = pygame.time.Clock()
|
|
34
|
-
while True:
|
|
35
|
-
for event in pygame.event.get():
|
|
36
|
-
if event.type == pygame.QUIT:
|
|
37
|
-
pygame.quit()
|
|
38
|
-
sys.exit()
|
|
39
|
-
elif event.type == pygame.KEYDOWN:
|
|
40
|
-
if event.key == pygame.K_UP and self.direction != DOWN:
|
|
41
|
-
self.direction = UP
|
|
42
|
-
elif event.key == pygame.K_DOWN and self.direction != UP:
|
|
43
|
-
self.direction = DOWN
|
|
44
|
-
elif event.key == pygame.K_LEFT and self.direction != RIGHT:
|
|
45
|
-
self.direction = LEFT
|
|
46
|
-
elif event.key == pygame.K_RIGHT and self.direction != LEFT:
|
|
47
|
-
self.direction = RIGHT
|
|
48
|
-
|
|
49
|
-
head = self.snake[-1]
|
|
50
|
-
if self.direction == UP:
|
|
51
|
-
new_head = (head[0], head[1] - 20)
|
|
52
|
-
elif self.direction == DOWN:
|
|
53
|
-
new_head = (head[0], head[1] + 20)
|
|
54
|
-
elif self.direction == LEFT:
|
|
55
|
-
new_head = (head[0] - 20, head[1])
|
|
56
|
-
elif self.direction == RIGHT:
|
|
57
|
-
new_head = (head[0] + 20, head[1])
|
|
58
|
-
|
|
59
|
-
self.snake.append(new_head)
|
|
60
|
-
if self.apple == new_head:
|
|
61
|
-
self.apple = self.set_new_apple()
|
|
62
|
-
self.score += 1
|
|
63
|
-
else:
|
|
64
|
-
self.snake.pop(0)
|
|
65
|
-
|
|
66
|
-
if (new_head[0] < 0 or new_head[0] >= self.width or
|
|
67
|
-
new_head[1] < 0 or new_head[1] >= self.height or
|
|
68
|
-
new_head in self.snake[:-1]):
|
|
69
|
-
break
|
|
70
|
-
|
|
71
|
-
self.display.fill((0, 0, 0))
|
|
72
|
-
for pos in self.snake:
|
|
73
|
-
pygame.draw.rect(self.display, (0, 255, 0), (pos[0], pos[1], 20, 20))
|
|
74
|
-
pygame.draw.rect(self.display, (255, 0, 0), (self.apple[0], self.apple[1], 20, 20))
|
|
75
|
-
text = self.font.render(f'Score: {self.score}', True, (255, 255, 255))
|
|
76
|
-
self.display.blit(text, (10, 10))
|
|
77
|
-
pygame.display.flip()
|
|
78
|
-
clock.tick(10)
|
|
79
|
-
|
|
80
|
-
time.sleep(1)
|
|
81
|
-
self.display.fill((0, 0, 0))
|
|
82
|
-
text = self.font.render('Game Over', True, (255, 255, 255))
|
|
83
|
-
self.display.blit(text, (self.width // 2 - 50, self.height // 2 - 18))
|
|
84
|
-
pygame.display.flip()
|
|
85
|
-
time.sleep(2)
|
|
86
|
-
pygame.quit()
|
|
87
|
-
|
|
88
|
-
if __name__ == '__main__':
|
|
89
|
-
game = SnakeGame()
|
|
90
|
-
game.play()
|
package/style.css
DELETED
package/tic_tac_toe.py
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
import random
|
|
2
|
-
|
|
3
|
-
class TicTacToe:
|
|
4
|
-
def __init__(self):
|
|
5
|
-
self.board = [' ' for _ in range(9)]
|
|
6
|
-
|
|
7
|
-
def print_board(self):
|
|
8
|
-
row1 = '| {} | {} | {} |'.format(self.board[0], self.board[1], self.board[2])
|
|
9
|
-
row2 = '| {} | {} | {} |'.format(self.board[3], self.board[4], self.board[5])
|
|
10
|
-
row3 = '| {} | {} | {} |'.format(self.board[6], self.board[7], self.board[8])
|
|
11
|
-
|
|
12
|
-
print()
|
|
13
|
-
print(row1)
|
|
14
|
-
print(row2)
|
|
15
|
-
print(row3)
|
|
16
|
-
print()
|
|
17
|
-
|
|
18
|
-
def available_moves(self):
|
|
19
|
-
return [i for i, spot in enumerate(self.board) if spot == ' ']
|
|
20
|
-
|
|
21
|
-
def empty_cells(self):
|
|
22
|
-
return ' ' in self.board
|
|
23
|
-
|
|
24
|
-
def num_empty_cells(self):
|
|
25
|
-
return self.board.count(' ')
|
|
26
|
-
|
|
27
|
-
def make_move(self, letter, move):
|
|
28
|
-
self.board[move] = letter
|
|
29
|
-
|
|
30
|
-
def winner(self):
|
|
31
|
-
winning_combos = [(0, 1, 2), (3, 4, 5), (6, 7, 8), (0, 3, 6), (1, 4, 7), (2, 5, 8), (0, 4, 8), (2, 4, 6)]
|
|
32
|
-
for combo in winning_combos:
|
|
33
|
-
if self.board[combo[0]] == self.board[combo[1]] == self.board[combo[2]] != ' ':
|
|
34
|
-
return self.board[combo[0]]
|
|
35
|
-
if ' ' not in self.board:
|
|
36
|
-
return 'Tie'
|
|
37
|
-
return False
|
|
38
|
-
|
|
39
|
-
def minimax(board, depth, is_maximizing):
|
|
40
|
-
result = board.winner()
|
|
41
|
-
if result:
|
|
42
|
-
if result == 'X':
|
|
43
|
-
return -10 + depth
|
|
44
|
-
elif result == 'O':
|
|
45
|
-
return 10 - depth
|
|
46
|
-
elif result == 'Tie':
|
|
47
|
-
return 0
|
|
48
|
-
|
|
49
|
-
if is_maximizing:
|
|
50
|
-
best_score = float('-inf')
|
|
51
|
-
for move in board.available_moves():
|
|
52
|
-
board.make_move('O', move)
|
|
53
|
-
score = minimax(board, depth + 1, False)
|
|
54
|
-
board.board[move] = ' '
|
|
55
|
-
best_score = max(score, best_score)
|
|
56
|
-
return best_score
|
|
57
|
-
else:
|
|
58
|
-
best_score = float('inf')
|
|
59
|
-
for move in board.available_moves():
|
|
60
|
-
board.make_move('X', move)
|
|
61
|
-
score = minimax(board, depth + 1, True)
|
|
62
|
-
board.board[move] = ' '
|
|
63
|
-
best_score = min(score, best_score)
|
|
64
|
-
return best_score
|
|
65
|
-
|
|
66
|
-
def ai_move(board):
|
|
67
|
-
best_score = float('-inf')
|
|
68
|
-
best_move = 0
|
|
69
|
-
for move in board.available_moves():
|
|
70
|
-
board.make_move('O', move)
|
|
71
|
-
score = minimax(board, 0, False)
|
|
72
|
-
board.board[move] = ' '
|
|
73
|
-
if score > best_score:
|
|
74
|
-
best_score = score
|
|
75
|
-
best_move = move
|
|
76
|
-
return best_move
|
|
77
|
-
|
|
78
|
-
def main():
|
|
79
|
-
board = TicTacToe()
|
|
80
|
-
while True:
|
|
81
|
-
board.print_board()
|
|
82
|
-
move = input("Enter your move (1-9): ")
|
|
83
|
-
if board.board[int(move) - 1] != ' ':
|
|
84
|
-
print("Invalid move, try again.")
|
|
85
|
-
continue
|
|
86
|
-
board.make_move('X', int(move) - 1)
|
|
87
|
-
result = board.winner()
|
|
88
|
-
if result:
|
|
89
|
-
board.print_board()
|
|
90
|
-
if result == 'X':
|
|
91
|
-
print("You win!")
|
|
92
|
-
elif result == 'O':
|
|
93
|
-
print("AI wins!")
|
|
94
|
-
else:
|
|
95
|
-
print("It's a tie!")
|
|
96
|
-
break
|
|
97
|
-
move = ai_move(board)
|
|
98
|
-
board.make_move('O', move)
|
|
99
|
-
result = board.winner()
|
|
100
|
-
if result:
|
|
101
|
-
board.print_board()
|
|
102
|
-
if result == 'X':
|
|
103
|
-
print("You win!")
|
|
104
|
-
elif result == 'O':
|
|
105
|
-
print("AI wins!")
|
|
106
|
-
else:
|
|
107
|
-
print("It's a tie!")
|
|
108
|
-
break
|
|
109
|
-
|
|
110
|
-
if __name__ == '__main__':
|
|
111
|
-
main()
|