NaaN日記

やったこと、覚えたことを発信する場

戦ってみたかった(C)

最初はポインタを使ってなにかしたいなーーと思った。
特に文字コードで何かやりたかった。
しかし気がついたら黒い画面の上で戦ってた。
構造体とかswitchとか色々と詰め込みたいものを詰め込んでいた。
途中で設定に飽きた。
色々とやりたかったことはある。
そのうち気力がでたら綺麗にしたい。


というわけで体力、攻撃力、防御力、回復力というステータスを設定して、
敵と戦っている気になれるものをつくった。
なお途中で疲れたため能力値の調整が不十分、よってたまにマイナスが出る。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

typedef struct{
    char name[20];
    int life;   //体力
    int attack;   //攻撃力
    int defense;   //防御力
    int cure;   //回復力
}status;

status cha();   //自分のステータス
status ene();   //敵のステータス
void battle(status *prof, char*);   //戦う


int main(void){
    status profile;
    char name[20];
    int command;

    printf("▶名前を入力してください\n");
    scanf("%s", name);
    printf("あなたの名前は%sです\n",name);

    profile = cha();

    battle(&profile, name);   //この辺りを綺麗にしたいと

    if (profile.life <= 0){
        printf("%sの体力が0になった\nYou lose\n", name);
    }
    else{
        printf("You win\n");
    }
    return 0;
}



void battle(status *prof, char *player_name){
    int command;
    int damage;
    int attack;
    double power;

    status enemy;
    enemy = ene();
    while(prof->life > 0 && enemy.life >0){
    damage = 0;
    printf("現在の体力:%d\n", prof->life);
    printf("敵の体力:%d\n", enemy.life);
    printf("▶どうする?\n攻撃:1,突進:2,全力を出す:3,休む:4\n");
    scanf("%d", &command);

    switch(command){
        case 1:
            printf("%sの攻撃!\n", player_name);
            srand((unsigned int)time(NULL));
            power = 0.8 + (rand()*(2.2)/(1.0+RAND_MAX));
            attack = (int)prof->attack*power*0.6-(int)enemy.defense*0.1;
            enemy.life -= attack;
            printf("敵に%dのダメージ\n", attack);
            break;
        case 2:
            attack = (int)prof->attack*1.2-(int)enemy.defense*0.1;
            enemy.life -= attack;
            printf("敵に%dのダメージ\n", attack);
            prof->life -= (int)attack / 2;
            printf("反動で%dのダメージを受けた!\n", (int)enemy.attack/2);
            break;
        case 3:
            prof->attack *= 1.5;
            prof->defense *= 1.5;
            prof->cure *= 1.5;
            printf("%sは本気を出した\n", player_name);
            break;
        case 4:
            prof->life += (int)prof->cure*0.8;
            printf("体力が%d回復した\n", (int)(prof->cure*0.8));
            break;
        default:
            continue;
    }

    if(enemy.life>0){
        int act;
        srand((unsigned int)time(NULL));
        act = 1+(int)(rand()*4.0/(1.0+RAND_MAX));

        switch(act){
            case 1:
                power=0.8+(rand()*(1.4)/(1.0+ RAND_MAX));
                damage += (int)enemy.attack*power-(int)prof->defense*0.1;
                prof->life -= damage;
                printf("敵の攻撃!!\n%s%dのダメージを受けた\n", player_name,damage);
                break;
            case 2:
                enemy.defense *= 1.2;
                printf("敵が固くなった!!\n敵の防御力が上がった\n");
                break;
            case 3:
                enemy.life += (int)enemy.cure * 0.8;
                printf("敵の休息!!\n敵の体力が%d回復した\n", (int)(enemy.cure * 0.8));
                break;
            case 4:
                printf("敵は様子を見ている\n");
                break;
            }
        }
    else{
        printf("敵の体力が0になった\n");
        }
    }
}



status cha(){
    int character;
    int i=0;

    while(i==0){
        printf("▶どのキャラクターにしますか?\natacker:1,shielder:2,priest:3\n");
        scanf("%d", &character);

        status chara;
        switch(character){
        case 1:
            strcpy(chara.name, "power");
            chara.life = 120;
            chara.attack = 65;
            chara.defense = 36;
            chara.cure = 40;
            i++;
            break;
        case 2:
            strcpy(chara.name, "shielder");
            chara.life = 150;
            chara.attack = 30;
            chara.defense = 80;
            chara.cure = 50;
            i++;
            break;
        case 3:
            strcpy(chara.name, "priest");
            chara.life = 200;
            chara.attack = 30;
            chara.defense = 40;
            chara.cure = 60;
            i++;
            break;
        default:
            printf("誤った入力です\n");
            continue;
        }
        return chara;
    }
}



status ene(){
    double power;
    static int flag;
    if (flag == 0){
        srand((unsigned int)time(NULL));
        flag = 1;
    }
    power = 0.8+(rand()*2.0/(1.0+RAND_MAX));

    status enemy;
    strcpy(enemy.name, "enemy");
    enemy.life = 140*power;
    enemy.attack = 30*power;
    enemy.defense = 30*power;
    enemy.cure = 10*power;
    return enemy; 
}

入力する文字を指定する(C)

今回はスキャン集合を確認。

この文字以外入力させたくない
scanf("%[abcdefg]", moji)   //abcdefgのみ入力できる
入力する範囲を選択

1.

scanf("%[a-z]", moji)   //aからz以外が入力されると終了


2.

scanf("%[^\n]" moji)   //改行以外の入力を受け付ける

これをするとホワイトスペースを含んだ文字列を入力できる

構造体を配列にする(C)

構造体は配列にもできる
#include <stdio.h>
#include <string.h>

typedef struct{
    int age;
    char animal[10];
}animals;

int main(void){
    int i;

    animals data[3];//配列として宣言

    data[0].age = 3;
    strcpy(data[0].animal, "いぬ");
    data[1].age = 4;
    strcpy(data[1].animal, "ねこ");
    data[2].age = 5;
    strcpy(data[2].animal, "インコ");
    for(i=0; i<3; i++){
        printf("年齢:%d\n", data[i].age);
        printf("種類:%s\n", data[i].animal);
    }
  
    return 0;
}
/*出力結果
年齢:3
種類:いぬ
年齢:4
種類:ねこ
年齢:5
種類:インコ
*/
勿論引数にも入れられる
#include <stdio.h>
#include <string.h>

typedef struct{
    char animal[64];
    int age;
} animals;

void animals_print(animals data[]);

int main(void)
{
    animals data[3];

    data[0].age = 3;
    strcpy(data[0].animal, "いぬ");
    data[1].age = 4;
    strcpy(data[1].animal, "ねこ");
    data[2].age = 5;
    strcpy(data[2].animal, "インコ");

    animals_print(data);

    return 0;
}

void animals_print(animals data[]){
    int i;

    for(i=0;i<3;i++){
        printf("種類:%s\n", data[i].animal);
        printf("年齢:%d\n", data[i].age);
    }
}

/*出力結果
種類:いぬ
年齢:3歳
種類:ねこ
年齢:4歳
種類:インコ
年齢:5歳
*/

構造体でポインタ

構造体型のポインタ変数
書き方
(*変数名).要素名
変数名->要素名
変数名[0].要素名
実際に使ってみる
#include <stdio.h>
#include <string.h>
typedef struct {
    char animal[64];
    char name[64];
    int old;
    int weight;
} animals;

int main(void){
    
    animals data;
    animals *p_data;
    p_data = &data;
    (*p_data).old = 2;   //p_data->old
    strcpy(p_data->animal, "ねこ");  //(*p_data).animal

    printf("%d\n", data.old);
    printf("%s\n", data.animal);
    
    return 0;
}
勿論引数にも入れられる
#include <stdio.h>
#include <string.h>
typedef struct {
    char animal[64];
    char name[64];
    int old;
    int weight;
} animals;

void animals_print(animals *data);

int main(void){
    animals data;
    strcpy(data.animal,"ねこ");
    strcpy(data.name, "きなこ");
    data.old = 2;
    data.weight = 5;
    
    animals_print(&data);
    
    return 0;
}
void animals_print(animals *data){
    
    data->weight = 6;//勿論元の値も書き換わる
    printf("種類:%s\n", data->animal);
    printf("名前:%s\n", data->name);
    printf("年齢:%d\n", data->old);
    printf("体重:%dkg\n", data->weight);
    return;
}

最大値と最小値を出す(C)

ポインタを使ってみるよ
ポインタを使った式を、もっと短くかけたので、追記。
素敵なことを教えていただいたので追記
与えられた数の中から最も大きいものと小さいものを表示するようにする

入力方式
[一つ目の値, 二つ目の値, 三つ目の値……, 最後の値,, 終了を意味する任意の値]

まずはポインタをできるだけ使わずにやってみる
#include <stdio.h>
int main(void){
	int i = 0;
	int array[10];//値を何個格納するか
	int max = 1;//入力する可能性のある最低値
	int min = 1000;//入力する可能性のある最大値
//この場合、1 ~ 1000の範囲の値を最大9個入力
	
	do{
		scanf("%d", &array[i]);
		printf("%d番:%d\n", i+1,array[i]);
		i++;
	}while(array[i - 1] != 0);//0が入力された時点で終了
	
	for (i = 0; array[i] >= 0; i++){
		if (array[i] == 0){
			break;//0のとき抜ける
		}
		else{
			if (array[i] > max){
				max = array[i];//array[i]の中で一番大きいものをmax
			}
			if (array[i] < min){
				min = array[i];//array[i]の中で一番小さいものをmin
			}
		}
	}
	printf("最小値%d\n", min);
	printf("最大値:%d\n", max);
	return 0;
}
ポインタを使うとこんな感じ
#include <stdio.h>

void calculate(int array[],int*,int*);

int main(void){
	int i = 0;
	int array[10];//値を格納
	int max, min;
	
	do {
		scanf("%d",&array[i]);
		printf("%d番:%d\n",i+1,array[i]);
		i++;
	} while (array[i-1] != 0);//0が入力された時点で終了
	
	calculate(array,&max,&min);
	
	printf("最大値:%d\n最小値:%d\n",max, min);
	
	return 0;
}

void calculate(int array[],int *max,int *min){
	int i;
	int high = 1;//最大値
    int low = 1000;//最小値

	for(i=0; array[i]!=0; i++){
	    if(array[i] > high){
	        high = array[i];
	    }
	    if(array[i] < low){
	        low = array[i];
	    }
	}
	        *max = high;
	        *min = low;
}
high lowにいれなくてもできた
void calculate(int array[], int *max, int *min){
	int i;
	*max = 1;
	*min = 1000;

	for (i = 0; array[i] != 0; i++){
		if (array[i] > *max){
			*max = array[i];
		}
		if (array[i] < *min){
			*min = array[i];
		}
	}
}
すごい
void calculate(int array[], int *max, int *min){
	int i;
	*max = *min = *array;   //max,minの初期値にarray[0]が入る

	for (i = 1; array[i] != 0; i++){
		if (array[i] > *max){
			*max = array[i];
		}
		if (array[i] < *min){
			*min = array[i];
		}
	}
}

比較の時に最初の値をmaxとminに入れておいてぐるぐるするので、
intの範囲でどんな数でも入れられるようになった

iを消した
void calculate(int array[], int *max, int *min){
	for (*min = *max = *array; *array; array++){
		if (*array > *max){
			*max = *array;
		}
		if (*array < *min){
			*min = *array;
		}
	}
}

2行短くなった!!

表示

[283 876 961 346 315 0]と入力すると、どれも
次のように表示される

1番:283
2番:876
3番:961
4番:346
5番:315
6番:0
最大値:961
最小値:283

やったね

配列のポインタ(C)

コメントで教えてくださりありがとうございます。
表し方の数を増やしました。

これらは同じ意味
data[i]
*(data + i)
*(i + data)    //New!!
i[data]    //New!!

i[data]で表せることにビックリした

配列を書き換えられる
    int arr[5] = {1,2,3,4,5};
    printf("%d\n", arr[2]); //3
    data = arr;
    data[2] = 8; //他の書き方可
    printf("%d\n", arr[2]); //8
使ってみる
#include <stdio.h>
int main(void){
    int *data;
    int i;
    int sum = 0;
    int arr[5] = {1,2,3,4,5};
    
    data = arr;
    for(i = 0; i < 5; i++){
        sum += data[i]; //他の書き方可
    }
    printf("%d\n", sum); //15

    return 0;
}
上のfor文はこう書ける
    for(data = arr; data != &arr[5]; data++){
        //data = &arr[0]
        sum += *data;
    }
引数に配列を与える

色々書ける

型 hoge(型 配列名[要素数]);
型 hoge(型 配列名[]);
型 hoge(型 *配列名);

ポインタを理解しようと思って解いた問題の解答例がエラーばかりで悲しい

ポインタを使えるようになりたい(C)

ポインタは難しい。

同じアドレスになる
#include <stdio.h>

int main(void){
    int *p; //ポインタ変数を宣言
    int i = 2;
    p = &i; //pにiのアドレスを入れる
    printf("%d\n", i);
    printf("%p\n", &i); //iのアドレスを表示
    printf("%p\n", p);//pのアドレスを表示

    return 0;
    }

//2
//0x7ffcfdad145c
//0x7ffcfdad145c
書き換えられる
int *p;
int i = 2;
p = &i;
*p = 5;
printf("%d\n", i); // 5
NULLを使って事故を防止

宣言したポインタ変数に使えないアドレスが入っていると事故る

int *p = NULL;
使ってみる

ポインタ演算を使わずに足し算

#include <stdio.h>
int main(void){
    int i, sum = 0, num[10]={1,2,3,4,5,6,7,8,9,10};
    for(i = 0; i<10; i++){
        sum += num[i];
    }
    printf("%d", sum);
    return 0;
}


ポインタ演算を使うと、

#include <stdio.h>
int main(void){
    int *data, sum = 0, num[10]={1,2,3,4,5,6,7,8,9,10};
    for(data = num; data != &num[10]; data++){
        sum += *data;
    }
    printf("%d", sum);
    return 0;
}

つづく