Last active
May 31, 2023 14:16
-
-
Save marmeladze/cfe009e6c4f287b39e81fc1cbaa86076 to your computer and use it in GitHub Desktop.
Cat Puzzle
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| require 'securerandom' | |
| class Box | |
| attr_reader :order, :filled | |
| def initialize(order) | |
| @order = order | |
| @filled = false | |
| end | |
| def fill! | |
| @filled = true | |
| end | |
| def empty! | |
| @filled = false | |
| end | |
| def filled? | |
| @filled | |
| end | |
| end | |
| class Reality | |
| attr_reader :boxes | |
| def initialize(box_count) | |
| @box_count = box_count | |
| create_boxes! | |
| fill_random_box! | |
| generate_possible_steps! | |
| end | |
| def create_boxes! | |
| @boxes = 1.upto(@box_count).map { |i| Box.new(i) } | |
| end | |
| def fill_random_box! | |
| @boxes.sample.fill! | |
| end | |
| def filled_box | |
| @filled_box = @boxes.find { |box| box.filled? } | |
| end | |
| def filled_box_order | |
| @filled_box_order = filled_box.order | |
| end | |
| def possible_moves | |
| @moves = @possible_moves[filled_box_order-1] | |
| end | |
| def generate_possible_steps! | |
| @possible_moves ||= (1..@box_count).each_with_object([]) do |i, acc| | |
| acc << case i | |
| when 1 then [2] | |
| when @box_count then [i-1] | |
| else | |
| [i-1, i+1] | |
| end | |
| end | |
| end | |
| def move_cat | |
| box_to_fill_order = possible_moves.sample | |
| @boxes.find { |box| box.order == filled_box_order }.empty! | |
| @boxes.find { |box| box.order == box_to_fill_order }.fill! | |
| end | |
| end | |
| class RealityActions | |
| def self.new(box_count) | |
| @reality = Reality.new(box_count) | |
| end | |
| def self.build_reality(box_count) | |
| @reality = new(box_count) | |
| end | |
| def self.move_cat | |
| @reality.move_cat | |
| end | |
| def self.filled_slot | |
| @reality.filled_box_order | |
| end | |
| end | |
| class DummySimulator | |
| def initialize(box_count) | |
| @box_count = box_count || 5 | |
| RealityActions.build_reality(@box_count) | |
| end | |
| def run(iterations) | |
| 1.upto iterations do |i| | |
| run_iteration | |
| end | |
| end | |
| private | |
| def run_iteration | |
| print "\e[2J\e[f" | |
| RealityActions.move_cat | |
| pointer = RealityActions.filled_slot | |
| board = [" "]*@box_count | |
| board[pointer-1] = "\u{1F408}" | |
| print board | |
| puts | |
| sleep(1) | |
| end | |
| end | |
| class GameSimulator | |
| def initialize(game_logs) | |
| @game_logs = game_logs | |
| end | |
| def build | |
| @game_logs.each do |log| | |
| draw_screen(log) | |
| end | |
| end | |
| def draw_screen(log) | |
| cat = "\u{1F408}" | |
| win = "\u{1F92B}" | |
| miss = "\u{1F595}" | |
| cat_position = log[:filled_box] | |
| player_guess = log[:player_guess] | |
| box_count =log[:box_count] || 5 | |
| board = [" "]*box_count | |
| board[cat_position-1] = cat | |
| just = 5*(player_guess-1)+3 | |
| print "\e[2J\e[f" | |
| if cat_position == player_guess | |
| print "#{board}\n#{win.rjust(just)}" | |
| else | |
| print "#{board}\n#{miss.rjust(just)}" | |
| end | |
| puts | |
| sleep(1) | |
| end | |
| end | |
| class GameLogs | |
| def initialize(uuid) | |
| @uuid = uuid | |
| end | |
| def fetch | |
| f = File.open("#{@uuid}.game.log", 'r') | |
| contents = f.read() | |
| f.close | |
| logs = contents.split("\n") | |
| logs.map! {|e| eval(e) } | |
| end | |
| end | |
| class Game | |
| def initialize(box_count) | |
| @box_count = box_count | |
| RealityActions.build_reality(@box_count) | |
| @finished = false | |
| @selection_range = 1..@box_count | |
| @game_log = [] | |
| end | |
| def run | |
| i = 0 | |
| puts "Choose a box from 1 to #{@box_count}" | |
| until @finished | |
| board = ["\u{1F961}"]*@box_count | |
| # board_hint = | |
| pointer = RealityActions.filled_slot | |
| player_input = gets.chomp.to_i | |
| if @selection_range.include? player_input | |
| if player_input == pointer | |
| board[pointer-1] = "\u{1F408} \u{1F92B}" | |
| print board | |
| puts "LUCKY WINNER" | |
| @finished = true | |
| else | |
| board[player_input-1] = "\u{1F595}" | |
| print board | |
| puts "MISS" | |
| sleep(0.5) | |
| end | |
| i+=1 | |
| RealityActions.move_cat | |
| log(i, pointer, player_input) | |
| end | |
| end | |
| print_and_save_game_log | |
| end | |
| private | |
| def log(i, pointer, player_input) | |
| @game_log << {iteration: i, filled_box: pointer, player_guess: player_input, box_count: @box_count} | |
| end | |
| def print_and_save_game_log | |
| file_name = "#{SecureRandom.uuid}.game.log" | |
| File.open(file_name, "w") do |f| | |
| @game_log.each do |iteration| | |
| f.puts iteration | |
| end | |
| end | |
| print "\e[2J\e[f" | |
| puts "Game logs saved to #{file_name}. Would you like to watch? Y/N" | |
| answer = gets.chomp | |
| unless answer == 'Y' | |
| return | |
| end | |
| simulate_last_game | |
| end | |
| end | |
| def play_game | |
| puts "How much boxes" | |
| f = gets.chomp.to_i | |
| print "\e[2J\e[f" | |
| if f > 0 | |
| Game.new(f).run | |
| end | |
| end | |
| def last_game_logs | |
| Dir['*.game.log'].sort_by do |f| | |
| File::Stat.new(f).birthtime | |
| end.last | |
| end | |
| def simulate_last_game | |
| id = File.basename last_game_logs, '.game.log' | |
| simulate_game(id) | |
| end | |
| def simulate_game(id) | |
| logs = GameLogs.new(id).fetch | |
| GameSimulator.new(logs).build | |
| end | |
| def run_dummy_simulator(boxes, iterations) | |
| DummySimulator.new(boxes).run(iterations) | |
| end | |
| play_game | |
| # simulate_game('1eb31c9c-3ce7-4671-91eb-318f0599d3ed') | |
| # run_dummy_simulator(7, 20) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment