From 81f05a47d2248859934ffdba790f1ed9bc4ac0b0 Mon Sep 17 00:00:00 2001 From: Jaden Tian <56417002+jadeandtea@users.noreply.github.com> Date: Fri, 27 Sep 2024 16:09:07 -0400 Subject: [PATCH] Revert "Merge updated binary puzzle, proof editor, puzzle editor, home panel, bug fixes" --- .gitignore | 3 +- .settings/org.eclipse.buildship.core.prefs | 13 - .../LastCellForNumberDirectRule/TestBoard | 19 - .../LastNumberForCellDirectRule/FullRegion | 19 - .../rules/RepeatedNumberContradictionRule/a | 0 .../10x10 Binary Hard/10x10 Binary Hard 1 | 111 ----- .../10x10 Binary Hard/10x10 Binary Hard 2 | 111 ----- .../10x10 Binary Hard/10x10 Binary Hard 3 | 111 ----- .../10x10 Binary Medium/10x10 Binary Medium 1 | 111 ----- .../10x10 Binary Medium/10x10 Binary Medium 2 | 111 ----- .../10x10 Binary Medium/10x10 Binary Medium 3 | 111 ----- .../10x10 Binary Very Hard 1 | 111 ----- .../binary/6x6 Binary Easy/6x6 Binary Easy 1 | 22 - .../binary/6x6 Binary Easy/6x6 Binary Easy 2 | 21 - .../binary/6x6 Binary Easy/6x6 Binary Easy 3 | 25 - .../binary/6x6 Binary Easy/6x6 Binary Easy 4 | 28 -- .../binary/6x6 Binary Easy/6x6 Binary Easy 5 | 47 -- .../binary/6x6 Binary Hard/6x6 Binary Hard 1 | 47 -- .../binary/6x6 Binary Hard/6x6 Binary Hard 2 | 47 -- .../binary/6x6 Binary Hard/6x6 Binary Hard 3 | 47 -- .../6x6 Binary Medium/6x6 Binary Medium 1 | 47 -- .../6x6 Binary Medium/6x6 Binary Medium 2 | 47 -- .../6x6 Binary Medium/6x6 Binary Medium 3 | 47 -- .../6x6 Binary Very Hard 1 | 47 -- .../6x6 Binary Very Hard 2 | 47 -- .../6x6 Binary Very Hard 3 | 47 -- .../binary/8x8 Binary Easy/8x8 Binary Easy 1 | 75 --- .../binary/8x8 Binary Easy/8x8 Binary Easy 2 | 75 --- .../binary/8x8 Binary Easy/8x8 Binary Easy 3 | 75 --- .../binary/8x8 Binary Hard/8x8 Binary Hard 1 | 75 --- .../binary/8x8 Binary Hard/8x8 Binary Hard 2 | 75 --- .../binary/8x8 Binary Hard/8x8 Binary Hard 3 | 75 --- .../8x8 Binary Medium/8x8 Binary Medium 1 | 75 --- .../8x8 Binary Medium/8x8 Binary Medium 2 | 75 --- .../8x8 Binary Medium/8x8 Binary Medium 3 | 75 --- .../8x8 Binary Very Hard 1 | 75 --- .../8x8 Binary Very Hard 2 | 75 --- .../8x8 Binary Very Hard 3 | 75 --- puzzles files/light-color-theme.txt | 99 ---- .../5x5 Star Battle 1 star Normal 1.xml | 53 ++ .../6x6 Star Battle 1star Normal1.xml | 68 +++ .../6x6 Star Battle 1star Normal2.xml | 68 +++ .../6x6 StarBattle 1star Normal3.xml | 68 +++ .../7x7 Star Battle 1star Hard1.xml | 85 ++++ .../7x7 Star Battle 1star Normal.xml | 85 ++++ .../7x7 Star Battle 1star Normal1.xml | 85 ++++ .../7x7 Star Battle 1star Normal2.xml | 85 ++++ .../8x8 Star Battle 1star Normal1.xml | 105 ++++ .../8x8 Star Battle 1star Normal2.xml | 104 ++++ .../8x8 Star Battle 1star Normal3.xml | 104 ++++ src/main/java/edu/rpi/legup/app/Config.java | 23 - .../edu/rpi/legup/app/GameBoardFacade.java | 119 ++--- .../rpi/legup/app/InvalidConfigException.java | 8 - .../edu/rpi/legup/app/LegupPreferences.java | 39 +- .../edu/rpi/legup/controller/Controller.java | 9 - .../legup/controller/CursorController.java | 4 - .../controller/EditorElementController.java | 20 +- .../legup/controller/ElementController.java | 18 +- .../rpi/legup/controller/RuleController.java | 5 - .../rpi/legup/controller/TreeController.java | 4 - .../legup/history/AddTreeElementCommand.java | 15 +- .../ApplyDefaultDirectRuleCommand.java | 15 +- .../legup/history/AutoCaseRuleCommand.java | 26 +- .../edu/rpi/legup/history/CommandError.java | 16 - .../edu/rpi/legup/history/CommandState.java | 14 - .../history/DeleteTreeElementCommand.java | 25 +- .../rpi/legup/history/EditDataCommand.java | 50 +- .../java/edu/rpi/legup/history/History.java | 25 +- .../java/edu/rpi/legup/history/ICommand.java | 16 +- .../rpi/legup/history/IHistoryListener.java | 18 +- .../rpi/legup/history/IHistorySubject.java | 18 +- .../InvalidCommandStateTransition.java | 11 - .../edu/rpi/legup/history/MergeCommand.java | 12 +- .../edu/rpi/legup/history/PuzzleCommand.java | 39 +- .../history/ValidateCaseRuleCommand.java | 13 +- .../ValidateContradictionRuleCommand.java | 19 +- .../history/ValidateDirectRuleCommand.java | 48 +- src/main/java/edu/rpi/legup/model/Puzzle.java | 105 ++-- .../edu/rpi/legup/model/PuzzleExporter.java | 26 - .../edu/rpi/legup/model/PuzzleImporter.java | 54 +- .../edu/rpi/legup/model/RegisterPuzzle.java | 3 - .../edu/rpi/legup/model/elements/Element.java | 50 -- .../rpi/legup/model/elements/ElementType.java | 7 +- .../model/elements/NonPlaceableElement.java | 18 +- .../model/elements/PlaceableElement.java | 13 - .../legup/model/elements/RegisterElement.java | 5 - .../edu/rpi/legup/model/gameboard/Board.java | 20 +- .../rpi/legup/model/gameboard/CaseBoard.java | 59 --- .../legup/model/gameboard/ElementFactory.java | 3 - .../rpi/legup/model/gameboard/GridBoard.java | 5 - .../rpi/legup/model/gameboard/GridCell.java | 7 - .../rpi/legup/model/gameboard/GridRegion.java | 13 +- .../legup/model/gameboard/PuzzleElement.java | 26 - .../legup/model/observer/IBoardListener.java | 6 +- .../legup/model/observer/IBoardSubject.java | 6 +- .../legup/model/observer/ITreeListener.java | 4 - .../legup/model/observer/ITreeSubject.java | 8 +- .../edu/rpi/legup/model/rules/CaseRule.java | 4 - .../legup/model/rules/ContradictionRule.java | 4 - .../edu/rpi/legup/model/rules/DirectRule.java | 4 - .../edu/rpi/legup/model/rules/MergeRule.java | 6 +- .../rpi/legup/model/rules/RegisterRule.java | 5 - .../java/edu/rpi/legup/model/rules/Rule.java | 11 - .../edu/rpi/legup/model/rules/RuleType.java | 4 - .../java/edu/rpi/legup/model/tree/Tree.java | 59 +-- .../edu/rpi/legup/model/tree/TreeElement.java | 5 +- .../rpi/legup/model/tree/TreeElementType.java | 4 - .../edu/rpi/legup/model/tree/TreeNode.java | 13 +- .../rpi/legup/model/tree/TreeTransition.java | 24 - .../edu/rpi/legup/puzzle/binary/Binary.java | 36 +- .../rpi/legup/puzzle/binary/BinaryBoard.java | 54 +- .../rpi/legup/puzzle/binary/BinaryCell.java | 57 +-- .../puzzle/binary/BinaryCellFactory.java | 16 +- .../legup/puzzle/binary/BinaryController.java | 16 +- .../puzzle/binary/BinaryElementView.java | 129 +++-- .../legup/puzzle/binary/BinaryExporter.java | 9 +- .../legup/puzzle/binary/BinaryImporter.java | 28 +- .../rpi/legup/puzzle/binary/BinaryType.java | 19 +- .../rpi/legup/puzzle/binary/BinaryView.java | 7 - .../puzzle/binary/elements/BlankTile.java | 1 + .../puzzle/binary/elements/NumberTile.java | 13 - .../puzzle/binary/elements/UnknownTile.java | 13 - .../binary_elements_reference_sheet.txt | 2 - .../rules/CompleteRowColumnDirectRule.java | 27 +- ...licateRowsOrColumnsContradictionRule.java} | 28 +- .../EliminateTheImpossibleDirectRule.java | 201 -------- ...neCaseRule.java => OneOrZeroCaseRule.java} | 57 +-- .../binary/rules/OneTileGapDirectRule.java | 64 +++ .../binary/rules/PreventTrioDirectRule.java | 58 --- .../binary/rules/SaveBlockerDirectRule.java | 60 --- .../binary/rules/SurroundPairDirectRule.java | 48 ++ .../rules/ThreeAdjacentContradictionRule.java | 127 +++++ .../binary/rules/TrioContradictionRule.java | 185 ------- ...balancedRowOrColumnContradictionRule.java} | 31 +- .../rules/UniqueRowColumnDirectRule.java | 288 ----------- .../rules/WastedBlockerContradictionRule.java | 178 ------- .../binary/rules/binary_reference_sheet.txt | 16 +- .../legup/puzzle/fillapix/FillapixCell.java | 6 +- .../puzzle/fillapix/elements/BlackTile.java | 2 +- .../puzzle/fillapix/elements/NumberTile.java | 6 +- .../puzzle/fillapix/elements/UnknownTile.java | 6 +- .../puzzle/fillapix/elements/WhiteTile.java | 2 +- .../fillapix_elements_reference_sheet.txt | 9 +- .../fillapix/rules/BlackOrWhiteCaseRule.java | 3 - .../fillapix/rules/SatisfyClueCaseRule.java | 3 - .../edu/rpi/legup/puzzle/lightup/LightUp.java | 2 +- .../legup/puzzle/lightup/LightUpBoard.java | 6 +- .../rpi/legup/puzzle/lightup/LightUpCell.java | 8 +- .../puzzle/lightup/elements/BlackTile.java | 6 +- .../puzzle/lightup/elements/BulbTile.java | 2 +- .../puzzle/lightup/elements/NumberTile.java | 8 +- .../puzzle/lightup/elements/UnknownTile.java | 6 +- .../lightup_elements_reference_sheet.txt | 4 - .../lightup/rules/LightOrEmptyCaseRule.java | 4 - .../lightup/rules/SatisfyNumberCaseRule.java | 6 +- .../rules/TooFewBulbsContradictionRule.java | 2 +- .../puzzle/minesweeper/elements/BombTile.java | 4 +- .../minesweeper/elements/UnsetTile.java | 4 +- .../legup/puzzle/nurikabe/NurikabeCell.java | 6 +- .../puzzle/nurikabe/NurikabeController.java | 10 - .../puzzle/nurikabe/NurikabeExporter.java | 7 - .../puzzle/nurikabe/elements/BlackTile.java | 2 +- .../puzzle/nurikabe/elements/NumberTile.java | 6 +- .../puzzle/nurikabe/elements/UnknownTile.java | 6 +- .../puzzle/nurikabe/elements/WhiteTile.java | 2 +- .../nurikabe_elements_reference_sheet.txt | 4 - .../nurikabe/rules/BlackOrWhiteCaseRule.java | 6 +- .../nurikabe/rules/FinishRoomCaseRule.java | 252 +++++----- .../shorttruthtable/ShortTruthTableCell.java | 10 +- .../elements/ArgumentElement.java | 6 +- .../elements/GreenElement.java | 2 +- .../elements/LogicSymbolElement.java | 6 +- .../shorttruthtable/elements/RedElement.java | 2 +- .../elements/UnknownElement.java | 2 +- .../shorttruthtable_elements_reference_sheet | 11 +- .../rules/caserule/CaseRuleAtomic.java | 3 - .../caserule/CaseRule_GenericStatement.java | 5 - .../legup/puzzle/skyscrapers/Skyscrapers.java | 2 +- .../puzzle/skyscrapers/SkyscrapersBoard.java | 6 +- .../puzzle/skyscrapers/SkyscrapersCell.java | 4 +- .../skyscrapers/SkyscrapersClueView.java | 19 +- .../puzzle/skyscrapers/elements/ClueTile.java | 14 + .../skyscrapers/elements/NumberTile.java | 6 +- .../skyscrapers/elements/UnknownTile.java | 6 +- .../skyscrapers_elements_reference_sheet.txt | 5 +- .../rules/CellForNumberCaseRule.java | 3 - .../rules/NumberForCellCaseRule.java | 3 - .../rpi/legup/puzzle/starbattle/allfiles.txt | 235 +++++++++ .../puzzle/starbattle/elements/BlackTile.java | 4 +- .../puzzle/starbattle/elements/StarTile.java | 4 +- .../starbattle/elements/UnknownTile.java | 4 +- .../starbattle_elements_reference_sheet.txt | 4 - .../starbattle/rules/StarOrEmptyCaseRule.java | 4 - .../rules/starbattle_reference_sheet.txt | 19 + .../legup/puzzle/sudoku/ModelSudokuBoard.java | 17 - .../sudoku/PossibleNumberCaseBoard.java | 4 +- .../edu/rpi/legup/puzzle/sudoku/Sudoku.java | 2 - .../rpi/legup/puzzle/sudoku/SudokuCell.java | 40 +- .../puzzle/sudoku/SudokuCellController.java | 2 +- .../legup/puzzle/sudoku/SudokuImporter.java | 10 + .../rpi/legup/puzzle/sudoku/SudokuView.java | 9 +- .../puzzle/sudoku/elements/NumberTile.java | 26 +- .../puzzle/sudoku/elements/UnknownTile.java | 13 - .../sudoku_elements_reference_sheet.txt | 2 - .../rules/AdvancedDeductionDirectRule.java | 99 ++++ .../rules/LastCellForNumberDirectRule.java | 136 +---- .../rules/LastNumberForCellDirectRule.java | 25 +- ...oCellForNumberColumnContradictionRule.java | 90 ---- ...oCellForNumberRegionContradictionRule.java | 90 ---- .../NoCellForNumberRowContradictionRule.java | 90 ---- ....java => NoSolutionContradictionRule.java} | 30 +- ...aseRule.java => PossibleCellCaseRule.java} | 73 +-- .../PossibleCellsForNumberRegionCaseRule.java | 104 ---- .../PossibleCellsForNumberRowCaseRule.java | 107 ---- ...eRule.java => PossibleNumberCaseRule.java} | 82 +-- .../RepeatedNumberContradictionRule.java | 64 +-- .../sudoku/rules/sudoku_reference_sheet.txt | 14 +- .../rpi/legup/puzzle/treetent/TreeTent.java | 17 +- .../puzzle/treetent/elements/GrassTile.java | 2 +- .../puzzle/treetent/elements/TentTile.java | 2 +- .../puzzle/treetent/elements/TreeTile.java | 6 +- .../puzzle/treetent/elements/UnknownTile.java | 6 +- .../treetent_elements_reference_sheet.txt | 4 - .../treetent/rules/FillinRowCaseRule.java | 4 - .../treetent/rules/LinkTentCaseRule.java | 4 - .../treetent/rules/LinkTreeCaseRule.java | 4 - .../edu/rpi/legup/ui/CreatePuzzleDialog.java | 78 +-- .../java/edu/rpi/legup/ui/DynamicView.java | 44 -- .../edu/rpi/legup/ui/DynamicViewType.java | 8 - src/main/java/edu/rpi/legup/ui/HomePanel.java | 206 +++----- .../java/edu/rpi/legup/ui/LegupPanel.java | 9 - src/main/java/edu/rpi/legup/ui/LegupUI.java | 79 +-- .../java/edu/rpi/legup/ui/PickGameDialog.java | 26 - .../edu/rpi/legup/ui/PreferencesDialog.java | 57 --- .../edu/rpi/legup/ui/ProofEditorPanel.java | 471 +++++------------- .../edu/rpi/legup/ui/PuzzleEditorPanel.java | 466 +++++------------ .../java/edu/rpi/legup/ui/ScrollView.java | 14 - .../java/edu/rpi/legup/ui/ToolbarName.java | 9 +- .../java/edu/rpi/legup/ui/ZoomWidget.java | 16 +- .../java/edu/rpi/legup/ui/ZoomablePane.java | 4 - .../edu/rpi/legup/ui/boardview/BoardView.java | 23 - .../legup/ui/boardview/DataSelectionView.java | 9 - .../legup/ui/boardview/ElementSelection.java | 54 -- .../rpi/legup/ui/boardview/ElementView.java | 114 ----- .../rpi/legup/ui/boardview/GridBoardView.java | 33 +- .../legup/ui/boardview/GridElementView.java | 5 - .../legup/ui/boardview/SelectionItemView.java | 40 -- .../rulesview/CaseRulePanel.java | 6 - .../rulesview/ContradictionRulePanel.java | 9 +- .../rulesview/DirectRulePanel.java | 8 +- .../proofeditorui/rulesview/RuleButton.java | 5 - .../ui/proofeditorui/rulesview/RuleFrame.java | 40 +- .../ui/proofeditorui/rulesview/RulePanel.java | 54 +- .../rulesview/SearchBarPanel.java | 5 - .../treeview/TreeElementView.java | 10 +- .../proofeditorui/treeview/TreeNodeView.java | 74 --- .../ui/proofeditorui/treeview/TreePanel.java | 55 -- .../treeview/TreeToolBarButton.java | 14 - .../treeview/TreeToolBarName.java | 4 - .../treeview/TreeToolbarPanel.java | 5 - .../treeview/TreeTransitionView.java | 115 +---- .../ui/proofeditorui/treeview/TreeView.java | 201 ++------ .../treeview/TreeViewSelection.java | 5 - .../elementsview/ElementFrame.java | 67 ++- .../elementsview/ElementPanel.java | 3 +- .../NonPlaceableElementPanel.java | 30 +- .../edu/rpi/legup/images/Legup/Reset.png | Bin 662 -> 0 bytes .../rules/CompleteRowColumnDirectRule.png | Bin 2174 -> 2835 bytes .../DuplicateRowOrColumnContradictionRule.png | Bin 0 -> 3455 bytes .../images/binary/rules/OneOrZeroCaseRule.png | Bin 0 -> 5578 bytes .../binary/rules/OneTileGapDirectRule.png | Bin 0 -> 2751 bytes .../binary/rules/PreventTrioDirectRule.png | Bin 1944 -> 0 bytes .../RepeatedRowColumnContradictionRule.png | Bin 2124 -> 0 bytes .../binary/rules/SaveBlockerDirectRule.png | Bin 2204 -> 0 bytes .../binary/rules/SurroundPairDirectRule.png | Bin 0 -> 2982 bytes .../rules/ThreeAdjacentContradictionRule.png | Bin 0 -> 2508 bytes .../binary/rules/TrioContradictionRule.png | Bin 2071 -> 0 bytes .../UnbalancedRowColumnContradictionRule.png | Bin 1783 -> 2793 bytes .../rules/UniqueRowColumnDirectRule.png | Bin 3304 -> 0 bytes .../rules/WastedBlockerContradictionRule.png | Bin 2006 -> 0 bytes .../images/binary/rules/ZeroOrOneCaseRule.png | Bin 2186 -> 0 bytes .../legup/images/binary/tiles/NumberTile.png | Bin 370 -> 0 bytes .../legup/images/binary/tiles/UnknownTile.png | Bin 7231 -> 0 bytes .../skyscrapers/cases/CellForNumber.png | Bin 1531 -> 1871 bytes .../skyscrapers/cases/NumberForCell.png | Bin 1603 -> 1875 bytes .../contradictions/DuplicateNumber.png | Bin 1991 -> 954 bytes .../contradictions/ExceedingVisibility.png | Bin 1949 -> 1184 bytes .../contradictions/InsufficientVisibility.png | Bin 2071 -> 1291 bytes .../contradictions/PreemptiveVisibility.png | Bin 1784 -> 1101 bytes .../contradictions/UnresolvedCell.png | Bin 2108 -> 1165 bytes .../contradictions/UnresolvedNumber.png | Bin 2658 -> 1357 bytes .../images/skyscrapers/rules/FixedMax.png | Bin 1783 -> 1225 bytes .../images/skyscrapers/rules/LastCell.png | Bin 2174 -> 1352 bytes .../images/skyscrapers/rules/LastNumber.png | Bin 2083 -> 1335 bytes .../legup/images/skyscrapers/rules/NEdge.png | Bin 2027 -> 1523 bytes .../images/skyscrapers/rules/OneEdge.png | Bin 1455 -> 934 bytes .../tiles => starbattle}/UnknownTile.png | Bin .../edu/rpi/legup/images/starbattle/black.gif | Bin 0 -> 856 bytes .../starbattle/cases/StarOrEmptyCaseRule.png | Bin 0 -> 4437 bytes .../ClashingOrbitContradictionRule.png | Bin 0 -> 5135 bytes .../TooFewStarsContradictionRule.png | Bin 0 -> 3218 bytes .../TooManyStarsContradictionRule.png | Bin 0 -> 6199 bytes .../edu/rpi/legup/images/starbattle/empty.gif | Bin 0 -> 857 bytes .../starbattle/rules/BlackOutDirectRule.png | Bin 0 -> 4717 bytes .../rules/ColumnsWithinRegionsDirectRule.png | Bin 0 -> 6456 bytes .../rules/ColumnsWithinRowsDirectRule.png | Bin 0 -> 5331 bytes .../rules/FinishWithStarDirectRule.png | Bin 0 -> 4326 bytes .../rules/RegionsWithinColumnsDirectRule.png | Bin 0 -> 6402 bytes .../rules/RegionsWithinRowsDirectRule.png | Bin 0 -> 6269 bytes .../rules/RowsWithinColumnsDirectRule.png | Bin 0 -> 5117 bytes .../rules/RowsWithinRegionsDirectRule.png | Bin 0 -> 6477 bytes .../images/starbattle/rules/SurroundStar.png | Bin 0 -> 5102 bytes .../edu/rpi/legup/images/starbattle/star.gif | Bin 0 -> 545 bytes .../NumberTile.png => starbattle/white.gif} | Bin 10067 -> 9700 bytes .../legup/images/sudoku/AdvancedDeduction.png | Bin 0 -> 3351 bytes .../images/sudoku/{rules => }/NoSolution.png | Bin .../sudoku/{rules => }/PossibleValues.png | Bin .../sudoku/{rules => }/RepeatedNumber.png | Bin .../sudoku/{rules => }/forcedByDeduction.png | Bin .../{rules => }/forcedByElimination.png | Bin .../{rules => }/possible_cells_number.png | Bin .../sudoku/rules/NoCellForNumberColumn.png | Bin 958 -> 0 bytes .../sudoku/rules/NoCellForNumberRegion.png | Bin 1077 -> 0 bytes .../sudoku/rules/NoCellForNumberRow.png | Bin 916 -> 0 bytes .../rules/possible_cells_number_column.png | Bin 3336 -> 0 bytes .../rules/possible_cells_number_region.png | Bin 3623 -> 0 bytes .../rules/possible_cells_number_row.png | Bin 803 -> 0 bytes .../legup/images/sudoku/{rules => }/tem.png | Bin src/main/resources/edu/rpi/legup/legup/config | 30 +- src/test/java/legup/TestUtilities.java | 10 +- .../rules/FinishRoomCaseRuleTest.java | 37 +- ...LastNumberForCellDirectRuleRegionTest.java | 100 ---- .../RepeatedNumberContradictionRuleTest.java | 88 ---- .../SurroundTwoZerosWithTwoOnes | 10 - .../binary/rules/SurroundPairDirectRule/test | 0 .../SurroundTwoZerosWithTwoOnes | 10 - .../BlackoutDirectRule/ColumnBlackout | 40 ++ .../BlackoutDirectRule/RegionBlackout | 36 ++ .../BlackoutDirectRule/RowBlackout | 36 ++ .../CorneredRegion | 14 - .../LastNumberForCellDirectRule/FullMixed | 18 - .../LastNumberForCellDirectRule/FullRegion | 18 - .../rules/LastNumberForCellDirectRule/FullRow | 18 - .../BlankBoard4 | 12 - .../BlankBoard7 | 11 - 345 files changed, 2975 insertions(+), 8239 deletions(-) delete mode 100644 .settings/org.eclipse.buildship.core.prefs delete mode 100644 output_path/test/src/resources/puzzles/sudoku/rules/LastCellForNumberDirectRule/TestBoard delete mode 100644 output_path/test/src/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRegion delete mode 100644 output_path/test/src/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/a delete mode 100644 puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 1 delete mode 100644 puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 2 delete mode 100644 puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 3 delete mode 100644 puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 1 delete mode 100644 puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 2 delete mode 100644 puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 3 delete mode 100644 puzzles files/binary/10x10 Binary Very Hard/10x10 Binary Very Hard 1 delete mode 100644 puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 1 delete mode 100644 puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 2 delete mode 100644 puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 3 delete mode 100644 puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 4 delete mode 100644 puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 5 delete mode 100644 puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 1 delete mode 100644 puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 2 delete mode 100644 puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 3 delete mode 100644 puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 1 delete mode 100644 puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 2 delete mode 100644 puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 3 delete mode 100644 puzzles files/binary/6x6 Binary Very Hard/6x6 Binary Very Hard 1 delete mode 100644 puzzles files/binary/6x6 Binary Very Hard/6x6 Binary Very Hard 2 delete mode 100644 puzzles files/binary/6x6 Binary Very Hard/6x6 Binary Very Hard 3 delete mode 100644 puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 1 delete mode 100644 puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 2 delete mode 100644 puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 3 delete mode 100644 puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 1 delete mode 100644 puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 2 delete mode 100644 puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 3 delete mode 100644 puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 1 delete mode 100644 puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 2 delete mode 100644 puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 3 delete mode 100644 puzzles files/binary/8x8 Binary Very Hard/8x8 Binary Very Hard 1 delete mode 100644 puzzles files/binary/8x8 Binary Very Hard/8x8 Binary Very Hard 2 delete mode 100644 puzzles files/binary/8x8 Binary Very Hard/8x8 Binary Very Hard 3 delete mode 100644 puzzles files/light-color-theme.txt create mode 100644 puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml create mode 100644 puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 Star Battle 1star Normal1.xml create mode 100644 puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 Star Battle 1star Normal2.xml create mode 100644 puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 StarBattle 1star Normal3.xml create mode 100644 puzzles files/starbattle/7x7 Star Battle 1 star Hard/7x7 Star Battle 1star Hard1.xml create mode 100644 puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal.xml create mode 100644 puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal1.xml create mode 100644 puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal2.xml create mode 100644 puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal1.xml create mode 100644 puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal2.xml create mode 100644 puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal3.xml create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/elements/UnknownTile.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/elements/binary_elements_reference_sheet.txt rename src/main/java/edu/rpi/legup/puzzle/binary/rules/{RepeatedRowColumnContradictionRule.java => DuplicateRowsOrColumnsContradictionRule.java} (60%) delete mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/EliminateTheImpossibleDirectRule.java rename src/main/java/edu/rpi/legup/puzzle/binary/rules/{ZeroOrOneCaseRule.java => OneOrZeroCaseRule.java} (56%) create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/PreventTrioDirectRule.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/SaveBlockerDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentContradictionRule.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/TrioContradictionRule.java rename src/main/java/edu/rpi/legup/puzzle/binary/rules/{UnbalancedRowColumnContradictionRule.java => UnbalancedRowOrColumnContradictionRule.java} (64%) delete mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/UniqueRowColumnDirectRule.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/binary/rules/WastedBlockerContradictionRule.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/lightup/elements/lightup_elements_reference_sheet.txt delete mode 100644 src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/nurikabe_elements_reference_sheet.txt create mode 100644 src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/ClueTile.java create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt delete mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/elements/starbattle_elements_reference_sheet.txt create mode 100644 src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt delete mode 100644 src/main/java/edu/rpi/legup/puzzle/sudoku/ModelSudokuBoard.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/sudoku/elements/UnknownTile.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/sudoku/elements/sudoku_elements_reference_sheet.txt create mode 100644 src/main/java/edu/rpi/legup/puzzle/sudoku/rules/AdvancedDeductionDirectRule.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberColumnContradictionRule.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberRegionContradictionRule.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberRowContradictionRule.java rename src/main/java/edu/rpi/legup/puzzle/sudoku/rules/{NoNumberForCellContradictionRule.java => NoSolutionContradictionRule.java} (72%) rename src/main/java/edu/rpi/legup/puzzle/sudoku/rules/{PossibleNumbersForCellCaseRule.java => PossibleCellCaseRule.java} (59%) delete mode 100644 src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberRegionCaseRule.java delete mode 100644 src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberRowCaseRule.java rename src/main/java/edu/rpi/legup/puzzle/sudoku/rules/{PossibleCellsForNumberColumnCaseRule.java => PossibleNumberCaseRule.java} (53%) delete mode 100644 src/main/java/edu/rpi/legup/puzzle/treetent/elements/treetent_elements_reference_sheet.txt delete mode 100644 src/main/resources/edu/rpi/legup/images/Legup/Reset.png create mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/DuplicateRowOrColumnContradictionRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/OneOrZeroCaseRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/OneTileGapDirectRule.png delete mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/PreventTrioDirectRule.png delete mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/RepeatedRowColumnContradictionRule.png delete mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/SaveBlockerDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/SurroundPairDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/ThreeAdjacentContradictionRule.png delete mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/TrioContradictionRule.png delete mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/UniqueRowColumnDirectRule.png delete mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/WastedBlockerContradictionRule.png delete mode 100644 src/main/resources/edu/rpi/legup/images/binary/rules/ZeroOrOneCaseRule.png delete mode 100644 src/main/resources/edu/rpi/legup/images/binary/tiles/NumberTile.png delete mode 100644 src/main/resources/edu/rpi/legup/images/binary/tiles/UnknownTile.png rename src/main/resources/edu/rpi/legup/images/{sudoku/tiles => starbattle}/UnknownTile.png (100%) create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/black.gif create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/cases/StarOrEmptyCaseRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/contradictions/ClashingOrbitContradictionRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/contradictions/TooFewStarsContradictionRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/contradictions/TooManyStarsContradictionRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/empty.gif create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/BlackOutDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/ColumnsWithinRegionsDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/ColumnsWithinRowsDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/FinishWithStarDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/RegionsWithinColumnsDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/RegionsWithinRowsDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/RowsWithinColumnsDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/RowsWithinRegionsDirectRule.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/rules/SurroundStar.png create mode 100644 src/main/resources/edu/rpi/legup/images/starbattle/star.gif rename src/main/resources/edu/rpi/legup/images/{sudoku/tiles/NumberTile.png => starbattle/white.gif} (82%) create mode 100644 src/main/resources/edu/rpi/legup/images/sudoku/AdvancedDeduction.png rename src/main/resources/edu/rpi/legup/images/sudoku/{rules => }/NoSolution.png (100%) rename src/main/resources/edu/rpi/legup/images/sudoku/{rules => }/PossibleValues.png (100%) rename src/main/resources/edu/rpi/legup/images/sudoku/{rules => }/RepeatedNumber.png (100%) rename src/main/resources/edu/rpi/legup/images/sudoku/{rules => }/forcedByDeduction.png (100%) rename src/main/resources/edu/rpi/legup/images/sudoku/{rules => }/forcedByElimination.png (100%) rename src/main/resources/edu/rpi/legup/images/sudoku/{rules => }/possible_cells_number.png (100%) delete mode 100644 src/main/resources/edu/rpi/legup/images/sudoku/rules/NoCellForNumberColumn.png delete mode 100644 src/main/resources/edu/rpi/legup/images/sudoku/rules/NoCellForNumberRegion.png delete mode 100644 src/main/resources/edu/rpi/legup/images/sudoku/rules/NoCellForNumberRow.png delete mode 100644 src/main/resources/edu/rpi/legup/images/sudoku/rules/possible_cells_number_column.png delete mode 100644 src/main/resources/edu/rpi/legup/images/sudoku/rules/possible_cells_number_region.png delete mode 100644 src/main/resources/edu/rpi/legup/images/sudoku/rules/possible_cells_number_row.png rename src/main/resources/edu/rpi/legup/images/sudoku/{rules => }/tem.png (100%) delete mode 100644 src/test/java/puzzles/sudoku/rules/LastNumberForCellDirectRuleRegionTest.java delete mode 100644 src/test/java/puzzles/sudoku/rules/RepeatedNumberContradictionRuleTest.java delete mode 100644 src/test/resources/puzzles/binary/rules/SurroundPairDirectRule/SurroundTwoZerosWithTwoOnes delete mode 100644 src/test/resources/puzzles/binary/rules/SurroundPairDirectRule/test delete mode 100644 src/test/resources/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRule/SurroundTwoZerosWithTwoOnes create mode 100644 src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/ColumnBlackout create mode 100644 src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RegionBlackout create mode 100644 src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RowBlackout delete mode 100644 src/test/resources/puzzles/sudoku/rules/LastCellForNumberDirectRule/CorneredRegion delete mode 100644 src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullMixed delete mode 100644 src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRegion delete mode 100644 src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRow delete mode 100644 src/test/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/BlankBoard4 delete mode 100644 src/test/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/BlankBoard7 diff --git a/.gitignore b/.gitignore index bafea84a3..baee37e51 100644 --- a/.gitignore +++ b/.gitignore @@ -92,5 +92,4 @@ gradle-app.setting gradle/wrapper/gradle-wrapper.properties # Visual Studio Code configs -.vscode/* -src/test/java/legup/TestRunner.java +.vscode/* \ No newline at end of file diff --git a/.settings/org.eclipse.buildship.core.prefs b/.settings/org.eclipse.buildship.core.prefs deleted file mode 100644 index ea640e792..000000000 --- a/.settings/org.eclipse.buildship.core.prefs +++ /dev/null @@ -1,13 +0,0 @@ -arguments=--init-script /home/gamma/.cache/jdtls/config/org.eclipse.osgi/55/0/.cp/gradle/init/init.gradle -auto.sync=false -build.scans.enabled=false -connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) -connection.project.dir= -eclipse.preferences.version=1 -gradle.user.home= -java.home=/usr/lib/jvm/java-21-openjdk-amd64 -jvm.arguments= -offline.mode=false -override.workspace.settings=true -show.console.view=true -show.executions.view=true diff --git a/output_path/test/src/resources/puzzles/sudoku/rules/LastCellForNumberDirectRule/TestBoard b/output_path/test/src/resources/puzzles/sudoku/rules/LastCellForNumberDirectRule/TestBoard deleted file mode 100644 index a41ad749c..000000000 --- a/output_path/test/src/resources/puzzles/sudoku/rules/LastCellForNumberDirectRule/TestBoard +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/output_path/test/src/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRegion b/output_path/test/src/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRegion deleted file mode 100644 index 49dae4aa6..000000000 --- a/output_path/test/src/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRegion +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/output_path/test/src/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/a b/output_path/test/src/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/a deleted file mode 100644 index e69de29bb..000000000 diff --git a/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 1 b/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 1 deleted file mode 100644 index 42ccf371b..000000000 --- a/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 1 +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 2 b/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 2 deleted file mode 100644 index d73caa5d2..000000000 --- a/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 2 +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 3 b/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 3 deleted file mode 100644 index 99ec9769b..000000000 --- a/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 3 +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 1 b/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 1 deleted file mode 100644 index d203617c8..000000000 --- a/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 1 +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 2 b/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 2 deleted file mode 100644 index db56f04f3..000000000 --- a/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 2 +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 3 b/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 3 deleted file mode 100644 index 11940a6eb..000000000 --- a/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 3 +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/10x10 Binary Very Hard/10x10 Binary Very Hard 1 b/puzzles files/binary/10x10 Binary Very Hard/10x10 Binary Very Hard 1 deleted file mode 100644 index 828a450cf..000000000 --- a/puzzles files/binary/10x10 Binary Very Hard/10x10 Binary Very Hard 1 +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 1 b/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 1 deleted file mode 100644 index 7b22ffc10..000000000 --- a/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 1 +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 2 b/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 2 deleted file mode 100644 index ea8ef93b0..000000000 --- a/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 2 +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 3 b/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 3 deleted file mode 100644 index 0f0ff745e..000000000 --- a/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 3 +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 4 b/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 4 deleted file mode 100644 index da76d067b..000000000 --- a/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 4 +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 5 b/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 5 deleted file mode 100644 index a1ea13988..000000000 --- a/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 5 +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 1 b/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 1 deleted file mode 100644 index 5f7f72a8a..000000000 --- a/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 1 +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 2 b/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 2 deleted file mode 100644 index a4ed30c31..000000000 --- a/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 2 +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 3 b/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 3 deleted file mode 100644 index fc0e413c1..000000000 --- a/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 3 +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 1 b/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 1 deleted file mode 100644 index a5ab8a2dc..000000000 --- a/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 1 +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 2 b/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 2 deleted file mode 100644 index 4be5fdaad..000000000 --- a/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 2 +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 3 b/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 3 deleted file mode 100644 index eba370cab..000000000 --- a/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 3 +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/6x6 Binary Very Hard/6x6 Binary Very Hard 1 b/puzzles files/binary/6x6 Binary Very Hard/6x6 Binary Very Hard 1 deleted file mode 100644 index faa68fa5e..000000000 --- a/puzzles files/binary/6x6 Binary Very Hard/6x6 Binary Very Hard 1 +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/6x6 Binary Very Hard/6x6 Binary Very Hard 2 b/puzzles files/binary/6x6 Binary Very Hard/6x6 Binary Very Hard 2 deleted file mode 100644 index 3c707bdaa..000000000 --- a/puzzles files/binary/6x6 Binary Very Hard/6x6 Binary Very Hard 2 +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/6x6 Binary Very Hard/6x6 Binary Very Hard 3 b/puzzles files/binary/6x6 Binary Very Hard/6x6 Binary Very Hard 3 deleted file mode 100644 index 217a032d8..000000000 --- a/puzzles files/binary/6x6 Binary Very Hard/6x6 Binary Very Hard 3 +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 1 b/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 1 deleted file mode 100644 index befd674f9..000000000 --- a/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 1 +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 2 b/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 2 deleted file mode 100644 index 724426c26..000000000 --- a/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 2 +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 3 b/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 3 deleted file mode 100644 index 92a96c72b..000000000 --- a/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 3 +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 1 b/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 1 deleted file mode 100644 index 34eaf8388..000000000 --- a/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 1 +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 2 b/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 2 deleted file mode 100644 index 9ef23277e..000000000 --- a/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 2 +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 3 b/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 3 deleted file mode 100644 index 287ff6f68..000000000 --- a/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 3 +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 1 b/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 1 deleted file mode 100644 index 47dae23dc..000000000 --- a/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 1 +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 2 b/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 2 deleted file mode 100644 index ae4cb8bb0..000000000 --- a/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 2 +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 3 b/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 3 deleted file mode 100644 index 2f951ecc4..000000000 --- a/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 3 +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/8x8 Binary Very Hard/8x8 Binary Very Hard 1 b/puzzles files/binary/8x8 Binary Very Hard/8x8 Binary Very Hard 1 deleted file mode 100644 index 9c875523b..000000000 --- a/puzzles files/binary/8x8 Binary Very Hard/8x8 Binary Very Hard 1 +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/8x8 Binary Very Hard/8x8 Binary Very Hard 2 b/puzzles files/binary/8x8 Binary Very Hard/8x8 Binary Very Hard 2 deleted file mode 100644 index 14f2e4ad2..000000000 --- a/puzzles files/binary/8x8 Binary Very Hard/8x8 Binary Very Hard 2 +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/binary/8x8 Binary Very Hard/8x8 Binary Very Hard 3 b/puzzles files/binary/8x8 Binary Very Hard/8x8 Binary Very Hard 3 deleted file mode 100644 index ad319a4b7..000000000 --- a/puzzles files/binary/8x8 Binary Very Hard/8x8 Binary Very Hard 3 +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/puzzles files/light-color-theme.txt b/puzzles files/light-color-theme.txt deleted file mode 100644 index 5c45bc71b..000000000 --- a/puzzles files/light-color-theme.txt +++ /dev/null @@ -1,99 +0,0 @@ -correct: BLUE -incorrect: RED -error: RED_700 -info: GRAY_900 -ui-movement: GRAY_300 -password-field-background: LIGHT_BLUE_400 -password-field-unfocused-background: GRAY_200 -progress-bar-background: GRAY_200 -progress-bar-foreground: LIGHT_BLUE_400 -text-field-background: LIGHT_BLUE_400 -text-field-unfocused-background: GRAY_200 -light-line-border: GRAY_200 -thick-line-border: GRAY_200 -data-selection-background: GRAY -element-view: BLACK -button-highlight: GRAY_300 -button-background: GRAY_200 -button-foreground: BLACK -checkbox-background: WHITE -checkbox-foreground: BLACK -combobox-background: WHITE -combobox-foreground: BLACK -combobox-button-background: GRAY_300 -combobox-selection-background: WHITE -combobox-selection-foreground: BLACK -combobox-selected-in-drop-down-background: GRAY_200 -label-background: WHITE -label-foreground: BLACK -menu-background: LIGHT_BLUE_100 -menu-foreground: BLACK -menu-selection-background: GRAY_200 -menu-selection-foreground: BLACK -menu-disabled-foreground: #000 -menu-bar-background: WHITE -menu-bar-foreground: BLACK -menu-item-disabled-foreground: #000 -menu-item-selection-background: GRAY_200 -menu-item-selection-foreground: BLACK -menu-item-background: WHITE -menu-item-foreground: BLACK -option-pane-background: WHITE -panel-background-color: WHITE -popup-menu-background: WHITE -popup-menu-foreground: BLACK -radio-button-background: WHITE -radio-button-foreground: BLACK -spinner-background: WHITE -spinner-foreground: BLACK -spinner-arrow-button-background: GRAY_200 -scroll-bar-track: GRAY_200 -scroll-bar-thumb: GRAY_300 -scroll-bar-thumb-dark-shadow: GRAY_300 -scroll-bar-thumb-highlight: GRAY_300 -scroll-bar-thumb-shadow: GRAY_300 -scroll-bar-arrow-button-background: GRAY_300 -scroll-pane-background: WHITE -slider-background: WHITE -slider-foreground: GRAY_700 -slider-track-color: BLACK -split-pane-background: WHITE -tabbed-pane-background: WHITE -tabbed-pane-foreground: BLACK -tabbed-pane-highlight: GRAY_200 -tabbed-pane-border-highlight: GRAY_300 -table-selection-background: GRAY_100 -table-selection-foreground: BLACK -table-background: WHITE -table-grid-color: GRAY_200 -table-header-background: GRAY_200 -text-area-background: GRAY_200 -text-area-foreground: BLACK -toggle-button-background: WHITE -toggle-button-foreground: BLACK -tool-bar-background: WHITE -tool-bar-foreground: BLACK -tool-bar-docking-background: LIGHT_GREEN_A100 -tool-bar-floating-background: GRAY_200 -tree-selection-foreground: BLACK -tree-foreground: BLACK -tree-selection-background: GRAY_200 -tree-background: WHITE -radio-button-menu-item-foreground: BLACK -radio-button-menu-item-selection-foreground: BLACK -radio-button-menu-item-selection-background: GRAY_200 -checkbox-menu-item-selection-background: GRAY_200 -checkbox-menu-item-foreground: BLACK -checkbox-menu-item-selection-foreground: BLACK -text-pane-background: GRAY_50 -text-pane-selection-background: LIGHT_BLUE_200 -text-pane-inactive-foreground: GRAY_500 -editor-pane-background: GRAY_50 -editor-pane-selection-background: LIGHT_BLUE_200 -editor-pane-inactive-foreground: GRAY_500 -separator-background: GRAY_300 -separator-foreground: GRAY_300 -tool-tip-background: GRAY_500 -tool-tip-foreground: GRAY_50 -color-chooser-background: WHITE -color-chooser-foreground: BLACK diff --git a/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml b/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml new file mode 100644 index 000000000..b86e16ff2 --- /dev/null +++ b/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 Star Battle 1star Normal1.xml b/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 Star Battle 1star Normal1.xml new file mode 100644 index 000000000..110dd9abe --- /dev/null +++ b/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 Star Battle 1star Normal1.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 Star Battle 1star Normal2.xml b/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 Star Battle 1star Normal2.xml new file mode 100644 index 000000000..49efeba59 --- /dev/null +++ b/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 Star Battle 1star Normal2.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 StarBattle 1star Normal3.xml b/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 StarBattle 1star Normal3.xml new file mode 100644 index 000000000..855943612 --- /dev/null +++ b/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 StarBattle 1star Normal3.xml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/starbattle/7x7 Star Battle 1 star Hard/7x7 Star Battle 1star Hard1.xml b/puzzles files/starbattle/7x7 Star Battle 1 star Hard/7x7 Star Battle 1star Hard1.xml new file mode 100644 index 000000000..c1d7770f6 --- /dev/null +++ b/puzzles files/starbattle/7x7 Star Battle 1 star Hard/7x7 Star Battle 1star Hard1.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal.xml b/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal.xml new file mode 100644 index 000000000..cab0a0a5e --- /dev/null +++ b/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal1.xml b/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal1.xml new file mode 100644 index 000000000..70b81e376 --- /dev/null +++ b/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal1.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal2.xml b/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal2.xml new file mode 100644 index 000000000..c541ece06 --- /dev/null +++ b/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal2.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal1.xml b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal1.xml new file mode 100644 index 000000000..02dd5d6c0 --- /dev/null +++ b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal1.xml @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal2.xml b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal2.xml new file mode 100644 index 000000000..0df84ef62 --- /dev/null +++ b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal2.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal3.xml b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal3.xml new file mode 100644 index 000000000..725c91d7f --- /dev/null +++ b/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal3.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/app/Config.java b/src/main/java/edu/rpi/legup/app/Config.java index 1016838c7..8a8e9665d 100644 --- a/src/main/java/edu/rpi/legup/app/Config.java +++ b/src/main/java/edu/rpi/legup/app/Config.java @@ -12,11 +12,6 @@ import org.w3c.dom.NodeList; import org.xml.sax.SAXException; -/** - * The {@code Config} class manages the configuration for puzzles by loading configuration data - * from an XML file. It provides methods to access puzzle class names, display names, and their - * file creation statuses - */ public class Config { private static final Logger Logger = LogManager.getLogger(Config.class.getName()); @@ -79,13 +74,6 @@ public static String convertClassNameToDisplayName(String className) { return displayName; } - /** - * Converts the display name of the puzzle back to its corresponding class name. - * For example: convertDisplayNameToClassName("Tree Tent") returns "TreeTent" - * - * @param displayName the display name of the puzzle - * @return the class name of the puzzle as a String - */ public static String convertDisplayNameToClassName(String displayName) { String className = ""; for (int i = 0; i < displayName.length(); i++) { @@ -96,11 +84,6 @@ public static String convertDisplayNameToClassName(String displayName) { return className; } - /** - * Gets a list of all available puzzle display names - * - * @return a List of puzzle display names as Strings - */ public List getPuzzleNames() { List names = new LinkedList(); for (String puzzle : this.getPuzzleClassNames()) { @@ -109,12 +92,6 @@ public List getPuzzleNames() { return names; } - /** - * Returns a list of the display names of the puzzles that can have files created and edited within - * the proof editor - * - * @return a List of puzzle display names as Strings with file creation enabled - */ public List getFileCreationEnabledPuzzleNames() { List names = new LinkedList(); for (String puzzle : this.getFileCreationEnabledPuzzles()) { diff --git a/src/main/java/edu/rpi/legup/app/GameBoardFacade.java b/src/main/java/edu/rpi/legup/app/GameBoardFacade.java index 835041c3c..c928c1209 100644 --- a/src/main/java/edu/rpi/legup/app/GameBoardFacade.java +++ b/src/main/java/edu/rpi/legup/app/GameBoardFacade.java @@ -31,10 +31,6 @@ import org.w3c.dom.Node; import org.xml.sax.SAXException; -/** - * {@code GameBoardFacade} is a class designed to manage the game board operations within the application. - * It integrates various components such as UI elements, puzzle management, and history tracking - */ public class GameBoardFacade implements IHistorySubject { private static final Logger LOGGER = LogManager.getLogger(GameBoardFacade.class.getName()); @@ -76,9 +72,6 @@ public static synchronized GameBoardFacade getInstance() { return instance; } - /** - * Initializes the UI components - */ public void initializeUI() { EventQueue.invokeLater( () -> { @@ -90,29 +83,18 @@ public void initializeUI() { }); } - /** - * Sets the current puzzle in the game board - * - * @param puzzle the Puzzle to set - */ public void setPuzzle(Puzzle puzzle) { this.puzzle = puzzle; this.puzzleSolver.setPuzzleView(puzzle); this.history.clear(); } - /** - * Clears the current puzzle - */ public void clearPuzzle() { this.puzzle = null; this.curFileName = null; this.history.clear(); } - /** - * Sets up the configuration by initializing the Config object - */ public static void setupConfig() { Config config = null; try { @@ -123,21 +105,11 @@ public static void setupConfig() { GameBoardFacade.getInstance().setConfig(config); } - /** - * Sets the current puzzle editor with the given puzzle - * - * @param puzzle the Puzzle to set in the editor - */ public void setPuzzleEditor(Puzzle puzzle) { this.puzzle = puzzle; this.puzzleEditor.setPuzzleView(puzzle); } - /** - * Sets the configuration object for the GameBoardFacade - * - * @param config config the Config object to set - */ public void setConfig(Config config) { this.config = config; } @@ -196,62 +168,53 @@ public boolean validateTextInput(String game, String[] statements) throws Runtim } /** - * Loads an empty puzzle with the specified dimensions + * Loads an empty puzzle * * @param game name of the puzzle * @param rows the number of rows on the board * @param columns the number of columns on the board */ public void loadPuzzle(String game, int rows, int columns) throws RuntimeException { - if (!game.equals("")) { - String qualifiedClassName = config.getPuzzleClassForName(game); - LOGGER.debug("Loading " + qualifiedClassName); + String qualifiedClassName = config.getPuzzleClassForName(game); + LOGGER.debug("Loading " + qualifiedClassName); - try { - Class c = Class.forName(qualifiedClassName); - Constructor cons = c.getConstructor(); - Puzzle puzzle = (Puzzle) cons.newInstance(); + try { + Class c = Class.forName(qualifiedClassName); + Constructor cons = c.getConstructor(); + Puzzle puzzle = (Puzzle) cons.newInstance(); - PuzzleImporter importer = puzzle.getImporter(); - if (importer == null) { - LOGGER.error("Puzzle importer is null"); - throw new RuntimeException("Puzzle importer null"); - } + PuzzleImporter importer = puzzle.getImporter(); + if (importer == null) { + LOGGER.error("Puzzle importer is null"); + throw new RuntimeException("Puzzle importer null"); + } - // Theoretically, this exception should never be thrown, since LEGUP should not be - // allowing the user to give row/column input for a puzzle that doesn't support it - if (!importer.acceptsRowsAndColumnsInput()) { - throw new IllegalArgumentException( - puzzle.getName() + " does not accept rows and columns input"); - } + // Theoretically, this exception should never be thrown, since LEGUP should not be + // allowing the user to give row/column input for a puzzle that doesn't support it + if (!importer.acceptsRowsAndColumnsInput()) { + throw new IllegalArgumentException( + puzzle.getName() + " does not accept rows and columns input"); + } - setWindowTitle(puzzle.getName(), "New " + puzzle.getName() + " Puzzle"); - importer.initializePuzzle(rows, columns); + setWindowTitle(puzzle.getName(), "New " + puzzle.getName() + " Puzzle"); + importer.initializePuzzle(rows, columns); - puzzle.initializeView(); - // - // puzzle.getBoardView().onTreeElementChanged(puzzle.getTree().getRootNode()); - setPuzzleEditor(puzzle); - } catch (IllegalArgumentException exception) { - throw new IllegalArgumentException(exception.getMessage()); - } catch (ClassNotFoundException - | NoSuchMethodException - | InvocationTargetException - | IllegalAccessException - | InstantiationException e) { - LOGGER.error(e); - throw new RuntimeException("Puzzle creation error"); - } + puzzle.initializeView(); + // + // puzzle.getBoardView().onTreeElementChanged(puzzle.getTree().getRootNode()); + setPuzzleEditor(puzzle); + } catch (IllegalArgumentException exception) { + throw new IllegalArgumentException(exception.getMessage()); + } catch (ClassNotFoundException + | NoSuchMethodException + | InvocationTargetException + | IllegalAccessException + | InstantiationException e) { + LOGGER.error(e); + throw new RuntimeException("Puzzle creation error"); } - } - /** - * Loads an empty puzzle with the specified input - * - * @param game name of the puzzle - * @param statements an array of statements to load the puzzle with - */ public void loadPuzzle(String game, String[] statements) { String qualifiedClassName = config.getPuzzleClassForName(game); LOGGER.debug("Loading " + qualifiedClassName); @@ -294,10 +257,10 @@ public void loadPuzzle(String game, String[] statements) { } /** - * Loads a puzzle file from the specified file + * Loads a puzzle file * * @param fileName file name of the board file - * @throws InvalidFileFormatException if the file format is invalid or if the file cannot be created + * @throws InvalidFileFormatException if input is invalid */ public void loadPuzzle(String fileName) throws InvalidFileFormatException { try { @@ -310,12 +273,6 @@ public void loadPuzzle(String fileName) throws InvalidFileFormatException { } } - /** - * Loads a puzzle into the editor from the specified file name - * - * @param fileName the name of the file to load - * @throws InvalidFileFormatException if the file format is invalid or if the file cannot be created - */ public void loadPuzzleEditor(String fileName) throws InvalidFileFormatException { try { loadPuzzleEditor(new FileInputStream(fileName)); @@ -327,12 +284,6 @@ public void loadPuzzleEditor(String fileName) throws InvalidFileFormatException } } - /** - * Loads a puzzle into the editor from the specified input stream - * - * @param inputStream the input stream to load the puzzle from - * @throws InvalidFileFormatException if the input stream cannot be processed or the file format is invalid - */ public void loadPuzzleEditor(InputStream inputStream) throws InvalidFileFormatException { Document document; try { diff --git a/src/main/java/edu/rpi/legup/app/InvalidConfigException.java b/src/main/java/edu/rpi/legup/app/InvalidConfigException.java index c66b23ec2..a570e7d44 100644 --- a/src/main/java/edu/rpi/legup/app/InvalidConfigException.java +++ b/src/main/java/edu/rpi/legup/app/InvalidConfigException.java @@ -1,14 +1,6 @@ package edu.rpi.legup.app; -/** - * {@code InvalidConfigException} is a custom exception class for handling invalid configuration errors - */ public class InvalidConfigException extends Exception { - /** - * Constructs a new InvalidConfigException with the specified detail message - * - * @param message the detail message - */ public InvalidConfigException(String message) { super(message); } diff --git a/src/main/java/edu/rpi/legup/app/LegupPreferences.java b/src/main/java/edu/rpi/legup/app/LegupPreferences.java index 03b8be8d9..12433d7e4 100644 --- a/src/main/java/edu/rpi/legup/app/LegupPreferences.java +++ b/src/main/java/edu/rpi/legup/app/LegupPreferences.java @@ -4,10 +4,6 @@ import java.util.Map; import java.util.prefs.Preferences; -/** - * {@code LegupPreferences} is a class responsible for managing user preferences within the application. - * It uses Java's Preferences API to store and retrieve preferences, and it provides methods for accessing and updating these preferences. - */ public class LegupPreferences { private static LegupPreferences instance; @@ -77,10 +73,9 @@ public class LegupPreferences { } /** - * Gets the legup preferences singleton instance - * This method ensures that only one instance of LegupPreferences exists + * Gets the legup preferences singleton instance. * - * @return the singleton instance of LegupPreferences + * @return legup preferences */ public static LegupPreferences getInstance() { if (instance == null) { @@ -89,40 +84,30 @@ public static LegupPreferences getInstance() { return instance; } - /** - * Private constructor to prevent instantiation from outside the class - * Use {@link #getInstance()} to access the singleton instance - */ + /** Private LegupPreferences Singleton Constructor */ private LegupPreferences() {} /** * Gets the user preference by the string key * * @param key key name of the preference - * @return value of the preference or {@code null} if the preference does not exist + * @return value of the preference */ public String getUserPref(String key) { return preferencesMap.get(key); } /** - * Sets the user preference for the specified key to the provided value + * Gets the user preference by the string key, value pair * - * @param key key to set for the preference - * @param value value to set for the preference + * @param key key name of the preference + * @param value value of the preference */ public void setUserPref(String key, String value) { preferences.put(key, value); preferencesMap.put(key, value); } - /** - * Retrieves the user preference associated with the specified key as a boolean - * - * @param key the key for the preference to retrieve - * @return the boolean value of the preference - * @throws RuntimeException if the preference value cannot be interpreted as a boolean - */ public boolean getUserPrefAsBool(String key) { if (preferencesMap.get(key).equalsIgnoreCase(Boolean.toString(true))) { return true; @@ -135,20 +120,10 @@ public boolean getUserPrefAsBool(String key) { } } - /** - * Gets the saved path - * - * @return the saved path as a String - */ public String getSavedPath() { return SAVED_PATH; } - /** - * Sets the saved path to the specified value - * - * @param path the new saved path - */ public void setSavedPath(String path) { SAVED_PATH = path; } diff --git a/src/main/java/edu/rpi/legup/controller/Controller.java b/src/main/java/edu/rpi/legup/controller/Controller.java index 47ef70e17..57ce107ac 100644 --- a/src/main/java/edu/rpi/legup/controller/Controller.java +++ b/src/main/java/edu/rpi/legup/controller/Controller.java @@ -5,10 +5,6 @@ import java.awt.event.*; import javax.swing.*; -/** - * {@code Controller} is an abstract class designed to handle various mouse events and provide control functionality for a {@code ScrollView}. - * It implements several mouse event interfaces to manage interactions such as panning and zooming within a {@code ScrollView} - */ public abstract class Controller implements MouseMotionListener, MouseListener, MouseWheelListener { protected ScrollView viewer; private int x, y; @@ -23,11 +19,6 @@ public Controller() { pan = false; } - /** - * Sets the ScrollView instance that this controller manages - * - * @param viewer The ScrollView instance to be set - */ public void setViewer(ScrollView viewer) { this.viewer = viewer; } diff --git a/src/main/java/edu/rpi/legup/controller/CursorController.java b/src/main/java/edu/rpi/legup/controller/CursorController.java index 6a3ab018d..2706bd522 100644 --- a/src/main/java/edu/rpi/legup/controller/CursorController.java +++ b/src/main/java/edu/rpi/legup/controller/CursorController.java @@ -6,10 +6,6 @@ import java.util.Timer; import java.util.TimerTask; -/** - * {@code CursorController} provides functionality for managing the cursor appearance during actions that take a certain amount of time. - * It allows for the display of a busy cursor while an action is being processed and reverts to the default cursor afterward - */ public class CursorController { public static final Cursor BUSY_CURSOR = new Cursor(Cursor.WAIT_CURSOR); public static final Cursor DEFAULT_CURSOR = new Cursor(Cursor.DEFAULT_CURSOR); diff --git a/src/main/java/edu/rpi/legup/controller/EditorElementController.java b/src/main/java/edu/rpi/legup/controller/EditorElementController.java index 040610137..5a23885af 100644 --- a/src/main/java/edu/rpi/legup/controller/EditorElementController.java +++ b/src/main/java/edu/rpi/legup/controller/EditorElementController.java @@ -9,10 +9,6 @@ import java.awt.event.ActionListener; import javax.swing.*; -/** - * {@code EditorElementController} manages actions related to UI elements within the puzzle editor environment. - * It handles button presses, updates element selection, and manages visual states of buttons - */ public class EditorElementController implements ActionListener { protected Object lastSource; protected ElementController elementController; @@ -24,33 +20,19 @@ public EditorElementController() { prevButton = null; } - /** - * Sets the ElementController instance for this controller - * - * @param elementController the ElementController instance to be set - */ public void setElementController(ElementController elementController) { this.elementController = elementController; } - /** - * Handles the event when a button associated with an Element is pressed - * - * @param element the Element associated with the button that was pressed - */ public void buttonPressed(Element element) { // TODO: implement what happens when element is pressed + System.out.printf("%s button pressed!\n", element.getElementName()); if (elementController != null) { elementController.setSelectedElement(element); } } - /** - * Handles action events triggered by buttons - * - * @param e the event to be processed - */ @Override public void actionPerformed(ActionEvent e) { lastSource = e.getSource(); diff --git a/src/main/java/edu/rpi/legup/controller/ElementController.java b/src/main/java/edu/rpi/legup/controller/ElementController.java index 436b078b9..5840650e1 100644 --- a/src/main/java/edu/rpi/legup/controller/ElementController.java +++ b/src/main/java/edu/rpi/legup/controller/ElementController.java @@ -26,17 +26,13 @@ import java.awt.*; import java.awt.event.*; -/** - * The ElementController class manages UI interactions related to elements in a {@link BoardView}. - * It handles mouse events, key events, and actions related to element selection and manipulation - */ public class ElementController implements MouseListener, MouseMotionListener, ActionListener, KeyListener { protected BoardView boardView; private Element selectedElement; /** - * ElementController Constructor controller to handle ui events associated interacting with a + * ElementController Constructor controller to handles ui events associated interacting with a * {@link BoardView} */ public ElementController() { @@ -90,7 +86,6 @@ public void mouseReleased(MouseEvent e) { if (boardView == null) { boardView = getInstance().getLegupUI().getEditorBoardView(); } - Board board = boardView.getBoard(); ElementView elementView = boardView.getElement(e.getPoint()); TreeViewSelection selection = null; @@ -142,8 +137,17 @@ public void mouseReleased(MouseEvent e) { if (this.boardView.getBoard() instanceof TreeTentBoard) { scaledPoint.setLocation(scaledPoint.getX() - 1, scaledPoint.getY() - 1); } - + System.out.printf( + "selected Element is NOT null, attempting to change board at (%d, %d)\n", + scaledPoint.x, scaledPoint.y); + // System.out.println("Before: " + b.getCell(scaledPoint.x, + // scaledPoint.y).getData()); b.setCell(scaledPoint.x, scaledPoint.y, this.selectedElement, e); + // System.out.println("After: " + b.getCell(scaledPoint.x, + // scaledPoint.y).getData()); + // } else { + // System.out.println("selected Element is null!"); + // } boardView.repaint(); } diff --git a/src/main/java/edu/rpi/legup/controller/RuleController.java b/src/main/java/edu/rpi/legup/controller/RuleController.java index 491e02f74..6e88dc4be 100644 --- a/src/main/java/edu/rpi/legup/controller/RuleController.java +++ b/src/main/java/edu/rpi/legup/controller/RuleController.java @@ -16,11 +16,6 @@ import java.awt.event.ActionListener; import java.util.List; -/** - * The RuleController class is responsible for handling UI events related to rule buttons - * in the RulePanel of the Legup application. It implements ActionListener to process action - * events triggered by rule buttons and applies rules to the puzzle accordingly - */ public class RuleController implements ActionListener { protected Object lastSource; diff --git a/src/main/java/edu/rpi/legup/controller/TreeController.java b/src/main/java/edu/rpi/legup/controller/TreeController.java index 2b12268ac..80fdee1af 100644 --- a/src/main/java/edu/rpi/legup/controller/TreeController.java +++ b/src/main/java/edu/rpi/legup/controller/TreeController.java @@ -11,10 +11,6 @@ import java.awt.event.MouseWheelEvent; import javax.swing.*; -/** - * The TreeController class handles UI events from a TreePanel. - * It extends the Controller class to provide specific behavior for tree interactions - */ public class TreeController extends Controller { /** * TreeController Constructor creates a controller object to listen to ui events from a {@link diff --git a/src/main/java/edu/rpi/legup/history/AddTreeElementCommand.java b/src/main/java/edu/rpi/legup/history/AddTreeElementCommand.java index 92a887f71..12b39cd85 100644 --- a/src/main/java/edu/rpi/legup/history/AddTreeElementCommand.java +++ b/src/main/java/edu/rpi/legup/history/AddTreeElementCommand.java @@ -10,11 +10,6 @@ import java.util.List; import java.util.Map; - -/** - * The AddTreeElementCommand class represents a command to add tree elements to the proof tree. - * It extends the PuzzleCommand class to handle the addition of tree elements and undo operation. - */ public class AddTreeElementCommand extends PuzzleCommand { private TreeViewSelection selection; @@ -32,10 +27,7 @@ public AddTreeElementCommand(TreeViewSelection selection) { this.addChild = new HashMap<>(); } - /** - * Executes the command to add selected tree elements to the tree. - * Updates the puzzle and tree view accordingly - */ + /** Executes an command */ @Override public void executeCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); @@ -103,10 +95,7 @@ public String getErrorString() { return null; } - /** - * Undoes the command by removing the added tree elements. - * Updates the puzzle and tree view accordingly - */ + /** Undoes an command */ @Override public void undoCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); diff --git a/src/main/java/edu/rpi/legup/history/ApplyDefaultDirectRuleCommand.java b/src/main/java/edu/rpi/legup/history/ApplyDefaultDirectRuleCommand.java index ad89ab636..02dffae44 100644 --- a/src/main/java/edu/rpi/legup/history/ApplyDefaultDirectRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ApplyDefaultDirectRuleCommand.java @@ -10,11 +10,6 @@ import java.util.List; import java.util.Map; -/** - * The ApplyDefaultDirectRuleCommand class represents a command to apply a default direct rule - * to selected tree nodes in the proof tree. - * It extends the PuzzleCommand class to handle rule application and undo operation. - */ public class ApplyDefaultDirectRuleCommand extends PuzzleCommand { private TreeViewSelection selection; @@ -74,10 +69,7 @@ public String getErrorString() { return null; } - /** - * Executes the command to apply the default rule to the selected tree nodes. - * Updates the puzzle and tree view accordingly. - */ + /** Executes an command */ @Override public void executeCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); @@ -117,10 +109,7 @@ public void executeCommand() { puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); } - /** - * Undoes the command by removing the applied default rule from the tree nodes. - * Updates the puzzle and tree view accordingly. - */ + /** Undoes an command */ @Override public void undoCommand() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); diff --git a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java index b86cda6ea..97192e145 100644 --- a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java @@ -13,11 +13,6 @@ import java.awt.event.MouseEvent; import java.util.*; -/** - * The AutoCaseRuleCommand class represents a command to automatically apply a case rule to a - * selected tree node in the proof tree. - * It extends the PuzzleCommand class to handle case rule application and undo operation. - */ public class AutoCaseRuleCommand extends PuzzleCommand { private ElementView elementView; @@ -51,10 +46,7 @@ public AutoCaseRuleCommand( this.caseTrans = new ArrayList<>(); } - /** - * Executes the command to apply the case rule to the selected tree node. - * Updates the puzzle and tree view accordingly. - */ + /** Executes an command */ @Override public void executeCommand() { Tree tree = getInstance().getTree(); @@ -64,10 +56,11 @@ public void executeCommand() { TreeNode node = (TreeNode) selection.getFirstSelection().getTreeElement(); if (caseTrans.isEmpty()) { - List cases = caseRule.getCases(caseBoard.getBaseBoard(), elementView.getPuzzleElement()); + List cases = + caseRule.getCases(caseBoard.getBaseBoard(), elementView.getPuzzleElement()); for (Board board : cases) { final TreeTransition transition = (TreeTransition) tree.addTreeElement(node); - //board.setModifiable(false); + board.setModifiable(false); transition.setBoard(board); transition.setRule(caseRule); transition.setSelection(elementView.getPuzzleElement().copy()); @@ -118,11 +111,13 @@ public String getErrorString() { return "The selected data element is not pickable with this case rule."; } - if (caseRule.getCases(caseBoard.getBaseBoard(), elementView.getPuzzleElement()).size() == 0) { + if (caseRule.getCases(caseBoard.getBaseBoard(), elementView.getPuzzleElement()).size() + == 0) { return "The selection must produce at least one case"; } - int numberOfCaseRules = caseRule.getCases(caseBoard.getBaseBoard(), elementView.getPuzzleElement()).size(); + int numberOfCaseRules = + caseRule.getCases(caseBoard.getBaseBoard(), elementView.getPuzzleElement()).size(); System.out.println("Number of cases:" + numberOfCaseRules); if (numberOfCaseRules > caseRule.MAX_CASES) { return "The selection can produce a max of " + caseRule.MAX_CASES + " cases"; @@ -134,10 +129,7 @@ public String getErrorString() { return null; } - /** - * Undoes the command by removing the applied case rules from the tree node. - * Updates the puzzle and tree view accordingly - */ + /** Undoes an command */ @Override public void undoCommand() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); diff --git a/src/main/java/edu/rpi/legup/history/CommandError.java b/src/main/java/edu/rpi/legup/history/CommandError.java index 26959cccd..35b7bb15b 100644 --- a/src/main/java/edu/rpi/legup/history/CommandError.java +++ b/src/main/java/edu/rpi/legup/history/CommandError.java @@ -1,16 +1,10 @@ package edu.rpi.legup.history; -/** - * The CommandError enum represents various error conditions that can occur when executing or - * validating commands related to tree elements in the proof tree. - * Each error condition is associated with a descriptive message. - */ public enum CommandError { NO_SELECTED_VIEWS("The selection does not have any tree elements."), ONE_SELECTED_VIEW("The selection must have exactly one tree element."), UNMODIFIABLE_BOARD("The selection contains a board which is not modifiable."), UNMODIFIABLE_DATA("The selection contains a board where the data element is not modifiable."), - UNMODIFIABLE_DATA_CASE_RULE("The proof tree contains a future case rule, causing previous transitions to become unmodifiable."), CONTAINS_ROOT("The selection contains the root tree node."), ONE_CHILD("The selection contains a tree node that does not have exactly one child."), ADD_WITH_CHILD("The selection contains a tree transition that already has a child tree node."), @@ -24,20 +18,10 @@ public enum CommandError { private String value; - /** - * Constructs a CommandError with the specified error message - * - * @param value The error message associated with the command error - */ CommandError(String value) { this.value = value; } - /** - * Returns the error message associated with this CommandError - * - * @return The error message - */ @Override public String toString() { return value; diff --git a/src/main/java/edu/rpi/legup/history/CommandState.java b/src/main/java/edu/rpi/legup/history/CommandState.java index 326490e28..f47c0405d 100644 --- a/src/main/java/edu/rpi/legup/history/CommandState.java +++ b/src/main/java/edu/rpi/legup/history/CommandState.java @@ -1,9 +1,5 @@ package edu.rpi.legup.history; -/** - * The CommandState enum represents the various states that a command can be in during its lifecycle. - * Each state is associated with a descriptive name. - */ public enum CommandState { CREATED("Created"), EXECUTED("Executed"), @@ -12,20 +8,10 @@ public enum CommandState { private String value; - /** - * Constructs a CommandState with the specified state name - * - * @param value The name associated with the command state - */ CommandState(String value) { this.value = value; } - /** - * Returns the name associated with this CommandState - * - * @return The state name - */ @Override public String toString() { return value; diff --git a/src/main/java/edu/rpi/legup/history/DeleteTreeElementCommand.java b/src/main/java/edu/rpi/legup/history/DeleteTreeElementCommand.java index e82197898..0469685c1 100644 --- a/src/main/java/edu/rpi/legup/history/DeleteTreeElementCommand.java +++ b/src/main/java/edu/rpi/legup/history/DeleteTreeElementCommand.java @@ -7,11 +7,6 @@ import edu.rpi.legup.ui.proofeditorui.treeview.*; import java.util.List; -/** - * The DeleteTreeElementCommand class represents a command to delete tree elements from a proof tree. - * It extends PuzzleCommand and implements the functionality to remove selected tree elements and - * handle undo operations. - */ public class DeleteTreeElementCommand extends PuzzleCommand { private TreeViewSelection selection; @@ -25,41 +20,35 @@ public DeleteTreeElementCommand(TreeViewSelection selection) { this.selection = selection.copy(); } - /** - * Executes the delete command, removing the selected tree elements from the tree. - */ + /** Executes an command */ @Override public void executeCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); List selectedViews = selection.getSelectedViews(); - if (selectedViews.isEmpty()) { - return; - } TreeElementView firstSelectedView = selectedViews.get(0); TreeElementView newSelectedView; if (firstSelectedView.getType() == TreeElementType.NODE) { - //System.out.println("FIRST SELECTION NODE, total selection views: " + selectedViews.size()); TreeNodeView nodeView = (TreeNodeView) firstSelectedView; newSelectedView = nodeView.getParentView(); } else { - //System.out.println("FIRST SELECTION TRANS, total selection views: " + selectedViews.size()); TreeTransitionView transitionView = (TreeTransitionView) firstSelectedView; newSelectedView = transitionView.getParentViews().get(0); } for (TreeElementView selectedView : selectedViews) { - System.out.println("DELETED"); TreeElement element = selectedView.getTreeElement(); tree.removeTreeElement(element); puzzle.notifyTreeListeners(listener -> listener.onTreeElementRemoved(element)); } final TreeViewSelection newSelection = new TreeViewSelection(newSelectedView); - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(newSelectedView.getTreeElement())); - puzzle.notifyTreeListeners((ITreeListener listener) -> listener.onTreeSelectionChanged(newSelection)); + puzzle.notifyBoardListeners( + listener -> listener.onTreeElementChanged(newSelectedView.getTreeElement())); + puzzle.notifyTreeListeners( + (ITreeListener listener) -> listener.onTreeSelectionChanged(newSelection)); } /** @@ -84,9 +73,7 @@ public String getErrorString() { return null; } - /** - * Undoes the delete command, re-adding the previously deleted tree elements. - */ + /** Undoes an command */ @Override public void undoCommand() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); diff --git a/src/main/java/edu/rpi/legup/history/EditDataCommand.java b/src/main/java/edu/rpi/legup/history/EditDataCommand.java index a629aa085..d65f03d66 100644 --- a/src/main/java/edu/rpi/legup/history/EditDataCommand.java +++ b/src/main/java/edu/rpi/legup/history/EditDataCommand.java @@ -9,19 +9,10 @@ import edu.rpi.legup.model.tree.*; import edu.rpi.legup.ui.boardview.BoardView; import edu.rpi.legup.ui.boardview.ElementView; -import edu.rpi.legup.ui.lookandfeel.materialdesign.MaterialColors; import edu.rpi.legup.ui.proofeditorui.treeview.*; - -import javax.swing.*; -import java.awt.*; import java.awt.event.MouseEvent; import java.util.List; -/** - * The EditDataCommand class represents a command to edit the data of a puzzle element within - * a tree transition. It extends PuzzleCommand and provides functionality to execute and undo - * changes made to puzzle elements. - */ public class EditDataCommand extends PuzzleCommand { private TreeTransition transition; private PuzzleElement savePuzzleElement; @@ -35,7 +26,7 @@ public class EditDataCommand extends PuzzleCommand { * EditDataCommand Constructor create a puzzle command for editing a board * * @param elementView currently selected puzzle puzzleElement view that is being edited - * @param selection currently selected tree puzzleElement views that are being edited + * @param selection currently selected tree puzzleElement views that is being edited * @param event mouse event */ public EditDataCommand(ElementView elementView, TreeViewSelection selection, MouseEvent event) { @@ -47,9 +38,7 @@ public EditDataCommand(ElementView elementView, TreeViewSelection selection, Mou this.transition = null; } - /** - * Executes the edit data command, modifying the puzzle element and propagating changes - */ + /** Executes a command */ @SuppressWarnings("unchecked") @Override public void executeCommand() { @@ -65,13 +54,16 @@ public void executeCommand() { if (treeElement.getType() == TreeElementType.NODE) { TreeNode treeNode = (TreeNode) treeElement; + if (treeNode.getChildren().isEmpty()) { if (transition == null) { transition = tree.addNewTransition(treeNode); } puzzle.notifyTreeListeners(listener -> listener.onTreeElementAdded(transition)); } + board = transition.getBoard(); + puzzleElement = board.getPuzzleElement(selectedPuzzleElement); savePuzzleElement = puzzleElement.copy(); } else { @@ -81,6 +73,7 @@ public void executeCommand() { } Board prevBoard = transition.getParents().get(0).getBoard(); + boardView.getElementController().changeCell(event, puzzleElement); if (prevBoard.getPuzzleElement(selectedPuzzleElement).equalsData(puzzleElement)) { @@ -109,54 +102,35 @@ public void executeCommand() { public String getErrorString() { List selectedViews = selection.getSelectedViews(); if (selectedViews.size() != 1) { - flashTreeViewRed(); return CommandError.ONE_SELECTED_VIEW.toString(); } TreeElementView selectedView = selection.getFirstSelection(); Board board = selectedView.getTreeElement().getBoard(); PuzzleElement selectedPuzzleElement = elementView.getPuzzleElement(); if (selectedView.getType() == TreeElementType.NODE) { + TreeNodeView nodeView = (TreeNodeView) selectedView; if (!nodeView.getChildrenViews().isEmpty()) { - flashTreeViewRed(); return CommandError.UNMODIFIABLE_BOARD.toString(); - } else if (!board.getPuzzleElement(selectedPuzzleElement).isModifiable()) { - flashTreeViewRed(); - return CommandError.UNMODIFIABLE_DATA.toString(); + } else { + if (!board.getPuzzleElement(selectedPuzzleElement).isModifiable()) { + return CommandError.UNMODIFIABLE_DATA.toString(); + } } } else { TreeTransitionView transitionView = (TreeTransitionView) selectedView; if (!transitionView.getTreeElement().getBoard().isModifiable()) { - flashTreeViewRed(); return CommandError.UNMODIFIABLE_BOARD.toString(); } else { if (!board.getPuzzleElement(selectedPuzzleElement).isModifiable()) { - flashTreeViewRed(); return CommandError.UNMODIFIABLE_DATA.toString(); - } else if (!board.getPuzzleElement(selectedPuzzleElement).isModifiableCaseRule()) { - flashTreeViewRed(); - return CommandError.UNMODIFIABLE_DATA_CASE_RULE.toString(); } } } return null; } - /** - * Causes the TreeView background to flash red for a short duration when an error occurs. - */ - private void flashTreeViewRed() { - TreeView treeView = getInstance().getLegupUI().getTreePanel().getTreeView(); - Color originalColor = treeView.getBackground(); - treeView.setBackground(MaterialColors.RED_700); - Timer timer = new Timer(400, e -> treeView.setBackground(originalColor)); - timer.setRepeats(false); - timer.start(); - } - - /** - * Undoes the edit data command, restoring the previous state of the puzzle element. - */ + /** Undoes an command */ @SuppressWarnings("unchecked") @Override public void undoCommand() { diff --git a/src/main/java/edu/rpi/legup/history/History.java b/src/main/java/edu/rpi/legup/history/History.java index b244e8f88..371284f8c 100644 --- a/src/main/java/edu/rpi/legup/history/History.java +++ b/src/main/java/edu/rpi/legup/history/History.java @@ -6,10 +6,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * The History class manages a stack of commands for undo and redo operations on the board and tree structure. - * It maintains a list of commands and a current index to track the position in the history stack. - */ public class History { private static final Logger LOGGER = LogManager.getLogger(History.class.getName()); @@ -18,8 +14,9 @@ public class History { private int curIndex; /** - * Constructs a History object to keep track of changes and allow undo and redo operations. - * The history is implemented as a stack, with curIndex pointing to the top of the stack. + * History Constructor this holds information about changes to the board and Tree structure for + * undoing and redoing operations. Though history is an List, it is implemented like a stack. + * The curIndex points to the top of the stack (where the last change was made). */ public History() { history = new ArrayList<>(); @@ -47,18 +44,13 @@ public void pushChange(ICommand command) { } } - /** - * Undoes the last action by calling the undo method of the command at the current index. - * Updates the current index and notifies listeners. - */ + /** Undoes an action */ public void undo() { synchronized (lock) { if (curIndex > -1) { ICommand command = history.get(curIndex--); command.undo(); LOGGER.info("Undoed " + command.getClass().getSimpleName()); - - GameBoardFacade.getInstance() .notifyHistoryListeners( l -> l.onUndo(curIndex < 0, curIndex == history.size() - 1)); @@ -66,10 +58,7 @@ public void undo() { } } - /** - * Redoes the next action by calling the redo method of the command at the current index. - * Updates the current index and notifies listeners. - */ + /** Redoes an action */ public void redo() { synchronized (lock) { if (curIndex < history.size() - 1) { @@ -83,9 +72,7 @@ public void redo() { } } - /** - * Clears all actions from the history stack and resets the current index - */ + /** Clears all actions from the history stack */ public void clear() { history.clear(); curIndex = -1; diff --git a/src/main/java/edu/rpi/legup/history/ICommand.java b/src/main/java/edu/rpi/legup/history/ICommand.java index 4f867dbab..913d9daaf 100644 --- a/src/main/java/edu/rpi/legup/history/ICommand.java +++ b/src/main/java/edu/rpi/legup/history/ICommand.java @@ -1,13 +1,7 @@ package edu.rpi.legup.history; -/** - * The ICommand interface defines the structure for command objects in a command pattern. - * It provides methods to execute, undo, redo commands, and to check if a command can be executed. - */ public interface ICommand { - /** - * Executes the command. The specific behavior depends on the implementation - */ + /** Executes a command */ void execute(); /** @@ -25,13 +19,9 @@ public interface ICommand { */ String getError(); - /** - * Undoes the command. Reverts the changes made by the execute method - */ + /** Undoes a command */ void undo(); - /** - * Redoes the command. Re-applies the changes made by the execute method after undoing - */ + /** Redoes a command */ void redo(); } diff --git a/src/main/java/edu/rpi/legup/history/IHistoryListener.java b/src/main/java/edu/rpi/legup/history/IHistoryListener.java index 48690168d..f464941d6 100644 --- a/src/main/java/edu/rpi/legup/history/IHistoryListener.java +++ b/src/main/java/edu/rpi/legup/history/IHistoryListener.java @@ -1,21 +1,15 @@ package edu.rpi.legup.history; -/** - * The IHistoryListener interface defines methods for listening to changes in the history of commands. - * Implementations of this interface can respond to events related to command history such as pushing, - * undoing, redoing commands, and clearing the history. - */ public interface IHistoryListener { - /** - * Called when a command is pushed onto the history stack. + * Called when a action is pushed onto the edu.rpi.legup.history stack * - * @param command the command that was pushed onto the stack + * @param command action to push onto the stack */ void onPushChange(ICommand command); /** - * Called when a command is undone. + * Called when an action is undone * * @param isBottom true if there are no more actions to undo, false otherwise * @param isTop true if there are no more changes to redo, false otherwise @@ -23,15 +17,13 @@ public interface IHistoryListener { void onUndo(boolean isBottom, boolean isTop); /** - * Called when a command is redone. + * Called when an action is redone * * @param isBottom true if there are no more actions to undo, false otherwise * @param isTop true if there are no more changes to redo, false otherwise */ void onRedo(boolean isBottom, boolean isTop); - /** - * Called when the history stack is cleared. - */ + /** Called when the edu.rpi.legup.history is cleared */ void onClearHistory(); } diff --git a/src/main/java/edu/rpi/legup/history/IHistorySubject.java b/src/main/java/edu/rpi/legup/history/IHistorySubject.java index 431adbb93..78fefff00 100644 --- a/src/main/java/edu/rpi/legup/history/IHistorySubject.java +++ b/src/main/java/edu/rpi/legup/history/IHistorySubject.java @@ -2,31 +2,25 @@ import java.util.function.Consumer; -/** - * The IHistorySubject interface defines methods for managing and notifying listeners - * about changes in the command history. Implementations of this interface can add, remove, - * and notify history listeners. - */ public interface IHistorySubject { - /** - * Adds a history listener to receive updates about changes in the command history. + * Adds a history listener * - * @param listener the listener to add + * @param listener listener to add */ void addHistoryListener(IHistoryListener listener); /** - * Removes a history listener, so it no longer receives updates about changes in the command history. + * Removes a history listener * - * @param listener the listener to remove + * @param listener listener to remove */ void removeHistoryListener(IHistoryListener listener); /** - * Notifies all registered listeners about a change in the command history. + * Notifies listeners * - * @param algorithm a Consumer function that takes an IHistoryListener and performs some action with it + * @param algorithm algorithm to notify the listeners with */ void notifyHistoryListeners(Consumer algorithm); } diff --git a/src/main/java/edu/rpi/legup/history/InvalidCommandStateTransition.java b/src/main/java/edu/rpi/legup/history/InvalidCommandStateTransition.java index 201babb9e..71d072328 100644 --- a/src/main/java/edu/rpi/legup/history/InvalidCommandStateTransition.java +++ b/src/main/java/edu/rpi/legup/history/InvalidCommandStateTransition.java @@ -1,18 +1,7 @@ package edu.rpi.legup.history; -/** - * The InvalidCommandStateTransition exception is thrown when an invalid state transition - * is attempted on a PuzzleCommand - */ public class InvalidCommandStateTransition extends RuntimeException { - /** - * Constructs a new InvalidCommandStateTransition exception with a detailed message - * - * @param puzzleCommand the PuzzleCommand involved in the invalid transition - * @param from the state from which the transition was attempted - * @param to the state to which the transition was attempted - */ public InvalidCommandStateTransition( PuzzleCommand puzzleCommand, CommandState from, CommandState to) { super( diff --git a/src/main/java/edu/rpi/legup/history/MergeCommand.java b/src/main/java/edu/rpi/legup/history/MergeCommand.java index c2851bced..f234a0884 100644 --- a/src/main/java/edu/rpi/legup/history/MergeCommand.java +++ b/src/main/java/edu/rpi/legup/history/MergeCommand.java @@ -10,10 +10,6 @@ import java.util.List; import java.util.Set; -/** - * The MergeCommand class represents a command to merge selected tree nodes into a single node - * and create a transition to represent the merge - */ public class MergeCommand extends PuzzleCommand { private TreeViewSelection selection; private TreeTransition transition; @@ -28,9 +24,7 @@ public MergeCommand(TreeViewSelection selection) { this.transition = null; } - /** - * Executes the merge command - */ + /** Executes an command */ @Override public void executeCommand() { List selectedViews = selection.getSelectedViews(); @@ -77,9 +71,7 @@ public void executeCommand() { puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); } - /** - * Undoes the merge command - */ + /** Undoes an command */ @Override public void undoCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); diff --git a/src/main/java/edu/rpi/legup/history/PuzzleCommand.java b/src/main/java/edu/rpi/legup/history/PuzzleCommand.java index d4385b3e8..3768e3cbd 100644 --- a/src/main/java/edu/rpi/legup/history/PuzzleCommand.java +++ b/src/main/java/edu/rpi/legup/history/PuzzleCommand.java @@ -1,27 +1,18 @@ package edu.rpi.legup.history; -/** - * The PuzzleCommand class is an abstract base class for commands that can be executed, undone, and redone - * within the puzzle model. It implements the ICommand interface and maintains the state and error handling - * for the command. - */ public abstract class PuzzleCommand implements ICommand { private CommandState state; private boolean isCached; private String cachedError; - /** - * Puzzle Command Constructor for creating an undoable and redoable change to the model - */ + /** Puzzle Command Constructor for creating an undoable and redoable change to the model. */ protected PuzzleCommand() { this.state = CommandState.CREATED; this.isCached = false; this.cachedError = null; } - /** - * Executes the command if it can be executed - */ + /** Executes an command */ @Override public final void execute() { if (canExecute()) { @@ -30,9 +21,7 @@ public final void execute() { } } - /** - * Determines whether the command can be executed by checking the error state - */ + /** Determines whether this command can be executed */ @Override public final boolean canExecute() { cachedError = getError(); @@ -63,21 +52,13 @@ public final String getError() { */ public abstract String getErrorString(); - /** - * Executes the command. - * This method must be implemented by subclasses to define the command's execution behavior. - */ + /** Executes an command */ public abstract void executeCommand(); - /** - * Undoes the command. - * This method must be implemented by subclasses to define the command's undo behavior. - */ + /** Undoes an command */ public abstract void undoCommand(); - /** - * Redoes the command. This method is called if the command was previously undone. - */ + /** Redoes an command */ public void redoCommand() { if (state == CommandState.UNDOED) { executeCommand(); @@ -87,9 +68,7 @@ public void redoCommand() { } } - /** - * Undoes the command if it was executed or redone - */ + /** Undoes an command */ @Override public final void undo() { if (state == CommandState.EXECUTED || state == CommandState.REDOED) { @@ -100,9 +79,7 @@ public final void undo() { } } - /** - * Redoes the command if it was previously undone. - */ + /** Redoes an command */ public final void redo() { if (state == CommandState.UNDOED) { redoCommand(); diff --git a/src/main/java/edu/rpi/legup/history/ValidateCaseRuleCommand.java b/src/main/java/edu/rpi/legup/history/ValidateCaseRuleCommand.java index 6827436e8..7737ecfd3 100644 --- a/src/main/java/edu/rpi/legup/history/ValidateCaseRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ValidateCaseRuleCommand.java @@ -12,10 +12,6 @@ import java.util.List; import java.util.Map; -/** - * The ValidateCaseRuleCommand class represents a command for validating a CaseRule in the tree structure. - * It extends the PuzzleCommand class and implements the ICommand interface. - */ public class ValidateCaseRuleCommand extends PuzzleCommand { private TreeViewSelection selection; @@ -37,9 +33,7 @@ public ValidateCaseRuleCommand(TreeViewSelection selection, CaseRule caseRule) { this.addNode = new HashMap<>(); } - /** - * Executes the command to validate the CaseRule - */ + /** Executes an command */ @Override public void executeCommand() { Tree tree = getInstance().getTree(); @@ -111,10 +105,7 @@ public String getErrorString() { return null; } - - /** - * Undoes the validation command, restoring the previous state - */ + /** Undoes an command */ @Override public void undoCommand() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); diff --git a/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java b/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java index b106a6072..8737b4008 100644 --- a/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java @@ -10,10 +10,6 @@ import java.util.List; import java.util.Map; -/** - * The ValidateContradictionRuleCommand class represents a command for validating and applying a ContradictionRule - * within a tree structure. It extends the PuzzleCommand class and implements the ICommand interface. - */ public class ValidateContradictionRuleCommand extends PuzzleCommand { private TreeViewSelection selection; @@ -35,9 +31,7 @@ public ValidateContradictionRuleCommand(TreeViewSelection selection, Contradicti this.addTran = new HashMap<>(); } - /** - * Executes the command to validate and apply the ContradictionRule. - */ + /** Executes a command */ @Override public void executeCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); @@ -90,12 +84,7 @@ public void executeCommand() { final TreeElement finalTreeElement; if (firstSelectedView.getType() == TreeElementType.NODE) { TreeNodeView nodeView = (TreeNodeView) firstSelectedView; - if (!nodeView.getChildrenViews().isEmpty()) { - finalTreeElement = nodeView.getChildrenViews().get(0).getTreeElement(); - } - else { - finalTreeElement = null; - } + finalTreeElement = nodeView.getChildrenViews().get(0).getTreeElement(); } else { TreeTransitionView transitionView = (TreeTransitionView) firstSelectedView; if (transitionView.getChildView() != null) { @@ -136,9 +125,7 @@ public String getErrorString() { return null; } - /** - * Undoes the validation command, restoring the previous state. - */ + /** Undoes a command */ @Override public void undoCommand() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); diff --git a/src/main/java/edu/rpi/legup/history/ValidateDirectRuleCommand.java b/src/main/java/edu/rpi/legup/history/ValidateDirectRuleCommand.java index f7694cc0a..d9c063464 100644 --- a/src/main/java/edu/rpi/legup/history/ValidateDirectRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ValidateDirectRuleCommand.java @@ -6,19 +6,11 @@ import edu.rpi.legup.model.rules.Rule; import edu.rpi.legup.model.tree.*; import edu.rpi.legup.ui.proofeditorui.treeview.*; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import java.util.HashMap; import java.util.List; import java.util.Map; -/** - * The ValidateDirectRuleCommand class represents a command for validating and applying a DirectRule - * to a set of selected tree elements. It extends the PuzzleCommand class and implements the ICommand interface. - */ public class ValidateDirectRuleCommand extends PuzzleCommand { - private static final Logger LOGGER = LogManager.getLogger(History.class.getName()); private TreeViewSelection selection; private Map oldRules; @@ -38,9 +30,7 @@ public ValidateDirectRuleCommand(TreeViewSelection selection, DirectRule rule) { this.addNode = new HashMap<>(); } - /** - * Executes the command to validate and apply the DirectRule. - */ + /** Executes an command */ @Override public void executeCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); @@ -52,15 +42,14 @@ public void executeCommand() { for (TreeElementView selectedView : selectedViews) { TreeElement element = selectedView.getTreeElement(); TreeTransitionView transitionView; - if (element.getType() == TreeElementType.NODE) { TreeNodeView nodeView = (TreeNodeView) selectedView; transitionView = nodeView.getChildrenViews().get(0); } else { transitionView = (TreeTransitionView) selectedView; } - TreeTransition transition = transitionView.getTreeElement(); + oldRules.put(transition, transition.getRule()); transition.setRule(newRule); @@ -77,46 +66,21 @@ public void executeCommand() { final TreeNode finalNode = childNode; puzzle.notifyTreeListeners(listener -> listener.onTreeElementAdded(finalNode)); } - - TreeElementView childView = treeView.getElementView(childNode); - if (childView == null) { - LOGGER.error("Child view is null for child node: " + childNode); - continue; - } - newSelection.addToSelection(childView); + newSelection.addToSelection(treeView.getElementView(childNode)); } - TreeElementView firstSelectedView = selection.getFirstSelection(); - final TreeElement finalTreeElement; if (firstSelectedView.getType() == TreeElementType.NODE) { TreeNodeView nodeView = (TreeNodeView) firstSelectedView; - if (nodeView.getChildrenViews().isEmpty()) { - LOGGER.error("NodeView has no children views"); - return; - } finalTreeElement = nodeView.getChildrenViews().get(0).getTreeElement(); } else { TreeTransitionView transitionView = (TreeTransitionView) firstSelectedView; - TreeNodeView childView = transitionView.getChildView(); - if (childView == null) { - LOGGER.error("Child view is null for transition view: " + transitionView); - TreeNode childNode = transitionView.getTreeElement().getChildNode(); - childView = (TreeNodeView) treeView.getElementView(childNode); - transitionView.setChildView(childView); - } - TreeTransition transition = transitionView.getTreeElement(); - if (transition.getParents().get(0).getChildren().isEmpty()) { - transition.getParents().get(0).addChild(transition); - } finalTreeElement = transitionView.getChildView().getTreeElement(); } - puzzle.notifyBoardListeners(listener -> listener.onTreeElementChanged(finalTreeElement)); puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); } - /** * Gets the reason why the command cannot be executed * @@ -146,9 +110,7 @@ public String getErrorString() { return null; } - /** - * Undoes the validation command, restoring the previous state. - */ + /** Undoes an command */ @Override public void undoCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); @@ -162,10 +124,8 @@ public void undoCommand() { transitionView = nodeView.getChildrenViews().get(0); } else { transitionView = (TreeTransitionView) selectedView; - } TreeTransition transition = transitionView.getTreeElement(); - transition.setRule(oldRules.get(transition)); if (addNode.get(transition) != null) { diff --git a/src/main/java/edu/rpi/legup/model/Puzzle.java b/src/main/java/edu/rpi/legup/model/Puzzle.java index 6cc92f347..7971c95af 100644 --- a/src/main/java/edu/rpi/legup/model/Puzzle.java +++ b/src/main/java/edu/rpi/legup/model/Puzzle.java @@ -35,11 +35,6 @@ import org.w3c.dom.Node; import org.xml.sax.SAXException; -/** - * Abstract class representing a puzzle. - * The Puzzle class manages the core components of a puzzle game, including the board, rules, and elements. - * It also handles importing and exporting puzzle configurations and notifies listeners about changes. - */ public abstract class Puzzle implements IBoardSubject, ITreeSubject { private static final Logger LOGGER = LogManager.getLogger(Puzzle.class.getName()); @@ -58,6 +53,7 @@ public abstract class Puzzle implements IBoardSubject, ITreeSubject { protected List contradictionRules; protected List caseRules; protected List placeableElements; + protected List nonPlaceableElements; /** Puzzle Constructor - creates a new Puzzle */ public Puzzle() { @@ -69,15 +65,12 @@ public Puzzle() { this.caseRules = new ArrayList<>(); this.placeableElements = new ArrayList<>(); + this.nonPlaceableElements = new ArrayList<>(); registerRules(); registerPuzzleElements(); } - /** - * Registers puzzle elements from the package of the derived class. - * Scans for classes annotated with {@link RegisterElement} and initializes them. - */ private void registerPuzzleElements() { String packageName = this.getClass().getPackage().toString().replace("package ", ""); @@ -86,10 +79,6 @@ private void registerPuzzleElements() { for (Class c : possElements) { - String classPackageName = c.getPackage().getName(); - if (!classPackageName.startsWith("edu.rpi.legup.puzzle.") || !classPackageName.endsWith(".elements")) { - continue; - } System.out.println("possible element: " + c.getName()); // check that the element is not abstract @@ -106,6 +95,9 @@ private void registerPuzzleElements() { case PLACEABLE: this.addPlaceableElement((PlaceableElement) element); break; + case NONPLACEABLE: + this.addNonPlaceableElement((NonPlaceableElement) element); + break; default: break; } @@ -116,15 +108,19 @@ private void registerPuzzleElements() { } } } + + // } catch (IOException | ClassNotFoundException | NoSuchMethodException | + // InstantiationException | IllegalAccessException | + // InvocationTargetException + // e) { + // LOGGER.error("Unable to find rules for " + + // this.getClass().getSimpleName(), e); + // } } catch (Exception e) { LOGGER.error("Unable to find elements for " + this.getClass().getSimpleName(), e); } } - /** - * Registers rules from the package of the derived class. - * Scans for classes annotated with {@link RegisterRule} and initializes them. - */ private void registerRules() { String packageName = this.getClass().getPackage().toString().replace("package ", ""); @@ -133,10 +129,6 @@ private void registerRules() { for (Class c : possRules) { - String classPackageName = c.getPackage().getName(); - if (!classPackageName.startsWith("edu.rpi.legup.puzzle.") || !classPackageName.endsWith(".rules")) { - continue; - } System.out.println("possible rule: " + c.getName()); // check that the rule is not abstract @@ -171,14 +163,20 @@ private void registerRules() { } } } + + // } catch (IOException | ClassNotFoundException | NoSuchMethodException | + // InstantiationException | IllegalAccessException | + // InvocationTargetException + // e) { + // LOGGER.error("Unable to find rules for " + + // this.getClass().getSimpleName(), e); + // } } catch (Exception e) { LOGGER.error("Unable to find rules for " + this.getClass().getSimpleName(), e); } } - /** - * Initializes the view. Called by the invoker of the class - */ + /** Initializes the view. Called by the invoker of the class */ public abstract void initializeView(); /** @@ -201,10 +199,10 @@ public boolean isValidDimensions(int rows, int columns) { } /** - * Checks if the provided text input is valid for the puzzle. + * Checks if the given array of statements is valid text input for the given puzzle * - * @param statements array of statements to check - * @return true if input is valid, false otherwise + * @param statements + * @return */ public boolean isValidTextInput(String[] statements) { return statements.length > 0; @@ -336,15 +334,14 @@ public List getDirectRules() { return directRules; } - /** - * Gets the list of placeable elements. - * - * @return list of PlaceableElement instances - */ public List getPlaceableElements() { return placeableElements; } + public List getNonPlaceableElements() { + return nonPlaceableElements; + } + /** * Sets the list of direct rules * @@ -363,15 +360,14 @@ public void addDirectRule(DirectRule rule) { directRules.add(rule); } - /** - * Adds a placeable element to this puzzle. - * - * @param element PlaceableElement to add - */ public void addPlaceableElement(PlaceableElement element) { placeableElements.add(element); } + public void addNonPlaceableElement(NonPlaceableElement element) { + nonPlaceableElements.add(element); + } + /** * Remove a basic rule from this Puzzle * @@ -584,10 +580,9 @@ public void setFactory(ElementFactory factory) { } /** - * Adds a board listener to the list of listeners. - * This allows the puzzle to notify the listener about changes to the board. + * Adds a board listener * - * @param listener The IBoardListener to be added to the list of listeners. + * @param listener listener to add */ @Override public void addBoardListener(IBoardListener listener) { @@ -595,10 +590,9 @@ public void addBoardListener(IBoardListener listener) { } /** - * Removes a board listener from the list of listeners. - * This prevents the puzzle from notifying the listener about future changes to the board. + * Removes a board listener * - * @param listener The IBoardListener to be removed from the list of listeners. + * @param listener listener to remove */ @Override public void removeBoardListener(IBoardListener listener) { @@ -606,10 +600,9 @@ public void removeBoardListener(IBoardListener listener) { } /** - * Notifies all registered board listeners about changes. - * The provided algorithm is applied to each listener to process the notification. + * Notifies listeners * - * @param algorithm A Consumer function that takes an IBoardListener and performs operations to notify the listener. + * @param algorithm algorithm to notify the listeners with */ @Override public void notifyBoardListeners(Consumer algorithm) { @@ -617,10 +610,9 @@ public void notifyBoardListeners(Consumer algorithm) { } /** - * Adds a tree listener to the list of listeners. - * This allows the puzzle to notify the listener about changes to the tree. + * Adds a board listener * - * @param listener The ITreeListener to be added to the list of listeners. + * @param listener listener to add */ @Override public void addTreeListener(ITreeListener listener) { @@ -628,10 +620,9 @@ public void addTreeListener(ITreeListener listener) { } /** - * Removes a tree listener from the list of listeners. - * This prevents the puzzle from notifying the listener about future changes to the tree. + * Removes a tree listener * - * @param listener The ITreeListener to be removed from the list of listeners. + * @param listener listener to remove */ @Override public void removeTreeListener(ITreeListener listener) { @@ -639,10 +630,9 @@ public void removeTreeListener(ITreeListener listener) { } /** - * Notifies all registered tree listeners about changes. - * The provided algorithm is applied to each listener to process the notification. + * Notifies listeners * - * @param algorithm A Consumer function that takes an ITreeListener and performs operations to notify the listener. + * @param algorithm algorithm to notify the listeners with */ @Override public void notifyTreeListeners(Consumer algorithm) { @@ -650,10 +640,9 @@ public void notifyTreeListeners(Consumer algorithm) { } /** - * Checks if the puzzle is valid. - * The implementation of this method can vary based on the specific criteria for puzzle validity. + * Check if the puzzle is valid * - * @return true if the puzzle is valid, false otherwise. + * @return if the puzzle is valid */ public boolean checkValidity() { return true; diff --git a/src/main/java/edu/rpi/legup/model/PuzzleExporter.java b/src/main/java/edu/rpi/legup/model/PuzzleExporter.java index 234d0f25c..a052a736a 100644 --- a/src/main/java/edu/rpi/legup/model/PuzzleExporter.java +++ b/src/main/java/edu/rpi/legup/model/PuzzleExporter.java @@ -20,11 +20,6 @@ import org.w3c.dom.Document; import org.w3c.dom.Element; -/** - * Abstract class for exporting puzzle data to XML format. - * This class provides functionality to export a puzzle object, including its board and tree structure, to an XML file. - * Subclasses must implement methods to create specific elements for the board and the tree. - */ public abstract class PuzzleExporter { private static final Logger LOGGER = LogManager.getLogger(PuzzleExporter.class.getName()); @@ -96,22 +91,8 @@ public void exportPuzzle(String fileName) throws ExportFileException { } } - /** - * Creates an XML element representing the board of the puzzle. - * Subclasses must implement this method to provide the XML structure for the board. - * - * @param newDocument The XML document to create elements within. - * @return An XML element representing the puzzle's board. - */ protected abstract Element createBoardElement(Document newDocument); - /** - * Creates an XML element representing the proof of the puzzle, including its tree structure. - * This method is used to generate the proof section of the XML, which contains the tree representation. - * - * @param newDocument The XML document to create elements within. - * @return An XML element representing the proof of the puzzle. - */ protected Element createProofElement(Document newDocument) { org.w3c.dom.Element proofElement = newDocument.createElement("proof"); org.w3c.dom.Element treeElement = createTreeElement(newDocument); @@ -119,13 +100,6 @@ protected Element createProofElement(Document newDocument) { return proofElement; } - /** - * Creates an XML element representing the tree structure of the puzzle. - * This method traverses the tree nodes and transitions, and creates XML elements for each. - * - * @param newDocument The XML document to create elements within. - * @return An XML element representing the puzzle's tree structure. - */ protected Element createTreeElement(Document newDocument) { org.w3c.dom.Element treeElement = newDocument.createElement("tree"); diff --git a/src/main/java/edu/rpi/legup/model/PuzzleImporter.java b/src/main/java/edu/rpi/legup/model/PuzzleImporter.java index b1e8a2dd9..0902478db 100644 --- a/src/main/java/edu/rpi/legup/model/PuzzleImporter.java +++ b/src/main/java/edu/rpi/legup/model/PuzzleImporter.java @@ -13,12 +13,6 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; -/** - * Abstract class for importing puzzle data from various sources into a puzzle object. - * This class provides methods to initialize and set up a puzzle, including its board and proof structure, - * from different input formats such as dimensions, statements, or XML files. - * Subclasses must implement methods to handle specific formats for board initialization and proof creation. - */ public abstract class PuzzleImporter { private static final Logger LOGGER = LogManager.getLogger(PuzzleImporter.class.getName()); @@ -52,13 +46,6 @@ public void initializePuzzle(int rows, int columns) throws RuntimeException { } } - /** - * Initializes the puzzle with the given array of statements - * - * @param statements the statements used to initialize the puzzle - * @throws InputMismatchException if the input statements are invalid - * @throws IllegalArgumentException if the statements are not suitable for initializing the puzzle - */ public void initializePuzzle(String[] statements) throws InputMismatchException, IllegalArgumentException { // Note: Error checking for the statements will be left up to the puzzles that support @@ -68,10 +55,10 @@ public void initializePuzzle(String[] statements) } /** - * Initializes the puzzle attributes from the XML document node + * Initializes the puzzle attributes * - * @param node the XML document node representing the puzzle - * @throws InvalidFileFormatException if the file format is invalid + * @param node xml document node + * @throws InvalidFileFormatException if file is invalid */ public void initializePuzzle(Node node) throws InvalidFileFormatException { if (node.getNodeName().equalsIgnoreCase("puzzle")) { @@ -124,29 +111,22 @@ public void initializePuzzle(Node node) throws InvalidFileFormatException { } /** - * Initializes the board with the specified number of rows and columns. + * Creates the board for building * - * @param rows the number of rows on the puzzle - * @param columns the number of columns on the puzzle - * @throws RuntimeException if the board cannot be created with the provided dimensions + * @param rows number of rows on the puzzle + * @param columns number of columns on the puzzle + * @throws RuntimeException if board can not be created */ public abstract void initializeBoard(int rows, int columns); /** - * Initializes the board from the XML document node. + * Creates an empty board for building * - * @param node the XML document node representing the board - * @throws InvalidFileFormatException if the file format is invalid + * @param node xml document node + * @throws InvalidFileFormatException if file is invalid */ public abstract void initializeBoard(Node node) throws InvalidFileFormatException; - /** - * Initializes the board using an array of statements. - * - * @param statements the statements used to initialize the board - * @throws UnsupportedOperationException if the operation is not supported - * @throws IllegalArgumentException if the statements are not suitable for initializing the board - */ public abstract void initializeBoard(String[] statements) throws UnsupportedOperationException, IllegalArgumentException; @@ -314,7 +294,6 @@ protected void createTree(Node node) throws InvalidFileFormatException { } } - protected void validateTreeStructure( HashMap nodes, HashMap transitions) throws InvalidFileFormatException { @@ -389,13 +368,6 @@ protected void validateTreeStructure( } } - /** - * Updates the board state based on the changes specified in the TreeTransition. - * - * @param transition the TreeTransition object representing the transition to be updated - * @param transElement the XML node containing the transition data - * @throws InvalidFileFormatException if the XML node format is incorrect or unknown nodes are encountered - */ protected void makeTransitionChanges(TreeTransition transition, Node transElement) throws InvalidFileFormatException { if (transition.getRule() instanceof MergeRule) { @@ -439,10 +411,6 @@ protected void makeTransitionChanges(TreeTransition transition, Node transElemen } } - /** - * Creates a default proof tree with a single root node. The root node is initialized with the current board state. - * The created tree is then set as the proof tree for the puzzle. - */ protected void createDefaultTree() { TreeNode root = new TreeNode(puzzle.getCurrentBoard()); root.setRoot(true); @@ -452,7 +420,7 @@ protected void createDefaultTree() { } /** - * Gets the result of building the Puzzle object. + * Gets the result of building the Puzzle * * @return puzzle */ diff --git a/src/main/java/edu/rpi/legup/model/RegisterPuzzle.java b/src/main/java/edu/rpi/legup/model/RegisterPuzzle.java index c473e0ecd..c4c1ed273 100644 --- a/src/main/java/edu/rpi/legup/model/RegisterPuzzle.java +++ b/src/main/java/edu/rpi/legup/model/RegisterPuzzle.java @@ -5,9 +5,6 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -/** - * Annotation for registering puzzle classes with the puzzle framework. - */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface RegisterPuzzle {} diff --git a/src/main/java/edu/rpi/legup/model/elements/Element.java b/src/main/java/edu/rpi/legup/model/elements/Element.java index 0adbbaf91..8b75d075d 100644 --- a/src/main/java/edu/rpi/legup/model/elements/Element.java +++ b/src/main/java/edu/rpi/legup/model/elements/Element.java @@ -4,10 +4,6 @@ import java.awt.image.BufferedImage; import javax.swing.*; -/** - * The Element class serves as an abstract base class for various elements used in the system. - * It handles basic properties such as ID, name, description, and image associated with the element. - */ @RegisterElement public abstract class Element { protected String elementID; @@ -21,14 +17,6 @@ public abstract class Element { private final String INVALID_USE_MESSAGE; - /** - * Constructs an Element with the specified ID, name, description, and image name - * - * @param elementID Unique identifier for the element - * @param elementName Name of the element - * @param description Description of the element - * @param imageName File name of the image associated with the element - */ public Element(String elementID, String elementName, String description, String imageName) { this.elementID = elementID; this.elementName = elementName; @@ -38,9 +26,6 @@ public Element(String elementID, String elementName, String description, String loadImage(); } - /** - * Loads the image for the element and resizes it to a width of 100 pixels while maintaining aspect ratio - */ private void loadImage() { if (imageName != null) { this.image = new ImageIcon(ClassLoader.getSystemClassLoader().getResource(imageName)); @@ -62,65 +47,30 @@ private void loadImage() { } } - /** - * Gets the name of the element - * - * @return The name of the element - */ public String getElementName() { return elementName; } - /** - * Sets the name of the element - * - * @param elementName The new name for the element - */ public void setElementName(String elementName) { this.elementName = elementName; } - /** - * Gets the unique identifier of the element - * - * @return The ID of the element - */ public String getElementID() { return elementID; } - /** - * Gets the description of the element - * - * @return The description of the element - */ public String getDescription() { return description; } - /** - * Gets the image icon associated with the element - * - * @return The ImageIcon for the element - */ public ImageIcon getImageIcon() { return image; } - /** - * Gets the type of the element - * - * @return The ElementType of the element - */ public ElementType getElementType() { return elementType; } - /** - * Gets the message for invalid use of the rule - * - * @return The invalid use message - */ public String getInvalidUseOfRuleMessage() { return this.INVALID_USE_MESSAGE; } diff --git a/src/main/java/edu/rpi/legup/model/elements/ElementType.java b/src/main/java/edu/rpi/legup/model/elements/ElementType.java index 4fee79d4f..dff4fe04f 100644 --- a/src/main/java/edu/rpi/legup/model/elements/ElementType.java +++ b/src/main/java/edu/rpi/legup/model/elements/ElementType.java @@ -1,9 +1,6 @@ package edu.rpi.legup.model.elements; -/** - * Enum representing the different types of elements that can be used in the system - */ public enum ElementType { - PLACEABLE - //NONPLACEABLE COMBINED ALL PLACEABLE AND NONPLACEABLE INTO JUST ONE CATEGORY + PLACEABLE, + NONPLACEABLE } diff --git a/src/main/java/edu/rpi/legup/model/elements/NonPlaceableElement.java b/src/main/java/edu/rpi/legup/model/elements/NonPlaceableElement.java index 019001128..4ab0ab509 100644 --- a/src/main/java/edu/rpi/legup/model/elements/NonPlaceableElement.java +++ b/src/main/java/edu/rpi/legup/model/elements/NonPlaceableElement.java @@ -1,9 +1,9 @@ -//package edu.rpi.legup.model.elements; -// -//public abstract class NonPlaceableElement extends Element { -// public NonPlaceableElement( -// String elementID, String elementName, String description, String imageName) { -// super(elementID, elementName, description, imageName); -// this.elementType = ElementType.PLACEABLE; -// } -//} +package edu.rpi.legup.model.elements; + +public abstract class NonPlaceableElement extends Element { + public NonPlaceableElement( + String elementID, String elementName, String description, String imageName) { + super(elementID, elementName, description, imageName); + this.elementType = ElementType.NONPLACEABLE; + } +} diff --git a/src/main/java/edu/rpi/legup/model/elements/PlaceableElement.java b/src/main/java/edu/rpi/legup/model/elements/PlaceableElement.java index 79a0dcff8..133658700 100644 --- a/src/main/java/edu/rpi/legup/model/elements/PlaceableElement.java +++ b/src/main/java/edu/rpi/legup/model/elements/PlaceableElement.java @@ -1,19 +1,6 @@ package edu.rpi.legup.model.elements; -/** - * Abstract class representing elements that can be placed within the system. - * Inherits from the {@link Element} class and sets the {@link ElementType} to {@link ElementType#PLACEABLE}. - */ public abstract class PlaceableElement extends Element { - - /** - * Constructs a PlaceableElement with the specified details - * - * @param elementID Unique identifier for the element - * @param elementName Name of the element - * @param description Description of the element - * @param imageName Name of the image file representing the element - */ public PlaceableElement( String elementID, String elementName, String description, String imageName) { super(elementID, elementName, description, imageName); diff --git a/src/main/java/edu/rpi/legup/model/elements/RegisterElement.java b/src/main/java/edu/rpi/legup/model/elements/RegisterElement.java index 5f59ad795..368ecc8d1 100644 --- a/src/main/java/edu/rpi/legup/model/elements/RegisterElement.java +++ b/src/main/java/edu/rpi/legup/model/elements/RegisterElement.java @@ -3,11 +3,6 @@ import java.lang.annotation.*; import java.lang.annotation.ElementType; -/** - * Annotation to mark classes as elements that should be registered. - * This annotation is used to indicate that a class is an element within the system and should be registered - * for use within the application. - */ @Inherited @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/edu/rpi/legup/model/gameboard/Board.java b/src/main/java/edu/rpi/legup/model/gameboard/Board.java index 4544caa36..d8bdf5199 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/Board.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/Board.java @@ -5,11 +5,6 @@ import java.util.List; import java.util.Set; -/** - * Abstract class representing a game board. - * This class provides functionality for managing puzzle elements, tracking modifications, - * and determining if the board is modifiable. - */ public abstract class Board { protected List puzzleElements; @@ -36,24 +31,21 @@ public Board(int size) { } /** - * Gets a specific {@link PuzzleElement} from the board. + * Gets a specific {@link PuzzleElement} on this board. * - * @param puzzleElement the puzzle element to retrieve - * @return the puzzle element at the corresponding index, or null if not found + * @param puzzleElement equivalent puzzleElement + * @return equivalent puzzleElement on this board */ public PuzzleElement getPuzzleElement(PuzzleElement puzzleElement) { - if (puzzleElement == null) { - return null; - } int index = puzzleElement.getIndex(); return index < puzzleElements.size() ? puzzleElements.get(index) : null; } /** - * Sets a specific {@link PuzzleElement} on the board + * Sets a specific {@link PuzzleElement} on the board. * * @param index index of the puzzleElement - * @param puzzleElement the puzzleElement to set at the index + * @param puzzleElement new puzzleElement at the index */ public void setPuzzleElement(int index, PuzzleElement puzzleElement) { if (index < puzzleElements.size()) { @@ -62,7 +54,7 @@ public void setPuzzleElement(int index, PuzzleElement puzzleElement) { } /** - * Gets the number of elements on the board + * Gets the number of elements on the board. * * @return number of elements on the board */ diff --git a/src/main/java/edu/rpi/legup/model/gameboard/CaseBoard.java b/src/main/java/edu/rpi/legup/model/gameboard/CaseBoard.java index 14dcb8609..fa3625a43 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/CaseBoard.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/CaseBoard.java @@ -5,108 +5,49 @@ import java.util.HashSet; import java.util.Set; -/** - * Represents a game board with specific rules for selecting puzzle elements. - * Extends the abstract `Board` class and adds functionality for handling pickable elements and case rules. - */ public class CaseBoard extends Board { protected Board baseBoard; protected CaseRule caseRule; protected Set pickablePuzzleElements; - /** - * Constructs a CaseBoard with a base board and a case rule. - * - * @param baseBoard the base board to use for this CaseBoard - * @param caseRule the case rule applied to this CaseBoard - */ public CaseBoard(Board baseBoard, CaseRule caseRule) { this.baseBoard = baseBoard; this.caseRule = caseRule; this.pickablePuzzleElements = new HashSet<>(); } - /** - * Adds a puzzle element to the set of pickable elements. - * - * @param puzzleElement the puzzle element to add - */ public void addPickableElement(PuzzleElement puzzleElement) { pickablePuzzleElements.add(puzzleElement); } - /** - * Removes a puzzle element from the set of pickable elements. - * - * @param puzzleElement the puzzle element to remove - */ public void removePickableElement(PuzzleElement puzzleElement) { pickablePuzzleElements.remove(puzzleElement); } - /** - * Checks if a puzzle element is pickable based on the mouse event. - * - * @param puzzleElement the puzzle element to check - * @param e the mouse event - * @return true if the puzzle element is pickable, false otherwise - */ public boolean isPickable(PuzzleElement puzzleElement, MouseEvent e) { return pickablePuzzleElements.contains(baseBoard.getPuzzleElement(puzzleElement)); } - /** - * Retrieves the base board for this CaseBoard. - * - * @return the base board - */ public Board getBaseBoard() { return baseBoard; } - /** - * Sets the base board for this CaseBoard. - * - * @param baseBoard the new base board - */ public void setBaseBoard(Board baseBoard) { this.baseBoard = baseBoard; } - /** - * Retrieves the case rule for this CaseBoard. - * - * @return the case rule - */ public CaseRule getCaseRule() { return caseRule; } - /** - * Sets the case rule for this CaseBoard. - * - * @param caseRule the new case rule - */ public void setCaseRule(CaseRule caseRule) { this.caseRule = caseRule; } - /** - * Gets the count of pickable puzzle elements. - * - * @return the number of pickable elements - */ public int getCount() { return pickablePuzzleElements.size(); } - - /** - * Performs a deep copy of this CaseBoard. - * CURRENTLY NOT IMPLEMENTED AND RETURNS NULL - * - * @return a new copy of the CaseBoard - */ public CaseBoard copy() { return null; } diff --git a/src/main/java/edu/rpi/legup/model/gameboard/ElementFactory.java b/src/main/java/edu/rpi/legup/model/gameboard/ElementFactory.java index 131feb122..bfc785bdd 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/ElementFactory.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/ElementFactory.java @@ -5,9 +5,6 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; -/** - * ElementFactory is an abstract class for importing and exporting {@link PuzzleElement} instances. - */ public abstract class ElementFactory { /** diff --git a/src/main/java/edu/rpi/legup/model/gameboard/GridBoard.java b/src/main/java/edu/rpi/legup/model/gameboard/GridBoard.java index 7338132f8..9593690ce 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/GridBoard.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/GridBoard.java @@ -6,11 +6,6 @@ import java.awt.*; import java.awt.event.MouseEvent; -/** - * GridBoard represents a grid-based board where each cell can be manipulated based on its - * coordinates. The board supports operations such as getting and setting cells, and provides - * dimensions of the grid. It also supports deep copying of the board. - */ public class GridBoard extends Board { protected Dimension dimension; diff --git a/src/main/java/edu/rpi/legup/model/gameboard/GridCell.java b/src/main/java/edu/rpi/legup/model/gameboard/GridCell.java index 5d6fe1ff3..a33c3ec80 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/GridCell.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/GridCell.java @@ -2,13 +2,6 @@ import java.awt.*; -/** - * GridCell represents a cell within a grid-based board. It holds data of type T and tracks its location - * on the board using a {@link Point}. The class extends from PuzzleElement and supports deep copying of - * the grid cell. - * - * @param the type of data held by the GridCell - */ public class GridCell extends PuzzleElement { protected Point location; diff --git a/src/main/java/edu/rpi/legup/model/gameboard/GridRegion.java b/src/main/java/edu/rpi/legup/model/gameboard/GridRegion.java index b2a10a153..c41d5e1b2 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/GridRegion.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/GridRegion.java @@ -3,12 +3,6 @@ import java.util.ArrayList; import java.util.List; -/** - * GridRegion represents a collection of cells within a grid. It manages a list of cells and provides - * methods to add, remove, and retrieve cells from the region. - * - * @param the type of cell managed by the GridRegion - */ public abstract class GridRegion { protected List regionCells; @@ -30,7 +24,7 @@ public void addCell(T cell) { /** * Removes the cell from the region * - * @param cell cell to be removed from the region + * @param cell cell to be remove from the region */ public void removeCell(T cell) { regionCells.remove(cell); @@ -53,4 +47,9 @@ public List getCells() { public int getSize() { return regionCells.size(); } + + /* + public void colorRegion(){} + */ + } diff --git a/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java b/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java index a92b3efb0..4ce030a04 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java @@ -3,18 +3,11 @@ import edu.rpi.legup.model.elements.Element; import java.awt.event.MouseEvent; -/** - * PuzzleElement represents a single element in a puzzle grid. It holds data and provides various - * methods to manage and retrieve its properties, including modifiability, modification status, and validity. - * - * @param the type of data held by the PuzzleElement - */ public abstract class PuzzleElement { protected int index; protected T data; protected boolean isModifiable; protected boolean isModified; - protected boolean isModifiableCaseRule; protected boolean isGiven; protected boolean isValid; protected int casesDepended; @@ -24,7 +17,6 @@ public PuzzleElement() { this.index = -1; this.data = null; this.isModifiable = true; - this.isModifiableCaseRule = true; this.isModified = false; this.isGiven = false; this.isValid = true; @@ -81,24 +73,6 @@ public void setModifiable(boolean isModifiable) { this.isModifiable = isModifiable; } - /** - * Gets whether this puzzle element is modifiable as a result of a case rule. - * - * @return true if this puzzle element is modifiable, false otherwise - */ - public boolean isModifiableCaseRule() { - return isModifiableCaseRule; - } - - /** - * Sets whether this puzzle element is modifiable as a result of a case rule. - * - * @param isModifiableCaseRule true if this puzzle element is modifiable, false otherwise - */ - public void setModifiableCaseRule(boolean isModifiableCaseRule) { - this.isModifiableCaseRule = isModifiableCaseRule; - } - /** * Gets whether the puzzle element has been modified. * diff --git a/src/main/java/edu/rpi/legup/model/observer/IBoardListener.java b/src/main/java/edu/rpi/legup/model/observer/IBoardListener.java index fa440369c..461128562 100644 --- a/src/main/java/edu/rpi/legup/model/observer/IBoardListener.java +++ b/src/main/java/edu/rpi/legup/model/observer/IBoardListener.java @@ -4,10 +4,6 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.tree.TreeElement; -/** - * IBoardListener defines methods for receiving notifications about changes to the board, - * including updates to tree elements, case boards, and puzzle elements. - */ public interface IBoardListener { /** * Called when the tree element has changed. @@ -17,7 +13,7 @@ public interface IBoardListener { void onTreeElementChanged(TreeElement treeElement); /** - * Called when a case board has been added to the view. + * Called when the a case board has been added to the view. * * @param caseBoard case board to be added */ diff --git a/src/main/java/edu/rpi/legup/model/observer/IBoardSubject.java b/src/main/java/edu/rpi/legup/model/observer/IBoardSubject.java index 53bfe444b..c7bf13141 100644 --- a/src/main/java/edu/rpi/legup/model/observer/IBoardSubject.java +++ b/src/main/java/edu/rpi/legup/model/observer/IBoardSubject.java @@ -2,10 +2,6 @@ import java.util.function.Consumer; -/** - * IBoardSubject defines methods for managing and notifying board listeners. - * It allows for adding and removing listeners, and for notifying them using a specified algorithm. - */ public interface IBoardSubject { /** * Adds a board listener. @@ -22,7 +18,7 @@ public interface IBoardSubject { void removeBoardListener(IBoardListener listener); /** - * Notifies all the listeners using the specified algorithm. + * Notifies all of the listeners using the specified algorithm. * * @param algorithm algorithm used to notify the listeners */ diff --git a/src/main/java/edu/rpi/legup/model/observer/ITreeListener.java b/src/main/java/edu/rpi/legup/model/observer/ITreeListener.java index c5a0355ca..d5e7fdb2d 100644 --- a/src/main/java/edu/rpi/legup/model/observer/ITreeListener.java +++ b/src/main/java/edu/rpi/legup/model/observer/ITreeListener.java @@ -3,10 +3,6 @@ import edu.rpi.legup.model.tree.TreeElement; import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; -/** - * ITreeListener defines methods for handling events related to changes in the tree. - * Implementations of this interface are notified of additions, removals, and selection changes in the tree. - */ public interface ITreeListener { /** * Called when a {@link TreeElement} is added to the tree. diff --git a/src/main/java/edu/rpi/legup/model/observer/ITreeSubject.java b/src/main/java/edu/rpi/legup/model/observer/ITreeSubject.java index 0b0aee348..66d5d9a5e 100644 --- a/src/main/java/edu/rpi/legup/model/observer/ITreeSubject.java +++ b/src/main/java/edu/rpi/legup/model/observer/ITreeSubject.java @@ -2,13 +2,9 @@ import java.util.function.Consumer; -/** - * ITreeSubject defines methods for managing and notifying listeners about changes to the tree model. - * Implementations of this interface handle adding, removing, and notifying listeners. - */ public interface ITreeSubject { /** - * Adds a tree listener. + * Adds a board listener. * * @param listener listener to add */ @@ -22,7 +18,7 @@ public interface ITreeSubject { void removeTreeListener(ITreeListener listener); /** - * Notifies all the tree listeners using the specified algorithm. + * Notifies all of the listeners using the specified algorithm. * * @param algorithm algorithm used to notify the listeners */ diff --git a/src/main/java/edu/rpi/legup/model/rules/CaseRule.java b/src/main/java/edu/rpi/legup/model/rules/CaseRule.java index ee5c91229..ab2cc8f0d 100644 --- a/src/main/java/edu/rpi/legup/model/rules/CaseRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/CaseRule.java @@ -11,10 +11,6 @@ import java.util.List; import java.util.Set; -/** - * CaseRule is an abstract class representing a rule that can be applied with multiple cases in a puzzle board. - * It defines methods for applying and checking the rule, as well as retrieving the necessary elements. - */ public abstract class CaseRule extends Rule { private final String INVALID_USE_MESSAGE; diff --git a/src/main/java/edu/rpi/legup/model/rules/ContradictionRule.java b/src/main/java/edu/rpi/legup/model/rules/ContradictionRule.java index cd2e20081..b38a95fd2 100644 --- a/src/main/java/edu/rpi/legup/model/rules/ContradictionRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/ContradictionRule.java @@ -6,10 +6,6 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.tree.TreeTransition; -/** - * ContradictionRule is an abstract class representing a rule that identifies contradictions in a puzzle. - * It provides methods to check for contradictions both globally and at specific puzzle elements. - */ public abstract class ContradictionRule extends Rule { private final String NO_CONTRADICTION_MESSAGE = diff --git a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java index 613574989..d550bc02c 100644 --- a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java @@ -7,10 +7,6 @@ import edu.rpi.legup.model.tree.TreeNode; import edu.rpi.legup.model.tree.TreeTransition; -/** - * DirectRule is an abstract class representing a direct rule for transitions in a puzzle. - * It provides methods for checking whether transitions and specific puzzle elements follow the rule. - */ public abstract class DirectRule extends Rule { /** * DirectRule Constructor creates a new basic rule. diff --git a/src/main/java/edu/rpi/legup/model/rules/MergeRule.java b/src/main/java/edu/rpi/legup/model/rules/MergeRule.java index f7bd887f3..f7badcd8b 100644 --- a/src/main/java/edu/rpi/legup/model/rules/MergeRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/MergeRule.java @@ -10,10 +10,6 @@ import java.util.ArrayList; import java.util.List; -/** - * MergeRule is an implementation of a rule that merges multiple nodes into one. - * It validates if the merging of nodes is done correctly. - */ public class MergeRule extends Rule { /** MergeRule Constructor merges to board states together */ public MergeRule() { @@ -27,7 +23,7 @@ public MergeRule() { /** * Checks whether the transition logically follows from the parent node using this rule. This - * method is the one that should have overridden in child classes + * method is the one that should overridden in child classes * * @param transition transition to check * @return null if the child node logically follow from the parent node, otherwise error message diff --git a/src/main/java/edu/rpi/legup/model/rules/RegisterRule.java b/src/main/java/edu/rpi/legup/model/rules/RegisterRule.java index d357e0d86..c1fe0b88c 100644 --- a/src/main/java/edu/rpi/legup/model/rules/RegisterRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/RegisterRule.java @@ -2,11 +2,6 @@ import java.lang.annotation.*; -/** - * Annotation to register a class as a rule in the system. - * This annotation can be applied to rule classes to indicate that they should be registered - * in the rule system. - */ @Inherited @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) diff --git a/src/main/java/edu/rpi/legup/model/rules/Rule.java b/src/main/java/edu/rpi/legup/model/rules/Rule.java index 107e72712..f70bb2889 100644 --- a/src/main/java/edu/rpi/legup/model/rules/Rule.java +++ b/src/main/java/edu/rpi/legup/model/rules/Rule.java @@ -8,12 +8,6 @@ import java.awt.image.BufferedImage; import javax.swing.ImageIcon; -/** - * Abstract base class for defining rules. - * This class encapsulates the common functionality and attributes of all rules, - * including rule identification, description, image handling, and validation logic. - * Subclasses must provide implementations for specific rule checking logic. - */ @RegisterRule public abstract class Rule { protected String ruleID; @@ -165,11 +159,6 @@ public RuleType getRuleType() { return ruleType; } - /** - * Gets the message indicating an invalid use of the rule. - * - * @return the invalid use message - */ public String getInvalidUseOfRuleMessage() { return this.INVALID_USE_MESSAGE; } diff --git a/src/main/java/edu/rpi/legup/model/rules/RuleType.java b/src/main/java/edu/rpi/legup/model/rules/RuleType.java index e72118e3c..06aa1844b 100644 --- a/src/main/java/edu/rpi/legup/model/rules/RuleType.java +++ b/src/main/java/edu/rpi/legup/model/rules/RuleType.java @@ -1,9 +1,5 @@ package edu.rpi.legup.model.rules; -/** - * Enumeration representing different types of rules in the rule-based system. - * This enum categorizes rules into various types based on their functionality and application. - */ public enum RuleType { BASIC, CASE, diff --git a/src/main/java/edu/rpi/legup/model/tree/Tree.java b/src/main/java/edu/rpi/legup/model/tree/Tree.java index 3e68015a1..a0746db87 100644 --- a/src/main/java/edu/rpi/legup/model/tree/Tree.java +++ b/src/main/java/edu/rpi/legup/model/tree/Tree.java @@ -1,20 +1,11 @@ package edu.rpi.legup.model.tree; -import edu.rpi.legup.controller.TreeController; import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.rules.CaseRule; -import edu.rpi.legup.ui.proofeditorui.treeview.TreeView; - import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -/** - * Represents a tree structure in a puzzle. - * The tree consists of {@link TreeNode}s and {@link TreeTransition}s - * and allows adding, removing, and validating elements. - */ public class Tree { private TreeNode rootNode; @@ -33,12 +24,6 @@ public Tree() { this.rootNode = null; } - /** - * Adds a new transition to the specified node. - * - * @param treeNode the node to add a transition to - * @return the created transition - */ public TreeTransition addNewTransition(TreeNode treeNode) { TreeTransition transition = new TreeTransition(treeNode, treeNode.getBoard().copy()); treeNode.addChild(transition); @@ -46,12 +31,13 @@ public TreeTransition addNewTransition(TreeNode treeNode) { return transition; } - /** - * Adds a tree element (node or transition) to the tree. - * - * @param element the tree element to add - * @return the added tree element - */ + public TreeNode addNode(TreeTransition transition) { + TreeNode treeNode = new TreeNode(transition.getBoard().copy()); + transition.setChildNode(treeNode); + treeNode.setParent(transition); + return treeNode; + } + public TreeElement addTreeElement(TreeElement element) { if (element.getType() == TreeElementType.NODE) { TreeNode treeNode = (TreeNode) element; @@ -60,55 +46,30 @@ public TreeElement addTreeElement(TreeElement element) { } else { TreeTransition transition = (TreeTransition) element; Board copyBoard = transition.board.copy(); - copyBoard.setModifiable(true); + copyBoard.setModifiable(false); return addTreeElement(transition, new TreeNode(copyBoard)); } } - /** - * Adds a tree node and its associated transition to the tree. - * - * @param treeNode the tree node to add - * @param transition the transition to associate with the node - * @return the added transition - */ public TreeElement addTreeElement(TreeNode treeNode, TreeTransition transition) { treeNode.addChild(transition); treeNode.getChildren().forEach(TreeTransition::reverify); return transition; } - /** - * Adds a transition and its associated tree node to the tree. - * - * @param transition the transition to add - * @param treeNode the tree node to associate with the transition - * @return the added tree node - */ public TreeElement addTreeElement(TreeTransition transition, TreeNode treeNode) { transition.setChildNode(treeNode); treeNode.setParent(transition); return treeNode; } - /** - * Removes a tree element (node or transition) from the tree. - * - * @param element the tree element to remove - */ public void removeTreeElement(TreeElement element) { if (element.getType() == TreeElementType.NODE) { TreeNode node = (TreeNode) element; - - node.getParent().removeChild(node); node.getParent().setChildNode(null); } else { TreeTransition transition = (TreeTransition) element; - transition.getParents().forEach(n -> n.removeChild(transition)); - TreeController treeController = new TreeController(); - TreeView treeView = new TreeView(treeController); - treeView.removeTreeTransition(transition); transition.getParents().get(0).getChildren().forEach(TreeTransition::reverify); } } @@ -135,10 +96,10 @@ public Set getLeafTreeElements() { } /** - * Gets a Set of TreeNodes that are leaf nodes from the subtree rooted at the specified node + * Gets a Set of TreeNodes that are leaf nodes from the sub tree rooted at the specified node * * @param node node that is input - * @return Set of TreeNodes that are leaf nodes from the subtree + * @return Set of TreeNodes that are leaf nodes from the sub tree */ public Set getLeafTreeElements(TreeNode node) { Set leafs = new HashSet<>(); diff --git a/src/main/java/edu/rpi/legup/model/tree/TreeElement.java b/src/main/java/edu/rpi/legup/model/tree/TreeElement.java index 2f6f45e4d..59f75acf3 100644 --- a/src/main/java/edu/rpi/legup/model/tree/TreeElement.java +++ b/src/main/java/edu/rpi/legup/model/tree/TreeElement.java @@ -2,9 +2,6 @@ import edu.rpi.legup.model.gameboard.Board; -/** - * Represents an element in a tree structure, which can be either a {@link TreeNode} or a {@link TreeTransition}. - */ public abstract class TreeElement { protected TreeElementType type; protected Board board; @@ -27,7 +24,7 @@ public TreeElement(TreeElementType type) { public abstract boolean isContradictoryBranch(); /** - * Recursively determines if the subtree rooted at this tree puzzleElement is valid by checking + * Recursively determines if the sub-tree rooted at this tree puzzleElement is valid by checking * whether this tree puzzleElement and all descendants of this tree puzzleElement is justified * and justified correctly * diff --git a/src/main/java/edu/rpi/legup/model/tree/TreeElementType.java b/src/main/java/edu/rpi/legup/model/tree/TreeElementType.java index 3a6dbb124..67437a535 100644 --- a/src/main/java/edu/rpi/legup/model/tree/TreeElementType.java +++ b/src/main/java/edu/rpi/legup/model/tree/TreeElementType.java @@ -1,9 +1,5 @@ package edu.rpi.legup.model.tree; -/** - * Enum representing the type of a tree element. - * Tree elements can be either nodes or transitions in the tree structure. - */ public enum TreeElementType { NODE, TRANSITION diff --git a/src/main/java/edu/rpi/legup/model/tree/TreeNode.java b/src/main/java/edu/rpi/legup/model/tree/TreeNode.java index 85a5e717b..a2ac7cb21 100644 --- a/src/main/java/edu/rpi/legup/model/tree/TreeNode.java +++ b/src/main/java/edu/rpi/legup/model/tree/TreeNode.java @@ -4,11 +4,6 @@ import edu.rpi.legup.utility.DisjointSets; import java.util.*; -/** - * Represents a node in a tree structure. Extends {@link TreeElement}. - * A {@code TreeNode} contains a board, references to its parent and children transitions, and indicates - * if it is the root node of the tree. - */ public class TreeNode extends TreeElement { private TreeTransition parent; private List children; @@ -61,9 +56,9 @@ public boolean isValidBranch() { } /** - * Gets a list of the ancestors of this node + * Gets all of the ancestors of this node * - * @return list of all the ancestors for this node + * @return list of all of the ancestors for this node */ public List getAncestors() { List ancestors = new ArrayList<>(); @@ -331,10 +326,6 @@ public void setRoot(boolean isRoot) { this.isRoot = isRoot; } - /** - * Clears all children transitions from this tree node. - * After calling this method, the node will have no child transitions. - */ public void clearChildren() { this.children.clear(); } diff --git a/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java b/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java index 9e441ac55..e79cd4b96 100644 --- a/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java +++ b/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java @@ -8,11 +8,6 @@ import java.util.ArrayList; import java.util.List; -/** - * Represents a transition between two nodes in a tree structure within a game. - * A transition is responsible for propagating changes through the tree and managing - * and verifying the associated rules and puzzle elements. - */ public class TreeTransition extends TreeElement { private ArrayList parents; private TreeNode childNode; @@ -337,25 +332,6 @@ public void setChildNode(TreeNode childNode) { this.childNode = childNode; } - /** - * Removes the child to this tree transition - * - * @param child child to remove - */ - public void removeChild(TreeNode child) { - parents.remove(child); - } - - /** - * Add the child to this tree transition - * - * @param child child to add - */ - public void addChild(TreeNode child) { - parents.add(child); - } - - /** * Gets the rule associated with this transition * diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java index d2dd0b181..773513cda 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java @@ -36,12 +36,18 @@ public Board generatePuzzle(int difficulty) { return null; } - /** - * Determines if the current board is a valid state - * - * @param board board to check for validity - * @return true if board is valid, false otherwise - */ + // /** + // * Determines if the given dimensions are valid for Binary + // * + // * @param rows the number of rows + // * @param columns the number of columns + // * @return true if the given dimensions are valid for Binary, false otherwise + // */ + // @Override + // public boolean isValidDimensions(int rows, int columns){ + // return rows >= 2 && rows % 2 == 0 && columns >= 2 && columns % 2 == 0; + // } + @Override public boolean isBoardComplete(Board board) { BinaryBoard binaryBoard = (BinaryBoard) board; @@ -60,24 +66,6 @@ public boolean isBoardComplete(Board board) { return true; } - /** - * Callback for when the board puzzleElement changes - * - * @param board the board that has changed - */ @Override public void onBoardChange(Board board) {} - - - /** - * Determines if the given dimensions are valid for Binary - * - * @param rows the number of rows - * @param columns the number of columns - * @return true if the given dimensions are valid for Binary, false otherwise - */ - @Override - public boolean isValidDimensions(int rows, int columns){ - return rows >= 2 && rows % 2 == 0 && rows == columns; - } } diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java index 34819410b..35c37b1a1 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryBoard.java @@ -2,7 +2,7 @@ import edu.rpi.legup.model.gameboard.GridBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; - +import java.awt.*; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; @@ -20,13 +20,6 @@ public BinaryBoard(int size) { this.size = size; } - /** - * Gets the cell at the (x,y) position - * - * @param x x-coordinate - * @param y y-coordinate - * @return BinaryCell cell at (x,y) - */ @Override public BinaryCell getCell(int x, int y) { if (y * dimension.width + x >= puzzleElements.size() @@ -39,12 +32,6 @@ public BinaryCell getCell(int x, int y) { return (BinaryCell) super.getCell(x, y); } - /** - * Get all the binary cells in a row - * - * @param rowNum row number - * @return set of all binary cells in specified rowNum - */ public Set getRowCells(int rowNum) { Set row = new HashSet<>(); for (int i = 0; i < size; i++) { @@ -54,26 +41,6 @@ public Set getRowCells(int rowNum) { return row; } - /** - * Get all the binary cells in a column - * - * @param colNum column number - * @return set of all binary cells in specified colNum - */ - public Set getColCells(int colNum) { - Set col = new HashSet<>(); - for (int i = 0; i < size; i++) { - col.add(getCell(colNum, i)); - } - return col; - } - - /** - * Get all the binary types in a row - * - * @param rowNum row number - * @return ArrayList of all binary types in specified rowNum - */ public ArrayList getRowTypes(int rowNum) { ArrayList row = new ArrayList(); for (int i = 0; i < size; i++) { @@ -83,12 +50,6 @@ public ArrayList getRowTypes(int rowNum) { return row; } - /** - * Get all the binary types in a column - * - * @param colNum column number - * @return ArrayList of all binary types in specified colNum - */ public ArrayList getColTypes(int colNum) { ArrayList col = new ArrayList(); for (int i = 0; i < size; i++) { @@ -98,12 +59,17 @@ public ArrayList getColTypes(int colNum) { return col; } - /** - * Get a copy of the binary board - * @return copy of current BinaryBoard - */ + public Set getCol(int colNum) { + Set col = new HashSet<>(); + for (int i = 0; i < size; i++) { + col.add(getCell(colNum, i)); + } + return col; + } + @Override public BinaryBoard copy() { + System.out.println("BinaryBoard copy()"); BinaryBoard copy = new BinaryBoard(dimension.width, dimension.height); for (int x = 0; x < this.dimension.width; x++) { for (int y = 0; y < this.dimension.height; y++) { diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java index d09f7115e..9007215ad 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java @@ -1,27 +1,13 @@ package edu.rpi.legup.puzzle.binary; -import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.GridCell; import java.awt.Point; -import java.awt.event.MouseEvent; - public class BinaryCell extends GridCell { - /** - * BinaryCell Constructor - creates a BinaryCell from the specified value and location - * - * @param value value of the BinaryCell - * @param location position of the BinaryCell - */ - public BinaryCell(int value, Point location) { - super(value, location); + public BinaryCell(int valueInt, Point location) { + super(valueInt, location); } - /** - * Gets the type of this BinaryCell - * - * @return type of BinaryCell - */ public BinaryType getType() { switch (data) { case 0: @@ -38,11 +24,6 @@ public BinaryType getType() { return null; } - /** - * Performs a deep copy on the BinaryCell - * - * @return a new copy of the BinaryCell that is independent of this one - */ @Override public BinaryCell copy() { BinaryCell copy = new BinaryCell(data, (Point) location.clone()); @@ -51,36 +32,4 @@ public BinaryCell copy() { copy.setGiven(isGiven); return copy; } - - /** - * Sets the type of this BinaryCell - * - * @param e element to set the type of this binary cell to - */ - @Override - public void setType(Element e, MouseEvent m) { - if (e.getElementName().equals("Number Tile")) { - if (m.getButton() == MouseEvent.BUTTON1) { - if (this.data == 2) { - this.data = 0; - } - else { - this.data = this.data + 1; - } - } - else { - if (m.getButton() == MouseEvent.BUTTON3) { - if (this.data > 0) { - this.data = this.data - 1; - } - else { - this.data = 2; - } - } - } - } - else { // unknown tile - this.data = 2; - } - } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java index a819177d6..890c26656 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java @@ -10,14 +10,7 @@ import org.w3c.dom.Node; public class BinaryCellFactory extends ElementFactory { - /** - * Creates a puzzleElement based on the xml document Node and adds it to the board - * - * @param node node that represents the puzzleElement - * @param board board to add the newly created cell - * @return newly created cell from the xml document Node - * @throws InvalidFileFormatException if file is invalid - */ + public BinaryCell importCell(Node node, Board board) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("cell")) { @@ -52,13 +45,6 @@ public BinaryCell importCell(Node node, Board board) throws InvalidFileFormatExc } } - /** - * Creates a xml document puzzleElement from a cell for exporting - * - * @param document xml document - * @param puzzleElement PuzzleElement cell - * @return xml PuzzleElement - */ public org.w3c.dom.Element exportCell(Document document, PuzzleElement puzzleElement) { org.w3c.dom.Element cellElement = document.createElement("cell"); diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryController.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryController.java index caf62f3fe..0bad559d9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryController.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryController.java @@ -6,16 +6,6 @@ public class BinaryController extends ElementController { - /** - * Handles cell state changes in the binary puzzle when a mouse event occurs - * If the left mouse button is clicked: - * - If the control key is held down, shows a context menu at the mouse position - * - Otherwise, toggles the cell data state between 0, 1, and 2 in a cyclic manner - * If the right mouse button is clicked, the cell state is also toggled between 2, 1, and 0 - * - * @param e MouseEvent triggered by the user interaction - * @param data PuzzleElement representing the cell being modified - */ @Override public void changeCell(MouseEvent e, PuzzleElement data) { BinaryCell cell = (BinaryCell) data; @@ -40,13 +30,13 @@ public void changeCell(MouseEvent e, PuzzleElement data) { } } else { if (e.getButton() == MouseEvent.BUTTON3) { - if (cell.getData() == 2) { + if (cell.getData() == 0) { data.setData(1); } else { if (cell.getData() == 1) { - data.setData(0); - } else { data.setData(2); + } else { + data.setData(0); } } } diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryElementView.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryElementView.java index f5ea33d4a..9ac99c958 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryElementView.java @@ -7,8 +7,6 @@ public class BinaryElementView extends GridElementView { private static final Font FONT = new Font("TimesRoman", Font.BOLD, 17); private static final Color FONT_COLOR = Color.BLACK; - private static final Color GIVEN_COLOR = Color.LIGHT_GRAY; - private static final Color ELEMENT_COLOR = Color.WHITE; public BinaryElementView(BinaryCell cell) { super(cell); @@ -24,66 +22,99 @@ public BinaryCell getPuzzleElement() { return (BinaryCell) super.getPuzzleElement(); } - - /** - * Draws the cells provided in the puzzle's .xml file with light gray background - * - * @param graphics2D The graphics object to draw on - */ @Override public void drawGiven(Graphics2D graphics2D) { - drawCell(graphics2D, GIVEN_COLOR); + BinaryCell cell = (BinaryCell) puzzleElement; + BinaryType type = cell.getType(); + if (type == BinaryType.ZERO) { + graphics2D.setStroke(new BasicStroke(1)); + graphics2D.setColor(Color.LIGHT_GRAY); + graphics2D.fillRect(location.x, location.y, size.width, size.height); + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + graphics2D.setColor(FONT_COLOR); + graphics2D.setFont(FONT); + FontMetrics metrics = graphics2D.getFontMetrics(FONT); + String value = String.valueOf(puzzleElement.getData()); + int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; + int yText = + location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); + graphics2D.drawString(String.valueOf(puzzleElement.getData()), xText, yText); + } else { + if (type == BinaryType.ONE) { + graphics2D.setStroke(new BasicStroke(1)); + graphics2D.setColor(Color.LIGHT_GRAY); + graphics2D.fillRect(location.x, location.y, size.width, size.height); + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + graphics2D.setColor(FONT_COLOR); + graphics2D.setFont(FONT); + FontMetrics metrics = graphics2D.getFontMetrics(FONT); + String value = String.valueOf(puzzleElement.getData()); + int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; + int yText = + location.y + + ((size.height - metrics.getHeight()) / 2) + + metrics.getAscent(); + graphics2D.drawString(String.valueOf(puzzleElement.getData()), xText, yText); + + } else { + if (type == BinaryType.UNKNOWN) { + graphics2D.setStroke(new BasicStroke(0)); + graphics2D.setColor(Color.WHITE); + graphics2D.fillRect(location.x, location.y, size.width, size.height); + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + } + } + } } - /** - * Draws new cells being added to board with white background - * - * @param graphics2D The graphics object to draw on - */ @Override public void drawElement(Graphics2D graphics2D) { - drawCell(graphics2D, ELEMENT_COLOR); - } - - /** - * Helper method to handle drawing the cell based on its type and background color - * - * @param graphics2D The graphics object to draw on - * @param bgColor The background color for the cell - */ - private void drawCell(Graphics2D graphics2D, Color bgColor) { BinaryCell cell = (BinaryCell) puzzleElement; BinaryType type = cell.getType(); - - if (type == BinaryType.ZERO || type == BinaryType.ONE) { + if (type == BinaryType.ZERO) { graphics2D.setStroke(new BasicStroke(1)); - graphics2D.setColor(bgColor); - graphics2D.fillRect(location.x, location.y, size.width, size.height); - graphics2D.setColor(Color.BLACK); - graphics2D.drawRect(location.x, location.y, size.width, size.height); - drawCenteredText(graphics2D); - } else if (type == BinaryType.UNKNOWN) { - graphics2D.setStroke(new BasicStroke(0)); graphics2D.setColor(Color.WHITE); graphics2D.fillRect(location.x, location.y, size.width, size.height); graphics2D.setColor(Color.BLACK); graphics2D.drawRect(location.x, location.y, size.width, size.height); - } - } + graphics2D.setColor(FONT_COLOR); + graphics2D.setFont(FONT); + FontMetrics metrics = graphics2D.getFontMetrics(FONT); + String value = String.valueOf(puzzleElement.getData()); + int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; + int yText = + location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); + graphics2D.drawString(String.valueOf(puzzleElement.getData()), xText, yText); + } else { + if (type == BinaryType.ONE) { + graphics2D.setStroke(new BasicStroke(1)); + graphics2D.setColor(Color.WHITE); + graphics2D.fillRect(location.x, location.y, size.width, size.height); + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + graphics2D.setColor(FONT_COLOR); + graphics2D.setFont(FONT); + FontMetrics metrics = graphics2D.getFontMetrics(FONT); + String value = String.valueOf(puzzleElement.getData()); + int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; + int yText = + location.y + + ((size.height - metrics.getHeight()) / 2) + + metrics.getAscent(); + graphics2D.drawString(String.valueOf(puzzleElement.getData()), xText, yText); - /** - * Helper method to draw the centered text within the cell - * - * @param graphics2D The graphics object to draw on - */ - private void drawCenteredText(Graphics2D graphics2D) { - graphics2D.setColor(FONT_COLOR); - graphics2D.setFont(FONT); - FontMetrics metrics = graphics2D.getFontMetrics(FONT); - String value = String.valueOf(puzzleElement.getData()); - int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; - int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); - graphics2D.drawString(value, xText, yText); + } else { + if (type == BinaryType.UNKNOWN) { + graphics2D.setStroke(new BasicStroke(0)); + graphics2D.setColor(Color.WHITE); + graphics2D.fillRect(location.x, location.y, size.width, size.height); + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + } + } + } } } - diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java index f12a07378..cd58314b6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java @@ -10,13 +10,6 @@ public BinaryExporter(Binary binary) { super(binary); } - /** - * Generates an XML element for the binary puzzle board, including its dimensions and the - * state of each cell. Binary cells that are not in the `UNKNOWN` state are included in the XML. - * - * @param newDocument The XML document to which the board element belongs. - * @return The XML element representing the board. - */ @Override protected org.w3c.dom.Element createBoardElement(Document newDocument) { BinaryBoard board; @@ -33,7 +26,7 @@ protected org.w3c.dom.Element createBoardElement(Document newDocument) { org.w3c.dom.Element cellsElement = newDocument.createElement("cells"); for (PuzzleElement puzzleElement : board.getPuzzleElements()) { BinaryCell cell = (BinaryCell) puzzleElement; - if (cell.getData() != BinaryType.UNKNOWN.toValue()) { + if (cell.getData() != -2) { org.w3c.dom.Element cellElement = puzzle.getFactory().exportCell(newDocument, puzzleElement); cellsElement.appendChild(cellElement); diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java index 8a4bad01e..2fc5b09ef 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java @@ -12,33 +12,16 @@ public BinaryImporter(Binary binary) { super(binary); } - /** - * Determines if puzzle uses row and column input - * - * @return true if row and column input is used, false otherwise - */ @Override public boolean acceptsRowsAndColumnsInput() { return true; } - /** - * Determines if puzzle uses text input - * - * @return true if text input is used, false otherwise - */ @Override public boolean acceptsTextInput() { return false; } - /** - * Creates an empty board for building - * - * @param rows the number of rows on the board - * @param columns the number of columns on the board - * @throws RuntimeException if board can not be created - */ @Override public void initializeBoard(int rows, int columns) { BinaryBoard binaryBoard = new BinaryBoard(columns, rows); @@ -65,12 +48,12 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { try { if (!node.getNodeName().equalsIgnoreCase("board")) { throw new InvalidFileFormatException( - "Binary Importer: cannot find board puzzleElement"); + "binary Importer: cannot find board puzzleElement"); } Element boardElement = (Element) node; if (boardElement.getElementsByTagName("cells").getLength() == 0) { throw new InvalidFileFormatException( - "Binary Importer: no puzzleElement found for board"); + "binary Importer: no puzzleElement found for board"); } Element dataElement = (Element) boardElement.getElementsByTagName("cells").item(0); @@ -93,7 +76,7 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { int height = binaryBoard.getHeight(); if (binaryBoard == null || width % 2 != 0 || height % 2 != 0) { - throw new InvalidFileFormatException("Binary Importer: invalid board dimensions"); + throw new InvalidFileFormatException("binary Importer: invalid board dimensions"); } for (int i = 0; i < elementDataList.getLength(); i++) { @@ -127,11 +110,6 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { } } - /** - * Initializes a board with text - * @param statements the text being used - * @throws UnsupportedOperationException Binary does not use text input - */ @Override public void initializeBoard(String[] statements) throws UnsupportedOperationException { throw new UnsupportedOperationException("Binary cannot accept text input"); diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryType.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryType.java index f03f2ee08..6e3413d7a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryType.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryType.java @@ -1,23 +1,10 @@ package edu.rpi.legup.puzzle.binary; -/** - * Enum representing the possible states of a binary puzzle cell - * - * The states include: - * - ZERO: Represents a cell with a value of 0 - * - ONE: Represents a cell with a value of 1 - * - UNKNOWN: Represents an empty cell - * - */ public enum BinaryType { - ZERO, // Enum constant 0 - ONE, // Enum constant 1 - UNKNOWN; // Enum constant 2 + ZERO, + ONE, + UNKNOWN; - /** - * The `toValue` method returns the ordinal value of the enum constant, - * which can be used to convert the enum to an integer representation. - */ public int toValue() { return this.ordinal(); } diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryView.java b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryView.java index e1869de6b..b11554f28 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryView.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryView.java @@ -7,13 +7,6 @@ public class BinaryView extends GridBoardView { - /** Creates and arranges the visual components for each cell in the binary puzzle. Initializes - * the view by setting up the board controller, binary controller, and the grid dimensions. - * For each cell in the BinaryBoard, it creates a corresponding BinaryElementView, sets its index, - * size, and location, and adds it to the list of element views to be displayed. - * - * @param board The BinaryBoard representing the current state of the binary puzzle - */ public BinaryView(BinaryBoard board) { super(new BoardController(), new BinaryController(), board.getDimension()); diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java b/src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java @@ -0,0 +1 @@ + diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/binary/elements/NumberTile.java index e996e246b..8b1378917 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/elements/NumberTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/elements/NumberTile.java @@ -1,14 +1 @@ -package edu.rpi.legup.puzzle.binary.elements; - -import edu.rpi.legup.model.elements.PlaceableElement; - -public class NumberTile extends PlaceableElement { - public NumberTile() { - super( - "BINA-ELEM-0001", - "Number Tile", - "A number tile", - "edu/rpi/legup/images/binary/tiles/NumberTile.png"); - } -} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/binary/elements/UnknownTile.java deleted file mode 100644 index 8c60ea8c3..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/binary/elements/UnknownTile.java +++ /dev/null @@ -1,13 +0,0 @@ -package edu.rpi.legup.puzzle.binary.elements; - -import edu.rpi.legup.model.elements.PlaceableElement; - -public class UnknownTile extends PlaceableElement { - public UnknownTile() { - super( - "BINA-ELEM-0002", - "Unknown Tile", - "A blank tile", - "edu/rpi/legup/images/binary/tiles/UnknownTile.png"); - } -} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/elements/binary_elements_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/binary/elements/binary_elements_reference_sheet.txt deleted file mode 100644 index 54db0ee0b..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/binary/elements/binary_elements_reference_sheet.txt +++ /dev/null @@ -1,2 +0,0 @@ -BINA-ELEM-0001 : NumberTile -BINA-ELEM-0002 : UnknownTile \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/CompleteRowColumnDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/CompleteRowColumnDirectRule.java index 359433685..e38c6b78d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/CompleteRowColumnDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/CompleteRowColumnDirectRule.java @@ -14,9 +14,8 @@ public class CompleteRowColumnDirectRule extends DirectRule { public CompleteRowColumnDirectRule() { super( "BINA-BASC-0003", - "Complete Row/Column", - "If a row/column of length n contains n/2 of a single value, the remaining " + - "cells must contain the other value", + "Complete Row Column", + "If a row/column of length n contains n/2 of a single value, the remaining cells must contain the other value", "edu/rpi/legup/images/binary/rules/CompleteRowColumnDirectRule.png"); } @@ -32,27 +31,23 @@ public CompleteRowColumnDirectRule() { @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { BinaryBoard origBoard = (BinaryBoard) transition.getParents().get(0).getBoard(); - ContradictionRule contraRule = new UnbalancedRowColumnContradictionRule(); + ContradictionRule contraRule = new UnbalancedRowOrColumnContradictionRule(); BinaryCell binaryCell = (BinaryCell) puzzleElement; BinaryBoard modified = origBoard.copy(); + BinaryCell c = (BinaryCell) modified.getPuzzleElement(puzzleElement); - // Flip the cell and check to see if there will be an unbalanced row/column contradiction, - // if so the rule is applied correctly - modified.getPuzzleElement(puzzleElement).setData(Math.abs(binaryCell.getData() - 1)); - if (contraRule.checkContradictionAt(modified, puzzleElement) == null) { + // System.out.println("ORIG" + binaryCell.getData()); + // System.out.println("AFTER" + Math.abs(binaryCell.getData() - 1)); + modified.getPuzzleElement(puzzleElement).setData(binaryCell.getData()); + System.out.println(contraRule.checkContradictionAt(modified, puzzleElement)); + + if (contraRule.checkContradictionAt(modified, puzzleElement) != null) { return null; } - return "Unbalanced row/column found"; + return "Grouping of Three Ones or Zeros not found"; } - /** - * Creates a transition {@link Board} that has this rule applied to it using the {@link - * TreeNode}. - * - * @param node tree node used to create default transition board - * @return default board or null if this rule cannot be applied to this tree node - */ @Override public Board getDefaultBoard(TreeNode node) { return null; diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/RepeatedRowColumnContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/DuplicateRowsOrColumnsContradictionRule.java similarity index 60% rename from src/main/java/edu/rpi/legup/puzzle/binary/rules/RepeatedRowColumnContradictionRule.java rename to src/main/java/edu/rpi/legup/puzzle/binary/rules/DuplicateRowsOrColumnsContradictionRule.java index 13eb35283..8b0d88ae4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/RepeatedRowColumnContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/DuplicateRowsOrColumnsContradictionRule.java @@ -8,38 +8,30 @@ import edu.rpi.legup.puzzle.binary.BinaryType; import java.util.ArrayList; -public class RepeatedRowColumnContradictionRule extends ContradictionRule { +public class DuplicateRowsOrColumnsContradictionRule extends ContradictionRule { private final String NO_CONTRADICTION_MESSAGE = "Does not contain a contradiction at this index"; private final String INVALID_USE_MESSAGE = "Row or column must have a value in each cell"; - public RepeatedRowColumnContradictionRule() { + public DuplicateRowsOrColumnsContradictionRule() { super( "BINA-CONT-0003", - "Repeated Row/Column", - "There must not be two of the same row or two of the same column in the puzzle", - "edu/rpi/legup/images/binary/rules/RepeatedRowColumnContradictionRule.png"); + "Duplicate Rows Or Columns", + "There must not be two rows or two columns that are duplicates", + "edu/rpi/legup/images/binary/rules/DuplicateRowOrColumnContradictionRule.png"); } - /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using - * this rule - * - * @param board board to check contradiction - * @param puzzleElement equivalent puzzleElement - * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message - */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { BinaryBoard binaryBoard = (BinaryBoard) board; BinaryCell cell = (BinaryCell) binaryBoard.getPuzzleElement(puzzleElement); - // Compare each row with row of current cell to see if they are equal, if so the rule is applied correctly ArrayList row = binaryBoard.getRowTypes(cell.getLocation().y); + int size = row.size(); + for (int i = 0; i < size; i++) { - if (i != cell.getLocation().y) { + if (i > cell.getLocation().y) { ArrayList currRow = binaryBoard.getRowTypes(i); if (currRow.equals(row)) { return null; @@ -47,10 +39,10 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } } - // Compare each column with column of current cell to see if they are equal, if so the rule is applied correctly ArrayList col = binaryBoard.getColTypes(cell.getLocation().x); + for (int i = 0; i < size; i++) { - if (i != cell.getLocation().x) { + if (i > cell.getLocation().x) { ArrayList currCol = binaryBoard.getColTypes(i); if (currCol.equals(col)) { return null; diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/EliminateTheImpossibleDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/EliminateTheImpossibleDirectRule.java deleted file mode 100644 index 02c1c2c31..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/EliminateTheImpossibleDirectRule.java +++ /dev/null @@ -1,201 +0,0 @@ -//package edu.rpi.legup.puzzle.binary.rules; -// -//import edu.rpi.legup.model.gameboard.Board; -//import edu.rpi.legup.model.gameboard.PuzzleElement; -//import edu.rpi.legup.model.rules.DirectRule; -//import edu.rpi.legup.model.tree.TreeNode; -//import edu.rpi.legup.model.tree.TreeTransition; -//import edu.rpi.legup.puzzle.binary.BinaryBoard; -//import edu.rpi.legup.puzzle.binary.BinaryCell; -//import edu.rpi.legup.puzzle.binary.BinaryType; -// -//import java.util.LinkedList; -//import java.util.Queue; -//import java.lang.Math.*; -//import java.lang.reflect.Array; -//import java.util.ArrayList; -// -//public class EliminateTheImpossibleDirectRule extends DirectRule { -// private final String INVALID_USE_MESSAGE = "Number at cell is incorrect"; -// -// public EliminateTheImpossibleDirectRule() { -// super( -// "BINA-BASC-0004", -// "Eliminate The Impossible", -// "Out of the remaining empty cells in this row or column, this digit must go here, otherwise there will be a future contradiction", -// "edu/rpi/legup/images/binary/rules/EliminateTheImpossibleDirectRule.png"); -// } -// -// // Function to generate all binary strings -// void generatePossibilitites(int spots, ArrayList possibilities, int zeroCount, int oneCount) -// // This function generates all the possible combinations of 0s and 1s for a -// // certain size, it does this -// // by basically just counting from 0 to the number - 1, so if you want all the -// // possible combinations for 3 -// // spots, you can just count in binary from 0 to 7 (taking 3 spots, so from 000 -// // to 111). To be practical, -// // the function does not return an array with all the possibilities as an array, -// // but populates the -// // arraylist you pass in (possibilities) -// { -// if (zeroCount + oneCount != spots) { -// System.out.println("INVALID INPUT"); -// return; -// } -// -// if (zeroCount == spots) { -// String zero = ""; -// for (int i = 0; i < spots; i++) { -// zero = zero + "0"; -// } -// possibilities.add(zero); -// -// } -// int count = (int) Math.pow(2, spots) - 1; -// int finalLen = spots; -// Queue q = new LinkedList(); -// q.add("1"); -// -// while (count-- > 0) { -// String s1 = q.peek(); -// q.remove(); -// -// String newS1 = s1; -// int curLen = newS1.length(); -// int runFor = spots - curLen; -// if (curLen < finalLen) { -// for (int i = 0; i < runFor; i++) { -// newS1 = "0" + newS1; -// } -// } -// int curZeros = 0; -// int curOnes = 0; -// -// for (int i = 0; i < spots; i++) { -// if (newS1.charAt(i) == '0') { -// curZeros++; -// } -// if (newS1.charAt(i) == '1') { -// curOnes++; -// } -// } -// -// if (zeroCount == curZeros && oneCount == curOnes) { -// possibilities.add(newS1); -// } -// String s2 = s1; -// q.add(s1 + "0"); -// q.add(s2 + "1"); -// } -// } -// -// @Override -// public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { -// // This function should first check if there are three open spaces, if so, -// // continue, else figure out -// // how many spots are open, all the possible binary combinations that could be -// // put there, and by -// // analyzing the common factors, logically determine which number has a set -// // spot, meaning that we know -// // that a certain spot must be a zero or a one -// -// BinaryBoard origBoard = (BinaryBoard) transition.getParents().get(0).getBoard(); -// BinaryCell binaryCell = (BinaryCell) puzzleElement; -// -// //Getting the row where the user clicked -// ArrayList row = origBoard.listRowCells(binaryCell.getLocation().y); -// int size = row.size(); -// int rowNumZeros = 0; -// int rowNumOnes = 0; -// -// for (BinaryCell item : row) { -// if (item.getType() == BinaryType.ZERO) { -// rowNumZeros++; -// } else if (item.getType() == BinaryType.ONE) { -// rowNumOnes++; -// } -// } -// -// ArrayList rowResult = new ArrayList(); -// -// // To call generatePossibilitites(), you must call it and pass in the amount of -// // unknown spots left, -// // an ArrayList that will be populated with the possible results (in String -// // form), the amount of zeros left and ones left -// generatePossibilitites((size - rowNumZeros - rowNumOnes), rowResult, size / 2 - rowNumZeros, size / 2 - rowNumOnes); -// -// // Create deep copies of each row -// ArrayList> rowCopies = new ArrayList<>(); -// for (int i = 0; i < rowResult.size(); i++) { -// ArrayList newRow = new ArrayList<>(); -// for (BinaryCell cell : row) { -// newRow.add(cell.copy()); -// } -// rowCopies.add(newRow); -// } -// -// System.out.println("Number of possible binary combinations: " + rowCopies.size()); -// -// ArrayList> nonContraRows = new ArrayList<>(); -// int rowIdx = 0; -// for(ArrayList curRow : rowCopies){ -// int charIdx = 0; -// System.out.println(rowResult.get(rowIdx)); -// for(int i = 0; i < curRow.size(); i++) { -// if (curRow.get(i).getData() == 2) { -// if (rowResult.get(rowIdx).charAt(charIdx) == '0') { -// curRow.get(i).setData(0); -// } -// else if (rowResult.get(rowIdx).charAt(charIdx) == '1') { -// curRow.get(i).setData(1); -// } -// charIdx++; -// } -// System.out.print(curRow.get(i).getData() + " "); -// } -// -// boolean threeAdjacent = false; -// int count = 1; -// for(int i = 1; i < curRow.size(); i++) { -// if (curRow.get(i).getData() == curRow.get(i-1).getData()) { -// count++; -// if (count == 3) { -// threeAdjacent = true; -// break; -// } -// } else { -// count = 1; -// } -// } -// -// if (!threeAdjacent) { -// nonContraRows.add(curRow); -// } -// -// rowIdx++; -// System.out.println(); -// } -// -// System.out.println("Number of non contradiction rows: " + nonContraRows.size()); -// int colNum = binaryCell.getLocation().x; -// boolean invalid = false; -// for(int i = 0; i < nonContraRows.size(); i++) { -// if (nonContraRows.get(i).get(colNum).getData() != binaryCell.getData()) { -// invalid = true; -// break; -// } -// } -// -// if (!invalid) { -// return null; -// } -// -// return "This cell can either be a 0 or a 1"; -// -// } -// -// @Override -// public Board getDefaultBoard(TreeNode node) { -// return null; -// } -//} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ZeroOrOneCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneOrZeroCaseRule.java similarity index 56% rename from src/main/java/edu/rpi/legup/puzzle/binary/rules/ZeroOrOneCaseRule.java rename to src/main/java/edu/rpi/legup/puzzle/binary/rules/OneOrZeroCaseRule.java index 3ae7a424d..70549cd72 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ZeroOrOneCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneOrZeroCaseRule.java @@ -11,23 +11,16 @@ import java.util.ArrayList; import java.util.List; -public class ZeroOrOneCaseRule extends CaseRule { +public class OneOrZeroCaseRule extends CaseRule { - public ZeroOrOneCaseRule() { + public OneOrZeroCaseRule() { super( "BINA-CASE-0001", - "Zero Or One", - "Each blank cell is either a zero or a one", - "edu/rpi/legup/images/binary/rules/ZeroOrOneCaseRule.png"); + "One or Zero", + "Each blank cell is either a one or a zero.", + "edu/rpi/legup/images/binary/rules/OneOrZeroCaseRule.png"); } - /** - * Checks whether the {@link TreeTransition} logically follows from the parent node using this - * rule. This method is the one that should be overridden in child classes. - * - * @param transition transition to check - * @return null if the child node logically follow from the parent node, otherwise error message - */ @Override public String checkRuleRaw(TreeTransition transition) { List childTransitions = transition.getParents().get(0).getChildren(); @@ -39,30 +32,26 @@ public String checkRuleRaw(TreeTransition transition) { TreeTransition case2 = childTransitions.get(1); if (case1.getBoard().getModifiedData().size() != 1 || case2.getBoard().getModifiedData().size() != 1) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must have 1 modified cell for each case."; + return super.getInvalidUseOfRuleMessage() + + ": This case rule must have 1 modified cell for each case."; } BinaryCell mod1 = (BinaryCell) case1.getBoard().getModifiedData().iterator().next(); BinaryCell mod2 = (BinaryCell) case2.getBoard().getModifiedData().iterator().next(); if (!mod1.getLocation().equals(mod2.getLocation())) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must modify the same cell for each case."; + return super.getInvalidUseOfRuleMessage() + + ": This case rule must modify the same cell for each case."; } if (!((mod1.getType() == BinaryType.ZERO && mod2.getType() == BinaryType.ONE) || (mod2.getType() == BinaryType.ZERO && mod1.getType() == BinaryType.ONE))) { - return super.getInvalidUseOfRuleMessage() + ": This case rule must modify an empty cell."; + return super.getInvalidUseOfRuleMessage() + + ": This case rule must an empty white and black cell."; } return null; } - /** - * Generates a {@link CaseBoard} that includes all blank cells from the given board that this - * case rule can be applied to - * - * @param board The board to find locations where this case rule can be applied - * @return A CaseBoard containing pickable elements where the case rule can be applied - */ @Override public CaseBoard getCaseBoard(Board board) { BinaryBoard binaryBoard = (BinaryBoard) board.copy(); @@ -76,44 +65,24 @@ public CaseBoard getCaseBoard(Board board) { return caseBoard; } - /** - * Gets the possible cases at a specific location based on this case rule - * - * @param board the current board state - * @param puzzleElement equivalent puzzleElement - * @return a list of elements the specified could be - */ @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { ArrayList cases = new ArrayList<>(); - if (puzzleElement == null) { - return cases; - } - Board case1 = board.copy(); PuzzleElement data1 = case1.getPuzzleElement(puzzleElement); - data1.setData(BinaryType.ONE.toValue()); + data1.setData(BinaryType.ZERO.toValue()); case1.addModifiedData(data1); cases.add(case1); Board case2 = board.copy(); PuzzleElement data2 = case2.getPuzzleElement(puzzleElement); - data2.setData(BinaryType.ZERO.toValue()); + data2.setData(BinaryType.ONE.toValue()); case2.addModifiedData(data2); cases.add(case2); return cases; } - /** - * Checks whether the child node logically follows from the parent node at the specific - * puzzleElement index using this rule - * - * @param transition transition to check - * @param puzzleElement equivalent puzzleElement - * @return null if the child node logically follow from the parent node at the specified - * puzzleElement, otherwise error message - */ @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { return null; diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java new file mode 100644 index 000000000..2e1e96fa5 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java @@ -0,0 +1,64 @@ +package edu.rpi.legup.puzzle.binary.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; +import edu.rpi.legup.model.rules.DirectRule; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; + +public class OneTileGapDirectRule extends DirectRule { + private final String INVALID_USE_MESSAGE = "Number at cell is incorrect"; + + public OneTileGapDirectRule() { + super( + "BINA-BASC-0002", + "One Tile Gap", + "If an empty tile is in between the same value, fill the gap with the other value.", + "edu/rpi/legup/images/binary/rules/OneTileGapDirectRule.png"); + } + + boolean checkLeftRight(BinaryCell c, BinaryBoard board) { + int x = c.getLocation().x; + int y = c.getLocation().y; + return board.getCell(x - 1, y).getType() != c.getType() + || board.getCell(x + 1, y).getType() != c.getType(); + } + + boolean checkUpDown(BinaryCell c, BinaryBoard board) { + int x = c.getLocation().x; + int y = c.getLocation().y; + return board.getCell(x, y - 1).getType() != c.getType() + || board.getCell(x, y + 1).getType() != c.getType(); + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + BinaryBoard origBoard = (BinaryBoard) transition.getParents().get(0).getBoard(); + ContradictionRule contraRule = new ThreeAdjacentContradictionRule(); + BinaryCell binaryCell = (BinaryCell) puzzleElement; + BinaryBoard modified = origBoard.copy(); + + // System.out.println("ORIG" + binaryCell.getData()); + // System.out.println("AFTER" + Math.abs(binaryCell.getData() - 1)); + modified.getPuzzleElement(puzzleElement).setData(Math.abs(binaryCell.getData() - 1)); + + PuzzleElement newP = binaryCell; + + System.out.println(contraRule.checkContradictionAt(modified, newP)); + + if (contraRule.checkContradictionAt(modified, newP) == null) { + return null; + } + modified.getPuzzleElement(puzzleElement).setData(Math.abs(binaryCell.getData() - 1)); + + return "Grouping of Three Ones or Zeros not found"; + } + + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/PreventTrioDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/PreventTrioDirectRule.java deleted file mode 100644 index 745e35d4e..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/PreventTrioDirectRule.java +++ /dev/null @@ -1,58 +0,0 @@ -package edu.rpi.legup.puzzle.binary.rules; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.model.rules.DirectRule; -import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.binary.BinaryBoard; -import edu.rpi.legup.puzzle.binary.BinaryCell; - -public class PreventTrioDirectRule extends DirectRule { - private final String INVALID_USE_MESSAGE = "Number at cell is incorrect"; - - public PreventTrioDirectRule() { - super( - "BINA-BASC-0001", - "Prevent Trio", - "If a trio contradiction state could appear, use the opposite digit to prevent the trio", - "edu/rpi/legup/images/binary/rules/PreventTrioDirectRule.png"); - } - - /** - * Checks whether the child node logically follows from the parent node at the specific - * puzzleElement index using this rule - * - * @param transition transition to check - * @param puzzleElement equivalent puzzleElement - * @return null if the child node logically follow from the parent node at the specified - * puzzleElement, otherwise error message - */ - @Override - public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - BinaryBoard origBoard = (BinaryBoard) transition.getParents().get(0).getBoard(); - TrioContradictionRule contraRule = new TrioContradictionRule(); - BinaryCell binaryCell = (BinaryCell) puzzleElement; - BinaryBoard modified = origBoard.copy(); - - // Flip the cell and check to see if there will be a trio contradiction, if so the rule is applied correctly - modified.getPuzzleElement(puzzleElement).setData(Math.abs(binaryCell.getData() - 1)); - if (contraRule.checkContradictionAt(modified, binaryCell) == null) { - return null; - } - - return "Trio Found"; - } - - /** - * Creates a transition {@link Board} that has this rule applied to it using the {@link - * TreeNode}. - * - * @param node tree node used to create default transition board - * @return default board or null if this rule cannot be applied to this tree node - */ - @Override - public Board getDefaultBoard(TreeNode node) { - return null; - } -} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/SaveBlockerDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/SaveBlockerDirectRule.java deleted file mode 100644 index 5f76c4f59..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/SaveBlockerDirectRule.java +++ /dev/null @@ -1,60 +0,0 @@ -package edu.rpi.legup.puzzle.binary.rules; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.model.rules.DirectRule; -import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.binary.BinaryBoard; -import edu.rpi.legup.puzzle.binary.BinaryCell; - -public class SaveBlockerDirectRule extends DirectRule { - - private final String INVALID_USE_MESSAGE = "Number at cell is incorrect"; - - public SaveBlockerDirectRule() { - super( - "BINA-BASC-0003", - "Save Blocker", - "If a future trio could appear in this row/col, save the digit that could block that trio", - "edu/rpi/legup/images/binary/rules/SaveBlockerDirectRule.png"); - } - - /** - * Checks whether the child node logically follows from the parent node at the specific - * puzzleElement index using this rule - * - * @param transition transition to check - * @param puzzleElement equivalent puzzleElement - * @return null if the child node logically follow from the parent node at the specified - * puzzleElement, otherwise error message - */ - public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - BinaryBoard origBoard = (BinaryBoard) transition.getParents().get(0).getBoard(); - WastedBlockerContradictionRule contraRule = new WastedBlockerContradictionRule(); - BinaryCell binaryCell = (BinaryCell) puzzleElement; - BinaryBoard modified = origBoard.copy(); - - // Flip the cell and check to see if a blocker digit is wasted, if so the rule is applied correctly - modified.getPuzzleElement(puzzleElement).setData(Math.abs(binaryCell.getData() - 1)); - if (contraRule.checkContradictionAt(modified, binaryCell) == null) { - return null; - } - - return "Wasted Digit Found"; - } - - /** - * Creates a transition {@link Board} that has this rule applied to it using the {@link - * TreeNode}. - * - * @param node tree node used to create default transition board - * @return default board or null if this rule cannot be applied to this tree node - */ - @Override - public Board getDefaultBoard(TreeNode node) { - return null; - } - - -} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java new file mode 100644 index 000000000..dc2f07c8b --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java @@ -0,0 +1,48 @@ +package edu.rpi.legup.puzzle.binary.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; +import edu.rpi.legup.model.rules.DirectRule; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; + +public class SurroundPairDirectRule extends DirectRule { + + public SurroundPairDirectRule() { + super( + "BINA-BASC-0001", + "Surround Pair", + "If two adjacent tiles have the same value, surround the tiles with the other value.", + "edu/rpi/legup/images/binary/rules/SurroundPairDirectRule.png"); + } + + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + BinaryBoard origBoard = (BinaryBoard) transition.getParents().get(0).getBoard(); + ContradictionRule contraRule = new ThreeAdjacentContradictionRule(); + BinaryCell binaryCell = (BinaryCell) puzzleElement; + BinaryBoard modified = origBoard.copy(); + + // System.out.println("ORIG" + binaryCell.getData()); + // System.out.println("AFTER" + Math.abs(binaryCell.getData() - 1)); + modified.getPuzzleElement(puzzleElement).setData(Math.abs(binaryCell.getData() - 1)); + + PuzzleElement newP = binaryCell; + + System.out.println(contraRule.checkContradictionAt(modified, newP)); + + if (contraRule.checkContradictionAt(modified, newP) == null) { + return null; + } + modified.getPuzzleElement(puzzleElement).setData(Math.abs(binaryCell.getData() - 1)); + + return "Grouping of Three Ones or Zeros not found"; + } + + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentContradictionRule.java new file mode 100644 index 000000000..075642246 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentContradictionRule.java @@ -0,0 +1,127 @@ +package edu.rpi.legup.puzzle.binary.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.ContradictionRule; +import edu.rpi.legup.puzzle.binary.BinaryBoard; +import edu.rpi.legup.puzzle.binary.BinaryCell; +import edu.rpi.legup.puzzle.binary.BinaryType; + +public class ThreeAdjacentContradictionRule extends ContradictionRule { + private final String NO_CONTRADICTION_MESSAGE = + "Does not contain a contradiction at this index"; + private final String INVALID_USE_MESSAGE = "Contradiction must be a zero or one"; + + public ThreeAdjacentContradictionRule() { + super( + "BINA-CONT-0001", + "Three Adjacent", + "There must not be three adjacent zeros or three adjacent ones in a row or column", + "edu/rpi/legup/images/binary/rules/ThreeAdjacentContradictionRule.png"); + } + + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + BinaryBoard binaryBoard = (BinaryBoard) board; + int height = binaryBoard.getHeight(); + int width = binaryBoard.getWidth(); + + BinaryCell cell = (BinaryCell) binaryBoard.getPuzzleElement(puzzleElement); + System.out.println("THE CELL IS : " + cell.getType()); + int cellX = cell.getLocation().x; + int cellY = cell.getLocation().y; + BinaryCell oneUp = null; + BinaryCell oneDown = null; + BinaryCell oneForward = null; + BinaryCell oneBackward = null; + BinaryCell twoUp = null; + BinaryCell twoDown = null; + BinaryCell twoForward = null; + BinaryCell twoBackward = null; + if (binaryBoard.getCell(cellX, cellY + 1) != null) { + oneUp = binaryBoard.getCell(cellX, cellY + 1); + } + if (binaryBoard.getCell(cellX, cellY - 1) != null) { + oneDown = binaryBoard.getCell(cellX, cellY - 1); + } + if (binaryBoard.getCell(cellX + 1, cellY) != null) { + oneForward = binaryBoard.getCell(cellX + 1, cellY); + } + if (binaryBoard.getCell(cellX - 1, cellY) != null) { + oneBackward = binaryBoard.getCell(cellX - 1, cellY); + } + if (binaryBoard.getCell(cellX, cellY + 2) != null) { + twoUp = binaryBoard.getCell(cellX, cellY + 2); + } + if (binaryBoard.getCell(cellX, cellY - 2) != null) { + twoDown = binaryBoard.getCell(cellX, cellY - 2); + } + if (binaryBoard.getCell(cellX + 2, cellY) != null) { + twoForward = binaryBoard.getCell(cellX + 2, cellY); + } + if (binaryBoard.getCell(cellX - 2, cellY) != null) { + twoBackward = binaryBoard.getCell(cellX - 2, cellY); + } + + if (cell.getType() == BinaryType.ONE || cell.getType() == BinaryType.ZERO) { + if (twoBackward != null + && oneBackward != null + && twoBackward.getType() != BinaryType.UNKNOWN + && oneBackward.getType() != BinaryType.UNKNOWN) { + if (twoBackward.getType() == cell.getType() + && oneBackward.getType() == cell.getType()) { + System.out.println("1"); + return null; + } + } + if (twoForward != null + && oneForward != null + && twoForward.getType() != BinaryType.UNKNOWN + && oneForward.getType() != BinaryType.UNKNOWN) { + if (twoForward.getType() == cell.getType() + && oneForward.getType() == cell.getType()) { + System.out.println("2"); + return null; + } + } + if (twoDown != null + && oneDown != null + && twoDown.getType() != BinaryType.UNKNOWN + && oneDown.getType() != BinaryType.UNKNOWN) { + if (twoDown.getType() == cell.getType() && oneDown.getType() == cell.getType()) { + System.out.println("3"); + return null; + } + } + if (twoUp != null + && oneUp != null + && twoUp.getType() != BinaryType.UNKNOWN + && oneUp.getType() != BinaryType.UNKNOWN) { + if (twoUp.getType() == cell.getType() && oneUp.getType() == cell.getType()) { + System.out.println("4"); + return null; + } + } + if (oneBackward != null + && oneForward != null + && oneBackward.getType() != BinaryType.UNKNOWN + && oneForward.getType() != BinaryType.UNKNOWN) { + if (oneBackward.getType() == cell.getType() + && oneForward.getType() == cell.getType()) { + System.out.println("5"); + return null; + } + } + if (oneUp != null + && oneDown != null + && oneUp.getType() != BinaryType.UNKNOWN + && oneDown.getType() != BinaryType.UNKNOWN) { + if (oneUp.getType() == cell.getType() && oneDown.getType() == cell.getType()) { + System.out.println("6"); + return null; + } + } + } + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/TrioContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/TrioContradictionRule.java deleted file mode 100644 index dce7fe371..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/TrioContradictionRule.java +++ /dev/null @@ -1,185 +0,0 @@ -package edu.rpi.legup.puzzle.binary.rules; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.model.rules.ContradictionRule; -import edu.rpi.legup.puzzle.binary.BinaryBoard; -import edu.rpi.legup.puzzle.binary.BinaryCell; -import edu.rpi.legup.puzzle.binary.BinaryType; - -public class TrioContradictionRule extends ContradictionRule { - private final String NO_CONTRADICTION_MESSAGE = - "Does not contain a contradiction at this index"; - private final String INVALID_USE_MESSAGE = "Contradiction must be a zero or one"; - - public TrioContradictionRule() { - super( - "BINA-CONT-0001", - "Trio", - "There must not be three adjacent zeros or three adjacent ones in a row or column", - "edu/rpi/legup/images/binary/rules/TrioContradictionRule.png"); - } - - /** - * This method checks the surrounding cells of a given puzzle element at a specified distance - * in both the vertical and horizontal directions - * - * @param board The board where the puzzle elements are located - * @param puzzleElement The puzzle element from which the distance is calculated - * @param n The distance away from the puzzle element to retrieve the surrounding cells - * @return An array of BinaryCells representing the surrounding cells - */ - public BinaryCell[] getCellsNAway(Board board, PuzzleElement puzzleElement, int n) { - BinaryBoard binaryBoard = (BinaryBoard) board; - BinaryCell cell = (BinaryCell) binaryBoard.getPuzzleElement(puzzleElement); - int cellX = cell.getLocation().x; - int cellY = cell.getLocation().y; - - BinaryCell[] cells = new BinaryCell[4]; // [0] up x, [1] down x, [2] right x, [3] left x - cells[0] = null; - cells[1] = null; - cells[2] = null; - cells[3] = null; - - if (binaryBoard.getCell(cellX, cellY + n) != null) { - cells[0] = binaryBoard.getCell(cellX, cellY + n); - } - if (binaryBoard.getCell(cellX, cellY - n) != null) { - cells[1] = binaryBoard.getCell(cellX, cellY - n); - } - if (binaryBoard.getCell(cellX + n, cellY) != null) { - cells[2] = binaryBoard.getCell(cellX + n, cellY); - } - if (binaryBoard.getCell(cellX - n, cellY) != null) { - cells[3] = binaryBoard.getCell(cellX - n, cellY); - } - - return cells; - } - - /** - * Checks whether the cell and its two surrounding cells form a trio of zeros or ones; - * If a trio is found, it indicates a contradiction - * - * @param board The board where the puzzle elements are located - * @param puzzleElement The puzzle element to check for contradiction - * @return true if no contradiction is found, false if contradiction detected - */ - public boolean checkSurroundPair(Board board, PuzzleElement puzzleElement) { - BinaryBoard binaryBoard = (BinaryBoard) board; - BinaryCell cell = (BinaryCell) binaryBoard.getPuzzleElement(puzzleElement); - - // [0] up n, [1] down n, [2] right n, [3] left n - BinaryCell[] cellsOneAway = getCellsNAway(board, puzzleElement, 1); - BinaryCell[] cellsTwoAway = getCellsNAway(board, puzzleElement, 2); - - if (cell.getType() == BinaryType.ONE || cell.getType() == BinaryType.ZERO) { - // left one and left two - if (cellsOneAway[3] != null - && cellsTwoAway[3] != null - && cellsOneAway[3].getType() != BinaryType.UNKNOWN - && cellsTwoAway[3].getType() != BinaryType.UNKNOWN) { - if (cellsOneAway[3].getType() == cell.getType() - && cellsTwoAway[3].getType() == cell.getType()) { - return false; - } - } - // right one and right two - if (cellsOneAway[2] != null - && cellsTwoAway[2] != null - && cellsOneAway[2].getType() != BinaryType.UNKNOWN - && cellsTwoAway[2].getType() != BinaryType.UNKNOWN) { - if (cellsOneAway[2].getType() == cell.getType() - && cellsTwoAway[2].getType() == cell.getType()) { - return false; - } - } - // down one and down two - if (cellsOneAway[1] != null - && cellsTwoAway[1] != null - && cellsOneAway[1].getType() != BinaryType.UNKNOWN - && cellsTwoAway[1].getType() != BinaryType.UNKNOWN) { - if (cellsOneAway[1].getType() == cell.getType() - && cellsTwoAway[1].getType() == cell.getType()) { - return false; - } - } - // up one and up two - if (cellsOneAway[0] != null - && cellsTwoAway[0] != null - && cellsOneAway[0].getType() != BinaryType.UNKNOWN - && cellsTwoAway[0].getType() != BinaryType.UNKNOWN) { - if (cellsOneAway[0].getType() == cell.getType() - && cellsTwoAway[0].getType() == cell.getType()) { - return false; - } - } - } - - return true; - } - - /** - * Checks whether there are two of the same cell type separated by one cell that also has - * the same type in any direction. If a trio is found, it indicates a contradiction - * - * @param board The board where the puzzle elements are located - * @param puzzleElement The puzzle element to check for contradiction - * @return true if no contradiction is found, false if contradiction detected - */ - public boolean checkOneTileGap(Board board, PuzzleElement puzzleElement) { - BinaryBoard binaryBoard = (BinaryBoard) board; - BinaryCell cell = (BinaryCell) binaryBoard.getPuzzleElement(puzzleElement); - - // [0] up n, [1] down n, [2] right n, [3] left n - BinaryCell[] cellsOneAway = getCellsNAway(board, puzzleElement, 1); - - if (cell.getType() == BinaryType.ONE || cell.getType() == BinaryType.ZERO) { - // left one and right one - if (cellsOneAway[3] != null - && cellsOneAway[2] != null - && cellsOneAway[3].getType() != BinaryType.UNKNOWN - && cellsOneAway[2].getType() != BinaryType.UNKNOWN) { - if (cellsOneAway[3].getType() == cell.getType() - && cellsOneAway[2].getType() == cell.getType()) { - return false; - } - } - // down one and up one - if (cellsOneAway[1] != null - && cellsOneAway[0] != null - && cellsOneAway[1].getType() != BinaryType.UNKNOWN - && cellsOneAway[0].getType() != BinaryType.UNKNOWN) { - if (cellsOneAway[1].getType() == cell.getType() - && cellsOneAway[0].getType() == cell.getType()) { - return false; - } - } - } - return true; - } - - /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using - * this rule - * - * @param board board to check contradiction - * @param puzzleElement equivalent puzzleElement - * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message - */ - @Override - public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { - - boolean surroundPairValid = checkSurroundPair(board, puzzleElement); - if (!surroundPairValid) { - return null; - } - boolean oneTileGapValid = checkOneTileGap(board, puzzleElement); - if (!oneTileGapValid) { - return null; - } - - return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowOrColumnContradictionRule.java similarity index 64% rename from src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContradictionRule.java rename to src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowOrColumnContradictionRule.java index 82f658013..5089b3b5f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowOrColumnContradictionRule.java @@ -6,32 +6,22 @@ import edu.rpi.legup.puzzle.binary.BinaryBoard; import edu.rpi.legup.puzzle.binary.BinaryCell; import edu.rpi.legup.puzzle.binary.BinaryType; - import java.util.Set; -public class UnbalancedRowColumnContradictionRule extends ContradictionRule { +public class UnbalancedRowOrColumnContradictionRule extends ContradictionRule { private final String NO_CONTRADICTION_MESSAGE = "Does not contain a contradiction at this index"; private final String INVALID_USE_MESSAGE = "Row or column must have a value in each cell"; - public UnbalancedRowColumnContradictionRule() { + public UnbalancedRowOrColumnContradictionRule() { super( "BINA-CONT-0002", - "Unbalanced Row/Column", + "Unbalanced Row Or Column", "Each row or column must contain an equal number of zeros and ones", "edu/rpi/legup/images/binary/rules/UnbalancedRowColumnContradictionRule.png"); } - /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using - * this rule - * - * @param board board to check contradiction - * @param puzzleElement equivalent puzzleElement - * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message - */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { BinaryBoard binaryBoard = (BinaryBoard) board; @@ -51,13 +41,11 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } } - // if there are too many zeros or ones in this row - if (rowNumZeros > size / 2 || rowNumOnes > size / 2) { - return null; + if (rowNumZeros == size / 2 && rowNumOnes == size / 2) { + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; } - - Set col = binaryBoard.getColCells(cell.getLocation().x); + Set col = binaryBoard.getCol(cell.getLocation().x); size = col.size(); int colNumZeros = 0; @@ -71,11 +59,10 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } } - // if there are too many zeros or ones in this column - if (colNumZeros > size / 2 || colNumOnes > size / 2) { - return null; + if (colNumZeros == size / 2 && colNumOnes == size / 2) { + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; } - return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; + return null; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/UniqueRowColumnDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UniqueRowColumnDirectRule.java deleted file mode 100644 index 3f90510e3..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/UniqueRowColumnDirectRule.java +++ /dev/null @@ -1,288 +0,0 @@ -package edu.rpi.legup.puzzle.binary.rules; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.model.rules.DirectRule; -import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.binary.Binary; -import edu.rpi.legup.puzzle.binary.BinaryBoard; -import edu.rpi.legup.puzzle.binary.BinaryCell; -import edu.rpi.legup.puzzle.binary.BinaryType; - -import java.lang.reflect.Array; -import java.util.ArrayList; - -public class UniqueRowColumnDirectRule extends DirectRule { - private final String INVALID_USE_MESSAGE = "Number at cell is incorrect"; - - public UniqueRowColumnDirectRule() { - super( - "BINA-BASC-0004", - "Unique Row/Column", - "If an unfinished row/column only differs by empty cells from a finished one, " + - "fill contradicting empty cells with opposite digit to prevent a repeated row/column", - "edu/rpi/legup/images/binary/rules/UniqueRowColumnDirectRule.png"); - } - - /** - * Counts the number of empty (UNKNOWN) cells in a given sequence - * - * @param seq The sequence of BinaryType elements to check - * @return The number of empty (UNKNOWN) cells in the sequence - */ - private int getNumEmpty(ArrayList seq) { - int numEmpty = 0; - for (BinaryType t : seq) { - if (t.equals(BinaryType.UNKNOWN)) { - numEmpty++; - } - } - return numEmpty; - } - - /** - * Checks if there is a valid opposite digit to prevent a repeated row/column - * - * @param seq The sequence (row or column) to check - * @param origBoard The original board - * @param binaryCell The binary cell being checked - * @param rowOrColumn Flag to indicate whether checking a row (0) or a column (1) - * @return Null if a valid opposite digit is found, otherwise an error message - */ - private String checkOppositeDigitDifference(ArrayList seq, BinaryBoard origBoard, - BinaryCell binaryCell, int rowOrColumn) { - // rowOrColumn : 0 for row, 1 for column - - int numEmpty = getNumEmpty(seq); - if (numEmpty > 2) { - return "Row/Column must have at most 2 empty cells"; - } - - boolean valid = false; - for (int i = 0; i < seq.size(); i++) { - ArrayList currSeq; - // Get the sequence (row or column) from the original board to compare - if (rowOrColumn == 0) { - if (i == binaryCell.getLocation().y) { - continue; - } - currSeq = origBoard.getRowTypes(i); - } else { - if (i == binaryCell.getLocation().x) { - continue; - } - currSeq = origBoard.getColTypes(i); - } - - int numDifferentCells = 0; - for (int j = 0; j < currSeq.size(); j++) { - int numEmptyInCurrSeq = getNumEmpty(currSeq); - // If the current sequence has empty cells, it's not valid for comparison - if (numEmptyInCurrSeq != 0) { - valid = false; - break; - } - // Count differences between the sequences, stopping if more than 1 difference is found - if (!seq.get(j).equals(currSeq.get(j)) && !seq.get(j).equals(BinaryType.UNKNOWN)) { - if (++numDifferentCells > 1 || numEmpty != 1) { - valid = false; - break; - } - } - - // Check if there's a contradiction with the current cell, if not mark as valid - if (currSeq.get(j).equals(BinaryType.ZERO) && seq.get(j).equals(BinaryType.UNKNOWN) - && binaryCell.getType().equals(BinaryType.ONE)) { - if ((rowOrColumn == 0 && binaryCell.getLocation().x == j) || rowOrColumn == 1 - && binaryCell.getLocation().y == j) { - valid = true; - } - } - else if (currSeq.get(j).equals(BinaryType.ONE) && seq.get(j).equals(BinaryType.UNKNOWN) - && binaryCell.getType().equals(BinaryType.ZERO)) { - if ((rowOrColumn == 0 && binaryCell.getLocation().x == j) || rowOrColumn == 1 - && binaryCell.getLocation().y == j) { - valid = true; - } - } - } - // Exit if a valid sequence is found - if (valid) { - break; - } - } - - if (valid) { - return null; - } - return "There does not exist an opposite digit difference in "; - } - - /** - * Checks if there is one digit remaining in a sequence that can be filled to avoid repeating - * another sequence on the board - * - * @param seq The sequence (row or column) to check - * @param origBoard The original board - * @param binaryCell The binary cell being checked - * @param rowOrColumn Flag to indicate whether checking a row (0) or a column (1) - * @param zeroOrOne Flag to indicate whether checking for 0s (0) or 1s (1) - * @return Null if the rule can be applied, otherwise an error message - */ - private String checkRemainingOneDigitDifference(ArrayList seq, BinaryBoard origBoard, - BinaryCell binaryCell, int rowOrColumn, int zeroOrOne) { - // zeroOrOne: zero for 0, one for 1 - - for (int i = 0; i < seq.size(); i++) { - ArrayList currSeq; - if (rowOrColumn == 0) { - if (i == binaryCell.getLocation().y) { - continue; - } - currSeq = origBoard.getRowTypes(i); - } - else { - if (i == binaryCell.getLocation().x) { - continue; - } - currSeq = origBoard.getColTypes(i); - } - - boolean valid = true; - for (int j = 0; j < currSeq.size(); j++) { - int numEmptyInCurrSeq = getNumEmpty(currSeq); - // If the current sequence has empty cells, it's not valid for comparison - if (numEmptyInCurrSeq != 0) { - valid = false; - break; - } - // Check if there is a cell difference from this seq and current seq - if (!seq.get(j).equals(currSeq.get(j)) && !seq.get(j).equals(BinaryType.UNKNOWN)) { - valid = false; - break; - } - } - // Determine if the current sequence can be modified to prevent repetition - if (valid) { - BinaryType currSeqCell = currSeq.get(binaryCell.getLocation().x); - if (rowOrColumn == 0) { - currSeqCell = currSeq.get(binaryCell.getLocation().x); - } else if (rowOrColumn == 1) { - currSeqCell = currSeq.get(binaryCell.getLocation().y); - } - - // Check if this sequence has only one more zero remaining and current sequence fills that zero in, - // if so, zero in this seq must go in another cell to prevent repetition - if (zeroOrOne == 0) { - if (currSeqCell.equals(BinaryType.ZERO) && binaryCell.getType().equals(BinaryType.ONE)) { - return null; - } - } - // Check if this sequence has only one more one remaining and current sequence fills that one in, - // if so, one in this seq must go in another cell to prevent repetition - else if (zeroOrOne == 1) { - if (currSeqCell.equals(BinaryType.ONE) && binaryCell.getType().equals(BinaryType.ZERO)) { - return null; - } - } - } - } - - - return "There does not exist a sequence that can be prevented by a remaining digit difference"; - } - - /** - * Checks whether the child node logically follows from the parent node at the specific - * puzzleElement index using this rule - * - * @param transition transition to check - * @param puzzleElement equivalent puzzleElement - * @return null if the child node logically follow from the parent node at the specified - * puzzleElement, otherwise error message - */ - public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - BinaryBoard origBoard = (BinaryBoard) transition.getParents().get(0).getBoard(); - BinaryCell binaryCell = (BinaryCell) puzzleElement; - - // Check if filling the current cell with the opposite digit would prevent repetition with another row - ArrayList row = origBoard.getRowTypes(binaryCell.getLocation().y); - if (checkOppositeDigitDifference(row, origBoard, binaryCell, 0) == null) { - return null; - } - int numZeros = 0; - int numOnes = 0; - for (int i = 0; i < row.size(); i++) { - if (row.get(i).equals(BinaryType.ZERO)) { - numZeros++; - } - else if (row.get(i).equals(BinaryType.ONE)) { - numOnes++; - } - } - - // Check if only one more zero is needed, then see this row will be repeated by another row - // if current cell is filled in with last zero as well - if (numZeros == row.size()/2 - 1) { - if (checkRemainingOneDigitDifference(row, origBoard, binaryCell, 0, 0) == null) { - return null; - } - } - - // Check if only one more one is needed, then see this row will be repeated by another row - // if current cell is filled in with last one as well - if (numOnes == row.size()/2 - 1) { - if (checkRemainingOneDigitDifference(row, origBoard, binaryCell, 0, 1) == null) { - return null; - } - } - - // Check if filling the current cell with the opposite digit would prevent repetition with another column - ArrayList col = origBoard.getColTypes(binaryCell.getLocation().x); - if (checkOppositeDigitDifference(col, origBoard, binaryCell, 1) == null) { - return null; - } - - numZeros = 0; - numOnes = 0; - for (int i = 0; i < col.size(); i++) { - if (col.get(i).equals(BinaryType.ZERO)) { - numZeros++; - } - else if (col.get(i).equals(BinaryType.ONE)) { - numOnes++; - } - } - - // Check if only one more zero is needed, then see this column will be repeated by another column - // if current cell is filled in with last zero as well - if (numZeros == col.size()/2 - 1) { - if (checkRemainingOneDigitDifference(col, origBoard, binaryCell, 1, 0) == null) { - return null; - } - } - - // Check if only one more one is needed, then see this column will be repeated by another column - // if current cell is filled in with last one as well - if (numOnes == col.size()/2 - 1) { - if (checkRemainingOneDigitDifference(col, origBoard, binaryCell, 1, 1) == null) { - return null; - } - } - - return "There is no row/column that forces this cell to be a " + binaryCell.getData().toString(); - } - - /** - * Creates a transition {@link Board} that has this rule applied to it using the {@link - * TreeNode}. - * - * @param node tree node used to create default transition board - * @return default board or null if this rule cannot be applied to this tree node - */ - @Override - public Board getDefaultBoard(TreeNode node) { - return null; - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/WastedBlockerContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/WastedBlockerContradictionRule.java deleted file mode 100644 index e7ab51b41..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/WastedBlockerContradictionRule.java +++ /dev/null @@ -1,178 +0,0 @@ -package edu.rpi.legup.puzzle.binary.rules; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.model.rules.ContradictionRule; -import edu.rpi.legup.puzzle.binary.BinaryBoard; -import edu.rpi.legup.puzzle.binary.BinaryCell; -import edu.rpi.legup.puzzle.binary.BinaryType; - -import java.util.ArrayList; - -public class WastedBlockerContradictionRule extends ContradictionRule { - private final String NO_CONTRADICTION_MESSAGE = "Does not contain a contradiction at this index"; - - public WastedBlockerContradictionRule() { - super( - "BINA-CONT-0004", - "Wasted Blocker", - "There exists a cell in this row/column that allocates a digit unnecessarily and" + - " will cause a future trio to appear", - "edu/rpi/legup/images/binary/rules/WastedBlockerContradictionRule.png"); - } - - /* - i [ n ] j - i -> digit on left (0 if no digit exists) - n -> number of empty cells - j -> digit on right (0 if no digit exists) - neededZeros = ( n + i + j ) / 3 - */ - /** - * Calculates the number of zeros needed in a sequence based on the values on either side and the number of empty cells. - * - * @param leftVal The value on the left side of the empty cells - * @param rightVal The value on the right side of the empty cells - * @param emptyCellsInCurSec The number of empty cells in the current section - * @return The number of zeros needed in the sequence - */ - private int calculateNeededZeros(int leftVal, int rightVal, int emptyCellsInCurSec) { - int leftCopy = leftVal; - int rightCopy = rightVal; - if (leftCopy == -1) { - leftCopy = 0; - } - if (rightCopy == -1) { - rightCopy = 0; - } - return ((emptyCellsInCurSec + leftCopy + rightCopy) / 3); - } - - /* - i [ n ] j - i -> digit on left (1 if no digit exists) - n -> number of empty cells - j -> digit on right (1 if no digit exists) - neededOnes = ( n + ( 1 - i ) + ( 1 - j ) ) / 3 - */ - /** - * Calculates the number of ones needed in a sequence based on the values on either side and the number of empty cells - * - * @param leftVal The value on the left side of the empty cells - * @param rightVal The value on the right side of the empty cells - * @param emptyCellsInCurSec The number of empty cells in the current section - * @return The number of ones needed in the sequence - */ - private int calculateNeededOnes(int leftVal, int rightVal, int emptyCellsInCurSec) { - int leftCopy = leftVal; - int rightCopy = rightVal; - if (leftCopy == -1) { - leftCopy = 1; - } - if (rightCopy == -1) { - rightCopy = 1; - } - return ((emptyCellsInCurSec + (1 - leftCopy) + (1 - rightCopy)) / 3); - } - - /** - * Checks a sequence (row or column) to see if a wasted blocker digit is used - * - * @param seq The sequence to check - * @return Null if the sequence contains a contradiction, otherwise an error message - */ - private String checkSequence(ArrayList seq) { - int numZeros = 0; - int numOnes = 0; - boolean emptyCell = false; - int emptyCellsInCurSec = 0; - int neededZeros = 0; - int neededOnes = 0; - - for (int i = 0; i < seq.size(); i++) { - if (seq.get(i).equals(BinaryType.ZERO) || seq.get(i).equals(BinaryType.ONE)) { - if (seq.get(i).equals(BinaryType.ZERO)) { - numZeros++; - } else if (seq.get(i).equals(BinaryType.ONE)) { - numOnes++; - } - - if (emptyCell) { - if (emptyCellsInCurSec > 1) { // Ignore case where there is only one empty cell - int leftVal; - int rightVal; - // Check if left cell is out of bounds - if (i-emptyCellsInCurSec-1 < 0) { - leftVal = -1; - } else { - leftVal = seq.get(i-emptyCellsInCurSec-1).toValue(); - } - rightVal = seq.get(i).toValue(); - neededZeros += calculateNeededZeros(leftVal, rightVal, emptyCellsInCurSec); - neededOnes += calculateNeededOnes(leftVal, rightVal, emptyCellsInCurSec); - } - emptyCell = false; - emptyCellsInCurSec = 0; - } - } else { - if (!emptyCell) { - emptyCell = true; - } - emptyCellsInCurSec++; - } - } - - // Check last cell is empty - if (emptyCell) { - if (emptyCellsInCurSec > 1) { // Ignore case where there is only one empty cell - int leftVal; - int rightVal; - // Check if left cell is out of bounds - if (seq.size()-1-emptyCellsInCurSec-1 < 0) { - leftVal = -1; - } else { - leftVal = seq.get(seq.size()-1-emptyCellsInCurSec).toValue(); - } - rightVal = -1; - neededZeros += calculateNeededZeros(leftVal, rightVal, emptyCellsInCurSec); - neededOnes += calculateNeededOnes(leftVal, rightVal, emptyCellsInCurSec); - } - } - - // Check if the number of needed zeros or ones exceeds half the sequence length - // If so, return null to indicate contradiction has occurred - if ((numZeros + neededZeros > seq.size()/2) || (numOnes + neededOnes > seq.size()/2)) { - return null; - } - - return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; - } - - /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using - * this rule - * - * @param board board to check contradiction - * @param puzzleElement equivalent puzzleElement - * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message - */ - @Override - public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { - - BinaryBoard binaryBoard = (BinaryBoard) board; - BinaryCell cell = (BinaryCell) binaryBoard.getPuzzleElement(puzzleElement); - - ArrayList row = binaryBoard.getRowTypes(cell.getLocation().y); - if (checkSequence(row) == null) { - return null; - } - - ArrayList col = binaryBoard.getColTypes(cell.getLocation().x); - if (checkSequence(col) == null) { - return null; - } - - return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/binary_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/binary/rules/binary_reference_sheet.txt index 619d183a5..c8cb0d1b9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/binary_reference_sheet.txt +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/binary_reference_sheet.txt @@ -1,11 +1,9 @@ -BINA-BASC-0001 : PreventTrioContradictionRule -BINA-BASC-0002 : CompleteRowColumnDirectRule -BINA-BASC-0003 : SaveBlockerDirectRule -BINA-BASC-0004 : UniqueRowColumnDirectRule +BINA-BASC-0001 : SurroundPairDirectRule +BINA-BASC-0002 : OneTileGapDirectRule +BINA-BASC-0003 : CompleteRowColumnDirectRule -BINA-CONT-0001 : TrioContradictionRule -BINA-CONT-0002 : UnbalancedRowColumnContradictionRule -BINA-CONT-0003 : RepeatedRowColumnContradictionRule -BINA-CONT-0004 : WastedBlockerContradictionRule +BINA-CONT-0001 : ThreeAdjacentContradictionRule +BINA-CONT-0002 : UnbalancedRowOrColumnContradictionRule +BINA-CONT-0003 : DuplicateRowsOrColumnsContradictionRule -BINA-CASE-0001 : ZeroOrOneCaseRule \ No newline at end of file +BINA-CASE-0001 : OneOrZeroCaseRule \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCell.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCell.java index 40c5e4a54..a9e5aa2df 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/FillapixCell.java @@ -42,13 +42,13 @@ public void setCellType(FillapixCellType type) { @Override public void setType(Element e, MouseEvent m) { switch (e.getElementID()) { - case "FPIX-ELEM-0001": + case "FPIX-PLAC-0001": this.setCellType(FillapixCellType.BLACK); break; - case "FPIX-ELEM-0004": + case "FPIX-PLAC-0002": this.setCellType(FillapixCellType.WHITE); break; - case "FPIX-ELEM-0002": + case "FPIX-UNPL-0001": int n = this.getNumber(); switch (m.getButton()) { case MouseEvent.BUTTON1: diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/BlackTile.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/BlackTile.java index a6993778d..1d7c038a3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/BlackTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/BlackTile.java @@ -5,7 +5,7 @@ public class BlackTile extends PlaceableElement { public BlackTile() { super( - "FPIX-ELEM-0001", + "FPIX-PLAC-0001", "Black Tile", "The black tile", "edu/rpi/legup/images/fillapix/tiles/BlackTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/NumberTile.java index 5852c1ad7..e869aeaf9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/NumberTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/NumberTile.java @@ -1,13 +1,13 @@ package edu.rpi.legup.puzzle.fillapix.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class NumberTile extends PlaceableElement { +public class NumberTile extends NonPlaceableElement { private int object_num; public NumberTile() { super( - "FPIX-ELEM-0002", + "FPIX-UNPL-0001", "Number Tile", "A numbered tile", "edu/rpi/legup/images/fillapix/tiles/NumberTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/UnknownTile.java index 82d0dffb9..6778c1758 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/UnknownTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/UnknownTile.java @@ -1,11 +1,11 @@ package edu.rpi.legup.puzzle.fillapix.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class UnknownTile extends PlaceableElement { +public class UnknownTile extends NonPlaceableElement { public UnknownTile() { super( - "FPIX-ELEM-0003", + "FPIX-UNPL-0002", "Unknown Tile", "A blank tile", "edu/rpi/legup/images/fillapix/tiles/UnknownTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/WhiteTile.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/WhiteTile.java index b2eedfc09..67065a7e9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/WhiteTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/WhiteTile.java @@ -5,7 +5,7 @@ public class WhiteTile extends PlaceableElement { public WhiteTile() { super( - "FPIX-ELEM-0004", + "FPIX-PLAC-0002", "White Tile", "The white tile", "edu/rpi/legup/images/fillapix/tiles/WhiteTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/fillapix_elements_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/fillapix_elements_reference_sheet.txt index 1aece4b97..0409fa800 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/fillapix_elements_reference_sheet.txt +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/elements/fillapix_elements_reference_sheet.txt @@ -1,4 +1,5 @@ -FPIX-ELEM-0001 : BlackTile -FPIX-ELEM-0002 : NumberTile -FPIX-ELEM-0003 : UnknownTile -FPIX-ELEM-0004 : WhiteTile \ No newline at end of file +FPIX-PLAC-0001 : BlackTile +FPIX-PLAC-0002 : WhiteTile + +FPIX-UNPL-0001 : NumberTile +FPIX-UNPL-0002 : UnknownTile \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/BlackOrWhiteCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/BlackOrWhiteCaseRule.java index f0194bd39..860a6c011 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/BlackOrWhiteCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/BlackOrWhiteCaseRule.java @@ -37,9 +37,6 @@ public CaseBoard getCaseBoard(Board board) { @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { ArrayList cases = new ArrayList<>(); - if (puzzleElement == null) { - return cases; - } Board case1 = board.copy(); FillapixCell cell1 = (FillapixCell) case1.getPuzzleElement(puzzleElement); diff --git a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/SatisfyClueCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/SatisfyClueCaseRule.java index f8bb2d4f5..7db833f76 100644 --- a/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/SatisfyClueCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/fillapix/rules/SatisfyClueCaseRule.java @@ -45,9 +45,6 @@ public CaseBoard getCaseBoard(Board board) { @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { ArrayList cases = new ArrayList(); - if (puzzleElement == null) { - return cases; - } // get value of cell FillapixBoard fillapixBoard = (FillapixBoard) board.copy(); diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUp.java b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUp.java index a73806cd7..ab95c4658 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUp.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUp.java @@ -47,7 +47,7 @@ public Board generatePuzzle(int difficulty) { * @return true if the given dimensions are valid for Light Up, false otherwise */ public boolean isValidDimensions(int rows, int columns) { - return rows >= 0 && columns >= 0; + return rows > 0 && columns > 0; } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpBoard.java b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpBoard.java index 21084b8c7..217ef79a8 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpBoard.java @@ -134,12 +134,12 @@ public int getNumAdjLite(LightUpCell cell) { } /** - * Gets the number of adjacent cells that are placeable + * Gets the number of adjacent cells that are placable * * @param cell specified cell - * @return number of adjacent cells that are placeable + * @return number of adjacent cells that are placable */ - public int getNumPlaceable(LightUpCell cell) { + public int getNumPlacble(LightUpCell cell) { int num = 0; Set adjCells = getAdj(cell); for (LightUpCell c : adjCells) { diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCell.java b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCell.java index 6d890e67b..8adf84cb4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/LightUpCell.java @@ -16,16 +16,16 @@ public LightUpCell(int valueInt, Point location) { @Override public void setType(Element e, MouseEvent m) { switch (e.getElementID()) { - case "LTUP-ELEM-0002": + case "LTUP-PLAC-0001": this.data = -4; break; - case "LTUP-ELEM-0001": + case "LTUP-UNPL-0002": this.data = -1; break; - case "LTUP-ELEM-0004": + case "LTUP-UNPL-0003": this.data = -2; break; - case "LTUP-ELEM-0003": + case "LTUP-UNPL-0001": switch (m.getButton()) { case MouseEvent.BUTTON1: if (this.data < 0 || this.data > 3) { diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/BlackTile.java b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/BlackTile.java index eed3795d7..2ddb4f754 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/BlackTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/BlackTile.java @@ -1,11 +1,11 @@ package edu.rpi.legup.puzzle.lightup.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class BlackTile extends PlaceableElement { +public class BlackTile extends NonPlaceableElement { public BlackTile() { super( - "LTUP-ELEM-0001", + "LTUP-UNPL-0002", "Black Tile", "The black tile", "edu/rpi/legup/images/lightup/black.gif"); diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/BulbTile.java b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/BulbTile.java index 61ebac3d0..d238baa56 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/BulbTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/BulbTile.java @@ -5,7 +5,7 @@ public class BulbTile extends PlaceableElement { public BulbTile() { super( - "LTUP-ELEM-0002", + "LTUP-PLAC-0001", "Bulb Tile", "The bulb tile", "edu/rpi/legup/images/lightup/light.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/NumberTile.java index 26f9be46c..ae314a4cf 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/NumberTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/NumberTile.java @@ -1,15 +1,15 @@ package edu.rpi.legup.puzzle.lightup.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class NumberTile extends PlaceableElement { +public class NumberTile extends NonPlaceableElement { int object_number; // Follow the default format and resolves the NoSuchMethod error public NumberTile() { super( - "LTUP-ELEM-0003", + "LTUP-UNPL-0001", "Number Tile", "The number tile", "edu/rpi/legup/images/lightup/1.gif"); @@ -17,7 +17,7 @@ public NumberTile() { public NumberTile(int num) { super( - "LTUP-ELEM-0003", + "LTUP-UNPL-0001", "Number Tile", "The number tile", "edu/rpi/legup/images/lightup/" + num + ".gif"); diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/UnknownTile.java index a724be600..24d420fe8 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/UnknownTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/UnknownTile.java @@ -1,11 +1,11 @@ package edu.rpi.legup.puzzle.lightup.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class UnknownTile extends PlaceableElement { +public class UnknownTile extends NonPlaceableElement { public UnknownTile() { super( - "LTUP-ELEM-0004", + "LTUP-UNPL-0003", "Unknown Tile", "A blank tile", "edu/rpi/legup/images/lightup/UnknownTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/lightup_elements_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/lightup_elements_reference_sheet.txt deleted file mode 100644 index 93c97de1c..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/elements/lightup_elements_reference_sheet.txt +++ /dev/null @@ -1,4 +0,0 @@ -LTUP-ELEM-0001 : BlackTile -LTUP-ELEM-0002 : BulbTile -LTUP-ELEM-0003 : NumberTile -LTUP-ELEM-0004 : UnknownTile \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/LightOrEmptyCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/LightOrEmptyCaseRule.java index 53efb6587..4ba754731 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/LightOrEmptyCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/LightOrEmptyCaseRule.java @@ -44,10 +44,6 @@ public CaseBoard getCaseBoard(Board board) { @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { ArrayList cases = new ArrayList<>(); - if (puzzleElement == null) { - return cases; - } - Board case1 = board.copy(); PuzzleElement data1 = case1.getPuzzleElement(puzzleElement); data1.setData(-4); diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java index f73a34b2d..490122874 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/SatisfyNumberCaseRule.java @@ -47,11 +47,6 @@ public CaseBoard getCaseBoard(Board board) { */ @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { - ArrayList cases = new ArrayList<>(); - if (puzzleElement == null) { - return cases; - } - LightUpBoard lightUpBoard = (LightUpBoard) board; LightUpCell cell = (LightUpCell) puzzleElement; Point loc = cell.getLocation(); @@ -101,6 +96,7 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { } } + ArrayList cases = new ArrayList<>(); if (numNeeded == 0) { return cases; } diff --git a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/TooFewBulbsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/TooFewBulbsContradictionRule.java index de1f85edc..8cf68e570 100644 --- a/src/main/java/edu/rpi/legup/puzzle/lightup/rules/TooFewBulbsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/rules/TooFewBulbsContradictionRule.java @@ -36,7 +36,7 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } int bulbs = lightUpBoard.getNumAdj(cell, LightUpCellType.BULB); - int placeable = lightUpBoard.getNumPlaceable(cell); + int placeable = lightUpBoard.getNumPlacble(cell); if (bulbs + placeable < cell.getData()) { return null; diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/BombTile.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/BombTile.java index cd5577eeb..78a5d320c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/BombTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/BombTile.java @@ -1,8 +1,8 @@ package edu.rpi.legup.puzzle.minesweeper.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class BombTile extends PlaceableElement { +public class BombTile extends NonPlaceableElement { public BombTile() { super( "MINE-UNPL-0001", diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/UnsetTile.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/UnsetTile.java index 6cfc34c8d..447e2840c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/UnsetTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/UnsetTile.java @@ -1,8 +1,8 @@ package edu.rpi.legup.puzzle.minesweeper.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class UnsetTile extends PlaceableElement { +public class UnsetTile extends NonPlaceableElement { public UnsetTile() { super( diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeCell.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeCell.java index 1e0e85ed8..c6cd2c64e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeCell.java @@ -46,13 +46,13 @@ public NurikabeType getType() { @Override public void setType(Element e, MouseEvent m) { switch (e.getElementID()) { - case "NURI-ELEM-0001": + case "NURI-PLAC-0001": this.data = -1; break; - case "NURI-ELEM-0004": + case "NURI-PLAC-0002": this.data = 0; break; - case "NURI-ELEM-0002": + case "NURI-UNPL-0001": switch (m.getButton()) { case MouseEvent.BUTTON1: if (this.data <= 0 || this.data > 8) { diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeController.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeController.java index f2fad0d50..158abe7b4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeController.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeController.java @@ -6,16 +6,6 @@ public class NurikabeController extends ElementController { - /** - * Handles cell state changes in the nurikabe puzzle when a mouse event occurs - * If the left mouse button is clicked: - * - If the control key is held down, shows a context menu at the mouse position - * - Otherwise, toggles the cell data state between 0, -1, and -2 in a cyclic manner - * If the right mouse button is clicked, the cell data state is also toggled between -2, -1, and 0 - * - * @param e MouseEvent triggered by the user interaction - * @param data PuzzleElement representing the cell being modified - */ @Override public void changeCell(MouseEvent e, PuzzleElement data) { NurikabeCell cell = (NurikabeCell) data; diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeExporter.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeExporter.java index e01821639..23efd4724 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeExporter.java @@ -10,13 +10,6 @@ public NurikabeExporter(Nurikabe nurikabe) { super(nurikabe); } - /** - * Generates an XML element for the nurikabe puzzle board, including its dimensions and the - * state of each cell. Nurikabe cells that are not empty are included in the XML. - * - * @param newDocument The XML document to which the board element belongs. - * @return The XML element representing the board. - */ @Override protected org.w3c.dom.Element createBoardElement(Document newDocument) { NurikabeBoard board; diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/BlackTile.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/BlackTile.java index a7972b9b2..459a809e0 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/BlackTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/BlackTile.java @@ -5,7 +5,7 @@ public class BlackTile extends PlaceableElement { public BlackTile() { super( - "NURI-ELEM-0001", + "NURI-PLAC-0001", "Black Tile", "The black tile", "edu/rpi/legup/images/nurikabe/tiles/BlackTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/NumberTile.java index 2015d990b..475b278da 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/NumberTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/NumberTile.java @@ -1,13 +1,13 @@ package edu.rpi.legup.puzzle.nurikabe.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class NumberTile extends PlaceableElement { +public class NumberTile extends NonPlaceableElement { private int object_num; public NumberTile() { super( - "NURI-ELEM-0002", + "NURI-UNPL-0001", "Number Tile", "A numbered tile", "edu/rpi/legup/images/nurikabe/tiles/NumberTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/UnknownTile.java index 8a18c80cc..85d47e208 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/UnknownTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/UnknownTile.java @@ -1,11 +1,11 @@ package edu.rpi.legup.puzzle.nurikabe.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class UnknownTile extends PlaceableElement { +public class UnknownTile extends NonPlaceableElement { public UnknownTile() { super( - "NURI-ELEM-0003", + "NURI-UNPL-0002", "Unknown Tile", "A blank tile", "edu/rpi/legup/images/nurikabe/tiles/UnknownTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/WhiteTile.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/WhiteTile.java index ae07c6d76..35eb63b81 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/WhiteTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/WhiteTile.java @@ -5,7 +5,7 @@ public class WhiteTile extends PlaceableElement { public WhiteTile() { super( - "NURI-ELEM-0004", + "NURI-PLAC-0002", "White Tile", "The white tile", "edu/rpi/legup/images/nurikabe/tiles/WhiteTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/nurikabe_elements_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/nurikabe_elements_reference_sheet.txt deleted file mode 100644 index 667972fd6..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/nurikabe_elements_reference_sheet.txt +++ /dev/null @@ -1,4 +0,0 @@ -NURI-ELEM-0001 : BlackTile -NURI-ELEM-0002 : NumberTile -NURI-ELEM-0003 : UnknownTile -NURI-ELEM-0004 : WhiteTile diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackOrWhiteCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackOrWhiteCaseRule.java index 1c87d5cfa..ac0ab6df6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackOrWhiteCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/BlackOrWhiteCaseRule.java @@ -24,7 +24,7 @@ public BlackOrWhiteCaseRule() { /** * Checks whether the {@link TreeTransition} logically follows from the parent node using this - * rule. This method is the one that should be overridden in child classes. + * rule. This method is the one that should overridden in child classes. * * @param transition transition to check * @return null if the child node logically follow from the parent node, otherwise error message @@ -84,10 +84,6 @@ public CaseBoard getCaseBoard(Board board) { @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { ArrayList cases = new ArrayList<>(); - if (puzzleElement == null) { - return cases; - } - Board case1 = board.copy(); PuzzleElement data1 = case1.getPuzzleElement(puzzleElement); data1.setData(NurikabeType.WHITE.toValue()); diff --git a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/FinishRoomCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/FinishRoomCaseRule.java index dd8943cdb..4901cfa6f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/FinishRoomCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/rules/FinishRoomCaseRule.java @@ -17,24 +17,24 @@ import java.util.Set; public class FinishRoomCaseRule extends CaseRule { - private int legitCases = 0; // placeholder for amount of cases originally generated in case user tries to delete - private Set uniqueCases; // stores the unique case hashes + private int legitCases = + 0; // placeholder for amount of cases originally generated in case user tries to delete + // cases public FinishRoomCaseRule() { super( "NURI-CASE-0002", "Finish Room", - "Room can be finished in up to nine ways", + "Room can be finished in up to five ways", "edu/rpi/legup/images/nurikabe/cases/FinishRoom.png"); - this.MAX_CASES = 9; - this.MIN_CASES = 1; - this.uniqueCases = new HashSet<>(); + this.MAX_CASES = 5; + this.MIN_CASES = 2; } /** * Checks whether the {@link TreeTransition} logically follows from the parent node using this - * rule. This method is the one that should have overridden in child classes. + * rule. This method is the one that should overridden in child classes. * * @param transition transition to check * @return null if the child node logically follow from the parent node, otherwise error message @@ -43,18 +43,18 @@ public FinishRoomCaseRule() { public String checkRuleRaw(TreeTransition transition) { NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); List childTransitions = transition.getParents().get(0).getChildren(); - if (childTransitions.size() > MAX_CASES) { + if (childTransitions.size() > 5) { return super.getInvalidUseOfRuleMessage() - + ": This case rule must have 9 or less children."; + + ": This case rule must have 5 or less children."; } - if (childTransitions.size() < MIN_CASES) { + if (childTransitions.size() < 2) { return super.getInvalidUseOfRuleMessage() - + ": This case rule must have 1 or more children."; + + ": This case rule must have 2 or more children."; } if (childTransitions.size() != legitCases) { return super.getInvalidUseOfRuleMessage() + ": Cases can not be removed from the branch."; - } // stops user from deleting 1 or more generated cases and still having path show as green + } // stops user from deleting 1 or mose generated cases and still having path show as green Set locations = new HashSet<>(); for (TreeTransition t1 : childTransitions) { locations.add( @@ -84,20 +84,31 @@ public CaseBoard getCaseBoard(Board board) { DisjointSets regions = NurikabeUtilities.getNurikabeRegions(nurikabeBoard); nurikabeBoard.setModifiable(false); - for (PuzzleElement element : nurikabeBoard.getPuzzleElements()) { // loops all puzzle elements - if (((NurikabeCell) element).getType() == NurikabeType.NUMBER) { // if the tile is a white number block - Set disRow = regions.getSet(((NurikabeCell) element)); // store the row of the white region - boolean only = true; // placeholder boolean of if the element being tested is the only number block in the room or not - + for (PuzzleElement element : + nurikabeBoard.getPuzzleElements()) { // loops all puzzle elements + if (((NurikabeCell) element).getType() + == NurikabeType.NUMBER) { // if the tile is a white number block + Set disRow = + regions.getSet( + ((NurikabeCell) element)); // store the row of the white region + boolean only = + true; // placeholder boolean of if the element being tested is the only + // number block in the room or not for (NurikabeCell d : disRow) { // loops through tiles in the room - // if found another number tile and it's data is different from the element we're working with if ((d.getType() == NurikabeType.NUMBER) - && !(d.getData().equals(((NurikabeCell) element).getData()))) { - only = false; + && !(d.getData() + .equals( + ((NurikabeCell) element) + .getData()))) { // if found another number tile + // and it's data is different + // than the element we're + // working with + only = false; // set only to false } } - // if size of region is 1 less than the number block and the number block is only number block in the region - if (disRow.size() < ((NurikabeCell) element).getData() && only) { + if (disRow.size() + 1 == ((NurikabeCell) element).getData() + && only) { // if size of region is 1 less than the number block and the + // number block is only number block in the region caseBoard.addPickableElement(element); // add that room as a pickable element } } @@ -115,19 +126,16 @@ public CaseBoard getCaseBoard(Board board) { @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { ArrayList cases = new ArrayList<>(); // makes array list of cases - if (puzzleElement == null) { - return cases; - } - NurikabeBoard nuriBoard = (NurikabeBoard) board.copy(); // nurikabe board to edit - NurikabeCell numberCell = nuriBoard.getCell( - ((NurikabeCell) puzzleElement).getLocation().x, - ((NurikabeCell) puzzleElement).getLocation().y - ); // number cell whose room we want to fill - - Point origPoint = new Point(numberCell.getLocation().x, numberCell.getLocation().y); - int filledRoomSize = numberCell.getData(); // size of room we want afterward - + NurikabeCell numbaCell = + nuriBoard.getCell( + ((NurikabeCell) puzzleElement).getLocation().x, + ((NurikabeCell) puzzleElement) + .getLocation() + .y); // number cell whose room we want to fill + int filledRoomSize = numbaCell.getData(); // size of room we want afterward + Set locations = + new HashSet<>(); // locations where white space is added to finish room Point left = new Point(-1, 0); Point right = new Point(1, 0); Point bot = new Point(0, -1); @@ -137,114 +145,92 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { directions.add(right); directions.add(top); directions.add(bot); - - Set checkedPoints = new HashSet<>(); // add all into checked points and continue at start of loop if inside - DisjointSets regions = NurikabeUtilities.getNurikabeRegions(nuriBoard); // gathers regions - Set numberCellRegion = regions.getSet(numberCell); // set of white spaces - - for (NurikabeCell d : numberCellRegion) { // loops through white spaces - generateCases(nuriBoard, d, filledRoomSize, directions, checkedPoints, cases, origPoint); - } - - legitCases = cases.size(); - return cases; - } - - /** - * Recursively generates possible cases for filling a room with white cells based on the current - * board state and specified parameters. - * - * @param nuriBoard the current Nurikabe board state - * @param currentCell the current cell being evaluated - * @param filledRoomSize the target size for the room being filled - * @param directions the set of possible directions to expand the room - * @param checkedPoints the set of points already evaluated to avoid redundancy - * @param cases the list of valid board cases generated - * @param origPoint the original point of the number cell initiating the room filling - */ - private void generateCases(NurikabeBoard nuriBoard, NurikabeCell currentCell, int filledRoomSize, - Set directions, Set checkedPoints, ArrayList cases, Point origPoint) { - for (Point direction : directions) { - Point newPoint = new Point( - currentCell.getLocation().x + direction.x, - currentCell.getLocation().y + direction.y - ); - - if (newPoint.x < 0 || newPoint.y < 0 || - newPoint.x >= nuriBoard.getWidth() || newPoint.y >= nuriBoard.getHeight()) { - continue; // out of bounds - } - - NurikabeCell newCell = nuriBoard.getCell(newPoint.x, newPoint.y); - if (checkedPoints.contains(newPoint)) { - continue; // already checked + Set checkedPoints = + new HashSet<>(); // add all into checked points and continue at start of loop if + // inside + DisjointSets regions = + NurikabeUtilities.getNurikabeRegions(nuriBoard); // gathers regions + Set disRow = regions.getSet(numbaCell); // set of white spaces + for (NurikabeCell d : disRow) { // loops through white spaces + if (cases.size() >= 6) { // no need to check this many cases + // throw new IllegalStateException("Too many cases"); + continue; // crash/runtime protection } - - if (newCell.getType() == NurikabeType.UNKNOWN) { - newCell.setData(NurikabeType.WHITE.toValue()); // changes adjacent cell color to white - newCell.setModifiable(false); - checkedPoints.add(newPoint); - - DisjointSets regions = NurikabeUtilities.getNurikabeRegions(nuriBoard); // update regions variable - Set newRoomSet = regions.getSet(newCell); // gets set of cells in room with new white cell added - - if (!touchesDifferentRoom(nuriBoard, newCell, filledRoomSize, directions, origPoint)) { - if (newRoomSet.size() == filledRoomSize) { // if adding white fills the room to exact size of - // number block and doesn't connect with another room - Board caseBoard = nuriBoard.copy(); - // check if case for board already exists - boolean unique = true; - for (Board board : cases) { - if (caseBoard.equalsBoard(board)) { - unique = false; + for (Point direction : directions) { + if (cases.size() >= 6) { // no need to check this many cases + // throw new IllegalStateException("Too many cases"); + continue; // crash/runtime protection + } + if (!((nuriBoard.getWidth() > (d.getLocation().x + direction.x) + && (nuriBoard.getHeight() > d.getLocation().y + direction.y) + && (d.getLocation().x + direction.x >= 0) + && (d.getLocation().y + direction.y >= 0)))) { + continue; // if next location check would be outside of grid then continue + } + NurikabeCell curr = + nuriBoard.getCell( + d.getLocation().x + direction.x, d.getLocation().y + direction.y); + if (checkedPoints.contains(curr.getLocation())) { + continue; // if we already checked whether or not making this tile white would + // complete the room then continue + } + checkedPoints.add( + curr.getLocation()); // adds location to checkedPoints so we don't check + // it again and accidentally add + if (curr.getType() + == NurikabeType + .UNKNOWN) { // found adjacent space to region that is currently + // unknown + curr.setData( + NurikabeType.WHITE.toValue()); // changes adjacent cell color to white + nuriBoard.addModifiedData(curr); // adds modified before check + regions = NurikabeUtilities.getNurikabeRegions(nuriBoard); // update regions + Set disCreatedRow = + regions.getSet( + curr); // gets set of created row with new white cell added + if (disCreatedRow.size() + == filledRoomSize) { // If adding white fills the room to exact size of + // number block and doesn't connect with another + // room + Point here = curr.getLocation(); // gets current location of new white tile + // that fills room + boolean alreadyIn = + false; // sets whether or not the tile has already been added to + // false + for (Point p : + locations) { // loops through locations of previously added tiles + if (p == here) { // if point is already in + alreadyIn = true; // change already in to true break; } } - if (unique) { - caseBoard.addModifiedData(newCell); - cases.add(caseBoard); + if (!alreadyIn) { // if point wasn't already in + Board casey = + nuriBoard.copy(); // copy the current board with white tile + // changed + PuzzleElement datacasey = + curr; // gets changed white tile as a puzzle element + datacasey.setData( + NurikabeType.WHITE + .toValue()); // ensure set to white, probably redundant + casey.addModifiedData(datacasey); // ensure confirmed white change + regions = + NurikabeUtilities.getNurikabeRegions( + nuriBoard); // update regions + cases.add(casey); // add this case to list of cases + locations.add( + here); // add location of new white tile to list of locations so + // that we don't accidentally add it again later } - } else if (newRoomSet.size() < filledRoomSize) { - generateCases(nuriBoard, newCell, filledRoomSize, directions, checkedPoints, cases, origPoint); - } - } - newCell.setData(NurikabeType.UNKNOWN.toValue()); - newCell.setModifiable(true); - checkedPoints.remove(newPoint); - } - } - } - - /** - * Determines if a given cell touches a different room by checking adjacent cells in specified directions. - * - * @param board the current Nurikabe board state - * @param cell the cell being evaluated - * @param origRoomSize the size of the original room being filled - * @param directions the set of possible directions to check around the cell - * @param origPoint the original point of the number cell initiating the room filling - * @return true if the cell touches a different room, false otherwise - */ - private boolean touchesDifferentRoom(NurikabeBoard board, NurikabeCell cell, int origRoomSize, Set directions, Point origPoint) { - for (Point direction : directions) { - Point adjacentPoint = new Point( - cell.getLocation().x + direction.x, - cell.getLocation().y + direction.y - ); - - if (adjacentPoint.x >= 0 && adjacentPoint.y >= 0 && - adjacentPoint.x < board.getWidth() && adjacentPoint.y < board.getHeight()) { // check if out of bounds - NurikabeCell adjacentCell = board.getCell(adjacentPoint.x, adjacentPoint.y); - // check if the adjacent cell is a number cell - if (adjacentCell.getType() == NurikabeType.NUMBER) { - // check if it's different from the original number cell - if (origRoomSize != adjacentCell.getData() || (adjacentPoint.x != origPoint.x || adjacentPoint.y != origPoint.y)) { - return true; } + curr.setData(NurikabeType.UNKNOWN.toValue()); // set cell type back to unknown + nuriBoard.addModifiedData(curr); // confirms change back to unknown + regions = NurikabeUtilities.getNurikabeRegions(nuriBoard); // updates regions } } + legitCases = cases.size(); } - return false; + return cases; } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCell.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCell.java index ffd7c491d..75bba369f 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/ShortTruthTableCell.java @@ -135,22 +135,22 @@ public void setType(Element e, MouseEvent m) { } // Red Element - if (e.getElementID().equals("STTT-ELEM-0004")) { + if (e.getElementID().equals("STTT-PLAC-0002")) { this.data = ShortTruthTableCellType.FALSE; } // Green Element else { - if (e.getElementID().equals("STTT-ELEM-0002")) { + if (e.getElementID().equals("STTT-PLAC-0001")) { this.data = ShortTruthTableCellType.TRUE; } // Unknown Element else { - if (e.getElementID().equals("STTT-ELEM-0005")) { + if (e.getElementID().equals("STTT-PLAC-0003")) { this.data = ShortTruthTableCellType.UNKNOWN; } // Argument Element else { - if (e.getElementID().equals("STTT-ELEM-0001")) { + if (e.getElementID().equals("STTT-UNPL-0001")) { // Prevents non-argument symbols from being changed if (!(this.symbol >= 'A' && this.symbol <= 'Z')) { return; @@ -172,7 +172,7 @@ public void setType(Element e, MouseEvent m) { } // And/Or Element else { - if (e.getElementID().equals("STTT-ELEM-0003")) { + if (e.getElementID().equals("STTT-UNPL-0002")) { if (m.getButton() == MouseEvent.BUTTON1) { if (this.symbol == '^') { this.symbol = '|'; diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/ArgumentElement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/ArgumentElement.java index 912fd2672..9294fba4e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/ArgumentElement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/ArgumentElement.java @@ -1,11 +1,11 @@ package edu.rpi.legup.puzzle.shorttruthtable.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class ArgumentElement extends PlaceableElement { +public class ArgumentElement extends NonPlaceableElement { public ArgumentElement() { super( - "STTT-ELEM-0001", + "STTT-UNPL-0001", "Argument Element", "Argument of logic statement element", "edu/rpi/legup/images/shorttruthtable/tiles/LetterTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/GreenElement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/GreenElement.java index 56221fef3..783186baa 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/GreenElement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/GreenElement.java @@ -5,7 +5,7 @@ public class GreenElement extends PlaceableElement { public GreenElement() { super( - "STTT-ELEM-0002", + "STTT-PLAC-0001", "Green Element", "A green tile to set certain tiles to true", "edu/rpi/legup/images/shorttruthtable/tiles/GreenTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/LogicSymbolElement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/LogicSymbolElement.java index b82ebc2cb..5fed4b1df 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/LogicSymbolElement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/LogicSymbolElement.java @@ -1,11 +1,11 @@ package edu.rpi.legup.puzzle.shorttruthtable.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class LogicSymbolElement extends PlaceableElement { +public class LogicSymbolElement extends NonPlaceableElement { public LogicSymbolElement() { super( - "STTT-ELEM-0003", + "STTT-UNPL-0002", "Logic Symbol Element", "Logic symbol element", "edu/rpi/legup/images/shorttruthtable/tiles/ConditionalBiconditionalTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/RedElement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/RedElement.java index 2114e62ec..e2a589b65 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/RedElement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/RedElement.java @@ -5,7 +5,7 @@ public class RedElement extends PlaceableElement { public RedElement() { super( - "STTT-ELEM-0004", + "STTT-PLAC-0002", "Red Element", "A red tile to set certain tiles to false", "edu/rpi/legup/images/shorttruthtable/tiles/RedTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/UnknownElement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/UnknownElement.java index 52b54f202..d475bc05d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/UnknownElement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/UnknownElement.java @@ -5,7 +5,7 @@ public class UnknownElement extends PlaceableElement { public UnknownElement() { super( - "STTT-ELEM-0005", + "STTT-PLAC-0003", "Unknown Element", "A blank tile", "edu/rpi/legup/images/shorttruthtable/tiles/UnknownTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/shorttruthtable_elements_reference_sheet b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/shorttruthtable_elements_reference_sheet index c5421169f..471631553 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/shorttruthtable_elements_reference_sheet +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/elements/shorttruthtable_elements_reference_sheet @@ -1,5 +1,6 @@ -STTT-ELEM-0001 : ArgumentElement -STTT-ELEM-0002 : GreenElement -STTT-ELEM-0003 : LogicSymbolElement -STTT-ELEM-0004 : RedElement -STTT-ELEM-0005 : UnknownElement \ No newline at end of file +STTT-UNPL-0001 : ArgumentElement +STTT-UNPL-0002 : ConditionalBiconditionalElement + +STTT-PLAC-0001 : GreenElement +STTT-PLAC-0002 : RedElement +STTT-PLAC-0003 : UnknownElement \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleAtomic.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleAtomic.java index 22b49fd77..58d2068b2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleAtomic.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRuleAtomic.java @@ -44,9 +44,6 @@ public CaseBoard getCaseBoard(Board board) { @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { ArrayList cases = new ArrayList<>(); - if (puzzleElement == null) { - return cases; - } Board case1 = board.copy(); PuzzleElement data1 = case1.getPuzzleElement(puzzleElement); diff --git a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java index 8aeb51a46..99f771246 100644 --- a/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java +++ b/src/main/java/edu/rpi/legup/puzzle/shorttruthtable/rules/caserule/CaseRule_GenericStatement.java @@ -85,11 +85,6 @@ public CaseBoard getCaseBoard(Board board) { @SuppressWarnings("unchecked") @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { - - if (puzzleElement == null) { - return new ArrayList(); - } - ShortTruthTableBoard sttBoard = ((ShortTruthTableBoard) board); ShortTruthTableCell cell = sttBoard.getCellFromElement(puzzleElement); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java index 44f416cef..df5ba78a3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/Skyscrapers.java @@ -46,7 +46,7 @@ public Board generatePuzzle(int difficulty) { * @return true if the given dimensions are valid for Skyscrapers, false otherwise */ public boolean isValidDimensions(int rows, int columns) { - return rows >= 3 && rows == columns; + return rows >= 4 && rows == columns; } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java index 0fc133786..4cd09b254 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersBoard.java @@ -209,7 +209,7 @@ public void setCell(int x, int y, Element e, MouseEvent m) { SkyscrapersClue clue = this.getClue(x, y); if (e == null) return; if (clue != null) { - if (!e.getElementID().equals("SKYS-ELEM-0001")) { + if (!e.getElementID().equals("SKYS-UNPL-0003")) { return; } @@ -217,10 +217,10 @@ public void setCell(int x, int y, Element e, MouseEvent m) { if (clue.getData() < dimension.height) { clue.setData(clue.getData() + 1); } else { - clue.setData(1); + clue.setData(0); } } else { - if (clue.getData() > 1) { + if (clue.getData() > 0) { clue.setData(clue.getData() - 1); } else { clue.setData(dimension.height); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCell.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCell.java index 9e7283b20..1cf9a357b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersCell.java @@ -27,10 +27,10 @@ public SkyscrapersType getType() { @Override public void setType(Element e, MouseEvent m) { switch (e.getElementID()) { - case "SKYS-ELEM-0002": + case "SKYS-UNPL-0001": this.data = 0; break; - case "SKYS-ELEM-0001": + case "SKYS-UNPL-0002": switch (m.getButton()) { case MouseEvent.BUTTON1: if (this.data <= 0 || this.data >= this.max) { diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClueView.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClueView.java index 0e6345ff1..5a49a1476 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClueView.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/SkyscrapersClueView.java @@ -25,11 +25,11 @@ public SkyscrapersClue getPuzzleElement() { @Override public void draw(Graphics2D graphics2D) { drawElement(graphics2D); - if (this.isHover()) { - drawHover(graphics2D); - } if (this.isShowCasePicker() && this.isCaseRulePickable()) { drawCase(graphics2D); + if (this.isHover()) { + drawHover(graphics2D); + } } } @@ -54,19 +54,6 @@ public void drawElement(Graphics2D graphics2D) { int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); - - // REPRESENT NO CLUE AS EMPTY STRING INSTEAD OF 0, SOLVING PUZZLES WITH NO CLUE IS CURRENTLY NOT WORKING - // IF YOU ARE IMPLEMENTING NO CLUE FUNCTIONALITY, UNCOMMENT BELOW CODE AND DELETE OTHER IF STATEMENT, - // ADDITIONALLY, GO TO SkyscrapersBoard AND EDIT LINES 220 AND 223 SO YOU CAN CYCLE FOR NO CLUE - // IN THE SKYSCRAPERS PUZZLE EDITOR -// if (value.equals("0")) { -// value = ""; -// } - if (value.equals("0")) { - value = "1"; - clue.setData(1); - } - graphics2D.drawString(value, xText, yText); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/ClueTile.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/ClueTile.java new file mode 100644 index 000000000..64c9033e6 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/ClueTile.java @@ -0,0 +1,14 @@ +package edu.rpi.legup.puzzle.skyscrapers.elements; + +import edu.rpi.legup.model.elements.NonPlaceableElement; + +public class ClueTile extends NonPlaceableElement { + + public ClueTile() { + super( + "SKYS-UNPL-0003", + "Clue Tile", + "Clue Updater", + "edu/rpi/legup/images/skyscrapers/tiles/ClueTile.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/NumberTile.java index f60e5fe8b..4d6b37c9a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/NumberTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/NumberTile.java @@ -1,11 +1,11 @@ package edu.rpi.legup.puzzle.skyscrapers.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class NumberTile extends PlaceableElement { +public class NumberTile extends NonPlaceableElement { public NumberTile() { super( - "SKYS-ELEM-0001", + "SKYS-UNPL-0002", "Number Tile", "A numbered tile", "edu/rpi/legup/images/skyscrapers/tiles/ClueTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/UnknownTile.java index 07f6a1238..2fb21193a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/UnknownTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/UnknownTile.java @@ -1,11 +1,11 @@ package edu.rpi.legup.puzzle.skyscrapers.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class UnknownTile extends PlaceableElement { +public class UnknownTile extends NonPlaceableElement { public UnknownTile() { super( - "SKYS-ELEM-0002", + "SKYS-UNPL-0001", "Unknown", "A blank tile", "edu/rpi/legup/images/skyscrapers/tiles/UnknownTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/skyscrapers_elements_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/skyscrapers_elements_reference_sheet.txt index 14e76a29d..604e1824e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/skyscrapers_elements_reference_sheet.txt +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/skyscrapers_elements_reference_sheet.txt @@ -1,2 +1,3 @@ -SKYS-ELEM-0001: NumberTile -SKYS-ELEM-0002: UnknownTile \ No newline at end of file +SKYS-UNPL-0001: Unknown Tile +SKYS-UNPL-0002: Number Tile +SKYS-UNPL-0003: Clue "Tile" \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java index b48962c41..45bdadea3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/CellForNumberCaseRule.java @@ -61,9 +61,6 @@ public CaseBoard getCaseBoard(Board board) { public ArrayList getCasesFor(Board board, PuzzleElement puzzleElement, Integer number) { ArrayList cases = new ArrayList<>(); - if (puzzleElement == null) { - return cases; - } SkyscrapersClue clue = (SkyscrapersClue) puzzleElement; SkyscrapersBoard skyscrapersboard = (SkyscrapersBoard) board; diff --git a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java index 4f8e1df6b..145dd6ee2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/skyscrapers/rules/NumberForCellCaseRule.java @@ -47,9 +47,6 @@ public CaseBoard getCaseBoard(Board board) { @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { ArrayList cases = new ArrayList<>(); - if (puzzleElement == null) { - return cases; - } SkyscrapersCell cell = (SkyscrapersCell) puzzleElement; SkyscrapersBoard skyscrapersboard = (SkyscrapersBoard) board; diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt b/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt new file mode 100644 index 000000000..5a9ec0f0a --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt @@ -0,0 +1,235 @@ +//StarBattle.java + +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.model.Puzzle; +import edu.rpi.legup.model.gameboard.Board; + +public class StarBattle extends Puzzle { + public StarBattle() { + super(); + this.name = "StarBattle"; + + this.importer = new StarBattleImporter(this); + this.exporter = new StarBattleExporter(this); + + this.factory = new StarBattleCellFactory(); + } + + @Override + public void initializeView() { + } + + @Override + public Board generatePuzzle(int difficulty) { + return null; + } + + @Override + public boolean isBoardComplete(Board board) { + return true; + } + + @Override + public void onBoardChange(Board board) { + } +} + +//StarBattleBoard.java + +package edu.rpi.legup.puzzle.lightup; + +import edu.rpi.legup.model.gameboard.GridBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +import java.awt.*; +import java.util.HashSet; +import java.util.Set; + +public class StarBattleBoard extends GridBoard { + + private int size; + private vector group_sizes; + + /** + * StarBattleBoard Constructor - create a new Star Battle board + * + * @param size size of one side of the star battle board + */ + + public StarBattleBoard(int size) { + super(size, size); + group_sizes = vector(size); + } + + @Override + public StarBattleCell getCell(int x, int y) { + return (StarBattleCell) super.getCell(x, y); + } + + +} + +//StarBattleCell.java + +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.model.gameboard.GridCell; + +import java.awt.*; +import java.util.HashSet; +import java.util.Set; + +public class StarBattleCell extends GridCell { + private int groupIndex; + private int max; + + /** + * StarBattleCell Constructor - creates a new StarBattle cell to hold the puzzleElement + * + * @param valueInt value of the star battle cell denoting its state + * @param location location of the cell on the board + * @param size size of the star battle cell + */ + public StarBattleCell(int value, Point location, int groupIndex, int size) { + super(value, location); + this.groupIndex = groupIndex; + this.max = size; + } + + @Override + public void setType(Element e, MouseEvent m) { + switch (e.getElementID()) { + case "SBUP-PLAC-0001": + this.data = -3; + break; + case "SBUP-PLAC-0002": + this.data = -2; + break; + case "SBUP-PLAC-0003": + this.data = -1; + break; + case "SBUP-UNPL-0001"://Not sure how button events work + switch (m.getButton()){ + case MouseEvent.BUTTON1: + if (this.data < 0 || this.data > 3) { + this.data = 0; + } + else { + this.data = this.data + 1; + } + break; + case MouseEvent.BUTTON3: + if (this.data > 0) { + this.data = this.data - 1; + } + else { + this.data = 3;//Unsure + } + break; + } + break; + } + } + + public LightUpCellType getType() { + switch (data) { + case -3: + return LightUpCellType.UNKNOWN; + case -2: + return LightUpCellType.STAR; + case -1: + return LightUpCellType.BLACK; + default: + if (data >= 0) { + return StarBattleCellType.WHITE; + } + } + return null; + } + + /** + * Gets the region index of the cell + * + * @return group index of the cell + */ + public int getGroupIndex() { + return groupIndex; + } + + /** + * Gets the size of the cell + * + * @return size of the cell + */ + + public int getMax() { + return max; + } + +} + +//StarBattleCellController.java + +package edu.rpi.legup.puzzle.starbattle; + +import edu.rpi.legup.controller.ElementController; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +import java.awt.event.MouseEvent; + +public class StarBattleCellController extends ElementController { + @Override + public void changeCell(MouseEvent e, PuzzleElement data) { + StarBattleCell cell = (StarBattleCell) data; + if (e.getButton() == MouseEvent.BUTTON1) { + if (e.isControlDown()) { + this.boardView.getSelectionPopupMenu().show(boardView, this.boardView.getCanvas().getX() + e.getX(), this.boardView.getCanvas().getY() + e.getY()); + } + else { + if (cell.getData() == 0) { + data.setData(-3); + } + else { + data.setData(cell.getData() + 1); + } + } + } + else { + if (e.getButton() == MouseEvent.BUTTON3) { + if (cell.getData() == -3) { + data.setData(0); + } + else { + data.setData(cell.getData() - 1); + } + } + } + } +} + +//StarBattleCellFactory.java + + + +//StarBattleCellType.java +package edu.rpi.legup.puzzle.starbattle; + +public enum StarBattleType { + UNKNOWN(-3), STAR(-2), BLACK(-1), WHITE(0); + + public int value; + + StarBattleCell(int value) { + this.value = value; + } +} + +//StarBattleExporter.java +//StarBattleImporter.java +//StarBattleView.java + +How to run Legup: + +./gradlew build +Java -jar build/libs/Legup.jar \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java index c4bbf7297..99f42886e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/BlackTile.java @@ -1,8 +1,8 @@ package edu.rpi.legup.puzzle.starbattle.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class BlackTile extends PlaceableElement { +public class BlackTile extends NonPlaceableElement { public BlackTile() { super( "STBL-PLAC-0002", diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java index 793d4dbeb..13ada3f4d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/StarTile.java @@ -1,8 +1,8 @@ package edu.rpi.legup.puzzle.starbattle.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class StarTile extends PlaceableElement { +public class StarTile extends NonPlaceableElement { public StarTile() { super( "STBL-PLAC-0001", diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java index 30921de8d..425fb5d5e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/UnknownTile.java @@ -1,8 +1,8 @@ package edu.rpi.legup.puzzle.starbattle.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class UnknownTile extends PlaceableElement { +public class UnknownTile extends NonPlaceableElement { public UnknownTile() { super( "STBL-UNPL-0001", diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/starbattle_elements_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/starbattle_elements_reference_sheet.txt deleted file mode 100644 index 82352bd04..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/starbattle_elements_reference_sheet.txt +++ /dev/null @@ -1,4 +0,0 @@ -STBL-ELEM-0001 : BlackTile -STBL-ELEM-0002 : StarTile -STBL-ELEM-0003 : UnknownTile -STBL-ELEM-0004 : WhiteTile \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java index efd86bd7b..df900dcd5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/StarOrEmptyCaseRule.java @@ -84,10 +84,6 @@ public CaseBoard getCaseBoard(Board board) { @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { ArrayList cases = new ArrayList<>(); - if (puzzleElement == null) { - return cases; - } - Board case1 = board.copy(); PuzzleElement data1 = case1.getPuzzleElement(puzzleElement); data1.setData(StarBattleCellType.STAR.value); diff --git a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt new file mode 100644 index 000000000..f18965fd6 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt @@ -0,0 +1,19 @@ +Case Rules: +Add Star: STBL-CASE-0001 +Star or Empty: STBL-CASE-0002 + +Basic Rules: +Blackout: STBL-BASC-0001 +Columns Within Regions: STBL-BASC-0002 +Columns Within Rows: STBL-BASC-0003 +Finish With Stars: STBL-BASC-0004 +Regions Within Columns: STBL-BASC-0005 +Regions Within Rows: STBL-BASC-0006 +Rows Within Columns: STBL-BASC-0007 +Rows Within Regions: STBL-BASC-0008 +Surround Star: STBL-BASC-0009 + +Contradiction Rules: +Too Many Stars: STBL-CONT-0001 +Too Few Stars: STBL-CONT-0002 +Clashing Orbit: STBL-CONT-0003 \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/ModelSudokuBoard.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/ModelSudokuBoard.java deleted file mode 100644 index f7893ca32..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/ModelSudokuBoard.java +++ /dev/null @@ -1,17 +0,0 @@ -package edu.rpi.legup.puzzle.sudoku; - -public class ModelSudokuBoard { - public int getModelRegionNumbers(int index) { - int columnMod = index % 3 + 1; - int rowMod = ((index / 9) % 3) * 3; - return columnMod + rowMod; - } - - public int getModelRowNumbers(int index) { - return index % 9 + 1; - } - - public int getModelColumnNumbers(int index) { - return index / 9 + 1; - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/PossibleNumberCaseBoard.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/PossibleNumberCaseBoard.java index c5f9eec2a..0b6971235 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/PossibleNumberCaseBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/PossibleNumberCaseBoard.java @@ -2,7 +2,7 @@ import edu.rpi.legup.model.gameboard.CaseBoard; import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.puzzle.sudoku.rules.PossibleCellsForNumberRegionCaseRule; +import edu.rpi.legup.puzzle.sudoku.rules.PossibleNumberCaseRule; import java.awt.event.MouseEvent; import java.util.HashSet; import java.util.Set; @@ -15,7 +15,7 @@ public class PossibleNumberCaseBoard extends CaseBoard { private Set pickableCols; public PossibleNumberCaseBoard( - SudokuBoard baseBoard, PossibleCellsForNumberRegionCaseRule caseRule, SudokuCell cell) { + SudokuBoard baseBoard, PossibleNumberCaseRule caseRule, SudokuCell cell) { super(baseBoard, caseRule); this.cell = cell; this.pickableRegions = new HashSet<>(); diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/Sudoku.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/Sudoku.java index c27269536..877c92665 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/Sudoku.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/Sudoku.java @@ -29,8 +29,6 @@ public BoardView getBoardView() { @Override public void initializeView() { boardView = new SudokuView((SudokuBoard) currentBoard); - boardView.setBoard(currentBoard); - addBoardListener(boardView); } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCell.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCell.java index 4e194ae2c..006e6c0a5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCell.java @@ -1,9 +1,7 @@ package edu.rpi.legup.puzzle.sudoku; -import edu.rpi.legup.model.elements.Element; import edu.rpi.legup.model.gameboard.GridCell; import java.awt.*; -import java.awt.event.MouseEvent; import java.util.HashSet; import java.util.Set; @@ -15,10 +13,10 @@ public class SudokuCell extends GridCell { /** * SudokuCell Constructor - creates a new Sudoku cell to hold the puzzleElement * - * @param value value of the sudoku cell - * @param location location of the cell on the board + * @param value value of the sudoku cell + * @param location location of the cell on the board * @param groupIndex index of the group the cell is in on the board - * @param size size of the sudoku cell + * @param size size of the sudoku cell */ public SudokuCell(int value, Point location, int groupIndex, int size) { super(value, location); @@ -61,36 +59,4 @@ public SudokuCell copy() { copy.setGiven(isGiven); return copy; } - - /** - * Sets the type of this NurikabeCell - * - * @param e element to set the type of this nurikabe cell to - */ - @Override - public void setType(Element e, MouseEvent m) { - if (e.getElementName().equals("Number Tile")) { - if (m.getButton() == MouseEvent.BUTTON1) { - if (this.data <= 0 || this.data > 8) { - this.data = 1; - } - else { - this.data = this.data + 1; - } - } - else { - if (m.getButton() == MouseEvent.BUTTON3) { - if (this.data > 1) { - this.data = this.data - 1; - } - else { - this.data = 9; - } - } - } - } - else if (e.getElementName().equals("Unknown Tile")) { - this.data = 0; - } - } } diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCellController.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCellController.java index bcad1a0ce..9b24f13da 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCellController.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCellController.java @@ -8,7 +8,7 @@ public class SudokuCellController extends ElementController { @Override public void changeCell(MouseEvent e, PuzzleElement data) { SudokuCell cell = (SudokuCell) data; - + System.out.print(111); if (e.getButton() == MouseEvent.BUTTON1) { if (e.isControlDown()) { this.boardView diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuImporter.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuImporter.java index 5084279c3..68bf1e795 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuImporter.java @@ -110,6 +110,16 @@ public void initializeBoard(Node node) throws InvalidFileFormatException { } } } + // + // for(int y = 0; y < size; y++) + // { + // for(int x = 0; x < size; x++) + // { + // SudokuCell cell = sudokuBoard.getCell(x, y); + // System.err.println("(" + x + ", " + y + ") - " + + // cell.getGroupIndex()); + // } + // } puzzle.setCurrentBoard(sudokuBoard); } catch (NumberFormatException e) { diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuView.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuView.java index d2a8d95ab..aa58f9a23 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuView.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuView.java @@ -22,15 +22,10 @@ public SudokuView(SudokuBoard board) { for (int k = 0; k < gridSize.width; k++) { Point location = new Point( - k * elementSize.width + (k / minorSize) * 4 + 5,// - i * elementSize.height + (i / minorSize) * 4 + 5); -// Point location = -// new Point( -// k * elementSize.width, -// i * elementSize.height); + k * elementSize.width + (k / minorSize) * 4 + 5, + i * elementSize.height + (i / minorSize) * 4 + 5); SudokuElementView element = new SudokuElementView(board.getCell(k, i)); element.setIndex(i * gridSize.width + k); - element.setIndex(i * gridSize.width); element.setSize(elementSize); element.setLocation(location); elementViews.add(element); diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/elements/NumberTile.java index b8f4a596c..12183d70d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/elements/NumberTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/elements/NumberTile.java @@ -1,14 +1,26 @@ - package edu.rpi.legup.puzzle.sudoku.elements; import edu.rpi.legup.model.elements.PlaceableElement; public class NumberTile extends PlaceableElement { + private int object_num; + public NumberTile() { - super( - "SUDO-ELEM-0001", - "Number Tile", - "A number tile", - "edu/rpi/legup/images/sudoku/tiles/NumberTile.png"); + super("SUDO-PLAC-0001", "Number Tile", "A numbered tile", null); + object_num = 0; + } + + /** + * @return this object's tile number... + */ + public int getTileNumber() { + return object_num; + } + + /** + * @param num Amount to set tile object to. + */ + public void setTileNumber(int num) { + object_num = num; } -} \ No newline at end of file +} diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/elements/UnknownTile.java deleted file mode 100644 index 162ba46d1..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/elements/UnknownTile.java +++ /dev/null @@ -1,13 +0,0 @@ -package edu.rpi.legup.puzzle.sudoku.elements; - -import edu.rpi.legup.model.elements.PlaceableElement; - -public class UnknownTile extends PlaceableElement { - public UnknownTile() { - super( - "SUDO-ELEM-0002", - "Unknown Tile", - "A blank tile", - "edu/rpi/legup/images/sudoku/tiles/UnknownTile.png"); - } -} \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/elements/sudoku_elements_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/sudoku/elements/sudoku_elements_reference_sheet.txt deleted file mode 100644 index b8df27eb6..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/elements/sudoku_elements_reference_sheet.txt +++ /dev/null @@ -1,2 +0,0 @@ -SUDO-ELEM-0001 : NumberTile -SUDO-ELEM-0002 : UnknownTile \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/AdvancedDeductionDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/AdvancedDeductionDirectRule.java new file mode 100644 index 000000000..190679b41 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/AdvancedDeductionDirectRule.java @@ -0,0 +1,99 @@ +package edu.rpi.legup.puzzle.sudoku.rules; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import edu.rpi.legup.model.rules.DirectRule; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.sudoku.SudokuBoard; +import edu.rpi.legup.puzzle.sudoku.SudokuCell; + +public class AdvancedDeductionDirectRule extends DirectRule { + + public AdvancedDeductionDirectRule() { + super( + "SUDO-BASC-0001", + "Advanced Deduction", + "Use of group logic deduces more answers by means of forced by Location and forced" + + " by Deduction", + "edu/rpi/legup/images/sudoku/AdvancedDeduction.png"); + } + + /** + * Checks whether the child node logically follows from the parent node at the specific + * puzzleElement index using this rule + * + * @param transition transition to check + * @param puzzleElement equivalent puzzleElement + * @return null if the child node logically follow from the parent node at the specified + * puzzleElement, otherwise error message + */ + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + SudokuBoard initialBoard = (SudokuBoard) transition.getParents().get(0).getBoard(); + SudokuBoard finalBoard = (SudokuBoard) transition.getBoard(); + + SudokuCell cell = (SudokuCell) finalBoard.getPuzzleElement(puzzleElement); + int index = cell.getIndex(); + int groupSize = initialBoard.getWidth(); + int groupDim = (int) Math.sqrt(groupSize); + int rowIndex = index / groupSize; + int colIndex = index % groupSize; + int relX = rowIndex / groupDim; + int relY = colIndex % groupDim; + int groupNum = rowIndex / groupDim * groupDim + colIndex / groupDim; + boolean[][] possible = new boolean[groupDim][groupDim]; + for (int y = 0; y < groupDim; y++) { + for (int x = 0; x < groupDim; x++) { + SudokuCell c = initialBoard.getCell(groupNum, x, y); + if (c.getData() == cell.getData() && x != relX && y != relY) { + return super.getRuleName() + ": Duplicate value in sub-region"; + } + possible[y][x] = c.getData() == 0; + } + } + for (int y = 0; y < groupDim; y++) { + for (int x = 0; x < groupSize; x++) { + SudokuCell r = initialBoard.getCell(x, (groupNum / groupDim) * groupDim + y); + SudokuCell c = initialBoard.getCell((groupNum % groupDim) * groupDim + y, x); + if (r.getData() == cell.getData()) { + for (int i = 0; i < groupDim; i++) { + possible[y][i] = false; + } + } + if (c.getData() == cell.getData()) { + for (int i = 0; i < groupDim; i++) { + possible[i][y] = false; + } + } + } + } + boolean isForced = false; + for (int y = 0; y < groupDim; y++) { + for (int x = 0; x < groupDim; x++) { + if (possible[y][x] && !isForced) { + isForced = true; + } else { + if (possible[y][x]) { + return super.getInvalidUseOfRuleMessage() + ": Not forced"; + } + } + } + } + if (!isForced) { + return super.getInvalidUseOfRuleMessage() + ": Not forced"; + } + return null; + } + + /** + * Creates a transition {@link Board} that has this rule applied to it using the {@link + * TreeNode}. + * + * @param node tree node used to create default transition board + * @return default board or null if this rule cannot be applied to this tree node + */ + @Override + public Board getDefaultBoard(TreeNode node) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/LastCellForNumberDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/LastCellForNumberDirectRule.java index 6544bf7c3..fd03ef36c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/LastCellForNumberDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/LastCellForNumberDirectRule.java @@ -15,7 +15,7 @@ public LastCellForNumberDirectRule() { "SUDO-BASC-0002", "Last Cell for Number", "This is the only cell open in its group for some number.", - "edu/rpi/legup/images/sudoku/rules/forcedByElimination.png"); + "edu/rpi/legup/images/sudoku/forcedByElimination.png"); } /** @@ -32,146 +32,52 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem SudokuBoard finalBoard = (SudokuBoard) transition.getBoard(); SudokuCell cell = (SudokuCell) finalBoard.getPuzzleElement(puzzleElement); - - // Check if empty cell placed if (cell.getData() == 0) { return super.getInvalidUseOfRuleMessage() + ": Cell is not forced at this index"; } - // Get defaults + int size = initialBoard.getSize(); + Set region = initialBoard.getRegion(cell.getGroupIndex()); Set row = initialBoard.getRow(cell.getLocation().y); Set col = initialBoard.getCol(cell.getLocation().x); - // Check if new cell conflicts group - for (SudokuCell c : region) { - if (c.getData() == cell.getData()) { - return super.getInvalidUseOfRuleMessage() + ": Cell is not forced at this index"; - } - } - for (SudokuCell c : row) { - if (c.getData() == cell.getData()) { - return super.getInvalidUseOfRuleMessage() + ": Cell is not forced at this index"; - } - } - for (SudokuCell c : col) { - if (c.getData() == cell.getData()) { - return super.getInvalidUseOfRuleMessage() + ": Cell is not forced at this index"; - } - } - - // // - // Loop to see if the number is constrained to the cell - boolean restrained = true; - for (SudokuCell c : region) { - // Test if its not a valid testing cell - if (c.getData() != 0) { - continue; - } - if (c.getLocation().y == cell.getLocation().y - && c.getLocation().x == cell.getLocation().x) { - continue; - } - // Check if cell is eligible to hold number - Set crow = initialBoard.getRow(c.getLocation().y); - Set ccol = initialBoard.getCol(c.getLocation().x); - boolean contains = false; - for (SudokuCell rc : crow) { - if (rc.getData() == cell.getData()) { + boolean contains = false; + if (region.size() == size - 1) { + for (SudokuCell c : region) { + if (cell.getData() == c.getData()) { contains = true; + break; } } - for (SudokuCell cc : ccol) { - if (cc.getData() == cell.getData()) { - contains = true; - } - } - // Stop if another cell can hold number if (!contains) { - restrained = false; - break; + return null; } } - // Output if success - if (restrained) { - return null; - } - - // // - // Loop to see if the number is constrained to the cell - restrained = true; - for (SudokuCell c : row) { - // Test if its not a valid testing cell - if (c.getData() != 0) { - continue; - } - if (c.getLocation().y == cell.getLocation().y - && c.getLocation().x == cell.getLocation().x) { - continue; - } - // Check if cell is eligible to hold number - Set cregion = initialBoard.getRegion(c.getGroupIndex()); - Set ccol = initialBoard.getCol(c.getLocation().x); - boolean contains = false; - for (SudokuCell rc : cregion) { - if (rc.getData() == cell.getData()) { - contains = true; - } - } - for (SudokuCell cc : ccol) { - if (cc.getData() == cell.getData()) { + if (row.size() == size - 1) { + contains = false; + for (SudokuCell c : row) { + if (cell.getData() == c.getData()) { contains = true; + break; } } - // Stop if another cell can hold number if (!contains) { - restrained = false; - break; + return null; } } - // Output if success - if (restrained) { - return null; - } - - // // - // Loop to see if the number is constrained to the cell - restrained = true; - for (SudokuCell c : col) { - // Test if its not a valid testing cell - if (c.getData() != 0) { - continue; - } - if (c.getLocation().y == cell.getLocation().y - && c.getLocation().x == cell.getLocation().x) { - continue; - } - // Check if cell is eligible to hold number - Set cregion = initialBoard.getRegion(c.getGroupIndex()); - Set crow = initialBoard.getRow(c.getLocation().y); - boolean contains = false; - for (SudokuCell rc : cregion) { - if (rc.getData() == cell.getData()) { + if (col.size() == size - 1) { + contains = false; + for (SudokuCell c : col) { + if (cell.getData() == c.getData()) { contains = true; + break; } } - for (SudokuCell cc : crow) { - if (cc.getData() == cell.getData()) { - contains = true; - } - } - // Stop if another cell can hold number if (!contains) { - restrained = false; - break; + return null; } } - // Output if success - if (restrained) { - return null; - } - - // Output fail return super.getInvalidUseOfRuleMessage() + ": Cell is not forced at this index"; } diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/LastNumberForCellDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/LastNumberForCellDirectRule.java index 333d91749..ca0ac3023 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/LastNumberForCellDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/LastNumberForCellDirectRule.java @@ -16,7 +16,7 @@ public LastNumberForCellDirectRule() { "SUDO-BASC-0003", "Last Number for Cell", "This is the only number left that can fit in the cell of a group.", - "edu/rpi/legup/images/sudoku/rules/forcedByDeduction.png"); + "edu/rpi/legup/images/sudoku/forcedByDeduction.png"); } /** @@ -32,37 +32,28 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem SudokuBoard initialBoard = (SudokuBoard) transition.getParents().get(0).getBoard(); SudokuBoard finalBoard = (SudokuBoard) transition.getBoard(); - // Assign basics + int index = puzzleElement.getIndex(); int groupSize = initialBoard.getWidth(); int groupDim = (int) Math.sqrt(groupSize); - - // Get position info - int index = puzzleElement.getIndex(); int rowIndex = index / groupSize; int colIndex = index % groupSize; - int groupNum = (rowIndex / groupDim) * groupDim + (colIndex / groupDim); - - // Create hashset of all numbers + int groupNum = rowIndex / groupDim * groupDim + colIndex % groupDim; HashSet numbers = new HashSet<>(); for (int i = 1; i <= groupSize; i++) { numbers.add(i); } - - // Run through region, row, col to see contradicitng numbers for (int i = 0; i < groupSize; i++) { SudokuCell cell = initialBoard.getCell(groupNum, i % groupDim, i / groupDim); numbers.remove(cell.getData()); } for (int i = 0; i < groupSize; i++) { - SudokuCell cell = initialBoard.getCell(i, rowIndex); + SudokuCell cell = initialBoard.getCell(i, colIndex); numbers.remove(cell.getData()); } for (int i = 0; i < groupSize; i++) { - SudokuCell cell = initialBoard.getCell(colIndex, i); + SudokuCell cell = initialBoard.getCell(rowIndex, i); numbers.remove(cell.getData()); } - - // Check if plausible if (numbers.size() > 1) { return super.getInvalidUseOfRuleMessage() + ": The number at the index is not forced"; } else { @@ -73,11 +64,7 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem + ": The number at the index is forced but not correct"; } } - if (numbers.toArray(new Integer[1])[0] == puzzleElement.getData()) { - return null; - } - return super.getInvalidUseOfRuleMessage() - + ": The number at the index is forced but not correct"; + return null; } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberColumnContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberColumnContradictionRule.java deleted file mode 100644 index c8d627634..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberColumnContradictionRule.java +++ /dev/null @@ -1,90 +0,0 @@ -package edu.rpi.legup.puzzle.sudoku.rules; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.model.rules.ContradictionRule; -import edu.rpi.legup.puzzle.sudoku.SudokuBoard; -import edu.rpi.legup.puzzle.sudoku.SudokuCell; -import java.util.HashSet; -import java.util.Set; - -public class NoCellForNumberColumnContradictionRule extends ContradictionRule { - - public NoCellForNumberColumnContradictionRule() { - super( - "SUDO-CONT-0003", - "No Cell for Number (Column)", - "Process of elimination yields no valid numbers for an empty cell in a column.", - "edu/rpi/legup/images/sudoku/rules/NoCellForNumberColumn.png"); - } - - /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using - * this rule - * - * @param board board to check contradiction - * @param puzzleElement equivalent puzzleElement - * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message - */ - @Override - public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { - SudokuBoard sudokuBoard = (SudokuBoard) board; - SudokuCell cell = (SudokuCell) sudokuBoard.getPuzzleElement(puzzleElement); - if (cell.getData() != 0) { - return super.getNoContradictionMessage(); - } - - int groupSize = sudokuBoard.getSize(); - - Set col = sudokuBoard.getCol(cell.getGroupIndex()); - Set numbersNotInColumn = new HashSet<>(); - - for (int i = 1; i <= groupSize; i++) { - numbersNotInColumn.add(i); - } - for (SudokuCell c : col) { - if (c.getData() != 0) { - numbersNotInColumn.remove(c.getData()); - } - } - - for (Integer i : numbersNotInColumn) { - // Check if number can be in cell - boolean canFit = false; - for (SudokuCell c : col) { - if (c.getData() != 0) { - continue; - } - - // Get row and col groups - Set region = sudokuBoard.getRow(c.getLocation().y); - Set row = sudokuBoard.getCol(c.getLocation().x); - - // Check if it alr exists in row or col - boolean duplicate = false; - for (SudokuCell rc : region) { - if (rc.getData() == i) { - duplicate = true; - } - } - for (SudokuCell cc : row) { - if (cc.getData() == i) { - duplicate = true; - } - } - - // If there is no duplicate it can exist in the region - if (!duplicate) { - canFit = true; - break; - } - } - // If the number can't fit anywhere in region then contradiction - if (!canFit) { - return null; - } - } - return super.getNoContradictionMessage(); - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberRegionContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberRegionContradictionRule.java deleted file mode 100644 index f5106b858..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberRegionContradictionRule.java +++ /dev/null @@ -1,90 +0,0 @@ -package edu.rpi.legup.puzzle.sudoku.rules; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.model.rules.ContradictionRule; -import edu.rpi.legup.puzzle.sudoku.SudokuBoard; -import edu.rpi.legup.puzzle.sudoku.SudokuCell; -import java.util.HashSet; -import java.util.Set; - -public class NoCellForNumberRegionContradictionRule extends ContradictionRule { - - public NoCellForNumberRegionContradictionRule() { - super( - "SUDO-CONT-0001", - "No Cell for Number (Region)", - "Process of elimination yields no valid numbers for an empty cell in a region.", - "edu/rpi/legup/images/sudoku/rules/NoCellForNumberRegion.png"); - } - - /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using - * this rule - * - * @param board board to check contradiction - * @param puzzleElement equivalent puzzleElement - * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message - */ - @Override - public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { - SudokuBoard sudokuBoard = (SudokuBoard) board; - SudokuCell cell = (SudokuCell) sudokuBoard.getPuzzleElement(puzzleElement); - if (cell.getData() != 0) { - return super.getNoContradictionMessage(); - } - - int groupSize = sudokuBoard.getSize(); - - Set region = sudokuBoard.getRegion(cell.getGroupIndex()); - Set numbersNotInRegion = new HashSet<>(); - - for (int i = 1; i <= groupSize; i++) { - numbersNotInRegion.add(i); - } - for (SudokuCell c : region) { - if (c.getData() != 0) { - numbersNotInRegion.remove(c.getData()); - } - } - - for (Integer i : numbersNotInRegion) { - // Check if number can be in cell - boolean canFit = false; - for (SudokuCell c : region) { - if (c.getData() != 0) { - continue; - } - - // Get row and col groups - Set row = sudokuBoard.getRow(c.getLocation().y); - Set col = sudokuBoard.getCol(c.getLocation().x); - - // Check if it alr exists in row or col - boolean duplicate = false; - for (SudokuCell rc : row) { - if (rc.getData() == i) { - duplicate = true; - } - } - for (SudokuCell cc : col) { - if (cc.getData() == i) { - duplicate = true; - } - } - - // If there is no duplicate it can exist in the region - if (!duplicate) { - canFit = true; - break; - } - } - // If the number can't fit anywhere in region then contradiction - if (!canFit) { - return null; - } - } - return super.getNoContradictionMessage(); - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberRowContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberRowContradictionRule.java deleted file mode 100644 index e3f9f764a..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberRowContradictionRule.java +++ /dev/null @@ -1,90 +0,0 @@ -package edu.rpi.legup.puzzle.sudoku.rules; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.model.rules.ContradictionRule; -import edu.rpi.legup.puzzle.sudoku.SudokuBoard; -import edu.rpi.legup.puzzle.sudoku.SudokuCell; -import java.util.HashSet; -import java.util.Set; - -public class NoCellForNumberRowContradictionRule extends ContradictionRule { - - public NoCellForNumberRowContradictionRule() { - super( - "SUDO-CONT-0002", - "No Cell for Number (Row)", - "Process of elimination yields no valid numbers for an empty cell in a row.", - "edu/rpi/legup/images/sudoku/rules/NoCellForNumberRow.png"); - } - - /** - * Checks whether the transition has a contradiction at the specific puzzleElement index using - * this rule - * - * @param board board to check contradiction - * @param puzzleElement equivalent puzzleElement - * @return null if the transition contains a contradiction at the specified puzzleElement, - * otherwise error message - */ - @Override - public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { - SudokuBoard sudokuBoard = (SudokuBoard) board; - SudokuCell cell = (SudokuCell) sudokuBoard.getPuzzleElement(puzzleElement); - if (cell.getData() != 0) { - return super.getNoContradictionMessage(); - } - - int groupSize = sudokuBoard.getSize(); - - Set row = sudokuBoard.getRow(cell.getGroupIndex()); - Set numbersNotInRow = new HashSet<>(); - - for (int i = 1; i <= groupSize; i++) { - numbersNotInRow.add(i); - } - for (SudokuCell c : row) { - if (c.getData() != 0) { - numbersNotInRow.remove(c.getData()); - } - } - - for (Integer i : numbersNotInRow) { - // Check if number can be in cell - boolean canFit = false; - for (SudokuCell c : row) { - if (c.getData() != 0) { - continue; - } - - // Get row and col groups - Set region = sudokuBoard.getRow(c.getLocation().y); - Set col = sudokuBoard.getCol(c.getLocation().x); - - // Check if it alr exists in row or col - boolean duplicate = false; - for (SudokuCell rc : region) { - if (rc.getData() == i) { - duplicate = true; - } - } - for (SudokuCell cc : col) { - if (cc.getData() == i) { - duplicate = true; - } - } - - // If there is no duplicate it can exist in the region - if (!duplicate) { - canFit = true; - break; - } - } - // If the number can't fit anywhere in region then contradiction - if (!canFit) { - return null; - } - } - return super.getNoContradictionMessage(); - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoNumberForCellContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoSolutionContradictionRule.java similarity index 72% rename from src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoNumberForCellContradictionRule.java rename to src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoSolutionContradictionRule.java index 6ea8f0a2a..e44728d3e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoNumberForCellContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoSolutionContradictionRule.java @@ -8,14 +8,14 @@ import java.util.HashSet; import java.util.Set; -public class NoNumberForCellContradictionRule extends ContradictionRule { +public class NoSolutionContradictionRule extends ContradictionRule { - public NoNumberForCellContradictionRule() { + public NoSolutionContradictionRule() { super( - "SUDO-CONT-0004", - "No Number for Cell", + "SUDO-CONT-0001", + "No Solution for Cell", "Process of elimination yields no valid numbers for an empty cell.", - "edu/rpi/legup/images/sudoku/rules/NoSolution.png"); + "edu/rpi/legup/images/sudoku/NoSolution.png"); } /** @@ -41,19 +41,21 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { Set row = sudokuBoard.getRow(cell.getLocation().y); Set col = sudokuBoard.getCol(cell.getLocation().x); Set solution = new HashSet<>(); - for (SudokuCell s : region) { - solution.add(s.getData()); - } - for (SudokuCell s : row) { - solution.add(s.getData()); + for (int i = 1; i <= groupSize; i++) { + solution.add(i); } - for (SudokuCell s : col) { - solution.add(s.getData()); + for (SudokuCell c : region) { + solution.remove(c.getData()); + } + for (SudokuCell c : row) { + solution.remove(c.getData()); + } + for (SudokuCell c : col) { + solution.remove(c.getData()); } - solution.remove(0); - if (solution.size() == 9) { + if (solution.isEmpty()) { return null; } diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumbersForCellCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellCaseRule.java similarity index 59% rename from src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumbersForCellCaseRule.java rename to src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellCaseRule.java index e17acc26b..fb6da62d4 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumbersForCellCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellCaseRule.java @@ -5,18 +5,19 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.sudoku.*; +import edu.rpi.legup.puzzle.sudoku.SudokuBoard; +import edu.rpi.legup.puzzle.sudoku.SudokuCell; import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; -public class PossibleNumbersForCellCaseRule extends CaseRule { - - public PossibleNumbersForCellCaseRule() { +public class PossibleCellCaseRule extends CaseRule { + public PossibleCellCaseRule() { super( "SUDO-CASE-0001", - "Possible Numbers for Cell", - "An empty cell has a limited set of possible numbers that can fill it.", - "edu/rpi/legup/images/sudoku/rules/PossibleValues.png"); + "Possible Cells for Number", + "A number has a limited set of cells in which it can be placed.", + "edu/rpi/legup/images/sudoku/possible_cells_number.png"); } /** @@ -65,34 +66,42 @@ public CaseBoard getCaseBoard(Board board) { */ @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { - return getCases(board, puzzleElement, 1, GroupType.REGION); - } - - /** - * Gets the possible cases at a specific location based on this case rule - * - * @param board the current board state - * @param puzzleElement equivalent puzzleElement - * @param value value that the rule will be applied from - * @param groupType group type - * @return a list of elements the specified could be - */ - public ArrayList getCases( - Board board, PuzzleElement puzzleElement, int value, GroupType groupType) { ArrayList cases = new ArrayList<>(); - if (puzzleElement == null) { - return cases; - } - SudokuBoard sudokuBoard = (SudokuBoard) board; - List caseCells = new ArrayList<>(); SudokuCell cell = (SudokuCell) puzzleElement; - for (int i = 1; i <= 9; i++) { - Board newCase = sudokuBoard.copy(); - PuzzleElement element = newCase.getPuzzleElement(puzzleElement); - element.setData(i); - newCase.addModifiedData(element); + Set possibleValue = new HashSet<>(); + for (int i = 1; i <= sudokuBoard.getSize(); i++) { + possibleValue.add(i); + } + + int groupNum = cell.getGroupIndex(); + for (SudokuCell c : sudokuBoard.getRegion(groupNum)) { + if (c.getData().equals(c.getData())) { + possibleValue.remove(c.getData()); + } + } + + int rowNum = cell.getLocation().y; + for (SudokuCell c : sudokuBoard.getRegion(rowNum)) { + if (c.getData().equals(c.getData())) { + possibleValue.remove(c.getData()); + } + } + + int colNum = cell.getLocation().x; + for (SudokuCell c : sudokuBoard.getRegion(colNum)) { + if (c.getData().equals(c.getData())) { + possibleValue.remove(c.getData()); + } + } + + for (Integer i : possibleValue) { + SudokuBoard newCase = sudokuBoard.copy(); + + PuzzleElement newCasePuzzleElement = newCase.getPuzzleElement(puzzleElement); + newCasePuzzleElement.setData(i); + newCase.addModifiedData(newCasePuzzleElement); cases.add(newCase); } diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberRegionCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberRegionCaseRule.java deleted file mode 100644 index 47e408369..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberRegionCaseRule.java +++ /dev/null @@ -1,104 +0,0 @@ -package edu.rpi.legup.puzzle.sudoku.rules; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.CaseBoard; -import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.model.rules.CaseRule; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.sudoku.*; -import java.util.ArrayList; -import java.util.Set; - -public class PossibleCellsForNumberRegionCaseRule extends CaseRule { - - // Board math for translating indexes to numbers - private ModelSudokuBoard model = new ModelSudokuBoard(); - - // Old board for caseBoard reference - private SudokuBoard lagBoard; - - public PossibleCellsForNumberRegionCaseRule() { - super( - "SUDO-CASE-0002", - "Possible Cells for Number - Region", - "An empty cell has a limited set of possible numbers that can fill it.", - "edu/rpi/legup/images/sudoku/rules/possible_cells_number_region.png"); - } - - /** - * Checks whether the transition logically follows from the parent node using this rule - * - * @param transition transition to check - * @return null if the child node logically follow from the parent node, otherwise error message - */ - @Override - public String checkRuleRaw(TreeTransition transition) { - return null; - } - - /** - * Checks whether the child node logically follows from the parent node at the specific - * puzzleElement index using this rule - * - * @param transition transition to check - * @param puzzleElement equivalent puzzleElement - * @return null if the child node logically follow from the parent node at the specified - * puzzleElement, otherwise error message - */ - @Override - public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } - - @Override - public CaseBoard getCaseBoard(Board board) { - SudokuBoard sudokuBoard = (SudokuBoard) board.copy(); - lagBoard = (SudokuBoard) sudokuBoard.copy(); - CaseBoard caseBoard = new CaseBoard(sudokuBoard, this); - for (PuzzleElement puzzleElement : sudokuBoard.getPuzzleElements()) { - puzzleElement.setData(model.getModelRegionNumbers(puzzleElement.getIndex())); - caseBoard.addPickableElement(puzzleElement); - } - return caseBoard; - } - - /** - * Gets the possible cases at a specific location based on this case rule - * - * @param board the current board state - * @param puzzleElement equivalent puzzleElement - * @return a list of elements the specified could be - */ - @Override - public ArrayList getCases(Board board, PuzzleElement puzzleElement) { - return getCases(board, puzzleElement, 1, GroupType.REGION); - } - - /** - * Gets the possible cases at a specific location based on this case rule - * - * @param board the current board state - * @param puzzleElement equivalent puzzleElement - * @param value value that the rule will be applied from - * @param groupType group type - * @return a list of elements the specified could be - */ - public ArrayList getCases( - Board board, PuzzleElement puzzleElement, int value, GroupType groupType) { - ArrayList cases = new ArrayList<>(); - SudokuBoard sudokuBoard = lagBoard; - SudokuCell sourceCell = (SudokuCell) puzzleElement; - - Set group = sudokuBoard.getRegion(sourceCell.getGroupIndex()); - for (SudokuCell cell : group) { - if (cell.getData() == 0) { - Board newCase = sudokuBoard.copy(); - PuzzleElement element = newCase.getPuzzleElement(cell); - element.setData(model.getModelRegionNumbers(sourceCell.getIndex())); - newCase.addModifiedData(element); - cases.add(newCase); - } - } - return cases; - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberRowCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberRowCaseRule.java deleted file mode 100644 index 868541377..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberRowCaseRule.java +++ /dev/null @@ -1,107 +0,0 @@ -package edu.rpi.legup.puzzle.sudoku.rules; - -import edu.rpi.legup.model.gameboard.Board; -import edu.rpi.legup.model.gameboard.CaseBoard; -import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.model.rules.CaseRule; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.sudoku.GroupType; -import edu.rpi.legup.puzzle.sudoku.ModelSudokuBoard; -import edu.rpi.legup.puzzle.sudoku.SudokuBoard; -import edu.rpi.legup.puzzle.sudoku.SudokuCell; -import java.util.ArrayList; -import java.util.Set; - -public class PossibleCellsForNumberRowCaseRule extends CaseRule { - - // Board math for translating indexes to numbers - private ModelSudokuBoard model = new ModelSudokuBoard(); - - // Old board for caseBoard reference - private SudokuBoard lagBoard; - - public PossibleCellsForNumberRowCaseRule() { - super( - "SUDO-CASE-0003", - "Possible Cells for Number - Row", - "An empty cell has a limited set of possible numbers that can fill it.", - "edu/rpi/legup/images/sudoku/rules/possible_cells_number_row.png"); - } - - /** - * Checks whether the transition logically follows from the parent node using this rule - * - * @param transition transition to check - * @return null if the child node logically follow from the parent node, otherwise error message - */ - @Override - public String checkRuleRaw(TreeTransition transition) { - return null; - } - - /** - * Checks whether the child node logically follows from the parent node at the specific - * puzzleElement index using this rule - * - * @param transition transition to check - * @param puzzleElement equivalent puzzleElement - * @return null if the child node logically follow from the parent node at the specified - * puzzleElement, otherwise error message - */ - @Override - public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { - return null; - } - - @Override - public CaseBoard getCaseBoard(Board board) { - SudokuBoard sudokuBoard = (SudokuBoard) board.copy(); - lagBoard = (SudokuBoard) sudokuBoard.copy(); - CaseBoard caseBoard = new CaseBoard(sudokuBoard, this); - for (PuzzleElement puzzleElement : sudokuBoard.getPuzzleElements()) { - puzzleElement.setData(model.getModelRowNumbers(puzzleElement.getIndex())); - caseBoard.addPickableElement(puzzleElement); - } - return caseBoard; - } - - /** - * Gets the possible cases at a specific location based on this case rule - * - * @param board the current board state - * @param puzzleElement equivalent puzzleElement - * @return a list of elements the specified could be - */ - @Override - public ArrayList getCases(Board board, PuzzleElement puzzleElement) { - return getCases(board, puzzleElement, 1, GroupType.ROW); - } - - /** - * Gets the possible cases at a specific location based on this case rule - * - * @param board the current board state - * @param puzzleElement equivalent puzzleElement - * @param value value that the rule will be applied from - * @param groupType group type - * @return a list of elements the specified could be - */ - public ArrayList getCases( - Board board, PuzzleElement puzzleElement, int value, GroupType groupType) { - ArrayList cases = new ArrayList<>(); - SudokuBoard sudokuBoard = lagBoard; - SudokuCell sourceCell = (SudokuCell) puzzleElement; - - Set group = sudokuBoard.getRow(sourceCell.getLocation().y); - for (SudokuCell cell : group) { - if (cell.getData() == 0) { - Board newCase = sudokuBoard.copy(); - PuzzleElement element = newCase.getPuzzleElement(cell); - element.setData(model.getModelRowNumbers(sourceCell.getIndex())); - newCase.addModifiedData(element); - cases.add(newCase); - } - } - return cases; - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberColumnCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumberCaseRule.java similarity index 53% rename from src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberColumnCaseRule.java rename to src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumberCaseRule.java index bab0bc79b..e6ab0e64c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberColumnCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumberCaseRule.java @@ -6,26 +6,21 @@ import edu.rpi.legup.model.rules.CaseRule; import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.sudoku.GroupType; -import edu.rpi.legup.puzzle.sudoku.ModelSudokuBoard; +import edu.rpi.legup.puzzle.sudoku.PossibleNumberCaseBoard; import edu.rpi.legup.puzzle.sudoku.SudokuBoard; import edu.rpi.legup.puzzle.sudoku.SudokuCell; import java.util.ArrayList; +import java.util.List; import java.util.Set; -public class PossibleCellsForNumberColumnCaseRule extends CaseRule { +public class PossibleNumberCaseRule extends CaseRule { - // Board math for translating indexes to numbers - private ModelSudokuBoard model = new ModelSudokuBoard(); - - // Old board for caseBoard reference - private SudokuBoard lagBoard; - - public PossibleCellsForNumberColumnCaseRule() { + public PossibleNumberCaseRule() { super( - "SUDO-CASE-0004", - "Possible Cells for Number - Column", + "SUDO-CASE-0002", + "Possible Numbers for Cell", "An empty cell has a limited set of possible numbers that can fill it.", - "edu/rpi/legup/images/sudoku/rules/possible_cells_number_column.png"); + "edu/rpi/legup/images/sudoku/PossibleValues.png"); } /** @@ -55,12 +50,12 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem @Override public CaseBoard getCaseBoard(Board board) { - SudokuBoard sudokuBoard = (SudokuBoard) board.copy(); - lagBoard = (SudokuBoard) sudokuBoard.copy(); - CaseBoard caseBoard = new CaseBoard(sudokuBoard, this); - for (PuzzleElement puzzleElement : sudokuBoard.getPuzzleElements()) { - puzzleElement.setData(model.getModelColumnNumbers(puzzleElement.getIndex())); - caseBoard.addPickableElement(puzzleElement); + SudokuBoard sudokuBoard = (SudokuBoard) board; + PossibleNumberCaseBoard caseBoard = new PossibleNumberCaseBoard(sudokuBoard, this, null); + for (int i = 0; i < sudokuBoard.getSize(); i++) { + caseBoard.addPickableRegion(i); + caseBoard.addPickableRow(i); + caseBoard.addPickableCol(i); } return caseBoard; } @@ -74,7 +69,7 @@ public CaseBoard getCaseBoard(Board board) { */ @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { - return getCases(board, puzzleElement, 1, GroupType.COLUMN); + return getCases(board, puzzleElement, 1, GroupType.REGION); } /** @@ -89,19 +84,48 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { public ArrayList getCases( Board board, PuzzleElement puzzleElement, int value, GroupType groupType) { ArrayList cases = new ArrayList<>(); - SudokuBoard sudokuBoard = lagBoard; - SudokuCell sourceCell = (SudokuCell) puzzleElement; + SudokuBoard sudokuBoard = (SudokuBoard) board; + List caseCells = new ArrayList<>(); + SudokuCell cell = (SudokuCell) puzzleElement; - Set group = sudokuBoard.getCol(sourceCell.getLocation().x); - for (SudokuCell cell : group) { - if (cell.getData() == 0) { - Board newCase = sudokuBoard.copy(); - PuzzleElement element = newCase.getPuzzleElement(cell); - element.setData(model.getModelColumnNumbers(sourceCell.getIndex())); - newCase.addModifiedData(element); - cases.add(newCase); + Set group; + if (groupType == GroupType.REGION) { + group = sudokuBoard.getRegion(cell.getGroupIndex()); + } else { + if (groupType == GroupType.ROW) { + group = sudokuBoard.getRow(cell.getLocation().y); + } else { + group = sudokuBoard.getCol(cell.getLocation().x); } } + + for (SudokuCell c : group) { + if (c.getData() == 0) { + Set blockableCells = sudokuBoard.getRegion(c.getGroupIndex()); + blockableCells.addAll(sudokuBoard.getRow(c.getLocation().y)); + blockableCells.addAll(sudokuBoard.getCol(c.getLocation().x)); + + boolean repeat = false; + for (SudokuCell bc : blockableCells) { + if (bc.getData() == value) { + repeat = true; + break; + } + } + if (!repeat) { + caseCells.add(c); + } + } + } + + for (SudokuCell c : caseCells) { + Board newCase = sudokuBoard.copy(); + PuzzleElement element = newCase.getPuzzleElement(c); + element.setData(value); + newCase.addModifiedData(element); + cases.add(newCase); + } + return cases; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/RepeatedNumberContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/RepeatedNumberContradictionRule.java index f8172d071..955414e8e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/RepeatedNumberContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/RepeatedNumberContradictionRule.java @@ -12,10 +12,10 @@ public class RepeatedNumberContradictionRule extends ContradictionRule { public RepeatedNumberContradictionRule() { super( - "SUDO-CONT-0005", + "SUDO-CONT-0002", "Repeated Numbers", "Two identical numbers are placed in the same group.", - "edu/rpi/legup/images/sudoku/rules/RepeatedNumber.png"); + "edu/rpi/legup/images/sudoku/RepeatedNumber.png"); } /** @@ -29,51 +29,39 @@ public RepeatedNumberContradictionRule() { */ @Override public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { - // Get board to check SudokuBoard sudokuBoard = (SudokuBoard) board; + SudokuCell cell = (SudokuCell) sudokuBoard.getPuzzleElement(puzzleElement); + if (cell.getData() == 0) { + return super.getNoContradictionMessage(); + } - // Loop all group indexes - for (int i = 0; i < 9; i++) { - // Get regions and sets to check duplicates - Set region = sudokuBoard.getRegion(i); - Set regionDup = new HashSet<>(); - - Set row = sudokuBoard.getRow(i); - Set rowDup = new HashSet<>(); + Set region = sudokuBoard.getRegion(cell.getGroupIndex()); + Set row = sudokuBoard.getRow(cell.getLocation().y); + Set col = sudokuBoard.getCol(cell.getLocation().x); - Set col = sudokuBoard.getCol(i); - Set colDup = new HashSet<>(); + Set regionDup = new HashSet<>(); + Set rowDup = new HashSet<>(); + Set colDup = new HashSet<>(); - // Check for non zero duplicates to trigger contradiction - for (SudokuCell c : region) { - if (c.getData() == 0) { - continue; - } - if (regionDup.contains(c.getData())) { - return null; - } - regionDup.add(c.getData()); + for (SudokuCell c : region) { + if (regionDup.contains(c.getData())) { + return null; } + regionDup.add(c.getData()); + } - for (SudokuCell c : row) { - if (c.getData() == 0) { - continue; - } - if (rowDup.contains(c.getData())) { - return null; - } - rowDup.add(c.getData()); + for (SudokuCell c : row) { + if (rowDup.contains(c.getData())) { + return null; } + rowDup.add(c.getData()); + } - for (SudokuCell c : col) { - if (c.getData() == 0) { - continue; - } - if (colDup.contains(c.getData())) { - return null; - } - colDup.add(c.getData()); + for (SudokuCell c : col) { + if (colDup.contains(c.getData())) { + return null; } + colDup.add(c.getData()); } return super.getNoContradictionMessage(); diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/sudoku_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/sudoku_reference_sheet.txt index ceffa168c..a8635330d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/sudoku_reference_sheet.txt +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/sudoku_reference_sheet.txt @@ -1,13 +1,9 @@ +SUDO-BASC-0001 : AdvancedDeductionDirectRule SUDO-BASC-0002 : LastCellForNumberDirectRule SUDO-BASC-0003 : LastNumberForCellDirectRule -SUDO-CONT-0001 : NoCellForNumberRegionContradictionRule -SUDO-CONT-0002 : NoCellForNumberRowContradictionRule -SUDO-CONT-0003 : NoCellForNumberColumnContradictionRule -SUDO-CONT-0004 : NoNumberForCellContradictionRule -SUDO-CONT-0005 : RepeatedNumberContradictionRule +SUDO-CONT-0001 : NoSolutionContradictionRule +SUDO-CONT-0002 : RepeatedNumberContradictionRule -SUDO-CASE-0001 : PossibleNumbersForCellCaseRule -SUDO-CASE-0002 : PossibleCellsForNumberRegionCaseRule -SUDO-CASE-0003 : PossibleCellsForNumberRowCaseRule -SUDO-CASE-0004 : PossibleCellsForNumberColumnCaseRule \ No newline at end of file +SUDO-CASE-0001 : PossibleCellCaseRule +SUDO-CASE-0002 : PossibleNumberCaseRule \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTent.java b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTent.java index 4b0113232..68c97865d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTent.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTent.java @@ -3,8 +3,6 @@ import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.gameboard.Board; import edu.rpi.legup.model.gameboard.PuzzleElement; -import edu.rpi.legup.model.rules.ContradictionRule; - import java.util.List; public class TreeTent extends Puzzle { @@ -60,20 +58,7 @@ public boolean isValidDimensions(int rows, int columns) { */ @Override public boolean isBoardComplete(Board board) { - TreeTentBoard treeTentBoard = (TreeTentBoard) board; - - for (ContradictionRule rule : contradictionRules) { - if (rule.checkContradiction(treeTentBoard) == null) { - return false; - } - } - for (PuzzleElement data : treeTentBoard.getPuzzleElements()) { - TreeTentCell cell = (TreeTentCell) data; - if (cell.getType() == TreeTentType.UNKNOWN) { - return false; - } - } - return true; + return false; } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/GrassTile.java b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/GrassTile.java index 1d33b9035..5356120a8 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/GrassTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/GrassTile.java @@ -6,7 +6,7 @@ public class GrassTile extends PlaceableElement { public GrassTile() { super( - "TREE-ELEM-0001", + "TREE-PlAC-0002", "Grass Tile", "The grass crest tile", "edu/rpi/legup/images/treetent/grass.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/TentTile.java b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/TentTile.java index 96124a98d..950aebfa7 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/TentTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/TentTile.java @@ -6,7 +6,7 @@ public class TentTile extends PlaceableElement { public TentTile() { super( - "TREE-ELEM-0002", + "TREE-PLAC-0001", "Tent Tile", "The tent tile", "edu/rpi/legup/images/treetent/tent.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/TreeTile.java b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/TreeTile.java index 3d94cbfba..d04886ed5 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/TreeTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/TreeTile.java @@ -1,12 +1,12 @@ package edu.rpi.legup.puzzle.treetent.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class TreeTile extends PlaceableElement { +public class TreeTile extends NonPlaceableElement { public TreeTile() { super( - "TREE-ELEM-0003", + "TREE-UNPL-0001", "Tree Tile", "The tree tile", "edu/rpi/legup/images/treetent/tree.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/UnknownTile.java b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/UnknownTile.java index 99b75b60c..a54240efd 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/UnknownTile.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/UnknownTile.java @@ -1,11 +1,11 @@ package edu.rpi.legup.puzzle.treetent.elements; -import edu.rpi.legup.model.elements.PlaceableElement; +import edu.rpi.legup.model.elements.NonPlaceableElement; -public class UnknownTile extends PlaceableElement { +public class UnknownTile extends NonPlaceableElement { public UnknownTile() { super( - "TREE-ELEM-0004", + "TREE-UNPL-0002", "Unknown Tile", "The blank tile", "edu/rpi/legup/images/treetent/UnknownTile.png"); diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/treetent_elements_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/treetent_elements_reference_sheet.txt deleted file mode 100644 index e0cfc1dfa..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/elements/treetent_elements_reference_sheet.txt +++ /dev/null @@ -1,4 +0,0 @@ -TREE-ELEM-0001 : GrassTile -TREE-ELEM-0002 : TentTile -TREE-ELEM-0003 : TreeTile -TREE-ELEM-0004 : UnknownTile \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java index 8fe9b6873..aaa1a8fbc 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/FillinRowCaseRule.java @@ -61,11 +61,7 @@ public CaseBoard getCaseBoard(Board board) { */ @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { - if (puzzleElement == null) { - return new ArrayList(); - } ArrayList cases; - List group; int tentsLeft; TreeTentClue clue = ((TreeTentClue) puzzleElement); diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java index cbe91c3a7..bd303174a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTentCaseRule.java @@ -60,10 +60,6 @@ public CaseBoard getCaseBoard(Board board) { @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { ArrayList cases = new ArrayList(); - if (puzzleElement == null) { - return cases; - } - TreeTentCell cell = (TreeTentCell) puzzleElement; List adj = ((TreeTentBoard) board).getAdjacent(cell, TreeTentType.TREE); List lines = ((TreeTentBoard) board).getLines(); diff --git a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java index 153692ad0..03d039898 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/rules/LinkTreeCaseRule.java @@ -62,10 +62,6 @@ public CaseBoard getCaseBoard(Board board) { @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { ArrayList cases = new ArrayList<>(); - if (puzzleElement == null) { - return cases; - } - TreeTentBoard treeTentBoard = (TreeTentBoard) board; TreeTentCell cell = (TreeTentCell) puzzleElement; List adjCells = treeTentBoard.getAdjacent(cell, TreeTentType.TENT); diff --git a/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java b/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java index b1aa24eca..70fbea033 100644 --- a/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java +++ b/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java @@ -10,10 +10,6 @@ import java.util.Objects; import javax.swing.*; -/** - * Provides the user interface components for creating a new puzzle in the Legup application. - * This package includes classes for displaying dialog boxes to configure and initialize puzzles. - */ public class CreatePuzzleDialog extends JDialog { private HomePanel homePanel; @@ -21,13 +17,6 @@ public class CreatePuzzleDialog extends JDialog { private JComboBox gameBox; private ActionListener gameBoxListener = new ActionListener() { - /** - * An ActionListener that handles changes in the drop-down menu for selecting puzzle types. - * When a new item is selected in the drop-down menu, this listener updates the visibility of - * the text input area and the row/column input fields based on the selected puzzle type. - * If "ShortTruthTable" is selected, the text input area is shown and the row/column fields are hidden. - * For other puzzle types, the row/column fields are shown and the text input area is hidden. - */ @Override public void actionPerformed(ActionEvent e) { JComboBox comboBox = (JComboBox) e.getSource(); @@ -68,7 +57,9 @@ public void actionPerformed(ActionEvent e) { */ @Override public void actionPerformed(ActionEvent ae) { - String game = getGame(); + String game = + Config.convertDisplayNameToClassName( + (String) gameBox.getSelectedItem()); // Check if all 3 TextFields are filled if (game.equals("ShortTruthTable") && textArea.getText().isEmpty()) { @@ -77,8 +68,8 @@ public void actionPerformed(ActionEvent ae) { } if (!game.equals("ShortTruthTable") && (game.isEmpty() - || getRows().isEmpty() - || getColumns().isEmpty())) { + || rows.getText().isEmpty() + || columns.getText().isEmpty())) { System.out.println("Unfilled fields"); return; } @@ -90,8 +81,8 @@ public void actionPerformed(ActionEvent ae) { } else { homePanel.openEditorWithNewPuzzle( game, - Integer.valueOf(getRows()), - Integer.valueOf(getColumns())); + Integer.valueOf(rows.getText()), + Integer.valueOf(columns.getText())); } setVisible(false); } catch (IllegalArgumentException e) { @@ -115,12 +106,6 @@ public void actionPerformed(ActionEvent e) { } }; - /** - * Constructs a new CreatePuzzleDialog - * - * @param parent the parent frame of the dialog - * @param homePanel the home panel where the created puzzle will be added - */ public CreatePuzzleDialog(JFrame parent, HomePanel homePanel) { super(parent, true); @@ -199,10 +184,6 @@ public CreatePuzzleDialog(JFrame parent, HomePanel homePanel) { cancel.addActionListener(cursorPressedCancel); } - /** - * Initializes the puzzle options available for selection in the dialog. - * The options are retrieved from the game board facade and sorted alphabetically. - */ public void initPuzzles() { this.games = GameBoardFacade.getInstance() @@ -213,12 +194,7 @@ public void initPuzzles() { gameBox = new JComboBox(this.games); } - - /** - * Handles the action events for the dialog, including interactions with the Ok and Cancel buttons - * - * @param e The action event to be processed - */ + // ^This method seems useless and never got covered public void actionPerformed(ActionEvent e) { if (e.getSource() == ok) { String game = Config.convertDisplayNameToClassName((String) gameBox.getSelectedItem()); @@ -235,7 +211,9 @@ public void actionPerformed(ActionEvent e) { } this.setVisible(false); } catch (IllegalArgumentException exception) { - // Do nothing. This is here to prevent the dialog from closing if the dimensions are invalid. + // Don't do anything. This is here to prevent the dialog from closing if the + // dimensions are + // invalid. } } else { if (e.getSource() == cancel) { @@ -245,38 +223,4 @@ public void actionPerformed(ActionEvent e) { } } } - - /** - * Retrieves the selected game from the combo box - * - * @return the class name of the selected game - */ - public String getGame() { - return Config.convertDisplayNameToClassName((String) gameBox.getSelectedItem()); - } - - /** - * Retrieves the number of rows specified in the dialog - * - * @return the number of rows as a string - */ - public String getRows() { - return rows.getText(); - } - - /** - * Retrieves the number of columns specified in the dialog - * - * @return the number of columns as a string - */ - public String getColumns() { - return columns.getText(); - } - - /** - * Retrieves the text entered in the text area, split by new lines. - * - * @return an array of strings, each representing as a line of text - */ - public String[] getTextArea() { return textArea.getText().split("\n"); } } diff --git a/src/main/java/edu/rpi/legup/ui/DynamicView.java b/src/main/java/edu/rpi/legup/ui/DynamicView.java index 344885783..8d3024c86 100644 --- a/src/main/java/edu/rpi/legup/ui/DynamicView.java +++ b/src/main/java/edu/rpi/legup/ui/DynamicView.java @@ -16,10 +16,6 @@ import javax.swing.*; import javax.swing.event.ChangeEvent; -/** - * A JPanel that provides a dynamic view with zooming capabilities for different types of content. - * This class supports views such as game boards or proof trees, allowing users to zoom in and out. - */ public class DynamicView extends JPanel { private ScrollView scrollView; @@ -33,12 +29,6 @@ public class DynamicView extends JPanel { private static final Font INFO_FONT = MaterialFonts.REGULAR; private static final Color INFO_COLOR = MaterialColors.GRAY_900; - /** - * Constructs a new DynamicView with the specified ScrollView and view type - * - * @param scrollView the ScrollView that provides the content to be displayed and zoomed - * @param type the type of dynamic view to set up (e.g., BOARD or PROOF_TREE) - */ public DynamicView(ScrollView scrollView, DynamicViewType type) { this.scrollView = scrollView; @@ -195,65 +185,34 @@ public void componentResized(ComponentEvent e) { return zoomWrapper; } - /** - * Gets the ScrollView component associated with this DynamicView - * - * @return the ScrollView component - */ public ScrollView getScrollView() { return this.scrollView; } - /** - * Gets the zoom wrapper that contains the zooming controls - * - * @return the zoom wrapper with zooming controls - */ public JPanel getZoomWrapper() { return this.zoomWrapper; } - /** - * Gets the zoomer that contains the zoomer component - * - * @return the zoomer with the zoomer component - */ public JPanel getZoomer() { return this.zoomer; } - /** - * Updates the status label with an informational message - * - * @param message the informational message to display - */ public void updateInfo(String message) { status.setFont(INFO_FONT); status.setForeground(INFO_COLOR); status.setText(message); } - /** - * Updates the status label with an error message - * - * @param message the error message to display - */ public void updateError(String message) { status.setFont(ERROR_FONT); status.setForeground(ERROR_COLOR); status.setText(message); } - /** - * Clears the status label - */ public void resetStatus() { status.setText(""); } - /** - * Resets the view to its default state and zooms the content to fit the screen - */ public void reset() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); Board board1 = GameBoardFacade.getInstance().getBoard(); @@ -262,9 +221,6 @@ public void reset() { this.getScrollView().zoomFit(); } - /** - * Fits the board view to the screen - */ protected void fitBoardViewToScreen() { scrollView.zoomFit(); } diff --git a/src/main/java/edu/rpi/legup/ui/DynamicViewType.java b/src/main/java/edu/rpi/legup/ui/DynamicViewType.java index d161f5b9c..8c2f285cd 100644 --- a/src/main/java/edu/rpi/legup/ui/DynamicViewType.java +++ b/src/main/java/edu/rpi/legup/ui/DynamicViewType.java @@ -1,13 +1,5 @@ package edu.rpi.legup.ui; -/** - * An enumeration representing the different types of dynamic views supported by the application. - * The two types of views are: - *
    - *
  • {@code BOARD} - Represents a dynamic view of a game board
  • - *
  • {@code PROOF_TREE} - Represents a dynamic view of a proof tree
  • - *
- */ public enum DynamicViewType { BOARD, PROOF_TREE diff --git a/src/main/java/edu/rpi/legup/ui/HomePanel.java b/src/main/java/edu/rpi/legup/ui/HomePanel.java index f12ffdbf0..11f51eb0e 100644 --- a/src/main/java/edu/rpi/legup/ui/HomePanel.java +++ b/src/main/java/edu/rpi/legup/ui/HomePanel.java @@ -25,12 +25,6 @@ import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; -/** - * The {@code HomePanel} class represents the home panel of the LEGUP application. - * This panel provides buttons for functionalities of opening the proof editor, - * opening the puzzle editor, and performing batch grading. It also includes a menu bar with - * options for preferences. - */ public class HomePanel extends LegupPanel { private static final Logger LOGGER = LogManager.getLogger(HomePanel.class.getName()); private LegupUI legupUI; @@ -42,37 +36,44 @@ public class HomePanel extends LegupPanel { private final int buttonSize = 100; - /** - * Initialize the proof solver to an empty panel with no puzzle - */ private ActionListener openProofListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - legupUI.getProofEditor().loadPuzzle("", null); + Object[] items = legupUI.getProofEditor().promptPuzzle(); + if (items == null) { + // The attempt to prompt a puzzle ended gracefully (cancel) + return; + } + String fileName = (String) items[0]; + File puzzleFile = (File) items[1]; + legupUI.getProofEditor().loadPuzzle(fileName, puzzleFile); } }; - /** - * Constructs a {@code HomePanel} with the specified {@code JFrame} and {@code LegupUI}. - * - * @param frame the main application frame - * @param legupUI the LEGUP user interface - */ - public HomePanel(JFrame frame, LegupUI legupUI) { + private ActionListener openPuzzleListener = + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + Object[] items = legupUI.getPuzzleEditor().promptPuzzle(); + if (items == null) { + // The attempt to prompt a puzzle ended gracefully (cancel) + return; + } + String fileName = (String) items[0]; + File puzzleFile = (File) items[1]; + legupUI.getPuzzleEditor().loadPuzzle(fileName, puzzleFile); + } + }; + + public HomePanel(FileDialog fileDialog, JFrame frame, LegupUI legupUI) { this.legupUI = legupUI; this.frame = frame; setLayout(new GridLayout(1, 2)); - setPreferredSize(new Dimension(440, 250)); initText(); initButtons(); } - /** - * Creates and returns the menu bar for this panel - * - * @return the menu bar - */ public JMenuBar getMenuBar() { this.menuBar = new JMenuBar(); JMenu settings = new JMenu("Settings"); @@ -101,37 +102,23 @@ public JMenuBar getMenuBar() { return this.menuBar; } - /** - * Makes the panel visible and sets the menu bar of the frame - */ @Override public void makeVisible() { render(); frame.setJMenuBar(this.getMenuBar()); } - /** - * Resizes the provided icon to the specified width and height - * - * @param icon the icon to resize - * @param width the target width - * @param height the target height - * @return the resized icon - */ private static ImageIcon resizeButtonIcon(ImageIcon icon, int width, int height) { Image image = icon.getImage(); Image resizedImage = image.getScaledInstance(width, height, Image.SCALE_SMOOTH); return new ImageIcon(resizedImage); } - /** - * Initializes the buttons for this panel - */ private void initButtons() { - this.buttons = new JButton[3]; + this.buttons = new JButton[4]; this.buttons[0] = - new JButton("Puzzle Solver") { + new JButton("Solve Puzzle") { { setSize(buttonSize, buttonSize); setMaximumSize(getSize()); @@ -149,7 +136,7 @@ private void initButtons() { this.buttons[0].addActionListener(CursorController.createListener(this, openProofListener)); this.buttons[1] = - new JButton("Puzzle Editor") { + new JButton("Create Puzzle") { { setSize(buttonSize, buttonSize); setMaximumSize(getSize()); @@ -163,17 +150,35 @@ private void initButtons() { this.buttons[1].setIcon(resizeButtonIcon(button1Icon, this.buttonSize, this.buttonSize)); this.buttons[1].setHorizontalTextPosition(AbstractButton.CENTER); this.buttons[1].setVerticalTextPosition(AbstractButton.BOTTOM); - this.buttons[1].addActionListener(l -> this.openPuzzleEditorDialog()); + this.buttons[1].addActionListener(l -> this.openNewPuzzleDialog()); - for (int i = 0; i < this.buttons.length - 1; i++) { // -1 to avoid the batch grader button - this.buttons[i].setBounds(200, 200, 700, 700); - } - this.buttons[2] = new JButton("Batch Grader"); + this.buttons[2] = + new JButton("Edit Puzzle") { + { + setSize(buttonSize, buttonSize); + setMaximumSize(getSize()); + } + }; + URL button2IconLocation = + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/homepanel/puzzle_file.png"); + ImageIcon button2Icon = new ImageIcon(button2IconLocation); this.buttons[2].setFocusPainted(false); + this.buttons[2].setIcon(resizeButtonIcon(button2Icon, this.buttonSize, this.buttonSize)); this.buttons[2].setHorizontalTextPosition(AbstractButton.CENTER); this.buttons[2].setVerticalTextPosition(AbstractButton.BOTTOM); - this.buttons[2].addActionListener( + CursorController.createListener(this, openPuzzleListener)); // PLACEHOLDER + + for (int i = 0; i < this.buttons.length - 1; i++) { // -1 to avoid the batch grader button + this.buttons[i].setBounds(200, 200, 700, 700); + } + this.buttons[3] = new JButton("Batch Grader"); + this.buttons[3].setFocusPainted(false); + this.buttons[3].setHorizontalTextPosition(AbstractButton.CENTER); + this.buttons[3].setVerticalTextPosition(AbstractButton.BOTTOM); + + this.buttons[3].addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -187,10 +192,6 @@ public void actionPerformed(ActionEvent e) { }); } - /** - * Opens a folder chooser dialog and grades puzzles in the selected folder. - * The results are written to a CSV file. - */ public void checkFolder() { GameBoardFacade facade = GameBoardFacade.getInstance(); /* @@ -271,17 +272,13 @@ public void checkFolder() { } } catch (IOException ex) { LOGGER.error(ex.getMessage()); - this.buttons[2].addActionListener((ActionEvent e) -> use_xml_to_check()); + this.buttons[3].addActionListener((ActionEvent e) -> use_xml_to_check()); } } /** - * Processes XML files within a selected directory and generates a CSV report on their "solved?" status. - * The method allows the user to select a directory, and evaluates each XML file for a "solved?" status. - * Results are saved in a "result.csv" file. - * - * @effect Selects a directory, processes each XML file to check for "solved?" status, - * and writes results to "result.csv". Opens the CSV file upon completion. + * @effect batch grade using .xml parser - go through a collection of files and report their + * "solved?" status */ private void use_xml_to_check() { /* Select a folder, go through each .xml file in the subfolders, look for "isSolved" flag */ @@ -480,10 +477,6 @@ public void endDocument() throws SAXException { } } - /** - * Initializes the text labels for the user interface. - * Sets up labels for welcome message, led by Bram, and version information. - */ private void initText() { // TODO: add version text after auto-changing version label is implemented. (text[2] = // version) @@ -505,14 +498,11 @@ private void initText() { this.text[1] = credits; } - /** - * Renders the user interface components - */ private void render() { this.removeAll(); this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS)); - this.legupUI.setTitle("LEGUP: A Better Way To Learn Formal Logic"); + this.legupUI.setTitle("LEGUP: A Better Way to Learn Formal Logic"); JPanel buttons = new JPanel(); buttons.add(Box.createRigidArea(new Dimension(5, 0))); @@ -520,8 +510,11 @@ private void render() { buttons.add(Box.createRigidArea(new Dimension(5, 0))); buttons.add(this.buttons[1]); buttons.add(Box.createRigidArea(new Dimension(5, 0))); + buttons.add(this.buttons[2]); + buttons.add(Box.createRigidArea(new Dimension(5, 0))); + JPanel batchGraderButton = new JPanel(); - batchGraderButton.add(this.buttons[2]); + batchGraderButton.add(this.buttons[3]); batchGraderButton.setAlignmentX(Component.CENTER_ALIGNMENT); this.add(Box.createRigidArea(new Dimension(0, 5))); @@ -533,28 +526,11 @@ private void render() { this.add(Box.createRigidArea(new Dimension(0, 5))); } - /** - * Opens the puzzle editor dialog with no selected puzzle, leaving a blank panel - * - * @throws IllegalArgumentException if the configuration parameters are invalid (should never happen) - */ - private void openPuzzleEditorDialog() { - String game = ""; - int r = 0; - int c = 0; - - try { - this.openEditorWithNewPuzzle(game, r, c); - } catch (IllegalArgumentException e) { - System.out.println("Failed to open editor with new puzzle"); - e.printStackTrace(System.out); - } + private void openNewPuzzleDialog() { + CreatePuzzleDialog cpd = new CreatePuzzleDialog(this.frame, this); + cpd.setVisible(true); } - /** - * Opens a dialog to select a directory, recursively processes the directory to grade puzzles, - * and generates a CSV report of the grading results. - */ private void checkProofAll() { /* * Select dir to grade; recursively grade sub-dirs using traverseDir() @@ -598,14 +574,6 @@ private void checkProofAll() { JOptionPane.showMessageDialog(null, "Batch grading complete."); } - /** - * Recursively traverses directories to grade puzzles and writes results to a CSV file - * - * @param folder the folder to traverse - * @param writer the BufferedWriter to write results to the CSV file - * @param path the current path within the directory structure - * @throws IOException if an I/O error occurs while writing to the CSV file - */ private void traverseDir(File folder, BufferedWriter writer, String path) throws IOException { // Recursively traverse directory GameBoardFacade facade = GameBoardFacade.getInstance(); @@ -658,48 +626,28 @@ private void traverseDir(File folder, BufferedWriter writer, String path) throws } } - /** - * Opens the puzzle editor for the specified puzzle with the specified dimensions - * - * @param game the name of the game - * @param rows the number of rows in the puzzle - * @param columns the number of columns in the puzzle - * @throws IllegalArgumentException if the dimensions are invalid - */ public void openEditorWithNewPuzzle(String game, int rows, int columns) throws IllegalArgumentException { - if (game.equals("")) { - this.legupUI.displayPanel(2); - this.legupUI.getPuzzleEditor().loadPuzzleFromHome(game, rows, columns); - } - else { - // Validate the dimensions - GameBoardFacade facade = GameBoardFacade.getInstance(); - boolean isValidDimensions = facade.validateDimensions(game, rows, columns); - if (!isValidDimensions) { - JOptionPane.showMessageDialog( - null, - "The dimensions you entered are invalid. Please double check \n" - + "the number of rows and columns and try again.", - "ERROR: Invalid Dimensions", - JOptionPane.ERROR_MESSAGE); - throw new IllegalArgumentException("ERROR: Invalid dimensions given"); - } - - if (this.legupUI == null) { - System.err.println("Error: legupUI is null in HomePanel"); - return; - } - - // Set game type on the puzzle editor - this.legupUI.displayPanel(2); - this.legupUI.getPuzzleEditor().loadPuzzleFromHome(game, rows, columns); + // Validate the dimensions + GameBoardFacade facade = GameBoardFacade.getInstance(); + boolean isValidDimensions = facade.validateDimensions(game, rows, columns); + if (!isValidDimensions) { + JOptionPane.showMessageDialog( + null, + "The dimensions you entered are invalid. Please double check \n" + + "the number of rows and columns and try again.", + "ERROR: Invalid Dimensions", + JOptionPane.ERROR_MESSAGE); + throw new IllegalArgumentException("ERROR: Invalid dimensions given"); } + // Set game type on the puzzle editor + this.legupUI.displayPanel(2); + this.legupUI.getPuzzleEditor().loadPuzzleFromHome(game, rows, columns); } /** - * Opens the puzzle editor for the specified puzzle with the given statements + * Opens the puzzle editor for the specified game with the given statements * * @param game a String containing the name of the game * @param statements an array of statements diff --git a/src/main/java/edu/rpi/legup/ui/LegupPanel.java b/src/main/java/edu/rpi/legup/ui/LegupPanel.java index 38c44cbe4..d16167b3d 100644 --- a/src/main/java/edu/rpi/legup/ui/LegupPanel.java +++ b/src/main/java/edu/rpi/legup/ui/LegupPanel.java @@ -2,18 +2,9 @@ import javax.swing.*; -/** - * An abstract base class for panels in the LEGUP application. - * This class extends {@link JPanel} and defines common properties and methods - * for all panels in the LEGUP user interface - * (currently only implements toolbar scale) - */ public abstract class LegupPanel extends JPanel { /** Alerts panel that it will be going visible now */ protected final int TOOLBAR_ICON_SCALE = 40; - /** - * Abstract method to make the panel visible - */ public abstract void makeVisible(); } diff --git a/src/main/java/edu/rpi/legup/ui/LegupUI.java b/src/main/java/edu/rpi/legup/ui/LegupUI.java index 30857f05f..eb8b1663c 100644 --- a/src/main/java/edu/rpi/legup/ui/LegupUI.java +++ b/src/main/java/edu/rpi/legup/ui/LegupUI.java @@ -14,11 +14,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * The main user interface class for the LEGUP application. - * This class extends {@link JFrame} and implements {@link WindowListener} - * to manage the overall window and provide functionality for displaying various panels. - */ public class LegupUI extends JFrame implements WindowListener { private static final Logger LOGGER = LogManager.getLogger(LegupUI.class.getName()); @@ -41,7 +36,7 @@ public static String getOS() { return os; } - /** LegupUI Constructor - creates a new LegupUI to set up the menu and toolbar */ + /** LegupUI Constructor - creates a new LegupUI to setup the menu and toolbar */ public LegupUI() { setTitle("LEGUP"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); @@ -96,27 +91,17 @@ public void keyTyped(KeyEvent e) { setVisible(true); } - /** - * Initializes the panels used in the UI. - * Sets up the layout and adds panels to the window. - */ private void initPanels() { window = new JPanel(); window.setLayout(new BorderLayout()); add(window); panels = new LegupPanel[3]; - panels[0] = new HomePanel(this, this); + panels[0] = new HomePanel(this.fileDialog, this, this); panels[1] = new ProofEditorPanel(this.fileDialog, this, this); panels[2] = new PuzzleEditorPanel(this.fileDialog, this, this); } - /** - * Displays the specified panel - * - * @param option the index of the panel to display - * @throws InvalidParameterException if the option is out of range - */ protected void displayPanel(int option) { if (option > panels.length || option < 0) { throw new InvalidParameterException("Invalid option"); @@ -130,31 +115,29 @@ protected void displayPanel(int option) { repaint(); } - /** - * Gets the ProofEditorPanel instance - * - * @return the ProofEditorPanel - */ public ProofEditorPanel getProofEditor() { return (ProofEditorPanel) panels[1]; } - /** - * Gets the PuzzleEditorPanel instance - * - * @return the PuzzleEditorPanel - */ public PuzzleEditorPanel getPuzzleEditor() { return (PuzzleEditorPanel) panels[2]; } - /** - * Repaints the tree view in the proof editor. - */ public void repaintTree() { getProofEditor().repaintTree(); } + private void directions() { + JOptionPane.showMessageDialog( + null, + "For every move you make, you must provide a rules for it (located in the Rules" + + " panel).\n" + + "While working on the edu.rpi.legup.puzzle, you may click on the \"Check\"" + + " button to test your proof for correctness.", + "Directions", + JOptionPane.PLAIN_MESSAGE); + } + public void showStatus(String status, boolean error) { showStatus(status, error, 1); } @@ -167,13 +150,8 @@ public void showStatus(String status, boolean error, int timer) { // TODO: implement } - /** - * Prompts the user to confirm if they want to exit LEGUP - * - * @param instr the prompt message - * @return true if the user chooses not to quit, false otherwise - */ - public boolean exit(String instr) { + // ask to edu.rpi.legup.save current proof + public boolean noquit(String instr) { int n = JOptionPane.showConfirmDialog(null, instr, "Confirm", JOptionPane.YES_NO_OPTION); return n != JOptionPane.YES_OPTION; } @@ -183,7 +161,7 @@ public void windowOpened(WindowEvent e) {} public void windowClosing(WindowEvent e) { if (GameBoardFacade.getInstance().getHistory().getIndex() > -1) { - if (exit("Exiting LEGUP?")) { + if (noquit("Exiting LEGUP?")) { this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); } else { this.setDefaultCloseOperation(EXIT_ON_CLOSE); @@ -205,47 +183,22 @@ public void windowActivated(WindowEvent e) {} public void windowDeactivated(WindowEvent e) {} - /** - * Gets the BoardView instance from the proof editor - * - * @return the BoardView - */ public BoardView getBoardView() { return getProofEditor().getBoardView(); } - /** - * Gets the BoardView instance from the puzzle editor - * - * @return the BoardView - */ public BoardView getEditorBoardView() { return getPuzzleEditor().getBoardView(); } - /** - * Gets the DynamicView instance from the proof editor - * - * @return the DynamicView - */ public DynamicView getDynamicBoardView() { return getProofEditor().getDynamicBoardView(); } - /** - * Gets the DynamicView instance from the puzzle editor. - * - * @return the DynamicView - */ public DynamicView getEditorDynamicBoardView() { return getPuzzleEditor().getDynamicBoardView(); } - /** - * Gets the TreePanel instance from the proof editor - * - * @return the TreePanel - */ public TreePanel getTreePanel() { return getProofEditor().getTreePanel(); } diff --git a/src/main/java/edu/rpi/legup/ui/PickGameDialog.java b/src/main/java/edu/rpi/legup/ui/PickGameDialog.java index a6501e2a0..f703ffcbc 100644 --- a/src/main/java/edu/rpi/legup/ui/PickGameDialog.java +++ b/src/main/java/edu/rpi/legup/ui/PickGameDialog.java @@ -15,11 +15,6 @@ import javax.swing.JLabel; import javax.swing.JTextField; -/** - * A dialog for selecting a game. - * This class extends {@link JDialog} and implements {@link ActionListener} - * to handle user interactions for selecting a game type and puzzle file. - */ public class PickGameDialog extends JDialog implements ActionListener { JLabel gameLabel = new JLabel("Game:"); String[] games; @@ -111,10 +106,6 @@ public PickGameDialog(JFrame parent, boolean pickBothAtOnce) { c.add(autojustifyCheckBox); } - /** - * Initializes the available games and puzzles. - * Populates the {@link JComboBox} with game types and sets up the puzzle options. - */ public void initPuzzles() { Object[] o = GameBoardFacade.getInstance().getConfig().getPuzzleClassNames().toArray(); @@ -137,31 +128,14 @@ public void initPuzzles() { gameBox = new JComboBox(games); } - /** - * Gets the selected puzzle file path - * - * @return the puzzle file path as a String - */ public String getPuzzle() { return puzzleBox.getText(); } - /** - * Returns the selected puzzle - * - * @return the selected puzzle as a String - */ public String getGame() { return (String) gameBox.getSelectedItem(); } - /** - * Handles action events for the dialog components. - * Responds to user interactions such as selecting a puzzle, choosing a puzzle file, - * and pressing the 'Ok' or 'Cancel' buttons. - * - * @param e the action event - */ public void actionPerformed(ActionEvent e) { if (e.getSource() == gameBox) { int index = gameBox.getSelectedIndex(); diff --git a/src/main/java/edu/rpi/legup/ui/PreferencesDialog.java b/src/main/java/edu/rpi/legup/ui/PreferencesDialog.java index c8639f796..475f4bb68 100644 --- a/src/main/java/edu/rpi/legup/ui/PreferencesDialog.java +++ b/src/main/java/edu/rpi/legup/ui/PreferencesDialog.java @@ -17,12 +17,6 @@ import javax.imageio.ImageIO; import javax.swing.*; -/** - * A dialog for managing user preferences in the LEGUP application. - * This dialog allows users to configure various settings such as screen mode, - * update preferences, work directory path, and specific features related to board and tree views. - * Users can access this dialog from the home screen or proof editor. - */ public class PreferencesDialog extends JDialog { private RuleFrame rulesFrame; @@ -53,24 +47,12 @@ public class PreferencesDialog extends JDialog { } } - /** - * Creates a new instance of PreferencesDialog for the proof editor - * - * @param frame the parent frame - * @param rules the RuleFrame associated with the proof editor - * @return a new instance of PreferencesDialog - */ public static PreferencesDialog CreateDialogForProofEditor(Frame frame, RuleFrame rules) { PreferencesDialog p = new PreferencesDialog(frame); p.rulesFrame = rules; return p; } - /** - * Constructs a PreferencesDialog - * - * @param frame the parent frame - */ public PreferencesDialog(Frame frame) { super(frame); @@ -120,11 +102,6 @@ public PreferencesDialog(Frame frame) { setVisible(true); } - /** - * Toggles between dark mode and light mode based on the given preferences - * - * @param prefs the LegupPreferences instance holding user preferences - */ private void toggleDarkMode(LegupPreferences prefs) { try { if (Boolean.valueOf(prefs.getUserPref(LegupPreferences.DARK_MODE))) { @@ -138,11 +115,6 @@ private void toggleDarkMode(LegupPreferences prefs) { } } - /** - * Creates the general preferences tab - * - * @return a JScrollPane containing the general preferences panel - */ private JScrollPane createGeneralTab() { LegupPreferences prefs = LegupPreferences.getInstance(); JScrollPane scrollPane = new JScrollPane(); @@ -363,15 +335,6 @@ private JScrollPane createPuzzleTab(Puzzle puzzle) { return scrollPane; } - /** - * Creates a JPanel that represents a single row for a rule in the rule list. - * Each row displays the rule's name and an area for showing keyboard shortcuts - * associated with the rule. The keyboard shortcuts are dynamically updated based - * on user input. - * - * @param rule the rule object to be displayed - * @return a JPanel representing the row for the rule - */ private JPanel createRuleRow(Rule rule) { JPanel ruleRow = new JPanel(); ruleRow.setLayout(new BorderLayout()); @@ -424,14 +387,6 @@ public void keyPressed(KeyEvent e) { return ruleRow; } - /** - * Creates a JPanel containing a left-aligned label with the specified text. - * This label is typically used for section headings or descriptive text in the - * preferences dialog. - * - * @param text the text to be displayed on the label - * @return a JPanel containing the left-aligned label - */ private JPanel createLeftLabel(String text) { JPanel labelRow = new JPanel(); labelRow.setLayout(new BorderLayout()); @@ -445,24 +400,12 @@ private JPanel createLeftLabel(String text) { return labelRow; } - /** - * Creates a JSeparator with a maximum height of 5 pixels. - * This separator is used to visually divide sections in the preferences dialog. - * - * @return a JSeparator with a fixed height - */ private JSeparator createLineSeparator() { JSeparator separator = new JSeparator(); separator.setMaximumSize(new Dimension(Integer.MAX_VALUE, 5)); return separator; } - /** - * Applies the current user preferences and updates the associated components. - * This method retrieves user preferences from the dialog's components and stores - * them in the {@link LegupPreferences} instance. It also updates the rule panels - * in the rules frame if it is not null. - */ public void applyPreferences() { LegupPreferences prefs = LegupPreferences.getInstance(); prefs.setUserPref(LegupPreferences.WORK_DIRECTORY, workDirectory.getText()); diff --git a/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java b/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java index 88c1ee427..645a2c0d7 100644 --- a/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java +++ b/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java @@ -17,7 +17,6 @@ import edu.rpi.legup.ui.boardview.BoardView; import edu.rpi.legup.ui.proofeditorui.rulesview.RuleFrame; import edu.rpi.legup.ui.proofeditorui.treeview.TreePanel; -import edu.rpi.legup.ui.proofeditorui.treeview.TreeTransitionView; import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; import edu.rpi.legup.user.Submission; import java.awt.*; @@ -37,14 +36,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * {@code ProofEditorPanel} is a panel that serves as the main user interface - * component for the proof editing functionality of LEGUP. It provides - * the graphical components and interactive elements necessary for editing and managing - * proofs, including toolbars, menus, and views for different aspects of proof editing. - * It also manages interactions with the rest of the application and updates the UI - * based on user actions and application state changes. - */ public class ProofEditorPanel extends LegupPanel implements IHistoryListener { private static final Logger LOGGER = LogManager.getLogger(ProofEditorPanel.class.getName()); private JMenuBar mBar; @@ -55,8 +46,8 @@ public class ProofEditorPanel extends LegupPanel implements IHistoryListener { private DynamicView dynamicBoardView; private JSplitPane topHalfPanel, mainPanel; private TitledBorder boardBorder; - private JButton[] toolBar1Buttons; - private JButton[] toolBar2Buttons; + + private JButton[] toolBarButtons; private JMenu file; private JMenuItem newPuzzle, resetPuzzle, @@ -76,8 +67,7 @@ public class ProofEditorPanel extends LegupPanel implements IHistoryListener { private JMenu about, help; private JMenuItem helpLegup, aboutLegup; - private JToolBar toolBar1; - private JToolBar toolBar2; + private JToolBar toolBar; private BoardView boardView; private JFileChooser folderBrowser; @@ -91,6 +81,7 @@ public class ProofEditorPanel extends LegupPanel implements IHistoryListener { public static final int IMD_FEEDBACK = 32; public static final int INTERN_RO = 64; public static final int AUTO_JUST = 128; + static final int[] TOOLBAR_SEPARATOR_BEFORE = {2, 4, 8}; private static final String[] PROFILES = { "No Assistance", "Rigorous Proof", @@ -120,13 +111,6 @@ public class ProofEditorPanel extends LegupPanel implements IHistoryListener { protected JMenuItem testAI = new JMenuItem("Test AI!"); protected JMenuItem hintAI = new JMenuItem("Hint"); - /** - * Constructs a new {@code ProofEditorPanel} with the specified parameters - * - * @param fileDialog the {@code FileDialog} used for file operations - * @param frame the {@code JFrame} that contains this panel - * @param legupUI the {@code LegupUI} instance managing the user interface - */ public ProofEditorPanel(FileDialog fileDialog, JFrame frame, LegupUI legupUI) { this.fileDialog = fileDialog; this.frame = frame; @@ -135,44 +119,22 @@ public ProofEditorPanel(FileDialog fileDialog, JFrame frame, LegupUI legupUI) { setPreferredSize(new Dimension(800, 700)); } - /** - * Makes the panel visible by setting up the toolbar and content components. - * This method also sets the menu bar of the frame to the one used by this panel. - */ @Override public void makeVisible() { this.removeAll(); - setupToolBar1(); + setupToolBar(); setupContent(); frame.setJMenuBar(getMenuBar()); } - /** - * Constructs and returns the {@code JMenuBar} for this panel. - * It populates it with various {@code JMenu} and {@code JMenuItem} components related - * to file operations, editing, viewing, and proof management. - * The menu bar includes: - *
    - *
  • {@code File} menu with options to open a new puzzle, reset the puzzle, save the proof, - * access preferences, and exit the editor.
  • - *
  • {@code Edit} menu with options for undo, redo, and fitting the board or tree to the screen.
  • - *
  • {@code Proof} menu with options for adding, deleting, merging, collapsing elements, - * and toggling settings related to rule applications and feedback.
  • - *
  • {@code About} menu with options to view information about the application and access help resources.
  • - *
- *

- * Accelerator keys are set based on the operating system (Mac or non-Mac). - * - * @return the {@code JMenuBar} instance containing the menus and menu items for this panel - */ public JMenuBar getMenuBar() { if (mBar != null) return mBar; mBar = new JMenuBar(); file = new JMenu("File"); newPuzzle = new JMenuItem("Open"); - resetPuzzle = new JMenuItem("Reset"); + resetPuzzle = new JMenuItem("Reset Puzzle"); // genPuzzle = new JMenuItem("Puzzle Generators"); // TODO: implement puzzle // generator saveProofAs = new JMenuItem("Save As"); // create a new file to save @@ -348,7 +310,6 @@ public JMenuBar getMenuBar() { } else { resetPuzzle.setAccelerator(KeyStroke.getKeyStroke('R', InputEvent.CTRL_DOWN_MASK)); } - file.addSeparator(); file.add(saveProofAs); @@ -496,10 +457,6 @@ public void actionPerformed(ActionEvent e) { return mBar; } - /** - * Clears the current puzzle, resets the UI to display the initial panel, - * and nullifies the references to the tree panel and board view. - */ public void exitEditor() { // Wipes the puzzle entirely as if LEGUP just started GameBoardFacade.getInstance().clearPuzzle(); @@ -508,13 +465,7 @@ public void exitEditor() { boardView = null; } - /** - * Opens a file chooser dialog allowing the user to select a directory. It uses the user's - * preferred directory or the last saved path if available. The selected directory is used - * to set the new working directory. - * - * @return an array containing the file name and the selected file, or {@code null} if the operation was canceled - */ + // File opener public Object[] promptPuzzle() { GameBoardFacade facade = GameBoardFacade.getInstance(); if (facade.getBoard() != null) { @@ -554,16 +505,9 @@ public Object[] promptPuzzle() { return null; } - System.out.println(preferences.getSavedPath()); return new Object[] {fileName, puzzleFile}; } - /** - * Calls {@link #promptPuzzle()} to get the file information and then loads the puzzle using - * the provided file name and file object. Updates the frame title to reflect the puzzle name. - * If the file is not valid or an error occurs, an error message is shown, and the user is - * prompted to try loading another puzzle. - */ public void loadPuzzle() { Object[] items = promptPuzzle(); // Return if items == null (cancel) @@ -575,19 +519,7 @@ public void loadPuzzle() { loadPuzzle(fileName, puzzleFile); } - /** - * Attempts to load a puzzle from the given file. If successful, it updates the - * UI to display the puzzle and changes the frame title to include the puzzle name. If the - * file is invalid or cannot be read, it shows an appropriate error message and prompts the - * user to try loading another puzzle. - * - * @param fileName the name of the file to load - * @param puzzleFile the file object representing the puzzle file - */ public void loadPuzzle(String fileName, File puzzleFile) { - if (puzzleFile == null && fileName.equals("")) { - legupUI.displayPanel(1); - } if (puzzleFile != null && puzzleFile.exists()) { try { legupUI.displayPanel(1); @@ -622,11 +554,7 @@ public void loadPuzzle(String fileName, File puzzleFile) { } } - /** - * Uses the current puzzle and its associated exporter to save the puzzle data - * to the file currently being used. If the puzzle or exporter is null, or if an error occurs - * during export, the method will catch the exception and print the stack trace. - */ + /** save the proof in the current file */ private void direct_save() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); if (puzzle == null) { @@ -646,11 +574,7 @@ private void direct_save() { } } - /** - * Opens a file chooser dialog for the user to select a directory. The chosen directory is used - * to determine where the puzzle file will be saved. If an exporter is available, it will be used - * to export the puzzle data to the selected path. - */ + /** Create a new file and save proof to it */ private void saveProofAs() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); if (puzzle == null) { @@ -688,11 +612,6 @@ private void saveProofAs() { } // Hyperlink for help button; links to wiki page for tutorials - /** - * Opens the default web browser to a help page related to the type of puzzle currently being used. - * The URL is chosen based on the name of the puzzle. If the puzzle type is not recognized, a general - * tutorial page is opened. - */ private void helpTutorial() { // redirecting to certain help link in wiki Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); @@ -720,14 +639,16 @@ private void helpTutorial() { default: url = "https://github.com/Bram-Hub/Legup/wiki/LEGUP-Tutorial"; } + Runtime rt = Runtime.getRuntime(); try { + // rt.exec("rundll32 url.dll,FileProtocolHandler "+url); java.awt.Desktop.getDesktop().browse(java.net.URI.create(url)); } catch (IOException e) { e.printStackTrace(); } } - // unfinished + // add the new function need to implement public void add_drop() { // add the mouse event then we can use the new listener to implement and // we should create a need jbuttom for it to ship the rule we select. @@ -744,11 +665,7 @@ public void actionPerformed(ActionEvent e) { panel.add(moveing_buttom); } - /** - * Saves the puzzle using the current file name and shows a message dialog to - * confirm that the save operation was successful. If the puzzle or exporter is null, or if - * an error occurs during export, the method will catch the exception and print the stack trace. - */ + // Quick save proof to the current file with a pop window to show "successfully saved" private void saveProofChange() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); if (puzzle == null) { @@ -771,22 +688,13 @@ private void saveProofChange() { } } - /** - * Displays a confirmation dialog with a specified message. Returns {@code true} if the user - * selects "No" or cancels the action, and {@code false} if the user selects "Yes". - * - * @param instr the message to display in the confirmation dialog - * @return {@code true} if the user chooses not to quit, {@code false} otherwise - */ + // ask to edu.rpi.legup.save current proof public boolean noquit(String instr) { int n = JOptionPane.showConfirmDialog(null, instr, "Confirm", JOptionPane.YES_NO_OPTION); return n != JOptionPane.YES_OPTION; } - /** - * Configures the layout and components for the main user interface. This includes setting up - * panels, split panes, and borders, and adding them to the main content pane. - */ + /** Sets the main content for the edu.rpi.legup.user interface */ protected void setupContent() { // JPanel consoleBox = new JPanel(new BorderLayout()); JPanel treeBox = new JPanel(new BorderLayout()); @@ -819,191 +727,110 @@ protected void setupContent() { ruleBox.add(boardPanel); treeBox.add(ruleBox); this.add(treeBox); + // consoleBox.add(treeBox); + // + // getContentPane().add(consoleBox); - mainPanel.setDividerLocation(mainPanel.getMaximumDividerLocation() + 100); + // JPopupPanel popupPanel = new JPopupPanel(); + // setGlassPane(popupPanel); + // popupPanel.setVisible(true); + mainPanel.setDividerLocation(mainPanel.getMaximumDividerLocation() + 100); + // frame.pack(); revalidate(); } - /** - * Initializes the first toolbar, configures its appearance, and adds an 'Open' button - * with an associated icon. An action listener is attached to the button to trigger the loading of - * a puzzle when clicked. - */ - private void setupToolBar1() { - toolBar1 = new JToolBar(); - toolBar1.setFloatable(false); - toolBar1.setRollover(true); - setToolBar2Buttons(new JButton[1]); - - URL open_url = - ClassLoader.getSystemClassLoader() - .getResource("edu/rpi/legup/images/Legup/Open.png"); - - // Scale the image icons down to make the buttons smaller - ImageIcon OpenImageIcon = new ImageIcon(open_url); - Image OpenImage = OpenImageIcon.getImage(); - OpenImageIcon = - new ImageIcon( - OpenImage.getScaledInstance( - this.TOOLBAR_ICON_SCALE, - this.TOOLBAR_ICON_SCALE, - Image.SCALE_SMOOTH)); - - JButton open = new JButton("Open", OpenImageIcon); - open.setFocusPainted(false); - - open.addActionListener((ActionEvent) -> loadPuzzle()); - - getToolBar2Buttons()[0] = open; - toolBar1.add(getToolBar2Buttons()[0]); - - this.add(toolBar1, BorderLayout.NORTH); - } + private void setupToolBar() { + setToolBarButtons(new JButton[ToolbarName.values().length]); + for (int i = 0; i < ToolbarName.values().length; i++) { + String toolBarName = ToolbarName.values()[i].toString(); + URL resourceLocation = + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/" + toolBarName + ".png"); + + // Scale the image icons down to make the buttons smaller + ImageIcon imageIcon = new ImageIcon(resourceLocation); + Image image = imageIcon.getImage(); + imageIcon = + new ImageIcon( + image.getScaledInstance( + this.TOOLBAR_ICON_SCALE, + this.TOOLBAR_ICON_SCALE, + Image.SCALE_SMOOTH)); + + JButton button = new JButton(toolBarName, imageIcon); + button.setFocusPainted(false); + getToolBarButtons()[i] = button; + } - /** - * Initializes the second toolbar, configures its appearance, and adds four buttons each - * with associated icons. Action listeners are attached to each button to trigger their respective - * actions when clicked: - *

    - *
  • 'Directions' button triggers the `directionsToolButton` method.
  • - *
  • 'Undo' button triggers the undo action in the puzzle's history.
  • - *
  • 'Redo' button triggers the redo action in the puzzle's history.
  • - *
  • 'Check' button triggers the `checkProof` method.
  • - *
- */ - private void setupToolBar2() { - toolBar2 = new JToolBar(); - toolBar2.setFloatable(false); - toolBar2.setRollover(true); - setToolBar2Buttons(new JButton[4]); - - URL directions_url = - ClassLoader.getSystemClassLoader() - .getResource("edu/rpi/legup/images/Legup/Directions.png"); - - ImageIcon DirectionsImageIcon = new ImageIcon(directions_url); - Image DirectionsImage = DirectionsImageIcon.getImage(); - DirectionsImageIcon = - new ImageIcon( - DirectionsImage.getScaledInstance( - this.TOOLBAR_ICON_SCALE, - this.TOOLBAR_ICON_SCALE, - Image.SCALE_SMOOTH)); - - JButton directions = new JButton("Directions", DirectionsImageIcon); - directions.setFocusPainted(false); - directions.addActionListener((ActionEvent) -> directionsToolButton()); - - getToolBar2Buttons()[0] = directions; - toolBar2.add(getToolBar2Buttons()[0]); - - URL undo_url = - ClassLoader.getSystemClassLoader() - .getResource("edu/rpi/legup/images/Legup/Undo.png"); - - ImageIcon UndoImageIcon = new ImageIcon(undo_url); - Image UndoImage = UndoImageIcon.getImage(); - UndoImageIcon = - new ImageIcon( - UndoImage.getScaledInstance( - this.TOOLBAR_ICON_SCALE, - this.TOOLBAR_ICON_SCALE, - Image.SCALE_SMOOTH)); - - JButton undo = new JButton("Undo", UndoImageIcon); - undo.setFocusPainted(false); - undo.addActionListener((ActionEvent) -> GameBoardFacade.getInstance().getHistory().undo()); + toolBar = new JToolBar(); + toolBar.setFloatable(false); + toolBar.setRollover(true); - getToolBar2Buttons()[1] = undo; - toolBar2.add(getToolBar2Buttons()[1]); - - URL redo_url = - ClassLoader.getSystemClassLoader() - .getResource("edu/rpi/legup/images/Legup/Redo.png"); - - ImageIcon RedoImageIcon = new ImageIcon(redo_url); - Image RedoImage = RedoImageIcon.getImage(); - RedoImageIcon = - new ImageIcon( - RedoImage.getScaledInstance( - this.TOOLBAR_ICON_SCALE, - this.TOOLBAR_ICON_SCALE, - Image.SCALE_SMOOTH)); - - JButton redo = new JButton("Redo", RedoImageIcon); - redo.setFocusPainted(false); - redo.addActionListener((ActionEvent) -> { - GameBoardFacade.getInstance().getHistory().redo(); - }); - - getToolBar2Buttons()[2] = redo; - toolBar2.add(getToolBar2Buttons()[2]); - - URL check_url = - ClassLoader.getSystemClassLoader() - .getResource("edu/rpi/legup/images/Legup/Check.png"); - - ImageIcon CheckImageIcon = new ImageIcon(check_url); - Image CheckImage = CheckImageIcon.getImage(); - CheckImageIcon = - new ImageIcon( - CheckImage.getScaledInstance( - this.TOOLBAR_ICON_SCALE, - this.TOOLBAR_ICON_SCALE, - Image.SCALE_SMOOTH)); - - JButton check = new JButton("Check", CheckImageIcon); - check.setFocusPainted(false); - check.addActionListener((ActionEvent) -> checkProof()); - - getToolBar2Buttons()[3] = check; - toolBar2.add(getToolBar2Buttons()[3]); - - - this.add(toolBar2, BorderLayout.NORTH); - } + for (int i = 0; i < getToolBarButtons().length; i++) { + for (int s = 0; s < TOOLBAR_SEPARATOR_BEFORE.length; s++) { + if (i == TOOLBAR_SEPARATOR_BEFORE[s]) { + toolBar.addSeparator(); + } + } + String toolBarName = ToolbarName.values()[i].toString(); - /** - * Sets the toolbar1 buttons - * - * @param toolBar1Buttons toolbar buttons - */ - public void setToolBar1Buttons(JButton[] toolBar1Buttons) { - this.toolBar1Buttons = toolBar1Buttons; - } + toolBar.add(getToolBarButtons()[i]); + getToolBarButtons()[i].setToolTipText(toolBarName); - /** - * Sets the toolbar2 buttons - * - * @param toolBar2Buttons toolbar buttons - */ - public void setToolBar2Buttons(JButton[] toolBar2Buttons) { - this.toolBar2Buttons = toolBar2Buttons; + getToolBarButtons()[i].setVerticalTextPosition(SwingConstants.BOTTOM); + getToolBarButtons()[i].setHorizontalTextPosition(SwingConstants.CENTER); + } + + // toolBarButtons[ToolbarName.OPEN_PUZZLE.ordinal()].addActionListener((ActionEvent + // e) -> + // promptPuzzle()); + // toolBarButtons[ToolbarName.SAVE.ordinal()].addActionListener((ActionEvent e) -> + // saveProof()); + // toolBarButtons[ToolbarName.UNDO.ordinal()].addActionListener((ActionEvent e) -> + // GameBoardFacade.getInstance().getHistory().undo()); + // toolBarButtons[ToolbarName.REDO.ordinal()].addActionListener((ActionEvent e) -> + // GameBoardFacade.getInstance().getHistory().redo()); + toolBarButtons[ToolbarName.HINT.ordinal()].addActionListener((ActionEvent e) -> {}); + toolBarButtons[ToolbarName.CHECK.ordinal()].addActionListener( + (ActionEvent e) -> checkProof()); + toolBarButtons[ToolbarName.SUBMIT.ordinal()].addActionListener((ActionEvent e) -> {}); + toolBarButtons[ToolbarName.DIRECTIONS.ordinal()].addActionListener((ActionEvent e) -> {}); + + toolBarButtons[ToolbarName.CHECK_ALL.ordinal()].addActionListener( + (ActionEvent e) -> checkProofAll()); + + // toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(false); + // toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(false); + // toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); + toolBarButtons[ToolbarName.HINT.ordinal()].setEnabled(false); + toolBarButtons[ToolbarName.CHECK.ordinal()].setEnabled(false); + toolBarButtons[ToolbarName.SUBMIT.ordinal()].setEnabled(false); + toolBarButtons[ToolbarName.DIRECTIONS.ordinal()].setEnabled(false); + toolBarButtons[ToolbarName.CHECK_ALL.ordinal()].setEnabled(true); + + this.add(toolBar, BorderLayout.NORTH); } /** - * Gets the toolbar1 buttons + * Sets the toolbar buttons * - * @return toolbar1 buttons + * @param toolBarButtons toolbar buttons */ - public JButton[] getToolBar1Buttons() { - return toolBar1Buttons; + public void setToolBarButtons(JButton[] toolBarButtons) { + this.toolBarButtons = toolBarButtons; } /** - * Gets the toolbar2 buttons + * Gets the toolbar buttons * - * @return toolbar2 buttons + * @return toolbar buttons */ - public JButton[] getToolBar2Buttons() { - return toolBar2Buttons; + public JButton[] getToolBarButtons() { + return toolBarButtons; } - /** - * Uses the {@link GameBoardFacade} to obtain the current puzzle and board. If the puzzle is complete, - * it notifies the user of a correct proof. If not, it alerts the user that the board is not solved. - */ + /** Checks the proof for correctness */ private void checkProof() { GameBoardFacade facade = GameBoardFacade.getInstance(); Tree tree = GameBoardFacade.getInstance().getTree(); @@ -1030,58 +857,14 @@ private void checkProof() { } } - /** - * Retrieves the puzzle name from the `GameBoardFacade` and opens a corresponding rules page in the default web browser. - * - * @throws IOException if an error occurs while trying to open the web page - */ - private void directionsToolButton() { - String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); - //System.out.println(puzzleName); - try { - if (puzzleName.equals("Fillapix")) { - java.awt.Desktop.getDesktop() - .browse(URI.create("https://github.com/Bram-Hub/LEGUP/wiki/Fill-a-pix-rules")); - } - else if (puzzleName.equals("LightUp")) { - java.awt.Desktop.getDesktop() - .browse(URI.create("https://github.com/Bram-Hub/LEGUP/wiki/Light-up-rules")); - } - else if (puzzleName.equals("TreeTent")) { - java.awt.Desktop.getDesktop() - .browse(URI.create("https://github.com/Bram-Hub/LEGUP/wiki/Tree-tent-rules")); - } - else if (puzzleName.equals("ShortTruthTables")) { - java.awt.Desktop.getDesktop() - .browse(URI.create("https://github.com/Bram-Hub/LEGUP/wiki/Short-truth-table-rules")); - } - else { - java.awt.Desktop.getDesktop() - .browse(URI.create("https://github.com/Bram-Hub/LEGUP/wiki/" + puzzleName + "-rules")); - } - } catch (IOException e) { - LOGGER.error("Can't open web page"); - } - } - - /** - * Repaints the board view and tree panel - */ private void repaintAll() { boardView.repaint(); treePanel.repaint(); } - /** - * Initializes the dynamic board view, updates the tree panel, and sets rules and search panels - * based on the provided puzzle. It also updates toolbars and reloads the GUI. - * - * @param puzzle the puzzle to be displayed - */ public void setPuzzleView(Puzzle puzzle) { this.boardView = puzzle.getBoardView(); - dynamicBoardView = new DynamicView(boardView, DynamicViewType.BOARD); this.topHalfPanel.setRightComponent(dynamicBoardView); this.topHalfPanel.setVisible(true); @@ -1102,20 +885,16 @@ public void setPuzzleView(Puzzle puzzle) { ruleFrame.getContradictionPanel().setRules(puzzle.getContradictionRules()); ruleFrame.getSearchPanel().setSearchBar(puzzle); - toolBar1.setVisible(false); - setupToolBar2(); + toolBarButtons[ToolbarName.CHECK.ordinal()].setEnabled(true); + // toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(true); + reloadGui(); } - /** - * Calls {@code repaintTree()} to refresh the tree view. - */ public void reloadGui() { repaintTree(); } - /** - * Updates the tree view displayed in the tree panel to reflect the current state of the tree. - */ + public void repaintTree() { treePanel.repaintTreeView(GameBoardFacade.getInstance().getTree()); } @@ -1170,15 +949,6 @@ private boolean basicCheckProof(int[][] origCells) { return false; } - /** - * Traverses a given directory, grades the proofs found in the directory, and writes the results - * to the specified CSV writer. - * - * @param folder the folder to traverse - * @param writer the CSV writer - * @param path the current path in the directory traversal - * @throws IOException if an error occurs while writing to the CSV file - */ private void traverseDir(File folder, BufferedWriter writer, String path) throws IOException { // Recursively traverse directory GameBoardFacade facade = GameBoardFacade.getInstance(); @@ -1233,36 +1003,20 @@ private void traverseDir(File folder, BufferedWriter writer, String path) throws } } - /** - * Returns the current board view. - * - * @return the current {@link BoardView} - */ public BoardView getBoardView() { return boardView; } - - /** - * Returns the current dynamic board view. - * - * @return the current {@link DynamicView} - */ public DynamicView getDynamicBoardView() { return dynamicBoardView; } - /** - * Returns the current tree panel. - * - * @return the current {@link TreePanel} - */ public TreePanel getTreePanel() { return treePanel; } /** - * Called when an action is pushed onto the edu.rpi.legup.history stack + * Called when a action is pushed onto the edu.rpi.legup.history stack * * @param command action to push onto the stack */ @@ -1270,19 +1024,22 @@ public TreePanel getTreePanel() { public void onPushChange(ICommand command) { LOGGER.info("Pushing " + command.getClass().getSimpleName() + " to stack."); undo.setEnabled(true); + // toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(true); redo.setEnabled(false); + // toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); File puzzleFile = new File(GameBoardFacade.getInstance().getCurFileName()); frame.setTitle(puzzleName + " - " + puzzleFile.getName() + " *"); } - /** - * Updates the state of the undo and redo buttons to reflect that there are no actions - * available to undo or redo. It disables both buttons when the history is cleared. - */ + /** Called when the history is cleared */ @Override public void onClearHistory() { + // undo.setEnabled(false); + // toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(false); + // redo.setEnabled(false); + // toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); } /** @@ -1294,7 +1051,9 @@ public void onClearHistory() { @Override public void onRedo(boolean isBottom, boolean isTop) { undo.setEnabled(!isBottom); + // toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(!isBottom); redo.setEnabled(!isTop); + // toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(!isTop); if (isBottom) { String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); File puzzleFile = new File(GameBoardFacade.getInstance().getCurFileName()); @@ -1315,7 +1074,9 @@ public void onRedo(boolean isBottom, boolean isTop) { @Override public void onUndo(boolean isBottom, boolean isTop) { undo.setEnabled(!isBottom); + // toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(!isBottom); redo.setEnabled(!isTop); + // toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(!isTop); String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); File puzzleFile = new File(GameBoardFacade.getInstance().getCurFileName()); if (isBottom) { @@ -1359,10 +1120,6 @@ public void showStatus(String status, boolean error, int timer) { // TODO: implement } - - /** - * Zooms the tree view to fit within the available screen space - */ protected void fitTreeViewToScreen() { this.treePanel.getTreeView().zoomFit(); } diff --git a/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java b/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java index 2f3c16eac..f50c8d6fc 100644 --- a/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java +++ b/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java @@ -10,14 +10,9 @@ import edu.rpi.legup.history.IHistoryListener; import edu.rpi.legup.model.Puzzle; import edu.rpi.legup.model.PuzzleExporter; -import edu.rpi.legup.model.tree.Tree; -import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.save.ExportFileException; import edu.rpi.legup.save.InvalidFileFormatException; -import edu.rpi.legup.ui.HomePanel; import edu.rpi.legup.ui.boardview.BoardView; -import edu.rpi.legup.ui.proofeditorui.treeview.TreeViewSelection; import edu.rpi.legup.ui.puzzleeditorui.elementsview.ElementFrame; import java.awt.*; import java.awt.event.ActionEvent; @@ -27,32 +22,24 @@ import java.io.IOException; import java.net.URI; import java.net.URL; -import java.util.List; import java.util.Objects; import javax.swing.*; import javax.swing.border.TitledBorder; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * Represents the panel used for puzzle editor in the LEGUP. - * This panel includes a variety of UI components such as toolbars, menus, and split panes. - * It handles puzzle file operations, including creating and editing puzzles. - */ public class PuzzleEditorPanel extends LegupPanel implements IHistoryListener { private static final Logger LOGGER = LogManager.getLogger(PuzzleEditorPanel.class.getName()); private JMenu[] menus; private JMenuItem helpLegup, aboutLegup; private JMenuBar menuBar; - private JToolBar toolBar1; - private JToolBar toolBar2; + private JToolBar toolBar; private JFileChooser folderBrowser; private JFrame frame; private JButton[] buttons; JSplitPane splitPanel; - private JButton[] toolBar1Buttons; - private JButton[] toolBar2Buttons; + private JButton[] toolBarButtons; private JPanel elementPanel; private DynamicView dynamicBoardView; private BoardView boardView; @@ -64,19 +51,8 @@ public class PuzzleEditorPanel extends LegupPanel implements IHistoryListener { private JPanel treePanel; private LegupUI legupUI; private EditorElementController editorElementController; - private CreatePuzzleDialog cpd; - private HomePanel hp; - private boolean existingPuzzle; - private String fileName; - private File puzzleFile; - - /** - * Constructs a {@code PuzzleEditorPanel} with the specified file dialog, frame, and Legup UI instance - * - * @param fileDialog the file dialog used for file operations - * @param frame the main application frame - * @param legupUI the Legup UI instance - */ + static final int[] TOOLBAR_SEPARATOR_BEFORE = {2, 4, 8}; + public PuzzleEditorPanel(FileDialog fileDialog, JFrame frame, LegupUI legupUI) { this.fileDialog = fileDialog; this.frame = frame; @@ -85,10 +61,6 @@ public PuzzleEditorPanel(FileDialog fileDialog, JFrame frame, LegupUI legupUI) { setPreferredSize(new Dimension(800, 700)); } - /** - * Sets up the content of the panel, including the layout and UI components. - * Initializes and configures the {@code DynamicView} and {@code ElementFrame}, and adds them to the panel. - */ protected void setupContent() { JSplitPane splitPanel; JPanel elementBox = new JPanel(new BorderLayout()); @@ -120,11 +92,6 @@ protected void setupContent() { revalidate(); } - /** - * Configures the menu bar with menus and menu items for the application. - * Adds actions for opening, creating, and exiting puzzles. - * Also sets up help and about menu items. - */ public void setMenuBar() { String os = LegupUI.getOS(); menuBar = new JMenuBar(); @@ -136,33 +103,28 @@ public void setMenuBar() { menus[0] = new JMenu("File"); // file>new - JMenuItem openPuzzle = new JMenuItem("Open"); - openPuzzle.addActionListener((ActionEvent) -> loadPuzzle()); + JMenuItem newPuzzle = new JMenuItem("New"); + newPuzzle.addActionListener((ActionEvent) -> loadPuzzle()); if (os.equals("mac")) { - openPuzzle.setAccelerator( + newPuzzle.setAccelerator( KeyStroke.getKeyStroke( - 'O', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + 'N', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); } else { - openPuzzle.setAccelerator(KeyStroke.getKeyStroke('O', InputEvent.CTRL_DOWN_MASK)); + newPuzzle.setAccelerator(KeyStroke.getKeyStroke('N', InputEvent.CTRL_DOWN_MASK)); } - // file>create - JMenuItem createPuzzle = new JMenuItem("Create"); - createPuzzle.addActionListener((ActionEvent) -> { - hp = new HomePanel(this.frame, this.legupUI); - cpd = new CreatePuzzleDialog(this.frame, hp); - cpd.setLocationRelativeTo(null); - cpd.setVisible(true); - existingPuzzle = false; - }); + // file>save + JMenuItem savePuzzle = new JMenuItem("Save As"); + savePuzzle.addActionListener((ActionEvent) -> savePuzzle()); + JMenuItem directSavePuzzle = new JMenuItem("Direct Save Proof "); + directSavePuzzle.addActionListener((ActionEvent) -> direct_save()); if (os.equals("mac")) { - createPuzzle.setAccelerator( + newPuzzle.setAccelerator( KeyStroke.getKeyStroke( - 'C', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + 'D', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); } else { - createPuzzle.setAccelerator(KeyStroke.getKeyStroke('C', InputEvent.CTRL_DOWN_MASK)); + newPuzzle.setAccelerator(KeyStroke.getKeyStroke('D', InputEvent.CTRL_DOWN_MASK)); } - JMenuItem exit = new JMenuItem("Exit"); exit.addActionListener((ActionEvent) -> exitEditor()); if (os.equals("mac")) { @@ -172,9 +134,9 @@ public void setMenuBar() { } else { exit.setAccelerator(KeyStroke.getKeyStroke('Q', InputEvent.CTRL_DOWN_MASK)); } - menus[0].add(openPuzzle); - menus[0].add(createPuzzle); - //menus[0].add(directSavePuzzle); + menus[0].add(newPuzzle); + menus[0].add(savePuzzle); + menus[0].add(directSavePuzzle); menus[0].add(exit); // EDIT @@ -185,8 +147,7 @@ public void setMenuBar() { redo = new JMenuItem("Redo"); fitBoardToScreen = new JMenuItem("Fit Board to Screen"); - // TODO: Undo operation currently does not get updated correctly in history - //menus[1].add(undo); + menus[1].add(undo); undo.addActionListener((ActionEvent) -> GameBoardFacade.getInstance().getHistory().undo()); if (os.equals("mac")) { undo.setAccelerator( @@ -196,8 +157,8 @@ public void setMenuBar() { undo.setAccelerator(KeyStroke.getKeyStroke('Z', InputEvent.CTRL_DOWN_MASK)); } - // TODO: Redo operation currently does not get updated correctly in history - //menus[1].add(redo); + menus[1].add(redo); + // Created action to support two keybinds (CTRL-SHIFT-Z, CTRL-Y) Action redoAction = new AbstractAction() { @@ -271,12 +232,6 @@ public void actionPerformed(ActionEvent e) { frame.setJMenuBar(menuBar); } - /** - * Exits the puzzle editor and resets the application state to its initial condition. - * This method clears the current puzzle from the {@code GameBoardFacade}, - * resets the display to the initial panel, and nullifies references to the - * tree panel and board view. - */ public void exitEditor() { // Wipes the puzzle entirely as if LEGUP just started GameBoardFacade.getInstance().clearPuzzle(); @@ -285,188 +240,118 @@ public void exitEditor() { boardView = null; } - /** - * Makes the panel visible by setting up the toolbar, content, and menu bar. - * This method is called to refresh the panel's user interface. - */ @Override public void makeVisible() { this.removeAll(); - setupToolBar1(); + + setupToolBar(); setupContent(); setMenuBar(); } - /** - * Sets up the first toolbar with buttons for opening and creating puzzles. - * This method initializes the toolbar buttons with their icons and actions. - */ - private void setupToolBar1() { - setToolBar1Buttons(new JButton[2]); - - URL open_url = - ClassLoader.getSystemClassLoader() - .getResource("edu/rpi/legup/images/Legup/Open.png"); - ImageIcon OpenImageIcon = new ImageIcon(open_url); - Image OpenImage = OpenImageIcon.getImage(); - OpenImageIcon = - new ImageIcon( - OpenImage.getScaledInstance( - this.TOOLBAR_ICON_SCALE, - this.TOOLBAR_ICON_SCALE, - Image.SCALE_SMOOTH)); - - JButton open = new JButton("Open", OpenImageIcon); - open.setFocusPainted(false); - open.addActionListener((ActionEvent) -> loadPuzzle()); - - getToolBar1Buttons()[0] = open; - - toolBar1 = new JToolBar(); - toolBar1.setFloatable(false); - toolBar1.setRollover(true); - toolBar1.add(getToolBar1Buttons()[0]); - - URL create_url = - ClassLoader.getSystemClassLoader() - .getResource("edu/rpi/legup/images/Legup/Open Puzzle.png"); - ImageIcon CreateImageIcon = new ImageIcon(create_url); - Image CreateImage = CreateImageIcon.getImage(); - CreateImageIcon = - new ImageIcon( - CreateImage.getScaledInstance( - this.TOOLBAR_ICON_SCALE, - this.TOOLBAR_ICON_SCALE, - Image.SCALE_SMOOTH)); - - JButton create = new JButton("Create", CreateImageIcon); - create.setFocusPainted(false); - create.addActionListener((ActionEvent) -> { - hp = new HomePanel(this.frame, this.legupUI); - cpd = new CreatePuzzleDialog(this.frame, hp); - cpd.setLocationRelativeTo(null); - cpd.setVisible(true); - existingPuzzle = false; - }); - getToolBar1Buttons()[1] = create; - - toolBar1.setFloatable(false); - toolBar1.setRollover(true); - toolBar1.add(getToolBar1Buttons()[1]); - - this.add(toolBar1, BorderLayout.NORTH); - } - - /** - * Sets up the second toolbar with buttons for resetting, saving, and saving & solving puzzles. - * This method initializes the toolbar buttons with their icons and actions. - */ - private void setupToolBar2() { - toolBar2 = new JToolBar(); - toolBar2.setFloatable(false); - toolBar2.setRollover(true); - setToolBar2Buttons(new JButton[3]); - - URL reset = - ClassLoader.getSystemClassLoader() - .getResource("edu/rpi/legup/images/Legup/Reset.png"); - ImageIcon ResetImageIcon = new ImageIcon(reset); - Image ResetImage = ResetImageIcon.getImage(); - ResetImageIcon = - new ImageIcon( - ResetImage.getScaledInstance( - this.TOOLBAR_ICON_SCALE, - this.TOOLBAR_ICON_SCALE, - Image.SCALE_SMOOTH)); - - JButton resetButton = new JButton("Reset", ResetImageIcon); - resetButton.setFocusPainted(false); - - resetButton.addActionListener( - a -> { - if (existingPuzzle) { - legupUI.getPuzzleEditor().loadPuzzle(fileName, puzzleFile); - } - else { - if (cpd.getGame().equals("ShortTruthTable")) { - GameBoardFacade.getInstance().loadPuzzle(cpd.getGame(), cpd.getTextArea()); - } - else { - GameBoardFacade.getInstance().loadPuzzle(cpd.getGame(), Integer.valueOf(cpd.getRows()), Integer.valueOf(cpd.getColumns())); - } - } - }); - - getToolBar2Buttons()[0] = resetButton; - toolBar2.add(getToolBar2Buttons()[0]); - - URL save_as = - ClassLoader.getSystemClassLoader() - .getResource("edu/rpi/legup/images/Legup/Save.png"); - ImageIcon SaveAsImageIcon = new ImageIcon(save_as); - Image SaveAsImage = SaveAsImageIcon.getImage(); - SaveAsImageIcon = - new ImageIcon( - SaveAsImage.getScaledInstance( - this.TOOLBAR_ICON_SCALE, - this.TOOLBAR_ICON_SCALE, - Image.SCALE_SMOOTH)); - - JButton saveas = new JButton("Save As", SaveAsImageIcon); - saveas.setFocusPainted(false); - saveas.addActionListener((ActionEvent) -> savePuzzle()); - - getToolBar2Buttons()[1] = saveas; - toolBar2.add(getToolBar2Buttons()[1]); + private void setupToolBar() { + setToolBarButtons(new JButton[ToolbarName.values().length + 1]); + int lastone = 0; + for (int i = 0; i < ToolbarName.values().length - 1; i++) { + String toolBarName = ToolbarName.values()[i].toString(); + URL resourceLocation = + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/" + toolBarName + ".png"); + + // Scale the image icons down to make the buttons smaller + ImageIcon imageIcon = new ImageIcon(resourceLocation); + Image image = imageIcon.getImage(); + imageIcon = + new ImageIcon( + image.getScaledInstance( + this.TOOLBAR_ICON_SCALE, + this.TOOLBAR_ICON_SCALE, + Image.SCALE_SMOOTH)); + + JButton button = new JButton(toolBarName, imageIcon); + button.setFocusPainted(false); + getToolBarButtons()[i] = button; + lastone = i; + } - URL save_and_solve = + URL check_and_save = ClassLoader.getSystemClassLoader() .getResource("edu/rpi/legup/images/Legup/Check.png"); - ImageIcon SaveSolveImageIcon = new ImageIcon(save_and_solve); - Image SaveSolveImage = SaveSolveImageIcon.getImage(); - SaveSolveImageIcon = + ImageIcon imageIcon = new ImageIcon(check_and_save); + Image image = imageIcon.getImage(); + imageIcon = new ImageIcon( - SaveSolveImage.getScaledInstance( + image.getScaledInstance( this.TOOLBAR_ICON_SCALE, this.TOOLBAR_ICON_SCALE, Image.SCALE_SMOOTH)); - JButton saveandsolve = new JButton("Save & Solve", SaveSolveImageIcon); - saveandsolve.setFocusPainted(false); - saveandsolve.addActionListener( + JButton checkandsave = new JButton("check and Save", imageIcon); + checkandsave.setFocusPainted(false); + checkandsave.addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - if (GameBoardFacade.getInstance().getPuzzleModule() != null) { - String filename = savePuzzle(); - File puzzlename = new File(filename); - System.out.println(filename); - - GameBoardFacade.getInstance().getLegupUI().displayPanel(1); - GameBoardFacade.getInstance() - .getLegupUI() - .getProofEditor() - .loadPuzzle(filename, new File(filename)); - String puzzleName = - GameBoardFacade.getInstance().getPuzzleModule().getName(); - frame.setTitle(puzzleName + " - " + puzzlename.getName()); - } + // savePuzzle(); + String filename = savePuzzle(); + File puzzlename = new File(filename); + System.out.println(filename); + + GameBoardFacade.getInstance().getLegupUI().displayPanel(1); + GameBoardFacade.getInstance() + .getLegupUI() + .getProofEditor() + .loadPuzzle(filename, new File(filename)); + String puzzleName = + GameBoardFacade.getInstance().getPuzzleModule().getName(); + frame.setTitle(puzzleName + " - " + puzzlename.getName()); } }); - getToolBar2Buttons()[2] = saveandsolve; - toolBar2.add(getToolBar2Buttons()[2]); + getToolBarButtons()[lastone + 1] = checkandsave; + System.out.println("it is create new file"); - this.add(toolBar2, BorderLayout.NORTH); + toolBar = new JToolBar(); + toolBar.setFloatable(false); + toolBar.setRollover(true); + + for (int i = 0; i < getToolBarButtons().length - 1; i++) { + for (int s = 0; s < TOOLBAR_SEPARATOR_BEFORE.length; s++) { + if (i == TOOLBAR_SEPARATOR_BEFORE[s]) { + toolBar.addSeparator(); + } + } + String toolBarName = ToolbarName.values()[i].toString(); + + toolBar.add(getToolBarButtons()[i]); + getToolBarButtons()[i].setToolTipText(toolBarName); + + getToolBarButtons()[i].setVerticalTextPosition(SwingConstants.BOTTOM); + getToolBarButtons()[i].setHorizontalTextPosition(SwingConstants.CENTER); + } + + // toolBarButtons[ToolbarName.OPEN_PUZZLE.ordinal()].addActionListener((ActionEvent + // e) -> + // promptPuzzle()); + // toolBarButtons[ToolbarName.SAVE.ordinal()].addActionListener((ActionEvent e) -> + // saveProof()); + // toolBarButtons[ToolbarName.UNDO.ordinal()].addActionListener((ActionEvent e) -> + // GameBoardFacade.getInstance().getHistory().undo()); + // toolBarButtons[ToolbarName.REDO.ordinal()].addActionListener((ActionEvent e) -> + // GameBoardFacade.getInstance().getHistory().redo()); + toolBarButtons[ToolbarName.HINT.ordinal()].addActionListener((ActionEvent e) -> {}); + toolBarButtons[ToolbarName.SUBMIT.ordinal()].addActionListener((ActionEvent e) -> {}); + toolBarButtons[ToolbarName.DIRECTIONS.ordinal()].addActionListener((ActionEvent e) -> {}); + + // toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(false); + // toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(false); + // toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); + toolBarButtons[ToolbarName.HINT.ordinal()].setEnabled(false); + toolBarButtons[ToolbarName.SUBMIT.ordinal()].setEnabled(false); + toolBarButtons[ToolbarName.DIRECTIONS.ordinal()].setEnabled(false); + + this.add(toolBar, BorderLayout.NORTH); } - /** - * Initializes a puzzle based on the provided game name, rows, and columns. - * - * @param game the name of the game or puzzle to load - * @param rows the number of rows in the puzzle - * @param columns the number of columns in the puzzle - * @throws IllegalArgumentException if the provided arguments are invalid - */ public void loadPuzzleFromHome(String game, int rows, int columns) throws IllegalArgumentException { GameBoardFacade facade = GameBoardFacade.getInstance(); @@ -480,13 +365,6 @@ public void loadPuzzleFromHome(String game, int rows, int columns) } } - /** - * Initializes a puzzle based on the provided game name and an array of statements. - * - * @param game the name of the game or puzzle to load - * @param statements an array of statements to initialize the puzzle - * @throws IllegalArgumentException if the provided arguments are invalid - */ public void loadPuzzleFromHome(String game, String[] statements) { GameBoardFacade facade = GameBoardFacade.getInstance(); try { @@ -499,17 +377,11 @@ public void loadPuzzleFromHome(String game, String[] statements) { } } - /** - * Prompts the user to select a puzzle file to open. - * Opens a file chooser dialog and returns the selected file's name and file object. - * If a puzzle is currently loaded, prompts the user to confirm if they want to open a new puzzle. - * - * @return an array containing the selected file name and file object, or null if the operation was canceled - */ + // File opener public Object[] promptPuzzle() { GameBoardFacade facade = GameBoardFacade.getInstance(); if (facade.getBoard() != null) { - if (noQuit("Open an existing puzzle?")) { + if (noQuit("Opening a new puzzle?")) { return new Object[0]; } } @@ -533,6 +405,7 @@ public Object[] promptPuzzle() { fileBrowser.setAcceptAllFileFilterUsed(false); File puzzlePath = fileBrowser.getSelectedFile(); + System.out.println(puzzlePath.getAbsolutePath()); if (puzzlePath != null) { fileName = puzzlePath.getAbsolutePath(); @@ -547,11 +420,6 @@ public Object[] promptPuzzle() { return new Object[] {fileName, puzzleFile}; } - /** - * Loads a puzzle by prompting the user to select a puzzle file. - * If the user cancels the operation, no action is taken. If a puzzle file is selected, - * it will be loaded using the file name and file object. - */ public void loadPuzzle() { Object[] items = promptPuzzle(); // Return if items == null (cancel) @@ -563,14 +431,6 @@ public void loadPuzzle() { loadPuzzle(fileName, puzzleFile); } - /** - * Loads a puzzle from the specified file. - * If the puzzle file is valid and exists, it loads the puzzle and updates the UI. - * If the file format is invalid, an error message is displayed. - * - * @param fileName the name of the puzzle file - * @param puzzleFile the file object representing the puzzle file - */ public void loadPuzzle(String fileName, File puzzleFile) { if (puzzleFile != null && puzzleFile.exists()) { try { @@ -578,9 +438,6 @@ public void loadPuzzle(String fileName, File puzzleFile) { GameBoardFacade.getInstance().loadPuzzleEditor(fileName); String puzzleName = GameBoardFacade.getInstance().getPuzzleModule().getName(); frame.setTitle(puzzleName + " - " + puzzleFile.getName()); - existingPuzzle = true; - this.fileName = fileName; - this.puzzleFile = puzzleFile; } catch (InvalidFileFormatException e) { legupUI.displayPanel(0); LOGGER.error(e.getMessage()); @@ -594,103 +451,42 @@ public void loadPuzzle(String fileName, File puzzleFile) { } } - /** - * Displays a confirmation dialog with the given instruction message. - * The method returns true if the user selected "No" or cancelled the dialog, - * and false if the user selected "Yes". - * - * @param instr the instruction message to display in the confirmation dialog - * @return true if the user selected "No" or canceled; false if the user selected "Yes" - */ public boolean noQuit(String instr) { int n = JOptionPane.showConfirmDialog(null, instr, "Confirm", JOptionPane.YES_NO_OPTION); return n != JOptionPane.YES_OPTION; } - /** - * {@inheritDoc} - */ @Override public void onPushChange(ICommand command) {} - /** - * {@inheritDoc} - */ @Override public void onUndo(boolean isBottom, boolean isTop) {} - /** - * {@inheritDoc} - */ @Override public void onRedo(boolean isBottom, boolean isTop) {} - /** - * {@inheritDoc} - */ @Override public void onClearHistory() { + // undo.setEnabled(false); + // redo.setEnabled(false); } - - /** - * Returns the current board view - * - * @return the board view - */ public BoardView getBoardView() { return boardView; } - /** - * Returns the array of buttons for the first toolbar - * - * @return the array of toolbar1 buttons - */ - public JButton[] getToolBar1Buttons() { - return toolBar1Buttons; - } - - /** - * Sets the array of buttons for the first toolbar - * - * @param toolBar1Buttons the array of toolbar1 buttons - */ - public void setToolBar1Buttons(JButton[] toolBar1Buttons) { - this.toolBar1Buttons = toolBar1Buttons; + public JButton[] getToolBarButtons() { + return toolBarButtons; } - /** - * Returns the array of buttons for the second toolbar - * - * @return the array of toolbar2 buttons - */ - public JButton[] getToolBar2Buttons() { - return toolBar2Buttons; + public void setToolBarButtons(JButton[] toolBarButtons) { + this.toolBarButtons = toolBarButtons; } - /** - * Sets the array of buttons for the second toolbar - * - * @param toolBar2Buttons the array of toolbar2 buttons - */ - public void setToolBar2Buttons(JButton[] toolBar2Buttons) { - this.toolBar2Buttons = toolBar2Buttons; - } - - /** - * Repaints the current board view - */ private void repaintAll() { boardView.repaint(); } - /** - * Sets the puzzle view based on the provided puzzle object. - * Updates the UI components to display the new puzzle. - * - * @param puzzle the puzzle object to display - */ public void setPuzzleView(Puzzle puzzle) { this.boardView = puzzle.getBoardView(); editorElementController.setElementController(boardView.getElementController()); @@ -706,11 +502,13 @@ public void setPuzzleView(Puzzle puzzle) { dynamicBoardView.setBorder(titleBoard); puzzle.addBoardListener(puzzle.getBoardView()); + System.out.println("Setting elements"); if (this.elementFrame != null) { elementFrame.setElements(puzzle); } - toolBar1.setVisible(false); - setupToolBar2(); + + toolBarButtons[ToolbarName.CHECK.ordinal()].setEnabled(true); + // toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(true); } /** Saves a puzzle */ @@ -733,13 +531,6 @@ private void direct_save() { } } - /** - * Saves the current puzzle to a user-selected directory. - * Prompts the user to select a directory and saves the puzzle to that directory. - * Returns the path where the puzzle was saved. - * - * @return the path where the puzzle was saved, or an empty string if the save operation was canceled - */ private String savePuzzle() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); if (puzzle == null) { @@ -776,7 +567,7 @@ private String savePuzzle() { folderBrowser.setAcceptAllFileFilterUsed(false); String path = folderBrowser.getSelectedFile().getAbsolutePath(); - preferences.setSavedPath(path); + if (path != null) { try { PuzzleExporter exporter = puzzle.getExporter(); @@ -791,11 +582,6 @@ private String savePuzzle() { return path; } - /** - * Returns the current dynamic board view - * - * @return the dynamic board view - */ public DynamicView getDynamicBoardView() { return dynamicBoardView; } diff --git a/src/main/java/edu/rpi/legup/ui/ScrollView.java b/src/main/java/edu/rpi/legup/ui/ScrollView.java index 18aff4d1c..0bf8335a2 100644 --- a/src/main/java/edu/rpi/legup/ui/ScrollView.java +++ b/src/main/java/edu/rpi/legup/ui/ScrollView.java @@ -6,10 +6,6 @@ import java.util.logging.Logger; import javax.swing.*; -/** - * ScrollView extends {@link JScrollPane} to provide a customizable view with zoom and scroll capabilities. - * It uses a {@link ZoomablePane} as the canvas and allows for zooming and scrolling with respect to the canvas content. - */ public class ScrollView extends JScrollPane { private static final Logger LOGGER = Logger.getLogger(ScrollView.class.getName()); @@ -169,11 +165,6 @@ public void zoom(int n, Point point) { revalidate(); } - /** - * Adjusts the zoom level to the given scale and centers the viewport on the current center point - * - * @param newScale the new scale to set - */ public void zoomTo(double newScale) { // check zoom bounds if (newScale < minScale) { @@ -291,11 +282,6 @@ public void setSize(Dimension size) { updateSize(); } - /** - * Gets the canvas for this {@code ScrollView} - * - * @return the ZoomablePane instance used as the canvas - */ public ZoomablePane getCanvas() { return canvas; } diff --git a/src/main/java/edu/rpi/legup/ui/ToolbarName.java b/src/main/java/edu/rpi/legup/ui/ToolbarName.java index 53936a141..ba02ebd2e 100644 --- a/src/main/java/edu/rpi/legup/ui/ToolbarName.java +++ b/src/main/java/edu/rpi/legup/ui/ToolbarName.java @@ -1,12 +1,11 @@ package edu.rpi.legup.ui; -/** - * This enum defines constants for toolbar names used in the user interface. - * Each represents a specific toolbar action. - */ public enum ToolbarName { + HINT, + CHECK, + SUBMIT, DIRECTIONS, - CHECK; + CHECK_ALL; /** * Gets the String representation of the ToolbarName enum diff --git a/src/main/java/edu/rpi/legup/ui/ZoomWidget.java b/src/main/java/edu/rpi/legup/ui/ZoomWidget.java index 34e828250..aa5b65c4e 100644 --- a/src/main/java/edu/rpi/legup/ui/ZoomWidget.java +++ b/src/main/java/edu/rpi/legup/ui/ZoomWidget.java @@ -10,10 +10,6 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -/** - * The {@code ZoomWidget} displays a zoom icon that, when clicked, shows a popup slider to adjust - * the zoom level of the associated {@code ScrollView}. - */ public class ZoomWidget extends JLabel { private ScrollView parent; private PopupSlider palette = new PopupSlider(); @@ -36,17 +32,12 @@ public ZoomWidget(ScrollView parent) { addMouseListener(open); } - /** - * A {@code JPopupMenu} subclass that contains a vertical slider for adjusting zoom level. - */ + /** */ private class PopupSlider extends JPopupMenu implements ChangeListener { private static final long serialVersionUID = 8225019381200459814L; private JSlider slider; - /** - * Constructs a {@code PopupSlider} with a vertical slider - */ public PopupSlider() { slider = new JSlider(SwingConstants.VERTICAL, 0, 400, 200); slider.setMajorTickSpacing(25); @@ -56,11 +47,6 @@ public PopupSlider() { slider.addChangeListener(this); } - /** - * Handles state changes in the slider by adjusting the zoom level of the {@code ScrollView} - * - * @param e the {@code ChangeEvent} indicating that the slider's state has changed - */ public void stateChanged(ChangeEvent e) { if (slider.getValueIsAdjusting()) { parent.zoomTo((double) slider.getValue() / 100.0); diff --git a/src/main/java/edu/rpi/legup/ui/ZoomablePane.java b/src/main/java/edu/rpi/legup/ui/ZoomablePane.java index 66af90abd..934d31c53 100644 --- a/src/main/java/edu/rpi/legup/ui/ZoomablePane.java +++ b/src/main/java/edu/rpi/legup/ui/ZoomablePane.java @@ -5,10 +5,6 @@ import java.awt.Graphics2D; import javax.swing.*; -/** - * The {@code ZoomablePane} class is used to display components in a zoomable and scalable manner. - * It uses {@code ScrollView} to handle scaling and drawing of the content. - */ public class ZoomablePane extends JLayeredPane { private ScrollView viewer; diff --git a/src/main/java/edu/rpi/legup/ui/boardview/BoardView.java b/src/main/java/edu/rpi/legup/ui/boardview/BoardView.java index fa3ec70b7..ca03f1e25 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/BoardView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/BoardView.java @@ -11,10 +11,6 @@ import java.awt.*; import java.util.ArrayList; -/** - * An abstract class representing a view for a board in the puzzle game. - * It handles the visual representation and user interactions with the board elements. - */ public abstract class BoardView extends ScrollView implements IBoardListener { protected TreeElement treeElement; protected Board board; @@ -129,9 +125,6 @@ public void setBoard(Board board) { } } - /** - * Configures the view to handle case interactions - */ protected void setCasePickable() { CaseBoard caseBoard = (CaseBoard) board; Board baseBoard = caseBoard.getBaseBoard(); @@ -190,26 +183,15 @@ public ArrayList getElementViews() { return elementViews; } - /** - * Gets the ElementController associated with this board view. - * - * @return the ElementController - */ public ElementController getElementController() { return elementController; } - @Override public void draw(Graphics2D graphics2D) { drawBoard(graphics2D); } - /** - * Draws the board and its elements. - * - * @param graphics2D the Graphics2D context used for drawing - */ public void drawBoard(Graphics2D graphics2D) { for (ElementView element : elementViews) { element.draw(graphics2D); @@ -226,10 +208,5 @@ public void onBoardDataChanged(PuzzleElement puzzleElement) { repaint(); } - /** - * Gets the selection popup menu for this board view. - * - * @return the DataSelectionView associated with this view - */ public abstract DataSelectionView getSelectionPopupMenu(); } diff --git a/src/main/java/edu/rpi/legup/ui/boardview/DataSelectionView.java b/src/main/java/edu/rpi/legup/ui/boardview/DataSelectionView.java index a3d82b461..cedfa08fe 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/DataSelectionView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/DataSelectionView.java @@ -5,17 +5,8 @@ import javax.swing.*; import javax.swing.border.BevelBorder; -/** - * DataSelectionView is a popup menu used for selecting data elements. - * It extends JPopupMenu and is styled with a gray background and a raised bevel border. - */ public class DataSelectionView extends JPopupMenu { - /** - * Constructs a DataSelectionView with the given controller. - * - * @param controller The ElementController to handle UI events. - */ public DataSelectionView(ElementController controller) { setBackground(Color.GRAY); setBorder(new BevelBorder(BevelBorder.RAISED)); diff --git a/src/main/java/edu/rpi/legup/ui/boardview/ElementSelection.java b/src/main/java/edu/rpi/legup/ui/boardview/ElementSelection.java index 9ad4132d6..8e6f2cb18 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/ElementSelection.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/ElementSelection.java @@ -3,48 +3,25 @@ import java.awt.*; import java.util.ArrayList; -/** - * ElementSelection manages the selection and hover states of ElementViews. - * It maintains a list of selected elements, the currently hovered element, and the mouse point location. - */ public class ElementSelection { private ArrayList selection; private ElementView hover; private Point mousePoint; - /** - * Constructs an ElementSelection instance with an empty selection and no hover or mouse point - */ public ElementSelection() { this.selection = new ArrayList<>(); this.hover = null; this.mousePoint = null; } - /** - * Gets the list of currently selected ElementViews. - * - * @return the list of selected ElementViews - */ public ArrayList getSelection() { return selection; } - /** - * Gets the first ElementView in the selection, or null if the selection is empty. - * - * @return the first selected ElementView, or null if there are no selections - */ public ElementView getFirstSelection() { return selection.size() == 0 ? null : selection.get(0); } - /** - * Toggles the selection state of an ElementView. - * If the ElementView is currently selected, it is deselected. Otherwise, it is selected. - * - * @param elementView the ElementView to toggle - */ public void toggleSelection(ElementView elementView) { if (selection.contains(elementView)) { selection.remove(elementView); @@ -55,20 +32,12 @@ public void toggleSelection(ElementView elementView) { } } - /** - * Sets a new selection, clearing the previous selection and selecting the specified ElementView. - * - * @param elementView the ElementView to select - */ public void newSelection(ElementView elementView) { clearSelection(); selection.add(elementView); elementView.setSelected(true); } - /** - * Clears the selection and deselects all ElementViews - */ public void clearSelection() { for (ElementView elementView : selection) { elementView.setSelected(false); @@ -76,20 +45,10 @@ public void clearSelection() { selection.clear(); } - /** - * Gets the currently hovered ElementView. - * - * @return the currently hovered ElementView, or null if no element is hovered - */ public ElementView getHover() { return hover; } - /** - * Sets a new hovered ElementView, updating the hover state of the previous and new elements. - * - * @param newHovered the new ElementView to be hovered - */ public void newHover(ElementView newHovered) { newHovered.setHover(true); if (hover != null) { @@ -98,9 +57,6 @@ public void newHover(ElementView newHovered) { hover = newHovered; } - /** - * Clears the current hover state if there exists one - */ public void clearHover() { if (hover != null) { hover.setHover(false); @@ -108,20 +64,10 @@ public void clearHover() { } } - /** - * Gets the current mouse point location. - * - * @return the current mouse point location - */ public Point getMousePoint() { return mousePoint; } - /** - * Sets the mouse point location. - * - * @param point the new mouse point location - */ public void setMousePoint(Point point) { this.mousePoint = point; } diff --git a/src/main/java/edu/rpi/legup/ui/boardview/ElementView.java b/src/main/java/edu/rpi/legup/ui/boardview/ElementView.java index ad6cd3d3f..83b2cb099 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/ElementView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/ElementView.java @@ -9,10 +9,6 @@ import java.awt.image.BufferedImage; import javax.swing.*; -/** - * ElementView represents a visual representation of a PuzzleElement. - * It handles drawing, selection, hover states, and interaction with the PuzzleElement. - */ public abstract class ElementView implements Shape { protected int index; protected Point location; @@ -77,11 +73,6 @@ public void draw(Graphics2D graphics2D) { } } - /** - * Draws the basic element representation (e.g., border, text) on the provided Graphics2D context. - * - * @param graphics2D the Graphics2D context to use for drawing - */ public void drawElement(Graphics2D graphics2D) { graphics2D.setStroke(new BasicStroke(1)); graphics2D.draw( @@ -96,19 +87,8 @@ public void drawElement(Graphics2D graphics2D) { graphics2D.drawString(String.valueOf(puzzleElement.getData()), xText, yText); } - /** - * Draws additional elements for given PuzzleElements (default implementation does nothing). - * Overriden in some puzzle element views. - * - * @param graphics2D the Graphics2D context to use for drawing - */ public void drawGiven(Graphics2D graphics2D) {} - /** - * Draws a hover effect on the ElementView. - * - * @param graphics2D the Graphics2D context to use for drawing - */ public void drawHover(Graphics2D graphics2D) { graphics2D.setColor(hoverColor); graphics2D.setStroke(new BasicStroke(2)); @@ -117,11 +97,6 @@ public void drawHover(Graphics2D graphics2D) { location.x + 1.5f, location.y + 1.5f, size.width - 3, size.height - 3)); } - /** - * Draws a modified effect on the ElementView. - * - * @param graphics2D the Graphics2D context to use for drawing - */ public void drawModified(Graphics2D graphics2D) { graphics2D.setColor(puzzleElement.isValid() ? modifiedColor : invalidColor); graphics2D.setStroke(new BasicStroke(2)); @@ -130,11 +105,6 @@ public void drawModified(Graphics2D graphics2D) { location.x + 1.5f, location.y + 1.5f, size.width - 3, size.height - 3)); } - /** - * Draws a case rule picker on the ElementView. - * - * @param graphics2D the Graphics2D context to use for drawing - */ public void drawCase(Graphics2D graphics2D) { graphics2D.setColor(caseColor); graphics2D.fill( @@ -142,11 +112,6 @@ public void drawCase(Graphics2D graphics2D) { location.x + 1.5f, location.y + 1.5f, size.width - 3, size.height - 3)); } - /** - * Creates an image representation of the ElementView. - * - * @return a BufferedImage of the ElementView - */ public BufferedImage getImage() { BufferedImage image = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB); @@ -228,20 +193,10 @@ public void setPuzzleElement(PuzzleElement data) { this.puzzleElement = data; } - /** - * Checks if the case picker should be shown for this ElementView - * - * @return true if the case picker should be shown, false otherwise - */ public boolean isShowCasePicker() { return showCasePicker; } - /** - * Sets whether the case picker should be shown for this ElementView - * - * @param showCasePicker true if the case picker should be shown, false otherwise - */ public void setShowCasePicker(boolean showCasePicker) { this.showCasePicker = showCasePicker; } @@ -326,13 +281,6 @@ public JMenuItem getSelectionMenuItem() { return item; } - /** - * Determines if the specified point (x, y) is within the bounds of this ElementView - * - * @param x the x-coordinate of the point to check - * @param y the y-coordinate of the point to check - * @return {@code true} if the point is within the bounds of this ElementView; {@code false} otherwise - */ @Override public boolean contains(double x, double y) { return x >= location.x @@ -341,38 +289,17 @@ public boolean contains(double x, double y) { && y <= location.y + size.height; } - /** - * Determines if the specified Point2D object is within the bounds of this ElementView - * - * @param point the Point2D object representing the point to check - * @return {@code true} if the point is within the bounds of this ElementView; {@code false} otherwise - */ @Override public boolean contains(Point2D point) { return contains(point.getX(), point.getY()); } - /** - * Determines if the specified rectangle defined by (x, y, width, height) intersects with the bounds of this ElementView. - * - * @param x The x-coordinate of the rectangle to check - * @param y The y-coordinate of the rectangle to check - * @param width The width of the rectangle to check - * @param height The height of the rectangle to check - * @return {@code true} if the rectangle intersects with the bounds of this ElementView; {@code false} otherwise - */ @Override public boolean intersects(double x, double y, double width, double height) { return (x + width >= location.x && x <= location.x + size.width) || (y + height >= location.y && y <= location.y + size.height); } - /** - * Determines if the specified Rectangle2D object intersects with the bounds of this ElementView. - * - * @param rectangle2D the Rectangle2D object representing the rectangle to check - * @return {@code true} if the rectangle intersects with the bounds of this ElementView; {@code false} otherwise - */ @Override public boolean intersects(Rectangle2D rectangle2D) { return intersects( @@ -382,27 +309,12 @@ public boolean intersects(Rectangle2D rectangle2D) { rectangle2D.getHeight()); } - /** - * Determines if the specified rectangle defined by (x, y, width, height) is entirely contained within the bounds of this ElementView - * - * @param x the x-coordinate of the rectangle to check - * @param y the y-coordinate of the rectangle to check - * @param width the width of the rectangle to check - * @param height the height of the rectangle to check - * @return {@code true} if the rectangle is entirely contained within the bounds of this ElementView; {@code false} otherwise - */ @Override public boolean contains(double x, double y, double width, double height) { return (x + width >= location.x && x <= location.x + size.width) && (y + height >= location.y && y <= location.y + size.height); } - /** - * Determines if the specified Rectangle2D object is entirely contained within the bounds of this ElementView. - * - * @param rectangle2D the Rectangle2D object representing the rectangle to check - * @return {@code true} if the rectangle is entirely contained within the bounds of this ElementView; {@code false} otherwise - */ @Override public boolean contains(Rectangle2D rectangle2D) { return contains( @@ -412,48 +324,22 @@ public boolean contains(Rectangle2D rectangle2D) { rectangle2D.getHeight()); } - - /** - * Returns an iterator over the path geometry of this ElementView. The iterator provides access to the path's - * segments and their coordinates, which can be used for rendering or hit testing. - * - * @param at the AffineTransform to apply to the path geometry - * @return a PathIterator that iterates over the path geometry of this ElementView - */ @Override public PathIterator getPathIterator(AffineTransform at) { return new Rectangle(location.x, location.y, size.width, size.height).getPathIterator(at); } - /** - * Returns an iterator over the path geometry of this ElementView with the specified flatness. The iterator provides - * access to the path's segments and their coordinates, which can be used for rendering or hit testing. - * - * @param at the AffineTransform to apply to the path geometry - * @param flatness the maximum distance that the line segments can deviate from the true path - * @return a PathIterator that iterates over the path geometry of this ElementView - */ @Override public PathIterator getPathIterator(AffineTransform at, double flatness) { return new Rectangle(location.x, location.y, size.width, size.height) .getPathIterator(at, flatness); } - /** - * Returns the bounding rectangle of this ElementView - * - * @return a Rectangle representing the bounding box of this ElementView - */ @Override public Rectangle getBounds() { return new Rectangle(location.x, location.y, size.width, size.height); } - /** - * Returns the bounding rectangle of this ElementView as a Rectangle2D - * - * @return a Rectangle2D representing the bounding box of this ElementView - */ @Override public Rectangle2D getBounds2D() { return new Rectangle(location.x, location.y, size.width, size.height); diff --git a/src/main/java/edu/rpi/legup/ui/boardview/GridBoardView.java b/src/main/java/edu/rpi/legup/ui/boardview/GridBoardView.java index 1baa34b3a..c40303192 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/GridBoardView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/GridBoardView.java @@ -5,11 +5,6 @@ import java.awt.Color; import java.awt.Dimension; -/** - * A view class for a grid-based board that displays elements in a grid layout. - * This class extends BoardView and is responsible for managing and rendering - * grid-based elements. - */ public class GridBoardView extends BoardView { protected Dimension gridSize; protected Dimension elementSize; @@ -56,14 +51,6 @@ public GridElementView getElement(int index) { return null; } - /** - * Retrieves the GridElementView at the specified grid coordinates (xIndex, yIndex). - * Returns null if the coordinates are out of bounds. - * - * @param xIndex the x-coordinate (column) of the element view to retrieve - * @param yIndex the y-coordinate (row) of the element view to retrieve - * @return the GridElementView at the specified coordinates, or null if out of bounds - */ public GridElementView getElement(int xIndex, int yIndex) { if (xIndex < gridSize.width && yIndex < gridSize.height) { return (GridElementView) elementViews.get(yIndex * gridSize.width + xIndex); @@ -71,10 +58,7 @@ public GridElementView getElement(int xIndex, int yIndex) { return null; } - /** - * Initializes the initial dimension of the viewport for the GridBoardView. - * Sets the size of the board view and adjusts the zoom to fit. - */ + /** Initializes the initial dimension of the viewport for the GridBoardView */ @Override public void initSize() { setSize(getProperSize()); @@ -82,9 +66,9 @@ public void initSize() { } /** - * Determines the proper dimension of the grid view based on grid size and element size. + * Helper method to determine the proper dimension of the grid view * - * @return the dimension of the grid view + * @return proper dimension of the grid view */ protected Dimension getProperSize() { Dimension boardViewSize = new Dimension(); @@ -93,21 +77,10 @@ protected Dimension getProperSize() { return boardViewSize; } - /** - * Retrieves the selection popup menu for data selection. - * Currently returns null as there is no implementation. - * - * @return null - */ public DataSelectionView getSelectionPopupMenu() { return null; } - /** - * Gets the size of each element in the grid - * - * @return the dimension of each element in the grid - */ public Dimension getElementSize() { return this.elementSize; } diff --git a/src/main/java/edu/rpi/legup/ui/boardview/GridElementView.java b/src/main/java/edu/rpi/legup/ui/boardview/GridElementView.java index 31e8fcd6c..440b3a693 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/GridElementView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/GridElementView.java @@ -2,11 +2,6 @@ import edu.rpi.legup.model.gameboard.GridCell; -/** - * A view class for a grid cell element in the board. - * This class extends ElementView and represents a specific type of element view - * associated with a GridCell. - */ public class GridElementView extends ElementView { public GridElementView(GridCell cell) { super(cell); diff --git a/src/main/java/edu/rpi/legup/ui/boardview/SelectionItemView.java b/src/main/java/edu/rpi/legup/ui/boardview/SelectionItemView.java index 15deb86d1..b2d3e31dd 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/SelectionItemView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/SelectionItemView.java @@ -3,68 +3,28 @@ import edu.rpi.legup.model.gameboard.PuzzleElement; import javax.swing.*; -/** - * A menu item view class that represents a selectable item in a menu, associated with a PuzzleElement. - * This class extends JMenuItem and provides additional functionality to - * handle PuzzleElement data. - */ public class SelectionItemView extends JMenuItem { private PuzzleElement data; - /** - * Constructs a SelectionItemView with the specified PuzzleElement and icon. - * Initializes the menu item with the given icon and associates it with the - * provided PuzzleElement. - * - * @param data the PuzzleElement associated with this menu item - * @param icon the icon to be displayed on the menu item - */ public SelectionItemView(PuzzleElement data, Icon icon) { super(icon); this.data = data; } - /** - * Constructs a SelectionItemView with the specified PuzzleElement and display text. - * Initializes the menu item with the given display text and associates it with the - * provided PuzzleElement. - * - * @param data the PuzzleElement associated with this menu item - * @param display the text to be displayed on the menu item - */ public SelectionItemView(PuzzleElement data, String display) { super(display); this.data = data; } - /** - * Constructs a SelectionItemView with the specified PuzzleElement and display integer. - * Initializes the menu item with the integer converted to a string and associates it with - * the provided PuzzleElement. - * - * @param data the PuzzleElement associated with this menu item - * @param display the integer to be displayed on the menu item - */ public SelectionItemView(PuzzleElement data, int display) { super(String.valueOf(display)); this.data = data; } - /** - * Constructs a SelectionItemView with the specified PuzzleElement. - * Initializes the menu item with the data's integer representation as display text. - * - * @param data the PuzzleElement associated with this menu item - */ public SelectionItemView(PuzzleElement data) { this(data, (Integer) data.getData()); } - /** - * Gets the PuzzleElement associated with this menu item - * - * @return the PuzzleElement associated with this menu item - */ public PuzzleElement getData() { return data; } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRulePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRulePanel.java index 849c5c145..1fb0a16ab 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRulePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/CaseRulePanel.java @@ -2,12 +2,6 @@ import javax.swing.ImageIcon; -/** - * The {@code CaseRulePanel} class is a specialized panel that represents case rules - * within a {@link RuleFrame}. It extends the {@link RulePanel} and provides - * specific functionality and UI components related to case rules. - * This class initializes with an icon and name that are specific to case rules. - */ public class CaseRulePanel extends RulePanel { /** * CaseRulePanel Constructor creates a CaseRulePanel diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/ContradictionRulePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/ContradictionRulePanel.java index 5bed7e17d..f695491fb 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/ContradictionRulePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/ContradictionRulePanel.java @@ -2,14 +2,7 @@ import javax.swing.*; -/** - * The {@code ContradictionRulePanel} class is a specialized panel that represents contradiction rules - * within a {@link RuleFrame}. It extends the {@link RulePanel} and provides - * specific functionality and UI components related to contradiction rules. - * This class initializes with an icon and name that are specific to contradiction rules. - */ - - public class ContradictionRulePanel extends RulePanel { +public class ContradictionRulePanel extends RulePanel { /** * ContradictionRulePanel Constructor creates a ContradictionRulePanel * diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/DirectRulePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/DirectRulePanel.java index c1562eb70..2795f2df7 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/DirectRulePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/DirectRulePanel.java @@ -4,13 +4,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * The {@code DirectRulePanel} class is a specialized panel that represents direct rules - * within a {@link RuleFrame}. It extends the {@link RulePanel} and provides - * specific functionality and UI components related to direct rules. - * This class initializes with an icon and name that are specific to direct rules. - */ - public class DirectRulePanel extends RulePanel { +public class DirectRulePanel extends RulePanel { private static final Logger LOGGER = LogManager.getLogger(DirectRulePanel.class.getName()); /** diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleButton.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleButton.java index 7222603bc..e9c274250 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleButton.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleButton.java @@ -3,11 +3,6 @@ import edu.rpi.legup.model.rules.Rule; import javax.swing.*; -/** - * The {@code RuleButton} class is a custom button that represents a rule in the user interface. - * It extends {@link JButton} and is designed to display a rule's name and icon. - * The button is initialized with a {@link Rule} object, which provides the name and icon for the button. - */ public class RuleButton extends JButton { private Rule rule; diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleFrame.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleFrame.java index 3131f474d..6279f93a4 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleFrame.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RuleFrame.java @@ -10,13 +10,6 @@ import javax.swing.*; import javax.swing.border.TitledBorder; -/** - * The {@code RuleFrame} class is a panel that contains and manages multiple rule-related panels - * within a tabbed interface. It extends {@link JPanel} and organizes the display of various rule types - * such as direct rules, contradiction rules, and case rules. - * The frame uses a {@link JTabbedPane} to allow users to switch between different rule panels. - * It also includes a search bar panel and a status label for displaying additional information. - */ public class RuleFrame extends JPanel { private static final String checkBox = " \u2714 "; private static final String xBox = " \u2718 "; @@ -35,12 +28,6 @@ public class RuleFrame extends JPanel { private RuleController controller; - /** - * Constructs a new {@code RuleFrame} instance. - * Initializes the frame with tabs for the different rule panels, a search bar panel, and a status label. - * - * @param controller the {@link RuleController} instance that manages the rules for this frame - */ public RuleFrame(RuleController controller) { MaterialTabbedPaneUI tabOverride = @@ -131,7 +118,7 @@ public void resetSize() { /** * Set the status label to a value. Use resetStatus to clear it. * - * @param check true if we want a checkbox, if false we'll have a red x box + * @param check true iff we want a check box, if false we'll have a red x box * @param text the text we're setting the label to display */ public void setStatus(boolean check, String text) { @@ -169,47 +156,22 @@ public RuleController getController() { return controller; } - /** - * Gets the JTabbedPane used in this frame - * - * @return the JTabbedPane instance - */ public JTabbedPane getTabbedPane() { return tabbedPane; } - /** - * Gets the {@code DirectRulePanel} contained in this frame - * - * @return the {@link DirectRulePanel} instance - */ public DirectRulePanel getDirectRulePanel() { return DirectRulePanel; } - /** - * Gets the {@code CaseRulePanel} contained in this frame - * - * @return the {@link CaseRulePanel} instance - */ public CaseRulePanel getCasePanel() { return casePanel; } - /** - * Gets the {@code ContradictionRulePanel} contained in this frame - * - * @return the {@link ContradictionRulePanel} instance - */ public ContradictionRulePanel getContradictionPanel() { return contradictionPanel; } - /** - * Gets the {@code SearchBarPanel} contained in this frame - * - * @return the {@link SearchBarPanel} instance - */ public SearchBarPanel getSearchPanel() { return searchPanel; } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java index 4c9ebf882..5d985d5c2 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/RulePanel.java @@ -9,10 +9,6 @@ import java.util.List; import javax.swing.*; -/** - * Abstract base class for panels displaying rules. Each subclass will represent a specific type - * of rule panel (e.g., DirectRulePanel, CaseRulePanel). - */ public abstract class RulePanel extends JPanel { protected ImageIcon icon; protected String name; @@ -36,7 +32,7 @@ public RulePanel(RuleFrame ruleFrame) { } /** - * Gets the array of rule buttons + * Gets the rule rule buttons * * @return rule ruleButtons */ @@ -59,30 +55,20 @@ public void setRules(List rules) { Rule rule = rules.get(i); ruleButtons[i] = new RuleButton(rule); - ruleButtons[i].setPreferredSize(new Dimension(150, 150)); // adjust the size of each RuleButton - - if (rule.getRuleName().length() > 18) { - ruleButtons[i].setFont(new Font("Segoe UI", Font.PLAIN, 11)); - } - if (rule.getRuleName().length() > 20) { - ruleButtons[i].setFont(new Font("Segoe UI", Font.PLAIN, 10)); - } - System.out.println(ruleButtons[i].getFont().getName()); - + ruleButtons[i].setPreferredSize( + new Dimension(150, 150)); // adjust the size of each RuleButton ruleButtons[i].setHorizontalTextPosition(JButton.CENTER); ruleButtons[i].setVerticalTextPosition(JButton.BOTTOM); ruleFrame.getButtonGroup().add(ruleButtons[i]); - ruleButtons[i].setToolTipText(rule.getRuleName() + ": " + rule.getDescription()); // showing description + ruleButtons[i].setToolTipText( + rule.getRuleName() + ": " + rule.getDescription()); // showing description ruleButtons[i].addActionListener(ruleFrame.getController()); add(ruleButtons[i]); } revalidate(); } - /** - * Updates the rules displayed by reloading images and setting the rules again. - */ public void updateRules() { for (Rule rule : rules) { rule.loadImage(); @@ -338,58 +324,28 @@ public List getRules() { return rules; } - /** - * Gets the icon associated with this panel - * - * @return The ImageIcon associated with this panel - */ public ImageIcon getIcon() { return icon; } - /** - * Sets the icon for this panel - * - * @return the ImageIcon associated with this panel - */ public void setIcon(ImageIcon icon) { this.icon = icon; } - /** - * Gets the name of this panel - * - * @return the name of this panel in a String - */ @Override public String getName() { return name; } - /** - * Sets the name of this panel - * - * @param name the name to set for this panel - */ @Override public void setName(String name) { this.name = name; } - /** - * Gets the tooltip text associated with this panel - * - * @return the tooltip text of this panel - */ public String getToolTip() { return toolTip; } - /** - * Sets the tooltip text for this panel - * - * @param toolTip the tooltip text to set for this panel - */ public void setToolTip(String toolTip) { this.toolTip = toolTip; } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/SearchBarPanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/SearchBarPanel.java index 842859ce2..aba4707cd 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/SearchBarPanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/rulesview/SearchBarPanel.java @@ -2,11 +2,6 @@ import javax.swing.*; - -/** - * The {@code SearchBarPanel} class creates a panel that allows users to search for rules within the rule frame. - * This panel provides a search bar for entering rule names and finding corresponding rules. - */ public class SearchBarPanel extends RulePanel { /** * SearchBarPanel Constructor creates a SearchBarPanel diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeElementView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeElementView.java index 228e69950..33c04717d 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeElementView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeElementView.java @@ -4,12 +4,6 @@ import edu.rpi.legup.model.tree.TreeElementType; import java.awt.*; -/** - * Abstract base class for views of tree elements in the tree structure. - * This class implements the Shape interface to support custom drawing and interaction - * with tree elements. - * It holds properties for rendering, interaction, and layout of the tree elements. - */ public abstract class TreeElementView implements Shape { protected TreeElement treeElement; protected double span; @@ -42,7 +36,7 @@ protected TreeElementView(TreeElementType type, TreeElement treeElement) { public abstract void draw(Graphics2D graphics2D); /** - * Gets the span for the subtree rooted at this view + * Gets the span for the sub tree rooted at this view * * @return span bounded y span */ @@ -51,7 +45,7 @@ public double getSpan() { } /** - * Sets the span for the subtree rooted at this view. + * Sets the span for the sub tree rooted at this view. * * @param span bounded y span */ diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeNodeView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeNodeView.java index 0e2a31bbf..990d96620 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeNodeView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeNodeView.java @@ -7,12 +7,6 @@ import java.awt.geom.*; import java.util.ArrayList; -/** - * Represents a view of a tree node in the tree structure. - * This class extends {@link TreeElementView} and provides specific rendering and interaction - * functionality for tree nodes. It includes visual properties and methods to manage the - * node's appearance, location, and its relationships with other nodes. - */ public class TreeNodeView extends TreeElementView { static final int RADIUS = 25; static final int DIAMETER = 2 * RADIUS; @@ -266,119 +260,51 @@ public int getRadius() { return RADIUS; } - /** - * Returns the bounding rectangle of this TreeNodeView - * - * @return a Rectangle representing the bounding box of this TreeNodeView - */ @Override public Rectangle getBounds() { return new Rectangle(location.x, location.y, DIAMETER, DIAMETER); } - /** - * Returns the bounding rectangle of this TreeNodeView as a Rectangle2D - * - * @return a Rectangle2D representing the bounding box of this TreeNodeView - */ @Override public Rectangle2D getBounds2D() { return new Rectangle(location.x, location.y, DIAMETER, DIAMETER); } - /** - * Determines if the specified point (x, y) is within the bounds of this TreeNodeView - * - * @param x the x-coordinate of the point to check - * @param y the y-coordinate of the point to check - * @return {@code true} if the point is within the bounds of this TreeNodeView; {@code false} otherwise - */ @Override public boolean contains(double x, double y) { return Math.sqrt(Math.pow(x - location.x, 2) + Math.pow(y - location.y, 2)) <= RADIUS; } - /** - * Determines if the specified Point2D object is within the bounds of this TreeNodeView - * - * @param p the Point2D object representing the point to check - * @return {@code true} if the point is within the bounds of this TreeNodeView; {@code false} otherwise - */ @Override public boolean contains(Point2D p) { return contains(p.getX(), p.getY()); } - /** - * Determines if the specified rectangle defined by (x, y, width, height) intersects with the bounds of this TreeNodeView. - * - * @param x The x-coordinate of the rectangle to check - * @param y The y-coordinate of the rectangle to check - * @param w The width of the rectangle to check - * @param h The height of the rectangle to check - * @return {@code true} if the rectangle intersects with the bounds of this TreeNodeView; {@code false} otherwise - */ @Override public boolean intersects(double x, double y, double w, double h) { return false; } - /** - * Determines if the specified Rectangle2D object intersects with the bounds of this TreeNodeView. - * - * @param r the Rectangle2D object representing the rectangle to check - * @return {@code true} if the rectangle intersects with the bounds of this TreeNodeView; {@code false} otherwise - */ @Override public boolean intersects(Rectangle2D r) { return intersects(r.getX(), r.getY(), r.getWidth(), r.getHeight()); } - /** - * Determines if the specified rectangle defined by (x, y, width, height) is entirely contained within the bounds of this TreeNodeView - * - * @param x the x-coordinate of the rectangle to check - * @param y the y-coordinate of the rectangle to check - * @param w the width of the rectangle to check - * @param h the height of the rectangle to check - * @return {@code true} if the rectangle is entirely contained within the bounds of this TreeNodeView; {@code false} otherwise - */ @Override public boolean contains(double x, double y, double w, double h) { return false; } - /** - * Determines if the specified Rectangle2D object is entirely contained within the bounds of this TreeNodeView. - * - * @param r the Rectangle2D object representing the rectangle to check - * @return {@code true} if the rectangle is entirely contained within the bounds of this TreeNodeView; {@code false} otherwise - */ @Override public boolean contains(Rectangle2D r) { return false; } - /** - * Returns an iterator over the path geometry of this TreeNodeView. The iterator provides access to the path's - * segments and their coordinates, which can be used for rendering or hit testing. - * - * @param at the AffineTransform to apply to the path geometry - * @return a PathIterator that iterates over the path geometry of this TreeNodeView - */ @Override public PathIterator getPathIterator(AffineTransform at) { return null; } - /** - * Returns an iterator over the path geometry of this TreeNodeView with the specified flatness. The iterator provides - * access to the path's segments and their coordinates, which can be used for rendering or hit testing. - * - * @param at the AffineTransform to apply to the path geometry - * @param flatness the maximum distance that the line segments can deviate from the true path - * @return a PathIterator that iterates over the path geometry of this TreeNodeView - */ @Override public PathIterator getPathIterator(AffineTransform at, double flatness) { return null; diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreePanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreePanel.java index 4bef664bd..b6a29f2b5 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreePanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreePanel.java @@ -17,12 +17,6 @@ import javax.swing.JPanel; import javax.swing.border.TitledBorder; - -/** - * {@code TreePanel} is a JPanel that manages and displays a tree view with associated toolbar and status information. - * It provides methods to interact with the tree view, such as adding, deleting, and merging tree elements, - * and updating the status based on actions performed. - */ public class TreePanel extends JPanel { public boolean modifiedSinceSave = false; public boolean modifiedSinceUndoPush = false; @@ -35,9 +29,6 @@ public class TreePanel extends JPanel { private JLabel status; - /** - * Constructs a {@code TreePanel} and initializes the UI components. - */ public TreePanel(/*LegupUI legupUI*/ ) { // this.legupUI = legupUI; @@ -69,20 +60,10 @@ public TreePanel(/*LegupUI legupUI*/ ) { updateStatusTimer = 0; } - /** - * Repaints the tree view with the provided {@link Tree} object - * - * @param tree the {@link Tree} object to update the view with - */ public void repaintTreeView(Tree tree) { treeView.updateTreeView(tree); } - /** - * Updates the status of the panel based on changes to the {@link Board} - * - * @param board the {@link Board} object representing the current board state - */ public void boardDataChanged(Board board) { modifiedSinceSave = true; modifiedSinceUndoPush = true; @@ -90,11 +71,6 @@ public void boardDataChanged(Board board) { // colorTransitions(); } - /** - * Updates the status display based on the status timer. - * If the timer is greater than 0, the status will not be updated. - * Otherwise, it clears the status text. - */ public void updateStatus() { updateStatusTimer = ((updateStatusTimer - 1) > 0) ? (updateStatusTimer - 1) : 0; if (updateStatusTimer > 0) { @@ -103,41 +79,22 @@ public void updateStatus() { this.status.setText(""); } - /** - * Updates the status display with the given status string - * - * @param statusString the status string to display - */ public void updateStatus(String statusString) { status.setForeground(Color.BLACK); status.setFont(MaterialFonts.REGULAR); status.setText(statusString); } - /** - * Updates the status display as an error with an error message - * - * @param error the error message to display - */ public void updateError(String error) { status.setForeground(Color.RED); status.setFont(MaterialFonts.ITALIC); status.setText(error); } - /** - * Gets the {@link TreeView} instance associated with this panel - * - * @return the {@link TreeView} instance - */ public TreeView getTreeView() { return treeView; } - /** - * Adds a new tree element by executing an {@link AddTreeElementCommand}. - * If the command cannot be executed, it updates the status display with an error and error message. - */ public void add() { TreeViewSelection selection = treeView.getSelection(); @@ -150,10 +107,6 @@ public void add() { } } - /** - * Deletes the selected tree element by executing a {@link DeleteTreeElementCommand}. - * If the command cannot be executed, it updates the status display with an error and an error message. - */ public void delete() { TreeViewSelection selection = treeView.getSelection(); @@ -166,10 +119,6 @@ public void delete() { } } - /** - * Merges selected tree elements by executing a {@link MergeCommand}. - * If the command cannot be executed, it updates the status display with an error and an error message. - */ public void merge() { TreeViewSelection selection = treeView.getSelection(); @@ -182,10 +131,6 @@ public void merge() { } } - /** - * Toggles the collapsed state of the selected tree elements. - * If an element is collapsed, it will be expanded, and vice versa. - */ public void collapse() { TreeViewSelection selection = treeView.getSelection(); for (TreeElementView view : selection.getSelectedViews()) { diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarButton.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarButton.java index 214c735df..002092155 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarButton.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarButton.java @@ -3,20 +3,11 @@ import java.awt.Dimension; import javax.swing.*; -/** - * {@code TreeToolBarButton} is a JButton that represents a button in the tree toolbar. - */ public class TreeToolBarButton extends JButton { private TreeToolBarName name; private final Dimension MINIMUM_DIMENSION = new Dimension(60, 60); - /** - * Constructs a {@code TreeToolBarButton} with the specified icon and name. - * - * @param imageIcon the {@link ImageIcon} to be displayed on the button - * @param name the {@link TreeToolBarName} associated with this button - */ public TreeToolBarButton(ImageIcon imageIcon, TreeToolBarName name) { super(imageIcon); this.name = name; @@ -25,11 +16,6 @@ public TreeToolBarButton(ImageIcon imageIcon, TreeToolBarName name) { this.setFocusPainted(false); } - /** - * Gets the {@link TreeToolBarName} associated with this button - * - * @return the {@link TreeToolBarName} associated with this button - */ public TreeToolBarName getToolBarName() { return name; } diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarName.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarName.java index 3aec664be..c805021be 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarName.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolBarName.java @@ -1,9 +1,5 @@ package edu.rpi.legup.ui.proofeditorui.treeview; -/** - * {@code TreeToolBarName} defines the names of actions represented by buttons in the tree toolbar. - * These actions are used for managing tree elements within the UI. - */ public enum TreeToolBarName { ADD_CHILD, DEL_CHILD, diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolbarPanel.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolbarPanel.java index 500ed29c5..8f3ebfc23 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolbarPanel.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeToolbarPanel.java @@ -3,11 +3,6 @@ import java.awt.*; import javax.swing.*; - -/** - * {@code TreeToolbarPanel} is a JPanel that provides a toolbar for managing tree elements in the tree view. - * It includes buttons for adding, deleting, merging, and collapsing nodes. - */ public class TreeToolbarPanel extends JPanel { private TreePanel treePanel; private TreeToolBarButton addChild, delChild, merge, collapse; diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeTransitionView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeTransitionView.java index 25c67bb5a..b022ac596 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeTransitionView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeTransitionView.java @@ -10,11 +10,6 @@ import java.util.ArrayList; import java.util.List; -/** - * {@code TreeTransitionView} is a visual representation of a tree transition in the tree view. - * It extends TreeElementView and displays a transition arrow between tree nodes and handles various - * visual states such as selection, hover, and correctness. - */ public class TreeTransitionView extends TreeElementView { static final int RADIUS = 25; static final int DIAMETER = 2 * RADIUS; @@ -271,165 +266,87 @@ public void removeParentView(TreeNodeView nodeView) { } } - /** - * Gets the x-coordinate of the end point of the transition arrow - * - * @return the x-coordinate of the end point - */ + public Point getEndPoint() { + return endPoint; + } + + public void setEndPoint(Point endPoint) { + this.endPoint = endPoint; + } + public int getEndX() { return endPoint.x; } - /** - * Sets the x-coordinate of the end point of the transition arrow - * - * @param x the new x-coordinate of the end point - */ public void setEndX(int x) { this.endPoint.x = x; } - /** - * Gets the y-coordinate of the end point of the transition arrow - * - * @return the y-coordinate of the end point - */ public int getEndY() { return endPoint.y; } - /** - * Sets the y-coordinate of the end point of the transition arrow - * - * @param y the new y-coordinate of the end point - */ public void setEndY(int y) { this.endPoint.y = y; } - /** - * Gets the start point at the specified index from the list of start points - * - * @param index the index of the start point to retrieve - * @return the start point at the specified index, or null if the index is out of range - */ + public List getLineStartPoints() { + return lineStartPoints; + } + + public void setLineStartPoints(List lineStartPoints) { + this.lineStartPoints = lineStartPoints; + } + public Point getLineStartPoint(int index) { return index < lineStartPoints.size() ? lineStartPoints.get(index) : null; } - /** - * Returns the bounding rectangle of this TreeTransitionView - * - * @return a Rectangle representing the bounding box of this TreeTransitionView - */ @Override public Rectangle getBounds() { return arrowhead.getBounds(); } - /** - * Returns the bounding rectangle of this TreeTransitionView as a Rectangle2D - * - * @return a Rectangle2D representing the bounding box of this TreeTransitionView - */ @Override public Rectangle2D getBounds2D() { return arrowhead.getBounds2D(); } - /** - * Determines if the specified point (x, y) is within the bounds of this TreeTransitionView - * - * @param x the x-coordinate of the point to check - * @param y the y-coordinate of the point to check - * @return {@code true} if the point is within the bounds of this TreeTransitionView; {@code false} otherwise - */ @Override public boolean contains(double x, double y) { return arrowhead.contains(x, y); } - /** - * Determines if the specified Point2D object is within the bounds of this TreeTransitionView - * - * @param p the Point2D object representing the point to check - * @return {@code true} if the point is within the bounds of this TreeTransitionView; {@code false} otherwise - */ @Override public boolean contains(Point2D p) { return arrowhead != null && arrowhead.contains(p); } - /** - * Determines if the specified rectangle defined by (x, y, width, height) intersects with the bounds of this TreeTransitionView. - * - * @param x The x-coordinate of the rectangle to check - * @param y The y-coordinate of the rectangle to check - * @param w The width of the rectangle to check - * @param h The height of the rectangle to check - * @return {@code true} if the rectangle intersects with the bounds of this TreeTransitionView; {@code false} otherwise - */ @Override public boolean intersects(double x, double y, double w, double h) { return arrowhead.intersects(x, y, w, h); } - /** - * Determines if the specified Rectangle2D object intersects with the bounds of this TreeTransitionView. - * - * @param r the Rectangle2D object representing the rectangle to check - * @return {@code true} if the rectangle intersects with the bounds of this TreeTransitionView; {@code false} otherwise - */ @Override public boolean intersects(Rectangle2D r) { return arrowhead.intersects(r); } - /** - * Determines if the specified rectangle defined by (x, y, width, height) is entirely contained within the bounds of this TreeTransitionView - * - * @param x the x-coordinate of the rectangle to check - * @param y the y-coordinate of the rectangle to check - * @param w the width of the rectangle to check - * @param h the height of the rectangle to check - * @return {@code true} if the rectangle is entirely contained within the bounds of this TreeTransitionView; {@code false} otherwise - */ @Override public boolean contains(double x, double y, double w, double h) { return arrowhead.contains(x, y, w, h); } - /** - * Determines if the specified Rectangle2D object is entirely contained within the bounds of this TreeTransitionView. - * - * @param r the Rectangle2D object representing the rectangle to check - * @return {@code true} if the rectangle is entirely contained within the bounds of this TreeTransitionView; {@code false} otherwise - */ @Override public boolean contains(Rectangle2D r) { return arrowhead.contains(r); } - /** - * Returns an iterator over the path geometry of this TreeTransitionView. The iterator provides access to the path's - * segments and their coordinates, which can be used for rendering or hit testing. - * - * @param at the AffineTransform to apply to the path geometry - * @return a PathIterator that iterates over the path geometry of this TreeTransitionView - */ @Override public PathIterator getPathIterator(AffineTransform at) { return arrowhead.getPathIterator(at); } - /** - * Returns an iterator over the path geometry of this TreeTransitionView with the specified flatness. The iterator provides - * access to the path's segments and their coordinates, which can be used for rendering or hit testing. - * - * @param at the AffineTransform to apply to the path geometry - * @param flatness the maximum distance that the line segments can deviate from the true path - * @return a PathIterator that iterates over the path geometry of this TreeTransitionView - */ @Override public PathIterator getPathIterator(AffineTransform at, double flatness) { return arrowhead.getPathIterator(at, flatness); diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java index 490cf1480..f491009b4 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeView.java @@ -26,12 +26,6 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -/** - * The {@code TreeView} class provides a graphical representation of a {@code Tree} structure, - * allowing interaction and visualization of tree elements, transitions, and selections. - * It extends {@code ScrollView} and implements {@code ITreeListener} to respond to updates - * in the tree structure. - */ public class TreeView extends ScrollView implements ITreeListener { private static final Logger LOGGER = LogManager.getLogger(TreeView.class.getName()); @@ -57,11 +51,6 @@ public class TreeView extends ScrollView implements ITreeListener { private TreeViewSelection selection; - /** - * Constructs a {@code TreeView} with the specified {@code TreeController}. - * - * @param treeController the {@code TreeController} used to manage tree operations - */ public TreeView(TreeController treeController) { super(treeController); currentStateBoxes = new ArrayList<>(); @@ -73,11 +62,6 @@ public TreeView(TreeController treeController) { selection = new TreeViewSelection(); } - /** - * Gets the current tree view selection - * - * @return the {@code TreeViewSelection} object representing the current selection - */ public TreeViewSelection getSelection() { return selection; } @@ -147,11 +131,6 @@ private TreeElementView getTreeElementView(Point point, TreeElementView elementV return null; } - /** - * Updates the tree view with the specified {@code Tree} - * - * @param tree the {@code Tree} to display in the view - */ public void updateTreeView(Tree tree) { this.tree = tree; if (selection.getSelectedViews().size() == 0) { @@ -169,9 +148,6 @@ public void setTree(Tree tree) { this.tree = tree; } - /** - * Updates the size of the tree view based on the bounds of its tree - */ public void updateTreeSize() { if (GameBoardFacade.getInstance().getTree() == null) { return; @@ -179,23 +155,15 @@ public void updateTreeSize() { setSize(bounds.getSize()); } - /** - * Resets the view if the tree bounds have been modified - */ public void reset() { if (bounds.x != 0 || bounds.y != 0) { updateTreeSize(); } } - /** - * Adjusts the zoom level to fit the entire tree within the viewport when - * the Resize Proof button is selected - */ public void zoomFit() { - final int MIN_HEIGHT = 200; - double fitWidth = (viewport.getWidth() - 7.0) / (getSize().width - 75); - double fitHeight = (viewport.getHeight()) / Math.max((getSize().height - 115), MIN_HEIGHT); + double fitWidth = (viewport.getWidth() - 8.0) / (getSize().width - 200); + double fitHeight = (viewport.getHeight() - 8.0) / (getSize().height - 120); zoomTo(Math.min(fitWidth, fitHeight)); viewport.setViewPosition(new Point(0, viewport.getHeight() / 2)); } @@ -244,11 +212,6 @@ public void layoutContainer(Container parent) { }; } - /** - * Draws the tree view on the provided {@code Graphics2D} context - * - * @param graphics2D the {@code Graphics2D} context to draw on - */ public void draw(Graphics2D graphics2D) { currentStateBoxes.clear(); Tree tree = GameBoardFacade.getInstance().getTree(); @@ -271,20 +234,11 @@ public void draw(Graphics2D graphics2D) { } } - /** - * Resets the zoom level to its default state and positions the viewport from the top-left corner - */ public void zoomReset() { zoomTo(1.0); viewport.setViewPosition(new Point(0, 0)); } - /** - * Recursively redraws the tree starting from the specified node view - * - * @param graphics2D the {@code Graphics2D} context to draw on - * @param nodeView the {@code TreeNodeView} to start drawing from - */ private void redrawTree(Graphics2D graphics2D, TreeNodeView nodeView) { if (nodeView != null) { nodeView.draw(graphics2D); @@ -295,11 +249,6 @@ private void redrawTree(Graphics2D graphics2D, TreeNodeView nodeView) { } } - /** - * Removes the specified {@code TreeElementView} from the tree view - * - * @param view the {@code TreeElementView} to remove - */ public void removeTreeElement(TreeElementView view) { if (view.getType() == NODE) { TreeNodeView nodeView = (TreeNodeView) view; @@ -333,9 +282,6 @@ public void drawMouseOver(Graphics2D g) { } } - /** - * Resets the view by clearing the current tree, root node view, and selection - */ public void resetView() { this.tree = null; this.rootNodeView = null; @@ -461,17 +407,8 @@ public TreeElementView getElementView(TreeElement element) { return viewMap.get(element); } - /** - * Removes the specified {@link TreeNode} and its associated views - * - * @param node the {@link TreeNode} to be removed - */ - public void removeTreeNode(TreeNode node) { + private void removeTreeNode(TreeNode node) { viewMap.remove(node); - if (node.getChildren() != null) { - node.getChildren().forEach(t -> removeTreeTransition(t)); - } - List children = node.getChildren(); // if child is a case rule, unlock ancestor elements @@ -498,13 +435,11 @@ public void removeTreeNode(TreeNode node) { } // set modifiable if started modifiable - boolean modifiable = false; - if (tree != null) { - tree.getRootNode() - .getBoard() - .getPuzzleElement(oldElement) - .isModifiable(); - } + boolean modifiable = + tree.getRootNode() + .getBoard() + .getPuzzleElement(oldElement) + .isModifiable(); // unmodifiable if already modified TreeNode modNode = ancestor.getParent().getParents().get(0); @@ -522,58 +457,22 @@ public void removeTreeNode(TreeNode node) { } } } + node.getChildren().forEach(t -> removeTreeTransition(t)); } - /** - * Removes the specified {@link TreeTransition} and its associated views - * - * @param trans the {@link TreeTransition} to be removed - */ - public void removeTreeTransition(TreeTransition trans) { + private void removeTreeTransition(TreeTransition trans) { viewMap.remove(trans); if (trans.getChildNode() != null) { removeTreeNode(trans.getChildNode()); } - - // Update transition modifiability if removing a case rule - List parents = trans.getParents(); - for (TreeNode parent : parents) { - // if transition is a case rule, unlock ancestor elements up until latest case rule or root node - boolean nextAncestorIsCaseRule = false; - Rule rule = trans.getRule(); - if (rule instanceof CaseRule) { - List ancestors = parent.getAncestors(); - for (int i = 0; i < ancestors.size(); i++) { - if (ancestors.get(i).getParent() == null) { - continue; - } - if (nextAncestorIsCaseRule) { - break; - } - for (PuzzleElement element : parent.getBoard().getPuzzleElements()) { - PuzzleElement curElement = ancestors.get(i).getParent().getBoard().getPuzzleElement(element); - if (!curElement.isModifiableCaseRule()) { - curElement.setModifiableCaseRule(true); - } - } - if (ancestors.get(i).getParent().getRule() instanceof CaseRule) { - nextAncestorIsCaseRule = true; - } - } - } - } } - /** - * Adds the specified {@link TreeNode} and its associated views - * - * @param node the {@link TreeNode} to be added - */ private void addTreeNode(TreeNode node) { TreeTransition parent = node.getParent(); - TreeNodeView nodeView = new TreeNodeView(node); + TreeNodeView nodeView = new TreeNodeView(node); TreeTransitionView parentView = (TreeTransitionView) viewMap.get(parent); + nodeView.setParentView(parentView); parentView.setChildView(nodeView); @@ -593,7 +492,8 @@ private void addTreeNode(TreeNode node) { continue; } for (PuzzleElement element : - caseRule.dependentElements(node.getBoard(), node.getChildren().get(0).getSelection())) { + caseRule.dependentElements( + node.getBoard(), node.getChildren().get(0).getSelection())) { // increment and lock PuzzleElement oldElement = ancestor.getParent().getBoard().getPuzzleElement(element); @@ -607,50 +507,47 @@ private void addTreeNode(TreeNode node) { } } - /** - * Adds the specified {@link TreeTransition} and its associated views - * - * @param trans The {@link TreeTransition} to be added - */ private void addTreeTransition(TreeTransition trans) { List parents = trans.getParents(); - TreeTransitionView transView = new TreeTransitionView(trans); + TreeTransitionView transView = new TreeTransitionView(trans); for (TreeNode parent : parents) { TreeNodeView parentNodeView = (TreeNodeView) viewMap.get(parent); transView.addParentView(parentNodeView); parentNodeView.addChildrenView(transView); - viewMap.put(trans, transView); - // if transition is a new case rule, lock dependent ancestor elements Rule rule = trans.getRule(); if (rule instanceof CaseRule && parent.getChildren().size() == 1) { + CaseRule caseRule = (CaseRule) rule; + List ancestors = parent.getAncestors(); for (TreeNode ancestor : ancestors) { // for all ancestors but root if (ancestor.getParent() == null) { continue; } - - for (PuzzleElement element : parent.getBoard().getPuzzleElements()) { - PuzzleElement curElement = ancestor.getParent().getBoard().getPuzzleElement(element); - curElement.setModifiableCaseRule(false); + for (PuzzleElement element : + caseRule.dependentElements(parent.getBoard(), trans.getSelection())) { + // increment and lock + PuzzleElement oldElement = + ancestor.getParent().getBoard().getPuzzleElement(element); + oldElement.setCasesDepended(oldElement.getCasesDepended() + 1); + oldElement.setModifiable(false); } } } } + viewMap.put(trans, transView); + if (trans.getChildNode() != null) { addTreeNode(trans.getChildNode()); } } - /** - * Draws the tree using the provided {@link Graphics2D} object - * - * @param graphics2D the {@link Graphics2D} object used for drawing the tree - */ + /// New Draw Methods + public void drawTree(Graphics2D graphics2D) { if (tree == null) { LOGGER.error("Unable to draw tree."); @@ -676,11 +573,6 @@ public void drawTree(Graphics2D graphics2D) { } } - /** - * Creates views for the given {@link TreeNodeView} and its children - * - * @param nodeView the {@link TreeNodeView} for which to create views - */ public void createViews(TreeNodeView nodeView) { if (nodeView != null) { viewMap.put(nodeView.getTreeElement(), nodeView); @@ -714,14 +606,6 @@ public void createViews(TreeNodeView nodeView) { } } - /** - * Calculates the layout locations (x and y coordinates) of the nodes in the tree. - * This method recursively traverses the tree and updates the positions of - * nodes and transitions based on their depth and parent relationships. - * - * @param nodeView the node view to calculate the positions for - * @param depth the depth of the node in the tree, used to calculate its x-coordinate - */ public void calculateViewLocations(TreeNodeView nodeView, int depth) { nodeView.setDepth(depth); int xLoc = (NODE_GAP_WIDTH + DIAMETER) * depth + DIAMETER; @@ -827,13 +711,6 @@ public void calculateViewLocations(TreeNodeView nodeView, int depth) { } } - /** - * Calculates the span (height) required for the given view, including its children. - * This method recursively determines the span for nodes and transitions based on their - * children and the merging branches they belong to. - * - * @param view the view whose span is to be calculated - */ public void calcSpan(TreeElementView view) { if (view.getType() == NODE) { TreeNodeView nodeView = (TreeNodeView) view; @@ -902,12 +779,12 @@ public void calcSpan(TreeElementView view) { } /** - * Calculates the span of a subtree rooted at the specified view, stopping at the given - * stop view. The stop view is not included in the span calculation. + * Calculates the sub span of a given sub tree rooted at the specified view and stops at the + * tree puzzleElement view specified as stop. Stop tree puzzleElement is NOT included in the + * span calculation * - * @param view the root view of the subtree to calculate the span for - * @param stop the view at which to stop the span calculation. The stop view itself is - * not included in the span calculation + * @param view + * @param stop */ private void subCalcSpan(TreeElementView view, TreeElementView stop) { // safe-guard for infinite loop @@ -982,14 +859,12 @@ private void subCalcSpan(TreeElementView view, TreeElementView stop) { } /** - * Reorders the branches of a given node such that branches that merge are grouped together sequentially. - * Transitions are kept in their relative order based on their original positions in the list of child transitions - * of the specified node. This ensures that the visual representation of the branches and transitions maintains - * a logical and readable structure. + * Reorders branches such that merging branches are sequentially grouped together and + * transitions are kept in relative order in the list of child transitions of the specified node * - * @param node the root node whose branches are to be reordered - * @param branches a DisjointSets structure representing the merging relationships of the child branches of the - * specified node. This determines which branches should be grouped together + * @param node root node of the branches + * @param branches DisjointSets of the child branches of the specified node which determine + * which branches merge */ private void reorderBranches(TreeNode node, DisjointSets branches) { List children = node.getChildren(); diff --git a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeViewSelection.java b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeViewSelection.java index c7893b168..71a65b49e 100644 --- a/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeViewSelection.java +++ b/src/main/java/edu/rpi/legup/ui/proofeditorui/treeview/TreeViewSelection.java @@ -4,11 +4,6 @@ import java.util.ArrayList; import java.util.List; - -/** - * {@code TreeViewSelection} manages the selection and hover state of tree element views in a tree view. - * It maintains a list of selected views, tracks the currently hovered view, and manages the mouse position. - */ public class TreeViewSelection { private ArrayList selectedViews; private TreeElementView hover; diff --git a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementFrame.java b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementFrame.java index 97c76919e..e0524f84d 100644 --- a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementFrame.java +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementFrame.java @@ -14,36 +14,52 @@ public class ElementFrame extends JPanel { private static final String htmlTail = ""; private PlaceableElementPanel placeableElementPanel; - //private JTabbedPane tabbedPane; + private NonPlaceableElementPanel nonPlaceableElementPanel; + private JTabbedPane tabbedPane; private ButtonGroup buttonGroup; private EditorElementController controller; public ElementFrame(EditorElementController controller) { - this.controller = controller; - + MaterialTabbedPaneUI tabOverride = + new MaterialTabbedPaneUI() { + // this prevents the tabs from moving around when you select them + @Override + protected boolean shouldRotateTabRuns(int i) { + return false; + } + }; + + this.tabbedPane = new JTabbedPane(); + tabbedPane.setUI(tabOverride); JLabel status = new JLabel("", SwingConstants.CENTER); this.buttonGroup = new ButtonGroup(); - // Parent panel to hold all elements - JPanel elementPanel = new JPanel(); - elementPanel.setLayout(new BoxLayout(elementPanel, BoxLayout.Y_AXIS)); + nonPlaceableElementPanel = new NonPlaceableElementPanel(this); + // nonPlaceableElementPanel.setMinimumSize(new Dimension(100,200)); + tabbedPane.addTab( + nonPlaceableElementPanel.getName(), + nonPlaceableElementPanel.getIcon(), + new JScrollPane(nonPlaceableElementPanel), + nonPlaceableElementPanel.getToolTip()); placeableElementPanel = new PlaceableElementPanel(this); - placeableElementPanel.setMinimumSize(new Dimension(100, 200)); - elementPanel.add(new JScrollPane(placeableElementPanel)); + // placeableElementPanel.setMinimuSize(new Dimension(100,200)); + tabbedPane.addTab( + placeableElementPanel.getName(), + placeableElementPanel.getIcon(), + new JScrollPane(placeableElementPanel), + placeableElementPanel.getToolTip()); + tabbedPane.setTabPlacement(JTabbedPane.TOP); - // Set layout and dimensions for the main panel setLayout(new BorderLayout()); setMinimumSize(new Dimension(250, 256)); setPreferredSize(new Dimension(330, 256)); - // Add components to the main panel - add(elementPanel, BorderLayout.CENTER); + add(tabbedPane); add(status, BorderLayout.SOUTH); - // Center-align the titled border TitledBorder title = BorderFactory.createTitledBorder("Elements"); title.setTitleJustification(TitledBorder.CENTER); setBorder(title); @@ -53,26 +69,29 @@ public ButtonGroup getButtonGroup() { return buttonGroup; } -// public void resetSize() { -// int buttonWidth = -// ((ElementPanel) tabbedPane.getSelectedComponent()) -// .getElementButtons()[0].getWidth(); -// this.setMinimumSize(new Dimension(2 * buttonWidth + 64, this.getHeight())); -// } + public void resetSize() { + int buttonWidth = + ((ElementPanel) tabbedPane.getSelectedComponent()) + .getElementButtons()[0].getWidth(); + this.setMinimumSize(new Dimension(2 * buttonWidth + 64, this.getHeight())); + } public void setElements(Puzzle puzzle) { - if (puzzle != null) { - placeableElementPanel.setElements(puzzle.getPlaceableElements()); - } + nonPlaceableElementPanel.setElements(puzzle.getNonPlaceableElements()); + placeableElementPanel.setElements(puzzle.getPlaceableElements()); } public EditorElementController getController() { return controller; } -// public JTabbedPane getTabbedPane() { -// return tabbedPane; -// } + public JTabbedPane getTabbedPane() { + return tabbedPane; + } + + public NonPlaceableElementPanel getNonPlaceableElementPanel() { + return nonPlaceableElementPanel; + } public PlaceableElementPanel getPlaceableElementPanel() { return placeableElementPanel; diff --git a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementPanel.java b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementPanel.java index 70826d25c..46198e226 100644 --- a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementPanel.java +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/ElementPanel.java @@ -20,7 +20,7 @@ public ElementPanel(ElementFrame eFrame) { setLayout(new WrapLayout()); } - public int setElements(List elements) { + public void setElements(List elements) { this.elements = elements; clearButtons(); @@ -38,7 +38,6 @@ public int setElements(List elements) { add(elementButtons[i]); } revalidate(); - return elements.size(); } protected void clearButtons() { diff --git a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/NonPlaceableElementPanel.java b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/NonPlaceableElementPanel.java index 796d1ae68..00b4f5379 100644 --- a/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/NonPlaceableElementPanel.java +++ b/src/main/java/edu/rpi/legup/ui/puzzleeditorui/elementsview/NonPlaceableElementPanel.java @@ -1,15 +1,15 @@ -//package edu.rpi.legup.ui.puzzleeditorui.elementsview; -// -//import javax.swing.*; -// -//public class NonPlaceableElementPanel extends ElementPanel { -// public NonPlaceableElementPanel(ElementFrame elementFrame) { -// super(elementFrame); -// this.icon = -// new ImageIcon( -// ClassLoader.getSystemClassLoader() -// .getResource("edu/rpi/legup/images/Legup/Direct Rules.gif")); -// this.name = "Non-Placeable Elements"; -// this.toolTip = "Non-Placeable Elements"; -// } -//} +package edu.rpi.legup.ui.puzzleeditorui.elementsview; + +import javax.swing.*; + +public class NonPlaceableElementPanel extends ElementPanel { + public NonPlaceableElementPanel(ElementFrame elementFrame) { + super(elementFrame); + this.icon = + new ImageIcon( + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/Direct Rules.gif")); + this.name = "Non-Placeable Elements"; + this.toolTip = "Non-Placeable Elements"; + } +} diff --git a/src/main/resources/edu/rpi/legup/images/Legup/Reset.png b/src/main/resources/edu/rpi/legup/images/Legup/Reset.png deleted file mode 100644 index fd6ffefa0fd001bd771519e6094c57919b51f2ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 662 zcmV;H0%`q;P)(^b8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10ryEnK~z{r?UqSO z#6TEE+hL&~;Jf$e3Vblb4i~dmL!9j@J(*YIms31lg-lTkxUpb^2)X4Dqu}o z(3TvO)t~@=^_R498?s&U9g}QguSKwN%OB=r+gBXy{GGua~04_B}z_KKR zEHb^>l?>b|fAYr)K>iIt23cf!!HVY|QguHK0Z_oZ)BYV~kVPihzlg73V_vdQ_T&W( z0q{#%^~i)k23cf!t%UB)ckXuV<3Z!!#7ckR+3A=7>n%f(}Xb zukwzDf*^}bvY8_yZ4LYfnVV0t0RBEy+a{^DD99p{Z03ka7VsfYDyO9hcPs+SBAYoP z(r&?_JI=Uc5zM-yWmlz^jVO((8&MW{x8gK{w_?cqFiOH$GRO*g7(t%LaU7c*$;!p5 zv=E;c5}*%#Z5$aj@z*OEa!woH(TBc_VQe8}y_CKWWh72gaC{&})GxDgXcg07*qoM6N<$f~1Tez5oCK diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/CompleteRowColumnDirectRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/CompleteRowColumnDirectRule.png index 27ccec97290109fe20d727d468465a57af330906..a74654d4301f75defc639727604f3d59f05612a3 100644 GIT binary patch literal 2835 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 W-$D%jr3*N)f}HK$?8r8Oq<;bxYC}%| literal 2174 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/OneOrZeroCaseRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/OneOrZeroCaseRule.png new file mode 100644 index 0000000000000000000000000000000000000000..73072f2ce5ea9d189fe55058aa6a4fa09ecc3979 GIT binary patch literal 5578 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/OneTileGapDirectRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/OneTileGapDirectRule.png new file mode 100644 index 0000000000000000000000000000000000000000..b68f67e441d3519f17daffd48da37f35e9b3bcfb GIT binary patch literal 2751 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/PreventTrioDirectRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/PreventTrioDirectRule.png deleted file mode 100644 index ed0701f26fc1880098ae5f07e8c309e113a63662..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1944 zcmbVN`8OK~7mZpHwfAFCu|!)MM6D$)v46IvRHJr+qKa0fG|ea_H4!9bx~SG(2{N&E zYFA5Yr$eJgYKyHXB2%?hFqwa$=iK|=x#zs|&bjaY@QALL?In(%JPrT=Bpe-V-S|E1 zKY~Q~RoTjSkKY6^ZuZuI$`9Zbz7PttLRtX;HIK!3fx>((8tveP;g6>LM**&q!5RPn zggM$;p|Jkzc|JE}Jro`uA!{37>~|r9APZ-ZkYR^jJj~5u1ZU-{BraT&_#+h20igi#ojLq7dHy0oD~tYL!BN$_r_vo-cOn49brRL&vy{vVf)Po z7N&!_In{5D5)3hihM3T~V*rMZxK-!EQ%EYTM^C(Bk+-+0XJS&%zTi$NFBhejmdg8< z(u_<@7~|vP$*U+iF`p01%VoJt#=^pBaVe>Rmns?>8tCiSZO6V#XGD2&!%2ENgr7uR zZb`{$cqu_n`4HX4aNPVoLo?U()DPRHHRap=W)fKJf2Kzs#H*Xf{pLcx!K`elc>gK_=h) z*X_;*sNAEJln(j^lsCc>$(NFdPMKGLH@e9LhRPn60!~HER@YU7I;R5r=PmVM!=0y4##7ZE9U^Wu-0znC_&muHHUjAzAQXOJCM_AEMqZeE2Y+(%t6i#S`(< zfIqX}TMg~BS^BzVD72TInCzocsf8^q;7Y${k(!#C7!r=oq{2#DTm4?;j&z9bew%PR zTc(`!*sOUL-{7+7ZOl6#d(0cRS{P9eOfd5nt09hfFR*WXq|vk*a9gwoUq_P8XqK_O z+d&U|@+2BZHnd^kGG23UY)FOu9S0Ef3lctvYgn)mQ6#*B0d0E6aCs4V@-x@#d*$~(tDbArirdEf-FmN9+EiHL^+TS#q z4OnBnu`iiQXjAQE45?78^Udxbo2b7{=Rh&Unb4hp&njTK%7E(d5Kael;O{x zk0T0P?t^L3r-A#3@ega`HG(l1OqT)u4^B$JyLhil9+kdODvQO6-u!~Z@^)*=&dKxM zdDcZui2UJE^~->3##d9+tb-({5eo~SYKqOw&DF`KEp?`d1>KB&RAGM7C0485w0g54 z;Q+_Qn#DlXeTG9_oGeV+sgVr^|$1%y0?IVpU7pwa0PK{ z9v+V`M(F9OQMFjLAz}+lOExw(__E*tp$)@wvlu-x-;DfRA@1{CzGdLl)KoNQ5}s8d zCo9{2u)kCE`n3)Hs&JghKOrZ^S>_2m)5z%-#hVck&y*>TNwV9(p( z;a5%)w7A%~yB5IgU?sGlTzh0#Xy}(`7L&Yv_@1#~^=8Fu*Id+@zjY<4t-FF3iW0u7 z8Qt|@%oJ4N77xJMl9K}TwIN4)*~g}}I~eZ3tQ5(*x)7|qPENV1@7$Fp=n|y>xROE~V^HjmF+LfbkfTMeYVVacM??MRn4*+C>B**8XCpF==@faf zMR1s0BEm&X;|D+3;W&H-q?+xNjEfxVeW@}iNuS-qg&?JnNMtu2C?&qxJ98`T|$CPGbez2o-NW+6xHbT#B6n+jq zJ=L^k&%&)j3dS@uU&iYTb#c) zcZIBvK-^kh7%jir&E;0pSBj|A?&a0hC&uQZpFSy?o152Cfdj;;FN08(-2($tO9ZN} z2v>a+6kYfKpESomi4U1`ge0B2$ApiGb?D!LLZP)(0y>h>sZ!rPjCYB+F0c#i(WWcP TsFd*d-xT0zciFbm8lC(vAL*4l diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/RepeatedRowColumnContradictionRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/RepeatedRowColumnContradictionRule.png deleted file mode 100644 index 5cc030a7671f6e567d56c9d71ddd640bd5a33336..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2124 zcmai0dpHy7AK#k$t&OG`5l(UtO|E4!*H&_gL#7RLmI>}({(;9>v(KoWh< z@)AFL^MfEF#9!U;*d%@ek}lbp1Ij)s%=3YO59%Tc04Prv-}4gWW6>Mu+(-bxk;K0O zn9g1!006?1XiJnc8Ml<@cP`si;eAxdTlw4PoMme-AL_3WIsbEx`aO5Z9S5;G;v)n& zyu8*yZ*W+w$t10wU^;F$H_Lsq5Vh_Z>5vOty! zH$*BKk?HSeL$8mISDVc46dI{4NC`KBgr3#M3R||!EiZ>i`FzZ$hL7v%85mgC21<|B zQfzF}6ejDtxPj8K)9K*XT==}4qB5QwpELTc`V6`Zybm({wV&ATmrn8^7$^pPWT+-}W<4rav_N-04uiopVBb`D!IC6EO@!pk9(TKYRcH{co<$T`mUOF)%prE2wwz&_NyoPge()RX^(*3lPC+#F* zt3^9_@<}7eCI!~G?ccBga~iLUV0mwRdSFr712gc4rOKU3V0n5c`y@)pHL+kQ50SV6yzwyg2mOQ6=NX!w7J+d_NHhp=#zB85< z+ZGfQ^r^qpJ*+i$(>8`o%h+BS?-UmhFp8J_!0X5}?CtHf9H|WO_OhkO`4^f-v~7*O z3)MM8@UVnh_+Z@J&{Qfl|I z(nBPL^C|PV{fqTPnVRz?|2dn}J|&s^_K=Y-D?_gn4cju=9ZeBd%7|vDHjv4iAMw^I=~I z1j5|}1Zq1ac(H%&GBR+?1XCNXZ!I{hwmWnDvCQ!{z_b9FNTapY=-Jr1XM!tGUj z6yh=l!|x2exzMM%WjS6HT7aZvsoB!Z0^Bc0j#oP5Kw6b=k6JFmJK-+vgFH}G{A(MF6^7^@DCQ4CadHYILqr2V zY^6_DY(4;&ZI;mR$rPgBK2cL$dcrKuvkM5s8UTUeDhtu{nM+ZFjbknSFFM6n1V6M^ z_-0^7?huTf!-`SZp+|;dSkaor?*Wavy9)yr{2?yzJNrm~B+A*9v*$NZ`NtnEWRn$1 zwQ3G0Bv;=rYvUDU95nN>;Q2BQ0S>t7m$PtY^9Qe-lSJ?pmRAl>tpv7eZLHM;>b~2o>t&zStShF+ zRLJ53$f}k_0t+&kY0uDm27`>QpV?1;V*2 z95`PwLykw*&51?soQ&8F3ML$k@3U7J!Ag|>iX|v diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/SaveBlockerDirectRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/SaveBlockerDirectRule.png deleted file mode 100644 index 7695ae2da1303c723d962d0da95bbb4fb9abee98..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2204 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/ThreeAdjacentContradictionRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/ThreeAdjacentContradictionRule.png new file mode 100644 index 0000000000000000000000000000000000000000..862408b63a9c6d72d815b79064ea587657bc5e5e GIT binary patch literal 2508 zcma)83p|@y7Ee`YLd#NAwMww9R6QD{q}w7D)x;~}Q4+LDWE9aRD2<}VdY5`@Cdjr$ zsd(1An4yAr^?auDb%+7wl`+eu$bI-ZId(Ziw|NW9M_SQ1e zhonIukPPY~5(`K+@b-Ye0={nIfiOTwQqH5C!N3y<_DuxFDj`;GAx^=5Ave9r1Q3xF z97NEg_>c(%66KrVkmcPSW`L32j*%sq;2q*0OoBN32N6Kdp#%u*Gz4*-3^6=uXaqTF zbjHvWXb_AY#0u$rWbU932qbkBg+w@q(*=_V@=TKwOIYe$UWan6V+SG&kZ({(I`Rby zS%`dL_q|fY>b!I3C71W4_u`ar}h@v4pK0QUV^9La|f%4u<^=c~2Ow|#Iq?KfBMVYT^ zjWv5!y=n-0S%27P_PXgKePXWhh#A2UD&F#3mz$+>`l2l_da}RnP)}bGyfofo%ViJTUOuytPATheT2gm8x^1TBlve|e#U@S_ zWJUXthgY>ErAGbW?AuC!E||;w)So{>NKEWi=auSv-#B|+QV}f8u)u)!0H={tw7A0n zNlWg*K%g<5(+GdAYeoGuU17N`PNL|2yxen~jbN{hyZ9b23>T^-WM zbURm&+-$2T4=);SjlrzWyK`t+OC3hW&#zMCC5zWr7y~NCXneJ7mDUpmsCjdR5!i@d9j4j3 zs_f$7Ba!@hwWEOY8!ooIToc+ms#$dF?j;lTbm$CchNpH#H^ISLSt^-gpBBw@ua z<(X54*XF`qvJmfDDx2yX;>~I^0}P7mZU(;npx3;H63Q-kD9X#G<(;8ZMALFUsW-?- ze_BaXj$o!;*X0JFb`6WM=PwPY20e|KsGj@sB^dV zzrjiZvFrB`CxM8N?|Q%)f(v=Xnw-wz>d|9m1&oS`YHCdV+G)Y}xbJ?76RYs3HKT05 zI#k1^nbVz1x^a+EoRHVyex&^ujAaV!iC-Y*K0xzDy4N%W1#6FmzaIMnuvYWrde=%7 zy_DyPwOEZfZknHFaO3#FbV%uIRNL@;CJV3hKCE>%FDVszF=~Hq45MO%zO*T9r@`6Y zPqm;YjEUJ-kP&H&3TOWS+8iJiwb%=q&=cK#+s$A$da!wyU*7z}PEU%x?)~TYUtHj0 zs%BAxM-c91aBXwMj(0X}-VUTTIdRFQ`&_aF!o9}&2|K-LJg<5sQnu)ge6|v0s83iW zm|+F!SYE1xU7%Brqn-9@M1s-D4b<3LvZY(Rlt-v%ipVRi=b?vw-pJfJqk-OvrOhr; ze3b#*D(19U`3;?VYLDqc%s!UFAlU-r1vU|wr`s6(Q8u#lbUisrjXw>(aL17aa9#8{ z&I>-CEXbNZGSm#!R8=(yHC=e$t-(4tGn@FzXt1K018@2lrzPiOj2r$pAO_F=Cgc1H z8XEqXiu&W*5f(v0M)-0!?!q$d@0yce5^OaTv-^OQbe6-?DN_%tck7{__jJYlYTaK*ZfEMipV;Cys58|Gl%#h^@%$aE-m7yH4NQ+JPAkQ6 zvE_h|Z3frqO{nhv>=J+Qu4LB#wR_vOTb@;%;>C3%%NasneS%yv{$2DsHP1u*C_I6p zpA7!G#|DTd^+qC$WeR^_Wal)2`+-Ll@{~PWVHZ+iW&QUFGW8Dk_o2%u$Y&xe4k#?_yKc7UweQ3_)RWjD(}SUUa{2X#QFp@fT z*x%8;JS?^rOR;D0HN28?fgM&TYH#lpt1%JT$ILSA8JA+CReX5#>*=I>Ps-W)Q23}l z#x-!l*7S4~L-dx2?PrF?K2SWr{$VJ0rYGNLsF|Dmc}DgvAO6>!l9bY+4#98YmCO^y z@AKE=L|!eGt%s!pn5wb~6~ujo!$kRwZ90u#54)hBXTyF~NFG8>iQGr*F@=xI%bT{$ zc!v{?#z7W7!dr>j>9bfML=UnR&{`Zv2dz-S#KV7H#lM&3{6O!%pf>Hhf0JpQ9jACH ztY`3-J%6)KkFgHU70AVgFS{dU3<%{mpGX+cv+n;uGC!IZ*`n}x&#E=>rv*Y;*&{2@ HUy1z(|GtR) literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/TrioContradictionRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/TrioContradictionRule.png deleted file mode 100644 index e292ec9faabb7c263093b94a3bacdaf56e0d05d7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2071 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 literal 1783 zcmeAS@N?(olHy`uVBq!ia0vp^sUXb31|&mjbk74R#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;>1s;*b3=FdEAk5hR^dnF^TaTxUV@O5Z+qtnBA?_l_`#aUNvW^_-pP<1p zal!872SaxB0&~Jselbu9a!kszQbh58x zsH{|SIl4hf`h*noN9E3&&+O0TWbclAe?~4Eue)&J^5x0K_8AWzG%HS-W@KyYJ9+YC ztJL$ix98tCKE-1t%^!q&MqQt9U44>ff7oK3=k3vJ2n+O zbc))NG12d4a72X1(W6JLp0ee9Xl&+hd!u%JYxebm!lZz0~kUwa{Az2VK> zyLWwidwZ>p%l&aQ%fGiK_*=mTM*ddDMJY8q>i$-#P4#*?MOs!?*6P`@UTO1^GrW9u z61q;3!5^mh`1-z_Vyn^rz**wy^IPoAXZE4GAJbzV8jEydcTJh58@(*}TY`Sl$w{gR zA6@#pv-o+&JsXv`2Od2;+|HkQv`bWLM}yU`-@jMx+js9~+rfj5t5&V*+QP4^tE)9n z^y!a}kH5USs_n8?LrZH>gw8Uv@@e|`SyQje0y_qaoM>TnM3QI zFZG`OqTT-SIkP_%KZ<`nJM(_xw}Tx*84Jo^Djz;}y-nligm4iayJs!z^VsHGbC}2b zV!h#J$NkySVn1#PxXOR(YU+A-XQ#8ce%zAJ&(B|;tnR<2@1lr^NPyAICEvc4y?Xt6 z@zHMa*VYF=ZdL5EJEO(bb8zEKVRgR?zrMZ>wXgp5<>i`3@=X3mPEXf=`R3+ktvwYV zo5XY?7QAOWe7$YOFN4(@0Yb(97cNx1TH@{Hb!Ch3R4>hBdzL!OWz*aFWP>uNFMFTu z98!DYcF?io$EONPESS(Xaee&$ZBiz&&)%@bOD}qLc+H{*%py}v6?m^+Tz<<&_nG+Z zKY~2_%v`RvN4i#ie#V=7V?(1^-klX}yDnY~ytOS?dTq?kAWc~oi^eL3Uun_HYjQhQ z@o(;85;b&?wJdVkE61F*ecLvz!%YqU#Mx{rJ~YglH7g?}#bxQmn&wAf$3$EI+FSbi z+Qol=e_s_kvMO{n)A#im%Vykve|PtEzj-#E?0hl-{*pXf4jyL%hhfqzJuQRq+0VM? zwA2lj7Xn+5g`9J^kF)=Qq-Kzq}zo9h|5B zX})XMu97poyUX9dnjtMyV-WR3_C<4({~E{TGj|?vcoi)7W7>0I;UN;(Qh;6}V9ikv z8fLv!+*;r4J5S;HhEESa|7HZ1FgO2BIo>b-_z}y!t)Y>TE6qA$4sd1L$Snuviq|sL z1v({{m-&LzbU~9)it9o9(n8kz35UWtPMtbc(kE-t(J*~M?%iFX&u=uF-@bbFDlnhR z)Eu~|-27eb`kCNqp35&=J#|Y@U*5OV@Ny8MOeknM_B1<}&7^kDrw@!~KMouurceMD zgA>+hL@X)c-SOnBEV>r&6` YvemvPddB(hz{-lj)78&qol`;+093R}SpWb4 diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/UniqueRowColumnDirectRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/UniqueRowColumnDirectRule.png deleted file mode 100644 index 7785d198c051b4d12db29f526f8cd33d2cd3dadd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3304 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 diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/WastedBlockerContradictionRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/WastedBlockerContradictionRule.png deleted file mode 100644 index 8813d79560f6a76fb82ef540090b7a0733a9cfe7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2006 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 Sv5EbE0GOFrq3V$CN&f<_R|ZM| diff --git a/src/main/resources/edu/rpi/legup/images/binary/tiles/NumberTile.png b/src/main/resources/edu/rpi/legup/images/binary/tiles/NumberTile.png deleted file mode 100644 index 41fb61dfa57c243c91f79eccd78abb00c0926052..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 370 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 diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/cases/NumberForCell.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/cases/NumberForCell.png index 307d0d754ce4ec8aecdfda71936ecb67fec0c3a7..fedbbbac856e3f5cd1679977ceb0685336bba8ac 100644 GIT binary patch delta 1825 zcmai#`9IT-1ILH3v*1TY^61bQna0;S7b1Ngq< zZyEeSeXx;k7e#cu+y!We?{)f3DY55&!IzRv&{nY`W>=`Qc-eWbdY4!751OcOEcFLm zGe=3HKAiu}q3e{ZJw-w-wzu!-@JP?@AmHSbF*@zGHPd}CvWhue@3qOS4ympFtVCrm z;$x_3pXERdy~;nvy1J9wP%br|5w@%iU4J zaHR2`c?7TYiuCu^Uk$;}OC8S#%-I7fOGRjrkA#R&gI|9bm@$h7$~w<^>MPaQFHNaXnQo%+|(FyY>ObI z{zXbru|xRz`B^$Rs63r%_Ol%Y2`nA9gmhp&OjLX2m%ZRSpZHq(rgFlQZ%jOq7>Ba0 zGA(szKya!i^k?2&J7n7c%Ncr@R3Z<*;G32HOr+4r!c0?8K4a-?&TaLOBdRWCG3w{W zq_)q!lb&lr1j&s4zE*734Dmt{a5LnHl;pQPZ>*%E@@L5LfybAGGIg)YP+Eqxo^L@T zb=z=5KjYMKb)1o(em%fq`Ev$xw9GMOx+!QS_pFAl5yq-G2r3C^5!_AC;@}Hf{_FPx z_u09GbRBa175#swAo?x~QNllGTt5w?MfdQ^2{n+$uw3bb`FDEz(ll~`)005gHUd}H zg6RQ9yY|U9%hNd~F>*Y7XQl;cDRyYM$MoO$Km(QS!!>YW>r&#{Po}<S1>J-Y%9!2Hc$sn zfZ$zK1qFq|)bd+yrXU`EvoI~{k6D%zi3uGvmG6p^%#Y}U(;?aA-doT~dzk0T1WDGu z1>DW^_m4TsY%5oscXmA1I?CmwRDe5vsW-(d(f01$+&mSt6+TMfW;!mk!gE+zLYMO1 z$KhXa_M-?vz_HndXhPiyw-58FlEXZ_sW()b6aiD z-sTr`E?pX?=hwz$zBJnGT`RMbLxi8R;SlzPm-kq?!AOanm>zhxesA*|5(b0O6N!AF zG<}BaPyv}9`p>w>lQrG1U`Rvgd(w??VH0hJ9Z<{2l0iqP5e-O|x1*=+`3yH4BCLJIY!K zP9Ij1tgcov19$Q`TV@iFk8)rR#X7&{Jp&$l817Sh)b7+0vvR|KJr!-?w>pFmxSq(e zA)(;G;F<}inv1CWLWPY9FMo*H)p*l4CTqPi3X6-2hey~B0Bq8QYpFdFYAgKdDp@dV zmB8TU>EN5+b%zOnU0o7!hW4YKfn)0hD}y@PuM|xw=#aK*ax-+ZzlG6s)p}vN)c7lD zO@_nS8xGgEDZy|4LL!l_pVM{eL>``bj_pkAl=tY^@2nEL+Ep`!z{sMy6^_H2ST`tc z$O{Vu|0elwOynZeV=3G+lAXJ}!EIP76EisOQs=M?pkA z&x#3oL{|9{nh{xO0bUkTcVDqLm2)U;Tx>Uw8Uo7te}RBslvXqLQ{A(gsfKW_5QI0j KxK(QC6!u@kxo!{u literal 1603 zcmb7^`!^E`0LRy9W+oZQP|8HvY?epdL++i~HS@?Lq~tN9mPaPC?ka_OMs8lo`?1tg z9+&beOj^ie@_yxUG4hDo^nYil8 z3NDVJfkUNe0li6&??biaP$>0bN@@$?js<3~f~N=znMh7G^-uZ6rk*|FcD08#A3WH( z{Cs6)MO%Ity7Crc1BG|~EMAoi6GKCb^z`)5;Na=m+EY3@4_#bbp0il--QC?emxbf? zHy;=p8C|1Nx$U~r((rQ6rjqGtlCi1j%^Tw(+usA}TN|25iHY3y_W0DLW8mDJ9Oj!h zhNnKFQ1z(l#N)q(bNT#A9>XPKiBF$cn_%wl-hHMq|45gitp0zX7)&P99*2ue7^mCD z4bITtdI-0(<$qwxB*7oDW4h>VQIfI?Upw3!yuH1((CDPmu`yOtQ$7R&anigEM0xz%j75pD8| zNAIgT#c&i60Sqi>qJiezB23&CuI=6MTY?>7RzQm+sX(w?Hd0&rLG1w<8iEtOz(OPE zcejO34i5Y=AIdM*x=8#d?1Rkg?BZv3`7b&k`^y{?rF!+OtgQzko^UuEZqGGpnm0Z! zBjUqT77O7=p_~q*#AGX+z^Uyw+MDk>Lf@DrQYQM#r!+J};c>%7zFwWpM$|q%11*BH zb9ykM5Ynqh?c2oPcvQ$Co2D*6DlsTHF51pQ6K_D#d?2NYu z%R-`F6%D^wnD5yLugBBXf<@MwzKxs_F>29%FfR?ONJu1)41ZD{WFBa@>6Q%Z90vdG>=;6u-Oq-CBA?LQDzfh2*))o%>R}J zUBP><2#M0QI$BC&>$EcMNn0tQ>Xj|`=t;h+j`#qS`=7vZnLm%KnLsu- WkIb#St||U~0D$=WIeZz;EAn4d&G7yJ diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/DuplicateNumber.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/DuplicateNumber.png index 6ab3cd769669c4269dfbed60c311fcd7629d644f..7ba489c2bb1b9dbdf7669a1f69c06abfc111e1a0 100644 GIT binary patch delta 930 zcmV;T16};b54r~-iBL{Q4GJ0x0000DNk~Le0001F0000Q2nGNE071(6<&hyLe9qmNt0sJv`SJzp%fDk_2NZ^qDT!nTBJx? zdQqsL(1Vxs(5s+Ak?Of1v3L|Mr1UEEALv2+ps9g0Vne#6Nz=61&Bw0uPPU-XgOu4Y z+C1>G%M3faJI{P3t7I}63l5+te+oZ7j>n;Pb@Owl=yEC0YjreKmz^RW(=u_Y!;Kd+ zGboixcB*(B6=Ikso{rwdo2!?AYMs56Y3B-poO?W&Ui*oMH?L!0Jq1|obwt0Bi-~rt zxb-=U56L8U3I#huJPr%7@MJuOx0lbOM7^ZjFHQ>ez(xc9%^c$E8tx5Ve?vI4irU%! zW(ATM5l~s$Cj4~+k<|^nUs(7Lgj^Uv@y;mB+amxQT7=+oL(%d$IrRu}ZNDkx>k9N& zuVDpGGg*S}!-gsM%jfWa`-Dh(({UlHeRLe9Y5)}-N_kgEpv=bc{XM(%-=H*>ci1&Y z2~=x9okfdinUFSrM_Y(we;ivO2g`EVkWPr(?S|jaR$~r9<@fgX@}rDWq!FU3DtdZ) zplKQ=CMI}=iN@>oBArfSa&i)ZK!A&p4Jm|>3=%OrJBx5QjQRO_bar+MZNV`vGNcva#H34Q(rSz5E6e^hShl^0G(xDNXu{pv+Ja#i$mjDy z_aX;N>qH^}kH>>rt;Uzv^kU}$KF?-%;|`h+frsVrUBQ79C6 z(78-92x*2as+elE$}5S2hOJiYO=LqFA#IwpOo&AjC!4q}RZQ^zx(}OQC{6TzS~%&q zMu@6FVMCBoEI=t0#l#Ma-O`^@*g2)~yu+?JO0Z|9k13dyc;-DE#_^9Fo{f(papfYg zw+|S)oht}xy=;{<{e1%uZVV!nT>~l=y8;Drq6_eLAo}G8-u=s8oix})eSc&G(f%`B zjuUuT!(Ay?a4r~tZ*Lc-;!LOa!otV0js+&9=$H`T7Zs%k0Plyy0{{R307*qoM6N<$ Ef<;BAh5!Hn literal 1991 zcmb`IX*e6`7RO_zwl=L0OSD{jFtnAZp{O>oM2Ib!d#83%5o?1`s+OT>?M!OlO1YBh zjH0#*?X4t&Xc$5Xy&bhz?RrCKJ8|#*bm!yT59dA4dH&};&-0%5Ilq(X>Wn&l^6W_f z0B{;@Z-W*2z+;n=5S^FbF5pE@jEqGg0Zm+$RZ(z)h;Tvx04@2aj{L<%v2=vJI~f2_ zy?1P4`HR~G06@|UZG-qO&S$-HI#l6%6}GLKJj)wlD{s2>Qb{QxTH{}~gV>64Xn0WQ zBlQ^TNVcTxiI*saZMO$Vgdt*2MoPQ%62jUB<0TjCMxHOWbxNulx!fB;Im9(RGp2Y7 z&+YqRxC?^bWn=Qv>|mvZ;S_TK%_E()&i_*P(@zc?|LisBy!j@psJK{>*3?vdbd*ps z$E~smq3hK`+Xt^H!h_fA(w^&DmqU`aR>yU3fBBsFbHe@>g3ssIQ>g=l{r&An{0k0L zbU3^q9pfRowmDb;l?c5QL?Y!Eb&}q8Yqd2uj}qbp8&&_C=ZD7{!A?T^Z{X!IIjNc3 zr4VuOBR{+))b1ryuN0D>>GuDTqdb5vJ-bFGweRZbbWaaYPw&Qp+QvpyxRMU9{#)e% zB=i^W2cv=jdoYf?Ufy;kUi-XYgXOSxR}$;#dr@3m+={`t_ur1VdU~!cL@Y4}?>IUPjoIXeP8pO(&6h^zgTj5Y=;c^ zVmJ80WJA&{QJeH9w(Db;k*Rp)krAvcqv|x;5Ti(J$&S3xb-4J2+Z2X%^%v z2Sem$snwfO4}!+?vHP#imel406(R%mVoVAd)8l7>pOQfgyebpdKt?#&K9OqUjAK%olYaTzcr>-Zx-8Np~rYRsP7yLNJ!X+q;7DJJk6 zYkR!Ui=_4U)+p3H{*q(bHyO_A_9W61%jC~qKFhM(fh+}s`r2BAxrN0<;@ALIRNxxi zGEw-QbCtP{!S_uM`upt!n(hCVB*kOvE_!z_Y{Z=R>T*>)@1ec8xY*>oq7{BM*OdvX z9P!fE*DoY+iU*mQU;@7_80+i1Y3?win`_7_NI7(5u~@CqtJpFl|J32i%eb4fUE48| zY;~4;x;Z;fmD1V@r#*g5(L&`7@Mt&!kLuQXdp9t)LT9|zL>{IhJ^T7u)MD1pzAt>B zF4czQrQt{-rV?LXQL(tQ$l1s8Mlm=<4W zf$nFk&QxEy6ENEq*^-Su(-=0>Ns3i|Cr?SM&4~}wb*?O!eoEvV>@HV=PbF1JFKW&U6-Nv6ORePy;XSpUXXixND*B^(|LxZ4uuwhm;i>NCk{s$t}t~8d{w^4 ztsWY3C~=t~{zu)4r@WR6^gk4AmY^xnXMn5#oK}>Dq4^u4+!u4jq6gr5=SDwB< z7vs|#|JT1`$~5V>)}vytPIYaK*APdlU-ex43MotJ`E8pzBI+w)VW4m$HFj^s&9reO zXrsCT0`H02?n`{X_Kub9Es@8YXXn!`#FUAUdPv83qYJ&9aZe_e1Q%?)F>ow4EW9fz z(+%`B*3Qt(!0j)+s)$+{)tK%GuQ=6qM{rZ{@fmp;PFE;wfcQu;22#G3oIQlVBNl#~ z>q|dX7UQ6ZH#358%|fXo(PexL0P^lYwamFlLmnot4Y8Kc_#t<^>?^)iaDaXW&?{Q` zLhF!`J#5hGw_b-R$4Tx=nCdk>JW|6ER$Gu>+6;ryj*)phVnfNOoGV);75ERb)3e|k zGDXWP){7lLEmVpBR9}ONcUOSpr>JO}+Wfgr)2=O+bmyh8$PY@2Y&aKYJQ@geL+a&0 z%+rBhQth|gkWL|#6vKcqzU?59NPzY;_ZZB$V?c|h_%FwR7SWo03}|uiJWRxI5v~8U zjC9WSPn4+PAn%&Z`G;8b7ZRib{KWDn|FBsyY>kfqp^<&EL+rffZ_|L5+fD7+av|6X tV1Ncil)iE&X70yAGWG4Hj0gTZGHWRGnasA}Wzq5lplzLPnvi~}zXNXnt#JSV diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/ExceedingVisibility.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/ExceedingVisibility.png index 2f5b5237d1799d52b9256e3a8479912423bd4fad..ff999f4bc3b1e829029189e50cc9104fe20b0280 100644 GIT binary patch delta 1141 zcmV-*1d98e51XH+LoU2%NN8$*XJ-ey zySx0e)06AJaAD6$jP><(ba!{7p`iiEWD>GWDVaEE#x5@}qqDOUO-)URL?Tv=7)W5l zFi=%hh4%J#w6(RNx3?EHH8s|MH{8R8g5+{J-huu7ef~cQBodmXM@L8K>+8ez_O>JL zBoqoE7K>qyg|1s$TWD@>#>&bH78Vu|3gp)tq=|Q3PN$_koi(;{Vh=YRz?iMvo zV~M@s&%$HvC6xw(mSI?bQCiT{lt1i9Uzp&`DPG&VL04^Bi-wjg$h z=*Cd79B7}Kot@QBW@cu%IHFb~4LXIpQ&Urzn3%xy^fboD$I;W%Km3UKjjUCBNqhl# ze)tZNjc=e8!&p^dOE|5L!VqaAsKC2;A8KL`S~7ig-l_9jCnfek)Iix#kio*)zi*x7 z&cxebqQEC#om(gWzL#Y>a4(C_h3$J;MZrOUji;jbvVgw;Kv7WAojSH}00000NkvXX Hu0mjfdweZ6 literal 1949 zcmb7FX*3%M5DsF+y{M+G(uT0QbuAiqt8p~8gs#?6>WCw{;)n!|I=1d3j*4|uuu>LP zM7DKQX`N|ToZE(|V=HY4X+`M=82zxToEMIIWvkJ8FWaTMq3!ib3MEr_lTzRYN-$`H zXLB4L*oGJIYxkt;X0Q{fQrt{7P- zC#U+xMjQ3J)`r~fnY)OAqWt$z%58+Dnu#F>s5yXYC9ZYKa{PN7*9cjdYMWw%tC?Q~ zntJLU5Y`v0mp&5D>U&b)XPWE>T><7Ns%Kkk2f~suX)n74#KT~r;?170v0Jm$o%1AyEnH&lyfHrSjO5BhS;W_&eY&3?z zM|Y1-&Mo9~WTWfP%Ag_%vW0d5cl_%nItFi^6mQa9-ha=xf)9235(Vf#N7j6nmwDD> zUy^vzpV=SWe@OTgxB2WE7qm{@CRgQKJA?b)z!q0~AUbqA);a%<_Z@cw1Lf0}L1A_u$) z1VWD+YF`hRt7^e48jI-Za#(tgts$E!^^wLzRn4M+UhUz7cf=$fd2nJ9 z+1~&SM_{q7$7x$UR?5~^4EaLR{3E136@>{aT8-6PER5UVSuZt0%V{CWnF2wS&h-!KZEMh6Owc=ed9|Ts&J+5+WzXX+KimWhCtE^PR zEc6^an>u(d=sXiJno3>u?TKgM*_xM&OWj_$alvw8fi*9qprsjBVQcfCi(W@aLA)&%$TEW>H4Iw$;T#XF83 zCHMBFNsmZ1H#QFI98uF8_|p9_)~A1SMt}hejl0gQb&Hl%?WC4Ki8MNtR zv$Nr>M$pgczN4)7{r857(_)?Y5o4c?)CHV==wEe+;v#d>Ooi8JuJ_aDj{Wunq;2b- zD|XsD2Wb4cgze|u4=0x{lcV+z35g{*=rBUEXgD2r;cg_reQYb3>%-Le4cLZqGrlha zrY0BR4|bzTB;^q(8;e*%RpJd=rs8jwmdK`k?r?blWhe5a{WVPqX%(5~!&dFRniQiL zv@*veiEcynkuM{&Ci~Tr7cSr5gM{%`Z693nZ@_b`63|!EY@$h5#{<3$#!v?RV%>sYlWR1Jp>PY!uGs!f5mV$~$ z)hng-V~VXJ-`(0&5<{3PcI-b=)~c$d&5eymPW~7e#2RLzH8331G^0c}Do&L`Bz~ns zL_^|vY4W8kP`J)|YqjHlUbue(oquzHbF^8`vyaNOIb!jzXME+<|CEXI^|K;tS?BBK RNkU5nIM^X=o8f-R{{UTqkxT#p diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/InsufficientVisibility.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/InsufficientVisibility.png index 285451ba1668936a8a70b3cce7f8dda36ea8e791..cfe04a503d478bcbf64d4484aa0cb57583efb426 100644 GIT binary patch delta 1270 zcmV5Q_>SiBL{Q4GJ0x0000DNk~Le0001F0000P2nGNE0LNa7OOYWbeDaS%kSh2Z}i=7Ao}`@^`b zfFR-&Qy6hZBYX&fgCQ1&e~?T_6-%VV!Ac(IEdQ)V{bc=B5ak7V$y0eLh|0LIf(R6_ zRfy^Yp@A|O3@9xv6*p1*yGQ3Ds@rHZvd@Bo0x5?dHk*yrO=dQmrBr#SN)QqwBW{Nm z7Z*`gRRyot%Lb4aR97$J4=aTTSktPCwJEwETDf6OK)Cs~?YR42&B z#s+3*XW2mM^?E6X4<#ifXm4*vZ*MR9`ucEmbcFi)dYDWmsl@X^UN<;6$m$yz8DVMD z)6-a6TSJk|TMAVPk`~V<4u=EP)z#dWD-xSLZh3hb{r&xzo0~&ZQxhVQh*aYFNhA_* zI-OWuUB%eg7@Jr`e;^Q$9kWy=NS1uu712^iGuLXhA`}X-G#Wti7|P>8DwRTAT^;;> zKWiiV9BplFtSvdHQjmN>15aK^-j_@!+44y7>hMrJ0CIvvAt<0vO-*5GX^FKZSM&=d zNEQmVw6yH(>@ab7ZX5`ze`aO|U0q%1=;)AA<#FKzp_xnTfBWd@C|km|wzlAQyVjcUsbK+~^5mc@L8#;F>+7)F?HC#w!ph1D_V@R>h(dKA92{VK zdmAp73&X?1f9#WXTr_Yz1Su{qX4~BE?r!w-^f2q}>_ls8E8_9ERO;y{fBpXXm&g?B zAw1#woepXAU<7#y?M^5V=Ys0d-dTp_=ez%1zh8SBsZRzV{Yj9QO9Y3$YQbBe-p(x546+C)_vM|LgyOV@}9P5 zd4HnH7;=$OUJwdyqDyBu7^~-Oe4!KZnR4efs)zMgL6j9ltApV4LhyR{*q?ik-|S^Y z(THLs;t)I!a+g1k{pDl*Rtm*}y5^e&ynp#silBV|6+)MGVjHa~1Mcdgv=e|Be56bc zkcXH`Oz_ox56b&*ycmCSxF;Tz7CxKAYgP+5O7Sl=fB(n`ctgnG@x=vS$p1WnAZ|>2 gifeCP$)g~^KlLY+KFX1xy8r+H07*qoM6N<$f-iqn&;S4c literal 2071 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 SQ+B7!0giUA=mwi$%6|cppUP(d diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/PreemptiveVisibility.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/PreemptiveVisibility.png index 6c4a6e4574f8154ce20de311be3f35f38bc1bb09..6d330baa6e76585ea860dd7d758c51d841081675 100644 GIT binary patch delta 1079 zcmV-71jzgN4b2E4iBL{Q4GJ0x0000DNk~Le0001F0000P2nGNE0LNa7OOYWbe z4l0%+v`7>PdNofT0{e&BS7{z-nbMZX82` z&`3#1NyyF3mGQV4MoUOYe?VqtCQihe;v$NYm6av0ySuy6PDTk?5E_Ru{^;nad}v=^ zA5v3O^;R{+$H!xSejc^8wPq>D=U$movpWqM#zG2 z2|ArlxLht7`q}Mvy+sYD{d46lE-o4#$a#|}4yZ#zLvpfCPEH~xeP=x&Na z!(-G-B$5huuLsCyAZUWV*jTU!_z8G*y$!1D64%zve%r092? zjUhppXydm5;`MrEn5q(P=3MsNM-C1S^xBI7H*dasMMVYX=H^gRQiAI0YV7RnNV|nf z$b!&VD=RD5-rknq1sLWnEG!s$*x%rQHa9o1w6rAsFJt(bf0-HBY&QLl$cB@1e0&`3 z?d|C4>5-+QqXVt2t%fqhhSKj8h0Bsx`bI_%XGKLnrQfe!gTMCyH21#V`ZE-T1Ea|7 zpP}UGiu?^8RK!#gyRSmLoSQa0$*rxe6>&O&^~`sP@G}|bV_le ziLiZBf01|u1cfLbg`Zt1vgrT8+jTTQc`i3~v;I0^!Z2!na=6>u3!ifx7P=Y5ScN8E xJYHnv=fkE)e=4CM+?=bbs?gHXVzMB>cPIA=MhldS4QK!W002ovPDHLkV1nN70}ucJ literal 1784 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 JPDHLkV1nhw1L^<( literal 2108 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 literal 2658 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 SyFCB^3IG5}MNUMnLSTaQ;veGx literal 1783 zcma)-dpy(oAIHChY1v7+Qs07&&2XU~!#bzd17^ zyz$lMX%al5U9@s{$&e@MLg5nD%kGxQ21}k2S3W}2)I2M4I*nvVrJvBM9?#S>ubZf9 z!PG6YjFD>YKkHoagDO@0uL8>Y#U82`{no9k%gb0Ic3b&y)X*OGB8^k#M!wGDA>>D~ z=u8h2tB^K_t%>C~E7Ps2**)Tp*C=Zrg6E(YcKnr~zt}eF8|{NZ7xPW&Il7$1Zq%Ra zUq)wU^54CSu{!?{u(h=nH`QvWD!qUI26o3O5e$JqYEt97wW7#+g-Po& zpIOcBEPo&?Jwg~@C#~p+5RO4@Mqb|jKVUsV2@$qwkC63)$t>depnV>C|D-}Q04H~3;Kqi4H zA8D13s@3Qh_otb=7SF$GPic0@?Q^;w#xMh-jUP+L`C+sAny?qgv^fX4k=o zY;2@zYHKrf7R~v|3Ily-gWTOSB)5@nh1X8bUS3@&`7~E83!P7?)w$#ex_`Qyo3Raq zpW{MIZ`=kGFFU%r#yxM28a51S!M~ZRw#?|Y?F$YLz8D|RZBJP*C@K<8rXik%(FTU7 z*48`sc5}#I@W1!(LY_Ta(a{m=dF_JR_0j2Rs$^AIb^ku-hvs-V4%hH4sRreZ(Jf^% z@$YBd4ct<7oPS*Itn#KA7=g%({j=0Trd=$4{<#)*s!^G=^15rN^ypCI!50rn1W6lV z&I^e|7W?^yhSJ`?MF*}QwMX^fX4-TE8$#?vNt@C{qKwg~!?I#LV6l2D;dy@N&S6&S z2L=Y9ZhQt}0rhA2q|47_;;XOuL8jN2$d+3mqw*AL{Q@mlze&jjq`LxrXB2R-r$3{@Xg027wRt!+K*;{*}i76VI1+&L$9n>JTf zZA(l{T$pC$?Xhvx>XQYQ&Zz(g=v`MsP_nSH7tDK?Sfy9D?c$J5vK?wks#7M;ou>MCePavS0pQg0az5 zFTEtj@Q6>SUe54Z^f#|v(LI_)*?kXza_vnw<&Y+ed7V{Kk4Q^7r uBxFNaxfLzmfoJ}e-v7Aszr05STM7qa6PBN;faISP03hJLp7kDKSN;N6UP^la diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/LastCell.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/LastCell.png index 8db8a3bbfd06e118b5052015f9e885c05aed2992..216a0f3a1209d7b126eb41f358c417c5a999e974 100644 GIT binary patch delta 1332 zcmV-41|mts*Ab^QPjnZ7DQ1*v_7C(R9qC=ii_f+b_5p|K@ePsR&-?-4)GC9e5U!%TtT{M zJGsYe(%fG-Bsmu^*ZbY`KmS(@fBXCUK7jR^8#pGD3H9~$=vbAxVX4bJ$N3TPc~sCe@G~x3-UJ% z27?$+g)Ybg?!6{%*s-y(%B;5Nf+$doxw$zZ21V)Z?R^qabV2;kL@_?u*x117=_#tJ zs)R@8^?EfoU%DWE=q)jD?Ca}8OiTX*HJ3A}VEkRkrWrnn+r3JOMwNg?J zX0rv~jed`l=Rd;djliA%6(|P(E6Qxd^$%HaeDMly`GaG3L&pPxs6e}C`_;&eJO zK0Yp`vxA8}$5$QrfAi%xNb=;uW_;f-$-BFu6oX>^VS0@>&I0`W?XtA%dyJa8bU{W& zMx;D3c6%(GEuA>%?7`Q@1$=D!6xRVO3SgY#!gs8kDfrRd-XKGBLFk>(fBw=dlLcMS{|lN4LZ6+U z$>ijus8=&jq*fHh7V+`%I5;>E@BAL>XeJ1sVhWLzlq7uj^z?MZ#lR@&90usABV$%ii!%<)zyh25Yya&!cyzZ%nXi>jQTF*sXa zUl)RhI+_SVF%}mWk&%%hHr<__9i*nFA}1$D)o(TEe_7G=6%-VRlnwVNW(-0ZO$4D` z&(F_CLP7#kQc|$Fxrwc(A;%(bqU9Sri$+!9UW5hN>71idk96jZy;d*tc-4shfN6_p)Ll|Oc0&_Mo0(} zW3a($e~f|65FfyZg~jlma;D(tQ3_NtDu&E^Ih#9i_^KO8-j856eh^@R$|(MB*R+f` zjzau2x-9Mbg1SIO4~K4NZf>rWXqm3^ufM#8=h^S@UB11kfnz|FDFW{05AoS2b?{tX qDD#2C48iK^Dh37yB$jZ2zW@@ncE+S`%bx%M3IG5}MNUMnLSTa4MR`R4 literal 2174 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 Y7pr?>TTCw0P5=M^07*qoM6N<$f|9&w@Bjb+ literal 2083 zcmai#cTm%58poqj1Oj9UNRSAEAv7sJ6(a~lrA9)FxEQJgkj_Fxnusga&;vn|qlt^6 zEXXPngA90>A_QeskRC+{A~8fnrGuC3&CSi;?aco1zVAHm%scPXzMqHW;pU_$zgr#z z0x4pg?Q!DsoMi2k5$~37`2_K??L5xu1n6!*d`@ggg&lW24g%dPR@exV7Ta=h&iL~n z(4Jh$+EzUCITQqvWnk=&dnE_VS7E|c@vxs?=Ocmm;6cWfNg{+T?Fn&DH3#yq%TR#S z14glXO|PnV_erT*8{E)Wb2qKf*rA2Io&o4gU)R;eAmjjTzqHUYzY!uj)M)oj3d>m>%Z<)5a zb~;8K!9pCiEK#tGn9s=S($5Vb{N|WB+!Cl?p7iNW`@RPZ#>mXV*5<`F{}M>q2>+NV zzrIaDWvvE|#i}A5yIILpm^Ad4vjl5wK?cdw9MFm|dH=i;_3Guzx~*n3w&2NG_TH)a z@rNRr`I)|oMZs(1Hvh}wH;)OBpG(|pZF}?8&G=8U74ApAHcHu8iJ$nyWErfeG1ChR z3-3n^An{#k?FE`Bw_9%2M#|c@lwxgk8D^Vwbhj{2r1tZ87EFd*qVQJj@Ppvt7Wx;l1bZELe5ZGGwaW?x=K^3fql zm}SnAw0$-QW9vP2zylVNcq#J_L>x9L!H;sz0#R$1AqtJzT@G4VA` zCMOTr{LwHsW?+ZD+9;)xG`LUniiS2aEO0oov2Ub)Z_9F-0zfLPTDNI0DZ3U`@?tsi zoyxRk@j(Uz3FR!$)}2e@$F4?|Qa0D~qZMYIBMeTRWNsUP2KycvXCn@WxiRjH&eYHB zMtto|E$klic+Q2*{=j_c;=~Kzblr+=p$O>c?b$GtPhY67!Be&)*3SQzK1`p5GmiKO zZgT5T)ysjP%XtZT2^SJ%<$)3=Eto*~V1jI&4d3Ejs2Z{*LoyFG9HN902mqjkM9M=4 zl#v`J%bq+$SV^w1U>$o`GIR^AqLafQ!>90BWIEx@fP>Q^6v`zk>eqEz<;~>0Tp8mW z_>m(%K0XryYVr`}W;IMIt|9fydsjn4Lvs81S?lyP<>keAIvs88>qC#G6~KTK z^jMz2_V=AQu$?+{hFfktBOGlNJ@aS8)nRD|95_XLKGr~#5xEakpjS}+$^D6<2}_#o zrANPJ)-$JQQY$vWW8>qzQEmx0aeXP~Vd83oy4_-0_MnK@!OMtc!EMt<%HIM@$_d*i zoxm^V$O<;Cfaym5E)$~NcyF0L4-fAUhi8~m6y*GFKfW`yjsO1rqc$cu!o9{?VFSe5`ln;Vvr3fvE!RpU9*>WmH}5;$wscRdxiF+A z6w-f9Ac#a|YxQVUUL(L9b%#}~rdTBCf%J`J6eqBqsAov{ke1UV7B6FTSj7EiqaySn7ar7>+Ff;r0m1--r(diR2-HG1> zr%|WuO6WgjJy2m*hRU!kvKM8G&K%BB3CLYjHQgHP+MUk+O&h1Q#|m*Ute>*asfba# zEF9gJl9F=wzR>BD_(Km-+}tZY?{15>OS@vCnZnqudq^^2_kfl*J)Oc=et@=jK~Pq?J>O+hwaXI;DkN=y z^n~>_Q^F5VDm7gU-|ssB`lOj2dqiav2GDO$K+A~v+-C4Thd%SBI70`8il#v`2bWFQ z+hj_2wi#_9Jhy1_wvH?(srk=%59s8W_ba|`D%h9frXy>bC7vWY$a}q4{CY4^>Ig$i z|HA;%k(is#u$WnmfbOA>U= c2J-v1plu?8L5$z3c(8*o4sP~$Pn^y87ys4PegFUf diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/NEdge.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/NEdge.png index b238b36aa4cd9478d1c76172df4d667b0d9ec138..d3dd36bbc10fb66ad83d8c04d0214e5f25836739 100644 GIT binary patch delta 1504 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 literal 1455 zcmb7^{WsGK9LHC*EhC&n$kW#qWfhAwb1`NQ*V7nsn=7$# zq%BM7x|kLbLfLZlFpE?hnO$5V72Wk8)H$#B=Y8J4e13bsi-|!vJsmS05D274z+*x+ z9{WvDu;!Mb#2k%nN)N^PfP_zWz1IZocyu5d1iD=Y`*M7XCT~r}N2Y^778ky0Q<-8h z4g}hQAz;v9EULO1g@?x*^?z*|7v1qBDEojrR>jr2X{=C=>nF3p^fMPaE_%fX?cv6Y^lb98YA z_vHK{%gNQ1JuyMO%;DHrmL0>5_N$hGow~HNG(%rs-`Umlr<9Nb-pJyXmV@ znSHz~ku|ldq!;Be8?vNF-Vcq89NmGW(P$4s;!3RzMIuq7PdR zxVCtq)WnX}X_@nthE0Z?n})K5W343e9+9P$6>9HOuhycqwM<4v2Cu%pH|^!}3UFQ; zEylbXom_|+w6@7`H(# zu-mhq5!S8RM_3S||JiLoPXU-Q`$Pu@3n7zl{?2!=WJ*Uuj~*iuYdaREtr75qxYuK2 zJCsVLlZVGewL00ZcYkDLWLsxv@9gYeFc_T5VAMCv+%PcR{b5_f*8agk9X_AmeD`jl zSnMgEm=IHtSN8w3`tF^!t*vbYm6|&q8ft?GNNBfTR=K1LnwtFL;_Sc>NO@INVzx03 z7?;T;a{0}bnu94ty09#J(sKk7S^W5M;G9~0Bjr4`MT?TtFApU2R8&-Rb93JEGIM#xn?$NmD>gi9^ie3(Ug)3UA%Ae_1}3Jv8QtdQ=FXm;rR(cw z7z{>CQqr&#=l&03=FO-}W^%F~lgXqLgV_U4#SdwGZOP}H zTFWyCU%O=C@8+Pd@Xr_yYRh+wdmd1*!Af&5y0dbX+Y~1;;|5@+GZPa{!`699e}5G- y{6V9!u!Y~C84=p&=*`;G14$)@z_}knuj~Hy*%ZJ2YL8sa$btxdK^UP=Ou_$bjIS#I diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/tiles/UnknownTile.png b/src/main/resources/edu/rpi/legup/images/starbattle/UnknownTile.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/sudoku/tiles/UnknownTile.png rename to src/main/resources/edu/rpi/legup/images/starbattle/UnknownTile.png diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/black.gif b/src/main/resources/edu/rpi/legup/images/starbattle/black.gif new file mode 100644 index 0000000000000000000000000000000000000000..13381a7179c2af7ad23ef064b743d3e479b56070 GIT binary patch literal 856 zcmZ?wbhEHbRA5kG_|Cv^;=~DtQ7{?;BQ*pRf3kqRt^*=Ld4hq%l;J<8jK_ur2b(#B zwPH?eSa`TyK-p`K$HqlRyCsaX?wr`T_;|m9bC-TP$)=GAEd9Z!$IhTga=X3G@YQ3og4-cz$S+!E(AlylPGv%5MYB7 z?L(weOf)n^06(eCC=@c@=NEfWh%S0_hPtstm*X@DjST^Tb@evP0s!DY@GvuM$cFf1 z6DM5ortm`|;5T<7p~)dQ=Wr6vM8gpmLZp&0xJ`zQML$U_$T&0whehb?=|gn%I6r+j z=-*Tud;DHtMWa|_5zxJQK4>%)tAm9=jCCN!IE;>uA=F352cz$c(f7eXF}UAEKbHKH zX1@=I#Zcc+-^dUOhJcNshI{pX?Yyb{C*GMz#rSWyg80q*du|g82W>=`fd3JcP2p!$ zei8o&*$?2~((>P@3XA!bCK8oQ*en4o281Kv0&zi8v(Gk)kE7uDW$@E4js!EKQv8WzgcF*K_6OMFgWwyOzh*gAzZv`gDZZSx z{1xDT7(qjm{?-8;7;Lk0iDYLYk$|v32Zf^HpnsqKkEZ;vj?>tj76<)02spy8qXZYk zIX}ppgTknlX351RmSk;d=1j|nsOeDxk}JOX_SVHJ(R6X} zqvLH$^=m{gTZ8ARC&zO~s;{3gxp}wrk1?0kWfvE>#uEXi>njExY8r@5Dv<1_-$)uLKVNF@5}bH(M+8F=Z3_h721w0oA z&v@7WFzJbJtwX6@!Emc;v@?>6kI4=AK0i_HRB9L8NdEjN`Mk&s@|bt@sv!^v+zn`o z8SiT>v@1lEOIl!dvv|WNKHhcX2J`YUWo#Ufa#H-3-2dC4?u)cnn`S>-{+dVR(5N@X zqWXH{1tr_x*ZRG0k(I}|?nnc506;x_Qxl02Z6+DW$%VDm?@n|&Onj?ejDW)?ZGvEX&(lD0dl>-~)GzkM{E*(gfnO^t~LoW27p*5y3=3th{Xu4>GbtP)Zp~D?x*|eZ!Zaq_U(w} zh`X;48&}VHTY7L8-?qPG%sv49%nd9-PMuchD_;H-FS+RH-q11C+w@00Tt@Si?6Ns= z_zd|HTt|WNI+=S?Ybw)Pjy0NY$kA<~TSTAb9n7o@_f(gX6N)-KHU8kky}%Fk!RRE3 z?I%VwOTn72BwPQ&;pLIbb7QUP{(qia@C_eiy0OhY4F|;d`RQh+*R|QF*2;MpY_ahV zw?HQa>+v0@jt1G=V>|;p7yHgvRuki+{<4LIsB+=TUlvz3BAK00H4V~|G~gF; z6;3-UyJR6b%=_o1!UXQvHO&JdE9o!ldD4yu=>k@d@03nOJq4ZZD6&ZMUs;@HcL!8f zKO0ge-YdEr{Q2P%PUl>mY+Sz>S)8$}$FL|#(MJEV8Sqr6!$8VPksv#>qUbq}>wraXNF@mp3;2b4APzAj~+=w-~Df^{EaBJ=p13D4q26Q)ELOOLpWn zC1Pc|m9jL0TfGjIlfoDnMl6!-QUZW@YqdfC3I3OY-;vg@9h*zUfv>jPdfyG5U?V!Z zA`HbD{-INaq75bmYFCgfKXbAztLD_IUm61(bgAr>CLS*{RLIP=a;xwH^IJ;bIs{8X zy6e#RvoO~lp=e3ndFlBo8Lgw5!Ku>5A6U*W_W_}a_cd2`(2-M<+qSZ-)D#HKu6x%d z4NR{*b8GfwNQ-D4CoLa0&B(*6fAg$q3AU+_r{C&e1=TvC9nfMi+d5EN!SZkA+oj)0 z!#=@~f=30FnY=G~>l%YhCv97{yfYbx9XgqRlGRVmm`^IJ>D2eHr@0I8d<2$v`d-6=ji(G@GGvK__827Z? zsnV{ksB#{eZK&((&;j~KD&1@L{0Wt}uWkmsqb<{iUKXsZDF)~4@i4FTYx}xO#M}Bo zs)o6X3$wSWSlII(4Xoo?pZd1kvo)J^p4t*^cQPzaVrcu;q7i65VM`-n`8{y_c0DdK5 z`i>0e1iKp67n$Sx{Jfc{!k$YUquRf|$O{iuFp~(;*47Rm(N~xN9jRlG$>ibHg}x#A zx|yr?g^hDXrvu~%{4`Vw$}GWyL_%O@z|^TNJMr5U5c;ajr02R z;YZ1`*XJE{3L7WWo3iNvyqC|^JQAM-Mq3rDW;{J~0b;F+E6No~h&8$BFtD^VJ2Zx` zt*tGzC*MF5vB=>kRa>|VYCe98&g5G!*%$T=j@z!kwa}r&Hm_t0&-QySo4*v29Uk_g zVis2veDZ7KR0#{;K0eW%63XmF^S^?#^--& z))bxoG=i=J7*b_`qM6)55JrlimExzDrgXM{y^5lJH6sZxFKo-5oR!Fm>gS{m-Xb9> zPZ_1@x3O5XgYT;`t)os;?Ks(RH9os1+5p=+DUZy)$c62#WAAG#UJ(P4WsIA0UY}Z3=!~S1 z&?T9J5@A>QS&e8H|F?RAryx@qXI~Vak;`Dj^ssx_ZYuXIm9!RVBf(1g6d`88o(`R+ z!O*30hSL7SY_^nQ|Na*7Z^Let9_5}{<9R0{3||Yjmlw4=LK<^W5a8|pZqQ>zR^XX_ zewT-gTcZnF%V~K-u^{N%2@^rbI+^xpbFKDS_3v$XU!P%o(7sRmk%K*Ev zYoYF+b5g%*Z_Ti`Kg8{k+n6QFjDX}8FqXu7i}#|+_t(>v-`^PT5gx)0S`HAz0gI!C zyt!k54~|8v&3m8tC-?Fv-ITe}PR6~y@AG5I-jNnU3fTe`4iMO>XJ4w21MR*M&B~LyHET>O52r7n_v+U66}(4u$^fT+#CXq%OKwM1@fW2TdF*#;ZYh(K zV~t0H^o)2LcFLNCm34@DHq)$ literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/ClashingOrbitContradictionRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/ClashingOrbitContradictionRule.png new file mode 100644 index 0000000000000000000000000000000000000000..d25340b407edf02eebfdbc35764b0527ecea59b7 GIT binary patch literal 5135 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 D%D^dv literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/TooManyStarsContradictionRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/TooManyStarsContradictionRule.png new file mode 100644 index 0000000000000000000000000000000000000000..b468ae6f6fb70271fd594e8cb6b5148dbd9153c5 GIT binary patch literal 6199 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 YEnSqZ8g?u{`D@=m$5i{drepa30M6m=2LJ#7 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/empty.gif b/src/main/resources/edu/rpi/legup/images/starbattle/empty.gif new file mode 100644 index 0000000000000000000000000000000000000000..38b91d0a2b9c6599eb19d14466db047a86d2ea97 GIT binary patch literal 857 zcmZ?wbhEHbRA5kG_|Cxa|Nno6Q7{?;BQ*qcKpqF>1qKc~21X7Uj|~eBHggDT#hlo% z@Nm0;vez7sjf;+UOBiR}IkEBaF$M+Z0v^pz$tNbN+pdZ^xoPR?=?2NC=6GICbzrat E0QgHHq5uE@ literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/BlackOutDirectRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/BlackOutDirectRule.png new file mode 100644 index 0000000000000000000000000000000000000000..f72fd7d7f82687cfae12872a76855ab94d80fdcb GIT binary patch literal 4717 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 G67esNMHw6b literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/ColumnsWithinRegionsDirectRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/ColumnsWithinRegionsDirectRule.png new file mode 100644 index 0000000000000000000000000000000000000000..1a88e9d07c6a0e4a1425d9725be9d1fc130058ee GIT binary patch literal 6456 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 A-2eap literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/ColumnsWithinRowsDirectRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/ColumnsWithinRowsDirectRule.png new file mode 100644 index 0000000000000000000000000000000000000000..24fb0d48a84c46608fc3ab3335ab0df053898bba GIT binary patch literal 5331 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/FinishWithStarDirectRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/FinishWithStarDirectRule.png new file mode 100644 index 0000000000000000000000000000000000000000..11cfbf89965d94b114cf32be6944f2703f4ee90f GIT binary patch literal 4326 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 E0_Y@20RR91 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/RowsWithinColumnsDirectRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/RowsWithinColumnsDirectRule.png new file mode 100644 index 0000000000000000000000000000000000000000..bac64a87c9ae9df53b8be031c17306773ccfba7d GIT binary patch literal 5117 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/RowsWithinRegionsDirectRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/RowsWithinRegionsDirectRule.png new file mode 100644 index 0000000000000000000000000000000000000000..8907e047521d5310676821ddb141672f2b5e4d7c GIT binary patch literal 6477 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 F^k2&Gt=s?r literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/SurroundStar.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/SurroundStar.png new file mode 100644 index 0000000000000000000000000000000000000000..13287f779e74d6c70489f32013ec3675b07dfe4c GIT binary patch literal 5102 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/star.gif b/src/main/resources/edu/rpi/legup/images/starbattle/star.gif new file mode 100644 index 0000000000000000000000000000000000000000..e289b287ae12d06741cf5d97a0413691752c4f49 GIT binary patch literal 545 zcmV++0^a?JP)Px$+et)0R7gwB)k~;OQ4|O8-%S#Q6f(WuN(@LbxC6?>z|bQh@)#H)BSjf;3uWXn zP!c6GWTuoTBMJi~86e~tQXYG0-#TsIx!*bRl~udbx7OP0zy9m7zdk*dz8=T--vKPZ zNZe}5ye)&{8DLYd0Dk7cjT39^h^+_)Av2;~qv~I+oyO zH;_68qf-gSS5A~<6}DyFAzZ*gyu-&-wh#DP0#*UeVHPIj6985Df-zYUfr3d@-r_ah z<1to;v1D)nd-JVpIkQjs`y%Q0XIp&7Vq88Gu{7JF`N8ys&8EnBIreozzwXyvjSvlDruB;5x!$Xnyj1X!07h$Pn6jcYf6SBM5q!d&djqZoq_nZLnHO!*stxbz$k z^O6OVs+_<8W@9oUnLlJW2AgrJxN#Uiu)EO55S(udFjW7z7Y*E=w)sI0G84zJJpYbF zwzLI!nx2AqGGew**VH(+^=R_>j2PR-CF)OWzawq%uDa$G`gogyQMoI%|H<=lr(y6R jw|ThRX3<*4@9h2yt8`HryKo;Y00000NkvXXu0mjflb`cS literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/tiles/NumberTile.png b/src/main/resources/edu/rpi/legup/images/starbattle/white.gif similarity index 82% rename from src/main/resources/edu/rpi/legup/images/sudoku/tiles/NumberTile.png rename to src/main/resources/edu/rpi/legup/images/starbattle/white.gif index 5a8540d02b509f12c7f059648bbadf021efcedda..fc2c683eb7860f47d4322fe0e73b0508099a89de 100644 GIT binary patch delta 1257 zcmVaB^>EX>4U6ba`-PAZ2)IW&i+q+Pzk7vg0TW z{Ld-&2$lp$9ET56wKtgKZ-X5_CzIKFaq`Ag3z)P4{AzAQdv27t4KbDMI z@kSedBpz)MbF-q2*gjDB-}!hX{AisBcXtC1gtOo6^1xF(>cj5N0~DSUJJ~y@Kt2}c zJ4A@1%lSDG+kbnrKKG1WWFNs|+&6XG$!s||77!zIT0~L&0@^xDLa`+;6@s*-E(N7h zB8C#3JT=PHX{F}Ss8oT`^U8{vxw1@~iE`p9n>HxaStUsmgJuZ zdg}R5ZCCZT@`7r#tHx8Q;irdcu&V9{8tLLjkbj{W6k|_NTpv+W z?G%nPxCl-k##hT+*52XM4gVsRdl|;9d%fpA6Q&n;Jxswx4#V6ZZpb`F_PxS`?PBdN zO@EwV?oqh`>l3KlcMUl-locdz%{XTmVP{9m0ocMrJr@Nt;je-wg6aTzqaJF{K{kqmz`Ha220 zFl1#dF)(B~Ei`5@Vl852VPq{fGG;R|G%{f{W@Tm~3LqdLcx`Y^O*%<#b97;DV`WK1 zJtBB*a4uY(#Hkb#0Sw5f3CWH(@n0G-YBfW;rrBEi`2~GA&{^VKOaYHD)w9 zGdDRlGcaV6p%G&UF*7kZIW{;mlME8oBw{o;V`DiuW-VnjVPY*bF*0T?Ib>xpEi__d zVPQ39Wiw=BFp~unRRS|Jvs@Es2n44`A)Aw41Rt~K8(a!hAzO}O2mk;8H%UZ6R9M69 z*Fg~gFc5=4(mDT@QyKYyg8dSp3_E~!V@SedoB#m=1PBlyK!5-N0t5&UkoBH_h^qxZ T9iesj00000NkvXXu0mjfY5XLV delta 1577 zcmV+^2G;rHOVdt}BYy-jdQ@0+Qek%>aB^>EX>4U6ba`-PAZ2)IW&i+q+O1bxlH)22 z{nsjH36=y1mcz5^?%*ze4%kUMnRIm?ZDIpL7DqY~u2BE~zd?U+iP%QYTMQ|L4VOzU zNyZ><`<2#{H@BYal4A+;8#!GU1R_PR`_%HGf06Umz}{ty{C~;NWpggK3-MTxcxUL? zNGGC+q8*0PWnK0Kc86De@5=>p{JPb44==iNPG^BLM5%dbsYqAW4NuXyZ=~}U&FBqJ zjG@QsFjkF(&cWge?3gIrzwx*a_;qv&INdDV1zg-A*Sk1{Yk!r~d4c`oz^>?>Qy^bA zmK#L5ZML1SQ-5H`XmKo`;YD@6V@>W4e+v--( zDkl~&G1zhA%!4~O!k}^mMkieiy+{>ZvZy9Ws_xvN@etFbO(Qs~U{oQM9SXS_$h2Br zyG88W7^=cB#*6YN8a=D{sEtu)h#1Z@9l-)Fnb`^$lYhDSgc=y(aRk+`0AJeu1w%+r zQ0P~fBOJ_I*-aGBU%91=Xoa3fzgGfVTZu7=a)iA-z_5aXEwFkHyctdyW&|>VpbY4u zP6`-c@1+5xIMo1dqRg%FiV-he@v_Tt9)St30reEYPzG7fdE}3|h#bmZz4P7&Z$A3u zbI{;|4}XDzg?X@LQ*-7mm|3)BIcjv##}F;Xm|{+n6z85#Az6wkrJS*6hUJWxbEFJO zk)p*FUqZ1GODee{eX6UzhH5p|RCAN24M#%@&01`!<<1?c)Lr*IbfX@7>Ukj7h7LFU z2*XAkY2+(w2dn?I7g*ziHCaqeJ-f07so7D`NPicaMkZ%qOdNx80|ktPmdV)`**Tfa zP0qF?s0uFt=_fahCu3mHbtdtH@#fCm7jrN3Mj?F5o4mptP3k^i?#df=J9vA*TASat zx)|H5aMs|as6MRsn5FbFpfe)AA~BCLh5PjV8U2o#QRsa$1qV3<%Riiud9ED3LPO%4 zJ%7FR;sSGviYBbzKt*r6bBBs9!TJ;x#ZQ8KLPci^KS4#GP<}>5_lflms=*Z{A`Zo0 zdbX9eQ8L4R%S$0`0V@!s)=NzxZaTM+g1u1e*ml{PJzWOh^jCbVG|q&tn)%YCZ%sUD zT1$aV7J_xdsDEPY9PB%wuj8a?yHzPwVSh*sW;?`N%fsZAET9L4d_X?Z;Ynk3lUsG* zOHsOWnBjmA?0~Ge@Pu>pKH;}LJqnzL+yEt(HSI0r?MGnrEYrvETZr_7`y=^T7`1kw zLfG=a-zsa4oyTwfA3A!v7yQ(FVDr|-Sc4_7(X7%jdd0+?S)B`~8Qo6rW^0e zke@|N&qwEJL!V)y&#^o(^a&>Vjrcq;^gX7P5p9+VGUUOWeo}aLPM;e11w_mHeLjGw zC7f=50bTn~%N}k&;Gh#V7HDhEgG-Eb6Ei__gWi4VhVKXgbF*!71GBRQ@ zH90bqU=SZ9W;io4H!wLhEj4ClHZ3$_I590aGB-IbGi5b0I5;vmG%+<}lcNwPBxN)) zFk)t9G%Yt}VP!2eI5ROVVPP{lEjD2|F*0E`V_`I7Vw3I=V+b-hI5s&qG&Pf15!EC$ zF)(B@GB;r@H)CRAEi`61HZ5W?F*YqRG&M73Gh;I|VPavEQW8}HG&r-V5@-kntGq6x zldA(CvosrA3V$#$K0^8c008qzL_t(o!|j(biiI!~MNi~|km{GNLTs$;(zpW)!7T_b z$Zgo^CftBvZ>tvaels&nH^dA-%#hoq2%J3beeXp9f=4|7MEo!)rHJsX01yBIz~2Mt z`yOKq&*u}ZHO3g$b#;Oe8$(;|mSquJeSeMtaTOp%QQW$xJ^-A1dK||b z$Kh`lBuU~Y1zpzx(6;S+O!!^XGzlPCmfiNwZGdqcC5j?R)6~hlw*ZD=5J2)gcTSHr zz&y_aIKA8{hFWXR^W?g&_pfACRk80oP1F3|UT9MRrPNPxvaZ0xGOuAA2Y>+Z{{WQl b@A(VSFcRW7YuzH900000NkvXXu0mjf3V+L@ diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/AdvancedDeduction.png b/src/main/resources/edu/rpi/legup/images/sudoku/AdvancedDeduction.png new file mode 100644 index 0000000000000000000000000000000000000000..d51538baf164400e6c4cca37fa44f738b2d3c6c1 GIT binary patch literal 3351 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/rules/NoSolution.png b/src/main/resources/edu/rpi/legup/images/sudoku/NoSolution.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/sudoku/rules/NoSolution.png rename to src/main/resources/edu/rpi/legup/images/sudoku/NoSolution.png diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/rules/PossibleValues.png b/src/main/resources/edu/rpi/legup/images/sudoku/PossibleValues.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/sudoku/rules/PossibleValues.png rename to src/main/resources/edu/rpi/legup/images/sudoku/PossibleValues.png diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/rules/RepeatedNumber.png b/src/main/resources/edu/rpi/legup/images/sudoku/RepeatedNumber.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/sudoku/rules/RepeatedNumber.png rename to src/main/resources/edu/rpi/legup/images/sudoku/RepeatedNumber.png diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/rules/forcedByDeduction.png b/src/main/resources/edu/rpi/legup/images/sudoku/forcedByDeduction.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/sudoku/rules/forcedByDeduction.png rename to src/main/resources/edu/rpi/legup/images/sudoku/forcedByDeduction.png diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/rules/forcedByElimination.png b/src/main/resources/edu/rpi/legup/images/sudoku/forcedByElimination.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/sudoku/rules/forcedByElimination.png rename to src/main/resources/edu/rpi/legup/images/sudoku/forcedByElimination.png diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/rules/possible_cells_number.png b/src/main/resources/edu/rpi/legup/images/sudoku/possible_cells_number.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/sudoku/rules/possible_cells_number.png rename to src/main/resources/edu/rpi/legup/images/sudoku/possible_cells_number.png diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/rules/NoCellForNumberColumn.png b/src/main/resources/edu/rpi/legup/images/sudoku/rules/NoCellForNumberColumn.png deleted file mode 100644 index 9dcade64b2fb79be751848670324692e7b464cce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 958 zcmV;v13~Px&cu7P-RCt{2oWE|{Fc62&MF))<`XUzK#XyG|=+Ni!%*l^cAWMOGFu=S>2M--I z6iA0!jigW#?|2fg((eO;1djX%`g}a{MBUEc{42Gn4i)*Er!iuVNjZMYeBeF-CwE>x zat^KzRp)mbNYa5N1Sfrd*h3bW7YH#1gcu{3LqbU#EJIMpD-pdYCGh>BqMgbsZG+&D z^>XvHSBR4Rp;8mO6$W!isuakOF`Cq+QXoUlkuw@xU5kk1@ z42hKf|F_I&QkO3JF#D1-C?vHUGA3o~Q7}eOh*oV1k-{gKLv}<&GoQ$*T(VVyLN1q! z>YF&{R9~;p{~$JYn9jN{3%TSyqH3>IEDGjEFLo{(144`eA;w4&GWYChi-?6l#yp6G zIOnJ=OYP4BC?P`5=d#AJ5s|J6k6Q zYtdl^baHsmEvKFI&$WY26S?GP}eopb)EZTEZ@n&YIIm((M}FAci_e_$FP!n zsA@nh%NU{vX%ADJAuHxC+^w^CCkF@^hb3gHlLJU6T1adshZqng!$b+;c5(o6CWS~l zIZS~l34=}!#u%LTYISlLdkyUxu5a?nJ-WHSNreC_+x(PW1HsiTu@5Q52ZgD#8|W{BrE3 z3A2IHi7_C=7!YC%2r))5sbeae4{UNU28JwSK!`CQ#264_3yOtH2F`>4l2P;C_ gZBv_{_TwO(KSVOPa4)&_O8@`>07*qoM6N<$g6E{9PXGV_ diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/rules/NoCellForNumberRegion.png b/src/main/resources/edu/rpi/legup/images/sudoku/rules/NoCellForNumberRegion.png deleted file mode 100644 index 000dc8b68935563c869c695b114190eac7499805..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1077 zcmeAS@N?(olHy`uVBq!ia0vp^DImV1DoE;uumf z=k1)me$tKt$N&2Y6uk`p=oNk>s4K#2;`4rb9xS z7iK=SsK4{>OjTNb-K1^@iM$_o@6}b*3zKK1sW$F0Z5C)l(Hs9l}f z^X*5i;>;_WlV*kS)yv6u_uOaS6dL9gwERxwt;h4ZUT!j1TWNPPqraZ($?pkvo<~&{ z+x|10G;Qzij7e2dzxV#0Iz@H)w;$~Zf=qi&Qf>9PFDPYX7BX0`12JonP$_7uTOJjLr~g}(jhe518xwdlFc%4K$?H*eL33p@`}XsTDGHtIQ#o$WvU9?^ly7T&Zh6Mm?bEUQXXw%DVPic> zk~d;l5Z>R?_#QR`O`wi_4*YwV@p4f8L}zC^ID7 zQOWF=cb+uiXp&>8to(}Cv%V^v5AFN=D|tguP~r>$6trO~V`<=Z)1N_`SLbyV?YtAW zuSjodqmNV4^(V(O_7}SNUgteh7~HzSe$%U6$5MDJ6&= zvfa~o{rI%slb+jXx!XmmmuAiSc{nuBd}p!vrozK&OJ<(9Eo^l>_L`Wp?$dAIbsKDo zf&O3D{ao(TzXQkFlTLn|!KIm%ydsuuK@A^g;nc$wHpgDoxk_!%=0B>bxmsXa>@U`a zx3BlFOr4V~zo@DuBR;S+@QO?jlbVYJ1bMa=9ZT$bJ$>okciL?CiRK zjpD*(@z(+ZuIguVtav>=)Jp4j>Q>JF6PD9XeSTleaNyZ}wi}=1vNpZFa=0$|t$2Hn z8%rtgjX%d_PVZKoR_^_Sz5ipynO&M;clBRCd&YKPcF)`?@g_lO_tS3txx44L)Yodg z+>r05tM1vA*C%_6{IB${aRc&q{j21Cklf0B)#j1>pZ3UUzj@Yo)I~Dv?I~WkD?Uu4 zF+1oxzue~ao9}v;y6=?vyX=tQK~t-+@2l^2OK<(V=^X2KnM0cl-(1euD&>?B+gw<~ zrz&DF)8~eK?r|b diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/rules/NoCellForNumberRow.png b/src/main/resources/edu/rpi/legup/images/sudoku/rules/NoCellForNumberRow.png deleted file mode 100644 index f984f836508fbc3d6f0dce4d9b749f5e53de8d87..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 916 zcmeAS@N?(olHy`uVBq!ia0vp^DImVD|HLaSW-L z^LFmWyxRsMuJgNjq)flK`#3oBl&omv`zv>T)}Kh$O)MSk%xC&bPOQ+K8F8a2*YnBZ zd#csv=l!sgw+mV%lJ@xgzhDlY`)imdWNWbSa4|MJ9atb>fW(}qV|S(W+@oaihv)A- zDZZ(gFr&odK$eKzpL-(rTv}e zMq{QoAme+oS|aR^Us%MYINiuA&fY~!P)H-T&BN8{*88=kETUcFn|^N;7M$3>U;db9 ziAGe%>p47!6?`v;7{+#r=&5}8_A5=+u~)0*ipxf)L$?B@k8%l4Ty?F_p>TG0W1_?w z<&=dl_*PC#vR<-nrPcvHOiuy*cUk&yRGiO+7Fnh?&-N?3-Y#Uc2Kirkwt(J^!j!iH zCYtMt3uYcGIK_k!B%3%K_g`~Y%Wf|aF0`Dp2^3(9=C>EjJ#kp=Ykk1cu9I8$E#VV+ zU8*1Hk?>A@W7t9OqY>R4D>ymt{8n1SHBYEnNoj$e3(Mz}){PQVVv;w-q_!qa?Amvx zX-c-wG(N?TIW8?u4HdJj7QPL3xi>Yc=CZW(p zor_s6c+mFH+uw12{p{HPJy}0*d)8XTll7O%EFT5A$48j!UFX+zoMp-U@5%qUInU3> zxq3~SP?(q))uCJV=jihZij(Gj7JAEUv3U8KYSzrRnwcBJ{4Q%|XO(339S|vc&Lebd zTj*`cIGvO{Mya5nWvfk#o>hF>k#QQ2@#E!F3`Y22N%uG!+zUgxUM2u-s3x+XM7 z(^B40(kq%sZU$yE22WQ%mvv4F FO#n+HlXCz7 diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/rules/possible_cells_number_column.png b/src/main/resources/edu/rpi/legup/images/sudoku/rules/possible_cells_number_column.png deleted file mode 100644 index 2ebdc582364e4afc93c26d2e41548abf30456ed8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3336 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 SmU`#_0000uJ@VVD_U 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 diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/rules/possible_cells_number_row.png b/src/main/resources/edu/rpi/legup/images/sudoku/rules/possible_cells_number_row.png deleted file mode 100644 index 80476a42820b213907af74ce76c96424a103b862..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 803 zcmeAS@N?(olHy`uVBq!ia0vp^DImV7lw+;uumf z=j~kEJgGntH+%OuhDZmoi4$WaPKH{U2a1`9YM){XaxLQWP7#cBh~o10Yjx^4&V1(k zgF7F~r6xG|Pp|$`SMmATxvRel|E_6xcy}o?$A2M310@!>mIECE5`s+1P6;j?JsgdN zw%=OcEwgr)$?y95xaz~^K*eOs8M+I!;Hlq$@iDW2kKmOj`3V_dO{b+#AWK6n|M}E5P11zN!Ub1 zQDG(7`FFgGmNYpnIXpSWw=E>3!(`r@E~6!foR&OX5l0AR_{`y}D+-{TV;{Rs_-QDFn=lPnv+ACit z!nbeZxLN<9^S7hMx>wh9 zAAtmTB6ZEb%@OrHzlqUu({e(~Cm3Z<`mh=YLWD)Q!J) z)xH;a(o@{Mde_#{sZak*T)QfI`wPiQ>+9C1p2+&De0NLN<;c6cPW~6@toGdMI_KQ0 zYhsGeJ@zH<$P!J@+JAlqIK=yAIqMa&z7<(@>qN^^MF)8F(Ut f1vEJ2J?7VB=2_pOylEaV)iZdy`njxgN@xNAzyM-G diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/rules/tem.png b/src/main/resources/edu/rpi/legup/images/sudoku/tem.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/sudoku/rules/tem.png rename to src/main/resources/edu/rpi/legup/images/sudoku/tem.png diff --git a/src/main/resources/edu/rpi/legup/legup/config b/src/main/resources/edu/rpi/legup/legup/config index e01767677..1ee9ed79c 100644 --- a/src/main/resources/edu/rpi/legup/legup/config +++ b/src/main/resources/edu/rpi/legup/legup/config @@ -34,22 +34,26 @@ fileCreationDisabled="false"/> + fileCreationDisabled="true"/> + - - - + + + diff --git a/src/test/java/legup/TestUtilities.java b/src/test/java/legup/TestUtilities.java index d48d648d7..83ce773d4 100644 --- a/src/test/java/legup/TestUtilities.java +++ b/src/test/java/legup/TestUtilities.java @@ -7,18 +7,10 @@ import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.save.InvalidFileFormatException; -import java.io.InputStream; - public final class TestUtilities { public static void importTestBoard(String fileName, Puzzle puzzle) throws InvalidFileFormatException { - InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName); - - if (inputStream == null) { - throw new IllegalArgumentException("InputStream cannot be null. File not found: " + fileName); - } - - puzzle.importPuzzle(inputStream); + puzzle.importPuzzle(ClassLoader.getSystemResourceAsStream(fileName)); Tree tree = puzzle.getTree(); TreeNode rootNode = tree.getRootNode(); Board board = rootNode.getBoard().copy(); diff --git a/src/test/java/puzzles/nurikabe/rules/FinishRoomCaseRuleTest.java b/src/test/java/puzzles/nurikabe/rules/FinishRoomCaseRuleTest.java index a29a1c934..5f5b6d35a 100644 --- a/src/test/java/puzzles/nurikabe/rules/FinishRoomCaseRuleTest.java +++ b/src/test/java/puzzles/nurikabe/rules/FinishRoomCaseRuleTest.java @@ -28,12 +28,13 @@ public static void setUp() { } /** - * Tests the Finish Room case rule by ensuring it produces the correct number of children + * Tests the Finish Room case rule by ensuring that it results in 5 or less children, that + * contain a modified cell that is white */ - @Test public void FinishRoomCaseRule_FinishRoomCaseRuleBaseTest() throws InvalidFileFormatException { - TestUtilities.importTestBoard("puzzles/nurikabe/rules/FinishRoomCaseRule/FinishRoomCaseRuleBase", nurikabe); + TestUtilities.importTestBoard( + "puzzles/nurikabe/rules/FinishRoomCaseRule/FinishRoomCaseRuleBase", nurikabe); TreeNode rootNode = nurikabe.getTree().getRootNode(); TreeTransition transition = rootNode.getChildren().get(0); transition.setRule(RULE); @@ -80,6 +81,34 @@ public void FinishRoomCaseRule_FinishRoomCaseRuleBaseTest() throws InvalidFileFo NurikabeCell cell2 = board.getCell(4, 2); ArrayList cases2 = RULE.getCases(board, cell2); - Assert.assertEquals(9, cases2.size()); + Assert.assertEquals(6, cases2.size()); // correctly stops generating possible cases after + // more than 5 (the max) is found. Would have generated 8 cases + // FinishRoomCaseRule finny = new FinishRoomCaseRule(); + // finny.checkRuleRaw(); + // "Invalid use of the case rule FinishRoom: This case rule must have 5 or less children." + + // getErrorString in auto case rule + // should display "The selection can produce a max of 5 cases." + // AutoCaseRuleCommand autoCaseRuleCommand = new AutoCaseRuleCommand(elementView, selection, + // caseBoard.getCaseRule(), caseBoard, e); + + // NurikabeBoard caseyBoard = (NurikabeBoard) cases2.get(0); + // NurikabeBoard caseyBoard2 = (NurikabeBoard) cases2.get(1); + // NurikabeBoard caseyBoard3 = (NurikabeBoard) cases2.get(2); + // NurikabeBoard caseyBoard4 = (NurikabeBoard) cases2.get(3); + // NurikabeBoard caseyBoard5 = (NurikabeBoard) cases2.get(4); + // NurikabeBoard caseyBoard6 = (NurikabeBoard) cases2.get(5); + // NurikabeBoard caseyBoard7 = (NurikabeBoard) cases2.get(6); + // NurikabeBoard caseyBoard8 = (NurikabeBoard) cases2.get(7); + // + // NurikabeType boardy1Type = caseyBoard.getCell(5,5).getType(); + // NurikabeType boardy2Type = caseyBoard2.getCell(6,6).getType(); + // NurikabeType boardy3Type = caseyBoard.getCell(5,5).getType(); + // NurikabeType boardy4Type = caseyBoard2.getCell(6,6).getType(); + // NurikabeType boardy5Type = caseyBoard.getCell(5,5).getType(); + // NurikabeType boardy6Type = caseyBoard2.getCell(6,6).getType(); + // NurikabeType boardy7Type = caseyBoard.getCell(5,5).getType(); + // NurikabeType boardy8Type = caseyBoard2.getCell(6,6).getType(); + } } diff --git a/src/test/java/puzzles/sudoku/rules/LastNumberForCellDirectRuleRegionTest.java b/src/test/java/puzzles/sudoku/rules/LastNumberForCellDirectRuleRegionTest.java deleted file mode 100644 index f27f38d8a..000000000 --- a/src/test/java/puzzles/sudoku/rules/LastNumberForCellDirectRuleRegionTest.java +++ /dev/null @@ -1,100 +0,0 @@ -package puzzles.sudoku.rules; - -import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.sudoku.Sudoku; -import edu.rpi.legup.puzzle.sudoku.SudokuBoard; -import edu.rpi.legup.puzzle.sudoku.SudokuCell; -import edu.rpi.legup.puzzle.sudoku.rules.LastNumberForCellDirectRule; -import edu.rpi.legup.save.InvalidFileFormatException; -import legup.MockGameBoardFacade; -import legup.TestUtilities; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -public class LastNumberForCellDirectRuleRegionTest { - private static final LastNumberForCellDirectRule RULE = new LastNumberForCellDirectRule(); - private static Sudoku sudoku; - - @BeforeClass - public static void setUp() { - MockGameBoardFacade.getInstance(); - sudoku = new Sudoku(); - } - - @Test - public void LastNumberForCellDirectRule_FullRegionTest() throws InvalidFileFormatException { - // Import board and create transition - TestUtilities.importTestBoard( - "puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRegion", sudoku); - TreeNode rootNode = sudoku.getTree().getRootNode(); - TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - // Loop all numbers at point - for (int i = 1; i < 10; i++) { - // Reset board - SudokuBoard board = (SudokuBoard) transition.getBoard(); - // Set cell - SudokuCell cell1 = board.getCell(2, 5); - cell1.setData(i); - board.addModifiedData(cell1); - - // Test the case - if (i == 9) { - Assert.assertNull(RULE.checkRuleAt(transition, cell1)); - } else { - Assert.assertNotNull(RULE.checkRuleAt(transition, cell1)); - } - } - - // Import Board and create transition - TestUtilities.importTestBoard( - "puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRow", sudoku); - rootNode = sudoku.getTree().getRootNode(); - transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - // Loop all numbers at point - for (int i = 1; i < 10; i++) { - // Reset board - SudokuBoard board = (SudokuBoard) transition.getBoard(); - // Set cell - SudokuCell cell = board.getCell(4, 4); - cell.setData(i); - board.addModifiedData(cell); - - // Test the case - if (i == 5) { - Assert.assertNull(RULE.checkRuleAt(transition, cell)); - } else { - Assert.assertNotNull(RULE.checkRuleAt(transition, cell)); - } - } - - // Import Board and create transition - TestUtilities.importTestBoard( - "puzzles/sudoku/rules/LastNumberForCellDirectRule/FullMixed", sudoku); - rootNode = sudoku.getTree().getRootNode(); - transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - // Loop all numbers at point - for (int i = 1; i < 10; i++) { - // Reset board - SudokuBoard board = (SudokuBoard) transition.getBoard(); - // Set cell - SudokuCell cell = board.getCell(5, 3); - cell.setData(i); - board.addModifiedData(cell); - - // Test the case - if (i == 2) { - Assert.assertNull(RULE.checkRuleAt(transition, cell)); - } else { - Assert.assertNotNull(RULE.checkRuleAt(transition, cell)); - } - } - } -} diff --git a/src/test/java/puzzles/sudoku/rules/RepeatedNumberContradictionRuleTest.java b/src/test/java/puzzles/sudoku/rules/RepeatedNumberContradictionRuleTest.java deleted file mode 100644 index 704167a29..000000000 --- a/src/test/java/puzzles/sudoku/rules/RepeatedNumberContradictionRuleTest.java +++ /dev/null @@ -1,88 +0,0 @@ -package puzzles.sudoku.rules; - -import edu.rpi.legup.model.tree.TreeNode; -import edu.rpi.legup.model.tree.TreeTransition; -import edu.rpi.legup.puzzle.sudoku.Sudoku; -import edu.rpi.legup.puzzle.sudoku.SudokuBoard; -import edu.rpi.legup.puzzle.sudoku.SudokuCell; -import edu.rpi.legup.puzzle.sudoku.rules.RepeatedNumberContradictionRule; -import edu.rpi.legup.save.InvalidFileFormatException; -import legup.MockGameBoardFacade; -import legup.TestUtilities; -import org.junit.Assert; -import org.junit.BeforeClass; -import org.junit.Test; - -public class RepeatedNumberContradictionRuleTest { - private static final RepeatedNumberContradictionRule RULE = - new RepeatedNumberContradictionRule(); - private static Sudoku sudoku; - - @BeforeClass - public static void setUp() { - MockGameBoardFacade.getInstance(); - sudoku = new Sudoku(); - } - - @Test - public void RepeatedNumberContradictionRule_GlobalTest() throws InvalidFileFormatException { - // Import board and create transition - TestUtilities.importTestBoard( - "puzzles/sudoku/rules/RepeatedNumberContradictionRule/BlankBoard7", sudoku); - TreeNode rootNode = sudoku.getTree().getRootNode(); - TreeTransition transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - // Loop through every cell - for (int i = 0; i < 81; i++) { - // Reset board - SudokuBoard board = (SudokuBoard) transition.getBoard(); - // Set cell - int x = i / 9; - int y = i % 9; - if (x == 0 && y == 0) { - continue; - } - SudokuCell cell = board.getCell(x, y); - cell.setData(7); - - // Test the case - if (x == 0 || y == 0 || (x < 3 && y < 3)) { - Assert.assertNull(RULE.checkRuleAt(transition, cell)); - } else { - Assert.assertNotNull(RULE.checkRuleAt(transition, cell)); - } - cell.setData(0); - } - // Import board and create transition - TestUtilities.importTestBoard( - "puzzles/sudoku/rules/RepeatedNumberContradictionRule/BlankBoard4", sudoku); - rootNode = sudoku.getTree().getRootNode(); - transition = rootNode.getChildren().get(0); - transition.setRule(RULE); - - // Loop through every cell - for (int i = 0; i < 81; i++) { - // Reset board - SudokuBoard board = (SudokuBoard) transition.getBoard(); - // Set cell - int x = i / 9; - int y = i % 9; - if ((x == 3 && y == 0) || (x == 6 && y == 8)) { - continue; - } - SudokuCell cell = board.getCell(x, y); - cell.setData(4); - - // Test the case - if ((x == 3 || y == 0 || x == 6 || y == 8) - || (x > 2 && x < 6 && y < 3) - || (x > 5 && y > 5)) { - Assert.assertNull(RULE.checkRuleAt(transition, cell)); - } else { - Assert.assertNotNull(RULE.checkRuleAt(transition, cell)); - } - cell.setData(0); - } - } -} diff --git a/src/test/resources/puzzles/binary/rules/SurroundPairDirectRule/SurroundTwoZerosWithTwoOnes b/src/test/resources/puzzles/binary/rules/SurroundPairDirectRule/SurroundTwoZerosWithTwoOnes deleted file mode 100644 index 026742fea..000000000 --- a/src/test/resources/puzzles/binary/rules/SurroundPairDirectRule/SurroundTwoZerosWithTwoOnes +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/src/test/resources/puzzles/binary/rules/SurroundPairDirectRule/test b/src/test/resources/puzzles/binary/rules/SurroundPairDirectRule/test deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/test/resources/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRule/SurroundTwoZerosWithTwoOnes b/src/test/resources/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRule/SurroundTwoZerosWithTwoOnes deleted file mode 100644 index 026742fea..000000000 --- a/src/test/resources/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRule/SurroundTwoZerosWithTwoOnes +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/ColumnBlackout b/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/ColumnBlackout new file mode 100644 index 000000000..ddcc4dc9a --- /dev/null +++ b/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/ColumnBlackout @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RegionBlackout b/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RegionBlackout new file mode 100644 index 000000000..f2a5b42d9 --- /dev/null +++ b/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RegionBlackout @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RowBlackout b/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RowBlackout new file mode 100644 index 000000000..f2a5b42d9 --- /dev/null +++ b/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RowBlackout @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/sudoku/rules/LastCellForNumberDirectRule/CorneredRegion b/src/test/resources/puzzles/sudoku/rules/LastCellForNumberDirectRule/CorneredRegion deleted file mode 100644 index 4d3c57225..000000000 --- a/src/test/resources/puzzles/sudoku/rules/LastCellForNumberDirectRule/CorneredRegion +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullMixed b/src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullMixed deleted file mode 100644 index 55b501fec..000000000 --- a/src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullMixed +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRegion b/src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRegion deleted file mode 100644 index 58fd02162..000000000 --- a/src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRegion +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRow b/src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRow deleted file mode 100644 index 07e502ed9..000000000 --- a/src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRow +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/src/test/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/BlankBoard4 b/src/test/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/BlankBoard4 deleted file mode 100644 index abaa0ba6b..000000000 --- a/src/test/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/BlankBoard4 +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/src/test/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/BlankBoard7 b/src/test/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/BlankBoard7 deleted file mode 100644 index 5692dea64..000000000 --- a/src/test/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/BlankBoard7 +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - -