i am just starting my journey with ruby and am very new to programming in general as well. This is code for a homework assignment. I am getting syntax error as described in title. I understand that I have added some extra end while end of file was expected? right?
but where is this extra "end".
The code:
class WrongNumberOfPlayersError < StandardError ; end
class NoSuchStrategyError < StandardError ; end
def rps_result(move1,move2)
if (move1 == "r" && move2 == "s") || (move1 == "s" && move2 == p) || (move1 == "p" && move2 == "r")
return true
else
return false
end
end
def rps_game_winner(player1, player2)
strategy = /[rps]/
raise NoSuchStrategyError unless (move1 =~ strategy) & (move2 =~ strategy)
move1 = player1[1].downcase
move2 = player2[2].downcase
rps_result(move1, move2)? player1 : player2
end
end
def rps_tournament_winner(game)
round_winners = []
if game.length < 2
raise WrongNumberOfPlayersError
else
game.each_slice(2) do
|l1 , l2|
round_winners << rps_game_winner(l1, l2)
end
rps_tournament_winner(round_winners)
end
end
rps_tournament_winner([[[["Richard", "S"], ["Dave", "S"]], [["Richard", "R"], ["Michael", "S"]]], [[["Allen", "S"], ["Omer", "P"]], [["David E.", "R"], ["Richard X.", "P"]]]])
The rps_result method is never closed. Move the second end at the end of rps_game_winner to the last intended line of the rps_result method.
P.s. sorry for all the end's :)
I have modified the second block of code. Please check if it works.
def rps_game_winner(player1, player2)
strategy = /[rps]/
raise NoSuchStrategyError unless (move1 =~ strategy) && (move2 =~ strategy)
move1 = player1[1].downcase
move2 = player2[2].downcase
rps_result(move1, move2) ? player1 : player2
end
def rps_tournament_winner(game)
round_winners = []
if game.length < 2
raise WrongNumberOfPlayersError
else
game.each_slice(2) do |l1 , l2|
round_winners << rps_game_winner(l1, l2)
end
rps_tournament_winner(round_winners)
end
end
rps_tournament_winner([[[["Richard", "S"], ["Dave", "S"]], [["Richard", "R"], ["Michael", "S"]]], [[["Allen", "S"], ["Omer", "P"]], [["David E.", "R"], ["Richard X.", "P"]]]])
Did 4 changes:
Removed extra end in rps_game_winner method.
Added space between tertiary operators in rps_game_winner method.
rps_game_winner method, 2nd line, unless condition had only one &.
In rps_tournament_winner method, moved the args |l1, l2| after do.
Related
I made a TicTacToe game in Ruby. I am pretty new to Ruby OOP. My win conditions for checking row, column and diagonals are really heavy and they take too much code line and look pretty horrible. My code also always repeating.
So my question is how to implement the below win conditions into my code?
WIN_COMBINATIONS = [
[0,1,2], # top_row
[3,4,5], # middle_row
[6,7,8], # bottom_row
[0,3,6], # left_column
[1,4,7], # center_column
[2,5,8], # right_column
[0,4,8], # left_diagonal
[6,4,2] # right_diagonal
]
Here is my win conditions:
module CheckWinner
def check_horizontal(player1, player2)
player1_win = false
player2_win = false
#board.each do |row|
player1_win = row.all?(player1)
player2_win = row.all?(player2)
break if player1_win || player2_win
end
puts "#{player1} won!" if player1_win
puts "#{player2} won!" if player2_win
player1_win || player2_win
end
def check_vertical(player1, player2)
player1_win = false
player2_win = false
#board.transpose.each do |row|
player1_win = row.all?(player1)
player2_win = row.all?(player2)
break if player1_win || player2_win
end
puts "#{player1} won!" if player1_win
puts "#{player2} won!" if player2_win
player1_win || player2_win
end
def check_diagonal(player1, player2)
if #board[0][0] == player1 && board[1][1] == player1 && board[2][2] == player1 ||
#board[0][2] == player1 && board[1][1] == player1 && board[2][0] == player1
puts "#{#player1} won!"
true
elsif #board[0][0] == player2 && board[1][1] == player2 && board[2][2] == player2 ||
#board[0][2] == player2 && board[1][1] == player2 && board[2][0] == player2
puts "#{#player2} won!"
true
end
end
end
I invoke these conditions from this line of code:
def game
choosing_player
loop do
player1(#player1)
break if check_vertical(#player1,#player2) == true || check_diagonal(#player1,#player2) == true || check_horizontal(#player1,#player2) == true || full?
player2(#player2)
break if check_vertical(#player1,#player2) == true || check_diagonal(#player1,#player2) == true || check_horizontal(#player1,#player2) == true || full?
end
end
end
Here is my full of project that it is correctly working.
module CheckWinner
def check_horizontal(player1, player2)
player1_win = false
player2_win = false
#board.each do |row|
player1_win = row.all?(player1)
player2_win = row.all?(player2)
break if player1_win || player2_win
end
puts "#{player1} won!" if player1_win
puts "#{player2} won!" if player2_win
player1_win || player2_win
end
def check_vertical(player1, player2)
player1_win = false
player2_win = false
#board.transpose.each do |row|
player1_win = row.all?(player1)
player2_win = row.all?(player2)
break if player1_win || player2_win
end
puts "#{player1} won!" if player1_win
puts "#{player2} won!" if player2_win
player1_win || player2_win
end
def check_diagonal(player1, player2)
if #board[0][0] == player1 && board[1][1] == player1 && board[2][2] == player1 ||
#board[0][2] == player1 && board[1][1] == player1 && board[2][0] == player1
puts "#{#player1} won!"
true
elsif #board[0][0] == player2 && board[1][1] == player2 && board[2][2] == player2 ||
#board[0][2] == player2 && board[1][1] == player2 && board[2][0] == player2
puts "#{#player2} won!"
true
end
end
end
# TicTacToe Board
class Board
include CheckWinner
attr_accessor :board
def initialize
#board = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
end
def print_board
puts '-------------'
#board.each do |row|
print '|'
row.each do |col|
print " #{col}"
print ' | '
end
puts
end
puts '-------------'
end
def twodimentional_board
#board = #board.each_slice(3).map { |el| el }
end
def occupied_error(value)
puts 'There is a value or wrong place! Try Again!'
twodimentional_board
print_board
value == #player1 ? player2(#player1) : player1(#player2) # Stay same player
end
def move_if_possible(place, value)
#board.flatten!
if #board[place - 1] == 'X' || #board[place - 1] == 'O' || !place.between?(1, 9)
occupied_error(value)
else
#board[place - 1] = value
twodimentional_board
#board
end
end
def full?
if #board.flatten.all?(String)
puts 'Draw!'
true
end
end
def choosing_player
puts 'Choose for Player1(X or O)'
loop do
#player1 = gets.chomp!
break if #player1 == 'X' || #player1 == 'O'
puts 'Try Again!(X or O)'
end
puts "Player 1 is: #{#player1}"
#player1 == 'X' ? #player2 = 'O' : #player2 = 'X'
puts "Player 2 is: #{#player2}"
print_board
end
def player1(player1)
puts "Choice #{player1} Place on a board(1 to 10)"
#place = gets.chomp!.to_i
move_if_possible(#place, player1)
print_board
end
def player2(player2)
puts "Choice #{player2} Place on a board(1 to 10)"
#place = gets.chomp!.to_i
move_if_possible(#place, player2)
print_board
end
def game
choosing_player
loop do
player1(#player1)
break if check_vertical(#player1,#player2) == true || check_diagonal(#player1,#player2) == true || check_horizontal(#player1,#player2) == true || full?
player2(#player2)
break if check_vertical(#player1,#player2) == true || check_diagonal(#player1,#player2) == true || check_horizontal(#player1,#player2) == true || full?
end
end
end
board = Board.new
board.game
If you want to live preview here is repl link
I'm not sure if this is exactly what you're after, but there are a few things here.
your #board is an array of 3 rows, but your possible wins is just an array of points. flattening your #board means you can use the index as is.
you already have an array of win conditions, you then ignore this array and work through manually all options
definitions
# example winning board for 'O'
#board = [['O','X','O'], ['X','O','X'], ['X','O','O']]
player1 = 'X'
player2 = 'O'
First, loop through the possible combinations in WIN_COMBINATIONS, and for each of those combinations get those cells from #board as individual arrays, for later checking. store the array of arrays as another array (using #map). the * character is the ruby splat operator, which converts an array into arguments for the called procedure.
shorthand:
winmap = WIN_COMBINATIONS.map {|w| #board.flatten.values_at(*w)}
longhand:
flat_board = #board.flatten
winmap = WIN_COMBINATIONS.map do |win|
flat_board.values_at(*win)
end
which sets up an array like follows:
winmap = [
["O", "X", "O"], # top_row
["X", "O", "X"], # middle_row
["X", "O", "O"], # bottom_row
["O", "X", "X"], # left_column
["X", "O", "O"], # center_column
["O", "X", "O"], # right_column
["O", "O", "O"], # left_diagonal
["X", "O", "O"] # right_diagonal
]
once you have the list of all possible winning cells from the board, you can use your existing row searching code, except rather than looping through each array, we use the array operation #any? to identify matches
player1_win = winmap.any?{|m| m.all?(player1)}
player2_win = winmap.any?{|m| m.all?(player2)}
this should eliminate all the individual subroutines, and give you a true/false answer for if anyone has won.
Here's a different approach to determining if the board indicates a win, and if it does, who wins.
Let board be an array of three elements, each an array of three elements, each element being 'X', 'O' or '_', the last indicating the cell has not yet been marked.
Let's create some methods.
def row_win?(board)
board.each { |row| return row.first if row_uniq.size == 1 }
nil
end
This method returns 'X' if 'X' wins in a row, 'O' if 'O' wins in a row and nil if neither player wins in a row.
def column_win?(board)
row_win?(board.transpose)
end
This method similarly returns 'X', 'O' or nil, indicating if there is a win in a column, and if so, who wins.
def diagonal_win?(board)
row_win? [[board[0][0], board[1][1], board[2][2]],
[board[0][2], board[1][1], board[2][0]]]
end
This method does the same for the two diagonals.
Lastly,
def win?(board)
row_win?(board) || column_win?(board) || diagonal_win?(board)
end
nil is returned if there is no winner.
Let's try it.
win? [['X', '_', 'X'],
['_', 'X', 'O'],
['X', 'O', 'O']]
#=> 'X'
win? [['X', 'O', 'X'],
['_', 'O', 'X'],
['X', 'O', 'O']]
#=> 'O'
win? [['X', 'O', 'X'],
['O', 'O', 'X'],
['X', 'X', 'O']]
#=> nil
Why don't you track state helping you determine winning condition as you mark new moves? That way you don't have to recompute all what you already know.
For instance, if I tell you my board size is 3, that there's already 2 Xs in the first row and now X played a new move in that row leading to 3 Xs, you know X won immediately right? You don't even need to know anything else about the state of the board.
You can apply the same logic here and simply keep track of X & O counts per row, cols & diags as marks are recorded and you won't even have to loop to check who's won.
The above-described approach is easy to implement and takes O(1) time complexity to check the winning condition. You trade a bit of memory (O(n)) for computation speed.
I'm having a problem with my code, and can't seem to figure out what I need to change. Here are my three files, and at the bottom is the error that I am getting.
require './PokerHand'
require "./Constants"
require 'minitest/autorun'
class TestClass < MiniTest::Test
include Constants
def test_1
arr1 = [Card.new(2, "S"), Card.new(3, "S"),
Card.new(4, "S"), Card.new(5, "S"),
Card.new(6, "S")]
ph1 = PokerHand.new(arr1)
ph1.classify
assert_equal STRAIGHT_FLUSH, ph1.hand_type
end
def test_2
arr2 = [Card.new(9, "C"), Card.new(9, "S"),
Card.new(9, "H"), Card.new(9, "D"),
Card.new(11, "S")]
ph2 = PokerHand.new(arr2)
ph2.classify
assert_equal FOUR_OF_A_KIND, ph2.hand_type
end
def test_3
arr3 = [Card.new(4, "C"), Card.new(9, "S"),
Card.new(9, "H"), Card.new(9, "D"),
Card.new(9, "C")]
ph3 = PokerHand.new(arr3)
ph3.classify
assert_equal FOUR_OF_A_KIND, ph3.hand_type
end
new file Pokerhand.rb:
require "./Constants"
require "./Card"
require "./Deck"
require "./CardSpaceship"
class PokerHand < Deck
include Constants
attr_reader :hand_type
def initialize(the_cards)
#cards = [ ]
#hand_type = UNCLASSIFIED
for card in the_cards
#cards << card
end
end
# Determine hand type of PokerHand object.
def classify
#cards.sort!
# Straight flush
if #cards[0].rank == #cards[1].rank +1 &&
#cards[1].rank == #cards[2].rank +1 &&
#cards[2].rank == #cards[3].rank +1 &&
#cards[3].rank == #cards[4].rank +1 &&
#cards[0].suit == #cards[1].suit &&
#cards[1].suit == #cards[2].suit &&
#cards[2].suit == #cards[3].suit &&
#cards[3].suit == #cards[4].suit
#hand_type = STRAIGHT_FLUSH
end
end
new file test2.rb:
require './PokerHand'
require "./Constants"
require 'minitest/autorun'
class TestClass < MiniTest::Test
include Constants
def test_1
arr1 = [Card.new(2, "S"), Card.new(3, "S"),
Card.new(4, "S"), Card.new(5, "S"),
Card.new(6, "S")]
ph1 = PokerHand.new(arr1)
ph1.classify
assert_equal STRAIGHT_FLUSH, ph1.hand_type
end
getting error:
TestClass#test_1:
PokerHand.rb:145: warning: else without rescue is useless
C:/Ruby22/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require': JulianHansen_P5/PokerHand.rb:30: syntax error, unexpected tINTEGER, expecting keyword_then or ';' or '\n' (SyntaxError)
if #cards[0].rank == #cards[1].rank +1 &&
^
PokerHand.rb:31: syntax error, unexpected tINTEGER, expecting keyword_end
#cards[1].rank == #cards[2].rank +1 &&
The warning: else without rescue is useless warning is displayed when you setup an if/else statement improperly.
if true
puts 'hi''
end # end shouldn't be here
else
puts 'whoops'
end
You should find that in your code and correct it as well, although that isn't what is causing the fatal error.
The +1s in your if statement either need spaces or parentheses:
if #cards[0].rank == #cards[1].rank + 1 &&
or
if #cards[0].rank == (#cards[1].rank +1) &&
I am banging my head against the error message I keep getting, reading that "syntax error, unexpected keyword_end, expecting end-of-input." I cannot find my mistake for the life of me. It's probably sloppy-looking, I am a newbie. And any tips on preventing this specific issue in the future would also be greatly appreciated!
$move_direction_hash = {"N" => [0,1], "E" => [1,0], "S" => [0,-1], "W" => [-1,0]}
$cardinal_directions = ["N", "E", "S", "W"]
class Martian
attr_accessor :coordinate_x, :coordinate_y, :bearing, :direction_string
def initialize (coordinate_x, coordinate_y, bearing, direction_string)
#coordinate_x = coordinate_x
#coordinate_y = coordinate_y
#bearing = bearing
#direction_string = direction_string
end
def check_valid
#coordinate_x.between?(0, $boundaries[0]) && coordinate_y.between?(0, $boundaries[1])
return true
end
end
#will be second and last called in source code
def get_final
return "#{coordinate_x} #{coordinate_y} #{bearing}"
end
def move_forward
# find where in the hash the bearing is
position_array = $move_direction_hash[#bearing]
# returns a temporary variable
# that is the
test_x = #coordinate_x + position_array[0]
test_y = #coordinate_y + position_array[1]
if $rovers_on_grid.include?([test_x.to_s, test_y.to_s])
puts "Stopping Martian. You're about to crash!"
get_final
break
else
#coordinate_x = test_x
#coordinate_y = test_y
if check_valid == false
puts "Stopping Martian. About to run off the plateau."
get_final
break
else
return #coordinate_x, #coordinate_y
end
end
end
def add_to_grid
$rovers_on_grid << [#x_coordinate, #y_coordinate]
end
def read_instructions
#direction_string.each_char do |direction|
if direction == "L" || direction == "R"
position = $cardinal_directions.index(#bearing)
if direction == "L"
position = (position - 1)%4
$cardinal_directions[position]
elsif direction == "R"
position = (position + 1)%4
$cardinal_directions[position]
else
puts "Error!"
end
#bearing = $cardinal_directions[position]
elsif direction == "M"
move_forward
end
end
end
end
This error is located in the check_valid method. You missed the if.
def check_valid
if #coordinate_x.between?(0, $boundaries[0]) && coordinate_y.between?(0, $boundaries[1])
return true
end
end
Like steenslag mentioned the if statement is not required. You can write:
def check_valid
return #coordinate_x.between?(0, $boundaries[0]) && coordinate_y.between?(0, $boundaries[1])
end
I am solving the " rock, paper, scissor" game. Input an array and recursively output the winner. Here is my code:
class RockPaperScissors
# Exceptions this class can raise:
class NoSuchStrategyError < StandardError; end
def self.winner(player1, player2)
strategy = player1[1]+player2[1]
raise NoSuchStrategyError.new("Strategy must be one of R,P,S") if strategy !~ /(R|P|S){2}/
strategy =~ /rs|sp|pr|rr|ss|pp/i ? player1 : player2
end
def self.tournament_winner(tournament)
if tournament.length==2 && tournament.first[0].is_a?(String)
winner(tournament[0],tournament[1])
else
#keep slice the array in half
***winner(tournament_winner(tournament[0,tournament.length/2]), tournament_winner(tournament[tournament.length/2]))***
end
end
end
I got stack level too deep because that code in bold. Is it because the tournament.length is changing so I shouldn't put it inside the recursion? Could someone gives a detailed explanation about how that happened?
I searched the answer and someone used the code below and worked. I wonder why that reference to tournament won't cause the same recursion issue.
winner(tournament_winner(tournament[0]), tournament_winner(tournament[1]))
Thank you for any help!
sample input:
[
[
[ ["Armando", "P"], ["Dave", "S"] ],
[ ["Richard", "R"], ["Michael", "S"] ],
],
[
[ ["Allen", "S"], ["Omer", "P"] ],
[ ["David E.", "R"], ["Richard X.", "P"] ]
]
]
Your code does not slice the tournament in half - tournament[tournament.length/2] does not return the second half of the array - it only returns the item in position tournament.length/2, instead you should do:
winner(tournament_winner(tournament[0...tournament.length/2]), tournament_winner(tournament[tournament.length/2..-1]))
Also, you do not consider an array of length 1, which results in an endless recursion.
Here is a working version of your code:
def tournament_winner(tournament)
if tournament.length == 1
tournament_winner(tournament[0])
elsif tournament.length==2 && tournament.first[0].is_a?(String)
winner(tournament[0],tournament[1])
else
winner(tournament_winner(tournament[0...tournament.length/2]),
tournament_winner(tournament[tournament.length/2..-1]))
end
end
There are so many better ways of handling this than messy regexs, confusing abbreviations, and strings all over the place.
For example, you could go with a more object oriented approach:
module RockPaperScissors
class NoSuchStrategyError < StandardError; end
class Move
include Comparable
attr_reader :strategy
def initialize(strategy)
raise NoSuchStrategyError unless strategies.include?(strategy)
#strategy = strategy
end
def <=>(opposing_move)
if strengths[strategy] == opposing_move.strategy
1
elsif strengths[opposing_move.strategy] == strategy
-1
else
0
end
end
protected
def strengths
{
rock: :scissors,
scissors: :paper,
paper: :rock
}
end
def strategies
strengths.keys
end
end
class Player
attr_reader :name, :move
def initialize(name, move)
#name, #move = name, move
end
end
class Tournament
def initialize(*players)
#player_1, #player_2, _ = *players
end
def results
p1move = #player_1.move
p2move = #player_2.move
if p1move > p2move
"#{#player_1.name} wins."
elsif p2move > p1move
"#{#player_2.name} wins."
else
"Tie."
end
end
end
end
Example usage:
rock = RockPaperScissors::Move.new(:rock)
paper = RockPaperScissors::Move.new(:paper)
player_1 = RockPaperScissors::Player.new('John Smith', rock)
player_2 = RockPaperScissors::Player.new('Corey', paper)
tournament = RockPaperScissors::Tournament.new(player_1, player_2)
tournament.results #=> "Corey wins."
I am trying to split the sub-arrays if there are more than 8. I have tried calling the rps_tournament_winner function on players if it has a flattened length longer than 16 but I get a "stack too deep error".
Do I have to work on the players variable or tournament? I'm looking for a nudge in the right direction; not a complete solution.
def rps_tournament_winner(tournament)
return rps_game_winner(tournament) if tournament.flatten.length == 4
players = tournament.flatten(2)
while players.length > 1
players = players.each_slice(2).map { |x| rps_game_winner(x) }
end
players[0]
end
This is the shortest I was able to come up with (with recursion)
def rps_tournament_winner(games)
if games.flatten.length > 4
rps_game_winner([rps_tournament_winner(games[0]), rps_tournament_winner(games[1])])
else
rps_game_winner(games)
end
end
This is also an elegant solution w/o flattening the array:
def rps_tournament_winner(tournament)
if tournament[0][0].is_a?(Array)
tournament =[rps_tournament_winner(tournament[0]), rps_tournament_winner(tournament[1])]
end
rps_game_winner(tournament)
end
I solved it using recursion
class WrongNumberOfPlayersError < StandardError ; end
class NoSuchStrategyError < StandardError ; end
def rps_game_winner(game)
raise WrongNumberOfPlayersError unless game.length == 2
if game[0][0].is_a?(Array) then
winner1 = rps_game_winner(game[0])
winner2 = rps_game_winner(game[1])
game = [winner1, winner2]
end
raise NoSuchStrategyError unless /^(P|R|S){2}$/ =~ game[0][1] + game[1][1]
case game[0][1]
when "R"
if game[1][1] == "P" then
game[1]
else
game[0]
end
when "P"
if game[1][1] == "S" then
game[1]
else
game[0]
end
when "S"
if game[1][1] == "R" then
game[1]
else
game[0]
end
end
end
def rps_tournament_winner(tournament)
rps_game_winner(tournament)
end
Only answering this to close the question, hopefully my answer will help some one else =D
After staring at it for about three hours, my mind actually stopped napping and i came up with this:
def rps_tournament_winner(tournament)
return rps_game_winner(tournament) if tournament.flatten.length == 4
if tournament.flatten.length == 16
players = tournament.flatten(2)
else
players = tournament.flatten(4)
end
while players.length > 1
players = players.each_slice(2).map { |x| rps_game_winner(x) }
end
players[0]
end
The if statement allows me to check that if a tournament of 8 or more players (the testing was only for 8, 16 and 32 players so I doubt this would work for larger sets) My problem before was that I would only flatten(2) which for the larger tournament would not work.