diff --git a/src/components/HumanVerification.tsx b/src/components/HumanVerification.tsx index 8a2c441..57d5f04 100644 --- a/src/components/HumanVerification.tsx +++ b/src/components/HumanVerification.tsx @@ -102,14 +102,51 @@ const HumanVerification = ({ onVerified }: HumanVerificationProps) => { ctx.fill(); } - // Matrix-style falling characters - ctx.font = '11px monospace'; - ctx.fillStyle = `hsla(${h}, ${s}%, ${l}%, 0.2)`; - const chars = '01アイウエオカキクケコ田由甲申電网'; - for (let i = 0; i < 40; i++) { + // Matrix-style falling characters with distortion to prevent AI detection + const chars = '01アイウエオカキクケコ田由甲申電网ЖДЯ§€¥£'; + for (let i = 0; i < 50; i++) { const cx = Math.random() * CANVAS_WIDTH; const cy = Math.random() * CANVAS_HEIGHT; - ctx.fillText(chars[Math.floor(Math.random() * chars.length)], cx, cy); + const fontSize = 8 + Math.random() * 8; + const rotation = (Math.random() - 0.5) * 0.6; + const opacity = 0.08 + Math.random() * 0.2; + const skewX = (Math.random() - 0.5) * 0.3; + + ctx.save(); + ctx.translate(cx, cy); + ctx.rotate(rotation); + ctx.transform(1, 0, skewX, 1, 0, 0); // Skew + ctx.font = `${fontSize}px monospace`; + ctx.fillStyle = `hsla(${h}, ${s}%, ${l}%, ${opacity})`; + ctx.fillText(chars[Math.floor(Math.random() * chars.length)], 0, 0); + ctx.restore(); + } + + // Add noise pixels to confuse OCR + const imageData = ctx.getImageData(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); + const data = imageData.data; + for (let i = 0; i < data.length; i += 4) { + if (Math.random() < 0.03) { + const noise = Math.random() * 40; + data[i] += noise; // R + data[i + 1] += noise; // G + data[i + 2] += noise; // B + } + } + ctx.putImageData(imageData, 0, 0); + + // Interference lines crossing through characters + ctx.strokeStyle = `hsla(${h}, ${s}%, ${l}%, 0.08)`; + ctx.lineWidth = 1; + for (let i = 0; i < 8; i++) { + ctx.beginPath(); + ctx.moveTo(0, Math.random() * CANVAS_HEIGHT); + ctx.bezierCurveTo( + CANVAS_WIDTH * 0.3, Math.random() * CANVAS_HEIGHT, + CANVAS_WIDTH * 0.7, Math.random() * CANVAS_HEIGHT, + CANVAS_WIDTH, Math.random() * CANVAS_HEIGHT + ); + ctx.stroke(); } // Generate target position (where the piece should go) @@ -244,14 +281,32 @@ const HumanVerification = ({ onVerified }: HumanVerificationProps) => { ctx.stroke(); } - // Matrix chars - ctx.font = '11px monospace'; - ctx.fillStyle = `hsla(${h}, ${s}%, ${l}%, 0.2)`; - const chars = '01アイウエオ田由甲申'; - for (let i = 0; i < 30; i++) { - ctx.fillText(chars[i % chars.length], (i * 17) % CANVAS_WIDTH, (i * 13) % CANVAS_HEIGHT); + // Matrix chars with distortion + const chars = '01アイウエオ田由甲申ЖДЯ§€¥'; + for (let i = 0; i < 35; i++) { + const fontSize = 8 + (i % 5) * 2; + const rotation = ((i % 7) - 3) * 0.15; + const opacity = 0.08 + (i % 4) * 0.05; + ctx.save(); + ctx.translate((i * 17) % CANVAS_WIDTH, (i * 13) % CANVAS_HEIGHT); + ctx.rotate(rotation); + ctx.font = `${fontSize}px monospace`; + ctx.fillStyle = `hsla(${h}, ${s}%, ${l}%, ${opacity})`; + ctx.fillText(chars[i % chars.length], 0, 0); + ctx.restore(); } + // Add noise and interference + const imgData = ctx.getImageData(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT); + for (let i = 0; i < imgData.data.length; i += 4) { + if (Math.random() < 0.025) { + imgData.data[i] += Math.random() * 30; + imgData.data[i + 1] += Math.random() * 30; + imgData.data[i + 2] += Math.random() * 30; + } + } + ctx.putImageData(imgData, 0, 0); + const pieceY = (CANVAS_HEIGHT - PIECE_HEIGHT) / 2; // Draw target slot