00001 using System; 00002 using System.Collections.Generic; 00003 using System.Linq; 00004 00005 namespace SpaceFlight.Simulation 00006 { 00007 public class AiShipController : IShipController 00008 { 00009 private INode root; 00010 00011 public AiShipController(string nodes) 00012 { 00013 root = Parse(nodes.Trim().Split(' ').AsEnumerable().GetEnumerator()); 00014 } 00015 00016 public void UpdateShip(TestCase testCase, Vector2D gravity) 00017 { 00018 var context = new Dictionary<string, double>(); 00019 context["pi"] = Math.PI; 00020 00021 Vector2D target = testCase.Waypoints[testCase.TargetWaypoint] - testCase.Ship.Position; 00022 context["td"] = target.Length; 00023 context["ta"] = Vector2D.Angle(testCase.Ship.Direction, target); 00024 00025 context["vm"] = testCase.Ship.Velocity.Length; 00026 context["va"] = Vector2D.Angle(testCase.Ship.Direction, testCase.Ship.Velocity); 00027 context["gm"] = gravity.Length; 00028 context["ga"] = Vector2D.Angle(testCase.Ship.Direction, gravity); 00029 00030 var nearest = testCase.Planets 00031 .DefaultIfEmpty(new Planet { Position = new Vector2D { X = -2000, Y = -2000 } }) 00032 .Select(p => new { Distance = p.Position - testCase.Ship.Position, Raidus = p.Radius }) 00033 .OrderBy(p => p.Distance.Length - p.Raidus) 00034 .First(); 00035 00036 context["npd"] = nearest.Distance.Length - nearest.Raidus; 00037 context["npa"] = Vector2D.Angle(testCase.Ship.Direction, nearest.Distance); 00038 00039 testCase.Ship.Turn = root.Evaluate(context); 00040 } 00041 00042 private INode Parse(IEnumerator<string> tokens) 00043 { 00044 tokens.MoveNext(); 00045 string symbol = tokens.Current; 00046 if (new [] { "+", "-", "*", "/" }.Contains(symbol)) 00047 { 00048 return new OperatorNode(symbol[0], Parse(tokens), Parse(tokens)); 00049 } 00050 else if (symbol == "iflt") 00051 { 00052 return new BranchNode(Parse(tokens), Parse(tokens), Parse(tokens), Parse(tokens)); 00053 } 00054 else if (new [] { "td", "ta", "vm", "va", "npd", "npa" }.Contains(symbol)) 00055 { 00056 return new VariableNode(symbol); 00057 } 00058 else 00059 { 00060 if (symbol.StartsWith("D_") || symbol.StartsWith("I_")) 00061 { 00062 symbol = symbol.Substring(2); 00063 } 00064 return new ValueNode(double.Parse(symbol)); 00065 } 00066 } 00067 } 00068 00069 internal interface INode 00070 { 00071 double Evaluate(IDictionary<string, double> context); 00072 } 00073 00074 internal class OperatorNode : INode 00075 { 00076 private char oper; 00077 private INode firstArg; 00078 private INode secondArg; 00079 00080 public OperatorNode(char oper, INode firstArg, INode secondArg) 00081 { 00082 this.oper = oper; 00083 this.firstArg = firstArg; 00084 this.secondArg = secondArg; 00085 } 00086 00087 public double Evaluate(IDictionary<string, double> context) 00088 { 00089 double first = firstArg.Evaluate(context); 00090 double second = secondArg.Evaluate(context); 00091 switch (oper) 00092 { 00093 case '+': 00094 { 00095 return first + second; 00096 } 00097 case '-': 00098 { 00099 return first - second; 00100 } 00101 case '*': 00102 { 00103 return first * second; 00104 } 00105 case '/': 00106 { 00107 return Math.Abs(second) > 1e-5 ? first / second : 1; 00108 } 00109 default: 00110 { 00111 throw new ArgumentException(); 00112 } 00113 } 00114 } 00115 } 00116 00117 internal class BranchNode : INode 00118 { 00119 private INode first, second; 00120 private INode less, greater; 00121 00122 public BranchNode(INode first, INode second, INode lessEqual, INode greater) 00123 { 00124 this.first = first; 00125 this.second = second; 00126 this.less = lessEqual; 00127 this.greater = greater; 00128 } 00129 00130 public double Evaluate(IDictionary<string, double> context) 00131 { 00132 if (first.Evaluate(context) < second.Evaluate(context)) 00133 { 00134 return less.Evaluate(context); 00135 } 00136 else 00137 { 00138 return greater.Evaluate(context); 00139 } 00140 } 00141 } 00142 00143 internal class ValueNode : INode 00144 { 00145 private double value; 00146 00147 public ValueNode(double value) 00148 { 00149 this.value = value; 00150 } 00151 00152 public double Evaluate(IDictionary<string, double> context) 00153 { 00154 return value; 00155 } 00156 } 00157 00158 internal class VariableNode : INode 00159 { 00160 private string variable; 00161 00162 public VariableNode(string variable) 00163 { 00164 this.variable = variable; 00165 } 00166 00167 public double Evaluate(IDictionary<string, double> context) 00168 { 00169 return context[variable]; 00170 } 00171 } 00172 }