Codemojex · C5.1 · a dive of The Economy and the Bank
Three currencies.
Three currencies move through the game, each in its own lane. A player row carries them as separate integer columns, and the rules that spend and earn them never cross: keys pay in paid rooms, clips pay in free rooms and carry no value, and diamonds are the prize, convertible to keys at a fixed 10:1.
Grounding — echo/…/codemojex/wallet.ex (charge_guess/3, convert_to_keys/2, balance/1) · economy.ex (diamonds_per_key, keys_from_diamonds/1, to_usd/1) · the wallet + economy stories.
guess_fee, default 1. The wallet story: a paid-room guess debits one key and leaves clips untouched.Keys, clips, diamonds
wallet.ex · charge_guess/3One function keeps the paid and free paths from crossing. charge_guess/3 is a cond over the game: a free game debits clips, a Golden Room’s paid guess is the two-sided keys charge that also funds the pool, and an ordinary paid game debits keys. A player can never spend a key in a free room or a clip in a paid one.
def charge_guess(player, game_map, ref) do
cond do
Map.get(game_map, :free, false) ->
debit(player, :clips, Map.get(game_map, :clip_cost, 1), "guess", ref)
Map.get(game_map, :golden, false) ->
charge_guess_golden(player, Map.get(game_map, :guess_fee, 1), ref) # two-sided
true ->
debit(player, :keys, Map.get(game_map, :guess_fee, 1), "guess", ref)
end
end
Clips carry no value by design — they are the free-play currency, excluded from the spendable balance and never converted to keys or diamonds. The wallet story pins both halves: a paid-room guess spends a key and never a clip; a free-room guess spends a clip and never a key.
The fixed, public conversion
economy.exDiamonds convert to keys at a fixed 10:1, and the rate is public. The valuation is pure arithmetic — keys_from_diamonds/1 floors, and to_cents/1 prices a diamond at 1.2 US cents. Pick a diamond amount and watch what it is worth.
keys_from_diamonds(100) = div(100, 10) = 10, and to_usd(100) = $1.20. An exact multiple of 10 — converts cleanly with no remainder.@diamonds_per_key 10 @cents_per_diamond 1.2 def keys_from_diamonds(d) when d >= 0, do: div(d, @diamonds_per_key) # floored def to_cents(d), do: round(d * @cents_per_diamond) # 1 dia = 1.2¢ def to_usd(d), do: "$" <> :erlang.float_to_binary(to_cents(d) / 100, decimals: 2)
The economy story pins the numbers: 283 diamonds are worth 28 keys — the remainder does not round up — and render as $3.40; 100 diamonds render as $1.20. The actual convert_to_keys/2 is stricter than the valuation: it refuses a non-multiple of 10 (rem(diamonds, 10) != 0 rolls back :bad_amount), so a conversion always moves whole keys and strands no diamonds.
Spendable versus held
wallet.ex · balance/1balance/1 sets the spendable figures apart from the raw columns. Keys are spendable as-is. Diamonds are spendable minus what is held: available_diamonds: p.diamonds − p.locked_diamonds. Clips are excluded from the spendable balance entirely, because they carry no value.
So the balance a player is shown to spend is never more than the money that is actually free to move. A diamond that is locked — reserved against an obligation — still sits in the diamonds column, but it is subtracted from the available figure, so it cannot be spent twice.
Next — C5.2 shows how each of these moves is made atomic: the row lock, the non-negative check, and the paired TXN ledger row.
References
sources · relatedSources
- All-pay auctionevery guess pays a fee whether or not it wins
- Repo.transactionthe transaction each spend runs inside
Related
- C5.2 — The transactional wallethow a spend is made atomic
- C6 — The Revenue Ledger and the KeyShopwhere keys are bought
- Redis Patterns Appliedthe currency and counter patterns applied