Skip to content

Latest commit

 

History

History
271 lines (163 loc) · 9.41 KB

File metadata and controls

271 lines (163 loc) · 9.41 KB

Let's get to the code already!

(this course assumes you have git, yarn, a text editor, and a command line available)

workbook 0 - blackjack

Agenda:

  • step 1: set up the dev environment
  • step 2: see something run in the browser
  • step 3: change some code
  • step 4: see the app change in the browser

git cheat sheet

git cheat sheet

step 1

Let's make our work directory for the course

$ mkdir -p ~/code/react-course
$ cd ~/code/react-course

and clone the first workbook

$ git clone https://github.com/nikfrank/react-course-workbook-0
$ cd react-course-workbook-0
$ yarn
$ npm start

we're done step 1... we should have a browser tab open to localhost:3000

step 2

go to the browser, see what's there! (that was easy)

step 3

open up our text editor to ./src/App.js

and write our first line of code

before:

  dealNextCard = ()=> 0 // noop

after:

  dealNextCard = ()=> this.setState(({ cards, handStatus })=> ({
    cards: (handStatus === 'hitting') ?
           cards.concat( newCard() ) :
           [ newCard(), newCard() ]
           
  }), ({
    hasAce = !!this.state.cards.find(({ rank })=> rank === 1),
    total = this.state.cards.reduce((p, { rank })=> p + Math.min(10, rank), 0),
    
  } = {})=> this.setState(({ cards })=> ({
         handStatus: (total > 21) ? 'bust' :
                     (total >= 17) ? 'standing' :
                     (hasAce && (total === 11)) ? 'blackjack' :
                     (hasAce && (total >= 8) && (total < 11)) ? 'standing' :
                     'hitting'
       }) )
  )

that was a lot of fun! (we're done step 3 now!)

step 4

now your app plays the dealer hand like a las vegas dealer, all in one line of code.

go have fun!


The purpose of this admittedly silly example is to intruduce you to the main topics we'll be covering:

  • react (here we use React.Component's this.setState function)
  • functional style programming
  • es6 (destructuring, fat arrows, default params used here)

also, it's important to understand javascript is a language of many styles, finding what works for you is most important - this function is how I like to write things, but maybe isn't your cup of tea!

You'll read plenty of javascript, some of it will be good stuff - so keep your eyes open.


the precocious student will want to learn how this works (instead of just typing it in and believing in magic):

feel free to skip over this section the first time you go through this lesson!... which is to say, you're already done lesson 0

First: calculate next cards

  dealNextCard = ()=> this.setState(({ cards, handStatus })=> ({
    cards: (handStatus === 'hitting') ?
           cards.concat( newCard() ) :
           [ newCard(), newCard() ]

  }),

first, we are declaring an instance method this.dealNextCard which takes no params ()=>

the body of the function is one line (it doesn't start with a { and end with a }) and consists of a call to this.setState

this.setState is from the React Component API, in this case using the updater function not the shallow merge (which is the simpler way)

after destructuring the cards and handStatus out of this.state, we return an object with a new value for this.state.cards

the new value we calculate is determined by handStatus:

  • if we're hitting, our new cards array will be the old array with a newCard() concatenated (newCard is a function declared earlier in the file which generates an object with a random rank and suit property, each being just a number)
  • if we're not hitting it is assumed the hand is over, so we return a two new cards

In total, the first chunk calculates the cards, either adding one new card or a new hand. After the comma }), we have finished our updater function as the first param, and are ready to pass the second param to this.setState (which is a callback function)

Intermezzo: calculate the total and if there is an ace in the hand

  ({
    hasAce = !!this.state.cards.find(({ rank })=> rank === 1),
    total = this.state.cards.reduce((p, { rank })=> p + Math.min(10, rank), 0),
    
  } = {})=>

Here, we're exploiting two facts to calculate the total and hasAce values without writing two lines of code:

  1. React's this.setState's second param is a callback which receives no parameters (React calls it once the state update we're requesting is done)
  2. We can set a default value for function params (here the {} near the end), which will always be used (as the function is called with undefined as the first param)... then we can destructure that empty object and set default values for fields of that object (which again, will always be used), and those default values are evaluated expressions - which since we are in the callback function, we know our state update has occured, and thus can use our new cards in our calculations.

this is actually bad code. It is hard to read (flow is backwards) and misuses React's setState's API. I only did this to avoid having a function body, which is totally irrelevant... just for fun!

after the => for one statement, we will have access to hasAce and total

hasAce is a boolean (!! is how to cast to boolean in javascript) which is true when we can .find a card in this.state.cards who has a field .rank which equals 1 (ie is an ace)

total is the sum of the cards, counting all face cards as 10, and aces as 1 (we'll manage the 'ace can be 11' rule in the body-statement of this callback function)

Finally: calculate our new handStatus

      this.setState(({ cards })=> ({
         handStatus: (total > 21) ? 'bust' :
                     (total >= 17) ? 'standing' :
                     (hasAce && (total === 11)) ? 'blackjack' :
                     (hasAce && (total >= 8) && (total < 11)) ? 'standing' :
                     'hitting'
       }) )

this statement is the body-statement of our callback function (fat arrow functions which are one line of code can be written without {curlies}!)

now that we know hasAce and total we can use our vast existing knowledge of the game of blackjack dealer rules to determine the next handStatus.

again we call this.setState with an updater function, destructuring cards from this.state

we return (for setState to do a shallow merge onto state) an object with handStatus calculated with chained ternary expressions.

let's take a minute to learn about ternary by flipping a coin

const condition = Math.random() > 0.5;

if( condition ) console.log('heads');
else console.log('tails');

can be refactored down to

const condition = Math.random() > 0.5;
const output = condition ? 'heads' : 'tails';

console.log( output );

the value of the ternary is that we could rewrite this into one line, without using an if block

console.log( Math.random() > 0.5 ? 'heads' : 'tails' );

because ternary is an expression... not a block statement!

as it turns out, each of the two outcomes are just expressions, so we can plug ternaries in there (chaining)

const flip = ()=> (Math.random() > 0.5);

console.log( flip() ? 'heads first' :
             flip() ? 'heads second' :
                      'two tails' );

some people don't like doing this, even writing lint rules against it. I liked it for the handStatus example because it reads like proper functional pattern matching.

So that's all there is to it!, we check each possible outcome, and the first one to match (true) determines our handStatus value.

After the state has been updated, our render function is triggered, and the new card(s) and status are displayed to the user.

This is just a silly example meant to prime you for the rest of the course. Don't worry if the code made not much sense yet!


I'll leave it as an exercise for you to write this function HOW you're comfortable, when you're ready.

It's important here to find your own way in JavaScript - there's no other way to be found.


for more reading, here are some google search term links


think it through:

  • play against dealer
  • keep track of bets
  • splitting, doubling down
  • insurance
  • multiplayer, online, ..!

back to index

next lesson