Ruby - Writing a program that guesses letters in a word - ruby-on-rails

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.

Related

Infinite loop bug in Lua

I'm new to this platform and I'm still learning to
program in Lua, so, if any newbie errors appear, forgive me.
The following code is from one of the functions in my project that reads the insert
of the user and validates whether or not it is a data of type "Number". If,
the loop will be broken and the function will return the user input, otherwise, the
program will ask the user to enter the data again:
function bin.readnum(text)
local insertion
if text == nil then text = "Text: " end
while (insertion == nil) do
insertion = nil
print(text)
insertion = io.read("number")
if insertion ~= nil then break end
end
return insertion
end
But, if the user enters a wrong data (string) the function prints the text
madly instead of asking the user to re-enter the data.
When io.read fails to parse the data it got into a number, it doesn't discard it, but instead leaves it in the buffer for the next call to it. That means that in your code, instead of letting the user enter something else, it'll just keep trying to parse the same non-number forever. To fix it, in your if insertion ~= nil then block, do io.read() right before break, to read and discard the whole invalid line.
In addition to what Joseph Sible said:
io.read("number") is wrong: 5.1 docs demand "*n" and 5.4 docs demand just "n" for reading numbers. It probably works nevertheless due to Lua just searching for the chars in the string.
I recommend just replacing insertion = io.read("number") withinsertion = tonumber(assert(io.read(), "EOF")) - this will read a line and try to parse it as a number; the assert gracefully deals with nil being returned by io.read for EOF.
You don't need to set insertion to nil, the later assignment will do that already if what was read is not a valid number.
Style: Consider replacing your explicit nil checks with truthiness checks and removing the parentheses around the while-condition. You don't need a break, you can immediately return the read number; finally, you can even replace the entire loop with tail recursion.
All in all I'd rewrite it as follows:
function bin.readnum(text)
print(text or "Text: ")
local num = tonumber(assert(io.read(), "EOF"))
if num then return num end
return bin.readnum(text)
end
or alternatively using a repeat-until loop:
function bin.readnum(text)
local num
repeat
print(text or "Text: ")
num = tonumber(assert(io.read(), "EOF"))
until num
return num
end

My code doesn't do anything

game:GetService("Players").PlayerAdded:connect(function()
for _, Player in pairs(game:GetService("Players"):GetPlayers()) do
Player.Chatted:connect(function(msg)
if string.sub(msg,1,5) == "oofergang" then
Player:Kick("no no no cringe baby")
end
end)
end
end)
return ''
How do I fix this? It doesn't do anything, no errors nothing.
Your issue seems to be in your string.sub() usage. (I'm assuming this is Roblox, which I don't know much about).
The string.sub(a, b, c) method takes the substring of the string a, starting from index b and going to index c.
Your problem is that you're trying to get the substring from characters 1-5. Character 1 is the first character and character 5 is the 5th character in the string. Your if block is checking the first 5 characters of the player's message. The issue is that the string you're comparing it to, "oofergang", is longer than 5 characters.
If the player does correctly type oofergang, the string.sub() that you're using will output oofer, which is the first 5 characters of the message. Essentially, this is what the program will see when running:
if "oofer" == "oofergang" then
oofer is never going to equal oofergang.
If you want to check if the player starts their message with oofergang then you should use the following if block instead:
if (string.sub(msg, 1, 9) == "oofergang") then
--Whatever you want to do here, in your case kick the player
end
EDIT: As suggested, the following code allows you to find a string within another string ANYwhere, not just a the start:
if (string.find(msg, "oofergang")) then
--Whatever you want to do here, in your case kick the player
end

Lua: Why does my table revert to nil after one remove?

The following is part of a script I use to indicate which items (in this case, MOBs in a MUD) are not in my tabled database.
if mqtable[1] == nil then
Note("No mobs missing from database!")
else
if "%1" ~= "" then
mindex = "%1"
mdesc = mqtable[tonumber(mindex)]
end
if "%2" ~= "" then mlvl = "%2" end
if "%3" ~= "" then mkeyw = "%3" end
if not mindex and mqtable[1] then
tprint(mqtable)
elseif mindex and not mlvl then
Note(mdesc)
elseif mindex and mlvl and not mkeyw then
Note("Syntax is: mqmob [index] [level] [keywords]")
else
mobtable[mdesc]={level = mlvl, keywords = mkeyw}
table.save(mobtable,savepath.."/Mobs/"..areazone..".tbl")
Note(mqtable[tonumber(mindex)] .. " saved. Level: ".. mlvl .. " -- Keywords: " .. mkeyw)
table.remove(mqtable, tonumber(mindex))
mobtable = table.load(savepath .. "/Mobs/" .. areazone .. ".tbl")
mlvl, mkeyw, mdesc = nil, nil, nil
end
Assume all undefined functions are working as intended and do not appear to be the root cause. "%1", "%2", and "%3" are passed parameters through a trigger. Essentially, if this were a function, those three parameters would be called as foo(blah, bleh, blargh).
Problem
If I type mqmob (which fires the above script), it displays within the room which MOBs have not been added to my table. I then use mqmob <index> <level> <keyword> to add the mob to that table. Once that has been done, however, the script refuses to work, and regardless of the number of items that were originally on the table, mqmob returns nil. Why is the table being completely wiped after storing one bit of information?
Example
I look in a room and see the following:
A rat dwells here.
A mouse chases after some cheese.
A spider makes its home in the corner.
A flea leaps about playfully.
Say that I have only A spider makes its home in the corner. in the database. The remaining three descriptions are placed into mqtable, and when I call mqmob, it displays:
1="A rat dwells here."
2="A mouse chases after some cheese."
3="A flea leaps about playfully."
Now, I type mqmob 1 20 rat to create mobtable["A rat dwells here."] with {level = 20, keywords = rat}. But once I do that, if I type mqmob again, it returns nil.
Explanation of variables
mobtable is merely a table that contains the mob's description as the key, and its corresponding level and keywords as values. For example:
mobtable = {"A rat nibbles on some cheese." = {keyword = "rat", level = 20}}
mqtable is populated only when there is no corresponding match in mobtable. Essentially, the trigger fires, compares to keys in mobtable, and if not found, populates mqtable. This is to help prevent me from unnecessary work of adding mobs already in mobtable.
The problem I keep running into is that I will add one mob, but when I run the print command a second time, it returns nil. However, I can still add and view the individual indexes in mqtable. It just doesn't seem able to print it up anymore.

Newbie rails logic & syntax questions

I am learning rails and have come across the following code which I would like to use. The code in question is the answer by John F Miller (first answer) in the following link:
How to render all records from a nested set into a real html tree
def tree_from_set(set) #set must be in order
buf = START_TAG(set[0])
stack = []
stack.push set[0]
set[1..-1].each do |node|
if stack.last.lft < node.lft < stack.last.rgt
if node.leaf? #(node.rgt - node.lft == 1)
buf << NODE_TAG(node)
else
buf << START_TAG(node)
stack.push(node)
end
else#
buf << END_TAG
stack.pop
retry
end
end
buf <<END_TAG
end
def START_TAG(node) #for example
"<li><p>#{node.name}</p><ul>"
end
def NODE_TAG(node)
"<li><p>#{node.name}</p></li>"
end
def END_TAG
"</li></ul>"
end
I am unsure of the following and would appreciate any guidance.
I see this will cycle through "set" assigning each item to the object "node" however I cannot determine what [1..-1] does.
set[1..-1].each do |node|
Following the logic I cannot understand the purpose of removing the last item from the array "stack"
stack.pop
It appears this command in this context is no longer supported in ruby after 1.9. I believe the intention was to return to the start of the loop and repeat.
retry
A "subarray" with all but zeroth element.
Negative array indices -x in Ruby are shorthands for length-x. That is, -1'st element is the last. Range 1..-1 is "first to last", but since arrays are zero-indexed in Ruby, that means "all but zeroth element".
The stack holds "how deep you are", more precisely, which elements are you currently in. When examining the next element, if you "went out", you should close the list you left (possibly multiple times!) before adding the current item.
As for retry: I think it should be redo. If you stepped outside, you have to make sure you close every list you have to: once per iteration you pop from the stack, close the closest list and loop this until you are inside the top element on the stack, in the context for the current node to be inserted.
Actually, thus code assumes you only have one tree with set[0] its root. By adding to line 6 a check (with ||) if the stack is empty you eliminate this flaw, need for pushing set[0] manually, and thus exclusion of it from the loop. Because if the stack is empty, you are in hyperspace that contains everything, so you shouldn't bother comparing anything. This gives you the possibility of rendering multiple element trees (possibly without common root) from one list.
I believe the clean solution to this is a recursive one, replacing "home-made stack" with Ruby's call stack. I can't come up with a solution too quickly on this though.

appending random integer to a string using .concat

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.

Resources