├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ └── java │ │ └── de │ │ └── thomas │ │ └── creatures │ │ └── implementation │ │ ├── WorldCreator.java │ │ ├── ai │ │ ├── DoNothingAI.java │ │ ├── CreatureAI.java │ │ └── BasicAI.java │ │ ├── util │ │ ├── VariationHelper.java │ │ ├── AssertionException.java │ │ └── AssertionHelper.java │ │ ├── model │ │ ├── Food.java │ │ ├── WorldModel.java │ │ ├── WorldFactory.java │ │ └── Creature.java │ │ ├── controller │ │ ├── WorldController.java │ │ └── WorldUpdater.java │ │ ├── statistics │ │ ├── StatElement.java │ │ ├── StatisticsSerializer.java │ │ └── Statistics.java │ │ ├── view │ │ ├── WorldView.java │ │ ├── WorldInputListener.java │ │ ├── CreateWorldView.java │ │ ├── StatisticsView.java │ │ ├── CreateCreatureView.java │ │ ├── MainWindow.java │ │ └── CreateCreaturesView.java │ │ └── CreaturesMain.java └── test │ └── java │ └── de │ └── thomas │ └── creatures │ └── tests │ └── FirstTest.java ├── README.md ├── .gitignore ├── gradlew.bat ├── gradlew └── LICENSE /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thopit/Creatures/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/WorldCreator.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation; 2 | 3 | import de.thomas.creatures.implementation.model.WorldModel; 4 | 5 | public interface WorldCreator { 6 | void setupWorld(WorldModel worldModel); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/ai/DoNothingAI.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.ai; 2 | 3 | public class DoNothingAI extends CreatureAI { 4 | 5 | @Override 6 | public void init() { 7 | //Nothing is done here 8 | } 9 | 10 | @Override 11 | public void update() { 12 | //Nothing is done here 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/util/VariationHelper.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.util; 2 | 3 | public class VariationHelper { 4 | public static double mutationFactor(double mutationRate) { 5 | double mutationDelta = Math.random() * mutationRate; 6 | 7 | if (Math.random() > 0.5) { 8 | return 1 + mutationDelta; 9 | } else { 10 | return 1 - mutationDelta; 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/util/AssertionException.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.util; 2 | 3 | public class AssertionException extends Exception { 4 | private static final long serialVersionUID = -4980406126997282476L; 5 | private final String message; 6 | 7 | public AssertionException(String message) { 8 | this.message = message; 9 | } 10 | 11 | @Override 12 | public String getMessage() { 13 | return message; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/model/Food.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.model; 2 | 3 | import java.awt.geom.Point2D; 4 | 5 | public class Food { 6 | private Point2D.Double position; 7 | private int value; 8 | 9 | public Food(Point2D.Double position, int value) { 10 | this.position = position; 11 | this.value = value; 12 | } 13 | 14 | public Point2D.Double getPosition() { 15 | return position; 16 | } 17 | 18 | public void setPosition(Point2D.Double position) { 19 | this.position = position; 20 | } 21 | 22 | public int getValue() { 23 | return value; 24 | } 25 | 26 | public void setValue(int value) { 27 | this.value = value; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/ai/CreatureAI.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.ai; 2 | 3 | import de.thomas.creatures.implementation.model.Creature; 4 | import de.thomas.creatures.implementation.model.WorldModel; 5 | 6 | public abstract class CreatureAI { 7 | protected WorldModel worldModel; 8 | protected Creature creature; 9 | 10 | public abstract void init(); 11 | 12 | public abstract void update(); 13 | 14 | public Creature getCreature() { 15 | return creature; 16 | } 17 | 18 | public void setCreature(Creature creature) { 19 | this.creature = creature; 20 | } 21 | 22 | public WorldModel getWorldModel() { 23 | return worldModel; 24 | } 25 | 26 | public void setWorldModel(WorldModel model) { 27 | this.worldModel = model; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Creatures 2 | 3 | ![Main view of creatures](http://i.imgur.com/03Cn22F.png) 4 | Creatures is a small evolution simulator. It's about creatures trying to survive and procreate. 5 | Each new creature inherits the properties of its parents, combined with mutations. This leads to gradual changes in the 6 | distribution of certain attributes. These changes can be graphically seen in a statistics module. 7 | 8 | ### Build 9 | 10 | This project uses [Gradle](https://gradle.org/). The gradle wrapper is also included, so gradle doesn't need to be 11 | installed manually. 12 | Run ```gradlew build``` to build the project and ```gradlew run``` to execute it. 13 | 14 | ### Documentation 15 | 16 | For more information take a look at the [Wiki](https://github.com/thopit/Creatures/wiki). 17 | 18 | ### Download 19 | 20 | Get the latest release [here](https://github.com/thopit/Creatures/releases). 21 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/util/AssertionHelper.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.util; 2 | 3 | public class AssertionHelper { 4 | public static void checkSmallerZero(double number, String name) throws AssertionException { 5 | if (number < 0) { 6 | throw new AssertionException(name + " must be at least 0"); 7 | } 8 | } 9 | 10 | public static void checkSmallerEqualThan(double number, double checkValue, String name) throws AssertionException { 11 | if (number > checkValue) { 12 | throw new AssertionException(name + " must be at most " + checkValue); 13 | } 14 | } 15 | 16 | public static void checkGreaterEqualThan(double number, double checkValue, String name) throws AssertionException { 17 | if (number < checkValue) { 18 | throw new AssertionException(name + " must be at least " + checkValue); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io 2 | 3 | ### Java ### 4 | *.class 5 | 6 | # Mobile Tools for Java (J2ME) 7 | .mtj.tmp/ 8 | 9 | # Package Files # 10 | *.jar 11 | *.war 12 | *.ear 13 | 14 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 15 | hs_err_pid* 16 | 17 | 18 | ### Eclipse ### 19 | *.pydevproject 20 | .metadata 21 | .gradle 22 | bin/ 23 | tmp/ 24 | *.tmp 25 | *.bak 26 | *.swp 27 | *~.nib 28 | local.properties 29 | .settings/ 30 | .loadpath 31 | 32 | # Eclipse Core 33 | .project 34 | 35 | # External tool builders 36 | .externalToolBuilders/ 37 | 38 | # Locally stored "Eclipse launch configurations" 39 | *.launch 40 | 41 | # CDT-specific 42 | .cproject 43 | 44 | # JDT-specific (Eclipse Java Development Tools) 45 | .classpath 46 | 47 | # PDT-specific 48 | .buildpath 49 | 50 | # sbteclipse plugin 51 | .target 52 | 53 | # TeXlipse plugin 54 | .texlipse 55 | 56 | 57 | ### Gradle ### 58 | .gradle 59 | build/ 60 | 61 | # Ignore Gradle GUI config 62 | gradle-app.setting 63 | 64 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 65 | !gradle-wrapper.jar 66 | 67 | 68 | ### Linux ### 69 | *~ 70 | 71 | # KDE directory preferences 72 | .directory 73 | 74 | # Linux trash folder which might appear on any partition or disk 75 | .Trash-* 76 | 77 | .idea 78 | -------------------------------------------------------------------------------- /src/test/java/de/thomas/creatures/tests/FirstTest.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.tests; 2 | 3 | 4 | import de.thomas.creatures.implementation.ai.BasicAI; 5 | import de.thomas.creatures.implementation.model.Creature; 6 | import de.thomas.creatures.implementation.model.Creature.Gender; 7 | import de.thomas.creatures.implementation.model.WorldModel; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import java.awt.geom.Point2D; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | import static org.junit.jupiter.api.Assertions.assertTrue; 14 | 15 | //TODO Add tests! 16 | public class FirstTest { 17 | 18 | @Test 19 | public void testWayPoints() { 20 | int width = 1000; 21 | int height = 500; 22 | int foodCreationRate = 50; 23 | 24 | WorldModel model = new WorldModel(width, height, foodCreationRate); 25 | Creature creature = new Creature(new Point2D.Double(100, 100), Gender.MALE); 26 | 27 | BasicAI basicAI = new BasicAI(); 28 | basicAI.setCreature(creature); 29 | basicAI.setWorldModel(model); 30 | 31 | basicAI.init(); 32 | 33 | for (Point2D.Double p : basicAI.getWayPoints()) { 34 | assertTrue(p.x <= width); 35 | assertTrue(p.x >= 0); 36 | assertTrue(p.y <= height); 37 | assertTrue(p.y >= 0); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/model/WorldModel.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.model; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class WorldModel { 7 | //TODO add better access methods for creatures and food (grid based etc.) 8 | public static int maxFoodEnergy = 100; 9 | public static int maxFoodAmount = 500; 10 | public static double baseEnergyDepletionRate = 1; 11 | public static double speedEnergyDepletionFactor = 0.5; 12 | public static double mutationRate = 0.1; 13 | public static double speedFactor = 1; 14 | 15 | private final List creatures; 16 | private final List foods; 17 | private double width; 18 | private double height; 19 | private int foodCreationRate; 20 | 21 | public WorldModel(int width, int height, int foodCreationRate) { 22 | creatures = new ArrayList<>(); 23 | foods = new ArrayList<>(); 24 | this.width = width; 25 | this.height = height; 26 | this.foodCreationRate = foodCreationRate; 27 | } 28 | 29 | public List getCreatures() { 30 | return creatures; 31 | } 32 | 33 | public void addCreature(Creature creature) { 34 | creatures.add(creature); 35 | } 36 | 37 | public void removeCreature(Creature creature) { 38 | creatures.remove(creature); 39 | } 40 | 41 | public List getFoods() { 42 | return foods; 43 | } 44 | 45 | public void addFood(Food food) { 46 | foods.add(food); 47 | } 48 | 49 | public void removeFood(Food food) { 50 | foods.remove(food); 51 | } 52 | 53 | public double getWidth() { 54 | return width; 55 | } 56 | 57 | public void setWidth(double width) { 58 | this.width = width; 59 | } 60 | 61 | public double getHeight() { 62 | return height; 63 | } 64 | 65 | public void setHeight(double height) { 66 | this.height = height; 67 | } 68 | 69 | public int getFoodCreationRate() { 70 | return foodCreationRate; 71 | } 72 | 73 | public void setFoodCreationRate(int foodCreationRate) { 74 | this.foodCreationRate = foodCreationRate; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/model/WorldFactory.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.model; 2 | 3 | import de.thomas.creatures.implementation.ai.BasicAI; 4 | import de.thomas.creatures.implementation.ai.CreatureAI; 5 | import de.thomas.creatures.implementation.model.Creature.Gender; 6 | 7 | import java.awt.geom.Point2D; 8 | 9 | public class WorldFactory { 10 | 11 | public static WorldModel createTestWorld() { 12 | WorldModel world = new WorldModel(1000, 800, 50); 13 | Creature[] creatures = {new Creature(new Point2D.Double(100, 100), Gender.MALE), 14 | new Creature(new Point2D.Double(200, 100), Gender.FEMALE)}; 15 | //Creature[] creatures = {new Creature(new Point2D.Double(100, 100), Gender.MALE)}; 16 | 17 | CreatureAI ai = new BasicAI(); 18 | 19 | creatures[0].setAi(ai); 20 | 21 | ai.setWorldModel(world); 22 | ai.setCreature(creatures[0]); 23 | ai.init(); 24 | 25 | 26 | Food[] foods = {new Food(new Point2D.Double(150, 150), 100), 27 | new Food(new Point2D.Double(230, 80), 100) 28 | }; 29 | 30 | for (Creature c : creatures) { 31 | world.addCreature(c); 32 | } 33 | 34 | for (Food f : foods) { 35 | world.addFood(f); 36 | } 37 | 38 | return world; 39 | } 40 | 41 | public static WorldModel createEmptyWorld(int width, int height, int foodRate) { 42 | WorldModel world = new WorldModel(width, height, foodRate); 43 | return world; 44 | } 45 | 46 | public static WorldModel createBasicWorld(int width, int height, int creatureAmount, int foodRate) { 47 | WorldModel world = new WorldModel(width, height, foodRate); 48 | Creature[] creatures = new Creature[creatureAmount]; 49 | 50 | for (int i = 0; i < creatureAmount; i++) { 51 | double posX = width * Math.random(); 52 | double posY = height * Math.random(); 53 | Gender gender; 54 | 55 | if (Math.random() >= 0.5) { 56 | gender = Gender.MALE; 57 | } else { 58 | gender = Gender.FEMALE; 59 | } 60 | 61 | creatures[i] = new Creature(new Point2D.Double(posX, posY), gender); 62 | 63 | CreatureAI ai = new BasicAI(); 64 | creatures[i].setAi(ai); 65 | 66 | ai.setCreature(creatures[i]); 67 | ai.setWorldModel(world); 68 | ai.init(); 69 | 70 | world.addCreature(creatures[i]); 71 | } 72 | 73 | Food[] foods = new Food[creatureAmount * 10]; 74 | for (int i = 0; i < creatureAmount * 10; i++) { 75 | double posX = width * Math.random(); 76 | double posY = height * Math.random(); 77 | foods[i] = new Food(new Point2D.Double(posX, posY), (int) (Math.random() * 100)); 78 | 79 | world.addFood(foods[i]); 80 | } 81 | 82 | return world; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/controller/WorldController.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.controller; 2 | 3 | import de.thomas.creatures.implementation.model.Creature; 4 | import de.thomas.creatures.implementation.model.WorldModel; 5 | import de.thomas.creatures.implementation.view.MainWindow; 6 | import de.thomas.creatures.implementation.view.WorldView; 7 | 8 | public class WorldController { 9 | private WorldModel worldModel; 10 | private WorldView worldView; 11 | private WorldUpdater worldUpdater; 12 | private MainWindow mainWindow; 13 | 14 | public WorldController(WorldModel worldModel, WorldView worldView) { 15 | this.worldModel = worldModel; 16 | this.worldView = worldView; 17 | worldUpdater = new WorldUpdater(worldModel, this); 18 | } 19 | 20 | public void updateWorld(double delta) { 21 | worldUpdater.updateWorld(delta); 22 | } 23 | 24 | public void changeZoomFactor(int zoomChange, boolean relative) { 25 | int zoomFactor = worldView.getZoomFactor(); 26 | 27 | if (zoomFactor + zoomChange > 0) { 28 | worldView.setZoomFactor(zoomFactor + zoomChange); 29 | 30 | if (relative) { 31 | if (zoomChange > 0) { 32 | worldView.setOffsetX(worldView.getOffsetX() + (int) ((worldView.getSize().width) * 0.8) / 2); 33 | worldView.setOffsetY(worldView.getOffsetY() + worldView.getSize().height / 2); 34 | } else { 35 | worldView.setOffsetX(worldView.getOffsetX() - (int) ((worldView.getSize().width) * 0.8) / 2); 36 | worldView.setOffsetY(worldView.getOffsetY() - worldView.getSize().height / 2); 37 | } 38 | } 39 | } 40 | } 41 | 42 | public void changeOffsetX(int offsetChange) { 43 | int offsetX = worldView.getOffsetX(); 44 | worldView.setOffsetX(offsetX + offsetChange); 45 | } 46 | 47 | public void changeOffsetY(int offsetChange) { 48 | int offsetY = worldView.getOffsetY(); 49 | worldView.setOffsetY(offsetY + offsetChange); 50 | } 51 | 52 | public void addCreature(Creature creature) { 53 | worldModel.addCreature(creature); 54 | creature.getAi().setCreature(creature); 55 | creature.getAi().setWorldModel(worldModel); 56 | creature.getAi().init(); 57 | } 58 | 59 | public void changeSpeed(double speedChange) { 60 | double change = WorldModel.speedFactor + speedChange; 61 | setSpeed(change); 62 | } 63 | 64 | public void setSpeed(double speed) { 65 | if (speed >= 0 && speed <= 15) { 66 | WorldModel.speedFactor = speed; 67 | mainWindow.setSpeedSlider(speed); 68 | } 69 | } 70 | 71 | public void setMainWindow(MainWindow mainWindow) { 72 | this.mainWindow = mainWindow; 73 | } 74 | 75 | public void setViewInputFocus() { 76 | mainWindow.setViewInputFocus(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/statistics/StatElement.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.statistics; 2 | 3 | public class StatElement { 4 | private final int creatureAmount; 5 | private final double averageLife; 6 | private final double averageMaxLife; 7 | private final double averageEnergy; 8 | private final double averageMaxEnergy; 9 | private final double averageSpeed; 10 | private final double averageVisionRange; 11 | private final double averageMatingEnergyNeeded; 12 | private final double averageBreedLength; 13 | private final double averageBreedProgressSpeed; 14 | private final double genderRatio; 15 | private final double pregnancyRatio; 16 | 17 | public StatElement(int creatureAmount, double averageLife, 18 | double averageMaxLife, double averageEnergy, 19 | double averageMaxEnergy, double averageSpeed, 20 | double averageVisionRange, double averageMatingEnergyNeeded, 21 | double averageBreedLength, double averageBreedProgressSpeed, 22 | double genderRatio, double pregnancyRatio) { 23 | super(); 24 | this.creatureAmount = creatureAmount; 25 | this.averageLife = averageLife; 26 | this.averageMaxLife = averageMaxLife; 27 | this.averageEnergy = averageEnergy; 28 | this.averageMaxEnergy = averageMaxEnergy; 29 | this.averageSpeed = averageSpeed; 30 | this.averageVisionRange = averageVisionRange; 31 | this.averageMatingEnergyNeeded = averageMatingEnergyNeeded; 32 | this.averageBreedLength = averageBreedLength; 33 | this.averageBreedProgressSpeed = averageBreedProgressSpeed; 34 | this.genderRatio = genderRatio; 35 | this.pregnancyRatio = pregnancyRatio; 36 | } 37 | 38 | public int getCreatureAmount() { 39 | return creatureAmount; 40 | } 41 | 42 | public double getAverageLife() { 43 | return averageLife; 44 | } 45 | 46 | public double getAverageMaxLife() { 47 | return averageMaxLife; 48 | } 49 | 50 | public double getAverageEnergy() { 51 | return averageEnergy; 52 | } 53 | 54 | public double getAverageMaxEnergy() { 55 | return averageMaxEnergy; 56 | } 57 | 58 | public double getAverageSpeed() { 59 | return averageSpeed; 60 | } 61 | 62 | public double getAverageVisionRange() { 63 | return averageVisionRange; 64 | } 65 | 66 | public double getAverageMatingEnergyNeeded() { 67 | return averageMatingEnergyNeeded; 68 | } 69 | 70 | public double getAverageBreedLength() { 71 | return averageBreedLength; 72 | } 73 | 74 | public double getAverageBreedProgressSpeed() { 75 | return averageBreedProgressSpeed; 76 | } 77 | 78 | public double getGenderRatio() { 79 | return genderRatio; 80 | } 81 | 82 | public double getPregnancyRatio() { 83 | return pregnancyRatio; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/statistics/StatisticsSerializer.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.statistics; 2 | 3 | import java.io.File; 4 | import java.io.FileWriter; 5 | import java.io.IOException; 6 | import java.text.DecimalFormat; 7 | import java.util.List; 8 | 9 | public class StatisticsSerializer { 10 | public void exportStatistics(List elements, File file) { 11 | try { 12 | FileWriter writer = new FileWriter(file); 13 | writer.write(getSerialization(elements)); 14 | writer.close(); 15 | } catch (IOException e) { 16 | e.printStackTrace(); 17 | } 18 | } 19 | 20 | private String getSerialization(List elements) { 21 | StringBuilder builder = new StringBuilder(); 22 | DecimalFormat df = new DecimalFormat("#.0000"); 23 | 24 | builder.append("\"Creature Amount\""); 25 | builder.append(";"); 26 | builder.append("\"Average Life\""); 27 | builder.append(";"); 28 | builder.append("\"Average Max Life\""); 29 | builder.append(";"); 30 | builder.append("\"Average Energy\""); 31 | builder.append(";"); 32 | builder.append("\"Average Max Energy\""); 33 | builder.append(";"); 34 | builder.append("\"Average Speed\""); 35 | builder.append(";"); 36 | builder.append("\"Average Vision Range\""); 37 | builder.append(";"); 38 | builder.append("\"Average Mating Energy Needed\""); 39 | builder.append(";"); 40 | builder.append("\"Average Breed Length\""); 41 | builder.append(";"); 42 | builder.append("\"Average Breed Progress Speed\""); 43 | builder.append(";"); 44 | builder.append("\"Gender Ratio\""); 45 | builder.append(";"); 46 | builder.append("\"Pregnancy Ratio\""); 47 | builder.append(System.lineSeparator()); 48 | 49 | for (StatElement element : elements) { 50 | builder.append(element.getCreatureAmount()); 51 | builder.append(";"); 52 | builder.append(df.format(element.getAverageLife())); 53 | builder.append(";"); 54 | builder.append(df.format(element.getAverageMaxLife())); 55 | builder.append(";"); 56 | builder.append(df.format(element.getAverageEnergy())); 57 | builder.append(";"); 58 | builder.append(df.format(element.getAverageMaxEnergy())); 59 | builder.append(";"); 60 | builder.append(df.format(element.getAverageSpeed())); 61 | builder.append(";"); 62 | builder.append(df.format(element.getAverageVisionRange())); 63 | builder.append(";"); 64 | builder.append(df.format(element.getAverageMatingEnergyNeeded())); 65 | builder.append(";"); 66 | builder.append(df.format(element.getAverageBreedLength())); 67 | builder.append(";"); 68 | builder.append(df.format(element.getAverageBreedProgressSpeed())); 69 | builder.append(";"); 70 | builder.append(df.format(element.getGenderRatio())); 71 | builder.append(";"); 72 | builder.append(df.format(element.getPregnancyRatio())); 73 | builder.append(System.lineSeparator()); 74 | } 75 | 76 | return builder.toString(); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/view/WorldView.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.view; 2 | 3 | import de.thomas.creatures.implementation.model.Creature; 4 | import de.thomas.creatures.implementation.model.Creature.Gender; 5 | import de.thomas.creatures.implementation.model.Food; 6 | import de.thomas.creatures.implementation.model.WorldModel; 7 | 8 | import javax.swing.*; 9 | import java.awt.*; 10 | 11 | public class WorldView extends JPanel { 12 | private static final long serialVersionUID = 5628493124295876371L; 13 | public static int CREATURE_SIZE = 12; 14 | public static int FOOD_SIZE = 8; 15 | public static boolean DEBUG_MODE = true; 16 | 17 | private transient WorldModel world; 18 | 19 | private int zoomFactor; 20 | private int offsetX; 21 | private int offsetY; 22 | 23 | public WorldView(WorldModel world) { 24 | this.world = world; 25 | zoomFactor = 1; 26 | 27 | setFocusable(true); 28 | } 29 | 30 | public void paint(Graphics g) { 31 | super.paint(g); 32 | 33 | //Draw food 34 | g.setColor(new Color(116, 195, 101)); 35 | for (Food food : world.getFoods()) { 36 | g.fillOval((int) (food.getPosition().x / zoomFactor + (double) offsetX / zoomFactor), 37 | (int) (food.getPosition().y / zoomFactor + (double) offsetY / zoomFactor), 38 | FOOD_SIZE / zoomFactor, 39 | FOOD_SIZE / zoomFactor); 40 | } 41 | 42 | //Draw creatures 43 | for (Creature creature : world.getCreatures()) { 44 | if (creature.getGender() == Gender.MALE) { 45 | g.setColor(new Color(0, 0, 255)); 46 | } else { 47 | g.setColor(new Color(255, 0, 0)); 48 | } 49 | 50 | g.fillOval((int) (creature.getPosition().x / zoomFactor + (double) offsetX / zoomFactor), 51 | (int) (creature.getPosition().y / zoomFactor + (double) offsetY / zoomFactor), 52 | CREATURE_SIZE / zoomFactor, 53 | CREATURE_SIZE / zoomFactor); 54 | } 55 | 56 | if (DEBUG_MODE) { 57 | for (Creature creature : world.getCreatures()) { 58 | g.setColor(Color.black); 59 | String displayString = ((int) ((creature.getLife() / creature.getMaxLife()) * 100)) + " | " 60 | + ((int) creature.getEnergy()); 61 | 62 | g.drawString(displayString, 63 | (int) (creature.getPosition().x / zoomFactor + (double) offsetX / zoomFactor), 64 | (int) (creature.getPosition().y / zoomFactor + (double) offsetY / zoomFactor)); 65 | } 66 | } 67 | } 68 | 69 | public int getZoomFactor() { 70 | return zoomFactor; 71 | } 72 | 73 | public void setZoomFactor(int zoomFactor) { 74 | this.zoomFactor = zoomFactor; 75 | } 76 | 77 | public int getOffsetX() { 78 | return offsetX; 79 | } 80 | 81 | public void setOffsetX(int offsetX) { 82 | this.offsetX = offsetX; 83 | } 84 | 85 | public int getOffsetY() { 86 | return offsetY; 87 | } 88 | 89 | public void setOffsetY(int offsetY) { 90 | this.offsetY = offsetY; 91 | } 92 | 93 | public void setWorld(WorldModel worldModel) { 94 | this.world = worldModel; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/ai/BasicAI.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.ai; 2 | 3 | import de.thomas.creatures.implementation.model.Creature; 4 | import de.thomas.creatures.implementation.model.Food; 5 | 6 | import java.awt.geom.Point2D; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class BasicAI extends CreatureAI { 11 | private final List wayPoints; 12 | 13 | public BasicAI() { 14 | wayPoints = new ArrayList<>(); 15 | } 16 | 17 | @Override 18 | public void init() { 19 | initWayPoints(); 20 | } 21 | 22 | private void initWayPoints() { 23 | int WAY_POINT_NUMBER = 10; 24 | int wayPointNumberX = WAY_POINT_NUMBER; 25 | int wayPointNumberY = (int) (WAY_POINT_NUMBER * (worldModel.getHeight() / worldModel.getWidth())); 26 | 27 | double deviationX = worldModel.getWidth() / wayPointNumberX; 28 | double deviationY = worldModel.getHeight() / wayPointNumberY; 29 | 30 | double maxRandom = deviationX / 2; 31 | 32 | for (int y = 0; y < wayPointNumberY; y++) { 33 | for (int x = 0; x < wayPointNumberX; x++) { 34 | Point2D.Double point = new Point2D.Double(x * deviationX + Math.random() * maxRandom, 35 | y * deviationY + Math.random() * maxRandom); 36 | 37 | wayPoints.add(point); 38 | } 39 | } 40 | } 41 | 42 | @Override 43 | public void update() { 44 | //Move random 45 | if (creature.getTarget() == null) { 46 | Point2D.Double point = wayPoints.get((int) (Math.random() * wayPoints.size())); 47 | creature.setTarget(point); 48 | } 49 | 50 | Food nearestFood = getNearestFood(); 51 | 52 | //Goto food 53 | if (nearestFood != null && creature.getEnergy() + nearestFood.getValue() <= creature.getMaxEnergy()) { 54 | creature.setTarget(nearestFood.getPosition()); 55 | } 56 | 57 | //Goto mate 58 | Creature nearestMate = getNearestMate(); 59 | 60 | if (nearestMate != null) { 61 | creature.setTarget(nearestMate.getPosition()); 62 | } 63 | } 64 | 65 | private Food getNearestFood() { 66 | Food nearestFood = null; 67 | double nearestDistance = Double.MAX_VALUE; 68 | 69 | for (Food f : getWorldModel().getFoods()) { 70 | double distance = f.getPosition().distance(getCreature().getPosition()); 71 | 72 | if (distance < getCreature().getVisionRange() && distance < nearestDistance) { 73 | nearestFood = f; 74 | nearestDistance = distance; 75 | } 76 | } 77 | 78 | return nearestFood; 79 | } 80 | 81 | private Creature getNearestMate() { 82 | Creature nearestMate = null; 83 | double nearestDistance = Double.MAX_VALUE; 84 | 85 | for (Creature c : getWorldModel().getCreatures()) { 86 | double distance = c.getPosition().distance(getCreature().getPosition()); 87 | 88 | if (getCreature().getGender() != c.getGender() 89 | && distance < getCreature().getVisionRange() 90 | && distance < nearestDistance 91 | && getCreature().getEnergy() > getCreature().getMatingEnergyNeeded() 92 | && c.getEnergy() > c.getMatingEnergyNeeded() && 93 | !getCreature().isPregnant() && !c.isPregnant()) { 94 | 95 | nearestMate = c; 96 | nearestDistance = distance; 97 | } 98 | } 99 | 100 | return nearestMate; 101 | } 102 | 103 | public List getWayPoints() { 104 | return wayPoints; 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/view/WorldInputListener.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.view; 2 | 3 | import de.thomas.creatures.implementation.controller.WorldController; 4 | 5 | import java.awt.*; 6 | import java.awt.event.*; 7 | import java.util.HashSet; 8 | import java.util.Iterator; 9 | import java.util.Set; 10 | 11 | public class WorldInputListener implements KeyListener, MouseListener, MouseWheelListener { 12 | private final Set pressed = new HashSet(); 13 | private WorldController controller; 14 | private boolean rightButtonPressed = false; 15 | private boolean leftButtonPressed = false; 16 | private Point lastPosition = MouseInfo.getPointerInfo().getLocation(); 17 | 18 | public WorldInputListener(WorldController controller) { 19 | this.controller = controller; 20 | } 21 | 22 | @Override 23 | public void keyTyped(KeyEvent e) { 24 | //Nothing is done here 25 | } 26 | 27 | @Override 28 | public void keyPressed(KeyEvent e) { 29 | pressed.add(e.getKeyCode()); 30 | } 31 | 32 | @Override 33 | public void keyReleased(KeyEvent e) { 34 | pressed.remove(e.getKeyCode()); 35 | } 36 | 37 | @Override 38 | public void mouseClicked(MouseEvent e) { 39 | //Nothing is done here 40 | } 41 | 42 | @Override 43 | public void mousePressed(MouseEvent e) { 44 | controller.setViewInputFocus(); 45 | 46 | if (e.getButton() == MouseEvent.BUTTON1) { 47 | leftButtonPressed = true; 48 | } else if (e.getButton() == MouseEvent.BUTTON3) { 49 | rightButtonPressed = true; 50 | } 51 | } 52 | 53 | @Override 54 | public void mouseReleased(MouseEvent e) { 55 | if (e.getButton() == MouseEvent.BUTTON1) { 56 | leftButtonPressed = false; 57 | } else if (e.getButton() == MouseEvent.BUTTON3) { 58 | rightButtonPressed = false; 59 | } 60 | } 61 | 62 | @Override 63 | public void mouseEntered(MouseEvent e) { 64 | //Nothing is done here 65 | } 66 | 67 | @Override 68 | public void mouseExited(MouseEvent e) { 69 | //Nothing is done here 70 | } 71 | 72 | @Override 73 | public void mouseWheelMoved(MouseWheelEvent e) { 74 | controller.changeZoomFactor(e.getWheelRotation(), true); 75 | } 76 | 77 | public void handlePressedKeys() { 78 | Iterator iterator = pressed.iterator(); 79 | 80 | while (iterator.hasNext()) { 81 | int keyCode = iterator.next(); 82 | 83 | if (keyCode == KeyEvent.VK_LEFT) { 84 | controller.changeOffsetX(5); 85 | } else if (keyCode == KeyEvent.VK_RIGHT) { 86 | controller.changeOffsetX(-5); 87 | } else if (keyCode == KeyEvent.VK_UP) { 88 | controller.changeOffsetY(5); 89 | } else if (keyCode == KeyEvent.VK_DOWN) { 90 | controller.changeOffsetY(-5); 91 | } else if (keyCode == KeyEvent.VK_ADD) { 92 | controller.changeSpeed(1); 93 | iterator.remove(); 94 | } else if (keyCode == KeyEvent.VK_SUBTRACT) { 95 | controller.changeSpeed(-1); 96 | iterator.remove(); 97 | } 98 | } 99 | } 100 | 101 | public void handlePressedMouseButtons(int zoomFactor) { 102 | double deltaX = (double) MouseInfo.getPointerInfo().getLocation().x - lastPosition.x; 103 | double deltaY = (double) MouseInfo.getPointerInfo().getLocation().y - lastPosition.y; 104 | 105 | if (rightButtonPressed || leftButtonPressed) { 106 | if (Math.abs(deltaX) > 0) { 107 | controller.changeOffsetX(((int) deltaX) * zoomFactor); 108 | } 109 | 110 | if (Math.abs(deltaY) > 0) { 111 | controller.changeOffsetY(((int) deltaY) * zoomFactor); 112 | } 113 | } 114 | 115 | lastPosition = MouseInfo.getPointerInfo().getLocation(); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/CreaturesMain.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation; 2 | 3 | 4 | import de.thomas.creatures.implementation.controller.WorldController; 5 | import de.thomas.creatures.implementation.model.Creature; 6 | import de.thomas.creatures.implementation.model.WorldFactory; 7 | import de.thomas.creatures.implementation.model.WorldModel; 8 | import de.thomas.creatures.implementation.statistics.Statistics; 9 | import de.thomas.creatures.implementation.view.MainWindow; 10 | import de.thomas.creatures.implementation.view.WorldInputListener; 11 | import de.thomas.creatures.implementation.view.WorldView; 12 | 13 | import javax.swing.*; 14 | import java.awt.event.ActionEvent; 15 | import java.awt.event.ActionListener; 16 | 17 | public class CreaturesMain implements WorldCreator, ActionListener { 18 | private final int REFRESH_TIME = 15; 19 | private Timer timer; 20 | private WorldModel worldModel; 21 | private WorldView view; 22 | private WorldController controller; 23 | private WorldInputListener listener; 24 | private Statistics statistics; 25 | private MainWindow mainWindow; 26 | private double lastTime; 27 | 28 | public CreaturesMain() { 29 | initProgram(); 30 | } 31 | 32 | public static void main(String[] args) { 33 | setLookAndFeel(); 34 | 35 | new CreaturesMain(); 36 | } 37 | 38 | private static void setLookAndFeel() { 39 | try { 40 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 41 | } catch (Exception e) { 42 | e.printStackTrace(); 43 | System.exit(1); 44 | } 45 | } 46 | 47 | private void initProgram() { 48 | if (timer != null && timer.isRunning()) { 49 | timer.stop(); 50 | } 51 | 52 | worldModel = WorldFactory.createBasicWorld(800, 600, 20, 50); 53 | 54 | 55 | view = new WorldView(worldModel); 56 | controller = new WorldController(worldModel, view); 57 | listener = new WorldInputListener(controller); 58 | statistics = new Statistics(worldModel); 59 | 60 | view.addKeyListener(listener); 61 | view.addMouseListener(listener); 62 | view.addMouseWheelListener(listener); 63 | 64 | mainWindow = new MainWindow(view, controller, statistics, this, worldModel); 65 | 66 | controller.setMainWindow(mainWindow); 67 | 68 | timer = new Timer(REFRESH_TIME, this); 69 | timer.start(); 70 | lastTime = System.nanoTime(); 71 | } 72 | 73 | @Override 74 | public void actionPerformed(ActionEvent e) { 75 | double currentTime = System.nanoTime(); 76 | double delta = (System.nanoTime() - lastTime) / 1E9; 77 | lastTime = currentTime; 78 | 79 | listener.handlePressedKeys(); 80 | listener.handlePressedMouseButtons(view.getZoomFactor()); 81 | 82 | view.repaint(); 83 | 84 | if (WorldModel.speedFactor > 0) { 85 | for (Creature c : worldModel.getCreatures()) { 86 | c.update(); 87 | } 88 | 89 | mainWindow.update(delta); 90 | controller.updateWorld(delta); 91 | statistics.update(delta); 92 | } 93 | } 94 | 95 | @Override 96 | public void setupWorld(WorldModel worldModel) { 97 | if (timer != null && timer.isRunning()) { 98 | timer.stop(); 99 | } 100 | 101 | this.worldModel = worldModel; 102 | 103 | 104 | view = new WorldView(worldModel); 105 | controller = new WorldController(worldModel, view); 106 | listener = new WorldInputListener(controller); 107 | statistics = new Statistics(worldModel); 108 | 109 | view.addKeyListener(listener); 110 | view.addMouseListener(listener); 111 | view.addMouseWheelListener(listener); 112 | 113 | mainWindow.setView(view); 114 | mainWindow.setController(controller); 115 | mainWindow.setStatistics(statistics); 116 | mainWindow.setWorldCreator(this); 117 | mainWindow.setWorldModel(worldModel); 118 | 119 | controller.setMainWindow(mainWindow); 120 | 121 | timer = new Timer(REFRESH_TIME, this); 122 | timer.start(); 123 | lastTime = System.nanoTime(); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/statistics/Statistics.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.statistics; 2 | 3 | import de.thomas.creatures.implementation.model.Creature; 4 | import de.thomas.creatures.implementation.model.Creature.Gender; 5 | import de.thomas.creatures.implementation.model.WorldModel; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class Statistics { 11 | private final WorldModel worldModel; 12 | private final List statElements; 13 | private double lastTimeUpdated = 0; 14 | 15 | public Statistics(WorldModel worldModel) { 16 | this.worldModel = worldModel; 17 | statElements = new ArrayList<>(); 18 | } 19 | 20 | public void update(double delta) { 21 | lastTimeUpdated += delta; 22 | 23 | if (lastTimeUpdated >= 1) { 24 | lastTimeUpdated = 0; 25 | 26 | addStatElement(); 27 | } 28 | } 29 | 30 | private void addStatElement() { 31 | StatElement element = getStatElement(); 32 | statElements.add(element); 33 | } 34 | 35 | private StatElement getStatElement() { 36 | int creatureAmount = worldModel.getCreatures().size(); 37 | double averageLife; 38 | double averageMaxLife; 39 | double averageEnergy; 40 | double averageMaxEnergy; 41 | double averageSpeed; 42 | double averageVisionRange; 43 | double averageMatingEnergyNeeded; 44 | double averageBreedLength; 45 | double averageBreedProgressSpeed; 46 | double genderRatio; 47 | double pregnancyRatio; 48 | 49 | double totalLife = 0; 50 | double totalMaxLife = 0; 51 | double totalEnergy = 0; 52 | double totalMaxEnergy = 0; 53 | double totalAverageSpeed = 0; 54 | double totalAverageVisionRange = 0; 55 | double totalAverageMatingEnergyNeeded = 0; 56 | double totalAverageBreedLength = 0; 57 | double totalAverageBreedProgressSpeed = 0; 58 | double femaleAmount = 0; 59 | double pregnancyAmount = 0; 60 | 61 | for (Creature c : worldModel.getCreatures()) { 62 | totalLife += c.getLife(); 63 | totalMaxLife += c.getMaxLife(); 64 | totalEnergy += c.getEnergy(); 65 | totalMaxEnergy += c.getMaxEnergy(); 66 | totalAverageSpeed += c.getSpeed(); 67 | totalAverageVisionRange += c.getVisionRange(); 68 | totalAverageMatingEnergyNeeded += c.getMatingEnergyNeeded(); 69 | totalAverageBreedLength += c.getBreedLength(); 70 | totalAverageBreedProgressSpeed += c.getBreedProgressSpeed(); 71 | 72 | if (c.getGender() == Gender.FEMALE) { 73 | femaleAmount += 1; 74 | } 75 | 76 | if (c.isPregnant()) { 77 | pregnancyAmount += 1; 78 | } 79 | } 80 | 81 | averageLife = totalLife / creatureAmount; 82 | averageMaxLife = totalMaxLife / creatureAmount; 83 | averageEnergy = totalEnergy / creatureAmount; 84 | averageMaxEnergy = totalMaxEnergy / creatureAmount; 85 | averageSpeed = totalAverageSpeed / creatureAmount; 86 | averageVisionRange = totalAverageVisionRange / creatureAmount; 87 | averageMatingEnergyNeeded = totalAverageMatingEnergyNeeded / creatureAmount; 88 | averageBreedLength = totalAverageBreedLength / creatureAmount; 89 | averageBreedProgressSpeed = totalAverageBreedProgressSpeed / creatureAmount; 90 | genderRatio = femaleAmount / creatureAmount; 91 | pregnancyRatio = pregnancyAmount / (genderRatio * creatureAmount); 92 | 93 | return new StatElement( 94 | creatureAmount, 95 | averageLife, 96 | averageMaxLife, 97 | averageEnergy, 98 | averageMaxEnergy, 99 | averageSpeed, 100 | averageVisionRange, 101 | averageMatingEnergyNeeded, 102 | averageBreedLength, 103 | averageBreedProgressSpeed, 104 | genderRatio, 105 | pregnancyRatio); 106 | } 107 | 108 | public List getStatElements() { 109 | return statElements; 110 | } 111 | 112 | public StatElement getTopStatElement() { 113 | return statElements.get(statElements.size() - 1); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/view/CreateWorldView.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.view; 2 | 3 | import de.thomas.creatures.implementation.WorldCreator; 4 | import de.thomas.creatures.implementation.model.WorldFactory; 5 | import de.thomas.creatures.implementation.model.WorldModel; 6 | import de.thomas.creatures.implementation.util.AssertionException; 7 | import de.thomas.creatures.implementation.util.AssertionHelper; 8 | 9 | import javax.swing.*; 10 | import java.awt.*; 11 | import java.awt.event.ActionEvent; 12 | import java.awt.event.ActionListener; 13 | 14 | public class CreateWorldView extends JFrame implements ActionListener { 15 | private static final long serialVersionUID = 1L; 16 | 17 | private transient WorldCreator worldCreator; 18 | 19 | private JPanel backPanel; 20 | private JLabel widthLabel; 21 | private JTextField widthInput; 22 | private JLabel heightLabel; 23 | private JTextField heightInput; 24 | private JLabel foodRateLabel; 25 | private JTextField foodRateInput; 26 | private JButton createButton; 27 | 28 | public CreateWorldView(WorldCreator worldCreator) { 29 | this.worldCreator = worldCreator; 30 | 31 | setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 32 | setSize(220, 150); 33 | setLocationRelativeTo(null); 34 | setTitle("Create World"); 35 | 36 | initUI(); 37 | 38 | setResizable(true); 39 | setVisible(true); 40 | setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 41 | } 42 | 43 | 44 | private void initUI() { 45 | backPanel = new JPanel(); 46 | backPanel.setLayout(new GridLayout(0, 1)); 47 | 48 | JPanel widthPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); 49 | widthLabel = new JLabel("Width:"); 50 | widthInput = new JTextField(); 51 | widthInput.setPreferredSize(new Dimension(50, 20)); 52 | widthPanel.add(widthLabel); 53 | widthPanel.add(widthInput); 54 | 55 | 56 | JPanel heightPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); 57 | heightLabel = new JLabel("Height:"); 58 | heightInput = new JTextField(); 59 | heightInput.setPreferredSize(new Dimension(50, 20)); 60 | heightPanel.add(heightLabel); 61 | heightPanel.add(heightInput); 62 | 63 | JPanel foodPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); 64 | foodRateLabel = new JLabel("Food Rate: (0-100)"); 65 | foodRateInput = new JTextField(); 66 | foodRateInput.setPreferredSize(new Dimension(50, 20)); 67 | foodPanel.add(foodRateLabel); 68 | foodPanel.add(foodRateInput); 69 | 70 | backPanel.add(widthPanel); 71 | backPanel.add(heightPanel); 72 | backPanel.add(foodPanel); 73 | 74 | createButton = new JButton("Create World"); 75 | createButton.addActionListener(this); 76 | 77 | backPanel.add(createButton); 78 | 79 | setContentPane(backPanel); 80 | } 81 | 82 | @Override 83 | public void actionPerformed(ActionEvent e) { 84 | if (e.getSource() == createButton) { 85 | createWorld(); 86 | } 87 | } 88 | 89 | private void createWorld() { 90 | int width = 0; 91 | int height = 0; 92 | int foodRate = 0; 93 | 94 | try { 95 | width = Integer.parseInt(widthInput.getText()); 96 | height = Integer.parseInt(heightInput.getText()); 97 | foodRate = Integer.parseInt(foodRateInput.getText()); 98 | } catch (NumberFormatException e) { 99 | JOptionPane.showMessageDialog(this, "Input must be a number.", "Wrong input", JOptionPane.WARNING_MESSAGE); 100 | return; 101 | } 102 | 103 | try { 104 | AssertionHelper.checkSmallerZero(foodRate, "Food Rate"); 105 | AssertionHelper.checkSmallerEqualThan(foodRate, 100, "Food Rate"); 106 | AssertionHelper.checkSmallerZero(width, "Width"); 107 | AssertionHelper.checkSmallerZero(height, "Height"); 108 | 109 | } catch (AssertionException e) { 110 | JOptionPane.showMessageDialog(this, e.getMessage(), "Wrong input", JOptionPane.WARNING_MESSAGE); 111 | return; 112 | } 113 | 114 | WorldModel model = WorldFactory.createEmptyWorld(width, height, foodRate); 115 | worldCreator.setupWorld(model); 116 | dispose(); 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/model/Creature.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.model; 2 | 3 | import de.thomas.creatures.implementation.ai.CreatureAI; 4 | import de.thomas.creatures.implementation.ai.DoNothingAI; 5 | 6 | import java.awt.geom.Point2D; 7 | 8 | public class Creature { 9 | private double life; 10 | 11 | private double maxLife; 12 | private double energy; 13 | private double maxEnergy; 14 | private Point2D.Double position; 15 | private double speed; 16 | private double visionRange; 17 | private Gender gender; 18 | private CreatureAI ai; 19 | private double matingEnergyNeeded; 20 | private double breedLength; 21 | private double breedProgressSpeed; 22 | private double breedTime; 23 | private boolean pregnant; 24 | private Creature fetus; 25 | private Point2D.Double target; 26 | 27 | //For test purposes 28 | public Creature(Point2D.Double position, Gender gender) { 29 | this( 30 | 1000, 31 | 1000, 32 | 500, 33 | position, 34 | 24, 35 | 100, 36 | gender, 37 | new DoNothingAI(), 38 | 100, 39 | 200, 40 | 5); 41 | } 42 | 43 | public Creature( 44 | double energy, 45 | double maxEnergy, 46 | double maxLife, 47 | Point2D.Double position, 48 | double speed, 49 | double visionRange, 50 | Gender gender, 51 | CreatureAI ai, 52 | double matingEnergyNeeded, 53 | double breedLength, 54 | double breedProgressSpeed) { 55 | 56 | this.energy = energy; 57 | this.maxEnergy = maxEnergy; 58 | this.setMaxLife(maxLife); 59 | this.position = position; 60 | this.speed = speed; 61 | this.visionRange = visionRange; 62 | this.gender = gender; 63 | this.ai = ai; 64 | this.matingEnergyNeeded = matingEnergyNeeded; 65 | this.breedLength = breedLength; 66 | this.breedProgressSpeed = breedProgressSpeed; 67 | 68 | this.target = null; 69 | this.pregnant = false; 70 | this.breedTime = breedLength; 71 | this.life = 0; 72 | } 73 | 74 | public void update() { 75 | ai.update(); 76 | } 77 | 78 | public Point2D.Double getPosition() { 79 | return position; 80 | } 81 | 82 | public void setPosition(Point2D.Double position) { 83 | this.position = position; 84 | } 85 | 86 | public double getSpeed() { 87 | return speed; 88 | } 89 | 90 | public void setSpeed(double speed) { 91 | this.speed = speed; 92 | } 93 | 94 | public Gender getGender() { 95 | return gender; 96 | } 97 | 98 | public void setGender(Gender gender) { 99 | this.gender = gender; 100 | } 101 | 102 | public Point2D.Double getTarget() { 103 | return target; 104 | } 105 | 106 | public void setTarget(Point2D.Double target) { 107 | this.target = target; 108 | } 109 | 110 | public double getEnergy() { 111 | return energy; 112 | } 113 | 114 | public void setEnergy(double energy) { 115 | this.energy = energy; 116 | } 117 | 118 | public CreatureAI getAi() { 119 | return ai; 120 | } 121 | 122 | public void setAi(CreatureAI ai) { 123 | this.ai = ai; 124 | } 125 | 126 | public double getVisionRange() { 127 | return visionRange; 128 | } 129 | 130 | public void setVisionRange(double visionRange) { 131 | this.visionRange = visionRange; 132 | } 133 | 134 | public double getMaxEnergy() { 135 | return maxEnergy; 136 | } 137 | 138 | public void setMaxEnergy(double maxEnergy) { 139 | this.maxEnergy = maxEnergy; 140 | } 141 | 142 | public double getLife() { 143 | return life; 144 | } 145 | 146 | public void setLife(double life) { 147 | this.life = life; 148 | } 149 | 150 | public double getMatingEnergyNeeded() { 151 | return matingEnergyNeeded; 152 | } 153 | 154 | public void setMatingEnergyNeeded(double matingEnergyNeeded) { 155 | this.matingEnergyNeeded = matingEnergyNeeded; 156 | } 157 | 158 | public double getBreedTime() { 159 | return breedTime; 160 | } 161 | 162 | public void setBreedTime(double breedTime) { 163 | this.breedTime = breedTime; 164 | } 165 | 166 | public boolean isPregnant() { 167 | return pregnant; 168 | } 169 | 170 | public void setPregnant(boolean pregnant) { 171 | this.pregnant = pregnant; 172 | } 173 | 174 | public double getBreedLength() { 175 | return breedLength; 176 | } 177 | 178 | public void setBreedLength(double breedLength) { 179 | this.breedLength = breedLength; 180 | } 181 | 182 | public double getBreedProgressSpeed() { 183 | return breedProgressSpeed; 184 | } 185 | 186 | public void setBreedProgressSpeed(double breedProgressSpeed) { 187 | this.breedProgressSpeed = breedProgressSpeed; 188 | } 189 | 190 | public double getMaxLife() { 191 | return maxLife; 192 | } 193 | 194 | public void setMaxLife(double maxLife) { 195 | this.maxLife = maxLife; 196 | } 197 | 198 | public Creature getFetus() { 199 | return fetus; 200 | } 201 | 202 | public void setFetus(Creature fetus) { 203 | this.fetus = fetus; 204 | } 205 | 206 | public enum Gender {MALE, FEMALE} 207 | } 208 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/view/StatisticsView.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.view; 2 | 3 | import de.thomas.creatures.implementation.statistics.StatElement; 4 | import org.jfree.chart.ChartFactory; 5 | import org.jfree.chart.ChartPanel; 6 | import org.jfree.chart.JFreeChart; 7 | import org.jfree.chart.plot.PlotOrientation; 8 | import org.jfree.chart.plot.XYPlot; 9 | import org.jfree.data.xy.XYDataset; 10 | import org.jfree.data.xy.XYSeries; 11 | import org.jfree.data.xy.XYSeriesCollection; 12 | 13 | import javax.swing.*; 14 | import java.awt.*; 15 | import java.awt.event.ActionEvent; 16 | import java.awt.event.ActionListener; 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | public class StatisticsView extends JFrame implements ActionListener { 21 | private static final long serialVersionUID = 1L; 22 | private JFreeChart chart; 23 | private JPanel backPanel; 24 | private ChartPanel chartPanel; 25 | 26 | private JPanel downPanel; 27 | private JCheckBox allCheckBox; 28 | private JCheckBox[] checkBoxes; 29 | 30 | private List statElements; 31 | 32 | private String[] statLabels = { 33 | "Creatures", 34 | "Avg. Life", 35 | "Avg. Max Life", 36 | "Avg. Energy", 37 | "Avg. Max Energy", 38 | "Avg. Speed", 39 | "Avg. Vision Range", 40 | "Avg. Mating Energy Needed", 41 | "Avg. Breed Length", 42 | "Avg. Breed Progress Speed", 43 | "Gender Ratio", 44 | "Pregnancy Ratio" 45 | }; 46 | 47 | private Color[] statColors = { 48 | new Color(255, 85, 85), 49 | new Color(85, 85, 255), 50 | new Color(85, 255, 85), 51 | new Color(255, 255, 85), 52 | new Color(255, 85, 255), 53 | new Color(85, 255, 255), 54 | new Color(255, 175, 175), 55 | new Color(128, 128, 128), 56 | new Color(192, 0, 0), 57 | new Color(0, 0, 192), 58 | new Color(0, 192, 0), 59 | new Color(192, 164, 0) 60 | }; 61 | 62 | 63 | public StatisticsView(List statElements) { 64 | this.statElements = statElements; 65 | 66 | setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 67 | setSize(1366, 768); 68 | setLocationRelativeTo(null); 69 | setTitle("Statistics"); 70 | 71 | initUI(statElements); 72 | 73 | setResizable(true); 74 | setVisible(true); 75 | setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 76 | } 77 | 78 | private void initUI(List statElements) { 79 | backPanel = new JPanel(); 80 | 81 | allCheckBox = new JCheckBox("Show all", true); 82 | allCheckBox.addActionListener(this); 83 | checkBoxes = new JCheckBox[statLabels.length]; 84 | 85 | for (int k = 0; k < statLabels.length; k++) { 86 | checkBoxes[k] = new JCheckBox(statLabels[k], true); 87 | checkBoxes[k].addActionListener(this); 88 | } 89 | 90 | XYDataset dataset = createDataset(statElements, false); 91 | 92 | List colors = getColors(); 93 | 94 | chart = createChart(dataset, colors); 95 | chartPanel = new ChartPanel(chart); 96 | 97 | backPanel.setLayout(new BorderLayout()); 98 | 99 | backPanel.add(chartPanel, BorderLayout.CENTER); 100 | 101 | downPanel = new JPanel(); 102 | downPanel.setLayout(new BoxLayout(downPanel, BoxLayout.LINE_AXIS)); 103 | 104 | downPanel.add(allCheckBox); 105 | 106 | for (JCheckBox box : checkBoxes) { 107 | downPanel.add(box); 108 | } 109 | 110 | backPanel.add(downPanel, BorderLayout.SOUTH); 111 | 112 | setContentPane(backPanel); 113 | } 114 | 115 | private List getColors() { 116 | List colors = new ArrayList<>(); 117 | 118 | for (int k = 0; k < checkBoxes.length; k++) { 119 | if (checkBoxes[k].isSelected()) { 120 | colors.add(statColors[k]); 121 | } 122 | } 123 | 124 | return colors; 125 | } 126 | 127 | private XYDataset createDataset(List statElements, boolean relative) { 128 | XYSeries[] allSeries = new XYSeries[statLabels.length]; 129 | 130 | for (int k = 0; k < allSeries.length; k++) { 131 | allSeries[k] = new XYSeries(statLabels[k]); 132 | } 133 | 134 | double current = 0; 135 | for (StatElement element : statElements) { 136 | allSeries[0].add(current, element.getCreatureAmount()); 137 | allSeries[1].add(current, element.getAverageLife()); 138 | allSeries[2].add(current, element.getAverageMaxLife()); 139 | allSeries[3].add(current, element.getAverageEnergy()); 140 | allSeries[4].add(current, element.getAverageMaxEnergy()); 141 | allSeries[5].add(current, element.getAverageSpeed()); 142 | allSeries[6].add(current, element.getAverageVisionRange()); 143 | allSeries[7].add(current, element.getAverageMatingEnergyNeeded()); 144 | allSeries[8].add(current, element.getAverageBreedLength()); 145 | allSeries[9].add(current, element.getAverageBreedProgressSpeed()); 146 | allSeries[10].add(current, element.getGenderRatio()); 147 | allSeries[11].add(current, element.getPregnancyRatio()); 148 | 149 | current++; 150 | } 151 | 152 | final XYSeriesCollection dataset = new XYSeriesCollection(); 153 | 154 | for (int k = 0; k < allSeries.length; k++) { 155 | if (checkBoxes[k].isSelected()) { 156 | dataset.addSeries(allSeries[k]); 157 | } 158 | } 159 | 160 | return dataset; 161 | } 162 | 163 | private JFreeChart createChart(final XYDataset dataset, List colors) { 164 | final JFreeChart chart = ChartFactory.createXYLineChart( 165 | "Creature Statistics", 166 | "Time", 167 | "Value", 168 | dataset, 169 | PlotOrientation.VERTICAL, 170 | true, 171 | true, 172 | false 173 | ); 174 | 175 | chart.setBackgroundPaint(Color.white); 176 | 177 | final XYPlot plot = chart.getXYPlot(); 178 | for (int k = 0; k < colors.size(); k++) { 179 | plot.getRenderer().setSeriesPaint(k, colors.get(k)); 180 | } 181 | 182 | plot.setBackgroundPaint(Color.lightGray); 183 | plot.setDomainGridlinePaint(Color.white); 184 | plot.setRangeGridlinePaint(Color.white); 185 | 186 | return chart; 187 | } 188 | 189 | @Override 190 | public void actionPerformed(ActionEvent e) { 191 | Object source = e.getSource(); 192 | 193 | if (source instanceof JCheckBox) { 194 | JCheckBox selectedBox = (JCheckBox) source; 195 | 196 | if (selectedBox == allCheckBox) { 197 | for (JCheckBox checkBox : checkBoxes) { 198 | checkBox.setSelected(selectedBox.isSelected()); 199 | } 200 | } 201 | 202 | List colors = getColors(); 203 | XYDataset dataset = createDataset(statElements, false); 204 | chart = createChart(dataset, colors); 205 | 206 | backPanel.remove(chartPanel); 207 | 208 | chartPanel = new ChartPanel(chart); 209 | 210 | backPanel.add(chartPanel, BorderLayout.CENTER); 211 | 212 | revalidate(); 213 | repaint(); 214 | } 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/view/CreateCreatureView.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.view; 2 | 3 | import de.thomas.creatures.implementation.ai.BasicAI; 4 | import de.thomas.creatures.implementation.controller.WorldController; 5 | import de.thomas.creatures.implementation.model.Creature; 6 | import de.thomas.creatures.implementation.model.Creature.Gender; 7 | import de.thomas.creatures.implementation.util.AssertionException; 8 | import de.thomas.creatures.implementation.util.AssertionHelper; 9 | 10 | import javax.swing.*; 11 | import java.awt.*; 12 | import java.awt.event.ActionEvent; 13 | import java.awt.event.ActionListener; 14 | import java.awt.geom.Point2D; 15 | 16 | public class CreateCreatureView extends JFrame implements ActionListener { 17 | private static final long serialVersionUID = 1L; 18 | 19 | private transient WorldController worldController; 20 | private double sizeX; 21 | private double sizeY; 22 | 23 | private JPanel backPanel; 24 | private JTextField maxEnergyInput; 25 | private JTextField maxLifeInput; 26 | private JTextField positionXInput; 27 | private JTextField positionYInput; 28 | private JTextField speedInput; 29 | private JTextField visionRangeInput; 30 | private JTextField matingEnergyNeededInput; 31 | private JTextField breedLengthInput; 32 | private JTextField breedProgressSpeedInput; 33 | private JComboBox genderComboBox; 34 | private JButton createButton; 35 | 36 | //TODO Default values (Save if changed) 37 | public CreateCreatureView(WorldController worldController, double sizeX, double sizeY) { 38 | setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 39 | setSize(300, 300); 40 | setLocationRelativeTo(null); 41 | setTitle("Create Creature"); 42 | 43 | this.worldController = worldController; 44 | this.sizeX = sizeX; 45 | this.sizeY = sizeY; 46 | 47 | initUI(); 48 | 49 | setResizable(true); 50 | setVisible(true); 51 | setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 52 | } 53 | 54 | 55 | private void initUI() { 56 | backPanel = new JPanel(); 57 | backPanel.setLayout(new GridLayout(0, 1)); 58 | 59 | JPanel[] panels = new JPanel[9]; 60 | for (int k = 0; k < panels.length; k++) { 61 | panels[k] = new JPanel(new FlowLayout(FlowLayout.LEFT)); 62 | } 63 | 64 | maxEnergyInput = new JTextField(); 65 | maxEnergyInput.setPreferredSize(new Dimension(50, 20)); 66 | maxLifeInput = new JTextField(); 67 | maxLifeInput.setPreferredSize(new Dimension(50, 20)); 68 | positionXInput = new JTextField(); 69 | positionXInput.setPreferredSize(new Dimension(50, 20)); 70 | positionYInput = new JTextField(); 71 | positionYInput.setPreferredSize(new Dimension(50, 20)); 72 | speedInput = new JTextField(); 73 | speedInput.setPreferredSize(new Dimension(50, 20)); 74 | visionRangeInput = new JTextField(); 75 | visionRangeInput.setPreferredSize(new Dimension(50, 20)); 76 | String[] values = {"Male", "Female"}; 77 | genderComboBox = new JComboBox(values); 78 | matingEnergyNeededInput = new JTextField(); 79 | matingEnergyNeededInput.setPreferredSize(new Dimension(50, 20)); 80 | breedLengthInput = new JTextField(); 81 | breedLengthInput.setPreferredSize(new Dimension(50, 20)); 82 | breedProgressSpeedInput = new JTextField(); 83 | breedProgressSpeedInput.setPreferredSize(new Dimension(50, 20)); 84 | 85 | panels[0].add(new JLabel("Max Energy:")); 86 | panels[0].add(maxEnergyInput); 87 | 88 | panels[1].add(new JLabel("Max Life:")); 89 | panels[1].add(maxLifeInput); 90 | 91 | panels[2].add(new JLabel("Position:")); 92 | panels[2].add(positionXInput); 93 | panels[2].add(positionYInput); 94 | 95 | panels[3].add(new JLabel("Speed:")); 96 | panels[3].add(speedInput); 97 | 98 | panels[4].add(new JLabel("Vision Range:")); 99 | panels[4].add(visionRangeInput); 100 | 101 | panels[5].add(new JLabel("Sex:")); 102 | panels[5].add(genderComboBox); 103 | 104 | panels[6].add(new JLabel("Mating Energy Needed:")); 105 | panels[6].add(matingEnergyNeededInput); 106 | 107 | panels[7].add(new JLabel("Breed Length:")); 108 | panels[7].add(breedLengthInput); 109 | 110 | panels[8].add(new JLabel("Breed Progress Speed:")); 111 | panels[8].add(breedProgressSpeedInput); 112 | 113 | for (JPanel p : panels) { 114 | backPanel.add(p); 115 | } 116 | 117 | createButton = new JButton("Create Creature"); 118 | createButton.addActionListener(this); 119 | 120 | backPanel.add(createButton); 121 | 122 | setContentPane(backPanel); 123 | } 124 | 125 | @Override 126 | public void actionPerformed(ActionEvent e) { 127 | if (e.getSource() == createButton) { 128 | createCreature(); 129 | } 130 | } 131 | 132 | private void createCreature() { 133 | int maxEnergy; 134 | int maxLife; 135 | int positionX; 136 | int positionY; 137 | int speed; 138 | int visionRange; 139 | int matingEnergyNeeded; 140 | int breedLength; 141 | int breedProgressSpeed; 142 | 143 | String gender; 144 | 145 | 146 | try { 147 | maxEnergy = Integer.parseInt(maxEnergyInput.getText()); 148 | maxLife = Integer.parseInt(maxLifeInput.getText()); 149 | positionX = Integer.parseInt(positionXInput.getText()); 150 | positionY = Integer.parseInt(positionYInput.getText()); 151 | speed = Integer.parseInt(speedInput.getText()); 152 | visionRange = Integer.parseInt(visionRangeInput.getText()); 153 | gender = (String) genderComboBox.getSelectedItem(); 154 | matingEnergyNeeded = Integer.parseInt(matingEnergyNeededInput.getText()); 155 | breedLength = Integer.parseInt(breedLengthInput.getText()); 156 | breedProgressSpeed = Integer.parseInt(breedProgressSpeedInput.getText()); 157 | } catch (NumberFormatException e) { 158 | JOptionPane.showMessageDialog(this, "Input must be a number.", "Wrong input", JOptionPane.WARNING_MESSAGE); 159 | return; 160 | } 161 | 162 | try { 163 | AssertionHelper.checkSmallerZero(maxEnergy, "Max Energy"); 164 | AssertionHelper.checkSmallerZero(maxLife, "Max Life"); 165 | AssertionHelper.checkSmallerZero(positionX, "Position X"); 166 | AssertionHelper.checkSmallerZero(positionY, "Position Y"); 167 | AssertionHelper.checkSmallerZero(speed, "Speed"); 168 | AssertionHelper.checkSmallerZero(visionRange, "Vision Range"); 169 | AssertionHelper.checkSmallerZero(matingEnergyNeeded, "Mating Energy Needed"); 170 | AssertionHelper.checkSmallerZero(breedLength, "Breed Length"); 171 | AssertionHelper.checkSmallerZero(breedProgressSpeed, "Breed Progress Speed"); 172 | 173 | AssertionHelper.checkSmallerEqualThan(positionX, sizeX, "Position X"); 174 | AssertionHelper.checkSmallerEqualThan(positionY, sizeY, "Position Y"); 175 | } catch (AssertionException e) { 176 | JOptionPane.showMessageDialog(this, e.getMessage(), "Wrong input", JOptionPane.WARNING_MESSAGE); 177 | return; 178 | } 179 | 180 | Gender gnd; 181 | if (gender.equals("Male")) 182 | gnd = Gender.MALE; 183 | else 184 | gnd = Gender.FEMALE; 185 | 186 | Creature creature = new Creature( 187 | maxEnergy, 188 | maxEnergy, 189 | maxLife, 190 | new Point2D.Double(positionX, positionY), 191 | speed, 192 | visionRange, 193 | gnd, 194 | new BasicAI(), 195 | matingEnergyNeeded, 196 | breedLength, 197 | breedProgressSpeed); 198 | 199 | 200 | worldController.addCreature(creature); 201 | dispose(); 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | # This is normally unused 84 | # shellcheck disable=SC2034 85 | APP_BASE_NAME=${0##*/} 86 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. 147 | # shellcheck disable=SC3045 148 | MAX_FD=$( ulimit -H -n ) || 149 | warn "Could not query maximum file descriptor limit" 150 | esac 151 | case $MAX_FD in #( 152 | '' | soft) :;; #( 153 | *) 154 | # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. 155 | # shellcheck disable=SC3045 156 | ulimit -n "$MAX_FD" || 157 | warn "Could not set maximum file descriptor limit to $MAX_FD" 158 | esac 159 | fi 160 | 161 | # Collect all arguments for the java command, stacking in reverse order: 162 | # * args from the command line 163 | # * the main class name 164 | # * -classpath 165 | # * -D...appname settings 166 | # * --module-path (only if needed) 167 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 168 | 169 | # For Cygwin or MSYS, switch paths to Windows format before running java 170 | if "$cygwin" || "$msys" ; then 171 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 172 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 173 | 174 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 175 | 176 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 177 | for arg do 178 | if 179 | case $arg in #( 180 | -*) false ;; # don't mess with options #( 181 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 182 | [ -e "$t" ] ;; #( 183 | *) false ;; 184 | esac 185 | then 186 | arg=$( cygpath --path --ignore --mixed "$arg" ) 187 | fi 188 | # Roll the args list around exactly as many times as the number of 189 | # args, so each arg winds up back in the position where it started, but 190 | # possibly modified. 191 | # 192 | # NB: a `for` loop captures its iteration list before it begins, so 193 | # changing the positional parameters here affects neither the number of 194 | # iterations, nor the values presented in `arg`. 195 | shift # remove old arg 196 | set -- "$@" "$arg" # push replacement arg 197 | done 198 | fi 199 | 200 | # Collect all arguments for the java command; 201 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 202 | # shell script including quotes and variable substitutions, so put them in 203 | # double quotes to make sure that they get re-expanded; and 204 | # * put everything else in single quotes, so that it's not re-expanded. 205 | 206 | set -- \ 207 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 208 | -classpath "$CLASSPATH" \ 209 | org.gradle.wrapper.GradleWrapperMain \ 210 | "$@" 211 | 212 | # Stop when "xargs" is not available. 213 | if ! command -v xargs >/dev/null 2>&1 214 | then 215 | die "xargs is not available" 216 | fi 217 | 218 | # Use "xargs" to parse quoted args. 219 | # 220 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 221 | # 222 | # In Bash we could simply go: 223 | # 224 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 225 | # set -- "${ARGS[@]}" "$@" 226 | # 227 | # but POSIX shell has neither arrays nor command substitution, so instead we 228 | # post-process each arg (as a line of input to sed) to backslash-escape any 229 | # character that might be a shell metacharacter, then use eval to reverse 230 | # that process (while maintaining the separation between arguments), and wrap 231 | # the whole thing up as a single "set" statement. 232 | # 233 | # This will of course break if any of these variables contains a newline or 234 | # an unmatched quote. 235 | # 236 | 237 | eval "set -- $( 238 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 239 | xargs -n1 | 240 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 241 | tr '\n' ' ' 242 | )" '"$@"' 243 | 244 | exec "$JAVACMD" "$@" 245 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/view/MainWindow.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.view; 2 | 3 | import de.thomas.creatures.implementation.WorldCreator; 4 | import de.thomas.creatures.implementation.controller.WorldController; 5 | import de.thomas.creatures.implementation.model.WorldModel; 6 | import de.thomas.creatures.implementation.statistics.StatElement; 7 | import de.thomas.creatures.implementation.statistics.Statistics; 8 | import de.thomas.creatures.implementation.statistics.StatisticsSerializer; 9 | import org.apache.commons.io.FilenameUtils; 10 | 11 | import javax.swing.*; 12 | import javax.swing.event.ChangeEvent; 13 | import javax.swing.event.ChangeListener; 14 | import javax.swing.filechooser.FileNameExtensionFilter; 15 | import java.awt.*; 16 | import java.awt.event.ActionEvent; 17 | import java.awt.event.ActionListener; 18 | import java.io.File; 19 | import java.util.ArrayList; 20 | 21 | public class MainWindow extends JFrame implements ActionListener, ChangeListener { 22 | private static final long serialVersionUID = 1L; 23 | private WorldView view; 24 | private transient Statistics statistics; 25 | private transient WorldController controller; 26 | private transient WorldCreator worldCreator; 27 | private transient WorldModel worldModel; 28 | 29 | private JSplitPane splitPane; 30 | 31 | private JLabel textLabel; 32 | private JLabel speedLabel; 33 | private JSlider speedSlider; 34 | private JLabel maxFoodLabel; 35 | private JSpinner maxFoodSpinner; 36 | 37 | private JMenuBar mainMenuBar; 38 | private JMenu fileMenu; 39 | private JMenuItem exportStatisticsItem; 40 | private JMenuItem closeItem; 41 | 42 | private JMenu worldMenu; 43 | private JMenuItem createWorldItem; 44 | private JMenuItem createCreatureItem; 45 | private JMenuItem createCreaturesItem; 46 | 47 | private JMenu statisticsMenu; 48 | private JMenuItem showStatisticsItem; 49 | 50 | private boolean isExternalUpdate = false; 51 | 52 | public MainWindow(WorldView view, WorldController controller, Statistics statistics, WorldCreator worldCreator, WorldModel worldModel) { 53 | this.view = view; 54 | this.controller = controller; 55 | this.statistics = statistics; 56 | this.worldCreator = worldCreator; 57 | this.worldModel = worldModel; 58 | 59 | initUI(view); 60 | 61 | setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 62 | setSize(1366, 768); 63 | setLocationRelativeTo(null); 64 | setTitle("Creatures"); 65 | 66 | setResizable(true); 67 | setVisible(true); 68 | } 69 | 70 | private void initUI(WorldView view) { 71 | JPanel rightContainer = new JPanel(); 72 | rightContainer.setLayout(new BoxLayout(rightContainer, BoxLayout.PAGE_AXIS)); 73 | 74 | textLabel = new JLabel("Creatures"); 75 | textLabel.setAlignmentX(Component.LEFT_ALIGNMENT); 76 | 77 | speedLabel = new JLabel("Speed"); 78 | speedLabel.setAlignmentX(Component.LEFT_ALIGNMENT); 79 | 80 | speedSlider = new JSlider(0, 15, 1); 81 | speedSlider.setAlignmentX(Component.LEFT_ALIGNMENT); 82 | speedSlider.setMajorTickSpacing(5); 83 | speedSlider.setMinorTickSpacing(1); 84 | speedSlider.setSnapToTicks(true); 85 | speedSlider.setPaintLabels(true); 86 | speedSlider.setPaintTicks(true); 87 | speedSlider.addChangeListener(this); 88 | 89 | JPanel maxFoodContainer = new JPanel(); 90 | maxFoodContainer.setLayout(new FlowLayout(FlowLayout.LEFT)); 91 | maxFoodContainer.setAlignmentX(Component.LEFT_ALIGNMENT); 92 | 93 | maxFoodLabel = new JLabel("Max Food"); 94 | SpinnerModel maxFoodSpinnerModel = new SpinnerNumberModel(WorldModel.maxFoodAmount, 0, 100000, 1); 95 | maxFoodSpinner = new JSpinner(maxFoodSpinnerModel); 96 | maxFoodSpinner.setEditor(new JSpinner.NumberEditor(maxFoodSpinner, "#")); 97 | maxFoodSpinner.addChangeListener(this); 98 | 99 | maxFoodContainer.add(maxFoodLabel); 100 | maxFoodContainer.add(maxFoodSpinner); 101 | 102 | rightContainer.add(textLabel); 103 | rightContainer.add(Box.createVerticalStrut(10)); 104 | rightContainer.add(speedLabel); 105 | rightContainer.add(speedSlider); 106 | rightContainer.add(maxFoodContainer); 107 | 108 | splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, view, rightContainer); 109 | splitPane.setResizeWeight(0.8); 110 | 111 | add(splitPane); 112 | 113 | mainMenuBar = new JMenuBar(); 114 | 115 | fileMenu = new JMenu("File"); 116 | exportStatisticsItem = new JMenuItem("Export Statistics"); 117 | exportStatisticsItem.addActionListener(this); 118 | closeItem = new JMenuItem("Close"); 119 | closeItem.addActionListener(this); 120 | fileMenu.add(exportStatisticsItem); 121 | fileMenu.addSeparator(); 122 | fileMenu.add(closeItem); 123 | 124 | worldMenu = new JMenu("Creation"); 125 | createWorldItem = new JMenuItem("Create World"); 126 | createWorldItem.addActionListener(this); 127 | worldMenu.add(createWorldItem); 128 | worldMenu.addSeparator(); 129 | createCreatureItem = new JMenuItem("Create Creature"); 130 | createCreatureItem.addActionListener(this); 131 | worldMenu.add(createCreatureItem); 132 | createCreaturesItem = new JMenuItem("Create Creatures"); 133 | createCreaturesItem.addActionListener(this); 134 | worldMenu.add(createCreaturesItem); 135 | 136 | statisticsMenu = new JMenu("Statistics"); 137 | showStatisticsItem = new JMenuItem("Show Statistics"); 138 | showStatisticsItem.addActionListener(this); 139 | statisticsMenu.add(showStatisticsItem); 140 | 141 | mainMenuBar.add(fileMenu); 142 | mainMenuBar.add(worldMenu); 143 | mainMenuBar.add(statisticsMenu); 144 | 145 | setJMenuBar(mainMenuBar); 146 | } 147 | 148 | public void update(double delta) { 149 | if (statistics.getStatElements().size() > 0) { 150 | StatElement top = statistics.getTopStatElement(); 151 | 152 | String displayString = ""; 153 | displayString += "Creatures: " + top.getCreatureAmount() + " " + 154 | "Avg. Speed: " + String.format("%1$,.2f", top.getAverageSpeed()); 155 | ; 156 | 157 | textLabel.setText(displayString); 158 | } 159 | } 160 | 161 | @Override 162 | public void stateChanged(ChangeEvent e) { 163 | if (e.getSource() == speedSlider && !isExternalUpdate) { 164 | JSlider source = (JSlider) e.getSource(); 165 | int value = source.getValue(); 166 | 167 | controller.setSpeed(value); 168 | } else if (e.getSource() == maxFoodSpinner) { 169 | JSpinner source = (JSpinner) e.getSource(); 170 | WorldModel.maxFoodAmount = (int) source.getValue(); 171 | } 172 | } 173 | 174 | public void setSpeedSlider(double d) { 175 | isExternalUpdate = true; 176 | speedSlider.setValue((int) d); 177 | isExternalUpdate = false; 178 | } 179 | 180 | public void setViewInputFocus() { 181 | view.requestFocus(); 182 | } 183 | 184 | @Override 185 | public void actionPerformed(ActionEvent e) { 186 | if (e.getSource() == closeItem) { 187 | System.exit(0); 188 | } else if (e.getSource() == exportStatisticsItem) { 189 | JFileChooser fileChooser = new JFileChooser(); 190 | FileNameExtensionFilter csvFilter = new FileNameExtensionFilter("CSV (comma-separated) (*.csv)", "csv"); 191 | fileChooser.setFileFilter(csvFilter); 192 | 193 | 194 | if (fileChooser.showSaveDialog(this) == JFileChooser.APPROVE_OPTION) { 195 | File file = fileChooser.getSelectedFile(); 196 | 197 | if (fileChooser.getFileFilter() == csvFilter && !FilenameUtils.getExtension(file.getName()).equalsIgnoreCase("csv")) { 198 | file = new File(file.toString() + ".csv"); 199 | } 200 | 201 | new StatisticsSerializer().exportStatistics(statistics.getStatElements(), file); 202 | } 203 | } else if (e.getSource() == createWorldItem) { 204 | new CreateWorldView(worldCreator); 205 | } else if (e.getSource() == createCreatureItem) { 206 | new CreateCreatureView(controller, worldModel.getWidth(), worldModel.getHeight()); 207 | } else if (e.getSource() == createCreaturesItem) { 208 | new CreateCreaturesView(controller, worldModel.getWidth(), worldModel.getHeight()); 209 | } else if (e.getSource() == showStatisticsItem) { 210 | new StatisticsView(new ArrayList<>(statistics.getStatElements())); 211 | } 212 | } 213 | 214 | public void setView(WorldView view) { 215 | this.view = view; 216 | splitPane.setLeftComponent(view); 217 | } 218 | 219 | public void setStatistics(Statistics statistics) { 220 | this.statistics = statistics; 221 | } 222 | 223 | public void setController(WorldController controller) { 224 | this.controller = controller; 225 | } 226 | 227 | public void setWorldCreator(WorldCreator worldCreator) { 228 | this.worldCreator = worldCreator; 229 | } 230 | 231 | public void setWorldModel(WorldModel worldModel) { 232 | this.worldModel = worldModel; 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/controller/WorldUpdater.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.controller; 2 | 3 | import de.thomas.creatures.implementation.ai.BasicAI; 4 | import de.thomas.creatures.implementation.ai.CreatureAI; 5 | import de.thomas.creatures.implementation.model.Creature; 6 | import de.thomas.creatures.implementation.model.Creature.Gender; 7 | import de.thomas.creatures.implementation.model.Food; 8 | import de.thomas.creatures.implementation.model.WorldModel; 9 | import de.thomas.creatures.implementation.util.VariationHelper; 10 | 11 | import java.awt.geom.Point2D; 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | import java.util.List; 15 | 16 | public class WorldUpdater { 17 | public static final double MIN_DISTANCE = 1.25; 18 | private WorldModel worldModel; 19 | private WorldController worldController; 20 | private List newBornList = new ArrayList<>(); 21 | 22 | public WorldUpdater(WorldModel worldModel, WorldController worldController) { 23 | this.worldModel = worldModel; 24 | this.worldController = worldController; 25 | } 26 | 27 | public void updateWorld(double delta) { 28 | Iterator creatureIterator = worldModel.getCreatures().iterator(); 29 | 30 | while (creatureIterator.hasNext()) { 31 | Creature creature = creatureIterator.next(); 32 | 33 | handleMoving(delta, creature); 34 | handleMating(delta, creature); 35 | handleFoodIntake(delta, creature); 36 | 37 | if (creature.isPregnant()) { 38 | handlePregnancy(creature, delta); 39 | } 40 | 41 | handleEnergyDepletion(delta, creatureIterator, creature); 42 | handleLifeDepletion(delta, creatureIterator, creature); 43 | } 44 | 45 | Iterator newBornIterator = newBornList.iterator(); 46 | while (newBornIterator.hasNext()) { 47 | Creature newBorn = newBornIterator.next(); 48 | worldController.addCreature(newBorn); 49 | newBornIterator.remove(); 50 | } 51 | 52 | handleFoodCreation(); 53 | } 54 | 55 | private void handleMoving(double delta, Creature creature) { 56 | //TODO Maybe only compute stuff if target changes, else let speed be the same 57 | 58 | Point2D.Double target = creature.getTarget(); 59 | Point2D.Double position = creature.getPosition(); 60 | 61 | //Move nearer to target 62 | if (target != null) { 63 | if (target.distance(position) > (creature.getSpeed() * delta * WorldModel.speedFactor * MIN_DISTANCE)) { 64 | double x = target.x - position.x; 65 | double y = target.y - position.y; 66 | double speed = creature.getSpeed() * delta * WorldModel.speedFactor; 67 | 68 | double alpha = speed / Math.sqrt(x * x + y * y); 69 | 70 | double speedX = alpha * x; 71 | double speedY = alpha * y; 72 | 73 | creature.getPosition().x += speedX; 74 | creature.getPosition().y += speedY; 75 | } else { 76 | creature.setPosition(new Point2D.Double(creature.getTarget().x, creature.getTarget().y)); 77 | creature.setTarget(null); 78 | } 79 | } 80 | } 81 | 82 | private void handleMating(double delta, Creature creature) { 83 | Iterator secondIterator = worldModel.getCreatures().iterator(); 84 | while (secondIterator.hasNext()) { 85 | Creature secondCreature = secondIterator.next(); 86 | 87 | if (creature.getPosition().distance(secondCreature.getPosition()) < (creature.getSpeed() * delta * WorldModel.speedFactor * 1.25) && 88 | creature.getEnergy() > creature.getMatingEnergyNeeded() && 89 | secondCreature.getEnergy() > secondCreature.getMatingEnergyNeeded() && 90 | creature.getGender() != secondCreature.getGender() && 91 | !creature.isPregnant() && !secondCreature.isPregnant()) { 92 | 93 | Creature father; 94 | Creature mother; 95 | 96 | if (creature.getGender() == Gender.FEMALE) { 97 | father = secondCreature; 98 | mother = creature; 99 | } else { 100 | father = creature; 101 | mother = secondCreature; 102 | } 103 | 104 | mother.setPregnant(true); 105 | Creature fetus = createFetus(father, mother); 106 | mother.setFetus(fetus); 107 | } 108 | } 109 | } 110 | 111 | private void handleFoodIntake(double delta, Creature creature) { 112 | Iterator foodIterator = worldModel.getFoods().iterator(); 113 | 114 | //Remove food and add energy to creature if it is close enough 115 | while (foodIterator.hasNext()) { 116 | Food f = foodIterator.next(); 117 | 118 | if (creature.getPosition().distance(f.getPosition()) < (creature.getSpeed() * delta * 1.25 * WorldModel.speedFactor)) { 119 | creature.setEnergy(creature.getEnergy() + f.getValue()); 120 | foodIterator.remove(); 121 | 122 | if (creature.getEnergy() > creature.getMaxEnergy()) 123 | creature.setEnergy(creature.getMaxEnergy()); 124 | } 125 | } 126 | } 127 | 128 | private void handleEnergyDepletion(double delta, Iterator creatureIterator, Creature creature) { 129 | double energyDepletion = WorldModel.baseEnergyDepletionRate; 130 | 131 | if (creature.getTarget() != null) { 132 | energyDepletion += creature.getSpeed(); 133 | } 134 | 135 | if (creature.isPregnant()) { 136 | energyDepletion += creature.getBreedProgressSpeed(); 137 | } 138 | 139 | creature.setEnergy(creature.getEnergy() - (energyDepletion * delta * WorldModel.speedFactor)); 140 | 141 | if (creature.getEnergy() <= 1) { 142 | creatureIterator.remove(); 143 | 144 | if (creatureIterator.hasNext()) { 145 | creatureIterator.next(); 146 | } 147 | } 148 | } 149 | 150 | private void handleLifeDepletion(double delta, Iterator creatureIterator, Creature creature) { 151 | creature.setLife(creature.getLife() + (1 * delta * WorldModel.speedFactor)); 152 | 153 | if (creature.getLife() >= creature.getMaxLife()) { 154 | creatureIterator.remove(); 155 | 156 | if (creatureIterator.hasNext()) { 157 | creatureIterator.next(); 158 | } 159 | } 160 | } 161 | 162 | private void handleFoodCreation() { 163 | for (int i = 0; i < WorldModel.speedFactor; i++) { 164 | if (worldModel.getFoods().size() < WorldModel.maxFoodAmount && 165 | (int) (Math.random() * 100) < worldModel.getFoodCreationRate()) { 166 | double xPos = worldModel.getWidth() * Math.random(); 167 | double yPos = worldModel.getHeight() * Math.random(); 168 | 169 | Food food = new Food(new Point2D.Double(xPos, yPos), (int) (Math.random() * WorldModel.maxFoodEnergy)); 170 | worldModel.addFood(food); 171 | } 172 | } 173 | } 174 | 175 | private void handlePregnancy(Creature creature, double delta) { 176 | if (creature.getBreedTime() > 1) { 177 | creature.setBreedTime(creature.getBreedTime() - (creature.getBreedProgressSpeed() * delta * WorldModel.speedFactor)); 178 | } else { 179 | creature.setBreedTime(creature.getBreedLength()); 180 | creature.setPregnant(false); 181 | Point2D.Double motherPosition = creature.getPosition(); 182 | 183 | Creature newBorn = creature.getFetus(); 184 | newBorn.setPosition(new Point2D.Double(motherPosition.x, motherPosition.y)); 185 | 186 | newBornList.add(newBorn); 187 | creature.setFetus(null); 188 | } 189 | } 190 | 191 | private Creature createFetus(Creature father, Creature mother) { 192 | double energy = mother.getBreedLength(); 193 | double maxEnergy = ((father.getMaxEnergy() + mother.getMaxEnergy()) / 2) * VariationHelper.mutationFactor(WorldModel.mutationRate); 194 | double maxLife = ((father.getMaxLife() + mother.getMaxLife()) / 2) * VariationHelper.mutationFactor(WorldModel.mutationRate); 195 | //Position will be added later 196 | Point2D.Double position = null; 197 | double speed = ((father.getSpeed() + mother.getSpeed()) / 2) * VariationHelper.mutationFactor(WorldModel.mutationRate); 198 | double visionRange = ((father.getVisionRange() + mother.getVisionRange()) / 2) * VariationHelper.mutationFactor(WorldModel.mutationRate); 199 | Gender gender; 200 | if (Math.random() >= 0.5) { 201 | gender = Gender.MALE; 202 | } 203 | else { 204 | gender = Gender.FEMALE; 205 | } 206 | CreatureAI ai = new BasicAI(); 207 | double matingEnergyNeeded = ((father.getMatingEnergyNeeded() + mother.getMatingEnergyNeeded()) / 2) 208 | * VariationHelper.mutationFactor(WorldModel.mutationRate); 209 | double breedLength = ((father.getBreedLength() + mother.getBreedLength()) / 2) * 210 | VariationHelper.mutationFactor(WorldModel.mutationRate); 211 | double breedProgressSpeed = ((father.getBreedProgressSpeed() + mother.getBreedProgressSpeed()) / 2) * 212 | VariationHelper.mutationFactor(WorldModel.mutationRate); 213 | 214 | Creature fetus = new Creature(energy, 215 | maxEnergy, 216 | maxLife, 217 | position, 218 | speed, 219 | visionRange, 220 | gender, 221 | ai, 222 | matingEnergyNeeded, 223 | breedLength, 224 | breedProgressSpeed); 225 | 226 | return fetus; 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /src/main/java/de/thomas/creatures/implementation/view/CreateCreaturesView.java: -------------------------------------------------------------------------------- 1 | package de.thomas.creatures.implementation.view; 2 | 3 | import de.thomas.creatures.implementation.ai.BasicAI; 4 | import de.thomas.creatures.implementation.controller.WorldController; 5 | import de.thomas.creatures.implementation.model.Creature; 6 | import de.thomas.creatures.implementation.model.Creature.Gender; 7 | import de.thomas.creatures.implementation.util.AssertionException; 8 | import de.thomas.creatures.implementation.util.AssertionHelper; 9 | import de.thomas.creatures.implementation.util.VariationHelper; 10 | 11 | import javax.swing.*; 12 | import java.awt.*; 13 | import java.awt.event.ActionEvent; 14 | import java.awt.event.ActionListener; 15 | import java.awt.geom.Point2D; 16 | 17 | public class CreateCreaturesView extends JFrame implements ActionListener { 18 | private static final long serialVersionUID = 1L; 19 | 20 | private transient WorldController worldController; 21 | private double sizeX; 22 | private double sizeY; 23 | 24 | private JPanel backPanel; 25 | 26 | private JTextField amountInput; 27 | private JTextField maxEnergyInput; 28 | private JTextField maxLifeInput; 29 | private JTextField speedInput; 30 | private JTextField visionRangeInput; 31 | private JTextField maleRationInput; 32 | private JTextField matingEnergyNeededInput; 33 | private JTextField breedLengthInput; 34 | private JTextField breedProgressSpeedInput; 35 | 36 | private JTextField maxEnergyVarianceInput; 37 | private JTextField maxLifeVarianceInput; 38 | private JTextField speedVarianceInput; 39 | private JTextField visionRangeVarianceInput; 40 | private JTextField matingEnergyNeededVarianceInput; 41 | private JTextField breedLengthVarianceInput; 42 | private JTextField breedProgressSpeedVarianceInput; 43 | 44 | private JButton createButton; 45 | 46 | //TODO Default Values 47 | public CreateCreaturesView(WorldController worldController, double sizeX, double sizeY) { 48 | this.worldController = worldController; 49 | this.sizeX = sizeX; 50 | this.sizeY = sizeY; 51 | 52 | setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 53 | setSize(350, 300); 54 | setLocationRelativeTo(null); 55 | setTitle("Create Creatures (With Variance)"); 56 | 57 | initUI(); 58 | 59 | setResizable(true); 60 | setVisible(true); 61 | setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); 62 | } 63 | 64 | 65 | private void initUI() { 66 | backPanel = new JPanel(); 67 | backPanel.setLayout(new GridLayout(0, 1)); 68 | 69 | JPanel[] panels = new JPanel[9]; 70 | for (int k = 0; k < panels.length; k++) { 71 | panels[k] = new JPanel(new FlowLayout(FlowLayout.LEFT)); 72 | } 73 | 74 | amountInput = new JTextField(); 75 | amountInput.setPreferredSize(new Dimension(50, 20)); 76 | maxEnergyInput = new JTextField(); 77 | maxEnergyInput.setPreferredSize(new Dimension(50, 20)); 78 | maxLifeInput = new JTextField(); 79 | maxLifeInput.setPreferredSize(new Dimension(50, 20)); 80 | speedInput = new JTextField(); 81 | speedInput.setPreferredSize(new Dimension(50, 20)); 82 | visionRangeInput = new JTextField(); 83 | visionRangeInput.setPreferredSize(new Dimension(50, 20)); 84 | maleRationInput = new JTextField(); 85 | maleRationInput.setPreferredSize(new Dimension(50, 20)); 86 | matingEnergyNeededInput = new JTextField(); 87 | matingEnergyNeededInput.setPreferredSize(new Dimension(50, 20)); 88 | breedLengthInput = new JTextField(); 89 | breedLengthInput.setPreferredSize(new Dimension(50, 20)); 90 | breedProgressSpeedInput = new JTextField(); 91 | breedProgressSpeedInput.setPreferredSize(new Dimension(50, 20)); 92 | 93 | maxEnergyVarianceInput = new JTextField(); 94 | maxEnergyVarianceInput.setPreferredSize(new Dimension(50, 20)); 95 | maxLifeVarianceInput = new JTextField(); 96 | maxLifeVarianceInput.setPreferredSize(new Dimension(50, 20)); 97 | speedVarianceInput = new JTextField(); 98 | speedVarianceInput.setPreferredSize(new Dimension(50, 20)); 99 | visionRangeVarianceInput = new JTextField(); 100 | visionRangeVarianceInput.setPreferredSize(new Dimension(50, 20)); 101 | matingEnergyNeededVarianceInput = new JTextField(); 102 | matingEnergyNeededVarianceInput.setPreferredSize(new Dimension(50, 20)); 103 | breedLengthVarianceInput = new JTextField(); 104 | breedLengthVarianceInput.setPreferredSize(new Dimension(50, 20)); 105 | breedProgressSpeedVarianceInput = new JTextField(); 106 | breedProgressSpeedVarianceInput.setPreferredSize(new Dimension(50, 20)); 107 | 108 | panels[0].add(new JLabel("Amount:")); 109 | panels[0].add(amountInput); 110 | 111 | panels[1].add(new JLabel("Max Energy:")); 112 | panels[1].add(maxEnergyInput); 113 | panels[1].add(maxEnergyVarianceInput); 114 | 115 | panels[2].add(new JLabel("Max Life:")); 116 | panels[2].add(maxLifeInput); 117 | panels[2].add(maxLifeVarianceInput); 118 | 119 | panels[3].add(new JLabel("Speed:")); 120 | panels[3].add(speedInput); 121 | panels[3].add(speedVarianceInput); 122 | 123 | panels[4].add(new JLabel("Vision Range:")); 124 | panels[4].add(visionRangeInput); 125 | panels[4].add(visionRangeVarianceInput); 126 | 127 | panels[5].add(new JLabel("Male Ratio: (0-100)")); 128 | panels[5].add(maleRationInput); 129 | 130 | panels[6].add(new JLabel("Mating Energy Needed:")); 131 | panels[6].add(matingEnergyNeededInput); 132 | panels[6].add(matingEnergyNeededVarianceInput); 133 | 134 | panels[7].add(new JLabel("Breed Length:")); 135 | panels[7].add(breedLengthInput); 136 | panels[7].add(breedLengthVarianceInput); 137 | 138 | panels[8].add(new JLabel("Breed Progress Speed:")); 139 | panels[8].add(breedProgressSpeedInput); 140 | panels[8].add(breedProgressSpeedVarianceInput); 141 | 142 | for (JPanel p : panels) { 143 | backPanel.add(p); 144 | } 145 | 146 | createButton = new JButton("Create Creatures"); 147 | createButton.addActionListener(this); 148 | 149 | backPanel.add(createButton); 150 | 151 | setContentPane(backPanel); 152 | } 153 | 154 | @Override 155 | public void actionPerformed(ActionEvent e) { 156 | if (e.getSource() == createButton) { 157 | createCreatures(); 158 | } 159 | } 160 | 161 | private void createCreatures() { 162 | double amount = 0; 163 | double maxEnergy = 0; 164 | double maxLife = 0; 165 | double speed = 0; 166 | double visionRange = 0; 167 | double maleRation = 0; 168 | double matingEnergyNeeded = 0; 169 | double breedLength = 0; 170 | double breedProgressSpeed = 0; 171 | 172 | double maxEnergyVariance = 0; 173 | double maxLifeVariance = 0; 174 | double speedVariance = 0; 175 | double visionRangeVariance = 0; 176 | double matingEnergyNeededVariance = 0; 177 | double breedLengthVariance = 0; 178 | double breedProgressSpeedVariance = 0; 179 | 180 | try { 181 | amount = Double.parseDouble(amountInput.getText()); 182 | maxEnergy = Double.parseDouble(maxEnergyInput.getText()); 183 | maxLife = Double.parseDouble(maxLifeInput.getText()); 184 | speed = Double.parseDouble(speedInput.getText()); 185 | visionRange = Double.parseDouble(visionRangeInput.getText()); 186 | maleRation = Double.parseDouble(maleRationInput.getText()); 187 | matingEnergyNeeded = Double.parseDouble(matingEnergyNeededInput.getText()); 188 | breedLength = Double.parseDouble(breedLengthInput.getText()); 189 | breedProgressSpeed = Double.parseDouble(breedProgressSpeedInput.getText()); 190 | 191 | maxEnergyVariance = Double.parseDouble(maxEnergyVarianceInput.getText()); 192 | maxLifeVariance = Double.parseDouble(maxLifeVarianceInput.getText()); 193 | speedVariance = Double.parseDouble(speedVarianceInput.getText()); 194 | visionRangeVariance = Double.parseDouble(visionRangeVarianceInput.getText()); 195 | matingEnergyNeededVariance = Double.parseDouble(matingEnergyNeededVarianceInput.getText()); 196 | breedLengthVariance = Double.parseDouble(breedLengthVarianceInput.getText()); 197 | breedProgressSpeedVariance = Double.parseDouble(breedProgressSpeedVarianceInput.getText()); 198 | } catch (NumberFormatException e) { 199 | JOptionPane.showMessageDialog(this, "Input must be a number.", "Wrong input", JOptionPane.WARNING_MESSAGE); 200 | return; 201 | } 202 | 203 | try { 204 | AssertionHelper.checkSmallerZero(amount, "Amount"); 205 | AssertionHelper.checkSmallerZero(maxEnergy, "Max Energy"); 206 | AssertionHelper.checkSmallerZero(maxLife, "Max Life"); 207 | AssertionHelper.checkSmallerZero(speed, "Speed"); 208 | AssertionHelper.checkSmallerZero(visionRange, "Vision Range"); 209 | AssertionHelper.checkSmallerZero(maleRation, "Male Ratio"); 210 | AssertionHelper.checkSmallerZero(matingEnergyNeeded, "Mating Energy Needed"); 211 | AssertionHelper.checkSmallerZero(breedLength, "Breed Length"); 212 | AssertionHelper.checkSmallerZero(breedProgressSpeed, "Breed Progress Speed"); 213 | AssertionHelper.checkSmallerZero(maxEnergyVariance, "Max Energy Variance"); 214 | AssertionHelper.checkSmallerZero(maxLifeVariance, "Max Life Variance"); 215 | AssertionHelper.checkSmallerZero(speedVariance, "Speed Variance"); 216 | AssertionHelper.checkSmallerZero(visionRangeVariance, "Vision Range Variance"); 217 | AssertionHelper.checkSmallerZero(matingEnergyNeededVariance, "Mating Energy Needed Variance"); 218 | AssertionHelper.checkSmallerZero(breedLengthVariance, "Breed Length Variance"); 219 | AssertionHelper.checkSmallerZero(breedProgressSpeedVariance, "Breed Progress Speed Variance"); 220 | 221 | AssertionHelper.checkSmallerEqualThan(maleRation, 100, "Male Ratio"); 222 | 223 | AssertionHelper.checkSmallerEqualThan(maxEnergyVariance, maxEnergy, "Max Energy Variance"); 224 | AssertionHelper.checkSmallerEqualThan(maxLifeVariance, maxLife, "Max Life Variance"); 225 | AssertionHelper.checkSmallerEqualThan(speedVariance, speed, "Speed Variance"); 226 | AssertionHelper.checkSmallerEqualThan(visionRangeVariance, visionRange, "Vision Range Variance"); 227 | AssertionHelper.checkSmallerEqualThan(matingEnergyNeededVariance, matingEnergyNeeded, "Mating Energy Needed Variance"); 228 | AssertionHelper.checkSmallerEqualThan(breedLengthVariance, breedLength, "Breed Length Variance"); 229 | AssertionHelper.checkSmallerEqualThan(breedProgressSpeedVariance, breedProgressSpeed, "Breed Progress Speed Variance"); 230 | } catch (AssertionException e) { 231 | JOptionPane.showMessageDialog(this, e.getMessage(), "Wrong input", JOptionPane.WARNING_MESSAGE); 232 | return; 233 | } 234 | 235 | for (int k = 0; k < amount; k++) { 236 | Gender gnd; 237 | 238 | if (Math.random() * 100 > maleRation) 239 | gnd = Gender.FEMALE; 240 | else 241 | gnd = Gender.MALE; 242 | 243 | double middleWidth = sizeX / 2; 244 | double middleHeight = sizeY / 2; 245 | 246 | Creature creature = new Creature( 247 | maxEnergy, 248 | maxEnergy * VariationHelper.mutationFactor(maxEnergyVariance / maxEnergy), 249 | maxLife * VariationHelper.mutationFactor(maxLifeVariance / maxLife), 250 | new Point2D.Double 251 | (middleWidth * VariationHelper.mutationFactor(0.95), 252 | middleHeight * VariationHelper.mutationFactor(0.95)), 253 | speed * VariationHelper.mutationFactor(speedVariance / speed), 254 | visionRange * VariationHelper.mutationFactor(visionRangeVariance / visionRange), 255 | gnd, 256 | new BasicAI(), 257 | matingEnergyNeeded * VariationHelper.mutationFactor(matingEnergyNeededVariance / matingEnergyNeeded), 258 | breedLength * VariationHelper.mutationFactor(breedLengthVariance / breedLength), 259 | breedProgressSpeed * VariationHelper.mutationFactor(breedProgressSpeedVariance / breedProgressSpeed) 260 | ); 261 | 262 | 263 | worldController.addCreature(creature); 264 | } 265 | 266 | dispose(); 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | --------------------------------------------------------------------------------