2
0

GeneticAlgorithm_BoardWarSimulation.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. //
  2. // Genetic Algorithm Example
  3. // Strategy board game war. Units(2-blue) try to capture the target(4-white/yellow) objective
  4. // and eliminate the enemy units(3-red)
  5. //
  6. // A random movement phase is executed. Here a random spot is selected on the map
  7. // if there is a movable unit than this is moved in any direction. If the new position
  8. // is a enemy than this enemy is taken out. The objective can also be captured.
  9. //
  10. // There are MAX_CODE of moves per set. Then a score is calculated based on how
  11. // close the units are to the GOAL(4) and how many enemies are destroyed(3) and if
  12. // the GOAL(4) has been captured.
  13. // There are MAX_ARC amount of moves per set stored. The top 3 scoring runs get
  14. // copied and these copies get mutated so they might score better. Also a number
  15. // of random new runs are added.
  16. //
  17. // The process of simulating the moves and scoring and mutating is done MAX_RUNS amount of times.
  18. // The final top scoring set of moves is played on the canvas.
  19. //
  20. // Better scoring could be done by storing the amount of move taken to take out x enemy units and capturing target.
  21. //
  22. // Building on this you might change the amount of units for each side. maybe mod the amount of goals to be captured.
  23. // In a real game you might use this to simulate if a war for a ai player would be doable in a certain tested setting like this.
  24. //
  25. // Create unit types with different att and def, more scoring with 1+units attacking at the same time.
  26. // add scoring in the execute script stage : units stay close to each other, direct path to target etc.
  27. // If move on enemy position (chance of being destroyed itself(.5) .25 if more units of own side are nearby)
  28. #include "raylib.h"
  29. #include <math.h>
  30. #define MAX_RUNS 150
  31. #define MAX_CODE 1500
  32. #define MAX_ARC 64
  33. enum terrain{TREE=1,GROUND=0,AIPLAYER1=2,AIPLAYER2=3,GOAL=4,GOALREACHED=5};
  34. enum flag{UP,DOWN,LEFT,RIGHT,WAIT};
  35. //1=border,2=ai unit,3=defensiveunit,4=objective
  36. int map1[10][10] = { {1,1,1,1,1,1,1,1,1,1},
  37. {1,0,0,0,0,0,0,0,0,1},
  38. {1,0,0,0,0,0,3,4,0,1},
  39. {1,0,0,3,0,0,0,0,0,1},
  40. {1,0,0,0,0,0,3,0,0,1},
  41. {1,2,0,0,0,0,0,3,0,1},
  42. {1,0,2,0,0,0,0,0,0,1},
  43. {1,0,2,2,0,0,0,0,0,1},
  44. {1,0,0,0,0,0,0,0,0,1},
  45. {1,1,1,1,1,1,1,1,1,1}
  46. };
  47. int map[10][10] = {0};
  48. typedef struct code{
  49. Vector2 position;
  50. Vector2 move;
  51. }code;
  52. static struct code arr_code[MAX_CODE];
  53. typedef struct codearc{
  54. Vector2 position[MAX_CODE];
  55. Vector2 move[MAX_CODE];
  56. int score;
  57. }codearc;
  58. static struct codearc arr_codearc[MAX_ARC];
  59. static struct codearc arr_temparc[MAX_ARC];
  60. int getscore();
  61. void executescript();
  62. void storescript(int z,int score);
  63. void sortarc();//copy top 3 to max-5
  64. void mutateandnew();
  65. void geneticalgorithm();
  66. static float distance(float x1,float y1,float x2,float y2);
  67. static float edistance(float x1,float y1,float x2,float y2);
  68. int screenWidth;
  69. int screenHeight;
  70. int tileWidth,tileHeight,mapWidth,mapHeight;
  71. int main(void)
  72. {
  73. // Initialization
  74. //--------------------------------------------------------------------------------------
  75. screenWidth = 800;
  76. screenHeight = 600;
  77. mapWidth = 10;
  78. mapHeight = 10;
  79. tileWidth = ceil((float)screenWidth/(float)mapWidth);
  80. tileHeight = ceil((float)screenHeight/(float)mapHeight);
  81. InitWindow(screenWidth, screenHeight, "raylib example.");
  82. SetTargetFPS(120); // Set our game to run at 60 frames-per-second
  83. //--------------------------------------------------------------------------------------
  84. geneticalgorithm();
  85. int playposition=0;
  86. // Main game loop
  87. while (!WindowShouldClose()) // Detect window close button or ESC key
  88. {
  89. // Update
  90. //----------------------------------------------------------------------------------
  91. if(IsKeyPressed(KEY_SPACE)|| playposition>MAX_CODE-2){
  92. geneticalgorithm();
  93. playposition=0;
  94. }
  95. if(playposition==0){
  96. // restore map
  97. for(int y=0;y<10;y++){
  98. for(int x=0;x<10;x++){
  99. map[y][x]=map1[y][x];
  100. }}
  101. }
  102. // execute script
  103. if(map[ (int)arr_code[playposition].position.y ][ (int)arr_code[playposition].position.x ]==AIPLAYER1){
  104. Vector2 p=arr_code[playposition].position;
  105. Vector2 m=arr_code[playposition].move;
  106. Vector2 np=(Vector2){p.x+m.x,p.y+m.y};
  107. if(map[ (int)np.y ][ (int)np.x ]==GOAL){
  108. map[ (int)np.y ][ (int)np.x ] = GOALREACHED;
  109. }
  110. if(map[ (int)np.y ][ (int)np.x ]==GROUND){
  111. map[ (int)np.y ][ (int)np.x ] = AIPLAYER1;
  112. map[ (int)p.y ][ (int)p.x ] = GROUND;
  113. }
  114. if(map[ (int)np.y ][ (int)np.x ]==AIPLAYER2){
  115. map[ (int)np.y ][ (int)np.x ] = AIPLAYER1;
  116. map[ (int)p.y ][ (int)p.x ] = GROUND;
  117. }
  118. }
  119. playposition+=1;
  120. if(playposition>MAX_CODE-1){
  121. playposition=0;
  122. }
  123. //----------------------------------------------------------------------------------
  124. // Draw
  125. //----------------------------------------------------------------------------------
  126. BeginDrawing();
  127. ClearBackground(RAYWHITE);
  128. //drawmap
  129. for(int y=0;y<10;y++){
  130. for(int x =0;x<10;x++){
  131. int m=map[y][x];
  132. if(m==TREE){
  133. DrawRectangle(x*tileWidth,y*tileHeight,tileWidth,tileHeight,(Color){40,90,20,255});
  134. DrawCircle(x*tileWidth+tileWidth/1.5,y*tileHeight+tileHeight/1.5,tileWidth/5,(Color){20,50,20,255});
  135. DrawRectangle(x*tileWidth+tileWidth/2.2,y*tileHeight+tileHeight/2,tileWidth/8,tileHeight/3,BROWN);
  136. DrawCircle(x*tileWidth+tileWidth/2,y*tileHeight+tileHeight/3,tileWidth/4,(Color){120,250,20,255});
  137. DrawCircle(x*tileWidth+tileWidth/2.2,y*tileHeight+tileHeight/4,tileWidth/9,(Color){220,255,220,155});
  138. }
  139. if(m==GROUND){
  140. DrawRectangle(x*tileWidth,y*tileHeight,tileWidth,tileHeight,DARKGREEN);
  141. DrawRectangle(x*tileWidth+5,y*tileHeight+10,2,1,GREEN);
  142. DrawRectangle(x*tileWidth+tileWidth/6,y*tileHeight+tileHeight/6,2,1,GREEN);
  143. DrawRectangle(x*tileWidth+tileWidth/1.5,y*tileHeight+tileHeight/1.5,2,1,GREEN);
  144. DrawRectangle(x*tileWidth+tileWidth/2,y*tileHeight+tileHeight/2,2,1,GREEN);
  145. }
  146. if(m==AIPLAYER1){
  147. DrawRectangle(x*tileWidth,y*tileHeight,tileWidth,tileHeight,BLUE);
  148. DrawText("AI_Player",x*tileWidth,y*tileHeight+tileHeight/4,16,BLACK);
  149. }
  150. if(m==AIPLAYER2){
  151. DrawRectangle(x*tileWidth,y*tileHeight,tileWidth,tileHeight,RED);
  152. DrawText("F",x*tileWidth+tileWidth/3,y*tileHeight+tileHeight/4,40,WHITE);
  153. }
  154. if(m==GOAL){
  155. DrawRectangle(x*tileWidth,y*tileHeight,tileWidth,tileHeight,WHITE);
  156. DrawText("City",x*tileWidth+tileWidth/4,y*tileHeight+tileHeight/4,26,BLACK);
  157. }
  158. if(m==GOALREACHED){
  159. DrawRectangle(x*tileWidth,y*tileHeight,tileWidth,tileHeight,YELLOW);
  160. DrawText("Captured",x*tileWidth+4,y*tileHeight+tileHeight/4,16,BLACK);
  161. }
  162. }
  163. }
  164. DrawText("Press space for new simulation.(Autorun=on)",10,10,26,BLACK);
  165. DrawText("Press space for new simulation.(Autorun=on)",9,9,26,WHITE);
  166. /*
  167. int c=0;
  168. for(int y=0;y<40;y++){
  169. for(int x=0;x<5;x++){
  170. if(c<MAX_ARC){
  171. DrawText(FormatText("%i",arr_codearc[c].score),x*200,y*20,20,BLACK);
  172. }
  173. c++;
  174. }}
  175. */
  176. EndDrawing();
  177. //----------------------------------------------------------------------------------
  178. }
  179. // De-Initialization
  180. //--------------------------------------------------------------------------------------
  181. CloseWindow(); // Close window and OpenGL context
  182. //--------------------------------------------------------------------------------------
  183. return 0;
  184. }
  185. void geneticalgorithm(){
  186. for(int z=0;z<MAX_ARC;z++){
  187. //create random script
  188. for(int i=0;i<MAX_CODE;i++){
  189. arr_code[i].position=(Vector2){GetRandomValue(0,10),GetRandomValue(0,10)};
  190. arr_code[i].move=(Vector2){GetRandomValue(-1,1),GetRandomValue(-1,1)};
  191. }
  192. // restore map
  193. for(int y=0;y<10;y++){
  194. for(int x=0;x<10;x++){
  195. map[y][x]=map1[y][x];
  196. }}
  197. executescript();
  198. // SCORING!
  199. int score = getscore();
  200. // store script
  201. storescript(z,score);
  202. }
  203. //keep top 3
  204. // copy top 3 to max-5
  205. //mutate 3..max-5
  206. //new random max-5 max
  207. //
  208. for(int t=0;t<MAX_RUNS;t++){
  209. sortarc();
  210. mutateandnew();
  211. for(int z=0;z<MAX_ARC;z++){
  212. // simulate new
  213. // restore map
  214. for(int y=0;y<10;y++){
  215. for(int x=0;x<10;x++){
  216. map[y][x]=map1[y][x];
  217. }}
  218. getscript(z);
  219. executescript();
  220. // SCORING!
  221. int score = getscore();
  222. arr_codearc[z].score = score;
  223. // store script
  224. //storescript(z,score);
  225. }
  226. }
  227. // Final set and display
  228. // restore map
  229. for(int y=0;y<10;y++){
  230. for(int x=0;x<10;x++){
  231. map[y][x]=map1[y][x];
  232. }}
  233. getscript(0);
  234. executescript();
  235. }
  236. void mutateandnew(){
  237. //mutate 3 to max-5
  238. // This is from a starting point from %10 to %50 to end
  239. for(int z=3;z<MAX_ARC-5;z++){
  240. int start=GetRandomValue(MAX_CODE/4,MAX_CODE/1.5);
  241. for(int i=start;i<MAX_CODE;i++){
  242. arr_codearc[z].position[i]=(Vector2){GetRandomValue(0,10),GetRandomValue(0,10)};
  243. arr_codearc[z].move[i]=(Vector2){GetRandomValue(-1,1),GetRandomValue(-1,1)};
  244. }
  245. }
  246. // max-5 to max is new ones
  247. for(int z=MAX_ARC-5;z<MAX_ARC;z++){
  248. for(int i=0;i<MAX_CODE;i++){
  249. arr_codearc[z].position[i]=(Vector2){GetRandomValue(0,10),GetRandomValue(0,10)};
  250. arr_codearc[z].move[i]=(Vector2){GetRandomValue(-1,1),GetRandomValue(-1,1)};
  251. }
  252. }
  253. }
  254. void sortarc(){
  255. int top3[3] = {0};
  256. int startval=800;
  257. int currentpos=0;
  258. // get top 3 in list top3
  259. while(startval>0 && currentpos<3){
  260. for(int i=0;i<MAX_ARC;i++){
  261. if(arr_codearc[i].score==startval){
  262. if(currentpos<3){
  263. top3[currentpos]=i;
  264. }
  265. currentpos++;
  266. }
  267. }
  268. startval--;
  269. }
  270. // copy the 3 tops into temp arc
  271. for(int i=0;i<MAX_CODE;i++){
  272. for(int j=0;j<3;j++){
  273. arr_temparc[j].position[i] = arr_codearc[top3[j]].position[i];
  274. arr_temparc[j].move[i] = arr_codearc[top3[j]].move[i];
  275. arr_temparc[j].score = arr_codearc[top3[j]].score;
  276. }
  277. }
  278. // copy top up to max-5
  279. for(int i=0;i<MAX_ARC-8;i+=3){
  280. for(int k=0;k<3;k++){
  281. for(int j=0;j<MAX_CODE;j++){
  282. arr_codearc[i+k].position[j] = arr_temparc[k].position[j];
  283. arr_codearc[i+k].move[j] = arr_temparc[k].move[j];
  284. arr_codearc[i+k].score = arr_temparc[k].score;
  285. }
  286. }
  287. }
  288. }
  289. void getscript(int z){
  290. // store script
  291. //arr_codearc[z].score = score;
  292. //arr_code.score =
  293. for(int i=0;i<MAX_CODE;i++){
  294. //arr_codearc[z].move[i] = arr_code[i].move;
  295. //arr_codearc[z].position[i] = arr_code[i].position;
  296. arr_code[i].move = arr_codearc[z].move[i];
  297. arr_code[i].position = arr_codearc[z].position[i];
  298. }
  299. }
  300. void storescript(int z,int score){
  301. // store script
  302. arr_codearc[z].score = score;
  303. for(int i=0;i<MAX_CODE;i++){
  304. arr_codearc[z].move[i] = arr_code[i].move;
  305. arr_codearc[z].position[i] = arr_code[i].position;
  306. }
  307. }
  308. int getscore(){
  309. int score=0;
  310. //count enemies left
  311. int numenemiesleft=0;
  312. for(int y=0;y<10;y++){
  313. for(int x=0;x<10;x++){
  314. if(map[y][x]==AIPLAYER2)numenemiesleft++;
  315. }
  316. }
  317. //was the goal taken
  318. bool objectivesuccess = false;
  319. Vector2 goalposition;
  320. for(int y=0;y<10;y++){
  321. for(int x=0;x<10;x++){
  322. if(map[y][x]==GOALREACHED)objectivesuccess=true;
  323. if(map[y][x]==GOAL || map[y][x]==GOALREACHED){
  324. goalposition.x = x;
  325. goalposition.y = y;
  326. }
  327. }
  328. }
  329. //count aiplayer1 and distance to target
  330. float avdist=0;
  331. int nums=0;
  332. int distanceunit[4]={0};
  333. for(int y=0;y<10;y++){
  334. for(int x=0;x<10;x++){
  335. if(map[y][x]==AIPLAYER1){
  336. int d = 0;
  337. d=edistance((float)goalposition.x,(float)goalposition.y,(float)x,(float)y)*2;
  338. distanceunit[nums]=(int)d;
  339. avdist+=d;
  340. nums++;
  341. }
  342. }}
  343. avdist=avdist/(float)4;
  344. score = (100-(numenemiesleft*25))*2;
  345. score+=(50-(int)avdist);
  346. if(objectivesuccess==true)score+=50;
  347. // if every unit is close then extra score
  348. bool allclose=true;
  349. for(int i=0;i<4;i++){
  350. if(distanceunit[i]>4)allclose=false;
  351. }
  352. if(allclose==true)score+=100;
  353. //score=avdist;
  354. if(score<0)score=9999;
  355. return score;
  356. }
  357. void executescript(){
  358. // execute script
  359. for(int i=0;i<MAX_CODE;i++){
  360. if(map[ (int)arr_code[i].position.y ][ (int)arr_code[i].position.x ]==AIPLAYER1){
  361. Vector2 p=arr_code[i].position;
  362. Vector2 m=arr_code[i].move;
  363. Vector2 np=(Vector2){p.x+m.x,p.y+m.y};
  364. if(map[ (int)np.y ][ (int)np.x ]==GOAL){
  365. map[ (int)np.y ][ (int)np.x ] = GOALREACHED;
  366. }
  367. if(map[ (int)np.y ][ (int)np.x ]==GROUND){
  368. map[ (int)np.y ][ (int)np.x ] = AIPLAYER1;
  369. map[ (int)p.y ][ (int)p.x ] = GROUND;
  370. }
  371. if(map[ (int)np.y ][ (int)np.x ]==AIPLAYER2){
  372. map[ (int)np.y ][ (int)np.x ] = AIPLAYER1;
  373. map[ (int)p.y ][ (int)p.x ] = GROUND;
  374. }
  375. }
  376. }
  377. }
  378. // Manhattan Distance (less precise)
  379. float distance(float x1,float y1,float x2,float y2){
  380. return (float)abs(x2-x1)+abs(y2-y1);
  381. }
  382. // Euclidean distance (more precise)
  383. float edistance(float x1,float y1,float x2,float y2){
  384. return sqrt( (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2) );
  385. }