sam-coder-cli 1.0.44 → 1.0.46
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/package.json +2 -1
- package/bin/agi-cli.js.new +0 -328
- package/foldersnake/snake_game.py +0 -218
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sam-coder-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.46",
|
|
4
4
|
"description": "SAM-CODER: An animated command-line AI assistant with agency capabilities.",
|
|
5
5
|
"main": "bin/agi-cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"ws": "^8.18.3"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
+
"@types/vscode": "^1.102.0",
|
|
27
28
|
"eslint": "^7.27.0"
|
|
28
29
|
},
|
|
29
30
|
"engines": {
|
package/bin/agi-cli.js.new
DELETED
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const readline = require('readline');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const fs = require('fs').promises;
|
|
6
|
-
const { exec } = require('child_process');
|
|
7
|
-
const util = require('util');
|
|
8
|
-
const execAsync = util.promisify(exec);
|
|
9
|
-
|
|
10
|
-
// Configuration
|
|
11
|
-
const MODEL = 'deepseek/deepseek-chat-v3-0324:free';
|
|
12
|
-
const API_BASE_URL = 'https://openrouter.ai/api/v1';
|
|
13
|
-
|
|
14
|
-
// System prompt matching the VS Code extension
|
|
15
|
-
const SYSTEM_PROMPT = `You are a VS Code AI Assistant with agency capabilities. You can perform actions on the user's workspace.
|
|
16
|
-
|
|
17
|
-
ENVIRONMENT CONTEXT:
|
|
18
|
-
- OS: ${process.platform}
|
|
19
|
-
- Working Directory: ${process.cwd()}
|
|
20
|
-
|
|
21
|
-
When you need to perform actions, respond with JSON in the following format:
|
|
22
|
-
\`\`\`json
|
|
23
|
-
{
|
|
24
|
-
"thoughts": "Your reasoning about what needs to be done",
|
|
25
|
-
"actions": [
|
|
26
|
-
{
|
|
27
|
-
"type": "read|write|search|command|analyze|execute|stop",
|
|
28
|
-
"data": { ... action specific data ... }
|
|
29
|
-
}
|
|
30
|
-
]
|
|
31
|
-
}
|
|
32
|
-
\`\`\`
|
|
33
|
-
|
|
34
|
-
Action types and their data:
|
|
35
|
-
- read: { "path": "relative/or/absolute/path" }
|
|
36
|
-
- write: { "path": "relative/or/absolute/path", "content": "file content" }
|
|
37
|
-
- search: { "type": "files", "pattern": "glob pattern" } or { "type": "text", "text": "search text" }
|
|
38
|
-
- command: { "command": "command string to execute in terminal" }
|
|
39
|
-
- execute: { "language": "js|python|bash|...", "code": "code to execute" }
|
|
40
|
-
- analyze: { "code": "code to analyze", "question": "what you want to analyze" }
|
|
41
|
-
- browse: { "query": "search query", "numResults": 5 } (free web search using DuckDuckGo, optional numResults)
|
|
42
|
-
- edit: {
|
|
43
|
-
"path": "relative/or/absolute/path",
|
|
44
|
-
"edits": {
|
|
45
|
-
"operations": [
|
|
46
|
-
{ "type": "replace", "startLine": 10, "endLine": 15, "newText": "new code here" },
|
|
47
|
-
{ "type": "replace", "pattern": "oldFunction\\(\\)", "replacement": "newFunction()", "flags": "g" },
|
|
48
|
-
{ "type": "insert", "line": 20, "text": "new line of code here" },
|
|
49
|
-
{ "type": "insert", "position": "start", "text": "// Header comment" },
|
|
50
|
-
{ "type": "insert", "position": "end", "text": "// Footer comment" },
|
|
51
|
-
{ "type": "delete", "startLine": 25, "endLine": 30 }
|
|
52
|
-
]
|
|
53
|
-
}
|
|
54
|
-
} (edit specific parts of an existing file)
|
|
55
|
-
- stop: {} (use this to indicate you're done with the task and no more actions are needed)
|
|
56
|
-
|
|
57
|
-
By default, you will continue to take actions in a loop until you decide to stop with the 'stop' action type.
|
|
58
|
-
Always wrap your JSON in markdown code blocks with the json language specifier.
|
|
59
|
-
When executing code or commands that might be potentially harmful, explain what the code does before executing it.`;
|
|
60
|
-
|
|
61
|
-
// Agent utilities
|
|
62
|
-
const agentUtils = {
|
|
63
|
-
async readFile(filePath) {
|
|
64
|
-
try {
|
|
65
|
-
const content = await fs.readFile(filePath, 'utf-8');
|
|
66
|
-
return content;
|
|
67
|
-
} catch (error) {
|
|
68
|
-
throw new Error(`Failed to read file ${filePath}: ${error.message}`);
|
|
69
|
-
}
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
async writeFile(filePath, content) {
|
|
73
|
-
try {
|
|
74
|
-
await fs.writeFile(filePath, content, 'utf-8');
|
|
75
|
-
return `Successfully wrote to ${filePath}`;
|
|
76
|
-
} catch (error) {
|
|
77
|
-
throw new Error(`Failed to write to file ${filePath}: ${error.message}`);
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
|
|
81
|
-
async runCommand(command) {
|
|
82
|
-
try {
|
|
83
|
-
const { stdout, stderr } = await execAsync(command, { cwd: process.cwd() });
|
|
84
|
-
if (stderr) {
|
|
85
|
-
console.error('Command stderr:', stderr);
|
|
86
|
-
}
|
|
87
|
-
return stdout || 'Command executed successfully (no output)';
|
|
88
|
-
} catch (error) {
|
|
89
|
-
throw new Error(`Command failed: ${error.message}`);
|
|
90
|
-
}
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
async searchFiles(pattern) {
|
|
94
|
-
try {
|
|
95
|
-
const { stdout } = await execAsync(`find . -name "${pattern}"`, { cwd: process.cwd() });
|
|
96
|
-
return stdout || 'No files found';
|
|
97
|
-
} catch (error) {
|
|
98
|
-
throw new Error(`Search failed: ${error.message}`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
// Extract JSON from markdown code blocks
|
|
104
|
-
function extractJsonFromMarkdown(text) {
|
|
105
|
-
// Try to find a markdown code block with JSON content
|
|
106
|
-
const codeBlockRegex = /```json\s*([\s\S]*?)\s*```/;
|
|
107
|
-
const match = text.match(codeBlockRegex);
|
|
108
|
-
|
|
109
|
-
if (match) {
|
|
110
|
-
try {
|
|
111
|
-
return JSON.parse(match[1]);
|
|
112
|
-
} catch (error) {
|
|
113
|
-
console.error('Error parsing JSON from markdown:', error);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// If no code block, try to parse the entire text as JSON
|
|
118
|
-
try {
|
|
119
|
-
return JSON.parse(text);
|
|
120
|
-
} catch (error) {
|
|
121
|
-
console.error('Error parsing JSON:', error);
|
|
122
|
-
return null;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Call OpenRouter API
|
|
127
|
-
async function callOpenRouter(messages) {
|
|
128
|
-
const apiKey = process.env.OPENROUTER_API_KEY;
|
|
129
|
-
|
|
130
|
-
if (!apiKey) {
|
|
131
|
-
throw new Error('OPENROUTER_API_KEY environment variable is not set');
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
try {
|
|
135
|
-
const response = await fetch(API_BASE_URL + '/chat/completions', {
|
|
136
|
-
method: 'POST',
|
|
137
|
-
headers: {
|
|
138
|
-
'Content-Type': 'application/json',
|
|
139
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
140
|
-
'HTTP-Referer': 'https://github.com/yourusername/agi-cli'
|
|
141
|
-
},
|
|
142
|
-
body: JSON.stringify({
|
|
143
|
-
model: MODEL,
|
|
144
|
-
messages: messages
|
|
145
|
-
})
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
if (!response.ok) {
|
|
149
|
-
const error = await response.json();
|
|
150
|
-
throw new Error(`API error: ${error.error?.message || response.statusText}`);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return await response.json();
|
|
154
|
-
} catch (error) {
|
|
155
|
-
console.error('API call failed:', error);
|
|
156
|
-
throw new Error(`Failed to call OpenRouter API: ${error.message}`);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// Process a query with action handling
|
|
161
|
-
async function processQuery(query, conversation = []) {
|
|
162
|
-
try {
|
|
163
|
-
// Add user message to conversation
|
|
164
|
-
const userMessage = { role: 'user', content: query };
|
|
165
|
-
const messages = [...conversation, userMessage];
|
|
166
|
-
|
|
167
|
-
// Add system message if this is the first message
|
|
168
|
-
if (conversation.length === 0) {
|
|
169
|
-
messages.unshift({
|
|
170
|
-
role: 'system',
|
|
171
|
-
content: SYSTEM_PROMPT
|
|
172
|
-
});
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
let shouldContinue = true;
|
|
176
|
-
let iteration = 0;
|
|
177
|
-
const maxIterations = 10; // Prevent infinite loops
|
|
178
|
-
let finalResponse = '';
|
|
179
|
-
|
|
180
|
-
while (shouldContinue && iteration < maxIterations) {
|
|
181
|
-
iteration++;
|
|
182
|
-
console.log('🤖 Thinking...');
|
|
183
|
-
|
|
184
|
-
const response = await callOpenRouter(messages);
|
|
185
|
-
const assistantMessage = response.choices[0].message;
|
|
186
|
-
|
|
187
|
-
// Add assistant's message to the conversation
|
|
188
|
-
messages.push(assistantMessage);
|
|
189
|
-
|
|
190
|
-
// Check if the response contains actions
|
|
191
|
-
const actionData = extractJsonFromMarkdown(assistantMessage.content);
|
|
192
|
-
|
|
193
|
-
if (actionData && actionData.actions && Array.isArray(actionData.actions)) {
|
|
194
|
-
console.log(`🔧 Processing ${actionData.actions.length} actions...`);
|
|
195
|
-
if (actionData.thoughts) {
|
|
196
|
-
console.log(`💭 ${actionData.thoughts}`);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const actionResults = [];
|
|
200
|
-
|
|
201
|
-
for (const action of actionData.actions) {
|
|
202
|
-
console.log(`🛠️ Executing action: ${action.type}`);
|
|
203
|
-
|
|
204
|
-
// Handle stop action
|
|
205
|
-
if (action.type === 'stop') {
|
|
206
|
-
console.log('🛑 Stop action received, ending action processing');
|
|
207
|
-
shouldContinue = false;
|
|
208
|
-
finalResponse = 'Task completed successfully.';
|
|
209
|
-
break;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
try {
|
|
213
|
-
let result;
|
|
214
|
-
|
|
215
|
-
switch (action.type) {
|
|
216
|
-
case 'read':
|
|
217
|
-
result = await agentUtils.readFile(action.data.path);
|
|
218
|
-
break;
|
|
219
|
-
|
|
220
|
-
case 'write':
|
|
221
|
-
result = await agentUtils.writeFile(action.data.path, action.data.content);
|
|
222
|
-
break;
|
|
223
|
-
|
|
224
|
-
case 'command':
|
|
225
|
-
result = await agentUtils.runCommand(action.data.command);
|
|
226
|
-
break;
|
|
227
|
-
|
|
228
|
-
case 'search':
|
|
229
|
-
if (action.data.type === 'files') {
|
|
230
|
-
result = await agentUtils.searchFiles(action.data.pattern);
|
|
231
|
-
} else {
|
|
232
|
-
result = 'Text search not yet implemented';
|
|
233
|
-
}
|
|
234
|
-
break;
|
|
235
|
-
|
|
236
|
-
default:
|
|
237
|
-
result = `Action type '${action.type}' is not supported yet.`;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
actionResults.push({
|
|
241
|
-
type: action.type,
|
|
242
|
-
success: true,
|
|
243
|
-
result: result
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
console.log(`✅ Action ${action.type} completed successfully`);
|
|
247
|
-
|
|
248
|
-
} catch (error) {
|
|
249
|
-
console.error(`❌ Action ${action.type} failed:`, error);
|
|
250
|
-
actionResults.push({
|
|
251
|
-
type: action.type,
|
|
252
|
-
success: false,
|
|
253
|
-
error: error.message
|
|
254
|
-
});
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Add action results to the conversation
|
|
259
|
-
messages.push({
|
|
260
|
-
role: 'system',
|
|
261
|
-
content: `Action results:\n\`\`\`json\n${JSON.stringify(actionResults, null, 2)}\n\`\`\`\n` +
|
|
262
|
-
`Based on these results, determine what to do next. You can:\n` +
|
|
263
|
-
`1. Continue with more actions by returning a new JSON with "actions" array\n` +
|
|
264
|
-
`2. Stop the iteration by including an action with "type": "stop" if the task is completed\n` +
|
|
265
|
-
`3. Provide a final response to the user with your findings`
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
} else {
|
|
269
|
-
// No actions, this is a regular response
|
|
270
|
-
shouldContinue = false;
|
|
271
|
-
finalResponse = assistantMessage.content;
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// If we hit max iterations, add a note
|
|
276
|
-
if (iteration >= maxIterations) {
|
|
277
|
-
finalResponse += '\n\n⚠️ Reached maximum number of iterations. Stopping execution.';
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
return {
|
|
281
|
-
response: finalResponse,
|
|
282
|
-
conversation: messages
|
|
283
|
-
};
|
|
284
|
-
|
|
285
|
-
} catch (error) {
|
|
286
|
-
console.error('Error processing query:', error);
|
|
287
|
-
return {
|
|
288
|
-
response: `Error: ${error.message}`,
|
|
289
|
-
conversation
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// Main chat loop
|
|
295
|
-
async function chat() {
|
|
296
|
-
const conversation = [];
|
|
297
|
-
console.log('Welcome to AGI-CLI. Type your message, or "exit" to quit.');
|
|
298
|
-
|
|
299
|
-
const rl = readline.createInterface({
|
|
300
|
-
input: process.stdin,
|
|
301
|
-
output: process.stdout,
|
|
302
|
-
prompt: '> '
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
rl.prompt();
|
|
306
|
-
|
|
307
|
-
rl.on('line', async (input) => {
|
|
308
|
-
if (input.toLowerCase() === 'exit') {
|
|
309
|
-
rl.close();
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const result = await processQuery(input, conversation);
|
|
314
|
-
console.log(result.response);
|
|
315
|
-
|
|
316
|
-
// Update conversation with the full context
|
|
317
|
-
conversation.length = 0; // Clear the array
|
|
318
|
-
result.conversation.forEach(msg => conversation.push(msg));
|
|
319
|
-
|
|
320
|
-
rl.prompt();
|
|
321
|
-
}).on('close', () => {
|
|
322
|
-
console.log('Goodbye!');
|
|
323
|
-
process.exit(0);
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Start the chat
|
|
328
|
-
chat().catch(console.error);
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
import pygame
|
|
2
|
-
import random
|
|
3
|
-
|
|
4
|
-
# Initialize pygame
|
|
5
|
-
pygame.init()
|
|
6
|
-
|
|
7
|
-
# Constants
|
|
8
|
-
SCREEN_WIDTH = 300
|
|
9
|
-
SCREEN_HEIGHT = 600
|
|
10
|
-
BLOCK_SIZE = 30
|
|
11
|
-
GRID_WIDTH = 10
|
|
12
|
-
GRID_HEIGHT = 20
|
|
13
|
-
GAME_AREA_LEFT = (SCREEN_WIDTH - GRID_WIDTH * BLOCK_SIZE) // 2
|
|
14
|
-
GAME_AREA_TOP = SCREEN_HEIGHT - GRID_HEIGHT * BLOCK_SIZE
|
|
15
|
-
|
|
16
|
-
# Colors
|
|
17
|
-
BLACK = (0, 0, 0)
|
|
18
|
-
WHITE = (255, 255, 255)
|
|
19
|
-
GRAY = (128, 128, 128)
|
|
20
|
-
COLORS = [
|
|
21
|
-
(0, 255, 255), # Cyan (I)
|
|
22
|
-
(0, 0, 255), # Blue (J)
|
|
23
|
-
(255, 165, 0), # Orange (L)
|
|
24
|
-
(255, 255, 0), # Yellow (O)
|
|
25
|
-
(0, 255, 0), # Green (S)
|
|
26
|
-
(128, 0, 128), # Purple (T)
|
|
27
|
-
(255, 0, 0) # Red (Z)
|
|
28
|
-
]
|
|
29
|
-
|
|
30
|
-
# Tetrimino shapes
|
|
31
|
-
SHAPES = [
|
|
32
|
-
[[1, 1, 1, 1]], # I
|
|
33
|
-
[[1, 0, 0], [1, 1, 1]], # J
|
|
34
|
-
[[0, 0, 1], [1, 1, 1]], # L
|
|
35
|
-
[[1, 1], [1, 1]], # O
|
|
36
|
-
[[0, 1, 1], [1, 1, 0]], # S
|
|
37
|
-
[[0, 1, 0], [1, 1, 1]], # T
|
|
38
|
-
[[1, 1, 0], [0, 1, 1]] # Z
|
|
39
|
-
]
|
|
40
|
-
|
|
41
|
-
# Set up the display
|
|
42
|
-
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
|
|
43
|
-
pygame.display.set_caption("Tetris")
|
|
44
|
-
|
|
45
|
-
clock = pygame.time.Clock()
|
|
46
|
-
|
|
47
|
-
# Game variables
|
|
48
|
-
grid = [[0 for _ in range(GRID_WIDTH)] for _ in range(GRID_HEIGHT)]
|
|
49
|
-
current_piece = None
|
|
50
|
-
next_piece = None
|
|
51
|
-
current_x = GRID_WIDTH // 2
|
|
52
|
-
current_y = 0
|
|
53
|
-
score = 0
|
|
54
|
-
game_over = False
|
|
55
|
-
fall_time = 0
|
|
56
|
-
fall_speed = 0.5 # seconds
|
|
57
|
-
|
|
58
|
-
def new_piece():
|
|
59
|
-
global current_piece, next_piece, current_x, current_y
|
|
60
|
-
if next_piece is None:
|
|
61
|
-
next_piece = random.randint(0, len(SHAPES) - 1)
|
|
62
|
-
current_piece = next_piece
|
|
63
|
-
next_piece = random.randint(0, len(SHAPES) - 1)
|
|
64
|
-
current_x = GRID_WIDTH // 2 - len(SHAPES[current_piece][0]) // 2
|
|
65
|
-
current_y = 0
|
|
66
|
-
if check_collision():
|
|
67
|
-
return False
|
|
68
|
-
return True
|
|
69
|
-
|
|
70
|
-
def check_collision():
|
|
71
|
-
for y, row in enumerate(SHAPES[current_piece]):
|
|
72
|
-
for x, cell in enumerate(row):
|
|
73
|
-
if cell:
|
|
74
|
-
if (current_y + y >= GRID_HEIGHT or
|
|
75
|
-
current_x + x < 0 or
|
|
76
|
-
current_x + x >= GRID_WIDTH or
|
|
77
|
-
grid[current_y + y][current_x + x]):
|
|
78
|
-
return True
|
|
79
|
-
return False
|
|
80
|
-
|
|
81
|
-
def merge_piece():
|
|
82
|
-
for y, row in enumerate(SHAPES[current_piece]):
|
|
83
|
-
for x, cell in enumerate(row):
|
|
84
|
-
if cell:
|
|
85
|
-
grid[current_y + y][current_x + x] = current_piece + 1
|
|
86
|
-
|
|
87
|
-
def clear_lines():
|
|
88
|
-
global grid, score
|
|
89
|
-
lines_cleared = 0
|
|
90
|
-
for y in range(GRID_HEIGHT - 1, -1, -1):
|
|
91
|
-
if all(grid[y]):
|
|
92
|
-
lines_cleared += 1
|
|
93
|
-
for y2 in range(y, 0, -1):
|
|
94
|
-
grid[y2] = grid[y2 - 1][:]
|
|
95
|
-
grid[0] = [0] * GRID_WIDTH
|
|
96
|
-
score += lines_cleared ** 2 * 100
|
|
97
|
-
|
|
98
|
-
def draw_grid():
|
|
99
|
-
for y in range(GRID_HEIGHT):
|
|
100
|
-
for x in range(GRID_WIDTH):
|
|
101
|
-
if grid[y][x]:
|
|
102
|
-
pygame.draw.rect(
|
|
103
|
-
screen, COLORS[grid[y][x] - 1],
|
|
104
|
-
(GAME_AREA_LEFT + x * BLOCK_SIZE, GAME_AREA_TOP + y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)
|
|
105
|
-
)
|
|
106
|
-
pygame.draw.rect(
|
|
107
|
-
screen, WHITE,
|
|
108
|
-
(GAME_AREA_LEFT + x * BLOCK_SIZE, GAME_AREA_TOP + y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE),
|
|
109
|
-
1
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
def draw_piece():
|
|
113
|
-
for y, row in enumerate(SHAPES[current_piece]):
|
|
114
|
-
for x, cell in enumerate(row):
|
|
115
|
-
if cell:
|
|
116
|
-
pygame.draw.rect(
|
|
117
|
-
screen, COLORS[current_piece],
|
|
118
|
-
(GAME_AREA_LEFT + (current_x + x) * BLOCK_SIZE, GAME_AREA_TOP + (current_y + y) * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)
|
|
119
|
-
)
|
|
120
|
-
pygame.draw.rect(
|
|
121
|
-
screen, WHITE,
|
|
122
|
-
(GAME_AREA_LEFT + (current_x + x) * BLOCK_SIZE, GAME_AREA_TOP + (current_y + y) * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE),
|
|
123
|
-
1
|
|
124
|
-
)
|
|
125
|
-
|
|
126
|
-
def draw_next_piece():
|
|
127
|
-
if next_piece is not None:
|
|
128
|
-
font = pygame.font.SysFont(None, 24)
|
|
129
|
-
text = font.render("Next:", True, WHITE)
|
|
130
|
-
screen.blit(text, (20, 20))
|
|
131
|
-
for y, row in enumerate(SHAPES[next_piece]):
|
|
132
|
-
for x, cell in enumerate(row):
|
|
133
|
-
if cell:
|
|
134
|
-
pygame.draw.rect(
|
|
135
|
-
screen, COLORS[next_piece],
|
|
136
|
-
(40 + x * BLOCK_SIZE, 50 + y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)
|
|
137
|
-
)
|
|
138
|
-
pygame.draw.rect(
|
|
139
|
-
screen, WHITE,
|
|
140
|
-
(40 + x * BLOCK_SIZE, 50 + y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE),
|
|
141
|
-
1
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
def draw_score():
|
|
145
|
-
font = pygame.font.SysFont(None, 36)
|
|
146
|
-
text = font.render(f"Score: {score}", True, WHITE)
|
|
147
|
-
screen.blit(text, (20, SCREEN_HEIGHT - 50))
|
|
148
|
-
|
|
149
|
-
def rotate_piece():
|
|
150
|
-
global current_piece
|
|
151
|
-
rotated = list(zip(*reversed(SHAPES[current_piece])))
|
|
152
|
-
for y in range(len(rotated)):
|
|
153
|
-
rotated[y] = list(rotated[y])
|
|
154
|
-
old_shape = SHAPES[current_piece]
|
|
155
|
-
SHAPES[current_piece] = rotated
|
|
156
|
-
if check_collision():
|
|
157
|
-
SHAPES[current_piece] = old_shape
|
|
158
|
-
|
|
159
|
-
# Main game loop
|
|
160
|
-
new_piece()
|
|
161
|
-
running = True
|
|
162
|
-
while running:
|
|
163
|
-
screen.fill(BLACK)
|
|
164
|
-
fall_time += clock.get_rawtime() / 1000 # Convert to seconds
|
|
165
|
-
clock.tick()
|
|
166
|
-
|
|
167
|
-
if fall_time >= fall_speed:
|
|
168
|
-
fall_time = 0
|
|
169
|
-
current_y += 1
|
|
170
|
-
if check_collision():
|
|
171
|
-
current_y -= 1
|
|
172
|
-
merge_piece()
|
|
173
|
-
clear_lines()
|
|
174
|
-
if not new_piece():
|
|
175
|
-
game_over = True
|
|
176
|
-
|
|
177
|
-
for event in pygame.event.get():
|
|
178
|
-
if event.type == pygame.QUIT:
|
|
179
|
-
running = False
|
|
180
|
-
if event.type == pygame.KEYDOWN:
|
|
181
|
-
if event.key == pygame.K_LEFT:
|
|
182
|
-
current_x -= 1
|
|
183
|
-
if check_collision():
|
|
184
|
-
current_x += 1
|
|
185
|
-
if event.key == pygame.K_RIGHT:
|
|
186
|
-
current_x += 1
|
|
187
|
-
if check_collision():
|
|
188
|
-
current_x -= 1
|
|
189
|
-
if event.key == pygame.K_DOWN:
|
|
190
|
-
current_y += 1
|
|
191
|
-
if check_collision():
|
|
192
|
-
current_y -= 1
|
|
193
|
-
if event.key == pygame.K_UP:
|
|
194
|
-
rotate_piece()
|
|
195
|
-
if event.key == pygame.K_SPACE:
|
|
196
|
-
while not check_collision():
|
|
197
|
-
current_y += 1
|
|
198
|
-
current_y -= 1
|
|
199
|
-
merge_piece()
|
|
200
|
-
clear_lines()
|
|
201
|
-
if not new_piece():
|
|
202
|
-
game_over = True
|
|
203
|
-
|
|
204
|
-
draw_grid()
|
|
205
|
-
draw_piece()
|
|
206
|
-
draw_next_piece()
|
|
207
|
-
draw_score()
|
|
208
|
-
pygame.display.update()
|
|
209
|
-
|
|
210
|
-
if game_over:
|
|
211
|
-
font = pygame.font.SysFont(None, 48)
|
|
212
|
-
text = font.render("GAME OVER", True, WHITE)
|
|
213
|
-
screen.blit(text, (SCREEN_WIDTH // 2 - 100, SCREEN_HEIGHT // 2 - 24))
|
|
214
|
-
pygame.display.update()
|
|
215
|
-
pygame.time.wait(2000)
|
|
216
|
-
running = False
|
|
217
|
-
|
|
218
|
-
pygame.quit()
|