using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
///
/// 公式计算模块,支持动态变量替换和多种运算符计算。
///
public class Equation : Singleton
{
///
/// 支持的运算符列表。
///
public static readonly List operatorList = new List() { '*', '-', '+', '/', '^', '!', '@', '%', ';', '#', '$', '~', '&' };
///
/// 用于临时字符串构建的共享实例。
///
public static StringBuilder textBuilder = new StringBuilder();
///
/// 存储变量名和值的键值对列表。
///
private List> keyValueDic = new List>();
///
/// 用于匹配变量名的正则表达式。
///
public static readonly Regex replaecRegex = new Regex(@"[a-zA-Z]{2,50}");
public T Eval(string equation) where T : struct
{
keyValueDic.Sort(Compare);
equation = GetEquation(equation);
T val = default(T);
try
{
EquationParse equationParse = new EquationParse(equation);
val = (T)Convert.ChangeType(equationParse.result, typeof(T));
equationParse = null;
}
catch (Exception e)
{
Debug.LogError(e.Message);
}
return val;
}
///
/// 替换公式中的变量名为对应的值。
///
/// 原始公式字符串。
/// 替换后的公式字符串。
private string GetEquation(string equation)
{
try
{
MatchCollection matchArray = replaecRegex.Matches(equation);
textBuilder.Length = 0;
int length = 0;
if (matchArray != null)
{
foreach (Match match in matchArray)
{
// 添加非变量部分
textBuilder.Append(equation, length, match.Index - length);
length = match.Index + match.Length;
// 查找变量对应的值
var foundPair = keyValueDic.Find(x => x.Key.Equals(match.Value));
string replacement = foundPair.Equals(default(KeyValuePair)) ? "0" : foundPair.Value;
textBuilder.Append(replacement ?? "0");
}
}
// 添加剩余部分
textBuilder.Append(equation, length, equation.Length - length);
}
catch (Exception e)
{
Debug.LogError($"公式变量替换失败: {e.Message}");
}
return textBuilder.ToString();
}
public void AddKeyValue(string key, object val)
{
int index = keyValueDic.FindIndex((x) => {
return x.Key == key;
});
if (index == -1)
{
KeyValuePair keyValuePair = new KeyValuePair(key, val.ToString());
keyValueDic.Add(keyValuePair);
}
}
public void Clear()
{
keyValueDic.Clear();
}
int Compare(KeyValuePair x, KeyValuePair y)
{
return -x.Key.Length.CompareTo(y.Key.Length);
}
public class EquationParse
{
public string equation { get; private set; }
public string result { get; private set; }
private StringBuilder equationBuilder = new StringBuilder();
private List subEquations = new List();
private List operatorTypeList = new List();
private List values = new List();
public EquationParse(string _equation)
{
this.equation = _equation;
this.result = string.Empty;
equationBuilder.Length = 0;
if (CheckSplit())
{
return;
}
Subsection();
GetResult();
}
private bool CheckSplit()
{
var _left = string.Empty;
var _right = string.Empty;
OperatorType _operatorType;
if (Split(equation, out _left, out _right, out _operatorType))
{
var _leftResult = new EquationParse(_left);
var _rightResult = new EquationParse(_right);
result = GetResult(_leftResult.result, _rightResult.result, _operatorType).ToString();
return true;
}
return false;
}
///
/// 解析公式中的子表达式(括号内的部分)。
///
private void Subsection()
{
try
{
int bracketCnt = 0;
int startIndex = 0;
int length = 0;
int index = 0;
for (int i = 0; i < equation.Length; i++)
{
if (equation[i] == '(')
{
i++;
startIndex = i;
// 提取括号内的子表达式
while (i < equation.Length && (equation[i] != ')' || bracketCnt > 0))
{
if (equation[i] == '(') bracketCnt++;
else if (equation[i] == ')') bracketCnt--;
length++;
i++;
}
// 处理子表达式
subEquations.Add(new EquationParse(equation.Substring(startIndex, length)));
equationBuilder.Append($"={index}=");
index++;
length = 0;
continue;
}
equationBuilder.Append(equation[i]);
}
}
catch (Exception e)
{
Debug.LogError($"子表达式解析失败: {e.Message}");
}
}
private void AnalysisEquation()
{
try
{
textBuilder.Length = 0;
for (int i = 0; i < subEquations.Count; i++)
{
equationBuilder.Replace(string.Format("={0}=", i), subEquations[i].result);
subEquations[i] = null;
}
subEquations.Clear();
var _result = equationBuilder.ToString();
char _lastChar = default(char);
for (int i = 0; i < _result.Length; i++)
{
if (i - 1 > 0 && _result[i - 1] == 'E')
{//过滤超大数值后的符号
textBuilder.Append(_result[i]);
continue;
}
int index = operatorList.IndexOf(_result[i]);
if (index != -1 && _lastChar >= '0' && _lastChar <= '9')
{
values.Add(textBuilder.ToString());
textBuilder.Length = 0;
operatorTypeList.Add(GetOperatorType(_result[i]));
}
else
{
textBuilder.Append(_result[i]);
}
_lastChar = _result[i];
}
values.Add(textBuilder.ToString());
}
catch (Exception e)
{
Debug.Log(e.Message);
}
}
public void GetResult()
{
AnalysisEquation();
try
{
for (int i = 0; i < operatorTypeList.Count; i++)
{
if (IsPriorityOperator(operatorTypeList[i]))
{
double _result = 0;
if (IsUnaryOperator(operatorTypeList[i]))
{
_result = GetResult(values[i], string.Empty, operatorTypeList[i]);
}
else
{
_result = GetResult(values[i], values[i + 1], operatorTypeList[i]);
values.RemoveAt(i);
}
values.RemoveAt(i);
values.Insert(i, _result.ToString());
operatorTypeList.RemoveAt(i);
i--;
}
}
while (values.Count > 1 && operatorTypeList.Count > 0)
{
double _result = 0;
if (IsUnaryOperator(operatorTypeList[0]))
{
_result = GetResult(values[0], string.Empty, operatorTypeList[0]);
}
else
{
_result = GetResult(values[0], values[1], operatorTypeList[0]);
values.RemoveAt(0);
}
values.RemoveAt(0);
values.Insert(0, _result.ToString());
operatorTypeList.RemoveAt(0);
}
if (operatorTypeList.Count == 1 && IsUnaryOperator(operatorTypeList[0]))
{
result = GetResult(values[0], string.Empty, operatorTypeList[0]).ToString();
}
else
{
result = values[0];
}
}
catch (Exception e)
{
Debug.Log(e.Message);
}
}
///
/// 根据运算符类型计算结果。
///
/// 左操作数。
/// 右操作数。
/// 运算符类型。
/// 计算结果。
public static double GetResult(string leftValue, string rightValue, OperatorType operatorType)
{
if (!double.TryParse(leftValue, out double _leftValue) || !double.TryParse(rightValue, out double _rightValue))
{
Debug.LogError($"无法将字符串转换为数值: {leftValue} 或 {rightValue}");
return 0;
}
switch (operatorType)
{
case OperatorType.Plus: return _leftValue + _rightValue;
case OperatorType.Subtract: return _leftValue - _rightValue;
case OperatorType.Ride: return _leftValue * _rightValue;
case OperatorType.Divide: return _rightValue == 0 ? _leftValue : _leftValue / _rightValue;
case OperatorType.Pow: return Math.Pow(_leftValue, _rightValue);
case OperatorType.Min: return Math.Min(_leftValue, _rightValue);
case OperatorType.Max: return Math.Max(_leftValue, _rightValue);
case OperatorType.Remain: return _leftValue % _rightValue;
case OperatorType.Random: return UnityEngine.Random.Range((float)_leftValue, (float)_rightValue);
case OperatorType.Floor: return Math.Floor(_leftValue);
case OperatorType.Ceil: return Math.Ceiling((float)_leftValue);
case OperatorType.RandomInt: return UnityEngine.Random.Range((int)_leftValue, (int)_rightValue);
case OperatorType.Sqrt: return Math.Sqrt(_leftValue);
default: return 0;
}
}
public static OperatorType GetOperatorType(char _operator)
{
switch (_operator)
{
case '+':
return OperatorType.Plus;
case '-':
return OperatorType.Subtract;
case '*':
return OperatorType.Ride;
case '/':
return OperatorType.Divide;
case '^':
return OperatorType.Pow;
case '!':
return OperatorType.Min;
case '@':
return OperatorType.Max;
case '%':
return OperatorType.Remain;
case ';':
return OperatorType.Random;
case '#':
return OperatorType.Floor;
case '$':
return OperatorType.Ceil;
case '~':
return OperatorType.RandomInt;
case '&':
return OperatorType.Sqrt;
}
return OperatorType.Plus;
}
public static bool IsPriorityOperator(OperatorType operatorType)
{
if (operatorType == OperatorType.Plus || operatorType == OperatorType.Subtract)
{
return false;
}
return true;
}
public static bool IsUnaryOperator(OperatorType operatorType)
{
if (operatorType == OperatorType.Floor || operatorType == OperatorType.Ceil
|| operatorType == OperatorType.Sqrt)
{
return true;
}
return false;
}
private static bool Split(string _source, out string _left, out string _right, out OperatorType _type)
{
_left = _right = string.Empty;
_type = OperatorType.Plus;
try
{
var _index = 0;
for (int i = _source.Length - 1; i >= 0; i--)
{
if (_source[i] == ')')
{
_index++;
continue;
}
else if (_source[i] == '(')
{
_index--;
continue;
}
if (IsSplitOperator(_source[i], out _type) && _index == 0)
{
_left = _source.Substring(0, i);
if (i + 1 < _source.Length)
{
_right = _source.Substring(i + 1);
}
return true;
}
}
}
catch (Exception e)
{
Debug.Log(e.Message);
}
return false;
}
public static bool IsSplitOperator(char _char, out OperatorType _type)
{
var _index = operatorList.FindIndex((x) =>
{
return x == _char;
});
_type = OperatorType.Plus;
if (_index == -1)
{
return false;
}
_type = GetOperatorType(_char);
switch (_type)
{
case OperatorType.Floor:
case OperatorType.Ceil:
case OperatorType.Min:
case OperatorType.Max:
case OperatorType.Remain:
case OperatorType.Pow:
case OperatorType.Random:
case OperatorType.RandomInt:
case OperatorType.Sqrt:
return true;
}
return false;
}
}
public enum OperatorType
{
Plus,//+
Subtract,//-
Ride,//*
Divide,// /
Pow,// ^
Min,// !
Max,// @
Remain,// %
Random,
Floor,
Ceil,
RandomInt,
Sqrt
}
}