Java | 論理演算子を使った「採点付きミニ演習プログラム」

APP Java
スポンサーリンク

機能一覧

✅ 問題をランダムに出題
✅ 問題数を変更できる(例:5問中3問をランダム出題)
✅ 「リセット」ボタンで平均点を初期化
✅ 正解時に「解説ポップアップ」表示
✅ 履歴を自動で CSV に保存(日時・得点・平均点)


💻 コード:LogicQuizPro.java

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;

public class LogicQuizPro extends JFrame implements ActionListener {
    // 全問題データ
    private String[] questions = {
        "true && false の結果は?",
        "true || false の結果は?",
        "!(true && true) の結果は?",
        "false || (true && false) の結果は?",
        "!(false || true) の結果は?",
        "true && (false || true) の結果は?",
        "!(false && false) の結果は?"
    };
    private String[][] options = {
        {"true", "false"},
        {"true", "false"},
        {"true", "false"},
        {"true", "false"},
        {"true", "false"},
        {"true", "false"},
        {"true", "false"}
    };
    private int[] answers = {1, 0, 1, 1, 1, 0, 0};
    private String[] explanations = {
        "true && false → どちらかが false なので結果は false。",
        "true || false → どちらかが true なので結果は true。",
        "!(true && true) → true && true は true、否定すると false。",
        "false || (true && false) → true && false は false、よって全体は false。",
        "!(false || true) → false || true は true、否定して false。",
        "true && (false || true) → (false || true) は true、よって全体は true。",
        "!(false && false) → false && false は false、否定して true。"
    };

    // 出題・GUI関係
    private int[] selectedIndices;
    private ButtonGroup[] groups;
    private JRadioButton[][] radios;
    private JPanel mainPanel;
    private JButton submitButton, resetButton;
    private JLabel resultLabel, avgLabel;
    private JProgressBar progressBar;

    // 採点・履歴
    private int totalScore = 0;
    private int attempts = 0;
    private final int NUM_QUESTIONS = 4; // 出題数

    public LogicQuizPro() {
        setTitle("論理演算子クイズ Pro - 豪華版");
        setSize(520, 700);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
        setLayout(new BorderLayout());

        // ランダム出題
        selectedIndices = randomizeQuestions(NUM_QUESTIONS);

        // 問題パネル
        mainPanel = new JPanel();
        mainPanel.setLayout(new GridLayout(NUM_QUESTIONS + 2, 1));

        groups = new ButtonGroup[NUM_QUESTIONS];
        radios = new JRadioButton[NUM_QUESTIONS][2];

        for (int i = 0; i < NUM_QUESTIONS; i++) {
            int qIndex = selectedIndices[i];
            JPanel qPanel = new JPanel(new GridLayout(3, 1));
            qPanel.setBorder(BorderFactory.createTitledBorder("問" + (i + 1) + ": " + questions[qIndex]));
            groups[i] = new ButtonGroup();
            for (int j = 0; j < 2; j++) {
                radios[i][j] = new JRadioButton(options[qIndex][j]);
                groups[i].add(radios[i][j]);
                qPanel.add(radios[i][j]);
            }
            mainPanel.add(qPanel);
        }

        // ボタン群
        JPanel buttonPanel = new JPanel();
        submitButton = new JButton("採点する");
        resetButton = new JButton("リセット");
        submitButton.addActionListener(this);
        resetButton.addActionListener(e -> resetAverage());
        buttonPanel.add(submitButton);
        buttonPanel.add(resetButton);
        mainPanel.add(buttonPanel);

        // 結果表示部
        resultLabel = new JLabel("ここに結果が表示されます", SwingConstants.CENTER);
        avgLabel = new JLabel("平均点: 0%", SwingConstants.CENTER);
        progressBar = new JProgressBar(0, 100);
        progressBar.setValue(0);
        progressBar.setStringPainted(true);
        progressBar.setForeground(new Color(100, 180, 255));

        JPanel resultPanel = new JPanel(new GridLayout(3, 1));
        resultPanel.add(resultLabel);
        resultPanel.add(avgLabel);
        resultPanel.add(progressBar);

        add(mainPanel, BorderLayout.CENTER);
        add(resultPanel, BorderLayout.SOUTH);
    }

    // 出題をランダムに選ぶ
    private int[] randomizeQuestions(int count) {
        List<Integer> indices = new ArrayList<>();
        for (int i = 0; i < questions.length; i++) indices.add(i);
        Collections.shuffle(indices);
        return indices.subList(0, count).stream().mapToInt(Integer::intValue).toArray();
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        int score = 0;
        StringBuilder popupMsg = new StringBuilder();

        for (int i = 0; i < NUM_QUESTIONS; i++) {
            int qIndex = selectedIndices[i];
            for (int j = 0; j < 2; j++) {
                if (radios[i][j].isSelected()) {
                    if (j == answers[qIndex]) {
                        score++;
                        popupMsg.append("問").append(i + 1).append(" ✅ 正解! ")
                                .append(explanations[qIndex]).append("\n\n");
                    } else {
                        popupMsg.append("問").append(i + 1).append(" ❌ 不正解。 ")
                                .append(explanations[qIndex]).append("\n\n");
                    }
                }
            }
        }

        int percent = (int) ((score / (double) NUM_QUESTIONS) * 100);
        resultLabel.setText("今回の得点: " + percent + "点 (" + score + "/" + NUM_QUESTIONS + ")");
        JOptionPane.showMessageDialog(this, popupMsg.toString(), "解説", JOptionPane.INFORMATION_MESSAGE);

        // 平均スコア更新
        attempts++;
        totalScore += percent;
        int avg = totalScore / attempts;
        avgLabel.setText("平均点: " + avg + "%");
        animateProgressBar(avg);

        // 履歴を保存
        saveToCSV(percent, avg);

        // 新しい問題を再生成
        regenerateQuiz();
    }

    // 平均点リセット
    private void resetAverage() {
        totalScore = 0;
        attempts = 0;
        avgLabel.setText("平均点: 0%");
        progressBar.setValue(0);
    }

    // プログレスバー演出
    private void animateProgressBar(int target) {
        new Thread(() -> {
            int current = progressBar.getValue();
            if (target < current) {
                for (int i = current; i >= target; i--) {
                    progressBar.setValue(i);
                    sleep(10);
                }
            } else {
                for (int i = current; i <= target; i++) {
                    progressBar.setValue(i);
                    sleep(10);
                }
            }
        }).start();
    }

    private void sleep(int ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
    }

    // CSV出力(履歴保存)
    private void saveToCSV(int score, int avg) {
        try (FileWriter fw = new FileWriter("quiz_history.csv", true);
             BufferedWriter bw = new BufferedWriter(fw);
             PrintWriter pw = new PrintWriter(bw)) {
            String time = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format(new Date());
            pw.println(time + "," + score + "," + avg);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    // 新しい問題を再出題
    private void regenerateQuiz() {
        selectedIndices = randomizeQuestions(NUM_QUESTIONS);
        getContentPane().removeAll();
        new LogicQuizPro().setVisible(true);
        dispose();
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new LogicQuizPro().setVisible(true));
    }
}
Java

機能の動作解説

機能内容
ランダム出題Collections.shuffle()で問題を毎回シャッフル
出題数調整NUM_QUESTIONS の値を変更するだけ
リセット平均スコアと進捗バーを初期化
ポップアップ解説JOptionPane.showMessageDialog()で正誤+説明表示
CSV出力quiz_history.csv に日時・得点・平均点を追記保存
演出バーが伸びるアニメーション付きで学習意欲UP!

履歴ファイル(例)

2025/10/22 16:10:35,75,75
2025/10/22 16:15:41,100,87
2025/10/22 16:20:10,50,75

次の拡張アイデア

  • 🕓 タイマー付き出題(制限時間モード)
  • 🧩 出題カテゴリを選べる(論理演算・比較演算など)
  • 🏆 最高点ランキング表示
  • 📈 平均スコアの推移グラフ表示(JFreeChartなどで)
タイトルとURLをコピーしました