RosettaCodeData/Task/Flipping-bits-game/Ruby/flipping-bits-game.rb

105 lines
2.2 KiB
Ruby

class FlipBoard
def initialize(size)
raise ArgumentError.new("Invalid board size: #{size}") if size < 2
@size = size
@board = Array.new(size**2, 0)
randomize_board
loop do
@target = generate_target
break unless solved?
end
# these are used for validating user input
@columns = [*'a'...('a'.ord+@size).chr]
@rows = (1..@size).map(&:to_s)
end
############################################################
def play
moves = 0
puts "your target:", target
until solved?
puts "", "move #{moves}:", self
print "Row/column to flip: "
ans = $stdin.gets.strip
if @columns.include? ans
flip_column @columns.index(ans)
moves += 1
elsif @rows.include? ans
flip_row @rows.index(ans)
moves += 1
else
puts "invalid input: " + ans
end
end
puts "", "you solved the game in #{moves} moves", self
end
# the target formation as a string
def target
format_array @target
end
# the current formation as a string
def to_s
format_array @board
end
############################################################
private
def solved?
@board == @target
end
# flip a random number of bits on the board
def randomize_board
(@size + rand(@size)).times do
flip_bit rand(@size), rand(@size)
end
end
# generate a random number of flip_row/flip_column calls
def generate_target
orig_board = @board.clone
(@size + rand(@size)).times do
rand(2).zero? ? flip_row( rand(@size) ) : flip_column( rand(@size) )
end
target, @board = @board, orig_board
target
end
def flip_row(row)
@size.times {|col| flip_bit(row, col)}
end
def flip_column(col)
@size.times {|row| flip_bit(row, col)}
end
def flip_bit(row, col)
@board[@size * row + col] ^= 1
end
def format_array(ary)
str = " " + @columns.join(" ") + "\n"
@size.times do |row|
str << "%2s " % @rows[row] + ary[@size*row, @size].join(" ") + "\n"
end
str
end
end
######################################################################
begin
FlipBoard.new(ARGV.shift.to_i).play
rescue => e
puts e.message
end