ChatGPT o1-miniで作ってみる。

プロンプト
英単語を並べ替えて文章を作成する英語学習ソフトを作成する。
起動時 ローカルストレージにデータが保存されている場合は自動的に読み込む データは、{英文,日本語訳,正解回数}を1レコードとする。
ローカルストレージにデータが無い時は、 {"These caves are very old.","この洞窟はとても古い",0} をセットする。

登録画面
ローカルストレージから読み込み、空行を削除して、最終行に空行を追加する。 英文と日本語訳、正解回数をセットにして複数レコード登録できるようにする。 エクセルの表をイメージして、英文と日本語訳と正解回数を横に並べ行数の制限はしない。 正解回数は初回登録時は0になる。 1行目が入力されると2行目が追加されるように常に空行があるようにする。 入力欄の消去とメニューに戻るボタンを用意する。 メニューに戻るときに、入力欄のデータより空行を削除しjsonフォーマットにしてローカルストレージに保存する。 このデータが起動時に自動的に読み込まれる。

問題画面
正解回数が2未満のレコードを総問題数として、問題の順番/総問題数を先頭に表示する。 2段目に日本語訳を表示する。 3段目は答えが表示される欄として確保する。 4段目に英文を英単語でばらばらにし、ランダムに並べ替えた単語を表示する。 表示された英単語は、一つ一つがボタンになっていて、一回押すと、答えの欄に押された順に並べられる。 一度押されたボタンはenable=falseとして、押せないようにする。 5段目にハートマークを5つ用意する。 ハートマークは、1問間違えると一つ減るようにする。 問題開始時点で5つハートマークがあり、一問間違えるごとに一つへり、5問間違えると、終了してメニューに戻る。 戻るボタンとチェックボタン、ギブアップボタンを用意する。 戻るボタンは、最後に押されたボタンを有効にし、解答欄から削除する。戻るボタンを2回以上押した場合も、押された順番と逆の順にボタンが有効になり、解答欄から削除されていく チェックボタンを押すと、正誤を判定して、正解の場合は、正解回数をインクリメントして次の問題に行く。 不正解の場合は、解答を削除し、ボタンもすべて有効にして、その問題の最初の状態にする。 ギブアップボタンを押されたらメニューに戻る。 メニューに戻る時はいつでも、データをローカルストレージに保存する。

ChatGPT o1-miniの解答
素晴らしいアイデアですね!英単語を並べ替えて文章を作成する英語学習ソフトを作成するために、以下のガイドラインとサンプルコードを提供します。このソフトウェアは、ウェブベースで実装し、HTML、CSS、JavaScriptを使用して構築します。

全体の構成

  1. HTML: ユーザーインターフェースの構造を定義します。

  2. CSS: デザインとレイアウトを整えます。

  3. JavaScript: アプリケーションのロジック、ローカルストレージの管理、ユーザーのインタラクションを処理します。

ディレクトリ構成

english-learning-app/
│
├── index.html
├── style.css
└── script.js

1. HTMLの作成
index.html ファイルに基本的な構造を作成します。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>英語学習ソフト</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div id="app">
        <!-- メニュー画面 -->
        <div id="menu-screen" class="screen">
            <h1>英語学習ソフト</h1>
            <button id="start-button">問題を始める</button>
            <button id="register-button">登録画面</button>
        </div>

        <!-- 登録画面 -->
        <div id="register-screen" class="screen" style="display: none;">
            <h2>レコード登録</h2>
            <table id="registration-table">
                <thead>
                    <tr>
                        <th>英文</th>
                        <th>日本語訳</th>
                        <th>正解回数</th>
                    </tr>
                </thead>
                <tbody id="registration-body">
                    <!-- レコード行が動的に追加されます -->
                </tbody>
            </table>
            <div class="buttons">
                <button id="save-button">保存</button>
                <button id="cancel-register-button">メニューに戻る</button>
            </div>
        </div>

        <!-- 問題画面 -->
        <div id="problem-screen" class="screen" style="display: none;">
            <div id="problem-header">
                <span id="problem-count">問題 1/1</span>
                <div id="hearts">
                    <!-- ハートが動的に追加されます -->
                </div>
            </div>
            <div id="problem-translation">
                <!-- 日本語訳が表示されます -->
            </div>
            <div id="answer-section">
                <!-- 答えが表示されます -->
            </div>
            <div id="word-buttons">
                <!-- 単語ボタンが動的に追加されます -->
            </div>
            <div class="buttons">
                <button id="back-button">戻る</button>
                <button id="check-button">チェック</button>
                <button id="giveup-button">ギブアップ</button>
            </div>
        </div>
    </div>

    <script src="script.js"></script>
</body>
</html>

2. CSSの作成

style.css ファイルに基本的なスタイルを追加します。以下は基本的なスタイルの例です。必要に応じてデザインを調整してください。

body {
    font-family: Arial, sans-serif;
    background-color: #f0f0f0 ;
    margin: 0;
    padding: 0;
}
 #app  {
    max-width: 800px;
    margin: 0 auto;
    padding: 20px;
}

.screen {
    background-color: #fff ;
    padding: 20px;
    border-radius: 8px;
}

h1, h2 {
    text-align: center;
}

button {
    padding: 10px 20px;
    margin: 5px;
    font-size: 16px;
}

table {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: 20px;
}

th, td {
    border: 1px solid #ccc ;
    padding: 10px;
    text-align: left;
}
 #hearts  {
    display: flex;
    gap: 5px;
}

.heart {
    font-size: 24px;
    color: red;
}

.word-button {
    padding: 10px;
    margin: 5px;
    font-size: 16px;
    cursor: pointer;
}

.word-button:disabled {
    background-color: #ddd ;
    cursor: not-allowed;
}
 #answer -section {
    min-height: 40px;
    border: 1px solid #ccc ;
    padding: 10px;
    margin-bottom: 20px;
}

3. JavaScriptの作成

script.js ファイルにアプリケーションのロジックを実装します。

// script.js

// データのキー
const STORAGE_KEY = 'englishLearningData';

// 初期データ
const initialData = [
    {
        english: "These caves are very old.",
        japanese: "この洞窟はとても古い",
        correctCount: 0
    }
];

// アプリケーションの状態
let appData = [];
let currentProblemIndex = 0;
let hearts = 5;
let shuffledWords = [];
let answer = [];
let selectedButtons = [];

// DOM要素の取得
const menuScreen = document.getElementById('menu-screen');
const registerScreen = document.getElementById('register-screen');
const problemScreen = document.getElementById('problem-screen');

const startButton = document.getElementById('start-button');
const registerButton = document.getElementById('register-button');

const registrationBody = document.getElementById('registration-body');
const saveButton = document.getElementById('save-button');
const cancelRegisterButton = document.getElementById('cancel-register-button');

const problemCount = document.getElementById('problem-count');
const heartsContainer = document.getElementById('hearts');
const problemTranslation = document.getElementById('problem-translation');
const answerSection = document.getElementById('answer-section');
const wordButtonsContainer = document.getElementById('word-buttons');

const backButton = document.getElementById('back-button');
const checkButton = document.getElementById('check-button');
const giveupButton = document.getElementById('giveup-button');

// 初期化関数
function init() {
    loadData();
    setupEventListeners();
}

// データのロード
function loadData() {
    const data = localStorage.getItem(STORAGE_KEY);
    if (data) {
        appData = JSON.parse(data);
    } else {
        appData = initialData;
        saveData();
    }
}

// データの保存
function saveData() {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(appData));
}

// イベントリスナーの設定
function setupEventListeners() {
    startButton.addEventListener('click', startProblems);
    registerButton.addEventListener('click', showRegisterScreen);
    saveButton.addEventListener('click', saveRegistrations);
    cancelRegisterButton.addEventListener('click', showMenuScreen);

    backButton.addEventListener('click', handleBack);
    checkButton.addEventListener('click', handleCheck);
    giveupButton.addEventListener('click', handleGiveUp);
}

// メニュー画面の表示
function showMenuScreen() {
    hideAllScreens();
    menuScreen.style.display = 'block';
}

// 登録画面の表示
function showRegisterScreen() {
    hideAllScreens();
    registerScreen.style.display = 'block';
    renderRegistrationTable();
}

// 問題画面の表示
function showProblemScreen() {
    hideAllScreens();
    problemScreen.style.display = 'block';
    renderProblem();
}

// すべての画面を非表示にする
function hideAllScreens() {
    menuScreen.style.display = 'none';
    registerScreen.style.display = 'none';
    problemScreen.style.display = 'none';
}

// 登録テーブルのレンダリング
function renderRegistrationTable() {
    // 既存の行をクリア
    registrationBody.innerHTML = '';

    // データを読み込み、空行を削除
    appData = appData.filter(record => record.english && record.japanese);
    
    // 各レコードをテーブルに追加
    appData.forEach(record => {
        const row = document.createElement('tr');

        const englishCell = document.createElement('td');
        const englishInput = document.createElement('input');
        englishInput.type = 'text';
        englishInput.value = record.english;
        englishCell.appendChild(englishInput);
        row.appendChild(englishCell);

        const japaneseCell = document.createElement('td');
        const japaneseInput = document.createElement('input');
        japaneseInput.type = 'text';
        japaneseInput.value = record.japanese;
        japaneseCell.appendChild(japaneseInput);
        row.appendChild(japaneseCell);

        const correctCountCell = document.createElement('td');
        const correctCountInput = document.createElement('input');
        correctCountInput.type = 'number';
        correctCountInput.value = record.correctCount;
        correctCountInput.disabled = true;
        correctCountCell.appendChild(correctCountInput);
        row.appendChild(correctCountCell);

        registrationBody.appendChild(row);
    });

    // 常に空行を追加
    const emptyRow = document.createElement('tr');

    const emptyEnglishCell = document.createElement('td');
    const emptyEnglishInput = document.createElement('input');
    emptyEnglishInput.type = 'text';
    emptyEnglishCell.appendChild(emptyEnglishInput);
    emptyRow.appendChild(emptyEnglishCell);

    const emptyJapaneseCell = document.createElement('td');
    const emptyJapaneseInput = document.createElement('input');
    emptyJapaneseInput.type = 'text';
    emptyJapaneseCell.appendChild(emptyJapaneseInput);
    emptyRow.appendChild(emptyJapaneseCell);

    const emptyCorrectCountCell = document.createElement('td');
    const emptyCorrectCountInput = document.createElement('input');
    emptyCorrectCountInput.type = 'number';
    emptyCorrectCountInput.value = 0;
    emptyCorrectCountInput.disabled = true;
    emptyCorrectCountCell.appendChild(emptyCorrectCountInput);
    emptyRow.appendChild(emptyCorrectCountCell);

    registrationBody.appendChild(emptyRow);
}

// 保存ボタンの処理
function saveRegistrations() {
    const rows = registrationBody.querySelectorAll('tr');
    const newData = [];

    rows.forEach(row => {
        const english = row.cells[0].querySelector('input').value.trim();
        const japanese = row.cells[1].querySelector('input').value.trim();
        let correctCount = parseInt(row.cells[2].querySelector('input').value, 10);
        if (isNaN(correctCount)) correctCount = 0;

        if (english && japanese) {
            // 既存のレコードを更新
            const existing = appData.find(record => record.english === english && record.japanese === japanese);
            if (existing) {
                newData.push(existing);
            } else {
                newData.push({
                    english: english,
                    japanese: japanese,
                    correctCount: correctCount
                });
            }
        }
    });

    // データを更新
    appData = newData;
    saveData();
    showMenuScreen();
}

// 問題を開始する
function startProblems() {
    // 総問題数を正解回数が2未満のレコードからフィルタリング
    const problems = appData.filter(record => record.correctCount < 2);
    if (problems.length === 0) {
        alert('全ての問題が完了しました!');
        return;
    }

    // シャッフル
    shuffleArray(problems);
    appData = problems;
    currentProblemIndex = 0;
    hearts = 5;
    showProblemScreen();
}

// 問題のレンダリング
function renderProblem() {
    if (currentProblemIndex >= appData.length) {
        alert('全ての問題が完了しました!');
        showMenuScreen();
        return;
    }

    const currentProblem = appData[currentProblemIndex];
    problemCount.textContent = `問題 ${currentProblemIndex + 1}/${appData.length}`;
    problemTranslation.textContent = currentProblem.japanese;
    answerSection.textContent = '';
    selectedButtons = [];

    // ハートのレンダリング
    heartsContainer.innerHTML = '';
    for (let i = 0; i < hearts; i++) {
        const heart = document.createElement('span');
        heart.classList.add('heart');
        heart.textContent = '❤️';
        heartsContainer.appendChild(heart);
    }

    // 英単語をシャッフル
    shuffledWords = shuffleArray(currentProblem.english.split(' '));
    wordButtonsContainer.innerHTML = '';

    shuffledWords.forEach((word, index) => {
        const button = document.createElement('button');
        button.classList.add('word-button');
        button.textContent = word;
        button.dataset.index = index;
        button.addEventListener('click', () => handleWordClick(button, word));
        wordButtonsContainer.appendChild(button);
    });
}

// 単語ボタンをクリックしたときの処理
function handleWordClick(button, word) {
    answer.push(word);
    selectedButtons.push(button);
    answerSection.textContent += word + ' ';
    button.disabled = true;
}

// 戻るボタンの処理
function handleBack() {
    if (selectedButtons.length === 0) return;

    const lastButton = selectedButtons.pop();
    const lastWord = answer.pop();

    // 解答欄から単語を削除
    const currentAnswer = answerSection.textContent.trim().split(' ');
    currentAnswer.pop();
    answerSection.textContent = currentAnswer.join(' ') + ' ';

    // ボタンを再度有効にする
    lastButton.disabled = false;
}

// チェックボタンの処理
function handleCheck() {
    const currentProblem = appData[currentProblemIndex];
    const userAnswer = answer.join(' ');
    const correctAnswer = currentProblem.english;

    if (userAnswer === correctAnswer) {
        alert('正解です!');
        // 正解回数をインクリメント
        currentProblem.correctCount += 1;
        saveData();
        currentProblemIndex += 1;
        renderProblem();
    } else {
        alert('不正解です。もう一度試してください。');
        // ハートを減らす
        hearts -= 1;
        if (hearts <= 0) {
            alert('ハートがなくなりました。メニューに戻ります。');
            showMenuScreen();
            return;
        }
        // ハートの更新
        heartsContainer.innerHTML = '';
        for (let i = 0; i < hearts; i++) {
            const heart = document.createElement('span');
            heart.classList.add('heart');
            heart.textContent = '❤️';
            heartsContainer.appendChild(heart);
        }
        // 解答をリセット
        answer = [];
        // ボタンを再度有効にする
        selectedButtons.forEach(button => button.disabled = false);
        selectedButtons = [];
        answerSection.textContent = '';
    }
}

// ギブアップボタンの処理
function handleGiveUp() {
    const currentProblem = appData[currentProblemIndex];
    alert(`正解は: ${currentProblem.english}`);
    currentProblemIndex += 1;
    renderProblem();
}

// シャッフル関数(Fisher-Yatesアルゴリズム)
function shuffleArray(array) {
    const newArray = array.slice(); // コピーを作成
    for (let i = newArray.length - 1; i > 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [newArray[i], newArray[j]] = [newArray[j], newArray[i]];
    }
    return newArray;
}

// ローカルストレージにデータを保存する際にページを離れる場合も考慮
window.addEventListener('beforeunload', saveData);

// アプリケーションの初期化
init();

JavaScriptの詳細説明

  1. データ管理:

    • appData は全てのレコードを保持します。

    • ローカルストレージからデータをロードし、存在しない場合は初期データをセットします。

    • データは {english, japanese, correctCount} の形式で管理されます。

  2. 画面の遷移:

    • メニュー画面、登録画面、問題画面の3つの画面を用意し、必要に応じて表示・非表示を切り替えます。

  3. 登録画面:

    • テーブル形式で英文、日本語訳、正解回数を表示します。

    • 新しいレコードを追加するために常に空行を1行保持します。

    • 保存ボタンで入力されたデータをローカルストレージに保存します。

  4. 問題画面:

    • 正解回数が2未満のレコードを対象に問題を出題します。

    • 日本語訳を表示し、英文を単語ごとに分割してシャッフルし、ボタンとして表示します。

    • ユーザーが単語ボタンをクリックすると、答え欄に単語が追加され、ボタンが無効化されます。

    • チェックボタンで正誤を判定し、正解なら次の問題へ、不正解ならハートを減らして再挑戦させます。

    • 戻るボタンで最後に選択した単語を取り消し、ボタンを再度有効化します。

    • ギブアップボタンで現在の問題をスキップし、メニューに戻ります。

  5. ハート機能:

    • 問題開始時に5つのハートを表示します。

    • 不正解するたびにハートを1つ減らし、ハートが0になるとメニューに戻ります。

  6. データの保存:

    • ローカルストレージにデータを保存し、アプリケーションの再起動時に自動的にデータを読み込みます。

    • ページを離れる前にもデータを保存するようにしています。

4. 追加の改善点

この基本的な構造を基に、以下のような機能を追加・改善することができます。

  • バリデーション: ユーザーが入力する際のデータの検証(例: 空白の英文や日本語訳の禁止)。

  • スタイルの向上: より魅力的なUIデザインの適用。

  • 複数言語対応: 日本語以外の翻訳にも対応。

  • 進捗の保存: 現在の問題の進捗を保存して再開できるようにする。

  • 音声機能: 単語や文章の音声読み上げ機能を追加。

5. 実装のテスト

  1. ローカルサーバーの起動:

    • 開発中はローカルサーバーを使用すると便利です。例えば、VSCodeの「Live Server」拡張機能を使用できます。

  2. ブラウザで確認:

    • index.html をブラウザで開き、各機能が期待通りに動作するか確認します。

  3. デバッグ:

    • ブラウザの開発者ツールを使用して、エラーや問題をデバッグします。

6. まとめ

このガイドでは、英単語を並べ替えて文章を作成する英語学習ソフトの基本的な構造と実装方法を説明しました。必要に応じて機能を追加・改善し、ユーザーにとって使いやすく効果的な学習ツールを作成してください。開発中に質問や問題が発生した場合は、ぜひ再度ご相談ください!

o1-mini

この記事が気に入ったらサポートをしてみませんか?