2018年7月
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        

Amazonウィジェット

  • お気に入り
  • 欲しいもの
  • 面白かった本
  • Raspberry Pi
  • ドローン
  • 書籍ランキング

AdSense

  • 広告
無料ブログはココログ

« VisualStudioにncurses(pdcurses)を導入する。 | トップページ | 遺伝的アルゴリズムで育てる「お掃除ロボット」 その9 ~選定と交叉の実装~ »

2018年4月11日 (水)

遺伝的アルゴリズムで育てる「お掃除ロボット」 その8 ~ちょっと修正~

前回までで、とりあえずシミュレーション環境でロボットが走るようになった。
しかしいくつか構造的な問題が見つかった。。。

その6で書いていた、普段の掃除モードと、壁当たり後の回避モードの切替のところで、
search,hittedの配列をグローバルで宣言していたのだが、
新しい遺伝子を作成したときに、そこの配列が一緒に更新されていなかった。

遺伝子を作成する関数と、robotの変数を初期化する関数を別々にし、
searchとhit時の行動パターンの入れ替え(移動の際に読み取るgen_t[]の区間の切替)を、
専用の関数を作って対処した。

最初から遺伝子を作る部分と、シミュレーション開始時のロボットの初期化をする部分を
分けて作っておけばよかった。


searchとhittedのgen_t[]配列を、ロボットの状態に合わせて簡単に切り替えるために、
2つの配列についての共用体と構造体を組み合わせて新しい型を作った。

typedef union {
	gen_t gen[GEN_LEN];
	struct {
		gen_t search[SEARCH_LEN];
		gen_t hitted[HITTED_LEN];
	}div;
}genSet_t;

typedef struct {
	int movCnt;//movement counter
	int curr;//current gen index
	int bufLen;
	gen_t *buff;
	int hittedFlag;
}work_t;

typedef struct{
	//value
	int life;
	int item;
	double score;
	//position
	point_t pos;
	point_t delta;
	//parameter
	work_t work;
	genSet_t gen_set;
}indiv_t;

gen_tのポインタ*buffに、search,hittedのどちらのアドレスを入れるかでアクセスする配列が切り替わる。 ただし切替の時に一緒にbufLenを入れないといけない。 上記共用体はindiv_tにいれる。work_tはこれまでに書いたときと変更なし。


void changeMode(indiv_t *ind, mode_t mode) {
	//カウンタ等はこの構造体で一括初期化することで、bufLenなどの書き忘れを防止した。
	//buffの中身の切り替えは分岐の中で直接ポインタ代入。
	work_t search_init = { 0,0,SEARCH_LEN,NULL,0 };
	work_t hitted_init = { 0,0,HITTED_LEN,NULL,1 };

	if (mode == search)
	{
		ind->work = search_init;
		ind->work.buff = ind->gen_set.div.search;
	}
	else
	{
		ind->work = hitted_init;
		ind->work.buff = ind->gen_set.div.hitted;
	}
}


//ind->gen_setが入った状態で呼び出す。
//ここで初期化セットされるのはそれ以外のパラメータ。
void initRobot(indiv_t *ind, point_t *ini_delta){

	//最初の位置
	ind->pos.x = INI_X;
	ind->pos.y = INI_Y;

	//最初の移動方向
	ind->delta.x = ini_delta->x;
	ind->delta.y = ini_delta->y;

	ind->life = INI_LIFE;
	ind->item = 0;
	ind->score = 0;
	
	//お掃除モードで始動
	changeMode(ind,search);

	//初期の移動方向をセット。
	rotRobot(ind, forward);
}


void execRobot(indiv_t *ind){
	//alias
	work_t *w = &ind->work;
    //life count
	ind->life--;

	point_t p;
	p.x = ind->pos.x + ind->delta.x;
	p.y = ind->pos.y + ind->delta.y;

	char c = getMapVal(p);
	if (c != CH_WALL) {
        //if movable
        moveRobot(ind,ind->delta.x,ind->delta.y);

		if (c == CH_ITEM) {
			getItem(ind);
		}

		w->movCnt++;
        if(w->movCnt > w->buff[w->curr].num-1){
			//movCnt一巡後
            w->curr++;

            if(w->curr > w->bufLen-1){
				//curr一巡後
                w->curr = 0;

				//hittedMode一周したら元のモードへ
				if (ind->work.hittedFlag) {
					changeMode(ind,search);
				}
            }
            w->movCnt = 0;
			rotRobot(ind, w->buff[w->curr].dir);
        }
	}
	else {
		//if hitted
		changeMode(ind, hitted);
        //puts("hitted");
		
		rotRobot(ind, w->buff[w->curr].dir);
	}
}

こまごまとした関数。乱数でgen_tを作るところはsaikoro関数。 配列の要素一個分を作るやつなので、引数は"&g->gen[i]"となっている。 moveRobotのマップ更新のところは本当はmap.cのほうに入れたかったが、あまり厳密にやっても面倒なのでそのままにした。

void saikoro(gen_t *g) {
	g->dir = rand() % NUMOFDIR;//kind of dir = 3
	g->num = 1 + rand() % MAX_MOVLEN;//min>=1
}

void makeIndiv(genSet_t *g) {
	for (int i = 0; igen[i]);
	}
}

void getItem(indiv_t *ind) {
	ind->life += ITEM_UP_MOUNT;
	ind->item += 1;

	ind->score = ind->item / (double)puttedItem;
}

void moveRobot(indiv_t *ind,int dx,int dy) {

	map[getIndex(ind->pos)] = ' ';//元居た場所を消す
	ind->pos.x += dx;
	ind->pos.y += dy;
	map[getIndex(ind->pos)] = CH_Robot;//新しい位置に書き込む
}

まだもう少し続く。

« VisualStudioにncurses(pdcurses)を導入する。 | トップページ | 遺伝的アルゴリズムで育てる「お掃除ロボット」 その9 ~選定と交叉の実装~ »

C言語」カテゴリの記事

コメント

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/518723/66483462

この記事へのトラックバック一覧です: 遺伝的アルゴリズムで育てる「お掃除ロボット」 その8 ~ちょっと修正~:

« VisualStudioにncurses(pdcurses)を導入する。 | トップページ | 遺伝的アルゴリズムで育てる「お掃除ロボット」 その9 ~選定と交叉の実装~ »