Algorithmic Sketchbook Studio AIR Femke Lokhorst 846049 November 2016
2
INDEX
4-25 Images 4 Systems 6 Spread 12 Emerging behaviour 14 Type related Behaviour 22 Site related behaviour 26-33 Code 26 Spread 28 Spawning 30 Evolution 32 Human Interaction 34 Personal Reflection
3
4
Exploring System Behaviours
BINARY
NON BINARY
Simple Forest Fire rules
F F
Rock Paper Scissors rules
S
Tree
F Fire
R
P
R Rock P Paper S Scissors
W1 W3 Tree Stress W1 Weed type W3 Weed type
Corporation Creek rules
5
6 ‘‘The Ring of Fire’’ Complex Cell SpreadBehaviour
blablablan
7
8
9
10
11
12
Emerged Behaviour Outcome of the same system
13
14
B
F
S
H
Weed evolution
Weed interaction
15
16
Blackberry Fennel
Stinkwort
Stinwort Hawthorn
Hawthorn
17
18 Final stage of the system
Sequences overlayed
19
20 BLACKBERRY
3
5
7
10
13
FENNEL
STINKWORT
HAWTHORN
Sequences of growth in the same system
21
22
Interaction with the environment
Blackberry
Fennel
Stinkwort
Hawthorn
Hawthorn is likely to spawn in places with high human activity. On cells that have this property, the chance is higher that the weed
Blackberry Is likely to spawn in
will spread to this neighbouring cell.
places with high moisture. The closer the cell is to the river, the higher the chance is that the weed will spread to a neighbouring cell.
Stinkwort Is likely to spawn in places with impermeable surfaces. On cells that have this property, the chance is higher that the weed will spread to this neighbouring cell.
Fennel Is likely to spawn in places with high High Human Activity & Impermeable Surfaces
moisture. The closer the cell is to the river, the
High Vegetation
higher the chance is that the weed will spread
Water
to a neighbouring cell.
23
24
B
F
S
H
Spawningbehaviour
Spreadbehaviour
25
public static void TestingRules(HexCell cell, List<HexDirection>[] Neighbours, List<int>[] Neighbour { tempGridIndex = HexCoordinates.iFh (cell.coordinates); //tempIntensity = CSV.getIntensity (SingleMeshRender._intensityMap, tempGridIndex); float elevation = CSV.getIntensity (CSVmaps.Elevation, tempGridIndex); float hydro = CSV.getIntensity (CSVmaps.Hydrology, tempGridIndex); float vegetation = CSV.getIntensity (CSVmaps.Vegetation, tempGridIndex); float humanactivity = CSV.getIntensity (CSVmaps.humanactivity, tempGridIndex);
26
SPREAD
Trees obtain stress when // Use a tempory variable which will vary when this cell is different they//have a neighbourand thatatis a location of different intensity values. cororations a weed. The amount of strees populatevegpercent = _populatevegpercent; stressdecreaseamount = _StressDecreaseAmount; received depends on the type stressincreaseamount of weed closest to the tree = _StressIncreaseAmount; stressincreasepercentage = _stressincreasepercentage; stressspreadpercent = _stressspreadpercentage; StressSpreadDecay = _StressSpreadDecay; StressMinWeedAge = _StressMinWeedAge; WeedGerminationAge = _WeedGerminationAge; weedspreadpercent = _weedspreadpercent; RandomWeedGeminationPercentage = _RandomWeedGeminationPercentage; healthincreasepercent = _healthincreasepercent; weedmaxhealthchange = _weedmaxhealthchange;
if (cell.type == CorpType.Tree && LevelNeighbourHealth [1].Count > 0) { maxNumber = LevelNeighbourHealth [1].Max (); indexOfMax=LevelNeighbourHealth[1].FindIndex(x=>x==maxNumber); newCellType= LevelNeighbourPos [1] [indexOfMax]; newWeedType = cell.GetNeighbor (newCellType).type;
if (CorpType.Blackberry == newWeedType) { weedspreadpercent= (21*CSV.getIntensity(CSVmaps.Hydrology,tempGridIndex)/2); stressincreasepercentage = 2; }
if (CorpType.Fennel == newWeedType) { The amount of strees that weedspreadpercent = 3 * CSV.getIntensity(CSVmaps.Hydrology, tempGridIn stressincreasepercentage = 30; a weed produces depends } on the location of the cell, in
terms of moisture, elevation,
if (CorpType.Stinkwort == newWeedType) or human activity-level. { weedspreadpercent = 6 * (CSV.getIntensity(CSVmaps.Vegetation, tempGrid stressincreasepercentage = 8; } if (CorpType.Hawthorn == newWeedType) { if(CSV.getIntensity(CSVmaps.humanactivity,tempGridIndex)==1) { weedspreadpercent = 4; } else { weedspreadpercent = 2; } } }
}
stressincreasepercentage = 20;
rhealth, List<int>[] LevelNeighbourHealth, List<HexDirection>[] LevelNeighbourPos, List<HexDirection
if (CorpType.Blackberry == cell.type) { weedmaxhealthchange = 7; healthincreasepercent = 100; } The chance of a weed to spread to cells occupied by trees if (CorpType.Fennel == cell.type) { depends on a number of variables. One of them can vary weedmaxhealthchange = 15; on the location of the weed within the map. Other healthincreasepercent depending = 35; events can also change the chance of spread for any specificic } if (CorpType.Stinkwort == cell.type) { weed, for instance the interaction of the player. weedmaxhealthchange = 3; healthincreasepercent = 50; } if (CorpType.Hawthorn == cell.type) { weedmaxhealthchange = 20; healthincreasepercent = 65; } if (CSV.SpecialRules.ContainsKey(tempGridIndex) && CSV.SpecialRules[tempGridIndex] == -1){ healthincreasepercent = 0; } if (cell.type == CorpType.Empty && CSV.getIntensity(CSVmaps.River, tempGridIndex) < 1 && (num.Next(0, 10000) < (int)(populatevegpercent * 100))){ cell.changeType(CorpType.Tree, 1); CSV.lowRender[tempGridIndex] = 0; } else if (cell.type == CorpType.Tree) { if (CSV.SpecialRules.ContainsKey (tempGridIndex) && CSV.SpecialRules [tempGridIndex] == 1 && Neighbours [6].Count > 0) { maxNumber = Neighbourhealth [6].Max (); indexOfMax = Neighbourhealth [6].FindIndex (x => x == maxNumber); newCellType = Neighbours [6] [indexOfMax]; newType = cell.GetNeighbor (newCellType).type; cell.changeType (newType, 100); if (num.Next(0,10000) < MultiSizeSystem._PercentageChanceForLowPoly * 100f){ CSV.lowRender[tempGridIndex] = 1; ndex); } } else if (neighbourSize.Contains(4)){ indexOfMax = neighbourSize.FindIndex(x => x == 4); newCellType = sizeNeighboursPos[indexOfMax]; newType = cell.GetNeighbor(newCellType).type; dIndex)); cell.changeType(newType, 100); if (num.Next(0, 10000) < MultiSizeSystem._PercentageChanceForLowPoly * 100f){ CSV.lowRender[tempGridIndex] = 1; } } else if (LevelNeighbourHealth [1].Count > 0 && LevelNeighbourHealth [1].Max () > WeedGerminationAge && (num.Next (0, 10000) < (int)(weedspreadpercent * 100))){ cell.changeType (newWeedType, 100); if (num.Next(0, 10000) < MultiSizeSystem._PercentageChanceForLowPoly * 100f){ CSV.lowRender[tempGridIndex] = 1; } }
Trees that have a neighbour that is weed-type have the chance to become a weed themselves. The likelihood for the tree to become a weed depends on the type of the weed and the location of the tree.
27
28
SPAWNING
if (cell.type == CorpType.Empty && (num.Next(0, 10000) && < (int)(populatevegpercent * 100)) && CSV.getIntensity(CSVmaps.River, tempGridIndex) < 1
{ cell.changeType(CorpType.Tree, 1); }
else if (LevelNeighbourHealth [1].Count > 0 && LevelNeighbourHealth [1].Max () > WeedGer && (num.Next (0, 10000) < (int)(weedspreadpercent * 100))) { cell.changeType (newWeedType, 100); } else if (num.Next (0, 100000000) < (int)(RandomWeedGeminationPercentage * 1000000)) { SpawnableWeeds.Clear (); for (int i = 0; i < 4; i++) { if (CAManager._typeCount [2 + i] < 10) { SpawnableWeeds.Add (2 + i); //CAManager._typeCount [2 + i] += 10; } } if (SpawnableWeeds.Count > 0) { CorpType newWeedType = (CorpType)SpawnableWeeds [num.Next (0, Spawnabl if (_allweeds) {
if (newWeedType == CorpType.Blackberry && CSV.getIntensity (CSVm cell.changeType (newWeedType, 100); } if (newWeedType == CorpType.Fennel && CSV.getIntensity (CSVmaps. This is a bool created for th cell.changeType (newWeedType, 100); run with either two or four W } if (newWeedType == CorpType.Stinkwort && CSV.getIntensity (CSVma
cell.changeType (newWeedType, 100); } if (newWeedType == CorpType.Hawthorn && CSV.getIntensity (CSVmap
This determines the likelyhood of a Weed (in this case, the Fennel) to randomly populate cell.changeType (newWeedType, 100); a Tree according to site information (in this case hydrology).
} } else { if (newWeedType == CorpType.Fennel && CSV.getIntensity (CSVmaps. { cell.changeType (newWeedType, 100); } if (newWeedType == CorpType.Hawthorn && CSV.getIntensity (CSVmap cell.changeType (newWeedType, 100); } } } }
))
This part makes sure Trees populate according to a percentage of change and that they wonâ&#x20AC;&#x2122;t go into the water.
rminationAge
This part gives Trees that are neighbouring any type of Weed have a percentage change to also become that Weed.
This section is about randomly germinating Weeds onto Trees.
leWeeds.Count)];
maps.Hydrology, tempGridIndex) < 0.5 ) {
.Hydrology, tempGridIndex) < 0.5 ) {
he fabrication team to make the engine Weeds.
aps.Vegetation, tempGridIndex) < 0.5 ) {
ps.humanactivity, tempGridIndex) > 0.300519 ) {
.Hydrology, tempGridIndex) < 0.3
)
ps.humanactivity, tempGridIndex) > 0.300519 ) {
29
public static void MakeLarger(HexCell cell, int gridIndex, List<HexDirection>[] Neighbours, List< { if (cell.health > 200) { if (cell.NewSize < 0 && cell.type > CorpType.Tree && Neighbours[(int)cell.type].Count == 6 && hiddenNeighbours.Count == 0 && num.Next(0, 10000) < 3000){ cell.NewSize = 1; CAManager.CellSizes[cell.NewSize].Add(gridIndex); cell.largeIndex = gridIndex; } else if (cell.size < 2 && CAManager.CellSizes[2].Count() < _maxNumberSize2 && cell.type>CorpType.Tree && CAManager._typeCount[(int)cell.type]>_numberOfWeedsToCreateSize2 && num.Next(0, 1000) < 100){ HexCell.makeLarge(cell, gridIndex, 2); } else if (cell.size < 3 && CAManager.CellSizes[3].Count() < _maxNumberSize3 && cell.type > CorpType.Tree && CAManager._typeCount[(int)cell.type] > _numberOfWeedsToCreateSize3 && num.Next(0, 1000) HexCell.makeLarge(cell, gridIndex, 3); } else if (cell.size < 4 && cell.health > 300 && CAManager.CellSizes[4].Count() < _maxNumberSize4 && cell.type > CorpType.Tree && was built so that there is a maximum>number of The system CAManager._typeCount[(int)cell.type] _numberOfWeedsToCreateSize4 && This is a measure weeds. num.Next(0, 1000) to < control 100){ the system so it does not HexCell.makeLarge(cell, gridIndex, 4); grow infinitely. Once the conditioAns are appropriate the weed } can increase their level according to their health. } } public static void MultiSizeRender(HexCell cell, int gridIndex, Mesh[] meshPerType, Material[] if (cell != null && cell.health > 0 ){ cell.currentPosition = Vector3.Lerp(cell.currentPosition, cell.cellPosition, Time.delta level = cell.health / 100; scaledHealth = (1.0f * (cell.health - (100 * level) + 100) / 100);
30
EVOLUTION
if (cell.type == CorpType.Tree ){ if (cell.health > SuchRulesMuchCoding._StressDecreaseAmount + 10){ voxTransform.SetTRS(new Vector3(cell.currentPosition.x, CSV.getIntensity(SingleMeshRender._intensityMap, gridIndex) +1, cell.currentPosition.z), Quaternion.Euler(0, 0, 0), new Vector3((.3f * cell.health)/20, 0.0001f, (0.3f * cell.health)/20)); Graphics.DrawMesh(meshPerType[0], voxTransform, MaterialsPerType[0], 0, null, 0 } }else if (cell.type > CorpType.Tree && (!cell.isHidden || (cell.size == 4))){ if (cell.type == CorpType.Blackberry){ cell changes<level, their looks change according to the if (cell.size == When 0 && a cell.health 200){ design of the Direction team. This allows to recognise cells voxTransform.SetTRS(new Vector3(cell.currentPosition.x, CSV.getIntensity(SingleMeshRender._intensityMap, gridIndex), cell.currentPo according to their level. Quaternion.Euler(0, 0, 0), new Vector3(1, 1, 1)); if (CSV.lowRender[gridIndex] == 0){ CellMesh = _Corporation1[0]; CellMaterial = _corporationMaterial1[0]; }else{ CellMesh = _LowPolyMesh[0]; CellMaterial = _LowPolyMaterial[0]; } } else if (cell.size == 0 && cell.health < 300){ voxTransform.SetTRS(new Vector3(cell.currentPosition.x, CSV.getIntensity(SingleMeshRender._intensityMap, gridIndex), cell.currentPosition.z),Quaternion.Euler(0,0,0),newVector3(1,1,1)); CellMesh = _Corporation1[1]; CellMaterial = _corporationMaterial1[1]; }
<HexDirection> hiddenNeighbours, System.Random num)
if (CSV.SpecialRules.ContainsKey(tempGridIndex) && CSV.SpecialRules[tempGridIndex] == -2){ CSV.addToIntensityValue (0.6f, CSVmaps.Hydrology, tempGridIndex); } if (CSV.SpecialRules.ContainsKey(tempGridIndex)&& CSV.SpecialRules[tempGridIndex]==-3){ cell.changeType(CorpType.Blackberry, 100); if(num.Next(0,10000)<MultiSizeSystem._PercentageChanceForLowPoly* 100f){ CSV.lowRender[tempGridIndex] = 1; } } if (CSV.SpecialRules.ContainsKey(tempGridIndex) && CSV.SpecialRules[tempGridIndex] == -4){ HexCell.makeLarge(cell, tempGridIndex, 4); } if (cell.size == 4){ if (cell.type == CorpType.Blackberry){ int sizeOfEffect = 5; Once a cell reaches level 4, if (tempGridIndex >= 0 && tempGridIndex < CSV.dataMap.Count){ it turns into a Boss, who will HexCoordinates hexCoor = cell.coordinates; ) < 100){ behave different than normal for (int dx = -sizeOfEffect; dx <= sizeOfEffect; dx++){ cells. for (int dz = -sizeOfEffect; dz <= sizeOfEffect; dz++){ if (Mathf.Abs(dx + dz) <= sizeOfEffect){ int tempIndex = HexCoordinates.iFh(new HexCoordinates(hexCoo if (tempIndex >= 0 && tempIndex < CSV.dataMap.Count{ if (CSV.SpecialRules.ContainsKey(tempIndex)){ CSV.SpecialRules[tempIndex] = -1; } else{ CSV.SpecialRules.Add(tempIndex, -1); } MaterialsPerType){ } } aTime); } } } } if (cell.type == CorpType.Fennel){ int sizeOfEffect = 4; if (tempGridIndex >= 0 && tempGridIndex < CSV.dataMap.Count){ HexCoordinates hexCoor = cell.coordinates; for (int dx = -sizeOfEffect; dx <= sizeOfEffect; dx++){ for (int dz = -sizeOfEffect; dz <= sizeOfEffect; dz++){ 0, block); if (Mathf.Abs(dx + dz) <= sizeOfEffect){ inttempIndex=HexCoordinates.iFh(newHexCoordinates (hexCoor.X + dx, hexCoor.Z + dz)); if (tempIndex>=0&&tempIndex < CSV.dataMap.Count){ if(CSV.SpecialRules.ContainsKey(tempIndex)){ CSV.SpecialRules[tempIndex] = -2; osition.z), } else{ CSV.SpecialRules.Add(tempIndex, -2); } } } } } } } if (cell.type == CorpType.Stinkwort) The { behaviour of the boss depends on its type and its coded so
that this behaviour repeats every turn until the boss cell is killed int sizeOfEffect = 3; // this is how many neighbour rings w by the player. Some of the behaviours include changing the values if (tempGridIndex >= 0 && tempGridIndex < CSV.dataMap.Count of the intensity maps from where the trees and weeds spawn. By { doing this we have created a system that can change itself. //HexCoordinates.hFi(tempGridIndex); // this is how y
31
HexCoordinates hexCoor = cell.coordinates; for (int dx = -sizeOfEffect; dx <= sizeOfEffect; dx++) { for (int dz = -sizeOfEffect; dz <= sizeOfEffect;
if (cell.type == CorpType.Tree && LevelNeighbourHealth [1].Count > 0) { maxNumber = LevelNeighbourHealth [1].Max (); indexOfMax = LevelNeighbourHealth [1].FindIndex (x => x == maxNumber); newCellType = LevelNeighbourPos [1] [indexOfMax]; newWeedType = cell.GetNeighbor (newCellType).type; if (CorpType.Blackberry == newWeedType){ weedspreadpercent = (21 * CSV.getIntensity(CSVmaps.Hydrology, tempGridIndex)/2); stressincreasepercentage = 2; } if (CorpType.Fennel == newWeedType){ weedspreadpercent = 3 * CSV.getIntensity(CSVmaps.Hydrology, tempGridIndex); stressincreasepercentage = 30; } if (CorpType.Stinkwort == newWeedType){ weedspreadpercent = 6 * (CSV.getIntensity(CSVmaps.Vegetation, tempGridIndex)); stressincreasepercentage = 8; } if (CorpType.Hawthorn == newWeedType){ if (CSV.getIntensity(CSVmaps.humanactivity, tempGridIndex) == 1){ weedspreadpercent = 4; } else{ weedspreadpercent = 2; } stressincreasepercentage = 20; } } if (CSV.SpecialRules.ContainsKey(tempGridIndex) && CSV.SpecialRules[tempGridIndex] == 2) { weedspreadpercent = 8; } if (CorpType.Blackberry == cell.type) { weedmaxhealthchange = 7; healthincreasepercent = 100; } if (CorpType.Fennel == cell.type) { weedmaxhealthchange = 15; healthincreasepercent = 35; } if (CorpType.Stinkwort == cell.type) { weedmaxhealthchange = 3; healthincreasepercent = 50; } if (CorpType.Hawthorn == cell.type) { weedmaxhealthchange = 20; healthincreasepercent = 65; } else if (cell.type > CorpType.Tree) { if (num.Next (0, 10000) < (int)(healthincreasepercent * 100)) { cell.health += (num.Next (0, weedmaxhealthchange)); } if (cell.health >= 100 && cell.health < 200) {} else if (cell.health >= 200 && cell.health < 300) {} else if (cell.health >= 300 && cell.health < 400) {} else if (cell.health >= 400 && cell.health < 500) {}
32
HUMAN INTERACTION
if (CSV.SpecialRules.ContainsKey(tempGridIndex) && CSV.SpecialRules[tempGridIndex] == 1){ cell.changeType(CorpType.Tree, 1); CSV.lowRender[tempGridIndex] = 0; } if (CSV.SpecialRules.ContainsKey(tempGridIndex) && CSV.SpecialRules[tempGridIndex] == -2) { CSV.addToIntensityValue (0.6f, CSVmaps.Hydrology, tempGridIndex); }
;
When the player decides to help the weeds in the game, all the weed types surrounding the player will spread quicker and take over larger areas in the site.
When the player helps the trees in the game, the area surrounding the player will be turned into trees regardless of what type or level of the weeds close to the player
33
34
Personal Reflection Within the taskforce of the Dynamics team, I typed a lot of the code as Nicolas and I generally worked on it together using my computer. During meet ups with our tutors I participated in discussion on different ways to establish behaviour in the system. Therefore, the first few weeks my role in the team has been mostly focussed on programming and tuning the engine. When we moved on to making presentable products for our presentation, I worked on explaining the engine we’ve made, interactions between its components and the way it relates to the real world. I would call myself the expert of the simulation. In the journal I’ve made chapter 4 and 6 on complex cells and ecology and I did the introduction and the part of the evaluation on the role of the architect. What I will take away from this studio is a lot of knowledge on using programming as a new tool for Architectural design. Besides a tool it is also a new way of thinking that I’ve assimilated. There is an endless amount of options to achieve things when you’re coding, but you’ll always be limited by the amount of means that you can come up with or find on the internet. What I really like about programming is how open source it is. Every little solution that you find on the internet is not a creative possession of someone. It is just a newly discovered mean to solve a broad range of design specific problems.
35