猫の手ツール

Algorithm & Visual Engineering

「解けるパズル」を数学的に保証する
アルゴリズムとCanvas合成技術

スライダーパズルは、単純にタイルをランダムに並べ替えると「数学的に絶対に解けない状態」が50%の確率で発生します。この課題を回避し、かつスムーズな画像処理を実現するためのテクニックを公開します。

1. 逆シミュレーションによる正解可能性の保証

スライダーパズル(15パズル等)には「置換のパリティ」という概念があり、完全にランダムな配置は解けないリスクを孕んでいます。本ツールでは、完成した状態から「空きマスをランダムな隣接タイルと入れ替える」という動作を数百回シミュレートすることで、初期盤面を生成しています。

メリット: 数学的な証明に基づく複雑な転倒数計算を実装することなく、100%確実に解けるパズルを瞬時に生成できます。ユーザーに「絶対に解けないパズル」を渡してしまうという最悪の体験を回避しています。

// 完成状態から「逆算」してシャッフルするアルゴリズムの概要
let emptyIdx = total - 1;

for (let i = 0; i < shuffleCount; i++) {
    // 独自のチューニングに基づいた隣接タイルの抽出
    const neighbors = getValidNeighbors(emptyIdx, gridSize);
    
    // ランダムに一箇所を選んで入れ替える
    const randomNeighbor = neighbors[Math.floor(Math.random() * neighbors.length)];
    
    // ピースの配列要素をスワップ
    [pieces[emptyIdx], pieces[randomNeighbor]] = [pieces[randomNeighbor], pieces[emptyIdx]];
    
    // 空きマスの位置を更新
    emptyIdx = randomNeighbor;
}

2. Canvasによる角丸クリッピングと非破壊合成

クリア時の記録画像生成では、Canvas APIの `roundRect` と `clip` を駆使しています。アップロードされた画像そのものを破壊せず、メモリ上のCanvasで「トリミング」「角丸加工」「統計情報のオーバーレイ」を一気に行います。

メリット: CSSだけでは難しい「画像への動的な文字の焼き付け」と「デザイン性の高い形状(角丸等)」の両立を、クライアントサイドのみで完結させています。外部サーバーへの送信が不要なため、プライバシー保護と処理速度の向上に寄与しています。

// 記録画像の合成描画プロセスの概要
const ctx = canvas.getContext('2d');

// 独自の計算ロジックによるクリッピングパスの作成
ctx.save();
ctx.beginPath();
// 独自の角丸半径とレイアウト座標を適用
ctx.roundRect(x, y, width, height, // 独自のチューニング値);
ctx.clip();

// 写真を指定範囲に収まるよう等倍クロップして描画
ctx.drawImage(uploadedImage, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
ctx.restore();

// 統計情報(タイム等)のレイヤー合成
ctx.fillStyle = brandColor;
// ここに矩形パネルとスタンプ風テキストの描画処理が入ります

3. Background-Position による「タイル表示」の最適化

ゲームプレイ中、各ピースを表示するのに `img` タグを分割して量産するのではなく、1枚の画像を `background-image` に指定した `div` の集合として扱っています。CSSの `background-position` をピースごとのIDに基づいて動的に計算することで、あたかも分割されているかのように見せています。

メリット: DOM要素の数を最小限に抑え、画像の再読み込みやDOMの再生成コストを排除しました。これにより、難易度(3x3, 5x5等)の切り替えがミリ秒単位で完了し、非常にスムーズなUIを実現しています。

// タイルの表示座標を1枚の画像から切り出す計算
const x = (pieceId % gridSize) * // 独自のオフセット係数;
const y = Math.floor(pieceId / gridSize) * // 独自のオフセット係数;

// CSSの座標系へマッピング
innerDiv.style.backgroundPosition = `-${x}% -${y}%`;
innerDiv.style.backgroundSize = `${gridSize * 100}%`;

Developer's Note

スライダーパズルの開発において、最も重視したのは「触り心地の良さ」です。特にスマホでの操作を想定し、タップした瞬間にピースが移動するレスポンスの速さを追求しました。Canvasでのリサイズ処理も、メモリ負荷を考慮して一定の解像度(1000px等)に抑えるようチューニングしています。

また、iPhone独自のHEIC形式はブラウザが直接扱えないため、`heic2any` を用いた動的な変換処理を組み込みました。ユーザーがファイル形式を気にせず、撮ったばかりの写真をすぐパズルにできる。そんな「当たり前」の体験を、すべて端末内完結で実装できたことを誇りに思っています。