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)
version 3.0.1 - 2020/01/08 - LociOiling
-- fix typoed variable, scoping error
-- move cleanup call to very end of main
version 3.0.2 - 2020/02/20 - LociOiling
-- fix condition checking for Move Count Limit and
similar "binary" conditions
-- add "throwback" feature to reject small gains
for sketchbook puzzles
version 3.0.3 - 2020/02/20 - LociOiling
-- update cleanup routine to fix error with "error", as spotted by robgee
(don't create a variable called "error", there's Lua built-in by that name...)
]]--
--
-- globals section
--
Recipe = "Rav3n_pl Fuzes"
Version = "3.0.2"
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.7
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
0.7 - LociOiling - 2020/02/18
* handle "MoveCountLimit" and similar binary conditions/filters
]]--
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
local hasbonus = filter.HasBonus ( fnames [ ii ] )
local fscore = nil
if hasbonus then
score = filter.GetBonus ( fnames [ ii ] )
else
score = nil
end
local satisfied = filter.ConditionSatisfied ( fnames [ ii ] )
filttab [ #filttab + 1 ] =
{ name = fnames [ ii ], hasbonus = hasbonus, score = score, satisfied = satisfied, }
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
if stopChunque.filters [ ii ].hasbonus then
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
else
if stopChunque.filters [ ii ].satisfied ~= startChunque.filters [ ii ].satisfied then
chg = chg + 1
condstr = condstr .. prefix ..
"condition \'"
.. stopChunque.filters [ ii ].name ..
"\', satisfied = "
.. tostring ( stopChunque.filters [ ii ].satisfied ) ..
", previously satisfied = "
.. tostring ( startChunque.filters [ ii ].satisfied ) ..
"\n"
end
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
THROWBACK = 99, -- slot for throwback
mingain = 0, -- minimum gain for throwback
--
-- 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 > self.mingain then
if gGain > 0.001 then
print ( "Gained another " .. self:round ( gGain ) .. ", score = " .. self:round ( zscore ) )
end
bestScore = zscore
decentbest.Save ()
save.Quicksave ( self.THROWBACK )
else
decentbest.Restore ()
save.Quickload ( self.THROWBACK )
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.mingain = dialog.AddSlider ( "min gain to keep" , FUZE.mingain, 0, 100, 1 )
dlg.daCapo = dialog.AddSlider ( "min gain to repeat" , 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 )
local 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
FUZE.mingain = dlg.mingain.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
local filterz = {}
HASFILTERS = true
for ii = 1, #fnames do
local fn = fnames [ ii ]
local sat = filter.ConditionSatisfied ( fn )
local bon = filter.HasBonus ( fn )
local val = 0
if bon then
val = filter.GetBonus ( fn )
end
filterz [ #filterz + 1 ] = { name = fn, satisfied = sat, hasbonus = bon, value = val }
end
print ( "filters/conditions detected" )
for ii = 1, #filterz do
print ( "filter/condition \""
.. filterz [ ii ].name ..
"\", satisfied = "
.. tostring ( filterz [ ii ].satisfied ) ..
", has bonus = "
.. tostring ( filterz [ ii ].hasbonus ) ..
", bonus value = "
.. FUZE:round ( filterz [ ii ].value )
)
end
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 to keep (sketchbook) = " .. FUZE.mingain )
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 ( "--" )
save.Quicksave ( FUZE.THROWBACK )
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 runGain > FUZE.mingain then
save.Quicksave ( FUZE.THROWBACK )
else
save.Quickload ( FUZE.THROWBACK )
end
if band.GetCount () > 0
and MeanBandStr >= 0.1 then
MeanBandStr = ReduceBandStrength ( BandReduceFact )
retryFuze = true
decentbest.Save ()
end
end
until runGain < daCapo and not retryFuze
end
cleanup () -- always clean up after the party is over
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 ( errmsg )
if CLEANUPENTRY ~= nil then
return
end
CLEANUPENTRY = true
print ( "---" )
local reason
local start, stop, line, msg
if errmsg == nil then
reason = "complete"
else
--
-- model 120 - civilized error reporting,
-- thanks to Bruno K. and Jean-Bob
--
start, stop, line, msg = errmsg:find ( ":(%d+):%s()" )
if msg ~= nil then
errmsg = errmsg:sub ( msg, #errmsg )
end
if errmsg:find ( "Cancelled" ) ~= nil then
reason = "cancelled"
else
reason = "error"
end
end
--
-- model 100 - print recipe name, puzzle, track, time, score, and gain
--
Ident ( ReVersion .. " " .. reason )
if reason == "error" then
print ( "Unexpected error detected" )
print ( "Error line: " .. line )
print ( "Error: \"" .. errmsg .. "\"" )
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