Wednesday, February 17, 2010

Ruby and Simple Dynamic Programming V

I've been reading Paolo Perrotta's new book "Metaprogramming Ruby" recently and enjoying it thoroughly. In the first chapter, he talks about Monkey Patching, the ability to add methods to classes at runtime. This combined with a need recently for an iterator through an array that returns both the index and the value led me to come up with the following code:

# Open the Array class and add a new method "each_with_index".
# This will loop through the array using the each_index method
# and yeild the index and the value of the array at that index.
class Array
def each_with_index
self.each_index do | i |
yield i, self[i]
end
end
end

if __FILE__ == $PROGRAM_NAME

# Create a simple array
x = [ "hello", "world", 42, 3.14159, ["test", "an", "array"] ]

# Go through the array and print each index and value in the
# array.
x.each_with_index do | i, v |
puts "index = #{i} value = #{v}"
end


end



The code is pretty simple. We open the Arrary class, define the new method, and then close the class. There's a bit of test code after that to show that everything works as expected.

Even though I'm only through the first chapter, I can recommend Perrotta's book. It's an easy read (so far anyway) and clearly shows the concepts that it discusses. The form it takes is conversational and the narrative is that you're a programmer, new to Ruby, who is paired with a senior developer Bill. This format may not be for everyone, especially if you're looking for more "formality", but I think it works well here and it's not overdone.

Let me know if you have questions or comments.

Wednesday, February 10, 2010

Beginning a Program

A young friend (YF) of mine was having a problem with a programming assignment from a large local university (LLU) and gave me a call the other day for some help. We spent a couple of hours together going through the piece that he was having issues with and finally got everything pretty much working. This episode though showed me that we don't necessarily teach the right things in college concerning how approach a program. I thought I'd take a few posts and show how I might tackle a problem like this. Not everyone will find this way of doing things appropriate and I wouldn't use it in certain cases, but it may help some of you in some cases.

Here's the problem as given:


Assignment Details
Homework 1 - Non-GUI Tower Defender
Players start with a fixed amount of money, in less they are a returning player. In that case, they are to start with the money they ended up with, or they can start over, if they wish.

Towers:There are to be 4 types of towers. Each has a different capability - you get to decide. Each has a different price - that you decide. A player starts the game by deciding which towers they want and where they are to be put. Given that the path for Creeps is only from left to right, towers can be above, or below, the path. The player gets to decide that, and the "x-direction" for the tower location.

Since this first assignment is not graphical - only command line - the Creep path is straight from left to right. You are to use keyboard characters as your graphics to display what is happening - tower location, and Creep location.

Creeps:Creeps may move one, or more, squares, from left to right each "turn". You get to decide how big squares are. Too big, and Creeps will easily get to the base - which is on the right side of the window. Creeps start from the left side of the display window. You are to have three (3) types of Creeps. Their behavior is up to you, but just moving faster, or slower, does not qualify as a different Creep type. Each Creep is worth a different amount of money - depending on how hard they are to kill.

Statistics: You are to track the number of Creeps killed (for each Creep type), the total number of times this player has played, the total amount of money they have spent, and the total amount of money they have earned. You may choose to have additional statistics.


There are a few things here that are worth noting. First the requirements are pretty loose. You have some options for doing things how you like (this may be good or bad depending on your temperament. Second there are a couple of things that don't make sense. We have the "x-direction" for the tower location. I'm pretty sure that what's meant is the "x-position", but if I were doing this for a grade at LLU, I'd check with the prof. I checked with YF and it sounds like since the towers don't move, the x-position is what is meant.

As a first cut, I'd spend my time working on the "logistics" of the program. What do I mean by that? Well, we'll have a main program that loops getting input from a player. We're going to have to create a player, give her some money, be able to save her totals when she quits, and on initialization, we're going to have to check if she's played before and if so if she'd like to resume or start a new game.

With that out of the way, here's what I came up with (this is in Ruby, YF for LLU had to write this in Java).

# Assignment Details Homework 1 - Non-GUI Tower Defender Players start with a
# fixed amount of money, in less they are a returning player. In that case,
# they are to start with the money they ended up with, or they can start over,
# if they wish.
#
# Towers: There are to be 4 types of towers. Each has a different capability -
# you get to decide. Each has a different price - that you decide. A player
# starts the game by deciding which towers they want and where they are to be
# put. Given that the path for Creeps is only from left to right, towers can be
# above, or below, the path. The player gets to decide that, and the
# "x-direction" for the tower location.
#
# Since this first assignment is not graphical - only command line - the Creep
# path is straight from left to right. You are to use keyboard characters as
# your graphics to display what is happening - tower location, and Creep
# location.
#
# Creeps: Creeps may move one, or more, squares, from left to right each "turn".
# You get to decide how big squares are. Too big, and Creeps will easily get to
# the base - which is on the right side of the window. Creeps start from the
# left side of the display window. You are to have three (3) types of Creeps.
# Their behavior is up to you, but just moving faster, or slower, does not
# qualify as a different Creep type. Each Creep is worth a different amount of
# money - depending on how hard they are to kill.
#
# Statistics: You are to track the number of Creeps killed (for each Creep
# type), the total number of times this player has played, the total amount of
# money they have spent, and the total amount of money they have earned. You
# may choose to have additional statistics.
#

class Tower
end

class Creep
end

class Game
end

class Player

# Initialize a player.
def initialize(name)
@name = name
@dollars = 100
@file_name = "#{@name}.txt"

if File.exist?(@file_name)
print "Welcome back #{@name}. Would you like to resume(r) or start a new game(n)?"
command = gets.chomp
case command
when "r"
puts "Resuming from #{@file_name}"
File.open(@file_name, "r") do |player_file|
line = player_file.gets
if line != nil
name_f, @dollars = line.split(",")
puts "Resume: #{name_f}: Dollars" #{dollars_f}"
else
puts "Could not read line"
end
end
when "n"
puts "Starting new game"
end
end

end

# Save the players and stats to a file.
def save
player_file = File.new(@file_name, "w+")
player_file.puts "#{@name}, #{@dollars}"
player_file.close
end

# Convenience funtion to get the player and their current
# stats.
def to_s
"name: #{@name}, dollars: #{@dollars}"
end

end

if __FILE__ == $PROGRAM_NAME

# Get the player name
print "Name: "
name = gets.chomp

# Create a new player
player = Player.new(name)
puts "#{player}"

# Do forever (or until a 'q' command anyway)
while true do

# Get the command
print "Command: "
command = gets.chomp

# Check the command and execute it.
case command
when "q"
# Quit so go ahead and save the player then break out
# of the loop.
player.save
break
end
end
end



You can see that I put the requirements at the top so they're pretty much always available for checking (you obviously can't do this with really big requirements, but when you can, it's not a bad idea). There are place holders for the classes that I think we'll have, but those may change. The one that's filled out, the Player class, has just three methods. initialize(), sets the player up and checks if the do/don't already have a saved game, save() saves the players current data, and to_s() let's us print out their data in a readable format. As we go along, these may (really will) change and get refactored, but for now, they'll suffice. The main program asks the user for their name and creates a player. We then start the main loop that just gets a command (here only the quit command) and processes it. Quit simply saves the players data and breaks out of the loop which then exits.

This would probably take less than 1/2 an hour for someone who knows the language they're working in fairly well. What is does though is provide us with places to hang major pieces of the game going forward.

Let me know if you have questions or comments and look for the next edition where we'll add in a bit more functionality to the program.