Pascalでプログラミングの練習がてら作ってみた、15パズル風絵合わせです。内容はシンプルで、バラバラになった絵を完成させるスタイル。 集中力が試されるので、ちょっとした頭の体操にもなります!
| タイトル | 15パズル |
|---|---|
| ジャンル | 絵合わせ |
| プレイ人数 | 1人 |
| 動作環境 | Windows11 (64bit) |
| 左クリック | スライドする |
|---|
下のリンクからファイルをダウンロードできます。
※ZIP形式で圧縮されてるので、使う前に解凍してね!
puzzle15.zip
本ソフトは Free Pascal 及び Lazarus を使用して開発されました。
本ソフトは SDL2-for-Pascal を使用しています。SDL2-for-Pascal は Zlib ライセンスの下で利用しています。
本ソフトはライブラリ(SDL2 (Simple DirectMedia Layer))を使用しています。
ゲームを起動すると、バラバラになった絵が表示されます。 元の絵は、400×400サイズのBMPファイルが使用でき、photo.bmpと差し替えれば何でも使うことができます! 左クリックで絵をスライドさせることができるので、頑張って完成させましょう。
{$APPTYPE GUI}
program puzzle15;
uses
SDL2, SysUtils, Math;
const
SCREEN_WIDTH = 400; // ウィンドウの幅
SCREEN_HEIGHT = 400; // ウィンドウの高さ
TILE_SIZE = 100; // 1タイルのサイズ(100x100ピクセル)
SHUFFLE_STEPS = 200; // シャッフルする回数
var
window: PSDL_Window;
renderer: PSDL_Renderer;
sdlEvent: TSDL_Event;
running: Boolean = True; // メインループ実行フラグ
isCleared: Boolean = False; // クリア判定フラグ
photoTexture: PSDL_Texture; // 分割前の元画像テクスチャ
imageSurface: PSDL_Surface;
board: array[0..3, 0..3] of Integer; // パズルの盤面データ(0が空きマス、1-15がタイルの番号)
emptyX, emptyY: Integer; // 現在の空きマスの位置
i, j, tx, ty, val, srcX, srcY: Integer;
srcR, dstR: TSDL_Rect;
{ --- 現在の盤面が正解(1〜15が順に並んでいるか)をチェック --- }
function CheckClear: Boolean;
var
i, j: Integer;
begin
for j := 0 to 3 do
for i := 0 to 3 do
begin
// 右下の最後のマスはチェック対象外(空きマスになるため)
if (i = 3) and (j = 3) then Continue;
// 計算上の正解数値と一致しているか確認
if board[i, j] <> (j * 4 + i + 1) then Exit(False);
end;
Result := True;
end;
{ --- 盤面をランダムに動かしてパズルを生成 --- }
procedure Shuffle;
var
steps, dir, nx, ny: Integer;
begin
Randomize;
steps := 0;
while steps < SHUFFLE_STEPS do
begin
nx := emptyX;
ny := emptyY;
dir := Random(4); // 0〜3の方向をランダム決定
case dir of
0: Dec(ny); // 上に移動
1: Inc(ny); // 下に移動
2: Dec(nx); // 左に移動
3: Inc(nx); // 右に移動
end;
// 移動先が盤面内に収まっている場合、空きマスと中身を入れ替える
if (nx >= 0) and (nx < 4) and (ny >= 0) and (ny < 4) then
begin
board[emptyX, emptyY] := board[nx, ny];
board[nx, ny] := 0;
emptyX := nx;
emptyY := ny;
Inc(steps);
end;
end;
end;
{ --- メインプログラム開始 --- }
begin
// SDL2の初期化
if SDL_Init(SDL_INIT_VIDEO) < 0 then Exit;
// ウィンドウとレンダラーの作成
window := SDL_CreateWindow('SDL2 15 Puzzle',
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
renderer := SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
// 画像の読み込み(ビットマップ形式)
imageSurface := SDL_LoadBMP('photo.bmp');
if imageSurface = nil then
begin
WriteLn('Error: photo.bmp not found!');
running := False; // 画像がない場合は実行を中止
end;
if running then
begin
// サーフェスをテクスチャに変換し、不要になったサーフェスを解放
photoTexture := SDL_CreateTextureFromSurface(renderer, imageSurface);
SDL_FreeSurface(imageSurface);
// 盤面の初期化(正解の形に並べる)
for j := 0 to 3 do
for i := 0 to 3 do
board[i, j] := (j * 4) + i + 1;
board[3, 3] := 0; // 右下を空きマスに設定
emptyX := 3;
emptyY := 3;
Shuffle; // パズルをバラバラにする
end;
{ --- メインイベントループ --- }
while running do
begin
while SDL_PollEvent(@sdlEvent) <> 0 do
begin
case sdlEvent.type_ of
// ウィンドウの×ボタンが押されたとき
SDL_QUITEV: running := False;
// マウスクリック時の処理
SDL_MOUSEBUTTONDOWN:
if not isCleared then // クリア後は操作不可
begin
// クリックされたマスのインデックスを算出
tx := sdlEvent.button.x div TILE_SIZE;
ty := sdlEvent.button.y div TILE_SIZE;
// 空きマスに隣接しているか判定(マンハッタン距離が1なら隣)
if (Abs(tx - emptyX) + Abs(ty - emptyY) = 1) then
begin
// 入れ替え処理
board[emptyX, emptyY] := board[tx, ty];
board[tx, ty] := 0;
emptyX := tx;
emptyY := ty;
// 移動ごとにクリアチェック
if CheckClear then
begin
isCleared := True;
SDL_SetWindowTitle(window, '15 Puzzle - GAME CLEAR!');
end;
end;
end;
end;
end;
{ --- 描画処理 --- }
SDL_SetRenderDrawColor(renderer, 30, 30, 30, 255); // 背景色(暗いグレー)
SDL_RenderClear(renderer);
for j := 0 to 3 do
for i := 0 to 3 do
begin
val := board[i, j];
// クリア時は空きマス(0)の部分に本来の16枚目のピース(右下隅)を描画する
if (val = 0) and isCleared then val := 16;
if val <> 0 then
begin
// 画像のどの部分を切り出すか計算 (Source Rect)
srcX := (val - 1) mod 4;
srcY := (val - 1) div 4;
srcR.x := srcX * TILE_SIZE;
srcR.y := srcY * TILE_SIZE;
srcR.w := TILE_SIZE;
srcR.h := TILE_SIZE;
// 画面上のどこに描画するか計算 (Destination Rect)
dstR.x := i * TILE_SIZE;
dstR.y := j * TILE_SIZE;
dstR.w := TILE_SIZE;
dstR.h := TILE_SIZE;
// 元画像から指定範囲を切り取って盤面にコピー
SDL_RenderCopy(renderer, photoTexture, @srcR, @dstR);
end;
end;
SDL_RenderPresent(renderer); // 画面を更新
SDL_Delay(16); // 約60FPSを維持するための待機
end;
{ --- 終了処理 --- }
if photoTexture <> nil then SDL_DestroyTexture(photoTexture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit;
end. |