Add uploading
This commit is contained in:
parent
b03ddadf86
commit
131607a3f9
|
@ -25,3 +25,4 @@ yarn-error.log*
|
||||||
db.sqlite3
|
db.sqlite3
|
||||||
__pycache__
|
__pycache__
|
||||||
*.swp
|
*.swp
|
||||||
|
/uploaded_agents
|
||||||
|
|
33
README.md
33
README.md
|
@ -30,16 +30,19 @@ The definitions of the fields are as follows:
|
||||||
|
|
||||||
| type | Resource | Parameters | Description |
|
| type | Resource | Parameters | Description |
|
||||||
|------|---------------------------|------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|
|
|------|---------------------------|------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|
|
||||||
| 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).
|
||||||
|
|
|
@ -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
|
|
@ -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')
|
||||||
|
|
||||||
|
|
|
@ -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')),
|
||||||
|
],
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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')
|
||||||
|
|
||||||
|
|
|
@ -22,3 +22,5 @@ MODEL_IDS['doudizhu'] = [
|
||||||
'doudizhu-rule-v1',
|
'doudizhu-rule-v1',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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'),
|
||||||
]
|
]
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
Loading…
Reference in New Issue