Git Product home page Git Product logo

ipoetdev / minitictactoe Goto Github PK

View Code? Open in Web Editor NEW
0.0 0.0 1.0 5.69 MB

MiniTicTacToe - NativeJS: UI - Client Controller - OOP Model/'Server'

Home Page: https://ipoetdev.github.io/MiniTicTacToe/

License: BSD 2-Clause "Simplified" License

JavaScript 74.30% HTML 9.81% CSS 15.89%
asynchronous classes-and-objects css3 ecmascript6 html javascript model-view-controller model-view-presenter proxy-objects tailwindcss

minitictactoe's Introduction

LinkedIn Badge wakatime  WakaTime

DITIGAL OPEN COFFEE

www: Roadmap.sh: Current progres and historical inventory of skills via step by step guides to Developer maturity

Current Progress Role Based Inventory Years Coverage
roadmap.sh
    DevOps 2019->Current 24% Covered
133 Total
    Backend 2018->Current
2003-2004
17% Covered
130 Total
    Full Stack 2023->Current 19% Covered
37 Total
    Frontend 2023->Current
2003-2004
57% Covered
123 Total

Computer Science (2001 - 2004) | Cyber Security (2005 -> Current) |

Current Progress Skill Based Inventory Years Coverage
roadmap.sh
    Javascript 2023->Current
2003-2004
56% Covered
118 Total
    Python
2023-> Current 34% Covered
50 Total
    Java,
Android
2019-2020
2001-2004,
4% Covered
52 Total
    Node.js> 2019-2020
2001-2004,
4% Covered
52 Total

Others: PHP: 2001-2004 | #C: 2001-2004 | CFML: 2001-2002

Workflows & Methods

  • 💬 Ask me about what I am ...
  • 🌱 I am currently employing ...
    • Workflow development i.e. Process & Flows
    • Project management i.e. Agile Boards, Kanban, Scrum
    • Agile & Scrum artefacts i.e. Epics, Features, Stories, User Acceptance
    • Test Strategies & Test Case Management i.e. Test Driven Development & Unit Testing
    • Configuration & Security: i.e. Infra, Settings & Secrets Management.
    • Deploy & Release: i.e. Cloud Hosting & App Publishing

Contact

  • 📫 How to reach me: ,
  • 😄 Pronouns: he/him/his/hij/il
  • ⚡ Fun fact: I climbed My Kenya, and I represented my country in sport, and I used to Dive

minitictactoe's People

Contributors

ipoetdev avatar

Watchers

 avatar

minitictactoe's Issues

BUG :: DRAW STATE is indeterminate: GameLogic._checkOutcome Conditionals

BUG: DRAW STATE is indeterminate: GameLogic._checkOutcome Conditionals

Label the Bug | Component| Feature Name: | File Name | Line No:

Related

Bug Outline

A clear and concise description of what the bug is.

I can declare a winner
I can not determine a draw state based on current logic and checks in GameLogic._checkOutcome() method
I am seeing that when there is checkSequenceWin === False, then Next Turn is always returned.
Further debugging is needed, however time is pressing for deadline submission in 15 hours.

Bug Behaviour

Reproducibility

  • Is It: Yes

Steps to reproduce :

  1. Go to 'Game Board'

  2. Click on ' The following sequenece' is technicall and visually a draw
    Cell 1: O
    Cell 3: X
    Cell 4: O:
    Cell: 7: X
    Cell 6: O
    Cell 2: X
    Cell 8: O
    Cell 5: X

  3. Scroll down to '....'

  4. See error in screenshot and Mermaid Flowchart

BUG - DRAW STATE is indeterminate GameLogic_checkOutcome

flowchart TD
    H2([sequence in\n WIn Combination])
    H2-->I(CheckOutcome)
    I-->|TRUE, FALSE|I0{CheckSequenceWin}
    I0-->|TRUE: :IF &&\n ! IFDRAW|I1([checkSequence: Streak, Sequnece])
    I0-->J(CheckSequnceWin)
    I0-->|FALSE: IF NOT &&\n ! IFDRAW|I2([checkSequence: Streak, Sequnece])
    J-->|foundWinner|J0(FoundWinnder \n Array.from, every, includes )
    J0-->|True, False|J
    I1-->K1{hasDrawn: TotalTurns}
    I2-->K2{hasDrawn: TotalTurns}
    K1-->|true|K11(Has Draw)
    K1-->|true|K12(Win Outcome)
    K2-->|true|K22(Has Draw)
    K2-->|true|K23(In Play)
    K11-->k3(Outcome)
    K12-->k3(Outcome)
    K22-->k3(Outcome)
    K23-->k3(Outcome)
    k3-->|Game Status \n OUTCOME|I
    I-->H2

Code & Logging: The following are not being reached. in ``GameLogic._checkOutcome`

// K11
outcome = __logOutcome('5.1.3.1.A1', '🏁🟰', `Game Drawn after maximum turns reached`, this.IFDRAWN);
//K22
outcome = __logOutcome('5.1.3.1.A2', '🏁🟰', `Game Drawn after maximum turns reached`, this.IFDRAWN);

Expected

User Acceptance Criteria is a format to derive consistent expectations

Scenario:

Given: The sequnces for a draw state
(And:) the TotalCount of Turns is greater or equal to 8

When: I click on the 8th or 9th grid cell
(And:): I complete the game

Then: I should declare a draw 
(And:) This should be announce
NOT: declare a Winner or
OR NOT: declare Next Turn

Copy to Readme || Source:

Visuals

Add screenshots if needed

Game UI
image

Logging in DevTools
image

Additional context**

Add any other context about the problem here.

BUG ::

BUG:

Label the Bug | Component| Feature Name: | File Name | Line No:
Triage:

Related

Bug Outline

A clear and concise description of what the bug is.


Bug Behaviour

Reproducibility

  • Is It: Yes | No

Steps to reproduce :

  1. Go to '...'
  2. Click on '....'
  3. Scroll down to '....'
  4. See error

Expected

User Acceptance Criteria is a format to derive consistent expectations

Scenario:

Given:
(And:)

When:
(And:)

Then:
(And:)

Copy to Readme || Source:

Visuals

Add screenshots if needed

Architecture: Code to UI: AlpineJS

image

Script Tags: Load Order

image

Environment

OS:

  • OS:

Developer Environment: IDE & Deployment

  • IDE:
  • Environment: Local | Cloud | Hosted:
  • Language:
  • Dependencies (Versions}:
  • Phase: Build | Pre Commit | CI Actions | Remote Build | Remote Deploy

Browsers: Mobile | Desktop

  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Platform: Model, Manufacturer, Ownership

  • Desktop | Mobile |
  • Personal Own | Business Own

Network: Firewall or Cloud Filters may

  • Wired @Home | Wireless @Home | Wireless @Mobile
  • Security: Network Firewall or Network Filters or Block Lists

Additional context**

Add any other context about the problem here.

[CODE]: AlpineJS - Architecture & Interactivity: Version 2.1

CODE:

Outline | Architecture | Modality:
Version: 2.1
Components:

  1. HTML: Page structure
  2. AlpineJS CDN:
  3. Internal Script :

File Name game.html


Related

DESIGN: Code to UI

image
click to view/enlarge

CODE Outline: Web User Interface.

A clear and concise description of what the Interactive CODE does

  1. HTML Page Outline:
    • Central Page Div: Game Grid Board
    • Core App Component (AlpineJS x-data directive)
  2. HTML Template with div, buttons and span (Design)
    • 1st Template: 1st Row: Grid 1 to 3 or array slice(0,3)
    • 2nd Template: 2nd Row: Grid 4 to 6 or array slice(3,6)
    • 3rd Template: 3rd Row: Grid 7 to 9 or array slice(6,9)
  3. HTML Button per each Grid Cell (Design)
    • Each Grid Cell (Loop iteration) is a generated button for interactive
      • @click.prevent for each user interactive on game's select() function.
      • $nextTick: used to wait for DOM to be updated.
    • Each Button as dynamic/shown text content when clicked and content is not null.
  4. AlpineJS Directives for Templates
    • x-data: The x-data directive is used to create a new instance of the gameApp object;
      • using an assignment to a local variable.
    • x-for: The x-for directive is used to create, via grid array loops, & display the game grid's first row.
      • :key
    • @click.prevent: Event Handler: On Click
      • @click or x-on:click:
        • @ is shorthand for x-on directive
      • .prevent: Prevents the default behaviour of click when template/button renders.
      • Net effect defers behaviour until clicked by user i.e. when selected, fires select and updates DOM.
    • $nextTick:
      • $nextTick: used to wait for DOM to be updated before updating the grid array with the selected item.
      • Combined with Alpine.reactive() on the game.grid array in the App class.
  5. AlpineJS Directives for Template's Grid Cell Span/Text Content.
    • x-show: The x-show directive: showing the game.grid[index] value when the grid is not null.
    • x-text: The x-text directive: displaying the game.grid[index] value when the grid is not null.
      • Default: x-text is assigned on template / #doc-fragment rendering - on initial page rendering.
      • x-text does not automatically DOM update.
    • x-transition: The x-transition directive is used to animate the display of the value of the item in the grid array.
      • :enter: Initialised transition state for CSS animation for duration and transition algorithm.
      • :enter-start: Start transition state for CSS animation, by transforming opacity (text) and scale of text.
      • :enter-end: End translate for CSS animation, by transforming opacity (text) and scale of text.
  6. AlpineJS Directives for Game Reset Button.
    • x-show: The x-show directive: showing the game.WON()||game.TURNS()>=9 value when the has won or game is __greater or less __ than or equal to 9 turns.
    • x-transition: The x-transition directive is used to animate the display of the value of the item in the grid array.
      • :enter: Initialised transition state for CSS animation for duration and transition algorithm.
      • :enter-start: Start transition state for CSS animation, by transforming opacity (text) and scale of text.
      • :enter-end: End transtate for CSS animation, by transforming opacity (text) and scale of text.
  7. AlpineJS Directives for Game Reset Button's Span/Text Content.
    • x-text: The x-text directive: displaying the game.WON()?'Winner!':'😔' value on a ternary operation: .
      • Default: x-text: game.WON() checks if the game is won or no result ('😔').
      • x-text does not automatically DOM update.

Script/Page Loading

1: Alpine JS loads on defer for page rendering and is located at end of Page: game.html
2: App class is defined in an internal script tag
3: App class is then instantiated and called by an local API function: gameApp()

image

1st AlpineJS by CDN: 1st JS script and Deferred

  • Loads AlpineJS framework.
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js"></script>
  • Located: End of Page
  • Deferred

Next/2nd: In Page Script: App Class

See CODE: Game Design/Function

Last/3rd In Page Script: gameApp Function as the app API.

  • Instantiates App class
  • Acts as the game's API or application public interface between web UI and the App class logic.
  • Log the console for debugging
  • Returns the gameApp's App class's instance properties (GET) and 1 public method.
    • GRID(): Returns the app's private _grid property.
    • WON(): Returns if the end state won: true or false.
    • TURNS(): Returns the private count (number) of turns to check against.
    • Select(): Returns the public select() method for user interaction on-click event handling.

CODE FLOW: Language Concerns

  1. Scripts load order
  2. Scripts load on defer image on page and script loading.
  3. Script and AlpineJS component initialisation: x-data and child DOM/HTML Elements of that x-data component
  4. Window objects and Objects not defined on access / scope / page initialisation. ⬅️⬅️⬅️ Current Bug: #6
  5. HTML Template/Document fragment loading on page rendering
  6. Array.slice() that uses slicing and arrays are normally index of n-1.
    i. 1st Row: ...... 0-3 (n=4) i.e. 0,1,2,3
    ii. 2nd Row: ... 3-6 (n=4) i.e. 3,4,5,6
    iii. 3rd Row: ... 6-9 (n=4) i.e. 6,7,8,9
  7. Event Handling linked to each dynamic button for user on click events
  8. Defering, by .prevent, default On-Click firing
  9. $nextTick for DOM updating on static elements and ...
  10. Adding Vue/AlpineJS reactivity further down into the code stack using Alpine.reactive() to reflect dyanmic changes to the grid's array and thus content. This is a alternative to updating x-text that is statically rendered on page load.
  11. Show/Hiding text based on outcome of user click
  12. Animating the text's CSS properties and style
  13. Updating the reset button based on

Concepts

  • Page Loading and Script Access
  • Windows Objects and Scope
  • Document Fragment Rendering
  • Slicing Array in Loops
  • Event Handling and User Interactions
  • Event deferral on page loading.
  • DOM updating and Reactivity
  • Text/Content Animation

Questions: Version 2.1

  1. What is the correct way to load scripts when a JS framework is loaded by CDN when a local and external interact?
  2. How can a local script tag access a function from that external CDN script as a component or import or module i.e. Alpine.reactive()
    i . Global scope: component
    ii. Import statement
    iii. As a module
  3. How does accessing windows objects and variables work when assigning ...:
    i. Alpine to windows i.e. windows.alpine = Alpine (is this correct?)
    ii. gameApp to windows i.e. windows.game = gameApp
  4. Should I use the let game = gameApp be defined, in local script tag as a global variable?
  5. Should window.tictactoe = game as well?

Questions: Version 2.2

  1. How do I port the app class to an external script class: https://github.com/iPoetDev/AlpineTicTacToe/blob/fix/game-app-001/docs/assets/js/game.js

Sources

1:
2:
3:
4:
5:
6:
7:
8:

[CODE] App Class API: GameLogic()

CODE:

Outline | Architecture | Modality: OOO JavaScript: Class
Version: 2.2
Components:

  1. HTML: Script tag
  2. JS: Internal Script :
    verses
  3. JS: External User Custom Code

File Name
2: game.html or game.js


Related


Previous

See #8 for a prior Class definition

Design: GameLogic() class.

The class GameLogic here is responsible for maintaining the state and rules of a game.

Decisions

  • Changed class name: from App() to GameLogic()
  • Use static getters as Class level constants: immutable and encapsulated.
    • Getter naming declares purpose/use. Still publicly accessible.
    • Uses the ClassName.CONSTANT_NAME as declaration.
    • Encapsulation via Getters/Setters allow for class/instance members (data) to be hidden (by convention) for state management/testing.
  • Assign Class data members values from static GameLogic.CONTANT_NAME
    • To the constructor() and reset() primarily,
    • And elsewhere where raw string/number/array values are declared in functions.
  • Use of underscore, by convention, to demarcate private access to class properties.

Changelog

  • Changelog
    • Added:
    • Modified:
    • Removed:
    • Obsolete:

Class API

  • Using JavaScript version ES6 class syntax and features.
  • Not using explicitly defined class members, instead, using per instance members, on class to instance constructor use: i.e. this._member

1. Constructor

The constructor function gets called whenever a new instance of the class GameLogic is created.
It initializes the game's state, such as the number of turns played, the winning status, the win sequences, the game grid, etc.

2. Class | Instance Members

  • Class data, as initial and end state and interpolated values.
  • Protected, by private access and encapsulated, using _ as convention, not by syntax.
  • Get/Set by Class properties accessors methods (ES6 JavaScript).
Class Member Access/Use Get/Set Type Default Class Constant Value Use Case
this.DEBUG Private, Internal ✅/✅ {boolean} false n/a {false, true} Enabled/disables class debug mode
this.LEVEL Private, Internal ✅/✅ {number} 0 n/a {0,1,2,3,4,5,6} Toggles level/type of console logging to DevTools
this._EMPTYCELL Private, Internal ✅/✅ {null} null GameLogic.CELL_RESET null Default, named placeholder for null, an empty cell
this._turns Private/As-Is Data ✅/❌ {number} 0 GameLogic.TURN_INIT n<=8 The sequences of grid indexes that are winning combinations of moves for a 3x3 UI grid
this._currentcell Private/As-Is Data ✅/✅ {string} , empty string GameLogic.CELL_RESET , X,O Value of the current grid's cell, by reference
this._draw Private/End State ✅/✅ {boolean} false GameLogic.NO_DRAW {false, true} Draw is a valid end state/game result
this._won Private/End State ✅/✅ {boolean, string} false GameLogic.IN_PLAY {false,X,O} Win is valid end state/game result
this._winSeq Private/Check Conditional ✅/❌ {array} Array(8)<string> GameLogic.WIN_COMBINATIONS GameLogic.WIN_COMBINATIONS Set of Winning Combinations of 3 length string sequences, i.e. combination of winning moves/sequences
this._grid Private/UI Control ✅/✅ {array} Array(9)<null|string> GameLogic.NEW_GRID null,X,O Data structure that generates the game board/grid and contains the game moves as string tokens
this._xChars Private/UI Tokens - {array} Array(2)<string> n/a [x,X] Character by lower\upper-case, variability in UI design for X Token
this._oChars Private/UI Tokens - {array} Array(2)<string> n/a [o,O] Character by lower\upper-case, variability in UI design for O Token
this._xTurns Private/Internal ✅/❌ {string} ._xTurn GameLogic.X_TURN_PROP .xTurn Property to select each X turn and update the grid by
this._oTurns Private/Internal ✅/❌ {string} ._oTurn GameLogic.O_TURN_PROP .oTurn Property to select each O turn and update the grid by

3. Class Properties (Getters/Setters)

Extensive Use Cases

  • Getters/Setters to manage

    • ✅ Class / Instance Value Initialisation & Reset
    • ❓ Getters/Setters for Value/Type Integrity, Error Control and Exceptional handling.
    • ✅ Class/Function/Parameter Default Values
    • ✅ Class Constants as Immutable/Static Default Raw Values
    • ✅ Class Member/Current Data inspection of As-Is values for testing/logging/debugging
    • ✅ Game State:
      • ✅ Game Initial State: Current and Next Turn.
      • ✅ Game End State: Curren and End
        • 🚧 Draw
        • ✅ P1 Wins
        • ✅ P2 Wins
  • Class Constants give name/use case to the raw values explicitly, and not implicit meaning, use or context.

Property Use Case Class Member Value/Type Getter Setter Access Mutable Intent
this.DEVMODE Instance Property,
Default Value
this.DEBUG - Public Gets/Sets the class debug state, affects the default value for per function optional param: debug
this.LOGLEVEL Instance Property,
Default Value
this.LEVEL - Public Gets/Sets the class VERBOSITY level for console, affects the default value for per function optional param: level
:--- :--- :--- :--- :--- :--- :--- :--- :---
GameLogic.P1 Encapsulated Class Constant,
Default Value
-- {string} Public 🛑 Gets the immutable Player 1 token: X
GameLogic.P2 Encapsulated Class Constant,
Default Value
-- {string} Public 🛑 Gets the immutable Player 2 token: O
GameLogic.P1_TOKENS Encapsulated Class Constant this._xChars {array} Private 🛑 Gets array of Player 1 token characters: X or x: A design choice for variability in token font/display based on font choice
GameLogic.P2_TOKENS Encapsulated Class Constant this._oChars {array} Private 🛑 Gets array of Player 2 token: O or o
GameLogic.REG_SEARCH_LENGTH Encapsulated Class Constant,
Default Value
-- {number} Private 🛑 Gets regex length of win sequence to search : 3
GameLogic.REG_SEARCH_ALL Encapsulated Class Constant,
Default Value
-- {string} Private 🛑 Gets global regex flag to search for all instances, not just first found: g
GameLogic.REG_SEARCH_FILTER Encapsulated Class Constant,
Default Value
-- {string} Private 🛑 Gets string to replace & filter out of the regex search on test: ``
GameLogic.WIN_COMBINATIONS Encapsulated Class Constant,
Default Value
-- {array(8)<strings(length:3)>} Private Gets the possible (8) winning combinations/permutations as array of (8) strings
GameLogic.AS_DRAW Encapsulated Class Constant,
Default Value
-- {string} Public Gets game's (end) state for a draw (has a result/end state), which is a truthy logic: true
GameLogic.IN_PLAY Encapsulated Class Constant,
Default Value
-- {string} Public Gets game's state for in play (no result/end state), which is a falsey logic: false
GameLoic.CELL_RESET Encapsulated Class Constant,
Default Value
-- {null} Private Gets default cell value for grid array initialisation/reset: null
GameLogic.MAX_LENGTH Encapsulated Class Constant,
Default Value
-- {null} Private Gets the default grid array's length/size: 9
GameLogic.TURN_RESET Encapsulated Class Constant,
Default Value
-- {string} Private Gets default turn value for initialisation/reset: ' '
GameLogic.TURN_INIT Encapsulated Class Constant,
Default Value
-- {string} Private Gets default turn init value for the number of turns per game, as this increments: ' '
GameLogic.X_TURNS_PROP Encapsulated Class Constant,
Default Value
-- {string} Private Gets default turn property for X 's turn tracking: '._xTurns'
GameLogic.O_TURNS_PROP Encapsulated Class Constant,
Default Value
-- {string} Private Gets default turn property for O's turn tracking : '._oTurns'
:--- :--- :--- :--- :--- :--- :--- :--- :---
this.WIN_SEQUENCE Instance Property,
As-Is Value
this._winSeq {string} Private Gets the current, static, Win Sequence array 's value
this.GRID Instance Property,
As-Is Value
this._grid {Array} Public Gets/sets the current grid's array
this.NEW_GRID Instance Property,
As-Is Value
this._grid new {Array} Private Gets/sets (initialises) the new grid's array of 9 length and null values: see MAX_LENGTH, CELL_RESET
this.IFWON Instance Property,
As-Is Value
this._won {boolean|string} Public Gets/sets the game's current/end win state, and winner. Initialsed as: false, per turn is false for next round, or ends as: X v O
this.IFDRAWN Instance Property,
As-Is Value
this._draw {boolean} Public Gets/sets the game's current/end drawn state. Initialised as: false, end state: true
this.TURNS Instance Property,
As-Is Value
this._turns {string} Public Gets the current, static, Win Sequence array's value
this.MAX_TURNS Instance Property,
As-Is Value
--- {number} Private Computes the max number of turns from grid length: MAX_LENGTH - 1
this.CELL Instance Property,
As-Is Value
--- {string|null} Private Gets/sets the current cell reference to grid value: X or O
:--- :--- :--- :--- :--- :--- :--- :--- :---
this.currentCell(index) Accessor/helper API function,
Computed Value
--- {string} Public Sets/returns the class member for current cell by retrieves the current cell value from the grid at index: *
:--- :--- :--- :--- :--- :--- :--- :--- :---

4. Class General Helpers/Debug

  • Helpers to debug the ES6 class in browser
  • Possible future interface to for logging, inspection, and debugging
Function Use Case Parameters Defaults Return Access Intent
SELECT() API Helpers --- --- --- --- --- ---
this._console Logging & debugging message, debug, level, ...args debug, level {void} Public/Protected Log function calls for class level debugging/inspection

5. select() and Helper Functions

Function Callable by Key Parameters Defaults Inner Closures Return Access Intent
SELECT() API Helpers --- --- --- --- --- --- ---
_getRandomCharacter _updateTurnsAndGrid characterArray debug, level _getRandomIndex array(index Private Returns a random character from the given character array.
_updateTurnsAndGrid _isEvenTurn index, characterArray, turnProperty debug, level n/a {void} Private Update the turns and Cell (grid at the specified index)
_isInvalidMove select index debug, level n/a {boolean} Private Checks if the given move is invalid
_isEvenTurn select index debug, level _isTurnEven
_chooseChar
_selectProp
{void} Private Determines if the turn is even or odd and calls to update the turns and grid accordingly
_incrementTurn select n/a n/a n/a {void} Private Increment Turn property (a counter) by 1
select UI index debug, level _whoWins,
_whoDraws,
_hasValidMove,
Object <anon><br> cell, next || endstate, message, state, valid, outcome Public Selects a cell, by index, on the game board, and updates the game state, checks the end game state, returns a game state object to pass to other APIs/UI calls.

6.1 checkWinner and Helper Functions

  • IFDRAWN state checks 🚧
Function Callable by Key Parameters Defaults Inner Closures Return Access Intent
checkWinner() API Helpers --- --- --- --- --- --- ---
_checkSequenceWin checkWinner turns, sequence debug, level n/a {boolean} Private Checks whether the sequence of turns is a winner.
checkWinner n/a n/a debug, level n/a {boolean|string} Public Checks if the sequence of turns, assigns a winner, AND end state flag: False, Draw (True), P1, P2.

6.2 checkWinningPlay and Helper Functions

  • IFDRAWN state checks 🚧
Function Callable by Key Parameters Defaults Inner Closures Return Access Intent
checkWinninPlay() API Helpers --- --- --- --- --- --- ---
_checkSequenceWin checkWinner turns, sequence debug, level n/a {boolean} Private Checks whether the sequence of turns is a winner.
checkWinninPlay n/a n/a debug, level _assignWinner
_defaultRound
...
{boolean|string} Public Checks if the sequence of turns, assigns a winner or a draw, AND end state flag: False, Draw (True), P1, P2.

7. reset

Function Callable by Key Parameters Defaults Inner Closures Return Access Intent
reset UI n/a default, index n/a {void} Public Reset the game state to its initial values using Class Constants (properties).

[CODE] :: App Class Re-Architected from External Origin: An API Reference & Guide

CODE:

Outline | Architecture | Modality: OOO JavaScript: Class
Version: 2.1
Components:

  1. HTML: Script tag
  2. JS: Internal Script :
    verses
  3. JS: External User Custom Code

File Name
2: game.html or game.js


Related


NOTICE: External Original Code as Source

This code's orignal author is: Scott Windon: GitHub
This code's version 1.0 was found on a CodePen: here
This code was refactored and improve upon to port it from version 1.0 to a version 2.0

Decision: Why Reuse

  • This project is a resubmission.
  • Decided to rearchitect the original Code base from 1st submission.
  • 1st attempt was over designed and was influenced by a old approach of code, and was not modern.
  • So starting off with a simple working code and logic model was a choice.

Why is this different

  • This is different and improved as this code based is refactored and demonstrates my refactoring skills.
  • Uses a Object Orrinetated / Class based approach over a functional approach.
  • Compoent methods have been refactored in to more efficient, more readable and lower congitive complex method length/signatures.

Original Code

function app() {
    return {
        turns: 0,
        won: false,
        winSeq: ['012','345','678','036','147','258','048','246'],
        grid: Array.apply(null, Array(9)).map(function (v,i) { return null}),
        xChars: ['x','X'],
        oChars: ['o','O'],
        xTurns: '',
        oTurns: '',
        select: function(index) {
            if(this.won || this.grid[index] !== null || this.turns>=9) return;
            this.turns++;
            if( this.turns % 2 == 0 ) {
                this.grid[index] = this.xChars[Math.floor(Math.random() * this.xChars.length)];
                this.xTurns += index;
            } else {
                this.grid[index] = this.oChars[Math.floor(Math.random() * this.oChars.length)];
                this.oTurns += index;
            }
            this.checkWinner();
        },
        checkWinner: function() {
            for(let i = 0, length = this.winSeq.length; i < length; i++){
                if( new RegExp(`[${this.winSeq[i]}]{3}`).test(this.xTurns.replace(new RegExp(`[^${this.winSeq[i]}]+`,'g'),'')) ) {
                    this.won = 'X';
                    break;
                } else if( new RegExp(`[${this.winSeq[i]}]{3}`).test(this.oTurns.replace(new RegExp(`[^${this.winSeq[i]}]+`,'g'),'')) ) {
                    this.won = 'O';
                    break;
                }
            }
            return this.won;
        },
        reset: function() {
            this.turns = 0;
            this.won = false;
            this.grid = Array.apply(null, Array(9)).map(function (v,i) { return null});
            this.xTurns = '';
            this.oTurns = '';
        }
    }
}

New Code:

Design

  • Defined as a Class
  • Uses a constructor() method initialise object/instance's state and private members.
  • Has private members: this._member has an underscore by convention.
  • Uses static Getters for accessing private and public members as class API methods.
    • Setter methods would be used if values need to be changed as an input into the class/instance state.
    • These static Properties (Get/Set) as the class API for class state/behaviours.
  • Public Methods are limited to those methods that relate to
    • User Interaction
    • User Feedback
    • User Action
  • Private Methods are internal class methods, refactored mostly from more complex original ones from original source. These have a underscore before the name by convention.

Retain Concepts

Syntactical sugar from source code retained.

  • Use of RegEx for Global Sequence (g) matching for each turn.

Code Use Improvements

Original New Notes
grid: Array.apply(null, Array(9)).map(function (v,i) { return null}) this._grid = Alpine.reactive(new Array(App.MAX_LENGTH).fill(null)) // ES6 Arrow Function 1: Apline.reactive
Fill an array over map(function) and return null for initialising an array.
--- --- ---
this.won = 'X'; this._won = App.P1 Remove hard coded values
Use of App.P1 | App.P2 for instance/class method getters of class properties (X or O)

|

API

  • Uses JSDoc to
    1. Define the API documentation from Automatic build and CLI scripts
    2. Define static typing for JavaScript (as JS is dynamically typed_ that is compatible with Typescript
    3. Well documented code as a technical writing practice + skillset.

Properties: Get/Set

class App {

    /**
     * Gets the Player 1 Token.
     * @returns {string} The name of the property for turns.
     * @static
     */
    static get P1 () {
        return 'X'
    }

    /**
     * Gets the Player 1 Token.
     * @returns {string} The name of the property for turns.
     * @static
     */
    // noinspection FunctionNamingConventionJS
    static get P2 () {
        return 'O'
    }

    /**
     * Gets the name of the property for keeping track off the number of X turns.
     * @returns {string} The name of the property for X turns.
     * @static
     */
    // noinspection FunctionNamingConventionJS
    static get X_TURNS_PROP () {
        return 'xTurns'
    }

    /**
     * Gets the name of the property for keeping track of the number of O turns.
     * @returns {string} The name of the property for O turns.
     * @static
     */
    // noinspection FunctionNamingConventionJS
    static get O_TURNS_PROP () {
        return '._oTurns'
    }

    /**
     * Returns the maximum (array/grid cell) length allowed for the grid
     * @returns {number} The maximum length allowed.
     * @static
     */
    // noinspection FunctionNamingConventionJS
    static get MAX_LENGTH () {
        return 9
    }

    /**
     * Returns the initialisation value for turns.
     * @returns {number} The value for TURN_INIT.
     * @static
     */
    // noinspection FunctionNamingConventionJS
    static get TURN_INIT () {
        return 0
    }

    /**
     * Returns the initialisation value for turns.
     * @returns {array} The value for private _grid.
     */
   get GRID () {
        return this._grid
    }

    /**
     * Returns the initialisation value for turns.
     * @returns {boolean} The value for private _won.
     */
   get WON () {
        return this._won
    }

    /**
     * Returns the initialisation value for turns.
     * @returns {number} The value for current private _turns.
     */
   get TURNS () {
        return this._turns
    }
....
}

App Class & Constructor

class App {
...
   /**
     * Constructor for the App class.
     * Initializes the App object with default values and reactivity, for
     * - turns,
     * - win state,
     * - win sequences,
     * - game grid,
     * - x characters | o characters.
     * The constructor also logs the instantiation of the ...
     *      App object to the console.
     * @url https://alpinejs.dev/advanced/reactivity
     * @constructor
     */
    // noinspection FunctionNamingConventionJS
    constructor () {
        //
        console.log('Instantiate App:', this) // jshint ignore:line
        /** @access private */
        this._turns = App.TURN_INIT
        /** @access private */
        this._won = false // The default check win state of the game
        /** @access private */
        this.winSeq = [
            '012',
            '345',
            '678', // HorizontalWins
            '036',
            '147', // DiagonalWins
            '258',
            '048',
            '246',
        ] // VerticalWins
        // noinspection JSUnresolvedReference, ChainedFunctionCallJS, NestedFunctionCallJS
        /** @access public */
        this._grid = Alpine.reactive(new Array(App.MAX_LENGTH).fill(null)) // ES6 Arrow Function
        // noinspection JSUnresolvedReference
        /** @access public */
        this._xChars = Alpine.reactive(['x', 'X'])
        // noinspection JSUnresolvedReference
        /** @access public */
        this._oChars = Alpine.reactive(['o', 'O'])
        /** @access public */
        this._xTurns = ''
        /** @access public */
        this._oTurns = ''
    }
....
}

App Public Methods

App.select()

  • Calls: _isInvalidMove (index)
  • Calls: _isEvenTurn (index)
  • Calls: checkWinner(index) for every turn and sets boolean value.
class App {
...
    /**
     * Selects a cell, bu index, on the game board.
     * @function select
     * @param {number} index - The index of the cell to be selected.
     * @returns {string} - Game Token for X || Y.
     * @desc  It logs the selected index, checks if the move is invalid, and returns the game token for X or Y.
     * If the move is invalid, it logs an error message and returns the current item from the grid. //
     * If the move is valid, it updates the turns and grid, checks if the game has been won, logs a success message,
     * and returns the updated item/token from the grid.
     * */
    select (index) {
        console.log('The selected index:', index) // jshint ignore:line
        // Check if move is invalid, and proceed to next turn if the return is false,
        if (this._isInvalidMove(index)) {
            //
            console.log('Invalid Move: ', index) // jshint ignore:line
            return this._grid[index]
        } else {
            // Update the turns and grid
            this._turns += 1
            this._isEvenTurn(index)
            // Check if the game has been won
            const isWinner = this.checkWinner()
            // Return the updated item from grid
            console.log('Valid Move: ', this._turns, this._grid[index], isWinner) // jshint ignore:line
            return this._grid[index] // @Update 23/12/06 to return updated item/token from grid
        }
    }

App.checkWinner()

  • Calls: _checkSequenceWin(turn, sequence) for either P1 or P2 properties per turn.
   /**
     * Checks if the sequence of turns is a winner.
     * @function checkWinner
     * @returns {boolean} True if the sequence of turns is a winner.
     * @desc checks for a winner in a game. It iterates over a list of winning sequences and calls the
     * _checkSequenceWin method to check if either player has won.
     * If Player 1 has won, the won variable is set to App.P1 (indicating Player 1's victory).
     * If Player 2 has won, the won variable is set to App.P2 (indicating Player 2's victory).
     * The method returns the value of the won variable, indicating the winner of the game.
     * */

    checkWinner () {
        for (const sequence of this.winSeq) {
            if (this._checkSequenceWin(this._xTurns, sequence)) {
                this._won = App.P1 // Player 1 wins | X wins
                break
            } else if (this._checkSequenceWin(this._oTurns, sequence)) {
                this._won = App.P2 // Player 2 wins | O wins
                break
            }
        }
        return this._won
    }

App.reset()

        /**
     * Reset the game state to its initial values.
     * @function reset
     * @return {void}
     * @desc that resets the game state to its initial values.
     * - Resets turns to a predefined value
     * - Reset won to false.
     * - Reset grid to an empty array.
     * - Reset _xTurns and _oTurns to empty strings.
     * */
    reset () {
        this._turns = App.TURN_INIT
        this._won = false
        // noinspection ChainedFunctionCallJS,NestedFunctionCallJS,JSUnresolvedReference
        this._grid = Alpine.reactive(new Array(App.MAX_LENGTH).fill(null))
        this._xTurns = ''
        this._oTurns = ''
    }
...
}

App Private Methods

_getRandonCharacter(characterArray)

  • Used/Called by: _updayeTurnsAndGrid()
    /**
     * Returns a random character from the given character array.
     * @design
     *  This randomiser allows variation of the game token sizes (lower case/upper case).
     *  It mimics variation in hand strokes when hand drawing the game tokens.
     * @function _getRandomCharacter
     * @param {Array} characterArray - The array containing characters.
     * @return {string} - A random character from the character array.
     */
    _getRandomCharacter (characterArray) {
        // noinspection LocalVariableNamingConventionJS,NestedFunctionCallJS
        /** An inner function/closure for generating a random index. Improve maintainability/readability.
         * @function {arrow function} _getRandomIndex
         * @param {number} arrayLength
         */
        const _getRandomIndex = arrayLength => Math.floor(Math.random() * arrayLength)
        const index = _getRandomIndex(characterArray.length)
        return characterArray[index]
    }

_updateTurnsAndGrid (index, characterArray, turnProperty)

  • Calls: _getRandomCharacter
    /**
     * Update the turns and grid at the specified index
     * with a random character from the characterArray.
     * @function _updateTurnsAndGrid
     * @param {number} index - The index of the grid to update.
     * @param {Array} characterArray - The array of characters to choose from.
     * @param {string} turnProperty - The property to update the turns with.
     * @return {undefined}
     * @desc It takes in three parameters: index, characterArray, and turnProperty.
     * It updates the grid array at the specified index with a random character from the characterArray and ...
     * increments the value of the turnProperty by index.
     */
    _updateTurnsAndGrid (index, characterArray, turnProperty) {
        this._grid[index] = this._getRandomCharacter(characterArray)
        this[turnProperty] += index
    }

_isInvalidMove (index)

  • Used/Called by: select()
    /**
     * Determines if the turn is even and updates the turns and grid accordingly.
     * @function _isEvenTurn
     * @param {number} index - The index of the grid to be updated.
     * @return {undefined}
     * @desc determines if the turn is even and updates the turns and grid accordingly.
     * It takes an input parameter index which represents the index of the grid to be updated.
     * It calculates whether the turn is even by checking if the remainder of the division of `this._turns`
     * by 2 is equal to 0. If the turn is even, it assigns certain values to variables char and prop.
     */
    _isEvenTurn (index) {
        const isEvenTurn = this._turns % 2 === 0
        // noinspection ConditionalExpressionJS
        const char = isEvenTurn ? this._xChars : this._oChars
        // noinspection ConditionalExpressionJS
        const prop = isEvenTurn ? App.X_TURNS_PROP : App.O_TURNS_PROP
        this._updateTurnsAndGrid(index, char, prop)
        console.log('isEvenTurn:', isEvenTurn, char, prop, index, this._grid[index]) // jshint ignore:line
    }

_isEvenTurn (index)

  • Used/Called by: select()
  • Calls: _updateTurnsAndGrid(index, char, prop)
     /**
     * Determines if the turn is even and updates the turns and grid accordingly.
     * @function _isEvenTurn
     * @param {number} index - The index of the grid to be updated.
     * @return {undefined}
     * @desc determines if the turn is even and updates the turns and grid accordingly.
     * It takes an input parameter index which represents the index of the grid to be updated.
     * It calculates whether the turn is even by checking if the remainder of the division of `this._turns`
     * by 2 is equal to 0. If the turn is even, it assigns certain values to variables char and prop.
     */
    _isEvenTurn (index) {
        const isEvenTurn = this._turns % 2 === 0
        // noinspection ConditionalExpressionJS
        const char = isEvenTurn ? this._xChars : this._oChars
        // noinspection ConditionalExpressionJS
        const prop = isEvenTurn ? App.X_TURNS_PROP : App.O_TURNS_PROP
        this._updateTurnsAndGrid(index, char, prop)
        console.log('isEvenTurn:', isEvenTurn, char, prop, index, this._grid[index]) // jshint ignore:line
    }

_checkSequenceWin

  • Used/Called by: checkWinner()
   /**
     * Checks whether the sequence of turns is a winner.
     * @function _checkSequenceWin
     * @param {string} turns - The sequence of turns.
     * @param {string} sequence - The sequence of turns to check.
     * @returns {boolean}
     * @private
     * @desc It takes in two parameters: turns and sequence.  Inside the function, it creates a regular expression
     * using the sequence parameter. This regular expression matches any three characters in the sequence string.
     * Then, it uses the created regular expression to filter out any characters in the turns string that are not
     * present in the sequence string. The filtered string is stored in the filteredTurns variable.
     * Finally, the function returns true if the filtered string contains a sequence of three consecutive characters
     * that match the sequence string, and false otherwise.
     * @credit Scott Window for use of RegExp constructor, regex pattern and RegExp.test() methods.
     * */
    _checkSequenceWin (turns, sequence) {
        const sequenceRegExp = new RegExp(`[${sequence}]{3}`)
        const searchRegExp = new RegExp(`[^${sequence}]+`, 'g')
        const filteredTurns = turns.replace(searchRegExp, '')
        return sequenceRegExp.test(filteredTurns)
    }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.