# frozen_string_literal: false module Codebreaker class Game include Validation include Storage attr_reader :secret_code, :attempts, :hints, :errors, :difficulty, :play, :result, :player, :hint_keeper, :name, :storage DIFFICULTY = { hell: { attempts: 5, hint: 1, mode: 'hell' }, medium: { attempts: 10, hint: 1, mode: 'medium' }, easy: { attempts: 15, hint: 2, mode: 'easy' } }.freeze def initialize @secret_code = 4.times.map { Random.rand(1..6) } @errors = [] end def set(user_input) return unless DIFFICULTY.key?(user_input.to_sym) @difficulty = DIFFICULTY.dig(user_input.to_sym) @hints = @difficulty.dig(:hint) @attempts = @difficulty.dig(:attempts) @hint_keeper = @secret_code.sample(hints) end def take_hint! hint_keeper.pop end def won?(guess) secret_code.join == guess end def lost? attempts.zero? end def to_yaml(name) data_to_save(name) end def data_to_save(name) { name: name, difficulty: difficulty[:mode], attempts_total: difficulty[:attempts], attempts_used: difficulty[:attempts] - attempts, hints_total: difficulty[:hint], hints_used: difficulty[:hint] - hint_keeper.size } end def use_attempt! @attempts -= 1 end def guess(guess) '+' * exact_match_count(guess) + '-' * number_match_count(guess) end private def exact_match_count(guess) (0..3).inject(0) do |count, index| count + (exact_match?(guess, index) ? 1 : 0) end end def number_match_count(guess) total_match_count(guess) - exact_match_count(guess) end def total_match_count(guess) secret = @secret_code.dup guess.inject(0) do |count, num| count + (delete_first(secret, num) ? 1 : 0) end end def delete_first(code, num) code.delete_at(code.index(num)) if code.index(num) end def exact_match?(guess, index) guess[index] == @secret_code[index] end end end