I have to implement a small part of a Rock, Paper, Scissors game for an assignment. Here is my code:
class RockPaperScissors
# Exceptions this class can raise:
class NoSuchStrategyError < StandardError ; end
def self.winner(player1, player2)
# YOUR CODE HERE
puts "player1 = " + player1[1]
if (player1[1] || player2[1] != 'R') || (player1[1] || player[2] != 'P') || (player1[1] || player2[1] != 'S')
raise NoSuchStrategyError.new("Strategy must be one of 'R','S','P'")
else
if player1[1] == player2[1]
return player1
elsif (player1[1] == 'R' && player2[1] == 'P') || (player1[1] == 'P' && player2[1] == 'S') || (player1[1] == 'S' && player2[1] == 'R')
return player2
else
return player1
end
end
end
def self.tournament_winner(tournament)
# YOUR CODE HERE
end
end
I am using rspec to test out the code and it breaks in both of the below cases. I am assuming that it has to do with where I check to make sure that the input is valid, and even though the input is valid, for whatever reason, the way I am doing the line if (player1[1] || player2[1] != 'R') || (player1[1] || player[2] != 'P') || (player1[1] || player2[1] != 'S') is making the input fail on every case. How can I fix that line so that if the input is valid, then it doesn't raise an error?
Failures:
1) RockPaperScissors game rock breaks scissors [10 points]
Failure/Error: raise NoSuchStrategyError.new("Strategy must be one of 'R','S','P'")
RockPaperScissors::NoSuchStrategyError:
Strategy must be one of 'R','S','P'
# ./lib/rock_paper_scissors.rb:11:in `winner'
# ./spec/rock_paper_scissors_spec.rb:10:in `block (3 levels) in <top (required)>'
EDIT: player1 and player2 are arrays with format ["playername", "move"] with the move being either R,S or P for rock, scissors or paper
Related
Just started learning looping and flow control in Ruby and got stuck with this exercise. I've been testing and searching for answers but did not find any so I'm posting here.
If I have my code setup like this:
ask_play = ''
loop do
print "Play?: "
ask_play = gets.chomp
break if (ask_play == 'n') || (ask_play == 'N')
end
Then I exit out of the loop after entering n or N.
However, if I have my code setup like this:
ask_play = ''
play_stop = (ask_play == 'n') || (ask_play == 'N')
loop do
print "Play?: "
ask_play = gets.chomp
break if play_stop
end
The condition does not seem to work. I still keep on looping even after typing in n or N and I'm just puzzled why.
play_stop = (ask_play == 'n') || (ask_play == 'N') gets evaluated before the loop begins. It sets the play_stop variable to false because the ask_play variable that you assigned to an empty string is neither n nor N.
It looks like you want to encapsulate the logic of the condition. You can create a method and pass a value to it.
def play_stop(input)
input == 'n' || input == 'N'
end
loop do
print "Play?: "
ask_play = gets.chomp
break if play_stop(ask_play)
end
You could use a proc to encapsulate the condition:
ask_play = ''
play_stop = -> { ask_play == 'n' || ask_play == 'N' }
loop do
print "Play?: "
ask_play = gets.chomp
break if play_stop.call
end
You could also pass the string to the proc instead of referring to the local variable:
play_stop = -> (s) { s == 'n' || s == 'N' }
loop do
print "Play?: "
break if play_stop.call(gets.chomp)
end
Procs also work nicely in case statements:
play_stop = -> (s) { s == 'n' || s == 'N' }
loop do
print "Play?: "
case gets.chomp
when play_stop
break
end
end
I have the following tic-tac-toe game: (I'm a noob, please disregard the design of the class, the game works, that's all I care about for now.)
#a tic tac toe game
class TicTacToe
require "yaml"
attr_accessor :player1, :player2
#crates playes and a game board to play tic tac toe
def initialize()
#player1 = Player.new("Player One", "x")
#player2 = Player.new("Player Two", "o")
#game_board = Board.new
end
#prints the board
def print_board
#game_board.board.each_with_index do |row, index|
puts "#{row.join(" | ")}"
puts "---------" unless index == 2
end
puts
end
#determines whose move it is
def move
if #turn % 2 == 1
player_one_turn
else
player_two_turn
end
#turn += 1
end
def valid_move?(row, col)
if #game_board.board[row][col] == " "
return true
else
return false
end
end
#player ones turn
def player_one_turn
print_board
puts "#{#player1.name} it's your turn:"
puts "Enter a row (0-2)"
row = gets.chomp.to_i
puts "Enter a column (0-2)"
col = gets.chomp.to_i
if valid_move?(row, col)
#game_board.board[row][col] = #player1.shape
else
puts "There's already a shape at that position."
player_one_turn
end
if win?(#player1.shape)
winner(#player1.name)
#winner = true
end
end
#player two's turn
def player_two_turn
print_board
puts "#{#player2.name} it's your turn:"
puts "Enter a row (0-2)"
row = gets.chomp.to_i
puts "Enter a column (0-2)"
col = gets.chomp.to_i
if valid_move?(row, col)
#game_board.board[row][col] = #player2.shape
else
puts "There's already a shape at that position."
player_two_turn
end
if win?(#player2.shape)
winner(#player2.name)
#winner = true
end
end
def win?(shape)
if (#game_board.board[0][0] == shape) && (#game_board.board[0][1] == shape) && (#game_board.board[0][2] == shape)
return true
elsif (#game_board.board[1][0] == shape) && (#game_board.board[1][1] == shape) && (#game_board.board[1][2] == shape)
return true
elsif (#game_board.board[2][0] == shape) && (#game_board.board[2][1] == shape) && (#game_board.board[2][2] == shape)
return true
elsif (#game_board.board[0][0] == shape) && (#game_board.board[1][0] == shape) && (#game_board.board[2][0] == shape)
return true
elsif (#game_board.board[0][1] == shape) && (#game_board.board[1][1] == shape) && (#game_board.board[2][1] == shape)
return true
elsif (#game_board.board[0][2] == shape) && (#game_board.board[1][2] == shape) && (#game_board.board[2][2] == shape)
return true
elsif (#game_board.board[0][0] == shape) && (#game_board.board[1][1] == shape) && (#game_board.board[2][2] == shape)
return true
elsif (#game_board.board[0][2] == shape) && (#game_board.board[1][1] == shape) && (#game_board.board[2][0] == shape)
return true
else
return false
end
end
def draw?
if #turn > 9
print_board
puts "The game ended in a draw. :)"
#winner = true
return true
end
false
end
def winner(winner_name)
puts "#{winner_name}, YOU WIN!!!"
end
def play
#turn = 1
#winner = false
until #winner
move unless draw?
save
end
end
#a class that generates an empty board
class Board
attr_accessor :board
def initialize
#board = [[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]
end
end
#a class that assigns creates plaers and assigns them a shape "x" or "o"
class Player
attr_accessor :name, :shape
def initialize(name, shape)
#name = name
#shape = shape
end
end
def save
yaml = YAML::dump(self)
File.open("save.txt", "w"){|file| file.write(yaml)}
end
def self.load
file = File.read("save.txt")
YAML::load_file(file)
end
end
game = TicTacToe.new
game.play
I want to start playing the game, quit the program in the middle of the game and then come back and finish it later after I call TicTacToe.load. However, when I do this now, the YAML file is loaded, but program does not resume where it's supposed to.
Can someone tell me if there is a way to do what I'm trying to do?
I regionally thought that doing something like YAML::load(self) would automatically load the save state of the file I was referring to via some kind of magic. However, I have come to learn that that the way that I designed my class and the dependencies in my "play" function would not allow me to load the previous state if my file.
When loading a YAML file, one has to load the file to a variable and then manually assign the object values to values of the class. That way, the current state of the variables are pretty much being manually assigned to the instance variables of the class. For example, I could have done something like this: file = YAML.load("file_name"), then assign variables values like: #board = file.board.
Had I known this before, I would have designed my class with less dependencies so that it would be loadable in a much cleaner and more convenient way.
I'm trying to build a simple 2 player tic tac toe game in Ruby.
Here is the code:
class Morpion
def init
create_grid
get_player
show_grid
end
def get_player
puts "Let play some Tic Tac Toe"
puts ""
#player1 ='X'
#player2='O'
puts ""
puts "Where would you like to move? (check out the grid below and type any number 1-9 to place your symbol): "
puts " 1 | 2 | 3 "
puts "---+---+---"
puts " 4 | 5 | 6 "
puts "---+---+---"
puts " 7 | 8 | 9 "
end
def create_grid
#grid = {
'1' => ' ',
'2' => ' ',
'3' => ' ',
'4' => ' ',
'5' => ' ',
'6' => ' ',
'7' => ' ',
'8' => ' ',
'9' => ' '
}
end
def show_grid
puts ""
puts "#{#grid['1']}|#{#grid['2']}|#{#grid['3']}"
puts "-----"
puts "#{#grid['4']}|#{#grid['5']}|#{#grid['6']}"
puts "-----"
puts "#{#grid['7']}|#{#grid['8']}|#{#grid['9']}"
puts ""
end
def play
number_turns=1
while number_turns < 10
number_turns.odd? ? player_turn(#player1) : player_turn(#player2)
game_checker
if game_checker
break
end
number_turns+=1
end
end
def player_turn(player)
puts player == 'X' ? "It's X's turn!" : "It's O's turn!"
puts ""
cell = gets.chomp
unless #grid.keys.include?(cell) #check if the user entered a number corresponding to the grid
puts ""
puts "it has to be a number from 1 to 9"
player_turn(player)
end
if #grid[cell] == ' ' #check if cell in grid is empty for user input
#grid[cell] = player
else
puts ""
puts "That cell is occupied. Choose again!"
player_turn(player)
end
show_grid
end
def game_checker
end_game = false
if #grid['1'] != ' ' && #grid['5'] != ' ' && #grid['9'] != ' '
if (#grid['1'] == #grid['2'] && #grid['1'] == #grid['3'])
end_game = true
victory = #grid['1']
elsif (#grid['4'] == #grid['5'] && #grid['4'] ==#grid['6'])
end_game = true
victory = #grid['4']
elsif (#grid['7'] == #grid['8'] && #grid['7'] == #grid['9'])
end_game = true
victory = #grid['7']
elsif (#grid['1'] == #grid['4'] && #grid['1'] == #grid['7'])
end_game = true
victory = #grid['1']
elsif (#grid['2'] == #grid['5'] && #grid['2'] == #grid['8'])
end_game= true
victory = #grid['2']
elsif (#grid['3'] == #grid['6'] && #grid['3'] == #grid['9'])
end_game = true
victory = #grid['3']
elsif (#grid['1'] == #grid['5'] && #grid['1'] == #grid['9'])
end_game = true
victory = #grid['1']
elsif (#grid['3'] == #grid['5'] && #grid['3'] == #grid['7'])
end_game = true
victory = #grid['3']
else
end_game = false
end
end
if end_game
puts "the winner of this game is #{victory}"
return true
end
end
end
m=Morpion.new
m.play
So my issue is this:
1. I am asking a player to add his symbol (X or O) in the grid that ranges from 1 to 9 (because there is 9 cells)
if I enter 1 for example, which is the upper left cell I get this error:
(eval):187: undefined method `keys' for nil:NilClass (NoMethodError)
from (eval):168:in `play'
from (eval):245
If you want to run this program I suggest using THIS LINK
EDIT:
The problem as #Paul and #August stated was that I used an incorrect constructor method init instead of using the correct one: initialize.
Now my program works. Thanks to them.
You initialize the #grid hash in a method called init. Ruby won't call this method when you construct a new instance of Game. You should instead rename the init method to initialize, which will be called automatically by Ruby.
The problem is that your #grid variable is never being created; it is nil. Hence the error message, you're attempting to invoke a method on a nil object.
The cause of your woes is because you've misnamed the constructor method. In Ruby, constructors are named initialize, however you named it init. Rename it to the correct name, and it should work.
This is my code:
class RockPaperScissors
# Exceptions this class can raise:
class NoSuchStrategyError < StandardError
end
def self.winner(player1, player2)
if ((player1[1] == 'R') && (player2[1] == 'S') ||
(player1[1] == 'S') && (player2[1] == 'P') ||
(player1[1] == 'P') && (player2[1] == 'R'))
return player1
elsif ((player1[1] == 'R') && (player2[1] == 'P') ||
(player1[1] == 'S') && (player2[1] == 'R') ||
(player1[1] == 'P') && (player2[1] == 'S'))
return player2
elsif ((player1[1] == 'R') && (player2[1] == 'R') ||
(player1[1] == 'S') && (player2[1] == 'S') ||
(player1[1] == 'P') && (player2[1] == 'P'))
return player1
end
end
def self.tournament_winner(tournament)
player1 = Array.new
player2 = Array.new
nextround = Array.new
while tournament.length != 1 do
tournament.each_with_index {|item, index|
if (index%2!=0)
player2[0] = item[0]
player2[1] = item[1]
elsif (index%2 ==0)
player1[0] = item[0]
player1[1] = item[1]
else
puts 'bananas'
end
if (index%2!=0)
nextround[(index-1)/2] = winner(player1, player2)
end
}
tournament=nextround
end
return tournament
end
end
RockPaperScissors.tournament_winner([["num1", "R"], ["num2", "P"], ["num3", "P"], ["num4", "R"]])
Well, the last line is an execution launch. This code makes a tournament of rock, paper scissors. It takes as input an array of arrays with each character and its attack, and it has to return the array with the champion and its attack.
The tournament is num1 vs num2 (num2 wins), and num3 vs num4 (num3 wins). Then the final is Num2 vs Num3, and in this stablemate wins the first guy in the array (Num2).
It seems overcomplicated because the code has to work with any number of characters, as long as their number is base2 (2, 4, 8, 16 characters..., etc).
My problem is next (debug the code and you will see). When it changes the value of the array 'Player1' or 'Player2', it also changes the value in the array 'nextround', even if it is not in that line!
That is not suppose to happen!
By the way, I am learning Ruby so it may be a really stupid failure.
why does this need to be true?
"It seems overcomplicated because the code has to work with any number of characters, as long as their number is base2 (2, 4, 8, 16 characters..., etc)."
Rather having player1 and player2 be arrays, I would rewrite them to be instances of a class Player. Then write methods in the Player class so you can call player1.hand and it returns 'S' || 'R' || 'P'
that way you can store how many wins a player has on the player object,
things I'd look into learning more about
case/when statements
the special initialize method
attrs_accessor (used for making data accessible across classes)
modules
also i've seen it done and i may be wrong on this, but generally you don't put classes inside classes.
I am not sure what's happening here...but say I do this:
def who_wins?(choice1, choice2)
if (choice1 == 'R' && choice2 == 'S') || (choice1 == 'S' && choice2 == 'P') || (choice1 == 'P' && choice2 == 'R')
return choice1
elsif choice1 == choice2
return "tie"
else
raise NoSuchStrategyError
end
end
won_wins?('R', 'P')
It gives me the following error:
NoMethodError: undefined method `won_wins?' for main:Object
at top level in my-file.rb at line 25
Why would it do that, even though I am calling the method AFTER the definition?
You typed won_wins? and not who_wins?