<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
    <channel>
        <title>Project-GC Forum - Script development</title>
        <description>Discuss your script development with others (bugs, optimizations, methods ...)</description>
        <link>/forum/list.php?4</link>
        <lastBuildDate>Sun, 12 Jul 2026 06:26:13 +0000</lastBuildDate>
        <generator>Phorum 5.2.23</generator>
        <item>
            <guid>/forum/read.php?4,122229,123585#msg-123585</guid>
            <title>Re: GetBadgeGenDataV4() changed?</title>
            <link>/forum/read.php?4,122229,123585#msg-123585</link>
            <description><![CDATA[ Just responding to say, I filed a support ticket, it was addressed on the backend, and everything&#039;s good now. Thanks!]]></description>
            <dc:creator>bmuzzin</dc:creator>
            <category>Script development</category>
            <pubDate>Thu, 21 May 2026 14:36:41 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,122229,123072#msg-123072</guid>
            <title>Re: GetBadgeGenDataV4() changed?</title>
            <link>/forum/read.php?4,122229,123072#msg-123072</link>
            <description><![CDATA[ Apparently.......... I need to pass in a second parameter, otherwise I get this unhelpful error. Unfortunately, now I get:<br />
<br />
<pre class="bbcode">
Error 2, LuaSandbox.php:188  LuaSandboxFunction::call(): Unable to convert object of type Pgc\Domain\ProfileStats\Subdomain\BadgeGen\V4\AchievementBadgeResult</pre>
<br />
... which happens after the script executes (eg. after the return). If only I knew what was at LuaSandbox.php:188.]]></description>
            <dc:creator>bmuzzin</dc:creator>
            <category>Script development</category>
            <pubDate>Tue, 12 May 2026 01:18:54 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,122229,122229#msg-122229</guid>
            <title>GetBadgeGenDataV4() changed?</title>
            <link>/forum/read.php?4,122229,122229#msg-122229</link>
            <description><![CDATA[ Has there been a change to GetBadgeGenDataV4?<br />
Another user contacted me, and said this script of mine no longer functions. I haven&#039;t changed in it a long time. It looks like the function is now returning a number instead of an array.<br />
<br />
<a href="https://project-gc.com/Challenges/87689"  rel="nofollow">https://project-gc.com/Challenges/87689</a><br />
[string &quot;&quot;]:1396: attempt to index local &#039;badgegenV4&#039; (a number value)]]></description>
            <dc:creator>bmuzzin</dc:creator>
            <category>Script development</category>
            <pubDate>Wed, 29 Apr 2026 22:01:55 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,117817,118361#msg-118361</guid>
            <title>Re: GetFinds, favorite_points_pct field</title>
            <link>/forum/read.php?4,117817,118361#msg-118361</link>
            <description><![CDATA[ Huh, yes this is my misunderstanding... I actually didn&#039;t realize that basic users can&#039;t award FPs. Guess it makes sense, although, kind of moot since I assume FP % challenges wouldn&#039;t be allowed anymore anyhow.]]></description>
            <dc:creator>bmuzzin</dc:creator>
            <category>Script development</category>
            <pubDate>Mon, 16 Feb 2026 21:39:21 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,117817,117955#msg-117955</guid>
            <title>Re: GetFinds, favorite_points_pct field</title>
            <link>/forum/read.php?4,117817,117955#msg-117955</link>
            <description><![CDATA[ Sounds like you&#039;ve missunderstood how FP% are calculated.<br />
<br />
FP% are number of FP divided by number of finders with premium and number of basic members that gave an FP (aka expired premium). That wouldn&#039;t be easily calculated by a script as the script lacks some of the data.]]></description>
            <dc:creator>Pleu</dc:creator>
            <category>Script development</category>
            <pubDate>Sun, 08 Feb 2026 17:01:36 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,117817,117817#msg-117817</guid>
            <title>GetFinds, favorite_points_pct field</title>
            <link>/forum/read.php?4,117817,117817#msg-117817</link>
            <description><![CDATA[ Take these two checkers:<br />
<a href="https://project-gc.com/Challenges//111480"  rel="nofollow">https://project-gc.com/Challenges//111480</a><br />
<a href="https://project-gc.com/Challenges/GC536C2/111479"  rel="nofollow">https://project-gc.com/Challenges/GC536C2/111479</a><br />
<br />
In theory, they should do the same thing, with the one using favorite_points_pct directly, and the other calculating favorite_points_pct  = floor(favorite_points / num_finds * 100). However, the favorite_points_pct field is considerably higher. For example, running myself on the calculated version, the first entry is:<br />
GC735VZ - 198/352 = 56%<br />
<br />
That similar entry using the field is:<br />
GC735VZ 198/352 = 64.2857%<br />
<br />
Is the favorite_points_pct field not just supposed to be favs/finds? Why is this field in the DB, since it can just easily be calculated?]]></description>
            <dc:creator>bmuzzin</dc:creator>
            <category>Script development</category>
            <pubDate>Fri, 06 Feb 2026 03:02:04 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,116206,116206#msg-116206</guid>
            <title>Incorrect error message</title>
            <link>/forum/read.php?4,116206,116206#msg-116206</link>
            <description><![CDATA[ I assume the release went out that I can now see disabled tags on my own scripts.... and there&#039;s a very minor issue.<br />
<br />
If I click on one of those, I get an error screen saying the script is disabled.... which isn&#039;t technically correct - it&#039;s really that the *tag* is disabled, but the script itself is enabled (well, potentially anyhow). Not sure what the negative effects of allowing a script owner to run disabled tags on their scripts would be - in fact, it might be useful if somebody tags your script and can&#039;t get it working, they could ask for assistance without making the tag public.]]></description>
            <dc:creator>bmuzzin</dc:creator>
            <category>Script development</category>
            <pubDate>Mon, 05 Jan 2026 16:49:05 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,115980,116020#msg-116020</guid>
            <title>Re: Chugach and Copper River Census Areas</title>
            <link>/forum/read.php?4,115980,116020#msg-116020</link>
            <description><![CDATA[ The script I am modifying reads in a blob of data via PGC.GetJsonData(&quot;USA-ContiguousCounties-MaxB&quot;) that I originally thought contained the map IDs that I mentioned.  However, I subsequently realized that those IDs are being pulled in via a call to PGC.GetCountyPolygonInfoFromCountry, which solved my problem.<br />
<br />
Thanks for the quick reply.]]></description>
            <dc:creator>lesdubois</dc:creator>
            <category>Script development</category>
            <pubDate>Thu, 01 Jan 2026 19:27:44 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,115980,116019#msg-116019</guid>
            <title>Re: Chugach and Copper River Census Areas</title>
            <link>/forum/read.php?4,115980,116019#msg-116019</link>
            <description><![CDATA[ Using the id of the region sounds odd and potentially like you&#039;re using uploaded dataset (and thus trying to use something that doesn&#039;t exists), can you link the checker you&#039;re modifying so I can see what it is you&#039;re trying to do?]]></description>
            <dc:creator>Pleu</dc:creator>
            <category>Script development</category>
            <pubDate>Thu, 01 Jan 2026 18:17:00 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,115980,115980#msg-115980</guid>
            <title>Chugach and Copper River Census Areas</title>
            <link>/forum/read.php?4,115980,115980#msg-115980</link>
            <description><![CDATA[ I&#039;m working on updating a challenge checker, and I&#039;m running into a problem with the Chugach Census Area and the Copper River Census Area in Alaska.  Both are recently created and serve as &quot;county equivalents&quot; in Alaska.<br />
<br />
I want to display their outlines on the map produced by the checker, but I can&#039;t find the ID numbers for those areas that are expected by the map API.  The checker I am updating contains the IDs for all the other boroughs and census areas in Alaska.<br />
<br />
So if anyone could fill me in or point me in the right direction I&#039;d appreciate it!]]></description>
            <dc:creator>lesdubois</dc:creator>
            <category>Script development</category>
            <pubDate>Thu, 01 Jan 2026 02:06:43 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,111590,112080#msg-112080</guid>
            <title>Re: Can&#039;t delete a script</title>
            <link>/forum/read.php?4,111590,112080#msg-112080</link>
            <description><![CDATA[ There hasn&#039;t been a new release of the site yet. There is another thing kind of blocking it.<br />
<br />
If it was a smaller patch I would have squeezed it in regardless. But this time I have had to hold it back a bit.]]></description>
            <dc:creator>magma1447</dc:creator>
            <category>Script development</category>
            <pubDate>Tue, 14 Oct 2025 09:55:27 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,111590,112072#msg-112072</guid>
            <title>Re: Can&#039;t delete a script</title>
            <link>/forum/read.php?4,111590,112072#msg-112072</link>
            <description><![CDATA[ I mean, this really *isn&#039;t* that big of a deal........<br />
<br />
Now it tells me there&#039;s one tag, but I can&#039;t see who has it, or delete it, so I can delete the script.]]></description>
            <dc:creator>bmuzzin</dc:creator>
            <category>Script development</category>
            <pubDate>Tue, 14 Oct 2025 02:07:14 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,111590,111757#msg-111757</guid>
            <title>Re: Can&#039;t delete a script</title>
            <link>/forum/read.php?4,111590,111757#msg-111757</link>
            <description><![CDATA[ A patch that fixes this has been pushed to git. It&#039;s not released yet. Planning a release sometime this week.]]></description>
            <dc:creator>magma1447</dc:creator>
            <category>Script development</category>
            <pubDate>Mon, 06 Oct 2025 21:45:26 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,111590,111756#msg-111756</guid>
            <title>Re: Can&#039;t delete a script</title>
            <link>/forum/read.php?4,111590,111756#msg-111756</link>
            <description><![CDATA[ The script has a disabled tag, owned by someone else. From what I can see in the code you can&#039;t see such tags. It might be worth add the disabled ones as well though.]]></description>
            <dc:creator>magma1447</dc:creator>
            <category>Script development</category>
            <pubDate>Mon, 06 Oct 2025 21:36:30 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,111590,111755#msg-111755</guid>
            <title>Re: Can&#039;t delete a script</title>
            <link>/forum/read.php?4,111590,111755#msg-111755</link>
            <description><![CDATA[ Nope, refreshing doesn&#039;t work, there aren&#039;t any tags at all showing, and remove script is disabled with the tooltip saying that you can&#039;t delete a script with tags.<br />
<br />
<a href="https://imgur.com/a/8I6JRDc"  rel="nofollow">https://imgur.com/a/8I6JRDc</a>]]></description>
            <dc:creator>bmuzzin</dc:creator>
            <category>Script development</category>
            <pubDate>Mon, 06 Oct 2025 21:08:08 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,111590,111663#msg-111663</guid>
            <title>Re: Can&#039;t delete a script</title>
            <link>/forum/read.php?4,111590,111663#msg-111663</link>
            <description><![CDATA[ Try refreshing the page? The page just isn&#039;t smart enough to see that you removed the last tag.<br />
<br />
That or there&#039;s a disabled tag on it, but as script owner, you should be able to see those.]]></description>
            <dc:creator>Hügh</dc:creator>
            <category>Script development</category>
            <pubDate>Sun, 05 Oct 2025 02:52:33 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,111590,111590#msg-111590</guid>
            <title>Can&#039;t delete a script</title>
            <link>/forum/read.php?4,111590,111590#msg-111590</link>
            <description><![CDATA[ I removed the only tag on this script, and want to remove it (it can be done by my uber script now)... but it won&#039;t let me:<br />
<a href="https://project-gc.com/Tools/Challenges?edit&amp;scriptId=11922"  rel="nofollow">https://project-gc.com/Tools/Challenges?edit&amp;scriptId=11922</a><br />
<br />
It says can&#039;t remove a script with tags... but it shows no tags.]]></description>
            <dc:creator>bmuzzin</dc:creator>
            <category>Script development</category>
            <pubDate>Fri, 03 Oct 2025 15:36:45 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,105418,105420#msg-105420</guid>
            <title>Re: Documenting scripts with AI</title>
            <link>/forum/read.php?4,105418,105420#msg-105420</link>
            <description><![CDATA[ New version:<br />
<pre class="bbcode">
--[[
Geocaching Birthday Geocache Challenge Checker

This script validates whether a geocacher has completed a &quot;birthday geocache&quot; challenge.
A birthday geocache is one where the geocache was found on the same month/day it was hidden,
but in a different year (hence the &quot;age&quot; calculation).

The checker supports:
- Finding a minimum number of birthday geocaches
- Achieving a minimum total &quot;age&quot; (sum of all geocache ages)
- Finding geocaches with a minimum number of different ages
- Optionally requiring unique dates (only one geocache per birth date)

Example: A geocache hidden on 2010-05-15 and found on 2015-05-15 has an &quot;age&quot; of 5 years.
]]

--[[
Changelog:
2025-06-19: Documented scripts with several comments.
2025-06-19: Rewrote script into a two pass procedure. This was done to make sure that the
            &quot;You have only found X of the Y...&quot; actually matched the number of items in the list.
2025-06-19: Return `html = false` when not used, not an empty string.
]]

-- Get input arguments and configuration
local args = {...}
conf = args[1].config

-- Initialize filter object for geocache search
local filter = {}
filter.country = conf.country
filter.region = conf.region
filter.county = conf.county
filter.minVisitDate = conf.minVisitDate
filter.maxVisitDate = conf.maxVisitDate
filter.minHiddenDate = conf.minHiddenDate
filter.maxHiddenDate = conf.maxHiddenDate
filter.types = conf.types
filter.sizes = conf.sizes
filter.difficulties = conf.difficulties
filter.terrains = conf.terrains

-- Parse configuration parameters with defaults

-- Minimum number of birthday geocaches required (default: 1)
local mincaches = 1
if (not (conf.mincaches == nil)) and tonumber(conf.mincaches) &gt; 0 then
    mincaches = tonumber(conf.mincaches)
end
PGC.print(&quot;mincaches: &quot;, mincaches, &quot;\n&quot;)

-- Minimum age of geocaches (years between hidden and found dates, default: 1)
local minage = 1
if (not (conf.minage == nil)) and tonumber(conf.minage) &gt;= 0 then
    minage = tonumber(conf.minage)
end
PGC.print(&quot;minage: &quot;, minage, &quot;\n&quot;)

-- Maximum age of geocaches (default: 999 years)
local maxage = 999
if (not (conf.maxage == nil)) and tonumber(conf.maxage) &gt;= 0 then
    maxage = tonumber(conf.maxage)
end
PGC.print(&quot;maxage: &quot;, maxage, &quot;\n&quot;)

-- Minimum total age (sum of all geocache ages, default: 1)
local mintotal = 1
if (not (conf.mintotal == nil)) and tonumber(conf.mintotal) &gt; 0 then
    mintotal = tonumber(conf.mintotal)
end
PGC.print(&quot;mintotal: &quot;, mintotal, &quot;\n&quot;)

-- Minimum number of different ages required (default: 1)
local mindifferentages = 1
if (conf.mindifferentages ~= nil) and tonumber(conf.mindifferentages) &gt; 0 then
    mindifferentages = tonumber(conf.mindifferentages)
end
PGC.print(&quot;mindifferentages: &quot;, mindifferentages, &quot;\n&quot;)

-- Whether to enforce unique dates (only one geocache per birth date)
uniquedates = false
if conf.uniquedates ~= nil and conf.uniquedates == &quot;YES&quot; then
    uniquedates = true
end
PGC.print(&quot;uniquedates: &quot;, uniquedates, &quot;\n&quot;)

-- Whether to show all qualifying geocaches or stop when requirements are met
local showall = false
if (not (conf.showall == nil)) and conf.showall == &quot;YES&quot; then
    showall = true
end
PGC.print(&quot;showall: &quot;, showall, &quot;\n&quot;)

-- Get profile information
profileName = args[1][&#039;profileName&#039;]
profileId = PGC.ProfileName2Id(profileName)

-- Retrieve user&#039;s geocache finds from backend
myFinds = PGC.GetFinds(profileId, {
    fields = { &#039;gccode&#039;, &#039;cache_name&#039;, &#039;hidden&#039;, &#039;visitdate&#039; },
    filter = filter,
    order = &#039;OLDESTFIRST&#039;
})

-- PASS 1: Determine best geocache for each birth date
local bestPerBirthdate = {}

for n, v in ipairs(myFinds) do
    -- Extract birth date (month-day) from hidden date
    local birthdate = string.sub(v.hidden, 6)

    -- Check if geocache was found on its &quot;birthday&quot; (same month-day)
    if birthdate == string.sub(v.visitdate, 6) then
        -- Calculate geocache age (years between hidden and found)
        local age = tonumber(string.sub(v.visitdate, 1, 4)) - tonumber(string.sub(v.hidden, 1, 4))

        -- Check if age is within acceptable range
        if age &gt;= minage and age &lt;= maxage then
            -- If uniquedates is enabled, only keep the oldest geocache for each birth date
            if uniquedates then
                if bestPerBirthdate[birthdate] == nil or age &gt; bestPerBirthdate[birthdate].age then
                    PGC.print(&quot;Adding &quot; .. v.gccode .. &quot; for birthdate &quot; .. birthdate .. &quot;, replacing potential duplicates&quot;, &quot;\n&quot;)
                    bestPerBirthdate[birthdate] = {
                        geocache = v,
                        age = age
                    }
                end
            else
                -- If uniquedates is disabled, keep all qualifying geocaches
                if bestPerBirthdate[birthdate] == nil then
                    bestPerBirthdate[birthdate] = {}
                end
                PGC.print(&quot;Adding &quot; .. v.gccode .. &quot; for birthdate &quot; .. birthdate, &quot;\n&quot;)
                table.insert(bestPerBirthdate[birthdate], {
                    geocache = v,
                    age = age
                })
            end
        end
    end
end

--PGC.print(&quot;Pass 1 complete - bestPerBirthdate:&quot;, bestPerBirthdate)

-- PASS 2: Build results from the clean data
local qualified = {}
local cachesfound = 0
local totalage = 0
local differentages = 0
local countsperage = {}
local resultText = &quot;&quot;

-- Initialize age counters
if #myFinds &gt; 0 then
    local lastyear = tonumber(string.sub(myFinds[#myFinds].visitdate, 1, 4));
    PGC.print(&quot;Initializing countsperage from age 1 to &quot; .. (lastyear - 2000), &quot;\n&quot;)
    for age = 1, lastyear - 2000 do
        countsperage[age] = 0
    end
end

-- Sort birth dates for consistent output (oldest hidden dates first)
local sortedEntries = {}
for birthdate, entry in pairs(bestPerBirthdate) do
    if uniquedates then
        table.insert(sortedEntries, {birthdate = birthdate, entry = entry})
    else
        for _, subentry in ipairs(entry) do
            table.insert(sortedEntries, {birthdate = birthdate, entry = subentry})
        end
    end
end

-- Sort by hidden date (oldest first)
table.sort(sortedEntries, function(a, b)
    return a.entry.geocache.hidden &lt; b.entry.geocache.hidden
end)

-- Process each entry in sorted order
for _, sortedEntry in ipairs(sortedEntries) do
    local birthdate = sortedEntry.birthdate
    local entry = sortedEntry.entry
    local v = entry.geocache
    local age = entry.age

    table.insert(qualified, v)
    cachesfound = cachesfound + 1
    totalage = totalage + age

    if countsperage[age] == 0 then
        differentages = differentages + 1
    end
    countsperage[age] = countsperage[age] + 1

    -- Add to result text
    if showall or (mincaches &gt; mindifferentages) or (countsperage[age] == 1) or (mintotal &gt; mindifferentages) then
        resultText = resultText .. birthdate .. &quot; [&quot; .. v.gccode .. &quot;](http://coord.info/&quot; .. v.gccode .. &quot;) &quot; ..
                   v.cache_name .. &quot;, hidden &quot; .. string.sub(v.hidden, 1, 4) ..
                   &quot; found &quot; .. string.sub(v.visitdate, 1, 4) .. &quot; (age &quot; .. age .. &quot;)\n&quot;
    end

    -- Check if all requirements are now met
    local meets = (totalage &gt;= mintotal) and (cachesfound &gt;= mincaches) and (differentages &gt;= mindifferentages)

    -- Stop processing if requirements are met and not showing all
    if meets and not showall then
        break
    end
end

-- Check final requirements
local meets = (totalage &gt;= mintotal) and (cachesfound &gt;= mincaches) and (differentages &gt;= mindifferentages)

-- Generate failure message if requirements not met
local htmlBlob = false
if not meets then
    htmlBlob = &quot;&quot;
    if cachesfound == 0 then
        htmlBlob = &quot;You have not found any qualifying geocache.&quot;
    elseif cachesfound &lt; mincaches then
        htmlBlob = &quot;You have only found &quot; .. cachesfound .. &quot; of the &quot; .. mincaches .. &quot; geocaches required.&quot;
    elseif differentages &lt; mindifferentages then
        htmlBlob = &quot;You have only found geocaches with &quot; .. differentages .. &quot; different ages (&quot;
        local printcomma = false
        for age, count in ipairs(countsperage) do
            if count &gt; 0 then
                if printcomma then
                    htmlBlob = htmlBlob .. &quot;,&quot;
                end
                htmlBlob = htmlBlob .. age
                printcomma = true
            end
        end
        htmlBlob = htmlBlob .. &quot;), but you need &quot; .. mindifferentages .. &quot;.&quot;
    elseif totalage &lt; mintotal then
        htmlBlob = &quot;The total age of the birthday geocaches you found is only &quot; .. totalage ..
                  &quot;, but you need at least &quot; .. mintotal .. &quot;.&quot;
    else
        htmlBlob = &quot;&quot;
    end

    -- Add table of qualifying geocaches even if requirements not fully met
    if #qualified &gt; 0 then
        htmlBlob = htmlBlob .. &quot;\n&lt;TABLE&gt;&lt;TR&gt;&lt;TH&gt;GCcode&lt;/TH&gt;&lt;TH&gt;Hidden&lt;/TH&gt;&lt;TH&gt;Found&lt;/TH&gt;&lt;/TR&gt;\n&quot;
        local loga = {}
        for _, v in ipairs(qualified) do
            table.insert(loga, &quot;&lt;TR&gt;&lt;TD&gt;&lt;A href=http://coord.info/&quot; .. v.gccode .. &quot;&gt;&quot; ..
                              v.gccode .. &quot;&lt;/A&gt;&lt;/TD&gt;&lt;TD&gt;&quot; .. v.hidden .. &quot;&lt;/TD&gt;&lt;TD&gt;&quot; ..
                              v.visitdate .. &quot;&lt;/TD&gt;&lt;TR&gt;&quot;)
        end
        htmlBlob = htmlBlob .. table.concat(loga, &quot;\n&quot;) .. &quot;\n&lt;/TABLE&gt;&quot;
    end
end

-- Return results
return {
    ok = meets,           -- Boolean: whether challenge is completed
    log = resultText,     -- Formatted log text
    html = htmlBlob       -- HTML formatted results
}
</pre>]]></description>
            <dc:creator>magma1447</dc:creator>
            <category>Script development</category>
            <pubDate>Thu, 19 Jun 2025 07:25:51 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,105418,105419#msg-105419</guid>
            <title>Re: Documenting scripts with AI</title>
            <link>/forum/read.php?4,105418,105419#msg-105419</link>
            <description><![CDATA[ Original:<br />
<pre class="bbcode">
local args={...}

conf = args[1].config

local filter = {}
filter.country = conf.country
filter.region = conf.region
filter.county = conf.county
filter.minVisitDate = conf.minVisitDate
filter.maxVisitDate = conf.maxVisitDate
filter.minHiddenDate = conf.minHiddenDate
filter.maxHiddenDate = conf.maxHiddenDate
filter.types = conf.types
filter.sizes = conf.sizes
filter.difficulties = conf.difficulties
filter.terrains = conf.terrains

local mincaches = 1
if (not (conf.mincaches == nil)) and tonumber(conf.mincaches)&gt;0 then
	mincaches = tonumber(conf.mincaches)
end

local minage = 1
if (not (conf.minage == nil)) and tonumber(conf.minage)&gt;=0 then
	minage = tonumber(conf.minage)
end

local maxage = 999
if (not (conf.maxage == nil)) and tonumber(conf.maxage)&gt;=0 then
	maxage = tonumber(conf.maxage)
end

local mintotal = 1
if (not (conf.mintotal == nil)) and tonumber(conf.mintotal)&gt;0 then
	mintotal = tonumber(conf.mintotal)
end

local mindifferentages = 1
if (conf.mindifferentages ~= nil) and tonumber(conf.mindifferentages)&gt;0 then
	mindifferentages = tonumber(conf.mindifferentages)
end

uniquedates = false
if conf.uniquedates ~= nil and conf.uniquedates==&quot;YES&quot; then
	uniquedates = true
end


local showall = false
if (not (conf.showall == nil)) and conf.showall==&quot;YES&quot; then
	showall = true
end

profileName = args[1][&#039;profileName&#039;]
profileId = PGC.ProfileName2Id(profileName)

local cachesfound = 0
local totalage = 0
local differentages = 0
local datelog = {}
local resultText = &quot;&quot;
local htmlBlob = &quot;&quot;
local meets
local countsperage = {}
local logperdate = {}

local caches
myFinds = PGC.GetFinds( profileId, { fields = { &#039;gccode&#039;, &#039;cache_name&#039;, &#039;hidden&#039;, &#039;visitdate&#039; }, filter=filter, order = &#039;OLDESTFIRST&#039; } )

if #myFinds&gt;0 then
	local lastyear = tonumber(string.sub(myFinds[#myFinds].visitdate,1,4));
	for age=1,lastyear-2000 do
		countsperage[age] = 0
    end
end

local qualified = {}
for n,v in ipairs(myFinds) do
	local birthdate = string.sub(v.hidden,6)
	if birthdate == string.sub(v.visitdate,6) then
		local age = tonumber(string.sub(v.visitdate,1,4))-tonumber(string.sub(v.hidden,1,4))
		if age &gt;= minage and age &lt;= maxage then
			if uniquedates then
				if datelog[birthdate] ~= nil then
					-- Project-GC has changed this from %d to %d+ to include multidigit ages
                                        local prevage = tonumber(string.match(datelog[birthdate],&quot; %(age (%d+)%)$&quot;))
					if prevage &lt; age then
						totalage = totalage - prevage
						cachesfound = cachesfound - 1
						countsperage[prevage] = countsperage[prevage] - 1
						if countsperage[prevage] &lt;= 0 then differentages = differentages - 1 end
					else
						age = 0
					end
				end
				if age &gt; 0 then
					datelog[birthdate] = &quot;[&quot; .. v.gccode .. &quot;](http://coord.info/&quot; .. v.gccode .. &quot;) &quot; .. v.cache_name .. &quot;, hidden &quot; .. string.sub(v.hidden,1,4) .. &quot; found &quot; .. string.sub(v.visitdate,1,4) .. &quot; (age &quot; .. age .. &quot;)&quot;
				end
			end
		end
		if age &gt;= minage and age &lt;= maxage then
			table.insert (qualified, v)
			cachesfound = cachesfound + 1
			totalage = totalage + age
			if countsperage[age] == 0 then
				differentages = differentages + 1
			end
			countsperage[age] = countsperage[age] + 1
			if showall or (mincaches&gt;mindifferentages) or (countsperage[age] == 1) or (mintotal&gt;mindifferentages) then
				resultText = resultText .. birthdate .. &quot; [&quot; .. v.gccode .. &quot;](http://coord.info/&quot; .. v.gccode .. &quot;) &quot; .. v.cache_name .. &quot;, hidden &quot; .. string.sub(v.hidden,1,4) .. &quot; found &quot; .. string.sub(v.visitdate,1,4) .. &quot; (age &quot; .. age .. &quot;)\n&quot;
			end
			meets = (totalage &gt;= mintotal) and (cachesfound &gt;= mincaches) and (differentages &gt;= mindifferentages)
			if meets and not showall then break end
		end
	end		
end

if meets and uniquedates then
	resultText = &quot;&quot;
	local a = {}
    for n in pairs(datelog) do table.insert(a, n) end
    table.sort(a)
    for i,n in ipairs(a) do
    	resultText = resultText .. n .. &quot; &quot; .. datelog[n] .. &quot;\n&quot;
    end
end

if not meets then
	if cachesfound == 0 then
		htmlBlob = &quot;You have not found any qualifying cache.&quot;
	elseif cachesfound &lt; mincaches then
		htmlBlob = &quot;You have only found &quot; .. cachesfound .. &quot; of the &quot; .. mincaches .. &quot; caches required.&quot;
	elseif differentages &lt; mindifferentages then
		htmlBlob = &quot;You have only found caches with &quot; .. differentages .. &quot; different ages (&quot;
		local printcomma = false
		for age,count in ipairs(countsperage) do
			if count &gt; 0 then
				if printcomma then htmlBlob = htmlBlob .. &quot;,&quot; end
				htmlBlob = htmlBlob .. age
				printcomma = true
			end
		end
		htmlBlob = htmlBlob .. &quot;), but you need &quot; .. mindifferentages .. &quot;.&quot;
	elseif totalage &lt; mintotal then
		htmlBlob = &quot;The total age of the birthday caches you found is only &quot; .. totalage .. &quot;, but you need at least &quot; .. mintotal .. &quot;.&quot;
	else
		htmlBlob = &quot;&quot;
	end
	if #qualified &gt; 0 then
		htmlBlob = htmlBlob .. &quot;\n&lt;TABLE&gt;&lt;TR&gt;&lt;TH&gt;GCcode&lt;/TH&gt;&lt;TH&gt;Hidden&lt;/TH&gt;&lt;TH&gt;Found&lt;/TH&gt;&lt;/TR&gt;\n&quot;
		local loga = {}
		for _,v in ipairs(qualified) do
			table.insert (loga, &quot;&lt;TR&gt;&lt;TD&gt;&lt;A href=http://coord.info/&quot;..v.gccode..&quot;&gt;&quot;..v.gccode..&quot;&lt;/A&gt;&lt;/TD&gt;&lt;TD&gt;&quot;..v.hidden..&quot;&lt;/TD&gt;&lt;TD&gt;&quot;..v.visitdate..&quot;&lt;/TD&gt;&lt;TR&gt;&quot;)
		end
		htmlBlob = htmlBlob .. table.concat (loga, &quot;\n&quot;) .. &quot;\n&lt;/TABLE&gt;&quot;
	end
end

return { ok = meets, log = resultText, html = htmlBlob }
</pre>]]></description>
            <dc:creator>magma1447</dc:creator>
            <category>Script development</category>
            <pubDate>Thu, 19 Jun 2025 07:25:27 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,105418,105418#msg-105418</guid>
            <title>Documenting scripts with AI</title>
            <link>/forum/read.php?4,105418,105418#msg-105418</link>
            <description><![CDATA[ I just want to make people aware of how useful AI can be.<br />
<br />
A question was raised on Facebook regarding a challenge checker that didn&#039;t give the expected result. During research I found at least two issues.<br />
1) A bug in the checker giving confusing output.<br />
2) The cloned tag not matching the challenge requirements.<br />
<br />
To understand what was happening (not my script) I wanted to document the LUA code. I gave it to an AI (Anthropic) and asked it to add comments. It was almost perfect on first attempt, only minor semantics.<br />
<br />
I then explained the issue itself, gave it output, and what was missing. From there we concluded about the issues, and how to best solve it. AI ain&#039;t always superb, but in this case it for sure wanted to solve things the same way I wanted.<br />
<br />
In the end there was a more or less complete rewrite of the script. Most of the code itself was kept, but changing the order of it quite a bit. Primarily making it from a single pass process into dual pass process. First figure out what data to use, then render output.<br />
<br />
In the end I ended with a more well written script, which became well documented. I will post before and after here.<br />
<br />
My main message here is, don&#039;t be afraid to use AI to produce comments. It makes the scripts much easier to read. Let the AI produce them. Read through it yourself and correct the minor issues that might show up. It&#039;s a fast way of producing well documented code.<br />
<br />
PS! One bug was that it could write that one had 23/25 geocaches, but yet it would list 24. The issue was unnecessary complex code to render output at the same time as it was figuring out which geocaches was valid and not.]]></description>
            <dc:creator>magma1447</dc:creator>
            <category>Script development</category>
            <pubDate>Thu, 19 Jun 2025 07:25:05 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,97665,97791#msg-97791</guid>
            <title>Re: GPT tool for Checking Challenge Cache Compliance with Guidelines</title>
            <link>/forum/read.php?4,97665,97791#msg-97791</link>
            <description><![CDATA[ Okay, that makes sense too. I’ll consider this matter closed.]]></description>
            <dc:creator>Orxion</dc:creator>
            <category>Script development</category>
            <pubDate>Thu, 23 Jan 2025 10:13:30 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,97665,97790#msg-97790</guid>
            <title>Re: GPT tool for Checking Challenge Cache Compliance with Guidelines</title>
            <link>/forum/read.php?4,97665,97790#msg-97790</link>
            <description><![CDATA[ Great example of why it&#039;s stupid to use ML for things like this.]]></description>
            <dc:creator>Pleu</dc:creator>
            <category>Script development</category>
            <pubDate>Thu, 23 Jan 2025 10:01:22 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,97665,97703#msg-97703</guid>
            <title>Re: GPT tool for Checking Challenge Cache Compliance with Guidelines</title>
            <link>/forum/read.php?4,97665,97703#msg-97703</link>
            <description><![CDATA[ I&#039;m in IT (although I&#039;m snobbish and would probably not call my job an &quot;IT job&quot;)... and honestly, I don&#039;t think every problem is best solved by technology. <br />
<br />
As I said, I think it&#039;s bad to have it available, because it will give inaccurate answers (at least sometimes), and I&#039;m sure some users will use its output as a justification that their challenge is allowed, when it isn&#039;t. The guidelines have translations into many different languages, and if you can&#039;t understand them (but more likely can&#039;t be bothered to read them at all), you probably won&#039;t be using the ChatGPT bot either. We&#039;ve got many active people on here who are very knowledgeable on the guidelines, including the &quot;unwritten rules&quot; which take into account regional variations... and will politely let you know if you (accidentally :)) request a challenge that isn&#039;t allowed.]]></description>
            <dc:creator>bmuzzin</dc:creator>
            <category>Script development</category>
            <pubDate>Mon, 20 Jan 2025 14:31:10 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,97665,97702#msg-97702</guid>
            <title>Re: GPT tool for Checking Challenge Cache Compliance with Guidelines</title>
            <link>/forum/read.php?4,97665,97702#msg-97702</link>
            <description><![CDATA[ The guidelines might seem straightforward if you’re familiar with them—you remember definitions and terms like “streak” without any issues, not to mention the English language itself. However, I come across many people who have an idea but, despite having access to the guidelines, only translate part of them with their clumsy translation and may not fully understand them. So, it’s probably not as straightforward as it seems. Based on what I sometimes see in the forum’s script requests, quite a few push the boundaries or even go beyond what’s allowed.<br />
<br />
Regarding your attempt, even with precise input, GPT often requires fine-tuning, but that’s the case with almost everything in the IT world. This was just a suggestion; I’m not forcing anyone to use it. :)]]></description>
            <dc:creator>Orxion</dc:creator>
            <category>Script development</category>
            <pubDate>Mon, 20 Jan 2025 13:49:49 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,97665,97687#msg-97687</guid>
            <title>Re: GPT tool for Checking Challenge Cache Compliance with Guidelines</title>
            <link>/forum/read.php?4,97665,97687#msg-97687</link>
            <description><![CDATA[ &quot;Is a streak challenge for 500 days allowed?&quot;<br />
<br />
ChatGPT said:<br />
<br />
A streak challenge requiring 500 consecutive days of geocaching is allowed under the Geocaching Challenge Cache Guidelines, but it must comply with certain conditions to ensure it aligns with the rules set as of June 23, 2021<br />
...<br />
<br />
Literally the first question I asked, and it&#039;s just wrong. The guidelines aren&#039;t that hard to follow... The problem is that people just aren&#039;t reading them at all, and this tool might give someone the false sense that their challenge idea is allowed, when it definitely isn&#039;t.]]></description>
            <dc:creator>bmuzzin</dc:creator>
            <category>Script development</category>
            <pubDate>Mon, 20 Jan 2025 00:48:10 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,97665,97665#msg-97665</guid>
            <title>GPT tool for Checking Challenge Cache Compliance with Guidelines</title>
            <link>/forum/read.php?4,97665,97665#msg-97665</link>
            <description><![CDATA[ Apologies in advance if this is not the correct place to share this. The tool is not a script but a simple tool designed to check whether a challenge cache idea complies with the current guidelines. I wasn’t sure where this belongs, so I posted it here.<br />
<br />
To reduce the number of non-compliant requests, it might be beneficial to add something like this directly to the “Checker Requests (Read Me First)” forum section.<br />
<br />
This is just a prototype where a GPT model compares the challenge idea against the guidelines. While it’s not entirely foolproof, it can be further improved.<br />
<br />
The only requirement is to be logged in to OpenAI with a registered account. It should work for everyone and is free to use (you only need an OpenAI account).<br />
<br />
<a href="https://chatgpt.com/g/g-678c2e0abbac81919722e49750b83e7b-gc-challenge-validator"  rel="nofollow">https://chatgpt.com/g/g-678c2e0abbac81919722e49750b83e7b-gc-challenge-validator</a>]]></description>
            <dc:creator>Orxion</dc:creator>
            <category>Script development</category>
            <pubDate>Sat, 18 Jan 2025 23:07:39 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,96238,96403#msg-96403</guid>
            <title>Re: What are the files available to PGC.GetJsonData()?</title>
            <link>/forum/read.php?4,96238,96403#msg-96403</link>
            <description><![CDATA[ To my knowledge, the list is not available publicly. There&#039;s not that much data though: 277 files.]]></description>
            <dc:creator>Hügh</dc:creator>
            <category>Script development</category>
            <pubDate>Fri, 27 Dec 2024 11:33:17 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,96238,96238#msg-96238</guid>
            <title>What are the files available to PGC.GetJsonData()?</title>
            <link>/forum/read.php?4,96238,96238#msg-96238</link>
            <description><![CDATA[ I&#039;ve always wondered what files were available through PGC.GetJsonData(), but I haven&#039;t been able to find a list of the files available to that method.  I would love to know if there is one (or request one if there isn&#039;t one already), to help de-duplicate data where possible.]]></description>
            <dc:creator>queengooborg</dc:creator>
            <category>Script development</category>
            <pubDate>Sun, 22 Dec 2024 09:19:00 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,91453,95411#msg-95411</guid>
            <title>Re: Country polygons break the map</title>
            <link>/forum/read.php?4,91453,95411#msg-95411</link>
            <description><![CDATA[ PolygonInfo functions and showing country polygons on the map now work as expected.<br />
Thanks to Magnus for fixing it!]]></description>
            <dc:creator>KaiserVonChina</dc:creator>
            <category>Script development</category>
            <pubDate>Mon, 02 Dec 2024 19:22:35 +0000</pubDate>
        </item>
        <item>
            <guid>/forum/read.php?4,94961,94967#msg-94967</guid>
            <title>Re: Debugging scripts?</title>
            <link>/forum/read.php?4,94961,94967#msg-94967</link>
            <description><![CDATA[ Perfect! Great hint - thanks. Found the culprit - outdated country name in parameter list which apparently no longer exists in PGC.]]></description>
            <dc:creator>sm07</dc:creator>
            <category>Script development</category>
            <pubDate>Wed, 20 Nov 2024 19:21:42 +0000</pubDate>
        </item>
    </channel>
</rss>
