Free cookie consent management tool by TermsFeed Policy Generator

source: branches/HeuristicLab.Problems.GrammaticalOptimization/HeuristicLab.Problems.GrammaticalOptimization/Problems/SantaFeAntProblem.cs @ 12840

Last change on this file since 12840 was 12840, checked in by aballeit, 9 years ago

#2283 Final

File size: 19.2 KB
Line 
1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using System.Linq;
5using System.Runtime.InteropServices;
6using System.Runtime.Remoting.Messaging;
7using System.Text;
8using System.Text.RegularExpressions;
9using HeuristicLab.Encodings.SymbolicExpressionTreeEncoding;
10
11namespace HeuristicLab.Problems.GrammaticalOptimization
12{
13    public class SantaFeAntProblem : ISymbolicExpressionTreeProblem
14    {
15        private const string grammarString = @"
16G(A):
17A -> l | r | m | ?(A)(A) | ?(A)(A)A | lA | rA | mA
18";
19        // for tree-based GP in HL we need a different grammar for the same language
20        // A = Ant, P = Prog2 and Prog3, I = IfFoodAhead
21        private const string hlGrammarString = @"
22    G(A):
23    A -> l | r | m | P | I
24    P -> AA | AAA
25    I -> AA
26    ";
27
28
29        // original koza grammar
30        // Ant -> left | right | move | if-food-ahead Ant Ant | Ant Ant | Ant Ant Ant
31        //
32        // here we use a modified grammar which has only one syntax tree for each sentence
33
34
35        private readonly IGrammar grammar;
36        public string Name { get { return "SantaFeAnt"; } }
37
38        public SantaFeAntProblem()
39        {
40            this.grammar = new Grammar(grammarString);
41            this.TreeBasedGPGrammar = new Grammar(hlGrammarString);
42        }
43
44        public double BestKnownQuality(int maxLen)
45        {
46            // for now only an upper bound is returned, ideally all food pellets are discovered
47            return 89;
48        }
49
50        public IGrammar Grammar
51        {
52            get { return grammar; }
53        }
54
55        public double Evaluate(string sentence)
56        {
57            var ant = new Ant();
58            int p = 0;
59            Run(ant, sentence, ref p);
60            return ant.FoodEaten;
61        }
62
63        private static void Run(Ant ant, string sentence, ref int p, bool stopAfterFirst = false)
64        {
65            while (!ant.Done())
66            {
67                if (p >= sentence.Length)
68                {
69                    if (stopAfterFirst) return;
70                    p = 0; // restart
71                }
72                switch (sentence[p])
73                {
74                    case 'r':
75                        {
76                            ant.TurnRight();
77                            p++;
78                            break;
79                        }
80                    case 'l':
81                        {
82                            ant.TurnLeft();
83                            p++;
84                            break;
85                        }
86                    case 'm':
87                        {
88                            ant.Move();
89                            p++;
90                            break;
91                        }
92                    case '?':
93                        {
94                            p++; // skip p
95                            if (ant.IsFoodAhead())
96                            {
97                                if (sentence[p] != '(') throw new ArgumentException();
98                                p++;
99                                Run(ant, sentence, ref p);      // it cannot happen that we run over the the end of the sentence here
100                                if (ant.Done()) return;
101                                if (sentence[p] != '(') throw new ArgumentException();
102                                p++;
103                                Skip(sentence, ref p);
104                            }
105                            else
106                            {
107                                if (sentence[p] != '(') throw new ArgumentException();
108                                p++;
109                                Skip(sentence, ref p);
110                                if (sentence[p] != '(') throw new ArgumentException();
111                                p++;
112                                Run(ant, sentence, ref p);    // it cannot happen that we run over the the end of the sentence here
113                            }
114                            break;
115                        }
116                    case '.':
117                        {
118                            // nop
119                            p++;
120                            ant.Nop();
121                            break;
122                        }
123                    case ')':
124                        {
125                            p++; // skip
126                            return;     // caller must continue processing
127                        }
128                    default:
129                        {
130                            throw new ArgumentException();
131                        }
132                }
133            }
134        }
135
136        private static void Skip(string sentence, ref int p)
137        {
138            int openCount = 1;
139            while (openCount > 0)
140            {
141                if (sentence[p] == '(') openCount++;
142                else if (sentence[p] == ')') openCount--;
143                p++;
144            }
145        }
146
147        public string CanonicalRepresentation(string phrase)
148        {
149            //phrase = phrase.Replace("A", ".");
150            var sb = new StringBuilder(phrase);
151            string canonicalPhrase = phrase;
152            string oldPhrase;
153            do
154            {
155                oldPhrase = canonicalPhrase;
156                sb.Replace("ll", "rr").Replace("rl", "").Replace("lr", "").Replace("lll", "r").Replace("rrr", "l").Replace("?()()", "");
157                //sb.Replace("?(m)(m)", "?()()m").Replace("?(l)(l)", "?()()l").Replace("?(r)(r)", "?()()r");
158                canonicalPhrase = sb.ToString();
159            } while (canonicalPhrase != oldPhrase);
160            return canonicalPhrase;
161        }
162
163        public IEnumerable<Feature> GetFeatures(string phrase)
164        {
165            phrase = CanonicalRepresentation(phrase);
166            var isTerminal = grammar.IsTerminal(phrase);
167
168            yield return new Feature(isTerminal + ToString(), 1.0);
169
170            yield return new Feature("$" + (phrase.Length > 0 ? phrase[0] : ' '), 1.0);
171            if (!isTerminal)
172            {
173                for (int i = 4; i < phrase.Length; i++)
174                {
175                    if (!grammar.IsTerminal(phrase[i]))
176                    {
177                        yield return new Feature(phrase[i - 4].ToString() + phrase[i - 3].ToString() + phrase[i - 2] + phrase[i - 1], 1.0);
178                        break;
179                    }
180                }
181            }
182
183            yield return new Feature(phrase, 1.0);
184        }
185
186        public IGrammar TreeBasedGPGrammar { get; private set; }
187        public string ConvertTreeToSentence(ISymbolicExpressionTree tree)
188        {
189            var sb = new StringBuilder();
190            ConvertTreeToSentence(tree.Root.GetSubtree(0).GetSubtree(0), sb);
191            return sb.ToString();
192        }
193        private void ConvertTreeToSentence(ISymbolicExpressionTreeNode node, StringBuilder sb)
194        {
195            if (node.SubtreeCount == 0)
196            {
197                // terminal
198                sb.Append(node.Symbol.Name);
199            }
200            else if (node.Symbol.Name == "I")
201            {
202                Debug.Assert(node.SubtreeCount == 2);
203                sb.Append("?(");
204                ConvertTreeToSentence(node.GetSubtree(0), sb);
205                sb.Append(")(");
206                ConvertTreeToSentence(node.GetSubtree(1), sb);
207                sb.Append(")");
208            }
209            else
210            {
211                foreach (var subTree in node.Subtrees) { ConvertTreeToSentence(subTree, sb); }
212            }
213        }
214
215        private string[] solutions;
216
217        public void GenerateProblemSolutions(int maxLen)
218        {
219            solutions = new string[12];
220            solutions[0] = "?(m)(ll?(m)(r))mr";
221            solutions[1] = "?(m)(rr?(m)(l))ml";
222            solutions[2] = "r?(m)(ll?(m)(r))m";
223            solutions[3] = "l?(m)(rr?(m)(l))m";
224            solutions[4] = "mr?(m)(ll?(m)(r))";
225            solutions[5] = "ml?(m)(rr?(m)(l))";
226            solutions[6] = "?(m)(ll?(m)(l))ml";
227            solutions[7] = "?(m)(rr?(m)(r))mr";
228            solutions[8] = "l?(m)(ll?(m)(l))m";
229            solutions[9] = "r?(m)(rr?(m)(r))m";
230            solutions[10] = "ml?(m)(ll?(m)(l))";
231            solutions[11] = "mr?(m)(rr?(m)(r))";
232
233            // A
234            // rA
235            // lA
236            // mA
237            // mlA
238            // mrA
239            // ?(A)(A)A
240            // mr?(m)(ll?(A)(A))
241            // ?(A)(A)?(A)(A)
242        }
243
244        private readonly List<string> validStarts3 = new List<string> { "A", "rA", "lA", "mA", "mlA", "mrA" };
245
246
247        public bool IsParentOfProblemSolution(string sentence, int maxLen)
248        {
249            //INFO: only works with maxlen 17!!
250
251            // eliminate wrong parents..
252            if (sentence.Length < 17 && !sentence.Contains('A'))
253            {
254                return false;
255            }
256            if (sentence.Length > 3 && !sentence.Contains('?'))
257            {
258                return false;
259            }
260            if (sentence.Length <= 3)
261            {
262                return validStarts3.Contains(sentence);
263            }
264            if (sentence.Contains("m?"))
265            {
266                return false;
267            }
268
269            if (sentence.Length <= 17 && sentence.Contains('A'))
270            {
271                // check front part of solutions..
272                // contains A, contains ?(x)(x), length > 3
273                int index = sentence.IndexOf('?');
274                if (index > 2) return false;
275
276                int firstCloseBracket = sentence.IndexOf(')');
277                // only one item in bracket
278                if ((firstCloseBracket - index) != 3) return false;
279                // must be m or A
280                if (sentence[firstCloseBracket - 1] != 'm' && sentence[firstCloseBracket - 1] != 'A') return false;
281
282                // check back and front part of solutions..
283                int lastCloseBracket = sentence.LastIndexOf(')');
284                // before first ? and after last ) either 1 left one right, or only two left (also A), or only two right (also A)
285                int diff;
286                if (index == 0)
287                {
288                    // must end with mr, ml, mA, A
289                    diff = sentence.Length - 1 - lastCloseBracket;
290                    if (diff == 0 || diff > 2) return false;
291                    if (diff == 1 && sentence[sentence.Length - 1] != 'A') return false;
292                    if (diff == 2 && (sentence[sentence.Length - 2] != 'm' || sentence[sentence.Length - 1] == 'm'))
293                        return false;
294                }
295                else if (index == 1 && (sentence[0] == 'm' || (sentence[sentence.Length - 1] != 'm' && sentence[sentence.Length - 1] != 'A'))) return false;
296                else if (index == 2)
297                {
298                    // must start with ml, mr, mA
299                    // must end with )
300                    if (sentence[sentence.Length - 1] != ')') return false;
301                    if (sentence[0] != 'm' || sentence[1] == 'm') return false;
302                }
303
304                // check middle part of solutions ..(ll?(m)(l))..
305                int index2 = sentence.IndexOf('?', index + 1);
306                if (index2 == -1)
307                {
308                    // no second ? so far..
309                    // allowed within second (..): A, lA, llA, rA, rrA
310                    int lastOpenBracket = sentence.LastIndexOf('(', lastCloseBracket);
311                    diff = lastCloseBracket - lastOpenBracket;
312                    if (diff > 4) return false;
313                    // last item must be A
314                    if (sentence[lastCloseBracket - 1] != 'A') return false;
315                    // first item must not be m
316                    if (sentence[lastOpenBracket + 1] == 'm') return false;
317                    // ll or rr
318                    if (diff == 4 && sentence[lastOpenBracket + 1] != sentence[lastOpenBracket + 2]) return false;
319                }
320                else
321                {
322                    // second ? there..
323                    // ?(x)(..?(x)(x))
324                    int secondLastCloseBracket = sentence.LastIndexOf(')', lastCloseBracket - 1);
325                    // has to end with '))'
326                    if (lastCloseBracket - secondLastCloseBracket != 1) return false;
327                    // only one item within second brackets
328                    if (sentence[secondLastCloseBracket - 2] != '(') return false;
329                    // the one item can be A, l or r
330                    if (sentence[secondLastCloseBracket - 1] == 'm') return false;
331                    // only one item in first brackets
332                    if (sentence[index2 + 3] != ')') return false;
333                    // only m or A allowed
334                    if (sentence[index2 + 2] == 'l' || sentence[index2 + 2] == 'r') return false;
335                    // ll or rr before second ? must already be there..
336                    if (sentence[index2 - 1] != 'l' && sentence[index2 - 1] != 'r') return false;
337                    if (sentence[index2 - 1] != sentence[index2 - 2]) return false;
338                }
339            }
340            else
341            {
342                // has to be solution or false..
343                return solutions.Any(solution => solution == sentence);
344            }
345
346            return true;
347        }
348    }
349
350    public class Ant
351    {
352        private int maxSteps = 600;
353        public int MaxSteps { get { return maxSteps; } set { maxSteps = value; } }
354        public enum HeadingEnum { North, East, South, West };
355        public int FoodEaten { get; private set; }
356        private readonly char[][] world = new char[32][];
357        private int posX;
358        private int posY;
359        private int steps;
360        private HeadingEnum heading;
361        public int PosX { get { return posX; } }
362        public int PosY { get { return posY; } }
363        public HeadingEnum Heading { get { return heading; } }
364        private bool recordTrail = false;
365        private StringBuilder trailBuilder;
366
367        public string Trail
368        {
369            get
370            {
371                if (!recordTrail) throw new NotSupportedException();
372                else return trailBuilder.ToString() + heading; // add final heading as state
373            }
374        }
375
376        public Ant(bool recordTrail = false)
377        {
378            world[00] = " ###                            ".ToCharArray();
379            world[01] = "   #                            ".ToCharArray();
380            world[02] = "   #                    .###..  ".ToCharArray();
381            world[03] = "   #                    #    #  ".ToCharArray();
382            world[04] = "   #                    #    #  ".ToCharArray();
383            world[05] = "   ####.#####       .##..    .  ".ToCharArray();
384            world[06] = "            #       .        #  ".ToCharArray();
385            world[07] = "            #       #        .  ".ToCharArray();
386            world[08] = "            #       #        .  ".ToCharArray();
387            world[09] = "            #       #        #  ".ToCharArray();
388            world[10] = "            .       #        .  ".ToCharArray();
389            world[11] = "            #       .        .  ".ToCharArray();
390            world[12] = "            #       .        #  ".ToCharArray();
391            world[13] = "            #       #        .  ".ToCharArray();
392            world[14] = "            #       #  ...###.  ".ToCharArray();
393            world[15] = "            .   .#...  #        ".ToCharArray();
394            world[16] = "            .   .      .        ".ToCharArray();
395            world[17] = "            #   .      .        ".ToCharArray();
396            world[18] = "            #   #      .#...    ".ToCharArray();
397            world[19] = "            #   #          #    ".ToCharArray();
398            world[20] = "            #   #          .    ".ToCharArray();
399            world[21] = "            #   #          .    ".ToCharArray();
400            world[22] = "            #   .      ...#.    ".ToCharArray();
401            world[23] = "            #   .      #        ".ToCharArray();
402            world[24] = " ..##..#####.   #               ".ToCharArray();
403            world[25] = " #              #               ".ToCharArray();
404            world[26] = " #              #               ".ToCharArray();
405            world[27] = " #     .#######..               ".ToCharArray();
406            world[28] = " #     #                        ".ToCharArray();
407            world[29] = " .     #                        ".ToCharArray();
408            world[30] = " .####..                        ".ToCharArray();
409            world[31] = "                                ".ToCharArray();
410
411            posX = 0;
412            posY = 0;
413            heading = HeadingEnum.East;
414            FoodEaten = 0;
415            steps = 0;
416
417            this.recordTrail = recordTrail;
418            if (this.recordTrail) trailBuilder = new StringBuilder();
419        }
420
421        public bool Done()
422        {
423            return steps > maxSteps;
424        }
425
426        public void TurnRight()
427        {
428            if (steps <= maxSteps)
429            {
430                steps++;
431                if (heading == HeadingEnum.West) heading = HeadingEnum.North;
432                else heading++;
433            }
434        }
435
436        public void TurnLeft()
437        {
438            if (steps <= maxSteps)
439            {
440                steps++;
441                if (heading == HeadingEnum.North) heading = HeadingEnum.West;
442                else heading--;
443            }
444        }
445
446        public void Move()
447        {
448            if (steps <= maxSteps)
449            {
450                steps++;
451                MoveInternal(ref posX, ref posY);
452                if (world[posY][posX] == '#')
453                {
454                    FoodEaten++;
455                    world[posY][posX] = '.';
456                }
457                if (recordTrail) trailBuilder.Append("m" + posX + "x" + posY); // record position change
458            }
459        }
460
461        public void Nop()
462        {
463            // wait one time step
464            if (steps <= maxSteps)
465            {
466                steps++;
467            }
468        }
469
470        private void MoveInternal(ref int posX, ref int posY)
471        {
472            switch (heading)
473            {
474                case HeadingEnum.North:
475                    posY = (posY + 31) % 32;
476                    break;
477                case HeadingEnum.East:
478                    posX = (posX + 1) % 32;
479                    break;
480                case HeadingEnum.South:
481                    posY = (posY + 1) % 32;
482                    break;
483                case HeadingEnum.West:
484                    posX = (posX + 31) % 32;
485                    break;
486            }
487        }
488
489        public bool IsFoodAhead()
490        {
491            int nextPosX = posX;
492            int nextPosY = posY;
493            MoveInternal(ref nextPosX, ref nextPosY);
494
495            if (recordTrail) trailBuilder.Append("?" + nextPosX + "x" + nextPosY); // record check
496
497            return world[nextPosY][nextPosX] == '#';
498        }
499    }
500}
Note: See TracBrowser for help on using the repository browser.