🔍 About This Code Showcase
This curated code snippet demonstrates how the Thematic Outline Puzzles transforms any theme into interactive AI-generated puzzle experiences with advanced game mechanics.
Full deployment scripts, API integrations, and proprietary details are omitted for clarity and security. This showcase highlights the core puzzle generation, AI integration, and interactive gameplay algorithms.
🎮 Core Algorithm: Interactive Puzzle Game Engine
The foundation of Thematic Outline Puzzles is its ability to generate themed puzzle images using AI, create interactive puzzle pieces, and manage complex game state with smooth user interactions:
import React, { useState, useCallback } from 'react';
import { PuzzleGenerator } from './components/PuzzleGenerator';
import { PuzzleBoard } from './components/PuzzleBoard';
import { generatePuzzleImage } from './services/geminiService';
import { Difficulty } from './types';
type AppState = 'CONFIG' | 'LOADING' | 'PLAYING';
export default function App() {
const [appState, setAppState] = useState<AppState>('CONFIG');
const [puzzleImage, setPuzzleImage] = useState<string | null>(null);
const [gridSize, setGridSize] = useState<Difficulty>(Difficulty.EASY);
const [theme, setTheme] = useState<string>('');
const handleGeneratePuzzle = useCallback(async (
newTheme: string,
difficulty: Difficulty
) => {
setAppState('LOADING');
setError(null);
setTheme(newTheme);
setGridSize(difficulty);
try {
const prompt = `A vibrant and clear, minimalist outline-style,
cartoon illustration of ${newTheme}. The image should be very simple,
with thick, bold outlines and distinct shapes, suitable for a children's
puzzle. Centered subject, plain background, square aspect ratio.`;
const imageUrl = await generatePuzzleImage(prompt);
setPuzzleImage(imageUrl);
setAppState('PLAYING');
} catch (err) {
setError('Failed to generate image. Please try a different theme.');
setAppState('CONFIG');
console.error(err);
}
}, []);
const handleNewPuzzle = () => {
setAppState('CONFIG');
setPuzzleImage(null);
setError(null);
};
const renderContent = () => {
switch (appState) {
case 'LOADING':
return <Spinner message={`Creating a puzzle about "${theme}"...`} />;
case 'PLAYING':
if (puzzleImage) {
return (
<PuzzleBoard
imageSrc={puzzleImage}
gridSize={gridSize}
onNewPuzzle={handleNewPuzzle}
theme={theme}
/>
);
}
return <PuzzleGenerator onGenerate={handleGeneratePuzzle} error={error} />;
case 'CONFIG':
default:
return <PuzzleGenerator onGenerate={handleGeneratePuzzle} error={error} />;
}
};
return (
<div className="min-h-screen bg-slate-900 text-slate-100 flex flex-col">
<header className="w-full max-w-4xl text-center mb-8">
<h1 className="text-4xl md:text-5xl font-bold gradient-text">
Thematic Outline Puzzles
</h1>
<p className="text-slate-400 text-lg">
Turn any theme into a fun, interactive puzzle.
</p>
</header>
<main>{renderContent()}</main>
</div>
);
}
🤖 AI Image Generation Service
The AI service leverages Imagen 4.0 to create puzzle-optimized illustrations with specific prompting strategies for educational content:
import { GoogleGenAI } from "@google/genai";
if (!process.env.API_KEY) {
throw new Error("API_KEY environment variable not set");
}
const ai = new GoogleGenAI({ apiKey: process.env.API_KEY });
export async function generatePuzzleImage(prompt: string): Promise<string> {
try {
const response = await ai.models.generateImages({
model: 'imagen-4.0-generate-001',
prompt: prompt,
config: {
numberOfImages: 1,
outputMimeType: 'image/png',
aspectRatio: '1:1',
safetyFilterLevel: 'BLOCK_MOST',
},
});
if (response.generatedImages && response.generatedImages.length > 0) {
const base64ImageBytes: string = response.generatedImages[0].image.imageBytes;
return `data:image/png;base64,${base64ImageBytes}`;
} else {
throw new Error("No images were generated by the API.");
}
} catch (error) {
console.error("Error generating image with Gemini AI:", error);
throw new Error("Failed to generate puzzle image.");
}
}
export function createPuzzlePrompt(theme: string, difficulty: Difficulty): string {
const basePrompt = `A vibrant and clear, minimalist outline-style,
cartoon illustration of ${theme}`;
const stylePrompts = {
[Difficulty.EASY]: "very simple with extra thick, bold outlines (4-5px),
large shapes, minimal detail, perfect for toddlers",
[Difficulty.MEDIUM]: "simple with clear, thick outlines (2-3px),
moderate detail, suitable for children",
[Difficulty.HARD]: "detailed with fine outlines (1-2px),
intricate patterns, suitable for adults"
};
return `${basePrompt}. The image should be ${stylePrompts[difficulty]}.
Centered subject, plain white background, square aspect ratio.
Black and white line art style, no shading or color fills.`;
}
🧩 Advanced Puzzle Board Logic
The puzzle board implements sophisticated piece management, drag-and-drop mechanics, and real-time solution detection:
import React, { useState, useEffect, useCallback } from 'react';
import { PuzzlePiece as PuzzlePieceType } from '../types';
interface PuzzleBoardProps {
imageSrc: string;
gridSize: number;
theme: string;
onNewPuzzle: () => void;
}
export function PuzzleBoard({
imageSrc,
gridSize,
theme,
onNewPuzzle
}: PuzzleBoardProps) {
const [pieces, setPieces] = useState<PuzzlePieceType[]>([]);
const [isSolved, setIsSolved] = useState(false);
const [draggedItem, setDraggedItem] = useState<PuzzlePieceType | null>(null);
const shufflePieces = useCallback((arr: PuzzlePieceType[]) => {
const shuffled = [...arr];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[shuffled[i].currentIndex, shuffled[j].currentIndex] =
[shuffled[j].currentIndex, shuffled[i].currentIndex];
}
return shuffled;
}, []);
const initializePuzzle = useCallback(() => {
const newPieces: PuzzlePieceType[] = [];
for (let i = 0; i < gridSize * gridSize; i++) {
const row = Math.floor(i / gridSize);
const col = i % gridSize;
newPieces.push({
id: i,
originalIndex: i,
currentIndex: i,
style: {
backgroundPosition: `${(col * 100) / (gridSize - 1)}% ${(row * 100) / (gridSize - 1)}%`,
backgroundSize: `${gridSize * 100}% ${gridSize * 100}%`,
},
});
}
setPieces(shufflePieces(newPieces));
setIsSolved(false);
}, [gridSize, shufflePieces]);
useEffect(() => {
const checkSolved = () => {
if (pieces.length === 0) return false;
return pieces.every(p => p.currentIndex === p.originalIndex);
};
if (checkSolved()) {
setIsSolved(true);
setTimeout(() => {
alert(`🎉 Congratulations! You solved the "${theme}" puzzle!`);
}, 500);
}
}, [pieces, theme]);
const handleDragStart = (piece: PuzzlePieceType) => {
setDraggedItem(piece);
};
const handleDrop = (targetPiece: PuzzlePieceType) => {
if (!draggedItem || draggedItem.id === targetPiece.id) return;
setPieces(currentPieces => {
const newPieces = [...currentPieces];
const draggedIndex = newPieces.findIndex(p => p.id === draggedItem.id);
const targetIndex = newPieces.findIndex(p => p.id === targetPiece.id);
[newPieces[draggedIndex].currentIndex, newPieces[targetIndex].currentIndex] =
[newPieces[targetIndex].currentIndex, newPieces[draggedIndex].currentIndex];
return newPieces;
});
setDraggedItem(null);
};
useEffect(() => {
initializePuzzle();
}, [initializePuzzle]);
return (
<div className="puzzle-container">
<div
className={`puzzle-grid grid-${gridSize}`}
style={{
gridTemplateColumns: `repeat(${gridSize}, 1fr)`,
gridTemplateRows: `repeat(${gridSize}, 1fr)`
}}
>
{pieces
.sort((a, b) => a.currentIndex - b.currentIndex)
.map(piece => (
<PuzzlePiece
key={piece.id}
piece={piece}
imageSrc={imageSrc}
onDragStart={handleDragStart}
onDrop={handleDrop}
isDragging={draggedItem?.id === piece.id}
/>
))}
</div>
{isSolved && (
<div className="celebration-overlay">
<h2>🎉 Puzzle Complete! 🎉</h2>
<button onClick={onNewPuzzle}>Create New Puzzle</button>
</div>
)}
</div>
);
}