Skip to content

Instantly share code, notes, and snippets.

@JenniferShola
Created July 9, 2013 01:13
Show Gist options
  • Save JenniferShola/5953863 to your computer and use it in GitHub Desktop.
Save JenniferShola/5953863 to your computer and use it in GitHub Desktop.
This is an assignment I did for CS 108 at Stanford. The instructors provided the bare, very bare, backbone for the assignment (MyDB.java, the default Sudoku grids and stringToGrid/parsing functions in Sudoku.java). This program is a Sudoku solver interface that prints out the puzzle, the solution, the number of available solutions and the time e…
package assign3;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
@SuppressWarnings("serial")
public class DatabaseViewer extends JFrame implements ActionListener {
private static JTable table;
private MetropolisModel model;
private static JTextField metroText;
private static JTextField continentText;
private static JTextField populationText;
private static JButton add;
private static JButton search;
private static JComboBox options;
private static JComboBox exactness;
static String[] optionsText = { "Population Greater Than", "Population Smaller Than" };
static String[] exactnessText = { "Exact Match", "Partial Match" };
public DatabaseViewer() {
JPanel top = new JPanel();
top.setLayout(new FlowLayout());
metroText = new JTextField(15);
continentText = new JTextField(15);
populationText = new JTextField(15);
top.add(new JLabel("Metropolis: "));
top.add(metroText);
top.add(new JLabel("Continent: "));
top.add(continentText);
top.add(new JLabel("Population: "));
top.add(populationText);
add(top, BorderLayout.PAGE_START);
JPanel side = new JPanel();
side.setLayout(new BoxLayout(side, BoxLayout.PAGE_AXIS));
add = new JButton("Add");
search = new JButton("Search");
options = new JComboBox(optionsText);
exactness = new JComboBox(exactnessText);
side.add(add);
side.add(search);
side.add(new JLabel("Search Options"));
side.add(options);
side.add(exactness);
add(side, BorderLayout.LINE_END);
add.addActionListener(this);
search.addActionListener(this);
metroText.addActionListener(this);
continentText.addActionListener(this);
populationText.addActionListener(this);
model = new MetropolisModel();
table = new JTable(model);
JScrollPane scrollpane = new JScrollPane(table);
add(scrollpane, BorderLayout.CENTER);
setVisible(true);
}
public static void main(String[] args) {
JFrame frame = new DatabaseViewer();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public void actionPerformed(ActionEvent e) {
if(e.getSource() == add){
model.add(metroText.getText(), continentText.getText(), populationText.getText());
model.fireTableDataChanged();
metroText.setText("");
continentText.setText("");
populationText.setText("");
}else if(e.getSource() == search){
boolean opt, ext;
if(options.getSelectedItem() == "Population Greater Than") opt = true;
else opt = false;
if(exactness.getSelectedItem() == "Exact Match") ext = true;
else ext = false;
model.search(metroText.getText(), continentText.getText(), populationText.getText(), opt, ext);
model.fireTableDataChanged();
metroText.setText("");
continentText.setText("");
populationText.setText("");
}
}
}
package assign3;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import javax.swing.table.AbstractTableModel;
/*
* CS108 Student: This file will be used to help the staff grade your assignment.
* You can modify this file as much as you like, provided three restrictions:
* 1) Do not change the class/file name
* - The class/file name should be MetropolisModel
* 2) Do not modify the MetropolisControl interface
* 3) MetropolisModel must implement the MetropolisControl interface
* - You can modify MetropolisModel to inherit/implement any additional class/interface
*/
@SuppressWarnings("serial")
public class MetropolisModel extends AbstractTableModel implements MetropolisControl {
private static Connection con;
private static ResultSet set;
private static ResultSetMetaData rsData;
private static final String MYSQL_DATABASE_NAME = "c_cs108_soyedele";
private static final String tableName = "metropolises";
/**
* Represents an implementation of the AbstractTableModel
* This class implements Metropolis Control.
* The model writes and displays data from a MySQL database.
*/
public MetropolisModel (){
con = MyDB.getConnection();
set = null;
try {
Statement st = con.createStatement(), use = con.createStatement();
use.executeQuery("USE "+MYSQL_DATABASE_NAME+";");
set = st.executeQuery("SELECT * FROM "+tableName+" WHERE metropolis = \"START_FIELD_OFF_EMPTY\";");
rsData = set.getMetaData();
} catch (SQLException e) {
System.out.print("Error in the MetropolisModel constructor.");
e.printStackTrace();
}
}
public ResultSet search(String metropolis, String continent, String population, boolean populationLargerThan, boolean exactMatch) {
try {
Statement st = con.createStatement();
set = st.executeQuery(getQuery(metropolis, continent, population, populationLargerThan, exactMatch));
} catch (SQLException e) {
e.printStackTrace();
}
return set;
}
public void add(String metropolis, String continent, String population) {
try {
Statement st = con.createStatement(), st2 = con.createStatement();
st.executeUpdate("INSERT INTO "+tableName+" VALUES(\""+metropolis+"\",\""+continent+"\",\""+population+"\");");
set = st2.executeQuery("SELECT * FROM "+ tableName +" WHERE metropolis =\""
+ metropolis +"\" AND continent = \""+continent+"\" AND population = "+population+";");
//update model - fireTableStructureChanged
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* Constructs the correct MYSQL query string based on their values
* Returns a search query string based on valid parameters
*
* @param metropolis value of the metropolis field
* @param continent value of the continent field
* @param population value of the population field
* @param populationLargerThan True if "Population Larger Than" has been selected
* @param exactMatch True if "Exact Match" has been selected
*
* @return String Resulting string query
*/
private String getQuery(String metropolis, String continent, String population, boolean populationLargerThan, boolean exactMatch){
String query = "SELECT * FROM "+tableName;
int count = 0;
if(!metropolis.isEmpty()) count += 100;
if(!continent.isEmpty()) count += 10;
if(!population.isEmpty()) count += 1;
switch(count){
case 0:
query += ";";
break;
case 100: //Metropolis Only
query += " WHERE metropolis ";
if(exactMatch){
query += "= \"";
query += metropolis;
query +="\";";
}else{
query += "LIKE \"";
query += metropolis;
query +="%\";";
}
break;
case 10: //Continent Only
query += " WHERE continent ";
if(exactMatch){
query += "= \"";
query += continent;
query +="\";";
}else{
query += "LIKE \"";
query += continent;
query +="%\";";
}
break;
case 1: //Population Only
query += " WHERE population ";
if(populationLargerThan) query += "> ";
else query += "< ";
query += population;
query +=";";
break;
case 11: //Continent and Population Only
query += " WHERE continent ";
if(exactMatch){
query += "= \"";
query += continent;
query +="\" AND ";
}else{
query += "LIKE \"";
query += continent;
query +="%\" AND ";
}
query += "population ";
if(populationLargerThan) query += "> ";
else query += "< ";
query += population;
query +=";";
break;
case 110: //Metropolis and Continent Only
query += " WHERE metropolis ";
if(exactMatch){
query += "= \"";
query += metropolis;
query +="\" AND continent = \"";
query += continent;
query +="\";";
}else{
query += "LIKE \"";
query += metropolis;
query +="%\" AND continent LIKE \"";
query += continent;
query +="%\";";
}
break;
case 101: //Metropolis and Population Only
query += " WHERE metropolis ";
if(exactMatch){
query += "= \"";
query += metropolis;
query +="\" AND ";
}else{
query += "LIKE \"";
query += metropolis;
query +="%\" AND ";
}
query += "population ";
if(populationLargerThan) query += "> ";
else query += "< ";
query += population;
query +=";";
break;
case 111: //Metropolis, Continent and Population Only
query += " WHERE metropolis ";
if(exactMatch){
query += "= \"";
query += metropolis;
query +="\" AND continent = \"";
query += continent;
query +="\"";
}else{
query += "LIKE \"";
query += metropolis;
query +="%\" AND continent LIKE \"";
query += continent;
query +="%\"";
}
query += " AND population ";
if(populationLargerThan) query += "> ";
else query += "< ";
query += population;
query +=";";
break;
}
return query;
}
/**
* Returns the number of columns in the database
*
* @return Math.int Number of columns in the database
*/
@Override
public int getColumnCount() {
try {
int cols = rsData.getColumnCount();
return cols;
} catch (SQLException e) {
System.out.print("Metropolis Model getColumnCount() error!");
e.printStackTrace();
}
return 0;
}
/**
* Returns the number of rows in the database
*
* @return Math.int Number of rows in the database
*/
@Override
public int getRowCount() {
try {
set.first();
int numRows = -1;
while(numRows < set.getRow()){
if(numRows < set.getRow()) numRows = set.getRow();
set.next();
}
return numRows;
} catch (SQLException e) {
System.out.print("Metropolis Model getRowCount() error!");
e.printStackTrace();
}
return 0;
}
/**
* Searches the Metropolis data-set for the value
* Returns the string found at the parameters provided
*
* @param arg0 row index
* @param arg1 column index
*
* @return Object Results the string at the appropriate parameters
*/
@Override
public Object getValueAt(int arg0, int arg1) {
try {
set.first();
for(int i = 0; i < arg0; i++)
set.next();
return set.getObject(arg1+1);
} catch (SQLException e) {
System.out.print("Metropolis Model getValueAt() error!");
e.printStackTrace();
}
return null;
}
/**
* Returns the column label associated with the given column index
*
* @param col column index
*
* @return String Column label associated with the @param col
*/
@Override
public String getColumnName(int col){
try {
return rsData.getColumnLabel(col+1);
} catch (SQLException e) {
e.printStackTrace();
}
return "Column Not Found!";
}
}
interface MetropolisControl {
/**
* Searches the Metropolis data-set for the provided search parameters.
* Returns the query results as a java.sql.ResultSet
*
* @param metropolis value of the metropolis field
* @param continent value of the continent field
* @param population value of the population field
* @param populationLargerThan True if "Population Larger Than" has been selected
* @param exactMatch True if "Exact Match" has been selected
*
* @return resultSet Results for the given query
*/
public ResultSet search(String metropolis, String continent, String population, boolean populationLargerThan, boolean exactMatch);
/**
* Adds the entry to the Metropolis data-set.
*
* @param metropolis value of the metropolis field
* @param continent value of the continent field
* @param population value of the population field
*/
public void add(String metropolis, String continent, String population);
}
package assign3;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/*
* CS108 Student: This file will be replaced when we test your code. So, do not add any of your
* assignment code to this file. Also, do not modify the public interface of this file.
* Only change the private MySQL constants so that it works with the database login credentials
* that we emailed to you.
*
* Usage:
* Connection con = MyDB.getConnection();
*
* Optional, but recommended: Call the close function when you no longer need to work with the database
* MyDB.close()
*/
public class MyDB {
private static final String MYSQL_USERNAME = "ccs108soyedele";
private static final String MYSQL_PASSWORD = "iaghowuk";
private static final String MYSQL_DATABASE_SERVER = "mysql-user.stanford.edu";
private static final String MYSQL_DATABASE_NAME = "c_cs108_soyedele";
private static Connection con;
static {
try {
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://" + MYSQL_DATABASE_SERVER + "/" + MYSQL_DATABASE_NAME;
con = DriverManager.getConnection(url, MYSQL_USERNAME, MYSQL_PASSWORD);
} catch (SQLException e) {
e.printStackTrace();
System.err.println("CS108 student: Update the MySQL constants to correct values!");
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.err.println("CS108 student: Add the MySQL jar file to your build path!");
}
}
public static Connection getConnection() {
return con;
}
public static void close() {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
/*
* CS108 student: Do not add/remove any methods to this file since this file will be replaced
* when we test your code!
*/
}
package assign3;
import java.util.*;
/*
* Encapsulates a Sudoku grid to be solved.
* CS108 Stanford.
*/
public class Sudoku {
// Provided grid data for main/testing
// The instance variable strategy is up to you.
// Provided easy 1 6 grid
// (can paste this text into the GUI too)
public static final int[][] easyGrid = Sudoku.stringsToGrid(
"1 6 4 0 0 0 0 0 2",
"2 0 0 4 0 3 9 1 0",
"0 0 5 0 8 0 4 0 7",
"0 9 0 0 0 6 5 0 0",
"5 0 0 1 0 2 0 0 8",
"0 0 8 9 0 0 0 3 0",
"8 0 9 0 4 0 2 0 0",
"0 7 3 5 0 9 0 0 1",
"4 0 0 0 0 0 6 7 9");
// Provided medium 5 3 grid
public static final int[][] mediumGrid = Sudoku.stringsToGrid(
"530070000",
"600195000",
"098000060",
"800060003",
"400803001",
"700020006",
"060000280",
"000419005",
"000080079");
// Provided hard 3 7 grid
// 1 solution this way, 6 solutions if the 7 is changed to 0
public static final int[][] hardGrid = Sudoku.stringsToGrid(
"3 7 0 0 0 0 0 8 0",
"0 0 1 0 9 3 0 0 0",
"0 4 0 7 8 0 0 0 3",
"0 9 3 8 0 0 0 1 2",
"0 0 0 0 4 0 0 0 0",
"5 2 0 0 0 6 7 9 0",
"6 0 0 0 2 1 0 4 0",
"0 0 0 5 3 0 9 0 0",
"0 3 0 0 0 0 0 5 1");
public static final int SIZE = 9; // size of the whole 9x9 puzzle
public static final int PART = 3; // size of each 3x3 part
public static final int MAX_SOLUTIONS = 100;
private static final String tab = "\t";
private final static String newline = "\n";
private final static String quotes = "\"";
private final static String quotesComma = "\",";
private final static String charEnding = "\");";
private static int[][] solution;
private static int[][] grid;
private static long time;
private static int numSolutions;
ArrayList<Spot> unassigned;
// Provided various static utility methods to
// convert data formats to int[][] grid.
/**
* Returns a 2-d grid parsed from strings, one string per row.
* The "..." is a Java 5 feature that essentially
* makes "rows" a String[] array.
* (provided utility)
* @param rows array of row strings
* @return grid
*/
public static int[][] stringsToGrid(String... rows) {
int[][] result = new int[rows.length][];
for (int row = 0; row<rows.length; row++) {
result[row] = stringToInts(rows[row]);
}
return result;
}
/**
* Given a single string containing 81 numbers, returns a 9x9 grid.
* Skips all the non-numbers in the text.
* (provided utility)
* @param text string of 81 numbers
* @return grid
*/
public static int[][] textToGrid(String text) {
int[] nums = stringToInts(text);
if (nums.length != SIZE*SIZE) {
throw new RuntimeException("Needed 81 numbers, but got:" + nums.length);
}
int[][] result = new int[SIZE][SIZE];
int count = 0;
for (int row = 0; row<SIZE; row++) {
for (int col=0; col<SIZE; col++) {
result[row][col] = nums[count];
count++;
}
}
return result;
}
/**
* Given a string containing digits, like "1 23 4",
* returns an int[] of those digits {1 2 3 4}.
* (provided utility)
* @param string string containing ints
* @return array of ints
*/
public static int[] stringToInts(String string) {
int[] a = new int[string.length()];
int found = 0;
for (int i=0; i<string.length(); i++) {
if (Character.isDigit(string.charAt(i))) {
a[found] = Integer.parseInt(string.substring(i, i+1));
found++;
}
}
int[] result = new int[found];
System.arraycopy(a, 0, result, 0, found);
return result;
}
// Provided -- the deliverable main().
// You can edit to do easier cases, but turn in
// solving hardGrid.
public static void main(String[] args) {
Sudoku sudoku = new Sudoku(hardGrid);
System.out.println(sudoku);
int count = sudoku.solve();
System.out.println("solutions:" + count);
System.out.println("elapsed: " + sudoku.getElapsed() + "ms");
System.out.println(sudoku.getSolutionText());
}
/**
* Sets up based on the given ints.
*/
public Sudoku(int[][] ints) {
unassigned = getSpots(ints);
numSolutions = 0;
grid = ints;
time = 0;
}
/**
* Solves the puzzle, invoking the underlying recursive search.
*/
public int solve() {
numSolutions = 0;
long startTime = System.currentTimeMillis();
solveHelper(0);
long endTime = System.currentTimeMillis();
time = endTime - startTime;
return numSolutions;
}
private void solveHelper(int index){
if(index >= unassigned.size()){
numSolutions++;
if(numSolutions == 1){
solution = new int[SIZE][SIZE];
for(int i = 0; i < SIZE; i++)
for(int j = 0; j < SIZE; j++)
solution[i][j] = grid[i][j];
}
}else if(numSolutions != MAX_SOLUTIONS){
Spot spot = unassigned.get(index);
Set<Integer> set = spot.getPossibleValues();
for(Integer val : set) {
index++;
spot.set(val);
solveHelper(index);
spot.set(0);
index--;
}
}
}
private ArrayList<Spot> getSpots(int[][] grid){
unassigned = new ArrayList<Spot>();
Spot spot;
for(int i = 0; i < SIZE; i++)
for(int j = 0; j < SIZE; j++)
if(grid[i][j] == 0){
spot = new Spot(i, j);
unassigned.add(spot);
}
return unassigned;
}
@Override
public String toString(){
String text = "";
for(int i = 0; i < SIZE; i++){
text += tab;
text+= quotes;
for(int j = 0; j < SIZE; j++){
text += Integer.toString(grid[i][j]);
text += " ";
}
if(i != SIZE-1) text += quotesComma;
else text += charEnding;
text += newline;
}
return text;
}
public String getSolutionText() {
if(solution == null) return "No Solution Found! \n";
String text = "";
for(int i = 0; i < SIZE; i++){
for(int j = 0; j < SIZE; j++){
text += Integer.toString(solution[i][j]);
text += " ";
}
text += newline;
}
return text;
}
public long getElapsed() {
if(solution == null) return 0; //For use when there is no valid solution
return time;
}
public int getNumSolutions(){
return numSolutions;
}
public static class Spot {
private int spotX;
private int spotY;
Spot(int x, int y){
spotX = x;
spotY = y;
}
private void set(int value){
grid[spotX][spotY] = value;
}
private Set<Integer> getPossibleValues(){
Set<Integer> set = new HashSet<Integer>();
for(int i = 1; i <= SIZE; i++)
if(checkRow(i) && checkColumn(i) && checkSquare(i))
set.add(i);
return set;
}
private boolean checkRow(int value){
for(int i = 0; i < SIZE; i++)
if(grid[i][spotY] == value) return false;
return true;
}
private boolean checkColumn(int value){
for(int i = 0; i < SIZE; i++)
if(grid[spotX][i] == value) return false;
return true;
}
private boolean checkSquare(int value){
int indexX = spotX/PART, indexY = spotY/PART;
for(int row = indexX*PART; row < indexX*PART + PART; row++)
for(int col = indexY*PART; col < indexY*PART + PART; col++)
if(grid[row][col] == value) return false;
return true;
}
}
}
package assign3;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.event.*;
import java.awt.*;
import java.awt.event.*;
@SuppressWarnings("serial")
public class SudokuFrame extends JFrame implements ActionListener, DocumentListener {
private static JButton check;
private static JCheckBox auto;
private static JTextArea original;
private static JTextArea results;
private static Sudoku sudoku;
private static String newline = "\n";
public SudokuFrame() {
super("Sudoku Solver");
JFrame frame = new JFrame();
frame.setLayout(new BorderLayout(4,4));
check = new JButton("Check");
auto = new JCheckBox("Auto Check");
auto.setSelected(false);
original = new JTextArea(15, 20);
results = new JTextArea(15, 20);
original.setBorder(new TitledBorder("Puzzle"));
results.setBorder(new TitledBorder("Solution"));
JPanel pane = new JPanel();
pane.setLayout(new FlowLayout());
pane.add(original);
pane.add(results);
JPanel bottomPane = new JPanel();
bottomPane.setLayout(new FlowLayout());
bottomPane.add(check);
bottomPane.add(auto);
add(pane, BorderLayout.PAGE_START);
add(bottomPane, BorderLayout.LINE_START);
check.addActionListener(this);
auto.addActionListener(this);
original.getDocument().addDocumentListener(this);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String[] args) {
// GUI Look And Feel
// Do this incantation at the start of main() to tell Swing
// to use the GUI LookAndFeel of the native platform. It's ok
// to ignore the exception.
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ignored) { }
new SudokuFrame();
sudoku = new Sudoku(Sudoku.hardGrid);
original.setText(sudoku.toString());
}
@Override
public void actionPerformed(ActionEvent e) {
int count = sudoku.solve();
if(e.getSource() == check){
String newText = sudoku.getSolutionText() + newline
+ "solutions: " + count + newline + "elapsed: " + sudoku.getElapsed() + "ms" + newline;
results.setText(newText + newline);
}
}
@Override
public void changedUpdate(DocumentEvent e) {
// DO NOTHING
}
@Override
public void insertUpdate(DocumentEvent arg0) {
if(auto.isSelected()){
String grid = original.getText();
if(!malformed(grid)){
sudoku = new Sudoku(Sudoku.textToGrid(grid));
}else{
sudoku = null;
results.setText("Parsing Problem");
}
if(sudoku != null){
int count = sudoku.solve();
String newText = sudoku.getSolutionText() + newline
+ "solutions: " + count + newline + "elapsed: " + sudoku.getElapsed() + "ms" + newline;
results.setText(newText + newline);
}
}
}
@Override
public void removeUpdate(DocumentEvent arg0) {
if(auto.isSelected()){
String grid = original.getText();
if(!malformed(grid)){
sudoku = new Sudoku(Sudoku.textToGrid(grid));
}else{
sudoku = null;
results.setText("Parsing Problem");
}
if(sudoku != null){
int count = sudoku.solve();
String newText = sudoku.getSolutionText() + newline
+ "solutions: " + count + newline + "elapsed: " + sudoku.getElapsed() + "ms" + newline;
results.setText(newText + newline);
}
}
}
private boolean malformed(String text){
int count = 0;
for (int i=0; i<text.length(); i++) {
if (Character.isDigit(text.charAt(i))) count++;
if (count > 81) return true;
}
if (count < 81) return true;
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment