https://github.com/dwayne/xo-python
A Python CLI game and library for Tic-tac-toe.
https://github.com/dwayne/xo-python
python tic-tac-toe
Last synced: 6 months ago
JSON representation
A Python CLI game and library for Tic-tac-toe.
- Host: GitHub
- URL: https://github.com/dwayne/xo-python
- Owner: dwayne
- License: mit
- Created: 2016-09-05T07:49:38.000Z (about 9 years ago)
- Default Branch: master
- Last Pushed: 2017-04-04T10:21:22.000Z (over 8 years ago)
- Last Synced: 2024-10-08T14:50:53.710Z (about 1 year ago)
- Topics: python, tic-tac-toe
- Language: Python
- Homepage: https://pypi.python.org/pypi/xo
- Size: 23.4 KB
- Stars: 10
- Watchers: 1
- Forks: 1
- Open Issues: 3
-
Metadata Files:
- Readme: README.rst
- Changelog: CHANGELOG.rst
- License: LICENSE.txt
Awesome Lists containing this project
README
xo
==.. image:: https://img.shields.io/pypi/v/xo.svg
:target: https://pypi.python.org/pypi/xoA `Python `_ CLI game and library for `Tic-tac-toe `_.
The library is written in a modular way. Its overall design consists of 4 decoupled components:
1. A Tic-tac-toe board data structure, ``xo.board``.
2. An arbiter for analyzing the state of a board, ``xo.arbiter``.
3. A game engine to implement and enforce the Tic-tac-toe game logic, ``xo.game``.
4. And finally, an AI for finding excellent moves, ``xo.ai``.**The board**
.. code-block:: python
>>> from xo.board import isempty, Board
>>> board = Board.fromstring('..x.o')
>>> print(board)
..x.o....>>> print(board.toascii())
| | x
---+---+---
| o |
---+---+---
| |>>> board[1, 3]
x
>>> board[3, 3] = 'x'
>>> print(board)
..x.o...x>>> for r, c, piece in board:
... if isempty(piece):
... print('{}, {}'.format(r, c))
...
1, 1
1, 2
2, 1
2, 3
3, 1
3, 2The 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:
.. code-block:: python
>>> board = Board.fromstring('xxxxxxxxo')
>>> print(board)
xxxxxxxxoThe arbiter is concerned about that though and can detect such invalid board layouts.
**The arbiter**
.. code-block:: python
>>> from xo import arbiter
>>> from xo.board import Board>>> arbiter.outcome(Board.fromstring(), 'x')
{
'piece_counts': {'os': 0, 'xs': 0, 'es': 9},
'status': 'in-progress'
}>>> arbiter.outcome(Board.fromstring('xxxoo'), 'o')
{
'piece_counts': {'os': 2, 'xs': 3, 'es': 4},
'details': [
{'index': 1, 'positions': [(1, 1), (1, 2), (1, 3)], 'where': 'row'}
],
'status': 'gameover',
'reason': 'loser'
}>>> arbiter.outcome(Board.fromstring('xxxxxxxxo'), 'x')
{
'piece_counts': {'os': 1, 'xs': 8, 'es': 0},
'status': 'invalid',
'reason': 'too-many-moves-ahead'
}**The game engine**
Enforcer of the game rules.
.. code-block:: python
>>> from xo.game import Game
>>> game = Game()
>>> game.start('x')
>>> game.moveto(1, 1)
{
'name': 'next-turn',
'last_move': {'token': 'x', 'r': 1, 'c': 1}
}
>>> game.moveto(1, 1)
{
'name': 'invalid-move',
'reason': 'occupied'
}
>>> game.moveto(0, 0)
{
'name': 'invalid-move',
'reason': 'out-of-bounds'
}
>>> game.moveto(2, 2)
{
'name': 'next-turn',
'last_move': {'token': 'o', 'r': 2, 'c': 2}
}
>>> game.moveto(3, 1)
{
'name': 'next-turn',
'last_move': {'token': 'x', 'r': 3, 'c': 1}
}
>>> print(game.board.toascii())
x | |
---+---+---
| o |
---+---+---
x | |>>> game.moveto(3, 3)
{
'name': 'next-turn',
'last_move': {'token': 'o', 'r': 3, 'c': 3}
}
>>> game.moveto(2, 1)
{
'name': 'gameover',
'reason': 'winner',
'last_move': {'token': 'x', 'r': 2, 'c': 1},
'details': [{'index': 1, 'positions': [(1, 1), (2, 1), (3, 1)], 'where': 'column'}]
}>>> game.moveto(1, 3)
...
xo.error.IllegalStateError: gameover>>> # start a new game
>>> game.restart()
>>> # since x won, it would be x's turn to play
>>> # if the game was squashed then it would have been o's turn to play
>>> game.moveto(1, 1)
>>> print(game.board.toascii())
x | |
---+---+---
| |
---+---+---
| |**The AI**
No Tic-tac-toe library is complete without an AI that can play a perfect game of Tic-tac-toe.
.. code-block:: python
>>> from xo import ai
>>> from xo.board import Board>>> ai.evaluate(Board.fromstring('xo.xo.'), 'x')
MinimaxResult(score=26, depth=1, positions=[(3, 1)])>>> ai.evaluate(Board.fromstring('xo.xo.'), 'o')
MinimaxResult(score=26, depth=1, positions=[(3, 2)])>>> ai.evaluate(Board.fromstring('x.o'), 'x')
MinimaxResult(score=18, depth=5, positions=[(2, 1), (3, 1), (3, 3)])Finally, ``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.
**Note:** *An extensive suite of tests is also available that can help you better understand how each component is supposed to work.*
Installation
------------Install it using:
.. code-block:: bash
$ pip install xo
You would now have access to an executable called ``xo``. Type
.. code-block:: bash
$ xo
to starting playing immediately.
Usage
-----For help, type
.. code-block:: bash
$ xo -h
By 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:
.. code-block:: bash
$ # Computer vs Human
$ xo -x computer -o human$ # Human vs Human
$ xo -x human -o human
$ xo -o human # since x defaults to human$ # Computer vs Computer
$ xo -x computer -o computer
$ xo -x computer # since o defaults to computerYou can also change who plays first. By default it's the ``x`` player.
.. code-block:: bash
$ # Let o play first
$ xo -f oFinally, 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.
.. code-block:: bash
$ xo -x computer -r 5
.....Game statistics
---------------
Total games played: 5 (2.438 secs)
Number of times x won: 0
Number of times o won: 0
Number of squashed games: 5Development
-----------Get the source code.
.. code-block:: bash
$ git clone git@github.com:dwayne/xo-python.git
Create a `virtual environment `_ and activate it.
.. code-block:: bash
$ cd xo-python
$ pyvenv venv
$ . venv/bin/activateThen, upgrade ``pip`` and ``setuptools`` and install the development dependencies.
.. code-block:: bash
(venv) $ pip install -U pip setuptools
(venv) $ pip install -r requirements-dev.txtYou're now all set to begin development.
Testing
-------Tests are written using the `unittest `_ unit testing framework.
Run all tests.
.. code-block:: bash
(venv) $ python -m unittest
Run a specific test module.
.. code-block:: bash
(venv) $ python -m unittest tests.test_arbiter
Run a specific test case.
.. code-block:: bash
(venv) $ python -m unittest tests.test_arbiter.GameoverPositionsTestCase
Run a specific test method.
.. code-block:: bash
(venv) $ python -m unittest tests.test_arbiter.GameoverPositionsTestCase.test_when_x_wins
Credits
-------Thanks to `Patrick Henry Winston `_ for clarifying the Minimax algorithm. His `video `_ on the topic was a joy to watch.
Copyright
---------Copyright (c) 2016 Dwayne Crooks. See `LICENSE `_ for further details.