private async static Task <Solution> Pack_async(PartList parts, BoardList stock) { List <Task> threads = new List <Task>(); int stockcount = stock.Count; Solution[] solutions = new Solution[stockcount]; for (int i = 0; i < stockcount; i++) { threads.Add( Task.Factory.StartNew(async(o) => { int ii = (int)o; Item iStock = stock[ii]; PartList mycopyofParts = parts.Copy(); Part iPart = mycopyofParts[0]; mycopyofParts.RemoveAt(0); if (!iStock.TrySplit(iPart, out Item H1, out Item H2, out Item V1, out Item V2)) { return; } if (mycopyofParts.Count == 0) { solutions[ii] = new Solution(iPart, iStock); return; } BoardList myVstock = stock.Copy(); myVstock.Remove(iStock); myVstock.AddRange(V2, V1); Solution solV = PackALL(mycopyofParts, myVstock); if (solV != null) { solutions[ii] = new Solution(iPart, iStock); solutions[ii].AddRange(solV); return; } BoardList myHstock = stock.Copy(); myHstock.Remove(iStock); myHstock.AddRange(H2, H1); Solution solH = PackALL(mycopyofParts, myHstock); if (solH != null) { solutions[ii] = new Solution(iPart, iStock); solutions[ii].AddRange(solH); return; } }, i)); } Task.WaitAll(threads.ToArray()); return(solutions.FirstOrDefault(t => t != null)); }
/// <summary> /// ALL parts must fit for an acceptable solution - we use this to check if a given combination of parts will fit on a board. /// </summary> /// <param name="parts"></param> /// <param name="stock"></param> /// <returns></returns> private static Solution PackALL(PartList parts, BoardList stock) { Solution sol = new Solution(); PartList mycopyofParts = parts.Copy(); Part iPart = mycopyofParts[0]; mycopyofParts.RemoveAt(0); int stockcount = stock.Count; for (int i = 0; i < stockcount; i++) { Item iStock = stock[i]; if (!iStock.TrySplit(iPart, out Item H1, out Item H2, out Item V1, out Item V2)) { continue; } if (mycopyofParts.Count == 0) { sol.Add(iPart, iStock); return(sol); } BoardList myVstock = stock.Copy(); myVstock.Remove(iStock); myVstock.AddRange(V2, V1); Solution solV = PackALL(mycopyofParts, myVstock); if (solV != null) { sol.Add(iPart, iStock); sol.AddRange(solV); return(sol); } BoardList myHstock = stock.Copy(); myHstock.Remove(iStock); myHstock.AddRange(H2, H1); Solution solH = PackALL(mycopyofParts, myHstock); if (solH != null) { sol.Add(iPart, iStock); sol.AddRange(solH); return(sol); } } return(null); }
public async static Task <Solution> Packold(PartList parts, BoardList boards) { #region // Algorithm : ... /* * for each board * remove all parts that will not fit on board * do * Get the combos of parts that have cumalative area smaller than board, but bigger than 90% of board (we aim high...) * level=level*level * while combos.count = 0 * sort combos by cum area desc * foreach combo * if can fit on board, break * * end for each board * keep board with leaste waste * * repeat for boards and parts left * */ #endregion if (parts.Count * boards.Count == 0) { return(null); } if (!parts.All(t => boards.Any(q => q.BiggerThan(t)))) { return(null); } PartList mycopyofParts = parts.Copy(); BoardList mycopyofBoards = boards.Copy(); mycopyofParts.Sort(Part.CompareByAreaDecending); Solution CompleteSolution = new Solution() { TotalStockArea = boards.Sum(t => t.Area) }; do { double minCoverageRatio = 0.9; int boardcount = mycopyofBoards.Count; List <Solution> iSolutionSet = new List <Solution>(); object lockobj = new object(); do { List <Task> threads = new List <Task>(); Trace.WriteLine($"Getting combinations for {boardcount} boards [{string.Join(",", mycopyofBoards.Select(t => t.Name))}] and {mycopyofParts.Count} parts with at least {minCoverageRatio * 100} % coverage"); for (int j = 0; j < boardcount; j++) { // get the best solution for every board threads.Add( Task.Factory.StartNew((o) => { Item iBoard = mycopyofBoards[(int)o]; var iCombos = GetCombos(mycopyofParts, iBoard, 0, minCoverageRatio * iBoard.Area); iCombos.Sort(Combo.CompareByCumAreaDesc); Trace.WriteLine($"Finding combinations for board {iBoard.Name}"); Solution topSolution = null; Combo topCombo = iCombos.FirstOrDefault(q => (topSolution = BruteForce.Pack_async(q.AsPartList(), iBoard).Result) != null); if (topSolution != null) { Trace.WriteLine($"Best satisfactory combination for board {iBoard.Name}: [{string.Join(",", topCombo.Select(q => q.Name))}] ; coverage = {(topSolution.PlacedArea / iBoard.Area * 100):0.0} %, waste = {topSolution.Waste / 1000000} m\u00b2"); iSolutionSet.Add(topSolution); } else { Trace.WriteLine($"No satisfactory combination for board {iBoard.Name}"); } }, j) ); } Task.WaitAll(threads.ToArray()); minCoverageRatio -= minCoverageRatio * minCoverageRatio; if (minCoverageRatio < 0.5) { minCoverageRatio = 0; } } while (iSolutionSet.Count == 0); // keep the best solution iSolutionSet.Sort(Solution.CompareByWasteAscending); // check if there is another solution that did not include any of these ... foreach (var topSol in iSolutionSet) { // if none of the parts in this solution is already part of the kept solutions if (!topSol.Any(t => CompleteSolution.FirstOrDefault(q => q.Part.Name == t.Part.Name) != null)) { // add this solution to the kept solutions Item topBoard = topSol.First().Stock; Trace.WriteLine($"Keeping solution for board {topBoard.Name}: [{string.Join(",", topSol.Select(q => q.Part.Name))}] ; coverage = {(topSol.PlacedArea / topBoard.Area * 100):0.0} %, waste = {topSol.Waste / 1000000} m\u00b2"); CompleteSolution.AddRange(topSol); // remove the parts and boards topSol.ForEach(t => mycopyofParts.Remove(t.Part)); mycopyofBoards.Remove(topBoard); } } Trace.WriteLine($"{mycopyofBoards.Count} boards and {mycopyofParts.Count} parts remain"); Trace.WriteLine(""); if (mycopyofBoards.Count == 0 && mycopyofParts.Count > 0) { return(null); } } while (mycopyofParts.Count > 0); return(CompleteSolution); }