From 1d806f85a3eb4680f064c8b59bdb04d543d8fbe6 Mon Sep 17 00:00:00 2001 From: nicho <3013379@stud.hs-mannheim.de> Date: Mon, 6 Jan 2025 21:04:04 +0100 Subject: [PATCH] Initial commit --- .gitignore | 38 +++ .idea/.gitignore | 3 + .idea/vcs.xml | 4 + gamestate.dat | Bin 0 -> 867 bytes pom.xml | 182 +++++++++++++++ src/main/java/GUI/GameUIController.java | 221 ++++++++++++++++++ src/main/java/GUI/HitoriBoardPanel.java | 94 ++++++++ src/main/java/GUI/HitoriControlPanel.java | 67 ++++++ src/main/java/GUI/HitoriDialogManager.java | 116 +++++++++ src/main/java/GUI/HitoriScorePanel.java | 39 ++++ src/main/java/Main/MainMethod.java | 96 ++++++++ src/main/java/domain/GameBase.java | 48 ++++ src/main/java/domain/GameSolver.java | 123 ++++++++++ src/main/java/domain/HitoriBoardLoader.java | 124 ++++++++++ src/main/java/domain/HitoriGameMain.java | 85 +++++++ src/main/java/domain/HitoriGameMoves.java | 98 ++++++++ src/main/java/domain/HitoriGameScores.java | 82 +++++++ src/main/java/domain/HitoriGameTimer.java | 56 +++++ .../resources/META-INF/Hitori10x10medium.csv | 41 ++++ .../resources/META-INF/Hitori15x15_medium.csv | 83 +++++++ .../resources/META-INF/Hitori4x4_leicht.csv | 11 + .../resources/META-INF/Hitori5x5leicht.csv | 15 ++ .../resources/META-INF/Hitori8x8leicht.csv | 29 +++ .../resources/META-INF/Hitori8x8medium.csv | 30 +++ src/main/resources/META-INF/MANIFEST.MF | 3 + 25 files changed, 1688 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/vcs.xml create mode 100644 gamestate.dat create mode 100644 pom.xml create mode 100644 src/main/java/GUI/GameUIController.java create mode 100644 src/main/java/GUI/HitoriBoardPanel.java create mode 100644 src/main/java/GUI/HitoriControlPanel.java create mode 100644 src/main/java/GUI/HitoriDialogManager.java create mode 100644 src/main/java/GUI/HitoriScorePanel.java create mode 100644 src/main/java/Main/MainMethod.java create mode 100644 src/main/java/domain/GameBase.java create mode 100644 src/main/java/domain/GameSolver.java create mode 100644 src/main/java/domain/HitoriBoardLoader.java create mode 100644 src/main/java/domain/HitoriGameMain.java create mode 100644 src/main/java/domain/HitoriGameMoves.java create mode 100644 src/main/java/domain/HitoriGameScores.java create mode 100644 src/main/java/domain/HitoriGameTimer.java create mode 100644 src/main/resources/META-INF/Hitori10x10medium.csv create mode 100644 src/main/resources/META-INF/Hitori15x15_medium.csv create mode 100644 src/main/resources/META-INF/Hitori4x4_leicht.csv create mode 100644 src/main/resources/META-INF/Hitori5x5leicht.csv create mode 100644 src/main/resources/META-INF/Hitori8x8leicht.csv create mode 100644 src/main/resources/META-INF/Hitori8x8medium.csv create mode 100644 src/main/resources/META-INF/MANIFEST.MF diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..d843f34 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/gamestate.dat b/gamestate.dat new file mode 100644 index 0000000000000000000000000000000000000000..24ef3f574d6b85c39fe8dcce3cd061ca91aca245 GIT binary patch literal 867 zcmbu7PbdUY9LIk%yNg}&C;t-iC%IV{B~fS#qunSJNGv(;! zor{ut}1p zQ1}!T!qZSmEs}MqG0)OegKCqxn6^1GwFZ`V%BO9Eb8^DGs%tv^drn zMtII-oJ#mQ?118D3qAMU7n=w099cEh(cE=)*w&~5$fO{~RXw?|-#OH9w)jvAi+i46 zRp2K0>q)}3=GVuWmi@hv5=FwbM1HAvU2xDed#}JKQ zPW*MP*!f7S|DcUK4%rl@`@1*o2bbh10Ez}dmu*pLr;-!RDR`;%+dJwJk7 + + 4.0.0 + + de.hs_mannheim.informatik.backend + HitoriFinal + 1.0-SNAPSHOT + + + 20 + 20 + UTF-8 + 20.0.2 + 20 + + + + + central + https://repo.maven.apache.org/maven2 + + + + + + org.junit.jupiter + junit-jupiter-api + 5.8.1 + test + + + org.junit.jupiter + junit-jupiter-engine + 5.8.1 + test + + + org.apache.logging.log4j + log4j-core + 2.24.2 + + + + + org.openjfx + javafx-controls + ${javafx.version} + compile + + + org.openjfx + javafx-fxml + ${javafx.version} + compile + + + org.openjfx + javafx-base + ${javafx.version} + compile + + + + + junit + junit + 4.13.2 + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.13.0 + + ${maven.compiler.source} + ${maven.compiler.target} + + + + + org.openjfx + javafx-maven-plugin + 0.0.1 + + Main.MainMethod + + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.6.0 + + false + + + + package + + shade + + + + + Main.MainMethod + + + + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.12 + + + + prepare-agent + + + + report + test + + report + + + + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.26.0 + + false + true + + + + verify + + check + + + + + + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.11.2 + + private + true + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.6.0 + + + + \ No newline at end of file diff --git a/src/main/java/GUI/GameUIController.java b/src/main/java/GUI/GameUIController.java new file mode 100644 index 0000000..c170533 --- /dev/null +++ b/src/main/java/GUI/GameUIController.java @@ -0,0 +1,221 @@ +package GUI; + +import domain.*; +import GUI.HitoriDialogManager; +import javafx.application.Platform; +import java.util.*; + +public class GameUIController { + private final HitoriGameMoves gameMoves; + private final GameSolver gameSolver; + private final domain.HitoriGameTimer gameTimer; + private final HitoriGameScores gameScores; + private final GUI.HitoriDialogManager dialogManager; + private final GUI.HitoriBoardPanel boardPanel; + private final GUI.HitoriControlPanel controlPanel; + private final GUI.HitoriScorePanel scorePanel; + private Timer guiTimer; + private boolean isPaused; + + public GameUIController(int[][] initialBoard, HitoriDialogManager dialogManager) { + this.gameMoves = new HitoriGameMoves(initialBoard); + this.gameSolver = new GameSolver(initialBoard); + this.gameTimer = new domain.HitoriGameTimer(); + this.gameScores = new HitoriGameScores(); + this.dialogManager = dialogManager; + this.isPaused = false; + + this.boardPanel = new GUI.HitoriBoardPanel(gameMoves, gameSolver, this); + this.controlPanel = new GUI.HitoriControlPanel(this); + this.scorePanel = new GUI.HitoriScorePanel(this); + + startTimer(); + loadHighScores(); + } + + public void handleLeftClick(int row, int col) { + gameMoves.markCellAsBlack(row, col); + updateUI(); + + + } + + public void handleRightClick(int row, int col) { + gameMoves.markCellAsWhite(row, col); + updateUI(); + + + } + + public void togglePause() { + isPaused = !isPaused; + if (isPaused) { + gameTimer.pauseTimer(); + controlPanel.setPauseButtonText("Resume"); + boardPanel.setDisable(true); + } else { + gameTimer.startTimer(); + controlPanel.setPauseButtonText("Pause"); + boardPanel.setDisable(false); + } + updateTimerLabel(); + } + + public void resetGame() { + gameMoves.reset(); + gameTimer.resetTimer(); + updateUI(); + } + + public void undo() { + if (gameMoves.undo()) { + updateUI(); + } + } + + public void redo() { + if (gameMoves.redo()) { + updateUI(); + } + } + + public void newGame() { + stopTimer(); + // Notify main application to show board selection + } + + public void checkSolution() { + if (gameSolver.isSolved()) { + handleWin(); + } else { + dialogManager.showAlert("Not Solved", "The current solution is not correct. Keep trying!"); + } + } + + public void showErrors() { + List errors = gameSolver.findIncorrectBlackMarks(); + if (errors.isEmpty()) { + dialogManager.showAlert("No Errors", "No rule violations found in current black markings."); + return; + } + + boardPanel.showErrors(); + StringBuilder message = new StringBuilder("Found " + errors.size() + " error(s):\n"); + for (int[] pos : errors) { + message.append(String.format("Row %d, Column %d\n", pos[0] + 1, pos[1] + 1)); + } + dialogManager.showAlert("Errors Found", message.toString()); + } + + public void saveGame() { + // Implementation for saving game state + dialogManager.showAlert("Game Saved", "Your game has been saved successfully."); + } + + public void deleteHighScores() { + if (dialogManager.confirmDeleteHighScores()) { + gameScores.deleteHighScores(); + updateHighScoreDisplay(); + dialogManager.showAlert("High Scores Deleted", "All high scores have been deleted successfully."); + } + } + + private void handleWin() { + stopTimer(); + Optional playerName = dialogManager.askForPlayerName(); + if (playerName.isPresent() && !playerName.get().trim().isEmpty()) { + gameScores.addHighScore(playerName.get(), gameTimer.getElapsedTimeInSeconds(), gameMoves.getMistakeCount()); + updateHighScoreDisplay(); + dialogManager.showAlert("Congratulations!", String.format( + "You've solved the puzzle!\nTime: %ds\nMistakes: %d", + gameTimer.getElapsedTimeInSeconds(), + gameMoves.getMistakeCount() + )); + + isPaused = false; + boardPanel.setDisable(false); + controlPanel.setPauseButtonText("Pause"); + + if (dialogManager.confirmNewGame()) { + newGame(); + } + } + } + + private void startTimer() { + if (guiTimer != null) { + guiTimer.cancel(); + } + gameTimer.startTimer(); + guiTimer = new Timer(true); + guiTimer.scheduleAtFixedRate(new TimerTask() { + @Override + public void run() { + Platform.runLater(() -> { + if (!isPaused) { + updateTimerLabel(); + } + }); + } + }, 0, 1000); + } + + private void stopTimer() { + if (guiTimer != null) { + guiTimer.cancel(); + guiTimer = null; + } + gameTimer.stopTimer(); + } + + private void updateUI() { + boardPanel.updateBoard(); + updateMistakeLabel(); + } + + private void updateTimerLabel() { + controlPanel.updateTimerLabel(gameTimer.getElapsedTimeInSeconds()); + } + + private void updateMistakeLabel() { + controlPanel.updateMistakeLabel(gameMoves.getMistakeCount()); + } + + private void updateHighScoreDisplay() { + scorePanel.updateHighScores(String.join("\n", gameScores.getHighScoresWithAverage())); + } + + private void loadHighScores() { + gameScores.loadHighScoresFromFile(); + updateHighScoreDisplay(); + } + + public Set convertErrorsToSet(List errors) { + Set errorPositions = new HashSet<>(); + for (int[] pos : errors) { + errorPositions.add(pos[0] + "," + pos[1]); + } + return errorPositions; + } + + public boolean isPaused() { + return isPaused; + } + + public void cleanup() { + stopTimer(); + saveGame(); + } + + public HitoriBoardPanel getBoardPanel() { + return boardPanel; + } + + public HitoriControlPanel getControlPanel() { + return controlPanel; + } + + public HitoriScorePanel getScorePanel() { + return scorePanel; + } +} diff --git a/src/main/java/GUI/HitoriBoardPanel.java b/src/main/java/GUI/HitoriBoardPanel.java new file mode 100644 index 0000000..490b093 --- /dev/null +++ b/src/main/java/GUI/HitoriBoardPanel.java @@ -0,0 +1,94 @@ +package GUI; + +import domain.GameSolver; +import domain.HitoriGameMoves; +import javafx.geometry.Insets; +import javafx.scene.control.Button; +import javafx.scene.input.MouseButton; +import javafx.scene.layout.GridPane; +import java.util.Set; + +public class HitoriBoardPanel extends GridPane { + private final HitoriGameMoves gameMoves; + private final GameSolver gameSolver; + private final GameUIController controller; + + public HitoriBoardPanel(HitoriGameMoves gameMoves, GameSolver gameSolver, GameUIController controller) { + this.gameMoves = gameMoves; + this.gameSolver = gameSolver; + this.controller = controller; + + setHgap(5); + setVgap(5); + setPadding(new Insets(10)); + + updateBoard(); + } + + public void updateBoard() { + getChildren().clear(); + int[][] boardState = gameMoves.getBoard(); + boolean[][] blackCells = gameMoves.getBlackCells(); + boolean[][] whiteCells = gameMoves.getWhiteCells(); + + for (int row = 0; row < boardState.length; row++) { + for (int col = 0; col < boardState[row].length; col++) { + Button cellButton = createCellButton(row, col, boardState[row][col], + blackCells[row][col], whiteCells[row][col]); + add(cellButton, col, row); + } + } + } + + public void showErrors() { + var errors = gameSolver.findIncorrectBlackMarks(); + Set errorPositions = controller.convertErrorsToSet(errors); + + getChildren().clear(); + int[][] boardState = gameMoves.getBoard(); + boolean[][] blackCells = gameMoves.getBlackCells(); + boolean[][] whiteCells = gameMoves.getWhiteCells(); + + for (int row = 0; row < boardState.length; row++) { + for (int col = 0; col < boardState[row].length; col++) { + Button cellButton = createCellButton(row, col, boardState[row][col], + blackCells[row][col], whiteCells[row][col]); + + if (errorPositions.contains(row + "," + col) && blackCells[row][col]) { + cellButton.setStyle("-fx-background-color: red; -fx-text-fill: white;"); + } + + add(cellButton, col, row); + } + } + } + + private Button createCellButton(int row, int col, int value, boolean isBlack, boolean isWhite) { + Button cellButton = new Button(String.valueOf(value)); + cellButton.setPrefSize(50, 50); + styleCellButton(cellButton, isBlack, isWhite); + + cellButton.setOnMouseClicked(event -> { + if (!controller.isPaused()) { + if (event.getButton() == MouseButton.PRIMARY) { + controller.handleLeftClick(row, col); + } else if (event.getButton() == MouseButton.SECONDARY) { + controller.handleRightClick(row, col); + } + } + }); + + return cellButton; + } + + private void styleCellButton(Button button, boolean isBlack, boolean isWhite) { + if (isBlack) { + button.setStyle("-fx-background-color: black; -fx-text-fill: white;"); + } else if (isWhite) { + button.setStyle("-fx-background-color: white; -fx-text-fill: black; -fx-border-color: gray;"); + } else { + button.setStyle("-fx-background-color: lightgray; -fx-text-fill: black;"); + } + } +} + diff --git a/src/main/java/GUI/HitoriControlPanel.java b/src/main/java/GUI/HitoriControlPanel.java new file mode 100644 index 0000000..58c7aca --- /dev/null +++ b/src/main/java/GUI/HitoriControlPanel.java @@ -0,0 +1,67 @@ +package GUI; + + +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; + +public class HitoriControlPanel extends VBox { + private final GameUIController controller; + private final Label timerLabel; + private final Label mistakeLabel; + private final Button pauseButton; + + public HitoriControlPanel(GameUIController controller) { + this.controller = controller; + + // Timer box setup + HBox timerBox = new HBox(10); + timerLabel = new Label("Time: 0s"); + mistakeLabel = new Label("Mistakes: 0"); + pauseButton = new Button("Pause"); + timerBox.getChildren().addAll(timerLabel, mistakeLabel, pauseButton); + timerBox.setAlignment(Pos.CENTER); + + // Button box setup + HBox buttonBox = new HBox(10); + Button resetButton = new Button("Reset"); + Button undoButton = new Button("Undo"); + Button redoButton = new Button("Redo"); + Button checkButton = new Button("Check Solution"); + Button newGameButton = new Button("New Game"); + Button showErrorsButton = new Button("Show Errors"); + + buttonBox.getChildren().addAll(resetButton, undoButton, redoButton, + checkButton, newGameButton, showErrorsButton); + buttonBox.setAlignment(Pos.CENTER); + + // Event handlers + pauseButton.setOnAction(e -> controller.togglePause()); + resetButton.setOnAction(e -> controller.resetGame()); + undoButton.setOnAction(e -> controller.undo()); + redoButton.setOnAction(e -> controller.redo()); + newGameButton.setOnAction(e -> controller.newGame()); + checkButton.setOnAction(e -> controller.checkSolution()); + showErrorsButton.setOnAction(e -> controller.showErrors()); + + // Layout setup + getChildren().addAll(timerBox, buttonBox); + setPadding(new Insets(10)); + setSpacing(10); + } + + public void updateTimerLabel(long seconds) { + timerLabel.setText("Time: " + seconds + "s"); + } + + public void updateMistakeLabel(int mistakes) { + mistakeLabel.setText("Mistakes: " + mistakes); + } + + public void setPauseButtonText(String text) { + pauseButton.setText(text); + } +} \ No newline at end of file diff --git a/src/main/java/GUI/HitoriDialogManager.java b/src/main/java/GUI/HitoriDialogManager.java new file mode 100644 index 0000000..f8d1197 --- /dev/null +++ b/src/main/java/GUI/HitoriDialogManager.java @@ -0,0 +1,116 @@ +package GUI; + +import domain.HitoriBoardLoader; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.layout.VBox; +import javafx.stage.Modality; +import javafx.stage.Window; +import java.util.Optional; + + +public class HitoriDialogManager { + private final Window owner; + + public HitoriDialogManager(Window owner) { + this.owner = owner; + } + + public Optional showBoardSelectionDialog(HitoriBoardLoader boardLoader) { + Dialog dialog = new Dialog<>(); + dialog.setTitle("Select Hitori Board"); + dialog.setHeaderText("Choose a board to play:"); + + if (owner != null && owner.getScene() != null) { + dialog.initOwner(owner); + dialog.initModality(Modality.APPLICATION_MODAL); + } + + ButtonType selectButtonType = new ButtonType("Play", ButtonBar.ButtonData.OK_DONE); + ButtonType randomButtonType = new ButtonType("Random", ButtonBar.ButtonData.OTHER); + dialog.getDialogPane().getButtonTypes().addAll(selectButtonType, randomButtonType, ButtonType.CANCEL); + + ChoiceBox boardChoice = new ChoiceBox<>(); + boardChoice.getItems().addAll(boardLoader.getAvailableBoardNames()); + if (!boardChoice.getItems().isEmpty()) { + boardChoice.setValue(boardChoice.getItems().get(0)); + } + + VBox content = new VBox(10); + content.getChildren().add(boardChoice); + dialog.getDialogPane().setContent(content); + + dialog.setResultConverter(dialogButton -> { + if (dialogButton == selectButtonType) { + return boardChoice.getValue(); + } else if (dialogButton == randomButtonType) { + int random = (int) (Math.random() * boardChoice.getItems().size()); + return boardChoice.getItems().get(random); + } + return null; + }); + + return dialog.showAndWait(); + } + + public void showAlert(String title, String message) { + Alert alert = new Alert(Alert.AlertType.INFORMATION); + alert.setTitle(title); + alert.setHeaderText(null); + alert.setContentText(message); + alert.initOwner(owner); + alert.showAndWait(); + } + + public Optional askForPlayerName() { + TextInputDialog dialog = new TextInputDialog(); + dialog.setTitle("High Score"); + dialog.setHeaderText("Congratulations! Enter your name:"); + dialog.setContentText("Name:"); + dialog.initOwner(owner); + return dialog.showAndWait(); + } + + public boolean confirmDeleteHighScores() { + Alert alert = new Alert(Alert.AlertType.CONFIRMATION); + alert.setTitle("Delete High Scores"); + alert.setHeaderText("Are you sure?"); + alert.setContentText("This will permanently delete all high scores."); + alert.initOwner(owner); + + Optional result = alert.showAndWait(); + return result.isPresent() && result.get() == ButtonType.OK; + } + + public boolean confirmNewGame() { + Alert alert = new Alert(Alert.AlertType.CONFIRMATION); + alert.setTitle("Game Complete"); + alert.setHeaderText("Would you like to start a new game?"); + alert.setContentText("Choose whether to start a new game or continue viewing this one."); + + ButtonType buttonTypeNew = new ButtonType("New Game"); + ButtonType buttonTypeStay = new ButtonType("Stay Here", ButtonBar.ButtonData.CANCEL_CLOSE); + + alert.getButtonTypes().setAll(buttonTypeNew, buttonTypeStay); + alert.initOwner(owner); + + Optional result = alert.showAndWait(); + return result.isPresent() && result.get() == buttonTypeNew; + } + + public boolean confirmLoadSavedGame() { + Alert alert = new Alert(Alert.AlertType.CONFIRMATION); + alert.setTitle("Saved Game Found"); + alert.setHeaderText("Would you like to continue your saved game?"); + alert.setContentText("Choose whether to load the saved game or start a new one."); + + ButtonType buttonTypeYes = new ButtonType("Load Saved Game"); + ButtonType buttonTypeNo = new ButtonType("Start New Game"); + + alert.getButtonTypes().setAll(buttonTypeYes, buttonTypeNo); + alert.initOwner(owner); + + Optional result = alert.showAndWait(); + return result.isPresent() && result.get() == buttonTypeYes; + } +} \ No newline at end of file diff --git a/src/main/java/GUI/HitoriScorePanel.java b/src/main/java/GUI/HitoriScorePanel.java new file mode 100644 index 0000000..204091b --- /dev/null +++ b/src/main/java/GUI/HitoriScorePanel.java @@ -0,0 +1,39 @@ +package GUI; + +import GUI.GameUIController; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.TextArea; +import javafx.scene.layout.VBox; + +public class HitoriScorePanel extends VBox { + private final GameUIController controller; + private final TextArea highScoreArea; + + public HitoriScorePanel(GameUIController controller) { + this.controller = controller; + + setPadding(new Insets(10)); + setAlignment(Pos.TOP_CENTER); + + Label highScoreLabel = new Label("High Scores"); + highScoreArea = new TextArea(); + highScoreArea.setEditable(false); + highScoreArea.setPrefRowCount(10); + highScoreArea.setPrefColumnCount(30); + + Button saveButton = new Button("Save Game"); + Button deleteHighScoresButton = new Button("Delete High Scores"); + + saveButton.setOnAction(e -> controller.saveGame()); + deleteHighScoresButton.setOnAction(e -> controller.deleteHighScores()); + + getChildren().addAll(highScoreLabel, highScoreArea, saveButton, deleteHighScoresButton); + } + + public void updateHighScores(String highScores) { + highScoreArea.setText(highScores); + } +} \ No newline at end of file diff --git a/src/main/java/Main/MainMethod.java b/src/main/java/Main/MainMethod.java new file mode 100644 index 0000000..6d6cb9e --- /dev/null +++ b/src/main/java/Main/MainMethod.java @@ -0,0 +1,96 @@ +package Main; + + +import domain.HitoriGameMain; +import domain.HitoriBoardLoader; +import GUI.GameUIController; +import GUI.HitoriDialogManager; +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.layout.BorderPane; +import javafx.stage.Stage; + +public class MainMethod extends Application { + private HitoriBoardLoader boardLoader; + private GameUIController controller; + private HitoriDialogManager dialogManager; + private String currentBoardName; + + public static void main(String[] args) { + launch(args); + } + + @Override + public void start(Stage primaryStage) { + boardLoader = new HitoriBoardLoader(); + dialogManager = new HitoriDialogManager(primaryStage); + + checkSavedGame(primaryStage); + } + + private void checkSavedGame(Stage primaryStage) { + HitoriGameMain savedGame = HitoriGameMain.loadGameState(); + if (savedGame != null) { + if (dialogManager.confirmLoadSavedGame()) { + initializeGame(savedGame.getBoard(), primaryStage); + } else { + showBoardSelectionDialog(primaryStage); + } + } else { + showBoardSelectionDialog(primaryStage); + } + } + + private void showBoardSelectionDialog(Stage primaryStage) { + dialogManager.showBoardSelectionDialog(boardLoader).ifPresentOrElse( + boardName -> { + currentBoardName = boardName; + int[][] selectedBoard = boardLoader.getBoard(boardName); + if (selectedBoard != null) { + initializeGame(selectedBoard, primaryStage); + } + }, + () -> System.exit(0) + ); + } + + private void initializeGame(int[][] board, Stage primaryStage) { + controller = new GameUIController(board, dialogManager); + createGameUI(primaryStage); + } + + private void createGameUI(Stage primaryStage) { + primaryStage.setTitle("Hitori Game - " + currentBoardName); + + BorderPane mainLayout = new BorderPane(); + mainLayout.setCenter(controller.getBoardPanel()); + mainLayout.setTop(controller.getControlPanel()); + mainLayout.setRight(controller.getScorePanel()); + + Scene scene = new Scene(mainLayout, 800, 600); + primaryStage.setScene(scene); + + // Window state listeners + primaryStage.iconifiedProperty().addListener((observable, oldValue, newValue) -> { + if (newValue && !controller.isPaused()) { + controller.togglePause(); + } + }); + + primaryStage.focusedProperty().addListener((observable, oldValue, newValue) -> { + if (!newValue && !controller.isPaused()) { + controller.togglePause(); + } + }); + + primaryStage.setOnCloseRequest(event -> controller.cleanup()); + primaryStage.show(); + } + + @Override + public void stop() { + if (controller != null) { + controller.cleanup(); + } + } +} diff --git a/src/main/java/domain/GameBase.java b/src/main/java/domain/GameBase.java new file mode 100644 index 0000000..02ed53e --- /dev/null +++ b/src/main/java/domain/GameBase.java @@ -0,0 +1,48 @@ +package domain; + +import java.io.Serializable; +import java.util.Arrays; + +public class GameBase implements Serializable { + + protected int[][] board; + public boolean[][] blackCells; + public boolean[][] whiteCells; + protected int mistakeCount; + + public GameBase(int[][] initialBoard) { + this.board = new int[initialBoard.length][initialBoard[0].length]; + for (int i = 0; i < initialBoard.length; i++) { + this.board[i] = Arrays.copyOf(initialBoard[i], initialBoard[i].length); + } + this.blackCells = new boolean[board.length][board[0].length]; + this.whiteCells = new boolean[board.length][board[0].length]; + this.mistakeCount = 0; + } + + public void reset() { + blackCells = new boolean[board.length][board[0].length]; + whiteCells = new boolean[board.length][board[0].length]; + mistakeCount = 0; + } + + public int[][] getBoard() { + return board; + } + + public int[][] getCurrentState() { + return board; + } + + public boolean[][] getBlackCells() { + return blackCells; + } + + public boolean[][] getWhiteCells() { + return whiteCells; + } + + public int getMistakeCount() { + return mistakeCount; + } +} diff --git a/src/main/java/domain/GameSolver.java b/src/main/java/domain/GameSolver.java new file mode 100644 index 0000000..aa491d9 --- /dev/null +++ b/src/main/java/domain/GameSolver.java @@ -0,0 +1,123 @@ +package domain; + +import java.util.*; + +public class GameSolver extends domain.HitoriGameMoves { + + public GameSolver(int[][] initialBoard) { + super(initialBoard); + } + + public boolean isSolved() { + + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (!blackCells[i][j] && !whiteCells[i][j]) { + return false; // Found an unmarked cell + } + } + } + + + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (blackCells[i][j] && !isValidBlackMark(i, j)) { + return false; + } + } + } + + + for (int i = 0; i < board.length; i++) { + Set seenInRow = new HashSet<>(); + Set seenInCol = new HashSet<>(); + + for (int j = 0; j < board[0].length; j++) { + + if (!blackCells[i][j]) { + if (seenInRow.contains(board[i][j])) { + return false; + } + seenInRow.add(board[i][j]); + } + + + if (!blackCells[j][i]) { + if (seenInCol.contains(board[j][i])) { + return false; + } + seenInCol.add(board[j][i]); + } + } + } + + // Check connectivity of white cells + return isOrthogonallyConnected(); + } + + public boolean isValidBlackMark(int row, int col) { + if (row > 0 && blackCells[row-1][col] || + row < board.length-1 && blackCells[row+1][col] || + col > 0 && blackCells[row][col-1] || + col < board[0].length-1 && blackCells[row][col+1]) { + return false; + } + return true; + } + + private boolean isOrthogonallyConnected() { + if (board.length == 0) return true; + + boolean[][] visited = new boolean[board.length][board[0].length]; + int[] start = findFirstWhiteCell(); + if (start == null) return true; + + dfs(start[0], start[1], visited); + + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (!blackCells[i][j] && !visited[i][j]) { + return false; + } + } + } + return true; + } + + private int[] findFirstWhiteCell() { + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (!blackCells[i][j]) { + return new int[]{i, j}; + } + } + } + return null; + } + + private void dfs(int row, int col, boolean[][] visited) { + if (row < 0 || row >= board.length || col < 0 || col >= board[0].length || + visited[row][col] || blackCells[row][col]) { + return; + } + + visited[row][col] = true; + + dfs(row - 1, col, visited); + dfs(row + 1, col, visited); + dfs(row, col - 1, visited); + dfs(row, col + 1, visited); + } + + public List findIncorrectBlackMarks() { + List incorrectMarks = new ArrayList<>(); + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (blackCells[i][j] && !isValidBlackMark(i, j)) { + incorrectMarks.add(new int[]{i, j}); + } + } + } + return incorrectMarks; + } +} diff --git a/src/main/java/domain/HitoriBoardLoader.java b/src/main/java/domain/HitoriBoardLoader.java new file mode 100644 index 0000000..a65747b --- /dev/null +++ b/src/main/java/domain/HitoriBoardLoader.java @@ -0,0 +1,124 @@ +package domain; + +import java.io.*; +import java.net.URL; +import java.util.*; + +public class HitoriBoardLoader { + private Map availableBoards; + private Map> solutions; + + public HitoriBoardLoader() { + availableBoards = new HashMap<>(); + solutions = new HashMap<>(); + loadAllBoards(); + } + + private void loadAllBoards() { + try { + // Define all board file names + String[] boardFiles = { + "Hitori4x4_leicht.csv", + "Hitori5x5leicht.csv", + "Hitori8x8leicht.csv", + "Hitori8x8medium.csv", + "Hitori10x10medium.csv", + "Hitori15x15_medium.csv" + }; + + // Try to load each board + for (String fileName : boardFiles) { + try { + InputStream is = getClass().getResourceAsStream("/META-INF/" + fileName); + if (is == null) { + is = getClass().getResourceAsStream("/" + fileName); + } + + if (is != null) { + loadBoard(fileName, is); + } + } catch (Exception e) { + System.out.println("Failed to load board: " + fileName); + e.printStackTrace(); + } + } + + if (availableBoards.isEmpty()) { + System.out.println("No board files found. Please ensure .csv files are in the resources folder."); + } else { + System.out.println("Successfully loaded " + availableBoards.size() + " boards."); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void loadBoard(String fileName, InputStream inputStream) throws IOException { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { + List lines = new ArrayList<>(); + String line; + while ((line = reader.readLine()) != null) { + lines.add(line); + } + + // Find where the solution starts + int solutionIndex = -1; + for (int i = 0; i < lines.size(); i++) { + if (lines.get(i).contains("//")) { + solutionIndex = i; + break; + } + } + + // Parse the board + List boardRows = new ArrayList<>(); + for (int i = 0; i < (solutionIndex == -1 ? lines.size() : solutionIndex); i++) { + line = lines.get(i).trim(); + if (!line.isEmpty()) { + boardRows.add(line.split(",")); + } + } + + if (!boardRows.isEmpty()) { + int rows = boardRows.size(); + int cols = boardRows.get(0).length; + int[][] board = new int[rows][cols]; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + board[i][j] = Integer.parseInt(boardRows.get(i)[j].trim()); + } + } + availableBoards.put(fileName, board); + + // Parse the solution if available + List solutionCoordinates = new ArrayList<>(); + if (solutionIndex != -1) { + for (int i = solutionIndex + 1; i < lines.size(); i++) { + line = lines.get(i).trim(); + if (!line.isEmpty() && !line.startsWith("//")) { + String[] coords = line.split(","); + solutionCoordinates.add(new int[]{ + Integer.parseInt(coords[0].trim()) - 1, + Integer.parseInt(coords[1].trim()) - 1 + }); + } + } + solutions.put(fileName, solutionCoordinates); + } + } + } + } + + public Set getAvailableBoardNames() { + return availableBoards.keySet(); + } + + public int[][] getBoard(String boardName) { + return availableBoards.get(boardName); + } + + public List getSolution(String boardName) { + return solutions.get(boardName); + } +} \ No newline at end of file diff --git a/src/main/java/domain/HitoriGameMain.java b/src/main/java/domain/HitoriGameMain.java new file mode 100644 index 0000000..9d22834 --- /dev/null +++ b/src/main/java/domain/HitoriGameMain.java @@ -0,0 +1,85 @@ +package domain; + +import domain.GameSolver; + +import java.io.*; + +public class HitoriGameMain extends GameSolver { + private static final long serialVersionUID = 1L; + + private final domain.HitoriGameTimer timer; + private final domain.HitoriGameScores scores; + + public HitoriGameMain(int[][] initialBoard) { + super(initialBoard); + this.timer = new domain.HitoriGameTimer(); + this.scores = new HitoriGameScores(); + } + + // Timer delegation methods + public void startTimer() { + timer.startTimer(); + } + + public void pauseTimer() { + timer.pauseTimer(); + } + + public void stopTimer() { + timer.stopTimer(); + } + + public void resetTimer() { + timer.resetTimer(); + } + + public long getElapsedTimeInSeconds() { + return timer.getElapsedTimeInSeconds(); + } + + // High score delegation methods + public void addHighScore(String playerName, long timeInSeconds) { + scores.addHighScore(playerName, timeInSeconds, getMistakeCount()); + } + + public void deleteHighScores() { + scores.deleteHighScores(); + } + + public java.util.List getHighScoresWithAverage() { + return scores.getHighScoresWithAverage(); + } + + public void loadHighScoresFromFile() { + scores.loadHighScoresFromFile(); + } + + public void saveHighScoresToFile() { + scores.saveHighScoresToFile(); + } + + // Game state persistence + public void saveGameState() { + try (ObjectOutputStream oos = new ObjectOutputStream( + new FileOutputStream("gamestate.dat"))) { + oos.writeObject(this); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static HitoriGameMain loadGameState() { + try (ObjectInputStream ois = new ObjectInputStream( + new FileInputStream("gamestate.dat"))) { + return (HitoriGameMain) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + return null; + } + } + + @Override + public void reset() { + super.reset(); + resetTimer(); + } +} \ No newline at end of file diff --git a/src/main/java/domain/HitoriGameMoves.java b/src/main/java/domain/HitoriGameMoves.java new file mode 100644 index 0000000..1d341f5 --- /dev/null +++ b/src/main/java/domain/HitoriGameMoves.java @@ -0,0 +1,98 @@ +package domain; + +import java.io.Serializable; +import java.util.*; + +public class HitoriGameMoves extends GameBase { + private List history; + private int historyPointer; + + private static class GameState implements Serializable { + boolean[][] blackCells; + boolean[][] whiteCells; + + GameState(boolean[][] blackCells, boolean[][] whiteCells) { + this.blackCells = new boolean[blackCells.length][blackCells[0].length]; + this.whiteCells = new boolean[whiteCells.length][whiteCells[0].length]; + for (int i = 0; i < blackCells.length; i++) { + this.blackCells[i] = Arrays.copyOf(blackCells[i], blackCells[i].length); + this.whiteCells[i] = Arrays.copyOf(whiteCells[i], whiteCells[i].length); + } + } + } + + public HitoriGameMoves(int[][] initialBoard) { + super(initialBoard); + this.history = new ArrayList<>(); + this.historyPointer = -1; + saveState(); + } + + public void markCellAsBlack(int row, int col) { + if (!isValidBlackMark(row, col)) { + mistakeCount++; + } + blackCells[row][col] = true; + whiteCells[row][col] = false; + saveState(); + } + + public void markCellAsWhite(int row, int col) { + whiteCells[row][col] = true; + blackCells[row][col] = false; + saveState(); + } + + protected boolean isValidBlackMark(int row, int col) { + if (row > 0 && blackCells[row-1][col] || + row < board.length-1 && blackCells[row+1][col] || + col > 0 && blackCells[row][col-1] || + col < board[0].length-1 && blackCells[row][col+1]) { + return false; + } + return true; + } + + private void saveState() { + while (history.size() > historyPointer + 1) { + history.remove(history.size() - 1); + } + history.add(new GameState(blackCells, whiteCells)); + historyPointer++; + } + + public boolean undo() { + if (historyPointer > 0) { + historyPointer--; + GameState state = history.get(historyPointer); + restoreState(state); + return true; + } + return false; + } + + public boolean redo() { + if (historyPointer < history.size() - 1) { + historyPointer++; + GameState state = history.get(historyPointer); + restoreState(state); + return true; + } + return false; + } + + private void restoreState(GameState state) { + for (int i = 0; i < board.length; i++) { + blackCells[i] = Arrays.copyOf(state.blackCells[i], state.blackCells[i].length); + whiteCells[i] = Arrays.copyOf(state.whiteCells[i], state.whiteCells[i].length); + } + } + + @Override + public void reset() { + super.reset(); + history.clear(); + historyPointer = -1; + saveState(); + } +} \ No newline at end of file diff --git a/src/main/java/domain/HitoriGameScores.java b/src/main/java/domain/HitoriGameScores.java new file mode 100644 index 0000000..482373a --- /dev/null +++ b/src/main/java/domain/HitoriGameScores.java @@ -0,0 +1,82 @@ +package domain; + +import java.io.*; +import java.util.*; + +public class HitoriGameScores implements Serializable { + private static final long serialVersionUID = 1L; + + private Map> highScores; + + private static class HighScoreEntry implements Serializable { + String playerName; + long time; + int mistakes; + + HighScoreEntry(String playerName, long time, int mistakes) { + this.playerName = playerName; + this.time = time; + this.mistakes = mistakes; + } + } + + public HitoriGameScores() { + this.highScores = new HashMap<>(); + } + + public void addHighScore(String playerName, long timeInSeconds, int mistakes) { + highScores.putIfAbsent(playerName, new ArrayList<>()); + highScores.get(playerName).add(new HighScoreEntry(playerName, timeInSeconds, mistakes)); + saveHighScoresToFile(); + } + + public void deleteHighScores() { + highScores.clear(); + saveHighScoresToFile(); + } + + public List getHighScoresWithAverage() { + List result = new ArrayList<>(); + List allEntries = new ArrayList<>(); + + for (List entries : highScores.values()) { + allEntries.addAll(entries); + } + + allEntries.sort((a, b) -> Long.compare(a.time, b.time)); + + double avgTime = allEntries.stream() + .mapToLong(e -> e.time) + .average() + .orElse(0); + + for (int i = 0; i < Math.min(10, allEntries.size()); i++) { + HighScoreEntry entry = allEntries.get(i); + result.add(String.format("%s - Time: %ds - Mistakes: %d", + entry.playerName, entry.time, entry.mistakes)); + } + + result.add(String.format("Average Time: %.1fs", avgTime)); + + return result; + } + + public void saveHighScoresToFile() { + try (ObjectOutputStream oos = new ObjectOutputStream( + new FileOutputStream("highscores.dat"))) { + oos.writeObject(highScores); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @SuppressWarnings("unchecked") + public void loadHighScoresFromFile() { + try (ObjectInputStream ois = new ObjectInputStream( + new FileInputStream("highscores.dat"))) { + highScores = (Map>) ois.readObject(); + } catch (IOException | ClassNotFoundException e) { + highScores = new HashMap<>(); + } + } +} \ No newline at end of file diff --git a/src/main/java/domain/HitoriGameTimer.java b/src/main/java/domain/HitoriGameTimer.java new file mode 100644 index 0000000..44655d9 --- /dev/null +++ b/src/main/java/domain/HitoriGameTimer.java @@ -0,0 +1,56 @@ +package domain; + +import java.io.Serializable; + +public class HitoriGameTimer implements Serializable { + private static final long serialVersionUID = 1L; + + private transient long startTime; + private long elapsedTime; + private boolean isPaused; + + public HitoriGameTimer() { + this.startTime = 0; + this.elapsedTime = 0; + this.isPaused = false; + } + + public void startTimer() { + if (isPaused || startTime == 0) { + startTime = System.currentTimeMillis(); + isPaused = false; + } + } + + public void pauseTimer() { + if (!isPaused && startTime > 0) { + elapsedTime += System.currentTimeMillis() - startTime; + startTime = 0; + isPaused = true; + } + } + + public void stopTimer() { + if (!isPaused && startTime > 0) { + elapsedTime += System.currentTimeMillis() - startTime; + startTime = 0; + } + } + + public void resetTimer() { + startTime = 0; + elapsedTime = 0; + isPaused = false; + } + + public long getElapsedTimeInSeconds() { + if (isPaused || startTime == 0) { + return elapsedTime / 1000; + } + return (elapsedTime + System.currentTimeMillis() - startTime) / 1000; + } + + public boolean isPaused() { + return isPaused; + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/Hitori10x10medium.csv b/src/main/resources/META-INF/Hitori10x10medium.csv new file mode 100644 index 0000000..b65f935 --- /dev/null +++ b/src/main/resources/META-INF/Hitori10x10medium.csv @@ -0,0 +1,41 @@ +5,8,2,6,3,7,7,5,1,8 +8,4,2,2,1,9,9,3,7,5 +3,2,7,8,2,5,5,4,3,10 +10,2,3,1,7,8,6,9,9,9 +3,3,3,7,6,2,4,10,4,1 +4,5,9,10,6,10,1,7,8,8 +2,7,5,9,1,10,3,1,3,6 +9,5,4,3,8,10,2,9,10,4 +9,6,10,8,5,3,10,2,5,8 +9,10,2,2,4,7,9,8,5,7 + +// Lösung (schwarze Felder) +1,1 +1,3 +1,6 +1,10 +2,4 +2,7 +3,2 +3,6 +3,9 +4,8 +4,10 +5,1 +5,3 +5,5 +5,7 +6,2 +6,6 +6,10 +7,5 +7,9 +8,1 +8,3 +8,6 +9,4 +9,7 +9,9 +10,1 +10,3 +10,10 \ No newline at end of file diff --git a/src/main/resources/META-INF/Hitori15x15_medium.csv b/src/main/resources/META-INF/Hitori15x15_medium.csv new file mode 100644 index 0000000..6db903b --- /dev/null +++ b/src/main/resources/META-INF/Hitori15x15_medium.csv @@ -0,0 +1,83 @@ +7,1,2,9,12,15,8,11,11,9,11,14,13,6,3 +2,3,8,1,2,11,10,9,5,8,14,3,12,13,15 +4,14,13,9,4,15,9,10,12,6,5,3,11,5,12 +15,9,5,6,10,15,1,15,8,3,5,4,6,2,8 +5,11,7,9,15,1,4,3,8,1,9,2,10,13,2 +15,15,10,3,1,14,8,12,11,1,9,8,2,7,2 +10,7,7,12,9,3,15,2,5,2,10,5,1,7,4 +3,8,9,14,1,6,12,4,15,2,13,11,5,10,11 +8,6,7,15,11,4,5,11,2,10,3,13,8,12,9 +2,2,3,3,4,13,5,6,5,11,5,15,8,9,12 +2,15,15,11,13,7,6,5,3,13,8,10,5,1,11 +12,5,11,13,13,2,2,8,8,4,10,9,3,2,5 +1,13,8,2,1,7,11,4,9,15,4,12,9,3,10 +13,10,12,5,15,3,2,7,13,14,12,12,9,11,6 +7,12,4,8,14,10,13,13,7,4,2,6,15,15,11 + +//Lösung +1,4 +1,6 +1,8 +1,11 +2,1 +2,3 +2,9 +2,12 +2,14 +3,5 +3,7 +3,11 +3,15 +4,1 +4,3 +4,6 +4,9 +4,13 +5,4 +5,10 +5,15 +6,2 +6,5 +6,7 +6,9 +6,11 +6,13 +7,1 +7,3 +7,6 +7,8 +7,12 +7,14 +8,5 +8,9 +8,15 +9,3 +9,8 +9,13 +10,1 +10,4 +10,7 +10,9 +10,11 +11,3 +11,10 +11,13 +11,15 +12,2 +12,5 +12,7 +12,9 +12,11 +12,14 +13,1 +13,6 +13,8 +13,13 +14,3 +14,5 +14,9 +14,12 +15,1 +15,7 +15,10 +15,14 \ No newline at end of file diff --git a/src/main/resources/META-INF/Hitori4x4_leicht.csv b/src/main/resources/META-INF/Hitori4x4_leicht.csv new file mode 100644 index 0000000..d4126ed --- /dev/null +++ b/src/main/resources/META-INF/Hitori4x4_leicht.csv @@ -0,0 +1,11 @@ +3,3,1,4 +4,3,2,2 +1,3,4,2 +3,4,3,2 + +//Lösung (schwarze Felder) +1,2 +2,4 +3,2 +4,1 +4,4 \ No newline at end of file diff --git a/src/main/resources/META-INF/Hitori5x5leicht.csv b/src/main/resources/META-INF/Hitori5x5leicht.csv new file mode 100644 index 0000000..4275794 --- /dev/null +++ b/src/main/resources/META-INF/Hitori5x5leicht.csv @@ -0,0 +1,15 @@ +3,4,5,5,2 +3,2,3,5,4 +2,1,4,5,5 +2,3,1,4,1 +2,5,2,3,2 + +//Lösung (schwarze Felder) +1,1 +1,4 +2,3 +3,1 +3,4 +4,3 +5,1 +5,5 \ No newline at end of file diff --git a/src/main/resources/META-INF/Hitori8x8leicht.csv b/src/main/resources/META-INF/Hitori8x8leicht.csv new file mode 100644 index 0000000..6020688 --- /dev/null +++ b/src/main/resources/META-INF/Hitori8x8leicht.csv @@ -0,0 +1,29 @@ +2,7,2,1,5,8,3,5 +6,6,1,6,2,4,4,7 +7,5,8,2,6,6,6,1 +3,6,8,8,4,2,1,8 +6,4,7,7,8,2,6,3 +1,6,4,5,1,3,5,8 +8,1,3,3,6,4,2,6 +5,3,6,4,3,4,8,2 + +// Lösung (schwarze Felder) +1,1 +1,8 +2,2 +2,4 +2,6 +3,5 +3,7 +4,3 +4,8 +5,1 +5,4 +5,6 +6,2 +6,5 +6,7 +7,4 +7,8 +8,2 +8,6 \ No newline at end of file diff --git a/src/main/resources/META-INF/Hitori8x8medium.csv b/src/main/resources/META-INF/Hitori8x8medium.csv new file mode 100644 index 0000000..9116a6e --- /dev/null +++ b/src/main/resources/META-INF/Hitori8x8medium.csv @@ -0,0 +1,30 @@ +4,4,3,5,6,5,7,7 +8,5,7,6,8,4,7,1 +7,2,1,2,4,6,2,3 +8,6,5,2,7,7,3,6 +3,1,2,7,3,8,6,4 +1,2,4,1,5,7,3,2 +5,8,2,4,3,4,1,5 +2,4,5,1,8,3,8,1 + +//Lösung (schwarze Zahlen) +1,2 +1,4 +1,8 +2,5 +2,7 +3,2 +3,4 +4,1 +4,6 +4,8 +5,3 +5,5 +6,2 +6,4 +6,7 +7,1 +7,6 +8,3 +8,5 +8,8 \ No newline at end of file diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 0000000..445bc24 --- /dev/null +++ b/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Main-Class: de.hs_mannheim.informatik.backend.HitoriGameGUI +