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.
Related
I can play the game. Switch the player all working fine but not getting result who won the game.
def initialize_board
#count = 9
#player = PLAYER_ONE #current_player
#board = Array.new(3){ Array.new(3, " ") }
end
def play
inputs = get_inputs
return false if !inputs
update_board(inputs)
print_board
end
def switch_player
if(#player == PLAYER_ONE)
#player = PLAYER_TWO
else
#player = PLAYER_ONE
end
end
def game_over?
# #count = #count - 1
# #count <= 0
if check_winner
puts "#{#player} won "
end
end
def check_winner
WIN_COMBINATIONS.find do |indices|
binding.pry
values = #board.values_at(*indices)
values.all?('X') || values.all?('O')
end
end
Here I am getting indices [0,1,2] in all cases while debugging.
The main reason why you're not getting the winner is because your 'values = #board.values_at(*indices)' statement returns an array of arrays. And values.all?('X') || values.all?('O') checks not an 'X' or 'O' pattern but an array object. So you need to flatten an array first.
values.flatten!
Stefan already answered similar question , but his board was one-dimensional because of %w expression, you can read about it here
I've been working on this for a few days, at least. Testing seems to show the correct value is being returned. My problem is being able to grab the best_move value and have it print out. I set up the suggested_move method and try to use return suggested_move(best_move) but it triggers the method for every level back up the tree. Also it returns the wrong value, which I'm guessing is because it's stopping before depth is back to 0.
In my minimax I had a similar setup the difference being the depth was incremented (not decremented) on successive calls. Point being I was able to say if depth == 0 p best_move. So I'm scratching my head because using that same conditional I get a nil class error in this code.
#board = ["X","O","O","X","X","","","O",""]
def available_spaces
#board.map.with_index {|a, i| a == "" ? i+1 : nil}.compact
end
def suggested_move(move)
p "Suggested move: #{move}"
end
def full?
#board.all?{|token| token == "X" || token == "O"}
end
def suggested_move(move)
p "Move: #{move}"
end
def bestmove(board, depth=0, best_score = {})
return 0 if full?
return 1 if won?
best = -Float::INFINITY
available_spaces.each do |space|
#board[space-1] = current_player
best_score[space] = -bestmove(#board, depth-1, {})
#board[space-1] = ""
end
p best_score
if best_score.max_by {|key,value| value }[1] > best
best = best_score.max_by {|key,value| value }[1]
best_move = best_score.max_by {|key,value| value }[0]
end
return best_move
end
bestmove(#board)
I am working on the "Rosetta Code 100 Doors" problem and have hit a wall.
I found "100 doors help using Ruby" which was of some help, but I still can't get my code to work.
My toggle method doesn't work within my array iteration method.
def toggle(state)
if state == 'closed'
state.replace('open')
elsif state == 'open'
state.replace('closed')
end
end
def one_hundred_doors(array)
i = 0
100.times do
i += 1
array.each_with_index.map do |state, index|
if (index + 1) % i == 0
toggle(state)
end
end
end
array.each_with_index { |state, door| puts "Door #{door + 1} is #{state}." }
end
doors = Array.new(100, "closed")
one_hundred_doors(doors)
Can someone please explain what I am doing wrong?
Your problem is in your array creation method. You create it to contain 100 references to the same string:
doors = Array.new(100, "closed")
doors.first.replace("lala")
doors # => ["lala", "lala", ...]
but you need different strings.
Create it this way:
doors = 100.times.map{"closed"}
These are incomplete codes, but it should at least update the game board until it fills up and error out. I don't know why its not updating the board. It definitely is registering my inputs as well as the computer's since the game does print my coordinates. Can someone please help me figure out what I'm doing wrong?
class Board
attr_reader :grid
def initialize
#grid = Array.new(3) { Array.new(3) }
#human = HumanPlayer.new
#computer = ComputerPlayer.new
#game = Game.new
end
def place_mark(pos)
if (pos[0] < 0 || pos[0] > 2) || (pos[1] < 0 || pos[1] > 2)
puts "Invalid coordinates! Please try again!"
#game.play
elsif empty?(pos)
if #game.current_player == "human"
puts "Player 1 goes: #{pos}"
#grid[pos[0]][pos[1]] = 'X'
elsif #game.current_player == "computer"
puts "Computer goes: #{pos}"
#grid[pos[0]][pos[1]] = "O"
end
if winner
puts "Congratulations! #{#game.current_player} wins!"
else
#game.switch_players!
end
else
puts "Space Taken! Please Try Again!"
#game.play
end
end
def empty?(pos)
#grid[pos[0]][pos[1]].nil?
end
def winner
#Need to set winning combinations
false
end
def over?
false #for now, it will go until all spaces are filled. Still need to set end game condition.
end
end
class HumanPlayer
def display
p Board.new.grid
end
def get_move
puts "Please enter your the quadrant you wish to place your mark."
pos = gets.chomp.scan(/[0-9]/).map!(&:to_i)
Board.new.place_mark(pos)
end
end
class ComputerPlayer
def get_move
x = rand(3).round
y = rand(3).round
Board.new.place_mark([x,y])
end
end
class Game
##turn_tracker = 0
def current_player
##turn_tracker.even? ? "human" : "computer"
end
def switch_players!
##turn_tracker += 1
play
end
def play_turn
if current_player == "human"
HumanPlayer.new.display
Board.new.place_mark(HumanPlayer.new.get_move)
elsif current_player == "computer"
ComputerPlayer.new.get_move
end
end
def play
play_turn until ##turn_tracker == 9 #Still need to set win conditions
end
end
board = Game.new
board.play
1) Game Initiates with Game#new#play
2) #play will run the game until 9 turns have passed (temporary
condition). It is passed to #play_turn
3) #play_turn figures out whose turn it is by using the
current_player.
4) It is then passed to HumanPlayer.get_move or
ComputerPlayer#get_move. These two will determine the moves of each
player and pass it to Board#place_mark.
5) #place_mark will determine if the move is valid using #empty? If
valid, it SHOULD update the grid. Then passes to the
Game#switch_players!
6)#switch_players! will change the player and passes back to #play.
7) It should iterate through this loop.
You always generate a new board, which then of course is again initalized with the starting position:
class HumanPlayer
def display
p Board.new.grid
end
...
end
As you want to learn something, I don't present you a solution.
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.