diff --git a/.gitignore b/.gitignore
index baee37e51..bafea84a3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -92,4 +92,5 @@ gradle-app.setting
gradle/wrapper/gradle-wrapper.properties
# Visual Studio Code configs
-.vscode/*
\ No newline at end of file
+.vscode/*
+src/test/java/legup/TestRunner.java
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/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/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/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/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/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..f5646a3c0 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
@@ -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..9f6f465dd 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
@@ -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..86d2c8dac
--- /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/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..714395cab
--- /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/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..e768405fd
--- /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/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 75%
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..a4646c45e 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,12 +8,12 @@
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");
}
@@ -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/PossibleCellsForNumberColumnCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberColumnCaseRule.java
new file mode 100644
index 000000000..9c4045fbb
--- /dev/null
+++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberColumnCaseRule.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 PossibleCellsForNumberColumnCaseRule extends CaseRule {
+
+ // 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-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/possible_cells_number_column.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.getModelColumnNumbers(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.COLUMN);
+ }
+
+ /**
+ * 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.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);
+ }
+ }
+ 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..43f27e888
--- /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/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/PossibleCellCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberRowCaseRule.java
similarity index 53%
rename from src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellCaseRule.java
rename to src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberRowCaseRule.java
index fb6da62d4..5308b84fa 100644
--- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellCaseRule.java
+++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleCellsForNumberRowCaseRule.java
@@ -5,19 +5,27 @@
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.HashSet;
import java.util.Set;
-public class PossibleCellCaseRule extends CaseRule {
- public PossibleCellCaseRule() {
+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-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");
+ "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/possible_cells_number_row.png");
}
/**
@@ -48,11 +56,11 @@ public String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleElem
@Override
public CaseBoard getCaseBoard(Board board) {
SudokuBoard sudokuBoard = (SudokuBoard) board.copy();
+ lagBoard = (SudokuBoard) sudokuBoard.copy();
CaseBoard caseBoard = new CaseBoard(sudokuBoard, this);
for (PuzzleElement puzzleElement : sudokuBoard.getPuzzleElements()) {
- if (((SudokuCell) puzzleElement).getData() == 0) {
- caseBoard.addPickableElement(puzzleElement);
- }
+ puzzleElement.setData(model.getModelRowNumbers(puzzleElement.getIndex()));
+ caseBoard.addPickableElement(puzzleElement);
}
return caseBoard;
}
@@ -66,45 +74,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());
- }
- }
+ return getCases(board, puzzleElement, 1, GroupType.ROW);
+ }
- int rowNum = cell.getLocation().y;
- for (SudokuCell c : sudokuBoard.getRegion(rowNum)) {
- 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<>();
+ SudokuBoard sudokuBoard = lagBoard;
+ SudokuCell sourceCell = (SudokuCell) puzzleElement;
- int colNum = cell.getLocation().x;
- for (SudokuCell c : sudokuBoard.getRegion(colNum)) {
- if (c.getData().equals(c.getData())) {
- possibleValue.remove(c.getData());
+ 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);
}
}
-
- for (Integer i : possibleValue) {
- SudokuBoard newCase = sudokuBoard.copy();
-
- PuzzleElement newCasePuzzleElement = newCase.getPuzzleElement(puzzleElement);
- newCasePuzzleElement.setData(i);
- newCase.addModifiedData(newCasePuzzleElement);
- cases.add(newCase);
- }
-
return cases;
}
}
diff --git a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumberCaseRule.java b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumbersForCellCaseRule.java
similarity index 60%
rename from src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumberCaseRule.java
rename to src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumbersForCellCaseRule.java
index e6ab0e64c..fbdc23b65 100644
--- a/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumberCaseRule.java
+++ b/src/main/java/edu/rpi/legup/puzzle/sudoku/rules/PossibleNumbersForCellCaseRule.java
@@ -5,19 +5,15 @@
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.PossibleNumberCaseBoard;
-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.List;
-import java.util.Set;
-public class PossibleNumberCaseRule extends CaseRule {
+public class PossibleNumbersForCellCaseRule extends CaseRule {
- public PossibleNumberCaseRule() {
+ public PossibleNumbersForCellCaseRule() {
super(
- "SUDO-CASE-0002",
+ "SUDO-CASE-0001",
"Possible Numbers for Cell",
"An empty cell has a limited set of possible numbers that can fill it.",
"edu/rpi/legup/images/sudoku/PossibleValues.png");
@@ -50,12 +46,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();
+ CaseBoard caseBoard = new CaseBoard(sudokuBoard, this);
+ for (PuzzleElement puzzleElement : sudokuBoard.getPuzzleElements()) {
+ if (((SudokuCell) puzzleElement).getData() == 0) {
+ caseBoard.addPickableElement(puzzleElement);
+ }
}
return caseBoard;
}
@@ -88,40 +84,10 @@ public ArrayList getCases(
List caseCells = new ArrayList<>();
SudokuCell cell = (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);
- }
- }
-
- 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) {
+ for (int i = 1; i <= 9; i++) {
Board newCase = sudokuBoard.copy();
- PuzzleElement element = newCase.getPuzzleElement(c);
- element.setData(value);
+ 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..4dcf95d63 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,7 +12,7 @@ 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");
@@ -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/resources/edu/rpi/legup/images/starbattle/UnknownTile.png b/src/main/resources/edu/rpi/legup/images/starbattle/UnknownTile.png
deleted file mode 100644
index 850fbf127..000000000
Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/UnknownTile.png and /dev/null 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/starbattle/white.gif b/src/main/resources/edu/rpi/legup/images/starbattle/white.gif
deleted file mode 100644
index fc2c683eb..000000000
Binary files a/src/main/resources/edu/rpi/legup/images/starbattle/white.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/NoCellForNumberColumn.png b/src/main/resources/edu/rpi/legup/images/sudoku/NoCellForNumberColumn.png
new file mode 100644
index 000000000..9dcade64b
Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/sudoku/NoCellForNumberColumn.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/NoCellForNumberRegion.png b/src/main/resources/edu/rpi/legup/images/sudoku/NoCellForNumberRegion.png
new file mode 100644
index 000000000..000dc8b68
Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/sudoku/NoCellForNumberRegion.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/NoCellForNumberRow.png b/src/main/resources/edu/rpi/legup/images/sudoku/NoCellForNumberRow.png
new file mode 100644
index 000000000..f984f8365
Binary files /dev/null and b/src/main/resources/edu/rpi/legup/images/sudoku/NoCellForNumberRow.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/possible_cells_number_column.png b/src/main/resources/edu/rpi/legup/images/sudoku/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/possible_cells_number_column.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/possible_cells_number_region.png b/src/main/resources/edu/rpi/legup/images/sudoku/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/possible_cells_number_region.png differ
diff --git a/src/main/resources/edu/rpi/legup/images/sudoku/possible_cells_number_row.png b/src/main/resources/edu/rpi/legup/images/sudoku/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/possible_cells_number_row.png differ
diff --git a/src/main/resources/edu/rpi/legup/legup/config b/src/main/resources/edu/rpi/legup/legup/config
index 1ee9ed79c..4b69da044 100644
--- a/src/main/resources/edu/rpi/legup/legup/config
+++ b/src/main/resources/edu/rpi/legup/legup/config
@@ -35,25 +35,21 @@
-
-
-
-
+
+
+
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/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 @@
+
+
+
+
+
+ |
+
+
+
+
+