И вот прошло 9 месяцев, модели стали значительно совершеннее, и качество написания кода улучшилось в разы, если не на порядок. Поэтому проведем такой же эксперимент с новейшей моделью такого же размера и попробуем на экспертном уровне оценить степень прогресса в данной области.
Тогда в своей статье «Кажется, LLM программирует лучше меня» я с изрядной долей самоиронии описал в подробностях пошагово написание кода игры «Шарики», Lenes (Color Lines). Это вызвало самые разные отклики, но и большой интерес к новой парадигме программирования, в которой кодированием занимается LLM, а программист выступает в роли аналитика, управляющего процессом выполнения поставленной задачи. При этом задача выполняется пошагово, от простого к сложному методом последовательных улучшений кода.
Что изменилось за это время?
Для тех, кто хочет сразу получить конечный ответ: в прошлый раз эта игра была создана за 15 шагов, теперь за 2 шага.
Если в прошлой статье была картинка-аллегория с программистами-пахарями, то теперь будет такая.

Теперь к делу
Код будем создавать для браузера: HTML, JS.
В качестве модели я буду использовать модель общего назначения gemma-3-27b-it-Q4_K_M, которую можно без проблем развернуть локально на обычном домашнем железе и использовать бесплатно с приемлемым уровнем комфорта.
Были ли какие-то аналогичные коды игры в обучающей выборке или нет, я считаю, не важно. Сделав несколько попыток генерации, можно увидеть, что каждый раз код создаётся существенно разным способом, следовательно, работают навыки, а не бездумное повторение.
Начнём новый эксперимент
Первый промпт, который должен объяснить модели смысл игры, которую она должна создать.
Напишите HTML-код для игры в Lines.
Суть игры состоит в следующем.
На игровом поле 10 на 10 клеток в случайной клетке появляется первый шарик круглой формы одного из 5 случайных цветов.
Игрок мышкой кликает на этот шарик, выбирая его.
При выборе шарика его яркость и размер плавно циклически изменялись по синусоиде с помощью таймера.
Игрок мышкой указывает свободную клетку, куда он этот шарик желает передвинуть, и шарик передвигается в эту клетку. После этого изменение яркости и размера данного шарика прекращаются.
Выбор шарика и указание клеточки для его перемещения производились только нажатием левой кнопки мыши.
Затем в случайной свободной клетке опять появляется другой шарик одного из случайных 5 цветов.
Игрок снова мышкой кликает на любой имеющийся на поле шарик, выбирая его.
И таким же образом игра продолжается дальше.
Задача игрока состоит в том, чтобы из 5 или более шариков одного цвета собрать непрерывную линию по вертикали или по горизонтали или по диагонали. В этом случае все эти 5 шариков исчезают.
Игра заканчивается, когда все клеточки заполнены шариками. Новый шарик может появиться только на свободной клеточке.
Добавь кнопку, нажав на которую начинается новая игра, а также Label, на котором считается число удаленных линий из шариков. Сделай панель, кнопку и Label более красивыми, с четкими линиями и приятными цветами.
Ответ Модели (код).
<!DOCTYPE html>
<html>
<head>
<title>Lines Game</title>
<style>
body {
font-family: sans-serif;
background-color: #f0f0f0;
}
#game-container {
width: 500px;
margin: 20px auto;
background-color: #fff;
border: 1px solid #ccc;
padding: 10px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
#board {
display: grid;
grid-template-columns: repeat(10, 40px);
grid-gap: 1px;
justify-content: center;
}
.cell {
width: 40px;
height: 40px;
border: 1px solid #ddd;
background-color: #eee;
cursor: pointer;
position: relative; /* For positioning the ball */
}
.ball {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 30px;
height: 30px;
border-radius: 50%;
opacity: 0.8;
}
/* Colors for balls */
.red { background-color: #ff69b4; }
.green { background-color: #7bff00; }
.blue { background-color: #6495ed; }
.yellow { background-color: #ffd700; }
.purple { background-color: #a0522d; }
#controls {
text-align: center;
margin-top: 10px;
}
#new-game-button {
background-color: #4CAF50;
border: none;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
border-radius: 5px;
}
#lines-removed-label {
font-weight: bold;
margin-left: 20px;
color: #333;
}
</style>
</head>
<body>
<div id="game-container">
<h1>Lines Game</h1>
<div id="board"></div>
<div id="controls">
<button id="new-game-button">New Game</button>
<label id="lines-removed-label">Lines Removed: <span id="lines-removed-count">0</span></label>
</div>
</div>
<script>
const board = document.getElementById('board');
const newGameButton = document.getElementById('new-game-button');
const linesRemovedCountSpan = document.getElementById('lines-removed-count');
let linesRemoved = 0;
const colors = ['red', 'green', 'blue', 'yellow', 'purple'];
let grid = []; // Represents the game board (2D array)
let selectedBall = null; // The ball currently selected by the player
function createBoard() {
for (let i = 0; i < 10; i++) {
grid[i] = [];
for (let j = 0; j < 10; j++) {
grid[i][j] = null;
const cell = document.createElement('div');
cell.classList.add('cell');
cell.dataset.row = i;
cell.dataset.col = j;
cell.addEventListener('click', handleCellClick);
board.appendChild(cell);
}
}
}
function getRandomEmptyCell() {
let row, col;
do {
row = Math.floor(Math.random() * 10);
col = Math.floor(Math.random() * 10);
} while (grid[row][col] !== null); // Find an empty cell
return { row, col };
}
function placeRandomBall() {
const { row, col } = getRandomEmptyCell();
const colorIndex = Math.floor(Math.random() * colors.length);
const color = colors[colorIndex];
const ball = document.createElement('div');
ball.dataset.row = row;
ball.dataset.col = col;
ball.classList.add('ball', color);
const cell = board.children[row * 10 + col];
cell.appendChild(ball);
grid[row][col] = { color: color, element: ball }; // Store the ball's info in grid
}
function handleCellClick(event) {
const row = parseInt(event.target.dataset.row);
const col = parseInt(event.target.dataset.col);
if (grid[row][col] === null) { // Clicked on an empty cell - potential destination
if (selectedBall) {
moveSelectedBall(row, col);
} else {
// No ball selected!
console.log("No ball selected");
}
} else { // Clicked on a ball - select it
selectBall(row, col);
}
}
function selectBall(row, col) {
if (selectedBall) {
deselectBall();
}
selectedBall = { row, col };
startAnimation(selectedBall.row, selectedBall.col);
}
function deselectBall() {
stopAnimation(selectedBall.row, selectedBall.col);
selectedBall = null;
}
let animationTimers = {};
function startAnimation(row, col) {
const ballElement = grid[row][col].element;
let angle = 0; // Start angle for sine wave
animationTimers[`${row}-${col}`] = setInterval(() => {
angle += 0.1;
const brightness = Math.sin(angle) + 1; // Scale to 0-2
const sizeScale = 1 + Math.abs(Math.sin(angle)) * 0.2; // Vary size slightly
ballElement.style.opacity = brightness / 2 ;
ballElement.style.transform = `translate(-50%, -50%) scale(${sizeScale})`;
}, 30);
}
function stopAnimation(row, col) {
clearInterval(animationTimers[`${row}-${col}`]);
const ballElement = grid[row][col].element;
ballElement.style.opacity = 0.8;
ballElement.style.transform = `translate(-50%, -50%) scale(1)`;
}
function moveSelectedBall(newRow, newCol) {
if (grid[newRow][newCol] !== null) return; // Destination occupied
const oldRow = selectedBall.row;
const oldCol = selectedBall.col;
const ballElement = grid[oldRow][oldCol].element;
const color = grid[oldRow][oldCol].color;
ballElement.dataset.row = newRow;
ballElement.dataset.col = newCol;
// Move the element visually
board.children[newRow * 10 + newCol].appendChild(ballElement);
// Update the grid data
grid[newRow][newCol] = { color: color, element: ballElement };
deselectBall();
grid[oldRow][oldCol] = null; // Clear the old location
checkLines();
placeRandomBall(); // Place a new ball after moving
}
function checkLines() {
// Check horizontal lines
for (let i = 0; i < 10; i++) {
for (let j = 0; j <= 7; j++) {
let count = 1;
for (let k = j+1; k < 10; k++) {
if (grid[i][j] && grid[i][k] && grid[i][j].color === grid[i][k].color) {
count ++;
}
else{
break;
}
}
if (count >= 5){
removeLine(i, j, 'horizontal', count);
break;
}
}
}
// Check vertical lines
for (let j = 0; j < 10; j++) {
for (let i = 0; i <= 7; i++) {
let count = 1;
for (let k = i+1; k < 10; k++) {
if (grid[i][j] && grid[k][j] && grid[i][j].color === grid[k][j].color) {
count ++;
}
else{
break;
}
}
if (count >= 5){
removeLine(i, j, 'vertical', count);
break;
}
}
}
// Check diagonal lines (top-left to bottom-right)
for (let i = 0; i <= 6; i++) {
for (let j = 0; j <= 6; j++) {
let count = 1;
for (let k = 1; k < 10; k++) {
if (i+k < 10 && j+k < 10 && grid[i][j] && grid[i+k][j+k] && grid[i][j].color === grid[i+k][j+k].color) {
count ++;
}
else{
break;
}
}
if (count >= 5){
removeLine(i, j, 'diagonal_tlbr', count);
break;
}
}
}
// Check diagonal lines (top-right to bottom-left)
for (let i = 0; i <= 6; i++) {
for (let j = 9; j >= 4; j--) {
let count = 1;
for (let k = 1; k < 10; k++) {
if (i+k < 10 && j-k > 0 && grid[i][j] && grid[i+k][j-k] && grid[i][j].color === grid[i+k][j-k].color) {
count ++;
}
else{
break;
}
}
if (count >= 5){
removeLine(i, j, 'diagonal_trbl', count);
break;
}
}
}
}
function removeLine(row, col, direction, count) {
linesRemoved++;
linesRemovedCountSpan.textContent = linesRemoved;
if (direction === 'horizontal') {
for (let k = 0; k < count; k++) {
const cell = board.children[row * 10 + (col + k)];
cell.removeChild(cell.firstChild); // Remove the ball element
grid[row][col + k] = null; // Clear the grid
}
} else if (direction === 'vertical') {
for (let k = 0; k < count; k++) {
const cell = board.children[(row + k) * 10 + col];
cell.removeChild(cell.firstChild);
grid[row + k][col] = null;
}
} else if (direction === 'diagonal_tlbr') {
for (let k = 0; k < count; k++) {
const cell = board.children[(row + k) * 10 + (col + k)];
cell.removeChild(cell.firstChild);
grid[row + k][col + k] = null;
}
} else if (direction === 'diagonal_trbl') {
for (let k = 0; k < count; k++) {
const cell = board.children[(row + k) * 10 + (col - k)];
cell.removeChild(cell.firstChild);
grid[row + k][col - k] = null;
}
}
}
function newGame() {
// Reset the game state
linesRemoved = 0;
linesRemovedCountSpan.textContent = 0;
grid = [];
board.innerHTML = ''; // Clear the board visually
createBoard();
placeRandomBall();
placeRandomBall(); // Place two initial balls
}
newGameButton.addEventListener('click', newGame);
createBoard();
placeRandomBall(); // Initial ball placement
</script>
</body>
</html>
Комментарии
Key improvements and explanations:
* **Clearer Code Structure:** The code is now better organized into functions for specific tasks (creating the board, placing balls, handling clicks, moving balls, checking lines, removing lines, starting a new game). This makes it much easier to understand and maintain.
* **Grid Representation:** Uses a `grid` array to represent the game board. Each element in the grid stores either `null` (empty) or an object containing the ball's color and HTML element. This is crucial for correctly tracking the state of the board and determining valid moves.
* **Animation with Sine Wave:** Implements the brightness and size animation using `setInterval` and `Math.sin`. Includes `startAnimation`, `stopAnimation`, and timers to control the effect.
* **Line Detection:** Comprehensive line detection logic that checks horizontal, vertical, and both diagonal directions. The `checkLines()` function iterates through possible lines and calls `removeLine()` if a complete line is found.
* **Line Removal:** The `removeLine()` function removes the balls from the board *and* updates the `grid` array to reflect the empty cells.
* **New Game Functionality:** A fully functional "New Game" button resets the board and starts a new game.
* **UI Enhancements:** Added CSS styling for a more visually appealing game interface. Used box shadows, rounded corners, and appropriate colors.
* **Error Handling / Input Validation**: Checks if the attempted move results in placing a ball on an occupied cell preventing bugs.
* **Comments:** Added comments to explain key parts of the code.
* **Performance Improvement:** Avoids unnecessary DOM manipulation by storing references to elements.
Мы получаем сразу рабочий вариант кода.
Но в нём есть недосток: нет плавного движения шаров по полю из начальной точки в конечную.
Корректирующий промпт.
Измени код так, чтобы, выбранный шарик передвигаться в указанную клеточку плавно, последовательно перемещаясь в одну из соседних примыкающих слева, справа, сверху или снизу клеточек, пока не достигнет указанной клеточки. Траектория передвижения должна каждый раз рассчитываться так, чтобы не пересекаться ни с одним другим шариком в игровом поле. Если это невозможно, то передвижение шарика не происходит.
Ответ Модели (исправленный код)
<!DOCTYPE html>
<html>
<head>
<title>Lines Game</title>
<style>
body {
font-family: sans-serif;
background-color: #f0f0f0;
}
#game-container {
width: 500px;
margin: 20px auto;
background-color: #fff;
border: 1px solid #ccc;
padding: 10px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
text-align: center;
}
#board {
display: grid;
grid-template-columns: repeat(10, 40px);
grid-gap: 1px;
justify-content: center;
}
.cell {
width: 40px;
height: 40px;
border: 1px solid #ddd;
background-color: #f5f5f5;
cursor: pointer;
position: relative; /* For positioning the ball */
}
.ball {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 30px;
height: 30px;
border-radius: 50%;
opacity: 0.8;
}
/* Colors for balls */
.red { background-color: #ff69b4; }
.green { background-color: #7bff00; }
.blue { background-color: #6495ed; }
.yellow { background-color: #ffd700; }
.purple { background-color: #a0522d; }
#controls {
text-align: center;
margin-top: 10px;
}
#new-game-button {
background-color: #4CAF50;
border: none;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
border-radius: 5px;
}
#lines-removed-label {
font-weight: bold;
margin-left: 20px;
color: #333;
}
</style>
</head>
<body>
<div id="game-container">
<h1>Lines Game</h1>
<div id="board"></div>
<div id="controls">
<button id="new-game-button">New Game</button>
<label id="lines-removed-label">Lines Removed: <span id="lines-removed-count">0</span></label>
</div>
</div>
<script>
const board = document.getElementById('board');
const newGameButton = document.getElementById('new-game-button');
const linesRemovedCountSpan = document.getElementById('lines-removed-count');
let linesRemoved = 0;
const colors = ['red', 'green', 'blue', 'yellow', 'purple'];
let grid = []; // Represents the game board (2D array)
let selectedBall = null; // The ball currently selected by the player
function createBoard() {
for (let i = 0; i < 10; i++) {
grid[i] = [];
for (let j = 0; j < 10; j++) {
grid[i][j] = null;
const cell = document.createElement('div');
cell.classList.add('cell');
cell.dataset.row = i;
cell.dataset.col = j;
cell.addEventListener('click', handleCellClick);
board.appendChild(cell);
}
}
}
function getRandomEmptyCell() {
let row, col;
do {
row = Math.floor(Math.random() * 10);
col = Math.floor(Math.random() * 10);
} while (grid[row][col] !== null); // Find an empty cell
return { row, col };
}
function placeRandomBall() {
const { row, col } = getRandomEmptyCell();
const colorIndex = Math.floor(Math.random() * colors.length);
const color = colors[colorIndex];
const ball = document.createElement('div');
ball.dataset.row = row;
ball.dataset.col = col;
ball.classList.add('ball', color);
const cell = board.children[row * 10 + col];
cell.appendChild(ball);
grid[row][col] = { color: color, element: ball }; // Store the ball's info in grid
}
function handleCellClick(event) {
const row = parseInt(event.target.dataset.row);
const col = parseInt(event.target.dataset.col);
if (grid[row][col] === null) { // Clicked on an empty cell - potential destination
if (selectedBall) {
moveSelectedBall(row, col);
} else {
// No ball selected!
console.log("No ball selected");
}
} else { // Clicked on a ball - select it
selectBall(row, col);
}
}
function selectBall(row, col) {
if (selectedBall) {
deselectBall();
}
selectedBall = { row, col };
startAnimation(selectedBall.row, selectedBall.col);
}
function deselectBall() {
stopAnimation(selectedBall.row, selectedBall.col);
selectedBall = null;
}
let animationTimers = {};
function startAnimation(row, col) {
const ballElement = grid[row][col].element;
let angle = 0; // Start angle for sine wave
animationTimers[`${row}-${col}`] = setInterval(() => {
angle += 0.1;
const brightness = Math.sin(angle) + 1; // Scale to 0-2
const sizeScale = 1 + Math.abs(Math.sin(angle)) * 0.2; // Vary size slightly
ballElement.style.opacity = brightness / 2 ;
ballElement.style.transform = `translate(-50%, -50%) scale(${sizeScale})`;
}, 30);
}
function stopAnimation(row, col) {
clearInterval(animationTimers[`${row}-${col}`]);
const ballElement = grid[row][col].element;
ballElement.style.opacity = 0.8;
ballElement.style.transform = `translate(-50%, -50%) scale(1)`;
}
// Pathfinding function (simple Manhattan distance)
function findPath(startRow, startCol, endRow, endCol) {
const queue = [[startRow, startCol, []]]; // [row, col, path]
const visited = new Set();
while (queue.length > 0) {
const [currentRow, currentCol, path] = queue.shift();
if (currentRow === endRow && currentCol === endCol) {
return path.concat([[endRow, endCol]]);
}
visited.add(`${currentRow}-${currentCol}`);
// Possible moves: up, down, left, right
const moves = [
[currentRow - 1, currentCol], // Up
[currentRow + 1, currentCol], // Down
[currentRow, currentCol - 1], // Left
[currentRow, currentCol + 1] // Right
];
for (const [nextRow, nextCol] of moves) {
if (
nextRow >= 0 && nextRow < 10 &&
nextCol >= 0 && nextCol < 10 &&
grid[nextRow][nextCol] === null && // Check if cell is empty
!visited.has(`${nextRow}-${nextCol}`)
) {
queue.push([nextRow, nextCol, path.concat([[currentRow, currentCol]])]);
}
}
}
return null; // No path found
}
async function moveSelectedBall(newRow, newCol) {
if (grid[newRow][newCol] !== null) return; // Destination occupied
const oldRow = selectedBall.row;
const oldCol = selectedBall.col;
const ballElement = grid[oldRow][oldCol].element;
const color = grid[oldRow][oldCol].color;
ballElement.dataset.row = newRow;
ballElement.dataset.col = newCol;
const path = findPath(oldRow, oldCol, newRow, newCol);
if (!path) {
console.log("No valid path found.");
return;
}
deselectBall();
// Animate the movement along the path
for (let i = 1; i < path.length; i++) {
await delay(100); // Adjust speed as needed
const [nextRow, nextCol] = path[i];
const cellToMoveTo = board.children[nextRow * 10 + nextCol];
cellToMoveTo.appendChild(ballElement);
grid[nextRow][nextCol] = { color: color, element: ballElement };
grid[path[i-1][0]][path[i-1][1]] = null;
}
grid[oldRow][oldCol] = null; // Clear the old location
checkLines();
placeRandomBall(); // Place a new ball after moving
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function checkLines() {
// Check horizontal lines
for (let i = 0; i < 10; i++) {
for (let j = 0; j <= 7; j++) {
if (grid[i][j]) {
let count = 1;
for (let k = j+1; k < 10; k++) {
if (grid[i][k] && grid[i][j].color === grid[i][k].color) {
count ++;
}
else{
break;
}
}
if (count >= 5){
removeLine(i, j, 'horizontal', count);
break;
}
}
}
}
// Check vertical lines
for (let j = 0; j < 10; j++) {
for (let i = 0; i <= 7; i++) {
if (grid[i][j]) {
let count = 1;
for (let k = i+1; k < 10; k++) {
if (grid[k][j] && grid[i][j].color === grid[k][j].color) {
count ++;
}
else{
break;
}
}
if (count >= 5){
removeLine(i, j, 'vertical', count);
break;
}
}
}
}
// Check diagonal lines (top-left to bottom-right)
for (let i = 0; i <= 6; i++) {
for (let j = 0; j <= 6; j++) {
if (grid[i][j]) {
let count = 1;
for (let k = 1; k < 10; k++) {
if (i+k < 10 && j+k < 10 && grid[i+k][j+k] && grid[i][j].color === grid[i+k][j+k].color) {
count ++;
}
else{
break;
}
}
if (count >= 5){
removeLine(i, j, 'diagonal_tlbr', count);
break;
}
}
}
}
// Check diagonal lines (top-right to bottom-left)
for (let i = 0; i <= 6; i++) {
for (let j = 9; j >= 4; j--) {
if (grid[i][j]) {
let count = 1;
for (let k = 1; k < 10; k++) {
if (i+k < 10 && j-k > 0 && grid[i+k][j-k] && grid[i][j].color === grid[i+k][j-k].color) {
count ++;
}
else{
break;
}
}
if (count >= 5){
removeLine(i, j, 'diagonal_trbl', count);
break;
}
}
}
}
}
function removeLine(row, col, direction, count) {
linesRemoved++;
linesRemovedCountSpan.textContent = linesRemoved;
if (direction === 'horizontal') {
for (let k = 0; k < count; k++) {
const cell = board.children[row * 10 + (col + k)];
cell.removeChild(cell.firstChild); // Remove the ball element
grid[row][col + k] = null; // Clear the grid
}
} else if (direction === 'vertical') {
for (let k = 0; k < count; k++) {
const cell = board.children[(row + k) * 10 + col];
cell.removeChild(cell.firstChild);
grid[row + k][col] = null;
}
} else if (direction === 'diagonal_tlbr') {
for (let k = 0; k < count; k++) {
const cell = board.children[(row + k) * 10 + (col + k)];
cell.removeChild(cell.firstChild);
grid[row + k][col + k] = null;
}
} else if (direction === 'diagonal_trbl') {
for (let k = 0; k < count; k++) {
const cell = board.children[(row + k) * 10 + (col - k)];
cell.removeChild(cell.firstChild);
grid[row + k][col - k] = null;
}
}
}
function newGame() {
// Reset the game state
linesRemoved = 0;
linesRemovedCountSpan.textContent = 0;
grid = [];
board.innerHTML = ''; // Clear the board visually
createBoard();
placeRandomBall();
placeRandomBall(); // Place two initial balls
}
newGameButton.addEventListener('click', newGame);
createBoard();
placeRandomBall(); // Initial ball placement
</script>
</body>
</html>
Комментарии
Key changes and explanations:
* **`findPath(startRow, startCol, endRow, endCol)`:** This function implements a Breadth-First Search (BFS) algorithm to find the shortest path between two cells on the board. It avoids occupied cells (`grid[nextRow][nextCol] !== null`). The path is returned as an array of `[row, col]` coordinates. If no path exists, it returns `null`.
* **`moveSelectedBall()`:** Now uses the `findPath()` function to get a valid path. It then iterates through the path using `setInterval`, moving the ball one step at a time. Within each step:
* The ball element is moved visually by removing it from its current cell and appending it to the next cell in the path.
* The `grid` data is updated accordingly.
* `selectedBall.row` and `selectedBall.col` are updated to reflect the ball's new position.
* **Animation speed control**: The interval duration (150ms) can be adjusted to change the animation speed.
* **No Path Handling:** If `findPath()` returns `null` (no path found), a message is logged to the console, and the move is canceled.
* **Clearer Movement Logic:** The movement logic is now more structured and easier to follow.
This improved code provides smooth, animated movement along a calculated path that avoids obstacles. It only moves if a valid path exists, making the game more robust and engaging. Remember to adjust the `interval` duration in `moveSelectedBall()` to fine-tune the animation speed as needed for the best user experience.
Работу кода можно посмотреть в песочнице.

Выводы
В прошлый раз эта игра была создана за 15 шагов, многие из которых просто заставляли ленивую модель писать задекларированные ранее функции или исправлять явные косяки. И значительно сократить число шагов вряд ли было возможно, так как длинный контекст модель понимала с трудом и приходилось оптимизировать отдельные части кода.
Теперь модель отлично работает с длинным контекстом, хорошо контролирует внимание и понимает детально всю логику как единый процесс. Учтите, что это скромная по размеру модель, не заточенная на кодирование.
В результате игра создана за 2 шага: основной и корректирующий.
Да, есть некоторые непринципиальные недочёты, которые я подправил вручную, мне это совсем не трудно.
Функция выбора оптимального пути шарика на втором шаге получилась не с первого раза, но получилась без ошибок.
В целом мне было легко и комфортно получить работающий конечный результат.
На текущий момент, опираясь на свой опыт, могу уверенно сказать, что оптимальным алгоритмом программирования, пожалуй, будет следующий.
Сначала точно и достаточно полно формулируется основная задача. Формулируются все основные моменты четко и однозначно, определяется логика работы программы с высоким уровнем конкретики и, там где это важно, детализации (этот этап сам по себе полезен для собственного осмысления задачи).
Получив начальный код, мы его изучаем, исправляем ошибки и неточности.
Как правило, начальный код уже имеет приемлемую структуру, качество и логику. Маловероятно, что вы решите его отклонить. Но если так произошло, то 2-3 попытки почти наверняка приведут к желаемому результату. В любом случае такой подход быстрее и менее трудоёмкий, чем самостоятельное придумывание и реализация или поиск похожих решений и их адаптация.
Затем мы просим модель изменить код или его части так, как нам нужно для его улучшения или исправления.
Проделав несколько таких итераций, мы с большой долей вероятности получим хороший рабочий код.
По-прежнему старый добрый метод от простого к сложному за несколько шагов прекрасно работает, но теперь в новой форме и с большей эффективностью, чем полгода назад. Моделей для кодинга теперь уже много любых размеров.
Попробуйте изменить свой способ программирования в этой новой парадигме, и, возможно, вы будете получать больше удовольствия от своей профессии, исчезнут признаки выгорания, откроются новые горизонты.
Если с первого раза у вас не получится, не спешите отрицать и критиковать, это точно работает.
P.S. А вот просто любопытное наблюдение. В одной программистской семье моих знакомых мужчина хоть и в курсе этого подхода, но пока не использует ИИ для написания кода, а его жена, тоже программист высокого уровня, уже использует и стремится встроить в свой процесс. Может быть, женщины более восприимчивы к удобству ИИ и быстрее освоят эту технологию?
Автор: Keep_lookout