Пример #1
0
    def test_one_choice__marked(self):
        start = facts.Start(uid="start", type="test", nesting=0)
        choice_1 = facts.Choice(uid="choice_1")
        finish_1 = facts.Finish(uid="finish_1", results={}, nesting=0, start="start")
        finish_2 = facts.Finish(uid="finish_2", results={}, nesting=0, start="start")
        marked_option = facts.Option(
            state_from=choice_1.uid, state_to=finish_2.uid, type="opt_2", markers=[relations.OPTION_MARKERS.HONORABLE]
        )

        facts_list = [
            start,
            choice_1,
            finish_1,
            finish_2,
            facts.Option(state_from=choice_1.uid, state_to=finish_1.uid, type="opt_1", markers=()),
            marked_option,
        ]
        self.kb += facts_list

        for i in xrange(100):
            transformators.determine_default_choices(self.kb, preferred_markers=[relations.OPTION_MARKERS.HONORABLE])
            self.check_in_knowledge_base(self.kb, facts_list)
            self.assertEqual(len(list(self.kb.filter(facts.OptionsLink))), 0)
            self.assertEqual(len(list(self.kb.filter(facts.ChoicePath))), 1)
            self.assertEqual(self.kb.filter(facts.ChoicePath).next().option, marked_option.uid)

            self.kb -= self.kb.filter(facts.ChoicePath)
Пример #2
0
    def test_two_choices(self):
        start = facts.Start(uid="start", type="test", nesting=0)
        choice_1 = facts.Choice(uid="choice_1")
        choice_2 = facts.Choice(uid="choice_2")
        finish_1 = facts.Finish(uid="finish_1", results={}, nesting=0, start="start")
        finish_2 = facts.Finish(uid="finish_2", results={}, nesting=0, start="start")

        option_1 = facts.Option(state_from=choice_1.uid, state_to=finish_1.uid, type="opt_1", markers=())
        option_2 = facts.Option(state_from=choice_1.uid, state_to=choice_2.uid, type="opt_2", markers=())

        option_2_1 = facts.Option(state_from=choice_2.uid, state_to=finish_1.uid, type="opt_2_1", markers=())
        option_2_2 = facts.Option(state_from=choice_2.uid, state_to=finish_2.uid, type="opt_2_2", markers=())

        facts_list = [start, choice_1, choice_2, finish_1, finish_2, option_1, option_2, option_2_1, option_2_2]

        self.kb += facts_list

        for i in xrange(100):
            transformators.determine_default_choices(self.kb)
            self.check_in_knowledge_base(self.kb, facts_list)
            self.assertEqual(len(list(self.kb.filter(facts.OptionsLink))), 0)
            self.assertEqual(len(list(self.kb.filter(facts.ChoicePath))), 2)
            self.assertEqual(len(set([path.choice for path in self.kb.filter(facts.ChoicePath)])), 2)

            self.kb -= self.kb.filter(facts.ChoicePath)
Пример #3
0
def _create_random_quest_for_hero(hero_info,
                                  start_quests,
                                  without_restrictions=False):
    knowledge_base = get_knowledge_base(
        hero_info, without_restrictions=without_restrictions)

    selector = Selector(knowledge_base,
                        QUESTS_BASE,
                        social_connection_probability=0)

    hero_uid = uids.hero(hero_info.id)

    quests_facts = selector.create_quest_from_place(
        nesting=0,
        initiator_position=selector.place_for(objects=(hero_uid, )),
        allowed=start_quests,
        excluded=[],
        tags=('can_start', ))

    knowledge_base += quests_facts

    transformators.activate_events(
        knowledge_base)  # TODO: after remove restricted states
    transformators.remove_restricted_states(knowledge_base)
    transformators.remove_broken_states(
        knowledge_base)  # MUST be called after all graph changes
    transformators.determine_default_choices(
        knowledge_base, preferred_markers=hero_info.prefered_quest_markers
    )  # MUST be called after all graph changes and on valid graph
    transformators.remove_unused_actors(knowledge_base)

    knowledge_base.validate_consistency(WORLD_RESTRICTIONS)
    knowledge_base.validate_consistency(QUEST_RESTRICTIONS)

    return knowledge_base
Пример #4
0
def _create_random_quest_for_hero(hero_info, start_quests, without_restrictions=False):
    knowledge_base = get_knowledge_base(hero_info, without_restrictions=without_restrictions)

    selector = Selector(knowledge_base, QUESTS_BASE, social_connection_probability=c.QUESTS_SOCIAL_CONNECTIONS_FRACTION)

    hero_uid = uids.hero(hero_info.id)

    quests_facts = selector.create_quest_from_place(nesting=0,
                                                    initiator_position=selector.place_for(objects=(hero_uid,)),
                                                    allowed=start_quests,
                                                    excluded=[],
                                                    tags=('can_start', ))

    knowledge_base += quests_facts

    transformators.activate_events(knowledge_base) # TODO: after remove restricted states
    transformators.remove_restricted_states(knowledge_base)
    transformators.remove_broken_states(knowledge_base) # MUST be called after all graph changes
    transformators.determine_default_choices(knowledge_base, preferred_markers=hero_info.prefered_quest_markers) # MUST be called after all graph changes and on valid graph
    transformators.remove_unused_actors(knowledge_base)

    knowledge_base.validate_consistency(WORLD_RESTRICTIONS)
    knowledge_base.validate_consistency(QUEST_RESTRICTIONS)

    return knowledge_base
Пример #5
0
    def test_one_choice(self):
        start = facts.Start(uid='start', type='test', nesting=0)
        choice_1 = facts.Choice(uid='choice_1')
        finish_1 = facts.Finish(uid='finish_1',
                                results={},
                                nesting=0,
                                start='start')
        finish_2 = facts.Finish(uid='finish_2',
                                results={},
                                nesting=0,
                                start='start')

        option_1 = facts.Option(state_from=choice_1.uid,
                                state_to=finish_1.uid,
                                type='opt_1',
                                markers=())
        option_2 = facts.Option(state_from=choice_1.uid,
                                state_to=finish_2.uid,
                                type='opt_2',
                                markers=())

        facts_list = [start, choice_1, finish_1, finish_2, option_1, option_2]
        self.kb += facts_list
        transformators.determine_default_choices(self.kb)
        self.check_in_knowledge_base(self.kb, facts_list)
        self.assertEqual(len(list(self.kb.filter(facts.OptionsLink))), 0)
        self.assertEqual(len(list(self.kb.filter(facts.ChoicePath))), 1)
        self.assertEqual(
            len(set([path.choice
                     for path in self.kb.filter(facts.ChoicePath)])), 1)
        self.assertTrue(
            self.kb.filter(facts.ChoicePath).next().option in set(
                [option_1.uid, option_2.uid]))
Пример #6
0
    def test_linked_choices(self):
        start = facts.Start(uid='start', type='test', nesting=0)
        choice_1 = facts.Choice(uid='choice_1')
        choice_2 = facts.Choice(uid='choice_2')
        finish_1 = facts.Finish(uid='finish_1', results={}, nesting=0, start='start')
        finish_2 = facts.Finish(uid='finish_2', results={}, nesting=0, start='start')

        option_1 = facts.Option(state_from=choice_1.uid, state_to=finish_1.uid, type='opt_1', markers=())
        option_2 = facts.Option(state_from=choice_1.uid, state_to=choice_2.uid, type='opt_2', markers=())

        option_2_1 = facts.Option(state_from=choice_2.uid, state_to=finish_1.uid, type='opt_2_1', markers=())
        option_2_2 = facts.Option(state_from=choice_2.uid, state_to=finish_2.uid, type='opt_2_2', markers=())

        facts_list = [ start,
                  choice_1,
                  choice_2,
                  finish_1,
                  finish_2,

                  option_1,
                  option_2,
                  option_2_1,
                  option_2_2,

                  facts.OptionsLink(options=(option_1.uid, option_2_1.uid)),
                  facts.OptionsLink(options=(option_2.uid, option_2_2.uid))]

        self.kb += facts_list
        transformators.determine_default_choices(self.kb)
        self.check_in_knowledge_base(self.kb, facts_list)
        self.assertEqual(len(list(self.kb.filter(facts.ChoicePath))), 2)
        self.assertEqual(len(set([path.choice for path in self.kb.filter(facts.ChoicePath)])), 2)

        chosen_options = set([path.option for path in self.kb.filter(facts.ChoicePath)])
        self.assertTrue(chosen_options == set([option_1.uid, option_2_1.uid]) or chosen_options == set([option_2.uid, option_2_2.uid]) )
Пример #7
0
    def test_one_choice__no_markers(self):
        start = facts.Start(uid='start', type='test', nesting=0)
        choice_1 = facts.Choice(uid='choice_1')
        finish_1 = facts.Finish(uid='finish_1', results={}, nesting=0, start='start')
        finish_2 = facts.Finish(uid='finish_2', results={}, nesting=0, start='start')

        option_1 = facts.Option(state_from=choice_1.uid, state_to=finish_1.uid, type='opt_1', markers=[relations.OPTION_MARKERS.DISHONORABLE])
        option_2 = facts.Option(state_from=choice_1.uid, state_to=finish_2.uid, type='opt_2', markers=[relations.OPTION_MARKERS.AGGRESSIVE])

        facts_list = [ start,
                       choice_1,
                       finish_1,
                       finish_2,
                       option_1,
                       option_2 ]

        self.kb += facts_list

        for i in xrange(100):
            transformators.determine_default_choices(self.kb, preferred_markers=[relations.OPTION_MARKERS.HONORABLE])
            self.check_in_knowledge_base(self.kb, facts_list)
            self.assertEqual(len(list(self.kb.filter(facts.OptionsLink))), 0)
            self.assertEqual(len(list(self.kb.filter(facts.ChoicePath))), 1)
            self.assertEqual(len(set([path.choice for path in self.kb.filter(facts.ChoicePath)])), 1)
            self.assertTrue(self.kb.filter(facts.ChoicePath).next().option in set([option_1.uid, option_2.uid]))

            self.kb -= self.kb.filter(facts.ChoicePath)
Пример #8
0
    def test_linked_choices__linked_option_with_processed_choice(self):
        is_raised = False

        for i in xrange(100):
            kb = KnowledgeBase()
            start = facts.Start(uid='start', type='test', nesting=0)
            choice_1 = facts.Choice(uid='choice_1')
            choice_2 = facts.Choice(uid='choice_2')
            finish_1 = facts.Finish(uid='finish_1',
                                    results={},
                                    nesting=0,
                                    start='start')
            finish_2 = facts.Finish(uid='finish_2',
                                    results={},
                                    nesting=0,
                                    start='start')

            option_1 = facts.Option(state_from=choice_1.uid,
                                    state_to=finish_1.uid,
                                    type='opt_1',
                                    markers=())
            option_2 = facts.Option(state_from=choice_1.uid,
                                    state_to=choice_2.uid,
                                    type='opt_2',
                                    markers=())

            option_2_1 = facts.Option(state_from=choice_2.uid,
                                      state_to=finish_1.uid,
                                      type='opt_2_1',
                                      markers=())
            option_2_2 = facts.Option(state_from=choice_2.uid,
                                      state_to=finish_2.uid,
                                      type='opt_2_2',
                                      markers=())

            facts_list = [
                start,
                facts.Jump(state_from=start.uid,
                           state_to=choice_1.uid), choice_1, choice_2,
                finish_1, finish_2, option_1, option_2, option_2_1, option_2_2,
                facts.OptionsLink(options=(option_2.uid, option_2_2.uid))
            ]

            kb += facts_list

            try:
                transformators.determine_default_choices(kb)
            except exceptions.LinkedOptionWithProcessedChoiceError:
                is_raised = True
                self.assertEqual(len(list(kb.filter(facts.ChoicePath))), 1)
                self.assertEqual(
                    [path.choice for path in kb.filter(facts.ChoicePath)],
                    [choice_2.uid])
                self.assertEqual(
                    [path.option for path in kb.filter(facts.ChoicePath)],
                    [option_2_1.uid])
                break

        self.assertFalse(is_raised)
Пример #9
0
 def test_no_choices(self):
     facts_list = [ facts.Start(uid='start', type='test', nesting=0),
               facts.Finish(uid='st_finish', results={}, nesting=0, start='start'),
               facts.Jump(state_from='start', state_to='st_finish') ]
     self.kb += facts_list
     transformators.determine_default_choices(self.kb)
     self.check_in_knowledge_base(self.kb, facts_list)
     self.assertEqual(len(list(self.kb.filter(facts.OptionsLink))), 0)
Пример #10
0
 def test_no_choices(self):
     facts_list = [
         facts.Start(uid='start', type='test', nesting=0),
         facts.Finish(uid='st_finish', results={}, nesting=0,
                      start='start'),
         facts.Jump(state_from='start', state_to='st_finish')
     ]
     self.kb += facts_list
     transformators.determine_default_choices(self.kb)
     self.check_in_knowledge_base(self.kb, facts_list)
     self.assertEqual(len(list(self.kb.filter(facts.OptionsLink))), 0)
Пример #11
0
    def test_one_choice__no_markers(self):
        start = facts.Start(uid='start', type='test', nesting=0)
        choice_1 = facts.Choice(uid='choice_1')
        finish_1 = facts.Finish(uid='finish_1',
                                results={},
                                nesting=0,
                                start='start')
        finish_2 = facts.Finish(uid='finish_2',
                                results={},
                                nesting=0,
                                start='start')

        option_1 = facts.Option(
            state_from=choice_1.uid,
            state_to=finish_1.uid,
            type='opt_1',
            markers=[relations.OPTION_MARKERS.DISHONORABLE])
        option_2 = facts.Option(state_from=choice_1.uid,
                                state_to=finish_2.uid,
                                type='opt_2',
                                markers=[relations.OPTION_MARKERS.AGGRESSIVE])

        facts_list = [start, choice_1, finish_1, finish_2, option_1, option_2]

        self.kb += facts_list

        for i in xrange(100):
            transformators.determine_default_choices(
                self.kb,
                preferred_markers=[relations.OPTION_MARKERS.HONORABLE])
            self.check_in_knowledge_base(self.kb, facts_list)
            self.assertEqual(len(list(self.kb.filter(facts.OptionsLink))), 0)
            self.assertEqual(len(list(self.kb.filter(facts.ChoicePath))), 1)
            self.assertEqual(
                len(
                    set([
                        path.choice
                        for path in self.kb.filter(facts.ChoicePath)
                    ])), 1)
            self.assertTrue(
                self.kb.filter(facts.ChoicePath).next().option in set(
                    [option_1.uid, option_2.uid]))

            self.kb -= self.kb.filter(facts.ChoicePath)
Пример #12
0
    def test_linked_choices__linked_option_with_processed_choice(self):
        is_raised = False

        for i in xrange(100):
            kb = KnowledgeBase()
            start = facts.Start(uid="start", type="test", nesting=0)
            choice_1 = facts.Choice(uid="choice_1")
            choice_2 = facts.Choice(uid="choice_2")
            finish_1 = facts.Finish(uid="finish_1", results={}, nesting=0, start="start")
            finish_2 = facts.Finish(uid="finish_2", results={}, nesting=0, start="start")

            option_1 = facts.Option(state_from=choice_1.uid, state_to=finish_1.uid, type="opt_1", markers=())
            option_2 = facts.Option(state_from=choice_1.uid, state_to=choice_2.uid, type="opt_2", markers=())

            option_2_1 = facts.Option(state_from=choice_2.uid, state_to=finish_1.uid, type="opt_2_1", markers=())
            option_2_2 = facts.Option(state_from=choice_2.uid, state_to=finish_2.uid, type="opt_2_2", markers=())

            facts_list = [
                start,
                facts.Jump(state_from=start.uid, state_to=choice_1.uid),
                choice_1,
                choice_2,
                finish_1,
                finish_2,
                option_1,
                option_2,
                option_2_1,
                option_2_2,
                facts.OptionsLink(options=(option_2.uid, option_2_2.uid)),
            ]

            kb += facts_list

            try:
                transformators.determine_default_choices(kb)
            except exceptions.LinkedOptionWithProcessedChoiceError:
                is_raised = True
                self.assertEqual(len(list(kb.filter(facts.ChoicePath))), 1)
                self.assertEqual([path.choice for path in kb.filter(facts.ChoicePath)], [choice_2.uid])
                self.assertEqual([path.option for path in kb.filter(facts.ChoicePath)], [option_2_1.uid])
                break

        self.assertFalse(is_raised)
Пример #13
0
def create_quest():

    # формируем список заданий для генерации
    qb = QuestsBase()
    qb += [
        Spying, Hunt, Hometown, SearchSmith, Delivery, Caravan, CollectDebt,
        HelpFriend, InterfereEnemy, Help
    ]

    kb = KnowledgeBase()

    # описываем мир
    kb += [
        facts.Hero(uid='hero'),  # наш герой
        facts.Place(uid='place_1', terrains=(
            1,
        )),  # есть место с идентификатором place_1 и типами ландшафта 1,
        facts.Place(uid='place_2', terrains=(0, )),
        facts.Place(uid='place_3', terrains=(0, )),
        facts.Place(uid='place_4', terrains=(1, )),
        facts.Place(uid='place_5', terrains=(2, )),
        facts.Place(uid='place_6', terrains=(1, )),
        facts.Place(uid='place_7', terrains=(2, )),
        facts.Place(uid='place_8', terrains=(2, )),
        facts.Place(uid='place_9', terrains=(1, )),
        facts.Place(uid='place_10', terrains=(2, )),
        facts.Person(
            uid='person_1', profession=PROFESSION.NONE
        ),  # есть персонаж с идентификатором perons_1 и без профессии
        facts.Person(uid='person_2', profession=PROFESSION.BLACKSMITH),
        facts.Person(uid='person_3', profession=PROFESSION.ROGUE),
        facts.Person(uid='person_4', profession=PROFESSION.NONE),
        facts.Person(uid='person_5', profession=PROFESSION.NONE),
        facts.Person(uid='person_6', profession=PROFESSION.NONE),
        facts.Person(uid='person_7', profession=PROFESSION.NONE),
        facts.Person(uid='person_8', profession=PROFESSION.NONE),
        facts.Person(uid='person_9', profession=PROFESSION.NONE),
        facts.Person(uid='person_10', profession=PROFESSION.NONE),
        facts.LocatedIn(
            object='person_1',
            place='place_1'),  # персонаж person_1 находится в place_1
        facts.LocatedIn(object='person_2', place='place_2'),
        facts.LocatedIn(object='person_3', place='place_3'),
        facts.LocatedIn(object='person_4', place='place_4'),
        facts.LocatedIn(object='person_5', place='place_5'),
        facts.LocatedIn(object='person_6', place='place_6'),
        facts.LocatedIn(object='person_7', place='place_7'),
        facts.LocatedIn(object='person_8', place='place_8'),
        facts.LocatedIn(object='person_9', place='place_9'),
        facts.LocatedIn(object='person_10', place='place_10'),
        facts.LocatedIn(object='hero',
                        place='place_1'),  # герой находится в place_1
        facts.Mob(
            uid='mob_1', terrains=(0, )
        ),  # есть монстр, обитающий на территориях с идентификатором 0 (для задания на охоту)
        facts.PreferenceMob(
            object='hero',
            mob='mob_1'),  # герой любит охотиться на монстра mob_1
        facts.PreferenceHometown(
            object='hero',
            place='place_2'),  # герой считате родным место place_2
        facts.PreferenceFriend(object='hero',
                               person='person_4'),  # герой дружит с person_4
        facts.PreferenceEnemy(object='hero',
                              person='person_5'),  # герой враждует с person_5

        # указываем, что обновление экипировки стоит 777 монет (для задания SearchSmith)
        # facts.HasMoney(object='hero', money=888), # если этот факт раскоментировать то в этом задании герой купит экипировку, а не пойдёт делать задание кузнеца
        facts.UpgradeEquipmentCost(money=777),
        facts.OnlyGoodBranches(object='place_2'),  # не вредить месту place_2
        facts.OnlyGoodBranches(
            object='person_4'),  # не вредить персонажу person_4
        facts.OnlyBadBranches(object='person_5')
    ]  # не помогать персонажу person_5

    kb.validate_consistency(
        WORLD_RESTRICTIONS)  # проверяем ограничения на мир,

    selector = Selector(kb, qb)

    # создаём квест (получаем список фактов)
    quests_facts = selector.create_quest_from_place(
        nesting=0, initiator_position=kb['place_1'], tags=('can_start', ))

    kb += quests_facts

    transformators.activate_events(
        kb
    )  # активируем события (из нескольких вершин графа оставляем одну, остальные удаляем)
    transformators.remove_restricted_states(
        kb
    )  # удаляем состояния, в которые нельзя переходить (например, которые вредят тому, кому вредить нельщя)
    transformators.remove_broken_states(
        kb
    )  # чистим граф задания от разрушений, вызванных предыдущими действиями
    transformators.determine_default_choices(
        kb)  # определяем выборы по умолчанию на развилках

    kb.validate_consistency(WORLD_RESTRICTIONS)  # ещё раз проверяем мир
    kb.validate_consistency(
        QUEST_RESTRICTIONS
    )  # проверяем граф задания (вдруг полностью разрушен)

    return kb
Пример #14
0
def create_quest():

    # формируем список заданий для генерации
    qb = QuestsBase()
    qb += [Spying, Hunt, Hometown, SearchSmith, Delivery, Caravan, CollectDebt, HelpFriend, InterfereEnemy, Help]

    kb = KnowledgeBase()

    # описываем мир
    kb += [ facts.Hero(uid='hero'), # наш герой

            facts.Place(uid='place_1', terrains=(1,)), # есть место с идентификатором place_1 и типами ландшафта 1,
            facts.Place(uid='place_2', terrains=(0,)),
            facts.Place(uid='place_3', terrains=(0,)),
            facts.Place(uid='place_4', terrains=(1,)),
            facts.Place(uid='place_5', terrains=(2,)),
            facts.Place(uid='place_6', terrains=(1,)),
            facts.Place(uid='place_7', terrains=(2,)),
            facts.Place(uid='place_8', terrains=(2,)),
            facts.Place(uid='place_9', terrains=(1,)),
            facts.Place(uid='place_10', terrains=(2,)),

            facts.Person(uid='person_1', profession=PROFESSION.NONE), # есть персонаж с идентификатором perons_1 и без профессии
            facts.Person(uid='person_2', profession=PROFESSION.BLACKSMITH),
            facts.Person(uid='person_3', profession=PROFESSION.ROGUE),
            facts.Person(uid='person_4', profession=PROFESSION.NONE),
            facts.Person(uid='person_5', profession=PROFESSION.NONE),
            facts.Person(uid='person_6', profession=PROFESSION.NONE),
            facts.Person(uid='person_7', profession=PROFESSION.NONE),
            facts.Person(uid='person_8', profession=PROFESSION.NONE),
            facts.Person(uid='person_9', profession=PROFESSION.NONE),
            facts.Person(uid='person_10', profession=PROFESSION.NONE),

            facts.LocatedIn(object='person_1', place='place_1'), # персонаж person_1 находится в place_1
            facts.LocatedIn(object='person_2', place='place_2'),
            facts.LocatedIn(object='person_3', place='place_3'),
            facts.LocatedIn(object='person_4', place='place_4'),
            facts.LocatedIn(object='person_5', place='place_5'),
            facts.LocatedIn(object='person_6', place='place_6'),
            facts.LocatedIn(object='person_7', place='place_7'),
            facts.LocatedIn(object='person_8', place='place_8'),
            facts.LocatedIn(object='person_9', place='place_9'),
            facts.LocatedIn(object='person_10', place='place_10'),

            facts.LocatedIn(object='hero', place='place_1'), # герой находится в place_1

            facts.Mob(uid='mob_1', terrains=(0,)), # есть монстр, обитающий на территориях с идентификатором 0 (для задания на охоту)
            facts.PreferenceMob(object='hero', mob='mob_1'), # герой любит охотиться на монстра mob_1
            facts.PreferenceHometown(object='hero', place='place_2'), # герой считате родным место place_2
            facts.PreferenceFriend(object='hero', person='person_4'), # герой дружит с person_4
            facts.PreferenceEnemy(object='hero', person='person_5'), # герой враждует с person_5

            # указываем, что обновление экипировки стоит 777 монет (для задания SearchSmith)
            # facts.HasMoney(object='hero', money=888), # если этот факт раскоментировать то в этом задании герой купит экипировку, а не пойдёт делать задание кузнеца
            facts.UpgradeEquipmentCost(money=777),

            facts.OnlyGoodBranches(object='place_2'), # не вредить месту place_2
            facts.OnlyGoodBranches(object='person_4'), # не вредить персонажу person_4
            facts.OnlyBadBranches(object='person_5') ] # не помогать персонажу person_5


    kb.validate_consistency(WORLD_RESTRICTIONS) # проверяем ограничения на мир,

    selector = Selector(kb, qb)

    # создаём квест (получаем список фактов)
    quests_facts = selector.create_quest_from_place(nesting=0,
                                                    initiator_position=kb['place_1'],
                                                    tags=('can_start', ))

    kb += quests_facts

    transformators.activate_events(kb) # активируем события (из нескольких вершин графа оставляем одну, остальные удаляем)
    transformators.remove_restricted_states(kb) # удаляем состояния, в которые нельзя переходить (например, которые вредят тому, кому вредить нельщя)
    transformators.remove_broken_states(kb) # чистим граф задания от разрушений, вызванных предыдущими действиями
    transformators.determine_default_choices(kb) # определяем выборы по умолчанию на развилках

    kb.validate_consistency(WORLD_RESTRICTIONS) # ещё раз проверяем мир
    kb.validate_consistency(QUEST_RESTRICTIONS) # проверяем граф задания (вдруг полностью разрушен)

    return kb