private static void SetDouble(FileFrame frame, string name, Action <double> set)
 {
     if (frame.VariableSets.TryGetValue(name, out var str))
     {
         set(double.Parse(str));
     }
 }
 private static void SetString(FileFrame frame, string name, Action <string> set)
 {
     if (frame.VariableSets.TryGetValue(name, out var str))
     {
         set(str);
     }
 }
        public static IEnumerable <FileFrame> Parse(string path, ParserConfig config)
        {
            const int nWidth  = 4;
            const int nHeight = 3;

            foreach (var line in File.ReadLines(path))
            {
                var frame  = new FileFrame();
                var notes  = line;
                var tokens = line.Split(';');
                if (tokens.Length >= 2)
                {
                    notes = tokens[0];
                    foreach (var t in tokens.Skip(1))
                    {
                        var vtokens = t.Split('=');
                        frame.VariableSets[vtokens[0].Trim()] = vtokens[1].Trim();
                    }
                }
                CutDirection?dir         = null;
                List <char>  obstacleDef = null;
                foreach (var c in notes.Where(c => !char.IsWhiteSpace(c)))
                {
                    if (obstacleDef != null)
                    {
                        if (c == config.Obstacle)
                        {
                            throw new Exception("Already defining an obstacle");
                        }
                        else if (c == config.ObstacleEnd)
                        {
                            var a = config.Positions.IndexOf(obstacleDef[0]);
                            var b = config.Positions.IndexOf(obstacleDef[1]);
                            frame.Obstacles.Add(new Obstacle
                            {
                                X1     = a % nWidth,
                                Y1     = (a / nWidth) % nHeight,
                                X2     = b % nWidth,
                                Y2     = (b / nWidth) % nHeight,
                                Length = int.Parse(new string(obstacleDef.Skip(2).ToArray()))
                            });
                            obstacleDef = null;
                        }
                        else
                        {
                            obstacleDef.Add(c);
                        }
                        continue;
                    }
                    if (c == config.Obstacle)
                    {
                        obstacleDef = new List <char>();
                        continue;
                    }
                    if (c == config.Bomb)
                    {
                        dir = null;
                        continue;
                    }
                    var posIdx = config.Positions.IndexOf(c);
                    var dirIdx = config.Directions.IndexOf(c);
                    if (posIdx < 0 && dirIdx < 0)
                    {
                        throw new Exception("Unexpected character " + c);
                    }
                    if (dirIdx >= 0)
                    {
                        dir = (CutDirection)dirIdx;
                    }
                    else if (!dir.HasValue)
                    {
                        var x = (posIdx % nWidth);
                        var y = (posIdx / nWidth) % nHeight;
                        frame.Notes.Add(new Note {
                            X = x, Y = y, Color = NoteColor.Bomb
                        });
                    }
                    else
                    {
                        var color = (NoteColor)(posIdx / (nWidth * nHeight));
                        var x     = (posIdx % nWidth);
                        var y     = (posIdx / nWidth) % nHeight;
                        frame.Notes.Add(new Note {
                            X = x, Y = y, Color = color, CutDirection = dir.Value
                        });
                    }
                }

                yield return(frame);
            }
        }