Merge branch 'config/hell'

This commit is contained in:
ajuvercr 2019-09-17 14:03:23 +02:00
commit a7e7218417
32 changed files with 2121 additions and 52 deletions

View file

@ -11,3 +11,6 @@ mozaic = { git = "https://github.com/ajuvercr/MOZAIC" }
tokio = "0.1.22" tokio = "0.1.22"
rand = { version = "0.6.5", default-features = true } rand = { version = "0.6.5", default-features = true }
futures = "0.1.28" futures = "0.1.28"
serde = "1.0.100"
serde_derive = "1.0.100"
serde_json = "1.0"

24
backend/game.json Normal file
View file

@ -0,0 +1,24 @@
{"planets":[{"ship_count":7,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":7,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":0,"ship_count":6,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":10}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":0,"ship_count":6,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":9},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":11},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":11}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":0,"ship_count":6,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":8},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":10},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":10},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":11},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":11}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":0,"ship_count":6,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":7},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":9},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":9},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":10},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":10},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":11},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":11}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":0,"ship_count":6,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":6},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":8},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":8},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":9},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":9},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":10},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":10},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":11},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":11}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":0,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":11},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":5},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":7},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":7},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":8},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":8},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":9},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":9},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":10},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":10},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":11}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":0,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":10},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":4},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":6},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":6},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":7},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":7},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":8},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":8},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":9},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":9},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":10},{"id":12,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":10}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":0,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":9},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":3},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":5},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":5},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":6},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":6},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":7},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":7},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":8},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":8},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":9},{"id":12,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":9},{"id":14,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":10}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":0,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":8},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":2},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":4},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":4},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":5},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":5},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":6},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":6},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":7},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":7},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":8},{"id":12,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":8},{"id":14,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":9},{"id":16,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":10}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":0,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":7},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":1},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":3},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":3},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":4},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":4},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":5},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":5},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":6},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":6},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":7},{"id":12,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":7},{"id":14,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":8},{"id":16,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":9},{"id":18,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":10}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":6},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":10},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":2},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":2},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":3},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":3},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":4},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":4},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":5},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":5},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":6},{"id":12,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":6},{"id":14,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":7},{"id":16,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":8},{"id":18,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":9},{"id":20,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":3,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":5},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":9},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":1},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":1},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":2},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":2},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":3},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":3},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":4},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":4},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":5},{"id":23,"ship_count":5,"origin":"duteros","destination":"protos","owner":2,"turns_remaining":5},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":5},{"id":14,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":6},{"id":16,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":7},{"id":18,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":8},{"id":20,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":11}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":4},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":8},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":11},{"id":24,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":1},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":1},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":2},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":2},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":3},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":3},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":4},{"id":23,"ship_count":5,"origin":"duteros","destination":"protos","owner":2,"turns_remaining":4},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":4},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":10},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":5},{"id":16,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":6},{"id":18,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":7},{"id":20,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":3},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":7},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":10},{"id":24,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":20,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":6},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":1},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":1},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":2},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":2},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":3},{"id":23,"ship_count":5,"origin":"duteros","destination":"protos","owner":2,"turns_remaining":3},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":3},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":9},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":4},{"id":18,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":5}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":2},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":6},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":9},{"id":24,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3},{"id":20,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":5},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":4},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":3},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":1},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":1},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":2},{"id":23,"ship_count":5,"origin":"duteros","destination":"protos","owner":2,"turns_remaining":2},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":2},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":8}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":1},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":5},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":8},{"id":24,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":7},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":4},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":3},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":2},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":1},{"id":23,"ship_count":5,"origin":"duteros","destination":"protos","owner":2,"turns_remaining":1},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":1}]}
{"planets":[{"ship_count":4,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":1},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":4},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":7},{"id":24,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":6},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":3},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":2}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":4,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":26,"ship_count":3,"origin":"protos","destination":"tritos","owner":2,"turns_remaining":10},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":3},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":6},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":1},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":5},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":2}]}
{"planets":[{"ship_count":3,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":3,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":26,"ship_count":3,"origin":"protos","destination":"tritos","owner":2,"turns_remaining":9},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":2},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":5},{"id":27,"ship_count":3,"origin":"duteros","destination":"tritos","owner":2,"turns_remaining":5},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":4},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":1}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":5,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":4,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":26,"ship_count":3,"origin":"protos","destination":"tritos","owner":2,"turns_remaining":8},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":1},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":4},{"id":27,"ship_count":3,"origin":"duteros","destination":"tritos","owner":2,"turns_remaining":4},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":3},{"id":28,"ship_count":2,"origin":"protos","destination":"tritos","owner":2,"turns_remaining":10}]}
{"planets":[{"ship_count":3,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":5,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":26,"ship_count":3,"origin":"protos","destination":"tritos","owner":2,"turns_remaining":7},{"id":29,"ship_count":4,"origin":"duteros","destination":"tritos","owner":2,"turns_remaining":5},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":3},{"id":27,"ship_count":3,"origin":"duteros","destination":"tritos","owner":2,"turns_remaining":3},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":2},{"id":28,"ship_count":2,"origin":"protos","destination":"tritos","owner":2,"turns_remaining":9}]}
{"planets":[{"ship_count":4,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":4,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":26,"ship_count":3,"origin":"protos","destination":"tritos","owner":2,"turns_remaining":6},{"id":29,"ship_count":4,"origin":"duteros","destination":"tritos","owner":2,"turns_remaining":4},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":2},{"id":27,"ship_count":3,"origin":"duteros","destination":"tritos","owner":2,"turns_remaining":2},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":1,"turns_remaining":1},{"id":28,"ship_count":2,"origin":"protos","destination":"tritos","owner":2,"turns_remaining":8},{"id":30,"ship_count":4,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":5}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":5,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":26,"ship_count":3,"origin":"protos","destination":"tritos","owner":2,"turns_remaining":5},{"id":29,"ship_count":4,"origin":"duteros","destination":"tritos","owner":2,"turns_remaining":3},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":2,"turns_remaining":1},{"id":27,"ship_count":3,"origin":"duteros","destination":"tritos","owner":2,"turns_remaining":1},{"id":31,"ship_count":3,"origin":"protos","destination":"tritos","owner":2,"turns_remaining":10},{"id":28,"ship_count":2,"origin":"protos","destination":"tritos","owner":2,"turns_remaining":7},{"id":30,"ship_count":4,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":4}]}

43
backend/hex.json Normal file
View file

@ -0,0 +1,43 @@
{
"planets": [
{
"name": "protos",
"x": -6,
"y": 0,
"owner": 1,
"ship_count": 6
},
{
"name": "duteros",
"x": -3,
"y": 5,
"ship_count": 6
},
{
"name": "tritos",
"x": 3,
"y": 5,
"ship_count": 6
},
{
"name": "tetartos",
"x": 6,
"y": 0,
"owner": 2,
"ship_count": 6
},
{
"name": "pemptos",
"x": 3,
"y": -5,
"ship_count": 6
},
{
"name": "extos",
"x": -3,
"y": -5,
"ship_count": 6
}
]
}

1
backend/map.json Normal file

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,7 @@
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate tokio; extern crate tokio;
extern crate futures; extern crate futures;
@ -11,32 +15,14 @@ use mozaic::errors;
use mozaic::modules::{Aggregator, Steplock, game}; use mozaic::modules::{Aggregator, Steplock, game};
mod planetwars;
// Load the config and start the game. // Load the config and start the game.
fn main() { fn main() {
run(env::args().collect()); run(env::args().collect());
} }
use std::str;
struct Server;
impl game::GameController for Server {
fn step<'a>(&mut self, turns: Vec<game::PlayerTurn<'a>>) -> Vec<game::Update> {
let mut out = Vec::new();
for (id, turn) in turns.iter() {
let postfix = match turn {
game::Turn::Action(bytes) => str::from_utf8(bytes).unwrap(),
game::Turn::Timeout => "Timed out",
};
let msg = format!("{}: {}", **id, postfix);
out.push(game::Update::Global(msg.as_bytes().to_vec()));
}
return out;
}
}
use mozaic::server::runtime::{Broker}; use mozaic::server::runtime::{Broker};
use rand::Rng; use rand::Rng;
use errors::Consumable; use errors::Consumable;
@ -55,14 +41,21 @@ pub fn run(args : Vec<String>) {
let number_of_clients = args.get(1).map(|x| x.parse().unwrap_or(1)).unwrap_or(1); let number_of_clients = args.get(1).map(|x| x.parse().unwrap_or(1)).unwrap_or(1);
let ids: HashMap<_, util::PlayerId> = (0..number_of_clients).map(|x| (x.into(), (10 - x).into())).collect(); let ids: HashMap<util::Identifier, util::PlayerId> = (0..number_of_clients).map(|x| (rand::thread_rng().gen::<u64>().into(), x.into())).collect();
println!("Ids: {:?}", ids); let config = planetwars::Config { map_file: String::from("hex.json"), max_turns: 500 };
let game = planetwars::PlanetWarsGame::new(config.create_game(number_of_clients as usize));
println!("Tokens:");
let keys: Vec<u64> = ids.keys().map(|&x| x.into()).collect();
for key in keys {
println!("key {}", key);
}
tokio::run(futures::lazy(move || { tokio::run(futures::lazy(move || {
let mut broker = Broker::new().unwrap(); let mut broker = Broker::new().unwrap();
broker.spawn(welcomer_id.clone(), game::GameReactor::params(steplock_id.clone(), Box::new(Server)), "Main").display(); broker.spawn(welcomer_id.clone(), game::GameReactor::params(steplock_id.clone(), Box::new(game)), "Main").display();
broker.spawn(steplock_id.clone(), Steplock::new(broker.clone(), ids.values().cloned().collect(), welcomer_id.clone(), aggregator_id.clone()).with_timeout(5000).params(), "Steplock").display(); broker.spawn(steplock_id.clone(), Steplock::new(broker.clone(), ids.values().cloned().collect(), welcomer_id.clone(), aggregator_id.clone()).with_timeout(5000).params(), "Steplock").display();
broker.spawn(aggregator_id.clone(), Aggregator::params(manager_id.clone(), steplock_id.clone()), "Aggregator").display(); broker.spawn(aggregator_id.clone(), Aggregator::params(manager_id.clone(), steplock_id.clone()), "Aggregator").display();
broker.spawn( broker.spawn(

View file

@ -0,0 +1,145 @@
use mozaic::modules::game;
use serde_json;
use std::collections::HashMap;
use std::convert::TryInto;
use std::fs::File;
use std::io::Write;
mod pw_config;
mod pw_serializer;
mod pw_rules;
mod pw_protocol;
use pw_protocol::{ self as proto, CommandError };
use pw_rules::Dispatch;
pub use pw_config::Config;
pub struct PlanetWarsGame {
state: pw_rules::PlanetWars,
planet_map: HashMap<String, usize>,
log_file: File
}
impl PlanetWarsGame {
pub fn new(state: pw_rules::PlanetWars) -> Self {
let planet_map = state.planets.iter().map(|p| (p.name.clone(), p.id)).collect();
let file = File::create("game.json").unwrap();
Self {
state, planet_map,
log_file: file,
}
}
fn dispatch_state(&mut self, were_alive: Vec<usize>, updates: &mut Vec<game::Update>, ) {
let state = pw_serializer::serialize(&self.state);
write!(self.log_file, "{}\n", serde_json::to_string(&state).unwrap()).unwrap();
// println!("{}", serde_json::to_string(&state).unwrap());
for player in self.state.players.iter().filter(|p| were_alive.contains(&p.id)) {
let state = pw_serializer::serialize_rotated(&self.state, player.id);
let state = if player.alive && !self.state.is_finished() {
proto::ServerMessage::GameState(state)
} else {
proto::ServerMessage::FinalState(state)
};
updates.push(
game::Update::Player((player.id as u64).into(), serde_json::to_vec(&state).unwrap())
);
if !player.alive || self.state.is_finished() {
updates.push(game::Update::Kick((player.id as u64).into()));
}
}
}
fn execute_commands<'a>(&mut self, turns: Vec<game::PlayerTurn<'a>>, updates: &mut Vec<game::Update>) {
for (player_id, command) in turns.into_iter() {
let player_num: usize = (*player_id).try_into().unwrap();
let action = proto::ServerMessage::PlayerAction(self.execute_action(player_num, command));
let serialized_action = serde_json::to_vec(&action).unwrap();
updates.push(game::Update::Player(player_id, serialized_action));
}
}
fn execute_action<'a>(&mut self, player_num: usize, turn: game::Turn<'a>) -> proto::PlayerAction {
let turn = match turn {
game::Turn::Timeout => return proto::PlayerAction::Timeout,
game::Turn::Action(bytes) => bytes,
};
let action: proto::Action = match serde_json::from_slice(&turn) {
Err(err) => return proto::PlayerAction::ParseError(err.to_string()),
Ok(action) => action,
};
let commands = action.commands.into_iter().map(|command| {
match self.check_valid_command(player_num, &command) {
Ok(dispatch) => {
self.state.dispatch(&dispatch);
proto::PlayerCommand {
command,
error: None,
}
},
Err(error) => {
proto::PlayerCommand {
command,
error: Some(error),
}
}
}
}).collect();
return proto::PlayerAction::Commands(commands);
}
fn check_valid_command(&self, player_num: usize, mv: &proto::Command) -> Result<Dispatch, CommandError> {
let origin_id = *self.planet_map
.get(&mv.origin)
.ok_or(CommandError::OriginDoesNotExist)?;
let target_id = *self.planet_map
.get(&mv.destination)
.ok_or(CommandError::DestinationDoesNotExist)?;
if self.state.planets[origin_id].owner() != Some(player_num) {
return Err(CommandError::OriginNotOwned);
}
if self.state.planets[origin_id].ship_count() < mv.ship_count {
return Err(CommandError::NotEnoughShips);
}
if mv.ship_count == 0 {
return Err(CommandError::ZeroShipMove);
}
Ok(Dispatch {
origin: origin_id,
target: target_id,
ship_count: mv.ship_count,
})
}
}
impl game::GameController for PlanetWarsGame {
fn step<'a>(&mut self, turns: Vec<game::PlayerTurn<'a>>) -> Vec<game::Update> {
let mut updates = Vec::new();
let alive = self.state.living_players();
self.state.repopulate();
self.execute_commands(turns, &mut updates);
self.state.step();
self.dispatch_state(alive, &mut updates);
updates
}
}

View file

@ -0,0 +1,80 @@
use std::fs::File;
use std::io::Read;
use std::io;
use serde_json;
use super::pw_protocol as proto;
use super::pw_rules::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Config {
pub map_file: String,
pub max_turns: u64,
}
impl Config {
pub fn create_game(&self, clients: usize) -> PlanetWars {
let planets = self.load_map(clients);
let players = (0..clients)
.map(|client_id| Player { id: client_id, alive: true })
.collect();
PlanetWars {
players: players,
planets: planets,
expeditions: Vec::new(),
expedition_num: 0,
turn_num: 0,
max_turns: self.max_turns,
}
}
fn load_map(&self, num_players: usize) -> Vec<Planet> {
let map = self.read_map().expect("[PLANET_WARS] reading map failed");
return map.planets
.into_iter()
.enumerate()
.map(|(num, planet)| {
let mut fleets = Vec::new();
let owner = planet.owner.and_then(|owner_num| {
// in the current map format, player numbers start at 1.
// TODO: we might want to change this.
let player_num = owner_num - 1;
// ignore players that are not in the game
if player_num < num_players {
Some(player_num)
} else {
None
}
});
if planet.ship_count > 0 {
fleets.push(Fleet {
owner: owner,
ship_count: planet.ship_count,
});
}
return Planet {
id: num,
name: planet.name,
x: planet.x,
y: planet.y,
fleets: fleets,
};
}).collect();
}
fn read_map(&self) -> io::Result<Map> {
let mut file = File::open(&self.map_file)?;
let mut buf = String::new();
file.read_to_string(&mut buf)?;
let map = serde_json::from_str(&buf)?;
return Ok(map);
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Map {
pub planets: Vec<proto::Planet>,
}

View file

@ -0,0 +1,79 @@
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Expedition {
pub id: u64,
pub ship_count: u64,
pub origin: String,
pub destination: String,
pub owner: usize,
pub turns_remaining: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Planet {
pub ship_count: u64,
pub x: f64,
pub y: f64,
pub owner: Option<usize>,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Action {
#[serde(rename = "moves")]
pub commands: Vec<Command>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Command {
pub origin: String,
pub destination: String,
pub ship_count: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct State {
pub planets: Vec<Planet>,
pub expeditions: Vec<Expedition>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GameInfo {
pub players: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum CommandError {
NotEnoughShips,
OriginNotOwned,
ZeroShipMove,
OriginDoesNotExist,
DestinationDoesNotExist,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlayerCommand {
pub command: Command,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<CommandError>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type", content = "value")]
pub enum PlayerAction {
Timeout,
ParseError(String),
Commands(Vec<PlayerCommand>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type", content = "content")]
pub enum ServerMessage {
/// Game state in current turn
GameState(State),
/// The action that was performed
PlayerAction(PlayerAction),
/// The game is over, and this is the concluding state.
FinalState(State),
}

View file

@ -0,0 +1,199 @@
/// The planet wars game rules.
pub struct PlanetWars {
pub players: Vec<Player>,
pub planets: Vec<Planet>,
pub expeditions: Vec<Expedition>,
// How many expeditions were already dispatched.
// This is needed for assigning expedition identifiers.
pub expedition_num: u64,
pub turn_num: u64,
pub max_turns: u64,
}
#[derive(Debug)]
pub struct Player {
pub id: usize,
pub alive: bool,
}
#[derive(Debug)]
pub struct Fleet {
pub owner: Option<usize>,
pub ship_count: u64,
}
#[derive(Debug)]
pub struct Planet {
pub id: usize,
pub name: String,
pub fleets: Vec<Fleet>,
pub x: f64,
pub y: f64,
}
#[derive(Debug)]
pub struct Expedition {
pub id: u64,
pub origin: usize,
pub target: usize,
pub fleet: Fleet,
pub turns_remaining: u64,
}
#[derive(Debug)]
pub struct Dispatch {
pub origin: usize,
pub target: usize,
pub ship_count: u64,
}
impl PlanetWars {
pub fn dispatch(&mut self, dispatch: &Dispatch) {
let distance = self.planets[dispatch.origin].distance(
&self.planets[dispatch.target]
);
let origin = &mut self.planets[dispatch.origin];
origin.fleets[0].ship_count -= dispatch.ship_count;
let expedition = Expedition {
id: self.expedition_num,
origin: dispatch.origin,
target: dispatch.target,
turns_remaining: distance,
fleet: Fleet {
owner: origin.owner(),
ship_count: dispatch.ship_count,
},
};
// increment counter
self.expedition_num += 1;
self.expeditions.push(expedition);
}
// Play one step of the game
pub fn step(&mut self) {
self.turn_num += 1;
// Initially mark all players dead, re-marking them as alive once we
// encounter a sign of life.
for player in self.players.iter_mut() {
player.alive = false;
}
self.step_expeditions();
self.resolve_combat();
}
pub fn repopulate(&mut self) {
for planet in self.planets.iter_mut() {
if planet.owner().is_some() {
planet.fleets[0].ship_count += 1;
}
}
}
fn step_expeditions(&mut self) {
let mut i = 0;
let exps = &mut self.expeditions;
while i < exps.len() {
// compare with 1 to avoid issues with planet distance 0
if exps[i].turns_remaining <= 1 {
// remove expedition from expeditions, and add to fleet
let exp = exps.swap_remove(i);
let planet = &mut self.planets[exp.target];
planet.orbit(exp.fleet);
} else {
exps[i].turns_remaining -= 1;
if let Some(owner_num) = exps[i].fleet.owner {
// owner has an expedition in progress; this is a sign of life.
self.players[owner_num].alive = true;
}
// proceed to next expedition
i += 1;
}
}
}
fn resolve_combat(&mut self) {
for planet in self.planets.iter_mut() {
planet.resolve_combat();
if let Some(owner_num) = planet.owner() {
// owner owns a planet; this is a sign of life.
self.players[owner_num].alive = true;
}
}
}
pub fn is_finished(&self) -> bool {
let remaining = self.players.iter().filter(|p| p.alive).count();
return remaining < 2 || self.turn_num >= self.max_turns;
}
pub fn living_players(&self) -> Vec<usize> {
self.players.iter().filter_map(|p| {
if p.alive {
Some(p.id)
} else {
None
}
}).collect()
}
}
impl Planet {
pub fn owner(&self) -> Option<usize> {
self.fleets.first().and_then(|f| f.owner)
}
pub fn ship_count(&self) -> u64 {
self.fleets.first().map_or(0, |f| f.ship_count)
}
/// Make a fleet orbit this planet.
fn orbit(&mut self, fleet: Fleet) {
// If owner already has a fleet present, merge
for other in self.fleets.iter_mut() {
if other.owner == fleet.owner {
other.ship_count += fleet.ship_count;
return;
}
}
// else, add fleet to fleets list
self.fleets.push(fleet);
}
fn resolve_combat(&mut self) {
// The player owning the largest fleet present will win the combat.
// Here, we resolve how many ships he will have left.
// note: in the current implementation, we could resolve by doing
// winner.ship_count -= second_largest.ship_count, but this does not
// allow for simple customizations (such as changing combat balance).
self.fleets.sort_by(|a, b| a.ship_count.cmp(&b.ship_count).reverse());
while self.fleets.len() > 1 {
let fleet = self.fleets.pop().unwrap();
// destroy some ships
for other in self.fleets.iter_mut() {
other.ship_count -= fleet.ship_count;
}
// remove dead fleets
while self.fleets.last().map(|f| f.ship_count) == Some(0) {
self.fleets.pop();
}
}
}
fn distance(&self, other: &Planet) -> u64 {
let dx = self.x - other.x;
let dy = self.y - other.y;
return (dx.powi(2) + dy.powi(2)).sqrt().ceil() as u64;
}
}

View file

@ -0,0 +1,74 @@
use super::pw_rules::{PlanetWars, Planet, Expedition};
use super::pw_protocol as proto;
/// Serialize given gamestate
pub fn serialize(state: &PlanetWars) -> proto::State {
serialize_rotated(state, 0)
}
/// Serialize given gamestate with player numbers rotated by given offset.
pub fn serialize_rotated(state: &PlanetWars, offset: usize) -> proto::State {
let serializer = Serializer::new(state, offset);
serializer.serialize_state()
}
struct Serializer<'a> {
state: &'a PlanetWars,
player_num_offset: usize,
}
impl<'a> Serializer<'a> {
fn new(state: &'a PlanetWars, offset: usize) -> Self {
Serializer {
state: state,
player_num_offset: offset,
}
}
fn serialize_state(&self) -> proto::State {
proto::State {
planets: self.state
.planets
.iter()
.map(|planet| self.serialize_planet(planet))
.collect(),
expeditions: self.state
.expeditions
.iter()
.map(|exp| self.serialize_expedition(exp))
.collect(),
}
}
/// Gets the player number for given player id.
/// Player numbers are 1-based (as opposed to player ids), They will also be
/// rotated based on the number offset for this serializer.
fn player_num(&self, player_id: usize) -> usize {
let num_players = self.state.players.len();
let rotated_id = (player_id + self.player_num_offset) % num_players;
// protocol player ids start at 1
return rotated_id + 1;
}
fn serialize_planet(&self, planet: &Planet) -> proto::Planet {
proto::Planet {
name: planet.name.clone(),
x: planet.x,
y: planet.y,
owner: planet.owner().map(|id| self.player_num(id)),
ship_count: planet.ship_count(),
}
}
fn serialize_expedition(&self, exp: &Expedition) -> proto::Expedition {
proto::Expedition {
id: exp.id,
owner: self.player_num(exp.fleet.owner.unwrap()),
ship_count: exp.fleet.ship_count,
origin: self.state.planets[exp.origin as usize].name.clone(),
destination: self.state.planets[exp.target as usize].name.clone(),
turns_remaining: exp.turns_remaining,
}
}
}

View file

@ -0,0 +1,82 @@
{"players":["bot_auto_player_full_rand","adversary_auto_player_orfirst_rand"]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[]}
{"planets":[{"ship_count":3,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":3,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":0,"ship_count":3,"origin":"protos","destination":"extos","owner":1,"turns_remaining":5},{"id":1,"ship_count":3,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":5}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":0,"ship_count":3,"origin":"protos","destination":"extos","owner":1,"turns_remaining":4},{"id":1,"ship_count":3,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":4}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":0,"ship_count":3,"origin":"protos","destination":"extos","owner":1,"turns_remaining":3},{"id":1,"ship_count":3,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":2,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":5},{"id":3,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":0,"ship_count":3,"origin":"protos","destination":"extos","owner":1,"turns_remaining":2},{"id":1,"ship_count":3,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":2,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":4},{"id":3,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":4,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":5},{"id":5,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":0,"ship_count":3,"origin":"protos","destination":"extos","owner":1,"turns_remaining":1},{"id":1,"ship_count":3,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":2,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":3},{"id":3,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3},{"id":4,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":4},{"id":5,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":6,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":5}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":3,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":1,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":7,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":5},{"id":6,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":4},{"id":2,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":2},{"id":3,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":4,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":3},{"id":5,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":4,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":7,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":4},{"id":6,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":3},{"id":2,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":1},{"id":3,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":4,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":2},{"id":5,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":8,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":0,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":0,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":3,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":7,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":3},{"id":6,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":2},{"id":10,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":9,"ship_count":4,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":10},{"id":4,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":1},{"id":5,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":8,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4}]}
{"planets":[{"ship_count":3,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":1,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":1,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":7,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":2},{"id":6,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":1},{"id":10,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":9,"ship_count":4,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":9},{"id":12,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":11,"ship_count":3,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":5},{"id":8,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":3,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":7,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":1},{"id":14,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":5},{"id":10,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":9,"ship_count":4,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":8},{"id":12,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":11,"ship_count":3,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":4},{"id":8,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":13,"ship_count":3,"origin":"protos","destination":"extos","owner":1,"turns_remaining":5}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":3,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":3,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":16,"ship_count":3,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":14,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":4},{"id":10,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":9,"ship_count":4,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":7},{"id":12,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":11,"ship_count":3,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":3},{"id":8,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":13,"ship_count":3,"origin":"protos","destination":"extos","owner":1,"turns_remaining":4},{"id":15,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":5}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":5,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":4,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":3,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":16,"ship_count":3,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":14,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":10,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1},{"id":9,"ship_count":4,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":6},{"id":12,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":11,"ship_count":3,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":2},{"id":18,"ship_count":2,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":5},{"id":13,"ship_count":3,"origin":"protos","destination":"extos","owner":1,"turns_remaining":3},{"id":15,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":4},{"id":17,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":5}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":1,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":3,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":1,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":4,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":16,"ship_count":3,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":14,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":20,"ship_count":4,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":5},{"id":9,"ship_count":4,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":5},{"id":12,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1},{"id":11,"ship_count":3,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":1},{"id":18,"ship_count":2,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":4},{"id":13,"ship_count":3,"origin":"protos","destination":"extos","owner":1,"turns_remaining":2},{"id":15,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":17,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":4},{"id":19,"ship_count":5,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":5}]}
{"planets":[{"ship_count":3,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":1,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":16,"ship_count":3,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":14,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":20,"ship_count":4,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":4},{"id":9,"ship_count":4,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":4},{"id":22,"ship_count":3,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":5},{"id":21,"ship_count":1,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":5},{"id":18,"ship_count":2,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":3},{"id":13,"ship_count":3,"origin":"protos","destination":"extos","owner":1,"turns_remaining":1},{"id":15,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":17,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":3},{"id":19,"ship_count":5,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":4}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":3,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":4,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":16,"ship_count":3,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1},{"id":24,"ship_count":3,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5},{"id":20,"ship_count":4,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":3},{"id":9,"ship_count":4,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":3},{"id":22,"ship_count":3,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":4},{"id":21,"ship_count":1,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":4},{"id":18,"ship_count":2,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":2},{"id":23,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":5},{"id":15,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":17,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":2},{"id":19,"ship_count":5,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":3}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":4,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":3,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":7,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":26,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5},{"id":24,"ship_count":3,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":20,"ship_count":4,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":2},{"id":9,"ship_count":4,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":2},{"id":22,"ship_count":3,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":3},{"id":21,"ship_count":1,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":3},{"id":18,"ship_count":2,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":1},{"id":23,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":4},{"id":25,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":10},{"id":17,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":1},{"id":19,"ship_count":5,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":2}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":4,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":5,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":1,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":4,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":26,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":24,"ship_count":3,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3},{"id":20,"ship_count":4,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":1},{"id":9,"ship_count":4,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":1},{"id":22,"ship_count":3,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":2},{"id":21,"ship_count":1,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":2},{"id":28,"ship_count":3,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":5},{"id":23,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":3},{"id":25,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":9},{"id":27,"ship_count":4,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":19,"ship_count":5,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":1}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":1,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":26,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3},{"id":24,"ship_count":3,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":30,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":5},{"id":29,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":5},{"id":22,"ship_count":3,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":1},{"id":21,"ship_count":1,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":1},{"id":28,"ship_count":3,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":4},{"id":23,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":2},{"id":25,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":8},{"id":27,"ship_count":4,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":4,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":3,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":26,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":24,"ship_count":3,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":30,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":4},{"id":29,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":4},{"id":32,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":5},{"id":31,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":28,"ship_count":3,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":3},{"id":23,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":1},{"id":25,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":7},{"id":27,"ship_count":4,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":4,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":26,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":34,"ship_count":4,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":5},{"id":30,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":3},{"id":29,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":32,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":4},{"id":31,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":28,"ship_count":3,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":2},{"id":33,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":5},{"id":25,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":6},{"id":27,"ship_count":4,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":1,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":3,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":5,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":3,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":36,"ship_count":6,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":5},{"id":34,"ship_count":4,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":4},{"id":30,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":2},{"id":29,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":32,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":31,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":28,"ship_count":3,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":1},{"id":33,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":4},{"id":25,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":5},{"id":27,"ship_count":4,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1},{"id":35,"ship_count":2,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":5}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":36,"ship_count":6,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":4},{"id":34,"ship_count":4,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":3},{"id":30,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":1},{"id":29,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":32,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":31,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":38,"ship_count":3,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":5},{"id":33,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":3},{"id":25,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":4},{"id":37,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5},{"id":35,"ship_count":2,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":4}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":4,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":3,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":5,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":4,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":36,"ship_count":6,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":3},{"id":34,"ship_count":4,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":2},{"id":40,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5},{"id":39,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":5},{"id":32,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":31,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1},{"id":38,"ship_count":3,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":4},{"id":33,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":2},{"id":25,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":3},{"id":37,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":35,"ship_count":2,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":3}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":5,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":4,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":1,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":36,"ship_count":6,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":2},{"id":34,"ship_count":4,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":1},{"id":40,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":39,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":4},{"id":42,"ship_count":4,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":41,"ship_count":5,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":5},{"id":38,"ship_count":3,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":3},{"id":33,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":1},{"id":25,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":2},{"id":37,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3},{"id":35,"ship_count":2,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":2}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":5,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":4,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":36,"ship_count":6,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":1},{"id":44,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":5},{"id":40,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3},{"id":39,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":3},{"id":42,"ship_count":4,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":41,"ship_count":5,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":4},{"id":38,"ship_count":3,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":2},{"id":43,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":5},{"id":25,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":2,"turns_remaining":1},{"id":37,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":35,"ship_count":2,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":1}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":0,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":0,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":3,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":46,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":5},{"id":44,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":4},{"id":40,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":39,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":2},{"id":42,"ship_count":4,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":41,"ship_count":5,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":3},{"id":38,"ship_count":3,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":1},{"id":43,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":4},{"id":45,"ship_count":3,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":37,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":3,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":4,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":3,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":46,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":4},{"id":44,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":3},{"id":40,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":39,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":1},{"id":42,"ship_count":4,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":41,"ship_count":5,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":2},{"id":48,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":5},{"id":43,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":45,"ship_count":3,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":47,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":5}]}
{"planets":[{"ship_count":3,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":4,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":5,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":4,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":46,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":44,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":2},{"id":49,"ship_count":3,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":5},{"id":47,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":4},{"id":42,"ship_count":4,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1},{"id":41,"ship_count":5,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":1},{"id":48,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":4},{"id":43,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":45,"ship_count":3,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3}]}
{"planets":[{"ship_count":4,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":1,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":3,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":0,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":46,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":44,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":1},{"id":49,"ship_count":3,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":4},{"id":47,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":51,"ship_count":2,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":5},{"id":50,"ship_count":4,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":5},{"id":48,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":3},{"id":43,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":45,"ship_count":3,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":5,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":46,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":53,"ship_count":4,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5},{"id":49,"ship_count":3,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":3},{"id":47,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":51,"ship_count":2,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":4},{"id":50,"ship_count":4,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":4},{"id":48,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":2},{"id":52,"ship_count":3,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":5},{"id":45,"ship_count":3,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":3,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":0,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":3,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":54,"ship_count":4,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":5},{"id":53,"ship_count":4,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":49,"ship_count":3,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":2},{"id":47,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":51,"ship_count":2,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":3},{"id":50,"ship_count":4,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":3},{"id":48,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":1},{"id":52,"ship_count":3,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":4}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":4,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":4,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":1,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":5,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":54,"ship_count":4,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":4},{"id":53,"ship_count":4,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3},{"id":49,"ship_count":3,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":1},{"id":56,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":5},{"id":51,"ship_count":2,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":2},{"id":50,"ship_count":4,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":2},{"id":55,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":5},{"id":52,"ship_count":3,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":3}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":5,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":1,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":54,"ship_count":4,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":3},{"id":53,"ship_count":4,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":58,"ship_count":5,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":56,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":4},{"id":51,"ship_count":2,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":1},{"id":50,"ship_count":4,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":1},{"id":55,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":4},{"id":52,"ship_count":3,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":2},{"id":57,"ship_count":1,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":5}]}
{"planets":[{"ship_count":3,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":54,"ship_count":4,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":2},{"id":53,"ship_count":4,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":58,"ship_count":5,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":56,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":3},{"id":59,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":5},{"id":57,"ship_count":1,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":4},{"id":55,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":3},{"id":52,"ship_count":3,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":1}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":3,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":3,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":54,"ship_count":4,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":1},{"id":61,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":10},{"id":58,"ship_count":5,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":56,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":2},{"id":59,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":4},{"id":57,"ship_count":1,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":3},{"id":55,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":2},{"id":60,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":5}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":1,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":4,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":0,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":63,"ship_count":5,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":5},{"id":61,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":9},{"id":58,"ship_count":5,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":56,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":1},{"id":59,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":57,"ship_count":1,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":2},{"id":55,"ship_count":2,"origin":"protos","destination":"extos","owner":1,"turns_remaining":1},{"id":60,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":4},{"id":62,"ship_count":6,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":5}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":4,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":5,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":63,"ship_count":5,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":4},{"id":61,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":8},{"id":58,"ship_count":5,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1},{"id":65,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5},{"id":59,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":57,"ship_count":1,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":1},{"id":64,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":5},{"id":60,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":62,"ship_count":6,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":4}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":4,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":5,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":1,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":63,"ship_count":5,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":3},{"id":61,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":7},{"id":67,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":5},{"id":65,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":59,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":66,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5},{"id":64,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":4},{"id":60,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":62,"ship_count":6,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":3}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":5,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":4,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":63,"ship_count":5,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":2},{"id":61,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":6},{"id":67,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":4},{"id":65,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3},{"id":69,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":5},{"id":66,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":64,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":3},{"id":60,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":62,"ship_count":6,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":2},{"id":68,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5}]}
{"planets":[{"ship_count":3,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":7,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":63,"ship_count":5,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":1},{"id":61,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":67,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":3},{"id":65,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":69,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":4},{"id":66,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3},{"id":64,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":2},{"id":71,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":5},{"id":62,"ship_count":6,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":1},{"id":68,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":70,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5}]}
{"planets":[{"ship_count":4,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":3,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":1,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":73,"ship_count":6,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":5},{"id":61,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":67,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":2},{"id":65,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":69,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":66,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":64,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":1},{"id":71,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":4},{"id":72,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":68,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":70,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":4,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":3,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":3,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":73,"ship_count":6,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":4},{"id":61,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":67,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":1},{"id":75,"ship_count":4,"origin":"protos","destination":"extos","owner":1,"turns_remaining":5},{"id":69,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":66,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":74,"ship_count":3,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":5},{"id":71,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":72,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":68,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":70,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":5,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":4,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":3,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":3,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":73,"ship_count":6,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":3},{"id":61,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":76,"ship_count":4,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":5},{"id":75,"ship_count":4,"origin":"protos","destination":"extos","owner":1,"turns_remaining":4},{"id":69,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":70,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":74,"ship_count":3,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":4},{"id":71,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":72,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":68,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":1,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":5,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":3,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":4,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":73,"ship_count":6,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":2},{"id":61,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":1},{"id":76,"ship_count":4,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":4},{"id":75,"ship_count":4,"origin":"protos","destination":"extos","owner":1,"turns_remaining":3},{"id":78,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5},{"id":70,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1},{"id":74,"ship_count":3,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":3},{"id":71,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":72,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":77,"ship_count":5,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":5}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":4,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":1,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":5,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":73,"ship_count":6,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":1},{"id":80,"ship_count":5,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":5},{"id":76,"ship_count":4,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":3},{"id":75,"ship_count":4,"origin":"protos","destination":"extos","owner":1,"turns_remaining":2},{"id":78,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":79,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":5},{"id":74,"ship_count":3,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":2},{"id":77,"ship_count":5,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":4},{"id":72,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":5,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":3,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":1,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":0,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":81,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":5},{"id":80,"ship_count":5,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":4},{"id":76,"ship_count":4,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":2},{"id":75,"ship_count":4,"origin":"protos","destination":"extos","owner":1,"turns_remaining":1},{"id":78,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3},{"id":79,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":4},{"id":74,"ship_count":3,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":1},{"id":77,"ship_count":5,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":3}]}
{"planets":[{"ship_count":3,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":4,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":4,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":81,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":4},{"id":80,"ship_count":5,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":3},{"id":76,"ship_count":4,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":1},{"id":82,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":5},{"id":78,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":79,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":3},{"id":77,"ship_count":5,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":2}]}
{"planets":[{"ship_count":4,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":4,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":1,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":1,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":81,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":3},{"id":80,"ship_count":5,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":2},{"id":84,"ship_count":4,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":82,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":4},{"id":78,"ship_count":2,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":79,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":2},{"id":77,"ship_count":5,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":1},{"id":83,"ship_count":2,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":5}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":81,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":2},{"id":80,"ship_count":5,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":1},{"id":84,"ship_count":4,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":82,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":86,"ship_count":4,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":10},{"id":79,"ship_count":1,"origin":"protos","destination":"duteros","owner":1,"turns_remaining":1},{"id":85,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":5},{"id":83,"ship_count":2,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":4}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":4,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":8,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":3,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":81,"ship_count":1,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":1},{"id":87,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":84,"ship_count":4,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":82,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":86,"ship_count":4,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":9},{"id":83,"ship_count":2,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":3},{"id":85,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":4}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":5,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":3,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":4,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":89,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":5},{"id":87,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":84,"ship_count":4,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":82,"ship_count":1,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":86,"ship_count":4,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":8},{"id":83,"ship_count":2,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":2},{"id":85,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":88,"ship_count":8,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":5}]}
{"planets":[{"ship_count":3,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":3,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":89,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":4},{"id":87,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":84,"ship_count":4,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1},{"id":91,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":86,"ship_count":4,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":7},{"id":83,"ship_count":2,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":1},{"id":85,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":88,"ship_count":8,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":4},{"id":90,"ship_count":3,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":5}]}
{"planets":[{"ship_count":4,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":1,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":3,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":89,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":3},{"id":87,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":93,"ship_count":3,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":5},{"id":91,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":86,"ship_count":4,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":6},{"id":92,"ship_count":6,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":5},{"id":85,"ship_count":2,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":88,"ship_count":8,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":3},{"id":90,"ship_count":3,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":4}]}
{"planets":[{"ship_count":5,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":89,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":2},{"id":87,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1},{"id":93,"ship_count":3,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":4},{"id":91,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":86,"ship_count":4,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":92,"ship_count":6,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":4},{"id":95,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":5},{"id":88,"ship_count":8,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":2},{"id":90,"ship_count":3,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":94,"ship_count":1,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":5}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":3,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":5,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":3,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":89,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":1},{"id":96,"ship_count":5,"origin":"protos","destination":"helios","owner":1,"turns_remaining":5},{"id":93,"ship_count":3,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":3},{"id":91,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":86,"ship_count":4,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":92,"ship_count":6,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":3},{"id":95,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":4},{"id":88,"ship_count":8,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":1},{"id":90,"ship_count":3,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":94,"ship_count":1,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":4}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":5,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":4,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":3,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":1,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":98,"ship_count":3,"origin":"extos","destination":"helios","owner":1,"turns_remaining":5},{"id":96,"ship_count":5,"origin":"protos","destination":"helios","owner":1,"turns_remaining":4},{"id":93,"ship_count":3,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":2},{"id":91,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1},{"id":86,"ship_count":4,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":92,"ship_count":6,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":2},{"id":95,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":3},{"id":97,"ship_count":5,"origin":"pemptos","destination":"helios","owner":2,"turns_remaining":5},{"id":90,"ship_count":3,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":94,"ship_count":1,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":3}]}
{"planets":[{"ship_count":3,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":5,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":4,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":4,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":98,"ship_count":3,"origin":"extos","destination":"helios","owner":1,"turns_remaining":4},{"id":96,"ship_count":5,"origin":"protos","destination":"helios","owner":1,"turns_remaining":3},{"id":93,"ship_count":3,"origin":"tritos","destination":"duteros","owner":2,"turns_remaining":1},{"id":100,"ship_count":2,"origin":"extos","destination":"helios","owner":1,"turns_remaining":5},{"id":86,"ship_count":4,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":92,"ship_count":6,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":1},{"id":95,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":2},{"id":97,"ship_count":5,"origin":"pemptos","destination":"helios","owner":2,"turns_remaining":4},{"id":99,"ship_count":3,"origin":"duteros","destination":"helios","owner":2,"turns_remaining":5},{"id":94,"ship_count":1,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":2}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":7,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":0,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":5,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":98,"ship_count":3,"origin":"extos","destination":"helios","owner":1,"turns_remaining":3},{"id":96,"ship_count":5,"origin":"protos","destination":"helios","owner":1,"turns_remaining":2},{"id":102,"ship_count":3,"origin":"tetartos","destination":"helios","owner":2,"turns_remaining":5},{"id":100,"ship_count":2,"origin":"extos","destination":"helios","owner":1,"turns_remaining":4},{"id":86,"ship_count":4,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":1},{"id":101,"ship_count":3,"origin":"protos","destination":"helios","owner":1,"turns_remaining":5},{"id":95,"ship_count":2,"origin":"tetartos","destination":"tritos","owner":2,"turns_remaining":1},{"id":97,"ship_count":5,"origin":"pemptos","destination":"helios","owner":2,"turns_remaining":3},{"id":99,"ship_count":3,"origin":"duteros","destination":"helios","owner":2,"turns_remaining":4},{"id":94,"ship_count":1,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":1}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":1,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":3,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":null,"name":"helios"}],"expeditions":[{"id":98,"ship_count":3,"origin":"extos","destination":"helios","owner":1,"turns_remaining":2},{"id":96,"ship_count":5,"origin":"protos","destination":"helios","owner":1,"turns_remaining":1},{"id":102,"ship_count":3,"origin":"tetartos","destination":"helios","owner":2,"turns_remaining":4},{"id":100,"ship_count":2,"origin":"extos","destination":"helios","owner":1,"turns_remaining":3},{"id":104,"ship_count":7,"origin":"duteros","destination":"protos","owner":2,"turns_remaining":5},{"id":101,"ship_count":3,"origin":"protos","destination":"helios","owner":1,"turns_remaining":4},{"id":103,"ship_count":2,"origin":"extos","destination":"protos","owner":1,"turns_remaining":5},{"id":97,"ship_count":5,"origin":"pemptos","destination":"helios","owner":2,"turns_remaining":2},{"id":99,"ship_count":3,"origin":"duteros","destination":"helios","owner":2,"turns_remaining":3}]}
{"planets":[{"ship_count":3,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":3,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":1,"x":0.0,"y":0.0,"owner":1,"name":"helios"}],"expeditions":[{"id":98,"ship_count":3,"origin":"extos","destination":"helios","owner":1,"turns_remaining":1},{"id":106,"ship_count":3,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":10},{"id":102,"ship_count":3,"origin":"tetartos","destination":"helios","owner":2,"turns_remaining":3},{"id":100,"ship_count":2,"origin":"extos","destination":"helios","owner":1,"turns_remaining":2},{"id":104,"ship_count":7,"origin":"duteros","destination":"protos","owner":2,"turns_remaining":4},{"id":101,"ship_count":3,"origin":"protos","destination":"helios","owner":1,"turns_remaining":3},{"id":103,"ship_count":2,"origin":"extos","destination":"protos","owner":1,"turns_remaining":4},{"id":97,"ship_count":5,"origin":"pemptos","destination":"helios","owner":2,"turns_remaining":1},{"id":99,"ship_count":3,"origin":"duteros","destination":"helios","owner":2,"turns_remaining":2},{"id":105,"ship_count":1,"origin":"extos","destination":"protos","owner":1,"turns_remaining":5}]}
{"planets":[{"ship_count":4,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":3,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":4,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":1,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":108,"ship_count":1,"origin":"helios","destination":"extos","owner":1,"turns_remaining":5},{"id":106,"ship_count":3,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":9},{"id":102,"ship_count":3,"origin":"tetartos","destination":"helios","owner":2,"turns_remaining":2},{"id":100,"ship_count":2,"origin":"extos","destination":"helios","owner":1,"turns_remaining":1},{"id":104,"ship_count":7,"origin":"duteros","destination":"protos","owner":2,"turns_remaining":3},{"id":101,"ship_count":3,"origin":"protos","destination":"helios","owner":1,"turns_remaining":2},{"id":103,"ship_count":2,"origin":"extos","destination":"protos","owner":1,"turns_remaining":3},{"id":107,"ship_count":2,"origin":"duteros","destination":"extos","owner":2,"turns_remaining":9},{"id":99,"ship_count":3,"origin":"duteros","destination":"helios","owner":2,"turns_remaining":1},{"id":105,"ship_count":1,"origin":"extos","destination":"protos","owner":1,"turns_remaining":4}]}
{"planets":[{"ship_count":5,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":4,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":3,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":1,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":3,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":3,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":108,"ship_count":1,"origin":"helios","destination":"extos","owner":1,"turns_remaining":4},{"id":106,"ship_count":3,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":8},{"id":102,"ship_count":3,"origin":"tetartos","destination":"helios","owner":2,"turns_remaining":1},{"id":109,"ship_count":4,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":5},{"id":104,"ship_count":7,"origin":"duteros","destination":"protos","owner":2,"turns_remaining":2},{"id":101,"ship_count":3,"origin":"protos","destination":"helios","owner":1,"turns_remaining":1},{"id":103,"ship_count":2,"origin":"extos","destination":"protos","owner":1,"turns_remaining":2},{"id":107,"ship_count":2,"origin":"duteros","destination":"extos","owner":2,"turns_remaining":8},{"id":105,"ship_count":1,"origin":"extos","destination":"protos","owner":1,"turns_remaining":3}]}
{"planets":[{"ship_count":5,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":1,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":4,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":2,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":4,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":108,"ship_count":1,"origin":"helios","destination":"extos","owner":1,"turns_remaining":3},{"id":106,"ship_count":3,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":7},{"id":111,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":5},{"id":109,"ship_count":4,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":4},{"id":104,"ship_count":7,"origin":"duteros","destination":"protos","owner":2,"turns_remaining":1},{"id":110,"ship_count":4,"origin":"tritos","destination":"protos","owner":2,"turns_remaining":10},{"id":103,"ship_count":2,"origin":"extos","destination":"protos","owner":1,"turns_remaining":1},{"id":107,"ship_count":2,"origin":"duteros","destination":"extos","owner":2,"turns_remaining":7},{"id":105,"ship_count":1,"origin":"extos","destination":"protos","owner":1,"turns_remaining":2}]}
{"planets":[{"ship_count":0,"x":-6.0,"y":0.0,"owner":null,"name":"protos"},{"ship_count":4,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":5,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":3,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":5,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":1,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":108,"ship_count":1,"origin":"helios","destination":"extos","owner":1,"turns_remaining":2},{"id":106,"ship_count":3,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":6},{"id":111,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":4},{"id":109,"ship_count":4,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":3},{"id":113,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":5},{"id":110,"ship_count":4,"origin":"tritos","destination":"protos","owner":2,"turns_remaining":9},{"id":112,"ship_count":4,"origin":"helios","destination":"extos","owner":2,"turns_remaining":5},{"id":107,"ship_count":2,"origin":"duteros","destination":"extos","owner":2,"turns_remaining":6},{"id":105,"ship_count":1,"origin":"extos","destination":"protos","owner":1,"turns_remaining":1}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":3,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":6,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":4,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":1,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":2,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":108,"ship_count":1,"origin":"helios","destination":"extos","owner":1,"turns_remaining":1},{"id":106,"ship_count":3,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":5},{"id":111,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":3},{"id":109,"ship_count":4,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":2},{"id":113,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":4},{"id":110,"ship_count":4,"origin":"tritos","destination":"protos","owner":2,"turns_remaining":8},{"id":112,"ship_count":4,"origin":"helios","destination":"extos","owner":2,"turns_remaining":4},{"id":107,"ship_count":2,"origin":"duteros","destination":"extos","owner":2,"turns_remaining":5},{"id":115,"ship_count":4,"origin":"duteros","destination":"protos","owner":2,"turns_remaining":5},{"id":114,"ship_count":5,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":4,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":5,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":2,"x":-3.0,"y":-5.0,"owner":1,"name":"extos"},{"ship_count":3,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":117,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":106,"ship_count":3,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":4},{"id":111,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":2},{"id":109,"ship_count":4,"origin":"pemptos","destination":"extos","owner":2,"turns_remaining":1},{"id":113,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":3},{"id":110,"ship_count":4,"origin":"tritos","destination":"protos","owner":2,"turns_remaining":7},{"id":112,"ship_count":4,"origin":"helios","destination":"extos","owner":2,"turns_remaining":3},{"id":107,"ship_count":2,"origin":"duteros","destination":"extos","owner":2,"turns_remaining":4},{"id":115,"ship_count":4,"origin":"duteros","destination":"protos","owner":2,"turns_remaining":4},{"id":114,"ship_count":5,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":116,"ship_count":5,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":5}]}
{"planets":[{"ship_count":3,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":5,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":3,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":3,"x":-3.0,"y":-5.0,"owner":2,"name":"extos"},{"ship_count":2,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":117,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":106,"ship_count":3,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":3},{"id":111,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":1},{"id":119,"ship_count":2,"origin":"helios","destination":"protos","owner":2,"turns_remaining":5},{"id":113,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":2},{"id":110,"ship_count":4,"origin":"tritos","destination":"protos","owner":2,"turns_remaining":6},{"id":112,"ship_count":4,"origin":"helios","destination":"extos","owner":2,"turns_remaining":2},{"id":107,"ship_count":2,"origin":"duteros","destination":"extos","owner":2,"turns_remaining":3},{"id":115,"ship_count":4,"origin":"duteros","destination":"protos","owner":2,"turns_remaining":3},{"id":114,"ship_count":5,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":116,"ship_count":5,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":4},{"id":118,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":5}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":4,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":1,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":4,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":7,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":3,"x":-3.0,"y":-5.0,"owner":2,"name":"extos"},{"ship_count":3,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":117,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":106,"ship_count":3,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":2},{"id":121,"ship_count":5,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":9},{"id":119,"ship_count":2,"origin":"helios","destination":"protos","owner":2,"turns_remaining":4},{"id":113,"ship_count":1,"origin":"protos","destination":"extos","owner":1,"turns_remaining":1},{"id":110,"ship_count":4,"origin":"tritos","destination":"protos","owner":2,"turns_remaining":5},{"id":112,"ship_count":4,"origin":"helios","destination":"extos","owner":2,"turns_remaining":1},{"id":107,"ship_count":2,"origin":"duteros","destination":"extos","owner":2,"turns_remaining":2},{"id":115,"ship_count":4,"origin":"duteros","destination":"protos","owner":2,"turns_remaining":2},{"id":114,"ship_count":5,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":116,"ship_count":5,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":118,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":120,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":10}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":5,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":1,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":5,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":8,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":7,"x":-3.0,"y":-5.0,"owner":2,"name":"extos"},{"ship_count":4,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":117,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":106,"ship_count":3,"origin":"tetartos","destination":"extos","owner":2,"turns_remaining":1},{"id":121,"ship_count":5,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":8},{"id":119,"ship_count":2,"origin":"helios","destination":"protos","owner":2,"turns_remaining":3},{"id":123,"ship_count":1,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":10},{"id":110,"ship_count":4,"origin":"tritos","destination":"protos","owner":2,"turns_remaining":4},{"id":122,"ship_count":1,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":9},{"id":107,"ship_count":2,"origin":"duteros","destination":"extos","owner":2,"turns_remaining":1},{"id":115,"ship_count":4,"origin":"duteros","destination":"protos","owner":2,"turns_remaining":1},{"id":114,"ship_count":5,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1},{"id":116,"ship_count":5,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":118,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":120,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":9}]}
{"planets":[{"ship_count":3,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":6,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":4,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":2,"name":"extos"},{"ship_count":5,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":117,"ship_count":1,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1},{"id":125,"ship_count":7,"origin":"extos","destination":"helios","owner":2,"turns_remaining":5},{"id":121,"ship_count":5,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":7},{"id":119,"ship_count":2,"origin":"helios","destination":"protos","owner":2,"turns_remaining":2},{"id":123,"ship_count":1,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":9},{"id":110,"ship_count":4,"origin":"tritos","destination":"protos","owner":2,"turns_remaining":3},{"id":122,"ship_count":1,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":8},{"id":124,"ship_count":1,"origin":"protos","destination":"helios","owner":1,"turns_remaining":5},{"id":120,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":8},{"id":118,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":116,"ship_count":5,"origin":"tetartos","destination":"pemptos","owner":2,"turns_remaining":1}]}
{"planets":[{"ship_count":4,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":2,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":3,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":7,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":9,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":7,"x":-3.0,"y":-5.0,"owner":2,"name":"extos"},{"ship_count":6,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":126,"ship_count":5,"origin":"duteros","destination":"pemptos","owner":2,"turns_remaining":11},{"id":125,"ship_count":7,"origin":"extos","destination":"helios","owner":2,"turns_remaining":4},{"id":121,"ship_count":5,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":6},{"id":119,"ship_count":2,"origin":"helios","destination":"protos","owner":2,"turns_remaining":1},{"id":123,"ship_count":1,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":8},{"id":110,"ship_count":4,"origin":"tritos","destination":"protos","owner":2,"turns_remaining":2},{"id":122,"ship_count":1,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":7},{"id":124,"ship_count":1,"origin":"protos","destination":"helios","owner":1,"turns_remaining":4},{"id":120,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":7},{"id":118,"ship_count":2,"origin":"extos","destination":"pemptos","owner":1,"turns_remaining":1}]}
{"planets":[{"ship_count":7,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":4,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":8,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":8,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":8,"x":-3.0,"y":-5.0,"owner":2,"name":"extos"},{"ship_count":5,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":126,"ship_count":5,"origin":"duteros","destination":"pemptos","owner":2,"turns_remaining":10},{"id":125,"ship_count":7,"origin":"extos","destination":"helios","owner":2,"turns_remaining":3},{"id":121,"ship_count":5,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":5},{"id":127,"ship_count":2,"origin":"helios","destination":"duteros","owner":2,"turns_remaining":5},{"id":123,"ship_count":1,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":7},{"id":110,"ship_count":4,"origin":"tritos","destination":"protos","owner":2,"turns_remaining":1},{"id":122,"ship_count":1,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":6},{"id":124,"ship_count":1,"origin":"protos","destination":"helios","owner":1,"turns_remaining":3},{"id":120,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":6}]}
{"planets":[{"ship_count":12,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":4,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":5,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":9,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":9,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":9,"x":-3.0,"y":-5.0,"owner":2,"name":"extos"},{"ship_count":6,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":126,"ship_count":5,"origin":"duteros","destination":"pemptos","owner":2,"turns_remaining":9},{"id":125,"ship_count":7,"origin":"extos","destination":"helios","owner":2,"turns_remaining":2},{"id":121,"ship_count":5,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":4},{"id":127,"ship_count":2,"origin":"helios","destination":"duteros","owner":2,"turns_remaining":4},{"id":123,"ship_count":1,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":6},{"id":120,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":122,"ship_count":1,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":5},{"id":124,"ship_count":1,"origin":"protos","destination":"helios","owner":1,"turns_remaining":2}]}
{"planets":[{"ship_count":13,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":5,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":2,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":10,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":10,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":10,"x":-3.0,"y":-5.0,"owner":2,"name":"extos"},{"ship_count":7,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":126,"ship_count":5,"origin":"duteros","destination":"pemptos","owner":2,"turns_remaining":8},{"id":125,"ship_count":7,"origin":"extos","destination":"helios","owner":2,"turns_remaining":1},{"id":121,"ship_count":5,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":3},{"id":127,"ship_count":2,"origin":"helios","destination":"duteros","owner":2,"turns_remaining":3},{"id":123,"ship_count":1,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":5},{"id":120,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":122,"ship_count":1,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":4},{"id":124,"ship_count":1,"origin":"protos","destination":"helios","owner":1,"turns_remaining":1},{"id":128,"ship_count":4,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":9}]}
{"planets":[{"ship_count":14,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":3,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":11,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":11,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":11,"x":-3.0,"y":-5.0,"owner":2,"name":"extos"},{"ship_count":14,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":126,"ship_count":5,"origin":"duteros","destination":"pemptos","owner":2,"turns_remaining":7},{"id":128,"ship_count":4,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":8},{"id":121,"ship_count":5,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":127,"ship_count":2,"origin":"helios","destination":"duteros","owner":2,"turns_remaining":2},{"id":123,"ship_count":1,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":4},{"id":120,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":122,"ship_count":1,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":3}]}
{"planets":[{"ship_count":15,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":7,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":4,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":12,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":12,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":12,"x":-3.0,"y":-5.0,"owner":2,"name":"extos"},{"ship_count":9,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":126,"ship_count":5,"origin":"duteros","destination":"pemptos","owner":2,"turns_remaining":6},{"id":128,"ship_count":4,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":7},{"id":121,"ship_count":5,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":127,"ship_count":2,"origin":"helios","destination":"duteros","owner":2,"turns_remaining":1},{"id":123,"ship_count":1,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":3},{"id":120,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":122,"ship_count":1,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":2},{"id":129,"ship_count":6,"origin":"helios","destination":"protos","owner":2,"turns_remaining":5}]}
{"planets":[{"ship_count":16,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":10,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":5,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":13,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":18,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":13,"x":-3.0,"y":-5.0,"owner":2,"name":"extos"},{"ship_count":10,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":126,"ship_count":5,"origin":"duteros","destination":"pemptos","owner":2,"turns_remaining":5},{"id":128,"ship_count":4,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":6},{"id":129,"ship_count":6,"origin":"helios","destination":"protos","owner":2,"turns_remaining":4},{"id":122,"ship_count":1,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":1},{"id":123,"ship_count":1,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":2},{"id":120,"ship_count":3,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":1}]}
{"planets":[{"ship_count":17,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":11,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":14,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":17,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":14,"x":-3.0,"y":-5.0,"owner":2,"name":"extos"},{"ship_count":8,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":126,"ship_count":5,"origin":"duteros","destination":"pemptos","owner":2,"turns_remaining":4},{"id":128,"ship_count":4,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":5},{"id":129,"ship_count":6,"origin":"helios","destination":"protos","owner":2,"turns_remaining":3},{"id":130,"ship_count":3,"origin":"helios","destination":"extos","owner":2,"turns_remaining":5},{"id":123,"ship_count":1,"origin":"protos","destination":"pemptos","owner":1,"turns_remaining":1}]}
{"planets":[{"ship_count":18,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":12,"x":-3.0,"y":5.0,"owner":2,"name":"duteros"},{"ship_count":7,"x":3.0,"y":5.0,"owner":2,"name":"tritos"},{"ship_count":15,"x":6.0,"y":0.0,"owner":2,"name":"tetartos"},{"ship_count":17,"x":3.0,"y":-5.0,"owner":2,"name":"pemptos"},{"ship_count":10,"x":-3.0,"y":-5.0,"owner":2,"name":"extos"},{"ship_count":9,"x":0.0,"y":0.0,"owner":2,"name":"helios"}],"expeditions":[{"id":126,"ship_count":5,"origin":"duteros","destination":"pemptos","owner":2,"turns_remaining":3},{"id":128,"ship_count":4,"origin":"tritos","destination":"pemptos","owner":2,"turns_remaining":4},{"id":129,"ship_count":6,"origin":"helios","destination":"protos","owner":2,"turns_remaining":2},{"id":130,"ship_count":3,"origin":"helios","destination":"extos","owner":2,"turns_remaining":4},{"id":131,"ship_count":5,"origin":"extos","destination":"duteros","owner":2,"turns_remaining":9}]}

View file

@ -7,3 +7,11 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
mozaic = { git = "https://github.com/ajuvercr/MOZAIC" }
tokio = "0.1.22"
capnp = "0.10.1"
futures = "0.1.28"
serde = "1.0.100"
serde_derive = "1.0.100"
serde_json = "1.0"
rand = { version = "0.6.5", default-features = true }

23
client/bot7.txt Normal file
View file

@ -0,0 +1,23 @@
start{"planets":[{"ship_count":7,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":7,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":0,"ship_count":6,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":5},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":10}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":0,"ship_count":6,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":4},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":9},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":11},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":11}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":0,"ship_count":6,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":3},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":8},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":10},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":10},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":11},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":11}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":0,"ship_count":6,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":2},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":7},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":9},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":9},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":10},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":10},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":11},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":11}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":0,"ship_count":6,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":1},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":6},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":8},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":8},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":9},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":9},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":10},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":10},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":11},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":11}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":0,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":11},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":5},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":7},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":7},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":8},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":8},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":9},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":9},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":10},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":10},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":11}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":0,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":10},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":4},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":6},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":6},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":7},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":7},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":8},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":8},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":9},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":9},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":10},{"id":12,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":5},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":10}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":0,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":9},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":3},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":5},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":5},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":6},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":6},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":7},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":7},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":8},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":8},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":9},{"id":12,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":4},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":9},{"id":14,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":5},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":10}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":0,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":8},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":2},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":4},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":4},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":5},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":5},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":6},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":6},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":7},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":7},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":8},{"id":12,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":3},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":8},{"id":14,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":4},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":9},{"id":16,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":5},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":10}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":0,"x":-3.0,"y":5.0,"owner":null,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":7},{"id":1,"ship_count":6,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":1},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":3},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":3},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":4},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":4},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":5},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":5},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":6},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":6},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":7},{"id":12,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":2},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":7},{"id":14,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":3},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":8},{"id":16,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":4},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":9},{"id":18,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":5},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":10}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":6,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":6},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":10},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":2},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":2},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":3},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":3},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":4},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":4},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":5},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":5},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":6},{"id":12,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":1},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":6},{"id":14,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":2},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":7},{"id":16,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":3},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":8},{"id":18,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":4},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":9},{"id":20,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":5}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":3,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":5},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":9},{"id":2,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":1},{"id":3,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":1},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":2},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":2},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":3},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":3},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":4},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":4},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":5},{"id":23,"ship_count":5,"origin":"duteros","destination":"protos","owner":1,"turns_remaining":5},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":5},{"id":14,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":1},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":6},{"id":16,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":2},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":7},{"id":18,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":3},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":8},{"id":20,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":4},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":11}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":4},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":8},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":11},{"id":24,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":5},{"id":4,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":1},{"id":5,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":1},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":2},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":2},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":3},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":3},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":4},{"id":23,"ship_count":5,"origin":"duteros","destination":"protos","owner":1,"turns_remaining":4},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":4},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":10},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":5},{"id":16,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":1},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":6},{"id":18,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":2},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":7},{"id":20,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":3}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":3},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":7},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":10},{"id":24,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":4},{"id":20,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":2},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":6},{"id":6,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":1},{"id":7,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":1},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":2},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":2},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":3},{"id":23,"ship_count":5,"origin":"duteros","destination":"protos","owner":1,"turns_remaining":3},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":3},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":9},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":4},{"id":18,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":1},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":5}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":2},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":6},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":9},{"id":24,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":3},{"id":20,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":1},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":5},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":4},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":3},{"id":8,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":1},{"id":9,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":1},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":2},{"id":23,"ship_count":5,"origin":"duteros","destination":"protos","owner":1,"turns_remaining":2},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":2},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":8}]}
{"planets":[{"ship_count":1,"x":-6.0,"y":0.0,"owner":2,"name":"protos"},{"ship_count":1,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":11,"ship_count":1,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":1},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":5},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":8},{"id":24,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":2},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":7},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":4},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":3},{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":2},{"id":13,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":1},{"id":23,"ship_count":5,"origin":"duteros","destination":"protos","owner":1,"turns_remaining":1},{"id":10,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":1}]}
{"planets":[{"ship_count":4,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":1,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":15,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":1},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":4},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":7},{"id":24,"ship_count":1,"origin":"protos","destination":"duteros","owner":2,"turns_remaining":1},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":6},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":3},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":2}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":4,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":26,"ship_count":3,"origin":"protos","destination":"tritos","owner":1,"turns_remaining":10},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":3},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":6},{"id":17,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":1},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":5},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":2}]}
{"planets":[{"ship_count":3,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":3,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":26,"ship_count":3,"origin":"protos","destination":"tritos","owner":1,"turns_remaining":9},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":2},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":5},{"id":27,"ship_count":3,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":5},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":4},{"id":19,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":1}]}
{"planets":[{"ship_count":2,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":5,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":4,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":26,"ship_count":3,"origin":"protos","destination":"tritos","owner":1,"turns_remaining":8},{"id":21,"ship_count":1,"origin":"tetartos","destination":"duteros","owner":1,"turns_remaining":1},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":4},{"id":27,"ship_count":3,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":4},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":3},{"id":28,"ship_count":2,"origin":"protos","destination":"tritos","owner":1,"turns_remaining":10}]}
{"planets":[{"ship_count":3,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":3,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":5,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":26,"ship_count":3,"origin":"protos","destination":"tritos","owner":1,"turns_remaining":7},{"id":29,"ship_count":4,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":5},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":3},{"id":27,"ship_count":3,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":3},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":2},{"id":28,"ship_count":2,"origin":"protos","destination":"tritos","owner":1,"turns_remaining":9}]}
{"planets":[{"ship_count":4,"x":-6.0,"y":0.0,"owner":1,"name":"protos"},{"ship_count":4,"x":-3.0,"y":5.0,"owner":1,"name":"duteros"},{"ship_count":6,"x":3.0,"y":5.0,"owner":null,"name":"tritos"},{"ship_count":2,"x":6.0,"y":0.0,"owner":1,"name":"tetartos"},{"ship_count":6,"x":3.0,"y":-5.0,"owner":null,"name":"pemptos"},{"ship_count":6,"x":-3.0,"y":-5.0,"owner":null,"name":"extos"}],"expeditions":[{"id":26,"ship_count":3,"origin":"protos","destination":"tritos","owner":1,"turns_remaining":6},{"id":29,"ship_count":4,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":4},{"id":25,"ship_count":2,"origin":"tetartos","destination":"protos","owner":1,"turns_remaining":2},{"id":27,"ship_count":3,"origin":"duteros","destination":"tritos","owner":1,"turns_remaining":2},{"id":22,"ship_count":1,"origin":"protos","destination":"tetartos","owner":2,"turns_remaining":1},{"id":28,"ship_count":2,"origin":"protos","destination":"tritos","owner":1,"turns_remaining":8},{"id":30,"ship_count":4,"origin":"tetartos","destination":"tritos","owner":1,"turns_remaining":5}]}

0
client/log.log Normal file
View file

4
client/run.sh Executable file
View file

@ -0,0 +1,4 @@
#!/bin/bash
rm bot*.txt
cargo run $1 python simple.py

29
client/simple.py Normal file
View file

@ -0,0 +1,29 @@
import sys, json, random
def move(command):
record = { 'moves': [command] }
print(json.dumps(record))
sys.stdout.flush()
f_name = f"bot{random.randint(0, 10)}.txt"
f = open(f_name,"w+")
f.write("start")
f.flush()
for line in sys.stdin:
f.write(line)
f.flush()
state = json.loads(line)
# find planet with most ships
my_planets = [p for p in state['planets'] if p['owner'] == 1]
other_planets = [p for p in state['planets'] if p['owner'] != 1]
if not my_planets or not other_planets:
move(None)
else:
planet = max(my_planets, key=lambda p: p['ship_count'])
dest = min(other_planets, key=lambda p: p['ship_count'])
move({
'origin': planet['name'],
'destination': dest['name'],
'ship_count': planet['ship_count'] - 1
})

View file

@ -1,3 +1,290 @@
extern crate mozaic;
extern crate tokio;
extern crate futures;
extern crate capnp;
extern crate rand;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
use rand::Rng;
use mozaic::core_capnp::{initialize, terminate_stream, identify, actor_joined};
use mozaic::messaging::reactor::*;
use mozaic::messaging::types::*;
use mozaic::errors::*;
use mozaic::client_capnp::{client_message, host_message, client_kicked};
use mozaic::mozaic_cmd_capnp::{bot_input, bot_return};
use mozaic::server::runtime::{Broker, BrokerHandle};
use mozaic::server;
use mozaic::modules::{BotReactor};
use std::env;
use std::str;
mod types;
fn main() { fn main() {
println!("Hello, world!"); let args: Vec<String> = env::args().collect();
let id = args.get(1).unwrap().parse().unwrap();
let client_args = args.get(2..).expect("How do you expect me to spawn your bot?").to_vec();
let addr = "127.0.0.1:9142".parse().unwrap();
let self_id: ReactorId = rand::thread_rng().gen();
tokio::run(futures::lazy(move || {
let mut broker = Broker::new().unwrap();
let reactor = ClientReactor {
server: None,
id,
broker: broker.clone(),
args: client_args,
};
broker.spawn(self_id.clone(), reactor.params(), "main").display();
tokio::spawn(server::connect_to_server(broker, self_id, &addr));
Ok(())
}));
}
// Main client logic
/// ? greeter_id is the server, the tcp stream that you connected to
/// ? runtime_id is your own runtime, to handle visualisation etc
/// ? user are you ...
struct ClientReactor {
server: Option<ReactorId>,
broker: BrokerHandle,
id: u64,
args: Vec<String>,
}
impl ClientReactor {
fn params<C: Ctx>(self) -> CoreParams<Self, C> {
let mut params = CoreParams::new(self);
params.handler(initialize::Owned, CtxHandler::new(Self::initialize));
params.handler(actor_joined::Owned, CtxHandler::new(Self::open_host));
return params;
}
// reactor setup
fn initialize<C: Ctx>(
&mut self,
handle: &mut ReactorHandle<C>,
_: initialize::Reader,
) -> Result<()>
{
// open link with runtime, for communicating with chat GUI
let runtime_link = RuntimeLink::params(handle.id().clone());
handle.open_link(runtime_link)?;
let bot = BotReactor::new(self.broker.clone(), handle.id().clone(), self.args.clone());
let bot_id = handle.spawn(bot.params(), "Bot Driver")?;
handle.open_link(BotLink::params(bot_id))?;
return Ok(());
}
fn open_host<C: Ctx>(
&mut self,
handle: &mut ReactorHandle<C>,
r: actor_joined::Reader,
) -> Result<()>
{
let id = r.get_id()?;
if let Some(server) = &self.server {
handle.open_link(HostLink::params(ReactorId::from(id)))?;
self.broker.register_as(id.into(), server.clone());
// Fake bot msg
let mut chat_message = MsgBuffer::<bot_return::Owned>::new();
chat_message.build(|b| {
b.set_message(b"");
});
handle.send_internal(chat_message)?;
} else {
handle.open_link(ServerLink::params(id.into()))?;
self.server = Some(id.into());
let mut identify = MsgBuffer::<identify::Owned>::new();
identify.build(|b| {
b.set_key(self.id);
});
handle.send_internal(identify).display();
}
Ok(())
}
}
// Handler for the connection with the chat server
struct ServerLink;
impl ServerLink {
fn params<C: Ctx>(foreign_id: ReactorId) -> LinkParams<Self, C> {
let mut params = LinkParams::new(foreign_id, Self);
params.external_handler( terminate_stream::Owned, CtxHandler::new(Self::close_handler) );
params.external_handler( actor_joined::Owned, CtxHandler::new(actor_joined::e_to_i) );
params.internal_handler( identify::Owned, CtxHandler::new(Self::identify) );
return params;
}
fn identify<C: Ctx>(
&mut self,
handle: &mut LinkHandle<C>,
id: identify::Reader,
) -> Result<()> {
let id = id.get_key();
let mut chat_message = MsgBuffer::<identify::Owned>::new();
chat_message.build(|b| {
b.set_key(id);
});
handle.send_message(chat_message).display();
Ok(())
}
fn close_handler<C: Ctx>(
&mut self,
handle: &mut LinkHandle<C>,
_: terminate_stream::Reader,
) -> Result<()>
{
// also close our end of the stream
handle.close_link()?;
return Ok(());
}
}
struct HostLink;
impl HostLink {
fn params<C: Ctx>(remote_id: ReactorId) -> LinkParams<Self, C> {
let mut params = LinkParams::new(remote_id, HostLink);
params.external_handler(
host_message::Owned,
CtxHandler::new(Self::receive_host_message),
);
params.internal_handler(
bot_return::Owned,
CtxHandler::new(Self::send_chat_message),
);
params.external_handler(
client_kicked::Owned,
CtxHandler::new(Self::client_kicked),
);
return params;
}
// pick up a 'send_message' event from the reactor, and put it to effect
// by constructing the chat message and sending it to the chat server.
fn send_chat_message<C: Ctx>(
&mut self,
handle: &mut LinkHandle<C>,
send_message: bot_return::Reader,
) -> Result<()>
{
let message = send_message.get_message()?;
println!("Our bot sent");
println!("{}", str::from_utf8(&message).unwrap());
let mut chat_message = MsgBuffer::<client_message::Owned>::new();
chat_message.build(|b| {
b.set_data(message);
});
handle.send_message(chat_message)?;
return Ok(());
}
// pick up a 'send_message' event from the reactor, and put it to effect
// by constructing the chat message and sending it to the chat server.
fn client_kicked<C: Ctx>(
&mut self,
handle: &mut LinkHandle<C>,
_: client_kicked::Reader,
) -> Result<()>
{
// Disconnect
handle.close_link()?;
return Ok(());
}
// receive a chat message from the chat server, and broadcast it on the
// reactor.
fn receive_host_message<C: Ctx>(
&mut self,
handle: &mut LinkHandle<C>,
host_message: host_message::Reader,
) -> Result<()>
{
let message = host_message.get_data()?;
let message: types::ServerMessage = serde_json::from_slice(message).unwrap();
println!("");
match message {
types::ServerMessage::GameState(state) => {
// println!("New game state");
let mut bot_msg = MsgBuffer::<bot_input::Owned>::new();
bot_msg.build(|b| {
b.set_input(&serde_json::to_vec(&state).unwrap());
});
handle.send_internal(bot_msg).display();
},
types::ServerMessage::FinalState(state) => {
println!("Game finished with");
println!("{:?}", state);
}
types::ServerMessage::PlayerAction(action) => {
println!("Out bot did");
println!("{:?}", action);
}
}
return Ok(());
}
}
struct BotLink;
impl BotLink {
fn params<C: Ctx>(foreign_id: ReactorId) -> LinkParams<Self, C> {
let mut params = LinkParams::new(foreign_id, Self);
params.external_handler(bot_return::Owned, CtxHandler::new(bot_return::e_to_i));
params.internal_handler(bot_input::Owned, CtxHandler::new(bot_input::i_to_e));
return params;
}
}
struct RuntimeLink;
impl RuntimeLink {
fn params<C: Ctx>(foreign_id: ReactorId) -> LinkParams<Self, C> {
let mut params = LinkParams::new(foreign_id, Self);
params.external_handler(
actor_joined::Owned,
CtxHandler::new(actor_joined::e_to_i),
);
return params;
}
} }

69
client/src/types.rs Normal file
View file

@ -0,0 +1,69 @@
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type", content = "content")]
pub enum ServerMessage {
/// Game state in current turn
GameState(State),
/// The action that was performed
PlayerAction(PlayerAction),
/// The game is over, and this is the concluding state.
FinalState(State),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type", content = "value")]
pub enum PlayerAction {
Timeout,
ParseError(String),
Commands(Vec<PlayerCommand>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PlayerCommand {
pub command: Command,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<CommandError>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum CommandError {
NotEnoughShips,
OriginNotOwned,
ZeroShipMove,
OriginDoesNotExist,
DestinationDoesNotExist,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Command {
pub origin: String,
pub destination: String,
pub ship_count: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct State {
pub planets: Vec<Planet>,
pub expeditions: Vec<Expedition>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Expedition {
pub id: u64,
pub ship_count: u64,
pub origin: String,
pub destination: String,
pub owner: usize,
pub turns_remaining: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Planet {
pub ship_count: u64,
pub x: f64,
pub y: f64,
pub owner: Option<usize>,
pub name: String,
}

View file

@ -1,31 +1,32 @@
import { Game } from "planetwars"; import { Game } from "planetwars";
import { memory } from "planetwars/plantwars_bg" import { memory } from "planetwars/plantwars_bg"
import { Shader } from "./webgl/shader"
import { main } from './index.ts'
const URL = window.location.origin+window.location.pathname; const URL = window.location.origin+window.location.pathname;
const LOCATION = URL.substring(0, URL.lastIndexOf("/") + 1); const LOCATION = URL.substring(0, URL.lastIndexOf("/") + 1);
const game_location = LOCATION + "static/game.json"; const game_location = LOCATION + "static/game.json";
// fetch(game_location) fetch(game_location)
// .then((r) => r.text()) .then((r) => r.text())
// .then((response) => { .then((response) => {
// console.log(response); main(Game.new(response));
// let game = Game.new(response); }).catch(console.error);
// console.log(game.turn_count());
// }).catch(console.error);
const g = Game.new(""); // const g = Game.new("");
const p1 = g.locations(); // const p1 = g.locations();
const s1 = g.location_count(); // const s1 = g.location_count();
console.log(p1, s1); // console.log(p1, s1);
const a1 = new Float64Array(memory.buffer, p1, s1 * 3); // const a1 = new Float64Array(memory.buffer, p1, s1 * 3);
console.log(a1); // console.log(a1);
g.add_location(0.5, 1.2, 3.14); // g.add_location(0.5, 1.2, 3.14);
const p2 = g.locations(); // const p2 = g.locations();
const s2 = g.location_count(); // const s2 = g.location_count();
const a2 = new Float64Array(memory.buffer, p2, s2 * 3); // const a2 = new Float64Array(memory.buffer, p2, s2 * 3);
console.log(a2); // console.log(a2);

5
frontend/www/index.ts Normal file
View file

@ -0,0 +1,5 @@
import { Game } from "planetwars";
export function main(game: Game) {
console.log(game.turn_count());
}

View file

@ -4063,6 +4063,12 @@
"sha.js": "^2.4.8" "sha.js": "^2.4.8"
} }
}, },
"picomatch": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.0.7.tgz",
"integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==",
"dev": true
},
"pify": { "pify": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
@ -4094,8 +4100,7 @@
} }
}, },
"planetwars": { "planetwars": {
"version": "file:../pkg", "version": "file:../pkg"
"dev": true
}, },
"portfinder": { "portfinder": {
"version": "1.0.21", "version": "1.0.21",
@ -5192,6 +5197,70 @@
"integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==",
"dev": true "dev": true
}, },
"ts-loader": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-6.1.0.tgz",
"integrity": "sha512-7JedeOu2rsYHQDEr2fwmMozABwbQTZXEaEMZPSIWG7gpzRefOLJCqwdazcegHtyaxp04PeEgs/b0m08WMpnIzQ==",
"dev": true,
"requires": {
"chalk": "^2.3.0",
"enhanced-resolve": "^4.0.0",
"loader-utils": "^1.0.2",
"micromatch": "^4.0.0",
"semver": "^6.0.0"
},
"dependencies": {
"braces": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
"integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
"dev": true,
"requires": {
"fill-range": "^7.0.1"
}
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
"integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
"dev": true,
"requires": {
"to-regex-range": "^5.0.1"
}
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true
},
"micromatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz",
"integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==",
"dev": true,
"requires": {
"braces": "^3.0.1",
"picomatch": "^2.0.5"
}
},
"semver": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
"dev": true
},
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
"requires": {
"is-number": "^7.0.0"
}
}
}
},
"tslib": { "tslib": {
"version": "1.10.0", "version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
@ -5220,6 +5289,12 @@
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
"dev": true "dev": true
}, },
"typescript": {
"version": "3.6.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz",
"integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==",
"dev": true
},
"union-value": { "union-value": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",

View file

@ -2,7 +2,8 @@
"name": "create-wasm-app", "name": "create-wasm-app",
"version": "0.1.0", "version": "0.1.0",
"description": "create an app to consume rust-generated wasm packages", "description": "create an app to consume rust-generated wasm packages",
"main": "index.js", "main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": { "scripts": {
"build": "webpack --config webpack.config.js", "build": "webpack --config webpack.config.js",
"start": "webpack-dev-server" "start": "webpack-dev-server"
@ -18,6 +19,9 @@
"webpack", "webpack",
"mozaic" "mozaic"
], ],
"dependencies": {
"planetwars": "file:../pkg"
},
"author": "Arthur Vercruysse <arthur.vercruysse@outlook.com>", "author": "Arthur Vercruysse <arthur.vercruysse@outlook.com>",
"license": "(MIT OR Apache-2.0)", "license": "(MIT OR Apache-2.0)",
"bugs": { "bugs": {
@ -25,8 +29,9 @@
}, },
"homepage": "https://github.com/ajuvercr/Planetwars#Readme", "homepage": "https://github.com/ajuvercr/Planetwars#Readme",
"devDependencies": { "devDependencies": {
"planetwars": "file:../pkg",
"webpack": "^4.29.3", "webpack": "^4.29.3",
"ts-loader": "^6.0.2",
"typescript": "^3.5.2",
"webpack-cli": "^3.1.0", "webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.5", "webpack-dev-server": "^3.1.5",
"copy-webpack-plugin": "^5.0.0" "copy-webpack-plugin": "^5.0.0"

View file

@ -0,0 +1,15 @@
{
"compilerOptions": {
"lib": ["es2017", "es7", "es6", "dom"],
"outDir": "./dist/",
"noImplicitAny": false,
"module": "commonjs",
"target": "es6",
"jsx": "react",
"declaration": true
},
"exclude": [
"node_modules",
"dist"
]
}

View file

@ -0,0 +1,55 @@
export class Buffer {
buffer: WebGLBuffer;
data: any;
count: number;
type: number;
constructor(gl: WebGLRenderingContext, data: number[], type: number) {
this.buffer = gl.createBuffer();
this.type = type;
if (data)
this.updateData(gl, data);
}
_toArray(data: number[]): any {
return new Float32Array(data);
}
updateData(gl: WebGLRenderingContext, data: number[]) {
this.data = data;
this.count = data.length;
gl.bindBuffer(this.type, this.buffer);
gl.bufferData(this.type, this._toArray(data), gl.STATIC_DRAW);
}
bind(gl: WebGLRenderingContext) {
gl.bindBuffer(this.type, this.buffer);
}
getCount(): number {
return this.count;
}
}
export class VertexBuffer extends Buffer {
constructor(gl: WebGLRenderingContext, data: any) {
super(gl, data, gl.ARRAY_BUFFER);
}
_toArray(data: number[]): any {
return new Float32Array(data);
}
}
export class IndexBuffer extends Buffer {
constructor(gl: WebGLRenderingContext, data: any) {
super(gl, data, gl.ELEMENT_ARRAY_BUFFER);
}
_toArray(data: number[]): any {
return new Uint16Array(data);
}
}

View file

@ -0,0 +1,72 @@
import { Renderable } from './renderer';
import { Shader, Uniform } from './shader';
import { Dictionary } from './util';
function createAndSetupTexture(gl: WebGLRenderingContext): WebGLTexture {
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
// Set up texture so we can render any size image and so we are
// working with pixels.
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
return texture;
}
export class Foo implements Renderable {
stages: Stage[];
textures: WebGLTexture[];
framebuffers: WebGLFramebuffer[];
width: number;
height: number;
constructor(gl: WebGLRenderingContext, width: number, height: number) {
this.width = width;
this.height = height;
for (let ii = 0; ii < 2; ++ii) {
const texture = createAndSetupTexture(gl);
this.textures.push(texture);
// make the texture the same size as the image
gl.texImage2D(
gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0,
gl.RGBA, gl.UNSIGNED_BYTE, null);
// Create a framebuffer
const fbo = gl.createFramebuffer();
this.framebuffers.push(fbo);
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
// Attach a texture to it.
gl.framebufferTexture2D(
gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
}
}
render(gl: WebGLRenderingContext) {
this.stages.forEach( (item, i) => {
gl.bindFramebuffer(gl.FRAMEBUFFER, this.framebuffers[i%2]);
item.render(gl);
gl.bindTexture(gl.TEXTURE_2D, this.textures[i % 2]);
});
}
}
class Stage implements Renderable {
program: Shader;
uniforms: Dictionary<Uniform>;
render(gl: WebGLRenderingContext) {
this.program.bind(gl);
for (let name in this.uniforms) {
this.program.uniform(gl, name, this.uniforms[name]);
}
}
}

View file

@ -0,0 +1,72 @@
import { IndexBuffer } from './buffer';
import { Shader, Uniform1i } from './shader';
import { VertexArray } from './vertexBufferLayout';
import { Texture } from './texture';
export interface Renderable {
render(gl: WebGLRenderingContext): void;
}
export class Renderer {
renderables: Renderable[];
indexBuffers: IndexBuffer[];
vertexArrays: VertexArray[];
shaders: Shader[];
textures: Texture[];
constructor() {
this.indexBuffers = [];
this.vertexArrays = [];
this.shaders = [];
this.textures = [];
}
addRenderable(item: Renderable) {
this.renderables.push(item);
}
addToDraw(indexBuffer: IndexBuffer, vertexArray: VertexArray, shader: Shader, texture?: Texture): number {
this.indexBuffers.push(indexBuffer);
this.vertexArrays.push(vertexArray);
this.shaders.push(shader);
this.textures.push(texture);
return this.indexBuffers.length - 1;
}
render(gl: WebGLRenderingContext) {
const maxTextures = gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS);
let texLocation = 0;
for(let i = 0; i < this.indexBuffers.length; i ++) {
const indexBuffer = this.indexBuffers[i];
const vertexArray = this.vertexArrays[i];
const shader = this.shaders[i];
const texture = this.textures[i];
if (texture) {
shader.uniform(gl, texture.name, new Uniform1i(texLocation));
texture.bind(gl, texLocation);
texLocation ++;
if (texLocation > maxTextures) {
console.error("Using too many textures, this is not supported yet\nUndefined behaviour!");
}
}
if (vertexArray && shader) {
vertexArray.bind(gl, shader);
if (indexBuffer) {
indexBuffer.bind(gl);
gl.drawElements(gl.TRIANGLES, indexBuffer.getCount(), gl.UNSIGNED_SHORT, 0);
} else {
console.error("IndexBuffer is required to render, for now");
}
}
}
}
}

View file

@ -0,0 +1,305 @@
import { Dictionary } from './util';
function error(msg: string) {
console.log(msg);
}
const defaultShaderType = [
"VERTEX_SHADER",
"FRAGMENT_SHADER"
];
function loadShader(
gl: WebGLRenderingContext,
shaderSource: string,
shaderType: number,
opt_errorCallback: any,
): WebGLShader {
var errFn = opt_errorCallback || error;
// Create the shader object
var shader = gl.createShader(shaderType);
// Load the shader source
gl.shaderSource(shader, shaderSource);
// Compile the shader
gl.compileShader(shader);
// Check the compile status
var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!compiled) {
// Something went wrong during compilation; get the error
var lastError = gl.getShaderInfoLog(shader);
errFn("*** Error compiling shader '" + shader + "':" + lastError);
gl.deleteShader(shader);
return null;
}
console.log("created shader with source");
console.log(shaderSource);
return shader;
}
function createProgram(
gl: WebGLRenderingContext,
shaders: WebGLShader[],
opt_attribs: string[],
opt_locations: number[],
opt_errorCallback: any,
): WebGLProgram {
var errFn = opt_errorCallback || error;
var program = gl.createProgram();
shaders.forEach(function (shader) {
gl.attachShader(program, shader);
});
if (opt_attribs) {
opt_attribs.forEach(function (attrib, ndx) {
gl.bindAttribLocation(
program,
opt_locations ? opt_locations[ndx] : ndx,
attrib);
});
}
gl.linkProgram(program);
// Check the link status
var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!linked) {
// something went wrong with the link
var lastError = gl.getProgramInfoLog(program);
errFn("Error in program linking:" + lastError);
gl.deleteProgram(program);
return null;
}
return program;
}
function createShaderFromScript(
gl: WebGLRenderingContext,
scriptId: string,
context: Dictionary<any>,
opt_shaderType: number,
opt_errorCallback: any,
): WebGLShader {
var shaderSource = "";
var shaderType;
var shaderScript = document.getElementById(scriptId) as HTMLScriptElement;
if (!shaderScript) {
console.log("*** Error: unknown script element" + scriptId);
}
shaderSource = shaderScript.text;
for (let key in context) {
console.log("substitute " + key);
shaderSource = shaderSource.replace(new RegExp("\\$" + key, 'g'), context[key]);
}
if (!opt_shaderType) {
if (shaderScript.type === "x-shader/x-vertex") {
shaderType = 35633;
} else if (shaderScript.type === "x-shader/x-fragment") {
shaderType = 35632;
} else if (shaderType !== gl.VERTEX_SHADER && shaderType !== gl.FRAGMENT_SHADER) {
console.log("*** Error: unknown shader type");
}
}
return loadShader(
gl, shaderSource, opt_shaderType ? opt_shaderType : shaderType,
opt_errorCallback);
}
export class Shader {
shader: WebGLProgram;
uniformCache: Dictionary<WebGLUniformLocation>;
attribCache: Dictionary<number>;
static createProgramFromScripts(
gl: WebGLRenderingContext,
shaderScriptIds: string[],
context = {},
opt_attribs?: string[],
opt_locations?: number[],
opt_errorCallback?: any,
): Shader {
var shaders = [];
for (var ii = 0; ii < shaderScriptIds.length; ++ii) {
shaders.push(createShaderFromScript(
gl, shaderScriptIds[ii], context, (gl as any)[defaultShaderType[ii % 2]] as number, opt_errorCallback));
}
return new Shader(createProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback));
}
static async createProgramFromUrls(
gl: WebGLRenderingContext,
vert_url: string,
frag_url: string,
context?: Dictionary<string>,
opt_attribs?: string[],
opt_locations?: number[],
opt_errorCallback?: any,
): Promise<Shader> {
const sources = (await Promise.all([
fetch(vert_url).then((r) => r.text()),
fetch(frag_url).then((r) => r.text()),
])).map(x => {
for (let key in context) {
x = x.replace(new RegExp("\\$" + key, 'g'), context[key]);
}
return x;
});
const shaders = [
loadShader(gl, sources[0], 35633, opt_errorCallback),
loadShader(gl, sources[1], 35632, opt_errorCallback),
];
return new Shader(createProgram(gl, shaders, opt_attribs, opt_locations, opt_errorCallback));
}
constructor(shader: WebGLProgram) {
this.shader = shader;
this.uniformCache = {};
this.attribCache = {};
}
bind(gl: WebGLRenderingContext) {
gl.useProgram(this.shader);
}
// Different locations have different types :/
getUniformLocation(gl: WebGLRenderingContext, name: string): WebGLUniformLocation {
if (this.uniformCache[name] === undefined) {
this.uniformCache[name] = gl.getUniformLocation(this.shader, name);
}
return this.uniformCache[name];
}
getAttribLocation(gl: WebGLRenderingContext, name: string): number {
if (this.attribCache[name] === undefined) {
this.attribCache[name] = gl.getAttribLocation(this.shader, name);
}
return this.attribCache[name];
}
uniform<T extends Uniform>(
gl: WebGLRenderingContext,
name: string,
uniform: T,
) {
this.bind(gl);
const location = this.getUniformLocation(gl, name);
if (location < 0) {
console.log("No location found with name " + name);
}
uniform.setUniform(gl, location);
}
}
export interface Uniform {
setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation): void;
}
export class Uniform2fv implements Uniform {
data: number[];
constructor(data: number[]) {
this.data = data;
}
setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
gl.uniform2fv(location, this.data);
}
}
export class Uniform3fv implements Uniform {
data: number[];
constructor(data: number[]) {
this.data = data;
}
setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
gl.uniform3fv(location, this.data);
}
}
export class Uniform1iv implements Uniform {
data: number[];
constructor(data: number[]) {
this.data = data;
}
setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
gl.uniform1iv(location, this.data);
}
}
export class Uniform1i implements Uniform {
texture: number;
constructor(texture: number) {
this.texture = texture;
}
setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
gl.uniform1i(location, this.texture);
}
}
export class Uniform1f implements Uniform {
texture: number;
constructor(texture: number) {
this.texture = texture;
}
setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
gl.uniform1f(location, this.texture);
}
}
export class Uniform2f implements Uniform {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
gl.uniform2f(location, this.x, this.y);
}
}
export class Uniform4f implements Uniform {
v0: number;
v1: number;
v2: number;
v3: number;
constructor(vec: number[]) {
this.v0 = vec[0];
this.v1 = vec[1];
this.v2 = vec[2];
this.v3 = vec[3];
}
setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
gl.uniform4f(location, this.v0, this.v1, this.v2, this.v3);
}
}
export class UniformMatrix3fv implements Uniform {
data: number[];
constructor(data: number[]) {
this.data = data;
}
setUniform(gl: WebGLRenderingContext, location: WebGLUniformLocation) {
gl.uniformMatrix3fv(location, false, this.data);
}
}

View file

@ -0,0 +1,68 @@
// TODO: fix texture locations, not use only 0
export class Texture {
texture: WebGLTexture;
image: HTMLImageElement;
loaded: boolean;
name: string;
constructor(
gl: WebGLRenderingContext,
path: string,
name: string,
) {
this.loaded = false;
this.name = name;
this.image = new Image();
this.image.onload = () => this.handleImageLoaded(gl);
this.image.onerror = error;
this.image.src = path;
this.texture = gl.createTexture();
this.bind(gl);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA,
gl.UNSIGNED_BYTE, new Uint8Array([255, 0, 0, 255]));
}
handleImageLoaded(gl: WebGLRenderingContext) {
console.log('handling image loaded');
this.bind(gl);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.image);
this.unbind(gl);
this.loaded = true;
}
bind(gl: WebGLRenderingContext, location=0) {
gl.activeTexture(gl.TEXTURE0 + location);
gl.bindTexture(gl.TEXTURE_2D, this.texture);
}
unbind(gl: WebGLRenderingContext) {
gl.bindTexture(gl.TEXTURE_2D, null);
}
getWidth(): number {
return this.image.width;
}
getHeight(): number {
return this.image.height;
}
}
function error(e: any) {
console.error("IMAGE LOAD ERROR");
console.error(e);
}

129
frontend/www/webgl/util.ts Normal file
View file

@ -0,0 +1,129 @@
export interface Dictionary<T> {
[Key: string]: T;
}
interface OnLoadable {
onload: any;
}
export function onload2promise<T extends OnLoadable>(obj: T): Promise<T> {
return new Promise(resolve => {
obj.onload = () => resolve(obj);
});
}
export function resizeCanvasToDisplaySize(
canvas: HTMLCanvasElement,
multiplier?: number,
): boolean {
multiplier = multiplier || 1;
var width = canvas.clientWidth * multiplier | 0;
var height = canvas.clientHeight * multiplier | 0;
if (canvas.width !== width || canvas.height !== height) {
canvas.width = width;
canvas.height = height;
return true;
}
return false;
}
export class FPSCounter {
last: number;
count: number;
constructor() {
this.last = 0;
this.count = 0;
}
frame(now: number) {
this.count += 1;
if (now - this.last > 1) {
this.last = now;
console.log(this.count + " fps");
this.count = 0;
}
}
}
export class M3 {
_data: any;
constructor(data: any) {
this._data = data;
}
static ident(): M3 {
return new M3([
1, 0, 0,
0, 1, 0,
0, 0, 1
]);
}
multiply(other: M3): M3 {
const a = this._data;
const b = other._data;
var a00 = a[0 * 3 + 0];
var a01 = a[0 * 3 + 1];
var a02 = a[0 * 3 + 2];
var a10 = a[1 * 3 + 0];
var a11 = a[1 * 3 + 1];
var a12 = a[1 * 3 + 2];
var a20 = a[2 * 3 + 0];
var a21 = a[2 * 3 + 1];
var a22 = a[2 * 3 + 2];
var b00 = b[0 * 3 + 0];
var b01 = b[0 * 3 + 1];
var b02 = b[0 * 3 + 2];
var b10 = b[1 * 3 + 0];
var b11 = b[1 * 3 + 1];
var b12 = b[1 * 3 + 2];
var b20 = b[2 * 3 + 0];
var b21 = b[2 * 3 + 1];
var b22 = b[2 * 3 + 2];
return new M3([
b00 * a00 + b01 * a10 + b02 * a20,
b00 * a01 + b01 * a11 + b02 * a21,
b00 * a02 + b01 * a12 + b02 * a22,
b10 * a00 + b11 * a10 + b12 * a20,
b10 * a01 + b11 * a11 + b12 * a21,
b10 * a02 + b11 * a12 + b12 * a22,
b20 * a00 + b21 * a10 + b22 * a20,
b20 * a01 + b21 * a11 + b22 * a21,
b20 * a02 + b21 * a12 + b22 * a22,
]);
}
translation(x: number, y: number): M3 {
const out = [...this._data];
out[6] += x;
out[7] += y;
return new M3(out);
}
rotate(rad: number): M3 {
var c = Math.cos(rad);
var s = Math.sin(rad);
const out = new M3([...this._data]);
return out.multiply(new M3([
c, -s, 0,
s, c, 0,
0, 0, 1
]));
}
scale(s_x: number, s_y = s_x, s_z = 1): M3 {
const out = new M3([...this._data]);
return out.multiply(new M3([
s_x, 0, 0,
0, s_y, 0,
0, 0, s_z,
]));
}
}

View file

View file

@ -0,0 +1,112 @@
import { Buffer, VertexBuffer } from './buffer';
import { Shader } from './shader';
export class VertexBufferElement {
type: number;
amount: number;
type_size: number;
normalized: boolean;
index: string;
constructor(
type: number,
amount: number,
type_size: number,
index: string,
normalized: boolean,
) {
this.type = type;
this.amount = amount;
this.type_size = type_size;
this.normalized = normalized;
this.index = index;
}
}
export class VertexBufferLayout {
elements: VertexBufferElement[];
stride: number;
offset: number;
constructor(offset = 0) {
this.elements = [];
this.stride = 0;
this.offset = offset;
}
// Maybe wrong normalized type
push(
type: number,
amount: number,
type_size: number,
index: string,
normalized = false,
) {
this.elements.push(new VertexBufferElement(type, amount, type_size, index, normalized));
this.stride += amount * type_size;
}
getElements(): VertexBufferElement[] {
return this.elements;
}
getStride(): number {
return this.stride;
}
}
// glEnableVertexAttribArray is to specify what location of the current program the follow data is needed
// glVertexAttribPointer tells gl that that data is at which location in the supplied data
export class VertexArray {
// There is no renderer ID, always at bind buffers and use glVertexAttribPointer
buffers: Buffer[];
layouts: VertexBufferLayout[];
constructor() {
this.buffers = [];
this.layouts = [];
}
addBuffer(vb: VertexBuffer, layout: VertexBufferLayout) {
this.buffers.push(vb);
this.layouts.push(layout);
}
/// Bind buffers providing program data
bind(gl: WebGLRenderingContext, shader: Shader) {
shader.bind(gl);
for(let i = 0; i < this.buffers.length; i ++) {
const buffer = this.buffers[i];
const layout = this.layouts[i];
buffer.bind(gl);
const elements = layout.getElements();
let offset = layout.offset;
for (let j = 0; j < elements.length; j ++) {
const element = elements[j];
const location = shader.getAttribLocation(gl, element.index);
if (location >= 0) {
gl.enableVertexAttribArray(location);
gl.vertexAttribPointer(
location, element.amount, element.type,
element.normalized, layout.stride, offset
);
}
offset += element.amount * element.type_size;
}
}
}
/// Undo bind operation
unbind(gl: WebGLRenderingContext) {
this.layouts.forEach((layout) => {
layout.getElements().forEach((_, index) => {
gl.disableVertexAttribArray(index);
});
})
}
}

View file

@ -2,12 +2,24 @@ const CopyWebpackPlugin = require("copy-webpack-plugin");
const path = require('path'); const path = require('path');
module.exports = { module.exports = {
entry: "./bootstrap.js", mode: 'development',
output: { entry: './bootstrap.js',
path: path.resolve(__dirname, "dist"), module: {
filename: "bootstrap.js", rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: [ '.tsx', '.ts', '.js', '.wasm' ]
},
output: {
filename: 'bootstrap.js',
path: path.resolve(__dirname, 'dist')
}, },
mode: "development",
plugins: [ plugins: [
new CopyWebpackPlugin(['index.html']) new CopyWebpackPlugin(['index.html'])
], ],