appending random integer to a string using .concat - ruby-on-rails

I'm trying to append an integer gotten from a method to a string, I have tried multiple different things: << and .concat (although the same) += as well
I have chosen to make the method due to a function I'm working on that will be added later on
the problem with this code is that instead of returning my string + firstrandomnumber + secondrandomnumber so on so forth it only returns my string + latestrandomnumber
def machineSlot()
tal1= rand(0..10)
return tal1
end
#makes the a random number
if startBool == true
#game startof
gameRunner=true
puts 'pull the lever with x'
leverPullTry =gets.chomp
while gameRunner
#keeps game running
i=1
slotThread='Your numbers are:'
#initialise game rollcount i and string that keeps the numbers
if leverPullTry=='x'
slotThread.concat( machineSlot.to_s)
#if x was entered the slotThread appends a random number
puts slotThread
#slotThreads current process
puts 'pull the lever with x again'
i+=1
#number of rolls increased
leverPullTry =gets.chomp
else
puts 'try again use type x to pull'
leverPullTry =gets.chomp
#user didnt manage to input anything correct
end
end
end

Change these two lines:
slotThread.concat( machineSlot.to_s)
#if x was entered the slotThread appends a random number
puts slotThread
#slotThreads current process
to
puts "#{slotThread}#{machineSlot}"
That's called string interpolation, something used a lot in ruby.
By the way, you're using CamelCase to name your variables and methods which is not the convention. Not urgent but I'd check out this when you have time.

Related

No Output for the Elixir Program

I am trying to solve a dynamic problem finding the subsets i have written the code but i didn't know why i am not getting anything it just blinks after running Todos.sum_of_one(arr_of_digits, sum_val), I think the problem is in the terminating case when n==0, can anyone please tell me where is the mistake
def Todos do
#find all the subsets whose sum is equal to sum_val
def sumofone(arr_of_digits,n,v,sum)do
if(sum==0) do
for i <- v do
i
end
end
#return if n becomes 0
if(n==0) do
v
end
sumofone(arr_of_digits,n-1,v,sum)
k = Enum.at(arr_of_digits,n-1)
#inserting the element in the list
[k | v]
sumofone(arr_of_digits,n-1,v,sum - arr_of_digits[n-1]);
end
def sum_of_one(arr_of_digits, sum_val) do
v = []
sumofone(arr_of_digits,l,v,sum_val)
end
end
It looks like you're trying to return from the function in the two if expressions. Elixir doesn't work that way - it always* runs through the entire function and returns the value of the last expression in the function.
One way to get around this is to break up the code into different function clauses, where each clause matches one of the conditions you're testing for:
# This clause executes when the fourth argument is 0
def sumofone(_arr_of_digits,_n,v,0) do
for i <- v do
i
end
end
# This clause executes when the second argument is 0
def sumofone(_arr_of_digits,0,v,_sum) do
v
end
# This clause executes in all other cases, as long as n is greater than 0
def sumofone(arr_of_digits,n,v,sum) when n > 0 do
sumofone(arr_of_digits,n-1,v,sum)
k = Enum.at(arr_of_digits,n-1)
#inserting the element in the list
[k | v]
sumofone(arr_of_digits,n-1,v,sum - arr_of_digits[n-1]);
end
With this change, it's guaranteed that the function will actually terminate. It still won't do what you expect it to do, since there are two lines that calculate a value but throw it away. In Elixir, if you want to update the value of a variable, you need to do so explicitly. Did you mean something like this?
sum = sumofone(arr_of_digits,n-1,v,sum)
and
#inserting the element in the list
v = [k | v]
But I'll leave that for you to debug.
Note that I prefixed some of the argument names with an underscore. Without that, the compiler would give a warning about the variable being unused. With the underscore, it's clear that this is in fact intended.
* Except if you're using errors, throws and exits. But try not to use them - it's often clearer not to.

Ruby - Writing a program that guesses letters in a word

Background: Using a class, I'm building a hangman-like game where player 1 enters a word to be guessed, and player 2 attempts to guess it. The number of guesses allotted to player 2 is relative to the word in question, but repeated guesses do not count against the player.
Each guess should provide continual feedback to player 2 by showing their progress towards guessing the word, which should be printed at the end of each guessing phase. ex) The word 'code' would be displayed as "_ _ _ _ ", if the letter 'o' were to be guessed, the feedback would look like " _ o _ _", etc. Once the word is guessed or a player has 0 attempts left, a winner is announced.
Issue 1: I can't get the program to close when the game_won? method evaluates to true. It continues to run until attempts == game_word.length + 2. Any ideas on how to end the program with the winning statement?
Issue 2: I tried adding a game_lost method, but couldn't get it to work once player 2 runs out of attempts (tried creating an instance variable to be tied to attempts in the interface, but whenever it was called outside of the class, an error popped up stating that it was a nil class, rather than an integer. How can I make a functional method that states when the game is lost?
Issue 3: Whenever an incorrect letter is entered, the "Nope, try again..." response is printed out as many times as the length of the word is. It seems to be printing out whatever is evaluated last, x amount of times, in the guess_the_letter method.
Issue 4: If the word entered contains more than one of the same letter, the word progress update will appear as many times as that letter exists within the game word. (Seems to be a similar issue to issue 3) Any ideas as to what I'm doing wrong here.
class GuessingGame
def initialize(word)
#word = word.downcase
#display_word = "_" * word.length
end
# Take a guess in the form of a letter and check to see if it is in the
# target word, update the word pattern to include the missing letter
def guess_the_letter(g_letter)
g_letter.downcase
#word.split("").each_with_index do |w_letter, index|
if g_letter == w_letter
#display_word[index] = g_letter
puts "Here is your progress towards guessing the word:"
p #display_word
end
if !#word.include? (g_letter)
puts "Nope, try again..."
end
end
end
# Determine winning conditions
def game_won?
if #word == #display_word
puts "Congratulations Player 2, you won!"
true
else
false
end
end
def game_lost?
#method body
end
end
puts "Welcome to the Word Guessing Game!"
puts "This game is for 2 players."
puts "Player 1, please enter a word for player 2 to guess..."
game_word = gets.chomp
game = GuessingGame.new(game_word)
attempts = 0
guessed_letters = []
#Create an interface for the users to play the game
until attempts == game_word.length + 2
puts "Player 2, please guess a letter..."
letter_guess = gets.chomp
if guessed_letters.include? letter_guess
puts "You already tried that letter, enter a new one."
letter_guess = gets.chomp
end
guessed_letters << letter_guess
game.guess_the_letter(letter_guess)
game.game_won?
attempts += 1
end
When asking this kind of question, you should indicate, where line 46 is.
In your case, I guess it's the expression attempts == #word.length. You are not in class context, so #word does not refer to the instance variable in GuessingGame.
You can not directly access an instance variable of another object (i.e. an object different to self), so you need to provide an accessor method. Ruby makes this easy using attr_reader:
attr_reader :word
creates a read-accessor method to #word which is called word.
UPDATE: I just see that you have modified your original code. With the new code, you will have the same problem with #bad_guesses.
BTW, if you edit your posting, please always explain what you changed.

How to refactor string containing variable names into booleans?

I have an SPSS variable containing lines like:
|2|3|4|5|6|7|8|10|11|12|13|14|15|16|18|20|21|22|23|24|25|26|27|28|29|
Every line starts with pipe, and ends with one. I need to refactor it into boolean variables as the following:
var var1 var2 var3 var4 var5
|2|4|5| 0 1 0 1 1
I have tried to do it with a loop like:
loop # = 1 to 72.
compute var# = SUBSTR(var,2#,1).
end loop.
exe.
My code won't work with 2 or more digits long numbers and also it won't place the values into their respective variables, so I've tried nest the char.substr(var,char.rindex(var,'|') + 1) into another loop with no luck because it still won't allow me to recognize the variable number.
How can I do it?
This looks like a nice job for the DO REPEAT command. However the type conversion is somewhat tricky:
DO REPEAT var#i=var1 TO var72
/i=1 TO 72.
COMPUTE var#i = CHAR.INDEX(var,CONCAT("|",LTRIM(STRING(i,F2.0)),"|"))>0).
END REPEAT.
Explanation: Let's go from the inside to the outside:
STRING(value,F2.0) converts the numeric values into a string of two digits (with a leading white space where the number consist of just one digit), e.g. 2 -> " 2".
LTRIM() removes the leading whitespaces, e.g. " 2" -> "2".
CONCAT() concatenates strings. In the above code it adds the "|" before and after the number, e.g. "2" -> "|2|"
CHAR.INDEX(stringvar,searchstring) returns the position at which the searchstring was found. It returns 0 if the searchstring wasn't found.
CHAR.INDEX(stringvar,searchstring)>0 returns a boolean value indicating if the searchstring was found or not.
It's easier to do the manipulations in Python than native SPSS syntax.
You can use SPSSINC TRANS extension for this purpose.
/* Example data*/.
data list free / TextStr (a99).
begin data.
"|2|3|4|5|6|7|8|10|11|12|13|14|15|16|18|20|21|22|23|24|25|26|27|28|29|"
end data.
/* defining function to achieve task */.
begin program.
def runTask(x):
numbers=map(int,filter(None,[i.strip() for i in x.lstrip('|').split("|")]))
answer=[1 if i in numbers else 0 for i in xrange(1,max(numbers)+1)]
return answer
end program.
/* Run job*/.
spssinc trans result = V1 to V30 type=0 /formula "runTask(TextStr)".
exe.

Ruby method returns hash values in binary

I wrote a method that takes six names then generates an array of seven random numbers using four 6-sided dice. The lowest value of the four 6-sided dice is dropped, then the remainder is summed to create the value. The value is then added to an array.
Once seven numbers have been generated, the array is then ordered from highest to lowest and the lowest value is dropped. Then the array of names and the array of values are zipped together to create a hash.
This method ensures that the first name in the array of names receives the highest value, and the last name receives the lowest.
This is the result of calling the method:
{:strength=>1, :dexterity=>1, :constitution=>0, :intelligence=>0, :wisdom=>0, :charisma=>1}
As you can see, all the values I receive are either "1" or "0". I have no idea how this is happening.
Here is the code:
module PriorityStatGenerator
def self.roll_stats(first_stat, second_stat, third_stat, fourth_stat, fifth_stat, sixth_stat)
stats_priority = [first_stat, second_stat, third_stat, fourth_stat, fifth_stat, sixth_stat].map(&:to_sym)
roll_array = self.roll
return Hash[stats_priority.zip(roll_array)]
end
private
def self.roll
roll_array = []
7.times {
roll_array << Array.new(4).map{ 1 + rand(6) }.sort.drop(1).sum
}
roll_array.reverse.delete_at(6)
end
end
This is how I'm calling the method while I'm testing:
render plain: PriorityStatGenerator.roll_stats(params[:prioritize][:first_stat], params[:prioritize][:second_stat], params[:prioritize][:third_stat], params[:prioritize][:fourth_stat], params[:prioritize][:fifth_stat], params[:prioritize][:sixth_stat])
I added require 'priority_stat_generator' where I'm calling the method, so it is properly calling it.
Can someone help me make it return proper values between 1 and 18?
Here's a refactoring to simplify things and use an actually random number generator, as rand is notoriously terrible:
require 'securerandom'
module PriorityStatGenerator
def self.roll_stats(*stats)
Hash[
stats.map(&:to_sym).zip(self.roll(stats.length).reverse)
]
end
private
def self.roll(n = 7)
(n + 1).times.map do
4.times.map { 1 + SecureRandom.random_number(6) }.sort.drop(1).inject(:+)
end.sort.last(n)
end
end
This makes use of inject(:+) so it works in plain Ruby, no ActiveSupport required.
The use of *stats makes the roll_stats function way more flexible. Your version has a very rigid number of parameters, which is confusing and often obnoxious to use. Treating the arguments as an array avoids a lot of the binding on the expectation that there's six of them.
As a note it's not clear why you're making N+1 roles and then discarding the last. That's the same as generating N and discarding none. Maybe you meant to sort them and take the N best?
Update: Added sort and reverse to properly map in terms of priority.
You need to learn to use IRB or PRY to test snippets of your code, or better, learn to use a debugger. They give you insight into what your code is doing.
In IRB:
[7,6,5,4,3,2,1].delete_at(6)
1
In other words, delete_at(6) is doing what it's supposed to, but that's not what you want. Instead, perhaps slicing the array will behave more like you expect:
>> [7,6,5,4,3,2,1][0..-2]
[
[0] 7,
[1] 6,
[2] 5,
[3] 4,
[4] 3,
[5] 2
]
Also, in your code, it's not necessary to return a value when that operation is the last logical step in a method. Ruby will return the last value seen:
Hash[stats_priority.zip(roll_array)]
As amadan said, I can't see how you are getting the results you are, but their is a definite bug in your code.
The last line in self.roll is the return value.
roll_array.reverse.delete_at(6)
Which is going to return the value that was deleted. You need to add a new lines to return the roll_array instead of the delete_at value. You are also not sorting your array prior to removing that last item which will give you the wrong values as well.
def self.roll
roll_array = []
7.times {
roll_array << Array.new(4).map{ 1 + rand(6) }.sort.drop(1).sum
}
roll_array.sort.drop(1)
roll_array
end

Generating a unique and random 6 character long string to represent link in ruby

I am generating a unique and random alphanumeric string segment to represent certain links that will be generated by the users. For doing that I was approaching with "uuid" number to ensure it's uniqueness and randomness, but, as per my requirements the string shouldn't be more than 5 characters long. So I dropped that idea.
Then I decided to generate such a string using random function of ruby and current time stamp.
The code for my random string goes like this:-
temp=DateTime.now
temp=temp + rand(DateTime.now.to_i)
temp= hash.abs.to_s(36)
What I did is that I stored the current DateTime in a temp variable and then I generated a random number passing the current datetime as parameter. Then in the second line actually added current datetime and random number together to make a unique and random string.
Soon I found,while I was testing my application in two different machines and send the request at the same time, it generated the same string(Though it's rare) once after more than 100 trials.
Now I'm thinking that I should add one more parameter like mac address or client ip address before passing to_s(36) on temp variable. But can't figure out how to do it and even then whether it will be unique or nor...
Thanks....
SecureRandom in ruby uses process id (if available) and current time. You can use the urlsafe_base64(n= 16) class method to generate the sequence you need. According to your requirements I think this is your best bet.
Edit: After a bit of testing, I still think that this approach will generate non-unique keys. The way I solved this problem for barcode generation was:
barcode= barcode_sql_id_hash("#{sql_id}#{keyword}")
Here, your keyword can be time + pid.
If you are certain that you will never need more than a given M amount of unique values, and you don't need more than rudimentary protection against guessing the next generated id, you can use a Linear Congruentual Generator to generate your identificators. All you have to do is remember the last id generated, and use that to generate a new one using the following formula:
newid = (A * oldid + B) mod M
If 2³² distinct id values are enough to suit your needs, try:
def generate_id
if #lcg
#lcg = (1664525 * #lcg + 1013904223) % (2**32)
else
#lcg = rand(2**32) # Random seed
end
end
Now just pick a suitable set of characters to represent the id in as little as 6 character. Uppercase and lowercase letters should do the trick, since (26+26)^6 > 2^32:
ENCODE_CHARS = [*?a..?z, *?A..?Z]
def encode(n)
6.times.map { |i|
n, mod = n.divmod(ENCODE_CHARS.size)
ENCODE_CHARS[mod]
}.join
end
Example:
> 10.times { n = generate_id ; puts "%10d = %s" % [n, encode(n)] }
2574974483 = dyhjOg
3636751446 = QxyuDj
368621501 = bBGvYa
1689949688 = yuTgxe
1457610999 = NqzsRd
3936504298 = MPpusk
133820481 = PQLpsa
2956135596 = yvXpOh
3269402651 = VFUhFi
724653758 = knLfVb
Due to the nature of the LCG, the generated id will not repeat until all 2³² values have been used exactly once each.
There is no way you can generate a unique UUID with only five chars, with chars and numbers you have a basic space of around 56 chars, so there is a max of 56^5 combinations , aprox 551 million (Around 2^29).
If with this scheme you were about to generate 10.000 UUIDs (A very low number of UUIDs) you would have a probability of 1/5.000 of generating a collision.
When using crypto, the standard definition of a big enough space to avert collisions is around 2^80.
To put this into perspective, your algorithm would be better off if it generated just a random integer (a 32 bit uint is 2^32, 8 times the size you are proposing) which is clearly a bad idea.

Resources