Skip to content

Commit

Permalink
Merge pull request #1 from cscenter/add_docker
Browse files Browse the repository at this point in the history
Add docker
  • Loading branch information
izhleba authored Apr 28, 2020
2 parents 3684155 + f6800bf commit b60d8be
Show file tree
Hide file tree
Showing 11 changed files with 63 additions and 74 deletions.
1 change: 1 addition & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ verify_ssl = true
[packages]
pyzmq = "==19.0.0"
numpy = ">=1.18.0"
scikit-learn = ">=0.22.2"

[requires]
python_version = "3.7"
72 changes: 0 additions & 72 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,78 +50,6 @@
```
В выводе смотрим статистику и распределенеи мест по агентам.
## Создание своего агента
### Структура класса
Для написания своего агента, необходимо создать класс, отнаследовавшись от базового класса
`agent.base.BaseAgent`. Далее нужно реализовать следующие методы:
- `def get_my_name(self)` возвращяет строкой ваше имя и фамилию
- `def offer_action(self, data)` возващяет размер вашего предложения (int) некоторому агенту в раунде.
Данные об агенте и общем размере разделяемых средства находятся в `data`
структура класса описана здесь `base.protocol.OfferRequest`. Размером вашего предложения `offer` это то сколько
вы предлагаете оставить другому агенту, в случае принятия предложения, ваша награда будет равна `total_amount - offer`.
- `deal_action(self, data)` возвращяет ответ (bool) согласны ли вы на предложение
от некоторого агента. Данные об агенте и о размере предложения находятся в `data`
структура класса описана здесь `base.protocol.DealRequest`
- `def round_result_action(self, data: RoundResult)` метод вызывается по окончанию раунда, собщяет общие
результаты в `data` описание `base.protocol.RoundResult`.
### Структуры данных и их поля
#### OfferRequest
```
round_id: int - номер раунда
target_agent_uid: str - идентификатор агента которому будет отосланно предложение
total_amount: int - общее количество которое необходимо разделить
```
#### DealRequest
```
round_id: int - номер раунда
from_agent_uid: str - идентификатор агента от которого поступило предложение
total_amount: int - общий размер разделяемых средств
offer: int - предложение от агента
```
#### RoundResult
```
round_id: int - номер раунда
win: bool - True если раунд успешен и предложение принято, False - в противном случае
agent_gain: Dict[str, int] - ассоциативный массив с размерами наград, ключ - идентификатор агентов раунда
disconnection_failure: bool - флаг показывает, что во время раунда произошел дисконект одного из участников
```
### Приме агента
Простой агент без памяти, который всегда делит поровну и принимает любое не нулевое предложение.
```python
class DummyAgent(BaseAgent):
def get_my_name(self):
return 'Dummy'
def offer_action(self, m):
return m.total_amount // 2
def deal_action(self, m):
if m.offer > 0:
return True
else:
return False
def round_result_action(self, data):
pass
```

Пример запуска агентов смотрите в run_local_example.sh

### Общие правила, рекомендации и советы
1. В качестве идентификаторов агентоа используются строковые представления UUID. После того как подключатся все агенты,
сервер оповестит вас о вашем uid, его можно будет узнать из поля agent_id. Обратите внимание, после инициализации класса
это поле будет `None` до оповещения от сервера.
2. Если агент не отвечает более чем 2 секунды, он удаляется из обработки, никакие дальнейшие его сообщения не обрабатываются.
3. Если нужна длительная инициализация, проведите ее в методе __init__, так как сервер будет 1 минуту ждать инициализации всех агентов.
4. Если клиент не ответил, что он готов к работе в течении минуты, то он удаляется из игры.
5. Агент должен не упасть с ошибкой или не дисконектнуться хотябы в 1% раундов чтобы его очки учитывались.
6. За любые попытки прямого использования сетевого протокола игры, будет засчитан провал задания.


## Решение частых проблем
Порой при тестировании агентов, некоторые процессы с ними не умирают,
Expand Down
2 changes: 1 addition & 1 deletion base/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ class DealResponse(MessageOutPayload):
accepted: bool

def find_error(self) -> Optional[str]:
if not self.accepted:
if self.accepted is None:
return "Non empty 'accepted' required"
if not isinstance(self.accepted, bool):
return "Bool type for 'accepted' required"
Expand Down
31 changes: 31 additions & 0 deletions docker-compose-game.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
version: "3"
services:
ai_server:
image: "ai_server"
container_name: "ai_server"
environment:
- SERVER_URL=*
- SERVER_PORT=4181
- TOTAL_OFFER=100
- TOTAL_ROUNDS=100
- CLIENTS_AMOUNT=2
- RESPONSE_TIMEOUT=2
- LOG_LEVEL=debug
ports:
- "4181:4181"
ai_agent_1:
image: "ai_agent"
container_name: "ai_agent_1"
environment:
- SERVER_URL=ai_server
- SERVER_PORT=4181
- AGENT_CLS_PATH=agent.chaotic.ChaoticAgent
- LOG_LEVEL=debug
ai_agent_2:
image: "ai_agent"
container_name: "ai_agent_2"
environment:
- SERVER_URL=ai_server
- SERVER_PORT=4181
- AGENT_CLS_PATH=agent.chaotic.ChaoticAgent
- LOG_LEVEL=debug
Empty file added docker/client/.dockerignore
Empty file.
10 changes: 10 additions & 0 deletions docker/client/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM python:3

WORKDIR /usr/src/app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD [ "python", "./client.py" ]
Empty file added docker/server/.dockerignore
Empty file.
10 changes: 10 additions & 0 deletions docker/server/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM python:3

WORKDIR /usr/src/app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

CMD [ "python", "./server.py" ]
5 changes: 4 additions & 1 deletion server.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,10 @@ async def inner_handler():
if connection_uid in wait_for_rounds and raw_msg.get('msg_type', None) == MessageOutType.OFFER_RESPONSE:
r = wait_for_rounds[connection_uid]
offer_response = OfferResponse(**raw_msg['payload'])
error = offer_response.find_error()
if offer_response.offer > r.total_amount:
error = f"Offer {offer_response.offer} is to large"
else:
error = offer_response.find_error()
if error:
logging.error(f"Wrong offer response from {connection_uid} skip it. Error: {error}")
else:
Expand Down
3 changes: 3 additions & 0 deletions submit_agent.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

docker build -t ai_agent -f docker/client/Dockerfile .
3 changes: 3 additions & 0 deletions submit_server.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

docker build -t ai_server -f docker/server/Dockerfile .

0 comments on commit b60d8be

Please sign in to comment.