はじめに
皆様は「ソクラテスラ~キメラティック偉人バトル~」(以降、ソクラテスラ)というゲームをご存じでしょうか。ソクラテスラとは、Azb.Studio様の製作した、様々な偉人を組み合わせて戦うキメラ偉人カードゲームのことです。
例えば、「ニコラ・テスラ」「ナイチンゲール」「シャーロック・ホームズ」を組み合わせて「ニコチン・ホームズ」というトンデモ偉人を作ることができます。
詳しくはこちら: Azb.Studio — ソクラテスラ
今回はこのソクラテスラを模して、JavaScriptで誰もが知っているであろう歴史上の偉人の名前や経歴をランダムに混ぜて生成する「キメラ偉人ジェネレーター~Chimeric Heroes~」というものを作成してみました。
仕様
配列データとして定義された歴史上の偉人の名前と経歴のパーツを、ランダムに組み合わせて新しい架空の偉人(キメラ偉人)を生成するWebアプリケーションです。
rightArms (右腕/名前・経歴の始点)、torsos (胴体/名前・経歴の中点)、leftArms (左腕/名前・経歴の終点)の3つの配列に、62人分の人物データがそれぞれで格納されています。
「新しい偉人を生成」ボタンを押すと、3つの配列からそれぞれランダムに1つのパーツを選択し、名前と経歴の文字列を結合して新たな偉人を生成し、「生成結果」の下に表示します。
生成された偉人は10件まで履歴に保存され、リスト形式で表示されます。履歴は「削除」ボタンを押して削除することができます。保存人数が10件を超えたらどれかを削除するように要求します。
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Chimeric Heroes</title>
<link rel="resetcss" href="https://unpkg.com/ress@4.0.0/dist/ress.min.css">
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>キメラ偉人ジェネレーター</h1>
<p>ランダムに偉人のパーツを組み合わせて、架空のキメラ偉人を爆誕させます。</p>
<button id="generateBtn">新しい偉人を生成</button>
<h2>生成結果</h2>
<div id="resultName">(未生成)</div>
<div id="resultDesc"></div>
<h2>履歴(最大10件)</h2>
<div id="historyArea"></div>
<script src="main.js"></script>
</body>
</html>
「新しい偉人を生成」ボタンと、生成結果を表示する用のdivタグ、履歴を表示する領域用のdivタグを用意しています。
CSS
body {
font-family: sans-serif;
max-width: 700px;
margin: 0 auto;
padding: 20px;
line-height: 1.6;
}
h1 {
text-align: center;
}
#historyArea div {
border-bottom: 1px solid #ccc;
padding: 5px 0;
}
.deleteBtn {
color: red;
cursor: pointer;
margin-left: 10px;
}
履歴を表示するエリアにボーダーを引いたり、削除ボタンを赤く表示させたりするようにしています。
JavaScript
// -------------------------
// ① パーツデータ
// 偉人のデータをrightArm(右腕)、torsos(胴体)、leftArm(左腕)の3つの配列に分けて格納。
// -------------------------
let rightArms = [
{ name: "アリス", desc: "古代ギリシアで学問を究める。" },
{ name: "クレオ", desc: "プトレマイオス朝のファラオとして即位。" },
{ name: "レオナルド", desc: "フィレンツェ出身の芸術家。" },
{ name: "ナポレ", desc: "フランスで軍人として頭角を現す。" },
{ name: "マリ", desc: "ポーランド出身の物理学者。" },
{ name: "アンネリース", desc: "ユダヤ系ドイツ人の少女。" },
{ name: "ガ", desc: "18歳でイギリスにて法律を学ぶ。" },
{ name: "リン", desc: "アメリカ南北戦争の最中に政治家として活躍。" },
{ name: "ウィンストン", desc: "英国王政復古期の指導者として名を馳せる。" },
{ name: "オットー", desc: "プロイセン王国の首相に就任。" },
{ name: "アー", desc: "ブリテンの伝説的な君主として知られる、ログレス王国の王。" },
{ name: "アレクサ", desc: "マケドニアの若き王として即位した。" },
{ name: "イエ", desc: "ナザレの大工の子として生まれた。" },
{ name: "ギルガ", desc: "古代ウルクの伝説的な半神の王。" },
{ name: "ソク", desc: "古代アテーナイで活動した哲学者。" },
{ name: "ヨシ", desc: "ソビエト連邦の2代目最高指導者。" },
{ name: "ジャン", desc: "神の声を聞いたオルレアンの少女。" },
{ name: "ナイ", desc: "クリミア戦争の野戦病院に赴任。" },
{ name: "チン", desc: "モンゴル高原の諸部族を統一した。" },
{ name: "ニコ", desc: "電流戦争でエジソンと対立。" },
{ name: "クー", desc: "ケルト神話のアルスターの英雄。" },
{ name: "ヘー", desc: "ゼウスの血を引く半神として生まれる。" },
{ name: "ジル", desc: "ジャンヌ・ダルクの戦友だった救国の英雄。" },
{ name: "イス", desc: "神の使いと格闘し、勝った者。" },
{ name: "ガイウス", desc: "共和政ローマの軍人であり政治家。" },
{ name: "アイザ", desc: "リンゴの落下を見て閃いたと言われる。" },
{ name: "始", desc: "戦国時代の中国を初めて統一した。" },
{ name: "アナク", desc: "ミレトス学派の自然哲学者。" },
{ name: "アナ", desc: "天体や自然現象を合理的に説明した。" },
{ name: "ハン", desc: "バビロン第1王朝の王として即位。" },
{ name: "シャル", desc: "亡命政府「自由フランス」を指揮した。" },
{ name: "ラム", desc: "古代エジプト第19王朝のファラオ。" },
{ name: "アッ", desc: "フン族の王として君臨。" },
{ name: "朱", desc: "貧農から身を起こして紅巾の乱に参加。" },
{ name: "ムスタ", desc: "オスマン帝国の将軍から革命家へ転身。" },
{ name: "アイン", desc: "特許局に勤めながら論文を発表。" },
{ name: "シモン", desc: "南米大陸の解放者と呼ばれる。" },
{ name: "ピョ", desc: "ロシア・ツァーリ国の君主。" },
{ name: "ノルマン", desc: "フランスから海を渡りイギリスに侵攻。" },
{ name: "ハン", desc: "カルタゴの将軍としてローマに挑む。" },
{ name: "ヨシップ", desc: "パルチザンを率いてドイツ軍に抵抗。" },
{ name: "フェ", desc: "スペイン帝国の最盛期を築く。" },
{ name: "インノケ", desc: "中世カトリック教会の頂点に立つ。" },
{ name: "司", desc: "前漢の時代、宮刑の屈辱を受ける。" },
{ name: "ヘロ", desc: "諸国を遍歴し見聞を広める。" },
{ name: "アッシュ", desc: "アッシリア帝国の最盛期を統治。" },
{ name: "カラ", desc: "セウェルス朝のローマ皇帝。" },
{ name: "ダレ", desc: "アケメネス朝ペルシア最後の王。" },
{ name: "ザラス", desc: "古代イランで善悪二元論を説く。" },
{ name: "グーテ", desc: "マインツ出身の金細工師。" },
{ name: "ゲ", desc: "ヴァイマル公国で政治に携わる。" },
{ name: "シ", desc: "劇作家として数々の戯曲を残す。" },
{ name: "ディオク", desc: "軍人皇帝時代の混乱を収束させる。" },
{ name: "ジョン", desc: "若きアメリカ合衆国大統領。" },
{ name: "ホメ", desc: "古代ギリシアの盲目の吟遊詩人。" },
{ name: "ティ", desc: "西チャガタイ・ハン国の出身。" },
{ name: "ウラジ", desc: "ボリシェヴィキを率いて革命を指導。" },
{ name: "プ", desc: "ソクラテスの弟子として学ぶ。" },
{ name: "ゴータ", desc: "シャカ族の王子として生まれ育つ。" },
{ name: "クリスト", desc: "西回りでインドを目指し航海に出る。" },
{ name: "カール", desc: "プロイセン出身の哲学者・経済学者。" },
{ name: "アドル", desc: "天性のカリスマで国家社会主義ドイツ労働者党を率いる。" },
{ name: "ムハ", desc: "メッカの商人だったが神の啓示を受ける。" },
];
let torsos = [
{ name: "ト", desc: "多くの思想を整理し、" },
{ name: "パトラ", desc: "その美貌と知略で、" },
{ name: "・ダ", desc: "博学者、科学者、占星術師としての一面も持ち、" },
{ name: "オン", desc: "優れた戦術と知力で、" },
{ name: "・スクウォドフスカ", desc: "放射線研究にその生涯を捧げ、" },
{ name: "・マリー", desc: "『アンネの日記』の著者として知られ、" },
{ name: "ン", desc: "非暴力不服従を唱え、" },
{ name: "カー", desc: "奴隷制度廃止のため奔走し、" },
{ name: "・チャー", desc: "戦略的決断で国を導き、" },
{ name: "・フォン", desc: "「鉄血演説」を行い軍備を拡張し、" },
{ name: "サー", desc: "円卓の騎士たちを率いて聖杯を探求し、" },
{ name: "ンドロス", desc: "東方遠征を敢行してペルシアを滅ぼし、" },
{ name: "ス・キリ", desc: "人々に神の愛と赦しを説いて回り、" },
{ name: "メッ", desc: "親友であるエンキドゥを喪い不老不死の旅に出て、" },
{ name: "ラ", desc: "「無知の知」を人々に問いかけ続け、" },
{ name: "フ・スター", desc: "粛清と五カ年計画を強引に推し進め、" },
{ name: "ヌ・ダ", desc: "オルレアンの包囲を解きシャルル7世を戴冠させ、" },
{ name: "チン", desc: "統計学を用いて医療衛生改革を行い、" },
{ name: "ギス", desc: "圧倒的な騎馬軍団を組織して、" },
{ name: "ラ", desc: "交流電流システムを発明し普及させ、" },
{ name: "フー", desc: "魔槍ゲイ・ボルグを自在に操り、" },
{ name: "ラク", desc: "ヒュドラー退治などの「十二の難業」を成し遂げ、" },
{ name: "・ド", desc: "晩年は錬金術と黒魔術に傾倒し、" },
{ name: "ラ", desc: "一族を率いてエジプトへ移住し、" },
{ name: "・ユリウス", desc: "ガリア遠征を成功させ「賽は投げられた」と叫び、" },
{ name: "ック・ニュー", desc: "万有引力の法則や微積分法を発見し、" },
{ name: "皇", desc: "法家思想に基づき度量衡や文字を統一し、" },
{ name: "シマン", desc: "万物のアルケーを「無限なるもの(ト・アペイロン)」とし、" },
{ name: "クサゴ", desc: "万物の種子(スペルマタ)説を提唱し、" },
{ name: "ム", desc: "「目には目を」で有名な同害報復の原則に基づく法を定め、" },
{ name: "ル・ド", desc: "対独レジスタンスを呼びかけ第五共和政を樹立し、" },
{ name: "セス", desc: "カデシュの戦いでヒッタイトと激突し、" },
{ name: "ティ", desc: "東西ローマ帝国を略奪と恐怖に陥れ、" },
{ name: "元", desc: "元軍を北へ追いやって明を建国し、" },
{ name: "ファ", desc: "トルコ共和国を建国して政教分離を進め、" },
{ name: "シュ", desc: "相対性理論を提唱して時空の概念を変え、" },
{ name: "・ボリ", desc: "スペインの植民地支配に対して立ち上がり、" },
{ name: "ートル", desc: "西欧化政策を断行しサンクトペテルブルクを建設し、" },
{ name: "ディ公ウィ", desc: "ヘイスティングズの戦いでイングランド軍を破り、" },
{ name: "ニ", desc: "象を連れてアルプス山脈を越え、" },
{ name: "・ブロズ", desc: "独自の社会主義路線で国をまとめ上げ、" },
{ name: "リペ", desc: "レパントの海戦でオスマン帝国を破り、" },
{ name: "ンティウス", desc: "第4回十字軍を提唱し各国の王を破門で従わせ、" },
{ name: "馬", desc: "父の遺志を継いで歴史の編纂に没頭し、" },
{ name: "ド", desc: "ペルシア戦争の経緯や各地の風俗を記録し、" },
{ name: "ール・バニ", desc: "古今の粘土板を収集して図書館を建設し、" },
{ name: "カラ", desc: "全自由民にローマ市民権を与え、" },
{ name: "イオス", desc: "イッソスの戦いやガウガメラの戦いで敗れ、" },
{ name: "シュトラ", desc: "アフラ・マズダーを最高神として崇め、" },
{ name: "ンベ", desc: "活版印刷技術を実用化・改良し、" },
{ name: "ー", desc: "『若きウェルテルの悩み』で名声を博し、" },
{ name: "ラ", desc: "ゲーテと親交を深め『群盗』などを発表し、" },
{ name: "レティ", desc: "帝国を四分割統治する四帝分治制(テトラルキア)を導入し、" },
{ name: "・F", desc: "キューバ危機を回避しアポロ計画を宣言し、" },
{ name: "ーロ", desc: "トロイア戦争の英雄たちの物語を紡ぎ、" },
{ name: "ムー", desc: "中央アジアから西アジアを席巻して、" },
{ name: "ーミル・レ", desc: "世界初の社会主義国家を樹立して、" },
{ name: "ラ", desc: "イデア論を説いてアカデメイアを創設し、" },
{ name: "マ・シッダ", desc: "生老病死の苦しみを超えるため出家し、" },
{ name: "ファー・コロン", desc: "サンタ・マリア号で大西洋を横断し、" },
{ name: "・マル", desc: "資本主義社会の構造を鋭く分析し、" },
{ name: "フ・ヒ", desc: "第二次世界大戦を引き起こしホロコーストを行い、" },
{ name: "ンマ", desc: "唯一神アッラーの教えを広めるため戦い、" },
];
let leftArms = [
{ name: "テレス", desc: "西洋哲学の礎を築いた。" },
{ name: "7世", desc: "ローマ帝国の権力者たちを操った。" },
{ name: "・ヴィンチ", desc: "ルネサンスの象徴となった。" },
{ name: "・ボナパルト", desc: "欧州全土に影響を与えた。" },
{ name: "=キュリー", desc: "二度のノーベル賞を受けた。" },
{ name: "・フランク", desc: "世界に希望を伝える象徴となった。" },
{ name: "ディー", desc: "インド独立の礎を築いた。" },
{ name: "ン", desc: "合衆国の統一を成し遂げた。" },
{ name: "チル", desc: "第二次世界大戦の勝利に寄与した。" },
{ name: "・ビスマルク", desc: "ドイツ帝国の統一を成し遂げた。" },
{ name: "王", desc: "聖剣エクスカリバーの伝説を残した。" },
{ name: "大王", desc: "ヘレニズム文化の融合をもたらした。" },
{ name: "スト", desc: "世界最大の宗教の祖となった。" },
{ name: "シュ", desc: "人類最古の叙事詩にその名を刻んだ。" },
{ name: "テス", desc: "毒杯を仰いでその生を閉じた。" },
{ name: "リン", desc: "超大国としての地位を確立した。" },
{ name: "ルク", desc: "フランスを救った聖女として火刑に処された。" },
{ name: "ゲール", desc: "近代看護教育の母と呼ばれた。" },
{ name: "・ハーン", desc: "ユーラシア大陸にまたがる大帝国を築いた。" },
{ name: "・テスラ", desc: "現代の電気文明の基礎を築いた。" },
{ name: "リン", desc: "その短い生涯を戦いの中で終えた。" },
{ name: "レース", desc: "ギリシア神話最強の英雄となった。" },
{ name: "・レー", desc: "「青髭」のモデルとしてその悪名を残した。" },
{ name: "エル", desc: "イスラエル十二部族の祖となった。" },
{ name: "・カエサル", desc: "終身独裁官となり帝政の礎を築いた。" },
{ name: "トン", desc: "近代物理学の父となった。" },
{ name: "帝", desc: "万里の長城や兵馬俑を残した。" },
{ name: "ドロス", desc: "最初期の世界地図を作成した。" },
{ name: "ラス", desc: "アテーナイから追放された。" },
{ name: "ラビ", desc: "巨大な石碑に法典を刻ませた。" },
{ name: "・ゴール", desc: "戦後フランスの復興を主導した。" },
{ name: "2世", desc: "アブ・シンベル神殿などの巨大建築を残した。" },
{ name: "ラ", desc: "「神の鞭(スカージ・オブ・ゴッド)」として恐れられた。" },
{ name: "璋", desc: "強権的な独裁体制を確立した。" },
{ name: "・ケマル", desc: "トルコの近代化改革を断行した。" },
{ name: "タイン", desc: "現代物理学の概念を一変させた。" },
{ name: "バル", desc: "グラン・コロンビアを創設した。" },
{ name: "大帝", desc: "ロシアを列強の一角へと押し上げた。" },
{ name: "リアム", desc: "ノルマン朝を開き初代国王となった。" },
{ name: "バル", desc: "カンナエの戦いで歴史的勝利を収めた。" },
{ name: "・ティトー", desc: "非同盟運動を牽引した。" },
{ name: "2世", desc: "「太陽の沈まぬ国」の王となった。" },
{ name: "3世", desc: "教皇権の絶頂期を現出した。" },
{ name: "遷", desc: "中国正史の基礎となる『史記』を完成させた。" },
{ name: "トス", desc: "「歴史の父」と呼ばれるようになった。" },
{ name: "パル", desc: "メソポタミア文化を後世に伝えた。" },
{ name: "帝", desc: "巨大な浴場を残したが暗殺された。" },
{ name: "3世", desc: "アレクサンドロスに帝国を奪われた。" },
{ name: "・スピターマ", desc: "ゾロアスター教の開祖となった。" },
{ name: "ルク", desc: "知識の爆発的な普及をもたらした。" },
{ name: "テ", desc: "『ファウスト』でドイツ文学の頂点に立った。" },
{ name: "ー", desc: "ドイツ古典主義を大成させた。" },
{ name: "アヌス帝", desc: "ローマ帝国の寿命を延ばした。" },
{ name: "・ケネディ", desc: "ダラスで凶弾に倒れた。" },
{ name: "ス", desc: "『イーリアス』と『オデュッセイア』を残した。" },
{ name: "ル", desc: "「跛者(ラング)」として歴史に名を残した。" },
{ name: "ーニン", desc: "ソビエト連邦の初代指導者となった。" },
{ name: "トーン", desc: "西洋哲学の大きな源流となった。" },
{ name: "ールタ", desc: "悟りを開いて仏教の開祖となった。" },
{ name: "ブス", desc: "新大陸発見のきっかけを作った。" },
{ name: "クス", desc: "社会主義思想の体系を築いた。" },
{ name: "トラー", desc: "自ら築き上げた第三帝国を破滅へと導いた。" },
{ name: "ド", desc: "イスラームの預言者(ナビー)となった。" },
];
// -------------------------
// ② 履歴配列
// 生成した偉人の情報を格納するための配列を用意。
// -------------------------
let history = [];
// -------------------------
// ③ ランダム取得
// -------------------------
function randomPick(array) {
return array[Math.floor(Math.random() * array.length)];
}
// -------------------------
// ④ 偉人を生成
// 生成された偉人heroは現在時刻(1970,1,1,0,0,0(UTC)から、生成された時刻までの経過時間)、名前、説明文、現在時刻(生成された時刻そのもの)で構成される。
// -------------------------
function generateHero() {
let R = randomPick(rightArms);
let T = randomPick(torsos);
let L = randomPick(leftArms);
let name = R.name + T.name + L.name; // ランダムに取得した名前たちを統合。
let desc = R.desc + T.desc + L.desc; // 同様に、対応する説明文を統合する。
let hero = {
id: Date.now(),
name,
desc,
createdAt: new Date().toLocaleString()
};
addToHistory(hero);
displayResult(hero);
}
// -------------------------
// ⑤ 履歴に追加(最大10件)
// 生成された偉人をhistoryに格納。その際、10件を超えていたらどれかを削除するように要求。
// 何も入力しなかった場合や、無効な入力をした場合、一番古い履歴(第1要素)を自動的に削除する。
// -------------------------
function addToHistory(hero) {
if (history.length >= 10) {
// 削除対象を選ばせる
let msg = "履歴が10件を超えました。削除するものを半角数字で入力してください。\n未入力、無効な入力がされた場合、最も古い履歴を削除します。\n\n";
history.forEach((h, i) => {
msg += `${i}: ${h.name}(${h.createdAt})\n`;
});
let index = parseInt(prompt(msg)); // promptで入力された数字をそのままindexに入れる
if (!isNaN(index) && index >= 0 && index < history.length) { // indexの中身、つまり入力されたものが、数字かつ0以上10未満の時にのみ、指定されたものをspliceで消す
history.splice(index, 1);
} else {
alert("最も古い履歴を削除します。");
history.shift();
}
}
history.push(hero);
updateHistoryDisplay();
}
// -------------------------
// ⑥ 結果表示
// -------------------------
function displayResult(hero) {
let resultName = document.getElementById("resultName");
resultName.textContent = `【名前】 ${hero.name}`;
let resultDesc = document.getElementById("resultDesc");
resultDesc.textContent = hero.desc;
}
// -------------------------
// ⑦ 履歴の表示(表示の更新)
// history配列が変更されるたびにHTMLの中身を置き換える
// -------------------------
function updateHistoryDisplay() {
let area = document.getElementById("historyArea");
area.innerHTML = "";
history.forEach((h, index) => {
let div = document.createElement("div");
div.textContent = `${index}. ${h.name}(${h.createdAt})`;
let del = document.createElement("span");
del.textContent = "削除";
del.className = "deleteBtn";
del.addEventListener("click", () => {
history.splice(index, 1);
updateHistoryDisplay();
});
div.appendChild(del);
area.appendChild(div);
});
}
// -------------------------
// ⑧ ボタン動作
// -------------------------
let generateBtn = document.getElementById("generateBtn");
generateBtn.addEventListener("click", generateHero);
これにて完成です!
実行結果
終わりに
実際のソクラテスラとの差別化として、対戦の要素(知力、武力などのパラメーター)を廃して、純粋に一人でキメラ偉人を見て楽しめるようにしています。
個人的に、ロジックを組むことよりも偉人のデータを186個用意することの方が大変でしたorz
次は履歴が10件を超えた場合に古いものを自動的に削除するような仕様にしてみたいです。
参考サイト
JavaScript で配列からランダムに抽出するコードをいま一度振り返るメモ - 1ft-seabass.jp.MEMO
https://www.1ft-seabass.jp/memo/2022/08/23/javascript-array-random-look-back/
Date - JavaScript | MDN
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Date
isNaN() - JavaScript | MDN
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/isNaN
forEachについて完全に理解した() #JavaScript - Qiita
https://qiita.com/TakehiroKATO/items/b8144523e72e3efe283d
【備忘録】Javascriptのshift, slice, spliceの違い #JavaScript - Qiita
https://qiita.com/ishikawa-takumi/items/e49744ee7f4d33771575

