npm-noxyai 1.0.8 → 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 +81 -27
- 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/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 { spawn } = require('child_process');
|
|
6
|
+
const { spawn } = require('child_process');
|
|
7
7
|
const readline = require('readline');
|
|
8
8
|
|
|
9
9
|
const BASE_URL = 'https://www.noxyai.com';
|
|
@@ -42,7 +42,6 @@ function logout() {
|
|
|
42
42
|
} else {
|
|
43
43
|
console.log('You are already logged out.');
|
|
44
44
|
}
|
|
45
|
-
process.exit(0);
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
function openBrowser(url) {
|
|
@@ -89,11 +88,10 @@ async function login() {
|
|
|
89
88
|
if (data.status === 'success') {
|
|
90
89
|
clearInterval(pollInterval);
|
|
91
90
|
saveToken(data.token);
|
|
92
|
-
|
|
93
91
|
console.clear();
|
|
94
92
|
printLogo();
|
|
95
93
|
console.log('✅ Login successful! Terminal connected.');
|
|
96
|
-
console.log('Type "noxyai
|
|
94
|
+
console.log('Type "noxyai" to start interactive mode.\n');
|
|
97
95
|
process.exit(0);
|
|
98
96
|
} else if (data.error) {
|
|
99
97
|
clearInterval(pollInterval);
|
|
@@ -108,37 +106,55 @@ async function login() {
|
|
|
108
106
|
}
|
|
109
107
|
}
|
|
110
108
|
|
|
111
|
-
// 🚨 THE FIX: Upgraded to spawn with interactive stdio
|
|
112
109
|
function runTerminalCommand(cmd) {
|
|
113
110
|
return new Promise((resolve, reject) => {
|
|
114
111
|
console.log(`\n\x1b[33m⚡ Running:\x1b[0m ${cmd}\n`);
|
|
115
112
|
|
|
113
|
+
const isServer = cmd.includes('dev') || cmd.includes('serve') || cmd.includes('host') || cmd.includes('start');
|
|
114
|
+
|
|
116
115
|
const child = spawn(cmd, {
|
|
117
116
|
shell: true,
|
|
118
|
-
stdio: 'inherit'
|
|
117
|
+
stdio: 'inherit'
|
|
119
118
|
});
|
|
120
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
|
+
|
|
121
125
|
child.on('close', (code) => {
|
|
122
|
-
if (
|
|
123
|
-
|
|
126
|
+
if (!isServer) {
|
|
127
|
+
if (code !== 0) reject(`Command failed with exit code ${code}`);
|
|
128
|
+
else resolve();
|
|
129
|
+
}
|
|
124
130
|
});
|
|
125
131
|
|
|
126
132
|
child.on('error', (error) => reject(error.message));
|
|
127
133
|
});
|
|
128
134
|
}
|
|
129
135
|
|
|
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
|
+
|
|
130
145
|
async function chat(prompt, depth = 0) {
|
|
131
146
|
const token = getToken();
|
|
132
147
|
if (!token) {
|
|
133
148
|
console.error('❌ Unauthorized: You must log in first. Run "noxyai login"');
|
|
134
|
-
|
|
149
|
+
return;
|
|
135
150
|
}
|
|
136
151
|
|
|
137
152
|
if (depth > 3) {
|
|
138
153
|
console.error('\n❌ Agent reached maximum retry depth. Please fix the error manually.');
|
|
139
|
-
|
|
154
|
+
return;
|
|
140
155
|
}
|
|
141
156
|
|
|
157
|
+
const enhancedPrompt = depth === 0 ? getLocalContext() + prompt : prompt;
|
|
142
158
|
const model = 'auto';
|
|
143
159
|
|
|
144
160
|
try {
|
|
@@ -148,13 +164,13 @@ async function chat(prompt, depth = 0) {
|
|
|
148
164
|
'Content-Type': 'application/json',
|
|
149
165
|
'Authorization': 'Bearer ' + token
|
|
150
166
|
},
|
|
151
|
-
body: JSON.stringify({ prompt, model })
|
|
167
|
+
body: JSON.stringify({ prompt: enhancedPrompt, model })
|
|
152
168
|
});
|
|
153
169
|
|
|
154
170
|
if (!res.ok) {
|
|
155
171
|
const errorText = await res.text();
|
|
156
172
|
console.error('\n❌ API Error: ' + errorText);
|
|
157
|
-
|
|
173
|
+
return;
|
|
158
174
|
}
|
|
159
175
|
|
|
160
176
|
if (depth === 0) console.log('\n🤖 \x1b[36mNoxyAI\x1b[0m is thinking...\n');
|
|
@@ -186,7 +202,10 @@ async function chat(prompt, depth = 0) {
|
|
|
186
202
|
}
|
|
187
203
|
}
|
|
188
204
|
|
|
189
|
-
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;
|
|
190
209
|
|
|
191
210
|
const fileRegex = /<file path="([^"]+)">([\s\S]*?)<\/file>/g;
|
|
192
211
|
let match;
|
|
@@ -197,7 +216,8 @@ async function chat(prompt, depth = 0) {
|
|
|
197
216
|
const dir = path.dirname(filePath);
|
|
198
217
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
199
218
|
fs.writeFileSync(filePath, content.trim());
|
|
200
|
-
console.log(`\x1b[32m✔ Created file:\x1b[0m ${filePath}`);
|
|
219
|
+
console.log(`\x1b[32m✔ Created/Modified file:\x1b[0m ${filePath}`);
|
|
220
|
+
filesCreated++;
|
|
201
221
|
}
|
|
202
222
|
|
|
203
223
|
const execRegex = /<execute>([\s\S]*?)<\/execute>/g;
|
|
@@ -207,6 +227,7 @@ async function chat(prompt, depth = 0) {
|
|
|
207
227
|
}
|
|
208
228
|
|
|
209
229
|
for (const cmd of commands) {
|
|
230
|
+
commandsRun++;
|
|
210
231
|
try {
|
|
211
232
|
await runTerminalCommand(cmd);
|
|
212
233
|
} catch (err) {
|
|
@@ -217,11 +238,16 @@ async function chat(prompt, depth = 0) {
|
|
|
217
238
|
return;
|
|
218
239
|
}
|
|
219
240
|
}
|
|
220
|
-
|
|
241
|
+
|
|
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');
|
|
221
248
|
|
|
222
249
|
} catch (error) {
|
|
223
250
|
console.error('\n❌ Connection error: ' + error.message);
|
|
224
|
-
process.exit(1);
|
|
225
251
|
}
|
|
226
252
|
}
|
|
227
253
|
|
|
@@ -230,29 +256,57 @@ function showHelp() {
|
|
|
230
256
|
console.log(`
|
|
231
257
|
\x1b[33mNoxyAI Agent CLI - Available Commands\x1b[0m
|
|
232
258
|
|
|
259
|
+
\x1b[32mnoxyai\x1b[0m Start Interactive Mode (Recommended)
|
|
233
260
|
\x1b[32mnoxyai login\x1b[0m Authenticate your terminal
|
|
234
261
|
\x1b[32mnoxyai logout\x1b[0m Remove authentication from this device
|
|
235
|
-
\x1b[32mnoxyai chat "<prompt>"\x1b[0m
|
|
262
|
+
\x1b[32mnoxyai chat "<prompt>"\x1b[0m Run a single prompt and exit
|
|
236
263
|
\x1b[32mnoxyai help\x1b[0m Show this menu
|
|
237
|
-
|
|
238
|
-
\x1b[33mExamples:\x1b[0m
|
|
239
|
-
noxyai chat "Create a snake game in python and run it"
|
|
240
|
-
noxyai chat "Initialize a react app called my-app"
|
|
241
|
-
noxyai chat "Create an express server that returns hello world on port 3000"
|
|
242
264
|
`);
|
|
243
265
|
}
|
|
244
266
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
+
|
|
289
|
+
// --- ROUTER ---
|
|
290
|
+
if (command === 'login') {
|
|
291
|
+
login();
|
|
292
|
+
} else if (command === 'logout') {
|
|
293
|
+
logout();
|
|
294
|
+
process.exit(0);
|
|
295
|
+
} else if (command === 'help' || command === '--help') { // 🚨 BUG FIXED HERE!
|
|
296
|
+
showHelp();
|
|
297
|
+
process.exit(0);
|
|
298
|
+
} else if (command === 'chat') {
|
|
249
299
|
const prompt = args.slice(1).join(' ');
|
|
250
300
|
if (!prompt) {
|
|
251
301
|
console.error('❌ Please provide a prompt. Example: noxyai chat "Create a python script"');
|
|
252
302
|
process.exit(1);
|
|
253
303
|
}
|
|
254
|
-
chat(prompt);
|
|
304
|
+
chat(prompt).then(() => process.exit(0));
|
|
305
|
+
} else if (!command) {
|
|
306
|
+
// Now this actually triggers!
|
|
307
|
+
startInteractiveMode();
|
|
255
308
|
} else {
|
|
256
309
|
console.error(`❌ Unknown command: ${command}`);
|
|
257
310
|
console.log('Run "noxyai help" for a list of commands.');
|
|
311
|
+
process.exit(1);
|
|
258
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()
|