チョウの話

書いた動機

 ついこの前、生物分類技能検定の4級を受けてきました。その検定の4級では身近な動植物の知識や高校の生物基礎のような内容を問う問題、それと植物のスケッチが出題されました。
 そこで検定を受けて終わりではなく継続的に生物に関わっていこうというのがこの記事、これから書かれるであろう生物に関した記事を書く動機です。

チョウ類の基本的な特チョウ

 成虫は翅は2対の4枚でストロー状の口を普段はぺろぺろキャンディのように丸めて持っています。彼らの主食は花の蜜だと思われがちですが樹液や腐った果実などにも集まってきます。
 幼虫はその種類に応じた特定の植物の葉を食べます。アゲハであればミカンやサンショウのミカン科の葉を、日本の国蝶であるオオムラサキの幼虫はエノキの葉を好んで食べます。(たいていのチョウの成虫は好き嫌いがないようですが、オオムラサキクヌギやコナラの樹液を好むそうです。したがって、エノキとクヌギやコナラが同時に多く生えている場所でないと生活できないのです。)
 また、幼虫から蛹となり成虫になるという完全変態をすることも特徴の一つです。

チョウの分類

 チョウ類はチョウ目の一部なのですが、このチョウ目にはガ類が含まれます。(チョウとガの区別は厳密にはない)
 一般的にチョウ類とガ類を見分ける方法として触覚の形が挙げられ、ガ類はふさふさな触覚や糸状の細い触覚を持つのに対し、チョウ類は細い触覚に先端が丸く膨らんでいるものやかぎ状になっているものが多いです。
 この記事はチョウ類を扱います。
 チョウ目の下には
 ・アゲハチョウ上科
  ・アゲハチョウ科
  ・シロチョウ科
  ・シジミチョウ科
  ・タテハチョウ科
 ・セセリチョウ上科
  ・セセリチョウ
 ・シャクガモドキ上科(1科1属)
  ・シャクガモドキ科
があり、だいたいどの科のチョウも日常的に見ることができます。(シャクガモドキは知らない)
(チョウ類-アゲハチョウ上科-セセリチョウ上科-シャクガモドキ上科-ガ類の近さらしい)

身近なチョウ

 ・アゲハ(アゲハチョウ科)
  写真準備中
 ・オオムラサキ(タテハチョウ科)
  写真準備中
 ・モンシロチョウ(シロチョウ科)
  写真準備中

これまでに出会ったチョウたち

 ・ツマグロヒョウモン(タテハチョウ科)
  高尾山山頂で撮った写真。
  

f:id:Pomato_b:20200703234105j:plain
ツマグロヒョウモンのオス
  高尾山に登った時の記事↓
 ・アカボシゴマダラ(タテハチョウ科)
  
f:id:Pomato_b:20201128001403j:plain
アカボシゴマダラ
 ・ヒカゲチョウ(タテハチョウ科)
  
f:id:Pomato_b:20201128001408j:plain
ヒカゲチョウ
 ・チャバネセセリ(セセリチョウ科)
  
f:id:Pomato_b:20201128000302j:plain
チャバネセセリ
  イチモンジセセリと似ているが後翅裏の白い斑点が弧を描いているように見えるのでチャバネセセリだと思われる。

おわりに

 今回の記事はチョウについて結構ざっくりとした説明で個々のチョウの特徴などの深いところまで触れられていません。次にチョウについて書くとしたらハチによる寄生やアリとの共生について書いてみたいです。
 実は写真を載せて皆さんに見てもらいたいのも記事を書く動機の一つだったりします。
 写真がないところはこれから撮ります。

参考

 ・オオムラサキについて | 北杜市オオムラサキセンター公式サイト
 ・槐 真史 編著 (2013)『ポケット図鑑日本の昆虫 1400 ①』伊丹市昆虫館 監修,文一総合出版

 

高尾山へ行きました

準備

よく(人生で5回くらい)高尾山には登りに行っていて、その時は自分一人のため革靴とか履いて手ぶらでポンポンポーンみたいに登っていくのですが、今回は友達と二人で登りに行ったため少し準備をしました。
・持ち物
 -登山バッグ(多分32L)
 -ペットボトル(水500ml*1,ポカリ500ml*1)
 -手ぬぐい, タオル, ハンカチ, ポケットティッシュ
 -救急セット
 -地図
 -帽子
 -雨具
 -カメラ

・装備
 -パーカー
 -Tシャツ
 -登山ズボン(発汗性に優れてるやつ)
 -登山靴

服装に関しては長そで長ズボン一択で、高い山ではないけれど一応日よけに帽子
地図とコンパスはセットで持った方がいいかも

ルート

ルートは
稲荷山コース
→山頂
→四号路
→少し登って薬王院
→リフト
→温泉

で行きました。これが最強だと思います。
六号路の川を登るのもいいと考えましたが前日に雨が降ったので安定を取って稲荷山コースにしました。
稲荷山コース(or六号路)を登るから登山靴を用意する必要があったんですね。

山頂ではいろいろな虫さんたちに出会えました。
鳥さんの声は勉強していないので誰がいたかはわかりません(次回リベンジ)

写真

f:id:Pomato_b:20200703233819j:plain
マイマイガの幼虫
発見当初は成虫のキアシドクガがたくさん飛んでいてこれもキアシドクガだろう(小並)とか思ってたら違うみたいです。
マイマイガの成虫は見られませんでした。
マイマイガの一齢幼虫には毒があるらしい。
キアシドクガの蛹は空になったのにしか出会えませんでしたorz
成虫さんはたくさん飛んでいてきれいでした
キアシドクガに毒はないらしい

f:id:Pomato_b:20200703234105j:plain
ツマグロヒョウモンのオス
タテハチョウ科の前足が退化していて四本に見えるいい写真だあ
山頂にはモンキアゲハやクロアゲハ(カラスアゲハかもしれない)がいっぱい飛んでた

f:id:Pomato_b:20200703234700j:plain
稲荷山コースの道中
最後に急な長い階段があるので注意

f:id:Pomato_b:20200703234824j:plain
展望台からの眺め
これはいい眺め
展望台の下に羽が破れたアサギマダラがひらひら飛んでいて近くで写真撮りたかったなあ


他にも写真はありますがここまでで

おわりに

めちゃくちゃ楽しかった
一人で行くのもいいけど友達と行くのも感動を共有できるうれしさがあってとてもよかった
高尾山は比較的低く登りやすいのでぜひ(一号路はTシャツ短パンでも登れる)
山頂には高尾山に住んでいる生き物を紹介する施設やうまい蕎麦屋があるので登る価値あり(ふもとにも生き物などを紹介する施設あり)
温泉はいいぞ

登山グッズはJack Wolfskin, モンベルで買ってます

TSPをGAで解いてみようという話

TSP(Traveling Salesman Problem)とは

・日本語で「巡回セールスマン問題」
・N個の都市と都市間の距離が与えられ,それぞれの都市を一回訪れ最初の都市に帰ってきたときの移動距離を最小化する問題
・全巡回経路を列挙して距離を計算して...とやっていると人生が終わってしまう(たしかそこらのpcだと13都市が限界)
wikipedia:巡回セールスマン問題

GA(Genetic Algorithm)とは

・日本語で「遺伝的アルゴリズム
・解の候補を遺伝子に見立てて操作する手法で,ナップサック問題であればあるものを入れるときは"1"入れない時は"0"としてこれらの組み合わせで解を探す.TSPでは都市に通し番号(激ウマギャグ)をつけて(0,1,...,N)これらの順列を考えることで探索をする.
・あくまで近似解が得られるアルゴリズム
wikipedia:遺伝的アルゴリズム

GAの手順

1.初期化(現世代,次世代)
while(指定回数 or (最適解-近似解)<閾値 or 時間)
 2.現世代を評価,近似解を更新
 3.現世代から次世代を作成
 4.次世代を現世代に代入
5.出力

・現世代から次世代を作成する方法は選択(淘汰)や親それぞれから遺伝子(経路)を引き継ぐ"交叉",突然変異がある

今回の形式

・ファイルから都市の個数とそれぞれの距離を入力する
・評価の方法
 -単純に巡回距離
・選択の方法(未定)
・交叉の方法(未定)
・GAの終了条件(未定)

コード

※ファイル入力からグラフ作成,ランダムで現世代を作成,現世代の評価とsortまで実装済
github.com
※追記:全列挙探索のソース書きました

おわりに

・これから頑張っていきます

C言語でシェルソート

シェルソートとは

・シェルさんが発表したソートアルゴリズムクイックソートが発見されるまでは最高速のソートアルゴリズムとして知られていたらしい。
・単純なソートアルゴリズムと知られる挿入ソートにひと手間加えて計算量を挿入ソートでは"O(n^2)"だったのを"O(n^(1.25))~O(n^1.5)"まで減らした。

※挿入ソートとは
バブルソートなどと同じ単純なソートアルゴリズムに数えられる。
 イメージは箱の中身をすべて一回出して一つずつ箱に入れていく。その時にすでに入っている箱の中身と比較してしかるべき場所に挿入する。これをn-1回(最初の一つはすでに箱の中に入れているため)繰り返して要素を整列させていく。
 最大でn*(n-1)/2回の比較がされるが、すでに整列されているものをソートにかけると(n-1)回で済む。つまり、ソート前の要素が整列されていればその分比較回数が減る。
追記: 交換回数は平均n/2回(多分)

挿入ソートと何が違うのか

・変数hを用いて要素をhの間隔で挿入ソートを行い、その間隔を狭めていき最終的にh=1で挿入ソートを行う。
 大まかに挿入ソートを行っていき少しずつ細かくしていくイメージ。ただし、間隔hが常に偶数または奇数の場合比較されるものが変わらないためシェルソートの強みは発揮できない。
 何回も挿入ソートを行うのでぱっと見計算量がもとより増えると思うかもしれないが、ある程度ソートされたものをソートしていくのでそれぞれのソートの効率がよく、計算量が減る。

シェルソートの数学的な解析は完全にはされていないらしい

コード

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

void shell_sort(int*, int);  //シェルソートのプロトタイプ宣言
//void insertion_sort(int*, int);  //挿入ソートのプロトタイプ宣言
void swap(int*, int*);  //要素入れ替えのプロトタイプ宣言

int main(void) {
    int n;  //データ数

    scanf("%d", &n);  //データ数入力

    int a[n];  //ソート対象の配列

    srand(0);  //初期化
    for (int i = 0; i < n; i++) {
        a[i] = rand();  //ランダムに配列の要素を決定
    }

    for (int i = 0; i < n; i++) {  //ソート前の配列要素の表示
        if(i == 0) printf("%d", a[i]);
        else printf(" %d", a[i]);
    }
    printf("\n");

    shell_sort(a, n);  //シェルソート

    for (int i = 0; i < n; i++) {  //ソート後の配列要素の表示
        if(i == 0) printf("%d", a[i]);
        else printf(" %d", a[i]);
    }
    printf("\n");

    return 0;
}

//シェルソート
void shell_sort(int a[], int n) {
    int h, i, j;

    for (h = 1; h < n/9; h = h*3+1);  //間隔hの決定
    for (; h > 0; h /= 3) {  //間隔hを狭めていく 
        for (i = h; i < n; i++) {  //ここから挿入ソート
            j = i;
            while ((j >= h) && (a[j-h] > a[j])) {
                swap(&a[j], &a[j-h]);
                j -= h;
            }
        }
    }
    return;
}

//挿入ソート
/*
void insertion_sort(int a[], int n) {
    int i, j;

    for (i = 1; i < n; i++) {
        j = i;
        while ((j >= 1) && (a[j-1] > a[j])) {
            swap(&a[j], &j[j-1]);
            j--;
        }
    }
    return;
}
*/

//入れ替え
void swap(int *a, int *b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
    return;
}

実行結果

20
38 7719 21238 2437 8855 11797 8365 32285 10450 30612 5853 28100 1142 281 20537 15921 8945 26285 2997 14680
38 281 1142 2437 2997 5853 7719 8365 8855 8945 10450 11797 14680 15921 20537 21238 26285 28100 30612 32285

おわりに

・たとえ要素数が少なすぎて間隔hが1になったとしてもただの挿入ソートになるのでつかわにゃそん。
 これまでは要素数が少ないとバブルソート、多いとクイックソートを使っていたが要素数が少ない時は積極的にシェルソートを使っていこうと思う。

参考図書

・近藤嘉雪 (1992)『Cプログラマのためのアルゴリズムとデータ構造』ソフトバンク株式会社

C言語でサイゼリヤナップサック問題-再帰

なぜ作ったか

蟻本で再帰深さ優先探索を読んで何か作ってみたくなったので作った。

問題

出せる料金の上限値を決め、それを超えないように摂取カロリーが最大になるように商品を選ぶ
そのときの商品名と料金、摂取カロリーを出力せよ

処理の流れ

  1. ファイルからメニュー表(商品名,カロリー,値段)を取得
  2. 商品を端から深さ優先探索で取捨選択していく
    • 制約として料金の上限値を超えた場合にはその時点でバックトラックしている(つもり)
    • 深さ優先探索ですべての商品を考慮し終わったら、その組み合わせと摂取カロリーをそれぞれ配列と変数に持っておく
  3. 最終的な解を配列と変数から取得、表示

※これからほかの手法(動的計画法、ビット演算)と計算時間を比較したいのでをインクルードして計算時間を計算している

メニュー表

商品名 カロリー コスト
彩りガーデンサラダ 130 299
小エビのサラダ 115 349
やわらかチキンのサラダ 134 299
わかめサラダ 92 299
イタリアンサラダ 196 299
シーフードサラダ 229 599
半熟卵とポークのサラダ 433 599
コーンクリームスープ 142 149
冷たいパンプキンスープ(季節限定) 105 149
たっぷり野菜のミネストローネ(季節限定) 222 299
削りたてペコリーノチーズ 59 100
ミニフィセル 188 169
ガーリックトースト 252 189
辛味チキン 374 299
アスパラガスのオーブン焼き(季節限定) 221 299
ポップコーンシュリンプ 215 299
エスカルゴのオーブン焼き 256 399
ムール貝のガーリック焼き 164 399
野菜ソースのグリルソーセージ 570 399
チョリソー 393 399
柔らか青豆の温サラダ 213 199
ほうれん草のソテー 138 199
キャベツとアンチョビのソテー 80 199
ポテトのグリル 366 199
セロリのピクルス(季節限定) 52 199
真イカのパプリカソース 138 199
フォッカチオ 214 119
プチフォッカ 214 139
セットプチフォッカ 107 79
フレッシュチーズとトマトのサラダ 203 299
フレッシュチーズとトマトのサラダ(Wサイズ) 406 598
プロシュート 162 399
プロシュート(Wサイズ) 324 798
熟成ミラノサラミ 95 299
熟成ミラノサラミ(Wサイズ) 190 598
マルゲリータピザ 568 399
パンチェッタのピザ 646 399
野菜ときのこのピザ 610 399
やわらかイカのアンチョビのピザ 593 499
バッファローモッツァレラのピザ 575 499
ミラノサラミのピザ 606 499
ほうれん草のグラタン(季節限定) 521 399
シーフードグラタン 537 499
アラビアータ 591 399
ミートソースボロニア風 582 399
半熟卵のミートソースボロニア風 672 468
アーリオ・オーリオ 560 299
キャベツのペペロンチーノ 686 399
タラコソースシシリー風 605 399
スープ入りトマト味ボンゴレ(季節限定) 686 499
パルマ風スパゲッティ 700 399
イカの墨入りスパゲッティ 610 499
カルボナーラ 865 499
アスパラガスとエビのクリームスパゲッティ(季節限定) 711 499
アラビアータ(Wサイズ) 1182 770
ミートソースボロニア風(Wサイズ) 1164 770
アーリオ・オーリオ(Wサイズ) 1120 574
キャベツのペペロンチーノ(Wサイズ) 1372 770
タラコソースシシリー風(Wサイズ) 1210 770
パルマ風スパゲッティ(Wサイズ) 1400 770
イカの墨入りスパゲッティ(Wサイズ) 1220 976
カルボナーラ(Wサイズ) 1730 976
アスパラガスとエビのクリームスパゲッティ(季節限定)(Wサイズ) 1422 976
トッピング半熟卵 90 69
ミラノ風ドリア 542 299
半熟卵のミラノ風ドリア 632 368
セットプチフォッカ付きミラノ風ドリア 649 378
いろどり野菜のミラノ風ドリア 590 399
エビとイカのドリア 624 499
シーフードパエリア 602 599
エビと野菜のトマトクリームリゾット 302 399
ハヤシ&ターメリックライス 638 499
半熟卵のハヤシ&ターメリックライス 728 568
ミックスグリル 823 599
ハンバーグステーキ 514 399
デミグラスソースのハンバーグ 628 499
野菜ソースのハンバーグ(ディアボラ風) 585 499
イタリアンハンバーグ 633 499
焼肉とハンバーグの盛合せ 709 599
若鶏のグリル(ディアボラ風) 541 499
柔らかチキンのチーズ焼き 588 499
パンチェッタと若鶏のグリル 663 599
リブステーキ 621 999
ライス 303 169
ラージライス 454 219
スモールライス 151 119
カプチーノ(アイスケーキ)(季節限定) 114 199
ティラミス(アイスケーキ) 131 199
シナモンフォッカチオ 246 169
プリンとカプチーノの盛合せ 330 399
プリンとティラミスの盛合せ 347 399
ミルクアイスのせシナモンフォッカチオ 346 319
ミルクジェラート 100 199
シチリア産レモンのソルベ 127 199
イタリアンプリン 216 249
チョコレートケーキ 166 299
コーヒーゼリー 162 299
トリフアイスクリーム 164 369

コード

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

#define MAX_N 110   //商品の最大個数
#define MAX_C 1000  //出せる最大金額

typedef struct MENU{
    char name[256]; //商品名
    int cal;    //カロリー
    int cost;   //値段
}M;

int ans[MAX_N]={};  //解
int cand[MAX_N]={}; //解候補
M m[MAX_N]; //商品のデータ
int N;  //読み込んだ商品の種類数
int max_cal=0;  //最大カロリー
int costy=0;    //最大カロリーを実現するためのコスト


void knap(int i,int sum_cal,int sum_cos){
    //出せる金額を上回った
    if(sum_cos>MAX_C) return;

    //すべての品物について考えた
    if(i==N){
        if(sum_cos<=MAX_C && max_cal<sum_cal){
            memcpy(ans,cand,sizeof(cand));
            max_cal = sum_cal;
            costy = sum_cos;
        }
        return;
    }

    //商品iを入れる
    cand[i] = 1;
    knap(i+1,sum_cal+m[i].cal,sum_cos+m[i].cost);
    
    //商品iを入れない
    cand[i] = 0;
    knap(i+1,sum_cal,sum_cos);

    return;

}

int main(void){
    FILE *fp;
    char fname[] = "test.txt";
    int cnt=0;
    clock_t start,end;

    if((fp = fopen(fname,"r"))==NULL){
        printf("errer\n");
        return -1;
    }else{
        printf("%s file opened\n",fname);
    }
    while(fscanf(fp,"%s %d %d",m[cnt].name,&m[cnt].cal,&m[cnt].cost)!=EOF) cnt++;
    fclose(fp);
    N = cnt;

    //メニュー一覧
    printf("%d\n",N);
    for(int i=0;i<N;i++) printf("name:%s\ncal:%d\ncost:%d\n\n",m[i].name,m[i].cal,m[i].cost);
    printf("\n");

    start = clock();    //開始時刻を取得
    knap(0,0,0);    //再帰ナップ
    end = clock();  //終了時刻を取得

    //最終解
    printf("you should order these! (cost<=%d)\n\n",MAX_C);
    for(int i=0;i<N;i++){
        if(ans[i]==1){
            printf("name:%s\ncal:%d\ncost:%d\n\n",m[i].name,m[i].cal,m[i].cost);
        }
    }
    printf("SUM_COST:%dyen\n",costy);   //かかるコスト
    printf("MAXI_CAL: %dcal\n\n",max_cal);  //摂取カロリー

    printf("calc_time:%fs\n\n",(double)(end-start)/CLOCKS_PER_SEC); //計算時間

    return 0;
}

実行結果

※都合上メニュー一覧は表示せず

you should order these! (cost<=1000)

name:ポテトのグリル
cal:366
cost:199

name:アーリオ・オーリオ(Wサイズ)
cal:1120
cost:574

name:ラージライス
cal:454
cost:219

SUM_COST:992yen
MAXI_CAL: 1940cal

calc_time:0.073000s

想定される解は得られたようだ

今後に向けて

上限料金の制約がない場合は再帰によって組み合わせが全列挙されて、メニューはたぶん100種類くらいあるので組み合わせは2^(100)通りくらいで実行してみたところ10分経っても終了しなかったので計算時間は測れていない。また、再帰関数で書いたので関数呼び出しのメモリが心配だったが問題なく動いた。追記で再帰関数とメモリの使用状況などで書き書きしたい。
今回は深さ優先探索ナップサック問題を解いたが今後動的計画法やビット演算などの手法と計算時間を比較し何か書ければと思う。(動的計画法では主に計算時間を、ビット演算ではメモリの使用について考えられると思う)

C言語でランダムな三文字の名前生成

なぜ作ったか

前々から音がよく、可愛い3文字の名前が欲しかった。

処理の流れ

  1. いくつ名前を生成するか指定
  2. 母音が3回出るまでランダム関数で各アルファベットに対応する数字を生成
    1. if(子音) ランダム関数で各母音に対する数字を続けて生成しカウントを+1
    2. else カウントを+1
  3. 指定した個数文字列を生成したら終了

コード

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

int main(void){
    int m,n,i,j,co=0;
    char tmp;
    char alpha[30];
    char V[5]={'A','I','U','E','O'};    //母音セット
    
    srand((unsigned)time(NULL));    //時間で初期化
    
    for(tmp='A';tmp<='Z';tmp++){    //アルファベットの配列を生成
        alpha[co] = tmp;
        co++;
    }

    printf("input Number of names to generate(1-10)>");
    scanf("%d",&m);

    for(i=0;i<m;i++){
        for(j=0;j<3;){
            n = rand()%26;  //26種類の数字を生成

            if(alpha[n]=='A' || alpha[n]=='I' || alpha[n]=='U'  //母音判定
            || alpha[n]=='E' || alpha[n]=='O'){
                printf("%c",alpha[n]);
                j++;
            }else{
                printf("%c",alpha[n]);
            
                n = rand()%5;   //5種類の数字を生成
                printf("%c",V[n]);
                j++;
            }
        }
        printf("\n");
    }
    return 0;
}

実行結果

input Number of names to generate(1-10)>10
PEOE
QEGABE
KEJOWI
TOLAKE
IDEMU
OPENU
MILIA
LOJEQU
AOFE
TUPESO

反省

あまりかわいい名前はできなかった。
生成された名前を見ればそれが可愛いのか瞬時にわかるが、どのように制約をつければ可愛い名前が生成できるのかまるで分らない。効率的に可愛い名前を生成するのは非常に難しい。この問題はNP困難かもしれない(違う)。

HNを変えるのはまだまだ先のことになりそうだ。

C言語によるじゃんけんゲーム作成

じゃんけんゲーム作成にあたって

今回はじゃんけんゲームを作る。特にこれといって作る動機はないのだが理由としては作るのが簡単そうだったからである。
本記事ではゲームの流れを考えその通りに書き最後にそのコードの問題点を挙げる。

ゲームの流れ

  1. じゃんけんの掛け声をする
  2. じゃんけんの手を出す
  3. 判定
    1. if(両者同じ手) 掛け声に戻る
    2. else 勝敗判定
  4. 勝敗表示
  5. 続けるか選択
  6. 判定
    1. if(入力がyes) 最初に戻る
    2. else 終了する

流れのコード化の構想

まずはじゃんけんを一回行えるプログラムを書いてみる。ここではじゃんけんの手は配列でもって置き、入力される数字(0,1,2)にじゃんけんの手を対応させる。また、じゃんけんの手の強弱関係は相子の時はif文で"=="で結べばいいが相子以外の時は手の組み合わせが複数あり、これをすべてif文で書くのは疲れるし何より汚い。そこで手の三すくみ関係を利用し、手と対応する数字に1を足し3のmodをとり、それが相手の手と同じになるかどうかを調べる。同じになれば勝ち、違えば負けとなる(ノートに書いて一度考えてみよう)。ほかに相子の時に追加の手の入力を促すがここで最初の変数に入力を入れてしまうと、今回は相子の条件をif(自分==相手)でとっているので相子用の入力変数が必要になる。(相手の手にはランダム関数を用いる)
それではコードを見ていこう

じゃんけんの手と強弱関係

    char ja[3][5]={"gu","chi","pa"};    //手の配列
    if((buf+1)%3==num) printf("あなたの勝ち!\n");    //勝敗判定
    else printf("あなたの負け!\n");

そのまんま
正直ここ以外のコードを抜き出して表示するほどの難しさではないので他は割愛(単にめんd(ry)

難点

自分が作っていてよくわからないところをメモ書き程度に書いておく

  • scanf後のenterキーが居残りして次のscanfが飛ばされる(かもしれない)。
    • 今回は回収用の変数を用いて回収している。
  • int型変数に文字列を入力してしまうと何も入力を受け付けない無限ループに陥ってしまう。
    • 今回は解決できていない。Javaなんかでは例外処理でtry-catchで難なく処理できるのだがcだとよくわからない。アドバイスいただいたのは「scanfで入力を文字列で受け取ればいい」とのことだったが、試してみたところむーんという感じ。改行まで前に入力されたものを流すやり方もあるみたいだが、こちらもうーんという感じ。これからどうにかします。

コード

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

int main(void){
    int flag;   //あいこになったかどうかの判定
    int num;    //com側の手
    char yn='y';    //続けるかどうかの判定
    char wakaran;   //enterkey回収(どうするのこれ)
    char ja[3][5]={"gu","chi","pa"};    //手の配列

    srand((unsigned int)time(NULL));    //乱数のシード値を現在の時刻で初期化

    while(yn=='y'){ //続けるを選ばれたならばループ
        flag = 1;   //初期化
        int buf = -1;   //入力された手

        while(buf<0 || 2<buf){ //入力がじゃんけんの手になっているか
            printf("最初はぐー\n");
            printf("じゃんけんポン!(数値で入力0.gu/1.chi/2.pa)>");
            scanf("%d",&buf);   /************文字列が入力されるとエラー*********/
            num = rand()%3; //comの手を生成

            if(buf<0 || 2<buf){    //じゃんけんの手でない
                printf("じゃんけんの手を入力してください\n");
            }else if(num==buf){   //あいこ
                flag = 0;   //あいこ判定
                printf("com:%s\nあなた:%s\n",ja[num],ja[buf]);

                while(num==buf){    //要改善点
                    int buf2=-1;
                    printf("あいこで しょ!(数値で入力0.gu/1.chi/2.pa)>");
                    scanf("%d",&buf2);

                    if(buf2<0 || 2<buf2){    //じゃんけんの手でない
                        printf("じゃんけんの手を入力してください\n");
                    }else{
                        num = rand()%3;
                        buf=buf2;
                        printf("com:%s\nあなた:%s\n",ja[num],ja[buf]);
                    }
                }
            }
        }
        if(flag) printf("com:%s\nあなた:%s\n",ja[num],ja[buf]); //あいこに一度もならなければ表示

        /************勝敗判定*************/
        if((buf+1)%3==num) printf("あなたの勝ち!\n");
        else printf("あなたの負け!\n");

        scanf("%c",&wakaran);   //enter回収

        printf("もう一度...する?(続けるならyをやめるならそれ以外を入力)>");
        scanf("%c",&yn);
    }

    printf("またね!\n");
    return 0;
}

おわりに

これから暇なときに改善していきます。
次の記事は今作っているオセロになるのかなぁ.