http://wcarss.ca/blog
Thanks!
The Guelph Seven
13 years ago
class World
def initialize(x_size=20, y_size=20, building_control=10)
remake(x_size, y_size, building_control)
end
def remake(x_size=20, y_size=20, building_control=10)
@x_size = x_size
@y_size = y_size
@building_control = building_control
@map = createMap
placeBuildings
placeRobot
end
def createMap
map = Array.new(@x_size)
map.each_index do |col|
map[col] = Array.new(y_size)
map[col].each_index do |i|
map[col][i] = " "
end
end
map
end
def display
@map.each do |col|
col.each do |i|
printf(i)
end
puts ""
end
end
def placeBuildings
1.upto(@building_control) do
placeObject("B")
end
# fills the world with buildings
end
def placeRobot
placeObject("P")
# puts Robot in a place where buildings aren't
end
def placeObject(string)
placed = false
while placed == false
x = rand(@x_size)
y = rand(@y_size)
if @map[x][y] == " " then
@map[x][y] = string
placed = true
end
end
end
def line_of_sight
# returns position of next visible object
end
attr_reader :map, :x_size, :y_size, :building_control
end
require 'test/unit'
require 'src/world.rb'
class WorldTest < Test::Unit::TestCase
@@x_size_list = Array.new(10) {|i| i+10}
@@y_size_list = Array.new(10) {|i| i+10}
@@building_control_list = Array.new(3) {|i| i+50}
def setup
@space_count = 0
@building_count = 0
@player_count = 0
@other_count = 0
@w = World.new(@@x_size_list[0], @@y_size_list[0], @@building_control_list[0])
end
must "properly set the x_size parameter" do
assert_equal @w.x_size, @@x_size_list[0]
end
must "properly set the y_size parameter" do
assert_equal @w.y_size, @@y_size_list[0]
end
must "properly set the building control parameter" do
assert_equal @w.building_control, @@building_control_list[0]
end
must "generate a 2D map" do
assert_equal @w.map[0][0].class, String
end
must "generate a 2D map to at least the specified dimensions" do
assert_equal @w.map[@w.x_size-1][@w.y_size-1].class, String
end
must "generate a 2D map not exceeding the specified dimensions" do
assert_raises(NoMethodError) do
assert_equal @w.map[@w.x_size][@w.y_size].class, String
end
end
@@x_size_list.each do |x|
@@y_size_list.each do |y|
@@building_control_list.each do |b|
# puts "#{x} #{y} #{b}: layout"
must "place #{b} buildings on size #{x} * #{y}" do
# puts "#{x}, #{y}, #{b}: buildings"
@w.remake(x, y, b)
count_map_contents
assert_equal b, @w.building_control
assert_equal b, @building_count
end
must "place #{(x * y) - (b+1)} spaces on size #{x} * #{y}" do
# puts "#{x}, #{y}, #{b}: spaces"
@w.remake(x, y, b)
count_map_contents
assert_equal ((x * y) - (b+1)), @space_count
end
must "place 1 player on size #{x} * #{y}, building control #{b}" do
# puts "#{x}, #{y}, #{b}: 1 player"
@w.remake(x, y, b)
count_map_contents
assert_equal 1, @player_count
end
must "not put any ambiguous spaces on size #{x} * #{y}, building control #{b}" do
# puts "#{x}, #{y}, #{b}: no ambiguous"
@w.remake(x, y, b)
count_map_contents
assert_equal 0, @other_count
end
end
end
end
def count_map_contents
@w.map.each_index do |col|
@w.map[col].each do |i|
if i == "B" then
@building_count += 1
elsif i == " " then
@space_count += 1
elsif i == "P" then
@player_count += 1
else
@other_count += 1
end
end
end
end
end
require "rake/testtask"test/test_game.rb
task :default => [:test]
Rake::TestTask.new do |test|
test.libs << "test"
test.test_files = Dir[ "test/test_*.rb" ]
test.verbose = true
end
require 'test/unit'src/game.rb
require 'test/extension.rb'
require 'stringio'
require 'src/game.rb'
class GameTest < Test::Unit::TestCase
command_list = ["left", "right", "forward",
"go", "back", "shoot", "pew",
"pewpew", "map", "look", "check"]
bad_command_list = ["up", "down", "north", "3", "go wildcats",
"wepwep", "a map", "map ", "go map"]
def setup
@input = StringIO.new
@game = Game.new(@input)
end
command_list.each do |command|
must "set the inputted command: \"#{command}\"" do
#puts "set the inputted command correctly when parsing #{command}"
provide_input(command)
@game.get_command
assert_equal @game.command, command
end
end
command_list.each do |command|
bad_command_list.each do |bad_command|
must "retain the command \"#{command}\" and fail to set \"#{bad_command}\"" do
provide_input(command)
@game.get_command
provide_input(bad_command)
@game.get_command
assert_equal @game.command, command
end
end
end
def provide_input(string)
remember = @input.pos
@input << string
@input.pos = remember
end
end
class GameThis has all gone rather well. Running 'rake test' gives:
attr_reader :command
def initialize(readin=STDIN, output=STDOUT)
@input = readin
@output = output
@command_list = ["left", "right", "go", "back",
"shoot", "check", "look", "pew",
"pewpew", "exit", "map", "forward"]
end
def get_command
temp_command = @input.gets
temp_command.chomp!
if @command_list.index(temp_command) != nil then
@command = temp_command
end
end
def loop
while @command != "exit"
get_command
puts @command
end
end
end
(in /media/sda1/Users/wcarss/code/ruby)Excellent! Now that's out of the way...
/usr/bin/ruby1.8 -I"lib:test" "/usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader.rb" "test/test_game.rb"
Loaded suite /usr/lib/ruby/gems/1.8/gems/rake-0.8.7/lib/rake/rake_test_loader
Started
..............................................................................................................
Finished in 0.012626 seconds.
110 tests, 110 assertions, 0 failures, 0 errors
require 'test/unit'robot.rb
require 'test/extension.rb'
require 'src/robot.rb'
require 'matrix'
class RobotTest < Test::Unit::TestCase
directions = ["north", "east", "south", "west"]
base_pos = Vector[5,5]
forward_list = ["go", "forward"]
backward_list = ["back", "backward"]
new_pos = [Vector[0,1], Vector[1,0], Vector[0,-1], Vector[-1,0]]
def setup
@bot = Robot.new
end
directions.each_index do |i|
must "change direction to #{directions[i]} from #{directions[i-1]} on right" do
@bot.direction = directions[i-1]
@bot.right
assert_equal directions[i], @bot.direction
end
end
directions.each_index do |i|
must "change direction to #{directions[i-1]} from #{directions[i]} on left" do
@bot.direction = directions[i]
@bot.left
assert_equal directions[i-1], @bot.direction
end
end
forward_list.each do |command|
directions.each_index do |i|
must "move correctly forward when facing #{directions[i]}, using #{command}" do
@bot.pos = base_pos
@bot.direction = directions[i]
@bot.send(:"#{command}")
assert_equal @bot.pos, base_pos + new_pos[i]
end
end
end
backward_list.each do |command|
directions.each_index do |i|
must "move correctly backward when facing #{directions[i]}, using #{command}" do
@bot.pos = base_pos
@bot.direction = directions[i]
@bot.send(:"#{command}")
assert_equal @bot.pos, base_pos - new_pos[i]
end
end
end
end
require 'matrix'So, what on earth's going on here? Hopefully a lot of it is self-evident! The robot knows 4 directions, and I generated tests to check that left/right properly cycle through those. Then I generated a set of tests to check that, for every command, for every direction, the robot alters its position correctly. For example, if facing north using 'go', the robot should increment its y-position by 1. (I'm using north as positive, south as negative y, while west is negative x and east is positive x).
class Robot
attr_reader :pos, :direction, :name, :score
attr_writer :pos, :direction, :name, :score
@@directions = ["north", "east", "south", "west"]
def initialize(name="Killbot 4000", x_pos = 0, y_pos = 0, direction="north")
@pos = Vector[x_pos, y_pos]
@name = name
@direction = direction
end
def left
@direction = @@directions[ direction_as_int() - 1]
end
def right
@direction = @@directions[ (direction_as_int() + 1) % @@directions.size]
end
def direction_as_int
@@directions.index(@direction)
end
def next_pos
angle = 90 * direction_as_int() * Math::PI/180
Vector[Math.sin(angle).round, Math.cos(angle).round]
end
def forward
@pos = @pos + next_pos()
end
def backward
@pos = @pos - next_pos()
end
def look
puts "Your windows are fogged!"
# not implemented
end
def shoot
puts "KABOOM (probably)"
# not implemented
end
alias back backward
alias go forward
alias check look
alias pew shoot
alias pewpew shoot
end
class GameTest < Test::Unit::TestCasenot having run this yet or ever done testing in ruby before, I have /no idea/ if this will actually work. Having written it though, I'm going to throw my Game class together (which now essentially writes itself)
def setup
@commandlist = ["left", "right", "forward",
"go", "back", "shoot", "pew",
"pewpew", "map", "look", "check"]
@input = StringIO.new
@game = Game.new(@input)
end
commandlist.each do |command|
must "set the inputted command correctly when parsing #{command}" do
provide_input(command)
Game.get_command
assert_equal Game.command, command
end
end
def provide_input(string)
@input << string
@input.rewind
end
end
class GameI don't have rake or rubygems on this computer, so I can't test this yet! I'll have to do so a little later - after a quick manual test of the Game class, it looks like it is in the right place. I realized while making it though, that I forgot to test that I'm not taking inappropriate commands! Fortunately I built it into the game class, and I'll modify the test soon to check for it.
attr_reader :command
def initialize(readin=STDIN, output=STDOUT)
@input = readin
@output = output
@command_list = ["left", "right", "go", "back",
"shoot", "check", "look", "pew",
"pewpew", "exit", "map", "forward"]
end
def get_command
temp_command = @input.gets
temp_command.chomp!
if @command_list.index(temp_command) != nil then
@command = temp_command
end
end
def loop
while @command != "exit"
get_command
puts @command
end
end
end
/ah, and what exactly is in the rakefile? I've never used rake before! But again lifting from Ruby Best Practices (a fantastic book!), I've got
/Rakefile
/src/
/src/game.rb
/test/
/test/test_game.rb
require "rake/testtask"And that's where I'll leave it for now. More later when I can actually run tests and find out all the things I've done incorrectly. :)
task :default => [:test]
Rake::TestTask.new do |test|
test.libs << "test"
test.test_files = Dir[ "test/test_*.rb" ]
test.verbose = true
end