Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Frogger Game #287

Merged
merged 7 commits into from
Aug 26, 2022
Merged

Frogger Game #287

merged 7 commits into from
Aug 26, 2022

Conversation

crealime
Copy link
Contributor

Frogger Game

Demo |
Code base

The code is submitted in a dedicated feature branch.

Only code files are submitted.

Please, review.

@github-actions
Copy link

Hey!

Congratulations on your PR! 😎😎😎

Let's do some self-checks to fix most common issues and to make some improvements to the code before reviewers put their hands on the code.

Go through the requirements/most common mistakes listed/linked below and fix the code as appropriate.

If you have any questions to requirements/common mistakes feel free asking them here or in Students' chat.

When you genuinely believe you are done put a comment stating that you have completed self-checks and fixed code accordingly.

Also, be aware, that if you would silently ignore this recommendation, a mentor can think that you are still working on fixes. And your PR will not be reviewed. 😒

Frogger Arcade Game -- JS OO exercise check list

Relates to Object-Oriented JavaScript task.

Check-list - definition of done

  • employ ES6 features like const, let etc. (with exclusion of ES6 class syntax)

  • the code is very DRY

  • Requirements re Constants:

    • all numbers like block dimensions, initial locations are defined as named constants (e.g. const STEP = 101;) as otherwise numbers scattered across code base look cryptic; named constants add semantic meaning and improve readability
    • every number that has a semantic purpose (like those listed above) should be defined as constants; think of how your code reads - the closer to plain English the better
    • there are core constants and derived constants
      (e.g. derived constant const FIELD_WIDTH = BLOCK_WIDTH * BLOCKS_NUMBER;)
    • arrays of constants are also constants
      (e.g. const INITIAL_POSITIONS = [1,2,3,4].map(rowNumber => rowNumber * BLOCK_HEIGHT);)
    • const objects help organizing and structure const data even better
      (e.g. const PLAYER_CONF = { initialPosition: {x: 1, y: 5}, sprite: '...', ...etc... };
  • Requirements re OOP:

    • OO is implemented using JS prototype chain object model (not ES6 classes syntax)
    • classes do not refer to any global variables, like global variable player, which is an instance of Player class
      (referring to global constants and globals provided by the gaming platform like Resources is OK);
      Hint: pass Player instance as an argument to every enemy
    • Separation of Concerns principle is followed
      (e.g. update method does only rendering and doesn't contain any unrelated inline code; for example collision check is defined as a dedicated method and only called from inside update)
    • Nice To Have: properties common for some classes are generalized into a base class
      (e.g. there is Character base class, which is extended by Enemy and Player classes)
    • class extension is implemented using Subclass.prototype = Object.create(Superclass.prototype), not Subclass.prototype = new Superclass(params);; Helpful resource
  • Most common mistakes

    • Make sure target = condition ? valueWhenConditionTrue : valueWhenConditionFalse is used instead of condition ? target = valueWhenConditionTrue : target = valueWhenConditionFalse; Conditional (ternary) operator

Universal recommendations:

  • Give variables and functions meaningful names. Avoid generic names like item, element, key, object, array or their variations. Exception: helper functions that are specifically and intentionally designed to be multipurpose.
  • Function names should start with a verb as they denote actions; variables are normally nouns; boolean variables/functions start with is, does, has etc; variable containing multiple entities and functions returning lists contain entity name in plural form.
  • Have consistent code style and formatting. Employ Prettier to do all dirty work for you.
  • Use common sense or seek for an advice whenever requirements look ambiguous or unclear.

Also take a note of the requirements above and follow them in all your future projects.

By the way, you may proceed to the next task before this one is reviewed and merged.

Sincerely yours,
Submissions Kottachecker 😺

Copy link
Member

@OleksiyRudenko OleksiyRudenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@crealime pretty well done!

At times calculations look overcomplicated. Probably, operating with concepts of row and column (truly questionable as enemies do not jump across the columns) would make it simpler.
Pixels make sense only at image drawing phase and characters intersections.

Player and enemy collide when they are on the same row and player's x is within specific range based on enemy's x. Collision with gems can be done based solely on row/column match.

This code works. But as a peer developer I feel I gonna struggle with understanding and deducing a lot of maths involved.

Can we simplify it a bit?

// all computers.
this.x += this.speed * dt

if (this.x > 530) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this mysterious number - 530? As a peer developer I want to read and understand the code in order ro be able to contribute effectively.
This refers to this number and other numbers across the code that are not defined as constants.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to WIDTH_CANVAS.

Comment on lines 265 to 276
if (player.level === 1) {
setStars('images/Gem Blue.png')
}
else if (player.level === 2) {
setStars('images/Gem Green.png')
}
else if (player.level === 3) {
setStars('images/Gem Orange.png')
}
else {
setStars('images/Gem Red.png')
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider dictionary pattern here (use appropriate domain-specific naming).

const valuesDictionary = {
  1: 'a',
  2: 'b',
  3: 'c',
  4: 'd',
}

const defaultValueKey = 4

valuesDictionary[2] || valuesDictionary[defaultValueKey]

valuesDictionary[999] || valuesDictionary[defaultValueKey]

You most likely may also re-use the dictionary in the other fragments of code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! This is a good idea. Done.

Comment on lines 238 to 252
if (player.level === 1) {
player.level = 2
allEnemies = creteEnemies(allEnemies.length + 1)
setStars('images/Gem Green.png')
}
else if (player.level === 2) {
player.level = 3
allEnemies = creteEnemies(allEnemies.length + 1)
setStars('images/Gem Orange.png')
}
else if (player.level === 3) {
player.level = 4
allEnemies = creteEnemies(allEnemies.length + 1)
setStars('images/Gem Red.png')
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a peer developer I am tasked to add 5 more player levels.
Can we please have next state calculable based on current player level? I see the repetition pattern here.
See also comment on dictionary pattern below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


function creteEnemies(num) {
if (num > 6) num = 6
const arrEnemies = []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enemies part already says the variable contains many similar instances. Referring of data type is redundant and abbreviation (arr) makes it obscure anyway. There is a reason we do not write numY, numSpeed, strMessage, arrAllEnemies.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand. Fixed. And I change the functions creteEnemies and createStars so that they change the arrays allEnemies and allStars directly.

Comment on lines 181 to 188
for (let i = START_ENEMIES_Y_POSITION; i < num * Y_STEP; i += Y_STEP) {
let y = i
if (i > START_ENEMIES_Y_POSITION + Y_STEP * 2 && i <= START_ENEMIES_Y_POSITION + Y_STEP * 3)
y = INITIAL_ENEMIES_Y_POSITIONS[0]
else if (i > START_ENEMIES_Y_POSITION + Y_STEP * 3 && i <= START_ENEMIES_Y_POSITION + Y_STEP * 4)
y = INITIAL_ENEMIES_Y_POSITIONS[1]
else if (i > START_ENEMIES_Y_POSITION + Y_STEP * 4 && i <= START_ENEMIES_Y_POSITION + Y_STEP * 5)
y = INITIAL_ENEMIES_Y_POSITIONS[2]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we calculate pixel y positions based on something more comprehensible, e.g. playfield row number? Add row offset, multiply by row height?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@OleksiyRudenko OleksiyRudenko self-assigned this Aug 25, 2022
@OleksiyRudenko
Copy link
Member

Also please before submitting your next task make your code editor do indents with 2 spaces, not tabs.
Tabs expand in different ways on different environments. 8 spaces indentation at times makes reading and following code structure a bit painful.

@crealime
Copy link
Contributor Author

Thanks a lot for your review! Сode got really better. I think I redid everything you suggested. If I misunderstood something, please write to me.

Copy link
Member

@OleksiyRudenko OleksiyRudenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@crealime great job!
Check the comments below and take these as a source of learning and inspiration.

Comment on lines +193 to +196
let y = i
if (i === 4) y = 1
if (i === 5) y = 2
if (i === 6) y = 3
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use maths more often whenever there is any correlation.

Comment on lines +12 to +13
const START_PLAYER_X_POSITION = X_SIZE * START_COLUMN
const START_PLAYER_Y_POSITION = Y_SIZE * START_ROW - Y_OFFSET_PLAYER
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may want to group constants per their common features.
e.g.

const FIELD = {
  rows: 5,
  cols: 6,
}

const FIELD_PIXEL_BASED_PROPS = {
  width: 530,
  height: 999,
}

const FIELD = {
  PIXEL_BASED: {
   TILE: {
      width: 50,
      height: 110,
   }
  },
  ROW: {
    count: 7,
    start: 5,
  },
  COL: {
    count: 8,
    start: WHATEVER * 5,
  },
}

const FIELD.PIXEL_BASED.DIMENSIONS = {
  width: FIELD.PIXEL_BASED.TILE.width * FIELD.COL.count,
  height: // .similar calcs
};


Look, how reads full path FIELD.PIXEL_BASED.width. Practically, you replace some underscores with dots, yet the constants definition has some logical structure.
The above is a quick example (so naming is not well elaborated) to give you some ideas and illustrate how we can leverage nested data to put data into some logical structure yet keeping things readable.

Also it might be more reasonable to have essential numbers in a constant object different from a constant object that contains calculated constants.

@OleksiyRudenko OleksiyRudenko merged commit ea92ba0 into kottans:main Aug 26, 2022
@Mifaresss
Copy link
Contributor

The best frogger I've ever seen!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants