lost in a sea of rspec - ruby

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

Related

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

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

Building tic tac toe game through scratch stuck while checking winning

I can play the game. Switch the player all working fine but not getting result who won the game.
def initialize_board
#count = 9
#player = PLAYER_ONE #current_player
#board = Array.new(3){ Array.new(3, " ") }
end
def play
inputs = get_inputs
return false if !inputs
update_board(inputs)
print_board
end
def switch_player
if(#player == PLAYER_ONE)
#player = PLAYER_TWO
else
#player = PLAYER_ONE
end
end
def game_over?
# #count = #count - 1
# #count <= 0
if check_winner
puts "#{#player} won "
end
end
def check_winner
WIN_COMBINATIONS.find do |indices|
binding.pry
values = #board.values_at(*indices)
values.all?('X') || values.all?('O')
end
end
Here I am getting indices [0,1,2] in all cases while debugging.
The main reason why you're not getting the winner is because your 'values = #board.values_at(*indices)' statement returns an array of arrays. And values.all?('X') || values.all?('O') checks not an 'X' or 'O' pattern but an array object. So you need to flatten an array first.
values.flatten!
Stefan already answered similar question , but his board was one-dimensional because of %w expression, you can read about it here

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

having trouble with .next method making a casear cipher

I'm not worried about what happens if my key will go past Z right now, or capital letters. All I want is my outcome to be something like. text=abc key=2 and it print "cde". Where am I going wrong?
puts "What would you like to cipher?"
text = gets.chomp
puts " what number key would you like?"
key = gets.chomp.to_i
def casear_cipher(text,key)
ciphered_text = []
text.chars.each do |letter|
ciphered_text = letter
ciphered_text = ciphered_text.next
end
end
puts casear_cipher(text,key)
You're not using the key yet, so it will always just do abc -> bcd. If you're really not concerned about "Z" going to "AA", you can try this:
def cipher(text, key)
text.chars.map { |c| (c.ord + key).chr }.join
end
Since 'Z'.next => 'AA' and 'z'.next #=> 'aa', we can use [-1] to select the last letter.
In the code below we perform next! on each character n times using the times method. next! modifies the character whereas next does not.
def casear_cipher(text, n)
text.chars.map do |c| n.times { c.next! }
c[-1]
end.join
end
p casear_cipher('abc',2) #=> "cde"
p casear_cipher('xyz',2) #=> "zab"
p casear_cipher('ZEBRA',2) #=> "BGDTC"
More information about these methods can be found at http://www.ruby-doc.org/core-2.4.1/

Recursively merge multidimensional arrays, hashes and symbols

I need a chunk of Ruby code to combine an array of contents like such:
[{:dim_location=>[{:dim_city=>:dim_state}]},
:dim_marital_status,
{:dim_location=>[:dim_zip, :dim_business]}]
into:
[{:dim_location => [:dim_business, {:dim_city=>:dim_state}, :dim_zip]},
:dim_marital_status]
It needs to support an arbitrary level of depth, though the depth will rarely be beyond 8 levels deep.
Revised after comment:
source = [{:dim_location=>[{:dim_city=>:dim_state}]}, :dim_marital_status, {:dim_location=>[:dim_zip, :dim_business]}]
expected = [{:dim_location => [:dim_business, {:dim_city=>:dim_state}, :dim_zip]}, :dim_marital_status]
source2 = [{:dim_location=>{:dim_city=>:dim_state}}, {:dim_location=>:dim_city}]
def merge_dim_locations(array)
return array unless array.is_a?(Array)
values = array.dup
dim_locations = values.select {|x| x.is_a?(Hash) && x.has_key?(:dim_location)}
old_index = values.index(dim_locations[0]) unless dim_locations.empty?
merged = dim_locations.inject({}) do |memo, obj|
values.delete(obj)
x = merge_dim_locations(obj[:dim_location])
if x.is_a?(Array)
memo[:dim_location] = (memo[:dim_location] || []) + x
else
memo[:dim_location] ||= []
memo[:dim_location] << x
end
memo
end
unless merged.empty?
values.insert(old_index, merged)
end
values
end
puts "source1:"
puts source.inspect
puts "result1:"
puts merge_dim_locations(source).inspect
puts "expected1:"
puts expected.inspect
puts "\nsource2:"
puts source2.inspect
puts "result2:"
puts merge_dim_locations(source2).inspect
I don't think there's enough detail in your question to give you a complete answer, but this might get you started:
class Hash
def recursive_merge!(other)
other.keys.each do |k|
if self[k].is_a?(Array) && other[k].is_a?(Array)
self[k] += other[k]
elsif self[k].is_a?(Hash) && other[k].is_a?(Hash)
self[k].recursive_merge!(other[k])
else
self[k] = other[k]
end
end
self
end
end

Resources