6
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

生成AIを簡単に組み込めるMarkdown AIで「相関図クイズゲーム」を作ってみた!

Last updated at Posted at 2025-01-24

この記事でわかること

  • Markdown AIを使ったオリジナルコンテンツ制作の事例
  • 「相関図クイズゲーム」の制作プロセス
  • Markdown AIの良かった点&改善希望ポイント

はじめに

突然ですがクイズです。

これはなんの相関図でしょう?

Screenshot 2025-01-29 at 10.33.06.png

                👇

                👇

                👇

                👇

                👇

                👇

                👇

                👇

                👇

                👇

                👇

                👇

                👇

                👇

答えは「桃太郎」でした!
Screenshot 2025-01-29 at 10.33.26.png
👦 - 桃太郎
👴 - おじいさん
👵 - おばあさん
🐕 - 犬
🐒 - 猿
🐦 - キジ
👹 - 鬼

今回私は、Markdown AI を使って「相関図クイズゲーム」という遊べるWebアプリを制作しました!

制作したアプリはこちら です!

この記事では、制作したクイズゲームの内容や、Markdown AIを使った制作プロセスについて詳しく紹介します。Markdown記法を駆使し、ロボットAIMermaidを活用するアイデアも盛り込みました。
「Markdown AIって何?」という方にもわかりやすく、かつ楽しく読める記事を目指しています。


相関図クイズゲームとは?

「相関図クイズゲーム」は、ある作品のキャラクターや関係性をヒントに、その作品名を当てるシンプルなクイズです。
クイズを解く中で、キャラクター同士の関係性やストーリーの奥深さも楽しむことができます!

例えば、ジャンルを「国民的アニメ」に設定すると、「ドラえもん」や「クレヨンしんちゃん」などが出題される仕組みです。

主な機能

  1. ジャンルを選んでスタートボタンをクリック!ジャンルは自分で入力することも可能!
  2. クイズに挑戦(ヒントや相関図を確認しながら楽しめます)
  3. 答えがわからない場合は答えを見る機能も完備!

制作に使用した技術

Markdown AIを選んだ理由

Markdown AIは、QiitaなどでおなじみのMarkdown記法を使いながら、簡単にWebサイトやツールを作れるツールです。特に以下の点に魅力を感じました:

  • HTML/CSS、JavaScript対応で高度なカスタマイズが可能。
  • ロボットAIを使ったAIの組み込みがスムーズ。
  • サーバーを設定せずにサイトを公開できる。

使用した主な技術や機能

  • Mermaid
    キャラクター相関図を作成するのに使用。関係性を視覚的に表現しました。
  • JavaScript
    ユーザーインタラクション(スタートボタンや次のクイズ機能)を実装。
  • Markdown AIのロボットAI
    クイズ生成プロセスで利用。プロンプトを入力するだけで、関係図やキャラクター情報を自動生成しました。

制作プロセス

  1. アイデア出し

    • 「みんなが知っている作品をクイズにしたら面白そう」と思い、相関図を使ったゲームを企画。
    • ジャンルごとに出題作品を選定。
  2. Markdown AI環境で開発

    • HTMLとJavaScriptを組み合わせてUIを作成。
    • Mermaidを使って相関図の描画に挑戦。
    • ロボットAIでクイズ生成を自動化。
  3. テストと改善

    • クイズ生成時のエラーや、相関図が正しく表示されない問題をデバッグ。
    • ユーザーが操作しやすいインターフェースに微調整。

Markdown AIを使った感想

良かった点

  1. 手軽さとスピード
    サイト制作のハードルが大幅に下がりました。特に、サーバー不要で即公開できる点は画期的!

  2. AIの活用
    簡単に生成AI(gpt-4o-mini、Claude、Geminiなど)を組み込んだアプリが作れるのがとてもいいと思いました。

  3. 無料
    AIを使用するためのAPIキーを発行する必要がなく、全て無料で使えるところが非常にありがたいです。

もう少しだった点

  1. Mermaidのサポート
    Markdown AIでは Mermaid の描画がサポートされているのですが、スクリプトで動的に Mermaid コードを作成した場合は描画されず、個別に JavaScript で Mermaid 描画用のライブラリを CDN からダウンロードして使用する必要がありました。

  2. ドキュメントの不足
    今はβ版とのことで、まだドキュメントが十分に揃っていないように感じました。最初 Markdown AI で何ができるかがすぐには分からず、Qiitaの記事などを参考に把握することができました。

  3. ファイル数の制限
    1つのサイトを作成するのに1ファイルだけしか作れないので、全てのコードを1ファイルにまとめる必要があります。

こんな感じで1ファイルにまとめています
# 相関図クイズ!

<p>相関図の作品名を当てるクイズゲーム🤖</p>
<form id="genreForm">
    <select id="genre" name="genre">
        <option value="" disabled selected style="display:none;">ジャンルを選んでね!</option>
    </select>
</form>
<form id="customGenre">
    <input type="text" id="customGenreInput" placeholder="自分でジャンルを入力!">
</form>
<button class="button" type="button" id="button-start">スタート!</button>
<div style="display: none;" id="loading-section">
    <div class="loader"></div>
    <p id="text-loading">
        クイズ作成中...
        ちょっと待っててね!(もしかしたら1分くらいかかっちゃうかも🥺)
    </p>
</div>
<div id="diagram"></div>
<div class="buttons">
    <button class="button " type="button" id="button-hint" style="display: none;">💡 グループ分けを見る</button>
    <button class="button " type="button" id="button-answer" style="display: none;">👀 答えを見る </button>
</div>
<h3 id="answer"></h3>
<p id="emojiMap"></p>
<button class="button" style="display: none;" type="button" id="button-next">👉 次の問題</button>

<style>
    body {
        font-family: Arial, sans-serif;
        background-color: #F2EFE7;
        margin: 0;
        padding: 20px;
    }

    h1 {
        color: #777;
        font-size: 24px;
        margin-bottom: 10px;
    }

    p {
        color: #777;
        font-size: 16px;
    }

    form {
        margin: 10px 0;
    }

    select,
    input {
        font-size: 16px;
        padding: 8px;
        margin: 5px;
        border: 1px solid #ccc;
        border-radius: 5px;
        width: 250px;
    }

    .button {
        font-size: 18px;
        padding: 10px 20px;
        margin: 10px;
        border: none;
        border-radius: 5px;
        background-color: #48A6A7;
        color: #F2EFE7;
        cursor: pointer;
        transition: 0.3s;
    }

    .button:hover {
        background-color: #9ACBD0;
        color: #F2EFE7;
    }

    #loading-section {
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        margin-top: 20px;
    }

    .loader {
        border: 5px solid #F2EFE7;
        border-top: 5px solid #2973B2;
        border-radius: 50%;
        width: 40px;
        height: 40px;
        animation: spin 1s linear infinite;
    }

    @keyframes spin {
        0% {
            transform: rotate(0deg);
        }

        100% {
            transform: rotate(360deg);
        }
    }

    #text-loading {
        font-size: 14px;
        color: #777;
        margin-top: 10px;
    }

    #diagram {
        margin-top: 20px;
        padding: 10px;
        border: 1px solid #ddd;
        background: white;
        min-height: 100px;
        border-radius: 5px;
    }

    .buttons {
        margin-top: 10px;
    }

    h3 {
        color: #777;
        font-size: 20px;
        margin-top: 20px;
    }
</style>

<script>
    document.title = "相関図クイズ!";
    document.querySelector('meta[property="og:site_name"]').setAttribute('content', '相関図クイズ!');
    async function loadMermaid() {
        const response = await fetch("https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js");
        const scriptContent = await response.text();
        const scriptElement = document.createElement("script");
        scriptElement.textContent = scriptContent;
        document.head.appendChild(scriptElement);

        mermaid.initialize({ startOnLoad: true });

        const genres = [
            "国民的アニメ", "少年漫画", "少女漫画", "スポーツ漫画", "NHKアニメ", "日常系アニメ", "ジブリ", "ディズニー", "ディズニープリンセス", "ピクサー", "日本の昔話", "世界の昔話", "グリム童話", "ことわざ", "四字熟語", "古事成語", "絵本", "児童書", "国語の教科書に載ってる話", "日本の偉人伝", "世界の偉人伝", "ミステリー小説", "国民的ドラマ", "朝ドラ", "月9ドラマ", "90年代人気ドラマ", "00年代人気ドラマ", "2010年代人気ドラマ", "2020年代人気ドラマ", "バラエティ番組", "邦画", "洋画", "ハリウッド名作", "アメリカンコメディドラマ", "数学の定理"
        ];

        const form = document.getElementById('genreForm');
        const select = document.getElementById('genre');


        genres.forEach((genre) => {
            const option = document.createElement('option');
            option.value = genre;
            option.textContent = genre;
            select.appendChild(option);
        });

        form.appendChild(select);

        let quizzes = [];
        let currentQuizIndex = 0;
        let isCreatingQuiz = false;

        function cleanseJson(jsonString) {
            jsonString = jsonString.replace(/'/g, '"');
            jsonString = jsonString.replace(/,\s*([}\]])/g, '$1');
            jsonString = jsonString.replace(/([,:{\[]\s*)([a-zA-Z0-9_]+)\s*(?=\s*:)/g, '$1"$2"');
            jsonString = jsonString.replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\t/g, '\\t');
            return jsonString;
        }

        function parseCharacterData(input) {
            try {
                const jsonStart = input.indexOf("{");
                const jsonEnd = input.lastIndexOf("}") + 1;
                if (jsonStart === -1 || jsonEnd === 0) {
                    throw new Error("JSONが見つかりません");
                }

                const jsonString = input.substring(jsonStart, jsonEnd);
                const processedJsonString = cleanseJson(jsonString);
                const parsedData = JSON.parse(processedJsonString);
                const diagram = parsedData["相関図"] || "";
                let diagramWithoutSubgraph = "";

                // 相関図のセクションを解析
                if (diagram) {
                    diagramWithoutSubgraph = diagram.replace(/subgraph (.+) \[(.*?)\]/g, (match, p1, p2) => `subgraph ${p1} [ ]`);
                }

                return {
                    relationships: parsedData["キャラクター同士の関係性"] || "",
                    diagram: diagram,
                    diagramWithoutSubgraph: diagramWithoutSubgraph,
                    emojiMap: parsedData["キャラクターと絵文字の対応表"] || ""
                };
            } catch (error) {
                console.error("データのパースに失敗しました:", error.message);
                return null;
            }
        }

        const startButton = document.getElementById('button-start');
        startButton.addEventListener('click', async event => {
            isCreatingQuiz = true;
            const selectedGenre = select.value;
            const customGenreInput = document.getElementById('customGenreInput').value;
            if (selectedGenre && customGenreInput) {
                alert('ジャンルを一つだけ選んでね!');
            } else if (selectedGenre || customGenreInput) {
                const genre = selectedGenre || customGenreInput;
                const loadingSection = document.getElementById('loading-section');
                loadingSection.style.display = 'flex';
                startButton.style.display = 'none';
                document.getElementById('diagram').innerHTML = "";
                const serverAi = new ServerAI();

                const prompt = `${genre}の人気の作品を10個教えてください。出力はカンマで区切り、それ以外の文章は加えないでください。出力例:「ドラえもん, クレヨンしんちゃん, ドラゴンボール, サザエさん, ちびまる子ちゃん, キン肉マン, ワンピース, ナルト, 名探偵コナン, あたしんち」`;
                const answer = await serverAi.getAnswerText('7M1Cgeu4c9xhSyGwfYMCux', '', prompt);

                const stories = answer.split(',').map(story => story.trim());


                try {
                    const quizPromises = stories.map(async (story, index) => {
                        const storyPrompt = `${genre}の「${story}」ついて、以下の出力例(ドラえもん)と同じ形式で、キャラクター同士の関係性とmaermaidの相関図とキャラクターと絵文字の対応表を作ってください。相関図の中の各キャラクターは絵文字1つで表現してください。\n注意点:\n1.嘘を書かないでください。あなたが持っている正確な情報の範囲で文章を生成してください。情報がない場合は「わかりません」のみ出力してください。2. maemaid記法は参考例を忠実に真似しシンタックスエラーが発生しないようにしてください。3. subgraphは必ず[]を使ってラベルをつけてください\n\n以下、ドラえもんを例にした出力形式\n{"キャラクター同士の関係性": "ドラえもんは22世紀からやってきたロボット猫で、のび太の良き相談相手であり支援者です。のび太は主人公で、成績が悪く運動も苦手ですが、優しい心を持っています。しずかちゃんはのび太の同級生で、のび太が好意を持っている女の子です。ジャイアンは力が強く、時にのび太をいじめる存在ですが、友情も持っています。スネ夫はジャイアンの取り巻きで、お金持ちの家庭の息子です。ドラミはドラえもんの妹で、時々未来からやってきて皆を手助けします。","相関図": "graph TD\\nsubgraph School [学校]\\nNobita[🧒]\\nShizuka[👧]\\nGian[👦]\\nSuneo[👦]\\nend\\n\\nsubgraph Future [未来]\\nDoraemon[🐱]\\nDorami[🐱]\\nend\\n\\nNobita <--親友--> Doraemon\\nNobita --好き--> Shizuka\\nNobita --いじめられる--> Gian\\nNobita --いじめられる--> Suneo\\nGian --手下--> Suneo\\nDoraemon --妹--> Dorami\\nDoraemon --助ける--> Nobita","キャラクターと絵文字の対応表": "🧒 - のび太\\n👧 - しずかちゃん\\n👦 - ジャイアン\\n👦 - スネ夫\\n🐱 - ドラえもん\\n🐱 - ドラミ"}`;
                        const storyAnswer = await serverAi.getAnswerText('7M1Cgeu4c9xhSyGwfYMCux', '', storyPrompt);
                        const parsedAnswer = parseCharacterData(storyAnswer);
                        if (parsedAnswer) {
                            quizzes.push({
                                story: story,
                                relationships: parsedAnswer.relationships,
                                diagram: parsedAnswer.diagram,
                                diagramWithoutSubgraph: parsedAnswer.diagramWithoutSubgraph,
                                emojiMap: parsedAnswer.emojiMap
                            });
                            loadingSection.style.display = 'none';
                            const buttonHint = document.getElementById('button-hint');
                            const buttonAnswer = document.getElementById('button-answer');

                            const isShowingNextButton = nextButton.style.display === 'block';

                            if (!isShowingNextButton) {
                                showQuiz(currentQuizIndex);
                            }
                        }
                    });

                    // バックグラウンドでリクエストが続行
                    await Promise.allSettled(quizPromises);
                    isCreatingQuiz = false;
                } catch (error) {
                    console.error("エラーが発生しました:", error.message);
                    alert("データの取得中にエラーが発生しました。もう一度試してください。");
                }
            } else {
                alert('ジャンルを選んでね!');
            }
        });

        // クイズの内容を表示
        function showQuiz(index) {
            if (quizzes.length > 0 && quizzes[index]) {

                buttonHint.style.display = 'block';
                buttonAnswer.style.display = 'block';
                nextButton.style.display = 'block';
                const quiz = quizzes[index];

                const diagramDiv = document.getElementById('diagram');
                diagramDiv.removeAttribute('data-processed');
                diagramDiv.innerHTML = quiz.diagramWithoutSubgraph;
                diagramDiv.classList.add("mermaid");
                mermaid.init();
            }
        }

        // 次の問題に進む
        const nextButton = document.getElementById('button-next');
        nextButton.addEventListener('click', () => {
            document.getElementById('answer').innerText = "";
            document.getElementById('emojiMap').innerText = "";
            currentQuizIndex++;
            if (currentQuizIndex < quizzes.length) {
                showQuiz(currentQuizIndex);
            } else {
                nextButton.style.display = 'none';
                buttonAnswer.style.display = 'none';
                buttonHint.style.display = 'none';
                const diagramDiv = document.getElementById('diagram');
                diagramDiv.removeAttribute('data-processed');
                diagramDiv.classList.remove("mermaid");
                if (isCreatingQuiz) {
                    document.getElementById('loading-section').style.display = 'flex';
                    diagramDiv.innerHTML = "";
                } else {
                    startButton.style.display = 'block';
                    select.value = '';
                    select.focus();
                    document.getElementById('customGenreInput').value = '';
                    diagramDiv.innerHTML = "全部のクイズが終わったよ👏\nジャンルを変えてまた遊んでみてね✨";
                }
            }
        });

        // ヒントを見るボタン
        const buttonHint = document.getElementById('button-hint');
        buttonHint.addEventListener('click', () => {
            const quiz = quizzes[currentQuizIndex];
            const diagramDiv = document.getElementById('diagram');
            diagramDiv.removeAttribute('data-processed');
            diagramDiv.innerHTML = quiz.diagram;
            diagramDiv.classList.add("mermaid");
            mermaid.init();
            buttonHint.style.display = 'none';
        });

        // 答えを見るボタン
        const buttonAnswer = document.getElementById('button-answer');
        buttonAnswer.addEventListener('click', () => {
            const quiz = quizzes[currentQuizIndex];
            document.getElementById('answer').innerText = `答えは「${quiz.story}」でした!`;
            document.getElementById('emojiMap').innerText = quiz.emojiMap;
            buttonAnswer.style.display = 'none';
        });

    }
    loadMermaid();
</script>

まとめ

Markdown AIを使った「相関図クイズゲーム」の制作プロセスを紹介しました!
このツールは、Markdown記法の手軽さと高度な機能を両立しており、クリエイターや開発者にとって強力な味方です。

今回のプロジェクトでは、「ロボットAI」と「Mermaid」を駆使して、クイズ生成の効率化と視覚的な楽しさを両立させました。ぜひ皆さんもMarkdown AIを使って、オリジナルコンテンツを制作してみてください!

👇 Markdown AIはこちらから試せます👇
Markdown AI公式サイト

👇相関図クイズゲームはこちらから👇
相関図クイズゲーム

6
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?