Skip to content

Commit

Permalink
feat(jsbattle-webpage): add league opponents to sandbox mode
Browse files Browse the repository at this point in the history
  • Loading branch information
jamro committed Jun 3, 2020
1 parent e369fc5 commit d5966a1
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 30 deletions.
2 changes: 2 additions & 0 deletions packages/jsbattle-server/app/services/ApiGateway.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ class ApiGatewayService extends Service {
"GET league/": "league.getLeagueSummary",
"GET league/replay/:id": "battleStore.get",
"GET league/submission": "league.getUserSubmission",
"GET league/ranktable": "league.getUserRankTable",
"GET league/scripts/:id": "league.getScript",
"PATCH league/submission": "league.joinLeague",
"DELETE league/submission": "league.leaveLeague",
},
Expand Down
45 changes: 42 additions & 3 deletions packages/jsbattle-server/app/services/League.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,11 @@ class LeagueService extends Service {
seedLeague: this.seedLeague,
getUserSubmission: this.getUserSubmission,
getHistory: this.getHistory,
getScript: this.getScript,
joinLeague: this.joinLeague,
leaveLeague: this.leaveLeague,
getLeagueSummary: this.getLeagueSummary,
getUserRankTable: this.getUserRankTable,
updateRank: this.updateRank
},
hooks: {
Expand Down Expand Up @@ -108,6 +110,31 @@ class LeagueService extends Service {
});
}


async getScript(ctx) {
const userId = ctx.meta.user ? ctx.meta.user.id : null;
if(!userId) {
throw new ValidationError('Not Authorized!', 401);
}

const scriptId = ctx.params.id
let response = await ctx.call('league.get', {
id: scriptId,
fields: [
"id",
"ownerName",
"scriptName",
"code"
]
});

return {
id: response.id,
scriptName: response.ownerName + '/' + response.scriptName,
code: response.code
};
}

async getHistory(ctx) {
let items = await ctx.call('battleStore.find', {
sort: '-createdAt',
Expand Down Expand Up @@ -349,7 +376,7 @@ class LeagueService extends Service {
return this.getLeagueSummary(ctx);
}

async getLeagueSummary(ctx) {
async getUserRankTable(ctx) {
const userId = ctx.meta.user ? ctx.meta.user.id : null;
if(!userId) {
throw new ValidationError('Not Authorized!', 401);
Expand All @@ -370,12 +397,24 @@ class LeagueService extends Service {
"history"
];

let submission = await this.getUserSubmission(ctx)
let submission = await this.getUserSubmission(ctx);
submission = _.pick(submission, fields);

return {
submission,
ranktable: this.ranktable.slice(submission.id, 7),
ranktable: this.ranktable.slice(submission.id, 7)
}
}

async getLeagueSummary(ctx) {
const userId = ctx.meta.user ? ctx.meta.user.id : null;
if(!userId) {
throw new ValidationError('Not Authorized!', 401);
}

let result = await this.getUserRankTable(ctx);
return {
...result,
history: await ctx.call('league.getHistory', {})
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/jsbattle-webpage/features/schema.feature
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ Feature: UBD Schema
| 1 |
| 2 |
| 3 |
| 4 |
39 changes: 34 additions & 5 deletions packages/jsbattle-webpage/src/actions/sandboxAction.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,24 @@ export const getSandboxOpponentList = (useRemoteService) => {
if(!result.ok) {
throw new Error(`Error ${result.status}: ${result.statusText} (url: ${result.url})`);
}
userTankList = await result.json();
userTankList = userTankList.map((script) => ({
result = await result.json();
userTankList = result.map((script) => ({
id: script.id,
label: "sandbox/" + script.scriptName,
label: script.scriptName,
source: 'remote_user'
}));
result = await fetch("/api/user/league/ranktable", {});
if(!result.ok) {
throw new Error(`Error ${result.status}: ${result.statusText} (url: ${result.url})`);
}
result = await result.json();
result = result.ranktable.map((item) => ({
id: item.id,
label: `${item.rank}. ${item.ownerName}/${item.scriptName}`,
source: 'league'
}));
userTankList = userTankList.concat(result);

} catch(err) {
console.log(err);
return dispatch({
Expand All @@ -144,7 +156,7 @@ export const getSandboxOpponentList = (useRemoteService) => {
userTankList = await aiRepoService.getScriptNameList();
userTankList = userTankList.map((script) => ({
id: script.id,
label: 'sandbox/' + script.scriptName,
label: script.scriptName,
source: 'local_user'
}));
}
Expand All @@ -154,7 +166,7 @@ export const getSandboxOpponentList = (useRemoteService) => {

bundledTanks = bundledTanks.map((name) => ({
id: name,
label: "jsbattle/" + name,
label: name,
source: "bundled"
}));

Expand Down Expand Up @@ -197,6 +209,23 @@ export const setSandboxOpponent = (source, id) => {
});
}
break;
case 'league':
try {
script = await fetch("/api/user/league/scripts/" + id);
if(!script.ok) {
throw new Error(`Error ${script.status}: ${script.statusText} (url: ${script.url})`);
}
script = await script.json();
scriptName = script.scriptName;
scriptCode = script.code;
} catch(err) {
console.log(err);
return dispatch({
type: SANDBOX_OPPONENT_LIST_FAILURE,
payload: err
});
}
break;
default:
throw new Error(`Not supported source ${source}`);
}
Expand Down
15 changes: 13 additions & 2 deletions packages/jsbattle-webpage/src/components/LeagueJoin.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@ export default class LeagueJoin extends React.Component {
submissionId = props.tankList[0].id;
}

let rejoin;
if(this.props.selected) {
rejoin = (submissionId == this.props.selected.scriptId);
}

this.state = {
editMode: false,
rejoin,
newSubmissionId: submissionId,
};
}
Expand All @@ -35,7 +41,12 @@ export default class LeagueJoin extends React.Component {
}

onSubmissionChange(event) {
this.setState({newSubmissionId: event.target.value});
let rejoin = false;
if(this.props.selected) {
rejoin = (event.target.value == this.props.selected.scriptId);
}

this.setState({newSubmissionId: event.target.value, rejoin});
}

openEditor() {
Expand Down Expand Up @@ -68,7 +79,7 @@ export default class LeagueJoin extends React.Component {
let leaveButton;
let tankSelect;
if(this.props.tankList.length > 0) {
joinButton = <p><button className="btn btn-primary btn-lg league-join" style={{width: "100%"}} onClick={() => this.join()}><i className="fas fa-check"></i> Join</button></p>;
joinButton = <p><button className="btn btn-primary btn-lg league-join" style={{width: "100%"}} onClick={() => this.join()}><i className="fas fa-check"></i> {this.state.rejoin ? 'Re-join' : 'Join'}</button></p>;
tankSelect = <div>
<small style={{color: '#888'}}>your tank: </small>
<select className="custom-select custom-select-lg mb-3" style={{width: "100%"}} value={this.state.newSubmissionId} onChange={(e) => this.onSubmissionChange(e)}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,55 @@ import Row from './Row.js';

class LiveCodeSandboxSettingsTab extends React.Component {

render() {
let opponents = this.props.opponents.map((opponent, index) => <option key={opponent.id} value={index}>{opponent.label}</option>);
onCategoryChange(category) {
let opponent = this.props.opponents.find((op) => op.source == category);
this.props.onOpponentChange(opponent);
}

render() {
let selectedCategory = this.props.selectedOpponent ? this.props.selectedOpponent.source : 'bundled';
let opponents = this.props.opponents
.map((opponent, index) => ({
...opponent,
index
}))
.filter((opponent) => opponent.source == selectedCategory)
.map((opponent) => <option key={opponent.id} value={opponent.index}>{opponent.label}</option>);
let selectedIndex = this.props.opponents.findIndex((opponent) => (opponent.id == this.props.selectedOpponent.id && opponent.source == this.props.selectedOpponent.source));

let categories = this.props.opponents.reduce((result, value) => {
if(result.indexOf(value.source) == -1) {
result.push(value.source);
}
return result;
}, []);
const categoryLabels = {
'bundled': 'Bundled',
'local_user': 'Sandbox',
'remote_user': 'Sandbox',
'league': 'League'
};

categories = categories.map((item) => <option key={item} value={item}>{categoryLabels[item]}</option>);
return <Row>
<Col sm={12}>
<div className="card" style={{marginTop: '1em'}}>
<div className="card-body debug-container" style={{padding: '1em'}}>
<form>
<div className="form-group">
<label htmlFor="opponent"><i className="fas fa-crosshairs"></i> Opponent</label>
<select className="form-control" id="opponent" value={selectedIndex} onChange={(e) => this.props.onOpponentChange(this.props.opponents[e.target.value])}>
{opponents}
</select>
<Row>
<Col md={5}>
<select className="form-control" id="category" value={selectedCategory} onChange={(e) => this.onCategoryChange(e.target.value)}>
{categories}
</select>
</Col>
<Col md={7}>
<select className="form-control" id="opponent" value={selectedIndex} onChange={(e) => this.props.onOpponentChange(this.props.opponents[e.target.value])}>
{opponents}
</select>
</Col>
</Row>
</div>
<div className="form-group">
<label htmlFor="opponent"><i className="fas fa-users-cog"></i> Mode</label>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'babel-polyfill';
import React from 'react';
import {shallow} from 'enzyme';
import LiveCodeSandboxSettingsTab from '../LiveCodeSandboxSettingsTab.js';
Expand All @@ -10,32 +11,38 @@ test('LiveCodeSandboxSettingsTab renders properly', () => {

test('list opponents', () => {
const opponents = [
{ id: 'op1', label: 'alpha753' },
{ id: 'op2', label: 'beta78732' }
{ id: 'op1', label: 'alpha753', source: 'bundled' },
{ id: 'op2', label: 'beta78732', source: 'league' },
{ id: 'op3', label: 'gamma8784', source: 'league' },
]
const wrapper = shallow(<LiveCodeSandboxSettingsTab
selectedOpponent={{ id: 'op2', label: 'beta78732' }}
selectedOpponent={{ id: 'op2', label: 'beta78732', source: 'league' }}
opponents={opponents}
/>);
expect(wrapper.find('#opponent').text()).toMatch(/alpha753/)
expect(wrapper.find('#category').text()).toMatch(/League/)
expect(wrapper.find('#opponent').text()).toMatch(/beta78732/)
expect(wrapper.find('#opponent').text()).toMatch(/gamma8784/)
expect(wrapper.find('#opponent').props().value).toBe(1);
});

test('change opponent', () => {
test('change opponent', async () => {
const opponents = [
{ id: 'op532234', label: 'alpha98324' },
{ id: 'op632243', label: 'beta77532' }
{ id: 'op532234', label: 'alpha98324', source: 'league' },
{ id: 'op6543', label: 'omega98324', source: 'league' },
{ id: 'op2345', label: 'phi77532', source: 'bundled' },
{ id: 'op632243', label: 'beta77532', source: 'bundled' },
];
const onOpponentChange = jest.fn();
const wrapper = shallow(<LiveCodeSandboxSettingsTab
selectedOpponent={{ id: 'op532234', label: 'alpha98324' }}
opponents={opponents}
onOpponentChange={onOpponentChange}
/>);
wrapper.find('#opponent').simulate('change', {target: { value : 1}});
expect(onOpponentChange.mock.calls).toHaveLength(1);
expect(onOpponentChange.mock.calls[0][0]).toHaveProperty('id', 'op632243');
await wrapper.find('#category').simulate('change', {target: { value : 'league'}});
await wrapper.find('#opponent').simulate('change', {target: { value : 1}});
expect(onOpponentChange.mock.calls).toHaveLength(2);
expect(onOpponentChange.mock.calls[0][0]).toHaveProperty('id', 'op532234');
expect(onOpponentChange.mock.calls[1][0]).toHaveProperty('id', 'op6543');
});

test('change mode', () => {
Expand Down
12 changes: 7 additions & 5 deletions packages/jsbattle-webpage/src/containers/SandboxScreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export class SandboxScreen extends React.Component {
case 'bundled':
aiDef.fromFile(opponent.name);
break;
case 'league':
case 'local_user':
case 'remote_user':
aiDef.fromCode(opponent.name, opponent.code);
Expand Down Expand Up @@ -136,17 +137,18 @@ export class SandboxScreen extends React.Component {

renderSettingsTab() {
let selectedOpponent;
if((this.props.opponent.source == 'local_user' || this.props.opponent.source == 'remote_user') && this.props.opponent.name == this.props.script.scriptName) {
selectedOpponent = {source: 'bundled', id: 'dummy'};
} else {
let opponentList = this.props.opponentList.filter((opponent) => !((opponent.source == 'local_user' || opponent.source == 'remote_user') && opponent.label == this.props.script.scriptName));

if(opponentList.find((opponent) => opponent.source == this.props.opponent.source && opponent.id == this.props.opponent.id)) {
selectedOpponent = {source: this.props.opponent.source, id: this.props.opponent.id};
} else {
selectedOpponent = {source: 'bundled', id: 'dummy'};
}

return <LiveCodeSandboxSettingsTab
rngSeed={this.state.rngSeed}
isRngLocked={this.props.lockRng}
mode={this.props.mode}
opponents={this.props.opponentList}
opponents={opponentList}
selectedOpponent={selectedOpponent}
onBattleModeChange={(isTeam) => this.props.setSandboxBattleMode(isTeam)}
onOpponentChange={(opponent) => this.onOpponentChange(opponent)}
Expand Down

0 comments on commit d5966a1

Please sign in to comment.