commit 529ef7b6a80bb6cd04181553f4c9a88fbba248fe Author: Thomas Forgione Date: Wed May 22 10:59:27 2019 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7585238 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +book diff --git a/book.toml b/book.toml new file mode 100644 index 0000000..809e250 --- /dev/null +++ b/book.toml @@ -0,0 +1,5 @@ +[book] +authors = ["Thomas Forgione"] +multilingual = false +src = "src" +title = "Pytron" diff --git a/src/Game.md b/src/Game.md new file mode 100644 index 0000000..481a128 --- /dev/null +++ b/src/Game.md @@ -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 diff --git a/src/Map.md b/src/Map.md new file mode 100644 index 0000000..e7eb1d6 --- /dev/null +++ b/src/Map.md @@ -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 +``` + + +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 + + +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) +``` diff --git a/src/Player.md b/src/Player.md new file mode 100644 index 0000000..f4f56b0 --- /dev/null +++ b/src/Player.md @@ -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 +``` diff --git a/src/SUMMARY.md b/src/SUMMARY.md new file mode 100644 index 0000000..9835fa2 --- /dev/null +++ b/src/SUMMARY.md @@ -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) diff --git a/src/assets/screenshot.png b/src/assets/screenshot.png new file mode 100644 index 0000000..2a0aaba Binary files /dev/null and b/src/assets/screenshot.png differ diff --git a/src/getting-started.md b/src/getting-started.md new file mode 100644 index 0000000..f14e44f --- /dev/null +++ b/src/getting-started.md @@ -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. + + diff --git a/src/intro.md b/src/intro.md new file mode 100644 index 0000000..83e5c74 --- /dev/null +++ b/src/intro.md @@ -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) + diff --git a/src/pytron-web.md b/src/pytron-web.md new file mode 100644 index 0000000..3dd6d0c --- /dev/null +++ b/src/pytron-web.md @@ -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. diff --git a/src/writing-an-ai.md b/src/writing-an-ai.md new file mode 100644 index 0000000..68a7e52 --- /dev/null +++ b/src/writing-an-ai.md @@ -0,0 +1 @@ +# Writing an AI