Changing a value in an array of random numbers in a loop - ruby

I'm trying to make a loop that when the random number matches the same index of the input the value is changed and stays that way for each following loop.
input = gets.chomp
tries_left = 12
while(tries_left > 0)
tries_left -= 1
computer = 4.times.map do rand(0..6) end.join
if computer[0] == input[0]
computer[0] = input[0]
end
end
in the code above after the first loop the value stored to input[0] resets.
computer = 4.times.map do rand(0..6) end.join
input = gets.chomp
tries_left = 12
while(tries_left > 0)
tries_left -= 1
if computer[0] == input[0]
computer[0] = input[0]
end
if I take computer out of the loop like this, it will have the same random number generated each time. Once again I need it to generate new numbers each time besides what was already a match.

If you make the computer an array of strings, you can freeze it to prevent further modifications to it, and then replace the contents in computer when it doesn't match the index:
input = gets.chomp
tries_left = 12
computer = Array.new(4) { '' }
# setting the srand to 1234, the next 48 calls to 'rand(0..6)' will always
# result in the following sequence:
# 3, 6, 5, 4, 4, 0, 1, 1, 1, 2, 6, 3, 6, 4, 4, 2, 6, 2, 0, 0, 4, 5, 0, 1,
# 6, 6, 2, 0, 3, 4, 5, 2, 6, 2, 3, 3, 0, 1, 3, 0, 3, 2, 3, 4, 1, 3, 3, 3
# this is useful for testing things are working correctly,
# but take it out for 'live' code
srand 1234
while tries_left > 0
# no need to keep iterating if we've generated all the correct values
if computer.all?(&:frozen?)
puts "won #{computer.inspect} in #{12 - tries_left} tries"
break
end
tries_left -= 1
computer.each.with_index do |random, index|
# generate a new random number here unless they guessed correctly previously
random.replace(rand(0..6).to_s) unless random.frozen?
# if they've guessed the new random number, mark the string so they we
# don't update it
random.freeze if random == input[index]
end
puts "#{computer.inspect} has #{computer.count(&:frozen?)} correct numbers"
end
and then when you run the script:
$ echo 3654 | ruby example.rb
# ["3", "6", "5", "4"] has 4 correct numbers
# won ["3", "6", "5", "4"] in 1 tries
$ echo 3644 | ruby example.rb
# ["3", "6", "5", "4"] has 3 correct numbers
# ["3", "6", "4", "4"] has 4 correct numbers
# won ["3", "6", "4", "4"] in 2 tries
$ echo 3555 | ruby example.rb
# ["3", "6", "5", "4"] has 2 correct numbers
# ["3", "4", "5", "0"] has 2 correct numbers
# ["3", "1", "5", "1"] has 2 correct numbers
# ["3", "1", "5", "2"] has 2 correct numbers
# ["3", "6", "5", "3"] has 2 correct numbers
# ["3", "6", "5", "4"] has 2 correct numbers
# ["3", "4", "5", "2"] has 2 correct numbers
# ["3", "6", "5", "2"] has 2 correct numbers
# ["3", "0", "5", "0"] has 2 correct numbers
# ["3", "4", "5", "5"] has 3 correct numbers
# ["3", "0", "5", "5"] has 3 correct numbers
# ["3", "1", "5", "5"] has 3 correct numbers

It’s not quite clear what you are trying to accomplish, but this:
if computer[0] == input[0]
computer[0] = input[0]
end
is obviously a noop. Nothing gets updated, since the computer[0], whatever it is, is being set to the same value it was. I believe you wanted to somehow use an index in the array:
4.times.map do |index|
value = rand(0..6)
# somehow check the similarity, e.g.:
if input[index] == value
# do something
end
end
I beg your pardon for the very vague answer, but it is really hard to understand the goal you are trying to achieve.

Related

How to split a long number into pairs of digits

I have a string of a long number 12345678 and want to convert it to an array like this :
["12", "34", "56", "78"].
I have tried array.split(//).map { |e| e.to_i } but it does
["1", "2", "3", "4", "5", "6", "7", "8"]
The simplest way would be to use String#scan with the Regexp /../, which matches any pair of characters:
n = 12345678
arr = n.to_s.scan(/../)
# => ["12", "34", "56", "78"]
If you need to handle odd numbers of digits and keep the last digit, use /..?/ instead:
n = 123456789
arr = n.to_s.scan(/..?/)
# => ["12", "34", "56", "78", "9"]
An alternative approach would be to map over the range 0...n.to_s.size using the Range#step method:
n = 123456789
str = n.to_s
arr = (0...str.size).step(2).map {|i| str[i, 2] }
# => ["12", "34", "56", "78", "9"]
You can see all three approaches in action on repl.it: https://repl.it/#jrunning/BlissfulAcclaimedStrategy
I think there is no need to use a regexp, I would do something like this:
12345678.to_s.chars.each_slice(2).map(&:join)
#=> ["12","34","56","78"]
You can use
p = 123456789.to_s
(0..p.length).each_cons(2).map {|i, j| "#{p[i]}#{p[j]}" if i%2 == 0}.compact

Multiply all even indexed integers by two

Wanting to take a fixnum of integers and multiply all even(indexed) integers by two. I figured the best way to do this is first turn fixnum into an array. So lets say the following number of 16 digits: a = 4408041234567901
I know I could:
a.to_s.split('')
Which will return 'a' to an array of 'stringed' numbers. But then I cant follow up with:
a.map!.with_index {|i,n| i.even? n*2}
Guess I'm kinda stuck on how to create a method to do this. So my question may even be how to turn that group of numbers into an array of fixnums/integers instead of strings.
I would prefer to remove the conditional altogether from the loop, by creating an Enumerator that contains the coefficients you want to multiply by (2 for even indexes and 1 for odd.)
coef = [2, 1].cycle
This essentially creates an Enumerator that alternately returns 2 and 1 when next is called on it. You can then use this to simplify your map to:
a.to_s.each_char.map { |v| v.to_i * coef.next }
To change it to an Array, you could do
a = 4408041234567901
arr = a.to_s.chars.map(&:to_i)
# => [4, 4, 0, 8, 0, 4, 1, 2, 3, 4, 5, 6, 7, 9, 0, 1]
You can also multiply alternate numbers by 2
arr = a.to_s.chars.map.with_index {|n,i| i.even? ? n.to_i * 2 : n.to_i }
# => [8, 4, 0, 8, 0, 4, 2, 2, 6, 4, 10, 6, 14, 9, 0, 1]
Improving a little bit, you can use a Hash to find the number to be multiplied.
h = {true => 2, false => 1}
a.to_s.each_char.map.with_index {|n,i| n.to_i * h[i.even?]}
EDIT
I can explain each step, But it will be better if you can try to figure it out on your own. Open irb, type a.to_s and check the output. Then type a.to_s.chars and inspect the output and so on..
a = 4408041234567901
even_odd = [:even, :odd].cycle
#=> #<Enumerator: [:even, :odd]:cycle>
If the indexing starts with the highest-order (leftmost) digit:
a.to_s.each_char.map { |d|
(even_odd.next == :even) ? 2*d.to_i : d.to_i }
#=> [8, 4, 0, 8, 0, 4, 2, 2, 6, 4, 10, 6, 14, 9, 0, 1]
If the indexing starts with the ones digit:
s = a.to_s
even_odd.next if s.size.even?
s.each_char.map { |d| ( even_odd.next == :even) ? 2*d.to_i : d.to_i }
#=> [4, 8, 0, 16, 0, 8, 1, 4, 3, 8, 5, 12, 7, 18, 0, 2]
Here are the steps for the example when zero-based indexing starts with the highest-order digit.
Array#cycle converts the array [:even, :odd] to an enumerator:
even_odd = [:even, :odd].cycle
even_odd.next #=> :even
even_odd.next #=> :odd
even_odd.next #=> :even
even_odd.next #=> :odd
...
b = a.to_s
#=> "4408041234567901"
enum0 = b.each_char
#=> #<Enumerator: "4408041234567901":each_char>
The enumerator enum0 passes the digits of b to map. I could have instead written:
b = a.to_s.chars
# => ["4", "4", "0", "8", "0", "4", "1", "2",
# "3", "4", "5", "6", "7", "9", "0", "1"]
but that creates an intermediate array. The enumerator does not and therefore is more efficient. Continuing...
enum1 = enum0.map
#=> #<Enumerator: #<Enumerator: "4408041234567901":each_char>:map>
You can think of this as a "compound enumerator". We can see its contents by converting it to an array:
enum1.to_a
#=> ["4", "4", "0", "8", "0", "4", "1", "2",
# "3", "4", "5", "6", "7", "9", "0", "1"]
The method each will pass each element of the enumerator into the block. Proof:
enum1.each { |d| (enum.next == :even) ? 2*d.to_i : d.to_i }
# => [8, 4, 0, 8, 0, 4, 2, 2, 6, 4, 10, 6, 14, 9, 0, 1]
We can manually step through the elements of enum1 by using Enumerator#next. We will assign the value to the block variable d and perform the calculation in the block to map the digit d:
d = enum1.next
#=> "4"
(enum.next == :even) ? 2*d.to_i : d.to_i
#=> (:even == :even) ? 2*"4".to_i : "4".to_i
#=> (true) ? 8 : 4
#=> 8 ("4" is mapped to 8)
d = enum1.next
#=> "4"
(enum.next == :even) ? 2*d.to_i : d.to_i
#=> (:odd == :even) ? 2*"4".to_i : "4".to_i
#=> (false) ? 8 : 4
#=> 4 ("4" is mapped to 4)
d = enum1.next
#=> "0"
#=> (:even == :even) ? 2*"0".to_i : "0".to_i
#=> (true) ? 0 : 0
#=> 8 ("0" is mapped to 0)
and so on.

Using each_slice to split input starting from end of string?

I would like to split a string into array groups of three as shown in desired output. Using Array#each_slice like this 1_223_213_213.to_s.split('').each_slice(3){|arr| p arr }
Current output: Desired output
# ["1", "2", "2"] # ["0", "0", "1"]
# ["3", "2", "1"] # ["2", "2", "3"]
# ["3", "2", "1"] # ["2", "1", "3"]
# ["3"] # ["2", "1", "3"]
Must work with numbers from (0..trillion). I posted my solution as an answer below. Hoping you all can give me some suggestion(s) to optimize or alternative implements?
Try left-padding with zeros until the string length is an even multiple of your "slice" target:
def slice_with_padding(s, n=3, &block)
s = "0#{s}" while s.to_s.size % n != 0
s.to_s.chars.each_slice(n, &block)
end
slice_with_padding(1_223_213_213) { |x| puts x.inspect }
# ["0", "0", "1"]
# ["2", "2", "3"]
# ["2", "1", "3"]
# ["2", "1", "3"]
slice_with_padding(12_345, 4) { |x| puts x.inspect }
# ["0", "0", "0", "1"]
# ["2", "3", "4", "5"]
You might find this a little more pleasing to your eye:
def slice_by_3(n)
n = n.to_s
l = n.length
[*n.rjust(l % 3 == 0 ? l : l + 3 - l % 3, '0').chars.each_slice(3)]
end
slice_by_3 2_123_456_544_545_355
=> [["0", "0", "2"],
["1", "2", "3"],
["4", "5", "6"],
["5", "4", "4"],
["5", "4", "5"],
["3", "5", "5"]]
Alternatively, if you want a more general solution:
def slice_by_n(num, n=3)
num = num.to_s
l = num.length
[*num.rjust(l % n == 0 ? l : l + n - l % n, '0').chars.each_slice(n)]
end
Here is a possible solution for the problem:
def slice_by_3 number
initial_number = number.to_s.split('').size
number = "00#{number}" if initial_number == 1
modulus = number.to_s.split(/.{3}/).size
padleft = '0' * ( (modulus*3) % number.to_s.split('').size )
("#{padleft}#{number}").split('').each_slice(3){|arr| p arr }
end
slice_by_3 2_123_456_544_545_355
# ["0", "0", "2"]
# ["1", "2", "3"]
# ["4", "5", "6"]
# ["5", "4", "4"]
# ["5", "4", "5"]
# ["3", "5", "5"]
Just seems somewhat complex and I want to believe there is a better way. I appreciate your feedback.
def slice_by_3 number
"000#{number}".split('').reverse
.each_slice(3).to_a[0..-2].reverse
.each { |arr| p arr.reverse }
end
slice_by_3 13_456_544_545_355
# ["0", "1", "3"]
# ["4", "5", "6"]
# ["5", "4", "4"]
# ["5", "4", "5"]
# ["3", "5", "5"]
This code reverses the whole array after adding 3 zeroes to the number start. each_slice(3) then slices to the proper groups (although reversed) plus one which consists of either ["0","0","0"], ["0","0"] or ["0"] depending on the original length of the number.
[0..-2] cuts the last group of zeroes. Then the groups are reversed back, and each group is printed (reversed back).
Here are a couple methods
n = 1_223_213_213.to_s
n.rjust(n.size + n.size % 3,"0").chars.each_slice(3).to_a
OR
n.rjust(15,"0").chars.each_slice(3).drop_while{|a| a.join == "000"}
15 is because you stated the max was a trillion obviously this number means very little as it rejects all results that contain all zeros so any number greater than 15 that is divisible by 3 will work for your example
Another way:
def split_nbr(n)
str = n.to_s
len = str.size
str.rjust(len + (3-len%3)%3, '0').scan(/.../).map(&:chars)
end
split_nbr( 1_223_213_213)
#=> [["0", "0", "1"], ["2", "2", "3"], ["2", "1", "3"], ["2", "1", "3"]]
split_nbr( 11_223_213_213)
#=> [["0", "1", "1"], ["2", "2", "3"], ["2", "1", "3"], ["2", "1", "3"]]
split_nbr(111_223_213_213)
#=> [["1", "1", "1"], ["2", "2", "3"], ["2", "1", "3"], ["2", "1", "3"]]

Nested Loops Ruby

Having Difficulty understanding this nested loop problem:
You have 10 pebbles (numbered 1-10). They are by default black. You must alter them by painting them white if they are black or painting them black if they are white. There are 10 rounds. Every round, you must alter the pebbles that are multiples of the current round.
The pebbles are by default black.
1st round, you alter every pebble (paint them all white).
2nd round, you alter every other pebble(you paint pebbles
#2,4,6,8,10 black).
3rd round, you alter pebbles #3,6,9.
4th round you alter pebbles #4,8.
...
...
10th round, you alter pebble #10.
After the 10th round, which pebbles are painted black and which are painted white?
My solution which doesn't run is below (I attempt to do so by making an array of numbers(turned to strings) and adding "w" if painted white and deleting "w" if painted black.
(I have tried editing it to make it run, however I am new to nested loops and I am just not grasping this concept). I would greatly appreciate it if someone could explain to me what I am doing wrong and give a better solution.
pebbles = (1..10).map {|element| element.to_s}
pebble_colors = (1..10).map {|element| element.to_s}
(1..10).each do |round|
pebbles_to_paint = []
pebbles.each_with_index {|element, index| pebbles_to_paint << index if element % round == 0}
pebbles_to_paint.each do |pebble_number|
if pebble_color[pebble_number].include?("w")
pebble_color[pebble_number].delete!("w")
else
pebble_color[pebble_number] << "w"
end
end
end
Because this is about nested loops, I just wanted to add that you don't necessarily have to iterate through all pebbles on each round. (that's 100 iterations for 10 pebbles!)
Instead you can use Range#step to iterate over each nth element, starting with the round's index:
(1..10).each { |r|
print "%2d:" % r
(r..10).step(r) { |i|
print " #{i}"
}
puts
}
produces:
1: 1 2 3 4 5 6 7 8 9 10
2: 2 4 6 8 10
3: 3 6 9
4: 4 8
5: 5 10
6: 6
7: 7
8: 8
9: 9
10: 10
That's only 27 iterations. A nice side effect is that you don't have to calculate the remainder any more.
Full example:
pebbles = Hash[(1..10).map{|i| [i, :black]}]
toggle = {:black => :white, :white => :black}
(1..10).each { |r|
(r..10).step(r) { |i|
pebbles[i] = toggle[pebbles[i]]
}
}
p pebbles
#=> {1=>:white, 2=>:black, 3=>:black, 4=>:white, 5=>:black, 6=>:black, 7=>:black, 8=>:black, 9=>:white, 10=>:black}
Your main problem appears to be in the deciding of which pebbles to paint. The following is not right:
element % round == 0
It should be:
(index+1) % round
You want to compare the pebble's index rather than the current value of the pebble. As well, you need to remember that indexes are 0-based (ie they start counting from 0). You need to have the indexes be 1-based (hence the adding of 1) otherwise the first element would always change and the others would be off by 1.
There was also a typo for pebble_color, which should be pebble_colors.
You could definitely re-factor the code to make it shorter, but the following appears to work (just making the minimal changes mentioned above):
pebbles = (1..10).map {|element| element.to_s}
pebble_colors = (1..10).map {|element| element.to_s}
(1..10).each do |round|
pebbles_to_paint = []
pebbles.each_with_index {|element, index| pebbles_to_paint << index if (index+1) % round == 0}
pebbles_to_paint.each do |pebble_number|
if pebble_colors[pebble_number].include?("w")
pebble_colors[pebble_number].delete!("w")
else
pebble_colors[pebble_number] << "w"
end
end
p pebble_colors
end
The output is:
["1w", "2w", "3w", "4w", "5w", "6w", "7w", "8w", "9w", "10w"]
["1w", "2", "3w", "4", "5w", "6", "7w", "8", "9w", "10"]
["1w", "2", "3", "4", "5w", "6w", "7w", "8", "9", "10"]
["1w", "2", "3", "4w", "5w", "6w", "7w", "8w", "9", "10"]
["1w", "2", "3", "4w", "5", "6w", "7w", "8w", "9", "10w"]
["1w", "2", "3", "4w", "5", "6", "7w", "8w", "9", "10w"]
["1w", "2", "3", "4w", "5", "6", "7", "8w", "9", "10w"]
["1w", "2", "3", "4w", "5", "6", "7", "8", "9", "10w"]
["1w", "2", "3", "4w", "5", "6", "7", "8", "9w", "10w"]
["1w", "2", "3", "4w", "5", "6", "7", "8", "9w", "10"]
There are a couple of problems in your code.
You use element % round instead of (index + 1) % round
You modify array pebble_color instead of pebble_colors
Fixing these two problems, the program leaves pebble_colors with the value
["1w", "2", "3", "4w", "5", "6", "7", "8", "9w", "10"]
although I think that isn't quite what you had in mind!
You have three arrays where one will do. All you need is an array of ten colours, starting all black. I would code it like this
pebbles = Array.new(10, :black)
(1..10).each do |round|
pebbles.each_with_index do |pebble, i|
if (i + 1).remainder(round) == 0
pebbles[i] = pebble == :black ? :white : :black
end
end
end
p pebbles
output
[:white, :black, :black, :white, :black, :black, :black, :black, :white, :black]
For make the problem simple, I think it is better calculate all round multiples before update the pebble.
pebbles = Array.new(10)
10.times do |i|
# calculate all multiples for each index
multiples = Array(1..10).select {|j| j % (i + 1) == 0}
# observer that we must have to sub 1 since Ruby use 0 indexed arrays
multiples.map! {|j| j - 1}
# update each pebble
multiples.each do |j|
pebbles[j] = pebbles[j] == 'b' ? 'w' : 'b'
end
end
puts "Black pebbles: #{pebbles.select {|p| p == 'b'}.size}"

To find the integer (Fixnum) values in ruby array

I have an array [1, 2, "3", "4", "1a", "abc", "a"] with
pure integers (1, 2),
string formatted integers ("1", "2"),
strings ("a", "b"), and
mixed string numbers ("1a", "2s").
From this, I need to pick up only the integers (including string formatted) 1, 2, "3", "4".
First I tried with to_i:
arr = [1, 2, "3", "4", "1a", "abc", "a"]
arr.map {|x| x.to_i}
# => [1, 2, 3, 4, 1, 0, 0]
but this one converts "1a" to 1, which I don't expect.
Then I tried Integer(item):
arr.map {|x| Integer(x) } # and it turned out to be
# => ArgumentError: invalid value for Integer(): "1a"
Now I am out of straight conversion options here. Finally, I decided to do this way, which converts the value to_i and to_s. So "1" == "1".to_i.to_s is an integer, but not "1a" == "1a".to_i.to_s and "a" == "a".to_i.to_s
arr = arr.map do |x|
if (x == x.to_i.to_s)
x.to_i
else
x
end
end
and
ids, names= arr.partition { |item| item.kind_of? Fixnum }
Now I got the arrays of integers and strings. Is there a simple way to do this?
Similar solution as provided by #maerics, but a bit slimmer:
arr.map {|x| Integer(x) rescue nil }.compact
class Array
def to_i
self.map {|x| begin; Integer(x); rescue; nil; end}.compact
end
end
arr = [1, 2, "3", "4", "1a", "abc", "a"]
arr.to_i # => [1, 2, 3, 4]
something like this:
a = [1,2,"3","4","1a","abc","a"]
irb(main):005:0> a.find_all { |e| e.to_s =~ /^\d+$/ }.map(&:to_i)
=> [1, 2, 3, 4]
Hey, thanks awakening my ruby. Here is my go at this problem:
arr=[1,2,"3","4","1a","abc","a"]
arr.map {|i| i.to_s}.select {|s| s =~ /^[0-9]+$/}.map {|i| i.to_i}
//=> [1, 2, 3, 4]
I noticed most of the answer so far changes the value of "3" and "4" to actual integers.
>> array=[1, 2, "3", "4", "1a", "abc", "a", "a13344a" , 10001, 3321]
=> [1, 2, "3", "4", "1a", "abc", "a", "a13344a", 10001, 3321]
>> array.reject{|x| x.to_s[/[^0-9]/] }
=> [1, 2, "3", "4", 10001, 3321]
#OP, I have not tested my solution exhaustively, but so far it seems to work (of course its done according to provided sample ), so please test thoroughly yourself.
How about this?
[1,2,"3","4","1a","abc","a"].select{|x| x.to_i.to_s == x.to_s}
# => [1, 2, "3", "4"]
Looks pretty simple
arr.select{ |b| b.to_s =~ /\d+$/ }
# or
arr.select{ |b| b.to_s[/\d+$/] }
#=> [1, 2, "3", "4"]

Resources