Skip to content

Commit 5be7ec1

Browse files
committed
feat(jsbattle-webpage): add description to each challenge
1 parent e0d3765 commit 5be7ec1

File tree

13 files changed

+298
-52
lines changed

13 files changed

+298
-52
lines changed

packages/jsbattle-docs/docs/manual/algorithms_aiming.md

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ The same rule applies to the radar. Its rotation is also relative to the body (*
1414
let radarAngle = Math.deg.normalize(90 - state.angle);
1515
```
1616

17+
Notice that results are normalized. Learn more about it [here](./algorithms_geometry.md).
18+
1719
## Aim at the target angle
1820

1921
Once you know the angle where the tank should point its gun, we need to write a proper algorithm in `tank.loop(...)` to start to move it in the proper direction and stop when it is on the target. Two examples are shown here. In both cases, we will be pointing the gun at the north (`targetAngle = -90`).

packages/jsbattle-webpage/features/challenge.feature

+82-8
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,44 @@ Feature: Challenge
44
Scenario: List all challenges
55
Given JsBattle open in the browser
66
And "Challenges" section open
7-
Then list of challenges contains 11 items
7+
Then list of challenges contains 10 items
88
And challenge [1] are unlocked
9-
And challenge [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] are locked
10-
And challenge [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] are incomplete
9+
And challenge [2, 3, 4, 5, 6, 7, 8, 9, 10] are locked
10+
And challenge [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] are incomplete
1111

12-
Scenario: Lose a challenge
12+
Scenario Outline: Lose challenge #<index>
1313
Given JsBattle open in the browser
1414
And "Challenges" section open
15+
And all challenges unlocked
1516
And battle quality set to "0"
1617
And battle speed set to "50"
17-
When open challenge 1
18+
When open challenge <index>
19+
And close challenge info
1820
And start current challenge
1921
And battle view is displayed
2022
And battle is completed
2123
Then the challenge is lost
2224

25+
Examples:
26+
| index |
27+
| 1 |
28+
| 2 |
29+
| 3 |
30+
| 4 |
31+
| 5 |
32+
| 6 |
33+
| 7 |
34+
| 8 |
35+
| 9 |
36+
| 10 |
37+
2338
Scenario: Win a challenge
2439
Given JsBattle open in the browser
2540
And "Challenges" section open
2641
And battle quality set to "0"
2742
And battle speed set to "50"
2843
When open challenge 1
44+
And close challenge info
2945
And type "<<jamro code>>" in AI Script editor
3046
And start current challenge
3147
And battle view is displayed
@@ -38,14 +54,72 @@ Feature: Challenge
3854
And battle quality set to "0"
3955
And battle speed set to "50"
4056
When open challenge 1
57+
And close challenge info
4158
And type "<<jamro code>>" in AI Script editor
4259
And start current challenge
4360
And battle view is displayed
4461
And battle is completed
4562
And the challenge is won
4663
And click next challenge
47-
Then list of challenges contains 11 items
64+
Then list of challenges contains 10 items
4865
And challenge [1, 2] are unlocked
49-
And challenge [3, 4, 5, 6, 7, 8, 9, 10, 11] are locked
66+
And challenge [3, 4, 5, 6, 7, 8, 9, 10] are locked
5067
And challenge [1] are complete
51-
And challenge [2, 3, 4, 5, 6, 7, 8, 9, 10, 11] are incomplete
68+
And challenge [2, 3, 4, 5, 6, 7, 8, 9, 10] are incomplete
69+
70+
Scenario: Open challenge info modal
71+
Given JsBattle open in the browser
72+
And all challenges unlocked
73+
And "Challenges" section open
74+
When open challenge 1
75+
And close challenge info
76+
And open challenge info
77+
Then challenge info is shown
78+
And challenge info description is not empty
79+
80+
Scenario: Close challenge info modal
81+
Given JsBattle open in the browser
82+
And all challenges unlocked
83+
And "Challenges" section open
84+
When open challenge 1
85+
And close challenge info
86+
Then challenge info is not shown
87+
88+
89+
Scenario: Show challenge info only at the beginning
90+
Given JsBattle open in the browser
91+
And "Challenges" section open
92+
And all challenges unlocked
93+
And battle quality set to "0"
94+
And battle speed set to "50"
95+
When open challenge 1
96+
And challenge info is shown
97+
And close challenge info
98+
And start current challenge
99+
And battle view is displayed
100+
And battle is completed
101+
And the challenge is lost
102+
And retry challenge
103+
Then challenge info is not shown
104+
105+
Scenario Outline: Show challenge #<index> info
106+
Given JsBattle open in the browser
107+
And all challenges unlocked
108+
And "Challenges" section open
109+
When open challenge <index>
110+
Then challenge info is shown
111+
And challenge info description is not empty
112+
And all links in challenge info work
113+
114+
Examples:
115+
| index |
116+
| 1 |
117+
| 2 |
118+
| 3 |
119+
| 4 |
120+
| 5 |
121+
| 6 |
122+
| 7 |
123+
| 8 |
124+
| 9 |
125+
| 10 |

packages/jsbattle-webpage/features/google_analytics.feature

+14-3
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,29 @@ Feature: Statistics
1313
When open challenge 1
1414
Then GA event "challenges/challenge_1/open" is sent
1515

16-
Scenario: Notify about starting a challenge battle
16+
Scenario Outline: Notify about starting challenge #<index> battle
1717
Given JsBattle open in the browser
18+
And all challenges unlocked
1819
And "Challenges" section open
19-
When open challenge 1
20+
When open challenge <index>
21+
And close challenge info
2022
And start current challenge
21-
Then GA event "challenges/challenge_1/battle" is sent
23+
Then GA event "challenges/challenge_<index>/battle" is sent
24+
25+
Examples:
26+
| index |
27+
| 1 |
28+
| 2 |
29+
| 5 |
30+
| 8 |
2231

2332
Scenario: Notify about winning a challenge
2433
Given JsBattle open in the browser
2534
And "Challenges" section open
2635
And battle quality set to "0"
2736
And battle speed set to "50"
2837
When open challenge 1
38+
And close challenge info
2939
And type "<<jamro code>>" in AI Script editor
3040
And start current challenge
3141
And battle view is displayed
@@ -39,6 +49,7 @@ Feature: Statistics
3949
And battle quality set to "0"
4050
And battle speed set to "50"
4151
When open challenge 1
52+
And close challenge info
4253
And start current challenge
4354
And battle view is displayed
4455
And battle is completed

packages/jsbattle-webpage/features/step_definitions/challenge_steps.js

+88
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@ var challengeHelper = {
2323
}
2424
};
2525
// GIVEN -----------------------------------------------------------------------
26+
Given('all challenges unlocked', async function () {
27+
await this.client.page.evaluate(() => {
28+
appController.unlockAllChallenges();
29+
});
30+
});
2631

2732
// WHEN ------------------------------------------------------------------------
2833
When('open challenge {int}', async function (index) {
@@ -31,6 +36,12 @@ When('open challenge {int}', async function (index) {
3136
await this.client.page.click(css);
3237
});
3338

39+
When('retry challenge', async function () {
40+
let css = ".retry-challenge";
41+
await this.client.page.waitFor(css);
42+
await this.client.page.click(css);
43+
});
44+
3445
When('start current challenge', async function () {
3546
let css = ".editor-fight";
3647
await this.client.page.waitFor(css);
@@ -43,6 +54,31 @@ When('click next challenge', async function () {
4354
await this.client.page.click(css);
4455
});
4556

57+
When('close challenge info', async function () {
58+
function delay(time) {
59+
return new Promise(function(resolve) {
60+
setTimeout(resolve, time)
61+
});
62+
}
63+
let css = ".close-challenge-info";
64+
await this.client.page.waitForSelector(css, {visible:true});
65+
await delay(300)
66+
await this.client.page.click(css);
67+
await delay(300)
68+
});
69+
70+
When('open challenge info', async function () {
71+
function delay(time) {
72+
return new Promise(function(resolve) {
73+
setTimeout(resolve, time)
74+
});
75+
}
76+
let css = ".open-challenge-info";
77+
await this.client.page.waitForSelector(css, {visible:true});
78+
await this.client.page.click(css);
79+
await delay(300)
80+
});
81+
4682
// THEN ------------------------------------------------------------------------
4783
Then('list of challenges contains {int} items', async function (count) {
4884
let data = await challengeHelper.getChallengeList(this.client.page);
@@ -102,3 +138,55 @@ Then('the challenge is won', async function () {
102138
});
103139
expect(result).to.be.ok;
104140
});
141+
142+
Then('challenge info is shown', async function () {
143+
await this.client.page.waitFor('.modal', {visible: true});
144+
let result = await this.client.page.evaluate(() => {
145+
const element = document.querySelector('.modal');
146+
return (element != null);
147+
});
148+
expect(result).to.be.ok;
149+
});
150+
151+
Then('challenge info is not shown', async function () {
152+
function delay(time) {
153+
return new Promise(function(resolve) {
154+
setTimeout(resolve, time)
155+
});
156+
}
157+
await delay(500);
158+
await this.client.page.waitFor('.modal', {visible: false});
159+
let result = await this.client.page.evaluate(() => {
160+
const element = document.querySelector('.modal');
161+
return (element != null);
162+
});
163+
expect(result).to.be.ok;
164+
});
165+
166+
Then('challenge info description is not empty', async function () {
167+
await this.client.page.waitFor('.modal-body', {visible: true});
168+
let result = await this.client.page.evaluate(() => {
169+
const html = document.querySelector('.modal-body').innerHTML;;
170+
return html;
171+
});
172+
expect(result).to.be.not.empty;
173+
});
174+
175+
Then('all links in challenge info work', async function () {
176+
await this.client.page.waitFor('.modal-body', {visible: true});
177+
let result = await this.client.page.evaluate(() => {
178+
const links = document.querySelectorAll('.modal-body a');
179+
return Object.values(links).map(el => el.href);
180+
});
181+
182+
let testPage = await this.client.browser.newPage();
183+
testPage.on('response', (response) => {
184+
expect(response.ok(), "Status " + response.status() + " during opening " + response.url()).to.be.ok;
185+
})
186+
for(let url of result) {
187+
await testPage.goto(url);
188+
await testPage.waitFor('body');
189+
}
190+
await testPage.close();
191+
192+
});

packages/jsbattle-webpage/src/components/App.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export default class App extends React.Component {
109109
case 'CHALLENGE_LIST':
110110
return <ChallengesListScreen
111111
{...this.state.challenges}
112-
onChallengeOpen={(id) => this.controller.openChallengeEditor(id)}
112+
onChallengeOpen={(id) => this.controller.openChallengeEditor(id, true)}
113113
/>;
114114
case 'CHALLENGE_EDITOR':
115115
return <ChallengeEditorScreen

packages/jsbattle-webpage/src/components/screen/challenges/ChallengeEditorScreen.js

+48-3
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,63 @@ export default class ChallengeEditorScreen extends React.Component {
77
super(props);
88
}
99

10+
componentDidMount() {
11+
if(this.props.showInfo) {
12+
$('#challengeInfo').modal('show');
13+
}
14+
15+
}
16+
17+
parseDescription(txt) {
18+
txt = txt.replace(/(\[.*\]\(.*\))/g, '<break-line>$1<break-line>');
19+
txt = txt.replace(/\n/g, '<break-line><br/><break-line>');
20+
txt = txt.split('<break-line>');
21+
txt = txt.map((line) => {
22+
let result;
23+
result = (/^\[(.*)\]\((.*)\)$/).exec(line);
24+
if(result) {
25+
return <a href={result[2]} target="_blank" rel="noopener noreferrer">{result[1]}</a>;
26+
}
27+
if(line == '<br/>') {
28+
return <br/>;
29+
}
30+
return line;
31+
});
32+
33+
return txt;
34+
}
35+
1036
render() {
1137
return <div>
1238
<FullRow>
1339
<button type="button" className="btn btn-secondary float-right close-challenge" onClick={() => this.props.onClose()}>
1440
<i className="fas fa-times" aria-hidden="true"></i> Close
1541
</button>
16-
<a className="btn btn-secondary float-right help" style={{marginRight: '5px'}} href="./docs/#/docs/getting_started" target="_blank">
17-
<i className="fas fa-question-circle"></i> Help
18-
</a>
42+
<button className="btn btn-secondary float-right open-challenge-info" style={{marginRight: '5px'}} data-toggle="modal" data-target="#challengeInfo">
43+
<i className="fas fa-info-circle"></i> Info
44+
</button>
1945
<button type="button" className="btn btn-primary float-right editor-fight" style={{marginRight: '5px'}} onClick={() => this.props.onStart()}>
2046
<i className="fas fa-play" aria-hidden="true"></i> Start the Battle
2147
</button>
48+
<div className="modal fade" id="challengeInfo" tabIndex="-1" role="dialog" aria-labelledby="challengeInfoLabel" aria-hidden="true">
49+
<div className="modal-dialog" role="document" style={{minWidth: '80%'}}>
50+
<div className="modal-content">
51+
<div className="modal-header">
52+
<h5 className="modal-title" id="challengeInfoLabel"><i className="fas fa-info-circle"></i> Level {this.props.level}: {this.props.name}</h5>
53+
<button type="button" className="close" data-dismiss="modal" aria-label="Close">
54+
<span aria-hidden="true">&times;</span>
55+
</button>
56+
</div>
57+
<div className="modal-body">
58+
{this.parseDescription(this.props.description)}
59+
</div>
60+
<div className="modal-footer">
61+
<button type="button" className="btn btn-primary close-challenge-info" data-dismiss="modal">Close</button>
62+
</div>
63+
</div>
64+
</div>
65+
</div>
66+
2267
</FullRow>
2368
<FullRow>
2469
<CodeEditorWidget

packages/jsbattle-webpage/src/controller/Controller.js

+2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import assignTanksToBattle from './action/battle/assignTanksToBattle.js';
2424
import shareBattle from './action/battle/shareBattle.js';
2525

2626
import saveCurrentChallengeScript from './action/challenge/saveCurrentChallengeScript.js';
27+
import unlockAllChallenges from './action/challenge/unlockAllChallenges.js';
2728

2829
import loadSettings from './action/loadSettings.js';
2930
import setSimulationSpeed from './action/setSimulationSpeed.js';
@@ -61,6 +62,7 @@ export default class Controller {
6162
this.addActon('shareBattle', shareBattle(this.stateHolder));
6263

6364
this.addActon('saveCurrentChallengeScript', saveCurrentChallengeScript(this.stateHolder, challengeLibrary));
65+
this.addActon('unlockAllChallenges', unlockAllChallenges(this.stateHolder, challengeLibrary));
6466

6567
this.addActon('loadSettings', loadSettings(this.stateHolder, aiRepository, challengeLibrary));
6668
this.addActon('setSimulationSpeed', setSimulationSpeed(this.stateHolder));

0 commit comments

Comments
 (0)