PK!uhshellcraft/__init__.py# -*- coding: utf-8 -*- PK!H:fL L shellcraft/_cli_impl.py# -*- coding: utf-8 -*- """CLI implementations.""" from shellcraft.grammar import Grammar, VERBS from shellcraft._colors import Color, Gradient import click from click.termui import get_terminal_size import time import re import sys import textwrap from itertools import zip_longest RESOURCE_COLORS = { "clay": Color.yellow, "ore": Color.green, "command": Color.grey, "craft": Color.purple, "research": Color.blue, "automata": Color.pink } def handle_exception(e): """Echo error message and shut down. Args: e: Exception """ echo(str(e), err=True) def alen(s): """Length of a string without ANSI characters.""" return len(s) - len("".join(re.findall(r"((?:\x1b|\033)\[[\d:;]+m)", s))) def grid_echo(*cols): """Align columns into a grid and echo. Args: cols: str - columns to print Returns: int - max height """ cols = [box(col, join=False) for col in cols] col_lengths = [max(map(alen, col)) for col in cols] line_fmt = "{:{}}" * len(cols) for parts in zip_longest(*cols, fillvalue=""): values = [j for i in zip(parts, col_lengths) for j in i] line = line_fmt.format(*values) click.echo(line) click.echo() return max(map(len, cols)) def box(s, join=True): """Draw a box around a string. Args: s: str join: bool - if true, return a string. Else, return list of lines. """ lines = s.splitlines() w = max(map(alen, lines)) result = ["╭" + "─" * w + "╮"] result += [f"│{line:{w}}│" for line in lines] result += ["╰" + "─" * w + "╯"] if join: return "\n".join(result) return result def draw_world(world, automaton, w, h): def _field(**resources): clay = resources.get('clay', 0) ore = resources.get('ore', 0) elevation = resources.get('elevation', 0) if ore: return Gradient.green('█', ore) elif clay: return Gradient.yellow('█', clay) else: return Gradient.dark("█", elevation) result = [] x, y = automaton.x, automaton.y for py in range(y - h // 2, y + h // 2 + 1): row = '' for px in range(x - w // 2, x + w // 2 + 1): if px == x and py == y: row += RESOURCE_COLORS['automata']("▲▶▼◀︎"[automaton.direction]) else: row += _field(**world.get_resources(px, py)) result.append(row) return "\n".join(result) def draw_automaton_state(automaton): s = "" for line in automaton.name._cells: for cell in line: s += click.style(cell.symbol.strip() or "·", bg=[None, 'yellow', 'red'][cell.state]) s += "\n" return s def animate_automaton(automaton, world): while True: automaton.step() a = draw_automaton_state(automaton) w = draw_world(world, automaton, 15, 9) height = grid_echo(a, w) time.sleep(1) click.echo(f"\x1b[{height + 2}A") def echo_alerts(game): """Display all alerts that are currently queued up.""" for m in game._messages: echo(m, use_cursor=True) game._messages = [] def _color_in(match): s = match.group(0) color = Color.white if s.startswith("$"): color = RESOURCE_COLORS['craft'] elif s.startswith("%"): color = RESOURCE_COLORS['research'] elif s.startswith("`"): color = RESOURCE_COLORS['command'] elif s.startswith("*"): for res, col in RESOURCE_COLORS.items(): if res in s: color = col else: color = Color.grey return color(s.strip("$*%`")) def _format_str(s): return re.sub(r'(([\$\*%`])[{};:.a-z0-9_\- \n]+(\2))', _color_in, s) def _unformat_str(s): return re.sub(r'(([\$\*%`])([{};:.a-z0-9_\- ]+)(\2))', r"\3", s) def _format_cost(cost): return ", ".join(f"*{v} {k}*" for k, v in cost.items()) def ask(msg): """Show a confirmation prompt.""" return click.confirm("❯ " + _format_str(msg)) def echo(s, *vals, **kwargs): """Echo a string with colours. Args: err (bool): If True, print to stderr use_cursor (bool): If True, use interaction cursor """ if vals: s = s.format(*vals) err = kwargs.pop("err", False) cont = kwargs.pop("cont", False) use_cursor = kwargs.pop("use_cursor", False) grammar_match = re.search("^~([a-zA-Z0-9_]+)~ *", s) if grammar_match: grammar_name = grammar_match.group(1) s = s[grammar_match.end():] grammar = Grammar.grammars[grammar_name] s = grammar.generate(s) if use_cursor: s = "❯ " + s term_width, _ = get_terminal_size() result = "" for line in s.splitlines(): if not line.startswith(">"): for l in textwrap.wrap(line, width=min(80, term_width - 2)): result += l + "\n" result += "\n" else: # Format letter line = line.replace("> > ", "\n") line = line.replace("> ", "") w = min(60, term_width - 12) result += Color.paper(" ╭┄┬┄" + "┄" * w + "┄╮\n") result += Color.paper(" ╰┄┤ " + " " * w + " ┊\n") for paragraph in filter(bool, line.splitlines()): for l in textwrap.wrap(paragraph, width=w, replace_whitespace=False): result += Color.paper(" ┊ ") + Color.ink(l) result += " " * (w - len(_unformat_str(l))) + Color.paper(" ┊\n") result += Color.paper(" ┊ " + " " * w + " ┊\n") result += Color.paper(" ┊ ╭" + "┄" * w + "┴┄╮\n") result += Color.paper(" ╰┄┴" + "┄" * w + "┄┄╯\n") result = result.rstrip("\n") # if not use_cursor: # result += "\n" if err: click.echo(Color.red(_unformat_str(result)), err=True) if not cont: sys.exit(1) else: click.echo(_format_str(result)) class Action: """Represent an action that takes some time to complete.""" def __init__(self, action, target, duration): """Set up the new action. Args: action (str): name of the action duration (float): duration of the action in seconds color (str): color representing the action if progress bar is used. """ self.duration = duration self.color = RESOURCE_COLORS[target] if action == "mine" else RESOURCE_COLORS[action] self.dark_color = self.color.mix(Color.dark, .7) target_str = f"*{target}*" if action == 'mine' else target self.action = _format_str(f"{VERBS[action]} {target_str}".capitalize()) self.elapsed = 0. def _eta(self): """Friendly formatted ETA.""" t = self.duration - self.elapsed if t < 1: return f"{t:.2f}s" elif t < 60: return f"{t:.0f}s" elif t < 3600: return f"{t // 60:.0f}m {t % 60:.0f}s" else: return f"{t // 3600:.0f}h {(t % 3600) / 60:.0f}m" def draw(self): """Echo the current progress bar.""" term_width, _ = get_terminal_size() bar_width = term_width - len(self.action) - 10 blocks = min(bar_width, int(self.elapsed / self.duration * bar_width)) remaining = bar_width - blocks info = self._eta() bar = "\r{} {}{} {:<18}".format( self.action, self.color('█' * blocks), self.dark_color('░' * remaining), info ) click.echo(bar, nl=False) def do(self, skip=False): """Start the action.""" if skip: return term_width, _ = get_terminal_size() blocks = (term_width - len(self.action) - 20.0) delta = min(1, self.duration / blocks) if self.duration - self.elapsed <= 2: delta = min(delta, 0.85) while self.elapsed < self.duration: self.draw() time.sleep(delta) self.elapsed += delta term_width, _ = get_terminal_size() click.echo("\r" + " " * (term_width - 1) + "\r", nl=False) PK!09 shellcraft/_colors.py#!/usr/bin/env python # encoding: utf-8 """Module documentation.""" __author__ = 'Manuel Ebert' __email__ = 'manuel@1450.me' class Color(object): """Truecolor.""" def __init__(self, r=None, g=None, b=None, underline=False): """Initialise the color. Examples: >>> Color('F6BA4C') >>> Color(264, 186, 76) """ if isinstance(r, str): self.rgb = int(r[:2], 16), int(r[2:4], 16), int(r[4:], 16) elif r and g and b: self.rgb = int(r), int(g), int(b) self.ansi256 = self._to_256(self.rgb) self.underline = "\033[31;4m" if underline else "" def _to_256(self, rgb): def _round(i): if i < 75: return 0 return int((i - 75) / 40) + 1 r, g, b = map(_round, rgb) return 16 + r * 36 + g * 6 + b @property def ansi(self): """Generate ANSI escape sequence for color.""" return f"\033[38;5;{self.ansi256}m" @property def truecolor(self): """Generate True Color ANSI escape sequence for color.""" return "\x1b[38;2;{};{};{}m".format(*self.rgb) @property def clear(self): """Generate ANSI escape sequence for unsetting color.""" return "\x1b[0m" def __call__(self, text): """Format a text with the given color.""" return self.underline + self.ansi + text + self.clear def mix(self, other, ratio): """Return an RGB blend between this and another color. Args: other (color) ratio (float): Ratio between 0 (100% this) and 1 (100% other) """ ratio = min(1, max(ratio, 0)) return Color(*[ (1 - ratio) * s + ratio * o for s, o in zip(self.rgb, other.rgb) ]) class Gradient(object): """Represents a gradient for colours.""" def __init__(self, start, end): self.start = start self.end = end def _generate(self, ratio=.5): """Generate a color given a value. Args: pos (float): Value in [0, 1] """ return self.start.mix(self.end, ratio) def __call__(self, text, ratio=.5): """Format a text with the given color.""" mix = self._generate(ratio) return mix.ansi + text + mix.clear Color.yellow = Color('F5C065') Color.green = Color('58BD86') Color.blue = Color('798BDF') Color.purple = Color('a35bb8') Color.red = Color('E8685D') Color.pink = Color('F25C80') Color.grey = Color('8390ab', underline=True) Color.paper = Color('aba483') Color.ink = Color('6f6b55') Color.dark = Color('2B333F') Color.white = Color('ffffff') Gradient.yellow = Gradient(Color.dark, Color.yellow) Gradient.green = Gradient(Color.dark, Color.green) Gradient.dark = Gradient(Color.dark, Color('3D495A')) PK!]b shellcraft/automata.py# -*- coding: utf-8 -*- """Automaton Class.""" from shellcraft.epithets import Name from random import seed, randint, paretovariate, random import math class World(object): def __init__(self, game, width, height): self.game = game self.width, self.height = width, height self.cache = {} # Distribute resources hills = width * height // 50 clay_deposits = width * height // 150 ore_deposits = width * height // 250 self.deposits = { 'ore': [(randint(0, width), randint(0, height), random() * math.pi / 2) for _ in range(clay_deposits)], 'elevation': [(randint(0, width), randint(0, height), random() * math.pi / 2) for _ in range(hills)], 'clay': [(randint(0, width), randint(0, height)) for _ in range(ore_deposits)] } def _resource_pr(self, resource, x, y, distance, deposit): seed("{}.{}".format(x, y)) if resource == 'clay': v = paretovariate(2) / (distance + 1) return v if v > .2 else 0 if resource == 'elevation': return paretovariate(4) / (distance + 1) if resource == 'ore': dx, dy, dr = deposit angle = math.atan2(dy - y, dx - x) % math.pi diff = .5 / (angle - dr + .5) v = paretovariate(2) / (distance + 1) * diff return v if v > .4 else 0 def _nearest_deposit(self, resource, x, y): bd = 9999999 best_deposit = None for deposit in self.deposits[resource]: dx, dy = deposit[:2] absx, absy = abs(dx - x), abs(dy - y) absx = min(absx, self.width - absx) absy = min(absy, self.height - absy) d = math.sqrt(absx ** 3 + absy ** 3) if d < bd: bd = d best_deposit = deposit return bd, best_deposit def _get_resource(self, resource, x, y): """Returns the amount of clay at a certain location.""" x, y = x % self.width, y % self.height if (x, y, resource) in self.cache: return self.cache[(x, y, resource)] distance, deposit = self._nearest_deposit(resource, x, y) self.cache[(x, y, resource)] = self._resource_pr(resource, x, y, distance, deposit) return self.cache[(x, y, resource)] def get_resources(self, x, y): x, y = x % self.width, y % self.height return { 'clay': self._get_resource('clay', x, y), 'ore': self._get_resource('ore', x, y), 'elevation': self._get_resource('elevation', x, y), } class Automaton(object): def __init__(self, name): self.name = Name(name) self.direction = 0 # Up self.x = 0 self.y = 0 def move(self): """Move one step into the current direction.""" delta = [(-1, 0), (0, 1), (1, 0), (0, -1)][self.direction] self.y += delta[0] self.x += delta[1] def turn_right(self): """Change orientation.""" self.direction = (self.direction + 1) % 4 def turn_left(self): """Change orientation.""" self.direction = (self.direction - 1) % 4 def step(self): for effect in self.name.step(): getattr(self, effect)() PK!١kshellcraft/cli.py# -*- coding: utf-8 -*- """Basic CLI for ShellCraft.""" import click from shellcraft.shellcraft import Game from shellcraft.exceptions import ResourceNotAvailable from shellcraft._cli_impl import echo, Action, VERBS, echo_alerts, _format_cost, animate_automaton, handle_exception import pkg_resources import datetime import os import sys import platform import traceback click.disable_unicode_literals_warning = True APP_NAME = 'ShellCraft' PYTHON_VERSION = platform.python_version() APP_VERSION = pkg_resources.get_distribution('shellcraft').version GAME = None def get_game(path=None): global GAME if GAME: return GAME path = path or os.path.join(click.get_app_dir(APP_NAME), 'config.json') GAME = Game.load(path) if os.path.exists(path) else Game.create(path) return GAME def action_step(callback, game): """Wrapper around actions.""" def inner(**kwargs): # Do the action try: callback(**kwargs) game.complete_missions() echo_alerts(game) game.tutorial.cont() game.save() except Exception as e: if game.state.debug: traceback.print_exc() else: handle_exception(e) return inner def handle_debug(game): if sys.argv[2] == "off": # Disable hyperlapse game.state.debug = False echo("Debug mode is `off`") elif sys.argv[2] == "on": # Enable hyperlapse game.state.debug = True echo("Debug mode is `on`") elif sys.argv[2] == "trigger": # Trigger an event game.events.trigger(sys.argv[3]) elif sys.argv[2] == "contract": # Trigger an event game.add_mission('trade_proposal') game.save() elif sys.argv[2] == "automata": # Trigger an event from shellcraft.automata import Automaton, World name = "".join(sys.stdin.readlines()) w = World(game, 20, 20) a = Automaton(name) animate_automaton(a, w) echo_alerts(game) game.save() def main(game_path=None): game = get_game(game_path) # Cheat mode, properly hardcoded. if len(sys.argv) > 2 and sys.argv[1] == "debug": handle_debug(game) sys.exit(0) # Remove all commands from the main group that are not enabled in the game yet. if not game.state.debug: cli.commands = {cmd: command for cmd, command in cli.commands.items() if cmd in game.state.commands_enabled} for cmd in ('mine', 'craft', 'research'): if cmd in cli.commands: cli.commands[cmd].callback = action_step(cli.commands[cmd].callback, game) cli() @click.group(invoke_without_command=True, options_metavar='', subcommand_metavar='') @click.option("--version", is_flag=True, help="Prints the version number and exits.") @click.pass_context def cli(ctx, version): """ShellCraft is a command line based crafting game.""" ctx.obj = game = get_game() if version: click.echo(f"{APP_NAME} {APP_VERSION} (Python {PYTHON_VERSION})") elif ctx.invoked_subcommand is None: if game.state.tutorial_step == 0: game.tutorial.cont() else: echo(ctx.get_help()) @cli.command(options_metavar='') @click.pass_obj def contract(game): game.add_mission('trade_proposal') game.save() @cli.command(options_metavar='') @click.argument("resource", metavar='') @click.pass_obj def mine(game, resource): """Mine a resource.""" if resource not in game.state.resources_enabled: raise ResourceNotAvailable(resource) duration, quantity = game.mine(resource) game.save() action = Action("mine", resource, duration) action.do(skip=game.state.debug) @cli.command(options_metavar='') @click.argument("item", required=False, type=str, metavar='') @click.pass_obj def craft(game, item): """Mine a resource.""" if not item: if not game.workshop.available_items: echo("There's nothing you can craft right now.", err=True) for item in game.workshop.available_items: echo("{} ({}) - {}", item, _format_cost(item.cost), item.description) else: item = game.workshop.get(item) if not item: echo("No such item. Use 'shellcraft craft' to see a list of available items.", err=True) return None if not game.workshop.is_available(item): echo("{} is not available yet.", item, err=True) return None if not game.workshop.can_afford(item): missing_resources = game.workshop._resources_missing_to_craft(item) e = f"Need another {_format_cost(missing_resources)} to craft {item}." echo(e, err=True) difficulty = game.craft(item) game.save() action = Action("craft", item, difficulty) action.do(skip=game.state.debug) @cli.command(options_metavar='') @click.argument("resource_types", nargs=-1, type=str, metavar="") @click.pass_obj def resources(game, resource_types=None): """Show available resources.""" types = resource_types or ("clay", "ore", "energy") for resource in types: if resource in game.state.resources_enabled: echo("*{}: {:.0f}*", resource, game.resources.get(resource)) elif resource_types: echo("*{}* is not available yet.", resource, err=True, cont=True) @cli.command(options_metavar='') @click.pass_obj def inventory(game): """Show owned items and their condition.""" if not game.tools: echo("You don't own any items", err=True) else: for item in game.tools: echo(str(item)) # echo("${}$ ({:.0%})", item.name, item.condition / item.durability) @cli.command(options_metavar='') @click.pass_obj def automata(game): """Show owned automata and their condition.""" if not game.tools: echo("You don't own any automata", err=True) else: for item in game.automata: echo(str(item)) # echo("${}$ ({:.0%})", item.name, item.condition / item.durability) @cli.command(options_metavar='') @click.argument("project", required=False, type=str, metavar="") @click.pass_obj def research(game, project=None): """Show owned items and their condition.""" if not project: for item in game.lab.available_items: echo("{} ({} sec) - {}", item, item.difficulty, item.description) if not game.lab.available_items: echo("There are currently no projects available for research.", err=True) else: # Researching something now if game.is_busy: dt = game.state.action.completion.ToDatetime() - datetime.datetime.now() echo("You're busy {} for another {:.0f} seconds.", VERBS[game.state.action.task], dt.total_seconds(), err=True) project = game.lab.get(project) if not project: echo("No such research project. Use 'shellcraft research' to see a list of available projects.", err=True) if project.name in game.state.research_completed: echo("You've already researched {}.", project, err=True) return None if not game.lab.is_available(project): echo("You can't research {} yet.", project, err=True) return None difficulty = game.research(project) game.save() action = Action("research", project, difficulty) action.do(skip=game.state.debug) @cli.command(options_metavar='') @click.option("--force", is_flag=True, help="Don't question my orders, just execute them!") @click.pass_obj def reset(game, force): """Reset all progress.""" if force or click.confirm("Do you really want to reset the game and start over again?"): Game.create(game.save_file) echo("Tohu wa-bohu.") else: echo("Nevermind then.") @cli.command() @click.pass_obj def tutorial(game): """Print the last step of the tutorial.""" game.tutorial.print_last_step() if __name__ == "__main__": main() PK!**shellcraft/core.py# -*- coding: utf-8 -*- """Core Classes.""" import os import poyo from copy import copy from shellcraft.utils import to_list, to_float from google.protobuf.descriptor import Descriptor from builtins import str RESOURCES = ['clay', 'energy', 'ore'] class ResourceProxy(object): """Proxy for accessing Resource messages in a GameState.""" def __init__(self, field): """Initialise the Proxy. Args: field (shellcraft.game_state_pb2.Resources): Resource message. """ self._resources = field @property def _fields(self): return self._resources.__class__.DESCRIPTOR.fields_by_name.keys() def add(self, resource, value): """Add resource. Args: resource (str) value (float) """ setattr(self._resources, resource, getattr(self._resources, resource, 0) + value) def get(self, resource, default=0): """Get value of resource.""" return getattr(self._resources, resource, default) def multiply(self, resource, factor): """Multiply resource by factor.""" setattr(self._resources, resource, getattr(self._resources, resource, 0) * factor) def __repr__(self): """String representation of resources.""" return str({f: self.get(f) for f in self._fields}) class ItemProxy(object): """Proxy for accessing serializable items.""" def __init__(self, field, factory, filter=None): """Initialise the Proxy. Args: field: Message of 'Repeated' type factory (shellcraft.core.BaseFactory): Factory used to produce the item. """ self._field = field self._factory = factory self._items = [factory.make(pb) for pb in field] self.filter = filter @property def _filtered_items(self): return filter(self.filter, self._items) def __iter__(self): """Iterate over items.""" return self._filtered_items.__iter__() def remove(self, item): """Remove an item.""" self._items.remove(item) self._field.remove(item._pb) def __bool__(self): """True if the proxy contains any items.""" return bool(self._items) @property def is_empty(self): """True if the proxy does not contain any items.""" return not list(self._filtered_items) def __repr__(self): """String representation of item proxy.""" return str(self._filtered_items) def add(self, item): """Add a new item. This is achieved by first adding a Protobuf message to the field, then using the factory to make a new item from a given template, setting all fields on the Protobuf message and attaching it to the newly generated item. Args: item: Item name or Item instance to copy from """ pb = self._field.add() new_item = self._factory.make(item) for field in self._factory.PB_CLASS.DESCRIPTOR.fields_by_name.keys(): if hasattr(new_item, field): if isinstance(self._factory.PB_CLASS.DESCRIPTOR.fields_by_name[field].message_type, Descriptor): getattr(pb, field).CopyFrom(getattr(new_item, field)) else: setattr(pb, field, getattr(new_item, field)) new_item._pb = pb self._items.append(new_item) return new_item class BaseItem(object): """Abstract class for new items.""" _pb = None """This links the item to a serializable Protobuf message that is part of the GameState.""" def __init__(self, name): """Initialise an empty item.""" self.name = name self.difficulty = 0 self.description = "" self.prerequisites = {} self.cost = {} self.effects = {} self.strings = {} @classmethod def from_dict(cls, name, data): """Load an item from dict representation.""" item = cls(name) item.description = data.get("description", "") item.difficulty = data.get("difficulty", 0) item.prerequisites = data.get("prerequisites", {}) item.prerequisites['items'] = to_list(item.prerequisites.get('items')) item.prerequisites['research'] = to_list(item.prerequisites.get('research')) item.prerequisites['triggers'] = to_list(item.prerequisites.get('triggers')) item.cost = data.get("cost", {}) item.strings = data.get("strings", {}) item.effects = data.get("effects", {}) for effect in ('enable_commands', 'enable_items', 'enable_resources', 'events', 'triggers'): item.effects[effect] = to_list(item.effects.get(effect)) return item def __repr__(self): """String representation of the item.""" return self.name def __setattr__(self, key, value): """Override attribute setter for items with attached Protobuf message.""" if key != "_pb" and self._pb and key in self._pb.__class__.DESCRIPTOR.fields_by_name.keys(): setattr(self._pb, key, value) else: self.__dict__[key] = value def __getattr__(self, key): """Override attribute getter for items with attached Protobuf message.""" if key != "_pb" and self._pb and key in self._pb.__class__.DESCRIPTOR.fields_by_name.keys(): return getattr(self._pb, key) raise AttributeError(key) class BaseFactory(object): """Factory pattern to instantiate items.""" FIXTURES = "collection.yaml" ITEM_CLASS = BaseItem PB_CLASS = None def __init__(self, game): """Initialise the Factory. Args: game (shellcraft.shellcraft.Game): Game object. """ with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "data", self.FIXTURES)) as f: contents = f.read() self.all_items = {name: self.ITEM_CLASS.from_dict(name, data) for name, data in poyo.parse_string(contents).items()} self.game = game def get(self, item_name): """Get an item instance by name.""" if isinstance(item_name, BaseItem): return item_name return self.all_items.get(item_name) def make(self, source): """Create a new unique item from a source. The source may be a string, in which case the item is created from the item library defined in a YAML file, or it may be a Protobuf message, which is typically the case when loading a saved game and instantiating serialized items (such as tools or missions), or it may be another Item instance which will be copied. """ if isinstance(source, str): return copy(self.get(source)) elif self.PB_CLASS and isinstance(source, self.PB_CLASS): item = copy(self.get(source.name)) item._pb = source return item else: return copy(source) @property def available_items(self): """Return all items that are currently available.""" return [item for item in self.all_items.values() if self.is_available(item)] def _resources_missing_to_craft(self, item_name): item = self.get(item_name) return {res: int(res_cost - self.game.resources.get(res)) for res, res_cost in item.cost.items() if res_cost - self.game.resources.get(res) > 0} def can_afford(self, item_name): """Return true if we have enough resources to create an item.""" item = self.get(item_name) for resource in RESOURCES: if item.cost.get(resource, 0) > self.game.resources.get(resource): return False return True def apply_effects(self, item_name): """Apply all effects of an item.""" item = self.get(item_name) # Enable commands for command in item.effects.get('enable_commands', []): if command not in self.game.state.commands_enabled: self.game.alert("You unlocked the `{}` command", command) self.game.state.commands_enabled.append(command) # Enable resouces for resources in item.effects.get('enable_resources', []): if resources not in self.game.state.resources_enabled: self.game.alert("You can now mine *{}*.", resources) self.game.state.resources_enabled.append(resources) # Enable items for item_name in item.effects.get('enable_items', []): if item_name not in self.game.state.tools_enabled: self.game.alert("You can now craft ${}$.", item_name) self.game.state.tools_enabled.append(item_name) # Enable research for research in item.effects.get('enable_research', []): if research not in self.game.state.research_enabled: self.game.alert("You can now research @{}@.", research) self.game.state.research_enabled.append(research) # Trigger flags for trigger in item.effects.get('triggers', []): if trigger not in self.game.state.triggers: self.game.state.triggers.append(trigger) # Grant resources for resource in RESOURCES: if resource in item.effects: value = to_float(item.effects[resource]) self.game.resources.add(resource, value) if value > 0: self.game.alert("You found *{} {}*.", value, resource) else: self.game.alert("You lost *{} {}*.", -value, resource) # Change mining difficulty for resource in RESOURCES: change = item.effects.get(f"{resource}_mining_difficulty", None) if change: change = to_float(change) self.game.mining_difficulty.multiply(resource, 1 - change) self.game.alert("*{}* mining difficulty reduced by {:.0%}.", resource, change) # Trigger events self.game.events.trigger(*item.effects.get('events', [])) def is_available(self, item_name): """Return true if the prerequisites for an item are met.""" item = self.get(item_name) if not item: return False for resource in RESOURCES: if self.game.resources.get(resource) < item.prerequisites.get(resource, 0): return False for required_item in item.prerequisites['items']: if not self.game.has_item(required_item): return False for research in item.prerequisites['research']: if research not in self.game.state.research_completed: return False for trigger in item.prerequisites['triggers']: if trigger not in self.game.state.triggers: return False return True PK!N*PFwwshellcraft/data/events.yamlnew_clay_deposit: description: You found a new *clay* deposit. effects: clay_mining_difficulty: random(.2, .5) PK!k呤shellcraft/data/fractions.yamlcrown: description: The Crown influence: 1 kabbalists: description: The Kabbalists influence: 1 union: description: The Sculptor's Union influence: 1.5 PK!e   shellcraft/data/game_state.proto/* Proto schema for the Game State */ syntax = 'proto3'; import "google/protobuf/timestamp.proto"; message Action { string task = 1; string target = 2; google.protobuf.Timestamp completion = 3; } message Fraction { string name = 1; float influence = 2; int32 missions_completed = 3; int32 missions_failed = 4; } message Resources { float clay = 1; float ore = 2; float energy = 3; } message Tool { string name = 1; float condition = 2; } message Mission { string name = 1; int32 demand = 2; int32 reward = 3; string demand_type = 4; string reward_type = 5; int32 due = 6; google.protobuf.Timestamp deadline = 7; NPC writer = 8; int32 reward_factor = 9; } message Stats { float total_game_duration = 1; Resources total_mined = 2; } message NPC { string first = 1; string middle = 2; string last = 3; string title = 4; string nickname = 5; string display = 6; string fraction_name = 7; } message GameState { bool debug = 1; Action action = 2; repeated Tool tools = 3; repeated Mission missions = 4; Resources resources = 5; repeated string tools_enabled = 6; repeated string resources_enabled = 7; repeated string commands_enabled = 8; repeated string research_enabled = 9; repeated string research_completed = 10; repeated string triggers = 11; Resources mining_difficulty = 12; Resources mining_difficulty_increment = 13; float trade_reputation = 14; int32 tutorial_step = 15; Stats stats = 16; repeated Fraction fractions = 17; } PK!|S#shellcraft/data/guild_names.grammar@s -> @_s~title @leader_role -> Chairman | President | Secretary General | Leader @_s -> @people~gen @party | @adj @party of @people | @adj @cause @party | @adj @people~gen @party | @cause @party @adj -> common | parlamentarian | federal | first | free | liberal | modern | national | northern | progressive | revolutionary | social | southern | united @cause -> humanitarian | preservation | conservation | emancipation | independence | royal interests | solidarity | regulation | resistance | unionist @people -> lexicographers | nomenclators | loyalists | iron workers | sculptors | colonists @party -> party | league | movement | union | guild | front | alliance | coalition | legion | order | society PK!Ҕ|shellcraft/data/letter.grammar@address -> @greeting Nomenclator @greeting -> Esteemed | Dear | Dearest | Respected | My dear @salutation -> Sincerely | Yours with esteem | Yours very sincerely | Yours respectfully | Your obedient servant PK! -''shellcraft/data/missions.yamltrade_proposal: description: A Trade Proposal strings: intro: > ~letter~ A letter arrives. > @address , > > We request the shipment of *{demand} {demand_type}* in no more than {due} seconds. > I am delighted to inform you that we will reward your efforts with the payment > of *{reward} {reward_type}* upon completion of this agreement. > > @salutation , > > {writer} ask: You need mine another *{deficit:.0f} {demand_type}* to meet the deadline. Do you agree to this trade contract? agree: The *{demand_type}* will be automatically collected as soon as you have enough. Time is of the essence now! disagree: The trader leaves, disappointed. failed: You failed to deliver the goods. completed: The trader takes *{demand} {demand_type}*, hands you *{reward} {reward_type}* and smiles. trade_proposal: description: A Trade Proposal reward_factor: 0 reward_type: reputation strings: intro: > ~letter~ A letter arrives. > @address , > > We request the shipment of *{demand} {demand_type}* in no more than {due} seconds. > I am delighted to inform you that we will reward your efforts with the payment > of *{reward} {reward_type}* upon completion of this agreement. > > @salutation , > > {writer} ask: You need mine another *{deficit:.0f} {demand_type}* to meet the deadline. Do you agree to this trade contract? agree: The *{demand_type}* will be automatically collected as soon as you have enough. Time is of the essence now! disagree: The trader leaves, disappointed. failed: You failed to deliver the goods. completed: The trader takes *{demand} {demand_type}*, hands you *{reward} {reward_type}* and smiles. PK!˹=SSshellcraft/data/names.grammar@display -> {title} @display_first {last} @display_nickname -> {first} '{nickname}' {last} | {first} 'The {nickname}' {last} | The {nickname} %.2 @display_first -> {first} %4 | {first} {middle} | {first} {middle_initial} %2 | {first_initial}{middle_initial} @name -> @female_name @family | @male_name @family @male_name -> @male | @male @male %.8 | @male @male~initial . | @male~initial . @male~initial . %.3 | @male ' @nickname ' %.1 | @male ' The @nickname ' %.1 @female_name -> @female | @female @female %.8 | @female @female~initial . | @female~initial . @female~initial . %.3 | @female ' @nickname ' %.1 | @male ' The @nickname ' %.1 @nickname -> Banks | Barrage | Basis | Beast | Beauty | Bells | Blade | Block | Bolt | Bones | Boots | Brickface | Bright | Brush | Cable | Hound | Catch | Craven | Design | Frost | Ghost | Wraith | Badger | Shark |Eagle |Hacks | Hollow | Hook | Ice | Impulse | Ink | Mellow | Nemo | Nightowl | Nightingale | Poison | Riddle | Shiny | Stitch | Stranger | Zigzag @family -> Ainsworth | Aldithley | Aldridge | Ambrose | Anvil | Archer | Ashton | Askew | Atkins | Atwater | Atwood | Bacon | Badger | Bagley | Bagstock | Baker | Banks | Barclay | Bardell | Barman | Bartholemew | Barton | Bashford | Batchelor | Batterbee | Beard | Beaumont | Beeching | Beggley | Belcher | Bennett | Betteridge | Bicker | Billinghurst | Bleeze | Blenkinsopp | Blundy | Boodle | Bowles | Bowyer | Bramble | Brattle | Bray | Brewer | Brick | Bridges | Bridges | Brock | Brogley | Bronze | Brownlow | Brownsmith | Bucket | Buckle | Burdon | Burnet | Butcher | Butler | Cadwell | Canning | Cannon | Carton | Castleton | Catchpole | Chambers | Champ | Chandler | Chester | Chickering | Chittenden | Cholmondeley | Chuffey | Claringbold | Cleverly | Cobb | Cogsmith | Cogwright | Compton | Corbyn | Corney | Courtenay | Cox | Crabb | Craft | Cuttle | Dalley | Darknoll | Darleston | Datchery | Davenport | Dawkins | Deal | Deering | Dickinson | Dolphin | Doughtry | Duncke | Eagleden | Elmstone | Emerson | Etherington | Fairbeard | Farrier | Featherstone | Fielding | Fishenden | Fleming | Flint | Fordham | Forge | Forger | Forward | Friend | Fuzzey | Gates | Giffard | Gifford| Godolphin | Goodall | Goodenough | Gooding | Gosling | Granger | Greensmith | Gresley | Gridley | Griffin | Griswold | Gunn | Gushlow | Habsburg | Halvorsen | Hammer | Hammerman | Harriden | Hawk | Hawkes | Hazard | Heaton | Hedersett | Hedgecock | Hellyer | Herndon | Heselarton | Hoadley | Hogbin | Hogwood | Hopperton | Huddleston | Humphries | Hussey | Igglesden | Inchcombe | Jarvest | Jarvis | Jelly | Jolliffe | Kadwell | Keeler | Kettell | Kettle | Kidham | King | Kingsford | Kingsland | Kipps | Kitchingham | Lambkin | Langdale | Langridge | Langstaff | Larkin | Latimer | Leeford | Leggett | Lenville | Letchford | Lever | Lewis | Lewkenor | Lidgett | Likeman | Lisney | Littlechild | Littlefield | Longhurst | Lovegrove | Lucke | Lurcock | Lushington | Lygon | Maitland | Mannerings | Manwaring | Marrable | Marsh | Massey | Maxwell | Maycock | Midgeley | Miller | Mills | Missing | Mockett | Moody | Mortimer | Muggeridge | Mugridge | Napper | Necket | Nethersole | Norris | Nottle | Nunn | Oakley | Oldfield | Osborne | Oswald | Overton | Packer | Palethorpe | Palmer | Pankhurst | Parkes | Parrott | Parson | Parsons | Patching | Payne | Pelham | Pennington | Perch | Philpott | Pinch | Pipe-Wolferstan | Playfoot | Pocket | Popkiss | Porter | Pott | Potter | Powers | Prescott | Price | Prioleau | Radcliff | Ramsbottom | Rayburn | Redsmith | Rexword | Riggs | Rowley | Sackville | Salwey | Sawyer | Scarborough | Sheridan | Silver | Sixsmith | Smith | Smithy | Sparks | Spencer | Stagg | Stanhope | Steele | Sterling | Sterling | Stillingfleet | Stokes | Stonor | Styles | Thatcher | Theobald | Throgmorton | Tinker | Train | Tremaine | Trigg | Turner | Turner | Vyner | Wallace | Walstrand | Ward | Wedge | Weller | Westley | Wheeler | Wheelwright | Whitesmith | Whiting | Wickham | Wilkins | Winch | Windlass | Winterman | Wrayburn | Wyndham | Wyverstone | Yardley | Yorke @female -> Abbie | Abigail | Ada | Adah | Adaline | Adda | Addie | Adela | Adelaide | Adele | Adelia | Adelina | Adeline | Adell | Adella | Adelle | Adline | Agatha | Agnes | Aileen | Aimee | Alberta | Albertha | Albertina | Albertine | Albina | Alda | Alene | Aletha | Alfreda | Alice | Alicia | Alida | Aline | Allene | Allie | Alma | Almeda | Almira | Alpha | Alta | Altha | Althea | Alva | Alvena | Alverta | Alvina | Alyce | Amalia | Amanda | Amber | Amelia | America | Amie | Amy | Ana | Anastasia | Andrea | Angela | Angelina | Angeline | Angie | Anita | Ann | Anna | Annabel | Annabell | Annabelle | Anne | Anner | Annetta | Annette | Annie | Annis | Antoinette | Antonette | Antonia | Ara | Ardella | Arie | Arizona | Arlene | Arlie | Arline | Arrie | Artie | Arvilla | Atha | Audie | Audra | Audrey | Augusta | Augustine | Aurelia | Aurora | Aurore | Ava | Avis | Barbara | Beatrice | Beaulah | Bell | Bella | Belle | Belva | Bennie | Berdie | Berenice | Bernadette | Bernadine | Bernice | Berniece | Berta | Bertha | Bertie | Beryl | Bess | Besse | Bessie | Beth | Bethel | Betsy | Bettie | Betty | Beula | Beulah | Billie | Birdie | Birtha | Birtie | Blanch | Blanche | Bobbie | Bonnie | Bridget | Buena | Bulah | Callie | Camilla | Camille | Carmela | Carmella | Carmen | Carol | Carolina | Caroline | Carolyn | Carrie | Cassie | Catharine | Catherine | Cathrine | Cathryn | Cecelia | Cecil | Cecile | Cecilia | Celeste | Celestine | Celia | Celina | Charity | Charles | Charlie | Charlotte | Cherry | Chloe | Christena | Christie | Christina | Christine | Claire | Clara | Clare | Claribel | Clarice | Clarissa | Claudia | Claudie | Claudine | Clementine | Clemmie | Cleo | Clifford | Clora | Clyde | Concepcion | Concetta | Connie | Constance | Consuelo | Cora | Coral | Corda | Cordelia | Cordia | Cordie | Corene | Corine | Corinne | Cornelia | Corrie | Corrine | Crystal | Cynthia | Dagmar | Daisy | Daphne | Deborah | Delia | Delilah | Dell | Della | Delma | Delpha | Delphia | Delphine | Delta | Dena | Dessie | Dewey | Diana | Dicie | Dillie | Dixie | Docia | Dollie | Dolly | Dolores | Dona | Donie | Donna | Donnie | Dora | Dorcas | Doris | Dorothea | Dorothy | Doshie | Dottie | Dovie | Drucilla | Dulcie | Easter | Ebba | Eda | Eddie | Edith | Edna | Edwina | Edyth | Edythe | Effie | Eileen | Elaine | Elda | Eldora | Eleanor | Eleanora | Eleanore | Electa | Elena | Elenora | Elfrieda | Elinor | Elisa | Elisabeth | Elise | Eliza | Elizabeth | Elizebeth | Ella | Ellen | Ellie | Elma | Elmira | Elna | Elnora | Eloise | Elsa | Elsie | Elta | Elva | Elvera | Elvie | Elvina | Elvira | Emelia | Emeline | Emilia | Emilie | Emily | Emma | Emmer | Emmie | Enid | Enola | Era | Erie | Erma | Erna | Ernestine | Essie | Esta | Estell | Estella | Estelle | Ester | Esther | Etha | Ethel | Ethelyn | Ethyl | Etta | Ettie | Eudora | Eugenia | Eugenie | Eula | Eulah | Eulalia | Eulalie | Euna | Eunice | Eura | Eva | Evalyn | Evangeline | Eve | Evelina | Evelyn | Evie | Exie | Fae | Fairy | Faith | Fannie | Fanny | Fay | Faye | Felicia | Fern | Ferne | Filomena | Fleta | Flo | Flora | Florance | Florence | Florida | Florine | Florrie | Flossie | Floy | Frances | Francis | Francisca | Frank | Frankie | Fred | Freda | Freddie | Freeda | Freida | Frieda | Frona | Fronie | Gail | Garnet | Garnett | Gay | Gena | Gene | Geneva | Genevieve | Genie | George | Georgia | Georgiana | Georgianna | Georgie | Georgina | Geraldine | Gertie | Gertrude | Gladys | Glenn | Glenna | Glennie | Golda | Golden | Goldia | Goldie | Grace | Gracie | Grayce | Greta | Gretchen | Guadalupe | Gussie | Gusta | Gwendolyn | Hallie | Hanna | Hannah | Harriet | Harriett | Harriette | Hassie | Hattie | Hazel | Hazle | Hedwig | Helen | Helena | Helene | Helga | Hellen | Helma | Henrietta | Henriette | Henry | Hermina | Hermine | Hertha | Hessie | Hester | Hettie | Hilda | Hildegard | Hildegarde | Hildred | Hildur | Hilma | Honora | Hope | Hortense | Hulda | Huldah | Icie | Icy | Ida | Idell | Idella | Ila | Ima | Imogene | Ina | India | Ines | Inez | Inga | Iola | Iona | Ione | Ira | Irene | Iris | Irma | Isa | Isabel | Isabell | Isabella | Isabelle | Iva | Ivah | Ivy | Izetta | Izora | James | Jane | Janet | Janette | Janie | Jannie | Jean | Jeanette | Jeanie | Jeanne | Jeannette | Jennie | Jenny | Jesse | Jessica | Jessie | Jettie | Jewel | Jewell | Jimmie | Jo | Joan | Joanna | Joe | Johanna | John | Johnie | Johnnie | Josefa | Joseph | Josephine | Josie | Jossie | Joy | Joyce | Juana | Juanita | Judith | Julia | Julie | Juliet | Juliette | June | Justine | Kate | Katharine | Katherine | Katheryn | Kathleen | Kathrine | Kathryn | Kathryne | Katie | Kattie | Katy | Kay | Kittie | Kitty | Kizzie | Lacy | Lala | Lassie | Laura | Lauretta | Lavada | Laverne | Lavina | Lavinia | Lea | Leah | Leanna | Leatha | Leda | Lee | Leila | Lela | Lelah | Lelia | Lella | Lena | Lenna | Lennie | Lenora | Lenore | Leo | Leola | Leona | Leone | Leonie | Leonora | Leonore | Leontine | Leora | Leota | Lera | Leslie | Lessie | Leta | Letha | Letitia | Lettie | Lexie | Libbie | Libby | Lida | Liddie | Lila | Lilah | Lilian | Lilla | Lillian | Lillie | Lilly | Lily | Lina | Linda | Linnie | Lissie | Liza | Lizzie | Lois | Lola | Lollie | Loma | Lona | Lonie | Lonnie | Lora | Loraine | Lorena | Lorene | Loretta | Loretto | Lorna | Lorraine | Lottie | Lou | Louella | Louie | Louisa | Louise | Louvenia | Lovie | Lovina | Lucia | Lucie | Lucile | Lucille | Lucinda | Lucretia | Lucy | Ludie | Lue | Luella | Luetta | Luisa | Lula | Lular | Lulu | Luna | Lura | Lutie | Luvenia | Lyda | Lydia | Lyla | Mabel | Mabell | Mabelle | Mable | Macie | Madaline | Madeleine | Madeline | Madelyn | Madge | Madie | Mae | Magdalen | Magdalena | Magdalene | Maggie | Magnolia | Mahala | Malinda | Malissa | Mallie | Malvina | Mamie | Mammie | Manda | Mandy | Manila | Manuela | Marcella | Marcia | Margaret | Margaretta | Margarette | Margarita | Margery | Margie | Margret | Marguerite | Maria | Mariah | Marian | Marie | Marietta | Marion | Marjorie | Marjory | Martha | Martina | Marvel | Mary | Mathilda | Mathilde | Matie | Matilda | Mattie | Maud | Maude | Maudie | Maurine | Maxie | Maxine | May | Maybell | Maybelle | Maye | Mayme | Maymie | Mazie | Meda | Melba | Melinda | Melissa | Mellie | Melva | Melvina | Mercedes | Merle | Mertie | Meta | Metta | Mettie | Mildred | Millicent | Millie | Mina | Minerva | Minna | Minnie | Minta | Mintie | Miriam | Missouri | Mittie | Mollie | Molly | Mona | Monica | Monnie | Mossie | Mozelle | Muriel | Myra | Myrl | Myrle | Myrna | Myrta | Myrtice | Myrtie | Myrtis | Myrtle | Nadine | Nan | Nancy | Nanie | Nannie | Naomi | Natalie | Nealie | Nell | Nella | Nelle | Nellie | Nena | Neta | Nettie | Neva | Nevada | Nina | Nita | Nola | Nona | Nonie | Nora | Norah | Norine | Norma | Nova | Novella | Ocie | Octavia | Oda | Odelia | Odessa | Odie | Odile | Ola | Olevia | Olga | Olive | Olivia | Ollie | Oma | Omie | Ona | Onie | Opal | Ophelia | Ora | Orpha | Osa | Osie | Ossie | Ottie | Ottilia | Ottilie | Ouida | Ova | Pansy | Paralee | Patricia | Patsy | Pattie | Paula | Paulina | Pauline | Pearl | Pearle | Pearlie | Peggy | Petra | Phebe | Philomena | Phoebe | Phyllis | Pinkie | Pollie | Polly | Priscilla | Prudence | Queen | Queenie | Rachael | Rachel | Rae | Ramona | Ray | Reba | Rebecca | Regina | Rella | Rena | Ressie | Reta | Retha | Retta | Reva | Rhea | Rhoda | Rilla | Rita | Robbie | Robert | Roberta | Roma | Rosa | Rosalee | Rosalia | Rosalie | Rosalind | Rosamond | Rosanna | Rose | Rosella | Rosemary | Rosetta | Rosia | Rosie | Rosina | Rossie | Rowena | Roxie | Rubie | Ruby | Rubye | Ruth | Ruthie | Sabina | Sadie | Sadye | Sallie | Sally | Salome | Samantha | Sammie | Sara | Sarah | Savannah | Selena | Selina | Selma | Sena | Serena | Shirley | Sibyl | Sidney | Signe | Sigrid | Sina | Sofia | Sophia | Sophie | Sophronia | Stella | Sudie | Sue | Sula | Susan | Susanna | Susie | Suzanne | Sybil | Sylvia | Tempie | Tena | Tennie | Teresa | Tessie | Thea | Thelma | Theo | Theodora | Theodosia | Theresa | Therese | Theresia | Thomas | Thora | Tilda | Tillie | Tina | Tommie | Tressa | Tressie | Treva | Trudie | Twila | Una | Ursula | Vada | Valeria | Valerie | Vallie | Vassie | Veda | Vella | Velma | Velva | Vena | Vera | Verda | Verdie | Vergie | Verla | Verna | Vernice | Vernie | Verona | Veronica | Versie | Vertie | Vesta | Veva | Victoria | Vida | Vina | Vinnie | Viola | Violet | Vira | Virgie | Virgil | Virginia | Viva | Vivian | Walter | Wanda | Wilda | Wilhelmina | Wilhelmine | Willa | William | Willie | Wilma | Winifred | Winnie | Winnifred | Winona | Yetta | Yvonne | Zada | Zelda | Zelia | Zella | Zelma | Zena | Zenobia | Zetta | Zettie | Zita | Zoe | Zola | Zona | Zora @male -> Aaron | Abe | Abel | Abner | Abraham | Abram | Ada | Adam | Addie | Addison | Adelbert | Admiral | Adolph | Adolphus | Adrian | Al | Alan | Albert | Alberto | Albin | Alden | Alec | Alex | Alexander | Alf | Alfonso | Alford | Alfred | Alfredo | Alice | Allan | Allen | Allie | Allison | Alma | Alois | Alonza | Alonzo | Aloysius | Alpha | Alphonse | Alphonso | Alton | Alva | Alvah | Alvie | Alvin | Alvis | Ambrose | Amos | Anderson | Andres | Andrew | Andy | Angelo | Angus | Anna | Annie | Ansel | Anson | Anthony | Anton | Antone | Antonio | Arch | Archibald | Archie | Arley | Arlie | Armand | Arnold | Aron | Art | Arther | Arthur | Artie | Arvid | Asa | Ashley | Aubrey | August | Augustine | Augustus | Austin | Avery | Axel | Bailey | Barney | Bart | Bartholomew | Barton | Basil | Baxter | Bee | Ben | Benedict | Benjaman | Benjamin | Benjamine | Benjiman | Bennett | Bennie | Benny | Benton | Bernard | Bernhard | Bernice | Bernie | Berry | Bert | Bertha | Bertie | Bertram | Bertrand | Bessie | Beverly | Bill | Billie | Billy | Bishop | Blaine | Blair | Bob | Bonnie | Booker | Boss | Boyd | Bradford | Bradley | Brady | Brice | Brooks | Brown | Bruce | Bruno | Bryan | Bryant | Buck | Bud | Buford | Burl | Burley | Burr | Burrell | Burt | Burton | Buster | Butler | Byron | Cal | Caleb | Calvin | Carey | Carl | Carleton | Carlos | Carlton | Carrie | Carroll | Carson | Carter | Cary | Casper | Cecil | Charles | Charley | Charlie | Chas | Chauncey | Chesley | Chester | Chris | Christ | Christian | Christopher | Cicero | Clair | Claire | Clara | Clarance | Clare | Clarence | Clark | Claud | Claude | Clay | Clayton | Clem | Clement | Cleo | Cletus | Cleve | Cleveland | Cliff | Clifford | Clifton | Clint | Clinton | Clovis | Cloyd | Clyde | Coleman | Colonel | Columbus | Connie | Conrad | Corbett | Cornelious | Cornelius | Courtney | Coy | Crawford | Curley | Curtis | Cyril | Cyrus | Dale | Dallas | Dalton | Damon | Dan | Dana | Daniel | Darrell | Dave | David | Davis | Dayton | Dean | Dee | Delbert | Dell | Delmar | Denis | Dennis | Denver | Dewey | Dewitt | Dexter | Dick | Dillard | Doc | Dock | Doctor | Dominic | Dominick | Don | Donald | Donnie | Dorsey | Douglas | Dow | Doyle | Dudley | Duke | Duncan | Dwight | Earl | Earle | Early | Earnest | Ed | Edd | Eddie | Edgar | Edison | Edith | Edmond | Edmund | Edna | Eduardo | Edward | Edwin | Egbert | Elbert | Elder | Eldon | Eldridge | Eli | Elias | Elie | Eliga | Eligah | Elige | Elijah | Elisha | Elizabeth | Ella | Ellie | Elliot | Elliott | Ellis | Ellsworth | Ellwood | Elmer | Elmo | Elmore | Elsie | Elton | Elvin | Elvis | Elwin | Elwood | Elza | Elzie | Emanuel | Emerson | Emery | Emil | Emile | Emma | Emmet | Emmett | Emmit | Emmitt | Emory | Ennis | Enoch | Enos | Ephraim | Ephriam | Eric | Erick | Ernest | Ernie | Ernst | Ervin | Erwin | Essie | Esther | Ethel | Eugene | Eva | Evan | Evans | Everett | Everette | Evert | Ezekiel | Ezra | Fate | Fay | Felipe | Felix | Ferd | Ferdinand | Finis | Finley | Fitzhugh | Fletcher | Florence | Florian | Floyd | Ford | Forest | Forrest | Foster | Frances | Francis | Francisco | Frank | Franklin | Fred | Freddie | Frederic | Frederick | Fredrick | Freeman | French | Fritz | Furman | Gabe | Gabriel | Gail | Gale | Garfield | Garland | Garnett | Garrett | Gary | Gaston | Gaylord | Gene | General | Geo | George | Gerald | Gerard | Gerhard | Gertrude | Gilbert | Giles | Glen | Glenn | Glover | Godfrey | Golden | Gordon | Grace | Grady | Graham | Grant | Granville | Green | Gregorio | Gregory | Grover | Guadalupe | Gus | Guss | Gust | Gustaf | Gustav | Gustave | Guy | Hal | Hallie | Hamilton | Hamp | Hampton | Hans | Hardy | Harlan | Harley | Harmon | Harold | Harper | Harris | Harrison | Harry | Harve | Harvey | Haskell | Hattie | Hayden | Hayes | Hayward | Haywood | Hazel | Heber | Hector | Helen | Helmer | Henderson | Henery | Henry | Herbert | Herman | Hermon | Herschel | Hershel | Hezekiah | Hillard | Hilliard | Hilton | Hiram | Hobart | Hobert | Hobson | Hollis | Homer | Horace | Hosea | Houston | Howard | Howell | Hoyt | Hubert | Hudson | Huey | Hugh | Hughie | Hugo | Hunter | Hurley | Hyman | Ida | Ignacio | Ignatius | Ike | Ira | Irene | Irl | Irvin | Irving | Irwin | Isaac | Isadore | Isaiah | Isham | Isiah | Isidore | Isom | Israel | Issac | Ivan | Ivey | Ivory | Ivy | Jack | Jackson | Jacob | Jake | James | Jason | Jasper | Jay | Jean | Jeff | Jefferson | Jennings | Jeremiah | Jerome | Jerry | Jess | Jesse | Jessie | Jesus | Jewel | Jewell | Jim | Jimmie | Jimmy | Jodie | Joe | Joel | John | Johnie | Johnnie | Johnny | Johnson | Jonah | Jonas | Jonathan | Jones | Jonnie | Jordan | Jose | Joseph | Josh | Joshua | Josiah | Juan | Judge | Judson | Jule | Jules | Julian | Julius | June | Junior | Junius | Justin | Karl | Keith | Kelly | Kenneth | King | Kirby | Kirk | Kyle | Lacy | Lafayette | Lamar | Lambert | Larkin | Larry | Laura | Laurence | Laverne | Lawrence | Lawson | Leander | Lee | Leigh | Leland | Lem | Lemon | Lemuel | Len | Lenard | Lennie | Leo | Leon | Leonard | Leopold | Leroy | Leslie | Lester | Levi | Levy | Lew | Lewis | Lige | Lillian | Lillie | Lincoln | Lindsay | Lindsey | Linwood | Lionel | Llewellyn | Lloyd | Logan | Lon | Lonie | Lonnie | Lonzo | Loren | Lorenzo | Lou | Louie | Louis | Louise | Lowell | Loy | Loyal | Loyd | Lucas | Lucian | Lucien | Lucious | Lucius | Ludwig | Luis | Luke | Lum | Luther | Lyle | Lyman | Lynn | Mabel | Mac | Mack | Madison | Mahlon | Major | Malcolm | Manley | Mannie | Manuel | Marcellus | Marcus | Margaret | Marie | Marion | Mark | Marshall | Mart | Martha | Martin | Marvin | Mary | Mason | Mat | Mathew | Mathias | Matt | Matthew | Mattie | Maude | Maurice | Max | Maxie | Maxwell | May | Maynard | Mckinley | Melton | Melville | Melvin | Merl | Merle | Merlin | Merrill | Merritt | Merton | Mervin | Meyer | Michael | Micheal | Miguel | Mike | Mildred | Miles | Milford | Millard | Miller | Milo | Milton | Minnie | Minor | Mitchel | Mitchell | Monroe | Mont | Morgan | Morris | Mortimer | Morton | Mose | Moses | Murphy | Murray | Murry | Myles | Myron | Myrtle | Napoleon | Nat | Nathan | Nathaniel | Neal | Ned | Neil | Nellie | Nels | Nelson | Newell | Newman | Newt | Newton | Nicholas | Nick | Noah | Noble | Noel | Nolan | Norbert | Norman | Norris | Oakley | Obie | Ocie | Odell | Odie | Odis | Okey | Olaf | Ole | Olen | Olin | Oliver | Ollie | Omar | Omer | Ora | Oral | Oran | Oren | Orie | Orin | Orion | Orlando | Orlo | Orrin | Orval | Orville | Oscar | Ossie | Oswald | Otha | Otho | Otis | Ottis | Otto | Owen | Pablo | Palmer | Paris | Park | Parker | Pat | Patrick | Paul | Pearl | Pedro | Percival | Percy | Perley | Perry | Pete | Peter | Peyton | Phil | Philip | Phillip | Pierce | Pierre | Pink | Pleas | Pleasant | Porter | Preston | Price | Prince | Quincy | Rafael | Raleigh | Ralph | Ramon | Randall | Randolph | Ransom | Raphael | Ray | Raymond | Reed | Reese | Reginald | Reid | Reinhold | Rene | Reuben | Rex | Richard | Richmond | Riley | Robert | Rocco | Roderick | Rodney | Roe | Roger | Rogers | Roland | Rolla | Rolland | Rollie | Rollin | Roman | Romeo | Ronald | Roosevelt | Roscoe | Ross | Roswell | Rowland | Roy | Royal | Royce | Rube | Ruben | Rubin | Ruby | Rudolph | Ruel | Rufus | Rupert | Rush | Russel | Russell | Ruth | Salvatore | Sam | Sammie | Sampson | Samuel | Sanders | Sanford | Santiago | Saul | Scott | Selmer | Seth | Seymour | Shelby | Shelly | Shelton | Sherman | Shirley | Sid | Sidney | Silas | Sim | Simeon | Simon | Smith | Sol | Solomon | Son | Spencer | Spurgeon | Squire | Stacy | Stanley | Stephen | Sterling | Steve | Steven | Stewart | Stuart | Sullivan | Sumner | Sydney | Sylvester | Talmage | Taylor | Ted | Terrence | Terry | Thad | Thaddeus | Theo | Theodore | Theron | Thomas | Thornton | Thurman | Tillman | Tim | Timothy | Tobe | Tom | Tomas | Tommie | Tommy | Toney | Tony | Tracy | Travis | Troy | Truman | Turner | Ulysses | Unknown | Urban | Valentine | Van | Vance | Vaughn | Vern | Verne | Verner | Vernie | Vernon | Vester | Victor | Vincent | Virgil | Vivian | Wade | Waldo | Walker | Wallace | Walter | Walton | Ward | Warner | Warren | Wash | Washington | Watson | Waverly | Wayman | Wayne | Webb | Webster | Weldon | Wellington | Wendell | Wesley | West | Wheeler | Wilber | Wilbert | Wilbur | Wilburn | Wiley | Wilford | Wilfred | Will | Willam | Willard | William | Williams | Willis | Wilmer | Wilson | Wilton | Winfield | Winfred | Winston | Worth | Wright | Wyatt | Wylie | Young | Zack | Zeb | Zollie PK!OБ((shellcraft/data/research.yamlsmall_cart: description: A way to transport your clay faster. difficulty: 120 prerequisites: clay: 20 effects: enable_items: small_cart ore: description: There's something shiny here... effects: enable_resources: ore difficulty: 400 prerequisites: clay: 30 dynamite: description: BOOM goes the dynamite. difficulty: 600 prerequisites: ore: 120 effects: enable_items: dynamite automata: description: Machines that can be animated with names difficulty: 1200 prerequisites: triggers: nomenclator PK!BV1ishellcraft/data/tools.yamlshovel: description: Basic clay mining tool. durability: 16 difficulty: 5 mining_bonus: clay: 2 cost: clay: 4 prerequisites: clay: 4 sturdy_shovel: description: Better clay mining tool. durability: 80 difficulty: 30 mining_bonus: clay: 2 cost: clay: 10 prerequisites: clay: 10 small_cart: description: As simple cart to transport raw materials. durability: 300 difficulty: 90 event_bonus: new_clay_deposit: .05 mining_bonus: clay: 4 ore: 4 cost: clay: 20 prerequisites: research: small_cart axe: description: A simple axe for mining ore. durability: 600 difficulty: 30 mining_bonus: ore: 8 cost: ore: 40 prerequisites: ore: 20 crawler: description: The easiest possible automaton. durability: 6000 difficulty: 60 type: automaton body: 14L40M cost: clay: 40 ore: 40 prerequisites: research: automata digger: description: A simple mining automaton. durability: 6000 difficulty: 300 type: automaton cost: clay: 200 ore: 120 prerequisites: research: digger PK!fTshellcraft/data/tutorials.yaml 0: description: > Welcome to ShellCraft. ShellCraft is a game about mining, crafting, and puzzling, loosely based on Ted Chiang's short story "72 letters". But all of that doesn't matter much now, we've got work to do. Let's start with mining some *clay* by typing `shellcraft mine clay` effects: enable_resources: clay enable_commands: - mine - reset 1: description: > Excellent! Mining resources (like everything in ShellCraft) takes some time, but you're rewarded with this warm, moist, delicate... well, *clay*. Time to stock up on *clay* some more: `shellcraft mine clay` prerequisites: clay: 1 effects: enable_commands: - tutorial 2: description: > Well done. Let's continue this until we have *4 clay*. prerequisites: clay: 2 3: description: > Notice how every time you run `shellcraft mine clay`, it takes a little longer? That's because the resources on the surface are easy to reach, but the more you mine them, the harder it is to get more. For now, there's not much you can do about it, however you can increase the yield of your mining operations with tools! Try crafting a $shovel$ by running `shellcraft craft shovel` prerequisites: clay: 4 effects: enable_commands: craft 4: description: > Whoop whoop! Let's put this to use by running `shellcraft mine clay` again. prerequisites: items: shovel 5: description: > See how we're getting a lot more clay now? You can see which $items$ are currently in your posession by running `shellcraft inventory`. You can also find out how much *clay* (and later, other resources) you have by running `shellcraft resources`. When you're ready, continue mining until you have *10 clay*. prerequisites: clay: 2 items: shovel effects: enable_commands: - inventory - resources 6: description: > Oh no, our $shovel$ broke! All items have a certain durability, which is the number of seconds you can use them before they break. When mining, shellcraft will always pick the best tool for the job until it breaks, and then continue with the next-best tool. In this case, your bare hands. Let's invest a little bit into our tools and craft a $sturdy_shovel$. prerequisites: clay: 10 7: description: > The $sturdy_shovel$ costs a little more than the good ol' $shovel$ and doens't give us any better bonus, but it lasts five times as long! That means we can get some serious mining done. ShellCraft is a hackable game. That doesn't mean that you should cheat, but rather, that you can use your coding skills to your advantage and automate and improve some aspects of the game! For example, let's mine *clay* ten times in a row by using this little bash script: `for i in {1..10}; do shellcraft mine clay; done` prerequisites: items: sturdy_shovel 8: description: > Have a look at `shellcraft inventory` - our $sturdy_shovel$ is still in pretty good shape. Some items are unlocked simply by having enough resources to craft them. Others need to be researched first. A quick look at `shellcraft research` shows you the available research projects. Researching doesn't cost you anything, but can take a long time that you can't spend mining or crafting anything. So let's embark on your first science project and research the %small_cart%! prerequisites: clay: 20 effects: enable_commands: research 9: description: > This should make our job a little easier. Keep on mining now! prerequisites: items: small_cart 10: description: > Whoa, what's that? Looks like there's a huge clay deposit waiting for us just below the surface! Some tools will have a chance of giving you bonus resources when used, or help you discover new deposits that greatly reduce the time it takes to mine, craft, or research things. Keep on mining. effects: events: new_clay_deposit prerequisites: clay: 4 11: description: > ~names~ Look, we've received a letter from the Sculptor's Union: > Esteemed citizen, > > We kindly ask for your assistance to our causes by supplying the Union > with *150 clay* at your earliest convenience. Your continued support will not > go unnoticed. > > With highest regards, > > D. H. Ramsbottom > > Assistant to Undersecretary Pierce That's a lot of clay, but it's probably worth making some friends within the Union. prerequisites: clay: 10 12: description: > Shortly after delivering the clay, another letter comes in: > Esteemed citizen, > > In light of your generous contribution to the Sculptor's Union, it is > my please to let it be known that by degree of Chairman Manings, you > are herewith bestowed with the title of Nomenclator. As such, you are free > to conduct research in, produce, and vend automata. I'd like to express my > personal congratulations and wish you success. > > With highest regards, > > D. H. Ramsbottom > > Assistant to Undersecretary Pierce effects: triggers: nomenclator clay: -50 prerequisites: clay: 150 13: description: > You completed your first automaton! prerequisites: items: crawler effects: enable_commands: automata PK!!R ""shellcraft/epithets.py# -*- coding: utf-8 -*- """Epithet Classes.""" import re class Name(object): def __init__(self, name_or_shortcode): if re.search(r"\d", name_or_shortcode): self.name = Name.shortcode_to_name(name_or_shortcode.replace("\n", "")) else: self.name = name_or_shortcode.splitlines()[:6] self._cells = [] self._padder = Epithet.get('S', self, -1, -1) for y in range(6): row = [Epithet.get(self.name[y][x], self, x, y) for x in range(12)] self._cells.append(row) @property def shortcode(self): return Name.name_to_shortcode(self.name) @classmethod def shortcode_to_name(cls, shortcode): """Convert a shortcode to a name. Example: Name.shortcode_to_name("30LA3M26") [' ', ' ', ' LA M', ...] """ name = re.sub(r'\d+', lambda m: " " * int(m.group(0)), shortcode) return [f"{name[n:n + 12]:12}" for n in range(0, 72, 12)] @classmethod def name_to_shortcode(cls, name): """Convert a name to a shortcode. Example: Name.shortcode_to_name([' ', ' ', ' LA M ']) "27LA3M3" """ shortcode = re.sub(r"[{}]+".format(Epithet.BLANKS), lambda m: str(len(m.group(0))), "".join(name)) return shortcode.replace('\n', "") @property def epithets(self): """Yield all epithets in the body.""" for row in self._cells: for epithet in row: yield epithet @property def weak_epithets(self): """Yield all weakly active epithets.""" return filter(lambda epithet: epithet.state == 1, self.epithets) @property def active_epithets(self): """Yield all active epithets.""" return filter(lambda epithet: epithet.state == 2, self.epithets) @property def special_epithets(self): """Yield all epithets with non-empty symbols.""" return filter(lambda epithet: epithet.is_special, self.epithets) def step(self): effects = [] # Weak epithets die for epithet in self.weak_epithets: epithet._next_state = 0 # Active epithets transport their energy for epithet in self.active_epithets: epithet.traverse() for epithet in self.special_epithets: effects.append(epithet.apply()) # Apply the next state for epithet in self.epithets: epithet.update() # Padders always day self._padder.state = 0 return filter(bool, effects) def reset(self): """Resets the state of all epithets to their default.""" for e in self.epithets: e.state = e.value = 0 @property def _state(self): """Returns a tuple with the state of each epithet.""" return tuple((e.state for e in self.epithets)) @property def cycle_length(self): """Returns the length of one cycle of the name.""" states = [self._state] self.step() for n in range(20): self.step() if len(states) >= 5 and self._state in states: cycle = len(states) - states.index(self._state) self.reset() return cycle else: states.append(self._state) self.reset() return -1 class Epithet(object): symbol = " " traversing = True period = 5 generative = False BLANKS = " *.·" def __init__(self, name, x, y): self.name = name self.x = x self.y = y self.state = 0 self._next_state = 0 self.value = 0 self._next_value = 0 @classmethod def get(cls, symbol, name, x=None, y=None): for epithet in cls.__subclasses__(): if epithet.symbol == symbol: return epithet(name, x, y) return cls(name, x, y) @property def is_special(self): return self.symbol not in self.BLANKS @property def neighbours(self): """Generate all the neighbours.""" yield self.above yield self.below yield self.left yield self.right @property def above(self): """Select the cell above.""" if self.y > 0: return self.name._cells[self.y - 1][self.x] return self.name._padder @property def below(self): """Select the cell below.""" if self.y < 5: return self.name._cells[self.y + 1][self.x] return self.name._padder @property def right(self): """Select the cell right.""" if self.x < 11: return self.name._cells[self.y][self.x + 1] return self.name._padder @property def left(self): """Select the cell left.""" if self.x > 0: return self.name._cells[self.y][self.x - 1] return self.name._padder def apply(self): pass def traverse(self, force=False): """Apply activity to neighbouring cells.""" if not self.traversing and not force: return # Only traverse if we're active if not self.state == 2: return if self.above.state == 1: self.below._next_state = 2 if self.below.state == 1: self.above._next_state = 2 if self.right.state == 1: self.left._next_state = 2 if self.left.state == 1: self.right._next_state = 2 # ...and turn weak self._next_state = 1 def update(self): """Update the inner state.""" self.state = self._next_state self._next_state = 0 self.value = self._next_value self._next_value = 0 class Move(Epithet): """Epithet that when activated will move the automaton.""" symbol = "M" traversing = True def apply(self): if self.state == 2: return "move" class Turn(Epithet): """Epithet that when activated will turn the automaton right.""" symbol = "D" traversing = True def apply(self): if self.state == 2: return "turn_right" class LeftTurn(Epithet): """Epithet that when activated will turn the automaton left.""" symbol = "G" traversing = True def apply(self): if self.state == 2: return "turn_left" class Life(Epithet): """Epithet that generates a weak charge every 5 cycles.""" symbol = "L" generative = True def apply(self): self._next_value = (self.value + 1) % self.period if self.value == 0: # Only weakly activate if not already activated self._next_state = max(self._next_state, 1) class Amplify(Epithet): """Epithet that will amplify a weak charge and allow it to travese.""" symbol = "A" def apply(self): if self.state != 2: for c in self.neighbours: if c.generative and c.state == 1: self._next_state = 2 c._next_state = max(c._next_state, 1) class Concurrence(Epithet): """Epithet that will split a charge into two horizontal charges.""" symbol = "C" def traverse(self): """Apply activity to neighbouring cells.""" if self.above.state == 1 or self.below.state == 1: self.left._next_state = 2 self.right._next_state = 2 if self.right.state == 1 or self.left.state == 1: self.above._next_state = 2 self.below._next_state = 2 # ...and turn weak self._next_state = 1 class Restraint(Epithet): """Epithet that will only traverse it's charge every second time.""" symbol = "R" traversing = False def apply(self): self._next_value = self.value if self.state == 2: self._next_value = (self.value + 1) self._next_state = 0 if self.value == 1: self._next_value = 0 self.traverse(force=True) class Silence(Epithet): """Epithet that will can never be charged.""" symbol = "S" traversing = False def apply(self): self.state = 0 self._next_state = 0 class Synchronicity(Epithet): """Similar to concurrence, but will only traverse if charged from both sides.""" symbol = "Y" def traverse(self): """Apply activity to neighbouring cells.""" if self.above.state == 1 and self.below.state == 1: self.left._next_state = 2 self.right._next_state = 2 self._next_state = 1 if self.right.state == 1 and self.left.state == 1: self.above._next_state = 2 self.below._next_state = 2 self._next_state = 1 else: self._next_state = 0 # ...and turn weak PK!1shellcraft/events.py# -*- coding: utf-8 -*- """Events Interface.""" from shellcraft.core import BaseItem, BaseFactory class Event(BaseItem): def __repr__(self): return self.name class EventFactory(BaseFactory): FIXTURES = 'events.yaml' ITEM_CLASS = Event def trigger(self, *events): for event in events: event = self.get(event) self.game.alert(event.description) self.apply_effects(event) PK!Ha??shellcraft/exceptions.py# -*- coding: utf-8 -*- """Exceptions.""" import datetime from shellcraft.grammar import VERBS class ShellcraftException(RuntimeError): pass class BusyException(ShellcraftException): """Exception that is raised if the parse tree runs too deep.""" def __init__(self, game): self._time_left = game.state.action.completion.ToDatetime() - datetime.datetime.now() self._action = game.state.action.task def __str__(self): return f"You're busy {VERBS[self._action]} for another { self._time_left.total_seconds():.0f} seconds." class ResourceNotAvailable(ShellcraftException): """Exception that is raised if the parse tree runs too deep.""" def __init__(self, resource): self._resource = resource def __str__(self): return f"You can't mine {self._resource} yet" PK!MMshellcraft/fractions.py# -*- coding: utf-8 -*- """Basic CLI for ShellCraft.""" import os import poyo class FractionProxy(object): FIXTURES = 'fractions.yaml' def __init__(self, field): self._field = field self._fractions = {f.name: f for f in field} with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "data", self.FIXTURES)) as f: contents = f.read() for name, fraction in poyo.parse_string(contents).items(): if name not in self._fractions: f = self._field.add(name=name, influence=fraction['influence']) self._fractions[name] = f def get(self, name): return self._fractions[name] def __getattr__(self, name): if name in self._fractions: return self._fractions[name] raise AttributeError PK!\u``shellcraft/game_state_pb2.py# Generated by the protocol buffer compiler. DO NOT EDIT! # source: game_state.proto import sys _b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message from google.protobuf import reflection as _reflection from google.protobuf import symbol_database as _symbol_database from google.protobuf import descriptor_pb2 # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 DESCRIPTOR = _descriptor.FileDescriptor( name='game_state.proto', package='', syntax='proto3', serialized_pb=_b('\n\x10game_state.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"V\n\x06\x41\x63tion\x12\x0c\n\x04task\x18\x01 \x01(\t\x12\x0e\n\x06target\x18\x02 \x01(\t\x12.\n\ncompletion\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"`\n\x08\x46raction\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x11\n\tinfluence\x18\x02 \x01(\x02\x12\x1a\n\x12missions_completed\x18\x03 \x01(\x05\x12\x17\n\x0fmissions_failed\x18\x04 \x01(\x05\"6\n\tResources\x12\x0c\n\x04\x63lay\x18\x01 \x01(\x02\x12\x0b\n\x03ore\x18\x02 \x01(\x02\x12\x0e\n\x06\x65nergy\x18\x03 \x01(\x02\"\'\n\x04Tool\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x11\n\tcondition\x18\x02 \x01(\x02\"\xc9\x01\n\x07Mission\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06\x64\x65mand\x18\x02 \x01(\x05\x12\x0e\n\x06reward\x18\x03 \x01(\x05\x12\x13\n\x0b\x64\x65mand_type\x18\x04 \x01(\t\x12\x13\n\x0breward_type\x18\x05 \x01(\t\x12\x0b\n\x03\x64ue\x18\x06 \x01(\x05\x12,\n\x08\x64\x65\x61\x64line\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x14\n\x06writer\x18\x08 \x01(\x0b\x32\x04.NPC\x12\x15\n\rreward_factor\x18\t \x01(\x05\"E\n\x05Stats\x12\x1b\n\x13total_game_duration\x18\x01 \x01(\x02\x12\x1f\n\x0btotal_mined\x18\x02 \x01(\x0b\x32\n.Resources\"{\n\x03NPC\x12\r\n\x05\x66irst\x18\x01 \x01(\t\x12\x0e\n\x06middle\x18\x02 \x01(\t\x12\x0c\n\x04last\x18\x03 \x01(\t\x12\r\n\x05title\x18\x04 \x01(\t\x12\x10\n\x08nickname\x18\x05 \x01(\t\x12\x0f\n\x07\x64isplay\x18\x06 \x01(\t\x12\x15\n\rfraction_name\x18\x07 \x01(\t\"\xd6\x03\n\tGameState\x12\r\n\x05\x64\x65\x62ug\x18\x01 \x01(\x08\x12\x17\n\x06\x61\x63tion\x18\x02 \x01(\x0b\x32\x07.Action\x12\x14\n\x05tools\x18\x03 \x03(\x0b\x32\x05.Tool\x12\x1a\n\x08missions\x18\x04 \x03(\x0b\x32\x08.Mission\x12\x1d\n\tresources\x18\x05 \x01(\x0b\x32\n.Resources\x12\x15\n\rtools_enabled\x18\x06 \x03(\t\x12\x19\n\x11resources_enabled\x18\x07 \x03(\t\x12\x18\n\x10\x63ommands_enabled\x18\x08 \x03(\t\x12\x18\n\x10research_enabled\x18\t \x03(\t\x12\x1a\n\x12research_completed\x18\n \x03(\t\x12\x10\n\x08triggers\x18\x0b \x03(\t\x12%\n\x11mining_difficulty\x18\x0c \x01(\x0b\x32\n.Resources\x12/\n\x1bmining_difficulty_increment\x18\r \x01(\x0b\x32\n.Resources\x12\x18\n\x10trade_reputation\x18\x0e \x01(\x02\x12\x15\n\rtutorial_step\x18\x0f \x01(\x05\x12\x15\n\x05stats\x18\x10 \x01(\x0b\x32\x06.Stats\x12\x1c\n\tfractions\x18\x11 \x03(\x0b\x32\t.Fractionb\x06proto3') , dependencies=[google_dot_protobuf_dot_timestamp__pb2.DESCRIPTOR,]) _sym_db.RegisterFileDescriptor(DESCRIPTOR) _ACTION = _descriptor.Descriptor( name='Action', full_name='Action', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='task', full_name='Action.task', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='target', full_name='Action.target', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='completion', full_name='Action.completion', index=2, number=3, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=53, serialized_end=139, ) _FRACTION = _descriptor.Descriptor( name='Fraction', full_name='Fraction', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='name', full_name='Fraction.name', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='influence', full_name='Fraction.influence', index=1, number=2, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(0), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='missions_completed', full_name='Fraction.missions_completed', index=2, number=3, type=5, cpp_type=1, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='missions_failed', full_name='Fraction.missions_failed', index=3, number=4, type=5, cpp_type=1, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=141, serialized_end=237, ) _RESOURCES = _descriptor.Descriptor( name='Resources', full_name='Resources', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='clay', full_name='Resources.clay', index=0, number=1, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(0), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='ore', full_name='Resources.ore', index=1, number=2, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(0), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='energy', full_name='Resources.energy', index=2, number=3, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(0), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=239, serialized_end=293, ) _TOOL = _descriptor.Descriptor( name='Tool', full_name='Tool', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='name', full_name='Tool.name', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='condition', full_name='Tool.condition', index=1, number=2, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(0), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=295, serialized_end=334, ) _MISSION = _descriptor.Descriptor( name='Mission', full_name='Mission', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='name', full_name='Mission.name', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='demand', full_name='Mission.demand', index=1, number=2, type=5, cpp_type=1, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='reward', full_name='Mission.reward', index=2, number=3, type=5, cpp_type=1, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='demand_type', full_name='Mission.demand_type', index=3, number=4, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='reward_type', full_name='Mission.reward_type', index=4, number=5, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='due', full_name='Mission.due', index=5, number=6, type=5, cpp_type=1, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='deadline', full_name='Mission.deadline', index=6, number=7, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='writer', full_name='Mission.writer', index=7, number=8, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='reward_factor', full_name='Mission.reward_factor', index=8, number=9, type=5, cpp_type=1, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=337, serialized_end=538, ) _STATS = _descriptor.Descriptor( name='Stats', full_name='Stats', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='total_game_duration', full_name='Stats.total_game_duration', index=0, number=1, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(0), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='total_mined', full_name='Stats.total_mined', index=1, number=2, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=540, serialized_end=609, ) _NPC = _descriptor.Descriptor( name='NPC', full_name='NPC', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='first', full_name='NPC.first', index=0, number=1, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='middle', full_name='NPC.middle', index=1, number=2, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='last', full_name='NPC.last', index=2, number=3, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='title', full_name='NPC.title', index=3, number=4, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='nickname', full_name='NPC.nickname', index=4, number=5, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='display', full_name='NPC.display', index=5, number=6, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='fraction_name', full_name='NPC.fraction_name', index=6, number=7, type=9, cpp_type=9, label=1, has_default_value=False, default_value=_b("").decode('utf-8'), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=611, serialized_end=734, ) _GAMESTATE = _descriptor.Descriptor( name='GameState', full_name='GameState', filename=None, file=DESCRIPTOR, containing_type=None, fields=[ _descriptor.FieldDescriptor( name='debug', full_name='GameState.debug', index=0, number=1, type=8, cpp_type=7, label=1, has_default_value=False, default_value=False, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='action', full_name='GameState.action', index=1, number=2, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='tools', full_name='GameState.tools', index=2, number=3, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='missions', full_name='GameState.missions', index=3, number=4, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='resources', full_name='GameState.resources', index=4, number=5, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='tools_enabled', full_name='GameState.tools_enabled', index=5, number=6, type=9, cpp_type=9, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='resources_enabled', full_name='GameState.resources_enabled', index=6, number=7, type=9, cpp_type=9, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='commands_enabled', full_name='GameState.commands_enabled', index=7, number=8, type=9, cpp_type=9, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='research_enabled', full_name='GameState.research_enabled', index=8, number=9, type=9, cpp_type=9, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='research_completed', full_name='GameState.research_completed', index=9, number=10, type=9, cpp_type=9, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='triggers', full_name='GameState.triggers', index=10, number=11, type=9, cpp_type=9, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='mining_difficulty', full_name='GameState.mining_difficulty', index=11, number=12, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='mining_difficulty_increment', full_name='GameState.mining_difficulty_increment', index=12, number=13, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='trade_reputation', full_name='GameState.trade_reputation', index=13, number=14, type=2, cpp_type=6, label=1, has_default_value=False, default_value=float(0), message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='tutorial_step', full_name='GameState.tutorial_step', index=14, number=15, type=5, cpp_type=1, label=1, has_default_value=False, default_value=0, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='stats', full_name='GameState.stats', index=15, number=16, type=11, cpp_type=10, label=1, has_default_value=False, default_value=None, message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), _descriptor.FieldDescriptor( name='fractions', full_name='GameState.fractions', index=16, number=17, type=11, cpp_type=10, label=3, has_default_value=False, default_value=[], message_type=None, enum_type=None, containing_type=None, is_extension=False, extension_scope=None, options=None), ], extensions=[ ], nested_types=[], enum_types=[ ], options=None, is_extendable=False, syntax='proto3', extension_ranges=[], oneofs=[ ], serialized_start=737, serialized_end=1207, ) _ACTION.fields_by_name['completion'].message_type = google_dot_protobuf_dot_timestamp__pb2._TIMESTAMP _MISSION.fields_by_name['deadline'].message_type = google_dot_protobuf_dot_timestamp__pb2._TIMESTAMP _MISSION.fields_by_name['writer'].message_type = _NPC _STATS.fields_by_name['total_mined'].message_type = _RESOURCES _GAMESTATE.fields_by_name['action'].message_type = _ACTION _GAMESTATE.fields_by_name['tools'].message_type = _TOOL _GAMESTATE.fields_by_name['missions'].message_type = _MISSION _GAMESTATE.fields_by_name['resources'].message_type = _RESOURCES _GAMESTATE.fields_by_name['mining_difficulty'].message_type = _RESOURCES _GAMESTATE.fields_by_name['mining_difficulty_increment'].message_type = _RESOURCES _GAMESTATE.fields_by_name['stats'].message_type = _STATS _GAMESTATE.fields_by_name['fractions'].message_type = _FRACTION DESCRIPTOR.message_types_by_name['Action'] = _ACTION DESCRIPTOR.message_types_by_name['Fraction'] = _FRACTION DESCRIPTOR.message_types_by_name['Resources'] = _RESOURCES DESCRIPTOR.message_types_by_name['Tool'] = _TOOL DESCRIPTOR.message_types_by_name['Mission'] = _MISSION DESCRIPTOR.message_types_by_name['Stats'] = _STATS DESCRIPTOR.message_types_by_name['NPC'] = _NPC DESCRIPTOR.message_types_by_name['GameState'] = _GAMESTATE Action = _reflection.GeneratedProtocolMessageType('Action', (_message.Message,), dict( DESCRIPTOR = _ACTION, __module__ = 'game_state_pb2' # @@protoc_insertion_point(class_scope:Action) )) _sym_db.RegisterMessage(Action) Fraction = _reflection.GeneratedProtocolMessageType('Fraction', (_message.Message,), dict( DESCRIPTOR = _FRACTION, __module__ = 'game_state_pb2' # @@protoc_insertion_point(class_scope:Fraction) )) _sym_db.RegisterMessage(Fraction) Resources = _reflection.GeneratedProtocolMessageType('Resources', (_message.Message,), dict( DESCRIPTOR = _RESOURCES, __module__ = 'game_state_pb2' # @@protoc_insertion_point(class_scope:Resources) )) _sym_db.RegisterMessage(Resources) Tool = _reflection.GeneratedProtocolMessageType('Tool', (_message.Message,), dict( DESCRIPTOR = _TOOL, __module__ = 'game_state_pb2' # @@protoc_insertion_point(class_scope:Tool) )) _sym_db.RegisterMessage(Tool) Mission = _reflection.GeneratedProtocolMessageType('Mission', (_message.Message,), dict( DESCRIPTOR = _MISSION, __module__ = 'game_state_pb2' # @@protoc_insertion_point(class_scope:Mission) )) _sym_db.RegisterMessage(Mission) Stats = _reflection.GeneratedProtocolMessageType('Stats', (_message.Message,), dict( DESCRIPTOR = _STATS, __module__ = 'game_state_pb2' # @@protoc_insertion_point(class_scope:Stats) )) _sym_db.RegisterMessage(Stats) NPC = _reflection.GeneratedProtocolMessageType('NPC', (_message.Message,), dict( DESCRIPTOR = _NPC, __module__ = 'game_state_pb2' # @@protoc_insertion_point(class_scope:NPC) )) _sym_db.RegisterMessage(NPC) GameState = _reflection.GeneratedProtocolMessageType('GameState', (_message.Message,), dict( DESCRIPTOR = _GAMESTATE, __module__ = 'game_state_pb2' # @@protoc_insertion_point(class_scope:GameState) )) _sym_db.RegisterMessage(GameState) # @@protoc_insertion_point(module_scope) PK!f}gUUshellcraft/grammar.py# coding=utf-8 """Probabilistic Context Free Grammar.""" from collections import defaultdict import random import re import sys import os VERBS = { "research": "researching", "mine": "mining", "craft": "crafting" } class MaximumDepthExceeded(Exception): """Exception that is raised if the parse tree runs too deep.""" pass class SymbolNotFound(Exception): """Fix yo grammar.""" pass class Grammar(object): grammars = {} def __init__(self, grammar_string): self.grammar = self.parse_grammar(grammar_string) @classmethod def load(cls, grammar_file): with open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "data", grammar_file + ".grammar")) as f: cls.grammars[grammar_file] = cls(f.read()) return cls.grammars[grammar_file] def weighted_choice(self, options, weights): """Choose a random item, according to weights. Args: options: list weights: list of floats -- don't have to add up to 1 Returns: element of options """ target = random.random() * sum(weights) acc = 0 for idx, weight in enumerate(weights): acc += weight if acc > target: return options[idx] def parse_grammar(self, grammar): """Return a dictionary mapping symbols to extensions. Example: >>> grammar = ''' @s -> @n @v @s -> @n @v @n @n -> dog | cat @v -> chases %3 | eats %2.0''' >>> parse_grammar(grammar) { "@s": [ [ "@n @v", 0.3 ], [ "@n @v @n", 0.7 ] ], "@v": [ [ "chases", 0.75 ], [ "eats", 0.25 ] ], "@n": [ [ "dog", 0.5 ], [ "cat", 0.5 ] ] } Args: grammar: str Returns: dict """ weight_re = r"%((?:[\d]*\.)?[\d]+)" result = defaultdict(list) for line in grammar.splitlines(): if "->" in line: symbol, extension = line.split("->") for extension in extension.split("|"): weight = re.search(weight_re, extension) if weight: extension = re.sub(weight_re, "", extension) weight = float(weight.group(1)) else: weight = 1.0 result[symbol.strip()].append((extension.strip(), weight)) # normalize for symbol, extensions in result.items(): total_weight = sum(ext[1] for ext in extensions) result[symbol] = [(ext[0], ext[1] / total_weight) for ext in extensions] return dict(result) def transform(self, parts, rule): if rule == "gen": if parts[-1].rstrip().endswith("s"): parts[-1] = parts[-1].rstrip() + "'" else: parts[-1] = parts[-1].rstrip() + "'s" if rule == "initial": return [p[0].upper() for p in parts] if rule == "title": return [p if p in ("by", "of", "and") else p.capitalize() for p in parts] return parts def extend_rule(self, symbol="@s", max_depth=8): """Start with a symbol and returns a list of tokens. Args: symbol: str -- should start with @ max_depth: int -- maximum tree depth. Returns: list -- list of parts Raises: MaximumDepthExceeded SymbolNotFound """ rule = None if "~" in symbol: symbol, rule = symbol.split("~") if max_depth == 0: raise MaximumDepthExceeded if symbol not in self.grammar: raise SymbolNotFound(symbol) extension = self.weighted_choice(*zip(*self.grammar[symbol])) result = self.extend_sentence(extension, max_depth) return self.transform(result, rule) def extend_sentence(self, sentence, max_depth=8): result = [] for part in sentence.replace("\n", "\n ").split(" "): if part.startswith("@"): result.extend(self.extend_rule(part, max_depth - 1)) else: result.append(part) return result # def extend_all(sentence, grammar, max_depth=8): # if max_depth == 0: # yield " ".join(sentence) # else: # if not isinstance(sentence, list): # sentence = sentence.split() # first_chars = [c[0] for c in sentence] # try: # part = first_chars.index("@") # for extension, pr in grammar[sentence[part]]: # for r in extend_all(sentence[:part] + [extension] + sentence[part + 1:], grammar, max_depth - 1): # yield r # except ValueError: # yield " ".join(sentence) def assemble_sentence(self, parts): """Clean up parts and applies some syntactic rules. Args: parts: list Returns: str """ sentence = " ".join(parts) sentence = re.sub(r" ([,.!?])", r'\1', sentence) sentence = re.sub(r"' ([A-Za-z0-9 ]+) '", r"'\1'", sentence) sentence = re.sub(r" +", r' ', sentence) sentence = re.sub(r"\n ", '\n', sentence) return sentence.strip() def generate(self, sentence=None): """Generate a sentence from a grammar string. Args: grammar: str Returns: str """ parts = None while not parts: try: parts = self.extend_sentence(sentence) except MaximumDepthExceeded: pass except SymbolNotFound as e: print(f"WARNING: Symbol {e.args[0]} not found", file=sys.stderr) return self.assemble_sentence(parts) NAMES = Grammar.load("names") GUILDS = Grammar.load("guild_names") LETTER = Grammar.load("letter") PK!/^shellcraft/missions.py# -*- coding: utf-8 -*- """Events Interface.""" from shellcraft.core import BaseItem, BaseFactory from shellcraft._cli_impl import echo, ask from shellcraft.utils import convert_resource_value, format_name from shellcraft.game_state_pb2 import Mission as MissionPB from shellcraft.world import NPCFactory import datetime import random class Mission(BaseItem): """A trade contract, requesting a certain quantity of a resource in a given time.""" def randomize(self, game): """Generate random mission.""" self.writer = NPCFactory.make() random.shuffle(game.state.resources_enabled) if not self.reward_type or self.reward_type == 'resource': demand_type, reward_type = game.state.resources_enabled[:2] elif self.reward_type == 'reputation': demand_type = game.state.resources_enabled[1] if game.workshop.available_items: best_available_tool = max(game.workshop.available_items, key=lambda item: item.mining_bonus.get(demand_type, 0)) efficiency = best_available_tool.mining_bonus.get(demand_type) or 1 else: efficiency = 1 difficulty = game.mining_difficulty.get(demand_type) extra_demand = random.random() * game.resources.get(demand_type) self.demand = int(game.resources.get(demand_type) + extra_demand) self.due = int(extra_demand / efficiency * difficulty * (2 + random.random())) + 10 self.demand_type = demand_type self.reward = int((1 + game.state.trade_reputation + .3 * random.random()) * convert_resource_value(demand_type, reward_type) * self.demand) self.reward_type = reward_type # a = str(self.writer) # print("DEM", format_name(self.writer)) # self.writer.CopyFrom(npc) def vars(self, game): return { "writer": format_name(self.writer), "demand": self.demand, "due": self.due, "demand_type": self.demand_type, "reward": self.reward, "reward_type": self.reward_type, "deficit": self.demand - game.resources.get(self.demand_type) } def offer(self, game): # return re.sub(" +", " ", d.replace("\n", " ")) echo(self.strings['intro'].format(**self.vars(game))) if ask(self.strings['ask'].format(**self.vars(game))): self.deadline.FromDatetime(datetime.datetime.now() + datetime.timedelta(seconds=self.due)) echo(self.strings['agree'].format(**self.vars(game))) return True else: game.state.trade_reputation -= 0.02 echo(self.strings['disagree'].format(**self.vars(game))) return False def is_completed(self, game): if datetime.datetime.now() > self.deadline.ToDatetime(): # Failed! echo(self.strings['failed'].format(**self.vars(game))) return True if game.resources.get(self.demand_type) >= self.demand: echo(self.strings['completed'].format(**self.vars(game))) game.resources.add(self.demand_type, -self.demand) game.resources.add(self.reward_type, self.reward) return True return False def __repr__(self): return "blank" return "<{demand} {demand_type} in {due}s for {reward} {reward_type}>".format(**vars(self)) class MissionFactory(BaseFactory): FIXTURES = 'missions.yaml' ITEM_CLASS = Mission PB_CLASS = MissionPB def make(self, mission): new_mission = super(MissionFactory, self).make(mission) if not hasattr(new_mission, "demand"): new_mission.randomize(self.game) return new_mission PK!t/shellcraft/research.py# -*- coding: utf-8 -*- """Basic CLI for ShellCraft.""" from shellcraft.core import BaseItem, BaseFactory class ResearchProject(BaseItem): def __repr__(self): return f"%{self.name}%" class ResearchFactory(BaseFactory): FIXTURES = 'research.yaml' ITEM_CLASS = ResearchProject def is_available(self, item_name): project = self.get(item_name) return project.name not in self.game.state.research_completed and super(ResearchFactory, self).is_available(project) PK!REWttshellcraft/shellcraft.py# -*- coding: utf-8 -*- """Game Classes.""" from shellcraft.tools import ToolFactory from shellcraft.research import ResearchFactory from shellcraft.tutorial import TutorialFactory from shellcraft.events import EventFactory from shellcraft.missions import MissionFactory from shellcraft.fractions import FractionProxy from shellcraft.exceptions import BusyException from shellcraft.game_state_pb2 import GameState from google.protobuf import json_format from shellcraft.core import ResourceProxy, ItemProxy from random import random import os import datetime class Game(object): """The Game class holds all information about, well, the game's state, and handles the logic.""" def __init__(self, state=None): """Create a new Game instante.""" self.state = state or GameState() self._messages = [] self.lab = ResearchFactory(self) self.workshop = ToolFactory(self) self.tutorial = TutorialFactory(self) self.events = EventFactory(self) self.mission_factory = MissionFactory(self) self.resources = ResourceProxy(self.state.resources) self.total_mined = ResourceProxy(self.state.stats.total_mined) self.mining_difficulty = ResourceProxy(self.state.mining_difficulty) self.mining_difficulty_increment = ResourceProxy(self.state.mining_difficulty_increment) self.tools = ItemProxy(self.state.tools, self.workshop, filter=lambda tool: tool.type == "tool") self.automata = ItemProxy(self.state.tools, self.workshop, filter=lambda tool: tool.type == "automaton") self.missions = ItemProxy(self.state.missions, self.mission_factory) self.fractions = FractionProxy(self.state.fractions) self.save_file = None def alert(self, msg, *args): """Add a message to the alert stack.""" self._messages.append(msg.format(*args)) @property def is_busy(self): """True if the player is currently mining or crafting.""" if self.state.action.task: if datetime.datetime.now() > self.state.action.completion.ToDatetime(): self.state.action.Clear() return False return True return False def add_mission(self, mission): """Add a new mission. Args: mission (str): Mission template to use. """ mission = self.missions.add(mission) if not mission.offer(self): self.missions.remove(mission) def complete_missions(self): """Check if any missions can be completed.""" for mission in self.missions: if mission.is_completed(self): self.missions.remove(mission) def craft(self, tool_name): """Craft a new tool by expending resources and time.""" item = self.workshop.get(tool_name) for resource, res_cost in item.cost.items(): self.resources.add(resource, -res_cost) self.tools.add(item) self._act("craft", tool_name, item.difficulty) self.alert("Crafted {}", item) return item.difficulty def research(self, project_name): """Research a new project by expending time.""" project = self.lab.get(project_name) self.state.research_completed.append(project.name) self._act("research", project_name, project.difficulty) self.alert("Researched {}.", project) self.lab.apply_effects(project) return project.difficulty def has_item(self, item_name): """True if the player has an instance of the tool in posession.""" for item in self.tools: if item.name == item_name: return True return False def _best_mining_tool(self, resource): """Return the (currently owned) tool that gives the highest bonus on mining a particular resource.""" if self.tools.is_empty: return None return max(self.tools, key=lambda item: item.mining_bonus.get(resource, 0)) def mine(self, resource): """Mine a resource.""" if self.is_busy: raise BusyException(self) difficulty = self.mining_difficulty.get(resource) total_wear = 0 efficiency = 0 events = [] while not self.tools.is_empty and total_wear < difficulty: tool = self._best_mining_tool(resource) if tool.condition <= (difficulty - total_wear): contribution = tool.condition / difficulty total_wear += tool.condition efficiency += tool.condition * tool.mining_bonus.get(resource, 1) / difficulty self.alert(f"Destroyed ${tool.name}$ while mining *{resource}*.") self.tools.remove(tool) else: contribution = (difficulty - total_wear) / difficulty tool.condition -= (difficulty - total_wear) total_wear = difficulty efficiency += contribution * tool.mining_bonus.get(resource, 1) for event, prob in tool.event_bonus.items(): if random() * contribution < prob: events.append(event) # Hand mining has efficiency of 1 efficiency += (difficulty - total_wear) / difficulty # Can only ever get integer results efficiency = int(efficiency) self.mining_difficulty.add(resource, self.mining_difficulty_increment.get(resource)) self._act("mine", resource, difficulty) self.resources.add(resource, efficiency) self.total_mined.add(resource, efficiency) self._unlock_items() self.alert(f"Mined *{efficiency} {resource}*.") self.events.trigger(*events) return difficulty, efficiency def _unlock_items(self): for item in self.workshop.available_items: if item.name not in self.state.tools_enabled: self.alert("You can now craft {}.", item) self.state.tools_enabled.append(item.name) for research in self.lab.available_items: if research.name not in self.state.research_enabled: self.alert("You can now research %{}%.", research.name) self.state.research_enabled.append(research.name) def _act(self, task, target, duration): if self.is_busy: raise BusyException(self) self.state.stats.total_game_duration += duration if self.state.debug: duration = 0 self.state.action.task = task self.state.action.target = str(target) self.state.action.completion.FromDatetime(datetime.datetime.now() + datetime.timedelta(seconds=duration)) @classmethod def load(cls, filename): """Load a game from a save file.""" with open(filename) as f: state = json_format.Parse(f.read(), GameState()) game = cls(state) game.save_file = filename return game @classmethod def create(cls, filename): """Create a new game.""" game = Game() game.state.mining_difficulty.clay = .5 game.state.mining_difficulty.ore = .5 game.state.mining_difficulty.energy = .5 game.state.mining_difficulty_increment.clay = .5 game.state.mining_difficulty_increment.ore = .5 game.state.mining_difficulty_increment.energy = .5 game.save_file = filename save_path, _ = os.path.split(filename) if save_path and not os.path.exists(save_path): os.makedirs(save_path) game.save() return game def save(self, filename=None): """Save a game to disk.""" with open(filename or self.save_file, 'w') as f: f.write(json_format.MessageToJson(self.state)) PK!>6shellcraft/tools.py# -*- coding: utf-8 -*- """Item Classes.""" from shellcraft.core import BaseItem, BaseFactory from shellcraft.game_state_pb2 import Tool as ToolPB class Tool(BaseItem): """Concept denoting any tool that the player can produce.""" @classmethod def from_dict(cls, name, data): tool = super(Tool, cls).from_dict(name, data) tool.type = data.get("type", "tool") tool.durability = data.get("durability", -1) tool.mining_bonus = data.get("mining_bonus", {}) tool.event_bonus = data.get("event_bonus", {}) tool.crafting_bonus = data.get("crafting_bonus", {}) tool.research_bonus = data.get("research_bonus", 0) return tool def __repr__(self): """Representation, e.g. 'clay_shovel (worn)'.""" if not hasattr(self, "condition") or self.durability == -1 or self.durability == self.condition: return f"${self.name}$" wear = 1.0 * self.condition / self.durability descriptions = { 1: "new", .9: "slightly used", .8: "used", .6: "worn", .3: "damaged", .15: "about to break", } for thresh, des in sorted(descriptions.items()): if wear < thresh: return f"${self.name}$ ({des})" class ToolFactory(BaseFactory): FIXTURES = "tools.yaml" ITEM_CLASS = Tool PB_CLASS = ToolPB def make(self, source): tool = super(ToolFactory, self).make(source) if not hasattr(tool, "condition"): tool.condition = tool.durability return tool def is_available(self, item_name): item = self.get(item_name) return item.name in self.game.state.tools_enabled or super(ToolFactory, self).is_available(item) PK!fBshellcraft/tutorial.py# -*- coding: utf-8 -*- """Basic CLI for ShellCraft.""" from shellcraft.core import BaseFactory, BaseItem from shellcraft._cli_impl import echo, echo_alerts class Step(BaseItem): def __repr__(self): return f"" class TutorialFactory(BaseFactory): FIXTURES = "tutorials.yaml" ITEM_CLASS = Step def get_next_step(self): step = self.get(self.game.state.tutorial_step) if not step or not self.is_available(step): return None return step def print_last_step(self): step = self.get(self.game.state.tutorial_step - 1) if step: echo(step.description) def cont(self): step = self.get_next_step() if step: self.game.state.tutorial_step += 1 self.apply_effects(step) self.game.save() echo(step.description) if self.game._messages: echo("") echo_alerts(self.game) return True PK![Ckkshellcraft/utils.py# coding=utf-8 """Utility methods.""" from random import random import datetime __author__ = "Manuel Ebert" __email__ = "manuel@1450.me" RESOURCE_WORTH = { 'clay': 1, 'ore': 2, 'energy': 4 } def to_date(delta_seconds=0): """Convert delta in seconds to ISO-8601 string.""" return (datetime.datetime.now() + datetime.timedelta(seconds=delta_seconds)).isoformat() def parse_isoformat(s): """Parse ISO-8601 string.""" return datetime.datetime.strptime(s, "%Y-%m-%dT%H:%M:%S.%f") def convert_resource_value(frm, to): """Return the market value of a resource to trade.""" return 1.0 * RESOURCE_WORTH[frm] / RESOURCE_WORTH[to] def to_list(string_or_list): """Encapsulate strings or numbers in a list.""" if not string_or_list: return [] if not isinstance(string_or_list, (list, tuple)): return [string_or_list] return string_or_list def to_float(s): """Convert anything to float. Examples: >>> to_float('.4') .4 >>> to_float('random(1, 9)') 6 """ if isinstance(s, str) and s.startswith("random"): low, high = map(float, s[6:].strip("()").split(",")) return low + random() * (high - low) return float(s) def format_name(npc): """Generate the display name of an NPC.""" if not npc or not npc.display: return "Anonymous" return npc.display.format( first=npc.first, middle=npc.middle, last=npc.last, title=npc.title, nickname=npc.nickname, first_initial=npc.first[0] + ".", middle_initial=npc.middle[0] + "." ).strip() PK!1\^shellcraft/world.py# coding=utf-8 """World generation tools.""" from __future__ import print_function from __future__ import unicode_literals from __future__ import absolute_import import random from shellcraft.grammar import Grammar from shellcraft.game_state_pb2 import NPC as NPCPB class NPCFactory(object): """A character in the game that may have a political affiliation, job, and attitude to the player.""" namer = Grammar.grammars['names'] @classmethod def make(cls, nobility_factor=8, fraction=None): """Generate a random NPC.""" gender = random.randint(0, 1) last = cls.namer.generate("@family") nobility = int((random.random() ** nobility_factor) * 4) nickname = cls.namer.generate("@nickname") if not nobility and random.random() < .1 else "" if gender: first = cls.namer.generate("@female") middle = cls.namer.generate("@female") title = ['', 'Lady', 'Baroness', 'Countess'][nobility] else: first = cls.namer.generate("@male") middle = cls.namer.generate("@male") title = ['', 'Lord', 'Baron', 'Earl'][nobility] display = cls.namer.generate("@display_nickname") if nickname else cls.namer.generate("@display") return NPCPB( first=first, middle=middle, last=last, title=title, nickname=nickname, display=display ) PK!H76/6,shellcraft-0.11.0.dist-info/entry_points.txtN+I/N.,()*HI.JL+-.JCps2r3PK! r00#shellcraft-0.11.0.dist-info/LICENSE MIT License Copyright (c) 2017, Manuel Ebert 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. PK!Hu)GTU!shellcraft-0.11.0.dist-info/WHEEL HM K-*ϳR03rOK-J,/R(O-)$qzd&Y)r$UV&UrPK!H$nm$shellcraft-0.11.0.dist-info/METADATASO0&i)*HT*[ՖIS5 7&INܦc0Oq{E'f‰++U!o"lY1w6oQT<1 -d@ 3XuE uFhЀ9m0N8HS.U3/c,,o{cvYTzE\{Z3\,|U>9!,A *B?nn&sԱQ_F=ڨ>Z ޔ Js/$Q:o73Nuę`gxЪR3ʩi9?]-?0vݰ)S5-#7.zw66R;NUJ9bZMm~t7bYhuW7חWL6yƾE`yH}ALUVKm> >||V_R2|Ln*Ɍn2_ LFUxѝJWÓlopmANd7k4{Rw@SaY#ia2/N ,s!iAo'M%f32\p]={ i_U7Aul]ӌkЗ򲞰~PK!Hc<  "shellcraft-0.11.0.dist-info/RECORD}ɖX}? d 3 "mY}j#"Nq2kdGVm5G@2[^1E^z˭^Y< W<Ɏnn_?,i͋]4;Ocr5ۼۜۻfJ%4, {] oLf,`&e`KWiGJg:ݦ篦腣NvM-[6f!3RD^\*D6`Gr^_wyoyQ/5bp|o(}bp&uCBa<`F]Gӡ 5 A T,azL}v|g?9k]Ot$l/Kxa8DGЩf l#}#!JkE+XϖΡqfb\P]Jr#7.Y8N;; )r t#ܥP[U{f;|4lQ  l3q+Vqvn]hZQjF#Ba߇dӔ PIgL)ƴWRml%-rٰ:.K0MTm#nU30u浢iswnyD F}w8G`iSe/ QB$,]plC6fѐo>BFO5\'LE tD؆ ̖U*NB$#݂ou{ѝ}zа!tढ़llOEPApU 4{ \SxJ.-ă>@"!!K [j*$:dmصv|EXШg6o7俠~a 匫1R 3v=z#Boʖ$'n4ZxU$9b֞W+D6N%#P#}%+us8VIކK'qGQ c{W>npV 0~ >(͹1 >-+_eG?7 wLꄏ܉1S!jn*꜈4M#Bv~;|:'z<'#=B`|>&tfPwgm~_JQra"X: D"[m>_e%-5,<4{~>BQko^N28}=alMϛ94 ǒ,8kGp -g^s^)аPƏ0vc" {'C,z+MyBwҊ6RMĚ,4}?Ф/yޯƲ(ͩG0Tpri&} >pHqB6~<;<¾L?i+vTH8*0S!u,ҷ!~ Ebyܠd]'ړ":cKRr[v=;a9 O"+uSF%4Ԩurʩi%XTަ9ڦ?iPcnިgl ]>Rep|Pgl̳hoqguC?PK!uhshellcraft/__init__.pyPK!H:fL L Lshellcraft/_cli_impl.pyPK!09  shellcraft/_colors.pyPK!]b  ,shellcraft/automata.pyPK!١k!9shellcraft/cli.pyPK!**Xshellcraft/core.pyPK!N*PFwwÃshellcraft/data/events.yamlPK!k呤sshellcraft/data/fractions.yamlPK!e   Sshellcraft/data/game_state.protoPK!|S#shellcraft/data/guild_names.grammarPK!Ҕ|shellcraft/data/letter.grammarPK! -''shellcraft/data/missions.yamlPK!˹=SS shellcraft/data/names.grammarPK!OБ((shellcraft/data/research.yamlPK!BV1iXshellcraft/data/tools.yamlPK!fT^shellcraft/data/tutorials.yamlPK!!R "" shellcraft/epithets.pyPK!1,shellcraft/events.pyPK!Ha??.shellcraft/exceptions.pyPK!MM2shellcraft/fractions.pyPK!\u``5shellcraft/game_state_pb2.pyPK!f}gUU–shellcraft/grammar.pyPK!/^Jshellcraft/missions.pyPK!t/shellcraft/research.pyPK!REWtt.shellcraft/shellcraft.pyPK!>6shellcraft/tools.pyPK!fBshellcraft/tutorial.pyPK![Ckk"shellcraft/utils.pyPK!1\^shellcraft/world.pyPK!H76/6,shellcraft-0.11.0.dist-info/entry_points.txtPK! r00#shellcraft-0.11.0.dist-info/LICENSEPK!Hu)GTU!shellcraft-0.11.0.dist-info/WHEELPK!H$nm$shellcraft-0.11.0.dist-info/METADATAPK!Hc<  "shellcraft-0.11.0.dist-info/RECORDPK"" *