Пример #1
0
 def uuid(self) -> str:
     # TODO: generate uuid from chaining options?
     uuid = "tw-game-{specs}-{grammar}-{seeds}"
     uuid = uuid.format(specs=encode_seeds((self.nb_rooms, self.nb_objects, self.quest_length, self.quest_breadth)),
                        grammar=self.grammar.uuid,
                        seeds=encode_seeds([self.seeds[k] for k in sorted(self._seeds)]))
     return uuid
Пример #2
0
 def uuid(self) -> str:
     # TODO: incomplete, finish this
     uuid = "tw-game-{specs}-{surface}-{seeds}"
     uuid = uuid.format(
         specs=encode_seeds((self.quest_gen_options.max_depth,
                             self.quest_gen_options.max_steps)),
         surface=self.surface_gen_options.uuid,
         seeds=encode_seeds([self.seeds[k] for k in sorted(self._seeds)]))
     return uuid
 def _make_game(self, slot_seed: int, attempt_seed: int) -> str:
     options = LabGameOptions()
     options.seeds = attempt_seed
     game = make_game_from_level(self.level, options)
     hashid = encode_seeds(
         [self.generator_seed, self.level] +
         [options.seeds[k] for k in sorted(options.seeds)])
     # slot seed in file name so we can reconstruct game collection
     # map upon loading an existing directory
     game_name = "{}_{}_{}".format(self.spec.id, hashid, slot_seed)
     options.path = str(self.output_dir / (game_name + ".ulx"))
     game_file = tw_textlabs.generator.compile_game(game, options)
     return game_file
Пример #4
0
def make_lab_game(options: LabGameOptions) -> Game:

    """
    Make a lab game (map + devices + quest).

    Arguments:
    nb_devices: Number of devices in the world.
    : Options for the grammar.

    Returns:
    Generated game.
    """

    max_quest_length = options.quest_gen_options.max_depth
    surface_gen_options = options.surface_gen_options
    max_search_steps = options.quest_gen_options.max_steps

    seeds = options.seeds
    metadata = {}  # Collect infos for reproducibility.
    metadata["desc"] = "Lab-Game"
    metadata["mode"] = "custom"
    metadata["seeds"] = seeds
    metadata["world_size"] = 1 # Single room always 
    metadata["max_quest_length"] = max_quest_length

    rng_quest = options.rngs['quest']

    sg = SurfaceGenerator(seed=seeds['surface'],
                        surface_gen_options=surface_gen_options)
    
    devices = options.lab_config.devices
    material_states = options.lab_config.material_states
    min_uses_per_device = options.lab_config.min_uses_per_device
    max_uses_per_device = options.lab_config.max_uses_per_device
    device_types = uniquify(KnowledgeBase.default().types.descendants('sa'))
    
    M = LabGameMaker(surface_generator=sg)
    # Create the lab room.
    lab = M.new_room("lab")
    # - Describe the room.
    lab.desc = "The lab is a magical place where you'll learn materials synthesis!"
    
    # Used so that Inform7 will be able to reference names of any of the
    # devices (needed for the single argument hack), but we only want the player
    # to be able to interact with the chosen ones.
    limbo = M.new_room("Limbo")
    limbo.desc = "Storeroom for unused devices."
    
    M.set_player(lab)
    
    # Add materials to the lab.
    for mat_state in material_states:
        mat = M.new_lab_entity('m')
        mat.add_property(mat_state)
        lab.add(mat)
        
    if options.lab_config.lab_container_available:
        # Add lab_container to the world.
        lab_container = M.new_lab_container()
        lab.add(lab_container)
        
    
    # Add devices to the world. We add all device types but only the available
    # ones to the lab room
    for device_type in device_types:
        device = M.new_lab_entity(device_type)
        if device_type in devices:
            lab.add(device)
        else:
            limbo.add(device)
            
            
    quest_gen_options = QuestGenerationOptions(max_depth=max_quest_length,
                            max_steps=max_search_steps,
                            win_condition=WinConditionType.ALL,
                            quest_rng=rng_quest,
                            min_uses_per_device=min_uses_per_device,
                            max_uses_per_device=max_uses_per_device
                            )
    
    quest = M.generate_quest_surface_pair(quest_gen_options)
    game = M.build()
    game.metadata = metadata
    # TODO add uuid based on rest of settings
    uuid = "tw-custom-lab-game-{specs}-{surface_gen}-{seeds}"
    uuid = uuid.format(specs=encode_seeds((quest_gen_options.max_depth, max_search_steps)), 
                        surface_gen=surface_gen_options.uuid,
                       seeds=encode_seeds([seeds[k] for k in sorted(seeds)]))
    game.metadata["uuid"] = uuid
    return game
Пример #5
0
def make_game(mode: str, options: GameOptions) -> tw_textlabs.Game:
    """ Make a Coin Collector game.

    Arguments:
        mode: Mode for the game where

              * `'simple'`: the distractor rooms are only placed orthogonaly
                to the chain. This means moving off the optimal path leads
                immediately to a dead end.
              * `'random'`: the distractor rooms are randomly place along the
                chain. This means a player can wander for a while before
                reaching a dead end.
        options:
            For customizing the game generation (see
            :py:class:`tw_textlabs.GameOptions <tw_textlabs.generator.game.GameOptions>`
            for the list of available options).

    Returns:
        Generated game.
    """
    if mode == "simple" and float(options.nb_rooms) / options.quest_length > 4:
        msg = ("Total number of rooms must be less than 4 * `quest_length` "
               "when distractor mode is 'simple'.")
        raise ValueError(msg)

    metadata = {}  # Collect infos for reproducibility.
    metadata["desc"] = "Coin Collector"
    metadata["mode"] = mode
    metadata["seeds"] = options.seeds
    metadata["world_size"] = options.nb_rooms
    metadata["quest_length"] = options.quest_length

    rngs = options.rngs
    rng_map = rngs['map']
    rng_grammar = rngs['grammar']

    # Generate map.
    M = tw_textlabs.GameMaker()
    M.grammar = tw_textlabs.generator.make_grammar(options.grammar,
                                                   rng=rng_grammar)

    rooms = []
    walkthrough = []
    for i in range(options.quest_length):
        r = M.new_room()
        if i >= 1:
            # Connect it to the previous rooms.
            free_exits = [
                k for k, v in rooms[-1].exits.items() if v.dest is None
            ]
            src_exit = rng_map.choice(free_exits)
            dest_exit = reverse_direction(src_exit)
            M.connect(rooms[-1].exits[src_exit], r.exits[dest_exit])
            walkthrough.append("go {}".format(src_exit))

        rooms.append(r)

    M.set_player(rooms[0])

    # Add object the player has to pick up.
    obj = M.new(type="o", name="coin")
    rooms[-1].add(obj)

    # Add distractor rooms, if needed.
    chain_of_rooms = list(rooms)
    while len(rooms) < options.nb_rooms:
        if mode == "random":
            src = rng_map.choice(rooms)
        else:
            # Add one distractor room per room along the chain.
            src = chain_of_rooms[len(rooms) % len(chain_of_rooms)]

        free_exits = [k for k, v in src.exits.items() if v.dest is None]
        if len(free_exits) == 0:
            continue

        dest = M.new_room()
        src_exit = rng_map.choice(free_exits)
        dest_exit = reverse_direction(src_exit)
        M.connect(src.exits[src_exit], dest.exits[dest_exit])
        rooms.append(dest)

    # Generate the quest thats by collecting the coin.
    walkthrough.append("take coin")
    # TODO: avoid compiling the game at all (i.e. use the inference engine).
    M.set_quest_from_commands(walkthrough)

    game = M.build()
    game.metadata = metadata
    mode_choice = 0 if mode == "simple" else 1
    uuid = "tw-coin_collector-{specs}-{grammar}-{seeds}"
    uuid = uuid.format(specs=encode_seeds(
        (mode_choice, options.nb_rooms, options.quest_length)),
                       grammar=options.grammar.uuid,
                       seeds=encode_seeds(
                           [options.seeds[k] for k in sorted(options.seeds)]))
    game.metadata["uuid"] = uuid
    return game
Пример #6
0
def make_game(mode: str, lab_game_options: LabGameOptions) -> tw_textlabs.Game:
    """ Make a TextLab game.

    Arguments:
        mode: Difficulty mode, where the different modes correspond to:
        lab_game_options: Options for customizing the game generation.

    Returns:
        game: Generated game.

    Notes:
        Difficulty modes are defined as follows:

        *  easy: nb. operations ranging from 1 to 2, 
        nb. materials ranging from 2 to 3 as the difficulty increases.
        Max number of descriptors per entity is 1;
        * medium: nb. operations ranging from 3 to 5, 
        nb. materials ranging from 3 to 4 as the difficulty increases.
        Max number of descriptors per entity is 2;
        * hard: nb. operations ranging from 4 to 6, 
        nb. materials ranging from 5 to 8 as the difficulty increases.
        Max number of descriptors per entity is 3;


    Returns:
        Generated game.
    """

    quest_gen_options = lab_game_options.quest_gen_options
    max_quest_length = quest_gen_options.max_depth
    surface_gen_options = lab_game_options.surface_gen_options
    quest_gen_options.win_condition = WinConditionType.ALL

    # Deal with any missing random seeds.
    seeds = get_seeds_for_game_generation(lab_game_options.seeds)

    rng_objects = lab_game_options.rngs['objects']
    rng_quest = lab_game_options.rngs['quest']

    n_materials = lab_game_options.quest_gen_options.nb_materials
    n_ops = lab_game_options.quest_gen_options.nb_ops

    all_op_types = KnowledgeBase.default().types.descendants('toe')
    chosen_op_types = rng_objects.choice(all_op_types, n_ops, replace=True)

    if lab_game_options.preset_ops:
        surface_gen_options.op_type_map = {
            'tlq_op_{}'.format(i): chosen_op_types[i]
            for i in range(n_ops)
        }

    sg = SurfaceGenerator(surface_gen_options=surface_gen_options)
    quest_gen_options.quest_rng = rng_quest

    modes = ["easy", "medium", "hard"]
    mode_idx = modes.index(mode)

    metadata = {}  # Collect infos for reproducibility.
    metadata["orig_seed"] = lab_game_options.seeds
    metadata["desc"] = "Lab-Game"
    metadata["mode"] = mode
    metadata["seeds"] = seeds
    metadata["world_size"] = 1  # Single room always
    metadata["max_quest_length"] = max_quest_length
    metadata["surface_gen_options"] = surface_gen_options.serialize()

    if mode == "easy":
        material_states = ["powder"]
    elif mode == "medium":
        material_states = rng_objects.choice(list(MATERIAL_STATES),
                                             n_materials,
                                             replace=True)
    elif mode == "hard":
        material_states = list(MATERIAL_STATES)

    M = LabGameMaker(surface_generator=sg)

    # Create the lab room.
    lab = M.new_room("lab")

    M.set_player(lab)

    # - Describe the room.
    lab.desc = "The lab is a magical place where you'll learn materials synthesis"

    # Add materials to the world.
    ent_type = 'm'
    n_max_descs = quest_gen_options.max_descs_per_ent[ent_type]
    for i in range(n_materials):
        mat_state = rng_objects.choice(material_states)
        mat = M.new_lab_entity('m')
        mat.add_property(mat_state)
        lab.add(mat)
        n_descs = rng_objects.randint(0, (n_max_descs + 1))
        for j in range(n_descs):
            mdesc = M.new_lab_entity('mdsc')
            lab.add(mdesc)
            quest_gen_options.ent_desc_map[mat.var.name].append(mdesc.var.name)

    # Add operations and their descriptors
    ent_type = 'tlq_op'
    n_max_descs = quest_gen_options.max_descs_per_ent[ent_type]
    ops = []

    # Either preset ops
    if lab_game_options.preset_ops:
        ops = [
            M.new_tlq_op(dynamic_define=False, op_type=chosen_op_types[i])
            for i in range(n_ops)
        ]
        op_type_map = {
            op.var.name: chosen_op_types[i]
            for i, op in enumerate(ops)
        }
        assert op_type_map == surface_gen_options.op_type_map
    else:  # or make their classification part of the quest
        ops = [M.new_tlq_op(dynamic_define=True) for i in range(n_ops)]

    for op in ops:
        lab.add(op)
        n_descs = rng_objects.randint(0, (n_max_descs + 1))
        for j in range(n_descs):
            odesc = M.new_lab_entity('odsc')
            lab.add(odesc)
            quest_gen_options.ent_desc_map[op.var.name].append(odesc.var.name)

    # Generate quest and corresponding surface (set in quest description)
    quest = M.generate_quest_surface_pair(quest_gen_options)
    game = M.build()
    game.metadata = metadata
    mode_choice = modes.index(mode)

    # TODO add uuid based on rest of settings
    uuid = "tw-lab_game-{specs}-{surface_gen}-{seeds}"
    uuid = uuid.format(specs=encode_seeds(
        (mode_choice, quest_gen_options.max_depth)),
                       surface_gen=surface_gen_options.uuid,
                       seeds=encode_seeds([seeds[k] for k in sorted(seeds)]))
    game.metadata["uuid"] = uuid
    return game
Пример #7
0
def make_game(mode: str, options: GameOptions) -> tw_textlabs.Game:
    """ Make a Treasure Hunter game.

    Arguments:
        mode: Mode for the game where

              * `'easy'`: rooms are all empty except where the two objects are
                placed. Also, connections between rooms have no door.
              * `'medium'`: adding closed doors and containers that might need
                to be open in order to find the object.
              * `'hard'`: adding locked doors and containers (necessary keys
                will in the inventory) that might need to be unlocked (and open)
                in order to find the object.
        options:
            For customizing the game generation (see
            :py:class:`tw_textlabs.GameOptions <tw_textlabs.generator.game.GameOptions>`
            for the list of available options).

    Returns:
        Generated game.
    """
    kb = KnowledgeBase.default()

    metadata = {}  # Collect infos for reproducibility.
    metadata["desc"] = "Treasure Hunter"
    metadata["mode"] = mode
    metadata["seeds"] = options.seeds
    metadata["world_size"] = options.nb_rooms
    metadata["quest_length"] = options.quest_length

    rngs = options.rngs
    rng_map = rngs['map']
    rng_objects = rngs['objects']
    rng_quest = rngs['quest']
    rng_grammar = rngs['grammar']

    modes = ["easy", "medium", "hard"]
    if mode == "easy":
        door_states = None
        n_distractors = 0
    elif mode == "medium":
        door_states = ["open", "closed"]
        n_distractors = 10
    elif mode == "hard":
        door_states = ["open", "closed", "locked"]
        n_distractors = 20

    # Generate map.
    map_ = tw_textlabs.generator.make_map(n_rooms=options.nb_rooms, rng=rng_map,
                                        possible_door_states=door_states)
    assert len(map_.nodes()) == options.nb_rooms

    world = World.from_map(map_)

    # Randomly place the player.
    starting_room = None
    if len(world.rooms) > 1:
        starting_room = rng_map.choice(world.rooms)

    world.set_player_room(starting_room)
    # Add object the player has to pick up.
    types_counts = kb.types.count(world.state)
    obj_type = kb.types.sample(parent_type='o', rng=rng_objects,
                                       include_parent=True)
    var_id = get_new(obj_type, types_counts)
    right_obj = Variable(var_id, obj_type)
    world.add_fact(Proposition("in", [right_obj, world.inventory]))

    # Add containers and supporters to the world.
    types_counts = kb.types.count(world.state)
    objects = []
    distractor_types = uniquify(['c', 's'] +
                                kb.types.descendants('c') +
                                kb.types.descendants('s'))
    for i in range(n_distractors):
        obj_type = rng_objects.choice(distractor_types)
        var_id = get_new(obj_type, types_counts)  # This update the types_counts.
        objects.append(Variable(var_id, obj_type))

    world.populate_with(objects, rng=rng_objects)

    # Add object the player should not pick up.
    types_counts = kb.types.count(world.state)
    obj_type = kb.types.sample(parent_type='o', rng=rng_objects,
                                       include_parent=True)
    var_id = get_new(obj_type, types_counts)
    wrong_obj = Variable(var_id, obj_type)
    # Place it anywhere in the world.
    world.populate_with([wrong_obj], rng=rng_objects)

    # Generate a quest that finishes by taking something (i.e. the right
    #  object since it's the only one in the inventory).
    options.chaining.rules_per_depth = [kb.rules.get_matching("take.*")]
    options.chaining.backward = True
    options.chaining.rng = rng_quest
    #options.chaining.restricted_types = exceptions
    #exceptions = ["r", "c", "s", "d"] if mode == "easy" else ["r"]
    chain = tw_textlabs.generator.sample_quest(world.state, options.chaining)

    # Add objects needed for the quest.
    world.state = chain.initial_state
    event = Event(chain.actions)
    quest = Quest(win_events=[event],
                  fail_events=[Event(conditions={Proposition("in", [wrong_obj, world.inventory])})])

    grammar = tw_textlabs.generator.make_grammar(options.grammar, rng=rng_grammar)
    game = tw_textlabs.generator.make_game_with(world, [quest], grammar)
    game.metadata = metadata
    mode_choice = modes.index(mode)
    uuid = "tw-treasure_hunter-{specs}-{grammar}-{seeds}"
    uuid = uuid.format(specs=encode_seeds((mode_choice, options.nb_rooms, options.quest_length)),
                       grammar=options.grammar.uuid,
                       seeds=encode_seeds([options.seeds[k] for k in sorted(options.seeds)]))
    game.metadata["uuid"] = uuid
    return game