Skip to main content

Dates and daily games

The current date Expression returns information about the current date and time. Combined with the set random seed response and daily leaderboards, it's the foundation for building daily challenges, seasonal content, and time-aware games.

Current date expression

Format

Controls what date information is returned:

FormatReturns
days since Castle epochA day number that increments by 1 each day. Useful for daily challenges, computing day differences, or as a leaderboard scope. This is the default.
yearThe current year (e.g. 2026).
monthThe current month as a number (1–12).
dayThe current day of the month (1–31).
timestampA Unix timestamp (seconds since January 1, 1970). Useful for precise time calculations.

Timezone

Controls which timezone is used to determine the date:

TimezoneBehavior
playerUses the player's local timezone. This is the default.
CastleUses Castle's server timezone. All players around the world share the same day boundary. Use this for daily challenges so that everyone gets the same challenge on the same day.

In multiplayer sessions, Castle timezone is always used regardless of this setting, so that all players in the same session agree on the date.

Daily challenge pattern

A daily challenge is a game that changes each day but is the same for every player on a given day. This lets players compete on a level playing field and compare scores on a daily leaderboard. Here's how to set one up:

1. Seed the randomness to today's date

Use the set random seed response with the current date expression (format: "days since Castle epoch", timezone: Castle). This ensures that every random expression in your rules produces the same sequence of values for all players on the same day — but a different sequence tomorrow.

When this card begins:
Set the random number seed to [current date (days since Castle epoch, Castle timezone)]

Any random expressions evaluated after this point (random number in a range, Gaussian, choose, etc.) will produce the same results for every player on the same day.

tip

The random seed only affects random expressions in rules. If you're using Lua scripting, you also need to call math.randomseed() with the same day number to seed Lua's math.random():

local date = castle.getServerDate("*t", "Castle")
math.randomseed(date.daysSinceCastleEpoch)

2. Use a daily leaderboard

Set the leaderboard scope to daily with Castle timezone. This way the leaderboard resets at the same time for everyone, matching the challenge reset.

When this receives a message "game over":
Save variable $score to the leaderboard (scope: daily, timezone: Castle)
Show the leaderboard for $score (sort: high, label: "High Scores")

The leaderboard UI will automatically show "Daily High Scores" when using daily scope.

3. Use Castle timezone consistently

Make sure both the random seed and the leaderboard use Castle timezone. If you mix timezones, a player could end up playing yesterday's challenge but posting to today's leaderboard (or vice versa).

Other date-based patterns

Seasonal content

Use the month format to change your deck based on the time of year:

When this card begins:
If [current date (month)] equals 12:
Set property Drawing: art frame to 2 -- winter art

Weekly leaderboards

Use a custom leaderboard scope with the day number divided by 7:

Save variable $score to the leaderboard (scope: custom, value: floor(current date / 7))

Time-limited events

Use the timestamp format to compare against a specific Unix timestamp and gate content before or after a deadline.

Scripting

Dates are also accessible from Lua scripts:

-- Get the current date as a table
local date = castle.getServerDate("*t", "Castle")
print(date.year, date.month, date.day)
print("Day number: " .. date.daysSinceCastleEpoch)

-- Seed both rule randomness and Lua randomness to today's date
castle.setRandomSeed(date.daysSinceCastleEpoch)
math.randomseed(date.daysSinceCastleEpoch)

-- Pick a daily level (cycles through 10 levels)
local dailyLevel = (date.daysSinceCastleEpoch % 10) + 1