public static ReportData GetReport(Level level, TimeSpan ts, TweakStats tweakStats = null) { var res = new ReportData(); res.lcStr = level.LevelConfiguration.GetStr(); res.totalSeconds = ts.TotalSeconds; res.segCount = level.Segs.Count; res.levelSize = $"{level.Width - 2}x{level.Height - 2}"; var decisions = GetDecisions(level); //hard decisions not done yet. doable now. var easyDecisions = decisions.Item1; var hardDecisions = decisions.Item2; res.decisionCount = easyDecisions.Count + hardDecisions.Count; res.coveragePercent = GetCoveragePercent(level, out int sqs); res.sqs = sqs; res.hardDecisionPercent = hardDecisions.Count * 1.0 / sqs * 100; res.easyDecisionPercent = easyDecisions.Count * 1.0 / sqs * 100; //Divergence = per seg, how distant other segs does it see? //problem - this prioritizes long paths. res.divergence = GetDivergence(level); res.avgNeighborCount = GetAvgNeighborCount(level); res.avgSegLen = GetAvgSegLen(level); var tweakTrySum = (tweakStats.SuccessCt + tweakStats.NoTweaks + tweakStats.NoTweaksQualify); var tweakSuccessPercent = tweakStats.SuccessCt * 100.0 / tweakTrySum; res.tweakSuccessPercent = tweakSuccessPercent; //TODO it would be cool to sparkline render various things like periodic segment length. return(res); }
public static void AfterLevelGenerated(Level level, LevelGenerationConfig config, string levelstem, LevelConfiguration lc, TweakStats tweakStats, TimeSpan ts, CsvWriter csv, Log log, int runcount) { var repdata = GetReport(level, ts, tweakStats); var rep = Report(repdata, multiline: true); log.Info(Report(repdata, multiline: false)); SaveLevelAsText(level, config.seed); if (config.saveCsv) { csv.Write(repdata); } if (config.saveEmpty) { SaveEmpty(level, $"{levelstem}/{lc.GetStr()}-empty-{config.seed} {runcount}.png", subtitle: rep, quiet: true); } if (config.saveEmptyUpperCorner) { SaveEmpty(level, $"{levelstem}/{lc.GetStr()}-corner-{config.seed} {runcount}.png", subtitle: rep, quiet: true, corner: true); } if (config.saveWithPath) { SaveWithPath(level, $"{levelstem}/{lc.GetStr()}-path-{config.seed} {runcount}.png", subtitle: rep, quiet: true); } if (config.saveArrows) { SaveArrowVersions(level, config.seed, levelstem, config.arrowLengthMin); } }
public override LinkedListNode <Seg> PickSeg(List <LinkedListNode <Seg> > newSegs, List <LinkedListNode <Seg> > modifiedSegs, TweakStats stats, bool success) { if (LastReturnedSeg == null) { LastReturnedSeg = Level.Segs.Last; return(Level.Segs.Last); } if (newSegs != null) { Success = true; var last = newSegs.Last(); LastReturnedSeg = last; return(last); } var val = LastReturnedSeg.Previous; if (val == null) { //we got all the way back to the start. if (Success) { val = Level.Segs.Last; LastReturnedSeg = val; Success = false; return(val); } else { return(null); } } val = LastReturnedSeg.Previous; LastReturnedSeg = val; return(val); }
public abstract LinkedListNode <Seg> PickSeg(List <LinkedListNode <Seg> > newSegs, List <LinkedListNode <Seg> > modifiedSegs, TweakStats stats, bool success);
/// <summary> /// When a seg length changes, I need to know about it - SL tweaks can do this invisibly which is annoying. /// </summary> public override LinkedListNode <Seg> PickSeg(List <LinkedListNode <Seg> > newSegs, List <LinkedListNode <Seg> > modifiedSegs, TweakStats stat, bool success) { //put in the new segs. if (newSegs != null) { foreach (var newSeg in newSegs) { AddSafely(newSeg); } } if (modifiedSegs != null) { foreach (var modseg in modifiedSegs) { RemoveSafely(modseg); AddSafely(modseg); } } if (success) { LoopStats.SuccessCt++; Success = true; } else { LoopStats.NoTweaks++; } if (Heap.Count == 0) { if (Success) { RedoHeap(); Success = false; stat.loopct++; var lastLoopSuccessPercentage = LoopStats.SuccessCt * 1.0 / (LoopStats.SuccessCt + LoopStats.NoTweaksQualify + LoopStats.NoTweaks); WL($"lastLoopSuccessPercentage: {stat.loopct} {lastLoopSuccessPercentage}"); //fail condition. if (stat.loopct > 2) { //hardcore return early. return(null); } } else { return(null); } } //the item will still hang around in the handles dict. var el = Heap.DeleteMax(); //WL($"Returning seg {el.Value.Index} of len {el.Value.Len}"); return(el); }
public override void Init(int seed, Level level) { BaseInit(seed, level); RedoHeap(); LoopStats = new TweakStats(); }
public TweakStats RepeatedlyTweak(bool saveState, int saveEvery, Stopwatch st) { //after doing the tweak we will call the segpicker with previous, new segs, next seg, success //initial setup. //change to do this all in the picker itself - it should keep state var stats = new TweakStats(); var current = LevelConfiguration.SegPicker.PickSeg(null, null, stats, false); var tweakct = 0; var lastLoopCt = 0; while (current != null) { var tweaks = new List <Tweak>() { }; tweaks.AddRange(GetTweaks(current, true)); tweaks.AddRange(GetTweaks(current, false)); //WL($"Generated {tweaks.Count} for {current.Value}: {current.Value.History}"); if (!tweaks.Any()) { stats.NoTweaks++; current = LevelConfiguration.SegPicker.PickSeg(null, null, stats, false); //WL("NOtweaks"); if (current != null) { current.Value.History += " no"; } continue; } //TODO is it bad that when we loop, we re-add every seg even ones which clearly haven't had anything change in the neighborhood. //most of the time the algo runs is recalculating things downstream of this decision var tweak = LevelConfiguration.TweakPicker.Picker.Invoke(tweaks); //if (tweak == null) //{ // //this should never happen now that I'm not using exclusionary tweakpickers. // stats.NoTweaksQualify++; // current = LevelConfiguration.SegPicker.PickSeg(null, null, stats, false); // WL("noqualifying"); // current.Value.History += " noq"; // continue; //} //WL($"\tGottweak {tweak}"); //seg is always destroyed. BUT, prev/next can also be modified. //can I just pass that along and have it removed/added with new len? var newSegs = ApplyTweak(tweak); stats.SuccessCt++; var modifiedSegs = new List <LinkedListNode <Seg> >() { }; //these should never be null since we don't allow longtweaks where next is null, or shorts to start. if (tweak.LongTweak) { modifiedSegs.Add(newSegs.Last().Next); } if (tweak.ShortTweak) { modifiedSegs.Add(newSegs.First().Previous); } //var nei = Solverutil.GetNeighbors(newSegs.Last().Value); var newLoop = false; if (stats.loopct > lastLoopCt) { lastLoopCt = stats.loopct; newLoop = true; } PossiblySaveDuringTweak(saveState, saveEvery, stats, newLoop: newLoop); if (stats.SuccessCt != 0 && stats.SuccessCt % 10000 == 0) { WL($"{stats.loopct} Segs={Segs.Count} Cov={GetCoveragePercent(this, out int _)} {st.Elapsed} tweakSuccess:{(stats.SuccessCt * 1.0 / (stats.NoTweaks+stats.NoTweaksQualify+stats.SuccessCt)* 100.0).ToString("##0.0")}%"); } current = LevelConfiguration.SegPicker.PickSeg(newSegs, modifiedSegs, stats, true); //WL($"success, advanced to: {current?.Value}"); } return(stats); }
public void PossiblySaveDuringTweak(bool saveState, int saveEvery, TweakStats stats, List <(int, int)> neighbors = null, bool newLoop = false)