#!/usr/bin/env python
"""
A python module with logging, doctest to implement simulation of
reversible random knight moves on a chessboard.

Metadata:

  - Author:    W.S.Kendall
  - Copyright: opensource (c) W.S.Kendall 2008
  - Date:      2008-07-03
  - URL:       http://www.wilfridkendall.co.uk
  - Generator: 'E:/HOME/WSK/lib/python/generate.py' version 1.18.


Command-line options::

  usage: %prog [options] arguments
    --doctest:             perform doctests;
    --logging=LOGGING:     logging verbosity level.

Notes:
 - values for 'logging' option:
 'DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'
 (decreasing level of verbosity), with 'WARNING' as default.

"""
__RCSID__ = '$Revision: 1.1 $ $Date: 2008/07/03 19:46:34 $'


# \section{Definitions}
# We need the logging module regardless of whether the #logging# option
# is deployed, since #logging.info# calls are scattered through the text.
import logging, reversible


#\section{Exceptions}
class Error(Exception):
    """
    Generic exception to be raised by this module.
    """


# \section{Classes}
class Knight(reversible.Reversible):
    """
    This class implements a reversible random chess knight, which has
    equal probability of making any permissible jump::

     >>> chain = Knight(seed=1) ; print chain
     []
     >>> chain.equilibrium_draw()
     (1, 6)
     >>> chain.evolution(5) ; print chain
     [(1, 6), (0, 4), (2, 5), (0, 6), (2, 5)]
     >>> print chain.reversed_statistics()
     [((2, 5), (0, 6)), ((0, 6), (2, 5)), ((2, 5), (0, 4)), ((0, 4), (1, 6))]
    """
    def __init__(self, rng=None, seed=None):
        super(Knight, self).__init__(rng=rng, seed=seed)

    def equilibrium_draw(self):
        """
        Draws from equilibrium of chain (namely, uniform distribution
        over pairs of integers each ranging from '0' to '7'
        inclusive), result used to initialize chain.
        """
        self[:]=[(self.rng.choice(range(8)), self.rng.choice(range(8)))]
        return self[-1]

    def update(self):
        """
        Updates chain by single step, which is equally likely to be
        any of the possible knight moves. We use rejection sampling
        for this. The accumulated path is stored in 'self'.
        """
        accepted = False
        while not accepted:
            x = list(self[-1])
            index = self.rng.choice([0, 1])
            x[index] += self.rng.choice([-2, 2])
            x[1 - index] += self.rng.choice([-1, 1])
            accepted = x[0] in range(8) and x[1] in range(8)
        self.append(tuple(x))


# \section{Functions}
def main(opt, arguments):
    """
    Make a single run.
    """
    logging.info('Entering main function')
    n = 10
    chain = Knight()
    chain.equilibrium_draw()
    chain.evolution(n)
    path = [a for a in chain.reversed_statistics() \
                if not a[0] == 0 and not a[0] == 10]
    print path

def _doctest():
    """
    Doctests.
    """
    logging.info('Commencing doctests')
    import doctest
    doctest.testmod()
    print __RCSID__.replace('$', '').strip()


# \section{Main script}
if __name__ == '__main__':
    import optionparse
    option, args = optionparse.parse(__doc__, version=__RCSID__)

    if option.logging is not None:
        logging.basicConfig(level=logging.__dict__[option.logging])
        logging.info('Logging level %s' % option.logging)

    if option.doctest:
        _doctest()


    # The action of the module as script should be concentrated
    # in the following function, to facilitate profiling.
    main(option, args)


    logging.info('Finishing')
    raise SystemExit


# \newpage \scriptsize
# \begin{multicols}{2}
# \section{History}
# \text{ }
#\\ $Log: knight.py,v $
#\\ Revision 1.1  2008/07/03 19:46:34  wskendall
#\\ Initial revision
#\\
#\\ Revision 1.5  2008/07/03 12:35:28  wskendall
#\\ Display confidence interval in function #main#.
#\\
#\\ Revision 1.2  2008/07/03 12:22:32  wskendall
#\\ Added doctest, also statistical test of a longer run.
#\\
#\\ Revision 1.1  2008/07/03 11:36:12  wskendall
#\\ Initial revision
#\\
#
# \end{multicols}

'$Id: knight.py,v 1.1 2008/07/03 19:46:34 wskendall Exp wskendall $'
