Metadata-Version: 2.5
Name: shufflejar
Version: 0.1.0
Summary: Persistent shuffle bags for everyday choices, with undo.
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# shufflejar

A jar of choices that remembers what you already picked.
Use it for lunch spots, practice prompts, or household chores.
Zero runtime dependencies. Python 3.10+. MIT licensed.

## Install and run

~~~console
python -m pip install shufflejar
shufflejar create lunch ramen tacos pizza
shufflejar draw lunch
shufflejar draw lunch
shufflejar undo lunch
shufflejar list
~~~

State is saved to .shufflejar.json in the current directory by default.
Choose one consistent explicit file to share the same jars across directories:

~~~console
shufflejar --state choices.json create chores dishes laundry vacuum
shufflejar --state choices.json draw chores
~~~

Global flags, including --state, go before the subcommand. Local installation:
**python -m pip install .**. All commands work as **python -m shufflejar** too.

## Python

~~~python
import random
from shufflejar import ShuffleBag, JarStore

bag = ShuffleBag(["ramen", "tacos", "pizza"], rng=random.Random(42))
picked = bag.draw()
assert bag.undo() == picked

store = JarStore("choices.json")
store.create("practice", ["lists", "loops", "functions"])
choice = store.draw("practice")
assert JarStore("choices.json").undo("practice") == choice
~~~

Each item appears once per cycle. After a cycle completes, a new shuffled cycle
starts. For bags of two or more items, the first item of a new cycle cannot be
the last item of the previous cycle. A one-item bag repeats that item.
Items are trimmed, non-empty and unique, with case-sensitive comparisons.
Drawing never edits the original choice list.

## State and undo

Only the latest draw in each jar can be undone. Undo restores the remaining
items and the previous choice. If a cycle had just started, undo returns to
the cycle boundary; drawing again can produce a different shuffle.

**ShuffleBag.to_dict()** and **ShuffleBag.from_dict()** round-trip validated
JSON data, including undo state. They do not persist the random generator state.
**items** returns all choices; **remaining** shows the rest of the current cycle.
An empty remaining list means a new cycle will start on the next draw.

**JarStore.create**, **draw**, and **undo** save atomically after acquiring an
exclusive sibling .lock file. Concurrent writers fail rather than overwrite
one another. Invalid JSON is reported without resetting your file. If a crash
leaves a lock file, ensure no shufflejar process is using the store before
removing that specific .lock file manually. Use local storage; remote filesystems
may have different locking and atomic-replacement guarantees.

Existing jars cannot be replaced by create. Keep a backup of your JSON file if
you edit it manually. This utility uses ordinary pseudorandomness and is not
intended for security-sensitive selection.

Exit status: 0 on success, 2 on input/storage errors.

## Development

~~~console
python -m pip install -e .
python -m unittest discover -s tests -v
~~~
