Merge pull request #430 from acoffman/36-flip-flop-ruby

Initial implementation of FlipFlop in Ruby
This commit is contained in:
Jeff Atwood
2022-01-05 21:36:43 -08:00
committed by GitHub

View File

@@ -0,0 +1,162 @@
#A class representing the internal state of a single game of flip flop
# state represents the list of X's (A in the original code)
# guesses represents the number of guesses the user has made (C in the original code)
# seed represents the random seed for an instance of the game (Q in the original code)
Game = Struct.new(:state, :guesses, :seed) do
#The original BASIC program used 1 indexed arrays while Ruby has 0-indexed arrays.
#We can't use 0 indexed arrays for the flip functions or we'll get divide by zero errors.
#These convenience functions allow us to modify and access internal game state in a 1-indexed fashion
def flip_letter(letter_number)
index = letter_number -1
if self.state[index] == 'X'
self.state[index] = 'O'
else
self.state[index] = 'X'
end
end
def letter_at(letter_number)
self.state[letter_number - 1]
end
end
def print_welcome
puts 'FLIPFLOP'.center(72)
puts 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY'.center(72)
puts <<~EOS
THE OBJECT OF THIS PUZZLE IS TO CHANGE THIS:
X X X X X X X X X X
TO THIS:
O O O O O O O O O O
BY TYPING THE NUMBER CORRESPONDING TO THE POSITION OF THE
LETTER ON SOME NUMBERS, ONE POSITION WILL CHANGE, ON
OTHERS, TWO WILL CHANGE. TO RESET LINE TO ALL X'S, TYPE 0
(ZERO) AND TO START OVER IN THE MIDDLE OF A GAME, TYPE
11 (ELEVEN).
EOS
end
def print_starting_message
puts <<~EOS
HERE IS THE STARTING LINE OF X'S.
1 2 3 4 5 6 7 8 9 10
X X X X X X X X X X
EOS
end
#Create a new game with [X,X,X,X,X,X,X,X,X,X] as the state
#0 as the number of guesses and a random seed between 0 and 1
def generate_new_game
Game.new(Array.new(10, 'X'), 0, rand())
end
#Given a game, an index, and a shuffle function, flip one or more letters
def shuffle_board(game, index, shuffle_function)
n = method(shuffle_function).call(game, index)
if game.letter_at(n) == "O"
game.flip_letter(n)
if index == n
n = shuffle_board(game, index, shuffle_function)
end
else
game.flip_letter(n)
end
return n
end
#Shuffle logic copied from original BASIC code
def shuffle_function1(game, index)
r = Math.tan(game.seed + index / game.seed - index) - Math.sin(game.seed / index) + 336 * Math.sin(8 * index)
n = r - r.floor
(10 * n).floor
end
def shuffle_function2(game, index)
r = 0.592 * (1/ Math.tan(game.seed / index + game.seed)) / Math.sin(index * 2 + game.seed) - Math.cos(index)
n = r - r.floor
(10 * n)
end
def play_game
print_starting_message
game = generate_new_game
working_index = nil
loop do
puts "INPUT THE NUMBER"
input = gets.chomp.downcase
#See if the user input a valid integer, fail otherwise
if numeric_input = Integer(input, exception: false)
#If 11 is entered, we're done with this version of the game
if numeric_input == 11
return :restart
end
if numeric_input > 11
puts 'ILLEGAL ENTRY--TRY AGAIN.'
next #illegal entries don't count towards your guesses
end
if working_index == numeric_input
game.flip_letter(numeric_input)
working_index = shuffle_board(game, numeric_input, :shuffle_function2)
#If 0 is entered, we want to reset the state, but not the random seed or number of guesses and keep playing
elsif numeric_input == 0
game.state = Array.new(10, 'X')
elsif game.letter_at(numeric_input) == "O"
game.flip_letter(numeric_input)
if numeric_input == working_index
working_index = shuffle_board(game, numeric_input, :shuffle_function1)
end
else
game.flip_letter(numeric_input)
working_index = shuffle_board(game, numeric_input, :shuffle_function1)
end
else
puts 'ILLEGAL ENTRY--TRY AGAIN.'
next #illegal entries don't count towards your guesses
end
game.guesses += 1
puts '1 2 3 4 5 6 7 8 9 10'
puts game.state.join(' ')
if game.state.all? { |x| x == 'O' }
if game.guesses > 12
puts "TRY HARDER NEXT TIME. IT TOOK YOU #{game.guesses} GUESSES."
else
puts "VERY GOOD. YOU GUESSED IT IN ONLY #{game.guesses} GUESSES."
end
#game is complete
return
end
end
end
#Execution starts
print_welcome
loop do
result = play_game
if result == :restart
next
end
puts 'DO YOU WANT TO TRY ANOTHER PUZZLE'
if gets.chomp.downcase[0] == 'n'
break
end
end