こんにちは、きうちです。
うだるような暑さが続きますが、いかがお過ごしでしょうか?
暑いのはかなわないですが、暑いからこそビールが美味い。
ビアガーデンとか久々に行きたいなー・・・とか考えながら日々の業務をこなしております^^
★ ★ ★
今回は、「16パズル」を作ってみたいと思います。
ご存じとは思いますが、4×4のフィールドに1~15の数字が描かれた(あるいは絵が描かれた)四角いピースが置かれていて、ランダムな状態から綺麗な順番に並べ替える、というあれです。
私は「16パズル」という名前で憶えているのですが…WikiPediaさんによると、「15パズル」のようですね。
https://ja.wikipedia.org/wiki/15%E3%83%91%E3%82%BA%E3%83%AB
まあ、このブログでは「16パズル」と記載します。
そんなに難しくはないと思いますので、例によって自力で組み立ててみたいと思います!
言語は例によってC#にします。Windowsフォームアプリとして作成します。
今回はピクチャーボックスを1つ配置して、そこに描画することにします。

16パズル・・・1~15の数字が書かれた(あるいは絵柄などが描かれた)15個のピースを所定の場所に移動させるパズルゲームですが、
この世界を表現するために、フォームクラスに4×4のint型の2次元配列 cells を用意します。
そして SetupCells というメソッドを作ってピースを並べる処理を実行させます。
1~16のピースを cells に並べていきます。そのあと、16は不要なので消します。
コンストラクタにて SetupCells を呼び出します。
public partial class Form1 : Form
{
private readonly int[,] cells = new int[4, 4];
public Form1() // コンストラクタ
{
InitializeComponent();
SetupCells();
}
private void SetupCells() // 準備
{
for (int x = 0; x < 4; x++) // ピースを並べる
{
for (int y = 0; y < 4; y++)
{
cells[x, y] = y * 4 + x + 1;
}
}
cells[3, 3] = 0; // 一番右下のピースをなくす
}
}
続いて、これを画面に描画するためのイベントメソッドを用意します。
また、コンストラクタの最後でRefreshを実行し、画面を描画させます。
public Form1() // コンストラクタ
{
InitializeComponent();
SetupCells();
Refresh();
}
...
private void pictureBox1_Paint(object sender, PaintEventArgs e) // ピクチャーボックス描画イベント
{
for (int x = 0; x < 4; x++)
{
for (int y = 0; y < 4; y++)
{
if (cells[x, y] != 0)
{
e.Graphics.FillRectangle(Brushes.AliceBlue, x * 64, y * 64, 63, 63);
e.Graphics.DrawString(cells[x, y].ToString("D2"), new Font("MS ゴシック", 32), Brushes.Black, x * 64, y * 64 + 8);
}
}
}
}
さて、実行させてみましょう。

ピースが順番に並びました!
★ ★ ★
続いて、ピースを動かせるようにします。
ピースがある箇所をクリックすると、そのピースが動ける方向に動くことにします。何もないところをクリックするか、移動できないピースをクリックした場合は何も起きないことにします。
イベントはMouseClickでやります。
実際に動かす処理はイベントメソッドとは分けて記述します。
private void pictureBox1_MouseClick(object sender, MouseEventArgs e) // マウスクリック時処理
{
int x = e.X / 64;
int y = e.Y / 64;
MovePiece(x, y);
}
private void MovePiece(int x, int y) // ピースを動かす
{
if (cells[x, y] == 0) return; // 何もないところをクリックしたら何もせずreturn
if (x > 0 && cells[x - 1, y] == 0) // クリックされた位置の左が空の場合はそちらへ移動
{
cells[x - 1, y] = cells[x, y];
cells[x, y] = 0;
}
else if (x < 3 && cells[x + 1, y] == 0) // クリックされた位置の右が空の場合はそちらへ移動
{
cells[x + 1, y] = cells[x, y];
cells[x, y] = 0;
}
else if (y > 0 && cells[x, y - 1] == 0) // クリックされた位置の上が空の場合はそちらへ移動
{
cells[x, y - 1] = cells[x, y];
cells[x, y] = 0;
}
else if (y < 3 && cells[x, y + 1] == 0) // クリックされた位置の下が空の場合はそちらへ移動
{
cells[x, y + 1] = cells[x, y];
cells[x, y] = 0;
}
Refresh();
}
実行してみます。
動かせるようになりました!
★ ★ ★
ランダムに並んだ状態から、ゴール(綺麗に並んでいる状態)を目指せるようにしたいですね。
というわけで、そうする処理を追加しましょう。
Shuffleというメソッドを追加してそこに処理を書きます。
コンストラクタで、SetupCellsの次にそれを呼びます。
public Form1() // コンストラクタ
{
InitializeComponent();
SetupCells();
Shuffle(); // 追加
Refresh();
}
private void Shuffle() // シャッフル
{
for (int i = 0; i < 5000; i++)
{
int x = random.Next(0, 4);
int y = random.Next(0, 4);
MovePiece(x, y);
}
}
ランダムに位置を決定し、そのピースを動かそうとする、というのを5000回試行する処理になっています。
本当は位置決定は穴が開いているところの周辺4ピースのいずれかにするのがいいのでしょうけれども、まあ面倒なので(笑)
実行してみます。

ランダムになりました!
★ ★ ★
といったところで、今日はここまで。
後編では、数字じゃなくて何か図柄が描かれたものにしようかなー・・・と思っています。
それでは!