source: trunk/sources/HeuristicLab.Problems.GeneticProgramming/3.3/robocode/Interpreter.cs @ 13210

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

#2069: copied relevant files for robocode problem from branch to trunk

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