Tealeaf Course One Review
I’ve finished the first course at Tealeaf Academy, and I feel great about what I’ve accomplished. I took my time, because I believe that it’s important to get the fundamentals right. Before I signed up, I read a lot of blogs from people who had gone through the course, and one thing I noticed was that some of them admitted that they’d rushed a bit in the beginning, and so felt overwhelmed later. Perhaps that is inevitable, because there is so much to learn, but I wanted to make sure that I had a solid foundation. Ruby on Rails is a powerful framework for building applications, but it is just Ruby. So that means the better I understand Ruby, the better I’ll understand how to use Ruby on Rails.
There are a lot of choices if you want to learn how to code. I’ve already discussed my rationale for signing up with Tealeaf in an earlier post. So how did my first course go?
In a word, it was outstanding. I know there are many other good programs out there there, but I know I picked the best one for me. I’ll address the four most important aspects of the program:
- The quality of instruction
- The structure of the course
- The content of the lessons
- Final thoughts
The quality of instruction
Unlike some programs, you don’t get to meet with a mentor each week. But you are never alone. If you have a question, or if you need to have someone review your code, you can use the forum to post it, and you’ll receive prompt and personalized attention. Obviously, the response times are faster during certain hours of the day, but you’ll get help within a few hours, at the very most. I often posted things at night, had a response waiting when I got up the next morning. On top of that, you can always chat with other students (and often TAs/instructors) in the ‘Student Lounge.’ Honestly, I probably retreated and spent too much time working through the material on my own, but that was my process. Officially, the course is a four-week course, but it took me closer to four months. Throughout all of that, the instructors are always kind and patient, and they know their stuff.
The structure of the course
The course stresses learning the fundamentals of Ruby, and then progressively builds upon that knowledge. Before the course, there is a pre-course (note that after I’d started the first course, they turned this into a free ‘Prep Course’, which might be different than the pre-course that I took). You can learn a lot for free, by going through the free textbooks and workbooks they’ve written. If you’re interested in trying the free Prep Course, create a free account here: http://www.gotealeaf.com). In the pre-course, you go through their textbook, and learn the basics of Ruby. I spent a lot of time with this. I made an org-mode outline, complete with code blocks, which I could export into their own Ruby files (see the files and the notes.org notebook here). In the notes, I reworded the lessons, in my own words, with extra experiments, comments, and my record of the exercises (complete with my successes and failures). Looking at it now, it’s a very intimate record of my learning process. I wanted to explain everything in my own way, because that helped me process the information and internalize it. Plus, it’s a great reference that I can refer to later.
The course itself consists of four lessons. In the past, before I joined Tealeaf, everyone learned together, and each lesson was scheduled to last one week. But over time, as some students fell behind, the format was changed to allow students to go at their own pace. This is one of the most unique aspects of the program, and one that has worked very well for me.
The content of the lessons
The Course included the following projects:
- tealeaf-course1-week1
- reverse_madlibs
- tealeaf-course1-lesson2
- Ruby_Blackjack
- sinatra_blackjack_webapp
Some of the other programs allow students to choose their own projects, and work with a mentor to complete them. That could work well for some, but while I have a lot of ideas for projects, I found it hard to choose one that would be appropriate. And worse yet, it might not allow me to learn everything I needed. The advantage of the Tealeaf way is that I know I’ll get a really good understanding, which I can use to complete my own projects with later.
The capstone project of the first lesson was a procedural Blackjack game. Ruby is a great Object-Oriented language, but it is possible to build some interesting things in a procedural way. But the limitations become very clear, very quickly. When I was 11, I learned BASIC in summer school. This lesson reminded me of that. The program worked from the top to the bottom, so everything had to be neatly ordered. It is possible to use loops and conditionals to build some sophisticated flow control behavior, but it’s very brittle, and I cringed at the idea of adding a feature or making any changes, for fear of bringing the whole house of cards down.
In the second lesson, we dived into Object-Oriented Programming, which allowed us to write cleaner, more efficient code. We redid the Tic-Tac-Toe and Blackjack assignments, and they were much easier to write, read, and debug.
For example, in both versions, if either the player or dealer gets Blackjack or busts, the game is over, but if not, both the player and the dealer get a turn. At the end, the final card values are calculated, and the winner is declared. In the procedural version, the code to calculate a winner looks like this:
1 # ...preceding code cut for brevity...
2
3 if (dealer_sum > 21) && (player_sum < 21)
4 system 'clear'
5 puts "---"
6 puts "#{name}'s hand: #{player_hand}"
7 say "value: #{player_sum}"
8 puts "---"
9 puts "Dealer's hand: #{dealer_hand}"
10 say "value: #{dealer_sum}"
11 puts "---"
12 puts " "
13 say "Dealer busted. You win!"
14 puts " "
15 player_cash_pot = (player_cash_pot.to_i + bet.to_i)
16 puts "You won \$#{bet}. You now have \$#{player_cash_pot}."
17 elsif (dealer_sum == 21) && (player_sum !=21) && (dealer_hand.count == 2)
18 system 'clear'
19 puts "---"
20 puts "#{name}'s hand: #{player_hand}"
21 say "value: #{player_sum}"
22 puts "---"
23 puts "Dealer's hand: #{dealer_hand}"
24 say "value: #{dealer_sum}"
25 puts "---"
26 puts " "
27 say "Dealer hit Blackjack!"
28 puts " "
29 player_cash_pot = player_cash_pot.to_i - bet.to_i
30 puts "You lost #{bet}. You now have \$#{player_cash_pot}."
31 elsif (dealer_sum == 21) && (player_sum !=21) && (dealer_hand.count >= 3)
32 system 'clear'
33 puts "---"
34 puts "#{name}'s hand: #{player_hand}"
35 say "value: #{player_sum}"
36 puts "---"
37 puts "Dealer's hand: #{dealer_hand}"
38 say "value: #{dealer_sum}"
39 puts "---"
40 puts " "
41 say "Dealer wins!"
42 puts " "
43 player_cash_pot = player_cash_pot.to_i - bet.to_i
44 puts "You lost #{bet}. You now have \$#{player_cash_pot}."
45 elsif (player_sum < 21) && (dealer_sum < player_sum)
46 system 'clear'
47 puts "---"
48 puts "#{name}'s hand: #{player_hand}"
49 say "value: #{player_sum}"
50 puts "---"
51 puts "Dealer's hand: #{dealer_hand}"
52 say "value: #{dealer_sum}"
53 puts "---"
54 puts " "
55 puts "#{name} wins!"
56 puts " "
57 player_cash_pot = (player_cash_pot.to_i + bet.to_i)
58 puts "You won \$#{bet.to_i}. You now have \$#{player_cash_pot}."
59 elsif (player_sum == 21) && (dealer_sum !=21) && (player_hand.count >= 3)
60 system 'clear'
61 puts "---"
62 puts "#{name}'s hand: #{player_hand}"
63 say "value: #{player_sum}"
64 puts "---"
65 puts "Dealer's hand: #{dealer_hand}"
66 say "value: #{dealer_sum}"
67 puts "---"
68 puts " "
69 say "#{name} wins!"
70 puts " "
71 player_cash_pot = (player_cash_pot.to_i - bet.to_i)
72 puts "You lost \$#{bet.to_i}. You now have \$#{player_cash_pot}."
73 elsif (player_sum > 21) && (dealer_sum < 21)
74 system 'clear'
75 puts "---"
76 puts "#{name}'s hand: #{player_hand}"
77 say "value: #{player_sum}"
78 puts "---"
79 puts "Dealer's hand: #{dealer_hand}"
80 say "value: #{dealer_sum}"
81 puts "---"
82 puts " "
83 say "You busted!"
84 puts " "
85 player_cash_pot = (player_cash_pot.to_i - bet.to_i)
86 puts "You lost \$#{bet.to_i}. You now have \$#{player_cash_pot}."
87 elsif (dealer_sum < 21) && (player_sum < dealer_sum)
88 system 'clear'
89 puts "---"
90 puts "#{name}'s hand: #{player_hand},"
91 say "value: #{player_sum}"
92 puts "---"
93 puts "Dealer's hand: #{dealer_hand},"
94 say "value: #{dealer_sum}"
95 puts "---"
96 puts " "
97 say "Dealer wins"
98 player_cash_pot = (player_cash_pot.to_i - bet.to_i)
99 puts "You lost \$#{bet}. You now have \$#{player_cash_pot}."
100 elsif (dealer_sum >= 17) && (dealer_sum <= 21) && (dealer_sum == player_sum)
101 system 'clear'
102 push = player_sum
103 puts "---"
104 puts "#{name}'s hand: #{player_hand},"
105 say "value: #{player_sum}"
106 puts "---"
107 puts "Dealer's hand: #{dealer_hand},"
108 say "value: #{dealer_sum}"
109 puts "---"
110 puts " "
111 player_cash_pot = player_cash_pot.to_i
112 say "We both got #{push}. It's a push!"
113 say "The bet will carry over to the next hand..."
114 elsif (dealer_sum > 21) && (player_sum > 21)
115 system 'clear'
116 puts "---"
117 puts "#{name}'s hand: #{player_hand},"
118 say "value: #{player_sum}"
119 puts "---"
120 puts "Dealer's hand: #{dealer_hand},"
121 say "value: #{dealer_sum}"
122 puts "---"
123 puts " "
124 say "We both busted. #{name} loses."
125 player_cash_pot = (player_cash_pot.to_i - bet.to_i)
126 puts "You lost \$#{bet}. You now have \$#{player_cash_pot}."
127 end
That’s 124 lines of code, just to calculate a winner. In the object-oriented version, most of the components of the game, including: player, dealer, hand, and game are encapsulated as Classes, with methods and modules to support the action. So instead of having to chronologically describe each outcome of the game, each game scenario has been extracted into a method in the Game class. For example, if the player wins:
The player is awarded the winning pot:
1 def calculate_player_winning_cash_pot_balance
2 self.cash_pot = show_cash_pot + show_bet
3 end
The player is declared a winner:
1 # ...preceding code cut for brevity...
2 def player_high_score_message
3 show_all_cards_at_end
4 puts "=> Congratulations, #{player.name}! You won!"
5 puts " "
6 puts "You bet \$#{show_bet}."
7 puts "You now have \$#{calculate_player_winning_cash_pot_balance}."
8 end
Notice that the calculate_player_winning_cash_pot_balance method is called from within the player_high_score_message method. There are similar methods for determining when the dealer wins, or when there is a tie, and they can be called during the game.
After I’d finished the game and submitted it for review, I looked at the last lines of the OOP code, and realized that when we begin a new game, we’re actually calling a new Game object. In other words, the game, itself, is an object!
game = Game.new
game.start
Confronted with the brilliance, expressiveness, and beauty of Ruby, how could I not love this language?
In the third and fourth lessons, we switched to web development, and after covering the basics of HTTP, HTML, CSS, and JavaScript, we rebuilt the Blackjack game as a Sinatra webapp, and pushed it to Heroku.
Sinatra is a great Domain-Specific Language, or DSL, based on Ruby. I saw a lot of parallels to Ruby, and Jekyll, which I’ve used to create this site. Sinatra has a simple syntax for specifying routes with Ruby blocks, which are outlined on Sinatra’s Getting Started.
I’m planning to talk more about my Sinatra Blackjack game in a separate post, but here is what the code looks like for comparing the Player’s and Dealer’s hands:
1 get '/game/compare' do
2 @show_hit_or_stand_buttons = false
3
4 player_total = calculate_total(session[:player_cards])
5 dealer_total = calculate_total(session[:dealer_cards])
6
7 if player_total < dealer_total
8 loser!("#{session[:player_name]} stands at #{player_total}, and the dealer stands at #{dealer_total}.")
9 elsif player_total > dealer_total
10 winner!("#{session[:player_name]} stands at #{player_total}, and the dealer stands at #{dealer_total}.")
11 else
12 tie!("Both #{session[:player_name]} and the dealer stand at #{player_total}.")
13 end
14
15 erb :game, layout: false
16 end
If you’d like to play the Blackjack webapp game, it’s here: https://salty-crag-8411.herokuapp.com/.
Final thoughts
The most important thing that I’ve learned in this course is not Ruby, or how to build any of the projects that I’ve completed. What really helped was having mentors, who could help me stay focused on what is truly important. I love learning things on my own, but it’s very easy to get sidetracked, and spend too much time on side topics that won’t help me move forward. That’s why I have a scattered skillset of HTML, CSS, JS, and SQL, but have struggled to bring them together to accomplish practical goals. The head teacher, Chris, always warns us, “Don’t go sideways.” In other words, you could spend a week online, looking at the differences between RVM and RBENV, for example, instead of just using one of them to learn how to build applications. Being able to rely on the experience and guidance of friendly and caring instructors, to help me understand what I needed to study (and more importantly, what I could ignore for now) has made a huge difference.
I enjoyed the first program, and I learned a lot. In the next course, I’ll begin working with Ruby on Rails. After the foundation that I’ve established in the first course, I’m sure it will be challenging, but not overwhelming.
If you enjoyed this post, you might want to subscribe, so you don't miss the next one!