{
"info": {
"author": "Dwayne Crooks",
"author_email": "me@dwaynecrooks.com",
"bugtrack_url": null,
"classifiers": [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: POSIX :: Linux",
"Programming Language :: Python",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: Implementation :: CPython",
"Topic :: Games/Entertainment :: Board Games"
],
"description": "xo\n==\n\n.. image:: https://img.shields.io/pypi/v/xo.svg\n :target: https://pypi.python.org/pypi/xo\n\nA `Python `_ CLI game and library for `Tic-tac-toe `_.\n\nThe library is written in a modular way. Its overall design consists of 4 decoupled components:\n\n1. A Tic-tac-toe board data structure, ``xo.board``.\n2. An arbiter for analyzing the state of a board, ``xo.arbiter``.\n3. A game engine to implement and enforce the Tic-tac-toe game logic, ``xo.game``.\n4. And finally, an AI for finding excellent moves, ``xo.ai``.\n\n**The board**\n\n.. code-block:: python\n\n >>> from xo.board import isempty, Board\n\n >>> board = Board.fromstring('..x.o')\n >>> print(board)\n ..x.o....\n\n >>> print(board.toascii())\n | | x\n ---+---+---\n | o |\n ---+---+---\n | |\n\n >>> board[1, 3]\n x\n >>> board[3, 3] = 'x'\n >>> print(board)\n ..x.o...x\n\n >>> for r, c, piece in board:\n ... if isempty(piece):\n ... print('{}, {}'.format(r, c))\n ...\n 1, 1\n 1, 2\n 2, 1\n 2, 3\n 3, 1\n 3, 2\n\nThe board isn't concerned with whether or not a given layout can be reached in an actual Tic-tac-toe game. Hence, the following is perfectly legal:\n\n.. code-block:: python\n\n >>> board = Board.fromstring('xxxxxxxxo')\n >>> print(board)\n xxxxxxxxo\n\nThe arbiter is concerned about that though and can detect such invalid board layouts.\n\n**The arbiter**\n\n.. code-block:: python\n\n >>> from xo import arbiter\n >>> from xo.board import Board\n\n >>> arbiter.outcome(Board.fromstring(), 'x')\n {\n 'piece_counts': {'os': 0, 'xs': 0, 'es': 9},\n 'status': 'in-progress'\n }\n\n >>> arbiter.outcome(Board.fromstring('xxxoo'), 'o')\n {\n 'piece_counts': {'os': 2, 'xs': 3, 'es': 4},\n 'details': [\n {'index': 1, 'positions': [(1, 1), (1, 2), (1, 3)], 'where': 'row'}\n ],\n 'status': 'gameover',\n 'reason': 'loser'\n }\n\n >>> arbiter.outcome(Board.fromstring('xxxxxxxxo'), 'x')\n {\n 'piece_counts': {'os': 1, 'xs': 8, 'es': 0},\n 'status': 'invalid',\n 'reason': 'too-many-moves-ahead'\n }\n\n**The game engine**\n\nEnforcer of the game rules.\n\n.. code-block:: python\n\n >>> from xo.game import Game\n\n >>> game = Game()\n >>> game.start('x')\n >>> game.moveto(1, 1)\n {\n 'name': 'next-turn',\n 'last_move': {'token': 'x', 'r': 1, 'c': 1}\n }\n >>> game.moveto(1, 1)\n {\n 'name': 'invalid-move',\n 'reason': 'occupied'\n }\n >>> game.moveto(0, 0)\n {\n 'name': 'invalid-move',\n 'reason': 'out-of-bounds'\n }\n >>> game.moveto(2, 2)\n {\n 'name': 'next-turn',\n 'last_move': {'token': 'o', 'r': 2, 'c': 2}\n }\n >>> game.moveto(3, 1)\n {\n 'name': 'next-turn',\n 'last_move': {'token': 'x', 'r': 3, 'c': 1}\n }\n >>> print(game.board.toascii())\n x | |\n ---+---+---\n | o |\n ---+---+---\n x | |\n\n >>> game.moveto(3, 3)\n {\n 'name': 'next-turn',\n 'last_move': {'token': 'o', 'r': 3, 'c': 3}\n }\n >>> game.moveto(2, 1)\n {\n 'name': 'gameover',\n 'reason': 'winner',\n 'last_move': {'token': 'x', 'r': 2, 'c': 1},\n 'details': [{'index': 1, 'positions': [(1, 1), (2, 1), (3, 1)], 'where': 'column'}]\n }\n\n >>> game.moveto(1, 3)\n ...\n xo.error.IllegalStateError: gameover\n\n >>> # start a new game\n >>> game.restart()\n >>> # since x won, it would be x's turn to play\n >>> # if the game was squashed then it would have been o's turn to play\n >>> game.moveto(1, 1)\n >>> print(game.board.toascii())\n x | |\n ---+---+---\n | |\n ---+---+---\n | |\n\n**The AI**\n\nNo Tic-tac-toe library is complete without an AI that can play a perfect game of Tic-tac-toe.\n\n.. code-block:: python\n\n >>> from xo import ai\n >>> from xo.board import Board\n\n >>> ai.evaluate(Board.fromstring('xo.xo.'), 'x')\n MinimaxResult(score=26, depth=1, positions=[(3, 1)])\n\n >>> ai.evaluate(Board.fromstring('xo.xo.'), 'o')\n MinimaxResult(score=26, depth=1, positions=[(3, 2)])\n\n >>> ai.evaluate(Board.fromstring('x.o'), 'x')\n MinimaxResult(score=18, depth=5, positions=[(2, 1), (3, 1), (3, 3)])\n\nFinally, ``xo.cli`` brings it all together in its implementation of the command-line Tic-tac-toe game. It's interesting to see how easy it becomes to implement the game so be sure to check it out.\n\n**Note:** *An extensive suite of tests is also available that can help you better understand how each component is supposed to work.*\n\nInstallation\n------------\n\nInstall it using:\n\n.. code-block:: bash\n\n $ pip install xo\n\nYou would now have access to an executable called ``xo``. Type\n\n.. code-block:: bash\n\n $ xo\n\nto starting playing immediately.\n\nUsage\n-----\n\nFor help, type\n\n.. code-block:: bash\n\n $ xo -h\n\nBy default ``xo`` is configured for a human player to play with ``x`` and a computer player to play with ``o``. However, this can be easily changed to allow any of the other 3 possibilities:\n\n.. code-block:: bash\n\n $ # Computer vs Human\n $ xo -x computer -o human\n\n $ # Human vs Human\n $ xo -x human -o human\n $ xo -o human # since x defaults to human\n\n $ # Computer vs Computer\n $ xo -x computer -o computer\n $ xo -x computer # since o defaults to computer\n\nYou can also change who plays first. By default it's the ``x`` player.\n\n.. code-block:: bash\n\n $ # Let o play first\n $ xo -f o\n\nFinally, when letting the computers battle it out you can specify the number of times you want them to play each other. By default they play 50 rounds.\n\n.. code-block:: bash\n\n $ xo -x computer -r 5\n .....\n\n Game statistics\n ---------------\n Total games played: 5 (2.438 secs)\n Number of times x won: 0\n Number of times o won: 0\n Number of squashed games: 5\n\nDevelopment\n-----------\n\nGet the source code.\n\n.. code-block:: bash\n\n $ git clone git@github.com:dwayne/xo-python.git\n\nCreate a `virtual environment `_ and activate it.\n\n.. code-block:: bash\n\n $ cd xo-python\n $ pyvenv venv\n $ . venv/bin/activate\n\nThen, upgrade ``pip`` and ``setuptools`` and install the development dependencies.\n\n.. code-block:: bash\n\n (venv) $ pip install -U pip setuptools\n (venv) $ pip install -r requirements-dev.txt\n\nYou're now all set to begin development.\n\nTesting\n-------\n\nTests are written using the `unittest `_ unit testing framework.\n\nRun all tests.\n\n.. code-block:: bash\n\n (venv) $ python -m unittest\n\nRun a specific test module.\n\n.. code-block:: bash\n\n (venv) $ python -m unittest tests.test_arbiter\n\nRun a specific test case.\n\n.. code-block:: bash\n\n (venv) $ python -m unittest tests.test_arbiter.GameoverPositionsTestCase\n\nRun a specific test method.\n\n.. code-block:: bash\n\n (venv) $ python -m unittest tests.test_arbiter.GameoverPositionsTestCase.test_when_x_wins\n\nCredits\n-------\n\nThanks to `Patrick Henry Winston `_ for clarifying the Minimax algorithm. His `video `_ on the topic was a joy to watch.\n\nCopyright\n---------\n\nCopyright (c) 2016 Dwayne Crooks. See `LICENSE `_ for further details.\n\n\nChange Log\n----------\n\n`1.0.0`_ (2016-09-09)\n+++++++++++++++++++++\n\n**Added**\n\n- A board data structure\n- An arbiter\n- A game engine\n- An AI based on the Minimax algorithm\n- A CLI\n\n0.0.1 (2016-09-05)\n++++++++++++++++++\n\nBirth!\n\n.. _`Unreleased`: https://github.com/dwayne/xo-python/compare/v1.0.0...HEAD\n.. _`1.0.0`: https://github.com/dwayne/xo-python/compare/v0.0.1...v1.0.0\n\n\n",
"description_content_type": null,
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "https://github.com/dwayne/xo-python",
"keywords": "tic-tac-toe tic tac toe noughts crosses",
"license": "MIT",
"maintainer": "",
"maintainer_email": "",
"name": "xo",
"package_url": "https://pypi.org/project/xo/",
"platform": "",
"project_url": "https://pypi.org/project/xo/",
"project_urls": {
"Homepage": "https://github.com/dwayne/xo-python"
},
"release_url": "https://pypi.org/project/xo/1.0.0/",
"requires_dist": null,
"requires_python": "",
"summary": "A Tic-tac-toe CLI game and library.",
"version": "1.0.0"
},
"last_serial": 2333659,
"releases": {
"0.0.1": [
{
"comment_text": "",
"digests": {
"md5": "935322658782cba6fde5e83dda70634f",
"sha256": "88422197c5011f8c84b4af55b4c80792440790cae0e114aff8be151540b6c25d"
},
"downloads": -1,
"filename": "xo-0.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "935322658782cba6fde5e83dda70634f",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 3339,
"upload_time": "2016-09-05T07:53:14",
"url": "https://files.pythonhosted.org/packages/3c/51/886c72a83aacdccb79edc8381745fc78b7bdf30613ae0979f396ac33e64d/xo-0.0.1-py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "e2cc016181240da08b0cc9982afb4f49",
"sha256": "138a2c3d2d2500119e6351e56c77df3f78a46e0bbe0d2b93fcf284ada6564fa7"
},
"downloads": -1,
"filename": "xo-0.0.1.tar.gz",
"has_sig": false,
"md5_digest": "e2cc016181240da08b0cc9982afb4f49",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 2464,
"upload_time": "2016-09-05T07:53:16",
"url": "https://files.pythonhosted.org/packages/17/7f/cf7b5464afc07912109b64ada8c38d024b720749bb5583a6c794804c0513/xo-0.0.1.tar.gz"
}
],
"1.0.0": [
{
"comment_text": "",
"digests": {
"md5": "3dee64abe184a68c6924fef40ea1ceea",
"sha256": "2442c5be4cfed225fdc07de42c769fc7c417d6d57b0ef0854915079ca9f36da7"
},
"downloads": -1,
"filename": "xo-1.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "3dee64abe184a68c6924fef40ea1ceea",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 15065,
"upload_time": "2016-09-09T14:30:58",
"url": "https://files.pythonhosted.org/packages/1b/34/69ad2017011bcbd45d6d9c98c1b3776c61f10181385702364bf720dcd417/xo-1.0.0-py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "cb2bfe5c2d7df21094355050fe3429f3",
"sha256": "7ea2c70890fb6994e6f751e5400f74a453adc48f075b3a9a0256fccf556750d1"
},
"downloads": -1,
"filename": "xo-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "cb2bfe5c2d7df21094355050fe3429f3",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 13578,
"upload_time": "2016-09-09T14:31:00",
"url": "https://files.pythonhosted.org/packages/ab/40/970c7abf0d5f990d4501c9508eeb9f524e9de1a201dbe760c1cb6c8a1a6e/xo-1.0.0.tar.gz"
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "3dee64abe184a68c6924fef40ea1ceea",
"sha256": "2442c5be4cfed225fdc07de42c769fc7c417d6d57b0ef0854915079ca9f36da7"
},
"downloads": -1,
"filename": "xo-1.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "3dee64abe184a68c6924fef40ea1ceea",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 15065,
"upload_time": "2016-09-09T14:30:58",
"url": "https://files.pythonhosted.org/packages/1b/34/69ad2017011bcbd45d6d9c98c1b3776c61f10181385702364bf720dcd417/xo-1.0.0-py3-none-any.whl"
},
{
"comment_text": "",
"digests": {
"md5": "cb2bfe5c2d7df21094355050fe3429f3",
"sha256": "7ea2c70890fb6994e6f751e5400f74a453adc48f075b3a9a0256fccf556750d1"
},
"downloads": -1,
"filename": "xo-1.0.0.tar.gz",
"has_sig": false,
"md5_digest": "cb2bfe5c2d7df21094355050fe3429f3",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 13578,
"upload_time": "2016-09-09T14:31:00",
"url": "https://files.pythonhosted.org/packages/ab/40/970c7abf0d5f990d4501c9508eeb9f524e9de1a201dbe760c1cb6c8a1a6e/xo-1.0.0.tar.gz"
}
]
}