見出し画像

【ChatGPT】簡単に作れるウェブアプリ:o1-miniを活用した「選択式練習問題アプリ」をプロンプトだけで開発するプロセス


今回は、ChatGPT(o1-mini)を活用して、選択式練習問題アプリを一から作成するプロセスをご紹介します。このプロジェクトを通じて、大規模言語モデルを使ったウェブアプリ開発の容易さを実感できると思います。

実際に作成し、サーバーにアップロードしたアプリはこちらです。
問題例は、大規模言語モデルについての練習問題をo1-miniに10問作ってもらいました。コード出力の後に、気を利かせてくれて問題例を自動で出力してくれました。

1. プロジェクトの仕様確定

1.1 最初のプロンプトとChatGPTの応答

プロジェクトの最初のステップとして、ChatGPT(モデルは、o1-mini)にアプリの基本仕様について相談しました。以下は、その際の対話です。

ユーザーのプロンプト:

役割: あなたはシングルページ Web アプリケーション開発の専門家です。 
指示: 4択の選択肢式練習問題を出題するアプリについてのシングルページ Web アプリケーションのコードを出力してください
条件:
• フロントエンドのみで動作する Web アプリケーションにしてください。サーバーサイドの機能は使用しません
• スタイリッシュでモダンなデザインにしてください
• htmlには、使用方法と注意事項を記載してください
• 出力内容: htmlコードの中にCSS,JavaScriptコードを含めてください
• 出力形式: コードブロックに出力してください
• 問題と回答のデータベースとして、CSVファイルを読み込むようにする

ChatGPTの応答:

ChatGPTは、指定された要件に基づいてシングルページのウェブアプリケーションのコードを提供しました。この初期対話により、プロジェクトの基本的な方向性と機能要件が明確になりました。ChatGPTは、フロントエンドのみで動作するスタイリッシュなデザインのアプリケーションを提案し、HTML内にCSSとJavaScriptを含める形でのコードを生成しました。また、CSVファイルから問題と回答を読み込む機能についても具体的に対応しました。

2. ディレクトリ構成の設計

次に、プロジェクトのディレクトリ構成を整理しました。適切なディレクトリ構成は、後々のメンテナンスや機能追加を容易にします。以下のような構成に決定しました。

/project-root
├── quiz.html
└── material
    └── questions.csv
  • `quiz.html`:メインのHTMLファイルで、アプリケーションの全体的な構造とデザインを担当します。

  • `material/questions.csv`:問題データを管理するCSVファイルで、質問、選択肢、正解、解説を含んでいます。

このシンプルな構成により、ファイルの管理が容易になり、必要なファイルを迅速に見つけることができます。

3. コーディングと機能実装

3.1 HTMLとCSSの設定

quiz.htmlでは、基本的なHTML構造とスタイリングを設定しました。ユーザーインターフェースが直感的で使いやすいことが重要です。ChatGPTを活用することで、シンプルなスタイル設定が可能となります。

3.2 JavaScriptによる動的機能の追加

選択肢のランダムシャッフルや回答の制御、スコアの管理など、動的な機能はJavaScriptで実装しました。ChatGPTは、これらの機能をコーディングしてくれました。

  • 選択肢のランダムシャッフル:ユーザーが毎回異なる選択肢の順序で問題に取り組むことができるよう、配列のシャッフルアルゴリズムを実装しました。

  • 無回答時の制御:ユーザーが回答を選択しない限り、「次の問題」ボタンを無効化するロジックを追加しました。これにより、全ての問題に確実に回答させることが可能となりました。

  • 全問回答後の採点結果表示:全ての問題に回答し終えた後、最終的なスコアを表示する機能を実装しました。ユーザーに対して達成感を提供し、学習のモチベーションを維持します。

3.3 データ管理

questions.csvファイルに、各問題の詳細を記載しました。CSV形式を採用することで、問題の追加や編集が容易になり、将来的な拡張性も確保しました。CSVデータの読み込みとパースを効率的に行うためのコードも自動で書いてくれました。

4. テストと調整

開発が一通り完了した後、アプリケーションの動作確認を行いました。以下の点を重点的にチェックしました。

  • 選択肢のシャッフル:各問題で選択肢がランダムに並んでいることを確認しました。

  • 回答の制御:回答を選択しない場合、「次の問題」ボタンが無効化されていることを確認しました。

  • スコアの管理:正答数が正確にカウントされ、最終スコアが正しく表示されることを確認しました。

  • ユーザーインターフェース:全てのボタンが適切に機能し、ユーザーが直感的に操作できることを確認しました。

出来上がりのコード

<!DOCTYPE html><html><head><base href="https://indepa.net/app/quiz.html"><meta charset="utf-8"><meta name="download_date" content="2024-10-27T12:06:26.685Z"/><style></style><script type="text/javascript">"use strict";</script><style data-rc-order="prependQueue" data-rc-priority="-999" data-css-hash="3lxbv9" data-token-hash="y903ur">:where(.css-1nisull) a{color:#1668dc;text-decoration:none;background-color:transparent;outline:none;cursor:pointer;transition:color 0.3s;-webkit-text-decoration-skip:objects;}:where(.css-1nisull) a:hover{color:#15417e;}:where(.css-1nisull) a:active{color:#1554ad;}:where(.css-1nisull) a:active,:where(.css-1nisull) a:hover{text-decoration:none;outline:0;}:where(.css-1nisull) a:focus{text-decoration:none;outline:0;}:where(.css-1nisull) a[disabled]{color:rgba(255, 255, 255, 0.25);cursor:not-allowed;}</style><style data-rc-order="prependQueue" data-rc-priority="-999" data-css-hash="12p02g4" data-token-hash="y903ur">:where(.css-1nisull)[class^="ant-modal"],:where(.css-1nisull)[class*=" ant-modal"]{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,'Noto Sans',sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol','Noto Color Emoji';font-size:12px;box-sizing:border-box;}:where(.css-1nisull)[class^="ant-modal"]::before,:where(.css-1nisull)[class*=" ant-modal"]::before,:where(.css-1nisull)[class^="ant-modal"]::after,:where(.css-1nisull)[class*=" ant-modal"]::after{box-sizing:border-box;}:where(.css-1nisull)[class^="ant-modal"] [class^="ant-modal"],:where(.css-1nisull)[class*=" ant-modal"] [class^="ant-modal"],:where(.css-1nisull)[class^="ant-modal"] [class*=" ant-modal"],:where(.css-1nisull)[class*=" ant-modal"] [class*=" ant-modal"]{box-sizing:border-box;}:where(.css-1nisull)[class^="ant-modal"] [class^="ant-modal"]::before,:where(.css-1nisull)[class*=" ant-modal"] [class^="ant-modal"]::before,:where(.css-1nisull)[class^="ant-modal"] [class*=" ant-modal"]::before,:where(.css-1nisull)[class*=" ant-modal"] [class*=" ant-modal"]::before,:where(.css-1nisull)[class^="ant-modal"] [class^="ant-modal"]::after,:where(.css-1nisull)[class*=" ant-modal"] [class^="ant-modal"]::after,:where(.css-1nisull)[class^="ant-modal"] [class*=" ant-modal"]::after,:where(.css-1nisull)[class*=" ant-modal"] [class*=" ant-modal"]::after{box-sizing:border-box;}:where(.css-1nisull).ant-modal-root .ant-modal-wrap-rtl{direction:rtl;}:where(.css-1nisull).ant-modal-root .ant-modal-centered{text-align:center;}:where(.css-1nisull).ant-modal-root .ant-modal-centered::before{display:inline-block;width:0;height:100%;vertical-align:middle;content:"";}:where(.css-1nisull).ant-modal-root .ant-modal-centered .ant-modal{top:0;display:inline-block;padding-bottom:0;text-align:start;vertical-align:middle;}@media (max-width: 767px){:where(.css-1nisull).ant-modal-root .ant-modal{max-width:calc(100vw - 16px);margin:4px auto;}:where(.css-1nisull).ant-modal-root .ant-modal-centered .ant-modal{flex:1;}}:where(.css-1nisull).ant-modal{box-sizing:border-box;margin:0 auto;padding:0;color:rgba(255, 255, 255, 0.85);font-size:12px;line-height:1.6666666666666667;list-style:none;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,'Noto Sans',sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol','Noto Color Emoji';pointer-events:none;position:relative;top:100px;width:auto;max-width:calc(100vw - 16px);padding-bottom:16px;}:where(.css-1nisull).ant-modal .ant-modal-title{margin:0;color:rgba(255, 255, 255, 0.85);font-weight:600;font-size:14px;line-height:1.5714285714285714;word-wrap:break-word;}:where(.css-1nisull).ant-modal .ant-modal-content{position:relative;background-color:#1f1f1f;background-clip:padding-box;border:0;border-radius:8px;box-shadow:0 6px 16px 0 rgba(0, 0, 0, 0.08),0 3px 6px -4px rgba(0, 0, 0, 0.12),0 9px 28px 8px rgba(0, 0, 0, 0.05);pointer-events:auto;padding:16px 16px;}:where(.css-1nisull).ant-modal .ant-modal-close{position:absolute;top:5px;inset-inline-end:5px;z-index:1010;padding:0;color:rgba(255, 255, 255, 0.45);font-weight:600;line-height:1;text-decoration:none;background:transparent;border-radius:4px;width:28px;height:28px;border:0;outline:0;cursor:pointer;transition:color 0.2s,background-color 0.2s;}:where(.css-1nisull).ant-modal .ant-modal-close-x{display:flex;font-size:14px;font-style:normal;line-height:28px;justify-content:center;text-transform:none;text-rendering:auto;}:where(.css-1nisull).ant-modal .ant-modal-close:hover{color:rgba(255, 255, 255, 0.85);background-color:rgba(255, 255, 255, 0.12);text-decoration:none;}:where(.css-1nisull).ant-modal .ant-modal-close:active{background-color:rgba(255, 255, 255, 0.18);}:where(.css-1nisull).ant-modal .ant-modal-close:focus-visible{outline:4px solid #15325b;outline-offset:1px;transition:outline-offset 0s,outline 0s;}:where(.css-1nisull).ant-modal .ant-modal-header{color:rgba(255, 255, 255, 0.85);background:#1f1f1f;border-radius:8px 8px 0 0;margin-bottom:4px;padding:0;border-bottom:none;}:where(.css-1nisull).ant-modal .ant-modal-body{font-size:12px;line-height:1.6666666666666667;word-wrap:break-word;padding:0;}:where(.css-1nisull).ant-modal .ant-modal-body .ant-modal-body-skeleton{width:100%;height:100%;display:flex;justify-content:center;align-items:center;margin:8px auto;}:where(.css-1nisull).ant-modal .ant-modal-footer{text-align:end;background:transparent;margin-top:8px;padding:0;border-top:none;border-radius:0;}:where(.css-1nisull).ant-modal .ant-modal-footer >.ant-btn+.ant-btn{margin-inline-start:4px;}:where(.css-1nisull).ant-modal .ant-modal-open{overflow:hidden;}:where(.css-1nisull).ant-modal-pure-panel{top:auto;padding:0;display:flex;flex-direction:column;}:where(.css-1nisull).ant-modal-pure-panel .ant-modal-content,:where(.css-1nisull).ant-modal-pure-panel .ant-modal-body,:where(.css-1nisull).ant-modal-pure-panel .ant-modal-confirm-body-wrapper{display:flex;flex-direction:column;flex:auto;}:where(.css-1nisull).ant-modal-pure-panel .ant-modal-confirm-body{margin-bottom:auto;}:where(.css-1nisull).ant-modal-root .ant-modal-wrap-rtl{direction:rtl;}:where(.css-1nisull).ant-modal-root .ant-modal-wrap-rtl .ant-modal-confirm-body{direction:rtl;}:where(.css-1nisull).ant-modal-root .ant-modal.ant-zoom-enter,:where(.css-1nisull).ant-modal-root .ant-modal.ant-zoom-appear{transform:none;opacity:0;animation-duration:0.3s;user-select:none;}:where(.css-1nisull).ant-modal-root .ant-modal.ant-zoom-leave .ant-modal-content{pointer-events:none;}:where(.css-1nisull).ant-modal-root .ant-modal-mask{position:fixed;inset:0;z-index:1000;height:100%;background-color:rgba(0, 0, 0, 0.45);pointer-events:none;}:where(.css-1nisull).ant-modal-root .ant-modal-mask .ant-modal-hidden{display:none;}:where(.css-1nisull).ant-modal-root .ant-modal-wrap{position:fixed;inset:0;z-index:1000;overflow:auto;outline:0;-webkit-overflow-scrolling:touch;}:where(.css-1nisull).ant-modal-root .ant-fade-enter,:where(.css-1nisull).ant-modal-root .ant-fade-appear{animation-duration:0.2s;animation-fill-mode:both;animation-play-state:paused;}:where(.css-1nisull).ant-modal-root .ant-fade-leave{animation-duration:0.2s;animation-fill-mode:both;animation-play-state:paused;}:where(.css-1nisull).ant-modal-root .ant-fade-enter.ant-fade-enter-active,:where(.css-1nisull).ant-modal-root .ant-fade-appear.ant-fade-appear-active{animation-name:css-1nisull-antFadeIn;animation-play-state:running;}:where(.css-1nisull).ant-modal-root .ant-fade-leave.ant-fade-leave-active{animation-name:css-1nisull-antFadeOut;animation-play-state:running;pointer-events:none;}:where(.css-1nisull).ant-modal-root .ant-fade-enter,:where(.css-1nisull).ant-modal-root .ant-fade-appear{opacity:0;animation-timing-function:linear;}:where(.css-1nisull).ant-modal-root .ant-fade-leave{animation-timing-function:linear;}:where(.css-1nisull).ant-zoom-enter,:where(.css-1nisull).ant-zoom-appear{animation-duration:0.2s;animation-fill-mode:both;animation-play-state:paused;}:where(.css-1nisull).ant-zoom-leave{animation-duration:0.2s;animation-fill-mode:both;animation-play-state:paused;}:where(.css-1nisull).ant-zoom-enter.ant-zoom-enter-active,:where(.css-1nisull).ant-zoom-appear.ant-zoom-appear-active{animation-name:css-1nisull-antZoomIn;animation-play-state:running;}:where(.css-1nisull).ant-zoom-leave.ant-zoom-leave-active{animation-name:css-1nisull-antZoomOut;animation-play-state:running;pointer-events:none;}:where(.css-1nisull).ant-zoom-enter,:where(.css-1nisull).ant-zoom-appear{transform:scale(0);opacity:0;animation-timing-function:cubic-bezier(0.08, 0.82, 0.17, 1);}:where(.css-1nisull).ant-zoom-enter-prepare,:where(.css-1nisull).ant-zoom-appear-prepare{transform:none;}:where(.css-1nisull).ant-zoom-leave{animation-timing-function:cubic-bezier(0.78, 0.14, 0.15, 0.86);}</style><style data-rc-order="prependQueue" data-rc-priority="-999" data-css-hash="_effect-css-1nisull-antFadeIn">@keyframes css-1nisull-antFadeIn{0%{opacity:0;}100%{opacity:1;}}</style><style data-rc-order="prependQueue" data-rc-priority="-999" data-css-hash="_effect-css-1nisull-antFadeOut">@keyframes css-1nisull-antFadeOut{0%{opacity:1;}100%{opacity:0;}}</style><style data-rc-order="prependQueue" data-rc-priority="-999" data-css-hash="_effect-css-1nisull-antZoomIn">@keyframes css-1nisull-antZoomIn{0%{transform:scale(0.2);opacity:0;}100%{transform:scale(1);opacity:1;}}</style><style data-rc-order="prependQueue" data-rc-priority="-999" data-css-hash="_effect-css-1nisull-antZoomOut">@keyframes css-1nisull-antZoomOut{0%{transform:scale(1);}100%{transform:scale(0.2);opacity:0;}}</style><style data-rc-order="prependQueue" data-css-hash="c10mn4" data-token-hash="krywmj">.anticon{display:inline-flex;align-items:center;color:inherit;font-style:normal;line-height:0;text-align:center;text-transform:none;vertical-align:-0.125em;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;}.anticon >*{line-height:1;}.anticon svg{display:inline-block;}.anticon .anticon .anticon-icon{display:block;}</style><style data-rc-order="prependQueue" data-css-hash="pn774g" data-token-hash="xeg8v4">.anticon{display:inline-flex;align-items:center;color:inherit;font-style:normal;line-height:0;text-align:center;text-transform:none;vertical-align:-0.125em;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;}.anticon >*{line-height:1;}.anticon svg{display:inline-block;}.anticon .anticon .anticon-icon{display:block;}</style><style data-rc-order="prependQueue" data-css-hash="ib2edl" data-token-hash="y903ur">.anticon{display:inline-flex;align-items:center;color:inherit;font-style:normal;line-height:0;text-align:center;text-transform:none;vertical-align:-0.125em;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;}.anticon >*{line-height:1;}.anticon svg{display:inline-block;}.anticon .anticon .anticon-icon{display:block;}</style>
    
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>選択肢式練習問題アプリ</title>
    <style>
        /* 全体のリセット */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        /* フォントと背景 */
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #f5f7fa, #c3cfe2);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        /* コンテナ */
        .container {
            background: #ffffff;
            padding: 2rem;
            border-radius: 10px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
            width: 90%;
            max-width: 800px;
            text-align: center;
        }

        /* タイトル */
        h1 {
            margin-bottom: 1.5rem;
            color: #333333;
        }

        /* 問題表示エリア */
        .question {
            font-size: 1.2rem;
            margin-bottom: 1rem;
            color: #555555;
        }

        /* 選択肢 */
        .options {
            list-style-type: none;
            padding: 0;
            margin-bottom: 1rem;
            text-align: left;
        }

        .options li {
            margin-bottom: 0.5rem;
        }

        /* ボタン */
        button {
            padding: 0.5rem 1rem;
            border: none;
            border-radius: 5px;
            background-color: #4CAF50;
            color: white;
            cursor: pointer;
            font-size: 1rem;
            margin: 0.5rem;
        }

        button:hover {
            background-color: #45a049;
        }

        /* 結果表示 */
        .result {
            margin-top: 1rem;
            font-size: 1rem;
            color: #ff5722;
        }

        /* 解説表示 */
        .explanation {
            margin-top: 1rem;
            font-size: 1rem;
            color: #333333;
            text-align: left;
            background-color: #f0f0f0;
            padding: 0.5rem;
            border-radius: 5px;
        }

        /* スコア表示 */
        .score {
            margin-top: 1rem;
            font-size: 1.2rem;
            color: #333333;
        }

        /* ナビゲーション */
        .nav {
            margin-top: 1.5rem;
        }

        .nav button {
            background-color: #2196F3;
            margin: 0 0.5rem;
        }

        .nav button:hover {
            background-color: #0b7dda;
        }

        /* 回答ボタン無効時 */
        button:disabled {
            background-color: #cccccc;
            cursor: not-allowed;
        }

        /* 結果表示のスタイル */
        .final-result {
            margin-top: 2rem;
            font-size: 1.2rem;
            color: #333333;
            background-color: #e0f7fa;
            padding: 1rem;
            border-radius: 5px;
        }
    </style>
<style>@charset "UTF-8";.felo--translation__loading{border-radius:100%!important;margin:2px!important;border:1px solid #bbb!important;border-bottom-color:transparent!important;height:14px!important;width:14px!important;background:0 0!important;display:inline-block!important;animation:feloLoadingRotate .75s 0s linear infinite}.felo--translate__element br:first-of-type{display:block;height:1px;line-height:1px;overflow:hidden;content:" "}.felo--translate__element br:first-of-type.felo--translation__safari__br{display:none}.felo--translate__element svg,.felo--translate__element path,.felo--translate__element input,.felo--translate__element form{display:none}.felo--translate__element img{max-width:20px;max-height:20px}@keyframes feloLoadingRotate{0%{transform:rotate(0) scale(1)}50%{transform:rotate(180deg) scale(1)}to{transform:rotate(360deg) scale(1)}}.felo--translation--success{font-size:0px!important}.felo--translation--success>:not(.felo--translate__element):not(.felo--summary):not(.felo-translate-tooltip):not(.felo--error--modal--root):not(.felo--screenshot):not(.felo--selection__menu){display:none}.felo--translation--success .felo--translate__element{font-size:initial!important;line-height:1.5}.felo--translation--success.felo--translate__fontsize10 .felo--translate__element{font-size:10px!important}.felo--translation--success.felo--translate__fontsize11 .felo--translate__element{font-size:11px!important}.felo--translation--success.felo--translate__fontsize12 .felo--translate__element{font-size:12px!important}.felo--translation--success.felo--translate__fontsize13 .felo--translate__element{font-size:13px!important}.felo--translation--success.felo--translate__fontsize14 .felo--translate__element{font-size:14px!important}.felo--translation--success.felo--translate__fontsize15 .felo--translate__element{font-size:15px!important}.felo--translation--success.felo--translate__fontsize16 .felo--translate__element{font-size:16px!important}.felo--translation--success.felo--translate__fontsize17 .felo--translate__element{font-size:17px!important}.felo--translation--success.felo--translate__fontsize18 .felo--translate__element{font-size:18px!important}.felo--translation--success.felo--translate__fontsize19 .felo--translate__element{font-size:19px!important}.felo--translation--success.felo--translate__fontsize20 .felo--translate__element{font-size:20px!important}.felo--translation--success.felo--translate__fontsize21 .felo--translate__element{font-size:21px!important}.felo--translation--success.felo--translate__fontsize22 .felo--translate__element{font-size:22px!important}.felo--translation--success.felo--translate__fontsize23 .felo--translate__element{font-size:23px!important}.felo--translation--success.felo--translate__fontsize24 .felo--translate__element{font-size:24px!important}.felo--translation--success.felo--translate__fontsize25 .felo--translate__element{font-size:25px!important}.felo--translation--success.felo--translate__fontsize26 .felo--translate__element{font-size:26px!important}.felo--translation--success.felo--translate__fontsize27 .felo--translate__element{font-size:27px!important}.felo--translation--success.felo--translate__fontsize28 .felo--translate__element{font-size:28px!important}.felo--translation--success.felo--translate__fontsize29 .felo--translate__element{font-size:29px!important}.felo--translation--success.felo--translate__fontsize30 .felo--translate__element{font-size:30px!important}.felo--translation--success.felo--translate__fontsize31 .felo--translate__element{font-size:31px!important}.felo--translation--success.felo--translate__fontsize32 .felo--translate__element{font-size:32px!important}.felo--translation--success.felo--translate__fontsize33 .felo--translate__element{font-size:33px!important}.felo--translation--success.felo--translate__fontsize34 .felo--translate__element{font-size:34px!important}.felo--translation--success.felo--translate__fontsize35 .felo--translate__element{font-size:35px!important}.felo--translation--success.felo--translate__fontsize36 .felo--translate__element{font-size:36px!important}.felo--translation--success.felo--translate__fontsize37 .felo--translate__element{font-size:37px!important}.felo--translation--success.felo--translate__fontsize38 .felo--translate__element{font-size:38px!important}.felo--translation--success.felo--translate__fontsize39 .felo--translate__element{font-size:39px!important}.felo--translation--success.felo--translate__fontsize40 .felo--translate__element{font-size:40px!important}.felo--translation--success.felo--translate__fontsize41 .felo--translate__element{font-size:41px!important}.felo--translation--success.felo--translate__fontsize42 .felo--translate__element{font-size:42px!important}.felo--translation--success.felo--translate__fontsize43 .felo--translate__element{font-size:43px!important}.felo--translation--success.felo--translate__fontsize44 .felo--translate__element{font-size:44px!important}.felo--translation--success.felo--translate__fontsize45 .felo--translate__element{font-size:45px!important}.felo--translation--success.felo--translate__fontsize46 .felo--translate__element{font-size:46px!important}.felo--translation--success.felo--translate__fontsize47 .felo--translate__element{font-size:47px!important}.felo--translation--success.felo--translate__fontsize48 .felo--translate__element{font-size:48px!important}.felo--translation--success.felo--translate__fontsize49 .felo--translate__element{font-size:49px!important}.felo--translation--success.felo--translate__fontsize50 .felo--translate__element{font-size:50px!important}.felo--translation--success.felo--translate__fontsize51 .felo--translate__element{font-size:51px!important}.felo--translation--success.felo--translate__fontsize52 .felo--translate__element{font-size:52px!important}.felo--translation--success.felo--translate__fontsize53 .felo--translate__element{font-size:53px!important}.felo--translation--success.felo--translate__fontsize54 .felo--translate__element{font-size:54px!important}.felo--translation--success.felo--translate__fontsize55 .felo--translate__element{font-size:55px!important}.felo--translation--success.felo--translate__fontsize56 .felo--translate__element{font-size:56px!important}.felo--translation--success.felo--translate__fontsize57 .felo--translate__element{font-size:57px!important}.felo--translation--success.felo--translate__fontsize58 .felo--translate__element{font-size:58px!important}.felo--translation--success.felo--translate__fontsize59 .felo--translate__element{font-size:59px!important}.felo--translation--success.felo--translate__fontsize60 .felo--translate__element{font-size:60px!important}.felo--translation--success.felo--translate__fontsize61 .felo--translate__element{font-size:61px!important}.felo--translation--success.felo--translate__fontsize62 .felo--translate__element{font-size:62px!important}.felo--translation--success.felo--translate__fontsize63 .felo--translate__element{font-size:63px!important}.felo--translation--success.felo--translate__fontsize64 .felo--translate__element{font-size:64px!important}.felo--translation--success.felo--translate__fontsize65 .felo--translate__element{font-size:65px!important}.felo--translation--success.felo--translate__fontsize66 .felo--translate__element{font-size:66px!important}.felo--translation--success.felo--translate__fontsize67 .felo--translate__element{font-size:67px!important}.felo--translation--success.felo--translate__fontsize68 .felo--translate__element{font-size:68px!important}.felo--translation--success.felo--translate__fontsize69 .felo--translate__element{font-size:69px!important}.felo--translation--success.felo--translate__fontsize70 .felo--translate__element{font-size:70px!important}.felo--translation--success.felo--translate__fontsize71 .felo--translate__element{font-size:71px!important}.felo--translation--success.felo--translate__fontsize72 .felo--translate__element{font-size:72px!important}.felo--translation--success.felo--translate__fontsize73 .felo--translate__element{font-size:73px!important}.felo--translation--success.felo--translate__fontsize74 .felo--translate__element{font-size:74px!important}.felo--translation--success.felo--translate__fontsize75 .felo--translate__element{font-size:75px!important}.felo--translation--success.felo--translate__fontsize76 .felo--translate__element{font-size:76px!important}.felo--translation--success.felo--translate__fontsize77 .felo--translate__element{font-size:77px!important}.felo--translation--success.felo--translate__fontsize78 .felo--translate__element{font-size:78px!important}.felo--translation--success.felo--translate__fontsize79 .felo--translate__element{font-size:79px!important}.felo--translation--success.felo--translate__fontsize80 .felo--translate__element{font-size:80px!important}.felo--translation--success.felo--translate__fontsize81 .felo--translate__element{font-size:81px!important}.felo--translation--success.felo--translate__fontsize82 .felo--translate__element{font-size:82px!important}.felo--translation--success.felo--translate__fontsize83 .felo--translate__element{font-size:83px!important}.felo--translation--success.felo--translate__fontsize84 .felo--translate__element{font-size:84px!important}.felo--translation--success.felo--translate__fontsize85 .felo--translate__element{font-size:85px!important}.felo--translation--success.felo--translate__fontsize86 .felo--translate__element{font-size:86px!important}.felo--translation--success.felo--translate__fontsize87 .felo--translate__element{font-size:87px!important}.felo--translation--success.felo--translate__fontsize88 .felo--translate__element{font-size:88px!important}.felo--translation--success.felo--translate__fontsize89 .felo--translate__element{font-size:89px!important}.felo--translation--success.felo--translate__fontsize90 .felo--translate__element{font-size:90px!important}.felo--translation--success.felo--translate__fontsize91 .felo--translate__element{font-size:91px!important}.felo--translation--success.felo--translate__fontsize92 .felo--translate__element{font-size:92px!important}.felo--translation--success.felo--translate__fontsize93 .felo--translate__element{font-size:93px!important}.felo--translation--success.felo--translate__fontsize94 .felo--translate__element{font-size:94px!important}.felo--translation--success.felo--translate__fontsize95 .felo--translate__element{font-size:95px!important}.felo--translation--success.felo--translate__fontsize96 .felo--translate__element{font-size:96px!important}.felo--translation--success.felo--translate__fontsize97 .felo--translate__element{font-size:97px!important}.felo--translation--success.felo--translate__fontsize98 .felo--translate__element{font-size:98px!important}.felo--translation--success.felo--translate__fontsize99 .felo--translate__element{font-size:99px!important}.felo--translation--success.felo--translate__fontsize100 .felo--translate__element{font-size:100px!important}.felo--translate--init--style{line-clamp:initial!important}.felo--translation_style_underline{text-decoration:underline}.felo--translation_style_highlight,.felo--translation_style_highlight *{background-color:#ff0!important}.felo--translation_style_auto_highlight{background-color:#fffe0433!important}.felo--translation_style_blue,.felo--translation_style_blue *{color:navy!important;background-color:#e6f3ff!important}.felo--translation_style_green,.felo--translation_style_green *{color:#006400!important;background-color:#e6ffe6!important}.felo--translation_style_orange,.felo--translation_style_orange *{color:#8b4513!important;background-color:#ffe4b5!important}.felo--translation_style_mask{filter:blur(5px)!important;border-radius:10px;display:inline-block;transition:all .3s!important}.felo--translation_style_mask:hover{filter:blur(0)!important}.felo--translation_style_italic{font-style:italic!important}.felo--translation_style_bold{font-weight:700!important}.felo--translation_style_opacity{opacity:.1!important;border-radius:10px;display:inline-block;transition:all .3s}.felo--translation_style_opacity:hover{opacity:1!important}.felo--translation_style_dashed{text-decoration:underline!important;text-decoration-color:#248df7!important;text-decoration-style:dashed!important}.felo--translation_style_wavy{text-decoration:underline!important;text-decoration-color:#1677ff!important;text-decoration-style:wavy!important}.felo--translate--error{text-align:center}.felo--translate--error h4{margin:1rem 0 2rem}.felo--translate--error .ant-modal-content{border-radius:20px}.felo--translate--error--content{text-align:left}.felo--translate--error--content .ant-typography code{display:block}.felo--translate--error--foot{margin:2rem 0}.felo--translate--error--switch{font-size:16px}.felo--translate--error--switch p{padding:0;margin:0}</style><base href="https://indepa.net/app/quiz.html"></head>
<body>
    <div class="container">
        <h1>選択肢式練習問題アプリ</h1>
        <!--
            使用方法:
            1. ページを開くと、自動的に問題が読み込まれます。
            2. 問題が表示されたら、適切な選択肢を選んで「回答を確認」ボタンをクリックします。
            3. 正誤と解説が表示され、スコアが更新されます。
            4. 「次の問題」ボタンで次の問題に進み、「前の問題」ボタンで前の問題に戻れます。
            5. 全問回答後、採点結果が表示されます。

            注意事項:
            - このアプリはフロントエンドのみで動作します。
            - CSVデータはサーバー上の「material/questions.csv」から自動的に読み込まれます。
            - 問題、選択肢、正解、解説はCSVファイルで管理されています。
        -->
        <div class="question" id="question">問題 1: 大規模言語モデルとは何ですか?</div>
        <ul class="options" id="options"><li><label><input type="radio" name="option" value="1" required=""> 大量のテキストデータを用いて訓練された機械学習モデルで、自然言語処理タスクを実行します。</label></li><li><label><input type="radio" name="option" value="3" required=""> 音声認識に特化したモデルです。</label></li><li><label><input type="radio" name="option" value="4" required=""> ロボット制御用のモデルです。</label></li><li><label><input type="radio" name="option" value="2" required=""> 画像認識を行うモデルです。</label></li></ul>
        <button id="submitAnswer">回答を確認</button>
        <div class="result" id="result"></div>
        <div class="explanation" id="explanation"></div>
        <div class="score" id="score">スコア: 0</div>
        <div class="nav">
            <button id="prevQuestion" disabled="">前の問題</button>
            <button id="nextQuestion" disabled="">次の問題</button>
        </div>
        <div class="final-result" id="finalResult" style="display: none;"></div>
    </div>

    <script>
        // CSVファイルのURLmaterialフォルダ内のquestions.csvconst csvUrl = 'material/questions.csv'; // quiz.html と同じディレクトリに material フォルダを配置

        // 問題と回答のデータ
        let qaData = [];
        let currentIndex = 0;
        let score = 0;
        let answered = []; // 各問題の回答状況を追跡

        const questionEl = document.getElementById('question');
        const optionsEl = document.getElementById('options');
        const submitAnswerBtn = document.getElementById('submitAnswer');
        const resultEl = document.getElementById('result');
        const explanationEl = document.getElementById('explanation');
        const scoreEl = document.getElementById('score');
        const prevBtn = document.getElementById('prevQuestion');
        const nextBtn = document.getElementById('nextQuestion');
        const finalResultEl = document.getElementById('finalResult');

        // CSVをフェッチしてパースする関数
        async function fetchAndParseCSV(url) {
            try {
                const response = await fetch(url);
                if (!response.ok) {
                    throw new Error(`HTTP error! Status: ${response.status}`);
                }
                const text = await response.text();
                parseCSV(text);
                // 初期化
                answered = Array(qaData.length).fill(false);
                displayQuestion();
                updateNavigationButtons();
            } catch (error) {
                questionEl.textContent = "問題の読み込みに失敗しました。CSVファイルが正しく配置されているか確認してください。";
                console.error('Error fetching CSV:', error);
            }
        }

        // CSVをパースする関数
        function parseCSV(text) {
            qaData = [];
            const lines = text.trim().split('\n');
            // 最初の行をヘッダーとしてスキップ
            for (let i = 1; i < lines.length; i++) {
                const line = lines[i];
                // ダブルクォーテーションで囲まれたカンマを考慮
                const regex = /(".*?"|[^",]+)(?=\s*,|\s*$)/g;
                const matches = line.match(regex);
                if (matches && matches.length >= 7) {
                    const [question, option1, option2, option3, option4, correct, ...explanationParts] = matches.map(item => item.replace(/(^"|"$)/g, '').trim());
                    const explanation = explanationParts.join(','); // 解説にカンマが含まれる場合を考慮
                    if (question && option1 && option2 && option3 && option4 && correct) {
                        qaData.push({
                            question,
                            options: [option1, option2, option3, option4],
                            correct: parseInt(correct), // 1, 2, 3, 4
                            explanation: explanation || '解説はありません。'
                        });
                    }
                }
            }
        }

        // 配列をシャッフルする関数(Fisher-Yatesアルゴリズム)
        function shuffle(array) {
            for (let i = array.length - 1; i > 0; i--) {
                const j = Math.floor(Math.random() * (i + 1));
                [array[i], array[j]] = [array[j], array[i]];
            }
            return array;
        }

        // 選択肢をシャッフルして表示する関数
        function displayQuestion() {
            if (qaData.length === 0) {
                questionEl.textContent = "問題がありません。CSVデータの読み込みに失敗しました。";
                optionsEl.innerHTML = '';
                submitAnswerBtn.disabled = true;
                return;
            }

            const qa = qaData[currentIndex];
            questionEl.textContent = `問題 ${currentIndex + 1}: ${qa.question}`;

            // 選択肢をシャッフル
            const shuffledOptions = shuffle([...qa.options]);

            optionsEl.innerHTML = '';
            shuffledOptions.forEach((option, index) => {
                const li = document.createElement('li');
                const label = document.createElement('label');
                const radio = document.createElement('input');
                radio.type = 'radio';
                radio.name = 'option';
                radio.value = qa.options.indexOf(option) + 1; // 元の選択肢番号を保持
                radio.required = true; // 必須入力
                label.appendChild(radio);
                label.appendChild(document.createTextNode(` ${option}`));
                li.appendChild(label);
                optionsEl.appendChild(li);
            });

            resultEl.textContent = '';
            explanationEl.textContent = '';

            // 回答済みの場合
            if (answered[currentIndex]) {
                const userAnswer = answered[currentIndex].userAnswer;
                const qaAnswer = qa.correct;
                if (userAnswer === qaAnswer) {
                    resultEl.textContent = "正解!";
                    resultEl.style.color = "#4CAF50";
                } else {
                    const correctOptionText = qa.options[qaAnswer - 1];
                    resultEl.textContent = `不正解。正しい回答は「${correctOptionText}」です。`;
                    resultEl.style.color = "#ff5722";
                }
                explanationEl.textContent = `解説: ${qa.explanation}`;
                submitAnswerBtn.disabled = true;
            } else {
                submitAnswerBtn.disabled = false;
            }

            // ナビゲーションボタンの状態を更新
            updateNavigationButtons();
        }

        // ナビゲーションボタンの状態を更新する関数
        function updateNavigationButtons() {
            // 「前の問題」ボタン
            if (currentIndex === 0) {
                prevBtn.disabled = true;
            } else {
                prevBtn.disabled = false;
            }

            // 「次の問題」ボタン
            if (answered[currentIndex]) {
                nextBtn.disabled = false;
            } else {
                nextBtn.disabled = true;
            }

            // 全問回答後の処理
            if (answered.every(ans => ans)) {
                nextBtn.disabled = true;
                submitAnswerBtn.disabled = true;
            }
        }

        // 回答を確認する関数
        submitAnswerBtn.addEventListener('click', function() {
            if (qaData.length === 0) return;

            const selectedOption = document.querySelector('input[name="option"]:checked');
            if (!selectedOption) {
                resultEl.textContent = "回答を選択してください。";
                resultEl.style.color = "#ff5722";
                explanationEl.textContent = '';
                return;
            }

            const userAnswer = parseInt(selectedOption.value);
            const qa = qaData[currentIndex];
            const correctAnswer = qa.correct;
            let isCorrect = false;

            if (userAnswer === correctAnswer) {
                resultEl.textContent = "正解!";
                resultEl.style.color = "#4CAF50";
                score++;
                isCorrect = true;
            } else {
                const correctOptionText = qa.options[correctAnswer - 1];
                resultEl.textContent = `不正解。正しい回答は「${correctOptionText}」です。`;
                resultEl.style.color = "#ff5722";
            }

            explanationEl.textContent = `解説: ${qa.explanation}`;

            // 回答済みとしてマーク
            answered[currentIndex] = {
                userAnswer: userAnswer
            };

            // スコアを更新
            scoreEl.textContent = `スコア: ${score}`;

            // 回答ボタンを無効化
            submitAnswerBtn.disabled = true;

            // ナビゲーションボタンの状態を更新
            updateNavigationButtons();

            // 全問回答後の処理
            if (answered.every(ans => ans)) {
                displayFinalResult();
            }
        });

        // 前の問題ボタンの処理
        prevBtn.addEventListener('click', function() {
            if (qaData.length === 0) return;
            if (currentIndex > 0) {
                currentIndex--;
                displayQuestion();
            } else {
                alert("これ以上前の問題はありません。");
            }
        });

        // 次の問題ボタンの処理
        nextBtn.addEventListener('click', function() {
            if (qaData.length === 0) return;
            if (currentIndex < qaData.length - 1) {
                currentIndex++;
                displayQuestion();
            } else {
                alert("これ以上次の問題はありません。");
            }
        });

        // 全問回答後に最終結果を表示する関数
        function displayFinalResult() {
            finalResultEl.style.display = 'block';
            finalResultEl.innerHTML = `全ての問題に回答しました。<br>最終スコア: ${score} / ${qaData.length}`;
        }

        // ページロード時にCSVデータをフェッチしてパース
        window.addEventListener('DOMContentLoaded', (event) => {
            fetchAndParseCSV(csvUrl);
        });
    </script>


<div data-wxt-integrated=""><div class="felo-dark"></div></div><div class="felo-floating"></div><div class="felo-panel"></div><div id="yt_article_summary_widget_wrapper" class="yt_article_summary_widget_wrapper" style="display: none;">
        <div id="yt_article_summary_widget" class="yt_article_summary_widget"><svg style="filter: brightness(0.8);" width="32" height="32" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
                  <mask id="path-1-outside-1_3606_3145" maskUnits="userSpaceOnUse" x="1" y="1" width="22" height="22" fill="black">
                  <rect fill="white" x="1" y="1" width="22" height="22"></rect>
                  <path d="M20.6816 10.1843C20.9588 9.34066 21.0063 8.4399 20.8192 7.57245C20.6321 6.70499 20.217 5.90134 19.6157 5.24216C19.0143 4.58298 18.2478 4.09146 17.393 3.81692C16.5382 3.54238 15.6253 3.49449 14.7459 3.67805C14.1453 3.01747 13.379 2.52468 12.524 2.24931C11.669 1.97394 10.7555 1.92571 9.87559 2.10947C8.99568 2.29324 8.18039 2.70252 7.51181 3.29608C6.84323 3.88965 6.34499 4.64654 6.06725 5.49055C5.18642 5.67292 4.3699 6.08122 3.70003 6.67426C3.03017 7.26731 2.53064 8.02413 2.25182 8.86842C1.97299 9.71271 1.92474 10.6146 2.11192 11.4832C2.2991 12.3517 2.71509 13.1562 3.31795 13.8155C3.09309 14.4899 3.01633 15.2037 3.09278 15.9095C3.16924 16.6154 3.39716 17.2971 3.76139 17.9093C4.30169 18.8351 5.12567 19.568 6.11483 20.0027C7.104 20.4373 8.20738 20.5512 9.26631 20.328C9.74353 20.8568 10.3291 21.2796 10.9844 21.5684C11.6396 21.8571 12.3495 22.0053 13.0672 22.003C14.1516 22.003 15.2081 21.6635 16.0847 21.0334C16.9612 20.4034 17.6125 19.5152 17.9449 18.4968C18.649 18.3539 19.3141 18.0649 19.8962 17.6489C20.4784 17.233 20.9642 16.6997 21.3214 16.0843C21.8585 15.1598 22.0858 14.0915 21.9709 13.032C21.856 11.9724 21.4048 10.9758 20.6816 10.1843ZM13.0798 20.6968C12.191 20.6968 11.3302 20.3894 10.6473 19.828L10.7677 19.7593L14.8029 17.4593C14.9069 17.4047 14.9935 17.3225 15.0528 17.2221C15.1121 17.1216 15.1418 17.0068 15.1386 16.8905V11.2655L16.8427 12.2405C16.8517 12.2441 16.8594 12.2501 16.865 12.2579C16.8706 12.2656 16.8739 12.2748 16.8744 12.2843V16.9343C16.876 17.4289 16.7785 17.9189 16.5875 18.3761C16.3964 18.8333 16.1156 19.2488 15.7611 19.5985C15.4067 19.9482 14.9856 20.2253 14.5222 20.4138C14.0588 20.6023 13.5621 20.6984 13.0608 20.6968H13.0798ZM4.90165 17.2593C4.46164 16.5029 4.3026 15.6189 4.45188 14.7593L4.57224 14.828L8.60749 17.128C8.70379 17.1829 8.81303 17.2118 8.92423 17.2118C9.03543 17.2118 9.14467 17.1829 9.24097 17.128L14.1758 14.3218V16.253C14.1797 16.2608 14.1817 16.2694 14.1817 16.278C14.1817 16.2867 14.1797 16.2953 14.1758 16.303L10.0962 18.628C9.66403 18.8748 9.18685 19.0352 8.69188 19.0999C8.19692 19.1647 7.69387 19.1326 7.21148 19.0055C6.72909 18.8784 6.27681 18.6587 5.88048 18.3591C5.48415 18.0595 5.15154 17.6858 4.90165 17.2593ZM3.83741 8.5843C4.28764 7.82089 4.99655 7.23878 5.83919 6.94055V11.6718C5.83595 11.7857 5.86434 11.8983 5.92128 11.9975C5.97823 12.0966 6.06156 12.1785 6.16227 12.2343L11.0717 15.028L9.36766 16.003C9.34918 16.0092 9.32914 16.0092 9.31065 16.003L5.23106 13.678C4.36041 13.1812 3.72487 12.3642 3.46364 11.4059C3.20242 10.4476 3.33682 9.42624 3.83741 8.56555V8.5843ZM17.8563 11.7968L12.9278 8.9718L14.6319 8.00305C14.6403 7.99741 14.6502 7.99439 14.6604 7.99439C14.6705 7.99439 14.6805 7.99741 14.6889 8.00305L18.7685 10.328C19.3915 10.684 19.8992 11.2072 20.2325 11.8368C20.5659 12.4664 20.7111 13.1764 20.6514 13.8843C20.5916 14.5921 20.3294 15.2687 19.8951 15.8352C19.4608 16.4017 18.8724 16.8349 18.1983 17.0843V12.353C18.1946 12.2391 18.1612 12.1281 18.1013 12.0306C18.0414 11.9332 17.957 11.8527 17.8563 11.7968ZM19.554 9.2968L19.4336 9.2218L15.4047 6.9093C15.3047 6.84846 15.1896 6.81624 15.0721 6.81624C14.9547 6.81624 14.8395 6.84846 14.7396 6.9093L9.8111 9.71555V7.75305C9.8061 7.7445 9.80346 7.7348 9.80346 7.72492C9.80346 7.71505 9.8061 7.70535 9.8111 7.6968L13.897 5.37805C14.5222 5.02257 15.2371 4.85003 15.958 4.88059C16.6789 4.91115 17.3762 5.14356 17.9682 5.55064C18.5601 5.95772 19.0225 6.52265 19.301 7.17939C19.5796 7.83614 19.663 8.55755 19.5413 9.2593L19.554 9.2968ZM8.87989 12.7218L7.1695 11.753C7.15339 11.7405 7.1422 11.7228 7.13782 11.703V7.06555C7.13785 6.35289 7.34371 5.65499 7.73128 5.0536C8.11885 4.45222 8.67209 3.97224 9.32619 3.6699C9.98029 3.36756 10.7082 3.25537 11.4246 3.34647C12.141 3.43757 12.8162 3.7282 13.3712 4.1843L13.2636 4.25305L9.21563 6.55305C9.11158 6.60765 9.02504 6.68981 8.96573 6.79029C8.90642 6.89076 8.87669 7.00557 8.87989 7.1218V12.7218ZM9.80476 10.753L11.9966 9.50305L14.1948 10.753V13.253L11.9966 14.503L9.79843 13.253L9.80476 10.753Z"></path>
                  </mask>
                  <path d="M20.6816 10.1843C20.9588 9.34066 21.0063 8.4399 20.8192 7.57245C20.6321 6.70499 20.217 5.90134 19.6157 5.24216C19.0143 4.58298 18.2478 4.09146 17.393 3.81692C16.5382 3.54238 15.6253 3.49449 14.7459 3.67805C14.1453 3.01747 13.379 2.52468 12.524 2.24931C11.669 1.97394 10.7555 1.92571 9.87559 2.10947C8.99568 2.29324 8.18039 2.70252 7.51181 3.29608C6.84323 3.88965 6.34499 4.64654 6.06725 5.49055C5.18642 5.67292 4.3699 6.08122 3.70003 6.67426C3.03017 7.26731 2.53064 8.02413 2.25182 8.86842C1.97299 9.71271 1.92474 10.6146 2.11192 11.4832C2.2991 12.3517 2.71509 13.1562 3.31795 13.8155C3.09309 14.4899 3.01633 15.2037 3.09278 15.9095C3.16924 16.6154 3.39716 17.2971 3.76139 17.9093C4.30169 18.8351 5.12567 19.568 6.11483 20.0027C7.104 20.4373 8.20738 20.5512 9.26631 20.328C9.74353 20.8568 10.3291 21.2796 10.9844 21.5684C11.6396 21.8571 12.3495 22.0053 13.0672 22.003C14.1516 22.003 15.2081 21.6635 16.0847 21.0334C16.9612 20.4034 17.6125 19.5152 17.9449 18.4968C18.649 18.3539 19.3141 18.0649 19.8962 17.6489C20.4784 17.233 20.9642 16.6997 21.3214 16.0843C21.8585 15.1598 22.0858 14.0915 21.9709 13.032C21.856 11.9724 21.4048 10.9758 20.6816 10.1843ZM13.0798 20.6968C12.191 20.6968 11.3302 20.3894 10.6473 19.828L10.7677 19.7593L14.8029 17.4593C14.9069 17.4047 14.9935 17.3225 15.0528 17.2221C15.1121 17.1216 15.1418 17.0068 15.1386 16.8905V11.2655L16.8427 12.2405C16.8517 12.2441 16.8594 12.2501 16.865 12.2579C16.8706 12.2656 16.8739 12.2748 16.8744 12.2843V16.9343C16.876 17.4289 16.7785 17.9189 16.5875 18.3761C16.3964 18.8333 16.1156 19.2488 15.7611 19.5985C15.4067 19.9482 14.9856 20.2253 14.5222 20.4138C14.0588 20.6023 13.5621 20.6984 13.0608 20.6968H13.0798ZM4.90165 17.2593C4.46164 16.5029 4.3026 15.6189 4.45188 14.7593L4.57224 14.828L8.60749 17.128C8.70379 17.1829 8.81303 17.2118 8.92423 17.2118C9.03543 17.2118 9.14467 17.1829 9.24097 17.128L14.1758 14.3218V16.253C14.1797 16.2608 14.1817 16.2694 14.1817 16.278C14.1817 16.2867 14.1797 16.2953 14.1758 16.303L10.0962 18.628C9.66403 18.8748 9.18685 19.0352 8.69188 19.0999C8.19692 19.1647 7.69387 19.1326 7.21148 19.0055C6.72909 18.8784 6.27681 18.6587 5.88048 18.3591C5.48415 18.0595 5.15154 17.6858 4.90165 17.2593ZM3.83741 8.5843C4.28764 7.82089 4.99655 7.23878 5.83919 6.94055V11.6718C5.83595 11.7857 5.86434 11.8983 5.92128 11.9975C5.97823 12.0966 6.06156 12.1785 6.16227 12.2343L11.0717 15.028L9.36766 16.003C9.34918 16.0092 9.32914 16.0092 9.31065 16.003L5.23106 13.678C4.36041 13.1812 3.72487 12.3642 3.46364 11.4059C3.20242 10.4476 3.33682 9.42624 3.83741 8.56555V8.5843ZM17.8563 11.7968L12.9278 8.9718L14.6319 8.00305C14.6403 7.99741 14.6502 7.99439 14.6604 7.99439C14.6705 7.99439 14.6805 7.99741 14.6889 8.00305L18.7685 10.328C19.3915 10.684 19.8992 11.2072 20.2325 11.8368C20.5659 12.4664 20.7111 13.1764 20.6514 13.8843C20.5916 14.5921 20.3294 15.2687 19.8951 15.8352C19.4608 16.4017 18.8724 16.8349 18.1983 17.0843V12.353C18.1946 12.2391 18.1612 12.1281 18.1013 12.0306C18.0414 11.9332 17.957 11.8527 17.8563 11.7968ZM19.554 9.2968L19.4336 9.2218L15.4047 6.9093C15.3047 6.84846 15.1896 6.81624 15.0721 6.81624C14.9547 6.81624 14.8395 6.84846 14.7396 6.9093L9.8111 9.71555V7.75305C9.8061 7.7445 9.80346 7.7348 9.80346 7.72492C9.80346 7.71505 9.8061 7.70535 9.8111 7.6968L13.897 5.37805C14.5222 5.02257 15.2371 4.85003 15.958 4.88059C16.6789 4.91115 17.3762 5.14356 17.9682 5.55064C18.5601 5.95772 19.0225 6.52265 19.301 7.17939C19.5796 7.83614 19.663 8.55755 19.5413 9.2593L19.554 9.2968ZM8.87989 12.7218L7.1695 11.753C7.15339 11.7405 7.1422 11.7228 7.13782 11.703V7.06555C7.13785 6.35289 7.34371 5.65499 7.73128 5.0536C8.11885 4.45222 8.67209 3.97224 9.32619 3.6699C9.98029 3.36756 10.7082 3.25537 11.4246 3.34647C12.141 3.43757 12.8162 3.7282 13.3712 4.1843L13.2636 4.25305L9.21563 6.55305C9.11158 6.60765 9.02504 6.68981 8.96573 6.79029C8.90642 6.89076 8.87669 7.00557 8.87989 7.1218V12.7218ZM9.80476 10.753L11.9966 9.50305L14.1948 10.753V13.253L11.9966 14.503L9.79843 13.253L9.80476 10.753Z" fill="#828282"></path>
                  <path d="M20.6816 10.1843C20.9588 9.34066 21.0063 8.4399 20.8192 7.57245C20.6321 6.70499 20.217 5.90134 19.6157 5.24216C19.0143 4.58298 18.2478 4.09146 17.393 3.81692C16.5382 3.54238 15.6253 3.49449 14.7459 3.67805C14.1453 3.01747 13.379 2.52468 12.524 2.24931C11.669 1.97394 10.7555 1.92571 9.87559 2.10947C8.99568 2.29324 8.18039 2.70252 7.51181 3.29608C6.84323 3.88965 6.34499 4.64654 6.06725 5.49055C5.18642 5.67292 4.3699 6.08122 3.70003 6.67426C3.03017 7.26731 2.53064 8.02413 2.25182 8.86842C1.97299 9.71271 1.92474 10.6146 2.11192 11.4832C2.2991 12.3517 2.71509 13.1562 3.31795 13.8155C3.09309 14.4899 3.01633 15.2037 3.09278 15.9095C3.16924 16.6154 3.39716 17.2971 3.76139 17.9093C4.30169 18.8351 5.12567 19.568 6.11483 20.0027C7.104 20.4373 8.20738 20.5512 9.26631 20.328C9.74353 20.8568 10.3291 21.2796 10.9844 21.5684C11.6396 21.8571 12.3495 22.0053 13.0672 22.003C14.1516 22.003 15.2081 21.6635 16.0847 21.0334C16.9612 20.4034 17.6125 19.5152 17.9449 18.4968C18.649 18.3539 19.3141 18.0649 19.8962 17.6489C20.4784 17.233 20.9642 16.6997 21.3214 16.0843C21.8585 15.1598 22.0858 14.0915 21.9709 13.032C21.856 11.9724 21.4048 10.9758 20.6816 10.1843ZM13.0798 20.6968C12.191 20.6968 11.3302 20.3894 10.6473 19.828L10.7677 19.7593L14.8029 17.4593C14.9069 17.4047 14.9935 17.3225 15.0528 17.2221C15.1121 17.1216 15.1418 17.0068 15.1386 16.8905V11.2655L16.8427 12.2405C16.8517 12.2441 16.8594 12.2501 16.865 12.2579C16.8706 12.2656 16.8739 12.2748 16.8744 12.2843V16.9343C16.876 17.4289 16.7785 17.9189 16.5875 18.3761C16.3964 18.8333 16.1156 19.2488 15.7611 19.5985C15.4067 19.9482 14.9856 20.2253 14.5222 20.4138C14.0588 20.6023 13.5621 20.6984 13.0608 20.6968H13.0798ZM4.90165 17.2593C4.46164 16.5029 4.3026 15.6189 4.45188 14.7593L4.57224 14.828L8.60749 17.128C8.70379 17.1829 8.81303 17.2118 8.92423 17.2118C9.03543 17.2118 9.14467 17.1829 9.24097 17.128L14.1758 14.3218V16.253C14.1797 16.2608 14.1817 16.2694 14.1817 16.278C14.1817 16.2867 14.1797 16.2953 14.1758 16.303L10.0962 18.628C9.66403 18.8748 9.18685 19.0352 8.69188 19.0999C8.19692 19.1647 7.69387 19.1326 7.21148 19.0055C6.72909 18.8784 6.27681 18.6587 5.88048 18.3591C5.48415 18.0595 5.15154 17.6858 4.90165 17.2593ZM3.83741 8.5843C4.28764 7.82089 4.99655 7.23878 5.83919 6.94055V11.6718C5.83595 11.7857 5.86434 11.8983 5.92128 11.9975C5.97823 12.0966 6.06156 12.1785 6.16227 12.2343L11.0717 15.028L9.36766 16.003C9.34918 16.0092 9.32914 16.0092 9.31065 16.003L5.23106 13.678C4.36041 13.1812 3.72487 12.3642 3.46364 11.4059C3.20242 10.4476 3.33682 9.42624 3.83741 8.56555V8.5843ZM17.8563 11.7968L12.9278 8.9718L14.6319 8.00305C14.6403 7.99741 14.6502 7.99439 14.6604 7.99439C14.6705 7.99439 14.6805 7.99741 14.6889 8.00305L18.7685 10.328C19.3915 10.684 19.8992 11.2072 20.2325 11.8368C20.5659 12.4664 20.7111 13.1764 20.6514 13.8843C20.5916 14.5921 20.3294 15.2687 19.8951 15.8352C19.4608 16.4017 18.8724 16.8349 18.1983 17.0843V12.353C18.1946 12.2391 18.1612 12.1281 18.1013 12.0306C18.0414 11.9332 17.957 11.8527 17.8563 11.7968ZM19.554 9.2968L19.4336 9.2218L15.4047 6.9093C15.3047 6.84846 15.1896 6.81624 15.0721 6.81624C14.9547 6.81624 14.8395 6.84846 14.7396 6.9093L9.8111 9.71555V7.75305C9.8061 7.7445 9.80346 7.7348 9.80346 7.72492C9.80346 7.71505 9.8061 7.70535 9.8111 7.6968L13.897 5.37805C14.5222 5.02257 15.2371 4.85003 15.958 4.88059C16.6789 4.91115 17.3762 5.14356 17.9682 5.55064C18.5601 5.95772 19.0225 6.52265 19.301 7.17939C19.5796 7.83614 19.663 8.55755 19.5413 9.2593L19.554 9.2968ZM8.87989 12.7218L7.1695 11.753C7.15339 11.7405 7.1422 11.7228 7.13782 11.703V7.06555C7.13785 6.35289 7.34371 5.65499 7.73128 5.0536C8.11885 4.45222 8.67209 3.97224 9.32619 3.6699C9.98029 3.36756 10.7082 3.25537 11.4246 3.34647C12.141 3.43757 12.8162 3.7282 13.3712 4.1843L13.2636 4.25305L9.21563 6.55305C9.11158 6.60765 9.02504 6.68981 8.96573 6.79029C8.90642 6.89076 8.87669 7.00557 8.87989 7.1218V12.7218ZM9.80476 10.753L11.9966 9.50305L14.1948 10.753V13.253L11.9966 14.503L9.79843 13.253L9.80476 10.753Z" stroke="#828282" stroke-width="0.2" mask="url(#path-1-outside-1_3606_3145)"></path>
              </svg></div>
        <div id="yt_article_summary_close_button" class="yt_article_summary_close_button">×</div>
    </div><div id="eagle-drag-images" style="position: fixed; top: -100000px;"></div><text-blaze-app-reference style="display: contents !important;"></text-blaze-app-reference></body>

5. ChatGPTとの対話の再現

このプロジェクトを通じて、ChatGPTとの対話がどのように進行したかを具体的に振り返ります。これにより、読者の皆さんが同様のプロジェクトをChatGPTとどのように進めるかのイメージを掴めるようにしました。

5.1 初期対話

プロジェクトの最初のステップとして、ChatGPTにアプリの仕様について相談しました。以下は、その際の対話の一部です。

ユーザーのプロンプト:

役割: あなたはシングルページ Web アプリケーション開発の専門家です。
指示: 4択の選択肢式練習問題を出題するアプリについてのシングルページ Web アプリケーションのコードを出力してください。
条件:
• フロントエンドのみで動作する Web アプリケーションにしてください。サーバーサイドの機能は使用しません
• スタイリッシュでモダンなデザインにしてください
• htmlには、使用方法と注意事項を記載してください
• 出力内容: htmlコードの中にCSS,JavaScriptコードを含めてください
• 出力形式: コードブロックに出力してください
• 問題と回答のデータベースとして、CSVファイルを読み込むようにする

ChatGPTの応答:

ChatGPTは、指定された要件に基づいてシングルページのウェブアプリケーションのコードを提供しました。この初期対話により、プロジェクトの基本的な方向性と機能要件が明確になりました。ChatGPTは、フロントエンドのみで動作するスタイリッシュなデザインのアプリケーションを提案し、HTML内にCSSとJavaScriptを含める形でのコードを生成しました。また、CSVファイルから問題と回答を読み込む機能についても具体的に対応しました。

5.2 機能の追加と修正

開発を進める中で、ユーザーは追加の機能や修正を求めました。例えば、選択肢のランダムシャッフル、無回答時の制御、全問回答後の採点結果表示などです。

ユーザーのプロンプト:

選択肢をランダムにシャッフルして表示するようにしてください。
また、無回答の場合は、先に進めないようにしてください。
全問回答後は、採点した結果を表示するようにしてください。

ChatGPTの応答:

ChatGPTは、指定された要件に基づいて`quiz.html`ファイルを修正し、選択肢のランダムシャッフル、無回答時の制御、全問回答後の採点結果表示を実現するコードを提供しました。また、各機能の実装方法について詳細な説明も行いました。

5.3 デバッグと最適化

開発中に発生した問題点についても、ChatGPTに相談しました。例えば、選択肢のシャッフルが期待通りに動作しない場合や、無回答時にボタンが適切に無効化されない場合などです。

ユーザーのプロンプト:

回答しない場合は、先に進めないようにして欲しかったのですが、そうなっていません。その辺のロジックを見直してください。

ChatGPTの応答:

申し訳ありません。前回のコードでは、ユーザーが回答を選択せずに「次の問題」ボタンをクリックできてしまう問題がありました。以下に、選択肢をランダムにシャッフルし、回答しない場合は先に進めないようにし、全問回答後に採点結果を表示するように修正した `quiz.html` ファイルを提供します。

このように、具体的な問題点を伝えることで、ChatGPTは修正案や最適化されたコードを提案してくれました。

以下から試していただけます。
このようなアプリを30分もあれば作れてしまいます。

6. まとめ

ChatGPT(o1-mini)を活用することで、ウェブアプリ開発の各ステップが非常にスムーズに進行しました。具体的な機能要件の設定から、コーディング、デバッグ、最終調整に至るまで、対話形式で効率的にサポートを受けることができました。

主なポイント

  1. 迅速な開発プロセス:ChatGPTの提案により、必要なコードや実装方法を迅速に得ることができ、開発スピードが向上しました。

  2. 高い拡張性:プロジェクトの要件が変わった際にも、柔軟に対応できるアドバイスとコード出力を受けることができました。

  3. 簡潔なコード管理:ディレクトリ構成の整理やファイルの配置について具体的な指示を受け、コード管理が容易になりました。

ChatGPTを活用することで、コツさえ掴めれば、プロンプトだけで、ウェブアプリ開発が初心者にも手軽に行えるようになります。ChatGPTを活用して、自分で使いたいウェブアプリ開発を始めてみてはいかがでしょうか。

o1-miniで作るWebアプリシリーズ続編

【重版出来】AIアプリをプログラミング不要で開発する書籍

ChatGPTでAIアプリを作って仕事で使いたい方と世界に公開したい方へ!

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