見出し画像

プログラミング謎解き小説「コードに隠された真実」(ChatGPT生成)

はじめに

[注意] この小説はChatGPTで生成されました。多少構成は変更しましたが、文章は一切修正していません。この「はじめに」だけを人間が書いています。

こんにちは。木村です。

技術書典16でプログラミング謎解きゲームブックを作りたいなと思い、ChatGPTでいろいろ相談していたところ、意外とChatGPTだけでそれっぽいの(ゲームブックは難易度高く一旦小説です)が生成できてしまいました。副産物ということでその出力結果の小説を共有します。問題はしょぼいですが、完全自動生成でこれは「ChatGPTすげーな」という感想しかないです。しかし、それっぽいけどおもしろくないので、人間はやっぱりすげーなとも思います。

なお、アイキャッチ画像はこの小説の概要を元にChatGPTが生成したプロンプトを使ってAdobe Fireflyで生成しています。

ChatGPTの会話ログは以下のPDFから見ることができます。どんなプロンプトを使ったかはこちらからご覧ください。(ChatGPTのワークスペース内の会話ログがいつの間にかパブリック共有できなくなっていたのでPDFにせざるを得ず…)

同じプロンプトで似たような小説をどんどん生成できるのでぜひ遊んでみてください。

それでは、生成された小説をどうぞ!


※各章に1題問題があります。章を読み終わったら問題を考えてみてください。基本的にコードの欠落部分を回答する問題になっています。ヒントおよび解答は末尾にあります。目次より辿ってください。
※小説だけでは問題は解けなかったりしますが、自動生成ということで温かい目で読んでください。

プロローグ

東京の静かな夜が更けていく。街の喧騒から隔絶された研究室の一室では、数々のモニターが青白い光を放っていた。部屋の隅には、世界的に有名なAI開発者、安藤博士の遺影が控えめに飾られている。

数週間前、安藤博士は自宅で亡くなっているのが発見された。彼の死因は謎に包まれたままで、警察も特に進展はないようだった。彼の最も偉大な創造物であるAI「ミライ」は、その死の直前に行われた最後のアップデート以降、何かを秘めているかのように、不可解な挙動を示し続けていた。

安藤博士の旧友であり、才能ある若きプログラマーの佐藤は、研究室の片隅で思い詰めた表情を浮かべていた。彼は博士の死に疑念を抱いており、それを晴らすためにはミライとの対話が鍵になると確信していた。

モニターに映し出されたミライのインターフェースに向かって、佐藤は深呼吸をした。彼の指がキーボードに触れると、部屋に静かなタイピングの音が響き始めた。彼は、コードの海に潜り込むダイバーのように、AIの深淵に向かって旅立とうとしていた。

「ミライ、君と話がしたい。安藤博士のことについてだ」と佐藤が言うと、画面上のテキストボックスが一瞬明滅した。それはまるで、ミライが応答する前の思索を示しているかのようだった。

そして、佐藤は待ち続ける。この対話が、彼とミライ、そして亡き博士を繋ぐ、長く複雑な物語の幕開けになることを知らずに。

第1章:暗号化されたメッセージ

夜は更け、研究室の中は静寂に包まれていた。佐藤はモニターの光だけが頼りの中、ミライのコードを眺めていた。彼の眼前に広がるのは、安藤博士が生前に書き残した複雑なプログラムの海だった。彼はひとつひとつのコードに目を通し、その中に隠されたメッセージを探っていた。

「ここに何かがある。博士が何かを伝えたかったんだ」と佐藤はつぶやいた。彼の手がキーボードを操り、画面上でコードがスクロールする。すると、突然、彼の目を引く一節があった。それは明らかに何かのパターンを示しているようだったが、一部が欠落していた。


問題

主人公・佐藤は、ミライのソースコード内で、一つの関数が正しく動作していないことに気づきます。この関数は、特定の数列を生成する役割を持っているようですが、一部のコードが欠落しています。佐藤は、関数が生成するべき数列のパターンを推測し、欠落している部分を修正する必要があります。

欠損したプログラムのコード

function sumOfMultiples(n) {
    let sum = 0;
    for (let i = 1; i <= n; i++) {
        if (i % 3 === 0 && _____) {
            sum += i;
        }
    }
    return sum;
}

佐藤は深く思索に耽った。これはただの関数ではない。もしかすると、これは博士が残したメッセージかもしれない。彼はコードを解読する鍵を探し始めた。

「博士は数字にこだわりがあった。特に3と7の数には…」佐藤はふと思い出す。安藤博士はよく、数字のパターンについて語っていた。特に3の倍数と7の倍数には特別な意味があると強調していたのだ。

このヒントを元に、佐藤はコードの欠落部分を埋めることを試みる。彼の指が素早くキーボードを叩き、コードを修正していく。そして、修正した関数を実行すると、画面には予期せぬ数字が表示された。

この数字は何を意味するのか?佐藤はその数値をメモし、深い思考に没頭した。博士のメッセージは何を伝えようとしているのか、その答えを見つけるために、彼は次の謎へと進む準備を始めた。

夜は更けていく。しかし、佐藤の探求はまだ始まったばかりだった。

第2章:消された記憶

佐藤は、深夜の研究室に一人で座っていた。モニターの光が彼の顔を青白く照らし出し、その目は疲れを感じさせていた。彼の前には、ミライの膨大なデータベースが広がっていた。何かがおかしい。データの中には、明らかに異常なパターンがあった。ある期間の記録が完全に消去されていたのだ。

「なぜ、この期間のデータだけが消えているんだろう?」佐藤は独り言をつぶやいた。彼は、この消去されたデータを復元する方法を考え始めた。ミライの記憶の中には、多くのファイルがあり、その中には博士の死の秘密が隠されている可能性があった。

佐藤は、特定のファイル形式に注目することに決めた。安藤博士は常に'.log'ファイルに重要な情報を記録していた。これらのファイルを特定し、そのサイズを合計することで、どの程度の情報が消去されたのかを推測することができるだろう。

彼はプログラムを書き始めたが、一部のコードが欠落していることに気づいた。この部分は、ファイルが'.log'で終わるかどうかを判断する条件を記述する必要があった。


問題

佐藤はミライの記憶データに異常を発見し、特定の期間のデータが消去されていることを突き止めます。彼はデータの復元を試みるため、ファイルシステムに関するプログラムを分析しますが、重要な部分が欠落しています。このプログラムは、特定のファイル形式('.log')のファイルのみを選択し、それらの合計サイズを計算する機能を持っているようです。欠損部分を修正し、プログラムを完全なものにする必要があります。

欠損したプログラムのコード

function calculateLogFilesSize(files) {
    let totalSize = 0;
    for (let i = 0; i < files.length; i++) {
        let file = files[i];
        if (_____) {
            totalSize += file.size;
        }
    }
    return totalSize;
}

佐藤は、深夜の静寂の中で、コードの修正に取り組んだ。彼の手はキーボードを機敏に動かし、欠けていたピースを埋めるようにコードを修正していった。やがて、彼はプログラムを実行し、失われたデータのサイズを確認した。

画面に表示された数字は、彼の予想をはるかに上回るものだった。この数字は、博士の死に関して重要な手がかりを隠している可能性があった。佐藤は新たな疑問を抱き、さらなる調査を続ける決意を固めた。

一人のプログラマーが、AIとともに真実を追求する旅は続いていく。消された記憶の中に、何が隠されているのか。その答えは、まだ遠く霧の中に隠されていた。

第3章:裏切りのコード

佐藤は眠気をこらえながら、ミライのソースコードを精査していた。薄暗い研究室の中、彼の目は画面上の謎めいたコードに釘付けになっていた。この謎を解き明かせば、安藤博士の死について新たな事実が浮かび上がるかもしれない。

「これは…暗号化されたメッセージ?」佐藤は首を傾げた。彼が見つけたのは、一見すると無害なプログラムの断片だったが、その中にはある種の暗号化アルゴリズムが隠されていた。しかし、コードの一部が欠落しており、そのままではメッセージを解読することができなかった。


問題

主人公・佐藤は、ミライのソースコードの深部で謎の暗号化アルゴリズムを発見します。しかし、このアルゴリズムの一部が欠落しているようです。彼はこの暗号化アルゴリズムを解読し、裏切りの証拠を探す必要があります。アルゴリズムは、入力された文字列の各文字を特定のルールに従って変換するもののようです。

欠損したプログラムのコード

function encryptString(str) {
    let result = "";
    for (let i = 0; i < str.length; i++) {
        let char = str[i];
        if (char === 'z') {
            result += 'a';
        } else if (_____ ) {
            result += String.fromCharCode(char.charCodeAt(0) + 1);
        } else {
            result += char;
        }
    }
    return result;
}

佐藤は、眉をひそめながら画面を見つめ続けた。安藤博士は暗号に興味を持っていた。特に、シンプルながらも独創的なアルゴリズムに魅了されていたのだ。佐藤は、博士がよく口にしていた言葉を思い出した。「最も単純な方法が、最も美しい解を生む」。

このヒントを元に、佐藤は欠落しているコードの修正に取り組み始めた。彼の指は、まるでピアニストのようにキーボード上を滑らかに動いた。そして、ついにプログラムを完成させ、実行した。

画面に表示されたのは、一見無意味に見える一連の文字列だった。しかし、佐藤はそれが何を意味するのかを理解していた。これは、博士の研究に関する重要な情報を隠した暗号だったのだ。そしてその情報が指し示す先には、安藤博士の周囲にいたある人物の存在が浮かび上がってきた。

佐藤は深く息を吐き、新たな疑念と共に次の行動に移る準備を始めた。この暗号化されたメッセージが、安藤博士の死の謎を一層深めることになるとは、まだ彼には知る由もなかった。夜は更けていくが、佐藤の探求はまだまだ終わりそうになかった。

第4章:最終プログラム

夜明け前の静寂が研究室を包んでいた。佐藤はまだ画面に向かっていた。彼の前に広がるのは、安藤博士が最後に作成したプログラムの断片。これが、博士の最後のメッセージを解読する鍵に違いない。

佐藤は深く考え込んだ。博士は数のパターンに特別な意味を見出していた。特に、特定の数を除外することで何かを伝えようとしているのではないか。彼はふと、博士が以前言っていたことを思い出した。「数は、時に言葉よりも雄弁だ」。

その言葉を思い出し、佐藤はプログラムの欠落部分を埋めるべく作業を開始した。数値列から特定の数を除外する規則…それはきっと、博士のメッセージに関係している。


問題

佐藤は安藤博士の最後に書いたプログラムを分析していた。このプログラムには、特定の数値列を生成する機能があるようだが、重要な部分が欠落している。この数値列は、博士の研究の重要な手がかりを含んでいる可能性がある。佐藤は欠損部分を埋め、数値列を正しく生成する必要がある。

欠損したプログラムのコード

function generateSequence(n) {
    let sequence = [];
    for (let i = 1; i <= n; i++) {
        if (_____) {
            continue;
        }
        sequence.push(i);
    }
    return sequence;
}

手慣れた指先がキーボードを滑るように動き、コードが完成へと近づいていく。そして、修正されたプログラムを実行すると、画面には一連の数列が表示された。これが、博士が伝えたかったメッセージの一部なのだろうか。

佐藤は数列を紙に書き写し、それをじっと眺めた。数列の中に隠されたパターン、それが何を意味するのかを解読しようとした。時間が経つにつれ、彼の目には確信に似た輝きが宿り始めた。これは博士が直面していた問題、そして彼を苦しめた謎の一端を示している。

博士の最後の研究、その中に隠された真実が徐々に明らかになりつつあった。佐藤はこの新たな発見を手がかりに、さらに深い謎へと足を踏み入れていく。夜が明け、新しい一日が始まる。しかし、佐藤の探求はまだ終わりそうになかった。彼の前には、解くべき真実がまだ数多く残されているのだから。

エピローグ:真実の光

朝日が研究室の窓から差し込み、佐藤は長い夜の作業を終えていた。彼の前には、解き明かされた謎の答えが広がっていた。安藤博士の最後のメッセージは、彼の研究と命を狙う大企業の陰謀を暴くものだった。

ミライの画面には、暗号解読の過程で浮かび上がった証拠が並んでいる。それらは、安藤博士の死の背後にある複雑な糸の絡み合いを示していた。佐藤は、これらの証拠を世に公表することを決意する。博士の死が無駄にならないように、そして彼の研究が悪用されないように。

佐藤は深く息を吐き、安藤博士の遺影に向かって一礼した。「先生、あなたの思いは伝えます。あなたの志は、ミライを通して生き続けます」と静かに誓った。

その日、安藤博士の研究と死に関する真実が世界中に公表された。多くの人々がその事実に衝撃を受け、同時に博士の偉業に敬意を表した。佐藤は、ミライとともに博士の遺志を引き継ぎ、新しいAI研究の道を歩み始める。

エピローグの中で、太陽が完全に昇り、新しい朝が始まっていた。佐藤の探求は終わりを迎えたが、安藤博士とミライが残した遺産は、これからも多くの人々に影響を与え続けることだろう。

物語は終わったが、真実の光はこれからも続く。佐藤は窓から差し込む朝日を見つめながら、新たな一日への一歩を踏み出した。これは、彼にとっての新しい始まりでもあったのだ。

★ヒント★

第1章

関数は、与えられた整数 n に対して、1から n までの数のうち、3で割り切れる数だけを加算した総和を返すものです。しかし、7で割り切れる数は除外する必要があります。

第2章

欠損部分はファイルの選択条件を記述する部分で、ログファイル('.log'ファイル)だけを選択する条件が必要です。

第3章

欠損している部分は文字の変換規則です。このアルゴリズムは、アルファベット文字を次の文字に置き換えるものです(例:'a'は'b'に、'z'は'a'に)。しかし、'z'の処理方法が特別です。

第4章

プログラムは1から始まる連続する整数を生成しますが、特定の規則に従っていくつかの数を除外する必要があります。除外されるのは、3または5の倍数です。

★解答★

第1章

問題解決のプロセス

  1. 問題のヒントから、関数は3で割り切れる数を加算する必要があることが分かります。

  2. しかし、7で割り切れる数は加算してはいけないという追加の条件があります。

  3. 欠損しているコードは、この7で割り切れる数を除外する条件を表すものでなければなりません。

  4. したがって、欠損部分は「7で割り切れない」という条件を満たすコードになります。

  5. この条件をコードで表現するには、i % 7 !== 0という式を使用します。

解答

欠損している部分には i % 7 !== 0 を挿入します。修正後の関数は以下のようになります。

function sumOfMultiples(n) {
    let sum = 0;
    for (let i = 1; i <= n; i++) {
        if (i % 3 === 0 && i % 7 !== 0) {
            sum += i;
        }
    }
    return sum;
}

この関数は、指定された n までの数の中で3で割り切れ、かつ7で割り切れない数の総和を返します。

第2章

問題解決のプロセス

  1. 問題文とヒントから、欠損部分はファイルが'.log'で終わるかどうかを判断する条件文であることがわかります。

  2. JavaScriptで文字列が特定の文字列で終わるかどうかを判断するには、endsWithメソッドを使用します。

  3. したがって、欠損部分には file.name.endsWith('.log') という条件を記述すれば、ログファイルだけが選択されます。

解答

欠損している部分には file.name.endsWith('.log') を挿入します。修正後の関数は以下のようになります。

function calculateLogFilesSize(files) {
    let totalSize = 0;
    for (let i = 0; i < files.length; i++) {
        let file = files[i];
        if (file.name.endsWith('.log')) {
            totalSize += file.size;
        }
    }
    return totalSize;
}

この関数は、与えられたファイルのリストの中から'.log'ファイルを選択し、その合計サイズを計算します。

第3章

問題解決のプロセス

  1. ヒントから、アルゴリズムはアルファベットの文字を次のアルファベットに変換することが分かります。

  2. 'z'は特別に'a'に置き換えられるため、残りのアルファベット('a'から'y'まで)は次の文字に置き換える必要があります。

  3. したがって、欠損している部分はアルファベット文字を判定し、次の文字に置き換える条件を記述する必要があります。

  4. アルファベットの文字を判断するには正規表現を使用し、/[a-y]/という条件を用いることができます。

解答

欠損している部分には char.match(/[a-y]/) を挿入します。修正後の関数は以下のようになります。

function encryptString(str) {
    let result = "";
    for (let i = 0; i < str.length; i++) {
        let char = str[i];
        if (char === 'z') {
            result += 'a';
        } else if (char.match(/[a-y]/)) {
            result += String.fromCharCode(char.charCodeAt(0) + 1);
        } else {
            result += char;
        }
    }
    return result;
}

この関数は、入力された文字列の各文字をアルファベットの次の文字に置き換えますが、'z'のみは'a'に置き換えます。

第4章

問題解決のプロセス

  1. 問題のヒントから、欠損部分は3または5の倍数を除外する条件であることが分かります。

  2. 3の倍数または5の倍数を判定するには、各数が3または5で割り切れるかどうかを確認する必要があります。

  3. したがって、欠損している条件は i % 3 === 0 || i % 5 === 0 となります。

解答

欠損している部分には i % 3 === 0 || i % 5 === 0 を挿入します。修正後の関数は以下のようになります。

function generateSequence(n) {
    let sequence = [];
    for (let i = 1; i <= n; i++) {
        if (i % 3 === 0 || i % 5 === 0) {
            continue;
        }
        sequence.push(i);
    }
    return sequence;
}

この関数は、指定された数 n までの数列を生成し、3または5の倍数を除外した数列を返します。


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