mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-21 23:00:43 -08:00
Merge pull request #430 from acoffman/36-flip-flop-ruby
Initial implementation of FlipFlop in Ruby
This commit is contained in:
162
36_Flip_Flop/ruby/flipflop.rb
Normal file
162
36_Flip_Flop/ruby/flipflop.rb
Normal 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
|
||||
Reference in New Issue
Block a user