Checkpoint initial implementation
This commit is contained in:
Executable
+1
@@ -0,0 +1 @@
|
||||
source .venv/bin/activate
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
[[ -d .venv ]] && rm -rf .venv
|
||||
python -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
source bin/activate.sh
|
||||
pytest tungston -vv $*
|
||||
Executable
+2
@@ -0,0 +1,2 @@
|
||||
source bin/activate.sh
|
||||
watch -c "pytest -q --color=yes --tb=short tungston"
|
||||
@@ -0,0 +1,8 @@
|
||||
exceptiongroup==1.3.0
|
||||
iniconfig==2.1.0
|
||||
packaging==25.0
|
||||
pluggy==1.6.0
|
||||
Pygments==2.19.2
|
||||
pytest==8.4.2
|
||||
tomli==2.2.1
|
||||
typing_extensions==4.15.0
|
||||
@@ -0,0 +1,74 @@
|
||||
# Class ############################################################################################
|
||||
|
||||
class Altitude:
|
||||
"""
|
||||
A range of world heights from a bottom to a top.
|
||||
"""
|
||||
|
||||
def __init__(self, name:str, bottom:int, top:int):
|
||||
self.name = name
|
||||
self.bottom = bottom
|
||||
self.top = top
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
if type(self) != type(other): return False
|
||||
if self.bottom != other.bottom: return False
|
||||
if self.top != other.top: return False
|
||||
return True
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.bottom, self.top))
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.name} <{self.bottom} to {self.top}>"
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
# Aboveground
|
||||
SKY = Altitude("sky", 284, 316)
|
||||
SUMMIT = Altitude("summit", 252, 284)
|
||||
PEAKS = Altitude("peaks", 222, 252)
|
||||
CRAGS = Altitude("crags", 190, 222)
|
||||
ALPINE = Altitude("alpine", 156, 190)
|
||||
HILLS = Altitude("hills", 124, 156)
|
||||
UPLANDS = Altitude("uplands", 92, 124)
|
||||
LOWLANDS = Altitude("lowlands", 70, 92)
|
||||
DUNES = Altitude("dunes", 62, 70)
|
||||
|
||||
# Transition
|
||||
EVAPORATES = Altitude("evaporates", 60, 64)
|
||||
ANYWHERE = Altitude("anywhere", -64, 320)
|
||||
|
||||
# Underground
|
||||
SOIL = Altitude("soil", 48, 62)
|
||||
SUBSTRATE = Altitude("substrate", 32, 48)
|
||||
OVERBURDEN = Altitude("overburden", 0, 32)
|
||||
CRUST = Altitude("crust", -32, 32)
|
||||
MANTLE = Altitude("mantle", -54, -32)
|
||||
PLUTONIC = Altitude("plutonic", -64, -54)
|
||||
|
||||
# Underwater
|
||||
SURFACE = Altitude("surface", 58, 62)
|
||||
SHALLOWS = Altitude("shallows", 46, 58)
|
||||
DEEPS = Altitude("deeps", 0, 46)
|
||||
ABYSS = Altitude("abyss", -32, 0)
|
||||
|
||||
# Groups
|
||||
OVERGROUND = (DUNES, LOWLANDS, UPLANDS, HILLS, ALPINE, CRAGS, PEAKS, SUMMIT, SKY)
|
||||
UNDERGROUND = (SOIL, SUBSTRATE, OVERBURDEN, CRUST, MANTLE, PLUTONIC)
|
||||
UNDERWATER = (SURFACE, SHALLOWS, DEEPS, ABYSS)
|
||||
|
||||
# Helper Functions #################################################################################
|
||||
|
||||
def span(*altitudes):
|
||||
if not altitudes: return ANYWHERE
|
||||
result = Altitude("", ANYWHERE.top, ANYWHERE.bottom)
|
||||
|
||||
for altitude in altitudes:
|
||||
result.bottom = min(result.bottom, altitude.bottom)
|
||||
result.top = max(result.top, altitude.top)
|
||||
|
||||
return result
|
||||
@@ -0,0 +1,18 @@
|
||||
from pytest import fixture
|
||||
|
||||
import tungston.core.altitude as A
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="merged")
|
||||
def mergeRange():
|
||||
yield A.span(A.UPLANDS, A.LOWLANDS)
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_span(merged):
|
||||
assert merged.bottom == A.LOWLANDS.bottom
|
||||
assert merged.top == A.UPLANDS.top
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
from tungston.core.ecology.biometrait import BiomeTrait
|
||||
from tungston.core.ecology.flora import Flora
|
||||
from tungston.core.ecology.geology import Geology
|
||||
from tungston.core.ecology.heat import Heat
|
||||
from tungston.core.ecology.humidity import Humidity
|
||||
from tungston.core.ecology.soil import Soil
|
||||
from tungston.core.ecology.water import Water
|
||||
|
||||
|
||||
####################################################################################################
|
||||
|
||||
class Biome(object):
|
||||
"""
|
||||
A region of the world defined by a unique combination of geology, topography, and ecology.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
city:str,
|
||||
gameId:str,
|
||||
flora:Flora,
|
||||
geology:Geology,
|
||||
heat:Heat,
|
||||
humidity:Humidity,
|
||||
soil:Soil,
|
||||
water:Water,
|
||||
):
|
||||
self.city = city
|
||||
self.gameId = gameId
|
||||
|
||||
self.flora = flora
|
||||
self.geology = geology
|
||||
self.heat = heat
|
||||
self.humidity = humidity
|
||||
self.soil = soil
|
||||
self.water = water
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
if type(self) != type(other): return False
|
||||
if self.biomeId != other.biomeId: return False
|
||||
if self.city != other.city: return False
|
||||
return True
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.biomeId, self.city))
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.city}<{self.gameId}>"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "".join([str(p) for p in [
|
||||
str(self), "{",
|
||||
repr(self.flora), ", ",
|
||||
repr(self.geology), ", ",
|
||||
repr(self.heat), ", ",
|
||||
repr(self.humidity), ", ",
|
||||
repr(self.soil), ", ",
|
||||
repr(self.water),
|
||||
"}"
|
||||
]])
|
||||
|
||||
def traits(self) -> set[BiomeTrait]:
|
||||
return set([
|
||||
self.flora,
|
||||
self.geology,
|
||||
self.heat,
|
||||
self.humidity,
|
||||
self.soil,
|
||||
self.water,
|
||||
])
|
||||
@@ -0,0 +1,25 @@
|
||||
from pytest import fixture
|
||||
from tungston.core.ecology.biome import Biome
|
||||
|
||||
import tungston.core.ecology.flora as F
|
||||
import tungston.core.ecology.geology as G
|
||||
import tungston.core.ecology.heat as E
|
||||
import tungston.core.ecology.humidity as U
|
||||
import tungston.core.ecology.soil as S
|
||||
import tungston.core.ecology.water as W
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="desert")
|
||||
def createDesert():
|
||||
return Biome(
|
||||
"phoenix", "minecraft:desert",
|
||||
F.BARREN, G.SEDIMENTARY, E.TROPICAL, U.DRY, S.SANDY, W.INLAND
|
||||
)
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_createBiome(desert):
|
||||
assert desert.city == "phoenix"
|
||||
assert desert.soil == S.SANDY
|
||||
@@ -0,0 +1,25 @@
|
||||
from tungston.core.ecology.biome import Biome
|
||||
from tungston.core.ecology.biomefilter import BiomeFilter
|
||||
|
||||
|
||||
# Classes ##########################################################################################
|
||||
|
||||
class BiomeCatalog:
|
||||
"""
|
||||
A comprehensive list of all available biomes in the modpack.
|
||||
"""
|
||||
|
||||
def __init__(self, biomes):
|
||||
self.biomes = sorted(biomes, key=lambda b: b.city)
|
||||
|
||||
def all(self) -> list[Biome]:
|
||||
return list(self.biomes)
|
||||
|
||||
def matching(self, biomeFilter:BiomeFilter) -> list[Biome]:
|
||||
result = []
|
||||
|
||||
for biome in self.biomes:
|
||||
if biomeFilter.accepts(biome):
|
||||
result.append(biome)
|
||||
|
||||
return result
|
||||
@@ -0,0 +1,46 @@
|
||||
from tungston.core.ecology.biome import Biome
|
||||
from tungston.core.ecology.biomecatalog import BiomeCatalog
|
||||
from tungston.core.ecology.biomefilter import BiomeFilter
|
||||
from pytest import fixture
|
||||
|
||||
import tungston.core.ecology.flora as F
|
||||
import tungston.core.ecology.geology as G
|
||||
import tungston.core.ecology.heat as E
|
||||
import tungston.core.ecology.humidity as U
|
||||
import tungston.core.ecology.soil as S
|
||||
import tungston.core.ecology.water as W
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="jungles")
|
||||
def createJungleFilter():
|
||||
yield BiomeFilter([U.WET, E.TROPICAL, F.within(F.CANOPY, F.CLEARING)])
|
||||
|
||||
@fixture(name="catalog")
|
||||
def createCatalog():
|
||||
yield BiomeCatalog([
|
||||
Biome("dallas", "minecraft:savanna",
|
||||
F.FIELD, G.SEDIMENTARY, E.SUBTROPICAL, U.DRY, S.SANDY, W.INLAND
|
||||
),
|
||||
Biome("kansascity", "minecraft:plains",
|
||||
F.FIELD, G.SEDIMENTARY, E.TEMPERATE, U.DAMP, S.LOAMY, W.INLAND
|
||||
),
|
||||
Biome("mobile", "minecraft:swamp",
|
||||
F.CLEARING, G.SEDIMENTARY, E.TEMPERATE, U.WET, S.PEATY, W.SWAMP
|
||||
),
|
||||
Biome("phoenix", "minecraft:desert",
|
||||
F.BARREN, G.SEDIMENTARY, E.TROPICAL, U.DRY, S.SANDY, W.INLAND
|
||||
),
|
||||
Biome("portland", "minecraft:forest",
|
||||
F.FOREST, G.SEDIMENTARY, E.TEMPERATE, U.DAMP, S.LOAMY, W.INLAND
|
||||
),
|
||||
Biome("singapore", "minecraft:jungle",
|
||||
F.CANOPY, G.SEDIMENTARY, E.TROPICAL, U.WET, S.ACIDIC, W.INLAND
|
||||
),
|
||||
])
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_findJungles(catalog, jungles):
|
||||
assert [b.city for b in catalog.matching(jungles)] == ["singapore"]
|
||||
@@ -0,0 +1,57 @@
|
||||
from tungston.core.ecology.biome import Biome
|
||||
from tungston.core.ecology.biometrait import BiomeTrait
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class BiomeFilter:
|
||||
"""
|
||||
A description of which biomes may be considered for a given purpose.
|
||||
|
||||
This class works by allowing *both* allowed and prohibited traits.
|
||||
|
||||
The list of allowed traits may contain either individual trait objects, or a list of such
|
||||
objects. When an individual trait is given, only biomes with that exact trait are permitted.
|
||||
When a list of traits is given, biomes containing any of the listed traits are permitted. To
|
||||
put it another way, each individual item in the required list is an "OR" filter, and the results
|
||||
of the entire list are "AND"ed together.
|
||||
|
||||
The list of prohibited traits only permits individual traits, but multiple traits of the same
|
||||
sort may be given.
|
||||
|
||||
The filter only accepts biomes which pass *both* lists. That is, it must possess *all* the
|
||||
required traits while simultaneously not possessing *any* of the prohibited ones.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
required:list[BiomeTrait|list[BiomeTrait]]=None,
|
||||
prohibited:list[BiomeTrait]=None,
|
||||
):
|
||||
self.required = required or []
|
||||
self.prohibited = prohibited or []
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"BiomeFilter([{repr(self.required)}], [{repr(self.prohibited)}])"
|
||||
|
||||
def accepts(self, biome: Biome) -> bool:
|
||||
biomeTraits = biome.traits()
|
||||
|
||||
for condition in self.required:
|
||||
if isinstance(condition, BiomeTrait):
|
||||
trait = condition
|
||||
if trait not in biomeTraits: return False
|
||||
else:
|
||||
traitOptions = condition
|
||||
foundMatch = False
|
||||
for trait in traitOptions:
|
||||
if trait in biomeTraits:
|
||||
foundMatch = True
|
||||
break
|
||||
|
||||
if not foundMatch: return False
|
||||
|
||||
for prohibitedTrait in self.prohibited:
|
||||
if prohibitedTrait in biomeTraits: return False
|
||||
|
||||
return True
|
||||
@@ -0,0 +1,61 @@
|
||||
from tungston.core.ecology.biome import Biome
|
||||
from tungston.core.ecology.biomefilter import BiomeFilter
|
||||
from pytest import fixture
|
||||
|
||||
import tungston.core.ecology.flora as F
|
||||
import tungston.core.ecology.geology as G
|
||||
import tungston.core.ecology.heat as E
|
||||
import tungston.core.ecology.humidity as U
|
||||
import tungston.core.ecology.soil as S
|
||||
import tungston.core.ecology.water as W
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="barren")
|
||||
def createBarrenFilter():
|
||||
yield BiomeFilter([F.BARREN])
|
||||
|
||||
@fixture(name="barrenNotSandy")
|
||||
def createBarrenNotSandFilter():
|
||||
yield BiomeFilter([F.BARREN], [S.SANDY])
|
||||
|
||||
@fixture(name="desert")
|
||||
def createDesertBiome():
|
||||
yield Biome(
|
||||
"phoenix", "minecraft:desert",
|
||||
F.BARREN, G.SEDIMENTARY, E.TROPICAL, U.DRY, S.SANDY, W.INLAND
|
||||
)
|
||||
|
||||
@fixture(name="fertile")
|
||||
def createFertileFilter():
|
||||
yield BiomeFilter([(S.LOAMY, S.PEATY)])
|
||||
|
||||
@fixture(name="forest")
|
||||
def createForestBiome():
|
||||
yield Biome(
|
||||
"portland", "minecraft:forest",
|
||||
F.FOREST, G.SEDIMENTARY, E.TEMPERATE, U.DAMP, S.LOAMY, W.INLAND
|
||||
)
|
||||
|
||||
@fixture(name="notDry")
|
||||
def createNotDryFilter():
|
||||
yield BiomeFilter([], [U.DRY])
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_prohibitsSingleTrait(notDry, desert, forest):
|
||||
assert notDry.accepts(forest)
|
||||
assert not notDry.accepts(desert)
|
||||
|
||||
def test_requireSingleTrait(barren, desert, forest):
|
||||
assert barren.accepts(desert)
|
||||
assert not barren.accepts(forest)
|
||||
|
||||
def test_requireOptions(fertile, desert, forest):
|
||||
assert fertile.accepts(forest)
|
||||
assert not fertile.accepts(desert)
|
||||
|
||||
def test_vetoed(barrenNotSandy, desert):
|
||||
assert not barrenNotSandy.accepts(desert)
|
||||
@@ -0,0 +1,49 @@
|
||||
# Class ############################################################################################
|
||||
|
||||
class BiomeTrait:
|
||||
"""
|
||||
Any of the many characteristics which uniquely define a biome.
|
||||
"""
|
||||
|
||||
def __init__(self, name:str):
|
||||
self.name = name
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
if type(self).__name__ != type(other).__name__: return False
|
||||
if self.name != other.name: return False
|
||||
return True
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((type(self).__name__, self.name))
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{type(self).__name__}<{self.name}>"
|
||||
|
||||
|
||||
# Helper Functions #################################################################################
|
||||
|
||||
def within(allTraits:list[BiomeTrait], start:BiomeTrait, end:BiomeTrait) -> list[BiomeTrait]:
|
||||
if not allTraits: return []
|
||||
start = start if start else allTraits[0]
|
||||
end = end if end else allTraits[-1]
|
||||
|
||||
minIndex = 0
|
||||
maxIndex = len(allTraits) - 1
|
||||
|
||||
for index, trait in enumerate(allTraits):
|
||||
if trait == start:
|
||||
minIndex = index
|
||||
elif trait == end:
|
||||
maxIndex = index
|
||||
|
||||
if minIndex > maxIndex:
|
||||
minIndex, maxIndex = maxIndex, minIndex
|
||||
|
||||
result = []
|
||||
for index in range(minIndex, maxIndex+1):
|
||||
result.append(allTraits[index])
|
||||
|
||||
return result
|
||||
@@ -0,0 +1,58 @@
|
||||
from tungston.core.ecology.biometrait import BiomeTrait
|
||||
from pytest import fixture
|
||||
|
||||
import tungston.core.ecology.biometrait as biomeTrait
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="blue")
|
||||
def defineBlue():
|
||||
yield BiomeTrait("blue")
|
||||
|
||||
@fixture(name="green")
|
||||
def defineGreen():
|
||||
yield BiomeTrait("green")
|
||||
|
||||
@fixture(name="yellow")
|
||||
def defineYellow():
|
||||
yield BiomeTrait("yellow")
|
||||
|
||||
@fixture(name="orange")
|
||||
def defineOrange():
|
||||
yield BiomeTrait("orange")
|
||||
|
||||
@fixture(name="red")
|
||||
def defineRed():
|
||||
yield BiomeTrait("red")
|
||||
|
||||
@fixture(name="traitMap")
|
||||
def createTraitMap(green, blue):
|
||||
yield {
|
||||
green: "emerald",
|
||||
blue: "sapphire"
|
||||
}
|
||||
|
||||
@fixture(name="traitList")
|
||||
def createTraitList(green, blue, yellow, orange, red):
|
||||
yield [blue, green, yellow, orange, red]
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_eq(green, blue):
|
||||
assert green == BiomeTrait("green")
|
||||
assert green != blue
|
||||
|
||||
def test_hash(green, blue, traitMap):
|
||||
assert traitMap[green] == "emerald"
|
||||
assert traitMap[blue] == "sapphire"
|
||||
|
||||
def test_repr(green):
|
||||
assert repr(green) == "BiomeTrait<green>"
|
||||
|
||||
def test_str(green):
|
||||
assert str(green) == "green"
|
||||
|
||||
def test_within(traitList, green, yellow, orange):
|
||||
assert biomeTrait.within(traitList, green, orange) == [green, yellow, orange]
|
||||
@@ -0,0 +1,28 @@
|
||||
from tungston.core.ecology.biometrait import BiomeTrait
|
||||
|
||||
import tungston.core.ecology.biometrait as biomeTrait
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Flora(BiomeTrait):
|
||||
"""
|
||||
Describes how densely the foiliage covers a biome.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
CANOPY = Flora("canopy")
|
||||
FOREST = Flora("forest")
|
||||
CLEARING = Flora("clearing")
|
||||
FIELD = Flora("field")
|
||||
BARREN = Flora("barren")
|
||||
|
||||
ALL = [CANOPY, FOREST, CLEARING, FIELD, BARREN]
|
||||
|
||||
# Helpers ##########################################################################################
|
||||
|
||||
def within(start:Flora, end:Flora) -> list[Flora]:
|
||||
return biomeTrait.within(ALL, start, end)
|
||||
@@ -0,0 +1,9 @@
|
||||
from pytest import fixture
|
||||
|
||||
import tungston.core.ecology.flora as flora
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_within():
|
||||
assert flora.within(flora.FOREST, flora.FIELD) == [flora.FOREST, flora.CLEARING, flora.FIELD]
|
||||
@@ -0,0 +1,19 @@
|
||||
from tungston.core.ecology.biometrait import BiomeTrait
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Geology(BiomeTrait):
|
||||
"""
|
||||
The natural of the rock strata in a biome.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
IGNEOUS = Geology("igneous")
|
||||
METAMORPHIC = Geology("metamorphic")
|
||||
SEDIMENTARY = Geology("sedimentary")
|
||||
|
||||
ALL = [IGNEOUS, METAMORPHIC, SEDIMENTARY]
|
||||
@@ -0,0 +1,9 @@
|
||||
from pytest import fixture
|
||||
|
||||
import tungston.core.ecology.geology as geology
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_syntax():
|
||||
pass
|
||||
@@ -0,0 +1,33 @@
|
||||
from tungston.core.ecology.biometrait import BiomeTrait
|
||||
|
||||
import tungston.core.ecology.biometrait as biomeTrait
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Heat(BiomeTrait):
|
||||
"""
|
||||
The general range of ambient temperatures in a biome (in °F).
|
||||
"""
|
||||
|
||||
def __init__(self, name, low:int, high:int):
|
||||
super().__init__(name)
|
||||
self.low = low
|
||||
self.high = high
|
||||
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
TROPICAL = Heat("tropical", 75, 88)
|
||||
SUBTROPICAL = Heat("subtropical", 58, 82)
|
||||
TEMPERATE = Heat("temperate", 36, 66)
|
||||
BOREAL = Heat("boreal", 10, 54)
|
||||
FROZEN = Heat("frozen", -20, 32)
|
||||
|
||||
ALL = [TROPICAL, SUBTROPICAL, TEMPERATE, BOREAL, FROZEN]
|
||||
|
||||
|
||||
# Helpers ##########################################################################################
|
||||
|
||||
def within(start:Heat, end:Heat) -> list[Heat]:
|
||||
return biomeTrait.within(ALL, start, end)
|
||||
@@ -0,0 +1,13 @@
|
||||
from pytest import fixture
|
||||
|
||||
import tungston.core.ecology.heat as heat
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_within():
|
||||
assert (
|
||||
heat.within(heat.SUBTROPICAL, heat.BOREAL)
|
||||
==
|
||||
[heat.SUBTROPICAL, heat.TEMPERATE, heat.BOREAL]
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
from tungston.core.ecology.biometrait import BiomeTrait
|
||||
|
||||
import tungston.core.ecology.biometrait as biomeTrait
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Humidity(BiomeTrait):
|
||||
"""
|
||||
The general level of humidity present in the air.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
WET = Humidity("wet")
|
||||
DAMP = Humidity("damp")
|
||||
DRY = Humidity("dry")
|
||||
|
||||
ALL = [WET, DAMP, DRY]
|
||||
|
||||
# Helpers ##########################################################################################
|
||||
|
||||
def within(start:Humidity, end:Humidity) -> list[Humidity]:
|
||||
return biomeTrait.within(ALL, start, end)
|
||||
@@ -0,0 +1,9 @@
|
||||
from pytest import fixture
|
||||
|
||||
import tungston.core.ecology.humidity as humidity
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_within():
|
||||
assert humidity.within(humidity.DAMP, humidity.DRY) == [humidity.DAMP, humidity.DRY]
|
||||
@@ -0,0 +1,23 @@
|
||||
from tungston.core.ecology.biometrait import BiomeTrait
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Soil(BiomeTrait):
|
||||
"""
|
||||
The nature of the soil within a certain region.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
ACIDIC = Soil("acidic")
|
||||
CLAYEY = Soil("clayey")
|
||||
FUNGAL = Soil("fungal")
|
||||
LOAMY = Soil("loamy")
|
||||
PEATY = Soil("peaty")
|
||||
ROCKY = Soil("rocky")
|
||||
SANDY = Soil("sandy")
|
||||
|
||||
ALL = [ACIDIC, CLAYEY, FUNGAL, LOAMY, PEATY, ROCKY, SANDY]
|
||||
@@ -0,0 +1,9 @@
|
||||
from pytest import fixture
|
||||
|
||||
import tungston.core.ecology.soil as soil
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_syntax():
|
||||
pass
|
||||
@@ -0,0 +1,21 @@
|
||||
from tungston.core.ecology.biometrait import BiomeTrait
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Water(BiomeTrait):
|
||||
"""
|
||||
The kind of large body of water which dominates a biome.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
RIVER = Water("river")
|
||||
SWAMP = Water("swamp")
|
||||
OCEAN = Water("ocean")
|
||||
COAST = Water("coast")
|
||||
INLAND = Water("inland")
|
||||
|
||||
ALL = (RIVER, SWAMP, OCEAN, COAST, INLAND)
|
||||
@@ -0,0 +1,9 @@
|
||||
from pytest import fixture
|
||||
|
||||
import tungston.core.ecology.water as water
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_syntax():
|
||||
pass
|
||||
@@ -0,0 +1,30 @@
|
||||
# Class ############################################################################################
|
||||
|
||||
class Active:
|
||||
"""
|
||||
The time of day when a creature is normally most active.
|
||||
"""
|
||||
|
||||
def __init__(self, name: str, start:int, end:int):
|
||||
self.name = name
|
||||
self.start = start
|
||||
self.end = end
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Active<{self.name}>{{start:{self.start}, end:{self.end}}}"
|
||||
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
DAY = Active("day", 0, 12000)
|
||||
NIGHT = Active("night", 13000, 23000)
|
||||
SUNRISE = Active("sunrise", 23000, 24000)
|
||||
SUNSET = Active("sunset", 12000, 13000)
|
||||
|
||||
ANY = (SUNRISE, DAY, SUNSET, NIGHT)
|
||||
DIURNAL = (DAY)
|
||||
NOCTURNAL = (NIGHT)
|
||||
CREPUSCULAR = (SUNRISE, SUNSET)
|
||||
@@ -0,0 +1,9 @@
|
||||
from pytest import fixture
|
||||
|
||||
import tungston.core.fauna.active as active
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_syntax():
|
||||
pass
|
||||
@@ -0,0 +1,50 @@
|
||||
# Class ############################################################################################
|
||||
|
||||
class Group:
|
||||
"""
|
||||
How many individuals are commonly present when encountering a creature.
|
||||
"""
|
||||
|
||||
def __init__(self, name, smallest, largest):
|
||||
self.name = name
|
||||
self.smallest = smallest
|
||||
self.largest = largest
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
if type(other) != type(self): return False
|
||||
if other.largest != self.largest: return False
|
||||
if other.smallest != self.smallest: return False
|
||||
return True
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash((self.smallest, self.largest))
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return str(self) + f"<{self.smallest} to {self.largest}>"
|
||||
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
SOLO = Group("solo", 1, 1)
|
||||
PAIR = Group("pair", 2, 2)
|
||||
FAMILY = Group("family", 2, 4)
|
||||
TROUP = Group("troup", 3, 6)
|
||||
HERD = Group("herd", 4, 8)
|
||||
|
||||
ALL = [SOLO, PAIR, FAMILY, TROUP, HERD]
|
||||
|
||||
|
||||
# Helper Functions #################################################################################
|
||||
|
||||
def merge(*groups):
|
||||
if not groups: return SOLO
|
||||
|
||||
result = Group(f"", HERD.largest, SOLO.smallest)
|
||||
for group in groups:
|
||||
result.smallest = min(result.smallest, group.smallest)
|
||||
result.largest = max(result.largest, group.largest)
|
||||
|
||||
return result
|
||||
@@ -0,0 +1,17 @@
|
||||
from pytest import fixture
|
||||
|
||||
import tungston.core.fauna.group as group
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="atomicFamily")
|
||||
def createAtomicFamily():
|
||||
yield group.merge(group.PAIR, group.FAMILY)
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_merge(atomicFamily):
|
||||
assert atomicFamily.smallest == 2
|
||||
assert atomicFamily.largest == 4
|
||||
@@ -0,0 +1,27 @@
|
||||
from tungston.core.ecology.biometrait import BiomeTrait
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Location:
|
||||
"""
|
||||
Where a creature is most likely to be encountered.
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Location<{self.name}>"
|
||||
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
OUTSIDE = Location("outside")
|
||||
CAVE = Location("cave")
|
||||
WATER = Location("water")
|
||||
|
||||
ALL = [OUTSIDE, CAVE, WATER]
|
||||
@@ -0,0 +1,36 @@
|
||||
from tungston.core.fauna.active import Active
|
||||
|
||||
import tungston.core.fauna.active as AC
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Mob:
|
||||
"""
|
||||
A creature which may appear in the game world.
|
||||
"""
|
||||
|
||||
def __init__(self, name:str, gameId:str, active:tuple[Active]=AC.DIURNAL):
|
||||
self.name = name
|
||||
self.gameId = gameId
|
||||
self.active = active
|
||||
|
||||
if not self.active:
|
||||
self.active = AC.DIURNAL
|
||||
|
||||
if isinstance(self.active, Active):
|
||||
self.active = (self.active,)
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
if type(self) != type(other): return False
|
||||
if self.gameId != other.gameId: return False
|
||||
return True
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.gameId)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.name}<{self.gameId}>{{active:{self.active}}}"
|
||||
@@ -0,0 +1,39 @@
|
||||
from tungston.core.fauna.mob import Mob
|
||||
from pytest import fixture
|
||||
|
||||
import tungston.core.fauna.active as AC
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="squid")
|
||||
def createSquid():
|
||||
yield Mob("squid", "minecraft:squid", AC.ANY)
|
||||
|
||||
@fixture(name="cow")
|
||||
def createCow():
|
||||
yield Mob("cow", "minecraft:cow", AC.DIURNAL)
|
||||
|
||||
@fixture(name="mobMap")
|
||||
def createMobMap(cow, squid):
|
||||
yield {
|
||||
cow: "beef",
|
||||
squid: "calamari"
|
||||
}
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_eq(squid):
|
||||
assert squid == Mob("squid", "minecraft:squid")
|
||||
assert squid != Mob("squid", "animalsplus:squid")
|
||||
|
||||
def test_hash(cow, squid, mobMap):
|
||||
assert mobMap[cow] == "beef"
|
||||
assert mobMap[squid] == "calamari"
|
||||
|
||||
def test_str(squid):
|
||||
assert str(squid) == "squid"
|
||||
|
||||
def test_repr(cow):
|
||||
assert repr(cow) == "cow<minecraft:cow>{active:(Active<day>{start:0, end:12000},)}"
|
||||
@@ -0,0 +1,16 @@
|
||||
from tungston.core.fauna.mob import Mob
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class MobCatalog:
|
||||
|
||||
def __init__(self, mobs):
|
||||
self.mobs = mobs
|
||||
|
||||
def findByName(self, name):
|
||||
for mob in self.mobs:
|
||||
if mob.name == name:
|
||||
return mob
|
||||
|
||||
return None
|
||||
@@ -0,0 +1,15 @@
|
||||
from tungston.core.habitat import Habitat
|
||||
from tungston.core.fauna.mob import Mob
|
||||
from tungston.core.placement import Placement
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class MobPlacement(Placement):
|
||||
"""
|
||||
A description of where a creature should appear in the world.
|
||||
"""
|
||||
|
||||
def __init__(self, mob:Mob, habitats:list[Habitat]=None):
|
||||
super().__init__(habitats)
|
||||
self.mob = mob
|
||||
@@ -0,0 +1,43 @@
|
||||
from pytest import fixture
|
||||
from tungston.core.ecology.biomefilter import BiomeFilter
|
||||
from tungston.core.fauna.mob import Mob
|
||||
from tungston.core.fauna.mobplacement import MobPlacement
|
||||
from tungston.core.habitat import Habitat
|
||||
|
||||
import tungston.core.ecology.flora as F
|
||||
import tungston.core.ecology.heat as E
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="cow")
|
||||
def createCow():
|
||||
yield Mob("cow", "minecraft:cow")
|
||||
|
||||
@fixture(name="fields")
|
||||
def createFieldsHabitat():
|
||||
yield Habitat(biomeFilter=BiomeFilter([F.FIELD, E.TEMPERATE]))
|
||||
|
||||
@fixture(name="placement")
|
||||
def createMobPlacement(cow, fields):
|
||||
yield MobPlacement(cow, fields)
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_cowsInFields(placement):
|
||||
assert str(placement) == (
|
||||
"Placement{" +
|
||||
"habitats: [" +
|
||||
"Habitat{" +
|
||||
"altitude: [anywhere <-64 to 320>], " +
|
||||
"biomeFilter: BiomeFilter([[Flora<field>, Heat<temperate>]], [[]]), " +
|
||||
"seasons: [Season<spring>, Season<summer>, Season<autumn>, Season<winter>], " +
|
||||
"group: solo<1 to 1>, " +
|
||||
"location: Location<outside>, " +
|
||||
"scarcity: Scarcity<sparse>" +
|
||||
"}" +
|
||||
"]" +
|
||||
"}"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import math
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Bulk:
|
||||
|
||||
def __init__(self, name:str, smallest:int, largest:int):
|
||||
self.name = name
|
||||
self.smallest = smallest
|
||||
self.largest = largest
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Bulk<{self.name}>{{smallest: {self.smallest}, largest: {self.largest}}}"
|
||||
|
||||
def scale(self, factor) -> "Bulk":
|
||||
return Bulk(
|
||||
self.name,
|
||||
math.floor(self.smallest * factor),
|
||||
math.floor(self.largest * factor)
|
||||
)
|
||||
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
SMALL = Bulk("tiny", 250, 500)
|
||||
MEDIUM = Bulk("small", 500, 1000)
|
||||
LARGE = Bulk("small", 1000, 2000)
|
||||
|
||||
ALL = [SMALL, MEDIUM, LARGE]
|
||||
@@ -0,0 +1,20 @@
|
||||
from pytest import fixture
|
||||
from tungston.core.geology.bulk import Bulk
|
||||
|
||||
|
||||
# Fixture ##########################################################################################
|
||||
|
||||
@fixture(name="bulk")
|
||||
def createBulk():
|
||||
return Bulk("chunk", 64, 128)
|
||||
|
||||
@fixture(name="scaledBulk")
|
||||
def scaleBulk(bulk):
|
||||
yield bulk.scale(2.0)
|
||||
|
||||
|
||||
# Fixture ##########################################################################################
|
||||
|
||||
def test_repr(bulk, scaledBulk):
|
||||
assert repr(bulk) == "Bulk<chunk>{smallest: 64, largest: 128}"
|
||||
assert repr(scaledBulk) == "Bulk<chunk>{smallest: 128, largest: 256}"
|
||||
@@ -0,0 +1,22 @@
|
||||
from tungston.core.ecology.biomefilter import BiomeFilter
|
||||
from tungston.core.geology.bulk import Bulk
|
||||
from tungston.core.geology.mineral import Mineral
|
||||
from tungston.core.scarcity import Scarcity
|
||||
|
||||
import tungston.core.scarcity as scarcity
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Deposit:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name:str,
|
||||
scarcity:Scarcity=scarcity.SPARSE,
|
||||
biomeFilter:BiomeFilter|None=None,
|
||||
):
|
||||
self.name = name
|
||||
self.biomeFilter = biomeFilter or BiomeFilter()
|
||||
self.scarcity = scarcity
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
from tungston.core.ecology.biomefilter import BiomeFilter
|
||||
from tungston.core.geology.bulk import Bulk
|
||||
from tungston.core.geology.deposit import Deposit
|
||||
from tungston.core.geology.inclusion import Inclusion
|
||||
from tungston.core.geology.proportion import Proportion
|
||||
from tungston.core.scarcity import Scarcity
|
||||
|
||||
import tungston.core.geology.bulk as BU
|
||||
import tungston.core.geology.proportion as PR
|
||||
import tungston.core.scarcity as SC
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class MetalDeposit(Deposit):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name:str,
|
||||
inclusions:tuple[Inclusion],
|
||||
bulk:Bulk=BU.MEDIUM,
|
||||
proportion:Proportion=PR.BODY,
|
||||
biomeFilter:BiomeFilter=None,
|
||||
scarcity:Scarcity=SC.SPARSE,
|
||||
):
|
||||
super().__init__(name, scarcity, biomeFilter)
|
||||
self.inclusions = inclusions
|
||||
self.bulk = bulk
|
||||
self.proportion = proportion
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "".join([str(p) for p in [
|
||||
"MetalDeposit<", self.name, ">{",
|
||||
"scarcity: ", repr(self.scarcity), ", ",
|
||||
"biomeFilter: ", self.biomeFilter, ", ",
|
||||
"inclusions: [", ", ".join([repr(m) for m in self.inclusions]), "], ",
|
||||
"bulk: ", repr(self.bulk), ", ",
|
||||
"proportion: ", repr(self.proportion),
|
||||
"}"
|
||||
]])
|
||||
@@ -0,0 +1,55 @@
|
||||
from pytest import fixture
|
||||
from tungston.core.ecology.biomefilter import BiomeFilter
|
||||
from tungston.core.geology.deposit.metaldeposit import MetalDeposit
|
||||
from tungston.core.geology.inclusion import Inclusion
|
||||
from tungston.core.geology.mineral import Mineral
|
||||
from tungston.core.geology.replacement import Replacement
|
||||
|
||||
import tungston.core.ecology.flora as FL
|
||||
import tungston.core.geology.bulk as BU
|
||||
import tungston.core.geology.proportion as PR
|
||||
import tungston.core.scarcity as SC
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="quartz")
|
||||
def createQuartz():
|
||||
yield Mineral("quartz", [
|
||||
Replacement("#minecraft:stone_replaceables", "minecraft:quartz_ore"),
|
||||
Replacement("#minecraft:deepslate_replaceables", "minecraft:deepslate_quartz_ore")
|
||||
])
|
||||
|
||||
@fixture(name="iron")
|
||||
def createIron():
|
||||
yield Mineral("iron", [
|
||||
Replacement("#minecraft:stone_replaceables", "minecraft:iron_ore"),
|
||||
Replacement("#minecraft:deepslate_replaceables", "minecraft:deepslate_iron_ore")
|
||||
])
|
||||
|
||||
@fixture(name="bifIron")
|
||||
def createBifIron(iron, quartz):
|
||||
yield MetalDeposit(
|
||||
name = "bifiron",
|
||||
biomeFilter = BiomeFilter([FL.FOREST]),
|
||||
bulk = BU.LARGE,
|
||||
inclusions = [Inclusion(iron, 60), Inclusion(quartz, 40)],
|
||||
proportion = PR.LENS,
|
||||
scarcity = SC.COMMON,
|
||||
)
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_repr(bifIron):
|
||||
assert repr(bifIron) == (
|
||||
"MetalDeposit<bifiron>{" +
|
||||
"scarcity: Scarcity<common>, " +
|
||||
"biomeFilter: BiomeFilter([[Flora<forest>]], [[]]), " +
|
||||
"inclusions: [" +
|
||||
"Inclusion{mineral: iron, weight: 60}, " +
|
||||
"Inclusion{mineral: quartz, weight: 40}" +
|
||||
"], " +
|
||||
"bulk: Bulk<small>{smallest: 1000, largest: 2000}, " +
|
||||
"proportion: Proportion<lens>{ratio: 0.4}" +
|
||||
"}"
|
||||
)
|
||||
@@ -0,0 +1,27 @@
|
||||
from tungston.core.geology.mineralcatalog import MineralCatalog
|
||||
from tungston.core.geology.deposit import Deposit
|
||||
from typing import Iterator
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class DepositCatalog:
|
||||
|
||||
def __init__(self, deposits:None|tuple[Deposit]=None):
|
||||
self._deposits = {}
|
||||
|
||||
for deposit in (deposits or []):
|
||||
self.add(deposit)
|
||||
|
||||
def add(self, deposit:Deposit) -> "DepositCatalog":
|
||||
if deposit.name in self._deposits:
|
||||
raise Exception("Catalog already has an entry for {deposit.name}")
|
||||
|
||||
self._deposits[deposit.name] = deposit
|
||||
|
||||
def all(self) -> Iterator[Deposit]:
|
||||
for deposit in self._deposits.values():
|
||||
yield deposit
|
||||
|
||||
def get(self, name:str) -> Deposit|None:
|
||||
return self._deposits.get(name, None)
|
||||
@@ -0,0 +1,53 @@
|
||||
from pytest import fixture
|
||||
from tungston.core.ecology.biomefilter import BiomeFilter
|
||||
from tungston.core.geology.deposit import Deposit
|
||||
from tungston.core.geology.deposit.metaldeposit import MetalDeposit
|
||||
from tungston.core.geology.depositcatalog import DepositCatalog
|
||||
from tungston.core.geology.inclusion import Inclusion
|
||||
from tungston.core.geology.mineral import Mineral
|
||||
from tungston.core.geology.replacement import Replacement
|
||||
|
||||
import tungston.core.ecology.flora as FL
|
||||
import tungston.core.geology.bulk as BU
|
||||
import tungston.core.scarcity as SC
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="quartz")
|
||||
def createQuartz():
|
||||
yield Mineral("quartz", [
|
||||
Replacement("#minecraft:stone_replaceables", "minecraft:quartz_ore"),
|
||||
Replacement("#minecraft:deepslate_replaceables", "minecraft:deepslate_quartz_ore")
|
||||
])
|
||||
|
||||
@fixture(name="iron")
|
||||
def createIron():
|
||||
yield Mineral("iron", [
|
||||
Replacement("#minecraft:stone_replaceables", "minecraft:iron_ore"),
|
||||
Replacement("#minecraft:deepslate_replaceables", "minecraft:deepslate_iron_ore")
|
||||
])
|
||||
|
||||
@fixture(name="bifiron")
|
||||
def createBifIron(iron, quartz):
|
||||
yield MetalDeposit(
|
||||
name = "bif_iron",
|
||||
scarcity = SC.COMMON,
|
||||
biomeFilter = BiomeFilter([FL.FOREST]),
|
||||
inclusions = [Inclusion(iron, 60), Inclusion(quartz, 40)],
|
||||
bulk = BU.LARGE,
|
||||
proportion = 0.5,
|
||||
)
|
||||
|
||||
@fixture(name="catalog")
|
||||
def createCatalog(bifiron):
|
||||
yield DepositCatalog([bifiron])
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_all(catalog):
|
||||
assert [m.name for m in catalog.all()] == ["bif_iron"]
|
||||
|
||||
def test_get(catalog):
|
||||
assert catalog.get("bif_iron").name == "bif_iron"
|
||||
@@ -0,0 +1,24 @@
|
||||
from tungston.core.geology.mineral import Mineral
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Inclusion:
|
||||
|
||||
def __init__(self, mineral:Mineral, weight:int=100):
|
||||
self.mineral = mineral
|
||||
self.weight = weight
|
||||
|
||||
def __str__(self) -> str:
|
||||
if self.weight == 100:
|
||||
return self.mineral.name
|
||||
|
||||
return f"{self.mineral.name} ({self.weight}%)"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "".join([str(p) for p in [
|
||||
"Inclusion{",
|
||||
"mineral: ", self.mineral.name, ", ",
|
||||
"weight: ", self.weight,
|
||||
"}"
|
||||
]])
|
||||
@@ -0,0 +1,24 @@
|
||||
from pytest import fixture
|
||||
from tungston.core.geology.inclusion import Inclusion
|
||||
from tungston.core.geology.mineral import Mineral
|
||||
from tungston.core.geology.replacement import Replacement
|
||||
|
||||
|
||||
# Fixtures #########################################################################################I
|
||||
|
||||
@fixture(name="iron")
|
||||
def createIron():
|
||||
yield Mineral("iron", [
|
||||
Replacement("#minecraft:stone_replaceables", "minecraft:iron_ore"),
|
||||
Replacement("#minecraft:deepslate_replaceables", "minecraft:deepslate_iron_ore")
|
||||
])
|
||||
|
||||
@fixture(name="inclusion")
|
||||
def createInclusion(iron):
|
||||
yield Inclusion(iron, 75)
|
||||
|
||||
|
||||
# Tests ############################################################################################I
|
||||
|
||||
def test_repr(inclusion):
|
||||
assert repr(inclusion) == "Inclusion{mineral: iron, weight: 75}"
|
||||
@@ -0,0 +1,32 @@
|
||||
from tungston.core.geology.replacement import Replacement
|
||||
from tungston.core.resourceid import ResourceId
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Mineral:
|
||||
|
||||
def __init__(self, name:str, replacements:None|tuple[Replacement]=None):
|
||||
self.name = name
|
||||
self.replacements = replacements or []
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "".join([str(p) for p in [
|
||||
f"Mineral<{self.name}>", "{",
|
||||
"replacements: [",
|
||||
", ".join([str(r) for r in self.replacements]),
|
||||
"]",
|
||||
"}"
|
||||
]])
|
||||
|
||||
def addStoneReplacement(self, blockId:str) -> "Mineral":
|
||||
blockId = ResourceId.canonical(blockId)
|
||||
self.replacements.append(Replacement(STONE_REPLACEABLES, blockId))
|
||||
return self
|
||||
|
||||
def addDeepslateReplacement(self, blockId:str) -> "Mineral":
|
||||
blockId = ResourceId.canonical(blockId)
|
||||
self.replacements.append(Replacement(DEEPSLATE_REPLACEABLES, blockId))
|
||||
@@ -0,0 +1,28 @@
|
||||
from tungston.core.geology.mineral import Mineral
|
||||
from tungston.core.geology.replacement import Replacement
|
||||
from pytest import fixture
|
||||
|
||||
import tungston.core.geology.mineral as mineral
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="iron")
|
||||
def createIron():
|
||||
yield Mineral("iron", [
|
||||
Replacement.inStone("iron_ore"),
|
||||
Replacement.inDeepslate("deepslate_iron_ore"),
|
||||
])
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_repr(iron):
|
||||
assert repr(iron) == (
|
||||
"Mineral<iron>{" +
|
||||
"replacements: [" +
|
||||
"#minecraft:stone_ore_replaceables => minecraft:iron_ore, " +
|
||||
"#minecraft:deepslate_ore_replaceables => minecraft:deepslate_iron_ore" +
|
||||
"]" +
|
||||
"}"
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
from tungston.core.geology.mineral import Mineral
|
||||
from typing import Iterator
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class MineralCatalog:
|
||||
|
||||
def __init__(self, minerals:tuple[Mineral]=None):
|
||||
self._minerals = {}
|
||||
|
||||
for mineral in (minerals or []):
|
||||
self.add(mineral)
|
||||
|
||||
def add(self, mineral:Mineral) -> "MineralCatalog":
|
||||
if mineral.name in self._minerals:
|
||||
raise Exception(f"Catalog already contains an entry for {mineral.name}")
|
||||
|
||||
self._minerals[mineral.name] = mineral
|
||||
|
||||
def all(self) -> Iterator[Mineral]:
|
||||
for mineral in self._minerals.values():
|
||||
yield mineral
|
||||
|
||||
def get(self, name:str) -> Mineral|None:
|
||||
return self._minerals.get(name, None)
|
||||
@@ -0,0 +1,34 @@
|
||||
from tungston.core.geology.mineral import Mineral
|
||||
from tungston.core.geology.mineralcatalog import MineralCatalog
|
||||
from tungston.core.geology.replacement import Replacement
|
||||
from pytest import fixture
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="copper")
|
||||
def createCopper():
|
||||
yield Mineral("copper", [
|
||||
Replacement("#minecraft:stone_replaceables", "minecraft:copper_ore"),
|
||||
Replacement("#minecraft:deepslate_replaceables", "minecraft:deepslate_copper_ore")
|
||||
])
|
||||
|
||||
@fixture(name="iron")
|
||||
def createIron():
|
||||
yield Mineral("iron", [
|
||||
Replacement("#minecraft:stone_replaceables", "minecraft:iron_ore"),
|
||||
Replacement("#minecraft:deepslate_replaceables", "minecraft:deepslate_iron_ore")
|
||||
])
|
||||
|
||||
@fixture(name="catalog")
|
||||
def createCatalog(copper, iron):
|
||||
yield MineralCatalog([copper, iron])
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_all(catalog):
|
||||
assert [m.name for m in catalog.all()] == ["copper", "iron"]
|
||||
|
||||
def test_get(catalog):
|
||||
assert catalog.get("iron").name == "iron"
|
||||
@@ -0,0 +1,34 @@
|
||||
# Class ############################################################################################
|
||||
|
||||
class Proportion:
|
||||
|
||||
def __init__(self, name:str, ratio:float):
|
||||
self.name = name
|
||||
self.ratio = ratio
|
||||
|
||||
def __eq__(self, other) -> bool:
|
||||
if type(self) != type(other): return False
|
||||
if self.name != other.name: return False
|
||||
return True
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.name)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Proportion<{self.name}>{{ratio: {self.ratio}}}"
|
||||
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
BED = Proportion("bed", 0.1) # salt, nitrate, redbed, loam, bif iron, peat coal
|
||||
LENS = Proportion("lens", 0.4) # bit.coal, aluminum, lapis, quartzite
|
||||
LODE = Proportion("lode", 0.7) # au.quartz, mvt lead
|
||||
BODY = Proportion("body", 1.0) # sed.iron
|
||||
STOCK = Proportion("stock", 1.3) # tin
|
||||
VENT = Proportion("vent", 1.6) # sulfur
|
||||
PIPE = Proportion("pipe", 1.9) # por.copper
|
||||
|
||||
ALL = [BED, LENS, LODE, BODY, STOCK, VENT, PIPE]
|
||||
@@ -0,0 +1,12 @@
|
||||
from pytest import fixture
|
||||
|
||||
import tungston.core.geology.proportion as proportion
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_str():
|
||||
assert str(proportion.LENS) == "lens"
|
||||
|
||||
def test_repr():
|
||||
assert repr(proportion.LENS) == "Proportion<lens>{ratio: 0.4}"
|
||||
@@ -0,0 +1,40 @@
|
||||
from tungston.core.resourceid import ResourceId
|
||||
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
STONE_REPLACEABLES = "#minecraft:stone_ore_replaceables"
|
||||
DEEPSLATE_REPLACEABLES = "#minecraft:deepslate_ore_replaceables"
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Replacement:
|
||||
|
||||
@staticmethod
|
||||
def inStone(target:str, weight:int=100):
|
||||
return Replacement(STONE_REPLACEABLES, target, weight)
|
||||
|
||||
@staticmethod
|
||||
def inDeepslate(target:str, weight:int=100):
|
||||
return Replacement(DEEPSLATE_REPLACEABLES, target, weight)
|
||||
|
||||
def __init__(self, source:str, target:str, weight:int=100):
|
||||
self.source = ResourceId.canonical(source)
|
||||
self.target = ResourceId.canonical(target)
|
||||
self.weight = weight
|
||||
|
||||
def __str__(self) -> str:
|
||||
if self.weight == 100:
|
||||
return f"{self.source} => {self.target}"
|
||||
|
||||
return f"{self.source} => {self.target} ({self.weight}%)"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "".join([str(p) for p in [
|
||||
"Replacement{" +
|
||||
"source: ", self.source, ", ",
|
||||
"target: ", self.target, ", ",
|
||||
"weight: ", self.weight,
|
||||
"}"
|
||||
]])
|
||||
@@ -0,0 +1,28 @@
|
||||
from pytest import fixture
|
||||
from tungston.core.geology.replacement import Replacement
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="replacement")
|
||||
def createReplacement():
|
||||
yield Replacement("#minecraft:stone", "minecraft:iron_ore")
|
||||
|
||||
@fixture(name="weightedReplacement")
|
||||
def createWeightedReplacement():
|
||||
yield Replacement("#minecraft:dirt", "minecraft:gravel", 25)
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_repr(replacement, weightedReplacement):
|
||||
assert repr(replacement) == (
|
||||
"Replacement{source: #minecraft:stone, target: minecraft:iron_ore, weight: 100}"
|
||||
)
|
||||
assert repr(weightedReplacement) == (
|
||||
"Replacement{source: #minecraft:dirt, target: minecraft:gravel, weight: 25}"
|
||||
)
|
||||
|
||||
def test_str(replacement, weightedReplacement):
|
||||
assert str(replacement) == "#minecraft:stone => minecraft:iron_ore"
|
||||
assert str(weightedReplacement) == "#minecraft:dirt => minecraft:gravel (25%)"
|
||||
@@ -0,0 +1,89 @@
|
||||
from tungston.core.altitude import Altitude
|
||||
from tungston.core.ecology.biomefilter import BiomeFilter
|
||||
from tungston.core.fauna.group import Group
|
||||
from tungston.core.fauna.location import Location
|
||||
from tungston.core.scarcity import Scarcity
|
||||
from tungston.core.season import Season
|
||||
|
||||
import tungston.core.altitude as A
|
||||
import tungston.core.fauna.group as G
|
||||
import tungston.core.fauna.location as L
|
||||
import tungston.core.scarcity as C
|
||||
import tungston.core.season as E
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Habitat:
|
||||
"""
|
||||
A description of where and when various features may be present.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
|
||||
altitudes:tuple[Altitude]|Altitude=[A.ANYWHERE],
|
||||
biomeFilter:BiomeFilter=None,
|
||||
seasons:tuple[Season]=E.ALL,
|
||||
|
||||
group:Group=G.SOLO,
|
||||
location:Location=L.OUTSIDE,
|
||||
scarcity:Scarcity=C.SPARSE,
|
||||
):
|
||||
self.altitudes = (altitudes,) if isinstance(altitudes, Altitude) else altitudes
|
||||
self.biomeFilter = biomeFilter or BiomeFilter()
|
||||
self.seasons = (seasons,) if isinstance(seasons, Season) else seasons
|
||||
|
||||
self.group = group
|
||||
self.location = location
|
||||
self.scarcity = scarcity
|
||||
|
||||
self._source = None
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "".join(str(p) for p in [
|
||||
self.altitude, "|",
|
||||
self.biomeFilter, "|",
|
||||
self.seasons, "|",
|
||||
self.group, "|",
|
||||
self.location, "|",
|
||||
self.scarcity
|
||||
])
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "".join([str(p) for p in [
|
||||
"Habitat{",
|
||||
"altitude: [", ", ".join([repr(a) for a in self.altitudes]), "], ",
|
||||
"biomeFilter: ", repr(self.biomeFilter), ", ",
|
||||
"seasons: [", ", ".join([repr(s) for s in self.seasons]), "], ",
|
||||
"group: ", repr(self.group), ", ",
|
||||
"location: ", repr(self.location), ", ",
|
||||
"scarcity: ", repr(self.scarcity),
|
||||
"}"
|
||||
]])
|
||||
|
||||
def derive(self, **kwargs):
|
||||
kwargs = {
|
||||
"altitudes": self.altitudes,
|
||||
"biomeFilter": self.biomeFilter,
|
||||
"seasons": self.seasons,
|
||||
"group": self.group,
|
||||
"location": self.location,
|
||||
"scarcity": self.scarcity
|
||||
} | kwargs
|
||||
|
||||
result = Habitat(**kwargs)
|
||||
result._source = self
|
||||
|
||||
return result
|
||||
|
||||
def collect(self) -> "tuple[Habitat]":
|
||||
result = []
|
||||
|
||||
current = self
|
||||
while current:
|
||||
result.append(current)
|
||||
current = current._source
|
||||
|
||||
return tuple(reversed(result))
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
from pytest import fixture
|
||||
from tungston.core.ecology.biomefilter import BiomeFilter
|
||||
from tungston.core.habitat import Habitat
|
||||
|
||||
import tungston.core.ecology.flora as FL
|
||||
import tungston.core.ecology.heat as HE
|
||||
import tungston.core.season as SE
|
||||
import tungston.core.scarcity as SC
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="fields")
|
||||
def createFieldsFilter():
|
||||
yield BiomeFilter([FL.FIELD, HE.TEMPERATE])
|
||||
|
||||
@fixture(name="derivedHabitats")
|
||||
def createDerivedHabitat(fields):
|
||||
yield (
|
||||
Habitat(biomeFilter=fields, seasons=SE.SUMMER, scarcity=SC.COMMON)
|
||||
.derive(seasons=SE.AUTUMN, scarcity=SC.UNCOMMON)
|
||||
.derive(seasons=SE.WINTER, scarcity=SC.ABSENT)
|
||||
.collect()
|
||||
)
|
||||
|
||||
@fixture(name="fields")
|
||||
def createFieldsHabitat(fields):
|
||||
yield Habitat(biomeFilter=fields)
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_derive(derivedHabitats):
|
||||
assert [h.scarcity for h in derivedHabitats] == [SC.COMMON, SC.UNCOMMON, SC.ABSENT]
|
||||
|
||||
def test_repr(fields):
|
||||
assert repr(fields) == (
|
||||
"Habitat{" +
|
||||
"altitude: [anywhere <-64 to 320>], " +
|
||||
"biomeFilter: BiomeFilter([[Flora<field>, Heat<temperate>]], [[]]), " +
|
||||
"seasons: [Season<spring>, Season<summer>, Season<autumn>, Season<winter>], " +
|
||||
"group: solo<1 to 1>, " +
|
||||
"location: Location<outside>, " +
|
||||
"scarcity: Scarcity<sparse>" +
|
||||
"}"
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
from tungston.core.habitat import Habitat
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Placement:
|
||||
"""
|
||||
Abstract base class for placing something into its various habitats.
|
||||
"""
|
||||
|
||||
def __init__(self, habitats:tuple[Habitat]=None):
|
||||
self.habitats = habitats
|
||||
|
||||
if not self.habitats:
|
||||
self.habitats = ()
|
||||
|
||||
if isinstance(self.habitats, Habitat):
|
||||
self.habitats = self.habitats.collect()
|
||||
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
"Placement{" +
|
||||
f"habitats: [{', '.join([repr(h) for h in self.habitats])}]" +
|
||||
"}"
|
||||
)
|
||||
@@ -0,0 +1,38 @@
|
||||
from pytest import fixture
|
||||
from tungston.core.ecology.biomefilter import BiomeFilter
|
||||
from tungston.core.habitat import Habitat
|
||||
from tungston.core.placement import Placement
|
||||
|
||||
import tungston.core.ecology.flora as F
|
||||
import tungston.core.ecology.heat as E
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="fields")
|
||||
def createFieldsHabitat():
|
||||
yield Habitat(biomeFilter=BiomeFilter([F.FIELD, E.TEMPERATE]))
|
||||
|
||||
@fixture(name="placement")
|
||||
def createPlacement(fields):
|
||||
yield Placement([fields])
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_repr(placement):
|
||||
assert repr(placement) == (
|
||||
"Placement{" +
|
||||
"habitats: [" +
|
||||
"Habitat{" +
|
||||
"altitude: [anywhere <-64 to 320>], " +
|
||||
"biomeFilter: BiomeFilter([[Flora<field>, Heat<temperate>]], [[]]), " +
|
||||
"seasons: [Season<spring>, Season<summer>, Season<autumn>, Season<winter>], " +
|
||||
"group: solo<1 to 1>, " +
|
||||
"location: Location<outside>, " +
|
||||
"scarcity: Scarcity<sparse>" +
|
||||
"}" +
|
||||
"]" +
|
||||
"}"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
from collections.abc import Callable
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class ResourceId:
|
||||
|
||||
@staticmethod
|
||||
def alterName(text:str, doTransform:Callable[[str],str]) -> str:
|
||||
resourceId = ResourceId.parse(text)
|
||||
resourceId.name = doTransform(resourceId.name)
|
||||
return str(resourceId)
|
||||
|
||||
@staticmethod
|
||||
def canonical(text:str):
|
||||
return str(ResourceId.parse(text))
|
||||
|
||||
@staticmethod
|
||||
def parse(text:str):
|
||||
isTag = False
|
||||
if text.startswith("#"):
|
||||
isTag = True
|
||||
text = text[1:]
|
||||
|
||||
index = text.find(":")
|
||||
if index > 0:
|
||||
mod = text[0:index]
|
||||
name = text[index+1:]
|
||||
elif index == 0:
|
||||
mod = "minecraft"
|
||||
name = text[1:]
|
||||
else:
|
||||
mod = "minecraft"
|
||||
name = text
|
||||
|
||||
return ResourceId(isTag, mod, name)
|
||||
|
||||
def __init__(self, isTag:bool, mod:str, name:str):
|
||||
self.isTag = isTag
|
||||
self.mod = mod
|
||||
self.name = name
|
||||
|
||||
def __str__(self) -> str:
|
||||
if not self.isTag:
|
||||
return f"{self.mod}:{self.name}"
|
||||
|
||||
return f"#{self.mod}:{self.name}"
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "".join([str(p) for p in [
|
||||
"ResourceId{",
|
||||
"isTag: ", ("True" if self.isTag else "False"), ", ",
|
||||
"mod: ", self.mod, ", ",
|
||||
"name: ", self.name,
|
||||
"}"
|
||||
]])
|
||||
@@ -0,0 +1,16 @@
|
||||
from tungston.core.resourceid import ResourceId
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_singlePart():
|
||||
assert str(ResourceId.parse("alpha")) == "minecraft:alpha"
|
||||
|
||||
def test_leadingSeparator():
|
||||
assert str(ResourceId.parse(":bravo")) == "minecraft:bravo"
|
||||
|
||||
def test_fullResource():
|
||||
assert str(ResourceId.parse("charlie:delta")) == "charlie:delta"
|
||||
|
||||
def test_fullTag():
|
||||
assert str(ResourceId.parse("#charlie:delta")) == "#charlie:delta"
|
||||
@@ -0,0 +1,27 @@
|
||||
# Class ############################################################################################
|
||||
|
||||
class Scarcity:
|
||||
"""
|
||||
How frequently something appears within a given region.
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Scarcity<{self.name}>"
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
ABSENT = Scarcity("absent")
|
||||
RARE = Scarcity("rare")
|
||||
UNUSUAL = Scarcity("unusual")
|
||||
SPARSE = Scarcity("sparse")
|
||||
UNCOMMON = Scarcity("uncommon")
|
||||
COMMON = Scarcity("common")
|
||||
CARPET = Scarcity("carpet")
|
||||
|
||||
ALL = [ABSENT, RARE, UNUSUAL, SPARSE, UNCOMMON, COMMON, CARPET]
|
||||
@@ -0,0 +1,9 @@
|
||||
from tungston.core.scarcity import Scarcity
|
||||
from pytest import fixture
|
||||
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_syntax():
|
||||
pass
|
||||
@@ -0,0 +1,44 @@
|
||||
# Class ############################################################################################
|
||||
|
||||
class Season:
|
||||
"""
|
||||
A time of year demonstrating different climate and activity among flora and fauna.
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"Season<{self.name}>"
|
||||
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
SPRING = Season("spring")
|
||||
SUMMER = Season("summer")
|
||||
AUTUMN = Season("autumn")
|
||||
WINTER = Season("winter")
|
||||
|
||||
ALL = [SPRING, SUMMER, AUTUMN, WINTER]
|
||||
|
||||
WET = [WINTER, SPRING]
|
||||
DRY = [SUMMER, AUTUMN]
|
||||
|
||||
HOT = [SUMMER]
|
||||
COOL = [SPRING, AUTUMN]
|
||||
COLD = [WINTER]
|
||||
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
def excluding(target:Season) -> list[Season]:
|
||||
result = []
|
||||
|
||||
for season in ALL:
|
||||
if season == target: continue
|
||||
result.append(season)
|
||||
|
||||
return result
|
||||
@@ -0,0 +1,16 @@
|
||||
from pytest import fixture
|
||||
|
||||
import tungston.core.season as season
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="notSummer")
|
||||
def createNotSummerList():
|
||||
yield season.excluding(season.SUMMER)
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_excluding(notSummer):
|
||||
assert ", ".join([str(s) for s in notSummer]) == "spring, autumn, winter"
|
||||
@@ -0,0 +1,21 @@
|
||||
from tungston.core.ecology.biomecatalog import BiomeCatalog
|
||||
from tungston.core.fauna.mobcatalog import MobCatalog
|
||||
from tungston.core.geology.mineralcatalog import MineralCatalog
|
||||
from tungston.core.geology.depositcatalog import DepositCatalog
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class World:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
biomes:BiomeCatalog,
|
||||
deposits:DepositCatalog,
|
||||
minerals:MineralCatalog,
|
||||
mobs:MobCatalog
|
||||
):
|
||||
self.biomes = biomes
|
||||
self.deposits = deposits
|
||||
self.minerals = minerals
|
||||
self.mobs = mobs
|
||||
@@ -0,0 +1,7 @@
|
||||
from tungston.core.world import World
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_syntax():
|
||||
pass
|
||||
@@ -0,0 +1,14 @@
|
||||
import os
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Datapack:
|
||||
|
||||
def __init__(self, path:str):
|
||||
self.path = path
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return os.path.basename(self.path)
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
from typing import Any
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class BlockState:
|
||||
|
||||
def __init__(self, blockId:str, properties:dict[str,str]=None):
|
||||
self.blockId = blockId
|
||||
self.properties = properties
|
||||
|
||||
def asData(self) -> dict[str,Any]:
|
||||
result = { "Name": self.blockId }
|
||||
|
||||
if self.properties:
|
||||
result["Properties"] = self.properties
|
||||
|
||||
return result
|
||||
@@ -0,0 +1,20 @@
|
||||
from pytest import fixture
|
||||
from tungston.generator.datapack.blockstate import BlockState
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="state")
|
||||
def createBlockState():
|
||||
yield BlockState("minecraft:button", {"waterlogged": "true"})
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_asData(state):
|
||||
assert state.asData() == {
|
||||
"Name": "minecraft:button",
|
||||
"Properties": {
|
||||
"waterlogged": "true"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
# Class ############################################################################################
|
||||
|
||||
class BlockStateProvider:
|
||||
|
||||
def __init__(self, gameId:str):
|
||||
self.gameId = gameId
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
from tungston.generator.datapack.blockstate import BlockState
|
||||
from tungston.generator.datapack.blockstateprovider import BlockStateProvider
|
||||
from typing import Any
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class SimpleStateProvider(BlockStateProvider):
|
||||
|
||||
def __init__(self, state:BlockState):
|
||||
super().__init__("minecraft:simple_state_provider")
|
||||
self.state = state
|
||||
|
||||
def asData(self) -> dict[str, Any]:
|
||||
return {
|
||||
"type": self.gameId,
|
||||
"state": self.state.asData(),
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
from tungston.generator.datapack.blockstate import BlockState
|
||||
from tungston.generator.datapack.blockstateprovider.simplestateprovider import SimpleStateProvider
|
||||
from pytest import fixture
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="state")
|
||||
def createBlockState():
|
||||
yield BlockState("minecraft:button", {"waterlogged": "true"})
|
||||
|
||||
@fixture(name="provider")
|
||||
def createProvider(state):
|
||||
return SimpleStateProvider(state)
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_asData(provider):
|
||||
assert provider.asData() == {
|
||||
"type": "minecraft:simple_state_provider",
|
||||
"state": {
|
||||
"Name": "minecraft:button",
|
||||
"Properties": {
|
||||
"waterlogged": "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# Class ############################################################################################
|
||||
|
||||
class ConfiguredFeature:
|
||||
|
||||
def __init__(self, gameId:str):
|
||||
self.gameId = gameId
|
||||
@@ -0,0 +1,31 @@
|
||||
from tungston.generator.datapack.configuredfeature.configuredfeature import ConfiguredFeature
|
||||
from typing import Any
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class RandomPatch(ConfiguredFeature):
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
gameId:str,
|
||||
feature:"PlacedFeature",
|
||||
tries:int=128,
|
||||
xzSpread:int=7,
|
||||
ySpread:int=3,
|
||||
):
|
||||
super().__init__("minecraft:random_patch")
|
||||
|
||||
self.feature = feature
|
||||
self.tries = tries
|
||||
self.xzSpread = xzSpread
|
||||
self.ySpread = ySpread
|
||||
|
||||
def asData(self) -> dict[str,Any]:
|
||||
return {
|
||||
"type": self.type,
|
||||
"feature": self.feature.gameId,
|
||||
"tries": self.tries,
|
||||
"xz_spread": self.xySpread,
|
||||
"y_spread": self.ySpread
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
from pytest import fixture
|
||||
from tungston.generator.datapack.blockstate import BlockState
|
||||
from tungston.generator.datapack.blockstateprovider.simplestateprovider import SimpleStateProvider
|
||||
from tungston.generator.datapack.configuredfeature.simpleblock import SimpleBlock
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="state")
|
||||
def createBlockState():
|
||||
yield BlockState("minecraft:button", {"waterlogged": "true"})
|
||||
|
||||
@fixture(name="provider")
|
||||
def createProvider(state):
|
||||
return SimpleStateProvider(state)
|
||||
|
||||
@fixture(name="feature")
|
||||
def createSimpleBlockFeature(provider):
|
||||
return SimpleBlock(provider)
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_asData(feature):
|
||||
assert feature.asData() == {
|
||||
"type": "minecraft:simple_block",
|
||||
"config": {
|
||||
"to_place": {
|
||||
"type": "minecraft:simple_state_provider",
|
||||
"state": {
|
||||
"Name": "minecraft:button",
|
||||
"Properties": {
|
||||
"waterlogged": "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
from tungston.generator.datapack.blockstateprovider import BlockStateProvider
|
||||
from tungston.generator.datapack.configuredfeature import ConfiguredFeature
|
||||
from typing import Any
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class SimpleBlock(ConfiguredFeature):
|
||||
|
||||
def __init__(self, toPlace:BlockStateProvider):
|
||||
super().__init__("minecraft:simple_block")
|
||||
self.toPlace = toPlace
|
||||
|
||||
def asData(self) -> dict[str, Any]:
|
||||
return {
|
||||
"type": self.gameId,
|
||||
"config": {
|
||||
"to_place": self.toPlace.asData()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
from pytest import fixture
|
||||
from tungston.generator.datapack.blockstate import BlockState
|
||||
from tungston.generator.datapack.blockstateprovider.simplestateprovider import SimpleStateProvider
|
||||
from tungston.generator.datapack.configuredfeature.simpleblock import SimpleBlock
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="state")
|
||||
def createBlockState():
|
||||
yield BlockState("minecraft:button", {"waterlogged": "true"})
|
||||
|
||||
@fixture(name="provider")
|
||||
def createProvider(state):
|
||||
return SimpleStateProvider(state)
|
||||
|
||||
@fixture(name="feature")
|
||||
def createSimpleBlockFeature(provider):
|
||||
return SimpleBlock(provider)
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_asData(feature):
|
||||
assert feature.asData() == {
|
||||
"type": "minecraft:simple_block",
|
||||
"config": {
|
||||
"to_place": {
|
||||
"type": "minecraft:simple_state_provider",
|
||||
"state": {
|
||||
"Name": "minecraft:button",
|
||||
"Properties": {
|
||||
"waterlogged": "true"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
# Class ############################################################################################
|
||||
|
||||
class HeightMapType:
|
||||
"""
|
||||
A cached record of the top block at all points in the world.
|
||||
|
||||
see: https://minecraft.wiki/w/Heightmap
|
||||
"""
|
||||
|
||||
def __init__(self, gameId:str):
|
||||
self.gameId = gameId
|
||||
|
||||
def asData(self) -> str:
|
||||
return self.gameId
|
||||
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
MOTION_BLOCKING = HeightMapType("MOTION_BLOCKING")
|
||||
MOTION_BLOCKING_NO_LEAVES = HeightMapType("MOTION_BLOCKING_NO_LEAVES")
|
||||
OCEAN_FLOOR = HeightMapType("OCEAN_FLOOR")
|
||||
OCEAN_FLOOR_WG = HeightMapType("OCEAN_FLOOR_WG")
|
||||
WORLD_SURFACE = HeightMapType("WORLD_SURFACE")
|
||||
WORLD_SURFACE_WG = HeightMapType("WORLD_SURFACE_WG")
|
||||
@@ -0,0 +1,7 @@
|
||||
from tungston.generator.datapack.heightmaptype import HeightMapType
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_syntax():
|
||||
pass
|
||||
@@ -0,0 +1,6 @@
|
||||
# Class ############################################################################################
|
||||
|
||||
class PlacedFeature:
|
||||
|
||||
def __init__(self, gameId:str):
|
||||
self.gameId = gameId
|
||||
@@ -0,0 +1,6 @@
|
||||
from tungston.generator.datapack.placedfeature import PlacedFeature
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_syntax():
|
||||
pass
|
||||
@@ -0,0 +1,6 @@
|
||||
# Class ############################################################################################
|
||||
|
||||
class Placement:
|
||||
|
||||
def __init__(self, gameId:str):
|
||||
self.gameId = gameId
|
||||
@@ -0,0 +1,13 @@
|
||||
from tungston.generator.datapack.placement import Placement
|
||||
from typing import Any
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Biome(Placement):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("minecraft:biome")
|
||||
|
||||
def asData(self) -> dict[str,Any]:
|
||||
return { "type": self.gameId }
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
from pytest import fixture
|
||||
from tungston.generator.datapack.placement.biome import Biome
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="placement")
|
||||
def createPlacement():
|
||||
yield Biome()
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_asData(placement):
|
||||
assert placement.asData() == {
|
||||
"type": "minecraft:biome",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
from tungston.generator.datapack.placement import Placement
|
||||
from tungston.generator.datapack.heightmaptype import HeightMapType
|
||||
from typing import Any
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class HeightMap(Placement):
|
||||
|
||||
def __init__(self, heightMap:HeightMapType):
|
||||
super().__init__("minecraft:heightmap")
|
||||
self.heightMap = heightMap
|
||||
|
||||
def asData(self) -> dict[str,Any]:
|
||||
return {
|
||||
"type": self.gameId,
|
||||
"heightmap": self.heightMap.asData()
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
from pytest import fixture
|
||||
from tungston.generator.datapack.placement.heightmap import HeightMap
|
||||
from tungston.generator.datapack.heightmaptype import WORLD_SURFACE
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="placement")
|
||||
def createPlacement():
|
||||
yield HeightMap(WORLD_SURFACE)
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_asData(placement):
|
||||
assert placement.asData() == {
|
||||
"type": "minecraft:heightmap",
|
||||
"heightmap": "WORLD_SURFACE"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
from tungston.generator.datapack.placement import Placement
|
||||
from typing import Any
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class InSquare(Placement):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__("minecraft:in_square")
|
||||
|
||||
def asData(self) -> dict[str,Any]:
|
||||
return { "type": self.gameId }
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
from pytest import fixture
|
||||
from tungston.generator.datapack.placement.insquare import InSquare
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="placement")
|
||||
def createPlacement():
|
||||
yield InSquare()
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_asData(placement):
|
||||
assert placement.asData() == {
|
||||
"type": "minecraft:in_square",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
from tungston.generator.datapack.placement import Placement
|
||||
from typing import Any
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class RarityFilter(Placement):
|
||||
|
||||
def __init__(self, chance:int):
|
||||
super().__init__("minecraft:rarity_filter")
|
||||
self.chance = chance
|
||||
|
||||
def asData(self) -> dict[str,Any]:
|
||||
return {
|
||||
"type": self.gameId,
|
||||
"chance": self.chance
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
from pytest import fixture
|
||||
from tungston.generator.datapack.placement.rarityfilter import RarityFilter
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="placement")
|
||||
def createPlacement():
|
||||
yield RarityFilter(4)
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_asData(placement):
|
||||
assert placement.asData() == {
|
||||
"type": "minecraft:rarity_filter",
|
||||
"chance": 4
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
from tungston.core.world import World
|
||||
from tungston.generator.markdown.report import Report
|
||||
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class MineralReport(Report):
|
||||
|
||||
def build(self):
|
||||
for mineral in self.world.minerals.all():
|
||||
self.line(f"# Mineral: {mineral.name}")
|
||||
self.line()
|
||||
|
||||
self.indent()
|
||||
for replacement in mineral.replacements:
|
||||
self.line(str(replacement))
|
||||
self.outdent()
|
||||
|
||||
self.line()
|
||||
@@ -0,0 +1,55 @@
|
||||
from pytest import fixture
|
||||
from tungston.core.geology.mineral import Mineral
|
||||
from tungston.core.geology.mineralcatalog import MineralCatalog
|
||||
from tungston.core.geology.replacement import Replacement
|
||||
from tungston.core.world import World
|
||||
from tungston.generator.markdown.mineralreport import MineralReport
|
||||
|
||||
import textwrap
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="copper")
|
||||
def createCopper():
|
||||
yield Mineral("copper", [
|
||||
Replacement("#minecraft:stone_replaceables", "minecraft:copper_ore"),
|
||||
Replacement("#minecraft:deepslate_replaceables", "minecraft:deepslate_copper_ore")
|
||||
])
|
||||
|
||||
@fixture(name="iron")
|
||||
def createIron():
|
||||
yield Mineral("iron", [
|
||||
Replacement("#minecraft:stone_replaceables", "minecraft:iron_ore"),
|
||||
Replacement("#minecraft:deepslate_replaceables", "minecraft:deepslate_iron_ore")
|
||||
])
|
||||
|
||||
@fixture(name="minerals")
|
||||
def createMineralCatalog(copper, iron):
|
||||
yield MineralCatalog([copper, iron])
|
||||
|
||||
@fixture(name="world")
|
||||
def createWorld(minerals):
|
||||
yield World(None, None, minerals, None)
|
||||
|
||||
@fixture(name="report")
|
||||
def createReport(world):
|
||||
report = MineralReport(world)
|
||||
report.build()
|
||||
yield report
|
||||
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_report(report):
|
||||
assert str(report) == textwrap.dedent("""
|
||||
# Mineral: copper
|
||||
|
||||
#minecraft:stone_replaceables => minecraft:copper_ore
|
||||
#minecraft:deepslate_replaceables => minecraft:deepslate_copper_ore
|
||||
|
||||
# Mineral: iron
|
||||
|
||||
#minecraft:stone_replaceables => minecraft:iron_ore
|
||||
#minecraft:deepslate_replaceables => minecraft:deepslate_iron_ore
|
||||
""").strip()
|
||||
@@ -0,0 +1,70 @@
|
||||
from collections.abc import Iterator
|
||||
from tungston.core.world import World
|
||||
|
||||
|
||||
# Constants ########################################################################################
|
||||
|
||||
INDENT = " "
|
||||
|
||||
# Class ############################################################################################
|
||||
|
||||
class Report:
|
||||
|
||||
def __init__(self, world:World):
|
||||
self.world = world
|
||||
self.reset()
|
||||
|
||||
def __str__(self) -> str:
|
||||
return "\n".join(self._lines).strip()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
"Report{" +
|
||||
f"lines: {len(self._lines)} lines, " +
|
||||
f"lineBuffer: {len(self._lineBuffer)} chunks, " +
|
||||
f"indent: {self._indent}" +
|
||||
"}"
|
||||
)
|
||||
|
||||
def build(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def reset(self) -> "Report":
|
||||
self._lineBuffer = []
|
||||
self._lines = []
|
||||
self._indent = 0
|
||||
return self
|
||||
|
||||
def asLines(self) -> Iterator[str]:
|
||||
for line in self._lines:
|
||||
yield line
|
||||
|
||||
# Helper Methods ###########################################################
|
||||
|
||||
def indent(self) -> "Report":
|
||||
self._indent += 1
|
||||
self._lineBuffer.insert(0, INDENT)
|
||||
return self
|
||||
|
||||
def line(self, text:str="") -> "Report":
|
||||
self.text(text)
|
||||
|
||||
self._lines.append("".join(self._lineBuffer))
|
||||
self._lineBuffer = []
|
||||
while len(self._lineBuffer) < self._indent:
|
||||
self._lineBuffer.append(INDENT)
|
||||
|
||||
return self
|
||||
|
||||
def outdent(self) -> "Report":
|
||||
self._indent = max(0, self._indent - 1)
|
||||
|
||||
if self._lineBuffer:
|
||||
if self._lineBuffer[0] == INDENT:
|
||||
self._lineBuffer.pop()
|
||||
|
||||
return self
|
||||
|
||||
def text(self, text:str) -> "Report":
|
||||
self._lineBuffer.append(str(text))
|
||||
return self
|
||||
@@ -0,0 +1,31 @@
|
||||
from pytest import fixture
|
||||
from tungston.generator.markdown.report import Report
|
||||
|
||||
|
||||
# Fixtures #########################################################################################
|
||||
|
||||
@fixture(name="emptyReport")
|
||||
def createEmptyReport():
|
||||
return Report()
|
||||
|
||||
@fixture(name="report")
|
||||
def createReport():
|
||||
return (
|
||||
Report(None)
|
||||
.text("alpha")
|
||||
.line("bravo")
|
||||
.indent()
|
||||
.line("charlie")
|
||||
.indent()
|
||||
.line("delta")
|
||||
.outdent()
|
||||
.line("echo")
|
||||
)
|
||||
|
||||
# Tests ############################################################################################
|
||||
|
||||
def test_repr(report):
|
||||
assert repr(report) == "Report{lines: 4 lines, lineBuffer: 1 chunks, indent: 1}"
|
||||
|
||||
def test_str(report):
|
||||
assert str(report) == "alphabravo\n charlie\n delta\n echo"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user