Browse Source

normalize line endings (again)

content-update
Clement Denis 4 years ago
parent
commit
756d1e249a
  1. 1710
      subjects/ascii-art/thinkertoy.txt
  2. 184
      subjects/audits-at-home.en.md
  3. 24
      subjects/biggie-smalls.en.md
  4. 66
      subjects/change.en.md
  5. 26
      subjects/circular.en.md
  6. 42
      subjects/currify.en.md
  7. 96
      subjects/curry-entries.en.md
  8. 54
      subjects/cut-corners.en.md
  9. 38
      subjects/date-is.en.md
  10. 24
      subjects/debounce.en.md
  11. 18
      subjects/deep-copy.en.md
  12. 56
      subjects/filter.en.md
  13. 60
      subjects/find-expression.en.md
  14. 38
      subjects/flat.en.md
  15. 48
      subjects/flow.en.md
  16. 40
      subjects/for-each.en.md
  17. 102
      subjects/forum/forum-moderation.audit.en.md
  18. 366
      subjects/forum/forum.audit.en.md
  19. 56
      subjects/get-json.en.md
  20. 90
      subjects/gougle-search.en.md
  21. 32
      subjects/greedy-url.en.md
  22. 206
      subjects/groupie-tracker/groupie-tracker.audit.en.md
  23. 64
      subjects/index-of.en.md
  24. 90
      subjects/interpolation.en.md
  25. 18
      subjects/invert.en.md
  26. 22
      subjects/ion-out.en.md
  27. 42
      subjects/its-a-match.en.md
  28. 52
      subjects/keep-cut.en.md
  29. 64
      subjects/match-cron.en.md
  30. 30
      subjects/more-or-less.en.md
  31. 62
      subjects/mutability.en.md
  32. 30
      subjects/nasa.en.md
  33. 30
      subjects/primitives.en.md
  34. 54
      subjects/pyramid.en.md
  35. 48
      subjects/race.en.md
  36. 48
      subjects/repeat.en.md
  37. 24
      subjects/replica.en.md
  38. 30
      subjects/returns.en.md
  39. 22
      subjects/series.en.md
  40. 54
      subjects/sign.en.md
  41. 22
      subjects/tetris-optimizer/badexample00.md
  42. 22
      subjects/tetris-optimizer/badexample01.md
  43. 22
      subjects/tetris-optimizer/badexample02.md
  44. 22
      subjects/tetris-optimizer/badexample03.md
  45. 22
      subjects/tetris-optimizer/badexample04.md
  46. 22
      subjects/tetris-optimizer/goodexample00.md
  47. 52
      subjects/tetris-optimizer/goodexample01.md
  48. 92
      subjects/tetris-optimizer/goodexample02.md
  49. 122
      subjects/tetris-optimizer/goodexample03.md
  50. 166
      subjects/tetris-optimizer/tetris-optimizer.audit.en.md
  51. 50
      subjects/triangle.en.md
  52. 342
      subjects/tron/ai/hard.js
  53. 194
      subjects/tron/ai/license-to-kill.js
  54. 114
      subjects/tron/ai/random.js
  55. 96
      subjects/tron/ai/right.js
  56. 80
      subjects/tron/ai/snail.js
  57. 768
      subjects/tron/index.html
  58. 264
      subjects/tron/lib/display.js
  59. 192
      subjects/tron/lib/state.js
  60. 90
      subjects/tron/tron.audit.en.md
  61. 152
      subjects/tron/tron.en.md

1710
subjects/ascii-art/thinkertoy.txt

File diff suppressed because it is too large diff.load

184
subjects/audits-at-home.en.md

@ -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!

24
subjects/biggie-smalls.en.md

@ -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/)

66
subjects/change.en.md

@ -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,
}
```

26
subjects/circular.en.md

@ -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)

42
subjects/currify.en.md

@ -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)

96
subjects/curry-entries.en.md

@ -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 },
}
```

54
subjects/cut-corners.en.md

@ -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
```

38
subjects/date-is.en.md

@ -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)

24
subjects/debounce.en.md

@ -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)

18
subjects/deep-copy.en.md

@ -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/)

56
subjects/filter.en.md

@ -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
```

60
subjects/find-expression.en.md

@ -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'
```

38
subjects/flat.en.md

@ -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
```

48
subjects/flow.en.md

@ -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)

40
subjects/for-each.en.md

@ -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
```

102
subjects/forum/forum-moderation.audit.en.md

@ -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?

366
subjects/forum/forum.audit.en.md

@ -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?

56
subjects/get-json.en.md

@ -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)

90
subjects/gougle-search.en.md

@ -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
```

32
subjects/greedy-url.en.md

@ -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)

206
subjects/groupie-tracker/groupie-tracker.audit.en.md

@ -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?

64
subjects/index-of.en.md

@ -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
```

90
subjects/interpolation.en.md

@ -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)

18
subjects/invert.en.md

@ -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)

22
subjects/ion-out.en.md

@ -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)

42
subjects/its-a-match.en.md

@ -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)

52
subjects/keep-cut.en.md

@ -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)

64
subjects/match-cron.en.md

@ -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)

30
subjects/more-or-less.en.md

@ -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)

62
subjects/mutability.en.md

@ -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',
}
```

30
subjects/nasa.en.md

@ -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)

30
subjects/primitives.en.md

@ -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)

54
subjects/pyramid.en.md

@ -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)

48
subjects/race.en.md

@ -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
```

48
subjects/repeat.en.md

@ -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
```

24
subjects/replica.en.md

@ -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)

30
subjects/returns.en.md

@ -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)

22
subjects/series.en.md

@ -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)

54
subjects/sign.en.md

@ -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
```

22
subjects/tetris-optimizer/badexample00.md

@ -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.
```
####
...#
....
....
```

22
subjects/tetris-optimizer/badexample01.md

@ -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.
```
...#
..#.
.#..
#...
```

22
subjects/tetris-optimizer/badexample02.md

@ -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.
```
...#
...#
#...
#...
```

22
subjects/tetris-optimizer/badexample03.md

@ -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.
```
....
....
....
....
```

22
subjects/tetris-optimizer/badexample04.md

@ -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.
```
..##
....
....
##..
```

22
subjects/tetris-optimizer/goodexample00.md

@ -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
....
.##.
.##.
....
```

52
subjects/tetris-optimizer/goodexample01.md

@ -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.
```
...#
...#
...#
...#
....
....
....
####
.###
...#
....
....
....
..##
.##.
....
```

92
subjects/tetris-optimizer/goodexample02.md

@ -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.
```
...#
...#
...#
...#
....
....
....
####
.###
...#
....
....
....
..##
.##.
....
....
.##.
.##.
....
....
....
##..
.##.
##..
.#..
.#..
....
....
###.
.#..
....
```

122
subjects/tetris-optimizer/goodexample03.md

@ -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.
```
....
.##.
.##.
....
...#
...#
...#
...#
....
..##
.##.
....
....
.##.
.##.
....
....
..#.
.##.
.#..
.###
...#
....
....
##..
.#..
.#..
....
....
..##
.##.
....
##..
.#..
.#..
....
.#..
.##.
..#.
....
....
###.
.#..
....
```

166
subjects/tetris-optimizer/tetris-optimizer.audit.en.md

@ -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?

50
subjects/triangle.en.md

@ -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)

342
subjects/tron/ai/hard.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)
}

194
subjects/tron/ai/license-to-kill.js

@ -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)
}

114
subjects/tron/ai/random.js

@ -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)
}

96
subjects/tron/ai/right.js

@ -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
}

80
subjects/tron/ai/snail.js

@ -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
}

768
subjects/tron/index.html

@ -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>

264
subjects/tron/lib/display.js

@ -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)
}

192
subjects/tron/lib/state.js

@ -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
})
`

90
subjects/tron/tron.audit.en.md

@ -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?

152
subjects/tron/tron.en.md

@ -1,76 +1,76 @@
## Tron
### Objectives
In this project you will have to create your own Tron AI snake
### Getting started
You will need to create a public repository with the name `tron`. Next you need to create a file named `ai.js`. It must respect the instructions given
### Controls
- `arrows` or `scroll` to move step by stem
- `shift` will make it fast
- you can click anywhere on the progress bar to seek into the history
### Rules
- Your AI has to move every turn *(it can not stay still)*
- Every time the AI moves somewhere the AI leaves a color trail.
- the AI can only move to a blank tile.
- the AI can not move out of the map *(100 x 100)*
- the AI can only move to its `left`, `forward` or its `right`.
*(Moving `backward` is suicide as it would hit its own trail !)*
- If too much CPU power is required to decide where to go, the AI dies.
- If two Ais moved to the same spot, both of them die.
- **The AI has to survive as long as it can.**
### The game ends
- Once no players can make a move the player with the biggest score wins
### How to write your AI
- Copy the code on the file [random.js](https://raw.githubusercontent.com/01-edu/public/master/subjects/tron/ai/random.js) to your file, `ai.js`
- You may now edit the `update` function which is called each turn
> ⚠ Do not rename the `update` function ⚠ \
> as it's the function that the worker will try to run to test your AI.
### How to test your AI
- You may use this link [tron](/public/subjects/tron?ai=&seed=1653547275), to test your AI
- You need to add your AI as a user in that link
> Example: if your git login is **Frenchris** and you want to test against **LEEDASILVA** the link becomes: `/public/subjects/tron?AI=Frenchris@master+LEEDASILVA@master&seed=1653547275`
- Open the inspector of the browser used and **disable the cache**
- let's change the update function so that your AI only goes forward.
Replace this line just before the `return` of the update function:
```js
const available = coordsInBound.filter(isFree)
// And I return a random available coord
return pickRandom(available)
```
...with this line:
```js
// always return the first free coordinates
return coordsInBound.filter(isFree)[0]
```
- save the file, push the changes and re-run the game in the browser.
If the cache was correctly disabled,
you have changed your AI behaviour from a random pick of available moves
to only going forward.
- To understand better the way of controlling your AI,
read the comments inside the AI file and do a lot of testing.
- When peer-corrected, you AI will be competing against other AIs.
Be aware that there will be the possibility for the peer-correcter
to use his or her own AI.
*May the best tron win :)*
Have fun and good luck.
## Tron
### Objectives
In this project you will have to create your own Tron AI snake
### Getting started
You will need to create a public repository with the name `tron`. Next you need to create a file named `ai.js`. It must respect the instructions given
### Controls
- `arrows` or `scroll` to move step by stem
- `shift` will make it fast
- you can click anywhere on the progress bar to seek into the history
### Rules
- Your AI has to move every turn *(it can not stay still)*
- Every time the AI moves somewhere the AI leaves a color trail.
- the AI can only move to a blank tile.
- the AI can not move out of the map *(100 x 100)*
- the AI can only move to its `left`, `forward` or its `right`.
*(Moving `backward` is suicide as it would hit its own trail !)*
- If too much CPU power is required to decide where to go, the AI dies.
- If two Ais moved to the same spot, both of them die.
- **The AI has to survive as long as it can.**
### The game ends
- Once no players can make a move the player with the biggest score wins
### How to write your AI
- Copy the code on the file [random.js](https://raw.githubusercontent.com/01-edu/public/master/subjects/tron/ai/random.js) to your file, `ai.js`
- You may now edit the `update` function which is called each turn
> ⚠ Do not rename the `update` function ⚠ \
> as it's the function that the worker will try to run to test your AI.
### How to test your AI
- You may use this link [tron](/public/subjects/tron?ai=&seed=1653547275), to test your AI
- You need to add your AI as a user in that link
> Example: if your git login is **Frenchris** and you want to test against **LEEDASILVA** the link becomes: `/public/subjects/tron?AI=Frenchris@master+LEEDASILVA@master&seed=1653547275`
- Open the inspector of the browser used and **disable the cache**
- let's change the update function so that your AI only goes forward.
Replace this line just before the `return` of the update function:
```js
const available = coordsInBound.filter(isFree)
// And I return a random available coord
return pickRandom(available)
```
...with this line:
```js
// always return the first free coordinates
return coordsInBound.filter(isFree)[0]
```
- save the file, push the changes and re-run the game in the browser.
If the cache was correctly disabled,
you have changed your AI behaviour from a random pick of available moves
to only going forward.
- To understand better the way of controlling your AI,
read the comments inside the AI file and do a lot of testing.
- When peer-corrected, you AI will be competing against other AIs.
Be aware that there will be the possibility for the peer-correcter
to use his or her own AI.
*May the best tron win :)*
Have fun and good luck.

Loading…
Cancel
Save