Java 数独游戏,序列化问题

Java 数独游戏,序列化问题,java,serialization,Java,Serialization,我正在创建数独游戏,并尝试提供保存、另存为和打开游戏的选项。我正在使用JFileChooser来完成此操作。我可以保存(或“另存为”),但当我试图打开保存的文件时,会出现错误。我是编程新手,我希望有人能发现这个问题,并教我如何在保存时阅读数独板的内容(以及如何在打开文件时重新创建数独板)。我听说使用InputStream/OutputStream而不是Reader/Writer可以更容易地处理这个问题 这是我的内部类代码,它实现了这一点(我不知道是否有办法在不超过此文本框的字符限制的情况下发布整

我正在创建数独游戏,并尝试提供保存、另存为和打开游戏的选项。我正在使用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;
  }

}