No.26ベストアンサー
- 回答日時:
後編:
// 味方の攻撃番号を取得
static int turnInput( int check[] )
{
int turn;
for ( ; ; ){
printf( "誰が攻撃しますか(1.人物A 2.人物B 3.人物C 4.人物D)--->" );
scanf( "%d", &turn );
// 1~4以外ならプログラムの終了
if ( (turn < 1) || (turn > MAX_MEMBER) ){
exit( 1 ); // 強制終了
}
// 0~3に変換
turn--;
// 戦闘不能のチェック
if ( Member[turn].hp <= 0 ){
printf( "戦闘不能です。\n" );
continue;
}
// 攻撃済みのチェック
if ( check[turn] ){
printf( "/*-----%sのターンは終了しています-----*/\n", Member[turn].name );
continue;
}
// 攻撃番号を返す
check[ turn ] = 1; // 攻撃済みフラグのセット
printf( "\n" );
return turn;
}
}
// 味方の攻撃ルーチン
extern int attackMember( int members )
{
int check[ MAX_MEMBER ] = { 0 };
int loop;
dispStatus();
for ( loop = 1 ; loop <= members ; loop++ ){
printf( "味方の攻撃ターン(%d/%d)\n", loop, members );
switch ( turnInput(check) ){
case 0:
chaAB( &Member[0], &Enemy[0] );
printf( "/*-----------------人物Aのターン終了------------------*/\n\n" );
break;
case 1:
chaAB( &Member[1], &Enemy[0] );
printf( "/*-----------------人物Bのターン終了------------------*/\n\n" );
break;
case 2:
chaCD( &Member[2], &Enemy[0] );
printf( "/*-----------------人物Cのターン終了------------------*/\n\n" );
break;
case 3:
chaCD( &Member[3], &Enemy[0] );
printf( "/*-----------------人物Dのターン終了------------------*/\n\n" );
break;
default:
loop = members; // 抜ける設定
break;
}
}
return checkEnemy(); // 1:生きている 0:死す
}
// 敵の攻撃ルーチン
extern int attackEnemy( void )
{
int i, n1, s1, s2, s3, s4;
dispStatus();
printf( "敵の攻撃ターン\n" );
for ( i = 0 ; i < MAX_MEMBER ; i++ ){
status *p = &Member[i];
srand( (unsigned)time(NULL) + rand() );
n1 = RAND( 1000, 1000 ); // ダメージ量(1000~1999)
s1 = RAND( 100, 1 ); // [毒]状態(1-100)
s2 = RAND( 100, 1 ); // [沈黙]状態(1-100)
s3 = RAND( 100, 1 ); // [暗闇]状態(1-100)
s4 = RAND( 100, 1 ); // [混乱]状態(1-100)
if ( (p->hp -= n1) < 0 ){
p->hp = 0;
}
p->st[ 0 ] |= s1 % 2; // [毒]状態の設定
p->st[ 1 ] |= s2 % 2; // [沈黙]状態の設定
p->st[ 2 ] |= s3 % 2; // [暗闇]状態の設定
p->st[ 3 ] |= s4 % 2; // [混乱]状態の設定
printf( "%sの攻撃--->%sに%dのダメージを与えた。\n", Enemy[0].name, p->name, n1 );
}
printf( "\n" );
return checkMember(); // 1:生きている 0:全滅
}
// 人物A/Bの攻撃(勇者,戦士)
extern void chaAB( status *member, status *enemy )
{
int n1;
// 初期化
srand( (unsigned)time(NULL) + rand() );
n1 = RAND( 1000, 1000 ); // ダメージ量(1000~1999)
if ( (enemy->hp -= n1) < 0 ){
enemy->hp = 0;
}
printf( "%sの攻撃--->%sに%dのダメージを与えた。\n\n", member->name, enemy->name, n1 );
}
// 人物C/Dの攻撃(魔法使い)
extern void chaCD( status *member, status *enemy )
{
int n1, select;
// 初期化
srand( (unsigned)time(NULL) + rand() );
do {
printf( "1.魔法1 2.魔法2--->" );
scanf( "%d", &select );
switch ( select ){
case 1:
if ( member->mp < 20 ){
printf( "MPが足りません。\n" );
select = -1;
break;
}
n1 = RAND( 1000, 1000 ); // ダメージ量(1000~1999)
if ( (enemy->hp -= n1) < 0 ){
enemy->hp = 0;
}
member->mp -= 20;
printf( "%sの魔法攻撃--->%sに%dのダメージを与えた。\n\n", member->name, enemy->name, n1 );
break;
case 2:
n1 = RAND( 50, 50 ); // MP吸収量(50~99)
if ( enemy->mp < n1 ){
n1 = enemy->mp;
}
member->mp += n1;
enemy->mp -= n1;
printf( "%sの魔法吸収--->%sから%dのMPを吸収した。\n\n", member->name, enemy->name, n1 );
break;
default:
select = -1;
break;
}
} while ( select == -1 );
}
以上。
わざわざ考えていただいてありがとうございますm(_ _)m
これを参考に、改良できる部分はしていきたいと思います。
いきなり直ったので、出来る限り原因追求していきます。
No.25
- 回答日時:
★似たものを作ってみました。
>実は、No16さん以降、ソースを追加したり修正しているうちに、文字化けやバグが無くなりました(汗)
↑
これ本当に直った?不思議ですね。たまたま文字化けしなくなった気が…。
・私も『戦闘シミュレーション』を作ってみました。→sikimori さんのソースを元に。
main() 関数を複数のサブ関数に分割してみました。今後の参考にしてみて下さい。
※1つのソースで作成しました。→前編、後編で貼り付けておく。
前編:
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
// 構造体
typedef struct Status {
char name[ 20 ]; // キャラクタ名
int hp; // HP量
int mp; // MP量
int st[ 4 ]; // 特殊状態(毒,沈黙,暗闇,混乱)
} status;
// 記号定数
#define MAX_ENEMY (sizeof(Enemy) / sizeof(status))
#define MAX_MEMBER (sizeof(Member) / sizeof(status))
// マクロ関数
#define RAND(max,ofs) ((rand() % (max)) + (ofs))
// 関数のプロトタイプ宣言
extern void dispStatus( void );
extern int checkEnemy( void );
extern int checkMember( void );
extern int checkGameOver( int *members );
extern int attackMember( int members );
extern int attackEnemy( void );
extern void chaAB( status *member, status *enemy );
extern void chaCD( status *member, status *enemy );
// 敵キャラのデータ
extern status Enemy[ 1 ] = {
"human", 25000, 9999, 0, 0, 0, 0,
};
// 味方キャラのデータ
extern status Member[ 4 ] = {
"人物A", 6000, 100, 0, 0, 0, 0,
"人物B", 4000, 300, 0, 0, 0, 0,
"人物C", 4000, 250, 0, 0, 0, 0,
"人物D", 6500, 200, 0, 0, 0, 0,
};
// 戦闘シミュレーション
int main( void )
{
// 生き残り人数
int members;
// 初期化
srand( (unsigned)time(NULL) );
printf( "/*-------------戦闘開始!!-------------*/\n\n" );
// 最初の1回だけ。味方or敵(0:先手,1:後手)
if ( RAND(2,0) == 0 ){ // 味方が先手
while ( !checkGameOver(&members) ){
if ( attackMember(members) ){ // 味方の攻撃
if ( attackEnemy() ){ // 敵の攻撃
Sleep( 2000 );
system( "cls" );
}
}
}
}
else{ // 敵が先手
while ( !checkGameOver(&members) ){
if ( attackEnemy() ){ // 敵の攻撃
if ( attackMember(members) ){ // 味方の攻撃
Sleep( 2000 );
system( "cls" );
}
}
}
}
return 0;
}
// 敵,味方ステータスの表示
static void dispStatusOne( status *data )
{
static const char *msg[] = {
"毒",
"沈黙",
"暗闇",
"混乱",
}; int i, flag = 0;
// 名前,HP,MPの表示
printf( "%-10s: HP = %6d MP = %4d 状態 :", data->name, data->hp, data->mp );
// 状態の表示
if ( data->hp == 0 ){
printf( "死亡\n" );
return;
}
for ( i = 0 ; i < 4 ; i++ ){
if ( data->st[i] ){
if ( flag ){
printf( " " );
}
printf( msg[i] ); // 毒,沈黙,暗闇,混乱の表示
flag = 1;
}
}
if ( flag == 0 ){
printf( "なし" );
}
printf( "\n" );
}
// 敵,味方ステータスの表示
extern void dispStatus( void )
{
int i;
printf( "/*-----------味方のステータス---------*/\n" );
for ( i = 0 ; i < MAX_MEMBER ; i++ ){
dispStatusOne( Member + i );
}
printf( "/*-------------敵のステータス---------*/\n" );
for ( i = 0 ; i < MAX_ENEMY ; i++ ){
dispStatusOne( Enemy + i );
}
printf( "/*------------------------------------*/\n" );
printf( "\n" );
}
// 敵のHPをチェック
extern int checkEnemy( void )
{
return (Enemy[0].hp > 0) ? 1 : 0; // 1:敵生きている 0:敵死す
}
//味方のHPをチェック
extern int checkMember( void )
{
int i;
for ( i = 0 ; i < MAX_MEMBER ; i++ ){
if ( Member[i].hp > 0 ){
return 1;
}
}
return 0; // 全滅
}
// ゲームオーバーのチェック
extern int checkGameOver( int *members )
{
int i, count = 0;
if ( checkEnemy() == 0 ){
dispStatus();
printf( "%sを倒しました!!\n", Enemy[0].name );
return 1; // 勝利!
}
if ( checkMember() == 0 ){
dispStatus();
printf( "/*----------ゲームオーバーです----------*/\n" );
return 1; // 全滅!
}
for ( i = 0 ; i < MAX_MEMBER ; i++ ){
if ( Member[i].hp > 0 ){
count++;
}
}
*members = count;
return 0;
}
No.23
- 回答日時:
ちらっと眺めただけで、プログラムを読んでないし、デバッグと直接関係ないですが。
。茶々入れてるだけかと思われるならゴメンナサイ^^;数字をそのまま使うより、
enum human_t { Ahuman, Bhuman, ... };
なんか定義して、列挙にしておいたほうがわかりやすいんじゃないですか?
配列の添え字として使う場合は、
enum human_t { Ahuman = 0, Bhuman, ..., Zhuman, humanNum = Zhuman };
などとして、0 を明示しておいたり、長さを別に定義しておいたり。。
あと、C++コンパイラが使えて、C99の拡張機能を使っていないなら、C++コンパイラにかけてみるのもプログラムチェックに役立つと思いますよ(文字サイズなどちょっとした違いが CとC++にはありますが)。
No.22
- 回答日時:
★訂正。
・check[] 配列のチェックは
if ( check[turn-1] ){
printf( "/*-----%sのターンは終了しています-----*/\n", dt[turn-1].name );
}
else switch ( turn ){
case 1: chaAB( &dt[0], &dt[4] ); printf( … ); break;
case 2: chaAB( &dt[1], &dt[4] ); printf( … ); break;
case 3: chaCD( &dt[2], &dt[4] ); printf( … ); break;
case 4: chaCD( &dt[3], &dt[4] ); printf( … ); break;
default:break;
}
でした。
for文で4回ifチェックしなくても良かった。
・以上。
No.21
- 回答日時:
★いろいろと怪しい箇所(疑問)が見つかった。
・(1)for ( j = 0 ; j < 4 ; j++ ) if( dt[j].hp == 0 ) flg2--;//死んだキャラがいたらflg2--。
この処理は
flg2=4 してから (hp == 0) なら flg2-- よりも
flg2=0 してから (hp > 0) なら flg2++ の方が分かりやすくないか。
for ( flg2 = j = 0 ; j < 4 ; j++ ) if ( dt[j].hp > 0 ) flg2++;
(2)変数 i と check[ i++ ] = turn; の部分がきっとずれるかも。
ここのロジックが変なので chaA()~chaD() 関数が正しく呼ばれていないと思う。
よって選択した人物とは違う人物関数が処理される。
そして『MPが足りない』となる。
(3)human() 関数で人物A~人物Dの4人に対して同じダメージで攻撃しています。
この動作で良いのですか?
(4)chaA()、chaB() が構造体データ以外はすべて同じなら1つの関数に出来ます。
chaC()、chaD() も構造体データ以外はすべて同じなら1つの関数に出来ます。
処理する構造体データの部分だけが違うのなら次の2つに出来ます。
void chaAB( status *member, status *enemy );
void chaCD( status *member, status *enemy );
このような引数にすれば
switch ( turn ){
case 1: chaAB( &dt[0], &dt[4] ); printf( … ); break;
case 2: chaAB( &dt[1], &dt[4] ); printf( … ); break;
case 3: chaCD( &dt[2], &dt[4] ); printf( … ); break;
case 4: chaCD( &dt[3], &dt[4] ); printf( … ); break;
default:break;
}
と出来ます。
(5)human() 関数で戦闘するたびに毒、沈黙、暗闇、混乱の状態がセット/リセットします。
普通は一度『毒』状態になったら『毒消し』しない限りはずっと『毒』状態になる気が…。
なので代入ではなくてビットORすれば良い。
(p+i)->st_ab[0] |= s1 % 2;
(p+i)->st_ab[1] |= s2 % 2;
(p+i)->st_ab[2] |= s3 % 2;
(p+i)->st_ab[3] |= s4 % 2;
とします。→『|=』を使う。
・上記の5つ以外にも気になる点がゴロゴロあります。
データは
人物A~人物Dまでを status member[4]; として定義して
humanという敵だけを status enemy[1]; として定義した方が分かりやすいですよ。
また構造体データ member、enemy をグローバル変数にして main() 関数の処理を
複数のサブ関数に分けたほうが見やすくなります。
これによりバグになりにくくなる。
・下に main() 関数と分けたほうが良さそうな関数のプロトタイプ宣言を載せておきます。
作り変えるときの参考にして下さい。
// 記号定数
#define MAX_ENEMY (sizeof(Enemy) / sizeof(status))
#define MAX_MEMBER (sizeof(Member) / sizeof(status))
// マクロ関数
#define RAND(max,ofs) ((rand() % (max)) + (ofs))
// 関数のプロトタイプ宣言
extern void dispStatus( void ); // ステータスの表示
extern int attackEnemy( void ); // 敵の攻撃(戻り値:checkEnemy()の値)
extern int attackMember( int members ); // 味方の攻撃(戻り値:checkMember()の値)
extern int checkEnemy( void ); // 敵のHP=0をチェック(1=生きている,0=倒れた)
extern int checkMember( void ); // 味方4人のHP=0をチェック(1=戦える,0=全滅)
extern int checkGameOver( int *members ); // ゲームオーバーをチェック(1=GameOver,0=戦闘続行)
extern void chaA( status *member, status *enemy ); // 人物Aの攻撃
extern void chaB( status *member, status *enemy ); // 人物Bの攻撃
extern void chaC( status *member, status *enemy ); // 人物Cの攻撃
extern void chaD( status *member, status *enemy ); // 人物Dの攻撃
// 敵キャラのデータ
extern status Enemy[ 1 ] = {
"human", 100000, 9999, 0, 0, 0, 0,
};
// 味方キャラのデータ
extern status Member[ 4 ] = {
"人物A", 6000, 100, 0, 0, 0, 0,
"人物B", 4000, 300, 0, 0, 0, 0,
"人物C", 4000, 250, 0, 0, 0, 0,
"人物D", 6500, 200, 0, 0, 0, 0,
};
// 戦闘シミュレーション
int main( void )
{
// 生き残り人数
int members;
// 初期化
srand( (unsigned)time(NULL) );
printf( "/*-------------戦闘開始!!-------------*/\n" );
// 最初の1回だけ。味方or敵(0:先手,1:後手)
if ( RAND(2,0) == 0 ){ // 味方が先手
while ( !checkGameOver(&members) ){
dispStatus(); // ステータスの表示
if ( attackMember(members) ){ // 味方の攻撃
if ( attackEnemy() ){ // 敵の攻撃
Sleep( 2000 );
system( "cls" );
}
}
}
}
else{ // 敵が先手
while ( !checkGameOver(&members) ){
dispStatus(); // ステータスの表示
if ( attackEnemy() ){ // 敵の攻撃
if ( attackMember(members) ){ // 味方の攻撃
Sleep( 2000 );
system( "cls" );
}
}
}
}
return 0;
}
最後に:
・今回の原因は check[] 配列のロジックが正しくないようです。
『check[ i++ ] = turn;』ではなくて『check[ turn - 1 ] = 1;』にした方が良いでしょう。
それからチェックは
for ( flg = j = 0 ; j < 4 ; j++ ){
if ( check[j] ){
printf( "/*-----%sのターンは終了しています-----*/\n", dt[turn-1].name );
flg = 1;
break;
}
}
とします。flg は変なところで初期化しないようにして下さい。見づらいよ。
・check[] 配列のロジックを直せばおかしな構造体データを操作しなくなります。
これにより
>(1)dtを初期化しているのに、魔法を使おうとすると、mpが足りないと出る
>(2)お互いにダメージを与えているのに敵だけhpが減らない
の2つが解決するはずです。
・以上。サブ関数に分けて作り直した方がデバッグするより早い気がします。
まず疑問点について私がいうのも変なのですが、回答します。
(1)これは先に思いついた方のやり方を書いてました。
(2)このプログラムで、状態異常をまだ付けていないソースがあったのですが、その時実行した時には何もバクや文字化けなく動きました。
(3)一応全体攻撃として考えているので^^
(4)確かにchaAとchaB、chaCとchaDは殆ど同じ処理内容ですが、まだ追加処理を入れるつもりだったので、一人一人別々に作っていたのですm(_ _)m
(5)状態異常のときの処理は、まだ手付かずなので・・・。ごめんなさい。
ビットOR、これは使ったことがなかったので参考にさせていただきます。
実は、No16さん以降、ソースを追加したり修正しているうちに、文字化けやバグが無くなりました(汗)
No.20
- 回答日時:
部分部分ばらばらに貼り付けずに、全部一緒に貼り付けちゃえばいいのに。
。そんなに長くなさそうだし。そのほうが、見てくれる方々も見やすいし、まぎれがなくていいでしょ^^
No.19
- 回答日時:
★アドバイス
>基本的に文字化けしたり、値か変わったりするのは、ダメージを受けたあとになっているようです。
>本題ですが、nameのNULL文字以降調べようとしたのですが、調べ方が分からなくて;;
↑
次のダンプ関数を作成して下さい。
そしてダンプ表示したい場所で呼び出して下さい。
// 構造体の16進ダンプ表示
void printDump( status data[], size_t max, const char msg[] )
{
FILE *fp;
if ( (fp = fopen("dump16.txt","a")) != NULL ){
fprintf( fp, "---- ↓[%s] ----\n", msg );
for ( size_t n = 0 ; n < max ; n++ ){
unsigned char *p = (unsigned char *)&data[n];
unsigned char *end = (unsigned char *)&data[n + 1];
fprintf( fp, "\n★status dt[%d]:%s\n", n, data[n].name );
for ( int y = 0x00 ; p < end ; y += 0x10 ){
fprintf( fp, "%08X: ", y );
for ( int x = 0 ; (p < end) && (x < 0x10) ; x++ ){
if ( x == 0x07 ){
fprintf( fp, "%02X-", *p++ );
}
else{
fprintf( fp, "%02X ", *p++ );
}
}
fprintf( fp, "\n" );
}
}
fprintf( fp, "---- ↑[%s] ----\n\n", msg );
fclose( fp );
}
}
// 使い方
printDump( dt, 5, "test(1)" );
printDump( dt, 5, "test(2)" ); ←ダンプ時のメッセージを指定可能
その他:
・上記の printDump() 関数をソースに組み込んで下さい。
インデント部が全角空白文字なのでエディタでタブ文字に一括変換して下さい。
それから dump16.txt というファイルに追加書き込みされていきます。
メモ帳などで開いて確認して下さい。
・あと else 部分のソースもすべて貼り付けてくれますか。
その他 human()、chaA()、chaB()、chaC()、chaD() 関数のソースもすべて貼り付けて欲しいです。
ANo.19 の回答への補足、ANo.19 の回答へのお礼、
ANo.18 の回答への補足、ANo.18 の回答へのお礼
の順に else 部分、human()、chaA()、chaB()、chaC()、chaD() 関数を貼り付けて。
・以上。
No.17
- 回答日時:
あっ、さらに修正です。
たびたび申し訳ない!。for( int di=0 ; di<5 ; di++ ) {
unsigned char *pDump = (unsigned char*)&dt[di];
printf( "--- dump dt[%d] struct ---",di );
for( int dj=0 ; dj<sizeof(status) ; dj++,pDump++ ) {
if( (dj&x0xf)==0 ) printf("\n%04X",dj);
printf( "%02X ", *pDump );
}
printf("\n");
}
printf( "--- dump dt end ---\n" );
この回答への補足
これはC++で書かれていますか?残念ながら私Cで書いてまして・・・。
取り合えず、ファイル名を「.cpp」に変えてやってみましたが、
エラー E2451 main.cpp 25: 未定義のシンボル x0xf(関数 main() )
っていうのが発生しました(汗)
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- その他(プログラミング・Web制作) pythonのプログラムについての質問です。 1 2023/05/26 10:31
- C言語・C++・C# C言語初心者 構造体 課題について 2 2023/03/10 19:48
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# このプログラミング誰か教えてくれませんか 1 2022/06/02 15:27
- C言語・C++・C# c言語 コマンドライン引数 4 2023/02/09 18:47
- C言語・C++・C# このプログラミングの問題を教えて欲しいです。 キーボードから整数kを入力し、kが配列aの中に何個存在 2 2022/12/19 22:50
- システム 質問です。 仮分数はどういう状態ですか? プログラムについてです。 例えば、とあるプログラムで、アイ 1 2023/07/24 01:39
- C言語・C++・C# このプログラミングの問題を教えてほしいです。 キーボードからデータ数nとn個のデータを入力し、平均値 3 2022/12/19 22:51
- C言語・C++・C# C言語 2 2022/07/21 00:02
- Visual Basic(VBA) 【再々投稿】VBAのプログラムで動作しなくて困っています 8 2022/10/14 09:06
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
既約分数の表示プログラム
-
10個出力で改行したいのですが...
-
printf で二進表示を行いたい。
-
Cのdoubleの浮動小数点表示につ...
-
万年カレンダーのC言語プログラ...
-
ホームページをC言語で作りたい...
-
(C言語)西暦年月日を入力して...
-
コマンドラインに出力した文字...
-
質問ですが
-
4の倍数を論理演算で表す。。
-
printfの出力内の文字をdefine...
-
C言語 プログラミング
-
strcmp
-
C言語での、年複利の計算方法...
-
コンパイルエラーについて
-
アドレスの比較について
-
入力したお金の金額からお札の...
-
3つの整数を画面から入力して...
-
C言語について
-
プログラミング言語C
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C言語について
-
printf で二進表示を行いたい。
-
cshの文字列操作(0埋め)
-
10個出力で改行したいのですが...
-
コンパイルエラーについて
-
テキストカーソル位置の取得
-
strcmp
-
unsigned int型について
-
c言語でAからZまでを表示する...
-
printf( " %2d", p * q );
-
コマンドラインに出力した文字...
-
printfの出力内の文字をdefine...
-
ホームページをC言語で作りたい...
-
コマンドプロンプトがすぐ消える
-
小数点切捨て表示
-
【C言語教えてください】sin波...
-
switch分のケースを範囲数?に...
-
二つの整数値の大小比較
-
4の倍数を論理演算で表す。。
-
defineで定数が置き換えられな...
おすすめ情報