Skip to content

Commit

Permalink
Comments/notes, examples. Bash-autogeneration handler definition boil…
Browse files Browse the repository at this point in the history
…erplates.
  • Loading branch information
will-ca committed Dec 29, 2021
1 parent 1ee62be commit 627beb3
Show file tree
Hide file tree
Showing 26 changed files with 1,174 additions and 165 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def stringPathList(pathlist):
# @indexOf # Not actually totally sure what this is. I thought it was implemented in lists and tuples as `.index()`?
# '__setitem__', # Implemented through foreign request.
('__hash__', 'hash') # Monkey-patched into operator module above.
)
) # TODO: __int__, __float__, and other stuff missing from operator. https://docs.python.org/3/reference/datamodel.html

_rmagicmeths = (
'__radd__',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,23 @@

# If you modify this file, please add any new functions to Tests.py.

def stripJsonComments(text):
re.sub("//.*", "", re.sub('/\*.*?\*/', "", text, flags=re.DOTALL))

try:
t = re.sub("//.*", "", re.sub('/\*.*\*/', "", real(unciv.apiHelpers.App.assetFileString("jsons/Civ V - Gods & Kings/Terrains.json")), flags=re.DOTALL))
terrainsjson = json.loads(t)
terrainsjson = json.loads(stripJsonComments(real(unciv.apiHelpers.App.assetFileString("jsons/Civ V - Gods & Kings/Terrains.json"))))
# In an actual implementation, you would want to read from the ruleset instead of the JSON. But this is easier for me.
del t
except Exception as e:
print("Couldn't load terrains Terrains.json")
else:
terrainbases = {t['name']: t for t in terrainsjson if t['type'] in ('Water', 'Land')}
terrainfeatures = {t['name']: t for t in terrainsjson if t['type'] == 'TerrainFeature'}


def showProgress():
return progressupdater, finisher


def genValidTerrains(*, forbid=('Fallout',)):
#Searches only two layers deep. I.E. Only combinations with at most two TerrainFeatures will be found.
terrains = set()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@
Call clearTechTree() and then extendTechTree(iterations=20) at any time to replace all undiscovered parts of the base tech tree with an entire new randomly generated one.
Call scrambleTechTree() to keep all current technologies but randomize the order in which they are unlocked.
Call reorgTechTree(webifyTechTree()) to turn the current tech tree into a tech web.
"""

# from unciv_scripting_examples.ProceduralTechtree import *
# from unciv_scripting_examples.ProceduralTechtree import *; scrambleTechTree()
# from unciv_scripting_examples.ProceduralTechtree import *; reorgTechTree(webifyTechTree())
# tuple(real(t) for t in apiHelpers.instancesAsInstances[gameInfo.ruleSet.technologies['Civil Service'].prerequisites.toArray()])

# This means that some kind of handler for the modding API, once it's implemented, would have to be called before the JVM has a chance to crash, so the script can read its own serialized data out of GameInfo and inject items into the ruleset… You can already provably have a GameInfo with invalid values that doesn't crash until WorldScreen tries to render it, so running onGameLoad immediately after deserializing the save might be good enough.
# Or I guess there could be a Kotlin-side mechanism for serializing injected rules. But on the Kotlin side, I think it would be cleaner to just let the script handle everything. Such a mechanism still may not cover the entire range of wild behaviours that can be done by scripts, and the entire point of having a dynamic scripting API is to avoid having to statically hard-code niche or esoteric uses.


import random
import random, math

from unciv import *
from unciv_pyhelpers import *
Expand Down Expand Up @@ -135,3 +138,43 @@ def scrambleTechTree():
except: print(tname, ot)
tech.prerequisites.addAll([techreplacements[ot] for ot in originalpreqs[techpositions[tname]]])
# toprereqs, oprereqs = (real(t.prerequisites) for t in (tech, other))

def reorgTechTree(techmap):
Constructors = apiHelpers.Jvm.constructorByQualname
techs = techtree()
columns = {x: Constructors['com.unciv.models.ruleset.tech.TechColumn']() for x in set(x for (x, y, era) in techmap.values())}
columneras = {x: era for (x, y, era) in techmap.values()}
for x, column in columns.items():
column.era = columneras[x]
column.columnNumber = x
for name, (x, y, era) in techmap.items():
techs[name].row = y
techs[name].column = columns[x]

def webifyTechTree(*, coordscale=(0.5, 2.0), fuzz=1.0):
techlocs = {}
for name, tech in techtree().items():
techlocs[name] = (tech.column.columnNumber, tech.row)
if tech.hasUnique("Starting tech", None):
era = tech.column.era
startingpos = techlocs[name]
oldmaxrow = max(y for (x, y) in techlocs.values())
techlocs = {name: tuple(round(trig((y-startingpos[1]+fuz)/oldmaxrow*math.pi*2)*(x-startingpos[0])*cscale) for trig, cscale in zip((math.sin, math.cos), coordscale)) for name, (x, y) in techlocs.items() for fuz in (fuzz and random.random()*fuzz,)}
def inc(n, chance=1.0):
return (round(n-1) if n < 0 else round(n+1) if n > 0 else round(n+random.getrandbits(1)*2-1)) if random.random() < chance else n
mutatechances = max(coordscale)
mutatechances = tuple(c/mutatechances for c in coordscale)
for i in range(100):
if len(set(techlocs.values())) == len(techlocs):
break
for name, pos in techlocs.items():
if tuple(techlocs.values()).count(pos) > 1:
techlocs[name] = tuple(inc(c, cscale) for c, cscale in zip(pos, mutatechances))
else:
raise RuntimeError(f"Couldn't generate unique tech positions:\n{locals()}")
mincolumn = min(x for (x, y) in techlocs.values())
minrow = min(y for (x, y) in techlocs.values())
techmap = {name: (x-mincolumn, y-minrow+1, era) for name, (x, y) in techlocs.items()}
return techmap


Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,44 @@


# from unciv_scripting_examples.Tests import *; TestRunner.run_tests()
# import cProfile; cProfile.run("from unciv_scripting_examples.Tests import *; TestRunner.run_tests()", sort='tottime')
# from unciv_scripting_examples.Tests import *; InMapEditor.__enter__()
# from unciv_scripting_examples.Tests import *; import time; Start=time.time(); tryRun(TestRunner.run_tests); print(time.time()-Start)
# from unciv_scripting_examples.Tests import *; import timeit; x=timeit.repeat(stmt='tryRun(TestRunner.run_tests)', setup='tryRun(TestRunner.run_tests)', repeat=3, number=3, globals=globals()); print(x); unciv.apiHelpers.Sys.copyToClipboard(repr(x)); unciv.apiHelpers.Sys.printLine(repr(x))

# TODO: Protocol desync:
# import cProfile; cProfile.run("from unciv_scripting_examples.Tests import *; TestRunner.run_tests()", sort='cumtime')
# Ran first without sort, then with.
# Or just run twice.
# Wait, there's erroring due to unrelated handlers too…


# I have no idea how it managed to print out to the system terminal.
# 4932599 function calls (4929366 primitive calls) in 23.806 seconds

# Ordered by: internal time

# ncalls tottime percall cumtime percall filename:lineno(function)
# 40610 17.682 0.000 17.822 0.000 {method 'readline' of '_io.TextIOWrapper' objects}
# 1624812/1623564 0.764 0.000 0.869 0.000 wrapping.py:267(__getattribute__)
# 40610/40604 0.534 0.000 0.540 0.000 encoder.py:204(iterencode)
# 40657 0.440 0.000 0.440 0.000 {built-in method builtins.print}
# 75512 0.381 0.000 0.865 0.000 wrapping.py:231(__init__)
# 631561 0.358 0.000 0.358 0.000 wrapping.py:207(__setattr__)
# 75512 0.244 0.000 1.263 0.000 wrapping.py:244(_clone_)
# 35849 0.206 0.000 21.154 0.001 wrapping.py:250(_bakereal_)
# 40610/40604 0.183 0.000 20.482 0.001 ipc.py:70(GetForeignActionResponse)
# 40611 0.178 0.000 0.178 0.000 decoder.py:343(raw_decode)
# 40610 0.156 0.000 0.695 0.000 ipc.py:30(deserialized)
# 40610/40604 0.155 0.000 20.890 0.001 wrapping.py:17(meth)
# 40610/40604 0.135 0.000 0.859 0.000 __init__.py:183(dumps)
# 40611 0.116 0.000 0.392 0.000 decoder.py:332(decode)
# 40610/40604 0.114 0.000 0.682 0.000 encoder.py:182(encode)
# 27010 0.100 0.000 7.731 0.000 wrapping.py:257(__getattr__)
# 40610 0.099 0.000 0.134 0.000 ipc.py:13(makeUniqueId)

# Is it going to turn out that Kotlin, not Python, is the bottleneck? I suppose that wouldn't be wholly a bad thing; It also means that the API design is sanely keeping the heavier lifting in the compiled part.


def tryRun(func):
# For debug and manual runs. Not used otherwise.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@

# Precedent for injected unique: "Requires a [] in all cities".
# Since uniques are transient and such, probably inject into promotions. (Except that checks against the ruleset.)
# Inject into temporaryUniques.
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@

RegistryKey = "python-package:Unciv/unciv_scripting_examples"

unciv.apiHelpers.registeredInstances[RegistryKey] = {}
registeredInstances = unciv.apiHelpers.registeredInstances

memalloc = unciv.apiHelpers.registeredInstances[RegistryKey]
if RegistryKey not in registeredInstances:
registeredInstances[RegistryKey] = {}

memalloc = registeredInstances[RegistryKey]


def singleton(*args, **kwargs):
Expand Down
2 changes: 1 addition & 1 deletion core/src/com/unciv/UncivGameParameters.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ class UncivGameParameters(val version: String,
val consoleMode: Boolean = false,
val customSaveLocationHelper: CustomSaveLocationHelper? = null,
val limitOrientationsHelper: LimitOrientationsHelper? = null,
val runScriptAndExit: Triple<ScriptingBackendType, String, ((ExecResult) -> Unit)?>? = null
val runScriptAndExit: Triple<ScriptingBackendType, String, ((ExecResult) -> Unit)?>? = null // TODO: Probably make exit optional? Or just make the scripts do it themselves.
) { }
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.unciv.models.modscripting

object ModScriptingDebugParameters {
object ModScriptingDebugParameters { // Enum-ifying this could let tests iterate through them exhaustively.
// Whether to print when script handlers are triggered.
var printHandlerRun = false // Const val? Faster. But I like the idea of the scripting API itself being able to change these. // Bah. Premature optimization. There are far slower places to worry about speed.
var printHandlerRun = true // Const val? Faster. But I like the idea of the scripting API itself being able to change these. // Bah. Premature optimization. There are far slower places to worry about speed.
//
var printHandlerRegister = false
var printModRead = false
var printModRegister = false

var typeCheckHandlerParamsAtRuntime = true
}
Loading

0 comments on commit 627beb3

Please sign in to comment.