This commit is contained in:
louiscklaw
2025-01-31 20:04:51 +08:00
parent 56285a7550
commit 2a6f19a43f
18 changed files with 1532 additions and 0 deletions

7
yena_/gitUpdate.bat Normal file
View File

@@ -0,0 +1,7 @@
git status .
@pause
git add .
git commit -m"update yena_,"
start git push

7
yena_/meta.md Normal file
View File

@@ -0,0 +1,7 @@
---
tags: [java, console, HKBU, COMP2026, COMP2045]
---
# yena\_
https://github.com/khwang0/comp2045-2324pa1

25
yena_/task1/COMP2045-2324PA1/.gitignore vendored Normal file
View File

@@ -0,0 +1,25 @@
# Compiled class file
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# Local solution that should never be committed. Or diseaster~~~
Solution/**

View File

@@ -0,0 +1,5 @@
public class Main {
public static void main(String[] args) {
System.out.println("Hello World");
}
}

View File

@@ -0,0 +1,458 @@
import java.util.Scanner;
import java.io.File;
/**
* @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 Sokoban {
/**
* 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 UP = 'W';
public static final char DOWN = 'S';
public static final char LEFT = 'A';
public static final char RIGHT = 'D';
public static final char PLAYER = 'o';
public static final char BOX = '@';
public static final char WALL = '#';
public static final char GOAL = '.';
public static final char BOXONGOAL = '%';
/**
* Finished. You are not allowed to touch this method.
* The main method.
*/
public static void main(String[] args) {
new Sokoban().runApp();
}
/**
* 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 runApp() {
String mapfile = "map1.txt"; //change this to test other maps
char[][] map = readmap(mapfile); //
char[][] oldMap = readmap(mapfile); //
if (map == null) { //
System.out.println("Map file not found");
return;
}
int[] start = findPlayer(map); //
if (start.length == 0) { //
System.out.println("Player not found");
return;
}
int row = start[0];
int col = start[1];
while (!gameOver(map)) { //
printMap(map);
System.out.println("\nPlease enter a move (WASD): ");
char input = readValidInput(); //
if (input == 'q') //
break;
if (input == 'r') { //
map = readmap(mapfile);
row = start[0]; //
col = start[1];
continue;
}
if (input == 'h') { //
printHelp();
}
if (!isValid(map, row, col, input)) //
continue;
movePlayer(map, row, col, input); //
// fixMap(map, oldMap); //
int[] newPos = findPlayer(map); //
row = newPos[0]; //
col = newPos[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 printHelp() {
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 readValidInput() {
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(_________________________) {
// }
/**
* 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 moveBox(char[][] map, int row, int col, char direction) {
// int n4;
// int[] arrn = this.a(row, col, direction);
// int n5 = arrn[0];
// map[n5][n4] = map[n5][n4 = arrn[1]] == '.' ? 37 : 64;
// map[row][col] = map[row][col] == '%' ? 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 movePlayer(char[][] map, int row, int col, char direction) {
// int n4;
// int[] arrn = this.a(row, col, direction);
// int n5 = arrn[0];
// if (map[n5][n4 = arrn[1]] == '@' || map[n5][n4] == '%') {
// this.a(map, n5, n4, direction);
// }
// map[n5][n4] = 111;
// map[row][col] = 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 gameOver(char[][] map) {
for (int i2 = 0; i2 < map.length; ++i2) {
for (int i3 = 0; i3 < map[i2].length; ++i3) {
if (map[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 numberOfRows(String fileName) {
try {
Scanner scanner = new Scanner(new File(fileName));
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[][] readmap(String fileName) {
return null;
// 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[] findPlayer(char[][] map) {
for (int i = 0; i < map.length; ++i) {
for (int j = 0; j < map[i].length; ++j) {
if (map[i][j] != 'o') continue;
int[] arrn = new int[]{i, j};
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 isValid(char[][] map, int row, int col, char direction) {
// return true;
return this.a(map, row, col, direction, 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 printMap(char[][] map) {
int i;
if (map == null) {
return;
}
int map_length = map[0].length;
for (i = 1; i < map.length; ++i) {
if (map_length >= map[i].length) continue;
map_length = map[i].length;
}
System.out.print(" ");
for (i = 0; i < map_length; ++i) {
// print header row
System.out.print(i % 10);
}
System.out.println();
for (i = 0; i < map.length; ++i) {
System.out.printf("%-3d", i % 100);
for (int j = 0; j < map[i].length; ++j) {
System.out.print(map[i][j]);
}
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;
}
// check if the move is valid
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;
}
}

BIN
yena_/task1/COMP2045-2324PA1/Sokoban_ani.gif (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1,379 @@
// Test Sokoban with junit
import org.junit.Test;
import java.io.*;
import java.util.Arrays;
import java.util.HashMap;
import static org.junit.Assert.*;
public class TestClass {
// test for readValidInput
@Test
public void readValidInputTest(){
System.out.println("readValidInput() method test: ");
String[] testCases = { "W", "A", "S", "D", "q", "r", "h" };
String[] negativeTestCases = { "w", "a", "s", "d", "Q", "R", "H", "1", "2", "3" };
String[] allCases = Arrays.copyOf(testCases, testCases.length + negativeTestCases.length);
System.arraycopy(negativeTestCases, 0, allCases, testCases.length, negativeTestCases.length);
HashMap<String, Boolean> results = new HashMap<>();
Thread[] inputThreads = new Thread[allCases.length];
// save the original output streams
PrintStream originalOut = System.out;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
System.setOut(new PrintStream(baos));
for (int i = 0; i < allCases.length; i++) {
final int index = i;
inputThreads[i] = new Thread(() -> {
try {
InputStream inputStream = System.in;
System.setIn(new ByteArrayInputStream(allCases[index].getBytes()));
Sokoban sokoban = new Sokoban();
char cleanedChar = sokoban.readValidInput();
results.put(allCases[index], cleanedChar == allCases[index].charAt(0));
System.setIn(inputStream);
} catch (Exception e) {}
});
}
for (int i = 0; i < allCases.length; i++) {
try {
inputThreads[i].start();
inputThreads[i].join(50);
} catch (InterruptedException e) {}
}
System.setOut(originalOut);
for (int i = 0; i < testCases.length; i++) {
if(!results.containsKey(testCases[i])){
fail("InputTest failed for " + testCases[i]);
}
assertTrue(results.containsKey(testCases[i]));
}
System.out.println("\tPassed for positive test cases");
for (int i = 0; i < negativeTestCases.length; i++) {
if(results.containsKey(negativeTestCases[i])){
fail("InputTest failed for " + negativeTestCases[i]);
}
assertFalse(results.containsKey(negativeTestCases[i]));
}
System.out.println("\tPassed for negative test cases");
}
// test for moveBox
@Test
public void moveBoxTest(){
System.out.println("moveBox() method test: ");
Sokoban sokoban = new Sokoban();
char[][] testMapOri = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','o','@','.',' ',' ','#'},
{'#',' ',' ','.',' ',' ','#'},
{'#',' ',' ','@',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
char[][] resultMapD = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','o','@','.',' ',' ','#'},
{'#',' ',' ','.',' ',' ','#'},
{'#',' ',' ',' ','@',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
char[][] resultMapW = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','o','@','.',' ',' ','#'},
{'#',' ',' ','%',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
char[][] resultMapS = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','o','@','.',' ',' ','#'},
{'#',' ',' ','.',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ',' ','@',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
char[][] resultMapA = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','o','@','.',' ',' ','#'},
{'#',' ',' ','.',' ',' ','#'},
{'#',' ','@',' ',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
// deep copy the testMapOri to testMap
char[][] testMap = CopyMap(testMapOri);
sokoban.moveBox(testMap, 4, 3, 'D');
assertTrue(Arrays.deepEquals(resultMapD, testMap));
testMap = CopyMap(testMapOri);
sokoban.moveBox(testMap, 4, 3, 'W');
assertTrue(Arrays.deepEquals(resultMapW, testMap));
testMap = CopyMap(testMapOri);
sokoban.moveBox(testMap, 4, 3, 'S');
assertTrue(Arrays.deepEquals(resultMapS, testMap));
testMap = CopyMap(testMapOri);
sokoban.moveBox(testMap, 4, 3, 'A');
assertTrue(Arrays.deepEquals(resultMapA, testMap));
System.out.println("\tPassed");
}
private char[][] CopyMap(char[][] map){
char[][] copy = new char[map.length][];
for(int i = 0; i < map.length; i++){
copy[i] = map[i].clone();
}
return copy;
}
@Test
public void movePlayerTest(){
System.out.println("movePlayer() method test: ");
Sokoban sokoban = new Sokoban();
char[][] testMapOri = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ','@','.',' ',' ','#'},
{'#',' ',' ','.',' ',' ','#'},
{'#',' ',' ','@',' ',' ','#'},
{'#',' ',' ','o',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
char[][] resultMapW = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ','@','.',' ',' ','#'},
{'#',' ',' ','%',' ',' ','#'},
{'#',' ',' ','o',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
char[][] resultMapD = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ','@','.',' ',' ','#'},
{'#',' ',' ','.',' ',' ','#'},
{'#',' ',' ','@',' ',' ','#'},
{'#',' ',' ',' ','o',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
char[][] resultMapS = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ','@','.',' ',' ','#'},
{'#',' ',' ','.',' ',' ','#'},
{'#',' ',' ','@',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ',' ','o',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
char[][] resultMapA = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ','@','.',' ',' ','#'},
{'#',' ',' ','.',' ',' ','#'},
{'#',' ',' ','@',' ',' ','#'},
{'#',' ','o',' ',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
// deep copy the testMapOri to testMap
char[][] testMap = CopyMap(testMapOri);
sokoban.movePlayer(testMap, 5, 3, 'D');
assertTrue(Arrays.deepEquals(resultMapD, testMap));
testMap = CopyMap(testMapOri);
sokoban.movePlayer(testMap, 5, 3, 'W');
assertTrue(Arrays.deepEquals(resultMapW, testMap));
testMap = CopyMap(testMapOri);
sokoban.movePlayer(testMap, 5, 3, 'S');
assertTrue(Arrays.deepEquals(resultMapS, testMap));
testMap = CopyMap(testMapOri);
sokoban.movePlayer(testMap, 5, 3, 'A');
assertTrue(Arrays.deepEquals(resultMapA, testMap));
System.out.println("\tPassed");
}
@Test
public void gameOverTest(){
System.out.println("gameOver() method test: ");
Sokoban sokoban = new Sokoban();
char[][] negativeTestMap = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','o','@','.',' ',' ','#'},
{'#',' ',' ','.',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ',' ','@',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
char[][] positiveTestMap = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','o',' ','%',' ',' ','#'},
{'#',' ',' ','%',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
assertTrue(sokoban.gameOver(positiveTestMap));
assertFalse(sokoban.gameOver(negativeTestMap));
System.out.println("\tPassed");
}
@Test
public void numberOfRowsTest(){
System.out.println("numberOfRows() method test: ");
Sokoban sokoban = new Sokoban();
assertEquals(11, sokoban.numberOfRows("map1.txt"));
System.out.println("\tPassed");
}
@Test
public void readmapTest(){
System.out.println("readmap() method test: ");
Sokoban sokoban = new Sokoban();
char[][] map = sokoban.readmap("map1.txt");
char[][] expected = new char[][]{
{' ', ' ', '#', '#', '#', '#', '#'},
{'#', '#', '#', ' ', ' ', ' ', '#'},
{'#', '.', 'o', '@', ' ', ' ', '#'},
{'#', '#', '#', ' ', '@', '.', '#'},
{'#', '.', '#', '#', '@', ' ', '#'},
{'#', ' ', '#', ' ', '.', ' ', '#', '#'},
{'#', '@', ' ', '%', '@', '@', '.', '#'},
{'#', ' ', ' ', ' ', ' ', ' ', ' ', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#'},
{'#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#'},
{'#', ' ', ' ', ' ', '.', ' ', ' ', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#'},
{'#', '#', '#', '#', '#', '#', '#', '#'}
};
for(int i = 0; i < Math.min(map.length, expected.length); i++){
for(int j = 0; j < Math.min(map[i].length, expected[i].length); j++){
assertEquals(expected[i][j], map[i][j]);
}
}
System.out.println("\tPassed");
}
@Test
public void findPlayerTest(){
System.out.println("findPlayer() method test: ");
Sokoban sokoban = new Sokoban();
char[][] testMap = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ','@','.',' ',' ','#'},
{'#',' ',' ','.',' ',' ','#'},
{'#',' ',' ',' ',' ','o','#'},
{'#',' ',' ','@',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
int[] expected = new int[]{4, 5};
int[] result = sokoban.findPlayer(testMap);
assertArrayEquals(expected, result);
System.out.println("\tPassed");
}
@Test
public void isValidTest(){
System.out.println("isValid() method test: ");
char[][] negativeTestMap01 = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','o','@','%',' ',' ','#'},
{'#',' ',' ','.',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
char[][] negativeTestMap02 = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ','@','.',' ',' ','#'},
{'#',' ',' ','.',' ',' ','#'},
{'#',' ',' ',' ',' ','o','#'},
{'#',' ',' ','@',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
char[][] positiveTestMap01 = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ','@','.',' ',' ','#'},
{'#',' ',' ','.',' ',' ','#'},
{'#',' ',' ',' ','o',' ','#'},
{'#',' ',' ','@',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
char[][] positiveTestMap02 = new char[][]{
{'#','#','#','#','#','#','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ','@','.',' ',' ','#'},
{'#',' ',' ','.',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#',' ','o','@',' ',' ','#'},
{'#',' ',' ',' ',' ',' ','#'},
{'#','#','#','#','#','#','#'}
};
Sokoban sokoban = new Sokoban();
assertFalse(sokoban.isValid(negativeTestMap01, 2,1, 'D'));
assertFalse(sokoban.isValid(negativeTestMap02, 4,5, 'D'));
assertTrue(sokoban.isValid(positiveTestMap01,4,4, 'D'));
assertTrue(sokoban.isValid(positiveTestMap02,5,2, 'D'));
System.out.println("\tPassed");
}
}

View File

@@ -0,0 +1,454 @@
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;
}
}

BIN
yena_/task1/COMP2045-2324PA1/hkbu.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
yena_/task1/COMP2045-2324PA1/img/add-5.5.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
yena_/task1/COMP2045-2324PA1/img/add-JUnit.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
yena_/task1/COMP2045-2324PA1/img/notok.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
yena_/task1/COMP2045-2324PA1/img/ok.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
yena_/task1/COMP2045-2324PA1/img/runTest.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1,11 @@
#####
### #
#.o@ #
### @.#
#.##@ #
# # . ##
#@ %@@.#
# ##########
# #
# . ##########
########

View File

@@ -0,0 +1,151 @@
![](hkbu.png)
# COMP2026/COMP2045 Programming Assignment 1 - Sokoban
## 2023-24 Semester 1
* Designed by: [Dr. Kevin Wang](mailto:kevinw@comp.hkbu.edu.hk)
* Q & A: [Discord](https://discordapp.com/channels/1004554070083776672/1004554070083776678)
* Assignment Website: [GitHub](https://github.com/khwang0/COMP2045-2324PA1)
* Due:
* UAT Due: 23:59pm 2/10/2023 (Monday midnight)
* Programming Due: 23:59pm 19/10/2023 (Thursday midnight)
* Download the starter code: [Sokoban](Sokoban.java)
* Download the demo program: [here](demo.jar)
* Download everything from the assignment: [here](https://github.com/khwang0/COMP2045-2324PA1/archive/refs/heads/master.zip)
> To run the demo program, type the following in your terminal:
>
> ```sh
> > java -jar demo.jar
> ```
![](Sokoban_ani.gif)
<sub><sup>
image ref: [Carloseow at English Wikipedia](https://en.wikipedia.org/wiki/Sokoban#/media/File:Sokoban_ani.gif)
</sup></sub>
# Learning outcome
Students are expected to have some practice on arrays/2D arrays/parameter passing/method construction and usage in this assignment. We expect most students would spend six hours or more to finish the assignment without any assistance. Make sure you start earlier as possible and ask us on Discord if you have any difficulty! Note: we want to familiarize arrays and 2D arrays, so you are not supposed (and not allowed) to use advanced data structures like ArrayList, Set, Map, etc...
# Introduction
You are going to complete the Sokoban program! Open [Sokoban.java](Sokoban.java) and complete the methods stated in the skeleton code. Some of these methods are very straightforward while some of them ain't that easy. Make sure you can follow the instructions given at the top of each method.
A [sample program](demo.jar) is given to you. When there is something you are not sure, you can take a look at the sample program to decide what to do. There is [another sample programe](demo_without_fixmap.jar) that would support you to deduce the function of the method `fixmap`.
Some methods are labeled as completed or given. Please don't make any change on those methods. You are not supposed to modify them. **You can add your own method if you want to**.
## Explanation of the game Sokoban
You can skip this section if you have some experience in playing the game Sokoban. It is a role play game where a player tries to push boxes to the target locations. The player can only push the box but not pull it. The player can only push one box at a time. The player can only move to an empty space or a target location. The player cannot move to a wall or a box. The player wins the game when all target locations have a box on it. The player loses the game when he/she is stuck and cannot move any more.
## Explanation about the assignment
You are given the skeleton code. Complete all methods in the assignment with respect to the instructions stated in the Java file.
## `TestClass` class
You don't need to do anything with this java file. The purpose of this file is to conduct some *unit test* for your code. If you are using IntelliJ, you should be able to use this file within a few quick steps. By looking at this file, you should have some ideas of how the TA grade your work!
1. Open the file `TestClass.java`
2. Click on the word `@Test` at line 31.
![](img/add-JUnit.png)
3. Click the red bulb and select `Add JUnit4 to classpath`.
![](img/add-5.5.png)
4. Click the arrow on the left of `public class TestClass` at line 17 and select `Run Tests`.
![](img/runTest.png)
5. You shall see the following screen if there isn't any problem
![](img/ok.png)
6. Or you will see some error if your code does not perform what it is suppose to do. For example in the figure below, the test case `testReadValidInput` is not working properly.
![](img/notok.png)
> Note: If you are struggle with this file (e.g. cannot compile your code), simply delete this file.
# Understanding the Assignment Test (UAT)
This part is independent to your programming code. You will need to answer the following short questions by **another due date**.
Submit your answers on Moodle.
1. Which method is responsible to print the game board?
2. Which method is responsible to decide if the game ends?
3. Can a player push two boxes at a time?
4. What key should I press if I decide to restart the game?
5. What does the method `fixmap` do?
---
# Submission
For submission upload the file `Sokoban.java` to Moodle.
Please be reminded that both the **Late Penalty Rule** and the **Penalty for Plagiarism** are applied strictly to all submissions of this course (including this assignment).
### Late Penalty Rule
```java
if (lateHour > 0) {
if (lateHour < 24)
mark *= 0.8;
else if (lateHour < 48)
mark = mark >> 1;
else if (lateHour < 72)
mark = mark >> 2;
else
mark &= 0;
}
```
## Plagiarism
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 practise 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.
## Marking Scheme
This assignment is worth 14% of the course mark (7% for COMP2026). There are three elements in the marking scheme:
* 5% - Understanding the Assignment Test (UAT)
* 10% - the program can be compiled without any error
* 85% - a working program that functions as specified
* -50% - if you define any class variable (field), addition class, or change the method `main`.
* -30% - if you use any advanced data structure such as `java.util.List`, `java.util.ArrayList`, `java.util.Arrays`, `java.util.Set`, `java.util.Map` etc to replace your primitive array.
* -10% - Unable to fill adequate comments, e.g. the `runApp` method.
Please note that submitting a program that cannot be compiled would result in a very low mark.
For the correctness of your program, we will test your program with a set of test cases. The test cases will be similar to the sample program. All test cases will be released after the submission deadline.
## Interview
Should the teaching team see fit, students may be requested to attend an interview to explain about their program. Students failing to attend such interview or to demonstrate a good understanding of their own program may result in mark deduction.

11
yena_/task1/notes.md Normal file
View File

@@ -0,0 +1,11 @@
經過考慮,我諗我真係下次先幫到你
不過嘅然我花左時間落去,唔好浪費
呢個係我做到一半嘅 code. 你試下理解再謄一次應該有用
希望幫到你
ttps://share-staging.louislabs.com/g/u-EHjpjjCmb
千其唔好成個就咁攞去交,
記得自己消化左之後寫返次出黎
如果唔係後果自負