Skip to content

Commit

Permalink
Merge pull request #8 from Bernardo-MG/merge_master
Browse files Browse the repository at this point in the history
Merge master
  • Loading branch information
Bernardo-MG authored Oct 23, 2016
2 parents 9c8c206 + c9f3c56 commit 8221a75
Show file tree
Hide file tree
Showing 52 changed files with 2,105 additions and 73 deletions.
15 changes: 13 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ matrix:
# Tests and deploys docs, also runs coverage report
- python: "3.5"
env: COVERAGE=true TEST_DOCS=true DEPLOY_DOCS=true
addons:
apt:
packages:
- sshpass

before_install:
# Gets scripts
- git clone -b v0.2.0 --single-branch https://github.com/Bernardo-MG/ci-shell-scripts.git ~/.scripts
- git clone -b v0.4.0 --single-branch https://github.com/Bernardo-MG/ci-shell-scripts.git ~/.scripts
# Sets scripts as executable
- chmod -R +x ~/.scripts/*
# Prepares CI environment
Expand All @@ -36,6 +40,10 @@ before_install:
install:
# tox is required for the tests
- pip install tox
# sphinx is required for the docs
- pip install sphinx
# Dependencies
- pip install --upgrade -r requirements.txt
script:
# Code is checked
- ~/.scripts/python/run_tests.sh true check
Expand All @@ -47,4 +55,7 @@ script:
- ~/.scripts/python/run_tests.sh $DO_COVERAGE coverage
after_success:
# Documentation deployment
- ~/.scripts/rtd/deploy.sh $DO_DEPLOY_DOCS dice-notation
- ~/.scripts/sphinx/build-html.sh $DO_DEPLOY_DOCS docs
- cd ~/sphinx/build/html
- ~/.scripts/deploy/deploy-ssh.sh $DO_DEPLOY_DOCS_RELEASE $DEPLOY_USERNAME $DEPLOY_PASSWORD $DEPLOY_HOST $DEPLOY_PORT $DEPLOY_PATH_RELEASE
- ~/.scripts/deploy/deploy-ssh.sh $DO_DEPLOY_DOCS_DEVELOP $DEPLOY_USERNAME $DEPLOY_PASSWORD $DEPLOY_HOST $DEPLOY_PORT $DEPLOY_PATH_DEVELOP
46 changes: 30 additions & 16 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,19 @@ operating with it on any Python application.
:target: https://pypi.python.org/pypi/dice-notation
:alt: Dice Notation Tools for Python Pypi package page

.. image:: https://readthedocs.org/projects/dice-notation/badge/?version=latest
:target: http://dice-notation.readthedocs.org/en/latest/
:alt: Dice Notation Tools for Python latest documentation Status
.. image:: https://readthedocs.org/projects/dice-notation/badge/?version=develop
:target: http://dice-notation.readthedocs.org/en/develop/
:alt: Dice Notation Tools for Python development documentation Status
.. image:: https://img.shields.io/badge/docs-release-blue.svg
:target: http://docs.wandrell.com/dice-notation-python
:alt: Dice Notation Tools for Python latest documentation
.. image:: https://img.shields.io/badge/docs-develop-blue.svg
:target: http://docs.wandrell.com/development/dice-notation-python/
:alt: Dice Notation Tools for Python development documentation

Features
--------

The library contains the following features:

- API for dice and dice notation, along classes to generate values from them
- Parser to create API instances from the notation
- Ply-based parser to generate objects for dice notation
- Easy-to-use objects to handle the notation, just call the 'roll' method
- Classes to support plain dice

Documentation
-------------
Expand All @@ -43,7 +42,7 @@ documentation sites:
- The `development docs`_ are generated from the latest code in the 'develop' branch

You can also create the documentation from the source files, kept in the 'docs'
folder, with the help of Sphinx. For this use the makefile, or the make.bat
folder, with the help of `Sphinx`_. For this use the makefile, or the make.bat
file, contained on that folder.

Prerequisites
Expand Down Expand Up @@ -80,9 +79,24 @@ the following command:
Usage
-----

The application has been coded in Python, without using any particular
The application has been coded in Python, and does not require any particular
framework.

To use it just import the parser::

from dice_notation.parser import DiceParser

And then use it to parse a dice notation expression::

parser = DiceParser()
dice = parser.parse('1d6+2')

The result can be accessed just by calling the 'roll' method as many times as
needed, which will generate a new random value each time it is called::

print(dice.roll())
print(dice.roll())

Collaborate
-----------

Expand All @@ -109,10 +123,10 @@ License

The project has been released under the `MIT License`_.

.. _GitHub project page: https://github.com/Bernardo-MG/dice-notation
.. _latest docs: http://dice-notation.readthedocs.org/en/latest/
.. _development docs: http://dice-notation.readthedocs.org/en/develop/
.. _GitHub project page: https://github.com/Bernardo-MG/dice-notation-python
.. _latest docs: http://docs.wandrell.com/dice-notation-python/
.. _development docs: http://docs.wandrell.com/development/dice-notation-python/
.. _Pypi package: https://pypi.python.org/pypi/dice-notation
.. _MIT License: http://www.opensource.org/licenses/mit-license.php
.. _project issues tracker: https://github.com/Bernardo-MG/dice-notation/issues
.. _project issues tracker: https://github.com/Bernardo-MG/dice-notation-python/issues
.. _Sphinx: http://sphinx-doc.org/
4 changes: 0 additions & 4 deletions dice-notation/__main__.py

This file was deleted.

2 changes: 1 addition & 1 deletion dice-notation/__init__.py → dice_notation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
:license: MIT, see LICENSE for more details.
"""

__version__ = '0.1.0'
__version__ = '1.0.0'
__license__ = 'MIT'
140 changes: 140 additions & 0 deletions dice_notation/dice.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# -*- coding: utf-8 -*-

import sys
from abc import ABCMeta, abstractmethod
from random import randint

"""
Dice classes.
These are just plain dice, not dice notation, even thought they may be used
for handling that too.
The classes in this file will allow creating and using dice and rollable
entities, which are able to generate a random value.
There are two dice classes, the Dice is just the bare dice, while the
RollableDice is an extension, which allows rolling the dice.
"""

__author__ = 'Benardo Martínez Garrido'
__license__ = 'MIT'

if sys.version_info[0] >= 3:
xrange = range


class Rollable(object):
"""
Interface for rollable classes.
While rolling implies using dice to generate a random value, this interface
just takes care of generating a random value.
This way it not only can support any kind of dice, but also more complex
constructions such as dice notation expressions, where calling the roll
method would execute the full expression.
As such, the value generated by rolling may be anything.
"""
__metaclass__ = ABCMeta

def __init__(self):
pass

@abstractmethod
def roll(self):
"""
Generates a random value.
This can be anything, the only expectation is that the output
is randomized somehow.
"""
raise NotImplementedError('The roll method must be implemented')


class Dice(object):
"""
A group of dice, all with the same number of sides. Such a group is just
composed of a quantity of dice, and their number of sides.
Both the quantity and the number of sides are expected to be positive, as
any other value would make no sense.
No other limitation is expected. In the real world the number of sides
which a die may physically have are limited by the rules of geometry,
but there is no reason to take care of that.
"""

def __init__(self, quantity, sides):
super(Dice, self).__init__()
self._quantity = quantity
self._sides = sides

def __str__(self):
return '%sd%s' % (self.quantity, self.sides)

def __repr__(self):
return '<class %s>(quantity=%r, sides=%r)' % \
(self.__class__.__name__, self.quantity, self.sides)

@property
def quantity(self):
"""
The number of dice which compose this group.
This is expected to be a positive value or zero.
:return: the number of dice
"""
return self._quantity

@quantity.setter
def quantity(self, quantity):
self._quantity = quantity

@property
def sides(self):
"""
The number of sides each die has.
All the dice in the group have the same number of sides.
This is expected to be a positive value or zero.
:return: the number of sides
"""
return self._sides

@sides.setter
def sides(self, sides):
self._sides = sides


class RollableDice(Dice, Rollable):
"""
A rollable dice group.
The result of calling the roll method will be an integer, which will be
between 1 and the number of sides. Actually one number will be generated
like that as many times as the value of the quantity field, and all those
values will be added, and then returned.
"""

def __init__(self, quantity, sides):
super(RollableDice, self).__init__(quantity, sides)

def roll(self):
result = 0

if self.quantity == 0 or self.sides == 0:
result = 0
elif self.quantity is None or self.sides is None:
result = None
elif self.quantity > 0 and self.sides > 0:
for x in xrange(self.quantity):
result += randint(1, self.sides)
else:
result = None

return result
5 changes: 5 additions & 0 deletions dice_notation/parser/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-

from dice_notation.parser.dice import DiceParser

__all__ = ['DiceParser']
69 changes: 69 additions & 0 deletions dice_notation/parser/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-

import os
from abc import ABCMeta, abstractmethod

import ply.lex as lex
import ply.yacc as yacc

"""
Base classes for parsers.
It contains some interfaces, along a base parser for the parsing library being
used.
"""

__author__ = 'Bernardo Martínez Garrido'
__license__ = 'MIT'


class Parser(object):
"""
Interface for implementing parsers.
It just contains a single method, 'parse', which will receive a value
and take care of parsing it into another.
"""

__metaclass__ = ABCMeta

def __init__(self):
pass

@abstractmethod
def parse(self, value):
raise NotImplementedError('The parse method must be implemented')


class PlyParser(Parser):
"""
Base class for a lexer/parser that has the rules defined as methods.
It makes use of Ply for the parsing.
"""

tokens = ()
precedence = ()

def __init__(self, **kw):
super(PlyParser, self).__init__()
self.debug = kw.get('debug', 0)
self.names = {}
try:
modname = os.path.split(os.path.splitext(__file__)[0])[
1] + "_" + self.__class__.__name__
except:
modname = "parser" + "_" + self.__class__.__name__
self.debugfile = modname + ".dbg"
self.tabmodule = modname + "_" + "parsetab"
# print self.debugfile, self.tabmodule

# Builds the lexer and parser
lex.lex(module=self, debug=self.debug)
yacc.yacc(module=self,
debug=self.debug,
debugfile=self.debugfile,
tabmodule=self.tabmodule)

def parse(self, value):
return yacc.parse(value)
Loading

0 comments on commit 8221a75

Please sign in to comment.