source: stable/HeuristicLab.Problems.GeneticProgramming/3.3/robocode/Interpreter.cs @ 13312

Last change on this file since 13312 was 13312, checked in by gkronber, 6 years ago

#2069: merged r13210,r13225,r13266,r13309,r13311 from trunk to stable

File size: 17.0 KB
Line 
1#region License Information
2/* HeuristicLab
3 * Copyright (C) 2002-2015 Heuristic and Evolutionary Algorithms Laboratory (HEAL)
4 *
5 * This file is part of HeuristicLab.
6 *
7 * HeuristicLab is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HeuristicLab is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HeuristicLab. If not, see <http://www.gnu.org/licenses/>.
19 */
20#endregion
21
22using System;
23using System.Diagnostics;
24using System.Diagnostics.Contracts;
25using System.Globalization;
26using System.IO;
27using System.Linq;
28using System.Reflection;
29using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
30
31namespace HeuristicLab.Problems.GeneticProgramming.Robocode {
32  public static class Interpreter {
33
34    // necessary for synchronization to guarantee that only one robocode program is executed at the same time
35    // NOTE: this does not guarantee OS-wide mutual exclusion, but we ignore that for now
36    private static readonly object syncRoot = new object();
37
38    // TODO performance: it would probably be useful to implement the BattleRunner in such a way that we don't have to restart the java process each time, e.g. using console IO to load & run robots
39    public static double EvaluateTankProgram(ISymbolicExpressionTree tree, string path, EnemyCollection enemies, string robotName = null, bool showUI = false, int nrOfRounds = 3) {
40      if (robotName == null)
41        robotName = GenerateRobotName();
42
43      string interpretedProgram = InterpretProgramTree(tree.Root, robotName);
44      string battleRunnerPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "robocode");
45      string roboCodeLibPath = Path.Combine(path, "libs");
46      string robocodeJar = Path.Combine(roboCodeLibPath, "robocode.jar");
47      string robocodeCoreJar = GetFileName(roboCodeLibPath, "robocode.core*");
48      string picocontainerJar = GetFileName(roboCodeLibPath, "picocontainer*");
49      string robotsPath = Path.Combine(path, "robots", "Evaluation");
50      string srcRobotPath = Path.Combine(robotsPath, robotName + ".java");
51
52      File.WriteAllText(srcRobotPath, interpretedProgram, System.Text.Encoding.Default);
53
54      // compile java source to class file
55      ProcessStartInfo javaCompileInfo = new ProcessStartInfo();
56      javaCompileInfo.FileName = "cmd.exe";
57      javaCompileInfo.Arguments = "/C javac -cp " + robocodeJar + "; " + srcRobotPath;
58      javaCompileInfo.RedirectStandardOutput = true;
59      javaCompileInfo.RedirectStandardError = true;
60      javaCompileInfo.UseShellExecute = false;
61      javaCompileInfo.CreateNoWindow = true;
62
63      // it's ok to compile multiple robocode programs concurrently
64      using (Process javaCompile = new Process()) {
65        javaCompile.StartInfo = javaCompileInfo;
66        javaCompile.Start();
67
68        string cmdOutput = javaCompile.StandardOutput.ReadToEnd();
69        cmdOutput += javaCompile.StandardError.ReadToEnd();
70
71        javaCompile.WaitForExit();
72        if (javaCompile.ExitCode != 0) {
73          DeleteRobotFiles(path, robotName);
74          throw new Exception("Compile Error: " + cmdOutput);
75        }
76      }
77
78      ProcessStartInfo evaluateCodeInfo = new ProcessStartInfo();
79
80      // execute a battle with numberOfRounds against a number of enemies
81      // TODO: seems there is a bug when selecting multiple enemies
82      evaluateCodeInfo.FileName = "cmd.exe";
83      var classpath = string.Join(";", new[] { battleRunnerPath, robocodeCoreJar, robocodeJar, picocontainerJar });
84      var enemyRobotNames = string.Join(" ", enemies.CheckedItems.Select(i => i.Value));
85      evaluateCodeInfo.Arguments = string.Format("/C java -cp {0} BattleRunner Evaluation.{1} {2} {3} {4} {5}", classpath, robotName, path, showUI, nrOfRounds, enemyRobotNames);
86
87      evaluateCodeInfo.RedirectStandardOutput = true;
88      evaluateCodeInfo.RedirectStandardError = true;
89      evaluateCodeInfo.UseShellExecute = false;
90      evaluateCodeInfo.CreateNoWindow = true;
91
92      // the robocode framework writes state to a file therefore parallel evaluation of multiple robocode programs is not possible yet.
93      double evaluation;
94      lock (syncRoot) {
95        using (Process evaluateCode = new Process()) {
96          evaluateCode.StartInfo = evaluateCodeInfo;
97          evaluateCode.Start();
98          evaluateCode.WaitForExit();
99
100          if (evaluateCode.ExitCode != 0) {
101            DeleteRobotFiles(path, robotName);
102            throw new Exception("Error running Robocode: " + evaluateCode.StandardError.ReadToEnd() +
103                                Environment.NewLine +
104                                evaluateCode.StandardOutput.ReadToEnd());
105          }
106
107          try {
108            string scoreString =
109              evaluateCode.StandardOutput.ReadToEnd()
110                .Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
111                .Last();
112            evaluation = Double.Parse(scoreString, CultureInfo.InvariantCulture);
113          }
114          catch (Exception ex) {
115            throw new Exception("Error parsing score string: " + ex);
116          }
117          finally {
118            DeleteRobotFiles(path, robotName);
119          }
120        }
121      }
122
123      return evaluation;
124    }
125
126    private static void DeleteRobotFiles(string path, string outputname) {
127      File.Delete(path + @"\robots\Evaluation\" + outputname + ".java");
128      File.Delete(path + @"\robots\Evaluation\" + outputname + ".class");
129    }
130
131    private static string GetFileName(string path, string pattern) {
132      string fileName = string.Empty;
133      try {
134        fileName = Directory.GetFiles(path, pattern).First();
135      }
136      catch {
137        throw new Exception("Error finding required Robocode files.");
138      }
139      return fileName;
140    }
141
142    private static string GenerateRobotName() {
143      // Robocode class names are 32 char max and
144      // Java class names have to start with a letter
145      string outputname = Guid.NewGuid().ToString();
146      outputname = outputname.Remove(8, 1);
147      outputname = outputname.Remove(12, 1);
148      outputname = outputname.Remove(16, 1);
149      outputname = outputname.Remove(20, 1);
150      outputname = outputname.Remove(0, 1);
151      outputname = outputname.Insert(0, "R");
152      return outputname;
153    }
154
155    public static string InterpretProgramTree(ISymbolicExpressionTreeNode node, string robotName) {
156      var tankNode = node;
157      while (tankNode.Symbol.Name != "Tank")
158        tankNode = tankNode.GetSubtree(0);
159
160      string result = Interpret(tankNode);
161      result = result.Replace("class output", "class " + robotName);
162      return result;
163    }
164
165    private static string Interpret(ISymbolicExpressionTreeNode node) {
166      switch (node.Symbol.Name) {
167        case "Block": return InterpretBlock(node);
168        case "Statement": return InterpretStat(node);
169        case "DoNothing": return string.Empty;
170        case "EmptyEvent": return string.Empty;
171
172        case "GetEnergy": return "getEnergy()";
173        case "GetHeading": return "getHeading()";
174        case "GetGunHeading": return "getGunHeading()";
175        case "GetRadarHeading": return "getRadarHeading()";
176        case "GetX": return "getX()";
177        case "GetY": return "getY()";
178
179        case "Addition": return InterpretBinaryOperator(" + ", node);
180        case "Subtraction": return InterpretBinaryOperator(" - ", node);
181        case "Multiplication": return InterpretBinaryOperator(" * ", node);
182        case "Division": return InterpretBinaryOperator(" / ", node);
183        case "Modulus": return InterpretBinaryOperator(" % ", node);
184
185        case "Equal": return InterpretComparison(" == ", node);
186        case "LessThan": return InterpretComparison(" < ", node);
187        case "LessThanOrEqual": return InterpretComparison(" <= ", node);
188        case "GreaterThan": return InterpretComparison("  >", node);
189        case "GreaterThanOrEqual": return InterpretComparison(" >= ", node);
190        case "ConditionalAnd": return InterpretBinaryOperator(" && ", node);
191        case "ConditionalOr": return InterpretBinaryOperator(" || ", node);
192        case "Negation": return InterpretFunc1("!", node);
193
194        case "IfThenElseStat": return InterpretIf(node);
195        case "WhileStat": return InterpretWhile(node);
196        case "BooleanExpression": return InterpretChild(node);
197        case "NumericalExpression": return InterpretChild(node);
198        case "Number": return InterpretNumber(node);
199        case "BooleanValue": return InterpretBoolValue(node);
200
201
202        case "Ahead": return InterpretFunc1("setAhead", node);
203        case "Back": return InterpretFunc1("setBack", node);
204        case "Fire": return InterpretFunc1("setFire", node);
205        case "TurnLeft": return InterpretFunc1("setTurnLeft", node);
206        case "TurnRight": return InterpretFunc1("setTurnRight", node);
207        case "TurnGunLeft": return InterpretFunc1("setTurnGunLeft", node);
208        case "TurnGunRight": return InterpretFunc1("setTurnGunRight", node);
209        case "TurnRadarLeft": return InterpretFunc1("setTurnRadarLeft", node);
210        case "TurnRadarRight": return InterpretFunc1("setTurnRadarRight", node);
211        case "ShotPower": return InterpetShotPower(node);
212
213        case "OnBulletHit": return InterpretOnBulletHit(node);
214        case "OnBulletMissed": return InterpretOnBulletMissed(node);
215        case "OnHitByBullet": return InterpretOnHitByBullet(node);
216        case "OnHitRobot": return InterpretOnHitRobot(node);
217        case "OnHitWall": return InterpretOnHitWall(node);
218        case "OnScannedRobot": return InterpretOnScannedRobot(node);
219
220        case "Run": return InterpretRun(node);
221        case "Tank": return InterpretTank(node);
222        case "CodeSymbol": return InterpretCodeSymbol(node);
223
224        default: throw new ArgumentException(string.Format("Found an unknown symbol {0} in a robocode solution", node.Symbol.Name));
225      }
226    }
227
228    private static string InterpretCodeSymbol(ISymbolicExpressionTreeNode node) {
229      var sy = (CodeSymbol)node.Symbol;
230      string code = string.Join(Environment.NewLine, node.Subtrees.Select(Interpret));
231      return sy.Prefix + Environment.NewLine + code + Environment.NewLine + sy.Suffix;
232    }
233
234    private static string InterpretBoolValue(ISymbolicExpressionTreeNode node) {
235      var boolNode = (BooleanTreeNode)node;
236      return string.Format(NumberFormatInfo.InvariantInfo, "{0}", boolNode.Value).ToLower();
237    }
238
239    private static string InterpretNumber(ISymbolicExpressionTreeNode node) {
240      var numberNode = (NumberTreeNode)node;
241      return string.Format(NumberFormatInfo.InvariantInfo, "{0}", numberNode.Value);
242    }
243
244    private static string InterpetShotPower(ISymbolicExpressionTreeNode node) {
245      var shotPowerNode = (ShotPowerTreeNode)node;
246      return string.Format(NumberFormatInfo.InvariantInfo, "{0:E}", shotPowerNode.Value);
247    }
248
249
250    internal static string InterpretBlock(ISymbolicExpressionTreeNode node) {
251      string result = string.Join(Environment.NewLine, node.Subtrees.Select(Interpret));
252      return string.Format("{{ {0} }}", result + Environment.NewLine);
253    }
254
255    internal static string InterpretStat(ISymbolicExpressionTreeNode node) {
256      // must only have one sub-tree
257      Contract.Assert(node.SubtreeCount == 1);
258      return Interpret(node.GetSubtree(0)) + " ;" + Environment.NewLine;
259    }
260
261    internal static string InterpretIf(ISymbolicExpressionTreeNode node) {
262      ISymbolicExpressionTreeNode condition = null, truePart = null, falsePart = null;
263      string[] parts = new string[3];
264
265      if (node.SubtreeCount < 2 || node.SubtreeCount > 3)
266        throw new Exception("Unexpected number of children. Expected 2 or 3 children.");
267
268      condition = node.GetSubtree(0);
269      truePart = node.GetSubtree(1);
270      if (node.SubtreeCount == 3)
271        falsePart = node.GetSubtree(2);
272
273      parts[0] = Interpret(condition);
274      parts[1] = Interpret(truePart);
275      if (falsePart != null) parts[2] = Interpret(falsePart);
276
277      return string.Format("if ({0}) {{ {1} }} else {{ {2} }}", Interpret(condition), Interpret(truePart),
278        falsePart == null ? string.Empty : Interpret(falsePart));
279    }
280
281    internal static string InterpretWhile(ISymbolicExpressionTreeNode node) {
282      var cond = Interpret(node.GetSubtree(0));
283      var body = Interpret(node.GetSubtree(1));
284      return string.Format("while ({0}) {{ {2} {1} {2} }} {2}", cond, body, Environment.NewLine);
285    }
286
287    public static string InterpretBinaryOperator(string opSy, ISymbolicExpressionTreeNode node) {
288      if (node.SubtreeCount < 2)
289        throw new ArgumentException(string.Format("Expected at least two children in {0}.", node.Symbol), "node");
290
291      string result = string.Join(opSy, node.Subtrees.Select(Interpret));
292      return "(" + result + ")";
293    }
294
295    public static string InterpretChild(ISymbolicExpressionTreeNode node) {
296      if (node.SubtreeCount != 1)
297        throw new ArgumentException(string.Format("Expected exactly one child in {0}.", node.Symbol), "node");
298
299      return Interpret(node.GetSubtree(0));
300    }
301
302    public static string InterpretComparison(string compSy, ISymbolicExpressionTreeNode node) {
303      ISymbolicExpressionTreeNode lhs = null, rhs = null;
304      if (node.SubtreeCount != 2)
305        throw new ArgumentException(string.Format("Expected exactly two children in {0}.", node.Symbol), "node");
306
307      lhs = node.GetSubtree(0);
308      rhs = node.GetSubtree(1);
309
310      return Interpret(lhs) + compSy + Interpret(rhs);
311    }
312
313    public static string InterpretFunc1(string functionId, ISymbolicExpressionTreeNode node) {
314      if (node.SubtreeCount != 1)
315        throw new ArgumentException(string.Format("Expected 1 child in {0}.", node.Symbol.Name), "node");
316
317      return string.Format("{0}({1})", functionId, Interpret(node.GetSubtree(0)));
318    }
319
320    public static string InterpretOnScannedRobot(ISymbolicExpressionTreeNode node) {
321      string code = string.Join(Environment.NewLine, node.Subtrees.Select(Interpret));
322      return string.Format(
323@"public void onScannedRobot(ScannedRobotEvent e) {{
324  double absoluteBearing = getHeading() + e.getBearing();
325  double bearingFromGun = normalRelativeAngleDegrees(absoluteBearing - getGunHeading());
326  setTurnGunRight(bearingFromGun);
327{0}
328  execute();
329}}", code);
330    }
331
332
333    public static string InterpretOnHitWall(ISymbolicExpressionTreeNode node) {
334      string code = string.Join(Environment.NewLine, node.Subtrees.Select(Interpret));
335      return string.Format(
336@"public void onHitWall(HitWallEvent e) {{
337{0}
338execute();
339}}", code);
340    }
341
342    public static string InterpretOnHitRobot(ISymbolicExpressionTreeNode node) {
343      string code = string.Join(Environment.NewLine, node.Subtrees.Select(Interpret));
344      return string.Format(
345@"public void onHitRobot(HitRobotEvent e) {{
346{0}
347execute();
348}}", code);
349    }
350
351    public static string InterpretOnHitByBullet(ISymbolicExpressionTreeNode node) {
352      string code = string.Join(Environment.NewLine, node.Subtrees.Select(Interpret));
353      return string.Format(
354@"public void onHitByBullet(HitByBulletEvent e) {{
355{0}
356execute();
357}}", code);
358    }
359
360    public static string InterpretOnBulletMissed(ISymbolicExpressionTreeNode node) {
361      string code = string.Join(Environment.NewLine, node.Subtrees.Select(Interpret));
362      return string.Format(
363@"public void onBulletMissed(BulletMissedEvent e) {{
364{0}
365execute();
366}}", code);
367    }
368
369    public static string InterpretOnBulletHit(ISymbolicExpressionTreeNode node) {
370      var Prefix = "public void onBulletHit(BulletHitEvent e) {";
371      var Suffix =
372@"execute();
373}";
374      string code = string.Join(Environment.NewLine, node.Subtrees.Select(Interpret));
375      return Prefix + code + Environment.NewLine + Suffix;
376    }
377
378    public static string InterpretRun(ISymbolicExpressionTreeNode node) {
379      string code = string.Join(Environment.NewLine, node.Subtrees.Select(Interpret));
380      return string.Format(
381@"public void run() {{
382  setAdjustGunForRobotTurn(true);
383  turnRadarRightRadians(Double.POSITIVE_INFINITY);
384{0}
385  execute();
386}}", code);
387    }
388
389    public static string InterpretTank(ISymbolicExpressionTreeNode node) {
390      string code = string.Join(Environment.NewLine, node.Subtrees.Select(Interpret));
391      return string.Format(
392@"package Evaluation;
393import robocode.*;
394import robocode.Robot;
395import robocode.util.*;
396import static robocode.util.Utils.normalRelativeAngleDegrees;
397import java.awt.*;
398
399public class output extends AdvancedRobot {{
400{0}
401}}", code);
402    }
403  }
404}
Note: See TracBrowser for help on using the repository browser.