-
-
Notifications
You must be signed in to change notification settings - Fork 589
Expand file tree
/
Copy pathMigrateSpecies.cs
More file actions
116 lines (89 loc) · 4.03 KB
/
MigrateSpecies.cs
File metadata and controls
116 lines (89 loc) · 4.03 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
namespace AutoEvo;
using System;
using System.Collections.Generic;
using Xoshiro.PRNG32;
/// <summary>
/// Step that generates species migrations for each species
/// </summary>
public class MigrateSpecies : IRunStep
{
private readonly Species species;
private readonly PatchMap map;
private readonly WorldGenerationSettings worldSettings;
private readonly Random random;
private readonly Miche.InsertWorkingMemory insertWorkingMemory = new();
public MigrateSpecies(Species species, PatchMap map, WorldGenerationSettings worldSettings, Random randomSource)
{
this.species = species;
this.map = map;
this.worldSettings = worldSettings;
random = new XoShiRo128starstar(randomSource.NextInt64());
}
public int TotalSteps => 1;
public bool CanRunConcurrently => true;
public bool RunStep(RunResults results, SimulationCache cache)
{
// Player has a separate GUI to control their migrations purposefully so auto-evo doesn't do it automatically
if (species.PlayerSpecies)
return true;
// To limit species migrations to the actual limit, we need to pick random patches to try to migrate from
// to ensure all patches have a chance to eventually send some population
int attemptsLeft = worldSettings.AutoEvoConfiguration.MoveAttemptsPerSpecies;
var sourcePatches = new List<Patch>();
// To prevent generating duplicate migrations this needs to remember target patches. This limitation is a
// data model limitation where a single target patch cannot have a species migrate to it form multiple patches
// at once.
var usedTargets = new HashSet<Patch>();
foreach (var patch in map.Patches.Values)
{
if (!patch.SpeciesInPatch.ContainsKey(species))
continue;
sourcePatches.Add(patch);
}
sourcePatches.Shuffle(random);
// To not randomly pick the same adjacent patch multiple times as a migration target
var shuffledNeighbours = new List<Patch>();
foreach (var patch in sourcePatches)
{
if (attemptsLeft <= 0)
{
// Stop checking once all attempts are used up even if there are still patches this species is in
break;
}
var population = patch.GetSpeciesSimulationPopulation(species);
if (population < Constants.AUTO_EVO_MINIMUM_MOVE_POPULATION)
continue;
// Try all neighbour patches in random order
shuffledNeighbours.Clear();
foreach (var adjacent in patch.Adjacent)
{
shuffledNeighbours.Add(adjacent);
}
// TODO: could prefer patches this species is not already in or about to go extinct, or really anything
// other than random selection
shuffledNeighbours.Shuffle(random);
foreach (var target in shuffledNeighbours)
{
// Skip checking population send to the same patch multiple times
if (usedTargets.Contains(target))
continue;
--attemptsLeft;
var targetMiche = results.GetMicheForPatch(target);
// Calculate random amount of population to send
var moveAmount = (long)random.Next(population * Constants.AUTO_EVO_MINIMUM_MOVE_POPULATION_FRACTION,
population * Constants.AUTO_EVO_MAXIMUM_MOVE_POPULATION_FRACTION);
if (moveAmount > 0 &&
targetMiche.InsertSpecies(species, target, null, cache, true, insertWorkingMemory))
{
results.AddMigrationResultForSpecies(species, new SpeciesMigration(patch, target, moveAmount));
usedTargets.Add(target);
// Only one migration per patch
break;
}
if (attemptsLeft <= 0)
break;
}
}
return true;
}
}