GeneticAlgorithm_BoardWarSimulation.c 13 KB

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