/* Useful imports */ import edu.neu.ccs.*; import edu.neu.ccs.gui.*; import edu.neu.ccs.codec.*; import edu.neu.ccs.console.*; import edu.neu.ccs.filter.*; import edu.neu.ccs.jpf.*; import edu.neu.ccs.parser.*; import edu.neu.ccs.pedagogy.*; import edu.neu.ccs.quick.*; import edu.neu.ccs.util.*; import java.awt.*; import java.awt.event.*; import java.awt.geom.*; import java.awt.font.*; import java.awt.image.*; import javax.swing.*; import javax.swing.border.*; import java.io.*; import java.util.*; import java.math.*; import java.beans.*; import java.lang.reflect.*; import java.net.URL; import java.util.regex.*; import java.text.ParseException; public class SudokuBase extends TablePanel { // Static definitions // /** The color of the border around the entire GUI. */ protected static final Color edgecolor = Colors.black; /** The tiny gap. */ protected static final int gaptiny = 2; /** The small gap. */ protected static final int gapsmall = 6; /** The medium gap. */ protected static final int gapmedium = 12; /** The large gap. */ protected static final int gaplarge = 18; // Member definitions // /** The model object. */ protected SudokuModel model = new SudokuModel(this); /** The table object. */ protected SudokuTable table = model.getSudokuTable(); /** The stack consisting of lists of frozen cells. */ protected Vector frozenstack = new Vector(); /** The action to compute and display hints in the view. */ protected final SimpleAction showHints = new SimpleAction("Show Hints") { public void perform() { showHints(); } }; /** The action to clear hints in the view. */ protected final SimpleAction clearHints = new SimpleAction("Clear Hints") { public void perform() { clearHints(); } }; /** The action to reset hints in the view. */ protected final SimpleAction resetHints = new SimpleAction("Reset Hints") { public void perform() { resetHints(); } }; /** The check box to select auto show hints. */ protected BooleanView autoShowHints = new BooleanView("Show Hints Automatically?", false); /** The IO stuff. */ protected Object[] hintsStuff = { showHints, clearHints, resetHints, autoShowHints }; /** The IO panel. */ protected TablePanel hintsPanel = new TablePanel (hintsStuff, VERTICAL, gapsmall, gapsmall, CENTER); /** The action to prune one cycle. */ protected final SimpleAction pruneOneCycle = new SimpleAction("Prune Once") { public void perform() { setSignal(pruneOneCycle()); showHints(); } }; /** The action to prune completely. */ protected final SimpleAction pruneComplete = new SimpleAction("Prune Repeatedly") { public void perform() { setSignal(pruneComplete()); showHints(); } }; /** The check box for the prune triplets algorithm. */ protected BooleanView pruneTriplets = new BooleanView("Prune Triplets?", false); /** The check box for the prune triplets algorithm. */ protected BooleanView pruneHintSets = new BooleanView("Prune Hint Sets?", false); /** The check box for the prune triplets algorithm. */ protected BooleanView pruneUniqueHints = new BooleanView("Prune Unique Hints?", false); /** The Prune Choice stuff. */ protected Object[] pruneChoiceStuff = { pruneTriplets, pruneHintSets, pruneUniqueHints }; /** The Prune Choice panel. */ protected TablePanel pruneChoicePanel = new TablePanel (pruneChoiceStuff, VERTICAL, gapsmall, gapsmall, WEST); /** The paint swatch to signal pruning. */ protected PaintSwatch swatch = new PaintSwatch(Colors.white); /** The signal stuff. */ protected Object[] signalStuff = { "Has Pruned?", swatch }; /** The signal table. */ protected TablePanel signalPanel = new TablePanel (signalStuff, HORIZONTAL, gapsmall, gapsmall, CENTER); /** The check box to select auto prune hints. */ protected BooleanView autoPruneHints = new BooleanView("Prune Hints Automatically?", false); /** The Prune stuff. */ protected Object[] pruneStuff = { pruneOneCycle, pruneComplete, pruneChoicePanel, signalPanel, autoPruneHints }; /** The Prune panel. */ protected TablePanel prunePanel = new TablePanel (pruneStuff, VERTICAL, gapsmall, gapsmall, CENTER); /** The freeze action. */ protected SimpleAction freeze = new SimpleAction("Freeze") { public void perform() { freeze(); } }; /** The thaw action. */ protected SimpleAction thaw = new SimpleAction("Thaw") { public void perform() { thaw(); } }; /** The thawAll action. */ protected SimpleAction thawAll = new SimpleAction("Thaw All") { public void perform() { thawAll(); } }; /** The freeze-thaw stuff. */ protected Object[] freezeStuff = { freeze, thaw, thawAll }; /** The freeze-thaw panel. */ protected TablePanel freezePanel = new TablePanel (freezeStuff, HORIZONTAL, gapsmall, gapsmall, CENTER); /** The action to remove back to the last freeze. */ protected final SimpleAction removeBackToLastFreeze = new SimpleAction("Remove Back To Last Freeze") { public void perform() { removeBackToLastFreeze(); } }; /** The action to empty the puzzle. */ protected final SimpleAction removeEverything = new SimpleAction("Remove Everything") { public void perform() { removeEverything(); } }; /** The controls stuff. */ protected Object[] controlsStuff = { hintsPanel, prunePanel, freezePanel, removeBackToLastFreeze, removeEverything }; /** The controls table. */ protected TablePanel controlsPanel = new TablePanel (controlsStuff, VERTICAL, gaplarge, gaplarge, CENTER); // Constructor // /** The constructor. */ public SudokuBase() { super(1, 2, gapmedium, gapmedium, NORTH); initializeSudokuBase(); } // Methods // /** The constructor initialize method. */ protected void initializeSudokuBase() { addObject(controlsPanel, 0, 0); addObject(table, 0, 1); createBorder(); } /** Create the border around the entire GUI. */ protected void createBorder() { int a = gaptiny; int b = gapmedium; Border inner = BorderFactory.createEmptyBorder(b, b, b, b); Border line = BorderFactory.createLineBorder(edgecolor, a); Border comp = BorderFactory.createCompoundBorder(line, inner); Border outer = BorderFactory.createEmptyBorder(b, b, b, b); Border full = BorderFactory.createCompoundBorder(outer, comp); setBorder(full); } /** Returns whether or not to do auto show hints. */ public boolean doAutoShowHints() { return autoShowHints.getBooleanValue(); } /** Returns whether or not to do auto prune hints. */ public boolean doAutoPruneHints() { return autoPruneHints.getBooleanValue(); } /** Returns whether or not to prune triplets. */ public boolean doPruneTriplets() { return pruneTriplets.getBooleanValue(); } /** Returns whether or not to prune hint sets. */ public boolean doPruneHintSets() { return pruneHintSets.getBooleanValue(); } /** Returns whether or not to prune unique hints. */ public boolean doPruneUniqueHints() { return pruneUniqueHints.getBooleanValue(); } /** Initialize the hints prior to pruning. */ public void initializeHints() { model.initializeHints(); setSignal(false); } /** * Sets the pruning algorithms in the model * from the user settings in this GUI. */ public void setPruningAlgorithms() { model.setPruneTriplets(doPruneTriplets()); model.setPruneHintSets(doPruneHintSets()); model.setPruneUniqueHints(doPruneUniqueHints()); } /** * Prune hints by eliminating hints made impossible * by data or hints in other rows or cols. * * This method repeats the pruning process until no * more hints can be pruned. * * The pruning algorithms used depend on the settings * made via the methods: * setPruneTriplets * setPruneHintSets * setPruneUniqueHints * * Returns true if at least one hint was pruned. */ public boolean pruneComplete() { setPruningAlgorithms(); return model.pruneComplete(); } /** * Performs one cycle of pruning hints. * * The pruning algorithms used depend on the settings * made via the methods: * setPruneTriplets * setPruneHintSets * setPruneUniqueHints * * Returns true if at least one hint was pruned. */ public boolean pruneOneCycle() { setPruningAlgorithms(); return model.pruneOneCycle(); } /** Display the hints in the GUI. */ public void showHints() { for (int row = 1; row <= 9; row++) for (int col = 1; col <= 9; col++) if (model.getCellData(row, col) == 0) { SudokuHint hint = model.getCellHint(row, col); SudokuBlock block = table.getSudokuBlock(row, col); block.paintHint(hint); } } /** Clear the hints in the GUI. */ public void clearHints() { for (int row = 1; row <= 9; row++) for (int col = 1; col <= 9; col++) if (model.getCellData(row, col) == 0) { SudokuBlock block = table.getSudokuBlock(row, col); block.clearBlock(); } } /** * Reset the hints to the initial hints * and then show the hints in the GUI. */ public void resetHints() { initializeHints(); showHints(); } /** Update all blocks with data and optional hints . */ public void updateAll() { updateData(); updateHints(); } /** Update all blocks with data only. */ public void updateData() { for (int row = 1; row <= 9; row++) for (int col = 1; col <= 9; col++) { SudokuBlock block = table.getSudokuBlock(row, col); block.paintDigit(model.getCellData(row, col)); } } /** * Initialize the hints based on the current cell data * then update the display after checking the settings * for auto-show-hints and auto-prune-hints. */ public void updateHints() { initializeHints(); setSignal(false); if (doAutoPruneHints()) { pruneComplete(); showHints(); } else if (doAutoShowHints()) showHints(); else clearHints(); } /** * Set the swatch signal to red if the given flag is true * and to white if the given flag is false. */ public void setSignal(boolean flag) { if (flag) swatch.setPaint(Colors.red); else swatch.setPaint(Colors.white); } /** * Freeze those cells which are non-empty, that is, * have data between 1 and 9, and which have not * been frozen earlier. * * Maintain knowledge of this group of frozen cells * so that they may be later thawed. * * @return the number of cells that were frozen */ public int freeze() { int count = 0; Vector list = new Vector(); for (int row = 1; row <= 9; row++) for (int col = 1; col <= 9; col++) { int data = model.getCellData(row, col); if (data > 0) { SudokuBlock block = table.getSudokuBlock(row, col); if (block.isActive()) { count++; block.freeze(); list.add(block.getCellPosition()); } } } if (count > 0) frozenstack.add(list); return count; } /** * Thaw the most recent group of frozen cells if any. * * @return the number of cells that were thawed */ public int thaw() { int size = frozenstack.size(); if (size <= 0) return 0; Vector list = (Vector) frozenstack.remove(size - 1); int count = list.size(); for (int i = (count - 1); i >= 0; i--) { CellPosition position = (CellPosition) list.remove(i); SudokuBlock block = table.getSudokuBlock(position); block.thaw(); } return count; } /** Thaw all frozen cells. */ public void thawAll() { while (frozenstack.size() > 0) thaw(); } /** Remove all cell data back to the last freeze. */ public void removeBackToLastFreeze() { int count = 0; for (int row = 1; row <= 9; row++) for (int col = 1; col <= 9; col++) { int data = model.getCellData(row, col); if (data > 0) { SudokuBlock block = table.getSudokuBlock(row, col); if (block.isActive()) { model.setCellData(row, col, 0); count++; } } } if (count > 0) updateAll(); } /** Remove all cell data from the puzzle. */ public void removeEverything() { model.removeEverything(); thawAll(); updateData(); } /** The main method. */ public static void main(String[] args) { SudokuBase sudoku = new SudokuBase(); sudoku.frame("Sudoku Puzzle"); } }