From 529ef7b6a80bb6cd04181553f4c9a88fbba248fe Mon Sep 17 00:00:00 2001 From: Thomas Forgione Date: Wed, 22 May 2019 10:59:27 +0200 Subject: [PATCH] Initial commit --- .gitignore | 1 + book.toml | 5 ++ src/Game.md | 51 ++++++++++++++++ src/Map.md | 121 ++++++++++++++++++++++++++++++++++++++ src/Player.md | 43 ++++++++++++++ src/SUMMARY.md | 7 +++ src/assets/screenshot.png | Bin 0 -> 5490 bytes src/getting-started.md | 38 ++++++++++++ src/intro.md | 6 ++ src/pytron-web.md | 45 ++++++++++++++ src/writing-an-ai.md | 1 + 11 files changed, 318 insertions(+) create mode 100644 .gitignore create mode 100644 book.toml create mode 100644 src/Game.md create mode 100644 src/Map.md create mode 100644 src/Player.md create mode 100644 src/SUMMARY.md create mode 100644 src/assets/screenshot.png create mode 100644 src/getting-started.md create mode 100644 src/intro.md create mode 100644 src/pytron-web.md create mode 100644 src/writing-an-ai.md 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 0000000000000000000000000000000000000000..2a0aaba704dbb490546a0ad53fba1e72791908e3 GIT binary patch literal 5490 zcmdUyXHb+)w#R1%kQ@XggG9+mkRV7F$pZ+=kTVQOj>8a!A?F}j!XQZ{M~9q)5 z{A1#Qe(mfF^jQJ`B)NWi2A;YWKFqG}F1FAYHq4$dR~u#6pgAO`?2L z8w~+5lT|S=tz>`d?(qO9TRbib(%YPp}cyY0f}*DlW?-)6H?dIHx0X}^*5^2OfHqbpN*1y?N> z@w;7;3j=>YvnQ{p%oJ@ndKZHQkB?KqLA=}xN8a#u!PeGDpAt%!!IrBjvt-fj=~F&v zxL#gJ&f%jGPv6B2{Z2drmq}x-e)6Xs1-^hYy{N{`bu!jNLsWl`EiQuvTYTWr z$yiP8uA0W5hE`%U*9K(Ku{?!`T26jEY>ShZXX|H&#(wMJ;_)|Lt_P=YR+>Q29c13Af@O;>Yb&ysXwM4to@L`0p;Wwu1zH}XE zt`>GETS#D3s;h^(MYRDboc#0y)1n)40c>3veOf-ig{$6>rfKI`{=9rOzLnD?Ee(4@ z#LuN}#Tn|k>X`{mFKSIj>k|K%W2kQx4>BvOEON|Sy=>e3D)LU1>+!rMUI!DTKl|e0 z#=@{7K&7cSbDR8Qjhi3OvjW_yAp`jHg{$HkeYM1zw^Ls>8vJ@uy#~+g{V!_xFH}rC zw&q^R``IlP=oc&r z0a#^qH1PRqpE&=Z>_KB+5F8?pavI ztt7jxV(6k*J#muh^jKF{?^)d>{N5FGGmhdQTI`tJv7pj2Q)pGJv}8b1TuyYPTc9-I zfx6~>xD?Fv8WyqWY+v5v$rIHkDzC0>mLqQ4{P6i)U$-}zWcWEl(?M)QEIr-}{U#uy zI4--QNkil<<9=y~9=Qc+!9->AwoJ)(bHg_WICUd$NhQfwV9xl=ciYMO9I~+$yZjVw-OjX_gYJ_?4`^!GkEHGZ;4WgD=etG`#$C^oWN8ZLetNaS z@t1dl=ijAV(T4A8o$2t<41U~n$MN7w(6UA?Wc0$)mS;rCY$B1)I8m+ z@mxn#zRr&JLy6n&dK5~?QARJd(Qg4$fjdzimDS(@2;_%Ou7Mb^`v<)2{=2>&;i0mn z7m~+&_BAcG?}V_0-QNonmU+M2QC3PRTL=v9%a?Im-vN@ciZ8O4nMTFy@|aYbeV^tYl?|r9QMQwF+hid$sn*spL3jlv<0|Nb{=C z;~Ex_e~M~W-)zR3%c-x^VmM+6%fEg<+5$J$zJ7GXB1j7_>;-+p^9uWN|+`B0nsLcXfa7b0%hA`=NqT z9J`WJfG$&ypYg6yI;OPid?djdUBP)Zcfti%tlH-Wo_nLs+8VV43G}M_@9{O|ibB1& zP+h^t{0j|6CK(=hn9R^)Qf&Y!%1?z1wykzZ1~^s_gH|x&MiZW$$P-(lM_EFg<7juX z?HCGCQvh1dICd5>iL0py+liV`n=dg07!9ZGj4gLUzPPbmoCTvOw5{OYr&~GrzCPy3 zwyVnFuAQI;?V9s*G=I7q?`IuC|=G3C71S^ zl7#6O{v6dlcl&!(J=w*6sY^E@T^+rbiLKPqFlZ>XA^~N z4#tb&z`CU9K}xASQ*XuE&WzZPl|Snr{?VhtKDHd&5dAus}t&^rIUB|3xx&&deLXlhNG7o(I~b4Mzwd5g_qIw)@Q# zh#~|=>?um^;2$-}IG%5D&xiIYG0sPFPrO-qCwglzAgx)=hiMiALz#>`U0b6-4DFf2ySfp!m_h_?t144L}vH)78 zQ6A7zx&!x3V>9#`Rl92uahp_9A!H?b9>tCy^4SfH-`1=3!K+JhP|1V7=^3eYQ38t$ ztkZ!nA!DCL&jq3^LpqKlVnzXYnw=E3gb^<@eG#|0+J9_D-XQ{0%1amGNdAn?RTjmh z8A~-drOG(r>n&`~wSZD%kW)XwzWa@MTFlpqcWPfx{4R+Qu6=Pqj9WkhFde^fVra5+ z9H~C2w4)wupJdUeQq#&sJ<{=5LeC!f7-{w`ur8H^sHBU88Bff$mbs4JG_FjTfX9#? zsxjL6#sF^aOJjA9V2)axE;Uz91Qo{|4~x2$w5n_&np37un>B&|Vd`nWApiREe{} zLIBpS@;*arlFM_R2U&FQ(B!GiGl@*nu~nzOZlA%5v4kAp^x~v#X{o(#+%g`1?0^CQCj7b^+9QYs)L_^v~w4Z2*eMMfpY|e3S0;*vlkw04|=6sJ`pG#hq zwRT+oB`+(we4n@xxa-p#X_<%tkl}R5%uWwLp#F-l$J6V9G>Gdxqhr4&)FXoa|Qcu$3g5x&;$Mi4`ym1$_}08@~?)kZ80ly!e47!E{+ z9CJ+-3!kHN4fNJO?Yr$=D zBOKbfl-!{8kL)u}!q`|!d^Q%8g+BT5RMg_uez28TfEw))Kz2gH|H69M(DQL`h5X@? z3X_FINwHKvD}fxsCKo%DN~9O%q!cUjBbdCdJFLoxG(8{ulR1#e!yGsJL0httK$Yus zGuh(8ogkJ-7EjzH<0@pyD%CKg;oICef?27i7eTKXp(5@t@X0>*yIkIM7nEj#prZlK z8=6|rCQl$7_kh6p2HP+ZB757*huk$T6vy!^cpOK~5is@sB3xvgkJsCnBlX>#!^WCj zuv`(HAE;e9qP4W*X~<(3`jqJ>L8gAzdfr=@eu@xb7i z!+LX4Lob$M?f&TX-k{oJW)5t_4BrkcpA0Ns?9KZ z)-)o4*;llHkhMdK)d-!N5xFh^V*k-vV5Cy#6NdMZMiOD*fQK(3JsIFMMZ#us;-btE zrO0?Z=wYOzWDK@x-n!f%!@>jXmc6>or?`{_z|`L9ySiId^+RZ8J133+Nbp=Zg z6PzzQZ9uu09!GqQEKb+MlaSR^!KG3Tl2&vcUc;6A4m^?O*@;UF4cWC)>0~!B4R;19 z)(u$rgwHE3+`d;h8H+N1&!UtbbMQaU2~2FvDa7sP-TLx1d|Mz3B^L(456(jeHN$zMKX>> zsjjJtK69ibsT>X{rb3T-_#}RK{rudS*WqK^!^nDveaOa*GmXs7>AKbR3itB+gtL$m z?U~nCD-J+?T~FefMjDu>t@Gl6UjxhrMuF#>lIMcW8V+{UDAY&{+4tDu7#o$x4w}Z) z>^9vw+4*$E7PkcNcc!hdZYpm1$p*bI3Kve;q9N;TxV%XbsvmcN!X!N04s&*6rEEW= z@{u%di&w`vaGOClpO6z*i{i^kr}erxf+eps>$5sMlI1%QSLn(HDH$C zLSR>8k@t|OkV*Z6QH27eRiRFa{u8}s8&MLA^jq*pejui`Se$>!{l5z#BruO!L9Pt$ zhH7rvQF-)%{u#udRB&*rLUC}U9e!*KjveA;SN|)tzxkTW1NkiEfeAGzgJD|2e_;A& zs9&`I0Q`#@7h!boUx(5$*7j}vdi?FX2Sl6i>tENALr74eb1`&XJqAB8YT93fg^E!9 zbHo1Z9v>HigOAHM7pK>ZSJox^w~nSxI@%X4+`rSp60H8Q{Ptn7gjMjr6U?vgcOpRs zT}b3cjv#?U~Fe`qIUn?o%_FID>R0*%>JF!&41kqiQkE`Lz*z!?f<6; qecDia=gAn4|1tZ&KO%pu=3%nNbN7G1>8VkFn5vJplqwX=k^cq4c(g(Q literal 0 HcmV?d00001 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