Last active
January 13, 2019 06:19
-
-
Save shiracamus/464a0cee29a7ddb13ef8aeb2d81c0765 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// 昔懐かし、CBM-3032のようなキャラクタゲームを | |
// ncurseswで実現。 | |
// Raging robots. | |
// | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdbool.h> | |
#include <time.h> | |
#include <locale.h> | |
#include <ncursesw/ncurses.h> | |
#define NUM_ROCK (120) | |
#define NUM_ROBOT (12) | |
#define rnd(x) (rand() % (x)) | |
#define center(x) ((x) / 2) | |
typedef const char * const String; | |
typedef int X, Y, Width, Height; | |
// class Symbol extends String { | |
typedef String Symbol; | |
static Symbol ROCK = "#"; | |
static Symbol ROBOT = "@"; | |
static Symbol PLAYER = "O"; | |
static Symbol CRASH = "X"; | |
static Symbol SPACE = " "; | |
// } | |
// class Screen { | |
struct { | |
Width width; | |
Height height; | |
} screen; | |
void screen_open() { | |
initscr(); // 画面初期化 | |
noecho(); // キーボードエコーなし | |
curs_set(0); // カーソル非表示 | |
getmaxyx(stdscr, screen.height, screen.width); // 画面サイズ取得 | |
screen.width /= 2; // 全角文字表示で2倍幅になるので画面幅を半分に見立てる | |
} | |
void screen_close() { | |
endwin(); | |
} | |
void screen_text(X x, Y y, String message) { | |
move(y, x * 2); | |
addstr(message); | |
} | |
bool screen_contains(X x, Y y) { | |
return 0 <= x && x < screen.width | |
&& 0 <= y && y < screen.height; | |
} | |
// } | |
// class Character { | |
typedef struct { | |
bool is_vaild; | |
X x; | |
Y y; | |
} character_t, *Character; | |
void Character_put(Character this, X x, Y y, Symbol symbol) { | |
this->is_vaild = true; | |
this->x = x; | |
this->y = y; | |
screen_text(this->x, this->y, symbol); | |
} | |
void Character_put_random(Character this, Symbol symbol) { | |
Character_put(this, rnd(screen.width), rnd(screen.height), symbol); | |
} | |
void Character_put_center(Character this, Symbol symbol) { | |
Character_put(this, center(screen.width), center(screen.height), symbol); | |
} | |
bool Character_hit(Character this, const Character target) { | |
return this != target | |
&& target->is_vaild | |
&& this->x == target->x | |
&& this->y == target->y; | |
} | |
// } | |
// class Rock extends Character { | |
typedef character_t rock_t; | |
typedef Character Rock; | |
static rock_t rocks[NUM_ROCK]; | |
static const Rock rocks_end = &rocks[NUM_ROCK]; | |
void rocks_init() { | |
for (Rock rock = rocks; rock != rocks_end; rock++) | |
Character_put_random(rock, ROCK); | |
} | |
bool rocks_hit(const Character target) { | |
for (Rock rock = rocks; rock != rocks_end; rock++) | |
if (Character_hit(rock, target)) return true; | |
return false; | |
} | |
// } | |
// class Robot extends Character { | |
typedef character_t robot_t; | |
typedef Character Robot; | |
static robot_t robots[NUM_ROBOT]; | |
static const Robot robots_end = &robots[NUM_ROBOT]; | |
void robots_init() { | |
for (Robot robot = robots; robot != robots_end; robot++) | |
Character_put_random(robot, ROBOT); | |
} | |
bool robots_hit(const Character target) { | |
for (Robot robot = robots; robot != robots_end; robot++) | |
if (Character_hit(robot, target)) return true; | |
return false; | |
} | |
void robots_move(const Character target) { | |
for (Robot robot = robots; robot != robots_end; robot++) { | |
if (!robot->is_vaild) continue; | |
// 現表示消去 | |
screen_text(robot->x, robot->y, SPACE); | |
// ターゲット(プレイヤー)を追いかけるように移動 | |
if (robot->x < target->x) robot->x += 1; | |
if (robot->x > target->x) robot->x -= 1; | |
if (robot->y < target->y) robot->y += 1; | |
if (robot->y > target->y) robot->y -= 1; | |
// 他のロボットや岩に衝突してなければ表示 | |
if (robots_hit(robot) || rocks_hit(robot)) | |
robot->is_vaild = false; | |
if (robot->is_vaild) | |
screen_text(robot->x, robot->y, ROBOT); | |
} | |
} | |
bool robots_are_wiped() { | |
for (Robot robot = robots; robot != robots_end; robot++) | |
if (robot->is_vaild) return false; | |
return true; | |
} | |
// } | |
// class Player extends Character { | |
typedef character_t player_t; | |
typedef Character Player; | |
static player_t player; | |
void player_init() { | |
Character_put_center(&player, PLAYER); | |
} | |
void player_move() { | |
player_t move = player; | |
do { | |
switch(getch()) { | |
case '7': move.x -= 1, move.y -= 1; break; | |
case '8': move.x += 0, move.y -= 1; break; | |
case '9': move.x += 1, move.y -= 1; break; | |
case '4': case 'u': move.x -= 1, move.y += 0; break; | |
case '5': case 'i': move.x += 0, move.y += 0; break; | |
case '6': case 'o': move.x += 1, move.y += 0; break; | |
case '1': case 'j': move.x -= 1, move.y += 1; break; | |
case '2': case 'k': move.x += 0, move.y += 1; break; | |
case '3': case 'l': move.x += 1, move.y += 1; break; | |
default: continue; | |
} | |
} while (false); | |
if (screen_contains(move.x, move.y)) { | |
screen_text(player.x, player.y, SPACE); | |
player = move; | |
screen_text(player.x, player.y, PLAYER); | |
} | |
} | |
bool player_is_crashed() { | |
return robots_hit(&player) || rocks_hit(&player); | |
} | |
// } | |
// class Game { | |
void game_show_instruction() { | |
clear(); | |
printw("Raging robots Ver 0.8\n"); // printwで、stdscrに出力します。 | |
printw("Mission : survive from raging robots.\n"); | |
printw("%s -- 岩 , 自分やロボットが当たると死ぬ\n", ROCK); | |
printw("%s -- ロボット , 段階的に追い詰めてくるロボット\n", ROBOT); | |
printw("%s -- プレイヤー , キーで操作し、岩にロボットを誘って\n", PLAYER); | |
printw(" 消滅させよう。\n"); | |
printw("\n"); | |
printw("キーコントロール テンキー \n"); | |
printw(" 7 8 9 7 8 9 \n"); | |
printw(" ↖ ↑ ↗ ↖ ↑ ↗ \n"); | |
printw(" u← i→ o 4← 5→ 6 \n"); | |
printw(" ↙ ↓ ↘ ↙ ↓ ↘ \n"); | |
printw(" j k l 1 2 3 \n"); | |
printw("\n"); | |
printw(" 'i' と '5' は、自分が動かず、ロボットが動くだけです。\n"); | |
printw(" Ctrl-Cで、止まります。\n"); | |
printw(" Good Luck\n"); | |
printw("Hit a key to start game\n"); | |
getch(); | |
} | |
void game_init() { | |
clear(); | |
rocks_init(); | |
robots_init(); | |
player_init(); | |
// 処理簡略化のため、特に岩とロボットと、プレイヤーの | |
// 初期位置のバッティングは考えないこととします。 | |
} | |
typedef enum { WIN, LOSE } GameResult; | |
GameResult game_fight() { | |
while (true) { | |
player_move(); | |
if (player_is_crashed()) return LOSE; | |
robots_move(&player); | |
if (player_is_crashed()) return LOSE; | |
if (robots_are_wiped()) return WIN; | |
} | |
} | |
void game_play() { | |
game_show_instruction(); | |
game_init(); | |
if (game_fight() == WIN) { | |
screen_text(0, 0, "You Win!!\n"); | |
} else{ | |
screen_text(player.x, player.y, CRASH); | |
screen_text(0, 0, "You Lose.\n"); | |
} | |
} | |
// } | |
bool try_again() { | |
printw("Try Again? [y/n] "); | |
while (true) { | |
int c = getch(); | |
if (c == 'y') return true; | |
if (c == 'n') return false; | |
} | |
} | |
int main() { | |
setlocale(LC_ALL, ""); | |
srand((unsigned)time(NULL)); | |
screen_open(); | |
do { | |
game_play(); | |
} while (try_again()); | |
screen_close(); | |
return EXIT_SUCCESS; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment