Initial commit
This commit is contained in:
commit
529ef7b6a8
|
@ -0,0 +1 @@
|
||||||
|
book
|
|
@ -0,0 +1,5 @@
|
||||||
|
[book]
|
||||||
|
authors = ["Thomas Forgione"]
|
||||||
|
multilingual = false
|
||||||
|
src = "src"
|
||||||
|
title = "Pytron"
|
|
@ -0,0 +1,51 @@
|
||||||
|
# The Game module
|
||||||
|
|
||||||
|
The `Game` class is probably the most important class in this architecture,
|
||||||
|
since it is the class you will get as a parameter in your AI. Your AI will need
|
||||||
|
to analyse the game in order to take a decision that will make it win.
|
||||||
|
|
||||||
|
It contains the size of the game, the [Map](/wiki/Map)s of each step of the
|
||||||
|
game, the players, and can run.
|
||||||
|
|
||||||
|
Before detailling the content of the `Game` class, we first need to describe
|
||||||
|
some other classes in the same module.
|
||||||
|
|
||||||
|
## The `PositionPlayer` class
|
||||||
|
|
||||||
|
*Yes, this is a really poor name, but I had no imagination for that one...*
|
||||||
|
|
||||||
|
Basically, this class contains the things that the game needs to know about the
|
||||||
|
player:
|
||||||
|
- the `player` attribute is the one corresponding to the player described in
|
||||||
|
the [player section](/wiki/Player) (which serves the purpose of the AI)
|
||||||
|
- the `position` attribute is the current position of the player
|
||||||
|
- the `id` is a number (between 1 and 2) that allows the game to know which
|
||||||
|
player we are talking about
|
||||||
|
- the `alive` attribute is a boolean which allows the game to know if the
|
||||||
|
player is still alive so it can know if the game ended and which player
|
||||||
|
has won
|
||||||
|
|
||||||
|
It also contains two methods, `head` and `body` which returns the tile the
|
||||||
|
corresponding to the head or the body of the given player.
|
||||||
|
|
||||||
|
## The `HistoryElement` class
|
||||||
|
|
||||||
|
This class desribes the elements you will find in the game history. It contains
|
||||||
|
the directions that were given by the players, and the state of the map after
|
||||||
|
the turn.
|
||||||
|
|
||||||
|
## And finally, the `Game` class
|
||||||
|
|
||||||
|
This class contains:
|
||||||
|
- the width and height of the game, which are integers
|
||||||
|
- the history of the states of the game, which is an array of
|
||||||
|
`HistoryElement`s that we described in the previous section
|
||||||
|
- the `PositionPlayer`s of the game (in the `pps` attribute), which is an
|
||||||
|
array of `PositionPlayer` described previously
|
||||||
|
- the `winner` which is initialized to `None`, at the begining of the game,
|
||||||
|
but will be 1 or 2 if player 1 or 2 won the game
|
||||||
|
|
||||||
|
It also contains a few methods:
|
||||||
|
- `map`, which returns the current map of the game
|
||||||
|
- `next_frame` which advances one step in the game and updates the history
|
||||||
|
- `main_loop`, that runs the game until it's finished
|
|
@ -0,0 +1,121 @@
|
||||||
|
# The map module
|
||||||
|
|
||||||
|
The `map` module contains two important classes:
|
||||||
|
- the `Tile` class
|
||||||
|
- the `Map` class
|
||||||
|
|
||||||
|
## The `Tile` class
|
||||||
|
|
||||||
|
This class contains the different possible values for the tiles of the map.
|
||||||
|
Each value has its own meaning, and the names given are explicit enough.
|
||||||
|
|
||||||
|
This class also contains a method named `color` which return a 3-uple
|
||||||
|
containing the red, green and blue channels of the colors of the corresponding
|
||||||
|
tile. This is espeacially useful for rendering the game on a window.
|
||||||
|
|
||||||
|
## The `Map` class
|
||||||
|
|
||||||
|
`Map` is one of the most important class of this project. It is made to be easy
|
||||||
|
to manipulate with things like PyTorch.
|
||||||
|
|
||||||
|
It contains an `np.array` of two dimensions containing two rows and two columns
|
||||||
|
more that what specify, because is automatically adds border to your map. This
|
||||||
|
means that when you run
|
||||||
|
|
||||||
|
``` python
|
||||||
|
my_map = Map(5, 5, Tile.EMPTY, Tile.WALL)
|
||||||
|
```
|
||||||
|
|
||||||
|
it really creates a 7x7 `np.array`, in which the interior is filled with
|
||||||
|
`Tile.EMPTY` and the border with `Tile.WALL`.
|
||||||
|
|
||||||
|
### Playing with maps
|
||||||
|
|
||||||
|
If you want to modify the map, you can do so by using the getters. **Be
|
||||||
|
careful, the getter doesn't take the border into account**. You won't be able
|
||||||
|
to modify the border with the getters.
|
||||||
|
|
||||||
|
To use the getters, you can do like this:
|
||||||
|
|
||||||
|
``` python
|
||||||
|
my_map = Map(5, 5, Tile.EMPTY, Tile.WALL)
|
||||||
|
|
||||||
|
# Sets the top left corner of the map to PLAYER_ONE_HEAD
|
||||||
|
# This does not modify the border, it modifies the inner tile.
|
||||||
|
my_map[0, 0] = Tile.PLAYER_ONE_HEAD
|
||||||
|
```
|
||||||
|
|
||||||
|
You can easily clone a map:
|
||||||
|
``` python
|
||||||
|
my_map = Map(5, 5, Tile.EMPTY, Tile.WALL)
|
||||||
|
my_second_map = my_map.clone() # Clones the whole map
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to clone the array of a map and apply a function to it at the same
|
||||||
|
time, you can do like this:
|
||||||
|
``` python
|
||||||
|
def my_function(tile):
|
||||||
|
return tile.value + 1
|
||||||
|
|
||||||
|
my_map = Map(5, 5, Tile.EMPTY, Tile.WALL)
|
||||||
|
my_map.apply(my_function)
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need, you can use some more parameters and create lambdas, which might
|
||||||
|
give you a clearer code:
|
||||||
|
``` python
|
||||||
|
def my_function(tile, offset):
|
||||||
|
return tile.value + offset
|
||||||
|
|
||||||
|
my_map = Map(5, 5, Tile.EMPTY, Tile.WALL)
|
||||||
|
my_map.apply(lambda tile: my_function(tile, 1))
|
||||||
|
```
|
||||||
|
|
||||||
|
### Playing with maps' np-arrays
|
||||||
|
|
||||||
|
At any moment, you can get a reference to the `np.array` of the map by using
|
||||||
|
the `array` method:
|
||||||
|
|
||||||
|
``` python
|
||||||
|
my_map = Map(5, 5, Tile.EMPTY, Tile.WALL)
|
||||||
|
my_array = my_map.array()
|
||||||
|
my_array[0][0] = ... # Modifies the top left tile of the border of the map
|
||||||
|
```
|
||||||
|
|
||||||
|
<strong>
|
||||||
|
Be careful: when you get a reference to an np-array, you need to remember:
|
||||||
|
- the np-array does cantain the border, so the (i, j) tile of the map is the
|
||||||
|
(i + 1, j + 1) tile of the array
|
||||||
|
- since you get a reference to the np-array, modifying it will also modify
|
||||||
|
the map
|
||||||
|
</strong>
|
||||||
|
|
||||||
|
If you wan to modify an np-array without modifying the map, you can use the
|
||||||
|
`clone_array` method:
|
||||||
|
|
||||||
|
``` python
|
||||||
|
my_map = Map(5, 5, Tile.EMPTY, Tile.WALL)
|
||||||
|
my_array = my_map.clone_array()
|
||||||
|
# Modifying my_array won't modify my_map
|
||||||
|
my_array[0][0] = ...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Getting input for your neural network
|
||||||
|
First of all, it's important to realize that the map does not depend on who is
|
||||||
|
the player 1. When a player's `action` method is called, it is called with the
|
||||||
|
map as parameter, and also the id so that the player is able to know who he is
|
||||||
|
on the map.
|
||||||
|
|
||||||
|
You can easily convert the map to an input for your neural network by using the
|
||||||
|
`state_for_player` method. It takes the id of the player as a parameter and
|
||||||
|
returns a numpy array that will have:
|
||||||
|
- 1 where tiles are empty
|
||||||
|
- -1 where tiles are walls or bodies of snakes
|
||||||
|
- 10 for the head of the player with the corresponding id
|
||||||
|
- -10 for the head of the other player
|
||||||
|
|
||||||
|
``` python
|
||||||
|
my_map = Map(5, 5, Tile.EMPTY, Tile.WALL);
|
||||||
|
# ...
|
||||||
|
my_input = map.state_for_player(1)
|
||||||
|
```
|
|
@ -0,0 +1,43 @@
|
||||||
|
# The `Player` class
|
||||||
|
|
||||||
|
The only thing the `player` module contains is the `Player` class.
|
||||||
|
|
||||||
|
Your AI will basically be a class that will extend the `Player` class.
|
||||||
|
|
||||||
|
Since your AI is not interactive, the only method you have to implement is the
|
||||||
|
`action` method. This method takes as parameters `self`, of course, and also:
|
||||||
|
- `map`, which is the current map of the game
|
||||||
|
- `id` which is the id of your player (because the map refers to player 1 and
|
||||||
|
2 but you don't know if you're player one or two)
|
||||||
|
|
||||||
|
You can refer to the [game page of the wiki](/wiki/Game) to have more
|
||||||
|
information about this class.
|
||||||
|
|
||||||
|
This function will return a `Direction`. It is a python enum that you can use
|
||||||
|
directly, like in
|
||||||
|
|
||||||
|
```python
|
||||||
|
return Direction.UP
|
||||||
|
```
|
||||||
|
|
||||||
|
or with an integer, like in
|
||||||
|
|
||||||
|
```python
|
||||||
|
i = 2
|
||||||
|
return Direction(i) # returns Direction.RIGHT
|
||||||
|
```
|
||||||
|
|
||||||
|
Your class may then look like this:
|
||||||
|
|
||||||
|
``` python
|
||||||
|
class MyMonsterAI(Player):
|
||||||
|
def __init__(self):
|
||||||
|
super(MyMonsterAI, self).__init__()
|
||||||
|
|
||||||
|
def action(self, map, id):
|
||||||
|
# Analyse the map
|
||||||
|
# Do many computations
|
||||||
|
# Make a sacrifice to the gods of tron
|
||||||
|
# Return a direction
|
||||||
|
return Direction.UP
|
||||||
|
```
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Summary
|
||||||
|
|
||||||
|
[Pytron](./intro.md)
|
||||||
|
|
||||||
|
- [Getting started](./getting-started.md)
|
||||||
|
- [Writing an AI](./writing-an-ai.md)
|
||||||
|
- [Uploading your AI on pytron-web](./pytron-web.md)
|
Binary file not shown.
After Width: | Height: | Size: 5.4 KiB |
|
@ -0,0 +1,38 @@
|
||||||
|
# How to download the game
|
||||||
|
|
||||||
|
The first step is to clone the repository:
|
||||||
|
|
||||||
|
``` sh
|
||||||
|
git clone https://gitea.tforgione.fr/tforgione/pytron
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, ensure you have `python` and `pygame` installed. You can test you have
|
||||||
|
everything by executing the following command:
|
||||||
|
```
|
||||||
|
python -c "import pygame"
|
||||||
|
```
|
||||||
|
|
||||||
|
- if you get a `python: command not found`, it means you don't have
|
||||||
|
`python`, you can install it on ubuntu like so:
|
||||||
|
``` sh
|
||||||
|
sudo apt install python
|
||||||
|
```
|
||||||
|
|
||||||
|
- if you get a `ImportError: No module named 'pygame'`, it means you don't
|
||||||
|
have `pygame`, you can install it on ubuntu like so:
|
||||||
|
``` sh
|
||||||
|
sudo apt install python-pip
|
||||||
|
sudo pip3 install pygame
|
||||||
|
```
|
||||||
|
|
||||||
|
# How to play the game
|
||||||
|
|
||||||
|
There are two runnable scripts in the repository, which are mostly here to be
|
||||||
|
examples:
|
||||||
|
|
||||||
|
- `play.py` which shows how to create a game with a window and play against
|
||||||
|
an AI.
|
||||||
|
- `headless.py` which shows how to run a game with AIs without watching the
|
||||||
|
interface, and thus, really fast.
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
# Welcome to Pytron's wiki
|
||||||
|
|
||||||
|
This program is a simple python game, made for implementing your own AIs.
|
||||||
|
|
||||||
|
![Screenshot of the game](/assets/screenshot.png)
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
# About Pytron-web
|
||||||
|
|
||||||
|
[Pytron-web](http://pytron.tforgione.fr) is a website where you can upload your
|
||||||
|
AI and see it compete with other AIs from other people.
|
||||||
|
|
||||||
|
On the index of the site, you can see the leaderboard, as well as the results
|
||||||
|
of all battles.
|
||||||
|
|
||||||
|
Note that this website is hosted on an N7 server, so you won't be able to
|
||||||
|
access it if you're not on the N7 local network (you can still use it through
|
||||||
|
the VPN).
|
||||||
|
|
||||||
|
### Battle system
|
||||||
|
All the possible battles are done: each AI will have to battle each other AI.
|
||||||
|
For each pair of AIs, 100 battles will be launched, on a predetermined,
|
||||||
|
symmetric set of initial positions.
|
||||||
|
|
||||||
|
The size of the map for this is 10x10, so you need to make sure that your AI
|
||||||
|
will work with those sizes.
|
||||||
|
|
||||||
|
### How to upload your AI
|
||||||
|
There are two ways to upload your AI:
|
||||||
|
|
||||||
|
##### [On the website](https://pytron.tforgione.fr/upload/)
|
||||||
|
You will have to enter the name of your AI and upload a ZIP archive of your AI.
|
||||||
|
This archive must contain a file named `ai.py` which must contain a class `Ai`
|
||||||
|
which must have a constructor that takes no arguments. It may also contain some
|
||||||
|
other files, that will be placed next to the `ai.py` file on the server.
|
||||||
|
|
||||||
|
##### With the upload script
|
||||||
|
In the `pytron` repository, there is a script named `upload` which will allow
|
||||||
|
you to upload your AI.
|
||||||
|
|
||||||
|
It assumes that your AIs are stored in the `ais` directory, at the root of the
|
||||||
|
repository. Each AI must be in its own directory, and must contain a file named
|
||||||
|
`ai.py` which must contain a class `Ai` which must have a constructor that
|
||||||
|
takes no arguments.
|
||||||
|
|
||||||
|
This script is interactive, you can launch it by typing
|
||||||
|
``` bash
|
||||||
|
./upload
|
||||||
|
```
|
||||||
|
and it will look for AIs. If it doesn't find any AI, it will print an error
|
||||||
|
message, and if it finds many, it will ask you which one you want to upload.
|
||||||
|
It will then process to create the archive and upload it to the server itself.
|
|
@ -0,0 +1 @@
|
||||||
|
# Writing an AI
|
Loading…
Reference in New Issue