I'm a newbie to Ruby programming. It is my second OOP project. I have trouble with counting white pegs under certain condition. And Wikipedia says that:
-A black key peg is placed for each code peg from the guess which is correct in both colour and position.
-A white key peg indicates the
existence of a correct colour code peg placed in the wrong position.
I believe that my black peg condition is true, but I can not count my white pegs correctly.
Here is the function code piece:
def check_guess(code, guess)
#white_peg = 0
#black_peg = 0
index = 0
while index < code.length
if code[index] == guess[index]
#black_peg += 1
puts "Black => #{#black_peg}"
elsif guess.any? { |c| code.include?(c) } && code[index] != guess[index]
#white_peg += 1
puts "White => #{#white_peg}"
end
index += 1
end
puts "There are #{#white_peg} white peg and #{#black_peg} black peg"
is_win?
end
end
I'm invoking above function inside this condition
def codebreaker_attempt
guess_array = #codebreaker.guess_code
#codebreaker.check_guess(#codemaker.code, guess_array)
end
And lastly my game method is here for stop the game
def play
codemaker_choose
(1..12).each do |i|
puts "#{i}. GUESS\n"
#puts "Guess array is #{guess}"
break if codebreaker_attempt == true
end
I'm leaving here repl.it repo of all my codes if you want to inspect.
https://replit.com/#Burakkepuc/Mastermind#main.rb
-A white key peg indicates the existence of a correct colour code peg placed in the wrong position
I found my answer. The white peg code control should be
elsif code.include?(guess[index]) && code[index] != guess[index]
Related
I'm taking an online course in Ruby programming and I need to make 5-Card Draw game as one of the projects. It all went well until I realized that Ace can have two values.
I've made 3 classes so far: Card, Deck and Hand. I'm currently working on a Hand class. The other two classes are below:
class Card
attr_reader :number, :sign, :color
def initialize(number, sign, color)
#number = number
#sign = sign
#color = color
end
end
require_relative 'card.rb'
class Deck
def initialize
#deck = make_deck
end
def make_deck
deck = []
signs = {'Club' => 'black', 'Spade' => 'black', 'Heart' => 'red', 'Diamond' => 'red'}
n = 1
while n < 15
if n == 11
n += 1
next
end
i = 0
4.times do
sign = signs.keys[i]
color = signs[sign]
deck << Card.new(n, sign, color)
i += 1
end
n += 1
end
deck
end
end
So, the problem appeared when I started coding the Poker Hands in Hand class. I'm not sure how to deal with the Ace because it can have a value of either 1 or 15. Any help/suggestion is welcomed.
"Ace can have two values" isn't the right way to think of it. Just make Aces high, always. Then, in the code that checks for straights you have to special-case the wheel. That is, a straight is defined as "5 cards in rank sequence, or A-2-3-4-5".
I have (almost) made a cup sorting algorithm that takes colour and radius parameters then spits out the cups names ranked by the radius length. Example input
2
blue 7
10 red
Example output
red
blue
The problem is that I want to create a filter that checks if the first value is a number when split. Then this number is divided by 2 and both values are reversed. I have tried is_a? Integer but get an expecting end of input error in the irb console. I tried == int.
Here is the code:
class Cup
attr_accessor :colour, :radius
def initialize(colour, radius)
#colour = colour
#radius = radius
end
end
cups = []
puts "How many cups are there?"
gets.to_i.times do |n|
puts "Enter Cup-#{n+1} colour & radius:"
value = gets.split " "
if
value.first.to_i == int?
then
value.first / 2
value.reverse
cups << Cup.new(value[0], value[1])
end
cups << Cup.new(value[0], value[1])
end
print cups.colour.sort_by { |cup| cup.radius }
Any other feedback about the algorithm is very much welcomed.
Whatever input is provided by user in console will be string so you can do as below,
puts "How many cups are there?"
gets.to_i.times do |n|
puts "Enter Cup-#{n+1} colour & radius:"
value = gets.chomp.split(" ")
order = Integer(value[0]) rescue false # order will have value if it is proper integer, else false
cups << (order ? Cup.new(value[1], value[0].to_i) : Cup.new(value[0], value[1].to_i))
end
cups.sort_by { |cup| cup.radius }.each { |cup| puts cup.colour } if cups.present?
Use of to_i is not valid here, as it will return 0 for string 'red'
And also it is also assumed user is putting integer for sure, otherwise code will not work as it is supposed to.
I am writing a bowling score calculator in Ruby that is defined and tested using RSpec. It currently runs, but only passes 5 of the 8 input tests. Here is the code for my implementation:
class ScoreKeeper
def calculate(input)
unless input.is_a? String
raise argumentException, "Score Keeper will only except string types for score calculation."
end
# Thanksgiving Turkey Edge Case
return 300 if input == "xxxxxxxxxxxx"
# Calculate Score
throws = input.gsub(/-/, "0").split(//)
score = 0
throws.each_with_index do |ball, i|
current_throw = i
last_throw = throws[i - 1] || "0"
lastlast_throw = throws[i - 2] || "0"
next_throw = throws[i + 1] || "0"
if current_throw == 0
last_throw = 0
lastlast_throw = 0
end
if current_throw == 1
lastlast_throw = 0
end
working_value = 0
# Add numbers directly (unless part of a spare frame)
if ((1..9) === ball.to_i)
working_value = ball.to_i
end
# Add strike as 10 points
if ball == "x"
working_value = 10
end
# Add spare as number of remaining pins from last throw
if ball == "/"
if last_throw == "/" || last_throw == "x"
raise argumentException, "Invalid score string. A spare cannot immediately follow a strike or spare."
end
working_value = 10 - last_throw.to_i
end
# Strike / Spare Bonus
if last_throw == "x" || last_throw == "/" || lastlast_throw == "x"
score += working_value
end
# Add current throw value
score += working_value
end
if score > 300 || score < 0
raise argumentExcpetion, "Invalid score string. Impossible score detected."
end
score
end
end
I can't tell why my code is not calculating a proper score in every test case.
The RSpec:
require "./score_keeper"
describe ScoreKeeper do
describe "calculating score" do
let(:score_keeper) { described_class.new }
context "when rolls are valid" do
{
"xxxxxxxxxxxx" => 300,
"--------------------" => 0,
"9-9-9-9-9-9-9-9-9-9-" => 90,
"5/5/5/5/5/5/5/5/5/5/5" => 150,
"14456/5/---17/6/--2/6" => 82,
"9/3561368153258-7181" => 86,
"9-3/613/815/0/8-7/8-" => 121,
"x3/61xxx2/9-7/xxx" => 193
}.each do |bowling_stats, score|
it "returns #{score} for #{bowling_stats}" do
expect(score_keeper.calculate(bowling_stats)).to eq score
end
end
end
end
end
The failing inputs are:
"5/5/5/5/5/5/5/5/5/5/5" (expected: 150, got: 155),
"x3/61xxx2/9-7/xxx" (expected: 82, got: 88),
"14456/5/---17/6/--2/6" (expected: 193, got: 223)
The first thing I see is your use of gsub:
input.gsub(/-/, "0")
You're not assigning the string returned by gsub to anything, and instead you're throwing it away.
input = '#0#'
input.gsub('0', '-') # => "#-#"
input # => "#0#"
I suspect you're thinking of the mutating gsub! but instead I suggest simply passing the value to split:
_frames = input.gsub(/-/, "0").split(//)
Your code is not idiomatic Ruby; There are a number of things you need to do differently:
Instead of if !input.is_a? String use:
unless input.is_a? String
raise argumentException, "Score Keeper will only except string types for score calculation."
end
It's considered better to use unless than a negated test.
Instead of
if input == "xxxxxxxxxxxx"
return 300
end
use a "trailing if":
return 300 if input == "xxxxxxxxxxxx"
Don't name variables with a leading _. _frames should be frames.
Don't name variables like lastFrame, lastlastFrame and workingValue with mixed-case AKA "camelCase". We use "snake_case" for Ruby variables and methods and camelCase for classes and modules. It_is_a matterOfReadability.
Don't end lines with a trailing ;:
workingValue = 0;
The only time we use a trailing semicolon is when we're using multiple statements on a single line, which should be extremely rare. Just don't do that unless you know why and when you should.
Consider the potential problem you have here:
"12".include?('1') # => true
"12".include?('2') # => true
"12".include?('12') # => true
While your code might skirt that issue, don't write code like that and think about side-effects. Perhaps you want to really test to see if the value is an integer between 1 and 9?
((1 .. 9) === '1'.to_i) # => true
((1 .. 9) === '2'.to_i) # => true
((1 .. 9) === '12'.to_i) # => false
Instead of using
return score
you can simply use
score
Ruby will return the last value seen; You don't have to explicitly return it.
Indent your code properly. Your future self will appreciate it when you have to dive back into code to debug something. Consistenly use two space indents.
Use whitespace liberally to separate your code into readable blocks. It doesn't affect the run-time speed of your code and it makes it a lot easier to read. Again, your future self will appreciate it.
While it might seem nit-picking, those little things go a long way when coding in a team of developers, and failing to do those things can land you in the hot seat during a code-review.
You're problem appears to be that that for your first two frames you're adding the last two frames. Consider the following.
arr = [1,2,3,4,5,6,7,8,9]
arr.each_with_index do |num, i|
puts "current number #{num}"
puts arr[i-1]
puts arr[i-2]
end
I think you need an if statement to handle the first two frames because - index will loop back to the end of the array if you're at 0 index.
so you need something like
arr = [1,2,3,4,5,6,7,8,9]
arr.each_with_index do |num, i|
puts "current number #{num}"
if i <= 1
puts "no previous frame"
elsif i == 1
puts arr[i-1] + "can be added to frame 2"
else
puts arr[i-1] + "can be added to frame 1"
puts arr[i-2] + "can be added to frame 2"
end
end
In a game of rock-paper-scissors, each player chooses to play Rock (R), Paper (P), or Scissors (S). The rules are: Rock breaks Scissors, Scissors cuts Paper, but Paper covers Rock. In a round of rock-paper-scissors, each player's name and strategy is encoded as an array of two elements. Create a RockPaperScissors class with a class method winner that takes two 2-element arrays like those above, and returns the one representing the winner:
RockPaperScissors.winner(['Armando','P'], ['Dave','S']) # => ['Dave','S']
If either player's strategy is something other than "R", "P" or "S" (case-SENSITIVE), the method should raise a 'RockPaperScissors::NoSuchStrategyError' exception and provide the message: "Strategy must be one of R,P,S"
If both players use the same strategy, the first player is the winner.
I have my code below. My code is not comparing the two strings correctly in the line
(#p1[1,1]==rules["#{p}"]?#p1:#p2).
Please help me out.
class RockPaperScissors
def winner(p1,p2)
#p1 = p1
#p2 = p2
p = (#p1[1,1]+#p2[1,1]).sort
rules = Hash.new(0)
rules = {"PR"=>"R","PS"=>"S", "RS"=>"R", "PP"=>"1","RR"=>"1","SS"=>"1"}
if rules["#{p}"].nil?
raise RockPaperScissors::NoSuchStrategyError,"Strategy must be one of R,P,S"
elseif rules["#{p}"]=="1"
return #p1
else
print #p1[1,1]
print rules["#{p}"]
#p1[1,1]==rules["#{p}"]?#p1:#p2
end
end
end
t = RockPaperScissors.new
print t.winner(['Armando','R'], ['Dave','S'])
Some general tips: You don't need [1,1], [1] or .last would be better. Also no need to initialize rules to a new hash, you can only keep the line rules = {"PR"=>"R".... puts is more commonly used than print. You're overthinking this a bit. Maybe clean up your code, try to simplify it with the tips posted above and see if this gets you unstuck.
Once you are done, have a look at what an idiomatic Ruby solution could look like, but don't submit it as your solution:
module RockPaperScissors
VALID_STRATEGIES = %i(R P S)
RULES = { R: :S, P: :R, S: :P }
def self.winner(p1, p2)
choice1, choice2 = p1.last.intern, p2.last.intern
unless [choice1, choice2].all? { |s| VALID_STRATEGIES.include? s }
raise RockPaperScissors::NoSuchStrategyError, "Strategy must be one of R,P,S"
end
return p1 if choice1 == choice2
RULES[choice1] == choice2 ? p1 : p2
end
end
When you use the [1,1] on an array, you receive an array of size 1 starting from index 1:
[1,2,3][1,1]
# => [2]
[1,2,3][1]
# => 2
Because of that when you compare it to the rules, you never get true, since no rule is an array...
["S"] == "S"
# => false
So to fix your code, instead of
p = (#p1[1,1]+#p2[1,1]).sort
# ...
(#p1[1,1]==rules["#{p}"]?#p1:#p2)
You should try:
p = (#p1[1]+#p2[1]).sort
# ...
(#p1[1]==rules[p]?#p1:#p2
I'm testing my basics of Ruby by writing a simple ChingChongCha program. One of my methods takes the choice entered and turns it into a number (for easy of use for future in the program) however this if statement keeps defaulting to the 'else' condition, even though I can see clearly that if rock is entered it matches the if condition perfectly with ROCK. Any ideas?
def user_choice(choice)
# 1 is rock
# 2 is paper
# 3 is scissors
userintchoice = 0
choice.upcase!
# turns user's choice into an integer
puts choice #debugging
if (choice == 'ROCK') then
userintchoice = 1
elsif (choice == 'PAPER') then
userintchoice = 2
elsif (choice == 'SCISSORS') then
userintchoice = 3
else
puts "Invalid Choice!"
end
return userintchoice
end
Code calling this method and getting input is:
puts "What would you like to choose (input Rock, Paper or Scissors and <ENTER>)?"
userstringchoice = gets()
userchoice = user_choice(userstringchoice)
It seems you have to call .strip on userchoice because otherwise the string will contain a trailing \n.