Итак, я хочу иметь возможность анализировать и оценивать «выражения костей» в C #. Выражение кости определяется так:
<expr> := <expr> + <expr>
| <expr> - <expr>
| [<number>]d(<number>|%)
| <number>
<number> := positive integer
Так, например d6+20-2d3
будет разрешено и должно оцениваться как
rand.Next(1, 7) + 20 - (rand.Next(1, 4) + rand.Next(1, 4))
Также d%
должно быть эквивалентно d100
.
Я знаю, что мог бы взломать какое-то решение, но я также знаю, что это похоже на очень типичную проблему компьютерного типа, поэтому должно быть какое-то супер-элегантное решение, на которое я должен обратить внимание.
Я бы хотел, чтобы результат моего анализа имел следующие возможности:
- Я должен быть в состоянии вывести нормализованную форму выражения; Сначала я думаю о кости, отсортированной по размеру кости, и всегда с префиксом. Так, например приведенный выше пример станет
1d6-2d3+20
. Также любые экземпляры d%
станут d100
в нормализованной форме.
- Я должен иметь возможность оценить выражение по желанию, каждый раз выпуская разные случайные числа.
- Я должен быть в состоянии оценить выражение при всех максимизированных бросках кубиков, например, образец выше даст (детерминистически)
1*6+20+2*3 = 32
.
Я знаю, что это именно тот тип вещей, в котором Haskell, и, возможно, другие языки функционального типа, были бы хороши, но я бы хотел остаться в C #, если это возможно.
Мои первоначальные мысли имеют тенденцию к рекурсии, спискам и, возможно, некоторому LINQ, но, опять же, если бы я попытался без указателей от людей, которые знают вещи, я уверен, что это закончилось бы бесполезным беспорядком.
Еще одна тактика, которая может сработать, - это некоторая первоначальная замена строк на основе регулярных выражений для преобразования выражений в кости в вызовы rand.Next
, а затем оценка или компиляция на лету ... это действительно сработает? Как можно избежать создания нового rand
объекта каждый раз?