Add uploading

This commit is contained in:
Daochen 2020-05-12 21:47:43 -05:00
parent b03ddadf86
commit 131607a3f9
10 changed files with 302 additions and 8 deletions

1
.gitignore vendored
View File

@ -25,3 +25,4 @@ yarn-error.log*
db.sqlite3 db.sqlite3
__pycache__ __pycache__
*.swp *.swp
/uploaded_agents

View File

@ -33,13 +33,16 @@ The definitions of the fields are as follows:
| GET | tournament/launch | `eval_num`, `name` | Launch tournment on the game. Each pair of models will play `eval_num` times. Results will be saved in database. | | GET | tournament/launch | `eval_num`, `name` | Launch tournment on the game. Each pair of models will play `eval_num` times. Results will be saved in database. |
| GET | tournament/query\_game | `name`, `index`, `agent0`, `agent1`, `win`, `payoff` | Query the games with the given parameters | | GET | tournament/query\_game | `name`, `index`, `agent0`, `agent1`, `win`, `payoff` | Query the games with the given parameters |
| GET | tournament/query\_payoff | `name`, `agent0`, `agent1`, `payoff` | Query the payoffs with the given parameters | | GET | tournament/query\_payoff | `name`, `agent0`, `agent1`, `payoff` | Query the payoffs with the given parameters |
| GET | tournament/replay | `name`, `agent0`, `agent1`, `index` | Return the replay data (only support Leduc Holdem for now) | | GET | tournament/replay | `name`, `agent0`, `agent1`, `index` | Return the replay data |
| POST | tournament/upload\_agent | `model`(Python file), `name`, `game`, `entry` | Upload a model file. `name` is model ID, `entry` is the class name of the model |
| GET | tournament/delete\_agent | `name` | Delete the agent of the given name |
| GET | tournament/list\_agents | | list all the agents |
## Example API ## Example API
| API | Description | | API | Description |
|-----------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| |-----------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|
| http://127.0.0.1:8000/tournamentlaunch?eval_num=200&name=leduc-holdem | Evaluate on Leduc Holdem with 200 games for each pair of models | | http://127.0.0.1:8000/tournament/launch?eval_num=200&name=leduc-holdem | Evaluate on Leduc Holdem with 200 games for each pair of models |
| http://127.0.0.1:8000/tournament/replay?name=leduc-holdem&agent0=leduc-holdem-rule-v1&agent1=leduc-holdem-cfr&index=3 | Obtain the replay data between rule model and CFR model. Obtain teh data of the 3rd game | | http://127.0.0.1:8000/tournament/replay?name=leduc-holdem&agent0=leduc-holdem-rule-v1&agent1=leduc-holdem-cfr&index=3 | Obtain the replay data between rule model and CFR model. Obtain the data of the 3rd game |
| http://127.0.0.1:8000/tournament/query_game | Get all the game data | | http://127.0.0.1:8000/tournament/query_game | Get all the game data |
| http://127.0.0.1:8000/tournament/query_game?name=leduc-holdem | Get all the game data of Leduc Holdem | | http://127.0.0.1:8000/tournament/query_game?name=leduc-holdem | Get all the game data of Leduc Holdem |
| http://127.0.0.1:8000/tournament/query_payoff | Get all the payoffs | | http://127.0.0.1:8000/tournament/query_payoff | Get all the payoffs |
@ -55,6 +58,28 @@ Some models have been pre-registered as baselines
| doudizhu-random | doudizhu | A random model | | doudizhu-random | doudizhu | A random model |
| doudizhu-rule-v1 | doudizhu | Dou Dizhu rule model | | doudizhu-rule-v1 | doudizhu | Dou Dizhu rule model |
## Example of uploading a new model
A example model file is prepared:
```
cd server/upload_test
```
Upload the model with `curl`:
```
curl -F 'model=@example_model.py' -F "name=leduc-new" -F "entry=LeducHoldemRuleModelV2" -F "game=leduc-holdem" http://127.0.0.1:8000/tournament/upload_agent
```
Launch the tounament with:
```
curl 'http://127.0.0.1:8000/tournament/launch?eval_num=200&name=leduc-holdem'
```
We list the uploaded agent with
```
curl http://127.0.0.1:8000/tournament/list_agents
```
We can delete the agent with
```
curl 'http://127.0.0.1:8000/tournament/delete_agent?name=leduc-new'
```
# Others # Others
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).

View File

@ -0,0 +1,84 @@
''' Leduc Hold 'em rule model
'''
import rlcard
from rlcard.models.model import Model
class LeducHoldemRuleAgentV2(object):
''' Leduc Hold 'em Rule agent version 2
'''
def __init__(self):
self.use_raw = True
def step(self, state):
''' Predict the action when given raw state. A simple rule-based AI.
Args:
state (dict): Raw state from the game
Returns:
action (str): Predicted action
'''
legal_actions = state['raw_legal_actions']
state = state['raw_obs']
hand = state['hand']
public_card = state['public_card']
action = 'fold'
'''
When having only 2 hand cards at the game start, choose fold to drop terrible cards:
Acceptable hand cards:
Pairs
AK, AQ, AJ, AT
A9s, A8s, ... A2s(s means flush)
KQ, KJ, QJ, JT
Fold all hand types except those mentioned above to save money
'''
if public_card:
if public_card[1] == hand[1]:
action = 'raise'
else:
action = 'fold'
else:
if hand[0] == 'K':
action = 'raise'
elif hand[0] == 'Q':
action = 'check'
else:
action = 'fold'
#return action
if action in legal_actions:
return action
else:
if action == 'raise':
return 'call'
if action == 'check':
return 'fold'
if action == 'call':
return 'raise'
else:
return action
def eval_step(self, state):
return self.step(state), []
class LeducHoldemRuleModelV2(Model):
''' Leduc holdem Rule Model version 2
'''
def __init__(self):
''' Load pretrained model
'''
env = rlcard.make('leduc-holdem')
rule_agent = LeducHoldemRuleAgentV2()
self.rule_agents = [rule_agent for _ in range(env.player_num)]
@property
def agents(self):
''' Get a list of agents for each position in a the game
Returns:
agents (list): A list of agents
Note: Each agent should be just like RL agent with step and eval_step
functioning well.
'''
return self.rule_agents

View File

@ -119,3 +119,5 @@ USE_TZ = True
# https://docs.djangoproject.com/en/2.2/howto/static-files/ # https://docs.djangoproject.com/en/2.2/howto/static-files/
STATIC_URL = '/static/' STATIC_URL = '/static/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

View File

@ -1,4 +1,4 @@
# Generated by Django 2.2.12 on 2020-05-11 05:03 # Generated by Django 2.2.12 on 2020-05-13 02:29
from django.db import migrations, models from django.db import migrations, models
@ -34,4 +34,14 @@ class Migration(migrations.Migration):
('payoff', models.FloatField()), ('payoff', models.FloatField()),
], ],
), ),
migrations.CreateModel(
name='UploadedAgent',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('game', models.CharField(max_length=100)),
('entry', models.CharField(max_length=100)),
('f', models.FileField(upload_to='uploaded_agents')),
],
),
] ]

View File

@ -1,4 +1,5 @@
from django.db import models from django.db import models
from django.conf import settings
class Game(models.Model): class Game(models.Model):
# The name of the game # The name of the game
@ -35,3 +36,16 @@ class Payoff(models.Model):
# The average payoff of the first agent # The average payoff of the first agent
payoff = models.FloatField() payoff = models.FloatField()
class UploadedAgent(models.Model):
# The name of the agent
name = models.CharField(max_length=100)
# The game of the agent
game = models.CharField(max_length=100)
# The class name of the Model
entry = models.CharField(max_length=100)
# File
f = models.FileField(upload_to='uploaded_agents')

View File

@ -22,3 +22,5 @@ MODEL_IDS['doudizhu'] = [
'doudizhu-rule-v1', 'doudizhu-rule-v1',
] ]

View File

@ -7,4 +7,7 @@ urlpatterns = [
path('launch', views.launch, name='launch'), path('launch', views.launch, name='launch'),
path('query_payoff', views.query_payoff, name='query_payoff'), path('query_payoff', views.query_payoff, name='query_payoff'),
path('query_game', views.query_game, name='query_game'), path('query_game', views.query_game, name='query_game'),
path('upload_agent', views.upload_agent, name='upload_agent'),
path('delete_agent', views.delete_agent, name='delete_agent'),
path('list_agents', views.list_agents, name='list_agents'),
] ]

View File

@ -1,13 +1,46 @@
import json
import os
import importlib.util
from django.shortcuts import render from django.shortcuts import render
from django.http import HttpResponse from django.http import HttpResponse
from django.db import transaction from django.db import transaction
from django.core import serializers from django.core import serializers
from django.views.decorators.csrf import csrf_exempt
from .models import Game, Payoff from django.dispatch import receiver
from django.db import models
from django.conf import settings
from .rlcard_wrap import rlcard, MODEL_IDS from .rlcard_wrap import rlcard, MODEL_IDS
from .models import Game, Payoff, UploadedAgent
from .tournament import Tournament from .tournament import Tournament
def reset_model_ids():
from .rlcard_wrap import rlcard, MODEL_IDS
agents = UploadedAgent.objects.all()
for agent in agents:
path = os.path.join(settings.MEDIA_ROOT, agent.f.name)
name = agent.name
game = agent.game
entry = agent.entry
module_name = path.split('/')[-1].split('.')[0]
spec = importlib.util.spec_from_file_location(module_name, path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
M = getattr(module, entry)
class ModelSpec(object):
def __init__(self):
self.model_id = name
self._entry_point = M
def load(self):
model = self._entry_point()
return model
rlcard.models.registration.model_registry.model_specs[name] = ModelSpec()
MODEL_IDS[game].append(name)
def replay(request): def replay(request):
if request.method == 'GET': if request.method == 'GET':
name = request.GET['name'] name = request.GET['name']
@ -56,5 +89,41 @@ def launch(request):
agent1=payoff_data['agent1'], agent1=payoff_data['agent1'],
payoff=payoff_data['payoff']) payoff=payoff_data['payoff'])
p.save() p.save()
return HttpResponse(1) return HttpResponse(json.dumps({'value': 0, 'info': 'success'}))
@csrf_exempt
def upload_agent(request):
if request.method == 'POST':
f = request.FILES['model']
name = request.POST['name']
game = request.POST['game']
entry = request.POST['entry']
if UploadedAgent.objects.filter(name=name).exists():
return HttpResponse(json.dumps({'value': -1, 'info': 'name exists'}))
a = UploadedAgent(name=name, game=game, f=f, entry=entry)
a.save()
reset_model_ids()
return HttpResponse(json.dumps({'value': 0, 'info': 'success'}))
def delete_agent(request):
if request.method == 'GET':
name = request.GET['name']
if not UploadedAgent.objects.filter(name=name).exists():
return HttpResponse(json.dumps({'value': -1, 'info': 'name not exists'}))
UploadedAgent.objects.filter(name=name).delete()
reset_model_ids()
return HttpResponse(json.dumps({'value': 0, 'info': 'success'}))
def list_agents(request):
if request.method == 'GET':
agents = UploadedAgent.objects.all()
result = serializers.serialize('json', agents)
return HttpResponse(result)
@receiver(models.signals.post_delete, sender=UploadedAgent)
def auto_delete_file_on_delete(sender, instance, **kwargs):
if instance.f:
if os.path.isfile(instance.f.path):
os.remove(instance.f.path)

View File

@ -0,0 +1,84 @@
''' Leduc Hold 'em rule model
'''
import rlcard
from rlcard.models.model import Model
class LeducHoldemRuleAgentV2(object):
''' Leduc Hold 'em Rule agent version 2
'''
def __init__(self):
self.use_raw = True
def step(self, state):
''' Predict the action when given raw state. A simple rule-based AI.
Args:
state (dict): Raw state from the game
Returns:
action (str): Predicted action
'''
legal_actions = state['raw_legal_actions']
state = state['raw_obs']
hand = state['hand']
public_card = state['public_card']
action = 'fold'
'''
When having only 2 hand cards at the game start, choose fold to drop terrible cards:
Acceptable hand cards:
Pairs
AK, AQ, AJ, AT
A9s, A8s, ... A2s(s means flush)
KQ, KJ, QJ, JT
Fold all hand types except those mentioned above to save money
'''
if public_card:
if public_card[1] == hand[1]:
action = 'raise'
else:
action = 'fold'
else:
if hand[0] == 'K':
action = 'raise'
elif hand[0] == 'Q':
action = 'check'
else:
action = 'fold'
#return action
if action in legal_actions:
return action
else:
if action == 'raise':
return 'call'
if action == 'check':
return 'fold'
if action == 'call':
return 'raise'
else:
return action
def eval_step(self, state):
return self.step(state), []
class LeducHoldemRuleModelV2(Model):
''' Leduc holdem Rule Model version 2
'''
def __init__(self):
''' Load pretrained model
'''
env = rlcard.make('leduc-holdem')
rule_agent = LeducHoldemRuleAgentV2()
self.rule_agents = [rule_agent for _ in range(env.player_num)]
@property
def agents(self):
''' Get a list of agents for each position in a the game
Returns:
agents (list): A list of agents
Note: Each agent should be just like RL agent with step and eval_step
functioning well.
'''
return self.rule_agents