public void Work(int id, Random rnd, XElement puzzledb, string filepath, List <BoardData> records, List <PatternData> patterns, List <RejectData> rejects, int maxNoMove, int minMove, int maxMove, int minScore, int idleFold, int maxSolutionSeconds) { List <ElementDetails> newdna = new List <ElementDetails>(); PatternData pattern = patterns.ElementAt(rnd.Next(patterns.Count())); char[] chrs = pattern.DNA.ToCharArray(); for (int i = 0; i < chrs.Length; i++) { char c = chrs[i]; newdna.Add(Element.CharToElementDetails(c)); } ElementDetails[] dna = newdna.ToArray(); Board board = new Board((byte)rnd.Next(pattern.MinWidth, pattern.MaxWidth), (byte)rnd.Next(pattern.MinHeight, pattern.MaxHeight)); board.Randomize(rnd, pattern, dna); Board original = new Board(board); ulong hash = original.FNV1aHash(); BoardData existing = records.Where(x => x.Hash == hash).FirstOrDefault(); if (existing != null) { //Console.WriteLine($"(Task {id}) Board already exists (hash: {hash})."); return; } RejectData rejected = rejects.Where(x => x.Hash == hash).FirstOrDefault(); if (rejected != null) { //Console.WriteLine($"(Task {id}) Board already rejected (hash: {hash})."); return; } for (int i = 0; i < idleFold; i++) { board.Fold(); } board.Place(new Element(Element.Player), board.StartY, board.StartX); //Console.WriteLine($"(Task {id}) Origin:"); //original.Dump(); //Console.WriteLine($"\n(Task {id}) Idle folded:"); //board.Dump(); Solution s = Solver.Solve(id, board, new TimeSpan(0, 0, maxSolutionSeconds), maxMove, 1f); if (s != null && s.Bound < minMove) { //Console.WriteLine($"(Task {id}) Move under the minimum ({minMove}), discarding."); puzzledb.Element("Rejects").Add( new XElement("Reject", new XElement("Hash", hash), new XElement("Reason", "MinMove") )); lock (puzzledb) { puzzledb.Save(filepath); } lock (rejects) { rejects.Add(new RejectData { Hash = hash }); } s = null; } else if (s == null && Solver.LastSearchResult == Solver.TIMEDOUT) { //Console.WriteLine($"(Task {id}) Timeout, couldn't verify board. Discarding."); puzzledb.Element("Rejects").Add( new XElement("Reject", new XElement("Hash", hash), new XElement("Reason", "Timeout with no solution"), new XElement("Data", original.ToString()) )); lock (puzzledb) { puzzledb.Save(filepath); } lock (rejects) { rejects.Add(new RejectData { Hash = hash }); } s = null; } else if (s == null) { //Console.WriteLine($"(Task {id}) No Solution found. Discarding."); puzzledb.Element("Rejects").Add( new XElement("Reject", new XElement("Hash", hash), new XElement("Reason", "Unsolveable") )); lock (puzzledb) { puzzledb.Save(filepath); } lock (rejects) { rejects.Add(new RejectData { Hash = hash }); } } if (s != null) { //Console.WriteLine($"(Task {id}) Trying to find better solutions."); float first = s.Bound; do { Solver.Tries++; Solution better = Solver.Solve(id, board, new TimeSpan(0, 0, maxSolutionSeconds), s.Bound, (s.Bound - 1) / first); if (better != null && better.Bound < s.Bound) { //Console.WriteLine($"(Task {id}) Better solution found: {better.Bound}"); s = better; if (s.Bound < minMove) { //Console.WriteLine($"(Task {id}) Move under the minimum ({minMove}), discarding."); s = null; break; } } else if (better == null && Solver.LastSearchResult == Solver.TIMEDOUT) { //Console.WriteLine($"(Task {id}) Timeout, couldn't verify board. Discarding."); puzzledb.Element("Rejects").Add( new XElement("Reject", new XElement("Hash", hash), new XElement("Width", original.ColCount), new XElement("Height", original.RowCount), new XElement("StartX", original.StartX), new XElement("StartY", original.StartY), new XElement("ExitX", original.ExitX), new XElement("ExitY", original.ExitY), new XElement("Reason", "Timeout while looking for a better solution"), new XElement("Idle", idleFold), new XElement("Data", original.ToString()) )); lock (puzzledb) { puzzledb.Save(filepath); } lock (rejects) { rejects.Add(new RejectData { Hash = hash }); } s = null; break; } else { //Console.WriteLine($"(Task {id}) No better solution, bailing."); break; } } while (true); if (s != null) { XElement solution = new XElement("Solution"); int steps = 0; Board prev = null; int len = s.Path[0].Data.Length; int diffTotal = 0; int goalTotal = 0; int mobBefore = 0; int mobAfter = 0; int fallingDelta = 0; int proximity = 0; foreach (Board b in s.Path) { var foldStr = b.ToString(); var fold = new XElement("Fold", foldStr); fold.SetAttributeValue("Move", b.NameMove()); int goals = b.Data.Where(x => x != null && x.Details == Element.Diamond).Count(); //fold.SetAttributeValue("Goals", goals); goalTotal += goals; var diffs = 0; if (prev != null) { for (int i = 0; i < len; i++) { if (prev.Data[i].Details != b.Data[i].Details) { diffs++; } } fallingDelta += prev.Data.Count(x => x.Falling) - b.Data.Count(x => x.Falling); } diffTotal += diffs; //fold.SetAttributeValue("Diffs", diffs); //var ff = b.Data.Count(x => x.Falling); //if (ff > 0) // fold.SetAttributeValue("Falling", ff); //fold.SetAttributeValue("Space", b.Data.Count(x => x == null || x.Details == Element.Space)); var moveOrder = new StringBuilder(); Boolean before = true; for (int i = 0; i < len; i++) { var details = b.Data[i].Details; if (details.Mob) { if (i > 0 && b.Data[i - 1].Details == Element.Player) { proximity++; } if (i < len - 1 && b.Data[i + 1].Details == Element.Player) { proximity++; } if (i > b.ColCount && b.Data[i - b.ColCount].Details == Element.Player) { proximity++; } if (i < b.ColCount - 1 && b.Data[i + b.ColCount].Details == Element.Player) { proximity++; } } if (details.Mob || details == Element.Boulder || details == Element.Diamond || details == Element.Player) { moveOrder.Append(details.Symbols[details.StartFacing]); if (details != Element.Player) { if (details.Mob) { if (before) { mobBefore++; } else { mobAfter++; } } } else { before = false; } } } //if (proximity > 0) // fold.SetAttributeValue("Proximity", proximity); //fold.SetAttributeValue("MoveOrder", moveOrder.ToString()); prev = b; solution.Add(fold); steps++; } fallingDelta = Math.Max(-10, fallingDelta * -1); int score = ((int)Math.Ceiling((double)diffTotal / (s.Path.Count - 1)) * 10) + ((mobBefore / s.Path.Count) * 5) + ((mobAfter / s.Path.Count) * 2) + (fallingDelta * 5) + len + ((goalTotal / s.Path.Count) * 3) + (proximity * 12); solution.SetAttributeValue("AvgDiff", (int)Math.Ceiling((double)diffTotal / (s.Path.Count - 1))); solution.SetAttributeValue("AvgGoals", goalTotal / s.Path.Count); solution.SetAttributeValue("AvgBefore", (mobBefore / s.Path.Count)); solution.SetAttributeValue("AvgAfter", (mobAfter / s.Path.Count)); solution.SetAttributeValue("FallingDelta", fallingDelta); solution.SetAttributeValue("Proximity", proximity); solution.SetAttributeValue("Score", score); if (score >= minScore) { puzzledb.Element("Boards").Add(new XElement("Board", new XElement("Hash", original.FNV1aHash()), new XElement("Width", original.ColCount), new XElement("Height", original.RowCount), new XElement("Par", steps), new XElement("StartX", original.StartX), new XElement("StartY", original.StartY), new XElement("ExitX", original.ExitX), new XElement("ExitY", original.ExitY), new XElement("Idle", idleFold), new XElement("Data", original.ToString()), solution )); lock (puzzledb) { puzzledb.Save(filepath); } lock (records) { records.Add(new BoardData { Hash = hash }); } } else { puzzledb.Element("Rejects").Add( new XElement("Reject", new XElement("Hash", hash), new XElement("Width", original.ColCount), new XElement("Height", original.RowCount), new XElement("StartX", original.StartX), new XElement("StartY", original.StartY), new XElement("ExitX", original.ExitX), new XElement("ExitY", original.ExitY), new XElement("Reason", "Min score"), new XElement("Idle", idleFold), new XElement("Data", original.ToString()), solution )); lock (puzzledb) { puzzledb.Save(filepath); } lock (rejects) { rejects.Add(new RejectData { Hash = hash }); } } // Console.WriteLine($"(Task {id}) Puzzle added to DB"); } } }
public void Randomize(Random rnd, PatternData pattern, ElementDetails[] dna) { ElementDetails[] nonMobs = Array.FindAll(dna, x => !x.Mob); for (int i = 0; i < RowCount; i++) { for (int j = 0; j < ColCount; j++) { int pick = rnd.Next(nonMobs.Length); Element element = new Element(nonMobs[pick]); Data[(i * ColCount) + j] = element; } } ElementDetails[] mobs = Array.FindAll(dna, x => x.Mob); int mobCount = (int)Math.Round((RowCount * ColCount) * pattern.MobRatio); for (int i = 0; i < mobCount; i++) { int mx, my; do { mx = rnd.Next(ColCount); my = rnd.Next(RowCount); Element under = GetElementAt(my, mx); if (under != null && !under.Details.Mob) { break; } } while (true); int pick = rnd.Next(mobs.Length); Element spawn = new Element(mobs[pick]); Place(spawn, my, mx); } foreach (PatternCommmand command in pattern.Commands) { int fx = (int)Math.Round(command.From.X * ColCount); int fy = (int)Math.Round(command.From.Y * RowCount); int tx = (int)Math.Round(command.To.X * ColCount); int ty = (int)Math.Round(command.To.Y * RowCount); if (command.Type.ToLower() == "line") { if (ty - fy < tx - fx) { float slopex = (ty - fy != 0) ? 1f / (float)(ty - fy) : 0f; float row = (float)fy; for (int i = fx; i < tx; i++) { Place(new Element(command.Element), (int)Math.Round(row), i); row += slopex; } } else { float slopey = (tx - fx != 0) ? 1f / (float)(tx - fx) : 0f; float col = (float)fx; for (int i = fy; i < ty; i++) { Place(new Element(command.Element), i, (int)Math.Round(col)); col += slopey; } } } else { if (command.Type.ToLower() == "rectangle") { for (int i = fx; i < tx; i++) { Place(new Element(command.Element), fy, i); Place(new Element(command.Element), ty, i); } for (int i = fy + 1; i < ty - 1; i++) { Place(new Element(command.Element), i, fx); Place(new Element(command.Element), i, tx); } } } } int px = rnd.Next(ColCount); int py = rnd.Next(RowCount); if (pattern.Start != null) { px = (int)Math.Round(pattern.Start.X * ColCount); py = (int)Math.Round(pattern.Start.Y * RowCount); } StartX = px; StartY = py; Place(new Element(Element.Steel), py, px); do { px = rnd.Next(ColCount); py = rnd.Next(RowCount); if (pattern.Exit != null) { px = (int)Math.Round(pattern.Exit.X * ColCount); py = (int)Math.Round(pattern.Exit.Y * RowCount); } Element under = GetElementAt(py, px); if (under.Details != null && under.Details == Element.Player) { continue; } if (px == StartX && py == StartY) { continue; } ExitX = px; ExitY = py; Place(new Element(Element.Steel), py, px); break; } while (true); }