455 lines
16 KiB
Java
455 lines
16 KiB
Java
import java.io.File;
|
|
import java.util.Scanner;
|
|
/**
|
|
* @author: ______your name here (SID)_________
|
|
*
|
|
* For the instruction of the assignment please refer to the assignment
|
|
* GitHub.
|
|
*
|
|
* Plagiarism is a serious offense and can be easily detected. Please
|
|
* don't share your code to your classmate even if they are threatening
|
|
* you with your friendship. If they don't have the ability to work on
|
|
* something that can compile, they would not be able to change your
|
|
* code to a state that we can't detect the act of plagiarism. For the
|
|
* first commit of plagiarism, regardless you shared your code or
|
|
* copied code from others, you will receive 0 with an addition of 5
|
|
* mark penalty. If you commit plagiarism twice, your case will be
|
|
* presented in the exam board and you will receive a F directly.
|
|
*
|
|
* Terms about generative AI:
|
|
* You are not allowed to use any generative AI in this assignment.
|
|
* The reason is straight forward. If you use generative AI, you are
|
|
* unable to practice your coding skills. We would like you to get
|
|
* familiar with the syntax and the logic of the Java programming.
|
|
* We will examine your code using detection software as well as
|
|
* inspecting your code with our eyes. Using generative AI tool
|
|
* may fail your assignment.
|
|
*
|
|
* If you cannot work out the logic of the assignment, simply contact
|
|
* us on Discord. The teaching team is more the eager to provide
|
|
* you help. We can extend your submission due if it is really
|
|
* necessary. Just please, don't give up.
|
|
*/
|
|
|
|
public class l {
|
|
/**
|
|
* The following constants are variables that you can use in your code.
|
|
* Use them whenever possible. Try to avoid writing something like:
|
|
* if (input == 'W') ...
|
|
* instead
|
|
* if (input == UP) ...
|
|
*/
|
|
public static final char a = 'W';
|
|
public static final char b = 'S';
|
|
public static final char c = 'A';
|
|
public static final char d = 'D';
|
|
public static final char e = 'o';
|
|
public static final char f = '@';
|
|
public static final char g = '#';
|
|
public static final char h = '.';
|
|
public static final char i = '%';
|
|
|
|
/**
|
|
* Finished. You are not allowed to touch this method.
|
|
* The main method.
|
|
*/
|
|
public static void main(String[] arrstring) {
|
|
new l().a();
|
|
}
|
|
|
|
/**
|
|
* All coding of this method has been finished.
|
|
* You are not supposed to add or change any code in this method.
|
|
* However, you are required to add comments after every // to explain the code below.
|
|
*/
|
|
public void a() {
|
|
String string = "map1.txt";
|
|
char[][] arrc = this.b(string);
|
|
char[][] arrc2 = this.b(string);
|
|
if (arrc == null) {
|
|
System.out.println("Map file not found");
|
|
return;
|
|
}
|
|
int[] arrn = this.b(arrc);
|
|
if (arrn.length == 0) {
|
|
System.out.println("Player not found");
|
|
return;
|
|
}
|
|
int n2 = arrn[0];
|
|
int n3 = arrn[1];
|
|
while (!this.a(arrc)) {
|
|
this.c(arrc);
|
|
System.out.println("\nPlease enter a move (WASD): ");
|
|
char c2 = this.c();
|
|
if (c2 == 'q') break;
|
|
if (c2 == 'r') {
|
|
arrc = this.b(string);
|
|
n2 = arrn[0];
|
|
n3 = arrn[1];
|
|
continue;
|
|
}
|
|
if (c2 == 'h') {
|
|
this.b();
|
|
}
|
|
if (!this.c(arrc, n2, n3, c2)) continue;
|
|
this.b(arrc, n2, n3, c2);
|
|
this.a(arrc, arrc2);
|
|
int[] arrn2 = this.b(arrc);
|
|
n2 = arrn2[0];
|
|
n3 = arrn2[1];
|
|
}
|
|
System.out.println("Bye!");
|
|
}
|
|
|
|
/**
|
|
* Print the Help menu.
|
|
* TODO:
|
|
*
|
|
* Inspect the code in runApp() and find out the function of each characters.
|
|
* The first one has been done for you.
|
|
*/
|
|
public void b() {
|
|
System.out.println("Sokoban Help:");
|
|
System.out.println("Move up: W");
|
|
System.out.println("Move down: S");
|
|
System.out.println("Move left: A");
|
|
System.out.println("Move right: D");
|
|
System.out.println("Restart level: r");
|
|
System.out.println("Quit game: q");
|
|
System.out.println("Help: h");
|
|
}
|
|
|
|
/**
|
|
* Reading a valid input from the user.
|
|
*
|
|
* TODO
|
|
*
|
|
* This method will return a character that the user has entered. However, if a user enter an invalid character (e.g. 'x'),
|
|
* the method should keep prompting the user until a valid character is entered. Noted, there are all together 7 valid characters
|
|
* which you need to figure out yourself.
|
|
*/
|
|
public char c() {
|
|
char c2;
|
|
Scanner scanner = new Scanner(System.in);
|
|
while (true) {
|
|
String string;
|
|
if ((string = scanner.nextLine()).length() == 0) {
|
|
continue;
|
|
}
|
|
c2 = string.charAt(0);
|
|
if (c2 == 'W' || c2 == 'S' || c2 == 'A' || c2 == 'D' || c2 == 'q' || c2 == 'r' || c2 == 'h') break;
|
|
System.out.println("Invalid input, please enter again: ");
|
|
}
|
|
return c2;
|
|
}
|
|
|
|
/**
|
|
* Mysterious method.
|
|
*
|
|
* TODO
|
|
*
|
|
* We know this method is to "fix" the map. But we don't know how it does and why it is needed.
|
|
* You need to figure out the function of this method and implement it accordingly.
|
|
*
|
|
* You are given an additional demo program that does not implement this method.
|
|
* You can run them to see the difference between the two demo programs.
|
|
*/
|
|
// public void fixMap(_________________________) {
|
|
// }
|
|
public void a(char[][] arrc, char[][] arrc2) {
|
|
for (int i2 = 0; i2 < arrc.length; ++i2) {
|
|
for (int i3 = 0; i3 < arrc[i2].length; ++i3) {
|
|
if (arrc2[i2][i3] != '.' && arrc2[i2][i3] != '%' || arrc[i2][i3] != ' ') continue;
|
|
arrc[i2][i3] = 46;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* To move a box in a map.
|
|
*
|
|
* TODO
|
|
*
|
|
* This method will move a box in the map. The box will be moved to the direction specified by the parameter "direction".
|
|
* You must call this method somewhere in movePlayer() method.
|
|
*
|
|
* After this method, a box should be moved to the new position from the coordinate [row, col] according to the direction.
|
|
* For example, if [row, col] is [2, 5] and the direction is 'S', the box should be moved to [3, 5].
|
|
*
|
|
* If a box is moved to a goal, the box should be marked as BOXONGOAL.
|
|
* If a box is moved to a non-goal, the box should be marked as BOX.
|
|
* You should set the original position of the box to ' ' in this method.
|
|
*
|
|
* Note, you may always assume that this method is called when the box can be moved to the direction.
|
|
* During grading, we will never call this method when the box cannot be moved to the direction.
|
|
*/
|
|
public void a(char[][] arrc, int n2, int n3, char c2) {
|
|
int n4;
|
|
int[] arrn = this.a(n2, n3, c2);
|
|
int n5 = arrn[0];
|
|
arrc[n5][n4] = arrc[n5][n4 = arrn[1]] == '.' ? 37 : 64;
|
|
arrc[n2][n3] = arrc[n2][n3] == '%' ? 46 : 32;
|
|
}
|
|
|
|
/**
|
|
* To move the player in the map.
|
|
*
|
|
* TODO
|
|
*
|
|
* This method will move the player in the map. The player will be moved to the direction specified by the parameter "direction".
|
|
*
|
|
* After this method, the player should be moved to the new position from the coordinate [row, col] according to the direction.
|
|
* At the same time, the original position of the player should be set to ' '.
|
|
*
|
|
* During the move of the player, it is also possible that a box is also moved.
|
|
*
|
|
* Note, you may always assume that this method is called when the player can be moved to the direction.
|
|
* During grading, we will never call this method when the player cannot be moved to the direction.
|
|
*/
|
|
public void b(char[][] arrc, int n2, int n3, char c2) {
|
|
int n4;
|
|
int[] arrn = this.a(n2, n3, c2);
|
|
int n5 = arrn[0];
|
|
if (arrc[n5][n4 = arrn[1]] == '@' || arrc[n5][n4] == '%') {
|
|
this.a(arrc, n5, n4, c2);
|
|
}
|
|
arrc[n5][n4] = 111;
|
|
arrc[n2][n3] = 32;
|
|
}
|
|
|
|
/**
|
|
* To check if the game is over.
|
|
*
|
|
* TODO
|
|
*
|
|
* This method should return true if the game is over, false otherwise.
|
|
* The condition for game over is that there is no goal left in the map that is not covered by a box.
|
|
*
|
|
* According to this definition, if the number of goal is actually more than the number of boxes,
|
|
* the game will never end even through all boxes are placed on the goals.
|
|
*/
|
|
public boolean a(char[][] arrc) {
|
|
for (int i2 = 0; i2 < arrc.length; ++i2) {
|
|
for (int i3 = 0; i3 < arrc[i2].length; ++i3) {
|
|
if (arrc[i2][i3] != '.') continue;
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* To count the number of rows in a file.
|
|
*
|
|
* TODO
|
|
*
|
|
* This method should return the number of rows in the file which filename is stated in the argument.
|
|
* If the file is not found, it should return -1.
|
|
*/
|
|
public int a(String string) {
|
|
try {
|
|
Scanner scanner = new Scanner(new File(string));
|
|
int n2 = 0;
|
|
while (scanner.hasNextLine()) {
|
|
++n2;
|
|
scanner.nextLine();
|
|
}
|
|
return n2;
|
|
}
|
|
catch (Exception exception) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* To read a map from a file.
|
|
*
|
|
* TODO
|
|
*
|
|
* This method should return a 2D array of characters which represents the map.
|
|
* This 2D array should be read from the file which filename is stated in the argument.
|
|
* If the file is not found, it should return null.
|
|
*
|
|
* The number of columns in each row may be different. However, there is no restriction on
|
|
* the number of columns that is declared in the array. You can declare the number of columns
|
|
* in your array as you wish, as long as it is enough to store the map.
|
|
*
|
|
* That is, if the map is as follow,
|
|
* ####
|
|
* #.@o#
|
|
* # #
|
|
* ###
|
|
* your array may be declared as
|
|
* char[][] map = {{'#', '#', '#', '#'},
|
|
* {'#', '.', '@', 'o', '#'},
|
|
* {'#', ' ', ' ', '#'},
|
|
* {'#', '#', '#'} };
|
|
* or something like
|
|
* char[][] map = {{'#', '#', '#', '#', ' ', ' ', ' '},
|
|
* {'#', '.', '@', 'o', '#', ' ', ' '},
|
|
* {'#', ' ', ' ', '#', ' ', ' ', ' '},
|
|
* {'#', '#', '#', ' ', ' ', ' ', ' '} };
|
|
*/
|
|
public char[][] b(String string) {
|
|
int n2 = this.a(string);
|
|
if (n2 == -1) {
|
|
return null;
|
|
}
|
|
char[][] arrarrc = new char[n2][];
|
|
try (Scanner scanner = new Scanner(new File(string));){
|
|
int n3 = 0;
|
|
while (true) {
|
|
int n4;
|
|
String string2;
|
|
Object object;
|
|
if (scanner.hasNextLine()) {
|
|
object = scanner.nextLine();
|
|
string2 = "";
|
|
} else {
|
|
object = arrarrc;
|
|
return object;
|
|
}
|
|
for (n4 = ((String)object).length() - 1; n4 >= 0 && ((String)object).charAt(n4) == ' '; --n4) {
|
|
}
|
|
for (int i2 = 0; i2 <= n4; ++i2) {
|
|
string2 = string2 + ((String)object).charAt(i2);
|
|
}
|
|
arrarrc[n3] = string2.toCharArray();
|
|
++n3;
|
|
}
|
|
}
|
|
catch (Exception exception) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* To find the coordinate of player in the map.
|
|
*
|
|
* TODO
|
|
*
|
|
* This method should return a 2D array that stores the [row, col] of the player in the map.
|
|
* For example, if the map is as follow,
|
|
* ####
|
|
* #.@o#
|
|
* # #
|
|
* ###
|
|
* this method should return {1, 3}.
|
|
*
|
|
* In case there is no player in the map, this method should return null.
|
|
*/
|
|
public int[] b(char[][] arrc) {
|
|
for (int i2 = 0; i2 < arrc.length; ++i2) {
|
|
for (int i3 = 0; i3 < arrc[i2].length; ++i3) {
|
|
if (arrc[i2][i3] != 'o') continue;
|
|
int[] arrn = new int[]{i2, i3};
|
|
return arrn;
|
|
}
|
|
}
|
|
int[] arrn = new int[]{-1, -1};
|
|
return arrn;
|
|
}
|
|
|
|
/**
|
|
* To check if a move is valid.
|
|
*
|
|
* TODO
|
|
*
|
|
* This method should return true if the move is valid, false otherwise.
|
|
* The parameter "map" represents the map.
|
|
* The parameter "row" and "col" indicates where the player is.
|
|
* The parameter "direction" indicates the direction of the move.
|
|
* At the end of the method, this method should not change any content of the map.
|
|
*
|
|
* The physics of the game is as follow:
|
|
* 1. The player can only move to a position that is not occupied by a wall or a box.
|
|
* 2. If the player is moving to a position that is occupied by a box, the box can only be moved to a position that is not occupied by a wall or a box.
|
|
*
|
|
* Thus, in the following condition, the player can move to the right
|
|
* o # <-- there is a space
|
|
* o@ # <-- there is a space right to the box.
|
|
* In the following condition, the player cannot move to the right
|
|
* o# <-- there is a wall
|
|
* o@# <-- there is a wall right to the box.
|
|
* o@@ # <-- there is a box right to the box.
|
|
*/
|
|
public boolean c(char[][] arrc, int n2, int n3, char c2) {
|
|
return this.a(arrc, n2, n3, c2, true);
|
|
}
|
|
|
|
/**
|
|
* To print the map.
|
|
*
|
|
* TODO
|
|
*
|
|
* This method should print the map in the console.
|
|
* At the top row, it should print a space followed by the last digit of the column indexes.
|
|
* At the leftmost column, it should print the last two digits of row indexes, aligning to the left.
|
|
*/
|
|
public void c(char[][] arrc) {
|
|
int n2;
|
|
if (arrc == null) {
|
|
return;
|
|
}
|
|
int n3 = arrc[0].length;
|
|
for (n2 = 1; n2 < arrc.length; ++n2) {
|
|
if (n3 >= arrc[n2].length) continue;
|
|
n3 = arrc[n2].length;
|
|
}
|
|
System.out.print(" ");
|
|
for (n2 = 0; n2 < n3; ++n2) {
|
|
System.out.print(n2 % 10);
|
|
}
|
|
System.out.println();
|
|
for (n2 = 0; n2 < arrc.length; ++n2) {
|
|
System.out.printf("%-2d", n2 % 100);
|
|
for (int i2 = 0; i2 < arrc[n2].length; ++i2) {
|
|
System.out.print(arrc[n2][i2]);
|
|
}
|
|
System.out.println();
|
|
}
|
|
}
|
|
|
|
private int[] a(int n2, int n3, int n4) {
|
|
int[] arrn = new int[]{n2, n3};
|
|
switch (n4) {
|
|
case 87: {
|
|
arrn[0] = arrn[0] - 1;
|
|
break;
|
|
}
|
|
case 83: {
|
|
arrn[0] = arrn[0] + 1;
|
|
break;
|
|
}
|
|
case 65: {
|
|
arrn[1] = arrn[1] - 1;
|
|
break;
|
|
}
|
|
case 68: {
|
|
arrn[1] = arrn[1] + 1;
|
|
}
|
|
}
|
|
return arrn;
|
|
}
|
|
|
|
private boolean a(char[][] arrc, int n2, int n3, char c2, boolean bl) {
|
|
int[] arrn = this.a(n2, n3, c2);
|
|
int n4 = arrn[0];
|
|
int n5 = arrn[1];
|
|
if (n4 < 0 || n4 >= arrc.length || n5 < 0 || n5 >= arrc[n4].length) {
|
|
return false;
|
|
}
|
|
if (arrc[n4][n5] == '#') {
|
|
return false;
|
|
}
|
|
if (arrc[n4][n5] == '@' || arrc[n4][n5] == '%') {
|
|
if (bl) {
|
|
return this.a(arrc, n4, n5, c2, false);
|
|
}
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|