Need help: localized a bug but still can't quash it - ruby

The program below is a substantial simplification of one that worked but in spite of hours of effort I can't identify why the following bug occurs:
Everything works as expected except that when the line "if behavior[2,0] > t then goright(brain,stimulus) end" is executed, the line "stimulus.each {|n| stimulus[n,0]=0 }" does NOT reset all of the elements of the stimulus vector to 0 as it does otherwise. Instead the "1" placed there by the previous trip through the "for c in (0..$Choices)" remains thereby generating double behaviors (both a left and right turn) when only one is expected. I've commented out everything that I believe is irrelevant yet it still happens.
class Matrix
def []=(i, j, x)
#rows[i][j] = x
end
end #code to allow putting individual elements in matrix at i,j
brain= Matrix[ [90,0,0,0,0,0,0,0,0,0,0],
[0,90,0,10,10,10,10,0,0,0,0],
[0,0,90,10,10,10,10,0,0,0,0] ]
longmem=Matrix[ [90,0,0,0,0,0,0,0,0,0,0],
[0,90,0,10,10,10,10,0,0,0,0],
[0,0,90,10,10,10,10,0,0,0,0] ]
stimulus=Matrix.column_vector([0,0,0,0,0,0,0,0,0,0,0])
behavior=Matrix.column_vector([0,0,0])
t=89 # t=threshold
# decay_rate=2
$Stimmax=10
$Behavmax=2
$Choices=2
# begin defining behavioral methods
def learn(ix, brain, stimulus)
psp=20
for j in (8..$Stimmax)
if brain[ix,j] > 0 then brain[ix,j]+= 20 end #modified hunting for bug
end # for j
end # learn
#def positive_fixer(brain,stimulus,longmem,energy)
# reinf=0.9
# for i in (0..$Behavmax)
# for j in (7..$Stimmax)
# if longmem[i,j]>0 then longmem[i,j]+=(reinf*(brain[i,j]-longmem[i,j])).round end
# end
# end
# learn(0, brain, stimulus)
#end #positive fixer comment out in bug hunt
def goleft(brain,stimulus)
puts " Left Turn"
learn(1,brain,stimulus)
end # def left
def goright(brain,stimulus)
puts " Right Turn"
learn(2,brain,stimulus)
end # def right
# end defining behavioral methods
# begin MAIN PROGRAM
for c in (0..$Choices)
stimulus.each {|n| stimulus[n,0]=0 }
# SET ALL STIMS TO O
puts "Should by all 0s"
stimulus.to_a.each {|r| puts r.inspect} # aid in bug hunt
stimulus[rand(1..2),0]= 1
#add print stimulus vector for debugging
puts "Should by just a single 1"
stimulus.to_a.each {|r| puts r.inspect} # aid in bug hunt
# memory decay
#for i in (0..$Behavmax)
#for j in (7..$Stimmax)
# if brain[i,j]>longmem[i,j] then brain[i,j]+=-(brain[i,j]-longmem[i,j])/decay_rate end
# if brain[i,j]<longmem[i,j] then brain[i,j]+=-1*((brain[i,j]+longmem[i,j])/decay_rate) end
#end #for j
#end #for i
# memory decay commented out in search for bug
behavior=brain*stimulus
if behavior[0,0] > t then positive_fixer(brain, stimulus, longmem, energy) end
if behavior[1,0] > t then goleft(brain,stimulus) end
if behavior[2,0] > t then goright(brain,stimulus) end
end #for c
puts
brain.to_a.each {|r| puts r.inspect}
# end main program```

the line "stimulus.each {|n| stimulus[n,0]=0 }" does NOT reset all of the elements of the stimulus vector to 0 as it does otherwise
One of the way how to reset vector:
require 'matrix'
vector = Matrix.column_vector([1, 2, 3])
# => Matrix[[1], [2], [3]]
vector.each_with_index { |_, i| vector[i, 0] = 0 }
# => Matrix[[0], [0], [0]]
So you need to use each_with_index instead of each

Related

Trying to cull a set but the set keeps disappearing

I'm trying to program the AI for a Mastermind game in ruby using Donal Knuth's 5 guess algorithm. The game consists of a codemaker, who uses 8 different colored pegs to create a set of 4, and a codebreaker, who guesses at the code and receives feedback (a red square for a peg which is both the right color and in the right spot, and a white square for a peg which is the right color but in the wrong spot).
I've created a set for all possible codes. My goal is to compare feedback from the guess to feedback from all codes in the set, then delete the ones that don't match. It seems to delete the entire set though.
class ComputerPlayer < Player
def initialize(game)
super(game)
#all_possible_codes = create_codes
#turn = 1
end
def get_code
Array.new(4){rand(1..6)}
end
def get_guess
puts #all_possible_codes.length
if #turn == 0
#turn += 1
cull_set([1, 1, 2, 2])
#all_possible_codes.delete("1122")
return [1, 1, 2, 2]
else
random_sample = #all_possible_codes.to_a.sample.split('').map{|str| str.to_i}
#all_possible_codes.delete(random_sample.join(''))
cull_set(random_sample)
random_sample
end
end
def cull_set(guess)
feedback = #game.feedback_on_guess(guess)
puts feedback
#all_possible_codes.delete_if { |str| #game.feedback_on_guess(str.split.map{|num| num.to_i}) != feedback }
end
def create_codes
set = Set.new
(1..8).each do |i|
(1..8).each do |j|
(1..8).each do |k|
(1..8).each do |l|
set << [i, j, k, l].join('')
end
end
end
end
set
end
end
#this is the feedback_on_guess method used by the above class
def feedback_on_guess(code_guess)
code_duplicate = #code
feedback = []
code_duplicate.map.with_index do |entry, i|
if entry == code_guess[i]
feedback.push('r')
code_guess[i] = -1
-2
else
entry
end
end.each do |entry|
found_index = code_guess.find_index(entry)
if found_index
feedback.push('g')
code_guess[found_index] = -1
end
end
puts feedback
feedback
end
Try
copy = something.dup
because after just
copy = something
copy and something are pointing to the same object. You can confirm this by checking the object_id of the object referenced by the variable. If it is the same, then it is the same object.
When you dup an object, you will cretae a copy. Depending on what you want to dup you might need to implement/override the logic to create a copy. For built in Classes like String, Hash and so on it will work out of the box.
Be aware that nested constructs (eq. Hash containing other Hashes) are not duplicated.
h1 = {"a" => {"b" => 2}}
h2 = h1.dup
puts h1.object_id # 70199597610060
puts h2.object_id # 70199597627020
puts h1["a"].object_id # 70199597610080
puts h2["a"].object_id # 70199597610080

Append Hash to Array

I have a little problem with this exercise, can you help me? I tried to edit a script, but for each time that I create new instance the last element overwrites the Others (i.e. )
The class must provide a functionality for the combination of consequent blocks: i.e. it must be able to inherit from the previous block the instructions that have not been explicitly deļ¬ned for the current block.
blocks = []
blocks << Block.new({ X:0, Y:100, Z:20}) # p blocks:({ X:0, Y:100, Z:20})
blocks << Block.new({X:100}, blocks[-1]) #p blocks:({ X:100, Y:100, Z:20},{ X:100, Y:100, Z:20})
blocks << Block.new({Y:0}, blocks[-1]) #p blocks:({ X:100, Y:0, Z:20},{ X:100, Y:0, Z:20},{ X:100, Y:0, Z:20})
My code with problems is:
#!/usr/bin/env ruby
class Block
$instance_2 = true
$instance_3 = true
attr_reader :out
def initialize(out, previous = nil )
#out = update(out, previous)
end # end initialize
def update(actual, previous)
puts "IN UPDATE: box is: #{previous}"
box = previous
if box != nil
puts "IF: changing is: #{actual}"
actual.each do |key, value|
box[key] = value
end
box
else
puts "ELSE"
actual
end # end if
end # end update
def inspect
#out
end
end # end Block
blocks = []
puts "\n#----------------------1 INSTANCE----------------------#"
blocks << Block.new({G: "00", X: 100, Y:100, Z: 100})
puts "\n#----------------------blocks element----------------------#"
p blocks
puts "last block is: #{blocks[-1].out}"
puts "\n#----------------------2 INSTANCE----------------------#" if $instance_2
blocks << Block.new({G: "01"}, blocks[-1].out) if $instance_2
puts "\n#----------------------blocks element----------------------#"if $instance_2
p blocks if $instance_2
puts "last block is: #{blocks[-1].out}"
puts "\n#----------------------3 INSTANCE----------------------#" if $instance_3
blocks << Block.new({G: "02"}, blocks[-1].out) if $instance_3
puts "\n#----------------------blocks element----------------------#" if $instance_3
p blocks if $instance_3
puts "last block is: #{blocks[-1].out}"
puts "\n#----------------------4 INSTANCE----------------------#" if $instance_3
blocks << Block.new({G: "03"}, blocks[-1].out) if $instance_3
puts "\n#----------------------blocks element----------------------#" if $instance_3
p blocks if $instance_3
puts "last block is: #{blocks[-1].out}"
The question is not exactly clear, but if I understand it correctly it is possible to provide a class that may or not accept a previous Block. What do you think of something like that?
#!/usr/bin/env ruby
class Block < Hash
def initialize(h, b=nil)
[:x, :y, :z].each do |s|
# We start by trying to assign the coordinate that is in the
# input hash
if h[s]
self[s] = h[s]
else
# If the coordinate is not in h, we check for it in b, but we have to
# remember that if the block that we are providing in b does not have
# the method :[] or the coordinate is nil we may raise an exception
begin
self[s] = b[s]
raise if not self[s]
rescue
raise(Exception, "Cannot initialize block.")
end
end
end
end
def inspect
"block:(x: #{self[:x]}, y: #{self[:y]}, z: #{self[:z]}"
end
end
# Let's try it!
blocks = []
blocks << Block.new({ x:0, y:100, z:20})
puts blocks
puts
blocks << Block.new({x:100}, blocks[-1])
puts blocks
puts
blocks << Block.new({y:0}, blocks[-1])
puts blocks
puts
With respect to your code
Let's consider only the update method:
def update(actual, previous)
puts "IN UPDATE: box is: #{previous}"
box = previous # ! ! ! ! ! ! ! ! WHOOPS!
if box != nil
puts "IF: changing is: #{actual}"
actual.each do |key, value|
box[key] = value
end
box
else
puts "ELSE"
actual
end # end if
end # end update
the "whoops" line is what is creating you the issue. With that line
you are giving the reference to previous (your object variable actually contains a reference) to the box variable. What you are actually doing, when you perform some action on box, is modifying what is pointed by both variable.
You can test what I'm saying immediately. Try to modify that line in this way:
box = previous.clone if previous
(nil does not have a method #clone that the rationale behind the if). If you run it again you will get a list of blocks that have not being modified. This is not an efficient code tough, and you should really rethink the update method code.

Ruby: String reconstruction algo, working only partially

I'm working on a string reconstruction algorithm (a classic in dynamic programming examples, turning space less text into normal spaced text) in Ruby. The code below is pure ruby, you can copy paste and start testing immediately, it's working 80% of the time and tends to break, the larger the dictionary becomes. I've tested it with more then 80k words dictionaries and it works less good, about 70% of the time.
If there's a way to make it work 100% if the word is present in the dictionary, please show me.
Here's the code: (it's well spaced and should be very readable)
# Partially working string reconstruction algo in pure Ruby
# the dictionary
def dict(someWord)
myArray = [" ", "best", "domain", "my", "successes", "image", "resizer", "high", "tech", "crime", "unit", "name", "edge", "times", "find", "a", "bargain", "free", "spirited", "style", "i", "command", "go", "direct", "to", "harness", "the", "force"]
return !!(myArray.index(someWord))
end
# inspired by http://cseweb.ucsd.edu/classes/wi12/cse202-a/lecture6-final.pdf
## Please uncomment the one you wanna use
#
# (all the words used are present in the dictionary above)
#
# working sentences
x = ' ' + "harnesstheforce"
# x = ' ' + "hightechcrimeunit"
#
# non working sentences
# x = ' ' + "findabargain"
# x = ' ' + "icommand"
puts "Trying to reconstruct #{x}"
# useful variables we're going to use in our algo
n = x.length
k = Array.new(n)
s = Array.new(n)
breakpoints = Hash.new
validBreakpoints = Hash.new
begin
# let's fill k
for i in 0..n-1
k[i] = i
end
# the core algo starts here
s[0] = true
for k in 1..n-1
s[k] = false
for j in 1..k
if s[j-1] && dict(x[j..k])
s[k] = true
# using a hash is just a trick to not have duplicates
breakpoints.store(k, true)
end
end
end
# debug
puts "breakpoints: #{breakpoints.inspect} for #{x}"
# let's create a valid break point vector
i=1
while i <= n-1 do
# we choose the longest valid word
breakpoints.keys.sort.each do |k|
if i >= k
next
end
# debug: when the algo breaks, it does so here and goes into an infinite loop
#puts "x[#{i}..#{k}]: #{x[i..k]}"
if dict(x[i..k])
validBreakpoints[i] = k
end
end
if validBreakpoints[i]
i = validBreakpoints[i] + 1
end
end
# debug
puts "validBreakpoints: #{validBreakpoints.inspect} for #{x}"
# we insert the spaces at the places defined by the valid breakpoints
x = x.strip
i = 0
validBreakpoints.each_key do |key|
validBreakpoints[key] = validBreakpoints[key] + i
i += 1
end
validBreakpoints.each_value do |value|
x.insert(value, ' ')
end
puts "Debug: x: #{x}"
# we capture ctrl-c
rescue SignalException
abort
# end of rescue
end
Note that your algorithm fails for strings containing single-character words. This is an off-by-one error. You are ignoring the breakpoints after such words, thus you end up with a word ("abargain") not contained in your dictionary.
Change
if i >= k
next
end
to
if i > k
next
end
or more Ruby-like
next if i > k
Note also that you are running into an endless loop whenever your string contains something which is not a word:
if validBreakpoints[i] # will be false
i = validBreakpoints[i] + 1 # i not incremented, so start over at the same position
end
You better treat this as an error
return '<no parse>' unless validBreakpoints[i] # or throw if you are not in a function
i = validBreakpoints[i] + 1
The problem with "inotifier" is a deficiency of your algorithm. Always choosing the longest word is not good. In this case, the first "valid" breakpoint detected is after the "in" which leaves you the non-word "otifier".

lost in a sea of rspec

I am so close. I am working to create a simple rspec test for this method. I want it to check that the method receives 'board' and returns a value of c3.
the current test looks like this...
describe 'attempt_win' do
it 'should contain all possible ai win moves' do
#player_computer.attempt_win(#board).should include(#ai_winmoves)
end
it 'should return a win move' do
# need to fake grid input here
board = Board.new
board.grid[:a1] = "O"
board.grid[:b2] = "O"
#player_computer.attempt_win(board).should include("c3") #how to say return 'c3' ??
end
end
the method I'm testing looks like this...
def attempt_win(board)
#keys_with_o = board.grid.select{ |k, v| v == "O" }.keys # find Os on the board
#answers_array = [] # initialize answers array
#ai_winmoves.each do |k, v| # go through each win move in the ai_winmoves array above.
ai_keys = v.select{ |k, v| v == "O"}.keys # grab all computer player's Os from the value hash
intersection = ai_keys & #keys_with_o
# get common elements between two arrays..note: keys_with_o = all current O's on game board
if intersection.length >=2 # when two intersections exist it means two O's are on the board
#answers_array << k # add to answers array per iteration
#answers_array.each do |key|
# answer = #anskey[#thing.last].to_sym
puts "which moves can ai win with?"
puts #anskey[key]
answer = #anskey[key].to_sym
puts "attempt win"
puts answer
if board.grid[answer] == " " #if win move space is empty take it
#move = answer
else #check for a block move
# attempt_block # handled at line 162
end
end
end
end # END #ai_winmoves.each do |k,v|
end
When I run rspec spec, my output related to this test looks like this...
attempt_win
should contain all possible ai win moves
which moves can ai win with?
c3
attempt win
c3
should return a win move (FAILED - 1)
So I need to somehow capture the "c3" in my rspec expectation
anybody point me in the right direction?
ok so the test turned out to be this...
describe 'attempt_win' do
it 'should contain all possible ai win moves' do
#player_computer.attempt_win(#board).should include(#ai_winmoves)
end
it 'should return a win move' do
# need to fake grid input here
myboard = Board.new
myboard.grid[:a1] = "O"
myboard.grid[:b2] = "O"
#player_computer.attempt_win(myboard).should eq(:c3)
end
end
The problem was located in how I was returning my answer in the method...Rspec made me write it correctly...
def attempt_win(board)
#keys_with_o = board.grid.select{ |k, v| v == "O" }.keys # find Os on the board
#answers_array = [] # initialize answers array
#ai_winmoves.each do |k, v| # go through each win move in the ai_winmoves array above.
ai_keys = v.select{ |k, v| v == "O"}.keys # grab all computer player's Os from the value hash
intersection = ai_keys & #keys_with_o
# get common elements between two arrays..note: keys_with_o = all current O's on game board
if intersection.length >=2 # when two intersections exist it means two O's are on the board
#answers_array << k # add to answers array per iteration
#answers_array.each do |key|
# puts "which moves can ai win with?"
# puts #anskey[key]
answer = #anskey[key].to_sym
# puts "attempt win"
# puts answer
if board.grid[answer] == " " #if win move space is empty take it
#move = answer
else #check for a block move
# attempt_block # handled at line 162
end
end
end
end # END #ai_winmoves.each do |k,v|
return #move
end

Passing arguments to functions in ruby using variables

Following the tutorial Zed A. Shaw, I'm writing a soccer game. My code so far is as follows:
$team_a_players = ["Basar", "Mehmet", "Abdullah", "Alpaslan", "Salih", "Recep", "Ibrahim", "Orhan", "Hakki", "Yakup", "Serdar"]
$team_a_substitutes = ["Hasan", "Turgay", "Umit"]
$team_b_players = ["Habib", "Erkan", "Sahin", "Cemal", "Ahmet", "Fikret", "Yucel", "Pergel", "Ali", "Sabri", "Yilmaz"]
$team_b_substitutes = ["Abdulkadir", "Gokhan", "Mustafa"]
$yellow = []
$red = []
$reasons = ["corner", "direct attack", "free kick", "side attack", "speed kick"]
$team_a_attack = 90.0
$team_a_defense = 80.0
$team_b_attack = 70.0
$team_b_defense = 60.0
$team_a_goals = 0
$team_b_goals = 0
def prompt()
print "> "
end
def dice()
if rand(2) == 0
round_team_a()
else
round_team_b()
end
end
def fauls()
if rand(0) > 0.95 and rand(10) % 2 == 0
faul_player = $team_a_players[rand(11)]
if $yellow.include?(faul_player)
$red.push(faul_player)
$team_a_players.delete("faulplayer")
puts "#{faul_player} of Team A gets a red card in #{$i}. minute!"
puts "Who would you like to substitute in place of #{faul_player}?"
list_subs_a()
prompt()
substitute = STDIN.gets.chomp()
$team_a_players.push("substitute")
$yellow.delete(faul_player)
else
$yellow.push(faul_player)
puts "#{faul_player} of Team A gets a yellow card in #{$i}. minute!"
end
elsif rand(0) > 0.95 and rand(10) % 2 == 1
faul_player = $team_b_players[rand(11)]
if $yellow.include?(faul_player)
$red.push(faul_player)
$team_b_players.delete("faulplayer")
puts "#{faul_player} of Team B gets a red card in #{$i}. minute!"
puts "Who would you like to substitute in place of #{faul_player}?"
list_subs_b()
prompt()
substitute = STDIN.gets.chomp()
$team_b_players.push("substitute")
$yellow.delete(faul_player)
else
$yellow.push(faul_player)
puts "#{faul_player} of Team B gets a yellow card in #{$i}. minute!"
end
else
faul_player = nil
end
end
def list_subs_a()
$team_a_substitutes.each {|p| puts p}
end
def list_subs_b()
$team_b_substitutes.each {|p| puts p}
end
def list_yellow()
$yellow.each {|p| puts p}
end
def list_red()
$red.each {|p| puts p}
end
def round_team_a()
score = $team_a_attack / $team_b_defense * rand(0)
if score > 1
goal = 1
$team_a_goals += 1
reason = $reasons[rand(5)]
puts "Team A scored #{goal} goal through a #{reason} in #{$i}. minute!"
else
goal = 0
end
end
def round_team_b()
score = $team_b_attack / $team_a_defense * rand(0)
if score > 1
goal = 1
$team_b_goals += 1
reason = $reasons[rand(5)]
puts "Team B scored #{goal} goal through a #{reason} in #{$i}. minute!"
else
goal = 0
end
end
def match()
$i = 0
until $i > 59 do
dice()
fauls()
$i += 1
end
puts "Team A scored a total of #{$team_a_goals} goals and Team B scored a total of #{$team_b_goals} goals."
if $team_a_goals > $team_b_goals
puts "Team A won against Team B by #{$team_a_goals}:#{$team_b_goals}!"
elsif $team_b_goals > $team_a_goals
puts "Team B won against Team A by #{$team_b_goals}:#{$team_b_goals}!"
else
puts "It's a tie!"
end
if $yellow.length > 0
puts "Players shown a yellow card are:"
list_yellow()
else
puts "No yellow cards in the end of the game"
end
if $red.length > 0
puts "Players shown a red card are:"
list_red()
else
puts "No red cards in the end of the game"
end
end
match()
From here, I would like to do the following:
Replace the arrays $yellow and $red with hashes so that I can also report minutes and teams of yellow- and red-cards.
Replace the arrays starting with the name $team_ with hashes so that I can add individualized attack- and defense-powers to players so that substitutions mean sth. but before the code gets any more complex, I have to solve sth. This looks similar to this question concerning php.
Define the functions list, round and faul in a way that can be used common to a_players and b_players . I tried doing team_#{team}_players instead of team_a_players etc, but cannot achieve it.
What I seek is a guide to that problem, not a solution, so that I can fix this myself. A link or long explanation in clear words is very much more welcome than a fixed code. And please note that the tutorial has not mentioned classes yet, so this is not an option yet.
The basic idea you haven't seemed to grasp is passing arguments to functions.
Assume we have two global variables and we wish to perform identical operations on them - say multiply elements of arrays by 2.
You write:
$a = [1,2,3]
$b = [2,3,4]
def multiply_a
result = []
for element in $a do
result << element * 2
end
result
end
def multiply_b
result = []
for element in $b do
result << element * 2
end
result
end
But this code is very bad. First of all, you should note that in Ruby $a is a special variable - a global variable. You should never need to use them - writing code containing them means that there is something wrong with it. So how to fix it?
The basic idea is to pass an argument to a function, instead of hard coding the variable, the function operates on. So you can transform the code as follows:
a = [1, 2, 3]
b = [2, 3, 4]
def multiply(argument)
result = []
for element in argument do
result << element * 2
end
result
end
# and then call
multiply(a) # instead of multiply_a
multiply(b) # instead of multiply_b
This is the way you should fix your code.

Resources