Ruby : stack level too deep (SystemStackError) - ruby

I try to solve this problem http://www.nattee.net/~dae/algo/prob/hw03b_tiling/problem.pdf
So I using divide and conquer method to solve it but when I execute my program I get
tile.rb:7: stack level too deep (SystemStackError)
And this is my code
def tile (x, y, bx, by, ex, ey)
mx = (bx+ex)/2
my = (by+ey)/2
if (by<=y && y<=my)
if (bx<=x && x<=mx) # top-left
puts "0 #{mx} #{my}"
elsif (mx+1<=x && x<=ex) # top-right
puts "1 #{mx} #{my}"
end
elsif (my+1<=y && y<=ey)
if (bx<=x && x<=mx) # bottom-left
puts "2 #{mx} #{my}"
elsif (mx+1<=x && x<=ex) # bottom-right
puts "3 #{mx} #{my}"
end
end
tile(x,y,bx,by,mx,my) #top-left
tile(x,y,mx+1,by,ey,my) #top-right
tile(x,y,bx,my+1,mx+1,ey) #bottom-left
tile(x,y,mx+1,my+1,ex,ey) #bottom-right
if ex-bx == 2 && ey-by == 2 then return end
end
temp = []
gets.chomp.strip.split(" ").each do |item|
temp << item.to_i
end
L = temp[0]
x = temp[1]
y = temp[2]
tile(x,y,0,0,L-1,L-1)
I can't find the cause.

There is no way out of your recursion - for every call to tile, it will make 4 more calls to tile. Recursion always needs a "relief valve" way out before the recursion call. Try moving your return up before the tile calls.
The return statement could stand to be written in more idiomatic ruby.
Try:
return if (ex-bx == 2 && ey-by == 2)

Related

How to create a Minimax algorithm comparing arrays

I'm trying to code a "minimax" algorithm for Tic Tac Toe.
Each node of the tree is of the form [nil/Int, String] where the last element is a nine character string describing the board, and the first is an Integer ranking the node, or nil by default.
If the value is nil, it tries to inherit the appropriate value from child nodes.
This is where I get an error, when comparing an array with an array failed.
class Scene_TicTacToe #Script 2/2
def initialize
#Boardstate as a str from top left corner to bottom right corner.
#boardstate = "---------"
#1 = player, -1 = comp
#active_player = 1
end
def wincheck(boardstate=#boardstate)
#should return -1 for loss, 0 for draw, 1 for win
["OOO","XXX"].each do |f|
for i in 0..2
if (boardstate[i]+boardstate[i+3]+boardstate[i+6]).chr == f || boardstate[(3*i)..(3*i)+2] == f
return f == "OOO" ? 1 : -1
end
end
if (boardstate[0]+boardstate[4]+boardstate[8]).chr == f || (boardstate[2]+boardstate[4]+boardstate[6]).chr == f
return f == "OOO" ? 1 : -1
end
end
return 0
end
def computer_play
#Sets depth,and alpha/beta for pruning, so far so good
depth = 3
alpha = -100
beta = 100
##boardstate starts as "---------"
##active_player: 1=player, -1=computer
play(minimax(#boardstate, depth, alpha, beta, #active_player))
end
def play(array)
#Check actual boardside with parameter boardside to see what move has been
#selected and plays that move
for i in 0...array[1].length
if #boardstate[i] != array[1][i]
#color = array[1][i].chr == "X" ? #ai : #player
##cursor.y = (i / 3) * #side
##cursor.x = (i % 3) * #side
##board.bitmap.fill_rect(#cursor.x,#cursor.y,#side,#side,color)
#boardstate = array[1].dup
end
end
end
def minimax(boardstate, depth, alpha, beta, active_player)
#If bottom node reached, returns [boardstate_score, boardstate]
#wincheck returns 1 if player wins, -1 if computer wins, and 0 otherwise
if depth == 0 or wincheck(boardstate) != 0 or (/-/ =~ boardstate) == nil
return [wincheck(boardstate),boardstate]
end
if active_player == 1 #if player's turn
#Gets an array of all the next possible boardstates and return the one with
#the best eval.
child = generate_child(boardstate, active_player)
child.each do |f| #f = [Int/nil, String]
if f[0] == nil
#This should turn all the nil wincheck values to the best value of children nodes
f[0] = minimax(f[1], depth-1, alpha, beta, -active_player).last[0]
end
alpha = [f[0], alpha].max
if beta <= alpha
break
end
end
return child.sort_by{|c| c[0]}
end
if active_player == -1 #if computer's turn
#Same as above but with worst eval.
child = generate_child(boardstate, active_player)
child.each do |f|
if f[0] == nil
f[0] = minimax(f[1], depth-1, alpha, beta, -active_player).first[0]
end
beta = [f[0], beta].min
if beta <= alpha
break
end
end
#Following line raises "comparison of array with array failed" error :
return child.sort_by{|c| c[0]}
end
end
def generate_child(boardstate, active_player)
#returns boardstate string with one X or O more than current boardstate
#and sets nil as a default wincheck value
c = active_player == 1 ? "O" : "X"
a = []
for i in 0...boardstate.length
if boardstate[i].chr == "-"
s = boardstate.dup
s[i]= c
a << [nil, s]
end
end
return a
end
end
Error: comparison of array with array failed

Getting a stack overflow when trying to impliment Negamax in Tic Tac Toe

I apologize in advance if this is a re-post, but I have read other posts on the topic and I am still can't figure out what to do. I am trying to implement Negamax in a ruby tic tac toe game and I am getting a stack overflow error on line 55. I have read so many posts and articles on Negamax and I still can't get it to work.
This file isn't part of a larger program yet, I just wanted to pass in a board and see if it would make a move.
class Negamax
attr_accessor :board, :mark, :depth, :winning_routes
def initialize
#board = ["X","2","3","4","5","6","7","8","9"]
#mark = "O"
#depth = 1
#winning_routes = [[0,1,2],[3,4,5],[6,7,8],[0,3,6],[1,4,7],[2,5,8],[0,4,8],[2,4,6]]
end
def negamax(board, mark, depth)
if winner?(mark) || isboardfull?(board)
return game_result(mark)
else
max = -1.0/0
mark == "O" ? opponent = "X" : "O"
available_moves.each do |space|
board[space] = mark
score = -negamax(board, opponent, depth + 1)
board[space] = "#{space + 1}"
if score > max
max = score
best_move = space if depth == 1
board[best_move] = mark
end
end
return max
end
end
def available_moves()
board.each_index.select { |s| board[s] != "X" && board[s] != "O"}
end
def isboardfull?(board)
boardtos = board.join(",")
boardtos =~ /\d/ ? false : true
end
def game_result(mark)
if winner?(mark)
return 1
elsif winner?(mark == "O" ? "X" : "O")
return -1
else
return 0
end
end
def winner?(mark)
result = false
marker = mark
winning_routes.each do |group|
if board[group[0]] == marker && board[group[1]] == marker && board[group[2]] == marker
result = true
end
end
result
end
end
game = Negamax.new()
game.negamax(game.board, game.mark, game.depth)
print game.board

ruby code stopping at run time, seemingly an infinite loop

if i run the code, it will stop and not do anything and i am unable to type. seems to be an infinite loop.
the problem seems to be the end until loop, however if i take that out, my condition will not be met.
can anyone find a solution? i have tried all the loops that i can think of.
/. 2d array board ./
board = Array.new(10) { Array.new(10, 0) }
/. printing board ./
if board.count(5) != 5 && board.count(4) != 4 && board.count(3) != 3
for i in 0..9
for j in 0..9
board[i][j] = 0
end
end
aircraftcoord1 = (rand*10).floor
aircraftcoord2 = (rand 6).floor
aircraftalign = rand
if aircraftalign < 0.5
for i in 0..4
board[aircraftcoord2+i][aircraftcoord1] = 5
end
else
for i in 0..4
board[aircraftcoord1][aircraftcoord2+i] = 5
end
end
cruisercoord1 = (rand*10).floor
cruisercoord2 = (rand 7).floor
cruiseralign = rand
if cruiseralign < 0.5
for i in 0..3
board[cruisercoord2+i][cruisercoord1] = 4
end
else
for i in 0..3
board[cruisercoord1][cruisercoord2+i] = 4
end
end
destroyercoord1 = (rand*10).floor
destroyercoord2 = (rand 8).floor
destroyeralign = rand
if destroyeralign < 0.5
for i in 0..2
board[destroyercoord2+i][destroyercoord1] = 3
end
else
for i in 0..2
board[destroyercoord1][destroyercoord2+i] = 3
end
end
end until board.count(5) == 5 && board.count(4) == 4 && board.count(3) == 3
print " "
for i in 0..9
print i
end
puts
for i in 0..9
print i
for j in 0..9
print board[i][j]
end
puts
end
The line board.count(5) == 5 ... will never be true because board is a two-dimensional array. I can't tell what the condition should be, but it could look something like:
board[5].count(5) == 5

How to Find a Sequence of 4 in an Array with 5 Elements & Fixing Input Error

I've built a Yahtzee game for Ruby Quiz #19. I have the game up and running, however, there are 2 bugs.
When a player chooses to use a roll of >=3 of a kind as a "small straight" (sequence of 4 dice) to "scratch" (score it as zero) that section an error occurs. Here is the code for the small straight:
def sm_straight
#roll = #roll.sort.uniq
if (0..1).any? {|x| (#roll[x+3] - #roll[x+2] == 1) && (#roll[x+2] - #roll[x+1] == 1) && (#roll[x+1] - #roll[x] == 1)}
#scorecard["sm. straight"] = 30
else
puts "Your roll is not a sm. straight! Please select another section or type scratch to score 0 for this section."
scratch = gets.chomp
if scratch == "scratch"
#scorecard["sm. straight"] = "scratch"
elsif #scorecard.has_key?(scratch)
#turn -= 1
section_to_score(scratch)
else
sm_straight
end
end
end
This is the error:
NoMethodError: undefined method -' for nil:NilClass
from Yahtzee_test.rb:209:inblock in sm_straight'
Line 209 is the "if statement" line
When a player incorrectly enters which dice to keep. I am trying to figure out a better way to ask the player how to enter the dice to keep or catch the error and have them re-enter the numbers with the current system. Here is the code"
def roll_again
puts "Which dice would you like to keep from this roll? (1, 2, 3, 4, 5)"
dice_to_keep = gets.chomp.split(',').map {|x| (x.to_i) - 1}.map {|x| #roll[x]}
new_roll = Array.new(5 - dice_to_keep.size) {rand(6) + 1}
#roll = new_roll + dice_to_keep
p #roll
#roll_count += 1
puts "That was roll number #{#roll_count}, you have #{3-#roll_count} remaining."
if #roll_count < 3
more_rolls?
else
section(#roll)
end
end
Any advice on how to write this code better and make it bug free would be greatly appreciated!
To check for a straight of at least 4 out of 5 dice, you could replace:
#roll = #roll.sort.uniq
if (0..1).any? {|x| (#roll[x+3] - #roll[x+2] == 1) && (#roll[x+2] - #roll[x+1] == 1) && (#roll[x+1] - #roll[x] == 1)}
with this:
if has_straight(roll, 4)
And define has_straight:
def has_straight( roll, need )
num = 1
roll = roll.sort.uniq
roll.each_with_index do |e, i|
if i < roll.length-1 then
if (roll[i+1] - roll[i]) > 1 then
break if num >= need
num = 1
end
num += 1
end
end
num >= need
end
There may be a slightly more clever Ruby-ism that will do this, but it fixes your array out-of-bounds issue.

Evaluating exit condition at bottom of Ruby while loop

I have a very basic question about Ruby loops.
This program as written returns the ith prime number +1 (ie the example should return 17). I know I could simply return cand-1, but I was wondering what the "Ruby way" of checking if the answer has been found at the bottom of the while loop and only incrementing if it hasn't.
def ith_prime(i)
pI = 0 # primes index
divs = []
cand = 2
until pI == i do
if divs.find { |div| cand%div == 0 } == nil
divs << cand
pI += 1
end
cand += 1
end
cand
end
puts ith_prime(7)
> 18
I use loop instead of while or until most of the time. This way I can put the exit condition anywhere in the loop.
I would write it like that (if I understood the problem correctly):
def ith_prime(i)
pI = 0 # primes index
divs = []
cand = 2
loop do
unless divs.find { |div| cand%div == 0 }
divs << cand
pI += 1
end
break if pI == i
cand += 1
end
cand
end

Resources