npm-noxyai 1.0.9 → 1.0.10

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 CHANGED
@@ -9,9 +9,25 @@ const readline = require('readline');
9
9
  const BASE_URL = 'https://www.noxyai.com';
10
10
  const CONFIG_FILE = path.join(os.homedir(), '.noxyai.json');
11
11
 
12
+ const GOOGLE_KEY = "AIzaSyAXoZgwIaEnXfO3JuKIvR8GzhydqRKPh20";
13
+ const GOOGLE_CX = "33d4204810fae4852";
14
+
12
15
  const args = process.argv.slice(2);
13
16
  const command = args[0];
14
17
 
18
+ // --- CONFIG MANAGEMENT ---
19
+ function loadConfig() {
20
+ if (fs.existsSync(CONFIG_FILE)) {
21
+ try { return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); } catch(e){}
22
+ }
23
+ return { token: null, model: 'auto' };
24
+ }
25
+
26
+ function saveConfig(config) {
27
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
28
+ }
29
+
30
+ // --- UI HELPERS ---
15
31
  function printLogo() {
16
32
  console.log('\x1b[36m' + `
17
33
  ███╗ ██╗ ██████╗ ██╗ ██╗██╗ ██╗ █████╗ ██╗
@@ -23,40 +39,38 @@ function printLogo() {
23
39
  ` + '\x1b[0m');
24
40
  }
25
41
 
26
- function saveToken(token) {
27
- fs.writeFileSync(CONFIG_FILE, JSON.stringify({ token }));
42
+ let spinnerInterval;
43
+ function startSpinner(text) {
44
+ const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
45
+ let i = 0;
46
+ process.stdout.write('\x1b[?25l'); // Hide cursor
47
+ spinnerInterval = setInterval(() => {
48
+ process.stdout.write(`\r\x1b[36m${frames[i]} ${text}\x1b[0m`);
49
+ i = (i + 1) % frames.length;
50
+ }, 80);
28
51
  }
29
52
 
30
- function getToken() {
31
- if (fs.existsSync(CONFIG_FILE)) {
32
- const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
33
- return config.token;
34
- }
35
- return null;
53
+ function stopSpinner() {
54
+ clearInterval(spinnerInterval);
55
+ process.stdout.write('\r\x1b[K\x1b[?25h'); // Clear line, show cursor
36
56
  }
37
57
 
38
58
  function logout() {
39
- if (fs.existsSync(CONFIG_FILE)) {
40
- fs.unlinkSync(CONFIG_FILE);
41
- console.log('✅ Successfully logged out.');
42
- } else {
43
- console.log('You are already logged out.');
44
- }
59
+ const config = loadConfig();
60
+ config.token = null;
61
+ saveConfig(config);
62
+ console.log('✅ Successfully logged out.');
45
63
  }
46
64
 
47
65
  function openBrowser(url) {
48
66
  const platform = os.platform();
49
- if (platform === 'android') {
50
- spawn('termux-open-url', [url], { stdio: 'ignore' });
51
- } else if (platform === 'darwin') {
52
- spawn('open', [url], { stdio: 'ignore' });
53
- } else if (platform === 'win32') {
54
- spawn('cmd.exe', ['/c', 'start', '""', url], { stdio: 'ignore' });
55
- } else {
56
- spawn('xdg-open', [url], { stdio: 'ignore' });
57
- }
67
+ if (platform === 'android') spawn('termux-open-url', [url], { stdio: 'ignore' });
68
+ else if (platform === 'darwin') spawn('open', [url], { stdio: 'ignore' });
69
+ else if (platform === 'win32') spawn('cmd.exe', ['/c', 'start', '""', url], { stdio: 'ignore' });
70
+ else spawn('xdg-open', [url], { stdio: 'ignore' });
58
71
  }
59
72
 
73
+ // --- CORE FUNCTIONS ---
60
74
  async function login() {
61
75
  printLogo();
62
76
  console.log('Initializing secure login...\n');
@@ -72,7 +86,7 @@ async function login() {
72
86
  rl.question('Press ENTER to automatically open the browser...', () => {
73
87
  openBrowser(verificationUrl);
74
88
  rl.close();
75
- console.log('\n⏳ Waiting for authentication...');
89
+ startSpinner('Waiting for authentication...');
76
90
  });
77
91
 
78
92
  const pollInterval = setInterval(async () => {
@@ -87,7 +101,11 @@ async function login() {
87
101
 
88
102
  if (data.status === 'success') {
89
103
  clearInterval(pollInterval);
90
- saveToken(data.token);
104
+ stopSpinner();
105
+ const config = loadConfig();
106
+ config.token = data.token;
107
+ saveConfig(config);
108
+
91
109
  console.clear();
92
110
  printLogo();
93
111
  console.log('✅ Login successful! Terminal connected.');
@@ -95,6 +113,7 @@ async function login() {
95
113
  process.exit(0);
96
114
  } else if (data.error) {
97
115
  clearInterval(pollInterval);
116
+ stopSpinner();
98
117
  console.error('\n❌ Login failed: ' + data.error);
99
118
  process.exit(1);
100
119
  }
@@ -109,16 +128,11 @@ async function login() {
109
128
  function runTerminalCommand(cmd) {
110
129
  return new Promise((resolve, reject) => {
111
130
  console.log(`\n\x1b[33m⚡ Running:\x1b[0m ${cmd}\n`);
112
-
113
131
  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
- });
132
+ const child = spawn(cmd, { shell: true, stdio: 'inherit' });
119
133
 
120
134
  if (isServer) {
121
- console.log(`\x1b[36m[i] Server detected. It will run in the foreground. Press Ctrl+C to stop it.\x1b[0m`);
135
+ console.log(`\x1b[36m[i] Server detected. Running in foreground. Press Ctrl+C to stop.\x1b[0m`);
122
136
  setTimeout(() => resolve(), 2500);
123
137
  }
124
138
 
@@ -128,53 +142,38 @@ function runTerminalCommand(cmd) {
128
142
  else resolve();
129
143
  }
130
144
  });
131
-
132
145
  child.on('error', (error) => reject(error.message));
133
146
  });
134
147
  }
135
148
 
136
149
  function getLocalContext() {
137
150
  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
- }
151
+ const files = fs.readdirSync(process.cwd()).filter(f => !f.startsWith('node_modules') && !f.startsWith('.git')).slice(0, 30);
152
+ return `\n[SYSTEM: You are in ${process.cwd()}. Files here: ${files.join(', ')}]\n`;
153
+ } catch (e) { return ""; }
143
154
  }
144
155
 
145
156
  async function chat(prompt, depth = 0) {
146
- const token = getToken();
147
- if (!token) {
148
- console.error('❌ Unauthorized: You must log in first. Run "noxyai login"');
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
- }
157
+ const config = loadConfig();
158
+ if (!config.token) { console.error('❌ Unauthorized: Run "noxyai login"'); return; }
159
+ if (depth > 6) { console.error('\nReached max reasoning depth. Stopping.'); return; }
156
160
 
157
161
  const enhancedPrompt = depth === 0 ? getLocalContext() + prompt : prompt;
158
- const model = 'auto';
162
+
163
+ startSpinner(depth === 0 ? 'NoxyAI is thinking...' : 'NoxyAI is analyzing results...');
159
164
 
160
165
  try {
161
166
  const res = await fetch(BASE_URL + '/api/io', {
162
167
  method: 'POST',
163
- headers: {
164
- 'Content-Type': 'application/json',
165
- 'Authorization': 'Bearer ' + token
166
- },
167
- body: JSON.stringify({ prompt: enhancedPrompt, model })
168
+ headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + config.token },
169
+ body: JSON.stringify({ prompt: enhancedPrompt, model: config.model })
168
170
  });
169
171
 
170
- if (!res.ok) {
171
- const errorText = await res.text();
172
- console.error('\n❌ API Error: ' + errorText);
173
- return;
174
- }
172
+ stopSpinner();
173
+
174
+ if (!res.ok) return console.error('\n❌ API Error: ' + await res.text());
175
175
 
176
- if (depth === 0) console.log('\n🤖 \x1b[36mNoxyAI\x1b[0m is thinking...\n');
177
- else console.log('\n🤖 \x1b[36mNoxyAI\x1b[0m is analyzing the error and rewriting...\n');
176
+ console.log(`\n🤖 \x1b[36mNoxyAI (${config.model}):\x1b[0m\n`);
178
177
 
179
178
  const reader = res.body.getReader();
180
179
  const decoder = new TextDecoder('utf-8');
@@ -183,10 +182,7 @@ async function chat(prompt, depth = 0) {
183
182
  while (true) {
184
183
  const { done, value } = await reader.read();
185
184
  if (done) break;
186
-
187
- const chunk = decoder.decode(value, { stream: true });
188
- const lines = chunk.split('\n');
189
-
185
+ const lines = decoder.decode(value, { stream: true }).split('\n');
190
186
  for (const line of lines) {
191
187
  if (line.startsWith('data: ')) {
192
188
  const data = line.slice(6).trim();
@@ -203,110 +199,136 @@ async function chat(prompt, depth = 0) {
203
199
  }
204
200
 
205
201
  console.log('\n\n\x1b[32m[Agent] Processing actions...\x1b[0m');
202
+ let agentFeedback = "";
206
203
 
207
- let filesCreated = 0;
208
- let commandsRun = 0;
204
+ // Tool: Read Files
205
+ const readRegex = /<read>([\s\S]*?)<\/read>/g;
206
+ let match;
207
+ while ((match = readRegex.exec(fullResponse)) !== null) {
208
+ const filePath = match[1].trim();
209
+ try {
210
+ const content = fs.readFileSync(filePath, 'utf8');
211
+ console.log(`\x1b[34m📖 Read file:\x1b[0m ${filePath}`);
212
+ agentFeedback += `\n[FILE: ${filePath}]\n\`\`\`\n${content}\n\`\`\`\n`;
213
+ } catch (err) {
214
+ console.log(`\x1b[31m❌ Failed to read:\x1b[0m ${filePath}`);
215
+ agentFeedback += `\n[ERROR reading ${filePath}: ${err.message}]\n`;
216
+ }
217
+ }
209
218
 
219
+ // Tool: Search Web
220
+ const searchRegex = /<search>([\s\S]*?)<\/search>/g;
221
+ while ((match = searchRegex.exec(fullResponse)) !== null) {
222
+ const query = match[1].trim();
223
+ console.log(`\x1b[35m🔍 Searching Web:\x1b[0m ${query}`);
224
+ try {
225
+ const searchRes = await fetch(`https://www.googleapis.com/customsearch/v1?key=${GOOGLE_KEY}&cx=${GOOGLE_CX}&q=${encodeURIComponent(query)}`);
226
+ const searchData = await searchRes.json();
227
+ const snippets = searchData.items ? searchData.items.map(i => `- ${i.title}: ${i.snippet}`).join('\n') : "No results.";
228
+ agentFeedback += `\n[SEARCH RESULTS FOR "${query}"]\n${snippets}\n`;
229
+ } catch (err) {
230
+ agentFeedback += `\n[SEARCH FAILED: ${err.message}]\n`;
231
+ }
232
+ }
233
+
234
+ // Tool: Write Files
210
235
  const fileRegex = /<file path="([^"]+)">([\s\S]*?)<\/file>/g;
211
- let match;
212
236
  while ((match = fileRegex.exec(fullResponse)) !== null) {
213
- const filePath = match[1];
214
- const content = match[2];
215
-
237
+ const filePath = match[1], content = match[2];
216
238
  const dir = path.dirname(filePath);
217
239
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
218
240
  fs.writeFileSync(filePath, content.trim());
219
- console.log(`\x1b[32m✔ Created/Modified file:\x1b[0m ${filePath}`);
220
- filesCreated++;
241
+ console.log(`\x1b[32m✔ Wrote file:\x1b[0m ${filePath}`);
221
242
  }
222
243
 
244
+ // Tool: Execute Commands
223
245
  const execRegex = /<execute>([\s\S]*?)<\/execute>/g;
224
246
  const commands = [];
225
- while ((match = execRegex.exec(fullResponse)) !== null) {
226
- commands.push(match[1].trim());
227
- }
228
-
247
+ while ((match = execRegex.exec(fullResponse)) !== null) commands.push(match[1].trim());
248
+
229
249
  for (const cmd of commands) {
230
- commandsRun++;
231
250
  try {
232
251
  await runTerminalCommand(cmd);
233
252
  } catch (err) {
234
- console.error(`\n\x1b[31m❌ Command Failed:\x1b[0m ${err}`);
235
- console.log(`\x1b[33m🔄 Triggering Auto-Heal Loop...\x1b[0m`);
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;
253
+ console.log(`\x1b[31m❌ Command Failed:\x1b[0m ${err}`);
254
+ agentFeedback += `\n[COMMAND ERROR running "${cmd}": ${err}]\n`;
239
255
  }
240
256
  }
241
257
 
258
+ if (agentFeedback) {
259
+ console.log(`\x1b[33m🔄 Sending data back to Agent...\x1b[0m`);
260
+ 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);
261
+ return;
262
+ }
263
+
242
264
  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`);
265
+ console.log(`\x1b[32m✨ Task Completed!\x1b[0m`);
247
266
  console.log('\x1b[32m════════════════════════════════════════\x1b[0m\n');
248
267
 
249
- } catch (error) {
250
- console.error('\n❌ Connection error: ' + error.message);
268
+ } catch (error) {
269
+ stopSpinner();
270
+ console.error('\n❌ Connection error: ' + error.message);
251
271
  }
252
272
  }
253
273
 
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
274
  function startInteractiveMode() {
275
+ const config = loadConfig();
268
276
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
269
277
  printLogo();
270
278
  console.log(`\x1b[33mWelcome to NoxyAI Interactive Mode!\x1b[0m`);
271
- console.log(`Type your prompt and press Enter. Type 'exit' to quit.\n`);
279
+ console.log(`Current Model: \x1b[32m${config.model}\x1b[0m`);
280
+ console.log(`Commands: \x1b[36m/model\x1b[0m (Change Model), \x1b[36m/clear\x1b[0m, \x1b[36m/exit\x1b[0m\n`);
272
281
 
273
282
  function ask() {
274
283
  rl.question('\x1b[36mNoxyAI > \x1b[0m', async (input) => {
275
284
  const trimmed = input.trim();
276
- if (trimmed.toLowerCase() === 'exit' || trimmed.toLowerCase() === 'quit') {
277
- console.log('Goodbye!');
285
+ const lower = trimmed.toLowerCase();
286
+
287
+ if (lower === 'exit' || lower === '/exit') {
278
288
  process.exit(0);
279
- }
280
- if (trimmed) {
289
+ }
290
+ else if (lower === '/clear') {
291
+ console.clear();
292
+ printLogo();
293
+ ask();
294
+ }
295
+ else if (lower === '/model') {
296
+ console.log('\n\x1b[33mSelect an AI Model:\x1b[0m');
297
+ console.log(' \x1b[36m1)\x1b[0m Auto (Llama 3.3 70B - Fast/Coding)');
298
+ console.log(' \x1b[36m2)\x1b[0m Qwen3 Next 80B Thinking (Deep Reasoning)');
299
+ console.log(' \x1b[36m3)\x1b[0m GLM 4.7');
300
+
301
+ rl.question('\nEnter number (1-3): ', (choice) => {
302
+ let selected = 'auto';
303
+ if (choice === '2') selected = 'Qwen3';
304
+ if (choice === '3') selected = 'GLM';
305
+
306
+ const currentConfig = loadConfig();
307
+ currentConfig.model = selected;
308
+ saveConfig(currentConfig);
309
+
310
+ console.log(`\n\x1b[32m✔ Model successfully changed to: ${selected}\x1b[0m\n`);
311
+ ask();
312
+ });
313
+ return; // Pause the main loop while waiting for model choice
314
+ }
315
+ else if (trimmed) {
281
316
  await chat(trimmed);
317
+ ask();
318
+ } else {
319
+ ask();
282
320
  }
283
- ask();
284
321
  });
285
322
  }
286
323
  ask();
287
324
  }
288
325
 
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') {
299
- const prompt = args.slice(1).join(' ');
300
- if (!prompt) {
301
- console.error('❌ Please provide a prompt. Example: noxyai chat "Create a python script"');
302
- process.exit(1);
303
- }
304
- chat(prompt).then(() => process.exit(0));
305
- } else if (!command) {
306
- // Now this actually triggers!
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);
326
+ if (command === 'login') login();
327
+ else if (command === 'logout') logout();
328
+ else if (command === 'help' || command === '--help') {
329
+ printLogo();
330
+ console.log(`\n \x1b[32mnoxyai\x1b[0m Start Interactive Mode\n \x1b[32mnoxyai chat "<prompt>"\x1b[0m Run single prompt\n`);
312
331
  }
332
+ else if (command === 'chat') { chat(args.slice(1).join(' ')).then(() => process.exit(0)); }
333
+ else if (!command) startInteractiveMode();
334
+ else { console.error(`❌ Unknown command. Run "noxyai help"`); process.exit(1); }
package/index.html CHANGED
@@ -1,13 +1,27 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
1
+ <html>
3
2
  <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Snake Game</title>
3
+ <title>Tic Tac Toe</title>
7
4
  <link rel="stylesheet" href="style.css">
8
5
  </head>
9
6
  <body>
10
- <canvas id="gameCanvas" width="400" height="400"></canvas>
7
+ <h1>Tic Tac Toe</h1>
8
+ <div class="game-board">
9
+ <div class="row">
10
+ <div class="cell" id="cell-0"></div>
11
+ <div class="cell" id="cell-1"></div>
12
+ <div class="cell" id="cell-2"></div>
13
+ </div>
14
+ <div class="row">
15
+ <div class="cell" id="cell-3"></div>
16
+ <div class="cell" id="cell-4"></div>
17
+ <div class="cell" id="cell-5"></div>
18
+ </div>
19
+ <div class="row">
20
+ <div class="cell" id="cell-6"></div>
21
+ <div class="cell" id="cell-7"></div>
22
+ <div class="cell" id="cell-8"></div>
23
+ </div>
24
+ </div>
11
25
  <script src="script.js"></script>
12
26
  </body>
13
27
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "npm-noxyai",
3
- "version": "1.0.9",
3
+ "version": "1.0.10",
4
4
  "description": "CLI for NoxyAI",
5
5
  "main": "index-noxyai.js",
6
6
  "bin": {
package/script.js CHANGED
@@ -1,92 +1,38 @@
1
- const canvas = document.getElementById('gameCanvas');
2
- const ctx = canvas.getContext('2d');
1
+ let currentPlayer = "X";
2
+ let gameBoard = ["", "", "", "", "", "", "", "", ""];
3
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);
4
+ function clickCell(cellId) {
5
+ if (gameBoard[cellId] === "") {
6
+ gameBoard[cellId] = currentPlayer;
7
+ document.getElementById(`cell-${cellId}`).innerText = currentPlayer;
8
+ checkWin();
9
+ currentPlayer = currentPlayer === "X" ? "O" : "X";
21
10
  }
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
11
  }
30
12
 
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';
13
+ function checkWin() {
14
+ const winConditions = [
15
+ [0, 1, 2],
16
+ [3, 4, 5],
17
+ [6, 7, 8],
18
+ [0, 3, 6],
19
+ [1, 4, 7],
20
+ [2, 5, 8],
21
+ [0, 4, 8],
22
+ [2, 4, 6]
23
+ ];
24
+
25
+ for (let i = 0; i < winConditions.length; i++) {
26
+ const [a, b, c] = winConditions[i];
27
+ if (gameBoard[a] !== "" && gameBoard[a] === gameBoard[b] && gameBoard[b] === gameBoard[c]) {
28
+ alert(`Player ${gameBoard[a]} wins!`);
29
+ return;
61
30
  }
62
31
  }
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
32
  }
76
33
 
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';
34
+ document.addEventListener("DOMContentLoaded", () => {
35
+ for (let i = 0; i < 9; i++) {
36
+ document.getElementById(`cell-${i}`).addEventListener("click", () => clickCell(i));
91
37
  }
92
38
  });
@@ -0,0 +1,46 @@
1
+ import tkinter as tk
2
+ from tkinter import messagebox
3
+
4
+ class TicTacToe:
5
+ def __init__(self):
6
+ self.window = tk.Tk()
7
+ self.window.title("Tic Tac Toe")
8
+ self.window.geometry("300x300")
9
+ self.player_turn = "X"
10
+
11
+ self.buttons = []
12
+ for i in range(3):
13
+ row = []
14
+ for j in range(3):
15
+ button = tk.Button(self.window, command=lambda row=i, column=j: self.click(row, column), height=3, width=6)
16
+ button.grid(row=i, column=j)
17
+ row.append(button)
18
+ self.buttons.append(row)
19
+
20
+ def click(self, row, column):
21
+ if self.buttons[row][column]['text'] == "":
22
+ self.buttons[row][column]['text'] = self.player_turn
23
+ if self.check_win():
24
+ messagebox.showinfo("Game Over", f"Player {self.player_turn} wins!")
25
+ self.window.quit()
26
+ self.player_turn = "O" if self.player_turn == "X" else "X"
27
+
28
+ def check_win(self):
29
+ for row in self.buttons:
30
+ if row[0]['text'] == row[1]['text'] == row[2]['text'] != "":
31
+ return True
32
+ for column in range(3):
33
+ if self.buttons[0][column]['text'] == self.buttons[1][column]['text'] == self.buttons[2][column]['text'] != "":
34
+ return True
35
+ if self.buttons[0][0]['text'] == self.buttons[1][1]['text'] == self.buttons[2][2]['text'] != "":
36
+ return True
37
+ if self.buttons[0][2]['text'] == self.buttons[1][1]['text'] == self.buttons[2][0]['text'] != "":
38
+ return True
39
+ return False
40
+
41
+ def run(self):
42
+ self.window.mainloop()
43
+
44
+ if __name__ == "__main__":
45
+ game = TicTacToe()
46
+ game.run()