#0.ソースコードのリンク
全体のソースコードは
↓リンク↓
http://qiita.com/66zaha_9su/items/3ebe6ba302b4627c53c6
#1.ことの発端
同胞で集まる定例会にて、内輪のPCを覗かせてもらった。
それが、AIを使った1対1のシューティングのデモだった。
それから着想を得て、「それに似たものを作りたいなぁ」と思い。
制作に至った。
#2.AIとは?
Artificial Intelligence の略。
日本語に訳せば、「人工的な知能」。
・
・
・
一括りに「AI」といっても、さまざま種類のAIが存在します。
大きく分けて2つ。
1つ目は「学習するAI」
もう1つは「学習しないAI」です。
1つ目の具体例として「Alpha Go」があり、
「Alpha Go」で囲碁プレーヤーを打ち負かしたことで
とても有名になりました。
他にも「Watson」など・・
もう1つの方は
各場面に応じて手を考える「オセロプログラムのAI」や
プレイヤーに対して絶対勝てる手を出す「じゃんけんプログラムの相手のAI」
などもその1つといえるでしょう。
#3.プログラムの紹介とその他の説明
サッカーのプレイヤーをAIとして作った。
サッカーの代表的なルールの
「オフサイド」
「イエローカード」
「レッドカード」
は無しとする。
そしてキーパーも居ない。
#4.ソース解説
プレイヤークラス
class Player{
float p_radius = 28;
float p_xPos;
float p_yPos;
float p_xVec;
float p_yVec;
float p_spd;
float p_angl;
float p_power;
int p_siya;
int p_siya_d;
Player(){ //mainly initiallize process initial position determine another process
p_spd=12*random(0.6,0.9);
p_angl=0;
p_xPos=0;
p_yPos=0;
p_siya=(int) random(2,12);
p_siya_d=(int) random(160,600);
p_power=random(12,32);
}
void P_initialize(int _number){
int p_number = _number;
if(p_number%2 ==0){
if(p_number == 0){
fill(#ff7fff);
ellipse(40, 80, p_radius, p_radius);
p_xPos=40; p_yPos=80;
}
if(p_number == 2){
fill(#ff7fff);
ellipse(200, 80, p_radius, p_radius);
p_xPos=200; p_yPos=80;
}
if(p_number == 4){
fill(#ff7fff);
ellipse(360, 80, p_radius, p_radius);
p_xPos=360; p_yPos=80;
}
if(p_number == 6){
fill(#ff7fff);
ellipse(150, 350, p_radius, p_radius);
p_xPos=150; p_yPos=350;
}
if(p_number == 8){
fill(#ff7fff);
ellipse(250, 350, p_radius, p_radius);
p_xPos=250; p_yPos=350;
}
}
else if(p_number%2 ==1){
if(p_number == 1){
fill(#00bfff);
ellipse(40, 720, p_radius, p_radius);
p_xPos=40; p_yPos=720;
}
if(p_number == 3){
fill(#00bfff);
ellipse(200, 720, p_radius, p_radius);
p_xPos=200; p_yPos=720;
}
if(p_number == 5){
fill(#00bfff);
ellipse(360, 720, p_radius, p_radius);
p_xPos=360; p_yPos=720;
}
if(p_number == 7){
fill(#00bfff);
ellipse(150, 450, p_radius, p_radius);
p_xPos=150; p_yPos=450;
}
if(p_number == 9){
fill(#00bfff);
ellipse(250, 450, p_radius, p_radius);
p_xPos=250; p_yPos=450;
}
}
}
void update(int num){
float p_dist;
float x_dist;
float y_dist;
float kakudo;
float p_siya_f; //f means from
float p_siya_e; //e means end
float p_x_ran;
float p_y_ran;
p_dist=sqrt((p_xPos-eb_xPos)*(p_xPos-eb_xPos)+(p_yPos-eb_yPos)*(p_yPos-eb_yPos));
x_dist=eb_xPos-p_xPos;
y_dist=eb_yPos-p_yPos;
kakudo=degrees(atan2(x_dist,y_dist));
if(kakudo < 0){
kakudo+=kakudo+360;
}
p_angl=kakudo;
p_siya_f = 360 * ( (float)frame % (float)p_siya ) / (float)p_siya;
p_siya_e = 360 * ( (float)frame % (float)p_siya +1) / (float)p_siya;
if( p_dist < ((float)p_siya_d) && p_siya_f < kakudo && kakudo < p_siya_e){
if(x_dist<=0 && y_dist <=0){
p_x_ran=p_spd*(abs(cos(kakudo*PI/180)))*-1;
p_y_ran=p_spd*abs(sin(kakudo*PI/180))*-1;
p_xPos+=p_x_ran;
p_yPos+=p_y_ran;
}
else if(x_dist>=0 && y_dist <=0){
p_x_ran=p_spd*abs(cos(kakudo*PI/180));
p_y_ran=p_spd*abs(sin(kakudo*PI/180))*-1;
p_xPos+=p_x_ran;
p_yPos+=p_y_ran;
}
if(x_dist<=0 && y_dist >=0){
p_x_ran=p_spd*abs(cos(kakudo*PI/180))*-1;
p_y_ran=p_spd*abs(sin(kakudo*PI/180));
p_xPos+=p_x_ran;
p_yPos+=p_y_ran;
}
else if(x_dist>=0 && y_dist >=0){
p_x_ran=p_spd*abs(cos(kakudo*PI/180));
p_y_ran=p_spd*abs(sin(kakudo*PI/180));
p_xPos+=p_x_ran;
p_yPos+=p_y_ran;
}
//hamidasi
if(p_xPos>400-p_radius) p_xPos=400-p_radius;
if(p_xPos<p_radius) p_xPos=p_radius;
if(p_yPos>800-p_radius) p_yPos=800-p_radius;
if(p_yPos<p_radius) p_yPos=p_radius;
dist[num]=sqrt((p_xPos-eb_xPos)*(p_xPos-eb_xPos)+(p_yPos-eb_yPos)*(p_yPos-eb_yPos));
}
else if(p_dist < ((float)p_siya_d) ){
if(x_dist<0 && y_dist <0){
p_x_ran=p_spd*abs(cos(kakudo*PI/180))*-1*random(-0.75,0.75);
p_y_ran=p_spd*abs(sin(kakudo*PI/180))*-1*random(-0.75,0.75);
p_xPos+=p_x_ran;
p_yPos+=p_y_ran;
}
else if(x_dist>0 && y_dist <0){
p_x_ran=p_spd*abs(cos(kakudo*PI/180))*random(-0.75,0.75);
p_y_ran=p_spd*abs(sin(kakudo*PI/180))*-1*random(-0.75,0.75);
p_xPos+=p_x_ran;
p_yPos+=p_y_ran;
}
if(x_dist<0 && y_dist >0){
p_x_ran=p_spd*abs(cos(kakudo*PI/180))*-1*random(-0.75,0.75);
p_y_ran=p_spd*abs(sin(kakudo*PI/180))*random(-0.75,0.75);
p_xPos+=p_x_ran;
p_yPos+=p_y_ran;
}
else if(x_dist>0 && y_dist >0){
p_x_ran=p_spd*abs(cos(kakudo*PI/180))*random(-0.75,0.75);
p_y_ran=p_spd*abs(sin(kakudo*PI/180))*random(-0.75,0.75);
p_xPos+=p_x_ran;
p_yPos+=p_y_ran;
}
//hamidasi
if(p_xPos>400-p_radius) p_xPos=400-p_radius;
if(p_xPos<p_radius) p_xPos=p_radius;
if(p_yPos>800-p_radius) p_yPos=800-p_radius;
if(p_yPos<p_radius) p_yPos=p_radius;
dist[num]=sqrt((p_xPos-eb_xPos)*(p_xPos-eb_xPos)+(p_yPos-eb_yPos)*(p_yPos-eb_yPos));
}
else if(p_dist > ((float)p_siya_d) ){
if(x_dist<=0 && y_dist <0){
p_x_ran=p_spd*(abs(cos(kakudo*PI/180))+random(-5.0,5.0))*-1*random(0,0.4);
p_y_ran=p_spd*abs(sin(kakudo*PI/180))*-1*random(0,0.4);
p_xPos+=p_x_ran;
p_yPos+=p_y_ran;
}
else if(x_dist>=0 && y_dist <0){
p_x_ran=p_spd*(abs(cos(kakudo*PI/180))+random(-5.0,5.0))*random(0,0.4);
p_y_ran=p_spd*abs(sin(kakudo*PI/180))*-1*random(0,0.4);
p_xPos+=p_x_ran;
p_yPos+=p_y_ran;
}
if(x_dist<=0 && y_dist >0){
p_x_ran=p_spd*(abs(cos(kakudo*PI/180))+random(-5.0,5.0))*-1*random(0,0.4);
p_y_ran=p_spd*abs(sin(kakudo*PI/180))*random(0,0.4);
p_xPos+=p_x_ran;
p_yPos+=p_y_ran;
}
else if(x_dist>=0 && y_dist >0){
p_x_ran=p_spd*(abs(cos(kakudo*PI/180))+random(-5.0,5.0))*random(0,0.4);
p_y_ran=p_spd*abs(sin(kakudo*PI/180))*random(0,0.4);
p_xPos+=p_x_ran;
p_yPos+=p_y_ran;
}
//hamidasi
if(p_xPos>400-p_radius) p_xPos=400-p_radius;
if(p_xPos<p_radius) p_xPos=p_radius;
if(p_yPos>800-p_radius) p_yPos=800-p_radius;
if(p_yPos<p_radius) p_yPos=p_radius;
}
}
void p_kick_hantei(int num){
float min=min(dist);
if(dist[num]<p_radius && min == dist[num]){
kicked=true;
ball.kick(p_angl,p_power,num); //ball.kick method uses as ball.kick(ANGLE,POWER);
}
}
void display(int num){
float hani =(float) p_siya;
float siya1 =(float) (frame%p_siya);
float siya2 =(float) ((frame+1)%p_siya);
float siya_d=p_siya_d;
float kakudo=360.0;
if(num%2 == 0){
fill(#ff7fff,255);
ellipse(p_xPos,p_yPos,p_radius,p_radius);
fill(#ff7fff,63);
arc( p_xPos, p_yPos, siya_d*2, siya_d*2, radians(kakudo*siya1/hani),radians(kakudo*siya2/hani));
alpha(0);
}
else{
fill(#00bfff,255);
ellipse(p_xPos,p_yPos,p_radius,p_radius);
fill(#00bfff,63);
arc( p_xPos, p_yPos, siya_d*2, siya_d*2, radians(kakudo*siya1/hani),radians(kakudo*siya2/hani));
alpha(0);
}
}
void p_goal_init(int num){
float x_pos=random(p_radius*2,400-p_radius*2);
float y_pos=random(p_radius*2,800-p_radius*2);
p_xPos=x_pos;
p_yPos=y_pos;
}
}
解説として、
①・ボールが視程の範囲内かつ視野のレーダーの中に入っていれば、活発に動き、
②・レーダーの中にボールが入っていないが、視程の範囲内であれば上の条件ほどではないが動く。
1,2にも満たさない場合は、少しずつボールに近づいていく。
ボールクラス
class Ball{
float b_radius;
float b_xPos;
float b_yPos;
Ball(){
b_radius=18.0;
b_xPos=200;
b_yPos=400;
}
void B_initialize(){
fill(#000000);
ellipse(200, 400, b_radius, b_radius);
b_xPos=200; b_yPos=400;
eb_xPos=200; eb_yPos=400;
}
void display(){
fill(#000000);
ellipse(b_xPos, b_yPos, b_radius, b_radius);
eb_xPos=b_xPos; eb_yPos=b_yPos;
}
void kick(float kakudo,float power,int num){
float p_x_kick=(cos(kakudo*PI/180)+0.2)*power;
float p_y_kick=(sin(kakudo*PI/180)+0.2)*power;
if(num%2 ==0){
b_xPos+=p_x_kick;
b_yPos+=p_y_kick;
if(b_xPos>400-b_radius) b_xPos=400-b_radius; //ball is out of vesel (not goal)
if(b_xPos<b_radius) b_xPos=b_radius; //ball is out of vesel (not goal)
if(b_yPos>800-b_radius){
p_point++;
b_yPos=400;
b_xPos=200;
for(int i=0; i<players.length; i++){
players[i].p_goal_init(i);
}
}
if(b_yPos<b_radius){
b_point++;
b_yPos=400;
b_xPos=200;
for(int i=0; i<players.length; i++){
players[i].p_goal_init(i);
}
}
eb_xPos=b_xPos;
eb_yPos=b_yPos;
}
else if(num%2 ==1){
b_xPos-=p_x_kick;
b_yPos-=p_y_kick;
if(b_xPos>400-b_radius) b_xPos=400-b_radius; //ball is out of vesel (not goal)
if(b_xPos<b_radius) b_xPos=b_radius; //ball is out of vesel (not goal)
if(b_yPos>800-b_radius){
p_point++;
b_yPos=400;
b_xPos=200;
for(int i=0; i<players.length; i++){
players[i].p_goal_init(i);
}
}
if(b_yPos<b_radius){
b_point++;
b_yPos=400;
b_xPos=200;
for(int i=0; i<players.length; i++){
players[i].p_goal_init(i);
}
}
eb_xPos=b_xPos;
eb_yPos=b_yPos;
}
}
}
その他
long frame =0;
float eb_xPos;
float eb_yPos;
boolean kicked=false;
int b_point=0;
int p_point=0;
float[] dist;
Player[] players =new Player[22];
Ball ball =new Ball();
void setup() {
size(400, 800);
background(255);
smooth();
noStroke();
fill(0);
frameRate(-60);
dist=new float[players.length];
for (int i = 0; i < players.length; i++) players[i] = new Player();
for (int j = 0; j < players.length; j++) players[j].P_initialize(j);
for (int k = 0; k < players.length; k++)dist[k]=9999.9;
ball=new Ball();
ball.B_initialize();
}
void draw() {
background(255);
frame++;
for (int i = 0; i < players.length; i++) {
dist[i]=9999.9;
players[i].update(i);
players[i].p_kick_hantei(i);
players[i].display(i);
//circles[i].update();
//circles[i].display();
}
ball.display();
kicked=false;
fill(0);
text(frame+"F", 5, 10);
text(nfs(frameRate, 3, 1)+"FPS", 80, 10);
text(p_point+"-"+b_point, 160, 10);
}
##4.1 変数などの説明
#5 改善点
ボールが壁に行きやすい
ボールとプレイヤーがほぼ一直線を描いて、動きにくくなる。
青チームと桃チームの戦力差
↑プレイヤーの人数が少ないときはとりわけ著しくなる。
戦力差はあっても、長時間シミュレーションをしていくと、段々2チームの差は数字に現れなくなる。
↑視野の扇形が一部描写されていませんが、更新処理よるもので、実際にはすべて見えています。↑
#6 感想
少し不満は残ったものの、概ね満足できるものができた。
パラグラフ4で説明している、不具合を解決した場合、
コメントで改善箇所などをコメントしていただくと、
大変嬉しゅうございます。
これは本当に声を大にして「人工知能(AI)」と呼べるのだろうか......
#7 出典
https://processing.org/
↑Processing.orgのリファレンス↑
詳細記事も書ければ書く予定であります。
#8 omake