8/15パズル

/******************************/
/* 8/15パズル             */
/*      coded by Y.Suganuma   */
/******************************/
import java.io.*;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.Random;

public class Test {
	public static void main(String args[]) throws IOException
	{
		Data dt = new Data();
	}
}

/********************/
/* クラスDataの定義 */
/********************/
class Data extends JFrame implements ItemListener {

	int game = 0;   // ゲームの種類
                    //   =0 : 8パズル
                    //   =1 : 15パズル
	int i_set = 0;   // 初期状態の設定方法
                     //   =0 : 入力する
                     //   =1 : ランダム
	int goal = 0;   // 目標
                    //   =0 : 周辺を回る
                    //   =1 : 上から横並び
	int g[][];   // 目標状態
	int p[][];   // 現在の状態

	JComboBox <String> choice1, choice2, choice3;
	Data dt = this;

	/******************/
	/* コンストラクタ */
	/******************/
	Data()
	{
						// JFrameクラスのコンストラクタの呼び出し
		super("データの入力");
						// レイアウトの変更
		Container cp = getContentPane();
		cp.setBackground(Color.white);
		cp.setLayout(null);
		JPanel jp = new JPanel();
		jp.setLayout(new GridLayout(4, 2, 5, 10));
		jp.setBackground(Color.white);
		cp.add(jp);
		Font f = new Font("TimesRoman", Font.BOLD, 20);
						// スクロールリスト
		JLabel fill1 = new JLabel("ゲーム");
		fill1.setFont(f);
		jp.add(fill1);
		choice1 = new JComboBox <String> ();
		choice1.setBackground(Color.white);
		choice1.setFont(f);
		choice1.addItem("8パズル");
		choice1.addItem("15パズル");
		jp.add(choice1);
		choice1.addItemListener(this);

		JLabel fill2 = new JLabel("初期設定");
		fill2.setFont(f);
		jp.add(fill2);
		choice2 = new JComboBox <String> ();
		choice2.setBackground(Color.white);
		choice2.setFont(f);
		choice2.addItem("入力");
		choice2.addItem("ランダム");
		jp.add(choice2);
		choice2.addItemListener(this);

		JLabel fill3 = new JLabel("ゴール");
		fill3.setFont(f);
		jp.add(fill3);
		choice3 = new JComboBox <String> ();
		choice3.setBackground(Color.white);
		choice3.setFont(f);
		choice3.addItem("回転");
		choice3.addItem("横並び");
		jp.add(choice3);
		choice3.addItemListener(this);
						// OK ボタンの設定
		JLabel fill4 = new JLabel(" スタート?");
		fill4.setFont(f);
		jp.add(fill4);
		JButton bt = new JButton("OK");
		bt.addMouseListener(new ClickMouse());
		bt.setFont(f);
		jp.add(bt);
						// Windowサイズを設定
		setSize(300, 250);
						// ウィンドウを表示
		setVisible(true);
		Insets in = getInsets();
		jp.setLocation(10, 10);
		jp.setSize(300-in.left-in.right-20, 250-in.top-in.bottom-20);
						// イベントアダプタ
		addWindowListener(new WinEnd());
	}

	/************************/
	/* 選択されたときの処理 */
	/************************/
	public void itemStateChanged(ItemEvent e)
	{
		if (e.getItemSelectable() == choice1) {
			game = choice1.getSelectedIndex();
		}
		if (e.getItemSelectable() == choice2) {
			i_set = choice2.getSelectedIndex();
		}
		if (e.getItemSelectable() == choice3) {
			goal = choice3.getSelectedIndex();
		}
	}

	/********************************/
	/* OKボタンが押されたときの処理 */
	/********************************/
	class ClickMouse extends MouseAdapter
	{
		/************************************/
		/* マウスがクリックされたときの処理 */
		/************************************/
		public void mouseClicked(MouseEvent e)
		{
			int i1, i2, k, k1, k2, n = 0;
		/*
		          ゲームの種類
		*/
							// 8パズル
			if (game == 0) {
				g = new int [3][3];
				p = new int [3][3];
				n = 3;
			}
							// 15パズル
			else {
				g = new int [4][4];
				p = new int [4][4];
				n = 4;
			}
		/*
		          初期状態と目標状態
		*/
					// 初期状態を入力する場合
			Initial initial;
			if (i_set == 0)
				initial = new Initial(dt);
					// 初期状態をランダムに決める場合
						// 目標状態の決定
							// 回転
			else {
				if (goal == 0) {
					if (game == 0) {
						k = 1;
						for (i1 = 0; i1 < n; i1++) {
							g[0][i1] = k;
							k++;
						}
						k = 7;
						for (i1 = 0; i1 < n; i1++) {
							g[2][i1] = k;
							k--;
						}
						g[1][0] = 8;
						g[1][1] = 0;
						g[1][2] = 4;
						k1      = 1;
						k2      = 1;
					}
					else {
						k = 1;
						for (i1 = 0; i1 < n; i1++) {
							g[0][i1] = k;
							g[1][i1] = k + 11;
							k++;
						}
						g[1][3] = 5;
						k       = 10;
						for (i1 = 0; i1 < n; i1++) {
							g[3][i1] = k;
							k--;
						}
						g[2][0] = 11;
						g[2][1] = 0;
						g[2][2] = 15;
						g[2][3] = 6;
						k1      = 2;
						k2      = 1;
					}
				}
							// 横並び
				else {
					k = 1;
					for (i1 = 0; i1 < n; i1++) {
						for (i2 = 0; i2 < n; i2++) {
							g[i1][i2] = k;
							k++;
						}
					}
					g[n-1][n-1] = 0;
					k1          = n - 1;
					k2          = n - 1;
				}
						// 初期状態の決定
				for (i1 = 0; i1 < n; i1++) {
					for (i2 = 0; i2 < n; i2++)
						p[i1][i2] = g[i1][i2];
				}

				for (i1 = 0; i1 < 200; i1++) {
					k = (int)(Math.random() * 4.0);
					switch (k) {
						case 0:   // 上へ移動
							if (k1 > 0) {
								p[k1][k2]   = p[k1-1][k2];
								p[k1-1][k2] = 0;
								k1--;
							}
							break;
						case 1:   // 下へ移動
							if (k1 < n-1) {
								p[k1][k2]   = p[k1+1][k2];
								p[k1+1][k2] = 0;
								k1++;
							}
							break;
						case 2:   // 左へ移動
							if (k2 > 0) {
								p[k1][k2]   = p[k1][k2-1];
								p[k1][k2-1] = 0;
								k2--;
							}
							break;
						default:   // 右へ移動
							if (k2 < n-1) {
								p[k1][k2]   = p[k1][k2+1];
								p[k1][k2+1] = 0;
								k2++;
							}
							break;
					}
				}
						// 目標状態の表示
				Goal goal_s = new Goal(dt);
			}
		}
	}

	/************/
	/* 終了処理 */
	/************/
	class WinEnd extends WindowAdapter
	{
		public void windowClosing(WindowEvent e) {
			System.exit(0);
		}
	}
}

/**************************/
/* クラスInitialの定義    */
/* (初期状態の入力)     */
/**************************/
class Initial extends JFrame {

	int err = 0;   // エラーの有無
	private int n;   // 行・列の数
	private Data dt;
	private Initial in = this;
	JTextField a[][];
	JLabel f[];
	Error_p ep;

	/****************************************/
	/* コンストラクタ                       */
	/*      dt_i : Dataクラスのオブジェクト */
	/****************************************/
	Initial(Data dt_i)
	{
						// JFrameクラスのコンストラクタの呼び出し
		super("初期状態");
						// ゲームの種類とレイアウトの変更
		int i1, i2;
		n  = (dt_i.game == 0) ? 3 : 4;
		dt = dt_i;
		a  = new JTextField [n][n];
		f  = new JLabel [n-1];
		Container cp = getContentPane();
		cp.setBackground(Color.white);
		cp.setLayout(null);
		JPanel jp = new JPanel();
		jp.setBackground(Color.white);
		cp.add(jp);
		jp.setLayout(new BorderLayout(5, 10));
		JPanel pn = new JPanel();
		pn.setBackground(Color.white);
		pn.setLayout(new GridLayout(n, n+1, 5, 10));
		jp.add(pn, BorderLayout.NORTH);
		Font fn = new Font("TimesRoman", Font.BOLD, 20);
						// テキストフィールドの設定
		for (i1 = 0; i1 < n; i1++) {
			for (i2 = 0; i2 < n; i2++) {
				a[i1][i2] = new JTextField(2);
				a[i1][i2].setFont(fn);
				pn.add(a[i1][i2]);
			}
			if (i1 < n-1) {
				f[i1] = new JLabel();
				pn.add(f[i1]);
			}
		}
						// OK ボタンの設定
		JButton bt = new JButton("OK");
		bt.setFont(fn);
		bt.addMouseListener(new ClickMouse());
		pn.add(bt);
						// メッセージパネルの追加
		ep = new Error_p(this);
		ep.setBackground(Color.white);
		jp.add(ep, BorderLayout.CENTER);
						// Windowサイズを設定
		if (dt.game == 0)
			setSize(350, 250);
		else
			setSize(400, 280);
						// ウィンドウを表示
		setVisible(true);
		Insets in = getInsets();
		jp.setLocation(10, 10);
		if (dt.game == 0)
			jp.setSize(350-in.left-in.right-20, 250-in.top-in.bottom-20);
		else
			jp.setSize(400-in.left-in.right-20, 280-in.top-in.bottom-20);
						// イベントアダプタ
		addWindowListener(new WinEnd());
	}

	/********************************/
	/* OKボタンが押されたときの処理 */
	/********************************/
	class ClickMouse extends MouseAdapter
	{
		/************************************/
		/* マウスがクリックされたときの処理 */
		/************************************/
		public void mouseClicked(MouseEvent e)
		{
			int i1, i2, i3, i4, k, k1;

			err = 0;
		/*
		          入力データの処理
		*/
			for (i1 = 0; i1 < n && err == 0; i1++) {
				for (i2 = 0; i2 < n && err == 0; i2++) {
					if ((a[i1][i2].getText()).length() <= 0)
						dt.p[i1][i2] = 0;
					else
						dt.p[i1][i2] = Integer.parseInt(a[i1][i2].getText());
					k1 = (dt.game == 0) ? 8 : 15;
					if (dt.p[i1][i2] < 0 || dt.p[i1][i2] > k1)
						err = 1;
					else {
						for (i3 = 0; i3 <= i1 && err == 0; i3++) {
							k1 = (i3 == i1) ? i2 : n;
							for (i4 = 0; i4 < k1 && err == 0; i4++) {
								if (dt.p[i1][i2] == dt.p[i3][i4])
									err = 1;
							}
						}
					}
				}
			}
		/*
		          目標状態
		*/
			ep.repaint();

			if (err == 0) {
							// 回転
				if (dt.goal == 0) {
					if (dt.game == 0) {
						k = 1;
						for (i1 = 0; i1 < n; i1++) {
							dt.g[0][i1] = k;
							k++;
						}
						k = 7;
						for (i1 = 0; i1 < n; i1++) {
							dt.g[2][i1] = k;
							k--;
						}
						dt.g[1][0] = 8;
						dt.g[1][1] = 0;
						dt.g[1][2] = 4;
					}
					else {
						k = 1;
						for (i1 = 0; i1 < n; i1++) {
							dt.g[0][i1] = k;
							dt.g[1][i1] = k + 11;
							k++;
						}
						dt.g[1][3] = 5;
						k          = 10;
						for (i1 = 0; i1 < n; i1++) {
							dt.g[3][i1] = k;
							k--;
						}
						dt.g[2][0] = 11;
						dt.g[2][1] = 0;
						dt.g[2][2] = 15;
						dt.g[2][3] = 6;
					}
				}
							// 横並び
				else {
					k = 1;
					for (i1 = 0; i1 < n; i1++) {
						for (i2 = 0; i2 < n; i2++) {
							dt.g[i1][i2] = k;
							k++;
						}
					}
					dt.g[n-1][n-1] = 0;
				}
							// 目標状態の表示
				in.setVisible(false);
				Goal goal_s = new Goal(dt);
			}
		}
	}

	/************/
	/* 終了処理 */
	/************/
	class WinEnd extends WindowAdapter
	{
		public void windowClosing(WindowEvent e) {
			in.setVisible(false);
		}
	}
}

/**************************/
/* エラーメッセージの表示 */
/**************************/
class Error_p extends JPanel {
	Initial in;

	Error_p(Initial in1)
	{
		in = in1;
	}

	public void paintComponent (Graphics g)
	{
		super.paintComponent(g);   // 親クラスの描画(必ず必要)
						// フォントの設定
		Font f = new Font("TimesRoman", Font.BOLD, 20);
		g.setFont(f);
						// エラーメッセージの出力
		int x = 10;
		int y = 25;
		if (in.err == 0)
			g.drawString("             ", x, y);
		else {
			g.setColor(Color.red);
			g.drawString("*** Error ***", x, y);
			g.setColor(Color.black);
		}
	}
}

/**********************/
/* クラスGoalの定義   */
/* (目標状態の表示) */
/**********************/
class Goal extends JFrame {

	int size = 50;   // コマの大きさ
	private Data dt;
	private Goal gl = this;

	/****************************************/
	/* コンストラクタ                       */
	/*      dt_i : Dataクラスのオブジェクト */
	/****************************************/
	Goal(Data dt_i)
	{
					// JFrameクラスのコンストラクタの呼び出し
		super("ゴール");
					// データの設定
		dt = dt_i;
		Container cp = getContentPane();
		cp.setBackground(Color.white);
		cp.setLayout(null);
		JPanel jp = new JPanel();
		jp.setBackground(Color.white);
		cp.add(jp);
		jp.setLayout(new BorderLayout(5, 10));
					// 描画パネルの追加
		Draw_p dp = new Draw_p(dt, this);
		dp.setBackground(Color.white);
		jp.add(dp, BorderLayout.CENTER);
					// 実行ボタンの設定
		JButton bt = new JButton("ゲームの実行");
		bt.addMouseListener(new ClickMouse());
		bt.setFont(new Font("TimesRoman", Font.BOLD, 20));
		jp.add(bt, BorderLayout.SOUTH);
					// Windowの大きさの設定
		if (dt_i.game == 0)
			setSize(3*(size+10)+70, 3*(size+10)+150);
		else
			setSize(4*(size+10)+70, 4*(size+10)+150);
					// ウィンドウを表示
		setVisible(true);
		Insets in = getInsets();
		jp.setLocation(10, 10);
		if (dt.game == 0)
			jp.setSize(250-in.left-in.right-20, 330-in.top-in.bottom-20);
		else
			jp.setSize(310-in.left-in.right-20, 390-in.top-in.bottom-20);
					// イベントアダプタ
		addWindowListener(new WinEnd());
	}

	/**********************************/
	/* 実行ボタンが押されたときの処理 */
	/**********************************/
	class ClickMouse extends MouseAdapter
	{
		/************************************/
		/* マウスがクリックされたときの処理 */
		/************************************/
		public void mouseClicked(MouseEvent e)
		{
			Run run = new Run(dt);
		}
	}

	/************/
	/* 終了処理 */
	/************/
	class WinEnd extends WindowAdapter
	{
		public void windowClosing(WindowEvent e) {
			gl.setVisible(false);
		}
	}
}

class Draw_p extends JPanel {

	private Data dt;
	private Goal gl;

	Draw_p(Data dt_i, Goal gl_i)
	{
		dt   = dt_i;
		gl   = gl_i;
	}

	/********/
	/* 描画 */
	/********/
	public void paintComponent (Graphics g)
	{
		super.paintComponent(g);   // 親クラスの描画(必ず必要)
		int i1, i2, x, y;
		int n = (dt.game == 0) ? 3 : 4;
					// 四角形を描く
		x = 20;
		y = 5;
		g.setColor(Color.cyan);
		for (i1 = 0; i1 < n; i1++) {
			for (i2 = 0; i2 < n; i2++) {
				g.fill3DRect(x, y, gl.size, gl.size, true);
				x += (gl.size + 10);
			}
			x  = 20;
			y += (gl.size + 10);
		}
					// 数字を描く
		x = 20;
		y = 5;
		g.setColor(Color.black);
		g.setFont(new Font("TimesRoman", Font.BOLD, gl.size));
		for (i1 = 0; i1 < n; i1++) {
			for (i2 = 0; i2 < n; i2++) {
				if (dt.g[i1][i2] > 0) {
					if (dt.g[i1][i2] < 10)
						g.drawString(Integer.toString(dt.g[i1][i2]), x+11, y+gl.size-7);
					else
						g.drawString(Integer.toString(dt.g[i1][i2]), x-5, y+gl.size-7);
				}
				x += (gl.size + 10);
			}
			x  = 20;
			y += (gl.size + 10);
		}
	}
}

/********************/
/* クラスRunの定義  */
/* (ゲームの実行) */
/********************/
class Run extends JFrame {

	int size = 50;   // コマの大きさ
	int count = 0;   // コマを動かした回数
	int ok = 0;   // ゲームの終了
	Data dt;
	private Draw_p_r dp;
	private Run rn = this;

	/****************************************/
	/* コンストラクタ                       */
	/*      dt_i : Dataクラスのオブジェクト */
	/****************************************/
	Run(Data dt_i)
	{
					// Frameクラスのコンストラクタの呼び出し
		super("ゲーム");
					// データの設定
		dt = dt_i;
		setBackground(Color.white);
		Container cp = getContentPane();
		cp.setBackground(Color.white);
		cp.setLayout(null);
		JPanel jp = new JPanel();
		jp.setBackground(Color.white);
		cp.add(jp);
		jp.setLayout(new BorderLayout(5, 10));
					// 描画パネルの追加
		dp = new Draw_p_r(dt, this);
		dp.setBackground(Color.white);
		jp.add(dp, BorderLayout.CENTER);
					// Windowの大きさの設定
		if (dt.game == 0)
			setSize(3*(size+10)+70, 3*(size+10)+150);
		else
			setSize(4*(size+10)+70, 4*(size+10)+150);
					// ウィンドウを表示
		setVisible(true);
		Insets in = getInsets();
		jp.setLocation(10, 10);
		if (dt.game == 0)
			jp.setSize(250-in.left-in.right-20, 330-in.top-in.bottom-20);
		else
			jp.setSize(310-in.left-in.right-20, 390-in.top-in.bottom-20);
					// イベントアダプタ
		addWindowListener(new WinEnd());
		addMouseListener(new ClickMouse());
	}

	/************************************/
	/* マウスがクリックされたときの処理 */
	/************************************/
	class ClickMouse extends MouseAdapter
	{
		public void mouseClicked(MouseEvent e)
		{
			int i1, i2, k1 = -1, k2 = -1, sw, x, xp, y, yp;
			int n = (dt.game == 0) ? 3 : 4;
							// マウスの位置
			Insets in = getInsets();
			xp = e.getX() - in.left;
			yp = e.getY() - in.top;
							// クリックされたコマを決定
			x = 20;
			y = 5;
			for (i1 = 0; i1 < n && k1 < 0; i1++) {
				for (i2 = 0; i2 < n && k1 < 0; i2++) {
					if (xp > x && xp < x+size && yp > y && yp < y+size) {
						k1 = i1;
						k2 = i2;
					}
					else
						x += (size + 10);
				}
				x  = 20;
				y += (size + 10);
			}
							// コマの移動
			if (k1 >= 0 && k2 >= 0) {
				sw = 0;

				if (k1 > 0) {                // 上へ移動
					if (dt.p[k1-1][k2] == 0) {
						dt.p[k1-1][k2] = dt.p[k1][k2];
						dt.p[k1][k2]   = 0;
						sw             = 1;
					}
				}

				if (sw == 0 && k1 < n-1) {   // 下へ移動
					if (dt.p[k1+1][k2] == 0) {
						dt.p[k1+1][k2] = dt.p[k1][k2];
						dt.p[k1][k2]   = 0;
						sw             = 1;
					}
				}

				if (sw == 0 && k2 > 0) {     // 左へ移動
					if (dt.p[k1][k2-1] == 0) {
						dt.p[k1][k2-1] = dt.p[k1][k2];
						dt.p[k1][k2]   = 0;
						sw             = 1;
					}
				}

				if (sw == 0 && k2 < n-1) {   // 右へ移動
					if (dt.p[k1][k2+1] == 0) {
						dt.p[k1][k2+1] = dt.p[k1][k2];
						dt.p[k1][k2]   = 0;
						sw             = 1;
					}
				}
							// ゴールに達したか否かのチェックと描画
				if (sw > 0) {
					count++;
					ok = 1;
					for (i1 = 0; i1 < n && ok > 0; i1++) {
						for (i2 = 0; i2 < n && ok > 0; i2++) {
							if (dt.g[i1][i2] != dt.p[i1][i2])
								ok = 0;
						}
					}
					dp.repaint();
				}
			}
		}
	}

	/************/
	/* 終了処理 */
	/************/
	class WinEnd extends WindowAdapter
	{
		public void windowClosing(WindowEvent e) {
			rn.setVisible(false);
		}
	}
}

class Draw_p_r extends JPanel {

	private Data dt;
	private Run rn;

	Draw_p_r(Data dt_i, Run rn_i)
	{
		dt   = dt_i;
		rn   = rn_i;
	}

	/********/
	/* 描画 */
	/********/
	public void paintComponent (Graphics g)
	{
		super.paintComponent(g);   // 親クラスの描画(必ず必要)
		int i1, i2, x, y;
		int n = (dt.game == 0) ? 3 : 4;
					// 四角形を描く
		x = 15;
		y = 5;
		g.setColor(Color.cyan);
		for (i1 = 0; i1 < n; i1++) {
			for (i2 = 0; i2 < n; i2++) {
				g.fill3DRect(x, y, rn.size, rn.size, true);
				x += (rn.size + 10);
			}
			x  = 15;
			y += (rn.size + 10);
		}
					// 数字を描く
		x = 15;
		y = 5;
		g.setColor(Color.black);
		g.setFont(new Font("TimesRoman", Font.BOLD, rn.size));
		for (i1 = 0; i1 < n; i1++) {
			for (i2 = 0; i2 < n; i2++) {
				if (dt.p[i1][i2] > 0) {
					if (dt.p[i1][i2] < 10)
						g.drawString(Integer.toString(dt.p[i1][i2]), x+13, y+rn.size-7);
					else
						g.drawString(Integer.toString(dt.p[i1][i2]), x-5, y+rn.size-7);
				}
				x += (rn.size + 10);
			}
			x  = 15;
			y += (rn.size + 10);
		}
					// ゲームの終了
		if (rn.ok > 0) {
			Insets in = rn.getInsets();
			x = 10;
			if (dt.game == 0)
				y = 330 - in.top - in.bottom - 30;
			else
				y = 390 - in.top - in.bottom - 30;
			g.setFont(new Font("TimesRoman", Font.PLAIN, 20));
			g.drawString("Congratulation!!", x, y-25);
			g.drawString("Number of Trial : "+Integer.toString(rn.count), x, y);
		}
	}
}