npm-noxyai 1.0.7 → 1.0.9
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 +88 -50
- package/index.html +13 -0
- package/package.json +11 -2
- package/reinstall_pip.py +1 -0
- package/script.js +92 -0
- package/server.js +11 -0
- package/snake_game.py +90 -0
- package/style.css +11 -0
- package/tic_tac_toe.py +111 -0
package/index-noxyai.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const os = require('os');
|
|
5
5
|
const path = require('path');
|
|
6
|
-
const {
|
|
6
|
+
const { spawn } = require('child_process');
|
|
7
7
|
const readline = require('readline');
|
|
8
8
|
|
|
9
9
|
const BASE_URL = 'https://www.noxyai.com';
|
|
@@ -12,7 +12,6 @@ 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
|
-
// --- HELPER: ASCII ART ---
|
|
16
15
|
function printLogo() {
|
|
17
16
|
console.log('\x1b[36m' + `
|
|
18
17
|
███╗ ██╗ ██████╗ ██╗ ██╗██╗ ██╗ █████╗ ██╗
|
|
@@ -24,7 +23,6 @@ function printLogo() {
|
|
|
24
23
|
` + '\x1b[0m');
|
|
25
24
|
}
|
|
26
25
|
|
|
27
|
-
// --- HELPER: AUTHENTICATION ---
|
|
28
26
|
function saveToken(token) {
|
|
29
27
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify({ token }));
|
|
30
28
|
}
|
|
@@ -44,24 +42,21 @@ function logout() {
|
|
|
44
42
|
} else {
|
|
45
43
|
console.log('You are already logged out.');
|
|
46
44
|
}
|
|
47
|
-
process.exit(0);
|
|
48
45
|
}
|
|
49
46
|
|
|
50
|
-
// --- HELPER: AUTO-OPEN URL ---
|
|
51
47
|
function openBrowser(url) {
|
|
52
48
|
const platform = os.platform();
|
|
53
49
|
if (platform === 'android') {
|
|
54
|
-
|
|
50
|
+
spawn('termux-open-url', [url], { stdio: 'ignore' });
|
|
55
51
|
} else if (platform === 'darwin') {
|
|
56
|
-
|
|
52
|
+
spawn('open', [url], { stdio: 'ignore' });
|
|
57
53
|
} else if (platform === 'win32') {
|
|
58
|
-
|
|
54
|
+
spawn('cmd.exe', ['/c', 'start', '""', url], { stdio: 'ignore' });
|
|
59
55
|
} else {
|
|
60
|
-
|
|
56
|
+
spawn('xdg-open', [url], { stdio: 'ignore' });
|
|
61
57
|
}
|
|
62
58
|
}
|
|
63
59
|
|
|
64
|
-
// --- COMMAND: LOGIN ---
|
|
65
60
|
async function login() {
|
|
66
61
|
printLogo();
|
|
67
62
|
console.log('Initializing secure login...\n');
|
|
@@ -93,11 +88,10 @@ async function login() {
|
|
|
93
88
|
if (data.status === 'success') {
|
|
94
89
|
clearInterval(pollInterval);
|
|
95
90
|
saveToken(data.token);
|
|
96
|
-
|
|
97
91
|
console.clear();
|
|
98
92
|
printLogo();
|
|
99
93
|
console.log('✅ Login successful! Terminal connected.');
|
|
100
|
-
console.log('Type "noxyai
|
|
94
|
+
console.log('Type "noxyai" to start interactive mode.\n');
|
|
101
95
|
process.exit(0);
|
|
102
96
|
} else if (data.error) {
|
|
103
97
|
clearInterval(pollInterval);
|
|
@@ -112,37 +106,55 @@ async function login() {
|
|
|
112
106
|
}
|
|
113
107
|
}
|
|
114
108
|
|
|
115
|
-
// --- HELPER: EXECUTE COMMANDS & ERROR HANDLING ---
|
|
116
109
|
function runTerminalCommand(cmd) {
|
|
117
110
|
return new Promise((resolve, reject) => {
|
|
118
|
-
console.log(`\n\x1b[33m⚡ Running:\x1b[0m ${cmd}`);
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
111
|
+
console.log(`\n\x1b[33m⚡ Running:\x1b[0m ${cmd}\n`);
|
|
112
|
+
|
|
113
|
+
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
|
+
});
|
|
119
|
+
|
|
120
|
+
if (isServer) {
|
|
121
|
+
console.log(`\x1b[36m[i] Server detected. It will run in the foreground. Press Ctrl+C to stop it.\x1b[0m`);
|
|
122
|
+
setTimeout(() => resolve(), 2500);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
child.on('close', (code) => {
|
|
126
|
+
if (!isServer) {
|
|
127
|
+
if (code !== 0) reject(`Command failed with exit code ${code}`);
|
|
128
|
+
else resolve();
|
|
123
129
|
}
|
|
124
|
-
resolve(stdout);
|
|
125
130
|
});
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
child.stdout.pipe(process.stdout);
|
|
129
|
-
child.stderr.pipe(process.stderr);
|
|
131
|
+
|
|
132
|
+
child.on('error', (error) => reject(error.message));
|
|
130
133
|
});
|
|
131
134
|
}
|
|
132
135
|
|
|
133
|
-
|
|
136
|
+
function getLocalContext() {
|
|
137
|
+
try {
|
|
138
|
+
const files = fs.readdirSync(process.cwd()).filter(f => !f.startsWith('node_modules') && !f.startsWith('.git')).slice(0, 20);
|
|
139
|
+
return `\n[SYSTEM CONTEXT: You are running inside the user's terminal. Current directory: ${process.cwd()}. Existing files here: ${files.join(', ')}. You can modify existing files or create new ones.]\n`;
|
|
140
|
+
} catch (e) {
|
|
141
|
+
return "";
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
134
145
|
async function chat(prompt, depth = 0) {
|
|
135
146
|
const token = getToken();
|
|
136
147
|
if (!token) {
|
|
137
148
|
console.error('❌ Unauthorized: You must log in first. Run "noxyai login"');
|
|
138
|
-
|
|
149
|
+
return;
|
|
139
150
|
}
|
|
140
151
|
|
|
141
152
|
if (depth > 3) {
|
|
142
153
|
console.error('\n❌ Agent reached maximum retry depth. Please fix the error manually.');
|
|
143
|
-
|
|
154
|
+
return;
|
|
144
155
|
}
|
|
145
156
|
|
|
157
|
+
const enhancedPrompt = depth === 0 ? getLocalContext() + prompt : prompt;
|
|
146
158
|
const model = 'auto';
|
|
147
159
|
|
|
148
160
|
try {
|
|
@@ -152,13 +164,13 @@ async function chat(prompt, depth = 0) {
|
|
|
152
164
|
'Content-Type': 'application/json',
|
|
153
165
|
'Authorization': 'Bearer ' + token
|
|
154
166
|
},
|
|
155
|
-
body: JSON.stringify({ prompt, model })
|
|
167
|
+
body: JSON.stringify({ prompt: enhancedPrompt, model })
|
|
156
168
|
});
|
|
157
169
|
|
|
158
170
|
if (!res.ok) {
|
|
159
171
|
const errorText = await res.text();
|
|
160
172
|
console.error('\n❌ API Error: ' + errorText);
|
|
161
|
-
|
|
173
|
+
return;
|
|
162
174
|
}
|
|
163
175
|
|
|
164
176
|
if (depth === 0) console.log('\n🤖 \x1b[36mNoxyAI\x1b[0m is thinking...\n');
|
|
@@ -190,9 +202,11 @@ async function chat(prompt, depth = 0) {
|
|
|
190
202
|
}
|
|
191
203
|
}
|
|
192
204
|
|
|
193
|
-
console.log('\n\n\x1b[32m[Agent]
|
|
205
|
+
console.log('\n\n\x1b[32m[Agent] Processing actions...\x1b[0m');
|
|
206
|
+
|
|
207
|
+
let filesCreated = 0;
|
|
208
|
+
let commandsRun = 0;
|
|
194
209
|
|
|
195
|
-
// 1. EXTRACT AND CREATE FILES
|
|
196
210
|
const fileRegex = /<file path="([^"]+)">([\s\S]*?)<\/file>/g;
|
|
197
211
|
let match;
|
|
198
212
|
while ((match = fileRegex.exec(fullResponse)) !== null) {
|
|
@@ -200,14 +214,12 @@ async function chat(prompt, depth = 0) {
|
|
|
200
214
|
const content = match[2];
|
|
201
215
|
|
|
202
216
|
const dir = path.dirname(filePath);
|
|
203
|
-
if (!fs.existsSync(dir)) {
|
|
204
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
205
|
-
}
|
|
217
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
206
218
|
fs.writeFileSync(filePath, content.trim());
|
|
207
|
-
console.log(`\x1b[32m✔ Created file:\x1b[0m ${filePath}`);
|
|
219
|
+
console.log(`\x1b[32m✔ Created/Modified file:\x1b[0m ${filePath}`);
|
|
220
|
+
filesCreated++;
|
|
208
221
|
}
|
|
209
222
|
|
|
210
|
-
// 2. EXTRACT AND RUN COMMANDS
|
|
211
223
|
const execRegex = /<execute>([\s\S]*?)<\/execute>/g;
|
|
212
224
|
const commands = [];
|
|
213
225
|
while ((match = execRegex.exec(fullResponse)) !== null) {
|
|
@@ -215,60 +227,86 @@ async function chat(prompt, depth = 0) {
|
|
|
215
227
|
}
|
|
216
228
|
|
|
217
229
|
for (const cmd of commands) {
|
|
230
|
+
commandsRun++;
|
|
218
231
|
try {
|
|
219
232
|
await runTerminalCommand(cmd);
|
|
220
233
|
} catch (err) {
|
|
221
|
-
console.error(`\n\x1b[31m❌ Command Failed:\x1b[0m ${err
|
|
234
|
+
console.error(`\n\x1b[31m❌ Command Failed:\x1b[0m ${err}`);
|
|
222
235
|
console.log(`\x1b[33m🔄 Triggering Auto-Heal Loop...\x1b[0m`);
|
|
223
|
-
|
|
224
|
-
const errorPrompt = `I ran the command "${cmd}" and got this error:\n\n${err}\n\nPlease fix the issue. If you need to rewrite a file, use the <file> tags. If you need to install a missing library, use the <execute> tag.`;
|
|
225
|
-
|
|
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.`;
|
|
226
237
|
await chat(errorPrompt, depth + 1);
|
|
227
238
|
return;
|
|
228
239
|
}
|
|
229
240
|
}
|
|
230
241
|
|
|
231
|
-
|
|
242
|
+
console.log('\n\x1b[32m════════════════════════════════════════\x1b[0m');
|
|
243
|
+
console.log(`\x1b[32m✨ Task Completed Successfully!\x1b[0m`);
|
|
244
|
+
if (filesCreated > 0) console.log(`📄 Modified ${filesCreated} file(s)`);
|
|
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');
|
|
232
248
|
|
|
233
249
|
} catch (error) {
|
|
234
250
|
console.error('\n❌ Connection error: ' + error.message);
|
|
235
|
-
process.exit(1);
|
|
236
251
|
}
|
|
237
252
|
}
|
|
238
253
|
|
|
239
|
-
// --- COMMAND: HELP ---
|
|
240
254
|
function showHelp() {
|
|
241
255
|
printLogo();
|
|
242
256
|
console.log(`
|
|
243
257
|
\x1b[33mNoxyAI Agent CLI - Available Commands\x1b[0m
|
|
244
258
|
|
|
259
|
+
\x1b[32mnoxyai\x1b[0m Start Interactive Mode (Recommended)
|
|
245
260
|
\x1b[32mnoxyai login\x1b[0m Authenticate your terminal
|
|
246
261
|
\x1b[32mnoxyai logout\x1b[0m Remove authentication from this device
|
|
247
|
-
\x1b[32mnoxyai chat "<prompt>"\x1b[0m
|
|
262
|
+
\x1b[32mnoxyai chat "<prompt>"\x1b[0m Run a single prompt and exit
|
|
248
263
|
\x1b[32mnoxyai help\x1b[0m Show this menu
|
|
249
|
-
|
|
250
|
-
\x1b[33mExamples:\x1b[0m
|
|
251
|
-
noxyai chat "Create a snake game in python and run it"
|
|
252
|
-
noxyai chat "Initialize a react app called my-app"
|
|
253
|
-
noxyai chat "Create an express server that returns hello world on port 3000"
|
|
254
264
|
`);
|
|
255
265
|
}
|
|
256
266
|
|
|
267
|
+
function startInteractiveMode() {
|
|
268
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
269
|
+
printLogo();
|
|
270
|
+
console.log(`\x1b[33mWelcome to NoxyAI Interactive Mode!\x1b[0m`);
|
|
271
|
+
console.log(`Type your prompt and press Enter. Type 'exit' to quit.\n`);
|
|
272
|
+
|
|
273
|
+
function ask() {
|
|
274
|
+
rl.question('\x1b[36mNoxyAI > \x1b[0m', async (input) => {
|
|
275
|
+
const trimmed = input.trim();
|
|
276
|
+
if (trimmed.toLowerCase() === 'exit' || trimmed.toLowerCase() === 'quit') {
|
|
277
|
+
console.log('Goodbye!');
|
|
278
|
+
process.exit(0);
|
|
279
|
+
}
|
|
280
|
+
if (trimmed) {
|
|
281
|
+
await chat(trimmed);
|
|
282
|
+
}
|
|
283
|
+
ask();
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
ask();
|
|
287
|
+
}
|
|
288
|
+
|
|
257
289
|
// --- ROUTER ---
|
|
258
290
|
if (command === 'login') {
|
|
259
291
|
login();
|
|
260
292
|
} else if (command === 'logout') {
|
|
261
293
|
logout();
|
|
262
|
-
|
|
294
|
+
process.exit(0);
|
|
295
|
+
} else if (command === 'help' || command === '--help') { // 🚨 BUG FIXED HERE!
|
|
263
296
|
showHelp();
|
|
297
|
+
process.exit(0);
|
|
264
298
|
} else if (command === 'chat') {
|
|
265
299
|
const prompt = args.slice(1).join(' ');
|
|
266
300
|
if (!prompt) {
|
|
267
301
|
console.error('❌ Please provide a prompt. Example: noxyai chat "Create a python script"');
|
|
268
302
|
process.exit(1);
|
|
269
303
|
}
|
|
270
|
-
chat(prompt);
|
|
304
|
+
chat(prompt).then(() => process.exit(0));
|
|
305
|
+
} else if (!command) {
|
|
306
|
+
// Now this actually triggers!
|
|
307
|
+
startInteractiveMode();
|
|
271
308
|
} else {
|
|
272
309
|
console.error(`❌ Unknown command: ${command}`);
|
|
273
310
|
console.log('Run "noxyai help" for a list of commands.');
|
|
311
|
+
process.exit(1);
|
|
274
312
|
}
|
package/index.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
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/package.json
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "npm-noxyai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "CLI for NoxyAI",
|
|
5
5
|
"main": "index-noxyai.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"noxyai": "index-noxyai.js"
|
|
8
8
|
},
|
|
9
9
|
"author": "Mohammad Junaid Rather",
|
|
10
|
-
"license": "ISC"
|
|
10
|
+
"license": "ISC",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
13
|
+
"start": "node server.js"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [],
|
|
16
|
+
"type": "commonjs",
|
|
17
|
+
"dependencies": {
|
|
18
|
+
"express": "^5.2.1"
|
|
19
|
+
}
|
|
11
20
|
}
|
package/reinstall_pip.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import pip; pip.uninstall('pip'); import os; os.system('python -m ensurepip')
|
package/script.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
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
ADDED
package/snake_game.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
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
ADDED
package/tic_tac_toe.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
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()
|