Up until now in Launch School, I have only made teeny tiny programs. Their philosophy is that it is more important to drill with the small building blocks until they become second nature rather than complete a huge task without completely understanding every aspect of that task. It seems slower, but in the long run, it helps speed up the process because there are fewer bugs in the program. I lived this firsthand when making the longest program I have written to date, Rock Paper Scissors Lizard Spock.
The lesson walked us through a simple Rock, Paper, Scissors game. The program used a simple logical structure that, while repetitive, was functional. Then, it showed us how to refactor that structure into code that was less redundant by using methods.
The lesson ended, and I was feeling pretty confident. I knew what was coming: writing a program based on the same game, but with a lot more options:
The instructions gave a few hints: try to use a hash, and think about the logic behind the game. For example, Rock has only two opportunities for winning: crushing Scissors or crushing Lizard. The instructions also said to use short-hand letters like “r” or “l” instead of “rock” and “lizard” so that the player could enter their choice much more quickly. They also want the game to be able to be played until either the user or the computer reaches 5 points and becomes the Grand Master. Whew!
I knew how to do each of these basic building blocks, theoretically. I was excited to get started. I began by creating loops and methods that would deal with the point part of the game. I created prompts that would instruct the user to enter their choice, and used the .sample method so that the computer could randomly select its choice. All of the user-side code was ready to go!
But how, oh how, to make the game actually work? I spent a good half hour trying to work out the logic of the game. I did not want to simply repeat the same logical structure of the Rock Paper Scissors game since that would be redundant. I realized that I have very little experience with hashes. It has been a while since the last hash lesson, and since then, I haven’t really found much of a use for them in the “wild.” As I looked at the docs for hashes, I realized that I had no idea what I was doing.
I looked at other students’ codes to see what they had done. I compared at least 5 programs, and everyone’s programs were so different that I saw there was no single golden ticket method to get this program to work. Some of the programs used methods that I didn’t even remotely recognize (probably someone with a programming background). I tried some of the methods that I saw in the other students’ codes, and I could not get any of them to work. (Plagiarism just doesn’t work in coding – if you don’t understand enough and you have to plagiarize, it won’t work for you anyway, and if you already understand enough, you don’t need to plagiarize.)
This is exactly why Launch School’s pedagogy is the way it is – coding is so open-ended that there really is no “right” way to program. There are more efficient ways, more readable ways, perhaps, but there simply is no magical formula for programming. This is why breaking things down is so crucial. They teach students to use the PEDAC method, which I hadn’t done in my haste to get started. My last post was about how important it is to plan – did I learn from my mistakes? Not enough, apparently. At this point, I had spent all afternoon and evening working on this program with nothing to show.
So, I got out my pencil and notebook and created a small diagram to plan what would happen in my program. Then, I went to sleep and let my brain do its synapses thing.
The problem with my plan was that I didn’t know how to do the most important part of the program, which is to compare the user input and the computer choice to the winning combos from a hash. I needed to test out scenarios with this small part of the program before adding any loops or counters.
To the Ruby Docs! From what I remember about my classmates’ codes and from what I found in the documentation, I decided to focus on two methods: .fetch and .include?
I needed a way for the program to compare the user input to the computer choice. If they have the same choice, it is a tie. If the computer choice is part of the winning choices hash, the user wins. If not, the user loses. So I needed a way to return the computer choice if it was a part of the hash, and check that value against the hash to see if it was included. The .include?
winning_combos = { ‘rock’ => ‘scissors’, ‘paper’ => ‘rock’ } |
I started with the mini hash above. The documentation provides this example for the .fetch method:
h = { "a" => 100, "b" => 200 } # this is the hash
h.fetch("a") # the method is invoked with key "a"
#=> 100 # the method returns this value
Using my mini hash, I created a variable called user_input and set it to ‘rock’. Then I invoked the .fetch method using the argument user_input.
user_input = 'rock'
winning_combos = { 'rock' => 'scissors', 'paper' => 'rock' }
winning_combos.fetch(user_input)
#=> "scissors"
It returns “scissors”. Now to create a way to test if the user won by using a conditional statement. I assigned the variable name user_win to the return value of the .fetch method.
user_input = 'rock'
computer_choice = 'scissors'
winning_combos = { 'rock' => 'scissors', 'paper' => 'rock' }
user_win = winning_combos.fetch(user_input)
if user_win == computer_choice
puts "You win!"
else
puts "You lose."
This scenario prints out “You win” because the key value “rock” contains “scissors”. Rock beats scissors. I tested the code again by changing computer_choice to “paper,” it printed out “You lose.” Paper beats rock. If
I added an
I searched online and saw that I can use an array for multiple values.
winning_combos = { 'rock' => ['scissors', 'lizard'], 'paper' => 'rock' }
Adding multiple values to the key causes the conditional statement to not be true anymore, because the computer only inputs one value and the key contains two. This is where the .include?
user_input = 'rock'
computer_choice = 'lizard'
winning_combos = { 'rock' => ['scissors','lizard'], 'paper' => 'rock' }
user_win = winning_combos.fetch(user_input)
if user_win.include?(computer_choice)
puts "You win."
elsif user_input == computer_choice
puts "Tie."
else
puts "You lose."
end
This prints out “You win” because “lizard” is one of the winning combo options for “rock”.
When I changed the variable computer_choice to “Spock”, the program prints “You lose” because Spock is not one of the value options for “rock”. It worked!
This seems so simple in retrospect, but once I had a plan, I spent an hour or so researching and testing this part of the program before adding anything else. The night before, I had spent 4-5 hours fiddling around and had nothing to show for it. With a plan uploaded into my brain and with the time I dedicated to my mini tests, I was able to complete the entire functional program in less than 2 hours. (I spent three additional hours improving/refactoring the program into methods, but that is a different story.)
It pays to plan, to test small parts of the program, and to research. I even spent a bit of time looking at the code review for my classmates’ programs, to see what the TA’s recommended and implement those changes into my own programs. The whole process was much smoother, more relaxing, and more fun than my previous shots in the dark.