goo blog サービス終了のお知らせ 

marunomaruno-memo

marunomaruno-memo

[Android] 15 パズル (1) - テーブル・レイアウト

2012年02月03日 | Android
                                                                    2012-02-03
[Android] 15 パズル (1) - テーブル・レイアウト
================================================================================

15 パズル。
テーブル・レイアウトを使って、15 個の数字ボタンと、空白ボタンを配置。
アルゴリズムはべた。

解ける問題のみを生成する。これには、
[Java] 対称群と置換のユーティリティ
http://blog.goo.ne.jp/marunomarunogoo/d/20120106
を使う。15パズルが解けるのは偶置換のときなので、ここで作ったユーティリティ 
SymmetricGroupUtil を使う。

なお、このユーティリティと合わせて、配列やコレクション関係のユーティリティ 
ArrayCollectionUtil も追加する。


今回作ったクラスはつぎの 3 つ。
FifteenPuzzleActivity    アクティビティ
ArrayCollectionUtil      配列やコレクション関係のユーティリティ(*)(**)
SymmetricGroupUtil       対称群と置換のユーティリティ(*)

(*) ユーティリティ関係として、パッケージは jp.marunomaruno.android.util とした。
(**) 上記「[Java] 対称群と置換のユーティリティ」とほぼ同じ


■ アクティビティ

すべてこのアクティビティのクラスでロジックを構築。
1~15 のボタンと、空のブロックに対するボタン 16 個に対して、すべてハンドラー・メ
ソッドをつけた。

基本的な考えは、ボタンの位置はすべてハンドラー・メソッドになっているので、これで
わかる。空ブロックの位置を覚えておき、数字ブロックと空ブロックの間のブロックを
ローテートする。


□ FifteenPuzzleActivity
---
package jp.marunomaruno.android.fifteenpuzzle;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import jp.marunomaruno.android.util.ArrayCollectionUtil;
import jp.marunomaruno.android.util.SymmetricGroupUtil;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

/**
 * 15パズルのアクティビティ
 * @author marunomaruno
 */
public class FifteenPuzzleActivity extends Activity {
    public static final int ORDER = 4; // 4次の正方行列    // (1)

    private Button[] numberButtons;    // 数字ボタン    // (2)
    private int emptyBlockIndex = ORDER * ORDER; // 空ブロックのインデックス // (3)

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        numberButtons = new Button[] {
                null, // ボード上の番号と合わせるため、インデックス0をダミーとする // (4)
                (Button) findViewById(R.id.numberButton1),
                (Button) findViewById(R.id.numberButton2),
                (Button) findViewById(R.id.numberButton3),
                (Button) findViewById(R.id.numberButton4),
                (Button) findViewById(R.id.numberButton5),
                (Button) findViewById(R.id.numberButton6),
                (Button) findViewById(R.id.numberButton7),
                (Button) findViewById(R.id.numberButton8),
                (Button) findViewById(R.id.numberButton9),
                (Button) findViewById(R.id.numberButton10),
                (Button) findViewById(R.id.numberButton11),
                (Button) findViewById(R.id.numberButton12),
                (Button) findViewById(R.id.numberButton13),
                (Button) findViewById(R.id.numberButton14),
                (Button) findViewById(R.id.numberButton15),
                (Button) findViewById(R.id.numberButton16), 
            };

        log("initialize() ");
    }

    /**
     * インデックス 1 ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickNumberButton1(View v) {    // (5)
        final int BLOCK_INDEX = 1;
        Button button = (Button) v;
        System.out.printf("onClickNumberButton%d() %s %d%n", BLOCK_INDEX,
                button.getText(), emptyBlockIndex);

        // このブロックが空なら何もしない
        if (emptyBlockIndex == BLOCK_INDEX) {    // (6)
            return;
        }

        // 空ブロックが右にあるとき、右にローテートする
        if (isEmptyBlockIndexIn(2, 3, 4)) {        // (7)
            rotateRight(BLOCK_INDEX);    // (8)
            return;
        }

        // 空ブロックが下にあるとき、下にローテートする
        if (isEmptyBlockIndexIn(5, 9, 13)) {
            rotateDown(BLOCK_INDEX);    // (9)
            return;
        }
    }

    /**
     * インデックス 2 ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickNumberButton2(View v) {
        final int BLOCK_INDEX = 2;
        Button button = (Button) v;
        System.out.printf("onClickNumberButton%d() %s %d%n", BLOCK_INDEX,
                button.getText(), emptyBlockIndex);

        // このブロックが空なら何もしない
        if (emptyBlockIndex == BLOCK_INDEX) {
            return;
        }

        // 空ブロックが左にあるとき、左にローテートする
        if (isEmptyBlockIndexIn(1)) {
            rotateLeft(BLOCK_INDEX);
            return;
        }

        // 空ブロックが右にあるとき、右にローテートする
        if (isEmptyBlockIndexIn(3, 4)) {
            rotateRight(BLOCK_INDEX);
            return;
        }

        // 空ブロックが下にあるとき、下にローテートする
        if (isEmptyBlockIndexIn(6, 10, 14)) {
            rotateDown(BLOCK_INDEX);
            return;
        }
    }

    /**
     * インデックス 3 ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickNumberButton3(View v) {
        final int BLOCK_INDEX = 3;
        Button button = (Button) v;
        System.out.printf("onClickNumberButton%d() %s %d%n", BLOCK_INDEX,
                button.getText(), emptyBlockIndex);

        // このブロックが空なら何もしない
        if (emptyBlockIndex == BLOCK_INDEX) {
            return;
        }

        // 空ブロックが左にあるとき、左にローテートする
        if (isEmptyBlockIndexIn(1, 2)) {
            rotateLeft(BLOCK_INDEX);
            return;
        }

        // 空ブロックが右にあるとき、右にローテートする
        if (isEmptyBlockIndexIn(4)) {
            rotateRight(BLOCK_INDEX);
            return;
        }

        // 空ブロックが下にあるとき、下にローテートする
        if (isEmptyBlockIndexIn(7, 11, 15)) {
            rotateDown(BLOCK_INDEX);
            return;
        }
    }

    /**
     * インデックス 4 ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickNumberButton4(View v) {
        final int BLOCK_INDEX = 4;
        Button button = (Button) v;
        System.out.printf("onClickNumberButton%d() %s %d%n", BLOCK_INDEX,
                button.getText(), emptyBlockIndex);

        // このブロックが空なら何もしない
        if (emptyBlockIndex == BLOCK_INDEX) {
            return;
        }

        // 空ブロックが左にあるとき、左にローテートする
        if (isEmptyBlockIndexIn(1, 2, 3)) {
            rotateLeft(BLOCK_INDEX);
            return;
        }

        // 空ブロックが下にあるとき、下にローテートする
        if (isEmptyBlockIndexIn(8, 12, 16)) {
            rotateDown(BLOCK_INDEX);
            return;
        }
    }

    /**
     * インデックス 5 ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickNumberButton5(View v) {
        final int BLOCK_INDEX = 5;
        Button button = (Button) v;
        System.out.printf("onClickNumberButton%d() %s %d%n", BLOCK_INDEX,
                button.getText(), emptyBlockIndex);

        // このブロックが空なら何もしない
        if (emptyBlockIndex == BLOCK_INDEX) {
            return;
        }

        // 空ブロックが右にあるとき、右にローテートする
        if (isEmptyBlockIndexIn(6, 7, 8)) {
            rotateRight(BLOCK_INDEX);
            return;
        }

        // 空ブロックが上にあるとき、上にローテートする
        if (isEmptyBlockIndexIn(1)) {
            rotateUp(BLOCK_INDEX);
            return;
        }

        // 空ブロックが下にあるとき、下にローテートする
        if (isEmptyBlockIndexIn(9, 13)) {
            rotateDown(BLOCK_INDEX);
            return;
        }
    }

    /**
     * インデックス 6 ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickNumberButton6(View v) {
        final int BLOCK_INDEX = 6;
        Button button = (Button) v;
        System.out.printf("onClickNumberButton%d() %s %d%n", BLOCK_INDEX,
                button.getText(), emptyBlockIndex);

        // このブロックが空なら何もしない
        if (emptyBlockIndex == BLOCK_INDEX) {
            return;
        }

        // 空ブロックが左にあるとき、左にローテートする
        if (isEmptyBlockIndexIn(5)) {
            rotateLeft(BLOCK_INDEX);
            return;
        }

        // 空ブロックが右にあるとき、右にローテートする
        if (isEmptyBlockIndexIn(7, 8)) {
            rotateRight(BLOCK_INDEX);
            return;
        }

        // 空ブロックが上にあるとき、上にローテートする
        if (isEmptyBlockIndexIn(2)) {
            rotateUp(BLOCK_INDEX);
            return;
        }

        // 空ブロックが下にあるとき、下にローテートする
        if (isEmptyBlockIndexIn(10, 14)) {
            rotateDown(BLOCK_INDEX);
            return;
        }
    }

    /**
     * インデックス 7 ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickNumberButton7(View v) {
        final int BLOCK_INDEX = 7;
        Button button = (Button) v;
        System.out.printf("onClickNumberButton%d() %s %d%n", BLOCK_INDEX,
                button.getText(), emptyBlockIndex);

        // このブロックが空なら何もしない
        if (emptyBlockIndex == BLOCK_INDEX) {
            return;
        }

        // 空ブロックが左にあるとき、左にローテートする
        if (isEmptyBlockIndexIn(5, 6)) {
            rotateLeft(BLOCK_INDEX);
            return;
        }

        // 空ブロックが右にあるとき、右にローテートする
        if (isEmptyBlockIndexIn(8)) {
            rotateRight(BLOCK_INDEX);
            return;
        }

        // 空ブロックが上にあるとき、上にローテートする
        if (isEmptyBlockIndexIn(3)) {
            rotateUp(BLOCK_INDEX);
            return;
        }

        // 空ブロックが下にあるとき、下にローテートする
        if (isEmptyBlockIndexIn(11, 15)) {
            rotateDown(BLOCK_INDEX);
            return;
        }
    }

    /**
     * インデックス 8ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickNumberButton8(View v) {
        final int BLOCK_INDEX = 8;
        Button button = (Button) v;
        System.out.printf("onClickNumberButton%d() %s %d%n", BLOCK_INDEX,
                button.getText(), emptyBlockIndex);

        // このブロックが空なら何もしない
        if (emptyBlockIndex == BLOCK_INDEX) {
            return;
        }

        // 空ブロックが左にあるとき、左にローテートする
        if (isEmptyBlockIndexIn(5, 6, 7)) {
            rotateLeft(BLOCK_INDEX);
            return;
        }

        // 空ブロックが上にあるとき、上にローテートする
        if (isEmptyBlockIndexIn(4)) {
            rotateUp(BLOCK_INDEX);
            return;
        }

        // 空ブロックが下にあるとき、下にローテートする
        if (isEmptyBlockIndexIn(12, 16)) {
            rotateDown(BLOCK_INDEX);
            return;
        }
    }

    /**
     * インデックス 9 ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickNumberButton9(View v) {
        final int BLOCK_INDEX = 9;
        Button button = (Button) v;
        System.out.printf("onClickNumberButton%d() %s %d%n", BLOCK_INDEX,
                button.getText(), emptyBlockIndex);

        // このブロックが空なら何もしない
        if (emptyBlockIndex == BLOCK_INDEX) {
            return;
        }

        // 空ブロックが右にあるとき、右にローテートする
        if (isEmptyBlockIndexIn(10, 11, 12)) {
            rotateRight(BLOCK_INDEX);
            return;
        }

        // 空ブロックが上にあるとき、上にローテートする
        if (isEmptyBlockIndexIn(1, 5)) {
            rotateUp(BLOCK_INDEX);
            return;
        }

        // 空ブロックが下にあるとき、下にローテートする
        if (isEmptyBlockIndexIn(13)) {
            rotateDown(BLOCK_INDEX);
            return;
        }
    }

    /**
     * インデックス 10 ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickNumberButton10(View v) {
        final int BLOCK_INDEX = 10;
        Button button = (Button) v;
        System.out.printf("onClickNumberButton%d() %s %d%n", BLOCK_INDEX,
                button.getText(), emptyBlockIndex);

        // このブロックが空なら何もしない
        if (emptyBlockIndex == BLOCK_INDEX) {
            return;
        }

        // 空ブロックが左にあるとき、左にローテートする
        if (isEmptyBlockIndexIn(9)) {
            rotateLeft(BLOCK_INDEX);
            return;
        }

        // 空ブロックが右にあるとき、右にローテートする
        if (isEmptyBlockIndexIn(11, 12)) {
            rotateRight(BLOCK_INDEX);
            return;
        }

        // 空ブロックが上にあるとき、上にローテートする
        if (isEmptyBlockIndexIn(2, 6)) {
            rotateUp(BLOCK_INDEX);
            return;
        }

        // 空ブロックが下にあるとき、下にローテートする
        if (isEmptyBlockIndexIn(14)) {
            rotateDown(BLOCK_INDEX);
            return;
        }
    }

    /**
     * インデックス 11 ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickNumberButton11(View v) {
        final int BLOCK_INDEX = 11;
        Button button = (Button) v;
        System.out.printf("onClickNumberButton%d() %s %d%n", BLOCK_INDEX,
                button.getText(), emptyBlockIndex);

        // このブロックが空なら何もしない
        if (emptyBlockIndex == BLOCK_INDEX) {
            return;
        }

        // 空ブロックが左にあるとき、左にローテートする
        if (isEmptyBlockIndexIn(9, 10)) {
            rotateLeft(BLOCK_INDEX);
            return;
        }

        // 空ブロックが右にあるとき、右にローテートする
        if (isEmptyBlockIndexIn(12)) {
            rotateRight(BLOCK_INDEX);
            return;
        }

        // 空ブロックが上にあるとき、上にローテートする
        if (isEmptyBlockIndexIn(3, 7)) {
            rotateUp(BLOCK_INDEX);
            return;
        }

        // 空ブロックが下にあるとき、下にローテートする
        if (isEmptyBlockIndexIn(15)) {
            rotateDown(BLOCK_INDEX);
            return;
        }
    }

    /**
     * インデックス 12 ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickNumberButton12(View v) {
        final int BLOCK_INDEX = 12;
        Button button = (Button) v;
        System.out.printf("onClickNumberButton%d() %s %d%n", BLOCK_INDEX,
                button.getText(), emptyBlockIndex);

        // このブロックが空なら何もしない
        if (emptyBlockIndex == BLOCK_INDEX) {
            return;
        }

        // 空ブロックが左にあるとき、左にローテートする
        if (isEmptyBlockIndexIn(9, 10, 11)) {
            rotateLeft(BLOCK_INDEX);
            return;
        }

        // 空ブロックが上にあるとき、上にローテートする
        if (isEmptyBlockIndexIn(4, 8)) {
            rotateUp(BLOCK_INDEX);
            return;
        }

        // 空ブロックが下にあるとき、下にローテートする
        if (isEmptyBlockIndexIn(16)) {
            rotateDown(BLOCK_INDEX);
            return;
        }
    }

    /**
     * インデックス 13 ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickNumberButton13(View v) {
        final int BLOCK_INDEX = 13;
        Button button = (Button) v;
        System.out.printf("onClickNumberButton%d() %s %d%n", BLOCK_INDEX,
                button.getText(), emptyBlockIndex);

        // このブロックが空なら何もしない
        if (emptyBlockIndex == BLOCK_INDEX) {
            return;
        }

        // 空ブロックが右にあるとき、右にローテートする
        if (isEmptyBlockIndexIn(14, 15, 16)) {
            rotateRight(BLOCK_INDEX);
            return;
        }

        // 空ブロックが上にあるとき、上にローテートする
        if (isEmptyBlockIndexIn(1, 5, 9)) {
            rotateUp(BLOCK_INDEX);
            return;
        }
    }

    /**
     * インデックス 14 ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickNumberButton14(View v) {
        final int BLOCK_INDEX = 14;
        Button button = (Button) v;
        System.out.printf("onClickNumberButton%d() %s %d%n", BLOCK_INDEX,
                button.getText(), emptyBlockIndex);

        // このブロックが空なら何もしない
        if (emptyBlockIndex == BLOCK_INDEX) {
            return;
        }

        // 空ブロックが左にあるとき、左にローテートする
        if (isEmptyBlockIndexIn(13)) {
            rotateLeft(BLOCK_INDEX);
            return;
        }

        // 空ブロックが右にあるとき、右にローテートする
        if (isEmptyBlockIndexIn(15, 16)) {
            rotateRight(BLOCK_INDEX);
            return;
        }

        // 空ブロックが上にあるとき、上にローテートする
        if (isEmptyBlockIndexIn(2, 6, 10)) {
            rotateUp(BLOCK_INDEX);
            return;
        }
    }

    /**
     * インデックス 15 ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickNumberButton15(View v) {
        final int BLOCK_INDEX = 15;
        Button button = (Button) v;
        System.out.printf("onClickNumberButton%d() %s %d%n", BLOCK_INDEX,
                button.getText(), emptyBlockIndex);

        // このブロックが空なら何もしない
        if (emptyBlockIndex == BLOCK_INDEX) {
            return;
        }

        // 空ブロックが左にあるとき、左にローテートする
        if (isEmptyBlockIndexIn(13, 14)) {
            rotateLeft(BLOCK_INDEX);
            return;
        }

        // 空ブロックが右にあるとき、右にローテートする
        if (isEmptyBlockIndexIn(16)) {
            rotateRight(BLOCK_INDEX);
            return;
        }

        // 空ブロックが上にあるとき、上にローテートする
        if (isEmptyBlockIndexIn(3, 7, 11)) {
            rotateUp(BLOCK_INDEX);
            return;
        }
    }

    /**
     * インデックス 16 ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickNumberButton16(View v) {
        final int BLOCK_INDEX = 16;
        Button button = (Button) v;
        System.out.printf("onClickNumberButton%d() %s %d%n", BLOCK_INDEX,
                button.getText(), emptyBlockIndex);

        // このブロックが空なら何もしない
        if (emptyBlockIndex == BLOCK_INDEX) {
            return;
        }

        // 空ブロックが左にあるとき、左にローテートする
        if (isEmptyBlockIndexIn(13, 14, 15)) {
            rotateLeft(BLOCK_INDEX);
            return;
        }

        // 空ブロックが上にあるとき、上にローテートする
        if (isEmptyBlockIndexIn(4, 8, 12)) {
            rotateUp(BLOCK_INDEX);
            return;
        }
    }

    /**
     * 左にローテートする。
     * @param index 空ブロックを置く位置
     */
    private void rotateLeft(int index) {
        System.out.printf("rotateLeft() %d %d%n", index, emptyBlockIndex);

        for (int i = emptyBlockIndex; i < index; i++) {
            numberButtons[i].setText(numberButtons[i + 1].getText());
        }

        numberButtons[index].setText(R.string.emptyText);
        emptyBlockIndex = index;
    }

    /**
     * 右にローテートする。
     * @param index 空ブロックを置く位置
     */
    private void rotateRight(int index) {
        System.out.printf("rotateRight() %d %d%n", index, emptyBlockIndex);

        for (int i = emptyBlockIndex; i > index; i--) {
            numberButtons[i].setText(numberButtons[i - 1].getText());
        }

        numberButtons[index].setText(R.string.emptyText);
        emptyBlockIndex = index;
    }

    /**
     * 下にローテートする。
     * @param index 空ブロックを置く位置
     */
    private void rotateDown(int index) {
        System.out.printf("rotateDown() %d %d%n", index, emptyBlockIndex);

        for (int i = emptyBlockIndex; i > index; i -= ORDER) {
            numberButtons[i].setText(numberButtons[i - ORDER].getText());
        }

        numberButtons[index].setText(R.string.emptyText);
        emptyBlockIndex = index;
    }

    /**
     * 上にローテートする。
     * @param index 空ブロックを置く位置
     */
    private void rotateUp(int index) {
        System.out.printf("rotateUp() %d %d%n", index, emptyBlockIndex);

        for (int i = emptyBlockIndex; i < index; i += ORDER) {
            numberButtons[i].setText(numberButtons[i + ORDER].getText());
        }

        numberButtons[index].setText(R.string.emptyText);
        emptyBlockIndex = index;
    }

    /**
     * 空ブロックが指定されたインデックスに入っているかどうか判定する。
     * @param indexes
     * @return 空ブロックが指定されたインデックスに入っていればtrue
     */
    private boolean isEmptyBlockIndexIn(int... indexes) {
        assert indexes.length > 0;

        return ArrayCollectionUtil.isKeyIn(emptyBlockIndex, indexes);    // (10)
    }

    /**
     * 開始ボタン・クリック時のハンドラー
     * @param v
     */
    public void onClickStartButton(View v) {    // (11)
        // 1~15 までの乱数を生成する
        List<Integer> numberList = new ArrayList<Integer>();
        numberList.addAll(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
                14, 15));
        
        // 解ける問題を生成する
        int[] numberArray;
        do {
            Collections.shuffle(numberList);
            numberArray = ArrayCollectionUtil.toArray(numberList);
        } while (SymmetricGroupUtil.sgn(numberArray) != 1);    // 解けない問題(遇置換でない)なら作り直し    // (12)

        // 数字ブロックを設定する
        for (int i = 1; i < numberArray.length + 1; i++) {    // (13)
            numberButtons[i].setText(String.valueOf(numberArray[i - 1])); // (14)
        }

        // 空ブロックを設定する
        numberButtons[ORDER * ORDER].setText(R.string.emptyText);
        emptyBlockIndex = ORDER * ORDER;

        log("onClickStartButton() ");
    }

    private void log(String message) {
        System.out.println(message);
        for (int i = 1; i < numberButtons.length; i += 4) {
            System.out.printf("[%s %s %s %s]%n",
                    numberButtons[i + 0].getText(), numberButtons[i + 1]
                            .getText(), numberButtons[i + 2].getText(),
                    numberButtons[i + 3].getText());
        }
    }
}
---

(1) 4 次の正方行列の次数の定数定義

15 パズルなので、4x4 の正方行列としてデータを扱う。ただし、実際には 16 個の要素
を持つ配列としてデータを保持する。
正方行列は以下のようなインデックスを持つとする。
    [ 1  2  3  4]
    [ 5  6  7  8]
    [ 9 10 11 12]
    [13 14 15 16]

    public static final int ORDER = 4; // 4 次の正方行列    // (1)


(2)(4) 数字ボタンの配列

    private Button[] numberButtons;    // 数字ボタン    // (2)

ボード上のボード上の番号と合わせるため、インデックス 0 をダミーとする。Button 型
なので、null を入れておく。

    numberButtons = new Button[] {
            null, // ボード上の番号と合わせるため、インデックス0をダミーとする // (4)
            (Button) findViewById(R.id.numberButton1),
            (Button) findViewById(R.id.numberButton2),
            (Button) findViewById(R.id.numberButton3),
            (Button) findViewById(R.id.numberButton4),
            (Button) findViewById(R.id.numberButton5),
            (Button) findViewById(R.id.numberButton6),
            (Button) findViewById(R.id.numberButton7),
            (Button) findViewById(R.id.numberButton8),
            (Button) findViewById(R.id.numberButton9),
            (Button) findViewById(R.id.numberButton10),
            (Button) findViewById(R.id.numberButton11),
            (Button) findViewById(R.id.numberButton12),
            (Button) findViewById(R.id.numberButton13),
            (Button) findViewById(R.id.numberButton14),
            (Button) findViewById(R.id.numberButton15),
            (Button) findViewById(R.id.numberButton16), 
        };


(3) 空ブロックのインデックス

パズルの初期状態は、インデックス 16 の位置に空ブロックがあるので、空ブロックのイ
ンデックスを 16 に設定する。

    private int emptyBlockIndex = ORDER * ORDER; // 空ブロックのインデックス // (3)


(5) インデックス 1 ボタン・クリック時のハンドラー

インデックス 1~16 までに対するボタン・クリック時のハンドラーをすべて定義する。

    public void onClickNumberButton1(View v) {    // (5)


(6) このブロックが空なら何もしない

空ブロックの場合は、何もせずに終わる。

    if (emptyBlockIndex == BLOCK_INDEX) {    // (6)
        return;
    }


(7)(8) 空ブロックが右にあるとき、右にローテートする

自身が 1 のとき、空ブロックが右にあるかどうかは、空ブロックのインデックスが 2, 3,
 4 のいずれかの場合である。
これは、プライベートなメソッドをつくって、これを確認している。なお、空ブロックの
インデックスは、インスタンス変数 emptyBlockIndex がもっているので、この引数には
指定していない。

    if (isEmptyBlockIndexIn(2, 3, 4)) {        // (7)

空ブロックが自ブロックの右側にあれば、右にローテートする。

    rotateRight(BLOCK_INDEX);    // (8)

仮に、自身が 1 で、空ブロックが 3 であれば、この行は次のようになる。
    [ 1  2  3  4] -> [ 3  1  2  4]


(9) 空ブロックが下にあるとき、下にローテートする

(8)と同様に、空ブロックが下にあれば、下にローテートする。

    rotateDown(BLOCK_INDEX);    // (9)

仮に、自身が 1 で、空ブロックが 9 であれば、この列は次のようになる。
    [ 1 ...]    [ 9 ...]
    [ 5 ...] -> [ 1 ...]
    [ 9 ...]    [ 5 ...]
    [13 ...]    [13 ...]

その他、左ローテート、上ローテートがある。
仮に、自身が 11 で、空ブロックが 9 であれば、左ローテートの行は次のようになる。
    [ 9 10 11 12] -> [10 11  9 12]

また、自身が 13 で、空ブロックが 5 であれば、上ローテートの列は次のようになる。
    [ 1 ...]    [ 1 ...]
    [ 5 ...] -> [ 9 ...]
    [ 9 ...]    [13 ...]
    [13 ...]    [ 5 ...]


(10) 空ブロックが指定されたインデックスに入っているかどうか判定する。

これは、ArrayCollectionUtil クラスの isKeyIn() メソッドを使う。

    return ArrayCollectionUtil.isKeyIn(emptyBlockIndex, indexes);    // (10)


(11) 開始ボタン・クリック時のハンドラー

    public void onClickStartButton(View v) {    // (11)


(12) 解ける問題を生成する

Collections.shuffle() を使って、ランダムに 1~15 までを設定する。ただし、このとき、
これらの値が遇置換でない場合は 15 パズルとして解けないので作り直す。

    do {
        Collections.shuffle(numberList);
        numberArray = ArrayCollectionUtil.toArray(numberList);
    } while (SymmetricGroupUtil.sgn(numberArray) != 1);
                            // 解けない問題(遇置換でない)なら作り直し    // (12)


(13)(14) 数字ブロックを設定する

ランダムな数字列はインデックス 0 からであるのに対し、数字ボタンのブロックはイン
デックス 1 からなので、調整しながら数字をブロックに設定する。

    for (int i = 1; i < numberArray.length + 1; i++) {    // (13)
        numberButtons[i].setText(String.valueOf(numberArray[i - 1]));    // (14)
    }


■ 設定データ

□ res/values/strings.xml
---
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">15パズル</string>
    <string name="start">スタート</string>
    <string name="emptyText">★</string>
</resources>
---

※この記事は続く
http://blog.goo.ne.jp/marunomarunogoo/d/20120203
                                                                            以上



最新の画像もっと見る

コメントを投稿

サービス終了に伴い、10月1日にコメント投稿機能を終了させていただく予定です。