本記事の構成および論理分析にはAI(人工知能)を使用しています。情報の正確性は、システム管理者(UNIXユーザー)による手動検証済みです。
C言語で作るターミナルタイピングRPG「クムドールの試練」開発記録 | UNIX Cafe

C言語製のターミナルタイピングRPG「クムドールの試練」を作りました。
GitHubでは kumdor-trial という名前で公開しています。最初の公開版は v0.1.0 です。
- Repository: https://github.com/k1117n-cmyk/kumdor-trial
- Version:
v0.1.0 - License: MIT License
この記事では、完成品の紹介というより、小さなCプログラムを少しずつターミナルゲームとして育てていった流れをまとめます。
クムドールの試練とは?
1990年代に流行ったPCゲームに、『クムドールの剣』というRPG型のタイピングゲームがありました。PC-98向け作品として知られていて、フィールド移動にも F や J、スペースキーを使い、敵と遭遇すると表示された文字列を正確に入力して戦うというゲームでした。
私自身も、当時全盛期だったパソコン通信のNIFTY-Serveでその存在を知りました。その後、Macintosh版の『クムドールの剣』が発売されていたことから、Color Classic や Quadra 650 で遊び、このゲームでタッチタイピングを覚えました。当時は「ブラインドタッチ」と呼ぶことが多かった記憶があります。
このゲームが面白いのは、最初からすべてのキーを使わせないところです。ホームポジション周辺のキーから始まり、ゲームが進むにつれて少しずつ扱うキーが増えていく。プレイヤーがゲーム内で進むことと、現実のタイピングが上達していくことが重なっていました。
今回作った「クムドールの試練」は、その記憶にある段階学習の気持ちよさを、C言語製の小さなターミナルゲームとして自分なりに再構成してみたものです。
本記事では、そんな思い出から生まれた自作タイピングゲーム「クムドールの試練」の概要と、こだわりの仕様についてご紹介します。
実際に作り出してみると、細かい部分はほとんど忘れていました。そのため、巨大な金魚さんが公開されている『Macintosh版クムドールの剣』の情報を参考にさせていただきました。
http://rocky75.web.fc2.com/kumu/
このゲームの内容について
「クムドールの試練」は、ターミナル上で動くタイピングRPGゲームです。
画面に表示された課題をそのまま正確に入力すると、敵にダメージを与えます。タイプミスをすると敵が反撃します。10個のステージで敵を順番に倒せば完全勝利、途中でプレイヤーのHPが0になると敗北です。
プレイ画面はこのような雰囲気です。
=========================================
【第1/10ステージ】
練習内容: ホームポジション: f / j / スペース
指使い : 左右の人差し指をfとjに置き、右親指でスペースを押す。
敵 : クムドールの影
=========================================
*************** BATTLE START ***************
クムドールの影が立ちはだかった!
********************************************
あなた Lv:1 EXP:0/10 HP: 10/10 | クムドールの影 HP: 3/3
敵の構え: [ f j ]
課題を入力してEnter(:save/:quit/:savequit): f j
➔ 見事なタイピング! 剣が炸裂した! (敵の残りHP: 2)ゲーム中に中断したい場合は、入力欄でコマンドを使えます。
| コマンド | 内容 |
|---|---|
:save | 現在ステージの先頭から再開できる状態で保存 |
:quit | 保存せずに終了 |
:savequit | 保存して終了 |
最初から全部のキーを使わせない
今回の一番大事な設計は、最初からすべてのキーを使わせないことでした。
昔のタイピングゲーム「クムドールの剣」について調べていると、序盤は限られたキーだけを使い、ゲームが進むにつれて少しずつ使えるキーが増えていく、という学習設計が魅力だったという話が出てきました。
この考え方を、今回のターミナル版でも取り入れています。
| ステージ | 練習内容 |
|---|---|
| 1 | ホームポジション: f / j / スペース |
| 2 | 左手ホーム段: a s d f |
| 3 | 右手ホーム段: j k l ; |
| 4 | 人差し指の上段: r t y u |
| 5 | 上段の広がり: q w e i o p |
| 6 | 中央から下段: g h v b n m |
| 7 | 下段と記号: z x c , . / ? |
| 8 | 総合練習: 大文字・小文字・記号 |
| 9 | 数字キー: 1 から 0 |
| 10 | 最終練習: Shift記号と混合課題 |
ステージごとに練習内容を変えることで、プレイヤーのゲーム内進行と、現実のタイピング練習が同期するようにしました。
単キーだけだと単調になるので、各ステージには左右交互、短いまとまり、スペース入りの課題も混ぜています。
たとえばステージ1では、f、j の単キーだけでなく、次のような課題も出ます。
static const char *const stage1_words[] = {
"f",
"j",
"fj",
"jf",
"ff",
"jj",
"fjjf",
"jffj",
"f j",
"j f",
"fj fj",
"jf jf"
};最初は小さなCプログラムからのスタート
最初は kumdor_01.c だけの小さなCプログラムでした。
プレイヤー、敵、ステージは struct で管理しています。
typedef struct {
const char *name;
int hp;
int max_hp;
unsigned char status;
int correct_count;
int miss_count;
int input_error_count;
int reached_stage;
int level;
int exp;
} Player;
typedef struct {
const char *name;
int hp;
int max_hp;
int attack;
} Enemy;最初は単純な1戦のバトルでしたが、Player や Enemy を構造体にしておいたことで、あとから経験値、レベル、スコア、状態異常を足しやすくなりました。
入力は fgets で1行読み取り、strcmp で課題と完全一致するか判定しています。
static int is_correct_input(const char input[], const char target[]) {
return strcmp(input, target) == 0;
}スペース入りの課題を扱いたかったので、scanf ではなく fgets を使っています。
RPGらしさを足していく
タイピング練習だけだと味気ないので、RPGらしい要素を少しずつ足しました。
- 複数の敵
- 10ステージ制
- プレイヤーHPと敵HP
- タイプミス時の反撃
- ステージ突破時の回復報酬
- 経験値とレベルアップ
- スコア、命中率、評価
- 毒と暗闇の状態異常
経験値はステージ突破時に入ります。一定値に達するとレベルアップし、最大HPが増えてHPが全回復します。
EXPを4獲得した!
【レベルアップ】Lv:2 最大HPが12になった! HP全回復!
現在のEXP: 2/10こういうメッセージを表示すると、ただ課題を入力しているだけではなく、プレイヤー自身が少しずつ強くなっている感じが出ます。
セーブと中断
ターミナルゲームでも、途中でやめたくなることはあります。
そこで、ステージ突破時の自動セーブと、入力欄からの中断コマンドを入れました。
セーブデータは kumdor_save.txt に保存します。
KUMDOR_SAVE_V1 1 10 10 0 0 0 0 1 1 0保存しているのは、次に始めるステージ番号、HP、最大HP、状態異常、スコア、到達ステージ、レベル、経験値です。
敵HPの途中状態は保存していません。中断セーブでは、現在ステージの先頭から再開します。
これは仕様として割り切りました。実装がシンプルになりますし、タイピング練習ゲームとしても、ステージの最初から再開するほうが分かりやすいからです。
ターミナル表示を読みやすくする
ターミナルゲームでは、表示の区切りがかなり大事です。
最初はステージ突破と次ステージ開始が続いて表示され、どこで一区切りなのか分かりにくい状態でした。
そこで、次のように表示を分けました。
- ステージ突破
- 次ステージへの移動
- ステージ開始
- 戦闘開始
- 通常ターン
- スコア表示
ステージ開始は枠で囲み、戦闘開始は BATTLE START として別の枠にしています。
=========================================
【第5/10ステージ】
練習内容: 上段の広がり: q / w / e / i / o / p
指使い : 小指・薬指・中指を上段へ伸ばし、戻る位置を意識する。
敵 : 上段の魔術師
=========================================
*************** BATTLE START ***************
上段の魔術師が立ちはだかった!
********************************************ターミナルでも、少し区切りを入れるだけでゲームの流れが見えやすくなります。
コードを分割する
最初は1ファイルで十分でしたが、ステージ、セーブ、経験値、表示、スコアが増えてくると、kumdor_01.c だけでは見通しが悪くなります。
そこで、最終的にこの構成に分けました。
kumdor_01.c エントリポイント
include/game.h 共有定義
src/game.c ゲーム進行、戦闘、セーブ、スコア表示
src/stages.c ステージ、敵、出題課題の定義kumdor_01.c は互換用の入口として残し、実際のゲーム本体は run_game() に移しています。
#include "game.h"
int main(void) {
return run_game();
}ステージ定義は src/stages.c に切り出しました。
const Stage *get_stages(int *stage_count) {
*stage_count = (int)(sizeof(stages) / sizeof(stages[0]));
return stages;
}細かく分けすぎると逆に読みにくくなるので、今回は battle.c や save.c までは作っていません。まずは、ゲーム進行とステージデータを分けるくらいがちょうどよいと判断しました。
GitHubで公開するための準備
GitHubで公開するにあたり、次のものを整えました。
- README
- MIT License
.gitignorev0.1.0表記v0.1.0Gitタグ- ビルド手順
- 実行方法
- プレイ例
作業用メモだった HANDOFF.md と kumdor_01_explanation.md は公開対象から外しました。ローカルには残しつつ、.gitignore に追加しています。
.gitignore では、実行ファイルやセーブデータも管理対象から外しています。
kumdor_01
kumdor_save.txt
HANDOFF.md
kumdor_01_explanation.md
*.o
*.out
.DS_Storeビルドは make で行います。
make
./kumdor_01不要になった実行ファイルは make clean で削除できます。
make clean作ってみて分かったこと
C言語でも、小さなターミナルゲームなら十分楽しく作れます。
今回やってみて、特に効果があったのは次の点でした。
structにまとめると機能追加が楽になる- 入力は早めに
fgetsにしておくとスペース入り課題に対応しやすい - ターミナル表示は区切り線と見出しが大事
- セーブ機能は小さくても満足度が高い
- 学習ゲームは「最初から全部やらせない」ほうが面白い
特に、段階的に使えるキーが増えていく設計は、タイピング練習とゲーム進行の相性がよいと感じました。
今後やりたいこと
v0.1.0 としては、最初から最後まで遊べる状態になりました。
今後改良していくなら次のような内容です。
- ステージごとの課題バランス調整
- 戦闘やセーブ処理のさらなるファイル分割
- ステージ演出の追加
- セーブデータ形式の拡張
- curses などを使った画面制御
ただ、今のシンプルなターミナルログ形式も気に入っています。
おわりに
「クムドールの試練」は、C言語の練習として始めた小さなターミナルゲームです。
最初は1ファイルの小さなプログラムでしたが、敵、ステージ、セーブ、経験値、スコア、段階的なタイピング練習を足していくことで、ひとまず1本の小さなゲームになりました。
UNIX的な道具立てだけで、小さく作って、動かしながら育てる。
そういう開発の楽しさを改めて感じたプロジェクトでした。
Repository:









