GeneticAlgorithm_BoardWarSimulation.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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. #include "raylib.h"
  28. #include <math.h>
  29. #define MAX_RUNS 150
  30. #define MAX_CODE 1500
  31. #define MAX_ARC 64
  32. enum terrain{TREE=1,GROUND=0,AIPLAYER1=2,AIPLAYER2=3,GOAL=4,GOALREACHED=5};
  33. enum flag{UP,DOWN,LEFT,RIGHT,WAIT};
  34. //1=border,2=ai unit,3=defensiveunit,4=objective
  35. int map1[10][10] = { {1,1,1,1,1,1,1,1,1,1},
  36. {1,0,0,0,0,0,0,0,0,1},
  37. {1,0,0,0,0,0,3,4,0,1},
  38. {1,0,0,3,0,0,0,0,0,1},
  39. {1,0,0,0,0,0,3,0,0,1},
  40. {1,2,0,0,0,0,0,3,0,1},
  41. {1,0,2,0,0,0,0,0,0,1},
  42. {1,0,2,2,0,0,0,0,0,1},
  43. {1,0,0,0,0,0,0,0,0,1},
  44. {1,1,1,1,1,1,1,1,1,1}
  45. };
  46. int map[10][10] = {0};
  47. typedef struct code{
  48. Vector2 position;
  49. Vector2 move;
  50. }code;
  51. static struct code arr_code[MAX_CODE];
  52. typedef struct codearc{
  53. Vector2 position[MAX_CODE];
  54. Vector2 move[MAX_CODE];
  55. int score;
  56. }codearc;
  57. static struct codearc arr_codearc[MAX_ARC];
  58. static struct codearc arr_temparc[MAX_ARC];
  59. int getscore();
  60. void executescript();
  61. void storescript(int z,int score);
  62. void sortarc();//copy top 3 to max-5
  63. void mutateandnew();
  64. void geneticalgorithm();
  65. static float distance(float x1,float y1,float x2,float y2);
  66. static float edistance(float x1,float y1,float x2,float y2);
  67. int screenWidth;
  68. int screenHeight;
  69. int tileWidth,tileHeight,mapWidth,mapHeight;
  70. int main(void)
  71. {
  72. // Initialization
  73. //--------------------------------------------------------------------------------------
  74. screenWidth = 800;
  75. screenHeight = 600;
  76. mapWidth = 10;
  77. mapHeight = 10;
  78. tileWidth = ceil((float)screenWidth/(float)mapWidth);
  79. tileHeight = ceil((float)screenHeight/(float)mapHeight);
  80. InitWindow(screenWidth, screenHeight, "raylib example.");
  81. SetTargetFPS(120); // Set our game to run at 60 frames-per-second
  82. //--------------------------------------------------------------------------------------
  83. geneticalgorithm();
  84. int playposition=0;
  85. // Main game loop
  86. while (!WindowShouldClose()) // Detect window close button or ESC key
  87. {
  88. // Update
  89. //----------------------------------------------------------------------------------
  90. if(IsKeyPressed(KEY_SPACE)|| playposition>MAX_CODE-2){
  91. geneticalgorithm();
  92. playposition=0;
  93. }
  94. if(playposition==0){
  95. // restore map
  96. for(int y=0;y<10;y++){
  97. for(int x=0;x<10;x++){
  98. map[y][x]=map1[y][x];
  99. }}
  100. }
  101. // execute script
  102. if(map[ (int)arr_code[playposition].position.y ][ (int)arr_code[playposition].position.x ]==AIPLAYER1){
  103. Vector2 p=arr_code[playposition].position;
  104. Vector2 m=arr_code[playposition].move;
  105. Vector2 np=(Vector2){p.x+m.x,p.y+m.y};
  106. if(map[ (int)np.y ][ (int)np.x ]==GOAL){
  107. map[ (int)np.y ][ (int)np.x ] = GOALREACHED;
  108. }
  109. if(map[ (int)np.y ][ (int)np.x ]==GROUND){
  110. map[ (int)np.y ][ (int)np.x ] = AIPLAYER1;
  111. map[ (int)p.y ][ (int)p.x ] = GROUND;
  112. }
  113. if(map[ (int)np.y ][ (int)np.x ]==AIPLAYER2){
  114. map[ (int)np.y ][ (int)np.x ] = AIPLAYER1;
  115. map[ (int)p.y ][ (int)p.x ] = GROUND;
  116. }
  117. }
  118. playposition+=1;
  119. if(playposition>MAX_CODE-1){
  120. playposition=0;
  121. }
  122. //----------------------------------------------------------------------------------
  123. // Draw
  124. //----------------------------------------------------------------------------------
  125. BeginDrawing();
  126. ClearBackground(RAYWHITE);
  127. //drawmap
  128. for(int y=0;y<10;y++){
  129. for(int x =0;x<10;x++){
  130. int m=map[y][x];
  131. if(m==TREE){
  132. DrawRectangle(x*tileWidth,y*tileHeight,tileWidth,tileHeight,GREEN);
  133. }
  134. if(m==GROUND){
  135. DrawRectangle(x*tileWidth,y*tileHeight,tileWidth,tileHeight,DARKGREEN);
  136. }
  137. if(m==AIPLAYER1){
  138. DrawRectangle(x*tileWidth,y*tileHeight,tileWidth,tileHeight,BLUE);
  139. }
  140. if(m==AIPLAYER2){
  141. DrawRectangle(x*tileWidth,y*tileHeight,tileWidth,tileHeight,RED);
  142. }
  143. if(m==GOAL){
  144. DrawRectangle(x*tileWidth,y*tileHeight,tileWidth,tileHeight,WHITE);
  145. }
  146. if(m==GOALREACHED){
  147. DrawRectangle(x*tileWidth,y*tileHeight,tileWidth,tileHeight,YELLOW);
  148. }
  149. }
  150. }
  151. DrawText("Press space for new simulation.",10,10,20,BLACK);
  152. /*
  153. int c=0;
  154. for(int y=0;y<40;y++){
  155. for(int x=0;x<5;x++){
  156. if(c<MAX_ARC){
  157. DrawText(FormatText("%i",arr_codearc[c].score),x*200,y*20,20,BLACK);
  158. }
  159. c++;
  160. }}
  161. */
  162. EndDrawing();
  163. //----------------------------------------------------------------------------------
  164. }
  165. // De-Initialization
  166. //--------------------------------------------------------------------------------------
  167. CloseWindow(); // Close window and OpenGL context
  168. //--------------------------------------------------------------------------------------
  169. return 0;
  170. }
  171. void geneticalgorithm(){
  172. for(int z=0;z<MAX_ARC;z++){
  173. //create random script
  174. for(int i=0;i<MAX_CODE;i++){
  175. arr_code[i].position=(Vector2){GetRandomValue(0,10),GetRandomValue(0,10)};
  176. arr_code[i].move=(Vector2){GetRandomValue(-1,1),GetRandomValue(-1,1)};
  177. }
  178. // restore map
  179. for(int y=0;y<10;y++){
  180. for(int x=0;x<10;x++){
  181. map[y][x]=map1[y][x];
  182. }}
  183. executescript();
  184. // SCORING!
  185. int score = getscore();
  186. // store script
  187. storescript(z,score);
  188. }
  189. //keep top 3
  190. // copy top 3 to max-5
  191. //mutate 3..max-5
  192. //new random max-5 max
  193. //
  194. for(int t=0;t<MAX_RUNS;t++){
  195. sortarc();
  196. mutateandnew();
  197. for(int z=0;z<MAX_ARC;z++){
  198. // simulate new
  199. // restore map
  200. for(int y=0;y<10;y++){
  201. for(int x=0;x<10;x++){
  202. map[y][x]=map1[y][x];
  203. }}
  204. getscript(z);
  205. executescript();
  206. // SCORING!
  207. int score = getscore();
  208. arr_codearc[z].score = score;
  209. // store script
  210. //storescript(z,score);
  211. }
  212. }
  213. // Final set and display
  214. // restore map
  215. for(int y=0;y<10;y++){
  216. for(int x=0;x<10;x++){
  217. map[y][x]=map1[y][x];
  218. }}
  219. getscript(0);
  220. executescript();
  221. }
  222. void mutateandnew(){
  223. //mutate 3 to max-5
  224. // This is from a starting point from %10 to %50 to end
  225. for(int z=3;z<MAX_ARC-5;z++){
  226. int start=GetRandomValue(MAX_CODE/4,MAX_CODE/1.5);
  227. for(int i=start;i<MAX_CODE;i++){
  228. arr_codearc[z].position[i]=(Vector2){GetRandomValue(0,10),GetRandomValue(0,10)};
  229. arr_codearc[z].move[i]=(Vector2){GetRandomValue(-1,1),GetRandomValue(-1,1)};
  230. }
  231. }
  232. // max-5 to max is new ones
  233. for(int z=MAX_ARC-5;z<MAX_ARC;z++){
  234. for(int i=0;i<MAX_CODE;i++){
  235. arr_codearc[z].position[i]=(Vector2){GetRandomValue(0,10),GetRandomValue(0,10)};
  236. arr_codearc[z].move[i]=(Vector2){GetRandomValue(-1,1),GetRandomValue(-1,1)};
  237. }
  238. }
  239. }
  240. void sortarc(){
  241. int top3[3] = {0};
  242. int startval=800;
  243. int currentpos=0;
  244. // get top 3 in list top3
  245. while(startval>0 && currentpos<3){
  246. for(int i=0;i<MAX_ARC;i++){
  247. if(arr_codearc[i].score==startval){
  248. if(currentpos<3){
  249. top3[currentpos]=i;
  250. }
  251. currentpos++;
  252. }
  253. }
  254. startval--;
  255. }
  256. // copy the 3 tops into temp arc
  257. for(int i=0;i<MAX_CODE;i++){
  258. for(int j=0;j<3;j++){
  259. arr_temparc[j].position[i] = arr_codearc[top3[j]].position[i];
  260. arr_temparc[j].move[i] = arr_codearc[top3[j]].move[i];
  261. arr_temparc[j].score = arr_codearc[top3[j]].score;
  262. }
  263. }
  264. // copy top up to max-5
  265. for(int i=0;i<MAX_ARC-8;i+=3){
  266. for(int k=0;k<3;k++){
  267. for(int j=0;j<MAX_CODE;j++){
  268. arr_codearc[i+k].position[j] = arr_temparc[k].position[j];
  269. arr_codearc[i+k].move[j] = arr_temparc[k].move[j];
  270. arr_codearc[i+k].score = arr_temparc[k].score;
  271. }
  272. }
  273. }
  274. }
  275. void getscript(int z){
  276. // store script
  277. //arr_codearc[z].score = score;
  278. //arr_code.score =
  279. for(int i=0;i<MAX_CODE;i++){
  280. //arr_codearc[z].move[i] = arr_code[i].move;
  281. //arr_codearc[z].position[i] = arr_code[i].position;
  282. arr_code[i].move = arr_codearc[z].move[i];
  283. arr_code[i].position = arr_codearc[z].position[i];
  284. }
  285. }
  286. void storescript(int z,int score){
  287. // store script
  288. arr_codearc[z].score = score;
  289. for(int i=0;i<MAX_CODE;i++){
  290. arr_codearc[z].move[i] = arr_code[i].move;
  291. arr_codearc[z].position[i] = arr_code[i].position;
  292. }
  293. }
  294. int getscore(){
  295. int score=0;
  296. //count enemies left
  297. int numenemiesleft=0;
  298. for(int y=0;y<10;y++){
  299. for(int x=0;x<10;x++){
  300. if(map[y][x]==AIPLAYER2)numenemiesleft++;
  301. }
  302. }
  303. //was the goal taken
  304. bool objectivesuccess = false;
  305. Vector2 goalposition;
  306. for(int y=0;y<10;y++){
  307. for(int x=0;x<10;x++){
  308. if(map[y][x]==GOALREACHED)objectivesuccess=true;
  309. if(map[y][x]==GOAL || map[y][x]==GOALREACHED){
  310. goalposition.x = x;
  311. goalposition.y = y;
  312. }
  313. }
  314. }
  315. //count aiplayer1 and distance to target
  316. float avdist=0;
  317. int nums=0;
  318. int distanceunit[4]={0};
  319. for(int y=0;y<10;y++){
  320. for(int x=0;x<10;x++){
  321. if(map[y][x]==AIPLAYER1){
  322. int d = 0;
  323. d=edistance((float)goalposition.x,(float)goalposition.y,(float)x,(float)y)*2;
  324. distanceunit[nums]=(int)d;
  325. avdist+=d;
  326. nums++;
  327. }
  328. }}
  329. avdist=avdist/(float)4;
  330. score = (100-(numenemiesleft*25))*2;
  331. score+=(50-(int)avdist);
  332. if(objectivesuccess==true)score+=50;
  333. // if every unit is close then extra score
  334. bool allclose=true;
  335. for(int i=0;i<4;i++){
  336. if(distanceunit[i]>4)allclose=false;
  337. }
  338. if(allclose==true)score+=100;
  339. //score=avdist;
  340. if(score<0)score=9999;
  341. return score;
  342. }
  343. void executescript(){
  344. // execute script
  345. for(int i=0;i<MAX_CODE;i++){
  346. if(map[ (int)arr_code[i].position.y ][ (int)arr_code[i].position.x ]==AIPLAYER1){
  347. Vector2 p=arr_code[i].position;
  348. Vector2 m=arr_code[i].move;
  349. Vector2 np=(Vector2){p.x+m.x,p.y+m.y};
  350. if(map[ (int)np.y ][ (int)np.x ]==GOAL){
  351. map[ (int)np.y ][ (int)np.x ] = GOALREACHED;
  352. }
  353. if(map[ (int)np.y ][ (int)np.x ]==GROUND){
  354. map[ (int)np.y ][ (int)np.x ] = AIPLAYER1;
  355. map[ (int)p.y ][ (int)p.x ] = GROUND;
  356. }
  357. if(map[ (int)np.y ][ (int)np.x ]==AIPLAYER2){
  358. map[ (int)np.y ][ (int)np.x ] = AIPLAYER1;
  359. map[ (int)p.y ][ (int)p.x ] = GROUND;
  360. }
  361. }
  362. }
  363. }
  364. // Manhattan Distance (less precise)
  365. float distance(float x1,float y1,float x2,float y2){
  366. return (float)abs(x2-x1)+abs(y2-y1);
  367. }
  368. // Euclidean distance (more precise)
  369. float edistance(float x1,float y1,float x2,float y2){
  370. return sqrt( (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2) );
  371. }