mirror of https://github.com/01-edu/public.git
Clement Denis
4 years ago
61 changed files with 3533 additions and 3533 deletions
File diff suppressed because it is too large
diff.load
@ -1,92 +1,92 @@
|
||||
## How to do an audit from home |
||||
|
||||
### Introduction |
||||
|
||||
A little reminder first, |
||||
|
||||
Audits, as we have told you many times, are an essential part of leveling up and truly acquiring your skills |
||||
and knowledge. |
||||
|
||||
Normally, they must be done with your physical presence. |
||||
The idea is that they encourage the exchange between the auditors and the members of the group. |
||||
|
||||
If the project fails, the whole group will learn why. And then after all your audits are done you will retry. |
||||
During all those extra audits, you, as a group, will discuss about your failures with the auditors. |
||||
If the project succeed, you will be an inspiration for the auditors. |
||||
In both of those cases you will learn or you will teach. The roles will keep reversing almost every time. |
||||
|
||||
It is, ultimaly, those interactions that are essential for really learning; |
||||
To give and receive feedback, but also to see what you really know as an individual and what you do not. |
||||
This will increase your capacity to solve problems and your capacity to adapt, which is one of the most important |
||||
skill of a good programmer. |
||||
|
||||
Today, once again, we must adapt... |
||||
Knowledge is important but not as essential as health. |
||||
|
||||
We really wish all of you to stay safe and healthy at home. |
||||
|
||||
This is why we created a little guideline in how to conduct your audit from home. |
||||
There is a video here : [youtube.com/watch?v=J8g8P-IJLJw](https://www.youtube.com/watch?v=J8g8P-IJLJw) |
||||
|
||||
But we are also going to summarize the process. |
||||
So here we go. |
||||
|
||||
### Prerequisites for the team captain and the auditor |
||||
|
||||
- A program to communicate and livestream installed (like Discord [discordapp.com](https://discordapp.com/) ) |
||||
- A program to allows the auditor to take charge of the computer |
||||
of the team captain installed (like teamViewer [teamviewer.com](https://www.teamviewer.com/) ) |
||||
|
||||
### Prerequisite for the team captain |
||||
|
||||
The team captain must communicate : |
||||
|
||||
- The contact of his or her teammates to the auditor |
||||
- The git repository of the project to be reviewed (the link must be public or accessible for the auditor) |
||||
- The team captain must be logged in in his or her session to allow the auditor to start its audit |
||||
|
||||
### Prerequisite for the auditor |
||||
|
||||
- The auditor will need to create the group on the communication program |
||||
- The auditor will need to download the project repository |
||||
- The auditor, if the internet bandwith allows it, will livestream the process |
||||
|
||||
### Instructions |
||||
|
||||
1. Let the auditor establish the communication with the group members and the captain. |
||||
|
||||
2. Once all prequisites are done from the team captain and the auditor let the audit begin. |
||||
|
||||
3. If possible, the auditor starts the stream. |
||||
|
||||
4. The captain, after checking that the login is done on his or her computer, allows the auditor |
||||
to take control of its computer (with teamViewer for example) |
||||
|
||||
5. The auditor now has the control to start the audit on the computer of the captain. |
||||
The organization of the windows by the auditor might be the tricky part. We suggest |
||||
to see how we did it in the video if you find it difficult. |
||||
|
||||
6. The auditor conducts the audit, if the auditor can not live stream its audits, he or she |
||||
will then explain by voice (or writing in the chat) what he or she is doing to the rest of the group. |
||||
|
||||
7. The audit is conducted until all questions are validated or until a mistake is made. |
||||
|
||||
8. If a mistake is made the auditor can copy paste the commands that detected the mistakes. |
||||
|
||||
9. The group discusses as much as needed until the audit is completed. |
||||
|
||||
10. Once the audit is completed, all programs can be closed up. Say bye and thank you for your time and make the |
||||
necessary conclusions among the members of the group. |
||||
|
||||
### Conclusion |
||||
|
||||
Thank you for reading until the end. Stay safe. |
||||
|
||||
### Bonus |
||||
|
||||
Did you know that there is a nice `Live Share` extension on `vscode`? |
||||
This allows to do remote group programming easily. It might be an alternative solution |
||||
to the livestream if necessary. |
||||
Try it out! |
||||
|
||||
Stay safe everyone! Happy coding! |
||||
## How to do an audit from home |
||||
|
||||
### Introduction |
||||
|
||||
A little reminder first, |
||||
|
||||
Audits, as we have told you many times, are an essential part of leveling up and truly acquiring your skills |
||||
and knowledge. |
||||
|
||||
Normally, they must be done with your physical presence. |
||||
The idea is that they encourage the exchange between the auditors and the members of the group. |
||||
|
||||
If the project fails, the whole group will learn why. And then after all your audits are done you will retry. |
||||
During all those extra audits, you, as a group, will discuss about your failures with the auditors. |
||||
If the project succeed, you will be an inspiration for the auditors. |
||||
In both of those cases you will learn or you will teach. The roles will keep reversing almost every time. |
||||
|
||||
It is, ultimaly, those interactions that are essential for really learning; |
||||
To give and receive feedback, but also to see what you really know as an individual and what you do not. |
||||
This will increase your capacity to solve problems and your capacity to adapt, which is one of the most important |
||||
skill of a good programmer. |
||||
|
||||
Today, once again, we must adapt... |
||||
Knowledge is important but not as essential as health. |
||||
|
||||
We really wish all of you to stay safe and healthy at home. |
||||
|
||||
This is why we created a little guideline in how to conduct your audit from home. |
||||
There is a video here : [youtube.com/watch?v=J8g8P-IJLJw](https://www.youtube.com/watch?v=J8g8P-IJLJw) |
||||
|
||||
But we are also going to summarize the process. |
||||
So here we go. |
||||
|
||||
### Prerequisites for the team captain and the auditor |
||||
|
||||
- A program to communicate and livestream installed (like Discord [discordapp.com](https://discordapp.com/) ) |
||||
- A program to allows the auditor to take charge of the computer |
||||
of the team captain installed (like teamViewer [teamviewer.com](https://www.teamviewer.com/) ) |
||||
|
||||
### Prerequisite for the team captain |
||||
|
||||
The team captain must communicate : |
||||
|
||||
- The contact of his or her teammates to the auditor |
||||
- The git repository of the project to be reviewed (the link must be public or accessible for the auditor) |
||||
- The team captain must be logged in in his or her session to allow the auditor to start its audit |
||||
|
||||
### Prerequisite for the auditor |
||||
|
||||
- The auditor will need to create the group on the communication program |
||||
- The auditor will need to download the project repository |
||||
- The auditor, if the internet bandwith allows it, will livestream the process |
||||
|
||||
### Instructions |
||||
|
||||
1. Let the auditor establish the communication with the group members and the captain. |
||||
|
||||
2. Once all prequisites are done from the team captain and the auditor let the audit begin. |
||||
|
||||
3. If possible, the auditor starts the stream. |
||||
|
||||
4. The captain, after checking that the login is done on his or her computer, allows the auditor |
||||
to take control of its computer (with teamViewer for example) |
||||
|
||||
5. The auditor now has the control to start the audit on the computer of the captain. |
||||
The organization of the windows by the auditor might be the tricky part. We suggest |
||||
to see how we did it in the video if you find it difficult. |
||||
|
||||
6. The auditor conducts the audit, if the auditor can not live stream its audits, he or she |
||||
will then explain by voice (or writing in the chat) what he or she is doing to the rest of the group. |
||||
|
||||
7. The audit is conducted until all questions are validated or until a mistake is made. |
||||
|
||||
8. If a mistake is made the auditor can copy paste the commands that detected the mistakes. |
||||
|
||||
9. The group discusses as much as needed until the audit is completed. |
||||
|
||||
10. Once the audit is completed, all programs can be closed up. Say bye and thank you for your time and make the |
||||
necessary conclusions among the members of the group. |
||||
|
||||
### Conclusion |
||||
|
||||
Thank you for reading until the end. Stay safe. |
||||
|
||||
### Bonus |
||||
|
||||
Did you know that there is a nice `Live Share` extension on `vscode`? |
||||
This allows to do remote group programming easily. It might be an alternative solution |
||||
to the livestream if necessary. |
||||
Try it out! |
||||
|
||||
Stay safe everyone! Happy coding! |
||||
|
@ -1,12 +1,12 @@
|
||||
## Biggie Smalls |
||||
|
||||
### Instructions |
||||
|
||||
Create 2 variables |
||||
- `smalls` with the smallest possible `number` value |
||||
- `biggie` with the greatest possible `number` value |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript-number](https://devdocs.io/javascript-number/) |
||||
## Biggie Smalls |
||||
|
||||
### Instructions |
||||
|
||||
Create 2 variables |
||||
- `smalls` with the smallest possible `number` value |
||||
- `biggie` with the greatest possible `number` value |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript-number](https://devdocs.io/javascript-number/) |
||||
|
@ -1,33 +1,33 @@
|
||||
## Change |
||||
|
||||
### Instructions |
||||
|
||||
Create 2 functions: |
||||
- `get`: a function that takes a key and return the corresponding |
||||
value from the sourceObject |
||||
|
||||
- `set`: a function that takes a key and a value update the |
||||
value for the corresponding property of the sourceObject |
||||
and return the set value |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/functions.js](https://nan-academy.github.io/js-training/examples/functions.js) |
||||
- [nan-academy.github.io/js-training/examples/data-structures.js](https://nan-academy.github.io/js-training/examples/data-structures.js) |
||||
- [nan-academy.github.io/js-training/examples/get.js](https://nan-academy.github.io/js-training/examples/get.js) |
||||
- [nan-academy.github.io/js-training/examples/set.js](https://nan-academy.github.io/js-training/examples/set.js) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
const sourceObject = { |
||||
num: 42, |
||||
bool: true, |
||||
str: 'some text', |
||||
log: console.log, |
||||
} |
||||
``` |
||||
## Change |
||||
|
||||
### Instructions |
||||
|
||||
Create 2 functions: |
||||
- `get`: a function that takes a key and return the corresponding |
||||
value from the sourceObject |
||||
|
||||
- `set`: a function that takes a key and a value update the |
||||
value for the corresponding property of the sourceObject |
||||
and return the set value |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/functions.js](https://nan-academy.github.io/js-training/examples/functions.js) |
||||
- [nan-academy.github.io/js-training/examples/data-structures.js](https://nan-academy.github.io/js-training/examples/data-structures.js) |
||||
- [nan-academy.github.io/js-training/examples/get.js](https://nan-academy.github.io/js-training/examples/get.js) |
||||
- [nan-academy.github.io/js-training/examples/set.js](https://nan-academy.github.io/js-training/examples/set.js) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
const sourceObject = { |
||||
num: 42, |
||||
bool: true, |
||||
str: 'some text', |
||||
log: console.log, |
||||
} |
||||
``` |
||||
|
@ -1,13 +1,13 @@
|
||||
## Circular |
||||
|
||||
### Instructions |
||||
|
||||
Create an object named `circular` that has a property named `circular` with |
||||
itself as the value |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/data-structures.js](https://nan-academy.github.io/js-training/examples/data-structures.js) |
||||
- [nan-academy.github.io/js-training/examples/get.js](https://nan-academy.github.io/js-training/examples/get.js) |
||||
- [nan-academy.github.io/js-training/examples/set.js](https://nan-academy.github.io/js-training/examples/set.js) |
||||
## Circular |
||||
|
||||
### Instructions |
||||
|
||||
Create an object named `circular` that has a property named `circular` with |
||||
itself as the value |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/data-structures.js](https://nan-academy.github.io/js-training/examples/data-structures.js) |
||||
- [nan-academy.github.io/js-training/examples/get.js](https://nan-academy.github.io/js-training/examples/get.js) |
||||
- [nan-academy.github.io/js-training/examples/set.js](https://nan-academy.github.io/js-training/examples/set.js) |
||||
|
@ -1,21 +1,21 @@
|
||||
## Currify |
||||
|
||||
### Instructions |
||||
|
||||
Create the function `currify` that will curry any functions put as argument. |
||||
|
||||
example: |
||||
```js |
||||
const mult2 = (el1,el2) => el1 * el2 |
||||
console.log(mult2(2,2)) // result epected 4 |
||||
|
||||
|
||||
const mult2Curried = currify(mult2) |
||||
|
||||
console.log(mult2Curried(2)(2)) // result expected 4 |
||||
// (same result, with a function that has technically only one argument) |
||||
``` |
||||
|
||||
### Notions |
||||
|
||||
- [stackoverflow.com/questions/36314/what-is-currying](https://stackoverflow.com/questions/36314/what-is-currying) |
||||
## Currify |
||||
|
||||
### Instructions |
||||
|
||||
Create the function `currify` that will curry any functions put as argument. |
||||
|
||||
example: |
||||
```js |
||||
const mult2 = (el1,el2) => el1 * el2 |
||||
console.log(mult2(2,2)) // result epected 4 |
||||
|
||||
|
||||
const mult2Curried = currify(mult2) |
||||
|
||||
console.log(mult2Curried(2)(2)) // result expected 4 |
||||
// (same result, with a function that has technically only one argument) |
||||
``` |
||||
|
||||
### Notions |
||||
|
||||
- [stackoverflow.com/questions/36314/what-is-currying](https://stackoverflow.com/questions/36314/what-is-currying) |
||||
|
@ -1,48 +1,48 @@
|
||||
## Curry Entries |
||||
|
||||
### Instructions |
||||
|
||||
This exercise consists in creating curry functions to apply in the objects |
||||
entries. |
||||
You will have to create the following curry functions: |
||||
|
||||
- `defaultCurry` that will curry two objects in witch the second object must |
||||
be the default object and returns a new object with the modifications applied |
||||
by the first object |
||||
- `mapCurry` that replicate the function `.map` |
||||
- `reduceCurry` that replicate the function `.reduce` |
||||
- `filterCurry` that replicate the function `.filter` |
||||
|
||||
You will also have to create for each curry function the following: |
||||
|
||||
- `reduceScore` that will return the total value of the scores |
||||
of the persons who use the force |
||||
- `filterForce` that will return the force users with `shootingScores` |
||||
equal or higher than 80 |
||||
- `mapAverage` that will return a new object with the propriety `averageScore` |
||||
that is the averages of the scores for each person |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/array/filter](https://devdocs.io/javascript/global_objects/array/filter) |
||||
- [devdocs.io/javascript/global_objects/array/map](https://devdocs.io/javascript/global_objects/array/map) |
||||
- [devdocs.io/javascript/global_objects/array/reduce](https://devdocs.io/javascript/global_objects/array/reduce) |
||||
- [devdocs.io/javascript/global_objects/object/entries](https://devdocs.io/javascript/global_objects/object/entries) |
||||
- [devdocs.io/javascript/global_objects/object/fromentries](https://devdocs.io/javascript/global_objects/object/fromentries) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
// prettier-ignore |
||||
const personnel = { |
||||
lukeSkywalker: { id: 5, pilotingScore: 98, shootingScore: 56, isForceUser: true }, |
||||
sabineWren: { id: 82, pilotingScore: 73, shootingScore: 99, isForceUser: false }, |
||||
zebOrellios: { id: 22, pilotingScore: 20, shootingScore: 59, isForceUser: false }, |
||||
ezraBridger: { id: 15, pilotingScore: 43, shootingScore: 67, isForceUser: true }, |
||||
calebDume: { id: 11, pilotingScore: 71, shootingScore: 85, isForceUser: true }, |
||||
} |
||||
``` |
||||
## Curry Entries |
||||
|
||||
### Instructions |
||||
|
||||
This exercise consists in creating curry functions to apply in the objects |
||||
entries. |
||||
You will have to create the following curry functions: |
||||
|
||||
- `defaultCurry` that will curry two objects in witch the second object must |
||||
be the default object and returns a new object with the modifications applied |
||||
by the first object |
||||
- `mapCurry` that replicate the function `.map` |
||||
- `reduceCurry` that replicate the function `.reduce` |
||||
- `filterCurry` that replicate the function `.filter` |
||||
|
||||
You will also have to create for each curry function the following: |
||||
|
||||
- `reduceScore` that will return the total value of the scores |
||||
of the persons who use the force |
||||
- `filterForce` that will return the force users with `shootingScores` |
||||
equal or higher than 80 |
||||
- `mapAverage` that will return a new object with the propriety `averageScore` |
||||
that is the averages of the scores for each person |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/array/filter](https://devdocs.io/javascript/global_objects/array/filter) |
||||
- [devdocs.io/javascript/global_objects/array/map](https://devdocs.io/javascript/global_objects/array/map) |
||||
- [devdocs.io/javascript/global_objects/array/reduce](https://devdocs.io/javascript/global_objects/array/reduce) |
||||
- [devdocs.io/javascript/global_objects/object/entries](https://devdocs.io/javascript/global_objects/object/entries) |
||||
- [devdocs.io/javascript/global_objects/object/fromentries](https://devdocs.io/javascript/global_objects/object/fromentries) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
// prettier-ignore |
||||
const personnel = { |
||||
lukeSkywalker: { id: 5, pilotingScore: 98, shootingScore: 56, isForceUser: true }, |
||||
sabineWren: { id: 82, pilotingScore: 73, shootingScore: 99, isForceUser: false }, |
||||
zebOrellios: { id: 22, pilotingScore: 20, shootingScore: 59, isForceUser: false }, |
||||
ezraBridger: { id: 15, pilotingScore: 43, shootingScore: 67, isForceUser: true }, |
||||
calebDume: { id: 11, pilotingScore: 71, shootingScore: 85, isForceUser: true }, |
||||
} |
||||
``` |
||||
|
@ -1,27 +1,27 @@
|
||||
## Cut Corners |
||||
|
||||
### Instructions |
||||
|
||||
Create a function for each rounding math functions: |
||||
- round (like `Math.round`) |
||||
- ceil (like `Math.ceil`) |
||||
- floor (like `Math.floor`) |
||||
- trunc (like `Math.trunc`) |
||||
|
||||
Some restrictions apply: |
||||
- You may not use strings conversion to do it |
||||
- No bitwise operator |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/math](https://devdocs.io/javascript/global_objects/math) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
Math.round = Math.ceil = Math.floor = Math.trunc = undefined |
||||
``` |
||||
## Cut Corners |
||||
|
||||
### Instructions |
||||
|
||||
Create a function for each rounding math functions: |
||||
- round (like `Math.round`) |
||||
- ceil (like `Math.ceil`) |
||||
- floor (like `Math.floor`) |
||||
- trunc (like `Math.trunc`) |
||||
|
||||
Some restrictions apply: |
||||
- You may not use strings conversion to do it |
||||
- No bitwise operator |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/math](https://devdocs.io/javascript/global_objects/math) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
Math.round = Math.ceil = Math.floor = Math.trunc = undefined |
||||
``` |
||||
|
@ -1,19 +1,19 @@
|
||||
## Date Is |
||||
|
||||
### Instructions |
||||
|
||||
Create the following functions: |
||||
- `isValid`, this function must return false if its an Invalid Date |
||||
- `isAfter`, this function will receive two dates and return true if the first date is bigger then the second date |
||||
- `isBefore`, this function will receive two dates and return true if the first date is lesser then the second date |
||||
- `isFuture`, will return true if the date given as parameter is higher then the present date |
||||
- `isPast`, will return true if the date given as parameter less then the present date |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [date-fns.org/v2.14.0/docs/isValid](https://date-fns.org/v2.14.0/docs/isValid) |
||||
- [date-fns.org/v2.14.0/docs/isAfter](https://date-fns.org/v2.14.0/docs/isAfter) |
||||
- [date-fns.org/v2.14.0/docs/isBefore](https://date-fns.org/v2.14.0/docs/isBefore) |
||||
- [date-fns.org/v2.14.0/docs/isFuture](https://date-fns.org/v2.14.0/docs/isFuture) |
||||
- [date-fns.org/v2.14.0/docs/isPast](https://date-fns.org/v2.14.0/docs/isPast) |
||||
## Date Is |
||||
|
||||
### Instructions |
||||
|
||||
Create the following functions: |
||||
- `isValid`, this function must return false if its an Invalid Date |
||||
- `isAfter`, this function will receive two dates and return true if the first date is bigger then the second date |
||||
- `isBefore`, this function will receive two dates and return true if the first date is lesser then the second date |
||||
- `isFuture`, will return true if the date given as parameter is higher then the present date |
||||
- `isPast`, will return true if the date given as parameter less then the present date |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [date-fns.org/v2.14.0/docs/isValid](https://date-fns.org/v2.14.0/docs/isValid) |
||||
- [date-fns.org/v2.14.0/docs/isAfter](https://date-fns.org/v2.14.0/docs/isAfter) |
||||
- [date-fns.org/v2.14.0/docs/isBefore](https://date-fns.org/v2.14.0/docs/isBefore) |
||||
- [date-fns.org/v2.14.0/docs/isFuture](https://date-fns.org/v2.14.0/docs/isFuture) |
||||
- [date-fns.org/v2.14.0/docs/isPast](https://date-fns.org/v2.14.0/docs/isPast) |
||||
|
@ -1,12 +1,12 @@
|
||||
## Debounce |
||||
|
||||
### Instructions |
||||
|
||||
Create two functions that will work like `_.debounce` from lodash |
||||
- `debounce`, this function doesn't need to take care of the options |
||||
- `opDebounce`, this function will take care of the `leading` options |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [lodash.com/docs/4.17.15#debounce](https://lodash.com/docs/4.17.15#debounce) |
||||
## Debounce |
||||
|
||||
### Instructions |
||||
|
||||
Create two functions that will work like `_.debounce` from lodash |
||||
- `debounce`, this function doesn't need to take care of the options |
||||
- `opDebounce`, this function will take care of the `leading` options |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [lodash.com/docs/4.17.15#debounce](https://lodash.com/docs/4.17.15#debounce) |
||||
|
@ -1,9 +1,9 @@
|
||||
## Deep Copy |
||||
|
||||
### Instructions |
||||
|
||||
Create a function called `deepCopy` that copy objects and arrays recursively. |
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript-object](https://devdocs.io/javascript-object/) |
||||
## Deep Copy |
||||
|
||||
### Instructions |
||||
|
||||
Create a function called `deepCopy` that copy objects and arrays recursively. |
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript-object](https://devdocs.io/javascript-object/) |
||||
|
@ -1,28 +1,28 @@
|
||||
## Filter |
||||
|
||||
### Instructions |
||||
|
||||
- Create a `filter` function that takes an array as first argument, a function as second, |
||||
and that works like the method [].filter |
||||
|
||||
- Create a `reject` function that takes an array as first argument, a function as second, |
||||
and that works like the reject function from lodash. |
||||
|
||||
- Create a `partition` function that takes an array as first argument, a function as second, |
||||
and that works like the partition function from lodash. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/array/filter](https://devdocs.io/javascript/global_objects/array/filter) |
||||
- [lodash.com/docs/4.17.15#reject](https://lodash.com/docs/4.17.15#reject) |
||||
- [lodash.com/docs/4.17.15#partition](https://lodash.com/docs/4.17.15#partition) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
Array.prototype.filter = undefined |
||||
``` |
||||
## Filter |
||||
|
||||
### Instructions |
||||
|
||||
- Create a `filter` function that takes an array as first argument, a function as second, |
||||
and that works like the method [].filter |
||||
|
||||
- Create a `reject` function that takes an array as first argument, a function as second, |
||||
and that works like the reject function from lodash. |
||||
|
||||
- Create a `partition` function that takes an array as first argument, a function as second, |
||||
and that works like the partition function from lodash. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/array/filter](https://devdocs.io/javascript/global_objects/array/filter) |
||||
- [lodash.com/docs/4.17.15#reject](https://lodash.com/docs/4.17.15#reject) |
||||
- [lodash.com/docs/4.17.15#partition](https://lodash.com/docs/4.17.15#partition) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
Array.prototype.filter = undefined |
||||
``` |
||||
|
@ -1,30 +1,30 @@
|
||||
## Find Expression |
||||
|
||||
### Instructions |
||||
|
||||
Create a function called `findExpression` that takes a number as parameter and returns a string |
||||
|
||||
- It will be given two constant variable `add4` and `mul2` |
||||
|
||||
- Your goal is to try to find a sequence, starting from the number 1, and repeatedly either adding 4 or multiplying 2 |
||||
that produces the number given has parameter. |
||||
For example, the number 8 you must first multiplying by 2 twice and then add 4. |
||||
It will look something like this `1 *2 *2 +4` |
||||
|
||||
- If the number can not be reached you should return `undefined` |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/loops.js](https://nan-academy.github.io/js-training/examples/loops.js) |
||||
- [nan-academy.github.io/js-training/examples/recursion.js](https://nan-academy.github.io/js-training/examples/recursion.js) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
const add4 = '+4' |
||||
const mul2 = '*2' |
||||
``` |
||||
## Find Expression |
||||
|
||||
### Instructions |
||||
|
||||
Create a function called `findExpression` that takes a number as parameter and returns a string |
||||
|
||||
- It will be given two constant variable `add4` and `mul2` |
||||
|
||||
- Your goal is to try to find a sequence, starting from the number 1, and repeatedly either adding 4 or multiplying 2 |
||||
that produces the number given has parameter. |
||||
For example, the number 8 you must first multiplying by 2 twice and then add 4. |
||||
It will look something like this `1 *2 *2 +4` |
||||
|
||||
- If the number can not be reached you should return `undefined` |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/loops.js](https://nan-academy.github.io/js-training/examples/loops.js) |
||||
- [nan-academy.github.io/js-training/examples/recursion.js](https://nan-academy.github.io/js-training/examples/recursion.js) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
const add4 = '+4' |
||||
const mul2 = '*2' |
||||
``` |
||||
|
@ -1,19 +1,19 @@
|
||||
## Flat |
||||
|
||||
### Instructions |
||||
|
||||
Create the `flat` functions that works like the `.flat` array method |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/array/flat](https://devdocs.io/javascript/global_objects/array/flat) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
Array.prototype.flat = undefined |
||||
``` |
||||
## Flat |
||||
|
||||
### Instructions |
||||
|
||||
Create the `flat` functions that works like the `.flat` array method |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/array/flat](https://devdocs.io/javascript/global_objects/array/flat) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
Array.prototype.flat = undefined |
||||
``` |
||||
|
@ -1,24 +1,24 @@
|
||||
## Flow |
||||
|
||||
### Instructions |
||||
|
||||
Create the function `flow` that will works as the \_.flow([funcs]) |
||||
from lodash. |
||||
|
||||
|
||||
### Example |
||||
|
||||
``` js |
||||
|
||||
const square = nbr => nbr * nbr |
||||
const add2Numbers = (nbr1, nbr2) => nbr1 + nbr2 |
||||
|
||||
const flowedFunctions = flow([add2numbers, square]) |
||||
flowedFunctions(2, 3) // -> 25 |
||||
|
||||
``` |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [lodash.com/docs/4.17.15#flow](https://lodash.com/docs/4.17.15#flow) |
||||
## Flow |
||||
|
||||
### Instructions |
||||
|
||||
Create the function `flow` that will works as the \_.flow([funcs]) |
||||
from lodash. |
||||
|
||||
|
||||
### Example |
||||
|
||||
``` js |
||||
|
||||
const square = nbr => nbr * nbr |
||||
const add2Numbers = (nbr1, nbr2) => nbr1 + nbr2 |
||||
|
||||
const flowedFunctions = flow([add2numbers, square]) |
||||
flowedFunctions(2, 3) // -> 25 |
||||
|
||||
``` |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [lodash.com/docs/4.17.15#flow](https://lodash.com/docs/4.17.15#flow) |
||||
|
@ -1,20 +1,20 @@
|
||||
## For Each |
||||
|
||||
### Instructions |
||||
|
||||
Create a `forEach` function that takes an array as first argument, a function as second, |
||||
and that works like the method .forEach |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/array/foreach](https://devdocs.io/javascript/global_objects/array/foreach) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
Array.prototype.forEach = undefined |
||||
``` |
||||
## For Each |
||||
|
||||
### Instructions |
||||
|
||||
Create a `forEach` function that takes an array as first argument, a function as second, |
||||
and that works like the method .forEach |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/array/foreach](https://devdocs.io/javascript/global_objects/array/foreach) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
Array.prototype.forEach = undefined |
||||
``` |
||||
|
@ -1,51 +1,51 @@
|
||||
#### Functional |
||||
|
||||
###### Does the forum present the 4 types of users? |
||||
|
||||
##### Try to enter the forum as a Guest |
||||
|
||||
###### Can you confirm that the content is only viewable? |
||||
|
||||
##### Try registering as a normal user. |
||||
|
||||
###### Can you create posts and comments? |
||||
|
||||
##### Try registering as a normal user. |
||||
|
||||
###### Can you like or dislike a post? |
||||
|
||||
##### Try registering as a moderator. Then login to an admin account and see if the admin user has received the request. |
||||
|
||||
###### Can you confirm that the admin received the request? |
||||
|
||||
##### Try accepting a moderator using the admin user. |
||||
|
||||
###### Has the moderator been promoted? |
||||
|
||||
##### Try using the moderator to delete a obscene post |
||||
|
||||
###### Can you confirm that it is possible? |
||||
|
||||
##### Try using the moderator to report a illegal post |
||||
|
||||
###### Did the admin user receive the report? |
||||
|
||||
##### Try using the admin user to answer the moderator request. |
||||
|
||||
###### Did the moderator receive the answer from the admin? |
||||
|
||||
##### Try using an admin user to demote a moderator. |
||||
|
||||
###### Can you confirm that it is possible? |
||||
|
||||
##### As an auditor, is this project up to every standard? If not, why are you failing the project?(Empty Work, Incomplete Work, Invalid compilation, Cheating, Crashing, Leaks) |
||||
|
||||
#### General |
||||
|
||||
###### +Does the project present more then 4 types of users? |
||||
|
||||
#### Social |
||||
|
||||
###### +Did you learn anything from this project? |
||||
|
||||
###### +Would you recommend/nominate this program as an example for the rest of the school? |
||||
#### Functional |
||||
|
||||
###### Does the forum present the 4 types of users? |
||||
|
||||
##### Try to enter the forum as a Guest |
||||
|
||||
###### Can you confirm that the content is only viewable? |
||||
|
||||
##### Try registering as a normal user. |
||||
|
||||
###### Can you create posts and comments? |
||||
|
||||
##### Try registering as a normal user. |
||||
|
||||
###### Can you like or dislike a post? |
||||
|
||||
##### Try registering as a moderator. Then login to an admin account and see if the admin user has received the request. |
||||
|
||||
###### Can you confirm that the admin received the request? |
||||
|
||||
##### Try accepting a moderator using the admin user. |
||||
|
||||
###### Has the moderator been promoted? |
||||
|
||||
##### Try using the moderator to delete a obscene post |
||||
|
||||
###### Can you confirm that it is possible? |
||||
|
||||
##### Try using the moderator to report a illegal post |
||||
|
||||
###### Did the admin user receive the report? |
||||
|
||||
##### Try using the admin user to answer the moderator request. |
||||
|
||||
###### Did the moderator receive the answer from the admin? |
||||
|
||||
##### Try using an admin user to demote a moderator. |
||||
|
||||
###### Can you confirm that it is possible? |
||||
|
||||
##### As an auditor, is this project up to every standard? If not, why are you failing the project?(Empty Work, Incomplete Work, Invalid compilation, Cheating, Crashing, Leaks) |
||||
|
||||
#### General |
||||
|
||||
###### +Does the project present more then 4 types of users? |
||||
|
||||
#### Social |
||||
|
||||
###### +Did you learn anything from this project? |
||||
|
||||
###### +Would you recommend/nominate this program as an example for the rest of the school? |
||||
|
@ -1,183 +1,183 @@
|
||||
#### Authentication |
||||
|
||||
###### Are an email and a password asked for in the resgistration? |
||||
|
||||
###### Does the project detect if the email or password are wrong? |
||||
|
||||
###### Does the project detect if the email or user name is already taken in the registration? |
||||
|
||||
##### Try to register as a new user in the forum. |
||||
|
||||
###### Is it possible to register? |
||||
|
||||
##### Try to login with the user you created. |
||||
|
||||
###### Can you login and have all the rights of a registered user? |
||||
|
||||
##### Try to login without any credentials. |
||||
|
||||
###### Does it show a warning message? |
||||
|
||||
###### Are sessions present in the project? |
||||
|
||||
##### Try opening two different browsers and login into one of them. Refresh the other browser. |
||||
|
||||
###### Can you confirm that the browser non logged remains unregistered? |
||||
|
||||
##### Try opening two different browsers and login into both of them. Refresh both browsers. |
||||
|
||||
###### Can you confirm that only one of those browsers has an active session? |
||||
|
||||
##### Try opening two different browsers and login into one of them. Then create a new post or just add a comment. Refresh both browsers. |
||||
|
||||
###### Does it present the comment/post on both browsers? |
||||
|
||||
#### SQLite |
||||
|
||||
###### Does the code contain at least one CREATE query? |
||||
|
||||
###### Does the code contain at least one INSERT query? |
||||
|
||||
###### Does the code contain at least one SELECT query? |
||||
|
||||
##### Try registering in the forum, open the database with `sqlite3 <database_name.db>` and perform a query to select all the users (Example: SELECT \* FROM users;). |
||||
|
||||
###### Does it present the user you created? |
||||
|
||||
##### Try creating a post in the forum, open the database with `sqlite3 <database_name.db>` and perform a query to select all the users (Example: SELECT \* FROM post;). |
||||
|
||||
###### Does it present the post you created? |
||||
|
||||
##### Try creating a comment in the forum, open the database with `sqlite3 <database_name.db>` and perform a query to select all the users (Example: SELECT \* FROM comment;). |
||||
|
||||
###### Does it present the comment you created? |
||||
|
||||
#### Docker |
||||
|
||||
###### Does the project have Dockerfiles? |
||||
|
||||
##### Try to run the command `"docker image build [OPTINS] PATH | URL | -"` to build the image using using the project Dockerfiles and run the command `"docker images"` to see images. |
||||
|
||||
``` |
||||
student$ docker images |
||||
REPOSITORY TAG IMAGE ID CREATED SIZE |
||||
<name of the image> latest 85a65d66ca39 7 seconds ago 795MB |
||||
``` |
||||
|
||||
###### Does all images build as above? |
||||
|
||||
##### Try running the command `"docker container run [OPTIONS] IMAGE [COMMAND] [ARG...]"` to start the containers using the images just created and run the command `"docker ps -a"` to see containers. |
||||
|
||||
``` |
||||
student$ docker ps -a |
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES |
||||
cc8f5dcf760f <name of the image> "./server" 6 seconds ago Up 6 seconds 0.0.0.0:8080->8080/tcp ascii-art-web |
||||
``` |
||||
|
||||
###### Is the docker containers running as above? |
||||
|
||||
###### Does the project present no [unused object](https://docs.docker.com/config/pruning/)? |
||||
|
||||
#### Functional |
||||
|
||||
##### Enter the forum as a non-registered user. |
||||
|
||||
###### Are you prohibited to create a post? |
||||
|
||||
##### Enter the forum as a non-registered user. |
||||
|
||||
###### Are you prohibited to create a comment? |
||||
|
||||
##### Enter the forum as a non-registered user and try to like a comment. |
||||
|
||||
###### Are you prohibited to like a post? |
||||
|
||||
##### Enter the forum as a non-registered user and try to dislike a comment. |
||||
|
||||
###### Are you prohibited to dislike a comment? |
||||
|
||||
##### Enter the forum as a registered user, go to a post and try to create a comment for it. |
||||
|
||||
###### Were you able to create the comment? |
||||
|
||||
##### Enter the forum as a registered user, go to a post and try to create an empty comment for it. |
||||
|
||||
###### Were you prohibited to create the comment? |
||||
|
||||
##### Enter the forum as a registered user and try to create a post. |
||||
|
||||
###### Were you able to create a post? |
||||
|
||||
##### Enter the forum as a registered user and try to create an empty post. |
||||
|
||||
###### Were you prohibited to create the post? |
||||
|
||||
##### Try creating a post as a registered user and try to choose a category for that post. |
||||
|
||||
###### Were you able to choose a category for that post? |
||||
|
||||
##### Enter the forum as a registered user and try to like or dislike a post. |
||||
|
||||
###### Can you like or dislike the post? |
||||
|
||||
##### Enter the forum as a registered user and try to like or dislike a comment. |
||||
|
||||
###### Can you like or dislike the comment? |
||||
|
||||
##### Enter the forum as a registered user, try liking and disliking a post and then refresh the page. |
||||
|
||||
###### Does the number of likes/dislikes change? |
||||
|
||||
##### Enter the forum as a registered user and try to like and then dislike the same post. |
||||
|
||||
###### Can you confirm that it is not possible that the post is liked and disliked at the same time? |
||||
|
||||
##### Enter the forum as a registered user and try seeing all of your created posts. |
||||
|
||||
###### Does it present the expected posts? |
||||
|
||||
##### Enter the forum as a registered user and try seeing all of your liked posts. |
||||
|
||||
###### Does it present the expected posts? |
||||
|
||||
##### Navigate to a post of your choice and see its comments. |
||||
|
||||
###### Are all users (registered or not) able to see the number of likes and dislikes that comment has? |
||||
|
||||
##### Try seeing all posts from one category using the filter. |
||||
|
||||
###### Are all posts displayed from that category? |
||||
|
||||
###### Did the server behaved as expected?(did not crashed) |
||||
|
||||
###### Does the server use the right [HTTP method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods)? |
||||
|
||||
###### Are all the pages working? (Absence of 404 page?) |
||||
|
||||
###### Does the project handle [HTTP status 400 - Bad Requests](https://kinsta.com/knowledgebase/400-bad-request/#causes)? |
||||
|
||||
###### Does the project handle [HTTP status 500 - Internal Server Errors](https://www.restapitutorial.com/httpstatuscodes.html)? |
||||
|
||||
###### Are only the allowed packages being used? |
||||
|
||||
###### As an auditor, is this project up to every standard? If not, why are you failing the project?(Empty Work, Incomplete Work, Invalid compilation, Cheating, Crashing, Leaks) |
||||
|
||||
#### General |
||||
|
||||
###### +Does the project present a script to build the images and containers? (using a script to simplify the build) |
||||
|
||||
#### Basic |
||||
|
||||
###### +Does the project runs quickly and effectively? (Favoring recursive, no unnecessary data requests, etc) |
||||
|
||||
###### +Does the code obey the [good practices](https://public.01-edu.org/subjects/good-practices.en)? |
||||
|
||||
###### +Is there a test file for this code? |
||||
|
||||
#### Social |
||||
|
||||
###### +Did you learn anything from this project? |
||||
|
||||
###### +Can it be open-sourced / be used for other sources? |
||||
|
||||
###### +Would you recommend/nominate this program as an example for the rest of the school? |
||||
#### Authentication |
||||
|
||||
###### Are an email and a password asked for in the resgistration? |
||||
|
||||
###### Does the project detect if the email or password are wrong? |
||||
|
||||
###### Does the project detect if the email or user name is already taken in the registration? |
||||
|
||||
##### Try to register as a new user in the forum. |
||||
|
||||
###### Is it possible to register? |
||||
|
||||
##### Try to login with the user you created. |
||||
|
||||
###### Can you login and have all the rights of a registered user? |
||||
|
||||
##### Try to login without any credentials. |
||||
|
||||
###### Does it show a warning message? |
||||
|
||||
###### Are sessions present in the project? |
||||
|
||||
##### Try opening two different browsers and login into one of them. Refresh the other browser. |
||||
|
||||
###### Can you confirm that the browser non logged remains unregistered? |
||||
|
||||
##### Try opening two different browsers and login into both of them. Refresh both browsers. |
||||
|
||||
###### Can you confirm that only one of those browsers has an active session? |
||||
|
||||
##### Try opening two different browsers and login into one of them. Then create a new post or just add a comment. Refresh both browsers. |
||||
|
||||
###### Does it present the comment/post on both browsers? |
||||
|
||||
#### SQLite |
||||
|
||||
###### Does the code contain at least one CREATE query? |
||||
|
||||
###### Does the code contain at least one INSERT query? |
||||
|
||||
###### Does the code contain at least one SELECT query? |
||||
|
||||
##### Try registering in the forum, open the database with `sqlite3 <database_name.db>` and perform a query to select all the users (Example: SELECT \* FROM users;). |
||||
|
||||
###### Does it present the user you created? |
||||
|
||||
##### Try creating a post in the forum, open the database with `sqlite3 <database_name.db>` and perform a query to select all the users (Example: SELECT \* FROM post;). |
||||
|
||||
###### Does it present the post you created? |
||||
|
||||
##### Try creating a comment in the forum, open the database with `sqlite3 <database_name.db>` and perform a query to select all the users (Example: SELECT \* FROM comment;). |
||||
|
||||
###### Does it present the comment you created? |
||||
|
||||
#### Docker |
||||
|
||||
###### Does the project have Dockerfiles? |
||||
|
||||
##### Try to run the command `"docker image build [OPTINS] PATH | URL | -"` to build the image using using the project Dockerfiles and run the command `"docker images"` to see images. |
||||
|
||||
``` |
||||
student$ docker images |
||||
REPOSITORY TAG IMAGE ID CREATED SIZE |
||||
<name of the image> latest 85a65d66ca39 7 seconds ago 795MB |
||||
``` |
||||
|
||||
###### Does all images build as above? |
||||
|
||||
##### Try running the command `"docker container run [OPTIONS] IMAGE [COMMAND] [ARG...]"` to start the containers using the images just created and run the command `"docker ps -a"` to see containers. |
||||
|
||||
``` |
||||
student$ docker ps -a |
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES |
||||
cc8f5dcf760f <name of the image> "./server" 6 seconds ago Up 6 seconds 0.0.0.0:8080->8080/tcp ascii-art-web |
||||
``` |
||||
|
||||
###### Is the docker containers running as above? |
||||
|
||||
###### Does the project present no [unused object](https://docs.docker.com/config/pruning/)? |
||||
|
||||
#### Functional |
||||
|
||||
##### Enter the forum as a non-registered user. |
||||
|
||||
###### Are you prohibited to create a post? |
||||
|
||||
##### Enter the forum as a non-registered user. |
||||
|
||||
###### Are you prohibited to create a comment? |
||||
|
||||
##### Enter the forum as a non-registered user and try to like a comment. |
||||
|
||||
###### Are you prohibited to like a post? |
||||
|
||||
##### Enter the forum as a non-registered user and try to dislike a comment. |
||||
|
||||
###### Are you prohibited to dislike a comment? |
||||
|
||||
##### Enter the forum as a registered user, go to a post and try to create a comment for it. |
||||
|
||||
###### Were you able to create the comment? |
||||
|
||||
##### Enter the forum as a registered user, go to a post and try to create an empty comment for it. |
||||
|
||||
###### Were you prohibited to create the comment? |
||||
|
||||
##### Enter the forum as a registered user and try to create a post. |
||||
|
||||
###### Were you able to create a post? |
||||
|
||||
##### Enter the forum as a registered user and try to create an empty post. |
||||
|
||||
###### Were you prohibited to create the post? |
||||
|
||||
##### Try creating a post as a registered user and try to choose a category for that post. |
||||
|
||||
###### Were you able to choose a category for that post? |
||||
|
||||
##### Enter the forum as a registered user and try to like or dislike a post. |
||||
|
||||
###### Can you like or dislike the post? |
||||
|
||||
##### Enter the forum as a registered user and try to like or dislike a comment. |
||||
|
||||
###### Can you like or dislike the comment? |
||||
|
||||
##### Enter the forum as a registered user, try liking and disliking a post and then refresh the page. |
||||
|
||||
###### Does the number of likes/dislikes change? |
||||
|
||||
##### Enter the forum as a registered user and try to like and then dislike the same post. |
||||
|
||||
###### Can you confirm that it is not possible that the post is liked and disliked at the same time? |
||||
|
||||
##### Enter the forum as a registered user and try seeing all of your created posts. |
||||
|
||||
###### Does it present the expected posts? |
||||
|
||||
##### Enter the forum as a registered user and try seeing all of your liked posts. |
||||
|
||||
###### Does it present the expected posts? |
||||
|
||||
##### Navigate to a post of your choice and see its comments. |
||||
|
||||
###### Are all users (registered or not) able to see the number of likes and dislikes that comment has? |
||||
|
||||
##### Try seeing all posts from one category using the filter. |
||||
|
||||
###### Are all posts displayed from that category? |
||||
|
||||
###### Did the server behaved as expected?(did not crashed) |
||||
|
||||
###### Does the server use the right [HTTP method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods)? |
||||
|
||||
###### Are all the pages working? (Absence of 404 page?) |
||||
|
||||
###### Does the project handle [HTTP status 400 - Bad Requests](https://kinsta.com/knowledgebase/400-bad-request/#causes)? |
||||
|
||||
###### Does the project handle [HTTP status 500 - Internal Server Errors](https://www.restapitutorial.com/httpstatuscodes.html)? |
||||
|
||||
###### Are only the allowed packages being used? |
||||
|
||||
###### As an auditor, is this project up to every standard? If not, why are you failing the project?(Empty Work, Incomplete Work, Invalid compilation, Cheating, Crashing, Leaks) |
||||
|
||||
#### General |
||||
|
||||
###### +Does the project present a script to build the images and containers? (using a script to simplify the build) |
||||
|
||||
#### Basic |
||||
|
||||
###### +Does the project runs quickly and effectively? (Favoring recursive, no unnecessary data requests, etc) |
||||
|
||||
###### +Does the code obey the [good practices](https://public.01-edu.org/subjects/good-practices.en)? |
||||
|
||||
###### +Is there a test file for this code? |
||||
|
||||
#### Social |
||||
|
||||
###### +Did you learn anything from this project? |
||||
|
||||
###### +Can it be open-sourced / be used for other sources? |
||||
|
||||
###### +Would you recommend/nominate this program as an example for the rest of the school? |
||||
|
@ -1,28 +1,28 @@
|
||||
## Get Json |
||||
|
||||
### Instructions |
||||
|
||||
In this exercise, we will focus on building complex async flows with promises. |
||||
|
||||
Create a `getJSON` function that takes 2 parameters: |
||||
- `path`, that will be the url called by your function |
||||
- `params` *optional*, that will be the search parameters appended to your url |
||||
|
||||
`getJSON` must construct a valid url with the `path` and stringified `params` |
||||
and call `fetch` with it. |
||||
If the response is not ok, your function must throw an error using |
||||
the response status message. |
||||
|
||||
The response body must then be read and parsed from json. |
||||
|
||||
The parsed object contains one of those 2 properties: |
||||
- `"data"` the actual data to return |
||||
- `"error"` the error message to throw |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/promise.js](https://nan-academy.github.io/js-training/examples/promise.js) |
||||
- [devdocs.io/dom/fetch_api/using_fetch](https://devdocs.io/dom/fetch_api/using_fetch) |
||||
- [devdocs.io/dom/urlsearchparams](https://devdocs.io/dom/urlsearchparams) |
||||
- [devdocs.io/javascript/global_objects/json](https://devdocs.io/javascript/global_objects/json) |
||||
## Get Json |
||||
|
||||
### Instructions |
||||
|
||||
In this exercise, we will focus on building complex async flows with promises. |
||||
|
||||
Create a `getJSON` function that takes 2 parameters: |
||||
- `path`, that will be the url called by your function |
||||
- `params` *optional*, that will be the search parameters appended to your url |
||||
|
||||
`getJSON` must construct a valid url with the `path` and stringified `params` |
||||
and call `fetch` with it. |
||||
If the response is not ok, your function must throw an error using |
||||
the response status message. |
||||
|
||||
The response body must then be read and parsed from json. |
||||
|
||||
The parsed object contains one of those 2 properties: |
||||
- `"data"` the actual data to return |
||||
- `"error"` the error message to throw |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/promise.js](https://nan-academy.github.io/js-training/examples/promise.js) |
||||
- [devdocs.io/dom/fetch_api/using_fetch](https://devdocs.io/dom/fetch_api/using_fetch) |
||||
- [devdocs.io/dom/urlsearchparams](https://devdocs.io/dom/urlsearchparams) |
||||
- [devdocs.io/javascript/global_objects/json](https://devdocs.io/javascript/global_objects/json) |
||||
|
@ -1,45 +1,45 @@
|
||||
## Gougle Search |
||||
|
||||
### Instructions |
||||
|
||||
Create the `queryServers` function, that takes 2 arguments: |
||||
- `serverName` a string of the name of the server |
||||
- `q` a string of the query given by the user |
||||
|
||||
You have to construct 2 urls, using `q` as a search parameter, |
||||
prepending a `'/'` and for the 2nd appending `'_backup'`. |
||||
|
||||
Then return the first value of those 2 calls |
||||
|
||||
```js |
||||
queryServers('pouet', 'hello+world') |
||||
// return the fastest of those 2 calls: |
||||
// -> getJSON('/pouet?q=hello+world') |
||||
// -> getJSON('/pouet_backup?q=hello+world') |
||||
``` |
||||
|
||||
|
||||
Create a `gougleSearch` function that takes a single query argument. |
||||
It must call `queryServers` in concurrently on 3 servers: |
||||
`'web'`, `'image'` and `'video'`. |
||||
|
||||
A timeout of 80milliseconds must be set for the whole operation. |
||||
|
||||
You must return the value from each servers in an object |
||||
using a the server name as key. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/promise/race](https://devdocs.io/javascript/global_objects/promise/race) |
||||
- [devdocs.io/javascript/global_objects/promise/all](https://devdocs.io/javascript/global_objects/promise/all) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
// fake `getJSON` function |
||||
let getJSON = async (url) => url |
||||
``` |
||||
## Gougle Search |
||||
|
||||
### Instructions |
||||
|
||||
Create the `queryServers` function, that takes 2 arguments: |
||||
- `serverName` a string of the name of the server |
||||
- `q` a string of the query given by the user |
||||
|
||||
You have to construct 2 urls, using `q` as a search parameter, |
||||
prepending a `'/'` and for the 2nd appending `'_backup'`. |
||||
|
||||
Then return the first value of those 2 calls |
||||
|
||||
```js |
||||
queryServers('pouet', 'hello+world') |
||||
// return the fastest of those 2 calls: |
||||
// -> getJSON('/pouet?q=hello+world') |
||||
// -> getJSON('/pouet_backup?q=hello+world') |
||||
``` |
||||
|
||||
|
||||
Create a `gougleSearch` function that takes a single query argument. |
||||
It must call `queryServers` in concurrently on 3 servers: |
||||
`'web'`, `'image'` and `'video'`. |
||||
|
||||
A timeout of 80milliseconds must be set for the whole operation. |
||||
|
||||
You must return the value from each servers in an object |
||||
using a the server name as key. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/promise/race](https://devdocs.io/javascript/global_objects/promise/race) |
||||
- [devdocs.io/javascript/global_objects/promise/all](https://devdocs.io/javascript/global_objects/promise/all) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
// fake `getJSON` function |
||||
let getJSON = async (url) => url |
||||
``` |
||||
|
@ -1,16 +1,16 @@
|
||||
## Greedy Url |
||||
|
||||
### Instructions |
||||
|
||||
Write 3 functions : |
||||
|
||||
- `getURL` that returns an array with all valid URLs present in a data-set, http and https |
||||
- `greedyQuery` that returns URL with at least 3 or more parameters from all URLs that contain a query. |
||||
- `notSoGreedy` that returns URL with at least 2, but not more then 3 parameters from all URLs that contain a query. |
||||
|
||||
You can search for greedy quantifiers for help |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [github.com/ziishaned/learn-regex](https://github.com/ziishaned/learn-regex) |
||||
## Greedy Url |
||||
|
||||
### Instructions |
||||
|
||||
Write 3 functions : |
||||
|
||||
- `getURL` that returns an array with all valid URLs present in a data-set, http and https |
||||
- `greedyQuery` that returns URL with at least 3 or more parameters from all URLs that contain a query. |
||||
- `notSoGreedy` that returns URL with at least 2, but not more then 3 parameters from all URLs that contain a query. |
||||
|
||||
You can search for greedy quantifiers for help |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [github.com/ziishaned/learn-regex](https://github.com/ziishaned/learn-regex) |
||||
|
@ -1,103 +1,103 @@
|
||||
#### Functional |
||||
|
||||
###### Has the requirement for the allowed packages been respected? (Reminder for this project: (only [standard packages](https://golang.org/pkg/) |
||||
|
||||
###### Is the data from the artists being used? |
||||
|
||||
###### Is data from the relations being used? |
||||
|
||||
##### Try to see the "members" for the artist/band `"Queen"` |
||||
|
||||
``` |
||||
"Freddie Mercury", |
||||
"Brian May", |
||||
"John Daecon", |
||||
"Roger Meddows-Taylor", |
||||
"Mike Grose", |
||||
"Barry Mitchell", |
||||
"Doug Fogie" |
||||
``` |
||||
|
||||
###### Does it present the right "member", as above? |
||||
|
||||
##### Try to see the "firstAlbum" for the artist/band `"Gorillaz"` |
||||
|
||||
``` |
||||
"26-03-2001" |
||||
``` |
||||
|
||||
###### Does it present the right date for the "firstAlbum", as above? |
||||
|
||||
##### Try to see the "locations" for the artist/band `"Travis Scott"` |
||||
|
||||
``` |
||||
"santiago-chile" |
||||
"sao_paulo-brasil" |
||||
"los_angeles-usa" |
||||
"houston-usa" |
||||
"atlanta-usa" |
||||
"new_orleans-usa" |
||||
"philadelphia-usa" |
||||
"london-uk" |
||||
"frauenfeld-switzerland" |
||||
"turku-finland" |
||||
``` |
||||
|
||||
###### Does it present the right "locations" as above? |
||||
|
||||
##### Try to see the ""members"" for the artist/band `"Foo Fighters"`. |
||||
|
||||
``` |
||||
"Dave Grohl" |
||||
"Nate Mendel" |
||||
"Taylor Hawkins" |
||||
"Chris Shiflett" |
||||
"Pat Smear" |
||||
"Rami Jaffee" |
||||
``` |
||||
|
||||
###### Does it present the right members as above? |
||||
|
||||
##### Try to trigger an event using some kind of action (ex: Clicking the mouse over a certain element, pressing a key on the keyboard, resizing or closing the browser window, a form being submitted, an error occurring, etc). |
||||
|
||||
###### Does the event responds as expected? |
||||
|
||||
###### Did the server behaved as expected?(did not crashed) |
||||
|
||||
###### Does the server use the right [HTTP method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods)? |
||||
|
||||
###### Has the website runned without crashing at anytime? |
||||
|
||||
###### Are all the pages working? (Absence of 404 page?) |
||||
|
||||
###### Does the project handle [HTTP status 400 - Bad Requests](https://kinsta.com/knowledgebase/400-bad-request/#causes)? |
||||
|
||||
###### Does the project handle [HTTP status 500 - Internal Server Errors](https://www.restapitutorial.com/httpstatuscodes.html)? |
||||
|
||||
###### Is the communication between server and client well established? |
||||
|
||||
###### Does the server present all the needed handlers and patterns for the http requests? |
||||
|
||||
###### As an auditor, is this project up to every standard? If not, why are you failing the project?(Empty Work, Incomplete Work, Invalid compilation, Cheating, Crashing, Leaks) |
||||
|
||||
#### General |
||||
|
||||
###### +Does the event system run as asynchronous? (usage of go routines and channels) |
||||
|
||||
###### +Is the site hosted or deployed? Can you access the website through a DNS (Domain Name System)? |
||||
|
||||
#### Basic |
||||
|
||||
###### +Does the project runs quickly and effectively? (Favoring recursive, no unnecessary data requests, etc) |
||||
|
||||
###### +Does the code obey the [good practices](https://public.01-edu.org/subjects/good-practices.en)? |
||||
|
||||
###### +Is there a test file for this code? |
||||
|
||||
#### Social |
||||
|
||||
###### +Did you learn anything from this project? |
||||
|
||||
###### +Can it be open-sourced / be used for other sources? |
||||
|
||||
###### +Would you recommend/nominate this program as an example for the rest of the school? |
||||
#### Functional |
||||
|
||||
###### Has the requirement for the allowed packages been respected? (Reminder for this project: (only [standard packages](https://golang.org/pkg/) |
||||
|
||||
###### Is the data from the artists being used? |
||||
|
||||
###### Is data from the relations being used? |
||||
|
||||
##### Try to see the "members" for the artist/band `"Queen"` |
||||
|
||||
``` |
||||
"Freddie Mercury", |
||||
"Brian May", |
||||
"John Daecon", |
||||
"Roger Meddows-Taylor", |
||||
"Mike Grose", |
||||
"Barry Mitchell", |
||||
"Doug Fogie" |
||||
``` |
||||
|
||||
###### Does it present the right "member", as above? |
||||
|
||||
##### Try to see the "firstAlbum" for the artist/band `"Gorillaz"` |
||||
|
||||
``` |
||||
"26-03-2001" |
||||
``` |
||||
|
||||
###### Does it present the right date for the "firstAlbum", as above? |
||||
|
||||
##### Try to see the "locations" for the artist/band `"Travis Scott"` |
||||
|
||||
``` |
||||
"santiago-chile" |
||||
"sao_paulo-brasil" |
||||
"los_angeles-usa" |
||||
"houston-usa" |
||||
"atlanta-usa" |
||||
"new_orleans-usa" |
||||
"philadelphia-usa" |
||||
"london-uk" |
||||
"frauenfeld-switzerland" |
||||
"turku-finland" |
||||
``` |
||||
|
||||
###### Does it present the right "locations" as above? |
||||
|
||||
##### Try to see the ""members"" for the artist/band `"Foo Fighters"`. |
||||
|
||||
``` |
||||
"Dave Grohl" |
||||
"Nate Mendel" |
||||
"Taylor Hawkins" |
||||
"Chris Shiflett" |
||||
"Pat Smear" |
||||
"Rami Jaffee" |
||||
``` |
||||
|
||||
###### Does it present the right members as above? |
||||
|
||||
##### Try to trigger an event using some kind of action (ex: Clicking the mouse over a certain element, pressing a key on the keyboard, resizing or closing the browser window, a form being submitted, an error occurring, etc). |
||||
|
||||
###### Does the event responds as expected? |
||||
|
||||
###### Did the server behaved as expected?(did not crashed) |
||||
|
||||
###### Does the server use the right [HTTP method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods)? |
||||
|
||||
###### Has the website runned without crashing at anytime? |
||||
|
||||
###### Are all the pages working? (Absence of 404 page?) |
||||
|
||||
###### Does the project handle [HTTP status 400 - Bad Requests](https://kinsta.com/knowledgebase/400-bad-request/#causes)? |
||||
|
||||
###### Does the project handle [HTTP status 500 - Internal Server Errors](https://www.restapitutorial.com/httpstatuscodes.html)? |
||||
|
||||
###### Is the communication between server and client well established? |
||||
|
||||
###### Does the server present all the needed handlers and patterns for the http requests? |
||||
|
||||
###### As an auditor, is this project up to every standard? If not, why are you failing the project?(Empty Work, Incomplete Work, Invalid compilation, Cheating, Crashing, Leaks) |
||||
|
||||
#### General |
||||
|
||||
###### +Does the event system run as asynchronous? (usage of go routines and channels) |
||||
|
||||
###### +Is the site hosted or deployed? Can you access the website through a DNS (Domain Name System)? |
||||
|
||||
#### Basic |
||||
|
||||
###### +Does the project runs quickly and effectively? (Favoring recursive, no unnecessary data requests, etc) |
||||
|
||||
###### +Does the code obey the [good practices](https://public.01-edu.org/subjects/good-practices.en)? |
||||
|
||||
###### +Is there a test file for this code? |
||||
|
||||
#### Social |
||||
|
||||
###### +Did you learn anything from this project? |
||||
|
||||
###### +Can it be open-sourced / be used for other sources? |
||||
|
||||
###### +Would you recommend/nominate this program as an example for the rest of the school? |
||||
|
@ -1,32 +1,32 @@
|
||||
## Index Of |
||||
|
||||
### Instructions |
||||
|
||||
Create 3 functions: |
||||
- `indexOf` that returns the index of the first occurence of a value |
||||
- `lastIndexOf` that returns the index of the last occurence of a value |
||||
- `includes` that returns true if the value was found in the array |
||||
|
||||
> If a value is not found, the returned index is -1 |
||||
|
||||
> functions should have an array element as first argument, |
||||
> both `indexOf` and `lastIndexOf` take an additional `fromIndex` argument |
||||
> that allows you to begin searching from a specific index. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/array/indexof](https://devdocs.io/javascript/global_objects/array/indexof) |
||||
- [devdocs.io/javascript/global_objects/array/lastindexof](https://devdocs.io/javascript/global_objects/array/lastindexof) |
||||
- [devdocs.io/javascript/global_objects/array/includes](https://devdocs.io/javascript/global_objects/array/includes) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
Array.prototype.indexOf = undefined |
||||
Array.prototype.lastIndexOf = undefined |
||||
Array.prototype.includes = undefined |
||||
``` |
||||
## Index Of |
||||
|
||||
### Instructions |
||||
|
||||
Create 3 functions: |
||||
- `indexOf` that returns the index of the first occurence of a value |
||||
- `lastIndexOf` that returns the index of the last occurence of a value |
||||
- `includes` that returns true if the value was found in the array |
||||
|
||||
> If a value is not found, the returned index is -1 |
||||
|
||||
> functions should have an array element as first argument, |
||||
> both `indexOf` and `lastIndexOf` take an additional `fromIndex` argument |
||||
> that allows you to begin searching from a specific index. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/array/indexof](https://devdocs.io/javascript/global_objects/array/indexof) |
||||
- [devdocs.io/javascript/global_objects/array/lastindexof](https://devdocs.io/javascript/global_objects/array/lastindexof) |
||||
- [devdocs.io/javascript/global_objects/array/includes](https://devdocs.io/javascript/global_objects/array/includes) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
Array.prototype.indexOf = undefined |
||||
Array.prototype.lastIndexOf = undefined |
||||
Array.prototype.includes = undefined |
||||
``` |
||||
|
@ -1,45 +1,45 @@
|
||||
## Interpolation |
||||
|
||||
### Instructions |
||||
|
||||
Create a function called `interpolation` that takes an object with 5 properties |
||||
`step`, `start`, `end`, `callback` and `duration`. |
||||
This function must calculate the interpolation points, (x, y), |
||||
from the `start` position to `end` position depending on the number of `steps`. |
||||
All the points must be calculated in the duration time. |
||||
|
||||
For each interpolation point you must execute and pass as parameters to the callback the interpolation point ([x, y]) |
||||
|
||||
|
||||
### Example |
||||
|
||||
``` |
||||
steps = 5 |
||||
start = 0 |
||||
end = 1 |
||||
duration = 10 |
||||
|
||||
t |
||||
| |
||||
10 |___________________. <- execute callback([1.0, 10]) |
||||
| | |
||||
| | |
||||
8 |_______________. | |
||||
| | | |
||||
| | | |
||||
6 |___________. | | |
||||
| | | | |
||||
| | | | |
||||
4 |_______. | | | |
||||
| | | | | |
||||
| | | | | |
||||
2 |___. | | | | |
||||
| | | | | | |
||||
|___|___|___|___|___|__d |
||||
0 0.2 0.4 0.6 0.8 1 |
||||
``` |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [javascript.info/settimeout-setinterval](https://javascript.info/settimeout-setinterval) |
||||
## Interpolation |
||||
|
||||
### Instructions |
||||
|
||||
Create a function called `interpolation` that takes an object with 5 properties |
||||
`step`, `start`, `end`, `callback` and `duration`. |
||||
This function must calculate the interpolation points, (x, y), |
||||
from the `start` position to `end` position depending on the number of `steps`. |
||||
All the points must be calculated in the duration time. |
||||
|
||||
For each interpolation point you must execute and pass as parameters to the callback the interpolation point ([x, y]) |
||||
|
||||
|
||||
### Example |
||||
|
||||
``` |
||||
steps = 5 |
||||
start = 0 |
||||
end = 1 |
||||
duration = 10 |
||||
|
||||
t |
||||
| |
||||
10 |___________________. <- execute callback([1.0, 10]) |
||||
| | |
||||
| | |
||||
8 |_______________. | |
||||
| | | |
||||
| | | |
||||
6 |___________. | | |
||||
| | | | |
||||
| | | | |
||||
4 |_______. | | | |
||||
| | | | | |
||||
| | | | | |
||||
2 |___. | | | | |
||||
| | | | | | |
||||
|___|___|___|___|___|__d |
||||
0 0.2 0.4 0.6 0.8 1 |
||||
``` |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [javascript.info/settimeout-setinterval](https://javascript.info/settimeout-setinterval) |
||||
|
@ -1,9 +1,9 @@
|
||||
## Invert |
||||
|
||||
### Instructions |
||||
|
||||
Create a function called `invert` that inverts the object keys and values. |
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/object](https://devdocs.io/javascript/global_objects/object) |
||||
## Invert |
||||
|
||||
### Instructions |
||||
|
||||
Create a function called `invert` that inverts the object keys and values. |
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/object](https://devdocs.io/javascript/global_objects/object) |
||||
|
@ -1,11 +1,11 @@
|
||||
## Ion Out |
||||
|
||||
### Instructions |
||||
|
||||
Make a function `ionOut` that receives a string and returns an array with every |
||||
word containing 'ion' following a t, without the 'ion'. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [github.com/ziishaned/learn-regex#4-lookarounds](https://github.com/ziishaned/learn-regex#4-lookarounds) |
||||
## Ion Out |
||||
|
||||
### Instructions |
||||
|
||||
Make a function `ionOut` that receives a string and returns an array with every |
||||
word containing 'ion' following a t, without the 'ion'. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [github.com/ziishaned/learn-regex#4-lookarounds](https://github.com/ziishaned/learn-regex#4-lookarounds) |
||||
|
@ -1,21 +1,21 @@
|
||||
## Its A Match |
||||
|
||||
### Instructions |
||||
|
||||
Create 4 regular expression in variables: |
||||
|
||||
- `normal` matches with the expression 'hi'. |
||||
|
||||
- `begin` matches with the expression 'hi', |
||||
only when it is in the beginning. |
||||
|
||||
- `end` matches with the expression 'hi', |
||||
only when it is in the end. |
||||
|
||||
- `beginEnd` matches with the expression 'hi', |
||||
only when it is in the beginning and the end. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [github.com/ziishaned/learn-regex#28-anchors](https://github.com/ziishaned/learn-regex#28-anchors) |
||||
## Its A Match |
||||
|
||||
### Instructions |
||||
|
||||
Create 4 regular expression in variables: |
||||
|
||||
- `normal` matches with the expression 'hi'. |
||||
|
||||
- `begin` matches with the expression 'hi', |
||||
only when it is in the beginning. |
||||
|
||||
- `end` matches with the expression 'hi', |
||||
only when it is in the end. |
||||
|
||||
- `beginEnd` matches with the expression 'hi', |
||||
only when it is in the beginning and the end. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [github.com/ziishaned/learn-regex#28-anchors](https://github.com/ziishaned/learn-regex#28-anchors) |
||||
|
@ -1,26 +1,26 @@
|
||||
## Keep Cut |
||||
|
||||
### Instructions |
||||
|
||||
Create the `cutFirst` function that takes a string |
||||
and remove the 2 first characters. |
||||
|
||||
Create the `cutLast` function that takes a string |
||||
and remove the 2 last characters. |
||||
|
||||
Create the `cutFirstLast` function that takes a string as parameter |
||||
and remove the 2 first characters and 2 last characters. |
||||
|
||||
Create a `keepFirst` function that takes a string as parameter |
||||
and return the string only keeping the 2 first characters. |
||||
|
||||
Create a `keepLast` function that takes a string as parameter |
||||
and return the string only keeping the 2 last characters. |
||||
|
||||
Create a `keepFirstLast` function that takes a string as parameter |
||||
and only keep 2 first characters and 2 last characters. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/array/slice](https://devdocs.io/javascript/global_objects/array/slice) |
||||
## Keep Cut |
||||
|
||||
### Instructions |
||||
|
||||
Create the `cutFirst` function that takes a string |
||||
and remove the 2 first characters. |
||||
|
||||
Create the `cutLast` function that takes a string |
||||
and remove the 2 last characters. |
||||
|
||||
Create the `cutFirstLast` function that takes a string as parameter |
||||
and remove the 2 first characters and 2 last characters. |
||||
|
||||
Create a `keepFirst` function that takes a string as parameter |
||||
and return the string only keeping the 2 first characters. |
||||
|
||||
Create a `keepLast` function that takes a string as parameter |
||||
and return the string only keeping the 2 last characters. |
||||
|
||||
Create a `keepFirstLast` function that takes a string as parameter |
||||
and only keep 2 first characters and 2 last characters. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [devdocs.io/javascript/global_objects/array/slice](https://devdocs.io/javascript/global_objects/array/slice) |
||||
|
@ -1,32 +1,32 @@
|
||||
## Match Cron |
||||
|
||||
### Instructions |
||||
|
||||
Create a function called `matchCron` it takes a valid cron schedule string |
||||
and a valid date. \ |
||||
It returns true if the date match the pattern |
||||
|
||||
> You only have to implement numbers and `*`. \ |
||||
> other complex patterns are not required. |
||||
|
||||
Only valid pattern will be tested. |
||||
|
||||
### Example |
||||
|
||||
```js |
||||
matchCron('9 * * * *', new Date('2020-05-30 18:09:00')) // -> true |
||||
matchCron('9 * * * *', new Date('2020-05-30 19:09:00')) // -> true |
||||
matchCron('9 * * * *', new Date('2020-05-30 19:21:00')) // -> false |
||||
// | | | | | |
||||
// | | | | +- Day of the Week (range: 1-7, 1 standing for Monday) |
||||
// | | | +--- Month of the Year (range: 1-12) |
||||
// | | +----- Day of the Month (range: 1-31) |
||||
// | +------- Hour (range: 0-23) |
||||
// +--------- Minute (range: 0-59) |
||||
``` |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [crontab.guru](https://crontab.guru/) |
||||
- [devdocs.io/javascript/global_objects/date](https://devdocs.io/javascript/global_objects/date) |
||||
## Match Cron |
||||
|
||||
### Instructions |
||||
|
||||
Create a function called `matchCron` it takes a valid cron schedule string |
||||
and a valid date. \ |
||||
It returns true if the date match the pattern |
||||
|
||||
> You only have to implement numbers and `*`. \ |
||||
> other complex patterns are not required. |
||||
|
||||
Only valid pattern will be tested. |
||||
|
||||
### Example |
||||
|
||||
```js |
||||
matchCron('9 * * * *', new Date('2020-05-30 18:09:00')) // -> true |
||||
matchCron('9 * * * *', new Date('2020-05-30 19:09:00')) // -> true |
||||
matchCron('9 * * * *', new Date('2020-05-30 19:21:00')) // -> false |
||||
// | | | | | |
||||
// | | | | +- Day of the Week (range: 1-7, 1 standing for Monday) |
||||
// | | | +--- Month of the Year (range: 1-12) |
||||
// | | +----- Day of the Month (range: 1-31) |
||||
// | +------- Hour (range: 0-23) |
||||
// +--------- Minute (range: 0-59) |
||||
``` |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [crontab.guru](https://crontab.guru/) |
||||
- [devdocs.io/javascript/global_objects/date](https://devdocs.io/javascript/global_objects/date) |
||||
|
@ -1,15 +1,15 @@
|
||||
## More Or Less |
||||
|
||||
### Instructions |
||||
|
||||
Create 4 functions |
||||
- `more` that takes 1 argument and add 1 to it |
||||
- `less` that takes 1 argument and substract 1 to it |
||||
- `add` that takes 2 arguments and add them |
||||
- `sub` that takes 2 arguments and substract them |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/functions](https://nan-academy.github.io/js-training/examples/functions.js) |
||||
- [nan-academy.github.io/js-training/examples/primitive-and-operators](https://nan-academy.github.io/js-training/examples/primitive-and-operators.js) |
||||
## More Or Less |
||||
|
||||
### Instructions |
||||
|
||||
Create 4 functions |
||||
- `more` that takes 1 argument and add 1 to it |
||||
- `less` that takes 1 argument and substract 1 to it |
||||
- `add` that takes 2 arguments and add them |
||||
- `sub` that takes 2 arguments and substract them |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/functions](https://nan-academy.github.io/js-training/examples/functions.js) |
||||
- [nan-academy.github.io/js-training/examples/primitive-and-operators](https://nan-academy.github.io/js-training/examples/primitive-and-operators.js) |
||||
|
@ -1,31 +1,31 @@
|
||||
## Mutability |
||||
|
||||
### Instructions |
||||
|
||||
Create a copy of the person object called clone1. |
||||
Create an other copy of the person object called clone2. |
||||
|
||||
Create a new variable `samePerson` with the same value as `person`. |
||||
|
||||
Increase by one the property age of `person` |
||||
and set his country to `'FR'`. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/set](https://nan-academy.github.io/js-training/examples/set.js) |
||||
- [nan-academy.github.io/js-training/examples/get](https://nan-academy.github.io/js-training/examples/get.js) |
||||
- [nan-academy.github.io/js-training/examples/data-structures](https://nan-academy.github.io/js-training/examples/data-structures.js) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
const person = { |
||||
name: 'Rick', |
||||
age: 77, |
||||
country: 'US', |
||||
} |
||||
``` |
||||
## Mutability |
||||
|
||||
### Instructions |
||||
|
||||
Create a copy of the person object called clone1. |
||||
Create an other copy of the person object called clone2. |
||||
|
||||
Create a new variable `samePerson` with the same value as `person`. |
||||
|
||||
Increase by one the property age of `person` |
||||
and set his country to `'FR'`. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/set](https://nan-academy.github.io/js-training/examples/set.js) |
||||
- [nan-academy.github.io/js-training/examples/get](https://nan-academy.github.io/js-training/examples/get.js) |
||||
- [nan-academy.github.io/js-training/examples/data-structures](https://nan-academy.github.io/js-training/examples/data-structures.js) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
const person = { |
||||
name: 'Rick', |
||||
age: 77, |
||||
country: 'US', |
||||
} |
||||
``` |
||||
|
@ -1,15 +1,15 @@
|
||||
## Nasa |
||||
|
||||
### Instructions |
||||
|
||||
Create a `nasa` function that takes a number `N` as a parameter and returns |
||||
a string with all numbers from 1 to the `N` separated by whitespace, with three exceptions: |
||||
- For numbers divisible by 3, add 'NA'. |
||||
- For numbers divisible by 5, add 'SA'. |
||||
- For the number that are divisible by 3 and 5 add 'NASA'. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/loops](https://nan-academy.github.io/js-training/examples/loops.js) |
||||
- [nan-academy.github.io/js-training/examples/recursion](https://nan-academy.github.io/js-training/examples/recursion.js) |
||||
## Nasa |
||||
|
||||
### Instructions |
||||
|
||||
Create a `nasa` function that takes a number `N` as a parameter and returns |
||||
a string with all numbers from 1 to the `N` separated by whitespace, with three exceptions: |
||||
- For numbers divisible by 3, add 'NA'. |
||||
- For numbers divisible by 5, add 'SA'. |
||||
- For the number that are divisible by 3 and 5 add 'NASA'. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/loops](https://nan-academy.github.io/js-training/examples/loops.js) |
||||
- [nan-academy.github.io/js-training/examples/recursion](https://nan-academy.github.io/js-training/examples/recursion.js) |
||||
|
@ -1,15 +1,15 @@
|
||||
## Primitives |
||||
|
||||
### Instructions |
||||
|
||||
Create a constant variable for each primitives: |
||||
- `str` for `String` |
||||
- `num` for `Number` |
||||
- `bool` for `Boolean` |
||||
- `undef` for `undefined` |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/primitive-and-operators](https://nan-academy.github.io/js-training/examples/primitive-and-operators.js) |
||||
- [nan-academy.github.io/js-training/examples/variables](https://nan-academy.github.io/js-training/examples/variables.js) |
||||
## Primitives |
||||
|
||||
### Instructions |
||||
|
||||
Create a constant variable for each primitives: |
||||
- `str` for `String` |
||||
- `num` for `Number` |
||||
- `bool` for `Boolean` |
||||
- `undef` for `undefined` |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/primitive-and-operators](https://nan-academy.github.io/js-training/examples/primitive-and-operators.js) |
||||
- [nan-academy.github.io/js-training/examples/variables](https://nan-academy.github.io/js-training/examples/variables.js) |
||||
|
@ -1,27 +1,27 @@
|
||||
## Pyramid |
||||
|
||||
### Instructions |
||||
|
||||
Create a `pyramid` function that takes a string and a number as parameters |
||||
and return a pyramid constructed by the string passed as argument and with the depth |
||||
of the number passed as argument. |
||||
|
||||
Just like triangle |
||||
|
||||
### Output example |
||||
|
||||
- `*` character and depth of 5 : |
||||
|
||||
``` |
||||
* |
||||
*** |
||||
***** |
||||
******* |
||||
********* |
||||
``` |
||||
> No new line in last line |
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/loops](https://nan-academy.github.io/js-training/examples/loops.js) |
||||
- [nan-academy.github.io/js-training/examples/recursion](https://nan-academy.github.io/js-training/examples/recursion.js) |
||||
## Pyramid |
||||
|
||||
### Instructions |
||||
|
||||
Create a `pyramid` function that takes a string and a number as parameters |
||||
and return a pyramid constructed by the string passed as argument and with the depth |
||||
of the number passed as argument. |
||||
|
||||
Just like triangle |
||||
|
||||
### Output example |
||||
|
||||
- `*` character and depth of 5 : |
||||
|
||||
``` |
||||
* |
||||
*** |
||||
***** |
||||
******* |
||||
********* |
||||
``` |
||||
> No new line in last line |
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/loops](https://nan-academy.github.io/js-training/examples/loops.js) |
||||
- [nan-academy.github.io/js-training/examples/recursion](https://nan-academy.github.io/js-training/examples/recursion.js) |
||||
|
@ -1,24 +1,24 @@
|
||||
## Race |
||||
|
||||
### Instructions |
||||
|
||||
Create a function `race` that works like `Promise.race` |
||||
|
||||
Create a function `some` that takes an array of promises or values |
||||
and a number and return the first resolved values up to the number given. |
||||
> Empty array or a count of 0 return a promise resolving to `undefined` |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/promise](https://nan-academy.github.io/js-training/examples/promise.js) |
||||
- [devdocs.io/javascript/global_objects/promise/race](https://devdocs.io/javascript/global_objects/promise/race) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
Promise.race = undefined |
||||
``` |
||||
## Race |
||||
|
||||
### Instructions |
||||
|
||||
Create a function `race` that works like `Promise.race` |
||||
|
||||
Create a function `some` that takes an array of promises or values |
||||
and a number and return the first resolved values up to the number given. |
||||
> Empty array or a count of 0 return a promise resolving to `undefined` |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/promise](https://nan-academy.github.io/js-training/examples/promise.js) |
||||
- [devdocs.io/javascript/global_objects/promise/race](https://devdocs.io/javascript/global_objects/promise/race) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
Promise.race = undefined |
||||
``` |
||||
|
@ -1,24 +1,24 @@
|
||||
## Repeat |
||||
|
||||
### Instructions |
||||
|
||||
Create a `repeat` function that takes a string and a number as parameters |
||||
and return the repeated string by the given number |
||||
Like the method [developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat) |
||||
Of course you may not use the method directly |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/loops](https://nan-academy.github.io/js-training/examples/loops.js) |
||||
- [nan-academy.github.io/js-training/examples/recursion](https://nan-academy.github.io/js-training/examples/recursion.js) |
||||
- [devdocs.io/javascript/global_objects/string/repeat](https://devdocs.io/javascript/global_objects/string/repeat) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
String.prototype.repeat = undefined |
||||
``` |
||||
## Repeat |
||||
|
||||
### Instructions |
||||
|
||||
Create a `repeat` function that takes a string and a number as parameters |
||||
and return the repeated string by the given number |
||||
Like the method [developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat) |
||||
Of course you may not use the method directly |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/loops](https://nan-academy.github.io/js-training/examples/loops.js) |
||||
- [nan-academy.github.io/js-training/examples/recursion](https://nan-academy.github.io/js-training/examples/recursion.js) |
||||
- [devdocs.io/javascript/global_objects/string/repeat](https://devdocs.io/javascript/global_objects/string/repeat) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
String.prototype.repeat = undefined |
||||
``` |
||||
|
@ -1,12 +1,12 @@
|
||||
## Replica |
||||
|
||||
### Instructions |
||||
|
||||
Create a function called `replica` that allows you to deep assign the values of all properties from one or more |
||||
objects to a target object. |
||||
|
||||
Attention with the shallow copies. |
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/data-structures](https://nan-academy.github.io/js-training/examples/data-structures.js) |
||||
## Replica |
||||
|
||||
### Instructions |
||||
|
||||
Create a function called `replica` that allows you to deep assign the values of all properties from one or more |
||||
objects to a target object. |
||||
|
||||
Attention with the shallow copies. |
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/data-structures](https://nan-academy.github.io/js-training/examples/data-structures.js) |
||||
|
@ -1,15 +1,15 @@
|
||||
## Returns |
||||
|
||||
### Instructions |
||||
|
||||
Create the following functions: |
||||
|
||||
- `id` that takes one argument and return it |
||||
- `getLength` that takes an array or a string and return its length |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/functions](https://nan-academy.github.io/js-training/examples/functions.js) |
||||
- [nan-academy.github.io/js-training/examples/data-structures](https://nan-academy.github.io/js-training/examples/data-structures.js) |
||||
- [nan-academy.github.io/js-training/examples/get](https://nan-academy.github.io/js-training/examples/get.js) |
||||
## Returns |
||||
|
||||
### Instructions |
||||
|
||||
Create the following functions: |
||||
|
||||
- `id` that takes one argument and return it |
||||
- `getLength` that takes an array or a string and return its length |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/functions](https://nan-academy.github.io/js-training/examples/functions.js) |
||||
- [nan-academy.github.io/js-training/examples/data-structures](https://nan-academy.github.io/js-training/examples/data-structures.js) |
||||
- [nan-academy.github.io/js-training/examples/get](https://nan-academy.github.io/js-training/examples/get.js) |
||||
|
@ -1,11 +1,11 @@
|
||||
## Series |
||||
|
||||
### Instructions |
||||
|
||||
Create a function `series` that works takes an array of async functions. |
||||
It must execute them in series and return the results in order. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/promise](https://nan-academy.github.io/js-training/examples/promise.js) |
||||
## Series |
||||
|
||||
### Instructions |
||||
|
||||
Create a function `series` that works takes an array of async functions. |
||||
It must execute them in series and return the results in order. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/promise](https://nan-academy.github.io/js-training/examples/promise.js) |
||||
|
@ -1,27 +1,27 @@
|
||||
## Sign |
||||
|
||||
### Instructions |
||||
|
||||
Create the `sign` function that takes one number argument |
||||
and return 1 if the number is positive, -1 if the number is negative |
||||
and 0 if the number is exactly 0 |
||||
You must not just use `Math.sign`, make your own. |
||||
|
||||
Create the `sameSign` function that takes 2 numbers as arguments and return true |
||||
if they both have the same sign, or false otherwise. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/functions](https://nan-academy.github.io/js-training/examples/functions.js) |
||||
- [nan-academy.github.io/js-training/examples/if-else](https://nan-academy.github.io/js-training/examples/if-else.js) |
||||
- [devdocs.io/javascript/global_objects/math/sign](https://devdocs.io/javascript/global_objects/math/sign) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
Math.sign = undefined |
||||
``` |
||||
## Sign |
||||
|
||||
### Instructions |
||||
|
||||
Create the `sign` function that takes one number argument |
||||
and return 1 if the number is positive, -1 if the number is negative |
||||
and 0 if the number is exactly 0 |
||||
You must not just use `Math.sign`, make your own. |
||||
|
||||
Create the `sameSign` function that takes 2 numbers as arguments and return true |
||||
if they both have the same sign, or false otherwise. |
||||
|
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/functions](https://nan-academy.github.io/js-training/examples/functions.js) |
||||
- [nan-academy.github.io/js-training/examples/if-else](https://nan-academy.github.io/js-training/examples/if-else.js) |
||||
- [devdocs.io/javascript/global_objects/math/sign](https://devdocs.io/javascript/global_objects/math/sign) |
||||
|
||||
|
||||
### Code provided |
||||
|
||||
> all code provided will be added to your solution and doesn't need to be submited. |
||||
|
||||
```js |
||||
Math.sign = undefined |
||||
``` |
||||
|
@ -1,11 +1,11 @@
|
||||
## bad example 00 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
``` |
||||
#### |
||||
...# |
||||
.... |
||||
.... |
||||
|
||||
``` |
||||
## bad example 00 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
``` |
||||
#### |
||||
...# |
||||
.... |
||||
.... |
||||
|
||||
``` |
||||
|
@ -1,11 +1,11 @@
|
||||
## bad example 01 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
``` |
||||
...# |
||||
..#. |
||||
.#.. |
||||
#... |
||||
|
||||
``` |
||||
## bad example 01 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
``` |
||||
...# |
||||
..#. |
||||
.#.. |
||||
#... |
||||
|
||||
``` |
||||
|
@ -1,11 +1,11 @@
|
||||
## bad example 02 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
``` |
||||
...# |
||||
...# |
||||
#... |
||||
#... |
||||
|
||||
``` |
||||
## bad example 02 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
``` |
||||
...# |
||||
...# |
||||
#... |
||||
#... |
||||
|
||||
``` |
||||
|
@ -1,11 +1,11 @@
|
||||
## bad example 03 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
``` |
||||
.... |
||||
.... |
||||
.... |
||||
.... |
||||
|
||||
``` |
||||
## bad example 03 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
``` |
||||
.... |
||||
.... |
||||
.... |
||||
.... |
||||
|
||||
``` |
||||
|
@ -1,11 +1,11 @@
|
||||
## bad example 04 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
``` |
||||
..## |
||||
.... |
||||
.... |
||||
##.. |
||||
|
||||
``` |
||||
## bad example 04 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
``` |
||||
..## |
||||
.... |
||||
.... |
||||
##.. |
||||
|
||||
``` |
||||
|
@ -1,11 +1,11 @@
|
||||
## good example 00 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
```console |
||||
.... |
||||
.##. |
||||
.##. |
||||
.... |
||||
|
||||
``` |
||||
## good example 00 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
```console |
||||
.... |
||||
.##. |
||||
.##. |
||||
.... |
||||
|
||||
``` |
||||
|
@ -1,26 +1,26 @@
|
||||
## good example 01 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
``` |
||||
...# |
||||
...# |
||||
...# |
||||
...# |
||||
|
||||
.... |
||||
.... |
||||
.... |
||||
#### |
||||
|
||||
.### |
||||
...# |
||||
.... |
||||
.... |
||||
|
||||
.... |
||||
..## |
||||
.##. |
||||
.... |
||||
|
||||
``` |
||||
## good example 01 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
``` |
||||
...# |
||||
...# |
||||
...# |
||||
...# |
||||
|
||||
.... |
||||
.... |
||||
.... |
||||
#### |
||||
|
||||
.### |
||||
...# |
||||
.... |
||||
.... |
||||
|
||||
.... |
||||
..## |
||||
.##. |
||||
.... |
||||
|
||||
``` |
||||
|
@ -1,46 +1,46 @@
|
||||
## good example 02 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
``` |
||||
...# |
||||
...# |
||||
...# |
||||
...# |
||||
|
||||
.... |
||||
.... |
||||
.... |
||||
#### |
||||
|
||||
.### |
||||
...# |
||||
.... |
||||
.... |
||||
|
||||
.... |
||||
..## |
||||
.##. |
||||
.... |
||||
|
||||
.... |
||||
.##. |
||||
.##. |
||||
.... |
||||
|
||||
.... |
||||
.... |
||||
##.. |
||||
.##. |
||||
|
||||
##.. |
||||
.#.. |
||||
.#.. |
||||
.... |
||||
|
||||
.... |
||||
###. |
||||
.#.. |
||||
.... |
||||
|
||||
``` |
||||
## good example 02 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
``` |
||||
...# |
||||
...# |
||||
...# |
||||
...# |
||||
|
||||
.... |
||||
.... |
||||
.... |
||||
#### |
||||
|
||||
.### |
||||
...# |
||||
.... |
||||
.... |
||||
|
||||
.... |
||||
..## |
||||
.##. |
||||
.... |
||||
|
||||
.... |
||||
.##. |
||||
.##. |
||||
.... |
||||
|
||||
.... |
||||
.... |
||||
##.. |
||||
.##. |
||||
|
||||
##.. |
||||
.#.. |
||||
.#.. |
||||
.... |
||||
|
||||
.... |
||||
###. |
||||
.#.. |
||||
.... |
||||
|
||||
``` |
||||
|
@ -1,61 +1,61 @@
|
||||
## good example 03 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
``` |
||||
.... |
||||
.##. |
||||
.##. |
||||
.... |
||||
|
||||
...# |
||||
...# |
||||
...# |
||||
...# |
||||
|
||||
.... |
||||
..## |
||||
.##. |
||||
.... |
||||
|
||||
.... |
||||
.##. |
||||
.##. |
||||
.... |
||||
|
||||
.... |
||||
..#. |
||||
.##. |
||||
.#.. |
||||
|
||||
.### |
||||
...# |
||||
.... |
||||
.... |
||||
|
||||
##.. |
||||
.#.. |
||||
.#.. |
||||
.... |
||||
|
||||
.... |
||||
..## |
||||
.##. |
||||
.... |
||||
|
||||
##.. |
||||
.#.. |
||||
.#.. |
||||
.... |
||||
|
||||
.#.. |
||||
.##. |
||||
..#. |
||||
.... |
||||
|
||||
.... |
||||
###. |
||||
.#.. |
||||
.... |
||||
|
||||
``` |
||||
## good example 03 |
||||
|
||||
- Create your file and copy the example into it. |
||||
|
||||
``` |
||||
.... |
||||
.##. |
||||
.##. |
||||
.... |
||||
|
||||
...# |
||||
...# |
||||
...# |
||||
...# |
||||
|
||||
.... |
||||
..## |
||||
.##. |
||||
.... |
||||
|
||||
.... |
||||
.##. |
||||
.##. |
||||
.... |
||||
|
||||
.... |
||||
..#. |
||||
.##. |
||||
.#.. |
||||
|
||||
.### |
||||
...# |
||||
.... |
||||
.... |
||||
|
||||
##.. |
||||
.#.. |
||||
.#.. |
||||
.... |
||||
|
||||
.... |
||||
..## |
||||
.##. |
||||
.... |
||||
|
||||
##.. |
||||
.#.. |
||||
.#.. |
||||
.... |
||||
|
||||
.#.. |
||||
.##. |
||||
..#. |
||||
.... |
||||
|
||||
.... |
||||
###. |
||||
.#.. |
||||
.... |
||||
|
||||
``` |
||||
|
@ -1,83 +1,83 @@
|
||||
#### Functional |
||||
|
||||
###### Has the requirement for the allowed packages been respected? (Reminder for this project: (only [standard packages](https://golang.org/pkg/) |
||||
|
||||
##### Try [bad example 00](https://public.01-edu.org/subjects/tetris-optimizer/badexample00). |
||||
|
||||
`ERROR` |
||||
|
||||
###### Does the program prints the value above? |
||||
|
||||
##### Try [bad example 01](https://public.01-edu.org/subjects/tetris-optimizer/badexample01). |
||||
|
||||
`ERROR` |
||||
|
||||
###### Does the program prints the value above? |
||||
|
||||
##### Try [bad example 02](https://public.01-edu.org/subjects/tetris-optimizer/badexample02). |
||||
|
||||
`ERROR` |
||||
|
||||
###### Does the program prints the value above? |
||||
|
||||
##### Try [bad example 03](https://public.01-edu.org/subjects/tetris-optimizer/badexample03). |
||||
|
||||
`ERROR` |
||||
|
||||
###### Does the program prints the value above? |
||||
|
||||
##### Try [bad example 04](https://public.01-edu.org/subjects/tetris-optimizer/badexample04). |
||||
|
||||
`ERROR` |
||||
|
||||
###### Does the program prints the value above? |
||||
|
||||
##### Try [bad format](https://public.01-edu.org/subjects/tetris-optimizer/badformat). |
||||
|
||||
`ERROR` |
||||
|
||||
###### Does the program prints the value above? |
||||
|
||||
##### Try [good example 00](https://public.01-edu.org/subjects/tetris-optimizer/goodexample00). |
||||
|
||||
###### Does the result contain 0 empty spaces (0 '.')? |
||||
|
||||
##### Try [good example 01](https://public.01-edu.org/subjects/tetris-optimizer/goodexample01). |
||||
|
||||
###### Does the result contain 9 empty spaces (9 '.')? |
||||
|
||||
##### Try [good example 02](https://public.01-edu.org/subjects/tetris-optimizer/goodexample02). |
||||
|
||||
###### Does the result contain 4 empty spaces (4 '.') and a time limit lesser or equal to the one in the [good practices](https://public.01-edu.org/subjects/good-practices.en)? |
||||
|
||||
##### Try [good example 03](https://public.01-edu.org/subjects/tetris-optimizer/goodexample03). |
||||
|
||||
###### Does the result contain 5 empty spaces (5 '.') and a time limit lesser or equal to the one in the [good practices](https://public.01-edu.org/subjects/good-practices.en)? |
||||
|
||||
##### Try [hard example](https://public.01-edu.org/subjects/tetris-optimizer/hardexam). |
||||
|
||||
###### Does the result contain 1 empty spaces (1 '.') and a time limit lesser or equal to the one in the [good practices](https://public.01-edu.org/subjects/good-practices.en)? |
||||
|
||||
###### Are all of the Tetrominos contained in the test file, present in the output? |
||||
|
||||
###### Different characters correspond to different Tetrominos? |
||||
|
||||
###### Does one Tetromino has only one character? |
||||
|
||||
###### As an auditor, is this project up to every standard? If not, why are you failing the project?(Empty Work, Incomplete Work, Invalid compilation, Cheating, Crashing, Leaks) |
||||
|
||||
#### Basic |
||||
|
||||
###### +Does the project runs quickly and effectively (Favoring of recursive, no unnecessary data requests, etc.)? |
||||
|
||||
###### +Is there a test file for this code? |
||||
|
||||
###### +Are the tests checking each possible case? |
||||
|
||||
###### +Does the code obey the [good practices](https://public.01-edu.org/subjects/good-practices.en)? |
||||
|
||||
#### Social |
||||
|
||||
###### +Did you learn anything from this project? |
||||
|
||||
###### +Would you recommend/nominate this program as an example for the rest of the school? |
||||
#### Functional |
||||
|
||||
###### Has the requirement for the allowed packages been respected? (Reminder for this project: (only [standard packages](https://golang.org/pkg/) |
||||
|
||||
##### Try [bad example 00](https://public.01-edu.org/subjects/tetris-optimizer/badexample00). |
||||
|
||||
`ERROR` |
||||
|
||||
###### Does the program prints the value above? |
||||
|
||||
##### Try [bad example 01](https://public.01-edu.org/subjects/tetris-optimizer/badexample01). |
||||
|
||||
`ERROR` |
||||
|
||||
###### Does the program prints the value above? |
||||
|
||||
##### Try [bad example 02](https://public.01-edu.org/subjects/tetris-optimizer/badexample02). |
||||
|
||||
`ERROR` |
||||
|
||||
###### Does the program prints the value above? |
||||
|
||||
##### Try [bad example 03](https://public.01-edu.org/subjects/tetris-optimizer/badexample03). |
||||
|
||||
`ERROR` |
||||
|
||||
###### Does the program prints the value above? |
||||
|
||||
##### Try [bad example 04](https://public.01-edu.org/subjects/tetris-optimizer/badexample04). |
||||
|
||||
`ERROR` |
||||
|
||||
###### Does the program prints the value above? |
||||
|
||||
##### Try [bad format](https://public.01-edu.org/subjects/tetris-optimizer/badformat). |
||||
|
||||
`ERROR` |
||||
|
||||
###### Does the program prints the value above? |
||||
|
||||
##### Try [good example 00](https://public.01-edu.org/subjects/tetris-optimizer/goodexample00). |
||||
|
||||
###### Does the result contain 0 empty spaces (0 '.')? |
||||
|
||||
##### Try [good example 01](https://public.01-edu.org/subjects/tetris-optimizer/goodexample01). |
||||
|
||||
###### Does the result contain 9 empty spaces (9 '.')? |
||||
|
||||
##### Try [good example 02](https://public.01-edu.org/subjects/tetris-optimizer/goodexample02). |
||||
|
||||
###### Does the result contain 4 empty spaces (4 '.') and a time limit lesser or equal to the one in the [good practices](https://public.01-edu.org/subjects/good-practices.en)? |
||||
|
||||
##### Try [good example 03](https://public.01-edu.org/subjects/tetris-optimizer/goodexample03). |
||||
|
||||
###### Does the result contain 5 empty spaces (5 '.') and a time limit lesser or equal to the one in the [good practices](https://public.01-edu.org/subjects/good-practices.en)? |
||||
|
||||
##### Try [hard example](https://public.01-edu.org/subjects/tetris-optimizer/hardexam). |
||||
|
||||
###### Does the result contain 1 empty spaces (1 '.') and a time limit lesser or equal to the one in the [good practices](https://public.01-edu.org/subjects/good-practices.en)? |
||||
|
||||
###### Are all of the Tetrominos contained in the test file, present in the output? |
||||
|
||||
###### Different characters correspond to different Tetrominos? |
||||
|
||||
###### Does one Tetromino has only one character? |
||||
|
||||
###### As an auditor, is this project up to every standard? If not, why are you failing the project?(Empty Work, Incomplete Work, Invalid compilation, Cheating, Crashing, Leaks) |
||||
|
||||
#### Basic |
||||
|
||||
###### +Does the project runs quickly and effectively (Favoring of recursive, no unnecessary data requests, etc.)? |
||||
|
||||
###### +Is there a test file for this code? |
||||
|
||||
###### +Are the tests checking each possible case? |
||||
|
||||
###### +Does the code obey the [good practices](https://public.01-edu.org/subjects/good-practices.en)? |
||||
|
||||
#### Social |
||||
|
||||
###### +Did you learn anything from this project? |
||||
|
||||
###### +Would you recommend/nominate this program as an example for the rest of the school? |
||||
|
@ -1,25 +1,25 @@
|
||||
## Triangle |
||||
|
||||
### Instructions |
||||
|
||||
Create a `triangle` function that takes a string and a number as parameters |
||||
and return a triangle constructed by the string passed as argument and with the depth |
||||
of the number passed as argument. |
||||
|
||||
### Output example |
||||
|
||||
- `*` character and depth of 5: |
||||
|
||||
``` |
||||
* |
||||
** |
||||
*** |
||||
**** |
||||
***** |
||||
``` |
||||
> No new line in last line |
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/loops](https://nan-academy.github.io/js-training/examples/loops.js) |
||||
- [nan-academy.github.io/js-training/examples/recursion](https://nan-academy.github.io/js-training/examples/recursion.js) |
||||
## Triangle |
||||
|
||||
### Instructions |
||||
|
||||
Create a `triangle` function that takes a string and a number as parameters |
||||
and return a triangle constructed by the string passed as argument and with the depth |
||||
of the number passed as argument. |
||||
|
||||
### Output example |
||||
|
||||
- `*` character and depth of 5: |
||||
|
||||
``` |
||||
* |
||||
** |
||||
*** |
||||
**** |
||||
***** |
||||
``` |
||||
> No new line in last line |
||||
|
||||
### Notions |
||||
|
||||
- [nan-academy.github.io/js-training/examples/loops](https://nan-academy.github.io/js-training/examples/loops.js) |
||||
- [nan-academy.github.io/js-training/examples/recursion](https://nan-academy.github.io/js-training/examples/recursion.js) |
||||
|
@ -1,171 +1,171 @@
|
||||
/******************************* |
||||
* functions given to the students |
||||
********************************/ |
||||
const SIZE = 100 |
||||
const MAP = new Int8Array(SIZE * SIZE) |
||||
const isFree = ({ x, y }) => MAP[y * SIZE + x] === 0 |
||||
const isOccupied = ({ x, y }) => MAP[y * SIZE + x] === 1 |
||||
const inBounds = (n) => n < SIZE && n >= 0 |
||||
const isInBounds = ({ x, y }) => inBounds(x) && inBounds(y) |
||||
const pickRandom = (arr) => arr[Math.floor(Math.random() * arr.length)] |
||||
|
||||
/*********** |
||||
* My functions |
||||
************/ |
||||
const isAlley = ({ x, y }) => !isFree({ x, y }) || !isInBounds({ x, y }) |
||||
|
||||
// this functions will find the best path, so the path that has more empty spaces
|
||||
// so use `isFree`,
|
||||
const findBestPath = (state) => { |
||||
let arr = [] |
||||
let car = state.player.cardinal |
||||
// if it as a block on the symmetric position it must
|
||||
// simulate the symmetric position and see witch path is the best
|
||||
if ( |
||||
(car === 3 || car === 0) && |
||||
!isFree({ x: state.player.x - 1, y: state.player.y - 1 }) && |
||||
isFree({ x: state.player.x, y: state.player.y - 1 }) && |
||||
isFree({ x: state.player.x - 1, y: state.player.y }) |
||||
) { |
||||
let xad = state.player.x - 1 |
||||
let yad = state.player.y - 1 |
||||
|
||||
let choose = [ |
||||
calDistance(xad + 1, yad, 1, 0), |
||||
calDistance(state.player.x, state.player.y - 1, car, 0), |
||||
calDistance(state.player.x - 1, state.player.y, car, 0), |
||||
calDistance(xad, yad + 1, 2, 0), |
||||
] |
||||
let index = choose.indexOf(Math.max(...choose)) |
||||
return index === 0 || index === 1 |
||||
? state.player.coords[0] |
||||
: state.player.coords[3] |
||||
} |
||||
if ( |
||||
(car === 1 || car === 0) && |
||||
!isFree({ x: state.player.x + 1, y: state.player.y - 1 }) && |
||||
isFree({ x: state.player.x, y: state.player.y - 1 }) && |
||||
isFree({ x: state.player.x + 1, y: state.player.y }) |
||||
) { |
||||
let xad = state.player.x + 1 |
||||
let yad = state.player.y - 1 |
||||
// choose will save the biggest path to be chosen
|
||||
// [ down, line1, line0, left ]
|
||||
let choose = [ |
||||
calDistance(xad, yad + 1, 2, 0), |
||||
calDistance(state.player.x + 1, state.player.y, car, 0), |
||||
calDistance(state.player.x, state.player.y - 1, car, 0), |
||||
calDistance(xad - 1, yad, 3, 0), |
||||
] |
||||
let index = choose.indexOf(Math.max(...choose)) |
||||
return index === 0 || index === 1 |
||||
? state.player.coords[1] |
||||
: state.player.coords[0] |
||||
} |
||||
if ( |
||||
(car === 2 || car === 1) && |
||||
!isFree({ x: state.player.x + 1, y: state.player.y + 1 }) && |
||||
isFree({ x: state.player.x, y: state.player.y + 1 }) && |
||||
isFree({ x: state.player.x + 1, y: state.player.y }) |
||||
) { |
||||
let xad = state.player.x + 1 |
||||
let yad = state.player.y + 1 |
||||
// choose will save the biggest path to be chosen
|
||||
// [ left, line2, line1, up ]
|
||||
let choose = [ |
||||
calDistance(xad - 1, yad, 3, 0), |
||||
calDistance(state.player.x, state.player.y + 1, car, 0), |
||||
calDistance(state.player.x + 1, state.player.y, car, 0), |
||||
calDistance(xad, yad - 1, 0, 0), |
||||
] |
||||
let index = choose.indexOf(Math.max(...choose)) |
||||
return index === 0 || index === 1 |
||||
? state.player.coords[2] |
||||
: state.player.coords[1] |
||||
} |
||||
if ( |
||||
(car === 2 || car === 3) && |
||||
!isFree({ x: state.player.x - 1, y: state.player.y + 1 }) && |
||||
isFree({ x: state.player.x, y: state.player.y + 1 }) && |
||||
isFree({ x: state.player.x - 1, y: state.player.y }) |
||||
) { |
||||
let xad = state.player.x - 1 |
||||
let yad = state.player.y + 1 |
||||
// choose will save the biggest path to be chosen
|
||||
// [ right, line2, line3, up ]
|
||||
let choose = [ |
||||
calDistance(xad + 1, yad, 1, 0), |
||||
calDistance(state.player.x - 1, state.player.y, car, 0), |
||||
calDistance(state.player.x, state.player.y + 1, car, 0), |
||||
calDistance(xad, yad - 1, 0, 0), |
||||
] |
||||
let index = choose.indexOf(Math.max(...choose)) |
||||
return index === 0 || index === 1 |
||||
? state.player.coords[3] |
||||
: state.player.coords[2] |
||||
} |
||||
|
||||
for ({ x, y, cardinal } of state.player.coords) { |
||||
// if everything is ok it must continue with the best path
|
||||
arr.push(calDistance(x, y, cardinal, 0)) |
||||
} |
||||
return state.player.coords[arr.indexOf(Math.max(...arr))] |
||||
} |
||||
|
||||
// recursion
|
||||
const calDistance = (x, y, car, count) => { |
||||
if (car <= 0) { |
||||
if ( |
||||
isFree({ x, y }) && |
||||
isAlley({ x: x + 1, y }) && |
||||
isAlley({ x, y: y - 1 }) && |
||||
isAlley({ x: x - 1, y }) |
||||
) |
||||
return -1 |
||||
return !isFree({ x, y }) || !inBounds(y - 1) |
||||
? count |
||||
: calDistance(x, y - 1, car, count + 1) |
||||
} |
||||
if (car === 1) { |
||||
if ( |
||||
isFree({ x, y }) && |
||||
isAlley({ x, y: y + 1 }) && |
||||
isAlley({ x, y: y - 1 }) && |
||||
isAlley({ x: x + 1, y }) |
||||
) |
||||
return -1 |
||||
return !isFree({ x, y }) || !inBounds(x + 1) |
||||
? count |
||||
: calDistance(x + 1, y, car, count + 1) |
||||
} |
||||
if (car === 2) { |
||||
if ( |
||||
isFree({ x, y }) && |
||||
isAlley({ x: x - 1, y }) && |
||||
isAlley({ x, y: y + 1 }) && |
||||
isAlley({ x: x + 1, y }) |
||||
) |
||||
return -1 |
||||
return !isFree({ x, y }) || !inBounds(y + 1) |
||||
? count |
||||
: calDistance(x, y + 1, car, count + 1) |
||||
} |
||||
if (car === 3) { |
||||
if ( |
||||
isFree({ x, y }) && |
||||
isAlley({ x, y: y - 1 }) && |
||||
isAlley({ x: x - 1, y }) && |
||||
isAlley({ x, y: y + 1 }) |
||||
) |
||||
return -1 |
||||
return !isFree({ x, y }) || !inBounds(x - 1) |
||||
? count |
||||
: calDistance(x - 1, y, car, count + 1) |
||||
} |
||||
} |
||||
|
||||
const addToMap = ({ x, y }) => MAP[y * SIZE + x] = 1 |
||||
const update = (state) => { |
||||
state.players.forEach(addToMap) |
||||
findBestPath(state) |
||||
} |
||||
/******************************* |
||||
* functions given to the students |
||||
********************************/ |
||||
const SIZE = 100 |
||||
const MAP = new Int8Array(SIZE * SIZE) |
||||
const isFree = ({ x, y }) => MAP[y * SIZE + x] === 0 |
||||
const isOccupied = ({ x, y }) => MAP[y * SIZE + x] === 1 |
||||
const inBounds = (n) => n < SIZE && n >= 0 |
||||
const isInBounds = ({ x, y }) => inBounds(x) && inBounds(y) |
||||
const pickRandom = (arr) => arr[Math.floor(Math.random() * arr.length)] |
||||
|
||||
/*********** |
||||
* My functions |
||||
************/ |
||||
const isAlley = ({ x, y }) => !isFree({ x, y }) || !isInBounds({ x, y }) |
||||
|
||||
// this functions will find the best path, so the path that has more empty spaces
|
||||
// so use `isFree`,
|
||||
const findBestPath = (state) => { |
||||
let arr = [] |
||||
let car = state.player.cardinal |
||||
// if it as a block on the symmetric position it must
|
||||
// simulate the symmetric position and see witch path is the best
|
||||
if ( |
||||
(car === 3 || car === 0) && |
||||
!isFree({ x: state.player.x - 1, y: state.player.y - 1 }) && |
||||
isFree({ x: state.player.x, y: state.player.y - 1 }) && |
||||
isFree({ x: state.player.x - 1, y: state.player.y }) |
||||
) { |
||||
let xad = state.player.x - 1 |
||||
let yad = state.player.y - 1 |
||||
|
||||
let choose = [ |
||||
calDistance(xad + 1, yad, 1, 0), |
||||
calDistance(state.player.x, state.player.y - 1, car, 0), |
||||
calDistance(state.player.x - 1, state.player.y, car, 0), |
||||
calDistance(xad, yad + 1, 2, 0), |
||||
] |
||||
let index = choose.indexOf(Math.max(...choose)) |
||||
return index === 0 || index === 1 |
||||
? state.player.coords[0] |
||||
: state.player.coords[3] |
||||
} |
||||
if ( |
||||
(car === 1 || car === 0) && |
||||
!isFree({ x: state.player.x + 1, y: state.player.y - 1 }) && |
||||
isFree({ x: state.player.x, y: state.player.y - 1 }) && |
||||
isFree({ x: state.player.x + 1, y: state.player.y }) |
||||
) { |
||||
let xad = state.player.x + 1 |
||||
let yad = state.player.y - 1 |
||||
// choose will save the biggest path to be chosen
|
||||
// [ down, line1, line0, left ]
|
||||
let choose = [ |
||||
calDistance(xad, yad + 1, 2, 0), |
||||
calDistance(state.player.x + 1, state.player.y, car, 0), |
||||
calDistance(state.player.x, state.player.y - 1, car, 0), |
||||
calDistance(xad - 1, yad, 3, 0), |
||||
] |
||||
let index = choose.indexOf(Math.max(...choose)) |
||||
return index === 0 || index === 1 |
||||
? state.player.coords[1] |
||||
: state.player.coords[0] |
||||
} |
||||
if ( |
||||
(car === 2 || car === 1) && |
||||
!isFree({ x: state.player.x + 1, y: state.player.y + 1 }) && |
||||
isFree({ x: state.player.x, y: state.player.y + 1 }) && |
||||
isFree({ x: state.player.x + 1, y: state.player.y }) |
||||
) { |
||||
let xad = state.player.x + 1 |
||||
let yad = state.player.y + 1 |
||||
// choose will save the biggest path to be chosen
|
||||
// [ left, line2, line1, up ]
|
||||
let choose = [ |
||||
calDistance(xad - 1, yad, 3, 0), |
||||
calDistance(state.player.x, state.player.y + 1, car, 0), |
||||
calDistance(state.player.x + 1, state.player.y, car, 0), |
||||
calDistance(xad, yad - 1, 0, 0), |
||||
] |
||||
let index = choose.indexOf(Math.max(...choose)) |
||||
return index === 0 || index === 1 |
||||
? state.player.coords[2] |
||||
: state.player.coords[1] |
||||
} |
||||
if ( |
||||
(car === 2 || car === 3) && |
||||
!isFree({ x: state.player.x - 1, y: state.player.y + 1 }) && |
||||
isFree({ x: state.player.x, y: state.player.y + 1 }) && |
||||
isFree({ x: state.player.x - 1, y: state.player.y }) |
||||
) { |
||||
let xad = state.player.x - 1 |
||||
let yad = state.player.y + 1 |
||||
// choose will save the biggest path to be chosen
|
||||
// [ right, line2, line3, up ]
|
||||
let choose = [ |
||||
calDistance(xad + 1, yad, 1, 0), |
||||
calDistance(state.player.x - 1, state.player.y, car, 0), |
||||
calDistance(state.player.x, state.player.y + 1, car, 0), |
||||
calDistance(xad, yad - 1, 0, 0), |
||||
] |
||||
let index = choose.indexOf(Math.max(...choose)) |
||||
return index === 0 || index === 1 |
||||
? state.player.coords[3] |
||||
: state.player.coords[2] |
||||
} |
||||
|
||||
for ({ x, y, cardinal } of state.player.coords) { |
||||
// if everything is ok it must continue with the best path
|
||||
arr.push(calDistance(x, y, cardinal, 0)) |
||||
} |
||||
return state.player.coords[arr.indexOf(Math.max(...arr))] |
||||
} |
||||
|
||||
// recursion
|
||||
const calDistance = (x, y, car, count) => { |
||||
if (car <= 0) { |
||||
if ( |
||||
isFree({ x, y }) && |
||||
isAlley({ x: x + 1, y }) && |
||||
isAlley({ x, y: y - 1 }) && |
||||
isAlley({ x: x - 1, y }) |
||||
) |
||||
return -1 |
||||
return !isFree({ x, y }) || !inBounds(y - 1) |
||||
? count |
||||
: calDistance(x, y - 1, car, count + 1) |
||||
} |
||||
if (car === 1) { |
||||
if ( |
||||
isFree({ x, y }) && |
||||
isAlley({ x, y: y + 1 }) && |
||||
isAlley({ x, y: y - 1 }) && |
||||
isAlley({ x: x + 1, y }) |
||||
) |
||||
return -1 |
||||
return !isFree({ x, y }) || !inBounds(x + 1) |
||||
? count |
||||
: calDistance(x + 1, y, car, count + 1) |
||||
} |
||||
if (car === 2) { |
||||
if ( |
||||
isFree({ x, y }) && |
||||
isAlley({ x: x - 1, y }) && |
||||
isAlley({ x, y: y + 1 }) && |
||||
isAlley({ x: x + 1, y }) |
||||
) |
||||
return -1 |
||||
return !isFree({ x, y }) || !inBounds(y + 1) |
||||
? count |
||||
: calDistance(x, y + 1, car, count + 1) |
||||
} |
||||
if (car === 3) { |
||||
if ( |
||||
isFree({ x, y }) && |
||||
isAlley({ x, y: y - 1 }) && |
||||
isAlley({ x: x - 1, y }) && |
||||
isAlley({ x, y: y + 1 }) |
||||
) |
||||
return -1 |
||||
return !isFree({ x, y }) || !inBounds(x - 1) |
||||
? count |
||||
: calDistance(x - 1, y, car, count + 1) |
||||
} |
||||
} |
||||
|
||||
const addToMap = ({ x, y }) => MAP[y * SIZE + x] = 1 |
||||
const update = (state) => { |
||||
state.players.forEach(addToMap) |
||||
findBestPath(state) |
||||
} |
||||
|
@ -1,97 +1,97 @@
|
||||
const SIZE = 100 |
||||
const MAP = new Int8Array(SIZE * SIZE) // State of the Map
|
||||
const isFree = ({ x, y }) => MAP[y * SIZE + x] === 0 // 0 = block free
|
||||
const isOccupied = ({ x, y }) => MAP[y * SIZE + x] === 1 // 1 = block occupied
|
||||
const inBounds = n => n < SIZE && n >= 0 |
||||
const isInBounds = ({ x, y }) => inBounds(x) && inBounds(y) |
||||
const pickRandom = arr => arr[Math.floor(Math.random() * arr.length)] |
||||
|
||||
// ok check if we can move on this block
|
||||
const ok = (x = -1, y = -1) => { |
||||
const coords = typeof x !== 'number' ? x : { x, y } |
||||
return isFree(coords) && isInBounds(coords) |
||||
} |
||||
|
||||
const isAlley = (card, x, y) => { |
||||
switch (card) { |
||||
case 0: |
||||
if (ok(x, y - 1) && !ok(x + 1, y - 1) && !ok(x - 1, y - 1)) { |
||||
while (ok(x, y - 1) && !hasLateralWalls(0, x, y)) { |
||||
y-- |
||||
if (hasLateralWalls(0, x, y)) return true |
||||
} |
||||
} |
||||
return false |
||||
case 1: |
||||
if (ok(x + 1, y) && !ok(x + 1, y + 1) && !ok(x + 1, y - 1)) { |
||||
while (ok(x + 1, y) && !hasLateralWalls(1, x, y)) { |
||||
x++ |
||||
if (hasLateralWalls(1, x, y)) return true |
||||
} |
||||
} |
||||
return false |
||||
case 2: |
||||
if (ok(x, y + 1) && !ok(x + 1, y + 1) && !ok(x - 1, y + 1)) { |
||||
while (ok(x, y + 1) && !hasLateralWalls(2, x, y)) { |
||||
y++ |
||||
if (hasLateralWalls(2, x, y)) return true |
||||
} |
||||
} |
||||
return false |
||||
case 3: |
||||
if (ok(x - 1, y) && !ok(x - 1, y + 1) && !ok(x - 1, y - 1)) { |
||||
while (ok(x - 1, y) && !hasLateralWalls(3, x, y)) { |
||||
x-- |
||||
if (hasLateralWalls(3, x, y)) return true |
||||
} |
||||
} |
||||
return false |
||||
} |
||||
} |
||||
|
||||
const hasLateralWalls = (card, x, y) => { |
||||
switch (card) { |
||||
case 0: return !(ok(x + 1, y) || ok(x - 1, y) || ok(x, y - 1)) |
||||
case 1: return !(ok(x, y - 1) || ok(x, y + 1) || ok(x + 1, y)) |
||||
case 2: return !(ok(x + 1, y) || ok(x - 1, y) || ok(x, y + 1)) |
||||
case 3: return !(ok(x, y - 1) || ok(x, y + 1) || ok(x - 1, y)) |
||||
} |
||||
} |
||||
|
||||
const goDirection = (state, card) => |
||||
ok(state.coords[card]) && |
||||
!isAlley(card, state.x, state.y) && |
||||
state.coords[card] |
||||
|
||||
const findEnemy = state => |
||||
state.players.filter(p => state.player.name !== p.name)[0] |
||||
|
||||
const seekEnemy = state => { |
||||
if (state.players.length === 1) return |
||||
const enemy = findEnemy(state) |
||||
const xPla = state.player.x |
||||
const yPla = state.player.y |
||||
const xOpo = enemy.x |
||||
const yOpo = enemy.y |
||||
const xDif = xPla - xOpo |
||||
const yDif = yPla - yOpo |
||||
|
||||
return ( |
||||
(Math.abs(xDif) > Math.abs(yDif) && |
||||
goDirection(state.player, xPla < xOpo ? 1 : 3)) || |
||||
goDirection(yPla < yOpo ? 2 : 0) |
||||
) |
||||
} |
||||
|
||||
const walk = state => |
||||
seekEnemy(state) || |
||||
goDirection(state.player, 0) || |
||||
goDirection(state.player, 1) || |
||||
goDirection(state.player, 2) || |
||||
goDirection(state.player, 3) |
||||
|
||||
const addToMap = ({ x, y }) => (MAP[y * SIZE + x] = 1) |
||||
const update = state => { |
||||
state.players.forEach(addToMap) |
||||
return walk(state) |
||||
} |
||||
const SIZE = 100 |
||||
const MAP = new Int8Array(SIZE * SIZE) // State of the Map
|
||||
const isFree = ({ x, y }) => MAP[y * SIZE + x] === 0 // 0 = block free
|
||||
const isOccupied = ({ x, y }) => MAP[y * SIZE + x] === 1 // 1 = block occupied
|
||||
const inBounds = n => n < SIZE && n >= 0 |
||||
const isInBounds = ({ x, y }) => inBounds(x) && inBounds(y) |
||||
const pickRandom = arr => arr[Math.floor(Math.random() * arr.length)] |
||||
|
||||
// ok check if we can move on this block
|
||||
const ok = (x = -1, y = -1) => { |
||||
const coords = typeof x !== 'number' ? x : { x, y } |
||||
return isFree(coords) && isInBounds(coords) |
||||
} |
||||
|
||||
const isAlley = (card, x, y) => { |
||||
switch (card) { |
||||
case 0: |
||||
if (ok(x, y - 1) && !ok(x + 1, y - 1) && !ok(x - 1, y - 1)) { |
||||
while (ok(x, y - 1) && !hasLateralWalls(0, x, y)) { |
||||
y-- |
||||
if (hasLateralWalls(0, x, y)) return true |
||||
} |
||||
} |
||||
return false |
||||
case 1: |
||||
if (ok(x + 1, y) && !ok(x + 1, y + 1) && !ok(x + 1, y - 1)) { |
||||
while (ok(x + 1, y) && !hasLateralWalls(1, x, y)) { |
||||
x++ |
||||
if (hasLateralWalls(1, x, y)) return true |
||||
} |
||||
} |
||||
return false |
||||
case 2: |
||||
if (ok(x, y + 1) && !ok(x + 1, y + 1) && !ok(x - 1, y + 1)) { |
||||
while (ok(x, y + 1) && !hasLateralWalls(2, x, y)) { |
||||
y++ |
||||
if (hasLateralWalls(2, x, y)) return true |
||||
} |
||||
} |
||||
return false |
||||
case 3: |
||||
if (ok(x - 1, y) && !ok(x - 1, y + 1) && !ok(x - 1, y - 1)) { |
||||
while (ok(x - 1, y) && !hasLateralWalls(3, x, y)) { |
||||
x-- |
||||
if (hasLateralWalls(3, x, y)) return true |
||||
} |
||||
} |
||||
return false |
||||
} |
||||
} |
||||
|
||||
const hasLateralWalls = (card, x, y) => { |
||||
switch (card) { |
||||
case 0: return !(ok(x + 1, y) || ok(x - 1, y) || ok(x, y - 1)) |
||||
case 1: return !(ok(x, y - 1) || ok(x, y + 1) || ok(x + 1, y)) |
||||
case 2: return !(ok(x + 1, y) || ok(x - 1, y) || ok(x, y + 1)) |
||||
case 3: return !(ok(x, y - 1) || ok(x, y + 1) || ok(x - 1, y)) |
||||
} |
||||
} |
||||
|
||||
const goDirection = (state, card) => |
||||
ok(state.coords[card]) && |
||||
!isAlley(card, state.x, state.y) && |
||||
state.coords[card] |
||||
|
||||
const findEnemy = state => |
||||
state.players.filter(p => state.player.name !== p.name)[0] |
||||
|
||||
const seekEnemy = state => { |
||||
if (state.players.length === 1) return |
||||
const enemy = findEnemy(state) |
||||
const xPla = state.player.x |
||||
const yPla = state.player.y |
||||
const xOpo = enemy.x |
||||
const yOpo = enemy.y |
||||
const xDif = xPla - xOpo |
||||
const yDif = yPla - yOpo |
||||
|
||||
return ( |
||||
(Math.abs(xDif) > Math.abs(yDif) && |
||||
goDirection(state.player, xPla < xOpo ? 1 : 3)) || |
||||
goDirection(yPla < yOpo ? 2 : 0) |
||||
) |
||||
} |
||||
|
||||
const walk = state => |
||||
seekEnemy(state) || |
||||
goDirection(state.player, 0) || |
||||
goDirection(state.player, 1) || |
||||
goDirection(state.player, 2) || |
||||
goDirection(state.player, 3) |
||||
|
||||
const addToMap = ({ x, y }) => (MAP[y * SIZE + x] = 1) |
||||
const update = state => { |
||||
state.players.forEach(addToMap) |
||||
return walk(state) |
||||
} |
||||
|
@ -1,57 +1,57 @@
|
||||
const SIZE = 100 |
||||
const MAP = new Int8Array(SIZE * SIZE) // State of the Map
|
||||
const isFree = ({ x, y }) => MAP[y * SIZE + x] === 0 // 0 = block free
|
||||
const isOccupied = ({ x, y }) => MAP[y * SIZE + x] === 1 // 1 = block occupied
|
||||
|
||||
// `inBounds` check if our coord (n) is an existing index in our MAP
|
||||
const inBounds = n => n < SIZE && n >= 0 |
||||
|
||||
// `isInBounds` check that properties x and y of our argument are both in bounds
|
||||
const isInBounds = ({ x, y }) => inBounds(x) && inBounds(y) |
||||
|
||||
// `pickRandom` get a random element from an array
|
||||
const pickRandom = arr => arr[Math.floor(Math.random() * arr.length)] |
||||
|
||||
// `addToMap` save the new positions into the map
|
||||
const addToMap = ({ x, y }) => MAP[y * SIZE + x] = 1 |
||||
|
||||
// `update` this function is called at each turn
|
||||
const update = state => { |
||||
// update is called with a state argument that has 2 properties:
|
||||
// players: an array of all the players
|
||||
// player: the player for this AI
|
||||
|
||||
// Each players contains:
|
||||
// color: A number that represent the color of a player
|
||||
// name: A string of the player name
|
||||
// score: A number of the total block collected by this player
|
||||
// x: The horizontal position of the player
|
||||
// y: The vertical position of the player
|
||||
// coords: An array of 4 coordinates of the nearest blocks
|
||||
// [ NORTH, EAST, SOUTH, WEST ]
|
||||
// N
|
||||
// W + E
|
||||
// S
|
||||
|
||||
// Each coordinate contains:
|
||||
// x: The horizontal position
|
||||
// y: The vertical position
|
||||
// cardinal: A number between 0 and 3 that represent the cardinal
|
||||
// [ 0: NORTH, 1: EAST, 2: SOUTH, 3: WEST ]
|
||||
// direction: A number between 0 and 3 that represent the direction
|
||||
// [ 0: FORWARD, 1: RIGHT, 2: BACKWARD, 3: LEFT ]
|
||||
|
||||
// Saving state between each updates:
|
||||
// I update the MAP with the new position of each players
|
||||
state.players.forEach(addToMap) |
||||
|
||||
// Actual AI logic:
|
||||
// I filter my array of coords to keep only those that are in bounds
|
||||
const coordsInBound = state.player.coords.filter(isInBounds) |
||||
|
||||
// I filter again to keep coords that are free
|
||||
const available = coordsInBound.filter(isFree) |
||||
|
||||
// And I return a random available coord
|
||||
return pickRandom(available) |
||||
} |
||||
const SIZE = 100 |
||||
const MAP = new Int8Array(SIZE * SIZE) // State of the Map
|
||||
const isFree = ({ x, y }) => MAP[y * SIZE + x] === 0 // 0 = block free
|
||||
const isOccupied = ({ x, y }) => MAP[y * SIZE + x] === 1 // 1 = block occupied
|
||||
|
||||
// `inBounds` check if our coord (n) is an existing index in our MAP
|
||||
const inBounds = n => n < SIZE && n >= 0 |
||||
|
||||
// `isInBounds` check that properties x and y of our argument are both in bounds
|
||||
const isInBounds = ({ x, y }) => inBounds(x) && inBounds(y) |
||||
|
||||
// `pickRandom` get a random element from an array
|
||||
const pickRandom = arr => arr[Math.floor(Math.random() * arr.length)] |
||||
|
||||
// `addToMap` save the new positions into the map
|
||||
const addToMap = ({ x, y }) => MAP[y * SIZE + x] = 1 |
||||
|
||||
// `update` this function is called at each turn
|
||||
const update = state => { |
||||
// update is called with a state argument that has 2 properties:
|
||||
// players: an array of all the players
|
||||
// player: the player for this AI
|
||||
|
||||
// Each players contains:
|
||||
// color: A number that represent the color of a player
|
||||
// name: A string of the player name
|
||||
// score: A number of the total block collected by this player
|
||||
// x: The horizontal position of the player
|
||||
// y: The vertical position of the player
|
||||
// coords: An array of 4 coordinates of the nearest blocks
|
||||
// [ NORTH, EAST, SOUTH, WEST ]
|
||||
// N
|
||||
// W + E
|
||||
// S
|
||||
|
||||
// Each coordinate contains:
|
||||
// x: The horizontal position
|
||||
// y: The vertical position
|
||||
// cardinal: A number between 0 and 3 that represent the cardinal
|
||||
// [ 0: NORTH, 1: EAST, 2: SOUTH, 3: WEST ]
|
||||
// direction: A number between 0 and 3 that represent the direction
|
||||
// [ 0: FORWARD, 1: RIGHT, 2: BACKWARD, 3: LEFT ]
|
||||
|
||||
// Saving state between each updates:
|
||||
// I update the MAP with the new position of each players
|
||||
state.players.forEach(addToMap) |
||||
|
||||
// Actual AI logic:
|
||||
// I filter my array of coords to keep only those that are in bounds
|
||||
const coordsInBound = state.player.coords.filter(isInBounds) |
||||
|
||||
// I filter again to keep coords that are free
|
||||
const available = coordsInBound.filter(isFree) |
||||
|
||||
// And I return a random available coord
|
||||
return pickRandom(available) |
||||
} |
||||
|
@ -1,48 +1,48 @@
|
||||
const SIZE = 100 |
||||
const [FREE, UNSAFE, FILLED] = Array(3).keys() |
||||
const MAP = new Int8Array(SIZE * SIZE) |
||||
const isFree = ({ x, y }) => MAP[y * SIZE + x] < FILLED |
||||
const isUnsafe = ({ x, y }) => MAP[y * SIZE + x] === UNSAFE |
||||
const setUnsafe = ({ x, y }) => MAP[y * SIZE + x] = UNSAFE |
||||
const setFilled = ({ x, y }) => MAP[y * SIZE + x] = FILLED |
||||
const inBounds = n => n < SIZE && n >= 0 |
||||
const isInBounds = ({ x, y }) => inBounds(x) && inBounds(y) |
||||
const isForward = el => el.direction === 0 |
||||
const isRight = el => el.direction === 1 |
||||
const isLeft = el => el.direction === 3 |
||||
const goForward = arr => arr.find(isForward) |
||||
const goRight = arr => arr.find(isRight) |
||||
const goLeft = arr => arr.find(isLeft) |
||||
const isOtherPlayer = player => !player.isOwnPlayer |
||||
|
||||
let wallReached = false |
||||
const pickForwardOrRightOrLeft = arr => { |
||||
if (arr.length === 3 && !wallReached) { |
||||
return goForward(arr) || goRight(arr) || goLeft(arr) |
||||
} |
||||
|
||||
if (arr.length <= 2 && !wallReached) { |
||||
wallReached = true |
||||
return goRight(arr) || goLeft(arr) || goForward(arr) |
||||
} |
||||
|
||||
return goLeft(arr) || goForward(arr) || goRight(arr) |
||||
} |
||||
|
||||
const update = ({ player, players }) => { |
||||
players.forEach(setFilled) |
||||
players |
||||
.filter(isOtherPlayer) |
||||
.flatMap(({ coords }) => coords) |
||||
.filter(isInBounds) |
||||
.filter(isFree) |
||||
.forEach(setUnsafe) |
||||
|
||||
const coordsInBound = player.coords |
||||
.filter(isInBounds) |
||||
.filter(isNotBackward) |
||||
|
||||
const available = pickForwardOrRightOrLeft(coordsInBound.filter(isFree)) |
||||
const lastResort = pickForwardOrRightOrLeft(coordsInBound.filter(isUnsafe)) |
||||
return available || lastResort |
||||
} |
||||
const SIZE = 100 |
||||
const [FREE, UNSAFE, FILLED] = Array(3).keys() |
||||
const MAP = new Int8Array(SIZE * SIZE) |
||||
const isFree = ({ x, y }) => MAP[y * SIZE + x] < FILLED |
||||
const isUnsafe = ({ x, y }) => MAP[y * SIZE + x] === UNSAFE |
||||
const setUnsafe = ({ x, y }) => MAP[y * SIZE + x] = UNSAFE |
||||
const setFilled = ({ x, y }) => MAP[y * SIZE + x] = FILLED |
||||
const inBounds = n => n < SIZE && n >= 0 |
||||
const isInBounds = ({ x, y }) => inBounds(x) && inBounds(y) |
||||
const isForward = el => el.direction === 0 |
||||
const isRight = el => el.direction === 1 |
||||
const isLeft = el => el.direction === 3 |
||||
const goForward = arr => arr.find(isForward) |
||||
const goRight = arr => arr.find(isRight) |
||||
const goLeft = arr => arr.find(isLeft) |
||||
const isOtherPlayer = player => !player.isOwnPlayer |
||||
|
||||
let wallReached = false |
||||
const pickForwardOrRightOrLeft = arr => { |
||||
if (arr.length === 3 && !wallReached) { |
||||
return goForward(arr) || goRight(arr) || goLeft(arr) |
||||
} |
||||
|
||||
if (arr.length <= 2 && !wallReached) { |
||||
wallReached = true |
||||
return goRight(arr) || goLeft(arr) || goForward(arr) |
||||
} |
||||
|
||||
return goLeft(arr) || goForward(arr) || goRight(arr) |
||||
} |
||||
|
||||
const update = ({ player, players }) => { |
||||
players.forEach(setFilled) |
||||
players |
||||
.filter(isOtherPlayer) |
||||
.flatMap(({ coords }) => coords) |
||||
.filter(isInBounds) |
||||
.filter(isFree) |
||||
.forEach(setUnsafe) |
||||
|
||||
const coordsInBound = player.coords |
||||
.filter(isInBounds) |
||||
.filter(isNotBackward) |
||||
|
||||
const available = pickForwardOrRightOrLeft(coordsInBound.filter(isFree)) |
||||
const lastResort = pickForwardOrRightOrLeft(coordsInBound.filter(isUnsafe)) |
||||
return available || lastResort |
||||
} |
||||
|
@ -1,40 +1,40 @@
|
||||
const SIZE = 100 |
||||
const FREE = 0 |
||||
const UNSAFE = -1 |
||||
const FILLED = 1 |
||||
const MAP = new Int8Array(SIZE * SIZE) |
||||
const isFree = ({ x, y }) => MAP[y * SIZE + x] < FILLED |
||||
const isUnsafe = ({ x, y }) => MAP[y * SIZE + x] === UNSAFE |
||||
const setUnsafe = ({ x, y }) => MAP[y * SIZE + x] = UNSAFE |
||||
const setFilled = ({ x, y }) => MAP[y * SIZE + x] = FILLED |
||||
const inBounds = n => n < SIZE && n >= 0 |
||||
const isInBounds = ({ x, y }) => inBounds(x) && inBounds(y) |
||||
const isNotBackward = el => el.direction !== 2 |
||||
const isForward = el => el.direction === 0 |
||||
const isRight = el => el.direction === 1 |
||||
const isLeft = el => el.direction === 3 |
||||
const goForward = arr => arr.find(isForward) |
||||
const goRight = arr => arr.find(isRight) |
||||
const goLeft = arr => arr.find(isLeft) |
||||
const isOtherPlayer = player => !player.isOwnPlayer |
||||
|
||||
// `snailIt` goes with the form of a snail
|
||||
const snailIt = arr => goRight(arr) || goForward(arr) || goLeft(arr) |
||||
|
||||
const update = ({ player, players }) => { |
||||
players.forEach(setFilled) |
||||
players |
||||
.filter(isOtherPlayer) |
||||
.flatMap(({ coords }) => coords) |
||||
.filter(isInBounds) |
||||
.filter(isFree) |
||||
.forEach(setUnsafe) |
||||
|
||||
const coordsInBound = player.coords |
||||
.filter(isInBounds) |
||||
.filter(isNotBackward) |
||||
|
||||
const available = snailIt(coordsInBound.filter(isFree)) |
||||
const lastResort = snailIt(coordsInBound.filter(isUnsafe)) |
||||
return available || lastResort |
||||
} |
||||
const SIZE = 100 |
||||
const FREE = 0 |
||||
const UNSAFE = -1 |
||||
const FILLED = 1 |
||||
const MAP = new Int8Array(SIZE * SIZE) |
||||
const isFree = ({ x, y }) => MAP[y * SIZE + x] < FILLED |
||||
const isUnsafe = ({ x, y }) => MAP[y * SIZE + x] === UNSAFE |
||||
const setUnsafe = ({ x, y }) => MAP[y * SIZE + x] = UNSAFE |
||||
const setFilled = ({ x, y }) => MAP[y * SIZE + x] = FILLED |
||||
const inBounds = n => n < SIZE && n >= 0 |
||||
const isInBounds = ({ x, y }) => inBounds(x) && inBounds(y) |
||||
const isNotBackward = el => el.direction !== 2 |
||||
const isForward = el => el.direction === 0 |
||||
const isRight = el => el.direction === 1 |
||||
const isLeft = el => el.direction === 3 |
||||
const goForward = arr => arr.find(isForward) |
||||
const goRight = arr => arr.find(isRight) |
||||
const goLeft = arr => arr.find(isLeft) |
||||
const isOtherPlayer = player => !player.isOwnPlayer |
||||
|
||||
// `snailIt` goes with the form of a snail
|
||||
const snailIt = arr => goRight(arr) || goForward(arr) || goLeft(arr) |
||||
|
||||
const update = ({ player, players }) => { |
||||
players.forEach(setFilled) |
||||
players |
||||
.filter(isOtherPlayer) |
||||
.flatMap(({ coords }) => coords) |
||||
.filter(isInBounds) |
||||
.filter(isFree) |
||||
.forEach(setUnsafe) |
||||
|
||||
const coordsInBound = player.coords |
||||
.filter(isInBounds) |
||||
.filter(isNotBackward) |
||||
|
||||
const available = snailIt(coordsInBound.filter(isFree)) |
||||
const lastResort = snailIt(coordsInBound.filter(isUnsafe)) |
||||
return available || lastResort |
||||
} |
||||
|
@ -1,384 +1,384 @@
|
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
<title>Tron</title> |
||||
<meta charset="utf-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
||||
<link rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAx42QAFP/FABXQkMAQC0uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAQBEREREREREAFEREREREQQAURERERERBABREREREREEAFEREREREQQAURERERERBABQERAAEREEAFCBEIiREQQAUQgRERERBABRAJEREREEAFAJEREREQQAUJERERERBABRERERERDEAEREREREREQQAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"> |
||||
<style> |
||||
body { |
||||
background: #13191a; |
||||
display: grid; |
||||
grid-template-columns: 1fr 300px 300px 1fr; |
||||
font-family: monospace; |
||||
color: #7cecff; |
||||
margin: 0; |
||||
padding: 0; |
||||
} |
||||
|
||||
h1 { letter-spacing: 6px } |
||||
h2 { |
||||
margin-bottom: 6px; |
||||
text-overflow: ellipsis; |
||||
max-width: calc(50vw - 300px); |
||||
} |
||||
|
||||
h2:after { |
||||
display: block; |
||||
content: ' '; |
||||
margin-top: 12px; |
||||
height: 3px; |
||||
} |
||||
|
||||
h2 b { |
||||
display: block; |
||||
overflow: hidden; |
||||
text-overflow: ellipsis; |
||||
} |
||||
|
||||
|
||||
#player-0 h2:after { |
||||
background-image: linear-gradient(to left, #0000, currentColor 18px, #0000); |
||||
margin-right: -10px; |
||||
} |
||||
|
||||
#player-1 h2:after { |
||||
margin-left: -10px; |
||||
background-image: linear-gradient(to right, #0000, currentColor 18px, #0000); |
||||
} |
||||
|
||||
canvas { |
||||
display: block; |
||||
width: 600px; |
||||
height: 600px; |
||||
grid-column: 2/4; |
||||
box-shadow: 0 0 20px 1px #7cecff42; |
||||
outline: 1px solid; |
||||
outline-offset: -1px; |
||||
} |
||||
|
||||
button { |
||||
border: 1px solid; |
||||
color: currentColor; |
||||
background: transparent; |
||||
padding: 0 2px; |
||||
border-radius: 3px; |
||||
} |
||||
|
||||
svg { |
||||
margin-top: 36px; |
||||
width: 300px; |
||||
} |
||||
|
||||
#notice { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
padding: 2px 0; |
||||
user-select: none; |
||||
} |
||||
|
||||
.hide { |
||||
visibility: hidden; |
||||
pointer-events: none; |
||||
} |
||||
|
||||
#title { |
||||
display: block; |
||||
margin: 0; |
||||
padding: 0; |
||||
grid-column: 2/4; |
||||
text-align: center; |
||||
color: cyan; |
||||
} |
||||
|
||||
#player-0, #player-1 { |
||||
margin: 12px; |
||||
padding: 6px; |
||||
user-select: none; |
||||
} |
||||
|
||||
#player-0 { |
||||
grid-column: 1; |
||||
text-align: right; |
||||
} |
||||
|
||||
#player-1 { grid-column: 4 } |
||||
|
||||
|
||||
#controls { |
||||
grid-column: 2/4; |
||||
height: 10px; |
||||
background: #7cecff20; |
||||
margin-top: 1px; |
||||
} |
||||
|
||||
#bar { |
||||
overflow: hidden; |
||||
position: relative; |
||||
box-shadow: 0 0 20px 1px #7cecff30; |
||||
outline: 1px solid; |
||||
outline-offset: -1px; |
||||
} |
||||
|
||||
#loading { background: #7cecff40 } |
||||
#position { background: #7cecff } |
||||
#loading, #position { |
||||
position: absolute; |
||||
transform: translate(-600px); |
||||
pointer-events: none; |
||||
} |
||||
|
||||
#bar, #loading, #position { |
||||
height: 100%; |
||||
width: 100%; |
||||
} |
||||
|
||||
@media (orientation: portrait) { |
||||
h2 { max-width: 300px } |
||||
#controls { grid-row: 3 } |
||||
#player-0 { grid-column: 2; grid-row: 4 } |
||||
#player-1 { grid-column: 3; grid-row: 4 } |
||||
} |
||||
|
||||
</style> |
||||
</head> |
||||
<body> |
||||
<div id="title"> |
||||
<svg fill="cyan" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 449.9 184.1"> |
||||
<defs><filter id="g"><feDropShadow dx="0" dy="0" stdDeviation="3" flood-color="cyan"/></filter></defs> |
||||
<path style="filter:url(#g)" d="M36 62.6H5.2v-19H36v19zM38.4 39H3l-3 2.8v23l3 2.5h35.4l3.1-2.7V41.7L38.4 39z"/><g clip-path="url(#clipPath4354)" transform="matrix(1.25 0 0 -1.25 -135 647)"><path style="filter:url(#g)" d="M333.3 426.4a18.9 18.9 0 00-19.1 18.5c0 10.3 8.6 18.6 19.1 18.6a18.9 18.9 0 0019.2-19.2c-.4-10-8.8-18-19.2-18m0 41.2c-12.8 0-23.2-10.1-23.2-22.6a22.9 22.9 0 0123.2-22.5c12.9 0 23.2 10 23.2 22.5a22.9 22.9 0 01-23.2 22.6"/><path style="filter:url(#g)" d="M333.3 406.6c-21.8 0-39.4 17.2-39.4 38.3s17.6 38.3 39.4 38.3c21.8 0 39.5-17.1 39.5-38.3s-17.7-38.3-39.5-38.3m0 80.6a42.9 42.9 0 01-43.4-42.9c.3-23 19.6-41.6 43.4-41.6a42.9 42.9 0 0143.5 42.2 42.9 42.9 0 01-43.5 42.3M463.7 407H457l-28.4 31.8v9.5H448v34.5h15.6V407zm1.5 79.5H447l-2.7-3.2v-31.5h-17.1c-2.2 0-2.2-1.2-2.2-2.1v-12.2l30.4-34.4h9.8l2.6 2.4v78.1l-2.6 3zM419.2 441.1h-19.6v-33.9h-15.7v75.6h6.9l28.4-32.1V441zm-26.9 45.3h-9.8l-2.6-2.5v-78.1l2.6-2.7h18.2l2.7 3v31.6h17.1c2.2 0 2.2 1.1 2.2 2V452l-30.4 34.5zM283.2 407h-18.8l-31.8 32.5v13.1h52a22.5 22.5 0 00-22-15.5h-9.2l29.8-30m-20 26.2c13.8.3 24.6 9.7 26.2 23h-58l-2.7-2.6V439c0-.5 0-1 .4-1.5l33.4-34.3h26.3v4.1l-26.2 26h.6zM220.2 407h-15.7v45.6h15.7V407zm1.3 49.4h-18.7l-2.5-2.7v-48l2.7-2.6h18.6l2.6 3v47.6l-2.7 2.7z"/><path style="filter:url(#g)" d="M172.5 467.4c-3.7 0-6.8-3.1-6.8-6.8v-53.5l-15.6-.1v54.8a20.7 20.7 0 0021.2 20.8h91.2a23 23 0 0022-15.2h-112zm90 19.1h-91.1a24.6 24.6 0 01-25.6-25v-55.9l3-2.4h18.5l2.4 2.9v54.4a3 3 0 003.2 3h116.4a27 27 0 01-26.7 23"/></g></svg></div> |
||||
<div id="player-0"> |
||||
<h2><b>AI-0</b></h2> |
||||
<b>LOADING</b> |
||||
<br> |
||||
<b>0</b> |
||||
</div> |
||||
<canvas width="1200" height="1200"></canvas> |
||||
<div id="player-1"> |
||||
<h2><b>AI-1</b></h2> |
||||
<b>LOADING</b> |
||||
<br> |
||||
<b>0</b> |
||||
</div> |
||||
<div id="controls"> |
||||
<div id="bar"> |
||||
<div id="loading"></div> |
||||
<div id="position"></div> |
||||
</div> |
||||
<div id="notice" class="hide"> |
||||
> scroll or keys to move step by step, click to jump. (shift = fast) |
||||
<button>hide</button> |
||||
</div> |
||||
</div> |
||||
<script type="module"> |
||||
import { move, update, colorize } from './lib/display.js' |
||||
import { init, injectedCode } from './lib/state.js' |
||||
|
||||
const [canvas] = document.getElementsByTagName('canvas') |
||||
const [hide] = document.getElementsByTagName('button') |
||||
const bar = document.getElementById('bar') |
||||
const loading = document.getElementById('loading') |
||||
const position = document.getElementById('position') |
||||
|
||||
hide.onclick = () => { |
||||
hide.parentElement.classList.add('hide') |
||||
localStorage.hide = 1 |
||||
} |
||||
|
||||
localStorage.hide || hide.parentElement.classList.remove('hide') |
||||
|
||||
const buildInfo = (player, i) => { |
||||
const elem = document.getElementById(`player-${i}`) |
||||
if (!elem) return { score: () => {}, status: () => {} } |
||||
const [name, status, score] = [...elem.children].map(e => e.firstChild).filter(Boolean) |
||||
name.firstChild.data = player.name |
||||
elem.style.color = `hsl(${player.hue*360}, 100%, 70%)` |
||||
elem.style.textShadow = `0 0 6px hsla(${player.hue*360}, 100%, 70%, 0.4)` |
||||
elem.style.width = 'calc(100% - 36px)' // force width force redraw |
||||
// this fix a bug on chrome, not re-applying `currentColor` to gradients |
||||
|
||||
return { |
||||
score: text => player.dead || (score.data = text), |
||||
status: text => status.data = text, |
||||
} |
||||
} |
||||
|
||||
const notInBounds = n => n >= 100 || n < 0 |
||||
|
||||
const getShaUrl = login => |
||||
`https://api.github.com/repos/${login}/tron/commits/master` |
||||
|
||||
const getAIUrl = (login, sha, ai) => |
||||
`https://rawcdn.githack.com/${login}/tron/${sha}/ai/${ai || login}.js` |
||||
|
||||
const getSha = async login => (await (await fetch(getShaUrl(login))).json()).sha |
||||
const toBlob = async r => |
||||
new Blob([`${await r.text()}${injectedCode}`], { type : 'text/javascript' }) |
||||
const toUrlObject = b => URL.createObjectURL(b, { type: 'text/javascript' }) |
||||
const memo = {} |
||||
const fetchBlob = url => memo[url] |
||||
|| (memo[url] = fetch(url).then(toBlob).then(toUrlObject)) |
||||
|
||||
const getGithackUrl = async (login, sha) => { |
||||
if (!sha || sha === 'master') { |
||||
sha = localStorage[login] || (localStorage[login] = await getSha(login)) |
||||
} |
||||
return getAIUrl(login, sha) |
||||
} |
||||
|
||||
const remoteURL = (login, sha) => location.host.startsWith('git.') |
||||
? `${location.origin}/${login}/tron/raw/branch/${sha || 'master'}/ai.js` |
||||
: getGithackUrl(login, sha) |
||||
|
||||
const formatURL = url => { |
||||
if (url.startsWith('/')) return `${location.pathname}/ai/${url}` |
||||
if (url.startsWith('https://')) return url |
||||
if (url.includes(location.hostname)) return `https://${url}` |
||||
return url.includes('@') |
||||
? remoteURL(...url.split('@')) |
||||
: `${location.origin}/${url}` |
||||
} |
||||
|
||||
const start = async ({ urls, seed }) => { |
||||
if (urls.length < 2) throw Error('2 AI urls are required to play') |
||||
const players = init({ players: urls, seed }) |
||||
|
||||
let turn = 1, maxTurn = 1, t = 1, cap = 10000, down |
||||
const done = new Set(players) |
||||
|
||||
const setPosition = e => { |
||||
const v = typeof e === 'number' |
||||
? Math.max(e, 1) |
||||
: Math.floor(Math.max(e.pageX - bar.offsetLeft, 1) / 600 * cap) |
||||
|
||||
t = Math.min(v, maxTurn) |
||||
requested || (requested = requestAnimationFrame(refresh)) |
||||
} |
||||
|
||||
window.onkeydown = e => { |
||||
const step = e.shiftKey ? 10 : 1 |
||||
switch (e.key) { |
||||
case 'ArrowLeft': case 'a': case 'q': case 'l': return setPosition(t - step) |
||||
case 'ArrowRight': case 'e': case 'd': case 'k': return setPosition(t + step) |
||||
} |
||||
} |
||||
|
||||
window.onmousemove = (e) => e.which ? (down && setPosition(e)) : (down = false) |
||||
bar.onmousedown = (e) => (down = true) && setPosition(e) |
||||
bar.onwheel = canvas.onwheel = (e) => |
||||
setPosition(t + Math.sign(e.deltaY) * (e.shiftKey ? 10 : 1)) |
||||
|
||||
let requested, timeout |
||||
const refresh = () => { |
||||
requested = update(t) |
||||
players[0].score(turn) |
||||
players[1].score(turn) |
||||
loading.style.transform = `translate(${((turn / cap)*600)-600}px)` |
||||
position.style.transform = `translate(${((t / cap)*600)-600}px)` |
||||
} |
||||
|
||||
const next = (player) => { |
||||
clearTimeout(player.timeout) |
||||
done.add(player) |
||||
requested || (requested = requestAnimationFrame(refresh)) |
||||
|
||||
// check if all AI are done |
||||
if (done.size >= players.length) { |
||||
turn++ |
||||
const data = `[${players.join(',')}]` |
||||
let allDead = true |
||||
for (const p of players) { |
||||
if (p.dead) continue |
||||
allDead ? (allDead = false) : cap-- |
||||
done.delete(p) |
||||
p.worker.postMessage(data) |
||||
p.timeout = setTimeout(p.kill, 50, 'TIMEOUT') |
||||
} |
||||
allDead && (cap = turn) |
||||
t === maxTurn ? (t = maxTurn = turn) : (maxTurn = turn) |
||||
} |
||||
} |
||||
|
||||
await Promise.all(players.map(async (player, i) => { |
||||
const info = buildInfo(player, i) |
||||
player.score = info.score |
||||
|
||||
const kill = (cause) => { |
||||
if (player.dead) return |
||||
console.log(`${player.name} died because he ${cause} at ${player.x} ${player.y}`) |
||||
player.cause = cause |
||||
player.dead = true |
||||
player.worker && player.worker.terminate() |
||||
info.status(cause) |
||||
} |
||||
|
||||
// init the worker |
||||
try { |
||||
const url = await fetchBlob(await formatURL(player.name)) |
||||
player.worker = new Worker(url, { type: 'module', name: player.name }) |
||||
await new Promise((s, f) => { |
||||
player.worker.onmessage = e => e.data === 'loaded' ? s() : f(Error(e.data)) |
||||
player.worker.onerror = f |
||||
player.worker.postMessage(JSON.stringify({ id: player.name, seed })) |
||||
}) |
||||
|
||||
// activate the AI |
||||
info.status('ACTIVE') |
||||
player.kill = cause => { |
||||
kill(cause) |
||||
next(player) |
||||
} |
||||
} catch (err) { |
||||
console.error(err) |
||||
kill('FAILED-TO-LOAD') |
||||
} |
||||
|
||||
move(player.x, player.y, player.color, turn) |
||||
|
||||
// handle each response from the AI |
||||
player.worker.onmessage = ({ data }) => { |
||||
if (done.has(player)) return player.kill('UNEXPECTED-MESSAGE') |
||||
if (!data) return player.kill('STUCK') |
||||
const { x, y } = JSON.parse(data) |
||||
if (typeof x !== 'number' || typeof y !== 'number') return player.kill('INVALID_INPUT') |
||||
if (notInBounds(x) || notInBounds(y)) return player.kill('OUT_OF_BOUNDS') |
||||
if ( |
||||
!(x === player.x - 1 && y === player.y) && |
||||
!(x === player.x + 1 && y === player.y) && |
||||
!(x === player.x && player.y === y + 1) && |
||||
!(x === player.x && player.y === y - 1) |
||||
) return player.kill('IMPOSSIBLE_MOVE') |
||||
|
||||
player.x = x |
||||
player.y = y |
||||
const failure = move(x, y, player.color, turn) |
||||
if (failure === turn) { |
||||
for (const p of players) { |
||||
p.x === player.x && p.y === player.y && p.kill('MULTI-CRASH') |
||||
} |
||||
colorize(x, y, 0xffffff) |
||||
return player.kill('MULTI-CRASH') |
||||
} else if (failure) return player.kill('CRASH') |
||||
next(player) |
||||
} |
||||
|
||||
player.worker.onerror = () => player.kill('AI-ERROR') |
||||
})) |
||||
|
||||
next(players[0]) |
||||
} |
||||
|
||||
const params = new URLSearchParams(location.search) |
||||
const { ai = '', seed } = Object.fromEntries(params) |
||||
const search = () => String(params).replace(/%2F/g, '/').replace(/%40/g, '@') |
||||
|
||||
if (!seed) { |
||||
params.set('seed', Math.abs(~~(Math.random()*0xffffffff))) |
||||
location = `${location.origin}${location.pathname}?${search()}${location.hash}` |
||||
} |
||||
|
||||
start({ urls: ai.split(' '), seed }).then(console.log, console.error) |
||||
|
||||
</script> |
||||
</body> |
||||
</html> |
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
<title>Tron</title> |
||||
<meta charset="utf-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
||||
<link rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;base64,AAABAAEAEBAQAAEABAAoAQAAFgAAACgAAAAQAAAAIAAAAAEABAAAAAAAgAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAx42QAFP/FABXQkMAQC0uAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAQBEREREREREAFEREREREQQAURERERERBABREREREREEAFEREREREQQAURERERERBABQERAAEREEAFCBEIiREQQAUQgRERERBABRAJEREREEAFAJEREREQQAUJERERERBABRERERERDEAEREREREREQQAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"> |
||||
<style> |
||||
body { |
||||
background: #13191a; |
||||
display: grid; |
||||
grid-template-columns: 1fr 300px 300px 1fr; |
||||
font-family: monospace; |
||||
color: #7cecff; |
||||
margin: 0; |
||||
padding: 0; |
||||
} |
||||
|
||||
h1 { letter-spacing: 6px } |
||||
h2 { |
||||
margin-bottom: 6px; |
||||
text-overflow: ellipsis; |
||||
max-width: calc(50vw - 300px); |
||||
} |
||||
|
||||
h2:after { |
||||
display: block; |
||||
content: ' '; |
||||
margin-top: 12px; |
||||
height: 3px; |
||||
} |
||||
|
||||
h2 b { |
||||
display: block; |
||||
overflow: hidden; |
||||
text-overflow: ellipsis; |
||||
} |
||||
|
||||
|
||||
#player-0 h2:after { |
||||
background-image: linear-gradient(to left, #0000, currentColor 18px, #0000); |
||||
margin-right: -10px; |
||||
} |
||||
|
||||
#player-1 h2:after { |
||||
margin-left: -10px; |
||||
background-image: linear-gradient(to right, #0000, currentColor 18px, #0000); |
||||
} |
||||
|
||||
canvas { |
||||
display: block; |
||||
width: 600px; |
||||
height: 600px; |
||||
grid-column: 2/4; |
||||
box-shadow: 0 0 20px 1px #7cecff42; |
||||
outline: 1px solid; |
||||
outline-offset: -1px; |
||||
} |
||||
|
||||
button { |
||||
border: 1px solid; |
||||
color: currentColor; |
||||
background: transparent; |
||||
padding: 0 2px; |
||||
border-radius: 3px; |
||||
} |
||||
|
||||
svg { |
||||
margin-top: 36px; |
||||
width: 300px; |
||||
} |
||||
|
||||
#notice { |
||||
display: flex; |
||||
justify-content: space-between; |
||||
padding: 2px 0; |
||||
user-select: none; |
||||
} |
||||
|
||||
.hide { |
||||
visibility: hidden; |
||||
pointer-events: none; |
||||
} |
||||
|
||||
#title { |
||||
display: block; |
||||
margin: 0; |
||||
padding: 0; |
||||
grid-column: 2/4; |
||||
text-align: center; |
||||
color: cyan; |
||||
} |
||||
|
||||
#player-0, #player-1 { |
||||
margin: 12px; |
||||
padding: 6px; |
||||
user-select: none; |
||||
} |
||||
|
||||
#player-0 { |
||||
grid-column: 1; |
||||
text-align: right; |
||||
} |
||||
|
||||
#player-1 { grid-column: 4 } |
||||
|
||||
|
||||
#controls { |
||||
grid-column: 2/4; |
||||
height: 10px; |
||||
background: #7cecff20; |
||||
margin-top: 1px; |
||||
} |
||||
|
||||
#bar { |
||||
overflow: hidden; |
||||
position: relative; |
||||
box-shadow: 0 0 20px 1px #7cecff30; |
||||
outline: 1px solid; |
||||
outline-offset: -1px; |
||||
} |
||||
|
||||
#loading { background: #7cecff40 } |
||||
#position { background: #7cecff } |
||||
#loading, #position { |
||||
position: absolute; |
||||
transform: translate(-600px); |
||||
pointer-events: none; |
||||
} |
||||
|
||||
#bar, #loading, #position { |
||||
height: 100%; |
||||
width: 100%; |
||||
} |
||||
|
||||
@media (orientation: portrait) { |
||||
h2 { max-width: 300px } |
||||
#controls { grid-row: 3 } |
||||
#player-0 { grid-column: 2; grid-row: 4 } |
||||
#player-1 { grid-column: 3; grid-row: 4 } |
||||
} |
||||
|
||||
</style> |
||||
</head> |
||||
<body> |
||||
<div id="title"> |
||||
<svg fill="cyan" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 449.9 184.1"> |
||||
<defs><filter id="g"><feDropShadow dx="0" dy="0" stdDeviation="3" flood-color="cyan"/></filter></defs> |
||||
<path style="filter:url(#g)" d="M36 62.6H5.2v-19H36v19zM38.4 39H3l-3 2.8v23l3 2.5h35.4l3.1-2.7V41.7L38.4 39z"/><g clip-path="url(#clipPath4354)" transform="matrix(1.25 0 0 -1.25 -135 647)"><path style="filter:url(#g)" d="M333.3 426.4a18.9 18.9 0 00-19.1 18.5c0 10.3 8.6 18.6 19.1 18.6a18.9 18.9 0 0019.2-19.2c-.4-10-8.8-18-19.2-18m0 41.2c-12.8 0-23.2-10.1-23.2-22.6a22.9 22.9 0 0123.2-22.5c12.9 0 23.2 10 23.2 22.5a22.9 22.9 0 01-23.2 22.6"/><path style="filter:url(#g)" d="M333.3 406.6c-21.8 0-39.4 17.2-39.4 38.3s17.6 38.3 39.4 38.3c21.8 0 39.5-17.1 39.5-38.3s-17.7-38.3-39.5-38.3m0 80.6a42.9 42.9 0 01-43.4-42.9c.3-23 19.6-41.6 43.4-41.6a42.9 42.9 0 0143.5 42.2 42.9 42.9 0 01-43.5 42.3M463.7 407H457l-28.4 31.8v9.5H448v34.5h15.6V407zm1.5 79.5H447l-2.7-3.2v-31.5h-17.1c-2.2 0-2.2-1.2-2.2-2.1v-12.2l30.4-34.4h9.8l2.6 2.4v78.1l-2.6 3zM419.2 441.1h-19.6v-33.9h-15.7v75.6h6.9l28.4-32.1V441zm-26.9 45.3h-9.8l-2.6-2.5v-78.1l2.6-2.7h18.2l2.7 3v31.6h17.1c2.2 0 2.2 1.1 2.2 2V452l-30.4 34.5zM283.2 407h-18.8l-31.8 32.5v13.1h52a22.5 22.5 0 00-22-15.5h-9.2l29.8-30m-20 26.2c13.8.3 24.6 9.7 26.2 23h-58l-2.7-2.6V439c0-.5 0-1 .4-1.5l33.4-34.3h26.3v4.1l-26.2 26h.6zM220.2 407h-15.7v45.6h15.7V407zm1.3 49.4h-18.7l-2.5-2.7v-48l2.7-2.6h18.6l2.6 3v47.6l-2.7 2.7z"/><path style="filter:url(#g)" d="M172.5 467.4c-3.7 0-6.8-3.1-6.8-6.8v-53.5l-15.6-.1v54.8a20.7 20.7 0 0021.2 20.8h91.2a23 23 0 0022-15.2h-112zm90 19.1h-91.1a24.6 24.6 0 01-25.6-25v-55.9l3-2.4h18.5l2.4 2.9v54.4a3 3 0 003.2 3h116.4a27 27 0 01-26.7 23"/></g></svg></div> |
||||
<div id="player-0"> |
||||
<h2><b>AI-0</b></h2> |
||||
<b>LOADING</b> |
||||
<br> |
||||
<b>0</b> |
||||
</div> |
||||
<canvas width="1200" height="1200"></canvas> |
||||
<div id="player-1"> |
||||
<h2><b>AI-1</b></h2> |
||||
<b>LOADING</b> |
||||
<br> |
||||
<b>0</b> |
||||
</div> |
||||
<div id="controls"> |
||||
<div id="bar"> |
||||
<div id="loading"></div> |
||||
<div id="position"></div> |
||||
</div> |
||||
<div id="notice" class="hide"> |
||||
> scroll or keys to move step by step, click to jump. (shift = fast) |
||||
<button>hide</button> |
||||
</div> |
||||
</div> |
||||
<script type="module"> |
||||
import { move, update, colorize } from './lib/display.js' |
||||
import { init, injectedCode } from './lib/state.js' |
||||
|
||||
const [canvas] = document.getElementsByTagName('canvas') |
||||
const [hide] = document.getElementsByTagName('button') |
||||
const bar = document.getElementById('bar') |
||||
const loading = document.getElementById('loading') |
||||
const position = document.getElementById('position') |
||||
|
||||
hide.onclick = () => { |
||||
hide.parentElement.classList.add('hide') |
||||
localStorage.hide = 1 |
||||
} |
||||
|
||||
localStorage.hide || hide.parentElement.classList.remove('hide') |
||||
|
||||
const buildInfo = (player, i) => { |
||||
const elem = document.getElementById(`player-${i}`) |
||||
if (!elem) return { score: () => {}, status: () => {} } |
||||
const [name, status, score] = [...elem.children].map(e => e.firstChild).filter(Boolean) |
||||
name.firstChild.data = player.name |
||||
elem.style.color = `hsl(${player.hue*360}, 100%, 70%)` |
||||
elem.style.textShadow = `0 0 6px hsla(${player.hue*360}, 100%, 70%, 0.4)` |
||||
elem.style.width = 'calc(100% - 36px)' // force width force redraw |
||||
// this fix a bug on chrome, not re-applying `currentColor` to gradients |
||||
|
||||
return { |
||||
score: text => player.dead || (score.data = text), |
||||
status: text => status.data = text, |
||||
} |
||||
} |
||||
|
||||
const notInBounds = n => n >= 100 || n < 0 |
||||
|
||||
const getShaUrl = login => |
||||
`https://api.github.com/repos/${login}/tron/commits/master` |
||||
|
||||
const getAIUrl = (login, sha, ai) => |
||||
`https://rawcdn.githack.com/${login}/tron/${sha}/ai/${ai || login}.js` |
||||
|
||||
const getSha = async login => (await (await fetch(getShaUrl(login))).json()).sha |
||||
const toBlob = async r => |
||||
new Blob([`${await r.text()}${injectedCode}`], { type : 'text/javascript' }) |
||||
const toUrlObject = b => URL.createObjectURL(b, { type: 'text/javascript' }) |
||||
const memo = {} |
||||
const fetchBlob = url => memo[url] |
||||
|| (memo[url] = fetch(url).then(toBlob).then(toUrlObject)) |
||||
|
||||
const getGithackUrl = async (login, sha) => { |
||||
if (!sha || sha === 'master') { |
||||
sha = localStorage[login] || (localStorage[login] = await getSha(login)) |
||||
} |
||||
return getAIUrl(login, sha) |
||||
} |
||||
|
||||
const remoteURL = (login, sha) => location.host.startsWith('git.') |
||||
? `${location.origin}/${login}/tron/raw/branch/${sha || 'master'}/ai.js` |
||||
: getGithackUrl(login, sha) |
||||
|
||||
const formatURL = url => { |
||||
if (url.startsWith('/')) return `${location.pathname}/ai/${url}` |
||||
if (url.startsWith('https://')) return url |
||||
if (url.includes(location.hostname)) return `https://${url}` |
||||
return url.includes('@') |
||||
? remoteURL(...url.split('@')) |
||||
: `${location.origin}/${url}` |
||||
} |
||||
|
||||
const start = async ({ urls, seed }) => { |
||||
if (urls.length < 2) throw Error('2 AI urls are required to play') |
||||
const players = init({ players: urls, seed }) |
||||
|
||||
let turn = 1, maxTurn = 1, t = 1, cap = 10000, down |
||||
const done = new Set(players) |
||||
|
||||
const setPosition = e => { |
||||
const v = typeof e === 'number' |
||||
? Math.max(e, 1) |
||||
: Math.floor(Math.max(e.pageX - bar.offsetLeft, 1) / 600 * cap) |
||||
|
||||
t = Math.min(v, maxTurn) |
||||
requested || (requested = requestAnimationFrame(refresh)) |
||||
} |
||||
|
||||
window.onkeydown = e => { |
||||
const step = e.shiftKey ? 10 : 1 |
||||
switch (e.key) { |
||||
case 'ArrowLeft': case 'a': case 'q': case 'l': return setPosition(t - step) |
||||
case 'ArrowRight': case 'e': case 'd': case 'k': return setPosition(t + step) |
||||
} |
||||
} |
||||
|
||||
window.onmousemove = (e) => e.which ? (down && setPosition(e)) : (down = false) |
||||
bar.onmousedown = (e) => (down = true) && setPosition(e) |
||||
bar.onwheel = canvas.onwheel = (e) => |
||||
setPosition(t + Math.sign(e.deltaY) * (e.shiftKey ? 10 : 1)) |
||||
|
||||
let requested, timeout |
||||
const refresh = () => { |
||||
requested = update(t) |
||||
players[0].score(turn) |
||||
players[1].score(turn) |
||||
loading.style.transform = `translate(${((turn / cap)*600)-600}px)` |
||||
position.style.transform = `translate(${((t / cap)*600)-600}px)` |
||||
} |
||||
|
||||
const next = (player) => { |
||||
clearTimeout(player.timeout) |
||||
done.add(player) |
||||
requested || (requested = requestAnimationFrame(refresh)) |
||||
|
||||
// check if all AI are done |
||||
if (done.size >= players.length) { |
||||
turn++ |
||||
const data = `[${players.join(',')}]` |
||||
let allDead = true |
||||
for (const p of players) { |
||||
if (p.dead) continue |
||||
allDead ? (allDead = false) : cap-- |
||||
done.delete(p) |
||||
p.worker.postMessage(data) |
||||
p.timeout = setTimeout(p.kill, 50, 'TIMEOUT') |
||||
} |
||||
allDead && (cap = turn) |
||||
t === maxTurn ? (t = maxTurn = turn) : (maxTurn = turn) |
||||
} |
||||
} |
||||
|
||||
await Promise.all(players.map(async (player, i) => { |
||||
const info = buildInfo(player, i) |
||||
player.score = info.score |
||||
|
||||
const kill = (cause) => { |
||||
if (player.dead) return |
||||
console.log(`${player.name} died because he ${cause} at ${player.x} ${player.y}`) |
||||
player.cause = cause |
||||
player.dead = true |
||||
player.worker && player.worker.terminate() |
||||
info.status(cause) |
||||
} |
||||
|
||||
// init the worker |
||||
try { |
||||
const url = await fetchBlob(await formatURL(player.name)) |
||||
player.worker = new Worker(url, { type: 'module', name: player.name }) |
||||
await new Promise((s, f) => { |
||||
player.worker.onmessage = e => e.data === 'loaded' ? s() : f(Error(e.data)) |
||||
player.worker.onerror = f |
||||
player.worker.postMessage(JSON.stringify({ id: player.name, seed })) |
||||
}) |
||||
|
||||
// activate the AI |
||||
info.status('ACTIVE') |
||||
player.kill = cause => { |
||||
kill(cause) |
||||
next(player) |
||||
} |
||||
} catch (err) { |
||||
console.error(err) |
||||
kill('FAILED-TO-LOAD') |
||||
} |
||||
|
||||
move(player.x, player.y, player.color, turn) |
||||
|
||||
// handle each response from the AI |
||||
player.worker.onmessage = ({ data }) => { |
||||
if (done.has(player)) return player.kill('UNEXPECTED-MESSAGE') |
||||
if (!data) return player.kill('STUCK') |
||||
const { x, y } = JSON.parse(data) |
||||
if (typeof x !== 'number' || typeof y !== 'number') return player.kill('INVALID_INPUT') |
||||
if (notInBounds(x) || notInBounds(y)) return player.kill('OUT_OF_BOUNDS') |
||||
if ( |
||||
!(x === player.x - 1 && y === player.y) && |
||||
!(x === player.x + 1 && y === player.y) && |
||||
!(x === player.x && player.y === y + 1) && |
||||
!(x === player.x && player.y === y - 1) |
||||
) return player.kill('IMPOSSIBLE_MOVE') |
||||
|
||||
player.x = x |
||||
player.y = y |
||||
const failure = move(x, y, player.color, turn) |
||||
if (failure === turn) { |
||||
for (const p of players) { |
||||
p.x === player.x && p.y === player.y && p.kill('MULTI-CRASH') |
||||
} |
||||
colorize(x, y, 0xffffff) |
||||
return player.kill('MULTI-CRASH') |
||||
} else if (failure) return player.kill('CRASH') |
||||
next(player) |
||||
} |
||||
|
||||
player.worker.onerror = () => player.kill('AI-ERROR') |
||||
})) |
||||
|
||||
next(players[0]) |
||||
} |
||||
|
||||
const params = new URLSearchParams(location.search) |
||||
const { ai = '', seed } = Object.fromEntries(params) |
||||
const search = () => String(params).replace(/%2F/g, '/').replace(/%40/g, '@') |
||||
|
||||
if (!seed) { |
||||
params.set('seed', Math.abs(~~(Math.random()*0xffffffff))) |
||||
location = `${location.origin}${location.pathname}?${search()}${location.hash}` |
||||
} |
||||
|
||||
start({ urls: ai.split(' '), seed }).then(console.log, console.error) |
||||
|
||||
</script> |
||||
</body> |
||||
</html> |
||||
|
@ -1,132 +1,132 @@
|
||||
const vertexArray = new Float32Array(100 * 100 * 12) |
||||
const colorArray = new Float32Array(100 * 100 * 6) |
||||
const state = new Float32Array(100 * 100 * 2) |
||||
const [canvas] = document.getElementsByTagName('canvas') |
||||
const gl = canvas.getContext('webgl2', { antialias: false }) |
||||
const S = 0.02 |
||||
|
||||
const applyState = (x, y, turn) => { |
||||
const index = x * 100 + y |
||||
const color = state[index * 2 + 0] > turn ? 0 : state[index * 2 + 1] |
||||
colorArray[index * 6 + 0] = color |
||||
colorArray[index * 6 + 1] = color |
||||
colorArray[index * 6 + 2] = color |
||||
colorArray[index * 6 + 3] = color |
||||
colorArray[index * 6 + 4] = color |
||||
colorArray[index * 6 + 5] = color |
||||
} |
||||
|
||||
export const colorize = (x, y, color) => state[(x * 100 + y) * 2 + 1] = color |
||||
export const move = (x, y, color, turn) => { |
||||
const index = (x * 100 + y) * 2 |
||||
if (state[index]) return state[index] |
||||
state[index] = turn |
||||
state[index + 1] = color |
||||
} |
||||
|
||||
const loop = fn => { |
||||
let x = -1, y = -1 |
||||
while (++x < 100) { |
||||
y = -1 |
||||
while (++y < 100) fn(x, y) |
||||
} |
||||
} |
||||
|
||||
const compileShader = (type, script) => { |
||||
const shader = gl.createShader(type) |
||||
gl.shaderSource(shader, script.trim()) |
||||
gl.compileShader(shader) |
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { |
||||
throw gl.getShaderInfoLog(shader) |
||||
} |
||||
return shader |
||||
} |
||||
|
||||
// program
|
||||
const program = gl.createProgram() |
||||
gl.attachShader(program, compileShader(gl.VERTEX_SHADER, ` |
||||
#version 300 es |
||||
|
||||
in vec2 a_position; |
||||
in float a_color; |
||||
out float v_color; |
||||
|
||||
void main() { |
||||
gl_Position = vec4(a_position * vec2(1, -1), 0, 1); |
||||
v_color = a_color; |
||||
}`))
|
||||
|
||||
gl.attachShader(program, compileShader(gl.FRAGMENT_SHADER, ` |
||||
#version 300 es |
||||
|
||||
precision mediump float; |
||||
in float v_color; |
||||
out vec4 outColor; |
||||
|
||||
vec4 unpackColor(float f) { |
||||
vec4 color; |
||||
color.r = floor(f / 65536.0); |
||||
color.g = floor((f - color.r * 65536.0) / 256.0); |
||||
color.b = floor(f - color.r * 65536.0 - color.g * 256.0); |
||||
color.a = 256.0; |
||||
return color / 256.0; |
||||
} |
||||
|
||||
void main() { |
||||
outColor = unpackColor(v_color); |
||||
}`))
|
||||
|
||||
gl.linkProgram(program) |
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { |
||||
throw gl.getProgramInfoLog(program) |
||||
} |
||||
|
||||
gl.useProgram(program) |
||||
|
||||
// initialize state
|
||||
loop((x, y) => { |
||||
const x1 = ((x + 1) - 50) / 50 - S |
||||
const y1 = ((y + 1) - 50) / 50 - S |
||||
const x2 = x1 + S |
||||
const y2 = y1 + S |
||||
const index = (x * 100 + y) * 12 |
||||
vertexArray[index + 0x0] = x1 |
||||
vertexArray[index + 0x1] = y1 |
||||
vertexArray[index + 0x2] = x2 |
||||
vertexArray[index + 0x3] = y1 |
||||
vertexArray[index + 0x4] = x1 |
||||
vertexArray[index + 0x5] = y2 |
||||
vertexArray[index + 0x6] = x1 |
||||
vertexArray[index + 0x7] = y2 |
||||
vertexArray[index + 0x8] = x2 |
||||
vertexArray[index + 0x9] = y1 |
||||
vertexArray[index + 0xa] = x2 |
||||
vertexArray[index + 0xb] = y2 |
||||
}) |
||||
|
||||
const vertexBuffer = gl.createBuffer() |
||||
const a_position = gl.getAttribLocation(program, 'a_position') |
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) |
||||
gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 0, 0) |
||||
gl.enableVertexAttribArray(a_position) |
||||
gl.bufferData(gl.ARRAY_BUFFER, vertexArray, gl.STATIC_DRAW) |
||||
gl.drawArrays(gl.TRIANGLES, 0, 60000) |
||||
|
||||
// color buffer
|
||||
const colorBuffer = gl.createBuffer() |
||||
const a_color = gl.getAttribLocation(program, 'a_color') |
||||
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer) |
||||
gl.vertexAttribPointer(a_color, 1, gl.FLOAT, false, 0, 0) |
||||
gl.enableVertexAttribArray(a_color) |
||||
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer) |
||||
|
||||
export const update = (turn) => { |
||||
loop((x, y) => applyState(x, y, turn)) |
||||
gl.bufferData(gl.ARRAY_BUFFER, colorArray, gl.STATIC_DRAW) |
||||
gl.drawArrays(gl.TRIANGLES, 0, 60000) |
||||
} |
||||
|
||||
export const reset = () => { |
||||
state.fill(0) |
||||
update(0) |
||||
} |
||||
const vertexArray = new Float32Array(100 * 100 * 12) |
||||
const colorArray = new Float32Array(100 * 100 * 6) |
||||
const state = new Float32Array(100 * 100 * 2) |
||||
const [canvas] = document.getElementsByTagName('canvas') |
||||
const gl = canvas.getContext('webgl2', { antialias: false }) |
||||
const S = 0.02 |
||||
|
||||
const applyState = (x, y, turn) => { |
||||
const index = x * 100 + y |
||||
const color = state[index * 2 + 0] > turn ? 0 : state[index * 2 + 1] |
||||
colorArray[index * 6 + 0] = color |
||||
colorArray[index * 6 + 1] = color |
||||
colorArray[index * 6 + 2] = color |
||||
colorArray[index * 6 + 3] = color |
||||
colorArray[index * 6 + 4] = color |
||||
colorArray[index * 6 + 5] = color |
||||
} |
||||
|
||||
export const colorize = (x, y, color) => state[(x * 100 + y) * 2 + 1] = color |
||||
export const move = (x, y, color, turn) => { |
||||
const index = (x * 100 + y) * 2 |
||||
if (state[index]) return state[index] |
||||
state[index] = turn |
||||
state[index + 1] = color |
||||
} |
||||
|
||||
const loop = fn => { |
||||
let x = -1, y = -1 |
||||
while (++x < 100) { |
||||
y = -1 |
||||
while (++y < 100) fn(x, y) |
||||
} |
||||
} |
||||
|
||||
const compileShader = (type, script) => { |
||||
const shader = gl.createShader(type) |
||||
gl.shaderSource(shader, script.trim()) |
||||
gl.compileShader(shader) |
||||
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { |
||||
throw gl.getShaderInfoLog(shader) |
||||
} |
||||
return shader |
||||
} |
||||
|
||||
// program
|
||||
const program = gl.createProgram() |
||||
gl.attachShader(program, compileShader(gl.VERTEX_SHADER, ` |
||||
#version 300 es |
||||
|
||||
in vec2 a_position; |
||||
in float a_color; |
||||
out float v_color; |
||||
|
||||
void main() { |
||||
gl_Position = vec4(a_position * vec2(1, -1), 0, 1); |
||||
v_color = a_color; |
||||
}`))
|
||||
|
||||
gl.attachShader(program, compileShader(gl.FRAGMENT_SHADER, ` |
||||
#version 300 es |
||||
|
||||
precision mediump float; |
||||
in float v_color; |
||||
out vec4 outColor; |
||||
|
||||
vec4 unpackColor(float f) { |
||||
vec4 color; |
||||
color.r = floor(f / 65536.0); |
||||
color.g = floor((f - color.r * 65536.0) / 256.0); |
||||
color.b = floor(f - color.r * 65536.0 - color.g * 256.0); |
||||
color.a = 256.0; |
||||
return color / 256.0; |
||||
} |
||||
|
||||
void main() { |
||||
outColor = unpackColor(v_color); |
||||
}`))
|
||||
|
||||
gl.linkProgram(program) |
||||
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { |
||||
throw gl.getProgramInfoLog(program) |
||||
} |
||||
|
||||
gl.useProgram(program) |
||||
|
||||
// initialize state
|
||||
loop((x, y) => { |
||||
const x1 = ((x + 1) - 50) / 50 - S |
||||
const y1 = ((y + 1) - 50) / 50 - S |
||||
const x2 = x1 + S |
||||
const y2 = y1 + S |
||||
const index = (x * 100 + y) * 12 |
||||
vertexArray[index + 0x0] = x1 |
||||
vertexArray[index + 0x1] = y1 |
||||
vertexArray[index + 0x2] = x2 |
||||
vertexArray[index + 0x3] = y1 |
||||
vertexArray[index + 0x4] = x1 |
||||
vertexArray[index + 0x5] = y2 |
||||
vertexArray[index + 0x6] = x1 |
||||
vertexArray[index + 0x7] = y2 |
||||
vertexArray[index + 0x8] = x2 |
||||
vertexArray[index + 0x9] = y1 |
||||
vertexArray[index + 0xa] = x2 |
||||
vertexArray[index + 0xb] = y2 |
||||
}) |
||||
|
||||
const vertexBuffer = gl.createBuffer() |
||||
const a_position = gl.getAttribLocation(program, 'a_position') |
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer) |
||||
gl.vertexAttribPointer(a_position, 2, gl.FLOAT, false, 0, 0) |
||||
gl.enableVertexAttribArray(a_position) |
||||
gl.bufferData(gl.ARRAY_BUFFER, vertexArray, gl.STATIC_DRAW) |
||||
gl.drawArrays(gl.TRIANGLES, 0, 60000) |
||||
|
||||
// color buffer
|
||||
const colorBuffer = gl.createBuffer() |
||||
const a_color = gl.getAttribLocation(program, 'a_color') |
||||
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer) |
||||
gl.vertexAttribPointer(a_color, 1, gl.FLOAT, false, 0, 0) |
||||
gl.enableVertexAttribArray(a_color) |
||||
gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer) |
||||
|
||||
export const update = (turn) => { |
||||
loop((x, y) => applyState(x, y, turn)) |
||||
gl.bufferData(gl.ARRAY_BUFFER, colorArray, gl.STATIC_DRAW) |
||||
gl.drawArrays(gl.TRIANGLES, 0, 60000) |
||||
} |
||||
|
||||
export const reset = () => { |
||||
state.fill(0) |
||||
update(0) |
||||
} |
||||
|
@ -1,96 +1,96 @@
|
||||
const SIZE = 100 |
||||
const h = SIZE / 2 |
||||
const m = h * 0.8 |
||||
const max = m => n => n > m ? max1(n - m) : n |
||||
const max1 = max(1) |
||||
const max2PI = max(Math.PI * 2) |
||||
const toInt = (r, g, b) => (r << 16) | (g << 8) | b |
||||
const toRange = n => Math.round(n * 0xFF) |
||||
const hue2rgb = (p, q, t) => { |
||||
if (t < 0) t += 1 |
||||
if (t > 1) t -= 1 |
||||
if (t < 1/6) return p + (q - p) * 6 * t |
||||
if (t < 1/2) return q |
||||
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6 |
||||
return p |
||||
} |
||||
|
||||
const hslToRgb = (h, s, l) => { |
||||
if (!s) return toInt(toRange(l), toRange(l), toRange(l)) |
||||
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s |
||||
const p = 2 * l - q |
||||
const r = hue2rgb(p, q, h + 1/3) |
||||
const g = hue2rgb(p, q, h) |
||||
const b = hue2rgb(p, q, h - 1/3) |
||||
|
||||
return toInt(toRange(r), toRange(g), toRange(b)) |
||||
} |
||||
|
||||
export const init = ({ players, seed }) => { |
||||
const rand = () => { |
||||
let t = seed += 0x6D2B79F5 |
||||
t = Math.imul(t ^ t >>> 15, t | 1) |
||||
t ^= t + Math.imul(t ^ t >>> 7, t | 61) |
||||
return ((t ^ t >>> 14) >>> 0) / 4294967296 |
||||
} |
||||
|
||||
const angle = (Math.PI * 2) / players.length |
||||
const rate = (SIZE / players.length / SIZE) |
||||
const shift = angle * rand() |
||||
|
||||
// shuffle using seeded random
|
||||
players.sort((a, b) => a.name - b.name) |
||||
let i = players.length, j, tmp |
||||
while (--i > 0) { |
||||
j = Math.floor(rand() * (i + 1)) |
||||
tmp = players[j] |
||||
players[j] = players[i] |
||||
players[i] = tmp |
||||
} |
||||
|
||||
return players.map((name, i) => { |
||||
const jsonName = `"name":${JSON.stringify(name)}` |
||||
const hue = max1(i * rate + 0.25) |
||||
const p = { |
||||
hue, |
||||
name, |
||||
x: Math.round(max2PI(Math.cos(angle * i + shift)) * m + h), |
||||
y: Math.round(max2PI(Math.sin(angle * i + shift)) * m + h), |
||||
cardinal: 0, |
||||
direction: 0, |
||||
color: hslToRgb(hue, 1, 0.5), |
||||
toString: () => `{${jsonName},"dead":${!!p.dead},"cardinal":${p.cardinal},"direction":${p.direction},"color":${p.color},"x":${p.x},"y":${p.y},"coords":[{"x":${p.x},"y":${p.y - 1},"cardinal":0,"direction":${(4 - p.cardinal) % 4}},{"x":${p.x + 1},"y":${p.y},"cardinal":1,"direction":${(5 - p.cardinal) % 4}},{"x":${p.x},"y":${p.y + 1},"cardinal":2,"direction":${(6 - p.cardinal) % 4}},{"x":${p.x - 1},"y":${p.y},"cardinal":3,"direction":${(7 - p.cardinal) % 4}}]}`, |
||||
} |
||||
return p |
||||
}) |
||||
} |
||||
|
||||
export const injectedCode = ` |
||||
if (typeof update !== 'function') throw Error('Update function not defined') |
||||
addEventListener('message', self.init = initEvent => { |
||||
const { seed, id } = JSON.parse(initEvent.data) |
||||
const isOwnPlayer = p => p.name === id |
||||
|
||||
Math.random = () => { |
||||
let t = seed += 0x6D2B79F5 |
||||
t = Math.imul(t ^ t >>> 15, t | 1) |
||||
t ^= t + Math.imul(t ^ t >>> 7, t | 61) |
||||
return ((t ^ t >>> 14) >>> 0) / 4294967296 |
||||
} |
||||
|
||||
removeEventListener('message', self.init) |
||||
addEventListener('message', ({ data }) => { |
||||
const players = JSON.parse(data) |
||||
const player = players.find(isOwnPlayer) |
||||
player.isOwnPlayer = true |
||||
|
||||
try { postMessage(JSON.stringify(update({ players, player }))) } |
||||
catch (err) { |
||||
console.error(err) |
||||
throw err |
||||
} |
||||
}) |
||||
postMessage('loaded') // Signal that the loading is over
|
||||
}) |
||||
` |
||||
const SIZE = 100 |
||||
const h = SIZE / 2 |
||||
const m = h * 0.8 |
||||
const max = m => n => n > m ? max1(n - m) : n |
||||
const max1 = max(1) |
||||
const max2PI = max(Math.PI * 2) |
||||
const toInt = (r, g, b) => (r << 16) | (g << 8) | b |
||||
const toRange = n => Math.round(n * 0xFF) |
||||
const hue2rgb = (p, q, t) => { |
||||
if (t < 0) t += 1 |
||||
if (t > 1) t -= 1 |
||||
if (t < 1/6) return p + (q - p) * 6 * t |
||||
if (t < 1/2) return q |
||||
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6 |
||||
return p |
||||
} |
||||
|
||||
const hslToRgb = (h, s, l) => { |
||||
if (!s) return toInt(toRange(l), toRange(l), toRange(l)) |
||||
|
||||
const q = l < 0.5 ? l * (1 + s) : l + s - l * s |
||||
const p = 2 * l - q |
||||
const r = hue2rgb(p, q, h + 1/3) |
||||
const g = hue2rgb(p, q, h) |
||||
const b = hue2rgb(p, q, h - 1/3) |
||||
|
||||
return toInt(toRange(r), toRange(g), toRange(b)) |
||||
} |
||||
|
||||
export const init = ({ players, seed }) => { |
||||
const rand = () => { |
||||
let t = seed += 0x6D2B79F5 |
||||
t = Math.imul(t ^ t >>> 15, t | 1) |
||||
t ^= t + Math.imul(t ^ t >>> 7, t | 61) |
||||
return ((t ^ t >>> 14) >>> 0) / 4294967296 |
||||
} |
||||
|
||||
const angle = (Math.PI * 2) / players.length |
||||
const rate = (SIZE / players.length / SIZE) |
||||
const shift = angle * rand() |
||||
|
||||
// shuffle using seeded random
|
||||
players.sort((a, b) => a.name - b.name) |
||||
let i = players.length, j, tmp |
||||
while (--i > 0) { |
||||
j = Math.floor(rand() * (i + 1)) |
||||
tmp = players[j] |
||||
players[j] = players[i] |
||||
players[i] = tmp |
||||
} |
||||
|
||||
return players.map((name, i) => { |
||||
const jsonName = `"name":${JSON.stringify(name)}` |
||||
const hue = max1(i * rate + 0.25) |
||||
const p = { |
||||
hue, |
||||
name, |
||||
x: Math.round(max2PI(Math.cos(angle * i + shift)) * m + h), |
||||
y: Math.round(max2PI(Math.sin(angle * i + shift)) * m + h), |
||||
cardinal: 0, |
||||
direction: 0, |
||||
color: hslToRgb(hue, 1, 0.5), |
||||
toString: () => `{${jsonName},"dead":${!!p.dead},"cardinal":${p.cardinal},"direction":${p.direction},"color":${p.color},"x":${p.x},"y":${p.y},"coords":[{"x":${p.x},"y":${p.y - 1},"cardinal":0,"direction":${(4 - p.cardinal) % 4}},{"x":${p.x + 1},"y":${p.y},"cardinal":1,"direction":${(5 - p.cardinal) % 4}},{"x":${p.x},"y":${p.y + 1},"cardinal":2,"direction":${(6 - p.cardinal) % 4}},{"x":${p.x - 1},"y":${p.y},"cardinal":3,"direction":${(7 - p.cardinal) % 4}}]}`, |
||||
} |
||||
return p |
||||
}) |
||||
} |
||||
|
||||
export const injectedCode = ` |
||||
if (typeof update !== 'function') throw Error('Update function not defined') |
||||
addEventListener('message', self.init = initEvent => { |
||||
const { seed, id } = JSON.parse(initEvent.data) |
||||
const isOwnPlayer = p => p.name === id |
||||
|
||||
Math.random = () => { |
||||
let t = seed += 0x6D2B79F5 |
||||
t = Math.imul(t ^ t >>> 15, t | 1) |
||||
t ^= t + Math.imul(t ^ t >>> 7, t | 61) |
||||
return ((t ^ t >>> 14) >>> 0) / 4294967296 |
||||
} |
||||
|
||||
removeEventListener('message', self.init) |
||||
addEventListener('message', ({ data }) => { |
||||
const players = JSON.parse(data) |
||||
const player = players.find(isOwnPlayer) |
||||
player.isOwnPlayer = true |
||||
|
||||
try { postMessage(JSON.stringify(update({ players, player }))) } |
||||
catch (err) { |
||||
console.error(err) |
||||
throw err |
||||
} |
||||
}) |
||||
postMessage('loaded') // Signal that the loading is over
|
||||
}) |
||||
` |
||||
|
@ -1,45 +1,45 @@
|
||||
#### Functional |
||||
|
||||
###### Does the AI crash because of too much usage of CPU? |
||||
|
||||
##### Modify the link so that the users are the audited `GITHUB_LOGIN` |
||||
|
||||
##### and `/random.js`. Try three times, changing the seed each time. The best of three, wins |
||||
|
||||
###### Did the audited AI won against random AI? |
||||
|
||||
##### Modify the link so that the users are the audited `GITHUB_LOGIN` |
||||
|
||||
##### and `/right.js`. Try three times, changing the seed each time. The best of three, wins |
||||
|
||||
###### Did the audited AI won against `right` AI? |
||||
|
||||
##### Modify the link so that the users are the audited `GITHUB_LOGIN` |
||||
|
||||
##### and `/snail.js`. Try three times, changing the seed each time. The best of three, wins |
||||
|
||||
###### Did the audited AI won against `snail` AI? |
||||
|
||||
###### Does the code avoid [deep nesting](https://testing.googleblog.com/2017/06/code-health-reduce-nesting-reduce.html)? |
||||
|
||||
##### Modify the link so that the users are the audited `GITHUB_LOGIN` |
||||
|
||||
##### and `/hard.js`. Try three times, changing the seed each time. The best of three, wins |
||||
|
||||
###### Did the audited AI won against `hard` AI? |
||||
|
||||
#### Bonus |
||||
|
||||
##### Modify the link so that the users are the audited `GITHUB_LOGIN` |
||||
|
||||
##### and `/licence-to-kill.js`. Try three times, changing the seed each time. The best of three, wins |
||||
|
||||
###### +Did the audited AI won against `licence-to-kill` AI? |
||||
|
||||
##### If you have an AI |
||||
|
||||
##### Modify the link so that the users are the audited `GITHUB_LOGIN` |
||||
|
||||
##### and **your AI**. Try three times, changing the seed each time. The best of three, wins |
||||
|
||||
###### Did the audited AI won against your AI? |
||||
#### Functional |
||||
|
||||
###### Does the AI crash because of too much usage of CPU? |
||||
|
||||
##### Modify the link so that the users are the audited `GITHUB_LOGIN` |
||||
|
||||
##### and `/random.js`. Try three times, changing the seed each time. The best of three, wins |
||||
|
||||
###### Did the audited AI won against random AI? |
||||
|
||||
##### Modify the link so that the users are the audited `GITHUB_LOGIN` |
||||
|
||||
##### and `/right.js`. Try three times, changing the seed each time. The best of three, wins |
||||
|
||||
###### Did the audited AI won against `right` AI? |
||||
|
||||
##### Modify the link so that the users are the audited `GITHUB_LOGIN` |
||||
|
||||
##### and `/snail.js`. Try three times, changing the seed each time. The best of three, wins |
||||
|
||||
###### Did the audited AI won against `snail` AI? |
||||
|
||||
###### Does the code avoid [deep nesting](https://testing.googleblog.com/2017/06/code-health-reduce-nesting-reduce.html)? |
||||
|
||||
##### Modify the link so that the users are the audited `GITHUB_LOGIN` |
||||
|
||||
##### and `/hard.js`. Try three times, changing the seed each time. The best of three, wins |
||||
|
||||
###### Did the audited AI won against `hard` AI? |
||||
|
||||
#### Bonus |
||||
|
||||
##### Modify the link so that the users are the audited `GITHUB_LOGIN` |
||||
|
||||
##### and `/licence-to-kill.js`. Try three times, changing the seed each time. The best of three, wins |
||||
|
||||
###### +Did the audited AI won against `licence-to-kill` AI? |
||||
|
||||
##### If you have an AI |
||||
|
||||
##### Modify the link so that the users are the audited `GITHUB_LOGIN` |
||||
|
||||
##### and **your AI**. Try three times, changing the seed each time. The best of three, wins |
||||
|
||||
###### Did the audited AI won against your AI? |
||||
|
Loading…
Reference in new issue