# frozen_string_literal: true module Codebreaker class Game include Validations attr_reader :difficulty, :errors, :secret_code, :result, :hints, :attempts_total, :hint_keeper, :attempts_used, :attempts_available, :hints_total, :hints_used, :hints_available TIME_FORMAT = '%d %b %Y - %H:%M:%S' CODE_DIGITS = (1..6).freeze CODE_LENGTH = 4 MATCH = '+' PRESENCE = '-' LEVELS = { silly: { attempts: 15, hints: 2, command: 'silly' }, moderate: { attempts: 10, hints: 1, command: 'moderate' }, clever: { attempts: 5, hints: 1, command: 'clever' } }.freeze def initialize @secret_code = Array.new(CODE_LENGTH) { rand(CODE_DIGITS) } @errors = [] end def guess(input) user_number = input.chars.map(&:to_i) validate(user_number) @user_number = user_number end def start @code = @secret_code.dup @attempts -= 1 attempts_calculations hints_calculations matches_result = check_matches presence_result = check_presence @result = (matches_result + presence_result).join end def win? @result == MATCH * CODE_LENGTH end def lost? @attempts.zero? end def hint @hint_keeper << tip = @hints.pop tip end def hints_left? @hints.any? end def set(input) return unless LEVELS.key?(input.to_sym) @difficulty = LEVELS.dig(input.to_sym, :command) @hints = @secret_code.sample(LEVELS.dig(@difficulty.to_sym, :hints)) @attempts = LEVELS.dig(@difficulty.to_sym, :attempts) @hint_keeper = [] end def attempts_calculations @attempts_total = LEVELS.dig(@difficulty.to_sym, :attempts) @attempts_used = LEVELS.dig(@difficulty.to_sym, :attempts) - @attempts @attempts_available = @attempts_total - @attempts_used end def hints_calculations @hints_total = LEVELS.dig(@difficulty.to_sym, :hints) @hints_used = LEVELS.dig(@difficulty.to_sym, :hints) - @hints.size @hints_available = @hints_total - @hints_used end def to_h(name) { name: name, difficulty: @difficulty, attempts_total: LEVELS.dig(@difficulty.to_sym, :attempts), attempts_used: LEVELS.dig(@difficulty.to_sym, :attempts) - @attempts, hints_total: LEVELS.dig(@difficulty.to_sym, :hints), hints_used: LEVELS.dig(@difficulty.to_sym, :hints) - @hints.size, time: Time.now.strftime(TIME_FORMAT) } end private def check_matches output = [] @code.each_with_index do |num, index| next unless num == @user_number[index] output << MATCH @user_number[index] = nil @code[index] = nil end output end def check_presence output = [] @code.compact! @user_number.compact.each do |num| next unless @code.include? num output << PRESENCE @code[@code.index(num)] = nil end output end # rubocop:disable Metrics/LineLength def validate(number) @errors = [] @errors << I18n.t(:GUESS_ERROR, quan: CODE_LENGTH, min: CODE_DIGITS.min, max: CODE_DIGITS.max) unless match_checker(number.size, CODE_LENGTH) number.each { |digit| @errors << I18n.t(:GUESS_ERROR, quan: CODE_LENGTH, min: CODE_DIGITS.min, max: CODE_DIGITS.max) unless range_checker(digit, CODE_DIGITS) } end # rubocop:enable Metrics/LineLength end end