Profile


Name
Fuzes 3.0
ID
103206
Shared with
Public
Parent
Rav3n_pl Fuzes v1.5.5
Children
Created on
December 16, 2019 at 06:59 AM UTC
Updated on
December 16, 2019 at 06:59 AM UTC
Description

Updated version of Rav3n_pl Fuzes. Adds a recipe control of the wiggle power setting, and a new CI scaling factor. Internally, the core fuze logic is package as a module for easier reuse.

Best for


Code


--[[ Rav3n_pl Fuzes Thanks to Rav3n_pl for the original, and Timo van der Laan, bertro, KarenCH, Bruno Kestemont, Jean-Bob, and others for code and ideas. version 2.0 - 2017/06/13 - LociOiling -- rachet down band strength -- do away with recentbest -- racheting band strength -- streamline band toggling logic -- don't loop in cleanup, added CLEANUPENTRY check version 3.0 - 2019/11/13 - 2019/12/11 - LociOiling -- new tech showcase + adjust wiggle power in the main dialog + (example of dialog with error messages) + detect filters by filter.GetNames, bonus by filter.GetBonusTotal -- latest version of CPT (formerly chunquePoint) + report on individual filters when their score changes + report on density changes + thanks to bertro for setting the bonus/filter reporting format -- added CIFactor, scaling factor to clashing importance in Fuze + adapted from TvdL logic in EDRW/DRemixW -- updated seedRandom (thanks, KarenCH) -- band adjustment logic, used bertro's wording in dialog -- packaged core functions as FUZE + demonstrates using "methods" in Lua (as does CPT) ]]-- -- -- globals section -- Recipe = "Rav3n_pl Fuzes" Version = "3.0" ReVersion = Recipe .. " v." .. Version -- options VVVV daCapo = 2 --minimum gain from loop to run again HASFILTERS = false FILTERBONUS = 0 WigglePower = "" WPOut = "" PowerNames = { l = "low", m = "medium", h = "high", a = "auto", } WFDefaults = { l = 2, m = 4, h = 4, a = 3, } WPPrompt = "" HighPowerOK = false InitialCI = 1.0 UserBands = 0 -- initial band count MeanBandStr = 0 -- mean band strength BandReduce = 0 -- reduce strength of band BandReduceFact = 0 -- multiplier -- end of options ^^^^^^ segCnt = structure.GetCount () bestScore = 0 startChunque = nil -- initial CPT -- -- end of globals section -- -- -- seedRandom -- original by KarenCH -- -- looks for a seed > 10,000,000 and < 2 ^ 32 -- -- v2 - LociOiling - 20191103 -- * added 2 ^ 32 overflow check -- function seedRandom() local seed = os.time () / math.abs ( current.GetEnergyScore () ) seed = seed % 0.001 seed = 1 / seed while seed < 10000000 do seed = seed * 1000 end while seed > 2 ^ 32 do seed = seed / 10 end seed = seed - seed % 1 print ( "Random number seed = " .. seed ) math.randomseed( seed ) -- throw away a couple of randoms math.random () math.random () end CPT = { -- CPT--CPT--CPT--CPT--CPT--CPT--CPT--CPT--CPT--CPT--CPT--CPT--CPT--CPT--CPT-- --[[ CPT package version 0.6 For a given section of code, the CPT package tracks the time used and the gain produced. The package provides two routines, CPT:start and CPT:stop, to be used at the start and end of the code section. Calls may also be nested. A second call to begin in effect creates a new nested level. A remix recipe is a typical example of nesting chunque point calls. The recipe would call CPT:start somewhere near the start. Such a recipe might remix multiple lengths. The recipe would call CPT:start at the start of each length. Within a length, the recipe might remix multiple ranges of segments. For each range, the recipe would call CPT:start at the start, and CPT:stop at the end of the remix process. The CPT:stop would report the time and gain for that range of segments. When all ranges of segments are remixed, the length is complete, and the recipe would call CPT:stop. The CPT:stop would report the time and gain for the entire length. When all lengths are complete, the recipe would call CPT:stop one last time, reporting the time and gain for the entire recipe. Each CPT:start returns a "chunque", a table containing information such as the starting time and score. The chunque is used to link calls. For example, the chunque returned by a CPT:start call should be used on the corresponding CPT:stop call to report results. For nested calls, the chunque from one CPT:start call is passed to the nested CPT:start. In the remix example, to chunque from the initial CPT:start call is passed to the CPT:start calls for each length. The chunques from the length-level beginChunques are in turn passed to the beginChunques for each range of segments. For long-running recipes, the package also prints status reports at intervals of an hour or more. These reports show the total time elapsed and the gain since the start of the recipe. The first CPT:start call sets the internal "root chunque" used to calculate the reports. See this wiki page for an explanation of how this package is constructed: https://foldit.fandom.com/wiki/Lua_packaging_for_Foldit Each chunque contains the following fields: prefix - text identifying the chunque, output in messages score - score returned by the score function bonus - bonus of chunque, from score function runDate - date of chunque, from os.date runTime - time of chunque, from os.time version history --------------- 0.5 - LociOiling - 2019/11/03 - 2019/12/08 * rename to CPT (was chunquePoint) * align with new packaging strategy * add internal documentation * eliminate CSV output option * include bonus detection and tracking * convert chunques to named fields * remove runClock (os.clock), never used * eliminate pose parm, always use "current" * change external function names to "start" and "stop" * eliminate printChunque, add fmtGain for gain formatting * eliminate second line of start message * include gain or loss for each condition (filter) in status reports 0.6 - LociOiling - 2019/12/11 * fixed format of condition reporting in CPT:stop ]]-- segCnt = 0, -- segment count repInterval = 60 * 60, -- seconds between status reports lastStatusRep = 0, -- time of last status report rootChunque = nil, -- chunque to use as basis for status reports filters = false, -- whether filters active fnames = {}, -- filter names density = false, -- whether this is a density puzzle -- -- start - start tracking time, points, and bonus -- -- the start and stop functions are the external interface -- -- parameters: -- -- startChunque - if nil, set the internal rootChunque -- if not nil, nested call, report the gain -- prefix - text to identify the chunque -- scoreFunc - (optional) function to use for score checking -- -- calls self:newChunque to create a new chunque -- -- returns the new chunque -- start = function ( self, startChunque, prefix, scoreFunc ) -- -- initialize if startChunque omitted -- if startChunque == nil then self.segCnt = structure.GetCount () -- -- determine if filters apply -- local fnames = filter.GetNames () if #fnames > 0 then self.filters = true self.fnames = fnames end -- -- check a few segments to determine if this is an ED puzzle -- if current.GetSegmentEnergySubscore ( 1, "Density" ) ~= 0 or current.GetSegmentEnergySubscore ( self.segCnt / 2, "Density" ) ~= 0 or current.GetSegmentEnergySubscore ( self.segCnt, "Density" ) ~= 0 then self.density = true end end -- -- create the chunque -- local chunque = self:newChunque ( prefix, scoreFunc ) -- -- set the root chunque if needed -- if startChunque == nil then self.rootChunque = chunque self.lastStatusRep = os.time () -- last status message end local sscr = self:fmtScore ( chunque ) if startChunque == nil then -- no gain yet print ( chunque.prefix .. ", score: " .. sscr .. ", " .. chunque.runDate ) else -- report gain relative to start chunque local gstr, hours, pph = self:fmtGain ( startChunque, chunque ) print ( chunque.prefix .. ", score: " .. sscr .. ", total gain: " .. gstr .. ", " .. chunque.runDate ) end return chunque end, -- -- stop - stop tracking time, points, and bonus, then report result -- -- startChunque - the chunque returned by the corresponding CPT:start call -- prefix - text to identify the chunque -- scoreFunc - (optional) function to use for score checking -- stop = function ( self, startChunque, prefix, scoreFunc ) local chunque = self:newChunque ( prefix, scoreFunc ) self:chunqueGain ( startChunque, chunque ) -- -- if print overall progress at hourly interval -- local Clocktime = os.difftime ( os.time (), self.lastStatusRep ) if Clocktime > self.repInterval then self:chunqueReport ( self.rootChunque, chunque, "--- " ) -- bonus/density since start self.lastStatusRep = os.time () else -- report just bonus changes in this chunque self:prtExtra ( startChunque, chunque, prefix .. ", " ) end end, -- -- scorePlus - determine score and bonus from conditions/filters, -- used if user doesnt' provide a score function -- -- turn filters on, -- use current.GetEnergyScore to determine score, -- and filter.GetBonusTotal to determine bonus, -- restore previous status of filters -- scorePlus = function ( self ) local filts = behavior.GetFiltersDisabled () -- remember setting behavior.SetFiltersDisabled ( false ) -- always turn filters off local score = current.GetEnergyScore () local bonus = filter.GetBonusTotal () behavior.SetFiltersDisabled ( filts ) -- restore setting return score, bonus end, -- -- get filter scores -- getFilters = function ( self, fnames ) local filttab = {} for ii = 1, #fnames do filttab [ #filttab + 1 ] = { name = fnames [ ii ], score = filter.GetBonus ( fnames [ ii ] ), } end return filttab end, -- -- get total density scores across all segments -- getDensity = function ( self ) local density = 0 for ii = 1, self.segCnt do density = density + current.GetSegmentEnergySubscore ( ii, "Density" ) end return density end, -- -- newChunque - create a new chunque -- -- used internally by CPT:start and CPT:stop -- newChunque = function ( self, prefix, scoreFunc ) if prefix == nil then prefix = "" end if scoreFunc == nil then scoreFunc = self.scorePlus end local score = 0 local bonus = 0 score, bonus = scoreFunc () if score == nil then score = 0 end if bonus == nil then bonus = 0 end local runDate local runTime runDate = os.date () runTime = os.time () local ch = { prefix = prefix, score = score, bonus = bonus, runDate = runDate, runTime = runTime } if self.filters then ch.filters = self:getFilters ( self.fnames ) end if self.density then ch.density = self:getDensity () end return ch end, -- -- fmtScore - return formatted score, showing bonus if applicable -- fmtScore = function ( self, chunque ) local scout = self:round ( chunque.score ) if self.filters then scout = scout .. " (Bonus: " .. self:round ( chunque.bonus ) .. ")" end if self.density then scout = scout .. " (ED: " .. self:round ( chunque.density ) .. ")" end return scout end, -- -- fmtGain - return formatted gain, hours, and points per hour -- fmtGain = function ( self, startChunque, stopChunque ) local Clocktime = os.difftime ( stopChunque.runTime , startChunque.runTime ) local gain = self:round ( stopChunque.score - startChunque.score ) local bgain = self:round ( stopChunque.bonus - startChunque.bonus ) local hours = Clocktime / ( 60 * 60 ) if hours == 0 then hours = 0.0036 -- nothing happens in no time at all end local pph = gain / hours local gstr = gain if self.filters then local bgstr = "" if bgain > 0 then bgstr = "+" .. bgain elseif bgain == 0 then bgstr = "0" else bgstr = tostring ( bgain ) end gstr = gstr .. " (Bonus: " .. bgstr .. ")" end if self.density then local dgain = self:round ( stopChunque.density - startChunque.density ) local dgstr = "" if dgain > 0 then dgstr = "+" .. dgain elseif dgain == 0 then dgstr = "0" else dgstr = tostring ( dgain ) end gstr = gstr .. " (ED: " .. dgstr .. ")" end return gstr, hours, pph end, -- -- chunqueGain - print gain and time for a chunque -- -- used internally -- chunqueGain = function ( self, startChunque, stopChunque ) local sstr = self:fmtScore ( stopChunque ) local gstr, hours, pph = self:fmtGain ( startChunque, stopChunque ) print ( stopChunque.prefix .. ", score: " .. sstr .. ", gain: " .. gstr .. ", " .. stopChunque.runDate ) print ( stopChunque.prefix .. ", hours: " .. self:round ( hours ) .. ", points per hour: " .. self:round ( pph ) ) end, -- -- chunqueReport - issue periodic progress report -- -- used internally, similar to chunqueGain -- chunqueReport = function ( self, startChunque, stopChunque, prefix ) local gstr, hours, pph = self:fmtGain ( startChunque, stopChunque ) local statuz = "--- status: total hours = " .. self:round ( hours ) .. ", gain = " .. gstr .. ", points per hour = " .. self:round ( pph ) if self.lastPPH ~= nil then statuz = statuz .. ", change = " .. self:round ( pph - self.lastPPH ) .. " points per hour" end self.lastPPH = pph print ( statuz ) self:prtExtra ( startChunque, stopChunque, "--- " ) end, prtExtra = function ( self, startChunque, stopChunque, prefix ) if prefix == nil then prefix = "" end -- -- detailed report on filters -- if self.filters then local chg = 0 local condstr = "" for ii = 1, #stopChunque.filters do local delta = stopChunque.filters [ ii ].score - startChunque.filters [ ii ].score if delta ~= 0 then chg = chg + 1 condstr = condstr .. prefix .. "condition \'" .. stopChunque.filters [ ii ].name .. "\', bonus = " .. self:round ( stopChunque.filters [ ii ].score ) .. ", change = " .. self:round ( delta ) .. "\n" end end if chg > 0 then print ( condstr ) end end -- -- overall change in density already reported, nothing to add here -- end, round = function ( self, ii ) return ii - ii % 0.001 end, } -- CPT--CPT--CPT--CPT--CPT--CPT--CPT--CPT--CPT--CPT--CPT--CPT--CPT--CPT--CPT-- -- -- begin decentbest Beta package version 0.1 -- -- decentbest can be used to replace key recentbest functions, -- specifically recentbest.Save and recentbest.Restore -- -- recentbest has been unreliable in some cases, such as in design puzzles -- where the recentbest score may overlook certain filter penalties -- -- decentbest is not yet 100% compatible with recentbest, -- since it doesn't include the functions that recentbest best does, -- such as GetSegmentEnergyScore -- -- version 0.1 is packaged as a psuedo-class or psuedo-module -- containing a mix of data fields and functions -- -- all entries must be terminated with a comma to keep Lua happy -- -- the commas aren't necessary if only function definitions are present -- -- decentbest = { DECENTSLOT = 1, -- quicksave slot for decentbest decentscore = -999999, -- score of the saved pose decentbonus = -999999, -- filter bonus of the saved pose -- -- decentbest.Save () saves the current pose as the "decent best" -- it's intended to replace recentbest.Save, but the previous -- decent best is *unconditionally* replaced -- Save = function ( ) local filt = behavior.GetFiltersDisabled () save.Quicksave ( decentbest.DECENTSLOT ) behavior.SetFiltersDisabled ( false ) decentbest.decentscore = current.GetEnergyScore () behavior.SetFiltersDisabled ( true ) local nofiltscore = current.GetEnergyScore () decentbest.decentbonus = decentbest.decentscore - nofiltscore behavior.SetFiltersDisabled ( filt ) end, -- -- decentbest.Restore () the "decent best" if it's better than the current pose -- it's intended to replace recentbest.Restore -- Restore = function ( ) local filt = behavior.GetFiltersDisabled () behavior.SetFiltersDisabled ( false ) local tscore = current.GetEnergyScore () if tscore > decentbest.decentscore then decentbest.Save () else save.Quickload ( decentbest.DECENTSLOT ) end behavior.SetFiltersDisabled ( filt ) end, -- -- decentbest.GetScore () returns the score of the "decent best" pose -- GetScore = function ( ) return decentbest.decentscore end, -- -- decentbest.GetBonus () returns the filter bonus of the "decent best" pose -- GetBonus = function ( ) return decentbest.decentbonus end, } -- -- end decentbest Beta package version 0.1 -- FUZE = { -- FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE-- --[[ FUZE - classic Rav3n_pl Fuzes package See Rav3n_pl Fuzes 1.2 for base code - https://fold.it/portal/recipe/26572 + depends on decentbest package + Score function is compatible with CPT package, but CPT is not required V3.0 - LociOiling - 20191113 * wrap it up in nice little package * subtle changes to FuzeEnd, call it from both Fuze1 and Fuze2 ]]-- WF = 2, -- wiggle factor, similar to the value in TvdL's EDRW or DRemix CIFactor = 1.0, -- clashing importance scaling factor DISABLEFILTERS = false, -- if true, disable filters during shake, wiggle, etc. IRFF = 0.00, -- irreproducible random fuzz factor -- -- Score - return current score and any filter bonus -- the bonus return is for compatibility with the CPT package -- Score = function ( self ) local filt = behavior.GetFiltersDisabled () behavior.SetFiltersDisabled ( false ) local zscore = current.GetEnergyScore () local zbonus = filter.GetBonusTotal () if filt then behavior.SetFiltersDisabled ( true ) end return zscore, zbonus end, round = function ( self, x )--cut all afer 3-rd place return x - x % 0.001 end, Wiggle = function ( self, how ) if how == nil then how = "wa" end if self.DISABLEFILTERS then behavior.SetFiltersDisabled ( true ) end if how == "s" then structure.ShakeSidechainsAll ( 1 ) else if how == "wb" then structure.WiggleAll ( 2 * self.WF, true, false ) elseif how == "ws" then structure.WiggleAll ( 2 * self.WF, false, true ) elseif how == "wa" then structure.WiggleAll ( 2 * self.WF, true, true ) end end if self.DISABLEFILTERS then behavior.SetFiltersDisabled ( false ) end end, SaveBest = function ( self ) local zscore = self:Score () local gGain = zscore - bestScore if gGain > 0 then if gGain > 0.001 then print ( "Gained another " .. self:round ( gGain ) .. ", score = " .. self:round ( zscore ) ) end bestScore = zscore decentbest.Save () else decentbest.Restore () end end, doRFF = function ( self, iCI ) local oCI if self.IRFF > 0 and iCI < 1.0 then tRFF = math.random ( self.IRFF * 100 ) / 100 if math.random () > 0.5 then oCI = iCI + tRFF else oCI = iCI - tRFF end else oCI = iCI end oCI = oCI * self.CIFactor if oCI > 1 then oCI = 1 end if oCI < 0 then oCI = 0 end return oCI end, FuzeEnd = function ( self ) if behavior.GetClashImportance () < 1.0 then behavior.SetClashImportance ( 1 * self.CIFactor ) self:Wiggle () self:Wiggle ( "s" ) self:Wiggle () end decentbest.Restore () self:SaveBest () end, Fuze1 = function ( self, ci1, ci2 ) behavior.SetClashImportance ( self:doRFF ( ci1 ) ) self:Wiggle ( "s" ) behavior.SetClashImportance ( self:doRFF ( ci2 ) ) self:Wiggle () self:FuzeEnd ( ) end, Fuze2 = function ( self, ci1, ci2 ) behavior.SetClashImportance ( self:doRFF ( ci1 ) ) self:Wiggle () behavior.SetClashImportance ( 1 * self.CIFactor ) self:Wiggle () behavior.SetClashImportance ( self:doRFF ( ci2 ) ) self:Wiggle () self:FuzeEnd () end, } -- FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE--FUZE-- function MeanBandStrength () local jCnt = 0 local jTotStr = 0 local tBnd = band.GetCount () local meanStr = 0 --[[ if tBnd > 0 then print ( "total number of bands = " .. tBnd ) end ]]-- if FUZE.DISABLEFILTERS then behavior.SetFiltersDisabled ( true ) end for jj = 1, tBnd do local jStr = band.GetStrength ( jj ) if band.IsEnabled ( jj ) then jCnt = jCnt + 1 jTotStr = jTotStr + jStr end end if jCnt > 0 then meanStr = jTotStr / jCnt print ( "mean strength of " .. jCnt .. " enabled bands = " .. FUZE:round ( meanStr ) ) else meanStr = 0 end if FUZE.DISABLEFILTERS then behavior.SetFiltersDisabled ( false ) end return meanStr end function ReduceBandStrength ( reduceFact ) local jCnt = 0 local jChg = 0 local tBnd = band.GetCount () local tStr = 0 local tNewStr = 0 --print ( "total number of bands = " .. tBnd ) if FUZE.DISABLEFILTERS then behavior.SetFiltersDisabled ( true ) end for jj = 1, tBnd do local jStr = band.GetStrength ( jj ) if band.IsEnabled ( jj ) and jStr > 0 then tStr = tStr + jStr local newstr = jStr * ( 1 - reduceFact ) tNewStr = tNewStr + newstr band.SetStrength ( jj, newstr ) jCnt = jCnt + 1 jChg = jChg + ( jStr - newstr ) end end if FUZE.DISABLEFILTERS then behavior.SetFiltersDisabled ( false ) end if jCnt > 0 then print ( "mean strength change for " .. jCnt .. " enabled bands = " .. FUZE:round ( jChg / jCnt ) ) print ( "mean strength of enabled bands = " .. FUZE:round ( tNewStr / jCnt ) ) end return tNewStr / jCnt end function GetParams () local changed = false local uerror = "" local rc repeat local dlg = dialog.CreateDialog ( ReVersion ) dlg.WigglePower = dialog.AddTextbox ( "Wiggle Power", WPOut ) dlg.WPkey = dialog.AddLabel ( WPPrompt ) dlg.WF = dialog.AddSlider ( "Wiggle Factor", FUZE.WF, 1, 5, 0 ) dlg.CIFactor = dialog.AddSlider ( "CI factor", FUZE.CIFactor, 0.1, 1, 2 ) dlg.IRFF = dialog.AddSlider ( "CI fuzz factor" , FUZE.IRFF, 0.00, 0.50, 2 ) dlg.daCapo = dialog.AddSlider ( "minimum gain" , daCapo, 0.1, 25, 1 ) dlg.DISABLEFILTERS = dialog.AddCheckbox ( "Disable filters? (filter bonus = " .. FUZE:round ( FILTERBONUS ) .. ")", FUZE.DISABLEFILTERS ) if UserBands > 0 then dlg.Banded1 = dialog.AddLabel ( UserBands .. " bands found, enter strength reduction per cycle" ) dlg.Banded2 = dialog.AddLabel ( "0 = no reduction, 100 = bands disabled after first cycle" ) dlg.BandReduce = dialog.AddSlider ( "% each cycle", BandReduce, 0, 100, 0 ) end if uerror:len () > 0 then dlg.lerr1 = dialog.AddLabel ( "" ) dlg.lerr2 = dialog.AddLabel ( "ERROR: " .. uerror ) end dlg.ok = dialog.AddButton ( "OK", 1 ) dlg.cancel = dialog.AddButton ( "Cancel", -1 ) rc = dialog.Show ( dlg ) uerror = "" if rc > 0 then local WPtest = dlg.WigglePower.value:lower() WPtest = WPtest:sub ( 1, 1 ) WPtnam = PowerNames [ WPtest ] if WPtnam ~= nil then if WPtest ~= "h" or HighPowerOK then print ( "new wiggle power = " .. WPtnam ) WPout = WPtnam behavior.SetWigglePower ( WPtest ) else uerror = "high wiggle power not allowed on this puzzle" print ( "ERROR: " .. uerror ) end else uerror = "invalid wiggle power entered: \"" .. dlg.WigglePower.value .. "\"" print ( "ERROR: " .. uerror ) end FUZE.CIFactor = dlg.CIFactor.value daCapo = dlg.daCapo.value FUZE.WF = dlg.WF.value FUZE.DISABLEFILTERS = dlg.DISABLEFILTERS.value FUZE.IRFF = dlg.IRFF.value if UserBands > 0 then BandReduce = dlg.BandReduce.value BandReduceFact = BandReduce / 100 end end until rc < 0 or ( not changed and uerror:len () == 0 ) return rc > 0 end function main () Ident ( ReVersion ) -- -- initialize -- seedRandom () local fnames = filter.GetNames () if #fnames > 0 then HASFILTERS = true end if HASFILTERS then behavior.SetFiltersDisabled ( false ) -- make sure filters are on at this point FILTERBONUS = filter.GetBonusTotal () FUZE.DISABLEFILTERS = true end UserBands = band.GetCount () if UserBands > 0 then MeanBandStr = MeanBandStrength () if MeanBandStr > 0 then BandReduce = 50 end end WigglePower = behavior.GetWigglePower () WPOut = PowerNames [ WigglePower ] FUZE.WF = WFDefaults [ WigglePower ] HighPowerOK = behavior.HighPowerAllowed () if HighPowerOK then WPPrompt = "wiggle power: l = low, m = medium, h = high, a = auto" else WPPrompt = "wiggle power: l = low, m = medium, a = auto" end InitialCI = behavior.GetClashImportance () FUZE.CIFactor = InitialCI if GetParams () then -- -- print recipe parms -- print ( "options:" ) print ( "Wiggle Power = " .. WPout ) print ( "Wiggle Factor = " .. FUZE.WF ) print ( "CI scaling factor = " .. FUZE.CIFactor ) behavior.SetClashImportance ( FUZE.CIFactor ) if FUZE.IRFF > 0 then print ( "max CI fuzz factor = " .. FUZE.IRFF ) end print ( "minimum gain per cycle = " .. daCapo ) if UserBands then print ( "Bands found = " .. UserBands ) print ( "Band strength reduction = " .. BandReduce .. "% per cycle" ) end print ( "disable filters = " .. tostring ( FUZE.DISABLEFILTERS ) ) print ( "--" ) startChunque = CPT:start ( nil, "Start", FUZE.Score ) bestScore = FUZE:Score () decentbest.Save () selection.SelectAll () local fuzeCount = 1 local retryFuze = false repeat retryFuze = false local ss = FUZE:Score () local runName = "Fuze run " .. fuzeCount local begChunque = CPT:start ( startChunque, runName, FUZE.Score ) -- -- pink fuse -- print ( "Run " .. fuzeCount .. ", Fuze 1/6" ) FUZE:Fuze1 ( 0.3, 0.6 ) print ( "Run " .. fuzeCount .. ", Fuze 2/6" ) FUZE:Fuze2 ( 0.5, 0.7 ) print ( "Run " .. fuzeCount .. ", Fuze 3/6" ) FUZE:Fuze2 ( 0.7, 0.5 ) -- -- blue fuse -- print ( "Run " .. fuzeCount .. ", Fuze 4/6" ) FUZE:Fuze1 ( 0.05, 1 ) print ( "Run " .. fuzeCount .. ", Fuze 5/6" ) FUZE:Fuze1 ( 0.07, 1 ) print ( "Run " .. fuzeCount .. ", Fuze 6/6" ) FUZE:Fuze2 ( 0.3, 1 ) local runGain = FUZE:Score () - ss CPT:stop ( begChunque, "end run " .. fuzeCount, FUZE.Score ) fuzeCount = fuzeCount + 1 if runGain < daCapo then if band.GetCount () > 0 and MeanBandStr >= 0.1 then MeanBandStr = ReduceBandStrength ( BandReduceFact ) retryFuze = true decentbest.Save () end end until runGain < daCapo and not retryFuze cleanup () -- always clean up after the party is over end end -- -- Ident - print identifying information at beginning and end of recipe -- -- slugline - first line to print - normally recipe name and version -- -- v0.2 - LociOiling - 20191118 -- + streamline the format -- + user.GetGroupName not working, remove -- function Ident ( slugline ) local function round ( ii ) return ii - ii % 0.001 end print ( slugline ) print ( "Puzzle: " .. puzzle.GetName () .. " (" .. puzzle.GetPuzzleID () .. ")" ) print ( "Track: " .. ui.GetTrackName () ) --[[ user.GetGroupName not working local gname = user.GetGroupName () if gname ~= nil then gname = " (" .. gname .. ")" else gname = "" end print ( "User: " .. user.GetPlayerName () .. gname ) ]]-- local luser = user.GetPlayerName () local scoretype = scoreboard.GetScoreType () local scort = "" if scoretype == 0 then scort = "soloist" elseif scoretype == 1 then scort = "evolver" elseif scoretype == 2 then scort = "all hands" elseif scoretype == 3 then scort = "no score" else scort = "unknown/error" end print ( "User: " .. luser ) print ( "Rank: " .. scoreboard.GetRank ( scoretype ) .. " (" .. scort .. ")" ) local sGroup = scoreboard.GetGroupScore () if sGroup ~= nil then print ( "Group rank / score: " .. scoreboard.GetGroupRank () .. " / " .. round ( 10 * ( 800 - sGroup ) ) ) end end function cleanup ( error ) if CLEANUPENTRY ~= nil then return end CLEANUPENTRY = true print ( "---" ) local reason local start, stop, line, msg if error == nil then reason = "complete" else -- -- civilized error reporting, thanks to Bruno K. and Jean-Bob -- start, stop, line, msg = error:find ( ":(%d+):%s()" ) if msg ~= nil then error = error:sub ( msg, #error ) end if error:find ( "Cancelled" ) ~= nil then reason = "cancelled" else reason = "error" end end Ident ( ReVersion .. " " .. reason ) if reason == "error" then print ( "Unexpected error detected" ) print ( "Error line: " .. line ) print ( "Error: \"" .. err .. "\"" ) end behavior.SetClashImportance ( InitialCI ) selection.DeselectAll () if error ~= nil then decentbest.Restore () end behavior.SetFiltersDisabled ( false ) -- make sure filters are on at end if startChunque ~= nil then CPT:stop ( startChunque, "Final", FUZE.Score ) end end -- main call xpcall ( main, cleanup ) --end of script

Comments


LociOiling Lv 1

It's been nearly five years since the last update, but this is the latest version of Rav3n_pl Fuzes. Although Rav3n_pl's name is gone from the recipe title just for clarity, it's still his recipe at heart.

This version has some new features, and has been restructured a bit internally.

The recipe now lets you control the wiggle power setting, letting you enter l, m, a, or h (if available) for low, medium, auto, or high wiggle power. The recipe only checks the first letter, so a or au or aux will all get you auto. (Internally, the code demonstrates a way to do error messages for bad input.)

The wiggle factor setting continues as before, and is similar to wiggle factor in Tvdl Enhanced DRW or TvdL DRemixW. The initial wiggle factor is now set based on the initial wiggle power. (Thank bertro for that.)

A new CI factor setting is added, and takes it initial value from the current CI. This setting is similar to what happens if CI is not 1.0 at the start of EDRW or DRemixW. The CI factor is used to scale whatever CI the recipe uses. So if you set CI factor 0.91, and the recipe calls for CI 0.5, the effective CI is 0.91 * 0.5 = 0.455.

The CI fuzz factor is now just a single slider, versus a checkbox and a slider. As before the fuzz factor adds or subtracts a small random value from the CI. The CI factor is applied on top of the fuzz factor. (Actually the fuzz factor is not a factor at all, since it doesn't involve multiplication, just addition.)

The minimum gain and disable filters settings are as before.

Internally, the recipe has been restructured, with the core fuze functions moved into a table called FUZ. In theory, this makes it easier to reuse this code in another recipe. (There is a cross-dependency on the "decentbest" package, sorry about that, hope to eliminate that some day.)

The score tracking and timing logic has also been repackaged into a table called CPT. In addition to the overall score, it now tracks and reports changes to bonus from filters, and changes in the density score on ED puzzles. Thanks to bertro for leading the way on this front.

Both FUZ and CPT demonstrate methods in Lua. Their methods are called using the colon operator, for example CPT:start and CPT:stop for timing a section of code.

There are some hopefully minor changes to the fuze logic (aside from the CI scaling factor). (The FuzeEnd method is now invoked by both Fuze1 and Fuze2.)

There are several other minor changes, such as new random number seeding logic, and an Ident function to log some information about the puzzle and the recipe.