[InlineData(1, "X", "A -> 'AB'", "X")] // implicit identity rule if no rule is found for a given symbol public void GenerateReturnsCorrectStateForGeneration(int n, string axiom, string rules, string expected) { var r = ParseRules(rules); var gen = new DeterministicStringGenerator(r); Assert.Equal(expected, gen.Generate(axiom, n)); }
public void CanAddProductions() { var gen = new DeterministicStringGenerator(); gen.AddProduction('A', "AB"); Assert.Equal("AB", gen.Generate("A", 1)); }
private static string RunFractal(int width, int height, string name, int generation) { var ctx = new SvgContext(width, height); double initialX; double initialY; int stepSize; double initialAngle; double turnAngle; string initialState; var lsys = new DeterministicStringGenerator(); switch (name) { case "koch-curve": initialX = 0; initialY = height / 2D; stepSize = 8; initialAngle = 0; turnAngle = 60; lsys.AddProduction('F', "F+F--F+F"); initialState = "F"; break; case "koch-curve-right": initialX = 0; initialY = height / 2D; stepSize = 8; initialAngle = 0; turnAngle = 90; lsys.AddProduction('F', "F+F-F-F+F"); initialState = "F"; break; case "sierpinski-arrow": initialX = 0; initialY = height; stepSize = 2; initialAngle = 0; turnAngle = 60; lsys.AddProduction('F', "G-F-G"); lsys.AddProduction('G', "F+G+F"); initialState = "F"; break; case "sierpinski-tri": initialX = width / 2D; initialY = height / 4D; stepSize = 6; initialAngle = 0; turnAngle = 60; lsys.AddProduction('F', "FF"); lsys.AddProduction('X', "--FXF++FXF++FXF--"); initialState = "FXF--FF--FF"; break; case "plant1": initialX = width / 2D; initialY = height; stepSize = 7; initialAngle = 75; turnAngle = 22.5; lsys.AddProduction('0', "F-[[0]+0]+F[+F0]-0"); lsys.AddProduction('F', "FF"); initialState = "0"; break; case "plant2": initialX = width / 2D; initialY = height; stepSize = 10; initialAngle = 80; turnAngle = 22.5; lsys.AddProduction('F', "FF-[-F+F+F]+[+F-F-F]"); initialState = "F"; break; case "dragon-curve": initialX = width / 2D; initialY = height / 2D; stepSize = 4; initialAngle = 0; turnAngle = 90; lsys.AddProduction('X', "X+YF+"); lsys.AddProduction('Y', "-FX-Y"); initialState = "FX"; break; case "hex-gosper-curve": initialX = width / 2D; initialY = height / 2D; stepSize = 6; initialAngle = 60; turnAngle = 60; lsys.AddProduction('F', "F-G--G+F++FF+G-"); lsys.AddProduction('G', "+F-GG--G-F++F+G"); initialState = "F"; break; case "peano-curve": initialX = width / 2D; initialY = height / 2D; stepSize = 4; initialAngle = 0; turnAngle = 90; lsys.AddProduction('F', "F+F-F-F-F+F+F+F-F"); initialState = "F"; break; case "quad-koch-island": initialX = width / 2D; initialY = height / 2D; stepSize = 3; initialAngle = 0; turnAngle = 90; lsys.AddProduction('F', "F-F+F+FFF-F-F+F"); initialState = "F+F+F+F"; break; case "32-segment-curve": initialX = width / 2D; initialY = height / 2D; stepSize = 2; initialAngle = 0; turnAngle = 90; lsys.AddProduction('F', "-F+F-F-F+F+FF-F+F+FF+F-F-FF+FF-FF+F+F-FF-F-F+FF-F-F+F+F-F+"); initialState = "F+F+F+F"; break; case "sierpinski-arrow-alt": initialX = width / 4D; initialY = height; stepSize = 2; initialAngle = 0; turnAngle = 60; lsys.AddProduction('X', "YF+XF+Y"); lsys.AddProduction('Y', "XF-YF-X"); initialState = "YF"; break; case "peano-gosper-curve": initialX = width / 2D; initialY = height / 2D; stepSize = 4; initialAngle = 0; turnAngle = 60; lsys.AddProduction('X', "X+YF++YF-FX--FXFX-YF+"); lsys.AddProduction('Y', "-FX+YFYF++YF+FX--FX-Y"); initialState = "FX"; break; case "square-curve": initialX = width / 2D; initialY = height / 2D; stepSize = 4; initialAngle = 0; turnAngle = 90; lsys.AddProduction('X', "XF-F+F-XF+F+XF-F+F-X"); initialState = "F+XF+F+XF"; break; case "hilbert-curve": initialX = width / 2D; initialY = height / 2D; stepSize = 6; initialAngle = 0; turnAngle = 90; lsys.AddProduction('L', "+RF-LFL-FR+"); lsys.AddProduction('R', "-LF+RFR+FL-"); initialState = "L"; break; case "hilbert-curve-2": initialX = width / 2D; initialY = height / 2D; stepSize = 4; initialAngle = 0; turnAngle = 90; lsys.AddProduction('X', "XFYFX+F+YFXFY-F-XFYFX"); lsys.AddProduction('Y', "YFXFY-F-XFYFX+F+YFXFY"); initialState = "X"; break; default: var fileName = $"{name}.lsys"; var files = Directory.GetFiles(Directory.GetCurrentDirectory()); if (files.All(s => Path.GetFileName(s) != fileName)) { throw new ArgumentException($"Unknown fractal name: '{name}'"); } var file = File.OpenText(fileName); var parsed = new FractalDefinitionParser().Parse(file); initialX = parsed.StartX; initialY = parsed.StartY; stepSize = parsed.StepSize; initialAngle = parsed.StartAngle; turnAngle = parsed.TurnAngle; initialState = parsed.Axiom; foreach (var kvp in parsed.Rules) { lsys.AddProduction(kvp.Key, kvp.Value); } break; } var turtle = new Turtle(ctx, stepSize, initialX, initialY, initialAngle); var interpreter = new TurtleStringInterpreter(turtle); interpreter.AddAction('F', t => t.Forward()).AddAction('G', t => t.Forward()); interpreter.AddAction('f', t => t.Move()).AddAction('g', t => t.Move()); interpreter.AddAction('-', t => t.TurnRight(turnAngle)); interpreter.AddAction('+', t => t.TurnLeft(turnAngle)); interpreter.AddAction('[', t => t.PushState()); interpreter.AddAction(']', t => t.PopState()); var derivation = lsys.Generate(initialState, generation); interpreter.Run(derivation); return(ctx.ToString()); }