diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7e1d0dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +releases +.classpath +.project +data +*.log diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7e1d0dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +releases +.classpath +.project +data +*.log diff --git a/README b/README index 7e39738..996df4c 100755 --- a/README +++ b/README @@ -2,6 +2,9 @@ Ecologia README =================== +--- THIS IS THE README FOR ECOLOGIA 1.1! --- + (a readme for version 2 is soon to follow) + Ecologia is a graphical ecosystem simulator. Currently it is still very basic, but hopefully it will soon be expanded to include more and more aspects of a real ecosystem. diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7e1d0dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +releases +.classpath +.project +data +*.log diff --git a/README b/README index 7e39738..996df4c 100755 --- a/README +++ b/README @@ -2,6 +2,9 @@ Ecologia README =================== +--- THIS IS THE README FOR ECOLOGIA 1.1! --- + (a readme for version 2 is soon to follow) + Ecologia is a graphical ecosystem simulator. Currently it is still very basic, but hopefully it will soon be expanded to include more and more aspects of a real ecosystem. diff --git a/analysis/demography/population.py b/analysis/demography/population.py index 9490f0f..1955b45 100755 --- a/analysis/demography/population.py +++ b/analysis/demography/population.py @@ -18,12 +18,11 @@ global generation_count global carnivore_count global herbivore_count -global kill_rate global grass_density global src_file global out_file -version = "0.3" +version = "0.3.1" ''' @@ -32,8 +31,7 @@ def load_log(): global src_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density try: log_file = open(src_file, "r") line = log_file.readline() @@ -47,9 +45,6 @@ elif "Carnivore count" in line: next_count = line[line.find("Carnivore count: ")+17:-1] carnivore_count.append(int(next_count)) - elif "Carnivore hunt success rate: " in line: - next_count = line[line.find("rate: ")+6:-2] - kill_rate.append(int(next_count)) elif "Average grass density: " in line: next_count = line[line.find("density: ")+9:-2] grass_density.append(next_count) @@ -68,14 +63,12 @@ ''' def compact(): global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density if len(update_count) < 1000: return new_update_count = [update_count[0]] new_herbivore_count = [herbivore_count[0]] new_carnivore_count = [carnivore_count[0]] - new_kill_rate = [kill_rate[0]] new_grass_density = [grass_density[0]] new_generation_count = [generation_count[0]] for i in range(1, len(update_count)): @@ -83,13 +76,11 @@ new_update_count.append(update_count[i]) new_herbivore_count.append(herbivore_count[i]) new_carnivore_count.append(carnivore_count[i]) - new_kill_rate.append(kill_rate[i]) new_grass_density.append(grass_density[i]) new_generation_count.append(generation_count[i]) update_count = new_update_count herbivore_count = new_herbivore_count carnivore_count = new_carnivore_count - kill_rate = new_kill_rate grass_density = new_grass_density generation_count = new_generation_count compact() #Keep going until the data set is small enough @@ -100,8 +91,7 @@ def save_csv(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate all the data data = "Updates" for u in update_count: @@ -112,9 +102,6 @@ data = data+"\nCarnivores" for c in carnivore_count: data = data+","+str(c) - data = data+"\nKill rate" - for k in kill_rate: - data = data+","+str(k) data = data+"\nGrass density" for g in grass_density: data = data+","+str(g) @@ -136,13 +123,12 @@ def save_table(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate the data - data = "Updates\tHerbivores\tCarnivores\tKillRate\tGrassDensity\tGenerations" + data = "Updates\tHerbivores\tCarnivores\tGrassDensity\tGenerations" for i in range(len(update_count)): data = data+"\n"+str(update_count[i])+"\t"+str(herbivore_count[i])+"\t"+str(carnivore_count[i]) - data = data+"\t"+str(kill_rate[i])+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) + data = data+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) #Then write it to file try: table_file = open(out_file, "w") @@ -174,8 +160,7 @@ global src_file global out_file global update_count, generation_count - global carnivore_count, herbivore_count - global kill_rate, grass_density + global carnivore_count, herbivore_count, grass_density src_file = "ecologia.log" #Choose the right default output file name if "--table" in sys.argv: out_file = "populations.txt" @@ -198,8 +183,7 @@ out_file = out_file+".csv" #Initialise variables update_count, generation_count = [], [] - carnivore_count, herbivore_count = [], [] - kill_rate, grass_density = [], [] + carnivore_count, herbivore_count, grass_density = [], [], [] #Do the actual work load_log() if "--compact" in sys.argv or "--ods" in sys.argv: compact() diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7e1d0dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +releases +.classpath +.project +data +*.log diff --git a/README b/README index 7e39738..996df4c 100755 --- a/README +++ b/README @@ -2,6 +2,9 @@ Ecologia README =================== +--- THIS IS THE README FOR ECOLOGIA 1.1! --- + (a readme for version 2 is soon to follow) + Ecologia is a graphical ecosystem simulator. Currently it is still very basic, but hopefully it will soon be expanded to include more and more aspects of a real ecosystem. diff --git a/analysis/demography/population.py b/analysis/demography/population.py index 9490f0f..1955b45 100755 --- a/analysis/demography/population.py +++ b/analysis/demography/population.py @@ -18,12 +18,11 @@ global generation_count global carnivore_count global herbivore_count -global kill_rate global grass_density global src_file global out_file -version = "0.3" +version = "0.3.1" ''' @@ -32,8 +31,7 @@ def load_log(): global src_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density try: log_file = open(src_file, "r") line = log_file.readline() @@ -47,9 +45,6 @@ elif "Carnivore count" in line: next_count = line[line.find("Carnivore count: ")+17:-1] carnivore_count.append(int(next_count)) - elif "Carnivore hunt success rate: " in line: - next_count = line[line.find("rate: ")+6:-2] - kill_rate.append(int(next_count)) elif "Average grass density: " in line: next_count = line[line.find("density: ")+9:-2] grass_density.append(next_count) @@ -68,14 +63,12 @@ ''' def compact(): global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density if len(update_count) < 1000: return new_update_count = [update_count[0]] new_herbivore_count = [herbivore_count[0]] new_carnivore_count = [carnivore_count[0]] - new_kill_rate = [kill_rate[0]] new_grass_density = [grass_density[0]] new_generation_count = [generation_count[0]] for i in range(1, len(update_count)): @@ -83,13 +76,11 @@ new_update_count.append(update_count[i]) new_herbivore_count.append(herbivore_count[i]) new_carnivore_count.append(carnivore_count[i]) - new_kill_rate.append(kill_rate[i]) new_grass_density.append(grass_density[i]) new_generation_count.append(generation_count[i]) update_count = new_update_count herbivore_count = new_herbivore_count carnivore_count = new_carnivore_count - kill_rate = new_kill_rate grass_density = new_grass_density generation_count = new_generation_count compact() #Keep going until the data set is small enough @@ -100,8 +91,7 @@ def save_csv(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate all the data data = "Updates" for u in update_count: @@ -112,9 +102,6 @@ data = data+"\nCarnivores" for c in carnivore_count: data = data+","+str(c) - data = data+"\nKill rate" - for k in kill_rate: - data = data+","+str(k) data = data+"\nGrass density" for g in grass_density: data = data+","+str(g) @@ -136,13 +123,12 @@ def save_table(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate the data - data = "Updates\tHerbivores\tCarnivores\tKillRate\tGrassDensity\tGenerations" + data = "Updates\tHerbivores\tCarnivores\tGrassDensity\tGenerations" for i in range(len(update_count)): data = data+"\n"+str(update_count[i])+"\t"+str(herbivore_count[i])+"\t"+str(carnivore_count[i]) - data = data+"\t"+str(kill_rate[i])+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) + data = data+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) #Then write it to file try: table_file = open(out_file, "w") @@ -174,8 +160,7 @@ global src_file global out_file global update_count, generation_count - global carnivore_count, herbivore_count - global kill_rate, grass_density + global carnivore_count, herbivore_count, grass_density src_file = "ecologia.log" #Choose the right default output file name if "--table" in sys.argv: out_file = "populations.txt" @@ -198,8 +183,7 @@ out_file = out_file+".csv" #Initialise variables update_count, generation_count = [], [] - carnivore_count, herbivore_count = [], [] - kill_rate, grass_density = [], [] + carnivore_count, herbivore_count, grass_density = [], [], [] #Do the actual work load_log() if "--compact" in sys.argv or "--ods" in sys.argv: compact() diff --git a/ecologia.config b/ecologia.config new file mode 100755 index 0000000..67f2b5c --- /dev/null +++ b/ecologia.config @@ -0,0 +1,45 @@ +# +# This is the default Ecologia configuration file. Its values have +# been chosen to provide stable ecosystem dynamics when run. +# +# Daniel Vedder, 20/09/2016 +# + +[world] +xsize 100 +ysize 100 +timelapse 100 +stopAt 200 +waterTiles 10 +humidity 1 +startGrassDensity 100 +startNoCarnivores 50 +startNoHerbivores 200 +startEnergyCarnivores 150 +startEnergyHerbivores 100 + +[herbivore] +mutationRate 0 +speed 2 +stamina 10 +sight 4 +metabolism 10 +ageLimit 150 +strength 10 +reproductiveEnergy 120 +maturityAge 15 +gestation 10 +reproductionRate 2 + +[carnivore] +mutationRate 0 +speed 3 +stamina 10 +sight 4 +metabolism 18 +ageLimit 200 +strength 11 +reproductiveEnergy 200 +maturityAge 30 +gestation 10 +reproductionRate 1 diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7e1d0dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +releases +.classpath +.project +data +*.log diff --git a/README b/README index 7e39738..996df4c 100755 --- a/README +++ b/README @@ -2,6 +2,9 @@ Ecologia README =================== +--- THIS IS THE README FOR ECOLOGIA 1.1! --- + (a readme for version 2 is soon to follow) + Ecologia is a graphical ecosystem simulator. Currently it is still very basic, but hopefully it will soon be expanded to include more and more aspects of a real ecosystem. diff --git a/analysis/demography/population.py b/analysis/demography/population.py index 9490f0f..1955b45 100755 --- a/analysis/demography/population.py +++ b/analysis/demography/population.py @@ -18,12 +18,11 @@ global generation_count global carnivore_count global herbivore_count -global kill_rate global grass_density global src_file global out_file -version = "0.3" +version = "0.3.1" ''' @@ -32,8 +31,7 @@ def load_log(): global src_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density try: log_file = open(src_file, "r") line = log_file.readline() @@ -47,9 +45,6 @@ elif "Carnivore count" in line: next_count = line[line.find("Carnivore count: ")+17:-1] carnivore_count.append(int(next_count)) - elif "Carnivore hunt success rate: " in line: - next_count = line[line.find("rate: ")+6:-2] - kill_rate.append(int(next_count)) elif "Average grass density: " in line: next_count = line[line.find("density: ")+9:-2] grass_density.append(next_count) @@ -68,14 +63,12 @@ ''' def compact(): global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density if len(update_count) < 1000: return new_update_count = [update_count[0]] new_herbivore_count = [herbivore_count[0]] new_carnivore_count = [carnivore_count[0]] - new_kill_rate = [kill_rate[0]] new_grass_density = [grass_density[0]] new_generation_count = [generation_count[0]] for i in range(1, len(update_count)): @@ -83,13 +76,11 @@ new_update_count.append(update_count[i]) new_herbivore_count.append(herbivore_count[i]) new_carnivore_count.append(carnivore_count[i]) - new_kill_rate.append(kill_rate[i]) new_grass_density.append(grass_density[i]) new_generation_count.append(generation_count[i]) update_count = new_update_count herbivore_count = new_herbivore_count carnivore_count = new_carnivore_count - kill_rate = new_kill_rate grass_density = new_grass_density generation_count = new_generation_count compact() #Keep going until the data set is small enough @@ -100,8 +91,7 @@ def save_csv(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate all the data data = "Updates" for u in update_count: @@ -112,9 +102,6 @@ data = data+"\nCarnivores" for c in carnivore_count: data = data+","+str(c) - data = data+"\nKill rate" - for k in kill_rate: - data = data+","+str(k) data = data+"\nGrass density" for g in grass_density: data = data+","+str(g) @@ -136,13 +123,12 @@ def save_table(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate the data - data = "Updates\tHerbivores\tCarnivores\tKillRate\tGrassDensity\tGenerations" + data = "Updates\tHerbivores\tCarnivores\tGrassDensity\tGenerations" for i in range(len(update_count)): data = data+"\n"+str(update_count[i])+"\t"+str(herbivore_count[i])+"\t"+str(carnivore_count[i]) - data = data+"\t"+str(kill_rate[i])+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) + data = data+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) #Then write it to file try: table_file = open(out_file, "w") @@ -174,8 +160,7 @@ global src_file global out_file global update_count, generation_count - global carnivore_count, herbivore_count - global kill_rate, grass_density + global carnivore_count, herbivore_count, grass_density src_file = "ecologia.log" #Choose the right default output file name if "--table" in sys.argv: out_file = "populations.txt" @@ -198,8 +183,7 @@ out_file = out_file+".csv" #Initialise variables update_count, generation_count = [], [] - carnivore_count, herbivore_count = [], [] - kill_rate, grass_density = [], [] + carnivore_count, herbivore_count, grass_density = [], [], [] #Do the actual work load_log() if "--compact" in sys.argv or "--ods" in sys.argv: compact() diff --git a/ecologia.config b/ecologia.config new file mode 100755 index 0000000..67f2b5c --- /dev/null +++ b/ecologia.config @@ -0,0 +1,45 @@ +# +# This is the default Ecologia configuration file. Its values have +# been chosen to provide stable ecosystem dynamics when run. +# +# Daniel Vedder, 20/09/2016 +# + +[world] +xsize 100 +ysize 100 +timelapse 100 +stopAt 200 +waterTiles 10 +humidity 1 +startGrassDensity 100 +startNoCarnivores 50 +startNoHerbivores 200 +startEnergyCarnivores 150 +startEnergyHerbivores 100 + +[herbivore] +mutationRate 0 +speed 2 +stamina 10 +sight 4 +metabolism 10 +ageLimit 150 +strength 10 +reproductiveEnergy 120 +maturityAge 15 +gestation 10 +reproductionRate 2 + +[carnivore] +mutationRate 0 +speed 3 +stamina 10 +sight 4 +metabolism 18 +ageLimit 200 +strength 11 +reproductiveEnergy 200 +maturityAge 30 +gestation 10 +reproductionRate 1 diff --git a/log_run b/log_run new file mode 100755 index 0000000..bb98de6 --- /dev/null +++ b/log_run @@ -0,0 +1,13 @@ +#!/bin/bash +# Run Ecologia during development +# Automatically analyses the log file + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --config ../../ecologia.config +cp ecologia.log ../../analysis +cd ../../analysis/demography +./population.py --table --logfile ../ecologia.log +R --slave -f population.r +mv populations-*.jpg ../../data diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7e1d0dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +releases +.classpath +.project +data +*.log diff --git a/README b/README index 7e39738..996df4c 100755 --- a/README +++ b/README @@ -2,6 +2,9 @@ Ecologia README =================== +--- THIS IS THE README FOR ECOLOGIA 1.1! --- + (a readme for version 2 is soon to follow) + Ecologia is a graphical ecosystem simulator. Currently it is still very basic, but hopefully it will soon be expanded to include more and more aspects of a real ecosystem. diff --git a/analysis/demography/population.py b/analysis/demography/population.py index 9490f0f..1955b45 100755 --- a/analysis/demography/population.py +++ b/analysis/demography/population.py @@ -18,12 +18,11 @@ global generation_count global carnivore_count global herbivore_count -global kill_rate global grass_density global src_file global out_file -version = "0.3" +version = "0.3.1" ''' @@ -32,8 +31,7 @@ def load_log(): global src_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density try: log_file = open(src_file, "r") line = log_file.readline() @@ -47,9 +45,6 @@ elif "Carnivore count" in line: next_count = line[line.find("Carnivore count: ")+17:-1] carnivore_count.append(int(next_count)) - elif "Carnivore hunt success rate: " in line: - next_count = line[line.find("rate: ")+6:-2] - kill_rate.append(int(next_count)) elif "Average grass density: " in line: next_count = line[line.find("density: ")+9:-2] grass_density.append(next_count) @@ -68,14 +63,12 @@ ''' def compact(): global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density if len(update_count) < 1000: return new_update_count = [update_count[0]] new_herbivore_count = [herbivore_count[0]] new_carnivore_count = [carnivore_count[0]] - new_kill_rate = [kill_rate[0]] new_grass_density = [grass_density[0]] new_generation_count = [generation_count[0]] for i in range(1, len(update_count)): @@ -83,13 +76,11 @@ new_update_count.append(update_count[i]) new_herbivore_count.append(herbivore_count[i]) new_carnivore_count.append(carnivore_count[i]) - new_kill_rate.append(kill_rate[i]) new_grass_density.append(grass_density[i]) new_generation_count.append(generation_count[i]) update_count = new_update_count herbivore_count = new_herbivore_count carnivore_count = new_carnivore_count - kill_rate = new_kill_rate grass_density = new_grass_density generation_count = new_generation_count compact() #Keep going until the data set is small enough @@ -100,8 +91,7 @@ def save_csv(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate all the data data = "Updates" for u in update_count: @@ -112,9 +102,6 @@ data = data+"\nCarnivores" for c in carnivore_count: data = data+","+str(c) - data = data+"\nKill rate" - for k in kill_rate: - data = data+","+str(k) data = data+"\nGrass density" for g in grass_density: data = data+","+str(g) @@ -136,13 +123,12 @@ def save_table(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate the data - data = "Updates\tHerbivores\tCarnivores\tKillRate\tGrassDensity\tGenerations" + data = "Updates\tHerbivores\tCarnivores\tGrassDensity\tGenerations" for i in range(len(update_count)): data = data+"\n"+str(update_count[i])+"\t"+str(herbivore_count[i])+"\t"+str(carnivore_count[i]) - data = data+"\t"+str(kill_rate[i])+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) + data = data+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) #Then write it to file try: table_file = open(out_file, "w") @@ -174,8 +160,7 @@ global src_file global out_file global update_count, generation_count - global carnivore_count, herbivore_count - global kill_rate, grass_density + global carnivore_count, herbivore_count, grass_density src_file = "ecologia.log" #Choose the right default output file name if "--table" in sys.argv: out_file = "populations.txt" @@ -198,8 +183,7 @@ out_file = out_file+".csv" #Initialise variables update_count, generation_count = [], [] - carnivore_count, herbivore_count = [], [] - kill_rate, grass_density = [], [] + carnivore_count, herbivore_count, grass_density = [], [], [] #Do the actual work load_log() if "--compact" in sys.argv or "--ods" in sys.argv: compact() diff --git a/ecologia.config b/ecologia.config new file mode 100755 index 0000000..67f2b5c --- /dev/null +++ b/ecologia.config @@ -0,0 +1,45 @@ +# +# This is the default Ecologia configuration file. Its values have +# been chosen to provide stable ecosystem dynamics when run. +# +# Daniel Vedder, 20/09/2016 +# + +[world] +xsize 100 +ysize 100 +timelapse 100 +stopAt 200 +waterTiles 10 +humidity 1 +startGrassDensity 100 +startNoCarnivores 50 +startNoHerbivores 200 +startEnergyCarnivores 150 +startEnergyHerbivores 100 + +[herbivore] +mutationRate 0 +speed 2 +stamina 10 +sight 4 +metabolism 10 +ageLimit 150 +strength 10 +reproductiveEnergy 120 +maturityAge 15 +gestation 10 +reproductionRate 2 + +[carnivore] +mutationRate 0 +speed 3 +stamina 10 +sight 4 +metabolism 18 +ageLimit 200 +strength 11 +reproductiveEnergy 200 +maturityAge 30 +gestation 10 +reproductionRate 1 diff --git a/log_run b/log_run new file mode 100755 index 0000000..bb98de6 --- /dev/null +++ b/log_run @@ -0,0 +1,13 @@ +#!/bin/bash +# Run Ecologia during development +# Automatically analyses the log file + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --config ../../ecologia.config +cp ecologia.log ../../analysis +cd ../../analysis/demography +./population.py --table --logfile ../ecologia.log +R --slave -f population.r +mv populations-*.jpg ../../data diff --git a/release.py b/release.py index c610a24..d7e6a0c 100755 --- a/release.py +++ b/release.py @@ -23,8 +23,9 @@ main_file = open("src/main/Ecologia.java") main_source = main_file.read() main_file.close() + #This string surgery is a little iffy, but it works version_start = main_source.find("version = ")+11 - version_end = main_source.find('"', version_start) + version_end = main_source.find(' ', version_start) return main_source[version_start:version_end] ''' diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7e1d0dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +releases +.classpath +.project +data +*.log diff --git a/README b/README index 7e39738..996df4c 100755 --- a/README +++ b/README @@ -2,6 +2,9 @@ Ecologia README =================== +--- THIS IS THE README FOR ECOLOGIA 1.1! --- + (a readme for version 2 is soon to follow) + Ecologia is a graphical ecosystem simulator. Currently it is still very basic, but hopefully it will soon be expanded to include more and more aspects of a real ecosystem. diff --git a/analysis/demography/population.py b/analysis/demography/population.py index 9490f0f..1955b45 100755 --- a/analysis/demography/population.py +++ b/analysis/demography/population.py @@ -18,12 +18,11 @@ global generation_count global carnivore_count global herbivore_count -global kill_rate global grass_density global src_file global out_file -version = "0.3" +version = "0.3.1" ''' @@ -32,8 +31,7 @@ def load_log(): global src_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density try: log_file = open(src_file, "r") line = log_file.readline() @@ -47,9 +45,6 @@ elif "Carnivore count" in line: next_count = line[line.find("Carnivore count: ")+17:-1] carnivore_count.append(int(next_count)) - elif "Carnivore hunt success rate: " in line: - next_count = line[line.find("rate: ")+6:-2] - kill_rate.append(int(next_count)) elif "Average grass density: " in line: next_count = line[line.find("density: ")+9:-2] grass_density.append(next_count) @@ -68,14 +63,12 @@ ''' def compact(): global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density if len(update_count) < 1000: return new_update_count = [update_count[0]] new_herbivore_count = [herbivore_count[0]] new_carnivore_count = [carnivore_count[0]] - new_kill_rate = [kill_rate[0]] new_grass_density = [grass_density[0]] new_generation_count = [generation_count[0]] for i in range(1, len(update_count)): @@ -83,13 +76,11 @@ new_update_count.append(update_count[i]) new_herbivore_count.append(herbivore_count[i]) new_carnivore_count.append(carnivore_count[i]) - new_kill_rate.append(kill_rate[i]) new_grass_density.append(grass_density[i]) new_generation_count.append(generation_count[i]) update_count = new_update_count herbivore_count = new_herbivore_count carnivore_count = new_carnivore_count - kill_rate = new_kill_rate grass_density = new_grass_density generation_count = new_generation_count compact() #Keep going until the data set is small enough @@ -100,8 +91,7 @@ def save_csv(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate all the data data = "Updates" for u in update_count: @@ -112,9 +102,6 @@ data = data+"\nCarnivores" for c in carnivore_count: data = data+","+str(c) - data = data+"\nKill rate" - for k in kill_rate: - data = data+","+str(k) data = data+"\nGrass density" for g in grass_density: data = data+","+str(g) @@ -136,13 +123,12 @@ def save_table(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate the data - data = "Updates\tHerbivores\tCarnivores\tKillRate\tGrassDensity\tGenerations" + data = "Updates\tHerbivores\tCarnivores\tGrassDensity\tGenerations" for i in range(len(update_count)): data = data+"\n"+str(update_count[i])+"\t"+str(herbivore_count[i])+"\t"+str(carnivore_count[i]) - data = data+"\t"+str(kill_rate[i])+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) + data = data+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) #Then write it to file try: table_file = open(out_file, "w") @@ -174,8 +160,7 @@ global src_file global out_file global update_count, generation_count - global carnivore_count, herbivore_count - global kill_rate, grass_density + global carnivore_count, herbivore_count, grass_density src_file = "ecologia.log" #Choose the right default output file name if "--table" in sys.argv: out_file = "populations.txt" @@ -198,8 +183,7 @@ out_file = out_file+".csv" #Initialise variables update_count, generation_count = [], [] - carnivore_count, herbivore_count = [], [] - kill_rate, grass_density = [], [] + carnivore_count, herbivore_count, grass_density = [], [], [] #Do the actual work load_log() if "--compact" in sys.argv or "--ods" in sys.argv: compact() diff --git a/ecologia.config b/ecologia.config new file mode 100755 index 0000000..67f2b5c --- /dev/null +++ b/ecologia.config @@ -0,0 +1,45 @@ +# +# This is the default Ecologia configuration file. Its values have +# been chosen to provide stable ecosystem dynamics when run. +# +# Daniel Vedder, 20/09/2016 +# + +[world] +xsize 100 +ysize 100 +timelapse 100 +stopAt 200 +waterTiles 10 +humidity 1 +startGrassDensity 100 +startNoCarnivores 50 +startNoHerbivores 200 +startEnergyCarnivores 150 +startEnergyHerbivores 100 + +[herbivore] +mutationRate 0 +speed 2 +stamina 10 +sight 4 +metabolism 10 +ageLimit 150 +strength 10 +reproductiveEnergy 120 +maturityAge 15 +gestation 10 +reproductionRate 2 + +[carnivore] +mutationRate 0 +speed 3 +stamina 10 +sight 4 +metabolism 18 +ageLimit 200 +strength 11 +reproductiveEnergy 200 +maturityAge 30 +gestation 10 +reproductionRate 1 diff --git a/log_run b/log_run new file mode 100755 index 0000000..bb98de6 --- /dev/null +++ b/log_run @@ -0,0 +1,13 @@ +#!/bin/bash +# Run Ecologia during development +# Automatically analyses the log file + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --config ../../ecologia.config +cp ecologia.log ../../analysis +cd ../../analysis/demography +./population.py --table --logfile ../ecologia.log +R --slave -f population.r +mv populations-*.jpg ../../data diff --git a/release.py b/release.py index c610a24..d7e6a0c 100755 --- a/release.py +++ b/release.py @@ -23,8 +23,9 @@ main_file = open("src/main/Ecologia.java") main_source = main_file.read() main_file.close() + #This string surgery is a little iffy, but it works version_start = main_source.find("version = ")+11 - version_end = main_source.find('"', version_start) + version_end = main_source.find(' ', version_start) return main_source[version_start:version_end] ''' diff --git a/run b/run new file mode 100755 index 0000000..9de1ff8 --- /dev/null +++ b/run @@ -0,0 +1,7 @@ +#!/bin/bash +# Run Ecologia during development + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --debug --config ../../ecologia.config diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7e1d0dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +releases +.classpath +.project +data +*.log diff --git a/README b/README index 7e39738..996df4c 100755 --- a/README +++ b/README @@ -2,6 +2,9 @@ Ecologia README =================== +--- THIS IS THE README FOR ECOLOGIA 1.1! --- + (a readme for version 2 is soon to follow) + Ecologia is a graphical ecosystem simulator. Currently it is still very basic, but hopefully it will soon be expanded to include more and more aspects of a real ecosystem. diff --git a/analysis/demography/population.py b/analysis/demography/population.py index 9490f0f..1955b45 100755 --- a/analysis/demography/population.py +++ b/analysis/demography/population.py @@ -18,12 +18,11 @@ global generation_count global carnivore_count global herbivore_count -global kill_rate global grass_density global src_file global out_file -version = "0.3" +version = "0.3.1" ''' @@ -32,8 +31,7 @@ def load_log(): global src_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density try: log_file = open(src_file, "r") line = log_file.readline() @@ -47,9 +45,6 @@ elif "Carnivore count" in line: next_count = line[line.find("Carnivore count: ")+17:-1] carnivore_count.append(int(next_count)) - elif "Carnivore hunt success rate: " in line: - next_count = line[line.find("rate: ")+6:-2] - kill_rate.append(int(next_count)) elif "Average grass density: " in line: next_count = line[line.find("density: ")+9:-2] grass_density.append(next_count) @@ -68,14 +63,12 @@ ''' def compact(): global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density if len(update_count) < 1000: return new_update_count = [update_count[0]] new_herbivore_count = [herbivore_count[0]] new_carnivore_count = [carnivore_count[0]] - new_kill_rate = [kill_rate[0]] new_grass_density = [grass_density[0]] new_generation_count = [generation_count[0]] for i in range(1, len(update_count)): @@ -83,13 +76,11 @@ new_update_count.append(update_count[i]) new_herbivore_count.append(herbivore_count[i]) new_carnivore_count.append(carnivore_count[i]) - new_kill_rate.append(kill_rate[i]) new_grass_density.append(grass_density[i]) new_generation_count.append(generation_count[i]) update_count = new_update_count herbivore_count = new_herbivore_count carnivore_count = new_carnivore_count - kill_rate = new_kill_rate grass_density = new_grass_density generation_count = new_generation_count compact() #Keep going until the data set is small enough @@ -100,8 +91,7 @@ def save_csv(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate all the data data = "Updates" for u in update_count: @@ -112,9 +102,6 @@ data = data+"\nCarnivores" for c in carnivore_count: data = data+","+str(c) - data = data+"\nKill rate" - for k in kill_rate: - data = data+","+str(k) data = data+"\nGrass density" for g in grass_density: data = data+","+str(g) @@ -136,13 +123,12 @@ def save_table(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate the data - data = "Updates\tHerbivores\tCarnivores\tKillRate\tGrassDensity\tGenerations" + data = "Updates\tHerbivores\tCarnivores\tGrassDensity\tGenerations" for i in range(len(update_count)): data = data+"\n"+str(update_count[i])+"\t"+str(herbivore_count[i])+"\t"+str(carnivore_count[i]) - data = data+"\t"+str(kill_rate[i])+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) + data = data+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) #Then write it to file try: table_file = open(out_file, "w") @@ -174,8 +160,7 @@ global src_file global out_file global update_count, generation_count - global carnivore_count, herbivore_count - global kill_rate, grass_density + global carnivore_count, herbivore_count, grass_density src_file = "ecologia.log" #Choose the right default output file name if "--table" in sys.argv: out_file = "populations.txt" @@ -198,8 +183,7 @@ out_file = out_file+".csv" #Initialise variables update_count, generation_count = [], [] - carnivore_count, herbivore_count = [], [] - kill_rate, grass_density = [], [] + carnivore_count, herbivore_count, grass_density = [], [], [] #Do the actual work load_log() if "--compact" in sys.argv or "--ods" in sys.argv: compact() diff --git a/ecologia.config b/ecologia.config new file mode 100755 index 0000000..67f2b5c --- /dev/null +++ b/ecologia.config @@ -0,0 +1,45 @@ +# +# This is the default Ecologia configuration file. Its values have +# been chosen to provide stable ecosystem dynamics when run. +# +# Daniel Vedder, 20/09/2016 +# + +[world] +xsize 100 +ysize 100 +timelapse 100 +stopAt 200 +waterTiles 10 +humidity 1 +startGrassDensity 100 +startNoCarnivores 50 +startNoHerbivores 200 +startEnergyCarnivores 150 +startEnergyHerbivores 100 + +[herbivore] +mutationRate 0 +speed 2 +stamina 10 +sight 4 +metabolism 10 +ageLimit 150 +strength 10 +reproductiveEnergy 120 +maturityAge 15 +gestation 10 +reproductionRate 2 + +[carnivore] +mutationRate 0 +speed 3 +stamina 10 +sight 4 +metabolism 18 +ageLimit 200 +strength 11 +reproductiveEnergy 200 +maturityAge 30 +gestation 10 +reproductionRate 1 diff --git a/log_run b/log_run new file mode 100755 index 0000000..bb98de6 --- /dev/null +++ b/log_run @@ -0,0 +1,13 @@ +#!/bin/bash +# Run Ecologia during development +# Automatically analyses the log file + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --config ../../ecologia.config +cp ecologia.log ../../analysis +cd ../../analysis/demography +./population.py --table --logfile ../ecologia.log +R --slave -f population.r +mv populations-*.jpg ../../data diff --git a/release.py b/release.py index c610a24..d7e6a0c 100755 --- a/release.py +++ b/release.py @@ -23,8 +23,9 @@ main_file = open("src/main/Ecologia.java") main_source = main_file.read() main_file.close() + #This string surgery is a little iffy, but it works version_start = main_source.find("version = ")+11 - version_end = main_source.find('"', version_start) + version_end = main_source.find(' ', version_start) return main_source[version_start:version_end] ''' diff --git a/run b/run new file mode 100755 index 0000000..9de1ff8 --- /dev/null +++ b/run @@ -0,0 +1,7 @@ +#!/bin/bash +# Run Ecologia during development + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --debug --config ../../ecologia.config diff --git a/src/controller/World.java b/src/controller/World.java index 4acb5f3..68088e3 100755 --- a/src/controller/World.java +++ b/src/controller/World.java @@ -25,7 +25,8 @@ private static World world; //The Singleton instance of this class //Parameter variables - private int[] size; //The size of the world (x*y) + //TODO Reduce these to a single hashmap + /*private int[] size; //The size of the world (x*y) private int timelapse; //When running, the simulation will be updated every so many milliseconds. private int stopAt; //The simulation will stop once this update is reached. private int autorun; //The number of updates the simulation will run for automatically before quitting @@ -34,6 +35,9 @@ private int waterTiles; //The number of water tiles that will be created. private int startNoCarnivores, startNoHerbivores; //The starting number of carnivores/herbivores. private int startEnergyCarnivores, startEnergyHerbivores; //The starting energy for carnivores/herbivores. + */ + + private HashMap parameters; //Runtime variables private boolean running; //Is the simulation running? @@ -51,7 +55,7 @@ private World() { //Parameter settings - size = new int[] {100, 100}; //Default: 100*100 + /*size = new int[] {100, 100}; //Default: 100*100 timelapse = 100; //Default: 300 - can be changed at run-time stopAt = 200; //Default: 100 - can be changed at run-time autorun = -1; //Default: -1 (off) @@ -61,7 +65,21 @@ startNoCarnivores = 50; //Default: 50 - Hypothetical ideal: 5 (?) startNoHerbivores = 200; //Default: 200 - Hypothetical ideal: 160 (?) startEnergyCarnivores = 150; //Default: 150 - startEnergyHerbivores = 100; //Default: 100 + startEnergyHerbivores = 100; //Default: 100 */ + parameters = new HashMap(); + + parameters.put("xsize", 100); + parameters.put("ysize", 100); + parameters.put("timelapse", 100); + parameters.put("stopAt", 200); + parameters.put("autorun", -1); + parameters.put("waterTiles", 10); + parameters.put("humidity", 1); + parameters.put("startGrassDensity", 100); + parameters.put("startNoCarnivores", 50); + parameters.put("startNoHerbivores", 200); + parameters.put("startEnergyCarnivores", 150); + parameters.put("startEnergyHerbivores", 100); reset(); //Runtime variables } @@ -114,7 +132,7 @@ if (line.startsWith("[") && line.endsWith("]")) section = line; //Deal with world variables else if (section.equals("[world]")) { - switch (var) { + /*switch (var) { case "width": size[0] = value; break; case "height": size[1] = value; break; case "timelapse": timelapse = value; break; @@ -128,7 +146,8 @@ case "startEnergyHerbivores": startEnergyHerbivores = value; break; case "startEnergyCarnivores": startEnergyCarnivores = value; break; default: EcologiaIO.error("Invalid config variable in the [world] section: "+var); - } + }*/ + setParam(var, value); } //Configure default animal genomes else if (section.equals("[herbivore]")) { @@ -164,7 +183,7 @@ herbivoreCounter = 0; carnivoreCounter = 0; highestGeneration = 1; - averageGrassDensity = startGrassDensity; + averageGrassDensity = parameters.get("startGrassDensity"); animals = null; news = new ArrayList(); } @@ -244,6 +263,33 @@ if (type == OccupantType.HERBIVORE) Herbivore.defaultGenome = genome; else if (type == OccupantType.CARNIVORE) Carnivore.defaultGenome = genome; } + + /** + * Return a parameter value. + */ + public int getParam(String param) + { + if (parameters.containsKey(param)) + return parameters.get(param); + else { + EcologiaIO.error("getParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + return -1; //Will never be reached, but is syntactically needed + } + } + + /** + * Set a parameter value. + */ + public void setParam(String param, int value) + { + //XXX Is this check necessary here? + if (parameters.containsKey(param)) + parameters.put(param, value); + else EcologiaIO.error("setParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + } + + +/* public int[] getSize() { @@ -358,6 +404,8 @@ this.startEnergyHerbivores = startEnergyHerbivores; } +*/ + public int getHerbivoreCount() { return herbivoreCounter; diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7e1d0dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +releases +.classpath +.project +data +*.log diff --git a/README b/README index 7e39738..996df4c 100755 --- a/README +++ b/README @@ -2,6 +2,9 @@ Ecologia README =================== +--- THIS IS THE README FOR ECOLOGIA 1.1! --- + (a readme for version 2 is soon to follow) + Ecologia is a graphical ecosystem simulator. Currently it is still very basic, but hopefully it will soon be expanded to include more and more aspects of a real ecosystem. diff --git a/analysis/demography/population.py b/analysis/demography/population.py index 9490f0f..1955b45 100755 --- a/analysis/demography/population.py +++ b/analysis/demography/population.py @@ -18,12 +18,11 @@ global generation_count global carnivore_count global herbivore_count -global kill_rate global grass_density global src_file global out_file -version = "0.3" +version = "0.3.1" ''' @@ -32,8 +31,7 @@ def load_log(): global src_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density try: log_file = open(src_file, "r") line = log_file.readline() @@ -47,9 +45,6 @@ elif "Carnivore count" in line: next_count = line[line.find("Carnivore count: ")+17:-1] carnivore_count.append(int(next_count)) - elif "Carnivore hunt success rate: " in line: - next_count = line[line.find("rate: ")+6:-2] - kill_rate.append(int(next_count)) elif "Average grass density: " in line: next_count = line[line.find("density: ")+9:-2] grass_density.append(next_count) @@ -68,14 +63,12 @@ ''' def compact(): global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density if len(update_count) < 1000: return new_update_count = [update_count[0]] new_herbivore_count = [herbivore_count[0]] new_carnivore_count = [carnivore_count[0]] - new_kill_rate = [kill_rate[0]] new_grass_density = [grass_density[0]] new_generation_count = [generation_count[0]] for i in range(1, len(update_count)): @@ -83,13 +76,11 @@ new_update_count.append(update_count[i]) new_herbivore_count.append(herbivore_count[i]) new_carnivore_count.append(carnivore_count[i]) - new_kill_rate.append(kill_rate[i]) new_grass_density.append(grass_density[i]) new_generation_count.append(generation_count[i]) update_count = new_update_count herbivore_count = new_herbivore_count carnivore_count = new_carnivore_count - kill_rate = new_kill_rate grass_density = new_grass_density generation_count = new_generation_count compact() #Keep going until the data set is small enough @@ -100,8 +91,7 @@ def save_csv(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate all the data data = "Updates" for u in update_count: @@ -112,9 +102,6 @@ data = data+"\nCarnivores" for c in carnivore_count: data = data+","+str(c) - data = data+"\nKill rate" - for k in kill_rate: - data = data+","+str(k) data = data+"\nGrass density" for g in grass_density: data = data+","+str(g) @@ -136,13 +123,12 @@ def save_table(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate the data - data = "Updates\tHerbivores\tCarnivores\tKillRate\tGrassDensity\tGenerations" + data = "Updates\tHerbivores\tCarnivores\tGrassDensity\tGenerations" for i in range(len(update_count)): data = data+"\n"+str(update_count[i])+"\t"+str(herbivore_count[i])+"\t"+str(carnivore_count[i]) - data = data+"\t"+str(kill_rate[i])+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) + data = data+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) #Then write it to file try: table_file = open(out_file, "w") @@ -174,8 +160,7 @@ global src_file global out_file global update_count, generation_count - global carnivore_count, herbivore_count - global kill_rate, grass_density + global carnivore_count, herbivore_count, grass_density src_file = "ecologia.log" #Choose the right default output file name if "--table" in sys.argv: out_file = "populations.txt" @@ -198,8 +183,7 @@ out_file = out_file+".csv" #Initialise variables update_count, generation_count = [], [] - carnivore_count, herbivore_count = [], [] - kill_rate, grass_density = [], [] + carnivore_count, herbivore_count, grass_density = [], [], [] #Do the actual work load_log() if "--compact" in sys.argv or "--ods" in sys.argv: compact() diff --git a/ecologia.config b/ecologia.config new file mode 100755 index 0000000..67f2b5c --- /dev/null +++ b/ecologia.config @@ -0,0 +1,45 @@ +# +# This is the default Ecologia configuration file. Its values have +# been chosen to provide stable ecosystem dynamics when run. +# +# Daniel Vedder, 20/09/2016 +# + +[world] +xsize 100 +ysize 100 +timelapse 100 +stopAt 200 +waterTiles 10 +humidity 1 +startGrassDensity 100 +startNoCarnivores 50 +startNoHerbivores 200 +startEnergyCarnivores 150 +startEnergyHerbivores 100 + +[herbivore] +mutationRate 0 +speed 2 +stamina 10 +sight 4 +metabolism 10 +ageLimit 150 +strength 10 +reproductiveEnergy 120 +maturityAge 15 +gestation 10 +reproductionRate 2 + +[carnivore] +mutationRate 0 +speed 3 +stamina 10 +sight 4 +metabolism 18 +ageLimit 200 +strength 11 +reproductiveEnergy 200 +maturityAge 30 +gestation 10 +reproductionRate 1 diff --git a/log_run b/log_run new file mode 100755 index 0000000..bb98de6 --- /dev/null +++ b/log_run @@ -0,0 +1,13 @@ +#!/bin/bash +# Run Ecologia during development +# Automatically analyses the log file + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --config ../../ecologia.config +cp ecologia.log ../../analysis +cd ../../analysis/demography +./population.py --table --logfile ../ecologia.log +R --slave -f population.r +mv populations-*.jpg ../../data diff --git a/release.py b/release.py index c610a24..d7e6a0c 100755 --- a/release.py +++ b/release.py @@ -23,8 +23,9 @@ main_file = open("src/main/Ecologia.java") main_source = main_file.read() main_file.close() + #This string surgery is a little iffy, but it works version_start = main_source.find("version = ")+11 - version_end = main_source.find('"', version_start) + version_end = main_source.find(' ', version_start) return main_source[version_start:version_end] ''' diff --git a/run b/run new file mode 100755 index 0000000..9de1ff8 --- /dev/null +++ b/run @@ -0,0 +1,7 @@ +#!/bin/bash +# Run Ecologia during development + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --debug --config ../../ecologia.config diff --git a/src/controller/World.java b/src/controller/World.java index 4acb5f3..68088e3 100755 --- a/src/controller/World.java +++ b/src/controller/World.java @@ -25,7 +25,8 @@ private static World world; //The Singleton instance of this class //Parameter variables - private int[] size; //The size of the world (x*y) + //TODO Reduce these to a single hashmap + /*private int[] size; //The size of the world (x*y) private int timelapse; //When running, the simulation will be updated every so many milliseconds. private int stopAt; //The simulation will stop once this update is reached. private int autorun; //The number of updates the simulation will run for automatically before quitting @@ -34,6 +35,9 @@ private int waterTiles; //The number of water tiles that will be created. private int startNoCarnivores, startNoHerbivores; //The starting number of carnivores/herbivores. private int startEnergyCarnivores, startEnergyHerbivores; //The starting energy for carnivores/herbivores. + */ + + private HashMap parameters; //Runtime variables private boolean running; //Is the simulation running? @@ -51,7 +55,7 @@ private World() { //Parameter settings - size = new int[] {100, 100}; //Default: 100*100 + /*size = new int[] {100, 100}; //Default: 100*100 timelapse = 100; //Default: 300 - can be changed at run-time stopAt = 200; //Default: 100 - can be changed at run-time autorun = -1; //Default: -1 (off) @@ -61,7 +65,21 @@ startNoCarnivores = 50; //Default: 50 - Hypothetical ideal: 5 (?) startNoHerbivores = 200; //Default: 200 - Hypothetical ideal: 160 (?) startEnergyCarnivores = 150; //Default: 150 - startEnergyHerbivores = 100; //Default: 100 + startEnergyHerbivores = 100; //Default: 100 */ + parameters = new HashMap(); + + parameters.put("xsize", 100); + parameters.put("ysize", 100); + parameters.put("timelapse", 100); + parameters.put("stopAt", 200); + parameters.put("autorun", -1); + parameters.put("waterTiles", 10); + parameters.put("humidity", 1); + parameters.put("startGrassDensity", 100); + parameters.put("startNoCarnivores", 50); + parameters.put("startNoHerbivores", 200); + parameters.put("startEnergyCarnivores", 150); + parameters.put("startEnergyHerbivores", 100); reset(); //Runtime variables } @@ -114,7 +132,7 @@ if (line.startsWith("[") && line.endsWith("]")) section = line; //Deal with world variables else if (section.equals("[world]")) { - switch (var) { + /*switch (var) { case "width": size[0] = value; break; case "height": size[1] = value; break; case "timelapse": timelapse = value; break; @@ -128,7 +146,8 @@ case "startEnergyHerbivores": startEnergyHerbivores = value; break; case "startEnergyCarnivores": startEnergyCarnivores = value; break; default: EcologiaIO.error("Invalid config variable in the [world] section: "+var); - } + }*/ + setParam(var, value); } //Configure default animal genomes else if (section.equals("[herbivore]")) { @@ -164,7 +183,7 @@ herbivoreCounter = 0; carnivoreCounter = 0; highestGeneration = 1; - averageGrassDensity = startGrassDensity; + averageGrassDensity = parameters.get("startGrassDensity"); animals = null; news = new ArrayList(); } @@ -244,6 +263,33 @@ if (type == OccupantType.HERBIVORE) Herbivore.defaultGenome = genome; else if (type == OccupantType.CARNIVORE) Carnivore.defaultGenome = genome; } + + /** + * Return a parameter value. + */ + public int getParam(String param) + { + if (parameters.containsKey(param)) + return parameters.get(param); + else { + EcologiaIO.error("getParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + return -1; //Will never be reached, but is syntactically needed + } + } + + /** + * Set a parameter value. + */ + public void setParam(String param, int value) + { + //XXX Is this check necessary here? + if (parameters.containsKey(param)) + parameters.put(param, value); + else EcologiaIO.error("setParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + } + + +/* public int[] getSize() { @@ -358,6 +404,8 @@ this.startEnergyHerbivores = startEnergyHerbivores; } +*/ + public int getHerbivoreCount() { return herbivoreCounter; diff --git a/src/main/EcoTest.java b/src/main/EcoTest.java index 6d456d4..240f503 100755 --- a/src/main/EcoTest.java +++ b/src/main/EcoTest.java @@ -9,6 +9,8 @@ * (below) is set to true. * * TODO: expand this into a proper testing framework? + * + * THIS IS OUTDATED - DO NOT USE WITHOUT MAJOR MODIFICATIONS * * @author Daniel Vedder * @date 24.12.2014 @@ -45,13 +47,13 @@ if (turn == 1) { EcologiaIO.debug("Ecotest: Creating a herbivore at (1, 0)"); Herbivore herbivore = new Herbivore(World.getInstance().getNextID(), - Herbivore.defaultGenome, -1, 1, 0, - World.getInstance().getStartEnergyHerbivores(), 0); + Herbivore.defaultGenome, -1, 1, 0, + World.getInstance().getParam("startEnergyHerbivores"), 0); Simulator.addAnimal(herbivore); EcologiaIO.debug("Ecotest: Creating a carnivore at (1, 3)"); Carnivore carnivore = new Carnivore(World.getInstance().getNextID(), - Carnivore.defaultGenome, -1, 1, 3, - World.getInstance().getStartEnergyCarnivores(), 0); + Carnivore.defaultGenome, -1, 1, 3, + World.getInstance().getParam("startEnergyCarnivores"), 0); Simulator.addAnimal(carnivore); } if (turn == 2) { @@ -98,8 +100,8 @@ tpcY = 0; EcologiaIO.debug("Creating a carnivore at (0, 0)"); Carnivore carnivore = new Carnivore(World.getInstance().getNextID(), - Carnivore.defaultGenome, -1, tpcX, tpcY, - World.getInstance().getStartEnergyCarnivores(), 0); + Carnivore.defaultGenome, -1, tpcX, tpcY, + World.getInstance().getParam("startEnergyCarnivores"), 0); Simulator.addAnimal(carnivore); } else if (turn < 5 && turn > 1) { diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7e1d0dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +releases +.classpath +.project +data +*.log diff --git a/README b/README index 7e39738..996df4c 100755 --- a/README +++ b/README @@ -2,6 +2,9 @@ Ecologia README =================== +--- THIS IS THE README FOR ECOLOGIA 1.1! --- + (a readme for version 2 is soon to follow) + Ecologia is a graphical ecosystem simulator. Currently it is still very basic, but hopefully it will soon be expanded to include more and more aspects of a real ecosystem. diff --git a/analysis/demography/population.py b/analysis/demography/population.py index 9490f0f..1955b45 100755 --- a/analysis/demography/population.py +++ b/analysis/demography/population.py @@ -18,12 +18,11 @@ global generation_count global carnivore_count global herbivore_count -global kill_rate global grass_density global src_file global out_file -version = "0.3" +version = "0.3.1" ''' @@ -32,8 +31,7 @@ def load_log(): global src_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density try: log_file = open(src_file, "r") line = log_file.readline() @@ -47,9 +45,6 @@ elif "Carnivore count" in line: next_count = line[line.find("Carnivore count: ")+17:-1] carnivore_count.append(int(next_count)) - elif "Carnivore hunt success rate: " in line: - next_count = line[line.find("rate: ")+6:-2] - kill_rate.append(int(next_count)) elif "Average grass density: " in line: next_count = line[line.find("density: ")+9:-2] grass_density.append(next_count) @@ -68,14 +63,12 @@ ''' def compact(): global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density if len(update_count) < 1000: return new_update_count = [update_count[0]] new_herbivore_count = [herbivore_count[0]] new_carnivore_count = [carnivore_count[0]] - new_kill_rate = [kill_rate[0]] new_grass_density = [grass_density[0]] new_generation_count = [generation_count[0]] for i in range(1, len(update_count)): @@ -83,13 +76,11 @@ new_update_count.append(update_count[i]) new_herbivore_count.append(herbivore_count[i]) new_carnivore_count.append(carnivore_count[i]) - new_kill_rate.append(kill_rate[i]) new_grass_density.append(grass_density[i]) new_generation_count.append(generation_count[i]) update_count = new_update_count herbivore_count = new_herbivore_count carnivore_count = new_carnivore_count - kill_rate = new_kill_rate grass_density = new_grass_density generation_count = new_generation_count compact() #Keep going until the data set is small enough @@ -100,8 +91,7 @@ def save_csv(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate all the data data = "Updates" for u in update_count: @@ -112,9 +102,6 @@ data = data+"\nCarnivores" for c in carnivore_count: data = data+","+str(c) - data = data+"\nKill rate" - for k in kill_rate: - data = data+","+str(k) data = data+"\nGrass density" for g in grass_density: data = data+","+str(g) @@ -136,13 +123,12 @@ def save_table(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate the data - data = "Updates\tHerbivores\tCarnivores\tKillRate\tGrassDensity\tGenerations" + data = "Updates\tHerbivores\tCarnivores\tGrassDensity\tGenerations" for i in range(len(update_count)): data = data+"\n"+str(update_count[i])+"\t"+str(herbivore_count[i])+"\t"+str(carnivore_count[i]) - data = data+"\t"+str(kill_rate[i])+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) + data = data+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) #Then write it to file try: table_file = open(out_file, "w") @@ -174,8 +160,7 @@ global src_file global out_file global update_count, generation_count - global carnivore_count, herbivore_count - global kill_rate, grass_density + global carnivore_count, herbivore_count, grass_density src_file = "ecologia.log" #Choose the right default output file name if "--table" in sys.argv: out_file = "populations.txt" @@ -198,8 +183,7 @@ out_file = out_file+".csv" #Initialise variables update_count, generation_count = [], [] - carnivore_count, herbivore_count = [], [] - kill_rate, grass_density = [], [] + carnivore_count, herbivore_count, grass_density = [], [], [] #Do the actual work load_log() if "--compact" in sys.argv or "--ods" in sys.argv: compact() diff --git a/ecologia.config b/ecologia.config new file mode 100755 index 0000000..67f2b5c --- /dev/null +++ b/ecologia.config @@ -0,0 +1,45 @@ +# +# This is the default Ecologia configuration file. Its values have +# been chosen to provide stable ecosystem dynamics when run. +# +# Daniel Vedder, 20/09/2016 +# + +[world] +xsize 100 +ysize 100 +timelapse 100 +stopAt 200 +waterTiles 10 +humidity 1 +startGrassDensity 100 +startNoCarnivores 50 +startNoHerbivores 200 +startEnergyCarnivores 150 +startEnergyHerbivores 100 + +[herbivore] +mutationRate 0 +speed 2 +stamina 10 +sight 4 +metabolism 10 +ageLimit 150 +strength 10 +reproductiveEnergy 120 +maturityAge 15 +gestation 10 +reproductionRate 2 + +[carnivore] +mutationRate 0 +speed 3 +stamina 10 +sight 4 +metabolism 18 +ageLimit 200 +strength 11 +reproductiveEnergy 200 +maturityAge 30 +gestation 10 +reproductionRate 1 diff --git a/log_run b/log_run new file mode 100755 index 0000000..bb98de6 --- /dev/null +++ b/log_run @@ -0,0 +1,13 @@ +#!/bin/bash +# Run Ecologia during development +# Automatically analyses the log file + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --config ../../ecologia.config +cp ecologia.log ../../analysis +cd ../../analysis/demography +./population.py --table --logfile ../ecologia.log +R --slave -f population.r +mv populations-*.jpg ../../data diff --git a/release.py b/release.py index c610a24..d7e6a0c 100755 --- a/release.py +++ b/release.py @@ -23,8 +23,9 @@ main_file = open("src/main/Ecologia.java") main_source = main_file.read() main_file.close() + #This string surgery is a little iffy, but it works version_start = main_source.find("version = ")+11 - version_end = main_source.find('"', version_start) + version_end = main_source.find(' ', version_start) return main_source[version_start:version_end] ''' diff --git a/run b/run new file mode 100755 index 0000000..9de1ff8 --- /dev/null +++ b/run @@ -0,0 +1,7 @@ +#!/bin/bash +# Run Ecologia during development + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --debug --config ../../ecologia.config diff --git a/src/controller/World.java b/src/controller/World.java index 4acb5f3..68088e3 100755 --- a/src/controller/World.java +++ b/src/controller/World.java @@ -25,7 +25,8 @@ private static World world; //The Singleton instance of this class //Parameter variables - private int[] size; //The size of the world (x*y) + //TODO Reduce these to a single hashmap + /*private int[] size; //The size of the world (x*y) private int timelapse; //When running, the simulation will be updated every so many milliseconds. private int stopAt; //The simulation will stop once this update is reached. private int autorun; //The number of updates the simulation will run for automatically before quitting @@ -34,6 +35,9 @@ private int waterTiles; //The number of water tiles that will be created. private int startNoCarnivores, startNoHerbivores; //The starting number of carnivores/herbivores. private int startEnergyCarnivores, startEnergyHerbivores; //The starting energy for carnivores/herbivores. + */ + + private HashMap parameters; //Runtime variables private boolean running; //Is the simulation running? @@ -51,7 +55,7 @@ private World() { //Parameter settings - size = new int[] {100, 100}; //Default: 100*100 + /*size = new int[] {100, 100}; //Default: 100*100 timelapse = 100; //Default: 300 - can be changed at run-time stopAt = 200; //Default: 100 - can be changed at run-time autorun = -1; //Default: -1 (off) @@ -61,7 +65,21 @@ startNoCarnivores = 50; //Default: 50 - Hypothetical ideal: 5 (?) startNoHerbivores = 200; //Default: 200 - Hypothetical ideal: 160 (?) startEnergyCarnivores = 150; //Default: 150 - startEnergyHerbivores = 100; //Default: 100 + startEnergyHerbivores = 100; //Default: 100 */ + parameters = new HashMap(); + + parameters.put("xsize", 100); + parameters.put("ysize", 100); + parameters.put("timelapse", 100); + parameters.put("stopAt", 200); + parameters.put("autorun", -1); + parameters.put("waterTiles", 10); + parameters.put("humidity", 1); + parameters.put("startGrassDensity", 100); + parameters.put("startNoCarnivores", 50); + parameters.put("startNoHerbivores", 200); + parameters.put("startEnergyCarnivores", 150); + parameters.put("startEnergyHerbivores", 100); reset(); //Runtime variables } @@ -114,7 +132,7 @@ if (line.startsWith("[") && line.endsWith("]")) section = line; //Deal with world variables else if (section.equals("[world]")) { - switch (var) { + /*switch (var) { case "width": size[0] = value; break; case "height": size[1] = value; break; case "timelapse": timelapse = value; break; @@ -128,7 +146,8 @@ case "startEnergyHerbivores": startEnergyHerbivores = value; break; case "startEnergyCarnivores": startEnergyCarnivores = value; break; default: EcologiaIO.error("Invalid config variable in the [world] section: "+var); - } + }*/ + setParam(var, value); } //Configure default animal genomes else if (section.equals("[herbivore]")) { @@ -164,7 +183,7 @@ herbivoreCounter = 0; carnivoreCounter = 0; highestGeneration = 1; - averageGrassDensity = startGrassDensity; + averageGrassDensity = parameters.get("startGrassDensity"); animals = null; news = new ArrayList(); } @@ -244,6 +263,33 @@ if (type == OccupantType.HERBIVORE) Herbivore.defaultGenome = genome; else if (type == OccupantType.CARNIVORE) Carnivore.defaultGenome = genome; } + + /** + * Return a parameter value. + */ + public int getParam(String param) + { + if (parameters.containsKey(param)) + return parameters.get(param); + else { + EcologiaIO.error("getParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + return -1; //Will never be reached, but is syntactically needed + } + } + + /** + * Set a parameter value. + */ + public void setParam(String param, int value) + { + //XXX Is this check necessary here? + if (parameters.containsKey(param)) + parameters.put(param, value); + else EcologiaIO.error("setParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + } + + +/* public int[] getSize() { @@ -358,6 +404,8 @@ this.startEnergyHerbivores = startEnergyHerbivores; } +*/ + public int getHerbivoreCount() { return herbivoreCounter; diff --git a/src/main/EcoTest.java b/src/main/EcoTest.java index 6d456d4..240f503 100755 --- a/src/main/EcoTest.java +++ b/src/main/EcoTest.java @@ -9,6 +9,8 @@ * (below) is set to true. * * TODO: expand this into a proper testing framework? + * + * THIS IS OUTDATED - DO NOT USE WITHOUT MAJOR MODIFICATIONS * * @author Daniel Vedder * @date 24.12.2014 @@ -45,13 +47,13 @@ if (turn == 1) { EcologiaIO.debug("Ecotest: Creating a herbivore at (1, 0)"); Herbivore herbivore = new Herbivore(World.getInstance().getNextID(), - Herbivore.defaultGenome, -1, 1, 0, - World.getInstance().getStartEnergyHerbivores(), 0); + Herbivore.defaultGenome, -1, 1, 0, + World.getInstance().getParam("startEnergyHerbivores"), 0); Simulator.addAnimal(herbivore); EcologiaIO.debug("Ecotest: Creating a carnivore at (1, 3)"); Carnivore carnivore = new Carnivore(World.getInstance().getNextID(), - Carnivore.defaultGenome, -1, 1, 3, - World.getInstance().getStartEnergyCarnivores(), 0); + Carnivore.defaultGenome, -1, 1, 3, + World.getInstance().getParam("startEnergyCarnivores"), 0); Simulator.addAnimal(carnivore); } if (turn == 2) { @@ -98,8 +100,8 @@ tpcY = 0; EcologiaIO.debug("Creating a carnivore at (0, 0)"); Carnivore carnivore = new Carnivore(World.getInstance().getNextID(), - Carnivore.defaultGenome, -1, tpcX, tpcY, - World.getInstance().getStartEnergyCarnivores(), 0); + Carnivore.defaultGenome, -1, tpcX, tpcY, + World.getInstance().getParam("startEnergyCarnivores"), 0); Simulator.addAnimal(carnivore); } else if (turn < 5 && turn > 1) { diff --git a/src/main/Ecologia.java b/src/main/Ecologia.java index 4d1ed75..cd308c0 100755 --- a/src/main/Ecologia.java +++ b/src/main/Ecologia.java @@ -5,8 +5,9 @@ import model.Simulator; /** - * Ecologia is a relatively simple ecosystem simulator, designed to show basic - * relationships between predators, prey and producers. + * This is an individual-based model of the ecology of the saiga antelope + * (Saiga tatarica tatarica). The source code was forked from the Ecologia + * ecosystem simulator, version 1.1 (www.launchpad.net/ecologia). * * Copyright (C) 2014-2016 Daniel Vedder * @@ -28,18 +29,18 @@ * manager, updating the program each round. * * @author Daniel Vedder - * @version 28.8.2014 + * @version 20.12.2016 */ public class Ecologia implements Runnable { public static Ecologia eco; //The singleton object - public static final String version = "1.1"; + public static final String version = "2.0 Saiga"; private static boolean noGUI = false; private GUI gui; private Simulator simulator; - private EcoTest tester; + //private EcoTest tester; //XXX Currently not used private Thread runningThread; @@ -73,7 +74,7 @@ else if (a.equals("--no-graphics")) noGUI = true; else if (a.equals("--autorun")) { - World.getInstance().setAutorun(new Integer(args[i+1])); + World.getInstance().setParam("autorun", new Integer(args[i+1])); i++; } else if (a.equals("--config")) { @@ -81,7 +82,7 @@ i++; } else if (a.equals("--timelapse")) { - World.getInstance().setTimelapse(new Integer(args[i+1])); + World.getInstance().setParam("timelapse", new Integer(args[i+1])); i++; } else EcologiaIO.error("Invalid commandline parameter: "+a); @@ -93,7 +94,7 @@ EcologiaIO.printStatus(); //Only use no-graphics mode when on autorun - if (noGUI && (World.getInstance().getAutorun() < 0)) { + if (noGUI && (World.getInstance().getParam("autorun") < 0)) { EcologiaIO.error("Returning to graphics mode as autorun not enabled."); noGUI = false; } @@ -119,9 +120,9 @@ EcologiaIO.log("Launching Ecologia..."); simulator = new Simulator(); if (!noGUI) gui = new GUI(); - tester = new EcoTest(); + //tester = new EcoTest(); EcologiaIO.debug("Launch completed."); - if (World.getInstance().getAutorun() > 0) autorun(); + if (World.getInstance().getParam("autorun") > 0) autorun(); } /** @@ -129,8 +130,8 @@ */ private void autorun() { - EcologiaIO.log("Performing autorun for "+World.getInstance().getAutorun()+" updates."); - World.getInstance().setStopAt(-1); + EcologiaIO.log("Performing autorun for "+World.getInstance().getParam("autorun")+" updates."); + World.getInstance().setParam("stopAt", -1); startThread(); } @@ -138,7 +139,7 @@ * Reset the simulator in order to start a new run. * * XXX: Depending on how long the simulation has already - * been running, this can take quite a long time. + * been running, this can take quite a long time. -- Does it? */ public void reset() { @@ -183,11 +184,11 @@ */ public synchronized void iterate() { - int autorun = World.getInstance().getAutorun(); + int autorun = World.getInstance().getParam("autorun"); World.getInstance().incrementTurn(); int turn = World.getInstance().getTurn(); EcologiaIO.log("Executing update "+turn); - if (EcologiaIO.debugging) tester.runTest(); + //if (EcologiaIO.debugging) tester.runTest(); simulator.update(); EcologiaIO.log("Average grass density: "+World.getInstance().getAverageGrassDensity()+"%"); EcologiaIO.log("Herbivore count: "+World.getInstance().getHerbivoreCount()); @@ -195,7 +196,7 @@ EcologiaIO.log("Generation counter: "+World.getInstance().getGeneration()); //If the stopAt number is reached, pause the simulation - if (World.getInstance().getStopAt() == turn) { + if (World.getInstance().getParam("stopAt") == turn) { World.getInstance().setRunning(false); } if (!noGUI) gui.update(); @@ -213,7 +214,7 @@ } //Pause for as long as the user wants try { - Thread.sleep(World.getInstance().getTimelapse()); + Thread.sleep(World.getInstance().getParam("timelapse")); } catch (InterruptedException ie) {} } @@ -223,7 +224,7 @@ */ private static void printHelp() { - System.out.println("Ecologia "+version+", the simple ecosystem simulator."); + System.out.println("Ecologia "+version+", an individual-based model of saiga ecology."); System.out.println("\nCommandline options:\n"); System.out.println("--help -h Print this help text"); System.out.println("--version -V Print the version number\n"); diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7e1d0dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +releases +.classpath +.project +data +*.log diff --git a/README b/README index 7e39738..996df4c 100755 --- a/README +++ b/README @@ -2,6 +2,9 @@ Ecologia README =================== +--- THIS IS THE README FOR ECOLOGIA 1.1! --- + (a readme for version 2 is soon to follow) + Ecologia is a graphical ecosystem simulator. Currently it is still very basic, but hopefully it will soon be expanded to include more and more aspects of a real ecosystem. diff --git a/analysis/demography/population.py b/analysis/demography/population.py index 9490f0f..1955b45 100755 --- a/analysis/demography/population.py +++ b/analysis/demography/population.py @@ -18,12 +18,11 @@ global generation_count global carnivore_count global herbivore_count -global kill_rate global grass_density global src_file global out_file -version = "0.3" +version = "0.3.1" ''' @@ -32,8 +31,7 @@ def load_log(): global src_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density try: log_file = open(src_file, "r") line = log_file.readline() @@ -47,9 +45,6 @@ elif "Carnivore count" in line: next_count = line[line.find("Carnivore count: ")+17:-1] carnivore_count.append(int(next_count)) - elif "Carnivore hunt success rate: " in line: - next_count = line[line.find("rate: ")+6:-2] - kill_rate.append(int(next_count)) elif "Average grass density: " in line: next_count = line[line.find("density: ")+9:-2] grass_density.append(next_count) @@ -68,14 +63,12 @@ ''' def compact(): global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density if len(update_count) < 1000: return new_update_count = [update_count[0]] new_herbivore_count = [herbivore_count[0]] new_carnivore_count = [carnivore_count[0]] - new_kill_rate = [kill_rate[0]] new_grass_density = [grass_density[0]] new_generation_count = [generation_count[0]] for i in range(1, len(update_count)): @@ -83,13 +76,11 @@ new_update_count.append(update_count[i]) new_herbivore_count.append(herbivore_count[i]) new_carnivore_count.append(carnivore_count[i]) - new_kill_rate.append(kill_rate[i]) new_grass_density.append(grass_density[i]) new_generation_count.append(generation_count[i]) update_count = new_update_count herbivore_count = new_herbivore_count carnivore_count = new_carnivore_count - kill_rate = new_kill_rate grass_density = new_grass_density generation_count = new_generation_count compact() #Keep going until the data set is small enough @@ -100,8 +91,7 @@ def save_csv(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate all the data data = "Updates" for u in update_count: @@ -112,9 +102,6 @@ data = data+"\nCarnivores" for c in carnivore_count: data = data+","+str(c) - data = data+"\nKill rate" - for k in kill_rate: - data = data+","+str(k) data = data+"\nGrass density" for g in grass_density: data = data+","+str(g) @@ -136,13 +123,12 @@ def save_table(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate the data - data = "Updates\tHerbivores\tCarnivores\tKillRate\tGrassDensity\tGenerations" + data = "Updates\tHerbivores\tCarnivores\tGrassDensity\tGenerations" for i in range(len(update_count)): data = data+"\n"+str(update_count[i])+"\t"+str(herbivore_count[i])+"\t"+str(carnivore_count[i]) - data = data+"\t"+str(kill_rate[i])+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) + data = data+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) #Then write it to file try: table_file = open(out_file, "w") @@ -174,8 +160,7 @@ global src_file global out_file global update_count, generation_count - global carnivore_count, herbivore_count - global kill_rate, grass_density + global carnivore_count, herbivore_count, grass_density src_file = "ecologia.log" #Choose the right default output file name if "--table" in sys.argv: out_file = "populations.txt" @@ -198,8 +183,7 @@ out_file = out_file+".csv" #Initialise variables update_count, generation_count = [], [] - carnivore_count, herbivore_count = [], [] - kill_rate, grass_density = [], [] + carnivore_count, herbivore_count, grass_density = [], [], [] #Do the actual work load_log() if "--compact" in sys.argv or "--ods" in sys.argv: compact() diff --git a/ecologia.config b/ecologia.config new file mode 100755 index 0000000..67f2b5c --- /dev/null +++ b/ecologia.config @@ -0,0 +1,45 @@ +# +# This is the default Ecologia configuration file. Its values have +# been chosen to provide stable ecosystem dynamics when run. +# +# Daniel Vedder, 20/09/2016 +# + +[world] +xsize 100 +ysize 100 +timelapse 100 +stopAt 200 +waterTiles 10 +humidity 1 +startGrassDensity 100 +startNoCarnivores 50 +startNoHerbivores 200 +startEnergyCarnivores 150 +startEnergyHerbivores 100 + +[herbivore] +mutationRate 0 +speed 2 +stamina 10 +sight 4 +metabolism 10 +ageLimit 150 +strength 10 +reproductiveEnergy 120 +maturityAge 15 +gestation 10 +reproductionRate 2 + +[carnivore] +mutationRate 0 +speed 3 +stamina 10 +sight 4 +metabolism 18 +ageLimit 200 +strength 11 +reproductiveEnergy 200 +maturityAge 30 +gestation 10 +reproductionRate 1 diff --git a/log_run b/log_run new file mode 100755 index 0000000..bb98de6 --- /dev/null +++ b/log_run @@ -0,0 +1,13 @@ +#!/bin/bash +# Run Ecologia during development +# Automatically analyses the log file + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --config ../../ecologia.config +cp ecologia.log ../../analysis +cd ../../analysis/demography +./population.py --table --logfile ../ecologia.log +R --slave -f population.r +mv populations-*.jpg ../../data diff --git a/release.py b/release.py index c610a24..d7e6a0c 100755 --- a/release.py +++ b/release.py @@ -23,8 +23,9 @@ main_file = open("src/main/Ecologia.java") main_source = main_file.read() main_file.close() + #This string surgery is a little iffy, but it works version_start = main_source.find("version = ")+11 - version_end = main_source.find('"', version_start) + version_end = main_source.find(' ', version_start) return main_source[version_start:version_end] ''' diff --git a/run b/run new file mode 100755 index 0000000..9de1ff8 --- /dev/null +++ b/run @@ -0,0 +1,7 @@ +#!/bin/bash +# Run Ecologia during development + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --debug --config ../../ecologia.config diff --git a/src/controller/World.java b/src/controller/World.java index 4acb5f3..68088e3 100755 --- a/src/controller/World.java +++ b/src/controller/World.java @@ -25,7 +25,8 @@ private static World world; //The Singleton instance of this class //Parameter variables - private int[] size; //The size of the world (x*y) + //TODO Reduce these to a single hashmap + /*private int[] size; //The size of the world (x*y) private int timelapse; //When running, the simulation will be updated every so many milliseconds. private int stopAt; //The simulation will stop once this update is reached. private int autorun; //The number of updates the simulation will run for automatically before quitting @@ -34,6 +35,9 @@ private int waterTiles; //The number of water tiles that will be created. private int startNoCarnivores, startNoHerbivores; //The starting number of carnivores/herbivores. private int startEnergyCarnivores, startEnergyHerbivores; //The starting energy for carnivores/herbivores. + */ + + private HashMap parameters; //Runtime variables private boolean running; //Is the simulation running? @@ -51,7 +55,7 @@ private World() { //Parameter settings - size = new int[] {100, 100}; //Default: 100*100 + /*size = new int[] {100, 100}; //Default: 100*100 timelapse = 100; //Default: 300 - can be changed at run-time stopAt = 200; //Default: 100 - can be changed at run-time autorun = -1; //Default: -1 (off) @@ -61,7 +65,21 @@ startNoCarnivores = 50; //Default: 50 - Hypothetical ideal: 5 (?) startNoHerbivores = 200; //Default: 200 - Hypothetical ideal: 160 (?) startEnergyCarnivores = 150; //Default: 150 - startEnergyHerbivores = 100; //Default: 100 + startEnergyHerbivores = 100; //Default: 100 */ + parameters = new HashMap(); + + parameters.put("xsize", 100); + parameters.put("ysize", 100); + parameters.put("timelapse", 100); + parameters.put("stopAt", 200); + parameters.put("autorun", -1); + parameters.put("waterTiles", 10); + parameters.put("humidity", 1); + parameters.put("startGrassDensity", 100); + parameters.put("startNoCarnivores", 50); + parameters.put("startNoHerbivores", 200); + parameters.put("startEnergyCarnivores", 150); + parameters.put("startEnergyHerbivores", 100); reset(); //Runtime variables } @@ -114,7 +132,7 @@ if (line.startsWith("[") && line.endsWith("]")) section = line; //Deal with world variables else if (section.equals("[world]")) { - switch (var) { + /*switch (var) { case "width": size[0] = value; break; case "height": size[1] = value; break; case "timelapse": timelapse = value; break; @@ -128,7 +146,8 @@ case "startEnergyHerbivores": startEnergyHerbivores = value; break; case "startEnergyCarnivores": startEnergyCarnivores = value; break; default: EcologiaIO.error("Invalid config variable in the [world] section: "+var); - } + }*/ + setParam(var, value); } //Configure default animal genomes else if (section.equals("[herbivore]")) { @@ -164,7 +183,7 @@ herbivoreCounter = 0; carnivoreCounter = 0; highestGeneration = 1; - averageGrassDensity = startGrassDensity; + averageGrassDensity = parameters.get("startGrassDensity"); animals = null; news = new ArrayList(); } @@ -244,6 +263,33 @@ if (type == OccupantType.HERBIVORE) Herbivore.defaultGenome = genome; else if (type == OccupantType.CARNIVORE) Carnivore.defaultGenome = genome; } + + /** + * Return a parameter value. + */ + public int getParam(String param) + { + if (parameters.containsKey(param)) + return parameters.get(param); + else { + EcologiaIO.error("getParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + return -1; //Will never be reached, but is syntactically needed + } + } + + /** + * Set a parameter value. + */ + public void setParam(String param, int value) + { + //XXX Is this check necessary here? + if (parameters.containsKey(param)) + parameters.put(param, value); + else EcologiaIO.error("setParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + } + + +/* public int[] getSize() { @@ -358,6 +404,8 @@ this.startEnergyHerbivores = startEnergyHerbivores; } +*/ + public int getHerbivoreCount() { return herbivoreCounter; diff --git a/src/main/EcoTest.java b/src/main/EcoTest.java index 6d456d4..240f503 100755 --- a/src/main/EcoTest.java +++ b/src/main/EcoTest.java @@ -9,6 +9,8 @@ * (below) is set to true. * * TODO: expand this into a proper testing framework? + * + * THIS IS OUTDATED - DO NOT USE WITHOUT MAJOR MODIFICATIONS * * @author Daniel Vedder * @date 24.12.2014 @@ -45,13 +47,13 @@ if (turn == 1) { EcologiaIO.debug("Ecotest: Creating a herbivore at (1, 0)"); Herbivore herbivore = new Herbivore(World.getInstance().getNextID(), - Herbivore.defaultGenome, -1, 1, 0, - World.getInstance().getStartEnergyHerbivores(), 0); + Herbivore.defaultGenome, -1, 1, 0, + World.getInstance().getParam("startEnergyHerbivores"), 0); Simulator.addAnimal(herbivore); EcologiaIO.debug("Ecotest: Creating a carnivore at (1, 3)"); Carnivore carnivore = new Carnivore(World.getInstance().getNextID(), - Carnivore.defaultGenome, -1, 1, 3, - World.getInstance().getStartEnergyCarnivores(), 0); + Carnivore.defaultGenome, -1, 1, 3, + World.getInstance().getParam("startEnergyCarnivores"), 0); Simulator.addAnimal(carnivore); } if (turn == 2) { @@ -98,8 +100,8 @@ tpcY = 0; EcologiaIO.debug("Creating a carnivore at (0, 0)"); Carnivore carnivore = new Carnivore(World.getInstance().getNextID(), - Carnivore.defaultGenome, -1, tpcX, tpcY, - World.getInstance().getStartEnergyCarnivores(), 0); + Carnivore.defaultGenome, -1, tpcX, tpcY, + World.getInstance().getParam("startEnergyCarnivores"), 0); Simulator.addAnimal(carnivore); } else if (turn < 5 && turn > 1) { diff --git a/src/main/Ecologia.java b/src/main/Ecologia.java index 4d1ed75..cd308c0 100755 --- a/src/main/Ecologia.java +++ b/src/main/Ecologia.java @@ -5,8 +5,9 @@ import model.Simulator; /** - * Ecologia is a relatively simple ecosystem simulator, designed to show basic - * relationships between predators, prey and producers. + * This is an individual-based model of the ecology of the saiga antelope + * (Saiga tatarica tatarica). The source code was forked from the Ecologia + * ecosystem simulator, version 1.1 (www.launchpad.net/ecologia). * * Copyright (C) 2014-2016 Daniel Vedder * @@ -28,18 +29,18 @@ * manager, updating the program each round. * * @author Daniel Vedder - * @version 28.8.2014 + * @version 20.12.2016 */ public class Ecologia implements Runnable { public static Ecologia eco; //The singleton object - public static final String version = "1.1"; + public static final String version = "2.0 Saiga"; private static boolean noGUI = false; private GUI gui; private Simulator simulator; - private EcoTest tester; + //private EcoTest tester; //XXX Currently not used private Thread runningThread; @@ -73,7 +74,7 @@ else if (a.equals("--no-graphics")) noGUI = true; else if (a.equals("--autorun")) { - World.getInstance().setAutorun(new Integer(args[i+1])); + World.getInstance().setParam("autorun", new Integer(args[i+1])); i++; } else if (a.equals("--config")) { @@ -81,7 +82,7 @@ i++; } else if (a.equals("--timelapse")) { - World.getInstance().setTimelapse(new Integer(args[i+1])); + World.getInstance().setParam("timelapse", new Integer(args[i+1])); i++; } else EcologiaIO.error("Invalid commandline parameter: "+a); @@ -93,7 +94,7 @@ EcologiaIO.printStatus(); //Only use no-graphics mode when on autorun - if (noGUI && (World.getInstance().getAutorun() < 0)) { + if (noGUI && (World.getInstance().getParam("autorun") < 0)) { EcologiaIO.error("Returning to graphics mode as autorun not enabled."); noGUI = false; } @@ -119,9 +120,9 @@ EcologiaIO.log("Launching Ecologia..."); simulator = new Simulator(); if (!noGUI) gui = new GUI(); - tester = new EcoTest(); + //tester = new EcoTest(); EcologiaIO.debug("Launch completed."); - if (World.getInstance().getAutorun() > 0) autorun(); + if (World.getInstance().getParam("autorun") > 0) autorun(); } /** @@ -129,8 +130,8 @@ */ private void autorun() { - EcologiaIO.log("Performing autorun for "+World.getInstance().getAutorun()+" updates."); - World.getInstance().setStopAt(-1); + EcologiaIO.log("Performing autorun for "+World.getInstance().getParam("autorun")+" updates."); + World.getInstance().setParam("stopAt", -1); startThread(); } @@ -138,7 +139,7 @@ * Reset the simulator in order to start a new run. * * XXX: Depending on how long the simulation has already - * been running, this can take quite a long time. + * been running, this can take quite a long time. -- Does it? */ public void reset() { @@ -183,11 +184,11 @@ */ public synchronized void iterate() { - int autorun = World.getInstance().getAutorun(); + int autorun = World.getInstance().getParam("autorun"); World.getInstance().incrementTurn(); int turn = World.getInstance().getTurn(); EcologiaIO.log("Executing update "+turn); - if (EcologiaIO.debugging) tester.runTest(); + //if (EcologiaIO.debugging) tester.runTest(); simulator.update(); EcologiaIO.log("Average grass density: "+World.getInstance().getAverageGrassDensity()+"%"); EcologiaIO.log("Herbivore count: "+World.getInstance().getHerbivoreCount()); @@ -195,7 +196,7 @@ EcologiaIO.log("Generation counter: "+World.getInstance().getGeneration()); //If the stopAt number is reached, pause the simulation - if (World.getInstance().getStopAt() == turn) { + if (World.getInstance().getParam("stopAt") == turn) { World.getInstance().setRunning(false); } if (!noGUI) gui.update(); @@ -213,7 +214,7 @@ } //Pause for as long as the user wants try { - Thread.sleep(World.getInstance().getTimelapse()); + Thread.sleep(World.getInstance().getParam("timelapse")); } catch (InterruptedException ie) {} } @@ -223,7 +224,7 @@ */ private static void printHelp() { - System.out.println("Ecologia "+version+", the simple ecosystem simulator."); + System.out.println("Ecologia "+version+", an individual-based model of saiga ecology."); System.out.println("\nCommandline options:\n"); System.out.println("--help -h Print this help text"); System.out.println("--version -V Print the version number\n"); diff --git a/src/model/Animal.java b/src/model/Animal.java index 2917b85..d5229d4 100755 --- a/src/model/Animal.java +++ b/src/model/Animal.java @@ -23,6 +23,7 @@ * (max value: 2**31) should last for >40,000,000 updates * (based on a 1000 update test run). * => long IDs are not very urgent... + * --> CHECK THIS AGAIN */ protected int IDnumber; //A unique identifier for this animal protected int parent; //The ID number of the parent @@ -205,8 +206,8 @@ ArrayList targets = new ArrayList(); for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int[] newTarget = {xdist, ydist}; targets.add(newTarget); @@ -228,8 +229,8 @@ int minDist = genome.getSight()+1; for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int distance = getDistance(xdist, ydist); if (distance != 0 && distance < minDist) { @@ -255,8 +256,8 @@ int minDist = genome.getSight()+1; for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int distance = getDistance(xdist, ydist); int[] newTarget = {xdist, ydist}; @@ -292,8 +293,8 @@ case TOP_LEFT: nextY--; nextX--; break; default: EcologiaIO.error("Invalid direction passed to Animal.getNeighbouringField()! ("+dir+") by "+type.toString()+" @"+x+"/"+y); } - if (nextX < 0 || nextX >= World.getInstance().getSize()[0] || - nextY < 0 || nextY >= World.getInstance().getSize()[1]) { + if (nextX < 0 || nextX >= World.getInstance().getParam("xsize") || + nextY < 0 || nextY >= World.getInstance().getParam("ysize")) { return null; } else { diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7e1d0dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +releases +.classpath +.project +data +*.log diff --git a/README b/README index 7e39738..996df4c 100755 --- a/README +++ b/README @@ -2,6 +2,9 @@ Ecologia README =================== +--- THIS IS THE README FOR ECOLOGIA 1.1! --- + (a readme for version 2 is soon to follow) + Ecologia is a graphical ecosystem simulator. Currently it is still very basic, but hopefully it will soon be expanded to include more and more aspects of a real ecosystem. diff --git a/analysis/demography/population.py b/analysis/demography/population.py index 9490f0f..1955b45 100755 --- a/analysis/demography/population.py +++ b/analysis/demography/population.py @@ -18,12 +18,11 @@ global generation_count global carnivore_count global herbivore_count -global kill_rate global grass_density global src_file global out_file -version = "0.3" +version = "0.3.1" ''' @@ -32,8 +31,7 @@ def load_log(): global src_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density try: log_file = open(src_file, "r") line = log_file.readline() @@ -47,9 +45,6 @@ elif "Carnivore count" in line: next_count = line[line.find("Carnivore count: ")+17:-1] carnivore_count.append(int(next_count)) - elif "Carnivore hunt success rate: " in line: - next_count = line[line.find("rate: ")+6:-2] - kill_rate.append(int(next_count)) elif "Average grass density: " in line: next_count = line[line.find("density: ")+9:-2] grass_density.append(next_count) @@ -68,14 +63,12 @@ ''' def compact(): global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density if len(update_count) < 1000: return new_update_count = [update_count[0]] new_herbivore_count = [herbivore_count[0]] new_carnivore_count = [carnivore_count[0]] - new_kill_rate = [kill_rate[0]] new_grass_density = [grass_density[0]] new_generation_count = [generation_count[0]] for i in range(1, len(update_count)): @@ -83,13 +76,11 @@ new_update_count.append(update_count[i]) new_herbivore_count.append(herbivore_count[i]) new_carnivore_count.append(carnivore_count[i]) - new_kill_rate.append(kill_rate[i]) new_grass_density.append(grass_density[i]) new_generation_count.append(generation_count[i]) update_count = new_update_count herbivore_count = new_herbivore_count carnivore_count = new_carnivore_count - kill_rate = new_kill_rate grass_density = new_grass_density generation_count = new_generation_count compact() #Keep going until the data set is small enough @@ -100,8 +91,7 @@ def save_csv(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate all the data data = "Updates" for u in update_count: @@ -112,9 +102,6 @@ data = data+"\nCarnivores" for c in carnivore_count: data = data+","+str(c) - data = data+"\nKill rate" - for k in kill_rate: - data = data+","+str(k) data = data+"\nGrass density" for g in grass_density: data = data+","+str(g) @@ -136,13 +123,12 @@ def save_table(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate the data - data = "Updates\tHerbivores\tCarnivores\tKillRate\tGrassDensity\tGenerations" + data = "Updates\tHerbivores\tCarnivores\tGrassDensity\tGenerations" for i in range(len(update_count)): data = data+"\n"+str(update_count[i])+"\t"+str(herbivore_count[i])+"\t"+str(carnivore_count[i]) - data = data+"\t"+str(kill_rate[i])+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) + data = data+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) #Then write it to file try: table_file = open(out_file, "w") @@ -174,8 +160,7 @@ global src_file global out_file global update_count, generation_count - global carnivore_count, herbivore_count - global kill_rate, grass_density + global carnivore_count, herbivore_count, grass_density src_file = "ecologia.log" #Choose the right default output file name if "--table" in sys.argv: out_file = "populations.txt" @@ -198,8 +183,7 @@ out_file = out_file+".csv" #Initialise variables update_count, generation_count = [], [] - carnivore_count, herbivore_count = [], [] - kill_rate, grass_density = [], [] + carnivore_count, herbivore_count, grass_density = [], [], [] #Do the actual work load_log() if "--compact" in sys.argv or "--ods" in sys.argv: compact() diff --git a/ecologia.config b/ecologia.config new file mode 100755 index 0000000..67f2b5c --- /dev/null +++ b/ecologia.config @@ -0,0 +1,45 @@ +# +# This is the default Ecologia configuration file. Its values have +# been chosen to provide stable ecosystem dynamics when run. +# +# Daniel Vedder, 20/09/2016 +# + +[world] +xsize 100 +ysize 100 +timelapse 100 +stopAt 200 +waterTiles 10 +humidity 1 +startGrassDensity 100 +startNoCarnivores 50 +startNoHerbivores 200 +startEnergyCarnivores 150 +startEnergyHerbivores 100 + +[herbivore] +mutationRate 0 +speed 2 +stamina 10 +sight 4 +metabolism 10 +ageLimit 150 +strength 10 +reproductiveEnergy 120 +maturityAge 15 +gestation 10 +reproductionRate 2 + +[carnivore] +mutationRate 0 +speed 3 +stamina 10 +sight 4 +metabolism 18 +ageLimit 200 +strength 11 +reproductiveEnergy 200 +maturityAge 30 +gestation 10 +reproductionRate 1 diff --git a/log_run b/log_run new file mode 100755 index 0000000..bb98de6 --- /dev/null +++ b/log_run @@ -0,0 +1,13 @@ +#!/bin/bash +# Run Ecologia during development +# Automatically analyses the log file + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --config ../../ecologia.config +cp ecologia.log ../../analysis +cd ../../analysis/demography +./population.py --table --logfile ../ecologia.log +R --slave -f population.r +mv populations-*.jpg ../../data diff --git a/release.py b/release.py index c610a24..d7e6a0c 100755 --- a/release.py +++ b/release.py @@ -23,8 +23,9 @@ main_file = open("src/main/Ecologia.java") main_source = main_file.read() main_file.close() + #This string surgery is a little iffy, but it works version_start = main_source.find("version = ")+11 - version_end = main_source.find('"', version_start) + version_end = main_source.find(' ', version_start) return main_source[version_start:version_end] ''' diff --git a/run b/run new file mode 100755 index 0000000..9de1ff8 --- /dev/null +++ b/run @@ -0,0 +1,7 @@ +#!/bin/bash +# Run Ecologia during development + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --debug --config ../../ecologia.config diff --git a/src/controller/World.java b/src/controller/World.java index 4acb5f3..68088e3 100755 --- a/src/controller/World.java +++ b/src/controller/World.java @@ -25,7 +25,8 @@ private static World world; //The Singleton instance of this class //Parameter variables - private int[] size; //The size of the world (x*y) + //TODO Reduce these to a single hashmap + /*private int[] size; //The size of the world (x*y) private int timelapse; //When running, the simulation will be updated every so many milliseconds. private int stopAt; //The simulation will stop once this update is reached. private int autorun; //The number of updates the simulation will run for automatically before quitting @@ -34,6 +35,9 @@ private int waterTiles; //The number of water tiles that will be created. private int startNoCarnivores, startNoHerbivores; //The starting number of carnivores/herbivores. private int startEnergyCarnivores, startEnergyHerbivores; //The starting energy for carnivores/herbivores. + */ + + private HashMap parameters; //Runtime variables private boolean running; //Is the simulation running? @@ -51,7 +55,7 @@ private World() { //Parameter settings - size = new int[] {100, 100}; //Default: 100*100 + /*size = new int[] {100, 100}; //Default: 100*100 timelapse = 100; //Default: 300 - can be changed at run-time stopAt = 200; //Default: 100 - can be changed at run-time autorun = -1; //Default: -1 (off) @@ -61,7 +65,21 @@ startNoCarnivores = 50; //Default: 50 - Hypothetical ideal: 5 (?) startNoHerbivores = 200; //Default: 200 - Hypothetical ideal: 160 (?) startEnergyCarnivores = 150; //Default: 150 - startEnergyHerbivores = 100; //Default: 100 + startEnergyHerbivores = 100; //Default: 100 */ + parameters = new HashMap(); + + parameters.put("xsize", 100); + parameters.put("ysize", 100); + parameters.put("timelapse", 100); + parameters.put("stopAt", 200); + parameters.put("autorun", -1); + parameters.put("waterTiles", 10); + parameters.put("humidity", 1); + parameters.put("startGrassDensity", 100); + parameters.put("startNoCarnivores", 50); + parameters.put("startNoHerbivores", 200); + parameters.put("startEnergyCarnivores", 150); + parameters.put("startEnergyHerbivores", 100); reset(); //Runtime variables } @@ -114,7 +132,7 @@ if (line.startsWith("[") && line.endsWith("]")) section = line; //Deal with world variables else if (section.equals("[world]")) { - switch (var) { + /*switch (var) { case "width": size[0] = value; break; case "height": size[1] = value; break; case "timelapse": timelapse = value; break; @@ -128,7 +146,8 @@ case "startEnergyHerbivores": startEnergyHerbivores = value; break; case "startEnergyCarnivores": startEnergyCarnivores = value; break; default: EcologiaIO.error("Invalid config variable in the [world] section: "+var); - } + }*/ + setParam(var, value); } //Configure default animal genomes else if (section.equals("[herbivore]")) { @@ -164,7 +183,7 @@ herbivoreCounter = 0; carnivoreCounter = 0; highestGeneration = 1; - averageGrassDensity = startGrassDensity; + averageGrassDensity = parameters.get("startGrassDensity"); animals = null; news = new ArrayList(); } @@ -244,6 +263,33 @@ if (type == OccupantType.HERBIVORE) Herbivore.defaultGenome = genome; else if (type == OccupantType.CARNIVORE) Carnivore.defaultGenome = genome; } + + /** + * Return a parameter value. + */ + public int getParam(String param) + { + if (parameters.containsKey(param)) + return parameters.get(param); + else { + EcologiaIO.error("getParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + return -1; //Will never be reached, but is syntactically needed + } + } + + /** + * Set a parameter value. + */ + public void setParam(String param, int value) + { + //XXX Is this check necessary here? + if (parameters.containsKey(param)) + parameters.put(param, value); + else EcologiaIO.error("setParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + } + + +/* public int[] getSize() { @@ -358,6 +404,8 @@ this.startEnergyHerbivores = startEnergyHerbivores; } +*/ + public int getHerbivoreCount() { return herbivoreCounter; diff --git a/src/main/EcoTest.java b/src/main/EcoTest.java index 6d456d4..240f503 100755 --- a/src/main/EcoTest.java +++ b/src/main/EcoTest.java @@ -9,6 +9,8 @@ * (below) is set to true. * * TODO: expand this into a proper testing framework? + * + * THIS IS OUTDATED - DO NOT USE WITHOUT MAJOR MODIFICATIONS * * @author Daniel Vedder * @date 24.12.2014 @@ -45,13 +47,13 @@ if (turn == 1) { EcologiaIO.debug("Ecotest: Creating a herbivore at (1, 0)"); Herbivore herbivore = new Herbivore(World.getInstance().getNextID(), - Herbivore.defaultGenome, -1, 1, 0, - World.getInstance().getStartEnergyHerbivores(), 0); + Herbivore.defaultGenome, -1, 1, 0, + World.getInstance().getParam("startEnergyHerbivores"), 0); Simulator.addAnimal(herbivore); EcologiaIO.debug("Ecotest: Creating a carnivore at (1, 3)"); Carnivore carnivore = new Carnivore(World.getInstance().getNextID(), - Carnivore.defaultGenome, -1, 1, 3, - World.getInstance().getStartEnergyCarnivores(), 0); + Carnivore.defaultGenome, -1, 1, 3, + World.getInstance().getParam("startEnergyCarnivores"), 0); Simulator.addAnimal(carnivore); } if (turn == 2) { @@ -98,8 +100,8 @@ tpcY = 0; EcologiaIO.debug("Creating a carnivore at (0, 0)"); Carnivore carnivore = new Carnivore(World.getInstance().getNextID(), - Carnivore.defaultGenome, -1, tpcX, tpcY, - World.getInstance().getStartEnergyCarnivores(), 0); + Carnivore.defaultGenome, -1, tpcX, tpcY, + World.getInstance().getParam("startEnergyCarnivores"), 0); Simulator.addAnimal(carnivore); } else if (turn < 5 && turn > 1) { diff --git a/src/main/Ecologia.java b/src/main/Ecologia.java index 4d1ed75..cd308c0 100755 --- a/src/main/Ecologia.java +++ b/src/main/Ecologia.java @@ -5,8 +5,9 @@ import model.Simulator; /** - * Ecologia is a relatively simple ecosystem simulator, designed to show basic - * relationships between predators, prey and producers. + * This is an individual-based model of the ecology of the saiga antelope + * (Saiga tatarica tatarica). The source code was forked from the Ecologia + * ecosystem simulator, version 1.1 (www.launchpad.net/ecologia). * * Copyright (C) 2014-2016 Daniel Vedder * @@ -28,18 +29,18 @@ * manager, updating the program each round. * * @author Daniel Vedder - * @version 28.8.2014 + * @version 20.12.2016 */ public class Ecologia implements Runnable { public static Ecologia eco; //The singleton object - public static final String version = "1.1"; + public static final String version = "2.0 Saiga"; private static boolean noGUI = false; private GUI gui; private Simulator simulator; - private EcoTest tester; + //private EcoTest tester; //XXX Currently not used private Thread runningThread; @@ -73,7 +74,7 @@ else if (a.equals("--no-graphics")) noGUI = true; else if (a.equals("--autorun")) { - World.getInstance().setAutorun(new Integer(args[i+1])); + World.getInstance().setParam("autorun", new Integer(args[i+1])); i++; } else if (a.equals("--config")) { @@ -81,7 +82,7 @@ i++; } else if (a.equals("--timelapse")) { - World.getInstance().setTimelapse(new Integer(args[i+1])); + World.getInstance().setParam("timelapse", new Integer(args[i+1])); i++; } else EcologiaIO.error("Invalid commandline parameter: "+a); @@ -93,7 +94,7 @@ EcologiaIO.printStatus(); //Only use no-graphics mode when on autorun - if (noGUI && (World.getInstance().getAutorun() < 0)) { + if (noGUI && (World.getInstance().getParam("autorun") < 0)) { EcologiaIO.error("Returning to graphics mode as autorun not enabled."); noGUI = false; } @@ -119,9 +120,9 @@ EcologiaIO.log("Launching Ecologia..."); simulator = new Simulator(); if (!noGUI) gui = new GUI(); - tester = new EcoTest(); + //tester = new EcoTest(); EcologiaIO.debug("Launch completed."); - if (World.getInstance().getAutorun() > 0) autorun(); + if (World.getInstance().getParam("autorun") > 0) autorun(); } /** @@ -129,8 +130,8 @@ */ private void autorun() { - EcologiaIO.log("Performing autorun for "+World.getInstance().getAutorun()+" updates."); - World.getInstance().setStopAt(-1); + EcologiaIO.log("Performing autorun for "+World.getInstance().getParam("autorun")+" updates."); + World.getInstance().setParam("stopAt", -1); startThread(); } @@ -138,7 +139,7 @@ * Reset the simulator in order to start a new run. * * XXX: Depending on how long the simulation has already - * been running, this can take quite a long time. + * been running, this can take quite a long time. -- Does it? */ public void reset() { @@ -183,11 +184,11 @@ */ public synchronized void iterate() { - int autorun = World.getInstance().getAutorun(); + int autorun = World.getInstance().getParam("autorun"); World.getInstance().incrementTurn(); int turn = World.getInstance().getTurn(); EcologiaIO.log("Executing update "+turn); - if (EcologiaIO.debugging) tester.runTest(); + //if (EcologiaIO.debugging) tester.runTest(); simulator.update(); EcologiaIO.log("Average grass density: "+World.getInstance().getAverageGrassDensity()+"%"); EcologiaIO.log("Herbivore count: "+World.getInstance().getHerbivoreCount()); @@ -195,7 +196,7 @@ EcologiaIO.log("Generation counter: "+World.getInstance().getGeneration()); //If the stopAt number is reached, pause the simulation - if (World.getInstance().getStopAt() == turn) { + if (World.getInstance().getParam("stopAt") == turn) { World.getInstance().setRunning(false); } if (!noGUI) gui.update(); @@ -213,7 +214,7 @@ } //Pause for as long as the user wants try { - Thread.sleep(World.getInstance().getTimelapse()); + Thread.sleep(World.getInstance().getParam("timelapse")); } catch (InterruptedException ie) {} } @@ -223,7 +224,7 @@ */ private static void printHelp() { - System.out.println("Ecologia "+version+", the simple ecosystem simulator."); + System.out.println("Ecologia "+version+", an individual-based model of saiga ecology."); System.out.println("\nCommandline options:\n"); System.out.println("--help -h Print this help text"); System.out.println("--version -V Print the version number\n"); diff --git a/src/model/Animal.java b/src/model/Animal.java index 2917b85..d5229d4 100755 --- a/src/model/Animal.java +++ b/src/model/Animal.java @@ -23,6 +23,7 @@ * (max value: 2**31) should last for >40,000,000 updates * (based on a 1000 update test run). * => long IDs are not very urgent... + * --> CHECK THIS AGAIN */ protected int IDnumber; //A unique identifier for this animal protected int parent; //The ID number of the parent @@ -205,8 +206,8 @@ ArrayList targets = new ArrayList(); for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int[] newTarget = {xdist, ydist}; targets.add(newTarget); @@ -228,8 +229,8 @@ int minDist = genome.getSight()+1; for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int distance = getDistance(xdist, ydist); if (distance != 0 && distance < minDist) { @@ -255,8 +256,8 @@ int minDist = genome.getSight()+1; for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int distance = getDistance(xdist, ydist); int[] newTarget = {xdist, ydist}; @@ -292,8 +293,8 @@ case TOP_LEFT: nextY--; nextX--; break; default: EcologiaIO.error("Invalid direction passed to Animal.getNeighbouringField()! ("+dir+") by "+type.toString()+" @"+x+"/"+y); } - if (nextX < 0 || nextX >= World.getInstance().getSize()[0] || - nextY < 0 || nextY >= World.getInstance().getSize()[1]) { + if (nextX < 0 || nextX >= World.getInstance().getParam("xsize") || + nextY < 0 || nextY >= World.getInstance().getParam("ysize")) { return null; } else { diff --git a/src/model/Herbivore.java b/src/model/Herbivore.java index ee40b4e..decbf8a 100755 --- a/src/model/Herbivore.java +++ b/src/model/Herbivore.java @@ -82,7 +82,7 @@ for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { if (!(xdist == x && ydist == y) && xdist >= 0 && ydist >= 0 && - xdist < World.getInstance().getSize()[0] && ydist < World.getInstance().getSize()[1] && + xdist < World.getInstance().getParam("xsize") && ydist < World.getInstance().getParam("ysize") && Simulator.getField(xdist, ydist).getGrassDensity() > currentGrassDensity) { Direction d = super.getDirection(xdist, ydist); if (!possibleDirs.contains(d)) possibleDirs.add(d); diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7e1d0dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +releases +.classpath +.project +data +*.log diff --git a/README b/README index 7e39738..996df4c 100755 --- a/README +++ b/README @@ -2,6 +2,9 @@ Ecologia README =================== +--- THIS IS THE README FOR ECOLOGIA 1.1! --- + (a readme for version 2 is soon to follow) + Ecologia is a graphical ecosystem simulator. Currently it is still very basic, but hopefully it will soon be expanded to include more and more aspects of a real ecosystem. diff --git a/analysis/demography/population.py b/analysis/demography/population.py index 9490f0f..1955b45 100755 --- a/analysis/demography/population.py +++ b/analysis/demography/population.py @@ -18,12 +18,11 @@ global generation_count global carnivore_count global herbivore_count -global kill_rate global grass_density global src_file global out_file -version = "0.3" +version = "0.3.1" ''' @@ -32,8 +31,7 @@ def load_log(): global src_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density try: log_file = open(src_file, "r") line = log_file.readline() @@ -47,9 +45,6 @@ elif "Carnivore count" in line: next_count = line[line.find("Carnivore count: ")+17:-1] carnivore_count.append(int(next_count)) - elif "Carnivore hunt success rate: " in line: - next_count = line[line.find("rate: ")+6:-2] - kill_rate.append(int(next_count)) elif "Average grass density: " in line: next_count = line[line.find("density: ")+9:-2] grass_density.append(next_count) @@ -68,14 +63,12 @@ ''' def compact(): global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density if len(update_count) < 1000: return new_update_count = [update_count[0]] new_herbivore_count = [herbivore_count[0]] new_carnivore_count = [carnivore_count[0]] - new_kill_rate = [kill_rate[0]] new_grass_density = [grass_density[0]] new_generation_count = [generation_count[0]] for i in range(1, len(update_count)): @@ -83,13 +76,11 @@ new_update_count.append(update_count[i]) new_herbivore_count.append(herbivore_count[i]) new_carnivore_count.append(carnivore_count[i]) - new_kill_rate.append(kill_rate[i]) new_grass_density.append(grass_density[i]) new_generation_count.append(generation_count[i]) update_count = new_update_count herbivore_count = new_herbivore_count carnivore_count = new_carnivore_count - kill_rate = new_kill_rate grass_density = new_grass_density generation_count = new_generation_count compact() #Keep going until the data set is small enough @@ -100,8 +91,7 @@ def save_csv(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate all the data data = "Updates" for u in update_count: @@ -112,9 +102,6 @@ data = data+"\nCarnivores" for c in carnivore_count: data = data+","+str(c) - data = data+"\nKill rate" - for k in kill_rate: - data = data+","+str(k) data = data+"\nGrass density" for g in grass_density: data = data+","+str(g) @@ -136,13 +123,12 @@ def save_table(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate the data - data = "Updates\tHerbivores\tCarnivores\tKillRate\tGrassDensity\tGenerations" + data = "Updates\tHerbivores\tCarnivores\tGrassDensity\tGenerations" for i in range(len(update_count)): data = data+"\n"+str(update_count[i])+"\t"+str(herbivore_count[i])+"\t"+str(carnivore_count[i]) - data = data+"\t"+str(kill_rate[i])+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) + data = data+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) #Then write it to file try: table_file = open(out_file, "w") @@ -174,8 +160,7 @@ global src_file global out_file global update_count, generation_count - global carnivore_count, herbivore_count - global kill_rate, grass_density + global carnivore_count, herbivore_count, grass_density src_file = "ecologia.log" #Choose the right default output file name if "--table" in sys.argv: out_file = "populations.txt" @@ -198,8 +183,7 @@ out_file = out_file+".csv" #Initialise variables update_count, generation_count = [], [] - carnivore_count, herbivore_count = [], [] - kill_rate, grass_density = [], [] + carnivore_count, herbivore_count, grass_density = [], [], [] #Do the actual work load_log() if "--compact" in sys.argv or "--ods" in sys.argv: compact() diff --git a/ecologia.config b/ecologia.config new file mode 100755 index 0000000..67f2b5c --- /dev/null +++ b/ecologia.config @@ -0,0 +1,45 @@ +# +# This is the default Ecologia configuration file. Its values have +# been chosen to provide stable ecosystem dynamics when run. +# +# Daniel Vedder, 20/09/2016 +# + +[world] +xsize 100 +ysize 100 +timelapse 100 +stopAt 200 +waterTiles 10 +humidity 1 +startGrassDensity 100 +startNoCarnivores 50 +startNoHerbivores 200 +startEnergyCarnivores 150 +startEnergyHerbivores 100 + +[herbivore] +mutationRate 0 +speed 2 +stamina 10 +sight 4 +metabolism 10 +ageLimit 150 +strength 10 +reproductiveEnergy 120 +maturityAge 15 +gestation 10 +reproductionRate 2 + +[carnivore] +mutationRate 0 +speed 3 +stamina 10 +sight 4 +metabolism 18 +ageLimit 200 +strength 11 +reproductiveEnergy 200 +maturityAge 30 +gestation 10 +reproductionRate 1 diff --git a/log_run b/log_run new file mode 100755 index 0000000..bb98de6 --- /dev/null +++ b/log_run @@ -0,0 +1,13 @@ +#!/bin/bash +# Run Ecologia during development +# Automatically analyses the log file + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --config ../../ecologia.config +cp ecologia.log ../../analysis +cd ../../analysis/demography +./population.py --table --logfile ../ecologia.log +R --slave -f population.r +mv populations-*.jpg ../../data diff --git a/release.py b/release.py index c610a24..d7e6a0c 100755 --- a/release.py +++ b/release.py @@ -23,8 +23,9 @@ main_file = open("src/main/Ecologia.java") main_source = main_file.read() main_file.close() + #This string surgery is a little iffy, but it works version_start = main_source.find("version = ")+11 - version_end = main_source.find('"', version_start) + version_end = main_source.find(' ', version_start) return main_source[version_start:version_end] ''' diff --git a/run b/run new file mode 100755 index 0000000..9de1ff8 --- /dev/null +++ b/run @@ -0,0 +1,7 @@ +#!/bin/bash +# Run Ecologia during development + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --debug --config ../../ecologia.config diff --git a/src/controller/World.java b/src/controller/World.java index 4acb5f3..68088e3 100755 --- a/src/controller/World.java +++ b/src/controller/World.java @@ -25,7 +25,8 @@ private static World world; //The Singleton instance of this class //Parameter variables - private int[] size; //The size of the world (x*y) + //TODO Reduce these to a single hashmap + /*private int[] size; //The size of the world (x*y) private int timelapse; //When running, the simulation will be updated every so many milliseconds. private int stopAt; //The simulation will stop once this update is reached. private int autorun; //The number of updates the simulation will run for automatically before quitting @@ -34,6 +35,9 @@ private int waterTiles; //The number of water tiles that will be created. private int startNoCarnivores, startNoHerbivores; //The starting number of carnivores/herbivores. private int startEnergyCarnivores, startEnergyHerbivores; //The starting energy for carnivores/herbivores. + */ + + private HashMap parameters; //Runtime variables private boolean running; //Is the simulation running? @@ -51,7 +55,7 @@ private World() { //Parameter settings - size = new int[] {100, 100}; //Default: 100*100 + /*size = new int[] {100, 100}; //Default: 100*100 timelapse = 100; //Default: 300 - can be changed at run-time stopAt = 200; //Default: 100 - can be changed at run-time autorun = -1; //Default: -1 (off) @@ -61,7 +65,21 @@ startNoCarnivores = 50; //Default: 50 - Hypothetical ideal: 5 (?) startNoHerbivores = 200; //Default: 200 - Hypothetical ideal: 160 (?) startEnergyCarnivores = 150; //Default: 150 - startEnergyHerbivores = 100; //Default: 100 + startEnergyHerbivores = 100; //Default: 100 */ + parameters = new HashMap(); + + parameters.put("xsize", 100); + parameters.put("ysize", 100); + parameters.put("timelapse", 100); + parameters.put("stopAt", 200); + parameters.put("autorun", -1); + parameters.put("waterTiles", 10); + parameters.put("humidity", 1); + parameters.put("startGrassDensity", 100); + parameters.put("startNoCarnivores", 50); + parameters.put("startNoHerbivores", 200); + parameters.put("startEnergyCarnivores", 150); + parameters.put("startEnergyHerbivores", 100); reset(); //Runtime variables } @@ -114,7 +132,7 @@ if (line.startsWith("[") && line.endsWith("]")) section = line; //Deal with world variables else if (section.equals("[world]")) { - switch (var) { + /*switch (var) { case "width": size[0] = value; break; case "height": size[1] = value; break; case "timelapse": timelapse = value; break; @@ -128,7 +146,8 @@ case "startEnergyHerbivores": startEnergyHerbivores = value; break; case "startEnergyCarnivores": startEnergyCarnivores = value; break; default: EcologiaIO.error("Invalid config variable in the [world] section: "+var); - } + }*/ + setParam(var, value); } //Configure default animal genomes else if (section.equals("[herbivore]")) { @@ -164,7 +183,7 @@ herbivoreCounter = 0; carnivoreCounter = 0; highestGeneration = 1; - averageGrassDensity = startGrassDensity; + averageGrassDensity = parameters.get("startGrassDensity"); animals = null; news = new ArrayList(); } @@ -244,6 +263,33 @@ if (type == OccupantType.HERBIVORE) Herbivore.defaultGenome = genome; else if (type == OccupantType.CARNIVORE) Carnivore.defaultGenome = genome; } + + /** + * Return a parameter value. + */ + public int getParam(String param) + { + if (parameters.containsKey(param)) + return parameters.get(param); + else { + EcologiaIO.error("getParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + return -1; //Will never be reached, but is syntactically needed + } + } + + /** + * Set a parameter value. + */ + public void setParam(String param, int value) + { + //XXX Is this check necessary here? + if (parameters.containsKey(param)) + parameters.put(param, value); + else EcologiaIO.error("setParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + } + + +/* public int[] getSize() { @@ -358,6 +404,8 @@ this.startEnergyHerbivores = startEnergyHerbivores; } +*/ + public int getHerbivoreCount() { return herbivoreCounter; diff --git a/src/main/EcoTest.java b/src/main/EcoTest.java index 6d456d4..240f503 100755 --- a/src/main/EcoTest.java +++ b/src/main/EcoTest.java @@ -9,6 +9,8 @@ * (below) is set to true. * * TODO: expand this into a proper testing framework? + * + * THIS IS OUTDATED - DO NOT USE WITHOUT MAJOR MODIFICATIONS * * @author Daniel Vedder * @date 24.12.2014 @@ -45,13 +47,13 @@ if (turn == 1) { EcologiaIO.debug("Ecotest: Creating a herbivore at (1, 0)"); Herbivore herbivore = new Herbivore(World.getInstance().getNextID(), - Herbivore.defaultGenome, -1, 1, 0, - World.getInstance().getStartEnergyHerbivores(), 0); + Herbivore.defaultGenome, -1, 1, 0, + World.getInstance().getParam("startEnergyHerbivores"), 0); Simulator.addAnimal(herbivore); EcologiaIO.debug("Ecotest: Creating a carnivore at (1, 3)"); Carnivore carnivore = new Carnivore(World.getInstance().getNextID(), - Carnivore.defaultGenome, -1, 1, 3, - World.getInstance().getStartEnergyCarnivores(), 0); + Carnivore.defaultGenome, -1, 1, 3, + World.getInstance().getParam("startEnergyCarnivores"), 0); Simulator.addAnimal(carnivore); } if (turn == 2) { @@ -98,8 +100,8 @@ tpcY = 0; EcologiaIO.debug("Creating a carnivore at (0, 0)"); Carnivore carnivore = new Carnivore(World.getInstance().getNextID(), - Carnivore.defaultGenome, -1, tpcX, tpcY, - World.getInstance().getStartEnergyCarnivores(), 0); + Carnivore.defaultGenome, -1, tpcX, tpcY, + World.getInstance().getParam("startEnergyCarnivores"), 0); Simulator.addAnimal(carnivore); } else if (turn < 5 && turn > 1) { diff --git a/src/main/Ecologia.java b/src/main/Ecologia.java index 4d1ed75..cd308c0 100755 --- a/src/main/Ecologia.java +++ b/src/main/Ecologia.java @@ -5,8 +5,9 @@ import model.Simulator; /** - * Ecologia is a relatively simple ecosystem simulator, designed to show basic - * relationships between predators, prey and producers. + * This is an individual-based model of the ecology of the saiga antelope + * (Saiga tatarica tatarica). The source code was forked from the Ecologia + * ecosystem simulator, version 1.1 (www.launchpad.net/ecologia). * * Copyright (C) 2014-2016 Daniel Vedder * @@ -28,18 +29,18 @@ * manager, updating the program each round. * * @author Daniel Vedder - * @version 28.8.2014 + * @version 20.12.2016 */ public class Ecologia implements Runnable { public static Ecologia eco; //The singleton object - public static final String version = "1.1"; + public static final String version = "2.0 Saiga"; private static boolean noGUI = false; private GUI gui; private Simulator simulator; - private EcoTest tester; + //private EcoTest tester; //XXX Currently not used private Thread runningThread; @@ -73,7 +74,7 @@ else if (a.equals("--no-graphics")) noGUI = true; else if (a.equals("--autorun")) { - World.getInstance().setAutorun(new Integer(args[i+1])); + World.getInstance().setParam("autorun", new Integer(args[i+1])); i++; } else if (a.equals("--config")) { @@ -81,7 +82,7 @@ i++; } else if (a.equals("--timelapse")) { - World.getInstance().setTimelapse(new Integer(args[i+1])); + World.getInstance().setParam("timelapse", new Integer(args[i+1])); i++; } else EcologiaIO.error("Invalid commandline parameter: "+a); @@ -93,7 +94,7 @@ EcologiaIO.printStatus(); //Only use no-graphics mode when on autorun - if (noGUI && (World.getInstance().getAutorun() < 0)) { + if (noGUI && (World.getInstance().getParam("autorun") < 0)) { EcologiaIO.error("Returning to graphics mode as autorun not enabled."); noGUI = false; } @@ -119,9 +120,9 @@ EcologiaIO.log("Launching Ecologia..."); simulator = new Simulator(); if (!noGUI) gui = new GUI(); - tester = new EcoTest(); + //tester = new EcoTest(); EcologiaIO.debug("Launch completed."); - if (World.getInstance().getAutorun() > 0) autorun(); + if (World.getInstance().getParam("autorun") > 0) autorun(); } /** @@ -129,8 +130,8 @@ */ private void autorun() { - EcologiaIO.log("Performing autorun for "+World.getInstance().getAutorun()+" updates."); - World.getInstance().setStopAt(-1); + EcologiaIO.log("Performing autorun for "+World.getInstance().getParam("autorun")+" updates."); + World.getInstance().setParam("stopAt", -1); startThread(); } @@ -138,7 +139,7 @@ * Reset the simulator in order to start a new run. * * XXX: Depending on how long the simulation has already - * been running, this can take quite a long time. + * been running, this can take quite a long time. -- Does it? */ public void reset() { @@ -183,11 +184,11 @@ */ public synchronized void iterate() { - int autorun = World.getInstance().getAutorun(); + int autorun = World.getInstance().getParam("autorun"); World.getInstance().incrementTurn(); int turn = World.getInstance().getTurn(); EcologiaIO.log("Executing update "+turn); - if (EcologiaIO.debugging) tester.runTest(); + //if (EcologiaIO.debugging) tester.runTest(); simulator.update(); EcologiaIO.log("Average grass density: "+World.getInstance().getAverageGrassDensity()+"%"); EcologiaIO.log("Herbivore count: "+World.getInstance().getHerbivoreCount()); @@ -195,7 +196,7 @@ EcologiaIO.log("Generation counter: "+World.getInstance().getGeneration()); //If the stopAt number is reached, pause the simulation - if (World.getInstance().getStopAt() == turn) { + if (World.getInstance().getParam("stopAt") == turn) { World.getInstance().setRunning(false); } if (!noGUI) gui.update(); @@ -213,7 +214,7 @@ } //Pause for as long as the user wants try { - Thread.sleep(World.getInstance().getTimelapse()); + Thread.sleep(World.getInstance().getParam("timelapse")); } catch (InterruptedException ie) {} } @@ -223,7 +224,7 @@ */ private static void printHelp() { - System.out.println("Ecologia "+version+", the simple ecosystem simulator."); + System.out.println("Ecologia "+version+", an individual-based model of saiga ecology."); System.out.println("\nCommandline options:\n"); System.out.println("--help -h Print this help text"); System.out.println("--version -V Print the version number\n"); diff --git a/src/model/Animal.java b/src/model/Animal.java index 2917b85..d5229d4 100755 --- a/src/model/Animal.java +++ b/src/model/Animal.java @@ -23,6 +23,7 @@ * (max value: 2**31) should last for >40,000,000 updates * (based on a 1000 update test run). * => long IDs are not very urgent... + * --> CHECK THIS AGAIN */ protected int IDnumber; //A unique identifier for this animal protected int parent; //The ID number of the parent @@ -205,8 +206,8 @@ ArrayList targets = new ArrayList(); for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int[] newTarget = {xdist, ydist}; targets.add(newTarget); @@ -228,8 +229,8 @@ int minDist = genome.getSight()+1; for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int distance = getDistance(xdist, ydist); if (distance != 0 && distance < minDist) { @@ -255,8 +256,8 @@ int minDist = genome.getSight()+1; for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int distance = getDistance(xdist, ydist); int[] newTarget = {xdist, ydist}; @@ -292,8 +293,8 @@ case TOP_LEFT: nextY--; nextX--; break; default: EcologiaIO.error("Invalid direction passed to Animal.getNeighbouringField()! ("+dir+") by "+type.toString()+" @"+x+"/"+y); } - if (nextX < 0 || nextX >= World.getInstance().getSize()[0] || - nextY < 0 || nextY >= World.getInstance().getSize()[1]) { + if (nextX < 0 || nextX >= World.getInstance().getParam("xsize") || + nextY < 0 || nextY >= World.getInstance().getParam("ysize")) { return null; } else { diff --git a/src/model/Herbivore.java b/src/model/Herbivore.java index ee40b4e..decbf8a 100755 --- a/src/model/Herbivore.java +++ b/src/model/Herbivore.java @@ -82,7 +82,7 @@ for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { if (!(xdist == x && ydist == y) && xdist >= 0 && ydist >= 0 && - xdist < World.getInstance().getSize()[0] && ydist < World.getInstance().getSize()[1] && + xdist < World.getInstance().getParam("xsize") && ydist < World.getInstance().getParam("ysize") && Simulator.getField(xdist, ydist).getGrassDensity() > currentGrassDensity) { Direction d = super.getDirection(xdist, ydist); if (!possibleDirs.contains(d)) possibleDirs.add(d); diff --git a/src/model/Simulator.java b/src/model/Simulator.java index a2d9477..cb9bdea 100755 --- a/src/model/Simulator.java +++ b/src/model/Simulator.java @@ -45,12 +45,12 @@ //Calculate the new grass density on each plot EcologiaIO.debug("Simulator: Recalculating grass density."); double averageDensity = 0; - int xsize = World.getInstance().getSize()[0]; - int ysize = World.getInstance().getSize()[1]; + int xsize = World.getInstance().getParam("xsize"); + int ysize = World.getInstance().getParam("ysize"); for (int x = 0; x < xsize; x++) { for (int y = 0; y < ysize; y++) { if (!map[x][y].nearWater()) { - map[x][y].setLocalHumidity(World.getInstance().getHumidity()); + map[x][y].setLocalHumidity(Humidity.getStatus(World.getInstance().getParam("humidity"))); } map[x][y].calculateGrassDensity(); averageDensity += map[x][y].getGrassDensity(); @@ -105,14 +105,14 @@ private void initMap() { EcologiaIO.debug("Simulator: initialising map."); - int xsize = World.getInstance().getSize()[0]; - int ysize = World.getInstance().getSize()[1]; + int xsize = World.getInstance().getParam("xsize"); + int ysize = World.getInstance().getParam("ysize"); map = new MapField[xsize][ysize]; for (int x = 0; x < xsize; x++) { for (int y = 0; y < ysize; y++) { map[x][y] = new MapField(x, y, OccupantType.NONE, - World.getInstance().getHumidity(), - World.getInstance().getStartGrassDensity()); + Humidity.getStatus(World.getInstance().getParam("humidity")), + World.getInstance().getParam("startGrassDensity")); } } } @@ -123,13 +123,13 @@ private void initWaterTiles() { EcologiaIO.debug("Simulator: initialising water tiles."); - for (int i = 0; i < World.getInstance().getWaterTiles(); i++) { + for (int i = 0; i < World.getInstance().getParam("waterTiles"); i++) { //Each water tile is placed in a random location - int setX = random.nextInt(World.getInstance().getSize()[0]); - int setY = random.nextInt(World.getInstance().getSize()[1]); + int setX = random.nextInt(World.getInstance().getParam("xsize")); + int setY = random.nextInt(World.getInstance().getParam("ysize")); while (map[setX][setY].getOccupant() != OccupantType.NONE) { - setX = random.nextInt(World.getInstance().getSize()[0]); - setY = random.nextInt(World.getInstance().getSize()[1]); + setX = random.nextInt(World.getInstance().getParam("xsize")); + setY = random.nextInt(World.getInstance().getParam("ysize")); } map[setX][setY].setOccupant(OccupantType.WATER); //The fields around each water tile are watered @@ -154,28 +154,28 @@ herbivorePopulation = new ArrayList(); //Create the initial carnivore population, setting each carnivore down at a random position EcologiaIO.debug("Simulator: initialising carnivores."); - for (int j = 0; j < World.getInstance().getStartNoCarnivores(); j++) { - int setXCarnivore = random.nextInt(World.getInstance().getSize()[0]); - int setYCarnivore = random.nextInt(World.getInstance().getSize()[1]); + for (int j = 0; j < World.getInstance().getParam("startNoCarnivores"); j++) { + int setXCarnivore = random.nextInt(World.getInstance().getParam("xsize")); + int setYCarnivore = random.nextInt(World.getInstance().getParam("ysize")); while (map[setXCarnivore][setYCarnivore].getOccupant() != OccupantType.NONE) { - setXCarnivore = random.nextInt(World.getInstance().getSize()[0]); - setYCarnivore = random.nextInt(World.getInstance().getSize()[1]); + setXCarnivore = random.nextInt(World.getInstance().getParam("xsize")); + setYCarnivore = random.nextInt(World.getInstance().getParam("ysize")); } - int startEnergyCarnivores = World.getInstance().getStartEnergyCarnivores(); + int startEnergyCarnivores = World.getInstance().getParam("startEnergyCarnivores"); carnivorePopulation.add(new Carnivore(World.getInstance().getNextID(), Carnivore.defaultGenome, 1, setXCarnivore, setYCarnivore, startEnergyCarnivores, 0)); } //Create the initial herbivore population, setting each herbivore down at a random position EcologiaIO.debug("Simulator: initialising herbivores."); - for (int i = 0; i < World.getInstance().getStartNoHerbivores(); i++) { - int setXHerbivore = random.nextInt(World.getInstance().getSize()[0]); - int setYHerbivore = random.nextInt(World.getInstance().getSize()[1]); + for (int i = 0; i < World.getInstance().getParam("startNoHerbivores"); i++) { + int setXHerbivore = random.nextInt(World.getInstance().getParam("xsize")); + int setYHerbivore = random.nextInt(World.getInstance().getParam("ysize")); while (map[setXHerbivore][setYHerbivore].getOccupant() != OccupantType.NONE) { - setXHerbivore = random.nextInt(World.getInstance().getSize()[0]); - setYHerbivore = random.nextInt(World.getInstance().getSize()[1]); + setXHerbivore = random.nextInt(World.getInstance().getParam("xsize")); + setYHerbivore = random.nextInt(World.getInstance().getParam("ysize")); } - int startEnergyHerbivores = World.getInstance().getStartEnergyHerbivores(); + int startEnergyHerbivores = World.getInstance().getParam("startEnergyHerbivores"); herbivorePopulation.add(new Herbivore(World.getInstance().getNextID(), Herbivore.defaultGenome, 1, setXHerbivore, setYHerbivore, startEnergyHerbivores, 0)); diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7e1d0dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +releases +.classpath +.project +data +*.log diff --git a/README b/README index 7e39738..996df4c 100755 --- a/README +++ b/README @@ -2,6 +2,9 @@ Ecologia README =================== +--- THIS IS THE README FOR ECOLOGIA 1.1! --- + (a readme for version 2 is soon to follow) + Ecologia is a graphical ecosystem simulator. Currently it is still very basic, but hopefully it will soon be expanded to include more and more aspects of a real ecosystem. diff --git a/analysis/demography/population.py b/analysis/demography/population.py index 9490f0f..1955b45 100755 --- a/analysis/demography/population.py +++ b/analysis/demography/population.py @@ -18,12 +18,11 @@ global generation_count global carnivore_count global herbivore_count -global kill_rate global grass_density global src_file global out_file -version = "0.3" +version = "0.3.1" ''' @@ -32,8 +31,7 @@ def load_log(): global src_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density try: log_file = open(src_file, "r") line = log_file.readline() @@ -47,9 +45,6 @@ elif "Carnivore count" in line: next_count = line[line.find("Carnivore count: ")+17:-1] carnivore_count.append(int(next_count)) - elif "Carnivore hunt success rate: " in line: - next_count = line[line.find("rate: ")+6:-2] - kill_rate.append(int(next_count)) elif "Average grass density: " in line: next_count = line[line.find("density: ")+9:-2] grass_density.append(next_count) @@ -68,14 +63,12 @@ ''' def compact(): global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density if len(update_count) < 1000: return new_update_count = [update_count[0]] new_herbivore_count = [herbivore_count[0]] new_carnivore_count = [carnivore_count[0]] - new_kill_rate = [kill_rate[0]] new_grass_density = [grass_density[0]] new_generation_count = [generation_count[0]] for i in range(1, len(update_count)): @@ -83,13 +76,11 @@ new_update_count.append(update_count[i]) new_herbivore_count.append(herbivore_count[i]) new_carnivore_count.append(carnivore_count[i]) - new_kill_rate.append(kill_rate[i]) new_grass_density.append(grass_density[i]) new_generation_count.append(generation_count[i]) update_count = new_update_count herbivore_count = new_herbivore_count carnivore_count = new_carnivore_count - kill_rate = new_kill_rate grass_density = new_grass_density generation_count = new_generation_count compact() #Keep going until the data set is small enough @@ -100,8 +91,7 @@ def save_csv(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate all the data data = "Updates" for u in update_count: @@ -112,9 +102,6 @@ data = data+"\nCarnivores" for c in carnivore_count: data = data+","+str(c) - data = data+"\nKill rate" - for k in kill_rate: - data = data+","+str(k) data = data+"\nGrass density" for g in grass_density: data = data+","+str(g) @@ -136,13 +123,12 @@ def save_table(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate the data - data = "Updates\tHerbivores\tCarnivores\tKillRate\tGrassDensity\tGenerations" + data = "Updates\tHerbivores\tCarnivores\tGrassDensity\tGenerations" for i in range(len(update_count)): data = data+"\n"+str(update_count[i])+"\t"+str(herbivore_count[i])+"\t"+str(carnivore_count[i]) - data = data+"\t"+str(kill_rate[i])+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) + data = data+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) #Then write it to file try: table_file = open(out_file, "w") @@ -174,8 +160,7 @@ global src_file global out_file global update_count, generation_count - global carnivore_count, herbivore_count - global kill_rate, grass_density + global carnivore_count, herbivore_count, grass_density src_file = "ecologia.log" #Choose the right default output file name if "--table" in sys.argv: out_file = "populations.txt" @@ -198,8 +183,7 @@ out_file = out_file+".csv" #Initialise variables update_count, generation_count = [], [] - carnivore_count, herbivore_count = [], [] - kill_rate, grass_density = [], [] + carnivore_count, herbivore_count, grass_density = [], [], [] #Do the actual work load_log() if "--compact" in sys.argv or "--ods" in sys.argv: compact() diff --git a/ecologia.config b/ecologia.config new file mode 100755 index 0000000..67f2b5c --- /dev/null +++ b/ecologia.config @@ -0,0 +1,45 @@ +# +# This is the default Ecologia configuration file. Its values have +# been chosen to provide stable ecosystem dynamics when run. +# +# Daniel Vedder, 20/09/2016 +# + +[world] +xsize 100 +ysize 100 +timelapse 100 +stopAt 200 +waterTiles 10 +humidity 1 +startGrassDensity 100 +startNoCarnivores 50 +startNoHerbivores 200 +startEnergyCarnivores 150 +startEnergyHerbivores 100 + +[herbivore] +mutationRate 0 +speed 2 +stamina 10 +sight 4 +metabolism 10 +ageLimit 150 +strength 10 +reproductiveEnergy 120 +maturityAge 15 +gestation 10 +reproductionRate 2 + +[carnivore] +mutationRate 0 +speed 3 +stamina 10 +sight 4 +metabolism 18 +ageLimit 200 +strength 11 +reproductiveEnergy 200 +maturityAge 30 +gestation 10 +reproductionRate 1 diff --git a/log_run b/log_run new file mode 100755 index 0000000..bb98de6 --- /dev/null +++ b/log_run @@ -0,0 +1,13 @@ +#!/bin/bash +# Run Ecologia during development +# Automatically analyses the log file + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --config ../../ecologia.config +cp ecologia.log ../../analysis +cd ../../analysis/demography +./population.py --table --logfile ../ecologia.log +R --slave -f population.r +mv populations-*.jpg ../../data diff --git a/release.py b/release.py index c610a24..d7e6a0c 100755 --- a/release.py +++ b/release.py @@ -23,8 +23,9 @@ main_file = open("src/main/Ecologia.java") main_source = main_file.read() main_file.close() + #This string surgery is a little iffy, but it works version_start = main_source.find("version = ")+11 - version_end = main_source.find('"', version_start) + version_end = main_source.find(' ', version_start) return main_source[version_start:version_end] ''' diff --git a/run b/run new file mode 100755 index 0000000..9de1ff8 --- /dev/null +++ b/run @@ -0,0 +1,7 @@ +#!/bin/bash +# Run Ecologia during development + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --debug --config ../../ecologia.config diff --git a/src/controller/World.java b/src/controller/World.java index 4acb5f3..68088e3 100755 --- a/src/controller/World.java +++ b/src/controller/World.java @@ -25,7 +25,8 @@ private static World world; //The Singleton instance of this class //Parameter variables - private int[] size; //The size of the world (x*y) + //TODO Reduce these to a single hashmap + /*private int[] size; //The size of the world (x*y) private int timelapse; //When running, the simulation will be updated every so many milliseconds. private int stopAt; //The simulation will stop once this update is reached. private int autorun; //The number of updates the simulation will run for automatically before quitting @@ -34,6 +35,9 @@ private int waterTiles; //The number of water tiles that will be created. private int startNoCarnivores, startNoHerbivores; //The starting number of carnivores/herbivores. private int startEnergyCarnivores, startEnergyHerbivores; //The starting energy for carnivores/herbivores. + */ + + private HashMap parameters; //Runtime variables private boolean running; //Is the simulation running? @@ -51,7 +55,7 @@ private World() { //Parameter settings - size = new int[] {100, 100}; //Default: 100*100 + /*size = new int[] {100, 100}; //Default: 100*100 timelapse = 100; //Default: 300 - can be changed at run-time stopAt = 200; //Default: 100 - can be changed at run-time autorun = -1; //Default: -1 (off) @@ -61,7 +65,21 @@ startNoCarnivores = 50; //Default: 50 - Hypothetical ideal: 5 (?) startNoHerbivores = 200; //Default: 200 - Hypothetical ideal: 160 (?) startEnergyCarnivores = 150; //Default: 150 - startEnergyHerbivores = 100; //Default: 100 + startEnergyHerbivores = 100; //Default: 100 */ + parameters = new HashMap(); + + parameters.put("xsize", 100); + parameters.put("ysize", 100); + parameters.put("timelapse", 100); + parameters.put("stopAt", 200); + parameters.put("autorun", -1); + parameters.put("waterTiles", 10); + parameters.put("humidity", 1); + parameters.put("startGrassDensity", 100); + parameters.put("startNoCarnivores", 50); + parameters.put("startNoHerbivores", 200); + parameters.put("startEnergyCarnivores", 150); + parameters.put("startEnergyHerbivores", 100); reset(); //Runtime variables } @@ -114,7 +132,7 @@ if (line.startsWith("[") && line.endsWith("]")) section = line; //Deal with world variables else if (section.equals("[world]")) { - switch (var) { + /*switch (var) { case "width": size[0] = value; break; case "height": size[1] = value; break; case "timelapse": timelapse = value; break; @@ -128,7 +146,8 @@ case "startEnergyHerbivores": startEnergyHerbivores = value; break; case "startEnergyCarnivores": startEnergyCarnivores = value; break; default: EcologiaIO.error("Invalid config variable in the [world] section: "+var); - } + }*/ + setParam(var, value); } //Configure default animal genomes else if (section.equals("[herbivore]")) { @@ -164,7 +183,7 @@ herbivoreCounter = 0; carnivoreCounter = 0; highestGeneration = 1; - averageGrassDensity = startGrassDensity; + averageGrassDensity = parameters.get("startGrassDensity"); animals = null; news = new ArrayList(); } @@ -244,6 +263,33 @@ if (type == OccupantType.HERBIVORE) Herbivore.defaultGenome = genome; else if (type == OccupantType.CARNIVORE) Carnivore.defaultGenome = genome; } + + /** + * Return a parameter value. + */ + public int getParam(String param) + { + if (parameters.containsKey(param)) + return parameters.get(param); + else { + EcologiaIO.error("getParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + return -1; //Will never be reached, but is syntactically needed + } + } + + /** + * Set a parameter value. + */ + public void setParam(String param, int value) + { + //XXX Is this check necessary here? + if (parameters.containsKey(param)) + parameters.put(param, value); + else EcologiaIO.error("setParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + } + + +/* public int[] getSize() { @@ -358,6 +404,8 @@ this.startEnergyHerbivores = startEnergyHerbivores; } +*/ + public int getHerbivoreCount() { return herbivoreCounter; diff --git a/src/main/EcoTest.java b/src/main/EcoTest.java index 6d456d4..240f503 100755 --- a/src/main/EcoTest.java +++ b/src/main/EcoTest.java @@ -9,6 +9,8 @@ * (below) is set to true. * * TODO: expand this into a proper testing framework? + * + * THIS IS OUTDATED - DO NOT USE WITHOUT MAJOR MODIFICATIONS * * @author Daniel Vedder * @date 24.12.2014 @@ -45,13 +47,13 @@ if (turn == 1) { EcologiaIO.debug("Ecotest: Creating a herbivore at (1, 0)"); Herbivore herbivore = new Herbivore(World.getInstance().getNextID(), - Herbivore.defaultGenome, -1, 1, 0, - World.getInstance().getStartEnergyHerbivores(), 0); + Herbivore.defaultGenome, -1, 1, 0, + World.getInstance().getParam("startEnergyHerbivores"), 0); Simulator.addAnimal(herbivore); EcologiaIO.debug("Ecotest: Creating a carnivore at (1, 3)"); Carnivore carnivore = new Carnivore(World.getInstance().getNextID(), - Carnivore.defaultGenome, -1, 1, 3, - World.getInstance().getStartEnergyCarnivores(), 0); + Carnivore.defaultGenome, -1, 1, 3, + World.getInstance().getParam("startEnergyCarnivores"), 0); Simulator.addAnimal(carnivore); } if (turn == 2) { @@ -98,8 +100,8 @@ tpcY = 0; EcologiaIO.debug("Creating a carnivore at (0, 0)"); Carnivore carnivore = new Carnivore(World.getInstance().getNextID(), - Carnivore.defaultGenome, -1, tpcX, tpcY, - World.getInstance().getStartEnergyCarnivores(), 0); + Carnivore.defaultGenome, -1, tpcX, tpcY, + World.getInstance().getParam("startEnergyCarnivores"), 0); Simulator.addAnimal(carnivore); } else if (turn < 5 && turn > 1) { diff --git a/src/main/Ecologia.java b/src/main/Ecologia.java index 4d1ed75..cd308c0 100755 --- a/src/main/Ecologia.java +++ b/src/main/Ecologia.java @@ -5,8 +5,9 @@ import model.Simulator; /** - * Ecologia is a relatively simple ecosystem simulator, designed to show basic - * relationships between predators, prey and producers. + * This is an individual-based model of the ecology of the saiga antelope + * (Saiga tatarica tatarica). The source code was forked from the Ecologia + * ecosystem simulator, version 1.1 (www.launchpad.net/ecologia). * * Copyright (C) 2014-2016 Daniel Vedder * @@ -28,18 +29,18 @@ * manager, updating the program each round. * * @author Daniel Vedder - * @version 28.8.2014 + * @version 20.12.2016 */ public class Ecologia implements Runnable { public static Ecologia eco; //The singleton object - public static final String version = "1.1"; + public static final String version = "2.0 Saiga"; private static boolean noGUI = false; private GUI gui; private Simulator simulator; - private EcoTest tester; + //private EcoTest tester; //XXX Currently not used private Thread runningThread; @@ -73,7 +74,7 @@ else if (a.equals("--no-graphics")) noGUI = true; else if (a.equals("--autorun")) { - World.getInstance().setAutorun(new Integer(args[i+1])); + World.getInstance().setParam("autorun", new Integer(args[i+1])); i++; } else if (a.equals("--config")) { @@ -81,7 +82,7 @@ i++; } else if (a.equals("--timelapse")) { - World.getInstance().setTimelapse(new Integer(args[i+1])); + World.getInstance().setParam("timelapse", new Integer(args[i+1])); i++; } else EcologiaIO.error("Invalid commandline parameter: "+a); @@ -93,7 +94,7 @@ EcologiaIO.printStatus(); //Only use no-graphics mode when on autorun - if (noGUI && (World.getInstance().getAutorun() < 0)) { + if (noGUI && (World.getInstance().getParam("autorun") < 0)) { EcologiaIO.error("Returning to graphics mode as autorun not enabled."); noGUI = false; } @@ -119,9 +120,9 @@ EcologiaIO.log("Launching Ecologia..."); simulator = new Simulator(); if (!noGUI) gui = new GUI(); - tester = new EcoTest(); + //tester = new EcoTest(); EcologiaIO.debug("Launch completed."); - if (World.getInstance().getAutorun() > 0) autorun(); + if (World.getInstance().getParam("autorun") > 0) autorun(); } /** @@ -129,8 +130,8 @@ */ private void autorun() { - EcologiaIO.log("Performing autorun for "+World.getInstance().getAutorun()+" updates."); - World.getInstance().setStopAt(-1); + EcologiaIO.log("Performing autorun for "+World.getInstance().getParam("autorun")+" updates."); + World.getInstance().setParam("stopAt", -1); startThread(); } @@ -138,7 +139,7 @@ * Reset the simulator in order to start a new run. * * XXX: Depending on how long the simulation has already - * been running, this can take quite a long time. + * been running, this can take quite a long time. -- Does it? */ public void reset() { @@ -183,11 +184,11 @@ */ public synchronized void iterate() { - int autorun = World.getInstance().getAutorun(); + int autorun = World.getInstance().getParam("autorun"); World.getInstance().incrementTurn(); int turn = World.getInstance().getTurn(); EcologiaIO.log("Executing update "+turn); - if (EcologiaIO.debugging) tester.runTest(); + //if (EcologiaIO.debugging) tester.runTest(); simulator.update(); EcologiaIO.log("Average grass density: "+World.getInstance().getAverageGrassDensity()+"%"); EcologiaIO.log("Herbivore count: "+World.getInstance().getHerbivoreCount()); @@ -195,7 +196,7 @@ EcologiaIO.log("Generation counter: "+World.getInstance().getGeneration()); //If the stopAt number is reached, pause the simulation - if (World.getInstance().getStopAt() == turn) { + if (World.getInstance().getParam("stopAt") == turn) { World.getInstance().setRunning(false); } if (!noGUI) gui.update(); @@ -213,7 +214,7 @@ } //Pause for as long as the user wants try { - Thread.sleep(World.getInstance().getTimelapse()); + Thread.sleep(World.getInstance().getParam("timelapse")); } catch (InterruptedException ie) {} } @@ -223,7 +224,7 @@ */ private static void printHelp() { - System.out.println("Ecologia "+version+", the simple ecosystem simulator."); + System.out.println("Ecologia "+version+", an individual-based model of saiga ecology."); System.out.println("\nCommandline options:\n"); System.out.println("--help -h Print this help text"); System.out.println("--version -V Print the version number\n"); diff --git a/src/model/Animal.java b/src/model/Animal.java index 2917b85..d5229d4 100755 --- a/src/model/Animal.java +++ b/src/model/Animal.java @@ -23,6 +23,7 @@ * (max value: 2**31) should last for >40,000,000 updates * (based on a 1000 update test run). * => long IDs are not very urgent... + * --> CHECK THIS AGAIN */ protected int IDnumber; //A unique identifier for this animal protected int parent; //The ID number of the parent @@ -205,8 +206,8 @@ ArrayList targets = new ArrayList(); for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int[] newTarget = {xdist, ydist}; targets.add(newTarget); @@ -228,8 +229,8 @@ int minDist = genome.getSight()+1; for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int distance = getDistance(xdist, ydist); if (distance != 0 && distance < minDist) { @@ -255,8 +256,8 @@ int minDist = genome.getSight()+1; for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int distance = getDistance(xdist, ydist); int[] newTarget = {xdist, ydist}; @@ -292,8 +293,8 @@ case TOP_LEFT: nextY--; nextX--; break; default: EcologiaIO.error("Invalid direction passed to Animal.getNeighbouringField()! ("+dir+") by "+type.toString()+" @"+x+"/"+y); } - if (nextX < 0 || nextX >= World.getInstance().getSize()[0] || - nextY < 0 || nextY >= World.getInstance().getSize()[1]) { + if (nextX < 0 || nextX >= World.getInstance().getParam("xsize") || + nextY < 0 || nextY >= World.getInstance().getParam("ysize")) { return null; } else { diff --git a/src/model/Herbivore.java b/src/model/Herbivore.java index ee40b4e..decbf8a 100755 --- a/src/model/Herbivore.java +++ b/src/model/Herbivore.java @@ -82,7 +82,7 @@ for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { if (!(xdist == x && ydist == y) && xdist >= 0 && ydist >= 0 && - xdist < World.getInstance().getSize()[0] && ydist < World.getInstance().getSize()[1] && + xdist < World.getInstance().getParam("xsize") && ydist < World.getInstance().getParam("ysize") && Simulator.getField(xdist, ydist).getGrassDensity() > currentGrassDensity) { Direction d = super.getDirection(xdist, ydist); if (!possibleDirs.contains(d)) possibleDirs.add(d); diff --git a/src/model/Simulator.java b/src/model/Simulator.java index a2d9477..cb9bdea 100755 --- a/src/model/Simulator.java +++ b/src/model/Simulator.java @@ -45,12 +45,12 @@ //Calculate the new grass density on each plot EcologiaIO.debug("Simulator: Recalculating grass density."); double averageDensity = 0; - int xsize = World.getInstance().getSize()[0]; - int ysize = World.getInstance().getSize()[1]; + int xsize = World.getInstance().getParam("xsize"); + int ysize = World.getInstance().getParam("ysize"); for (int x = 0; x < xsize; x++) { for (int y = 0; y < ysize; y++) { if (!map[x][y].nearWater()) { - map[x][y].setLocalHumidity(World.getInstance().getHumidity()); + map[x][y].setLocalHumidity(Humidity.getStatus(World.getInstance().getParam("humidity"))); } map[x][y].calculateGrassDensity(); averageDensity += map[x][y].getGrassDensity(); @@ -105,14 +105,14 @@ private void initMap() { EcologiaIO.debug("Simulator: initialising map."); - int xsize = World.getInstance().getSize()[0]; - int ysize = World.getInstance().getSize()[1]; + int xsize = World.getInstance().getParam("xsize"); + int ysize = World.getInstance().getParam("ysize"); map = new MapField[xsize][ysize]; for (int x = 0; x < xsize; x++) { for (int y = 0; y < ysize; y++) { map[x][y] = new MapField(x, y, OccupantType.NONE, - World.getInstance().getHumidity(), - World.getInstance().getStartGrassDensity()); + Humidity.getStatus(World.getInstance().getParam("humidity")), + World.getInstance().getParam("startGrassDensity")); } } } @@ -123,13 +123,13 @@ private void initWaterTiles() { EcologiaIO.debug("Simulator: initialising water tiles."); - for (int i = 0; i < World.getInstance().getWaterTiles(); i++) { + for (int i = 0; i < World.getInstance().getParam("waterTiles"); i++) { //Each water tile is placed in a random location - int setX = random.nextInt(World.getInstance().getSize()[0]); - int setY = random.nextInt(World.getInstance().getSize()[1]); + int setX = random.nextInt(World.getInstance().getParam("xsize")); + int setY = random.nextInt(World.getInstance().getParam("ysize")); while (map[setX][setY].getOccupant() != OccupantType.NONE) { - setX = random.nextInt(World.getInstance().getSize()[0]); - setY = random.nextInt(World.getInstance().getSize()[1]); + setX = random.nextInt(World.getInstance().getParam("xsize")); + setY = random.nextInt(World.getInstance().getParam("ysize")); } map[setX][setY].setOccupant(OccupantType.WATER); //The fields around each water tile are watered @@ -154,28 +154,28 @@ herbivorePopulation = new ArrayList(); //Create the initial carnivore population, setting each carnivore down at a random position EcologiaIO.debug("Simulator: initialising carnivores."); - for (int j = 0; j < World.getInstance().getStartNoCarnivores(); j++) { - int setXCarnivore = random.nextInt(World.getInstance().getSize()[0]); - int setYCarnivore = random.nextInt(World.getInstance().getSize()[1]); + for (int j = 0; j < World.getInstance().getParam("startNoCarnivores"); j++) { + int setXCarnivore = random.nextInt(World.getInstance().getParam("xsize")); + int setYCarnivore = random.nextInt(World.getInstance().getParam("ysize")); while (map[setXCarnivore][setYCarnivore].getOccupant() != OccupantType.NONE) { - setXCarnivore = random.nextInt(World.getInstance().getSize()[0]); - setYCarnivore = random.nextInt(World.getInstance().getSize()[1]); + setXCarnivore = random.nextInt(World.getInstance().getParam("xsize")); + setYCarnivore = random.nextInt(World.getInstance().getParam("ysize")); } - int startEnergyCarnivores = World.getInstance().getStartEnergyCarnivores(); + int startEnergyCarnivores = World.getInstance().getParam("startEnergyCarnivores"); carnivorePopulation.add(new Carnivore(World.getInstance().getNextID(), Carnivore.defaultGenome, 1, setXCarnivore, setYCarnivore, startEnergyCarnivores, 0)); } //Create the initial herbivore population, setting each herbivore down at a random position EcologiaIO.debug("Simulator: initialising herbivores."); - for (int i = 0; i < World.getInstance().getStartNoHerbivores(); i++) { - int setXHerbivore = random.nextInt(World.getInstance().getSize()[0]); - int setYHerbivore = random.nextInt(World.getInstance().getSize()[1]); + for (int i = 0; i < World.getInstance().getParam("startNoHerbivores"); i++) { + int setXHerbivore = random.nextInt(World.getInstance().getParam("xsize")); + int setYHerbivore = random.nextInt(World.getInstance().getParam("ysize")); while (map[setXHerbivore][setYHerbivore].getOccupant() != OccupantType.NONE) { - setXHerbivore = random.nextInt(World.getInstance().getSize()[0]); - setYHerbivore = random.nextInt(World.getInstance().getSize()[1]); + setXHerbivore = random.nextInt(World.getInstance().getParam("xsize")); + setYHerbivore = random.nextInt(World.getInstance().getParam("ysize")); } - int startEnergyHerbivores = World.getInstance().getStartEnergyHerbivores(); + int startEnergyHerbivores = World.getInstance().getParam("startEnergyHerbivores"); herbivorePopulation.add(new Herbivore(World.getInstance().getNextID(), Herbivore.defaultGenome, 1, setXHerbivore, setYHerbivore, startEnergyHerbivores, 0)); diff --git a/src/view/Display.java b/src/view/Display.java index 29f5e73..f5b61d1 100755 --- a/src/view/Display.java +++ b/src/view/Display.java @@ -104,8 +104,8 @@ public void mouseClicked(MouseEvent click) { int fieldX = click.getX()/20; int fieldY = click.getY()/20; - if (fieldX >= 0 && fieldX < World.getInstance().getSize()[0] && fieldY >= 0 - && fieldY < World.getInstance().getSize()[1]) { + if (fieldX >= 0 && fieldX < World.getInstance().getParam("xsize") && fieldY >= 0 + && fieldY < World.getInstance().getParam("ysize")) { infobox.show(click.getX()/20, click.getY()/20); } } diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7e1d0dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +releases +.classpath +.project +data +*.log diff --git a/README b/README index 7e39738..996df4c 100755 --- a/README +++ b/README @@ -2,6 +2,9 @@ Ecologia README =================== +--- THIS IS THE README FOR ECOLOGIA 1.1! --- + (a readme for version 2 is soon to follow) + Ecologia is a graphical ecosystem simulator. Currently it is still very basic, but hopefully it will soon be expanded to include more and more aspects of a real ecosystem. diff --git a/analysis/demography/population.py b/analysis/demography/population.py index 9490f0f..1955b45 100755 --- a/analysis/demography/population.py +++ b/analysis/demography/population.py @@ -18,12 +18,11 @@ global generation_count global carnivore_count global herbivore_count -global kill_rate global grass_density global src_file global out_file -version = "0.3" +version = "0.3.1" ''' @@ -32,8 +31,7 @@ def load_log(): global src_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density try: log_file = open(src_file, "r") line = log_file.readline() @@ -47,9 +45,6 @@ elif "Carnivore count" in line: next_count = line[line.find("Carnivore count: ")+17:-1] carnivore_count.append(int(next_count)) - elif "Carnivore hunt success rate: " in line: - next_count = line[line.find("rate: ")+6:-2] - kill_rate.append(int(next_count)) elif "Average grass density: " in line: next_count = line[line.find("density: ")+9:-2] grass_density.append(next_count) @@ -68,14 +63,12 @@ ''' def compact(): global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density if len(update_count) < 1000: return new_update_count = [update_count[0]] new_herbivore_count = [herbivore_count[0]] new_carnivore_count = [carnivore_count[0]] - new_kill_rate = [kill_rate[0]] new_grass_density = [grass_density[0]] new_generation_count = [generation_count[0]] for i in range(1, len(update_count)): @@ -83,13 +76,11 @@ new_update_count.append(update_count[i]) new_herbivore_count.append(herbivore_count[i]) new_carnivore_count.append(carnivore_count[i]) - new_kill_rate.append(kill_rate[i]) new_grass_density.append(grass_density[i]) new_generation_count.append(generation_count[i]) update_count = new_update_count herbivore_count = new_herbivore_count carnivore_count = new_carnivore_count - kill_rate = new_kill_rate grass_density = new_grass_density generation_count = new_generation_count compact() #Keep going until the data set is small enough @@ -100,8 +91,7 @@ def save_csv(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate all the data data = "Updates" for u in update_count: @@ -112,9 +102,6 @@ data = data+"\nCarnivores" for c in carnivore_count: data = data+","+str(c) - data = data+"\nKill rate" - for k in kill_rate: - data = data+","+str(k) data = data+"\nGrass density" for g in grass_density: data = data+","+str(g) @@ -136,13 +123,12 @@ def save_table(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate the data - data = "Updates\tHerbivores\tCarnivores\tKillRate\tGrassDensity\tGenerations" + data = "Updates\tHerbivores\tCarnivores\tGrassDensity\tGenerations" for i in range(len(update_count)): data = data+"\n"+str(update_count[i])+"\t"+str(herbivore_count[i])+"\t"+str(carnivore_count[i]) - data = data+"\t"+str(kill_rate[i])+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) + data = data+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) #Then write it to file try: table_file = open(out_file, "w") @@ -174,8 +160,7 @@ global src_file global out_file global update_count, generation_count - global carnivore_count, herbivore_count - global kill_rate, grass_density + global carnivore_count, herbivore_count, grass_density src_file = "ecologia.log" #Choose the right default output file name if "--table" in sys.argv: out_file = "populations.txt" @@ -198,8 +183,7 @@ out_file = out_file+".csv" #Initialise variables update_count, generation_count = [], [] - carnivore_count, herbivore_count = [], [] - kill_rate, grass_density = [], [] + carnivore_count, herbivore_count, grass_density = [], [], [] #Do the actual work load_log() if "--compact" in sys.argv or "--ods" in sys.argv: compact() diff --git a/ecologia.config b/ecologia.config new file mode 100755 index 0000000..67f2b5c --- /dev/null +++ b/ecologia.config @@ -0,0 +1,45 @@ +# +# This is the default Ecologia configuration file. Its values have +# been chosen to provide stable ecosystem dynamics when run. +# +# Daniel Vedder, 20/09/2016 +# + +[world] +xsize 100 +ysize 100 +timelapse 100 +stopAt 200 +waterTiles 10 +humidity 1 +startGrassDensity 100 +startNoCarnivores 50 +startNoHerbivores 200 +startEnergyCarnivores 150 +startEnergyHerbivores 100 + +[herbivore] +mutationRate 0 +speed 2 +stamina 10 +sight 4 +metabolism 10 +ageLimit 150 +strength 10 +reproductiveEnergy 120 +maturityAge 15 +gestation 10 +reproductionRate 2 + +[carnivore] +mutationRate 0 +speed 3 +stamina 10 +sight 4 +metabolism 18 +ageLimit 200 +strength 11 +reproductiveEnergy 200 +maturityAge 30 +gestation 10 +reproductionRate 1 diff --git a/log_run b/log_run new file mode 100755 index 0000000..bb98de6 --- /dev/null +++ b/log_run @@ -0,0 +1,13 @@ +#!/bin/bash +# Run Ecologia during development +# Automatically analyses the log file + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --config ../../ecologia.config +cp ecologia.log ../../analysis +cd ../../analysis/demography +./population.py --table --logfile ../ecologia.log +R --slave -f population.r +mv populations-*.jpg ../../data diff --git a/release.py b/release.py index c610a24..d7e6a0c 100755 --- a/release.py +++ b/release.py @@ -23,8 +23,9 @@ main_file = open("src/main/Ecologia.java") main_source = main_file.read() main_file.close() + #This string surgery is a little iffy, but it works version_start = main_source.find("version = ")+11 - version_end = main_source.find('"', version_start) + version_end = main_source.find(' ', version_start) return main_source[version_start:version_end] ''' diff --git a/run b/run new file mode 100755 index 0000000..9de1ff8 --- /dev/null +++ b/run @@ -0,0 +1,7 @@ +#!/bin/bash +# Run Ecologia during development + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --debug --config ../../ecologia.config diff --git a/src/controller/World.java b/src/controller/World.java index 4acb5f3..68088e3 100755 --- a/src/controller/World.java +++ b/src/controller/World.java @@ -25,7 +25,8 @@ private static World world; //The Singleton instance of this class //Parameter variables - private int[] size; //The size of the world (x*y) + //TODO Reduce these to a single hashmap + /*private int[] size; //The size of the world (x*y) private int timelapse; //When running, the simulation will be updated every so many milliseconds. private int stopAt; //The simulation will stop once this update is reached. private int autorun; //The number of updates the simulation will run for automatically before quitting @@ -34,6 +35,9 @@ private int waterTiles; //The number of water tiles that will be created. private int startNoCarnivores, startNoHerbivores; //The starting number of carnivores/herbivores. private int startEnergyCarnivores, startEnergyHerbivores; //The starting energy for carnivores/herbivores. + */ + + private HashMap parameters; //Runtime variables private boolean running; //Is the simulation running? @@ -51,7 +55,7 @@ private World() { //Parameter settings - size = new int[] {100, 100}; //Default: 100*100 + /*size = new int[] {100, 100}; //Default: 100*100 timelapse = 100; //Default: 300 - can be changed at run-time stopAt = 200; //Default: 100 - can be changed at run-time autorun = -1; //Default: -1 (off) @@ -61,7 +65,21 @@ startNoCarnivores = 50; //Default: 50 - Hypothetical ideal: 5 (?) startNoHerbivores = 200; //Default: 200 - Hypothetical ideal: 160 (?) startEnergyCarnivores = 150; //Default: 150 - startEnergyHerbivores = 100; //Default: 100 + startEnergyHerbivores = 100; //Default: 100 */ + parameters = new HashMap(); + + parameters.put("xsize", 100); + parameters.put("ysize", 100); + parameters.put("timelapse", 100); + parameters.put("stopAt", 200); + parameters.put("autorun", -1); + parameters.put("waterTiles", 10); + parameters.put("humidity", 1); + parameters.put("startGrassDensity", 100); + parameters.put("startNoCarnivores", 50); + parameters.put("startNoHerbivores", 200); + parameters.put("startEnergyCarnivores", 150); + parameters.put("startEnergyHerbivores", 100); reset(); //Runtime variables } @@ -114,7 +132,7 @@ if (line.startsWith("[") && line.endsWith("]")) section = line; //Deal with world variables else if (section.equals("[world]")) { - switch (var) { + /*switch (var) { case "width": size[0] = value; break; case "height": size[1] = value; break; case "timelapse": timelapse = value; break; @@ -128,7 +146,8 @@ case "startEnergyHerbivores": startEnergyHerbivores = value; break; case "startEnergyCarnivores": startEnergyCarnivores = value; break; default: EcologiaIO.error("Invalid config variable in the [world] section: "+var); - } + }*/ + setParam(var, value); } //Configure default animal genomes else if (section.equals("[herbivore]")) { @@ -164,7 +183,7 @@ herbivoreCounter = 0; carnivoreCounter = 0; highestGeneration = 1; - averageGrassDensity = startGrassDensity; + averageGrassDensity = parameters.get("startGrassDensity"); animals = null; news = new ArrayList(); } @@ -244,6 +263,33 @@ if (type == OccupantType.HERBIVORE) Herbivore.defaultGenome = genome; else if (type == OccupantType.CARNIVORE) Carnivore.defaultGenome = genome; } + + /** + * Return a parameter value. + */ + public int getParam(String param) + { + if (parameters.containsKey(param)) + return parameters.get(param); + else { + EcologiaIO.error("getParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + return -1; //Will never be reached, but is syntactically needed + } + } + + /** + * Set a parameter value. + */ + public void setParam(String param, int value) + { + //XXX Is this check necessary here? + if (parameters.containsKey(param)) + parameters.put(param, value); + else EcologiaIO.error("setParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + } + + +/* public int[] getSize() { @@ -358,6 +404,8 @@ this.startEnergyHerbivores = startEnergyHerbivores; } +*/ + public int getHerbivoreCount() { return herbivoreCounter; diff --git a/src/main/EcoTest.java b/src/main/EcoTest.java index 6d456d4..240f503 100755 --- a/src/main/EcoTest.java +++ b/src/main/EcoTest.java @@ -9,6 +9,8 @@ * (below) is set to true. * * TODO: expand this into a proper testing framework? + * + * THIS IS OUTDATED - DO NOT USE WITHOUT MAJOR MODIFICATIONS * * @author Daniel Vedder * @date 24.12.2014 @@ -45,13 +47,13 @@ if (turn == 1) { EcologiaIO.debug("Ecotest: Creating a herbivore at (1, 0)"); Herbivore herbivore = new Herbivore(World.getInstance().getNextID(), - Herbivore.defaultGenome, -1, 1, 0, - World.getInstance().getStartEnergyHerbivores(), 0); + Herbivore.defaultGenome, -1, 1, 0, + World.getInstance().getParam("startEnergyHerbivores"), 0); Simulator.addAnimal(herbivore); EcologiaIO.debug("Ecotest: Creating a carnivore at (1, 3)"); Carnivore carnivore = new Carnivore(World.getInstance().getNextID(), - Carnivore.defaultGenome, -1, 1, 3, - World.getInstance().getStartEnergyCarnivores(), 0); + Carnivore.defaultGenome, -1, 1, 3, + World.getInstance().getParam("startEnergyCarnivores"), 0); Simulator.addAnimal(carnivore); } if (turn == 2) { @@ -98,8 +100,8 @@ tpcY = 0; EcologiaIO.debug("Creating a carnivore at (0, 0)"); Carnivore carnivore = new Carnivore(World.getInstance().getNextID(), - Carnivore.defaultGenome, -1, tpcX, tpcY, - World.getInstance().getStartEnergyCarnivores(), 0); + Carnivore.defaultGenome, -1, tpcX, tpcY, + World.getInstance().getParam("startEnergyCarnivores"), 0); Simulator.addAnimal(carnivore); } else if (turn < 5 && turn > 1) { diff --git a/src/main/Ecologia.java b/src/main/Ecologia.java index 4d1ed75..cd308c0 100755 --- a/src/main/Ecologia.java +++ b/src/main/Ecologia.java @@ -5,8 +5,9 @@ import model.Simulator; /** - * Ecologia is a relatively simple ecosystem simulator, designed to show basic - * relationships between predators, prey and producers. + * This is an individual-based model of the ecology of the saiga antelope + * (Saiga tatarica tatarica). The source code was forked from the Ecologia + * ecosystem simulator, version 1.1 (www.launchpad.net/ecologia). * * Copyright (C) 2014-2016 Daniel Vedder * @@ -28,18 +29,18 @@ * manager, updating the program each round. * * @author Daniel Vedder - * @version 28.8.2014 + * @version 20.12.2016 */ public class Ecologia implements Runnable { public static Ecologia eco; //The singleton object - public static final String version = "1.1"; + public static final String version = "2.0 Saiga"; private static boolean noGUI = false; private GUI gui; private Simulator simulator; - private EcoTest tester; + //private EcoTest tester; //XXX Currently not used private Thread runningThread; @@ -73,7 +74,7 @@ else if (a.equals("--no-graphics")) noGUI = true; else if (a.equals("--autorun")) { - World.getInstance().setAutorun(new Integer(args[i+1])); + World.getInstance().setParam("autorun", new Integer(args[i+1])); i++; } else if (a.equals("--config")) { @@ -81,7 +82,7 @@ i++; } else if (a.equals("--timelapse")) { - World.getInstance().setTimelapse(new Integer(args[i+1])); + World.getInstance().setParam("timelapse", new Integer(args[i+1])); i++; } else EcologiaIO.error("Invalid commandline parameter: "+a); @@ -93,7 +94,7 @@ EcologiaIO.printStatus(); //Only use no-graphics mode when on autorun - if (noGUI && (World.getInstance().getAutorun() < 0)) { + if (noGUI && (World.getInstance().getParam("autorun") < 0)) { EcologiaIO.error("Returning to graphics mode as autorun not enabled."); noGUI = false; } @@ -119,9 +120,9 @@ EcologiaIO.log("Launching Ecologia..."); simulator = new Simulator(); if (!noGUI) gui = new GUI(); - tester = new EcoTest(); + //tester = new EcoTest(); EcologiaIO.debug("Launch completed."); - if (World.getInstance().getAutorun() > 0) autorun(); + if (World.getInstance().getParam("autorun") > 0) autorun(); } /** @@ -129,8 +130,8 @@ */ private void autorun() { - EcologiaIO.log("Performing autorun for "+World.getInstance().getAutorun()+" updates."); - World.getInstance().setStopAt(-1); + EcologiaIO.log("Performing autorun for "+World.getInstance().getParam("autorun")+" updates."); + World.getInstance().setParam("stopAt", -1); startThread(); } @@ -138,7 +139,7 @@ * Reset the simulator in order to start a new run. * * XXX: Depending on how long the simulation has already - * been running, this can take quite a long time. + * been running, this can take quite a long time. -- Does it? */ public void reset() { @@ -183,11 +184,11 @@ */ public synchronized void iterate() { - int autorun = World.getInstance().getAutorun(); + int autorun = World.getInstance().getParam("autorun"); World.getInstance().incrementTurn(); int turn = World.getInstance().getTurn(); EcologiaIO.log("Executing update "+turn); - if (EcologiaIO.debugging) tester.runTest(); + //if (EcologiaIO.debugging) tester.runTest(); simulator.update(); EcologiaIO.log("Average grass density: "+World.getInstance().getAverageGrassDensity()+"%"); EcologiaIO.log("Herbivore count: "+World.getInstance().getHerbivoreCount()); @@ -195,7 +196,7 @@ EcologiaIO.log("Generation counter: "+World.getInstance().getGeneration()); //If the stopAt number is reached, pause the simulation - if (World.getInstance().getStopAt() == turn) { + if (World.getInstance().getParam("stopAt") == turn) { World.getInstance().setRunning(false); } if (!noGUI) gui.update(); @@ -213,7 +214,7 @@ } //Pause for as long as the user wants try { - Thread.sleep(World.getInstance().getTimelapse()); + Thread.sleep(World.getInstance().getParam("timelapse")); } catch (InterruptedException ie) {} } @@ -223,7 +224,7 @@ */ private static void printHelp() { - System.out.println("Ecologia "+version+", the simple ecosystem simulator."); + System.out.println("Ecologia "+version+", an individual-based model of saiga ecology."); System.out.println("\nCommandline options:\n"); System.out.println("--help -h Print this help text"); System.out.println("--version -V Print the version number\n"); diff --git a/src/model/Animal.java b/src/model/Animal.java index 2917b85..d5229d4 100755 --- a/src/model/Animal.java +++ b/src/model/Animal.java @@ -23,6 +23,7 @@ * (max value: 2**31) should last for >40,000,000 updates * (based on a 1000 update test run). * => long IDs are not very urgent... + * --> CHECK THIS AGAIN */ protected int IDnumber; //A unique identifier for this animal protected int parent; //The ID number of the parent @@ -205,8 +206,8 @@ ArrayList targets = new ArrayList(); for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int[] newTarget = {xdist, ydist}; targets.add(newTarget); @@ -228,8 +229,8 @@ int minDist = genome.getSight()+1; for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int distance = getDistance(xdist, ydist); if (distance != 0 && distance < minDist) { @@ -255,8 +256,8 @@ int minDist = genome.getSight()+1; for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int distance = getDistance(xdist, ydist); int[] newTarget = {xdist, ydist}; @@ -292,8 +293,8 @@ case TOP_LEFT: nextY--; nextX--; break; default: EcologiaIO.error("Invalid direction passed to Animal.getNeighbouringField()! ("+dir+") by "+type.toString()+" @"+x+"/"+y); } - if (nextX < 0 || nextX >= World.getInstance().getSize()[0] || - nextY < 0 || nextY >= World.getInstance().getSize()[1]) { + if (nextX < 0 || nextX >= World.getInstance().getParam("xsize") || + nextY < 0 || nextY >= World.getInstance().getParam("ysize")) { return null; } else { diff --git a/src/model/Herbivore.java b/src/model/Herbivore.java index ee40b4e..decbf8a 100755 --- a/src/model/Herbivore.java +++ b/src/model/Herbivore.java @@ -82,7 +82,7 @@ for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { if (!(xdist == x && ydist == y) && xdist >= 0 && ydist >= 0 && - xdist < World.getInstance().getSize()[0] && ydist < World.getInstance().getSize()[1] && + xdist < World.getInstance().getParam("xsize") && ydist < World.getInstance().getParam("ysize") && Simulator.getField(xdist, ydist).getGrassDensity() > currentGrassDensity) { Direction d = super.getDirection(xdist, ydist); if (!possibleDirs.contains(d)) possibleDirs.add(d); diff --git a/src/model/Simulator.java b/src/model/Simulator.java index a2d9477..cb9bdea 100755 --- a/src/model/Simulator.java +++ b/src/model/Simulator.java @@ -45,12 +45,12 @@ //Calculate the new grass density on each plot EcologiaIO.debug("Simulator: Recalculating grass density."); double averageDensity = 0; - int xsize = World.getInstance().getSize()[0]; - int ysize = World.getInstance().getSize()[1]; + int xsize = World.getInstance().getParam("xsize"); + int ysize = World.getInstance().getParam("ysize"); for (int x = 0; x < xsize; x++) { for (int y = 0; y < ysize; y++) { if (!map[x][y].nearWater()) { - map[x][y].setLocalHumidity(World.getInstance().getHumidity()); + map[x][y].setLocalHumidity(Humidity.getStatus(World.getInstance().getParam("humidity"))); } map[x][y].calculateGrassDensity(); averageDensity += map[x][y].getGrassDensity(); @@ -105,14 +105,14 @@ private void initMap() { EcologiaIO.debug("Simulator: initialising map."); - int xsize = World.getInstance().getSize()[0]; - int ysize = World.getInstance().getSize()[1]; + int xsize = World.getInstance().getParam("xsize"); + int ysize = World.getInstance().getParam("ysize"); map = new MapField[xsize][ysize]; for (int x = 0; x < xsize; x++) { for (int y = 0; y < ysize; y++) { map[x][y] = new MapField(x, y, OccupantType.NONE, - World.getInstance().getHumidity(), - World.getInstance().getStartGrassDensity()); + Humidity.getStatus(World.getInstance().getParam("humidity")), + World.getInstance().getParam("startGrassDensity")); } } } @@ -123,13 +123,13 @@ private void initWaterTiles() { EcologiaIO.debug("Simulator: initialising water tiles."); - for (int i = 0; i < World.getInstance().getWaterTiles(); i++) { + for (int i = 0; i < World.getInstance().getParam("waterTiles"); i++) { //Each water tile is placed in a random location - int setX = random.nextInt(World.getInstance().getSize()[0]); - int setY = random.nextInt(World.getInstance().getSize()[1]); + int setX = random.nextInt(World.getInstance().getParam("xsize")); + int setY = random.nextInt(World.getInstance().getParam("ysize")); while (map[setX][setY].getOccupant() != OccupantType.NONE) { - setX = random.nextInt(World.getInstance().getSize()[0]); - setY = random.nextInt(World.getInstance().getSize()[1]); + setX = random.nextInt(World.getInstance().getParam("xsize")); + setY = random.nextInt(World.getInstance().getParam("ysize")); } map[setX][setY].setOccupant(OccupantType.WATER); //The fields around each water tile are watered @@ -154,28 +154,28 @@ herbivorePopulation = new ArrayList(); //Create the initial carnivore population, setting each carnivore down at a random position EcologiaIO.debug("Simulator: initialising carnivores."); - for (int j = 0; j < World.getInstance().getStartNoCarnivores(); j++) { - int setXCarnivore = random.nextInt(World.getInstance().getSize()[0]); - int setYCarnivore = random.nextInt(World.getInstance().getSize()[1]); + for (int j = 0; j < World.getInstance().getParam("startNoCarnivores"); j++) { + int setXCarnivore = random.nextInt(World.getInstance().getParam("xsize")); + int setYCarnivore = random.nextInt(World.getInstance().getParam("ysize")); while (map[setXCarnivore][setYCarnivore].getOccupant() != OccupantType.NONE) { - setXCarnivore = random.nextInt(World.getInstance().getSize()[0]); - setYCarnivore = random.nextInt(World.getInstance().getSize()[1]); + setXCarnivore = random.nextInt(World.getInstance().getParam("xsize")); + setYCarnivore = random.nextInt(World.getInstance().getParam("ysize")); } - int startEnergyCarnivores = World.getInstance().getStartEnergyCarnivores(); + int startEnergyCarnivores = World.getInstance().getParam("startEnergyCarnivores"); carnivorePopulation.add(new Carnivore(World.getInstance().getNextID(), Carnivore.defaultGenome, 1, setXCarnivore, setYCarnivore, startEnergyCarnivores, 0)); } //Create the initial herbivore population, setting each herbivore down at a random position EcologiaIO.debug("Simulator: initialising herbivores."); - for (int i = 0; i < World.getInstance().getStartNoHerbivores(); i++) { - int setXHerbivore = random.nextInt(World.getInstance().getSize()[0]); - int setYHerbivore = random.nextInt(World.getInstance().getSize()[1]); + for (int i = 0; i < World.getInstance().getParam("startNoHerbivores"); i++) { + int setXHerbivore = random.nextInt(World.getInstance().getParam("xsize")); + int setYHerbivore = random.nextInt(World.getInstance().getParam("ysize")); while (map[setXHerbivore][setYHerbivore].getOccupant() != OccupantType.NONE) { - setXHerbivore = random.nextInt(World.getInstance().getSize()[0]); - setYHerbivore = random.nextInt(World.getInstance().getSize()[1]); + setXHerbivore = random.nextInt(World.getInstance().getParam("xsize")); + setYHerbivore = random.nextInt(World.getInstance().getParam("ysize")); } - int startEnergyHerbivores = World.getInstance().getStartEnergyHerbivores(); + int startEnergyHerbivores = World.getInstance().getParam("startEnergyHerbivores"); herbivorePopulation.add(new Herbivore(World.getInstance().getNextID(), Herbivore.defaultGenome, 1, setXHerbivore, setYHerbivore, startEnergyHerbivores, 0)); diff --git a/src/view/Display.java b/src/view/Display.java index 29f5e73..f5b61d1 100755 --- a/src/view/Display.java +++ b/src/view/Display.java @@ -104,8 +104,8 @@ public void mouseClicked(MouseEvent click) { int fieldX = click.getX()/20; int fieldY = click.getY()/20; - if (fieldX >= 0 && fieldX < World.getInstance().getSize()[0] && fieldY >= 0 - && fieldY < World.getInstance().getSize()[1]) { + if (fieldX >= 0 && fieldX < World.getInstance().getParam("xsize") && fieldY >= 0 + && fieldY < World.getInstance().getParam("ysize")) { infobox.show(click.getX()/20, click.getY()/20); } } diff --git a/src/view/GUI.java b/src/view/GUI.java index 9712106..c69dbf3 100755 --- a/src/view/GUI.java +++ b/src/view/GUI.java @@ -45,7 +45,7 @@ public GUI() { EcologiaIO.debug("Creating GUI"); - this.setTitle("Ecologia"); + this.setTitle("Ecologia "+Ecologia.version); this.setSize(1000, 560); this.setDefaultCloseOperation(EXIT_ON_CLOSE); createMenu(); @@ -75,16 +75,16 @@ else run.setText("Start"); //Update the humidity from the combo box Humidity setHumidity = Humidity.fromString((String) humidityChooser.getSelectedItem()); - if (setHumidity != World.getInstance().getHumidity()) { - World.getInstance().setHumidity(setHumidity); + if (setHumidity.getValue() != World.getInstance().getParam("humidity")) { + World.getInstance().setParam("humidity", setHumidity.getValue()); EcologiaIO.log("Humidity set to "+setHumidity.getString()); } //Update the simulation speed from the speed slider int setSpeed = speedSlider.getMaximum() - speedSlider.getValue(); - World.getInstance().setTimelapse(setSpeed); + World.getInstance().setParam("timelapse", setSpeed); //Update the stopAt variable from user input try { - World.getInstance().setStopAt(Integer.parseInt((stopAtField.getText()))); + World.getInstance().setParam("stopAt", Integer.parseInt((stopAtField.getText()))); } catch (NumberFormatException nfe) {} //Update the various counters @@ -93,7 +93,7 @@ carnivore_counter.setText("Carnivores: "+ World.getInstance().getCarnivoreCount()); generation_counter.setText("Generations: "+World.getInstance().getGeneration()); grass_counter.setText("Grass density: "+World.getInstance().getAverageGrassDensity()); - humidityChooser.setSelectedItem(World.getInstance().getHumidity().getString()); + humidityChooser.setSelectedItem(Humidity.getStatus(World.getInstance().getParam("humidity")).getString()); } /** @@ -251,7 +251,7 @@ {Humidity.SATURATION.getString(), Humidity.WET.getString(), Humidity.DRY.getString(), Humidity.DROUGHT.getString(), Humidity.SEVERE_DROUGHT.getString()}); humidityChooser.setMaximumSize(new Dimension(140, 30)); - humidityChooser.setSelectedItem(World.getInstance().getHumidity().getString()); + humidityChooser.setSelectedItem(Humidity.getStatus(World.getInstance().getParam("humidity")).getString()); hum_panel.add(humidity); hum_panel.add(humidityChooser); information.add(hum_panel); @@ -292,7 +292,7 @@ //Add the simulation speed slider information.add(new JLabel("Simulation speed:")); information.add(Box.createVerticalStrut(3)); - speedSlider = new JSlider(0, 1500, 1500-World.getInstance().getTimelapse()); + speedSlider = new JSlider(0, 1500, 1500-World.getInstance().getParam("timelapse")); speedSlider.setMajorTickSpacing(300); speedSlider.setMinorTickSpacing(50); speedSlider.setPaintTicks(true); @@ -304,7 +304,7 @@ JLabel stopLabel = new JLabel("Pause at update:"); stopAtField = new JTextField(5); stopAtField.setMaximumSize(stopAtField.getPreferredSize()); - stopAtField.setText(Integer.toString(World.getInstance().getStopAt())); + stopAtField.setText(Integer.toString(World.getInstance().getParam("stopAt"))); stopPanel.add(Box.createVerticalStrut(3)); stopPanel.add(stopLabel); stopPanel.add(Box.createVerticalStrut(1)); @@ -324,7 +324,8 @@ */ private void addDisplay() { - display = new Display(World.getInstance().getSize()); + display = new Display(new int[] {World.getInstance().getParam("xsize"), + World.getInstance().getParam("ysize")}); scrollscreen = new JScrollPane(display, JScrollPane. VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); this.add(scrollscreen, BorderLayout.CENTER); diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7e1d0dc --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +releases +.classpath +.project +data +*.log diff --git a/README b/README index 7e39738..996df4c 100755 --- a/README +++ b/README @@ -2,6 +2,9 @@ Ecologia README =================== +--- THIS IS THE README FOR ECOLOGIA 1.1! --- + (a readme for version 2 is soon to follow) + Ecologia is a graphical ecosystem simulator. Currently it is still very basic, but hopefully it will soon be expanded to include more and more aspects of a real ecosystem. diff --git a/analysis/demography/population.py b/analysis/demography/population.py index 9490f0f..1955b45 100755 --- a/analysis/demography/population.py +++ b/analysis/demography/population.py @@ -18,12 +18,11 @@ global generation_count global carnivore_count global herbivore_count -global kill_rate global grass_density global src_file global out_file -version = "0.3" +version = "0.3.1" ''' @@ -32,8 +31,7 @@ def load_log(): global src_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density try: log_file = open(src_file, "r") line = log_file.readline() @@ -47,9 +45,6 @@ elif "Carnivore count" in line: next_count = line[line.find("Carnivore count: ")+17:-1] carnivore_count.append(int(next_count)) - elif "Carnivore hunt success rate: " in line: - next_count = line[line.find("rate: ")+6:-2] - kill_rate.append(int(next_count)) elif "Average grass density: " in line: next_count = line[line.find("density: ")+9:-2] grass_density.append(next_count) @@ -68,14 +63,12 @@ ''' def compact(): global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density if len(update_count) < 1000: return new_update_count = [update_count[0]] new_herbivore_count = [herbivore_count[0]] new_carnivore_count = [carnivore_count[0]] - new_kill_rate = [kill_rate[0]] new_grass_density = [grass_density[0]] new_generation_count = [generation_count[0]] for i in range(1, len(update_count)): @@ -83,13 +76,11 @@ new_update_count.append(update_count[i]) new_herbivore_count.append(herbivore_count[i]) new_carnivore_count.append(carnivore_count[i]) - new_kill_rate.append(kill_rate[i]) new_grass_density.append(grass_density[i]) new_generation_count.append(generation_count[i]) update_count = new_update_count herbivore_count = new_herbivore_count carnivore_count = new_carnivore_count - kill_rate = new_kill_rate grass_density = new_grass_density generation_count = new_generation_count compact() #Keep going until the data set is small enough @@ -100,8 +91,7 @@ def save_csv(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate all the data data = "Updates" for u in update_count: @@ -112,9 +102,6 @@ data = data+"\nCarnivores" for c in carnivore_count: data = data+","+str(c) - data = data+"\nKill rate" - for k in kill_rate: - data = data+","+str(k) data = data+"\nGrass density" for g in grass_density: data = data+","+str(g) @@ -136,13 +123,12 @@ def save_table(): global out_file global update_count, generation_count - global herbivore_count, carnivore_count - global kill_rate, grass_density + global herbivore_count, carnivore_count, grass_density #Accumulate the data - data = "Updates\tHerbivores\tCarnivores\tKillRate\tGrassDensity\tGenerations" + data = "Updates\tHerbivores\tCarnivores\tGrassDensity\tGenerations" for i in range(len(update_count)): data = data+"\n"+str(update_count[i])+"\t"+str(herbivore_count[i])+"\t"+str(carnivore_count[i]) - data = data+"\t"+str(kill_rate[i])+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) + data = data+"\t"+str(grass_density[i])+"\t"+str(generation_count[i]) #Then write it to file try: table_file = open(out_file, "w") @@ -174,8 +160,7 @@ global src_file global out_file global update_count, generation_count - global carnivore_count, herbivore_count - global kill_rate, grass_density + global carnivore_count, herbivore_count, grass_density src_file = "ecologia.log" #Choose the right default output file name if "--table" in sys.argv: out_file = "populations.txt" @@ -198,8 +183,7 @@ out_file = out_file+".csv" #Initialise variables update_count, generation_count = [], [] - carnivore_count, herbivore_count = [], [] - kill_rate, grass_density = [], [] + carnivore_count, herbivore_count, grass_density = [], [], [] #Do the actual work load_log() if "--compact" in sys.argv or "--ods" in sys.argv: compact() diff --git a/ecologia.config b/ecologia.config new file mode 100755 index 0000000..67f2b5c --- /dev/null +++ b/ecologia.config @@ -0,0 +1,45 @@ +# +# This is the default Ecologia configuration file. Its values have +# been chosen to provide stable ecosystem dynamics when run. +# +# Daniel Vedder, 20/09/2016 +# + +[world] +xsize 100 +ysize 100 +timelapse 100 +stopAt 200 +waterTiles 10 +humidity 1 +startGrassDensity 100 +startNoCarnivores 50 +startNoHerbivores 200 +startEnergyCarnivores 150 +startEnergyHerbivores 100 + +[herbivore] +mutationRate 0 +speed 2 +stamina 10 +sight 4 +metabolism 10 +ageLimit 150 +strength 10 +reproductiveEnergy 120 +maturityAge 15 +gestation 10 +reproductionRate 2 + +[carnivore] +mutationRate 0 +speed 3 +stamina 10 +sight 4 +metabolism 18 +ageLimit 200 +strength 11 +reproductiveEnergy 200 +maturityAge 30 +gestation 10 +reproductionRate 1 diff --git a/log_run b/log_run new file mode 100755 index 0000000..bb98de6 --- /dev/null +++ b/log_run @@ -0,0 +1,13 @@ +#!/bin/bash +# Run Ecologia during development +# Automatically analyses the log file + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --config ../../ecologia.config +cp ecologia.log ../../analysis +cd ../../analysis/demography +./population.py --table --logfile ../ecologia.log +R --slave -f population.r +mv populations-*.jpg ../../data diff --git a/release.py b/release.py index c610a24..d7e6a0c 100755 --- a/release.py +++ b/release.py @@ -23,8 +23,9 @@ main_file = open("src/main/Ecologia.java") main_source = main_file.read() main_file.close() + #This string surgery is a little iffy, but it works version_start = main_source.find("version = ")+11 - version_end = main_source.find('"', version_start) + version_end = main_source.find(' ', version_start) return main_source[version_start:version_end] ''' diff --git a/run b/run new file mode 100755 index 0000000..9de1ff8 --- /dev/null +++ b/run @@ -0,0 +1,7 @@ +#!/bin/bash +# Run Ecologia during development + +rm -r releases/ecologia-2.0 +./release.py --no-javadoc --no-sign +cd releases/ecologia-2.0 +java -jar ecologia-2.0.jar --logging --verbose --debug --config ../../ecologia.config diff --git a/src/controller/World.java b/src/controller/World.java index 4acb5f3..68088e3 100755 --- a/src/controller/World.java +++ b/src/controller/World.java @@ -25,7 +25,8 @@ private static World world; //The Singleton instance of this class //Parameter variables - private int[] size; //The size of the world (x*y) + //TODO Reduce these to a single hashmap + /*private int[] size; //The size of the world (x*y) private int timelapse; //When running, the simulation will be updated every so many milliseconds. private int stopAt; //The simulation will stop once this update is reached. private int autorun; //The number of updates the simulation will run for automatically before quitting @@ -34,6 +35,9 @@ private int waterTiles; //The number of water tiles that will be created. private int startNoCarnivores, startNoHerbivores; //The starting number of carnivores/herbivores. private int startEnergyCarnivores, startEnergyHerbivores; //The starting energy for carnivores/herbivores. + */ + + private HashMap parameters; //Runtime variables private boolean running; //Is the simulation running? @@ -51,7 +55,7 @@ private World() { //Parameter settings - size = new int[] {100, 100}; //Default: 100*100 + /*size = new int[] {100, 100}; //Default: 100*100 timelapse = 100; //Default: 300 - can be changed at run-time stopAt = 200; //Default: 100 - can be changed at run-time autorun = -1; //Default: -1 (off) @@ -61,7 +65,21 @@ startNoCarnivores = 50; //Default: 50 - Hypothetical ideal: 5 (?) startNoHerbivores = 200; //Default: 200 - Hypothetical ideal: 160 (?) startEnergyCarnivores = 150; //Default: 150 - startEnergyHerbivores = 100; //Default: 100 + startEnergyHerbivores = 100; //Default: 100 */ + parameters = new HashMap(); + + parameters.put("xsize", 100); + parameters.put("ysize", 100); + parameters.put("timelapse", 100); + parameters.put("stopAt", 200); + parameters.put("autorun", -1); + parameters.put("waterTiles", 10); + parameters.put("humidity", 1); + parameters.put("startGrassDensity", 100); + parameters.put("startNoCarnivores", 50); + parameters.put("startNoHerbivores", 200); + parameters.put("startEnergyCarnivores", 150); + parameters.put("startEnergyHerbivores", 100); reset(); //Runtime variables } @@ -114,7 +132,7 @@ if (line.startsWith("[") && line.endsWith("]")) section = line; //Deal with world variables else if (section.equals("[world]")) { - switch (var) { + /*switch (var) { case "width": size[0] = value; break; case "height": size[1] = value; break; case "timelapse": timelapse = value; break; @@ -128,7 +146,8 @@ case "startEnergyHerbivores": startEnergyHerbivores = value; break; case "startEnergyCarnivores": startEnergyCarnivores = value; break; default: EcologiaIO.error("Invalid config variable in the [world] section: "+var); - } + }*/ + setParam(var, value); } //Configure default animal genomes else if (section.equals("[herbivore]")) { @@ -164,7 +183,7 @@ herbivoreCounter = 0; carnivoreCounter = 0; highestGeneration = 1; - averageGrassDensity = startGrassDensity; + averageGrassDensity = parameters.get("startGrassDensity"); animals = null; news = new ArrayList(); } @@ -244,6 +263,33 @@ if (type == OccupantType.HERBIVORE) Herbivore.defaultGenome = genome; else if (type == OccupantType.CARNIVORE) Carnivore.defaultGenome = genome; } + + /** + * Return a parameter value. + */ + public int getParam(String param) + { + if (parameters.containsKey(param)) + return parameters.get(param); + else { + EcologiaIO.error("getParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + return -1; //Will never be reached, but is syntactically needed + } + } + + /** + * Set a parameter value. + */ + public void setParam(String param, int value) + { + //XXX Is this check necessary here? + if (parameters.containsKey(param)) + parameters.put(param, value); + else EcologiaIO.error("setParam: invalid parameter "+param, EcologiaIO.FATAL_ERROR); + } + + +/* public int[] getSize() { @@ -358,6 +404,8 @@ this.startEnergyHerbivores = startEnergyHerbivores; } +*/ + public int getHerbivoreCount() { return herbivoreCounter; diff --git a/src/main/EcoTest.java b/src/main/EcoTest.java index 6d456d4..240f503 100755 --- a/src/main/EcoTest.java +++ b/src/main/EcoTest.java @@ -9,6 +9,8 @@ * (below) is set to true. * * TODO: expand this into a proper testing framework? + * + * THIS IS OUTDATED - DO NOT USE WITHOUT MAJOR MODIFICATIONS * * @author Daniel Vedder * @date 24.12.2014 @@ -45,13 +47,13 @@ if (turn == 1) { EcologiaIO.debug("Ecotest: Creating a herbivore at (1, 0)"); Herbivore herbivore = new Herbivore(World.getInstance().getNextID(), - Herbivore.defaultGenome, -1, 1, 0, - World.getInstance().getStartEnergyHerbivores(), 0); + Herbivore.defaultGenome, -1, 1, 0, + World.getInstance().getParam("startEnergyHerbivores"), 0); Simulator.addAnimal(herbivore); EcologiaIO.debug("Ecotest: Creating a carnivore at (1, 3)"); Carnivore carnivore = new Carnivore(World.getInstance().getNextID(), - Carnivore.defaultGenome, -1, 1, 3, - World.getInstance().getStartEnergyCarnivores(), 0); + Carnivore.defaultGenome, -1, 1, 3, + World.getInstance().getParam("startEnergyCarnivores"), 0); Simulator.addAnimal(carnivore); } if (turn == 2) { @@ -98,8 +100,8 @@ tpcY = 0; EcologiaIO.debug("Creating a carnivore at (0, 0)"); Carnivore carnivore = new Carnivore(World.getInstance().getNextID(), - Carnivore.defaultGenome, -1, tpcX, tpcY, - World.getInstance().getStartEnergyCarnivores(), 0); + Carnivore.defaultGenome, -1, tpcX, tpcY, + World.getInstance().getParam("startEnergyCarnivores"), 0); Simulator.addAnimal(carnivore); } else if (turn < 5 && turn > 1) { diff --git a/src/main/Ecologia.java b/src/main/Ecologia.java index 4d1ed75..cd308c0 100755 --- a/src/main/Ecologia.java +++ b/src/main/Ecologia.java @@ -5,8 +5,9 @@ import model.Simulator; /** - * Ecologia is a relatively simple ecosystem simulator, designed to show basic - * relationships between predators, prey and producers. + * This is an individual-based model of the ecology of the saiga antelope + * (Saiga tatarica tatarica). The source code was forked from the Ecologia + * ecosystem simulator, version 1.1 (www.launchpad.net/ecologia). * * Copyright (C) 2014-2016 Daniel Vedder * @@ -28,18 +29,18 @@ * manager, updating the program each round. * * @author Daniel Vedder - * @version 28.8.2014 + * @version 20.12.2016 */ public class Ecologia implements Runnable { public static Ecologia eco; //The singleton object - public static final String version = "1.1"; + public static final String version = "2.0 Saiga"; private static boolean noGUI = false; private GUI gui; private Simulator simulator; - private EcoTest tester; + //private EcoTest tester; //XXX Currently not used private Thread runningThread; @@ -73,7 +74,7 @@ else if (a.equals("--no-graphics")) noGUI = true; else if (a.equals("--autorun")) { - World.getInstance().setAutorun(new Integer(args[i+1])); + World.getInstance().setParam("autorun", new Integer(args[i+1])); i++; } else if (a.equals("--config")) { @@ -81,7 +82,7 @@ i++; } else if (a.equals("--timelapse")) { - World.getInstance().setTimelapse(new Integer(args[i+1])); + World.getInstance().setParam("timelapse", new Integer(args[i+1])); i++; } else EcologiaIO.error("Invalid commandline parameter: "+a); @@ -93,7 +94,7 @@ EcologiaIO.printStatus(); //Only use no-graphics mode when on autorun - if (noGUI && (World.getInstance().getAutorun() < 0)) { + if (noGUI && (World.getInstance().getParam("autorun") < 0)) { EcologiaIO.error("Returning to graphics mode as autorun not enabled."); noGUI = false; } @@ -119,9 +120,9 @@ EcologiaIO.log("Launching Ecologia..."); simulator = new Simulator(); if (!noGUI) gui = new GUI(); - tester = new EcoTest(); + //tester = new EcoTest(); EcologiaIO.debug("Launch completed."); - if (World.getInstance().getAutorun() > 0) autorun(); + if (World.getInstance().getParam("autorun") > 0) autorun(); } /** @@ -129,8 +130,8 @@ */ private void autorun() { - EcologiaIO.log("Performing autorun for "+World.getInstance().getAutorun()+" updates."); - World.getInstance().setStopAt(-1); + EcologiaIO.log("Performing autorun for "+World.getInstance().getParam("autorun")+" updates."); + World.getInstance().setParam("stopAt", -1); startThread(); } @@ -138,7 +139,7 @@ * Reset the simulator in order to start a new run. * * XXX: Depending on how long the simulation has already - * been running, this can take quite a long time. + * been running, this can take quite a long time. -- Does it? */ public void reset() { @@ -183,11 +184,11 @@ */ public synchronized void iterate() { - int autorun = World.getInstance().getAutorun(); + int autorun = World.getInstance().getParam("autorun"); World.getInstance().incrementTurn(); int turn = World.getInstance().getTurn(); EcologiaIO.log("Executing update "+turn); - if (EcologiaIO.debugging) tester.runTest(); + //if (EcologiaIO.debugging) tester.runTest(); simulator.update(); EcologiaIO.log("Average grass density: "+World.getInstance().getAverageGrassDensity()+"%"); EcologiaIO.log("Herbivore count: "+World.getInstance().getHerbivoreCount()); @@ -195,7 +196,7 @@ EcologiaIO.log("Generation counter: "+World.getInstance().getGeneration()); //If the stopAt number is reached, pause the simulation - if (World.getInstance().getStopAt() == turn) { + if (World.getInstance().getParam("stopAt") == turn) { World.getInstance().setRunning(false); } if (!noGUI) gui.update(); @@ -213,7 +214,7 @@ } //Pause for as long as the user wants try { - Thread.sleep(World.getInstance().getTimelapse()); + Thread.sleep(World.getInstance().getParam("timelapse")); } catch (InterruptedException ie) {} } @@ -223,7 +224,7 @@ */ private static void printHelp() { - System.out.println("Ecologia "+version+", the simple ecosystem simulator."); + System.out.println("Ecologia "+version+", an individual-based model of saiga ecology."); System.out.println("\nCommandline options:\n"); System.out.println("--help -h Print this help text"); System.out.println("--version -V Print the version number\n"); diff --git a/src/model/Animal.java b/src/model/Animal.java index 2917b85..d5229d4 100755 --- a/src/model/Animal.java +++ b/src/model/Animal.java @@ -23,6 +23,7 @@ * (max value: 2**31) should last for >40,000,000 updates * (based on a 1000 update test run). * => long IDs are not very urgent... + * --> CHECK THIS AGAIN */ protected int IDnumber; //A unique identifier for this animal protected int parent; //The ID number of the parent @@ -205,8 +206,8 @@ ArrayList targets = new ArrayList(); for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int[] newTarget = {xdist, ydist}; targets.add(newTarget); @@ -228,8 +229,8 @@ int minDist = genome.getSight()+1; for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int distance = getDistance(xdist, ydist); if (distance != 0 && distance < minDist) { @@ -255,8 +256,8 @@ int minDist = genome.getSight()+1; for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { - if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getSize()[0] - && ydist < World.getInstance().getSize()[1]) { + if (xdist >= 0 && ydist >= 0 && xdist < World.getInstance().getParam("xsize") + && ydist < World.getInstance().getParam("ysize")) { if (Simulator.getField(xdist, ydist).getOccupant() == type) { int distance = getDistance(xdist, ydist); int[] newTarget = {xdist, ydist}; @@ -292,8 +293,8 @@ case TOP_LEFT: nextY--; nextX--; break; default: EcologiaIO.error("Invalid direction passed to Animal.getNeighbouringField()! ("+dir+") by "+type.toString()+" @"+x+"/"+y); } - if (nextX < 0 || nextX >= World.getInstance().getSize()[0] || - nextY < 0 || nextY >= World.getInstance().getSize()[1]) { + if (nextX < 0 || nextX >= World.getInstance().getParam("xsize") || + nextY < 0 || nextY >= World.getInstance().getParam("ysize")) { return null; } else { diff --git a/src/model/Herbivore.java b/src/model/Herbivore.java index ee40b4e..decbf8a 100755 --- a/src/model/Herbivore.java +++ b/src/model/Herbivore.java @@ -82,7 +82,7 @@ for (int xdist = x-genome.getSight(); xdist < x+genome.getSight(); xdist++) { for (int ydist = y-genome.getSight(); ydist < y+genome.getSight(); ydist++) { if (!(xdist == x && ydist == y) && xdist >= 0 && ydist >= 0 && - xdist < World.getInstance().getSize()[0] && ydist < World.getInstance().getSize()[1] && + xdist < World.getInstance().getParam("xsize") && ydist < World.getInstance().getParam("ysize") && Simulator.getField(xdist, ydist).getGrassDensity() > currentGrassDensity) { Direction d = super.getDirection(xdist, ydist); if (!possibleDirs.contains(d)) possibleDirs.add(d); diff --git a/src/model/Simulator.java b/src/model/Simulator.java index a2d9477..cb9bdea 100755 --- a/src/model/Simulator.java +++ b/src/model/Simulator.java @@ -45,12 +45,12 @@ //Calculate the new grass density on each plot EcologiaIO.debug("Simulator: Recalculating grass density."); double averageDensity = 0; - int xsize = World.getInstance().getSize()[0]; - int ysize = World.getInstance().getSize()[1]; + int xsize = World.getInstance().getParam("xsize"); + int ysize = World.getInstance().getParam("ysize"); for (int x = 0; x < xsize; x++) { for (int y = 0; y < ysize; y++) { if (!map[x][y].nearWater()) { - map[x][y].setLocalHumidity(World.getInstance().getHumidity()); + map[x][y].setLocalHumidity(Humidity.getStatus(World.getInstance().getParam("humidity"))); } map[x][y].calculateGrassDensity(); averageDensity += map[x][y].getGrassDensity(); @@ -105,14 +105,14 @@ private void initMap() { EcologiaIO.debug("Simulator: initialising map."); - int xsize = World.getInstance().getSize()[0]; - int ysize = World.getInstance().getSize()[1]; + int xsize = World.getInstance().getParam("xsize"); + int ysize = World.getInstance().getParam("ysize"); map = new MapField[xsize][ysize]; for (int x = 0; x < xsize; x++) { for (int y = 0; y < ysize; y++) { map[x][y] = new MapField(x, y, OccupantType.NONE, - World.getInstance().getHumidity(), - World.getInstance().getStartGrassDensity()); + Humidity.getStatus(World.getInstance().getParam("humidity")), + World.getInstance().getParam("startGrassDensity")); } } } @@ -123,13 +123,13 @@ private void initWaterTiles() { EcologiaIO.debug("Simulator: initialising water tiles."); - for (int i = 0; i < World.getInstance().getWaterTiles(); i++) { + for (int i = 0; i < World.getInstance().getParam("waterTiles"); i++) { //Each water tile is placed in a random location - int setX = random.nextInt(World.getInstance().getSize()[0]); - int setY = random.nextInt(World.getInstance().getSize()[1]); + int setX = random.nextInt(World.getInstance().getParam("xsize")); + int setY = random.nextInt(World.getInstance().getParam("ysize")); while (map[setX][setY].getOccupant() != OccupantType.NONE) { - setX = random.nextInt(World.getInstance().getSize()[0]); - setY = random.nextInt(World.getInstance().getSize()[1]); + setX = random.nextInt(World.getInstance().getParam("xsize")); + setY = random.nextInt(World.getInstance().getParam("ysize")); } map[setX][setY].setOccupant(OccupantType.WATER); //The fields around each water tile are watered @@ -154,28 +154,28 @@ herbivorePopulation = new ArrayList(); //Create the initial carnivore population, setting each carnivore down at a random position EcologiaIO.debug("Simulator: initialising carnivores."); - for (int j = 0; j < World.getInstance().getStartNoCarnivores(); j++) { - int setXCarnivore = random.nextInt(World.getInstance().getSize()[0]); - int setYCarnivore = random.nextInt(World.getInstance().getSize()[1]); + for (int j = 0; j < World.getInstance().getParam("startNoCarnivores"); j++) { + int setXCarnivore = random.nextInt(World.getInstance().getParam("xsize")); + int setYCarnivore = random.nextInt(World.getInstance().getParam("ysize")); while (map[setXCarnivore][setYCarnivore].getOccupant() != OccupantType.NONE) { - setXCarnivore = random.nextInt(World.getInstance().getSize()[0]); - setYCarnivore = random.nextInt(World.getInstance().getSize()[1]); + setXCarnivore = random.nextInt(World.getInstance().getParam("xsize")); + setYCarnivore = random.nextInt(World.getInstance().getParam("ysize")); } - int startEnergyCarnivores = World.getInstance().getStartEnergyCarnivores(); + int startEnergyCarnivores = World.getInstance().getParam("startEnergyCarnivores"); carnivorePopulation.add(new Carnivore(World.getInstance().getNextID(), Carnivore.defaultGenome, 1, setXCarnivore, setYCarnivore, startEnergyCarnivores, 0)); } //Create the initial herbivore population, setting each herbivore down at a random position EcologiaIO.debug("Simulator: initialising herbivores."); - for (int i = 0; i < World.getInstance().getStartNoHerbivores(); i++) { - int setXHerbivore = random.nextInt(World.getInstance().getSize()[0]); - int setYHerbivore = random.nextInt(World.getInstance().getSize()[1]); + for (int i = 0; i < World.getInstance().getParam("startNoHerbivores"); i++) { + int setXHerbivore = random.nextInt(World.getInstance().getParam("xsize")); + int setYHerbivore = random.nextInt(World.getInstance().getParam("ysize")); while (map[setXHerbivore][setYHerbivore].getOccupant() != OccupantType.NONE) { - setXHerbivore = random.nextInt(World.getInstance().getSize()[0]); - setYHerbivore = random.nextInt(World.getInstance().getSize()[1]); + setXHerbivore = random.nextInt(World.getInstance().getParam("xsize")); + setYHerbivore = random.nextInt(World.getInstance().getParam("ysize")); } - int startEnergyHerbivores = World.getInstance().getStartEnergyHerbivores(); + int startEnergyHerbivores = World.getInstance().getParam("startEnergyHerbivores"); herbivorePopulation.add(new Herbivore(World.getInstance().getNextID(), Herbivore.defaultGenome, 1, setXHerbivore, setYHerbivore, startEnergyHerbivores, 0)); diff --git a/src/view/Display.java b/src/view/Display.java index 29f5e73..f5b61d1 100755 --- a/src/view/Display.java +++ b/src/view/Display.java @@ -104,8 +104,8 @@ public void mouseClicked(MouseEvent click) { int fieldX = click.getX()/20; int fieldY = click.getY()/20; - if (fieldX >= 0 && fieldX < World.getInstance().getSize()[0] && fieldY >= 0 - && fieldY < World.getInstance().getSize()[1]) { + if (fieldX >= 0 && fieldX < World.getInstance().getParam("xsize") && fieldY >= 0 + && fieldY < World.getInstance().getParam("ysize")) { infobox.show(click.getX()/20, click.getY()/20); } } diff --git a/src/view/GUI.java b/src/view/GUI.java index 9712106..c69dbf3 100755 --- a/src/view/GUI.java +++ b/src/view/GUI.java @@ -45,7 +45,7 @@ public GUI() { EcologiaIO.debug("Creating GUI"); - this.setTitle("Ecologia"); + this.setTitle("Ecologia "+Ecologia.version); this.setSize(1000, 560); this.setDefaultCloseOperation(EXIT_ON_CLOSE); createMenu(); @@ -75,16 +75,16 @@ else run.setText("Start"); //Update the humidity from the combo box Humidity setHumidity = Humidity.fromString((String) humidityChooser.getSelectedItem()); - if (setHumidity != World.getInstance().getHumidity()) { - World.getInstance().setHumidity(setHumidity); + if (setHumidity.getValue() != World.getInstance().getParam("humidity")) { + World.getInstance().setParam("humidity", setHumidity.getValue()); EcologiaIO.log("Humidity set to "+setHumidity.getString()); } //Update the simulation speed from the speed slider int setSpeed = speedSlider.getMaximum() - speedSlider.getValue(); - World.getInstance().setTimelapse(setSpeed); + World.getInstance().setParam("timelapse", setSpeed); //Update the stopAt variable from user input try { - World.getInstance().setStopAt(Integer.parseInt((stopAtField.getText()))); + World.getInstance().setParam("stopAt", Integer.parseInt((stopAtField.getText()))); } catch (NumberFormatException nfe) {} //Update the various counters @@ -93,7 +93,7 @@ carnivore_counter.setText("Carnivores: "+ World.getInstance().getCarnivoreCount()); generation_counter.setText("Generations: "+World.getInstance().getGeneration()); grass_counter.setText("Grass density: "+World.getInstance().getAverageGrassDensity()); - humidityChooser.setSelectedItem(World.getInstance().getHumidity().getString()); + humidityChooser.setSelectedItem(Humidity.getStatus(World.getInstance().getParam("humidity")).getString()); } /** @@ -251,7 +251,7 @@ {Humidity.SATURATION.getString(), Humidity.WET.getString(), Humidity.DRY.getString(), Humidity.DROUGHT.getString(), Humidity.SEVERE_DROUGHT.getString()}); humidityChooser.setMaximumSize(new Dimension(140, 30)); - humidityChooser.setSelectedItem(World.getInstance().getHumidity().getString()); + humidityChooser.setSelectedItem(Humidity.getStatus(World.getInstance().getParam("humidity")).getString()); hum_panel.add(humidity); hum_panel.add(humidityChooser); information.add(hum_panel); @@ -292,7 +292,7 @@ //Add the simulation speed slider information.add(new JLabel("Simulation speed:")); information.add(Box.createVerticalStrut(3)); - speedSlider = new JSlider(0, 1500, 1500-World.getInstance().getTimelapse()); + speedSlider = new JSlider(0, 1500, 1500-World.getInstance().getParam("timelapse")); speedSlider.setMajorTickSpacing(300); speedSlider.setMinorTickSpacing(50); speedSlider.setPaintTicks(true); @@ -304,7 +304,7 @@ JLabel stopLabel = new JLabel("Pause at update:"); stopAtField = new JTextField(5); stopAtField.setMaximumSize(stopAtField.getPreferredSize()); - stopAtField.setText(Integer.toString(World.getInstance().getStopAt())); + stopAtField.setText(Integer.toString(World.getInstance().getParam("stopAt"))); stopPanel.add(Box.createVerticalStrut(3)); stopPanel.add(stopLabel); stopPanel.add(Box.createVerticalStrut(1)); @@ -324,7 +324,8 @@ */ private void addDisplay() { - display = new Display(World.getInstance().getSize()); + display = new Display(new int[] {World.getInstance().getParam("xsize"), + World.getInstance().getParam("ysize")}); scrollscreen = new JScrollPane(display, JScrollPane. VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); this.add(scrollscreen, BorderLayout.CENTER); diff --git a/src/view/SimulationConfig.java b/src/view/SimulationConfig.java index 05566bd..7b7df89 100755 --- a/src/view/SimulationConfig.java +++ b/src/view/SimulationConfig.java @@ -46,8 +46,8 @@ heading = new JLabel("Initial Parameter Settings"); //Dimension settings dimensions = new JLabel("World dimensions (x*y): "); - width = new JTextField(String.valueOf(World.getInstance().getSize()[0]), 4); - height = new JTextField(String.valueOf(World.getInstance().getSize()[1]), 4); + width = new JTextField(String.valueOf(World.getInstance().getParam("xsize")), 4); + height = new JTextField(String.valueOf(World.getInstance().getParam("ysize")), 4); Box dimBox = new Box(BoxLayout.X_AXIS); dimBox.add(dimensions); dimBox.add(Box.createHorizontalStrut(15)); @@ -57,38 +57,38 @@ //Initial numbers of animals and water tiles Box waterBox = new Box(BoxLayout.X_AXIS); waterLabel = new JLabel("Number of water tiles: "); - no_water_tiles = new JTextField(String.valueOf(World.getInstance().getWaterTiles()), 3); + no_water_tiles = new JTextField(String.valueOf(World.getInstance().getParam("waterTiles")), 3); waterBox.add(waterLabel); waterBox.add(Box.createHorizontalStrut(30)); waterBox.add(no_water_tiles); Box grassBox = new Box(BoxLayout.X_AXIS); grassLabel = new JLabel("Starting grass density: "); - grassDensity = new JTextField(String.valueOf(World.getInstance().getStartGrassDensity()), 4); + grassDensity = new JTextField(String.valueOf(World.getInstance().getParam("startGrassDensity")), 4); grassBox.add(grassLabel); grassBox.add(Box.createHorizontalStrut(25)); grassBox.add(grassDensity); Box nCarnBox = new Box(BoxLayout.X_AXIS); nCarnLabel = new JLabel("Number of carnivores: "); - no_carnivores = new JTextField(String.valueOf(World.getInstance().getStartNoCarnivores()), 3); + no_carnivores = new JTextField(String.valueOf(World.getInstance().getParam("startNoCarnivores")), 3); nCarnBox.add(nCarnLabel); nCarnBox.add(Box.createHorizontalStrut(25)); nCarnBox.add(no_carnivores); Box nHerbBox = new Box(BoxLayout.X_AXIS); nHerbLabel = new JLabel("Number of herbivores: "); - no_herbivores = new JTextField(String.valueOf(World.getInstance().getStartNoHerbivores()), 3); + no_herbivores = new JTextField(String.valueOf(World.getInstance().getParam("startNoHerbivores")), 3); nHerbBox.add(nHerbLabel); nHerbBox.add(Box.createHorizontalStrut(25)); nHerbBox.add(no_herbivores); //Initial energy for the animals Box energyCarnBox = new Box(BoxLayout.X_AXIS); energyCarnLabel = new JLabel("Start energy carnivores: "); - energyCarnivores = new JTextField(String.valueOf(World.getInstance().getStartEnergyCarnivores()), 4); + energyCarnivores = new JTextField(String.valueOf(World.getInstance().getParam("startEnergyCarnivores")), 4); energyCarnBox.add(energyCarnLabel); energyCarnBox.add(Box.createHorizontalStrut(25)); energyCarnBox.add(energyCarnivores); Box energyHerbBox = new Box(BoxLayout.X_AXIS); energyHerbLabel = new JLabel("Start energy herbivores: "); - energyHerbivores = new JTextField(String.valueOf(World.getInstance().getStartEnergyHerbivores()), 4); + energyHerbivores = new JTextField(String.valueOf(World.getInstance().getParam("startEnergyHerbivores")), 4); energyHerbBox.add(energyHerbLabel); energyHerbBox.add(Box.createHorizontalStrut(25)); energyHerbBox.add(energyHerbivores); @@ -98,12 +98,14 @@ { public void actionPerformed(ActionEvent e) { - EcologiaIO.log("SimulationConfig: World parameter settings updated."); if (showRestartDialog) { int restart = JOptionPane.showConfirmDialog(null, "Please note: The new settings will only take \neffect on the next run.\nRestart now?", "Restart?", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); - if (restart != JOptionPane.CANCEL_OPTION) updateWorld(); + if (restart != JOptionPane.CANCEL_OPTION) { + updateWorld(); + EcologiaIO.log("SimulationConfig: World parameter settings updated."); + } if (restart == JOptionPane.YES_OPTION) Ecologia.getInstance().reset(); } setVisible(false); @@ -153,14 +155,14 @@ */ public void refresh() { - width.setText(String.valueOf(World.getInstance().getSize()[0])); - height.setText(String.valueOf(World.getInstance().getSize()[1])); - grassDensity.setText(String.valueOf(World.getInstance().getStartGrassDensity())); - no_water_tiles.setText(String.valueOf(World.getInstance().getWaterTiles())); - no_carnivores.setText(String.valueOf(World.getInstance().getStartNoCarnivores())); - no_herbivores.setText(String.valueOf(World.getInstance().getStartNoHerbivores())); - energyCarnivores.setText(String.valueOf(World.getInstance().getStartEnergyCarnivores())); - energyHerbivores.setText(String.valueOf(World.getInstance().getStartEnergyHerbivores())); + width.setText(String.valueOf(World.getInstance().getParam("xsize"))); + height.setText(String.valueOf(World.getInstance().getParam("ysize"))); + grassDensity.setText(String.valueOf(World.getInstance().getParam("startGrassDensity"))); + no_water_tiles.setText(String.valueOf(World.getInstance().getParam("waterTiles"))); + no_carnivores.setText(String.valueOf(World.getInstance().getParam("startNoCarnivores"))); + no_herbivores.setText(String.valueOf(World.getInstance().getParam("startNoHerbivores"))); + energyCarnivores.setText(String.valueOf(World.getInstance().getParam("startEnergyCarnivores"))); + energyHerbivores.setText(String.valueOf(World.getInstance().getParam("startEnergyHerbivores"))); } /** @@ -168,12 +170,13 @@ */ public void updateWorld() { - World.getInstance().setSize(new int[] {new Integer(width.getText()), new Integer(height.getText())}); - World.getInstance().setStartGrassDensity(new Integer(grassDensity.getText())); - World.getInstance().setStartNoWaterTiles(new Integer(no_water_tiles.getText())); - World.getInstance().setStartNoHerbivores(new Integer(no_herbivores.getText())); - World.getInstance().setStartNoCarnivores(new Integer(no_carnivores.getText())); - World.getInstance().setStartEnergyCarnivores(new Integer(energyCarnivores.getText())); - World.getInstance().setStartEnergyHerbivores(new Integer(energyHerbivores.getText())); + World.getInstance().setParam("xsize", new Integer(width.getText())); + World.getInstance().setParam("ysize", new Integer(height.getText())); + World.getInstance().setParam("startGrassDensity", new Integer(grassDensity.getText())); + World.getInstance().setParam("waterTiles", new Integer(no_water_tiles.getText())); + World.getInstance().setParam("startNoHerbivores", new Integer(no_herbivores.getText())); + World.getInstance().setParam("startNoCarnivores", new Integer(no_carnivores.getText())); + World.getInstance().setParam("startEnergyCarnivores", new Integer(energyCarnivores.getText())); + World.getInstance().setParam("startEnergyHerbivores", new Integer(energyHerbivores.getText())); } }