2018Java小作业

自己写的,由于题目要求十分基础,整个作业下来基本就是体力活了。

下面是代码:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.*;
import java.nio.file.NoSuchFileException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.text.AbstractDocument;

public class Additor {
    public static void main(String[] args) {
        new MainFrame();
    }

    /**
     * 控制台调试用代码
     */
    private static void testCode() {
        FixedPointNumber fpn = new FixedPointNumber(-12300, 3);
        System.out.println(fpn);
        FixedPointNumber b = new FixedPointNumber(246, 1);
        System.out.println(fpn.subtract(b));
        System.out.println(fpn.add(b));
        System.out.println(b.subtract(fpn));
        try {
            System.out.println(fpn.format(2));
            System.out.println(fpn.format(4));
            System.out.println(fpn.format(0));
            System.out.println(fpn.format(-1));
        } catch (NotValidFormatException e) {
            e.printStackTrace();
        }
        ArrayList<FixedPointNumber[]> list = DataGenerator.generate(10);
        for (FixedPointNumber[] x : list) {
            System.out.println(x[0] + " + " + x[1] + " = " + x[2]);
        }
        try {
            list = DataGenerator.loadData("D:\\Program Files\\Additor_1_0\\asset\\ques\\20181120-07-31-00.quesdat");
            for (FixedPointNumber[] x : list) {
                System.out.println(x[0] + " + " + x[1] + " = " + x[2]);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        DataGenerator.update(100, 10);
        int[] dat = DataGenerator.queryProfile();
        System.out.println("Num : " + dat[0] + "\ncorrect: " + dat[1]);
        DataGenerator.update(2, 2);
        dat = DataGenerator.queryProfile();
        System.out.println("Num : " + dat[0] + "\ncorrect: " + dat[1]);
    }
}

/**
 * 定点数类,实现定点数的构造、随机生成、加法、减法、格式化。
 */
class FixedPointNumber implements Comparable<FixedPointNumber>, Serializable {
    private int value;
    private int pointPos;
    private static int seed;
    private boolean isNegative;

    /*
     * 静态初始化块初始化随机数种子
     */
    static {
        seed = (int) (Math.random() * 1000 + 1);
    }

    /**
     * 伪随机数生成器。生成含有pointPos位小数的定点数。 使用伪随机数公式 x = (a * seed + offset) % mod生成随机数。
     * Note: 此版本仅支持生成非负数
     *
     * @param pointPos
     *            小数点位置
     * @param lowerBound
     *            生成数的下界
     * @param upperBound
     *            生成数的上界
     * @return FixedPointNumber 返回构造出的定点数
     */
    public static FixedPointNumber random(int pointPos, int lowerBound, int upperBound) {
        int mod = upperBound - lowerBound;
        for (int i = 0; i < pointPos; i++) {
            mod *= 10;
            lowerBound *= 10;
        }
        seed = (17 * seed + 13) % mod + lowerBound;
        return new FixedPointNumber(seed, pointPos);
    }

    private FixedPointNumber() {
        this.pointPos = 0;
        this.value = 0;
        this.isNegative = false;
    }

    /**
     * 拷贝构造函数。实现深层复制。
     *
     * @param fpn
     *            要被拷贝的对象。
     */
    FixedPointNumber(FixedPointNumber fpn) {
        this.value = fpn.value;
        this.isNegative = fpn.isNegative;
        this.pointPos = fpn.pointPos;
    }

    /**
     * 含有两个参数的构造方法,用来构造一个定点数。 pointPos = 0表示小数点在最右端,此时这个数就是一个整数。
     *
     * @param value
     *            存储的数
     * @param pointPos
     *            小数点位置
     */
    FixedPointNumber(int value, int pointPos) {
        if (value < 0) {
            value = -value;
            isNegative = true;
        } else {
            isNegative = false;
        }
        this.value = value;
        this.pointPos = pointPos;
    }

    /**
     * 使用字符串表示的小数来构造定点数对象的构造器。
     *
     * @param str
     *            使用字符串表示的小数
     * @throws NotValidPointNumberException
     *             当作为参数的字符串不是一个合法的小数时抛出此异常
     */
    FixedPointNumber(String str) throws NotValidPointNumberException {
        int startPos = 0;
        if (str.charAt(0) == '-') {
            this.isNegative = true;
            startPos = 1;
        } else {
            this.isNegative = false;
        }
        // 检查数据有效性
        // if the str is "-"
        if (startPos == str.length())
            throw new NotValidPointNumberException();
        for (int i = startPos; i < str.length(); i++) {
            if (!Character.isDigit(str.charAt(i)) && str.charAt(i) != '.')
                throw new NotValidPointNumberException();
        }
        int pos = str.indexOf('.');
        // 如果存在两个及以上数目小数点,也要抛出异常
        // e.g. "12.3.56"
        if (str.lastIndexOf('.') != pos)
            throw new NotValidPointNumberException();
        this.pointPos = pos >= 0 ? str.length() - pos - 1 : 0;
        if (pos == startPos)
            str = str.substring(1);
        else if (pos == str.length() - 1)
            str = str.substring(startPos, str.length() - 1);
        else if (pos >= 1)
            str = str.substring(startPos, pos) + str.substring(pos + 1, str.length());
        this.value = Integer.parseInt(str);
    }

    /**
     * 计算两个定点数的相加。 需要将小数点位置对齐。
     *
     * @param fpn
     *            加数
     * @return FixedPointNumber 返回两数相加的结果
     * @throws NullPointerException
     *             当传入参数为null时抛出
     */
    FixedPointNumber add(FixedPointNumber fpn) throws NullPointerException {
        if (fpn == null)
            throw new NullPointerException();
        FixedPointNumber ret = new FixedPointNumber();
        FixedPointNumber a = new FixedPointNumber(this);
        FixedPointNumber b = new FixedPointNumber(fpn);
        ret.pointPos = Math.max(a.pointPos, b.pointPos);
        if (a.pointPos < ret.pointPos) {
            int t = ret.pointPos - a.pointPos;
            for (int i = 0; i < t; i++)
                a.value *= 10;
        }
        if (b.pointPos < ret.pointPos) {
            int t = ret.pointPos - b.pointPos;
            for (int i = 0; i < t; i++)
                b.value *= 10;
        }
        if (a.isNegative)
            a.value = -a.value;
        if (b.isNegative)
            b.value = -b.value;
        ret.value = a.value + b.value;
        if (ret.value < 0) {
            ret.value = -ret.value;
            ret.isNegative = true;
        } else {
            ret.isNegative = false;
        }
        return ret;
    }

    /**
     * 实现两个定点数的减法 需要将小数点位置对齐
     *
     * @param fpn
     *            减数
     * @return FixedPointNumber 返回两数相减的结果
     * @throws NullPointerException
     *             减数传入null时抛出此异常
     */
    FixedPointNumber subtract(FixedPointNumber fpn) throws NullPointerException {
        if (fpn == null)
            throw new NullPointerException();
        FixedPointNumber ret = new FixedPointNumber();
        FixedPointNumber a = new FixedPointNumber(this);
        FixedPointNumber b = new FixedPointNumber(fpn);
        ret.pointPos = Math.max(a.pointPos, b.pointPos);
        if (a.pointPos < ret.pointPos) {
            int t = ret.pointPos - a.pointPos;
            for (int i = 0; i < t; i++)
                a.value *= 10;
        }
        if (b.pointPos < ret.pointPos) {
            int t = ret.pointPos - b.pointPos;
            for (int i = 0; i < t; i++)
                b.value *= 10;
        }
        if (a.isNegative)
            a.value = -a.value;
        if (b.isNegative)
            b.value = -b.value;
        ret.value = a.value - b.value;
        if (ret.value < 0) {
            ret.value = -ret.value;
            ret.isNegative = true;
        } else {
            ret.isNegative = false;
        }
        return ret;
    }

    /**
     * 小数位数格式化方法
     *
     * @param pointPos
     * @return FixedPointNumber 返回格式化后的定点数
     * @throws NotValidFormatException
     *             格式不规范,参数小于0时抛出此异常
     */
    public FixedPointNumber format(int pointPos) throws NotValidFormatException {
        if (pointPos < 0)
            throw new NotValidFormatException();
        if (pointPos == this.pointPos) {
            return new FixedPointNumber(this);
        } else if (pointPos < this.pointPos) {
            int t = this.pointPos - pointPos;
            int newVal = this.value;
            while (t-- != 0)
                newVal /= 10;
            return new FixedPointNumber(newVal, pointPos);
        } else {
            int t = pointPos - this.pointPos;
            int newVal = this.value;
            while (t-- != 0)
                newVal *= 10;
            return new FixedPointNumber(newVal, pointPos);
        }
    }

    /**
     * 重载equals方法。
     *
     * @param fpn
     *            被比较的对象
     * @return Boolean 相同返回真,不同返回假
     */
    @Override
    public boolean equals(Object fpn) {
        if (fpn == null)
            return false;
        if (fpn.getClass() != this.getClass())
            return false;
        FixedPointNumber tmp = new FixedPointNumber((FixedPointNumber) fpn);
        FixedPointNumber self = new FixedPointNumber(this);
        if (tmp.isNegative != self.isNegative)
            return false;
        while (tmp.value % 10 == 0) {
            tmp.value /= 10;
            tmp.pointPos--;
        }
        return this.pointPos == tmp.pointPos && this.value == tmp.value;
    }

    /**
     * 重载hash值方法。 保证对于所有调用equals方法返回为真的对象具有相同的hash值。 反之具有不同的hash值。
     *
     * @return int 返回此对象的hash值。
     */
    @Override
    public int hashCode() {
        int tmpvalue = this.value;
        int tmppos = this.pointPos;
        while (tmpvalue % 10 == 0) {
            tmpvalue /= 10;
            tmppos--;
        }
        return Objects.hash(tmpvalue, tmppos, isNegative);
    }

    /**
     * 重载toString方法
     *
     * @return String 此对象的字符串表示。
     */
    @Override
    public String toString() {
        if (pointPos == 0)
            return (this.isNegative ? "-" : "") + value;
        int rate = 1;
        for (int i = 0; i < pointPos; i++) {
            rate *= 10;
        }
        String ret = this.isNegative ? "-" : "";
        ret += value / rate + ".";
        String tmp = "";
        int decimal = value % rate;
        int cnt = 0;
        while (decimal != 0) {
            tmp += decimal % 10;
            decimal /= 10;
            cnt++;
        }
        for (int i = cnt; i < pointPos; i++)
            ret += "0";
        for (int i = tmp.length() - 1; i >= 0; i--)
            ret += tmp.charAt(i);
        return ret;
    }

    /**
     * 重载比较函数
     */
    @Override
    public int compareTo(FixedPointNumber fpn) {
        FixedPointNumber a = new FixedPointNumber(this);
        FixedPointNumber b = new FixedPointNumber(fpn);
        int pos = Math.max(a.pointPos, b.pointPos);
        if (a.pointPos < pos) {
            int t = pos - a.pointPos;
            for (int i = 0; i < t; i++)
                a.value *= 10;
        }
        if (b.pointPos < pos) {
            int t = pos - b.pointPos;
            for (int i = 0; i < t; i++)
                b.value *= 10;
        }
        if (a.isNegative)
            a.value = -a.value;
        if (b.isNegative)
            b.value = -b.value;
        return Integer.compare(a.value, b.value);
    }

    public int getValue() {
        return (this.isNegative ? -1 : 1) * value;
    }
}

class NotValidPointNumberException extends Exception {}
class NotValidFormatException extends Exception {}

class DataGenerator {

    /**
     * 隐藏构造器,禁止构造此类对象
     */
    private DataGenerator() {}

    /**
     * 用来随机生成问题
     * @param num 生成问题的数量
     * @return 使用ArrayList存储的问题题集
     */
    public static ArrayList<FixedPointNumber[]> generate(int num) {
        ArrayList ret = new ArrayList<FixedPointNumber[]>();
        // 题目应当是一个三元组,位置0和1上是题目的两个加数,2上式正确结果
        FixedPointNumber[] dat = new FixedPointNumber[3];
        try {
            while (num-- != 0) {
                dat[2] = FixedPointNumber.random(2, 20, 100).format(2);
                dat[0] = FixedPointNumber.random(2, 10, dat[2].getValue() / 100 / 2).format(2);
                dat[1] = dat[2].subtract(dat[0]).format(2);
                ret.add(dat.clone());
            }
        } catch (NotValidFormatException e) {
            e.printStackTrace();
        }
        saveData(ret);
        return ret;
    }

    /**
     * 保存题组
     * @param list 需要序列化的题组
     * @return 为真表示成功保存,为假表示保存失败
     */
    public static boolean saveData(ArrayList<FixedPointNumber[]> list) {
        try {
            String to = "D:\\Program Files\\Additor_1_0\\asset\\ques\\";
            File f = new File(to);
            if (!f.exists()) f.mkdirs();
            to += new SimpleDateFormat("yyyyMMdd-hh-mm-ss").format(new Date())
                    + ".quesdat";
            f = new File(to);
            if (!f.exists()) f.createNewFile();
            FileOutputStream fos = new FileOutputStream(f);
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(list);
            oos.close();
            fos.close();
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * 题集载入
     * @param src
     * @return ArraList存储的题集
     * @throws java.io.FileNotFoundException 文件不存在
     * @throws java.io.IOException
     * @throws ClassNotFoundException
     */
    public static ArrayList<FixedPointNumber[]> loadData(String src)
            throws java.io.FileNotFoundException, java.io.IOException, ClassNotFoundException {
        File f = new File(src);
        FileInputStream fis = new FileInputStream(src);
        ObjectInputStream ois = new ObjectInputStream(fis);
        ArrayList<FixedPointNumber[]> ret = (ArrayList<FixedPointNumber[]>)ois.readObject();
        return ret;
    }

    /**
     * 更新当前成绩到文件中
     * @param num 新增加的题数
     * @param correct 新正确的题数
     * @return
     */
    public static boolean update(int num, int correct) {
        int prenum = 0, precnt = 0;
        String to = "D:\\Program Files\\Additor_1_0\\asset";
        try {
            File f = new File(to);
            if (!f.exists()) f.mkdirs();
            to += "\\profile.dat";
            f = new File(to);
            if (!f.exists()) {
                f.createNewFile();
            } else {
                int[] dat = queryProfile();
                prenum = dat[0];
                precnt = dat[1];
            }
            prenum += num;
            precnt += correct;
            FileWriter fw = new FileWriter(f);
            fw.append("All: ");
            fw.append(new Integer(prenum).toString());
            fw.append("\r\nCorrect: ");
            fw.append(new Integer(precnt).toString());
            fw.append("\r\n");
            fw.flush();
            fw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 读取以前的成绩
     * @return 两个元素的数组,0号位置存储总题数,1号位置存储正确数
     */
    public static int[] queryProfile() {
        int[] ret = new int[2];
        try {
            Scanner in = new Scanner(new File("D:\\Program Files\\Additor_1_0\\asset\\profile.dat"));
            in.next();
            ret[0] = in.nextInt();
            in.next();
            ret[1] = in.nextInt();
            in.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return ret;
    }
}

class PaintPanel extends JComponent implements Runnable, KeyListener {

    /**
     * 大小
     */
    private int xWindowSize;
    private int yWindowSize;
    /**
     * 两个背景的水平位置坐标,以及其图片资源的引用
     */
    private int cloudPos[];
    private int cloudY[];
    private int soilY;
    private int soilHeight;
    private int grassY;
    private int grassHeight;
    private int boardX;
    private int boardY;
    private Color sky;
    private Color cloudNorm;
    private Color cloudDark;
    private Color soilNorm;
    private Color soilDark;
    private Color grassNorm;
    private Color grassMid;
    private Color grassDark;
    private Color board;
    private Color wordColor;
    private Color maskColor;
    private int maskAlpha;
    private Font font;
    private Font hint;
    private int fontAX;
    private int fontAY;
    private int fontBX;
    private int fontBY;
    private int fontCX;
    private int fontCY;
    private int lifeX;
    private int lifeY;
    private int lifeHeight;
    private int lifeWidth;
    private int crtLifeLen;
    private int lifeDiff;
    // questions
    private int correct;
    private int quesCnt;
    private ArrayList<FixedPointNumber[]> list;
    private String a, b, ans;
    private int quesPos;
    private int hp;
    private int tmphp;
    private int maskDiff;
    private int correctFlag;
    private int wrongFlag;
    private MainFrame frame;
    private Thread myThread;
    private boolean notFinish;

    PaintPanel(int xWindowSize, int yWindowSize, int hp, int quesCnt, MainFrame frame, ArrayList<FixedPointNumber[]> list) {
        super();
        this.frame = frame;
        notFinish = true;
        // init the window size
        this.xWindowSize = xWindowSize;
        this.yWindowSize = yWindowSize;
        // load the background
        Toolkit tk = Toolkit.getDefaultToolkit();
        sky = new Color(78, 173, 245);
        cloudDark = new Color(149, 210, 240);
        cloudNorm = new Color(245, 245, 245);
        soilNorm = new Color(156, 91, 73);
        soilDark = new Color(103, 59, 50);
        grassNorm = new Color(139, 200, 60);
        grassMid = new Color(109, 164, 37);
        grassDark = new Color(67, 118, 13);
        board = new Color(245, 245, 245, 75);
        wordColor = new Color(255, 215, 0);
        maskAlpha = 0;
        maskColor = new Color(0, 0, 0, maskAlpha);
        maskDiff = 80 / (hp - 1);
        font = new Font("SAO UI", Font.PLAIN, 70);
        hint = new Font("SAO UI", Font.BOLD, 90);
        cloudPos = new int[3];
        cloudPos[0] = 0;
        cloudPos[1] = xWindowSize / 3;
        cloudPos[2] = xWindowSize / 4 * 3;
        cloudY = new int[3];
        int skyAreaHeight = yWindowSize / 4 * 3;
        cloudY[0] = skyAreaHeight / 5 * 3;
        cloudY[1] = skyAreaHeight / 3;
        cloudY[2] = skyAreaHeight / 6;
        soilY = skyAreaHeight;
        soilHeight = yWindowSize - soilY;
        grassY = soilY;
        grassHeight = soilHeight / 4;
        boardX = xWindowSize / 4;
        boardY = yWindowSize / 6;
        // init question
        this.quesCnt = quesCnt;
        this.correct = 0;
        if (list == null) {
            this.list = DataGenerator.generate(10);
        } else {
            this.list = list;
        }
        this.quesPos = 0;
        a = list.get(quesPos)[0].toString();
        b = list.get(quesPos)[1].toString();
        ans = "";
        this.hp = hp;
        this.tmphp = hp;
        this.lifeHeight = boardY / 3;
        this.lifeWidth = xWindowSize / 3;
        this.lifeX = xWindowSize / 3;
        this.lifeY = boardY / 3;
        this.crtLifeLen = lifeWidth;
        lifeDiff = lifeWidth / hp;
        correctFlag = 0;
        wrongFlag = 0;

        setVisible(true);
        myThread = new Thread(this);
        myThread.start();
        this.setFocusable(true);
        this.addKeyListener(this);
    }

    private void drawState() {
        renderState();
        updateState();
    }

    private void updateState() {
        // update the cloud position
        for (int i = 0; i < cloudPos.length; i++) {
            cloudPos[i] -= 2 * (i + 1);
            if (cloudPos[i] <= -150) cloudPos[i] = xWindowSize;
        }
    }

    private void renderState() {
        repaint();
    }

    @Override
    public void paint(Graphics g) {
        Graphics2D g2 = (Graphics2D)g;
        g2.setColor(this.sky);
        g2.fillRect(0, 0, xWindowSize, yWindowSize);
        // draw the clouds
        for (int i = 0; i < cloudPos.length; i++) {
            g2.setColor(cloudNorm);
            g2.fillRect(cloudPos[i] + 50, cloudY[i] + 30, 50, 50);
            g2.fillRect(cloudPos[i] + 20, cloudY[i] + 50, 30, 35);
            g2.fillRect(cloudPos[i] + 50, cloudY[i] + 60, 10, 30);
            g2.fillRect(cloudPos[i] + 100, cloudY[i] + 45, 30, 45);
            g2.setColor(cloudDark);
            g2.fillRect(cloudPos[i] + 20, cloudY[i] + 50, 5, 35);
            g2.fillRect(cloudPos[i] + 25, cloudY[i] + 60, 5, 25);
            g2.fillRect(cloudPos[i] + 30, cloudY[i] + 70, 5, 15);
            g2.fillRect(cloudPos[i] + 35, cloudY[i] + 80, 15, 5);
            g2.fillRect(cloudPos[i] + 50, cloudY[i] + 30, 8, 40);
            g2.fillRect(cloudPos[i] + 50, cloudY[i] + 30, 30, 5);
            g2.setColor(sky);
            g2.fillRect(cloudPos[i] + 20, cloudY[i] + 70, 8, 15);
            g2.fillRect(cloudPos[i] + 120, cloudY[i] + 85, 10, 5);
        }
        g2.setColor(soilNorm);
        g2.fillRect(0, soilY, xWindowSize, soilHeight);
        g2.setColor(soilDark);
        g2.fillRect(0, soilY, xWindowSize, soilHeight / 3);
        g2.setColor(grassNorm);
        g2.fillRect(0, grassY, xWindowSize, grassHeight);
        g2.setColor(grassMid);
        g2.fillRect(0, grassY + grassHeight / 5 * 2, xWindowSize, grassHeight / 5 * 3);
        g2.setColor(grassDark);
        g2.fillRect(0, grassY + grassHeight / 5 * 4, xWindowSize, grassHeight / 5 * 2);
        // draw the board
        g2.setColor(board);
        g2.fillRect(boardX, boardY, boardX * 2, boardY * 4);
        g2.setFont(font);
        g2.setColor(wordColor);
        // paint the word
        FontMetrics fm = g2.getFontMetrics();
        int areaHeight = boardY * 20 / 6;
        int areaWidth = boardX / 2 * 3;
        int strWidth = fm.stringWidth("XX.XX");
        this.fontAX = (areaWidth - strWidth) / 2 + (boardX * 2 - areaWidth) / 2 + boardX;
        this.fontAY = (boardY * 4 - areaHeight) / 2 + boardY + fm.getAscent() / 2;
        int diffY = fontAY - boardY + fm.getAscent() / 3;
        this.fontBX = this.fontAX;
        this.fontBY = this.fontAY + diffY;
        this.fontCX = this.fontBX;
        this.fontCY = this.fontBY + diffY * 2;
        g2.drawString(this.a, fontAX, fontAY);
        g2.drawString(this.b, fontBX, fontBY);
        g2.drawString(this.ans, fontCX, fontCY);
        // draw hint
        g2.setFont(hint);
        fm = g2.getFontMetrics();
        if (correctFlag > 0) {
            g2.setColor(new Color(127, 255, 0));
            int wid = fm.stringWidth("Correct                Answer");
            int tx = (xWindowSize - wid) / 2;
            int ty = (yWindowSize - fm.getDescent()) / 2;
            g2.drawString("Correct                Answer", tx, ty);
            correctFlag--;
            System.out.println(correctFlag);
        }
        if (wrongFlag > 0) {
            g2.setColor(new Color(227, 38, 54));
            int wid = fm.stringWidth("Wrong                Answer");
            int tx = (xWindowSize - wid) / 2;
            int ty = (yWindowSize - fm.getDescent()) / 2;
            g2.drawString("Wrong                Answer", tx, ty);
            wrongFlag--;
        }
        if (correctFlag < 0) correctFlag = 0;
        if (wrongFlag < 0) wrongFlag = 0;
        // draw life
        g2.setColor(Color.RED);
        g2.drawRect(lifeX, lifeY, lifeWidth, lifeHeight);
        g2.fillRect(lifeX, lifeY, crtLifeLen, lifeHeight);
        // draw mask
        maskColor = new Color(0, 0, 0, maskAlpha);
        g2.setColor(maskColor);
        g2.fillRect(0, 0, xWindowSize, yWindowSize);
        g2.finalize();
    }

    @Override
    public void run() {
        while (notFinish) {
            drawState();
            try {
                // 60 FPS
                Thread.sleep(16);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(xWindowSize, yWindowSize);
    }

    private static String mapList[] = {"0", "1", "2", "3", "4", "5",
        "6", "7", "8", "9"};
    @Override
    public void keyPressed(KeyEvent e) {
        int code = e.getKeyCode();
        // backSpace is 8
        if (code == 8) {
            ans = "";
            tmphp--;
            maskAlpha += maskDiff;
            crtLifeLen -= lifeDiff;
        } else if (code >= 48 && code <= 57) {
            ans += mapList[code - 48];
        } else if (code >= 96 && code <= 105) {
            ans += mapList[code - 96];
        }
        if (ans.length() == 2) ans += ".";
        if (ans.length() == 5) {
//            System.out.println(ans);
//            System.out.println(list.get(quesPos)[2]);
            if (ans.equals(list.get(quesPos)[2].toString())) {
//                System.out.println("Here");
                correctFlag = 100;
                correct++;
                tmphp = hp;
                quesPos++;
                // finish all question
                if (quesPos == quesCnt) {
                    frame.showMenu();
                    notFinish = false;
                    return;
                }
                a = list.get(quesPos)[0].toString();
                b = list.get(quesPos)[1].toString();
                maskAlpha = 0;
                crtLifeLen = lifeWidth;
            } else {
                maskAlpha += maskDiff;
                tmphp--;
                crtLifeLen -= lifeDiff;
            }
            ans = "";
        }
        if (tmphp == 0) {
            wrongFlag = 100;
            System.out.println("Here");
            quesPos++;
            // finish all question
            if (quesPos == quesCnt) {
                frame.showMenu();
                notFinish = false;
                return;
            }
            a = list.get(quesPos)[0].toString();
            b = list.get(quesPos)[1].toString();
            tmphp = hp;
            maskAlpha = 0;
            crtLifeLen = lifeWidth;
        }
        System.out.println(maskAlpha);
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyReleased(KeyEvent e) {

    }
}

class MenuPanel extends JPanel {
    private int xWindowSize;
    private int yWindowSize;
    private MainFrame frame;
    // start
    private JButton button0;
    // load data
    private JButton button1;
    // show profile
    private JButton button2;
    MenuPanel(int xWindowSize, int yWIndowSize, MainFrame frame) {
        this.xWindowSize = xWindowSize;
        this.yWindowSize = yWIndowSize;
        button0 = new JButton("新的测试");
        button1 = new JButton("加载记录");
        button2 = new JButton("个人成绩");
        button0.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                frame.newTest();
            }
        });
        button1.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JFileChooser jfc = new JFileChooser("D:\\Program Files\\Additor_1_0\\asset\\ques");
                jfc.setFileSelectionMode(JFileChooser.FILES_ONLY);
                int result = jfc.showOpenDialog(null);
                if (result == JFileChooser.APPROVE_OPTION) {
                    File file = jfc.getSelectedFile();
                    ArrayList<FixedPointNumber[]> list;
                    try {
                        list = DataGenerator.loadData(file.getPath());
                        frame.loadTest(list);
                    } catch (Exception ee) {
                        ee.printStackTrace();
                    }
                }
            }
        });
        // set the position of buttons
        int buttonWidth = xWindowSize / 4;
        int buttonHeight = yWindowSize / 7;
        int buttonX = (xWindowSize - buttonWidth) / 2;
        int buttonY = buttonHeight;
        button0.setBounds(buttonX, buttonY, buttonWidth, buttonHeight);
        buttonY += buttonHeight * 2;
        button1.setBounds(buttonX, buttonY, buttonWidth, buttonHeight);
        buttonY += buttonHeight * 2;
        button2.setBounds(buttonX, buttonY, buttonWidth, buttonHeight);
        this.setLayout(null);
        this.add(button0);
        this.add(button1);
        this.add(button2);
        this.setVisible(true);
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(xWindowSize, yWindowSize);
    }
}

class MainFrame extends JFrame {
    private Container frameContainer;
    private int xWindowSize;
    private int yWindowSize;
    private int xScreenSize;
    private int yScreenSize;
    private Image icon;
    public MainFrame() {
        Toolkit tk = Toolkit.getDefaultToolkit();
        icon = tk.getImage("D:\\Program Files\\Additor_1_0\\asset\\pics\\Author.png");
        this.xScreenSize = tk.getScreenSize().width;
        this.yScreenSize = tk.getScreenSize().height;
        this.xWindowSize = this.xScreenSize / 2;
        this.yWindowSize = this.yScreenSize / 2;
        this.setBounds(this.xWindowSize / 2, this.yWindowSize / 2, this.xWindowSize, this.yWindowSize);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setTitle("Additor V1.0");
        this.setIconImage(this.icon);
        this.setResizable(false);
        frameContainer = this.getContentPane();
        frameContainer.add(new MenuPanel(xWindowSize, yWindowSize, this));
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setVisible(true);
    }

    public void newTest() {
        JDialog hpQuest = new JDialog();
        hpQuest.setTitle("提示");
        hpQuest.setLayout(null);
        JTextField words = new JTextField(10);
        JButton yes = new JButton();
        yes.setText("确定");
        words.setBounds(320 / 3, 150 / 5, 320 / 3, 150 / 5);
        yes.setBounds(320 / 5 * 2, 150 / 7 * 3, 320 / 5, 150 / 7);
        hpQuest.add(words);
        hpQuest.add(yes);
        hpQuest.setBounds((xScreenSize - 320) / 2, (yScreenSize - 150) / 2, 320, 150);
        hpQuest.setSize(320, 150);
        hpQuest.setVisible(true);
        yes.addActionListener(new startListener(this, hpQuest, words, null));
    }

    public void loadTest(ArrayList<FixedPointNumber[]> list) {
        JDialog hpQuest = new JDialog();
        hpQuest.setTitle("提示");
        hpQuest.setLayout(null);
        JTextField words = new JTextField(10);
        JButton yes = new JButton();
        yes.setText("确定");
        words.setBounds(320 / 3, 150 / 5, 320 / 3, 150 / 5);
        yes.setBounds(320 / 5 * 2, 150 / 7 * 3, 320 / 5, 150 / 7);
        hpQuest.add(words);
        hpQuest.add(yes);
        hpQuest.setBounds((xScreenSize - 320) / 2, (yScreenSize - 150) / 2, 320, 150);
        hpQuest.setSize(320, 150);
        hpQuest.setVisible(true);
        yes.addActionListener(new startListener(this, hpQuest, words, list));
    }

    public void showMenu() {
        frameContainer.removeAll();
        frameContainer.add(new MenuPanel(xWindowSize, yWindowSize, this));
        this.repaint();
        this.validate();
    }

    public int getXWindowSize() {
        return xWindowSize;
    }
    public int getYWindowSize() {
        return yWindowSize;
    }
}

class startListener implements ActionListener {
    private MainFrame frame;
    private JDialog hpQuest;
    private JTextField words;
    private ArrayList<FixedPointNumber[]> list;
    startListener(MainFrame frame, JDialog hpQuest,
                  JTextField words, ArrayList<FixedPointNumber[]> list) {
        this.frame = frame;
        this.hpQuest = hpQuest;
        this.words = words;
        this.list = list;
    }
    @Override
    public void actionPerformed(ActionEvent e) {
        int hp = Integer.parseInt(words.getText());
        hpQuest.dispose();
        frame.getContentPane().removeAll();
        if (list != null)
            frame.getContentPane().add(new PaintPanel(frame.getXWindowSize(), frame.getYWindowSize(), hp, 10, frame, list));
        else
            frame.getContentPane().add(new PaintPanel(frame.getXWindowSize(), frame.getYWindowSize(), hp, 10, frame, null));
        frame.repaint();
        frame.validate();
    }
}