Metadata-Version: 2.4
Name: dice_engine
Version: 0.1.0
Summary: A lightweight Dice Engine that allows you to create infinite dice types
Author-email: Dillan Notice <noticedillan2@gmail.com>
License: MIT
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

# Dice Engine
***Dice Engine: Infinite Possibilities***

A lightweight Dice Engine that allows you to create infinite dice types
## Features
- **Lightweight**: Only *3 core classes* and a total of **90 lines**

- **Extensive**: You can create `infinite` die classes using this framework

- **OOP-based**: Uses OOP for its core features

- **No external dependencies**: There are no external dependencies(only `randint` from `random` and `Any` from `typing`. These are things you get when installing python)

## Installation
To install the dice engine, open your terminal and do:

```bash
pip install dice_engine
```
Thats it!
## Importing
The Dice engine has 3 main Classes:  

1. `Die()`
2. `CDice()`
3. `IDice()`

To import the classes, do:

```python
# importing Die()
from dice_engine import Die
# importing CDice()
from dice_engine import CDice
# importing IDice()
from dice_engine import IDice
```
To import all classes, its **Recommended** you do `from dice_engine import Die, CDice, IDice` rather than `import dice_engine` so you dont need to do something like `dice_engine.Die()`
# Part 1: Classes of the Engine
## Class 1: Die()
The base die class for creating **Die with a fixed number of Sides**

It is created like this:

```python
Die(sides: int)
# example
die = Die(6) # creating a d6 die
# minimum sides: 2 sides
```
## Die Methods
# roll()
Quite simple, a method that ***Rolls the Die once***:

```python
# example
die = Die(7) # a d7 Die
print(die.roll()) # eg, 5
```
`roll()` returns the number that was rolled(between **1 and number of sides**)

# results()
Each `Die` object stores how much times a number was rolled(stored in a list), `results()` shows you how much times a number was rolled in a nice format.

```python
die = Die(6) # a d6 die
for _ in range(5):
    die.roll() # rolling 5 times(for loop)
die.results()
# example output:
# Rolls on 1: 0
# Rolls on 2: 1
# Rolls on 3: 1
# Rolls on 4: 1
# Rolls on 5: 1
# Rolls on 6: 1
```
Doesnt return anything, prints directly
# get_results()
Returns the array of how much times a number was rolled

```python
die = Die(9)
die.roll()
print(die.get_results()) # eg, [0,0,0,0,0,1,0,0,0]
```

## Class 2: CDice()
`CDice` is short for as `Class Dice`

A factory for **mass producing Dice with the same properties(sides, class and arguments)**.

It is Created like this:

```python
from typing import Type, Any

CDice(amount: int, classe: Type, sides: int, **kwargs: Any)
# minimum amount: 2
# minimum sides: 2
# Type means it accepts a class reference
```
```python
# example
dice = CDice(20, Die, 6) # 20 d6 Dice/20 Die instances each having 6 sides
# kwargs appear later
```
You only use kwargs in CDice when you give it **a class that accepts more than 1 arguments**, ***ignore*** **the kwargs when the class you give CDice only takes a sides argument**


## CDice Methods
# roll_all()
Very simple, rolls **ALL** the dice inside the `CDice` instance **ONCE**

```python
dice = CDice(10, Die, 5)
dice.roll_all()
dice.results() # remember from Die()?
# example output:
# Max Sides: 5
# Total Rolls on 1: 20
# Total Rolls on 2: 10
# Total Rolls on 4: 10
# Total Rolls on 5: 10
```
**Note: Sides that were not rolled does not appear in** `results()`

`roll_all()` **doesnt return anything**
# specific_roll(index: int)
Finally a method that needs a parameter.

`specific_roll()` rolls a specific die at the index you put in.

```python
specific_roll(index: int)
```
```python
dice = CDice(10, Die, 2)
dice.specific_roll(5) # rolling the die at index 5
dice.results()
# output(example):
# Max Sides: 2
# Total rolls on 2: 1
```
**Note: Index value must be zero-based**

**Note 2: Putting an invalid index will raise an IndexError**

doesnt return anything

# results()
Shows you the amount of times a **Side was rolled in total**

you can see examples of `results()` in the previous examples above

doesnt return anything, prints directly

# get_dices()
Returns **the array of class instances stored inside the `CDice` instance**
## Class 3: IDice()
`IDice` is short for ` Instance Dice`

This is used to manage **Different die types at the same time**, useful when wanting to roll and manage different kinds of dice

It is *Created like this:*
```python
IDice(dices: list[Die]) # Accepts a list of Die objects or a list of Die subclass objects or both
```
```python
dice = IDice([Die(3), Die(7), Die(2)])
```
## IDice Methods
# roll_all()
*Very Simple*, rolls **All** the Dice in the IDice instance **ONCE**
```python
dice = IDice([Die(6), Die(7)])
dice.roll_all() # rolls the 2 Die objects once
#dice.results()
```
**DOESNT RETURN ANYTHING**
# specific_roll(index: int)
*Works exactly like the one in* `CDice`
```python
dice = IDice([Die(8), Die(9), Die(10)])
dice.specific_roll(1) # rolls the Die(9)
#dice.results(False)
```
**Revisit `specific_roll` in `CDice` for more understanding**

**Doesnt return anything**
# results(combine: bool)
This is *different* from the one in `CDice`

`results()` has 2 Modes:
- Mode 1: when **combine = True**(Default Mode)
- Mode 2: when **combine = False**

Both modes dont return anything
## Mode 1: combine = True
This is *quite simple*, it combines the results from **All** dice in the `IDice` instance and displays the total number of times a side was rolled.

***Excluding sides that were not rolled***

Works exactly like `results()` from `CDice`
## Mode 2: combine = False
This mode is *precise*

This *mode* gives you the results of each dice *seperately*
```python
dice = IDice([Die(2), Die(3), Die(4)])
dice.roll_all()
dice.results(False)
```
```python
# example output
# Die 1(Rolled 2):
# Max Sides: 2
#   Rolls on 2: 1
# Die 2(Rolled 1):
# Max Sides: 3
#   Rolls on 1: 1
# Die 3(Rolled 4):
# Max Sides: 4
#   Rolls on 4: 1
```
```python
# General Format
# DieClassName X(Rolled nums, etc)
# Max Sides: Sides
#   Rolls
```
Use this Mode if you want to see the *results* for **Each Individual Die**
# get_dices()
returns the list of instances inside the `IDice` instance
# see_dices()
Lets you see what class each instance inside `IDice` belongs to, doesnt return anything
```python
dice = IDice([Die(10), Die(100), Die(1000)])
dice.see_dices()
# Output
# 1. Die
# 2. Die
# 3. Die
```
```python
# General Format

# X. DieClassName
```
Well, thats all the main classes. *The interesting part is just starting*

# Part 2: Creating Custom Dice
Welcome, this is a unique and interesting part of the engine. To create a custom die type, these *4 Criteria* **MUST BE MET**:

Die Idea: A die that rolls a certain amount of times, lets call it `MultiDie`

## Criteria 1: Your Die type class must inherit from Die()
Yes, your custom die type must be a class.

example:
```python
class MultiDie(Die):
    ...
```
```python
# Generally:
class DieName(Die):
```
## Criteria 2: `__init__`must have `super().__init__(max(sides, 2))` and a sides parameter
Very simple, Inside `__init__` of your custom die class, it must have `super().__init__(max(sides, 2))` it serves 2 imporant reasons:

1. Does `self.sides` for you
2. Gives your class `self._resultss`(very important list)

Example

```python
class MultiDie(Die):
    def __init__(self, sides, rolls):
        super().__init__(max(sides, 2))
        self.rolls = max(rolls, 2)
```
## Criteria 3: Override roll() from Die()
In this criteria, you override the roll method to implement *how* your custom die rolls

**RELATED TO CRITERIA 4**

example:
```python
class MultiDie(Die):
    def __init__(self, sides, rolls):
        super().__init__(max(sides, 2))
        self.rolls = max(rolls, 2)
    def roll(self):
        number = 0
        for _ in range(self.rolls):
            number = randint(1, self.sides) # You got randint when you imported Die()
        return number
# roll() basically does nothing because criteria 4 isnt completed
```
## Criteria 4: Using `self._resultss` to update your dice roll results
The last but most important criteria, updating your results list

`self._resultss` is a list that stores how much times a side of the dice was rolled for all sides of the die

the list is updated via:

```python
self._resultss[number - 1] += 1
# number = the random number you rolled
```

example:

```python
# A class that met all criteria
class MultiDie(Die): # Criteria 1 check
    def __init__(self, sides, rolls): # sides param
        super().__init__(max(sides, 2)) # Criteria 2 check
        self.rolls = max(rolls, 2)
    def roll(self): # Criteria 3 check
        number = 0
        for _ in range(self.rolls):
            number = randint(1, self.sides)
            self._resultss[number - 1] += 1 # Criteria 4 check
        return number # you can return Anything
```
Remember kwargs back at `Cdice`?

The kwargs are used to enter additional arguments into `CDice` apart from sides


```python
dice = CDice(10, MultiDie, 8, rolls=5)
# kwargs being used
```
## Additional examples
Idea: ExplodigDie, rolls again if you roll the highest number

```python
class ExplodingDie(Die):
    def __init__(self, sides = 6):
        super().__init__(max(sides, 2))
    def roll(self):
        explosions = 0
        number = randint(1, self.sides)
        self._resultss[number - 1] += 1
        roll = number
        while roll == self.sides:
            roll = randint(1, self.sides)
            self._resultss[roll - 1] += 1
            explosions += 1
        return explosions
```
Idea 2: RiggedDie, self explanitory
```python
from random import choices
class RiggedDie(Die): 
    def __init__(self, rig = 6, sides = 6, bias = 2):
        super().__init__(max(sides, 2))
        self.bias = max(bias, 1)
        self.rig = max(rig, sides)
    def roll(self):
        weights = [1] * self.sides
        weights[self.rig - 1] = self.bias
        number = choices(range(1, self.sides + 1), weights = weights)[0]
        self._resultss[number - 1] += 1
        return number
```
Creativity holds no bounds
## Additional info
As long as you follow these 4 criteria, **Any dice can be accepted into `CDice` and `IDice`

Remember: *You can create **Any** Die type ypu want*

This is the first and **Last** Version of this engine

There will be no updates whatsoever
