mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-24 20:10:15 -08:00
163 lines
4.3 KiB
Ruby
163 lines
4.3 KiB
Ruby
#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
|