diff --git a/Forge.py b/Forge.py new file mode 100644 index 00000000..bdcc1072 --- /dev/null +++ b/Forge.py @@ -0,0 +1,74 @@ +#Main file. Hooks into high level world/render updates +from pdb import set_trace as T +import argparse + +import experiments +from forge.trinity import smith, Trinity, Pantheon, God, Sword + +def parseArgs(): + parser = argparse.ArgumentParser('Projekt Godsword') + parser.add_argument('--nRealm', type=int, default='1', + help='Number of environments (1 per core)') + parser.add_argument('--api', type=str, default='native', + help='API to use (native/vecenv)') + parser.add_argument('--ray', type=str, default='default', + help='Ray mode (local/default/remote)') + parser.add_argument('--render', action='store_true', default=False, + help='Render env') + return parser.parse_args() + +#Example runner using the (slower) vecenv api +#The actual vecenv spec was not designed for +#multiagent, so this is a best-effort facsimile +class GymExample: + def __init__(self, config, args): + self.env = smith.VecEnv(config, args, self.step) + #The environment is persistent. Reset only to start it. + self.envsObs = self.env.reset() + + #the ANN used internally by Trinity + from forge.trinity import ANN + self.ann = ANN(config) + + #Runs a single step of each environment + #With slow comms at each step + def step(self): + actions = [] + for obs in self.envsObs: #Environment + atns = [] + for ob in obs: #Agent + ent, stim = ob + action, arguments, atnArgs, val = self.ann(ent, stim) + atns.append((ent.entID, action, arguments, float(val))) + actions.append(atns) + self.envsObs, rews, dones, infos = self.env.step(actions) + + def run(self): + while True: + self.step() + +class NativeExample: + def __init__(self, config, args): + trinity = Trinity(Pantheon, God, Sword) + self.env = smith.Native(config, args, trinity) + + def run(self): + while True: + self.env.run() + +if __name__ == '__main__': + args = parseArgs() + assert args.api in ('native', 'vecenv') + config = experiments.exps['testchaos128'] + + if args.api == 'native': + example = NativeExample(config, args) + elif args.api == 'vecenv': + example = GymExample(config, args) + + #Rendering by necessity snags control flow + #This will automatically set local mode with 1 core + if args.render: + example.env.render() + + example.run() diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..cb36e12e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 OpenAI + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 00000000..5e8d7e33 --- /dev/null +++ b/README.md @@ -0,0 +1,205 @@ +[ags]: resource/docs/ags.png?raw=true +[earth]: resource/docs/earth_thumbnail.png +[fire]: resource/docs/fire_thumbnail.png +[water]: resource/docs/water_thumbnail.png +[air]: resource/docs/air_thumbnail.png +[env]: resource/docs/env.jpg + +# Neural MMO + +This environment is the first neural MMO; it attempts to create agents that scale to real world complexity. Simulating evolution on Earth is computationally infeasible, but we can construct a reasonable and efficient facsimile. We consider MMORPGs (Massive Multiplayer Online Role Playing Games) the best proxy for the real world among human games: they are complete macrocosms featuring thousands of agents per persistent world, diverse skilling systems, global economies, and ad-hoc high stakes single and team based conflict. + +![][env] + +## ![][ags] Quickstart + +``` +# Recommended Setup: +mkdir projekt +cd projekt + +git clone https://github.com/jsuarez5341/neural-mmo-client +cd neural-mmo-client +bash setup.sh +cd .. + +git clone https://github.com/openai/neural-mmo +cd neural-mmo +python setup.py +``` + +This will download both the OpenAI environment and the independent client, which is required for rendering. Some setups may require you fix the symlink to the client (e.g. ln -s ../../neural-mmo-client/ embyr from forge/embyr). We assume you have Anaconda setup -- the setup file will only install the python dependencies not included in Anaconda. The environment is framework independently, but our experiment code does depend on PyTorch -- set this up separately. + +Run the following, then navigate to http://localhost:8080/forge/ in Firefox (Chrome may not work) to pull up the renderer. Click to start and give it a few seconds to load assets and stabilize fps. For troubleshooting, see the [Client Repo](https://github.com/jsuarez5341/neural-mmo-client). +``` +python Forge.py --render #Run the environment with rendering on +``` + +## ![][ags] Overview + +The project is divided into four modules: + +| Engineering | Research | +| ------------- |-------------| +| ![][earth] Blade: Env | ![][water] Trinity: API | +| ![][fire] Embyr: Render | ![][air] Ethyr: Neural | + +The objective is to create agents that scale to the complexity and robustness of the real world. This is a variant phrasing of "artificial life." A key perspective of the project is decoupling this statement into subproblems that are concrete, feasible, and directly composable to solve the whole problem. We split the objective into "agents that scale to their environment" and "environments that scale to the real world." These are large respective research and engineering problems, but unlike the original objective, they are specific enough to attempt individually. For a more thorough overview of the project approach and objective, see this [Two Pager](https://docs.google.com/document/d/1_76rYTPtPysSh2_cFFz3Mfso-9VL3_tF5ziaIZ8qmS8/edit?usp=sharing). + +![][water]![][air] Research: Agents that scale to env complexity + +![][earth]![][fire] Engineering: Env that scales to real world complexity + +### ![][water] Trinity +Trinity is the native API for researchers (the naming is simply flavor -- see "Namesake" below). It consists of three base classes, Pantheon, God, and Sword, which you can override to execute code at the Cluster, Server, and Agent levels, respectively. + +```python +from forge.trinity import smith, Trinity, Pantheon, God, Sword +trinity = Trinity(Pantheon, God, Sword) +envs = smith.Native(config, args, trinity) +envs.run() +``` +That's it -- all communications are handled internally. The provided Pantheon class includes sample gradient aggregation, optimization, and model saving code. The provided God class is mainly a stub to enable future work incorporating population level training algorithms. The provided Sword class contains our simple fully connected model. The full API is defined in forge/trinity/trinity.py. + +The VecEnv/Gym computation model not well suited to this setting and performs 10-1000x more interprocess communication than is required. The native API is simpler, more efficient, and requires much less code. However, we do provide a canonical Gym API minimally modified to support a variable number of agents. Forge.py contains a complete forward pass example for a single population. Basic usage is: + +```python +from forge.trinity import smith +envs = smith.VecEnv(config, args, self.step) + +#The environment is persistent: call reset only upon initialization +obs = envs.reset() + +#Observations contain entity and stimulus +#for each agent in each environment. +actions = your_algorithm_here(obs) + +#The environment is persistent: "dones" is always None +#If an observation is missing, that agent has died +obs, rewards, dones, infos = envs.step(actions) +``` + +You can try these both out with: +``` +# Run Options: +python Forge.py --nRealm 2 --api native #Run 2 environments with native API +python Forge.py --nRealm 2 --api vecenv #Run 2 environments with vecenv API +``` + +### ![][air] Ethyr +Ethyr is the "contrib" or this project. It contains useful research tools for interacting with the project. I've seeded it with the helper classes for our experiments, including a model save/load manager, a rollout objects, and a basic optimizer. If you would like to contribute code (in any framework, not just PyTorch), please submit a pull request. + +### ![][earth] Blade +Blade is the core environment, including game state and control flow. Researchers should not need to touch this, outside perhaps importing core configurations and enums. +### ![][fire] Embyr +Embyr is the independent THREE.js web client. This is downloaded from a separate repository in the setup above and symlinked to the OpenAI repository. You may have to fix the symbolic link if you get forge/embyr import errors. Again, researchers should not have to edit this. In order to run it, run Forge.py with --render enabled, then navigate to localhost:8080/forge/embyr in Firefox. It will take a couple seconds to initialize and load assets. You will need to refresh the page whenever you reboot the server (Forge.py). Chrome and Safari might work, but we do not currently offer official support. + +## ![][ags] Failure Modes +Evaluation can be somewhat difficult in our setting but is not a major blocker. For smaller experiments, we find population size and resource utilization to be reasonable metrics of success. For larger experiments with sufficient domain randomization, Tournaments (as described in the accompanying paper) allow for cross validation of approaches. + +We are currently aware of three failure cases for the project: + * Computational infeasibility + * "Agents that scale to their environment" is too hard + * "Environments that scale to the real world" is too hard + +The first failure case is a serious risk, but is shared among all areas of the field. This project is not uniquely compute intensive -- in fact, it is one of few environments where it is straightforward to train reasonable policies on a single CPU. If scale is the main issue here, it is likely shared among most if not all other approaches. + +The second problem is probably most familiar to researchers as exploration. Given a cold start, how can agents bootstrap both to better policies and to better exploration strategies? This is a hard problem, but it is unlikely to kill the project because: + * This is independently an important problem that many researchers are already working on already + * The environment of this project is designed collaboratively to assist agents early on in learning, rather than adversarially as a hard benchmark + * [Recent](https://blog.openai.com/openai-five/) [projects](https://blog.openai.com/learning-dexterity/) have demonstrated success at scale. + +The third problem probably appears most likely to many researchers, but least likely to anyone who has spent a significant amount of time in MMOs. Here is a map of the NYC subway: + +![QuestMap](resource/docs/quests.png) +[Source](https://www.reddit.com/user/Gamez_X) + +Actually, it's a quest map of Runescape, a particular MMO that our environment is loosely based upon. Each quest is a puzzle in itself, takes anywhere from several minutes to several hours to complete, is part of an interconnected web of prerequisites of other quests, and provides different incentives for completion ranging from equipment to unlockable content to experience in a tightly connected set of skills: + +![Skills](resource/docs/skills.png) +![Equipment](resource/docs/equipment.png) +[Source](https://www.jagex.com/en-GB/) + +In a massive open world: + +![GameMap](resource/docs/map.png) +[Source](https://www.jagex.com/en-GB/) + +The most complex class of games considered to date is MOBAs (Massive Online Battle Arenas, e.g. Dota, Quake CTF), which are round based, take on order of an hour, and are mechanically intensive. Achieving 99 in all skills and acquiring the best gear in Runescape takes, at minimum, several thousand hours. In a tournament setting where attacking other players is allowed everywhere, moment-to-moment gameplay is less important than balancing the risks and rewards of any potential strategy--especially in the presence of hundreds of other players attempting to do the same. There is almost certainly still a complexity gap from MMOs to the real world, but we believe it is much smaller than that in environments currently available. + +While our environment is nowhere near the level of complexity of a real MMO yet, it does contain key properties of persistence, population scale, and open-endedness. As agents begin to reach the ceiling of the current environment, we plan on continuing development to raise the ceiling. + +## ![][ags] File Structure +![][water] **/forge/trinity** ~350 lines +* **/forge/trinity/ann.py** - Defines architectures +* **/forge/trinity/god.py** - Defines server level code (e.g. entity tagging) +* **/forge/trinity/pantheon.py** - Defines cluster level code (e.g. gradient averaging) +* **/forge/trinity/sword.py** - Defines core level code (e.g. running networks, collecting rollouts, computing gradients) +* **/forge/trinity/trinity.py** - Wraps a pantheon, god, and sword +* **/forge/trinity/smith.py** - Defines the Native and VecEnv / Gym APIs + +![][air] **/forge/ethyr** ~250 lines +* **/forge/ethyr/rollouts.py** - Collects and merges rollouts +* **/forge/ethyr/stim.py** - Produces a stimulus from local game state +* **/forge/ethyr/torch** - pytorch specific neural utilities + * **/forge/ethyr/torch/loss.py** - Defines policy/value loss and advantage + * **/forge/ethyr/torch/optim.py** - Defines optimization and gradient computation + * **/forge/ethyr/torch/param.py** - Network surgery useful for serialization + * **/forge/ethyr/torch/stim.py** - Wraps the generic stimulus library with pytorch tensors + * **/forge/ethyr/torch/utils.py** - Generic pytorch tools + +![][earth] **/forge/blade** ~2k lines, of which >1k are for future expansion. Only italicized files are relevant. + * **/forge/blade/action** - Defines what entities can do, what occurs when they do it, and provides structure for reasoning over actions. + * **/forge/blade/action/action.py** - Class stubs for each action + * **/forge/blade/action/tree.py** - Tree structure for assembling actions (e.g. action -> sub-action -> args) + * **_/forge/blade/action/v2.py_** - Actions that entities can select, instantiate, and .call() to directly modify world state + * **/forge/blade/core** — Contains ~500 lines of state and game loop code. + * **_/forge/blade/core/config.py_** - Defines specifications for each test environment, including entity statistics, the map, and spawn locations. + * **_/forge/blade/core/tile.py_** - Defines an individual game tile + * **_/forge/blade/core/map.py_** - Defines a map of game tiles + * **_/forge/blade/core/env.py_** - Wraps the full game state + * **_/forge/blade/core/realm.py_** - Defines the game loop updating the environment and agents. + * **/forge/blade/entity** - Defines agents, both neural and scripted + * **_/forge/blade/entity/player.py_** — Defines "player" state + * **/forge/blade/entity/npc/** + * **/forge/blade/entity/npc/npc.py** — This defines client state (e.g. wraps the neural net making decisions) + * **/forge/blade/entity/npc/mobs.py** - Defines scripted npcs + * **/forge/blade/hook** - Defines startup scripts that preassemble references + * **/forge/blade/item** - Defines all items in the game, including equipment + * **/forge/blade/lib** - Defines generic utilities that either do not have clean python implementations or require specific changes for the project + * **/forge/blade/systems** - Defines game content + +![][fire] **/forge/embyr** See [Client Repo](https://github.com/jsuarez5341/Godsword-Client) + +## ![][ags] Namesake +In formal publications, we refer to our project as simply a "Neural MMO." Internally and informally, we call it "Projekt: Godsword." The name comes from two sources: CD Projekt Red, my personal favorite game dev studio, and OldSchool Runescape, which contains an iconic set of weapons called god swords. The latter is a particularly good model for AI environments; the former is more of a soft flavor inspiration. + +## ![][ags] Authorship + I, [Joseph Suarez](https://github.com/jsuarez5341), am the author of the core code base. Yilun Du assisted with running experiments and particularly in setting up tournaments. Phillip Isola and Igor Mordatch have been invaluable collaborators and advisers throughout the project. The client was developed independently as a collaboration between myself and Clare Zhu. + +## ![][ags] License & Assets +The OpenAI repository is open sourced under the MIT License. There is a smaller original code base and game kernel that I (Joseph Suarez) retain ownership of, along with associated ideas. I created these before my employment -- the initial commit here represents the latest pre-employment timestep. + +Some assets used in this project belong to [Jagex](https://www.jagex.com/en-GB/) +, the creators of Runescape, such as + +![Alt text](resource/docs/ags.png?raw=true "Title") +![Alt text](resource/docs/earth_thumbnail.png?raw=true "Title") +![Alt text](resource/docs/water_thumbnail.png?raw=true "Title") +![Alt text](resource/docs/fire_thumbnail.png?raw=true "Title") +![Alt text](resource/docs/air_thumbnail.png?raw=true "Title") + +We currently use them for flavor as an homage to the game that inspired the project. We believe these fall under fair use as a not-for-profit project for the advancement of artificial intelligence research -- however, we are more than happy to remove them upon request. We do own the 2D and 3D files for agents. + +![Alt text](resource/docs/neuralRED.png?raw=true "Title") +![Alt text](resource/docs/neuralBLUE.png?raw=true "Title") +![Alt text](resource/docs/neuralGREEN.png?raw=true "Title") +![Alt text](resource/docs/neuralFUCHSIA.png?raw=true "Title") +![Alt text](resource/docs/neuralORANGE.png?raw=true "Title") +![Alt text](resource/docs/neuralMINT.png?raw=true "Title") +![Alt text](resource/docs/neuralPURPLE.png?raw=true "Title") +![Alt text](resource/docs/neuralSPRING.png?raw=true "Title") +![Alt text](resource/docs/neuralYELLOW.png?raw=true "Title") +![Alt text](resource/docs/neuralCYAN.png?raw=true "Title") +![Alt text](resource/docs/neuralMAGENTA.png?raw=true "Title") +![Alt text](resource/docs/neuralSKY.png?raw=true "Title") diff --git a/configs.py b/configs.py new file mode 100644 index 00000000..8bd4824b --- /dev/null +++ b/configs.py @@ -0,0 +1,63 @@ +from pdb import set_trace as T +from forge.blade.core.config import Config +from forge.blade.lib import utils +import numpy as np + +class Experiment(Config): + def defaults(self): + super().defaults() + self.MODELDIR='resource/logs/' + self.HIDDEN = 32 + self.TEST = False + self.LOAD = False + self.BEST = False + self.SAMPLE = False + self.NATTN = 2 + self.NPOP = 1 + self.SHAREINIT = False + self.ENTROPY = 0.01 + self.VAMPYR = 1 + self.AUTO_TARGET = False + +#Foraging only +class Law(Experiment): + def defaults(self): + super().defaults() + + #Damage + def MELEEDAMAGE(self, ent, targ): return 0 + def RANGEDAMAGE(self, ent, targ): return 0 + def MAGEDAMAGE(self, ent, targ): return 0 + +#Foraging + Combat +class Chaos(Experiment): + def defaults(self): + super().defaults() + self.RANGERANGE = 2 + self.MAGERANGE = 3 + + def vamp(self, ent, targ, frac, dmg): + dmg = int(frac * dmg) + targ.food.decrement(amt=dmg) + targ.water.decrement(amt=dmg) + ent.food.increment(amt=dmg) + ent.water.increment(amt=dmg) + + #Damage formulas. Lambdas don't pickle well + def MELEEDAMAGE(self, ent, targ): + dmg = 10 + targ.applyDamage(dmg) + self.vamp(ent, targ, self.VAMPYR, dmg) + return dmg + + def RANGEDAMAGE(self, ent, targ): + dmg = 2 + targ.applyDamage(dmg) + self.vamp(ent, targ, self.VAMPYR, dmg) + return dmg + + def MAGEDAMAGE(self, ent, targ): + dmg = 1 + targ.applyDamage(dmg) + self.vamp(ent, targ, self.VAMPYR, dmg) + return dmg diff --git a/experiments.py b/experiments.py new file mode 100644 index 00000000..dc5985d4 --- /dev/null +++ b/experiments.py @@ -0,0 +1,60 @@ +from pdb import set_trace as T +from configs import Law, Chaos +import os + +#Oversimplified user specification +#for 1-3 collaborators +USER = 'your-username' +if USER == 'your-username': + #Thousandth + prefix = 'test' + remote = False + local = not remote + + test = True#local + best = True#local + load = True#local + + sample = not test + singles = True + tournaments = False + + exps = {} + szs = [128] + #For full distributed runs + #szs = (16, 32, 64, 128) + names = 'law chaos'.split() + confs = (Law, Chaos) + + def makeExp(name, conf, sz, test=False): + NENT, NPOP = sz, sz//16 + ROOT = 'resource/exps/' + name + '/' + try: + os.mkdir(ROOT) + os.mkdir(ROOT + 'model') + os.mkdir(ROOT + 'train') + os.mkdir(ROOT + 'test') + except FileExistsError: + pass + MODELDIR = ROOT + 'model/' + + exp = conf(remote, + NENT=NENT, NPOP=NPOP, + MODELDIR=MODELDIR, + SAMPLE=sample, + BEST=best, + LOAD=load, + TEST=test) + exps[name] = exp + print(name, ', NENT: ', NENT, ', NPOP: ', NPOP) + + def makeExps(): + #Training runs + for label, conf in zip(names, confs): + for sz in szs: + name = prefix + label + str(sz) + makeExp(name, conf, sz, test=test) + + #Sample config + makeExps() + makeExp('sample', Chaos, 128, test=True) diff --git a/figures.py b/figures.py new file mode 100644 index 00000000..2c739946 --- /dev/null +++ b/figures.py @@ -0,0 +1,159 @@ +import numpy as np +import sys, json +from forge.blade.lib.enums import Neon, Color256 +from forge.blade.lib.log import InkWell +from matplotlib import pyplot as plt +from pdb import set_trace as T +from itertools import groupby +from collections import defaultdict +import collections +import logs as loglib +import experiments +import pickle +import os.path as osp +import os + +def plot(x, idxs, label, idx, path): + colors = Neon.color12() + loglib.dark() + c = colors[idx % 12] + loglib.plot(x, inds=idxs, label=str(idx), c=c.norm) + loglib.godsword() + loglib.save(path + label + '.png') + plt.close() + +def plots(x, label, idx, path, split): + colors = Neon.color12() + loglib.dark() + for idx, item in enumerate(x.items()): + annID, val = item + c = colors[idx % 12] + idxs, val = compress(val, split) + loglib.plot(val, inds=idxs, label=str(annID), c=c.norm) + loglib.godsword() + loglib.save(path + label + '.png') + plt.close() + +def meanfilter(x, n=1): + ret = [] + for idx in range(len(x) - n): + val = np.mean(x[idx:(idx+n)]) + ret.append(val) + return ret + +def compress(x, split): + rets, idxs = [], [] + if split == 'train': + n = 1 + len(x) // 20 + else: + n = 1 + len(x) // 20 + for idx in range(0, len(x) - n, n): + rets.append(np.mean(x[idx:(idx+n)])) + idxs.append(idx) + return 10*np.array(idxs), rets + +def popPlots(popLogs, path, split): + idx = 0 + print(path) + + for key, val in popLogs.items(): + print(key) + #val = meanfilter(val, 1+len(val)//100) + plots(val, str(key), idx, path, split) + idx += 1 + +def flip(popLogs): + ret = defaultdict(dict) + for annID, logs in popLogs.items(): + for key, log in logs.items(): + if annID not in ret[key]: + ret[key][annID] = [] + if type(log) != list: + ret[key][annID].append(log) + else: + ret[key][annID] += log + return ret + +def group(blobs, idmaps): + rets = defaultdict(list) + for blob in blobs: + groupID = idmaps[blob.annID] + rets[groupID].append(blob) + return rets + +def mergePops(blobs, idMap): + #blobs = sorted(blobs, key=lambda x: x.annID) + #blobs = dict(blobs) + #idMap = {} + #for idx, accumList in enumerate(accum): + # for e in accumList: + # idMap[e] = idx + + blobs = group(blobs, idMap) + pops = defaultdict(list) + for groupID, blobList in blobs.items(): + pops[groupID] += list(blobList) + return pops + +def individual(blobs, logDir, name, accum, split): + savedir = logDir + name + '/' + split + '/' + if not osp.exists(savedir): + os.makedirs(savedir) + + blobs = mergePops(blobs, accum) + popLogs = {} + for annID, blobList in blobs.items(): + logs, blobList = {}, list(blobList) + logs = {**logs, **InkWell.counts(blobList)} + logs = {**logs, **InkWell.unique(blobList)} + logs = {**logs, **InkWell.explore(blobList)} + logs = {**logs, **InkWell.lifetime(blobList)} + logs = {**logs, **InkWell.reward(blobList)} + logs = {**logs, **InkWell.value(blobList)} + popLogs[annID] = logs + + popLogs = flip(popLogs) + popPlots(popLogs, savedir, split) + +def makeAccum(config, form='single'): + assert form in 'pops single split'.split() + if form == 'pops': + return dict((idx, idx) for idx in range(config.NPOP)) + elif form == 'single': + return dict((idx, 0) for idx in range(config.NPOP)) + elif form == 'split': + pop1 = dict((idx, 0) for idx in range(config.NPOP1)) + pop2 = dict((idx, 0) for idx in range(config.NPOP2)) + return {**pop1, **pop2} + +if __name__ == '__main__': + arg = None + if len(sys.argv) > 1: + arg = sys.argv[1] + + logDir = 'resource/exps/' + logName = '/model/logs.p' + fName = 'frag.png' + name = 'newfig' + exps = [] + for name, config in experiments.exps.items(): + try: + with open(logDir + name + logName, 'rb') as f: + dat = [] + idx = 0 + while True: + idx += 1 + try: + dat += pickle.load(f) + except EOFError as e: + break + print('Blob length: ', idx) + split = 'test' if config.TEST else 'train' + accum = makeAccum(config) + individual(dat, logDir, name, accum, split) + print('Log success: ', name) + except Exception as err: + print(str(err)) + + + diff --git a/forge/__init__.py b/forge/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/forge/blade/__init__.py b/forge/blade/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/forge/blade/action/action.py b/forge/blade/action/action.py new file mode 100644 index 00000000..70e11637 --- /dev/null +++ b/forge/blade/action/action.py @@ -0,0 +1,56 @@ +class Pass: + pass + +class Move: + pass + +class Reproduce: + pass + +class Attack: + pass + +class Range(Attack): + pass + +class Mage(Attack): + pass + +class Melee(Attack): + pass + +class Skill: + pass + +class Harvest: + pass + +class Fish: + pass + +class Mine: + pass + +class Process: + pass + +class Cook: + pass + +class Smith: + pass + +class Exchange: + pass + +class Buy: + pass + +class Sell: + pass + +class CancelOffer: + pass + +class Message: + pass diff --git a/forge/blade/action/tree.py b/forge/blade/action/tree.py new file mode 100644 index 00000000..4f3284d3 --- /dev/null +++ b/forge/blade/action/tree.py @@ -0,0 +1,70 @@ +class ActionNode: + def edges(self, world, entity): + pass + +class ActionLeaf(ActionNode): + def __call__(self, world, entity, *args): + if type(args) == EmptyArgs: + args = [] + +class Args: + isEmpty = False + isSet = False + isDiscrete = False + def __init__(self, value=None): + self.value = value + +class Arg(Args): pass + +class SetArgs(Args): + isSet = True + + def __init__(self): + self.setValue = set() + + def add(self, value): + self.setValue.add(value) + + def toList(self): + return list(self.setValue) + + @property + def empty(self): + return len(self.toList()) == 0 + +class DiscreteArgs(SetArgs): + isDiscrete = True + +class EmptyArgs(Args): + isEmpty = True + +class ActionTree: + def __init__(self, world, entity, rootVersion): + self.world, self.entity = world, entity + self.root, self.args = rootVersion(), None + + @property + def n(self): + return sum([a.nArgs() for a in self.action.edges()]) + + def flat(self): + rets = [] + for action in self.root.edges(None, None): + for args in action.args(None, None): + rets.append((action, args)) + return rets + + def actions(self): + return self.root.edges(None, None) + + def rand(self): + nodes = self.flat() + ind = np.random.randint(0, len(nodes)) + return nodes[ind] + + def actionArgPair(self): + assert self.action is not None and self.args is not None + if type(self.args) not in (list, tuple): + self.args = [self.args] + return type(self.action), self.args + diff --git a/forge/blade/action/v2.py b/forge/blade/action/v2.py new file mode 100644 index 00000000..f67dcde7 --- /dev/null +++ b/forge/blade/action/v2.py @@ -0,0 +1,127 @@ +from pdb import set_trace as T +from forge.blade.action import action +from forge.blade.lib import utils, enums +import numpy as np + +class Arg: + def __init__(self, val, discrete=True, set=False, min=-1, max=1): + self.val = val + self.discrete = discrete + self.continuous = not discrete + self.min = min + self.max = max + self.n = self.max - self.min + 1 + +class ActionV2: + def edges(self, world, entity, inclusive=False): + return [Pass, Move, Attack]#, Ranged] + +class Pass(action.Pass): + priority = 0 + + @staticmethod + def call(world, entity): + return + + def args(stim, entity, config): + return [()] + + @property + def nArgs(): + return 1 + +class Move(action.Move): + priority = 1 + def call(world, entity, rDelta, cDelta): + r, c = entity.pos + rNew, cNew = r+rDelta, c+cDelta + if world.env.tiles[rNew, cNew].state.index in enums.IMPASSIBLE: + return + if not utils.inBounds(rNew, cNew, world.shape): + return + if entity.freeze > 0: + return + + entity._pos = rNew, cNew + entID = entity.entID + + r, c = entity.lastPos + world.env.tiles[r, c].delEnt(entID) + + r, c = entity.pos + world.env.tiles[r, c].addEnt(entID, entity) + + def args(stim, entity, config): + rets = [] + for delta in ((0, 0), (0, 1), (1, 0), (0, -1), (-1, 0)): + r, c = delta + #r, c = Arg(r), Arg(c) + rets.append((r, c)) + return rets + + @property + def nArgs(): + return len(Move.args(None, None)) + +class Attack(action.Attack): + def inRange(entity, stim, N): + R, C = stim.shape + R, C = R//2, C//2 + #R, C = entity.pos + + rets = [] + for r in range(R-N, R+N+1): + for c in range(C-N, C+N+1): + for e in stim[r, c].ents.values(): + rets.append(e) + return rets + + def l1(pos, cent): + r, c = pos + rCent, cCent = cent + return abs(r - rCent) + abs(c - cCent) + + def call(world, entity, targ, damageF, freeze=False): + if entity.entID == targ.entID: + entity._attack = None + return + #entity.targPos = targ.pos + #entity.attkPos = entity.lastPos + #entity.targ = targ + damage = damageF(entity, targ) + assert type(damage) == int + if freeze and damage > 0: + targ._freeze = 3 + return + #return damage + + def args(stim, entity, config): + return [Melee, Range, Mage] + #return Melee.args(stim, entity, config) + Range.args(stim, entity, config) + Mage.args(stim, entity, config) + +class Melee(action.Melee): + priority = 2 + def call(world, entity, targ): + damageF = world.config.MELEEDAMAGE + Attack.call(world, entity, targ, damageF) + + def args(stim, entity, config): + return Attack.inRange(entity, stim, config.MELEERANGE) + +class Range(action.Range): + priority = 2 + def call(world, entity, targ): + damageF = world.config.RANGEDAMAGE + Attack.call(world, entity, targ, damageF) + + def args(stim, entity, config): + return Attack.inRange(entity, stim, config.RANGERANGE) + +class Mage(action.Mage): + priority = 2 + def call(world, entity, targ): + damageF = world.config.MAGEDAMAGE + dmg = Attack.call(world, entity, targ, damageF, freeze=True) + + def args(stim, entity, config): + return Attack.inRange(entity, stim, config.MAGERANGE) diff --git a/forge/blade/core/__init__.py b/forge/blade/core/__init__.py new file mode 100644 index 00000000..509a1a08 --- /dev/null +++ b/forge/blade/core/__init__.py @@ -0,0 +1,4 @@ +from .env import Env +from .map import Map +from .realm import VecEnvRealm, NativeRealm +from .tile import Tile diff --git a/forge/blade/core/config.py b/forge/blade/core/config.py new file mode 100644 index 00000000..05fd718f --- /dev/null +++ b/forge/blade/core/config.py @@ -0,0 +1,48 @@ +from pdb import set_trace as T +import numpy as np + +class Config: + def __init__(self, remote=False, **kwargs): + self.defaults() + for k, v in kwargs.items(): + setattr(self, k, v) + + if remote: + self.ROOT = '/root/code/Projekt-Godsword/' + self.ROOT + + def defaults(self): + self.ROOT = 'resource/maps/procedural/map' + self.SUFFIX = '/map.tmx' + self.BORDER = 9 + self.SZ = 62 + self.R = self.SZ + self.BORDER + self.C = self.SZ + self.BORDER + + self.STIM = 7 + self.NENT = 256 + + #Base agent stats + self.HEALTH = 10 + self.FOOD = 32 + self.WATER = 32 + + #Attack ranges + self.MELEERANGE = 1 + self.RANGERANGE = 4 + self.MAGERANGE = 4 + + def SPAWN(self): + R, C = self.R, self.C + spawn, border, sz = [], self.BORDER, self.SZ + spawn += [(border, border+i) for i in range(sz)] + spawn += [(border+i, border) for i in range(sz)] + spawn += [(R-1, border+i) for i in range(sz)] + spawn += [(border+i, C-1) for i in range(sz)] + idx = np.random.randint(0, len(spawn)) + return spawn[idx] + + #Damage formulas. Lambdas don't pickle well + def MELEEDAMAGE(self, ent, targ): return 0 + def RANGEDAMAGE(self, ent, targ): return 0 + def MAGEDAMAGE(self, ent, targ): return 0 + diff --git a/forge/blade/core/env.py b/forge/blade/core/env.py new file mode 100644 index 00000000..c2f8d8ca --- /dev/null +++ b/forge/blade/core/env.py @@ -0,0 +1,58 @@ +#Main world definition. Defines and manages entity handlers, +#Defines behavior of the world under all circumstances and handles +#interaction by agents. Also defines an accurate stimulus that +#encapsulates the world as seen by a particular agent + +import numpy as np + +from forge.blade import systems +from forge.blade.lib import utils +from forge.blade import lib + +from forge.blade import core +from forge.blade.item import rawfish, knife, armor +from pdb import set_trace as T + +class Env: + def __init__(self, config, idx): + #Load the world file + self.env = core.Map(config, idx) + self.shape = self.env.shape + self.spawn = config.SPAWN + self.config = config + + #Entity handlers + self.stimSize = 3 + self.worldDim = 2*self.stimSize+1 + + #Exchange - For future updates + self.market = systems.Exchange() + sardine = rawfish.Sardine + nife = knife.Iron + armr = armor.Iron + self.market.buy(sardine, 10, 30) + self.market.sell(nife, 20, 50) + self.market.buy(armr, 1, 500) + self.market.sell(armr, 3, 700) + + self.stats = lib.StatTraker() + + self.tick = 0 + self.envTimer = utils.BenchmarkTimer() + self.entTimer = utils.BenchmarkTimer() + self.cpuTimer = utils.BenchmarkTimer() + self.handlerTimer = utils.BenchmarkTimer() + self.statTimer = utils.BenchmarkTimer() + + def stim(self, pos): + return self.env.getPadded(self.env.tiles, pos, + self.stimSize, key=lambda e:e.index).astype(np.int8) + + #Hook for render + def graphicsData(self): + return self.env, self.stats + + def step(self, pcs, npcs): + self.stats.update(pcs, npcs, self.market) + self.tick += 1 + diff --git a/forge/blade/core/map.py b/forge/blade/core/map.py new file mode 100644 index 00000000..c21bc1bf --- /dev/null +++ b/forge/blade/core/map.py @@ -0,0 +1,69 @@ +from pdb import set_trace as T +import numpy as np + +from forge.blade import core +from forge.blade.lib import enums, utils + +def loadTiled(fPath, tiles, nCounts): + import pytmx + tm = pytmx.TiledMap(fPath) + assert len(tm.layers) == 1 + layer = tm.layers[0] + W, H = layer.width, layer.height + tilemap = np.zeros((H, W), dtype=object) + for w, h, dat in layer.tiles(): + f = dat[0] + tex = f.split('/')[-1].split('.')[0] + tilemap[h, w] = core.Tile(tiles[tex], h, w, nCounts, tex) + return tilemap + +class Map: + def __init__(self, config, idx): + self.updateList = set() + self.nCounts = config.NPOP + self.genEnv(config.ROOT + str(idx) + config.SUFFIX) + + def harvest(self, r, c): + self.updateList.add(self.tiles[r, c]) + return self.tiles[r, c].harvest() + + def inds(self): + return np.array([[j.state.index for j in i] for i in self.tiles]) + + def step(self): + for e in self.updateList.copy(): + if e.static: + self.updateList.remove(e) + #Perform after check: allow texture to reset + e.step() + + def stim(self, pos, rng): + r, c = pos + rt, rb = r-rng, r+rng+1 + cl, cr = c-rng, c+rng+1 + return self.tiles[rt:rb, cl:cr] + + #Fix this function to key by attr for mat.index + def getPadded(self, mat, pos, sz, key=lambda e: e): + ret = np.zeros((2*sz+1, 2*sz+1), dtype=np.int32) + R, C = pos + rt, rb = R-sz, R+sz+1 + cl, cr = C-sz, C+sz+1 + for r in range(rt, rb): + for c in range(cl, cr): + if utils.inBounds(r, c, self.size): + ret[r-rt, c-cl] = key(mat[r, c]) + else: + ret[r-rt, c-cl] = 0 + return ret + + def np(self): + env = np.array([e.state.index for e in + self.tiles.ravel()]).reshape(*self.shape) + return env + + def genEnv(self, fName): + tiles = dict((mat.value.tex, mat.value) for mat in enums.Material) + self.tiles = loadTiled(fName, tiles, self.nCounts) + self.shape = self.tiles.shape + diff --git a/forge/blade/core/realm.py b/forge/blade/core/realm.py new file mode 100644 index 00000000..29bc0a3b --- /dev/null +++ b/forge/blade/core/realm.py @@ -0,0 +1,183 @@ +import ray +import pickle +import numpy as np +from pdb import set_trace as T +import numpy as np + +from forge import trinity as Trinity +from forge.blade import entity, core +from itertools import chain +from copy import deepcopy + +class ActionArgs: + def __init__(self, action, args): + self.action = action + self.args = args + +class Realm: + def __init__(self, config, args, idx): + #Random samples + if config.SAMPLE: + config = deepcopy(config) + nent = np.random.randint(0, config.NENT) + config.NENT = config.NPOP * (1 + nent // config.NPOP) + self.world, self.desciples = core.Env(config, idx), {} + self.config, self.args, self.tick = config, args, 0 + self.npop = config.NPOP + + self.env = self.world.env + self.values = None + + def clientData(self): + if self.values is None and hasattr(self, 'sword'): + self.values = self.sword.anns[0].visVals() + + ret = { + 'environment': self.world.env, + 'entities': dict((k, v.packet()) for k, v in self.desciples.items()), + 'values': self.values + } + return pickle.dumps(ret) + + def spawn(self): + if len(self.desciples) >= self.config.NENT: + return + + entID, color = self.god.spawn() + ent = entity.Player(entID, color, self.config) + self.desciples[ent.entID] = ent + + r, c = ent.pos + self.world.env.tiles[r, c].addEnt(entID, ent) + self.world.env.tiles[r, c].counts[ent.colorInd] += 1 + + def cullDead(self, dead): + for entID in dead: + ent = self.desciples[entID] + r, c = ent.pos + self.world.env.tiles[r, c].delEnt(entID) + self.god.cull(ent.annID) + del self.desciples[entID] + + def stepWorld(self): + ents = list(chain(self.desciples.values())) + self.world.step(ents, []) + + def stepEnv(self): + self.world.env.step() + self.env = self.world.env.np() + + def stepEnt(self, ent, action, arguments): + move, attack = action + moveArgs, attackArgs = arguments + + ent.move = ActionArgs(move, moveArgs) + ent.attack = ActionArgs(attack, attackArgs[0]) + + def getStim(self, ent): + return self.world.env.stim(ent.pos, self.config.STIM) + +@ray.remote +class NativeRealm(Realm): + def __init__(self, trinity, config, args, idx): + super().__init__(config, args, idx) + self.god = trinity.god(config, args) + self.sword = trinity.sword(config, args) + self.sword.anns[0].world = self.world + + def stepEnts(self): + dead = [] + for ent in self.desciples.values(): + ent.step(self.world) + + if self.postmortem(ent, dead): + continue + + stim = self.getStim(ent) + action, arguments, val = self.sword.decide(ent, stim) + ent.act(self.world, action, arguments, val) + + self.stepEnt(ent, action, arguments) + + self.cullDead(dead) + + def postmortem(self, ent, dead): + entID = ent.entID + if not ent.alive or ent.kill: + dead.append(entID) + if not self.config.TEST: + self.sword.collectRollout(entID, ent) + return True + return False + + def step(self): + self.spawn() + self.stepEnv() + self.stepEnts() + self.stepWorld() + + def run(self, swordUpdate=None): + self.recvSwordUpdate(swordUpdate) + + updates = None + while updates is None: + self.step() + updates, logs = self.sword.sendUpdate() + return updates, logs + + def recvSwordUpdate(self, update): + if update is None: + return + self.sword.recvUpdate(update) + + def recvGodUpdate(self, update): + self.god.recv(update) + +@ray.remote +class VecEnvRealm(Realm): + #Use the default God behind the scenes for spawning + def __init__(self, config, args, idx): + super().__init__(config, args, idx) + self.god = Trinity.God(config, args) + + def stepEnts(self, decisions): + dead = [] + for tup in decisions: + entID, action, arguments, val = tup + ent = self.desciples[entID] + ent.step(self.world) + + if self.postmortem(ent, dead): + continue + + ent.act(self.world, action, arguments, val) + self.stepEnt(ent, action, arguments) + self.cullDead(dead) + + def postmortem(self, ent, dead): + entID = ent.entID + if not ent.alive or ent.kill: + dead.append(entID) + return True + return False + + def step(self, decisions): + decisions = pickle.loads(decisions) + self.stepEnts(decisions) + self.stepWorld() + self.spawn() + self.stepEnv() + + stims, rews, dones = [], [], [] + for entID, ent in self.desciples.items(): + stim = self.getStim(ent) + stims.append((ent, self.getStim(ent))) + rews.append(1) + return pickle.dumps((stims, rews, None, None)) + + def reset(self): + self.spawn() + self.stepEnv() + return [(e, self.getStim(e)) for e in self.desciples.values()] + + diff --git a/forge/blade/core/tile.py b/forge/blade/core/tile.py new file mode 100644 index 00000000..b1b98868 --- /dev/null +++ b/forge/blade/core/tile.py @@ -0,0 +1,47 @@ +from pdb import set_trace as T +import numpy as np + + +class Tile: + def __init__(self, mat, r, c, nCounts, tex): + self.r, self.c = r, c + self.mat = mat() + self.ents = {} + self.state = mat() + self.capacity = self.mat.capacity + self.counts = np.zeros(nCounts) + self.tex = tex + + @property + def nEnts(self): + return len(self.ents) + + def addEnt(self, entID, ent): + assert entID not in self.ents + self.ents[entID] = ent + + def delEnt(self, entID): + assert entID in self.ents + del self.ents[entID] + + def step(self): + if (not self.static and + np.random.rand() < self.mat.respawnProb): + self.capacity += 1 + #Try inserting a pass + if self.static: + self.state = self.mat + + @property + def static(self): + assert self.capacity <= self.mat.capacity + return self.capacity == self.mat.capacity + + def harvest(self): + if self.capacity == 0: + return False + elif self.capacity <= 1: + self.state = self.mat.degen() + self.capacity -= 1 + return True + return self.mat.dropTable.roll() diff --git a/forge/blade/entity/__init__.py b/forge/blade/entity/__init__.py new file mode 100644 index 00000000..1ef3df6c --- /dev/null +++ b/forge/blade/entity/__init__.py @@ -0,0 +1,2 @@ +from .entity import Entity +from .player import Player diff --git a/forge/blade/entity/entity.py b/forge/blade/entity/entity.py new file mode 100644 index 00000000..fcfe4edc --- /dev/null +++ b/forge/blade/entity/entity.py @@ -0,0 +1,37 @@ +import numpy as np + +from forge.blade.action import action +from forge.blade.systems import skill, droptable + +class Entity(): + def __init__(self, pos): + self.pos = pos + self.alive = True + self.skills = skill.Skills() + self.entityIndex=0 + self.health = -1 + self.lastAttacker = None + + def act(self, world): + pass + + def decide(self, stimuli): + pass + + def death(self): + pass + + def registerHit(self, attacker, dmg): + self.lastAttacker = attacker + self.health -= dmg + + def remove(self, ent): + r, c = self.pos + ent[r, c] = 0 + + def isAlive(self): + return self.health > 0 + + @property + def isPC(self): + return False diff --git a/forge/blade/entity/npc/__init__.py b/forge/blade/entity/npc/__init__.py new file mode 100644 index 00000000..d1ba208a --- /dev/null +++ b/forge/blade/entity/npc/__init__.py @@ -0,0 +1 @@ +from .npc import NPC diff --git a/forge/blade/entity/npc/mobs.py b/forge/blade/entity/npc/mobs.py new file mode 100644 index 00000000..1abdcea8 --- /dev/null +++ b/forge/blade/entity/npc/mobs.py @@ -0,0 +1,47 @@ +#NPC definitions with AI overrides per NPC, as required. + +from forge.blade.entity import NPC +from forge.blade.item import Item, RawMeat, Sword +from forge.blade.lib import AI +from forge.blade.lib.Enums import Material +from forge.blade.modules import DropTable, Skill + + +class Chicken(NPC.Passive): + def __init__(self, pos): + super().__init__(pos) + self.index = 2 + self.maxHealth = 5 + self.health = self.maxHealth + self.expOnKill = 10 + + self.drops.add(RawMeat.Chicken, 1) + self.drops.add(RawMeat.Chicken, 1, 0.5) + + def decide(self, world): + action, args = AI.randomOnTurf(world, self, + [Material.GRASS.value]) + return action, args + +class Goblin(NPC.PassiveAgressive): + def __init__(self, pos): + super().__init__(pos) + self.index = 3 + self.searchRange = 5 + self.maxHealth = 15 + self.health = self.maxHealth + self.expOnKill = 50 + self.skills.melee.exp = Skill.Skill.expCalculator.expAtLevel(5) + self.skills.defense.exp = Skill.Skill.expCalculator.expAtLevel(5) + + self.drops.add(RawMeat.Goblin, 1) + self.drops.add(Item.Gold, DropTable.Range(1, 100), 0.5) + self.drops.add(Sword.Copper, 1, 0.2) + + def decide(self, world): + #action, args = AI.turfSearchAndDestroy(world, self, + # whitelist=[Material.FOREST.value]) + action, args = AI.randomOnTurf(world, self, + [Material.FOREST.value]) + return action, args + diff --git a/forge/blade/entity/npc/npc.py b/forge/blade/entity/npc/npc.py new file mode 100644 index 00000000..060169bf --- /dev/null +++ b/forge/blade/entity/npc/npc.py @@ -0,0 +1,52 @@ +from sim.entity.Entity import Entity +from sim.lib import Enums, AI +from sim.modules import DropTable + + +#NPC +class NPC(Entity): + def __init__(self, pos): + super().__init__(pos) + self.expOnKill = 10 + self.drops = DropTable.DropTable() + + #NPCs have direct access to the world + def act(self, world): + action, args = self.decide(world) + if type(args) == Actions.EmptyArgs: + args = [] + action(world, self, *args) + self.lastAction = action + + def yieldDrops(self): + self.lastAttacker.receiveDrops(self.drops.roll()) + +#Wanders aimlessly. Ignores attacks +class Passive(NPC): + def __init__(self, pos): + super().__init__(pos) + + def decide(self, env): + return Actions.move4(), EmptyArgs + +#Adds basic agressive behavior +class PassiveAgressive(NPC): + def __init__(self, pos, rageTime=5): + super().__init__(pos) + self.rageClock = AI.RageClock(0) + self.rageTime = rageTime + + def decide(self, world): + if not self.rageClock.isActive(): + return Actions.move4(), EmptyArgs + + self.rageClock.tick() + action = AI.searchAndDestroy, (world, + self.pos, Enums.Entity.NEURAL) + return action, [] + + def registerHit(self, attacker, dmg): + super(PassiveAgressive, self).registerHit(attacker, dmg) + self.rageClock = AI.RageClock(self.rageTime) + + diff --git a/forge/blade/entity/player.py b/forge/blade/entity/player.py new file mode 100644 index 00000000..74e5c272 --- /dev/null +++ b/forge/blade/entity/player.py @@ -0,0 +1,179 @@ +import numpy as np +from forge.blade.systems import ai +from forge.blade.lib.enums import Material, Neon +from pdb import set_trace as T + +class Stat: + def __init__(self, val, maxVal): + self._val = val + self._max = maxVal + + def increment(self, amt=1): + self._val = min(self.max, self.val + amt) + + def decrement(self, amt=1): + self._val = max(0, self.val - amt) + + def center(self): + return (self.val - self.max/2.0) / self.max + + @property + def val(self): + return self._val + + @property + def max(self): + return self._max + + def packet(self): + return {'val': self.val, 'max': self.max} + +class Player: + public = set( + 'pos lastPos R C food water health entID annID name colorInd color timeAlive kill attackMap damage freeze immune'.split()) + + def __init__(self, entID, color, config): + self._config = config + + self._R, self._C = config.R, config.C + self._pos = config.SPAWN() + self._lastPos = self.pos + + self._food = Stat(config.FOOD, config.FOOD) + self._water = Stat(config.WATER, config.WATER) + self._health = Stat(config.HEALTH, config.HEALTH) + + self._entID = entID + self._name = 'Neural_' + str(self._entID) + self._timeAlive = 0 + + self._damage = None + self._freeze = 0 + self._immune = True + self._kill = False + + self._annID, self._color = color + self._colorInd = self._annID + self._attackMap = np.zeros((7, 7, 3)).tolist() + + self._index = 1 + self._immuneTicks = 15 + + self.move = None + self.attack = None + + def __getattribute__(self, name): + if name in Player.public: + return getattr(self, '_' + name) + return super().__getattribute__(name) + + def __setattr__(self, name, value): + if name in Player.public: + raise AttributeError('Property \"' + name + '\" is read only: agents cannot modify their server-side data') + return super().__setattr__(name, value) + + def packet(self): + data = {} + for key in Player.public: + val = getattr(self, key) + data[key] = val + if hasattr(val, 'packet'): + data[key] = val.packet() + if self.attack is not None: + data['attack'] = { + 'style': self.attack.action.__name__, + 'target': self.attack.args.entID} + return data + + #PCs interact with the world only through stimuli + #to prevent cheating + def decide(self, packets): + action, args = self.cpu.decide(self, packets) + return action, args + + def forage(self, world): + r, c = self._pos + isForest = type(world.env.tiles[r, c].mat) in [Material.FOREST.value] + if isForest and world.env.harvest(r, c): + self.food.increment(5) + + isWater = Material.WATER.value in ai.adjacentMats(world.env, self._pos) + if isWater: + self.water.increment(5) + + def lavaKill(self, world): + r, c = self._pos + if type(world.env.tiles[r, c].mat) == Material.LAVA.value: + self._kill = True + return self._kill + + def updateStats(self): + if (self._food.val > self._food.max//2 and + self._water.val > self._water.max//2): + self._health.increment() + + self._water.decrement() + self._food.decrement() + + if self._food.val <= 0: + self._health.decrement() + if self._water.val <= 0: + self._health.decrement() + + def updateCounts(self, world): + r, c = self._pos + world.env.tiles[r, c].counts[self._colorInd] += 1 + + def mapAttack(self): + if self.attack is not None: + attack = self.attack + name = attack.action.__name__ + if name == 'Melee': + attackInd = 0 + elif name == 'Range': + attackInd = 1 + elif name == 'Mage': + attackInd = 2 + rt, ct = attack.args.pos + rs, cs = self._pos + dr = rt - rs + dc = ct - cs + if abs(dr)<=3 and abs(dc)<=3: + self._attackMap[3+dr][3+dc][attackInd] += 1 + + def step(self, world): + if not self.alive: return + self._freeze = max(0, self._freeze-1) + self.updateCounts(world) + + if self.lavaKill(world): return + self.forage(world) + self.updateStats() + + self._damage = None + self._timeAlive += 1 + self.updateImmune() + + def act(self, world, actions, arguments, val): + if not self.alive: return + self.mapAttack() + + self.val = val + self._lastPos = self._pos + for action, args in zip(actions, arguments): + action.call(world, self, *args) + + @property + def alive(self): + return self._health.val > 0 + + def updateImmune(self): + if self._timeAlive >= self._immuneTicks: + self._immune = False + + #Note: does not stack damage, but still applies to health + def applyDamage(self, damage): + if self.immune: + return + self._damage = damage + self._health.decrement(damage) diff --git a/forge/blade/hook/__init__.py b/forge/blade/hook/__init__.py new file mode 100644 index 00000000..0f417a51 --- /dev/null +++ b/forge/blade/hook/__init__.py @@ -0,0 +1 @@ +from .modules import modules diff --git a/forge/blade/hook/item.py b/forge/blade/hook/item.py new file mode 100644 index 00000000..221ccaa8 --- /dev/null +++ b/forge/blade/hook/item.py @@ -0,0 +1,17 @@ +#Iterates through all items and hooks them to their respective skills +from forge.blade.item import item +from forge.blade.lib import utils + +#Filled in at runtime with items +class ItemList: + items = [] + +def hook(): + cls = item.Item + for e in utils.terminalClasses(cls): + skill = e.createSkill + if skill is not None: + if type(skill.skillItems) != list: + skill.skillItems = [] + skill.skillItems += [e] + ItemList.items += [e] diff --git a/forge/blade/hook/modules.py b/forge/blade/hook/modules.py new file mode 100644 index 00000000..cefe34b4 --- /dev/null +++ b/forge/blade/hook/modules.py @@ -0,0 +1,8 @@ +from os.path import dirname, basename, isfile +import glob +def modules(f): + modules = glob.glob(dirname(f)+"/*.py") + return [ basename(f)[:-3] for f in modules + if isfile(f) and not f.endswith('__init__.py')] + + diff --git a/forge/blade/item/__init__.py b/forge/blade/item/__init__.py new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/forge/blade/item/__init__.py @@ -0,0 +1 @@ + diff --git a/forge/blade/item/armor.py b/forge/blade/item/armor.py new file mode 100644 index 00000000..4cfaea43 --- /dev/null +++ b/forge/blade/item/armor.py @@ -0,0 +1,37 @@ +from pdb import set_trace as T +from forge.blade import systems +from forge.blade.systems import skill +from forge.blade.item import item, ore + +class Armor(item.Item): + createSkill = skill.Smithing + useSkill = skill.Defense + alwaysSucceeds = True + oreReq = 4 + defense = 0 + +class Base(Armor): + useLevel = 1 + defense = 0 + +class Copper(Armor): + createLevel = 1 + useLevel = 1 + exp = 40 + recipe = systems.Recipe(ore.Copper, Armor.oreReq) + defense = 55 + +class Tin(Armor): + createLevel = 5 + useLevel = 5 + exp = 80 + recipe = systems.Recipe(ore.Tin, Armor.oreReq) + defense = 90 + +class Iron(Armor): + createLevel = 10 + useLevel = 10 + exp = 120 + recipe = systems.Recipe(ore.Iron, Armor.oreReq) + defense = 105 + diff --git a/forge/blade/item/food.py b/forge/blade/item/food.py new file mode 100644 index 00000000..238c09bc --- /dev/null +++ b/forge/blade/item/food.py @@ -0,0 +1,42 @@ +from forge.blade.systems import Skill +from forge.blade.item import Item + +class Food(Item.Item): + createSkill = Skill.Cooking + useSkill = Skill.Constitution + heal = None + +class Ration(Food): + useLevel = 1 + exp = 0 + heal = 1 + +class Shrimp(Food): + createLevel = 1 + useLevel = 1 + exp = 10 + heal = 2 + +class Sardine(Food): + createLevel = 5 + useLevel = 5 + exp = 20 + heal = 3 + +class Herring(Food): + createLevel = 10 + useLevel = 10 + exp = 30 + heal = 5 + +class Chicken(Food): + createLevel = 1 + useLevel = 1 + exp = 10 + heal = 3 + +class Goblin(Food): + createLevel = 1 + useLevel = 5 + exp = 10 + heal = 5 diff --git a/forge/blade/item/item.py b/forge/blade/item/item.py new file mode 100644 index 00000000..606f1809 --- /dev/null +++ b/forge/blade/item/item.py @@ -0,0 +1,34 @@ +from forge.blade.systems import skill + +class Item: + createSkill = None + useSkill = None + + createLevel = None + useLevel = None + + exp = 0 + amtMade = 1 + alwaysSucceeds = True + recipe = None + +class Tool(Item): pass + +class Weapon(Item): + alwaysSucceeds = True + createSkill = skill.Smithing + +#Used as a stand in when no weapon is present +class Base(Weapon): + useLevel = 1 + attack = 0 + strength = 0 + ammo = None + +class Gold(Item): + pass + +class Hammer(Tool): pass +class Tinderbox(Tool): pass +class Pickaxe(Tool): pass +class Rod(Tool): pass diff --git a/forge/blade/item/knife.py b/forge/blade/item/knife.py new file mode 100644 index 00000000..dc6e7a1e --- /dev/null +++ b/forge/blade/item/knife.py @@ -0,0 +1,33 @@ +from forge.blade import systems +from forge.blade.systems import skill +from forge.blade.item import item, ore + +class Knife(item.Weapon): + createSkill = skill.Smithing + useSkill = skill.Ranged + amtMade = 10 + oreReq = 1 + +class Copper(Knife): + createLevel = 1 + useLevel = 1 + exp = 10 + attack = 5 + strength = 4 + recipe = systems.Recipe(ore.Copper, Knife.oreReq, amtMade=Knife.amtMade) + +class Tin(Knife): + createLevel = 5 + useLevel = 5 + exp = 20 + recipe = systems.Recipe(ore.Tin, Knife.oreReq, amtMade=Knife.amtMade) + attack = 8 + strength = 7 + +class Iron(Knife): + createLevel = 10 + useLevel = 10 + exp = 30 + recipe = systems.Recipe(ore.Iron, Knife.oreReq, amtMade=Knife.amtMade) + attack = 10 + strength = 8 diff --git a/forge/blade/item/ore.py b/forge/blade/item/ore.py new file mode 100644 index 00000000..6453f378 --- /dev/null +++ b/forge/blade/item/ore.py @@ -0,0 +1,22 @@ +from forge.blade.systems import skill +from forge.blade.item import item + +class Ore(item.Item): + createSkill = skill.Mining + useSkill = skill.Smithing + +class Copper(Ore): + createLevel = 1 + useLevel = 1 + exp = 10 +class Tin(Ore): + createLevel = 5 + useLevel = 5 + exp = 20 + +class Iron(Ore): + createLevel = 10 + useLevel = 10 + exp = 30 + + diff --git a/forge/blade/item/rawfish.py b/forge/blade/item/rawfish.py new file mode 100644 index 00000000..dc883c09 --- /dev/null +++ b/forge/blade/item/rawfish.py @@ -0,0 +1,24 @@ +from forge.blade.systems import skill +from forge.blade.item import item + +class RawFish(item.Item): + createSkill = skill.Fishing + useSkill = skill.Cooking + alwaysSucceeds = False + +class Shrimp(RawFish): + createLevel = 1 + useLevel = 1 + exp = 10 + +class Sardine(RawFish): + createLevel = 5 + useLevel = 5 + exp = 20 + +class Herring(RawFish): + createLevel = 10 + useLevel = 10 + exp = 30 + + diff --git a/forge/blade/item/rawmeat.py b/forge/blade/item/rawmeat.py new file mode 100644 index 00000000..82cbb30a --- /dev/null +++ b/forge/blade/item/rawmeat.py @@ -0,0 +1,15 @@ +from forge.blade.systems.import Skill +from forge.blade.item import Item + +class RawMeat(Item.Item): + useSkill = Skill.Cooking + alwaysSucceeds = False + +class Chicken(RawMeat): + useLevel = 1 + exp = 20 + +class Goblin(RawMeat): + useLevel = 5 + exp = 40 + diff --git a/forge/blade/item/sword.py b/forge/blade/item/sword.py new file mode 100644 index 00000000..9beb7db6 --- /dev/null +++ b/forge/blade/item/sword.py @@ -0,0 +1,34 @@ +from forge.blade.systems.Recipe import Recipe +from forge.blade.systems import Skill +from forge.blade.item import Item, Ore + + +class Sword(Item.Weapon): + createSkill = Skill.Smithing + useSkill = Skill.Melee + oreReq = 2 + +class Copper(Sword): + createLevel = 1 + useLevel = 1 + exp = 10 + recipe = Recipe(Ore.Copper, Sword.oreReq) + attack = 10 + strength = 9 + +class Tin(Sword): + createLevel = 5 + useLevel = 5 + exp = 20 + recipe = Recipe(Ore.Tin, Sword.oreReq) + attack = 15 + strength = 14 + +class Iron(Sword): + createLevel = 10 + useLevel = 10 + exp = 30 + recipe = Recipe(Ore.Iron, Sword.oreReq) + attack = 19 + strength = 14 + diff --git a/forge/blade/item/tool.py b/forge/blade/item/tool.py new file mode 100644 index 00000000..1d987e5f --- /dev/null +++ b/forge/blade/item/tool.py @@ -0,0 +1,3 @@ +from forge.blade.item import Item + +class Tool(Item.Item): pass diff --git a/forge/blade/lib/__init__.py b/forge/blade/lib/__init__.py new file mode 100644 index 00000000..9e399d48 --- /dev/null +++ b/forge/blade/lib/__init__.py @@ -0,0 +1,5 @@ +from .multiset import MultiSet +from .priorityqueue import PriorityQueue +from .stattrak import StatTraker +from .ray import init +from .ray import clearbuffers diff --git a/forge/blade/lib/comms.py b/forge/blade/lib/comms.py new file mode 100644 index 00000000..0b6a4f9c --- /dev/null +++ b/forge/blade/lib/comms.py @@ -0,0 +1,26 @@ +from forge.blade.action import action, v2 + +def isInt(x): + return type(x) in (float, int) and int(x) == x + +class CommChannel: + def __init__(self): + self.outbox = [] + self.inbox = [] + + def put(self, update): + if len(update) > 0: + self.outbox.append(update) + + def get(self): + inbox = self.inbox + self.inbox = [] + return inbox + + def send(self): + outbox = self.outbox + self.outbox = [] + return outbox + + def recv(self, updates): + self.inbox += updates diff --git a/forge/blade/lib/enums.py b/forge/blade/lib/enums.py new file mode 100644 index 00000000..3536ad26 --- /dev/null +++ b/forge/blade/lib/enums.py @@ -0,0 +1,186 @@ +#Various Enums used for handling materials, entity types, etc. +#Data texture pairs are used for enums that require textures. +#These textures are filled in by the Render class at run time. + +from pdb import set_trace as T +import numpy as np +import colorsys +from enum import Enum +from forge.blade.item import ore +from forge.blade import systems + +class Tile: + capacity = 3 + respawnProb = 0.1 + def __init__(self): + self.harvestable = False + +class Lava(Tile): + index = 0 + tex = 'lava' +class Water(Tile): + index = 1 + tex = 'water' +class Grass(Tile): + index = 2 + tex = 'grass' +class Scrub(Tile): + index = 3 + tex = 'scrub' +class Forest(Tile): + index = 4 + degen = Scrub + tex = 'forest' + #capacity = 3 + capacity = 1 + respawnProb = 0.025 + def __init__(self): + super().__init__() + self.harvestable = True + #self.dropTable = DropTable.DropTable() +class Stone(Tile): + index = 5 + tex = 'stone' +class Orerock(Tile): + index = 6 + degenIndex = Stone.index + tex = 'iron_ore' + capacity = 1 + respawnprob = 0.05 + def __init__(self): + super().__init__() + self.harvestable = True + self.dropTable = systems.DropTable() + self.dropTable.add(ore.Copper, 1) + +class Material(Enum): + LAVA = Lava + WATER = Water + GRASS = Grass + SCRUB = Scrub + FOREST = Forest + STONE = Stone + OREROCK = Orerock + +IMPASSIBLE = (1, 5, 6) + +class Defaults: + BLACK = (0, 0, 0) + GRAY3 = (20, 20, 20) + GRAY2 = (40, 40, 40) + GRAY1 = (60, 60, 60) + RED = (255, 0, 0) + GREEN = (0, 255, 0) + BLUE = (0, 0, 255) + YELLOW = (255, 255, 0) + GOLD = (212, 175, 55) + MASK = (214, 127, 255) + +def rgb(h): + h = h.lstrip('#') + return tuple(int(h[i:i+2], 16) for i in (0, 2, 4)) + +def rgbNorm(h): + h = h.lstrip('#') + return tuple(int(h[i:i+2], 16)/255.0 for i in (0, 2, 4)) + +class Color: + def __init__(self, name, hexVal): + self.name = name + self.hex = hexVal + self.rgb = rgb(hexVal) + self.norm = rgbNorm(hexVal) + self.value = self.rgb #Emulate enum + + def packet(self): + return self.hex + +def makeColor(idx, h=1, s=1, v=1): + r, g, b = colorsys.hsv_to_rgb(h, s, v) + rgbval = tuple(int(255*e) for e in [r, g, b]) + hexval = '%02x%02x%02x' % rgbval + return Color(str(idx), hexval) + +class Color256: + def make256(): + parh, parv = np.meshgrid(np.linspace(0.075, 1, 16), np.linspace(0.25, 1, 16)[::-1]) + parh, parv = parh.T.ravel(), parv.T.ravel() + idxs = np.arange(256) + params = zip(idxs, parh, parv) + colors = [makeColor(idx, h=h, s=1, v=v) for idx, h, v in params] + return colors + colors = make256() + +class Neon: + RED = Color('RED', '#ff0000') + ORANGE = Color('ORANGE', '#ff8000') + YELLOW = Color('YELLOW', '#ffff00') + + GREEN = Color('GREEN', '#00ff00') + MINT = Color('MINT', '#00ff80') + CYAN = Color('CYAN', '#00ffff') + + BLUE = Color('BLUE', '#0000ff') + PURPLE = Color('PURPLE', '#8000ff') + MAGENTA = Color('MAGENTA', '#ff00ff') + + FUCHSIA = Color('FUCHSIA', '#ff0080') + SPRING = Color('SPRING', '#80ff80') + SKY = Color('SKY', '#0080ff') + + WHITE = Color('WHITE', '#ffffff') + GRAY = Color('GRAY', '#666666') + BLACK = Color('BLACK', '#000000') + + BLOOD = Color('BLOOD', '#bb0000') + BROWN = Color('BROWN', '#7a3402') + GOLD = Color('GOLD', '#eec600') + SILVER = Color('SILVER', '#b8b8b8') + + TERM = Color('TERM', '#41ff00') + MASK = Color('MASK', '#d67fff') + + def color12(): + return ( + Neon.RED, Neon.ORANGE, Neon.YELLOW, + Neon.GREEN, Neon.MINT, Neon.CYAN, + Neon.BLUE, Neon.PURPLE, Neon.MAGENTA, + Neon.FUCHSIA, Neon.SPRING, Neon.SKY) + + def rand12(): + twelveColor = color12() + randInd = np.random.randint(0, len(twelveColor)) + return twelveColor[randInd] + +class Palette: + def __init__(self, n): + self.n = n + if n <= 12: + self.colors = Neon.color12() + else: + self.colors = Color256.colors + + def color(self, idx): + if self.n > 12: + idx = int(idx * 256 // self.n) + return self.colors[idx] + +class DataTexturePair: + def __init__(self, data, texture=None): + self.data = data + self.tex = texture + + def __eq__(self, ind): + return ind == self.data + + def __hash__(self): + return hash(self.data) + + +class Entity(Enum): + NONE = DataTexturePair(0) + NEURAL = DataTexturePair(1) + CHICKEN = DataTexturePair(2) + GOBLIN = DataTexturePair(3) + BEAR = DataTexturePair(4) + diff --git a/forge/blade/lib/log.py b/forge/blade/lib/log.py new file mode 100644 index 00000000..ef344482 --- /dev/null +++ b/forge/blade/lib/log.py @@ -0,0 +1,134 @@ +from pdb import set_trace as T +from collections import defaultdict +from forge.blade.lib.enums import Material +from forge.blade.lib import enums +from copy import deepcopy +import os + +import numpy as np +import json, pickle +import time +import ray + +#Static blob analytics +class InkWell: + def unique(blobs): + tiles = defaultdict(list) + for blob in blobs: + for t, v in blob.unique.items(): + tiles['unique_'+t.tex].append(v) + return tiles + + def counts(blobs): + tiles = defaultdict(list) + for blob in blobs: + for t, v in blob.counts.items(): + tiles['counts_'+t.tex].append(v) + return tiles + + def explore(blobs): + tiles = defaultdict(list) + for blob in blobs: + for t in blob.counts.keys(): + counts = blob.counts[t] + unique = blob.unique[t] + if counts != 0: + tiles['explore_'+t.tex].append(unique / counts) + return tiles + + def lifetime(blobs): + return {'lifetime':[blob.lifetime for blob in blobs]} + + def reward(blobs): + return {'reward':[blob.reward for blob in blobs]} + + def value(blobs): + return {'value': [blob.value for blob in blobs]} + +#Agent logger +class Blob: + def __init__(self): + self.unique = {Material.GRASS.value: 0, + Material.SCRUB.value: 0, + Material.FOREST.value: 0} + self.counts = deepcopy(self.unique) + self.lifetime = 0 + + self.reward, self.ret = [], [] + self.value, self.entropy= [], [] + self.pg_loss, self.val_loss = [], [] + + def finish(self): + self.lifetime = len(self.reward) + self.reward = np.sum(self.reward) + self.value = np.mean(self.value) + +class Quill: + def __init__(self, modeldir): + self.time = time.time() + self.dir = modeldir + self.index = 0 + try: + os.remove(modeldir + 'logs.p') + except: + pass + + def timestamp(self): + cur = time.time() + ret = cur - self.time + self.time = cur + return str(ret) + + def print(self): + print( + 'Time: ', self.timestamp(), + ', Iter: ', str(self.index)) + + def scrawl(self, logs): + #Collect log update + self.index += 1 + rewards, blobs = [], [] + for blobList in logs: + blobs += blobList + for blob in blobList: + rewards.append(float(blob.lifetime)) + + self.lifetime = np.mean(rewards) + blobRet = [] + for e in blobs: + if np.random.rand() < 0.1: + blobRet.append(e) + self.save(blobRet) + + def latest(self): + return self.lifetime + + def save(self, blobs): + with open(self.dir + 'logs.p', 'ab') as f: + pickle.dump(blobs, f) + + def scratch(self): + pass + +#Log wrapper and benchmarker +class Benchmarker: + def __init__(self, logdir): + self.benchmarks = {} + + def wrap(self, func): + self.benchmarks[func] = Utils.BenchmarkTimer() + def wrapped(*args): + self.benchmarks[func].startRecord() + ret = func(*args) + self.benchmarks[func].stopRecord() + return ret + return wrapped + + def bench(self, tick): + if tick % 100 == 0: + for k, benchmark in self.benchmarks.items(): + bench = benchmark.benchmark() + print(k.__func__.__name__, 'Tick: ', tick, + ', Benchmark: ', bench, ', FPS: ', 1/bench) + + diff --git a/forge/blade/lib/multiset.py b/forge/blade/lib/multiset.py new file mode 100644 index 00000000..bbe6212e --- /dev/null +++ b/forge/blade/lib/multiset.py @@ -0,0 +1,56 @@ +from collections import defaultdict + +class MultiSet: + def __init__(self, capacity=0): + self.data = defaultdict(int) + self.count = 0 + self.capacity = capacity + + def __iter__(self): + return self.data.__iter__() + + def countItem(self, item): + return self.data[item] + + @property + def full(self): + return self.capacity != 0 and self.count >= self.capacity + + @property + def empty(self): + return self.count == 0 + + def get(self, item): + return self.data[item] + + def isIn(self, item, num=1): + return self.data[item] > num + + def add(self, item, num=1): + assert self.capacity == 0 or self.count+num <= self.capacity + self.data[item] += num + self.count += num + + #Alias + def remove(self, item, num=1): + self.pop(item, num) + + def pop(self, item, num=1): + assert self.capacity == 0 or self.count-num >= 0 + self.data[item] -= num + self.count -= num + + def union(self, other): + for e in other: + self.add(e, other.count(e)) + + def diff(self, other): + for e in other: + self.remove(e, other.count(e)) + + def contains(self, other): + for e in other: + if not isIn(e, other[e]): + return False + return True + diff --git a/forge/blade/lib/priorityqueue.py b/forge/blade/lib/priorityqueue.py new file mode 100644 index 00000000..8da3c0f3 --- /dev/null +++ b/forge/blade/lib/priorityqueue.py @@ -0,0 +1,61 @@ +import heapq, itertools +import itertools + +class PriorityQueue: + def __init__(self, capacity, unique=False): + self.q, self.items = [], set() + self.capacity = capacity + self.count = itertools.count() + self.unique = unique + + def get(self, ind): + priority, item = self.tolist()[ind] + return priority, item + + def push(self, item, priority, uniqueKey=None): + if self.unique: + self.items.add(uniqueKey) + count = next(self.count) + if len(self.q) >= self.capacity: + return heapq.heappushpop(self.q, (priority, count, item)) + heapq.heappush(self.q, (priority, count, item)) + + def pop(self): + priority, _, item = heapq.heappop(self.q) + if self.unique: + self.items.remove(item) + return priority, item + + @property + def peek(self): + return self.peekPriority, self.peekValue + + @property + def peekPriority(self): + ret = heapq.nlargest(1, self.q) + if len(ret) > 0: + return ret[0][0] + + @property + def peekValue(self): + ret = heapq.nlargest(1, self.q) + if len(ret) > 0: + return ret[0][2] + + + def tolist(self): + q = heapq.nlargest(self.n, self.q) + return [(e[0], e[2]) for e in q] + + def priorities(self): + return sorted([e[0] for e in self.q], reverse=True) + + def print(self): + q = heapq.nlargest(self.n, self.q) + print([(e[0]) for e in q], end='') + print() + + @property + def n(self): + return len(self.q) + diff --git a/forge/blade/lib/ray.py b/forge/blade/lib/ray.py new file mode 100644 index 00000000..d3e058c7 --- /dev/null +++ b/forge/blade/lib/ray.py @@ -0,0 +1,174 @@ +import ray, os +import time + +OBJECT_INFO_PREFIX = b"OI:" +OBJECT_LOCATION_PREFIX = b"OL:" +TASK_TABLE_PREFIX = b"TT:" + +def init(mode): + os.environ['MKL_NUM_THREADS'] = '1' + os.environ['OMP_NUM_THREADS'] = '1' + + if mode == 'local': + ray.init(local_mode=ray.PYTHON_MODE) + elif mode == 'default': + ray.init() + elif mode == 'remote': + print('Set up HOSTNAME') + ray.init(redis_address=os.environ['HOSTNAME'] + ':6379') + else: + print('Invalid ray mode (local/default/remote)') + exit(-1) + +def clearbuffers(): + start = time.time() + print('Clearing ray buffers...') + flush_task_and_object_metadata_unsafe() + flush_redis_unsafe() + print('Done. Free time: ', time.time() - start) + +#Continuous moving average +class CMA(): + def __init__(self): + self.t = 1.0 + self.cma = None + + def update(self, x): + if self.cma is None: + self.cma = x + return + self.cma = (x + self.t*self.cma)/(self.t+1) + self.t += 1.0 + +#Continuous moving average +class CMV(): + def __init__(self): + self.cma = CMA() + self.cmv = None + + def update(self, x): + if self.cmv is None: + self.cma.update(x) + self.cmv = 0 + return + prevMean = self.cma.cma + self.cma.update(x) + self.cmv += (x-prevMean)*(x-self.cma.cma) + + @property + def stats(self): + return self.cma.cma, self.cmv + +class RayBenchmark: + def __init__(self): + self.cmv = CMV() + def startRecord(self): + self.start = time.time() + + def stopRecord(self): + delta = time.time() - self.start + self.cmv.update(delta) + + def reset(self): + self.cmv = CMV() + + @property + def stats(self): + mean, var = self.cmv.stats + return {'mean': mean, 'var': var} + +def put(*args, profile=None): + if profile is not None: + if not hasattr(put, 'bench'): + put.bench = {} + if not profile in put.bench: + put.bench[profile] = RayBenchmark() + put.bench[profile].startRecord() + ret = ray.put(*args) + if profile is not None: + put.bench[profile].stopRecord() + return ret + +def get(*args, profile=None): + if profile is not None: + if not hasattr(get, 'bench'): + get.bench = {} + if not profile in get.bench: + get.bench[profile] = RayBenchmark() + get.bench[profile].startRecord() + ret = ray.get(*args) + if profile is not None: + get.bench[profile].stopRecord() + return ret + +def profile(): + stats = {} + for k, f in zip('put get'.split(), (put, get)): + if hasattr(f, 'bench'): + stats[k] = dict((k, v.stats) for k, v in put.bench.items()) + return stats + +def flush_redis_unsafe(): + """This removes some non-critical state from the primary Redis shard. + + This removes the log files as well as the event log from Redis. This can + be used to try to address out-of-memory errors caused by the accumulation + of metadata in Redis. However, it will only partially address the issue as + much of the data is in the task table (and object table), which are not + flushed. + """ + if not hasattr(ray.worker.global_worker, "redis_client"): + raise Exception("ray.experimental.flush_redis_unsafe cannot be called " + "before ray.init() has been called.") + + redis_client = ray.worker.global_worker.redis_client + + # Delete the log files from the primary Redis shard. + keys = redis_client.keys("LOGFILE:*") + if len(keys) > 0: + num_deleted = redis_client.delete(*keys) + else: + num_deleted = 0 + print("Deleted {} log files from Redis.".format(num_deleted)) + + # Delete the event log from the primary Redis shard. + keys = redis_client.keys("event_log:*") + if len(keys) > 0: + num_deleted = redis_client.delete(*keys) + else: + num_deleted = 0 + print("Deleted {} event logs from Redis.".format(num_deleted)) + + +def flush_task_and_object_metadata_unsafe(): + """This removes some critical state from the Redis shards. + + This removes all of the object and task metadata. This can be used to try + to address out-of-memory errors caused by the accumulation of metadata in + Redis. However, after running this command, fault tolerance will most + likely not work. + """ + if not hasattr(ray.worker.global_worker, "redis_client"): + raise Exception("ray.experimental.flush_redis_unsafe cannot be called " + "before ray.init() has been called.") + + def flush_shard(redis_client): + num_task_keys_deleted = 0 + for key in redis_client.scan_iter(match=TASK_TABLE_PREFIX + b"*"): + num_task_keys_deleted += redis_client.delete(key) + print("Deleted {} task keys from Redis.".format(num_task_keys_deleted)) + + num_object_keys_deleted = 0 + for key in redis_client.scan_iter(match=OBJECT_INFO_PREFIX + b"*"): + num_object_keys_deleted += redis_client.delete(key) + print("Deleted {} object info keys from Redis.".format( + num_object_keys_deleted)) + + num_object_location_keys_deleted = 0 + for key in redis_client.scan_iter(match=OBJECT_LOCATION_PREFIX + b"*"): + num_object_location_keys_deleted += redis_client.delete(key) + print("Deleted {} object location keys from Redis.".format( + num_object_location_keys_deleted)) + + for redis_client in ray.worker.global_state.redis_clients: + flush_shard(redis_client) diff --git a/forge/blade/lib/stattrak.py b/forge/blade/lib/stattrak.py new file mode 100644 index 00000000..d581bf9a --- /dev/null +++ b/forge/blade/lib/stattrak.py @@ -0,0 +1,88 @@ +from collections import deque + +import numpy as np +from queue import PriorityQueue +from forge.blade.systems import combat + +class ExchangeEntry: + def __init__(self, item, numBuy, numSell, maxBuyPrice, maxSellPrice): + self.item = item + self.numBuy = numBuy + self.numSell = numSell + self.maxBuyPrice = maxBuyPrice + self.maxSellPrice = maxSellPrice + + #Hackey str get. Only for printing + @property + def itemName(self): + return str(self.item)[17:-2] + + @property + def worth(self): + return (self.numBuy + self.numSell)*self.maxBuyPrice + + def __lt__(self, other): + return True + + def __eq__(self, other): + return False + +class StatTraker: + def __init__(self, maxLen=2048): + self.lenTrak = deque(maxlen=maxLen) + + def update(self, pcs, npcs, exchange): + self.pcs = pcs + self.npcs = npcs + + self.lenTrak.append(len(pcs)) + #Update exchange + self.updateExchange(exchange) + + def updateExchange(self, exchange): + self.exchange = PriorityQueue() + buyOffers = exchange.buyOffers + sellOffers = exchange.sellOffers + buyKeys, sellKeys = buyOffers.keys(), sellOffers.keys() + exchangeKeys = list(set(list(buyKeys) + list(sellKeys))) + + for key in exchangeKeys: + keyBuys, keySells = buyOffers[key], sellOffers[key] + topBuy = keyBuys.peek() + topSell = keySells.peek() + + if topBuy is not None: + numBuy, maxBuyPrice = topBuy.quantLeft, topBuy.itemPrice + item = topBuy.item + else: + numBuy, maxBuyPrice = 0, 0 + + if topSell is not None: + numSell, minSellPrice = topSell.quantLeft, topSell.itemPrice + item = topSell.item + else: + numSell, minSellPrice = 0, 0 + + #Compute total buy value + totalBuy = 0 + for e in keyBuys.queue: + totalBuy += e.coffer + + entry = ExchangeEntry(item, numBuy, numSell, + maxBuyPrice, minSellPrice) + + self.exchange.put(entry, totalBuy) + + @property + def numEntities(self): + return np.asarray(list(self.lenTrak)) + +class StatBlock: + def __init__(self, entity): + self.timeAlive = entity.timeAlive + self.level = combat.combatLevel(entity.skills) + self.defense = entity.skills.defense.level + self.melee = entity.skills.melee.level + self.ranged = entity.skills.ranged.level + + diff --git a/forge/blade/lib/utils.py b/forge/blade/lib/utils.py new file mode 100644 index 00000000..f0a9a526 --- /dev/null +++ b/forge/blade/lib/utils.py @@ -0,0 +1,182 @@ +import itertools +import time + +import numpy as np + +def cosSim(x): + from sklearn.metrics.pairwise import euclidean_distances as pdist + mag = np.sqrt(np.sum(x**2, 1)) + x = x / mag.reshape(-1, 1) + dists = pdist(x) + return dists + +def vstack(x): + if len(x) > 0: + return np.vstack(x) + return [] + +def groupby(items, key): + targs = sorted(items, key=key) + return itertools.groupby(targs, key=key) + +#Generic +def uniqueKey(refDict): + idx = seed() + if idx in refDict: + #print('Hash collision--highly unlikely, check your code') + return uniqueKey(refDict) + return idx + +def seed(): + return int(np.random.randint(0, 2**32)) + +def invertDict(x): + return {v: k for k, v in x.items()} + +def loadDict(fName): + with open(fName) as f: + s = eval(f.read()) + return s + +def terminalClasses(cls): + ret = [] + subclasses = cls.__subclasses__() + if len(subclasses) == 0: + ret += [cls] + else: + for e in subclasses: + ret += terminalClasses(e) + return ret + +def l1(pos1, pos2): + r1, c1 = pos1 + r2, c2 = pos2 + return abs(r1 - r2) + abs(c1 - c2) + +def l2(pos, cent): + r1, c1 = pos1 + r2, c2 = pos2 + return np.sqrt((r1 - r2)**2 + (c1 - c2)**2) + +def linf(pos, cent): + r1, c1 = pos1 + r2, c2 = pos2 + return max(abs(r1 - r2), abs(c1 - c2)) + +def norm(x, n=2): + return (np.sum(np.abs(x)**n)**(1.0/n)) / np.prod(x.shape) + +#Bounds checker +def inBounds(r, c, shape, border=0): + R, C = shape + return ( + r > border and + c > border and + r < R - border and + c < C - border + ) + +#Because the numpy version is horrible +def randomChoice(aList): + lLen = len(aList) + ind = np.random.randint(0, lLen) + return aList[ind] + +#Tracks inds of a permutation +class Perm(): + def __init__(self, n): + self.inds = np.random.permutation(np.arange(n)) + self.m = n + self.pos = 0 + + def next(self, n): + assert(self.pos + n < self.m) + ret = self.inds[self.pos:(self.pos+n)] + self.pos += n + return ret + +#Exponentially decaying average +class EDA(): + def __init__(self, k=0.9): + self.k = k + self.eda = None + + def update(self, x): + if self.eda is None: + self.eda = x + return + #self.eda = self.eda * k / (x * (1-k)) + self.eda = (1-self.k)*x + self.k*self.eda + +class Timer: + def __init__(self): + self.start = time.time() + + def ticked(self, delta): + if time.time() - self.start > delta: + self.start = time.time() + return True + return False + +class BenchmarkTimer: + def __init__(self): + self.eda = EDA() + self.accum = 0 + def startRecord(self): + self.start = time.time() + + def stopRecord(self, accum=False): + if accum: + self.accum += time.time() - self.start + else: + self.eda.update(self.accum + time.time() - self.start) + self.accum = 0 + + def benchmark(self): + return self.eda.eda + +#Continuous moving average +class CMA(): + def __init__(self): + self.t = 1.0 + self.cma = None + + def update(self, x): + if self.cma is None: + self.cma = x + return + self.cma = (x + self.t*self.cma)/(self.t+1) + self.t += 1.0 + +#Continuous moving average +class CMV(): + def __init__(self): + self.cma = CMA() + self.cmv = None + + def update(self, x): + if self.cmv is None: + self.cma.update(x) + self.cmv = 0 + return + prevMean = self.cma.cma + self.cma.update(x) + self.cmv += (x-prevMean)*(x-self.cma.cma) + + @property + def stats(self): + return self.cma.cma, self.cmv + +def matCrop(mat, pos, stimSz): + ret = np.zeros((2*stimSz+1, 2*stimSz+1), dtype=np.int32) + R, C = pos + rt, rb = R-stimSz, R+stimSz+1 + cl, cr = C-stimSz, C+stimSz+1 + for r in range(rt, rb): + for c in range(cl, cr): + if inBounds(r, c, mat.shape): + ret[r-rt, c-cl] = mat[r, c] + else: + ret[r-rt, c-cl] = 0 + return ret + diff --git a/forge/blade/systems/__init__.py b/forge/blade/systems/__init__.py new file mode 100644 index 00000000..13f01a85 --- /dev/null +++ b/forge/blade/systems/__init__.py @@ -0,0 +1,5 @@ +from .droptable import DropTable +from .exchange import Exchange +from .recipe import Recipe +from .inventory import Inventory +from .skill import Skill diff --git a/forge/blade/systems/ai.py b/forge/blade/systems/ai.py new file mode 100644 index 00000000..4c127667 --- /dev/null +++ b/forge/blade/systems/ai.py @@ -0,0 +1,167 @@ +#Various high level routines and tools for building quick +#NPC AIs. Returns only action arguments where possible. More +#complex routines return (action, args) pairs where required. + +import numpy as np +from queue import Queue +from forge.blade.lib import enums, utils + +def turfSearchAndDestroy(world, entity, whitelist): + for e in sortedByl1(world.env.ent, world.size, entity): + if e.entityIndex == enums.Entity.NEURAL.value.data: + if isAdjacent(entity.pos, e.pos): + tree = Actions.ActionTree(world, entity, rootVersion=Actions.MeleeV2) + tree.decideArgs(e) + return tree.actionArgPair() + move = routePath(entity.pos, e.pos) + if inWhitelist(world.env.tiles, entity.pos, move, whitelist): + tree = Actions.ActionTree(world, entity, rootVersion=Actions.MoveV2) + tree.decideArgs(move) + return tree.actionArgPair() + return randomOnTurf(world, entity, whitelist) + +def randomMove(world, entity): + tree = Actions.ActionTree(world, entity, rootVersion=Actions.MoveV2) + tree.randomArgs() + return tree.actionArgPair() + +def randomOnTurf(world, entity, whitelist): + delMatPairs = adjacencyDelMatPairs(world.env, entity.pos) + moves = whitelistByBlock(delMatPairs, whitelist) + if len(moves) == 0: return Actions.Pass(), Actions.EmptyArgs() + ind = np.random.randint(0, len(moves)) + + tree = Actions.ActionTree(world, entity, rootVersion=Actions.MoveV2) + tree.decideArgs(moves[ind]) + return tree.actionArgPair() + +def inWhitelist(env, pos, delta, whitelist): + r, c = posSum(pos, delta) + return env[r, c] in whitelist + +def whitelistByBlock(delMatPairs, whitelist): + ret = [] + for deli, mati in delMatPairs: + if mati in whitelist: + ret += [deli] + return ret + +#Adjacency functions +def adjacentDeltas(): + return [(-1, 0), (1, 0), (0, 1), (0, -1)] + +def l1Deltas(s): + rets = [] + for r in range(-s, s+1): + for c in range(-s, s+1): + rets.append((r, c)) + return rets + +def adjacentPos(pos): + return [posSum(pos, delta) for delta in adjacentDeltas()] + +def adjacentEmptyPos(env, pos): + return [p for p in adjacentPos(pos) + if utils.inBounds(*p, env.size)] + +def adjacentTiles(env, pos): + return [env.tiles[p] for p in adjacentPos(pos) + if utils.inBounds(*p, env.size)] + +def adjacentMats(env, pos): + return [type(env.tiles[p].mat) for p in adjacentPos(pos) + if utils.inBounds(*p, env.shape)] + +def adjacencyDelMatPairs(env, pos): + return zip(adjacentDeltas(), adjacentMats(env, pos)) +###End### + +def l1(pos1, pos2): + r1, c1 = pos1 + r2, c2 = pos2 + return abs(r1-r2) + abs(c1-c2) + +def sortedByl1(ent, sz, startEnt): + targs = l1Range(ent, sz, startEnt.pos, startEnt.searchRange) + targs = sorted(targs, key=lambda targ: l1(startEnt.pos, targ.pos)) + return targs + +def l1Range(ent, sz, start, rng): + R, C = sz, sz + rs, cs = start + rt = max(0, rs-rng) + rb = min(R, rs+rng) + cl = max(0, cs-rng) + cr = min(C, cs+rng) + ret = [] + for r in range(rt, rb): + for c in range(cl, cr): + if len(ent[r, c]) > 0: + ret += ent[r, c] + return ret + +def isAdjacent(pos1, pos2): + rs, re = pos1 + es, ee = pos2 + return np.logical_xor(abs(re - rs) == 1, abs(ee - es) == 1) + +def posSum(pos1, pos2): + return pos1[0] + pos2[0], pos1[1] + pos2[1] + +def routePath(start, end): + sr, sc = start + er, ec = end + if abs(sc - ec) > abs(sr - er): + return (0, np.sign(ec - sc)) + return (np.sign(er - sr), 0) + +def inRange(env, start, targ, rng): + R, C = env.shape + rs, cs = start + rt = max(0, rs-rng) + rb = min(R, rs+rng) + cl = max(0, cs-rng) + cr = min(C, cs+rng) + return targ in env[rt:rb, cl:cr] + +#Fix this +def findNearest(env, start, targ, rng=4 ): + #Quick check + rs, ts = start + if not inRange(env, start, targ, rng): + return + + #Expensive search + cur = Queue() + visited = {} + cur.push(start) + while not cur.empty: + r, c = cur.pop() + if (r, c) in visited: + continue + + if env[r, c] == targ: + return (r, c) + + visited[(r, c)] = 1 + if rs - r < targ: + cur.push(r+1, c) + if cs - c < targ: + cur.push(r, c+1) + if r - rs < targ: + cur.push(r-1, c) + if c - cs < targ: + cur.push(r, c-1) + + return None + +class RageClock: + def __init__(self, ticks): + self.ticks = ticks + + def tick(self): + self.ticks -= 1 + + def isActive(self): + return self.ticks > 0 + diff --git a/forge/blade/systems/combat.py b/forge/blade/systems/combat.py new file mode 100644 index 00000000..ef260846 --- /dev/null +++ b/forge/blade/systems/combat.py @@ -0,0 +1,68 @@ +#Various utilities for managing combat, including hit/damage + +import numpy as np + +def combatLevel(skills): + hp = skills.constitution.level + defense = skills.defense.level + melee = skills.melee.level + ranged = skills.ranged.level + + base = 0.25*(defense + hp) + meleeAdjust = 0.65*melee + rangeAdjust = 0.325*(np.floor(ranged/2)+ranged) + final = np.floor(base + max(meleeAdjust, rangeAdjust)) + return final + +def attack(entity, targ, skill): + attackLevel = skill.level + defenseLevel = targ.skills.defense.level + + attackBonus, strengthBonus = 0, 0 + if entity.isPC: + if skill.isMelee: + equip = entity.equipment.melee + elif skill.isRanged: + equip = entity.equipment.ranged + if equip.ammo is not None and equip.ammo <= 0: + return + attackBonus, strengthBonus = equip.attack, equip.strength + + defenseBonus = 0 + if targ.isPC: + defenseBonus = targ.equipment.armor.defense + + dmg = 0 + if isHit(attackLevel, attackBonus, defenseLevel, defenseBonus): + dmg = damage(attackLevel, strengthBonus) + + if entity.isPC: + entity.skills.addCombatExp(skill, dmg) + if entity.isPC: + targ.skills.addCombatExp(targ.skills.defense, dmg) + targ.registerHit(entity, dmg) + +#Compute maximum damage roll +def maxHit(effectiveLevel, equipmentBonus): + return np.floor(0.5 + (8+effectiveLevel)*(equipmentBonus+64.0)/640.0) + +#Compute maximum attack or defense roll (same formula) +def maxAttackDefense(effectiveLevel, equipmentBonus): + return effectiveLevel*(equipmentBonus+64) + +#Compute hit chance from max attack and defense +def accuracy(atk, dfn): + if atk > dfn: + return 1 - (dfn+2) / (2*(atk+1)) + return atk/(2*(dfn+1)) + +def isHit(attackLevel, attackBonus, defenseLevel, defenseBonus): + maxAttack = maxAttackDefense(attackLevel, attackBonus) + maxDefense = maxAttackDefense(defenseLevel, defenseBonus) + acc = accuracy(maxAttack, maxDefense) + return np.random.rand() < acc + +def damage(strengthLevel, strengthBonus): + mmax = maxHit(strengthLevel, strengthBonus) + return np.random.randint(1, 1 + mmax) + diff --git a/forge/blade/systems/droptable.py b/forge/blade/systems/droptable.py new file mode 100644 index 00000000..9c9c2857 --- /dev/null +++ b/forge/blade/systems/droptable.py @@ -0,0 +1,37 @@ +import numpy as np + +class Range(): + def __init__(self, mmin, mmax): + self.mmin = mmin + self.mmax = mmax + + @property + def value(self): + return np.random.randint(self.mmin, self.mmax+1) + +class Drop: + def __init__(self, item, amount, prob): + self.item = item + self.amount = amount + self.prob = prob + + def roll(self): + if np.random.rand() < self.prob: + if type(self.amount) == int: + return (self.item(), self.amount) + return (self.item(), self.amount.value) + +class DropTable: + def __init__(self): + self.drops = [] + + def add(self, item, quant, prob=1.0): + self.drops += [Drop(item, quant, prob)] + + def roll(self): + ret = [] + for e in self.drops: + drop = e.roll() + if drop is not None: + ret += [drop] + return ret diff --git a/forge/blade/systems/equipment.py b/forge/blade/systems/equipment.py new file mode 100644 index 00000000..e65396bb --- /dev/null +++ b/forge/blade/systems/equipment.py @@ -0,0 +1,18 @@ +from forge.blade.item import item, armor + +class Equipment: + def __init__(self): + self.ammo = 0 + self.resetArmor() + self.resetMelee() + self.resetRanged() + + def resetArmor(self): + self.armor = armor.Base() + + def resetMelee(self): + self.melee = item.Base() + + def resetRanged(self): + self.ranged = item.Base() + diff --git a/forge/blade/systems/exchange.py b/forge/blade/systems/exchange.py new file mode 100644 index 00000000..4ee55b1f --- /dev/null +++ b/forge/blade/systems/exchange.py @@ -0,0 +1,97 @@ +from collections import defaultdict +from queue import PriorityQueue + +class Offer: + def __init__(self, item, quant, itemPrice, coffer=0): + self.item = item + self.quantLeft = quant + self.quantFulfilled = 0 + self.itemPrice = itemPrice + self.coffer = coffer + + @property + def complete(self): + return self.quantLeft == 0 + + def partialCollect(self): + pass + + def cancel(self): + return self.coffer, [self.item() for e in self.quantFulfilled] + + def __lt__(self, other): + return True + + def __eq__(self, other): + return False + +class BuyOffer(Offer): + def buy(self, quant, itemPrice): + self.coffer -= itemPrice * quant + + #Collect only profits thus far + def partialCollect(self): + ret = [self.item() for e in self.quantFulfilled] + self.quantFulfilled = 0 + return ret + +class SellOffer(Offer): + def sell(self, quant): + self.coffer += self.itemPrice * quant + self.quantLeft -= quant + assert self.quantLeft >= 0 + + def partialCollect(self): + ret = self.coffer + self.coffer = 0 + return self.coffer + +#Why is there no peek fuction... +class PQ(PriorityQueue): + def peek(self): + if len(self.queue) > 0: + return self.queue[0] + return None + +class Exchange: + def __init__(self): + self.buyOffers = defaultdict(PQ) + self.sellOffers = defaultdict(PQ) + + def buy(self, item, quant, maxPrice): + offer = BuyOffer(item, quant, maxPrice, coffer=quant*maxPrice) + self.buyOffers[item].put(offer, -maxPrice) + self.update(item) + return offer + + def sell(self, item, quant, itemPrice): + offer = SellOffer(item, quant, itemPrice) + self.sellOffers[item].put(offer, itemPrice) + self.update(item) + return offer + + def update(self, item): + buyOffer = self.buyOffers[item].peek() + sellOffer = self.sellOffers[item].peek() + + if None in (buyOffer, sellOffer): + return + maxBuy, minSell = buyOffer.itemPrice, sellOffer.itemPrice + itemPrice = minSell #Advantage given to buyer arbitrarily + + if maxBuy >= minSell: + if sellOffer.quantLeft < buyOffer.quantLeft: + buyOffer.buy(sellOffer.quantLeft, itemPrice) + sellOffer.sell(sellOffer.quantLeft) + self.sellOffers[item].get() + elif sellOffer.quantLeft > buyOffer.quantLeft: + buyOffer.buy(buyOffer.quantLeft, itemPrice) + sellOffer.sell(buyOffer.quantLeft) + self.buyOffers[item].get() + elif sellOffer.quantLeft == buyOffer.quantLeft: + buyOffers.buy(buyOffer.quantLeft, itemPrice) + sellOffer.sell(sellOffer.quantLeft) + self.buyOffers[item].get() + self.sellOffers[item].get() + + self.update(item) diff --git a/forge/blade/systems/experience.py b/forge/blade/systems/experience.py new file mode 100644 index 00000000..e0feed9e --- /dev/null +++ b/forge/blade/systems/experience.py @@ -0,0 +1,20 @@ +import numpy as np + +class ExperienceCalculator: + def __init__(self): + self.exp = [] + self.tabulateExp() + + def tabulateExp(self, numLevels=99): + for i in range(numLevels): + if i == 0: + self.exp += [0] + else: + increment = np.floor(np.floor(i + 300*(2**(i/7.0)))/4.0) + self.exp += [self.exp[-1] + increment] + + def expAtLevel(self, level): + return self.exp[level] + + def levelAtExp(self, exp): + return np.searchsorted(self.exp, exp) diff --git a/forge/blade/systems/inventory.py b/forge/blade/systems/inventory.py new file mode 100644 index 00000000..d47bfbe1 --- /dev/null +++ b/forge/blade/systems/inventory.py @@ -0,0 +1,17 @@ +from forge.blade.item import item, armor + +class Inventory: + def __init__(self): + self.ammo = 0 + self.resetArmor() + self.resetMelee() + self.resetRanged() + + def resetArmor(self): + self.armor = Armor.Base() + + def resetMelee(self): + self.melee = Item.Base() + + def resetRanged(self): + self.ranged = Item.Base() diff --git a/forge/blade/systems/recipe.py b/forge/blade/systems/recipe.py new file mode 100644 index 00000000..57ab7bd1 --- /dev/null +++ b/forge/blade/systems/recipe.py @@ -0,0 +1,10 @@ +from forge.blade import lib + +class Recipe: + def __init__(self, *args, amtMade=1): + self.amtMade = amtMade + self.blueprint = lib.MultiSet() + for i in range(0, len(args), 2): + inp = args[i] + amt = args[i+1] + self.blueprint.add(inp, amt) diff --git a/forge/blade/systems/skill.py b/forge/blade/systems/skill.py new file mode 100644 index 00000000..70b0c8ce --- /dev/null +++ b/forge/blade/systems/skill.py @@ -0,0 +1,99 @@ +import abc + +import numpy as np +from forge.blade.systems import experience + +class Skills: + def __init__(self): + self.melee = Melee() + self.ranged = Ranged() + self.defense = Defense() + self.constitution = Constitution() + + self.fishing = Fishing() + self.mining = Mining() + + self.cooking = Cooking() + self.smithing = Smithing() + + def addCombatExp(self, skill, dmg): + skill.exp += int(3*dmg) + self.constitution.exp += int(dmg) + +class Skill: + skillItems = abc.ABCMeta + expCalculator = experience.ExperienceCalculator() + def __init__(self): + self.exp = 0 + + @property + def level(self): + return self.expCalculator.levelAtExp(self.exp) + +class CombatSkill(Skill): + @property + def isMelee(self): + return False + + @property + def isRanged(self): + return False + +class NonCombatSkill(Skill): + def success(self, levelReq): + level = self.level + if level < levelReq: + return False + chance = 0.5 + 0.05*(level - levelReq) + if chance >= 1.0: + return True + return np.random.rand() < chance + + def attempt(self, inv, item): + if (item.createSkill != self.__class__ or + self.level < item.createLevel): + return + + if item.recipe is not None: + #Check that everything is available + if not inv.satisfies(item.recipe): return + inv.removeRecipe(item.recipe) + + if item.alwaysSucceeds or self.success(item.createLevel): + inv.add(item, item.amtMade) + self.exp += item.exp + return True + +class HarvestingSkill(NonCombatSkill): + #Attempt each item from highest to lowest tier until success + def harvest(self, inv): + for e in self.skillItems: + if self.attempt(inv, e): + return + +class ProcessingSkill(NonCombatSkill): + def process(self, inv, item): + self.attempt(inv, item) + +class Fishing(HarvestingSkill): pass + +class Mining(HarvestingSkill): pass + +class Cooking(ProcessingSkill): pass + +class Smithing(ProcessingSkill): pass + +class Melee(CombatSkill): + @property + def isMelee(self): + return True + +class Ranged(CombatSkill): + @property + def isRanged(self): + return True + +class Defense(CombatSkill): pass + +class Constitution(CombatSkill): pass + diff --git a/forge/embyr b/forge/embyr new file mode 120000 index 00000000..4f875394 --- /dev/null +++ b/forge/embyr @@ -0,0 +1 @@ +../../neural-mmo-client/ \ No newline at end of file diff --git a/forge/ethyr/rollouts.py b/forge/ethyr/rollouts.py new file mode 100644 index 00000000..2163b51e --- /dev/null +++ b/forge/ethyr/rollouts.py @@ -0,0 +1,88 @@ +from pdb import set_trace as T +from itertools import chain +import numpy as np + +from forge.blade.lib.log import Blob + +#Untested function +def discountRewards(rewards, gamma=0.99): + rets, N = [], len(rewards) + discounts = np.array([gamma**i for i in range(N)]) + rewards = np.array(rewards) + for idx in range(N): rets.append(sum(rewards[idx:]*discounts[:N-idx])) + return rets + +def sumReturn(rewards): + return [sum(rewards) for e in rewards] + +def mergeRollouts(rollouts): + atnArgs = [rollout.atnArgs for rollout in rollouts] + vals = [rollout.vals for rollout in rollouts] + rets = [rollout.returns for rollout in rollouts] + + atnArgs = list(chain(*atnArgs)) + atnArgs = list(zip(*atnArgs)) + vals = list(chain(*vals)) + rets = list(chain(*rets)) + + return atnArgs, vals, rets + +class Rollout: + def __init__(self, returnf=discountRewards): + self.atnArgs = [] + self.vals = [] + self.rewards = [] + self.pop_rewards = [] + self.returnf = returnf + self.feather = Feather() + + def step(self, atnArgs, val, reward): + self.atnArgs.append(atnArgs) + self.vals.append(val) + self.rewards.append(reward) + + def finish(self): + self.rewards[-1] = -1 + self.returns = self.returnf(self.rewards) + self.lifespan = len(self.rewards) + self.feather.finish() + +#Rollout logger +class Feather: + def __init__(self): + self.expMap = set() + self.blob = Blob() + + def scrawl(self, stim, ent, val, reward): + self.blob.annID = ent.annID + tile = self.tile(stim) + self.move(tile, ent.pos) + #self.action(arguments, atnArgs) + self.stats(val, reward) + + def tile(self, stim): + R, C = stim.shape + rCent, cCent = R//2, C//2 + tile = stim[rCent, cCent] + return tile + + def action(self, arguments, atnArgs): + move, attk = arguments + moveArgs, attkArgs, _ = atnArgs + moveLogits, moveIdx = moveArgs + attkLogits, attkIdx = attkArgs + + def move(self, tile, pos): + tile = type(tile.state) + if pos not in self.expMap: + self.expMap.add(pos) + self.blob.unique[tile] += 1 + self.blob.counts[tile] += 1 + + def stats(self, value, reward): + self.blob.reward.append(reward) + self.blob.value.append(float(value)) + + def finish(self): + self.blob.finish() + diff --git a/forge/ethyr/stim.py b/forge/ethyr/stim.py new file mode 100644 index 00000000..2fb009cc --- /dev/null +++ b/forge/ethyr/stim.py @@ -0,0 +1,78 @@ +from pdb import set_trace as T +import numpy as np + +def center(x, mmax): + return (x - mmax/2.0)/mmax + +def oneHot(v, n): + ary = np.zeros(n) + ary[v] = 1 + return ary + +def entity(ent, other, config): + r, c = ent.pos + r = center(r, ent.R) + c = center(c, ent.C) + return stats(ent, other, config) + +def deltaOneHot(pos, stimSz): + n = 2 * stimSz + 1 + r, c = pos + r, c = r+stimSz, c+stimSz + r = r * n + idx = int(r + c) + return oneHot(idx, int(n**2)) + +def stats(ent, other, config): + health = ent.health.center() + food = ent.food.center() + water = ent.water.center() + + lifetime = center(ent.timeAlive, 100) + damage = ent.damage + if damage is None: + damage = 0 + damage = center(damage, 5) + freeze = center(ent.freeze, 2) + + #Cant use one hot colors because it causes dimensional + #conflicts when new pops are added at test time + sameColor = float(ent.colorInd == other.colorInd) - 0.5 + + rSelf, cSelf = ent.pos + r, c = center(rSelf, ent.R), center(cSelf, ent.C) + + rOther, cOther = other.pos + rDelta, cDelta = rOther - rSelf, cOther - cSelf + ret = np.array([lifetime, health, food, water, + r, c, rDelta, cDelta, damage, sameColor, freeze]) + return ret + +def environment(env, ent, sz, config): + R, C = env.shape + conv, ents = np.zeros((2, R, C)), [] + for r in range(R): + for c in range(C): + t, e = tile(ent, env[r, c], sz, config) + conv[:, r, c] = t + ents += e + assert len(ents) > 0 + ents = np.array(ents) + return conv, ents + +def tile(ent, t, sz, config): + nTiles = 8 + index = t.state.index + assert index >= 0 and index < nTiles + conv = [index, t.nEnts] + + ents = [] + r, c = ent.pos + for e in t.ents.values(): + statStim = stats(ent, e, config) + e.stim = statStim + ents.append(statStim) + + return conv, ents + + diff --git a/forge/ethyr/torch/__init__.py b/forge/ethyr/torch/__init__.py new file mode 100644 index 00000000..5c5d32e7 --- /dev/null +++ b/forge/ethyr/torch/__init__.py @@ -0,0 +1 @@ +from .stim import Stim diff --git a/forge/ethyr/torch/loss.py b/forge/ethyr/torch/loss.py new file mode 100644 index 00000000..999a2f2d --- /dev/null +++ b/forge/ethyr/torch/loss.py @@ -0,0 +1,73 @@ +from pdb import set_trace as T +import numpy as np + +import torch +from torch import nn +from torch.nn import functional as F + +def advantage(returns, val): + A = returns - val + adv = (A - A.mean()) / (1e-4 + A.std()) + adv = adv.detach() + return adv + +def policyLoss(logProb, atn, adv): + pgLoss = -logProb.gather(1, atn.view(-1, 1)) + return (pgLoss * adv).mean() + +def valueLoss(v, returns): + return (0.5 * (v - returns) **2).mean() + +def entropyLoss(prob, logProb): + return (prob * logProb).sum(1).mean() + +def pad(seq): + seq = [e.view(-1) for e in seq] + lens = [(len(e), idx) for idx, e in enumerate(seq)] + lens, idx = zip(*sorted(lens, reverse=True)) + seq = np.array(seq)[np.array(idx)] + + seq = torch.nn.utils.rnn.pad_sequence(seq, batch_first=True) + #seq = seq.squeeze(dim=1) + idx = torch.tensor(idx).view(-1, 1).expand_as(seq) + seq = seq.gather(0, idx) + + return seq + +def PG(pi, atn, val, returns): + prob = pad([F.softmax(e, dim=1) for e in pi]) + logProb = pad([F.log_softmax(e, dim=1) for e in pi]) + + adv = advantage(returns, val) + polLoss = policyLoss(logProb, atn, adv) + entLoss = entropyLoss(prob, logProb) + return polLoss, entLoss + +#A bit stale--not currently used because +#Vanilla PG is equivalent with 1 update per minibatch +class PPO(nn.Module): + def forward(self, pi, v, atn, returns): + prob, logProb = F.softmax(pi, dim=1), F.log_softmax(pi, dim=1) + atnProb = prob.gather(1, atn.view(-1, 1)) + + #Compute advantage + A = returns - v + adv = (A - A.mean()) / (1e-4 + A.std()) + adv = adv.detach() + + #Clipped ratio loss + prob, probOld, logProb = F.softmax(pi, dim=1), F.softmax(piOld, dim=1), F.log_softmax(pi, dim=1) + atnProb, atnProbOld = prob.gather(1, atn), probOld.gather(1, atn) + + ratio = atnProb / (atnProbOld + 1e-6) + surr1 = ratio*adv + surr2 = torch.clamp(ratio, min=1. - self.clip, max=1. + self.clip) * adv + policyLoss = -torch.min(surr1, surr2) + + #Compute value loss + valueLoss = (0.5 * (v - returns) **2).mean() + + #Compute entropy loss + entropyLoss = (prob * logProb).sum(1).mean() + + return policyLoss, valueLoss, entropyLoss diff --git a/forge/ethyr/torch/optim.py b/forge/ethyr/torch/optim.py new file mode 100644 index 00000000..eca6a738 --- /dev/null +++ b/forge/ethyr/torch/optim.py @@ -0,0 +1,43 @@ +import numpy as np +import torch +from torch import optim +from torch.autograd import Variable +from pdb import set_trace as T + +from forge.ethyr import rollouts +from forge.ethyr.torch import loss +from forge.ethyr.torch import param + +class ManualAdam(optim.Adam): + def step(self, grads): + grads = Variable(torch.Tensor(np.array(grads))) + self.param_groups[0]['params'][0].grad = grads + super().step() + +class ManualSGD(optim.SGD): + def step(self, grads): + grads = Variable(torch.Tensor(np.array(grads))) + self.param_groups[0]['params'][0].grad = grads + super().step() + +def backward(rolls, anns, valWeight=0.5, entWeight=0): + atns, vals, rets = rollouts.mergeRollouts(rolls.values()) + returns = torch.tensor(rets).view(-1, 1).float() + vals = torch.cat(vals) + pg, entropy, attackentropy = 0, 0, 0 + for i, atnList in enumerate(atns): + aArg, aArgIdx = list(zip(*atnList)) + aArgIdx = torch.stack(aArgIdx) + l, e = loss.PG(aArg, aArgIdx, vals, returns) + pg += l + entropy += e + + valLoss = loss.valueLoss(vals, returns) + totLoss = pg + valWeight*valLoss + entWeight*entropy + + totLoss.backward() + grads = [param.getGrads(ann) for ann in anns] + reward = np.mean(rets) + + return reward, vals.mean(), grads, pg, valLoss, entropy + diff --git a/forge/ethyr/torch/param.py b/forge/ethyr/torch/param.py new file mode 100644 index 00000000..3a9535ca --- /dev/null +++ b/forge/ethyr/torch/param.py @@ -0,0 +1,44 @@ +import numpy as np +import torch + +def zeroGrads(ann): + ind = 0 + for e in ann.parameters(): + if e.grad is None: + continue + shape = e.size() + nParams = np.prod(shape) + e.grad.data *= 0 + ind += nParams + +def setParameters(ann, meanVec): + ind = 0 + for e in ann.parameters(): + shape = e.size() + nParams = np.prod(shape) + e.data = torch.Tensor(np.array(meanVec[ind:ind+nParams]).reshape(*shape)) + ind += nParams + +def setGrads(ann, grads): + ind = 0 + for e in ann.parameters(): + shape = e.size() + nParams = np.prod(shape) + e.grad.data = torch.Tensor(np.array(grads[ind:ind+nParams]).reshape(*shape)) + ind += nParams + +def getParameters(ann): + ret = [] + for e in ann.parameters(): + ret += e.data.view(-1).numpy().tolist() + return ret + +def getGrads(ann): + ret = [] + for param, e in ann.named_parameters(): + try: + ret += e.grad.data.view(-1).numpy().tolist() + except: + print('Gradient dimension mismatch. This usually means you have either (1) loaded a model with a different architecture or (2) have a layer for which gradients are not available (e.g. not differentiable or more commonly not being used)') + return ret + diff --git a/forge/ethyr/torch/policy.py b/forge/ethyr/torch/policy.py new file mode 100644 index 00000000..4cd6ac53 --- /dev/null +++ b/forge/ethyr/torch/policy.py @@ -0,0 +1,273 @@ +#Previous networks tried. Fully connected nets are much +#easier to get working than conv nets. + +from pdb import set_trace as T +import torch +from torch import nn +from torch.nn import functional as F +from forge.ethyr.torch import utils as tu + +class Attention(nn.Module): + def __init__(self, xDim, yDim): + super().__init__() + self.fc = torch.nn.Linear(2*xDim, yDim) + + #Compute all normalized scores + def score(args, x, normalize=True, scale=False): + + scores = torch.matmul(args, x.transpose(0, 1)) + + if scale: + scores = scores / (32**0.5) + + if normalize: + scores = Attention.normalize(scores) + return scores.view(1, -1) + + #Normalize exp + def normalize(x): + b = x.max() + y = torch.exp(x - b) + return y / y.sum() + + def attend(self, args, scores): + attn = args * scores + return torch.sum(attn, dim=0).view(1, -1) + + def forward(self, args, x, normalize=True): + scores = Attention.score(args, x, normalize) + scores = self.attend(args, scores) + scores = torch.cat((scores, x), dim=1) + scores = torch.nn.functional.tanh(self.fc(scores)) + return scores + +class AttnCat(nn.Module): + def __init__(self, h): + super().__init__() + self.fc1 = torch.nn.Linear(2*h, h) + self.fc2 = torch.nn.Linear(h, 1) + self.h = h + + def forward(self, x, args): + n = args.shape[0] + x = x.expand(n, self.h) + xargs = torch.cat((x, args), dim=1) + + x = F.relu(self.fc1(xargs)) + x = self.fc2(x) + return x.view(1, -1) + +class AtnNet(nn.Module): + def __init__(self, h, nattn): + super().__init__() + self.fc1 = torch.nn.Linear(h, nattn) + + def forward(self, stim, args): + atn = self.fc1(stim) + return atn + +class ArgNet(nn.Module): + def __init__(self, h): + super().__init__() + self.attn = AttnCat(h) + + #Arguments: stim, action/argument embedding + def forward(self, key, atn, args): + atn = atn.expand_as(args) + vals = torch.cat((atn, args), 1) + arg = self.attn(key, vals) + argIdx = classify(arg) + return argIdx, argd + +class Env(nn.Module): + def __init__(self, config): + super().__init__() + h = config.HIDDEN + entDim = 12 + 255 + self.fc1 = torch.nn.Linear(entDim+1800+2*h, h) + self.embed = torch.nn.Embedding(7, 7) + self.ent1 = torch.nn.Linear(entDim, 2*h) + + def forward(self, conv, flat, ents): + tiles, nents = conv[0], conv[1] + tiles = self.embed(tiles.view(-1).long()).view(-1) + nents = nents.view(-1) + conv = torch.cat((tiles, nents)) + ents = self.ent1(ents) + ents, _ = torch.max(ents, 0) + x = torch.cat((conv.view(-1), flat, ents)).view(1, -1) + x = torch.nn.functional.relu(self.fc1(x)) + return x + +class Full(nn.Module): + def __init__(self, h): + super().__init__() + self.conv1 = tu.Conv2d(8, int(h/2), 3, stride=2) + self.conv2 = tu.Conv2d(int(h/2), h, 3, stride=2) + self.fc1 = torch.nn.Linear(6+4*4*h + h, h) + #self.fc1 = torch.nn.Linear(5+4*4*h, h) + self.ent1 = torch.nn.Linear(6, h) + + def forward(self, conv, flat, ents): + if len(conv.shape) == 3: + conv = conv.view(1, *conv.shape) + flat = flat.view(1, *flat.shape) + + x, batch = conv, conv.shape[0] + x = torch.nn.functional.relu(self.conv1(x)) + x = torch.nn.functional.relu(self.conv2(x)) + x = x.view(batch, -1) + + ents = self.ent1(ents) + ents, _ = torch.max(ents, 0) + ents = ents.view(batch, -1) + + #x = torch.cat((x, flat), dim=1) + x = torch.cat((x, flat, ents), dim=1) + + x = torch.nn.functional.relu(self.fc1(x)) + return x + +class FC1(nn.Module): + def __init__(self, h): + super().__init__() + #h = config.HIDDEN + self.fc1 = torch.nn.Linear(12+1800, h) + + def forward(self, conv, flat, ents): + x = torch.cat((conv.view(-1), flat)).view(1, -1) + x = torch.nn.functional.relu(self.fc1(x)) + return x + +class FC2(nn.Module): + def __init__(self, config): + super().__init__() + h = config.HIDDEN + self.fc1 = torch.nn.Linear(12+1800+2*h, h) + self.ent1 = torch.nn.Linear(12, 2*h) + self.embed = torch.nn.Embedding(7, 7) + + def forward(self, conv, flat, ents): + tiles, nents = conv[0], conv[1] + tiles = self.embed(tiles.view(-1).long()).view(-1) + nents = nents.view(-1) + conv = torch.cat((tiles, nents)) + ents = self.ent1(ents) + ents, _ = torch.max(ents, 0) + x = torch.cat((conv.view(-1), flat, ents)).view(1, -1) + x = torch.nn.functional.relu(self.fc1(x)) + return x + +#Use this one. Nice embedding property. +#They all work pretty well +class FC3(nn.Module): + def __init__(self, h): + super().__init__() + self.conv1 = tu.Conv2d(8, 8, 1) + self.fc1 = tu.FCRelu(5+1800, h) + + def forward(self, conv, flat, ents): + conv = conv.view(1, *conv.shape) + x = self.conv1(conv) + x = torch.cat((x.view(-1), flat)).view(1, -1) + x = self.fc1(x) + return x + +class FC4(nn.Module): + def __init__(self, h): + super().__init__() + self.conv1 = tu.Conv2d(8, 4, 1) + self.fc1 = tu.FCRelu(5+900, h) + + def forward(self, conv, flat, ents): + conv = conv.view(1, *conv.shape) + x = self.conv1(conv) + x = torch.cat((x.view(-1), flat)).view(1, -1) + x = self.fc1(x) + return x + +class FCEnt(nn.Module): + def __init__(self, h): + super().__init__() + self.fc1 = torch.nn.Linear(5+1800+h, h) + self.ent1 = torch.nn.Linear(5, h) + + def forward(self, conv, flat, ents): + ents = self.ent1(ents) + ents, _ = torch.max(ents, 0) + ents = ents.view(-1) + + x = torch.cat((conv.view(-1), flat, ents)).view(1, -1) + x = torch.nn.functional.relu(self.fc1(x)) + return x + +class FCAttention(nn.Module): + def __init__(self, h): + super().__init__() + self.fc1 = torch.nn.Linear(5+1800+h, h) + self.ent1 = torch.nn.Linear(5, h) + self.attn = Attention(h, h) + + def forward(self, conv, flat, ents): + ents = self.ent1(ents) + T() + ents = self.attn(ents) + ents = ents.view(-1) + + x = torch.cat((conv.view(-1), flat, ents)).view(1, -1) + x = torch.nn.functional.relu(self.fc1(x)) + return x + + +class CNN1(nn.Module): + def __init__(self, h): + super().__init__() + self.conv1 = tu.Conv2d(8, h, 5, stride=3) + self.conv2 = tu.Conv2d(h, h, 5, stride=3) + self.fc1 = torch.nn.Linear(4*h+5, h) + + def forward(self, conv, flat, ents): + x = conv.view(1, *conv.shape) + x = torch.nn.functional.relu(self.conv1(x)) + x = torch.nn.functional.relu(self.conv2(x)) + x = x.view(-1) + + x = torch.cat((x, flat)) + x = torch.nn.functional.relu(self.fc1(x)) + return x.view(1, -1) + +class CNN2(nn.Module): + def __init__(self, h): + super().__init__() + self.conv1 = tu.Conv2d(8, h, 3, stride=2) + self.conv2 = tu.Conv2d(h, h, 3, stride=2) + self.fc1 = torch.nn.Linear(16*h+5, h) + + def forward(self, conv, flat, ents): + x = conv.view(1, *conv.shape) + x = torch.nn.functional.relu(self.conv1(x)) + x = torch.nn.functional.relu(self.conv2(x)) + x = x.view(-1) + + x = torch.cat((x, flat)) + x = torch.nn.functional.relu(self.fc1(x)) + return x.view(1, -1) + +class CNN3(nn.Module): + def __init__(self, h): + super().__init__() + self.conv1 = tu.ConvReluPool(8, h, 5) + self.conv2 = tu.ConvReluPool(h, h//2, 5) + self.fc1 = torch.nn.Linear(h//2*7*7+5, h) + + def forward(self, conv, flat, ents): + x = conv.view(1, *conv.shape) + x = torch.nn.functional.relu(self.conv1(x)) + x = torch.nn.functional.relu(self.conv2(x)) + x = x.view(-1) + + x = torch.cat((x, flat)) + x = torch.nn.functional.relu(self.fc1(x)) + return x.view(1, -1) + + diff --git a/forge/ethyr/torch/save.py b/forge/ethyr/torch/save.py new file mode 100644 index 00000000..687fb8f1 --- /dev/null +++ b/forge/ethyr/torch/save.py @@ -0,0 +1,69 @@ +from pdb import set_trace as T +import numpy as np +import torch +import time +from forge.blade.lib.utils import EDA + +class Resetter: + def __init__(self, resetTol): + self.resetTicks, self.resetTol = 0, resetTol + def step(self, best=False): + if best: + self.resetTicks = 0 + elif self.resetTicks < self.resetTol: + self.resetTicks += 1 + else: + self.resetTicks = 0 + return True + return False + +class Saver: + def __init__(self, nANN, root, savef, bestf, resetTol): + self.bestf, self.savef = bestf, savef, + self.root, self.extn = root, '.pth' + self.nANN = nANN + + self.resetter = Resetter(resetTol) + self.rewardAvg, self.best = EDA(), 0 + self.start, self.epoch = time.time(), 0 + self.resetTol = resetTol + + def save(self, params, opt, fname): + data = {'param': params, + 'opt' : opt.state_dict(), + 'epoch': self.epoch} + torch.save(data, self.root + fname + self.extn) + + def checkpoint(self, params, opt, reward): + self.save(params, opt, self.savef) + best = reward > self.best + if best: + self.best = reward + self.save(params, opt, self.bestf) + + self.time = time.time() - self.start + self.start = time.time() + self.reward = reward + self.epoch += 1 + + if self.epoch % 100 == 0: + self.save(params, opt, 'model'+str(self.epoch)) + + return self.resetter.step(best) + + def load(self, opt, param, best=False): + fname = self.bestf if best else self.savef + data = torch.load(self.root + fname + self.extn) + param.data = data['param'] + if opt is not None: + opt.load_state_dict(data['opt']) + epoch = data['epoch'] + return epoch + + def print(self): + print( + 'Tick: ', self.epoch, + ', Time: ', str(self.time)[:5], + ', Lifetime: ', str(self.reward)[:5], + ', Best: ', str(self.best)[:5]) + diff --git a/forge/ethyr/torch/stim.py b/forge/ethyr/torch/stim.py new file mode 100644 index 00000000..d8b0f3fd --- /dev/null +++ b/forge/ethyr/torch/stim.py @@ -0,0 +1,14 @@ +from forge.ethyr.torch import utils as tu +from forge.ethyr import stim + +from pdb import set_trace as T + +class Stim: + def __init__(self, ent, env, config): + sz = config.STIM + flat = stim.entity(ent, ent, config) + conv, ents = stim.environment(env, ent, sz, config) + + self.flat = tu.var(flat) + self.conv = tu.var(conv) + self.ents = tu.var(ents) diff --git a/forge/ethyr/torch/utils.py b/forge/ethyr/torch/utils.py new file mode 100644 index 00000000..a72e3283 --- /dev/null +++ b/forge/ethyr/torch/utils.py @@ -0,0 +1,72 @@ +import numpy as np +import torch +from torch import nn +from torch.autograd import Variable + +#Print model size +def modelSize(net): + params = 0 + for e in net.parameters(): + params += np.prod(e.size()) + params = int(params/1000) + print("Network has ", params, "K params") + +#Same padded (odd k) +def Conv2d(fIn, fOut, k, stride=1): + pad = int((k-1)/2) + return torch.nn.Conv2d(fIn, fOut, k, stride=stride, padding=pad) + +def Pool(k, stride=1, pad=0): + #pad = int((k-1)/2) + return torch.nn.MaxPool2d(k, stride=stride, padding=pad) + +def Relu(): + return torch.nn.ReLU() + +class FCRelu(nn.Module): + def __init__(self, xdim, ydim): + super().__init__() + self.fc = torch.nn.Linear(xdim, ydim) + self.relu = Relu() + + def forward(self, x): + x = self.fc(x) + x = self.relu(x) + return x + +class ConvReluPool(nn.Module): + def __init__(self, fIn, fOut, k, stride=1, pool=2): + super().__init__() + self.conv = Conv2d(fIn, fOut, k, stride) + self.relu = Relu() + self.pool = Pool(k) + + def forward(self, x): + x = self.conv(x) + x = self.relu(x) + x = self.pool(x) + return x + +#ModuleList wrapper +def moduleList(module, *args, n=1): + return nn.ModuleList([module(*args) for i in range(n)]) + +#Variable wrapper +def var(xNp, volatile=False, cuda=False): + x = Variable(torch.from_numpy(xNp), volatile=volatile).float() + if cuda: + x = x.cuda() + return x + +#Full-network initialization wrapper +def initWeights(net, scheme='orthogonal'): + print('Initializing weights. Warning: may overwrite sensitive bias parameters (e.g. batchnorm)') + for e in net.parameters(): + if scheme == 'orthogonal': + if len(e.size()) >= 2: + init.orthogonal(e) + elif scheme == 'normal': + init.normal(e, std=1e-2) + elif scheme == 'xavier': + init.xavier_normal(e) + diff --git a/forge/trinity/__init__.py b/forge/trinity/__init__.py new file mode 100644 index 00000000..b74d60d8 --- /dev/null +++ b/forge/trinity/__init__.py @@ -0,0 +1,5 @@ +from .trinity import Trinity +from .pantheon import Pantheon +from .god import God +from .sword import Sword +from .ann import ANN diff --git a/forge/trinity/ann.py b/forge/trinity/ann.py new file mode 100644 index 00000000..1e47d436 --- /dev/null +++ b/forge/trinity/ann.py @@ -0,0 +1,281 @@ +from pdb import set_trace as T +import numpy as np +import torch + +from torch import nn +from torch.nn import functional as F +from torch.distributions import Categorical + +from forge.blade.action.tree import ActionTree +from forge.blade.action.v2 import ActionV2 +from forge.blade.lib.enums import Neon +from forge.blade.lib import enums +from forge.ethyr import torch as torchlib +from forge.blade import entity + +def classify(logits): + if len(logits.shape) == 1: + logits = logits.view(1, -1) + distribution = Categorical(1e-3+F.softmax(logits, dim=1)) + atn = distribution.sample() + return atn + +####### Network Modules +class ConstDiscrete(nn.Module): + def __init__(self, config, h, nattn): + super().__init__() + self.fc1 = torch.nn.Linear(h, nattn) + self.config = config + + def forward(self, env, ent, action, stim): + leaves = action.args(env, ent, self.config) + x = self.fc1(stim) + xIdx = classify(x) + leaf = leaves[int(xIdx)] + return leaf, x, xIdx + +class VariableDiscrete(nn.Module): + def __init__(self, config, xdim, h): + super().__init__() + self.attn = AttnCat(xdim, h) + self.config = config + + #Arguments: stim, action/argument embedding + def forward(self, env, ent, action, key, vals): + leaves = action.args(env, ent, self.config) + x = self.attn(key, vals) + xIdx = classify(x) + leaf = leaves[int(xIdx)] + return leaf, x, xIdx + +class AttnCat(nn.Module): + def __init__(self, xdim, h): + super().__init__() + #self.fc1 = torch.nn.Linear(xdim, h) + #self.fc2 = torch.nn.Linear(h, 1) + self.fc = torch.nn.Linear(xdim, 1) + self.h = h + + def forward(self, x, args): + n = args.shape[0] + x = x.expand(n, self.h) + xargs = torch.cat((x, args), dim=1) + + x = self.fc(xargs) + #x = F.relu(self.fc1(xargs)) + #x = self.fc2(x) + return x.view(1, -1) +####### End network modules + +class ValNet(nn.Module): + def __init__(self, config): + super().__init__() + self.fc = torch.nn.Linear(config.HIDDEN, 1) + self.envNet = Env(config) + + def forward(self, conv, flat, ent): + stim = self.envNet(conv, flat, ent) + x = self.fc(stim) + x = x.view(1, -1) + return x + +class Ent(nn.Module): + def __init__(self, entDim, h): + super().__init__() + self.ent = torch.nn.Linear(entDim, h) + + def forward(self, ents): + ents = self.ent(ents) + ents, _ = torch.max(ents, 0) + return ents + +class Env(nn.Module): + def __init__(self, config): + super().__init__() + h = config.HIDDEN + entDim = 11 # + 225 + + self.fc1 = torch.nn.Linear(3*h, h) + self.embed = torch.nn.Embedding(7, 7) + + self.conv = torch.nn.Linear(1800, h) + self.flat = torch.nn.Linear(entDim, h) + self.ents = Ent(entDim, h) + + def forward(self, conv, flat, ents): + tiles, nents = conv[0], conv[1] + nents = nents.view(-1) + + tiles = self.embed(tiles.view(-1).long()).view(-1) + conv = torch.cat((tiles, nents)) + + conv = self.conv(conv) + ents = self.ents(ents) + flat = self.flat(flat) + + x = torch.cat((conv, flat, ents)).view(1, -1) + x = self.fc1(x) + #Removed relu (easier training, lower policy cap) + #x = torch.nn.functional.relu(self.fc1(x)) + return x + +class MoveNet(nn.Module): + def __init__(self, config): + super().__init__() + self.moveNet = ConstDiscrete(config, config.HIDDEN, 5) + self.envNet = Env(config) + + def forward(self, env, ent, action, s): + stim = self.envNet(s.conv, s.flat, s.ents) + action, arg, argIdx = self.moveNet(env, ent, action, stim) + return action, (arg, argIdx) + +#Network that selects an attack style +class StyleAttackNet(nn.Module): + def __init__(self, config): + super().__init__() + self.config, h = config, config.HIDDEN + self.h = h + + self.envNet = Env(config) + self.targNet = ConstDiscrete(config, h, 3) + + def target(self, ent, arguments): + if len(arguments) == 1: + return arguments[0] + arguments = [e for e in arguments if e.entID != ent.entID] + arguments = sorted(arguments, key=lambda a: a.health.val) + return arguments[0] + + def forward(self, env, ent, action, s): + stim = self.envNet(s.conv, s.flat, s.ents) + action, atn, atnIdx = self.targNet(env, ent, action, stim) + + #Hardcoded targeting + arguments = action.args(env, ent, self.config) + argument = self.target(ent, arguments) + + attkOuts = [(atn, atnIdx)] + return action, [argument], attkOuts + +#Network that selects an attack and target (In progress, +#for learned targeting experiments) +class AttackNet(nn.Module): + def __init__(self, config): + super().__init__() + self.config, h = config, config.HIDDEN + entDim = 11 + self.styleEmbed = torch.nn.Embedding(3, h) + self.targEmbed = Ent(entDim, h) + self.h = h + + self.envNet = Env(config) + self.styleNet = ConstDiscrete(config, h, 3) + self.targNet = VariableDiscrete(config, 3*h, h) + + def forward(self, env, ent, action, s): + stim = self.envNet(s.conv, s.flat, s.ents) + action, atn, atnIdx = self.styleNet(env, ent, action, stim) + + #Embed targets + targets = action.args(env, ent, self.config) + targets = torch.tensor([e.stim for e in targets]).float() + targets = self.targEmbed(targets).unsqueeze(0) + nTargs = len(targets) + + atns = self.styleEmbed(atnIdx).expand(nTargs, self.h) + vals = torch.cat((atns, targets), 1) + + argument, arg, argIdx = self.targNet( + env, ent, action, stim, vals) + + attkOuts = ((atn, atnIdx), (arg, argIdx)) + return action, [argument], attkOuts + +class ANN(nn.Module): + def __init__(self, config): + super().__init__() + self.valNet = ValNet(config) + + self.config = config + self.moveNet = MoveNet(config) + self.attackNet = (StyleAttackNet(config) if + config.AUTO_TARGET else AttackNet(config)) + + def forward(self, ent, env): + s = torchlib.Stim(ent, env, self.config) + val = self.valNet(s.conv, s.flat, s.ents) + + actions = ActionTree(env, ent, ActionV2).actions() + _, move, attk = actions + + #Actions + moveArg, moveOuts = self.moveNet( + env, ent, move, s) + attk, attkArg, attkOuts = self.attackNet( + env, ent, attk, s) + + action = (move, attk) + arguments = (moveArg, attkArg) + outs = (moveOuts, *attkOuts) + return action, arguments, outs, val + + #Messy hooks for visualizers + def visDeps(self): + from forge.blade.core import realm + from forge.blade.core.tile import Tile + colorInd = int(12*np.random.rand()) + color = Neon.color12()[colorInd] + color = (colorInd, color) + ent = realm.Desciple(-1, self.config, color).server + targ = realm.Desciple(-1, self.config, color).server + + sz = 15 + tiles = np.zeros((sz, sz), dtype=object) + for r in range(sz): + for c in range(sz): + tiles[r, c] = Tile(enums.Grass, r, c, 1, None) + + targ.pos = (7, 7) + tiles[7, 7].addEnt(0, targ) + posList, vals = [], [] + for r in range(sz): + for c in range(sz): + ent.pos = (r, c) + tiles[r, c].addEnt(1, ent) + s = torchlib.Stim(ent, tiles, self.config) + conv, flat, ents = s.conv, s.flat, s.ents + val = self.valNet(conv, s.flat, s.ents) + vals.append(float(val)) + tiles[r, c].delEnt(1) + posList.append((r, c)) + vals = list(zip(posList, vals)) + return vals + + def visVals(self, food='max', water='max'): + from forge.blade.core import realm + posList, vals = [], [] + R, C = self.world.shape + for r in range(self.config.BORDER, R-self.config.BORDER): + for c in range(self.config.BORDER, C-self.config.BORDER): + colorInd = int(12*np.random.rand()) + color = Neon.color12()[colorInd] + color = (colorInd, color) + ent = entity.Player(-1, color, self.config) + ent._pos = (r, c) + + if food != 'max': + ent._food = food + if water != 'max': + ent._water = water + posList.append(ent.pos) + + self.world.env.tiles[r, c].addEnt(ent.entID, ent) + stim = self.world.env.stim(ent.pos, self.config.STIM) + s = torchlib.Stim(ent, stim, self.config) + val = self.valNet(s.conv, s.flat, s.ents).detach() + self.world.env.tiles[r, c].delEnt(ent.entID) + vals.append(float(val)) + + vals = list(zip(posList, vals)) + return vals diff --git a/forge/trinity/god.py b/forge/trinity/god.py new file mode 100644 index 00000000..3521d29e --- /dev/null +++ b/forge/trinity/god.py @@ -0,0 +1,38 @@ +from pdb import set_trace as T +from forge.blade.lib.enums import Palette +import numpy as np + +class God: + def __init__(self, config, args): + self.config, self.args = config, args + self.nEnt, self.nANN = config.NENT, config.NPOP + self.popSz = self.nEnt // self.nANN + self.popCounts = np.zeros(self.nANN) + self.palette = Palette(config.NPOP) + self.entID = 0 + + #Returns IDs for spawning + def spawn(self): + entID = str(self.entID) + annID = hash(entID) % self.nANN + self.entID += 1 + + assert self.popCounts[annID] <= self.popSz + if self.popCounts[annID] == self.popSz: + return self.spawn() + + self.popCounts[annID] += 1 + color = self.palette.color(annID) + + return entID, (annID, color) + + def cull(self, annID): + self.popCounts[annID] -= 1 + assert self.popCounts[annID] >= 0 + + def send(self): + return + + def recv(self, pantheonUpdates): + return + diff --git a/forge/trinity/pantheon.py b/forge/trinity/pantheon.py new file mode 100644 index 00000000..687a0006 --- /dev/null +++ b/forge/trinity/pantheon.py @@ -0,0 +1,115 @@ +from pdb import set_trace as T +import numpy as np +import torch +import time + +from collections import defaultdict +from torch.nn.parameter import Parameter + +from forge.ethyr.torch import save +from forge.ethyr.torch.optim import ManualAdam, ManualSGD +from forge.ethyr.torch.param import getParameters +from forge.blade.lib.log import Quill +from forge import trinity + +class Model: + def __init__(self, config, args): + self.saver = save.Saver(config.NPOP, config.MODELDIR, + 'models', 'bests', resetTol=256) + self.config, self.args = config, args + + self.init() + if self.config.LOAD or self.config.BEST: + self.load(self.config.BEST) + + def init(self): + print('Initializing new model...') + if self.config.SHAREINIT: + self.shared(self.config.NPOP) + else: + self.unshared(self.config.NPOP) + + self.params = Parameter(torch.Tensor(np.array(self.models))) + self.opt = None + if not self.config.TEST: + self.opt = ManualAdam([self.params], lr=0.001, weight_decay=0.00001) + + #Initialize a new network + def initModel(self): + return getParameters(trinity.ANN(self.config)) + + def shared(self, n): + model = self.initModel() + self.models = [model for _ in range(n)] + + def unshared(self, n): + self.models = [self.initModel() for _ in range(n)] + + #Grads and clip + def stepOpt(self, gradDicts): + grads = defaultdict(list) + keysets = [grads.keys() for grads in gradDicts] + for gradDict in gradDicts: + for worker, grad in gradDict.items(): + grads[worker].append(grad) + for worker, gradList in grads.items(): + grad = np.array(gradList) + grad = np.mean(grad, 0) + grad = np.clip(grad, -5, 5) + grads[worker] = grad + gradAry = torch.zeros_like(self.params) + for worker, grad in grads.items(): + gradAry[worker] = torch.Tensor(grad) + self.opt.step(gradAry) + + def checkpoint(self, reward): + if self.config.TEST: + return + self.saver.checkpoint(self.params, self.opt, reward) + + def load(self, best=False): + print('Loading model...') + epoch = self.saver.load( + self.opt, self.params, best) + + @property + def nParams(self): + nParams = sum([len(e) for e in self.model]) + print('#Params: ', str(nParams/1000), 'K') + + @property + def model(self): + return self.params.detach().numpy() + +class Pantheon: + def __init__(self, config, args): + self.start, self.tick, self.nANN = time.time(), 0, config.NPOP + self.config, self.args = config, args + self.net = Model(config, args) + self.quill = Quill(config.MODELDIR) + self.log = defaultdict(list) + self.net.nParams + + self.period = 1 + + @property + def model(self): + return self.net.model + + def step(self, recvs): + recvs, logs = list(zip(*recvs)) + + #Write logs + self.quill.scrawl(logs) + self.tick += 1 + + if not self.config.TEST: + lifetime = self.quill.latest() + self.net.stepOpt(recvs) + self.net.checkpoint(lifetime) + self.net.saver.print() + else: + self.quill.print() + + return self.model + diff --git a/forge/trinity/smith.py b/forge/trinity/smith.py new file mode 100644 index 00000000..d299ce98 --- /dev/null +++ b/forge/trinity/smith.py @@ -0,0 +1,107 @@ +import ray, pickle +from pdb import set_trace as T +from forge.blade import core, lib + +#Wrapper for remote async multi environments (realms) +#Supports both the native and vecenv per-env api + +class VecEnvServer: + def __init__(self, config, args): + self.envs = [core.VecEnvRealm.remote(config, args, i) + for i in range(args.nRealm)] + + #Reset the environments (only for vecenv api. This only returns + #initial empty buffers to avoid special-case first iteration + #code. Environments are persistent--attempting to reset them + #will result in undefined behavior. Don't do it after setup. + def reset(self): + recvs = [e.reset.remote() for e in self.envs] + return ray.get(recvs) + + def step(self, actions): + recvs = ray.get([e.step.remote(pickle.dumps(a)) for e, a in + zip(self.envs, actions)]) + recvs = [pickle.loads(e) for e in recvs] + return zip(*recvs) + +class NativeServer: + def __init__(self, config, args, trinity): + self.envs = [core.NativeRealm.remote(trinity, config, args, i) + for i in range(args.nRealm)] + + def step(self, actions=None): + recvs = [e.step.remote() for e in self.envs] + return ray.get(recvs) + + #Use native api (runs full trajectories) + def run(self, swordUpdate=None): + recvs = [e.run.remote(swordUpdate) for e in self.envs] + recvs = ray.get(recvs) + return recvs + + def send(self, swordUpdate): + [e.recvSwordUpdate.remote(swordUpdate) for e in self.envs] + + +#Example base runner class +class Blacksmith: + def __init__(self, config, args): + if args.render: + print('Enabling local test mode for render') + args.ray = 'local' + args.nRealm = 1 + + lib.ray.init(args.ray) + + def render(self): + from forge.embyr.twistedserver import Application + Application(self.env, self.renderStep) + +#Example runner using the (slower) vecenv api +#The actual vecenv spec was not designed for +#multiagent, so this is a best-effort facsimile +class VecEnv(Blacksmith): + def __init__(self, config, args, renderStep): + super().__init__(config, args) + self.env = VecEnvServer(config, args) + self.renderStep = renderStep + + def step(self, actions): + return self.env.step(actions) + + def reset(self): + return self.env.reset() + +#Example runner using the (faster) native api +#Use the /forge/trinity/ spec for model code +class Native(Blacksmith): + def __init__(self, config, args, trinity): + super().__init__(config, args) + self.pantheon = trinity.pantheon(config, args) + self.trinity = trinity + + self.env = NativeServer(config, args, trinity) + self.env.send(self.pantheon.model) + + self.renderStep = self.step + self.idx = 0 + + #Runs full trajectories on each environment + #With no communication -- all on the env cores. + def run(self): + recvs = self.env.run(self.pantheon.model) + self.pantheon.step(recvs) + self.rayBuffers() + + #Only for render -- steps are run per core + def step(self): + self.env.step() + + #In early versions of ray, freeing memory was + #an issue. It is possible this has been patched. + def rayBuffers(self): + self.idx += 1 + if self.idx % 32 == 0: + lib.ray.clearbuffers() + + diff --git a/forge/trinity/sword.py b/forge/trinity/sword.py new file mode 100644 index 00000000..d5775094 --- /dev/null +++ b/forge/trinity/sword.py @@ -0,0 +1,85 @@ +from pdb import set_trace as T +from collections import defaultdict +import numpy as np + +from forge import trinity +from forge.ethyr.torch.param import setParameters, zeroGrads +from forge.ethyr.torch import optim +from forge.ethyr.rollouts import Rollout + +class Sword: + def __init__(self, config, args): + self.config, self.args = config, args + self.nANN, self.h = config.NPOP, config.HIDDEN + self.anns = [trinity.ANN(config) + for i in range(self.nANN)] + + self.init, self.nRollouts = True, 32 + self.networksUsed = set() + self.updates, self.rollouts = defaultdict(Rollout), {} + self.ents, self.rewards, self.grads = {}, [], None + self.nGrads = 0 + + def backward(self): + ents = self.rollouts.keys() + anns = [self.anns[idx] for idx in self.networksUsed] + + reward, val, grads, pg, valLoss, entropy = optim.backward( + self.rollouts, anns, valWeight=0.25, + entWeight=self.config.ENTROPY) + self.grads = dict((idx, grad) for idx, grad in + zip(self.networksUsed, grads)) + + self.blobs = [r.feather.blob for r in self.rollouts.values()] + self.rollouts = {} + self.nGrads = 0 + self.networksUsed = set() + + def sendGradUpdate(self): + grads = self.grads + self.grads = None + return grads + + def sendLogUpdate(self): + blobs = self.blobs + self.blobs = [] + return blobs + + def sendUpdate(self): + if self.grads is None: + return None, None + return self.sendGradUpdate(), self.sendLogUpdate() + + def recvUpdate(self, update): + for idx, paramVec in enumerate(update): + setParameters(self.anns[idx], paramVec) + zeroGrads(self.anns[idx]) + + def collectStep(self, entID, atnArgs, val, reward): + if self.config.TEST: + return + self.updates[entID].step(atnArgs, val, reward) + + def collectRollout(self, entID, ent): + assert entID not in self.rollouts + rollout = self.updates[entID] + rollout.finish() + self.nGrads += rollout.lifespan + self.rollouts[entID] = rollout + del self.updates[entID] + + # assert ent.annID == (hash(entID) % self.nANN) + self.networksUsed.add(ent.annID) + + #Two options: fixed number of gradients or rollouts + #if len(self.rollouts) >= self.nRollouts: + if self.nGrads >= 100*32: + self.backward() + + def decide(self, ent, stim): + reward, entID, annID = 0, ent.entID, ent.annID + action, arguments, atnArgs, val = self.anns[annID](ent, stim) + self.collectStep(entID, atnArgs, val, reward) + self.updates[entID].feather.scrawl( + stim, ent, val, reward) + return action, arguments, float(val) diff --git a/forge/trinity/trinity.py b/forge/trinity/trinity.py new file mode 100644 index 00000000..9b8b090e --- /dev/null +++ b/forge/trinity/trinity.py @@ -0,0 +1,26 @@ +class Trinity: + def __init__(self, pantheon, god, sword): + self.pantheon = pantheon + self.god = god + self.sword = sword + +#Cluster/Master logic +class Pantheon: + def __init__(self, args): pass + def step(self, recvs): pass + +#Environment logic +class God: + def __init__(self, args, idx): pass + def spawn(self): pass + def send(self): pass + def recv(self, pantheonUpdates): pass + +#Agent logic +class Sword: + def __init__(self, args): pass + def sendUpdate(self): pass + def recvUpdate(self, update): pass + def collectRollout(self, entID, ent): pass + def decide(self, entID, ent, stim): pass + diff --git a/jsuarez/Baselines.py b/jsuarez/Baselines.py new file mode 100644 index 00000000..4b4266ab --- /dev/null +++ b/jsuarez/Baselines.py @@ -0,0 +1,49 @@ +class NaiveRandom(): + def __init__(self, cuda=False): + #self.cpu = cpu + self.alive = True + self.timeAlive = 0 + + def reproduce(self): + return NaiveRandom() + + def interact(self, pc, stimuli, actions): + self.timeAlive += 1 + return self.decide(pc, stimuli, actions) + + def death(self): + self.alive = False + + def decide(self, pc, stimuli, actionTree): + done = False + while not actionTree.isLeaf: + actionTree.randomNode() + + #Note: will pass if no valid args + if type(actionTree.action) == Actions.Reproduce: + self.reproduce() + actionTree.decideArgs([]) + else: + actionTree.randomArgs() + if actionTree.args is None: + return actionTree.passActionArgs() + + action, args = actionTree.actionArgPair() + #print(str(action) + ' ' + str(args)) + return action, args + +class AlwaysPass(): + def __init__(self, cuda=False): + self.alive = True + self.timeAlive = 0 + + def reproduce(self): + return self.cpu.reproduce() + + def decide(self, pc, stimuli, actionTree): + self.timeAlive += 1 + return Actions.Pass(), [] + + def death(self): + self.alive = False + diff --git a/jsuarez/BatchSnippets.py b/jsuarez/BatchSnippets.py new file mode 100644 index 00000000..6e82c92d --- /dev/null +++ b/jsuarez/BatchSnippets.py @@ -0,0 +1,122 @@ + ann, rets = self.anns[0], [] + for entID, ent, stim in ents: + annID = hash(entID) % self.nANN + unpacked = unpackStim(ent, stim) + self.anns[annID].recv(unpacked, ent, stim, entID) + + #Ret order matters + for logits, val, ent, stim, atn, entID in ann.send(): + action, args = self.actionArgs(stim, ent, atn.item()) + rets.append((action, args, float(val))) + if ent.alive and not self.args.test: + self.collectStep(entID, (logits, val, atn)) + return rets + +class CosineNet(nn.Module): + def __init__(self, xdim, h, ydim): + super().__init__() + self.feats = FeatNet(xdim, h, ydim) + self.fc1 = torch.nn.Linear(h, h) + self.ent1 = torch.nn.Linear(5, h) + + def forward(self, stim, conv, flat, ents, ent, actions): + x = self.feats(conv, flat, ents) + x = self.fc1(x) + arguments = actions.args(stim, ent) + ents = torch.tensor(np.array([e.stim for + e in arguments])).float() + args = self.ent1(ents) #center this in preprocess + + arg, argIdx = CosineClassifier(x, args) + argument = [arguments[int(argIdx)]] + return actions, argument, (arg, argIdx) + +def CosineClassifier(x, a): + ret = torch.sum(x*a, dim=1).view(1, -1) + return ret, classify(ret) + +class AtnNet(nn.Module): + def __init__(self, xdim, h, ydim): + super().__init__() + self.feats = FeatNet(xdim, h, ydim) + self.atn1 = torch.nn.Linear(h, 2) + + def forward(self, conv, flat, ent, flatEnts, actions): + x = self.feats(conv, flat, flatEnts) + atn = self.atn1(x) + atnIdx = classify(atn) + return x, atn, atnIdx + +class ActionEmbed(nn.Module): + def __init__(self, nEmbed, dim): + super().__init__() + self.embed = torch.nn.Embedding(nEmbed, dim) + self.atnIdx = {} + + def forward(self, actions): + idxs = [] + for a in actions: + if a not in self.atnIdx: + self.atnIdx[a] = len(self.atnIdx) + idxs.append(self.atnIdx[a]) + idxs = torch.tensor(idxs) + atns = self.embed(idxs) + return atns + +def vDiffs(v): + pad = v[0] * 0 + diffs = [vNew - vOld for vNew, vOld in zip(v[1:], v[:-1])] + vRet = diffs + [pad] + return vRet + +def embedArgsLists(argsLists): + args = [embedArgs(args) for args in argsLists] + return np.stack(args) + +def embedArgs(args): + args = [embedArg(arg) for arg in args] + return np.concatenate(args) + +def embedArg(arg): + arg = Arg(arg) + arg = oneHot(arg.val - arg.min, arg.n) + return arg + +def matOneHot(mat, dim): + r, c = mat.shape + x = np.zeros((r, c, dim)) + for i in range(r): + for j in range(c): + x[i, j, mat[i,j]] = 1 + return x + +#Old unzip. Don't use. Soft breaks PG +def unzipRollouts(rollouts): + atnArgList, atnArgIdxList, valList, rewList = [], [], [], [] + for atnArgs, val, rew in rollouts: + for atnArg, idx in atnArgs: + atnArgList.append(atnArg) + atnArgIdxList.append(idx) + valList.append(val) + rewList.append(rew) + atnArgs = atnArgList + atnArgsIdx = torch.stack(atnArgIdxList) + vals = torch.stack(valList).view(-1, 1) + rews = torch.tensor(rewList).view(-1, 1).float() + return atnArgs, atnArgsIdx, vals, rews + +def l1Range(ent, sz, me, rng): + R, C = sz + rs, cs = me.pos + rt = max(0, rs-rng) + rb = min(R, rs+rng+1) + cl = max(0, cs-rng) + cr = min(C, cs+rng+1) + ret = [] + for r in range(rt, rb): + for c in range(cl, cr): + if me in ent[r, c].ents: + continue + if len(ent[r, c].ents) > 0: + ret += ent[r, c].ents + return ret diff --git a/jsuarez/CPUUtils.py b/jsuarez/CPUUtils.py new file mode 100644 index 00000000..7f2adc52 --- /dev/null +++ b/jsuarez/CPUUtils.py @@ -0,0 +1,77 @@ +#Utilities for Neural CPUs to interact with the set of actions. + +from pdb import set_trace as T +from sim.lib import Utils +from sim.modules import Skill +from sim.entity.NPC import NPC +from sim.action import Action + +class ActionStats: + numMoves = 4 + numAttacks = 2 + numEntities = 1 + len(Utils.terminalClasses(NPC)) + numSkills = len(Utils.terminalClasses(Skill.Skill)) + numActions = 2 + numSkills + +class ActionSpec: + def __init__(self): + self.prev = None + self.roots = [Action.ActionNode, Action.ActionLeaf] + #self.roots = [Actions.ActionLeaf] + + def edges(self): + ret = [] + blacklist = (Action.ActionLeaf, Action.Args) + for root in self.roots: + for e in root.__subclasses__(): + if e not in blacklist: + ret += [e] + return ret + + def leaves(self): + return Action.ActionLeaf.__subclasses__() + +class SaveManager(): + def __init__(self, root): + self.tl, self.ta, self.vl, self.va = [], [], [], [] + self.root = root + self.stateDict = None + self.lock = False + + def update(self, net, tl, ta, vl, va): + nan = np.isnan(sum([t.sum(e) for e in net.state_dict().values()])) + if nan or self.lock: + self.lock = True + print('NaN in update. Locking. Call refresh() to reset') + return + + if self.epoch() == 1 or vl < np.min(self.vl): + self.stateDict = net.state_dict().copy() + t.save(net.state_dict(), self.root+'weights') + + self.tl += [tl]; self.ta += [ta] + self.vl += [vl]; self.va += [va] + + np.save(self.root + 'tl.npy', self.tl) + np.save(self.root + 'ta.npy', self.ta) + np.save(self.root + 'vl.npy', self.vl) + np.save(self.root + 'va.npy', self.va) + + def load(self, net, raw=False): + stateDict = t.load(self.root+'weights') + self.stateDict = stateDict + if not raw: + net.load_state_dict(stateDict) + self.tl = np.load(self.root + 'tl.npy').tolist() + self.ta = np.load(self.root + 'ta.npy').tolist() + self.vl = np.load(self.root + 'vl.npy').tolist() + self.va = np.load(self.root + 'va.npy').tolist() + + def refresh(self, net): + self.lock = False + net.load_state_dict(self.stateDict) + + def epoch(self): + return len(self.tl)+1 + + diff --git a/jsuarez/Color256.py b/jsuarez/Color256.py new file mode 100644 index 00000000..0de95e5d --- /dev/null +++ b/jsuarez/Color256.py @@ -0,0 +1,16 @@ +def makeColor(idx, h=1, s=1, v=1): + r, g, b = colorsys.hsv_to_rgb(h, s, v) + rgbval = tuple(int(255*e) for e in [r, g, b]) + hexval = '%02x%02x%02x' % rgbval + return Color(str(idx), hexval) + +class Color256: + def make256(): + parh, parv = np.meshgrid(np.linspace(0.075, 1, 16), np.linspace(0.25, 1, 16)[::-1]) + parh, parv = parh.T.ravel(), parv.T.ravel() + idxs = np.arange(256) + params = zip(idxs, parh, parv) + colors = [makeColor(idx, h=h, s=1, v=v) for idx, h, v in params] + return colors + colors = make256() + diff --git a/jsuarez/ColorEnts.py b/jsuarez/ColorEnts.py new file mode 100644 index 00000000..75b2365f --- /dev/null +++ b/jsuarez/ColorEnts.py @@ -0,0 +1,34 @@ +from pdb import set_trace as T +import numpy as np +from scipy.misc import imread, imsave +from matplotlib import pyplot as plt +from forge.blade.lib.enums import makeColor + +fDir = 'resource/Entity/' +fName = 'neural' +extension = '.png' +neural = imread('resource/docs/' + fName + extension) +#neural = np.concatenate((neural, 255+np.zeros((16, 16, 1))), axis=2) + +R, C, three = neural.shape +for r in range(R): + for c in range(C): + if neural[r,c,0] == 214: + neural[r, c, -1] = 0 + +inds = [(5, 13), (5, 14), (6, 13), (6, 14), + (9, 9), (9, 10), (10, 9), (10, 10), + (13, 13), (13, 14), (14, 13), (14, 14)] + +parh, parv = np.meshgrid(np.linspace(0.075, 1, 16), np.linspace(0.25, 1, 16)[::-1]) +parh, parv = parh.T.ravel(), parv.T.ravel() + +idxs = np.arange(256) +params = zip(idxs, parh, parv) +colors = [makeColor(idx, h=h, s=1, v=v) for idx, h, v in params] +for color in colors: + name, val = color.name, color.value + for r, c in inds: + neural[r, c, :3] = val + f = fDir + fName + name + extension + imsave(f, neural.astype(np.uint8)) diff --git a/jsuarez/Curiosity.py b/jsuarez/Curiosity.py new file mode 100644 index 00000000..967a2c24 --- /dev/null +++ b/jsuarez/Curiosity.py @@ -0,0 +1,87 @@ + +class PhiNet(nn.Module): + def __init__(self, xdim, h): + super().__init__() + self.conv1 = Conv2d(8, int(h/2), 3, stride=2) + self.conv2 = Conv2d(int(h/2), h, 3, stride=2) + self.fc1 = torch.nn.Linear(5+4*4*h, h) + self.fc2 = torch.nn.Linear(h, h) + + def forward(self, conv, flat): + x = torch.nn.functional.relu(self.conv1(conv)) + x = torch.nn.functional.relu(self.conv2(x)) + x = x.view(-1) + + x = torch.cat((x, flat)) + x = torch.nn.functional.relu(self.fc1(x)) + x = self.fc2(x) + + return x.view(1, -1) + +class ForwardsNet(nn.Module): + def __init__(self, xdim, h, ydim): + super().__init__() + self.loss = torch.nn.MSELoss() + + self.fc1 = torch.nn.Linear(NATN+h, h) + self.fc2 = torch.nn.Linear(h, h) + + def forward(self, atn, phiPrev, phi): + atnHot = torch.zeros(NATN) + atnHot.scatter_(0, atn, 1) + atnHot = atnHot.view(1, -1) + + x = torch.cat((atnHot, phiPrev), 1) + x = torch.nn.functional.relu(self.fc1(x)) + x = self.fc2(x) + + #Stop grads on phi + loss = self.loss(x, phi) + return loss + +class BackwardsNet(nn.Module): + def __init__(self, h, ydim): + super().__init__() + self.loss = torch.nn.CrossEntropyLoss() + + self.fc1 = torch.nn.Linear(2*h, h) + self.fc2 = torch.nn.Linear(h, ydim) + + def forward(self, phiPrev, phi, atn): + x = torch.cat((phiPrev, phi), 1) + x = torch.nn.functional.relu(self.fc1(x)) + x = self.fc2(x) + + loss = self.loss(x, atn) + return loss + +class CurNet(nn.Module): + def __init__(self, xdim, h, ydim): + super().__init__() + self.phi = PhiNet(xdim, h) + self.forwardsDynamics = ForwardsNet(xdim, h, ydim) + self.backwardsDynamics = BackwardsNet(h, ydim) + + def forward(self, ents, entID, atn, conv, flat): + + conv = conv.view(1, *conv.size()) + conv = conv.permute(0, 3, 1, 2) + + if entID in ents: + atn, convPrev, flatPrev = ents[entID] + phiPrev = self.phi(convPrev, flatPrev) + phi = self.phi(conv, flat) + + #Stop both phi's on forward, train both on backward. Confirmed by Harri + fLoss = self.forwardsDynamics(atn, phiPrev.detach(), phi.detach()) + bLoss = self.backwardsDynamics(phiPrev, phi, atn) + + ri = fLoss.data[0] + + li = 0.20*fLoss + 0.80*bLoss + else: + ri, li = 0, torch.tensor(0.0) + #ri, li = 0, tu.var(np.zeros(1)) + + ents[entID] = (atn, conv, flat) + return ri, li diff --git a/jsuarez/Desciple.py b/jsuarez/Desciple.py new file mode 100644 index 00000000..0971191f --- /dev/null +++ b/jsuarez/Desciple.py @@ -0,0 +1,13 @@ +class Desciple: + def __init__(self, entID, config, color=None): + self.packet = pc.Packet(entID, config, color) + self.client = pc.Client(self.packet) + self.server = pc.Server(config) + self.sync(self.packet, self.server) + self.sync(self.packet, self.client) + + def sync(self, src, dst): + for attr in self.packet.paramNames(): + setattr(dst, attr, + getattr(src, attr)) + diff --git a/jsuarez/ES.py b/jsuarez/ES.py new file mode 100644 index 00000000..49d3afcc --- /dev/null +++ b/jsuarez/ES.py @@ -0,0 +1,137 @@ +from pdb import set_trace as T +import ray +import numpy as np +from sim.lib import Utils +from collections import defaultdict +from itertools import chain +from sim.lib.PriorityQueue import PriorityQueue +from scipy.stats import rankdata + +class ES: + def __init__(self, foo, hyperparams, test=False): + #self.nPop = 2048 + self.nPop = 512 + self.nRollouts = 1 + self.minRollouts = 1 + self.nDims = 25248 + 16 + 96 + 6 + self.nNoise = 10000 + self.sigma = hyperparams['sigma'] + self.alpha = hyperparams['alpha'] + self.topP = hyperparams['topP'] + self.tstep = hyperparams['step'] + #0.0020*51.2 + self.tick = 0 + self.test = test + self.desciples= {} + self.elite = defaultdict(list) + #self.topk = PriorityQueue(20) + self.priorities = [] + self.bench = Utils.BenchmarkTimer() + + #if not test: + self.meanVec = self.sigma*np.random.randn(self.nDims) + self.resetNoise() + self.data = (self.meanVec, self.noise) + + def collectRollout(self, iden, ent): + if self.test: + return + f, mut = (ent.timeAlive, ent.packet) + mut = mut[0] + self.elite[mut].append(f) + + def stepES(self): + if self.tick % self.tstep != 0: + return + meanOff, n = 0, 0 + noise = self.noise + + #Mean over rollouts + elite = [(np.min(v), k) for k, v in self.elite.items() if len(v) >= self.minRollouts] + if len(elite) == 0: + return + elite = sorted(elite, reverse=True) + self.elite = defaultdict(list) + Fs, mutations = list(zip(*elite)) + topP = int(self.topP * len(Fs)) + Fs, mutations = Fs[:topP], mutations[:topP] + + Fs = rankdata(Fs, method='dense') + Fs = np.asarray(Fs)[:, np.newaxis] + mutations = noise[mutations, :] + + if len(Fs) == 0: + return + #Weighted mean vec update + meanOff = np.mean(Fs*mutations, 0) + meanVec = self.meanVec + self.meanVec = meanVec + self.alpha * meanOff + + self.data = self.meanVec, self.noise + + def getParams(self): + return self.meanVec, self.priorities + + def setParams(self, meanVec, hyperparams): + self.meanVec = meanVec + self.data = (self.meanVec, self.shared[1]) + + def print(self): + print(sorted(self.priorities, reverse=True)[:20]) + + @property + def n(self): + return len(self.desciples) + + def step(self): + self.tick += 1 + self.cullDead() + #self.priorities = self.topk.priorities() + #self.priorities = self.priorities[:len(self.priorities)] + elite = [np.min(v) for v in self.elite.values() if len(v) >= self.minRollouts] + elite = sorted(elite, reverse=True) + priorities = int(self.topP * len(elite)) + self.priorities = elite[:priorities] + self.stepES() + pass + + @property + def shared(self): + ret = self.data + self.data = (None, None) + return ret + + def cullDead(self): + for k in list(self.desciples.keys()): + if not self.desciples[k].alive: + del self.desciples[k] + + def resetNoise(self): + self.noiseInd = 0 + self.noiseIncInd = 0 + if self.test: + self.noise = np.zeros((self.nNoise, self.nDims)) + else: + self.noise = 0.1*np.random.randn(self.nNoise, self.nDims) + + def save(self): + return self.meanVec + + def load(self, meanVec): + self.meanVec = meanVec + + #Returns a seed to use for spawning + def spawn(self): + iden = Utils.uniqueKey(self.desciples) + + self.noiseIncInd += 1 + if self.noiseIncInd == self.nRollouts: + self.noiseIncInd = 0 + self.noiseInd += 1 + + if self.noiseInd == self.nNoise: + self.noiseInd = 0 + self.noiseIncInd = 0 + + packet = self.noiseInd + return iden, [packet] diff --git a/jsuarez/Handler.py b/jsuarez/Handler.py new file mode 100644 index 00000000..a12f3e76 --- /dev/null +++ b/jsuarez/Handler.py @@ -0,0 +1,48 @@ +#Abstract class. Defines shared routines for managing and updating +#sets of entities within the world. Mainly subclassed by PC/NPC + +from pdb import set_trace as T + +class Handler(): + def __init__(self, env, entityType, maxSpawn=None): + self.entities = [] + self.initSpawn(env) + self.entityType = entityType + self.maxSpawn = maxSpawn + + def addEntity(self, entity, ent): + self.entities += [entity] + r, c = entity.pos + ent[r, c].append(entity) + + def step(self, world): + for i in range(len(self.entities) - 1, -1, -1): + self.stepAction(world, self.entities[i], i) + self.stepEnv(world.env, self.entities[i]) + + def stepEnv(self, env, entity): + pass + + #override + def stepAction(self, world, entity, ind): + pass + + def removeIfDead(self, world, ind): + if not self.entities[ind].isAlive(): + self.entities[ind].cpu.death() + r, c = self.entities[ind].pos + world.env.ent[r, c].remove(self.entities[ind]) + del self.entities[ind] + + def initSpawn(self, env): + pass + + def spawnPos(self): + pass + + def spawn(self, ent): + if self.maxSpawn is None or len(self.entities) < self.maxSpawn: + pos = self.spawnPos() + entity = self.entityType(pos) + self.addEntity(entity, ent) + diff --git a/jsuarez/MPIUtils.py b/jsuarez/MPIUtils.py new file mode 100644 index 00000000..4a16610e --- /dev/null +++ b/jsuarez/MPIUtils.py @@ -0,0 +1,81 @@ +#Author: Joseph Suarez + +from pdb import set_trace as T +import sys, builtins +import numpy as np + +from mpi4py import MPI +from mpi4py.MPI import COMM_WORLD as comm + +MASTER = 0 +SILENT = 1 +ALL = 2 + +class LoadBalancer: + def __init__(self, cores): + self.nCores = len(cores) + self.cores = cores + self.loads = dict((core, 0) for core in cores) + + def assignWorker(self): + #core = 1 + #self.loads[core] += 1 + #return np.random.choice(self.cores) + #return min([len(e) for e in load.values()]) + core = min(self.loads, key=self.loads.get) + self.loads[core] += 1 + return core + + def deleteWorker(self, core): + self.loads[core] -= 1 + +def print(verbose, *args): + if verbose == ALL or (verbose == MASTER and isMaster()): + builtins.print(*args) + sys.stdout.flush() + +def send(data, dst, seq=None, usePar=False): + if not usePar: + seq.inbox = data + return + comm.send(data, dst) + +def recv(src, seq=None, usePar=False): + if not usePar: + return seq.inbox + return comm.recv(source=src) + +#Returns a req +def isend(data, dst, tag): + return comm.isend(data, dest=dst, tag=tag) + +#Returns a req +def irecv(src, tag): + return comm.irecv(source=src, tag=tag) + +def gather(dst): + return comm.gather(root=dst) + +def assignWorker(clients): + return np.random.choice(clients) + +def distributeFunc(f): + if isMaster(): + x = f() + else: + x = None + return distribute(x) + +def npValMean(val): + meanVal = np.zeros_like(val) + comm.Allreduce(val, meanVal, op=MPI.SUM) + return meanVal / comm.Get_size() + +def distribute(x): + return comm.bcast(x, root=MASTER) + +def isMaster(): + return comm.Get_rank() == MASTER + +def core(): + return comm.Get_rank() diff --git a/jsuarez/MapMaker.py b/jsuarez/MapMaker.py new file mode 100644 index 00000000..1af9307b --- /dev/null +++ b/jsuarez/MapMaker.py @@ -0,0 +1,174 @@ +from pdb import set_trace as T + +from scipy.misc import imread +from scipy.misc import imsave +from sim.lib import Enums +from sim.lib.Enums import Material +import sys +import numpy as np +import pygame + +def readRGB(path): + return imread(path)[:, :, :3] + +def pgRead(path, alpha=False, rot=90): + try: + img = readRGB(path) + except FileNotFoundError: + return None + img = pygame.pixelcopy.make_surface(img) + if alpha: + img.set_colorkey((255, 255, 255)) + return pygame.transform.rotate(img, rot) + +class TextureInitializer(): + def __init__(self, sz, root='resource/'): + self.width = sz + self.statHeight = 2 + + self.material = readRGB(root+'Material/textures.png') + self.material = self.textureTiles() + self.entity = self.textureFromFile(Enums.Entity, root+'Entity/') + + def textureTiles(self): + reverse = {} + for mat in Material: + mat = mat.value + texCoords = mat.tex + tex = self.getTile(*texCoords) + tex = pygame.pixelcopy.make_surface(tex) + mat.tex = pygame.transform.rotate(tex, 90) + reverse[mat.index] = mat.tex + return reverse + + def getTile(self, r, c): + w = self.width + tile = self.material[r*w:r*w+w, c*w:c*w+w, :] + return tile + + def textureFromFile(self, enum, path, alpha=True, rot=270): + reverse = {} + for e in enum: + texPath = path + e.name.lower() + '.png' + tex = pgRead(texPath, alpha=alpha, rot=rot) + e.value.tex = tex + if type(e.value.data) == tuple: + reverse[e.value.data[0]] = tex + else: + reverse[e.value.data] = tex + + return reverse + +class MapMaker: + def __init__(self, w, h, res=16): + self.W, self.H, self.res = w, h, res + self.env = np.zeros((h, w), dtype=np.uint8) + self.textures = TextureInitializer(self.res) + + self.setupScreen(self.W, self.H) + + self.zoom = 1 + self.maxZoom = int(np.max(list(self.textures.material.keys()))) + self.zoomDelta = 1 + self.deltaX, self.deltaY = 0, 0 + self.volDeltaX, self.volDeltaY = 0, 0 + self.clicked = False + self.rightClicked = False + + + def getTile(self, rPx, cPx): + return rPx//self.res, cPx//self.res + + def renderTile(self, r, c, tex): + w = self.res + self.buffer.blit(tex, (c*w, r*w)) + + def render(self): + self.screen.fill((0, 0, 0)) + + #Draw + if self.rightClicked: + x, y = pygame.mouse.get_pos() + x = x - self.deltaX + y = y - self.deltaY + c, r = self.getTile(x, y) + tex = self.textures.material[self.zoom] + self.env[r, c] = np.uint8(self.zoom) + self.renderTile(r, c, tex) + + #Scale + scaleX, scaleY = int(self.H), int(self.W) + buff = pygame.transform.scale(self.buffer, (scaleX, scaleY)) + + #Translate + deltaX = self.deltaX + self.volDeltaX - scaleX//2 + self.W//2 + deltaY = self.deltaY + self.volDeltaY - scaleY//2 + self.H//2 + + #Render + self.screen.blit(buff, (deltaX, deltaY)) + pygame.display.flip() + + def setupScreen(self, envR, envC): + self.W = envC * self.res + self.H = envR * self.res + + pygame.init() + self.screen = pygame.display.set_mode((self.W, self.H)) + self.buffer = pygame.surface.Surface((self.W, self.H)) + + def update(self): + self.processEvents(pygame.event.get()) + self.updateMouse() + imsave('resource/map/smallmap.png', self.env) + self.render() + + def updateMouse(self): + if self.clicked: + volX, volY = self.volX, self.volY + curX, curY = pygame.mouse.get_pos() + self.volDeltaX = curX - volX + self.volDeltaY = curY - volY + + def quit(self): + pygame.quit() + sys.exit() + + def mouseDown(self, button): + if button == 1 and not self.clicked: + self.volX, self.volY = pygame.mouse.get_pos() + self.clicked = True + if button == 3: + self.rightClicked = True + + def mouseUp(self, button): + if button == 1: + if self.clicked: + self.deltaX += self.volDeltaX + self.deltaY += self.volDeltaY + self.volDeltaX, self.volDeltaY = 0, 0 + self.clicked = False + elif button == 3: + self.rightClicked = False + elif button == 4: + if self.zoom < self.maxZoom: + self.zoom += self.zoomDelta + elif button == 5: + if self.zoom > 0: + self.zoom -= self.zoomDelta + + def processEvents(self, events): + for e in events: + if e.type == pygame.QUIT: + self.quit() + elif e.type == pygame.MOUSEBUTTONDOWN: + self.mouseDown(e.button) + elif e.type == pygame.MOUSEBUTTONUP: + self.mouseUp(e.button) + + + +if __name__ == '__main__': + w, h = int(sys.argv[1]), int(sys.argv[2]) + mapMaker = MapMaker(w, h) + while True: + mapMaker.update() diff --git a/jsuarez/NPCHandler.py b/jsuarez/NPCHandler.py new file mode 100644 index 00000000..69f82193 --- /dev/null +++ b/jsuarez/NPCHandler.py @@ -0,0 +1,30 @@ +#Defines various handlers for correctly spawning/updating NPCs + +from sim.handler.Handler import Handler +import numpy as np +from pdb import set_trace as T + +class MaterialHandler(Handler): + def __init__(self, env, entityType, material, maxSpawn=None): + self.material= material + super().__init__(env, entityType, maxSpawn=maxSpawn) + + def initSpawn(self, env): + R, C = env.shape + self.spawns = [] + for r in range(R): + for c in range(C): + if type(env[r, c].mat) == self.material.value: + self.spawns += [(r, c)] + + def spawnPos(self): + ind = np.random.randint(0, len(self.spawns)) + return self.spawns[ind] + + def stepAction(self, world, entity, ind): + ret = self.entities[ind].act(world) + + if not self.entities[ind].isAlive(): + self.entities[ind].yieldDrops() + self.removeIfDead(world, ind) + return ret diff --git a/jsuarez/PC.py b/jsuarez/PC.py new file mode 100644 index 00000000..f31031d9 --- /dev/null +++ b/jsuarez/PC.py @@ -0,0 +1,118 @@ +from pdb import set_trace as T +import numpy as np + +from sim.lib import MultiSet +from sim.modules import Inventory +from sim.entity.Entity import Entity +from sim.item import Food + +class PC(Entity): + def __init__(self, cpu, pos): + super().__init__(pos) + self.cpu = cpu + self.inv = MultiSet.MultiSet(capacity=1024) + + self.hitRange = 4 + self.timeAlive = 0 + self.maxHealth = 10 + self.maxFood = 16 + self.maxWater = 16 + self.health = self.maxHealth + self.food = self.maxFood + self.water = self.maxWater + self.decProb = 0.2 + self.index = 1 + self.lastAction = None + + self.equipment = Inventory.Inventory() + self.inbox = [] + + def act(self, world, actions): + if (self.food > self.maxFood//2 and + self.water > self.maxWater//2 and + self.health < self.maxHealth): + self.health += 1 + self.decrementFood() + self.decrementWater() + + stim = world.stim(self.pos) + action, args = self.decide(stim, actions) + action(world, self, *(args)) + self.lastAction = action + + return self.post() + + #PCs interact with the world only through stimuli + #to prevent cheating + def decide(self, stimuli, actions): + action, args = self.cpu.interact(self, stimuli, actions) + return action, args + + def post(self): + self.inbox = [] + self.timeAlive += 1 + #if self.timeAlive % 10 == 0: + # return self.cpu.reproduce() + + def receiveDrops(self, drops): + for e in drops: + item, num = e + self.inv.add(item, num) + + def receiveMessage(self, sender, data): + self.inbox += [(sender, data)] + + def eat(self, food): + self.health = min(self.health+food.heal, self.maxHealth) + self.inv.remove(food, 1) + + def equipArmor(self, item): + if not self.equipment.armor.isBase: + self.inv.add(self.equipment.armor) + self.equipment.armor = item + + def equipMelee(self, item): + if not self.equipment.melee.isBase: + self.inv.add(self.equipment.melee) + self.equipment.melee = item + + def equipRanged(self, item): + if not self.equipment.ranged.isBase: + self.inv.add(self.equipment.ranged) + self.equipment.ranged = item + + def unequipArmor(self): + if self.equipment.armor.isBase: + return + self.inv.add(self.equipment.armor) + self.equipment.resetArmor() + + def unequipMelee(self): + if self.equipment.melee.isBase: + return + self.inv.add(self.equipment.melee) + self.equipment.resetMelee() + + def unequipRanged(self): + if self.equipment.ranged.isBase: + return + self.inv.add(self.equipment.ranged) + self.equipment.resetRanged() + + def decrementFood(self): + if np.random.rand() < self.decProb: + if self.food > 0: + self.food -= 1 + else: + self.health -= 1 + + def decrementWater(self): + if np.random.rand() < self.decProb: + if self.water > 0: + self.water -= 1 + else: + self.health -= 1 + + @property + def isPC(self): + return True diff --git a/jsuarez/PCHandler.py b/jsuarez/PCHandler.py new file mode 100644 index 00000000..931f62fd --- /dev/null +++ b/jsuarez/PCHandler.py @@ -0,0 +1,59 @@ +#Handler for PCs. Unlike NPCs, the NPC handler must manage environment +#updates, including food/water levels. Also handles reproduction. + +from pdb import set_trace as T +from collections import defaultdict +from sim.handler.Handler import Handler +from sim.lib import Enums, AI, Actions +from sim.entity.PC import PC +from sim.modules import Skill, ItemHook +from sim.lib.Enums import Material + +class PCHandler(Handler): + def __init__(self, env, cpu): + super().__init__(env, cpu.ann) + self.cpu = cpu + + #Const for now. Better later + def initSpawn(self, env): + self.spawnCent = 13 + + def spawnPos(self): + return self.spawnCent, self.spawnCent + + def stepEnv(self, env, entity): + r, c = entity.pos + tile = env.tiles[r, c] + ''' + if tile.mat.harvestable: + for drop in env.harvest(r, c): + entity.inv.add(*drop) + ''' + #if (type(env.tiles[r, c].mat) in [Material.FOREST.value]): + if (entity.food < entity.maxFood and type(env.tiles[r, c].mat) in [Material.FOREST.value]): + if env.harvest(r, c): + entity.food += 1 + if (entity.water < entity.maxWater and + Material.WATER.value in + AI.adjacentMats(env, entity.pos)): + entity.water += 1 + + def stepAction(self, world, entity, ind): + actions = Actions.ActionTree(world, entity) + ret = self.entities[ind].act(world, actions) + + self.removeIfDead(world, ind) + ''' + if ret not in (-1, None): + pos = AI.randAdjacent(*self.entities[ind].pos) + entity = PC(ret, pos) + self.addEntity(entity, world.ent) + ''' + + def spawn(self, ent): + pos = self.spawnPos() + entity = self.cpu.spawn() + entityWrapper = PC(entity, self.spawnPos()) + self.addEntity(entityWrapper, ent) + + diff --git a/jsuarez/SkillActions.py b/jsuarez/SkillActions.py new file mode 100644 index 00000000..2d5feb7e --- /dev/null +++ b/jsuarez/SkillActions.py @@ -0,0 +1,146 @@ +#Defines actions which entities may use to interact with the world. +#Does not validate whether an action is allowed; this should be +#handled prior to the function call. These are mainly passed as +#function handles within AI logic before being called during act(). + +from pdb import set_trace as T +import numpy as np +from sim.lib import Combat, Enums, AI, Utils +from sim.lib.Enums import Material +from sim.modules import Skill as skill +from sim.action import ActionTree +from sim.action.ActionTree import ActionNode, ActionLeaf +from copy import deepcopy + +class Pass(ActionLeaf): + def edges(self, stim, entity): + return ActionTree.EmptyArgs() + +class Reproduce(ActionLeaf): + def __call__(self, world, entity): + #if entity.food < entity.maxFood//2 or entity.water < entity.maxWater//2: + # return + #posList = AI.adjacentEmptyPos(world.env, entity.pos) + #if len(posList) == 0: + # return + + #posInd = np.random.choice(np.arange(len(posList))) + #pos = posList[posInd] + ''' + offspring = ServerPC( + pos=entity.pos, + health = entity.health, + water = entity.water) + entity.health = entity.health//2.0 + offspring.health = entity.health + entity.water = entity.water//2.0 + offspring.water = entity.water + return offspring + ''' + pass + + def edges(self, stim, entity): + return Args() + +class Attack(ActionNode): + def edges(self, world, entity): + return [MeleeV2, Ranged] + +class Ranged(ActionLeaf): + def __call__(self, world, entity, targ): + damage = Combat.attack(entity, targ, entity.skills.ranged) + return damage + + def edges(self, world, entity): + args = SetArgs() + for e in AI.l1Range(world.env.ent, world.size, + entity.pos, entity.hitRange): + args.add(e) + return args + +class Skill(ActionNode): + def edges(self, world, entity): + return [Harvest, Process] + +class Harvest(ActionNode): + def edges(self, world, entity): + return [Fish, Mine] + +class Fish(ActionLeaf): + def __call__(self, world, entity): + entity.skills.fishing.harvest(entity.inv) + + def edges(self, world, entity): + for mat in AI.adjacentMat(world.env.tiles, entity.pos): + if mat == Material.WATER.value: + return EmptyArgs() + +class Mine(ActionLeaf): + def __call__(self, world, entity, tile): + world.env.harvest(tile.r, tile.c) + entity.skills.mining.harvest(entity.inv) + + def edges(self, world, entity): + r, c = entity.pos + args = SetArgs() + for tile in AI.adjacencyTiles(world.env.tiles, r, c): + if type(tile.mat) == Material.OREROCK.value: + args.add(tile) + return args + ''' + for mat in AI.adjacentMat(world.env.tiles, entity.pos): + if mat == Material.OREROCK.value: + return EmptyArgs() + ''' + +class Process(ActionNode): + def edges(self, world, entity): + return [Cook, Smith] + +class Cook(ActionLeaf): + def __call__(self, world, entity, item): + entity.skills.cooking.process(entity.inv) + + def edges(self, world, entity): + args = DiscreteArgs() + for itm in entity.inv: + if itm.useSkill == skill.Cooking: + args.add(Args([itm])) + return args + +class Smith(ActionLeaf): + def __call__(self, world, entity, item): + entity.skills.smithing.process(entity.inv) + + def edges(self, world, entity): + args = DiscreteArgs() + for itm in entity.inv: + if itm.useSkill == skill.Smithing: + args.add(enums.args([itm])) + return args + +''' +def buy(entity, world, item, quant, itemPrice): + world.market.buy(item, quant, itemPrice) + +def sell(entity, world, item, quant, itemPrice): + world.market.sell(item, quant, itemPrice) + +def cancelOffer(entity, world, offer): + offer.cancel() + +def message(entity, world, other, data): + other.receiveMessage(entity, data) + +#There are a huge number of possible exchanges. +#For effeciency, these are not validated a priori +#and will return False if invalid +def allowableExchanges(self, world, entity): + ret = defaultdict(set) + for itm in ItemHook.ItemList.items: + for atn in (Actions.buy, Actions.sell): + ret[atn].add(Enums.Args([itm, IntegerArg(), IntegerArg()])) + ret[atn].add(Enums.Args([itm, IntegerArg(), IntegerArg()])) + return ret +''' + diff --git a/jsuarez/extra/embyr_deprecated/embyr/EnvViewport3D.py b/jsuarez/extra/embyr_deprecated/embyr/EnvViewport3D.py new file mode 100644 index 00000000..1ebe4569 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/EnvViewport3D.py @@ -0,0 +1,97 @@ +from pdb import set_trace as T +import numpy as np +import time +from scipy.misc import imresize, imsave +from enum import Enum + +import pygame +from pygame import Surface + +from forge.embyr import embyr +from forge.embyr import utils as renderutils +from forge.embyr import render +from forge.embyr.texture import TextureInitializer +from forge.blade.lib.enums import Neon, Color256, Defaults +from forge.blade.action.v2 import Attack +from forge.blade.action import action + +from pdb import set_trace as T +import numpy as np + +import os +import kivy3 +from kivy.app import App +from kivy3 import Scene, Renderer, PerspectiveCamera +from kivy3.loaders import OBJMTLLoader +from kivy.uix.floatlayout import FloatLayout +from kivy.config import Config +from kivy.graphics import opengl as gl +from kivy.graphics import Mesh as KivyMesh +from kivy3.core.object3d import Object3D +from kivy3.materials import Material +from kivy.core.image import Image +from copy import deepcopy +from forge.embyr.embyr import Application as KivyApp +import pywavefront as pywave +import pytmx +from forge.blade.lib import enums +from forge.embyr.transform import Transform +from forge.embyr import embyr3D + +root = 'forge/embyr/' +Config.set('input', 'mouse', 'mouse,multitouch_on_demand') +shaderf = 'tex/default.glsl' +pi = 3.14159265 +NANIM = 4 + +class EnvViewport3D(embyr3D.Widget): + def __init__(self, root, realm, **kwargs): + super().__init__(root) + self.root = 'forge/embyr/' + self.rs = embyr3D.RS(root, **kwargs) + self.setupScene() + + def setupScene(self): + self.map = embyr3D.Map(self.rs) + + obj = embyr3D.OBJLoader.load(self.root + 'tex/nn.obj') + + ent = embyr3D.Ent() + ent.pos.x = 40 + ent.pos.y = 10 + ent.pos.z = 40 + self.vecent = ent + self.rs.add(ent) + + ent = embyr3D.Ent() + ent.pos.x = 8 + ent.pos.y = 20 + ent.pos.z = 8 + self.cament = ent + self.rs.add(ent) + + def render(self, dt): + #self.client.render(dt) + #self.step() + x, y, z = self.rs.update(dt) + self.vecent.update_pos(x, 3, z) + + ''' + desciples = sorted(self.realm.desciples.items()) + if len(desciples) == 0: + return + ent = desciples[0][1] + z, x = 32, 32 #ent.server.pos + self.ent.update_pos(x, self.ent.pos.y, z) + ''' + + def refresh(self, trans, iso): + self.iso = iso + mmap = self.map.refresh(trans, self.iso) + ent = self.ent.refresh(trans, self.iso) + + self.blit(mmap, (0, 0)) + self.blit(ent, (0, 0)) + + self.flip() + return self.surf diff --git a/jsuarez/extra/embyr_deprecated/embyr/__init__.py b/jsuarez/extra/embyr_deprecated/embyr/__init__.py new file mode 100644 index 00000000..4da4a0b7 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/__init__.py @@ -0,0 +1 @@ +from .application import Application diff --git a/jsuarez/extra/embyr_deprecated/embyr/application.py b/jsuarez/extra/embyr_deprecated/embyr/application.py new file mode 100644 index 00000000..d9999ac2 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/application.py @@ -0,0 +1,80 @@ +from pdb import set_trace as T +import numpy as np + +import os +import kivy3 +from kivy.app import App +from kivy3 import Scene, Renderer, PerspectiveCamera +from kivy3.loaders import OBJMTLLoader +from kivy.uix.floatlayout import FloatLayout +from kivy.uix.boxlayout import BoxLayout +from kivy.uix.widget import Widget +from kivy.config import Config +from kivy.graphics import opengl as gl +from kivy.graphics import Mesh as KivyMesh +from kivy3.core.object3d import Object3D +from kivy3.materials import Material +from kivy.core.image import Image +from copy import deepcopy +from forge.embyr.embyr import Application as KivyApp +import pywavefront as pywave +import pytmx +from forge.blade.lib import enums +from forge.embyr.transform import Transform +from forge.embyr.client import Client, Canvas +from forge.embyr.EnvViewport3D import EnvViewport3D + +root = 'forge/embyr/' +Config.set('input', 'mouse', 'mouse,multitouch_on_demand') +shaderf = 'tex/default.glsl' +pi = 3.14159265 +NANIM = 4 + +class Application(KivyApp): + def __init__(self, size, realm, step, conf): + super().__init__(size) + self.W, self.H = size + self.appSize = size + self.root = 'forge/embyr/' + self.title = 'Projekt: Godsword' + self.blocks = dict((mat.value.tex, mat.value) + for mat in enums.Material) + + self.realm = realm + self.step = step + self.conf = conf + + def build(self): + root = FloatLayout() + W, H, side = self.W, self.H, 256 + dims = (self.W-side, self.H-side, side) + #self.env1 = EnvViewport3D(root, self.realm, (W, W)) + #self.env2 = EnvViewport3D(root, self.realm, (W, W)) + canvas = Canvas(self.appSize, root, self.realm, dims, self.conf) + #self.client = Client(canvas, self.appSize, self.realm, + # self.step, dims, NANIM) + self.canvas = canvas + + root.add_widget(canvas) + + #root.add_widget(self.env1) + #root.add_widget(self.env2) + + self.loop(self.update) + return root + + def update(self, dt): + self.step() + #self.env1.render() + #self.env2.render() + self.canvas.render(dt) + + ''' + desciples = sorted(self.realm.desciples.items()) + if len(desciples) == 0: + return + ent = desciples[0][1] + z, x = 32, 32 #ent.server.pos + self.ent.update_pos(x, self.ent.pos.y, z) + ''' + diff --git a/jsuarez/extra/embyr_deprecated/embyr/block.mtl b/jsuarez/extra/embyr_deprecated/embyr/block.mtl new file mode 100644 index 00000000..f706e090 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/block.mtl @@ -0,0 +1,13 @@ +# Blender MTL File: 'block.blend' +# Material Count: 1 + +newmtl Material +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.640000 0.640000 0.640000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 +map_Kd bg.png diff --git a/jsuarez/extra/embyr_deprecated/embyr/block.obj b/jsuarez/extra/embyr_deprecated/embyr/block.obj new file mode 100644 index 00000000..d177ad21 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/block.obj @@ -0,0 +1,47 @@ +# Blender v2.79 (sub 0) OBJ File: 'block.blend' +# www.blender.org +mtllib block.mtl +o Cube +v 1.000000 0.000000 -1.000000 +v 1.000000 0.000000 0.000000 +v -0.000000 0.000000 -0.000000 +v 0.000000 0.000000 -1.000000 +v 1.000000 1.000000 -1.000000 +v 1.000000 1.000000 0.000000 +v -0.000000 1.000000 -0.000000 +v 0.000000 1.000000 -1.000000 +vt 0.000000 0.333333 +vt 0.333333 0.333333 +vt 0.333333 0.666667 +vt 0.000000 0.666667 +vt 0.666667 0.000000 +vt 0.666667 0.333333 +vt 0.333334 0.333333 +vt 0.333333 0.000000 +vt 0.333333 1.000000 +vt 0.000000 1.000000 +vt 0.000000 0.666667 +vt 0.333333 0.666667 +vt 0.333333 0.333334 +vt 0.666667 0.333333 +vt 0.666667 0.666667 +vt 0.333333 0.333333 +vt 0.000000 0.333333 +vt 0.000000 0.000000 +vt 0.333333 0.000000 +vt 1.000000 0.000000 +vt 1.000000 0.333333 +vn 0.0000 -1.0000 0.0000 +vn 0.0000 1.0000 0.0000 +vn 1.0000 0.0000 0.0000 +vn -0.0000 -0.0000 1.0000 +vn -1.0000 -0.0000 -0.0000 +vn 0.0000 0.0000 -1.0000 +usemtl Material +s off +f 1/1/1 2/2/1 3/3/1 4/4/1 +f 5/5/2 8/6/2 7/7/2 6/8/2 +f 1/9/3 5/10/3 6/11/3 2/12/3 +f 2/12/4 6/13/4 7/14/4 3/15/4 +f 3/16/5 7/17/5 8/18/5 4/19/5 +f 5/5/6 1/20/6 4/21/6 8/6/6 diff --git a/jsuarez/extra/embyr_deprecated/embyr/blocks.png b/jsuarez/extra/embyr_deprecated/embyr/blocks.png new file mode 100644 index 00000000..5be1985f Binary files /dev/null and b/jsuarez/extra/embyr_deprecated/embyr/blocks.png differ diff --git a/jsuarez/extra/embyr_deprecated/embyr/client.py b/jsuarez/extra/embyr_deprecated/embyr/client.py new file mode 100644 index 00000000..79201225 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/client.py @@ -0,0 +1,121 @@ +from pdb import set_trace as T +import pygame, time +import numpy as np + +from forge.embyr import embyr +from forge.embyr.modules import * + +class Client(embyr.Container): + def __init__(self, view, size, realm, step, dims, nAnim, **kwargs): + super().__init__(size, **kwargs) + self.W, self.H, self.side = dims + self.realm, self.step = realm, step + self.view, self.nAnim = view, nAnim + + offset = 16 * 8 + self.x, self.y, self.xVol, self.yVol = -offset, offset, 0, 0 + self.zoom, self.zoomVol = 1, 0 + + self.count = 0 + self.frame = 0 + self.frames = [] + self.vals = [] + self.init = time.time() + + def setup(self): + surf = self.view.renderTitle() + self.blit(surf, (0, 0)) + self.frame += 1 + self.flip() + + def render(self, t): + self.update() + return + if self.frame == 0: + return self.setup() + if self.frame == 1: + time.sleep(2.5) + self.update() + + def update(self): + self.view.render(self.realm, None, None) + return + self.writeFrame() + self.trans = self.renderOffsets(self.H, self.H) + keyframe = self.count == 0 + if keyframe: + self.step() + + self.surf = self.view.render(self.realm, self.trans, keyframe) + + self.count = (self.count + 1) % (self.nAnim+1) + self.blit(self.surf, (0,0)) + self.flip() + self.frame += 1 + + def writeFrame(self): + NFRAMES=1800 + return + if self.frame < NFRAMES: + print('Frame: ', len(self.frames)) + frame = pygame.surfarray.array3d(pygame.transform.rotate(self.surf, 90)) + frame = np.fliplr(frame) + #frame = frame[:1024, 256:256+1024] + frame = frame[:1024, 1024+256:1024+256+1024] + self.frames.append(frame) + #pygame.image.save(self.screen, 'resource/data/lrframe'+str(self.frame)+'.png') + elif self.frame == NFRAMES: + import imageio + print('Saving MP4...') + imageio.mimwrite('swordfrag.mp4', self.frames, fps = 30) + print('Saved') + + def clipZoom(self, zoom): + return np.clip(zoom, 1.0, 8.0) + + def renderOffsets(self, W, H): + #Scale + zoom = self.clipZoom(self.zoom + self.zoomVol) + scaleX, scaleY = int(W*zoom), int(H*zoom) + + #Translate + deltaX = self.x + self.xVol - scaleX/2 + W/2 + deltaY = -self.y - self.yVol - scaleY/2 + H/2 + return scaleX, scaleY, deltaX, deltaY + + def on_touch_down(self, touch): + self.xStart, self.yStart = touch.pos + + def on_touch_up(self, touch): + if touch.button == 'left': + self.xVol, self.yVol= 0, 0 + xEnd, yEnd = touch.pos + self.x += xEnd - self.xStart + self.y += yEnd - self.yStart + elif touch.button == 'right': + self.zoom = self.clipZoom(self.zoom + self.zoomVol) + self.zoomVol = 0 + + def on_touch_move(self, touch): + if touch.button == 'left': + xEnd, yEnd = touch.pos + self.xVol = xEnd - self.xStart + self.yVol = yEnd - self.yStart + elif touch.button == 'right': + xEnd, yEnd = touch.pos + delta = (xEnd - self.xStart)/2 - (yEnd - self.yStart)/2 + self.zoomVol = delta/100 + + def on_key_down(self, *args): + text = args[3] + if text == 'i': + #Toggle isometric + trans = self.renderOffsets(self.H, self.H) + self.view.toggleEnv(trans) + elif text == 'p': + T() + elif text == '[': + self.view.leftScreenshot() + else: + #Toggle overlay + self.view.key(text) diff --git a/jsuarez/extra/embyr_deprecated/embyr/embyr.py b/jsuarez/extra/embyr_deprecated/embyr/embyr.py new file mode 100644 index 00000000..976c44ae --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/embyr.py @@ -0,0 +1,357 @@ +#Author: Joseph Suarez + +from pdb import set_trace as T +from scipy.misc import imread +import numpy as np +import time + +import pygame +from pygame.surface import Surface + +import kivy as kv +from kivy.app import App +from kivy.uix.widget import Widget +from kivy.graphics.texture import Texture as kvTex +from kivy.graphics import Rectangle +from kivy.config import Config +from kivy.clock import Clock +from kivy.core.window import Window + +Config.set('input', 'mouse', 'mouse,multitouch_on_demand') + +#Wrapper for all Embyr programs +#Mandatory, required by kivy +class Application(kv.app.App): + def __init__(self, size): + super().__init__() + pygame.init() + self.size = size + self.W, self.H = size + Window.size = (self.W//2, self.H//2) + self.scale = 1.5 + + #Run a thunk forever + def loop(self, func, interval=0): + Clock.schedule_interval(func, interval) + + #Run a thunk once + def once(self, func, delay=0): + Clock.schedule_once(func, delay) + +#Wrapper for all draw canvas objects. +#Provides a seamless interface between +#pygame surfaces and kivy graphics using +#a fast flip() buffer transfer +#Expected top performance on a laptop: +#~60fps at 1080p or ~24fps at 4k +#key: alpha mask color +#border: size of frame around canvas +class Container(kv.uix.widget.Widget): + def __init__(self, size, pos=(0, 0), key=None, border=3, **kwargs): + super().__init__(**kwargs) + self.W, self.H = size + self.size, self.border = size, border + self.surf = pygame.Surface((self.W, self.H)) + #self.surf = pygame.Surface((int(1.5*self.W), int(1.5*self.H))) + self.texture = kvTex.create(size=size, colorfmt="rgb") + + self.left, self.right = self.border, self.W-self.border + self.top, self.bottom = self.border, self.H-self.border + self.scale = 1.5 + + if key is not None: + self.surf.set_colorkey(key) + with self.canvas: + self.screen = Rectangle(pos=pos, size=size) + if hasattr(self, 'on_key_down'): + Window.bind(on_key_down=self.on_key_down) + + #Clear the canvas + def reset(self): + self.fill(Neon.BLACK.rgb) + if self.border > 0: + self.renderBorder() + + #Only use in the top level canvas + #Transfers a pygame data buffer to a kivyv texture + def flip(self): + W, H = self.surf.get_width(), self.surf.get_height() + #surf = pygame.transform.scale(self.surf, (int(W*self.scale), int(H*self.scale))) + data = pygame.image.tostring(self.surf, 'RGB', True) + self.texture.blit_buffer(data, bufferfmt="ubyte", colorfmt="rgb") + self.screen.texture = self.texture + + #Render a frame around the canvas + def renderBorder(self): + if self.border == 0: + return + for coords in [ + (0, 0, self.W, self.top), + (0, 0, self.left, self.H), + (0, self.bottom, self.W, self.H), + (self.right, 0, self.W, self.H) + ]: + pygame.draw.rect(self.surf, Neon.RED.rgb, coords) + + ### Blitting and drawing wrappers ### + + def blit(self, container, pos, area=None, flags=0): + pos = (pos[0]+self.border, pos[1]+self.border) + self.surf.blit(container, pos, area=area, special_flags=flags) + self.renderBorder() + + def fill(self, color): + self.surf.fill(color) + + def rect(self, color, coords, lw=0): + pygame.draw.rect(self.surf, color, coords, lw) + + def line(self, color, start, end, lw=1): + pygame.draw.line(self.surf, color, start, end, lw) + +class ECanvas(Container): + def __init__(self, args, **kwargs): + size = kwargs.pop('size') + super().__init__(size, **kwargs) + + canvas, realm, textures, fonts = args + self.textures = textures + self.fonts = fonts + self.realm = realm + self.canvas = canvas + +class Panel(ECanvas): + def __init__(self, args, **kwargs): + super().__init__(args, **kwargs) + self.canvas.add(self.screen) + +#Return the visible portion of a tiled environment +def mapCrop(screen, env, txSz, trans): + offs, txSz = offsets(screen, *env.shape[:2], txSz, trans) + minH, maxH, minW, maxW = offs + return env[minH:maxH, minW:maxW], txSz + +#Array index offsets based on a transformation +def offsets(screen, R, C, txSz, trans=None): + W, H = screen.W, screen.H + sx, sy, tx, ty = W, H, 0, 0 + if trans is not None: + sx, sy, tx, ty = trans + ratio = sx / W #assume uniform transform + txSz = txSz * ratio + + nW = int(W / txSz) + nH = int(H / txSz) + minW = -int(min(tx / txSz, 0)) + minH = -int(min(ty / txSz, 0)) + + maxW = min(minW + nW, C) + maxH = min(minH + nH, H) + return (minH, maxH, minW, maxW), txSz + +#Make a set of mipmaps for a dict of textures +#with sizes specivied by mipLevels +def mips(texs, mipLevels): + rets = {} + for sz in mipLevels: + if sz not in rets: + rets[sz] = {} + for k, tx in texs.items(): + rets[sz][k] = pygame.transform.scale(tx, (sz, sz)) + return rets + +#Mipmaps for a single texture +def mip(tex, mipLevels): + return dict((sz, pygame.transform.scale(tex, (sz, sz))) for sz in mipLevels) + +#Get the mip map "closest" to the specified resolution +def mipKey(txSz, mipLevels): + inds = np.array(mipLevels) >= txSz + return mipLevels[np.argmax(inds)] + +#Render a full tiled map to the given screen (Container) +#env specifies an array of texture indices (requires a tex dict) +#or an array of colors otherwise. +#iso can be used to render square maps in isometric perspective +def renderMap(screen, env, txSz, tex=None, iso=False): + shape = env.shape + H, W = shape[:2] + for h in range(H): + for w in range(W): + if tex is None: + ww, hh = tileCoords(w, h, W, H, txSz, iso) + screen.rect(env[h, w], (ww, hh, txSz, txSz)) + else: + mat = tex[env[h, w]] + renderTile(screen, mat, w, h, txSz, iso) + +#Convert a tile to isometric +def tileToIso(tex, txSz): + tex.set_colorkey(Neon.BLACK.rgb) + tex = pygame.transform.rotate(tex, -45) + ww, hh = tex.get_width(), tex.get_height()//2 + tex = pygame.transform.scale(tex, (ww, hh)) + return tex + +#coords of a tile in ortho perspective +def cartCoords(w, h, txSz): + return int(w*txSz), int(h*txSz) + +#coords of a tile in isometric perspective +def isoCoords(w, h, W, H, txSz): + nW, nH = W//txSz, H//txSz + ww = nW//2 + (w - h) / 2 + hh = nH//4 + (w + h) / 4 + w, h = cartCoords(ww, hh, txSz) + return w, h + +#coords of a tile in ortho/isometric perspective +def tileCoords(w, h, W, H, txSz, iso): + if iso: + return isoCoords(w, h, W, H, txSz) + else: + return cartCoords(w, h, txSz) + +#Isometric render a tile +def renderIso(screen, tex, w, h, txSz): + tex = tileToIso(tex, txSz) + w, h = isoCoords(w, h, screen.W, screen.H, txSz) + screen.blit(tex, (w, h)) + +#Ortho render a tile +def renderCart(screen, tex, w, h, txSz): + w, h = cartCoords(w, h, txSz) + screen.blit(tex, (w, h)) + +#Ortho/isometric render a tile +def renderTile(screen, tex, w, h, txSz, iso): + if iso: + renderIso(screen, tex, w, h, txSz) + else: + renderCart(screen, tex, w, h, txSz) + +#Basic color class with different color schemes +#usable almost anywhere (provides hex/rgb255/rgb1 vals) +class Color: + def __init__(self, name, hexVal): + self.name = name + self.hex = hexVal + self.rgb = rgb(hexVal) + self.norm = rgbNorm(hexVal) + self.value = self.rgb #Emulate enum + +def rgb(h): + h = h.lstrip('#') + return tuple(int(h[i:i+2], 16) for i in (0, 2, 4)) + +def rgbNorm(h): + h = h.lstrip('#') + return tuple(int(h[i:i+2], 16)/255.0 for i in (0, 2, 4)) + +#Neon color pallete + a few common game colors. +#Why would you need anything else? +class Neon: + RED = Color('RED', '#ff0000') + ORANGE = Color('ORANGE', '#ff8000') + YELLOW = Color('YELLOW', '#ffff00') + + GREEN = Color('GREEN', '#00ff00') + MINT = Color('MINT', '#00ff80') + CYAN = Color('CYAN', '#00ffff') + + BLUE = Color('BLUE', '#0000ff') + PURPLE = Color('PURPLE', '#8000ff') + MAGENTA = Color('MAGENTA', '#ff00ff') + + FUCHSIA = Color('FUCHSIA', '#ff0080') + SPRING = Color('SPRING', '#80ff80') + SKY = Color('SKY', '#0080ff') + + WHITE = Color('WHITE', '#ffffff') + GRAY = Color('GRAY', '#666666') + BLACK = Color('BLACK', '#000000') + + BLOOD = Color('BLOOD', '#bb0000') + BROWN = Color('BROWN', '#7a3402') + GOLD = Color('GOLD', '#eec600') + SILVER = Color('SILVER', '#b8b8b8') + + #Hacker green + TERM = Color('TERM', '#41ff00') + + #Purple alpha + MASK = Color('MASK', '#d67fff') + + #Get the 12 neon colors + def color12(): + return ( + Neon.RED, Neon.ORANGE, Neon.YELLOW, + Neon.GREEN, Neon.MINT, Neon.CYAN, + Neon.BLUE, Neon.PURPLE, Neon.MAGENTA, + Neon.FUCHSIA, Neon.SPRING, Neon.SKY) + + #Get a random neon color + def rand12(): + twelveColor = color12() + randInd = np.random.randint(0, len(twelveColor)) + return twelveColor[randInd] + +#Pygame Surface initializer from file +def Texture(path, mask=None): + try: + img = imread(path)[:, :, :3] + except FileNotFoundError: + raise + + img = pygame.pixelcopy.make_surface(img) + if mask is not None: + img.set_colorkey(mask) + + #For some reason, pygame loads images transformed + img = pygame.transform.flip(img, True, False) + img = pygame.transform.rotate(img, 90) + return img + +#Pygame blank Surface initializer +def Canvas(size, mask=None): + img = pygame.Surface(size) + if mask is not None: + img.set_colorkey(mask) + return img + +#Pygame font wrapper +class Font: + def __init__(self, size, name='freesansbold.ttf'): + self.font = pygame.font.Font(name, size) + + def render(self, size, color): + return self.font.render(size, 1, color) + +#Exponentially decaying FPS tracker +class FPSTracker: + def __init__(self): + self.start = time.time() + self.eda = EDA(k=0.95) + self.fpsVal = 0.0 + + def update(self): + tick = time.time() - self.start + self.eda.update(1.0/tick) + self.fpsVal = self.eda.eda + self.start = time.time() + + @property + def fps(self): + return str(self.fpsVal)[:5] + +#Exponentially decaying average +class EDA(): + def __init__(self, k=0.9): + self.k = k + self.eda = None + + def update(self, x): + if self.eda is None: + self.eda = x + return + self.eda = (1-self.k)*x + self.k*self.eda diff --git a/jsuarez/extra/embyr_deprecated/embyr/embyr3D.py b/jsuarez/extra/embyr_deprecated/embyr/embyr3D.py new file mode 100644 index 00000000..0bd551fd --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/embyr3D.py @@ -0,0 +1,257 @@ +from pdb import set_trace as T +import numpy as np +import time +from scipy.misc import imresize, imsave +from enum import Enum + +import pygame +from pygame import Surface + +from forge.embyr import embyr +from forge.embyr import utils as renderutils +from forge.embyr import render +from forge.embyr.texture import TextureInitializer +from forge.blade.lib.enums import Neon, Color256, Defaults +from forge.blade.action.v2 import Attack +from forge.blade.action import action + +from pdb import set_trace as T +import numpy as np + +import os +import kivy3 +from kivy.app import App +from kivy3 import Scene, Renderer, PerspectiveCamera +from kivy3.loaders import OBJMTLLoader +from kivy.uix.floatlayout import FloatLayout +from kivy.uix.widget import Widget as KivyWidget +from kivy.config import Config +from kivy.graphics import opengl as gl +from kivy.graphics import Mesh as KivyMesh +from kivy3.core.object3d import Object3D +from kivy3.materials import Material +from kivy.core.image import Image +from copy import deepcopy +from forge.embyr.embyr import Application as KivyApp +import pywavefront as pywave +import pytmx +from forge.blade.lib import enums +from forge.embyr.transform import Transform + +root = 'forge/embyr/' +Config.set('input', 'mouse', 'mouse,multitouch_on_demand') +shaderf = 'tex/default.glsl' +pi = 3.14159265 +NANIM = 4 + +class MaterialMesh(Object3D): + def __init__(self, mesh, material): + super().__init__() + self._mesh = mesh + self.material = material + self.mtl = material + self.bind(pos = self.update_pos) + + self.material.diffuse = material.diffuse[:3] + self.material.specular = material.specular[:3] + self.material.shininess = material.shininess + + def retexturef(self, texf): + tex = Image(texf).texture + self.retexture(self, tex) + + def retexture(self, tex): + self._mesh.texture = tex + + def update_pos(self, x, y, z): + self.pos.x = float(x) + self.pos.y = float(y) + self.pos.z = float(z) + + def custom_instructions(self): + yield self.material + yield self._mesh + +class OBJ(MaterialMesh): + def __init__(self, objs): + super(MaterialMesh, self).__init__() + for _, obj in objs.items(): + mesh, mat = obj + matmesh = MaterialMesh(mesh, mat) + self.add(matmesh) + + def custom_instructions(self): + for child in self.children: + for instr in child.custom_instructions(): + yield instr + +class Block(MaterialMesh): + def __init__(self, tile): + objs = OBJLoader.loadRaw(root+'block.obj', + root+'tex/'+tile+'.png')['Cube'] + super().__init__(*objs) + self.block(tile) + + def block(self, tile): + if tile == 'lava': + self.pos.y = -0.5 + elif tile == 'stone': + self.pos.y = 1 + elif tile == 'water': + self.pos.y = -0.33 + +class Ent(OBJ): + def __init__(self): + objs = OBJLoader.loadRaw(root+'tex/nn.obj') + super().__init__(objs) + +class Map: + def __init__(self, scene, path='resource/maps/map1/map.tmx'): + tiled = self.loadTiled(path) + self.blocks = self.load(tiled, scene) + + def load(self, tiles, scene, border=8): + blocks, self.shape = {}, tiles.shape + self.R, self.C = tiles.shape + for i in range(border, self.R-border): + for j in range(border, self.C-border): + tile = tiles[i, j] + + #Load unit cube + block = Block(tile) + scene.add(block) + blocks[(i, j)] = block + + #NEVER set .pos directly. + #It won't do anything + block.pos.x = i + block.pos.z = j + + block.x0 = block.pos.x + block.y0 = block.pos.y + block.z0 = block.pos.z + + return blocks + + def loadTiled(self, fPath): + import pytmx + tm = pytmx.TiledMap(fPath) + assert len(tm.layers) == 1 + layer = tm.layers[0] + W, H = layer.width, layer.height + tilemap = np.zeros((H, W), dtype=object) + for w, h, dat in layer.tiles(): + f = dat[0] + tex = f.split('/')[-1].split('.')[0] + tilemap[h, w] = tex + return tilemap + +class OBJLoader: + def formats(name): + if name == 'T2F_N3F_V3F': + return [ + (b'v_tc0', 2, 'float'), + (b'v_normal', 3, 'float'), + (b'v_pos', 3, 'float')] + elif name == 'N3F_V3F': + return [ + (b'v_normal', 3, 'float'), + (b'v_pos', 3, 'float')] + + def load(objf, texf=None): + raw = OBJLoader.loadRaw(objf, texf) + return OBJ(raw) + + def loadRaw(objf, texf=None): + rets, obj = {}, pywave.Wavefront(objf, collect_faces=True) + for name, mesh in obj.meshes.items(): + rets[name] = OBJLoader.loadSingle(mesh, texf) + return rets + + def loadSingle(obj, texf=None): + assert len(obj.materials) == 1 + material = obj.materials[0] + + if texf is None: + texf = material.texture.image_name + tex = Image(texf).texture + + vertices = material.vertices + nVertex = len(vertices) / material.vertex_size + indices = np.arange(nVertex).astype(int).tolist() + fmt = OBJLoader.formats(material.vertex_format) + + kw = {"vertices": vertices, "indices": indices, + "fmt": fmt, + "mode": "triangles", + 'texture':tex + } + + mesh = KivyMesh(**kw) + mat = Material(tex) + return mesh, mat + +class Widget(KivyWidget): + def __init__(self, root, pos=(0, 0), size=(1, 1)): + super().__init__() + self.root = root + w, h = pos + self.pos_hint = {'left':w, 'top':h} + self.size_hint = size + + +class RS(Widget): + def __init__(self, root, **kwargs): + super().__init__(root) + self.path = 'forge/embyr/' + self.glSetup() + self.camera = PerspectiveCamera(30, 1, 10, 1000) + #self.transform = Transform() + self.transform = Transform( + pos=[8, 20, 8], lookvec=[40, 10, 40]) + + pos = kwargs.pop('pos', None) + size = kwargs.pop('size', None) + #pos_hint = {'right':ww, 'bottom':hh} + self.renderer = Renderer(shader_file=self.path+shaderf) + #self.renderer.pos_hint = pos_hint + + self.scene = Scene() + #self.renderer.size_hint = (0.5, 0.5) + self.transform.size = (0, 0) + self.transform.size_hint = (0, 0) + + self.renderer.render(self.scene, self.camera) + self.renderer.bind(size=self._adjust_aspect) + self.renderer.size_hint = size + self.renderer.pos = pos + + #self.renderer.pos = (256, 256) + + self.root.add_widget(self.renderer) + self.root.add_widget(self.transform) + #self._disabled_count = 0 + #self.add_widget(self.renderer) + #self.add_widget(self.transform) + + def glSetup(self): + gl.glEnable(gl.GL_CULL_FACE) + gl.glCullFace(gl.GL_BACK) + + def add(self, obj): + self.scene.add(obj) + self.renderer.add(obj) + + def update(self, dt): + pos, vec = self.transform.update(dt) + self.renderer.camera.look_at(vec) + x, y, z = pos + self.renderer.camera.pos = (-x, -y, -z) + return vec + + def _adjust_aspect(self, inst, val): + rsize = self.renderer.size + aspect = rsize[0] / float(rsize[1]) + self.renderer.camera.aspect = aspect + + diff --git a/jsuarez/extra/embyr_deprecated/embyr/enums.py b/jsuarez/extra/embyr_deprecated/embyr/enums.py new file mode 100644 index 00000000..d269b952 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/enums.py @@ -0,0 +1,187 @@ +#Various Enums used for handling materials, entity types, etc. +#Data texture pairs are used for enums that require textures. +#These textures are filled in by the Render class at run time. + +from pdb import set_trace as T +import numpy as np +import colorsys +from enum import Enum + +class Tile: + capacity = 3 + respawnProb = 0.1 + def __init__(self): + self.harvestable = False + +class Lava(Tile): + index = 0 + tex = 'lava' + obj = 'tex/' + tex + '.obj' +class Water(Tile): + index = 1 + tex = 'water' + obj = 'tex/' + tex + '.obj' +class Grass(Tile): + index = 2 + tex = 'grass' + obj = 'tex/' + tex + '.obj' +class Scrub(Tile): + index = 3 + tex = 'scrub' + obj = 'tex/' + tex + '.obj' +class Forest(Tile): + index = 4 + degen = Scrub + tex = 'forest' + obj = 'tex/' + tex + '.obj' + #capacity = 3 + capacity = 1 + respawnProb = 0.025 + def __init__(self): + super().__init__() + self.harvestable = True + #self.dropTable = DropTable.DropTable() +class Stone(Tile): + index = 5 + tex = 'stone' + obj = 'tex/' + tex + '.obj' +class Orerock(Tile): + index = 6 + degenIndex = Stone.index + tex = 'iron_ore' + obj = 'tex/' + tex + '.obj' + capacity = 1 + respawnprob = 0.05 + def __init__(self): + super().__init__() + self.harvestable = True + self.dropTable = systems.DropTable() + self.dropTable.add(ore.Copper, 1) + +class Material(Enum): + LAVA = Lava + WATER = Water + GRASS = Grass + SCRUB = Scrub + FOREST = Forest + STONE = Stone + OREROCK = Orerock + +IMPASSIBLE = (1, 5, 6) + +class Defaults: + BLACK = (0, 0, 0) + GRAY3 = (20, 20, 20) + GRAY2 = (40, 40, 40) + GRAY1 = (60, 60, 60) + RED = (255, 0, 0) + GREEN = (0, 255, 0) + BLUE = (0, 0, 255) + YELLOW = (255, 255, 0) + GOLD = (212, 175, 55) + MASK = (214, 127, 255) + +def rgb(h): + h = h.lstrip('#') + return tuple(int(h[i:i+2], 16) for i in (0, 2, 4)) + +def rgbNorm(h): + h = h.lstrip('#') + return tuple(int(h[i:i+2], 16)/255.0 for i in (0, 2, 4)) + +class Color: + def __init__(self, name, hexVal): + self.name = name + self.hex = hexVal + self.rgb = rgb(hexVal) + self.norm = rgbNorm(hexVal) + self.value = self.rgb #Emulate enum + +def makeColor(idx, h=1, s=1, v=1): + r, g, b = colorsys.hsv_to_rgb(h, s, v) + rgbval = tuple(int(255*e) for e in [r, g, b]) + hexval = '%02x%02x%02x' % rgbval + return Color(str(idx), hexval) + +class Color256: + def make256(): + parh, parv = np.meshgrid(np.linspace(0.075, 1, 16), np.linspace(0.25, 1, 16)[::-1]) + parh, parv = parh.T.ravel(), parv.T.ravel() + idxs = np.arange(256) + params = zip(idxs, parh, parv) + colors = [makeColor(idx, h=h, s=1, v=v) for idx, h, v in params] + return colors + colors = make256() + +class Neon: + RED = Color('RED', '#ff0000') + ORANGE = Color('ORANGE', '#ff8000') + YELLOW = Color('YELLOW', '#ffff00') + + GREEN = Color('GREEN', '#00ff00') + MINT = Color('MINT', '#00ff80') + CYAN = Color('CYAN', '#00ffff') + + BLUE = Color('BLUE', '#0000ff') + PURPLE = Color('PURPLE', '#8000ff') + MAGENTA = Color('MAGENTA', '#ff00ff') + + FUCHSIA = Color('FUCHSIA', '#ff0080') + SPRING = Color('SPRING', '#80ff80') + SKY = Color('SKY', '#0080ff') + + WHITE = Color('WHITE', '#ffffff') + GRAY = Color('GRAY', '#666666') + BLACK = Color('BLACK', '#000000') + + BLOOD = Color('BLOOD', '#bb0000') + BROWN = Color('BROWN', '#7a3402') + GOLD = Color('GOLD', '#eec600') + SILVER = Color('SILVER', '#b8b8b8') + + TERM = Color('TERM', '#41ff00') + MASK = Color('MASK', '#d67fff') + + def color12(): + return ( + Neon.RED, Neon.ORANGE, Neon.YELLOW, + Neon.GREEN, Neon.MINT, Neon.CYAN, + Neon.BLUE, Neon.PURPLE, Neon.MAGENTA, + Neon.FUCHSIA, Neon.SPRING, Neon.SKY) + + def rand12(): + twelveColor = color12() + randInd = np.random.randint(0, len(twelveColor)) + return twelveColor[randInd] + +class Palette: + def __init__(self, n): + self.n = n + if n <= 12: + self.colors = Neon.color12() + else: + self.colors = Color256.colors + + def color(self, idx): + if self.n > 12: + idx = int(idx * 256 // self.n) + return self.colors[idx] + +class DataTexturePair: + def __init__(self, data, texture=None): + self.data = data + self.tex = texture + + def __eq__(self, ind): + return ind == self.data + + def __hash__(self): + return hash(self.data) + + +class Entity(Enum): + NONE = DataTexturePair(0) + NEURAL = DataTexturePair(1) + CHICKEN = DataTexturePair(2) + GOBLIN = DataTexturePair(3) + BEAR = DataTexturePair(4) diff --git a/jsuarez/extra/embyr_deprecated/embyr/example1.py b/jsuarez/extra/embyr_deprecated/embyr/example1.py new file mode 100644 index 00000000..b12fec83 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/example1.py @@ -0,0 +1,71 @@ +""" +The MIT License (MIT) + +Copyright (c) 2014 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +""" +This example loads simple objects from .obj file and shows how +to use custom shader file +""" + +import os +#import kivy3 +from kivy.app import App +from kivy3 import Scene, Renderer, PerspectiveCamera +from kivy3.loaders import OBJLoader +from kivy.uix.floatlayout import FloatLayout + + +# Resources pathes +_this_path = os.path.dirname(os.path.realpath(__file__)) +shader_file = os.path.join(_this_path, "examples/simple.glsl") +obj_path = os.path.join(_this_path, "examples/testnurbs.obj") + + +class MainApp(App): + + def build(self): + root = FloatLayout() + self.renderer = Renderer(shader_file=shader_file) + scene = Scene() + camera = PerspectiveCamera(15, 1, 1, 1000) + # load obj file + loader = OBJLoader() + obj = loader.load(obj_path) + + scene.add(*obj.children) + for obj in scene.children: + obj.pos.z = -20 + + self.renderer.render(scene, camera) + root.add_widget(self.renderer) + self.renderer.bind(size=self._adjust_aspect) + return root + + def _adjust_aspect(self, inst, val): + rsize = self.renderer.size + aspect = rsize[0] / float(rsize[1]) + self.renderer.camera.aspect = aspect + + +if __name__ == '__main__': + MainApp().run() diff --git a/jsuarez/extra/embyr_deprecated/embyr/example2.py b/jsuarez/extra/embyr_deprecated/embyr/example2.py new file mode 100644 index 00000000..485af992 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/example2.py @@ -0,0 +1,78 @@ +""" +The MIT License (MIT) + +Copyright (c) 2014 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +""" +Same as example1 but with using default shader file and colorizing +of the objects +""" + +import os +#import kivy3 +from kivy.app import App +from kivy3 import Scene, Renderer, PerspectiveCamera +from kivy3.loaders import OBJLoader +from kivy.uix.floatlayout import FloatLayout +from pdb import set_trace as T + +class MainApp(App): + + def build(self): + root = FloatLayout() + self.renderer = Renderer() + scene = Scene() + camera = PerspectiveCamera(30, 1, 1, 1000) + # load obj file + loader = OBJLoader() + obj_path = os.path.join(os.path.dirname(__file__), "examples/testnurbs.obj") + obj = loader.load(obj_path) + + scene.add(*obj.children) + for obj in scene.children: + obj.pos.z = -20 + obj.material.specular = .35, .35, .35 + + # set colors to 3d objects + scene.children[0].material.color = 0., .7, 0. # green + scene.children[1].material.color = .7, 0., 0. # red + scene.children[2].material.color = 0., 0., .7 # blue + scene.children[3].material.color = .7, .7, 0. # yellow + + scene.children[0].material.diffuse = 0., .7, 0. # green + scene.children[1].material.diffuse = .7, 0., 0. # red + scene.children[2].material.diffuse = 0., 0., .7 # blue + scene.children[3].material.diffuse = .7, .7, 0. # yellow + + self.renderer.render(scene, camera) + root.add_widget(self.renderer) + self.renderer.bind(size=self._adjust_aspect) + return root + + def _adjust_aspect(self, inst, val): + rsize = self.renderer.size + aspect = rsize[0] / float(rsize[1]) + self.renderer.camera.aspect = aspect + + +if __name__ == '__main__': + MainApp().run() diff --git a/jsuarez/extra/embyr_deprecated/embyr/examplecust.py b/jsuarez/extra/embyr_deprecated/embyr/examplecust.py new file mode 100644 index 00000000..d8ccda9b --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/examplecust.py @@ -0,0 +1,76 @@ +""" +The MIT License (MIT) + +Copyright (c) 2014 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +""" +Same as example1 but with using default shader file and colorizing +of the objects +""" + +import os +#import kivy3 +from kivy.app import App +from kivy3 import Scene, Renderer, PerspectiveCamera +from kivy3.loaders import OBJLoader +from kivy.uix.floatlayout import FloatLayout +from pdb import set_trace as T + + +class MainApp(App): + + def build(self): + root = FloatLayout() + self.renderer = Renderer(shader_file='examples/simple.glsl') + scene = Scene() + camera = PerspectiveCamera(30, 1, 1, 1000) + # load obj file + loader = OBJLoader() + obj_path = os.path.join(os.path.dirname(__file__), "cube.obj") + obj = loader.load(obj_path) + cube = obj.children[0] + #scene.add(*obj.children) + #for obj in scene.children: + + scene.add(cube) + cube.pos.z = -20 + cube.rot.y = -45 + cube.rot.x = 45 + cube.material.specular = .35, .35, .35 + + # set colors to 3d objects + scene.children[0].material.color = 0., .7, 0. # green + scene.children[0].material.diffuse = 0., .7, 0. # green + + self.renderer.render(scene, camera) + root.add_widget(self.renderer) + self.renderer.bind(size=self._adjust_aspect) + return root + + def _adjust_aspect(self, inst, val): + rsize = self.renderer.size + aspect = rsize[0] / float(rsize[1]) + self.renderer.camera.aspect = aspect + + +if __name__ == '__main__': + MainApp().run() diff --git a/jsuarez/extra/embyr_deprecated/embyr/examples/camera/main.py b/jsuarez/extra/embyr_deprecated/embyr/examples/camera/main.py new file mode 100644 index 00000000..eed87891 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/examples/camera/main.py @@ -0,0 +1,77 @@ + +import os +import kivy3 +from kivy.app import App +from kivy.clock import Clock +from kivy.core.window import Window +from kivy3 import Scene, Renderer, PerspectiveCamera, Vector3 +from kivy3.loaders import OBJMTLLoader +from kivy.uix.floatlayout import FloatLayout + +# Resources pathes +_this_path = os.path.dirname(os.path.realpath(__file__)) +shader_file = os.path.join(_this_path, "../textures/simple.glsl") +obj_file = os.path.join(_this_path, "../textures/orion.obj") +mtl_file = os.path.join(_this_path, "../textures/orion.mtl") + +class MainApp(App): + + def build(self): + self.look_at = Vector3(0, 0, -1) + root = FloatLayout() + self.renderer = Renderer(shader_file=shader_file) + scene = Scene() + self.camera = PerspectiveCamera(75, 1, 1, 1000) + self.camera.pos.z = 5 + loader = OBJMTLLoader() + obj = loader.load(obj_file, mtl_file) + self._keyboard = Window.request_keyboard(self._keyboard_closed, self) + self._keyboard.bind(on_key_down=self._on_keyboard_down) + + scene.add(*obj.children) + + self.renderer.render(scene, self.camera) + self.orion = scene.children[0] + + root.add_widget(self.renderer) + self.renderer.bind(size=self._adjust_aspect) + Clock.schedule_interval(self._rotate_obj, 1 / 20) + return root + + def _adjust_aspect(self, inst, val): + rsize = self.renderer.size + aspect = rsize[0] / float(rsize[1]) + self.renderer.camera.aspect = aspect + + def _keyboard_closed(self): + self._keyboard.unbind(on_key_down=self._on_keyboard_down) + self._keyboard = None + + def _on_keyboard_down(self, keyboard, keycode, text, modifiers): + if keycode[1] == 'w': + self.camera.pos.z -= 0.2 + elif keycode[1] == 's': + self.camera.pos.z += 0.2 + elif keycode[1] == 'a': + self.camera.pos.y -= 0.2 + elif keycode[1] == 'd': + self.camera.pos.y += 0.2 + + elif keycode[1] == 'up': + self.look_at.y += 0.2 + elif keycode[1] == 'down': + self.look_at.y -= 0.2 + elif keycode[1] == 'right': + self.look_at.x += 0.2 + elif keycode[1] == 'left': + self.look_at.x -= 0.2 + + self.camera.look_at(self.look_at) + + def _rotate_obj(self, dt): + self.orion.rot.x += 2 + self.orion.rot.z += 2 + + +if __name__ == '__main__': + MainApp().run() diff --git a/jsuarez/extra/embyr_deprecated/embyr/examples/createscene.py b/jsuarez/extra/embyr_deprecated/embyr/examples/createscene.py new file mode 100644 index 00000000..0d829b96 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/examples/createscene.py @@ -0,0 +1,48 @@ + +import os +import kivy3 +from kivy.app import App +from kivy.uix.floatlayout import FloatLayout +from kivy.clock import Clock +from kivy3 import Mesh, Material +from kivy3 import Scene, Renderer, PerspectiveCamera +from kivy3.extras.geometries import BoxGeometry + + +class SceneApp(App): + + def build(self): + root = FloatLayout() + + self.renderer = Renderer() + self.renderer.set_clear_color((.2, .2, .2, 1.)) + scene = Scene() + geometry = BoxGeometry(1, 1, 1) + material = Material(color=(0., 0., 1.), diffuse=(1., 1., 0.), + specular=(.35, .35, .35)) + self.cube = Mesh(geometry, material) + self.cube.pos.z = -5 + camera = PerspectiveCamera(75, 0.3, 1, 1000) + + scene.add(self.cube) + self.renderer.render(scene, camera) + + root.add_widget(self.renderer) + Clock.schedule_interval(self._rotate_cube, 1 / 20) + self.renderer.bind(size=self._adjust_aspect) + + return root + + def _adjust_aspect(self, inst, val): + rsize = self.renderer.size + aspect = rsize[0] / float(rsize[1]) + self.renderer.camera.aspect = aspect + + def _rotate_cube(self, dt): + self.cube.rotation.x += 1 + self.cube.rotation.y += 1 + self.cube.rotation.z += 1 + + +if __name__ == '__main__': + SceneApp().run() diff --git a/jsuarez/extra/embyr_deprecated/embyr/examples/example1.py b/jsuarez/extra/embyr_deprecated/embyr/examples/example1.py new file mode 100644 index 00000000..2c0148aa --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/examples/example1.py @@ -0,0 +1,71 @@ +""" +The MIT License (MIT) + +Copyright (c) 2014 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +""" +This example loads simple objects from .obj file and shows how +to use custom shader file +""" + +import os +import kivy3 +from kivy.app import App +from kivy3 import Scene, Renderer, PerspectiveCamera +from kivy3.loaders import OBJLoader +from kivy.uix.floatlayout import FloatLayout + + +# Resources pathes +_this_path = os.path.dirname(os.path.realpath(__file__)) +shader_file = os.path.join(_this_path, "./simple.glsl") +obj_path = os.path.join(_this_path, "./testnurbs.obj") + + +class MainApp(App): + + def build(self): + root = FloatLayout() + self.renderer = Renderer(shader_file=shader_file) + scene = Scene() + camera = PerspectiveCamera(15, 1, 1, 1000) + # load obj file + loader = OBJLoader() + obj = loader.load(obj_path) + + scene.add(*obj.children) + for obj in scene.children: + obj.pos.z = -20 + + self.renderer.render(scene, camera) + root.add_widget(self.renderer) + self.renderer.bind(size=self._adjust_aspect) + return root + + def _adjust_aspect(self, inst, val): + rsize = self.renderer.size + aspect = rsize[0] / float(rsize[1]) + self.renderer.camera.aspect = aspect + + +if __name__ == '__main__': + MainApp().run() diff --git a/jsuarez/extra/embyr_deprecated/embyr/examples/example2.py b/jsuarez/extra/embyr_deprecated/embyr/examples/example2.py new file mode 100644 index 00000000..102d4146 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/examples/example2.py @@ -0,0 +1,78 @@ +""" +The MIT License (MIT) + +Copyright (c) 2014 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +""" +Same as example1 but with using default shader file and colorizing +of the objects +""" + +import os +import kivy3 +from kivy.app import App +from kivy3 import Scene, Renderer, PerspectiveCamera +from kivy3.loaders import OBJLoader +from kivy.uix.floatlayout import FloatLayout + + +class MainApp(App): + + def build(self): + root = FloatLayout() + self.renderer = Renderer() + scene = Scene() + camera = PerspectiveCamera(15, 1, 1, 1000) + # load obj file + loader = OBJLoader() + obj_path = os.path.join(os.path.dirname(__file__), "./testnurbs.obj") + obj = loader.load(obj_path) + + scene.add(*obj.children) + for obj in scene.children: + obj.pos.z = -20 + obj.material.specular = .35, .35, .35 + + # set colors to 3d objects + scene.children[0].material.color = 0., .7, 0. # green + scene.children[1].material.color = .7, 0., 0. # red + scene.children[2].material.color = 0., 0., .7 # blue + scene.children[3].material.color = .7, .7, 0. # yellow + + scene.children[0].material.diffuse = 0., .7, 0. # green + scene.children[1].material.diffuse = .7, 0., 0. # red + scene.children[2].material.diffuse = 0., 0., .7 # blue + scene.children[3].material.diffuse = .7, .7, 0. # yellow + + self.renderer.render(scene, camera) + root.add_widget(self.renderer) + self.renderer.bind(size=self._adjust_aspect) + return root + + def _adjust_aspect(self, inst, val): + rsize = self.renderer.size + aspect = rsize[0] / float(rsize[1]) + self.renderer.camera.aspect = aspect + + +if __name__ == '__main__': + MainApp().run() diff --git a/jsuarez/extra/embyr_deprecated/embyr/examples/example3.py b/jsuarez/extra/embyr_deprecated/embyr/examples/example3.py new file mode 100644 index 00000000..552b5b26 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/examples/example3.py @@ -0,0 +1,66 @@ +""" +The MIT License (MIT) + +Copyright (c) 2014 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +""" +This example loads simple objects from .obj file and shows how +to use custom shader file +""" + +import os +import kivy3 +from kivy.app import App +from kivy3 import Scene, Renderer, PerspectiveCamera +from kivy3.loaders import OBJMTLLoader +from kivy.uix.floatlayout import FloatLayout + + +class MainApp(App): + + def build(self): + root = FloatLayout() + self.renderer = Renderer() + scene = Scene() + camera = PerspectiveCamera(15, 1, 1, 1000) + # load obj file + loader = OBJMTLLoader() + obj_path = os.path.join(os.path.dirname(__file__), "./testnurbs.obj") + obj = loader.load(obj_path, "./testnurbs.mtl") + + scene.add(*obj.children) + for obj in scene.children: + obj.pos.z = -20 + + self.renderer.render(scene, camera) + root.add_widget(self.renderer) + self.renderer.bind(size=self._adjust_aspect) + return root + + def _adjust_aspect(self, inst, val): + rsize = self.renderer.size + aspect = rsize[0] / float(rsize[1]) + self.renderer.camera.aspect = aspect + + +if __name__ == '__main__': + MainApp().run() diff --git a/jsuarez/extra/embyr_deprecated/embyr/examples/monkey.py b/jsuarez/extra/embyr_deprecated/embyr/examples/monkey.py new file mode 100644 index 00000000..e792f294 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/examples/monkey.py @@ -0,0 +1,70 @@ +""" +The MIT License (MIT) + +Copyright (c) 2014 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +import os +from kivy.app import App +from kivy3 import Scene, Renderer, PerspectiveCamera +from kivy3.loaders import OBJLoader +from kivy.uix.floatlayout import FloatLayout +from kivy.clock import Clock + +# Resources pathes +_this_path = os.path.dirname(os.path.realpath(__file__)) +shader_file = os.path.join(_this_path, "./simple.glsl") +obj_file = os.path.join(_this_path, "./monkey.obj") + + +class MainApp(App): + + def build(self): + root = FloatLayout() + self.renderer = Renderer(shader_file=shader_file) + scene = Scene() + # load obj file + loader = OBJLoader() + obj = loader.load(obj_file) + self.monkey = obj.children[0] + + scene.add(*obj.children) + camera = PerspectiveCamera(15, 1, 1, 1000) + + self.renderer.render(scene, camera) + root.add_widget(self.renderer) + Clock.schedule_interval(self._update_obj, 1. / 20) + self.renderer.bind(size=self._adjust_aspect) + return root + + def _update_obj(self, dt): + obj = self.monkey + if obj.pos.z > -30: + obj.pos.z -= 0.5 + + def _adjust_aspect(self, inst, val): + rsize = self.renderer.size + aspect = rsize[0] / float(rsize[1]) + self.renderer.camera.aspect = aspect + + +if __name__ == '__main__': + MainApp().run() diff --git a/jsuarez/extra/embyr_deprecated/embyr/examples/textures/main.py b/jsuarez/extra/embyr_deprecated/embyr/examples/textures/main.py new file mode 100644 index 00000000..630b5c5c --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/examples/textures/main.py @@ -0,0 +1,49 @@ + +import os +import kivy3 +from kivy.app import App +from kivy.clock import Clock +from kivy3 import Scene, Renderer, PerspectiveCamera +from kivy3.loaders import OBJMTLLoader +from kivy.uix.floatlayout import FloatLayout + +# Resources pathes +_this_path = os.path.dirname(os.path.realpath(__file__)) +shader_file = os.path.join(_this_path, "./simple.glsl") +obj_file = os.path.join(_this_path, "./orion.obj") +mtl_file = os.path.join(_this_path, "./orion.mtl") + + +class MainApp(App): + + def build(self): + root = FloatLayout() + self.renderer = Renderer(shader_file=shader_file) + scene = Scene() + camera = PerspectiveCamera(15, 1, 1, 1000) + loader = OBJMTLLoader() + obj = loader.load(obj_file, mtl_file) + + scene.add(*obj.children) + for obj in scene.children: + obj.pos.z = -20. + + self.renderer.render(scene, camera) + self.orion = scene.children[0] + + root.add_widget(self.renderer) + self.renderer.bind(size=self._adjust_aspect) + Clock.schedule_interval(self._rotate_obj, 1 / 20) + return root + + def _adjust_aspect(self, inst, val): + rsize = self.renderer.size + aspect = rsize[0] / float(rsize[1]) + self.renderer.camera.aspect = aspect + + def _rotate_obj(self, dt): + self.orion.rot.x += 2 + + +if __name__ == '__main__': + MainApp().run() diff --git a/jsuarez/extra/embyr_deprecated/embyr/examples/trackball/main.py b/jsuarez/extra/embyr_deprecated/embyr/examples/trackball/main.py new file mode 100644 index 00000000..cac09931 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/examples/trackball/main.py @@ -0,0 +1,87 @@ + +import os +import math +from kivy.app import App +from kivy.clock import Clock +from kivy3 import Scene, Renderer, PerspectiveCamera +from kivy3.loaders import OBJLoader +from kivy.uix.floatlayout import FloatLayout + +# Resources pathes +_this_path = os.path.dirname(os.path.realpath(__file__)) +obj_file = os.path.join(_this_path, "./MQ-27.obj") + + +class ObjectTrackball(FloatLayout): + + def __init__(self, camera, radius, *args, **kw): + super(ObjectTrackball, self).__init__(*args, **kw) + self.camera = camera + self.radius = radius + self.phi = 90 + self.theta = 0 + self._touches = [] + self.camera.pos.z = radius + camera.look_at((0, 0, 0)) + + def define_rotate_angle(self, touch): + theta_angle = (touch.dx / self.width) * -360 + phi_angle = -1 * (touch.dy / self.height) * 360 + return phi_angle, theta_angle + + def on_touch_down(self, touch): + touch.grab(self) + self._touches.append(touch) + + def on_touch_up(self, touch): + touch.ungrab(self) + self._touches.remove(touch) + + def on_touch_move(self, touch): + if touch in self._touches and touch.grab_current == self: + if len(self._touches) == 1: + self.do_rotate(touch) + elif len(self._touches) == 2: + pass + + def do_rotate(self, touch): + d_phi, d_theta = self.define_rotate_angle(touch) + self.phi += d_phi + self.theta += d_theta + + _phi = math.radians(self.phi) + _theta = math.radians(self.theta) + z = self.radius * math.cos(_theta) * math.sin(_phi) + x = self.radius * math.sin(_theta) * math.sin(_phi) + y = self.radius * math.cos(_phi) + self.camera.pos = x, y, z + + +class MainApp(App): + + def build(self): + self.renderer = Renderer() + scene = Scene() + camera = PerspectiveCamera(15, 1, 1, 1000) + loader = OBJLoader() + obj = loader.load(obj_file) + self.obj3d = obj + self.camera = camera + root = ObjectTrackball(camera, 1500) + + scene.add(obj) + + self.renderer.render(scene, camera) + + root.add_widget(self.renderer) + self.renderer.bind(size=self._adjust_aspect) + return root + + def _adjust_aspect(self, inst, val): + rsize = self.renderer.size + aspect = rsize[0] / float(rsize[1]) + self.renderer.camera.aspect = aspect + + +if __name__ == '__main__': + MainApp().run() diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/__init__.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/__init__.py new file mode 100644 index 00000000..4b7225af --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/__init__.py @@ -0,0 +1,10 @@ + +from .scenes.scene import Scene +from .core.object3d import Object3D +from .math.vectors import Vector4, Vector3, Vector2 +from .cameras import PerspectiveCamera, OrthographicCamera +from .renderer import Renderer +from .objects.mesh import Mesh +from .materials import Material +from .core.geometry import Geometry +from .core.face3 import Face3 diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/cameras/__init__.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/cameras/__init__.py new file mode 100644 index 00000000..ed2b79dc --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/cameras/__init__.py @@ -0,0 +1,3 @@ +from .orthographic_camera import OrthographicCamera +from .perspective_camera import PerspectiveCamera +from .camera import Camera diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/cameras/camera.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/cameras/camera.py new file mode 100644 index 00000000..9ca477cb --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/cameras/camera.py @@ -0,0 +1,110 @@ +""" +The MIT License (MIT) + +Copyright (c) 2013 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +""" +Camera module +============= + +In this module base camera class is implemented. +""" + + +__all__ = ('Camera', ) + +import math + +from kivy.event import EventDispatcher +from kivy.properties import NumericProperty, ListProperty, ObjectProperty, \ + AliasProperty +from kivy.graphics.transformation import Matrix +from ..math.vectors import Vector3 + + +class Camera(EventDispatcher): + """ + Base camera class + """ + + scale = NumericProperty(1.0) + up = ObjectProperty(Vector3(0, 1, 0)) + + def __init__(self): + super(Camera, self).__init__() + self.projection_matrix = Matrix() + self.modelview_matrix = Matrix() + self.renderer = None # renderer camera is bound to + self._position = Vector3(0, 0, 0) + self._position.set_change_cb(self.on_pos_changed) + self._look_at = None + self.look_at(Vector3(0, 0, -1)) + + def _set_position(self, val): + if isinstance(val, Vector3): + self._position = val + else: + self._position = Vector3(val) + self._position.set_change_cb(self.on_pos_changed) + self.look_at(self._look_at) + self.update() + + def _get_position(self): + return self._position + + position = AliasProperty(_get_position, _set_position) + pos = position # just shortcut + + def on_pos_changed(self, coord, v): + """ Camera position was changed """ + self.look_at(self._look_at) + self.update() + + def on_up(self, instance, up): + """ Camera up vector was changed """ + pass + + def on_scale(self, instance, scale): + """ Handler for change scale parameter event """ + + def look_at(self, *v): + if len(v) == 1: + v = v[0] + m = Matrix() + pos = self._position * -1 + m = m.look_at(pos[0], pos[1], pos[2], v[0], v[1], v[2], + self.up[0], self.up[1], self.up[2]) + self.modelview_matrix = m + self._look_at = v + self.update() + + def bind_to(self, renderer): + """ Bind this camera to renderer """ + self.renderer = renderer + + def update(self): + if self.renderer: + self.renderer._update_matrices() + + def update_projection_matrix(self): + """ This function should be overridden in the subclasses + """ diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/cameras/orthographic_camera.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/cameras/orthographic_camera.py new file mode 100644 index 00000000..f8e15dc0 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/cameras/orthographic_camera.py @@ -0,0 +1,36 @@ +""" +The MIT License (MIT) + +Copyright (c) 2013 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +""" +Orthographic camera module +============= + +Implements orthographic camera type. +""" + +__all__ = () + + +class OrthographicCamera(): + """ Implements orthographic camera """ diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/cameras/perspective_camera.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/cameras/perspective_camera.py new file mode 100644 index 00000000..8f93c102 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/cameras/perspective_camera.py @@ -0,0 +1,63 @@ +""" +The MIT License (MIT) + +Copyright (c) 2013 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +""" +Perspective camera module +============= + +Implements perspective camera. +""" + +__all__ = ('PerspectiveCamera', ) + +from kivy.properties import NumericProperty +from kivy.graphics.transformation import Matrix +from .camera import Camera + + +class PerspectiveCamera(Camera): + """ + Implementation of the perspective camera. + """ + + aspect = NumericProperty() + + def __init__(self, fov, aspect, near, far, **kw): + + super(PerspectiveCamera, self).__init__(**kw) + self.fov = fov + self.aspect = aspect + self.near = near + self.far = far + self.update_projection_matrix() + self.bind(aspect=self._on_aspect) + + def _on_aspect(self, inst, value): + self.update_projection_matrix() + self.update() + + def update_projection_matrix(self): + m = Matrix() + m.perspective(self.fov * 0.5, self.aspect, self.near, self.far) + self.projection_matrix = m diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/core/__init__.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/core/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/core/face3.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/core/face3.py new file mode 100644 index 00000000..d65c56e6 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/core/face3.py @@ -0,0 +1,45 @@ +""" +The MIT License (MIT) + +Copyright (c) 2013 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +""" +Face3 class +============= + +This class should store information about mesh face. Should be used +with some vertices information as it doesn't contain vertices itself, +but only its indices + +""" + +from kivy3 import Vector3 + + +class Face3(object): + + def __init__(self, a, b, c, normal=None): + self.a = a + self.b = b + self.c = c + self.normal = normal or Vector3(0, 0, 0) # face normal + self.vertex_normals = [] # vertices normals diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/core/geometry.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/core/geometry.py new file mode 100644 index 00000000..a7780181 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/core/geometry.py @@ -0,0 +1,41 @@ +""" +The MIT License (MIT) + +Copyright (c) 2013 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +""" +Geometry class +============= + +""" + + +class Geometry(object): + + def __init__(self, name=''): + self.name = name + self.faces = [] + self.vertices = [] + self.face_vertex_uvs = [[]] + + def compute_vertex_normal(self): + pass diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/core/object3d.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/core/object3d.py new file mode 100644 index 00000000..e7f71027 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/core/object3d.py @@ -0,0 +1,150 @@ +""" +The MIT License (MIT) + +Copyright (c) 2013 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +""" +Object3D class +============= + +""" + +from kivy.properties import NumericProperty, ListProperty, ObjectProperty, \ + AliasProperty +from kivy.graphics import Scale, Rotate, PushMatrix, PopMatrix, Translate, \ + UpdateNormalMatrix +from kivy.graphics.instructions import InstructionGroup +from kivy.event import EventDispatcher + +from kivy3.math.vectors import Vector3 + + +class Object3D(EventDispatcher): + """Base class for all 3D objects in rendered + 3D world. + """ + + def __init__(self, **kw): + + super(Object3D, self).__init__(**kw) + self.name = kw.pop('name', '') + self.children = list() + self.parent = None + + self._scale = Scale(1., 1., 1.) + self._position = Vector3(0, 0, 0) + self._rotation = Vector3(0, 0, 0) + self._position.set_change_cb(self.on_pos_changed) + self._rotation.set_change_cb(self.on_angle_change) + + # general instructions + self._pop_matrix = PopMatrix() + self._push_matrix = PushMatrix() + self._translate = Translate(*self._position) + self._rotors = { + "x": Rotate(self._rotation.x, 1, 0, 0), + "y": Rotate(self._rotation.y, 0, 1, 0), + "z": Rotate(self._rotation.z, 0, 0, 1), + } + + self._instructions = InstructionGroup() + + def add(self, *objs): + for obj in objs: + self._add_child(obj) + + def _add_child(self, obj): + self.children.append(obj) + obj.parent = self + + def _set_position(self, val): + if isinstance(val, Vector3): + self._position = val + else: + self._position = Vector3(val) + self._position.set_change_cb(self.on_pos_changed) + + def _get_position(self): + return self._position + + position = AliasProperty(_get_position, _set_position) + pos = position # just shortcut + + def _set_rotation(self, val): + if isinstance(val, Vector3): + self._rotation = val + else: + self._rotation = Vector3(val) + self._rotation.set_change_cb(self.on_angle_change) + self._rotors["x"].angle = self._rotation.x + self._rotors["y"].angle = self._rotation.y + self._rotors["z"].angle = self._rotation.z + + def _get_rotation(self): + return self._rotation + + rotation = AliasProperty(_get_rotation, _set_rotation) + rot = rotation + + def _set_scale(self, val): + if isinstance(val, Scale): + self._scale = val + else: + self._scale = Scale(*val) + + def _get_scale(self): + return self._scale + + scale = AliasProperty(_get_scale, _set_scale) + + def on_pos_changed(self, coord, v): + """ Some coordinate was changed """ + self._translate.xyz = self._position + + def on_angle_change(self, axis, angle): + self._rotors[axis].angle = angle + + def as_instructions(self): + """ Get instructions set for renderer """ + if not self._instructions.children: + self._instructions.add(self._push_matrix) + self._instructions.add(self._translate) + self._instructions.add(self.scale) + for rot in self._rotors.values(): + self._instructions.add(rot) + self._instructions.add(UpdateNormalMatrix()) + for instr in self.custom_instructions(): + self._instructions.add(instr) + for child in self.get_children_instructions(): + self._instructions.add(child) + self._instructions.add(self._pop_matrix) + return self._instructions + + def custom_instructions(self): + """ Should be overriden in subclasses to provide some extra + instructions + """ + return [] + + def get_children_instructions(self): + for child in self.children: + yield child.as_instructions() diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/extras/__init__.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/extras/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/extras/geometries.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/extras/geometries.py new file mode 100644 index 00000000..2aa74326 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/extras/geometries.py @@ -0,0 +1,75 @@ +""" +The MIT License (MIT) + +Copyright (c) 2013 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +from kivy3 import Vector3 +from kivy3.core.geometry import Geometry +from kivy3.core.face3 import Face3 + + +class BoxGeometry(Geometry): + + _cube_vertices = [(-1, 1, -1), (1, 1, -1), + (1, -1, -1), (-1, -1, -1), + (-1, 1, 1), (1, 1, 1), + (1, -1, 1), (-1, -1, 1), + ] + + _cube_faces = [(0, 1, 2), (0, 2, 3), (3, 2, 6), + (3, 6, 7), (7, 6, 5), (7, 5, 4), + (4, 5, 1), (4, 1, 0), (4, 0, 3), + (7, 4, 3), (5, 1, 2), (6, 5, 2) + ] + + _cube_normals = [(0, 0, 1), (-1, 0, 0), (0, 0, -1), + (1, 0, 0), (0, 1, 0), (0, -1, 0) + ] + + def __init__(self, width, height, depth, **kw): + name = kw.pop('name', '') + super(BoxGeometry, self).__init__(name) + self.width_segment = kw.pop('width_segment', 1) + self.height_segment = kw.pop('height_segment', 1) + self.depth_segment = kw.pop('depth_segment', 1) + + self.w = width + self.h = height + self.d = depth + + self._build_box() + + def _build_box(self): + + for v in self._cube_vertices: + v = Vector3(0.5 * v[0] * self.w, + 0.5 * v[1] * self.h, + 0.5 * v[2] * self.d) + self.vertices.append(v) + + n_idx = 0 + for f in self._cube_faces: + face3 = Face3(*f) + normal = self._cube_normals[n_idx / 2] + face3.vertex_normals = [normal, normal, normal] + n_idx += 1 + self.faces.append(face3) diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/loaders/__init__.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/loaders/__init__.py new file mode 100644 index 00000000..e1088982 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/loaders/__init__.py @@ -0,0 +1,2 @@ + +from .objloader import OBJLoader, OBJMTLLoader diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/loaders/loader.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/loaders/loader.py new file mode 100644 index 00000000..59da8a1c --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/loaders/loader.py @@ -0,0 +1,83 @@ +""" +The MIT License (MIT) + +Copyright (c) 2013 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +""" +Loader class +============= + +Base loader class which should be used by all other loaders implementations +""" + +from kivy.clock import Clock + + +class BaseLoader(object): + + def __init__(self, **kw): + self._on_load_start = kw.pop("on_load_start", None) + self._on_load_progress = kw.pop("on_load_progress", None) + self._on_load_complete = kw.pop("on_load_complete", None) + self.source = None + + def on_load_start(self): + if callable(self._on_load_start): + self._on_load_start() + + def on_load_progress(self): + if callable(self._on_load_progress): + self._on_load_progress() + + def on_load_complete(self): + if callable(self._on_load_complete): + self._on_load_complete() + + def __setattr__(self, k, v): + if k in ["on_load_start", "on_load_progress", "on_load_complete"]: + if not callable(v): + raise Exception("%s should be callable" % k) + setattr(self, "_%s" % k, v) + else: + super(BaseLoader, self).__setattr__(k, v) + + def load(self, source, on_load=None, on_progress=None, on_error=None): + """This function loads objects from source. This function may work + in both synchronous or asynchronous way. To make it asynchronous + on_load callback function should be provided. + """ + self.source = source + + if not callable(on_load): + return self.parse() + + def _async_load(dt): + obj = self.parse() + on_load(obj) + + Clock.schedule_once(_async_load, 0) + + def parse(self): + """ This should be overridden in subclasses to provide + parse of the source and return loaded from source object + """ + raise NotImplementedError('Must be overriden in subclass') diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/loaders/objloader.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/loaders/objloader.py new file mode 100644 index 00000000..94e6edb0 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/loaders/objloader.py @@ -0,0 +1,227 @@ +""" +The MIT License (MIT) + +Copyright (c) 2013 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +""" +Loaders for Wavefront format .obj files +============= + +""" +from pdb import set_trace as T + +import os +from .loader import BaseLoader +from kivy.core.image import Image +from kivy3 import Object3D, Mesh, Material, Vector2 +from kivy3.core.geometry import Geometry +from kivy3.core.face3 import Face3 + + +class WaveObject(object): + """ This class contains top level mesh object information like vertices, + normals, texcoords and faces + """ + + _mtl_map = {"Ka": "color", "Kd": "diffuse", "Ks": "specular", + "Ns": "shininess", "Tr": "transparency", + "d": "transparency", "map_Kd": "map" + } + + def __init__(self, loader, name=''): + self.name = name + self.faces = [] + self.loader = loader + self.mtl_name = None + + def convert_to_mesh(self, vertex_format=None): + """Converts data gotten from the .obj definition + file and create Kivy3 Mesh object which may be used + for drawing object in the scene + """ + + geometry = Geometry() + material = Material() + mtl_dirname = os.path.abspath(os.path.dirname(self.loader.mtl_source)) + + v_idx = 0 + # create geometry for mesh + for f in self.faces: + verts = f[0] + norms = f[1] + tcs = f[2] + face3 = Face3(0, 0, 0) + for i, e in enumerate(['a', 'b', 'c']): + #get normal components + n = (0.0, 0.0, 0.0) + if norms[i] != -1: + n = self.loader.normals[norms[i] - 1] + face3.vertex_normals.append(n) + + #get vertex components + v = self.loader.vertices[verts[i] - 1] + geometry.vertices.append(v) + setattr(face3, e, v_idx) + v_idx += 1 + + #get texture coordinate components + t = (0.0, 0.0) + if tcs[i] != -1: + t = self.loader.texcoords[tcs[i] - 1] + tc = Vector2(t[0], 1. - t[1]) + geometry.face_vertex_uvs[0].append(tc) + + geometry.faces.append(face3) + + # apply material for object + if self.mtl_name in self.loader.mtl_contents: + raw_material = self.loader.mtl_contents[self.mtl_name] + for k, v in raw_material.items(): + _k = self._mtl_map.get(k, None) + if k in ["map_Kd", ]: + map_path = os.path.join(mtl_dirname, v[0]) + tex = Image(map_path).texture + material.map = tex + continue + if _k: + if len(v) == 1: + v = float(v[0]) + if k == 'Tr': + v = 1. - v + setattr(material, _k, v) + else: + v = list(map(lambda x: float(x), v)) + setattr(material, _k, v) + mesh = Mesh(geometry, material) + return mesh + + +class OBJLoader(BaseLoader): + + def __init__(self, **kw): + super(OBJLoader, self).__init__(**kw) + self.mtl_source = None # source of MTL + self.mtl_contents = {} # should be filled in load_mtl + + def load_mtl(self): + if not os.path.exists(self.mtl_source): + #TODO show warning about materials file is not found + return + for line in open(self.mtl_source, "r"): + if line.startswith('#'): + continue + values = line.split() + if not values: + continue + if values[0] == 'newmtl': + mtl = self.mtl_contents[values[1]] = {} + continue + elif mtl is None: + raise ValueError("mtl doesn't start with newmtl statement") + mtl[values[0]] = values[1:] + + def _load_meshes(self): + + wvobj = WaveObject(self) + self.vertices = [] + self.normals = [] + self.texcoords = [] + faces_section = False + + for line in open(self.source, "r"): + if line.startswith('#'): + continue + if line.startswith('s'): + continue + values = line.split() + if not values: + continue + if values[0] == 'o' or values[0] == 'g': + wvobj.name = values[1] + elif values[0] == 'mtllib': + if not self.mtl_source: + _obj_dir = os.path.abspath(os.path.dirname(self.source)) + self.mtl_source = os.path.join(_obj_dir, values[1]) + self.load_mtl() + elif values[0] == 'usemtl': + wvobj.mtl_name = values[1] + elif values[0] == 'v': + if faces_section: + # here we yield new mesh object + faces_section = False + yield wvobj + wvobj = WaveObject(self) + v = list(map(float, values[1:4])) + if self.swapyz: + v = v[0], v[2], v[1] + self.vertices.append(v) + elif values[0] == 'vn': + v = list(map(float, values[1:4])) + if self.swapyz: + v = v[0], v[2], v[1] + self.normals.append(v) + elif values[0] == 'vt': + self.texcoords.append(list(map(float, values[1:3]))) + elif values[0] == 'f': + if not faces_section: + faces_section = True + face = [] + texcoords = [] + norms = [] + for v in values[1:]: + w = v.split('/') + face.append(int(w[0])) + if len(w) >= 2 and len(w[1]) > 0: + texcoords.append(int(w[1])) + else: + texcoords.append(-1) + if len(w) >= 3 and len(w[2]) > 0: + norms.append(int(w[2])) + else: + norms.append(-1) + wvobj.faces.append((face, norms, texcoords)) + yield wvobj + + def load(self, source, **kw): + self.swapyz = kw.pop("swapyz", False) + return super(OBJLoader, self).load(source, **kw) + + def parse(self): + + obj = Object3D() + + for wvobj in self._load_meshes(): + obj.add(wvobj.convert_to_mesh()) + + return obj + + +class OBJMTLLoader(OBJLoader): + """ This subclass of Wafefront format files loader + which allows to use custom MTL file, but not the one is + defined in .obj file + """ + + def load(self, source, mtl_source, **kw): + self.mtl_source = mtl_source + self.load_mtl() + return super(OBJMTLLoader, self).load(source, **kw) diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/materials.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/materials.py new file mode 100644 index 00000000..14e9f1fd --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/materials.py @@ -0,0 +1,66 @@ +""" +The MIT License (MIT) + +Copyright (c) 2013 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +from kivy.graphics import ChangeState + +# Map for material attributes to shader +# uniform variables +MATERIAL_TO_SHADER_MAP = { + "color": "Ka", + "transparency": "Tr", + "diffuse": "Kd", + "specular": "Ks", + "shininess": "Ns", # specular coefficient + } + + +def set_attribute_to_uniform(attr_name, uniform_var): + MATERIAL_TO_SHADER_MAP[attr_name] = uniform_var + + +class Material(ChangeState): + + def __init__(self, map=None, transparency=1.0, color=(1, 1, 1), + diffuse=(0, 0, 0), specular=(0, 0, 0), + shininess=10.0, **kwargs): + self.map = map + super(Material, self).__init__() + transparency = float(transparency) + color = tuple(float(c) for c in color) + diffuse = tuple(float(d) for d in diffuse) + specular = tuple(float(s) for s in specular) + shininess = float(shininess) + + # set attribute from locals + for k, v in locals().items(): + setattr(self, k, v) + + def __setattr__(self, k, v): + if k in MATERIAL_TO_SHADER_MAP: + uniform_var = MATERIAL_TO_SHADER_MAP[k] + self.changes[uniform_var] = v + else: + if type(v) in [float, int, str, list]: + self.changes[k] = v + super(Material, self).__setattr__(k, v) diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/math/__init__.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/math/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/math/vectors.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/math/vectors.py new file mode 100644 index 00000000..7cf08bd2 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/math/vectors.py @@ -0,0 +1,249 @@ +""" +The MIT License (MIT) + +Copyright (c) 2013 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +__all__ = ('Vector2', 'Vector3', 'Vector4', ) + +import math + +from copy import copy + + +class BaseVector(list): + """ + BaseVector is actually 4D vector for optimization + """ + + _d = 4 # dimension size + _indeces = [0, 1, 2, 3] + _null = [0, 0, 0, 0] + _coords = {'x': 0, 'y': 1, 'z': 2, 'v': 3} + + def __init__(self, *largs): + if len(largs) == 1: + if len(largs[0]) == self._d: + super(BaseVector, self).__init__(largs[0]) + else: + raise Exception('Invalid vector') + else: + if len(largs) == self._d: + super(BaseVector, self).__init__(largs) + else: + raise Exception('Invalid vector') + self._change_cb = None + + def set_change_cb(self, cb): + self._change_cb = cb + + def set_vector(self, v): + for i in self._indeces: + self[i] = v[i] + + def __add__(self, other): + res = copy(self._null) + + if isinstance(other, BaseVector): + for i in self._indeces: + res[i] = self[i] + other[i] + else: + for i in self._indeces: + res[i] = self[i] + other + return self.__class__(res) + + def add(self, other): + self.set_vector(self + other) + + @classmethod + def add_vectors(cls, first, second): + return first + second + + def __sub__(self, other): + res = copy(self._null) + if isinstance(other, BaseVector): + for i in self._indeces: + res[i] = self[i] - other[i] + else: + for i in self._indeces: + res[i] = self[i] - other + return self.__class__(res) + + def sub(self, other): + self.set_vector(self - other) + + @classmethod + def sub_vectors(cls, first, second): + return first - second + + def __mul__(self, other): + res = copy(self._null) + if isinstance(other, BaseVector): + for i in self._indeces: + res[i] = self[i] * float(other[i]) + else: + for i in self._indeces: + res[i] = self[i] * float(other) + return self.__class__(res) + + def multiply(self, other): + self.set_vector(self * other) + + @classmethod + def multiply_vectors(cls, first, second): + return first * second + + def __div__(self, other): + res = copy(self._null) + if isinstance(other, BaseVector): + for i in self._indeces: + res[i] = self[i] / float(other[i]) + else: + for i in self._indeces: + res[i] = self[i] / float(other) + return self.__class__(res) + + def divide(self, other): + self.set_vector(self / other) + + @classmethod + def divide_vectors(cls, first, second): + return first / second + + def min(self, v): + for i in self._indeces: + if v[i] < self[i]: + self[i] = v[i] + + def max(self, v): + for i in self._indeces: + if v[i] > self[i]: + self[i] = v[i] + + def clamp(self, vmin, vmax): + """ This function assumes min < max, if this assumption isn't true + it will not operate correctly + """ + for i in self._indeces: + if self[i] < vmin[i]: + self[i] = vmin[i] + elif self[i] > vmax[i]: + self[i] = vmax[i] + + def negate(self): + self.set_vector(self * -1) + + def dot(self, v): + dot = 0 + for i in self._indeces: + dot += v[i] * self[i] + return dot + + def length_sq(self): + length_sq = 0 + for i in self._indeces: + length_sq += self[i] * self[i] + return length_sq + + def length(self): + return math.sqrt(self.length_sq()) + + def length_manhattan(self): + res = 0 + for i in self._indeces: + res += math.fabs(self[i]) + return res + + def normalize(self): + return self / self.length() + + def lerp(self, v, alpha): + for i in self._indeces: + self[i] += (v[i] - self[i]) * alpha + + return self + + def clamp_scalar(self, n, min, max): + if n < min: + return min + if n > max: + return max + return n + + def angle(self, v): + theta = self.dot(v) / (self.length() * v.length()) + + return math.acos(self.clamp_scalar(theta, -1, 1)) + + angle_to = angle # alias for three.js back capability + + def distance(self, v): + d = self - v + return d.length() + + distance_to = distance + + def distance_to_squared(self, v): + d = self - v + return d.length_sq() + + def __getattr__(self, k): + if k in self._coords: + return self[self._coords[k]] + else: + raise AttributeError + + def __setattr__(self, k, v): + if k in self._coords: + if type(v) == int or type(v) == float: + self[self._coords[k]] = v + if self._change_cb: + self._change_cb(k, v) + super(BaseVector, self).__setattr__(k, v) + + +class Vector4(BaseVector): + pass + + +class Vector3(BaseVector): + _d = 3 + _indeces = [0, 1, 2] + _null = [0, 0, 0] + _coords = {'x': 0, 'y': 1, 'z': 2} + + def cross(self, v): + t = copy(self) + + self[0] = t[1] * v[2] - t[2] * v[1] + self[1] = t[2] * v[0] - t[0] * v[2] + self[2] = t[0] * v[1] - t[1] * v[0] + + @classmethod + def cross_vectors(cls): + pass + + +class Vector2(BaseVector): + _d = 2 + _indeces = [0, 1] + _null = [0, 0] + _coords = {'x': 0, 'y': 1} diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/objects/__init__.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/objects/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/objects/mesh.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/objects/mesh.py new file mode 100644 index 00000000..6944de75 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/objects/mesh.py @@ -0,0 +1,76 @@ +""" +The MIT License (MIT) + +Copyright (c) 2013 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +from pdb import set_trace as T +from kivy.graphics import Mesh as KivyMesh +from kivy3 import Vector3 +from kivy3.core.object3d import Object3D + +DEFAULT_VERTEX_FORMAT = [(b'v_pos', 3, 'float'), + (b'v_normal', 3, 'float'), + (b'v_tc0', 2, 'float')] + + +class Mesh(Object3D): + + def __init__(self, geometry, material, **kw): + super(Mesh, self).__init__(**kw) + self.geometry = geometry + self.material = material + self.mtl = self.material # shortcut for material property + self.vertex_format = kw.pop("vertex_format", DEFAULT_VERTEX_FORMAT) + self.create_mesh() + + def create_mesh(self): + """ Create real mesh object from the geometry and material """ + vertices = [] + indices = [] + idx = 0 + for face in self.geometry.faces: + for i, k in enumerate(['a', 'b', 'c']): + v_idx = getattr(face, k) + vertex = self.geometry.vertices[v_idx] + vertices.extend(vertex) + try: + normal = face.vertex_normals[i] + except IndexError: + normal = Vector3([0, 0, 0]) + vertices.extend(normal) + try: + tex_coords = self.geometry.face_vertex_uvs[0][idx] + vertices.extend(tex_coords) + except IndexError: + vertices.extend([0, 0]) + indices.append(idx) + idx += 1 + kw = {"vertices": vertices, "indices": indices, + "fmt": self.vertex_format, "mode": "triangles" + } + if self.material.map: + kw["texture"] = self.material.map + + self._mesh = KivyMesh(**kw) + + def custom_instructions(self): + yield self.material + yield self._mesh diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/renderer.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/renderer.py new file mode 100644 index 00000000..2adee498 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/renderer.py @@ -0,0 +1,124 @@ + +""" +The MIT License (MIT) + +Copyright (c) 2013 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +""" +Renderer class +============= + +Unlike of THREE.js we may provide only one renderer which is the +Kivy widget and uses Kivy canvas and FBO concept for drawing graphics. +You may use this class as usual widget and place it wherever you need +in your application +""" + +import os +import kivy3 + + +from kivy.uix.widget import Widget +from kivy.clock import Clock +from kivy.graphics.fbo import Fbo +from kivy.graphics.instructions import InstructionGroup +from kivy.graphics.opengl import glEnable, glDisable, GL_DEPTH_TEST +from kivy.graphics import Callback, PushMatrix, PopMatrix, \ + Rectangle, Canvas, UpdateNormalMatrix + + +kivy3_path = os.path.abspath(os.path.dirname(kivy3.__file__)) + + +class RendererError(Exception): + pass + + +class Renderer(Widget): + + def __init__(self, **kw): + self.shader_file = kw.pop("shader_file", None) + self.canvas = Canvas() + super(Renderer, self).__init__(**kw) + + with self.canvas: + self._viewport = Rectangle(size=self.size, pos=self.pos) + self.fbo = Fbo(size=self.size, + with_depthbuffer=True, compute_normal_mat=True, + clear_color=(0., 0., 0., 0.)) + self._config_fbo() + self.texture = self.fbo.texture + self.camera = None + self.scene = None + + def _config_fbo(self): + # set shader file here + self.fbo.shader.source = self.shader_file or \ + os.path.join(kivy3_path, "default.glsl") + with self.fbo: + Callback(self._setup_gl_context) + PushMatrix() + # instructions set for all instructions + self._instructions = InstructionGroup() + PopMatrix() + Callback(self._reset_gl_context) + + def _setup_gl_context(self, *args): + glEnable(GL_DEPTH_TEST) + self.fbo.clear_buffer() + + def _reset_gl_context(self, *args): + glDisable(GL_DEPTH_TEST) + + def render(self, scene, camera): + self.scene = scene + self.camera = camera + self.camera.bind_to(self) + self._instructions.add(scene.as_instructions()) + Clock.schedule_once(self._update_matrices, -1) + + def add(self, obj): + self._instructions.add(obj.as_instructions()) + + def on_size(self, instance, value): + self.fbo.size = value + self._viewport.texture = self.fbo.texture + self._viewport.size = value + self._viewport.pos = self.pos + self._update_matrices() + + def on_pos(self, instance, value): + self._viewport.pos = self.pos + self._update_matrices() + + def on_texture(self, instance, value): + self._viewport.texture = value + + def _update_matrices(self, dt=None): + if self.camera: + self.fbo['projection_mat'] = self.camera.projection_matrix + self.fbo['modelview_mat'] = self.camera.modelview_matrix + else: + raise RendererError("Camera is not defined for renderer") + + def set_clear_color(self, color): + self.fbo.clear_color = color diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/scenes/__init__.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/scenes/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/jsuarez/extra/embyr_deprecated/embyr/kivy3/scenes/scene.py b/jsuarez/extra/embyr_deprecated/embyr/kivy3/scenes/scene.py new file mode 100644 index 00000000..b6fbeacf --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/kivy3/scenes/scene.py @@ -0,0 +1,38 @@ + +""" +The MIT License (MIT) + +Copyright (c) 2013 Niko Skrypnik + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +""" + +from kivy.graphics import Callback, UpdateNormalMatrix +from kivy.graphics.opengl import glEnable, glDisable, GL_DEPTH_TEST +from kivy3.core.object3d import Object3D + + +class Scene(Object3D): + """ Scene object """ + + def as_instructions(self): + if not self._instructions.children: + for child in self.get_children_instructions(): + self._instructions.add(child) + return self._instructions diff --git a/jsuarez/extra/embyr_deprecated/embyr/modules.py b/jsuarez/extra/embyr_deprecated/embyr/modules.py new file mode 100644 index 00000000..588c6666 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/modules.py @@ -0,0 +1,691 @@ +from pdb import set_trace as T +import numpy as np +import time +from scipy.misc import imresize, imsave +from enum import Enum + +import pygame +from pygame import Surface + +from forge.embyr import embyr +from forge.embyr import utils as renderutils +from forge.embyr import render +from forge.embyr.EnvViewport3D import EnvViewport3D +from forge.embyr.texture import TextureInitializer +from kivy.graphics.texture import Texture as kvTex +from forge.blade.lib.enums import Neon, Color256, Defaults +from forge.blade.action.v2 import Attack +from forge.blade.action import action +import kivy + +names = open('resource/ents.txt').read().splitlines() +NANIM = 8 +class Map(embyr.Panel): + def __init__(self, realm, textures, + canvasSize, tileSz, mipLevels, iso, **kwargs): + super().__init__(canvasSize, **kwargs) + self.realm, self.textures, self.iso = realm, textures, iso + self.tileSz, self.mipLevels = tileSz, mipLevels + + def render(self, trans): + self.reset() + envCrop, txSz = embyr.mapCrop(self, + self.realm.world.env.inds(), self.tileSz, trans) + txSz = embyr.mipKey(txSz, self.mipLevels) + embyr.renderMap(self, envCrop, txSz, self.textures.material[txSz], self.iso) + self.flip() + return self.surf + + def refresh(self, trans, iso): + self.iso = iso + return self.render(trans) + +class SpriteGroup: + def __init__(self, tex, fonts): + self.tex, self.fonts = tex, fonts + self.sprites = [] + + def add(self, ent): + sprite = Sprite(ent) + self.sprites.append(sprite) + + def remove(self, ent): + self.sprites.remove(ent) + + def update(self): + for e in self.sprites: + e.update() + + def render(self, screen, offsets, txSz, iso): + for e in self.sprites: + #crop + e.render(screen, offsets, self.tex, self.fonts, txSz, iso) + +class Sprite: + def __init__(self, ent): + self.ent = ent + self.frame = 0 + + #self.pos = ent.lastPos + #self.newPos = ent.pos + + self.lastPos = ent.lastPos + if not ent.alive: + self.lastPos = ent.pos + r, c = self.lastPos + self.posAnim = self.lastPos + rNew, cNew = self.ent.pos + + self.rOff, self.cOff = np.sign(rNew-r), np.sign(cNew-c) + #self.targ = ent.attack.args + nNames = len(names) + self.entName = names[int(ent.entID)%nNames] + self.nameColor = Neon.color12()[int(ent.entID) % 12] + + def update(self): + self.frame += 1.0/NANIM + + r, c = self.lastPos + rAnim = r + self.frame * self.rOff + cAnim = c + self.frame * self.cOff + self.posAnim = (rAnim, cAnim) + + def render(self, screen, offsets, tex, fonts, txSz, iso): + self.targ = self.ent.targ + render.renderSprite(screen, self, offsets, tex, fonts, txSz, iso) + +class Ent(embyr.Panel): + def __init__(self, realm, fonts, textures, + canvasSize, tileSz, mipLevels, iso, **kwargs): + super().__init__(canvasSize, **kwargs) + self.realm, self.fonts, self.textures = realm, fonts, textures + self.tileSz, self.mipLevels, self.iso = tileSz, mipLevels, iso + + def render(self, trans, keyframe): + tiles = self.realm.world.env.tiles + if keyframe: + self.makeSprites(tiles) + self.sprites.update() + self.renderEnts(trans) + return self.surf + + def renderEnts(self, trans): + self.reset() + R, C = self.realm.world.env.shape + offs, txSz = embyr.offsets(self, R, C, self.tileSz, trans) + txSz = embyr.mipKey(txSz, self.mipLevels) + minH, _, minW, _ = offs + self.sprites.render(self, (minH, minW), txSz, self.iso) + return self.surf + + def makeSprites(self, tiles): + self.sprites = SpriteGroup(self.textures, self.fonts) + for tile in tiles.ravel(): + for e in tile.ents.values(): + self.sprites.add(e) + + def refresh(self, trans, iso): + self.iso = iso + return self.render(None, trans, keyframe=False) + +class EnvViewport2D(embyr.Container): + def __init__(self, realm, textures, + mipLevels, fonts, canvasSize, iso, **kwargs): + super().__init__(canvasSize, **kwargs) + self.textures, self.mipLevels, self.fonts = textures, mipLevels, fonts + self.tileSz, self.iso = textures.tileSz, iso + + self.map = Map(realm, self.textures, + canvasSize, self.tileSz, mipLevels, iso) + self.ent = Ent(realm, fonts, self.textures, canvasSize, + self.tileSz, mipLevels, iso, key=Neon.BLACK.rgb) + self.screenshot_idx = 0 + + def render(self, trans, keyframe=True): + mmap = self.map.render(trans) + ent = self.ent.render(trans, keyframe) + self.reset() + + self.blit(mmap, (0, 0)) + self.blit(ent, (0, 0)) + + self.flip() + return self.surf + + def refresh(self, trans, iso): + self.iso = iso + mmap = self.map.refresh(trans, self.iso) + ent = self.ent.refresh(trans, self.iso) + + self.blit(mmap, (0, 0)) + self.blit(ent, (0, 0)) + + self.flip() + return self.surf + + def leftScreenshot(self): + print("Saved left screenshot") + im = pygame.surfarray.array3d(self.surf) + im = im.transpose((1, 0, 2)) + imsave("env{}.png".format(self.screenshot_idx), im) + self.screenshot_idx += 1 + + +class DatViewport(embyr.Panel): + def __init__(self, args, **kwargs): + canvasSize = kwargs['size'] + self.conf = kwargs.pop('conf') + super().__init__(args, **kwargs) + self.W, self.H = canvasSize + self.graphHeigh = 128 + self.exchangeWidth = 128 + self.screenshot_idx = 0 + + self.idx = 0 + self.nIdx = 3 + self.counts = Counts(args, size=canvasSize) + self.deps = Deps(args, size=canvasSize) + self.action = Action(args, size=canvasSize) + self.vals = Vals(args, size=canvasSize) + self.help = Help(args, size=canvasSize) + self.transparent = False + self.charIdx = {'h':0, 'v':1, 'c':2, 't':3, 'f':4, + 'w':5, 'q':6, 'a':7, '1':8, '2':9, + '3':10, 'd': 11, ']':12} + self.funcIdx = {} + self.funcIdx[self.charIdx['h']] = self.renderHelp + self.funcIdx[self.charIdx['v']] = self.renderDefaults + self.funcIdx[self.charIdx['c']] = self.renderCounts + self.funcIdx[self.charIdx['f']] = self.renderFood + self.funcIdx[self.charIdx['w']] = self.renderWater + self.funcIdx[self.charIdx['q']] = self.renderHalf + self.funcIdx[self.charIdx['d']] = self.renderDeps + self.funcIdx[self.charIdx['a']] = self.renderAction + self.funcIdx[self.charIdx['1']] = self.renderMelee + self.funcIdx[self.charIdx['2']] = self.renderRange + self.funcIdx[self.charIdx['3']] = self.renderMage + self.funcIdx[self.charIdx[']']] = self.renderRight + + def gridApply(self, damageF): + rets = [] + for r in range(self.conf.R): + for c in range(self.conf.C): + dist = Attack.l1((r, c), self.conf.SPAWN) + rets.append(((r,c), damageF(dist))) + return rets + + def renderMelee(self, update=False): + damageF = self.conf.MELEEDAMAGE + vals = self.gridApply(damageF) + self.renderVals(env, vals, update=update) + + def renderRange(self, update=False): + damageF = self.conf.RANGEDAMAGE + vals = self.gridApply(damageF) + self.renderVals(env, vals, update=update) + + def renderMage(self, update=False): + damageF = self.conf.MAGEDAMAGE + vals = self.gridApply(damageF) + self.renderVals(env, vals, update=update) + + def renderRight(self, update=False): + print("Saved right screenshot") + surf = self.dat + im = pygame.surfarray.array3d(surf) + im = im.transpose((1, 0, 2)) + imsave("dat{}.png".format(self.screenshot_idx), im) + + self.screenshot_idx += 1 + + def renderHelp(self, update=False): + if update: + self.dat = self.help.render() + + def renderAction(self, update=False): + #if update: + self.dat = self.action.render() + + def renderFood(self, update=False): + if update: + self.val = valF(0, 36) + self.renderVals(env, self.val, update) + + def renderWater(self, update=False): + if update: + self.val = valF(36, 0) + self.renderVals(env, self.val, update) + + def renderHalf(self, update=False): + if update: + self.val = valF(18, 18) + self.renderVals(env, self.val, update) + + def renderEmpty(self, update=False): + if update: + self.val = valF(0, 0) + self.renderVals(env, self.val, update) + + def renderDefaults(self, update=False): + self.renderVals(update) + + def renderVals(self, update=False): + self.dat = self.vals.render(self.trans, update) + self.dat.set_alpha(255) + if self.transparent: + self.dat.set_alpha(0) + env = self.env.copy() + env.blit(self.dat, (0, 0), + special_flags=pygame.BLEND_RGB_ADD) + #env.blit(self.dat, (0, 0)) + self.dat = env + + def renderDeps(self, update=False): + if update: + self.dat = self.deps.render() + + def renderCounts(self, update=False): + self.dat = self.counts.render(self.trans, update) + + def render(self, env, trans, update=False): + #self.ents, self.valF, self.trans = ents, valF, trans + self.env = env + self.reset() + self.trans = trans + self.funcIdx[self.idx](update) + self.surf.blit(self.dat, (0, 0)) + self.renderBorder() + self.flip() + + def key(self, c): + old_idx = self.idx + if c == 'o': + self.toggle() + elif c == 't': + self.transparent = not self.transparent + elif c in self.charIdx: + self.idx = self.charIdx[c] + else: + return + + if old_idx == self.charIdx[']'] and self.idx == old_idx: + return + + self.funcIdx[self.idx](update=True) + + if c == ']': + self.idx = old_idx + + def toggle(self): + self.idx = (self.idx + 1) % self.nIdx + +class Title(embyr.ECanvas): + def __init__(self, args, **kwargs): + super().__init__(args, **kwargs) + godsword = embyr.Texture('resource/Splash/ags.png', mask=Neon.MASK.rgb) + w, h = godsword.get_width(), godsword.get_height() + scaled = int(w*self.H/h), int(self.H) + self.godsword = pygame.transform.scale(godsword, scaled) + + def render(self): + self.reset() + self.blit(self.godsword, (0, 0)) + return self.surf + +class FPS(embyr.ECanvas): + def __init__(self, args, **kwargs): + super().__init__(args, **kwargs) + self.fpsTracker = embyr.FPSTracker() + + def render(self): + self.reset() + self.fpsTracker.update() + fps = self.fonts.Large.render('FPS: '+self.fpsTracker.fps, 1, Neon.GOLD.rgb) + self.surf.blit(fps, (50,10)) + return self.surf + +class Corner(embyr.Panel): + def __init__(self, args, **kwargs): + super().__init__(args, **kwargs) + self.title = Title(args, size=(256, 64)) + self.fps = FPS(args, size=(256, 64)) + + def render(self): + self.reset() + title = self.title.render() + fps = self.fps.render() + + self.surf.blit(title, (0, 0)) + self.surf.blit(fps, (0, 64)) + self.flip() + +class LeftSidebar(embyr.Panel): + def render(self, dt): + self.reset() + stats = self.realm.world.stats + render.renderExchange(self, stats, self.fonts, self.W, self.H) + self.flip() + +class Histogram(embyr.ECanvas): + def render(self, dat): + self.reset() + render.renderHist(self, dat, self.fonts, self.W, self.H) + return self.surf + +class Graph(embyr.ECanvas): + def render(self, dat): + self.reset() + render.renderGraph(self, dat, self.fonts, self.W, self.H, color=Neon.GOLD.rgb) + return self.surf + +class BottomSidebar(embyr.Panel): + def __init__(self, args, **kwargs): + super().__init__(args, **kwargs) + self.hist = Histogram(args, size=(self.W, self.H//2)) + self.graph = Graph(args, size=(self.W, self.H//2)) + + def render(self): + self.fill(Neon.BLACK.rgb) + stats = self.realm.world.stats + + #Render histograms of time alive, levels, and best agents + lifespans = [[e.timeAlive] for e in stats.pcs] + #timesAlive = Render.histogram(timesAlive) + #combatStats = [(e.melee, e.ranged, e.defense) for e in blocks] + + hist = self.hist.render(lifespans) + graph = self.graph.render(stats.numEntities[-self.W:]) + + self.surf.blit(hist, (0, 0)) + self.surf.blit(graph, (0, self.H//2)) + + #Render + #Render.renderHist(self.canvas, self.fonts, lifespans, self.W, self.H, 0, self.H//2) + #renderHist(screen, fonts, combatStats, 0, H+h, W, h) + #Render.renderGraph(self.canvas, stats.numEntities[-self.W:], self.fonts, self.W, self.H, color=Color.GOLD) + + # Render.renderGraphs(self.canvas, stats, self.fonts, + # self.W, self.H) + self.flip() + +class Counts(embyr.ECanvas): + def __init__(self, args, **kwargs): + super().__init__(args, **kwargs) + self.R, self.C = self.realm.world.env.tiles.shape + self.nColors = self.realm.world.env.nCounts + self.tileSz = 16 + + def render(self, trans, update): + env = self.realm.world.env + tiles = env.tiles + R, C = tiles.shape + counts = np.array([tile.counts for tile in tiles.ravel()]) + nCounts = counts.shape[1] + counts = counts.reshape(R, C, counts.shape[1]) + counts = [counts[:, :, i] for i in range(counts.shape[2])] + if nCounts <= 12: + colors = Neon.color12() + else: + colors = Color256.colors[0::256//nCounts] + for idx, count in enumerate(counts): + counts[idx] = 20*counts[idx][:, :, np.newaxis]*np.array(colors[idx].value)/255.0 + sumCounts = sum(counts) + + R, C, _ = sumCounts.shape + counts = np.clip(sumCounts, 0, 255).astype(np.uint8) + counts = np.clip(counts, 0, 255).astype(np.uint8) + + valCrop, txSz = embyr.mapCrop(self, counts, + self.tileSz, trans) + txSz = embyr.mipKey(txSz, self.mipLevels) + embyr.renderMap(self, valCrop, txSz) + return self.surf + +class Deps(embyr.ECanvas): + def __init__(self, args, **kwargs): + super().__init__(args, **kwargs) + self.R, self.C = self.realm.world.env.tiles.shape + self.tileSz = 16 + + def render(self): + anns = self.realm.sword.anns + deps = [ann.visDeps() for ann in anns] + grids = [] + for dep in deps: + vals = dep + vList = [e[1] for e in vals] + vMean, vStd = np.mean(vList), np.std(vList)+1e-3 + nStd, nTol = 4.0, 0.5 + grayVal = int(255 / nStd * nTol) + grid = np.zeros((16, 16, 3)) + for v in vals: + pos, mat = v + r, c = pos + mat = (mat - vMean) / vStd + color = np.clip(mat, -nStd, nStd) + color = int(color * 255.0 / nStd) + if color > 0: + color = (0, color, 128) + else: + color = (-color, 0, 128) + grid[r, c] = color + grids.append(grid) + grids += 15*[0*grids[0]] + grids = grids[:16] + grids1 = np.vstack(grids[:4]) + grids2 = np.vstack(grids[4:8]) + grids3 = np.vstack(grids[8:12]) + grids4 = np.vstack(grids[12:16]) + grids = np.hstack((grids1, grids2, grids3, grids4)) + embyr.renderMap(self, grids, self.tileSz) + return self.surf + +class Action(embyr.ECanvas): + def __init__(self, args, **kwargs): + super().__init__(args, **kwargs) + self.makeMaps() + self.tileSz = 16 + self.RED = np.array(Defaults.RED) + self.GREEN = np.array(Defaults.GREEN) + self.BLUE = np.array(Defaults.BLUE) + + def render(self): + ents = [e.server for e in self.realm.desciples.values()] + self.updateMaps(ents) + maps = self.maps + maps = maps + 15*[0*maps[0]] + maps = maps[:16] + mapv1 = np.vstack(maps[:4]) + mapv2 = np.vstack(maps[4:8]) + mapv3 = np.vstack(maps[8:12]) + mapv4 = np.vstack(maps[12:16]) + maps = np.hstack((mapv1, mapv2, mapv3, mapv4)) + + maps = maps.astype(np.uint8) + #maps = pygame.pixelcopy.make_surface(maps) + #valCrop, txSz = embyr.mapCrop(self, maps, + # self.tileSz, trans) + #txSz = embyr.mipKey(txSz, self.mipLevels) + embyr.renderMap(self, maps, int(1.7*self.tileSz)) + return self.surf + + def updateMaps(self, ents): + for ent in ents: + attack = ent.attack.action + #targ = ent.attack.args + targ = ent.targ + if targ is None or targ.damage is None: + continue + idx = ent.colorInd + if idx >= 16: + continue + entr, entc = ent.attkPos + targr, targc = ent.targPos + r = self.MAPCENT + targr - entr + c = self.MAPCENT + targc - entc + + if issubclass(attack, action.Melee): + self.counts[idx][r, c, 0] += 1 + elif issubclass(attack, action.Range): + self.counts[idx][r, c, 1] += 1 + elif issubclass(attack, action.Mage): + self.counts[idx][r, c, 2] += 1 + + rgb = self.counts[idx][r, c] + normSum = np.sum(rgb) + redVal = self.RED * rgb[0] / normSum + greenVal = self.GREEN * rgb[1] / normSum + blueVal = self.BLUE * rgb[2] / normSum + color = (redVal + greenVal + blueVal).astype(np.uint8) + self.maps[idx][r, c] = color + #colorIdx = np.argmax(self.counts[idx][r, c]) + #self.maps[idx][r, c] = self.colors[colorIdx] + + def makeMaps(self): + self.NMAPS = 16#self.config.NPOP + self.MAPCENT = 4 #self.config.STIM + self.MAPSZ = 2*self.MAPCENT + 1 + + self.maps = [] + self.counts = [] + for idx in range(self.NMAPS): + self.maps.append(np.zeros((self.MAPSZ, self.MAPSZ, 3))) + self.counts.append(np.zeros((self.MAPSZ, self.MAPSZ, 3))) + + + def renderEnts(self, trans): + self.reset() + R, C = self.env.shape + offs, txSz = embyr.offsets(self, R, C, self.tileSz, trans) + txSz = embyr.mipKey(txSz, self.mipLevels) + minH, _, minW, _ = offs + self.sprites.render(self, (minH, minW), txSz, self.iso) + return self.surf + + def refresh(self, trans, iso): + self.iso = iso + return self.render(None, trans, keyframe=False) + +class Vals(embyr.ECanvas): + def __init__(self, args, **kwargs): + super().__init__(args, **kwargs) + self.R, self.C = self.realm.world.env.tiles.shape + self.vals = np.zeros((self.R, self.C), dtype=object) + self.mipLevels = self.textures.mipLevels + self.tileSz = 16 + + def update(self): + ann = self.realm.sword.anns[0] + vals = ann.visVals() + vList = [e[1] for e in vals] + vMean, vStd = np.mean(vList), np.std(vList) + nStd, nTol = 4.0, 0.5 + grayVal = int(255 / nStd * nTol) + for v in vals: + pos, mat = v + r, c = pos + mat = (mat - vMean) / vStd + color = np.clip(mat, -nStd, nStd) + color = int(color * 255.0 / nStd) + if color > 0: + color = (0, color, 128) + else: + color = (-color, 0, 128) + self.vals[r, c] = color + + def render(self, trans, update): + self.reset() + if update: + self.update() + + valCrop, txSz = embyr.mapCrop(self, self.vals, + self.tileSz, trans) + txSz = embyr.mipKey(txSz, self.mipLevels) + embyr.renderMap(self, valCrop, txSz) + return self.surf + +class Help(embyr.ECanvas): + def __init__(self, args, **kwargs): + super().__init__(args, **kwargs) + self.text = [ + 'Command List:', + ' Environment', + ' i: toggle isometric', + ' Overlay:', + ' o: cycle overlay', + ' h: help overlay', + ' c: count overlay', + ' v: value overlay', + ' t: toggle transparency', + ' w: value no water', + ' f: value no food', + ' q: value half food/water', + ' a: value no food/water', + ' r: screenshot the right side of screen', + ] + + def render(self): + self.reset() + offset, margin, pos = 64, 16, 0 + for item in self.text: + text = self.fonts.Huge.render(item, 1, Neon.GOLD.rgb) + self.blit(text, (margin, pos+margin)) + pos += offset + return self.surf + +from forge.embyr.embyr3D import Widget +class Canvas(Widget): + def __init__(self, size, root, realm, dims, conf, **kwargs): + super().__init__(size, **kwargs) + self.realm, self.iso = realm, False + self.W, self.H, self.side = dims + mipLevels = [8, 16, 32, 48, 64, 128] + + textures = TextureInitializer(16, mipLevels = mipLevels) + self.fonts = renderutils.Fonts('resource/Fonts/dragonslapper.ttf') + args = [self.canvas, realm, textures, self.fonts] + + self.envViewport = EnvViewport3D(root, realm, + pos=(256, 256), size=(4/9, 1)) + + self.left = LeftSidebar(args, + size=(self.side, self.H), pos=(0, self.side)) + self.bottom = BottomSidebar(args, + pos=(self.side, 0), size=(self.W, self.side)) + self.corner = Corner(args, + pos=(0, 0), size=(self.side, self.side)) + self.datViewport = DatViewport(args, conf=conf, + pos=(self.H+self.side, self.side), size=(self.H, self.H)) + + def render(self, dt): + self.envViewport.render(dt) + self.left.render(dt) + self.bottom.render() + self.corner.render() + self.datViewport.render(self.realm.env, None, update=True) + + def renderTitle(self): + godsword = renderutils.pgRead('resource/Splash/agsfull.png', mask=Neon.MASK.rgb) + w, h = godsword.get_width(), godsword.get_height() + W, H = self.size + ratio = W/w + w, h = int(ratio*w), int(ratio*h) + godsword = pygame.transform.scale(godsword, (w, h)) + hOff = int(H/2 - godsword.get_height()/2) + self.blit(godsword, (0, hOff)) + return self.surf + + def toggleEnv(self, trans): + sx, sy, tx, ty = trans + self.iso = not self.iso + env = self.envViewport.refresh(trans, self.iso) + self.surf.set_clip((self.side, 0, self.H, self.H)) + env = pygame.transform.scale(env, (sx, sy)) + self.surf.blit(env, (self.side+tx, ty)) + self.surf.set_clip(None) + + def key(self, c): + self.datViewport.key(c) + + def leftScreenshot(self): + self.envViewport.leftScreenshot() diff --git a/jsuarez/extra/embyr_deprecated/embyr/oldapp.py b/jsuarez/extra/embyr_deprecated/embyr/oldapp.py new file mode 100644 index 00000000..97d817c9 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/oldapp.py @@ -0,0 +1,136 @@ +from pdb import set_trace as T +import pygame, time +import numpy as np + +from forge.embyr import embyr +from forge.embyr.modules import * + +class Client(embyr.Container): + def __init__(self, view, size, realm, step, dims, nAnim, **kwargs): + super().__init__(size, **kwargs) + self.W, self.H, self.side = dims + self.realm, self.step = realm, step + self.view, self.nAnim = view, nAnim + + offset = 16 * 8 + self.x, self.y, self.xVol, self.yVol = -offset, offset, 0, 0 + self.zoom, self.zoomVol = 1, 0 + + self.count = 0 + self.frame = 0 + self.frames = [] + self.vals = [] + self.init = time.time() + + def setup(self): + surf = self.view.renderTitle() + self.blit(surf, (0, 0)) + self.frame += 1 + self.flip() + + def render(self, t): + if self.frame == 0: + return self.setup() + if self.frame == 1: + time.sleep(2.5) + self.update() + + def update(self): + self.writeFrame() + self.trans = self.renderOffsets(self.H, self.H) + keyframe = self.count == 0 + if keyframe: + self.step() + + self.surf = self.view.render(self.realm, self.trans, keyframe) + + self.count = (self.count + 1) % (self.nAnim+1) + self.blit(self.surf, (0,0)) + self.flip() + self.frame += 1 + + def writeFrame(self): + NFRAMES=1800 + return + if self.frame < NFRAMES: + print('Frame: ', len(self.frames)) + frame = pygame.surfarray.array3d(pygame.transform.rotate(self.surf, 90)) + frame = np.fliplr(frame) + #frame = frame[:1024, 256:256+1024] + frame = frame[:1024, 1024+256:1024+256+1024] + self.frames.append(frame) + #pygame.image.save(self.screen, 'resource/data/lrframe'+str(self.frame)+'.png') + elif self.frame == NFRAMES: + import imageio + print('Saving MP4...') + imageio.mimwrite('swordfrag.mp4', self.frames, fps = 30) + print('Saved') + + def clipZoom(self, zoom): + return np.clip(zoom, 1.0, 8.0) + + def renderOffsets(self, W, H): + #Scale + zoom = self.clipZoom(self.zoom + self.zoomVol) + scaleX, scaleY = int(W*zoom), int(H*zoom) + + #Translate + deltaX = self.x + self.xVol - scaleX/2 + W/2 + deltaY = -self.y - self.yVol - scaleY/2 + H/2 + return scaleX, scaleY, deltaX, deltaY + + def on_touch_down(self, touch): + self.xStart, self.yStart = touch.pos + + def on_touch_up(self, touch): + if touch.button == 'left': + self.xVol, self.yVol= 0, 0 + xEnd, yEnd = touch.pos + self.x += xEnd - self.xStart + self.y += yEnd - self.yStart + elif touch.button == 'right': + self.zoom = self.clipZoom(self.zoom + self.zoomVol) + self.zoomVol = 0 + + def on_touch_move(self, touch): + if touch.button == 'left': + xEnd, yEnd = touch.pos + self.xVol = xEnd - self.xStart + self.yVol = yEnd - self.yStart + elif touch.button == 'right': + xEnd, yEnd = touch.pos + delta = (xEnd - self.xStart)/2 - (yEnd - self.yStart)/2 + self.zoomVol = delta/100 + + def on_key_down(self, *args): + text = args[3] + if text == 'i': + #Toggle isometric + trans = self.renderOffsets(self.H, self.H) + self.view.toggleEnv(trans) + elif text == 'p': + T() + elif text == '[': + self.view.leftScreenshot() + else: + #Toggle overlay + self.view.key(text) + +class Application(embyr.Application): + def __init__(self, size, realm, step, conf): + super().__init__(size) + self.realm, self.step = realm, step + self.conf = conf + + def build(self): + W, H, side = self.W, self.H, 256 + self.appSize = (self.W, self.H) + + self.title = 'Projekt: Godsword' + dims = (self.W-side, self.H-side, side) + canvas = Canvas(self.appSize, self.realm, dims, self.conf) + self.client = Client(canvas, self.appSize, self.realm, + self.step, dims, NANIM) + + self.loop(self.client.render) + return self.client diff --git a/jsuarez/extra/embyr_deprecated/embyr/oldnewapi.py b/jsuarez/extra/embyr_deprecated/embyr/oldnewapi.py new file mode 100644 index 00000000..c231349b --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/oldnewapi.py @@ -0,0 +1,304 @@ +from pdb import set_trace as T +import numpy as np + +import os +import kivy3 +from kivy.app import App +from kivy3 import Scene, Renderer, PerspectiveCamera +from kivy3.loaders import OBJLoader, OBJMTLLoader +from kivy.uix.floatlayout import FloatLayout +from kivy.uix.widget import Widget +from kivy.config import Config +from kivy.graphics import opengl as gl +from kivy.graphics import Mesh as KivyMesh +from kivy3.core.object3d import Object3D +from kivy3.materials import Material +from kivy.core.image import Image +from copy import deepcopy +from embyr import Application +import pywavefront as pywave +import pytmx +import enums + +Config.set('input', 'mouse', 'mouse,multitouch_on_demand') + +def loadTiled(fPath): + import pytmx + tm = pytmx.TiledMap(fPath) + assert len(tm.layers) == 1 + layer = tm.layers[0] + W, H = layer.width, layer.height + tilemap = np.zeros((H, W), dtype=object) + for w, h, dat in layer.tiles(): + f = dat[0] + tex = f.split('/')[-1].split('.')[0] + tilemap[h, w] = tex + return tilemap + +class Pan(Widget): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.x, self.y, self.xVol, self.yVol = 0, 0, 0, 0 + self.zoom, self.zoomVol = 1, 0 + + def on_touch_down(self, touch): + self.xStart, self.yStart = touch.pos + + def on_touch_up(self, touch): + if touch.button == 'left': + self.xVol, self.yVol= 0, 0 + xEnd, yEnd = touch.pos + self.x += xEnd - self.xStart + self.y += yEnd - self.yStart + + def on_touch_move(self, touch): + if touch.button == 'left': + xEnd, yEnd = touch.pos + self.xVol = xEnd - self.xStart + self.yVol = yEnd - self.yStart + +class Rotate(Widget): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.x, self.y, self.xVol, self.yVol = 0, 0, 0, 0 + self.zoom, self.zoomVol = 1, 0 + + def on_touch_down(self, touch): + self.xStart, self.yStart = touch.pos + + def on_touch_up(self, touch): + if touch.button == 'right': + self.xVol, self.yVol= 0, 0 + xEnd, yEnd = touch.pos + self.x += xEnd - self.xStart + self.y += yEnd - self.yStart + bound = 3.14159*250 + self.y = int(np.clip(self.y, -bound, 0)) + + def on_touch_move(self, touch): + if touch.button == 'right': + xEnd, yEnd = touch.pos + self.xVol = xEnd - self.xStart + self.yVol = yEnd - self.yStart + +class Zoom(Widget): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.zoom, self.delta = 1, 0.2 + + def clipZoom(zoom, exprange=2): + return np.clip(zoom, 0.5**exprange, 2**exprange) + + def on_touch_down(self, touch): + print(touch.button) + if touch.button == 'scrollup': + self.zoom = Zoom.clipZoom(self.zoom + self.delta) + if touch.button == 'scrolldown': + self.zoom = Zoom.clipZoom(self.zoom - self.delta) + +class MyApp(Application): + def __init__(self, size): + super().__init__(size) + self.title = 'Projekt: Godsword' + self.pan = Pan() + self.rotate = Rotate() + self.zoom = Zoom() + + self.loader = OBJMTLLoader() + self.blocks = dict((mat.value.tex, mat.value) + for mat in enums.Material) + + self.vec = np.array([0, 0, 1]) + self.t = 0 + + ''' + super(Mesh, self).__init__(**kw) + self.geometry = geometry + self.material = material + self.mtl = self.material # shortcut for material property + self.vertex_format = kw.pop("vertex_format", DEFAULT_VERTEX_FORMAT) + self.create_mesh() + ''' + def cube(self, tile): + DEFAULT_VERTEX_FORMAT = [ + (b'v_tc0', 2, 'float'), + (b'v_normal', 3, 'float'), + (b'v_pos', 3, 'float')] + + + obj = self.blocks[tile].obj + T() + obj = pywave.Wavefront('tex/block.obj', collect_faces=True) + material = obj.materials['grass'] + cube = obj.meshes['Cube'] + + vertices = obj.vertices + faces = cube.faces + grass = obj.materials['grass'] + dirt = obj.materials['dirt'] + vertices = grass.vertices + dirt.vertices + #indices = np.array(faces).ravel().tolist() + indices = np.arange(36).astype(int).tolist() + #vertices = np.array(vertices).ravel().tolist() + + tex = Image('tex/grass.png').texture + mat = Material(tex) + kw = {"vertices": vertices, "indices": indices, + "fmt": DEFAULT_VERTEX_FORMAT, + "mode": "triangles", + 'texture':tex + } + #if self.material.map: + # kw["texture"] = self.material.map + + mesh = KivyMesh(**kw) + class Meshy(Object3D): + def __init__(self, mesh, material): + super().__init__() + self._mesh = mesh + self.material = material + self.mtl = material + self.vertex_format = DEFAULT_VERTEX_FORMAT + cube = Meshy(mesh, tex) + + #cube.material = orig.material + #cube.geometry = orig.geometry + orig._mesh = cube._mesh + orig.material = mat + cube = orig + + #cube = kivy3.Mesh([], material) + if tile == 'lava': + cube.pos.y = -0.5 + elif tile == 'stone': + cube.pos.y = 1 + elif tile == 'grass': + pass + elif tile == 'forest': + pass + elif tile == 'water': + cube.pos.y = -0.33 + + #cube.material.color = 0., .7, 0. # green + #cube.material.diffuse = 0., .7, 0. # green + return cube + + def makeMap(self): + tiles = loadTiled('../Projekt-Godsword/resource/maps/map1/map.tmx') + n, sz = tiles.shape[0], 1 + for i in range(n): + for j in range(n): + tile = tiles[i, j] + cube = self.cube(tile) + self.scene.add(cube) + + #NEVER set cube.pos directly. + #It won't do anything + cube.pos.x = i - n//2 + sz//2 + cube.pos.z = j - n//2 + sz//2 + + def glSetup(self): + #gl.glEnable(gl.GL_CULL_FACE) + #gl.glCullFace(gl.GL_BACK) + pass + + def build(self): + camera = PerspectiveCamera(30, 1, 1, 1000) + self.renderer = Renderer() + self.scene = Scene() + root = FloatLayout() + + obj = self.loader.load('tex/nn.obj', 'tex/nn.mtl') + self.scene.add(obj) + obj.pos.y = 1 + self.makeMap() + + self.renderer.render(self.scene, camera) + self.renderer.camera.look_at(0, 0, 0) + root.add_widget(self.renderer) + root.add_widget(self.pan) + root.add_widget(self.rotate) + root.add_widget(self.zoom) + self.renderer.bind(size=self._adjust_aspect) + self.loop(self.update) + return root + + def update(self, t): + print(1/t) + self.t += t + rad = 80 + sz = 500 + pi = 3.14159265 + + + r = self.rotate + x = r.x + r.xVol + y = r.y + r.yVol + + x = x / sz + y = y / sz + yclip = np.clip(y, -pi/2, 0) + + xz_x = np.cos(x) + xz_z = np.sin(x) + + yz_y = np.cos(yclip) + yz_z = np.sin(yclip) + + xz = np.array([ + [xz_x, 0, -xz_z], + [0 , 1, 0 ], + [xz_z, 0, xz_x]]) + + yz = np.array([ + [1, 0, 0 ], + [0, yz_y, -yz_z], + [0, yz_z, yz_y]]) + + #Find cylindrical xz plane rotation + rot_xz = rad*np.dot(xz, self.vec) + xx, _, zz = rot_xz + xz_vec = np.array([xx, 0, zz]) + + #Find spherical yz plane rotation + _, yy, zn = np.dot(yz, self.vec) + xz_norm = zn + + #For x, z: shrink to position of spherical rotation + #For y: use height from spherical rotation + vec = np.array([xx*xz_norm, -rad*yy, zz*xz_norm]) + + #Zoom factor + zoom = Zoom.clipZoom(self.zoom.zoom) + vec = vec * zoom + + p = self.pan + x = p.x + p.xVol + z = p.y + p.yVol + + x = 10*x / sz + z = -10*z / sz + + #Horizontal component + unit_y = np.array([0, 1, 0]) + xx, _, zz = np.cross(rot_xz, unit_y) + norm = np.sqrt(xx**2 + zz**2) + xh, zh = x*xx/norm, x*zz/norm + + #Depth component + xx, _, zz = -xz_vec + norm = np.sqrt(xx**2 + zz**2) + xd, zd = z*xx/norm, z*zz/norm + + xx, yy, zz = vec + vec = np.array([xx+xh+xd, yy, zz+zh+zd]) + self.renderer.camera.look_at([-xh-xd, 0, -zh-zd]) + self.renderer.camera.pos = vec.tolist() + + def _adjust_aspect(self, inst, val): + rsize = self.renderer.size + aspect = rsize[0] / float(rsize[1]) + self.renderer.camera.aspect = aspect + +if __name__ == '__main__': + MyApp((2048, 1024)).run() diff --git a/jsuarez/extra/embyr_deprecated/embyr/oldtrans.py b/jsuarez/extra/embyr_deprecated/embyr/oldtrans.py new file mode 100644 index 00000000..23c47d6c --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/oldtrans.py @@ -0,0 +1,153 @@ +from pdb import set_trace as T + +import numpy as np +import time +from kivy.uix.widget import Widget +from kivy.config import Config + +rad = 80 +sz = 500 +pi = 3.14159265 + +class Transform(Widget): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.zoom = Zoom() + self.pan = Pan('left', self.zoom) + self.rotate = Rotate('right', self.pan) + + self.t = 0 + self.vInit = np.array([0, 0, 1]) + + self.add_widget(self.pan) + self.add_widget(self.zoom) + self.add_widget(self.rotate) + self.button = None + + def update(self, dt): + self.t += dt + + rot_xz, xz_vec, xz_norm, vec = self.rotate(self.vInit) + vec = self.zoom(vec) + pos, lookvec = self.pan(vec, rot_xz, xz_vec) + + return lookvec, pos + +class TouchWidget(Widget): + def __init__(self, button, **kwargs): + super().__init__(**kwargs) + self.x, self.y, self.xVol, self.yVol = 0, 0, 0, 0 + self.button = button + self.reset = True + + #Currently broken due to race condition? + #def on_touch_down(self, touch): + # if touch.button == self.button: + # self.xStart, self.yStart = touch.pos + + def on_touch_up(self, touch): + if touch.button == self.button: + self.x += self.xVol + self.y += self.yVol + self.xVol, self.yVol= 0, 0 + self.reset = True + + def on_touch_move(self, touch): + if self.reset: + self.xStart, self.yStart = touch.pos + self.reset = False + + if touch.button == self.button: + xEnd, yEnd = touch.pos + self.xVol = xEnd - self.xStart + self.yVol = yEnd - self.yStart + +class Zoom(Widget): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.zoom, self.delta = 1, 0.2 + + def clip(self, zoom, exprange=2): + return np.clip(zoom, 0.5**exprange, 2**exprange) + + def on_touch_down(self, touch): + if touch.button == 'scrollup': + self.zoom = self.clip(self.zoom + self.delta) + elif touch.button == 'scrolldown': + self.zoom = self.clip(self.zoom - self.delta) + + def __call__(self, vec): + return vec * self.zoom + + +class Pan(TouchWidget): + def __init__(self, button, zoom, **kwargs): + super().__init__(button, **kwargs) + self.x, self.y, self.xVol, self.yVol = 0, 0, 0, 0 + self.zoom = zoom + + def on_touch_move(self, touch): + super().on_touch_move(touch) + self.xVol = int(self.xVol * self.zoom.zoom) + self.yVol = int(self.yVol * self.zoom.zoom) + + def __call__(self, vec, rot_xz, xz_vec): + x = 10 * (self.x + self.xVol) / sz + z = -10 * (self.y + self.yVol) / sz + + #Horizontal component + unit_y = np.array([0, 1, 0]) + xx, _, zz = np.cross(rot_xz, unit_y) + norm = np.sqrt(xx**2 + zz**2) + xh, zh = x*xx/norm, x*zz/norm + + #Depth component + xx, _, zz = -xz_vec + norm = np.sqrt(xx**2 + zz**2) + xd, zd = z*xx/norm, z*zz/norm + + xx, yy, zz = vec + vec = [xx+xh+xd, yy, zz+zh+zd] + lookvec = [-xh-xd, 0, -zh-zd] + return vec, lookvec + +class Rotate(TouchWidget): + def __init__(self, button, pan, **kwargs): + super().__init__(button, **kwargs) + self.pan = pan + + def __call__(self, vec): + x = (self.x + self.xVol) / sz + y = (self.y + self.yVol) / sz + yclip = np.clip(y, -pi/2, 0) + + xz_x = np.cos(x) + xz_z = np.sin(x) + + yz_y = np.cos(yclip) + yz_z = np.sin(yclip) + + xz = np.array([ + [xz_x, 0, -xz_z], + [0 , 1, 0 ], + [xz_z, 0, xz_x]]) + + yz = np.array([ + [1, 0, 0 ], + [0, yz_y, -yz_z], + [0, yz_z, yz_y]]) + + #Find cylindrical xz plane rotation + rot_xz = rad*np.dot(xz, vec) + xx, _, zz = rot_xz + xz_vec = np.array([xx, 0, zz]) + + #Find spherical yz plane rotation + _, yy, zn = np.dot(yz, vec) + xz_norm = zn + + #For x, z: shrink to position of spherical rotation + #For y: use height from spherical rotation + vec = np.array([xx*xz_norm, -rad*yy, zz*xz_norm]) + return rot_xz, xz_vec, xz_norm, vec + diff --git a/jsuarez/extra/embyr_deprecated/embyr/render.py b/jsuarez/extra/embyr_deprecated/embyr/render.py new file mode 100644 index 00000000..8e172d39 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/render.py @@ -0,0 +1,243 @@ +#Main renderer. Loads all textures, takes render information from +#the world, applies texturing for env/entities/actions/additional +#statistic visuals + +from pdb import set_trace as T +import pygame +import sys +import time + +import numpy as np +from scipy.misc import imread +from enum import Enum + +from forge.embyr import embyr +from forge.embyr import utils as renderutils +from forge.blade.action import action +from forge.blade.action import v2 +from forge.blade.lib.enums import Neon + +def renderEnts(screen, ent, fonts, entTx, atnTx, txSz, iso): + H, W = ent.shape + for h in range(H): + for w in range(W): + if ent[h, w] is None: + continue + for e in ent[h, w]: + renderSprite(screen, e, (h, w), fonts, entTx, + atnTx, txSz, iso) + +def renderVals(screen, vals, mipLevels, txSz): + H, W = vals.shape + for h in range(H): + for w in range(W): + if vals[h, w] is 0: + continue + v = vals[h, w] + mask = np.array(mipLevels) >= txSz + key = np.array(mipLevels)[np.argmax(mask)] + ww, hh = cartCoords(w, h, key) + screen.rect(v, (ww, hh, key, key)) + +def renderSprite(screen, sprite, offs, tex, fonts, txSz, iso): + renderEntity(screen, sprite, offs, tex, fonts, txSz, iso) + renderStats(screen, sprite, offs, tex, fonts, txSz, iso) + if sprite.targ is not None: + renderAction(screen, sprite, offs, tex, fonts, txSz, iso) + +def renderEntity(screen, sprite, offs, tex, fonts, txSz, iso): + rOff, cOff = offs + r, c = sprite.posAnim + W, H = screen.size + pos = embyr.tileCoords(c-cOff, r-rOff, W, H, txSz, iso) + screen.blit(tex.entity[txSz][sprite.ent.color.name], pos) + +def renderAction(screen, sprite, offs, tex, fonts, txSz, iso): + rAnim, cAnim = sprite.posAnim + rTarg, cTarg = sprite.targ.pos + rTrans, cTrans= offs + frame = sprite.frame + + rOff, cOff = rTarg - rAnim, cTarg - cAnim + r = rAnim + frame * rOff + c = cAnim + frame * cOff + + e = sprite.ent + W, H = screen.W, screen.H + if e.entID != sprite.targ.entID: + attk = e.attack.action + w, h = embyr.tileCoords(c-cTrans, r-rTrans, W, H, txSz, iso) + if attk is v2.Melee: + angle = np.arctan2(rAnim-rTarg, cTarg-cAnim) * 180 / np.pi - 90 + tex = tex.action[txSz]['melee'] + tex = pygame.transform.rotate(tex, angle + 360*frame) + elif attk is v2.Range: + angle = np.arctan2(rAnim-rTarg, cTarg-cAnim) * 180 / np.pi - 90 + tex = tex.action[txSz]['range'] + tex = pygame.transform.rotate(tex, angle) + elif attk is v2.Mage: + tex = tex.action[txSz]['mage'] + sz = int(txSz * frame) + tex = pygame.transform.scale(tex, (sz, sz)) + else: + print('invalid attack texture in render.py') + exit(0) + + screen.blit(tex, (w, h)) + #w, h = embyr.tileCoords(cAnim-cOff, rAnim-rOff, W, H, txSz, iso) + #screen.rect(Neon.BLUE.rgb, (w, h, txSz, txSz), 2) + + #w, h = embyr.tileCoords(cTarg-cOff, rTarg-rOff, W, H, txSz, iso) + #screen.rect(Neon.ORANGE.rgb, (w, h, txSz, txSz), 4) + + damage = e.damage + if damage is not None: + w, h = embyr.tileCoords(cAnim-cTrans, rAnim-rTrans, W, H, txSz, iso) + if damage == 0: + text = fonts.huge.render(str(damage), 1, Neon.BLUE.rgb) + else: + text = fonts.huge.render(str(damage), 1, Neon.RED.rgb) + screen.blit(text, (w, h-16)) + +def renderStat(screen, stat, maxStat, statInd, w, h, txSz, color): + #w, h = tileCoords(w, h, txSz) + barSz = (1.5*txSz) // 16 + scale = txSz / maxStat + scaledStat = int(scale*stat) + if stat > 0: + screen.rect(color, (w, h+statInd*barSz, scaledStat, barSz)) + if maxStat-stat > 0: + screen.rect(Neon.RED.rgb, + (w+scaledStat, h+statInd*barSz, txSz-scaledStat, barSz)) + +def renderStats(screen, sprite, offs, tex, fonts, txSz, iso): + e = sprite.ent + rOff, cOff = offs + r, c = sprite.posAnim + W, H = screen.W, screen.H + w, h = embyr.tileCoords(c-cOff, r-rOff, W, H, txSz, iso) + stats = [] + stats.append((e.health, e.maxHealth, Neon.GREEN.rgb)) + stats.append((e.water, e.maxWater, Neon.BLUE.rgb)) + stats.append((e.food, e.maxFood, Neon.GOLD.rgb)) + + text = fonts.large.render(sprite.entName, 1, sprite.nameColor.rgb) + screen.blit(text, (w-40, h-20)) + + for (ind, dat) in enumerate(stats): + s, sMax, color = dat + renderStat(screen, s, sMax, ind, w, h, txSz, color) + +def renderGraph(screen, dat, fonts, w, h, border=2, + bg=Neon.BLACK.rgb, color=Neon.RED.rgb): + if len(dat) == 0: + return + + tickHeight = 5 + fontWidth = 40 + fontHeight = 40 + yy = dat[-w:] + xx = np.arange(len(yy)) + for x, y in zip(xx, yy): + screen.line(color, (x, h-y), (x, h-(y+tickHeight))) + + #screen.rect(Color.RED, + # (x - fontWidth+3, h-(y+tickHeight+3), fontWidth, tickHeight)) + + text = fonts.large.render(str(y), 1, Neon.YELLOW.rgb) + screen.blit(text, (x-fontWidth, h-y-fontHeight)) + +def renderHist(screen, dat, fonts, W, H, mul=1, border=4, + bg=Neon.BLACK.rgb, colors=(Neon.GREEN.rgb, Neon.RED.rgb, Neon.BLUE.rgb)): + px = 8 + spacer = 3 + blockSize = 64 + valSize = 32 + txtW = 45 + txtH = 20 + leftPad = 16 + barSz = 8 + delta = 0 + colorInd = 0 + x = 0 + scale = 1 + if np.max(dat) > H: + scale = H / np.max(dat) + for block in dat: + for origVal in block: + val = int(origVal / scale) + #val = int(100*np.random.rand()) + color = colors[colorInd % len(block)] + xx, yy = x+border+barSz, H-val-border + screen.rect(color, (xx, yy, barSz, val)) + + x += valSize + #if ww > graphW: + # return + text = fonts.small.render(str(origVal), 1, Neon.YELLOW.rgb) + screen.blit(text, (xx-txtW, yy-txtH)) + + #delta += px+spacer + #colorInd += 1 + + #delta += setSpacer + +def histogram(data, numBins=10): + data = sorted(data, reverse=True) + split = int(np.ceil(len(data)/float(numBins))) + hist = [] + for i in range(numBins): + val = 0 + datSplit = data[split*i:split*(i+1)] + if len(datSplit) > 0: + val = int(np.mean(datSplit)) + hist += [[val]] + return hist + +def renderGraphs(screen, stats, fonts, W, H, border=4): + #Render histograms of time alive, levels, and best agents + + blocks = stats.statBlocks + timesAlive = np.asarray([[e.timeAlive] for e in blocks]) + timesAlive = histogram(timesAlive) + combatStats = [(e.melee, e.ranged, e.defense) for e in blocks] + + #Render + renderHist(screen, fonts, timesAlive, 0, H, w, h) + #renderHist(screen, fonts, combatStats, 0, H+h, W, h) + renderGraph(screen, stats.numEntities[-W:], fonts, W, H, + color=Neon.GOLD.rgb) + +def renderExchangeBlock(screen, entry, fonts, ind, + W, H, blockH, pad=4): + screen.rect(Neon.RED.rgb, + (0, (ind+1)*blockH, W, 3), 0) + + numBuy, numSell = entry.numBuy, entry.numSell + maxBuyPrice, minSellPrice = entry.maxBuyPrice, entry.maxSellPrice + color = Neon.YELLOW.rgb + + text = [] + text.append(entry.itemName) + text.append('Buy/Sell Offers: ' + str(numBuy) + ', ' + str(numSell)) + text.append('Min/Max Price: ' + str(maxBuyPrice) + ', ' + str(minSellPrice)) + + def height(i, inc): + return i*blockH+ int(inc*blockH/5.0) + + for i, e in enumerate(text): + txt = fonts.normal.render(e, 1, color) + screen.blit(txt, (pad, height(ind, i))) + +def renderExchange(screen, stats, fonts, W, H): + blockH = W/2 + numRender = H / blockH + exchange = stats.exchange + + i = -1 + for e in exchange.queue: + i += 1 + if i == numRender: + break + + renderExchangeBlock(screen, e, fonts, i, W, H, blockH) diff --git a/jsuarez/extra/embyr_deprecated/embyr/setup.py b/jsuarez/extra/embyr_deprecated/embyr/setup.py new file mode 100644 index 00000000..9e3fa9f7 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/setup.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python + +from setuptools import find_packages, setup +from os.path import join, dirname +from os import walk + +examples = {} +for root, subFolders, files in walk('examples'): + for fn in files: + ext = fn.split('.')[-1].lower() + filename = join(root, fn) + directory = '%s%s' % ('share/kivy3-', dirname(filename)) + if directory not in examples: + examples[directory] = [] + examples[directory].append(filename) + +setup( + name='kivy3', + version='0.1', + description='Kivy extensions for 3D graphics', + author='Niko Skrypnik', + author_email='nskrypnik@gmail.com', + include_package_data=True, + packages=find_packages(exclude=("tests",)), + data_files=list(examples.items()), + requires=['kivy', ] +) diff --git a/jsuarez/extra/embyr_deprecated/embyr/tests/__init__.py b/jsuarez/extra/embyr_deprecated/embyr/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/jsuarez/extra/embyr_deprecated/embyr/tests/core/__init__.py b/jsuarez/extra/embyr_deprecated/embyr/tests/core/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/jsuarez/extra/embyr_deprecated/embyr/tests/core/test_object3d.py b/jsuarez/extra/embyr_deprecated/embyr/tests/core/test_object3d.py new file mode 100644 index 00000000..63a2e1aa --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/tests/core/test_object3d.py @@ -0,0 +1,45 @@ + +import unittest +from kivy3 import Object3D +from tests.utils import Sandbox + + +class DummyObject: + pass + + +class Object3DTest(unittest.TestCase): + + def setUp(self): + self.sandbox = Sandbox() + self.obj = Object3D() + + def tearDown(self): + self.sandbox.restore() + + def test_position(self): + obj = self.obj + obj.pos.x = 10 + self.assertEqual(obj._position[0], 10) + obj.position.y = 8 + self.assertEqual(obj._position[1], 8) + obj.pos.z = 3 + self.assertEqual(obj._position[2], 3) + + def test_add_objects(self): + obj = self.obj + self.sandbox.stub(obj, '_add_child') + + obj.add(DummyObject(), DummyObject(), DummyObject()) + self.assertEqual(obj._add_child.call_count, 3) + + def test_add_child(self): + obj = self.obj + child = DummyObject() + obj._add_child(child) + self.assertEqual(child.parent, obj) + self.assertEqual(len(obj.children), 1) + + +if __name__ == "__main__": + unittest.main() diff --git a/jsuarez/extra/embyr_deprecated/embyr/tests/loaders/__init__.py b/jsuarez/extra/embyr_deprecated/embyr/tests/loaders/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/jsuarez/extra/embyr_deprecated/embyr/tests/loaders/test_loader.py b/jsuarez/extra/embyr_deprecated/embyr/tests/loaders/test_loader.py new file mode 100644 index 00000000..defa825d --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/tests/loaders/test_loader.py @@ -0,0 +1,39 @@ + +import os +import unittest +from kivy3.loaders.loader import BaseLoader +from tests.utils import Spy, Sandbox +from kivy.clock import Clock + + +this_dir = os.path.abspath(os.path.dirname(__file__)) + + +class BaseLoadertestCase(unittest.TestCase): + + def setUp(self): + self.loader = BaseLoader() + self.sandbox = Sandbox() + + def tearDown(self): + self.sandbox.restore() + + def test_load_when_no_on_load(self): + loader = self.loader + loader.parse = Spy() + loader.load('somefile') + self.assertTrue(loader.parse.is_called()) + + def test_on_load_called(self): + loader = self.loader + loader.parse = Spy() + _on_load = Spy() + # mock Clock.schedule_once + self.sandbox.stub(Clock, 'schedule_once', call_fake=lambda x, t: x(0)) + loader.load('somesource', on_load=_on_load) + self.assertTrue(_on_load.is_called(), 'on_load callback should be called') + + + +if __name__ == '__main__': + unittest.main() diff --git a/jsuarez/extra/embyr_deprecated/embyr/tests/loaders/test_objloader.py b/jsuarez/extra/embyr_deprecated/embyr/tests/loaders/test_objloader.py new file mode 100644 index 00000000..afa176fe --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/tests/loaders/test_objloader.py @@ -0,0 +1,8 @@ +import unittest +from kivy3.loaders import OBJLoader + +class OBJLoaderTest(unittest.TestCase): + pass + +if __name__ == '__main__': + unittest.main() diff --git a/jsuarez/extra/embyr_deprecated/embyr/tests/test_materials.py b/jsuarez/extra/embyr_deprecated/embyr/tests/test_materials.py new file mode 100644 index 00000000..35b7b111 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/tests/test_materials.py @@ -0,0 +1,22 @@ +import unittest +from utils import Sandbox +from kivy3 import Material + + +class MaterialTest(unittest.TestCase): + + def setUp(self): + self.sandbox = Sandbox() + self.mat = Material() + + def tearDown(self): + self.sandbox.restore() + + def test_setattr(self): + self.mat.color = (0., 0., 0.) + self.assertEquals(self.mat.changes['Ka'], (0., 0., 0.)) + self.mat.shininess = 5 + self.assertEquals(self.mat.changes['Ns'], 5.) + +if __name__ == '__main__': + unittest.main() diff --git a/jsuarez/extra/embyr_deprecated/embyr/tests/test_vectors.py b/jsuarez/extra/embyr_deprecated/embyr/tests/test_vectors.py new file mode 100644 index 00000000..9391c1c1 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/tests/test_vectors.py @@ -0,0 +1,153 @@ + +import unittest +import math + +from kivy3 import Vector3, Vector4, Vector2 + +# good values for vector 3, 4, 12, 84 + + +class Vector3Test(unittest.TestCase): + + def test_create(self): + v = Vector3(1, 2, 3) + self.assertEquals(v[0], 1) + self.assertEquals(v[1], 2) + self.assertEquals(v[2], 3) + v = Vector3([4, 5, 6]) + self.assertEquals(v[0], 4) + self.assertEquals(v[1], 5) + self.assertEquals(v[2], 6) + try: + Vector3(1, 2, 3, 4) + assert False, "This shold not reached" + except: + pass + try: + Vector3([3, 4, 2, 1]) + assert False, "This shold not reached" + except: + pass + + def test_add(self): + v1 = Vector3(1, 2, 3) + v2 = Vector3(4, 5, 6) + v = v1 + v2 + self.assertEqual(v, [5, 7, 9]) + v1.add(v2) + self.assertEqual(v1, [5, 7, 9]) + self.assertEqual(v + 2, [7, 9, 11]) + + def test_sub(self): + v1 = Vector3(4, 5, 6) + v2 = Vector3(1, 2, 3) + v = v1 - v2 + self.assertEqual(v, [3, 3, 3]) + v1.sub(v2) + self.assertEqual(v1, [3, 3, 3]) + self.assertEqual(v - 3, [0, 0, 0]) + + def test_multiply(self): + v1 = Vector3(5, 6, 7) + v2 = Vector3(2, 2, 2) + self.assertEqual(v1 * v2, [10., 12., 14.]) + v1.multiply(v2) + self.assertEqual(v1, [10., 12., 14.]) + + def test_divide(self): + v1 = Vector3(6, 4, 8) + v2 = Vector3(2, 2, 2) + self.assertEqual(v1 / v2, [3., 2., 4.]) + v1.divide(v2) + self.assertEqual(v1, [3., 2., 4.]) + + def test_minmax(self): + v = Vector3(6, 7, 4) + v1 = Vector3(3, 5, 8) + v.min(v1) + self.assertEqual(v, [3, 5, 4]) + v2 = Vector3(1, 7, 6) + v.max(v2) + self.assertEqual(v, [3, 7, 6]) + + def test_clamp(self): + v1 = Vector3(1, 2, 3) + v2 = Vector3(3, 4, 6) + v = Vector3(0, 5, 4) + v.clamp(v1, v2) + self.assertEqual(v, [1, 4, 4]) + + def test_negate(self): + v = Vector3(2, 2, 2) + v.negate() + self.assertEqual(v, [-2, -2, -2]) + + def test_length(self): + v = Vector3(3, 12, 4) + v = Vector3(12, 4, 3) + + self.assertEqual(v.length(), 13) + self.assertEqual(v.length_sq(), 13*13) + + def test_angle(self): + v1 = Vector3(0, 0, 1) + v2 = Vector3(0, 1, 0) + angle = v1.angle(v2) + self.assertEqual(math.degrees(angle), 90.0) + v1 = Vector3(0, 0, 1) + v2 = Vector3(0, 0, -1) + angle = v1.angle(v2) + self.assertEqual(math.degrees(angle), 180.0) + + def test_distance(self): + v1 = Vector3(2, 1, 6) + v2 = Vector3(2, 5, 6) + + self.assertEqual(v1.distance(v2), 4) + + def test_attributes(self): + v = Vector3(0, 0, 0) + v.x = 4 + self.assertEqual(v[0], v.x) + self.assertEqual(v[0], 4) + + v.z = 6 + self.assertEqual(v[2], v.z) + self.assertEqual(v[2], 6) + + try: + t = v.v + assert False, "executing of this string is error" + except AttributeError: + pass + + +class Vector2Test(unittest.TestCase): + + def test_create(self): + v = Vector2(1, 2) + try: + v = Vector2(1, 2, 3) + assert False, "This should not be normally reached" + except: + pass # test is passed normally + + def test_attrbutes(self): + v = Vector2(0, 0) + v.x = 4 + self.assertEqual(v[0], v.x) + self.assertEqual(v[0], 4) + + v.y = 7 + self.assertEqual(v[1], v.y) + self.assertEqual(v[1], 7) + + try: + t = v.z + assert False, "executing of this string is error" + except AttributeError: + pass + + +if __name__ == '__main__': + unittest.main() diff --git a/jsuarez/extra/embyr_deprecated/embyr/tests/utils.py b/jsuarez/extra/embyr_deprecated/embyr/tests/utils.py new file mode 100644 index 00000000..e0be01ff --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/tests/utils.py @@ -0,0 +1,70 @@ +''' +Module provides some test utilities +''' + +class Spy: + ''' + This is the test helper which helps us to check whether function has been + called and which parameters have been passed to + ''' + def __init__(self, call_fake=None, returns=None): + self.call_count = 0 + self.args = [] + self.call_fake = call_fake + self.returns = returns + + def __call__(self, *args, **kwargs): + self.args.append((args, kwargs)) + self.call_count += 1 + if callable(self.call_fake): + return self.call_fake(*args, **kwargs) + else: + if self.returns: + return self.returns + + def is_called(self): + return self.call_count > 0 + + +class Sandbox: + ''' + This little sucker helps us to mock some modules and functions and then + restore it + ''' + + def __init__(self): + 'Constructor' + self._storage = {} + + def stub(self, obj, func_name, **kw): + if not callable(getattr(obj, func_name)): + raise Exception('You may stub only callable objects') + # save previous value of stubbed function + if not obj in self._storage: + obj_storage = {} + self._storage[obj] = obj_storage + else: + obj_storage = self._storage[obj] + + if func_name in obj_storage: + raise Exception('%s function has been already stubbed' % func_name) + # store function into sandbox storage + obj_storage[func_name] = getattr(obj, func_name) + + setattr(obj, func_name, Spy(**kw)) + + def restore(self): + if not hasattr(self, '_storage'): + return + + for obj, obj_storage in self._storage.iteritems(): + keys_to_del = [] + for key, original_value in obj_storage.iteritems(): + # restore all original value to an object + setattr(obj, key, original_value) + keys_to_del.append(key) + del self._storage + + def __del__(self): + 'When we destruct object don\'t forget to restore all values' + self.restore() diff --git a/jsuarez/extra/embyr_deprecated/embyr/tex/black.png b/jsuarez/extra/embyr_deprecated/embyr/tex/black.png new file mode 100644 index 00000000..80cbc35e Binary files /dev/null and b/jsuarez/extra/embyr_deprecated/embyr/tex/black.png differ diff --git a/jsuarez/extra/embyr_deprecated/embyr/tex/blinn-phong.glsl b/jsuarez/extra/embyr_deprecated/embyr/tex/blinn-phong.glsl new file mode 100644 index 00000000..e113d957 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/tex/blinn-phong.glsl @@ -0,0 +1,61 @@ +---VERTEX SHADER------------------------------------------------------- +#ifdef GL_ES + precision highp float; +#endif + +attribute vec3 v_pos; +attribute vec3 v_normal; +attribute vec4 v_color; +attribute vec2 v_tc0; + +uniform mat4 modelview_mat; +uniform mat4 projection_mat; +uniform float Tr; + +varying vec4 frag_color; +varying vec2 uv_vec; +varying vec4 normal_vec; +varying vec4 vertex_pos; + +void main (void) { + vec4 pos = modelview_mat * vec4(v_pos,1.0); + frag_color = v_color; + uv_vec = v_tc0; + vertex_pos = pos; + normal_vec = vec4(v_normal,0.0); + gl_Position = projection_mat * pos; +} + + +---FRAGMENT SHADER----------------------------------------------------- +#ifdef GL_ES + precision highp float; +#endif + +varying vec4 normal_vec; +varying vec4 vertex_pos; +varying vec4 frag_color; +varying vec2 uv_vec; + +uniform mat4 normal_mat; +uniform vec3 Kd; // diffuse color +uniform vec3 Ka; // color +uniform vec3 Ks; // specular color +uniform float Tr; // transparency +uniform float Ns; // shininess + + +uniform sampler2D tex; + +void main (void){ + vec4 v_normal = normalize( normal_mat * normal_vec ); + vec4 v_light = normalize( vec4(0,0,0,1) - vertex_pos ); + + vec3 Ia = Ka; + vec3 Id = Kd * max(dot(v_light, v_normal), 0.0); + vec3 Is = Ks * pow(max(dot(v_light, v_normal), 0.0), Ns); + + vec4 tex_color = texture2D(tex, uv_vec); + gl_FragColor = vec4(Ia + Id + Is, Tr); + gl_FragColor = gl_FragColor * tex_color; +} diff --git a/jsuarez/extra/embyr_deprecated/embyr/tex/default.glsl b/jsuarez/extra/embyr_deprecated/embyr/tex/default.glsl new file mode 100644 index 00000000..e113d957 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/tex/default.glsl @@ -0,0 +1,61 @@ +---VERTEX SHADER------------------------------------------------------- +#ifdef GL_ES + precision highp float; +#endif + +attribute vec3 v_pos; +attribute vec3 v_normal; +attribute vec4 v_color; +attribute vec2 v_tc0; + +uniform mat4 modelview_mat; +uniform mat4 projection_mat; +uniform float Tr; + +varying vec4 frag_color; +varying vec2 uv_vec; +varying vec4 normal_vec; +varying vec4 vertex_pos; + +void main (void) { + vec4 pos = modelview_mat * vec4(v_pos,1.0); + frag_color = v_color; + uv_vec = v_tc0; + vertex_pos = pos; + normal_vec = vec4(v_normal,0.0); + gl_Position = projection_mat * pos; +} + + +---FRAGMENT SHADER----------------------------------------------------- +#ifdef GL_ES + precision highp float; +#endif + +varying vec4 normal_vec; +varying vec4 vertex_pos; +varying vec4 frag_color; +varying vec2 uv_vec; + +uniform mat4 normal_mat; +uniform vec3 Kd; // diffuse color +uniform vec3 Ka; // color +uniform vec3 Ks; // specular color +uniform float Tr; // transparency +uniform float Ns; // shininess + + +uniform sampler2D tex; + +void main (void){ + vec4 v_normal = normalize( normal_mat * normal_vec ); + vec4 v_light = normalize( vec4(0,0,0,1) - vertex_pos ); + + vec3 Ia = Ka; + vec3 Id = Kd * max(dot(v_light, v_normal), 0.0); + vec3 Is = Ks * pow(max(dot(v_light, v_normal), 0.0), Ns); + + vec4 tex_color = texture2D(tex, uv_vec); + gl_FragColor = vec4(Ia + Id + Is, Tr); + gl_FragColor = gl_FragColor * tex_color; +} diff --git a/jsuarez/extra/embyr_deprecated/embyr/tex/dirt.png b/jsuarez/extra/embyr_deprecated/embyr/tex/dirt.png new file mode 100644 index 00000000..6f6bcb8d Binary files /dev/null and b/jsuarez/extra/embyr_deprecated/embyr/tex/dirt.png differ diff --git a/jsuarez/extra/embyr_deprecated/embyr/tex/forest.png b/jsuarez/extra/embyr_deprecated/embyr/tex/forest.png new file mode 100644 index 00000000..e0944b3b Binary files /dev/null and b/jsuarez/extra/embyr_deprecated/embyr/tex/forest.png differ diff --git a/jsuarez/extra/embyr_deprecated/embyr/tex/grass.png b/jsuarez/extra/embyr_deprecated/embyr/tex/grass.png new file mode 100644 index 00000000..cafb7966 Binary files /dev/null and b/jsuarez/extra/embyr_deprecated/embyr/tex/grass.png differ diff --git a/jsuarez/extra/embyr_deprecated/embyr/tex/lava.png b/jsuarez/extra/embyr_deprecated/embyr/tex/lava.png new file mode 100644 index 00000000..16ca8e51 Binary files /dev/null and b/jsuarez/extra/embyr_deprecated/embyr/tex/lava.png differ diff --git a/jsuarez/extra/embyr_deprecated/embyr/tex/map.tmx b/jsuarez/extra/embyr_deprecated/embyr/tex/map.tmx new file mode 100644 index 00000000..b89e8202 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/tex/map.tmx @@ -0,0 +1,9 @@ + + + + + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 5, 5, 5, 3, 3, 5, 6, 3, 3, 2, 2, 3, 5, 6, 3, 3, 3, 5, 3, 2, 3, 3, 6, 5, 3, 3, 3, 6, 5, 3, 3, 5, 5, 3, 3, 5, 5, 3, 3, 3, 5, 5, 5, 5, 6, 6, 5, 5, 6, 5, 3, 3, 3, 5, 5, 3, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 5, 6, 5, 3, 3, 3, 5, 6, 3, 3, 2, 2, 2, 3, 6, 3, 3, 3, 6, 5, 3, 3, 3, 3, 5, 3, 3, 3, 6, 5, 3, 3, 6, 5, 3, 3, 5, 5, 5, 5, 6, 6, 6, 6, 6, 5, 5, 3, 3, 5, 6, 3, 3, 3, 3, 5, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 6, 5, 3, 3, 3, 3, 5, 6, 6, 5, 3, 3, 3, 3, 6, 5, 3, 3, 3, 6, 6, 5, 3, 5, 6, 3, 3, 3, 6, 5, 5, 5, 5, 5, 5, 6, 6, 5, 5, 5, 3, 3, 5, 6, 3, 3, 5, 3, 3, 3, 5, 6, 5, 3, 3, 5, 5, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 5, 6, 5, 3, 3, 3, 3, 3, 3, 3, 5, 5, 3, 3, 3, 5, 6, 6, 3, 3, 5, 5, 6, 5, 5, 5, 3, 3, 3, 6, 5, 3, 3, 3, 6, 5, 3, 5, 5, 3, 3, 3, 3, 5, 3, 3, 3, 6, 5, 3, 3, 3, 5, 6, 3, 3, 5, 6, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 5, 6, 6, 5, 3, 3, 3, 3, 3, 3, 5, 5, 3, 5, 3, 5, 5, 5, 5, 3, 3, 3, 5, 3, 3, 3, 3, 5, 5, 3, 3, 3, 3, 5, 3, 3, 3, 5, 5, 3, 3, 3, 6, 3, 3, 3, 5, 5, 3, 2, 2, 3, 5, 5, 3, 3, 3, 6, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 3, 3, 3, 3, 5, 6, 5, 3, 3, 3, 3, 5, 6, 5, 3, 3, 3, 3, 3, 6, 5, 3, 3, 3, 3, 3, 3, 3, 5, 5, 3, 3, 3, 3, 5, 3, 3, 3, 5, 3, 5, 6, 5, 5, 3, 3, 3, 5, 6, 3, 3, 2, 3, 3, 6, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 2, 2, 2, 3, 3, 3, 5, 6, 6, 6, 6, 5, 3, 5, 5, 3, 3, 3, 3, 5, 6, 5, 3, 3, 3, 3, 3, 3, 6, 3, 3, 3, 5, 6, 6, 6, 5, 3, 3, 3, 5, 5, 5, 6, 6, 5, 5, 5, 6, 3, 3, 2, 2, 3, 5, 5, 3, 2, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 2, 2, 2, 2, 3, 2, 3, 5, 5, 5, 3, 3, 3, 5, 5, 5, 5, 5, 3, 3, 5, 6, 6, 5, 3, 3, 3, 5, 5, 3, 3, 3, 6, 3, 3, 3, 5, 5, 3, 3, 3, 3, 6, 6, 5, 5, 5, 5, 6, 3, 3, 2, 2, 3, 5, 5, 3, 2, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 2, 2, 3, 2, 2, 3, 3, 3, 3, 3, 5, 5, 5, 3, 5, 6, 5, 5, 3, 3, 6, 6, 3, 3, 3, 5, 5, 3, 3, 5, 5, 3, 3, 3, 3, 5, 5, 3, 3, 3, 6, 3, 3, 3, 6, 6, 6, 5, 3, 2, 2, 3, 5, 6, 5, 3, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 5, 5, 3, 3, 3, 3, 2, 3, 3, 3, 3, 6, 5, 3, 3, 3, 3, 3, 5, 5, 3, 3, 5, 6, 5, 3, 3, 6, 5, 3, 3, 5, 3, 3, 3, 2, 3, 3, 5, 6, 3, 3, 6, 3, 3, 5, 6, 3, 5, 6, 3, 2, 2, 3, 3, 5, 3, 2, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 6, 5, 6, 5, 5, 5, 3, 3, 3, 3, 5, 6, 5, 3, 2, 2, 2, 3, 3, 5, 5, 3, 3, 3, 3, 6, 3, 3, 6, 3, 3, 3, 5, 3, 3, 3, 3, 3, 3, 3, 5, 6, 6, 5, 3, 5, 6, 3, 3, 3, 6, 3, 2, 2, 3, 3, 6, 5, 3, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 5, 5, 5, 3, 5, 6, 5, 3, 5, 6, 5, 3, 3, 3, 2, 2, 3, 6, 5, 3, 3, 2, 3, 3, 6, 6, 6, 5, 3, 3, 5, 6, 3, 3, 3, 6, 6, 3, 3, 3, 3, 5, 5, 5, 6, 5, 3, 3, 3, 6, 3, 3, 2, 2, 3, 5, 5, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 6, 6, 5, 3, 3, 3, 5, 5, 6, 5, 5, 5, 5, 3, 3, 3, 5, 3, 3, 3, 3, 3, 3, 3, 5, 3, 5, 3, 3, 3, 6, 3, 3, 3, 6, 5, 6, 5, 3, 3, 3, 5, 6, 3, 3, 3, 3, 3, 3, 5, 5, 5, 3, 2, 3, 3, 6, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 6, 6, 6, 3, 3, 3, 3, 6, 6, 3, 3, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 3, 3, 5, 5, 3, 3, 6, 3, 3, 3, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 5, 6, 3, 3, 3, 5, 5, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 6, 5, 5, 5, 6, 3, 2, 3, 3, 6, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 6, 5, 6, 6, 6, 3, 3, 3, 3, 3, 6, 3, 3, 3, 5, 5, 3, 3, 5, 5, 5, 5, 6, 5, 3, 3, 5, 5, 3, 3, 3, 3, 3, 6, 5, 3, 3, 5, 5, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 5, 3, 3, 6, 3, 3, 3, 5, 5, 3, 2, 2, 3, 2, 3, 3, 5, 6, 6, 5, 3, 3, 6, 5, 5, 3, 3, 3, 3, 6, 3, 2, 2, 3, 6, 3, 3, 5, 6, 5, 5, 5, 5, 5, 6, 6, 5, 6, 3, 3, 3, 3, 5, 6, 5, 5, 5, 5, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 6, 3, 3, 5, 3, 3, 3, 6, 3, 3, 2, 2, 2, 2, 3, 5, 6, 5, 3, 3, 3, 3, 3, 5, 6, 6, 5, 5, 5, 6, 3, 3, 3, 3, 6, 5, 5, 6, 5, 3, 3, 5, 5, 5, 3, 3, 3, 5, 5, 5, 3, 5, 6, 6, 6, 5, 5, 3, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 6, 5, 5, 6, 3, 3, 3, 6, 3, 3, 2, 2, 2, 2, 3, 5, 6, 5, 3, 2, 2, 2, 2, 3, 3, 3, 5, 6, 5, 5, 3, 3, 3, 5, 6, 5, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 6, 5, 5, 6, 5, 6, 6, 3, 3, 3, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 5, 5, 5, 5, 5, 3, 5, 5, 3, 2, 2, 2, 3, 3, 3, 3, 5, 3, 3, 2, 2, 2, 2, 2, 3, 3, 3, 5, 5, 5, 3, 6, 6, 5, 5, 6, 6, 3, 3, 3, 3, 3, 5, 3, 3, 5, 3, 5, 5, 5, 5, 3, 3, 3, 6, 3, 3, 3, 6, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 5, 6, 3, 5, 6, 5, 6, 5, 3, 2, 3, 3, 3, 5, 5, 5, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 5, 3, 6, 5, 6, 5, 3, 3, 3, 5, 5, 3, 3, 3, 3, 5, 6, 5, 5, 6, 5, 5, 5, 3, 3, 3, 3, 5, 5, 3, 3, 6, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 6, 3, 3, 5, 5, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 3, 5, 3, 5, 3, 3, 3, 3, 3, 5, 6, 6, 6, 6, 6, 3, 3, 3, 3, 3, 6, 6, 5, 6, 3, 3, 3, 3, 3, 5, 6, 5, 3, 3, 3, 3, 3, 5, 5, 3, 3, 5, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 6, 5, 3, 3, 5, 3, 5, 3, 3, 3, 5, 5, 3, 3, 3, 3, 5, 6, 6, 6, 3, 3, 5, 6, 5, 5, 5, 6, 6, 6, 5, 3, 2, 2, 3, 3, 5, 3, 3, 3, 2, 2, 2, 3, 3, 6, 5, 3, 3, 3, 3, 5, 5, 3, 3, 3, 3, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 3, 5, 5, 3, 3, 5, 6, 6, 5, 3, 3, 6, 6, 3, 3, 3, 5, 5, 5, 3, 3, 6, 6, 5, 5, 3, 3, 3, 5, 5, 5, 6, 3, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, 5, 6, 3, 2, 2, 2, 3, 6, 3, 3, 3, 3, 3, 6, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 3, 5, 6, 3, 3, 6, 5, 6, 6, 3, 3, 3, 5, 6, 5, 5, 6, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 6, 5, 3, 3, 5, 5, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 3, 3, 6, 5, 3, 3, 2, 2, 3, 5, 3, 3, 2, 2, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 3, 3, 5, 6, 5, 6, 3, 3, 5, 5, 3, 2, 3, 3, 5, 6, 6, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 6, 3, 3, 3, 3, 6, 5, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 5, 6, 6, 3, 3, 2, 2, 3, 3, 6, 3, 2, 2, 2, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 5, 3, 3, 3, 5, 5, 3, 2, 2, 2, 3, 5, 6, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 6, 5, 3, 3, 3, 5, 6, 6, 5, 3, 3, 3, 2, 2, 3, 3, 5, 5, 5, 6, 6, 5, 3, 5, 3, 3, 5, 5, 3, 2, 2, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 5, 6, 3, 3, 2, 2, 3, 5, 5, 3, 3, 3, 6, 5, 5, 3, 3, 3, 3, 5, 6, 6, 5, 3, 3, 3, 3, 3, 5, 5, 3, 3, 3, 5, 5, 6, 6, 5, 3, 5, 3, 3, 6, 3, 3, 3, 5, 5, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 6, 5, 3, 3, 3, 3, 3, 3, 3, 5, 6, 3, 3, 3, 3, 3, 6, 5, 3, 2, 2, 3, 3, 6, 3, 2, 2, 3, 3, 5, 3, 6, 5, 3, 3, 3, 3, 5, 6, 5, 3, 6, 6, 3, 3, 6, 6, 5, 3, 3, 3, 5, 5, 3, 3, 5, 5, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 6, 3, 3, 3, 3, 3, 3, 5, 6, 6, 5, 5, 3, 5, 5, 5, 3, 3, 2, 2, 2, 3, 6, 3, 3, 2, 2, 3, 3, 3, 5, 5, 3, 3, 2, 3, 5, 6, 6, 5, 5, 3, 3, 3, 3, 5, 5, 5, 5, 5, 6, 6, 3, 3, 3, 6, 5, 5, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 5, 6, 5, 5, 5, 5, 5, 3, 3, 3, 3, 6, 6, 6, 6, 5, 3, 3, 3, 3, 3, 3, 5, 5, 3, 2, 3, 3, 3, 3, 6, 5, 3, 3, 2, 3, 3, 6, 6, 6, 5, 3, 3, 3, 3, 3, 5, 5, 5, 6, 6, 5, 3, 3, 5, 6, 3, 3, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 5, 5, 6, 5, 5, 3, 3, 3, 3, 3, 5, 6, 3, 3, 3, 5, 5, 5, 5, 5, 5, 3, 5, 6, 5, 3, 3, 3, 5, 6, 5, 3, 3, 3, 3, 3, 5, 6, 5, 6, 6, 5, 5, 3, 3, 3, 3, 3, 3, 5, 5, 3, 3, 3, 5, 5, 3, 3, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 3, 5, 5, 3, 3, 2, 2, 2, 3, 3, 5, 5, 3, 3, 3, 3, 5, 5, 3, 5, 5, 6, 6, 6, 5, 3, 6, 6, 5, 3, 3, 2, 2, 2, 2, 3, 6, 3, 3, 3, 3, 5, 5, 5, 3, 3, 2, 2, 3, 3, 3, 3, 3, 3, 6, 5, 3, 3, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 6, 5, 3, 2, 2, 2, 3, 3, 3, 5, 3, 3, 2, 2, 3, 3, 5, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 3, 2, 2, 2, 2, 2, 3, 5, 6, 3, 2, 2, 2, 3, 3, 5, 3, 3, 2, 2, 2, 3, 3, 3, 3, 3, 5, 3, 3, 3, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 5, 5, 3, 2, 3, 3, 3, 3, 5, 5, 5, 3, 2, 2, 3, 3, 6, 5, 3, 2, 2, 3, 3, 6, 5, 3, 3, 2, 2, 2, 2, 2, 3, 3, 6, 5, 3, 2, 2, 2, 2, 3, 6, 3, 2, 2, 2, 2, 3, 3, 5, 6, 5, 3, 3, 3, 5, 6, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 5, 6, 3, 3, 2, 3, 5, 6, 6, 5, 6, 5, 3, 3, 3, 2, 3, 5, 5, 3, 2, 2, 2, 3, 5, 5, 3, 2, 2, 2, 2, 2, 3, 3, 5, 6, 5, 3, 3, 2, 2, 3, 5, 5, 3, 2, 2, 2, 2, 3, 5, 5, 3, 3, 3, 3, 2, 3, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 6, 5, 5, 3, 3, 3, 6, 5, 5, 6, 5, 5, 6, 6, 5, 3, 3, 5, 6, 5, 3, 2, 3, 3, 6, 3, 3, 2, 2, 2, 2, 3, 3, 5, 3, 6, 6, 6, 5, 3, 3, 3, 6, 5, 3, 2, 2, 2, 3, 5, 5, 3, 3, 3, 3, 2, 3, 3, 6, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 5, 5, 3, 3, 6, 5, 3, 3, 5, 6, 3, 3, 3, 3, 6, 5, 3, 3, 5, 5, 3, 2, 2, 2, 3, 3, 3, 6, 6, 5, 3, 3, 5, 6, 3, 3, 5, 5, 3, 3, 2, 2, 3, 3, 5, 5, 3, 2, 2, 2, 3, 3, 3, 6, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 3, 2, 3, 3, 3, 5, 5, 3, 3, 6, 3, 3, 3, 5, 6, 5, 3, 3, 3, 3, 5, 6, 5, 6, 5, 3, 3, 3, 3, 5, 6, 6, 5, 5, 5, 3, 3, 5, 6, 5, 3, 5, 3, 3, 2, 2, 2, 3, 3, 5, 3, 3, 3, 3, 3, 5, 5, 6, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 2, 2, 3, 3, 5, 5, 3, 3, 3, 6, 5, 5, 6, 6, 5, 3, 3, 3, 2, 3, 3, 5, 6, 6, 5, 5, 5, 6, 5, 5, 3, 3, 3, 5, 6, 3, 3, 3, 5, 5, 3, 5, 5, 3, 2, 2, 3, 5, 5, 6, 5, 5, 5, 5, 6, 5, 6, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 2, 2, 3, 3, 5, 5, 3, 5, 5, 6, 6, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 6, 5, 3, 3, 5, 6, 3, 3, 5, 5, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 3, 5, 6, 5, 5, 5, 5, 5, 6, 6, 3, 3, 3, 3, 5, 6, 5, 3, 3, 5, 6, 3, 3, 3, 5, 5, 5, 5, 5, 5, 6, 5, 5, 3, 3, 3, 3, 5, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 5, 6, 5, 6, 3, 5, 3, 3, 3, 6, 5, 3, 3, 2, 3, 3, 5, 5, 5, 3, 3, 3, 6, 5, 3, 5, 5, 3, 3, 3, 5, 5, 5, 3, 3, 5, 5, 6, 3, 2, 3, 5, 6, 5, 5, 5, 3, 5, 6, 3, 3, 3, 5, 5, 6, 6, 6, 6, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 6, 5, 3, 3, 5, 6, 3, 3, 3, 5, 6, 5, 3, 2, 2, 3, 5, 5, 3, 3, 3, 3, 5, 5, 3, 3, 5, 6, 3, 3, 5, 6, 3, 3, 3, 3, 5, 6, 5, 3, 3, 2, 3, 3, 3, 3, 3, 3, 5, 5, 3, 3, 5, 6, 5, 5, 3, 5, 5, 6, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 3, 3, 3, 5, 6, 5, 3, 6, 5, 5, 3, 2, 2, 3, 5, 5, 3, 3, 3, 5, 6, 5, 3, 3, 3, 3, 5, 5, 3, 5, 6, 3, 5, 5, 5, 5, 5, 5, 3, 2, 2, 2, 3, 3, 3, 3, 3, 6, 5, 5, 6, 5, 3, 3, 3, 3, 3, 3, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 5, 3, 5, 6, 6, 6, 5, 5, 3, 3, 3, 2, 2, 3, 5, 3, 3, 3, 5, 5, 5, 3, 3, 3, 3, 3, 5, 3, 5, 5, 6, 5, 5, 6, 5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 3, 6, 6, 5, 3, 6, 6, 3, 3, 3, 2, 2, 3, 3, 6, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 6, 5, 6, 5, 3, 5, 5, 5, 3, 3, 3, 2, 2, 3, 3, 6, 3, 3, 3, 5, 5, 3, 3, 3, 3, 3, 5, 5, 3, 3, 5, 5, 3, 3, 3, 3, 3, 5, 6, 6, 5, 5, 3, 3, 5, 5, 5, 3, 3, 3, 5, 3, 3, 2, 2, 3, 3, 3, 5, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 6, 5, 3, 3, 3, 6, 3, 2, 2, 3, 3, 3, 3, 3, 5, 3, 3, 3, 6, 5, 3, 5, 5, 5, 5, 5, 3, 3, 3, 3, 5, 3, 3, 3, 3, 5, 6, 5, 5, 5, 5, 6, 5, 6, 5, 3, 3, 3, 3, 5, 5, 3, 2, 3, 3, 5, 5, 6, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 6, 5, 5, 3, 3, 3, 5, 3, 2, 3, 3, 5, 5, 5, 5, 3, 3, 3, 3, 6, 5, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 3, 5, 6, 5, 6, 5, 3, 3, 3, 5, 5, 5, 5, 6, 6, 5, 5, 6, 3, 3, 3, 3, 5, 5, 6, 5, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 6, 5, 6, 6, 5, 5, 6, 3, 3, 3, 3, 6, 5, 5, 3, 3, 3, 3, 3, 5, 5, 3, 3, 3, 3, 3, 3, 3, 3, 5, 6, 6, 5, 3, 5, 5, 5, 5, 6, 3, 3, 3, 3, 5, 6, 5, 3, 3, 6, 6, 5, 3, 3, 3, 3, 3, 3, 5, 5, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 5, 3, 5, 5, 6, 5, 3, 3, 3, 5, 5, 5, 3, 3, 2, 2, 2, 3, 3, 3, 3, 3, 3, 5, 5, 3, 6, 6, 5, 3, 3, 3, 3, 3, 5, 6, 6, 6, 6, 5, 3, 3, 5, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 6, 3, 3, 3, 6, 6, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 6, 3, 3, 3, 3, 3, 3, 3, 3, 6, 3, 3, 2, 2, 2, 2, 2, 3, 5, 5, 5, 6, 6, 6, 6, 5, 5, 3, 3, 3, 2, 2, 2, 3, 5, 5, 3, 3, 3, 5, 5, 5, 5, 5, 6, 5, 3, 2, 3, 3, 3, 3, 3, 6, 5, 3, 5, 6, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 6, 3, 3, 3, 3, 3, 3, 3, 5, 3, 3, 3, 2, 3, 3, 3, 3, 5, 6, 5, 5, 5, 3, 3, 5, 5, 3, 3, 3, 2, 2, 2, 3, 3, 5, 5, 3, 2, 2, 3, 5, 6, 3, 3, 5, 5, 3, 2, 2, 2, 3, 3, 5, 5, 3, 3, 5, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 5, 5, 5, 3, 3, 3, 3, 3, 5, 5, 3, 3, 3, 3, 3, 6, 6, 5, 5, 3, 3, 3, 3, 3, 3, 3, 5, 5, 3, 3, 2, 2, 3, 3, 6, 5, 3, 2, 2, 3, 3, 6, 5, 3, 3, 5, 3, 2, 2, 3, 3, 5, 5, 6, 3, 5, 5, 3, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 5, 6, 3, 3, 3, 3, 6, 5, 3, 3, 3, 5, 6, 5, 3, 3, 3, 3, 3, 3, 3, 5, 6, 6, 5, 6, 5, 3, 3, 2, 3, 3, 6, 3, 3, 2, 2, 2, 3, 5, 5, 3, 5, 6, 3, 3, 3, 5, 6, 6, 5, 5, 5, 6, 3, 2, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 5, 3, 3, 5, 6, 3, 3, 5, 5, 3, 3, 3, 6, 5, 3, 3, 2, 2, 2, 3, 3, 5, 5, 5, 3, 3, 3, 3, 3, 6, 3, 3, 3, 3, 5, 5, 3, 3, 2, 2, 3, 3, 5, 3, 5, 6, 5, 5, 6, 5, 6, 3, 3, 3, 5, 5, 3, 2, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 6, 3, 3, 3, 3, 6, 3, 3, 5, 3, 3, 3, 5, 5, 3, 3, 2, 2, 2, 2, 3, 5, 6, 3, 3, 3, 3, 2, 3, 3, 6, 5, 3, 3, 3, 3, 6, 5, 3, 2, 2, 2, 3, 3, 5, 3, 5, 6, 5, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 6, 3, 2, 2, 3, 6, 5, 3, 6, 3, 3, 3, 6, 5, 3, 2, 2, 2, 2, 2, 3, 6, 3, 3, 2, 2, 3, 2, 3, 3, 5, 6, 3, 2, 3, 3, 5, 6, 3, 2, 2, 2, 3, 3, 3, 3, 3, 6, 5, 3, 3, 3, 2, 2, 3, 3, 6, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 6, 3, 2, 2, 3, 3, 5, 5, 5, 3, 3, 5, 6, 3, 3, 2, 2, 2, 3, 3, 3, 6, 3, 3, 2, 3, 3, 3, 3, 3, 3, 6, 5, 3, 2, 3, 3, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 5, 3, 2, 2, 3, 3, 3, 3, 5, 5, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 5, 3, 2, 2, 3, 3, 3, 3, 3, 3, 3, 5, 5, 3, 2, 2, 3, 3, 3, 5, 5, 5, 3, 3, 2, 3, 3, 5, 6, 3, 3, 5, 5, 3, 2, 2, 3, 5, 6, 3, 3, 5, 5, 5, 3, 3, 3, 6, 5, 3, 2, 3, 3, 3, 3, 3, 3, 5, 5, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 6, 3, 2, 2, 3, 3, 3, 3, 2, 3, 3, 5, 5, 3, 3, 3, 5, 6, 6, 5, 3, 3, 3, 2, 3, 3, 5, 6, 5, 6, 3, 6, 5, 3, 2, 2, 2, 5, 6, 5, 5, 5, 5, 6, 6, 5, 5, 6, 3, 3, 3, 3, 5, 5, 5, 3, 3, 5, 6, 5, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + + + \ No newline at end of file diff --git a/jsuarez/extra/embyr_deprecated/embyr/tex/nn.mtl b/jsuarez/extra/embyr_deprecated/embyr/tex/nn.mtl new file mode 100644 index 00000000..2c731d01 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/tex/nn.mtl @@ -0,0 +1,24 @@ +# Blender MTL File: 'nn.blend' +# Material Count: 2 + +newmtl ball +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 1.000000 1.000000 1.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 +map_Kd lava.png + +newmtl rod +Ns 96.078431 +Ka 1.000000 1.000000 1.000000 +Kd 0.000000 0.000000 0.000000 +Ks 0.500000 0.500000 0.500000 +Ke 0.000000 0.000000 0.000000 +Ni 1.000000 +d 1.000000 +illum 2 +map_Kd dirt.png diff --git a/jsuarez/extra/embyr_deprecated/embyr/tex/nn.obj b/jsuarez/extra/embyr_deprecated/embyr/tex/nn.obj new file mode 100644 index 00000000..4f8ac801 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/tex/nn.obj @@ -0,0 +1,888 @@ +# Blender v2.79 (sub 0) OBJ File: 'nn.blend' +# www.blender.org +mtllib nn.mtl +o Icosphere.002 +v 0.000000 0.350000 -0.385029 +v 0.108541 0.432917 -0.306170 +v -0.041458 0.432917 -0.257431 +v -0.134164 0.432918 -0.385029 +v -0.041458 0.432917 -0.512626 +v 0.108541 0.432917 -0.463887 +v 0.041458 0.567083 -0.257431 +v -0.108541 0.567083 -0.306170 +v -0.108541 0.567083 -0.463887 +v 0.041458 0.567083 -0.512626 +v 0.134164 0.567082 -0.385029 +v 0.000000 0.650000 -0.385029 +v -0.024368 0.372402 -0.310029 +v 0.063798 0.372402 -0.338677 +v 0.039430 0.421139 -0.263677 +v 0.127597 0.421140 -0.385029 +v 0.063798 0.372402 -0.431380 +v -0.078859 0.372402 -0.385029 +v -0.103228 0.421140 -0.310029 +v -0.024368 0.372402 -0.460028 +v -0.103228 0.421140 -0.460028 +v 0.039430 0.421139 -0.506380 +v 0.142659 0.500000 -0.338677 +v 0.142659 0.500000 -0.431380 +v 0.000000 0.500000 -0.235029 +v 0.088168 0.500000 -0.263676 +v -0.142659 0.500000 -0.338677 +v -0.088168 0.500000 -0.263676 +v -0.088168 0.500000 -0.506381 +v -0.142659 0.500000 -0.431380 +v 0.088168 0.500000 -0.506381 +v 0.000000 0.500000 -0.535029 +v 0.103228 0.578860 -0.310029 +v -0.039430 0.578861 -0.263677 +v -0.127597 0.578860 -0.385029 +v -0.039430 0.578861 -0.506380 +v 0.103228 0.578860 -0.460028 +v 0.024368 0.627598 -0.310029 +v 0.078859 0.627598 -0.385029 +v -0.063798 0.627598 -0.338677 +v -0.063798 0.627598 -0.431380 +v 0.024368 0.627598 -0.460028 +vn 0.1024 -0.9435 0.3151 +vn 0.7002 -0.6617 0.2680 +vn -0.2680 -0.9435 0.1947 +vn -0.2680 -0.9435 -0.1947 +vn 0.1024 -0.9435 -0.3151 +vn 0.9050 -0.3304 0.2680 +vn 0.0247 -0.3304 0.9435 +vn -0.8897 -0.3304 0.3151 +vn -0.5746 -0.3304 -0.7488 +vn 0.5346 -0.3304 -0.7779 +vn 0.8026 -0.1256 0.5831 +vn -0.3066 -0.1256 0.9435 +vn -0.9921 -0.1256 0.0000 +vn -0.3066 -0.1256 -0.9435 +vn 0.8026 -0.1256 -0.5831 +vn 0.4089 0.6617 0.6284 +vn -0.4713 0.6617 0.5831 +vn -0.7002 0.6617 -0.2680 +vn 0.0385 0.6617 -0.7488 +vn 0.7240 0.6617 -0.1947 +vn 0.2680 0.9435 -0.1947 +vn 0.4911 0.7947 -0.3568 +vn 0.4089 0.6617 -0.6284 +vn -0.1024 0.9435 -0.3151 +vn -0.1876 0.7947 -0.5773 +vn -0.4713 0.6617 -0.5831 +vn -0.3313 0.9435 0.0000 +vn -0.6071 0.7947 0.0000 +vn -0.7002 0.6617 0.2680 +vn -0.1024 0.9435 0.3151 +vn -0.1876 0.7947 0.5773 +vn 0.0385 0.6617 0.7488 +vn 0.2680 0.9435 0.1947 +vn 0.4911 0.7947 0.3568 +vn 0.7240 0.6617 0.1947 +vn 0.8897 0.3304 -0.3151 +vn 0.7947 0.1876 -0.5773 +vn 0.5746 0.3304 -0.7488 +vn -0.0247 0.3304 -0.9435 +vn -0.3035 0.1876 -0.9342 +vn -0.5346 0.3304 -0.7779 +vn -0.9050 0.3304 -0.2680 +vn -0.9822 0.1876 0.0000 +vn -0.9050 0.3304 0.2680 +vn -0.5346 0.3304 0.7779 +vn -0.3035 0.1876 0.9342 +vn -0.0247 0.3304 0.9435 +vn 0.5746 0.3304 0.7488 +vn 0.7947 0.1876 0.5773 +vn 0.8897 0.3304 0.3151 +vn 0.3066 0.1256 -0.9435 +vn 0.3035 -0.1876 -0.9342 +vn 0.0247 -0.3304 -0.9435 +vn -0.8026 0.1256 -0.5831 +vn -0.7947 -0.1876 -0.5773 +vn -0.8897 -0.3304 -0.3151 +vn -0.8026 0.1256 0.5831 +vn -0.7947 -0.1876 0.5773 +vn -0.5746 -0.3304 0.7488 +vn 0.3066 0.1256 0.9435 +vn 0.3035 -0.1876 0.9342 +vn 0.5346 -0.3304 0.7779 +vn 0.9921 0.1256 0.0000 +vn 0.9822 -0.1876 0.0000 +vn 0.9050 -0.3304 -0.2680 +vn 0.4713 -0.6617 -0.5831 +vn 0.1876 -0.7947 -0.5773 +vn -0.0385 -0.6617 -0.7488 +vn -0.4089 -0.6617 -0.6284 +vn -0.4911 -0.7947 -0.3568 +vn -0.7240 -0.6617 -0.1947 +vn -0.7240 -0.6617 0.1947 +vn -0.4911 -0.7947 0.3568 +vn -0.4089 -0.6617 0.6284 +vn 0.7002 -0.6617 -0.2680 +vn 0.6071 -0.7947 0.0000 +vn 0.3313 -0.9435 0.0000 +vn -0.0385 -0.6617 0.7488 +vn 0.1876 -0.7947 0.5773 +vn 0.4713 -0.6617 0.5831 +usemtl ball +s off +f 1//1 14//1 13//1 +f 2//2 14//2 16//2 +f 1//3 13//3 18//3 +f 1//4 18//4 20//4 +f 1//5 20//5 17//5 +f 2//6 16//6 23//6 +f 3//7 15//7 25//7 +f 4//8 19//8 27//8 +f 5//9 21//9 29//9 +f 6//10 22//10 31//10 +f 2//11 23//11 26//11 +f 3//12 25//12 28//12 +f 4//13 27//13 30//13 +f 5//14 29//14 32//14 +f 6//15 31//15 24//15 +f 7//16 33//16 38//16 +f 8//17 34//17 40//17 +f 9//18 35//18 41//18 +f 10//19 36//19 42//19 +f 11//20 37//20 39//20 +f 39//21 42//21 12//21 +f 39//22 37//22 42//22 +f 37//23 10//23 42//23 +f 42//24 41//24 12//24 +f 42//25 36//25 41//25 +f 36//26 9//26 41//26 +f 41//27 40//27 12//27 +f 41//28 35//28 40//28 +f 35//29 8//29 40//29 +f 40//30 38//30 12//30 +f 40//31 34//31 38//31 +f 34//32 7//32 38//32 +f 38//33 39//33 12//33 +f 38//34 33//34 39//34 +f 33//35 11//35 39//35 +f 24//36 37//36 11//36 +f 24//37 31//37 37//37 +f 31//38 10//38 37//38 +f 32//39 36//39 10//39 +f 32//40 29//40 36//40 +f 29//41 9//41 36//41 +f 30//42 35//42 9//42 +f 30//43 27//43 35//43 +f 27//44 8//44 35//44 +f 28//45 34//45 8//45 +f 28//46 25//46 34//46 +f 25//47 7//47 34//47 +f 26//48 33//48 7//48 +f 26//49 23//49 33//49 +f 23//50 11//50 33//50 +f 31//51 32//51 10//51 +f 31//52 22//52 32//52 +f 22//53 5//53 32//53 +f 29//54 30//54 9//54 +f 29//55 21//55 30//55 +f 21//56 4//56 30//56 +f 27//57 28//57 8//57 +f 27//58 19//58 28//58 +f 19//59 3//59 28//59 +f 25//60 26//60 7//60 +f 25//61 15//61 26//61 +f 15//62 2//62 26//62 +f 23//63 24//63 11//63 +f 23//64 16//64 24//64 +f 16//65 6//65 24//65 +f 17//66 22//66 6//66 +f 17//67 20//67 22//67 +f 20//68 5//68 22//68 +f 20//69 21//69 5//69 +f 20//70 18//70 21//70 +f 18//71 4//71 21//71 +f 18//72 19//72 4//72 +f 18//73 13//73 19//73 +f 13//74 3//74 19//74 +f 16//75 17//75 6//75 +f 16//76 14//76 17//76 +f 14//77 1//77 17//77 +f 13//78 15//78 3//78 +f 13//79 14//79 15//79 +f 14//80 2//80 15//80 +o Cylinder.001 +v 0.000000 0.512132 -0.291421 +v 0.000000 0.158579 0.062132 +v 0.009755 0.511453 -0.292101 +v 0.009755 0.157899 0.061453 +v 0.019134 0.509441 -0.294113 +v 0.019134 0.155887 0.059441 +v 0.027779 0.506174 -0.297380 +v 0.027779 0.152620 0.056174 +v 0.035355 0.501777 -0.301777 +v 0.035355 0.148223 0.051777 +v 0.041573 0.496419 -0.307134 +v 0.041573 0.142866 0.046419 +v 0.046194 0.490307 -0.313247 +v 0.046194 0.136753 0.040307 +v 0.049039 0.483674 -0.319879 +v 0.049039 0.130121 0.033674 +v 0.050000 0.476777 -0.326777 +v 0.050000 0.123223 0.026777 +v 0.049039 0.469879 -0.333674 +v 0.049039 0.116326 0.019879 +v 0.046194 0.463247 -0.340307 +v 0.046194 0.109693 0.013247 +v 0.041573 0.457134 -0.346419 +v 0.041573 0.103581 0.007134 +v 0.035355 0.451777 -0.351777 +v 0.035355 0.098223 0.001777 +v 0.027779 0.447380 -0.356174 +v 0.027779 0.093826 -0.002620 +v 0.019134 0.444113 -0.359441 +v 0.019134 0.090559 -0.005887 +v 0.009755 0.442101 -0.361453 +v 0.009755 0.088547 -0.007899 +v -0.000000 0.441421 -0.362132 +v -0.000000 0.087868 -0.008579 +v -0.009755 0.442101 -0.361453 +v -0.009755 0.088547 -0.007899 +v -0.019134 0.444113 -0.359441 +v -0.019134 0.090559 -0.005887 +v -0.027779 0.447380 -0.356174 +v -0.027779 0.093826 -0.002620 +v -0.035355 0.451777 -0.351777 +v -0.035355 0.098223 0.001777 +v -0.041574 0.457134 -0.346419 +v -0.041574 0.103581 0.007134 +v -0.046194 0.463247 -0.340307 +v -0.046194 0.109693 0.013247 +v -0.049039 0.469879 -0.333674 +v -0.049039 0.116326 0.019879 +v -0.050000 0.476777 -0.326777 +v -0.050000 0.123223 0.026777 +v -0.049039 0.483674 -0.319879 +v -0.049039 0.130121 0.033674 +v -0.046194 0.490307 -0.313247 +v -0.046194 0.136753 0.040307 +v -0.041573 0.496419 -0.307134 +v -0.041573 0.142866 0.046419 +v -0.035355 0.501777 -0.301777 +v -0.035355 0.148223 0.051777 +v -0.027778 0.506174 -0.297380 +v -0.027778 0.152620 0.056174 +v -0.019134 0.509441 -0.294113 +v -0.019134 0.155887 0.059441 +v -0.009754 0.511453 -0.292101 +v -0.009754 0.157899 0.061453 +vn 0.0980 0.7037 0.7037 +vn 0.2903 0.6767 0.6767 +vn 0.4714 0.6236 0.6236 +vn 0.6344 0.5466 0.5466 +vn 0.7730 0.4486 0.4486 +vn 0.8819 0.3333 0.3333 +vn 0.9569 0.2053 0.2053 +vn 0.9952 0.0693 0.0693 +vn 0.9952 -0.0693 -0.0693 +vn 0.9569 -0.2053 -0.2053 +vn 0.8819 -0.3333 -0.3333 +vn 0.7730 -0.4486 -0.4486 +vn 0.6344 -0.5466 -0.5466 +vn 0.4714 -0.6236 -0.6236 +vn 0.2903 -0.6767 -0.6767 +vn 0.0980 -0.7037 -0.7037 +vn -0.0980 -0.7037 -0.7037 +vn -0.2903 -0.6767 -0.6767 +vn -0.4714 -0.6236 -0.6236 +vn -0.6344 -0.5466 -0.5466 +vn -0.7730 -0.4486 -0.4486 +vn -0.8819 -0.3333 -0.3333 +vn -0.9569 -0.2053 -0.2053 +vn -0.9952 -0.0693 -0.0693 +vn -0.9952 0.0693 0.0693 +vn -0.9569 0.2053 0.2053 +vn -0.8819 0.3333 0.3333 +vn -0.7730 0.4486 0.4486 +vn -0.6344 0.5466 0.5466 +vn -0.4714 0.6236 0.6236 +vn -0.0000 -0.7071 0.7071 +vn -0.2903 0.6767 0.6767 +vn -0.0980 0.7037 0.7037 +vn -0.0000 0.7071 -0.7071 +usemtl rod +s off +f 43//81 44//81 46//81 45//81 +f 45//82 46//82 48//82 47//82 +f 47//83 48//83 50//83 49//83 +f 49//84 50//84 52//84 51//84 +f 51//85 52//85 54//85 53//85 +f 53//86 54//86 56//86 55//86 +f 55//87 56//87 58//87 57//87 +f 57//88 58//88 60//88 59//88 +f 59//89 60//89 62//89 61//89 +f 61//90 62//90 64//90 63//90 +f 63//91 64//91 66//91 65//91 +f 65//92 66//92 68//92 67//92 +f 67//93 68//93 70//93 69//93 +f 69//94 70//94 72//94 71//94 +f 71//95 72//95 74//95 73//95 +f 73//96 74//96 76//96 75//96 +f 75//97 76//97 78//97 77//97 +f 77//98 78//98 80//98 79//98 +f 79//99 80//99 82//99 81//99 +f 81//100 82//100 84//100 83//100 +f 83//101 84//101 86//101 85//101 +f 85//102 86//102 88//102 87//102 +f 87//103 88//103 90//103 89//103 +f 89//104 90//104 92//104 91//104 +f 91//105 92//105 94//105 93//105 +f 93//106 94//106 96//106 95//106 +f 95//107 96//107 98//107 97//107 +f 97//108 98//108 100//108 99//108 +f 99//109 100//109 102//109 101//109 +f 101//110 102//110 104//110 103//110 +f 46//111 44//111 106//111 104//111 102//111 100//111 98//111 96//111 94//111 92//111 90//111 88//111 86//111 84//111 82//111 80//111 78//111 76//111 74//111 72//111 70//111 68//111 66//111 64//111 62//111 60//111 58//111 56//111 54//111 52//111 50//111 48//111 +f 103//112 104//112 106//112 105//112 +f 105//113 106//113 44//113 43//113 +f 43//114 45//114 47//114 49//114 51//114 53//114 55//114 57//114 59//114 61//114 63//114 65//114 67//114 69//114 71//114 73//114 75//114 77//114 79//114 81//114 83//114 85//114 87//114 89//114 91//114 93//114 95//114 97//114 99//114 101//114 103//114 105//114 +o Cylinder +v 0.000000 0.558579 -0.362132 +v 0.000000 0.912132 -0.008579 +v 0.009755 0.557899 -0.361453 +v 0.009755 0.911453 -0.007899 +v 0.019134 0.555887 -0.359441 +v 0.019134 0.909441 -0.005887 +v 0.027779 0.552620 -0.356174 +v 0.027779 0.906174 -0.002620 +v 0.035355 0.548223 -0.351777 +v 0.035355 0.901777 0.001777 +v 0.041573 0.542866 -0.346419 +v 0.041573 0.896419 0.007134 +v 0.046194 0.536753 -0.340307 +v 0.046194 0.890307 0.013247 +v 0.049039 0.530121 -0.333674 +v 0.049039 0.883674 0.019879 +v 0.050000 0.523223 -0.326777 +v 0.050000 0.876777 0.026777 +v 0.049039 0.516326 -0.319879 +v 0.049039 0.869879 0.033674 +v 0.046194 0.509693 -0.313247 +v 0.046194 0.863247 0.040307 +v 0.041573 0.503581 -0.307134 +v 0.041573 0.857134 0.046419 +v 0.035355 0.498223 -0.301777 +v 0.035355 0.851777 0.051777 +v 0.027779 0.493826 -0.297380 +v 0.027779 0.847380 0.056174 +v 0.019134 0.490559 -0.294113 +v 0.019134 0.844113 0.059441 +v 0.009755 0.488547 -0.292101 +v 0.009755 0.842101 0.061453 +v -0.000000 0.487868 -0.291421 +v -0.000000 0.841421 0.062132 +v -0.009755 0.488547 -0.292101 +v -0.009755 0.842101 0.061453 +v -0.019134 0.490559 -0.294113 +v -0.019134 0.844113 0.059441 +v -0.027779 0.493826 -0.297380 +v -0.027779 0.847380 0.056174 +v -0.035355 0.498223 -0.301777 +v -0.035355 0.851777 0.051777 +v -0.041574 0.503581 -0.307134 +v -0.041574 0.857134 0.046419 +v -0.046194 0.509693 -0.313247 +v -0.046194 0.863247 0.040307 +v -0.049039 0.516326 -0.319879 +v -0.049039 0.869879 0.033674 +v -0.050000 0.523223 -0.326777 +v -0.050000 0.876777 0.026777 +v -0.049039 0.530121 -0.333674 +v -0.049039 0.883674 0.019879 +v -0.046194 0.536753 -0.340307 +v -0.046194 0.890307 0.013247 +v -0.041573 0.542866 -0.346419 +v -0.041573 0.896419 0.007134 +v -0.035355 0.548223 -0.351777 +v -0.035355 0.901777 0.001777 +v -0.027778 0.552620 -0.356174 +v -0.027778 0.906174 -0.002620 +v -0.019134 0.555887 -0.359441 +v -0.019134 0.909441 -0.005887 +v -0.009754 0.557899 -0.361453 +v -0.009754 0.911453 -0.007899 +vn 0.0980 0.7037 -0.7037 +vn 0.2903 0.6767 -0.6767 +vn 0.4714 0.6236 -0.6236 +vn 0.6344 0.5466 -0.5466 +vn 0.7730 0.4486 -0.4486 +vn 0.8819 0.3333 -0.3333 +vn 0.9569 0.2053 -0.2053 +vn 0.9952 0.0693 -0.0693 +vn 0.9952 -0.0693 0.0693 +vn 0.9569 -0.2053 0.2053 +vn 0.8819 -0.3333 0.3333 +vn 0.7730 -0.4486 0.4486 +vn 0.6344 -0.5466 0.5466 +vn 0.4714 -0.6236 0.6236 +vn 0.2903 -0.6767 0.6767 +vn 0.0980 -0.7037 0.7037 +vn -0.0980 -0.7037 0.7037 +vn -0.2903 -0.6767 0.6767 +vn -0.4714 -0.6236 0.6236 +vn -0.6344 -0.5466 0.5466 +vn -0.7730 -0.4486 0.4486 +vn -0.8819 -0.3333 0.3333 +vn -0.9569 -0.2053 0.2053 +vn -0.9952 -0.0693 0.0693 +vn -0.9952 0.0693 -0.0693 +vn -0.9569 0.2053 -0.2053 +vn -0.8819 0.3333 -0.3333 +vn -0.7730 0.4486 -0.4486 +vn -0.6344 0.5466 -0.5466 +vn -0.4714 0.6236 -0.6236 +vn -0.0000 0.7071 0.7071 +vn -0.2903 0.6767 -0.6767 +vn -0.0980 0.7037 -0.7037 +vn 0.0000 -0.7071 -0.7071 +usemtl rod +s off +f 107//115 108//115 110//115 109//115 +f 109//116 110//116 112//116 111//116 +f 111//117 112//117 114//117 113//117 +f 113//118 114//118 116//118 115//118 +f 115//119 116//119 118//119 117//119 +f 117//120 118//120 120//120 119//120 +f 119//121 120//121 122//121 121//121 +f 121//122 122//122 124//122 123//122 +f 123//123 124//123 126//123 125//123 +f 125//124 126//124 128//124 127//124 +f 127//125 128//125 130//125 129//125 +f 129//126 130//126 132//126 131//126 +f 131//127 132//127 134//127 133//127 +f 133//128 134//128 136//128 135//128 +f 135//129 136//129 138//129 137//129 +f 137//130 138//130 140//130 139//130 +f 139//131 140//131 142//131 141//131 +f 141//132 142//132 144//132 143//132 +f 143//133 144//133 146//133 145//133 +f 145//134 146//134 148//134 147//134 +f 147//135 148//135 150//135 149//135 +f 149//136 150//136 152//136 151//136 +f 151//137 152//137 154//137 153//137 +f 153//138 154//138 156//138 155//138 +f 155//139 156//139 158//139 157//139 +f 157//140 158//140 160//140 159//140 +f 159//141 160//141 162//141 161//141 +f 161//142 162//142 164//142 163//142 +f 163//143 164//143 166//143 165//143 +f 165//144 166//144 168//144 167//144 +f 110//145 108//145 170//145 168//145 166//145 164//145 162//145 160//145 158//145 156//145 154//145 152//145 150//145 148//145 146//145 144//145 142//145 140//145 138//145 136//145 134//145 132//145 130//145 128//145 126//145 124//145 122//145 120//145 118//145 116//145 114//145 112//145 +f 167//146 168//146 170//146 169//146 +f 169//147 170//147 108//147 107//147 +f 107//148 109//148 111//148 113//148 115//148 117//148 119//148 121//148 123//148 125//148 127//148 129//148 131//148 133//148 135//148 137//148 139//148 141//148 143//148 145//148 147//148 149//148 151//148 153//148 155//148 157//148 159//148 161//148 163//148 165//148 167//148 169//148 +o Icosphere.001 +v 0.000000 0.000000 0.000000 +v 0.108541 0.082917 0.078859 +v -0.041458 0.082917 0.127597 +v -0.134164 0.082918 0.000000 +v -0.041458 0.082917 -0.127597 +v 0.108541 0.082917 -0.078859 +v 0.041458 0.217083 0.127597 +v -0.108541 0.217083 0.078859 +v -0.108541 0.217083 -0.078859 +v 0.041458 0.217083 -0.127597 +v 0.134164 0.217082 0.000000 +v 0.000000 0.300000 0.000000 +v -0.024368 0.022402 0.074999 +v 0.063798 0.022402 0.046352 +v 0.039430 0.071139 0.121352 +v 0.127597 0.071140 0.000000 +v 0.063798 0.022402 -0.046352 +v -0.078859 0.022402 0.000000 +v -0.103228 0.071140 0.075000 +v -0.024368 0.022402 -0.074999 +v -0.103228 0.071140 -0.075000 +v 0.039430 0.071139 -0.121352 +v 0.142659 0.150000 0.046352 +v 0.142659 0.150000 -0.046352 +v 0.000000 0.150000 0.150000 +v 0.088168 0.150000 0.121353 +v -0.142659 0.150000 0.046352 +v -0.088168 0.150000 0.121353 +v -0.088168 0.150000 -0.121353 +v -0.142659 0.150000 -0.046352 +v 0.088168 0.150000 -0.121353 +v 0.000000 0.150000 -0.150000 +v 0.103228 0.228860 0.075000 +v -0.039430 0.228861 0.121352 +v -0.127597 0.228860 0.000000 +v -0.039430 0.228861 -0.121352 +v 0.103228 0.228860 -0.075000 +v 0.024368 0.277598 0.074999 +v 0.078859 0.277598 0.000000 +v -0.063798 0.277598 0.046352 +v -0.063798 0.277598 -0.046352 +v 0.024368 0.277598 -0.074999 +vn 0.1024 -0.9435 0.3151 +vn 0.7002 -0.6617 0.2680 +vn -0.2680 -0.9435 0.1947 +vn -0.2680 -0.9435 -0.1947 +vn 0.1024 -0.9435 -0.3151 +vn 0.9050 -0.3304 0.2680 +vn 0.0247 -0.3304 0.9435 +vn -0.8897 -0.3304 0.3151 +vn -0.5746 -0.3304 -0.7488 +vn 0.5346 -0.3304 -0.7779 +vn 0.8026 -0.1256 0.5831 +vn -0.3066 -0.1256 0.9435 +vn -0.9921 -0.1256 0.0000 +vn -0.3066 -0.1256 -0.9435 +vn 0.8026 -0.1256 -0.5831 +vn 0.4089 0.6617 0.6284 +vn -0.4713 0.6617 0.5831 +vn -0.7002 0.6617 -0.2680 +vn 0.0385 0.6617 -0.7488 +vn 0.7240 0.6617 -0.1947 +vn 0.2680 0.9435 -0.1947 +vn 0.4911 0.7947 -0.3568 +vn 0.4089 0.6617 -0.6284 +vn -0.1024 0.9435 -0.3151 +vn -0.1876 0.7947 -0.5773 +vn -0.4713 0.6617 -0.5831 +vn -0.3313 0.9435 0.0000 +vn -0.6071 0.7947 0.0000 +vn -0.7002 0.6617 0.2680 +vn -0.1024 0.9435 0.3151 +vn -0.1876 0.7947 0.5773 +vn 0.0385 0.6617 0.7488 +vn 0.2680 0.9435 0.1947 +vn 0.4911 0.7947 0.3568 +vn 0.7240 0.6617 0.1947 +vn 0.8897 0.3304 -0.3151 +vn 0.7947 0.1876 -0.5773 +vn 0.5746 0.3304 -0.7488 +vn -0.0247 0.3304 -0.9435 +vn -0.3035 0.1876 -0.9342 +vn -0.5346 0.3304 -0.7779 +vn -0.9050 0.3304 -0.2680 +vn -0.9822 0.1876 0.0000 +vn -0.9050 0.3304 0.2680 +vn -0.5346 0.3304 0.7779 +vn -0.3035 0.1876 0.9342 +vn -0.0247 0.3304 0.9435 +vn 0.5746 0.3304 0.7488 +vn 0.7947 0.1876 0.5773 +vn 0.8897 0.3304 0.3151 +vn 0.3066 0.1256 -0.9435 +vn 0.3035 -0.1876 -0.9342 +vn 0.0247 -0.3304 -0.9435 +vn -0.8026 0.1256 -0.5831 +vn -0.7947 -0.1876 -0.5773 +vn -0.8897 -0.3304 -0.3151 +vn -0.8026 0.1256 0.5831 +vn -0.7947 -0.1876 0.5773 +vn -0.5746 -0.3304 0.7488 +vn 0.3066 0.1256 0.9435 +vn 0.3035 -0.1876 0.9342 +vn 0.5346 -0.3304 0.7779 +vn 0.9921 0.1256 0.0000 +vn 0.9822 -0.1876 0.0000 +vn 0.9050 -0.3304 -0.2680 +vn 0.4713 -0.6617 -0.5831 +vn 0.1876 -0.7947 -0.5773 +vn -0.0385 -0.6617 -0.7488 +vn -0.4089 -0.6617 -0.6284 +vn -0.4911 -0.7947 -0.3568 +vn -0.7240 -0.6617 -0.1947 +vn -0.7240 -0.6617 0.1947 +vn -0.4911 -0.7947 0.3568 +vn -0.4089 -0.6617 0.6284 +vn 0.7002 -0.6617 -0.2680 +vn 0.6071 -0.7947 0.0000 +vn 0.3313 -0.9435 0.0000 +vn -0.0385 -0.6617 0.7488 +vn 0.1876 -0.7947 0.5773 +vn 0.4713 -0.6617 0.5831 +usemtl ball +s off +f 171//149 184//149 183//149 +f 172//150 184//150 186//150 +f 171//151 183//151 188//151 +f 171//152 188//152 190//152 +f 171//153 190//153 187//153 +f 172//154 186//154 193//154 +f 173//155 185//155 195//155 +f 174//156 189//156 197//156 +f 175//157 191//157 199//157 +f 176//158 192//158 201//158 +f 172//159 193//159 196//159 +f 173//160 195//160 198//160 +f 174//161 197//161 200//161 +f 175//162 199//162 202//162 +f 176//163 201//163 194//163 +f 177//164 203//164 208//164 +f 178//165 204//165 210//165 +f 179//166 205//166 211//166 +f 180//167 206//167 212//167 +f 181//168 207//168 209//168 +f 209//169 212//169 182//169 +f 209//170 207//170 212//170 +f 207//171 180//171 212//171 +f 212//172 211//172 182//172 +f 212//173 206//173 211//173 +f 206//174 179//174 211//174 +f 211//175 210//175 182//175 +f 211//176 205//176 210//176 +f 205//177 178//177 210//177 +f 210//178 208//178 182//178 +f 210//179 204//179 208//179 +f 204//180 177//180 208//180 +f 208//181 209//181 182//181 +f 208//182 203//182 209//182 +f 203//183 181//183 209//183 +f 194//184 207//184 181//184 +f 194//185 201//185 207//185 +f 201//186 180//186 207//186 +f 202//187 206//187 180//187 +f 202//188 199//188 206//188 +f 199//189 179//189 206//189 +f 200//190 205//190 179//190 +f 200//191 197//191 205//191 +f 197//192 178//192 205//192 +f 198//193 204//193 178//193 +f 198//194 195//194 204//194 +f 195//195 177//195 204//195 +f 196//196 203//196 177//196 +f 196//197 193//197 203//197 +f 193//198 181//198 203//198 +f 201//199 202//199 180//199 +f 201//200 192//200 202//200 +f 192//201 175//201 202//201 +f 199//202 200//202 179//202 +f 199//203 191//203 200//203 +f 191//204 174//204 200//204 +f 197//205 198//205 178//205 +f 197//206 189//206 198//206 +f 189//207 173//207 198//207 +f 195//208 196//208 177//208 +f 195//209 185//209 196//209 +f 185//210 172//210 196//210 +f 193//211 194//211 181//211 +f 193//212 186//212 194//212 +f 186//213 176//213 194//213 +f 187//214 192//214 176//214 +f 187//215 190//215 192//215 +f 190//216 175//216 192//216 +f 190//217 191//217 175//217 +f 190//218 188//218 191//218 +f 188//219 174//219 191//219 +f 188//220 189//220 174//220 +f 188//221 183//221 189//221 +f 183//222 173//222 189//222 +f 186//223 187//223 176//223 +f 186//224 184//224 187//224 +f 184//225 171//225 187//225 +f 183//226 185//226 173//226 +f 183//227 184//227 185//227 +f 184//228 172//228 185//228 +o Icosphere +v 0.000000 0.700000 0.000000 +v 0.108541 0.782917 0.078859 +v -0.041458 0.782917 0.127597 +v -0.134164 0.782918 0.000000 +v -0.041458 0.782917 -0.127597 +v 0.108541 0.782917 -0.078859 +v 0.041458 0.917083 0.127597 +v -0.108541 0.917083 0.078859 +v -0.108541 0.917083 -0.078859 +v 0.041458 0.917083 -0.127597 +v 0.134164 0.917082 0.000000 +v 0.000000 1.000000 0.000000 +v -0.024368 0.722402 0.074999 +v 0.063798 0.722402 0.046352 +v 0.039430 0.771139 0.121352 +v 0.127597 0.771140 0.000000 +v 0.063798 0.722402 -0.046352 +v -0.078859 0.722402 0.000000 +v -0.103228 0.771140 0.075000 +v -0.024368 0.722402 -0.074999 +v -0.103228 0.771140 -0.075000 +v 0.039430 0.771139 -0.121352 +v 0.142659 0.850000 0.046352 +v 0.142659 0.850000 -0.046352 +v 0.000000 0.850000 0.150000 +v 0.088168 0.850000 0.121353 +v -0.142659 0.850000 0.046352 +v -0.088168 0.850000 0.121353 +v -0.088168 0.850000 -0.121353 +v -0.142659 0.850000 -0.046352 +v 0.088168 0.850000 -0.121353 +v 0.000000 0.850000 -0.150000 +v 0.103228 0.928860 0.075000 +v -0.039430 0.928861 0.121352 +v -0.127597 0.928860 0.000000 +v -0.039430 0.928861 -0.121352 +v 0.103228 0.928860 -0.075000 +v 0.024368 0.977598 0.074999 +v 0.078859 0.977598 0.000000 +v -0.063798 0.977598 0.046352 +v -0.063798 0.977598 -0.046352 +v 0.024368 0.977598 -0.074999 +vn 0.1024 -0.9435 0.3151 +vn 0.7002 -0.6617 0.2680 +vn -0.2680 -0.9435 0.1947 +vn -0.2680 -0.9435 -0.1947 +vn 0.1024 -0.9435 -0.3151 +vn 0.9050 -0.3304 0.2680 +vn 0.0247 -0.3304 0.9435 +vn -0.8897 -0.3304 0.3151 +vn -0.5746 -0.3304 -0.7488 +vn 0.5346 -0.3304 -0.7779 +vn 0.8026 -0.1256 0.5831 +vn -0.3066 -0.1256 0.9435 +vn -0.9921 -0.1256 0.0000 +vn -0.3066 -0.1256 -0.9435 +vn 0.8026 -0.1256 -0.5831 +vn 0.4089 0.6617 0.6284 +vn -0.4713 0.6617 0.5831 +vn -0.7002 0.6617 -0.2680 +vn 0.0385 0.6617 -0.7488 +vn 0.7240 0.6617 -0.1947 +vn 0.2680 0.9435 -0.1947 +vn 0.4911 0.7947 -0.3568 +vn 0.4089 0.6617 -0.6284 +vn -0.1024 0.9435 -0.3151 +vn -0.1876 0.7947 -0.5773 +vn -0.4713 0.6617 -0.5831 +vn -0.3313 0.9435 0.0000 +vn -0.6071 0.7947 0.0000 +vn -0.7002 0.6617 0.2680 +vn -0.1024 0.9435 0.3151 +vn -0.1876 0.7947 0.5773 +vn 0.0385 0.6617 0.7488 +vn 0.2680 0.9435 0.1947 +vn 0.4911 0.7947 0.3568 +vn 0.7240 0.6617 0.1947 +vn 0.8897 0.3304 -0.3151 +vn 0.7947 0.1876 -0.5773 +vn 0.5746 0.3304 -0.7488 +vn -0.0247 0.3304 -0.9435 +vn -0.3035 0.1876 -0.9342 +vn -0.5346 0.3304 -0.7779 +vn -0.9050 0.3304 -0.2680 +vn -0.9822 0.1876 0.0000 +vn -0.9050 0.3304 0.2680 +vn -0.5346 0.3304 0.7779 +vn -0.3035 0.1876 0.9342 +vn -0.0247 0.3304 0.9435 +vn 0.5746 0.3304 0.7488 +vn 0.7947 0.1876 0.5773 +vn 0.8897 0.3304 0.3151 +vn 0.3066 0.1256 -0.9435 +vn 0.3035 -0.1876 -0.9342 +vn 0.0247 -0.3304 -0.9435 +vn -0.8026 0.1256 -0.5831 +vn -0.7947 -0.1876 -0.5773 +vn -0.8897 -0.3304 -0.3151 +vn -0.8026 0.1256 0.5831 +vn -0.7947 -0.1876 0.5773 +vn -0.5746 -0.3304 0.7488 +vn 0.3066 0.1256 0.9435 +vn 0.3035 -0.1876 0.9342 +vn 0.5346 -0.3304 0.7779 +vn 0.9921 0.1256 0.0000 +vn 0.9822 -0.1876 0.0000 +vn 0.9050 -0.3304 -0.2680 +vn 0.4713 -0.6617 -0.5831 +vn 0.1876 -0.7947 -0.5773 +vn -0.0385 -0.6617 -0.7488 +vn -0.4089 -0.6617 -0.6284 +vn -0.4911 -0.7947 -0.3568 +vn -0.7240 -0.6617 -0.1947 +vn -0.7240 -0.6617 0.1947 +vn -0.4911 -0.7947 0.3568 +vn -0.4089 -0.6617 0.6284 +vn 0.7002 -0.6617 -0.2680 +vn 0.6071 -0.7947 0.0000 +vn 0.3313 -0.9435 0.0000 +vn -0.0385 -0.6617 0.7488 +vn 0.1876 -0.7947 0.5773 +vn 0.4713 -0.6617 0.5831 +usemtl ball +s off +f 213//229 226//229 225//229 +f 214//230 226//230 228//230 +f 213//231 225//231 230//231 +f 213//232 230//232 232//232 +f 213//233 232//233 229//233 +f 214//234 228//234 235//234 +f 215//235 227//235 237//235 +f 216//236 231//236 239//236 +f 217//237 233//237 241//237 +f 218//238 234//238 243//238 +f 214//239 235//239 238//239 +f 215//240 237//240 240//240 +f 216//241 239//241 242//241 +f 217//242 241//242 244//242 +f 218//243 243//243 236//243 +f 219//244 245//244 250//244 +f 220//245 246//245 252//245 +f 221//246 247//246 253//246 +f 222//247 248//247 254//247 +f 223//248 249//248 251//248 +f 251//249 254//249 224//249 +f 251//250 249//250 254//250 +f 249//251 222//251 254//251 +f 254//252 253//252 224//252 +f 254//253 248//253 253//253 +f 248//254 221//254 253//254 +f 253//255 252//255 224//255 +f 253//256 247//256 252//256 +f 247//257 220//257 252//257 +f 252//258 250//258 224//258 +f 252//259 246//259 250//259 +f 246//260 219//260 250//260 +f 250//261 251//261 224//261 +f 250//262 245//262 251//262 +f 245//263 223//263 251//263 +f 236//264 249//264 223//264 +f 236//265 243//265 249//265 +f 243//266 222//266 249//266 +f 244//267 248//267 222//267 +f 244//268 241//268 248//268 +f 241//269 221//269 248//269 +f 242//270 247//270 221//270 +f 242//271 239//271 247//271 +f 239//272 220//272 247//272 +f 240//273 246//273 220//273 +f 240//274 237//274 246//274 +f 237//275 219//275 246//275 +f 238//276 245//276 219//276 +f 238//277 235//277 245//277 +f 235//278 223//278 245//278 +f 243//279 244//279 222//279 +f 243//280 234//280 244//280 +f 234//281 217//281 244//281 +f 241//282 242//282 221//282 +f 241//283 233//283 242//283 +f 233//284 216//284 242//284 +f 239//285 240//285 220//285 +f 239//286 231//286 240//286 +f 231//287 215//287 240//287 +f 237//288 238//288 219//288 +f 237//289 227//289 238//289 +f 227//290 214//290 238//290 +f 235//291 236//291 223//291 +f 235//292 228//292 236//292 +f 228//293 218//293 236//293 +f 229//294 234//294 218//294 +f 229//295 232//295 234//295 +f 232//296 217//296 234//296 +f 232//297 233//297 217//297 +f 232//298 230//298 233//298 +f 230//299 216//299 233//299 +f 230//300 231//300 216//300 +f 230//301 225//301 231//301 +f 225//302 215//302 231//302 +f 228//303 229//303 218//303 +f 228//304 226//304 229//304 +f 226//305 213//305 229//305 +f 225//306 227//306 215//306 +f 225//307 226//307 227//307 +f 226//308 214//308 227//308 diff --git a/jsuarez/extra/embyr_deprecated/embyr/tex/simple.glsl b/jsuarez/extra/embyr_deprecated/embyr/tex/simple.glsl new file mode 100644 index 00000000..2178a3d0 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/tex/simple.glsl @@ -0,0 +1,47 @@ +/* simple.glsl + +simple diffuse lighting based on laberts cosine law; see e.g.: + http://en.wikipedia.org/wiki/Lambertian_reflectance + http://en.wikipedia.org/wiki/Lambert%27s_cosine_law +*/ +---VERTEX SHADER------------------------------------------------------- +#ifdef GL_ES + precision highp float; +#endif + +attribute vec3 v_pos; +attribute vec3 v_normal; + +uniform mat4 modelview_mat; +uniform mat4 projection_mat; + +varying vec4 normal_vec; +varying vec4 vertex_pos; + +void main (void) { + //compute vertex position in eye_sapce and normalize normal vector + vec4 pos = modelview_mat * vec4(v_pos,1.0); + vertex_pos = pos; + normal_vec = vec4(v_normal,0.0); + gl_Position = projection_mat * pos; +} + + +---FRAGMENT SHADER----------------------------------------------------- +#ifdef GL_ES + precision highp float; +#endif + +varying vec4 normal_vec; +varying vec4 vertex_pos; + +uniform mat4 normal_mat; + +void main (void){ + //correct normal, and compute light vector (assume light at the eye) + vec4 v_normal = normalize( normal_mat * normal_vec ) ; + vec4 v_light = normalize( vec4(0,0,0,1) - vertex_pos ); + //reflectance based on lamberts law of cosine + float theta = clamp(dot(v_normal, v_light), 0.0, 1.0); + gl_FragColor = vec4(theta, theta, theta, 1.0); +} diff --git a/jsuarez/extra/embyr_deprecated/embyr/tex/stone.png b/jsuarez/extra/embyr_deprecated/embyr/tex/stone.png new file mode 100644 index 00000000..b3af07e7 Binary files /dev/null and b/jsuarez/extra/embyr_deprecated/embyr/tex/stone.png differ diff --git a/jsuarez/extra/embyr_deprecated/embyr/tex/water.png b/jsuarez/extra/embyr_deprecated/embyr/tex/water.png new file mode 100644 index 00000000..50c96fa0 Binary files /dev/null and b/jsuarez/extra/embyr_deprecated/embyr/tex/water.png differ diff --git a/jsuarez/extra/embyr_deprecated/embyr/tex/white.png b/jsuarez/extra/embyr_deprecated/embyr/tex/white.png new file mode 100644 index 00000000..22144619 Binary files /dev/null and b/jsuarez/extra/embyr_deprecated/embyr/tex/white.png differ diff --git a/jsuarez/extra/embyr_deprecated/embyr/texture.py b/jsuarez/extra/embyr_deprecated/embyr/texture.py new file mode 100644 index 00000000..1ef6dc7e --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/texture.py @@ -0,0 +1,57 @@ +from pdb import set_trace as T +from forge.embyr import utils as renderutils +from forge.embyr import embyr + +from forge.blade.lib import enums +from forge.blade.lib.enums import Neon + +class TextureInitializer(): + def __init__(self, sz, root='resource/', mipLevels=[16, 32, 64, 96, 128], barSz=2): + self.tileSz = sz + self.width = sz + self.barSz = barSz + self.root = root + self.mipLevels = mipLevels + + #self.material = RenderUtils.readRGB(root+'Material/textures.png') + self.material = self.textureTiles(mipLevels) + self.entity = self.textureEnum(enums.Entity, + root+'Entity/', mask=Neon.MASK.rgb) + proj = { + 'melee':renderutils.pgRead(root+'Action/melee.png', mask=Neon.MASK.rgb), + 'range':renderutils.pgRead(root+'Action/range.png', mask=Neon.MASK.rgb), + 'mage':renderutils.pgRead(root+'Action/mage.png', mask=Neon.MASK.rgb), + } + action = { + 'melee':renderutils.pgRead(root+'Action/meleeattack.png', mask=Neon.MASK.rgb), + 'range':renderutils.pgRead(root+'Action/rangeattack.png', mask=Neon.MASK.rgb), + 'mage':renderutils.pgRead(root+'Action/mageattack.png', mask=Neon.MASK.rgb), + } + + self.action = embyr.mips(action, mipLevels) + + #self.action = self.textureEnum(Enums.Entity, + # root+'Action/', mask=Color.MASK) + + def textureTiles(self, mipLevels): + reverse = {} + for mat in enums.Material: + mat = mat.value + texCoords = mat.tex + mat.tex = renderutils.pgRead(self.root + '/tiles/' + mat.tex + '.png') + reverse[mat.index] = mat.tex + return embyr.mips(reverse, self.mipLevels) + + def textureEnum(self, enum, path, mask=None, suffix='.png'): + reverse = {} + for color in enums.Neon.color12(): + name = color.name + texPath = path + 'neural' + name + suffix + reverse[name] = renderutils.pgRead(texPath, mask=mask) + for name in range(0, 256): + name = str(name) + texPath = path + 'neural' + name + suffix + reverse[name] = renderutils.pgRead(texPath, mask=mask) + return embyr.mips(reverse, self.mipLevels) + + diff --git a/jsuarez/extra/embyr_deprecated/embyr/transform.py b/jsuarez/extra/embyr_deprecated/embyr/transform.py new file mode 100644 index 00000000..1bcceac0 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/transform.py @@ -0,0 +1,177 @@ +from pdb import set_trace as T + +import numpy as np +import time +from kivy.uix.widget import Widget +from kivy.config import Config + +rad = 80 +sz = 500 +pi = 3.14159265 + +class Transform(Widget): + def __init__(self, + pos=[0, 0, 0.01], lookvec=[0, 0, 0], **kwargs): + super().__init__(**kwargs) + self.zoom = Zoom() + self.pan = Pan('left', self.zoom) + self.rotate = Rotate('right') + + self.t = 0 + self.pos0 = np.array(pos) + self.vec0 = np.array(lookvec) + self.posn = np.array(pos) + self.vecn = np.array(lookvec) + + + self.add_widget(self.pan) + self.add_widget(self.zoom) + self.add_widget(self.rotate) + self.button = None + + def update(self, dt): + self.t += dt + pos, vec = self.pos0, self.vec0 + + pos = self.zoom(pos, vec) + pos, vec = self.rotate(pos, vec) + pos, vec = self.pan(pos, vec) + + self.posn = pos + self.vecn = vec + return pos, vec + + #pos = self.zoom(pos, vec) + #pos, vec = self.pan(pos, vec) + #pos = self.rotate(pos, vec) + + return pos, vec + +class TouchWidget(Widget): + def __init__(self, button, **kwargs): + super().__init__(**kwargs) + self.x, self.y, self.xVol, self.yVol = 0, 0, 0, 0 + self.button = button + self.reset = True + + #Currently broken due to race condition? + #def on_touch_down(self, touch): + # if touch.button == self.button: + # self.xStart, self.yStart = touch.pos + + def on_touch_up(self, touch): + if touch.button == self.button: + self.x += self.xVol + self.y += self.yVol + self.xVol, self.yVol= 0, 0 + self.reset = True + + def on_touch_move(self, touch): + if self.reset: + self.xStart, self.yStart = touch.pos + self.reset = False + + if touch.button == self.button: + xEnd, yEnd = touch.pos + self.xVol = xEnd - self.xStart + self.yVol = yEnd - self.yStart + +class Zoom(Widget): + def __init__(self, **kwargs): + super().__init__(**kwargs) + self.zoom, self.delta = 1, 0.2 + + def clip(self, zoom, exprange=4): + return np.clip(zoom, 0.5**exprange, 2**exprange) + + def on_touch_down(self, touch): + if touch.button == 'scrollup': + self.zoom = self.clip(self.zoom + self.delta) + elif touch.button == 'scrolldown': + self.zoom = self.clip(self.zoom - self.delta) + + def __call__(self, pos, vec): + #return offvec * self.zoom + pos = vec + self.zoom*(pos - vec) + return pos + +class Pan(TouchWidget): + def __init__(self, button, zoom, **kwargs): + super().__init__(button, **kwargs) + self.x, self.y, self.xVol, self.yVol = 0, 0, 0, 0 + self.zoom = zoom + self.panx, self.pany = 0, 0 + + def on_touch_move(self, touch): + super().on_touch_move(touch) + self.xVol = int(self.xVol * self.zoom.zoom) + self.yVol = int(self.yVol * self.zoom.zoom) + + def __call__(self, pos, vec): + x = 10 * (self.x + self.xVol) / sz + z = 10 * (self.y + self.yVol) / sz + + pos0 = pos + vec0 = vec + + #pos = pos0 + np.array([x, 0, 0]) + #vec = vec0 + np.array([x, 0, 0]) + #return pos, vec + + #Depth vector + xx, _, zz = pos - vec + norm = np.sqrt(xx**2 + zz**2) + xx, zz = xx/norm, zz/norm + vec = np.array([xx, 0, zz]) + + #Horizontal component + unit_y = np.array([0, 1, 0]) + xh, _, zh = np.cross(vec, unit_y) + xh, zh = x*xh, x*zh + horiz = np.array([xh, 0, zh]) + + #Depth component + xd, zd = z*xx, z*zz + depth = np.array([xd, 0, zd]) + + delta = horiz + depth + #delta = np.array([xh, 0, zh]) + #delta = np.array([xd, 0, zd]) + return pos0 + delta, vec0 + delta + +class Rotate(TouchWidget): + def __call__(self, pos, vec): + x = (self.x + self.xVol) / sz + y = (self.y + self.yVol) / sz + #yclip = np.clip(y, -pi/2, 0) + yclip = -y + + xz_x = np.cos(x) + xz_z = np.sin(x) + + yz_y = np.cos(yclip) + yz_z = np.sin(yclip) + + xz = np.array([ + [xz_x, 0, -xz_z], + [0 , 1, 0 ], + [xz_z, 0, xz_x]]) + + yz = np.array([ + [1, 0, 0 ], + [0, yz_y, -yz_z], + [0, yz_z, yz_y]]) + + #Find cylindrical xz plane rotation + xx, _, zz = np.dot(xz, pos - vec) + + #Find spherical yz plane rotation + _, yy, _ = np.dot(yz, pos - vec) + + #For x, z: shrink to position of spherical rotation + #For y: use height from spherical rotation + #pos = np.array([xx*xz_norm, yy, zz*xz_norm]) + pos = np.array([xx, yy, zz]) + pos = vec + pos + return pos, vec + diff --git a/jsuarez/extra/embyr_deprecated/embyr/utils.py b/jsuarez/extra/embyr_deprecated/embyr/utils.py new file mode 100644 index 00000000..9fb29be5 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr/utils.py @@ -0,0 +1,51 @@ +from scipy.misc import imread +import pygame +import time + +from forge.blade.lib.utils import EDA +from forge.blade.lib.enums import Neon + +def readRGB(path): + return imread(path)[:, :, :3] + +def pgRead(path, mask=None): + try: + img = readRGB(path) + except FileNotFoundError: + return None + + img = pygame.pixelcopy.make_surface(img) + if mask is not None: + img.set_colorkey(mask) + + #For some reason, pygame loads images transformed + img = pygame.transform.flip(img, True, False) + img = pygame.transform.rotate(img, 90) + return img + +class Font: + def render(txt, size, color=Neon.GOLD.rgb): + pass + #return pygame.font.Font('freesansbold.tt', size).render(text, 1, color) + +class Fonts: + def __init__(self, font='freesansbold.ttf'): + sizes=(9, 12, 18, 24, 28, 32, 36) + fonts = [pygame.font.Font(font, sz) for sz in sizes] + self.tiny, self.small, self.normal, self.large, self.Large, self.huge, self.Huge = fonts + +class FPSTracker: + def __init__(self): + self.start = time.time() + self.eda = EDA(k=0.95) + self.fpsVal = 0.0 + + def update(self): + tick = time.time() - self.start + self.eda.update(1.0/tick) + self.fpsVal = self.eda.eda + self.start = time.time() + + @property + def fps(self): + return str(self.fpsVal)[:5] diff --git a/jsuarez/extra/embyr_deprecated/embyr2d/__init__.py b/jsuarez/extra/embyr_deprecated/embyr2d/__init__.py new file mode 100644 index 00000000..4da4a0b7 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr2d/__init__.py @@ -0,0 +1 @@ +from .application import Application diff --git a/jsuarez/extra/embyr_deprecated/embyr2d/application.py b/jsuarez/extra/embyr_deprecated/embyr2d/application.py new file mode 100644 index 00000000..97d817c9 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr2d/application.py @@ -0,0 +1,136 @@ +from pdb import set_trace as T +import pygame, time +import numpy as np + +from forge.embyr import embyr +from forge.embyr.modules import * + +class Client(embyr.Container): + def __init__(self, view, size, realm, step, dims, nAnim, **kwargs): + super().__init__(size, **kwargs) + self.W, self.H, self.side = dims + self.realm, self.step = realm, step + self.view, self.nAnim = view, nAnim + + offset = 16 * 8 + self.x, self.y, self.xVol, self.yVol = -offset, offset, 0, 0 + self.zoom, self.zoomVol = 1, 0 + + self.count = 0 + self.frame = 0 + self.frames = [] + self.vals = [] + self.init = time.time() + + def setup(self): + surf = self.view.renderTitle() + self.blit(surf, (0, 0)) + self.frame += 1 + self.flip() + + def render(self, t): + if self.frame == 0: + return self.setup() + if self.frame == 1: + time.sleep(2.5) + self.update() + + def update(self): + self.writeFrame() + self.trans = self.renderOffsets(self.H, self.H) + keyframe = self.count == 0 + if keyframe: + self.step() + + self.surf = self.view.render(self.realm, self.trans, keyframe) + + self.count = (self.count + 1) % (self.nAnim+1) + self.blit(self.surf, (0,0)) + self.flip() + self.frame += 1 + + def writeFrame(self): + NFRAMES=1800 + return + if self.frame < NFRAMES: + print('Frame: ', len(self.frames)) + frame = pygame.surfarray.array3d(pygame.transform.rotate(self.surf, 90)) + frame = np.fliplr(frame) + #frame = frame[:1024, 256:256+1024] + frame = frame[:1024, 1024+256:1024+256+1024] + self.frames.append(frame) + #pygame.image.save(self.screen, 'resource/data/lrframe'+str(self.frame)+'.png') + elif self.frame == NFRAMES: + import imageio + print('Saving MP4...') + imageio.mimwrite('swordfrag.mp4', self.frames, fps = 30) + print('Saved') + + def clipZoom(self, zoom): + return np.clip(zoom, 1.0, 8.0) + + def renderOffsets(self, W, H): + #Scale + zoom = self.clipZoom(self.zoom + self.zoomVol) + scaleX, scaleY = int(W*zoom), int(H*zoom) + + #Translate + deltaX = self.x + self.xVol - scaleX/2 + W/2 + deltaY = -self.y - self.yVol - scaleY/2 + H/2 + return scaleX, scaleY, deltaX, deltaY + + def on_touch_down(self, touch): + self.xStart, self.yStart = touch.pos + + def on_touch_up(self, touch): + if touch.button == 'left': + self.xVol, self.yVol= 0, 0 + xEnd, yEnd = touch.pos + self.x += xEnd - self.xStart + self.y += yEnd - self.yStart + elif touch.button == 'right': + self.zoom = self.clipZoom(self.zoom + self.zoomVol) + self.zoomVol = 0 + + def on_touch_move(self, touch): + if touch.button == 'left': + xEnd, yEnd = touch.pos + self.xVol = xEnd - self.xStart + self.yVol = yEnd - self.yStart + elif touch.button == 'right': + xEnd, yEnd = touch.pos + delta = (xEnd - self.xStart)/2 - (yEnd - self.yStart)/2 + self.zoomVol = delta/100 + + def on_key_down(self, *args): + text = args[3] + if text == 'i': + #Toggle isometric + trans = self.renderOffsets(self.H, self.H) + self.view.toggleEnv(trans) + elif text == 'p': + T() + elif text == '[': + self.view.leftScreenshot() + else: + #Toggle overlay + self.view.key(text) + +class Application(embyr.Application): + def __init__(self, size, realm, step, conf): + super().__init__(size) + self.realm, self.step = realm, step + self.conf = conf + + def build(self): + W, H, side = self.W, self.H, 256 + self.appSize = (self.W, self.H) + + self.title = 'Projekt: Godsword' + dims = (self.W-side, self.H-side, side) + canvas = Canvas(self.appSize, self.realm, dims, self.conf) + self.client = Client(canvas, self.appSize, self.realm, + self.step, dims, NANIM) + + self.loop(self.client.render) + return self.client diff --git a/jsuarez/extra/embyr_deprecated/embyr2d/embyr.py b/jsuarez/extra/embyr_deprecated/embyr2d/embyr.py new file mode 100644 index 00000000..e4b8b067 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr2d/embyr.py @@ -0,0 +1,341 @@ +#Author: Joseph Suarez + +from pdb import set_trace as T +from scipy.misc import imread +import numpy as np +import time + +import pygame +from pygame.surface import Surface + +import kivy as kv +from kivy.app import App +from kivy.uix.widget import Widget +from kivy.graphics.texture import Texture as kvTex +from kivy.graphics import Rectangle +from kivy.config import Config +from kivy.clock import Clock +from kivy.core.window import Window + +Config.set('input', 'mouse', 'mouse,multitouch_on_demand') + +#Wrapper for all Embyr programs +#Mandatory, required by kivy +class Application(kv.app.App): + def __init__(self, size): + super().__init__() + pygame.init() + self.size = size + self.W, self.H = size + Window.size = (self.W//2, self.H//2) + self.scale = 1.5 + + #Run a thunk forever + def loop(self, func, interval=0): + Clock.schedule_interval(func, interval) + + #Run a thunk once + def once(self, func, delay=0): + Clock.schedule_once(func, delay) + +#Wrapper for all draw canvas objects. +#Provides a seamless interface between +#pygame surfaces and kivy graphics using +#a fast flip() buffer transfer +#Expected top performance on a laptop: +#~60fps at 1080p or ~24fps at 4k +#key: alpha mask color +#border: size of frame around canvas +class Container(kv.uix.widget.Widget): + def __init__(self, size, key=None, border=0): + super().__init__() + self.W, self.H = size + self.size, self.border = size, border + self.surf = pygame.Surface((self.W, self.H)) + #self.surf = pygame.Surface((int(1.5*self.W), int(1.5*self.H))) + self.texture = kvTex.create(size=size, colorfmt="rgb") + + self.left, self.right = self.border, self.W-self.border + self.top, self.bottom = self.border, self.H-self.border + self.scale = 1.5 + + if key is not None: + self.surf.set_colorkey(key) + with self.canvas: + self.screen = Rectangle(pos=(0, 0), size=size) + if hasattr(self, 'on_key_down'): + Window.bind(on_key_down=self.on_key_down) + + #Clear the canvas + def reset(self): + self.fill(Neon.BLACK.rgb) + if self.border > 0: + self.renderBorder() + + #Only use in the top level canvas + #Transfers a pygame data buffer to a kivyv texture + def flip(self): + W, H = self.surf.get_width(), self.surf.get_height() + #surf = pygame.transform.scale(self.surf, (int(W*self.scale), int(H*self.scale))) + data = pygame.image.tostring(self.surf, 'RGB', True) + self.texture.blit_buffer(data, bufferfmt="ubyte", colorfmt="rgb") + self.screen.texture = self.texture + + #Render a frame around the canvas + def renderBorder(self): + if self.border == 0: + return + for coords in [ + (0, 0, self.W, self.top), + (0, 0, self.left, self.H), + (0, self.bottom, self.W, self.H), + (self.right, 0, self.W, self.H) + ]: + pygame.draw.rect(self.surf, Neon.RED.rgb, coords) + + ### Blitting and drawing wrappers ### + + def blit(self, container, pos, area=None, flags=0): + pos = (pos[0]+self.border, pos[1]+self.border) + self.surf.blit(container, pos, area=area, special_flags=flags) + self.renderBorder() + + def fill(self, color): + self.surf.fill(color) + + def rect(self, color, coords, lw=0): + pygame.draw.rect(self.surf, color, coords, lw) + + def line(self, color, start, end, lw=1): + pygame.draw.line(self.surf, color, start, end, lw) + +#Return the visible portion of a tiled environment +def mapCrop(screen, env, txSz, trans): + offs, txSz = offsets(screen, *env.shape[:2], txSz, trans) + minH, maxH, minW, maxW = offs + return env[minH:maxH, minW:maxW], txSz + +#Array index offsets based on a transformation +def offsets(screen, R, C, txSz, trans=None): + W, H = screen.W, screen.H + sx, sy, tx, ty = W, H, 0, 0 + if trans is not None: + sx, sy, tx, ty = trans + ratio = sx / W #assume uniform transform + txSz = txSz * ratio + + nW = int(W / txSz) + nH = int(H / txSz) + minW = -int(min(tx / txSz, 0)) + minH = -int(min(ty / txSz, 0)) + + maxW = min(minW + nW, C) + maxH = min(minH + nH, H) + return (minH, maxH, minW, maxW), txSz + +#Make a set of mipmaps for a dict of textures +#with sizes specivied by mipLevels +def mips(texs, mipLevels): + rets = {} + for sz in mipLevels: + if sz not in rets: + rets[sz] = {} + for k, tx in texs.items(): + rets[sz][k] = pygame.transform.scale(tx, (sz, sz)) + return rets + +#Mipmaps for a single texture +def mip(tex, mipLevels): + return dict((sz, pygame.transform.scale(tex, (sz, sz))) for sz in mipLevels) + +#Get the mip map "closest" to the specified resolution +def mipKey(txSz, mipLevels): + inds = np.array(mipLevels) >= txSz + return mipLevels[np.argmax(inds)] + +#Render a full tiled map to the given screen (Container) +#env specifies an array of texture indices (requires a tex dict) +#or an array of colors otherwise. +#iso can be used to render square maps in isometric perspective +def renderMap(screen, env, txSz, tex=None, iso=False): + shape = env.shape + H, W = shape[:2] + for h in range(H): + for w in range(W): + if tex is None: + ww, hh = tileCoords(w, h, W, H, txSz, iso) + screen.rect(env[h, w], (ww, hh, txSz, txSz)) + else: + mat = tex[env[h, w]] + renderTile(screen, mat, w, h, txSz, iso) + +#Convert a tile to isometric +def tileToIso(tex, txSz): + tex.set_colorkey(Neon.BLACK.rgb) + tex = pygame.transform.rotate(tex, -45) + ww, hh = tex.get_width(), tex.get_height()//2 + tex = pygame.transform.scale(tex, (ww, hh)) + return tex + +#coords of a tile in ortho perspective +def cartCoords(w, h, txSz): + return int(w*txSz), int(h*txSz) + +#coords of a tile in isometric perspective +def isoCoords(w, h, W, H, txSz): + nW, nH = W//txSz, H//txSz + ww = nW//2 + (w - h) / 2 + hh = nH//4 + (w + h) / 4 + w, h = cartCoords(ww, hh, txSz) + return w, h + +#coords of a tile in ortho/isometric perspective +def tileCoords(w, h, W, H, txSz, iso): + if iso: + return isoCoords(w, h, W, H, txSz) + else: + return cartCoords(w, h, txSz) + +#Isometric render a tile +def renderIso(screen, tex, w, h, txSz): + tex = tileToIso(tex, txSz) + w, h = isoCoords(w, h, screen.W, screen.H, txSz) + screen.blit(tex, (w, h)) + +#Ortho render a tile +def renderCart(screen, tex, w, h, txSz): + w, h = cartCoords(w, h, txSz) + screen.blit(tex, (w, h)) + +#Ortho/isometric render a tile +def renderTile(screen, tex, w, h, txSz, iso): + if iso: + renderIso(screen, tex, w, h, txSz) + else: + renderCart(screen, tex, w, h, txSz) + +#Basic color class with different color schemes +#usable almost anywhere (provides hex/rgb255/rgb1 vals) +class Color: + def __init__(self, name, hexVal): + self.name = name + self.hex = hexVal + self.rgb = rgb(hexVal) + self.norm = rgbNorm(hexVal) + self.value = self.rgb #Emulate enum + +def rgb(h): + h = h.lstrip('#') + return tuple(int(h[i:i+2], 16) for i in (0, 2, 4)) + +def rgbNorm(h): + h = h.lstrip('#') + return tuple(int(h[i:i+2], 16)/255.0 for i in (0, 2, 4)) + +#Neon color pallete + a few common game colors. +#Why would you need anything else? +class Neon: + RED = Color('RED', '#ff0000') + ORANGE = Color('ORANGE', '#ff8000') + YELLOW = Color('YELLOW', '#ffff00') + + GREEN = Color('GREEN', '#00ff00') + MINT = Color('MINT', '#00ff80') + CYAN = Color('CYAN', '#00ffff') + + BLUE = Color('BLUE', '#0000ff') + PURPLE = Color('PURPLE', '#8000ff') + MAGENTA = Color('MAGENTA', '#ff00ff') + + FUCHSIA = Color('FUCHSIA', '#ff0080') + SPRING = Color('SPRING', '#80ff80') + SKY = Color('SKY', '#0080ff') + + WHITE = Color('WHITE', '#ffffff') + GRAY = Color('GRAY', '#666666') + BLACK = Color('BLACK', '#000000') + + BLOOD = Color('BLOOD', '#bb0000') + BROWN = Color('BROWN', '#7a3402') + GOLD = Color('GOLD', '#eec600') + SILVER = Color('SILVER', '#b8b8b8') + + #Hacker green + TERM = Color('TERM', '#41ff00') + + #Purple alpha + MASK = Color('MASK', '#d67fff') + + #Get the 12 neon colors + def color12(): + return ( + Neon.RED, Neon.ORANGE, Neon.YELLOW, + Neon.GREEN, Neon.MINT, Neon.CYAN, + Neon.BLUE, Neon.PURPLE, Neon.MAGENTA, + Neon.FUCHSIA, Neon.SPRING, Neon.SKY) + + #Get a random neon color + def rand12(): + twelveColor = color12() + randInd = np.random.randint(0, len(twelveColor)) + return twelveColor[randInd] + +#Pygame Surface initializer from file +def Texture(path, mask=None): + try: + img = imread(path)[:, :, :3] + except FileNotFoundError: + raise + + img = pygame.pixelcopy.make_surface(img) + if mask is not None: + img.set_colorkey(mask) + + #For some reason, pygame loads images transformed + img = pygame.transform.flip(img, True, False) + img = pygame.transform.rotate(img, 90) + return img + +#Pygame blank Surface initializer +def Canvas(size, mask=None): + img = pygame.Surface(size) + if mask is not None: + img.set_colorkey(mask) + return img + +#Pygame font wrapper +class Font: + def __init__(self, size, name='freesansbold.ttf'): + self.font = pygame.font.Font(name, size) + + def render(self, size, color): + return self.font.render(size, 1, color) + +#Exponentially decaying FPS tracker +class FPSTracker: + def __init__(self): + self.start = time.time() + self.eda = EDA(k=0.95) + self.fpsVal = 0.0 + + def update(self): + tick = time.time() - self.start + self.eda.update(1.0/tick) + self.fpsVal = self.eda.eda + self.start = time.time() + + @property + def fps(self): + return str(self.fpsVal)[:5] + +#Exponentially decaying average +class EDA(): + def __init__(self, k=0.9): + self.k = k + self.eda = None + + def update(self, x): + if self.eda is None: + self.eda = x + return + self.eda = (1-self.k)*x + self.k*self.eda diff --git a/jsuarez/extra/embyr_deprecated/embyr2d/modules.py b/jsuarez/extra/embyr_deprecated/embyr2d/modules.py new file mode 100644 index 00000000..ff6a69b4 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr2d/modules.py @@ -0,0 +1,726 @@ +from pdb import set_trace as T +import numpy as np +import time +from scipy.misc import imresize, imsave +from enum import Enum + +import pygame +from pygame import Surface + +from forge.embyr import embyr +from forge.embyr import utils as renderutils +from forge.embyr import render +from forge.embyr.texture import TextureInitializer +from forge.blade.lib.enums import Neon, Color256, Defaults +from forge.blade.action.v2 import Attack +from forge.blade.action import action + +names = open('resource/ents.txt').read().splitlines() +NANIM = 8 +class Map(embyr.Container): + def __init__(self, realm, textures, + canvasSize, tileSz, mipLevels, iso, **kwargs): + super().__init__(canvasSize, **kwargs) + self.realm, self.textures, self.iso = realm, textures, iso + self.tileSz, self.mipLevels = tileSz, mipLevels + + def render(self, trans): + self.reset() + envCrop, txSz = embyr.mapCrop(self, + self.realm.world.env.inds(), self.tileSz, trans) + txSz = embyr.mipKey(txSz, self.mipLevels) + embyr.renderMap(self, envCrop, txSz, self.textures.material[txSz], self.iso) + self.flip() + return self.surf + + def refresh(self, trans, iso): + self.iso = iso + return self.render(trans) + +class SpriteGroup: + def __init__(self, tex, fonts): + self.tex, self.fonts = tex, fonts + self.sprites = [] + + def add(self, ent): + sprite = Sprite(ent) + self.sprites.append(sprite) + + def remove(self, ent): + self.sprites.remove(ent) + + def update(self): + for e in self.sprites: + e.update() + + def render(self, screen, offsets, txSz, iso): + for e in self.sprites: + #crop + e.render(screen, offsets, self.tex, self.fonts, txSz, iso) + +class Sprite: + def __init__(self, ent): + self.ent = ent + self.frame = 0 + + #self.pos = ent.lastPos + #self.newPos = ent.pos + + self.lastPos = ent.lastPos + if not ent.alive: + self.lastPos = ent.pos + r, c = self.lastPos + self.posAnim = self.lastPos + rNew, cNew = self.ent.pos + + self.rOff, self.cOff = np.sign(rNew-r), np.sign(cNew-c) + #self.targ = ent.attack.args + nNames = len(names) + self.entName = names[int(ent.entID)%nNames] + self.nameColor = Neon.color12()[int(ent.entID) % 12] + + def update(self): + self.frame += 1.0/NANIM + + r, c = self.lastPos + rAnim = r + self.frame * self.rOff + cAnim = c + self.frame * self.cOff + self.posAnim = (rAnim, cAnim) + + def render(self, screen, offsets, tex, fonts, txSz, iso): + self.targ = self.ent.targ + render.renderSprite(screen, self, offsets, tex, fonts, txSz, iso) +class Ent(embyr.Container): + def __init__(self, realm, fonts, textures, + canvasSize, tileSz, mipLevels, iso, **kwargs): + super().__init__(canvasSize, **kwargs) + self.realm, self.fonts, self.textures = realm, fonts, textures + self.tileSz, self.mipLevels, self.iso = tileSz, mipLevels, iso + + def render(self, trans, keyframe): + tiles = self.realm.world.env.tiles + if keyframe: + self.makeSprites(tiles) + self.sprites.update() + self.renderEnts(trans) + return self.surf + + def renderEnts(self, trans): + self.reset() + R, C = self.realm.world.env.shape + offs, txSz = embyr.offsets(self, R, C, self.tileSz, trans) + txSz = embyr.mipKey(txSz, self.mipLevels) + minH, _, minW, _ = offs + self.sprites.render(self, (minH, minW), txSz, self.iso) + return self.surf + + def makeSprites(self, tiles): + self.sprites = SpriteGroup(self.textures, self.fonts) + for tile in tiles.ravel(): + for e in tile.ents.values(): + self.sprites.add(e) + + def refresh(self, trans, iso): + self.iso = iso + return self.render(None, trans, keyframe=False) + +class EnvViewport(embyr.Container): + def __init__(self, realm, textures, + mipLevels, fonts, canvasSize, iso, **kwargs): + super().__init__(canvasSize, **kwargs) + self.textures, self.mipLevels, self.fonts = textures, mipLevels, fonts + self.tileSz, self.iso = textures.tileSz, iso + + self.map = Map(realm, self.textures, + canvasSize, self.tileSz, mipLevels, iso) + self.ent = Ent(realm, fonts, self.textures, canvasSize, + self.tileSz, mipLevels, iso, key=Neon.BLACK.rgb) + self.screenshot_idx = 0 + + def render(self, trans, keyframe=True): + mmap = self.map.render(trans) + ent = self.ent.render(trans, keyframe) + self.reset() + + self.blit(mmap, (0, 0)) + self.blit(ent, (0, 0)) + + self.flip() + return self.surf + + def refresh(self, trans, iso): + self.iso = iso + mmap = self.map.refresh(trans, self.iso) + ent = self.ent.refresh(trans, self.iso) + + self.blit(mmap, (0, 0)) + self.blit(ent, (0, 0)) + + self.flip() + return self.surf + + def leftScreenshot(self): + print("Saved left screenshot") + im = pygame.surfarray.array3d(self.surf) + im = im.transpose((1, 0, 2)) + imsave("env{}.png".format(self.screenshot_idx), im) + self.screenshot_idx += 1 + +class DatViewport(embyr.Container): + def __init__(self, realm, textures, fonts, + canvasSize, conf, **kwargs): + super().__init__(canvasSize, **kwargs) + self.W, self.H = canvasSize + self.graphHeigh = 128 + self.exchangeWidth = 128 + self.conf = conf + self.screenshot_idx = 0 + + self.idx = 0 + self.nIdx = 3 + self.textures = textures + self.counts = Counts(realm, textures.mipLevels, + canvasSize, border=3) + self.deps = Deps(realm, + canvasSize, border=3) + self.action = Action(realm, + canvasSize, border=3) + self.vals = Vals(realm, textures.mipLevels, canvasSize, + conf, border=3) + self.help = Help(fonts, canvasSize, border=3) + self.transparent = False + self.charIdx = {'h':0, 'v':1, 'c':2, 't':3, 'f':4, + 'w':5, 'q':6, 'a':7, '1':8, '2':9, + '3':10, 'd': 11, ']':12} + self.funcIdx = {} + self.funcIdx[self.charIdx['h']] = self.renderHelp + self.funcIdx[self.charIdx['v']] = self.renderDefaults + self.funcIdx[self.charIdx['c']] = self.renderCounts + self.funcIdx[self.charIdx['f']] = self.renderFood + self.funcIdx[self.charIdx['w']] = self.renderWater + self.funcIdx[self.charIdx['q']] = self.renderHalf + self.funcIdx[self.charIdx['d']] = self.renderDeps + self.funcIdx[self.charIdx['a']] = self.renderAction + self.funcIdx[self.charIdx['1']] = self.renderMelee + self.funcIdx[self.charIdx['2']] = self.renderRange + self.funcIdx[self.charIdx['3']] = self.renderMage + self.funcIdx[self.charIdx[']']] = self.renderRight + + self.renderHelp(True) + + def gridApply(self, damageF): + rets = [] + for r in range(self.conf.R): + for c in range(self.conf.C): + dist = Attack.l1((r, c), self.conf.SPAWN) + rets.append(((r,c), damageF(dist))) + return rets + + def renderMelee(self, update=False): + damageF = self.conf.MELEEDAMAGE + vals = self.gridApply(damageF) + self.renderVals(env, vals, update=update) + + def renderRange(self, update=False): + damageF = self.conf.RANGEDAMAGE + vals = self.gridApply(damageF) + self.renderVals(env, vals, update=update) + + def renderMage(self, update=False): + damageF = self.conf.MAGEDAMAGE + vals = self.gridApply(damageF) + self.renderVals(env, vals, update=update) + + def renderRight(self, update=False): + print("Saved right screenshot") + surf = self.dat + im = pygame.surfarray.array3d(surf) + im = im.transpose((1, 0, 2)) + imsave("dat{}.png".format(self.screenshot_idx), im) + + self.screenshot_idx += 1 + + def renderHelp(self, update=False): + if update: + self.dat = self.help.render() + + def renderAction(self, update=False): + #if update: + self.dat = self.action.render() + + def renderFood(self, update=False): + if update: + self.val = valF(0, 36) + self.renderVals(env, self.val, update) + + def renderWater(self, update=False): + if update: + self.val = valF(36, 0) + self.renderVals(env, self.val, update) + + def renderHalf(self, update=False): + if update: + self.val = valF(18, 18) + self.renderVals(env, self.val, update) + + def renderEmpty(self, update=False): + if update: + self.val = valF(0, 0) + self.renderVals(env, self.val, update) + + def renderDefaults(self, update=False): + self.renderVals(update) + + def renderVals(self, update=False): + self.dat = self.vals.render(self.trans, update) + self.dat.set_alpha(255) + if self.transparent: + self.dat.set_alpha(0) + env = self.env.copy() + env.blit(self.dat, (0, 0), + special_flags=pygame.BLEND_RGB_ADD) + #env.blit(self.dat, (0, 0)) + self.dat = env + + def renderDeps(self, update=False): + if update: + self.dat = self.deps.render() + + def renderCounts(self, update=False): + self.dat = self.counts.render(self.trans, update) + + def render(self, env, trans, update=False): + #self.ents, self.valF, self.trans = ents, valF, trans + self.env = env + self.reset() + self.trans = trans + self.funcIdx[self.idx](update) + self.surf.blit(self.dat, (0, 0)) + self.renderBorder() + return self.surf + + def key(self, c): + old_idx = self.idx + if c == 'o': + self.toggle() + elif c == 't': + self.transparent = not self.transparent + elif c in self.charIdx: + self.idx = self.charIdx[c] + else: + return + + if old_idx == self.charIdx[']'] and self.idx == old_idx: + return + + self.funcIdx[self.idx](update=True) + + if c == ']': + self.idx = old_idx + + def toggle(self): + self.idx = (self.idx + 1) % self.nIdx + +class Title(embyr.Container): + def __init__(self, fonts, canvasSize, **kwargs): + super().__init__(canvasSize, **kwargs) + godsword = embyr.Texture('resource/Splash/ags.png', mask=Neon.MASK.rgb) + w, h = godsword.get_width(), godsword.get_height() + scaled = int(w*self.H/h), int(self.H) + self.godsword = pygame.transform.scale(godsword, scaled) + + def render(self): + self.reset() + self.blit(self.godsword, (0, 0)) + return self.surf + +class FPS(embyr.Container): + def __init__(self, fonts, canvasSize, **kwargs): + super().__init__(canvasSize, **kwargs) + self.fpsTracker = embyr.FPSTracker() + self.fonts = fonts + + def render(self): + self.reset() + self.fpsTracker.update() + fps = self.fonts.Large.render('FPS: '+self.fpsTracker.fps, 1, Neon.GOLD.rgb) + self.surf.blit(fps, (50,10)) + return self.surf + +class Corner(embyr.Container): + def __init__(self, env, fonts, canvasSize, **kwargs): + super().__init__(canvasSize, **kwargs) + self.title = Title(fonts, (256, 64), border=3) + self.fps = FPS(fonts, (256, 64), border=3) + + def render(self): + self.reset() + title = self.title.render() + fps = self.fps.render() + + self.surf.blit(title, (0, 0)) + self.surf.blit(fps, (0, 64)) + return self.surf + +#Work on +class LeftSidebar(embyr.Container): + def __init__(self, realm, fonts, canvasSize, **kwargs): + super().__init__(canvasSize, **kwargs) + self.realm, self.fonts = realm, fonts + + def render(self): + self.reset() + stats = self.realm.world.stats + render.renderExchange(self, stats, self.fonts, self.W, self.H) + return self.surf + +class Histogram(embyr.Container): + def __init__(self, fonts, canvasSize, **kwargs): + super().__init__(canvasSize, **kwargs) + self.fonts = fonts + + def render(self, dat): + self.reset() + render.renderHist(self, dat, self.fonts, self.W, self.H) + return self.surf + +class Graph(embyr.Container): + def __init__(self, fonts, canvasSize, **kwargs): + super().__init__(canvasSize, **kwargs) + self.fonts = fonts + + def render(self, dat): + self.reset() + render.renderGraph(self, dat, self.fonts, self.W, self.H, color=Neon.GOLD.rgb) + return self.surf + +class BottomSidebar(embyr.Container): + def __init__(self, realm, fonts, canvasSize, **kwargs): + super().__init__(canvasSize, **kwargs) + self.hist = Histogram(fonts, (self.W, self.H//2), border=3) + self.graph = Graph(fonts, (self.W, self.H//2), border=3) + self.realm = realm + + def render(self): + self.fill(Neon.BLACK.rgb) + stats = self.realm.world.stats + + #Render histograms of time alive, levels, and best agents + lifespans = [[e.timeAlive] for e in stats.pcs] + #timesAlive = Render.histogram(timesAlive) + #combatStats = [(e.melee, e.ranged, e.defense) for e in blocks] + + hist = self.hist.render(lifespans) + graph = self.graph.render(stats.numEntities[-self.W:]) + + self.surf.blit(hist, (0, 0)) + self.surf.blit(graph, (0, self.H//2)) + + #Render + #Render.renderHist(self.canvas, self.fonts, lifespans, self.W, self.H, 0, self.H//2) + #renderHist(screen, fonts, combatStats, 0, H+h, W, h) + #Render.renderGraph(self.canvas, stats.numEntities[-self.W:], self.fonts, self.W, self.H, color=Color.GOLD) + + # Render.renderGraphs(self.canvas, stats, self.fonts, + # self.W, self.H) + return self.surf + +class Counts(embyr.Container): + def __init__(self, realm, mipLevels, canvasSize, **kwargs): + super().__init__(canvasSize, **kwargs) + self.R, self.C = realm.world.env.tiles.shape + self.nColors = realm.world.env.nCounts + self.mipLevels = mipLevels + self.realm = realm + self.tileSz = 16 + + def render(self, trans, update): + env = self.realm.world.env + tiles = env.tiles + R, C = tiles.shape + counts = np.array([tile.counts for tile in tiles.ravel()]) + nCounts = counts.shape[1] + counts = counts.reshape(R, C, counts.shape[1]) + counts = [counts[:, :, i] for i in range(counts.shape[2])] + if nCounts <= 12: + colors = Neon.color12() + else: + colors = Color256.colors[0::256//nCounts] + for idx, count in enumerate(counts): + counts[idx] = 20*counts[idx][:, :, np.newaxis]*np.array(colors[idx].value)/255.0 + sumCounts = sum(counts) + + R, C, _ = sumCounts.shape + counts = np.clip(sumCounts, 0, 255).astype(np.uint8) + counts = np.clip(counts, 0, 255).astype(np.uint8) + + valCrop, txSz = embyr.mapCrop(self, counts, + self.tileSz, trans) + txSz = embyr.mipKey(txSz, self.mipLevels) + embyr.renderMap(self, valCrop, txSz) + return self.surf + +class Deps(embyr.Container): + def __init__(self, realm, canvasSize, **kwargs): + super().__init__(canvasSize, **kwargs) + self.R, self.C = realm.world.env.tiles.shape + self.realm = realm + self.tileSz = 16 + + def render(self): + anns = self.realm.sword.anns + deps = [ann.visDeps() for ann in anns] + grids = [] + for dep in deps: + vals = dep + vList = [e[1] for e in vals] + vMean, vStd = np.mean(vList), np.std(vList)+1e-3 + nStd, nTol = 4.0, 0.5 + grayVal = int(255 / nStd * nTol) + grid = np.zeros((16, 16, 3)) + for v in vals: + pos, mat = v + r, c = pos + mat = (mat - vMean) / vStd + color = np.clip(mat, -nStd, nStd) + color = int(color * 255.0 / nStd) + if color > 0: + color = (0, color, 128) + else: + color = (-color, 0, 128) + grid[r, c] = color + grids.append(grid) + grids += 15*[0*grids[0]] + grids = grids[:16] + grids1 = np.vstack(grids[:4]) + grids2 = np.vstack(grids[4:8]) + grids3 = np.vstack(grids[8:12]) + grids4 = np.vstack(grids[12:16]) + grids = np.hstack((grids1, grids2, grids3, grids4)) + embyr.renderMap(self, grids, self.tileSz) + return self.surf + +class Action(embyr.Container): + def __init__(self, realm, + canvasSize, **kwargs): + super().__init__(canvasSize, **kwargs) + self.realm = realm + self.makeMaps() + self.tileSz = 16 + self.RED = np.array(Defaults.RED) + self.GREEN = np.array(Defaults.GREEN) + self.BLUE = np.array(Defaults.BLUE) + + def render(self): + ents = [e.server for e in self.realm.desciples.values()] + self.updateMaps(ents) + maps = self.maps + maps = maps + 15*[0*maps[0]] + maps = maps[:16] + mapv1 = np.vstack(maps[:4]) + mapv2 = np.vstack(maps[4:8]) + mapv3 = np.vstack(maps[8:12]) + mapv4 = np.vstack(maps[12:16]) + maps = np.hstack((mapv1, mapv2, mapv3, mapv4)) + + maps = maps.astype(np.uint8) + #maps = pygame.pixelcopy.make_surface(maps) + #valCrop, txSz = embyr.mapCrop(self, maps, + # self.tileSz, trans) + #txSz = embyr.mipKey(txSz, self.mipLevels) + embyr.renderMap(self, maps, int(1.7*self.tileSz)) + return self.surf + + def updateMaps(self, ents): + for ent in ents: + attack = ent.attack.action + #targ = ent.attack.args + targ = ent.targ + if targ is None or targ.damage is None: + continue + idx = ent.colorInd + if idx >= 16: + continue + entr, entc = ent.attkPos + targr, targc = ent.targPos + r = self.MAPCENT + targr - entr + c = self.MAPCENT + targc - entc + + if issubclass(attack, action.Melee): + self.counts[idx][r, c, 0] += 1 + elif issubclass(attack, action.Range): + self.counts[idx][r, c, 1] += 1 + elif issubclass(attack, action.Mage): + self.counts[idx][r, c, 2] += 1 + + rgb = self.counts[idx][r, c] + normSum = np.sum(rgb) + redVal = self.RED * rgb[0] / normSum + greenVal = self.GREEN * rgb[1] / normSum + blueVal = self.BLUE * rgb[2] / normSum + color = (redVal + greenVal + blueVal).astype(np.uint8) + self.maps[idx][r, c] = color + #colorIdx = np.argmax(self.counts[idx][r, c]) + #self.maps[idx][r, c] = self.colors[colorIdx] + + def makeMaps(self): + self.NMAPS = 16#self.config.NPOP + self.MAPCENT = 4 #self.config.STIM + self.MAPSZ = 2*self.MAPCENT + 1 + + self.maps = [] + self.counts = [] + for idx in range(self.NMAPS): + self.maps.append(np.zeros((self.MAPSZ, self.MAPSZ, 3))) + self.counts.append(np.zeros((self.MAPSZ, self.MAPSZ, 3))) + + + def renderEnts(self, trans): + self.reset() + R, C = self.env.shape + offs, txSz = embyr.offsets(self, R, C, self.tileSz, trans) + txSz = embyr.mipKey(txSz, self.mipLevels) + minH, _, minW, _ = offs + self.sprites.render(self, (minH, minW), txSz, self.iso) + return self.surf + + + def refresh(self, trans, iso): + self.iso = iso + return self.render(None, trans, keyframe=False) + +class Vals(embyr.Container): + def __init__(self, realm, mipLevels, + canvasSize, conf, **kwargs): + super().__init__(canvasSize, **kwargs) + self.R, self.C = realm.world.env.tiles.shape + self.vals = np.zeros((self.R, self.C), dtype=object) + self.mipLevels = mipLevels + self.realm = realm + self.tileSz = 16 + + def update(self): + ann = self.realm.sword.anns[0] + vals = ann.visVals() + vList = [e[1] for e in vals] + vMean, vStd = np.mean(vList), np.std(vList) + nStd, nTol = 4.0, 0.5 + grayVal = int(255 / nStd * nTol) + for v in vals: + pos, mat = v + r, c = pos + mat = (mat - vMean) / vStd + color = np.clip(mat, -nStd, nStd) + color = int(color * 255.0 / nStd) + if color > 0: + color = (0, color, 128) + else: + color = (-color, 0, 128) + self.vals[r, c] = color + + def render(self, trans, update): + self.reset() + if update: + self.update() + + valCrop, txSz = embyr.mapCrop(self, self.vals, + self.tileSz, trans) + txSz = embyr.mipKey(txSz, self.mipLevels) + embyr.renderMap(self, valCrop, txSz) + return self.surf + +class Help(embyr.Container): + def __init__(self, fonts, canvasSize, **kwargs): + super().__init__(canvasSize, **kwargs) + self.fonts = fonts + self.text = [ + 'Command List:', + ' Environment', + ' i: toggle isometric', + ' Overlay:', + ' o: cycle overlay', + ' h: help overlay', + ' c: count overlay', + ' v: value overlay', + ' t: toggle transparency', + ' w: value no water', + ' f: value no food', + ' q: value half food/water', + ' a: value no food/water', + ' r: screenshot the right side of screen', + ] + + def render(self): + self.reset() + offset, margin, pos = 64, 16, 0 + for item in self.text: + text = self.fonts.Huge.render(item, 1, Neon.GOLD.rgb) + self.blit(text, (margin, pos+margin)) + pos += offset + return self.surf + +class Canvas(embyr.Container): + def __init__(self, size, realm, dims, conf, **kwargs): + super().__init__(size, **kwargs) + self.realm, border, self.iso = realm, 3, False + self.W, self.H, self.side = dims + mipLevels = [8, 16, 32, 48, 64, 128] + textures = TextureInitializer(16, mipLevels = mipLevels) + self.fonts = renderutils.Fonts('resource/Fonts/dragonslapper.ttf') + + self.envViewport = EnvViewport(realm, textures, + mipLevels, self.fonts, (self.H, self.H), self.iso) + + self.datViewport = DatViewport(realm, textures, + self.fonts, (self.H, self.H), conf, border=border) + self.leftSidebar = LeftSidebar(realm, self.fonts, + (self.side, self.H), border=border) + self.bottomSidebar = BottomSidebar(realm, self.fonts, (self.W, self.side)) + self.corner = Corner(realm, self.fonts, (self.side, self.side)) + + def render(self, realm, trans, overlay=True): + #env, stats, valF + #stats.pcs + env = self.envViewport.render(trans, overlay) + dat = self.datViewport.render(env, trans, False) + corner = self.corner.render() + self.env = env + + self.surf.blit(env, (self.side, 0)) + self.surf.blit(dat, (self.H+self.side, 0)) + self.surf.blit(corner, (0, self.H)) + + if overlay: + leftSidebar = self.leftSidebar.render() + bottomSidebar = self.bottomSidebar.render() + + self.surf.blit(leftSidebar, (0, 0)) + self.surf.blit(bottomSidebar, (self.side, self.H)) + + return self.surf + + def renderTitle(self): + godsword = renderutils.pgRead('resource/Splash/agsfull.png', mask=Neon.MASK.rgb) + w, h = godsword.get_width(), godsword.get_height() + W, H = self.size + ratio = W/w + w, h = int(ratio*w), int(ratio*h) + godsword = pygame.transform.scale(godsword, (w, h)) + hOff = int(H/2 - godsword.get_height()/2) + self.blit(godsword, (0, hOff)) + return self.surf + + def toggleEnv(self, trans): + sx, sy, tx, ty = trans + self.iso = not self.iso + env = self.envViewport.refresh(trans, self.iso) + self.surf.set_clip((self.side, 0, self.H, self.H)) + env = pygame.transform.scale(env, (sx, sy)) + self.surf.blit(env, (self.side+tx, ty)) + self.surf.set_clip(None) + + def key(self, c): + self.datViewport.key(c) + + def leftScreenshot(self): + self.envViewport.leftScreenshot() diff --git a/jsuarez/extra/embyr_deprecated/embyr2d/render.py b/jsuarez/extra/embyr_deprecated/embyr2d/render.py new file mode 100644 index 00000000..8e172d39 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr2d/render.py @@ -0,0 +1,243 @@ +#Main renderer. Loads all textures, takes render information from +#the world, applies texturing for env/entities/actions/additional +#statistic visuals + +from pdb import set_trace as T +import pygame +import sys +import time + +import numpy as np +from scipy.misc import imread +from enum import Enum + +from forge.embyr import embyr +from forge.embyr import utils as renderutils +from forge.blade.action import action +from forge.blade.action import v2 +from forge.blade.lib.enums import Neon + +def renderEnts(screen, ent, fonts, entTx, atnTx, txSz, iso): + H, W = ent.shape + for h in range(H): + for w in range(W): + if ent[h, w] is None: + continue + for e in ent[h, w]: + renderSprite(screen, e, (h, w), fonts, entTx, + atnTx, txSz, iso) + +def renderVals(screen, vals, mipLevels, txSz): + H, W = vals.shape + for h in range(H): + for w in range(W): + if vals[h, w] is 0: + continue + v = vals[h, w] + mask = np.array(mipLevels) >= txSz + key = np.array(mipLevels)[np.argmax(mask)] + ww, hh = cartCoords(w, h, key) + screen.rect(v, (ww, hh, key, key)) + +def renderSprite(screen, sprite, offs, tex, fonts, txSz, iso): + renderEntity(screen, sprite, offs, tex, fonts, txSz, iso) + renderStats(screen, sprite, offs, tex, fonts, txSz, iso) + if sprite.targ is not None: + renderAction(screen, sprite, offs, tex, fonts, txSz, iso) + +def renderEntity(screen, sprite, offs, tex, fonts, txSz, iso): + rOff, cOff = offs + r, c = sprite.posAnim + W, H = screen.size + pos = embyr.tileCoords(c-cOff, r-rOff, W, H, txSz, iso) + screen.blit(tex.entity[txSz][sprite.ent.color.name], pos) + +def renderAction(screen, sprite, offs, tex, fonts, txSz, iso): + rAnim, cAnim = sprite.posAnim + rTarg, cTarg = sprite.targ.pos + rTrans, cTrans= offs + frame = sprite.frame + + rOff, cOff = rTarg - rAnim, cTarg - cAnim + r = rAnim + frame * rOff + c = cAnim + frame * cOff + + e = sprite.ent + W, H = screen.W, screen.H + if e.entID != sprite.targ.entID: + attk = e.attack.action + w, h = embyr.tileCoords(c-cTrans, r-rTrans, W, H, txSz, iso) + if attk is v2.Melee: + angle = np.arctan2(rAnim-rTarg, cTarg-cAnim) * 180 / np.pi - 90 + tex = tex.action[txSz]['melee'] + tex = pygame.transform.rotate(tex, angle + 360*frame) + elif attk is v2.Range: + angle = np.arctan2(rAnim-rTarg, cTarg-cAnim) * 180 / np.pi - 90 + tex = tex.action[txSz]['range'] + tex = pygame.transform.rotate(tex, angle) + elif attk is v2.Mage: + tex = tex.action[txSz]['mage'] + sz = int(txSz * frame) + tex = pygame.transform.scale(tex, (sz, sz)) + else: + print('invalid attack texture in render.py') + exit(0) + + screen.blit(tex, (w, h)) + #w, h = embyr.tileCoords(cAnim-cOff, rAnim-rOff, W, H, txSz, iso) + #screen.rect(Neon.BLUE.rgb, (w, h, txSz, txSz), 2) + + #w, h = embyr.tileCoords(cTarg-cOff, rTarg-rOff, W, H, txSz, iso) + #screen.rect(Neon.ORANGE.rgb, (w, h, txSz, txSz), 4) + + damage = e.damage + if damage is not None: + w, h = embyr.tileCoords(cAnim-cTrans, rAnim-rTrans, W, H, txSz, iso) + if damage == 0: + text = fonts.huge.render(str(damage), 1, Neon.BLUE.rgb) + else: + text = fonts.huge.render(str(damage), 1, Neon.RED.rgb) + screen.blit(text, (w, h-16)) + +def renderStat(screen, stat, maxStat, statInd, w, h, txSz, color): + #w, h = tileCoords(w, h, txSz) + barSz = (1.5*txSz) // 16 + scale = txSz / maxStat + scaledStat = int(scale*stat) + if stat > 0: + screen.rect(color, (w, h+statInd*barSz, scaledStat, barSz)) + if maxStat-stat > 0: + screen.rect(Neon.RED.rgb, + (w+scaledStat, h+statInd*barSz, txSz-scaledStat, barSz)) + +def renderStats(screen, sprite, offs, tex, fonts, txSz, iso): + e = sprite.ent + rOff, cOff = offs + r, c = sprite.posAnim + W, H = screen.W, screen.H + w, h = embyr.tileCoords(c-cOff, r-rOff, W, H, txSz, iso) + stats = [] + stats.append((e.health, e.maxHealth, Neon.GREEN.rgb)) + stats.append((e.water, e.maxWater, Neon.BLUE.rgb)) + stats.append((e.food, e.maxFood, Neon.GOLD.rgb)) + + text = fonts.large.render(sprite.entName, 1, sprite.nameColor.rgb) + screen.blit(text, (w-40, h-20)) + + for (ind, dat) in enumerate(stats): + s, sMax, color = dat + renderStat(screen, s, sMax, ind, w, h, txSz, color) + +def renderGraph(screen, dat, fonts, w, h, border=2, + bg=Neon.BLACK.rgb, color=Neon.RED.rgb): + if len(dat) == 0: + return + + tickHeight = 5 + fontWidth = 40 + fontHeight = 40 + yy = dat[-w:] + xx = np.arange(len(yy)) + for x, y in zip(xx, yy): + screen.line(color, (x, h-y), (x, h-(y+tickHeight))) + + #screen.rect(Color.RED, + # (x - fontWidth+3, h-(y+tickHeight+3), fontWidth, tickHeight)) + + text = fonts.large.render(str(y), 1, Neon.YELLOW.rgb) + screen.blit(text, (x-fontWidth, h-y-fontHeight)) + +def renderHist(screen, dat, fonts, W, H, mul=1, border=4, + bg=Neon.BLACK.rgb, colors=(Neon.GREEN.rgb, Neon.RED.rgb, Neon.BLUE.rgb)): + px = 8 + spacer = 3 + blockSize = 64 + valSize = 32 + txtW = 45 + txtH = 20 + leftPad = 16 + barSz = 8 + delta = 0 + colorInd = 0 + x = 0 + scale = 1 + if np.max(dat) > H: + scale = H / np.max(dat) + for block in dat: + for origVal in block: + val = int(origVal / scale) + #val = int(100*np.random.rand()) + color = colors[colorInd % len(block)] + xx, yy = x+border+barSz, H-val-border + screen.rect(color, (xx, yy, barSz, val)) + + x += valSize + #if ww > graphW: + # return + text = fonts.small.render(str(origVal), 1, Neon.YELLOW.rgb) + screen.blit(text, (xx-txtW, yy-txtH)) + + #delta += px+spacer + #colorInd += 1 + + #delta += setSpacer + +def histogram(data, numBins=10): + data = sorted(data, reverse=True) + split = int(np.ceil(len(data)/float(numBins))) + hist = [] + for i in range(numBins): + val = 0 + datSplit = data[split*i:split*(i+1)] + if len(datSplit) > 0: + val = int(np.mean(datSplit)) + hist += [[val]] + return hist + +def renderGraphs(screen, stats, fonts, W, H, border=4): + #Render histograms of time alive, levels, and best agents + + blocks = stats.statBlocks + timesAlive = np.asarray([[e.timeAlive] for e in blocks]) + timesAlive = histogram(timesAlive) + combatStats = [(e.melee, e.ranged, e.defense) for e in blocks] + + #Render + renderHist(screen, fonts, timesAlive, 0, H, w, h) + #renderHist(screen, fonts, combatStats, 0, H+h, W, h) + renderGraph(screen, stats.numEntities[-W:], fonts, W, H, + color=Neon.GOLD.rgb) + +def renderExchangeBlock(screen, entry, fonts, ind, + W, H, blockH, pad=4): + screen.rect(Neon.RED.rgb, + (0, (ind+1)*blockH, W, 3), 0) + + numBuy, numSell = entry.numBuy, entry.numSell + maxBuyPrice, minSellPrice = entry.maxBuyPrice, entry.maxSellPrice + color = Neon.YELLOW.rgb + + text = [] + text.append(entry.itemName) + text.append('Buy/Sell Offers: ' + str(numBuy) + ', ' + str(numSell)) + text.append('Min/Max Price: ' + str(maxBuyPrice) + ', ' + str(minSellPrice)) + + def height(i, inc): + return i*blockH+ int(inc*blockH/5.0) + + for i, e in enumerate(text): + txt = fonts.normal.render(e, 1, color) + screen.blit(txt, (pad, height(ind, i))) + +def renderExchange(screen, stats, fonts, W, H): + blockH = W/2 + numRender = H / blockH + exchange = stats.exchange + + i = -1 + for e in exchange.queue: + i += 1 + if i == numRender: + break + + renderExchangeBlock(screen, e, fonts, i, W, H, blockH) diff --git a/jsuarez/extra/embyr_deprecated/embyr2d/texture.py b/jsuarez/extra/embyr_deprecated/embyr2d/texture.py new file mode 100644 index 00000000..1ef6dc7e --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr2d/texture.py @@ -0,0 +1,57 @@ +from pdb import set_trace as T +from forge.embyr import utils as renderutils +from forge.embyr import embyr + +from forge.blade.lib import enums +from forge.blade.lib.enums import Neon + +class TextureInitializer(): + def __init__(self, sz, root='resource/', mipLevels=[16, 32, 64, 96, 128], barSz=2): + self.tileSz = sz + self.width = sz + self.barSz = barSz + self.root = root + self.mipLevels = mipLevels + + #self.material = RenderUtils.readRGB(root+'Material/textures.png') + self.material = self.textureTiles(mipLevels) + self.entity = self.textureEnum(enums.Entity, + root+'Entity/', mask=Neon.MASK.rgb) + proj = { + 'melee':renderutils.pgRead(root+'Action/melee.png', mask=Neon.MASK.rgb), + 'range':renderutils.pgRead(root+'Action/range.png', mask=Neon.MASK.rgb), + 'mage':renderutils.pgRead(root+'Action/mage.png', mask=Neon.MASK.rgb), + } + action = { + 'melee':renderutils.pgRead(root+'Action/meleeattack.png', mask=Neon.MASK.rgb), + 'range':renderutils.pgRead(root+'Action/rangeattack.png', mask=Neon.MASK.rgb), + 'mage':renderutils.pgRead(root+'Action/mageattack.png', mask=Neon.MASK.rgb), + } + + self.action = embyr.mips(action, mipLevels) + + #self.action = self.textureEnum(Enums.Entity, + # root+'Action/', mask=Color.MASK) + + def textureTiles(self, mipLevels): + reverse = {} + for mat in enums.Material: + mat = mat.value + texCoords = mat.tex + mat.tex = renderutils.pgRead(self.root + '/tiles/' + mat.tex + '.png') + reverse[mat.index] = mat.tex + return embyr.mips(reverse, self.mipLevels) + + def textureEnum(self, enum, path, mask=None, suffix='.png'): + reverse = {} + for color in enums.Neon.color12(): + name = color.name + texPath = path + 'neural' + name + suffix + reverse[name] = renderutils.pgRead(texPath, mask=mask) + for name in range(0, 256): + name = str(name) + texPath = path + 'neural' + name + suffix + reverse[name] = renderutils.pgRead(texPath, mask=mask) + return embyr.mips(reverse, self.mipLevels) + + diff --git a/jsuarez/extra/embyr_deprecated/embyr2d/utils.py b/jsuarez/extra/embyr_deprecated/embyr2d/utils.py new file mode 100644 index 00000000..9fb29be5 --- /dev/null +++ b/jsuarez/extra/embyr_deprecated/embyr2d/utils.py @@ -0,0 +1,51 @@ +from scipy.misc import imread +import pygame +import time + +from forge.blade.lib.utils import EDA +from forge.blade.lib.enums import Neon + +def readRGB(path): + return imread(path)[:, :, :3] + +def pgRead(path, mask=None): + try: + img = readRGB(path) + except FileNotFoundError: + return None + + img = pygame.pixelcopy.make_surface(img) + if mask is not None: + img.set_colorkey(mask) + + #For some reason, pygame loads images transformed + img = pygame.transform.flip(img, True, False) + img = pygame.transform.rotate(img, 90) + return img + +class Font: + def render(txt, size, color=Neon.GOLD.rgb): + pass + #return pygame.font.Font('freesansbold.tt', size).render(text, 1, color) + +class Fonts: + def __init__(self, font='freesansbold.ttf'): + sizes=(9, 12, 18, 24, 28, 32, 36) + fonts = [pygame.font.Font(font, sz) for sz in sizes] + self.tiny, self.small, self.normal, self.large, self.Large, self.huge, self.Huge = fonts + +class FPSTracker: + def __init__(self): + self.start = time.time() + self.eda = EDA(k=0.95) + self.fpsVal = 0.0 + + def update(self): + tick = time.time() - self.start + self.eda.update(1.0/tick) + self.fpsVal = self.eda.eda + self.start = time.time() + + @property + def fps(self): + return str(self.fpsVal)[:5] diff --git a/jsuarez/extra/envfig.py b/jsuarez/extra/envfig.py new file mode 100644 index 00000000..551ba132 --- /dev/null +++ b/jsuarez/extra/envfig.py @@ -0,0 +1,22 @@ +from pdb import set_trace as T +from scipy.misc import imread, imresize, imsave +import numpy as np +import imageio + +fracs, imgs = [], [] +for i in range(6): + mapPath = 'resource/maps/map' + str(i) + '/map.png' + fractalPath = 'resource/maps/map' + str(i) + '/fractal.png' + + mapImg = imread(mapPath)[256:-256, 256:-256] + fractalImg = imread(fractalPath)[8:-8, 8:-8] + fractalImg = imresize(fractalImg, mapImg.shape) + fractalImg = np.stack(3*[fractalImg], 2) + + fracs.append(fractalImg) + imgs.append(mapImg) + +fracs = np.concatenate(fracs, 1) +imgs = np.concatenate(imgs, 1) +rets = np.concatenate((fracs, imgs), 0) +imsave('envgen.png', rets) diff --git a/jsuarez/extra/envgif.py b/jsuarez/extra/envgif.py new file mode 100644 index 00000000..230a5ed3 --- /dev/null +++ b/jsuarez/extra/envgif.py @@ -0,0 +1,23 @@ +from pdb import set_trace as T +from scipy.misc import imread, imresize +import numpy as np +import imageio + +frames = [] +for i in range(100): + mapPath = 'resource/maps/map' + str(i) + '/map.png' + fractalPath = 'resource/maps/map' + str(i) + '/fractal.png' + print(i) + + mapImg = imread(mapPath) + fractalImg = imread(fractalPath) + fractalImg = imresize(fractalImg, mapImg.shape) + fractalImg = np.stack(3*[fractalImg], 2) + + img = np.concatenate((fractalImg, mapImg), 1) + frames.append(img) + +imageio.mimwrite('envgen.mp4', frames, fps=1.5) + + + diff --git a/jsuarez/extra/figures.py b/jsuarez/extra/figures.py new file mode 100644 index 00000000..db651e17 --- /dev/null +++ b/jsuarez/extra/figures.py @@ -0,0 +1,220 @@ +import numpy as np +import sys, json +from forge.blade.lib.enums import Neon, Color256 +from matplotlib import pyplot as plt +from pdb import set_trace as T +import logs as loglib +import experiments +import os.path as osp +import os + +def gen_plot(log, keys, savename, train=True): + loglib.dark() + + if len(keys) > 12: + colors = Color256.colors + else: + colors = Neon.color12() + + pops = [] + for i, key in enumerate(keys): + c = colors[i] + + if not train: + log[key] = np.cumsum(np.array(log[key])) / (1+np.arange(len(log[key]))) + + if i == 0: + loglib.plot(log[key], key, (1.0, 0, 0)) + else: + loglib.plot(log[key], key, c.norm) + loglib.godsword() + loglib.save(savename) + plt.close() + +def individual(log, label, npop, logDir='resource/data/exps/', train=True): + + if train: + split = 'train' + else: + split = 'test' + + savedir = osp.join(logDir, label, split) + if not osp.exists(savedir): + os.makedirs(savedir) + + if len(log['return']) > 0: + loglib.dark() + keys = reversed('return lifespan value value_loss pg_loss entropy grad_mean grad_std grad_min grad_max'.split()) + colors = Neon.color12() + fName = 'frag.png' + for idx, key in enumerate(keys): + if idx == 0: + c = colors[idx] + loglib.plot(log[key], key, (1.0, 0, 0)) + else: + c = colors[idx] + loglib.plot(log[key], key, c.norm) + maxLife = np.max(log['return']) + loglib.limits(ylims=[0, 50*(1+maxLife//50)]) + loglib.godsword() + savepath = osp.join(logDir, label, split, fName) + loglib.save(savepath) + print(savepath) + plt.close() + + # Construct population specific code + pop_mean_keys = ['lifespan{}_mean'.format(i) for i in range(npop)] + savefile = osp.join(logDir, label, split, 'pop_mean.png') + gen_plot(log, pop_mean_keys, savefile, train=train) + + # Per population movement probability + pop_move_keys = ['pop{}_move'.format(i) for i in range(npop)] + savefile = osp.join(logDir, label, split, 'pop_move.png') + gen_plot(log, pop_move_keys, savefile, train=train) + + # Attack probability plots + pop_move_keys = ['pop{}_range'.format(i) for i in range(npop)] + savefile = osp.join(logDir, label, split, 'pop_range.png') + gen_plot(log, pop_move_keys, savefile, train=train) + + pop_move_keys = ['pop{}_melee'.format(i) for i in range(npop)] + savefile = osp.join(logDir, label, split, 'pop_melee.png') + gen_plot(log, pop_move_keys, savefile, train=train) + + pop_move_keys = ['pop{}_mage'.format(i) for i in range(npop)] + savefile = osp.join(logDir, label, split, 'pop_mage.png') + gen_plot(log, pop_move_keys, savefile, train=train) + + # Movement tile entropy + pop_move_keys = ['pop{}_entropy'.format(i) for i in range(npop)] + savefile = osp.join(logDir, label, split, 'pop_move_entropy.png') + gen_plot(log, pop_move_keys, savefile, train=train) + + # Population attack probabilities when action is selected + pop_move_keys = ['pop{}_melee_logit'.format(i) for i in range(npop)] + savefile = osp.join(logDir, label, split, 'pop_melee_logit.png') + gen_plot(log, pop_move_keys, savefile, train=train) + + pop_move_keys = ['pop{}_range_logit'.format(i) for i in range(npop)] + savefile = osp.join(logDir, label, split, 'pop_range_logit.png') + gen_plot(log, pop_move_keys, savefile, train=train) + + pop_move_keys = ['pop{}_mage_logit'.format(i) for i in range(npop)] + savefile = osp.join(logDir, label, split, 'pop_mage_logit.png') + gen_plot(log, pop_move_keys, savefile, train=train) + + # Sum up all the logits to check if they actually sum to zero + for i in range(npop): + logit_sum = np.array(log['pop{}_melee_logit'.format(i)]) + np.array(log['pop{}_range_logit'.format(i)]) + np.array(log['pop{}_mage_logit'.format(i)]) + log['pop{}_sum_logit'.format(i)] = logit_sum + + pop_move_keys = ['pop{}_sum_logit'.format(i) for i in range(npop)] + savefile = osp.join(logDir, label, split, 'pop_sum_logit.png') + gen_plot(log, pop_move_keys, savefile, train=train) + + # Tile exploration statistics + pop_move_keys = ['pop{}_grass_tiles'.format(i) for i in range(npop)] + savefile = osp.join(logDir, label, split, 'pop_grass_tiles.png') + gen_plot(log, pop_move_keys, savefile, train=train) + + pop_move_keys = ['pop{}_forest_tiles'.format(i) for i in range(npop)] + savefile = osp.join(logDir, label, split, 'pop_forest_tiles.png') + gen_plot(log, pop_move_keys, savefile, train=train) + + pop_move_keys = ['pop{}_forest_tiles_depleted'.format(i) for i in range(npop)] + savefile = osp.join(logDir, label, split, 'pop_forest_depleted.png') + gen_plot(log, pop_move_keys, savefile, train=train) + + # pop_move_keys = ['pop{}_forest_tiles_other'.format(i) for i in range(npop)] + # savefile = osp.join(logDir, label, 'pop_forest_tiles_other.png') + # gen_plot(log, pop_move_keys, savefile, train=train) + + for i in range(npop): + forest_tiles = np.array(log['pop{}_forest_tiles'.format(i)]) + other_tiles = np.array(log['pop{}_grass_tiles'.format(i)]) + np.array(log['pop{}_forest_tiles_depleted'.format(i)]) + forest_tiles + forage_percent = forest_tiles / other_tiles + log['pop{}_forage_success'.format(i)] = forage_percent + + pop_move_keys = ['pop{}_forage_success'.format(i) for i in range(npop)] + savefile = osp.join(logDir, label, split, 'pop_forage_success.png') + gen_plot(log, pop_move_keys, savefile, train=train) + + +def individuals(exps): + for name, npop, log in exps: + try: + individual(log, name, npop) + print('Log success: ', name) + except Exception as e: + print(e) + print('Log failure: ', name) + +def joints(exps): + print('Joints...') + keys = reversed('return lifespan value value_loss pg_loss entropy grad_mean grad_std grad_min grad_max'.split()) + colors = Neon.color12() + for key in keys: + loglib.dark() + maxVal = 0 + for idx, dat in enumerate(exps): + name, _, log = dat + loglib.plot(log[key], name, colors[idx].norm, lw=3) + maxVal = max(maxVal, np.max(log[key])) + loglib.limits(ylims=[0, 50*(1+maxVal//50)]) + loglib.godsword() + loglib.save(logDir+'joint/'+key) + plt.close() + +def agents(): + exps = list(experiments.exps.keys()) + loglib.dark() + colors = Neon.color12() + maxVal = 0 + for idx, exp in enumerate(exps): + name, log = exp + c = colors[idx] + loglib.plot(log['lifespan'], name, c.norm) + maxVal = max(maxVal, np.max(log['lifespan'])) + loglib.limits(ylims=[0, 50*(1+maxVal//50)]) + loglib.godsword() + loglib.save(logDir+'/agents.png') + plt.close() + +def populations(): + pass + +def combat(): + pass + +if __name__ == '__main__': + arg = None + if len(sys.argv) > 1: + arg = sys.argv[1] + + logDir = 'resource/data/exps/' + logName = 'logs.json' + fName = 'frag.png' + + #exps = [(name, config.NPOP, loglib.load(logDir+name+'/'+logName)) + # for name, config in experiments.exps.items()] + + exps = [] + for name, config in experiments.exps.items(): + try: + exp = loglib.load(logDir + name + '/' + logName) + individual(exp, name, config.NPOP) + exps.append(exp) + print('Log success: ', name) + except Exception as e: + print(e) + print('Log failure: ', name) + + if arg == 'individual': + individuals(exps) + elif arg == 'joint': + joints(exps) + else: + individuals(exps) + joints(exps) + + # agents() diff --git a/jsuarez/extra/makeTourney.py b/jsuarez/extra/makeTourney.py new file mode 100644 index 00000000..86a2281a --- /dev/null +++ b/jsuarez/extra/makeTourney.py @@ -0,0 +1,41 @@ +from pdb import set_trace as T +import sys, torch +import os + +expPath = 'resource/data/exps/' +fName = '/bests.pth' + +def loadExp(path): + return torch.load(path)['param'] + +def saveExp(dat, path, expName): + if not os.path.exists(path): + os.makedirs(path) + torch.save({'param':dat, 'epoch':0}, path + expName) + +def runMatches(): + sz = [8, 32, 64, 128] + n = len(sz) + for prefix in ('nlaw', 'nchaos'): + for i in range(n): + for j in range(i+1, n): + v1, v2 = str(sz[i]), str(sz[j]) + runMatch(prefix, v1, v2) + +def runMatch(prefix, v1, v2): + #exp1 = loadExp(expPath + prefix + v1 + fName) + #exp2 = loadExp(expPath + prefix + v2 + fName) + + prefix = 'adam' + exp1 = loadExp(expPath + 'adamentlaw128' + fName) + exp2 = loadExp(expPath + 'adamentchaos128' + fName) + + params = torch.cat((exp1, exp2)) + path = expPath + 'tourney_' + prefix + v1 + '_' + v2 + saveExp(params, path, fName) + + +#runMatches() +runMatch(None, '128', '128') + + diff --git a/jsuarez/extra/plottourney.py b/jsuarez/extra/plottourney.py new file mode 100644 index 00000000..34210bdf --- /dev/null +++ b/jsuarez/extra/plottourney.py @@ -0,0 +1,85 @@ +import numpy as np +import sys, json +from scipy.ndimage.filters import convolve +from forge.blade.lib.enums import Neon, Color256 +from forge.blade.lib.log import Well +from matplotlib import pyplot as plt +from pdb import set_trace as T +from itertools import groupby +from collections import defaultdict +import collections +import logs as loglib +import experiments +import pickle +import os.path as osp +import os + +#Trajectory equated runs +def law128(): + rets = { + '16': [(67.16070920182207 + 67.27818177485464) / 2, 65.53879245987099, 53.50681153837636, 47.796987075763205], + '32': [74.32249298595012, (74.23310069290461 + 74.37120932322682) / 2, 62.39473940496259, 52.534221364625466], + '64': [93.80093851827966, 97.00682644982263, (90.09223534011689 + 87.19457911719405) / 2, 75.07034083273426], + '128': [109.72334511905568, 121.50424928230957, 121.95939904929756, (103.88169608454398 + 109.89533168831244) / 2], + } + return rets + +def law64(): + rets = { + '16': [70.34231691439271, 68.08311250680488, 59.693583536433955, 53.73726866287061], + '32': [81.34780761917122, 77.11407163999272, 69.42298166302263, 64.08609402748493], + '64': [119.08108040007559, 128.70866058606734, 107.03441943941378, 96.49304033434062], + '128': [228.30000553907186, 248.20682367308666, 241.56711251586333, 176.9001893357281] + } + return rets + +def law32(): + rets = { + '16': [85.87269967542795, 81.46462612460303, 76.59887029973338, 69.42979843534654], + '32': [101.75715811220914, 97.42858762722568, 93.53564789260295, 82.37687552017428], + '64': [175.1085580402948, 166.6336066014243, 158.17929884080706, 150.65147472068676], + '128': [467.5510996383706, 452.08437078645403, 453.8226233743415, 343.7846252190053] + } + return rets + +def law16(): + rets = { + '16': [106.08449913178288, 104.23812888187803, 89.77015854798023, 91.83535796395272], + '32': [118.97521974482278, 112.70542221817735, 115.6731408617378, 106.67977814200242], + '64': [205.66711549698138, 225.70099427243377, 208.46919256447077, 186.89705040539604], + '128': [735.7536877976454, 785.5794464698289, 781.9688869034464, 595.1878608252023] + } + return rets + +#Max len runs +def adamlaw128(): + rets = { + '16': [], + '32': [], + '64': [], + '128': [], + } + +def plots(x, idxs, sz): + plt.style.use('bmh') + colors = Neon.color12() + idx = 0 + for label, vals in x.items(): + #c = colors[idx % 12] + plt.plot(idxs, vals, linewidth=5, linestyle='--', marker='o', markersize=15, markerfacecolor='None', label=str(label), markeredgewidth=3) + idx += 1 + plt.grid(linestyle='--', linewidth=1) + plt.xticks(idxs) + loglib.ticks(24, Neon.BLACK.norm) + loglib.legend(24, Neon.BLACK.norm) + loglib.labels(xlabel='Opponent Population Size at Train Time', ylabel='Lifetime In Tournament', title='Tournament at Population Size '+str(sz), axsz=32, titlesz=36) + loglib.fig() + loglib.save('tourney'+str(sz)+'.png') + plt.close() + +#Rows: 16, 32, 64, 128 +rets = [law16(), law32(), law64(), law128()] +idxs = [16, 32, 64, 128] +for idx, ret in zip(idxs, rets): + plots(ret, idxs, idx) + diff --git a/jsuarez/extra/returns.py b/jsuarez/extra/returns.py new file mode 100644 index 00000000..f2ff39c9 --- /dev/null +++ b/jsuarez/extra/returns.py @@ -0,0 +1,24 @@ +import sys +import experiments +import numpy as np +import logs as loglib +from pdb import set_trace as T + +if __name__ == '__main__': + arg = None + if len(sys.argv) > 1: + arg = sys.argv[1] + + logDir = 'resource/data/exps/' + logName = 'logs.json' + fName = 'frag.png' + + exp = loglib.load(logDir + 'combatscale/' + logName) + rets = [] + for idx in range(32): + ret = np.mean(exp['lifespan' + str(idx) + '_mean'][:-50]) + rets.append((ret, idx)) + rets = sorted(rets) + print(rets) + + diff --git a/jsuarez/log.sh b/jsuarez/log.sh new file mode 100644 index 00000000..dac4dbfc --- /dev/null +++ b/jsuarez/log.sh @@ -0,0 +1,3 @@ +#tensorboard --logdir ./resource/logs/ +tensorboard --logdir ../results/interactive/gatests/ + diff --git a/jsuarez/map/edge.png b/jsuarez/map/edge.png new file mode 100644 index 00000000..77414710 Binary files /dev/null and b/jsuarez/map/edge.png differ diff --git a/jsuarez/map/edge.txt b/jsuarez/map/edge.txt new file mode 100644 index 00000000..8e4e7042 --- /dev/null +++ b/jsuarez/map/edge.txt @@ -0,0 +1,64 @@ +6 6 6 6 6 6 6 6 6 0 0 0 0 0 6 6 6 6 6 4 3 3 3 3 3 3 3 3 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 3 3 3 3 3 3 3 3 3 3 +6 6 6 6 6 6 6 6 6 2 1 0 0 0 0 6 6 6 4 1 1 1 3 3 3 3 3 3 3 6 6 6 6 6 6 6 4 6 4 4 6 6 6 6 6 6 6 6 6 6 6 6 4 1 1 3 3 3 3 3 3 3 3 3 +6 6 6 2 1 2 6 6 2 1 1 2 0 0 0 6 6 4 1 1 1 2 1 1 1 3 3 3 3 3 3 6 6 6 4 1 1 1 1 1 1 4 6 4 4 6 6 6 6 6 6 4 1 1 1 3 3 3 3 3 3 3 3 3 +6 6 1 2 1 1 1 1 1 1 2 1 1 0 0 0 4 4 1 1 1 1 1 1 1 1 1 3 3 3 3 3 6 4 1 1 1 1 1 1 1 1 1 1 1 1 4 6 6 6 4 1 1 1 1 3 3 3 3 3 3 3 3 3 +6 1 1 1 2 1 2 1 2 1 1 2 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 4 1 1 1 1 1 1 1 1 1 1 1 1 1 4 6 4 1 1 1 1 1 1 3 3 3 3 3 3 3 3 +6 1 2 1 2 1 1 2 1 1 2 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 6 6 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 +6 2 1 1 1 1 2 1 2 1 1 1 0 0 0 1 1 1 1 2 1 1 1 1 1 2 1 1 1 3 3 3 3 6 4 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 +6 1 2 2 1 2 1 2 1 1 2 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 2 1 1 1 3 3 3 3 3 3 3 +6 6 2 1 1 1 1 1 2 2 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 3 3 3 3 3 3 +6 6 1 1 2 1 2 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 3 3 3 3 6 4 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 +6 6 1 2 1 1 1 1 2 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 4 6 4 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 +6 6 6 1 1 2 1 1 1 0 0 0 0 5 5 5 5 0 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 3 3 3 +6 6 6 2 1 1 2 1 2 0 0 0 5 5 5 5 5 0 0 0 1 1 1 1 1 1 1 1 1 2 1 1 3 3 3 3 6 6 1 1 1 1 2 1 1 1 1 1 2 1 1 2 1 2 1 1 2 1 1 1 1 6 3 3 +6 6 6 1 2 1 1 1 0 0 0 5 5 5 4 5 5 5 0 0 0 1 1 1 2 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 2 1 2 2 1 2 1 1 1 1 1 1 1 4 6 6 3 +6 6 6 1 1 1 1 1 0 0 0 5 5 5 5 5 5 4 5 0 0 0 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 2 1 1 1 1 1 1 1 2 1 2 1 2 2 1 1 1 1 1 1 6 6 6 6 +6 6 1 1 2 1 2 1 0 0 0 5 4 5 5 4 5 5 5 5 0 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 2 1 1 2 1 2 1 2 1 1 1 1 1 2 1 1 6 6 6 6 +6 6 2 1 1 1 1 1 0 0 0 5 5 5 5 5 5 4 5 5 0 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 2 1 2 1 2 2 1 1 1 1 1 4 6 6 6 6 +6 6 1 1 1 1 1 1 1 0 0 0 5 5 4 5 5 5 5 5 0 0 1 1 1 1 1 1 1 1 2 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 2 1 2 1 1 1 1 1 1 1 6 6 6 6 6 +6 1 1 1 1 1 1 1 1 0 0 0 0 5 5 5 5 5 5 0 0 0 1 1 1 1 2 1 1 1 1 1 1 1 1 3 3 3 3 1 1 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 +6 1 1 1 1 1 1 1 1 1 0 0 0 0 5 5 5 5 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 6 6 6 6 6 +3 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 6 6 6 6 +3 3 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 4 6 6 6 +3 3 3 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 +3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 2 1 1 1 1 1 1 1 1 2 1 1 1 1 2 1 6 6 6 +3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 6 6 +3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 4 6 +3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 2 1 1 1 2 1 1 2 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 +3 3 3 3 3 3 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 2 1 6 6 6 +3 3 3 3 3 3 1 2 1 1 1 1 1 1 2 1 1 1 1 2 1 1 1 1 1 2 1 1 1 2 1 1 1 2 1 1 1 1 1 3 3 3 1 1 1 1 1 2 1 1 2 1 1 2 1 1 1 1 1 1 1 4 6 6 +3 3 3 3 3 3 3 1 1 2 1 1 2 1 1 1 1 2 1 1 1 1 2 1 1 1 1 2 1 1 1 2 1 1 1 1 2 1 1 3 3 3 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 6 6 +3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 2 1 1 1 1 2 1 1 1 1 1 1 1 1 4 6 +3 3 3 3 3 3 3 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 3 3 3 3 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 +3 3 3 3 3 3 3 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 3 3 3 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 +3 3 3 3 3 3 3 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 2 1 1 1 1 2 1 1 1 2 1 1 1 1 1 2 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 0 0 +3 3 3 3 3 3 1 1 1 1 2 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 1 3 3 3 1 1 1 1 2 1 1 1 1 1 1 1 1 2 1 1 1 0 0 0 +3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 2 0 0 0 0 +3 3 3 3 3 2 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 +3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 2 1 1 1 1 0 0 0 0 +3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 3 3 3 3 1 1 1 2 1 1 1 1 1 1 1 1 1 2 1 1 0 0 0 0 +3 3 3 3 1 1 1 1 1 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 +3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 0 0 0 +3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 2 1 1 0 0 0 +3 3 3 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 +3 3 3 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 0 0 0 +3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 2 1 1 1 1 1 2 1 1 1 1 1 1 1 2 1 1 1 1 0 0 +3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 +3 3 1 1 1 1 1 1 1 1 1 1 1 5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 1 1 1 1 1 2 0 0 +3 3 1 1 1 1 1 1 1 1 1 1 5 5 5 1 1 1 1 1 1 1 2 1 1 1 1 2 1 1 3 3 3 3 3 3 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 +3 1 1 1 1 1 1 1 1 1 1 5 5 5 5 5 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 2 1 1 1 0 0 0 +6 1 1 1 1 1 1 2 1 1 1 1 5 5 5 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 +6 6 1 2 1 1 1 1 1 1 1 1 1 5 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 1 1 1 0 0 0 0 +6 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 6 6 6 6 1 1 2 1 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 +6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 3 3 3 3 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 2 1 1 0 0 0 0 +6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 6 6 4 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 +6 6 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 6 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 0 0 0 0 +6 6 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 6 6 6 4 1 1 1 2 1 1 1 2 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 0 0 0 0 +6 6 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 +6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 2 1 3 3 3 6 6 6 4 1 1 1 1 1 1 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 +6 6 6 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 6 6 6 2 1 2 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 2 1 1 1 1 2 1 0 0 0 +6 6 6 6 4 1 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 3 3 3 3 3 3 6 4 1 1 1 1 1 1 2 1 1 6 6 4 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 +6 6 6 6 6 4 6 4 1 1 6 4 6 4 1 1 1 1 1 3 3 3 3 3 3 3 3 6 6 1 2 1 1 1 1 1 1 4 6 6 6 6 4 1 1 1 2 1 1 0 0 0 0 0 0 0 1 2 1 0 0 0 0 0 +6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 1 1 1 3 3 3 3 3 3 3 3 3 3 6 6 1 1 1 1 1 4 6 6 6 6 6 6 6 6 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 3 3 3 3 3 3 3 3 3 3 3 3 3 6 4 6 6 4 6 6 6 6 6 6 6 6 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/jsuarez/map/edgeville.txt b/jsuarez/map/edgeville.txt new file mode 100644 index 00000000..8e4e7042 --- /dev/null +++ b/jsuarez/map/edgeville.txt @@ -0,0 +1,64 @@ +6 6 6 6 6 6 6 6 6 0 0 0 0 0 6 6 6 6 6 4 3 3 3 3 3 3 3 3 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 3 3 3 3 3 3 3 3 3 3 +6 6 6 6 6 6 6 6 6 2 1 0 0 0 0 6 6 6 4 1 1 1 3 3 3 3 3 3 3 6 6 6 6 6 6 6 4 6 4 4 6 6 6 6 6 6 6 6 6 6 6 6 4 1 1 3 3 3 3 3 3 3 3 3 +6 6 6 2 1 2 6 6 2 1 1 2 0 0 0 6 6 4 1 1 1 2 1 1 1 3 3 3 3 3 3 6 6 6 4 1 1 1 1 1 1 4 6 4 4 6 6 6 6 6 6 4 1 1 1 3 3 3 3 3 3 3 3 3 +6 6 1 2 1 1 1 1 1 1 2 1 1 0 0 0 4 4 1 1 1 1 1 1 1 1 1 3 3 3 3 3 6 4 1 1 1 1 1 1 1 1 1 1 1 1 4 6 6 6 4 1 1 1 1 3 3 3 3 3 3 3 3 3 +6 1 1 1 2 1 2 1 2 1 1 2 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 4 1 1 1 1 1 1 1 1 1 1 1 1 1 4 6 4 1 1 1 1 1 1 3 3 3 3 3 3 3 3 +6 1 2 1 2 1 1 2 1 1 2 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 6 6 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 +6 2 1 1 1 1 2 1 2 1 1 1 0 0 0 1 1 1 1 2 1 1 1 1 1 2 1 1 1 3 3 3 3 6 4 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 +6 1 2 2 1 2 1 2 1 1 2 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 2 1 1 1 3 3 3 3 3 3 3 +6 6 2 1 1 1 1 1 2 2 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 3 3 3 3 3 3 +6 6 1 1 2 1 2 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 3 3 3 3 6 4 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 +6 6 1 2 1 1 1 1 2 1 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 4 6 4 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 +6 6 6 1 1 2 1 1 1 0 0 0 0 5 5 5 5 0 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 3 3 3 +6 6 6 2 1 1 2 1 2 0 0 0 5 5 5 5 5 0 0 0 1 1 1 1 1 1 1 1 1 2 1 1 3 3 3 3 6 6 1 1 1 1 2 1 1 1 1 1 2 1 1 2 1 2 1 1 2 1 1 1 1 6 3 3 +6 6 6 1 2 1 1 1 0 0 0 5 5 5 4 5 5 5 0 0 0 1 1 1 2 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 2 1 2 2 1 2 1 1 1 1 1 1 1 4 6 6 3 +6 6 6 1 1 1 1 1 0 0 0 5 5 5 5 5 5 4 5 0 0 0 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 2 1 1 1 1 1 1 1 2 1 2 1 2 2 1 1 1 1 1 1 6 6 6 6 +6 6 1 1 2 1 2 1 0 0 0 5 4 5 5 4 5 5 5 5 0 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 2 1 1 2 1 2 1 2 1 1 1 1 1 2 1 1 6 6 6 6 +6 6 2 1 1 1 1 1 0 0 0 5 5 5 5 5 5 4 5 5 0 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 2 1 2 1 2 2 1 1 1 1 1 4 6 6 6 6 +6 6 1 1 1 1 1 1 1 0 0 0 5 5 4 5 5 5 5 5 0 0 1 1 1 1 1 1 1 1 2 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 2 1 2 1 1 1 1 1 1 1 6 6 6 6 6 +6 1 1 1 1 1 1 1 1 0 0 0 0 5 5 5 5 5 5 0 0 0 1 1 1 1 2 1 1 1 1 1 1 1 1 3 3 3 3 1 1 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 +6 1 1 1 1 1 1 1 1 1 0 0 0 0 5 5 5 5 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 6 6 6 6 6 +3 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 6 6 6 6 +3 3 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 4 6 6 6 +3 3 3 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 +3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 2 1 1 1 1 1 1 1 1 2 1 1 1 1 2 1 6 6 6 +3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 6 6 +3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 4 6 +3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 2 1 1 1 2 1 1 2 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 +3 3 3 3 3 3 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 2 1 6 6 6 +3 3 3 3 3 3 1 2 1 1 1 1 1 1 2 1 1 1 1 2 1 1 1 1 1 2 1 1 1 2 1 1 1 2 1 1 1 1 1 3 3 3 1 1 1 1 1 2 1 1 2 1 1 2 1 1 1 1 1 1 1 4 6 6 +3 3 3 3 3 3 3 1 1 2 1 1 2 1 1 1 1 2 1 1 1 1 2 1 1 1 1 2 1 1 1 2 1 1 1 1 2 1 1 3 3 3 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 6 6 +3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 2 1 1 1 1 2 1 1 1 1 1 1 1 1 4 6 +3 3 3 3 3 3 3 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 3 3 3 3 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 6 +3 3 3 3 3 3 3 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 3 3 3 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 6 +3 3 3 3 3 3 3 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 2 1 1 1 1 2 1 1 1 2 1 1 1 1 1 2 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 0 0 +3 3 3 3 3 3 1 1 1 1 2 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 1 3 3 3 1 1 1 1 2 1 1 1 1 1 1 1 1 2 1 1 1 0 0 0 +3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 2 0 0 0 0 +3 3 3 3 3 2 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 2 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 +3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 2 1 1 1 1 0 0 0 0 +3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 3 3 3 3 1 1 1 2 1 1 1 1 1 1 1 1 1 2 1 1 0 0 0 0 +3 3 3 3 1 1 1 1 1 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 +3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 0 0 0 +3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 2 1 1 0 0 0 +3 3 3 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 +3 3 3 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 0 0 0 +3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 2 1 1 1 1 1 2 1 1 1 1 1 1 1 2 1 1 1 1 0 0 +3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 +3 3 1 1 1 1 1 1 1 1 1 1 1 5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 1 1 1 1 1 2 0 0 +3 3 1 1 1 1 1 1 1 1 1 1 5 5 5 1 1 1 1 1 1 1 2 1 1 1 1 2 1 1 3 3 3 3 3 3 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 +3 1 1 1 1 1 1 1 1 1 1 5 5 5 5 5 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 2 1 1 1 0 0 0 +6 1 1 1 1 1 1 2 1 1 1 1 5 5 5 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 +6 6 1 2 1 1 1 1 1 1 1 1 1 5 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 1 1 1 0 0 0 0 +6 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 6 6 6 6 1 1 2 1 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 +6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 3 3 3 3 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 2 1 1 0 0 0 0 +6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 6 6 4 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 +6 6 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 6 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 0 0 0 0 +6 6 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 6 6 6 4 1 1 1 2 1 1 1 2 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 0 0 0 0 +6 6 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 +6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 2 1 3 3 3 6 6 6 4 1 1 1 1 1 1 1 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 +6 6 6 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 6 6 6 2 1 2 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 2 1 1 1 1 2 1 0 0 0 +6 6 6 6 4 1 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 3 3 3 3 3 3 6 4 1 1 1 1 1 1 2 1 1 6 6 4 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 +6 6 6 6 6 4 6 4 1 1 6 4 6 4 1 1 1 1 1 3 3 3 3 3 3 3 3 6 6 1 2 1 1 1 1 1 1 4 6 6 6 6 4 1 1 1 2 1 1 0 0 0 0 0 0 0 1 2 1 0 0 0 0 0 +6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 1 1 1 3 3 3 3 3 3 3 3 3 3 6 6 1 1 1 1 1 4 6 6 6 6 6 6 6 6 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 3 3 3 3 3 3 3 3 3 3 3 3 3 6 4 6 6 4 6 6 6 6 6 6 6 6 6 6 6 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/jsuarez/map/fullmap.png b/jsuarez/map/fullmap.png new file mode 100644 index 00000000..b2030c8c Binary files /dev/null and b/jsuarez/map/fullmap.png differ diff --git a/jsuarez/map/fullmap.txt b/jsuarez/map/fullmap.txt new file mode 100644 index 00000000..fea83d83 --- /dev/null +++ b/jsuarez/map/fullmap.txt @@ -0,0 +1,128 @@ +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 0 +0 2 2 2 2 4 1 1 4 4 2 2 2 2 1 1 1 1 1 1 1 1 2 2 2 2 2 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 0 +0 2 2 2 4 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 0 +0 2 2 1 1 1 1 1 1 1 4 2 2 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 4 1 1 1 1 1 1 4 2 1 1 1 1 1 1 1 2 1 1 1 1 2 1 1 1 1 1 1 1 1 3 3 3 3 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 3 3 3 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 2 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 4 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 3 3 1 1 1 2 1 1 1 1 1 1 3 3 3 3 1 1 1 1 2 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 1 1 4 1 1 1 1 1 1 1 1 1 1 2 1 1 3 3 3 3 1 1 2 1 1 1 1 1 1 3 3 3 3 1 1 1 1 2 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 1 4 1 4 1 1 1 1 1 1 1 1 1 2 1 1 3 3 3 3 1 1 2 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 3 3 1 1 1 2 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 2 1 1 1 1 1 2 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 2 6 6 6 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 6 6 6 6 6 2 2 2 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 2 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 2 6 6 6 6 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 2 2 6 6 6 6 6 6 6 2 2 2 2 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 2 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 6 6 2 2 2 2 6 6 6 6 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 2 2 2 2 6 6 6 6 6 6 6 6 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 6 6 6 2 2 2 2 6 6 6 6 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 6 6 6 6 6 6 6 2 2 2 2 2 1 1 1 1 1 2 1 1 2 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 6 6 1 2 2 2 1 6 6 6 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 1 1 1 2 2 2 2 2 6 6 6 6 6 6 6 6 6 2 2 2 1 1 1 1 1 1 1 1 1 2 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 6 3 3 3 1 1 1 1 1 1 6 6 1 2 2 1 6 6 6 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 1 1 1 1 2 2 2 2 2 2 2 2 6 6 6 6 6 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 3 3 3 1 1 1 1 1 1 6 6 6 1 1 1 6 6 6 1 1 1 1 2 1 1 1 1 1 2 2 2 0 +0 2 2 1 1 1 1 1 1 1 2 2 2 2 2 2 1 6 6 6 6 6 6 6 6 2 2 1 1 2 1 2 1 1 1 1 1 3 3 3 3 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 3 3 3 3 1 1 1 1 1 6 6 6 1 1 1 6 6 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 1 1 1 2 1 1 1 1 1 1 2 2 2 2 2 2 2 2 6 6 6 6 6 6 2 2 2 1 1 1 1 2 1 1 1 1 3 3 3 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 3 3 3 3 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 1 1 1 1 1 1 1 2 1 1 1 1 1 2 2 2 2 2 2 6 6 6 6 6 6 2 2 1 1 1 1 1 1 2 1 1 3 3 3 6 6 6 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 6 6 6 6 2 2 1 1 1 2 1 1 1 1 1 1 3 3 3 6 6 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 3 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 1 2 1 1 3 3 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 4 6 6 6 6 2 2 1 1 1 1 1 1 2 1 1 3 3 3 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 3 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 1 3 3 1 1 1 1 1 2 2 2 1 1 1 1 2 1 1 4 6 6 6 6 2 2 1 2 1 1 2 1 1 1 1 3 3 3 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 2 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 4 4 6 6 6 2 2 1 1 1 1 1 1 1 1 1 3 3 3 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 6 6 6 6 1 2 1 1 1 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 6 6 6 6 2 2 1 2 1 2 1 2 1 3 3 3 3 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 6 6 6 2 2 1 1 1 1 1 1 1 3 3 3 3 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 3 3 3 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 2 2 2 1 1 1 3 3 3 1 1 1 1 1 1 1 3 3 3 1 1 6 6 6 2 2 1 1 2 1 2 1 1 3 3 3 3 6 6 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 3 3 3 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 2 2 2 1 1 1 3 3 3 3 3 1 1 1 1 1 1 3 3 1 1 1 1 6 6 6 2 2 1 1 1 1 1 1 3 3 3 3 6 6 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 2 2 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 2 2 1 1 1 1 2 1 3 3 3 3 6 6 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 2 2 1 1 1 1 1 6 6 6 3 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 2 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 2 2 1 1 1 1 1 1 1 6 6 6 6 2 2 1 1 2 1 1 3 3 3 3 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 6 6 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 1 1 2 2 0 +0 2 2 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 2 2 1 1 1 2 1 1 1 1 6 6 6 2 2 2 1 1 1 1 3 3 3 3 6 6 6 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 6 6 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 1 2 2 0 +0 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 6 6 6 6 2 2 1 2 1 1 3 3 3 3 3 6 6 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 6 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 6 6 6 6 1 1 1 1 1 1 1 2 2 0 +0 6 2 2 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 6 6 6 6 2 2 1 1 2 1 1 3 3 3 3 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 6 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 2 2 0 +0 6 2 2 2 1 1 1 2 2 2 1 1 1 1 2 2 1 1 1 1 3 3 3 1 1 1 1 6 6 6 6 2 2 1 1 1 1 1 1 3 3 3 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 6 6 6 3 3 3 1 1 1 1 1 3 3 1 1 1 1 1 1 2 2 3 6 6 6 1 1 2 1 1 1 1 2 2 0 +0 6 6 2 2 2 1 1 1 2 2 1 1 1 2 2 2 1 1 1 1 1 3 3 1 1 2 1 6 6 6 6 2 2 1 2 1 1 1 1 3 3 3 3 6 6 6 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 3 3 3 1 1 1 1 1 1 3 3 1 1 1 1 1 6 2 2 2 6 6 6 1 1 1 1 1 1 2 2 2 0 +0 6 6 2 2 2 2 1 1 1 1 1 1 1 2 2 1 1 1 2 1 1 1 1 1 1 1 1 2 6 6 6 2 2 1 1 1 1 1 1 1 3 3 3 3 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 2 2 2 2 6 6 1 1 1 1 1 1 2 2 2 0 +0 6 6 6 4 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 6 6 6 2 2 1 1 1 1 1 1 1 3 3 3 3 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 2 2 2 2 6 6 1 1 1 1 1 2 2 2 2 0 +0 6 6 6 6 4 4 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 2 6 6 6 2 2 1 1 1 1 1 1 1 1 3 3 3 3 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 3 6 6 1 1 1 1 1 2 2 2 2 0 +0 6 6 6 6 6 6 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 2 2 6 6 2 1 2 6 6 2 2 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 6 6 6 1 1 1 1 1 2 2 2 2 0 +0 3 3 6 6 6 6 6 6 6 2 2 2 2 2 2 2 2 2 2 2 2 2 6 6 2 2 1 2 6 6 2 2 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 6 6 1 1 1 1 1 1 2 2 2 2 0 +0 3 3 3 6 6 6 6 6 6 6 6 6 2 2 2 2 2 2 2 6 6 6 6 2 2 1 1 2 6 6 2 2 2 2 2 1 1 1 1 1 1 1 3 3 3 3 6 6 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 3 3 3 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 0 +0 3 3 3 3 2 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 2 2 2 1 1 1 2 6 6 6 2 2 2 2 2 1 1 1 1 1 1 3 3 3 3 6 6 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 0 +0 3 3 3 1 2 2 6 6 6 6 6 6 6 6 6 6 6 6 6 2 2 2 1 1 1 1 1 2 2 6 6 6 2 2 2 2 2 1 1 1 1 1 1 3 3 3 6 6 6 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 2 2 2 2 2 0 +0 3 3 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 2 6 6 6 6 2 2 2 2 1 1 1 1 1 1 3 3 3 3 6 6 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 2 1 1 1 1 2 2 2 2 2 0 +0 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 6 6 6 6 2 2 2 2 1 1 1 1 1 1 3 3 3 3 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 3 3 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 2 2 2 2 2 0 +0 2 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 3 3 3 1 1 2 6 6 6 6 6 1 2 2 2 2 6 6 1 1 3 3 3 3 6 6 6 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 3 3 1 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 1 1 2 2 2 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 2 2 2 6 6 6 6 6 6 6 6 6 6 1 1 3 3 3 3 3 6 6 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 2 2 2 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 2 2 2 6 6 6 6 6 6 6 1 1 1 1 3 3 3 3 6 6 6 2 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 1 2 2 2 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 3 3 3 3 6 6 6 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 2 2 2 2 2 0 +0 2 1 1 1 1 1 1 2 1 1 1 1 1 2 1 1 1 1 1 2 1 1 1 3 3 3 3 3 1 1 1 1 1 2 1 1 1 1 1 1 1 2 2 2 1 1 3 3 3 3 6 6 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 2 2 2 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 3 3 3 3 6 6 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 2 2 2 2 2 0 +0 2 2 1 1 1 1 1 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 2 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 3 3 3 3 6 6 6 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 2 2 2 2 2 0 +0 2 2 2 1 1 1 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 2 2 2 2 2 2 1 1 1 1 1 1 3 3 3 3 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 2 1 1 1 1 2 1 1 2 2 2 2 2 2 1 1 1 2 1 1 3 3 3 3 6 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 6 6 6 1 1 1 1 1 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 1 1 1 1 1 1 3 3 3 3 1 6 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 6 6 6 6 1 1 2 1 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 2 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 3 3 3 3 1 1 6 6 6 6 6 6 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 2 2 2 2 2 0 +0 2 2 2 2 2 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 4 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 2 2 2 2 2 0 +0 2 2 2 2 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 2 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 3 3 3 3 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 2 2 2 2 2 0 +0 2 2 2 2 1 1 1 1 1 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 2 4 4 1 1 1 2 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 6 6 6 6 6 6 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 3 3 3 3 1 1 1 1 1 6 6 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 2 2 2 2 2 0 +0 2 2 2 2 1 1 1 1 1 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 4 4 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 6 6 6 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 2 2 2 2 0 +0 2 2 2 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 4 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 6 6 2 2 2 2 3 3 6 6 6 1 1 1 1 1 2 1 1 1 1 2 2 2 2 0 +0 2 2 2 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 2 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 2 1 1 1 1 1 1 6 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 6 6 2 2 2 2 3 6 6 6 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 0 +0 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 2 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 6 6 6 6 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 6 6 2 2 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 6 6 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 2 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 3 3 3 3 3 3 3 3 3 3 3 1 1 1 2 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 6 6 6 6 6 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 6 6 6 6 6 6 6 6 1 1 2 1 1 1 1 1 1 1 2 2 0 +0 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 6 6 6 6 6 6 1 1 1 1 2 1 1 1 1 2 2 0 +0 3 3 3 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 1 1 2 2 0 +0 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 2 2 0 +0 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 2 2 0 +0 3 3 3 3 3 3 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 2 2 2 0 +0 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 2 2 2 0 +0 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 2 2 2 2 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/jsuarez/map/largemap.png b/jsuarez/map/largemap.png new file mode 100644 index 00000000..e920e172 Binary files /dev/null and b/jsuarez/map/largemap.png differ diff --git a/jsuarez/map/largemap.txt b/jsuarez/map/largemap.txt new file mode 100644 index 00000000..994ac9bc --- /dev/null +++ b/jsuarez/map/largemap.txt @@ -0,0 +1,64 @@ +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 2 3 3 2 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 2 3 3 2 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 0 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 0 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 2 2 2 1 0 0 0 0 0 0 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 1 2 2 2 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 2 2 1 1 1 1 1 1 1 1 1 2 2 3 3 2 2 1 1 1 1 1 1 1 1 1 2 2 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 4 2 2 3 3 2 2 4 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 4 4 4 1 3 3 1 4 4 4 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 4 1 1 3 3 1 1 4 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 4 1 1 1 1 1 1 1 1 3 2 2 3 1 1 1 1 1 1 1 1 4 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 4 4 4 1 1 1 1 1 3 2 2 2 2 2 2 3 1 1 1 1 1 4 4 4 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 0 2 2 4 1 1 1 1 1 1 2 2 2 2 2 2 2 2 1 1 1 1 1 1 4 2 2 0 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 0 2 2 1 1 1 1 1 1 3 2 2 2 2 2 2 2 2 3 1 1 1 1 1 1 2 2 0 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 0 +0 3 3 3 3 3 3 3 3 2 3 3 3 3 3 3 3 3 0 3 3 3 3 3 3 3 3 2 2 2 2 3 3 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 3 3 3 3 3 3 3 3 0 +0 3 3 3 3 3 3 3 3 2 3 3 3 3 3 3 3 3 0 3 3 3 3 3 3 3 3 2 2 2 2 3 3 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 3 3 3 3 3 3 3 3 0 +0 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 0 2 2 1 1 1 1 1 1 3 2 2 2 2 2 2 2 2 3 1 1 1 1 1 1 2 2 0 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 0 2 2 4 1 1 1 1 1 1 2 2 2 2 2 2 2 2 1 1 1 1 1 1 4 2 2 0 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 4 4 4 1 1 1 1 1 3 2 2 2 2 2 2 3 1 1 1 1 1 4 4 4 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 4 1 1 1 1 1 1 1 1 3 2 2 3 1 1 1 1 1 1 1 1 4 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 4 1 1 3 3 1 1 4 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 4 4 4 1 3 3 1 4 4 4 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 4 2 2 3 3 2 2 4 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 2 2 1 1 1 1 1 1 1 1 1 2 2 3 3 2 2 1 1 1 1 1 1 1 1 1 2 2 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 2 2 2 1 0 0 0 0 0 0 0 0 0 0 3 3 0 0 0 0 0 0 0 0 0 0 1 2 2 2 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 +0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 0 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 0 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 2 3 3 2 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 2 3 3 2 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 2 2 2 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/jsuarez/map/map.png b/jsuarez/map/map.png new file mode 100644 index 00000000..7008f50f Binary files /dev/null and b/jsuarez/map/map.png differ diff --git a/jsuarez/map/medmap.png b/jsuarez/map/medmap.png new file mode 100644 index 00000000..a2592f21 Binary files /dev/null and b/jsuarez/map/medmap.png differ diff --git a/jsuarez/map/medmap.txt b/jsuarez/map/medmap.txt new file mode 100644 index 00000000..e85fdd79 --- /dev/null +++ b/jsuarez/map/medmap.txt @@ -0,0 +1,32 @@ +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 3 3 3 2 2 2 2 2 2 2 2 1 1 1 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 3 3 3 3 3 2 2 2 2 2 2 1 1 4 1 1 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 3 3 3 3 3 1 1 1 1 1 1 1 4 4 4 1 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 3 3 3 3 3 1 1 1 1 1 1 1 1 4 1 1 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 3 3 3 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 1 1 1 1 1 3 3 1 1 1 1 1 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 1 1 1 1 3 3 3 3 1 1 1 1 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 1 1 1 3 3 3 3 3 3 1 1 1 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 1 1 1 3 3 3 3 3 3 1 1 1 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 1 1 1 1 3 3 3 3 1 1 1 1 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 1 1 1 1 1 3 3 1 1 1 1 4 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 4 4 4 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 1 1 4 1 1 1 1 1 1 1 1 4 4 4 4 4 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 1 4 4 4 1 1 1 1 1 1 4 4 4 4 4 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 1 1 4 1 1 2 2 2 2 2 2 4 4 4 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 1 1 1 2 2 2 2 2 2 2 2 4 2 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/jsuarez/map/oldfull.png b/jsuarez/map/oldfull.png new file mode 100644 index 00000000..1607132f Binary files /dev/null and b/jsuarez/map/oldfull.png differ diff --git a/jsuarez/map/oldfull.txt b/jsuarez/map/oldfull.txt new file mode 100644 index 00000000..1e40e7cf --- /dev/null +++ b/jsuarez/map/oldfull.txt @@ -0,0 +1,128 @@ +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 3 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 0 +0 2 2 2 2 4 1 1 4 4 1 1 1 1 2 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 0 +0 2 2 2 4 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 0 +0 2 2 1 1 1 1 1 1 1 4 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 2 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 4 1 1 1 1 1 1 4 1 2 1 1 1 1 1 1 2 1 1 1 1 2 1 1 1 1 1 1 1 1 3 3 3 3 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 3 3 3 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 2 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 2 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 1 1 4 1 1 1 1 1 1 2 1 1 1 2 1 1 3 3 3 3 1 1 2 1 1 2 1 1 1 3 3 3 3 1 1 1 1 2 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 1 4 1 4 1 1 1 1 1 1 1 1 1 2 1 1 3 3 3 3 1 1 2 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 2 2 2 1 1 1 1 3 1 1 1 2 1 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 6 6 2 1 1 1 3 3 3 1 1 1 1 1 1 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 2 6 6 6 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 6 6 6 6 6 3 3 3 3 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 2 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 2 6 6 6 6 6 6 3 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 2 2 6 6 6 6 6 6 6 1 1 1 1 1 1 2 1 1 1 1 2 1 1 1 1 1 1 1 1 2 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 6 6 2 2 2 2 6 6 6 6 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 2 2 2 2 6 6 6 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 6 6 6 2 2 2 2 6 6 6 6 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 6 6 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 6 6 1 2 2 2 1 6 6 6 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 6 6 6 6 6 6 6 6 6 2 1 1 1 1 1 1 2 1 1 1 1 2 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 6 3 3 3 1 1 1 1 1 1 6 6 1 2 2 1 6 6 6 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 2 2 2 2 2 2 2 2 2 2 2 2 2 6 6 6 6 6 6 6 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 3 3 3 1 1 1 1 1 1 6 6 6 1 1 1 6 6 6 1 1 1 1 2 1 1 1 1 1 2 2 2 0 +0 2 2 2 1 1 1 1 1 1 2 2 2 2 2 2 1 6 6 6 6 6 6 6 6 1 1 1 1 2 1 1 1 1 1 1 1 3 3 3 3 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 3 3 3 3 1 1 1 1 1 6 6 6 1 1 1 6 6 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 6 6 6 6 6 6 2 1 1 1 1 1 1 1 1 1 1 1 3 3 3 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 3 3 3 3 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 6 6 6 6 6 6 1 1 1 1 1 1 1 1 2 1 1 3 3 3 6 6 6 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 1 6 6 6 6 1 1 1 1 1 2 1 1 1 1 1 1 3 3 3 6 6 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 3 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 2 1 1 1 1 3 3 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 4 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 3 3 3 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 3 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 1 3 3 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 4 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 3 3 3 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 2 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 4 4 6 6 6 1 2 1 1 1 1 2 1 1 1 1 3 3 3 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 6 6 6 6 1 2 1 1 1 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 3 3 1 1 6 6 6 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 3 3 3 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 2 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 3 3 1 1 6 6 6 1 1 1 1 1 1 2 1 1 3 3 3 3 6 6 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 3 3 3 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 2 2 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 3 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 3 3 3 3 6 6 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 2 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 2 1 1 1 1 3 3 3 3 6 6 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 2 2 1 1 1 1 1 6 6 6 3 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 2 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 3 3 3 3 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 6 6 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 2 1 1 1 1 6 6 6 2 1 1 1 1 1 1 3 3 3 3 6 6 6 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 6 6 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 1 2 2 0 +0 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 6 6 6 6 1 1 1 2 1 1 3 3 3 3 3 6 6 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 6 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 6 6 6 6 1 1 1 1 1 1 1 2 2 0 +0 6 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 3 3 3 3 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 6 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 2 2 0 +0 6 2 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 3 3 3 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 6 6 6 3 3 3 1 1 1 1 1 3 3 1 1 1 1 1 1 2 2 3 6 6 6 1 1 2 1 1 1 1 2 2 0 +0 6 6 2 1 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 1 1 3 3 3 1 2 1 6 6 6 6 2 1 1 1 1 1 1 1 3 3 3 3 6 6 6 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 3 3 3 1 1 1 1 1 1 3 3 1 1 1 1 1 6 2 2 2 6 6 6 1 1 1 1 1 1 2 2 2 0 +0 6 6 2 2 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 3 3 1 1 1 1 1 6 6 6 1 1 1 1 1 2 1 1 1 3 3 3 3 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 2 2 2 2 6 6 1 1 1 1 1 1 2 2 2 0 +0 6 6 6 4 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 2 2 2 2 6 6 1 1 1 1 1 2 2 2 2 0 +0 6 6 6 6 4 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 2 1 1 6 6 1 1 1 2 1 1 1 1 1 1 3 3 3 3 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 3 6 6 1 1 1 1 1 2 2 2 2 0 +0 6 6 6 6 6 6 2 2 2 2 2 1 1 1 1 1 2 1 1 1 1 1 1 6 6 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 6 6 6 1 1 1 1 1 2 2 2 2 0 +0 3 3 6 6 6 6 6 6 6 2 2 2 2 2 2 2 2 2 1 1 1 1 6 6 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 6 6 1 1 1 1 1 1 2 2 2 2 0 +0 3 3 3 6 6 6 6 6 6 6 6 6 2 2 2 2 2 2 2 6 6 6 6 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 3 3 3 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 0 +0 3 3 3 3 1 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 0 +0 3 3 3 1 1 1 6 6 6 6 6 6 6 6 6 6 6 6 6 1 1 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 2 1 1 1 1 3 3 3 6 6 6 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 2 2 2 2 2 0 +0 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 2 1 1 1 1 2 2 2 2 2 0 +0 3 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 3 3 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 2 2 2 2 2 0 +0 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 6 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 3 3 1 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 1 1 2 2 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 6 6 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 2 2 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 2 1 1 1 1 1 1 2 1 1 1 1 1 1 3 3 3 3 6 6 6 2 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 1 2 2 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 6 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 2 2 2 2 2 0 +0 1 1 1 1 1 1 1 2 1 1 1 1 1 2 1 1 1 1 1 2 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 6 6 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 2 2 2 2 2 0 +0 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 3 3 3 3 6 6 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 2 2 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 2 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 3 3 3 3 6 6 6 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 2 2 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 3 3 3 3 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 2 2 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 3 3 3 3 6 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 6 6 6 1 1 1 1 1 2 2 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 3 3 3 3 1 6 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 6 6 6 6 1 1 2 1 2 2 2 2 2 0 +0 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 6 6 6 6 6 6 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 2 2 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 4 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 2 2 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 2 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 3 3 3 3 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 2 2 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1 1 1 1 2 4 4 1 1 1 2 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 6 6 6 6 6 6 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 3 3 3 3 1 1 1 1 1 6 6 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 2 2 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 4 4 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 6 6 6 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 2 2 2 2 0 +0 1 2 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 4 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 6 6 2 2 2 2 3 3 6 6 6 1 1 1 1 1 2 1 1 1 1 2 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 2 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 2 1 1 1 1 1 1 6 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 6 6 2 2 2 2 3 6 6 6 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 2 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 6 6 6 6 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 6 6 2 2 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 6 6 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 3 3 3 3 3 3 3 3 3 1 1 1 2 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 6 6 6 6 6 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 6 6 6 6 6 6 6 6 1 1 2 1 1 1 1 1 1 1 2 2 0 +0 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 6 6 6 6 6 6 1 1 1 1 2 1 1 1 1 2 2 0 +0 3 3 3 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 1 1 1 2 2 0 +0 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 2 2 0 +0 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 2 2 0 +0 3 3 3 3 3 3 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 2 2 2 0 +0 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 2 2 2 0 +0 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 2 2 2 2 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/jsuarez/map/profmap.png b/jsuarez/map/profmap.png new file mode 100644 index 00000000..08efe3e5 Binary files /dev/null and b/jsuarez/map/profmap.png differ diff --git a/jsuarez/map/profmap.txt b/jsuarez/map/profmap.txt new file mode 100644 index 00000000..8bcd7273 --- /dev/null +++ b/jsuarez/map/profmap.txt @@ -0,0 +1,64 @@ +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 4 6 6 6 6 6 6 6 6 6 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 4 1 4 1 1 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 1 1 4 4 6 6 6 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 1 1 1 1 1 4 6 6 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 6 4 1 1 1 1 1 1 4 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 6 4 1 1 1 1 1 4 1 4 1 1 1 1 1 6 6 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 6 4 1 1 1 1 1 4 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 4 1 1 1 1 1 1 1 1 1 4 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 4 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 1 1 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/jsuarez/map/profs.png b/jsuarez/map/profs.png new file mode 100644 index 00000000..8249f1a3 Binary files /dev/null and b/jsuarez/map/profs.png differ diff --git a/jsuarez/map/profs.txt b/jsuarez/map/profs.txt new file mode 100644 index 00000000..9d73329f --- /dev/null +++ b/jsuarez/map/profs.txt @@ -0,0 +1,128 @@ +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 3 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 3 3 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 3 3 3 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 1 1 1 1 1 1 1 1 1 3 3 3 3 3 3 1 1 3 3 3 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 1 1 1 1 1 1 1 1 1 3 3 3 1 1 1 1 1 3 3 3 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 1 1 1 1 1 1 1 1 1 3 3 3 1 1 3 3 3 3 3 3 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 6 6 1 1 1 1 1 1 1 1 1 3 3 3 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 1 6 6 1 1 1 1 1 1 1 1 1 1 3 3 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 1 1 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 1 1 1 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 1 1 6 6 6 6 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 1 6 6 1 1 1 1 1 1 6 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 1 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 1 1 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 3 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 1 1 1 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 3 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 3 3 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 1 1 1 1 1 1 1 3 3 3 3 1 1 1 1 1 1 1 6 6 6 6 6 6 6 4 4 6 6 6 6 6 6 6 6 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 1 1 1 1 1 1 1 1 3 3 1 1 1 1 1 1 1 1 6 6 6 6 6 6 1 1 1 1 1 4 4 4 6 6 6 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 4 4 4 1 1 1 1 1 1 1 1 4 6 6 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 2 2 2 1 1 1 1 1 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 4 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 2 2 2 1 1 2 1 1 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 1 1 1 1 1 1 4 4 1 1 1 1 1 1 1 1 4 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 4 1 1 1 1 1 1 1 1 1 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 4 6 1 1 1 1 1 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 2 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 4 6 4 1 1 1 1 4 6 6 1 1 1 1 1 1 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 2 2 2 2 2 1 1 1 2 1 1 2 1 1 1 1 1 2 1 1 1 1 1 6 6 1 1 1 1 1 1 6 6 1 1 1 1 1 4 6 6 6 4 6 4 6 6 6 1 1 1 1 1 1 1 4 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 6 6 1 1 1 1 1 1 1 4 4 6 6 6 6 6 6 4 1 1 1 1 1 1 1 4 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 2 2 2 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 4 1 1 1 1 1 1 1 1 1 6 6 6 6 6 4 1 1 1 1 1 1 1 1 4 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 2 1 1 2 2 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 1 4 6 1 1 1 1 1 4 6 6 6 1 1 1 4 1 1 1 1 4 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 2 1 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 6 6 1 1 1 1 1 4 6 4 1 1 4 1 1 4 6 6 6 1 1 4 1 1 1 1 6 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 2 2 1 1 2 1 6 6 1 1 1 1 1 6 4 1 1 4 4 1 1 4 6 6 6 1 1 1 4 1 1 4 6 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 4 1 1 1 1 4 6 6 1 1 1 1 1 1 6 6 6 6 6 1 1 1 1 1 4 6 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 2 1 1 2 1 1 1 1 2 2 1 2 1 1 1 2 1 1 2 1 1 6 4 1 1 1 1 4 6 6 6 1 1 1 4 6 6 6 6 6 6 1 1 1 1 1 6 6 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 2 1 1 1 2 2 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 1 6 4 1 1 1 1 6 6 6 6 4 4 6 6 6 4 4 6 6 4 1 1 1 1 1 1 4 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 2 2 1 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 1 6 4 1 1 1 1 4 6 6 6 6 6 6 6 4 1 1 4 6 6 4 4 1 1 1 1 1 4 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 2 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 2 2 1 1 1 1 1 1 1 2 1 1 1 6 6 1 1 1 1 4 6 6 6 6 6 6 6 1 1 1 1 6 6 6 6 1 1 1 1 1 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 2 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 1 1 1 1 6 6 6 4 1 1 1 1 1 1 1 4 6 6 6 6 1 1 1 1 1 4 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 2 1 1 2 1 1 1 1 1 1 6 6 6 1 1 1 1 4 4 6 4 1 1 1 1 1 4 1 1 4 6 6 4 1 1 1 1 1 4 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 6 6 4 1 1 1 1 1 1 4 6 6 1 1 1 4 1 1 1 1 1 4 4 1 1 1 1 1 4 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 2 1 1 2 1 1 1 1 1 2 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 4 1 1 1 1 1 1 1 4 6 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 4 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 2 2 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 6 6 6 4 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 2 1 1 2 2 1 1 6 6 6 6 1 1 1 1 1 1 1 1 1 1 6 4 4 4 1 1 1 1 1 1 1 1 4 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 6 6 6 6 4 4 1 1 1 1 1 1 1 4 6 6 6 6 4 1 1 1 1 1 1 4 6 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 2 1 1 1 1 2 2 1 1 2 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 2 2 2 2 1 6 6 6 6 6 6 6 1 1 1 6 4 4 6 6 6 6 6 6 6 4 1 1 4 6 6 6 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 1 1 1 1 2 1 2 2 1 1 6 6 6 6 6 6 6 4 4 4 6 6 6 6 6 6 6 6 6 6 6 4 4 6 6 6 6 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/jsuarez/map/smallmap.png b/jsuarez/map/smallmap.png new file mode 100644 index 00000000..0293601f Binary files /dev/null and b/jsuarez/map/smallmap.png differ diff --git a/jsuarez/map/smallmap.txt b/jsuarez/map/smallmap.txt new file mode 100644 index 00000000..e5901057 --- /dev/null +++ b/jsuarez/map/smallmap.txt @@ -0,0 +1,16 @@ +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 1 2 2 2 2 2 2 2 2 2 2 2 2 1 0 +0 1 2 2 2 2 2 2 2 2 2 2 2 2 1 0 +0 1 2 2 2 2 2 1 1 2 2 2 2 2 1 0 +0 1 2 2 2 2 1 1 1 1 2 2 2 2 1 0 +0 1 2 2 2 1 1 3 3 1 1 2 2 2 1 0 +0 1 2 2 1 1 3 3 3 3 1 1 2 2 1 0 +0 1 2 2 1 1 3 3 3 3 1 1 2 2 1 0 +0 1 2 2 2 1 1 3 3 1 1 2 2 2 1 0 +0 1 2 2 2 2 1 1 1 1 2 2 2 2 1 0 +0 1 2 2 2 2 2 1 1 2 2 2 2 2 1 0 +0 1 2 2 2 2 2 2 2 2 2 2 2 2 1 0 +0 1 2 2 2 2 2 2 2 2 2 2 2 2 1 0 +0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 +0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/jsuarez/map/testmap.txt b/jsuarez/map/testmap.txt new file mode 100644 index 00000000..85f5dfca Binary files /dev/null and b/jsuarez/map/testmap.txt differ diff --git a/jsuarez/quote.txt b/jsuarez/quote.txt new file mode 100644 index 00000000..e69de29b diff --git a/jsuarez/reset.sh b/jsuarez/reset.sh new file mode 100644 index 00000000..1ac398b1 --- /dev/null +++ b/jsuarez/reset.sh @@ -0,0 +1 @@ +rm resource/logs/events* diff --git a/jsuarez/todo.txt b/jsuarez/todo.txt new file mode 100644 index 00000000..e69de29b diff --git a/jsuarez/tools/ColorEnts.py b/jsuarez/tools/ColorEnts.py new file mode 100644 index 00000000..8f2e7209 --- /dev/null +++ b/jsuarez/tools/ColorEnts.py @@ -0,0 +1,29 @@ +from pdb import set_trace as T +import numpy as np +from sim.lib.Enums import Neon +from tools.Colors import hex2rgb +from scipy.misc import imread, imsave +from matplotlib import pyplot as plt + +fDir = 'resource/Entity/' +fName = 'neural' +extension = '.png' +neural = imread(fDir + fName + extension) + +inds = [(5, 13), (5, 14), (6, 13), (6, 14), + (9, 9), (9, 10), (10, 9), (10, 10), + (13, 13), (13, 14), (14, 13), (14, 14)] +#px = [neural[r, c] for r, c in inds] + +for color in Neon: + if color not in (Neon.RED, Neon.ORANGE, Neon.YELLOW, Neon.GREEN, + Neon.MINT, Neon.CYAN, Neon.BLUE, Neon.PURPLE, Neon.MAGENTA, + Neon.FUCHSIA, Neon.SPRING, Neon.SKY): + continue + name, val = color.name, hex2rgb(color.value) + + for r, c in inds: + neural[r, c, :] = val + + f = fDir + fName + name + extension + imsave(f, neural) diff --git a/jsuarez/tools/ColorTransform.py b/jsuarez/tools/ColorTransform.py new file mode 100644 index 00000000..0cf8932b --- /dev/null +++ b/jsuarez/tools/ColorTransform.py @@ -0,0 +1,33 @@ +from pdb import set_trace as T +import numpy as np +from scipy.misc import imread, imsave +from sim.lib.Enums import Neon +from skimage.color import rgb2lab, deltaE_cie76 + +fdir = 'resource/Material/' +fname = 'textures.png' +tex = imread(fdir+fname) +colors = ( + Neon.RED, Neon.ORANGE, Neon.YELLOW, + Neon.GREEN, Neon.MINT, Neon.CYAN, + Neon.BLUE, Neon.PURPLE, Neon.MAGENTA, + Neon.FUCHSIA, Neon.SPRING, Neon.SKY, + Neon.BLOOD, Neon.BROWN, Neon.GOLD, Neon.SILVER) +colors = np.stack([Neon.rgb(c) for c in colors]) +sz = tex.shape[0] +alpha = tex[:, :, 3] +tex = tex[:, :, :3] + +tex = tex.reshape(-1, 1, 3) +tex = rgb2lab(tex/255) + +clrs = colors.reshape(1, -1, 3) +clrs = rgb2lab(clrs/255) + +dists = deltaE_cie76(tex, clrs) +#dists = np.sum((tex - clrs)**2, 2) +inds = np.argmin(dists, 1) +px = np.array([colors[i] for i in inds]) +px = px.reshape(sz, sz, 3) +imsave('tex.png', px) + diff --git a/jsuarez/tools/Colors.py b/jsuarez/tools/Colors.py new file mode 100644 index 00000000..829566ff --- /dev/null +++ b/jsuarez/tools/Colors.py @@ -0,0 +1,54 @@ +from pdb import set_trace as T +from matplotlib import pyplot as plt +import numpy as np + +def hex2rgb(h): + h = h.lstrip('#') + return tuple(int(h[i:i+2], 16) for i in (0, 2, 4)) + +class Neon: + RED = '#ff0000' + ORANGE = '#ff8000' + YELLOW = '#ffff00' + + GREEN = '#00ff00' + MINT = '#00ff80' + CYAN = '#00ffff' + + BLUE = '#0000ff' + PURPLE = '#8000ff' + MAGENTA = '#ff00ff' + + WHITE = '#ffffff' + GRAY = '#666666' + BLACK = '#000000' + + BLOOD = '#bb0000' + BROWN = '#7a3402' + GOLD = '#eec600' + SILVER = '#b8b8b8' + + FUCHSIA = '#ff0080' + SPRING = '#80ff80' + SKY = '#0080ff' + TERM = '#41ff00' + +if __name__ == '__main__': + colors = np.array([ + [Neon.RED, Neon.ORANGE, Neon.YELLOW, Neon.BLOOD, Neon.FUCHSIA], + [Neon.GREEN, Neon.MINT, Neon.CYAN, Neon.BROWN, Neon.SPRING], + [Neon.BLUE, Neon.PURPLE, Neon.MAGENTA, Neon.GOLD, Neon.SKY], + [Neon.BLACK, Neon.GRAY, Neon.WHITE, Neon.SILVER, Neon.TERM]]) + + + sz = 64 + R, C = colors.shape + img = np.zeros((R*sz, C*sz, 3), dtype=np.uint8) + for r in range(R): + for c in range(C): + color = hex2rgb(colors[r][c]) + tile = np.zeros((sz, sz, 3)) + color + img[r*sz:(r+1)*sz, c*sz:(c+1)*sz, :] = tile + + plt.imshow(img) + plt.show() diff --git a/jsuarez/tools/GPUTest.py b/jsuarez/tools/GPUTest.py new file mode 100644 index 00000000..7d38f728 --- /dev/null +++ b/jsuarez/tools/GPUTest.py @@ -0,0 +1,102 @@ +from pdb import set_trace as T +import torch +from torch import nn, optim +from torch.nn import functional as F +from torch.nn.parameter import Parameter +from torch.autograd import Variable +from torch.distributions import Categorical + +import numpy as np +import time + +#Same padded (odd k) +def Conv2d(fIn, fOut, k, stride=1): + pad = int((k-1)/2) + return torch.nn.Conv2d(fIn, fOut, k, stride=stride, padding=pad) + +class StimNet(nn.Module): + def __init__(self, xdim, h, ydim): + super().__init__() + self.conv1 = Conv2d(8, int(h/2), 3, stride=2) + self.conv2 = Conv2d(int(h/2), h, 3, stride=2) + self.fc1 = torch.nn.Linear(5+4*4*h, h) + self.fc2 = torch.nn.Linear(h, ydim) + + def forward(self, conv, flat): + if len(conv.shape) == 3: + conv = conv.view(1, *conv.shape) + flat = flat.view(1, *flat.shape) + x, batch = conv, conv.shape[0] + x = torch.nn.functional.relu(self.conv1(x)) + x = torch.nn.functional.relu(self.conv2(x)) + x = x.view(batch, -1) + + x = torch.cat((x, flat), dim=1) + x = torch.nn.functional.relu(self.fc1(x)) + x = self.fc2(x) + + pi = x.view(batch, -1) + + return pi + +def classify(logits): + #logits = logits + 0.15*torch.norm(logits) + distribution = Categorical(F.softmax(logits, dim=1)) + atn = distribution.sample() + return atn + +class ANN(nn.Module): + def __init__(self, xdim, h, ydim): + super().__init__() + self.stimNet = StimNet(xdim, 24, ydim) + self.valNet = StimNet(xdim, 24, 1) + #self.curNet = CurNet(xdim, 24, ydim) + self.conv, self.flat, self.ent, self.stim, self.idx = [], [], [], [], [] + + def recv(self, conv, flat, ent, stim, idx): + self.conv.append(conv) + self.flat.append(flat) + self.ent.append(ent) + self.stim.append(stim) + self.idx.append(idx) + + def send(self): + conv = torch.stack(self.conv, dim=0) + flat = torch.stack(self.flat, dim=0) + + pi, val, atn = [], [], [] + #for c, f in zip(conv, flat): + # p, v, a = self.forward(c, f) + # pi.append(p) + # val.append(v) + # atn.append(a) + pi, val, atn = self.forward(conv, flat) + + pi = [e.view(1, -1) for e in pi] + val = [e.view(1, -1) for e in val] + atn = [e.view(1) for e in atn] + + ret = list(zip(pi, val, self.ent, self.stim, atn, self.idx)) + self.conv, self.flat, self.ent, self.stim, self.idx = [], [], [], [], [] + return ret + + def forward(self, conv, flat): + pi = self.stimNet(conv, flat) + val = self.valNet(conv, flat) + atn = classify(pi) + #ri, li = self.curNet(ents, entID, atn, conv, flat) + + return pi, val, atn + +if __name__ == '__main__': + ann = ANN(1850, 32, 6)#.cuda() + batch = 100 + + conv = torch.rand(batch, 8, 15, 15)#.cuda() + flat = torch.rand(batch, 5)#.cuda() + + while True: + start = time.time() + _ = ann(conv, flat) + print(1.0 / (time.time() - start)) + diff --git a/jsuarez/tools/MapMaker.py b/jsuarez/tools/MapMaker.py new file mode 100644 index 00000000..3ff35d98 --- /dev/null +++ b/jsuarez/tools/MapMaker.py @@ -0,0 +1,225 @@ +from pdb import set_trace as T + +from scipy.misc import imread +from scipy.misc import imsave +from sim.lib import Enums +from sim.lib.Enums import Material +import time +import sys +import numpy as np +import pygame + +def readRGB(path): + return imread(path)[:, :, :3] + +def pgRead(path, alpha=False, rot=90): + try: + img = readRGB(path) + except FileNotFoundError: + return None + img = pygame.pixelcopy.make_surface(img) + if alpha: + img.set_colorkey((255, 255, 255)) + return pygame.transform.rotate(img, rot) + +class TextureInitializer(): + def __init__(self, sz, root='resource/', scale=1): + self.width = sz + self.statHeight = 2 + self.scale = scale + + self.material = readRGB(root+'Material/textures.png') + self.material = self.textureTiles() + self.entity = self.textureFromFile(Enums.Entity, root+'Entity/') + + def textureTiles(self): + reverse = {} + for mat in Material: + mat = mat.value + texCoords = mat.tex + tex = self.getTile(*texCoords) + tex = pygame.pixelcopy.make_surface(tex) + mat.tex = pygame.transform.rotate(tex, 90) + mat.tex = pygame.transform.scale(mat.tex, (int(self.width*self.scale), + int(self.width*self.scale))) + reverse[mat.index] = mat.tex + return reverse + + def getTile(self, r, c): + w = self.width + tile = self.material[r*w:r*w+w, c*w:c*w+w, :] + return tile + + def textureFromFile(self, enum, path, alpha=True, rot=270): + reverse = {} + for e in enum: + texPath = path + e.name.lower() + '.png' + tex = pgRead(texPath, alpha=alpha, rot=rot) + e.value.tex = tex + if type(e.value.data) == tuple: + reverse[e.value.data[0]] = tex + else: + reverse[e.value.data] = tex + return reverse + +class MapMaker: + def __init__(self, w, h, load, res=4): + self.W, self.H = 2*512, 2*512 + self.deltaX, self.deltaY = 0, 0 + self.fDir = 'resource/map/' + self.fName = 'profs' + self.env = np.ones((h, w), dtype=int) + self.textures = TextureInitializer(16 ) + self.client = Client(res) + self.resInd = res + self.setupScreen(self.W, self.H) + self.loadIf(load) + self.start = time.time() + + def save(self): + np.savetxt(self.fDir + self.fName + '.txt', self.env, fmt='%d') + pygame.image.save(self.screen, self.fDir + self.fName + '.png') + + def loadIf(self, load): + if load: + self.env = np.genfromtxt(self.fDir + self.fName + '.txt') + #self.env = imread(self.fDir + self.fName + '.png') + self.redraw() + + def redraw(self): + self.screen.fill((0, 0, 0)) + R, C = int(self.H//2**self.resInd), int(self.W//2**self.resInd) + RR, CC = self.env.shape + for r in range(R): + for c in range(C): + rr, cc = r+self.deltaY, c+self.deltaX + if rr < 0 or cc < 0 or rr >= RR or cc >= CC: + continue + tex = self.env[rr, cc] + self.renderTile(r, c, self.textures.material[tex]) + + def setupScreen(self, W, H): + pygame.init() + self.screen = pygame.display.set_mode((W, H)) + + def getTile(self, rPx, cPx): + return int(rPx//2**self.resInd), int(cPx//2**self.resInd) + + def renderTile(self, r, c, tex): + w = 2**self.resInd + tex = pygame.transform.scale(tex, (w, w)) + self.screen.blit(tex, (c*w, r*w)) + + def render(self): + #Draw + if self.client.clicked: + x, y = pygame.mouse.get_pos() + c, r = self.getTile(x, y) + rr, cc = r+self.deltaY, c+self.deltaX + tex = self.textures.material[self.client.tex] + R, C = self.env.shape + if rr >= 0 and cc >= 0 and rr 5.0: + self.start = time.time() + self.save() + +class Client: + def __init__(self, resInd): + self.volX, self.volY = 0, 0 + self.volDeltaX, self.volDeltaY = 0, 0 + self.deltaX, self.deltaY = 0, 0 + self.clicked = False + self.rightClicked = False + self.tex = 1 + self.resInd = resInd + self.delta = 10 + + def update(self): + self.processEvents(pygame.event.get()) + + def quit(self): + pygame.quit() + sys.exit() + + def mouseDown(self, button): + if button == 1 and not self.clicked: + self.volX, self.volY = pygame.mouse.get_pos() + self.clicked = True + if button == 3 and not self.rightClicked: + self.volX, self.volY = pygame.mouse.get_pos() + self.rightClicked = True + + def mouseUp(self, button): + if button == 1: + if self.clicked: + self.deltaX += self.volDeltaX + self.deltaY += self.volDeltaY + self.volDeltaX, self.volDeltaY = 0, 0 + self.clicked = False + elif button == 3: + self.rightClicked = False + elif button == 4: + if self.resInd < 6: + self.resInd += 1 + elif button == 5: + if self.resInd > 3: + self.resInd -= 1 + print(self.resInd) + + def keyUp(self, button): + if button == pygame.K_a: + self.deltaX -= self.delta + elif button == pygame.K_d: + self.deltaX += self.delta + elif button == pygame.K_s: + self.deltaY += self.delta + elif button == pygame.K_w: + self.deltaY -= self.delta + elif button == pygame.K_e: + if self.tex < 6: + self.tex += 1 + elif button == pygame.K_q: + if self.tex > 0: + self.tex -= 1 + + def processEvents(self, events): + for e in events: + if e.type == pygame.QUIT: + self.quit() + elif e.type == pygame.MOUSEBUTTONDOWN: + self.mouseDown(e.button) + elif e.type == pygame.MOUSEBUTTONUP: + self.mouseUp(e.button) + elif e.type == pygame.KEYUP: + print('KeyUp') + self.keyUp(e.key) + +if __name__ == '__main__': + w, h, load = int(sys.argv[1]), int(sys.argv[2]), True + if len(sys.argv) > 3 and sys.argv[3] == '--noload': + load=False + mapMaker = MapMaker(w, h, load) + while True: + mapMaker.update() diff --git a/jsuarez/tools/RayTest.py b/jsuarez/tools/RayTest.py new file mode 100644 index 00000000..d97b8eea --- /dev/null +++ b/jsuarez/tools/RayTest.py @@ -0,0 +1,57 @@ +from pdb import set_trace as T +import numpy as np +import ray +import time + +ray.init() + +def timeit(f): + def profile(*args, iters=1000): + sumTime = 0 + for i in range(iters): + start = time.time() + f(*args) + sumTime += time.time() - start + print(sumTime/iters) + return profile + +@timeit +def rayShared(x): + #xId = ray.put(x) + return ray.get(x) + +@ray.remote +class Foo: + def __init__(self): + pass + + def bar(self): + return 1 + +''' +foo = Foo.remote() +start = time.time() +for i in range(1000): + a = foo.bar.remote() +print(time.time() - start) +''' + + +times = [] +x = np.random.rand(15000, 15000) +x = ray.put(x) +#noise = [str(np.random.rand())*int(sz)] +rayShared(x, iters=1000) + +''' +@ray.remote +class Foo(object): + def method(self): + return 1 + +a = Foo.remote() +start = time.time() +for i in range(1000): + ray.get(a.method.remote()) # This also takes about 440us for me (on my laptop) +print(time.time()-start) +''' diff --git a/jsuarez/tools/RenderTest.py b/jsuarez/tools/RenderTest.py new file mode 100644 index 00000000..de941b11 --- /dev/null +++ b/jsuarez/tools/RenderTest.py @@ -0,0 +1,189 @@ +from pdb import set_trace as T +import pygame +from pygame.surface import Surface +import numpy as np +import time +from enum import Enum + +class Neon(Enum): + def rgb(h): + h = h.lstrip('#') + return tuple(int(h[i:i+2], 16) for i in (0, 2, 4)) + + RED = rgb('#ff0000') + ORANGE = rgb('#ff8000') + YELLOW = rgb('#ffff00') + + GREEN = rgb('#00ff00') + MINT = rgb('#00ff80') + CYAN = rgb('#00ffff') + + BLUE = rgb('#0000ff') + PURPLE = rgb('#8000ff') + MAGENTA = rgb('#ff00ff') + + WHITE = rgb('#ffffff') + GRAY = rgb('#666666') + BLACK = rgb('#000000') + + BLOOD = rgb('#bb0000') + BROWN = rgb('#7a3402') + GOLD = rgb('#eec600') #238 198 + SILVER = rgb('#b8b8b8') + + FUCHSIA = rgb('#ff0080') + SPRING = rgb('#80ff80') + SKY = rgb('#0080ff') + TERM = rgb('#41ff00') + + def rand12(): + ind = np.random.randint(0, 12) + return ( + Neon.RED, Neon.ORANGE, Neon.YELLOW, + Neon.GREEN, Neon.MINT, Neon.CYAN, + Neon.BLUE, Neon.PURPLE, Neon.MAGENTA, + Neon.FUCHSIA, Neon.SPRING, Neon.SKY)[ind].value + +class Container: + def __init__(self, size, border=0, reset=False, key=None): + self.W, self.H = size + self.canvas = Surface((self.W, self.H)) + if key is not None: + self.canvas.set_colorkey(key) + + self.border = border + self.left, self.right = self.border, self.W-self.border + self.top, self.bottom = self.border, self.H-self.border + + def renderBorder(self): + for coords in [ + (0, 0, self.W, self.top), + (0, 0, self.left, self.H), + (0, self.bottom, self.W, self.H), + (self.right, 0, self.W, self.H) + ]: + pygame.draw.rect(self.canvas, Color.RED, coords) + + def reset(self): + self.canvas.fill((Color.BLACK)) + if self.border > 0: + self.renderBorder() + + def fill(self, color): + self.canvas.fill((color)) + + def blit(self, container, pos, area=None, flags=0): + w, h = pos + pos = (w+self.border, h+self.border) + if type(container) == Surface: + self.canvas.blit(container, pos, area=area, special_flags=flags) + else: + self.canvas.blit(container.canvas, pos, area=area, special_flags=flags) + + if self.border > 0: + self.renderBorder() + + def rect(self, color, coords, lw=0): + pygame.draw.rect(self.canvas, color, coords, lw) + + def line(self, color, start, end, lw=1): + pygame.draw.line(self.canvas, color, start, end, lw) + +def fromTiled(tiles, tileSz): + R, C, three = tiles.shape + ret = np.zeros((R, C), dtype=object) + for r in range(R): + for c in range(C): + ret[r, c] = Surface((tileSz, tileSz)) + ret[r, c].fill(tiles[r, c, :]) + return ret + +def makeMap(tiles, tileSz): + R, C = tiles.shape + surf = Surface((int(R*tileSz), int(C*tileSz))) + for r in range(R): + for c in range(C): + surf.blit(tiles[r, c], (int(r*tileSz), int(c*tileSz))) + return surf + +def surfToIso(surf): + ret = pygame.transform.rotate(surf, 45) + W, H = ret.get_width(), ret.get_height() + ret = pygame.transform.scale(ret, (W, H//2)) + return ret + +def rotate(x, y, theta): + pt = np.array([[x], [y]]) + mat = np.array([ + [np.cos(theta), -np.sin(theta)], + [np.sin(theta), np.cos(theta)]]) + ret = np.dot(mat, pt) + return ret.ravel() + +def degrees(rad): + return 180 * rad / 3.14159265 + +def rad(degrees): + return 3.14159265 * degrees / 180.0 + +def pointToIso(point, tW, tH, W, H): + x, y = point + x, y = x-W//2, y+H//2 + x, y = rotate(x, y, rad(-45)) + #x, y = x+(W//2*np.sqrt(2)), y-(H//2*np.sqrt(2)) + y = y / 2 + return x, y + +def makeMap(tiles, tileSz): + R, C = tiles.shape + surf = Surface((int(R*tileSz), int(C*tileSz))) + for r in range(R): + for c in range(C): + #tile = surfToIso(tiles[r, c]) + tile = tiles[r, c] + rr, cc = int(r*tileSz), int(c*tileSz) + #cc, rr = pointToIso((cc, rr), tileSz) + surf.blit(tile, (rr, cc)) + return surf + +class Application: + def __init__(self): + self.W, self.H, self.side = 512, 512, 256 + self.appSize = (self.W+self.side, self.H+self.side) + + pygame.init() + pygame.display.set_caption('Projekt: Godsword') + self.canvas = pygame.display.set_mode(self.appSize, pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE) + #self.fonts = RenderUtils.Fonts('resource/dragonslapper.ttf') + ''' + + self.buf = Container(self.appSize) + + sz = 16 + tiles = np.array([[Neon.rand12() for i in range(sz)] for j in range(sz)]) + tileSz = 32 + tiles = fromTiled(tiles, tileSz) + surf = makeMap(tiles, tileSz) + #self.canvas.blit(surf, (0, 0)) + surf = surfToIso(surf) + p1, p2 = (0, 0), (tileSz, tileSz) + p1 = pointToIso(p1, tileSz, tileSz, self.W, self.H) + p2 = pointToIso(p2, tileSz, tileSz, self.W, self.H) + #rect = (*p1, *p2) + self.canvas.blit(surf, (0, 0)) + pygame.draw.line(self.canvas, (255, 255, 255), p1, p2) + + ''' + while True: + self.render() + + def render(self): + start = time.time() + #self.canvas.blit(self.buf.canvas, (0,0)) + pygame.event.get() + #pygame.draw.rect(self.canvas, (255,0,0), (0, 0, 50, 50)) + pygame.display.flip() + #pygame.display.update((0, 0, 50, 50)) + print(1/(time.time()-start)) + +app = Application() diff --git a/jsuarez/tools/SeedTest.py b/jsuarez/tools/SeedTest.py new file mode 100644 index 00000000..77da5c4f --- /dev/null +++ b/jsuarez/tools/SeedTest.py @@ -0,0 +1,19 @@ +from pdb import set_trace as T +import numpy as np +import ray +import torch +from torch import nn + + +@ray.remote +def foo(i): + np.random.seed(i) + torch.manual_seed(i) + fc = nn.Sequential(nn.Linear(2, 2)) + print(i, ': ', [e for e in fc.parameters()][0]) + +ray.init() +jobs = [foo.remote(i) for i in range(5)] +print(ray.get(jobs)) + + diff --git a/jsuarez/tools/TensorboardExample.py b/jsuarez/tools/TensorboardExample.py new file mode 100644 index 00000000..cf6f5c38 --- /dev/null +++ b/jsuarez/tools/TensorboardExample.py @@ -0,0 +1,31 @@ +from pdb import set_trace as T +import tensorflow as tf +import numpy as np +import time + + +class Logger: + def __init__(self): + self.writer = tf.summary.FileWriter("./logdir") + + def log(self, val, tag): + summary = tf.Summary(value=[tf.Summary.Value( + tag=tag, simple_value=val)]) + self.writer.add_summary(summary) + +i = 0 +logger = Logger() +while True: + i += 1 + s = np.sin(i/100) + logger.log(s, 'sin') + logger.log(np.log(i), 'log') + + if i % 30 == 0: + print(s) + + time.sleep(0.01) + +# while the above is running, execute +# tensorboard --logdir ./logdir + diff --git a/jsuarez/tools/TestDropbox.py b/jsuarez/tools/TestDropbox.py new file mode 100644 index 00000000..dfc981c7 --- /dev/null +++ b/jsuarez/tools/TestDropbox.py @@ -0,0 +1,50 @@ +from sim.lib import MPIUtils + +def packets(): + return [i for i in range(10000)] + +class Client: + def __init__(self, server): + self.server = server + + def step(self): + MPIUtils.print(MPIUtils.ALL, 'Client Recv') + packets = MPIUtils.recv(self.server, usePar=True) + MPIUtils.print(MPIUtils.ALL, 'Client Send') + MPIUtils.isend(packets, self.server, tag=MPIUtils.core()) + +class Server: + def __init__(self, clients): + self.clients = clients + + def map(self): + MPIUtils.print(MPIUtils.ALL, 'Server Send') + for worker in self.clients: + MPIUtils.print(MPIUtils.ALL, 'Server Send') + for pack in packets() + MPIUtils.isend(pack, worker, tag=worker) + + def reduce(self): + reqs = [] + for client in self.clients: + MPIUtils.print(MPIUtils.ALL, 'Server Recv') + reqs.append(MPIUtils.irecv(client, tag=client)) + for req in reqs: + req.wait() + + +def test(): + if MPIUtils.core() == MPIUtils.MASTER: + server = Server([1]) + else: + client = Client(0) + while True: + if MPIUtils.core() == MPIUtils.MASTER: + server.map() + elif MPIUtils.core() == 1: + client.step() + if MPIUtils.core() == MPIUtils.MASTER: + server.reduce() + +if __name__ == '__main__': + test() diff --git a/jsuarez/tools/TestGA.py b/jsuarez/tools/TestGA.py new file mode 100644 index 00000000..bdb21d9c --- /dev/null +++ b/jsuarez/tools/TestGA.py @@ -0,0 +1,68 @@ +import torch +from torch import nn +from torch.autograd import Variable + +from copy import deepcopy +import numpy as np +from pdb import set_trace as T + +def var(xNp, volatile=False, cuda=False): + x = Variable(torch.from_numpy(xNp), volatile=volatile).float() + if cuda: + x = x.cuda() + return x + +class StimNet(nn.Module): + def __init__(self, xdim, h, ydim): + super().__init__() + self.fc1 = torch.nn.Linear(xdim, h) + self.fc2 = torch.nn.Linear(h, ydim) + + def forward(self, x): + a = self.fc1(x) + a = torch.nn.functional.relu(a) + a = self.fc2(a) + return a + +def randomMutation(ann, sigma): + annNew = deepcopy(ann) + for e in annNew.parameters(): + e.data = e.data + torch.Tensor(sigma*np.random.randn(*e.size())) + return annNew + +def GA(fitness, generations, n, t, sigma, dims): + P, F = [], [] + for g in range(generations): + Pn, Fn = [], [] + for i in range(n): + if g == 0: + P.append(StimNet(*dims)) + F.append(fitness(P[-1])) + elif i == 0: + Pn.append(P[0]) + Fn.append(F[0]) + else: + k = np.random.randint(0, t) + Pn.append(randomMutation(P[k], sigma)) + Fn.append(fitness(Pn[-1])) + #Sort dec by F + if g > 0: + inds = np.argsort(Fn)[::-1] + F = np.asarray(Fn)[inds].tolist() + P = np.asarray(Pn)[inds].tolist() + print(F[0]) + +if __name__ == '__main__': + generations = 100 + n = 1000 + t = 10 + sigma = 0.01 + dims = (847, 16, 6) + + def fitness(ann): + inp = var(np.random.randn(dims[0])) + out = ann(inp) + loss = -torch.sum((1 - out)**2) + return loss.data[0] + + ret = GA(fitness, generations, n, t, sigma, dims) diff --git a/jsuarez/tools/Tests.py b/jsuarez/tools/Tests.py new file mode 100644 index 00000000..8bbb198c --- /dev/null +++ b/jsuarez/tools/Tests.py @@ -0,0 +1,26 @@ +from pdb import set_trace as T +from sim import Exchange +from sim.item import RawFish + +def testExchange(): + market = Exchange.Exchange() + sardine = RawFish.Sardine + + def checkPrint(): + print(market.buyOffers[sardine].queue) + print(market.sellOffers[sardine].queue) + print() + + market.buy(sardine, 1, 90) + checkPrint() + market.sell(sardine, 2, 100) + checkPrint() + market.buy(sardine, 3, 130) + checkPrint() + market.sell(sardine, 5, 90) + checkPrint() + market.buy(sardine, 5, 100) + checkPrint() + +if __name__ == '__main__': + testExchange() diff --git a/jsuarez/tools/VideoLog.py b/jsuarez/tools/VideoLog.py new file mode 100644 index 00000000..55b7813f --- /dev/null +++ b/jsuarez/tools/VideoLog.py @@ -0,0 +1,45 @@ +from pdb import set_trace as T +import cv2 +import numpy as np +from scipy.misc import imresize + +framedir = 'resource/data/' +prefixes = 'sl ml ll sr mr lr'.split() +suffixes = [str(i) for i in range(250)] +sz = 1024 +fNames, frames = [], [] +for suf in suffixes: + print(suf) + fName = framedir + 'slframe' + suf + '.png' + sl = cv2.imread(fName, cv2.IMREAD_COLOR)[:256, :256, :] + sl = imresize(sl, (sz, sz)) + + fName = framedir + 'srframe' + suf + '.png' + sr = cv2.imread(fName, cv2.IMREAD_COLOR)[:256, :256, :] + sr = imresize(sr, (sz, sz)) + + fName = framedir + 'mlframe' + suf + '.png' + ml = cv2.imread(fName, cv2.IMREAD_COLOR)[:512, :512, :] + ml = imresize(ml, (sz, sz)) + + fName = framedir + 'mrframe' + suf + '.png' + mr = cv2.imread(fName, cv2.IMREAD_COLOR)[:512, :512, :] + mr = imresize(mr, (sz, sz)) + + fName = framedir + 'llframe' + suf + '.png' + ll = cv2.imread(fName, cv2.IMREAD_COLOR)[:1024, :1024, :] + ll = imresize(ll, (sz, sz)) + + fName = framedir + 'lrframe' + suf + '.png' + lr = cv2.imread(fName, cv2.IMREAD_COLOR)[:1024, :1024, :] + lr = imresize(lr, (sz, sz)) + + l = np.concatenate((sl, ml, ll), axis=1) + r = np.concatenate((sr, mr, lr), axis=1) + frame = np.concatenate((l, r), axis=0) + frame = np.stack((frame[:,:,2], frame[:,:,1], frame[:,:,0]), axis=2) + frames.append(frame) + +import imageio +imageio.mimwrite('godsword.mp4', frames, fps = 6) + diff --git a/jsuarez/tools/codesize.sh b/jsuarez/tools/codesize.sh new file mode 100644 index 00000000..33d16260 --- /dev/null +++ b/jsuarez/tools/codesize.sh @@ -0,0 +1 @@ +find . -name "*.py" -type f -exec grep . {} \; | wc -l diff --git a/jsuarez/tools/gl.py b/jsuarez/tools/gl.py new file mode 100644 index 00000000..42a571a6 --- /dev/null +++ b/jsuarez/tools/gl.py @@ -0,0 +1,161 @@ +# author: Somsubhra Bairi (201101056) + +# Draws a polyhedron by cutting corners of cube +# Controls: UP - rotate up +# DOWN - rotate down +# LEFT - rotate left +# RIGHT - rotate right + +# OpenGL imports for python +try: + from OpenGL.GL import * + from OpenGL.GLU import * + from OpenGL.GLUT import * +except: + print("OpenGL wrapper for python not found") + + +# The cube class +class Cube: + + # Constructor for the cube class + def __init__(self): + self.rotate_y = 0.0 + self.rotate_x = 0.0 + self.scale = 2.0 + + # Initialize + def init(self): + # Set background to black + glClearColor(0.0, 0.0, 0.0, 0.0) + + # Set the shade model to flat + glShadeModel(GL_FLAT) + + # Draw half of the cube with corners cut + def draw_half(self, mirror): + + # The plane equations cutting corners of cube + eqn = [-1.0, 0.0, 0.0, 0.0] + eqn1 = [1.0, 1.0, 1.0, 1.25] + eqn2 = [1.0, -1.0, 1.0, 1.25] + eqn3 = [1.0, 1.0, -1.0, 1.25] + eqn4 = [1.0, -1.0, -1.0, 1.25] + eqn5 = [-1.0, 1.0, 1.0, 1.25] + + # Set the color to white + glColor3f(1.0, 1.0, 1.0) + + # Reset the matrix + glLoadIdentity() + + # Set the camera + gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0) + + if mirror: + glScalef(-self.scale, self.scale, self.scale) + glRotatef(-self.rotate_y, 0.0, 1.0, 0.0) + else: + glScalef(self.scale, self.scale, self.scale) + glRotatef(self.rotate_y, 0.0, 1.0, 0.0) + + glRotatef(self.rotate_x, 1.0, 0.0, 0.0) + + # Draw solid cube + glutSolidCube(1.0) + + # Draw a red wire cube to highlight the background + glColor3f(1.0, 0.0, 0.0) + glutWireCube(1.0) + + # Clip the corners of the cube with these equations + glClipPlane(GL_CLIP_PLANE0, eqn1) + glEnable(GL_CLIP_PLANE0) + + glClipPlane(GL_CLIP_PLANE1, eqn2) + glEnable(GL_CLIP_PLANE1) + + glClipPlane(GL_CLIP_PLANE2, eqn3) + glEnable(GL_CLIP_PLANE2) + + glClipPlane(GL_CLIP_PLANE3, eqn4) + glEnable(GL_CLIP_PLANE3) + + glClipPlane(GL_CLIP_PLANE4, eqn5) + glEnable(GL_CLIP_PLANE4) + + # Cut the cube into half + glClipPlane(GL_CLIP_PLANE5, eqn) + glEnable(GL_CLIP_PLANE5) + + glFlush() + + # The display function + def display(self): + glClear(GL_COLOR_BUFFER_BIT) + + # Draw half the cube with corners cut + self.draw_half(False) + + # Draw a mirror image of the above half cube + self.draw_half(True) + + # The reshape function + def reshape(self, w, h): + glViewport(0, 0, GLsizei(w), GLsizei(h)) + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + glFrustum(-1.0, 1.0, -1.0, 1.0, 1.5, 20.0) + glMatrixMode(GL_MODELVIEW) + + # The keyboard controls + def special(self, key, x, y): + + # Rotate cube according to keys pressed + if key == GLUT_KEY_RIGHT: + self.rotate_y += 5 + if key == GLUT_KEY_LEFT: + self.rotate_y -= 5 + if key == GLUT_KEY_UP: + self.rotate_x += 5 + if key == GLUT_KEY_DOWN: + self.rotate_x -= 5 + glutPostRedisplay() + + +# The main function +def main(): + + # Initialize OpenGL + glutInit(sys.argv) + + # Set display mode + glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB) + + # Set size and position of window size + glutInitWindowSize(400, 400) + glutInitWindowPosition(100, 100) + + # Create window with given title + glutCreateWindow("Cube") + + # Instantiate the cube + cube = Cube() + + cube.init() + + # The callback for display function + glutDisplayFunc(cube.display) + + # The callback for reshape function + glutReshapeFunc(cube.reshape) + + # The callback function for keyboard controls + glutSpecialFunc(cube.special) + + # Start the main loop + glutMainLoop() + +# Call the main function +if __name__ == '__main__': + main() diff --git a/jsuarez/tools/handletest.py b/jsuarez/tools/handletest.py new file mode 100644 index 00000000..595f5171 --- /dev/null +++ b/jsuarez/tools/handletest.py @@ -0,0 +1,19 @@ +import ray + +ray.init(driver_mode=ray.PYTHON_MODE) + +@ray.remote +class Actor1: + def method(self): + pass + +@ray.remote +class Actor2: + def __init__(self, a): + self.a = a + def method(self): + ray.get(self.a.method.remote()) + +a1 = Actor1.remote() +a2 = Actor2.remote(a1) +ray.get(a2.method.remote()) diff --git a/jsuarez/tools/kiv.py b/jsuarez/tools/kiv.py new file mode 100644 index 00000000..7fffda0e --- /dev/null +++ b/jsuarez/tools/kiv.py @@ -0,0 +1,156 @@ +from pdb import set_trace as T +import numpy as np +import pygame +import kivy as kv +from kivy.app import App +from kivy.core.image import ImageData +from kivy.uix.widget import Widget +from kivy.core.image import Image +from kivy.graphics.texture import Texture +from kivy.graphics import Rectangle, Color +from kivy.config import Config +from kivy.clock import Clock +from enum import Enum + +class Neon(Enum): + def rgb(h): + h = h.lstrip('#') + return tuple(int(h[i:i+2], 16) for i in (0, 2, 4)) + + RED = rgb('#ff0000') + ORANGE = rgb('#ff8000') + YELLOW = rgb('#ffff00') + + GREEN = rgb('#00ff00') + MINT = rgb('#00ff80') + CYAN = rgb('#00ffff') + + BLUE = rgb('#0000ff') + PURPLE = rgb('#8000ff') + MAGENTA = rgb('#ff00ff') + + WHITE = rgb('#ffffff') + GRAY = rgb('#666666') + BLACK = rgb('#000000') + + BLOOD = rgb('#bb0000') + BROWN = rgb('#7a3402') + GOLD = rgb('#eec600') #238 198 + SILVER = rgb('#b8b8b8') + + FUCHSIA = rgb('#ff0080') + SPRING = rgb('#80ff80') + SKY = rgb('#0080ff') + TERM = rgb('#41ff00') + + def rand12(): + ind = np.random.randint(0, 12) + return ( + Neon.RED, Neon.ORANGE, Neon.YELLOW, + Neon.GREEN, Neon.MINT, Neon.CYAN, + Neon.BLUE, Neon.PURPLE, Neon.MAGENTA, + Neon.FUCHSIA, Neon.SPRING, Neon.SKY)[ind].value + +def blit(canvas, data, pos): + data = data.tostring() + tex = Texture.create(size=sz, colorfmt="rgb") + tex.blit_buffer(data, bufferfmt="ubyte", colorfmt="rgb") + canvas.texture = tex + canvas.pos = pos + +def makeTex(): + #sz = (2048+256, 1024+256) + sz = (2880, 1800) + texture = Texture.create(size=sz, colorfmt="rgb") + arr = np.random.randint(0, 256, (sz[0], sz[1], 3)).astype(np.uint8) + data = arr.tostring() + texture.blit_buffer(data, bufferfmt="ubyte", colorfmt="rgb") + return texture + +class Container(Widget): + def __init__(self, size, **kwargs): + super(Container, self).__init__(**kwargs) + self.size = size + self.W, self.H = size + with self.canvas: + self.screen = Rectangle(pos=(0, 0), size=size) + self.surf = pygame.Surface((self.W, self.H)) + self.texture = Texture.create(size=size, colorfmt="rgb") + + def reset(self): + self.fill((Color.BLACK)) + if self.border > 0: + self.renderBorder() + + def flip(self): + fast = True + if fast: + data = pygame.image.tostring(self.surf, 'RGB', False) + self.texture.blit_buffer(data, bufferfmt="ubyte", colorfmt="rgb") + else: + data = pygame.surfarray.array3d(self.surf) + data = data.transpose(1, 0, 2).tostring() + self.texture.blit_buffer(data, bufferfmt="ubyte", colorfmt="rgb") + self.screen.texture = self.texture + + def renderBorder(self): + for coords in [ + (0, 0, self.W, self.top), + (0, 0, self.left, self.H), + (0, self.bottom, self.W, self.H), + (self.right, 0, self.W, self.H) + ]: + pygame.draw.rect(self.surf, Color.RED, coords) + + def blit(self, container, pos, area=None, flags=0): + w, h = pos + pos = (w+self.border, h+self.border) + if type(container) == Surface: + self.canvas.blit(container, pos, area=area, special_flags=flags) + else: + self.canvas.blit(container.canvas, pos, area=area, special_flags=flags) + + if self.border > 0: + self.renderBorder() + + def fill(self, color): + self.surf.fill((color)) + + def rect(self, color, coords, lw=0): + pygame.draw.rect(self.surf, color, coords, lw) + + def line(self, color, start, end, lw=1): + pygame.draw.line(self.surf, color, start, end, lw) + +class Game(Container): + def __init__(self, size, **kwargs): + super(Game, self).__init__(size, **kwargs) + + def update(self, t): + print(1/t) + sz = 32 + n = 32 + self.rect(Neon.GRAY.value, (0, 0, self.W, self.H)) + for r in range(n): + for c in range(n): + if r not in (0, n-1): + if c not in (0, n-1): + continue + color = Neon.rand12() + rect = ((c+1)*sz, (r+1)*sz, sz, sz) + self.rect(color, rect) + self.flip() + +class MyApp(App): + def build(self): + size = (2048+256, 1024+256) + self.title = 'hello' + #surf = pygame.Surface(size) + #surf.fill(Neon.RED.value) + game = Game(size) + game.update(1) + #Clock.schedule_interval(game.update, 1.0/.001) + return game + +if __name__ == '__main__': + MyApp().run() diff --git a/jsuarez/tools/test.py b/jsuarez/tools/test.py new file mode 100644 index 00000000..20af734f --- /dev/null +++ b/jsuarez/tools/test.py @@ -0,0 +1,23 @@ +from pdb import set_trace as T +from kivy.app import App +from kivy.uix.widget import Widget +from kivy.graphics import Rectangle, Color + +class CornerRectangleWidget(Widget): + def __init__(self, **kwargs): + super(CornerRectangleWidget, self).__init__(**kwargs) + + with self.canvas: + Color(1, 0, 0, 1) # set the colour to red + self.rect = Rectangle(pos=self.center, + size=(self.width/2., + self.height/2.)) + +class MyApp(App): + def build(self): + game = CornerRectangleWidget() + #Clock.schedule_interval(game.update, 1.0/1000.0) + return game + +if __name__ == '__main__': + MyApp().run() diff --git a/logs.py b/logs.py new file mode 100644 index 00000000..51955e79 --- /dev/null +++ b/logs.py @@ -0,0 +1,106 @@ +import numpy as np +import sys +import json +from pdb import set_trace as T +from collections import defaultdict +from matplotlib import pyplot as plt +from forge.blade.lib.enums import Neon + +def plot(data, inds=None, label='data', c=Neon.RED.norm, lw=3): + if inds is None: + inds = np.arange(len(data)) + plt.plot(inds, data, c=c, linewidth=lw, label=label) + +def dark(): + plt.style.use('dark_background') + +def labels(xlabel='x', ylabel='y', title='title', + axsz=24, titlesz=28): + plt.xlabel(xlabel, fontsize=axsz) + plt.ylabel(ylabel, fontsize=axsz) + plt.title(title, fontsize=titlesz) + +def axes(ac, tc): + ax = plt.gca() + ax.title.set_color(ac) + ax.xaxis.label.set_color(ac) + ax.yaxis.label.set_color(ac) + for spine in ax.spines.values(): + spine.set_color(tc) + +def limits(xlims=None, ylims=None): + if xlims is not None: + plt.xlim(*xlims) + + if ylims is not None: + plt.ylim(*ylims) + +def ticks(ts, tc): + ax = plt.gca() + ax.tick_params(axis='x', colors=tc) + ax.tick_params(axis='y', colors=tc) + for tick in ax.xaxis.get_major_ticks(): + tick.label1.set_fontsize(ts) + tick.label1.set_fontweight('bold') + for tick in ax.yaxis.get_major_ticks(): + tick.label1.set_fontsize(ts) + tick.label1.set_fontweight('bold') + +def legend(ts, tc): + leg = plt.legend(loc='upper right') + for text in leg.get_texts(): + plt.setp(text, color=tc) + plt.setp(text, fontsize=ts) + +def fig(): + fig = plt.gcf() + fig.set_size_inches(12, 8, forward=True) + #plt.tight_layout() + +def show(): + fig.canvas.set_window_title('Projekt Godsword') + +def save(fPath): + fig = plt.gcf() + fig.savefig(fPath, dpi=300) + +def load(fDir): + try: + with open(fDir, 'r') as f: + logs = json.load(f) + + logDict = defaultdict(list) + for log in logs: + for k, v in log.items(): + logDict[k].append(v) + return logDict + except Exception as e: + print(e) + return None + +def godsword(): + labels('Steps', 'Value', 'Projekt: Godsword') + axes(Neon.MINT.norm, Neon.CYAN.norm) + ticks(18, Neon.CYAN.norm) + legend(18, Neon.CYAN.norm) + fig() + +def plots(logs): + colors = Neon.color12() + logs = reversed([e for e in logs.items()]) + for idx, kv in enumerate(logs): + k, v = kv + color = colors[idx].norm + plot(v, k, color) + +def log(): + fDir = 'resource/logs/' + fName = 'frag.png' + logs = load(fDir + 'logs.json') + dark() + plots(logs) + plt.ylim(0, 150) + godsword() + save(fDir+fName) + plt.close() + diff --git a/resource/assets/action/mage.png b/resource/assets/action/mage.png new file mode 100644 index 00000000..6b82a050 Binary files /dev/null and b/resource/assets/action/mage.png differ diff --git a/resource/assets/action/mageattack.png b/resource/assets/action/mageattack.png new file mode 100644 index 00000000..8a252536 Binary files /dev/null and b/resource/assets/action/mageattack.png differ diff --git a/resource/assets/action/melee.png b/resource/assets/action/melee.png new file mode 100644 index 00000000..3973a14e Binary files /dev/null and b/resource/assets/action/melee.png differ diff --git a/resource/assets/action/meleeattack.png b/resource/assets/action/meleeattack.png new file mode 100644 index 00000000..298d0566 Binary files /dev/null and b/resource/assets/action/meleeattack.png differ diff --git a/resource/assets/action/range.png b/resource/assets/action/range.png new file mode 100644 index 00000000..c317fda9 Binary files /dev/null and b/resource/assets/action/range.png differ diff --git a/resource/assets/action/rangeattack.png b/resource/assets/action/rangeattack.png new file mode 100644 index 00000000..0eea4bdb Binary files /dev/null and b/resource/assets/action/rangeattack.png differ diff --git a/resource/assets/entity/chicken.png b/resource/assets/entity/chicken.png new file mode 100644 index 00000000..a322344e Binary files /dev/null and b/resource/assets/entity/chicken.png differ diff --git a/resource/assets/entity/goblin.png b/resource/assets/entity/goblin.png new file mode 100644 index 00000000..52d6077a Binary files /dev/null and b/resource/assets/entity/goblin.png differ diff --git a/resource/assets/entity/neural.png b/resource/assets/entity/neural.png new file mode 100644 index 00000000..f5976861 Binary files /dev/null and b/resource/assets/entity/neural.png differ diff --git a/resource/assets/entity/neuralBLUE.png b/resource/assets/entity/neuralBLUE.png new file mode 100644 index 00000000..c0629e15 Binary files /dev/null and b/resource/assets/entity/neuralBLUE.png differ diff --git a/resource/assets/entity/neuralCYAN.png b/resource/assets/entity/neuralCYAN.png new file mode 100644 index 00000000..da1fc09e Binary files /dev/null and b/resource/assets/entity/neuralCYAN.png differ diff --git a/resource/assets/entity/neuralFUCHSIA.png b/resource/assets/entity/neuralFUCHSIA.png new file mode 100644 index 00000000..a7e723b9 Binary files /dev/null and b/resource/assets/entity/neuralFUCHSIA.png differ diff --git a/resource/assets/entity/neuralGREEN.png b/resource/assets/entity/neuralGREEN.png new file mode 100644 index 00000000..a8555024 Binary files /dev/null and b/resource/assets/entity/neuralGREEN.png differ diff --git a/resource/assets/entity/neuralMAGENTA.png b/resource/assets/entity/neuralMAGENTA.png new file mode 100644 index 00000000..a5eef66f Binary files /dev/null and b/resource/assets/entity/neuralMAGENTA.png differ diff --git a/resource/assets/entity/neuralMINT.png b/resource/assets/entity/neuralMINT.png new file mode 100644 index 00000000..a3a6e277 Binary files /dev/null and b/resource/assets/entity/neuralMINT.png differ diff --git a/resource/assets/entity/neuralORANGE.png b/resource/assets/entity/neuralORANGE.png new file mode 100644 index 00000000..b360ed27 Binary files /dev/null and b/resource/assets/entity/neuralORANGE.png differ diff --git a/resource/assets/entity/neuralPURPLE.png b/resource/assets/entity/neuralPURPLE.png new file mode 100644 index 00000000..01e7ecc1 Binary files /dev/null and b/resource/assets/entity/neuralPURPLE.png differ diff --git a/resource/assets/entity/neuralRED.png b/resource/assets/entity/neuralRED.png new file mode 100644 index 00000000..23b89c4e Binary files /dev/null and b/resource/assets/entity/neuralRED.png differ diff --git a/resource/assets/entity/neuralSKY.png b/resource/assets/entity/neuralSKY.png new file mode 100644 index 00000000..49f05926 Binary files /dev/null and b/resource/assets/entity/neuralSKY.png differ diff --git a/resource/assets/entity/neuralSPRING.png b/resource/assets/entity/neuralSPRING.png new file mode 100644 index 00000000..dbf42480 Binary files /dev/null and b/resource/assets/entity/neuralSPRING.png differ diff --git a/resource/assets/entity/neuralYELLOW.png b/resource/assets/entity/neuralYELLOW.png new file mode 100644 index 00000000..07e47f6f Binary files /dev/null and b/resource/assets/entity/neuralYELLOW.png differ diff --git a/resource/assets/fonts/dragonslapper.ttf b/resource/assets/fonts/dragonslapper.ttf new file mode 100755 index 00000000..85b5dc0d Binary files /dev/null and b/resource/assets/fonts/dragonslapper.ttf differ diff --git a/resource/assets/splash/splash.png b/resource/assets/splash/splash.png new file mode 100644 index 00000000..17f5c2d3 Binary files /dev/null and b/resource/assets/splash/splash.png differ diff --git a/resource/assets/tiles/.DS_Store b/resource/assets/tiles/.DS_Store new file mode 100644 index 00000000..d7271e86 Binary files /dev/null and b/resource/assets/tiles/.DS_Store differ diff --git a/resource/assets/tiles/brick.png b/resource/assets/tiles/brick.png new file mode 100755 index 00000000..4fd1eb95 Binary files /dev/null and b/resource/assets/tiles/brick.png differ diff --git a/resource/assets/tiles/clay.png b/resource/assets/tiles/clay.png new file mode 100755 index 00000000..238561ec Binary files /dev/null and b/resource/assets/tiles/clay.png differ diff --git a/resource/assets/tiles/coal_ore.png b/resource/assets/tiles/coal_ore.png new file mode 100755 index 00000000..9dfd2c4e Binary files /dev/null and b/resource/assets/tiles/coal_ore.png differ diff --git a/resource/assets/tiles/cobblestone.png b/resource/assets/tiles/cobblestone.png new file mode 100755 index 00000000..e82a2c95 Binary files /dev/null and b/resource/assets/tiles/cobblestone.png differ diff --git a/resource/assets/tiles/dirt.png b/resource/assets/tiles/dirt.png new file mode 100755 index 00000000..a5ff6571 Binary files /dev/null and b/resource/assets/tiles/dirt.png differ diff --git a/resource/assets/tiles/forest.png b/resource/assets/tiles/forest.png new file mode 100755 index 00000000..c9840470 Binary files /dev/null and b/resource/assets/tiles/forest.png differ diff --git a/resource/assets/tiles/grass.png b/resource/assets/tiles/grass.png new file mode 100755 index 00000000..0920bc90 Binary files /dev/null and b/resource/assets/tiles/grass.png differ diff --git a/resource/assets/tiles/grass_side.png b/resource/assets/tiles/grass_side.png new file mode 100755 index 00000000..fb039a55 Binary files /dev/null and b/resource/assets/tiles/grass_side.png differ diff --git a/resource/assets/tiles/gravel.png b/resource/assets/tiles/gravel.png new file mode 100755 index 00000000..fc4751de Binary files /dev/null and b/resource/assets/tiles/gravel.png differ diff --git a/resource/assets/tiles/ice.png b/resource/assets/tiles/ice.png new file mode 100755 index 00000000..e976ebaa Binary files /dev/null and b/resource/assets/tiles/ice.png differ diff --git a/resource/assets/tiles/iron_ore.png b/resource/assets/tiles/iron_ore.png new file mode 100755 index 00000000..118301cd Binary files /dev/null and b/resource/assets/tiles/iron_ore.png differ diff --git a/resource/assets/tiles/lava.png b/resource/assets/tiles/lava.png new file mode 100644 index 00000000..6d771d77 Binary files /dev/null and b/resource/assets/tiles/lava.png differ diff --git a/resource/assets/tiles/lavaold.png b/resource/assets/tiles/lavaold.png new file mode 100644 index 00000000..c1fe7b52 Binary files /dev/null and b/resource/assets/tiles/lavaold.png differ diff --git a/resource/assets/tiles/planks_oak.png b/resource/assets/tiles/planks_oak.png new file mode 100755 index 00000000..71eadeaa Binary files /dev/null and b/resource/assets/tiles/planks_oak.png differ diff --git a/resource/assets/tiles/sand.png b/resource/assets/tiles/sand.png new file mode 100755 index 00000000..186c863d Binary files /dev/null and b/resource/assets/tiles/sand.png differ diff --git a/resource/assets/tiles/sandstone.png b/resource/assets/tiles/sandstone.png new file mode 100755 index 00000000..5c016198 Binary files /dev/null and b/resource/assets/tiles/sandstone.png differ diff --git a/resource/assets/tiles/scrub.png b/resource/assets/tiles/scrub.png new file mode 100755 index 00000000..cb85930a Binary files /dev/null and b/resource/assets/tiles/scrub.png differ diff --git a/resource/assets/tiles/snow.png b/resource/assets/tiles/snow.png new file mode 100755 index 00000000..65acfcac Binary files /dev/null and b/resource/assets/tiles/snow.png differ diff --git a/resource/assets/tiles/stone.png b/resource/assets/tiles/stone.png new file mode 100755 index 00000000..60537f3a Binary files /dev/null and b/resource/assets/tiles/stone.png differ diff --git a/resource/assets/tiles/tree.png b/resource/assets/tiles/tree.png new file mode 100644 index 00000000..4cd0de74 Binary files /dev/null and b/resource/assets/tiles/tree.png differ diff --git a/resource/assets/tiles/water.png b/resource/assets/tiles/water.png new file mode 100644 index 00000000..8b5126a4 Binary files /dev/null and b/resource/assets/tiles/water.png differ diff --git a/resource/docs/ags.png b/resource/docs/ags.png new file mode 100644 index 00000000..569ac53d Binary files /dev/null and b/resource/docs/ags.png differ diff --git a/resource/docs/air.png b/resource/docs/air.png new file mode 100644 index 00000000..525c6b29 Binary files /dev/null and b/resource/docs/air.png differ diff --git a/resource/docs/air_thumbnail.png b/resource/docs/air_thumbnail.png new file mode 100644 index 00000000..1e04c097 Binary files /dev/null and b/resource/docs/air_thumbnail.png differ diff --git a/resource/docs/early_demo.mp4 b/resource/docs/early_demo.mp4 new file mode 100644 index 00000000..93c17c93 Binary files /dev/null and b/resource/docs/early_demo.mp4 differ diff --git a/resource/docs/earth.png b/resource/docs/earth.png new file mode 100644 index 00000000..2d940427 Binary files /dev/null and b/resource/docs/earth.png differ diff --git a/resource/docs/earth_thumbnail.png b/resource/docs/earth_thumbnail.png new file mode 100644 index 00000000..a2456e22 Binary files /dev/null and b/resource/docs/earth_thumbnail.png differ diff --git a/resource/docs/env.jpg b/resource/docs/env.jpg new file mode 100644 index 00000000..518f4501 Binary files /dev/null and b/resource/docs/env.jpg differ diff --git a/resource/docs/equipment.png b/resource/docs/equipment.png new file mode 100644 index 00000000..7d532f48 Binary files /dev/null and b/resource/docs/equipment.png differ diff --git a/resource/docs/fire.png b/resource/docs/fire.png new file mode 100644 index 00000000..301fe5a2 Binary files /dev/null and b/resource/docs/fire.png differ diff --git a/resource/docs/fire_thumbnail.png b/resource/docs/fire_thumbnail.png new file mode 100644 index 00000000..83321220 Binary files /dev/null and b/resource/docs/fire_thumbnail.png differ diff --git a/resource/docs/logo.png b/resource/docs/logo.png new file mode 100644 index 00000000..165cc471 Binary files /dev/null and b/resource/docs/logo.png differ diff --git a/resource/docs/logodark.png b/resource/docs/logodark.png new file mode 100644 index 00000000..40a72e6f Binary files /dev/null and b/resource/docs/logodark.png differ diff --git a/resource/docs/map.png b/resource/docs/map.png new file mode 100644 index 00000000..b70ae737 Binary files /dev/null and b/resource/docs/map.png differ diff --git a/resource/docs/neural.png b/resource/docs/neural.png new file mode 100644 index 00000000..b9657cc4 Binary files /dev/null and b/resource/docs/neural.png differ diff --git a/resource/docs/neuralBLUE.png b/resource/docs/neuralBLUE.png new file mode 100644 index 00000000..cbf49f97 Binary files /dev/null and b/resource/docs/neuralBLUE.png differ diff --git a/resource/docs/neuralCYAN.png b/resource/docs/neuralCYAN.png new file mode 100644 index 00000000..cfff3430 Binary files /dev/null and b/resource/docs/neuralCYAN.png differ diff --git a/resource/docs/neuralFUCHSIA.png b/resource/docs/neuralFUCHSIA.png new file mode 100644 index 00000000..e7d6ed03 Binary files /dev/null and b/resource/docs/neuralFUCHSIA.png differ diff --git a/resource/docs/neuralGREEN.png b/resource/docs/neuralGREEN.png new file mode 100644 index 00000000..cc92cc40 Binary files /dev/null and b/resource/docs/neuralGREEN.png differ diff --git a/resource/docs/neuralMAGENTA.png b/resource/docs/neuralMAGENTA.png new file mode 100644 index 00000000..8279bddc Binary files /dev/null and b/resource/docs/neuralMAGENTA.png differ diff --git a/resource/docs/neuralMINT.png b/resource/docs/neuralMINT.png new file mode 100644 index 00000000..a5084e24 Binary files /dev/null and b/resource/docs/neuralMINT.png differ diff --git a/resource/docs/neuralORANGE.png b/resource/docs/neuralORANGE.png new file mode 100644 index 00000000..965965ab Binary files /dev/null and b/resource/docs/neuralORANGE.png differ diff --git a/resource/docs/neuralPURPLE.png b/resource/docs/neuralPURPLE.png new file mode 100644 index 00000000..eedf6942 Binary files /dev/null and b/resource/docs/neuralPURPLE.png differ diff --git a/resource/docs/neuralRED.png b/resource/docs/neuralRED.png new file mode 100644 index 00000000..8be83b0e Binary files /dev/null and b/resource/docs/neuralRED.png differ diff --git a/resource/docs/neuralSKY.png b/resource/docs/neuralSKY.png new file mode 100644 index 00000000..b55a31ee Binary files /dev/null and b/resource/docs/neuralSKY.png differ diff --git a/resource/docs/neuralSPRING.png b/resource/docs/neuralSPRING.png new file mode 100644 index 00000000..85cfd31a Binary files /dev/null and b/resource/docs/neuralSPRING.png differ diff --git a/resource/docs/neuralYELLOW.png b/resource/docs/neuralYELLOW.png new file mode 100644 index 00000000..f5980308 Binary files /dev/null and b/resource/docs/neuralYELLOW.png differ diff --git a/resource/docs/quests.png b/resource/docs/quests.png new file mode 100644 index 00000000..4f53edbc Binary files /dev/null and b/resource/docs/quests.png differ diff --git a/resource/docs/skills.png b/resource/docs/skills.png new file mode 100644 index 00000000..e86b57e7 Binary files /dev/null and b/resource/docs/skills.png differ diff --git a/resource/docs/water.png b/resource/docs/water.png new file mode 100644 index 00000000..e261957a Binary files /dev/null and b/resource/docs/water.png differ diff --git a/resource/docs/water_thumbnail.png b/resource/docs/water_thumbnail.png new file mode 100644 index 00000000..12a14121 Binary files /dev/null and b/resource/docs/water_thumbnail.png differ diff --git a/resource/exps/sample-autotarget/model/bests.pth b/resource/exps/sample-autotarget/model/bests.pth new file mode 100644 index 00000000..87bb8671 Binary files /dev/null and b/resource/exps/sample-autotarget/model/bests.pth differ diff --git a/resource/exps/sample-autotarget/train/counts_forest.png b/resource/exps/sample-autotarget/train/counts_forest.png new file mode 100644 index 00000000..0315c620 Binary files /dev/null and b/resource/exps/sample-autotarget/train/counts_forest.png differ diff --git a/resource/exps/sample-autotarget/train/counts_grass.png b/resource/exps/sample-autotarget/train/counts_grass.png new file mode 100644 index 00000000..ca9dda04 Binary files /dev/null and b/resource/exps/sample-autotarget/train/counts_grass.png differ diff --git a/resource/exps/sample-autotarget/train/counts_scrub.png b/resource/exps/sample-autotarget/train/counts_scrub.png new file mode 100644 index 00000000..c5287657 Binary files /dev/null and b/resource/exps/sample-autotarget/train/counts_scrub.png differ diff --git a/resource/exps/sample-autotarget/train/explore_grass.png b/resource/exps/sample-autotarget/train/explore_grass.png new file mode 100644 index 00000000..d5345cfa Binary files /dev/null and b/resource/exps/sample-autotarget/train/explore_grass.png differ diff --git a/resource/exps/sample-autotarget/train/explore_scrub.png b/resource/exps/sample-autotarget/train/explore_scrub.png new file mode 100644 index 00000000..c3566a7e Binary files /dev/null and b/resource/exps/sample-autotarget/train/explore_scrub.png differ diff --git a/resource/exps/sample-autotarget/train/lifetime.png b/resource/exps/sample-autotarget/train/lifetime.png new file mode 100644 index 00000000..c9ac8bc3 Binary files /dev/null and b/resource/exps/sample-autotarget/train/lifetime.png differ diff --git a/resource/exps/sample-autotarget/train/reward.png b/resource/exps/sample-autotarget/train/reward.png new file mode 100644 index 00000000..0315c620 Binary files /dev/null and b/resource/exps/sample-autotarget/train/reward.png differ diff --git a/resource/exps/sample-autotarget/train/unique_forest.png b/resource/exps/sample-autotarget/train/unique_forest.png new file mode 100644 index 00000000..0315c620 Binary files /dev/null and b/resource/exps/sample-autotarget/train/unique_forest.png differ diff --git a/resource/exps/sample-autotarget/train/unique_grass.png b/resource/exps/sample-autotarget/train/unique_grass.png new file mode 100644 index 00000000..f4afc8ab Binary files /dev/null and b/resource/exps/sample-autotarget/train/unique_grass.png differ diff --git a/resource/exps/sample-autotarget/train/unique_scrub.png b/resource/exps/sample-autotarget/train/unique_scrub.png new file mode 100644 index 00000000..a78683a4 Binary files /dev/null and b/resource/exps/sample-autotarget/train/unique_scrub.png differ diff --git a/resource/exps/sample-autotarget/train/value.png b/resource/exps/sample-autotarget/train/value.png new file mode 100644 index 00000000..41fd490b Binary files /dev/null and b/resource/exps/sample-autotarget/train/value.png differ diff --git a/resource/exps/testchaos128/model/bests.pth b/resource/exps/testchaos128/model/bests.pth new file mode 100644 index 00000000..c9119115 Binary files /dev/null and b/resource/exps/testchaos128/model/bests.pth differ diff --git a/resource/logs/.gitignore b/resource/logs/.gitignore new file mode 100644 index 00000000..5e7d2734 --- /dev/null +++ b/resource/logs/.gitignore @@ -0,0 +1,4 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore diff --git a/resource/maps/fixed/br.tmx b/resource/maps/fixed/br.tmx new file mode 100644 index 00000000..fb3d9359 --- /dev/null +++ b/resource/maps/fixed/br.tmx @@ -0,0 +1,90 @@ + + + + + +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,20,8,8,8,18,8,8,8,8,8,18,18,8,8,8,8,8,8,6,8,8,8,8,8,8,8,8,8,20,20,8,8,8,8,8,8,8,8,8,6,8,8,8,8,8,8,18,18,8,8,8,8,8,18,8,8,8,20,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,8,18,18,18,18,8,8,8,8,8,18,8,8,8,8,6,8,8,8,8,18,18,8,8,8,8,6,8,8,8,8,18,8,8,8,8,8,18,18,18,18,8,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,8,8,6,6,8,8,8,8,20,8,18,8,20,8,8,8,8,8,8,8,18,18,8,8,8,8,8,8,8,20,8,18,8,20,8,8,8,8,6,6,8,8,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,6,18,20,8,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,6,18,18,6,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,8,20,18,6,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,18,18,18,18,18,18,18,18,18,8,8,8,8,18,18,18,18,18,18,18,18,18,18,18,8,8,18,18,18,18,18,18,8,8,18,18,18,18,18,18,18,18,18,18,18,8,8,8,8,18,18,18,18,18,18,18,18,18,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,20,18,8,8,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,18,18,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,8,8,18,20,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,8,8,18,8,6,8,8,8,8,8,8,8,18,18,8,8,8,8,8,8,8,6,8,18,8,8,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,20,20,8,8,18,8,8,6,6,8,8,11,8,8,8,8,8,8,6,8,8,18,18,8,8,8,6,8,8,8,8,8,11,8,8,6,6,8,8,18,8,8,20,20,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,20,20,8,8,18,8,8,6,6,8,8,8,8,8,8,18,8,8,6,8,8,18,18,8,8,8,6,8,18,8,8,8,8,8,8,6,6,8,8,18,8,8,20,20,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,18,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,20,8,18,8,8,8,8,8,8,8,8,8,8,8,8,18,8,20,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,18,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,18,18,6,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,6,18,18,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,18,18,6,8,8,8,8,18,18,18,18,18,18,8,8,18,18,18,18,8,8,8,11,18,18,18,8,8,20,20,8,8,18,18,18,11,8,8,8,18,18,18,18,8,8,18,18,18,18,18,18,8,8,8,8,6,18,18,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,18,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,18,8,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,18,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,18,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,6,6,8,8,8,8,8,20,20,8,8,18,18,18,18,8,8,6,8,8,8,11,11,8,8,8,6,8,8,18,18,18,18,8,8,20,20,8,8,8,8,8,6,6,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,6,6,8,8,18,8,8,20,20,8,8,8,8,8,8,8,8,8,8,8,8,18,18,8,8,8,8,8,8,8,8,8,8,8,8,20,20,8,8,18,8,8,6,6,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,20,8,18,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,20,8,8,6,6,18,18,6,6,8,8,20,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,18,8,20,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,8,8,8,18,8,8,8,8,8,8,18,6,8,8,8,8,8,8,6,6,18,18,6,6,8,8,8,8,8,8,6,18,8,8,8,8,8,8,18,8,8,8,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,6,18,18,18,18,18,18,11,8,8,8,18,18,18,18,8,8,18,18,18,18,18,8,8,8,8,18,18,18,18,18,18,8,8,8,8,18,18,18,18,18,8,8,18,18,18,18,8,8,8,11,18,18,18,18,18,18,6,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,18,8,8,6,18,6,8,8,8,8,8,6,8,8,20,20,8,8,6,8,8,8,8,8,6,18,6,8,8,18,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,20,8,18,8,6,8,8,20,8,8,6,8,18,8,8,8,18,8,8,8,8,6,8,8,8,8,8,8,8,8,8,8,6,8,8,8,8,18,8,8,8,18,8,6,8,8,20,8,8,6,8,18,8,20,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,18,8,8,8,18,8,8,20,18,8,8,8,8,8,8,8,8,8,8,8,8,18,20,8,8,18,8,8,8,18,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,18,18,18,11,8,8,8,8,8,8,8,8,8,18,11,8,8,8,8,11,8,8,11,8,8,8,8,11,18,8,8,8,8,8,8,8,8,8,11,18,18,18,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,6,8,8,18,8,8,8,8,8,8,18,8,8,8,8,20,8,8,8,6,8,8,8,6,18,18,18,8,8,18,18,18,6,8,8,8,6,8,8,8,20,8,8,8,8,18,8,8,8,8,8,8,18,8,8,6,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,8,8,8,8,8,8,8,18,8,8,6,8,8,8,8,8,8,8,8,6,18,18,6,6,8,8,6,6,18,18,6,8,8,8,8,8,8,8,8,6,8,8,18,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,8,8,8,6,6,8,8,18,8,8,8,8,8,8,8,6,8,8,8,18,18,6,8,8,8,8,8,8,6,18,18,8,8,8,6,8,8,8,8,8,8,8,18,8,8,6,6,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,6,6,18,8,8,8,8,18,6,8,11,11,8,8,11,11,8,6,18,8,8,8,8,18,6,6,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,6,18,8,8,8,8,8,8,8,8,8,8,8,6,6,18,8,8,8,11,18,6,8,11,8,8,8,8,11,8,6,18,11,8,8,8,18,6,6,8,8,8,8,8,8,8,8,8,8,8,18,6,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,20,18,18,18,18,18,18,18,18,8,8,20,8,8,11,18,18,18,18,20,8,8,8,8,8,8,8,8,20,20,8,8,8,8,8,8,8,8,20,18,18,18,18,11,8,8,20,8,8,18,18,18,18,18,18,18,18,20,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,20,18,18,18,18,18,18,18,18,8,8,20,8,8,11,18,18,18,18,20,8,8,8,8,8,8,8,8,20,20,8,8,8,8,8,8,8,8,20,18,18,18,18,11,8,8,20,8,8,18,18,18,18,18,18,18,18,20,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,6,18,8,8,8,8,8,8,8,8,8,8,8,6,6,18,8,8,8,11,18,6,8,11,8,8,8,8,11,8,6,18,11,8,8,8,18,6,6,8,8,8,8,8,8,8,8,8,8,8,18,6,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,6,6,18,8,8,8,8,18,6,8,11,11,8,8,11,11,8,6,18,8,8,8,8,18,6,6,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,8,8,8,6,6,8,8,18,8,8,8,8,8,8,8,6,8,8,8,18,18,6,8,8,8,8,8,8,6,18,18,8,8,8,6,8,8,8,8,8,8,8,18,8,8,6,6,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,8,8,8,8,8,8,8,18,8,8,6,8,8,8,8,8,8,8,8,6,18,18,6,6,8,8,6,6,18,18,6,8,8,8,8,8,8,8,8,6,8,8,18,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,6,8,8,18,8,8,8,8,8,8,18,8,8,8,8,20,8,8,8,6,8,8,8,6,18,18,18,8,8,18,18,18,6,8,8,8,6,8,8,8,20,8,8,8,8,18,8,8,8,8,8,8,18,8,8,6,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,18,18,18,11,8,8,8,8,8,8,8,8,8,18,11,8,8,8,8,11,8,8,11,8,8,8,8,11,18,8,8,8,8,8,8,8,8,8,11,18,18,18,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,18,8,8,8,18,8,8,20,18,8,8,8,8,8,8,8,8,8,8,8,8,18,20,8,8,18,8,8,8,18,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,20,8,18,8,6,8,8,20,8,8,6,8,18,8,8,8,18,8,8,8,8,6,8,8,8,8,8,8,8,8,8,8,6,8,8,8,8,18,8,8,8,18,8,6,8,8,20,8,8,6,8,18,8,20,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,18,8,8,6,18,6,8,8,8,8,8,6,8,8,20,20,8,8,6,8,8,8,8,8,6,18,6,8,8,18,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,6,18,18,18,18,18,18,11,8,8,8,18,18,18,18,8,8,18,18,18,18,18,8,8,8,8,18,18,18,18,18,18,8,8,8,8,18,18,18,18,18,8,8,18,18,18,18,8,8,8,11,18,18,18,18,18,18,6,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,8,8,8,18,8,8,8,8,8,8,18,6,8,8,8,8,8,8,6,6,18,18,6,6,8,8,8,8,8,8,6,18,8,8,8,8,8,8,18,8,8,8,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,20,8,18,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,20,8,8,6,6,18,18,6,6,8,8,20,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,18,8,20,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,6,6,8,8,18,8,8,20,20,8,8,8,8,8,8,8,8,8,8,8,8,18,18,8,8,8,8,8,8,8,8,8,8,8,8,20,20,8,8,18,8,8,6,6,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,6,6,8,8,8,8,8,20,20,8,8,18,18,18,18,8,8,6,8,8,8,11,11,8,8,8,6,8,8,18,18,18,18,8,8,20,20,8,8,8,8,8,6,6,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,18,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,18,8,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,18,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,18,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,18,18,6,8,8,8,8,18,18,18,18,18,18,8,8,18,18,18,18,8,8,8,18,18,18,18,8,8,20,20,8,8,18,18,18,18,8,8,8,18,18,18,18,8,8,18,18,18,18,18,18,8,8,8,8,6,18,18,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,18,18,6,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,6,18,18,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,18,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,20,8,18,8,8,8,8,8,8,8,8,8,8,8,8,18,8,20,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,18,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,20,20,8,8,18,8,8,6,6,8,8,8,8,8,8,18,8,8,6,8,8,18,18,8,8,6,8,8,18,8,8,8,8,8,8,6,6,8,8,18,8,8,20,20,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,20,20,8,8,18,8,8,6,6,8,8,11,8,8,8,8,8,8,6,8,8,18,18,8,8,6,8,8,8,8,8,8,11,8,8,6,6,8,8,18,8,8,20,20,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,8,8,18,8,6,8,8,8,8,8,8,8,18,18,8,8,8,8,8,8,8,6,8,18,8,8,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,20,18,8,8,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,18,18,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,8,8,18,20,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,18,18,18,18,18,18,18,18,18,8,8,8,8,18,18,18,18,18,18,18,18,18,18,18,8,8,18,18,18,18,18,18,8,8,18,18,18,18,18,18,18,18,18,18,18,8,8,8,8,18,18,18,18,18,18,18,18,18,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,6,18,20,8,8,8,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,6,18,18,6,8,8,8,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,8,8,8,20,18,6,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,8,8,6,6,8,8,8,8,20,8,18,8,20,8,8,8,8,8,8,8,18,18,8,8,8,8,8,8,8,20,8,18,8,20,8,8,8,8,6,6,8,8,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,18,8,8,8,8,18,18,18,18,8,8,8,8,8,18,8,8,8,8,8,8,8,8,8,18,18,8,8,8,8,8,8,8,8,8,18,8,8,8,8,8,18,18,18,18,8,8,8,8,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,20,8,8,8,18,8,8,8,8,8,18,18,8,8,8,8,8,8,6,8,8,8,8,6,8,8,8,8,20,20,8,8,8,8,6,8,8,8,8,6,8,8,8,8,8,8,18,18,8,8,8,8,8,18,8,8,8,20,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 + + + diff --git a/resource/maps/fixed/testmap.tmx b/resource/maps/fixed/testmap.tmx new file mode 100644 index 00000000..ed967b2e --- /dev/null +++ b/resource/maps/fixed/testmap.tmx @@ -0,0 +1,72 @@ + + + + + +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12, +12,8,8,20,8,8,8,12,8,8,8,8,8,12,12,8,8,8,8,8,8,6,8,8,8,8,8,8,8,8,8,20,20,8,8,8,8,8,8,8,8,8,6,8,8,8,8,8,8,12,12,8,8,8,8,8,12,8,8,8,20,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,8,12,12,12,12,8,8,8,8,8,12,8,8,8,8,6,8,8,8,8,12,12,8,8,8,8,6,8,8,8,8,12,8,8,8,8,8,12,12,12,12,8,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,8,8,6,6,8,8,8,8,20,8,12,8,20,8,8,8,8,8,8,8,12,12,8,8,8,8,8,8,8,20,8,12,8,20,8,8,8,8,6,6,8,8,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,8,6,12,20,8,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,6,12,12,6,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,20,12,6,8,8,8,8,8,12, +12,8,8,12,12,12,12,12,12,12,12,12,8,8,8,8,12,12,12,12,12,12,12,12,12,12,12,8,8,12,12,12,12,12,12,8,8,12,12,12,12,12,12,12,12,12,12,12,8,8,8,8,12,12,12,12,12,12,12,12,12,8,8,12, +12,8,8,8,8,8,20,12,8,8,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,12,12,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,8,12,20,8,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,8,12,8,6,8,8,8,8,8,8,8,12,12,8,8,8,8,8,8,8,6,8,12,8,8,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,20,20,8,8,12,8,8,6,6,8,8,12,8,8,8,8,8,8,6,8,8,12,12,8,8,8,6,8,8,8,8,8,12,8,8,6,6,8,8,12,8,8,20,20,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,20,20,8,8,12,8,8,6,6,8,8,8,8,8,8,12,8,8,6,8,8,12,12,8,8,8,6,8,12,8,8,8,8,8,8,6,6,8,8,12,8,8,20,20,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,12,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,20,8,12,8,8,8,8,8,8,8,8,8,8,8,8,12,8,20,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,12,8,8,8,12, +12,8,8,12,12,6,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,6,12,12,8,8,12, +12,8,8,12,12,6,8,8,8,8,12,12,12,12,12,12,8,8,12,12,12,12,8,8,8,12,12,12,12,8,8,20,20,8,8,12,12,12,12,8,8,8,12,12,12,12,8,8,12,12,12,12,12,12,8,8,8,8,6,12,12,8,8,12, +12,8,8,8,12,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,12,8,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,12,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,12,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,6,6,8,8,8,8,8,20,20,8,8,12,12,12,12,8,8,6,8,8,8,12,12,8,8,8,6,8,8,12,12,12,12,8,8,20,20,8,8,8,8,8,6,6,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,6,6,8,8,12,8,8,20,20,8,8,8,8,8,8,8,8,8,8,8,8,12,12,8,8,8,8,8,8,8,8,8,8,8,8,20,20,8,8,12,8,8,6,6,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,20,8,12,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,20,8,8,6,6,12,12,6,6,8,8,20,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,12,8,20,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,8,8,8,12,8,8,8,8,8,8,12,6,8,8,8,8,8,8,6,6,12,12,6,6,8,8,8,8,8,8,6,12,8,8,8,8,8,8,12,8,8,8,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,6,12,12,12,12,12,12,12,8,8,8,12,12,12,12,8,8,12,12,12,12,12,8,8,8,8,12,12,12,12,12,12,8,8,8,8,12,12,12,12,12,8,8,12,12,12,12,8,8,8,12,12,12,12,12,12,12,6,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,12,8,8,6,12,6,8,8,8,8,8,6,8,8,20,20,8,8,6,8,8,8,8,8,6,12,6,8,8,12,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,20,8,12,8,6,8,8,20,8,8,6,8,12,8,8,8,12,8,8,8,8,6,8,8,8,8,8,8,8,8,8,8,6,8,8,8,8,12,8,8,8,12,8,6,8,8,20,8,8,6,8,12,8,20,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,12,8,8,8,12,8,8,20,12,8,8,8,8,8,8,8,8,8,8,8,8,12,20,8,8,12,8,8,8,12,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,12,12,12,12,8,8,8,8,8,8,8,8,8,12,12,8,8,8,8,12,8,8,12,8,8,8,8,12,12,8,8,8,8,8,8,8,8,8,12,12,12,12,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,6,8,8,12,8,8,8,8,8,8,12,8,8,8,8,20,8,8,8,6,8,8,8,6,12,12,12,8,8,12,12,12,6,8,8,8,6,8,8,8,20,8,8,8,8,12,8,8,8,8,8,8,12,8,8,6,8,8,8,12, +12,8,8,8,8,8,8,8,8,8,8,8,8,8,12,8,8,6,8,8,8,8,8,8,8,8,6,12,12,6,6,8,8,6,6,12,12,6,8,8,8,8,8,8,8,8,6,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,8,8,8,6,6,8,8,12,8,8,8,8,8,8,8,6,8,8,8,12,12,6,8,8,8,8,8,8,6,12,12,8,8,8,6,8,8,8,8,8,8,8,12,8,8,6,6,8,8,8,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,6,6,12,8,8,8,8,12,6,8,12,12,8,8,12,12,8,6,12,8,8,8,8,12,6,6,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,8,6,12,8,8,8,8,8,8,8,8,8,8,8,6,6,12,8,8,8,12,12,6,8,12,8,8,8,8,12,8,6,12,12,8,8,8,12,6,6,8,8,8,8,8,8,8,8,8,8,8,12,6,8,8,8,8,8,12, +12,8,8,20,12,12,12,12,12,12,12,12,8,8,20,8,8,12,12,12,12,12,20,8,8,8,8,8,8,8,8,20,20,8,8,8,8,8,8,8,8,20,12,12,12,12,12,8,8,20,8,8,12,12,12,12,12,12,12,12,20,8,8,12, +12,8,8,20,12,12,12,12,12,12,12,12,8,8,20,8,8,12,12,12,12,12,20,8,8,8,8,8,8,8,8,20,20,8,8,8,8,8,8,8,8,20,12,12,12,12,12,8,8,20,8,8,12,12,12,12,12,12,12,12,20,8,8,12, +12,8,8,8,8,8,6,12,8,8,8,8,8,8,8,8,8,8,8,6,6,12,8,8,8,12,12,6,8,12,8,8,8,8,12,8,6,12,12,8,8,8,12,6,6,8,8,8,8,8,8,8,8,8,8,8,12,6,8,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,6,6,12,8,8,8,8,12,6,8,12,12,8,8,12,12,8,6,12,8,8,8,8,12,6,6,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,8,8,8,6,6,8,8,12,8,8,8,8,8,8,8,6,8,8,8,12,12,6,8,8,8,8,8,8,6,12,12,8,8,8,6,8,8,8,8,8,8,8,12,8,8,6,6,8,8,8,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,8,8,8,8,8,8,8,12,8,8,6,8,8,8,8,8,8,8,8,6,12,12,6,6,8,8,6,6,12,12,6,8,8,8,8,8,8,8,8,6,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,8,12, +12,8,8,8,6,8,8,12,8,8,8,8,8,8,12,8,8,8,8,20,8,8,8,6,8,8,8,6,12,12,12,8,8,12,12,12,6,8,8,8,6,8,8,8,20,8,8,8,8,12,8,8,8,8,8,8,12,8,8,6,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,12,12,12,12,8,8,8,8,8,8,8,8,8,12,12,8,8,8,8,12,8,8,12,8,8,8,8,12,12,8,8,8,8,8,8,8,8,8,12,12,12,12,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,12,8,8,8,12,8,8,20,12,8,8,8,8,8,8,8,8,8,8,8,8,12,20,8,8,12,8,8,8,12,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,20,8,12,8,6,8,8,20,8,8,6,8,12,8,8,8,12,8,8,8,8,6,8,8,8,8,8,8,8,8,8,8,6,8,8,8,8,12,8,8,8,12,8,6,8,8,20,8,8,6,8,12,8,20,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,12,8,8,6,12,6,8,8,8,8,8,6,8,8,20,20,8,8,6,8,8,8,8,8,6,12,6,8,8,12,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,6,12,12,12,12,12,12,12,8,8,8,12,12,12,12,8,8,12,12,12,12,12,8,8,8,8,12,12,12,12,12,12,8,8,8,8,12,12,12,12,12,8,8,12,12,12,12,8,8,8,12,12,12,12,12,12,12,6,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,8,8,8,12,8,8,8,8,8,8,12,6,8,8,8,8,8,8,6,6,12,12,6,6,8,8,8,8,8,8,6,12,8,8,8,8,8,8,12,8,8,8,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,20,8,12,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,20,8,8,6,6,12,12,6,6,8,8,20,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,12,8,20,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,6,6,8,8,12,8,8,20,20,8,8,8,8,8,8,8,8,8,8,8,8,12,12,8,8,8,8,8,8,8,8,8,8,8,8,20,20,8,8,12,8,8,6,6,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,6,6,8,8,8,8,8,20,20,8,8,12,12,12,12,8,8,6,8,8,8,12,12,8,8,8,6,8,8,12,12,12,12,8,8,20,20,8,8,8,8,8,6,6,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,12,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,12,8,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,12,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,12,8,8,8,12, +12,8,8,12,12,6,8,8,8,8,12,12,12,12,12,12,8,8,12,12,12,12,8,8,8,12,12,12,12,8,8,20,20,8,8,12,12,12,12,8,8,8,12,12,12,12,8,8,12,12,12,12,12,12,8,8,8,8,6,12,12,8,8,12, +12,8,8,12,12,6,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,6,12,12,8,8,12, +12,8,8,8,12,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,20,8,12,8,8,8,8,8,8,8,8,8,8,8,8,12,8,20,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,12,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,20,20,8,8,12,8,8,6,6,8,8,8,8,8,8,12,8,8,6,8,8,12,12,8,8,6,8,8,12,8,8,8,8,8,8,6,6,8,8,12,8,8,20,20,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,20,20,8,8,12,8,8,6,6,8,8,12,8,8,8,8,8,8,6,8,8,12,12,8,8,6,8,8,8,8,8,8,12,8,8,6,6,8,8,12,8,8,20,20,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,8,12,8,6,8,8,8,8,8,8,8,12,12,8,8,8,8,8,8,8,6,8,12,8,8,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,8,20,12,8,8,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,12,12,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,8,12,20,8,8,8,8,8,12, +12,8,8,12,12,12,12,12,12,12,12,12,8,8,8,8,12,12,12,12,12,12,12,12,12,12,12,8,8,12,12,12,12,12,12,8,8,12,12,12,12,12,12,12,12,12,12,12,8,8,8,8,12,12,12,12,12,12,12,12,12,8,8,12, +12,8,8,8,8,8,6,12,20,8,8,8,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,6,12,12,6,8,8,8,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,8,8,8,20,12,6,8,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,8,8,6,6,8,8,8,8,20,8,12,8,20,8,8,8,8,8,8,8,12,12,8,8,8,8,8,8,8,20,8,12,8,20,8,8,8,8,6,6,8,8,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,12,8,8,8,8,12,12,12,12,8,8,8,8,8,12,8,8,8,8,8,8,8,8,8,12,12,8,8,8,8,8,8,8,8,8,12,8,8,8,8,8,12,12,12,12,8,8,8,8,12,8,8,8,8,8,8,12, +12,8,8,20,8,8,8,12,8,8,8,8,8,12,12,8,8,8,8,8,8,6,8,8,8,8,6,8,8,8,8,20,20,8,8,8,8,6,8,8,8,8,6,8,8,8,8,8,8,12,12,8,8,8,8,8,12,8,8,8,20,8,8,12, +12,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12, +12,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 + + + diff --git a/resource/maps/fixed/v1full.tmx b/resource/maps/fixed/v1full.tmx new file mode 100644 index 00000000..da01e3e4 --- /dev/null +++ b/resource/maps/fixed/v1full.tmx @@ -0,0 +1,108 @@ + + + + + +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,8,8,8,8,8,20,20,20,20,20,20,20,20,20,20,20,20,20,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,8,6,6,6,6,6,6,6,6,6,8,8,8,8,8,8,8,20,20,20,20,20,20,20,20,20,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,6,11,8,8,11,11,6,6,6,6,8,8,8,8,8,8,8,8,6,6,6,6,6,8,8,8,8,8,8,8,8,20,20,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,11,8,8,8,8,8,6,6,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,8,8,8,8,8,8,8,11,6,6,8,8,8,8,8,8,8,6,6,6,6,8,8,8,8,8,8,8,8,8,20,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,11,8,8,8,8,8,8,11,6,8,8,8,8,8,8,8,6,8,8,8,8,6,8,8,8,8,8,8,8,8,20,20,20,20,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,11,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,8,8,8,8,8,6,8,8,8,8,8,8,8,20,20,20,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,11,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,8,8,20,20,8,8,8,6,8,8,8,8,8,8,20,20,20,20,8,8,8,8,6,8,8,8,8,8,8,20,20,20,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,8,8,11,8,8,8,8,8,8,8,8,8,8,6,8,8,20,20,20,20,8,8,6,8,8,8,8,8,8,20,20,20,20,8,8,8,8,6,8,8,8,8,8,20,20,20,20,20,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,8,11,8,11,8,8,8,8,8,8,8,8,8,6,8,8,20,20,20,20,8,8,6,8,8,8,8,8,8,8,20,20,20,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,6,6,8,8,8,8,8,8,8,8,8,8,8,6,8,8,8,20,20,8,8,8,6,8,8,8,8,8,8,8,20,20,20,20,8,8,8,8,8,8,8,8,8,20,20,20,20,20,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,6,6,6,6,8,8,8,8,8,8,8,8,8,8,6,8,8,8,8,8,8,6,8,8,8,8,8,6,8,8,8,20,20,20,8,8,8,8,8,8,8,8,8,8,20,20,20,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,18,18,6,6,6,6,8,8,8,8,8,8,8,8,8,6,8,8,8,8,6,8,8,8,8,8,8,8,8,8,8,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,18,18,18,18,18,6,6,6,8,8,8,8,8,8,8,8,6,6,6,6,8,8,8,8,6,8,8,8,8,8,8,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,6,18,18,18,18,18,18,6,6,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,8,8,8,8,8,6,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,6,6,18,18,18,18,18,18,18,6,6,6,6,8,8,8,8,8,8,8,6,8,8,8,8,8,8,8,6,8,8,8,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,6,6,6,6,18,18,18,18,18,18,18,18,6,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,6,6,6,6,6,6,18,18,18,18,18,18,18,6,6,6,6,6,8,8,8,8,8,6,8,8,6,8,8,8,8,8,8,20,20,20,8,8,8,8,8,8,8,8,8,8,8,6,6,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,8,8,8,6,6,6,6,6,18,18,18,18,18,18,18,18,18,6,6,6,8,8,8,8,8,8,8,8,8,6,8,8,8,8,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,8,8,8,8,6,6,6,6,6,6,6,6,18,18,18,18,18,18,18,6,6,6,6,8,8,8,8,8,8,8,8,8,8,8,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,8,8,8,8,8,8,8,6,6,6,6,6,6,6,18,18,18,18,18,18,18,18,6,6,8,8,6,8,6,8,8,8,8,8,20,20,20,20,6,6,6,6,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,6,8,8,8,8,8,8,6,6,6,6,6,6,6,6,18,18,18,18,18,18,6,6,6,8,8,8,8,6,8,8,8,8,20,20,20,18,18,6,6,6,6,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,8,8,6,8,8,8,8,8,6,6,6,6,6,6,18,18,18,18,18,18,6,6,8,8,8,8,8,8,6,8,8,20,20,20,18,18,18,6,6,6,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,6,6,6,8,18,18,18,18,6,6,8,8,8,6,8,8,8,8,8,8,20,20,20,18,18,6,6,6,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,6,8,8,20,20,8,8,8,8,8,6,6,8,8,8,8,8,8,8,8,11,18,18,18,18,6,6,8,8,8,8,8,8,6,8,8,20,20,20,18,18,6,6,6,6,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,20,20,8,8,8,8,8,6,6,6,8,8,8,8,6,8,8,11,18,18,18,18,6,6,8,6,8,8,6,8,8,8,8,20,20,20,18,18,6,6,6,6,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,8,8,8,8,8,8,8,6,6,6,8,8,8,8,8,8,11,11,18,18,18,6,6,8,8,8,8,8,8,8,8,8,20,20,20,18,18,6,6,6,6,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,8,8,8,8,8,8,8,8,6,6,8,8,8,8,8,8,8,8,18,18,18,18,6,6,8,6,8,6,8,6,8,20,20,20,20,18,18,6,6,6,6,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,6,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,8,8,18,18,18,6,6,8,8,8,8,8,8,8,20,20,20,20,18,18,6,6,6,6,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,6,6,6,8,8,8,20,20,20,8,8,8,8,8,8,8,20,20,20,8,8,18,18,18,6,6,8,8,6,8,6,8,8,20,20,20,20,18,18,6,6,6,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,6,6,6,8,8,8,20,20,20,20,20,8,8,8,8,8,8,20,20,8,8,8,8,18,18,18,6,6,8,8,8,8,8,8,20,20,20,20,18,18,6,6,6,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,8,8,8,20,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,18,18,18,6,6,8,8,8,8,6,8,20,20,20,20,18,18,6,8,8,8,8,6,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,8,8,8,8,8,8,8,20,20,20,20,20,8,8,8,6,6,8,8,8,8,8,8,8,18,18,18,18,6,8,8,8,6,8,8,20,20,20,20,18,18,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,8,8,8,8,8,8,8,8,20,20,20,8,8,8,8,6,6,8,8,8,6,8,8,8,8,18,18,18,6,6,6,8,8,8,8,20,20,20,20,18,18,18,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,18,18,18,18,6,6,8,6,8,8,20,20,20,20,20,18,18,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,18,6,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,8,8,8,8,8,18,18,18,18,6,6,8,8,6,8,8,20,20,20,20,18,18,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,18,6,6,6,8,8,8,8,8,6,8,8,8,8,6,6,8,8,8,8,20,20,20,8,8,8,8,18,18,18,18,6,6,8,8,8,8,8,8,20,20,20,18,18,18,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,18,18,6,6,6,8,8,8,8,6,8,8,8,6,6,6,8,8,8,8,8,20,20,8,8,6,8,18,18,18,18,6,6,8,6,8,8,8,8,20,20,20,20,18,18,18,8,8,6,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,18,18,6,6,6,6,8,8,8,8,8,8,8,6,6,8,8,8,6,8,8,8,8,8,8,8,8,6,18,18,18,6,6,8,8,8,8,8,8,8,20,20,20,20,18,18,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,18,18,18,11,6,6,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,18,18,18,6,6,8,8,8,8,8,8,8,20,20,20,20,18,18,18,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,18,18,18,18,11,11,6,6,6,6,6,8,8,8,8,8,8,8,8,8,8,8,6,6,6,6,8,6,18,18,18,6,6,8,8,8,8,8,8,8,8,20,20,20,20,18,18,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,18,18,18,18,18,18,6,6,6,6,6,6,6,6,6,6,6,8,8,8,8,6,6,18,18,6,8,6,18,18,6,6,8,8,8,8,8,8,8,8,8,20,20,20,20,18,18,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,20,20,18,18,18,18,18,18,18,6,6,6,6,6,6,6,6,6,6,6,6,6,18,18,6,6,8,6,18,18,6,6,8,8,8,8,8,8,8,8,8,20,20,20,20,18,18,18,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,20,20,20,18,18,18,18,18,18,18,18,18,6,6,6,6,6,6,6,18,18,18,18,6,6,8,8,6,18,18,6,6,6,6,6,8,8,8,8,8,8,8,20,20,20,20,18,18,6,6,6,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,20,20,20,20,6,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,6,6,6,8,8,8,6,18,18,18,6,6,6,6,6,8,8,8,8,8,8,20,20,20,20,18,18,6,6,6,6,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,20,20,20,8,6,6,18,18,18,18,18,18,18,18,18,18,18,18,18,6,6,6,8,8,8,8,8,6,6,18,18,18,6,6,6,6,6,8,8,8,8,8,8,20,20,20,18,18,18,6,6,6,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,20,20,8,8,8,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,8,8,8,8,8,6,18,18,18,18,6,6,6,6,8,8,8,8,8,8,20,20,20,20,18,18,6,6,6,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,6,18,18,18,18,6,6,6,6,8,8,8,8,8,8,20,20,20,20,18,18,6,6,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,6,8,8,8,8,8,8,8,8,8,8,8,6,8,8,8,8,8,20,20,20,8,8,6,18,18,18,18,18,6,6,6,6,6,18,18,8,8,20,20,20,20,18,18,18,6,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,8,6,6,6,18,18,18,18,18,18,18,18,18,18,8,8,20,20,20,20,20,18,18,6,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,20,20,8,8,6,6,6,18,18,18,18,18,18,8,8,8,8,8,20,20,20,20,18,18,18,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,8,20,20,20,20,18,18,18,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,8,6,8,8,8,8,8,6,8,8,8,8,8,6,8,8,8,20,20,20,20,20,8,8,8,8,8,6,8,8,8,8,8,8,8,6,6,6,8,8,20,20,20,20,18,18,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,8,8,20,20,20,20,18,18,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,8,8,8,8,8,6,6,6,6,6,6,8,8,8,8,8,8,8,8,8,8,8,20,20,8,8,6,8,8,8,8,8,8,6,6,6,6,8,8,8,8,8,8,8,20,20,20,20,18,18,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,8,8,8,6,6,6,6,6,6,6,8,8,8,8,8,8,8,8,8,8,20,20,20,8,8,8,8,8,8,8,8,6,6,6,6,6,6,8,8,8,8,8,8,20,20,20,20,18,18,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,6,6,6,6,6,6,6,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,8,6,8,8,8,8,6,8,8,6,6,6,6,6,6,8,8,8,6,8,8,20,20,20,20,18,18,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,6,6,6,6,6,6,8,8,8,8,6,8,8,8,8,8,8,8,8,8,20,20,8,8,8,8,8,8,8,8,8,6,6,6,6,6,6,8,8,8,8,8,8,20,20,20,20,8,18,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,6,6,6,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,8,8,8,6,8,8,8,8,8,6,6,6,6,8,8,8,8,8,8,8,20,20,20,20,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,6,6,8,8,8,8,8,8,6,6,8,8,8,8,8,8,8,8,8,8,8,20,20,8,8,8,8,8,8,8,8,8,11,8,8,8,8,8,8,8,8,8,8,20,20,20,20,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,6,8,8,8,8,8,8,6,6,6,6,8,8,8,8,8,6,8,8,8,8,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,8,8,20,20,20,20,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,6,8,8,8,8,8,6,6,6,6,6,6,8,8,8,8,8,8,8,8,8,8,20,20,8,8,8,8,8,6,11,11,8,8,8,6,8,8,8,8,8,8,8,20,20,20,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,6,8,8,8,8,8,6,6,6,6,6,6,8,8,8,8,8,8,8,8,8,8,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,11,11,8,8,8,20,20,20,20,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,8,8,8,8,8,8,8,6,6,6,6,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,8,8,8,8,8,8,11,8,8,8,8,8,8,8,8,20,20,20,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,6,8,8,8,8,8,8,8,8,6,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,8,8,8,6,8,8,8,8,8,8,8,8,8,20,20,20,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,20,8,8,8,8,8,8,8,6,8,8,20,20,20,20,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,8,8,8,8,8,8,8,8,20,20,20,20,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,8,8,8,8,8,8,8,20,20,20,20,8,8,8,8,8,8,8,20,20,20,20,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,8,8,8,8,8,20,20,20,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,8,8,8,8,8,8,8,6,8,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,8,20,20,20,20,20,20,20,20,20,20,20,20,8,8,8,6,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,20,20,20,20,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,8,6,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,20,20,20,8,8,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,8,8,8,8,8,20,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,20,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,20,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,20,8,8,8,8,8,8,6,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,20,20,20,20,20,20,8,8,8,8,8,8,8,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,20,20,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,20,20,20,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,20,20,20,20,20,20,20,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, +12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12 + + + diff --git a/resource/maps/procedural/.keep b/resource/maps/procedural/.keep new file mode 100644 index 00000000..e69de29b diff --git a/resource/maps/testtileset.tsx b/resource/maps/testtileset.tsx new file mode 100644 index 00000000..0e943072 --- /dev/null +++ b/resource/maps/testtileset.tsx @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resource/maps/tiles.tsx b/resource/maps/tiles.tsx new file mode 100644 index 00000000..7bb43199 --- /dev/null +++ b/resource/maps/tiles.tsx @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/resource/maps/tileset.tsx b/resource/maps/tileset.tsx new file mode 100644 index 00000000..39f08512 --- /dev/null +++ b/resource/maps/tileset.tsx @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/setup/__init__.py b/scripts/setup/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/scripts/setup/requirements.txt b/scripts/setup/requirements.txt new file mode 100644 index 00000000..3747f32e --- /dev/null +++ b/scripts/setup/requirements.txt @@ -0,0 +1,12 @@ +autobahn==18.12.1 +cloudpickle==0.5.2 +matplotlib==2.1.2 +noise==1.2.2 +numpy==1.16.0 +opensimplex==0.2 +PyTMX==3.21.5 +ray==0.6.2 +redis==3.1.0 +torch==0.4.0 +torchvision==0.2.0 +Twisted==18.9.0 diff --git a/scripts/setup/setup.sh b/scripts/setup/setup.sh new file mode 100755 index 00000000..4d4cbeda --- /dev/null +++ b/scripts/setup/setup.sh @@ -0,0 +1,26 @@ +#Pytorch +pip install http://download.pytorch.org/whl/cu90/torch-0.4.0-cp36-cp36m-linux_x86_64.whl +pip install torchvision + +#Ray -- will segfault in .backward with wrong version +#pip uninstall -y ray +#pip install -U https://s3-us-west-2.amazonaws.com/ray-wheels/68b11c8251dc41e79dc9dc4b1da31630708a6bd8/ray-0.4.0-cp36-cp36m-manylinux1_x86_64.whl + +#Ray +pip install ray +pip install setproctitle +pip install service_identity + +#Basics +pip install numpy +pip install scipy +pip install matplotlib + + +#Tiled map loader +pip install pytmx + +#jsonpickle +pip install jsonpickle +pip install opensimplex +pip install twisted diff --git a/setup.py b/setup.py new file mode 100644 index 00000000..48cfc66f --- /dev/null +++ b/setup.py @@ -0,0 +1,6 @@ +#This fails on some systems +#import os +#os.system('scripts/setup/setup.sh') +#from scripts.setup import terrain + +import terrain diff --git a/terrain.py b/terrain.py new file mode 100644 index 00000000..55caa8e4 --- /dev/null +++ b/terrain.py @@ -0,0 +1,148 @@ +from pdb import set_trace as T +from opensimplex import OpenSimplex +gen = OpenSimplex() + +from forge.blade.lib import enums +from matplotlib import pyplot as plt +from scipy.misc import imread, imsave +from shutil import copyfile +from copy import deepcopy +import numpy as np +import os + +template_tiled = """ + + + + + {2} + + +""" + +def saveTiled(dat, path): + """"Saved a map into into a tiled compatiable file given a save_path, + width and hieght of the map, and 2D numpy array specifiying enums for the array""" + height, width = dat.shape + dat = str(dat.ravel().tolist()) + dat = dat.strip('[').strip(']') + with open(path + 'map.tmx', "w") as f: + f.write(template_tiled.format(width, height, dat)) + +#Bounds checker +def inBounds(r, c, shape, border=0): + R, C = shape + return ( + r > border and + c > border and + r < R - border - 1 and + c < C - border - 1 + ) + +def noise(nx, ny): + # Rescale from -1.0:+1.0 to 0.0:1.0 + return gen.noise2d(nx, ny) / 2.0 + 0.5 + +def sharp(nx, ny): + return 2 * (0.5 - abs(0.5 - noise(nx, ny))); + +def perlin(nx, ny, octaves, scale=1): + val = 0 + for mag, freq in octaves: + val += mag * noise(scale*freq*nx, scale*freq*ny) + return + +def ridge(nx, ny, octaves, scale=1): + val = [] + for idx, octave in enumerate(octaves): + mag, freq = octave + v = mag*sharp(scale*freq*nx, scale*freq*ny) + if idx > 0: + v *= sum(val) + val.append(v) + return sum(val) + +def expParams(n): + return [(0.5**i, 2**i) for i in range(n)] + +def norm(x, X): + return x/X - 0.5 + +def grid(X, Y, n=8, scale=1, seed=0): + terrain = np.zeros((Y, X)) + for y in range(Y): + for x in range(X): + octaves = expParams(n) + nx, ny = norm(x, X), norm(y, Y) + terrain[x, y] = ridge(seed+nx, seed+ny, octaves, scale) + return terrain / np.max(terrain) + +def textures(): + lookup = {} + for mat in enums.Material: + mat = mat.value + tex = imread( + 'resource/assets/tiles/' + mat.tex + '.png') + key = mat.tex + mat.tex = tex[:, :, :3][::4, ::4] + lookup[key] = mat + return lookup + +def tile(val, tex): + if val == 0: + return tex['lava'] + elif val < 0.3: + return tex['water'] + elif val < 0.57: + return tex['grass'] + elif val < 0.715: + return tex['forest'] + else: + return tex['stone'] + +def material(terrain, tex, X, Y, border=9): + terrain = deepcopy(terrain).astype(object) + for y in range(Y): + for x in range(X): + if not inBounds(y, x, (Y, X), border-1): + terrain[y, x] = tex['lava'] + elif not inBounds(y, x, (Y, X), border): + terrain[y, x] = tex['grass'] + else: + val = float(terrain[y, x]) + terrain[y, x] = tile(val, tex) + return terrain + +def render(mats, path): + images = [[e.tex for e in l] for l in mats] + image = np.vstack([np.hstack(e) for e in images]) + imsave(path, image) + +def index(mats, path): + inds = np.array([[e.index+1 for e in l] for l in mats]) + saveTiled(inds, path) + +def fractal(terrain, path): + frac = (256*terrain).astype(np.uint8) + imsave(path, terrain) + +nMaps, sz = 200, 64 + 16 +#nMaps, sz = 1, 512 + 16 +seeds = np.linspace(0, 2**32, nMaps) +scale = int(sz / 5) +root = 'resource/maps/' +tex = textures() + +for i, seed in enumerate(seeds): + print('Generating map ' + str(i)) + path = root + 'procedural/map' + str(i) + '/' + try: + os.mkdir(path) + except: + pass + terrain = grid(sz, sz, scale=scale, seed=seed) + tiles = material(terrain, tex, sz, sz) + fractal(terrain, path+'fractal.png') + render(tiles, path+'map.png') + index(tiles, path) +