/* aiplayer.c ** ** code for positioning artifitial intelligence player ** Copyright (C) 2001 Florian Berger ** Email: harpin_floh@yahoo.de, florian.berger@jk.uni-linz.ac.at ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation; ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ** */ #define AIPLAYER_C #include "aiplayer.h" #include "vmath.h" #include #include #include VMvect (*ai_get_stroke_dir)( BallsType * balls, BordersType * walls, struct Player *pplayer ) = ai_get_stroke_dir_8ball; static double ai_skill=1.0; /* not used ...yet */ static double ai_err=0.0; void ai_set_skill( double skill ) /* not used ...yet */ { ai_skill=skill; } void ai_set_err( double err ) { ai_err=err; } static int my_rand(int nr) { return rand()%nr; } static double my_rand01() { return (double)rand()/(double)RAND_MAX; } VMfloat stroke_angle( BallType * bcue, BallType * bhit, HoleType * hole ) { VMvect r_hit; r_hit = vec_scale(vec_unit(vec_diff(bhit->r,hole->aim)),(bcue->d+bhit->d)/2.0); r_hit = vec_add(bhit->r,r_hit); return( vec_angle( vec_diff(r_hit,bcue->r), vec_diff(hole->aim,r_hit) ) ); } int ball_in_way( int ballnr, VMvect aim, BallsType * balls ) { VMvect way, iball; VMfloat par, norm, lway; int inway=0; int i; for(i=0;inr;i++) if( balls->ball[i].in_game && i!=ballnr ){ way = vec_diff(aim,balls->ball[ballnr].r); lway = vec_abs(way); iball = vec_diff(balls->ball[i].r,balls->ball[ballnr].r); par = vec_mul(vec_unit(way),iball); norm = vec_abs(vec_cross(vec_unit(way),iball)); if( par>0.0 && parball[i].d+balls->ball[ballnr].d)/2.0 ){ DPRINTF("ball_in_way:%d (ballnr=%d)\n",i,ballnr); inway=1; break; } } return( inway ); } int ind_ball_nr( int nr, BallsType * balls ) { int i; for( i=0 ; inr ; i++ ){ if( balls->ball[i].nr == nr ) break; } return i; } int nth_in_game( int n, BallsType * balls, int full_half ) { int i; for( i=0 ; inr && n>=0 ; i++ ){ if( full_half == BALL_FULL && balls->ball[i].nr<8 && balls->ball[i].nr>0 ){ n--; } if( full_half == BALL_HALF && balls->ball[i].nr>8 ){ n--; } if( full_half == BALL_ANY && ( balls->ball[i].nr>8 || (balls->ball[i].nr<8 && balls->ball[i].nr>0) )){ n--; } } return i; } VMvect ai_get_stroke_dir_8ball( BallsType * balls, BordersType * walls, struct Player * pplayer ) { #define full_half (pplayer->half_full) VMvect r_hit; VMfloat angle, minangle; BallType *bhit, *bcue; HoleType *hole; int minball; int minhole; int i,j; minball=0; minhole=-1; minangle=M_PI; bcue = &balls->ball[0]; for( i=1; inr; i++ ) if ( balls->ball[i].in_game ){ if( ( full_half==BALL_HALF && balls->ball[i].nr>8 ) || ( full_half==BALL_FULL && balls->ball[i].nr<8 ) || ( full_half==BALL_ANY && balls->ball[i].nr!=8 ) || ( balls->ball[i].nr==8 && balls_in_game(balls,full_half)==0 ) ){ bhit = &balls->ball[i]; for( j=0; jholenr; j++ ){ hole = &walls->hole[j]; r_hit = vec_scale(vec_unit(vec_diff(bhit->r,hole->aim)),(bcue->d+bhit->d)/2.0); r_hit = vec_add(bhit->r,r_hit); if( !ball_in_way(0,r_hit,balls) && !ball_in_way(i,hole->aim,balls) ){ angle = fabs( vec_angle( vec_diff(r_hit,bcue->r), vec_diff(hole->aim,r_hit) ) ); if( angleball[i].nr,minhole); } } } } } DPRINTF("aiplayer: 1\n"); if( minball==0 ){ /* no proper ball found */ minball=0; if (full_half==BALL_FULL){ if( BM_get_balls_out_full()!=7 ){ minball=1+my_rand(7-BM_get_balls_out_full()); } } else if (full_half==BALL_HALF){ if( BM_get_balls_out_half()!=7 ){ minball=1+my_rand(7-BM_get_balls_out_half()); } } else if (full_half==BALL_ANY){ if( BM_get_balls_out_total()!=15 ){ minball=1+my_rand(15-BM_get_balls_out_total()); } } if( minball==0 ){ minball = ind_ball_nr(8,balls); } else { minball = nth_in_game(minball,balls,full_half); } } DPRINTF("aiplayer: 2\n"); bhit = &balls->ball[minball]; if(minhole!=-1){ hole = &walls->hole[minhole]; r_hit = vec_scale(vec_unit(vec_diff(bhit->r,hole->aim)),(bcue->d+bhit->d)/2.0); r_hit = vec_diff(vec_add(bhit->r,r_hit),bcue->r); } else { /* no proper ball found */ DPRINTF("aiplayer: no proper ball found\n"); r_hit = vec_diff(bhit->r,bcue->r); } r_hit=vec_add(r_hit,vec_scale(vec_xyz(my_rand01()-0.5,my_rand01()-0.5,my_rand01()-0.5),0.02*ai_err/vec_abs(r_hit))); return vec_unit(r_hit); #undef full_half } VMvect ai_get_stroke_dir_9ball( BallsType * balls, BordersType * walls, struct Player * pplayer ) { VMvect r_hit; VMfloat angle, minangle; BallType *bhit, *bcue; HoleType *hole; int minind,minnr; int minhole; int i,j; minnr=15; minind=0; minhole=-1; minangle=M_PI; bcue = &balls->ball[0]; for( i=1; inr; i++ ){ if ( balls->ball[i].in_game && balls->ball[i].nrball[i].nr; minind = i; } } bhit = &balls->ball[minind]; for( j=0; jholenr; j++ ){ hole = &walls->hole[j]; r_hit = vec_scale(vec_unit(vec_diff(bhit->r,hole->aim)),(bcue->d+bhit->d)/2.0); r_hit = vec_add(bhit->r,r_hit); if( !ball_in_way(0,r_hit,balls) && !ball_in_way(minind,hole->aim,balls) ){ angle = fabs( vec_angle( vec_diff(r_hit,bcue->r), vec_diff(hole->aim,r_hit) ) ); if( angleaim,balls)); } } if(minhole!=-1){ hole = &walls->hole[minhole]; r_hit = vec_scale(vec_unit(vec_diff(bhit->r,hole->aim)),(bcue->d+bhit->d)/2.0); r_hit = vec_diff(vec_add(bhit->r,r_hit),bcue->r); } else { /* no proper ball found */ DPRINTF("aiplayer: no proper hole found\n"); r_hit = vec_diff(bhit->r,bcue->r); } r_hit=vec_add(r_hit,vec_scale(vec_xyz(my_rand01()-0.5,my_rand01()-0.5,my_rand01()-0.5),0.02*ai_err*vec_abs(r_hit))); return vec_unit(r_hit); } #define IS_RED(x) ( x==1 || x>=8 ) int snooker_ball_legal(int ball,struct Player *player) { return ((IS_RED(ball) && player->snooker_on_red) ||(!IS_RED(ball) && !player->snooker_on_red && player->snooker_next_color<=1) ||(player->snooker_on_red==0 && ball==player->snooker_next_color && player->snooker_next_color>1)); } #define CUE_OBJ_WEIGHT 0.2 #define OBJ_HOLE_WEIGHT 0.7 #define POINT_WEIGHT 0.3 #define CENTERHOLE_ANGLE_WEIGHT 1 #define CORNERHOLE_ANGLE_WEIGHT 1 #define ANGLE_WEIGHT 1 VMvect ai_get_stroke_dir_snooker( BallsType * balls, BordersType * walls, struct Player * pplayer ) { //return vec_xyz(1,1,0);; VMvect r_hit,hole_aim,hole_aim_def,min_r_hit; VMfloat angle, minweight,hole_angle,weight,corner_acentrism; BallType *bhit, *bcue; HoleType *hole; int minball; int minhole; int i,j; int legal_ball=1; fprintf(stderr,"aiplayer: start player[%s]\n",pplayer->name); minball=0; minhole=-1; minweight=12; bcue = &balls->ball[0]; for( i=balls->nr-1;i>=1; i-- ) if ( balls->ball[i].in_game ){ if(snooker_ball_legal(i,pplayer)) { bhit = &balls->ball[i]; for( j=0; jholenr; j++ ){ hole = &walls->hole[j]; if(j>1) { hole_aim=hole->aim; if(hole_aim.x>0) hole_aim.x-=0.007; if(hole_aim.x<0) hole_aim.x+=0.007; if(hole_aim.y>0) hole_aim.y-=0.007; if(hole_aim.y<0) hole_aim.y+=0.007; } else { hole_aim=vec_scale(hole->aim,1.02); } r_hit = vec_scale(vec_unit(vec_diff(bhit->r,hole_aim)),(bcue->d+bhit->d)/2.0); r_hit = vec_add(bhit->r,r_hit); if( !ball_in_way(0,r_hit,balls) && !ball_in_way(i,hole_aim,balls) ){ angle = fabs( vec_angle( vec_diff(r_hit,bcue->r), vec_diff(hole_aim,r_hit) ) ); hole_angle = vec_angle(vec_diff(hole_aim,bhit->r),vec_diff(vec_xyz(2*hole_aim.x,hole_aim.y,0),hole_aim)); corner_acentrism=fabs(hole_angle-M_PI/4); /*fprintf(stderr,"aiplayer: ball:%d hole:%d raw hole_angle %f\n",balls->ball[i].nr,j, hole_angle*180/M_PI);*/ /* normalized to give an unweighted maximum of +- 2.5 per item */ weight = 0.64 * ANGLE_WEIGHT * pow(angle,3) +(j<=1)*0.64 * CENTERHOLE_ANGLE_WEIGHT * pow(hole_angle,3) +(j>1) * 5.0 * CORNERHOLE_ANGLE_WEIGHT * pow(corner_acentrism,3) +0.04 * OBJ_HOLE_WEIGHT * vec_abssq(vec_diff(bhit->r,hole_aim)) +0.15 * CUE_OBJ_WEIGHT * vec_abs(vec_diff(bhit->r,bcue->r)) -1.5 * POINT_WEIGHT *(i>=2||i<=7?i-2:0) ; if(weight nr && minball==0;i++) if ( balls->ball[i].in_game ){ for( i=balls->nr-1;i>=1 && minball==0;i--) if ( balls->ball[i].in_game ){ if(snooker_ball_legal(i,pplayer)) { VMfloat d; for(d=-(balls->ball[0].d-0.001);d<(balls->ball[0].d-0.001) && minball==0;d+=0.005) { VMvect offset,new_r_hit; bhit = &balls->ball[i]; r_hit = vec_diff(bhit->r,bcue->r); /*r_hit=vec_rotate(r_hit,vec_xyz(0,0,asin(d/vec_abs(r_hit))));*/ offset=vec_scale(vec_unit(vec_rotate(r_hit,vec_xyz(0,0,M_PI/2))),d); new_r_hit = vec_diff(vec_add(bhit->r,offset),bcue->r); new_r_hit = vec_diff(new_r_hit,vec_scale(vec_unit(new_r_hit),balls->ball[i].d/1.99)); if( !ball_in_way(0,vec_add(balls->ball[0].r,new_r_hit),balls)) { fprintf(stderr,"ball found\n"); minball=i; min_r_hit=new_r_hit; } } } } } if( minball==0 ) /* no legal ball found, just play anything */ { legal_ball=0; minball=0; if (pplayer->snooker_on_red) { minball=2+my_rand(6); } else if (pplayer->snooker_on_red==0 && pplayer->snooker_next_color<2) { minball=7+my_rand(15); if(minball==21) minball=1; } else { minball=pplayer->snooker_next_color; } } /*fprintf(stderr,"aiplayer: 2\n");*/ bhit = &balls->ball[minball]; if(minhole!=-1){ hole = &walls->hole[minhole]; r_hit = vec_scale(vec_unit(vec_diff(bhit->r,hole_aim_def)),(bcue->d+bhit->d)/2.0); r_hit = vec_diff(vec_add(bhit->r,r_hit),bcue->r); } else { /* no proper ball found */ if(legal_ball==0) { fprintf(stderr,"aiplayer: no proper ball found\n"); r_hit = vec_diff(bhit->r,bcue->r); } else { fprintf(stderr,"aiplayer: can hit legal ball : %d\n",minball); r_hit=min_r_hit; } } r_hit=vec_add(r_hit,vec_scale(vec_xyz(my_rand01()-0.5,my_rand01()-0.5,my_rand01()-0.5),0.02*ai_err/vec_abs(r_hit))); fprintf(stderr,"aiplayer: done\n"); return vec_unit(r_hit); } #undef IS_RED VMvect ai_get_stroke_dir_carambol( BallsType * balls, BordersType * walls, struct Player * pplayer ) { #define CUE_BALL_IND (pplayer->cue_ball) int i,j,k, foundshot; VMvect wc; /* way of cueball to objectball 1 */ VMvect w12; /* way of cueball from objectball 1 to objectball 2 */ VMvect d12; /* vec from objectball 1 to objectball 2 */ VMvect p1; /* vec from ball1 to cueball at hit */ VMvect min_wc; double min_angle; double th; foundshot=0; min_angle=M_PI; for(i=0;inr;i++) if(i!=CUE_BALL_IND){ /* i = objectball 1 */ for( j=0 ; jnr ; j++ ){ /* j = objectball 2 */ if(j!=CUE_BALL_IND && j!=i) break; } DPRINTF("ai_get_stroke_dir_carambol:i=%d,j=%d\n",i,j); d12 = vec_diff( balls->ball[j].r, balls->ball[i].r ); th = acos(BALL_D/vec_abs(d12)); for(k=0;k<2;k++){ /* the two possible tangents */ p1 = vec_scale( vec_unit( vec_rotate(d12,vec_xyz(0,0,(k==0)?th:-th)) ), BALL_D ); wc = vec_diff( vec_add(balls->ball[i].r,p1), balls->ball[CUE_BALL_IND].r ); w12 = vec_diff( balls->ball[j].r, vec_add(balls->ball[i].r,p1) ); if( vec_mul(wc,w12)>0.0 && vec_mul(wc,p1)<0.0 ){ /* conditions for possible shot */ foundshot=1; if( vec_angle( wc, w12 ) < min_angle ){ min_angle = vec_angle( wc, w12 ); min_wc = wc; } } } } if(foundshot){ return vec_unit(min_wc); } else { return vec_unit( vec_diff(balls->ball[(CUE_BALL_IND+1+(rand()%2))%3].r, balls->ball[CUE_BALL_IND].r) ); } #undef CUE_BALL_IND } void setfunc_ai_get_stroke_dir(VMvect (*func)( BallsType * balls, BordersType * walls, struct Player * pplayer )) { ai_get_stroke_dir=func; }