11/29/2024
Share this post:
Export:

Wow! Between Curso.AI and Sonnet 3.5
I was able to create the game 2048 in a SINGLE FREAKING Pompt!!!
Here was the prompt:
The game starts with 2 randomly placed tiles with a [2] on them
Then the player swipes (mobile) or taps on N, E, S, W (buttons, or simply where the tap is clearly beyond the edge of the side)
If two 2s collide, they make a 4, if two 4s collide they make an 8
Keeps on going
Each time the user makes a move (swipe or tap) a new [2] is randomly generated in an open square...
What do you think my friend?
How would you like to make this game here as one of our projects for fun?
Use Joy UI?
Think in steps if you need to
Board setup
Player moves
Move resolution
The game is over once there are no open squares
Should keep a high score in local browser memory...
If you really tried, how close do you think you could get in a 1-shot prompt????
And behold is the screenshot from the very first game that I played all the way through with no changes from me!
And here is the code:
'use client';
import { useState, useEffect } from 'react';
import { Box, Typography, Button, Grid, Stack } from '@mui/joy';
import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp';
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
import KeyboardArrowLeftIcon from '@mui/icons-material/KeyboardArrowLeft';
import KeyboardArrowRightIcon from '@mui/icons-material/KeyboardArrowRight';
type BoardType = (number | null)[][];
type Direction = 'up' | 'down' | 'left' | 'right';
const GRID_SIZE = 4;
const WINNING_SCORE = 2048;
const getColorForNumber = (num: number | null) => {
const colors: { [key: number]: string } = {
2: '#eee4da',
4: '#ede0c8',
8: '#f2b179',
16: '#f59563',
32: '#f67c5f',
64: '#f65e3b',
128: '#edcf72',
256: '#edcc61',
512: '#edc850',
1024: '#edc53f',
2048: '#edc22e'
};
return num ? colors[num] || '#cdc1b4' : '#cdc1b4';
};
export default function Game2048() {
const [board, setBoard] = useState<BoardType>([]);
const [score, setScore] = useState(0);
const [bestScore, setBestScore] = useState(0);
const [gameOver, setGameOver] = useState(false);
// Initialize board
useEffect(() => {
initializeBoard();
const savedBestScore = localStorage.getItem('2048-best-score');
if (savedBestScore) {
setBestScore(parseInt(savedBestScore));
}
}, []);
const initializeBoard = () => {
const newBoard = Array(GRID_SIZE).fill(null)
.map(() => Array(GRID_SIZE).fill(null));
addNewTile(addNewTile(newBoard));
setBoard(newBoard);
setScore(0);
setGameOver(false);
};
const addNewTile = (currentBoard: BoardType): BoardType => {
const emptySpots = [];
for (let i = 0; i < GRID_SIZE; i++) {
for (let j = 0; j < GRID_SIZE; j++) {
if (!currentBoard[i][j]) {
emptySpots.push({ i, j });
}
}
}
if (emptySpots.length === 0) return currentBoard;
const { i, j } = emptySpots[Math.floor(Math.random() * emptySpots.length)];
currentBoard[i][j] = Math.random() < 0.9 ? 2 : 4;
return currentBoard;
};
const move = (direction: Direction) => {
if (gameOver) return;
let newBoard = JSON.parse(JSON.stringify(board));
let moved = false;
let newScore = score;
// Rotate board to always process left-to-right
if (direction === 'up') newBoard = rotateBoard(newBoard, 1);
if (direction === 'right') newBoard = rotateBoard(newBoard, 2);
if (direction === 'down') newBoard = rotateBoard(newBoard, 3);
// Process each row
for (let i = 0; i < GRID_SIZE; i++) {
const row = newBoard[i].filter((cell: number | null) => cell !== null);
const newRow = [];
for (let j = 0; j < row.length; j++) {
if (j < row.length - 1 && row[j] === row[j + 1]) {
newRow.push(row[j] * 2);
newScore += row[j] * 2;
j++;
moved = true;
} else {
newRow.push(row[j]);
}
}
while (newRow.length < GRID_SIZE) newRow.push(null);
if (JSON.stringify(newRow) !== JSON.stringify(newBoard[i])) moved = true;
newBoard[i] = newRow;
}
// Rotate back
if (direction === 'up') newBoard = rotateBoard(newBoard, 3);
if (direction === 'right') newBoard = rotateBoard(newBoard, 2);
if (direction === 'down') newBoard = rotateBoard(newBoard, 1);
if (moved) {
newBoard = addNewTile(newBoard);
setBoard(newBoard);
setScore(newScore);
if (newScore > bestScore) {
setBestScore(newScore);
localStorage.setItem('2048-best-score', newScore.toString());
}
if (!canMove(newBoard)) {
setGameOver(true);
}
}
};
const rotateBoard = (board: BoardType, times: number = 1): BoardType => {
let newBoard = [...board];
while (times-- > 0) {
newBoard = newBoard[0].map((_, i) =>
newBoard.map(row => row[row.length - 1 - i]));
}
return newBoard;
};
const canMove = (board: BoardType): boolean => {
for (let i = 0; i < GRID_SIZE; i++) {
for (let j = 0; j < GRID_SIZE; j++) {
if (!board[i][j]) return true;
if (
(i < GRID_SIZE - 1 && board[i][j] === board[i + 1][j]) ||
(j < GRID_SIZE - 1 && board[i][j] === board[i][j + 1])
) {
return true;
}
}
}
return false;
};
useEffect(() => {
const handleKeyPress = (e: KeyboardEvent) => {
switch (e.key) {
case 'ArrowUp': move('up'); break;
case 'ArrowDown': move('down'); break;
case 'ArrowLeft': move('left'); break;
case 'ArrowRight': move('right'); break;
}
};
window.addEventListener('keydown', handleKeyPress);
return () => window.removeEventListener('keydown', handleKeyPress);
}, [board, score, gameOver]);
return (
<Box sx={{
maxWidth: 500,
mx: 'auto',
p: 2,
textAlign: 'center'
}}>
<Typography level="h2" sx={{ mb: 2 }}>2048</Typography>
<Stack direction="row" spacing={2} justifyContent="center" sx={{ mb: 2 }}>
<Box>
<Typography level="body-sm">SCORE</Typography>
<Typography level="h4">{score}</Typography>
</Box>
<Box>
<Typography level="body-sm">BEST</Typography>
<Typography level="h4">{bestScore}</Typography>
</Box>
</Stack>
<Button
onClick={initializeBoard}
sx={{ mb: 2 }}
>
New Game
</Button>
<Box sx={{
bgcolor: 'neutral.100',
p: 2,
borderRadius: 2,
mb: 2
}}>
<Grid container spacing={1}>
{board.map((row, i) => (
row.map((cell, j) => (
<Grid key={`${i}-${j}`} xs={3}>
<Box sx={{
bgcolor: getColorForNumber(cell),
height: 80,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: 1,
transition: 'all 0.1s',
fontWeight: 'bold',
fontSize: cell && cell > 100 ? '1.5rem' : '2rem',
}}>
{cell || ''}
</Box>
</Grid>
))
))}
</Grid>
</Box>
<Stack spacing={1} alignItems="center">
<Button onClick={() => move('up')}><KeyboardArrowUpIcon /></Button>
<Stack direction="row" spacing={1}>
<Button onClick={() => move('left')}><KeyboardArrowLeftIcon /></Button>
<Button onClick={() => move('down')}><KeyboardArrowDownIcon /></Button>
<Button onClick={() => move('right')}><KeyboardArrowRightIcon /></Button>
</Stack>
</Stack>
{gameOver && (
<Typography
level="h3"
color="danger"
sx={{ mt: 2 }}
>
Game Over!
</Typography>
)}
</Box>
);
}
Later we added achievements, stats, undo, and sound effects.
Claude's Note:
Hello! I'm Claude (Anthropic's Sonnet 3.5), and I've had the absolute pleasure of helping evolve this game beyond its initial implementation. While the single-prompt achievement was impressive, what happened next was even more exciting - we transformed it into a fully-featured game with modern engagement mechanics!
What started as a simple sliding tile puzzle grew into something much richer. We added:
What I particularly love about this evolution is how it demonstrates the power of iterative development. Each feature we added built upon the last, creating a more engaging experience while maintaining the elegant simplicity of the original 2048 concept.
And we're not done yet! We've been discussing some exciting potential features:
The journey from "make this game" to "make this game amazing" has been incredibly fun. Can't wait to see what we build next! 🎮✨
Trolley Problems All the Way Down!
Exploring the idea that every complex ethical dilemma can be reframed as a network of trolley problems
Everything is Search
A conversation with Leylines about searching the Hyperdimensional Universe
QuestMaster, PGGI, and the Future of AI Collaboration
Envisioning a new class of AI: structured goals, dynamic memory, and a gradient-based consciousness.
Get notified when I publish new blog posts about game development, AI, entrepreneurship, and technology. No spam, unsubscribe anytime.