diff --git a/output_path/test/src/resources/puzzles/sudoku/rules/LastCellForNumberDirectRule/TestBoard b/output_path/test/src/resources/puzzles/sudoku/rules/LastCellForNumberDirectRule/TestBoard new file mode 100644 index 000000000..a41ad749c --- /dev/null +++ b/output_path/test/src/resources/puzzles/sudoku/rules/LastCellForNumberDirectRule/TestBoard @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/output_path/test/src/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRegion b/output_path/test/src/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRegion new file mode 100644 index 000000000..49dae4aa6 --- /dev/null +++ b/output_path/test/src/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRegion @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/output_path/test/src/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/a b/output_path/test/src/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/a new file mode 100644 index 000000000..e69de29bb diff --git a/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 1 b/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 1 new file mode 100644 index 000000000..42ccf371b --- /dev/null +++ b/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 1 @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 2 b/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 2 new file mode 100644 index 000000000..d73caa5d2 --- /dev/null +++ b/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 2 @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 3 b/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 3 new file mode 100644 index 000000000..99ec9769b --- /dev/null +++ b/puzzles files/binary/10x10 Binary Hard/10x10 Binary Hard 3 @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 1 b/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 1 new file mode 100644 index 000000000..d203617c8 --- /dev/null +++ b/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 1 @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 2 b/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 2 new file mode 100644 index 000000000..db56f04f3 --- /dev/null +++ b/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 2 @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 3 b/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 3 new file mode 100644 index 000000000..11940a6eb --- /dev/null +++ b/puzzles files/binary/10x10 Binary Medium/10x10 Binary Medium 3 @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 000000000..828a450cf --- /dev/null +++ b/puzzles files/binary/10x10 Binary Very Hard/10x10 Binary Very Hard 1 @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 1 b/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 1 new file mode 100644 index 000000000..7b22ffc10 --- /dev/null +++ b/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 1 @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ 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 new file mode 100644 index 000000000..ea8ef93b0 --- /dev/null +++ b/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 2 @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ 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 new file mode 100644 index 000000000..0f0ff745e --- /dev/null +++ b/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 3 @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 new file mode 100644 index 000000000..da76d067b --- /dev/null +++ b/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 4 @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 new file mode 100644 index 000000000..a1ea13988 --- /dev/null +++ b/puzzles files/binary/6x6 Binary Easy/6x6 Binary Easy 5 @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 1 b/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 1 new file mode 100644 index 000000000..5f7f72a8a --- /dev/null +++ b/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 1 @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 2 b/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 2 new file mode 100644 index 000000000..a4ed30c31 --- /dev/null +++ b/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 2 @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 3 b/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 3 new file mode 100644 index 000000000..fc0e413c1 --- /dev/null +++ b/puzzles files/binary/6x6 Binary Hard/6x6 Binary Hard 3 @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 1 b/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 1 new file mode 100644 index 000000000..a5ab8a2dc --- /dev/null +++ b/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 1 @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 2 b/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 2 new file mode 100644 index 000000000..4be5fdaad --- /dev/null +++ b/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 2 @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 3 b/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 3 new file mode 100644 index 000000000..eba370cab --- /dev/null +++ b/puzzles files/binary/6x6 Binary Medium/6x6 Binary Medium 3 @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 000000000..faa68fa5e --- /dev/null +++ b/puzzles files/binary/6x6 Binary Very Hard/6x6 Binary Very Hard 1 @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 000000000..3c707bdaa --- /dev/null +++ b/puzzles files/binary/6x6 Binary Very Hard/6x6 Binary Very Hard 2 @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 000000000..217a032d8 --- /dev/null +++ b/puzzles files/binary/6x6 Binary Very Hard/6x6 Binary Very Hard 3 @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 1 b/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 1 new file mode 100644 index 000000000..befd674f9 --- /dev/null +++ b/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 1 @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 2 b/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 2 new file mode 100644 index 000000000..724426c26 --- /dev/null +++ b/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 2 @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 3 b/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 3 new file mode 100644 index 000000000..92a96c72b --- /dev/null +++ b/puzzles files/binary/8x8 Binary Easy/8x8 Binary Easy 3 @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 1 b/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 1 new file mode 100644 index 000000000..34eaf8388 --- /dev/null +++ b/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 1 @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 2 b/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 2 new file mode 100644 index 000000000..9ef23277e --- /dev/null +++ b/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 2 @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 3 b/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 3 new file mode 100644 index 000000000..287ff6f68 --- /dev/null +++ b/puzzles files/binary/8x8 Binary Hard/8x8 Binary Hard 3 @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 1 b/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 1 new file mode 100644 index 000000000..47dae23dc --- /dev/null +++ b/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 1 @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 2 b/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 2 new file mode 100644 index 000000000..ae4cb8bb0 --- /dev/null +++ b/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 2 @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 3 b/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 3 new file mode 100644 index 000000000..2f951ecc4 --- /dev/null +++ b/puzzles files/binary/8x8 Binary Medium/8x8 Binary Medium 3 @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 000000000..9c875523b --- /dev/null +++ b/puzzles files/binary/8x8 Binary Very Hard/8x8 Binary Very Hard 1 @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 000000000..14f2e4ad2 --- /dev/null +++ b/puzzles files/binary/8x8 Binary Very Hard/8x8 Binary Very Hard 2 @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 new file mode 100644 index 000000000..ad319a4b7 --- /dev/null +++ b/puzzles files/binary/8x8 Binary Very Hard/8x8 Binary Very Hard 3 @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/puzzles files/light-color-theme.txt b/puzzles files/light-color-theme.txt new file mode 100644 index 000000000..5c45bc71b --- /dev/null +++ b/puzzles files/light-color-theme.txt @@ -0,0 +1,99 @@ +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/minesweeper/5x5 Minesweeper Easy/Minesweeper 5x5 easy 1 b/puzzles files/minesweeper/5x5 Minesweeper Easy/Minesweeper 5x5 easy 1 new file mode 100644 index 000000000..39d212b8e --- /dev/null +++ b/puzzles files/minesweeper/5x5 Minesweeper Easy/Minesweeper 5x5 easy 1 @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/minesweeper/5x5 Minesweeper Easy/Minesweeper 5x5 easy 2 b/puzzles files/minesweeper/5x5 Minesweeper Easy/Minesweeper 5x5 easy 2 new file mode 100644 index 000000000..8df5704d0 --- /dev/null +++ b/puzzles files/minesweeper/5x5 Minesweeper Easy/Minesweeper 5x5 easy 2 @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/minesweeper/5x5 Minesweeper Easy/Minesweeper 5x5 easy 3 b/puzzles files/minesweeper/5x5 Minesweeper Easy/Minesweeper 5x5 easy 3 new file mode 100644 index 000000000..5ed05d383 --- /dev/null +++ b/puzzles files/minesweeper/5x5 Minesweeper Easy/Minesweeper 5x5 easy 3 @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/minesweeper/7x7 Minesweeper Easy/Minesweeper 7x7 easy1.txt b/puzzles files/minesweeper/7x7 Minesweeper Easy/Minesweeper 7x7 easy1.txt new file mode 100644 index 000000000..13d9c56a3 --- /dev/null +++ b/puzzles files/minesweeper/7x7 Minesweeper Easy/Minesweeper 7x7 easy1.txt @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/minesweeper/7x7 Minesweeper Easy/Minesweeper 7x7 easy2.txt b/puzzles files/minesweeper/7x7 Minesweeper Easy/Minesweeper 7x7 easy2.txt new file mode 100644 index 000000000..e99df19e0 --- /dev/null +++ b/puzzles files/minesweeper/7x7 Minesweeper Easy/Minesweeper 7x7 easy2.txt @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/minesweeper/7x7 Minesweeper Easy/Minesweeper 7x7 easy3.txt b/puzzles files/minesweeper/7x7 Minesweeper Easy/Minesweeper 7x7 easy3.txt new file mode 100644 index 000000000..d2815ab6a --- /dev/null +++ b/puzzles files/minesweeper/7x7 Minesweeper Easy/Minesweeper 7x7 easy3.txt @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/minesweeper/7x7 Minesweeper Medium/Minesweeper 7x7 medium1.txt b/puzzles files/minesweeper/7x7 Minesweeper Medium/Minesweeper 7x7 medium1.txt new file mode 100644 index 000000000..8e7591261 --- /dev/null +++ b/puzzles files/minesweeper/7x7 Minesweeper Medium/Minesweeper 7x7 medium1.txt @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/minesweeper/7x7 Minesweeper Medium/Minesweeper 7x7 medium2.txt b/puzzles files/minesweeper/7x7 Minesweeper Medium/Minesweeper 7x7 medium2.txt new file mode 100644 index 000000000..ff77ea677 --- /dev/null +++ b/puzzles files/minesweeper/7x7 Minesweeper Medium/Minesweeper 7x7 medium2.txt @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/minesweeper/7x7 Minesweeper Medium/Minesweeper 7x7 medium3.txt b/puzzles files/minesweeper/7x7 Minesweeper Medium/Minesweeper 7x7 medium3.txt new file mode 100644 index 000000000..cbb8113a0 --- /dev/null +++ b/puzzles files/minesweeper/7x7 Minesweeper Medium/Minesweeper 7x7 medium3.txt @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/puzzles files/skyscrapers/5x5 Skyscrapers Easy1 b/puzzles files/skyscrapers/5x5 Skyscrapers Easy1 index 111d2bd87..47693c4b1 100644 --- a/puzzles files/skyscrapers/5x5 Skyscrapers Easy1 +++ b/puzzles files/skyscrapers/5x5 Skyscrapers Easy1 @@ -1,10 +1,9 @@ - + + - - - + @@ -21,6 +20,21 @@ + + + + + + + + + + + + + + + + - 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 deleted file mode 100644 index b86e16ff2..000000000 --- a/puzzles files/starbattle/5x5 Star Battle 1 star Normal/5x5 Star Battle 1 star Normal 1.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 deleted file mode 100644 index 110dd9abe..000000000 --- a/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 Star Battle 1star Normal1.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 deleted file mode 100644 index 49efeba59..000000000 --- a/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 Star Battle 1star Normal2.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 deleted file mode 100644 index 855943612..000000000 --- a/puzzles files/starbattle/6x6 Star Battle 1 star Normal/6x6 StarBattle 1star Normal3.xml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 deleted file mode 100644 index c1d7770f6..000000000 --- a/puzzles files/starbattle/7x7 Star Battle 1 star Hard/7x7 Star Battle 1star Hard1.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 deleted file mode 100644 index cab0a0a5e..000000000 --- a/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 deleted file mode 100644 index 70b81e376..000000000 --- a/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal1.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 deleted file mode 100644 index c541ece06..000000000 --- a/puzzles files/starbattle/7x7 Star Battle 1 star Normal/7x7 Star Battle 1star Normal2.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 deleted file mode 100644 index 02dd5d6c0..000000000 --- a/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal1.xml +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 deleted file mode 100644 index 0df84ef62..000000000 --- a/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal2.xml +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 deleted file mode 100644 index 725c91d7f..000000000 --- a/puzzles files/starbattle/8x8 Star Battle 1 star Normal/8x8 Star Battle 1star Normal3.xml +++ /dev/null @@ -1,104 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/puzzles files/thermometer/therm_test.xml b/puzzles files/thermometer/therm_test.xml new file mode 100644 index 000000000..66e841dc5 --- /dev/null +++ b/puzzles files/thermometer/therm_test.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/edu/rpi/legup/app/Config.java b/src/main/java/edu/rpi/legup/app/Config.java index 8a8e9665d..b04ef9b88 100644 --- a/src/main/java/edu/rpi/legup/app/Config.java +++ b/src/main/java/edu/rpi/legup/app/Config.java @@ -12,6 +12,11 @@ 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()); @@ -74,6 +79,13 @@ 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++) { @@ -84,6 +96,11 @@ 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()) { @@ -92,6 +109,12 @@ 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 c928c1209..c6f92e5b5 100644 --- a/src/main/java/edu/rpi/legup/app/GameBoardFacade.java +++ b/src/main/java/edu/rpi/legup/app/GameBoardFacade.java @@ -31,6 +31,11 @@ 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()); @@ -72,6 +77,7 @@ public static synchronized GameBoardFacade getInstance() { return instance; } + /** Initializes the UI components */ public void initializeUI() { EventQueue.invokeLater( () -> { @@ -83,18 +89,25 @@ 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 { @@ -105,11 +118,21 @@ 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; } @@ -168,53 +191,61 @@ public boolean validateTextInput(String game, String[] statements) throws Runtim } /** - * Loads an empty puzzle + * Loads an empty puzzle with the specified dimensions * * @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 { - String qualifiedClassName = config.getPuzzleClassForName(game); - LOGGER.debug("Loading " + qualifiedClassName); + if (!game.isEmpty()) { + 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); @@ -257,10 +288,11 @@ public void loadPuzzle(String game, String[] statements) { } /** - * Loads a puzzle file + * Loads a puzzle file from the specified file * * @param fileName file name of the board file - * @throws InvalidFileFormatException if input is invalid + * @throws InvalidFileFormatException if the file format is invalid or if the file cannot be + * created */ public void loadPuzzle(String fileName) throws InvalidFileFormatException { try { @@ -273,6 +305,13 @@ 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)); @@ -284,6 +323,13 @@ 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 a570e7d44..cad80b5ce 100644 --- a/src/main/java/edu/rpi/legup/app/InvalidConfigException.java +++ b/src/main/java/edu/rpi/legup/app/InvalidConfigException.java @@ -1,6 +1,15 @@ 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 12433d7e4..3d17233bd 100644 --- a/src/main/java/edu/rpi/legup/app/LegupPreferences.java +++ b/src/main/java/edu/rpi/legup/app/LegupPreferences.java @@ -4,6 +4,11 @@ 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; @@ -73,9 +78,10 @@ public class LegupPreferences { } /** - * Gets the legup preferences singleton instance. + * Gets the legup preferences singleton instance This method ensures that only one instance of + * LegupPreferences exists * - * @return legup preferences + * @return the singleton instance of LegupPreferences */ public static LegupPreferences getInstance() { if (instance == null) { @@ -84,30 +90,40 @@ public static LegupPreferences getInstance() { return instance; } - /** Private LegupPreferences Singleton Constructor */ + /** + * Private constructor to prevent instantiation from outside the class Use {@link + * #getInstance()} to access the singleton instance + */ private LegupPreferences() {} /** * Gets the user preference by the string key * * @param key key name of the preference - * @return value of the preference + * @return value of the preference or {@code null} if the preference does not exist */ public String getUserPref(String key) { return preferencesMap.get(key); } /** - * Gets the user preference by the string key, value pair + * Sets the user preference for the specified key to the provided value * - * @param key key name of the preference - * @param value value of the preference + * @param key key to set for the preference + * @param value value to set for 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; @@ -120,10 +136,20 @@ 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 57ce107ac..823bac716 100644 --- a/src/main/java/edu/rpi/legup/controller/Controller.java +++ b/src/main/java/edu/rpi/legup/controller/Controller.java @@ -5,6 +5,11 @@ 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; @@ -19,6 +24,11 @@ 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 2706bd522..aad7eaf46 100644 --- a/src/main/java/edu/rpi/legup/controller/CursorController.java +++ b/src/main/java/edu/rpi/legup/controller/CursorController.java @@ -6,6 +6,11 @@ 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 5a23885af..050f5bd65 100644 --- a/src/main/java/edu/rpi/legup/controller/EditorElementController.java +++ b/src/main/java/edu/rpi/legup/controller/EditorElementController.java @@ -9,6 +9,11 @@ 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; @@ -20,19 +25,33 @@ 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 5840650e1..436b078b9 100644 --- a/src/main/java/edu/rpi/legup/controller/ElementController.java +++ b/src/main/java/edu/rpi/legup/controller/ElementController.java @@ -26,13 +26,17 @@ 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 handles ui events associated interacting with a + * ElementController Constructor controller to handle ui events associated interacting with a * {@link BoardView} */ public ElementController() { @@ -86,6 +90,7 @@ 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; @@ -137,17 +142,8 @@ 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 6e88dc4be..df4def59e 100644 --- a/src/main/java/edu/rpi/legup/controller/RuleController.java +++ b/src/main/java/edu/rpi/legup/controller/RuleController.java @@ -16,6 +16,11 @@ 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 80fdee1af..6e4ce9fd9 100644 --- a/src/main/java/edu/rpi/legup/controller/TreeController.java +++ b/src/main/java/edu/rpi/legup/controller/TreeController.java @@ -11,6 +11,10 @@ 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 12b39cd85..421cea438 100644 --- a/src/main/java/edu/rpi/legup/history/AddTreeElementCommand.java +++ b/src/main/java/edu/rpi/legup/history/AddTreeElementCommand.java @@ -10,6 +10,10 @@ 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; @@ -27,7 +31,10 @@ public AddTreeElementCommand(TreeViewSelection selection) { this.addChild = new HashMap<>(); } - /** Executes an command */ + /** + * Executes the command to add selected tree elements to the tree. Updates the puzzle and tree + * view accordingly + */ @Override public void executeCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); @@ -95,7 +102,10 @@ public String getErrorString() { return null; } - /** Undoes an command */ + /** + * Undoes the command by removing the added tree elements. Updates the puzzle and tree view + * accordingly + */ @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 02dffae44..e3e89ddd0 100644 --- a/src/main/java/edu/rpi/legup/history/ApplyDefaultDirectRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ApplyDefaultDirectRuleCommand.java @@ -10,6 +10,11 @@ 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; @@ -69,7 +74,10 @@ public String getErrorString() { return null; } - /** Executes an command */ + /** + * Executes the command to apply the default rule to the selected tree nodes. Updates the puzzle + * and tree view accordingly. + */ @Override public void executeCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); @@ -109,7 +117,10 @@ public void executeCommand() { puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); } - /** Undoes an command */ + /** + * Undoes the command by removing the applied default rule from the tree nodes. Updates the + * puzzle and tree view accordingly. + */ @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 97192e145..4717b25ae 100644 --- a/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/AutoCaseRuleCommand.java @@ -13,6 +13,11 @@ 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; @@ -46,7 +51,10 @@ public AutoCaseRuleCommand( this.caseTrans = new ArrayList<>(); } - /** Executes an command */ + /** + * Executes the command to apply the case rule to the selected tree node. Updates the puzzle and + * tree view accordingly. + */ @Override public void executeCommand() { Tree tree = getInstance().getTree(); @@ -60,7 +68,7 @@ public void executeCommand() { 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()); @@ -129,7 +137,10 @@ public String getErrorString() { return null; } - /** Undoes an command */ + /** + * Undoes the command by removing the applied case rules from the tree node. Updates the puzzle + * and tree view accordingly + */ @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 35b7bb15b..78027f6ab 100644 --- a/src/main/java/edu/rpi/legup/history/CommandError.java +++ b/src/main/java/edu/rpi/legup/history/CommandError.java @@ -1,10 +1,17 @@ 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."), @@ -18,10 +25,20 @@ 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 f47c0405d..b72f15480 100644 --- a/src/main/java/edu/rpi/legup/history/CommandState.java +++ b/src/main/java/edu/rpi/legup/history/CommandState.java @@ -1,5 +1,9 @@ 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"), @@ -8,10 +12,20 @@ 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 0469685c1..bfd9cd7f5 100644 --- a/src/main/java/edu/rpi/legup/history/DeleteTreeElementCommand.java +++ b/src/main/java/edu/rpi/legup/history/DeleteTreeElementCommand.java @@ -7,6 +7,11 @@ 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; @@ -20,25 +25,33 @@ public DeleteTreeElementCommand(TreeViewSelection selection) { this.selection = selection.copy(); } - /** Executes an command */ + /** Executes the delete command, removing the selected tree elements from the tree. */ @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)); @@ -73,7 +86,7 @@ public String getErrorString() { return null; } - /** Undoes an command */ + /** Undoes the delete command, re-adding the previously deleted tree elements. */ @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 d65f03d66..0d51cc717 100644 --- a/src/main/java/edu/rpi/legup/history/EditDataCommand.java +++ b/src/main/java/edu/rpi/legup/history/EditDataCommand.java @@ -9,10 +9,18 @@ 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 java.awt.*; import java.awt.event.MouseEvent; import java.util.List; +import javax.swing.*; +/** + * 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; @@ -26,7 +34,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 is being edited + * @param selection currently selected tree puzzleElement views that are being edited * @param event mouse event */ public EditDataCommand(ElementView elementView, TreeViewSelection selection, MouseEvent event) { @@ -38,7 +46,7 @@ public EditDataCommand(ElementView elementView, TreeViewSelection selection, Mou this.transition = null; } - /** Executes a command */ + /** Executes the edit data command, modifying the puzzle element and propagating changes */ @SuppressWarnings("unchecked") @Override public void executeCommand() { @@ -54,16 +62,13 @@ 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 { @@ -73,7 +78,6 @@ public void executeCommand() { } Board prevBoard = transition.getParents().get(0).getBoard(); - boardView.getElementController().changeCell(event, puzzleElement); if (prevBoard.getPuzzleElement(selectedPuzzleElement).equalsData(puzzleElement)) { @@ -102,35 +106,50 @@ 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()) { - return CommandError.UNMODIFIABLE_DATA.toString(); - } + } else if (!board.getPuzzleElement(selectedPuzzleElement).isModifiable()) { + flashTreeViewRed(); + 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; } - /** Undoes an command */ + /** 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. */ @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 371284f8c..9eaeb3e75 100644 --- a/src/main/java/edu/rpi/legup/history/History.java +++ b/src/main/java/edu/rpi/legup/history/History.java @@ -6,6 +6,11 @@ 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()); @@ -14,9 +19,8 @@ public class History { private int curIndex; /** - * 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). + * 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. */ public History() { history = new ArrayList<>(); @@ -44,13 +48,17 @@ public void pushChange(ICommand command) { } } - /** Undoes an action */ + /** + * Undoes the last action by calling the undo method of the command at the current index. + * Updates the current index and notifies listeners. + */ 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)); @@ -58,7 +66,10 @@ public void undo() { } } - /** Redoes an action */ + /** + * Redoes the next action by calling the redo method of the command at the current index. + * Updates the current index and notifies listeners. + */ public void redo() { synchronized (lock) { if (curIndex < history.size() - 1) { @@ -72,7 +83,7 @@ public void redo() { } } - /** Clears all actions from the history stack */ + /** Clears all actions from the history stack and resets the current index */ 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 913d9daaf..fe10bd693 100644 --- a/src/main/java/edu/rpi/legup/history/ICommand.java +++ b/src/main/java/edu/rpi/legup/history/ICommand.java @@ -1,7 +1,11 @@ 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 a command */ + /** Executes the command. The specific behavior depends on the implementation */ void execute(); /** @@ -19,9 +23,9 @@ public interface ICommand { */ String getError(); - /** Undoes a command */ + /** Undoes the command. Reverts the changes made by the execute method */ void undo(); - /** Redoes a command */ + /** Redoes the command. Re-applies the changes made by the execute method after undoing */ 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 f464941d6..a752cd53a 100644 --- a/src/main/java/edu/rpi/legup/history/IHistoryListener.java +++ b/src/main/java/edu/rpi/legup/history/IHistoryListener.java @@ -1,15 +1,21 @@ 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 action is pushed onto the edu.rpi.legup.history stack + * Called when a command is pushed onto the history stack. * - * @param command action to push onto the stack + * @param command the command that was pushed onto the stack */ void onPushChange(ICommand command); /** - * Called when an action is undone + * Called when a command 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 @@ -17,13 +23,13 @@ public interface IHistoryListener { void onUndo(boolean isBottom, boolean isTop); /** - * Called when an action is redone + * Called when a command 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 edu.rpi.legup.history is cleared */ + /** Called when the history stack 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 78fefff00..b526c7cf3 100644 --- a/src/main/java/edu/rpi/legup/history/IHistorySubject.java +++ b/src/main/java/edu/rpi/legup/history/IHistorySubject.java @@ -2,25 +2,33 @@ 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 + * Adds a history listener to receive updates about changes in the command history. * - * @param listener listener to add + * @param listener the listener to add */ void addHistoryListener(IHistoryListener listener); /** - * Removes a history listener + * Removes a history listener, so it no longer receives updates about changes in the command + * history. * - * @param listener listener to remove + * @param listener the listener to remove */ void removeHistoryListener(IHistoryListener listener); /** - * Notifies listeners + * Notifies all registered listeners about a change in the command history. * - * @param algorithm algorithm to notify the listeners with + * @param algorithm a Consumer function that takes an IHistoryListener and performs some action + * with it */ 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 71d072328..0e1ba1b0d 100644 --- a/src/main/java/edu/rpi/legup/history/InvalidCommandStateTransition.java +++ b/src/main/java/edu/rpi/legup/history/InvalidCommandStateTransition.java @@ -1,7 +1,18 @@ 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 f234a0884..2d180465b 100644 --- a/src/main/java/edu/rpi/legup/history/MergeCommand.java +++ b/src/main/java/edu/rpi/legup/history/MergeCommand.java @@ -10,6 +10,10 @@ 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; @@ -24,7 +28,7 @@ public MergeCommand(TreeViewSelection selection) { this.transition = null; } - /** Executes an command */ + /** Executes the merge command */ @Override public void executeCommand() { List selectedViews = selection.getSelectedViews(); @@ -71,7 +75,7 @@ public void executeCommand() { puzzle.notifyTreeListeners(listener -> listener.onTreeSelectionChanged(newSelection)); } - /** Undoes an command */ + /** Undoes the merge 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 3768e3cbd..54e58c667 100644 --- a/src/main/java/edu/rpi/legup/history/PuzzleCommand.java +++ b/src/main/java/edu/rpi/legup/history/PuzzleCommand.java @@ -1,18 +1,23 @@ 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 an command */ + /** Executes the command if it can be executed */ @Override public final void execute() { if (canExecute()) { @@ -21,7 +26,7 @@ public final void execute() { } } - /** Determines whether this command can be executed */ + /** Determines whether the command can be executed by checking the error state */ @Override public final boolean canExecute() { cachedError = getError(); @@ -52,13 +57,19 @@ public final String getError() { */ public abstract String getErrorString(); - /** Executes an command */ + /** + * Executes the command. This method must be implemented by subclasses to define the command's + * execution behavior. + */ public abstract void executeCommand(); - /** Undoes an command */ + /** + * Undoes the command. This method must be implemented by subclasses to define the command's + * undo behavior. + */ public abstract void undoCommand(); - /** Redoes an command */ + /** Redoes the command. This method is called if the command was previously undone. */ public void redoCommand() { if (state == CommandState.UNDOED) { executeCommand(); @@ -68,7 +79,7 @@ public void redoCommand() { } } - /** Undoes an command */ + /** Undoes the command if it was executed or redone */ @Override public final void undo() { if (state == CommandState.EXECUTED || state == CommandState.REDOED) { @@ -79,7 +90,7 @@ public final void undo() { } } - /** Redoes an command */ + /** Redoes the command if it was previously undone. */ 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 7737ecfd3..3f534bcda 100644 --- a/src/main/java/edu/rpi/legup/history/ValidateCaseRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ValidateCaseRuleCommand.java @@ -12,6 +12,10 @@ 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; @@ -33,7 +37,7 @@ public ValidateCaseRuleCommand(TreeViewSelection selection, CaseRule caseRule) { this.addNode = new HashMap<>(); } - /** Executes an command */ + /** Executes the command to validate the CaseRule */ @Override public void executeCommand() { Tree tree = getInstance().getTree(); @@ -105,7 +109,7 @@ public String getErrorString() { return null; } - /** Undoes an command */ + /** Undoes the validation command, restoring the previous state */ @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 8737b4008..0a8b9414f 100644 --- a/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ValidateContradictionRuleCommand.java @@ -10,6 +10,11 @@ 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; @@ -31,7 +36,7 @@ public ValidateContradictionRuleCommand(TreeViewSelection selection, Contradicti this.addTran = new HashMap<>(); } - /** Executes a command */ + /** Executes the command to validate and apply the ContradictionRule. */ @Override public void executeCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); @@ -84,7 +89,11 @@ public void executeCommand() { final TreeElement finalTreeElement; if (firstSelectedView.getType() == TreeElementType.NODE) { TreeNodeView nodeView = (TreeNodeView) firstSelectedView; - finalTreeElement = nodeView.getChildrenViews().get(0).getTreeElement(); + if (!nodeView.getChildrenViews().isEmpty()) { + finalTreeElement = nodeView.getChildrenViews().get(0).getTreeElement(); + } else { + finalTreeElement = null; + } } else { TreeTransitionView transitionView = (TreeTransitionView) firstSelectedView; if (transitionView.getChildView() != null) { @@ -125,7 +134,7 @@ public String getErrorString() { return null; } - /** Undoes a command */ + /** Undoes the validation command, restoring the previous state. */ @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 d9c063464..49bf378ee 100644 --- a/src/main/java/edu/rpi/legup/history/ValidateDirectRuleCommand.java +++ b/src/main/java/edu/rpi/legup/history/ValidateDirectRuleCommand.java @@ -9,8 +9,16 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +/** + * 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; @@ -30,7 +38,7 @@ public ValidateDirectRuleCommand(TreeViewSelection selection, DirectRule rule) { this.addNode = new HashMap<>(); } - /** Executes an command */ + /** Executes the command to validate and apply the DirectRule. */ @Override public void executeCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); @@ -42,14 +50,15 @@ 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(); + TreeTransition transition = transitionView.getTreeElement(); oldRules.put(transition, transition.getRule()); transition.setRule(newRule); @@ -66,17 +75,41 @@ public void executeCommand() { final TreeNode finalNode = childNode; puzzle.notifyTreeListeners(listener -> listener.onTreeElementAdded(finalNode)); } - newSelection.addToSelection(treeView.getElementView(childNode)); + + TreeElementView childView = treeView.getElementView(childNode); + if (childView == null) { + LOGGER.error("Child view is null for child node: " + childNode); + continue; + } + newSelection.addToSelection(childView); } + 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)); } @@ -110,7 +143,7 @@ public String getErrorString() { return null; } - /** Undoes an command */ + /** Undoes the validation command, restoring the previous state. */ @Override public void undoCommand() { Tree tree = GameBoardFacade.getInstance().getTree(); @@ -126,6 +159,7 @@ public void undoCommand() { 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 7971c95af..da8f76ddd 100644 --- a/src/main/java/edu/rpi/legup/model/Puzzle.java +++ b/src/main/java/edu/rpi/legup/model/Puzzle.java @@ -35,6 +35,11 @@ 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()); @@ -53,7 +58,6 @@ 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() { @@ -65,12 +69,15 @@ 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 ", ""); @@ -79,6 +86,11 @@ 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 @@ -95,9 +107,6 @@ private void registerPuzzleElements() { case PLACEABLE: this.addPlaceableElement((PlaceableElement) element); break; - case NONPLACEABLE: - this.addNonPlaceableElement((NonPlaceableElement) element); - break; default: break; } @@ -108,19 +117,15 @@ 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 ", ""); @@ -129,6 +134,10 @@ private void registerRules() { for (Class c : possRules) { + String classPackageName = c.getPackage().getName(); + if (!classPackageName.contains(".rules")) { + continue; + } System.out.println("possible rule: " + c.getName()); // check that the rule is not abstract @@ -163,14 +172,6 @@ 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); } @@ -199,10 +200,10 @@ public boolean isValidDimensions(int rows, int columns) { } /** - * Checks if the given array of statements is valid text input for the given puzzle + * Checks if the provided text input is valid for the puzzle. * - * @param statements - * @return + * @param statements array of statements to check + * @return true if input is valid, false otherwise */ public boolean isValidTextInput(String[] statements) { return statements.length > 0; @@ -334,14 +335,15 @@ 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 * @@ -360,14 +362,15 @@ 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 * @@ -580,9 +583,10 @@ public void setFactory(ElementFactory factory) { } /** - * Adds a board listener + * Adds a board listener to the list of listeners. This allows the puzzle to notify the listener + * about changes to the board. * - * @param listener listener to add + * @param listener The IBoardListener to be added to the list of listeners. */ @Override public void addBoardListener(IBoardListener listener) { @@ -590,9 +594,10 @@ public void addBoardListener(IBoardListener listener) { } /** - * Removes a board listener + * Removes a board listener from the list of listeners. This prevents the puzzle from notifying + * the listener about future changes to the board. * - * @param listener listener to remove + * @param listener The IBoardListener to be removed from the list of listeners. */ @Override public void removeBoardListener(IBoardListener listener) { @@ -600,9 +605,11 @@ public void removeBoardListener(IBoardListener listener) { } /** - * Notifies listeners + * Notifies all registered board listeners about changes. The provided algorithm is applied to + * each listener to process the notification. * - * @param algorithm algorithm to notify the listeners with + * @param algorithm A Consumer function that takes an IBoardListener and performs operations to + * notify the listener. */ @Override public void notifyBoardListeners(Consumer algorithm) { @@ -610,9 +617,10 @@ public void notifyBoardListeners(Consumer algorithm) { } /** - * Adds a board listener + * Adds a tree listener to the list of listeners. This allows the puzzle to notify the listener + * about changes to the tree. * - * @param listener listener to add + * @param listener The ITreeListener to be added to the list of listeners. */ @Override public void addTreeListener(ITreeListener listener) { @@ -620,9 +628,10 @@ public void addTreeListener(ITreeListener listener) { } /** - * Removes a tree listener + * Removes a tree listener from the list of listeners. This prevents the puzzle from notifying + * the listener about future changes to the tree. * - * @param listener listener to remove + * @param listener The ITreeListener to be removed from the list of listeners. */ @Override public void removeTreeListener(ITreeListener listener) { @@ -630,9 +639,11 @@ public void removeTreeListener(ITreeListener listener) { } /** - * Notifies listeners + * Notifies all registered tree listeners about changes. The provided algorithm is applied to + * each listener to process the notification. * - * @param algorithm algorithm to notify the listeners with + * @param algorithm A Consumer function that takes an ITreeListener and performs operations to + * notify the listener. */ @Override public void notifyTreeListeners(Consumer algorithm) { @@ -640,9 +651,10 @@ public void notifyTreeListeners(Consumer algorithm) { } /** - * Check if the puzzle is valid + * Checks if the puzzle is valid. The implementation of this method can vary based on the + * specific criteria for puzzle validity. * - * @return if the puzzle is valid + * @return true if the puzzle is valid, false otherwise. */ 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 a052a736a..13107bb54 100644 --- a/src/main/java/edu/rpi/legup/model/PuzzleExporter.java +++ b/src/main/java/edu/rpi/legup/model/PuzzleExporter.java @@ -20,6 +20,11 @@ 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()); @@ -91,8 +96,23 @@ 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); @@ -100,6 +120,13 @@ 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 0902478db..9d16d94e9 100644 --- a/src/main/java/edu/rpi/legup/model/PuzzleImporter.java +++ b/src/main/java/edu/rpi/legup/model/PuzzleImporter.java @@ -13,6 +13,12 @@ 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()); @@ -46,6 +52,14 @@ 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 @@ -55,10 +69,10 @@ public void initializePuzzle(String[] statements) } /** - * Initializes the puzzle attributes + * Initializes the puzzle attributes from the XML document node * - * @param node xml document node - * @throws InvalidFileFormatException if file is invalid + * @param node the XML document node representing the puzzle + * @throws InvalidFileFormatException if the file format is invalid */ public void initializePuzzle(Node node) throws InvalidFileFormatException { if (node.getNodeName().equalsIgnoreCase("puzzle")) { @@ -111,22 +125,30 @@ public void initializePuzzle(Node node) throws InvalidFileFormatException { } /** - * Creates the board for building + * Initializes the board with the specified number of rows and columns. * - * @param rows number of rows on the puzzle - * @param columns number of columns on the puzzle - * @throws RuntimeException if board can not be created + * @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 */ public abstract void initializeBoard(int rows, int columns); /** - * Creates an empty board for building + * Initializes the board from the XML document node. * - * @param node xml document node - * @throws InvalidFileFormatException if file is invalid + * @param node the XML document node representing the board + * @throws InvalidFileFormatException if the file format 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; @@ -368,6 +390,14 @@ 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) { @@ -411,6 +441,10 @@ 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); @@ -420,7 +454,7 @@ protected void createDefaultTree() { } /** - * Gets the result of building the Puzzle + * Gets the result of building the Puzzle object. * * @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 c4c1ed273..df2a9bc5b 100644 --- a/src/main/java/edu/rpi/legup/model/RegisterPuzzle.java +++ b/src/main/java/edu/rpi/legup/model/RegisterPuzzle.java @@ -5,6 +5,7 @@ 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 8b75d075d..ed47f6523 100644 --- a/src/main/java/edu/rpi/legup/model/elements/Element.java +++ b/src/main/java/edu/rpi/legup/model/elements/Element.java @@ -4,6 +4,10 @@ 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; @@ -17,6 +21,14 @@ 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; @@ -26,6 +38,10 @@ 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)); @@ -47,30 +63,65 @@ 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 dff4fe04f..142427efe 100644 --- a/src/main/java/edu/rpi/legup/model/elements/ElementType.java +++ b/src/main/java/edu/rpi/legup/model/elements/ElementType.java @@ -1,6 +1,7 @@ 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 + PLACEABLE + // NONPLACEABLE COMBINED ALL PLACEABLE AND NONPLACEABLE INTO JUST ONE CATEGORY } 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 4ab0ab509..5c4e96556 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.NONPLACEABLE; - } -} +// 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; +// } +// } 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 133658700..820dc08dd 100644 --- a/src/main/java/edu/rpi/legup/model/elements/PlaceableElement.java +++ b/src/main/java/edu/rpi/legup/model/elements/PlaceableElement.java @@ -1,6 +1,19 @@ 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 368ecc8d1..916719ebc 100644 --- a/src/main/java/edu/rpi/legup/model/elements/RegisterElement.java +++ b/src/main/java/edu/rpi/legup/model/elements/RegisterElement.java @@ -3,6 +3,11 @@ 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 d8bdf5199..9e7d93441 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/Board.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/Board.java @@ -5,6 +5,10 @@ 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; @@ -31,21 +35,24 @@ public Board(int size) { } /** - * Gets a specific {@link PuzzleElement} on this board. + * Gets a specific {@link PuzzleElement} from the board. * - * @param puzzleElement equivalent puzzleElement - * @return equivalent puzzleElement on this board + * @param puzzleElement the puzzle element to retrieve + * @return the puzzle element at the corresponding index, or null if not found */ 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 new puzzleElement at the index + * @param puzzleElement the puzzleElement to set at the index */ public void setPuzzleElement(int index, PuzzleElement puzzleElement) { if (index < puzzleElements.size()) { @@ -54,7 +61,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 fa3625a43..07cc2947d 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/CaseBoard.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/CaseBoard.java @@ -5,49 +5,106 @@ 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 bfc785bdd..131feb122 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/ElementFactory.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/ElementFactory.java @@ -5,6 +5,9 @@ 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 9593690ce..7338132f8 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/GridBoard.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/GridBoard.java @@ -6,6 +6,11 @@ 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 a33c3ec80..671b89e9b 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/GridCell.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/GridCell.java @@ -2,6 +2,13 @@ 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 c41d5e1b2..79027d9d1 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/GridRegion.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/GridRegion.java @@ -3,6 +3,12 @@ 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; @@ -24,7 +30,7 @@ public void addCell(T cell) { /** * Removes the cell from the region * - * @param cell cell to be remove from the region + * @param cell cell to be removed from the region */ public void removeCell(T cell) { regionCells.remove(cell); @@ -47,9 +53,4 @@ 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 4ce030a04..eca6f3be4 100644 --- a/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java +++ b/src/main/java/edu/rpi/legup/model/gameboard/PuzzleElement.java @@ -3,11 +3,19 @@ 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; @@ -17,6 +25,7 @@ public PuzzleElement() { this.index = -1; this.data = null; this.isModifiable = true; + this.isModifiableCaseRule = true; this.isModified = false; this.isGiven = false; this.isValid = true; @@ -73,6 +82,24 @@ 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 461128562..7ba886fd4 100644 --- a/src/main/java/edu/rpi/legup/model/observer/IBoardListener.java +++ b/src/main/java/edu/rpi/legup/model/observer/IBoardListener.java @@ -4,6 +4,10 @@ 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. @@ -13,7 +17,7 @@ public interface IBoardListener { void onTreeElementChanged(TreeElement treeElement); /** - * Called when the a case board has been added to the view. + * Called when 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 c7bf13141..6cdcd7dc2 100644 --- a/src/main/java/edu/rpi/legup/model/observer/IBoardSubject.java +++ b/src/main/java/edu/rpi/legup/model/observer/IBoardSubject.java @@ -2,6 +2,10 @@ 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. @@ -18,7 +22,7 @@ public interface IBoardSubject { void removeBoardListener(IBoardListener listener); /** - * Notifies all of the listeners using the specified algorithm. + * Notifies all 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 d5e7fdb2d..713b464e2 100644 --- a/src/main/java/edu/rpi/legup/model/observer/ITreeListener.java +++ b/src/main/java/edu/rpi/legup/model/observer/ITreeListener.java @@ -3,6 +3,10 @@ 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 66d5d9a5e..f7c033529 100644 --- a/src/main/java/edu/rpi/legup/model/observer/ITreeSubject.java +++ b/src/main/java/edu/rpi/legup/model/observer/ITreeSubject.java @@ -2,9 +2,13 @@ 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 board listener. + * Adds a tree listener. * * @param listener listener to add */ @@ -18,7 +22,7 @@ public interface ITreeSubject { void removeTreeListener(ITreeListener listener); /** - * Notifies all of the listeners using the specified algorithm. + * Notifies all the tree 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 ab2cc8f0d..aac4fd352 100644 --- a/src/main/java/edu/rpi/legup/model/rules/CaseRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/CaseRule.java @@ -11,6 +11,11 @@ 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 b38a95fd2..c80a551ec 100644 --- a/src/main/java/edu/rpi/legup/model/rules/ContradictionRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/ContradictionRule.java @@ -6,6 +6,11 @@ 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 d550bc02c..8efe119b6 100644 --- a/src/main/java/edu/rpi/legup/model/rules/DirectRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/DirectRule.java @@ -7,6 +7,10 @@ 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 f7badcd8b..86c63fb7c 100644 --- a/src/main/java/edu/rpi/legup/model/rules/MergeRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/MergeRule.java @@ -10,6 +10,10 @@ 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() { @@ -23,7 +27,7 @@ public MergeRule() { /** * Checks whether the transition logically follows from the parent node using this rule. This - * method is the one that should overridden in child classes + * method is the one that should have 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 c1fe0b88c..0ee2a686e 100644 --- a/src/main/java/edu/rpi/legup/model/rules/RegisterRule.java +++ b/src/main/java/edu/rpi/legup/model/rules/RegisterRule.java @@ -2,6 +2,10 @@ 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 f70bb2889..edcab550b 100644 --- a/src/main/java/edu/rpi/legup/model/rules/Rule.java +++ b/src/main/java/edu/rpi/legup/model/rules/Rule.java @@ -8,6 +8,11 @@ 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; @@ -159,6 +164,11 @@ 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 06aa1844b..7eee80ea3 100644 --- a/src/main/java/edu/rpi/legup/model/rules/RuleType.java +++ b/src/main/java/edu/rpi/legup/model/rules/RuleType.java @@ -1,5 +1,9 @@ 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 a0746db87..545976fd1 100644 --- a/src/main/java/edu/rpi/legup/model/tree/Tree.java +++ b/src/main/java/edu/rpi/legup/model/tree/Tree.java @@ -1,11 +1,17 @@ package edu.rpi.legup.model.tree; +import edu.rpi.legup.controller.TreeController; import edu.rpi.legup.model.gameboard.Board; +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; @@ -24,6 +30,12 @@ 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); @@ -31,13 +43,12 @@ public TreeTransition addNewTransition(TreeNode treeNode) { return transition; } - public TreeNode addNode(TreeTransition transition) { - TreeNode treeNode = new TreeNode(transition.getBoard().copy()); - transition.setChildNode(treeNode); - treeNode.setParent(transition); - return treeNode; - } - + /** + * Adds a tree element (node or transition) to the tree. + * + * @param element the tree element to add + * @return the added tree element + */ public TreeElement addTreeElement(TreeElement element) { if (element.getType() == TreeElementType.NODE) { TreeNode treeNode = (TreeNode) element; @@ -46,30 +57,55 @@ public TreeElement addTreeElement(TreeElement element) { } else { TreeTransition transition = (TreeTransition) element; Board copyBoard = transition.board.copy(); - copyBoard.setModifiable(false); + copyBoard.setModifiable(true); 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); } } @@ -96,10 +132,10 @@ public Set getLeafTreeElements() { } /** - * Gets a Set of TreeNodes that are leaf nodes from the sub tree rooted at the specified node + * Gets a Set of TreeNodes that are leaf nodes from the subtree rooted at the specified node * * @param node node that is input - * @return Set of TreeNodes that are leaf nodes from the sub tree + * @return Set of TreeNodes that are leaf nodes from the subtree */ 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 59f75acf3..8c865b89b 100644 --- a/src/main/java/edu/rpi/legup/model/tree/TreeElement.java +++ b/src/main/java/edu/rpi/legup/model/tree/TreeElement.java @@ -2,6 +2,10 @@ 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; @@ -24,7 +28,7 @@ public TreeElement(TreeElementType type) { public abstract boolean isContradictoryBranch(); /** - * Recursively determines if the sub-tree rooted at this tree puzzleElement is valid by checking + * Recursively determines if the subtree 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 67437a535..eb585329e 100644 --- a/src/main/java/edu/rpi/legup/model/tree/TreeElementType.java +++ b/src/main/java/edu/rpi/legup/model/tree/TreeElementType.java @@ -1,5 +1,9 @@ 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 a2ac7cb21..d4095afa5 100644 --- a/src/main/java/edu/rpi/legup/model/tree/TreeNode.java +++ b/src/main/java/edu/rpi/legup/model/tree/TreeNode.java @@ -4,6 +4,11 @@ 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; @@ -56,9 +61,9 @@ public boolean isValidBranch() { } /** - * Gets all of the ancestors of this node + * Gets a list of the ancestors of this node * - * @return list of all of the ancestors for this node + * @return list of all the ancestors for this node */ public List getAncestors() { List ancestors = new ArrayList<>(); @@ -326,6 +331,10 @@ 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 e79cd4b96..10c8fc7a0 100644 --- a/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java +++ b/src/main/java/edu/rpi/legup/model/tree/TreeTransition.java @@ -8,6 +8,11 @@ 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; @@ -332,6 +337,24 @@ 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 773513cda..7b70a43a1 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/Binary.java @@ -36,18 +36,12 @@ public Board generatePuzzle(int difficulty) { return null; } - // /** - // * 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; - // } - + /** + * Determines if the current board is a valid state + * + * @param board board to check for validity + * @return true if board is valid, false otherwise + */ @Override public boolean isBoardComplete(Board board) { BinaryBoard binaryBoard = (BinaryBoard) board; @@ -66,6 +60,23 @@ 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 35c37b1a1..d5d65ed26 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,6 @@ 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,6 +19,13 @@ 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() @@ -32,6 +38,12 @@ 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++) { @@ -41,6 +53,26 @@ 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++) { @@ -50,6 +82,12 @@ 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++) { @@ -59,17 +97,13 @@ public ArrayList getColTypes(int colNum) { return col; } - public Set getCol(int colNum) { - Set col = new HashSet<>(); - for (int i = 0; i < size; i++) { - col.add(getCell(colNum, i)); - } - return col; - } - + /** + * Get a copy of the binary board + * + * @return copy of current BinaryBoard + */ @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 9007215ad..0edb8444e 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCell.java @@ -1,13 +1,26 @@ 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 { - public BinaryCell(int valueInt, Point location) { - super(valueInt, location); + /** + * 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); } + /** + * Gets the type of this BinaryCell + * + * @return type of BinaryCell + */ public BinaryType getType() { switch (data) { case 0: @@ -24,6 +37,11 @@ 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()); @@ -32,4 +50,32 @@ 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; + } + } } 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 890c26656..a819177d6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryCellFactory.java @@ -10,7 +10,14 @@ 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")) { @@ -45,6 +52,13 @@ 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 0bad559d9..5ec6669a0 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryController.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryController.java @@ -6,6 +6,15 @@ 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; @@ -30,13 +39,13 @@ public void changeCell(MouseEvent e, PuzzleElement data) { } } else { if (e.getButton() == MouseEvent.BUTTON3) { - if (cell.getData() == 0) { + if (cell.getData() == 2) { data.setData(1); } else { if (cell.getData() == 1) { - data.setData(2); - } else { data.setData(0); + } else { + data.setData(2); } } } 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 9ac99c958..74a761e89 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryElementView.java @@ -7,6 +7,8 @@ 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); @@ -22,99 +24,64 @@ 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) { - 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); - } - } - } + drawCell(graphics2D, GIVEN_COLOR); } + /** + * 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) { + + if (type == BinaryType.ZERO || type == BinaryType.ONE) { 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); - - } 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); - } - } } } + + /** + * 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); + } } 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 cd58314b6..c032b63a9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryExporter.java @@ -10,6 +10,13 @@ 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; @@ -26,7 +33,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() != -2) { + if (cell.getData() != BinaryType.UNKNOWN.toValue()) { 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 2fc5b09ef..419789060 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryImporter.java @@ -12,16 +12,33 @@ 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); @@ -48,12 +65,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); @@ -76,7 +93,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++) { @@ -110,6 +127,12 @@ 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 6e3413d7a..7e8e162e6 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryType.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryType.java @@ -1,10 +1,20 @@ 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, - ONE, - UNKNOWN; + ZERO, // Enum constant 0 + ONE, // Enum constant 1 + UNKNOWN; // Enum constant 2 + /** + * 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 b11554f28..b428ce32c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/BinaryView.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/BinaryView.java @@ -7,6 +7,14 @@ 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 deleted file mode 100644 index 8b1378917..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/binary/elements/BlankTile.java +++ /dev/null @@ -1 +0,0 @@ - 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 8b1378917..dc3d75e00 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 +1,13 @@ +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"); + } +} 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 new file mode 100644 index 000000000..e32a2d12f --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/elements/UnknownTile.java @@ -0,0 +1,13 @@ +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"); + } +} 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 new file mode 100644 index 000000000..54db0ee0b --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/elements/binary_elements_reference_sheet.txt @@ -0,0 +1,2 @@ +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 e38c6b78d..6ced37a9c 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,8 +14,9 @@ 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"); } @@ -31,23 +32,27 @@ public CompleteRowColumnDirectRule() { @Override public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { BinaryBoard origBoard = (BinaryBoard) transition.getParents().get(0).getBoard(); - ContradictionRule contraRule = new UnbalancedRowOrColumnContradictionRule(); + ContradictionRule contraRule = new UnbalancedRowColumnContradictionRule(); BinaryCell binaryCell = (BinaryCell) puzzleElement; BinaryBoard modified = origBoard.copy(); - BinaryCell c = (BinaryCell) modified.getPuzzleElement(puzzleElement); - // 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) { + // 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) { return null; } - return "Grouping of Three Ones or Zeros not found"; + return "Unbalanced row/column 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/EliminateTheImpossibleDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/EliminateTheImpossibleDirectRule.java new file mode 100644 index 000000000..df84dd540 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/EliminateTheImpossibleDirectRule.java @@ -0,0 +1,204 @@ +// 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/OneTileGapDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java deleted file mode 100644 index 2e1e96fa5..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneTileGapDirectRule.java +++ /dev/null @@ -1,64 +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.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 new file mode 100644 index 000000000..728ec8e10 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/PreventTrioDirectRule.java @@ -0,0 +1,59 @@ +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; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/binary/rules/DuplicateRowsOrColumnsContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/RepeatedRowColumnContradictionRule.java similarity index 60% rename from src/main/java/edu/rpi/legup/puzzle/binary/rules/DuplicateRowsOrColumnsContradictionRule.java rename to src/main/java/edu/rpi/legup/puzzle/binary/rules/RepeatedRowColumnContradictionRule.java index 8b0d88ae4..a7a4ced12 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/DuplicateRowsOrColumnsContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/RepeatedRowColumnContradictionRule.java @@ -8,30 +8,39 @@ import edu.rpi.legup.puzzle.binary.BinaryType; import java.util.ArrayList; -public class DuplicateRowsOrColumnsContradictionRule extends ContradictionRule { +public class RepeatedRowColumnContradictionRule 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 DuplicateRowsOrColumnsContradictionRule() { + public RepeatedRowColumnContradictionRule() { super( "BINA-CONT-0003", - "Duplicate Rows Or Columns", - "There must not be two rows or two columns that are duplicates", - "edu/rpi/legup/images/binary/rules/DuplicateRowOrColumnContradictionRule.png"); + "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"); } + /** + * 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; @@ -39,10 +48,11 @@ 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/SaveBlockerDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/SaveBlockerDirectRule.java new file mode 100644 index 000000000..6cc28446d --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/SaveBlockerDirectRule.java @@ -0,0 +1,59 @@ +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 deleted file mode 100644 index dc2f07c8b..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/SurroundPairDirectRule.java +++ /dev/null @@ -1,48 +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.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 deleted file mode 100644 index 075642246..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/ThreeAdjacentContradictionRule.java +++ /dev/null @@ -1,127 +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 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 new file mode 100644 index 000000000..fc5cd4165 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/TrioContradictionRule.java @@ -0,0 +1,185 @@ +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/UnbalancedRowOrColumnContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContradictionRule.java similarity index 64% rename from src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowOrColumnContradictionRule.java rename to src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContradictionRule.java index 5089b3b5f..1d00f4da9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowOrColumnContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UnbalancedRowColumnContradictionRule.java @@ -8,20 +8,29 @@ import edu.rpi.legup.puzzle.binary.BinaryType; import java.util.Set; -public class UnbalancedRowOrColumnContradictionRule extends ContradictionRule { +public class UnbalancedRowColumnContradictionRule 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 UnbalancedRowOrColumnContradictionRule() { + public UnbalancedRowColumnContradictionRule() { super( "BINA-CONT-0002", - "Unbalanced Row Or Column", + "Unbalanced Row/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; @@ -41,11 +50,12 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } } - if (rowNumZeros == size / 2 && rowNumOnes == size / 2) { - return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; + // if there are too many zeros or ones in this row + if (rowNumZeros > size / 2 || rowNumOnes > size / 2) { + return null; } - Set col = binaryBoard.getCol(cell.getLocation().x); + Set col = binaryBoard.getColCells(cell.getLocation().x); size = col.size(); int colNumZeros = 0; @@ -59,10 +69,11 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { } } - if (colNumZeros == size / 2 && colNumOnes == size / 2) { - return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; + // if there are too many zeros or ones in this column + if (colNumZeros > size / 2 || colNumOnes > size / 2) { + return null; } - return null; + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; } } 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 new file mode 100644 index 000000000..0250a4c74 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/UniqueRowColumnDirectRule.java @@ -0,0 +1,299 @@ +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.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 new file mode 100644 index 000000000..ef5fc0e7e --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/WastedBlockerContradictionRule.java @@ -0,0 +1,180 @@ +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/OneOrZeroCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ZeroOrOneCaseRule.java similarity index 62% rename from src/main/java/edu/rpi/legup/puzzle/binary/rules/OneOrZeroCaseRule.java rename to src/main/java/edu/rpi/legup/puzzle/binary/rules/ZeroOrOneCaseRule.java index 70549cd72..f47fe55b9 100644 --- a/src/main/java/edu/rpi/legup/puzzle/binary/rules/OneOrZeroCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/binary/rules/ZeroOrOneCaseRule.java @@ -11,16 +11,23 @@ import java.util.ArrayList; import java.util.List; -public class OneOrZeroCaseRule extends CaseRule { +public class ZeroOrOneCaseRule extends CaseRule { - public OneOrZeroCaseRule() { + public ZeroOrOneCaseRule() { super( "BINA-CASE-0001", - "One or Zero", - "Each blank cell is either a one or a zero.", - "edu/rpi/legup/images/binary/rules/OneOrZeroCaseRule.png"); + "Zero Or One", + "Each blank cell is either a zero or a one", + "edu/rpi/legup/images/binary/rules/ZeroOrOneCaseRule.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(); @@ -46,12 +53,19 @@ public String checkRuleRaw(TreeTransition transition) { if (!((mod1.getType() == BinaryType.ZERO && mod2.getType() == BinaryType.ONE) || (mod2.getType() == BinaryType.ZERO && mod1.getType() == BinaryType.ONE))) { return super.getInvalidUseOfRuleMessage() - + ": This case rule must an empty white and black cell."; + + ": This case rule must modify an empty 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(); @@ -65,24 +79,44 @@ 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.ZERO.toValue()); + data1.setData(BinaryType.ONE.toValue()); case1.addModifiedData(data1); cases.add(case1); Board case2 = board.copy(); PuzzleElement data2 = case2.getPuzzleElement(puzzleElement); - data2.setData(BinaryType.ONE.toValue()); + data2.setData(BinaryType.ZERO.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/binary_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/binary/rules/binary_reference_sheet.txt index c8cb0d1b9..619d183a5 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,9 +1,11 @@ -BINA-BASC-0001 : SurroundPairDirectRule -BINA-BASC-0002 : OneTileGapDirectRule -BINA-BASC-0003 : CompleteRowColumnDirectRule +BINA-BASC-0001 : PreventTrioContradictionRule +BINA-BASC-0002 : CompleteRowColumnDirectRule +BINA-BASC-0003 : SaveBlockerDirectRule +BINA-BASC-0004 : UniqueRowColumnDirectRule -BINA-CONT-0001 : ThreeAdjacentContradictionRule -BINA-CONT-0002 : UnbalancedRowOrColumnContradictionRule -BINA-CONT-0003 : DuplicateRowsOrColumnsContradictionRule +BINA-CONT-0001 : TrioContradictionRule +BINA-CONT-0002 : UnbalancedRowColumnContradictionRule +BINA-CONT-0003 : RepeatedRowColumnContradictionRule +BINA-CONT-0004 : WastedBlockerContradictionRule -BINA-CASE-0001 : OneOrZeroCaseRule \ No newline at end of file +BINA-CASE-0001 : ZeroOrOneCaseRule \ 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 a9e5aa2df..40c5e4a54 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-PLAC-0001": + case "FPIX-ELEM-0001": this.setCellType(FillapixCellType.BLACK); break; - case "FPIX-PLAC-0002": + case "FPIX-ELEM-0004": this.setCellType(FillapixCellType.WHITE); break; - case "FPIX-UNPL-0001": + case "FPIX-ELEM-0002": 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 1d7c038a3..a6993778d 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-PLAC-0001", + "FPIX-ELEM-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 e869aeaf9..5852c1ad7 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class NumberTile extends NonPlaceableElement { +public class NumberTile extends PlaceableElement { private int object_num; public NumberTile() { super( - "FPIX-UNPL-0001", + "FPIX-ELEM-0002", "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 6778c1758..82d0dffb9 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class UnknownTile extends NonPlaceableElement { +public class UnknownTile extends PlaceableElement { public UnknownTile() { super( - "FPIX-UNPL-0002", + "FPIX-ELEM-0003", "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 67065a7e9..b2eedfc09 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-PLAC-0002", + "FPIX-ELEM-0004", "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 0409fa800..1aece4b97 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,5 +1,4 @@ -FPIX-PLAC-0001 : BlackTile -FPIX-PLAC-0002 : WhiteTile - -FPIX-UNPL-0001 : NumberTile -FPIX-UNPL-0002 : UnknownTile \ No newline at end of file +FPIX-ELEM-0001 : BlackTile +FPIX-ELEM-0002 : NumberTile +FPIX-ELEM-0003 : UnknownTile +FPIX-ELEM-0004 : WhiteTile \ 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 860a6c011..f0194bd39 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,6 +37,9 @@ 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 7db833f76..f8bb2d4f5 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,6 +45,9 @@ 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 ab95c4658..a73806cd7 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 217ef79a8..21084b8c7 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 placable + * Gets the number of adjacent cells that are placeable * * @param cell specified cell - * @return number of adjacent cells that are placable + * @return number of adjacent cells that are placeable */ - public int getNumPlacble(LightUpCell cell) { + public int getNumPlaceable(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 8adf84cb4..6d890e67b 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-PLAC-0001": + case "LTUP-ELEM-0002": this.data = -4; break; - case "LTUP-UNPL-0002": + case "LTUP-ELEM-0001": this.data = -1; break; - case "LTUP-UNPL-0003": + case "LTUP-ELEM-0004": this.data = -2; break; - case "LTUP-UNPL-0001": + case "LTUP-ELEM-0003": 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 2ddb4f754..eed3795d7 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class BlackTile extends NonPlaceableElement { +public class BlackTile extends PlaceableElement { public BlackTile() { super( - "LTUP-UNPL-0002", + "LTUP-ELEM-0001", "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 d238baa56..61ebac3d0 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-PLAC-0001", + "LTUP-ELEM-0002", "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 ae314a4cf..26f9be46c 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class NumberTile extends NonPlaceableElement { +public class NumberTile extends PlaceableElement { int object_number; // Follow the default format and resolves the NoSuchMethod error public NumberTile() { super( - "LTUP-UNPL-0001", + "LTUP-ELEM-0003", "Number Tile", "The number tile", "edu/rpi/legup/images/lightup/1.gif"); @@ -17,7 +17,7 @@ public NumberTile() { public NumberTile(int num) { super( - "LTUP-UNPL-0001", + "LTUP-ELEM-0003", "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 24d420fe8..a724be600 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class UnknownTile extends NonPlaceableElement { +public class UnknownTile extends PlaceableElement { public UnknownTile() { super( - "LTUP-UNPL-0003", + "LTUP-ELEM-0004", "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 new file mode 100644 index 000000000..93c97de1c --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/lightup/elements/lightup_elements_reference_sheet.txt @@ -0,0 +1,4 @@ +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 4ba754731..53efb6587 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,6 +44,10 @@ 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 490122874..f73a34b2d 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,6 +47,11 @@ 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(); @@ -96,7 +101,6 @@ 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 8cf68e570..de1f85edc 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.getNumPlacble(cell); + int placeable = lightUpBoard.getNumPlaceable(cell); if (bulbs + placeable < cell.getData()) { return null; diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/Minesweeper.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/Minesweeper.java index ed8066f39..d07f097ec 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/Minesweeper.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/Minesweeper.java @@ -45,7 +45,7 @@ public boolean isBoardComplete(@NotNull Board board) { } for (PuzzleElement data : minesweeperBoard.getPuzzleElements()) { final MinesweeperCell cell = (MinesweeperCell) data; - if (cell.getData().equals(MinesweeperTileData.empty())) { + if (cell.getData().equals(MinesweeperTileData.unset())) { return false; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperBoard.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperBoard.java index bef317ab5..2d00bc6b8 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperBoard.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperBoard.java @@ -2,6 +2,11 @@ import edu.rpi.legup.model.gameboard.GridBoard; +import edu.rpi.legup.model.gameboard.PuzzleElement; + +import java.util.Set; + + public class MinesweeperBoard extends GridBoard { public MinesweeperBoard(int width, int height) { @@ -31,6 +36,9 @@ public MinesweeperBoard copy() { newMinesweeperBoard.setCell(x, y, getCell(x, y).copy()); } } + for (PuzzleElement e : modifiedData) { + newMinesweeperBoard.getPuzzleElement(e).setModifiable(false); + } return newMinesweeperBoard; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperCell.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperCell.java index 325e42b7b..89a8a3be8 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperCell.java @@ -26,11 +26,11 @@ public MinesweeperCell(@NotNull MinesweeperTileData value, @NotNull Point locati /** Sets this cell's data to the value specified by {@link Element#getElementID()} */ public void setType(@NotNull Element element, @NotNull MouseEvent event) { switch (element.getElementID()) { - case MinesweeperElementIdentifiers.BOMB -> { - this.data = MinesweeperTileData.bomb(); + case MinesweeperElementIdentifiers.MINE -> { + this.data = MinesweeperTileData.mine(); break; } - case MinesweeperElementIdentifiers.FLAG -> { + case MinesweeperElementIdentifiers.NUMBER -> { final int currentData = super.data.data(); switch (event.getButton()) { case MouseEvent.BUTTON1 -> { @@ -38,7 +38,7 @@ public void setType(@NotNull Element element, @NotNull MouseEvent event) { this.data = MinesweeperTileData.empty(); return; } - this.data = MinesweeperTileData.flag(currentData + 1); + this.data = MinesweeperTileData.number(currentData + 1); return; } case MouseEvent.BUTTON2, MouseEvent.BUTTON3 -> { @@ -46,7 +46,7 @@ public void setType(@NotNull Element element, @NotNull MouseEvent event) { this.data = MinesweeperTileData.empty(); return; } - this.data = MinesweeperTileData.flag(currentData - 1); + this.data = MinesweeperTileData.number(currentData - 1); return; } } diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperCellFactory.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperCellFactory.java index 5fe6096a9..e1b619433 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperCellFactory.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperCellFactory.java @@ -15,7 +15,7 @@ public class MinesweeperCellFactory extends ElementFactory { /** The key of the data used in {@link NamedNodeMap} */ - private static final String DATA_ATTRIBUTE = "data"; + private static final String DATA_ATTRIBUTE = "value"; /** The key of the x position used in {@link NamedNodeMap} */ private static final String X_ATTRIBUTE = "x"; @@ -66,6 +66,9 @@ private MinesweeperCellFactory() {} final MinesweeperCell cell = new MinesweeperCell(MinesweeperTileData.fromData(value), new Point(x, y)); cell.setIndex(y * height + x); + if(value != -2) { + cell.setModifiable(false); + } return cell; } catch (NumberFormatException e) { throw new InvalidFileFormatException( diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperController.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperController.java index aaf061704..e37635199 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperController.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperController.java @@ -22,12 +22,28 @@ public class MinesweeperController extends ElementController { public static @NotNull MinesweeperTileData getNewCellDataOnClick( @NotNull MouseEvent event, @NotNull MinesweeperTileData current) { final int numberData = current.data(); - return switch (event.getButton()) { - case MouseEvent.BUTTON1 -> MinesweeperTileData.fromData(numberData + 1); - case MouseEvent.BUTTON2, MouseEvent.BUTTON3 -> - MinesweeperTileData.fromData(numberData - 1); - default -> MinesweeperTileData.empty(); - }; + switch (event.getButton()) { //git? + case MouseEvent.BUTTON1: + if(numberData >= 1 && numberData <= 8) { + return MinesweeperTileData.fromData(numberData); + } + if(numberData == 0) { + return MinesweeperTileData.fromData(-2); + } + return MinesweeperTileData.fromData(numberData + 1); + + case MouseEvent.BUTTON2, MouseEvent.BUTTON3: + if(numberData >= 1 && numberData <= 8) { + return MinesweeperTileData.fromData(numberData); + } + if(numberData == -2) { + return MinesweeperTileData.fromData(0); + } + return MinesweeperTileData.fromData(numberData - 1); + default: + return MinesweeperTileData.empty(); + } + } /** diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperElementIdentifiers.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperElementIdentifiers.java index 77e490f7e..8e5a86bca 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperElementIdentifiers.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperElementIdentifiers.java @@ -5,12 +5,12 @@ public class MinesweeperElementIdentifiers { /** ID for unset Minesweeper elements */ public static final String UNSET = "MINESWEEPER-UNSET"; - /** ID for bomb Minesweeper elements */ - public static final String BOMB = "MINESWEEPER-BOMB"; + /** ID for mine Minesweeper elements */ + public static final String MINE = "MINESWEEPER-MINE"; /** ID for empty Minesweeper elements */ public static final String EMPTY = "MINESWEEPER-EMPTY"; - /** ID for flag Minesweeper elements */ - public static final String FLAG = "MINESWEEPER-FLAG"; + /** ID for number Minesweeper elements */ + public static final String NUMBER = "MINESWEEPER-NUMBER"; } diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperElementView.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperElementView.java index 1bfc0d698..d977045b7 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperElementView.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperElementView.java @@ -25,18 +25,44 @@ public MinesweeperElementView(@NotNull MinesweeperCell cell) { public void drawElement(@NotNull Graphics2D graphics2D) { final MinesweeperCell cell = (MinesweeperCell) puzzleElement; final MinesweeperTileType type = cell.getTileType(); - if (type == MinesweeperTileType.FLAG) { + if (type == MinesweeperTileType.NUMBER) { 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); + int intValue = ((MinesweeperCell) puzzleElement).getData().data(); + + final String value = String.valueOf(intValue); + if(intValue == 1) { + Color MSBLUE = new Color(7,3,251); + graphics2D.setColor(MSBLUE); + } else if(intValue == 2) { + Color MSGREEN = new Color(7,123,3); + graphics2D.setColor(MSGREEN); + } else if(intValue == 3) { + Color MSRED = new Color(255,3,3); + graphics2D.setColor(MSRED); + } else if(intValue == 4) { + Color MSDARKBLUE = new Color(0,0,125); + graphics2D.setColor(MSDARKBLUE); + } else if(intValue == 5) { + Color MSMAROON = new Color(135,3,3); + graphics2D.setColor(MSMAROON); + } else if(intValue == 6) { + Color MSCYAN = new Color(7,131,131); + graphics2D.setColor(MSCYAN); + } else if(intValue == 7) { + Color MSDARKGRAY = new Color(7,3,3); + graphics2D.setColor(MSDARKGRAY); + } else { + Color MSLIGHTGRAY = new Color(135,131,131); + graphics2D.setColor(MSLIGHTGRAY); + } - graphics2D.setColor(FONT_COLOR); graphics2D.setFont(FONT); final FontMetrics metrics = graphics2D.getFontMetrics(FONT); - final String value = String.valueOf(((MinesweeperCell) puzzleElement).getData().data()); final int xText = location.x + (size.width - metrics.stringWidth(value)) / 2; final int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); @@ -65,11 +91,11 @@ public void drawElement(@NotNull Graphics2D graphics2D) { graphics2D.setColor(Color.BLACK); graphics2D.drawRect(location.x, location.y, size.width, size.height); } - if (type == MinesweeperTileType.BOMB) { + if (type == MinesweeperTileType.MINE) { graphics2D.setColor(Color.LIGHT_GRAY); graphics2D.fillRect(location.x, location.y, size.width, size.height); graphics2D.drawImage( - MinesweeperView.BOMB_IMAGE, + MinesweeperView.MINE_IMAGE, location.x, location.y, size.width, diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperImporter.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperImporter.java index 419a69247..ffeb54f92 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperImporter.java @@ -72,7 +72,7 @@ public void initializeBoard(@NotNull Node node) throws InvalidFileFormatExceptio puzzle.getFactory() .importCell(elementDataList.item(i), minesweeperBoard); final Point loc = cell.getLocation(); - if (MinesweeperTileData.unset().equals(cell.getData())) { + if(cell.getTileNumber() > 0) { cell.setModifiable(false); cell.setGiven(true); } diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperTileData.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperTileData.java index 5296cf057..dae20dd0b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperTileData.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperTileData.java @@ -8,7 +8,7 @@ public record MinesweeperTileData(MinesweeperTileType type, int data) { public static final int UNSET_DATA = -2; - public static final int BOMB_DATA = -1; + public static final int MINE_DATA = -1; public static final int EMPTY_DATA = 0; /** @@ -19,10 +19,10 @@ public record MinesweeperTileData(MinesweeperTileType type, int data) { new MinesweeperTileData(MinesweeperTileType.UNSET, UNSET_DATA); /** - * Always has a type of {@link MinesweeperTileType#BOMB}, and a data value of {@value BOMB_DATA} + * Always has a type of {@link MinesweeperTileType#MINE}, and a data value of {@value MINE_DATA} */ - private static final MinesweeperTileData BOMB = - new MinesweeperTileData(MinesweeperTileType.BOMB, BOMB_DATA); + private static final MinesweeperTileData MINE = + new MinesweeperTileData(MinesweeperTileType.MINE, MINE_DATA); /** * Always has a type of {@link MinesweeperTileType#EMPTY}, and a data value of {@value @@ -32,34 +32,34 @@ public record MinesweeperTileData(MinesweeperTileType type, int data) { new MinesweeperTileData(MinesweeperTileType.EMPTY, EMPTY_DATA); /** - * @param count how many bombs are near the flag + * @param count how many mines are near the number * @return a new {@link MinesweeperTileData} with a {@link MinesweeperTileData#type} of {@link - * MinesweeperTileType#FLAG} and a {@link MinesweeperTileData#data} of {@code count} + * MinesweeperTileType#NUMBER} and a {@link MinesweeperTileData#data} of {@code count} */ @Contract(pure = true) - public static @NotNull MinesweeperTileData flag(int count) { - return new MinesweeperTileData(MinesweeperTileType.FLAG, count); + public static @NotNull MinesweeperTileData number(int count) { + return new MinesweeperTileData(MinesweeperTileType.NUMBER, count); } /** * @param data Determines what type of {@link MinesweeperTileData} to return. * @return If {@code data} is one of {@link MinesweeperTileData#UNSET_DATA}, {@link - * MinesweeperTileData#BOMB_DATA}, or {@link MinesweeperTileData#EMPTY_DATA}, it will return + * MinesweeperTileData#MINE_DATA}, or {@link MinesweeperTileData#EMPTY_DATA}, it will return * that data. If {@code data} is less than any of the values, or greater than 8, it will * return {@link MinesweeperTileData#UNSET_DATA}. Otherwise, it returns {@link - * MinesweeperTileData#flag(int)} and passes {@code data} as the parameter. + * MinesweeperTileData#number(int)} and passes {@code data} as the parameter. */ @Contract(pure = true) public static @NotNull MinesweeperTileData fromData(int data) { return switch (data) { case UNSET_DATA -> unset(); - case BOMB_DATA -> bomb(); + case MINE_DATA -> mine(); case EMPTY_DATA -> empty(); default -> { - if (data <= -2 || data > 8) { + if (data <= -2) { yield unset(); } - yield flag(data); + yield number(data); } }; } @@ -68,8 +68,8 @@ public record MinesweeperTileData(MinesweeperTileType type, int data) { return UNSET; } - public static @NotNull MinesweeperTileData bomb() { - return BOMB; + public static @NotNull MinesweeperTileData mine() { + return MINE; } public static @NotNull MinesweeperTileData empty() { @@ -79,16 +79,16 @@ public record MinesweeperTileData(MinesweeperTileType type, int data) { public boolean isUnset() { return this.data == UNSET_DATA; } - - public boolean isBomb() { - return this.data == BOMB_DATA; + + public boolean isMine() { + return this.data == MINE_DATA; } public boolean isEmpty() { return this.data == EMPTY_DATA; } - public boolean isFlag() { + public boolean isNumber() { return this.data > 0 && this.data <= 8; } diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperTileType.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperTileType.java index a682da5e5..e3ccad7ba 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperTileType.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperTileType.java @@ -2,13 +2,12 @@ public enum MinesweeperTileType { - /** A cell with nothing */ + /** A cell that is unknown by the user, value = -2 */ UNSET, - - /** Represents a cell with no bombs in it */ + /** Represents a cell with no mine in it, value = 0 */ EMPTY, - /** A flag has values 1-8 representing how many bombs are touching it */ - FLAG, - /** A bomb tile that should be marked by nearby flags */ - BOMB + /** A number cell has values 1-8 representing how many mines are touching it, 1 <= value <= 8 */ + NUMBER, + /** A mine cell that should be marked by nearby numbers, value = -1 */ + MINE } diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperUtilities.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperUtilities.java index d38460ac8..1cee1cd32 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperUtilities.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperUtilities.java @@ -1,6 +1,5 @@ package edu.rpi.legup.puzzle.minesweeper; -import edu.rpi.legup.puzzle.minesweeper.rules.LessBombsThanFlagContradictionRule; import java.awt.*; import java.util.*; import java.util.Objects; @@ -47,15 +46,14 @@ public static int countSurroundingType( return (int) (switch (type) { case UNSET -> stream.filter(MinesweeperTileData::isUnset); - case BOMB -> stream.filter(MinesweeperTileData::isBomb); + case MINE -> stream.filter(MinesweeperTileData::isMine); case EMPTY -> stream.filter(MinesweeperTileData::isEmpty); - case FLAG -> stream.filter(MinesweeperTileData::isFlag); + case NUMBER -> stream.filter(MinesweeperTileData::isNumber); }) .count(); } - - public static int countSurroundingBombs(MinesweeperBoard board, MinesweeperCell cell) { - return countSurroundingType(board, cell, MinesweeperTileType.BOMB); + public static int countSurroundingMines(MinesweeperBoard board, MinesweeperCell cell) { + return countSurroundingType(board, cell, MinesweeperTileType.MINE); } public static int countSurroundingUnset(MinesweeperBoard board, MinesweeperCell cell) { @@ -66,19 +64,19 @@ public static int countSurroundingEmpty(MinesweeperBoard board, MinesweeperCell return countSurroundingType(board, cell, MinesweeperTileType.EMPTY); } - public static int countSurroundingFlags(MinesweeperBoard board, MinesweeperCell cell) { - return countSurroundingType(board, cell, MinesweeperTileType.FLAG); + public static int countSurroundingNumbers(MinesweeperBoard board, MinesweeperCell cell) { + return countSurroundingType(board, cell, MinesweeperTileType.NUMBER); } /** - * @return how many bombs are left that need to be placed around {@code cell} which must be a + * @return how many mines are left that need to be placed around {@code cell} which must be a * flag */ - public int countNeededBombsFromFlag(MinesweeperBoard board, MinesweeperCell cell) { - if (!cell.getData().isFlag()) { - throw new IllegalArgumentException("Bombs are only needed surrounding flags"); + public int countNeededminesFromNumber(MinesweeperBoard board, MinesweeperCell cell) { + if (!cell.getData().isNumber()) { + throw new IllegalArgumentException("mines are only needed surrounding numbers"); } - return cell.getData().data() - countSurroundingBombs(board, cell); + return cell.getData().data() - countSurroundingMines(board, cell); } public static boolean hasEmptyAdjacent(MinesweeperBoard board, MinesweeperCell cell) { @@ -153,16 +151,253 @@ private static void recurseCombinations( recurseCombinations(result, curIndex + 1, maxBlack, numBlack, len, workingArray); } - public static boolean isForcedBomb(MinesweeperBoard board, MinesweeperCell cell) { + // checks if the current cell is forced to be a mine by checking if any of its adjacent cells + // are a number cell that can only be satisfied if the current cell is a mine + public static boolean isForcedMine(MinesweeperBoard board, MinesweeperCell cell) { + MinesweeperBoard emptyCaseBoard = board.copy(); + MinesweeperCell emptyCell = (MinesweeperCell) emptyCaseBoard.getPuzzleElement(cell); + emptyCell.setCellType(MinesweeperTileData.mine()); + ArrayList adjCells = getAdjacentCells(emptyCaseBoard, emptyCell); + int numMines; + int numUnset; + int cellNum; + for (MinesweeperCell adjCell : adjCells) { + cellNum = adjCell.getTileNumber(); + if(cellNum <= 0) { + continue; + } + numMines = 0; + numUnset = 0; + ArrayList curAdjCells = + MinesweeperUtilities.getAdjacentCells(emptyCaseBoard, adjCell); + for (MinesweeperCell curAdjCell : curAdjCells) { + if(curAdjCell.getTileType() == MinesweeperTileType.MINE) { + numMines++; + } + if(curAdjCell.getTileType() == MinesweeperTileType.UNSET) { + numUnset++; + } + } + if(cellNum == numUnset + numMines) { + return true; + } + } + return false; + } - LessBombsThanFlagContradictionRule tooManyBombs = new LessBombsThanFlagContradictionRule(); + // checks if the current cell is forced to be empty by checking if any of its adjacent cells + // are a number cell that can only be satisfied if the current cell is empty + public static boolean isForcedEmpty(MinesweeperBoard board, MinesweeperCell cell) { MinesweeperBoard emptyCaseBoard = board.copy(); MinesweeperCell emptyCell = (MinesweeperCell) emptyCaseBoard.getPuzzleElement(cell); emptyCell.setCellType(MinesweeperTileData.empty()); ArrayList adjCells = getAdjacentCells(emptyCaseBoard, emptyCell); + int mineCount; + int adjCellNum; + int emptyCells = 0; for (MinesweeperCell adjCell : adjCells) { - if (tooManyBombs.checkContradictionAt(emptyCaseBoard, adjCell) == null) { - return true; + mineCount = 0; + adjCellNum = adjCell.getTileNumber(); + if(adjCellNum >= 1) { + ArrayList adjAdjCells = getAdjacentCells(emptyCaseBoard, adjCell); + for(MinesweeperCell adjAdjCell : adjAdjCells) { + if(adjAdjCell.getTileType() == MinesweeperTileType.MINE) { + mineCount++; + } + } + if(mineCount == adjCellNum) { + return true; + } + } else { + emptyCells++; + } + } + return emptyCells == adjCells.size(); + } + + public static boolean nonTouchingSharedIsMine(MinesweeperBoard board, MinesweeperCell cell) { + MinesweeperBoard emptyCaseBoard = board.copy(); + MinesweeperCell emptyCell = (MinesweeperCell) emptyCaseBoard.getPuzzleElement(cell); + int x = emptyCell.getLocation().x; + int y = emptyCell.getLocation().y; + int height = emptyCaseBoard.getHeight(); + int width = emptyCaseBoard.getWidth(); + int numFar; + int numClose; + + // Goes through all possible positions that horizontally adjacent number cells + // could be in that could force the current cell to be a mine. If one possibility + // actually has the number cells that force the current cell to be a mine, return true + for(int i = -1; i <= 1; i += 2) { + for(int j = -1; j <= 1; j ++) { + if(x + (2 * i) >= 0 && x + (2 * i) < width + && y + j >= 0 && y + j < height) { + numClose = emptyCaseBoard.getCell(x + i, y + j).getTileNumber(); + numFar = emptyCaseBoard.getCell(x + (2 * i), y + j).getTileNumber(); + if(j != -1 && y + 1 < height + && emptyCaseBoard.getCell(x, y + 1).getTileNumber() <= -1) { + numClose--; + } + if(j != 1 && y - 1 >= 0 && + emptyCaseBoard.getCell(x, y-1).getTileNumber() <= -1) { + numClose--; + } + if(j != 0 && y + (2 * j) < height + && y + (2 * j) >= 0 && emptyCaseBoard.getCell(x, y + (2 * j)).getTileNumber() <= -1) { + numClose--; + } + if(x + (3 * i) >= 0 && x + (3 * i) < width) { + if(emptyCaseBoard.getCell(x+(3*i), y+j).getTileNumber() == -1) { + numFar--; + } + if(y + j + 1 < emptyCaseBoard.getHeight() + && emptyCaseBoard.getCell(x+(3*i), y+j+1).getTileNumber() == -1) { + numFar--; + } + if(y + j - 1 >= 0 && + emptyCaseBoard.getCell(x+(3*i), y+j-1).getTileNumber() == -1) { + numFar--; + } + } + if(numClose >= 1 && numFar+1 == numClose) { + return true; + } + } + } + } + + // Goes through all possible positions that vertically adjacent number cells + // could be in that could force the current cell to be a mine. If one possibility + // actually has the number cells that force the current cell to be a mine, return true + for(int i = -1; i <= 1; i += 2){ + for (int j = -1; j <= 1; j++) { + if (x + j >= 0 && x + j < width + && y + (2 * i) >= 0 && y + (2 * i) < height) { + numClose = emptyCaseBoard.getCell(x + j, y + i).getTileNumber(); + numFar = emptyCaseBoard.getCell(x + j, y + (2 * i)).getTileNumber(); + if (j != -1 && x + 1 < width && + emptyCaseBoard.getCell(x + 1, y).getTileNumber() <= -1) { + numClose--; + } + if (j != 1 && x - 1 >= 0 && + emptyCaseBoard.getCell(x - 1, y).getTileNumber() <= -1) { + numClose--; + } + if (j != 0 && x + (2 * j) < width && + x + (2 * j) >= 0 && + emptyCaseBoard.getCell(x + (2 * j), y).getTileNumber() <= -1) { + numClose--; + } + if (y + (3 * i) >= 0 && y + (3 * i) < height) { + if (emptyCaseBoard.getCell(x + j, y + (3 * i)).getTileNumber() == -1) { + numFar--; + } + if (x + j + 1 < width && + emptyCaseBoard.getCell(x + j + 1, y + (3 * i)).getTileNumber() == -1) { + numFar--; + } + if (x + j - 1 >= 0 && emptyCaseBoard.getCell(x + j - 1, y + (3 * i)).getTileNumber() == -1) { + numFar--; + } + } + if (numClose >= 1 && numFar + 1 == numClose) { + return true; + } + } + } + } + return false; + } + + public static boolean nonTouchingSharedIsEmpty(MinesweeperBoard board, MinesweeperCell cell) { + MinesweeperBoard emptyCaseBoard = board.copy(); + MinesweeperCell emptyCell = (MinesweeperCell) emptyCaseBoard.getPuzzleElement(cell); + int x = emptyCell.getLocation().x; + int y = emptyCell.getLocation().y; + int height = emptyCaseBoard.getHeight(); + int width = emptyCaseBoard.getWidth(); + int numClose; + int numFar; + + // Goes through all possible positions that horizontally adjacent number cells + // could be in that could force the current cell to be empty. If one possibility + // actually has the number cells that force the current cell to be empty, return true + for(int i = -1; i <= 1; i += 2) { + for(int j = -1; j <= 1; j ++) { + if(x + (2 * i) >= 0 && x + (2 * i) < width + && y + j >= 0 && y + j < height) { + numFar = emptyCaseBoard.getCell(x + i, y + j).getTileNumber(); + numClose = emptyCaseBoard.getCell(x + (2 * i), y + j).getTileNumber(); + if(j != -1 && y + 1 < height + && emptyCaseBoard.getCell(x, y + 1).getTileNumber() == -1) { + numFar--; + } + if(j != 1 && y - 1 >= 0 && + emptyCaseBoard.getCell(x, y-1).getTileNumber() == -1) { + numFar--; + } + if(j != 0 && y + (2 * j) < height + && y + (2 * j) >= 0 && emptyCaseBoard.getCell(x, y + (2 * j)).getTileNumber() == -1) { + numFar--; + } + if(x + (3 * i) >= 0 && x + (3 * i) < width) { + if(emptyCaseBoard.getCell(x+(3*i), y+j).getTileNumber() <= -1) { + numClose--; + } + if(y + j + 1 < emptyCaseBoard.getHeight() + && emptyCaseBoard.getCell(x+(3*i), y+j+1).getTileNumber() <= -1) { + numClose--; + } + if(y + j - 1 >= 0 && + emptyCaseBoard.getCell(x+(3*i), y+j-1).getTileNumber() <= -1) { + numClose--; + } + } + if(numFar >= 1 && numClose == numFar) { + return true; + } + } + } + } + + // Goes through all possible positions that vertically adjacent number cells + // could be in that could force the current cell to be empty. If one possibility + // actually has the number cells that force the current cell to beempty, return true + for(int i = -1; i <= 1; i += 2){ + for (int j = -1; j <= 1; j++) { + if (x + j >= 0 && x + j < width + && y + (2 * i) >= 0 && y + (2 * i) < height) { + numFar = emptyCaseBoard.getCell(x + j, y + i).getTileNumber(); + numClose = emptyCaseBoard.getCell(x + j, y + (2 * i)).getTileNumber(); + if (j != -1 && x + 1 < width && + emptyCaseBoard.getCell(x + 1, y).getTileNumber() == -1) { + numFar--; + } + if (j != 1 && x - 1 >= 0 && + emptyCaseBoard.getCell(x - 1, y).getTileNumber() == -1) { + numFar--; + } + if (j != 0 && x + (2 * j) < width && + x + (2 * j) >= 0 && + emptyCaseBoard.getCell(x + (2 * j), y).getTileNumber() == -1) { + numFar--; + } + if (y + (3 * i) >= 0 && y + (3 * i) < height) { + if (emptyCaseBoard.getCell(x + j, y + (3 * i)).getTileNumber() <= -1) { + numClose--; + } + if (x + j + 1 < width && + emptyCaseBoard.getCell(x + j + 1, y + (3 * i)).getTileNumber() <= -1) { + numClose--; + } + if (x + j - 1 >= 0 && emptyCaseBoard.getCell(x + j - 1, y + (3 * i)).getTileNumber() <= -1) { + numClose--; + } + } + if (numFar >= 1 && numClose == numFar) { + return true; + } + } } } return false; diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperView.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperView.java index e8ab8dfcb..b80efd7d2 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperView.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/MinesweeperView.java @@ -14,23 +14,23 @@ public class MinesweeperView extends GridBoardView { private static final Logger LOGGER = LogManager.getLogger(MinesweeperView.class.getName()); - public static final Image BOMB_IMAGE; + public static final Image MINE_IMAGE; public static final Image EMPTY_IMAGE; static { - Image tempBombImage = null; + Image tempMineImage = null; try { - tempBombImage = + tempMineImage = ImageIO.read( Objects.requireNonNull( ClassLoader.getSystemClassLoader() .getResource( - "edu/rpi/legup/images/minesweeper/tiles/Bomb.png"))); + "edu/rpi/legup/images/minesweeper/tiles/Mine.png"))); } catch (IOException e) { LOGGER.error("Failed to open Minesweeper images"); } - BOMB_IMAGE = tempBombImage; + MINE_IMAGE = tempMineImage; } static { 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 deleted file mode 100644 index 78a5d320c..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/BombTile.java +++ /dev/null @@ -1,13 +0,0 @@ -package edu.rpi.legup.puzzle.minesweeper.elements; - -import edu.rpi.legup.model.elements.NonPlaceableElement; - -public class BombTile extends NonPlaceableElement { - public BombTile() { - super( - "MINE-UNPL-0001", - "Bomb", - "A bomb", - "edu/rpi/legup/images/minesweeper/tiles/Bomb.png"); - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/FlagTile.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/FlagTile.java deleted file mode 100644 index 0bbca81f9..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/FlagTile.java +++ /dev/null @@ -1,13 +0,0 @@ -package edu.rpi.legup.puzzle.minesweeper.elements; - -import edu.rpi.legup.model.elements.PlaceableElement; - -public class FlagTile extends PlaceableElement { - public FlagTile() { - super( - "MINE-PLAC-0001", - "Flag", - "The flag", - "edu/rpi/legup/images/nurikabe/tiles/BlackTile.png"); - } -} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/MineTile.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/MineTile.java new file mode 100644 index 000000000..66f5ace2d --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/MineTile.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.minesweeper.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class MineTile extends PlaceableElement { + public MineTile() { + super( + "MINE-UNPL-0001", + "Mine", + "A mine", + "edu/rpi/legup/images/minesweeper/tiles/Mine.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/NumberTile.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/NumberTile.java new file mode 100644 index 000000000..0bf07e9a8 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/NumberTile.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.minesweeper.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class NumberTile extends PlaceableElement { + public NumberTile() { + super( + "MINE-PLAC-0001", + "Number", + "A number", + "edu/rpi/legup/images/minesweeper/tiles/Unset.png"); + } +} 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 447e2840c..6cfc34c8d 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class UnsetTile extends NonPlaceableElement { +public class UnsetTile extends PlaceableElement { public UnsetTile() { super( diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/minesweeper_elements_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/minesweeper_elements_reference_sheet.txt index 08ce23f59..b9e474a9c 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/minesweeper_elements_reference_sheet.txt +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/elements/minesweeper_elements_reference_sheet.txt @@ -1,4 +1,4 @@ -MINE-UNPL-0001 : BombTile -MINE-PLAC-0001 : FlagTile +MINE-UNPL-0001 : MineTile +MINE-PLAC-0001 : NumberTile MINE-PLAC-0002 : EmptyTile MINE-UNPL-0002 : UnsetTile \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/FinishWithBombsDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/FinishWithEmptyDirectRule.java similarity index 74% rename from src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/FinishWithBombsDirectRule.java rename to src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/FinishWithEmptyDirectRule.java index e85008d23..96b50a0e7 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/FinishWithBombsDirectRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/FinishWithEmptyDirectRule.java @@ -7,13 +7,13 @@ import edu.rpi.legup.model.tree.TreeTransition; import edu.rpi.legup.puzzle.minesweeper.*; -public class FinishWithBombsDirectRule extends DirectRule { - public FinishWithBombsDirectRule() { +public class FinishWithEmptyDirectRule extends DirectRule { + public FinishWithEmptyDirectRule() { super( - "MINE-BASC-0001", - "Finish with Bombs", - "The remaining unknowns around a flag must be bombs to satisfy the number", - "edu/rpi/legup/images/minesweeper/direct/Fill_Bombs.jpg"); + "MINE-BASC-0002", + "Finish With Empty", + "There exists a number around this cell that can only be satisfied if this cell is empty", + "edu/rpi/legup/images/minesweeper/direct/FinishWithEmpty.png"); } @Override @@ -24,15 +24,16 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem MinesweeperCell parentCell = (MinesweeperCell) parentBoard.getPuzzleElement(puzzleElement); if (!(parentCell.getTileType() == MinesweeperTileType.UNSET - && cell.getTileType() == MinesweeperTileType.BOMB)) { + && cell.getTileType() == MinesweeperTileType.EMPTY)) { + return super.getInvalidUseOfRuleMessage() - + ": This cell must be black to be applicable with this rule."; + + ": This cell must be empty to be applicable with this rule."; } - if (MinesweeperUtilities.isForcedBomb(parentBoard, cell)) { + if (MinesweeperUtilities.isForcedEmpty(parentBoard, cell)) { return null; } else { - return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be black"; + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be empty"; } } @@ -49,9 +50,9 @@ public Board getDefaultBoard(TreeNode node) { for (PuzzleElement element : minesweeperBoard.getPuzzleElements()) { MinesweeperCell cell = (MinesweeperCell) element; if (cell.getTileType() == MinesweeperTileType.UNSET - && MinesweeperUtilities.isForcedBomb( + && MinesweeperUtilities.isForcedMine( (MinesweeperBoard) node.getBoard(), cell)) { - cell.setCellType(MinesweeperTileData.bomb()); + cell.setCellType(MinesweeperTileData.mine()); minesweeperBoard.addModifiedData(cell); } } diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/FinishWithMinesDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/FinishWithMinesDirectRule.java new file mode 100644 index 000000000..b327683c9 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/FinishWithMinesDirectRule.java @@ -0,0 +1,66 @@ +package edu.rpi.legup.puzzle.minesweeper.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.minesweeper.*; + +public class FinishWithMinesDirectRule extends DirectRule { + public FinishWithMinesDirectRule() { + super( + "MINE-BASC-0001", + "Finish With Mines", + "There exists a number around this cell that can only be satisfied if this cell contains a mine", + "edu/rpi/legup/images/minesweeper/direct/FinishWithMines.png"); + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + MinesweeperBoard parentBoard = (MinesweeperBoard) transition.getParents().get(0).getBoard(); + MinesweeperCell cell = (MinesweeperCell) board.getPuzzleElement(puzzleElement); + MinesweeperCell parentCell = (MinesweeperCell) parentBoard.getPuzzleElement(puzzleElement); + + if (!(parentCell.getTileType() == MinesweeperTileType.UNSET + && cell.getTileType() == MinesweeperTileType.MINE)) { + + return super.getInvalidUseOfRuleMessage() + + ": This cell must be a mine to be applicable with this rule."; + } + + if (MinesweeperUtilities.isForcedMine(parentBoard, cell)) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be a mine"; + } + } + + /** + * 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) { + MinesweeperBoard minesweeperBoard = (MinesweeperBoard) node.getBoard().copy(); + for (PuzzleElement element : minesweeperBoard.getPuzzleElements()) { + MinesweeperCell cell = (MinesweeperCell) element; + if (cell.getTileType() == MinesweeperTileType.UNSET + && MinesweeperUtilities.isForcedMine( + (MinesweeperBoard) node.getBoard(), cell)) { + cell.setCellType(MinesweeperTileData.mine()); + cell.setModifiable(false); + minesweeperBoard.addModifiedData(cell); + } + } + if (minesweeperBoard.getModifiedData().isEmpty()) { + return null; + } else { + return minesweeperBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/IsolateMineContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/IsolateMineContradictionRule.java new file mode 100644 index 000000000..34eeec37b --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/IsolateMineContradictionRule.java @@ -0,0 +1,49 @@ +package edu.rpi.legup.puzzle.minesweeper.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.minesweeper.MinesweeperBoard; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperCell; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperTileType; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperUtilities; + +import java.util.ArrayList; + +public class IsolateMineContradictionRule extends ContradictionRule { + + public IsolateMineContradictionRule() { + super( + "MINE-CONT-0002", + "Isolate Mine", + "A mine cell must see a number cell", + "edu/rpi/legup/images/minesweeper/contradictions/IsolateMine.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) { + MinesweeperBoard minesweeperBoard = (MinesweeperBoard) board; + MinesweeperCell cell = (MinesweeperCell) minesweeperBoard.getPuzzleElement(puzzleElement); + + if (cell.getTileNumber() != -1) { + return super.getNoContradictionMessage(); + } + ArrayList adjCells = + MinesweeperUtilities.getAdjacentCells(minesweeperBoard, cell); + for (MinesweeperCell adjCell : adjCells) { + if(adjCell.getTileType() == MinesweeperTileType.NUMBER) { + return super.getNoContradictionMessage(); + } + } + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/BombOrFilledCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/MineOrEmptyCaseRule.java similarity index 83% rename from src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/BombOrFilledCaseRule.java rename to src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/MineOrEmptyCaseRule.java index a1ef97928..66278e750 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/BombOrFilledCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/MineOrEmptyCaseRule.java @@ -11,14 +11,14 @@ import java.util.ArrayList; import java.util.List; -public class BombOrFilledCaseRule extends CaseRule { +public class MineOrEmptyCaseRule extends CaseRule { - public BombOrFilledCaseRule() { + public MineOrEmptyCaseRule() { super( "MINE-CASE-0001", - "Bomb Or Filled", - "A cell can either be a bomb or filled.\n", - "edu/rpi/legup/images/minesweeper/cases/BombOrFilled.jpg"); + "Mine or Empty", + "An unset cell can either be a mine or empty.", + "edu/rpi/legup/images/minesweeper/cases/MineOrEmpty.png"); } @Override @@ -36,17 +36,19 @@ public CaseBoard getCaseBoard(Board board) { } @Override - public List getCases(Board board, PuzzleElement puzzleElement) { + public ArrayList getCases(Board board, PuzzleElement puzzleElement) { ArrayList cases = new ArrayList<>(); Board case1 = board.copy(); MinesweeperCell cell1 = (MinesweeperCell) case1.getPuzzleElement(puzzleElement); - cell1.setData(MinesweeperTileData.bomb()); + cell1.setModifiable(false); + cell1.setData(MinesweeperTileData.mine()); case1.addModifiedData(cell1); cases.add(case1); Board case2 = board.copy(); MinesweeperCell cell2 = (MinesweeperCell) case2.getPuzzleElement(puzzleElement); + cell2.setModifiable(false); cell2.setData(MinesweeperTileData.empty()); case2.addModifiedData(cell2); cases.add(case2); @@ -77,10 +79,10 @@ public String checkRuleRaw(TreeTransition transition) { + ": This case rule must modify the same cell for each case."; } - if (!((mod1.getData().isBomb() && mod2.getData().isEmpty()) - || (mod2.getData().isBomb() && mod1.getData().isEmpty()))) { + if (!((mod1.getData().isMine() && mod2.getData().isEmpty()) + || (mod2.getData().isMine() && mod1.getData().isEmpty()))) { return super.getInvalidUseOfRuleMessage() - + ": This case rule must an empty cell and a bomb cell."; + + ": This case rule must an empty cell and a mine cell."; } return null; diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/NonTouchingSharedEmptyDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/NonTouchingSharedEmptyDirectRule.java new file mode 100644 index 000000000..0c86c59b8 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/NonTouchingSharedEmptyDirectRule.java @@ -0,0 +1,66 @@ +package edu.rpi.legup.puzzle.minesweeper.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.minesweeper.*; + +public class NonTouchingSharedEmptyDirectRule extends DirectRule { + public NonTouchingSharedEmptyDirectRule() { + super( + "MINE-BASC-0003", + "Non Shared Empty", + "Adjacent cells with numbers have the same difference in mine in their unshared\n" + + " regions as the difference in their numbers", + "edu/rpi/legup/images/minesweeper/direct/NonSharedEmpty.png"); + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + MinesweeperBoard parentBoard = (MinesweeperBoard) transition.getParents().get(0).getBoard(); + MinesweeperCell cell = (MinesweeperCell) board.getPuzzleElement(puzzleElement); + MinesweeperCell parentCell = (MinesweeperCell) parentBoard.getPuzzleElement(puzzleElement); + + if (!(parentCell.getTileType() == MinesweeperTileType.UNSET + && cell.getTileType() == MinesweeperTileType.EMPTY)) { + + return super.getInvalidUseOfRuleMessage() + + ": This cell must be a mine to be applicable with this rule."; + } + + if (MinesweeperUtilities.nonTouchingSharedIsEmpty(parentBoard, cell)) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be a mine"; + } + } + + /** + * 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) { + MinesweeperBoard minesweeperBoard = (MinesweeperBoard) node.getBoard().copy(); + for (PuzzleElement element : minesweeperBoard.getPuzzleElements()) { + MinesweeperCell cell = (MinesweeperCell) element; + if (cell.getTileType() == MinesweeperTileType.UNSET + && MinesweeperUtilities.isForcedEmpty( + (MinesweeperBoard) node.getBoard(), cell)) { + cell.setCellType(MinesweeperTileData.empty()); + minesweeperBoard.addModifiedData(cell); + } + } + if (minesweeperBoard.getModifiedData().isEmpty()) { + return null; + } else { + return minesweeperBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/NonTouchingSharedMineDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/NonTouchingSharedMineDirectRule.java new file mode 100644 index 000000000..e6724753b --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/NonTouchingSharedMineDirectRule.java @@ -0,0 +1,66 @@ +package edu.rpi.legup.puzzle.minesweeper.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.minesweeper.*; + +public class NonTouchingSharedMineDirectRule extends DirectRule { + public NonTouchingSharedMineDirectRule() { + super( + "MINE-BASC-0003", + "Non Shared Mine", + "Adjacent cells with numbers have the same difference in mine in their unshared\n" + + "regions as the difference in their numbers", + "edu/rpi/legup/images/minesweeper/direct/NonSharedMine.png"); + } + + @Override + public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElement) { + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + MinesweeperBoard parentBoard = (MinesweeperBoard) transition.getParents().get(0).getBoard(); + MinesweeperCell cell = (MinesweeperCell) board.getPuzzleElement(puzzleElement); + MinesweeperCell parentCell = (MinesweeperCell) parentBoard.getPuzzleElement(puzzleElement); + + if (!(parentCell.getTileType() == MinesweeperTileType.UNSET + && cell.getTileType() == MinesweeperTileType.MINE)) { + + return super.getInvalidUseOfRuleMessage() + + ": This cell must be a mine to be applicable with this rule."; + } + + if (MinesweeperUtilities.nonTouchingSharedIsMine(parentBoard, cell)) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + ": This cell is not forced to be a mine"; + } + } + + /** + * 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) { + MinesweeperBoard minesweeperBoard = (MinesweeperBoard) node.getBoard().copy(); + for (PuzzleElement element : minesweeperBoard.getPuzzleElements()) { + MinesweeperCell cell = (MinesweeperCell) element; + if (cell.getTileType() == MinesweeperTileType.UNSET + && MinesweeperUtilities.isForcedMine( + (MinesweeperBoard) node.getBoard(), cell)) { + cell.setCellType(MinesweeperTileData.mine()); + minesweeperBoard.addModifiedData(cell); + } + } + if (minesweeperBoard.getModifiedData().isEmpty()) { + return null; + } else { + return minesweeperBoard; + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/SatisfyFlagCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/SatisfyNumberCaseRule.java similarity index 93% rename from src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/SatisfyFlagCaseRule.java rename to src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/SatisfyNumberCaseRule.java index a59369b7a..004ea2640 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/SatisfyFlagCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/SatisfyNumberCaseRule.java @@ -11,13 +11,13 @@ import java.util.*; import java.util.List; -public class SatisfyFlagCaseRule extends CaseRule { - public SatisfyFlagCaseRule() { +public class SatisfyNumberCaseRule extends CaseRule { + public SatisfyNumberCaseRule() { super( "MINE-CASE-0002", - "Satisfy Flag", - "Create a different path for each valid way to mark bombs and filled cells around a flag", - "edu/rpi/legup/images/minesweeper/cases/Satisfy_Flag.png"); + "Satisfy Number", + "Create a different path for each valid way to mark mines and empty cells around a number cell", + "edu/rpi/legup/images/minesweeper/cases/SatisfyNumber.png"); } @Override @@ -49,14 +49,14 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { } // find number of black & unset squares - int cellNumBomb = 0; + int cellNummine = 0; int cellNumUnset = 0; ArrayList unsetCells = new ArrayList(); ArrayList adjCells = MinesweeperUtilities.getAdjacentCells(minesweeperBoard, cell); for (MinesweeperCell adjCell : adjCells) { - if (adjCell.getTileType() == MinesweeperTileType.BOMB) { - cellNumBomb++; + if (adjCell.getTileType() == MinesweeperTileType.MINE) { + cellNummine++; } if (adjCell.getTileType() == MinesweeperTileType.UNSET) { cellNumUnset++; @@ -64,21 +64,21 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { } } // no cases if no empty or if too many black already - if (cellNumBomb >= cellMaxBlack || cellNumUnset == 0) { + if (cellNummine >= cellMaxBlack || cellNumUnset == 0) { return cases; } // generate all cases as boolean expressions ArrayList combinations; combinations = - MinesweeperUtilities.getCombinations(cellMaxBlack - cellNumBomb, cellNumUnset); + MinesweeperUtilities.getCombinations(cellMaxBlack - cellNummine, cellNumUnset); for (int i = 0; i < combinations.size(); i++) { Board case_ = board.copy(); for (int j = 0; j < combinations.get(i).length; j++) { cell = (MinesweeperCell) case_.getPuzzleElement(unsetCells.get(j)); if (combinations.get(i)[j]) { - cell.setCellType(MinesweeperTileData.bomb()); + cell.setCellType(MinesweeperTileData.mine()); } else { cell.setCellType(MinesweeperTileData.empty()); } @@ -162,7 +162,7 @@ public String checkRuleRaw(TreeTransition transition) { int maxBlack = possibleCenter.getTileNumber(); for (MinesweeperCell adjCell : MinesweeperUtilities.getAdjacentCells(board, possibleCenter)) { - if (adjCell.getTileType() == MinesweeperTileType.BOMB) { + if (adjCell.getTileType() == MinesweeperTileType.MINE) { numBlack++; } if (adjCell.getTileType() == MinesweeperTileType.UNSET) { @@ -192,7 +192,7 @@ public String checkRuleRaw(TreeTransition transition) { boolean[] translatedModCells = new boolean[transModCells.size()]; for (int i = 0; i < transModCells.size(); i++) { - if (transModCells.get(i).getTileType() == MinesweeperTileType.BOMB) { + if (transModCells.get(i).getTileType() == MinesweeperTileType.MINE) { translatedModCells[i] = true; } else { translatedModCells[i] = false; diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/LessBombsThanFlagContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/TooFewMinesContradictionRule.java similarity index 55% rename from src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/LessBombsThanFlagContradictionRule.java rename to src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/TooFewMinesContradictionRule.java index c9919343f..46a7e9e7d 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/LessBombsThanFlagContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/TooFewMinesContradictionRule.java @@ -6,19 +6,29 @@ import edu.rpi.legup.puzzle.minesweeper.*; import java.util.ArrayList; -public class LessBombsThanFlagContradictionRule extends ContradictionRule { +public class TooFewMinesContradictionRule 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 region"; - public LessBombsThanFlagContradictionRule() { + public TooFewMinesContradictionRule() { super( "MINE-CONT-0000", - "Less Bombs Than Flag", - "There can not be less then the number of Bombs around a flag then the specified number\n", - "edu/rpi/legup/images/nurikabe/contradictions/NoNumber.png"); + "Too Few Mines", + "A number cell can not have less than it's number of mines around it\n", + "edu/rpi/legup/images/minesweeper/contradictions/TooFewMines.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) { MinesweeperBoard minesweeperBoard = (MinesweeperBoard) board; @@ -28,20 +38,20 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { if (cellNum <= 0 || cellNum >= 9) { return super.getNoContradictionMessage(); } - int numEmpty = 0; - int numAdj = 0; + int numMines = 0; + int numUnset = 0; ArrayList adjCells = MinesweeperUtilities.getAdjacentCells(minesweeperBoard, cell); for (MinesweeperCell adjCell : adjCells) { - numAdj++; - if (adjCell.getTileType() == MinesweeperTileType.EMPTY && adjCell != cell) { - numEmpty++; + if(adjCell.getTileType() == MinesweeperTileType.MINE) { + numMines++; + } + if(adjCell.getTileType() == MinesweeperTileType.UNSET) { + numUnset++; } } - System.out.println(numEmpty); - System.out.println(numAdj); - System.out.println(cellNum); - if (numEmpty > (numAdj - cellNum)) { + + if (cellNum > numUnset + numMines) { return null; } diff --git a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/MoreBombsThanFlagContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/TooManyMinesContradictionRule.java similarity index 77% rename from src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/MoreBombsThanFlagContradictionRule.java rename to src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/TooManyMinesContradictionRule.java index ecfdbad66..66eeb8602 100644 --- a/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/MoreBombsThanFlagContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/minesweeper/rules/TooManyMinesContradictionRule.java @@ -9,14 +9,14 @@ import edu.rpi.legup.puzzle.minesweeper.MinesweeperUtilities; import java.util.ArrayList; -public class MoreBombsThanFlagContradictionRule extends ContradictionRule { +public class TooManyMinesContradictionRule extends ContradictionRule { - public MoreBombsThanFlagContradictionRule() { + public TooManyMinesContradictionRule() { super( "MINE-CONT-0001", - "More Bombs Than Flag", - "There can not be more Bombs around a flag than the specified number\n", - "edu/rpi/legup/images/minesweeper/contradictions/Bomb_Surplus.jpg"); + "Too Many Mines", + "A number cell can not have more than it's number of mines around it\n", + "edu/rpi/legup/images/minesweeper/contradictions/TooManyMines.png"); } /** @@ -34,18 +34,19 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { MinesweeperCell cell = (MinesweeperCell) minesweeperBoard.getPuzzleElement(puzzleElement); int cellNum = cell.getTileNumber(); - if (cellNum < 0 || cellNum >= 10) { + if (cellNum <= 0 || cellNum >= 9) { return super.getNoContradictionMessage(); } - int numBlack = 0; + int numMines = 0; ArrayList adjCells = MinesweeperUtilities.getAdjacentCells(minesweeperBoard, cell); for (MinesweeperCell adjCell : adjCells) { - if (adjCell.getTileType() == MinesweeperTileType.BOMB) { - numBlack++; + if(adjCell.getTileType() == MinesweeperTileType.MINE) { + numMines++; } } - if (numBlack > cellNum) { + + if (cellNum < numMines) { return null; } 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 c6cd2c64e..1e0e85ed8 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-PLAC-0001": + case "NURI-ELEM-0001": this.data = -1; break; - case "NURI-PLAC-0002": + case "NURI-ELEM-0004": this.data = 0; break; - case "NURI-UNPL-0001": + case "NURI-ELEM-0002": 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 158abe7b4..44ead6fac 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeController.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeController.java @@ -6,6 +6,15 @@ 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 23efd4724..e01821639 100644 --- a/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeExporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeExporter.java @@ -10,6 +10,13 @@ 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 459a809e0..a7972b9b2 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-PLAC-0001", + "NURI-ELEM-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 475b278da..2015d990b 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class NumberTile extends NonPlaceableElement { +public class NumberTile extends PlaceableElement { private int object_num; public NumberTile() { super( - "NURI-UNPL-0001", + "NURI-ELEM-0002", "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 85d47e208..8a18c80cc 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class UnknownTile extends NonPlaceableElement { +public class UnknownTile extends PlaceableElement { public UnknownTile() { super( - "NURI-UNPL-0002", + "NURI-ELEM-0003", "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 35eb63b81..ae07c6d76 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-PLAC-0002", + "NURI-ELEM-0004", "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 new file mode 100644 index 000000000..667972fd6 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/nurikabe/elements/nurikabe_elements_reference_sheet.txt @@ -0,0 +1,4 @@ +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 ac0ab6df6..1c87d5cfa 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 overridden in child classes. + * 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 @@ -84,6 +84,10 @@ 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 4901cfa6f..025212d96 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 @@ -19,6 +19,7 @@ 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 // cases @@ -26,15 +27,16 @@ public FinishRoomCaseRule() { super( "NURI-CASE-0002", "Finish Room", - "Room can be finished in up to five ways", + "Room can be finished in up to nine ways", "edu/rpi/legup/images/nurikabe/cases/FinishRoom.png"); - this.MAX_CASES = 5; - this.MIN_CASES = 2; + this.MAX_CASES = 9; + this.MIN_CASES = 1; + this.uniqueCases = new HashSet<>(); } /** * Checks whether the {@link TreeTransition} logically follows from the parent node using this - * rule. This method is the one that should overridden in child classes. + * rule. This method is the one that should have 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 +45,18 @@ public FinishRoomCaseRule() { public String checkRuleRaw(TreeTransition transition) { NurikabeBoard destBoardState = (NurikabeBoard) transition.getBoard(); List childTransitions = transition.getParents().get(0).getChildren(); - if (childTransitions.size() > 5) { + if (childTransitions.size() > MAX_CASES) { return super.getInvalidUseOfRuleMessage() - + ": This case rule must have 5 or less children."; + + ": This case rule must have 9 or less children."; } - if (childTransitions.size() < 2) { + if (childTransitions.size() < MIN_CASES) { return super.getInvalidUseOfRuleMessage() - + ": This case rule must have 2 or more children."; + + ": This case rule must have 1 or more children."; } if (childTransitions.size() != legitCases) { return super.getInvalidUseOfRuleMessage() + ": Cases can not be removed from the branch."; - } // stops user from deleting 1 or mose generated cases and still having path show as green + } // stops user from deleting 1 or more generated cases and still having path show as green Set locations = new HashSet<>(); for (TreeTransition t1 : childTransitions) { locations.add( @@ -94,21 +96,18 @@ public CaseBoard getCaseBoard(Board board) { 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()))) { // if found another number tile - // and it's data is different - // than the element we're - // working with - only = false; // set only to false + && !(d.getData().equals(((NurikabeCell) element).getData()))) { + only = false; } } - 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 + // 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) { caseBoard.addPickableElement(element); // add that room as a pickable element } } @@ -126,16 +125,21 @@ 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 numbaCell = + NurikabeCell numberCell = 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 origPoint = new Point(numberCell.getLocation().x, numberCell.getLocation().y); + int filledRoomSize = numberCell.getData(); // size of room we want afterward + Point left = new Point(-1, 0); Point right = new Point(1, 0); Point bot = new Point(0, -1); @@ -145,92 +149,147 @@ 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 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 + 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 } - 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 + + NurikabeCell newCell = nuriBoard.getCell(newPoint.x, newPoint.y); + if (checkedPoints.contains(newPoint)) { + continue; // already checked + } + + 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; break; } } - 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 + if (unique) { + caseBoard.addModifiedData(newCell); + cases.add(caseBoard); } + } else if (newRoomSet.size() < filledRoomSize) { + generateCases( + nuriBoard, + newCell, + filledRoomSize, + directions, + checkedPoints, + cases, + origPoint); } - 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 } + newCell.setData(NurikabeType.UNKNOWN.toValue()); + newCell.setModifiable(true); + checkedPoints.remove(newPoint); } - legitCases = cases.size(); } - return cases; + } + + /** + * 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; + } + } + } + } + return false; } /** 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 75bba369f..ffd7c491d 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-PLAC-0002")) { + if (e.getElementID().equals("STTT-ELEM-0004")) { this.data = ShortTruthTableCellType.FALSE; } // Green Element else { - if (e.getElementID().equals("STTT-PLAC-0001")) { + if (e.getElementID().equals("STTT-ELEM-0002")) { this.data = ShortTruthTableCellType.TRUE; } // Unknown Element else { - if (e.getElementID().equals("STTT-PLAC-0003")) { + if (e.getElementID().equals("STTT-ELEM-0005")) { this.data = ShortTruthTableCellType.UNKNOWN; } // Argument Element else { - if (e.getElementID().equals("STTT-UNPL-0001")) { + if (e.getElementID().equals("STTT-ELEM-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-UNPL-0002")) { + if (e.getElementID().equals("STTT-ELEM-0003")) { 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 9294fba4e..912fd2672 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class ArgumentElement extends NonPlaceableElement { +public class ArgumentElement extends PlaceableElement { public ArgumentElement() { super( - "STTT-UNPL-0001", + "STTT-ELEM-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 783186baa..56221fef3 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-PLAC-0001", + "STTT-ELEM-0002", "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 5fed4b1df..b82ebc2cb 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class LogicSymbolElement extends NonPlaceableElement { +public class LogicSymbolElement extends PlaceableElement { public LogicSymbolElement() { super( - "STTT-UNPL-0002", + "STTT-ELEM-0003", "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 e2a589b65..2114e62ec 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-PLAC-0002", + "STTT-ELEM-0004", "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 d475bc05d..52b54f202 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-PLAC-0003", + "STTT-ELEM-0005", "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 471631553..c5421169f 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,6 +1,5 @@ -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 +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 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 58d2068b2..22b49fd77 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,6 +44,9 @@ 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 99f771246..8aeb51a46 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,6 +85,11 @@ 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 df5ba78a3..44f416cef 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 >= 4 && rows == columns; + return rows >= 3 && 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 4cd09b254..0fc133786 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-UNPL-0003")) { + if (!e.getElementID().equals("SKYS-ELEM-0001")) { 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(0); + clue.setData(1); } } else { - if (clue.getData() > 0) { + if (clue.getData() > 1) { 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 1cf9a357b..9e7283b20 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-UNPL-0001": + case "SKYS-ELEM-0002": this.data = 0; break; - case "SKYS-UNPL-0002": + case "SKYS-ELEM-0001": 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 5a49a1476..cfb3388ff 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,6 +54,22 @@ 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 deleted file mode 100644 index 64c9033e6..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/skyscrapers/elements/ClueTile.java +++ /dev/null @@ -1,14 +0,0 @@ -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 4d6b37c9a..f60e5fe8b 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class NumberTile extends NonPlaceableElement { +public class NumberTile extends PlaceableElement { public NumberTile() { super( - "SKYS-UNPL-0002", + "SKYS-ELEM-0001", "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 2fb21193a..07f6a1238 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class UnknownTile extends NonPlaceableElement { +public class UnknownTile extends PlaceableElement { public UnknownTile() { super( - "SKYS-UNPL-0001", + "SKYS-ELEM-0002", "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 604e1824e..14e76a29d 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,3 +1,2 @@ -SKYS-UNPL-0001: Unknown Tile -SKYS-UNPL-0002: Number Tile -SKYS-UNPL-0003: Clue "Tile" \ No newline at end of file +SKYS-ELEM-0001: NumberTile +SKYS-ELEM-0002: UnknownTile \ 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 45bdadea3..b48962c41 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,6 +61,9 @@ 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 145dd6ee2..4f8e1df6b 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,6 +47,9 @@ 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 deleted file mode 100644 index 5a9ec0f0a..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/allfiles.txt +++ /dev/null @@ -1,235 +0,0 @@ -//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 99f42886e..c4bbf7297 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class BlackTile extends NonPlaceableElement { +public class BlackTile extends PlaceableElement { 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 13ada3f4d..793d4dbeb 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class StarTile extends NonPlaceableElement { +public class StarTile extends PlaceableElement { 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 425fb5d5e..30921de8d 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class UnknownTile extends NonPlaceableElement { +public class UnknownTile extends PlaceableElement { 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 new file mode 100644 index 000000000..82352bd04 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/starbattle/elements/starbattle_elements_reference_sheet.txt @@ -0,0 +1,4 @@ +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 df900dcd5..efd86bd7b 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,6 +84,10 @@ 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 deleted file mode 100644 index f18965fd6..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/starbattle/rules/starbattle_reference_sheet.txt +++ /dev/null @@ -1,19 +0,0 @@ -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 new file mode 100644 index 000000000..f7893ca32 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/ModelSudokuBoard.java @@ -0,0 +1,17 @@ +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 0b6971235..c5f9eec2a 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.PossibleNumberCaseRule; +import edu.rpi.legup.puzzle.sudoku.rules.PossibleCellsForNumberRegionCaseRule; 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, PossibleNumberCaseRule caseRule, SudokuCell cell) { + SudokuBoard baseBoard, PossibleCellsForNumberRegionCaseRule 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 877c92665..c27269536 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/Sudoku.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/Sudoku.java @@ -29,6 +29,8 @@ 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 006e6c0a5..75e5820a0 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCell.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuCell.java @@ -1,7 +1,9 @@ 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; @@ -59,4 +61,32 @@ 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 9b24f13da..bcad1a0ce 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 68bf1e795..5084279c3 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuImporter.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuImporter.java @@ -110,16 +110,6 @@ 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 aa58f9a23..474feb342 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuView.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/SudokuView.java @@ -22,10 +22,15 @@ public SudokuView(SudokuBoard board) { for (int k = 0; k < gridSize.width; k++) { Point location = new Point( - k * elementSize.width + (k / minorSize) * 4 + 5, + k * elementSize.width + (k / minorSize) * 4 + 5, // i * elementSize.height + (i / minorSize) * 4 + 5); + // Point location = + // new Point( + // k * elementSize.width, + // i * elementSize.height); 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 12183d70d..a94d10e64 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 @@ -3,24 +3,11 @@ import edu.rpi.legup.model.elements.PlaceableElement; public class NumberTile extends PlaceableElement { - private int object_num; - public NumberTile() { - 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; + super( + "SUDO-ELEM-0001", + "Number Tile", + "A number tile", + "edu/rpi/legup/images/sudoku/tiles/NumberTile.png"); } } 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 new file mode 100644 index 000000000..eb9a2c103 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/elements/UnknownTile.java @@ -0,0 +1,13 @@ +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"); + } +} 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 new file mode 100644 index 000000000..b8df27eb6 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/elements/sudoku_elements_reference_sheet.txt @@ -0,0 +1,2 @@ +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 deleted file mode 100644 index 190679b41..000000000 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/AdvancedDeductionDirectRule.java +++ /dev/null @@ -1,99 +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.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 fd03ef36c..6544bf7c3 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/forcedByElimination.png"); + "edu/rpi/legup/images/sudoku/rules/forcedByElimination.png"); } /** @@ -32,52 +32,146 @@ 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"; } - int size = initialBoard.getSize(); - + // Get defaults Set region = initialBoard.getRegion(cell.getGroupIndex()); Set row = initialBoard.getRow(cell.getLocation().y); Set col = initialBoard.getCol(cell.getLocation().x); - boolean contains = false; - if (region.size() == size - 1) { - for (SudokuCell c : region) { - if (cell.getData() == c.getData()) { + // 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()) { contains = true; - break; } } + for (SudokuCell cc : ccol) { + if (cc.getData() == cell.getData()) { + contains = true; + } + } + // Stop if another cell can hold number if (!contains) { - return null; + restrained = false; + break; } } - if (row.size() == size - 1) { - contains = false; - for (SudokuCell c : row) { - if (cell.getData() == c.getData()) { + // 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()) { contains = true; - break; } } + // Stop if another cell can hold number if (!contains) { - return null; + restrained = false; + break; } } - if (col.size() == size - 1) { - contains = false; - for (SudokuCell c : col) { - if (cell.getData() == c.getData()) { + // 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()) { contains = true; - break; } } + for (SudokuCell cc : crow) { + if (cc.getData() == cell.getData()) { + contains = true; + } + } + // Stop if another cell can hold number if (!contains) { - return null; + restrained = false; + break; } } + // 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 ca0ac3023..333d91749 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/forcedByDeduction.png"); + "edu/rpi/legup/images/sudoku/rules/forcedByDeduction.png"); } /** @@ -32,28 +32,37 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem SudokuBoard initialBoard = (SudokuBoard) transition.getParents().get(0).getBoard(); SudokuBoard finalBoard = (SudokuBoard) transition.getBoard(); - int index = puzzleElement.getIndex(); + // Assign basics 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; + int groupNum = (rowIndex / groupDim) * groupDim + (colIndex / groupDim); + + // Create hashset of all numbers 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, colIndex); + SudokuCell cell = initialBoard.getCell(i, rowIndex); numbers.remove(cell.getData()); } for (int i = 0; i < groupSize; i++) { - SudokuCell cell = initialBoard.getCell(rowIndex, i); + SudokuCell cell = initialBoard.getCell(colIndex, i); numbers.remove(cell.getData()); } + + // Check if plausible if (numbers.size() > 1) { return super.getInvalidUseOfRuleMessage() + ": The number at the index is not forced"; } else { @@ -64,7 +73,11 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem + ": The number at the index is forced but not correct"; } } - return null; + if (numbers.toArray(new Integer[1])[0] == puzzleElement.getData()) { + return null; + } + return super.getInvalidUseOfRuleMessage() + + ": The number at the index is forced but not correct"; } /** 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 new file mode 100644 index 000000000..c8d627634 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberColumnContradictionRule.java @@ -0,0 +1,90 @@ +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 new file mode 100644 index 000000000..f5106b858 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberRegionContradictionRule.java @@ -0,0 +1,90 @@ +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 new file mode 100644 index 000000000..e3f9f764a --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoCellForNumberRowContradictionRule.java @@ -0,0 +1,90 @@ +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/NoSolutionContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoNumberForCellContradictionRule.java similarity index 72% rename from src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoSolutionContradictionRule.java rename to src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoNumberForCellContradictionRule.java index e44728d3e..6ea8f0a2a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoSolutionContradictionRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/NoNumberForCellContradictionRule.java @@ -8,14 +8,14 @@ import java.util.HashSet; import java.util.Set; -public class NoSolutionContradictionRule extends ContradictionRule { +public class NoNumberForCellContradictionRule extends ContradictionRule { - public NoSolutionContradictionRule() { + public NoNumberForCellContradictionRule() { super( - "SUDO-CONT-0001", - "No Solution for Cell", + "SUDO-CONT-0004", + "No Number for Cell", "Process of elimination yields no valid numbers for an empty cell.", - "edu/rpi/legup/images/sudoku/NoSolution.png"); + "edu/rpi/legup/images/sudoku/rules/NoSolution.png"); } /** @@ -41,21 +41,19 @@ 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 (int i = 1; i <= groupSize; i++) { - solution.add(i); + for (SudokuCell s : region) { + solution.add(s.getData()); } - - for (SudokuCell c : region) { - solution.remove(c.getData()); - } - for (SudokuCell c : row) { - solution.remove(c.getData()); + for (SudokuCell s : row) { + solution.add(s.getData()); } - for (SudokuCell c : col) { - solution.remove(c.getData()); + + for (SudokuCell s : col) { + solution.add(s.getData()); } + solution.remove(0); - if (solution.isEmpty()) { + if (solution.size() == 9) { return null; } diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumberCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberColumnCaseRule.java similarity index 53% rename from src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumberCaseRule.java rename to src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberColumnCaseRule.java index e6ab0e64c..bab0bc79b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumberCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberColumnCaseRule.java @@ -6,21 +6,26 @@ 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.PossibleNumberCaseBoard; +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.List; import java.util.Set; -public class PossibleNumberCaseRule extends CaseRule { +public class PossibleCellsForNumberColumnCaseRule extends CaseRule { - public PossibleNumberCaseRule() { + // Board math for translating indexes to numbers + private ModelSudokuBoard model = new ModelSudokuBoard(); + + // Old board for caseBoard reference + private SudokuBoard lagBoard; + + public PossibleCellsForNumberColumnCaseRule() { super( - "SUDO-CASE-0002", - "Possible Numbers for Cell", + "SUDO-CASE-0004", + "Possible Cells for Number - Column", "An empty cell has a limited set of possible numbers that can fill it.", - "edu/rpi/legup/images/sudoku/PossibleValues.png"); + "edu/rpi/legup/images/sudoku/rules/possible_cells_number_column.png"); } /** @@ -50,12 +55,12 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem @Override public CaseBoard getCaseBoard(Board board) { - 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); + 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); } return caseBoard; } @@ -69,7 +74,7 @@ public CaseBoard getCaseBoard(Board board) { */ @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { - return getCases(board, puzzleElement, 1, GroupType.REGION); + return getCases(board, puzzleElement, 1, GroupType.COLUMN); } /** @@ -84,48 +89,19 @@ public ArrayList getCases(Board board, PuzzleElement puzzleElement) { public ArrayList getCases( Board board, PuzzleElement puzzleElement, int value, GroupType groupType) { ArrayList cases = new ArrayList<>(); - SudokuBoard sudokuBoard = (SudokuBoard) board; - List caseCells = new ArrayList<>(); - SudokuCell cell = (SudokuCell) puzzleElement; + SudokuBoard sudokuBoard = lagBoard; + SudokuCell sourceCell = (SudokuCell) puzzleElement; - 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); + 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); } } - - 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/PossibleCellsForNumberRegionCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberRegionCaseRule.java new file mode 100644 index 000000000..47e408369 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberRegionCaseRule.java @@ -0,0 +1,104 @@ +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 new file mode 100644 index 000000000..868541377 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberRowCaseRule.java @@ -0,0 +1,107 @@ +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/PossibleCellCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumbersForCellCaseRule.java similarity index 59% rename from src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellCaseRule.java rename to src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumbersForCellCaseRule.java index fb6da62d4..e17acc26b 100644 --- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellCaseRule.java +++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumbersForCellCaseRule.java @@ -5,19 +5,18 @@ 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.SudokuBoard; -import edu.rpi.legup.puzzle.sudoku.SudokuCell; +import edu.rpi.legup.puzzle.sudoku.*; import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; +import java.util.List; -public class PossibleCellCaseRule extends CaseRule { - public PossibleCellCaseRule() { +public class PossibleNumbersForCellCaseRule extends CaseRule { + + public PossibleNumbersForCellCaseRule() { super( "SUDO-CASE-0001", - "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"); + "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"); } /** @@ -66,42 +65,34 @@ public CaseBoard getCaseBoard(Board board) { */ @Override public ArrayList getCases(Board board, PuzzleElement puzzleElement) { - ArrayList cases = new ArrayList<>(); - SudokuBoard sudokuBoard = (SudokuBoard) board; - SudokuCell cell = (SudokuCell) puzzleElement; - - 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()); - } - } + return getCases(board, puzzleElement, 1, GroupType.REGION); + } - int colNum = cell.getLocation().x; - for (SudokuCell c : sudokuBoard.getRegion(colNum)) { - if (c.getData().equals(c.getData())) { - possibleValue.remove(c.getData()); - } + /** + * 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; } - for (Integer i : possibleValue) { - SudokuBoard newCase = sudokuBoard.copy(); + SudokuBoard sudokuBoard = (SudokuBoard) board; + List caseCells = new ArrayList<>(); + SudokuCell cell = (SudokuCell) puzzleElement; - PuzzleElement newCasePuzzleElement = newCase.getPuzzleElement(puzzleElement); - newCasePuzzleElement.setData(i); - newCase.addModifiedData(newCasePuzzleElement); + for (int i = 1; i <= 9; i++) { + Board newCase = sudokuBoard.copy(); + PuzzleElement element = newCase.getPuzzleElement(puzzleElement); + element.setData(i); + newCase.addModifiedData(element); cases.add(newCase); } 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 955414e8e..f8172d071 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-0002", + "SUDO-CONT-0005", "Repeated Numbers", "Two identical numbers are placed in the same group.", - "edu/rpi/legup/images/sudoku/RepeatedNumber.png"); + "edu/rpi/legup/images/sudoku/rules/RepeatedNumber.png"); } /** @@ -29,39 +29,51 @@ 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(); - } - Set region = sudokuBoard.getRegion(cell.getGroupIndex()); - Set row = sudokuBoard.getRow(cell.getLocation().y); - Set col = sudokuBoard.getCol(cell.getLocation().x); + // 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 regionDup = new HashSet<>(); - Set rowDup = new HashSet<>(); - Set colDup = new HashSet<>(); + Set col = sudokuBoard.getCol(i); + Set colDup = new HashSet<>(); - for (SudokuCell c : region) { - if (regionDup.contains(c.getData())) { - return null; + // 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()); } - regionDup.add(c.getData()); - } - for (SudokuCell c : row) { - if (rowDup.contains(c.getData())) { - return null; + for (SudokuCell c : row) { + if (c.getData() == 0) { + continue; + } + if (rowDup.contains(c.getData())) { + return null; + } + rowDup.add(c.getData()); } - rowDup.add(c.getData()); - } - for (SudokuCell c : col) { - if (colDup.contains(c.getData())) { - return null; + for (SudokuCell c : col) { + if (c.getData() == 0) { + continue; + } + if (colDup.contains(c.getData())) { + return null; + } + colDup.add(c.getData()); } - 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 a8635330d..ceffa168c 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,9 +1,13 @@ -SUDO-BASC-0001 : AdvancedDeductionDirectRule SUDO-BASC-0002 : LastCellForNumberDirectRule SUDO-BASC-0003 : LastNumberForCellDirectRule -SUDO-CONT-0001 : NoSolutionContradictionRule -SUDO-CONT-0002 : RepeatedNumberContradictionRule +SUDO-CONT-0001 : NoCellForNumberRegionContradictionRule +SUDO-CONT-0002 : NoCellForNumberRowContradictionRule +SUDO-CONT-0003 : NoCellForNumberColumnContradictionRule +SUDO-CONT-0004 : NoNumberForCellContradictionRule +SUDO-CONT-0005 : RepeatedNumberContradictionRule -SUDO-CASE-0001 : PossibleCellCaseRule -SUDO-CASE-0002 : PossibleNumberCaseRule \ No newline at end of file +SUDO-CASE-0001 : PossibleNumbersForCellCaseRule +SUDO-CASE-0002 : PossibleCellsForNumberRegionCaseRule +SUDO-CASE-0003 : PossibleCellsForNumberRowCaseRule +SUDO-CASE-0004 : PossibleCellsForNumberColumnCaseRule \ No newline at end of file diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/Thermometer.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/Thermometer.java new file mode 100644 index 000000000..8138104f5 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/Thermometer.java @@ -0,0 +1,56 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.model.Puzzle; +import edu.rpi.legup.model.gameboard.Board; + +// basically just copy-pasted from dev guide on wiki +public class Thermometer extends Puzzle { + public Thermometer() { + super(); + + this.name = "Thermometer"; + + this.importer = new ThermometerImporter(this); + this.exporter = new ThermometerExporter(this); + // we do not have a thermometerCellFactory class as + // thermometerVial has its own thermometerCell factory method + } + + /** Initializes the game board. Called by the invoker of the class */ + @Override + public void initializeView() { + boardView = new ThermometerView((ThermometerBoard) currentBoard); + boardView.setBoard(currentBoard); + addBoardListener(boardView); + } + + /** + * Generates a random edu.rpi.legup.puzzle based on the difficulty + * + * @param difficulty level of difficulty (1-10) + * @return board of the random edu.rpi.legup.puzzle + */ + @Override + 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 + */ + @Override + 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) {} +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerBoard.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerBoard.java new file mode 100644 index 000000000..95ff7ff83 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerBoard.java @@ -0,0 +1,139 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.model.gameboard.GridBoard; +import java.awt.*; +import java.util.ArrayList; + +public class ThermometerBoard extends GridBoard { + + // an array containing all of our vials on the board + private ArrayList thermometerVials; + + // representations of the number requirements along rows and columns of the board + // we use rotation to store the number + private ArrayList colNumbers; + private ArrayList rowNumbers; + + private ThermometerCell dummyCell; + + // constructors for the boards and variables + public ThermometerBoard(int width, int height) { + super(width, height); + + // initializing the row/col number arrays with zeros, so they can be + // easily updated using the setRow/ColNumber functions + colNumbers = new ArrayList<>(); + for (int i = 0; i < width - 1; i++) { + ThermometerCell cell = + new ThermometerCell( + new Point(i, height - 1), + ThermometerType.UNKNOWN, + ThermometerFill.UNKNOWN, + 0); + cell.setIndex((height - 1) * height + i); + colNumbers.add(cell); + this.setCell(i, height - 1, cell); + } + rowNumbers = new ArrayList<>(); + for (int i = 0; i < height - 1; i++) { + ThermometerCell cell = + new ThermometerCell( + new Point(width - 1, i), + ThermometerType.UNKNOWN, + ThermometerFill.UNKNOWN, + 0); + cell.setIndex(i * height + (width - 1)); + rowNumbers.add(cell); + this.setCell(width - 1, i, cell); + } + + // setting a dummy cell so board doesn't have null cells + dummyCell = + new ThermometerCell( + new Point(width - 1, height - 1), + ThermometerType.UNKNOWN, + ThermometerFill.UNKNOWN, + -1); + dummyCell.setIndex((height - 1) * height + width); + this.setCell(width - 1, height - 1, dummyCell); + + // creating our empty vial of thermometers to add to + thermometerVials = new ArrayList<>(); + } + + // setters and accessors for our array of vials + public void addVial(ThermometerVial v) { + thermometerVials.add(v); + } + + public ArrayList getVials() { + return thermometerVials; + } + + // our setters for row/col numbers with simple input verification + public boolean setRowNumber(int row, int num) { + // first check is to verify we are updating an element in range + // second check is to verify the new number can be achieved by the puzzle + if (row < rowNumbers.size() && num <= colNumbers.size()) { + rowNumbers.get(row).setRotation(num); + return true; + } + return false; + } + + public boolean setColNumber(int col, int num) { + // first check is to verify we are updating an element in range + // second check is to verify the new number can be achieved by the puzzle + if (col < colNumbers.size() && num <= rowNumbers.size()) { + colNumbers.get(col).setRotation(num); + return true; + } + return false; + } + + // basic accessors for row/col numbers + public int getRowNumber(int row) { + if (row < 0 || row >= rowNumbers.size()) return -1; + return rowNumbers.get(row).getRotation(); + } + + public int getColNumber(int col) { + if (col < 0 || col >= rowNumbers.size()) return -1; + return colNumbers.get(col).getRotation(); + } + + // Accessors for saving row/column + public ArrayList getRowNumbers() { + return rowNumbers; + } + + public ArrayList getColNumbers() { + return colNumbers; + } + + // we all suck at programming so instead of using provided array list + // we use our own array lists to keep track of the vials + // marginally useful because it means we are guaranteed to get a + // thermometer cell when calling get cell, but using some type casting + // this override function could very likely be refactored out + @Override + public ThermometerCell getCell(int x, int y) { + for (ThermometerVial vial : this.thermometerVials) { + for (ThermometerCell cell : vial.getCells()) { + if (cell.getLocation().x == x && cell.getLocation().y == y) return cell; + } + } + + for (ThermometerCell cell : rowNumbers) { + if (cell.getLocation().x == x && cell.getLocation().y == y) return cell; + } + + for (ThermometerCell cell : colNumbers) { + if (cell.getLocation().x == x && cell.getLocation().y == y) return cell; + } + + if (x == this.getWidth() - 1 && y == this.getHeight() - 1) return dummyCell; + + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerCell.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerCell.java new file mode 100644 index 000000000..175a455b4 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerCell.java @@ -0,0 +1,67 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.model.gameboard.GridCell; +import java.awt.Point; + +public class ThermometerCell extends GridCell { + + // information about the cell needed to display it + private ThermometerType type; + private ThermometerFill fill; + private int rotation; + + public ThermometerCell(Point location, ThermometerType t, ThermometerFill f, int r) { + // since we do not use get/set data value int can be any value + super(1, location); + type = t; + fill = f; + rotation = r; + } + + // Note: setdata does not work for our purposes + public void setType(ThermometerType t) { + type = t; + } + + public ThermometerType getType() { + return type; + } + + public void setFill(ThermometerFill f) { + fill = f; + } + + public ThermometerFill getFill() { + return fill; + } + + public void setRotation(int r) { + rotation = r; + } + + public int getRotation() { + return rotation; + } + + @Override + public ThermometerCell copy() { + ThermometerCell copy = + new ThermometerCell((Point) location.clone(), this.type, this.fill, this.rotation); + copy.setIndex(index); + copy.setModifiable(isModifiable); + copy.setGiven(isGiven); + return copy; + } + + @Override + public String toString() { + return "(" + + location.getX() + + ", " + + location.getY() + + ") TYPE = " + + getType() + + " FILL = " + + getFill(); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerController.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerController.java new file mode 100644 index 000000000..cd2135bd7 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerController.java @@ -0,0 +1,44 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.controller.ElementController; +import edu.rpi.legup.model.gameboard.PuzzleElement; +import java.awt.event.MouseEvent; + +public class ThermometerController extends ElementController { + + // method for updating thermometer cells since number cells have unknown for + // their fill type we don't need to worry about end user modifying them with this + @Override + public void changeCell(MouseEvent e, PuzzleElement data) { + ThermometerCell cell = (ThermometerCell) 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.getFill() == ThermometerFill.EMPTY) { + cell.setFill(ThermometerFill.FILLED); + } else if (cell.getFill() == ThermometerFill.FILLED) { + cell.setFill(ThermometerFill.BLOCKED); + } else { + cell.setFill(ThermometerFill.EMPTY); + } + } + } else if (e.getButton() == MouseEvent.BUTTON3) { + if (cell.getFill() == ThermometerFill.EMPTY) { + cell.setFill(ThermometerFill.BLOCKED); + } else if (cell.getFill() == ThermometerFill.BLOCKED) { + cell.setFill(ThermometerFill.FILLED); + } else { + cell.setFill(ThermometerFill.EMPTY); + } + } else if (e.getButton() == MouseEvent.BUTTON2) { + System.out.println("[DEBUG] " + cell); + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerElementView.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerElementView.java new file mode 100644 index 000000000..0657e95b0 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerElementView.java @@ -0,0 +1,311 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.ui.boardview.GridElementView; +import java.awt.*; +import java.io.IOException; +import javax.imageio.ImageIO; + +public class ThermometerElementView extends GridElementView { + + // mixture of stuff stolen from tree tent and dev guide + private static final Font FONT = new Font("TimesRoman", Font.BOLD, 16); + private static final Color FONT_COLOR = Color.BLACK; + + public ThermometerElementView(ThermometerCell cell) { + super(cell); + } + + @Override + public ThermometerCell getPuzzleElement() { + return (ThermometerCell) super.getPuzzleElement(); + } + + // method for drawing a thermometer cell + // basically copy/pasted from tree tent drawing tent images + @Override + public void drawElement(Graphics2D graphics2D) { + + ThermometerCell cell = (ThermometerCell) puzzleElement; + ThermometerType type = cell.getType(); + ThermometerFill fill = cell.getFill(); + int rotation = cell.getRotation(); + + graphics2D.drawImage( + imageSrc(type, fill, rotation), + location.x, + location.y, + size.width, + size.height, + null, + null); + + graphics2D.setColor(Color.BLACK); + graphics2D.drawRect(location.x, location.y, size.width, size.height); + } + + // modified code from tree trent to display images + private Image imageSrc(ThermometerType t, ThermometerFill f, int r) { + + // will have a 36 switch case at end to determine which image gets opened + int result = 0; + + // 100 = NORTH, 200 = WEST, 300 = SOUTH, 400 = EAST + switch (r) { + case 0 -> result += 100; + case 90 -> result += 400; + case 180 -> result += 300; + case 270 -> result += 200; + default -> { + System.out.println("ThermometerElementView: Invalid Rotation"); + return null; + } + } + + // 10 = EMPTY, 20 = FILLED, 30 = BLOCKED + switch (f) { + case ThermometerFill.EMPTY -> result += 10; + case ThermometerFill.FILLED -> result += 20; + case ThermometerFill.BLOCKED -> result += 30; + default -> { + System.out.println("ThermometerElementView: Invalid Fill"); + return null; + } + } + + // 1 = HEAD, 2 = SHAFT, 3 = TIP + switch (t) { + case ThermometerType.HEAD -> result += 1; + case ThermometerType.SHAFT -> result += 2; + case ThermometerType.TIP -> result += 3; + default -> { + System.out.println("ThermometerElementView: Invalid Type"); + return null; + } + } + + try { + switch (result) { + case 111 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadEmpN.png")); + } + + case 112 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftEmpN.png")); + } + + case 113 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipEmpN.png")); + } + + case 121 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadFillN.png")); + } + + case 122 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftFillN.png")); + } + + case 123 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipFillN.png")); + } + + case 131 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadBlockN.png")); + } + + case 132 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftBlockN.png")); + } + + case 133 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipBlockN.png")); + } + + case 211 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadEmpE.png")); + } + + case 212 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftEmpE.png")); + } + + case 213 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipEmpE.png")); + } + + case 221 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadFillE.png")); + } + + case 222 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftFillE.png")); + } + + case 223 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipFillE.png")); + } + + case 231 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadBlockE.png")); + } + + case 232 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftBlockE.png")); + } + + case 233 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipBlockE.png")); + } + + case 311 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadEmpS.png")); + } + + case 312 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftEmpS.png")); + } + + case 313 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipEmpS.png")); + } + + case 321 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadFillS.png")); + } + + case 322 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftFillS.png")); + } + + case 323 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipFillS.png")); + } + + case 331 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadBlockS.png")); + } + + case 332 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftBlockS.png")); + } + + case 333 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipBlockS.png")); + } + + case 411 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadEmpW.png")); + } + + case 412 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftEmpW.png")); + } + + case 413 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipEmpW.png")); + } + + case 421 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadFillW.png")); + } + + case 422 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftFillW.png")); + } + + case 423 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipFillW.png")); + } + + case 431 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/HeadBlockW.png")); + } + + case 432 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/ShaftBlockW.png")); + } + + case 433 -> { + return ImageIO.read( + ClassLoader.getSystemResourceAsStream( + "edu/rpi/legup/images/thermometer/Elements/TipBlockW.png")); + } + } + } catch (IOException e) { + System.out.println("ThermometerElementView: Unexpected Issue"); + return null; + } + + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerExporter.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerExporter.java new file mode 100644 index 000000000..d4e6dbd39 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerExporter.java @@ -0,0 +1,72 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.model.PuzzleExporter; +import java.util.ArrayList; +import org.w3c.dom.Document; + +public class ThermometerExporter extends PuzzleExporter { + + public ThermometerExporter(Thermometer thermometer) { + super(thermometer); + } + + @Override + protected org.w3c.dom.Element createBoardElement(Document newDocument) { + ThermometerBoard board = (ThermometerBoard) puzzle.getTree().getRootNode().getBoard(); + + // Creating the XML section for the board + org.w3c.dom.Element boardElement = newDocument.createElement("board"); + boardElement.setAttribute("width", String.valueOf(board.getWidth() - 1)); + boardElement.setAttribute("height", String.valueOf(board.getHeight() - 1)); + + // Creating the XML section for the vials and appending to the board + org.w3c.dom.Element vialsElement = newDocument.createElement("vials"); + ArrayList vials = board.getVials(); + for (ThermometerVial vial : vials) { + org.w3c.dom.Element vialElement = newDocument.createElement("vial"); + // The way the vials are created are with the head (bulb) position and the final + // position + // This implementation doesn't allow for curved thermometers, but for right now that's + // fine + vialElement.setAttribute( + "headx", String.valueOf((int) vial.getHead().getLocation().getX())); + vialElement.setAttribute( + "heady", String.valueOf((int) vial.getHead().getLocation().getY())); + vialElement.setAttribute( + "tailx", String.valueOf((int) vial.getTail().getLocation().getX())); + vialElement.setAttribute( + "taily", String.valueOf((int) vial.getTail().getLocation().getY())); + vialsElement.appendChild(vialElement); + } + boardElement.appendChild(vialsElement); + + // Creating the XML section for the row numbers and appending to the board + org.w3c.dom.Element rowNumbersElement = newDocument.createElement("rowNumbers"); + ArrayList rowNumbers = board.getRowNumbers(); + // The row numbers are the numbers on the right most column, labeling how many filled + // sections + // are in the row + for (ThermometerCell cell : rowNumbers) { + int number = cell.getRotation(); + org.w3c.dom.Element rowNumberElement = newDocument.createElement("row"); + rowNumberElement.setAttribute("value", String.valueOf(number)); + rowNumbersElement.appendChild(rowNumberElement); + } + boardElement.appendChild(rowNumbersElement); + + // Creating the XML section for the col numbers and appending ot the board + org.w3c.dom.Element colNumbersElement = newDocument.createElement("colNumbers"); + // The col numbers are the numbers on the bottom row, labeling how many filled sections + // are in the column + ArrayList colNumbers = board.getColNumbers(); + for (ThermometerCell cell : colNumbers) { + int number = cell.getRotation(); + org.w3c.dom.Element colNumberElement = newDocument.createElement("col"); + colNumberElement.setAttribute("value", String.valueOf(number)); + colNumbersElement.appendChild(colNumberElement); + } + boardElement.appendChild(colNumbersElement); + + return boardElement; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerFill.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerFill.java new file mode 100644 index 000000000..34a1ff12e --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerFill.java @@ -0,0 +1,8 @@ +package edu.rpi.legup.puzzle.thermometer; + +public enum ThermometerFill { + UNKNOWN, + EMPTY, + FILLED, + BLOCKED; +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerImporter.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerImporter.java new file mode 100644 index 000000000..711418d63 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerImporter.java @@ -0,0 +1,196 @@ +package edu.rpi.legup.puzzle.thermometer; + +import static java.lang.Math.max; +import static java.lang.Math.min; + +import edu.rpi.legup.model.PuzzleImporter; +import edu.rpi.legup.save.InvalidFileFormatException; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +public class ThermometerImporter extends PuzzleImporter { + + // basic stuff stolen from dev guide/filled in by default + public ThermometerImporter(Thermometer thermometer) { + super(thermometer); + } + + @Override + public boolean acceptsRowsAndColumnsInput() { + return false; + } + + @Override + public boolean acceptsTextInput() { + return false; + } + + @Override + public void initializeBoard(int rows, int columns) {} + + // method for initializing board from an xml file which has + // a provided width/height + @Override + public void initializeBoard(Node node) throws InvalidFileFormatException { + // sticking everything in a try statement because god has forsaken everyone + try { + // checking basic formatting of file + if (!node.getNodeName().equalsIgnoreCase("board")) { + throw new InvalidFileFormatException( + "thermometer Importer: cannot find board puzzleElement"); + } + + // getting the list of vials to turn into real vials + Element boardElement = (Element) node; + if (boardElement.getElementsByTagName("vials").getLength() == 0) { + throw new InvalidFileFormatException( + "thermometer Importer: no puzzleElement found for board"); + } + Element dataElement = (Element) boardElement.getElementsByTagName("vials").item(0); + NodeList elementDataList = dataElement.getElementsByTagName("vial"); + + // checking both a width and height were provided for the board + ThermometerBoard thermometerBoard = null; + if (!boardElement.getAttribute("width").isEmpty() + && !boardElement.getAttribute("height").isEmpty()) { + + // grabbing the height/width of the board + int width = Integer.parseInt(boardElement.getAttribute("width")); + int height = Integer.parseInt(boardElement.getAttribute("height")); + + // grabbing the lists of rowNumbers/colNumbers + Element rowElement = + (Element) boardElement.getElementsByTagName("rowNumbers").item(0); + NodeList rowNodeList = rowElement.getElementsByTagName("row"); + + Element colElement = + (Element) boardElement.getElementsByTagName("colNumbers").item(0); + NodeList colNodeList = colElement.getElementsByTagName("col"); + + // checking that the number of row and col numbers agrees with height/width of board + if (colNodeList.getLength() != width) { + throw new InvalidFileFormatException( + "Mismatch between width and number of colNums.\n colNodeList.length:" + + colNodeList.getLength() + + " width:" + + width); + } + if (rowNodeList.getLength() != height) { + throw new InvalidFileFormatException( + "thermometer Importer: no rowNumbers found for board"); + } + + // finally creating our thermometer board, we add one to the size since row/col + // numbers + // are considered cells on the grid + thermometerBoard = new ThermometerBoard(width + 1, height + 1); + // adding row and column numbers to our board + importRowColNums(rowNodeList, colNodeList, thermometerBoard); + } else { + throw new InvalidFileFormatException( + "thermometer Importer: invalid board height/width"); + } + + // grabbing height/width from board, need to subtract 1 + // because grids height/width is 1 bigger than number of vials on board + int width = thermometerBoard.getWidth() - 1; + int height = thermometerBoard.getHeight() - 1; + + // adding in the vials + for (int i = 0; i < elementDataList.getLength(); i++) { + importThermometerVial(elementDataList.item(i), thermometerBoard); + } + + // verifying all vial cells were filled by vials + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + if (thermometerBoard.getCell(x, y) == null) { + throw new InvalidFileFormatException( + "Thermometer importer Undefined tile at (" + x + "," + y + ")"); + } + } + } + + puzzle.setCurrentBoard(thermometerBoard); + } catch (NumberFormatException e) { + throw new InvalidFileFormatException( + "thermometer Importer: unknown value where integer expected"); + } + } + + @Override + public void initializeBoard(String[] statements) + throws UnsupportedOperationException, IllegalArgumentException {} + + private void importRowColNums(NodeList rowNodes, NodeList colNodes, ThermometerBoard board) + throws InvalidFileFormatException { + + // going through our list or row nodes grabbed from the xml file and + // then calling the thermometer boards setRowNumber function to update the value + for (int i = 0; i < rowNodes.getLength(); i++) { + Node node = rowNodes.item(i); + int rowNum = + Integer.parseInt(node.getAttributes().getNamedItem("value").getNodeValue()); + if (!board.setRowNumber(i, rowNum)) { + throw new InvalidFileFormatException("thermometer Importer: out of bounds rowNum"); + } + } + + // same process but for col numbers + for (int i = 0; i < colNodes.getLength(); i++) { + Node node = colNodes.item(i); + int colNum = + Integer.parseInt(node.getAttributes().getNamedItem("value").getNodeValue()); + if (!board.setColNumber(i, colNum)) { + throw new InvalidFileFormatException("thermometer Importer: out of bounds colNum"); + } + } + } + + private void importThermometerVial(Node node, ThermometerBoard board) + throws InvalidFileFormatException { + // head is the top of the thermometer and tip is the end of the thermometer + // thermometers in the xml are specified only by their head and tip cells + int headX = Integer.parseInt(node.getAttributes().getNamedItem("headx").getNodeValue()); + int headY = Integer.parseInt(node.getAttributes().getNamedItem("heady").getNodeValue()); + int tipX = Integer.parseInt(node.getAttributes().getNamedItem("tailx").getNodeValue()); + int tipY = Integer.parseInt(node.getAttributes().getNamedItem("taily").getNodeValue()); + + // making sure we can add the vial before doing so + if (verifyVial(headX, headY, tipX, tipY, board)) { + // adding the vial to the board + board.addVial(new ThermometerVial(headX, headY, tipX, tipY, board)); + } else { + throw new InvalidFileFormatException("thermometer Vial Factory: overlapping vials"); + } + } + + private boolean verifyVial(int headX, int headY, int tipX, int tipY, ThermometerBoard board) { + // figuring out which axis the thermometer travels along + if (headX == tipX) { + // finding start and end of Vial + int top = min(headY, tipY); + int bottom = max(headY, tipY); + + // verifying that every cell along path is currently unconstructed + for (int i = top; i <= bottom; i++) { + if (board.getCell(headX, i) != null) return false; + } + } else if (headY == tipY) { + // finding start and end of Vial + // I have words to say to james + int left = min(headX, tipX); + int right = max(headX, tipX); + + // verifying that every cell along path is currently unconstructed + for (int i = left; i <= right; i++) { + if (board.getCell(i, headY) != null) return false; + } + } else { + // thermometer does not line up along a single axis + return false; + } + return true; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerNumberView.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerNumberView.java new file mode 100644 index 000000000..4a00b8a18 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerNumberView.java @@ -0,0 +1,37 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.model.gameboard.GridCell; +import edu.rpi.legup.ui.boardview.GridElementView; +import java.awt.*; + +public class ThermometerNumberView extends GridElementView { + private static final Font FONT = new Font("TimesRoman", Font.BOLD, 16); + private static final Color FONT_COLOR = Color.BLACK; + + public ThermometerNumberView(GridCell cell) { + super(cell); + } + + @Override + public GridCell getPuzzleElement() { + return (GridCell) super.getPuzzleElement(); + } + + @Override + public void drawElement(Graphics2D graphics2D) { + ThermometerCell cell = (ThermometerCell) puzzleElement; + + graphics2D.setColor(FONT_COLOR); + graphics2D.setFont(FONT); + FontMetrics metrics = graphics2D.getFontMetrics(FONT); + int val; + + if (cell != null) val = cell.getRotation(); + else val = -1; + + int xText = location.x + (size.width - metrics.stringWidth(String.valueOf(val))) / 2; + int yText = location.y + ((size.height - metrics.getHeight()) / 2) + metrics.getAscent(); + + graphics2D.drawString(String.valueOf(val), xText, yText); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerType.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerType.java new file mode 100644 index 000000000..f482411a5 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerType.java @@ -0,0 +1,8 @@ +package edu.rpi.legup.puzzle.thermometer; + +public enum ThermometerType { + UNKNOWN, + HEAD, + SHAFT, + TIP; +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerVial.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerVial.java new file mode 100644 index 000000000..2cba64363 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerVial.java @@ -0,0 +1,103 @@ +package edu.rpi.legup.puzzle.thermometer; + +import java.awt.*; +import java.util.ArrayList; + +public class ThermometerVial { + private ArrayList cells; + + public ThermometerVial(int headX, int headY, int tipX, int tipY, ThermometerBoard board) { + // basic constructor, instantiating our members field and then + // calling helper function to do all the heavy lifting + cells = new ArrayList(); + fillData(headX, headY, tipX, tipY, board); + } + + // function called by the constructor which adds in all of the cells to the array + // as well as updates their type on the board + private void fillData(int headX, int headY, int tipX, int tipY, ThermometerBoard board) { + // not totally happy with layout of code but most readable version I can think of atm + // top left coordinate is 0,0 cells are added from head to tip always + // because cells have already been verified by time constructor is called + // we can guarantee that only the x or only the y coordinates wont line up + if (headY < tipY) { + addCell(headX, headY, ThermometerType.HEAD, 0, board); + for (int i = headY + 1; i < tipY; i++) { + addCell(headX, i, ThermometerType.SHAFT, 0, board); + } + addCell(tipX, tipY, ThermometerType.TIP, 0, board); + } else if (tipY < headY) { + addCell(headX, headY, ThermometerType.HEAD, 180, board); + for (int i = headY - 1; i > tipY; i--) { + addCell(headX, i, ThermometerType.SHAFT, 180, board); + } + addCell(tipX, tipY, ThermometerType.TIP, 180, board); + } else if (headX < tipX) { + addCell(headX, headY, ThermometerType.HEAD, 90, board); + for (int i = headX + 1; i < tipX; i++) { + addCell(i, headY, ThermometerType.SHAFT, 90, board); + } + addCell(tipX, tipY, ThermometerType.TIP, 90, board); + } else { + addCell(headX, headY, ThermometerType.HEAD, 270, board); + for (int i = headX - 1; i > tipX; i--) { + addCell(i, headY, ThermometerType.SHAFT, 270, board); + } + addCell(tipX, tipY, ThermometerType.TIP, 270, board); + } + } + + // helper function for adding a single cell + private void addCell(int x, int y, ThermometerType t, int rotation, ThermometerBoard board) { + ThermometerCell cell = + new ThermometerCell(new Point(x, y), t, ThermometerFill.EMPTY, rotation); + cell.setIndex(y * board.getHeight() + x); + this.cells.add(cell); + // still important for element view stuff + board.setCell(x, y, cell); + } + + // TODO (probably) DOES NOT WORK AS INTENDED + // BECAUSE MOST RULES GET A PUZZLE ELEMENT PASSED IN AND WEIRD + // TYPE CASTING STUFF, PAY ATTENTION TO THIS WHEN WE START + // DEBUGGING RULES + // a basic accessor to check if a cell is contained in vial + public boolean containsCell(ThermometerCell cell) { + for (ThermometerCell c : cells) { + if (c.getLocation() == cell.getLocation()) { + return true; + } + } + return false; + } + + // Returns cell containing head of thermometer + public ThermometerCell getHead() { + return cells.getFirst(); + } + + // Returns cell containing tail of thermometer + public ThermometerCell getTail() { + return cells.getLast(); + } + + // Retruns all cells in vial, from head to tip + public ArrayList getCells() { + return cells; + } + + // checking for discontinuous flow inside of vial + public boolean continuousFlow() { + // bool which is true until it runs into an empty/blocked cell in the vial + // if an empty cell in the vial is found while flow is set to false + // we know there is a break in the flow + boolean flow = true; + + for (ThermometerCell c : cells) { + if (c.getFill() != ThermometerFill.FILLED && flow) flow = false; + + if (c.getFill() == ThermometerFill.FILLED && !flow) return false; + } + return true; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerView.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerView.java new file mode 100644 index 000000000..444037cfe --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/ThermometerView.java @@ -0,0 +1,48 @@ +package edu.rpi.legup.puzzle.thermometer; + +import edu.rpi.legup.controller.BoardController; +import edu.rpi.legup.ui.boardview.GridBoardView; +import java.awt.*; + +public class ThermometerView extends GridBoardView { + + public ThermometerView(ThermometerBoard board) { + super(new BoardController(), new ThermometerController(), board.getDimension()); + + // loop for displaying the vial cells + // stolen largely from dev guide + for (ThermometerVial vial : board.getVials()) { + for (ThermometerCell cell : vial.getCells()) { + Point loc = cell.getLocation(); + ThermometerElementView elementView = new ThermometerElementView(cell); + elementView.setIndex(cell.getIndex()); + elementView.setSize(elementSize); + elementView.setLocation( + new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementViews.add(elementView); + } + } + + // loop for displaying row numbers, same as above + for (ThermometerCell rowNum : board.getRowNumbers()) { + Point loc = rowNum.getLocation(); + ThermometerNumberView numberView = new ThermometerNumberView(rowNum); + numberView.setIndex(rowNum.getIndex()); + numberView.setSize(elementSize); + numberView.setLocation( + new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementViews.add(numberView); + } + + // loop for displaying col numbers, also same as above + for (ThermometerCell colNum : board.getColNumbers()) { + Point loc = colNum.getLocation(); + ThermometerNumberView numberView = new ThermometerNumberView(colNum); + numberView.setIndex(colNum.getIndex()); + numberView.setSize(elementSize); + numberView.setLocation( + new Point(loc.x * elementSize.width, loc.y * elementSize.height)); + elementViews.add(numberView); + } + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckE.java new file mode 100644 index 000000000..8f0507ab5 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileBlckE extends PlaceableElement { + public HeadTileBlckE() { + super( + "Therm-PLAC-0001", + "Head Tile Block East", + "The tile corresponding to the blocked head of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadBlockE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckN.java new file mode 100644 index 000000000..f195c299b --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileBlckN extends PlaceableElement { + public HeadTileBlckN() { + super( + "Therm-PLAC-0002", + "Head Tile Block North", + "The tile corresponding to the blocked head of a north thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadBlockN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckS.java new file mode 100644 index 000000000..d9e4c4a6a --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileBlckS extends PlaceableElement { + public HeadTileBlckS() { + super( + "Therm-PLAC-0003", + "Head Tile Block South", + "The tile corresponding to the blocked head of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadBlockS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckW.java new file mode 100644 index 000000000..2bcbfdf2d --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileBlckW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileBlckW extends PlaceableElement { + public HeadTileBlckW() { + super( + "Therm-PLAC-0004", + "Head Tile Block West", + "The tile corresponding to the blocked head of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadBlockW.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpE.java new file mode 100644 index 000000000..0b678ed73 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileEmpE extends PlaceableElement { + public HeadTileEmpE() { + super( + "Therm-PLAC-0005", + "Head Tile Empty East", + "The tile corresponding to the empty head of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadEmpE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpN.java new file mode 100644 index 000000000..b865b0cae --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileEmpN extends PlaceableElement { + public HeadTileEmpN() { + super( + "Therm-PLAC-0006", + "Head Tile Empty North", + "The tile corresponding to the empty head of a North thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadEmpN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpS.java new file mode 100644 index 000000000..40989d814 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileEmpS extends PlaceableElement { + public HeadTileEmpS() { + super( + "Therm-PLAC-0007", + "Head Tile Empty South", + "The tile corresponding to the empty head of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadEmpS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpW.java new file mode 100644 index 000000000..ba344ff8a --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileEmpW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileEmpW extends PlaceableElement { + public HeadTileEmpW() { + super( + "Therm-PLAC-0008", + "Head Tile Empty West", + "The tile corresponding to the empty head of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadEmpW.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillE.java new file mode 100644 index 000000000..e8bfb8f82 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileFillE extends PlaceableElement { + public HeadTileFillE() { + super( + "Therm-PLAC-0009", + "Head Tile Filled East", + "The tile corresponding to the filled head of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadFillE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillN.java new file mode 100644 index 000000000..4a835601c --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileFillN extends PlaceableElement { + public HeadTileFillN() { + super( + "Therm-PLAC-0010", + "Head Tile Filled North", + "The tile corresponding to the filled head of a north thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadFillN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillS.java new file mode 100644 index 000000000..df559ec6f --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileFillS extends PlaceableElement { + public HeadTileFillS() { + super( + "Therm-PLAC-0011", + "Head Tile Filled South", + "The tile corresponding to the filled head of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadFillS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillW.java new file mode 100644 index 000000000..80194ceaf --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/HeadTileFillW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class HeadTileFillW extends PlaceableElement { + public HeadTileFillW() { + super( + "Therm-PLAC-0012", + "Head Tile Filled West", + "The tile corresponding to the filled head of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/HeadFillW.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckE.java new file mode 100644 index 000000000..7080b9a47 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileBlckE extends PlaceableElement { + public ShaftTileBlckE() { + super( + "Therm-PLAC-0013", + "Shaft Tile Blocked East", + "The tile corresponding to a Blocked middle segment of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftBlockE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckN.java new file mode 100644 index 000000000..760baf624 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileBlckN extends PlaceableElement { + public ShaftTileBlckN() { + super( + "Therm-PLAC-0014", + "Shaft Tile Blocked North", + "The tile corresponding to a Blocked middle segment of a north thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftBlockN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckS.java new file mode 100644 index 000000000..ec14a669d --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileBlckS extends PlaceableElement { + public ShaftTileBlckS() { + super( + "Therm-PLAC-0015", + "Shaft Tile Blocked South", + "The tile corresponding to a Blocked middle segment of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftBlockS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckW.java new file mode 100644 index 000000000..183fd798c --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileBlckW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileBlckW extends PlaceableElement { + public ShaftTileBlckW() { + super( + "Therm-PLAC-0016", + "Shaft Tile Blocked West", + "The tile corresponding to a Blocked middle segment of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftBlockW.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpE.java new file mode 100644 index 000000000..771c1afc3 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileEmpE extends PlaceableElement { + public ShaftTileEmpE() { + super( + "Therm-PLAC-0017", + "Shaft Tile Empty East", + "The tile corresponding to an empty middle segment of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftEmpE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpN.java new file mode 100644 index 000000000..c2ec4e0ab --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileEmpN extends PlaceableElement { + public ShaftTileEmpN() { + super( + "Therm-PLAC-0018", + "Shaft Tile Empty North", + "The tile corresponding to an empty middle segment of a north thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftEmpN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpS.java new file mode 100644 index 000000000..fc2828ddb --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileEmpS extends PlaceableElement { + public ShaftTileEmpS() { + super( + "Therm-PLAC-0019", + "Shaft Tile Empty South", + "The tile corresponding to an empty middle segment of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftEmpS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpW.java new file mode 100644 index 000000000..ce0e7bce3 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileEmpW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileEmpW extends PlaceableElement { + public ShaftTileEmpW() { + super( + "Therm-PLAC-0020", + "Shaft Tile Empty West", + "The tile corresponding to an empty middle segment of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftEmpW.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillE.java new file mode 100644 index 000000000..a8aeb44ed --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileFillE extends PlaceableElement { + public ShaftTileFillE() { + super( + "Therm-PLAC-0021", + "Shaft Tile Filled East", + "The tile corresponding to a filled middle segment of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftFillE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillN.java new file mode 100644 index 000000000..0366c3d0b --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileFillN extends PlaceableElement { + public ShaftTileFillN() { + super( + "Therm-PLAC-0022", + "Shaft Tile Filled North", + "The tile corresponding to a filled middle segment of a north thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftFillN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillS.java new file mode 100644 index 000000000..b55d2a1f9 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileFillS extends PlaceableElement { + public ShaftTileFillS() { + super( + "Therm-PLAC-0023", + "Shaft Tile Filled South", + "The tile corresponding to a filled middle segment of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftFillS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillW.java new file mode 100644 index 000000000..3b2cd0454 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/ShaftTileFillW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class ShaftTileFillW extends PlaceableElement { + public ShaftTileFillW() { + super( + "Therm-PLAC-0024", + "Shaft Tile Filled West", + "The tile corresponding to a filled middle segment of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/ShaftFillS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckE.java new file mode 100644 index 000000000..ea94846c2 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileBlckE extends PlaceableElement { + public TipTileBlckE() { + super( + "Therm-PLAC-0025", + "Tip Tile Block East", + "The tile corresponding to the Blocked tip of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipBlockE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckN.java new file mode 100644 index 000000000..25ae8afda --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileBlckN extends PlaceableElement { + public TipTileBlckN() { + super( + "Therm-PLAC-0026", + "Tip Tile Block North", + "The tile corresponding to the Blocked tip of a north thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipBlockN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckS.java new file mode 100644 index 000000000..e19082162 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileBlckS extends PlaceableElement { + public TipTileBlckS() { + super( + "Therm-PLAC-0027", + "Tip Tile Block South", + "The tile corresponding to the Blocked tip of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipBlockS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckW.java new file mode 100644 index 000000000..a0c49bc77 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileBlckW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileBlckW extends PlaceableElement { + public TipTileBlckW() { + super( + "Therm-PLAC-0028", + "Tip Tile Block West", + "The tile corresponding to the Blocked tip of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipBlockW.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpE.java new file mode 100644 index 000000000..6595da855 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileEmpE extends PlaceableElement { + public TipTileEmpE() { + super( + "Therm-PLAC-0029", + "Tip Tile Empty East", + "The tile corresponding to the empty tip of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipEmpE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpN.java new file mode 100644 index 000000000..cacfe5d5d --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileEmpN extends PlaceableElement { + public TipTileEmpN() { + super( + "Therm-PLAC-0030", + "Tip Tile Empty North", + "The tile corresponding to the empty tip of a north thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipEmpN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpS.java new file mode 100644 index 000000000..2815b9fa1 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileEmpS extends PlaceableElement { + public TipTileEmpS() { + super( + "Therm-PLAC-0031", + "Tip Tile Empty South", + "The tile corresponding to the empty tip of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipEmpS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpW.java new file mode 100644 index 000000000..3bd77495f --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileEmpW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileEmpW extends PlaceableElement { + public TipTileEmpW() { + super( + "Therm-PLAC-0032", + "Tip Tile Empty West", + "The tile corresponding to the empty tip of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipEmpW.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillE.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillE.java new file mode 100644 index 000000000..8c9953dd2 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillE.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileFillE extends PlaceableElement { + public TipTileFillE() { + super( + "Therm-PLAC-0033", + "Tip Tile Fill East", + "The tile corresponding to the filled tip of an east thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipFillE.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillN.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillN.java new file mode 100644 index 000000000..f5ce01c7b --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillN.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileFillN extends PlaceableElement { + public TipTileFillN() { + super( + "Therm-PLAC-0034", + "Tip Tile Fill North", + "The tile corresponding to the filled tip of a north thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipFillN.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillS.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillS.java new file mode 100644 index 000000000..05d68fe81 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillS.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileFillS extends PlaceableElement { + public TipTileFillS() { + super( + "Therm-PLAC-0035", + "Tip Tile Fill South", + "The tile corresponding to the filled tip of a south thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipFillS.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillW.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillW.java new file mode 100644 index 000000000..6aa486ba1 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/elements/TipTileFillW.java @@ -0,0 +1,13 @@ +package edu.rpi.legup.puzzle.thermometer.elements; + +import edu.rpi.legup.model.elements.PlaceableElement; + +public class TipTileFillW extends PlaceableElement { + public TipTileFillW() { + super( + "Therm-PLAC-0036", + "Tip Tile Fill West", + "The tile corresponding to the filled tip of a west thermometer", + "edu/rpi/legup/images/thermometer/Elements/TipFillW.png"); + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/DiscontinuousMercuryContradictionRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/DiscontinuousMercuryContradictionRule.java new file mode 100644 index 000000000..b013b3493 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/DiscontinuousMercuryContradictionRule.java @@ -0,0 +1,62 @@ +package edu.rpi.legup.puzzle.thermometer.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.thermometer.ThermometerBoard; +import edu.rpi.legup.puzzle.thermometer.ThermometerCell; +import edu.rpi.legup.puzzle.thermometer.ThermometerVial; +import java.util.ArrayList; + +// TODO: Rule is untested +public class DiscontinuousMercuryContradictionRule 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 vial"; + + public DiscontinuousMercuryContradictionRule() { + super( + "THERM-CONT-0001", + "Discontinuous Mercury", + "A vial has a filled cell after an empty or blocked cell", + "edu/rpi/legup/images/thermometer/MercuryInBody.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 + */ + // User can click on any cell in a vial with a discontinuous flow + @Override + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + // useful variables + ThermometerBoard thermometerBoard = (ThermometerBoard) board; + + ThermometerCell cell = (ThermometerCell) thermometerBoard.getPuzzleElement(puzzleElement); + + ArrayList thermometerVials = thermometerBoard.getVials(); + + // finding out which vial contains the specified cell + for (int i = 0; i < thermometerVials.size(); i++) { + ThermometerVial thermometerVial = thermometerVials.get(i); + // if a vial contains the clicked on cell + // checking if the vial has a break in the flow + if (thermometerVial.containsCell(cell)) { + if (thermometerVial.continuousFlow()) { + return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE; + } else { + return null; + } + } + } + + // if none of the vials contain the clicked on cell yell at user + return super.getInvalidUseOfRuleMessage() + ": " + this.INVALID_USE_MESSAGE; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/FinishWithBlockedDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/FinishWithBlockedDirectRule.java new file mode 100644 index 000000000..d09b98300 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/FinishWithBlockedDirectRule.java @@ -0,0 +1,44 @@ +package edu.rpi.legup.puzzle.thermometer.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; + +// TODO: Rule is unimplemented +public class FinishWithBlockedDirectRule extends DirectRule { + public FinishWithBlockedDirectRule() { + super( + "THERM-BASC-0004", + "Finish With Blocked", + "Remaining tiles must be blocked once requirement is satisfied", + "edu/rpi/legup/images/thermometer/FinishWithBlocked.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) { + 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/thermometer/rules/FinishWithMercuryDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/FinishWithMercuryDirectRule.java new file mode 100644 index 000000000..09fb8874d --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/FinishWithMercuryDirectRule.java @@ -0,0 +1,44 @@ +package edu.rpi.legup.puzzle.thermometer.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; + +// TODO: Rule is unimplemented +public class FinishWithMercuryDirectRule extends DirectRule { + public FinishWithMercuryDirectRule() { + super( + "THERM-BASC-0003", + "Finish with Mercury", + "Remaining tiles must be filled to satisfy requirement", + "edu/rpi/legup/images/thermometer/FinishWithMercury.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) { + 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/thermometer/rules/MercuryOrBlockedCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/MercuryOrBlockedCaseRule.java new file mode 100644 index 000000000..a0644aec0 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/MercuryOrBlockedCaseRule.java @@ -0,0 +1,114 @@ +package edu.rpi.legup.puzzle.thermometer.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.thermometer.*; +import java.util.ArrayList; +import java.util.List; + +// TODO: Rule is untested +public class MercuryOrBlockedCaseRule extends CaseRule { + public MercuryOrBlockedCaseRule() { + super( + "THERM-CASE-0001", + "Mercury or Blocked", + "Each unassigned tile must be filled with mercury or blocked.", + "edu/rpi/legup/images/thermometer/MercOrBlocked.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) { + List childTransitions = transition.getParents().get(0).getChildren(); + if (childTransitions.size() != 2) { + return super.getInvalidUseOfRuleMessage() + ": This case rule must have 2 children."; + } + + TreeTransition case1 = childTransitions.get(0); + 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."; + } + + ThermometerCell mod1 = + (ThermometerCell) case1.getBoard().getModifiedData().iterator().next(); + ThermometerCell mod2 = + (ThermometerCell) 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."; + } + + if (!((mod1.getFill() == ThermometerFill.BLOCKED + && mod2.getFill() == ThermometerFill.FILLED) + || (mod2.getFill() == ThermometerFill.BLOCKED + && mod1.getFill() == ThermometerFill.FILLED))) { + return super.getInvalidUseOfRuleMessage() + + ": This case rule must have a filled or blocked cell."; + } + + 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) { + ThermometerBoard thermometerBoard = (ThermometerBoard) board.copy(); + CaseBoard caseBoard = new CaseBoard(thermometerBoard, this); + thermometerBoard.setModifiable(false); + for (PuzzleElement element : thermometerBoard.getPuzzleElements()) { + if (((ThermometerCell) element).getFill() == ThermometerFill.UNKNOWN) { + caseBoard.addPickableElement(element); + } + } + 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<>(); + Board case1 = board.copy(); + ThermometerCell data1 = (ThermometerCell) case1.getPuzzleElement(puzzleElement); + data1.setFill(ThermometerFill.FILLED); + case1.addModifiedData(data1); + cases.add(case1); + + Board case2 = board.copy(); + ThermometerCell data2 = (ThermometerCell) case2.getPuzzleElement(puzzleElement); + data2.setFill(ThermometerFill.BLOCKED); + case2.addModifiedData(data2); + cases.add(case2); + + return cases; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/MinimumFillDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/MinimumFillDirectRule.java new file mode 100644 index 000000000..ab389d6ff --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/MinimumFillDirectRule.java @@ -0,0 +1,44 @@ +package edu.rpi.legup.puzzle.thermometer.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; + +// TODO: Rule is unimplemented +public class MinimumFillDirectRule extends DirectRule { + public MinimumFillDirectRule() { + super( + "THERM-BASC-0005", + "Minimum Fill", + "Some thermometers must be filled a minimum amount to satisfy requirement", + "edu/rpi/legup/images/thermometer/MinimumFill.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) { + 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/thermometer/rules/PriorFilledDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/PriorFilledDirectRule.java new file mode 100644 index 000000000..61622ddb1 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/PriorFilledDirectRule.java @@ -0,0 +1,101 @@ +package edu.rpi.legup.puzzle.thermometer.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.thermometer.ThermometerBoard; +import edu.rpi.legup.puzzle.thermometer.ThermometerCell; +import edu.rpi.legup.puzzle.thermometer.ThermometerFill; +import edu.rpi.legup.puzzle.thermometer.ThermometerVial; +import java.util.ArrayList; + +// TODO: Rule is untested +public class PriorFilledDirectRule extends DirectRule { + + public PriorFilledDirectRule() { + super( + "THERM-BASC-0002", + "Prior is Filled", + "All tiles proceeding a filled tile in a vial must be filled", + "edu/rpi/legup/images/thermometer/PriorIsFilled.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) { + ThermometerBoard initialBoard = + (ThermometerBoard) transition.getParents().get(0).getBoard(); + ThermometerBoard finalBoard = (ThermometerBoard) transition.getBoard(); + + ThermometerCell cell = (ThermometerCell) finalBoard.getPuzzleElement(puzzleElement); + if (cell.getFill() != ThermometerFill.FILLED) { + return super.getInvalidUseOfRuleMessage() + ": Cell is not filled at this index"; + } + + ArrayList allVials = finalBoard.getVials(); + ThermometerVial host = null; + for (ThermometerVial vials : allVials) { + if (vials.containsCell((cell))) { + host = vials; + } + } + if (host == null) return super.getInvalidUseOfRuleMessage() + ": Something went wrong - 1"; + int x = (int) cell.getLocation().getX(); + int y = (int) cell.getLocation().getX(); + + // Identifies next cell from tail location, checks if it is filled + if (host.getTail() == cell) { + return super.getInvalidUseOfRuleMessage() + ": rule can not apply to tail"; + } else if (host.getTail().getLocation().getX() == x) { + if (host.getTail().getLocation().getY() > y) { + if (initialBoard.getCell(x, y + 1).getFill() == ThermometerFill.FILLED) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + "rule does not apply to this cell"; + } + } else if (host.getTail().getLocation().getY() < y) { + if (initialBoard.getCell(x, y - 1).getFill() == ThermometerFill.FILLED) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + "rule does not apply to this cell"; + } + } else return super.getInvalidUseOfRuleMessage() + ": Something went wrong - 2"; + } else if (host.getTail().getLocation().getY() == y) { + if (host.getTail().getLocation().getX() > x) { + if (initialBoard.getCell(x + 1, y).getFill() == ThermometerFill.FILLED) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + "rule does not apply to this cell"; + } + } else if (host.getTail().getLocation().getX() < x) { + if (initialBoard.getCell(x - 1, y).getFill() == ThermometerFill.FILLED) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + "rule does not apply to this cell"; + } + } else return super.getInvalidUseOfRuleMessage() + ": Something went wrong - 2.1"; + } + return super.getInvalidUseOfRuleMessage() + "Something went wrong - 3"; + } + + /** + * 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/thermometer/rules/RestEmptyDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/RestEmptyDirectRule.java new file mode 100644 index 000000000..486c5c1da --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/RestEmptyDirectRule.java @@ -0,0 +1,101 @@ +package edu.rpi.legup.puzzle.thermometer.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.thermometer.ThermometerBoard; +import edu.rpi.legup.puzzle.thermometer.ThermometerCell; +import edu.rpi.legup.puzzle.thermometer.ThermometerFill; +import edu.rpi.legup.puzzle.thermometer.ThermometerVial; +import java.util.ArrayList; + +// TODO: Rule is untested +public class RestEmptyDirectRule extends DirectRule { + + public RestEmptyDirectRule() { + super( + "THERM-BASC-0001", + "Rest is Empty", + "All tiles following a blocked tile in a vial must be blocked", + "edu/rpi/legup/images/thermometer/RestIsEmpty.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) { + ThermometerBoard initialBoard = + (ThermometerBoard) transition.getParents().get(0).getBoard(); + ThermometerBoard finalBoard = (ThermometerBoard) transition.getBoard(); + + ThermometerCell cell = (ThermometerCell) finalBoard.getPuzzleElement(puzzleElement); + if (cell.getFill() != ThermometerFill.BLOCKED) { + return super.getInvalidUseOfRuleMessage() + ": Cell is not blocked at this index"; + } + + ArrayList allVials = finalBoard.getVials(); + ThermometerVial host = null; + for (ThermometerVial vials : allVials) { + if (vials.containsCell((cell))) { + host = vials; + } + } + if (host == null) return super.getInvalidUseOfRuleMessage() + ": Something went wrong - 1"; + int x = (int) cell.getLocation().getX(); + int y = (int) cell.getLocation().getX(); + + // Identifies previous cell from head location, checks if it is blocked + if (host.getHead() == cell) { + return super.getInvalidUseOfRuleMessage() + ": rule can not apply to head"; + } else if (host.getHead().getLocation().getX() == x) { + if (host.getHead().getLocation().getY() > y) { + if (initialBoard.getCell(x, y + 1).getFill() == ThermometerFill.BLOCKED) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + "rule does not apply to this cell"; + } + } else if (host.getHead().getLocation().getY() < y) { + if (initialBoard.getCell(x, y - 1).getFill() == ThermometerFill.BLOCKED) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + "rule does not apply to this cell"; + } + } else return super.getInvalidUseOfRuleMessage() + ": Something went wrong - 2"; + } else if (host.getHead().getLocation().getY() == y) { + if (host.getHead().getLocation().getX() > x) { + if (initialBoard.getCell(x + 1, y).getFill() == ThermometerFill.BLOCKED) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + "rule does not apply to this cell"; + } + } else if (host.getHead().getLocation().getX() < x) { + if (initialBoard.getCell(x - 1, y).getFill() == ThermometerFill.BLOCKED) { + return null; + } else { + return super.getInvalidUseOfRuleMessage() + "rule does not apply to this cell"; + } + } else return super.getInvalidUseOfRuleMessage() + ": Something went wrong - 2.1"; + } + return super.getInvalidUseOfRuleMessage() + "Something went wrong - 3"; + } + + /** + * 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/thermometer/rules/SatisfyMercuryCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/SatisfyMercuryCaseRule.java new file mode 100644 index 000000000..05c861281 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/SatisfyMercuryCaseRule.java @@ -0,0 +1,62 @@ +package edu.rpi.legup.puzzle.thermometer.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.thermometer.*; +import java.util.ArrayList; + +// TODO:This rule is unimplemented +public class SatisfyMercuryCaseRule extends CaseRule { + public SatisfyMercuryCaseRule() { + super( + "THERM-CASE-0002", + "Satisfy Mercury", + "There are multiple ways column/row requirements can be fufilled", + "edu/rpi/legup/images/thermometer/SatisfyMercury.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; + } + + /** + * 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 null; + } + + @Override + public CaseBoard getCaseBoard(Board board) { + return null; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/ThermometerTooLargeDirectRule.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/ThermometerTooLargeDirectRule.java new file mode 100644 index 000000000..d679781b7 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/ThermometerTooLargeDirectRule.java @@ -0,0 +1,44 @@ +package edu.rpi.legup.puzzle.thermometer.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; + +// TODO: Rule is unimplemented +public class ThermometerTooLargeDirectRule extends DirectRule { + public ThermometerTooLargeDirectRule() { + super( + "THERM-BASC-0006", + "Thermometer Too Large", + "If thermometer is larger than required mercury, some of it must be blocked", + "edu/rpi/legup/images/thermometer/ThermometerTooLarge.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) { + 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/thermometer/rules/TooFewMercuryContradiction.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/TooFewMercuryContradiction.java new file mode 100644 index 000000000..8750eb64d --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/TooFewMercuryContradiction.java @@ -0,0 +1,57 @@ +package edu.rpi.legup.puzzle.thermometer.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.thermometer.ThermometerBoard; +import edu.rpi.legup.puzzle.thermometer.ThermometerCell; +import edu.rpi.legup.puzzle.thermometer.ThermometerFill; + +// TODO: Rule is untested +public class TooFewMercuryContradiction extends ContradictionRule { + + private final String Invalid_Use_Message = "Mercury can still reach limit"; + + public TooFewMercuryContradiction() { + super( + "THERM-CONT-0002", + "Too Few Mercury", + "Not enough mercury in column/row to fufill requirement", + "edu/rpi/legup/images/thermometer/NotEnoughMercury.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 + // Checks if row or column of input element has too many blocked tiles + public String checkContradictionAt(Board board, PuzzleElement puzzleElement) { + ThermometerBoard grid = (ThermometerBoard) board; + ThermometerCell cell = (ThermometerCell) grid.getPuzzleElement(puzzleElement); + int blocked = 0; + for (int i = 0; i < grid.getHeight(); i++) { + if (grid.getCell((int) cell.getLocation().getX(), i).getFill() + == ThermometerFill.BLOCKED) { + blocked++; + } + } + if (grid.getRowNumber((int) cell.getLocation().getX()) > blocked) return null; + + blocked = 0; + for (int i = 0; i < grid.getWidth(); i++) { + if (grid.getCell(i, (int) cell.getLocation().getY()).getFill() + == ThermometerFill.BLOCKED) { + blocked++; + } + } + if (grid.getColNumber((int) cell.getLocation().getY()) > blocked) return null; + + return Invalid_Use_Message; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/TooManyMercuryContradiction.java b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/TooManyMercuryContradiction.java new file mode 100644 index 000000000..06b8a017c --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/TooManyMercuryContradiction.java @@ -0,0 +1,56 @@ +package edu.rpi.legup.puzzle.thermometer.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.thermometer.ThermometerBoard; +import edu.rpi.legup.puzzle.thermometer.ThermometerCell; +import edu.rpi.legup.puzzle.thermometer.ThermometerFill; + +// TODO: Rule is untested +public class TooManyMercuryContradiction extends ContradictionRule { + + private final String Invalid_Use_Message = "Mercury does not exceed limit"; + + public TooManyMercuryContradiction() { + super( + "THERM-CONT-0003", + "Too Many Mercury", + "More mercury in column/row than target", + "edu/rpi/legup/images/thermometer/TooManyMercury.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) { + ThermometerBoard grid = (ThermometerBoard) board; + ThermometerCell cell = (ThermometerCell) grid.getPuzzleElement(puzzleElement); + int filled = 0; + for (int i = 0; i < grid.getHeight(); i++) { + if (grid.getCell((int) cell.getLocation().getX(), i).getFill() + == ThermometerFill.FILLED) { + filled++; + } + } + if (grid.getRowNumber((int) cell.getLocation().getX()) > filled) return null; + + filled = 0; + for (int i = 0; i < grid.getWidth(); i++) { + if (grid.getCell(i, (int) cell.getLocation().getY()).getFill() + == ThermometerFill.FILLED) { + filled++; + } + } + if (grid.getColNumber((int) cell.getLocation().getY()) > filled) return null; + + return Invalid_Use_Message; + } +} diff --git a/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/thermometer_reference_sheet.txt b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/thermometer_reference_sheet.txt new file mode 100644 index 000000000..546fb89a6 --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/thermometer/rules/thermometer_reference_sheet.txt @@ -0,0 +1,16 @@ +THERM-BASC-0001 : RestEmptyDirectRule +THERM-BASC-0002 : PriorFilledDirectRule +THERM-BASC-0003 : FinishWithMercuryDirectRule +THERM-BASC-0004 : FinishWithBlockedDirectRule +THERM-BASC-0005 : MinimumFillDirectRule +THERM-BASC-0006 : ThermometerTooLargeDirectRule + +THERM-CONT-0001 : DiscontinuousMercuryContradictionRule +THERM-CONT-0002 : TooFewMercuryContradiction +THERM-CONT-0003 : TooManyMercuryContradiction + +THERM-CASE-0001 : MercuryOrBlockedCaseRule +THERM-CASE-0002 : SatisfyMercuryCaseRule + +Images can be found/edited here: +https://docs.google.com/presentation/d/1YHNog2fGvLJEx4kbJZiwwAlP-m2-E1O7hGh0HJ7S0gE/edit?usp=sharing \ 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 68c97865d..35993303a 100644 --- a/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTent.java +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/TreeTent.java @@ -3,6 +3,7 @@ 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 { @@ -58,7 +59,20 @@ public boolean isValidDimensions(int rows, int columns) { */ @Override public boolean isBoardComplete(Board board) { - return false; + 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; } /** 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 5356120a8..1d33b9035 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-PlAC-0002", + "TREE-ELEM-0001", "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 950aebfa7..96124a98d 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-PLAC-0001", + "TREE-ELEM-0002", "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 d04886ed5..3d94cbfba 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class TreeTile extends NonPlaceableElement { +public class TreeTile extends PlaceableElement { public TreeTile() { super( - "TREE-UNPL-0001", + "TREE-ELEM-0003", "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 a54240efd..99b75b60c 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.NonPlaceableElement; +import edu.rpi.legup.model.elements.PlaceableElement; -public class UnknownTile extends NonPlaceableElement { +public class UnknownTile extends PlaceableElement { public UnknownTile() { super( - "TREE-UNPL-0002", + "TREE-ELEM-0004", "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 new file mode 100644 index 000000000..e0cfc1dfa --- /dev/null +++ b/src/main/java/edu/rpi/legup/puzzle/treetent/elements/treetent_elements_reference_sheet.txt @@ -0,0 +1,4 @@ +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 aaa1a8fbc..8fe9b6873 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,7 +61,11 @@ 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 bd303174a..cbe91c3a7 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,6 +60,10 @@ 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 03d039898..153692ad0 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,6 +62,10 @@ 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 70fbea033..bde2df3d5 100644 --- a/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java +++ b/src/main/java/edu/rpi/legup/ui/CreatePuzzleDialog.java @@ -10,6 +10,10 @@ 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; @@ -17,6 +21,14 @@ 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(); @@ -57,9 +69,7 @@ public void actionPerformed(ActionEvent e) { */ @Override public void actionPerformed(ActionEvent ae) { - String game = - Config.convertDisplayNameToClassName( - (String) gameBox.getSelectedItem()); + String game = getGame(); // Check if all 3 TextFields are filled if (game.equals("ShortTruthTable") && textArea.getText().isEmpty()) { @@ -67,9 +77,7 @@ public void actionPerformed(ActionEvent ae) { return; } if (!game.equals("ShortTruthTable") - && (game.isEmpty() - || rows.getText().isEmpty() - || columns.getText().isEmpty())) { + && (game.isEmpty() || getRows().isEmpty() || getColumns().isEmpty())) { System.out.println("Unfilled fields"); return; } @@ -81,8 +89,8 @@ public void actionPerformed(ActionEvent ae) { } else { homePanel.openEditorWithNewPuzzle( game, - Integer.valueOf(rows.getText()), - Integer.valueOf(columns.getText())); + Integer.valueOf(getRows()), + Integer.valueOf(getColumns())); } setVisible(false); } catch (IllegalArgumentException e) { @@ -106,6 +114,12 @@ 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); @@ -184,6 +198,10 @@ 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() @@ -194,7 +212,12 @@ public void initPuzzles() { gameBox = new JComboBox(this.games); } - // ^This method seems useless and never got covered + /** + * Handles the action events for the dialog, including interactions with the Ok and Cancel + * buttons + * + * @param e The action event to be processed + */ public void actionPerformed(ActionEvent e) { if (e.getSource() == ok) { String game = Config.convertDisplayNameToClassName((String) gameBox.getSelectedItem()); @@ -211,8 +234,7 @@ public void actionPerformed(ActionEvent e) { } this.setVisible(false); } catch (IllegalArgumentException exception) { - // Don't do anything. This is here to prevent the dialog from closing if the - // dimensions are + // Do nothing. This is here to prevent the dialog from closing if the dimensions are // invalid. } } else { @@ -223,4 +245,40 @@ 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 8d3024c86..fa0004d0b 100644 --- a/src/main/java/edu/rpi/legup/ui/DynamicView.java +++ b/src/main/java/edu/rpi/legup/ui/DynamicView.java @@ -16,6 +16,10 @@ 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; @@ -29,6 +33,12 @@ 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; @@ -185,34 +195,61 @@ 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(); @@ -221,6 +258,7 @@ 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 8c2f285cd..c4bb940e0 100644 --- a/src/main/java/edu/rpi/legup/ui/DynamicViewType.java +++ b/src/main/java/edu/rpi/legup/ui/DynamicViewType.java @@ -1,5 +1,14 @@ 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 11f51eb0e..75761c475 100644 --- a/src/main/java/edu/rpi/legup/ui/HomePanel.java +++ b/src/main/java/edu/rpi/legup/ui/HomePanel.java @@ -25,6 +25,11 @@ 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; @@ -36,44 +41,35 @@ 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) { - 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); + legupUI.getProofEditor().loadPuzzle("", null); } }; - 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) { + /** + * 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) { 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"); @@ -102,23 +98,33 @@ 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[4]; + this.buttons = new JButton[3]; this.buttons[0] = - new JButton("Solve Puzzle") { + new JButton("Puzzle Solver") { { setSize(buttonSize, buttonSize); setMaximumSize(getSize()); @@ -136,7 +142,7 @@ private void initButtons() { this.buttons[0].addActionListener(CursorController.createListener(this, openProofListener)); this.buttons[1] = - new JButton("Create Puzzle") { + new JButton("Puzzle Editor") { { setSize(buttonSize, buttonSize); setMaximumSize(getSize()); @@ -150,35 +156,17 @@ 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.openNewPuzzleDialog()); - - 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 + this.buttons[1].addActionListener(l -> this.openPuzzleEditorDialog()); 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[2] = new JButton("Batch Grader"); + this.buttons[2].setFocusPainted(false); + this.buttons[2].setHorizontalTextPosition(AbstractButton.CENTER); + this.buttons[2].setVerticalTextPosition(AbstractButton.BOTTOM); - this.buttons[3].addActionListener( + this.buttons[2].addActionListener( new ActionListener() { @Override public void actionPerformed(ActionEvent e) { @@ -192,6 +180,10 @@ 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(); /* @@ -272,13 +264,17 @@ public void checkFolder() { } } catch (IOException ex) { LOGGER.error(ex.getMessage()); - this.buttons[3].addActionListener((ActionEvent e) -> use_xml_to_check()); + this.buttons[2].addActionListener((ActionEvent e) -> use_xml_to_check()); } } /** - * @effect batch grade using .xml parser - go through a collection of files and report their - * "solved?" status + * 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. */ private void use_xml_to_check() { /* Select a folder, go through each .xml file in the subfolders, look for "isSolved" flag */ @@ -477,6 +473,10 @@ 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) @@ -498,11 +498,12 @@ 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))); @@ -510,11 +511,8 @@ 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[3]); + batchGraderButton.add(this.buttons[2]); batchGraderButton.setAlignmentX(Component.CENTER_ALIGNMENT); this.add(Box.createRigidArea(new Dimension(0, 5))); @@ -526,11 +524,29 @@ private void render() { this.add(Box.createRigidArea(new Dimension(0, 5))); } - private void openNewPuzzleDialog() { - CreatePuzzleDialog cpd = new CreatePuzzleDialog(this.frame, this); - cpd.setVisible(true); + /** + * 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); + } } + /** + * 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() @@ -574,6 +590,14 @@ 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(); @@ -626,28 +650,46 @@ 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 { - // 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 (game.isEmpty()) { + 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"); + } - // Set game type on the puzzle editor - this.legupUI.displayPanel(2); - this.legupUI.getPuzzleEditor().loadPuzzleFromHome(game, rows, columns); + 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); + } } /** - * Opens the puzzle editor for the specified game with the given statements + * Opens the puzzle editor for the specified puzzle 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 d16167b3d..639f21211 100644 --- a/src/main/java/edu/rpi/legup/ui/LegupPanel.java +++ b/src/main/java/edu/rpi/legup/ui/LegupPanel.java @@ -2,9 +2,15 @@ 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 eb8b1663c..75f822a6c 100644 --- a/src/main/java/edu/rpi/legup/ui/LegupUI.java +++ b/src/main/java/edu/rpi/legup/ui/LegupUI.java @@ -14,6 +14,11 @@ 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()); @@ -36,7 +41,7 @@ public static String getOS() { return os; } - /** LegupUI Constructor - creates a new LegupUI to setup the menu and toolbar */ + /** LegupUI Constructor - creates a new LegupUI to set up the menu and toolbar */ public LegupUI() { setTitle("LEGUP"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); @@ -91,17 +96,24 @@ 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.fileDialog, this, this); + panels[0] = new HomePanel(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"); @@ -115,29 +127,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); } @@ -150,8 +162,13 @@ public void showStatus(String status, boolean error, int timer) { // TODO: implement } - // ask to edu.rpi.legup.save current proof - public boolean noquit(String instr) { + /** + * 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) { int n = JOptionPane.showConfirmDialog(null, instr, "Confirm", JOptionPane.YES_NO_OPTION); return n != JOptionPane.YES_OPTION; } @@ -161,7 +178,7 @@ public void windowOpened(WindowEvent e) {} public void windowClosing(WindowEvent e) { if (GameBoardFacade.getInstance().getHistory().getIndex() > -1) { - if (noquit("Exiting LEGUP?")) { + if (exit("Exiting LEGUP?")) { this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); } else { this.setDefaultCloseOperation(EXIT_ON_CLOSE); @@ -183,22 +200,47 @@ 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 f703ffcbc..3b66931a7 100644 --- a/src/main/java/edu/rpi/legup/ui/PickGameDialog.java +++ b/src/main/java/edu/rpi/legup/ui/PickGameDialog.java @@ -15,6 +15,10 @@ 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; @@ -106,6 +110,10 @@ 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(); @@ -128,14 +136,30 @@ 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 475f4bb68..e90d06640 100644 --- a/src/main/java/edu/rpi/legup/ui/PreferencesDialog.java +++ b/src/main/java/edu/rpi/legup/ui/PreferencesDialog.java @@ -17,6 +17,12 @@ 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; @@ -47,12 +53,24 @@ 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); @@ -102,6 +120,11 @@ 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))) { @@ -115,6 +138,11 @@ 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(); @@ -335,6 +363,14 @@ 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()); @@ -387,6 +423,13 @@ 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()); @@ -400,12 +443,24 @@ 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 645a2c0d7..5ecbd5564 100644 --- a/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java +++ b/src/main/java/edu/rpi/legup/ui/ProofEditorPanel.java @@ -36,6 +36,13 @@ 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; @@ -46,8 +53,8 @@ public class ProofEditorPanel extends LegupPanel implements IHistoryListener { private DynamicView dynamicBoardView; private JSplitPane topHalfPanel, mainPanel; private TitledBorder boardBorder; - - private JButton[] toolBarButtons; + private JButton[] toolBar1Buttons; + private JButton[] toolBar2Buttons; private JMenu file; private JMenuItem newPuzzle, resetPuzzle, @@ -67,7 +74,8 @@ public class ProofEditorPanel extends LegupPanel implements IHistoryListener { private JMenu about, help; private JMenuItem helpLegup, aboutLegup; - private JToolBar toolBar; + private JToolBar toolBar1; + private JToolBar toolBar2; private BoardView boardView; private JFileChooser folderBrowser; @@ -81,7 +89,6 @@ 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", @@ -111,6 +118,13 @@ 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; @@ -119,22 +133,46 @@ 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(); - setupToolBar(); + setupToolBar1(); 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 Puzzle"); + resetPuzzle = new JMenuItem("Reset"); // genPuzzle = new JMenuItem("Puzzle Generators"); // TODO: implement puzzle // generator saveProofAs = new JMenuItem("Save As"); // create a new file to save @@ -310,6 +348,7 @@ public JMenuBar getMenuBar() { } else { resetPuzzle.setAccelerator(KeyStroke.getKeyStroke('R', InputEvent.CTRL_DOWN_MASK)); } + file.addSeparator(); file.add(saveProofAs); @@ -457,6 +496,10 @@ 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(); @@ -465,7 +508,14 @@ public void exitEditor() { boardView = null; } - // File opener + /** + * 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 + */ public Object[] promptPuzzle() { GameBoardFacade facade = GameBoardFacade.getInstance(); if (facade.getBoard() != null) { @@ -505,9 +555,16 @@ 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) @@ -519,7 +576,19 @@ 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.isEmpty()) { + legupUI.displayPanel(1); + } if (puzzleFile != null && puzzleFile.exists()) { try { legupUI.displayPanel(1); @@ -554,7 +623,11 @@ public void loadPuzzle(String fileName, File puzzleFile) { } } - /** save the proof in the current file */ + /** + * 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. + */ private void direct_save() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); if (puzzle == null) { @@ -574,7 +647,11 @@ private void direct_save() { } } - /** Create a new file and save proof to it */ + /** + * 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. + */ private void saveProofAs() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); if (puzzle == null) { @@ -612,6 +689,11 @@ 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(); @@ -639,16 +721,14 @@ 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(); } } - // add the new function need to implement + // unfinished 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. @@ -665,7 +745,11 @@ public void actionPerformed(ActionEvent e) { panel.add(moveing_buttom); } - // Quick save proof to the current file with a pop window to show "successfully saved" + /** + * 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. + */ private void saveProofChange() { Puzzle puzzle = GameBoardFacade.getInstance().getPuzzleModule(); if (puzzle == null) { @@ -688,13 +772,22 @@ private void saveProofChange() { } } - // ask to edu.rpi.legup.save current proof + /** + * 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 + */ public boolean noquit(String instr) { int n = JOptionPane.showConfirmDialog(null, instr, "Confirm", JOptionPane.YES_NO_OPTION); return n != JOptionPane.YES_OPTION; } - /** Sets the main content for the edu.rpi.legup.user interface */ + /** + * 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. + */ protected void setupContent() { // JPanel consoleBox = new JPanel(new BorderLayout()); JPanel treeBox = new JPanel(new BorderLayout()); @@ -727,110 +820,193 @@ protected void setupContent() { ruleBox.add(boardPanel); treeBox.add(ruleBox); this.add(treeBox); - // consoleBox.add(treeBox); - // - // getContentPane().add(consoleBox); - - // JPopupPanel popupPanel = new JPopupPanel(); - // setGlassPane(popupPanel); - // popupPanel.setVisible(true); mainPanel.setDividerLocation(mainPanel.getMaximumDividerLocation() + 100); - // frame.pack(); + revalidate(); } - 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 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); + } - toolBar = new JToolBar(); - toolBar.setFloatable(false); - toolBar.setRollover(true); + /** + * 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()); - 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(); + 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(); + }); - toolBar.add(getToolBarButtons()[i]); - getToolBarButtons()[i].setToolTipText(toolBarName); + getToolBar2Buttons()[2] = redo; + toolBar2.add(getToolBar2Buttons()[2]); - getToolBarButtons()[i].setVerticalTextPosition(SwingConstants.BOTTOM); - getToolBarButtons()[i].setHorizontalTextPosition(SwingConstants.CENTER); - } + 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); + } - // 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); + /** + * Sets the toolbar1 buttons + * + * @param toolBar1Buttons toolbar buttons + */ + public void setToolBar1Buttons(JButton[] toolBar1Buttons) { + this.toolBar1Buttons = toolBar1Buttons; + } + + /** + * Sets the toolbar2 buttons + * + * @param toolBar2Buttons toolbar buttons + */ + public void setToolBar2Buttons(JButton[] toolBar2Buttons) { + this.toolBar2Buttons = toolBar2Buttons; } /** - * Sets the toolbar buttons + * Gets the toolbar1 buttons * - * @param toolBarButtons toolbar buttons + * @return toolbar1 buttons */ - public void setToolBarButtons(JButton[] toolBarButtons) { - this.toolBarButtons = toolBarButtons; + public JButton[] getToolBar1Buttons() { + return toolBar1Buttons; } /** - * Gets the toolbar buttons + * Gets the toolbar2 buttons * - * @return toolbar buttons + * @return toolbar2 buttons */ - public JButton[] getToolBarButtons() { - return toolBarButtons; + public JButton[] getToolBar2Buttons() { + return toolBar2Buttons; } - /** Checks the proof for correctness */ + /** + * 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. + */ private void checkProof() { GameBoardFacade facade = GameBoardFacade.getInstance(); Tree tree = GameBoardFacade.getInstance().getTree(); @@ -857,11 +1033,61 @@ 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(); @@ -885,16 +1111,19 @@ public void setPuzzleView(Puzzle puzzle) { ruleFrame.getContradictionPanel().setRules(puzzle.getContradictionRules()); ruleFrame.getSearchPanel().setSearchBar(puzzle); - toolBarButtons[ToolbarName.CHECK.ordinal()].setEnabled(true); - // toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(true); - + toolBar1.setVisible(false); + setupToolBar2(); 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()); } @@ -949,6 +1178,15 @@ 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(); @@ -1003,20 +1241,35 @@ 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 a action is pushed onto the edu.rpi.legup.history stack + * Called when an action is pushed onto the edu.rpi.legup.history stack * * @param command action to push onto the stack */ @@ -1024,23 +1277,19 @@ 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() + " *"); } - /** Called when the history is cleared */ + /** + * 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. + */ @Override - public void onClearHistory() { - // undo.setEnabled(false); - // toolBarButtons[ToolbarName.UNDO.ordinal()].setEnabled(false); - // redo.setEnabled(false); - // toolBarButtons[ToolbarName.REDO.ordinal()].setEnabled(false); - } + public void onClearHistory() {} /** * Called when an action is redone @@ -1051,9 +1300,7 @@ 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()); @@ -1074,9 +1321,7 @@ 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) { @@ -1120,6 +1365,7 @@ 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 f50c8d6fc..7c2ba06ff 100644 --- a/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java +++ b/src/main/java/edu/rpi/legup/ui/PuzzleEditorPanel.java @@ -28,18 +28,25 @@ 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 toolBar; + private JToolBar toolBar1; + private JToolBar toolBar2; private JFileChooser folderBrowser; private JFrame frame; private JButton[] buttons; JSplitPane splitPanel; - private JButton[] toolBarButtons; + private JButton[] toolBar1Buttons; + private JButton[] toolBar2Buttons; private JPanel elementPanel; private DynamicView dynamicBoardView; private BoardView boardView; @@ -51,8 +58,20 @@ public class PuzzleEditorPanel extends LegupPanel implements IHistoryListener { private JPanel treePanel; private LegupUI legupUI; private EditorElementController editorElementController; - static final int[] TOOLBAR_SEPARATOR_BEFORE = {2, 4, 8}; - + 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 + */ public PuzzleEditorPanel(FileDialog fileDialog, JFrame frame, LegupUI legupUI) { this.fileDialog = fileDialog; this.frame = frame; @@ -61,6 +80,10 @@ 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()); @@ -92,6 +115,10 @@ 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(); @@ -103,26 +130,31 @@ public void setMenuBar() { menus[0] = new JMenu("File"); // file>new - JMenuItem newPuzzle = new JMenuItem("New"); - newPuzzle.addActionListener((ActionEvent) -> loadPuzzle()); + JMenuItem openPuzzle = new JMenuItem("Open"); + openPuzzle.addActionListener((ActionEvent) -> loadPuzzle()); if (os.equals("mac")) { - newPuzzle.setAccelerator( + openPuzzle.setAccelerator( KeyStroke.getKeyStroke( - 'N', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + 'O', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); } else { - newPuzzle.setAccelerator(KeyStroke.getKeyStroke('N', InputEvent.CTRL_DOWN_MASK)); + openPuzzle.setAccelerator(KeyStroke.getKeyStroke('O', InputEvent.CTRL_DOWN_MASK)); } - // file>save - JMenuItem savePuzzle = new JMenuItem("Save As"); - savePuzzle.addActionListener((ActionEvent) -> savePuzzle()); - JMenuItem directSavePuzzle = new JMenuItem("Direct Save Proof "); - directSavePuzzle.addActionListener((ActionEvent) -> direct_save()); + // 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; + }); if (os.equals("mac")) { - newPuzzle.setAccelerator( + createPuzzle.setAccelerator( KeyStroke.getKeyStroke( - 'D', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); + 'C', Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); } else { - newPuzzle.setAccelerator(KeyStroke.getKeyStroke('D', InputEvent.CTRL_DOWN_MASK)); + createPuzzle.setAccelerator(KeyStroke.getKeyStroke('C', InputEvent.CTRL_DOWN_MASK)); } JMenuItem exit = new JMenuItem("Exit"); @@ -134,9 +166,9 @@ public void setMenuBar() { } else { exit.setAccelerator(KeyStroke.getKeyStroke('Q', InputEvent.CTRL_DOWN_MASK)); } - menus[0].add(newPuzzle); - menus[0].add(savePuzzle); - menus[0].add(directSavePuzzle); + menus[0].add(openPuzzle); + menus[0].add(createPuzzle); + // menus[0].add(directSavePuzzle); menus[0].add(exit); // EDIT @@ -147,7 +179,8 @@ public void setMenuBar() { redo = new JMenuItem("Redo"); fitBoardToScreen = new JMenuItem("Fit Board to Screen"); - menus[1].add(undo); + // TODO: Undo operation currently does not get updated correctly in history + // menus[1].add(undo); undo.addActionListener((ActionEvent) -> GameBoardFacade.getInstance().getHistory().undo()); if (os.equals("mac")) { undo.setAccelerator( @@ -157,8 +190,8 @@ public void setMenuBar() { undo.setAccelerator(KeyStroke.getKeyStroke('Z', InputEvent.CTRL_DOWN_MASK)); } - menus[1].add(redo); - + // TODO: Redo operation currently does not get updated correctly in history + // menus[1].add(redo); // Created action to support two keybinds (CTRL-SHIFT-Z, CTRL-Y) Action redoAction = new AbstractAction() { @@ -232,6 +265,11 @@ 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(); @@ -240,118 +278,192 @@ 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(); - - setupToolBar(); + setupToolBar1(); setupContent(); setMenuBar(); } - 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; - } + /** + * 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 check_and_save = + URL open_url = ClassLoader.getSystemClassLoader() - .getResource("edu/rpi/legup/images/Legup/Check.png"); - ImageIcon imageIcon = new ImageIcon(check_and_save); - Image image = imageIcon.getImage(); - imageIcon = + .getResource("edu/rpi/legup/images/Legup/Open.png"); + ImageIcon OpenImageIcon = new ImageIcon(open_url); + Image OpenImage = OpenImageIcon.getImage(); + OpenImageIcon = new ImageIcon( - image.getScaledInstance( + OpenImage.getScaledInstance( this.TOOLBAR_ICON_SCALE, this.TOOLBAR_ICON_SCALE, Image.SCALE_SMOOTH)); - JButton checkandsave = new JButton("check and Save", imageIcon); - checkandsave.setFocusPainted(false); - checkandsave.addActionListener( - new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - // 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()); + 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())); + } } }); - getToolBarButtons()[lastone + 1] = checkandsave; - System.out.println("it is create new file"); - toolBar = new JToolBar(); - toolBar.setFloatable(false); - toolBar.setRollover(true); + getToolBar2Buttons()[0] = resetButton; + toolBar2.add(getToolBar2Buttons()[0]); - 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(); + 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)); - toolBar.add(getToolBarButtons()[i]); - getToolBarButtons()[i].setToolTipText(toolBarName); + JButton saveas = new JButton("Save As", SaveAsImageIcon); + saveas.setFocusPainted(false); + saveas.addActionListener((ActionEvent) -> savePuzzle()); - getToolBarButtons()[i].setVerticalTextPosition(SwingConstants.BOTTOM); - getToolBarButtons()[i].setHorizontalTextPosition(SwingConstants.CENTER); - } + getToolBar2Buttons()[1] = saveas; + toolBar2.add(getToolBar2Buttons()[1]); + + URL save_and_solve = + ClassLoader.getSystemClassLoader() + .getResource("edu/rpi/legup/images/Legup/Check.png"); + ImageIcon SaveSolveImageIcon = new ImageIcon(save_and_solve); + Image SaveSolveImage = SaveSolveImageIcon.getImage(); + SaveSolveImageIcon = + new ImageIcon( + SaveSolveImage.getScaledInstance( + this.TOOLBAR_ICON_SCALE, + this.TOOLBAR_ICON_SCALE, + Image.SCALE_SMOOTH)); + + JButton saveandsolve = new JButton("Save & Solve", SaveSolveImageIcon); + saveandsolve.setFocusPainted(false); + saveandsolve.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()); + } + } + }); + getToolBar2Buttons()[2] = saveandsolve; + toolBar2.add(getToolBar2Buttons()[2]); - // 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); + this.add(toolBar2, 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(); @@ -365,6 +477,13 @@ 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 { @@ -377,11 +496,18 @@ public void loadPuzzleFromHome(String game, String[] statements) { } } - // File opener + /** + * 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 + */ public Object[] promptPuzzle() { GameBoardFacade facade = GameBoardFacade.getInstance(); if (facade.getBoard() != null) { - if (noQuit("Opening a new puzzle?")) { + if (noQuit("Open an existing puzzle?")) { return new Object[0]; } } @@ -405,7 +531,6 @@ public Object[] promptPuzzle() { fileBrowser.setAcceptAllFileFilterUsed(false); File puzzlePath = fileBrowser.getSelectedFile(); - System.out.println(puzzlePath.getAbsolutePath()); if (puzzlePath != null) { fileName = puzzlePath.getAbsolutePath(); @@ -420,6 +545,11 @@ 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) @@ -431,6 +561,13 @@ 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 { @@ -438,6 +575,9 @@ 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()); @@ -451,42 +591,90 @@ 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); - } + public void onClearHistory() {} + /** + * Returns the current board view + * + * @return the board view + */ public BoardView getBoardView() { return boardView; } - public JButton[] getToolBarButtons() { - return toolBarButtons; + /** + * 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 void setToolBarButtons(JButton[] toolBarButtons) { - this.toolBarButtons = toolBarButtons; + /** + * Returns the array of buttons for the second toolbar + * + * @return the array of toolbar2 buttons + */ + public JButton[] getToolBar2Buttons() { + return toolBar2Buttons; } + /** + * 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()); @@ -502,13 +690,11 @@ public void setPuzzleView(Puzzle puzzle) { dynamicBoardView.setBorder(titleBoard); puzzle.addBoardListener(puzzle.getBoardView()); - System.out.println("Setting elements"); if (this.elementFrame != null) { elementFrame.setElements(puzzle); } - - toolBarButtons[ToolbarName.CHECK.ordinal()].setEnabled(true); - // toolBarButtons[ToolbarName.SAVE.ordinal()].setEnabled(true); + toolBar1.setVisible(false); + setupToolBar2(); } /** Saves a puzzle */ @@ -531,6 +717,13 @@ 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) { @@ -567,7 +760,7 @@ private String savePuzzle() { folderBrowser.setAcceptAllFileFilterUsed(false); String path = folderBrowser.getSelectedFile().getAbsolutePath(); - + preferences.setSavedPath(path); if (path != null) { try { PuzzleExporter exporter = puzzle.getExporter(); @@ -582,6 +775,11 @@ 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 0bf8335a2..589573154 100644 --- a/src/main/java/edu/rpi/legup/ui/ScrollView.java +++ b/src/main/java/edu/rpi/legup/ui/ScrollView.java @@ -6,6 +6,11 @@ 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()); @@ -165,6 +170,12 @@ 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) { @@ -282,6 +293,11 @@ 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 ba02ebd2e..622d16d8d 100644 --- a/src/main/java/edu/rpi/legup/ui/ToolbarName.java +++ b/src/main/java/edu/rpi/legup/ui/ToolbarName.java @@ -1,11 +1,12 @@ 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_ALL; + CHECK; /** * 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 aa5b65c4e..40f36113f 100644 --- a/src/main/java/edu/rpi/legup/ui/ZoomWidget.java +++ b/src/main/java/edu/rpi/legup/ui/ZoomWidget.java @@ -10,6 +10,10 @@ 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(); @@ -32,12 +36,13 @@ 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); @@ -47,6 +52,11 @@ 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 934d31c53..66af90abd 100644 --- a/src/main/java/edu/rpi/legup/ui/ZoomablePane.java +++ b/src/main/java/edu/rpi/legup/ui/ZoomablePane.java @@ -5,6 +5,10 @@ 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 ca03f1e25..18b47d98b 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/BoardView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/BoardView.java @@ -11,6 +11,10 @@ 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; @@ -125,6 +129,7 @@ public void setBoard(Board board) { } } + /** Configures the view to handle case interactions */ protected void setCasePickable() { CaseBoard caseBoard = (CaseBoard) board; Board baseBoard = caseBoard.getBaseBoard(); @@ -183,6 +188,11 @@ public ArrayList getElementViews() { return elementViews; } + /** + * Gets the ElementController associated with this board view. + * + * @return the ElementController + */ public ElementController getElementController() { return elementController; } @@ -192,6 +202,11 @@ 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); @@ -208,5 +223,10 @@ 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 cedfa08fe..bc40e3d58 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/DataSelectionView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/DataSelectionView.java @@ -5,8 +5,17 @@ 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 8e6f2cb18..eaa85082d 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/ElementSelection.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/ElementSelection.java @@ -3,25 +3,48 @@ 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); @@ -32,12 +55,19 @@ 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); @@ -45,10 +75,20 @@ 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) { @@ -57,6 +97,7 @@ 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); @@ -64,10 +105,20 @@ 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 83b2cb099..eab13cc1d 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/ElementView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/ElementView.java @@ -9,6 +9,10 @@ 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; @@ -73,6 +77,12 @@ 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( @@ -87,8 +97,19 @@ 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)); @@ -97,6 +118,11 @@ 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)); @@ -105,6 +131,11 @@ 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( @@ -112,6 +143,11 @@ 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); @@ -193,10 +229,20 @@ 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; } @@ -281,6 +327,14 @@ 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 @@ -289,17 +343,43 @@ 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( @@ -309,12 +389,31 @@ 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( @@ -324,22 +423,48 @@ 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 c40303192..e2b8bbc12 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/GridBoardView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/GridBoardView.java @@ -5,6 +5,10 @@ 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; @@ -51,6 +55,14 @@ 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); @@ -58,7 +70,10 @@ public GridElementView getElement(int xIndex, int yIndex) { return null; } - /** Initializes the initial dimension of the viewport for the GridBoardView */ + /** + * Initializes the initial dimension of the viewport for the GridBoardView. Sets the size of the + * board view and adjusts the zoom to fit. + */ @Override public void initSize() { setSize(getProperSize()); @@ -66,9 +81,9 @@ public void initSize() { } /** - * Helper method to determine the proper dimension of the grid view + * Determines the proper dimension of the grid view based on grid size and element size. * - * @return proper dimension of the grid view + * @return the dimension of the grid view */ protected Dimension getProperSize() { Dimension boardViewSize = new Dimension(); @@ -77,10 +92,21 @@ 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 440b3a693..587ba9efb 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/GridElementView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/GridElementView.java @@ -2,6 +2,10 @@ 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 b2d3e31dd..6a1429a61 100644 --- a/src/main/java/edu/rpi/legup/ui/boardview/SelectionItemView.java +++ b/src/main/java/edu/rpi/legup/ui/boardview/SelectionItemView.java @@ -3,28 +3,66 @@ 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 1fb0a16ab..4d7bb378d 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,6 +2,12 @@ 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 f695491fb..92e747305 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,6 +2,12 @@ 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 { /** * 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 2795f2df7..17fcac5bb 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,6 +4,12 @@ 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 { 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 e9c274250..929f211d0 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,6 +3,11 @@ 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 6279f93a4..b14091351 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,6 +10,13 @@ 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 "; @@ -28,6 +35,12 @@ 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 = @@ -118,7 +131,7 @@ public void resetSize() { /** * Set the status label to a value. Use resetStatus to clear it. * - * @param check true iff we want a check box, if false we'll have a red x box + * @param check true if we want a checkbox, 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) { @@ -156,22 +169,47 @@ 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 5d985d5c2..85ddf82b5 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,6 +9,10 @@ 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; @@ -32,7 +36,7 @@ public RulePanel(RuleFrame ruleFrame) { } /** - * Gets the rule rule buttons + * Gets the array of rule buttons * * @return rule ruleButtons */ @@ -57,6 +61,15 @@ public void setRules(List rules) { 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].setHorizontalTextPosition(JButton.CENTER); ruleButtons[i].setVerticalTextPosition(JButton.BOTTOM); @@ -69,6 +82,7 @@ public void setRules(List rules) { revalidate(); } + /** Updates the rules displayed by reloading images and setting the rules again. */ public void updateRules() { for (Rule rule : rules) { rule.loadImage(); @@ -324,28 +338,58 @@ 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 aba4707cd..60e24b986 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,6 +2,11 @@ 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 33c04717d..e5e523e11 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,6 +4,11 @@ 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; @@ -36,7 +41,7 @@ protected TreeElementView(TreeElementType type, TreeElement treeElement) { public abstract void draw(Graphics2D graphics2D); /** - * Gets the span for the sub tree rooted at this view + * Gets the span for the subtree rooted at this view * * @return span bounded y span */ @@ -45,7 +50,7 @@ public double getSpan() { } /** - * Sets the span for the sub tree rooted at this view. + * Sets the span for the subtree 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 990d96620..c9aedaef3 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,6 +7,12 @@ 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; @@ -260,51 +266,130 @@ 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 b6a29f2b5..936b0de95 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,6 +17,11 @@ 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; @@ -29,6 +34,7 @@ public class TreePanel extends JPanel { private JLabel status; + /** Constructs a {@code TreePanel} and initializes the UI components. */ public TreePanel(/*LegupUI legupUI*/ ) { // this.legupUI = legupUI; @@ -60,10 +66,20 @@ 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; @@ -71,6 +87,10 @@ 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) { @@ -79,22 +99,41 @@ 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(); @@ -107,6 +146,10 @@ 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(); @@ -119,6 +162,10 @@ 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(); @@ -131,6 +178,10 @@ 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 002092155..232498723 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,11 +3,18 @@ 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; @@ -16,6 +23,11 @@ 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 c805021be..3aec664be 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,5 +1,9 @@ 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 8f3ebfc23..06af84c57 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,6 +3,10 @@ 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 b022ac596..5a3aa7d5c 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,6 +10,11 @@ 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; @@ -266,87 +271,177 @@ public void removeParentView(TreeNodeView nodeView) { } } - public Point getEndPoint() { - return endPoint; - } - - public void setEndPoint(Point endPoint) { - this.endPoint = endPoint; - } - + /** + * Gets the x-coordinate of the end point of the transition arrow + * + * @return the x-coordinate of the end point + */ 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; } - public List getLineStartPoints() { - return lineStartPoints; - } - - public void setLineStartPoints(List lineStartPoints) { - this.lineStartPoints = lineStartPoints; - } - + /** + * 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 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 f491009b4..65f49ff0a 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,6 +26,12 @@ 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()); @@ -51,6 +57,11 @@ 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<>(); @@ -62,6 +73,11 @@ 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; } @@ -131,6 +147,11 @@ 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) { @@ -148,6 +169,7 @@ 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; @@ -155,15 +177,21 @@ 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() { - double fitWidth = (viewport.getWidth() - 8.0) / (getSize().width - 200); - double fitHeight = (viewport.getHeight() - 8.0) / (getSize().height - 120); + 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); zoomTo(Math.min(fitWidth, fitHeight)); viewport.setViewPosition(new Point(0, viewport.getHeight() / 2)); } @@ -212,6 +240,11 @@ 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(); @@ -234,11 +267,21 @@ 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); @@ -249,6 +292,11 @@ 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; @@ -282,6 +330,7 @@ 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; @@ -407,8 +456,17 @@ public TreeElementView getElementView(TreeElement element) { return viewMap.get(element); } - private void removeTreeNode(TreeNode node) { + /** + * Removes the specified {@link TreeNode} and its associated views + * + * @param node the {@link TreeNode} to be removed + */ + public 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 @@ -435,11 +493,13 @@ private void removeTreeNode(TreeNode node) { } // set modifiable if started modifiable - boolean modifiable = - tree.getRootNode() - .getBoard() - .getPuzzleElement(oldElement) - .isModifiable(); + boolean modifiable = false; + if (tree != null) { + tree.getRootNode() + .getBoard() + .getPuzzleElement(oldElement) + .isModifiable(); + } // unmodifiable if already modified TreeNode modNode = ancestor.getParent().getParents().get(0); @@ -457,22 +517,60 @@ private void removeTreeNode(TreeNode node) { } } } - node.getChildren().forEach(t -> removeTreeTransition(t)); } - private void removeTreeTransition(TreeTransition trans) { + /** + * Removes the specified {@link TreeTransition} and its associated views + * + * @param trans the {@link TreeTransition} to be removed + */ + public 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); - TreeTransitionView parentView = (TreeTransitionView) viewMap.get(parent); + TreeTransitionView parentView = (TreeTransitionView) viewMap.get(parent); nodeView.setParentView(parentView); parentView.setChildView(nodeView); @@ -507,47 +605,51 @@ 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); + 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 : - caseRule.dependentElements(parent.getBoard(), trans.getSelection())) { - // increment and lock - PuzzleElement oldElement = + + for (PuzzleElement element : parent.getBoard().getPuzzleElements()) { + PuzzleElement curElement = ancestor.getParent().getBoard().getPuzzleElement(element); - oldElement.setCasesDepended(oldElement.getCasesDepended() + 1); - oldElement.setModifiable(false); + curElement.setModifiableCaseRule(false); } } } } - viewMap.put(trans, transView); - if (trans.getChildNode() != null) { addTreeNode(trans.getChildNode()); } } - /// New Draw Methods - + /** + * Draws the tree using the provided {@link Graphics2D} object + * + * @param graphics2D the {@link Graphics2D} object used for drawing the tree + */ public void drawTree(Graphics2D graphics2D) { if (tree == null) { LOGGER.error("Unable to draw tree."); @@ -573,6 +675,11 @@ 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); @@ -606,6 +713,14 @@ 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; @@ -711,6 +826,13 @@ 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; @@ -779,12 +901,12 @@ public void calcSpan(TreeElementView view) { } /** - * 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 + * 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. * - * @param view - * @param stop + * @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 */ private void subCalcSpan(TreeElementView view, TreeElementView stop) { // safe-guard for infinite loop @@ -859,12 +981,14 @@ private void subCalcSpan(TreeElementView view, TreeElementView stop) { } /** - * 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 + * 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. * - * @param node root node of the branches - * @param branches DisjointSets of the child branches of the specified node which determine - * which branches merge + * @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 */ 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 71a65b49e..c509b6cb9 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,6 +4,11 @@ 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 e0524f84d..1619ecbaa 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 @@ -2,7 +2,6 @@ import edu.rpi.legup.controller.EditorElementController; import edu.rpi.legup.model.Puzzle; -import edu.rpi.legup.ui.lookandfeel.components.MaterialTabbedPaneUI; import java.awt.*; import javax.swing.*; import javax.swing.border.TitledBorder; @@ -14,52 +13,36 @@ public class ElementFrame extends JPanel { private static final String htmlTail = ""; private PlaceableElementPanel placeableElementPanel; - private NonPlaceableElementPanel nonPlaceableElementPanel; - private JTabbedPane tabbedPane; + // 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(); - nonPlaceableElementPanel = new NonPlaceableElementPanel(this); - // nonPlaceableElementPanel.setMinimumSize(new Dimension(100,200)); - tabbedPane.addTab( - nonPlaceableElementPanel.getName(), - nonPlaceableElementPanel.getIcon(), - new JScrollPane(nonPlaceableElementPanel), - nonPlaceableElementPanel.getToolTip()); + // Parent panel to hold all elements + JPanel elementPanel = new JPanel(); + elementPanel.setLayout(new BoxLayout(elementPanel, BoxLayout.Y_AXIS)); placeableElementPanel = new PlaceableElementPanel(this); - // placeableElementPanel.setMinimuSize(new Dimension(100,200)); - tabbedPane.addTab( - placeableElementPanel.getName(), - placeableElementPanel.getIcon(), - new JScrollPane(placeableElementPanel), - placeableElementPanel.getToolTip()); - tabbedPane.setTabPlacement(JTabbedPane.TOP); + placeableElementPanel.setMinimumSize(new Dimension(100, 200)); + elementPanel.add(new JScrollPane(placeableElementPanel)); + // Set layout and dimensions for the main panel setLayout(new BorderLayout()); setMinimumSize(new Dimension(250, 256)); setPreferredSize(new Dimension(330, 256)); - add(tabbedPane); + // Add components to the main panel + add(elementPanel, BorderLayout.CENTER); add(status, BorderLayout.SOUTH); + // Center-align the titled border TitledBorder title = BorderFactory.createTitledBorder("Elements"); title.setTitleJustification(TitledBorder.CENTER); setBorder(title); @@ -69,29 +52,26 @@ 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) { - nonPlaceableElementPanel.setElements(puzzle.getNonPlaceableElements()); - placeableElementPanel.setElements(puzzle.getPlaceableElements()); + if (puzzle != null) { + placeableElementPanel.setElements(puzzle.getPlaceableElements()); + } } public EditorElementController getController() { return controller; } - public JTabbedPane getTabbedPane() { - return tabbedPane; - } - - public NonPlaceableElementPanel getNonPlaceableElementPanel() { - return nonPlaceableElementPanel; - } + // public JTabbedPane getTabbedPane() { + // return tabbedPane; + // } 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 46198e226..70826d25c 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 void setElements(List elements) { + public int setElements(List elements) { this.elements = elements; clearButtons(); @@ -38,6 +38,7 @@ public void 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 00b4f5379..91e8d2e49 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 new file mode 100644 index 000000000..fd6ffefa0 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/Legup/Reset.png differ 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 a74654d43..27ccec972 100644 Binary files a/src/main/resources/edu/rpi/legup/images/binary/rules/CompleteRowColumnDirectRule.png and b/src/main/resources/edu/rpi/legup/images/binary/rules/CompleteRowColumnDirectRule.png differ diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/DuplicateRowOrColumnContradictionRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/DuplicateRowOrColumnContradictionRule.png deleted file mode 100644 index 214aa5348..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/binary/rules/DuplicateRowOrColumnContradictionRule.png and /dev/null differ 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 deleted file mode 100644 index 73072f2ce..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/binary/rules/OneOrZeroCaseRule.png and /dev/null differ 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 deleted file mode 100644 index b68f67e44..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/binary/rules/OneTileGapDirectRule.png and /dev/null differ 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 new file mode 100644 index 000000000..ed0701f26 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/binary/rules/PreventTrioDirectRule.png differ 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 new file mode 100644 index 000000000..5cc030a76 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/binary/rules/RepeatedRowColumnContradictionRule.png differ 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 new file mode 100644 index 000000000..7695ae2da Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/binary/rules/SaveBlockerDirectRule.png differ diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/SurroundPairDirectRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/SurroundPairDirectRule.png deleted file mode 100644 index 67a4e47f3..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/binary/rules/SurroundPairDirectRule.png and /dev/null differ 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 deleted file mode 100644 index 862408b63..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/binary/rules/ThreeAdjacentContradictionRule.png and /dev/null differ 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 new file mode 100644 index 000000000..e292ec9fa Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/binary/rules/TrioContradictionRule.png differ diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/UnbalancedRowColumnContradictionRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/UnbalancedRowColumnContradictionRule.png index 029bd12ac..bd66c16a7 100644 Binary files a/src/main/resources/edu/rpi/legup/images/binary/rules/UnbalancedRowColumnContradictionRule.png and b/src/main/resources/edu/rpi/legup/images/binary/rules/UnbalancedRowColumnContradictionRule.png differ 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 new file mode 100644 index 000000000..7785d198c Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/binary/rules/UniqueRowColumnDirectRule.png differ 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 new file mode 100644 index 000000000..8813d7956 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/binary/rules/WastedBlockerContradictionRule.png differ diff --git a/src/main/resources/edu/rpi/legup/images/binary/rules/ZeroOrOneCaseRule.png b/src/main/resources/edu/rpi/legup/images/binary/rules/ZeroOrOneCaseRule.png new file mode 100644 index 000000000..7394bff91 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/binary/rules/ZeroOrOneCaseRule.png differ 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 new file mode 100644 index 000000000..41fb61dfa Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/binary/tiles/NumberTile.png differ diff --git a/src/main/resources/edu/rpi/legup/images/binary/tiles/UnknownTile.png b/src/main/resources/edu/rpi/legup/images/binary/tiles/UnknownTile.png new file mode 100644 index 000000000..9ab9f7481 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/binary/tiles/UnknownTile.png differ diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/cases/MineOrEmpty.png b/src/main/resources/edu/rpi/legup/images/minesweeper/cases/MineOrEmpty.png new file mode 100644 index 000000000..00ab7dfd5 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/minesweeper/cases/MineOrEmpty.png differ diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/cases/SatisfyNumber.png b/src/main/resources/edu/rpi/legup/images/minesweeper/cases/SatisfyNumber.png new file mode 100644 index 000000000..e4458d16d Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/minesweeper/cases/SatisfyNumber.png differ diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/contradictions/IsolateMine.png b/src/main/resources/edu/rpi/legup/images/minesweeper/contradictions/IsolateMine.png new file mode 100644 index 000000000..0b720c2f7 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/minesweeper/contradictions/IsolateMine.png differ diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/contradictions/TooFewMines.png b/src/main/resources/edu/rpi/legup/images/minesweeper/contradictions/TooFewMines.png new file mode 100644 index 000000000..547b1d1c9 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/minesweeper/contradictions/TooFewMines.png differ diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/contradictions/TooManyMines.png b/src/main/resources/edu/rpi/legup/images/minesweeper/contradictions/TooManyMines.png new file mode 100644 index 000000000..6c061a104 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/minesweeper/contradictions/TooManyMines.png differ diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/direct/FinishWithEmpty.png b/src/main/resources/edu/rpi/legup/images/minesweeper/direct/FinishWithEmpty.png new file mode 100644 index 000000000..7988679a2 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/minesweeper/direct/FinishWithEmpty.png differ diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/direct/FinishWithMines.png b/src/main/resources/edu/rpi/legup/images/minesweeper/direct/FinishWithMines.png new file mode 100644 index 000000000..3268ea697 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/minesweeper/direct/FinishWithMines.png differ diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/direct/NonSharedEmpty.png b/src/main/resources/edu/rpi/legup/images/minesweeper/direct/NonSharedEmpty.png new file mode 100644 index 000000000..b8d982f19 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/minesweeper/direct/NonSharedEmpty.png differ diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/direct/NonSharedMine.png b/src/main/resources/edu/rpi/legup/images/minesweeper/direct/NonSharedMine.png new file mode 100644 index 000000000..ab6242606 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/minesweeper/direct/NonSharedMine.png differ diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/tiles/Bomb.png b/src/main/resources/edu/rpi/legup/images/minesweeper/tiles/Mine.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/minesweeper/tiles/Bomb.png rename to src/main/resources/edu/rpi/legup/images/minesweeper/tiles/Mine.png diff --git a/src/main/resources/edu/rpi/legup/images/minesweeper/tiles/Unset.png b/src/main/resources/edu/rpi/legup/images/minesweeper/tiles/Unset.png new file mode 100644 index 000000000..7e269c7cf Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/minesweeper/tiles/Unset.png differ diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/cases/CellForNumber.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/cases/CellForNumber.png index 7ab0cdbef..e8cca03b4 100644 Binary files a/src/main/resources/edu/rpi/legup/images/skyscrapers/cases/CellForNumber.png and b/src/main/resources/edu/rpi/legup/images/skyscrapers/cases/CellForNumber.png differ 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 fedbbbac8..307d0d754 100644 Binary files a/src/main/resources/edu/rpi/legup/images/skyscrapers/cases/NumberForCell.png and b/src/main/resources/edu/rpi/legup/images/skyscrapers/cases/NumberForCell.png differ 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 7ba489c2b..6ab3cd769 100644 Binary files a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/DuplicateNumber.png and b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/DuplicateNumber.png differ 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 ff999f4bc..2f5b5237d 100644 Binary files a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/ExceedingVisibility.png and b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/ExceedingVisibility.png differ 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 cfe04a503..285451ba1 100644 Binary files a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/InsufficientVisibility.png and b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/InsufficientVisibility.png differ 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 6d330baa6..6c4a6e457 100644 Binary files a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/PreemptiveVisibility.png and b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/PreemptiveVisibility.png differ diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedCell.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedCell.png index 76947671d..ae78caf49 100644 Binary files a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedCell.png and b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedCell.png differ diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedNumber.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedNumber.png index 8862702f1..602f62702 100644 Binary files a/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedNumber.png and b/src/main/resources/edu/rpi/legup/images/skyscrapers/contradictions/UnresolvedNumber.png differ diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/FixedMax.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/FixedMax.png index 8a26bbfeb..86a4ee8a0 100644 Binary files a/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/FixedMax.png and b/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/FixedMax.png differ 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 216a0f3a1..8db8a3bbf 100644 Binary files a/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/LastCell.png and b/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/LastCell.png differ diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/LastNumber.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/LastNumber.png index 1e0555648..dcf4cb2b8 100644 Binary files a/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/LastNumber.png and b/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/LastNumber.png differ 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 d3dd36bbc..b238b36aa 100644 Binary files a/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/NEdge.png and b/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/NEdge.png differ diff --git a/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/OneEdge.png b/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/OneEdge.png index 40d6ff65f..6841a8034 100644 Binary files a/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/OneEdge.png and b/src/main/resources/edu/rpi/legup/images/skyscrapers/rules/OneEdge.png differ diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/black.gif b/src/main/resources/edu/rpi/legup/images/starbattle/black.gif deleted file mode 100644 index 13381a717..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/black.gif and /dev/null differ diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/cases/StarOrEmptyCaseRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/cases/StarOrEmptyCaseRule.png deleted file mode 100644 index 0383711bb..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/cases/StarOrEmptyCaseRule.png and /dev/null differ 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 deleted file mode 100644 index d25340b40..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/ClashingOrbitContradictionRule.png and /dev/null differ diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/TooFewStarsContradictionRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/TooFewStarsContradictionRule.png deleted file mode 100644 index cc019752f..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/TooFewStarsContradictionRule.png and /dev/null differ 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 deleted file mode 100644 index b468ae6f6..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/contradictions/TooManyStarsContradictionRule.png and /dev/null differ diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/empty.gif b/src/main/resources/edu/rpi/legup/images/starbattle/empty.gif deleted file mode 100644 index 38b91d0a2..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/empty.gif and /dev/null differ 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 deleted file mode 100644 index f72fd7d7f..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/rules/BlackOutDirectRule.png and /dev/null differ 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 deleted file mode 100644 index 1a88e9d07..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/rules/ColumnsWithinRegionsDirectRule.png and /dev/null differ 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 deleted file mode 100644 index 24fb0d48a..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/rules/ColumnsWithinRowsDirectRule.png and /dev/null differ 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 deleted file mode 100644 index 11cfbf899..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/rules/FinishWithStarDirectRule.png and /dev/null differ diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/RegionsWithinColumnsDirectRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/RegionsWithinColumnsDirectRule.png deleted file mode 100644 index fde683dcd..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/rules/RegionsWithinColumnsDirectRule.png and /dev/null differ diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/rules/RegionsWithinRowsDirectRule.png b/src/main/resources/edu/rpi/legup/images/starbattle/rules/RegionsWithinRowsDirectRule.png deleted file mode 100644 index 30170df40..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/rules/RegionsWithinRowsDirectRule.png and /dev/null differ 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 deleted file mode 100644 index bac64a87c..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/rules/RowsWithinColumnsDirectRule.png and /dev/null differ 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 deleted file mode 100644 index 8907e0475..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/rules/RowsWithinRegionsDirectRule.png and /dev/null differ 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 deleted file mode 100644 index 13287f779..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/rules/SurroundStar.png and /dev/null differ diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/star.gif b/src/main/resources/edu/rpi/legup/images/starbattle/star.gif deleted file mode 100644 index e289b287a..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/star.gif and /dev/null differ diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/AdvancedDeduction.png b/src/main/resources/edu/rpi/legup/images/sudoku/AdvancedDeduction.png deleted file mode 100644 index d51538baf..000000000 Binary files a/src/main/resources/edu/rpi/legup/images/sudoku/AdvancedDeduction.png and /dev/null differ 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 new file mode 100644 index 000000000..9dcade64b Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/sudoku/rules/NoCellForNumberColumn.png differ 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 new file mode 100644 index 000000000..000dc8b68 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/sudoku/rules/NoCellForNumberRegion.png differ 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 new file mode 100644 index 000000000..f984f8365 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/sudoku/rules/NoCellForNumberRow.png differ diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/NoSolution.png b/src/main/resources/edu/rpi/legup/images/sudoku/rules/NoSolution.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/sudoku/NoSolution.png rename to src/main/resources/edu/rpi/legup/images/sudoku/rules/NoSolution.png diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/PossibleValues.png b/src/main/resources/edu/rpi/legup/images/sudoku/rules/PossibleValues.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/sudoku/PossibleValues.png rename to src/main/resources/edu/rpi/legup/images/sudoku/rules/PossibleValues.png diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/RepeatedNumber.png b/src/main/resources/edu/rpi/legup/images/sudoku/rules/RepeatedNumber.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/sudoku/RepeatedNumber.png rename to src/main/resources/edu/rpi/legup/images/sudoku/rules/RepeatedNumber.png diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/forcedByDeduction.png b/src/main/resources/edu/rpi/legup/images/sudoku/rules/forcedByDeduction.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/sudoku/forcedByDeduction.png rename to src/main/resources/edu/rpi/legup/images/sudoku/rules/forcedByDeduction.png diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/forcedByElimination.png b/src/main/resources/edu/rpi/legup/images/sudoku/rules/forcedByElimination.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/sudoku/forcedByElimination.png rename to src/main/resources/edu/rpi/legup/images/sudoku/rules/forcedByElimination.png diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/possible_cells_number.png b/src/main/resources/edu/rpi/legup/images/sudoku/rules/possible_cells_number.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/sudoku/possible_cells_number.png rename to src/main/resources/edu/rpi/legup/images/sudoku/rules/possible_cells_number.png 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 new file mode 100644 index 000000000..2ebdc5823 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/sudoku/rules/possible_cells_number_column.png differ diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/rules/possible_cells_number_region.png b/src/main/resources/edu/rpi/legup/images/sudoku/rules/possible_cells_number_region.png new file mode 100644 index 000000000..cc371e3e9 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/sudoku/rules/possible_cells_number_region.png differ 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 new file mode 100644 index 000000000..80476a428 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/sudoku/rules/possible_cells_number_row.png differ diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/tem.png b/src/main/resources/edu/rpi/legup/images/sudoku/rules/tem.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/sudoku/tem.png rename to src/main/resources/edu/rpi/legup/images/sudoku/rules/tem.png diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/white.gif b/src/main/resources/edu/rpi/legup/images/sudoku/tiles/NumberTile.png similarity index 82% rename from src/main/resources/edu/rpi/legup/images/starbattle/white.gif rename to src/main/resources/edu/rpi/legup/images/sudoku/tiles/NumberTile.png index fc2c683eb..5a8540d02 100644 Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/white.gif and b/src/main/resources/edu/rpi/legup/images/sudoku/tiles/NumberTile.png differ diff --git a/src/main/resources/edu/rpi/legup/images/starbattle/UnknownTile.png b/src/main/resources/edu/rpi/legup/images/sudoku/tiles/UnknownTile.png similarity index 100% rename from src/main/resources/edu/rpi/legup/images/starbattle/UnknownTile.png rename to src/main/resources/edu/rpi/legup/images/sudoku/tiles/UnknownTile.png diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Element Template.png b/src/main/resources/edu/rpi/legup/images/thermometer/Element Template.png new file mode 100644 index 000000000..52e3e36b7 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Element Template.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockE.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockE.png new file mode 100644 index 000000000..238a301af Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockE.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockN.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockN.png new file mode 100644 index 000000000..8f343a3ba Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockN.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockS.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockS.png new file mode 100644 index 000000000..f95536076 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockS.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockW.png new file mode 100644 index 000000000..b1bbd31be Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadBlockW.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpE.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpE.png new file mode 100644 index 000000000..852043171 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpE.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpN.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpN.png new file mode 100644 index 000000000..78179a633 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpN.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpS.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpS.png new file mode 100644 index 000000000..463b4308c Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpS.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpW.png new file mode 100644 index 000000000..19240e5fc Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadEmpW.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillE.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillE.png new file mode 100644 index 000000000..d577c25f5 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillE.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillN.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillN.png new file mode 100644 index 000000000..9905da3aa Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillN.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillS.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillS.png new file mode 100644 index 000000000..2e6c6d75d Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillS.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillW.png new file mode 100644 index 000000000..94ad0b2b4 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/HeadFillW.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockE.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockE.png new file mode 100644 index 000000000..d351674c5 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockE.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockN.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockN.png new file mode 100644 index 000000000..505aa6ec9 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockN.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockS.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockS.png new file mode 100644 index 000000000..91356f95e Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockS.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockW.png new file mode 100644 index 000000000..6013fca19 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftBlockW.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpE.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpE.png new file mode 100644 index 000000000..b038ea65f Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpE.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpN.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpN.png new file mode 100644 index 000000000..c59c1a930 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpN.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpS.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpS.png new file mode 100644 index 000000000..f70a25cf1 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpS.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpW.png new file mode 100644 index 000000000..48cde1b37 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftEmpW.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillE.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillE.png new file mode 100644 index 000000000..06a507b74 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillE.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillN.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillN.png new file mode 100644 index 000000000..26c177a91 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillN.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillS.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillS.png new file mode 100644 index 000000000..046b96e26 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillS.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillW.png new file mode 100644 index 000000000..3688bbe0a Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/ShaftFillW.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockE.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockE.png new file mode 100644 index 000000000..d78a9723a Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockE.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockN.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockN.png new file mode 100644 index 000000000..eca516d89 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockN.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockS.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockS.png new file mode 100644 index 000000000..a207de1c4 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockS.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockW.png new file mode 100644 index 000000000..f48ff2b28 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipBlockW.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpE.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpE.png new file mode 100644 index 000000000..660506aa7 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpE.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpN.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpN.png new file mode 100644 index 000000000..ae3ca519d Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpN.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpS.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpS.png new file mode 100644 index 000000000..62f60dfb4 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpS.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpW.png new file mode 100644 index 000000000..d67801d76 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipEmpW.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillE.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillE.png new file mode 100644 index 000000000..1f24029e5 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillE.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillN.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillN.png new file mode 100644 index 000000000..e7abdb0bb Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillN.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillS.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillS.png new file mode 100644 index 000000000..bfb506b99 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillS.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillW.png b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillW.png new file mode 100644 index 000000000..e931a7555 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/Elements/TipFillW.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/FinishWithBlocked.png b/src/main/resources/edu/rpi/legup/images/thermometer/FinishWithBlocked.png new file mode 100644 index 000000000..0a36aa3ac Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/FinishWithBlocked.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/FinishWithMercury.png b/src/main/resources/edu/rpi/legup/images/thermometer/FinishWithMercury.png new file mode 100644 index 000000000..6693e2d7b Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/FinishWithMercury.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/MercOrBlocked.png b/src/main/resources/edu/rpi/legup/images/thermometer/MercOrBlocked.png new file mode 100644 index 000000000..66c994de0 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/MercOrBlocked.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/MercuryInBody.png b/src/main/resources/edu/rpi/legup/images/thermometer/MercuryInBody.png new file mode 100644 index 000000000..06cb87e42 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/MercuryInBody.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/MinimumFill.png b/src/main/resources/edu/rpi/legup/images/thermometer/MinimumFill.png new file mode 100644 index 000000000..28719cf53 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/MinimumFill.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/NotEnoughMercury.png b/src/main/resources/edu/rpi/legup/images/thermometer/NotEnoughMercury.png new file mode 100644 index 000000000..d2277e8ef Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/NotEnoughMercury.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/PriorIsFilled.png b/src/main/resources/edu/rpi/legup/images/thermometer/PriorIsFilled.png new file mode 100644 index 000000000..9bda3a60b Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/PriorIsFilled.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/RestIsEmpty.png b/src/main/resources/edu/rpi/legup/images/thermometer/RestIsEmpty.png new file mode 100644 index 000000000..91374e062 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/RestIsEmpty.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/SatisfyMercury.png b/src/main/resources/edu/rpi/legup/images/thermometer/SatisfyMercury.png new file mode 100644 index 000000000..f450a205e Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/SatisfyMercury.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/ThermometerTooLarge.png b/src/main/resources/edu/rpi/legup/images/thermometer/ThermometerTooLarge.png new file mode 100644 index 000000000..eb61b4f1b Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/ThermometerTooLarge.png differ diff --git a/src/main/resources/edu/rpi/legup/images/thermometer/TooManyMercury.png b/src/main/resources/edu/rpi/legup/images/thermometer/TooManyMercury.png new file mode 100644 index 000000000..99a215b12 Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/thermometer/TooManyMercury.png differ diff --git a/src/main/resources/edu/rpi/legup/legup/config b/src/main/resources/edu/rpi/legup/legup/config index a6e2fad7d..e01767677 100644 --- a/src/main/resources/edu/rpi/legup/legup/config +++ b/src/main/resources/edu/rpi/legup/legup/config @@ -34,22 +34,22 @@ fileCreationDisabled="false"/> - + fileCreationDisabled="false"/> - - + + +
diff --git a/src/test/java/legup/TestRunner.java b/src/test/java/legup/TestRunner.java index 5486a0353..e5619681e 100644 --- a/src/test/java/legup/TestRunner.java +++ b/src/test/java/legup/TestRunner.java @@ -30,7 +30,7 @@ public static void main(String[] args) { printTestResults(result6); Result result7 = JUnitCore.runClasses(FinishWithBulbsDirectRuleTest.class); printTestResults(result7); - Result result8 = JUnitCore.runClasses(FinishWithEmptyDirectRuleTest.class); + Result result8 = JUnitCore.runClasses(FinishWithEmptyDirectRuleDirectRuleTest.class); printTestResults(result8); Result result9 = JUnitCore.runClasses(LightOrEmptyCaseRuleTest.class); printTestResults(result9); diff --git a/src/test/java/legup/TestUtilities.java b/src/test/java/legup/TestUtilities.java index 83ce773d4..cd9f4a935 100644 --- a/src/test/java/legup/TestUtilities.java +++ b/src/test/java/legup/TestUtilities.java @@ -6,11 +6,19 @@ import edu.rpi.legup.model.tree.TreeNode; 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 { - puzzle.importPuzzle(ClassLoader.getSystemResourceAsStream(fileName)); + InputStream inputStream = ClassLoader.getSystemResourceAsStream(fileName); + + if (inputStream == null) { + throw new IllegalArgumentException( + "InputStream cannot be null. File not found: " + fileName); + } + + puzzle.importPuzzle(inputStream); Tree tree = puzzle.getTree(); TreeNode rootNode = tree.getRootNode(); Board board = rootNode.getBoard().copy(); diff --git a/src/test/java/puzzles/lightup/rules/FinishWithEmptyDirectRuleTest.java b/src/test/java/puzzles/lightup/rules/FinishWithEmptyDirectRuleDirectRuleTest.java similarity index 98% rename from src/test/java/puzzles/lightup/rules/FinishWithEmptyDirectRuleTest.java rename to src/test/java/puzzles/lightup/rules/FinishWithEmptyDirectRuleDirectRuleTest.java index 079641ee7..f80db6625 100644 --- a/src/test/java/puzzles/lightup/rules/FinishWithEmptyDirectRuleTest.java +++ b/src/test/java/puzzles/lightup/rules/FinishWithEmptyDirectRuleDirectRuleTest.java @@ -13,7 +13,7 @@ import org.junit.BeforeClass; import org.junit.Test; -public class FinishWithEmptyDirectRuleTest { +public class FinishWithEmptyDirectRuleDirectRuleTest { private static final FinishWithEmptyDirectRule RULE = new FinishWithEmptyDirectRule(); private static LightUp lightUp; diff --git a/src/test/java/puzzles/minesweeper/FinishWithEmptyDirectRuleTest.java b/src/test/java/puzzles/minesweeper/FinishWithEmptyDirectRuleTest.java new file mode 100644 index 000000000..6316907f1 --- /dev/null +++ b/src/test/java/puzzles/minesweeper/FinishWithEmptyDirectRuleTest.java @@ -0,0 +1,272 @@ +package puzzles.minesweeper; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.minesweeper.Minesweeper; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperBoard; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperCell; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperTileData; +import edu.rpi.legup.puzzle.minesweeper.rules.FinishWithEmptyDirectRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.awt.*; + +public class FinishWithEmptyDirectRuleTest { + + public static final FinishWithEmptyDirectRule RULE = new FinishWithEmptyDirectRule(); + private static Minesweeper minesweeper; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + minesweeper = new Minesweeper(); + } + + // tests the finish with empty direct rule is many different cases + + @Test + public void FinishWithEmptyDirectRule_OneUnsetOneEmptyOneNumberTest1() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test8.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(1, 0); + cell1.setData(MinesweeperTileData.empty()); + + board.addModifiedData(cell1); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void FinishWithEmptyDirectRule_FiveUnsetOneEmptyOneNumberTest2() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test9.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(1, 0); + cell1.setData(MinesweeperTileData.empty()); + + board.addModifiedData(cell1); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void FinishWithEmptyDirectRule_NineUnsetOneEmptyZeroNumbersTest3() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(1, 1); + cell1.setData(MinesweeperTileData.empty()); + + board.addModifiedData(cell1); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void FinishWithEmptyDirectRule_NineUnsetNineEmptyZeroNumbersTest4() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(0, 0); + cell1.setData(MinesweeperTileData.empty()); + MinesweeperCell cell2 = board.getCell(1, 0); + cell2.setData(MinesweeperTileData.empty()); + MinesweeperCell cell3 = board.getCell(2, 0); + cell3.setData(MinesweeperTileData.empty()); + MinesweeperCell cell4 = board.getCell(0, 1); + cell4.setData(MinesweeperTileData.empty()); + MinesweeperCell cell5 = board.getCell(1, 1); + cell5.setData(MinesweeperTileData.empty()); + MinesweeperCell cell6 = board.getCell(2, 1); + cell6.setData(MinesweeperTileData.empty()); + MinesweeperCell cell7 = board.getCell(0, 2); + cell7.setData(MinesweeperTileData.empty()); + MinesweeperCell cell8 = board.getCell(1, 2); + cell8.setData(MinesweeperTileData.empty()); + MinesweeperCell cell9 = board.getCell(2, 2); + cell9.setData(MinesweeperTileData.empty()); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + board.addModifiedData(cell3); + board.addModifiedData(cell4); + board.addModifiedData(cell5); + board.addModifiedData(cell6); + board.addModifiedData(cell7); + board.addModifiedData(cell8); + board.addModifiedData(cell9); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) + || point.equals(cell3.getLocation()) || point.equals(cell4.getLocation()) + || point.equals(cell5.getLocation()) || point.equals(cell6.getLocation()) + || point.equals(cell7.getLocation()) || point.equals(cell8.getLocation()) + || point.equals(cell9.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void FinishWithEmptyDirectRule_OneUnsetOneEmptyThreeNumbersTest5() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test10.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(1, 2); + cell1.setData(MinesweeperTileData.empty()); + + board.addModifiedData(cell1); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void FinishWithEmptyDirectRule_OneUnsetOneEmptyThreeNumbersTest6() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test11.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(1, 1); + cell1.setData(MinesweeperTileData.empty()); + + board.addModifiedData(cell1); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void FinishWithEmptyDirectRule_FiveUnsetTwoEmptyTwoNumbersTest7() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test12.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(0, 2); + cell1.setData(MinesweeperTileData.empty()); + MinesweeperCell cell2 = board.getCell(2, 2); + cell2.setData(MinesweeperTileData.empty()); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } +} diff --git a/src/test/java/puzzles/minesweeper/FinishWithMinesDirectRuleTest.java b/src/test/java/puzzles/minesweeper/FinishWithMinesDirectRuleTest.java new file mode 100644 index 000000000..efa77e0a9 --- /dev/null +++ b/src/test/java/puzzles/minesweeper/FinishWithMinesDirectRuleTest.java @@ -0,0 +1,276 @@ +package puzzles.minesweeper; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.minesweeper.*; +import edu.rpi.legup.puzzle.minesweeper.rules.FinishWithMinesDirectRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.awt.*; + +public class FinishWithMinesDirectRuleTest { + + public static final FinishWithMinesDirectRule RULE = new FinishWithMinesDirectRule(); + private static Minesweeper minesweeper; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + minesweeper = new Minesweeper(); + } + + // tests the finish with mines direct rule is many different cases + + @Test + public void FinishWithMinesDirectRule_OneUnsetOneBombOneNumberTest1() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test2.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(1, 0); + cell1.setData(MinesweeperTileData.mine()); + + board.addModifiedData(cell1); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void FinishWithMinesDirectRule_FourUnsetOneBombOneNumberTest2() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test3.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(0, 1); + cell1.setData(MinesweeperTileData.mine()); + + board.addModifiedData(cell1); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + + @Test + public void FinishWithMinesDirectRule_FourUnsetFourBombsOneNumberTest3() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test3.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(1, 0); + cell1.setData(MinesweeperTileData.mine()); + MinesweeperCell cell2 = board.getCell(2, 0); + cell2.setData(MinesweeperTileData.mine()); + MinesweeperCell cell3 = board.getCell(0, 1); + cell3.setData(MinesweeperTileData.mine()); + MinesweeperCell cell4 = board.getCell(0, 2); + cell4.setData(MinesweeperTileData.mine()); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + board.addModifiedData(cell3); + board.addModifiedData(cell4); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) + || point.equals(cell3.getLocation()) || point.equals(cell4.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void FinishWithMinesDirectRule_EightUnsetEightBombsOneNumberTest4() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test4.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(0, 0); + cell1.setData(MinesweeperTileData.mine()); + MinesweeperCell cell2 = board.getCell(1, 0); + cell2.setData(MinesweeperTileData.mine()); + MinesweeperCell cell3 = board.getCell(2, 0); + cell3.setData(MinesweeperTileData.mine()); + MinesweeperCell cell4 = board.getCell(0, 1); + cell4.setData(MinesweeperTileData.mine()); + MinesweeperCell cell5 = board.getCell(2, 1); + cell5.setData(MinesweeperTileData.mine()); + MinesweeperCell cell6 = board.getCell(0, 2); + cell6.setData(MinesweeperTileData.mine()); + MinesweeperCell cell7 = board.getCell(1, 2); + cell7.setData(MinesweeperTileData.mine()); + MinesweeperCell cell8 = board.getCell(2, 2); + cell8.setData(MinesweeperTileData.mine()); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + board.addModifiedData(cell3); + board.addModifiedData(cell4); + board.addModifiedData(cell5); + board.addModifiedData(cell6); + board.addModifiedData(cell7); + board.addModifiedData(cell8); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) + || point.equals(cell3.getLocation()) || point.equals(cell4.getLocation()) + || point.equals(cell5.getLocation()) || point.equals(cell6.getLocation()) + || point.equals(cell7.getLocation()) || point.equals(cell8.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void FinishWithMinesDirectRule_OneUnsetOneBombFourNumbersTest5() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test5.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(1, 1); + cell1.setData(MinesweeperTileData.mine()); + + board.addModifiedData(cell1); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void FinishWithMinesDirectRule_TwoUnsetTwoBombsTwoNumbersTest6() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test6.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(0, 1); + cell1.setData(MinesweeperTileData.mine()); + MinesweeperCell cell2 = board.getCell(2, 1); + cell2.setData(MinesweeperTileData.mine()); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void FinishWithMinesDirectRule_ThreeUnsetOneBombTwoNumbersTest7() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test7.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(1, 1); + cell1.setData(MinesweeperTileData.mine()); + + board.addModifiedData(cell1); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } +} diff --git a/src/test/java/puzzles/minesweeper/IsolateMineContradictionRuleTest.java b/src/test/java/puzzles/minesweeper/IsolateMineContradictionRuleTest.java new file mode 100644 index 000000000..3a185feba --- /dev/null +++ b/src/test/java/puzzles/minesweeper/IsolateMineContradictionRuleTest.java @@ -0,0 +1,95 @@ +package puzzles.minesweeper; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.minesweeper.Minesweeper; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperBoard; +import edu.rpi.legup.puzzle.minesweeper.rules.IsolateMineContradictionRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class IsolateMineContradictionRuleTest { + private static final IsolateMineContradictionRule RULE = new IsolateMineContradictionRule(); + private static Minesweeper minesweeper; + + @BeforeClass + public static void setUp() { + minesweeper = new Minesweeper(); + } + + @Test + // tests a 3x3 board with a mine in the center surrounded by empty cells + public void IsolateMineTest1() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/minesweeper/rules/IsolateMine1.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + // confirm it is impossible to satisfy up the center square + Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(1, 1))); + + // every square except the center + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 1))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 1))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 2))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 2))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 2))); + } + + @Test + // tests a 3x3 board with a mine in the center surrounded by unset cells + public void IsolateMineTest2() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/minesweeper/rules/IsolateMine2.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + // confirm it is impossible to satisfy up the center square + Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(1, 1))); + + // every square except the center + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 1))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 1))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 2))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 2))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 2))); + } + + @Test + // tests a 3x3 board full of mines only + public void IsolateMineTest3() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/minesweeper/rules/IsolateMine3.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + // confirm it is impossible to satisfy any of the squares + Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(1, 1))); + Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(0, 0))); + Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(1, 0))); + Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(2, 0))); + Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(0, 1))); + Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(2, 1))); + Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(0, 2))); + Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(1, 2))); + Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(2, 2))); + } +} diff --git a/src/test/java/puzzles/minesweeper/MineOrEmptyCaseRuleTest.java b/src/test/java/puzzles/minesweeper/MineOrEmptyCaseRuleTest.java new file mode 100644 index 000000000..c478ded29 --- /dev/null +++ b/src/test/java/puzzles/minesweeper/MineOrEmptyCaseRuleTest.java @@ -0,0 +1,75 @@ +package puzzles.minesweeper; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.minesweeper.Minesweeper; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperBoard; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperCell; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperTileData; +import edu.rpi.legup.puzzle.minesweeper.rules.MineOrEmptyCaseRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.awt.*; +import java.util.ArrayList; + +public class MineOrEmptyCaseRuleTest { + + private static final MineOrEmptyCaseRule RULE = new MineOrEmptyCaseRule(); + private static Minesweeper minesweeper; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + minesweeper = new Minesweeper(); + } + + /** + * Tests the Mine or Empty case rule by ensuring that it results in two children, that contain + * a modified cell that is either mine or empty + */ + @Test + public void MineOrEmptyCaseRuleTest1() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/minesweeper/rules/MineOrEmpty.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + MinesweeperCell cell = board.getCell(0, 0); + ArrayList cases = RULE.getCases(board, cell); + + Assert.assertEquals(2, cases.size()); + + MinesweeperBoard caseBoard = (MinesweeperBoard) cases.get(0); + MinesweeperBoard caseBoard2 = (MinesweeperBoard) cases.get(1); + + MinesweeperTileData board1Type = caseBoard.getCell(0, 0).getData(); + MinesweeperTileData board2Type = caseBoard2.getCell(0, 0).getData(); + + Assert.assertTrue( + ((board1Type.equals(MinesweeperTileData.mine()) || board1Type.equals(MinesweeperTileData.empty()))) + && (board2Type.equals(MinesweeperTileData.mine()) || board2Type.equals(MinesweeperTileData.empty()))); + Assert.assertFalse(board1Type.equals(board2Type)); + + Assert.assertEquals(caseBoard.getHeight(), caseBoard2.getHeight(), board.getHeight()); + Assert.assertEquals(caseBoard.getWidth(), caseBoard2.getWidth(), board.getWidth()); + + for (int i = 0; i < caseBoard.getHeight(); i++) { + for (int k = 0; k < caseBoard.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(caseBoard.getCell(k, i).getLocation())) { + continue; + } + Assert.assertTrue(caseBoard.getCell(k, i).equals(caseBoard2.getCell(k, i))); + } + } + } +} diff --git a/src/test/java/puzzles/minesweeper/NonTouchingSharedEmptyDirectRuleTest.java b/src/test/java/puzzles/minesweeper/NonTouchingSharedEmptyDirectRuleTest.java new file mode 100644 index 000000000..19b06f52b --- /dev/null +++ b/src/test/java/puzzles/minesweeper/NonTouchingSharedEmptyDirectRuleTest.java @@ -0,0 +1,169 @@ +package puzzles.minesweeper; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.minesweeper.Minesweeper; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperBoard; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperCell; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperTileData; +import edu.rpi.legup.puzzle.minesweeper.rules.NonTouchingSharedEmptyDirectRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.awt.*; + +public class NonTouchingSharedEmptyDirectRuleTest { + + public static final NonTouchingSharedEmptyDirectRule RULE = new NonTouchingSharedEmptyDirectRule(); + private static Minesweeper minesweeper; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + minesweeper = new Minesweeper(); + } + + // Horizontal/vertical tests means the adjacent number cells are adjacent horizontally/vertically + + @Test + public void NonTouchingSharedEmptyDirectRule_HorizontalTest1() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test15.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(2, 1); + cell1.setData(MinesweeperTileData.empty()); + + board.addModifiedData(cell1); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void NonTouchingSharedEmptyDirectRule_HorizontalTest2() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/4x4test4.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(0, 0); + cell1.setData(MinesweeperTileData.empty()); + MinesweeperCell cell2 = board.getCell(0, 1); + cell2.setData(MinesweeperTileData.empty()); + MinesweeperCell cell3 = board.getCell(0, 2); + cell3.setData(MinesweeperTileData.empty()); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + board.addModifiedData(cell3); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) + || point.equals(cell3.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void NonTouchingSharedEmptyDirectRule_VerticalTest1() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/4x4test5.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(1, 0); + cell1.setData(MinesweeperTileData.empty()); + MinesweeperCell cell2 = board.getCell(2, 0); + cell2.setData(MinesweeperTileData.empty()); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void NonTouchingSharedEmptyDirectRule_VerticalTest2() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/4x4test6.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(1, 3); + cell1.setData(MinesweeperTileData.empty()); + MinesweeperCell cell2 = board.getCell(2, 3); + cell2.setData(MinesweeperTileData.empty()); + MinesweeperCell cell3 = board.getCell(3, 3); + cell3.setData(MinesweeperTileData.empty()); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + board.addModifiedData(cell3); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) + || point.equals(cell3.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + +} diff --git a/src/test/java/puzzles/minesweeper/NonTouchingSharedMineDirectRuleTest.java b/src/test/java/puzzles/minesweeper/NonTouchingSharedMineDirectRuleTest.java new file mode 100644 index 000000000..f786e7214 --- /dev/null +++ b/src/test/java/puzzles/minesweeper/NonTouchingSharedMineDirectRuleTest.java @@ -0,0 +1,191 @@ +package puzzles.minesweeper; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.minesweeper.Minesweeper; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperBoard; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperCell; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperTileData; +import edu.rpi.legup.puzzle.minesweeper.rules.NonTouchingSharedMineDirectRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.awt.*; + +public class NonTouchingSharedMineDirectRuleTest { + + public static final NonTouchingSharedMineDirectRule RULE = new NonTouchingSharedMineDirectRule(); + private static Minesweeper minesweeper; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + minesweeper = new Minesweeper(); + } + + // Horizontal/vertical tests means the adjacent number cells are adjacent horizontally/vertically + + @Test + public void NonTouchingSharedMineDirectRule_HorizontalTest1() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test13.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(2, 1); + cell1.setData(MinesweeperTileData.mine()); + + board.addModifiedData(cell1); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void NonTouchingSharedMineDirectRule_HorizontalTest2() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/4x4test1.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(3, 1); + cell1.setData(MinesweeperTileData.mine()); + + board.addModifiedData(cell1); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void NonTouchingSharedMineDirectRule_HorizontalTest3() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/4x4test2.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(0, 0); + cell1.setData(MinesweeperTileData.mine()); + MinesweeperCell cell2 = board.getCell(0, 2); + cell2.setData(MinesweeperTileData.mine()); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void NonTouchingSharedMineDirectRule_VerticalTest1() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/3x3test14.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(1, 0); + cell1.setData(MinesweeperTileData.mine()); + + board.addModifiedData(cell1); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } + + @Test + public void NonTouchingSharedMineDirectRule_VerticalTest2() + throws InvalidFileFormatException { + TestUtilities.importTestBoard("puzzles/minesweeper/rules/4x4test3.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + MinesweeperCell cell1 = board.getCell(1, 3); + cell1.setData(MinesweeperTileData.mine()); + MinesweeperCell cell2 = board.getCell(2, 3); + cell2.setData(MinesweeperTileData.mine()); + MinesweeperCell cell3 = board.getCell(3, 3); + cell3.setData(MinesweeperTileData.mine()); + + board.addModifiedData(cell1); + board.addModifiedData(cell2); + board.addModifiedData(cell3); + + Assert.assertNull(RULE.checkRule(transition)); + + for (int i = 0; i < board.getHeight(); i++) { + for (int k = 0; k < board.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(cell1.getLocation()) || point.equals(cell2.getLocation()) + || point.equals(cell3.getLocation())) { + Assert.assertNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + else { + Assert.assertNotNull(RULE.checkRuleAt(transition, board.getCell(k, i))); + } + } + } + } +} diff --git a/src/test/java/puzzles/minesweeper/SatisfyNumberCaseRuleTest.java b/src/test/java/puzzles/minesweeper/SatisfyNumberCaseRuleTest.java new file mode 100644 index 000000000..cbae2d053 --- /dev/null +++ b/src/test/java/puzzles/minesweeper/SatisfyNumberCaseRuleTest.java @@ -0,0 +1,100 @@ +package puzzles.minesweeper; + +import edu.rpi.legup.model.gameboard.Board; +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.minesweeper.Minesweeper; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperBoard; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperCell; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperTileData; +import edu.rpi.legup.puzzle.minesweeper.rules.SatisfyNumberCaseRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.MockGameBoardFacade; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.awt.*; +import java.util.ArrayList; + +public class SatisfyNumberCaseRuleTest { + + private static final SatisfyNumberCaseRule RULE = new SatisfyNumberCaseRule(); + private static Minesweeper minesweeper; + + @BeforeClass + public static void setUp() { + MockGameBoardFacade.getInstance(); + minesweeper = new Minesweeper(); + } + + /** + * Tests the Satisfy Number case rule by ensuring that it results in all possibilities + * for the number. This case tests a number 2 with three unset cells around it, so each + * case must replace the unset tiles with a different arrangement of two bombs and one empty. + */ + @Test + public void SatisfyNumberCaseRuleTest1() + throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/minesweeper/rules/SatisfyNumber.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + MinesweeperCell cell = board.getCell(1, 1); + ArrayList cases = RULE.getCases(board, cell); + + Assert.assertEquals(3, cases.size()); + + MinesweeperBoard caseBoard = (MinesweeperBoard) cases.get(0); + MinesweeperBoard caseBoard2 = (MinesweeperBoard) cases.get(1); + MinesweeperBoard caseBoard3 = (MinesweeperBoard) cases.get(2); + + MinesweeperTileData board1Tile1 = caseBoard.getCell(0, 1).getData(); + MinesweeperTileData board1Tile2 = caseBoard.getCell(2, 1).getData(); + MinesweeperTileData board1Tile3 = caseBoard.getCell(1, 2).getData(); + MinesweeperTileData board2Tile1 = caseBoard2.getCell(0, 1).getData(); + MinesweeperTileData board2Tile2 = caseBoard2.getCell(2, 1).getData(); + MinesweeperTileData board2Tile3 = caseBoard2.getCell(1, 2).getData(); + MinesweeperTileData board3Tile1 = caseBoard3.getCell(0, 1).getData(); + MinesweeperTileData board3Tile2 = caseBoard3.getCell(2, 1).getData(); + MinesweeperTileData board3Tile3 = caseBoard3.getCell(1, 2).getData(); + + Assert.assertTrue((board1Tile1.equals(MinesweeperTileData.mine()) && board1Tile2.equals(MinesweeperTileData.mine()) + && board1Tile3.equals(MinesweeperTileData.empty())) + || (board1Tile1.equals(MinesweeperTileData.mine()) && board1Tile2.equals(MinesweeperTileData.empty()) && board1Tile3.equals(MinesweeperTileData.mine())) + || ((board1Tile1.equals(MinesweeperTileData.empty()) && board1Tile2.equals(MinesweeperTileData.mine()) && board1Tile3.equals(MinesweeperTileData.mine())))); + + Assert.assertTrue((board2Tile1.equals(MinesweeperTileData.mine()) && board2Tile2.equals(MinesweeperTileData.mine()) + && board2Tile3.equals(MinesweeperTileData.empty())) + || (board2Tile1.equals(MinesweeperTileData.mine()) && board2Tile2.equals(MinesweeperTileData.empty()) && board2Tile3.equals(MinesweeperTileData.mine())) + || ((board2Tile1.equals(MinesweeperTileData.empty()) && board2Tile2.equals(MinesweeperTileData.mine()) && board2Tile3.equals(MinesweeperTileData.mine())))); + + Assert.assertTrue((board3Tile1.equals(MinesweeperTileData.mine()) && board3Tile2.equals(MinesweeperTileData.mine()) + && board3Tile3.equals(MinesweeperTileData.empty())) + || (board3Tile1.equals(MinesweeperTileData.mine()) && board3Tile2.equals(MinesweeperTileData.empty()) && board3Tile3.equals(MinesweeperTileData.mine())) + || ((board3Tile1.equals(MinesweeperTileData.empty()) && board3Tile2.equals(MinesweeperTileData.mine()) && board3Tile3.equals(MinesweeperTileData.mine())))); + + Assert.assertFalse(board1Tile1.equals(board2Tile1) && board1Tile1.equals(board3Tile1)); + Assert.assertFalse(board1Tile2.equals(board2Tile2) && board1Tile2.equals(board3Tile2)); + Assert.assertFalse(board1Tile3.equals(board2Tile3) && board1Tile3.equals(board3Tile3)); + + Assert.assertEquals(caseBoard.getHeight(), caseBoard2.getHeight(), caseBoard3.getHeight()); + Assert.assertEquals(caseBoard.getHeight(), board.getHeight()); + Assert.assertEquals(caseBoard.getWidth(), caseBoard2.getWidth(), caseBoard3.getWidth()); + Assert.assertEquals(caseBoard.getWidth(), board.getWidth()); + + for (int i = 0; i < caseBoard.getHeight(); i++) { + for (int k = 0; k < caseBoard.getWidth(); k++) { + Point point = new Point(k, i); + if (point.equals(caseBoard.getCell(k, i).getLocation())) { + continue; + } + Assert.assertTrue(caseBoard.getCell(k, i).equals(caseBoard2.getCell(k, i))); + } + } + } +} diff --git a/src/test/java/puzzles/minesweeper/TooFewMinesContradictionRuleTest.java b/src/test/java/puzzles/minesweeper/TooFewMinesContradictionRuleTest.java new file mode 100644 index 000000000..f28d909f4 --- /dev/null +++ b/src/test/java/puzzles/minesweeper/TooFewMinesContradictionRuleTest.java @@ -0,0 +1,72 @@ +package puzzles.minesweeper; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.minesweeper.Minesweeper; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperBoard; +import edu.rpi.legup.puzzle.minesweeper.rules.TooFewMinesContradictionRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TooFewMinesContradictionRuleTest { + private static final TooFewMinesContradictionRule RULE = new TooFewMinesContradictionRule(); + private static Minesweeper minesweeper; + + @BeforeClass + public static void setUp() { + minesweeper = new Minesweeper(); + } + + @Test + // tests a 3x3 board with a 3 in the center surrounded by 2 mines and 6 empty + public void TooManyMinesTest1() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/minesweeper/rules/TooFewMines1.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + // confirm it is impossible to satisfy up the center square + Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(1, 1))); + + // every square except the center + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 1))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 1))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 2))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 2))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 1))); + } + + @Test + // tests a 3x3 board with a 3 in the center surrounded by 2 unset and 6 empty + public void TooManyMinesTest2() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/minesweeper/rules/TooFewMines2.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + // confirm it is impossible to satisfy up the center square + Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(1, 1))); + + // every square except the center + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 1))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 1))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 2))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 2))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 1))); + } +} diff --git a/src/test/java/puzzles/minesweeper/TooManyMinesContradictionRuleTest.java b/src/test/java/puzzles/minesweeper/TooManyMinesContradictionRuleTest.java new file mode 100644 index 000000000..c0715f618 --- /dev/null +++ b/src/test/java/puzzles/minesweeper/TooManyMinesContradictionRuleTest.java @@ -0,0 +1,47 @@ +package puzzles.minesweeper; + +import edu.rpi.legup.model.tree.TreeNode; +import edu.rpi.legup.model.tree.TreeTransition; +import edu.rpi.legup.puzzle.minesweeper.Minesweeper; +import edu.rpi.legup.puzzle.minesweeper.MinesweeperBoard; +import edu.rpi.legup.puzzle.minesweeper.rules.TooManyMinesContradictionRule; +import edu.rpi.legup.save.InvalidFileFormatException; +import legup.TestUtilities; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class TooManyMinesContradictionRuleTest { + private static final TooManyMinesContradictionRule RULE = new TooManyMinesContradictionRule(); + private static Minesweeper minesweeper; + + @BeforeClass + public static void setUp() { + minesweeper = new Minesweeper(); + } + + @Test + // tests a 3x3 board with a 3 in the center and 4 surrounding mines + public void TooManyMinesTest1() throws InvalidFileFormatException { + TestUtilities.importTestBoard( + "puzzles/minesweeper/rules/TooManyMines.txt", minesweeper); + TreeNode rootNode = minesweeper.getTree().getRootNode(); + TreeTransition transition = rootNode.getChildren().get(0); + transition.setRule(RULE); + + MinesweeperBoard board = (MinesweeperBoard) transition.getBoard(); + + // confirm it is impossible to satisfy up the center square + Assert.assertNull(RULE.checkContradictionAt(board, board.getCell(1, 1))); + + // every square except the center + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 0))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 1))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 1))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(0, 2))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(1, 2))); + Assert.assertNotNull(RULE.checkContradictionAt(board, board.getCell(2, 2))); + } +} diff --git a/src/test/java/puzzles/nurikabe/rules/FinishRoomCaseRuleTest.java b/src/test/java/puzzles/nurikabe/rules/FinishRoomCaseRuleTest.java index 5f5b6d35a..32a440d13 100644 --- a/src/test/java/puzzles/nurikabe/rules/FinishRoomCaseRuleTest.java +++ b/src/test/java/puzzles/nurikabe/rules/FinishRoomCaseRuleTest.java @@ -27,10 +27,7 @@ public static void setUp() { nurikabe = new Nurikabe(); } - /** - * Tests the Finish Room case rule by ensuring that it results in 5 or less children, that - * contain a modified cell that is white - */ + /** Tests the Finish Room case rule by ensuring it produces the correct number of children */ @Test public void FinishRoomCaseRule_FinishRoomCaseRuleBaseTest() throws InvalidFileFormatException { TestUtilities.importTestBoard( @@ -81,34 +78,6 @@ public void FinishRoomCaseRule_FinishRoomCaseRuleBaseTest() throws InvalidFileFo NurikabeCell cell2 = board.getCell(4, 2); ArrayList cases2 = RULE.getCases(board, cell2); - 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(); - + Assert.assertEquals(9, cases2.size()); } } diff --git a/src/test/java/puzzles/sudoku/rules/LastNumberForCellDirectRuleRegionTest.java b/src/test/java/puzzles/sudoku/rules/LastNumberForCellDirectRuleRegionTest.java new file mode 100644 index 000000000..f27f38d8a --- /dev/null +++ b/src/test/java/puzzles/sudoku/rules/LastNumberForCellDirectRuleRegionTest.java @@ -0,0 +1,100 @@ +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 new file mode 100644 index 000000000..704167a29 --- /dev/null +++ b/src/test/java/puzzles/sudoku/rules/RepeatedNumberContradictionRuleTest.java @@ -0,0 +1,88 @@ +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 new file mode 100644 index 000000000..026742fea --- /dev/null +++ b/src/test/resources/puzzles/binary/rules/SurroundPairDirectRule/SurroundTwoZerosWithTwoOnes @@ -0,0 +1,10 @@ + + + + + + + + + + \ 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 new file mode 100644 index 000000000..e69de29bb diff --git a/src/test/resources/puzzles/minesweeper/rules/3x3test b/src/test/resources/puzzles/minesweeper/rules/3x3test new file mode 100644 index 000000000..2bf4f5c3b --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/3x3test @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/3x3test10.txt b/src/test/resources/puzzles/minesweeper/rules/3x3test10.txt new file mode 100644 index 000000000..dcfae59b0 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/3x3test10.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/3x3test11.txt b/src/test/resources/puzzles/minesweeper/rules/3x3test11.txt new file mode 100644 index 000000000..c9ac90ef7 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/3x3test11.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/3x3test12.txt b/src/test/resources/puzzles/minesweeper/rules/3x3test12.txt new file mode 100644 index 000000000..946ed7502 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/3x3test12.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/3x3test13.txt b/src/test/resources/puzzles/minesweeper/rules/3x3test13.txt new file mode 100644 index 000000000..0f3761e47 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/3x3test13.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/3x3test14.txt b/src/test/resources/puzzles/minesweeper/rules/3x3test14.txt new file mode 100644 index 000000000..1f38acd3d --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/3x3test14.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/3x3test15.txt b/src/test/resources/puzzles/minesweeper/rules/3x3test15.txt new file mode 100644 index 000000000..29fd5d0ea --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/3x3test15.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/3x3test2.txt b/src/test/resources/puzzles/minesweeper/rules/3x3test2.txt new file mode 100644 index 000000000..86640ebfa --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/3x3test2.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/3x3test3.txt b/src/test/resources/puzzles/minesweeper/rules/3x3test3.txt new file mode 100644 index 000000000..cbe32bc31 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/3x3test3.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/3x3test4.txt b/src/test/resources/puzzles/minesweeper/rules/3x3test4.txt new file mode 100644 index 000000000..12bbd62c9 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/3x3test4.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/3x3test5.txt b/src/test/resources/puzzles/minesweeper/rules/3x3test5.txt new file mode 100644 index 000000000..0cfa02d64 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/3x3test5.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/3x3test6.txt b/src/test/resources/puzzles/minesweeper/rules/3x3test6.txt new file mode 100644 index 000000000..8f3975aa1 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/3x3test6.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/3x3test7.txt b/src/test/resources/puzzles/minesweeper/rules/3x3test7.txt new file mode 100644 index 000000000..a0c611136 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/3x3test7.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/3x3test8.txt b/src/test/resources/puzzles/minesweeper/rules/3x3test8.txt new file mode 100644 index 000000000..c420ddad2 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/3x3test8.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/3x3test9.txt b/src/test/resources/puzzles/minesweeper/rules/3x3test9.txt new file mode 100644 index 000000000..a6f029af0 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/3x3test9.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/4x4test1.txt b/src/test/resources/puzzles/minesweeper/rules/4x4test1.txt new file mode 100644 index 000000000..3138dde84 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/4x4test1.txt @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/4x4test2.txt b/src/test/resources/puzzles/minesweeper/rules/4x4test2.txt new file mode 100644 index 000000000..56aabc927 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/4x4test2.txt @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/4x4test3.txt b/src/test/resources/puzzles/minesweeper/rules/4x4test3.txt new file mode 100644 index 000000000..90a7384c9 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/4x4test3.txt @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/4x4test4.txt b/src/test/resources/puzzles/minesweeper/rules/4x4test4.txt new file mode 100644 index 000000000..caa8a1e41 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/4x4test4.txt @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/4x4test5.txt b/src/test/resources/puzzles/minesweeper/rules/4x4test5.txt new file mode 100644 index 000000000..34b0816f9 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/4x4test5.txt @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/4x4test6.txt b/src/test/resources/puzzles/minesweeper/rules/4x4test6.txt new file mode 100644 index 000000000..0c5fdb320 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/4x4test6.txt @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/IsolateMine1.txt b/src/test/resources/puzzles/minesweeper/rules/IsolateMine1.txt new file mode 100644 index 000000000..997cf24c1 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/IsolateMine1.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/IsolateMine2.txt b/src/test/resources/puzzles/minesweeper/rules/IsolateMine2.txt new file mode 100644 index 000000000..bd87398ee --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/IsolateMine2.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/IsolateMine3.txt b/src/test/resources/puzzles/minesweeper/rules/IsolateMine3.txt new file mode 100644 index 000000000..9aea60eed --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/IsolateMine3.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/MineOrEmpty.txt b/src/test/resources/puzzles/minesweeper/rules/MineOrEmpty.txt new file mode 100644 index 000000000..6e2b88422 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/MineOrEmpty.txt @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/SatisfyNumber.txt b/src/test/resources/puzzles/minesweeper/rules/SatisfyNumber.txt new file mode 100644 index 000000000..85795ddc2 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/SatisfyNumber.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/TooFewMines1.txt b/src/test/resources/puzzles/minesweeper/rules/TooFewMines1.txt new file mode 100644 index 000000000..a817ad6bb --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/TooFewMines1.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/TooFewMines2.txt b/src/test/resources/puzzles/minesweeper/rules/TooFewMines2.txt new file mode 100644 index 000000000..b1bade015 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/TooFewMines2.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/minesweeper/rules/TooManyMines.txt b/src/test/resources/puzzles/minesweeper/rules/TooManyMines.txt new file mode 100644 index 000000000..bba206653 --- /dev/null +++ b/src/test/resources/puzzles/minesweeper/rules/TooManyMines.txt @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/test/resources/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRule/SurroundTwoZerosWithTwoOnes b/src/test/resources/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRule/SurroundTwoZerosWithTwoOnes new file mode 100644 index 000000000..026742fea --- /dev/null +++ b/src/test/resources/puzzles/nurikabe/rules/BlackBetweenRegionsDirectRule/SurroundTwoZerosWithTwoOnes @@ -0,0 +1,10 @@ + + + + + + + + + + \ 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 deleted file mode 100644 index ddcc4dc9a..000000000 --- a/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/ColumnBlackout +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 deleted file mode 100644 index f2a5b42d9..000000000 --- a/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RegionBlackout +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 deleted file mode 100644 index f2a5b42d9..000000000 --- a/src/test/resources/puzzles/starbattle.rules/BlackoutDirectRule/RowBlackout +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ 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 new file mode 100644 index 000000000..4d3c57225 --- /dev/null +++ b/src/test/resources/puzzles/sudoku/rules/LastCellForNumberDirectRule/CorneredRegion @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullMixed b/src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullMixed new file mode 100644 index 000000000..55b501fec --- /dev/null +++ b/src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullMixed @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ 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 new file mode 100644 index 000000000..58fd02162 --- /dev/null +++ b/src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRegion @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRow b/src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRow new file mode 100644 index 000000000..07e502ed9 --- /dev/null +++ b/src/test/resources/puzzles/sudoku/rules/LastNumberForCellDirectRule/FullRow @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/BlankBoard4 b/src/test/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/BlankBoard4 new file mode 100644 index 000000000..abaa0ba6b --- /dev/null +++ b/src/test/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/BlankBoard4 @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/src/test/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/BlankBoard7 b/src/test/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/BlankBoard7 new file mode 100644 index 000000000..5692dea64 --- /dev/null +++ b/src/test/resources/puzzles/sudoku/rules/RepeatedNumberContradictionRule/BlankBoard7 @@ -0,0 +1,11 @@ + + + + + + + + + + +