ホームページに戻る

Qiitaでロボット開発中

僕の作っている人間頭脳型ロボットを、Qiitaで、@shiracamusと言う方とともに共同開発中です。

以下は、そのログのコピーです。

人間の頭脳を模したロボットを作りました。

@schwarz1009 - 2017年04月12日に更新

僕の作ったロボットのコードまとめ。コードだけですみません。

ホームページでも公開しています。

思考型ロボット

一号機。

robot1.c

#include <stdio.h>
#include <stdlib.h>
int getlp(int *p){
*p = rand()%2;
return 1;
}
void action(int *p){
if(*p==0){
printf("はらが減った。");
}
if(*p==1){
printf("はらは減っていない。");
}
}
int main(void){
int p;
while(getlp(&p)){
action(&p);
}
}

二号機。

robot2.c

#include <stdio.h>
#include <stdlib.h>
struct _obj {
int p;
int hara;
};
int getlp(struct _obj *obj){
obj->p = rand()%4;
return 1;
}
void action(struct _obj *obj){
if(obj->p==0){
printf("はらは");
if(obj->hara==0){printf("減った。");}
else{printf("減っていない。");}
}
if(obj->p==1){printf("楽しい。");}
if(obj->p==2){
printf("はらが減る");
obj->hara=0;
}
if(obj->p==3){
printf("おいしいごはんを食べた");
obj->hara=1;
}
}
int main(void){
struct _obj obj;
obj.hara=1;
while(getlp(&obj)){
action(&obj);
}
}

三号機。

robot3.c

#include <stdio.h>
#include <stdlib.h>
struct _obj {
int p;
int w;
int t;
};
int getlp(struct _obj *obj){
obj->p = rand()%5;
return 1;
}
void action(struct _obj *obj){
if(obj->p==0){
printf("ここは");
if(obj->w==1){
printf("日本です。");
}
if(obj->w==2){
printf("ドイツです。");
}
}
if(obj->p==1){
printf("日本にやってきました。");
obj->w=1;
}
if(obj->p==2){
printf("ドイツにやってきました。");
obj->w=2;
}
if(obj->p==3){
printf("想像力を使います。");
printf("りんごは赤い。");
obj->t=1;
}
if(obj->p==4){
printf("想像力を使います。");
printf("りんごは白い。");
obj->t=0;
}
if(obj->p==3||obj->p==4){
if(obj->t==1){
printf("正しい。");
}
else{
printf("間違っている。");
}
}
}
int main(void){
struct _obj obj;
obj.w=1;
obj.t=1;
while(getlp(&obj)){
action(&obj);
}
}

四号機。

robot4.c

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

struct _obj {
  int o;
  int n;
};

void action(int* p, struct _obj *obj){
  if(*p==0){
    printf("[1] テレビは、つまらない\n");
  }
  if(*p==1){
    printf("[2] テレビは、面白い\n");
  }
  if(*p==2){
    printf("[3] パソコンは、つまらない\n");
  }
  if(*p==3){
    printf("[4] パソコンは、面白い\n");
  }
  if(*p==4){
    printf("[5] テレビやパソコンは、機械だ。\n");
  }
  if(*p==5){
    printf("[6] テレビやパソコンは、生き物ではない。\n");
  }
  if(*p==6){
    printf("[7] テレビって何なのだろう。\n");
  }
  if(*p==7){
    printf("[8] テレビとパソコンは好きだ。\n");
  }
  if(*p==8){
    printf("[9] テレビとパソコンは嫌いだ。\n");
  }
  if(*p==9){
    if(obj->n==0){
      printf("[10] 比較すると、テレビの方が普通だ。\n");
    }
    if(obj->n==1){
      printf("[11] 比較すると、パソコンの方が普通だ。\n");
    }
  }
  if(*p==10){
    if(obj->n==0){
      printf("[12] テレビよりパソコンの方が普通かな。\n");
      obj->n=1;
    }
    else if(obj->n==1){
      printf("[13] パソコンよりテレビの方が普通かな。\n");
      obj->n=0;
    }
  }
  if(*p==11){
    if(obj->o==0){
      printf("[14] リンゴが好きです。\n");
    }
    if(obj->o==1){
      printf("[15] みかんが好きです。\n");
    }
  }
  if(*p==12){
    if(obj->o==0){
      printf("[16] リンゴより、みかんが好きになりました。\n");
      obj->o=1;
    }
    else if(obj->o==1){
      printf("[17] みかんより、リンゴが好きになりました。\n");
      obj->o=0;
    }
  }
}

int main(void){
  int p;
  struct _obj obj;
  p=0;
  obj.o=0;
  obj.n=0;
  while(1){
    p=rand()%13;
    action(&p, &obj);
  }
}

このロボットは、改善点として、普通の人工知能を乗せる、思考を覚えて思い出す、マルチスレッドにして複数にする、などが考えられる。

推論型ロボット

僕は、何をすべきなのか、自分の力で考えるコンピュータを作るべきだと思う。

robot5.c

#include <stdio.h>

struct _obj {
  int p;
};

int check1 (struct _obj *obj) {
  if (obj->p == 0) {
    return 1;
  }
  return 0;
}
int check2 (struct _obj *obj) {
  if (obj->p == 1) {
    return 1;
  }
  return 0;
}
void action1 (struct _obj *obj) {
  printf("ここではアクション1をやります。\n");
}
void action2 (struct _obj *obj) {
  printf("ここではアクション2をやります。\n");
}
int main (void) {
  struct _obj obj;
  obj.p = 0;
  if (check1(&obj)){
    action1(&obj);
  }
  if (check2(&obj)){
    action2(&obj);
  }
}

コメント

@shiracamus - 2017-04-11 08:09

いちいちif文を書くのは大変でしょうから、配列にしてはいかがでしょう?

それと、インデントが失われているコードは、vimで gg=G するか indentコマンドで整形するといいですよ。インデント幅を指定して(vimなら :set shiftwidth=2 expandtab とか)。

#include <stdio.h>

struct _obj {
  int p;
};

int check1 (struct _obj *obj) {
  if (obj->p == 0) {
    return 1;
  }
  return 0;
}
int check2 (struct _obj *obj) {
  if (obj->p == 1) {
    return 1;
  }
  return 0;
}
void action1 (struct _obj *obj) {
  printf("ここではアクション1をやります。\n");
}
void action2 (struct _obj *obj) {
  printf("ここではアクション2をやります。\n");
}

struct brain {
  int (*check)(struct _obj *obj);
  void (*action)(struct _obj *obj);
} brain[] = {
  { check1, action1 },
  { check2, action2 },
  { NULL, NULL }
};

int main (void) {
  struct _obj obj = { .p = 0 };
  for (struct brain *b = brain; b->check; b++) {
    if (b->check(&obj)){
      b->action(&obj);
    }
  }
}

@schwarz1009 - 2017-04-12 10:09

shiracamusさん、素晴らしいです。改善ありがとうございます。

コードを良く分かっていないので、トンチンカンなことを言っているかもしれないのですが、無限ループでないと人間の頭脳のような思考ロボットにならないような気がします。ただ、コードが美しくなっているので、本当に嬉しいです。

@schwarz1009 - 2017-04-12 16:59

こうすれば良いのだと思います。

robot7.c

#include <stdio.h>

struct _obj {
  int p;
};

int check1 (struct _obj *obj) {
  if (obj->p == 0) {
    return 1;
  }
  return 0;
}
int check2 (struct _obj *obj) {
  if (obj->p == 1) {
    return 1;
  }
  return 0;
}
void action1 (struct _obj *obj) {
  printf("ここではアクション1をやります。\n");
}
void action2 (struct _obj *obj) {
  printf("ここではアクション2をやります。\n");
}

struct brain {
  int (*check)(struct _obj *obj);
  void (*action)(struct _obj *obj);
} brain[] = {
  { check1, action1 },
  { check2, action2 },
  { NULL, NULL }
};

int main (void) {
  struct _obj obj = { .p = 0 };
  while(1){
    for (struct brain *b = brain; b->check; b++) {
      if (b->check(&obj)){
        b->action(&obj);
      }
    }
  }
}

@schwarz1009 - 2017-04-12 17:38

なんか、ループの中がおかしくバグっている気がします。変数定義をループ外に追い出して、ポインタと配列をなんとかした方が良いかもしれません。

@schwarz1009 - 2017-04-12 18:49

バグを直すと、こんな風になりました。

robot8.c

#include <stdio.h>

struct _obj {
  int p;
};

int check1 (struct _obj *obj) {
  if (obj->p == 0) {
    return 1;
  }
  return 0;
}
int check2 (struct _obj *obj) {
  if (obj->p == 1) {
    return 1;
  }
  return 0;
}
void action1 (struct _obj *obj) {
  printf("ここではアクション1をやります。\n");
}
void action2 (struct _obj *obj) {
  printf("ここではアクション2をやります。\n");
}

void doing (struct _obj *obj) {
  struct brain {
    int (*check)(struct _obj *obj);
    void (*action)(struct _obj *obj);
  } brain[] = {
    { check1, action1 },
    { check2, action2 },
    { NULL, NULL }
  };
  struct brain *b;
  for (b = brain; b->check; b++) {
    if (b->check(obj)){
      b->action(obj);
    }
  }
}

int main (void) {
  struct _obj obj = { .p = 0 };
  struct _obj obj2 = { .p = 1 };
  while(1){
    doing(&obj);
    doing(&obj2);
  }
}

@schwarz1009 - 2017-04-12 19:24

すみません。今度はメモリリークしているような気がします。

@schwarz1009 - 2017-04-12 19:27

メモリリークの直し方が分からないです。すみません。

@shiracamus - 2017-04-12 20:07

静的データしかないのでメモリリークはしません。

無限ループして出力が山ほどたまってメモリ消費してるというオチじゃありませんか?

sleep(1); でも入れてゆっくり無限ループしてみてはいかがでしょう?

@schwarz1009 - 2017-04-12 20:14

すみません。メモリリークしていないのかも知れないです。

自分の技術力の無さを反省します。

@schwarz1009 - 2017-04-12 20:27

結局こんな感じになりました。

robot9.c

#include <stdio.h>

struct _obj {
  int p;
};

int check1 (struct _obj *obj) {
  if (obj->p == 0) {
    return 1;
  }
  return 0;
}
int check2 (struct _obj *obj) {
  if (obj->p == 1) {
    return 1;
  }
  return 0;
}
void action1 (struct _obj *obj) {
  printf("ここではアクション1をやります。\n");
}
void action2 (struct _obj *obj) {
  printf("ここではアクション2をやります。\n");
}

struct brain {
  int (*check)(struct _obj *obj);
  void (*action)(struct _obj *obj);
} brain[] = {
  { check1, action1 },
  { check2, action2 },
  { NULL, NULL }
};

void doing (struct _obj *obj) {
  struct brain *b;
  for (b = brain; b->check; b++) {
    if (b->check(obj)){
      b->action(obj);
    }
  }
}

int main (void) {
  struct _obj obj = { .p = 0 };
  struct _obj obj2 = { .p = 1 };
  while(1){
    doing(&obj);
    doing(&obj2);
  }
}

@shiracamus - 2017-04-12 22:48

mainをこう変更してみてはいかがですか?

#include <unistd.h>

int main (void) {
  struct _obj obj = { .p = 0 };
  struct _obj obj2 = { .p = 1 };
  while(1){
    doing(&obj);
    doing(&obj2);
    sleep(1);
  }
}

@schwarz1009 - 2017-04-12 22:53

sleep(1);の必要性はちょっと分からないです。ただ、ちょっと表示が速すぎるような気はします。

@schwarz1009 - 2017-04-28 22:58

今度のロボットは、永遠に次の思考と主題に反応して、良いか悪いかを表示します。

robot10.c

#include <stdio.h>

struct _obj {
  int tf;
  int sbj;
};

void next_subject (struct _obj *obj){
  if (obj->sbj == 0){
    obj->tf = 0;
  }
  if (obj->sbj == 1){
    obj->tf = 1;
  }
}

void mind (struct _obj *obj){
  if (obj->tf == 0){
    printf("それはきっと正しい\n");
  }
  if (obj->tf == 1){
    printf("それはきっと間違っている\n");
  }
}

int main(void){
  struct _obj obj = { .tf = 0 };
  while(1){
    obj.sbj=0;
    next_subject(&obj);
    mind(&obj);
    obj.sbj=1;
    next_subject(&obj);
    mind(&obj);
  }
}

@shiracamus - 2017-04-28 23:51

void next_subject (struct _obj *obj){
  if (obj->sbj == 0){
    obj->tf = 0;
  }
  if (obj->sbj == 1){
    obj->tf = 1;
  }
}

これって、こうしてはだめなんでしょうか?

void next_subject (struct _obj *obj){
  obj->tf = obj->sbj;
}

そもそも、sbj って何? tf って何? 0 って何? 1 って何?

@schwarz1009 - 2017-04-29 10:28

sbjは主題です。tfは真偽値です。理解出来ないかもしれないです。ごめんなさい。

@schwarz1009 - 2017-04-29 10:31

主題と真偽値は今のところ0と1になっていますが、これを追加して、たとえば主題2の時は正しい、3の時は間違っている、などとすることを想定しています。

主題と言うのは、数値になっているのは計算しやすくするためで、「猫は動物である」の時に正しい、「猫は植物である」の時に間違っている、などとすることを想定しています。

説明しないと、分からなかったかもしれないです。

@shiracamus - 2017-04-29 13:32

0 とか 1 はマジックナンバーと呼ばれ、プログラムの可読性、保守性が低下します。

enum を使うようにしましょう。

変数名はできる限り省略せず、変数に格納される値の「意味」を表す名前にしましょう。

こんなことをしたいのかな? と考えてみた例を示しておきます。参考になれば幸いです。

#include <stdio.h>
#include <unistd.h>

typedef const char *String;
typedef enum { TRUE = (0 == 0), FALSE = (0 != 0) } Boolean;
typedef enum { CORRECT, WRONG, UNKNOWN } Judge;

typedef enum {
    NONE,
    ROBOT, CAT, DOG,
    MACHINE, HUMAN, ANIMAL, PLANT,
    PEACE, WAR
} Object;

typedef struct {
    Object object;
    Object type;
} Category;

Category categorize[] = {
    { ROBOT, MACHINE },
    { CAT, ANIMAL },
    { DOG, ANIMAL },
    { NONE, NONE }
};

Boolean Object_is(Object object, Object complement) {
    if (object == complement)
        return TRUE;
    for (Category *category = categorize; category->object != NONE; category++) {
        if (category->object == object &&
            category->type == complement)
            return TRUE;
    }
    return FALSE;
}

typedef struct {
    Object object;
    Judge judge;
} Mind;

typedef struct {
    String name;
    Object type;
    Mind *mind;
} Robot;

void Robot_areYou(Robot *robot, Object complement) {
    if (Object_is(robot->type, complement)) {
        printf("%s: はい、そうです\n", robot->name);
    } else {
        printf("%s: いいえ、違います\n", robot->name);
    }
}

void Robot_isThis(Robot *robot, Object object, Object complement) {
    if (Object_is(object, complement)) {
        printf("%s: はい、そうです\n", robot->name);
    } else {
        printf("%s: いいえ、違います\n", robot->name);
    }
}

void Robot_judge(Robot *robot, Object object) {
    for (Mind *mind = robot->mind; mind->object != NONE; mind++) {
        if (mind->object != object)
            continue;
        switch (mind->judge) {
        case CORRECT:
            printf("%s: それはきっと正しい\n", robot->name);
            return;
        case WRONG:
            printf("%s: それはきっと間違っている\n", robot->name);
            return;
        default:
            printf("%s: それは分かりません\n", robot->name);
            return;
        }
    }
    printf("%s: それは分かりません\n", robot->name);
}

int main(void) {
    Mind mind_of_robot[] = {
        { PEACE, CORRECT },
        { WAR, WRONG },
        { NONE, UNKNOWN },
    };
    Robot robot = {
        .name = "Mr.Robot",
        .type = ROBOT,
        .mind = mind_of_robot,
    };
    while(1){
        Robot_areYou(&robot, ROBOT);
        Robot_areYou(&robot, MACHINE);
        Robot_areYou(&robot, HUMAN);
        Robot_isThis(&robot, CAT, ANIMAL);
        Robot_isThis(&robot, CAT, PLANT);
        Robot_isThis(&robot, CAT, DOG);
        Robot_judge(&robot, PEACE);
        Robot_judge(&robot, WAR);
        sleep(1);
    }
}

実行結果

Mr.Robot: はい、そうです
Mr.Robot: はい、そうです
Mr.Robot: いいえ、違います
Mr.Robot: はい、そうです
Mr.Robot: いいえ、違います
Mr.Robot: いいえ、違います
Mr.Robot: それはきっと正しい
Mr.Robot: それはきっと間違っている

@schwarz1009 - 2017-04-29 14:22

ありがとうございます。すごいです。感謝します!

@schwarz1009 - 2017-04-29 15:23

shiracamusさん。本当に嬉しいです。こちらのページに、コードの引用を載せさせていただきました。

ロボット

良かったら、また教えてください。感謝します。

@shiracamus - 2017-04-30 10:27

列挙型から文字列の配列を作る を使って改良してみました。

#include <stdio.h>
#include <unistd.h>

typedef const char *String;
typedef enum { YES = (0 == 0), NO = (0 != 0) } Boolean;
typedef enum { CORRECT, WRONG, UNKNOWN } Judge;

#define WORD(T) \
    T(ROBOT), T(CAT), T(DOG), \
    T(MACHINE), T(HUMAN), T(ANIMAL), T(PLANT), \
    T(PEACE), T(WAR), \
    T(NONE)
#define toEnum(name) name
#define toString(name) #name

typedef enum { WORD(toEnum) } Word;

String name[] = { WORD(toString) };

typedef struct {
    Word word;
    Word group;
} Category;

Category categories[] = {
    { ROBOT, MACHINE },
    { CAT, ANIMAL },
    { DOG, ANIMAL },
    { NONE, NONE } // terminator (Don't remove)
};

Boolean is(Word subject, Word complement) { // S+V+C
    if (subject == complement)
        return YES;
    for (Category *category = categories; category->word != NONE; category++) {
        if (subject == category->word && complement == category->group)
            return YES;
    }
    return NO;
}

typedef struct {
    Word object;
    Judge judge;
} Mind;

typedef struct {
    String name;
    Word type;
    Mind *mind;
} Robot;

void Robot_areYou(Robot *robot, Word complement) {
    if (is(robot->type, complement)) {
        printf("%s: はい、私は %s です\n",
               robot->name, name[complement]);
    } else {
        printf("%s: いいえ、私は %s ではありません\n",
               robot->name, name[complement]);
    }
}

void Robot_isThis(Robot *robot, Word object, Word complement) {
    if (is(object, complement)) {
        printf("%s: はい、%s は %s です\n",
               robot->name, name[object], name[complement]);
    } else {
        printf("%s: いいえ、%s は %s ではありません\n",
               robot->name, name[object], name[complement]);
    }
}

void Robot_judge(Robot *robot, Word object) {
    for (Mind *mind = robot->mind; mind->object != NONE; mind++) {
        if (mind->object != object)
            continue;
        switch (mind->judge) {
        case CORRECT:
            printf("%s: %s はきっと正しい\n",
                   robot->name, name[object]);
            return;
        case WRONG:
            printf("%s: %s はきっと間違っている\n",
                   robot->name, name[object]);
            return;
        default:
            printf("%s: %s は分かりません\n",
                   robot->name, name[object]);
            return;
        }
    }
    printf("%s: %s は分かりません\n",
           robot->name, name[object]);
}

int main(void) {
    Mind mind_of_robot[] = {
        { PEACE, CORRECT },
        { WAR, WRONG },
        { NONE, UNKNOWN }, // terminator (Don't remove)
    };
    Robot robot = {
        .name = "Mr.Robot",
        .type = ROBOT,
        .mind = mind_of_robot,
    };
    for (;;) {
        Robot_areYou(&robot, ROBOT);
        Robot_areYou(&robot, MACHINE);
        Robot_areYou(&robot, HUMAN);
        Robot_isThis(&robot, CAT, ANIMAL);
        Robot_isThis(&robot, CAT, PLANT);
        Robot_isThis(&robot, CAT, DOG);
        Robot_judge(&robot, PEACE);
        Robot_judge(&robot, WAR);
        sleep(1);
    }
}

実行結果

Mr.Robot: はい、私は ROBOT です
Mr.Robot: はい、私は MACHINE です
Mr.Robot: いいえ、私は HUMAN ではありません
Mr.Robot: はい、CAT は ANIMAL です
Mr.Robot: いいえ、CAT は PLANT ではありません
Mr.Robot: いいえ、CAT は DOG ではありません
Mr.Robot: PEACE はきっと正しい
Mr.Robot: WAR はきっと間違っている

@shiracamus - 2017-04-30 11:09

言葉を覚えるたびにプログラムをコンパイルするのは馬鹿げているので、いずれ言葉辞書やカテゴリー辞書を外部から読み込むことになると思います。

文章の構造を分析して単語抽出や品詞分類するライブラリもありますから、そういったライブラリを使って入力された文章を解釈して答えるロボットにするのもいいと思います。

@schwarz1009 - 2017-04-30 12:01

自分としては、作り方や解決方法を考えたり、批判や同意をするようにすると、人間らしくなるのかな、と言う気がします。

あるいは、感情表現や要求、願望などがあると良いと思います。

ただし、ただ考えているように見えて、本当は表示しているだけ、となってしまうと、ロボットとは言えないかもしれません。

@shiracamus - 2017-04-30 14:01

指示した動作を疲れ知らずで正確に繰り返す産業ロボットもロボットですよね。

人間らしくするとなると、AI(人工知能)や機械学習を取り入れると良さそうですね。

Pythonのライブラリが充実してるので、C言語よりもPythonを学んだ方が楽できると思います。