├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── libraries │ └── com_hynnet_jxl_2_6_12_1.xml ├── misc.xml ├── modules.xml ├── uiDesigner.xml ├── vcs.xml └── workspace.xml ├── README.md ├── Read me.txt ├── Thumbs.db ├── WRCI Results.xls ├── WRCI_Assignment_3.5_Group_9.docx ├── WRCI_ass5.iml ├── apple.png ├── apple.psd ├── body.png ├── body.psd ├── head.png ├── head.psd ├── lib ├── jxl-2.6.12.1.jar └── log4j-1.2.14.jar ├── out └── production │ └── WRCI_ass5 │ ├── Chromosone.class │ ├── Console$1.class │ ├── Console.class │ ├── GeneticTraining$1.class │ ├── GeneticTraining.class │ ├── Main.class │ ├── SnakeGame$1.class │ ├── SnakeGame$TAdapter.class │ ├── SnakeGame.class │ ├── XYPair.class │ ├── neuralNetWork.class │ └── storedNetClass.class └── src ├── Chromosone.java ├── Console.java ├── GeneticTraining.java ├── Main.java ├── SnakeGame.java ├── XYPair.java ├── neuralNetWork.java └── storedNetClass.java /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/libraries/com_hynnet_jxl_2_6_12_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 20 | 21 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 121 | 122 | 123 | 125 | 126 | 129 | 130 | 131 | 144 | 145 | 146 | 151 | 152 | 153 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 209 | 210 | 211 | 215 | 216 | 217 | 239 | 240 | 241 | 262 | 263 | 270 | 271 | 272 | 285 | 286 | 287 | 294 | 297 | 299 | 300 | 301 | 302 | 303 | 304 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 364 | 365 | 366 | 377 | 378 | 379 | 389 | 390 | 397 | 398 | 399 | 400 | 418 | 425 | 426 | 427 | 428 | 446 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 1474364074112 472 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 538 | 541 | 542 | 543 | 545 | 546 | 547 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | No facets are configured 1019 | 1020 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1.8 1042 | 1043 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | WRCI_ass5 1054 | 1055 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1.8 1066 | 1067 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SnakeGame 2 | A classic game of snake that is controlled by a neural network (NN) and trained using a genetic algorithm (GA) 3 | 4 | Written in Java, the objective of the game is to maximize the snake's length without running into it's own body or the boundry wall. 5 | The game is run using a neural network made up 15 input neurons, 7 hidden layer neurons and 4 output neurons. 6 | 7 | -------------------------------------------- 8 | Input neurons consist of (note, all values are taken as x and y PIXEL coordinates): 9 | 10 | 0: position of head x 11 | 12 | 1: position of head y 13 | 14 | 2: position of midpoint of body x 15 | 16 | 3: position of midpoint of body y 17 | 18 | 4: position of tail x 19 | 20 | 5: position of tail y 21 | 22 | 6: position of food x 23 | 24 | 7: position of food y 25 | 26 | 8: distance to food x 27 | 28 | 9: distance to food y 29 | 30 | 10: is the block on the left of the head free? (1 or 0) 31 | 32 | 11: is the block on the right of the head free? (1 or 0) 33 | 34 | 12: is the block on the up of the head free? (1 or 0) 35 | 36 | 13: is the block on the down of the head free? (1 or 0) 37 | 38 | 14: bias (-1) 39 | 40 | (note the bias is -1, as the formula in the activation function is given as a negative 41 | 42 | Output neruons are: 43 | 44 | 0: move left 45 | 46 | 1: move right 47 | 48 | 2: move down 49 | 50 | 3: move up 51 | 52 | -------------------------------------------- 53 | 54 | Training of the neural network happens through a genetic algorithm through the updating of weights that are optimized around a fitness function. 55 | Random weights are initially generated for the population size of the genetic alorithm (GA) and then a game of snake is played by the NN for every chromozone in the population 56 | At the end of each playing cycle, the GA performs cross overs and mutations around a fitness function in order to try and better each chromozone's score. 57 | The fitness function is: 58 | if (stalled) 59 | out = (stepsTaken/4) + (snakeLength * 100000.0); 60 | else 61 | out = (stepsTaken/2) + (snakeLength * 100000.0); 62 | 63 | It should be noted, that the stalled value is 1, when a game ends because the snake has not found food with a set number of moves. 64 | -------------------------------------------------------------------------------- /Read me.txt: -------------------------------------------------------------------------------- 1 | wrci project created by: 2 | Adriaan, 3 | Ashley, 4 | Dillon, 5 | Gareth, and 6 | Ridwaan -------------------------------------------------------------------------------- /Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/Thumbs.db -------------------------------------------------------------------------------- /WRCI Results.xls: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/WRCI Results.xls -------------------------------------------------------------------------------- /WRCI_Assignment_3.5_Group_9.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/WRCI_Assignment_3.5_Group_9.docx -------------------------------------------------------------------------------- /WRCI_ass5.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /apple.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/apple.png -------------------------------------------------------------------------------- /apple.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/apple.psd -------------------------------------------------------------------------------- /body.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/body.png -------------------------------------------------------------------------------- /body.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/body.psd -------------------------------------------------------------------------------- /head.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/head.png -------------------------------------------------------------------------------- /head.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/head.psd -------------------------------------------------------------------------------- /lib/jxl-2.6.12.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/lib/jxl-2.6.12.1.jar -------------------------------------------------------------------------------- /lib/log4j-1.2.14.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/lib/log4j-1.2.14.jar -------------------------------------------------------------------------------- /out/production/WRCI_ass5/Chromosone.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/out/production/WRCI_ass5/Chromosone.class -------------------------------------------------------------------------------- /out/production/WRCI_ass5/Console$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/out/production/WRCI_ass5/Console$1.class -------------------------------------------------------------------------------- /out/production/WRCI_ass5/Console.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/out/production/WRCI_ass5/Console.class -------------------------------------------------------------------------------- /out/production/WRCI_ass5/GeneticTraining$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/out/production/WRCI_ass5/GeneticTraining$1.class -------------------------------------------------------------------------------- /out/production/WRCI_ass5/GeneticTraining.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/out/production/WRCI_ass5/GeneticTraining.class -------------------------------------------------------------------------------- /out/production/WRCI_ass5/Main.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/out/production/WRCI_ass5/Main.class -------------------------------------------------------------------------------- /out/production/WRCI_ass5/SnakeGame$1.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/out/production/WRCI_ass5/SnakeGame$1.class -------------------------------------------------------------------------------- /out/production/WRCI_ass5/SnakeGame$TAdapter.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/out/production/WRCI_ass5/SnakeGame$TAdapter.class -------------------------------------------------------------------------------- /out/production/WRCI_ass5/SnakeGame.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/out/production/WRCI_ass5/SnakeGame.class -------------------------------------------------------------------------------- /out/production/WRCI_ass5/XYPair.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/out/production/WRCI_ass5/XYPair.class -------------------------------------------------------------------------------- /out/production/WRCI_ass5/neuralNetWork.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/out/production/WRCI_ass5/neuralNetWork.class -------------------------------------------------------------------------------- /out/production/WRCI_ass5/storedNetClass.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mraythree/SnakeGame/86c25e06083f06c901687e8e30bab76227bc29da/out/production/WRCI_ass5/storedNetClass.class -------------------------------------------------------------------------------- /src/Chromosone.java: -------------------------------------------------------------------------------- 1 | import java.util.Random; 2 | 3 | /** 4 | * Created by Adriaan on 2016/11/12. 5 | */ 6 | public class Chromosone 7 | { 8 | double[][] vWeights, wWeights; 9 | double fitness; 10 | double minSearchSpace, maxSearchSpace; 11 | long timeTaken; 12 | double snakeLength; 13 | int stepsTaken; 14 | boolean stalled; 15 | 16 | public Chromosone() 17 | { 18 | this.minSearchSpace = SnakeGame.minSearchSpace; 19 | this.maxSearchSpace = SnakeGame.maxSearchSpace; 20 | stalled = false; 21 | //initializeWeights(); 22 | } 23 | 24 | public Chromosone(boolean firstRun) 25 | { 26 | this.minSearchSpace = SnakeGame.minSearchSpace; 27 | this.maxSearchSpace = SnakeGame.maxSearchSpace; 28 | if (firstRun) 29 | initializeWeights(); 30 | stalled = false; 31 | } 32 | 33 | public void initializeWeights() 34 | { 35 | //give initial weights 36 | vWeights = new double[SnakeGame.noHiddenNeurons][SnakeGame.noInputsNeurons]; //2D array for weights going from every input node, to every hidden node. 37 | wWeights = new double[SnakeGame.noOutputNeurons][SnakeGame.noHiddenNeurons]; //2D array for weights going from every hidden node to every output node 38 | for (int i = 0; i<= SnakeGame.noInputsNeurons -1; i++) //set the last one to be the bias 39 | for (int j = 0; j <= SnakeGame.noHiddenNeurons-1; j++) 40 | vWeights[j][i] = randomDouble(); 41 | 42 | for (int j = 0; j <= SnakeGame.noHiddenNeurons-1; j++) 43 | { 44 | for (int k = 0; k<= SnakeGame.noOutputNeurons -1; k++) 45 | wWeights[k][j] = randomDouble(); 46 | } 47 | } 48 | 49 | //so we are using the time taken and the snake's length as the FF. 50 | //this means that we are encouraging the snake to stay alive, but are rewarding it more for finding the food. 51 | public void setFitness(long timeTaken, double snakeLength) 52 | { 53 | //this.timeTaken = timeTaken; 54 | this.snakeLength = snakeLength; 55 | this.stepsTaken = SnakeGame.stepsTaken; 56 | double out; 57 | if (stalled) 58 | out = (stepsTaken/4) + (snakeLength * 100000.0); 59 | else 60 | out = (stepsTaken/2) + (snakeLength * 100000.0); 61 | 62 | //double out = timeTaken + (snakeLength * 10000000); 63 | //double out = snakeLength * 1.0; 64 | stalled = false; 65 | fitness = out; 66 | } 67 | 68 | public Chromosone clone() 69 | { 70 | Chromosone out = new Chromosone(); 71 | double[][] newVWeights = new double[SnakeGame.noHiddenNeurons][SnakeGame.noInputsNeurons]; 72 | double[][] newWWeights = new double[SnakeGame.noOutputNeurons][SnakeGame.noHiddenNeurons]; 73 | 74 | for (int i = 0; i < SnakeGame.noHiddenNeurons; i++) 75 | newVWeights[i] = getvWeights()[i].clone(); 76 | out.setvWeights(newVWeights); 77 | 78 | for (int i = 0; i < SnakeGame.noOutputNeurons; i++) 79 | newWWeights[i] = getwWeights()[i].clone(); 80 | out.setwWeights(newWWeights); 81 | out.setFitness(timeTaken, fitness); 82 | return out; 83 | } 84 | 85 | public double getFitness() 86 | { 87 | return fitness; 88 | } 89 | 90 | public double randomDouble() 91 | { 92 | Random r = new Random(); 93 | double min = -1 / Math.sqrt(SnakeGame.noInputsNeurons); 94 | double max = 1/ Math.sqrt(SnakeGame.noInputsNeurons); 95 | double randomVal = (r.nextDouble() * (max - min)) + min; 96 | return randomVal; 97 | } 98 | 99 | public double randomDoubleInSearchSpace() 100 | { 101 | Random r = new Random(); 102 | double min = minSearchSpace; 103 | double max = maxSearchSpace; 104 | double randomVal = r.nextDouble()*(max - min) + min; 105 | return randomVal; 106 | } 107 | 108 | public long getTimeTaken() { 109 | return timeTaken; 110 | } 111 | 112 | public void setTimeTaken(long timeTaken) { 113 | this.timeTaken = timeTaken; 114 | } 115 | 116 | public double[][] getvWeights() { 117 | return vWeights; 118 | } 119 | 120 | public void setvWeights(double[][] vWeights) { 121 | this.vWeights = vWeights; 122 | } 123 | 124 | public double[][] getwWeights() { 125 | return wWeights; 126 | } 127 | 128 | public void setwWeights(double[][] wWeights) { 129 | this.wWeights = wWeights; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/Console.java: -------------------------------------------------------------------------------- 1 | import jxl.write.WriteException; 2 | 3 | import javax.swing.*; 4 | import java.awt.*; 5 | import java.awt.event.ActionEvent; 6 | import java.awt.event.ActionListener; 7 | import java.io.IOException; 8 | 9 | /** 10 | * Created by Adriaan on 2016/11/11. 11 | */ 12 | public class Console extends JFrame 13 | { 14 | DefaultListModel model; 15 | JList list; 16 | JButton btnStop; 17 | JScrollPane listScroller; 18 | public Console() 19 | { 20 | JPanel pnl = new JPanel(new BorderLayout()); 21 | list = new JList(); 22 | list.setLayoutOrientation(JList.HORIZONTAL_WRAP); 23 | list.setVisibleRowCount(-1); 24 | model = new DefaultListModel(); 25 | model.addElement("Snake Trainer"); 26 | list = new JList(model); 27 | listScroller = new JScrollPane(list); 28 | listScroller.setPreferredSize(new Dimension(250, 80)); 29 | btnStop = new JButton("Stop"); 30 | pnl.add("North", btnStop); 31 | pnl.add("Center", listScroller); 32 | this.add(pnl); 33 | setResizable(true); //we don't want to let the user resize the window 34 | pack(); //take the dimensions of the frame we are loading 35 | setTitle("console"); //we should be the generation number in the title. 36 | this.setBounds(200, 200, 500, 700); 37 | this.setVisible(true); 38 | 39 | btnStop.addActionListener(new ActionListener() { 40 | @Override 41 | public void actionPerformed(ActionEvent e) 42 | { 43 | SnakeGame.btnStoppedPushed = true; 44 | addToConsole("Game Stopping..."); 45 | } 46 | }); 47 | } 48 | 49 | public void addToConsole(String message) 50 | { 51 | model.addElement(message); 52 | list = new JList(model); 53 | //listScroller.scrollRectToVisible(new Rectangle(250, 80)); 54 | JScrollBar vertical = listScroller.getVerticalScrollBar(); 55 | vertical.setValue(vertical.getMaximum()); 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/GeneticTraining.java: -------------------------------------------------------------------------------- 1 | import java.util.ArrayList; 2 | import java.util.Collections; 3 | import java.util.Comparator; 4 | import java.util.Random; 5 | 6 | /** 7 | * Created by Adriaan on 2016/11/11. 8 | */ 9 | public class GeneticTraining 10 | { 11 | public int populationSize, hallOfFameSize, tournamentSize; 12 | ArrayList chromosones; 13 | public int curChromosone; 14 | public double mutationRate; 15 | 16 | //this gets called once. this is tricky. 17 | //in order to evaluate the fitness function, we need to play a game of snake. 18 | //this means, that for every chromosone in the population we have to play a game of snake before we do anything with the population. 19 | public GeneticTraining(int populationSize) 20 | { 21 | this.populationSize = populationSize; 22 | chromosones = new ArrayList<>(); 23 | Chromosone c; 24 | for (int i = 0; i <= populationSize -1; i++) 25 | { 26 | c = new Chromosone(true); 27 | chromosones.add(c); 28 | } 29 | curChromosone = 0; 30 | hallOfFameSize = 2; 31 | tournamentSize = 3; 32 | mutationRate = 10; 33 | } 34 | 35 | //once a population of chomozones have been evaluated, do the GA. 36 | public void Train() 37 | { 38 | //comparator is what we use to sort the list. we want to sort them in order of highest to lowest fitness. 39 | Comparator comp = new Comparator() 40 | { 41 | @Override 42 | public int compare(Chromosone o1, Chromosone o2) 43 | { 44 | if (o1.getFitness() < o2.getFitness()) 45 | return 1; 46 | if (o1.getFitness() > o2.getFitness()) 47 | return -1; 48 | return 0; 49 | } 50 | }; 51 | 52 | Collections.sort(chromosones, comp); 53 | ArrayList nextGen = new ArrayList<>(); //this will eventually be the new population of chromozones. 54 | for (int i = 0; i< hallOfFameSize; i++) //let the first fittest individuals take the first slots to the halloffamesize counter. 55 | { 56 | nextGen.add(chromosones.get(i).clone()); 57 | } 58 | 59 | //cross and mutate a child from the best 2 (this assumes that the halloffamesize is 2. 60 | Chromosone c1 = nextGen.get(0); 61 | Chromosone c2 = nextGen.get(1); 62 | Chromosone cross = crossover(c1, c2); 63 | cross = mutate(cross); 64 | //cross = mutatePositive(cross); 65 | nextGen.add(cross); 66 | 67 | //do tournament selection for the rest of the population. 68 | for (int i = 0; i< populationSize-(hallOfFameSize+1); i++) 69 | { 70 | c1 = doTournamentSelection(chromosones, comp); 71 | c2 = doTournamentSelection(chromosones, comp); 72 | cross = crossover(c1, c2); 73 | cross = mutate(cross); 74 | //cross = mutatePositive(cross); 75 | nextGen.add(cross); 76 | } 77 | chromosones = nextGen; 78 | } 79 | 80 | private Chromosone doTournamentSelection(ArrayList chromosones, Comparator comp) 81 | { 82 | Chromosone out = null; 83 | double lowestFitness = -9999999; 84 | ArrayList tournamentChromosones = new ArrayList<>(); 85 | for (int i = 0; i < tournamentSize; i++) 86 | { 87 | int randI = randomInt1andPopSize(); 88 | Chromosone temp = chromosones.get(randI); 89 | tournamentChromosones.add(temp); 90 | } 91 | 92 | Collections.sort(tournamentChromosones, comp); 93 | out = tournamentChromosones.get(0); 94 | return out; 95 | } 96 | 97 | private Chromosone mutate(Chromosone c) 98 | { 99 | double diff; 100 | Random R = new Random(); 101 | //v 102 | for (int i = 0; i < SnakeGame.noHiddenNeurons; i++) 103 | { 104 | for (int j = 0; j < SnakeGame.noInputsNeurons; j++) 105 | { 106 | if (randomInt1andPopSize() > populationSize - mutationRate) 107 | { 108 | if (randomInt1andPopSize() > populationSize/2) //50% chance 109 | { 110 | if (j > 7) 111 | diff = randomGauss(); 112 | else 113 | //diff = R.nextDouble() * minSearchSpace; 114 | diff = c.getvWeights()[i][j] + R.nextDouble() * SnakeGame.minSearchSpace/50; 115 | } 116 | else 117 | { 118 | if (j > 7) 119 | diff = randomGauss(); 120 | else 121 | //diff = R.nextDouble() * maxSearchSpace; 122 | diff = c.getvWeights()[i][j] + R.nextDouble() * SnakeGame.maxSearchSpace/50; 123 | } 124 | c.vWeights[i][j] = diff; 125 | } 126 | } 127 | } 128 | 129 | //w 130 | for (int i = 0; i < SnakeGame.noOutputNeurons; i++) 131 | { 132 | for (int j = 0; j < SnakeGame.noHiddenNeurons; j++) 133 | { 134 | if (randomInt1andPopSize() > populationSize - mutationRate) 135 | { 136 | if (randomInt1andPopSize() > populationSize/2) 137 | { 138 | if (i > 2) 139 | diff = randomGauss(); 140 | else 141 | //diff = R.nextDouble() * minSearchSpace; 142 | diff = c.getwWeights()[i][j] + R.nextDouble() * SnakeGame.minSearchSpace/50; 143 | } 144 | else 145 | { 146 | if (i > 2) 147 | diff = randomGauss(); 148 | else 149 | //diff = R.nextDouble() * maxSearchSpace; 150 | diff = c.getwWeights()[i][j] + R.nextDouble() * SnakeGame.maxSearchSpace/50; 151 | } 152 | c.wWeights[i][j] = diff; 153 | } 154 | } 155 | } 156 | 157 | return c; 158 | } 159 | 160 | //get a value around 1 to move in. 161 | public double randomGauss() 162 | { 163 | Random r = new Random(); 164 | double desiredStandardDeviation = 0.05; 165 | double desiredMean = 1; 166 | return r.nextGaussian()*desiredStandardDeviation+desiredMean; 167 | } 168 | 169 | public Chromosone crossover(Chromosone c1, Chromosone c2) 170 | { 171 | Chromosone out = new Chromosone(); 172 | double[][] newVWeights = new double[SnakeGame.noHiddenNeurons][SnakeGame.noInputsNeurons]; 173 | double[][] newWWeights = new double[SnakeGame.noOutputNeurons][SnakeGame.noHiddenNeurons]; 174 | //v 175 | for (int i = 0; i < SnakeGame.noHiddenNeurons; i++) 176 | { 177 | for (int j = 0; j < SnakeGame.noInputsNeurons; j++) 178 | { 179 | int r = randomInt1andPopSize(); 180 | if (r % 2 == 0) 181 | newVWeights[i][j] = c1.getvWeights()[i][j]; 182 | else 183 | newVWeights[i][j] = c2.getvWeights()[i][j]; 184 | } 185 | } 186 | //w 187 | for (int i = 0; i < SnakeGame.noOutputNeurons; i++) 188 | { 189 | for (int j = 0; j < SnakeGame.noHiddenNeurons; j++) 190 | { 191 | int r = randomInt1andPopSize(); 192 | if (r % 2 == 0) 193 | newWWeights[i][j] = c1.getwWeights()[i][j]; 194 | else 195 | newWWeights[i][j] = c2.getwWeights()[i][j]; 196 | } 197 | } 198 | out.setvWeights(newVWeights); 199 | out.setwWeights(newWWeights); 200 | return out; 201 | } 202 | 203 | public int randomInt1andPopSize() 204 | { 205 | Random r = new Random(); 206 | int min = 0; 207 | int max = populationSize; 208 | int randomVal = r.nextInt((max - min)) + min; 209 | return randomVal; 210 | } 211 | 212 | public Chromosone getCurChromosone() 213 | { 214 | return chromosones.get(curChromosone); 215 | } 216 | 217 | public void incCurChromosone() 218 | { 219 | if (curChromosone < populationSize-1) 220 | curChromosone++; 221 | else 222 | curChromosone = 0; 223 | } 224 | 225 | 226 | 227 | 228 | } 229 | -------------------------------------------------------------------------------- /src/Main.java: -------------------------------------------------------------------------------- 1 | import javax.swing.*; 2 | 3 | /** 4 | * Created by Adriaan on 2016/09/20. 5 | */ 6 | public class Main extends JFrame 7 | { 8 | public static SnakeGame snakeGame; 9 | public Main() 10 | { 11 | snakeGame = SnakeGame.getClassInstance(); 12 | add(snakeGame); 13 | setResizable(false); //we don't want to let the user resize the window 14 | pack(); //take the dimensions of the frame we are loading 15 | setTitle("Snake Being Trained"); //we should be the generation number in the title. 16 | setLocationRelativeTo(null); 17 | setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 18 | } 19 | 20 | public static void main(String[] args) 21 | { 22 | JFrame ex = new Main(); 23 | ex.setVisible(true); 24 | try 25 | { 26 | snakeGame.runGame(); 27 | } 28 | catch (InterruptedException e) 29 | { 30 | e.printStackTrace(); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/SnakeGame.java: -------------------------------------------------------------------------------- 1 | import jxl.Workbook; 2 | import jxl.write.*; 3 | 4 | import javax.swing.*; 5 | import java.awt.*; 6 | import java.awt.Label; 7 | import java.awt.event.KeyAdapter; 8 | import java.awt.event.KeyEvent; 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.util.ArrayList; 12 | import java.util.Date; 13 | import java.util.Random; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | /** 17 | * Created by Adriaan on 2016/09/20 18 | */ 19 | public class SnakeGame extends JPanel 20 | { 21 | //constant variables 22 | private static final int DOT_SIZE = 15; 23 | private static final int populationSize = 50; 24 | private static final int boarderHeight = 425; 25 | private static final int boarderWidth = 425; 26 | private static final int ALL_DOTS = 600; 27 | private static final int randomPos = 27; //seed value for randoms 28 | private static final int noStepsTakenTillStallAllowed = 100; 29 | 30 | //pulic static vars 31 | public static int noInputsNeurons = 15; 32 | public static int noHiddenNeurons = 7; 33 | public static int noOutputNeurons = 4; 34 | public static int minSearchSpace = -10000; 35 | public static int maxSearchSpace = 10000; 36 | 37 | //size of board 38 | private static final int x[] = new int[ALL_DOTS]; 39 | private static final int y[] = new int[ALL_DOTS]; 40 | 41 | //global variables 42 | private static boolean leftDirection; 43 | private static boolean rightDirection; 44 | private static boolean upDirection; 45 | private static boolean downDirection; 46 | private static boolean inGame; 47 | 48 | 49 | private static int dots; 50 | private static int foodX; 51 | private static int foodY; 52 | private static int snakeLength; 53 | private static int maxSnakeLength; 54 | private static int iterationCounter; 55 | private static int applesEaten; 56 | public static int stepsTaken; 57 | public static int stepsTakenSinceLastFood; 58 | 59 | //mins and maxes for normalization 60 | private static double maxX; 61 | private static double maxY; 62 | private static double minY; 63 | private static double minX; 64 | 65 | 66 | //images to use 67 | private static Image imgBody; 68 | private static Image imgApple; 69 | private static Image imgHead; 70 | 71 | //positions of things 72 | private static XYPair headBody; 73 | private static XYPair midBody; 74 | private static XYPair tailBody; 75 | private static XYPair foodXY; 76 | private static boolean leftDirFree; 77 | private static boolean rightDirFree; 78 | private static boolean upDirFree; 79 | private static boolean downDirFree; 80 | 81 | //timer 82 | private static long timeSinceLastFood; 83 | private static long elapsedTimeSinceLastFood; 84 | private static long gameTime; 85 | private static long elapsedGameTime; 86 | private static boolean invalidMove; 87 | 88 | public static SnakeGame snakeGame; 89 | public static boolean btnStoppedPushed; 90 | 91 | private static WritableSheet writableSheet; 92 | private static WritableWorkbook writableWorkbook; 93 | private static Console console; 94 | //constructor 95 | public SnakeGame() throws IOException, WriteException { 96 | //initialze Excel 97 | btnStoppedPushed = false; 98 | String path = "C:\\Users\\Adriaan\\Desktop\\WRCI Results.xls"; 99 | File exlFile = new File(path); 100 | writableWorkbook = Workbook.createWorkbook(exlFile); 101 | writableSheet = writableWorkbook.createSheet("Sheet1", 0); 102 | jxl.write.Label label = new jxl.write.Label(0, 0, "WRCI Results"); 103 | writableSheet.addCell(label); 104 | //initialize variables 105 | inGame = true; 106 | snakeLength = 3; 107 | headBody = new XYPair(0,0); 108 | midBody = new XYPair(0,0); 109 | tailBody = new XYPair(0,0); 110 | foodXY = new XYPair(0,0); 111 | 112 | //put the key listener in 113 | addKeyListener(new TAdapter()); 114 | 115 | setBackground(Color.black); 116 | setFocusable(true); 117 | setPreferredSize(new Dimension(boarderWidth, boarderHeight)); 118 | iterationCounter = 0; 119 | applesEaten = 0; 120 | stepsTaken = 0; 121 | stepsTakenSinceLastFood = 0; 122 | invalidMove = false; 123 | initializeImages(); 124 | initializeGame(); 125 | } 126 | 127 | //used by the Main class. 128 | public static SnakeGame getClassInstance() 129 | { 130 | if (snakeGame == null) 131 | { 132 | try { 133 | //console lets us see the result from each iteration 134 | snakeGame = new SnakeGame(); 135 | console = new Console(); 136 | } catch (IOException e) { 137 | e.printStackTrace(); 138 | } catch (WriteException e) { 139 | e.printStackTrace(); 140 | } 141 | return snakeGame; 142 | } 143 | return snakeGame; 144 | } 145 | 146 | public void runGame() throws InterruptedException 147 | { 148 | //iteration of each game. at the end of each iteration info is passed back and fourth to the GA. 149 | GeneticTraining ga = new GeneticTraining(populationSize); //takes in the population size 150 | //set up the NN with the info from the GA 151 | neuralNetWork NN = new neuralNetWork(); 152 | Chromosone c; //this will be used to keep track of the current chromozone we are on. 153 | iterationCounter = 0; 154 | maxSnakeLength = 3; //always starts with length 3. 155 | while (iterationCounter < 200001 && !btnStoppedPushed) 156 | { 157 | c = ga.getCurChromosone(); 158 | NN.setWeightsOfNN(c.getvWeights(), c.getwWeights()); 159 | 160 | //restart stall counter. --> these have been depricated. working with number of steps is better. 161 | restartFoodTimer(); 162 | restartGameTimer(); 163 | snakeLength = 3; 164 | applesEaten = 0; 165 | //gives us updates in console. 166 | if (iterationCounter % 20000 == 0) 167 | System.out.println("Max length achieved so far: " + maxSnakeLength); 168 | 169 | //this while loop runs the actual game within each iteration. 170 | while (inGame) 171 | { 172 | //every 20 thousand iterations you can see how the snake is doing. 173 | //if the snake reachers a length greater than 25, we will also see what it is doing. 174 | //reduce the sleep time, to make the snake move faster. 175 | if (iterationCounter % 20000 == 0 || snakeLength > 29) 176 | TimeUnit.MILLISECONDS.sleep(100); 177 | 178 | //this is also deprecated, as invalid moves should not be made. leave just in case. 179 | if (invalidMove) 180 | inGame = false; 181 | 182 | //we want to know which direction we are going in, so that if the NN tries to go that way, we say "NO BITCH", and take the next best move. the game stalls if the NN tries to go in the opposite direction as this is not allowed, but the inputs don't change, so the NN will continuing trying. 183 | int currentOppositeDir = 0; 184 | if (leftDirection) 185 | currentOppositeDir = 1; 186 | else if (rightDirection) 187 | currentOppositeDir = 0; 188 | else if (upDirection) 189 | currentOppositeDir = 3; 190 | else if (downDirection) 191 | currentOppositeDir = 2; 192 | double[] inputs = getInputs(); //get new inputs for the NN for the next move. 193 | int Move = NN.getNextMove(currentOppositeDir, inputs); //calculate the next move. 194 | 195 | managemove(Move); //find out in which way the NN wants us to go. 196 | 197 | handleMovement(); //actually move the snake. 198 | checkEatenApple(); //did the snake find food? if so increase its length and reset the stall counter. 199 | 200 | //did the snake just learn how to survive without the need to find food? kill it dead. game stalled. 201 | //this value might need to be changed to something dynamic, as when the size increases, it might need more steps. 202 | if (stepsTakenSinceLastFood > noStepsTakenTillStallAllowed) 203 | { 204 | inGame = false; 205 | c.stalled = true; 206 | } 207 | checkForCollision(); //did the snake collide with anything? wall or body? if so, kill it. 208 | if (iterationCounter % 20000 == 0 || snakeLength > 29) //we don't show what is going on the screen in "simulation mode", takes too long. 209 | repaint(); //refresh screen. 210 | } 211 | //set the chromosone's fitness. //use of time has been depricated. 212 | c.setFitness(getElapsedGameTime(), snakeLength); 213 | //use the console to update what happened in the current iteration. 214 | //console.addToConsole("Iteation: " + iterationCounter + " Length: " + snakeLength); 215 | System.out.println("Iteation: " + iterationCounter + " Length: " + snakeLength); 216 | if (snakeLength > maxSnakeLength) 217 | { 218 | maxSnakeLength = snakeLength; 219 | console.addToConsole("New Highscoore of: " + maxSnakeLength + ", in iteration: " + iterationCounter); 220 | } 221 | 222 | 223 | 224 | iterationCounter++; 225 | //if we have been through a population of chromosone's its time to do crossovers and mutations, etc. 226 | if (iterationCounter % ga.populationSize == 0) 227 | ga.Train(); 228 | //move onto the next chromosone. this wraps around to the beginning once the population size is reached. 229 | ga.incCurChromosone(); 230 | //initialize the next iteration 231 | initializeGame(); 232 | } 233 | console.addToConsole("Game Stopped!"); 234 | 235 | //write to excel 236 | writeExcelResults(ga); 237 | try { 238 | writableWorkbook.write(); 239 | writableWorkbook.close(); 240 | } catch (IOException e) { 241 | e.printStackTrace(); 242 | } catch (WriteException e) { 243 | e.printStackTrace(); 244 | } 245 | } 246 | 247 | private void writeExcelResults(GeneticTraining ga) 248 | { 249 | jxl.write.Label label; 250 | try { 251 | // LABEL: (COLOUMN, ROW, STRING); 252 | //NN Details 253 | label = new jxl.write.Label(0, 2, "NN Details"); 254 | writableSheet.addCell(label); 255 | label = new jxl.write.Label(0, 3, "Input Neurons"); 256 | writableSheet.addCell(label); 257 | label = new jxl.write.Label(0, 4, "Hidden Neurons"); 258 | writableSheet.addCell(label); 259 | label = new jxl.write.Label(0, 5, "Output Neurons"); 260 | writableSheet.addCell(label); 261 | label = new jxl.write.Label(1, 3, String.valueOf(noInputsNeurons)); 262 | writableSheet.addCell(label); 263 | label = new jxl.write.Label(1, 4, String.valueOf(noHiddenNeurons)); 264 | writableSheet.addCell(label); 265 | label = new jxl.write.Label(1, 5, String.valueOf(noOutputNeurons)); 266 | writableSheet.addCell(label); 267 | 268 | //Game Details 269 | label = new jxl.write.Label(3, 2, "Game Details"); 270 | writableSheet.addCell(label); 271 | label = new jxl.write.Label(3, 3, "No Steps taken until stall"); 272 | writableSheet.addCell(label); 273 | label = new jxl.write.Label(3, 2, "No Iterations over all chromosomes"); 274 | writableSheet.addCell(label); 275 | label = new jxl.write.Label(3, 2, "Max Snake Length Achieved"); 276 | writableSheet.addCell(label); 277 | label = new jxl.write.Label(4, 3, String.valueOf(noStepsTakenTillStallAllowed)); 278 | writableSheet.addCell(label); 279 | label = new jxl.write.Label(4, 2, String.valueOf(iterationCounter)); 280 | writableSheet.addCell(label); 281 | label = new jxl.write.Label(4, 2, String.valueOf(maxSnakeLength)); 282 | writableSheet.addCell(label); 283 | 284 | //GA Details 285 | label = new jxl.write.Label(6, 2, "GA Details"); 286 | writableSheet.addCell(label); 287 | label = new jxl.write.Label(6, 3, "Population Size"); 288 | writableSheet.addCell(label); 289 | label = new jxl.write.Label(6, 4, "Mutation Rate"); 290 | writableSheet.addCell(label); 291 | label = new jxl.write.Label(6, 5, "Hall Of Fame Size"); 292 | writableSheet.addCell(label); 293 | label = new jxl.write.Label(6, 6, "Tournament Size"); 294 | writableSheet.addCell(label); 295 | label = new jxl.write.Label(6, 7, "Min Search Space"); 296 | writableSheet.addCell(label); 297 | label = new jxl.write.Label(6, 8, "Max Search Space"); 298 | writableSheet.addCell(label); 299 | label = new jxl.write.Label(7, 3, String.valueOf(populationSize)); 300 | writableSheet.addCell(label); 301 | label = new jxl.write.Label(7, 4, String.valueOf(ga.mutationRate)); 302 | writableSheet.addCell(label); 303 | label = new jxl.write.Label(7, 5, String.valueOf(ga.hallOfFameSize)); 304 | writableSheet.addCell(label); 305 | label = new jxl.write.Label(7, 6, String.valueOf(ga.tournamentSize)); 306 | writableSheet.addCell(label); 307 | label = new jxl.write.Label(7, 7, String.valueOf(minSearchSpace)); 308 | writableSheet.addCell(label); 309 | label = new jxl.write.Label(7, 8, String.valueOf(maxSearchSpace)); 310 | writableSheet.addCell(label); 311 | } 312 | catch (WriteException e) 313 | { 314 | e.printStackTrace(); 315 | } 316 | } 317 | 318 | private void managemove(int Move) 319 | { 320 | //check for invalid moves. this should never happen now. 321 | if ((Move == 0) && (rightDirection)) //move left 322 | { 323 | invalidMove = true; 324 | } 325 | else if ((Move == 1) && (leftDirection)) //move right 326 | { 327 | invalidMove = true; 328 | } 329 | else if ((Move == 2) && (downDirection)) //move down 330 | { 331 | invalidMove = true; 332 | } 333 | else if ((Move == 3) && (upDirection)) //move up 334 | { 335 | invalidMove = true; 336 | } 337 | 338 | //set the new direction 339 | if ((Move == 0) && (!rightDirection)) //move left 340 | { 341 | leftDirection = true; 342 | upDirection = false; 343 | downDirection = false; 344 | } 345 | else if ((Move == 1) && (!leftDirection)) //move right 346 | { 347 | rightDirection = true; 348 | upDirection = false; 349 | downDirection = false; 350 | } 351 | else if ((Move == 2) && (!downDirection)) //move up 352 | { 353 | upDirection = true; 354 | rightDirection = false; 355 | leftDirection = false; 356 | } 357 | else if ((Move == 3) && (!upDirection))//move down 358 | { 359 | downDirection = true; 360 | rightDirection = false; 361 | leftDirection = false; 362 | } 363 | } 364 | 365 | //start with a length of 3. place the food on the board. food will never be placed somewhere on the snake's body. 366 | private static void initializeGame() 367 | { 368 | inGame = true; 369 | dots = 3; 370 | // rightDirection = true; 371 | leftDirection = false; 372 | rightDirection = false; 373 | upDirection = false; 374 | downDirection = false; 375 | stepsTaken = 0; 376 | stepsTakenSinceLastFood = 0; 377 | invalidMove = false; 378 | //place the width and height of the pixels for each "dot"/length of body. the paint method will take care of the rest. 379 | // for (int i = 0; i <= dots -1; i++) 380 | // { 381 | // x[i] = 50 - i * 15; 382 | // y[i] = 50; 383 | // } 384 | 385 | //snake needs to start in a random pos 386 | //pick direction 387 | int dir = randomInt0and3() + 1; 388 | //always start in the centre of the board. 389 | int headpos = (13* 15) + 5; 390 | //int headpos = randomInt0and25(); 391 | if (dir == 1) //going right 392 | { 393 | rightDirection = true; 394 | x[0] = headpos; 395 | y[0] = headpos; 396 | x[1] = headpos - 15; 397 | y[1] = headpos; 398 | x[2] = headpos - 30; 399 | y[2] = headpos; 400 | } 401 | else if (dir == 2) //going left 402 | { 403 | leftDirection = true; 404 | x[0] = headpos; 405 | y[0] = headpos; 406 | x[1] = headpos + 15; 407 | y[1] = headpos; 408 | x[2] = headpos + 30; 409 | y[2] = headpos; 410 | } 411 | else if (dir == 3) //going up 412 | { 413 | upDirection = true; 414 | x[0] = headpos; 415 | y[0] = headpos; 416 | x[1] = headpos; 417 | y[1] = headpos + 15; 418 | x[2] = headpos; 419 | y[2] = headpos + 30; 420 | } 421 | else if (dir == 4) //going down 422 | { 423 | downDirection = true; 424 | x[0] = headpos; 425 | y[0] = headpos; 426 | x[1] = headpos; 427 | y[1] = headpos - 15; 428 | x[2] = headpos; 429 | y[2] = headpos - 30; 430 | } 431 | placeApple(); 432 | updatePostitions(); 433 | } 434 | 435 | //used to find a random pos on the board. 436 | public static int randomInt0and25() 437 | { 438 | // Random r = new Random(); 439 | // int min = 3; 440 | // int max = 21; 441 | // int randomVal = r.nextInt((max - min)) + min; 442 | int randomVal = 13; 443 | return (randomVal * 15) + 5; 444 | } 445 | 446 | //use for a random starting direction. 447 | public static int randomInt0and3() 448 | { 449 | Random r = new Random(); 450 | int min = 0; 451 | int max = 4; 452 | int randomVal = r.nextInt((max - min)) + min; 453 | return randomVal; 454 | } 455 | 456 | //pop up message method - currently not being used. 457 | public static void infoBox(String infoMessage) 458 | { 459 | JOptionPane.showMessageDialog(null, infoMessage, "Snake", JOptionPane.INFORMATION_MESSAGE); 460 | } 461 | 462 | //resets the timer if the snake finds food. 463 | private static void restartFoodTimer() 464 | { 465 | timeSinceLastFood = System.currentTimeMillis(); 466 | } 467 | 468 | //used to check if the snake is stalling. 469 | private static long getElapsedTimeSinceLastFood() 470 | { 471 | elapsedTimeSinceLastFood = (new Date()).getTime() - timeSinceLastFood; 472 | return elapsedTimeSinceLastFood; 473 | } 474 | 475 | private static void restartGameTimer() 476 | { 477 | gameTime = System.currentTimeMillis(); 478 | } 479 | 480 | private static long getElapsedGameTime() 481 | { 482 | elapsedGameTime = (new Date()).getTime() - gameTime; 483 | return elapsedGameTime; 484 | } 485 | 486 | //this is what gets passed to the NN for it to make a decision 487 | // public double[] getInputs() 488 | // { 489 | // double inputs[] = new double[11]; 490 | // inputs[0] = headBody.getX(); 491 | // inputs[1] = headBody.getY(); 492 | // inputs[2] = foodX; 493 | // inputs[3] = foodY; 494 | // double distToFoodX = Math.abs(headBody.getX() - foodX); 495 | // inputs[4] = distToFoodX; 496 | // double distToFoodY = Math.abs(headBody.getY() - foodY); 497 | // inputs[5] = distToFoodY; 498 | // double L = 0.0; 499 | // double R = 0.0; 500 | // double U = 0.0; 501 | // double D = 0.0; 502 | // if (leftDirFree) 503 | // L = 1.0; 504 | // if (rightDirFree) 505 | // R = 1.0; 506 | // if (upDirFree) 507 | // U = 1.0; 508 | // if (downDirFree) 509 | // D = 1.0; 510 | // inputs[6] = L; 511 | // inputs[7] = R; 512 | // inputs[8] = U; 513 | // inputs[9] = D; 514 | // inputs[10] = -1.0; 515 | // 516 | // normalizeInputs(inputs); 517 | // 518 | // return inputs; 519 | // } 520 | public double[] getInputs() 521 | { 522 | double inputs[] = new double[15]; 523 | inputs[0] = headBody.getX(); 524 | inputs[1] = headBody.getY(); 525 | inputs[2] = midBody.getX(); 526 | inputs[3] = midBody.getY(); 527 | inputs[4] = tailBody.getX(); 528 | inputs[5] = tailBody.getY(); 529 | inputs[6] = foodX; 530 | inputs[7] = foodY; 531 | double distToFoodX = Math.abs(headBody.getX() - foodX); 532 | inputs[8] = distToFoodX; 533 | double distToFoodY = Math.abs(headBody.getY() - foodY); 534 | inputs[9] = distToFoodY; 535 | double L = 0.0; 536 | double R = 0.0; 537 | double U = 0.0; 538 | double D = 0.0; 539 | if (leftDirFree) 540 | L = 1.0; 541 | if (rightDirFree) 542 | R = 1.0; 543 | if (upDirFree) 544 | U = 1.0; 545 | if (downDirFree) 546 | D = 1.0; 547 | inputs[10] = L; 548 | inputs[11] = R; 549 | inputs[12] = U; 550 | inputs[13] = D; 551 | inputs[14] = -1.0; 552 | 553 | normalizeInputs(inputs); 554 | 555 | return inputs; 556 | } 557 | 558 | //we are using a sigmoid activation function. all inputs must be normalized. 559 | //x and y are normalized to their own x and y mins and maxes. this ensure that their distances don't affect each other. 560 | private void normalizeInputs(double[] inputs) 561 | { 562 | maxX = -1; 563 | maxY = -1; 564 | minX = 999999; 565 | minY = 999999; 566 | for (int i = 0; i <= 9; i++) 567 | { 568 | if (i % 2 == 0) 569 | { 570 | if (inputs[i] > maxX) 571 | maxX = inputs[i]; 572 | if (inputs[i] < minX) 573 | minX = inputs[i]; 574 | } 575 | else 576 | { 577 | if (inputs[i] > maxY) 578 | maxY = inputs[i]; 579 | if (inputs[i] < minY) 580 | minY = inputs[i]; 581 | } 582 | } 583 | for (int i = 0; i <= 9; i++) 584 | { 585 | double numerator; 586 | double demoninator; 587 | double normalized; 588 | if (i % 2 == 0) 589 | { 590 | numerator = inputs[i] - minX; 591 | demoninator = maxX - minX; 592 | normalized = numerator / demoninator; 593 | inputs[i] = normalized; 594 | } 595 | else 596 | { 597 | numerator = inputs[i] - minY; 598 | demoninator = maxY - minY; 599 | normalized = numerator / demoninator; 600 | inputs[i] = normalized; 601 | } 602 | } 603 | } 604 | 605 | //take the snake's current pos for everything that we need for the NN inputs 606 | private static void updatePostitions() { 607 | //get the length of the snake 608 | int sizeXandY = 0; 609 | 610 | for (int i : x) 611 | if (i != 0) 612 | sizeXandY++; 613 | else 614 | break; 615 | //set snake body info 616 | headBody.setX(x[0]); 617 | headBody.setY(y[0]); 618 | Double halfPos = snakeLength / 2.0; 619 | String temp = String.valueOf(halfPos); 620 | if (temp.contains(".5")) 621 | halfPos = halfPos + 0.5; 622 | int hp = Integer.valueOf(halfPos.intValue()) - 1; //indexes start at a different places 623 | int tempX = x[hp]; 624 | int tempY = y[hp]; 625 | midBody.setX(x[hp]); 626 | midBody.setY(y[hp]); 627 | tailBody.setX(x[snakeLength]); 628 | tailBody.setY(y[snakeLength]); 629 | 630 | foodXY.setX(foodX); 631 | foodXY.setY(foodY); 632 | 633 | //available spaces the snake can more. true means the space is free, and the head can move there. 634 | leftDirFree = true; 635 | rightDirFree = true; 636 | upDirFree = true; 637 | downDirFree = true; 638 | //check the direction the snake is moving and rule out the opposite direction 639 | if (leftDirection) 640 | rightDirFree = false; 641 | else if (rightDirection) 642 | leftDirFree = false; 643 | else if (upDirection) 644 | downDirFree = false; 645 | else if (downDirection) 646 | upDirFree = false; 647 | //rule out boundary movements 648 | if (x[0] == 5) 649 | leftDirFree = false; 650 | else if (x[0] == 410) 651 | rightDirFree = false; 652 | if (y[0] == 5) 653 | upDirFree = false; 654 | else if (y[0] == 410) 655 | downDirFree = false; 656 | //rule out moving into the body. 657 | //check to the left 658 | tempX = x[0]; 659 | tempY = y[0]; 660 | if (leftDirFree) { 661 | int tempXX = tempX - DOT_SIZE; 662 | for (int i = dots; i > 0; i--) 663 | if ((i > 0) && (tempXX == x[i]) && (tempY == y[i])) { 664 | leftDirFree = false; 665 | break; 666 | } 667 | } 668 | if (rightDirFree) { 669 | int tempXX = tempX + DOT_SIZE; 670 | for (int i = dots; i > 0; i--) 671 | if ((i > 0) && (tempXX == x[i]) && (tempY == y[i])) { 672 | rightDirFree = false; 673 | break; 674 | } 675 | } 676 | if (upDirFree) { 677 | int tempYY = tempY - DOT_SIZE; 678 | for (int i = dots; i > 0; i--) 679 | if ((i > 0) && (tempX == x[i]) && (tempYY == y[i])) { 680 | upDirFree = false; 681 | break; 682 | } 683 | } 684 | if (downDirFree) { 685 | int tempYY = tempY + DOT_SIZE; 686 | for (int i = dots; i > 0; i--) 687 | if ((i > 0) && (tempX == x[i]) && (tempYY == y[i])) 688 | { 689 | downDirFree = false; 690 | break; 691 | } 692 | } 693 | } 694 | 695 | //drawing method that handles the changing and updting of pixels 696 | //called by the "repaint()" method. 697 | @Override 698 | protected void paintComponent(Graphics g) 699 | { 700 | super.paintComponent(g); 701 | if (inGame) { 702 | 703 | g.drawImage(imgApple, foodX, foodY, this); 704 | 705 | for (int z = 0; z < dots; z++) { 706 | if (z == 0) { 707 | g.drawImage(imgHead, x[z], y[z], this); 708 | } else { 709 | g.drawImage(imgBody, x[z], y[z], this); 710 | } 711 | } 712 | Toolkit.getDefaultToolkit().sync(); 713 | } 714 | else 715 | { 716 | gameOver(g); 717 | } 718 | } 719 | 720 | //handle the moving of the snake. 721 | private static void handleMovement() 722 | { 723 | //shift the tail up. 724 | for (int i = dots; i > 0; i--) 725 | { 726 | x[i] = x[(i - 1)]; 727 | y[i] = y[(i - 1)]; 728 | } 729 | 730 | //move the head 731 | if (leftDirection) 732 | x[0] -= DOT_SIZE; 733 | 734 | 735 | if (rightDirection) 736 | x[0] += DOT_SIZE; 737 | 738 | 739 | if (upDirection) 740 | y[0] -= DOT_SIZE; 741 | 742 | 743 | if (downDirection) 744 | y[0] += DOT_SIZE; 745 | 746 | stepsTaken++; 747 | stepsTakenSinceLastFood++; 748 | updatePostitions(); 749 | 750 | } 751 | 752 | //we only need to check where the head is, as the body can't go where the head hasn't been 753 | private static void checkForCollision() { 754 | 755 | for (int i = dots; i > 0; i--) 756 | if ((i > 3) && (x[0] == x[i]) && (y[0] == y[i])) 757 | inGame = false; 758 | 759 | if (y[0] >= boarderHeight) 760 | inGame = false; 761 | 762 | 763 | if (y[0] < 0) 764 | inGame = false; 765 | 766 | 767 | if (x[0] >= boarderWidth) 768 | inGame = false; 769 | 770 | 771 | if (x[0] < 0) 772 | inGame = false; 773 | } 774 | 775 | //this is not used for anything at the moment. 776 | private void gameOver(Graphics g) 777 | { 778 | 779 | } 780 | 781 | //have we eaten an apple? if so, increase length and place a new apple. 782 | //again, need to have check that the apple does not spawn on the body. 783 | private static void checkEatenApple() 784 | { 785 | if ((x[0] == foodX) && (y[0] == foodY)) 786 | { 787 | dots++; 788 | snakeLength++; 789 | applesEaten++; 790 | placeApple(); 791 | restartFoodTimer(); 792 | stepsTakenSinceLastFood = 0; 793 | } 794 | } 795 | 796 | //random location for the apple, that is not on the snake's body 797 | private static void placeApple() 798 | { 799 | int R = (int) (Math.random() * randomPos); 800 | foodX = (R * DOT_SIZE + 5); 801 | R = (int) (Math.random() * randomPos); 802 | foodY = (R * DOT_SIZE + 5); 803 | while (checkIfFoodPlacedOnBody()) 804 | { 805 | R = (int) (Math.random() * randomPos); 806 | foodX = (R * DOT_SIZE + 5); 807 | R = (int) (Math.random() * randomPos); 808 | foodY = (R * DOT_SIZE + 5); 809 | } 810 | } 811 | 812 | private static boolean checkIfFoodPlacedOnBody() 813 | { 814 | for (int i = dots; i > 0; i--) 815 | { 816 | if (foodX == x[i] && foodY == y[i]) 817 | return true; 818 | } 819 | return false; 820 | } 821 | 822 | //load the dots that will be drawn on the paint method. 823 | private static void initializeImages() 824 | { 825 | ImageIcon tempIcon = new ImageIcon("body.png"); 826 | imgBody = tempIcon.getImage(); 827 | tempIcon = new ImageIcon("head.png"); 828 | imgHead = tempIcon.getImage(); 829 | tempIcon = new ImageIcon("apple.png"); 830 | imgApple = tempIcon.getImage(); 831 | } 832 | 833 | //this is not used anymore as snake controls itself. if you want to be an asshole you can interrupt the snake's game. but maybe let him, you know? do his thang. 834 | private class TAdapter extends KeyAdapter 835 | { 836 | //what to do when a key is pressed. 837 | //will we eaten an apple? 838 | //will we hit the body or wall? 839 | //okay, cool, time to move. 840 | //repaint -> update the board. 841 | public void keyPressed() { 842 | 843 | } 844 | 845 | @Override 846 | public void keyPressed(KeyEvent e) 847 | { 848 | super.keyPressed(e); 849 | 850 | int key = e.getKeyCode(); 851 | 852 | if ((key == KeyEvent.VK_LEFT) && (!rightDirection)) { 853 | leftDirection = true; 854 | upDirection = false; 855 | downDirection = false; 856 | keyPressed(); 857 | } 858 | 859 | if ((key == KeyEvent.VK_RIGHT) && (!leftDirection)) { 860 | rightDirection = true; 861 | upDirection = false; 862 | downDirection = false; 863 | keyPressed(); 864 | } 865 | 866 | if ((key == KeyEvent.VK_UP) && (!downDirection)) { 867 | upDirection = true; 868 | rightDirection = false; 869 | leftDirection = false; 870 | keyPressed(); 871 | } 872 | 873 | if ((key == KeyEvent.VK_DOWN) && (!upDirection)) { 874 | downDirection = true; 875 | rightDirection = false; 876 | leftDirection = false; 877 | keyPressed(); 878 | } 879 | } 880 | } 881 | } 882 | -------------------------------------------------------------------------------- /src/XYPair.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Adriaan on 2016/11/10. 3 | */ 4 | public class XYPair 5 | { 6 | int x, y; 7 | 8 | public XYPair(int x, int y) 9 | { 10 | this.x = x; 11 | this.y = y; 12 | } 13 | 14 | public int getX() { 15 | return x; 16 | } 17 | 18 | public void setX(int x) { 19 | this.x = x; 20 | } 21 | 22 | public int getY() { 23 | return y; 24 | } 25 | 26 | public void setY(int y) { 27 | this.y = y; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/neuralNetWork.java: -------------------------------------------------------------------------------- 1 | import java.util.Random; 2 | 3 | /** 4 | * Created by Adriaan on 2016/11/10. 5 | */ 6 | public class neuralNetWork 7 | { 8 | int noInputsNeurons, noOutputNeurons, noHiddenNeurons; 9 | public double[][] wWeights; 10 | public double[][] vWeights; 11 | 12 | public neuralNetWork() 13 | { 14 | noInputsNeurons = SnakeGame.noInputsNeurons; //head x and y, mid x and y, tail x and y, is space free in the direction left, right up and down, food x and y, distance to food x and y, bias; 15 | noHiddenNeurons = SnakeGame.noHiddenNeurons; 16 | noOutputNeurons = SnakeGame.noOutputNeurons; 17 | vWeights = new double[noHiddenNeurons][noInputsNeurons]; //2D array for weights going from every input node, to every hidden node. 18 | wWeights = new double[noOutputNeurons][noHiddenNeurons]; //2D array for weights going from every hidden node to every output node 19 | 20 | //setWeightsOfNN(); 21 | } 22 | 23 | //will take the weights from the GA and set them here. 24 | public void setWeightsOfNN(double[][] inputtedVWeights, double[][] inputtedWWeights) 25 | { 26 | //give initial weights 27 | vWeights = inputtedVWeights; 28 | wWeights = inputtedWWeights; 29 | } 30 | 31 | //takes the inputs and then uses the NN to get a move out. 32 | public int getNextMove(int curDir, double[] inputs) 33 | { 34 | double highestActual = 0; 35 | int toOutput = 0; 36 | for (int j = 0; j <= noOutputNeurons - 1; j++) 37 | { 38 | double actual = calcuateFNetHiddenAndOutput(inputs).storedOutputNets[j]; 39 | if (actual > highestActual && curDir != j) 40 | { 41 | highestActual = actual; 42 | toOutput = j; 43 | } 44 | } 45 | return toOutput; 46 | } 47 | 48 | //for testing purposes 49 | public void setWeightsOfNN() 50 | { 51 | //give initial weights 52 | for (int i = 0; i<= noInputsNeurons -1; i++) //set the last one to be the bias 53 | for (int j = 0; j <= noHiddenNeurons-1; j++) 54 | vWeights[j][i] = randomDouble(); 55 | 56 | for (int j = 0; j <= noHiddenNeurons-1; j++) 57 | { 58 | for (int k = 0; k<= noOutputNeurons -1; k++) 59 | wWeights[k][j] = randomDouble(); 60 | } 61 | } 62 | 63 | //based on the weights, calculate the NN; 64 | private storedNetClass calcuateFNetHiddenAndOutput(double[] inputPattern) 65 | { 66 | double[] hiddenFNets = new double[noHiddenNeurons]; 67 | double[] outputFNets = new double[noOutputNeurons]; 68 | 69 | double outputNet, hiddenNet; 70 | for (int k = 0; k<= noOutputNeurons -1; k++) 71 | { 72 | outputNet = 0.0; 73 | for (int j = 0; j<= noHiddenNeurons-1; j++) 74 | { 75 | hiddenNet = 0.0; 76 | for (int i = 0; i<= noInputsNeurons-1; i++) 77 | { 78 | hiddenNet = hiddenNet + (vWeights[j][i] * inputPattern[i]); 79 | } 80 | if (j == (noHiddenNeurons -1)) //bias for hidden layer 81 | hiddenFNets[j] = -1.0; 82 | else 83 | hiddenFNets[j] = getActivationFunctionValue(hiddenNet, "sig"); 84 | 85 | outputNet = outputNet + (wWeights[k][j] * hiddenFNets[j]); 86 | } 87 | outputFNets[k] = getActivationFunctionValue(outputNet, "sig"); 88 | } 89 | return new storedNetClass(hiddenFNets, outputFNets); 90 | } 91 | 92 | 93 | public double randomDouble() 94 | { 95 | Random r = new Random(); 96 | double min = -1 / Math.sqrt(noInputsNeurons); 97 | double max = 1/ Math.sqrt(noInputsNeurons); 98 | double randomVal = (r.nextDouble() * (max - min)) + min; 99 | return randomVal; 100 | } 101 | 102 | private double getActivationFunctionValue(double in, String activFunc) 103 | { 104 | double result; 105 | if (activFunc.equals("lin")) //linear 106 | { 107 | return in; 108 | } 109 | else //if (activFunc.equals("sig")) //sigmoid 110 | { 111 | result = 1.0 / (1.0 + (Math.exp(-in))); 112 | return result; 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/storedNetClass.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Adriaan on 2016/11/11. 3 | */ 4 | public class storedNetClass 5 | { 6 | public double[] storedHiddenNets; 7 | public double[] storedOutputNets; 8 | 9 | public storedNetClass(double[] storedHiddenNets, double[] storedOutputNets) { 10 | this.storedHiddenNets = storedHiddenNets; 11 | this.storedOutputNets = storedOutputNets; 12 | } 13 | } 14 | --------------------------------------------------------------------------------