module BoggleGame
Constants
- PASSIVE_LINE_PATTERN
- PASSIVE_STOPWORDS
- PLUGIN
- STYLE_TITLE
Public Instance Methods
Source
# File lib/boggle_game.rb, line 172 def current(data:) return { error: BoggleReport.reject("No active round — !boggle new") } unless data && data["stage"] == "active" && data["grid"] { message: BoggleReport.current(data) } end
Source
# File lib/boggle_game.rb, line 153 def end_round(data:) return { error: BoggleReport.reject("No round to end") } unless data found_count = Array(data["found"]).length scores = data["scores"] || {} data = data.dup data["stage"] = "idle" { data: data, message: BoggleReport.end_round(scores, found_count: found_count) } end
Source
# File lib/boggle_game.rb, line 166 def format_status(data) return BoggleReport.reject("No round — !boggle new") unless data && data["grid"] BoggleReport.status(data) end
Source
# File lib/boggle_game.rb, line 202 def hint(data:, words: nil, rng: Random.new, rand: nil, exceptions: nil) return { error: BoggleReport.reject("No active round — !boggle new") } unless data && data["stage"] == "active" pick = rand || ->(max) { rng.rand(max) } grid = data["grid"] found = data["found"] || {} hidden = BoggleLexicon.findable_words(grid, words: words, exceptions: exceptions).reject { |w| found.key?(w) } return { error: BoggleReport.reject("All words found") } if hidden.empty? word = hidden[pick.call(hidden.length)] mask = mask_word(word, pick.call(word.length)) { message: BoggleReport.hint(mask: mask) } end
Source
# File lib/boggle_game.rb, line 178 def info(data: nil, words: nil, exceptions: nil) unless data && data["stage"] == "active" && data["grid"] return { message: BoggleReport.info_rules } end grid = data["grid"] findable = BoggleLexicon.findable_words(grid, words: words, exceptions: exceptions) breakdown = findable.group_by(&:length).transform_values(&:count) found_count = Array(data["found"]).length remaining = findable.length - found_count { message: BoggleReport.info_active( total: findable.length, breakdown: breakdown, found_count: found_count, remaining: remaining ) } end
Source
# File lib/boggle_game.rb, line 198 def mask_word(word, reveal_index) word.to_s.chars.map.with_index { |ch, i| i == reveal_index ? ch : "_" }.join end
Source
# File lib/boggle_game.rb, line 25 def parse_command(text) parts = text.to_s.strip.split(/\s+/) return { action: :status } if parts.empty? case parts[0].downcase when "new" { action: :new } when "find" return { error: usage_message } if parts.length < 2 { action: :find, word: parts[1..].join(" ") } when "end" { action: :end } when "hint" { action: :hint } when "info" { action: :info } when "current" { action: :current } when "status", "help" { action: :status } else { error: usage_message } end end
Source
# File lib/boggle_game.rb, line 120 def parse_passive_words(text) return nil unless passive_line_submittable?(text) text.to_s.strip.downcase.split(/\s+/) end
Source
# File lib/boggle_game.rb, line 107 def passive_line_submittable?(text) normalized = text.to_s.strip return false if normalized.empty? return false if normalized.start_with?("!") return false unless normalized.match?(PASSIVE_LINE_PATTERN) tokens = normalized.downcase.split(/\s+/) return false unless tokens.all? { |token| token.length >= BoggleLexicon::MIN_LENGTH } return false if tokens.any? { |token| PASSIVE_STOPWORDS.include?(token) } true end
Source
# File lib/boggle_game.rb, line 216 def remaining_words(data) total = data["total_words"] return nil if total.nil? total - Array(data["found"]).length end
Source
# File lib/boggle_game.rb, line 51 def score_for(word) len = word.to_s.length case len when 3, 4 then 1 when 5 then 2 when 6 then 3 when 7 then 5 else 11 end end
Source
# File lib/boggle_game.rb, line 62 def start_round(rng: Random.new, rand: nil, grid_roll: nil, words: nil, exceptions: nil) grid_roll ||= ->(*) { BoggleGrid.roll_grid(rng: rng, rand: rand) } grid = grid_roll.call findable = BoggleLexicon.findable_words(grid, words: words, exceptions: exceptions) data = { "stage" => "active", "grid" => grid, "found" => {}, "scores" => {}, "total_words" => findable.length } { data: data, message: BoggleReport.new_round(grid, total_words: findable.length) } end
Source
# File lib/boggle_game.rb, line 126 def submit_line(data:, user:, line:, words: nil, gloss_lookup: Dictionary.default_run, exceptions: nil) tokens = parse_passive_words(line) return { skip: true } unless tokens return { skip: true } unless data && data["stage"] == "active" accepts = [] current = data tokens.each do |token| result = submit_word(data: current, user: user, word: token, words: words, gloss_lookup: gloss_lookup, exceptions: exceptions) next if result[:error] current = result[:data] accepts << { word: token, points: score_for(token), user: GameStore.norm_nick(user), gloss: result[:gloss], remaining: remaining_words(current) } end return { skip: true, data: current } if accepts.empty? { data: current, message: BoggleReport.accept_many(accepts), skip: false } end
Source
# File lib/boggle_game.rb, line 76 def submit_word(data:, user:, word:, words: nil, gloss_lookup: Dictionary.default_run, exceptions: nil) return { error: BoggleReport.reject("No active round — !boggle new") } unless data && data["stage"] == "active" token = word.to_s.strip.downcase return { error: BoggleReport.reject("Too short — need 3+ letters") } if token.length < BoggleLexicon::MIN_LENGTH return { error: BoggleReport.reject("Invalid word — letters only") } unless token.match?(/\A[a-z]+\z/) found = data["found"] || {} return { error: BoggleReport.reject("Already found") } if found.key?(token) grid = data["grid"] return { error: BoggleReport.reject("Not on grid") } unless BoggleGrid.word_on_grid?(grid, token) return { error: BoggleReport.reject("Not in WordNet") } unless BoggleLexicon.valid_word?(token, words: words, exceptions: exceptions) nick = GameStore.norm_nick(user) points = score_for(token) data = data.dup data["found"] = found.merge(token => nick) scores = (data["scores"] || {}).dup scores[nick] = scores.fetch(nick, 0) + points data["scores"] = scores gloss = BoggleLexicon.gloss_for(token, lookup: gloss_lookup) remaining = remaining_words(data) { data: data, message: BoggleReport.accept(word: token, points: points, user: nick, gloss: gloss, remaining: remaining), gloss: gloss } end
Source
# File lib/boggle_game.rb, line 21 def usage_message "Usage: !boggle new | find <word> | end | current | status | hint | info — or type words while a round is active" end