Cauldron2D.Ledger (Cauldron2D v0.1.4)

Copy Markdown View Source

Every player's results, kept on disk, and the boards made from them.

{:ok, ledger} = Cauldron2D.Ledger.start_link(path: Cauldron2D.Paths.data(:my_game, "ledger.dets"), metrics: [kills: {:sum, :kills}, ratio: {:ratio, :kills, :deaths}, wins: {:count, :won?}])
Cauldron2D.Ledger.record(ledger, "Pit", %{id: "alice", name: "alice", kills: 3, deaths: 1, won?: true})
Cauldron2D.Ledger.board(ledger, :week, :kills, arena: "Pit")

A result is a map with the player's account as :id (their :name when there is no id), the name they played under as :name, and whatever fields the metrics read. A result whose id is {:robot, n} is not kept. Cauldron2D.Ledger.Recorder takes them from a world's events.

Metrics

The game declares its metrics as a keyword list, each one of:

  • {:sum, field} — the field summed over the rounds
  • {:max, field} — the field's largest value
  • {:count, field} — how many rounds had the field true
  • {:ratio, field, by} — the sum of field over the sum of by, to two places; the sum of field alone when by sums to zero
  • a function of the rounds returning a number

A board is the accounts of a period — :day (since midnight UTC), :week (the last seven days) or :all — ranked by one metric, each with every metric's value, the rounds counted and the name last played under, twenty at most. Boards are computed from the table and kept for ten seconds.

The file

The file belongs to one operating system process at a time, held through Cauldron2D.Ledger.Lock. Starting a ledger on a held file fails with {:error, {:held, pid}}, or with held: :idle starts one that keeps nothing: its boards are empty, record/4 and forget/2 answer {:error, {:held, pid}}, and access/1 says {:held, pid}. A DETS failure stops the ledger, and a restart reopens the file repaired.

Summary

Functions

Whether the ledger holds its file: :read_write, or {:held, pid} naming the operating system process that does.

The board of period by metric: the top 20, or top: n of them, or every account with top: :all; arena: narrows it to one arena.

Returns a specification to start this module under a supervisor.

How many results are kept.

Drop every result keep_out? is true of — a function of the row as it was recorded, with its :arena and :at — and say how many went.

The operating system process holding the file at path, or nil when it is free.

The metrics the ledger was started with, in order.

Keep a result from arena, taken at (default now).

Start the ledger.

Types

entry()

@type entry() :: %{
  name: String.t(),
  account: term(),
  value: number(),
  rounds: pos_integer(),
  values: %{required(metric()) => number()}
}

metric()

@type metric() :: atom()

period()

@type period() :: :day | :week | :all

Functions

access(ledger)

@spec access(GenServer.server()) :: :read_write | {:held, pos_integer()}

Whether the ledger holds its file: :read_write, or {:held, pid} naming the operating system process that does.

board(ledger, period, metric, opts \\ [])

@spec board(GenServer.server(), period(), metric(), keyword()) :: [entry()]

The board of period by metric: the top 20, or top: n of them, or every account with top: :all; arena: narrows it to one arena.

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

count(ledger)

@spec count(GenServer.server()) :: non_neg_integer()

How many results are kept.

forget(ledger, keep_out?)

@spec forget(GenServer.server(), (map() -> boolean())) ::
  {:ok, non_neg_integer()} | {:error, term()}

Drop every result keep_out? is true of — a function of the row as it was recorded, with its :arena and :at — and say how many went.

holder(path)

@spec holder(Path.t()) :: pos_integer() | nil

The operating system process holding the file at path, or nil when it is free.

metrics(ledger)

@spec metrics(GenServer.server()) :: [metric()]

The metrics the ledger was started with, in order.

record(ledger, arena, row, at \\ DateTime.utc_now())

@spec record(GenServer.server(), String.t(), map(), DateTime.t()) ::
  :ok | {:error, term()}

Keep a result from arena, taken at (default now).

start_link(opts)

@spec start_link(keyword()) :: GenServer.on_start()

Start the ledger.

Options

  • :path — the DETS file. Required
  • :metrics — the metrics, as the module documentation describes. Required
  • :name — the registered name; default the module, nil for none
  • :held — what to do when another process holds the file: :stop (default) or :idle