I'm happy to report that Lolife playtesting has been giving me lots to work on! While I'm waiting on another round of playtesters I've also been adding some calendar events that trigger based on the Playdate's system clock and I thought it might be interesting to post about how I'm doing that for an Easter event specifically.
The challenge with Easter is that it's a moveable feast - the date changes every year. In Resonant Tale I had calendar events but I deliberately chose to base them on real-world celebrations that have a fixed date. As in other areas (like multi-save support) Lolife presents a good opportunity to improve on what I've done before.
There are two approaches we can take to handling the moving date of (Gregorian) Easter. Approach A is to simply look up the future date of Easter for every year for some amount of years into the future and hard code those into the game. With a simple (but long) chain of elseif datetime.year==20xx I could look up the date and be on my merry way (and not worry about anyone trying to play Lolife in a 100 years or whenever the cut-off is, which is a reasonable thing not to worry about). Approach B is to implement a known algorithm for determining the date of Easter given any year. It's more complex and probably overkill... but I went with Approach B, because that's more fun!
Before we can implement a known algorithm in Pulpscript, first we need to pick which algorithm. Being a popular subject, there are several. The most famous seems to be Gauss's Easter algorithm but (scrolling just past it) I instead opted for what Wikipedia calls the Anonymous Gregorian algorithm. It has the nice property of outputting the month n and the day of the month p which align with the format of datetime.month and datetime.day in Pulpscript.
The algorithm requires some basic arithmetic (addition, subtraction, multiplication, division) as well as the floor function and modulo operation. Pulpscript has a built-in floor function but no mod, so we have to implement that ourselves like so:
m = x
m /= y
_ = floor m
m -= _
m *= y
m = round m
which is equivalent to the expression m = x mod y
(A note on my personal pulpscript convention: I use _ as a temporary variable that is only relevant within a multi-line expression like this one. I also treat single-letter variables as "local" meaning I reuse them freely knowing I will never need to persist them between frames or across multiple events. It helps me keep my scripts neat!)
The round function call at the end is necessary due to rounding precision limitations. While the result of the line above m *= y should always be an integer, in practice it is a float with a value very close to but not quite an integer. If we don't round it off (to the correct value) it can lead to further errors when performing later modulo operations. (To demonstrate, 2 mod 2 should equal 0, but 2 mod 2.000000001 will equal 2.) This caught me out until I realised what was going on!
With that in place, here is an event that implements the Anonymous Gregorian algorithm in Pulpscript:
on getEasterDate do
// Anonymous Gregorian algorithm
// Provide Y as input year
// a = Y mod 19
a = Y
a /= 19
_ = floor a
a -= _
a *= 19
a = round a
// b = floor(Y/100)
b = Y
b /= 100
b = floor b
// c = Y mod 100
c = Y
c /= 100
_ = floor c
c -= _
c *= 100
c = round c
// d = floor(b/4)
d = b
d /= 4
d = floor d
// e = b mod 4
e = b
e /= 4
_ = floor e
e -= _
e *= 4
e = round e
// g = floor((8b + 13)/25)
g = b
g *= 8
g += 13
g /= 25
g = floor g
// h = (19a + b − d − g + 15) mod 30
h = a
h *= 19
h += b
h -= d
h -= g
h += 15
h /= 30
_ = floor h
h -= _
h *= 30
h = round h
// i = floor(c/4)
i = c
i /= 4
i = floor i
// k = c mod 4
k = c
k /= 4
_ = floor k
k -= _
k *= 4
k = round k
// l = (32 + 2e + 2i − h − k) mod 7
l = e
l *= 2
_ = i
_ *= 2
l += _
l += 32
l -= h
l -= k
l /= 7
_ = floor l
l -= _
l *= 7
l = round l
// m = floor((a + 11h + 19l)/433)
m = h
m *= 11
_ = l
_ *= 19
m += _
m += a
m /= 433
m = floor m
// n = floor((h + l - 7m + 90)/25)
n = m
n *= 7
n *= -1
n += h
n += l
n += 90
n /= 25
n = floor n
// p = (h + l − 7m + 33n + 19) mod 32
p = n
p *= 33
_ = m
_ *= 7
p -= _
p += h
p += l
p += 19
p /= 32
_ = floor p
p -= _
p *= 32
p = round p
end
And that's it! I'm actually then doing a little more maths because I want my calendar events to run for 5 days with 2 days either side of the date they are based around (and that can bridge a month boundary), but if you need to get the date of Easter in any year in Pulpscript the above event will do it for you!
Unrelated to the above, but to show something that isn't all code, here is the new card and launch animation I've made for Lolife (followed by starting a new game for the first time):
