# TamerlaneHelpers
Helper library for building Tamerlane card game rules engines.
## Installation
Add `tamerlane_helpers` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:tamerlane_helpers, "~> 0.1.0"}
]
end
```
## Quick Start
```elixir
defmodule MyGame.Rules do
@behaviour TamerlaneHelpers.RulesEngine
alias TamerlaneHelpers.Helpers, as: H
alias TamerlaneHelpers.Response
@impl true
def config do
%{
"game" => %{"name" => "My Game", "min_players" => 2, "max_players" => 4},
"deck" => "52",
"stacks" => [%{"id" => "discard", "label" => "Discard", "layout" => "pile"}]
}
end
@impl true
def init(player_ids) do
%{
"players" => Enum.map(player_ids, &%{"id" => &1, "meta" => %{}}),
"state" => %{"phase" => "deal"}
}
end
@impl true
def next(state, players, action) do
hand = H.hand_for(players, state["turn"])
prompt = H.build_prompt(state["turn"], hand, name: "play", count: 1)
decorations = H.build_decorations([{state["turn"], "turn", "Your Turn"}])
response = Response.new([], state, prompt: prompt, decorations: decorations)
{:ok, Response.to_map(response)}
end
end
```
## Modules
### `TamerlaneHelpers.Helpers`
Core helper functions:
- **Player Management**: `hand_for/2`, `seating_from/2`, `next_player/2`, `other_player/2`, `player_ids/1`
- **Card Operations**: `card_value/1`, `card_suit/1`, `cards_of_suit/2`
- **Events**: `draw/2`, `draw_to_stack/2`, `move_cards/2`, `reset_cards/0`, `start_new_hand/1`
- **Locations**: `stack/1`, `hand/1`
- **Prompts**: `build_prompt/3`
- **Decorations**: `build_decorations/1`
- **State**: `increment_in/2`, `build_infos/2`
- **Scoring**: `game_over?/2`, `winners/1`
- **Turn Validation**: `extract_action/1`, `turn?/2`, `validate_turn/2`
- **Utilities**: `hand_empty_after_play?/3`
### `TamerlaneHelpers.Response`
Response struct for `next/3` return values. State must be a map with a `"phase"` key:
```elixir
state = %{"phase" => "play", "turn" => "Alice"}
response = Response.new(events, state,
prompt: prompt,
decorations: decorations,
infos: infos
)
{:ok, Response.to_map(response)}
```
### `TamerlaneHelpers.TrickTaking`
Helpers for trick-taking games using Parlett's notation:
- `playable_cards/3` - Filter hand to legally playable cards
- `trick_winner/2` - Determine who won a trick
- `lead_suit/1` - Get the led suit from trick cards
- `follows_suit?/2` - Check if a card follows the led suit
- `heads_trick?/3` - Check if a card would win the current trick
```elixir
# Standard Whist: follow suit or play any card
playable = TrickTaking.playable_cards(state, hand, rules: [:f, :r])
# Tarock: must trump if void in led suit
playable = TrickTaking.playable_cards(state, hand,
rules: [:f, :t, :r],
trump_suit: "trumps"
)
# Determine trick winner
{winner, card} = TrickTaking.trick_winner(trick_cards, trump_suit: "spades")
```
### `TamerlaneHelpers.RulesEngine`
Behaviour that rules engines must implement:
```elixir
@callback config() :: map()
@callback init([String.t()]) :: map()
@callback next(map(), [map()], map() | nil) :: {:ok, map()}
```
## License
MIT