Java Java-Datei refaktorieren

atlyaca

Newbie
Registriert
Dez. 2016
Beiträge
4
Hallo java-forum,

ich würde gerne meinen Quellcode in verschieden Klassen refaktorieren, sprich aufräumen. Komme jedoch nicht wirklich weiter. Der Code soll aus drei Teilen bestehen.

1.Sokoban
In diesem Part möchte ich die main-Methode einbringen. Dieser soll auf 2.Player und 3.Level zugreifen können.
2.Player
Hier möchte ich mit einer move-Methode alle auf den Spieler bezogenen Informationen unterbringen.
3.Level
Hier soll das Level (.txt Datei) geladen werden.

Mein Ansatz:
Ich habe den Quellcode mit den jeweils relevanten Bereichen in eigene Klassen geteilt, ohne was am Quellcode zu verändern. Habe es aber nicht geschafft, dass die main-Methode auf die anderen Klassen zugreifen kann.

Würde mich über jeden Ansatz freuen.
MfG

Quellcode
Code:
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Scanner;

/**
* This class is the second part for the Sokoban game
*
* @author Jane Doe 1234567 Group 42h
* @author John Doe 1234567 Group 42h
*/
class Sokoban {

    private final static int X = 0;
    private final static int Y = 1;

    private final static char WALL = '#';
    private final static char PLAYER = '@';
    private final static char BOX = '$';
    private final static char GOAL = '.';
    private final static char PLAYER_ON_GOAL = '+';
    private final static char BOX_ON_GOAL = '*';
    private final static char FREE = ' ';

    private final static int[] UP = {0, -1};
    private final static int[] DOWN = {0, 1};
    private final static int[] LEFT = {-1, 0};
    private final static int[] RIGHT = {1, 0};

    private static char[][] room;
    private static int freeBox;
    private static int emptyGoal;

    private static int[] size = {-1, 0};
    private static int[] player;

    /**
     * Loads the level from the "file" and validate it
     *
     * @param file path to the file
     * @return false iff an error occurs or the level is invalid, true otherwise
     */
    private static boolean loadLevel(String file) {
        BufferedReader bufferedReader;
        try {
            bufferedReader = Files.newBufferedReader(Paths.get(file));
            bufferedReader.mark(100 * 100);
            String line;
            while ((line = bufferedReader.readLine()) != null) {
                size[Y]++;
                if (size[X] > -1 && size[X] != line.length()) {
                    return false;
                } else {
                    size[X] = line.length();
                }
            }

            bufferedReader.reset();
            room = new char[size[Y]][];

            int i = 0;
            while ((line = bufferedReader.readLine()) != null) {
                room[i] = new char[line.length()];
                for (int j = 0; j < line.length(); j++) {
                    room[i][j] = line.charAt(j);
                }
                i++;
                // oder room[i++] = line.toCharArray();
            }
            bufferedReader.close();
        } catch (IOException e) {
            return false;
        }

        for (int i = 0; i < room.length; i++) {
            for (int j = 0; j < room[i].length; j++) {
                switch (room[i][j]) {
                    case FREE:
                    case BOX_ON_GOAL:
                    case WALL:
                        break;
                    case PLAYER_ON_GOAL:
                        emptyGoal++;
                    case PLAYER:
                        if (player != null) {
                            return false;
                        } else {
                            player = new int[]{j, i};
                        }
                        break;
                    case BOX:
                        freeBox++;
                        break;
                    case GOAL:
                        emptyGoal++;
                        break;
                    default:
                        return false;
                }
            }
        }
        return !(player == null || emptyGoal != freeBox);
    }

    /**
     * Prints the level to the output stream
     */
    private static void printLevel() {
        for (char[] row : room) {
            System.out.println(row);
        }
    }

    /**
     * Function for vector addition
     *
     * @param first  first vector
     * @param second second vector
     * @return new vector = first + second
     */
    private static int[] add(int[] first, int[] second) {
        return new int[]{first[X] + second[X], first[Y] + second[Y]};
    }

    /**
     * Game logic for Sokoban
     *
     * @return true if the level was solved, otherwise false
     */
    private static boolean game() {
        // create new Scanner that reads from console
        Scanner input = new Scanner(System.in);

        // flag if we quit the program
        boolean run = true;
        int[] direction;
        do {
            printLevel();
            System.out.println("Do you want to go up, down, left, right or exit the program?");

            // check which command was chosen and execute it
            switch (input.next()) {
                case "w":
                case "up":
                    direction = UP;
                    break;
                case "s":
                case "down":
                    direction = DOWN;
                    break;
                case "a":
                case "left":
                    direction = LEFT;
                    break;
                case "d":
                case "right":
                    direction = RIGHT;
                    break;
                case "exit":
                    run = false;
                    continue;
                default: // if the user input is not one of our commands print help
                    System.out.println("Command unknown! Please type up, down, left or right to move or exit to quit this program");
                    continue;
            }

            if (!move(direction)) {
                System.out.println("You can not go there!");
            }
        } while (run && emptyGoal != 0 && freeBox != 0);
        return run;
    }

    /**
     * Makes a move
     *
     * @param direction as a vector
     * @return true iff it was successful, otherwise false
     */
    private static boolean move(int[] direction) {
        int[] next = add(player, direction);

        switch (room[next[Y]][next[X]]) {
            case BOX_ON_GOAL:
            case BOX:
                int[] behind = add(next, direction);
                if (!(room[behind[Y]][behind[X]] == FREE || room[behind[Y]][behind[X]] == GOAL)) {
                    return false;
                }

                if (room[next[Y]][next[X]] == BOX_ON_GOAL) {
                    emptyGoal++;
                    freeBox++;
                }

                if (room[behind[Y]][behind[X]] == GOAL) {
                    room[behind[Y]][behind[X]] = BOX_ON_GOAL;
                    emptyGoal--;
                    freeBox--;
                } else {
                    room[behind[Y]][behind[X]] = BOX;
                }

                if (room[next[Y]][next[X]] == BOX_ON_GOAL) {
                    room[next[Y]][next[X]] = GOAL;
                } else {
                    room[next[Y]][next[X]] = FREE;
                }
            case GOAL:
            case FREE:
                if (room[player[Y]][player[X]] == PLAYER_ON_GOAL) {
                    room[player[Y]][player[X]] = GOAL;
                } else {
                    room[player[Y]][player[X]] = FREE;
                }

                player = next;

                if (room[player[Y]][player[X]] == FREE) {
                    room[player[Y]][player[X]] = PLAYER;
                } else {
                    room[player[Y]][player[X]] = PLAYER_ON_GOAL;
                }
                return true;
            default:
                return false;
        }
    }

    /**
     * The Main method for the Sokoban game with contains all of the game logic
     *
     * @param args args[0] the path to the level
     */
    public static void main(String[] args) {
        String file = "sokoban.txt";
        if (args.length > 0) {
            file = args[0];
        }
        if (!loadLevel(file)) {
            System.err.println("Level has an invalid format");
            return;
        }
        if (game()) {
            System.out.println("Yeah you have solved the level :)");
        } else {
            System.out.println("You have not solved the level :(");
        }
        printLevel();
        System.out.println("Goodbye");
    }
}
 
atlyaca schrieb:
Habe es aber nicht geschafft, dass die main-Methode auf die anderen Klassen zugreifen kann.

Heißt? Offensichtlich handelt es sich dabei um eine Hausaufgabe... Aber immerhin hast du dir bisschen Mühe beim Post gegeben.

Du musst dafür sorgen, dass die Klassen (also Sokoban, Player & Level) miteinander kommunizieren können. Bisher hast du Klassenvariablen wie "room", welche von loadLevel geschrieben und von anderen gelesen wird. Da müsstest du dir Gedanken machen, wie du das am besten löst.
 
Ja es sind Hausaufgaben und der Quellcode ist auch nur eine Musterlösung. Ich möchte hier nur einen Ansatz finden den ich dann auf meinen eigenen Code anwenden kann :)

Hab leider überhaupt keinen Ansatz wie die Klassen miteinander kommunizieren sollen :)
 
Naja, ein primitiver Ansatz wäre, nur die loadLevel() Methode auszulagern. Aber du möchtest ja, dass Level nicht nur ein Level lädt, sondern das Level beschreibt. Das ist ein Unterschied ,)

Ich würde zumindest damit anfangen, die loadLevel() Methode in eine Klasse namens "Level" zu packen. Dann würde ich mir überlegen, wie ich von außen auf das Level zugreifen möchte.

Ich würde also gerne eine Methode at(int x, int y) oder so basteln, die mir den Wert von room[x][y] liefert. Dort kann man dann natürlich noch Checks zum Vermeiden von ArrayIndexOutOfBoundsExceptions einbauen.

Dann würde ich von außen eben nur die at() Methode zum lesen von Levelblöcken nutzen. Die Konstanten WALL, PLAYER, etc. würde ich ebenfalls in Level auslagern.

Und dann kannst du natürlich auch eine setBlock() oder set() Methode zum Schreiben von Spielflächen schreiben.

EDIT:
Einzig tricky, wo mir jetzt auf Anhieb nichts einfällt, ist das Verwalten der Spieler. Schließlich schreibt / initialisiert die ja die loadLevel() Methode, wärend move(), die ja in die Player-Klasse soll, die ebenfalls benutzt. Also müsste man sich da auch was einfallen lassen.

Wieso ist dem denn so, also wieso liest loadLevel() auch Spieler ein? Vllt. ist das ja Domänenwissen, welches ich nicht habe.
 
Zuletzt bearbeitet:
Ich verstehe ehrlich gesagt das Ziel überhaupt nicht.
Wozu verschiedene Klassen, wenn es dann nicht objektorientiert ist oder sie in irgendeiner Art und Weise wiederverwendbar wären? Willst du nicht im Prinzip nur ein
#region
#endregion
?
Wenn es auf objektorientiert umgebaut werden soll, sind ein paar Logikblöcke das einzige, was du übernehmen könntest, da ist es bestimmt einfacher ein neues Projekt anzulegen und diese rüberzukopieren anstatt in diesem hier alles hin und her zu schieben.
 
Mehrere Klassen zu verwenden muss nichts mit Wiederverwendbarkeit zu tun haben. Es geht in erster Linie um Responsibilities.
 
Zurück
Oben