Play my Ruby blackjack game, and learn how to use PRISM.JS to pretty up your code

Mary Dean

My blackjack game
The author at the casino

My first programming class was in the Ruby language. At the time, Ruby on Rails was a big player in the "full stack" world, so it seemed like a good idea to climb onto that bandwagon. The class was challenging and fun. If anyone reading this is interested in learning Ruby, you can get a huge head start just by reading the free introductory materials and doing the pre-class exercises at Launchschool.com.

Anyway, one of my assignments was to write a blackjack game. That was four years ago, mind you, so I was recently surprised to discover that my game is still up and running on trusty Heroku.com, so you can play it, too. Woo hoo! There might be a five second delay before the page loads -- that's how Horoku punishes you for not being a paying customer, which is fair enough.


It's strange to find something that you made and forgot about still "living" perpetually on the web, like reading a diary entry about events you don't remember, or coming across a video of yourself speaking a language you don't speak anymore.

I love my tiny playing cards, how they look grimy and bent. No, I didn't make them. I found them on this useful website that everyone should know, openclipart.org. It has free, public-domain clip-art that you can help yourself to, and use for any purpose. And it's well organized, too.

Blackjack
Get free clip-art from openclipart.org

After I found those cards, I was faced with the prospect of downloading 52 of them to make my deck, and that motivated me to write a "web scraper". That's a program that searches the web for something and downloads it for you. It was the first time I felt like a real programmer, because -- surprise! -- it actually worked. That's a pretty rare occurrence when you're a beginner.

My scraper looked like this:

require 'mechanize'
a = Mechanize.new
a.agent.http.verify_mode = OpenSSL::SSL::VERIFY_NONE
page = a.get 'https://openclipart.org/search/?query=ornamental+deck&page=2'
url_list = []
page.links_with(:href => /ornamental-deck/).each do |link|
  url_list << link.href
end
url_list.uniq!
url_list.each do |card_url|
  card_page = a.get card_url
  card_page.links_with(:href => /\/2400px\//).each do |link|
    fullname = link.href.split('/').last
    shortname = fullname.sub('nicubunu_Ornamental_deck_','').downcase
    download_page = link.click
    a.get(download_page.uri).save_as "images/cards2/#{shortname}"
  end
end

LOL, when I first pasted that code into this post, it made a big mess. You see, this blog uses a Markdown editor, and Markdown uses "#" pound signs for formatting your headings, while the Ruby language uses "#" pound signs for making comments.

"No problem," I thought, "I will simply wrap my code in <code> and <pre> tags. That should neutralize those pesky octothorpes." It looked like it was working, so I continued on my way. But later, when I "published," eek, it looked like this:

Code gone bad
Ruby code gone bad

Sigh.

Why is nothing ever easy?

Just escape it, you say.

A brief detour into the world of HTML character entities.

I am writing this blog for self-taught coders who often have giant gaps in their education, like me, so feel free to skip this section if you know what I am talking about.

HTML character entities (a.k.a. "named character references," "reserved symbols," "numeric character references," "escape codes") are special codes that you can use to represent certain symbols that might have different meanings in different contexts.

The most common example is the less-than and greater-than signs that HTML uses for everything. For example, take a look at this boring sentence.

The <center> tag, along with <menu> and <strike> have been deprecated.

What I actually typed was:

char_entities2

I had to type it that way, because otherwise, it would have looked like this:

yikes
This is what happens when you don't escape!

Do you see why? The browser interprets HTML code as HTML code! Who knew?

The escape from this problem (ha ha) is to "escape" your symbols, which means replace them with secret-code equivalents that tell the browser, "make it LOOK like a less-than sign but don't INTERPRET it as a less-than sign."

Side note: FreeFormatter.com is a handy website for escaping things.

So, I thought, I simply need to look up the escape code for the "#" sign, and replace all my #'s with that code. And I was right about that. But I was wrong about something else. I'd never really deep-dived into escape codes before, so I had a vague and incorrect, suspicion that replacing "#" with "&#35;" in my code would mean that anyone who copied my code would end up with a bunch of "&#35;"'s instead of "#" signs in their code.

Nobody would want my scraper code, of course, but someone who happens to be taking a Ruby course and has to write a blackjack game might actually want to copy my blackjack code (for learning purposes, of course). And how ugly would my comments look if they were all preceded with "&#35;" ?

Like I said, I was WRONG about that suspicion. If you copy text that contains character codes from a browser, you actually get the real thing, not the code! Kind of weird, huh?

But I didn't go in that direction because, in addition to the above misconception, I was thrown off by this post on Stack Overflow, in which three people responded to someone with my exact question by saying, basically, "don't be stupid, the pound sign isn't a reserved character in HTML so there is no character entity for it."

Now, if I had kept reading, I would have seen a later comment that said, "Markdown interprets # as 'headers', so you need to use &#35;1" and that would have saved my day, but I didn't.

Instead, I thought, "@#*&%! I CAN'T BE THE ONLY PERSON IN THE HISTORY OF THE WORLD WHO WANTS TO PASTE SOME RUBY CODE INTO A MARKDOWN EDITOR! WHERE DO I GET A SYNTAX HIGHLIGHTER AND HOW DO I INSTALL IT?"

Which leads us to the topic at hand: SYNTAX HIGHLIGHTING

How DO other people get their code to look so nice?

If you start out programming in Notepad, as many of us do, there will come a moment when your mind is blown by a professional code editor, like Sublime Text, for example.

Pretty code
Sublime text, a sublime code editor

Wowzer! Look how everything is neatly lined up and color coded. Even a person who knows nothing about computers could change the words to that song, or make the beer disappear by twos or by fives. Seriously, how does it know what's a variable and what's a function and what's an operator?

Notepad++ is another great code editor, and it's free. Notice the lines connecting each opening <div> to its closing </div>. When I click on one element, it highlights its partner, even if it is on the other side of the room. How sweet is that?

Div lovers
"I'm a closing div, and I belong to YOU!"

Alas, when you copy that colorful code out of your editor and paste it into a web page, or into Microsoft Word, or wherever you keep your notes, it looks terrible, or at least boring, like my "scraper" code did above.

NOTE: One free cure for ugly notes in MS Word or wherever is a Notepad++ plugin called "NppExport." It gives you a "Copy to RTF" menu option which preserves all your pretty colors for pasting into other programs.

The solution, for web pages, is to use a syntax highlighter. And after snooping around a bit, it looks like Prism.js is the code formatter du jour. Why? Because it's easy, free, and it speaks dozens of languages. Oh, and you can link to it via a CDN so you don't even have to download it.

How to use Prism.js

OK, we want the pretty colors, so how do we get them into our blog pages?

Here's the easiest way:

Paste this line of code BEFORE the ugly code you want to format. It will load the Prism.js CSS style sheet.


<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/themes/prism.min.css" rel="stylesheet" />  

Then, paste this line AFTER the ugly code you want to format. It will load the actual Prism.js javascript file:


 <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/prism.min.js"> </script>

That's it!

Now you just need to learn how to tell Prism.js what to format, and what language to format it in. By default, Prism can format HTML, CSS, and Javascript. So let's do a little test.

<!-- I am HTML -->
<h4>I am a heading</h4>
<p>I am a paragraph with <em>emphasized</em>text</p>
/* I am CSS */
h2, h3, h4 {
    color:blanchedalmond;
    background-color:cornflowerblue
    }
//I am javascript
$('body').on('mouseleave', '.popover', function () {
				$('.popover').popover('hide');
			})
#I am ruby, but I am not being formatted :-(
array_of_cards.each do |c|
        #cards are in string format, e.g. "jack_of_diamonds"
        face = c.split('_',0)[0]

You should be seeing some different colors. And you might have noticed how it correctly identifies how comments are formatted in each language.

Wait? How did Prism know what language I was speaking in each of those examples?

This is how.

You should wrap your code in <pre> and <code> tags, in that order, and then add a class called "language" to the <code> tag, like this:


<pre>
<code class="language-javascript">
//This is my javascript code 
</code>
</pre>

Obviously, you would change the "language-javascript" to "language-css" or "language-html" or "language-fortran".

Even simpler, if you are using a Markdown editor, you can use back ticks, like this:


```css
#pretty-code {
  color: pink; 
  font-style: 'gum drop';
  }
```

Get it?

As you have probably noticed, I am playing around with the colors right now. I'll explain how to do that, too.

But first, what about poor Ruby? She needs some formatting, too.

Adding other languages to Prism

By default, Prism will format Javascript, CSS, and HTML. If you want other languages, you'll have to load them yourself. But it's easy.

Here's how to add syntax highlighting for Ruby.

Copy the following line, and paste it AFTER the javascript line that we pasted before, at the bottom of your file.


<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.12.2/components/prism-ruby.min.js"></script> 

Now we can add class="language-ruby" to our code tags. See my blackjack game below for an example.

But what if you want Java or Python or LOLCode?

Here's the lazy way to add more languages. Examine that string I just gave you and look for the word "ruby". All you need to do is change the word "ruby" to any of the other languages available.

Once you have it working, you should ask yourself: will I frequently need to format code? If so, you should add the lines I gave you to your default.hbs file (that's the file in your ghost theme that holds the stuff you always want to load). Either edit the file directly, or go into your admin module in Ghost and add them via the "Code Injection" section of the Admin panel. Obviously, you want to put the .css file in the header, and the .js files in the footer.

If the answer is "no," you could actually do it the way we discussed, by simply pasting the lines immediately above and below your code, right inside your blog post.

(Note: if you have trouble getting that to work in Ghost, make sure your script tags are aligned to the left with NO spaces or tabs in front of them, and make sure that all URLs start with https:// not plain // or http:// without the s. Also: I am working on a self-hosting Ghost blog. I have no idea if they block scripts on a hosted version of Ghost... let me know in the comments).

This strategy has the added benefit that you only load the syntax highlighter when someone is actually viewing a post that contains code, instead of everywhere and all the time.

If you are one of those people who likes to collect everything, yes, you could go to the Prism website and create a massive file that will format all possible languages. But we should resist that impulse. Just load the modules you actually need when you need them. That's the beauty of the Prism approach to code formatting.

On the other hand, if you are one of those people who likes to try out different color schemes and create your own color combinations, I give you permission to do that, but ONLY if you promise not to spend more than an hour or two on it. That sort of thing can be addictive. But here's how you would do that.

Color schemes
Prism color schemes

How to customize your color scheme

First, get Prism working with the default styles. Then look in these two places. The colors they demonstrate on their website can be found in the "themes" folder in their GitHub site. And if you want more, take a look at this prism-themes repository. They are just CSS files. Choose one that you like and download it to your assets/css folder, then link to it in your default.hbs file, right AFTER the default Prism css file. Remember that in Ghost, you need to use the "asset helper." My link looks like this:

<link rel="stylesheet" type="text/css" href="{{asset 'css/prism-ghcolors.css'}}"/>

Once you have that working, open it in a code editor and go to town changing the colors around. I love the CSS color names, like PapayaWhip and DodgerBlue. I'm using a creamy "FloralWhite" for my background, and isn't it so much nicer than regular white? You don't have to limit yourself to named colors. Here's a nicely arranged list of 500 colors. If a color doesn't have an official css name, you can use the hex code.

Final considerations

Here are a few more technical notes before I actually paste my Ruby blackjack code below.

  • Whenever you are loading javascript files, it's a good idea to press F12 on your Chrome or Firefox browser and look at the Console. You might see errors about the files being blocked. Chrome will reject them, for example, if your links don't start start with https://. Pay attention to the "s" which stands for "super secure."
  • If you are reading this in the future (Hi, Future!), you will want to change the version number in all those links I gave you to the current version. The version I have installed is 1.12.2. Just during the week I was messing with this stuff, it went from 1.11.0 to 1.12.0 to 1.12.2. The way to find the most current version is go to this page and click the version dropdown at the top of the page. Luckily, they keep the old versions around, so if everything is working nicely, there's no need to upgrade. You will see the version number in the URL of all the links I gave you. Just make sure they are all the same.
  • We are using the CDN ("content delivery network") version of the PRISM files. That means we are calling them from a distant web server rather than serving them up from our own web server. Personally, I have grown to prefer that method because:
    1. It makes it easier to update to newer versions of the program (just edit the links)
    2. It is fast because your browser only makes the call once, and then caches the files. Also because CDN owners buy faster servers than you do.
    3. You can easily change your mind and try out different things. A better syntax highlighter might show up tomorrow, and we might want to check it out
    4. Most importantly, it doesn't really MATTER to me if the CDN link were to fail one day and I suddenly didn't have syntax highlighting on my blog. It's not of vital importance. If it were, then I would definitely want to download the files and serve them up from my own server. It's all just text files, after all.

Here are some useful links:

  1. Prismjs website: https://prismjs.com/
  2. List of all the languages available: https://prismjs.com/#languages-list
  3. Prism Github site where you can play with color schemes: https://github.com/PrismJS.
  4. CDN links. This is where you will find all of the links you might need to load Prism and its many accessoiries. https://cdnjs.com/libraries/prism

OK, without further ado, here is the hopefully prettified code to my brilliant blackjack game, followed by a decorative rooster.



#BLACKJACK by Mary Dean
#working game located at 
#https://marydean-blackjack.herokuapp.com

#main.rb
require 'rubygems'
require 'sinatra'
require 'pry'

#removed this line due to possible conflict with chrome
#set :sessions, true
#and added this line. 
use Rack::Session::Cookie, :key => 'rack.session',
                            :path => '/',
                            :secret => 'some_random_string'

#these lines are for me to test over home network
set :environment, :production
#set :bind, 'IP ADDRESS'

helpers do

    def value_of_hand(array_of_cards)
      faces = {"ace" =>11,
               "2" => 2,
               "3" => 3,
               "4" => 4,
               "5" => 5,
               "6" => 6,
               "7" => 7,
               "8" => 8,
               "9" => 9,
               "10" => 10,
               "jack" => 10,
               "queen" => 10,
               "king" => 10
      }
      total_value = 0
      num_aces = 0
      array_of_cards.each do |c|
        #cards are in string format, e.g. "jack_of_diamonds"
        face = c.split('_',0)[0]

        value = faces[face]

        total_value += value

        num_aces += 1 if face == 'ace'

      end
      #correct for Aces -- BORROWED THIS LOGIC FROM TEACHER'S CODE
      num_aces.times do
        total_value -= 10 if total_value > 21
      end

      return total_value
    end #end value_of_hand

    def decrease_kitty_by(amount)
      old_kitty = session[:kitty].to_i
      new_kitty = old_kitty - amount
      session[:kitty] = new_kitty.to_s
      if new_kitty <= 0
        @error = "You have run out of money, please put more money in your kitty"
      end
    end

    def increase_kitty_by(amount)
      old_kitty = session[:kitty].to_i
      new_kitty = old_kitty + amount
      session[:kitty] = new_kitty.to_s
    end

    def check_kitty
      kitty = session[:kitty].to_i
      bet = session[:current_bet].to_i
      if kitty < bet
        redirect "/game/overdraft"
      end
    end

def set_helpful_message(type)
    #after extracting all these I think it was a mistake because now the
    #lookups will be done even if they aren't needed
    player_value = value_of_hand(session[:player_cards]).to_s
    dealer_value = value_of_hand(session[:dealer_cards]).to_s
    bet_value = session[:current_bet]
    name_value = session[:player_name]
    message =
      case type
        when 'continue_player_hand'
          "

Your cards add up to: #{player_value}

What would you like to do?" when 'player_went_over' "

Drat!

Too bad! With #{player_value} you have busted, #{name_value}. I hope you will play again" when 'player_stays' "

Player stays

You have chosen to stay with #{player_value}. Now it's the dealer's turn." when 'dealer_must_hit' "The dealer must hit..." when 'dealer_went_over' "

Yahoo!

The dealer went over! You win #{bet_value} chips!" when 'player_blackjack' "

Awesome! #{name_value} got a BLACKJACK!

You win #{(session[:current_bet].to_i * 2).to_s} chips!" when 'dealer_blackjack' "

Dealer Blackjack :-(

Too bad, #{name_value}, the dealer got a blackjack. You lose #{bet_value} chips" when 'dealer_wins' "

Darn!

The dealer's #{dealer_value} beats your #{player_value}. You lose #{bet_value} chips" when 'player_wins' "

You win!

Congratulations, #{name_value}, you won #{bet_value} chips!" when 'its_a_tie' "

Tie game

You both have #{dealer_value}, so looks like we have a TIE! Let's play again." end #end of case session[:helpful_message]=message message end #of def end #of helpers #WELCOME #I wanted to serve a static page but couldn't figure out how get '/' do erb :index, :layout => :layout_plain end #GET and SUBMIT USER NAME get '/set_name' do erb :set_name, :layout => :layout_plain end #SET NAME, KITTY, AND INITIAL BET post '/set_name' do session[:player_name] = params[:player_name] session[:kitty] = params[:kitty] session[:current_bet] = params[:current_bet] check_kitty redirect "/game" end ##CREATE DECK OF CARDS AND DEAL 2 CARDS TO EACH #ALSO SET SHOW/HIDE VARIABLES get '/game' do @show_dealer_first_card = false @show_hit_stay_buttons = true @show_dealer_turn_button = false @dealer_blackjack = false @player_blackjack = false session[:dealer_cards] = [] session[:player_cards] = [] #I WANT TO RE-USE DECK FOR CARD COUNTING SO ONLY CREATE NEW DECK IF NEEDED if session[:deck].nil? || session[:deck].size < 10 suits = ["hearts", "clubs", "spades", "diamonds"] faces = ["ace", "2", "3", "4", "5", "6", "7", "8", "9", "10","jack","queen","king"] deck = [] cards= faces.each do |s| suits.each do |f| card = "#{s}_of_#{f}" deck.push(card) end end deck.shuffle! session[:deck] = deck end session[:player_cards] = [session[:deck].pop, session[:deck].pop] session[:dealer_cards] = [session[:deck].pop, session[:deck].pop] if value_of_hand(session[:player_cards])== 21 @player_blackjack = true @show_hit_stay_buttons = false @show_play_again_button = true increase_kitty_by(session[:current_bet].to_i * 2) set_helpful_message('player_blackjack') else set_helpful_message('continue_player_hand') end erb :game end post '/game/player/hit' do session[:player_cards] << session[:deck].pop if value_of_hand(session[:player_cards])>21 set_helpful_message('player_went_over') decrease_kitty_by(session[:current_bet].to_i) @show_hit_stay_buttons = false @show_dealer_turn_button = false @show_play_again_button = true @show_dealer_first_card = false erb :game else @show_hit_stay_buttons = true @show_dealer_turn_button = false @show_play_again_button = false @show_dealer_first_card = false set_helpful_message('continue_player_hand') end erb :game end post '/game/player/stay' do set_helpful_message('player_stays') #@success = "You have chosen to stay with #{value_of_hand(session[:player_cards])}" @show_hit_stay_buttons = false @show_dealer_turn_button = true @show_dealer_first_card = false erb :game end get '/game/dealer' do @show_hit_stay_buttons = false dealer_value = value_of_hand(session[:dealer_cards]) player_value = value_of_hand(session[:player_cards]) if dealer_value == 21 && session[:dealer_cards].size == 2 set_helpful_message('dealer_blackjack') decrease_kitty_by(session[:current_bet].to_i) @show_play_again_button = true elsif dealer_value > 21 set_helpful_message('dealer_went_over') @show_play_again_button = true increase_kitty_by(session[:current_bet].to_i) elsif dealer_value < 17 set_helpful_message('dealer_must_hit') redirect '/game/dealer/hit' elsif dealer_value >= 17 if dealer_value > player_value set_helpful_message('dealer_wins') @show_play_again_button = true decrease_kitty_by(session[:current_bet].to_i) elsif dealer_value < player_value set_helpful_message('player_wins') increase_kitty_by(session[:current_bet].to_i) @show_play_again_button = true elsif dealer_value == player_value set_helpful_message('its_a_tie') @show_play_again_button = true end end @show_dealer_first_card = true erb :game end get '/game/dealer/hit' do session[:dealer_cards] << session[:deck].pop redirect '/game/dealer' end get '/game/overdraft' do erb :overdraft end get '/game/player/set_new_bet' do erb :new_bet end post '/game/player/set_new_bet' do current_kitty = session[:kitty].to_i new_amount = params[:add_to_kitty].to_i total = current_kitty + new_amount session[:kitty] = total.to_s session[:current_bet] = params[:current_bet] check_kitty redirect '/game' end get '/profile' do "Edit your profile here" end get '/cash_out' do "Here are your big winnings!" end
Fancy rooster
An example of free clipart from openclipart.org