Сегодня я думал об идее небольшой игры и наткнулся на то, как ее реализовать. Идея состоит в том, что игрок может сделать серию ходов, которые вызовут небольшой эффект, но если они будут выполнены в определенной последовательности, они вызовут больший эффект. Пока все хорошо, я знаю, как это сделать. Очевидно, мне пришлось сделать его более сложным (потому что мы любим делать его более сложным), поэтому я подумал, что может быть более одного возможного пути для последовательности, которые оба вызовут больший эффект, хотя и разные. Кроме того, часть некоторых последовательностей может быть началом других последовательностей, или даже целые последовательности могут содержаться в других более крупных последовательностях. Сейчас я точно не знаю, как это лучше реализовать. Хотя у меня были некоторые идеи.
1) Я мог бы реализовать круговой n-связанный список. Но поскольку список ходов никогда не заканчивается, я боюсь, что это может вызвать переполнение стека ™. Идея состоит в том, что у каждого узла будет n дочерних узлов, и после получения команды он может привести вас к одному из его дочерних узлов или, если для такой команды не было доступных дочерних узлов, вернуть вас к началу. При появлении любых детей будет выполняться пара функций, вызывающих большой и малый эффект. Однако это может привести к множеству дублированных узлов в дереве, чтобы справиться со всеми возможными последовательностями, заканчивающимися этим конкретным ходом с разными эффектами, что может быть болезненным для поддержания, но я не уверен. Я никогда не пробовал что-то настолько сложное с кодом, только теоретически. Существует ли этот алгоритм и есть ли у него название? Это хорошая идея?
2) Я мог бы реализовать конечный автомат. Тогда вместо того, чтобы бродить по связному списку, у меня был бы какой-то гигантский вложенный переключатель, который будет вызывать функции и соответственно обновлять состояние машины. Кажется, проще реализовать, но ... ну ... не кажется забавным ... или изящным. Гигантские переключатели всегда кажутся мне некрасивыми, но будет ли это работать лучше?
3) Предложения? У меня все хорошо, но у меня нет опыта. Преимущество поля кодирования в том, что какой бы странной ни была ваша проблема, кто-то решил ее в прошлом, но вы должны знать, где искать. У кого-то может быть идея получше, чем у меня, и мне очень хотелось услышать предложения.





То, что вы описываете, очень похоже на дерево технологий в живой игре Civilization.
Я не знаю, как авторы Civ построили свои, но я был бы склонен использовать мультиграф для представления возможных `` ходов '' - будут некоторые, с которых вы можете начать без `` опыта '', и как только вы в них , будет несколько путей до конца.
Обозначьте, какие возможные варианты вы можете иметь на каждом этапе игры, а затем проведите линии, переходящие от одних вариантов к другим.
Это должно дать вам начало реализации, поскольку графы - это [относительно] простые концепции для реализации и использования.
Древо технологий цивилизации одностороннее и имеет определенный конец (технологии будущего). Мое дерево должно быть круглым без определенного конца. Несмотря на то, что вы можете считать прямые линии круговыми деревьями, в этом дереве технологий вы не можете пересекать один и тот же узел более одного раза.
В любом случае вы можете захотеть реализовать конечный автомат, но вам не нужно жестко кодировать переходы между состояниями.
Попробуйте построить график состояний, где связь между состоянием A и состоянием B будет означать, что A может привести к B.
Затем вы можете перемещаться по графику во время выполнения, чтобы найти, куда идет игрок.
Обновлено: вы можете определить узел графика как:
-state-id
-список ссылок на другие государства,
где каждая ссылка определяет:
-state-id
-precondition, список состояний, которые необходимо посетить перед переходом в это состояние
Это может сработать, но состояния на самом деле не определены, поэтому я не уверен. Я имею в виду, что A-B-C может вызвать эффект, а B-C - нет. Вот почему я упомянул в своем посте, что мне, возможно, пришлось бы иметь дублированные узлы, чтобы справиться с такими ситуациями. Хотя идея выглядит неплохо. Хотите уточнить?
И как мне легко проверить предварительные условия?
Вы просто отслеживаете уже посещенные узлы в стеке, а затем проверяете каждое предварительное условие снова для недавно посещенных узлов.
Мне пришлось бы сделать стек круговым списком, чтобы избежать переполнения стека, но я думаю, что мы кое-что достигли.
Похоже на нейронная сеть. Вы можете создать его и обучить распознавать шаблоны, вызывающие различные эффекты, которые вы ищете.
Но тогда не будет ли сложно добавить новые паттерны?
Это намного сложнее, чем должно быть.
То, что вы описываете, звучит несколько похоже на граф зависимостей или граф слов. Вы можете изучить их.
Похоже, граф зависимостей - хороший вызов, но я не смог найти ни одной надежной статьи о графе слов. Вы можете помочь мне?
Я не совсем уверен, что правильно понимаю, что вы говорите, но в качестве аналогичной ситуации предположим, что кто-то вводит бесконечный поток чисел с клавиатуры. «117» - это магическая последовательность, «468» - еще одна, «411799» - еще одна (содержащая первую).
Итак, если пользователь входит:
55468411799
вы хотите запускать «волшебные события» в * s:
55468*4117*99*
или что-то в этом роде, да? Если это аналогично проблеме, о которой вы говорите, то как насчет чего-то вроде (псевдокода в стиле Java):
MagicSequence fireworks = new MagicSequence(new FireworksAction(), 1, 1, 7);
MagicSequence playMusic = new MagicSequence(new MusicAction(), 4, 6, 8);
MagicSequence fixUserADrink = new MagicSequence(new ManhattanAction(), 4, 1, 1, 7, 9, 9);
Collection<MagicSequence> sequences = ... all of the above ...;
while (true) {
int num = readNumberFromUser();
for (MagicSequence seq : sequences) {
seq.handleNumber(num);
}
}
в то время как MagicSequence имеет что-то вроде:
Action action = ... populated from constructor ...;
int[] sequence = ... populated from constructor ...;
int position = 0;
public void handleNumber(int num) {
if (num == sequence[position]) {
// They've entered the next number in the sequence
position++;
if (position == sequence.length) {
// They've got it all!
action.fire();
position = 0; // Or disable this Sequence from accepting more numbers if it's a once-off
}
} else {
position = 0; // missed a number, start again!
}
}
Вы правильно поняли идею, но я все еще пытаюсь проглотить ваш код. Дайте мне 5 минут, чтобы понять, что он делает.
Ваша идея работает, но не масштабируется. Подумайте о 100 последовательностях, подумайте о 1000, каждая из которых проверяет каждый символ. Это могло бы работать на настольном компьютере, но было бы смертельно опасно. В любом случае, я буду держать эту идею в уме. Возможно, мы сможем его улучшить.
В этом примере с игрушкой я сомневаюсь, что это имеет значение. 100 поисков в массиве? 1000? 1000 x почти ничего = почти ничего. Я знаю, что ваша ситуация не будет такой простой, поэтому она может не подойти; но я уверен, что вы видели, что идея здесь заключалась в демонстрации идеи позволить последовательностям управлять своим собственным состоянием, а не делать это централизованно.
Вы также можете использовать базу данных для представления последовательностей, которые вы хотите сопоставить, и чтобы она следовала шаблону Коуэна, выполняя их поиск и возвращая соответствующие последовательности.
У Дэйва есть интересная идея ... если самая длинная последовательность - это 10 чисел, сохраните последние 10 номеров пользователей (скажем, 1234567890) в списке, а затем выполните из БД что-то вроде SELECT * FROM magicSequence WHERE '%' + последовательность LIKE «1234567890». Опять игрушечный пример, но идея.
создайте небольшой конечный автомат для каждого эффекта, который вам нужен. при каждом действии пользователя «транслировать» его на все конечные автоматы. большинству из них все равно, но некоторые пойдут вперед или, возможно, пойдут назад. когда один из них достигает своей цели, производит желаемый эффект.
Чтобы код оставался аккуратным, не кодируйте конечные автоматы жестко, вместо этого создайте простую структуру данных, которая кодирует граф состояний: каждое состояние - это узел со списком интересных событий, каждое из которых указывает на узел следующего состояния. Состояние каждой машины - это просто ссылка на соответствующий узел состояния.
edit: Кажется, совет Коуэна эквивалентен этому, но он оптимизирует свои конечные автоматы, чтобы выражать только простые последовательности. кажется достаточно для вашей конкретной проблемы, но для более сложных условий может потребоваться более общее решение.
Может быть более одного «следующего состояния». Идеи?
это означает создание новой машины
@Cowan, @Javier: Хорошая идея, не возражаете, если я добавлю к ней?
Позвольте объектам MagicSequence прослушивать входящий поток пользовательского ввода, то есть уведомлять их о вводе (широковещании) и позволять каждому из них добавлять ввод во внутренний поток ввода. Этот поток очищается, когда ввод не является ожидаемым следующим вводом в шаблоне, который вызвал бы действие MagicSequence. Как только шаблон будет завершен, запустите действие и очистите внутренний входной поток.
Оптимизируйте это, подавая входные данные только в ожидающие его MagicSequences. Это можно было сделать двумя способами:
У вас есть объект, который позволяет всем MagicSequences связываться с событиями, которые соответствуют числам в их шаблонах. MagicSequence (1,1,7) добавится к got1 и got7, например:
UserInput.got1 + = MagicSequnece [i] .SendMeInput;
Вы можете оптимизировать это так, чтобы после каждого ввода MagicSequences отменяли регистрацию недопустимых событий и регистрировались с действительными.
Отлично, это более оптимизированный общий случай, если накладные расходы на регистрацию / отмену регистрации меньше, чем стоимость простой трансляции. Конечно, у вас может быть обратный поток информации и в обратном направлении - «Действия» могут «регистрировать» НОВЫЕ действия обратно с помощью контроллера, чтобы «разблокировать» новые действия и т. д.
Как общее замечание: начните с простого, добавляйте сложности позже.