Learn Ruby the Hard Way, Exercise 36: Designing and Debugging

There is no code to copy in this exercise, instead you make a game like in the last exercise and the author asks you to make it as big and fancy as you can including things covered so far like arrays, modules and methods.

I decided to go for an old style dungeon crawler based on an idiotic click and point adventure game me and my mate Jono envisioned making a few years ago:20158525_1532605813472491_367272900_n.png

You play the above rat-man thing, armed with your trusty ‘rat slayer’ sword on a dangerous quest to reclaim your underground dungeon kingdom inhabited by intelligent bipedal rat people from the usurper who dethroned you.  Yes it is that dumb.

 

The game working game and code can be found here.  I’m not going to go through and write a comment about every part of the game or draw a map however to summarise:

  • The game uses methods to call different rooms and navigate in the same way as in the last exercise, if-else statements and while loops with incrementors are used to give more options so you can navigate to a degree.
  • To create a hit point system to simulate fighting,  instance variables were made with integer values of 100 assigned to each.  The part of the game where you enter combat starts a loop that calls the various combat methods stored in the combat.rb file using the module.  These update the values of the instance variables assigned to ‘@player’ and ‘@enemy’, the ‘dead’ method is called if you fall below 0.
  • In the part of the game where you choose a weapon I used a hash to store the weapon name and assign a value that the the combat ‘attack’ method takes as a parameter.  I wanted to have the weapon you choose have a name, e.g. crossbow that I could reference when in combat but I couldn’t access the key from inside the attack method, Ruby stated  and had to use a new method .keys which returns the keys from a hash as an element in an array.

Summary

This exercise was about practicing everything covered so far in the book and it was good to combine it all into one single project for the purpose of revision.   Rather than making a longer game I wanted to make a short one with only a few rooms but featuring more things you can do, for instance there is a room you have to unlock, an inventory system to aquire a weapon (using hash key-value pairs) and a basic combat system.

I didn’t know how to do any of that at the start so instead of writing a map I fumbled on and experimented as I went.  As such the code itself isn’t great; the ways of carrying out tasks varied a lot as I tried different ways to do things and this make’s it difficult to read and the importance of seperation of concerns became apparent the more code I wrote.  If I were to write it agian from the ground up I’d look to write the code in a more succint and flexible way –   possibly creating seperate character and enemy objects with in built methods to manage combat, inventories, health and so on. It was a fun project to work on but I’m eager to move on.

Advertisements

Learn Ruby the Hard Way, Exercise 35: Branches and Functions

Exercise 35 was all about practicing using combinations of functions with if-statements together to make a little game below.

Right first off there is a deliberate bug in the code on line 8 I’ll discuss a bit further on as it took me quite some time and use of my scarce brain power to figure out.  Below is the fully working code for the the exercise and the output:

ex35 1ex35 2

ex 35 3

At the start of the game you are given routes to follow so here is the output if you go right:

ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
   
You are in a dark room.
There is a door to your right and left.
Which one do you take?
>  right
Here you see the great evil Cthulhu.
He, it, whatever stares at you and you go insane.
Do you flee for your life or eat your head?
>  flee
You are in a dark room.
There is a door to your right and left.
Which one do you take?
>  right
Here you see the great evil Cthulhu.
He, it, whatever stares at you and you go insane.
Do you flee for your life or eat your head?
>  dance
Here you see the great evil Cthulhu.
He, it, whatever stares at you and you go insane.
Do you flee for your life or eat your head?
>  eat head
Well that was tasty!
Good job!

And the output if you choose to go left:

ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
   
You are in a dark room.
There is a door to your right and left.
Which one do you take?
>  left
There is a bear here.
The bear has a bunch of honey.
The fat bear is in front of another door.
How are you going to move the bear?
>  taunt bear
The bear has moved from the door. You can go through it now.
>  open door
This room is full of gold.  How much do you take?
>  555555555
You greedy bastard!
Good job!
exit

 

Study Drills

1. Draw a map of the game and indicate how you flow through it

Sure thing

Untitled Diagram

2. Fix all of your mistakes, including spelling mistakes.

I’m pretty sure I did that as i went along

3. Write comments  for the function you do not understand.

I’m good fam.

4. Add more to the game.  What can you do to both simplify and expand it.

I added a bridge between the Cthulu room and the gold room by having a third option where Cthulu sends you straight there (calls the gold room function.)

Screenshot_1.jpg

5. The gold_room has a weird way of getting you to type a number.  What are all the bugs in this way of doing it?  Can you make it better than what I’ve written.  Look at how =~ works for clues.

ex35 4.jpg

The first if statement is a way of getting a a number from the user and creating a variable if the user has entered numerical data correctly, and calling the dead function if not.

The problem with this was that it only recognized combinations of the 0 and 1.  I tried endless ways to add the digits 2-9 as input the program could recognize and convert to integer values without success, in the end I repeated the choice.include?(” “) section of code repeatedly to include every digit from 0-9.

ex35 5

 

 

This is…ugly. And inefficient I know.  After getting to the study drills the author gives you a hint to look at regex or regular expressions for more eloquent ways to solve the problem.  Some googling led to a very helpful answer from a dude on stack overflow who solved it thus on line 9:

ex35 6

That’s it for this exercise.  The expression above helped to make the code a lot more succinct but I don’t yet understand anything about regular expressions.  I do vaguely get the expression here which broken down into stages can be understood as:   =~ is the match operator, the expression to be evaluated is written inside the pair of /  / forward slashes and the \d means ‘any digit.’   The + plus  means ‘0 or greater than zero.’

 

 

 

Learn Ruby the Hard Way, Exercise 33: While Loops

This exercise was all about while loops, here is the code…

ex33 1

 

…and the output

ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
   
At the top i is 0
Numbers now: 
0
At the bottom i is 1
At the top i is 1
Numbers now: 
0
1
At the bottom i is 2
At the top i is 2
Numbers now: 
0
1
2
At the bottom i is 3
At the top i is 3
Numbers now: 
0
1
2
3
At the bottom i is 4
At the top i is 4
Numbers now: 
0
1
2
3
4
At the bottom i is 5
At the top i is 5
Numbers now: 
0
1
2
3
4
5
At the bottom i is 6
The numbers: 
0
1
2
3
4
5
=> [0, 1, 2, 3, 4, 5]

What I learned was basically a while loop is a piece of code that keeps running and doing whatever you tell it to while the conditions for it to run are not true, that is false, as soon as they are true it stops running.

 

If you’re not careful you can end up writing a infinite while loop that will totally crash which will suck.  The author gives tips for things to check

  1.  Make sure that you use while-loops sparingly.  Usually a for-loop is better.
  2.  Review your while statements and make sure that the Boolean test will become false at some point.
  3. When in doubt, print out your test variable at the top and bottom of the while-loop to see what it’s doing

Study Drills

1. Convert the while-loop to a function that you can call, and replace 6 in the test (i < 6) with a variable

I cut out out some of the extra stuff just for the sake of space and making this simple.

ex 33 3.png

This output:

   
At the top i is 0
At the top i is 1
At the top i is 2
At the top i is 3
At the top i is 4
At the top i is 5
At the top i is 6
At the top i is 7
At the top i is 8
At the top i is 9
=> nil

2. Use this function to rewrite the script to try different numbers.

See above.

3. Add another variable to the function arguments that you can pass in that lets you change the + 1 on line 8 so you can change the size of the increment.

The first parameter when you call custom_loop is the value that will make the while loop evaluate to true ending the loop, the second replaces the integer after the += operator so we can give it any value we want.

ex 33 4.png

4. Rewrite the script again to use this function to see what effect that has.

No.

5. Write it to use for-loops and the (0 .. 6) range operator.  Do you need the incrementor in the middle anymore?  What happens if you do not get rid of it?

ex 33 5.png

I added a for-loop as above replacing the while-loop, the increment operator makes no difference as Ruby reads the for loop and carries that out.  Interestingly I googled around how you could increment through a range and found you could do it using the .step method thus:

ex 33 final

which output:

At the top i is 0
At the top i is 2
At the top i is 4
At the top i is 6
At the top i is 8
At the top i is 10
[0, 2, 4, 6, 8, 10]
=> nil

Learn Ruby the Hard Way Exercise 32: Loops and Arrays

Exercise 32 shows….Arrays.  Arrays are indexed lists of stuff that can be assigned to a variable.   So far as I know at this point they can contain basically any object- stirngs (and string interpolation), integers and floast, true or false values and hashes and others arrays.

Objects inside arrays are referenced using numbered indices

arr = Array.new
arr.push([“Cat”, “Pig”, “Duck”])

The items inside the array will be indexed and can be access this way (starting at 0)

puts arr[0]  #=> Cat
puts arr[1]  #=> Pig
puts arr[2]  #=> Duck

Here is the code for exercise 32:

ex32 code

On line 3-5 we created a bunch of different arrays that have Integers and stringes applied to them.

The looping part of the title of the exercise refers to creating code that goes through and does stuff to each item in an array. There are two different ways of doing this given above – on lines 7-9 we have a for loop, a more common way of doing this is in ruby is given on lines 12-14 using the .each method.  On Line 26 a similar loop is written using {} curly braces instead of do-end keywords. In Ruby the curly braces are generally interchangable with the do-end keywords.

Here is the output:

   
This is the count: 1
This is the count: 2
This is the count: 3
This is the count: 4
This is the count: 5
A fruit of type: apples
A fruit of type: oranges
A fruit of type: pears
A fruit of type: apricotes
I got 1
I got pennies
I got 2
I got dimes
I got 3
I got quaters
adding 0 to the list
adding 1 to the list
adding 2 to the list
adding 3 to the list
adding 4 to the list
adding 5 to the list
Element was: 0
Element was: 1
Element was: 2
Element was: 3
Element was: 4
Element was: 5
=> [0, 1, 2, 3, 4, 5]

Study Drills

1. Take a look at how you used (0..5) in the last for-loop.  Look up Ruby’s “range operator” (.. and …) online to see what it does.

A range object represents the values between the start and end value.  It saves memory as you can store a huge a amount of data without having to put each individual value in so for example if we wanted to represent the number 1-ten zillion we could write 0..10000000.

We can assign the results to a variable and use it to compare stuff.

years_range = 1980..2017
my_birthday = 1985

years_range.include?(my_birthday)
=> true

Behind the scenes this uses the .succ method that returns the next element in a data type range so

1.succ #=>2,

B.succ #=> C

This doesn’t work with floats(decimals.) Interestingly you can also use the .to_a method on arrays that contain ranges to fill them up. I suppose this is faster.

test=  (‘a’..’d’).to_a
print test

[“a”, “b”, “c”, “d”]=> nil

2. Change the first for number in the_count to be a more typical .each style loop like the others.

the_count= [1,2,3,4,5]

the_count.each {|x| puts “This is the count: #{x}”}

  
This is the count: 1
This is the count: 2
This is the count: 3
This is the count: 4
This is the count: 5
=> [1, 2, 3, 4, 5]

 

3. Find the Ruby documentation on arrays and read about them.  What other operations can you do beside the push function? Try <<, which is the same as push but is an oeprator. Fruits MM x is the same as fruits.push(x)

There are tons of array methods but here are some of the ones I found out about:

  • The .size and. length methods can return the number of elemenents in an array
  • An you can compare to arrays to remove duplicates thus:
  • .clear will empty an array of elements
  • .empty? will return true if an array is empty
  • .compact will removes all nil values from an array
  • .pop removes last elements, can be given ( ) parameters to specify how many elements to pop
  • .reverse – reverses the order of elements
  • .sort sorts lists of integers or strings, cannot compare the two or true/fals
  • .to_a to array method, we can use it like this

alphabet = (‘a’..’z’).to_a

#=> assigns all letters in alphabet to the the variable on the left.

 

 

Learn Ruby the Hard way Exercise 31: Making Decisions

This exercise expanded on the last and showed a simple fun text game that uses nests of if-statements within if-statements to create more branching output based on the users choices. What caught my eyes was the % symbol used on line 19. Here is can be used reference the users input (assuming none of the conditions for the if/elsif were met.)

Here is the code for exercise 31:

 

ex31.png
Exercise 31 code

The code above is basically two if-statements nested within the if and elsif keywords of a larger if-statement.

Study Drills

Make new parets of the game and change which decisions people can make. Expand the game out as much as you can before it gets ridiculous.

I didn’t do much apart from add a third elsif branch if the users picks a third door I added.  One of the options prints the entire lyrics for the song Safety Dance. No joke. But seriously you could add millions of elsif options to the top level if-statement and if your text editor allows you to collapse these branches it will still be kind of legible.

Write a completely new game. Maybe you don’t like this one. so make your own. This is your computer, do what you want!

Ok. I made this game based on this song. I made it quite lengthy before I got bored and moved onto the next exercise.

puts “””
You’re walking in the woods
There’s no one around and your phone is dead
>”””

$stdin.gets

puts “””Out of the corner of your eye, you spot him
(Shia LaBeouf)

He’s following you about thirty feet back
He gets down on all fours and breaks into a sprint
He’s gaining on you.
What do you do?

1.Get to the car!
2.Run for your life!
“””

part1=gets.chomp

if
part1 == “1”
puts “Shia Labeouf catches you while your fumbling for your keys. Goood Job!”
elsif
part1 == “2”
puts “He’s almost upon you now, and you can see there’s blood on his face”
puts “My God, there’s blood everywhere!”
part2 = gets.chomp

puts “””
Now it’s dark and you seem to have lost him
You see a cabin in the woods

1. creep silently through the underbrush away from the cabin.
2. Move toward the cottage, quietly.”””

part2 = gets.chomp

if
part2 == “1”
puts “You move stealthily toward it
But your leg! AH! It’s caught in a bear trap!”

puts “> ”
puts “What do you do?
1. Saw off your leg
2. Try to pry the bear trap open”
part2_sub =gets.chomp
if
part2_sub == “1”
puts “You gnaw off your leg and limp towards the cottage”

elsif part2_sub == “2”
puts “You try to escape the bear trap but it’s too strong and Shia LaBeouf heard that and caught you. Good job!”
else
puts “You tried %s but Shia LaBeouf heard that and caught you. Good job!” %part2_sub
end

elsif
part2 == “2”
puts “Now you’re on the doorstep but sitting inside is Shia Labeouf
Sharpening an axe!”
else
puts “You tried %s but Shia LaBeouf heard that and caught you. Good job!” %part2
end

puts “> ”
puts “But wait he doesn’t here you:”
puts “What do you do? ”
puts “1. Sneak up and strangle him”
puts “2. Wrestle the axe from him”

part3 = gets.chomp
if part3 == “1”
puts “You strangle him. Your safe at last from Shia Labeouf”
elsif part3 == “2”
puts “Shia LaBeouf overpowers you. Good job!”
else
puts “You tried %s but Shia LaBeouf heard you and attacks you with his axe. Your dead….good job!” %part3
end
else
puts “You tried to %s but Shia Labeouf caught you. Good job!” %part1
end