1. ホーム
  2. Java

Javaコースデザイン 倉庫番ゲーム

2022-02-26 08:23:26

実行例です。

グラフィカルインターフェースは、Swingコンポーネントで構成されています

地図生成のアルゴリズムは以下の通りです。

マップを作成するアルゴリズム(produceMap):まず、2次元配列をすべて1に設定し、すべての壁の初期状態を表す。まず、2つの重ならないランダムな点を生成する。1つは人の始点、もう1つは箱の始点とする。マップを左上、左下、右上、右下の4つの領域に分割し、4つの領域で開いた空きスペースの数を記録するために4つの変数を設定する。必要なステップ数の後、4つの領域の中で最も空きスペースの数が少ない領域を探し、その領域内にランダムな点を作成し、その点を起点として、空きスペースの総数が地図の総面積の半分以上になるまで上記の操作をループし、作成完了とする。
SolveMapアルゴリズム(solveMap)。箱の位置とこの箱を探す間の人の位置、箱がこの位置に到達するまでの歩数を状態として表現する状態クラスを作成する。優先度キューを作成し、キューの要素は状態クラスのオブジェクトとし、歩数の少ない状態を高い優先度とする。幅優先探索は、始点から上、下、左、右の順で行う。stateクラスのキーとBooleanクラスの値を持つHashMapを作成して各状態が歩いたかどうかを保存し、pointクラスのキーと同じくpointクラスの値を持つHashMapを作成して最終出力経路のボックスの各位置の前位置を保存する。優先キューを使うことで、ボックスをプッシュする状態がキューを先送りする回数を増やすことができるので、ボックスがプッシュできるすべての方向にアクセスしてプッシュすることができ、キューを直接使った場合、すべてのケースを歩かせることができなくなる。また、マップが大きい場合、HashMapを使用してアクセス情報を格納することで、より大きなスペースを節約することができる。優先度キューは、発信操作を行うたびに発信状態のステップ数を記録する。現在の発信状態のステップ数と前回の発信状態のステップ数とが異なる場合、状態のアクセス情報を格納するHashMapをクリアする。もう一つは、ポイントクラスのHashMapキーを設定し、整数クラスの値は、数回訪問した各ポイントボックスを保存し、4回以上であれば、ポイントを訪問しないようにします。優先キューの性質から、キューから出た状態のステップ番号は、キュー内の最小のステップ数を持つ状態、つまり、キューから出た現在の状態のステップ番号が前のものと異なる場合、ボックスの前のステップの状態はすべてキューから出たことが知られている。このとき、ボックスの前のアクセス状態は不要になり、アクセス状態を保持するHashMapを適時にクリアすることで、メモリ領域を大幅に節約することができる。ワイドサーチで終了位置のボックスの状態をサーチすると、サーチが終了する。状態を終了するまでのステップ数が指定された最小ステップ数より少ない場合は解を返せず、そうでない場合は解を成功させる。終了位置の箱の状態が検索されず、キューが空である場合、解答は失敗する。
RecordPathアルゴリズム。地図を解く過程で作成された、箱の各位置の前の位置を記録したHashMapを元に、終点から出発点を一歩ずつ探し、その過程で見つかった点を順にスタックに載せ、スタック上の点を一つずつ取り出して点の配列に記録して、道の正順出力を実現します。

Main_Class.java

public class Main_Class {
	public static void main(String args[]) {
		PTB_Frame frame=new PTB_Frame("Push The Box");
		frame.setBounds(0,0,1200,1200);
	}
}




PTB_Frame.java

import java.awt.*;
import java.awt.event.*;

import javax.swing.*;
@SuppressWarnings("serial")

public class PTB_Frame extends JFrame {
	private Font font = new Font("宋体", Font.PLAIN, 23);
	private Map_Manager map_manager = new Map_Manager();
	private ConsolePanel console = new ConsolePanel(map_manager);
	private JButton creat_map=new JButton("create_map");
	private MapPanel map=new MapPanel(map_manager);
	PTB_Frame(String title) {
		init();
		this.setTitle(title);
		this.setVisible(true);
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
	}

	void init() {
		this.setLayout(null);
		console.setBounds(0, 0, 1200,250);
		console.add(create_map);
		this.add(console);
		map.setBounds(80, 250, 1200, 800);
		this.add(map);
		creat_map.setFont(font);
		creat_map.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				console.createMap();
				map.setMap(map_manager.getHeight(), map_manager.getWidth(), map_manager.getStepOfMap(), map_manager.getMap());
				map.createMap();
			}
		});
	}
}




ConsolePanel.java

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

@SuppressWarnings("serial")
public class ConsolePanel extends JPanel {
	Font font = new Font("宋体", Font.PLAIN, 23);
	JTextField t_height = new JTextField(3);
	JTextField t_width = new JTextField(3);
	JTextField t_diff = new JTextField(5);
	JButton get_path = new JButton("View shortest path");
	JTextArea show_path = new JTextArea(5, 40);
	int height, width;
	double diff;
	Map_Manager map_manager;

	ConsolePanel(Map_Manager map_manager) {
		this.map_manager = map_manager;
		UIManager.put("Label.font", font);
		this.add(new JLabel("Map height: "));
		t_height.setFont(font);
		this.add(t_height);
		this.add(new JLabel(" (integers from 3 to 100)"));
		this.add(new JLabel("Map width: "));
		t_width.setFont(font);
		this.add(t_width);
		this.add(new JLabel("(integer from 3 to 100)"));
		this.add(new JLabel("Map difficulty:"));
		t_diff.setFont(font);
		this.add(t_diff);
		this.add(new JLabel(" (decimal between 1.0~10.0)"));
		this.add(new JLabel("Note: the map height and width and the greater the difficulty, the longer it takes to generate the map"));
		get_path.setFont(font);
		this.add(get_path);
		show_path.setFont(font);
		show_path.setLineWrap(true);// auto line wrap
		show_path.setWrapStyleWord(true);// line wrap without line break
		JPanel show_path_panel = new JPanel();
		show_path_panel.add(new JScrollPane(show_path));// scroll window
		this.add(show_path_panel);
		get_path.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				Map_Manager.Point path[] = map_manager.getPath();
				if (path ! = null) {
					show_path.setText(null);
					for (int i = 0; i<map_manager.getStepOfMap(); ++i)
						show_path.append(path[i] + " ");
				}
			}
		});
	}

	public void createMap() {
		try {
			height = Integer.valueOf(t_height.getText());
			width = Integer.valueOf(t_width.getText());
			diff = Double.valueOf(t_diff.getText());
			if (height < 3 || height > 100 || width < 3 || width > 100 || diff < 1 || diff > 10)
				throw new NumberFormatException();
			map_manager.setMap(height, width, diff);
			map_manager.createMap();
			show_path.setText(null);
		} catch (NumberFormatException ex) {
			JOptionPane.showMessageDialog(getRootPane(), "Parameter format incorrect");
		}
	}
}



MapPanel.java

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

@SuppressWarnings("serial")
public class MapPanel extends JPanel {
	private Font font = new Font("宋体", Font.PLAIN, 23);
	private JPanel map_area;
	JPanel control_bar = new JPanel();
	private JButton drawback, restart,start;
	private JLabel l_min_step, l_left_step, l_passed_step;
	private JLabel show_cur_point;
	private JLabel show_cur_box;
	private Point person = new Point();
	private Point start_of_person = new Point(). private Point start_of_box = new Point(). private Point start_of_person = new Point();
	private Point start_of_box = new Point(). private Point end = new Point(). private Point start_of_box = new Point();
	private Point end = new Point();
	private Point box = new Point();
	private int width, height;
	private int map[][] = new int[100][100];
	private int di[][] = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
	private int min_step, passed_step = 0;
	private Stack<Operation> operation_recorder = new Stack<Operation>();
	private JButton block[][] = new JButton[100][100];
	private Map_Manager map_manager;

	private class Point implements Cloneable {
		int x, y;

		public boolean equals(Object obj) {
			if (! (obj instanceof Point))
				return false;
			Point point = (Point) obj;
			return this.x == point.x && this.y == point.y;
		}

		public Object clone() {
			Point newPoint = null;
			try {
				newPoint = (Point) super.clone();
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
			return newPoint;
		}

		public String toString() {
			return "(" + (this.x + 1) + "," + (this.y + 1) + ")";
		}
	}

	public void setMap(int height, int width, int min_step, int map[][]) {
		this.height = height;
		this.width = width;
		this.min_step = min_step;
		this.map = map;
	}
    private class PtbKeyAdapter extends KeyAdapter {
		public void keyPressed(KeyEvent e) {
			int keycode = e.getKeyCode();
			if (keycode == KeyEvent.VK_UP) {
				pointMove(0);
			} else if (keycode == KeyEvent.
				pointMove(1);
			} else if (keycode == KeyEvent.
				pointMove(2);
			} else if (keycode == KeyEvent.
				pointMove(3);
			}
		}
	}
	
	private class Operation {
		int stuff;// 1 for the box, 0 for the person
		int dir;// 0 for top, 1 for bottom, 2 for left, 3 for right

		Operation(int stuff, int dir) {
			this.stuff = stuff;
			this.dir = dir;
		}
	}

	private boolean accessible(Point point) {// point is accessible then return true
		if (point.x < 0 || point.x >= height || point.y < 0 || point.y >= width) // out of bounds
			return false;
		if (map[point.x][point.y] == 1)// go to the wall
			return false;
		return true;
	}

	private void pointMove(int dir) {
		if(passed_step>=min_step)
			return;
		// first determine whether the exchange can be done, if so, then exchange the two button color values
		Point cur_point = new Point();
		cur_point.x = person.x + di[dir][0];
		cur_point.y = person.y + di[dir][1];
		if (!accessible(cur_point))// current point is not walkable
			return;
		if (cur_point.equals(box)) {
			// When the previous point in the direction one is going is a box
			Point next_box = new Point();
			next_box.x = box.x + di[dir][0];
			next_box.y = box.y + di[dir][1];
			if (!accessible(next_box))// box cannot be pushed
				return;
			// if the box can advance, the person also advances, can not then the person can not advance
			go(box, dir);
			++this.passed_step;
			updateStep();
			operation_recorder.push(new Operation(1, dir));
		}
		go(person, dir);
		show_cur_point.setText(" "+person.toString()+" ");
		operation_recorder.push(new Operation(0, dir));
		if (box.equals(end))
			JOptionPane.showMessageDialog(this.getRootPane(), "WINNER WINNER CHICKEN DINNER!");
		else if(this.passed_step==this.min_step)
			JOptionPane.showMessageDialog(this.getRootPane(), "The river is still there, but the fate is over, sad! ")
	public void paintMap() {
		map_area = new JPanel(new GridLayout(this.height, this.width));
		for (int i = 0; i < this.height; i++)
			for (int j = 0; j < this.width; j++) {
				block[i][j] = new JButton();
				if (map[i][j] == 0)// 0 in the array is the way
					block[i][j].setBackground(Color.WHITE);
				else if (map[i][j] == 1)// 1 in the array is the wall
					block[i][j].setBackground(Color.BLACK);
				else if (map[i][j] == 2)// block[i][j] == 2
				{
					block[i][j].setBackground(Color.BLUE);
					start_of_box.x = i;
					start_of_box.y = j;
				} else if (map[i][j] == 3)// 3 in the array is the end point
				{
					block[i][j].setBackground(Color.GREEN);
					end.x = i;
					end.y = j;
				} else if (map[i][j] == 4) {// 4 in the array is the position of the person
					block[i][j].setBackground(Color.RED);
					start_of_person.x = i;
					start_of_person.y = j;
				}
				map_area.add(block[i][j]);
			}
		person = (Point) start_of_person.clone();
		box = (Point) start_of_box.clone();
		l_min_step.setText(Integer.toString(min_step));
		show_cur_point.setText(" "+person.toString()+" ");
		passed_step=0;
		updateStep();
		int map_height=750,map_width=750;
		if(this.height>this.width)
			map_width=(750/this.height)*this.width;
		else if(this.width>this.height)
			map_height=(750/this.width)*this.height;
		map_area.setBounds(0, 0, map_width, map_height);
		this.add(map_area);
	}

	public MapPanel(Map_Manager map_manager) {
		this.map_manager = map_manager;
		init();
	}

	private void init() {
		this.setLayout(null);
		map_manager.setMap(20, 20, 7.0);
		map_manager.createMap();
		this.height = map_manager.getHeight();
		this.width = map_manager.getWidth();
		this.min_step = map_manager.getStepOfMap();
		this.map = map_manager.getMap();
		UIManager.put("Label.font", font);
		drawback = new JButton("Step back");
		restart = new JButton("Restart");
		start = new JButton("Start");
		control_bar.add(new JLabel("Current box position"));
		show_cur_box=new JLabel();
		control_bar.add(show_cur_box);
		control_bar.add(new JLabel("Current person's position"));
		show_cur_point=new JLabel();
		control_bar.add(show_cur_point);
		control_bar.add(new JLabel("min_step:"));
		l_min_step = new JLabel();
		l_min_step.setFont(font);
		control_bar.add(l_min_step);
		control_bar.add(new JLabel("Number of steps taken: "));
		l_passed_step = new JLabel();
		l_passed_step.setFont(font);
		control_bar.add(l_passed_step);
		control_bar.add(new JLabel("Number of steps left: "));
		l_left_step = new JLabel();
		l_left_step.setFont(font);
		control_bar.add(l_left_step);
		control_bar.add(new JLabel("Note: here the step is "));
		control_bar.add(new JLabel("The number of steps the box moves"));
		control_bar.add(new JLabel("Red for people"));
		control_bar.add(new JLabel("blue for the box"));
		control_bar.add(new JLabel("green for the end"));
		restart.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				block[person.x][person.y].setBackground(Color.WHITE);
				person = (Point) start_of_person.clone();
				block[box.x][box.y].setBackground(Color.WHITE);
				box = (Point) start_of_box.clone();
				block[person.x][person.y].setBackground(Color.RED);
				block[box.x][box.y].setBackground(Color.BLUE);
				block[end.x][end.y].setBackground(Color.GREEN);
				passed_step = 0;
				show_cur_point.setText(" "+person.toString()+" ");
				updateStep();
			}
		});
		drawback.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (operation_recorder.empty())
					return;
				Operation cur_op = operation_recorder.peek();
				operation_recorder

Map_Manager.java

import java.util.*;

public class Map_Manager {
	private PTB_Map ptb_map = new PTB_Map();
	private int di[][] = { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } };
	private double search_limit;
	private int area[] = new int[4];
	private Map<Status, Boolean> visit_of_status = new HashMap<Status, Boolean>();
	private Map<Point, Integer> visit_of_box = new HashMap<Point, Integer>();
	private PriorityQueue<Status> q = new PriorityQueue<Status>(new Comparator<Status>() {
		public int compare(Status s1, Status s2) {
			return s1.step - s2.step;
		}
	});

	private class PTB_Map {
		private int width;
		private int height;
		private int step_of_map;
		private int accessible_point;
		private Point end_p = new Point();
		private Status start = new Status();
		private int matrix[][] = new int[100][100];
		private Map<Point, Point> path_map = new HashMap<Point, Point>();
		private Point[] path = new Point[1000];
		private double ratio_of_space;
		private double ratio_of_step;
	}

	public class Point implements Cloneable {
		int x, y;

		public boolean equals(Object obj) {
			if (! (obj instanceof Point))
				return false;
			Point point = (Point) obj;
			return this.x == point.x && this.y == point.y;
		}

		public int hashCode() {
			return this.x * ptb_map.width + this.y;
		}

		public Object clone() {
			Point newPoint = null;
			try {
				newPoint = (Point) super.clone();
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
			return newPoint;
		}

		public String toString() {
			return "(" + (this.x + 1) + "," + (this.y + 1) + ")";
		}
	}

	private class Status implements Cloneable {
		Point box = new Point();
		Point person = new Point();
		int step;

		public int hashCode() {
			return this.box.hashCode() * this.person.hashCode() + this.step;
		}

		public boolean equals(Object obj) {
			if (! (obj instanceof Status))
				return false;
			Status status = (Status) obj;
			return this.box.equals(status.box) && this.person.equals(status.person) && this.step == status.step;
		}

		public Object clone() {
			Status newStatus = null;
			try {
				newStatus = (Status) super.clone();
				newStatus.box = (Point) box.clone();
				newStatus.person = (Point) person.clone();
			} catch (CloneNotSupportedException e) {
				e.printStackTrace();
			}
			return newStatus;
		}
	}

	public void setMap(int height, int width, double degree_of_difficulty) {
		this.ptb_map.height = height;
		this.ptb_map.width = width;
		this.ptb_map.ratio_of_space = 0.5;
		double ratio = 1;
		if (height + width < 20)
			ratio = 0.05 * (height + width);
		this.ptb_map.ratio_of_step = 0.5 + degree_of_difficulty * 0.05 * ratio;
		this.search_limit = (this.ptb_map.height * this.ptb_map.width) / 10;
	}

	public int getHeight() {
		return ptb_map.height;
	}

	public int getWidth() {
		return ptb_map.width;
	}

	public int getStepOfMap() {
		return ptb_map.step_of_map;
	}

	private boolean accessible(Point point) {// point can go then return true
		if (point.x < 0 || point.x >= ptb_map.height || point.y < 0 || point.y >= ptb_map.width) // out of bounds
			return false;
		if (ptb_map.matrix[point.x][point.y] == 1)// go to the wall
			return false;
		return true;
	}

	private void creatSpace(Point point) {
		Random random = new Random();
		int l = 0;
		while (l <= search_limit) {
			int dir = random.nextInt(4);
			if (point.x + di[dir][0] < 0 || point.x + di[dir][0] >= ptb_m
		{
			ptb_map.start.box.x = random.nextInt(ptb_map.height);
			ptb_map.start.box.y = random.nextInt(ptb_map.width);
			ptb_map.start.person.x = random.nextInt(ptb_map.height);
			ptb_map.start.person.y = random.nextInt(ptb_map.width);
		} while (ptb_map.start.box.equals(ptb_map.start.person));
		ptb_map.accessible_point = 0;
		ptb_map.matrix[ptb_map.start.person.x][ptb_map.start.person.x] = 0;
		// Set a certain wall
		ptb_map.accessible_point = 0;
		Point start = (Point) ptb_map.start.person.clone(); // the initial position of the person at the beginning of the walk
		area[0] = area[1] = area[2] = area[3] = 0;
		creatSpace(start);
		while (ptb_map.accessible_point < (ptb_map.height * ptb_map.width) * ptb_map.ratio_of_space) {
			int min = 10000, min_area = 0;
			for (int i = 0; i < 4; ++i)
				if (area[i] < min) {
					min = area[i];
					min_area = i;
				}
			switch (min_area) {
			case 0:// top left
				start.x = random.nextInt(ptb_map.height / 2);
				start.y = random.nextInt(ptb_map.width / 2);
				break;
			case 1:// lower left
				start.x = random.nextInt(ptb_map.height / 2) + ptb_map.height / 2;
				start.y = random.nextInt(ptb_map.width / 2);
				break;
			case 2:// top right
				start.x = random.nextInt(ptb_map.height / 2);
				start.y = random.nextInt(ptb_map.width / 2) + ptb_map.width / 2;
				break;
			case 3:// lower right
				start.x = random.nextInt(ptb_map.height / 2) + ptb_map.height / 2;
				start.y = random.nextInt(ptb_map.width / 2) + ptb_map.width / 2;
				break;
			}
			createSpace(start);
		}
		ptb_map.end_p = (Point) start.clone();
		ptb_map.matrix[ptb_map.start.person.x][ptb_map.start.person.y] = 4;
		ptb_map.matrix[ptb_map.start.box.x][ptb_map.start.box.y] = 2;
		ptb_map.matrix[ptb_map.end_p.x][ptb_map.end_p.y] = 3;
		return true;
	}

	private boolean solveMap() {// return value is whether the current map has a path
		q.clear();
		visit_of_box.clear();
		ptb_map.path_map.clear();
		ptb_map.step_of_map = 0;
		ptb_map.start.step = 0;
		int pre_step = -1;
		q.add(ptb_map.start);
		visit_of_status.put(ptb_map.start, true);
		while (!q.isEmpty()) {
			Status pre_Status = (Status) q.peek().clone();
			if (pre_Status.step ! = pre_step) {
				visit_of_status.clear();
				pre_step = pre_Status.step;
			}
			for (int i = 0; i < 4; ++i) {
				Status cur_Status = (Status) pre_Status.clone();
				cur_Status.person.x += di[i][0];
				cur_Status.person.y += di[i][1];
				if (!accessible(cur_Status.person))// the point is not walkable
					continue;
				if (visit_of_status.containsKey(cur_Status))// the point has been traveled
					continue;
				// save the previous one of the current point, used to output the path
				if (cur_Status.person.equals(cur_Status.box))// walk to the box
				{
					Point next_box = new Point();// the current person will push the box to the position
					next_box.x = cur_Status.box.x + di[i][0];
					next_box.y = cur_Status.box.y + di[i][1];
					if (!accessible(next_box))// the point is not walkable
						continue;
					if (ptb_map.path_map.containsKey(next_box))
						continue;
					if (visit_of_box.containsKey(next_box)) { if (visit_of_box.containsKey(next_box))
						if (visit_of_box.get(next_box) > 4)// the current position box has been walked four times
							continue;
					}
					// If the box can go to that point, the box takes one step
					ptb_map.path_map.put(next_box, cur_Status.box);
					cur_Status.box = next_box;
					++cur_Status.step;
					if (!visit_of_box.containsKey(cur_Status.box))
						visit_of_box.put(cur_Status.box, 1);
					else {
						int t = visit_of_box.get(cur_Status.box);
						++t;
						visit_of_box.put(cur_Status.box, t);
					}
					if (cur_Status.box.equals(ptb_map.end_p))// box goes to the end
					{
						ptb_map.step_of_map = cur_Status.step;
						q.clear();
						if (ptb_map.step_of_map < (ptb_map.height + ptb_map.width) * ptb_map.ratio_of_step)
							return false;
						else
							return true;
					}
				}
				q.add(cur_Status);
				visit

個人サイト:https://jqh.zone