Java 数独游戏,序列化问题
我正在创建数独游戏,并尝试提供保存、另存为和打开游戏的选项。我正在使用JFileChooser来完成此操作。我可以保存(或“另存为”),但当我试图打开保存的文件时,会出现错误。我是编程新手,我希望有人能发现这个问题,并教我如何在保存时阅读数独板的内容(以及如何在打开文件时重新创建数独板)。我听说使用InputStream/OutputStream而不是Reader/Writer可以更容易地处理这个问题 这是我的内部类代码,它实现了这一点(我不知道是否有办法在不超过此文本框的字符限制的情况下发布整个类):Java 数独游戏,序列化问题,java,serialization,Java,Serialization,我正在创建数独游戏,并尝试提供保存、另存为和打开游戏的选项。我正在使用JFileChooser来完成此操作。我可以保存(或“另存为”),但当我试图打开保存的文件时,会出现错误。我是编程新手,我希望有人能发现这个问题,并教我如何在保存时阅读数独板的内容(以及如何在打开文件时重新创建数独板)。我听说使用InputStream/OutputStream而不是Reader/Writer可以更容易地处理这个问题 这是我的内部类代码,它实现了这一点(我不知道是否有办法在不超过此文本框的字符限制的情况下发布整
这里是SudokuMain类的第一部分(我无意中在MenuAtTop中使用了“SudokuMain 2”而不是“SudokuMain”,所以忽略2):
//允许短名称访问以下类
导入java.awt.*;
导入java.awt.event.*;
导入javax.swing.*;
导入java.util.*;
导入java.io.*;
公共类SudokuMain扩展了JComponent{
/**
*应用方法。
*
*@param指定命令行参数。
*/
公共静态void main(字符串[]args){
新SudokuMain();
}
//此字段引用访问信息的SudokuBase类
//董事会主席。
私人SudokuBase板;
//此字段引用SudokuView对象以访问其信息
//并提供输出
私人SudokuView视图;
//窗口中包含所有组件
私营部门赢得;
//在窗口中居中JPanel对象
私人杰帕内尔中心;
//窗口中的左JPanel对象
私人杰帕内尔西;
//窗口中的右JPanel对象
私人杰帕内尔东区;
//窗口中的底部JPanel对象
私人杰帕内尔南部;
//JPanel对象用于保存图形“按钮”
私人JPanel符号;
//JPanel对象显示数独板的“状态”
私人JPanel rowColRegStates;
//第一个设置窗口(不能更改一次
//实例化)
私有最终对话框1 setWin1;
/**
*构造SudokuMain对象。
*/
公共SudokuMain(){
//开始比赛
board=makeBoard();
视图=新的SudokuView(板);
win=新的JFrame(“数独游戏”);
中心=新的JPanel();
west=新JPanel();
east=新JPanel();
南=新JPanel();
//当前数独板的图形“按钮”
符号=新的设置符号(视图);
//当前数独板的“状态”
rowColRegStates=新的显示状态(视图);
//第一个设置窗口
setWin1=newdialog1(这是“新游戏”,true);
//创建菜单栏
最终菜单顶部菜单栏=新菜单顶部(本);
win.setJMenuBar(菜单栏);
//显示游戏模式
JLabel模式=新的JLabel(“正常播放模式”);
模式设置水平对齐(JLabel.CENTER);
Font modeFont=新字体(“Arial”,Font.BOLD,14);
mode.setFont(modeFont);
////将所选单元格设置为(0,0)
//视图。选择的设置(0,0);
//添加窗口焦点侦听器
win.addWindowFocusListener(新的WindowFocusListener(){
公共无效windowGainedFocus(WindowEvent e){
win.pack();
//将选定单元格设置为(0,0)
视图。选择的设置(0,0);
}
公共无效windowLostFocus(WindowEvent e){
}
});
//添加窗口侦听器
win.addWindowListener(新的WindowAdapter(){
公共无效窗口关闭(WindowEvent e){
menuBar.closePrompt();
}
});
win.setLayout(新的BorderLayout());
setLayout(新的BorderLayout());
setLayout(新的BorderLayout());
setLayout(新的FlowLayout());
添加(符号);
添加(视图、边框布局、中心);
南部。添加(rowColRegStates);
中加(西);
中加(东);
win.add(南、边界布局、北);
win.add(中心,BorderLayout.center);
win.add(模式,BorderLayout.SOUTH);
win.setDefaultCloseOperation(JFrame.DISPOSE\u ON\u CLOSE);
win.pack();
win.setVisible(true);
}
//这个内部类构造图形“按钮”来设置所需的
//电路板单元
类SudokuControlButton扩展了JPanel{
//选定单元格的行
私家车;
//选定单元格的列
私家车;
//与所需符号对应的值
私有int值;
/**
*构造SudokuControlButton对象;图形“按钮”
*控制董事会。
*
*@param view要控制的SudokuView对象。
*@param v对应于所需符号的值。
*/
公共数独控制按钮(最终数独视图,int v){
//设置图形“按钮”的特性
设置首选尺寸(新尺寸(50,50));
立根背景(颜色:浅灰色);
值=v;
addMouseListener(新MouseListener(){
/**
*此方法选择一个“按钮”,并在鼠标移动时将其置于焦点
*点击它。
*
*@param事件捕获鼠标按钮的信息
*在组件上单击(按下并释放)。
*/
公共无效mouseClicked(MouseEvent e){
selRow=view.getSelectedRow();
selCol=view.getSelectedColumn();
如果(!board.isGiven(selRow,selCol)){
线路板设置值(selRow、selCol、value);
view.new SudokuCell(selRow、selCol、board);
//设置为“突出显示”颜色
挫折地面(颜色:白色);
view.repaint();
}
否则{//系统会发出嘟嘟声
getToolkit().beep();
}
重新油漆();
}
/**
*此方法处理鼠标进入图形时的行为
*“按钮”。
*
*@param事件捕获鼠标按钮的信息
*在组件上输入。
*/
公共无效鼠标事件(鼠标事件e){
//设置为“突出显示”co
// this inner class provides a JMenuBar object at the top of
// the board
class MenuAtTop extends JMenuBar implements ActionListener{
// SudokuMain2 object we are dealing with
private SudokuMain2 main;
// the "File" menu
private JMenu fileMenu;
// the "New Game" option
private JMenuItem newGame;
// the "Open" option
private JMenuItem open;
// the "Save" option
private JMenuItem save;
// the "Save As" option
private JMenuItem saveAs;
// the "Reset" option
private JMenuItem reset;
// the "Quit" option
private JMenuItem quit;
// the ability to choose files
private JFileChooser choose;
// the saved file
// // compiler would not allow "static" keyword
private File fileSaved = null;
private Object opener;
// JDialog object to create a dialog box to prompt
// user for new game information
private JDialog createNewWin;
/**
* Constructs MenuAtTop object.
*
* @param m The SudokuMain2 object to be referred to.
*/
public MenuAtTop(final SudokuMain2 m) {
main = m;
opener = null;
choose = new JFileChooser();
// instantiate and bind to reference
fileMenu = new JMenu("File");
add(fileMenu);
// instantiate and bind to reference
newGame = new JMenuItem("New Game");
newGame.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
ActionEvent.CTRL_MASK));
fileMenu.add(newGame);
newGame.addActionListener(this);
open = new JMenuItem("Open");
open.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,
ActionEvent.CTRL_MASK));
fileMenu.add(open);
// add action listener to "Open" option
open.addActionListener(this);
save = new JMenuItem("Save");
save.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
ActionEvent.CTRL_MASK));
fileMenu.add(save);
// //save.setEnabled(false);
// add action listener to "Save" option
save.addActionListener(this);
saveAs = new JMenuItem("Save As");
saveAs.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,
ActionEvent.CTRL_MASK));
fileMenu.add(saveAs);
// add action listener to "Save As" option
saveAs.addActionListener(this);
reset = new JMenuItem("Reset");
reset.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R,
ActionEvent.CTRL_MASK));
fileMenu.add(reset);
// add action listener to "Reset" option
reset.addActionListener(this);
quit = new JMenuItem("Quit");
quit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
ActionEvent.CTRL_MASK));
fileMenu.add(quit);
// add action listener to "Quit" option
quit.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(quit)) {
closePrompt();
//main.win.dispose();
}
else if(e.getSource().equals(reset)) {
int n = JOptionPane.showConfirmDialog(main.win,
"Any player values will" +
" be lost. Proceed?",
"Warning!", 2);
if(n == JOptionPane.OK_OPTION) {
main.board.reset();
main.view.repaint();
}
}
else if(e.getSource().equals(saveAs)) {
saveAs();
}
else if(e.getSource().equals(save)) {
if(fileSaved == null) {
saveAs();
}
else {
try {
board.writeToStream(new FileOutputStream(fileSaved));
// main.board.setDirty(false);
} catch (Exception ex) {
JOptionPane.showMessageDialog(main.win, "Error saving file.");
}
}
}
else if(e.getSource().equals(open)) {
int returnVal = choose.showOpenDialog(main.win);
if(returnVal == JFileChooser.APPROVE_OPTION) {
boolean error = false;
File openFile = choose.getSelectedFile();
try {
FileInputStream fin = new FileInputStream(openFile);
ObjectInputStream ois = new ObjectInputStream(fin);
opener = ois.readObject();
} catch (Exception ex) {
JOptionPane.showMessageDialog(main.win, "Error opening file.");
error = true;
}
if(opener != null && opener instanceof SudokuBase){
main.west.remove(main.symbols);
main.east.remove(main.view);
//add in state information for new board
main.south.remove(main.rowColRegStates);
main.view = new SudokuView((SudokuBase) opener);
main.symbols = new SetSymbols(main.view);
//add in state information for new board
main.rowColRegStates = new ShowStates(main.view);
main.west.add(main.symbols);
main.east.add(main.view);
//add in state information for new board
main.south.add(main.rowColRegStates);
main.win.requestFocus();
fileSaved = openFile;
// main.board.setDirty(false);
} else {
if(error) {
JOptionPane.showMessageDialog(main.win, " Incorrect file type!");
}
}
}
// else: user cancelled
}
else if(e.getSource().equals(newGame)) {
setEnabled(false);
// create dialog box prompting for the new board information
createNewWin = new Dialog1(main, "Create New Board", true);
// make it visible
createNewWin.setVisible(true);
fileSaved = null;
}
}
// This method prompts the user to choose a file to save to,
// and then saves the file.
private int saveAs() {
boolean saveError;
int rtn = choose.showSaveDialog(main.win);
if(rtn == JFileChooser.APPROVE_OPTION) {
saveError = false;
File fileSaveAs = choose.getSelectedFile();
try {
board.writeToStream(new FileOutputStream(fileSaveAs));
} catch (Exception e) {
JOptionPane.showMessageDialog(main.win, "Error saving file.");
saveError = true;
}
if(!saveError) {
fileSaved = fileSaveAs;
// main.board.setDirty(false);
}
}
return rtn;
}
/**
* Asks the user if they want to save before closing if changes were made.
*/
private void closePrompt() {
if(true) { //board.isDirty()) {
int n = JOptionPane.showConfirmDialog(main.win, "Save game?");
if(n == JOptionPane.YES_OPTION) {
int saved = saveAs();
if(saved != JFileChooser.CANCEL_OPTION){
main.win.dispose();
}
}
else if(n == JOptionPane.NO_OPTION) {
main.win.dispose();
}
}
else
main.win.dispose();
}
}
// Allow short name access to following classes
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
import java.io.*;
public class SudokuMain extends JComponent {
/**
* The application method.
*
* @param args The command-line arguments.
*/
public static void main(String[] args) {
new SudokuMain();
}
// this field refers to the SudokuBase class to access information
// of the board.
private SudokuBase board;
// this field refers to SudokuView object to access its information
// and provide output
private SudokuView view;
// the window all the components are contained in
private JFrame win;
// center JPanel object in window
private JPanel center;
// left JPanel object in window
private JPanel west;
// right JPanel object in window
private JPanel east;
// bottom JPanel object in window
private JPanel south;
// JPanel object to hold graphic "buttons"
private JPanel symbols;
// JPanel object to show "states" of Sudoku board
private JPanel rowColRegStates;
// the first set-up window (cannot be changed once
// instantiated)
private final Dialog1 setWin1;
/**
* Constructs SudokuMain object.
*/
public SudokuMain() {
// start game
board = makeBoard();
view = new SudokuView(board);
win = new JFrame("Sudoku Game");
center = new JPanel();
west = new JPanel();
east = new JPanel();
south = new JPanel();
// graphic "buttons" for current Sudoku board
symbols = new SetSymbols(view);
// "states" of current Sudoku board
rowColRegStates = new ShowStates(view);
// the first set-up window
setWin1 = new Dialog1(this, "New Game", true);
// create menu bar
final MenuAtTop menuBar = new MenuAtTop(this);
win.setJMenuBar(menuBar);
// display game mode
JLabel mode = new JLabel("Normal Play Mode");
mode.setHorizontalAlignment(JLabel.CENTER);
Font modeFont = new Font("Arial", Font.BOLD, 14);
mode.setFont(modeFont);
// // set selected cell at (0, 0)
// view.setSelected(0, 0);
// add window focus listener
win.addWindowFocusListener(new WindowFocusListener() {
public void windowGainedFocus(WindowEvent e) {
win.pack();
// set selected cell at (0, 0)
view.setSelected(0, 0);
}
public void windowLostFocus(WindowEvent e) {
}
});
// add window listener
win.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
menuBar.closePrompt();
}
});
win.setLayout(new BorderLayout());
west.setLayout(new BorderLayout());
east.setLayout(new BorderLayout());
center.setLayout(new FlowLayout());
west.add(symbols);
east.add(view, BorderLayout.CENTER);
south.add(rowColRegStates);
center.add(west);
center.add(east);
win.add(south, BorderLayout.NORTH);
win.add(center, BorderLayout.CENTER);
win.add(mode, BorderLayout.SOUTH);
win.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
win.pack();
win.setVisible(true);
}
// this inner class constructs graphic "buttons" to set desired
// cells of board
class SudokuControlButton extends JPanel {
// row of selected cell
private int selRow;
// column of selected cell
private int selCol;
// the value that corresponds with the desired symbol
private int value;
/**
* Constructs SudokuControlButton object; the graphic "button"
* to control the board.
*
* @param view The SudokuView object to be controlled.
* @param v The value that corresponds to the desired symbol.
*/
public SudokuControlButton(final SudokuView view, int v) {
// set characteristics of graphic "button"
setPreferredSize(new Dimension(50, 50));
setBackground(Color.LIGHT_GRAY);
value = v;
addMouseListener(new MouseListener() {
/**
* This method selects a "button" and puts it in focus when the mouse
* is clicked on it.
*
* @param event Captures information on the mouse button being
* clicked (pressed and released) on a component.
*/
public void mouseClicked(MouseEvent e) {
selRow = view.getSelectedRow();
selCol = view.getSelectedColumn();
if(!board.isGiven(selRow, selCol)) {
board.setValue(selRow, selCol, value);
view.new SudokuCell(selRow, selCol, board);
// set to "highlighted" color
setBackground(Color.WHITE);
view.repaint();
}
else { // have system beep sound
getToolkit().beep();
}
repaint();
}
/**
* This method handles behavior when the mouse enters a graphic
* "button".
*
* @param event Captures information on the mouse button being
* entered over a component.
*/
public void mouseEntered(MouseEvent e){
// set to "highlighted" color
setBackground(Color.WHITE);
repaint();
}
/**
* This method handles behavior when the mouse exits a graphic
* "button".
*
* @param event Captures information on the mouse button being
* exited from a component.
*/
public void mouseExited(MouseEvent e){
// set to default color
SudokuControlButton button = (SudokuControlButton) e.getSource();
setBackground(Color.LIGHT_GRAY);
repaint();
}
/**
* This method handles behavior when the mouse is pressed on a
* graphic "button".
*
* @param event Captures information on the mouse button being
* pressed on a component.
*/
public void mousePressed(MouseEvent e){
// set to "active" color
setBackground(Color.YELLOW);
repaint();
}
/**
* This method handles behavior when the mouse is released on a
* graphic "button".
*
* @param e Captures information on the mouse button being
* released on a component.
*/
public void mouseReleased(MouseEvent e){
}
});
}
/**
* This method draws the graphic "button" associated with
* each numeric value, 0 to 12.
*
* @param g The drawing mechanism.
*/
public void paintComponent(Graphics g) {
super.paintComponent(g);
switch(value) {
case 0:
drawSymbol(g, 0);
break;
case 1:
drawSymbol(g, 1);
break;
case 2:
drawSymbol(g, 2);
break;
case 3:
drawSymbol(g, 3);
break;
case 4:
drawSymbol(g, 4);
break;
case 5:
drawSymbol(g, 5);
break;
case 6:
drawSymbol(g, 6);
break;
case 7:
drawSymbol(g, 7);
break;
case 8:
drawSymbol(g, 8);
break;
case 9:
drawSymbol(g, 9);
break;
case 10:
drawSymbol(g, 10);
break;
case 11:
drawSymbol(g, 11);
break;
case 12:
drawSymbol(g, 12);
break;
}
}
/**
* This method draws the symbol that corresponds with
* the specified value (0-12).
*
* @param g The drawing mechanism.
* @param value The specified value.
*/
public void drawSymbol(Graphics g, int value) {
if(value < 0 || value > 12) {
String msg = "Value cannot be less than 0 or greater than 12.";
throw new IllegalArgumentException(msg);
}
// enable drawing with "thick" lines
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(3));
switch(value) {
case 0:
// draw borders
g.drawRect(0, 0, 50, 50);
break;
case 1:
// draw borders
g.drawRect(0, 0, 50, 50);
// draw symbol
g2.drawLine(5, 5, 5, 45);
break;
case 2:
// draw borders
g.drawRect(0, 0, 50, 50);
// draw symbol
g2.drawLine(5, 5, 5, 45);
g2.drawLine(10, 5, 10, 45);
break;
case 3:
// draw borders
g.drawRect(0, 0, 50, 50);
// draw symbol
g2.drawLine(5, 5, 5, 45);
g2.drawLine(10, 5, 10, 45);
g2.drawLine(15, 5, 15, 45);
break;
case 4:
// draw borders
g.drawRect(0, 0, 50, 50);
// draw symbol
g2.drawLine(5, 5, 5, 45);
g2.drawLine(10, 5, 10, 45);
g2.drawLine(15, 5, 15, 45);
g2.drawLine(20, 5, 20, 45);
break;
case 5:
// draw borders
g.drawRect(0, 0, 50, 50);
// draw symbol
g2.drawLine(5, 5, 5, 45);
g2.drawLine(10, 5, 10, 45);
g2.drawLine(15, 5, 15, 45);
g2.drawLine(20, 5, 20, 45);
g2.drawLine(25, 5, 25, 45);
break;
case 6:
// draw borders
g.drawRect(0, 0, 50, 50);
// draw symbol
g2.drawLine(5, 5, 5, 45);
g2.drawLine(10, 5, 10, 45);
g2.drawLine(15, 5, 15, 45);
g2.drawLine(20, 5, 20, 45);
g2.drawLine(25, 5, 25, 45);
g2.drawLine(30, 5, 30, 45);
break;
case 7:
// draw borders
g.drawRect(0, 0, 50, 50);
// draw symbol
g2.drawLine(5, 5, 5, 20);
g2.drawLine(10, 5, 10, 20);
g2.drawLine(15, 5, 15, 20);
g2.drawLine(20, 5, 20, 20);
g2.drawLine(25, 5, 25, 20);
g2.drawLine(30, 5, 30, 20);
g2.drawLine(5, 30, 5, 45);
break;
case 8:
// draw borders
g.drawRect(0, 0, 50, 50);
// draw symbol
g2.drawLine(5, 5, 5, 20);
g2.drawLine(10, 5, 10, 20);
g2.drawLine(15, 5, 15, 20);
g2.drawLine(20, 5, 20, 20);
g2.drawLine(25, 5, 25, 20);
g2.drawLine(30, 5, 30, 20);
g2.drawLine(5, 30, 5, 45);
g2.drawLine(10, 30, 10, 45);
break;
case 9:
// draw borders
g.drawRect(0, 0, 50, 50);
// draw symbol
g2.drawLine(5, 5, 5, 20);
g2.drawLine(10, 5, 10, 20);
g2.drawLine(15, 5, 15, 20);
g2.drawLine(20, 5, 20, 20);
g2.drawLine(25, 5, 25, 20);
g2.drawLine(30, 5, 30, 20);
g2.drawLine(5, 30, 5, 45);
g2.drawLine(10, 30, 10, 45);
g2.drawLine(15, 30, 15, 45);
break;
case 10:
// draw borders
g.drawRect(0, 0, 50, 50);
// draw symbol
g.drawLine(5, 5, 5, 20);
g.drawLine(10, 5, 10, 20);
g.drawLine(15, 5, 15, 20);
g.drawLine(20, 5, 20, 20);
g.drawLine(25, 5, 25, 20);
g.drawLine(30, 5, 30, 20);
g.drawLine(5, 30, 5, 45);
g.drawLine(10, 30, 10, 45);
g.drawLine(15, 30, 15, 45);
g.drawLine(20, 30, 20, 45);
break;
case 11:
// draw borders
g.drawRect(0, 0, 50, 50);
// draw symbol
g.drawLine(5, 5, 5, 20);
g.drawLine(10, 5, 10, 20);
g.drawLine(15, 5, 15, 20);
g.drawLine(20, 5, 20, 20);
g.drawLine(25, 5, 25, 20);
g.drawLine(30, 5, 30, 20);
g.drawLine(5, 30, 5, 45);
g.drawLine(10, 30, 10, 45);
g.drawLine(15, 30, 15, 45);
g.drawLine(20, 30, 20, 45);
g.drawLine(25, 30, 25, 45);
break;
case 12:
// draw borders
g.drawRect(0, 0, 50, 50);
// draw symbol
g.drawLine(5, 5, 5, 20);
g.drawLine(10, 5, 10, 20);
g.drawLine(15, 5, 15, 20);
g.drawLine(20, 5, 20, 20);
g.drawLine(25, 5, 25, 20);
g.drawLine(30, 5, 30, 20);
g.drawLine(5, 30, 5, 45);
g.drawLine(10, 30, 10, 45);
g.drawLine(15, 30, 15, 45);
g.drawLine(20, 30, 20, 45);
g.drawLine(25, 30, 25, 45);
g.drawLine(30, 30, 30, 45);
break;
}
}
}
// this inner class provides a JMenuBar object at the top of
// the board
class MenuAtTop extends JMenuBar implements ActionListener{
// SudokuMain object we are dealing with
private SudokuMain main;
// the "File" menu
private JMenu fileMenu;
// the "New Game" option
private JMenuItem newGame;
// the "Open" option
private JMenuItem open;
// the "Save" option
private JMenuItem save;
// the "Save As" option
private JMenuItem saveAs;
// the "Reset" option
private JMenuItem reset;
// the "Quit" option
private JMenuItem quit;
// the ability to choose files
private JFileChooser choose;
// the saved file
// // compiler would not allow "static" keyword
private File fileSaved = null;
private Object opener;
// JDialog object to create a dialog box to prompt
// user for new game information
private JDialog createNewWin;
/**
* Constructs MenuAtTop object.
*
* @param m The SudokuMain object to be referred to.
*/
public MenuAtTop(final SudokuMain m) {
main = m;
opener = null;
choose = new JFileChooser();
// instantiate and bind to reference
fileMenu = new JMenu("File");
add(fileMenu);
// instantiate and bind to reference
newGame = new JMenuItem("New Game");
newGame.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
ActionEvent.CTRL_MASK));
fileMenu.add(newGame);
newGame.addActionListener(this);
open = new JMenuItem("Open");
open.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O,
ActionEvent.CTRL_MASK));
fileMenu.add(open);
// add action listener to "Open" option
open.addActionListener(this);
save = new JMenuItem("Save");
save.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
ActionEvent.CTRL_MASK));
fileMenu.add(save);
// add action listener to "Save" option
save.addActionListener(this);
saveAs = new JMenuItem("Save As");
saveAs.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,
ActionEvent.CTRL_MASK));
fileMenu.add(saveAs);
// add action listener to "Save As" option
saveAs.addActionListener(this);
reset = new JMenuItem("Reset");
reset.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R,
ActionEvent.CTRL_MASK));
fileMenu.add(reset);
// add action listener to "Reset" option
reset.addActionListener(this);
quit = new JMenuItem("Quit");
quit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
ActionEvent.CTRL_MASK));
fileMenu.add(quit);
// add action listener to "Quit" option
quit.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
if(e.getSource().equals(newGame)) {
setEnabled(false);
// create dialog box prompting for the new board information
createNewWin = new Dialog1(main, "Create New Board", true);
// make it visible
createNewWin.setVisible(true);
fileSaved = null;
} else if(e.getSource().equals(open)) {
int returnVal = choose.showOpenDialog(main.win);
if(returnVal == JFileChooser.APPROVE_OPTION) {
boolean error = false;
File openFile = choose.getSelectedFile();
try {
FileInputStream fin = new FileInputStream(openFile);
ObjectInputStream ois = new ObjectInputStream(fin);
opener = ois.readObject();
} catch (Exception exc) {
JOptionPane.showMessageDialog(main.win, "Error opening file.");
error = true;
}
// "opener" reads something and it is of type SudokuBase
if(opener != null && opener instanceof SudokuBase){
main.west.remove(main.symbols);
main.east.remove(main.view);
main.south.remove(main.rowColRegStates);
main.view = new SudokuView((SudokuBase) opener);
main.symbols = new SetSymbols(main.view);
main.rowColRegStates = new ShowStates(main.view);
main.west.add(main.symbols);
main.east.add(main.view);
main.south.add(main.rowColRegStates);
main.win.requestFocus();
fileSaved = openFile;
} else {
if(error) {
JOptionPane.showMessageDialog(main.win, "Incorrect file type.");
}
}
}
} else if(e.getSource().equals(save)) {
if(fileSaved == null) {
saveAsPrompt();
} else {
try {
board.writeToStream(new FileOutputStream(fileSaved));
} catch (Exception exc) {
JOptionPane.showMessageDialog(main.win, "Error saving file.");
}
}
} else if(e.getSource().equals(saveAs)) {
saveAsPrompt();
} else if(e.getSource().equals(reset)) {
int n = JOptionPane.showConfirmDialog(main.win,
"Any player values will" +
" be lost. Proceed?",
"Warning!", 2);
if(n == JOptionPane.OK_OPTION) {
main.board.reset();
main.view.repaint();
}
} else if(e.getSource().equals(quit)) {
closePrompt();
}
}
// This method prompts the user to choose a file to save to,
// and then saves the file.
private int saveAsPrompt() {
boolean saveError;
int rtn = choose.showSaveDialog(main.win);
if(rtn == JFileChooser.APPROVE_OPTION) {
saveError = false;
File fileSaveAs = choose.getSelectedFile();
try {
board.writeToStream(new FileOutputStream(fileSaveAs));
} catch (Exception e) {
JOptionPane.showMessageDialog(main.win, "Error saving file.");
saveError = true;
}
if(!saveError) {
fileSaved = fileSaveAs;
}
}
return rtn;
}
// This method prompts the user whether they want to save before
// closing, only if changes occurred.
private void closePrompt() {
if(true) {
int n = JOptionPane.showConfirmDialog(main.win, "Save game?");
if(n == JOptionPane.YES_OPTION) {
int saved = saveAsPrompt();
if(saved != JFileChooser.CANCEL_OPTION){
main.win.dispose();
}
} else if(n == JOptionPane.NO_OPTION) {
main.win.dispose();
}
}
else { // no changes were made
main.win.dispose();
}
}
}
// this inner class provides a dialog box to prompt the user
// for new board information
class Dialog1 extends JDialog {
// rows for new game
private JTextField rows;
// cols for new game
private JTextField cols;
// button to create a new board
private JButton createBoard;
// button to cancel new board and return to
// previous game
private JButton cancel;
// labels for rows per region
private JLabel rowLabel;
// label for columns per region
private JLabel colLabel;
// label dislayed when error occurs
private JLabel errorMes;
// JPanel object to house error message
private JPanel center;
// JPanel object to house rows and columns prompt
private JPanel north;
// JPanel object to house create new board and cancel buttons
private JPanel south;
// JDialog object to create window for new game
private JDialog createWin2;
/**
* Constructs Dialog1 object.
*
* @param win The window containing the dialog box.
* @param header The title of the dialog box.
* @param modal Whether dialog box is modal or not.
*/
public Dialog1(final SudokuMain win, String header, boolean modal) {
// call superclass constructor
super();
// instantiate and bind to references
rows = new JTextField(2);
cols = new JTextField(2);
createBoard = new JButton("Create New Board");
cancel = new JButton("Cancel");
rowLabel = new JLabel("Rows per region: ");
colLabel = new JLabel("Columns per region: ");
errorMes = new JLabel();
north = new JPanel(new FlowLayout());
center = new JPanel(new FlowLayout());
south = new JPanel(new FlowLayout());
// set characteristics
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
setTitle(header);
setModal(modal);
setLayout(new BorderLayout());
// set characteristics of error message
errorMes.setForeground(Color.RED);
errorMes.setFont(new Font("Arial", Font.ITALIC, 12));
errorMes.setVisible(false);
// keep track of "old" board
final SudokuBase oldBoard = board;
// add action listener for "Cancel" button
cancel.addActionListener(new ActionListener() {
/**
* This method handles the action of activating the
* "Cancel" button to make the dialog box "invisible".
*
* @param e Captures information about the event that occurred.
*/
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
});
// add action listener for "Create Board" button
createBoard.addActionListener(new ActionListener() {
/**
* This method handles the action of activating the
* "Cancel" button to make the dialog box "invisible".
*
* @param e Captures information about the event that occurred.
*/
public void actionPerformed(ActionEvent e) {
int newRows;
int newCols;
int newSize;
// handles potential exception when converting String input
// to int
try{
newRows = Integer.parseInt(rows.getText());
newCols = Integer.parseInt(cols.getText());
} catch (NumberFormatException nfe) {
newRows = 0;
newCols = 0;
}
newSize = newRows * newCols;
// input validation
if(newSize <= 0 || newSize > 12) {
errorMes.setText("Rows times columns cannot be less than one" +
" or greater than 12!");
errorMes.setVisible(true);
pack();
} else {
errorMes.setVisible(false);
setVisible(false);
// update board to new board
board = new SudokuBoard(newRows, newCols);
createWin2 = new Dialog2(win, oldBoard, view, symbols, newRows,
newCols, "New Sudoku Game", true);
}
}});
// place error message in the center
center.add(errorMes);
// place labels for rows and columns at the top
north.add(rowLabel);
north.add(rows);
north.add(colLabel);
north.add(cols);
// place both buttons at bottom
south.add(createBoard);
south.add(cancel);
add(center, BorderLayout.CENTER);
add(north, BorderLayout.NORTH);
add(south, BorderLayout.SOUTH);
pack();
if(!win.win.isVisible()) {
dispose();
}
}
}
// this inner class a dialog box to house a new game
class Dialog2 extends JDialog {
// view to be used
private SudokuView view;
// the panel to house the board (view) and both the
// "Set givens" and "Cancel" buttons
private JPanel panel;
// panel placed within "panel" that houses both the "Set givens"
// and "Cancel" buttons
private JPanel northPanel;
// panel to house the graphic "buttons"
private JPanel symbols;
// "Set givens" button
private JButton setGivenCells;
// "Cancel" button
private JButton cancel;
/**
* Constructs Dialog2 object.
*
* @param win The window containing the dialog box.
* @param oldBoard The "old" SudokuBoard to keep track of.
* @param oldView The "old" SudokuView to keep track of.
* @param oldSymbols The "old" graphic "buttons" to keep track of.
* @param rows The rows of the new Sudoku board to be created.
* @param cols The columns of the new Sudoku board to be created.
* @param header The title of the dialog box.
* @param modal Whether the dialog box is modal or not.
*/
public Dialog2(final SudokuMain mainWin, final SudokuBase oldBoard,
final SudokuView oldView, final JPanel oldSymbols,
int rows, int cols, String header, boolean modal) {
// call superclass constructor
super();
// instantiate and bind to references
view = new SudokuView(board);
panel = new JPanel();
northPanel = new JPanel();
setGivenCells = new JButton("Set givens");
cancel = new JButton("Cancel");
symbols = new SetSymbols(view);
// create menu bar
final MenuAtTop menuBar = new MenuAtTop(mainWin);
setJMenuBar(menuBar);
// display "Set-Up Mode"
final JLabel setupMode = new JLabel("Set-Up Mode");
setupMode.setHorizontalAlignment(JLabel.CENTER);
Font setupModeFont = new Font("Comic Sans MS", Font.BOLD, 18);
setupMode.setFont(setupModeFont);
setupMode.setForeground(Color.RED);
// display "Normal Play Mode"
final JLabel mode = new JLabel("Normal Play Mode");
mode.setHorizontalAlignment(JLabel.CENTER);
Font modeFont = new Font("Arial", Font.BOLD, 14);
mode.setFont(modeFont);
// set up characteristics
setTitle(header);
setModal(modal);
setLayout(new FlowLayout());
panel.setLayout(new BorderLayout());
northPanel.setLayout(new FlowLayout());
// add action listener to "Set givens" button
setGivenCells.addActionListener(new ActionListener() {
/**
* This method handles the action of activating the
* "Set givens" button.
*
* @param e Captures information about the event that occurred.
*/
public void actionPerformed(ActionEvent e) {
// set "given" cells
board.fixGivens();
// // have window refer to new board
// mainWin.west.remove(mainWin.symbols);
// mainWin.east.remove(mainWin.view);
//
// mainWin.view = view;
// mainWin.symbols = symbols;
//
// mainWin.west.add(mainWin.symbols);
// mainWin.east.add(mainWin.view);
// remove "Set-Up Mode" label and replace with
// "Normal Play Mode" label
panel.remove(setupMode);
panel.add(mode, BorderLayout.SOUTH);
// disable both buttons
setGivenCells.setEnabled(false);
cancel.setEnabled(false);
validate();
repaint();
}
});
// add action listener to "Cancel" button
cancel.addActionListener(new ActionListener() {
/**
* This method handles the action of activating the
* "Cancel" button.
*
* @param e Captures information about the event that occurred.
*/
public void actionPerformed(ActionEvent e) {
// have window refer to "old" board
board = oldBoard;
mainWin.west.remove(mainWin.symbols);
mainWin.east.remove(mainWin.view);
mainWin.view = oldView;
mainWin.symbols = oldSymbols;
mainWin.west.add(mainWin.symbols);
mainWin.east.add(mainWin.view);
// disable both buttons
setGivenCells.setEnabled(false);
cancel.setEnabled(false);
setVisible(false);
repaint();
}
});
// place buttons at the top
northPanel.add(setGivenCells);
northPanel.add(cancel);
// place board to fill remainder of space not
// occupied by buttons at the top
panel.add(view, BorderLayout.CENTER);
panel.add(northPanel, BorderLayout.NORTH);
panel.add(setupMode, BorderLayout.SOUTH);
// place graphic "buttons" to left of board
add(symbols);
add(panel);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
pack();
setVisible(true);
}
}
// this inner class creates the graphic "buttons" to set the selected cell
// to the desired "button"
class SetSymbols extends JPanel {
// temporary board provides information to create graphic "buttons"
private SudokuBoard tempBd;
private int value;
/**
* Constructs SetSymbols object.
*
* @param view The SudokuView object for SetSymbols.
*/
public SetSymbols(final SudokuView view) {
// instantiate and bind to reference
tempBd = new SudokuBoard(1, board.getBoardSize() + 1);
setLayout(new GridLayout((tempBd.getBoardSize())/2 + 1, 2));
for(int colSymbol = 0; colSymbol < tempBd.getBoardSize(); colSymbol++) {
// keep track of value of graphic "button"
value = colSymbol;
final JPanel symPanel = new JPanel();
// set value for each graphic "button"
tempBd.setValue(0, colSymbol, colSymbol);
// add the appropriate symbol to each graphic "button"
symPanel.add(new SudokuControlButton(view, value));
// add graphic "button"
add(symPanel);
}
}
/**
* Draws the symbol associated with each
* numeric value (0 to 12) on the non-given
* selected cell.
*
* @param g The drawing mechanism.
*/
public void paintComponent(Graphics g) {
super.paintComponent(g);
// get selected cell information from SudokuView reference
int row = view.getSelectedRow();
int col = view.getSelectedColumn();
switch(value) {
case 0:
view.new SudokuCell(row, col, board).drawSymbol(g, row, col);
break;
case 1:
view.new SudokuCell(row, col, board).drawSymbol(g, row, col);
break;
case 2:
view.new SudokuCell(row, col, board).drawSymbol(g, row, col);
break;
case 3:
view.new SudokuCell(row, col, board).drawSymbol(g, row, col);
break;
case 4:
view.new SudokuCell(row, col, board).drawSymbol(g, row, col);
break;
case 5:
view.new SudokuCell(row, col, board).drawSymbol(g, row, col);
break;
case 6:
view.new SudokuCell(row, col, board).drawSymbol(g, row, col);
break;
case 7:
view.new SudokuCell(row, col, board).drawSymbol(g, row, col);
break;
case 8:
view.new SudokuCell(row, col, board).drawSymbol(g, row, col);
break;
case 9:
view.new SudokuCell(row, col, board).drawSymbol(g, row, col);
break;
case 10:
view.new SudokuCell(row, col, board).drawSymbol(g, row, col);
break;
case 11:
view.new SudokuCell(row, col, board).drawSymbol(g, row, col);
break;
case 12:
view.new SudokuCell(row, col, board).drawSymbol(g, row, col);
break;
}
}
}
// this inner class displays the state information for each row, column, and region
class ShowStates extends JPanel{
// the SudokuView object to be used
private SudokuView view;
// JPanel for row state
private JPanel rowSt;
// JPanel for column state
private JPanel columnSt;
// JPanel for region state
private JPanel regionSt;
// displays row state
private JPanel[] rowStColor;
// displays column state
private JPanel[] columnStColor;
// displays region state
private JPanel[] regionStColor;
// number of rows per region
private int rows;
// number of columns per region
private int columns;
// size per region (rows * columns)
private int size;
/**
* Constructs the ShowStates object.
*
* @param view The SudokuView object for ShowStates.
*/
public ShowStates(SudokuView v) {
// bind to references
rows = board.getRowsPerRegion();
columns = board.getColumnsPerRegion();
size = rows * columns;
// bind to reference
view = v;
// row characteristics
rowSt = new JPanel();
rowSt.setLayout(new GridLayout(size, 1));
rowStColor = new JPanel[size];
rowSt.setPreferredSize(new Dimension(50, 50));
rowSt.setBorder(BorderFactory.createLineBorder(Color.BLACK));
// column characteristics
columnSt = new JPanel();
columnSt.setLayout(new GridLayout(1, size));
columnStColor = new JPanel[size];
columnSt.setPreferredSize(new Dimension(50, 50));
columnSt.setBorder(BorderFactory.createLineBorder(Color.BLACK));
// region characteristics
regionSt = new JPanel();
regionSt.setLayout(new GridLayout(rows, columns));
regionStColor = new JPanel[size];
regionSt.setPreferredSize(new Dimension(50, 50));
regionSt.setBorder(BorderFactory.createLineBorder(Color.BLACK));
for(int i = 0; i < size; i++) {
// instantiate and bind to references
JPanel rowPanel = new JPanel();
JPanel columnPanel = new JPanel();
JPanel regionPanel = new JPanel();
rowStColor[i] = rowPanel;
columnStColor[i] = columnPanel;
regionStColor[i] = regionPanel;
// add to respective JPanel objects
rowSt.add(rowPanel);
columnSt.add(columnPanel);
regionSt.add(regionPanel);
}
// add each state to "this" JPanel
add(rowSt);
add(columnSt);
add(regionSt);
}
/**
* This method draws the appropriate color to display the state information.
*
* @param g The drawing mechanism.
*/
public void paintComponent(java.awt.Graphics g) {
super.paintComponent(g);
for(int i = 0; i < rows * columns; i++) {
// check the state of each row
if(board.getRowState(i) == SudokuBase.State.ERROR){
rowStColor[i].setBackground(Color.RED);
}
else if(board.getRowState(i) == SudokuBase.State.INCOMPLETE) {
rowStColor[i].setBackground(Color.YELLOW);
}
else { // board.getRowState(i) == SudokuBase.State.COMPLETE
rowStColor[i].setBackground(Color.GREEN);
}
// check the state of each column
if(board.getColumnState(i) == SudokuBase.State.ERROR){
columnStColor[i].setBackground(Color.RED);
}
else if(board.getColumnState(i) == SudokuBase.State.INCOMPLETE) {
columnStColor[i].setBackground(Color.YELLOW);
}
else { // board.getColumnState(i) == SudokuBase.State.COMPLETE
columnStColor[i].setBackground(Color.GREEN);
}
// check the state of each region
if(board.getRegionState(i) == SudokuBase.State.ERROR){
regionStColor[i].setBackground(Color.RED);
}
else if(board.getRegionState(i) == SudokuBase.State.INCOMPLETE) {
regionStColor[i].setBackground(Color.YELLOW);
}
else { // board.getRegionState(i) == SudokuBase.State.COMPLETE
regionStColor[i].setBackground(Color.GREEN);
}
}
}
}
/**
* This method provides a pre-set board to start with in
* "Normal Play" mode.
*
* @return The board with the "givens" already set.
*/
public static SudokuBase makeBoard() {
SudokuBase board = new SudokuBoard(2, 3);
board.setValue(0, 3, 6);
board.setValue(0, 5, 1);
board.setValue(1, 2, 4);
board.setValue(1, 4, 5);
board.setValue(1, 5, 3);
board.setValue(2, 3, 3);
board.setValue(3, 2, 6);
board.setValue(4, 0, 2);
board.setValue(4, 1, 3);
board.setValue(4, 3, 1);
board.setValue(5, 0, 6);
board.setValue(5, 2, 1);
board.fixGivens();
return board;
}
}