class Blackjack
Constants
- DEFAULT_BET
- INVENTORY_TITLE
- LEADERBOARD_TITLE
- SHOP_ITEMS
- SHOP_TITLE
- STATS_TEMPLATE
- TIPS
- TITLE
Public Class Methods
Source
# File plugins/blackjack.rb, line 225 def self.add_inventory_item(user, item_key, inventory: default_inventory) key = normalize_user(user) owned = store_entry(inventory, key, {}) owned[item_key] = owned.fetch(item_key, 0) + 1 persist_store_entry!(inventory, key, owned) end
Source
# File plugins/blackjack.rb, line 532 def self.advance_to_next_hand(game) next_index = game[:current_hand] + 1 while next_index < game[:hands].length && (game[:hands][next_index][:busted] || game[:hands][next_index][:stood]) next_index += 1 end game[:current_hand] = next_index if next_index < game[:hands].length end
Source
# File plugins/blackjack.rb, line 79 def self.append_game_tip(line, tip: nil, rand: ->(max) { Random.rand(0...max) }) "#{line}#{format_game_tip(tip, rand: rand)}" end
Source
# File plugins/blackjack.rb, line 781 def self.blackjack(args_text, user:, store: default_store, games: default_games, stats: default_stats, inventory: default_inventory, deck: nil, rand: ->(max) { Random.rand(0...max) }) result = dispatch_command( args_text, user: user, store: store, games: games, stats: stats, inventory: inventory, deck: deck, rand: rand ) return result[:error] if result[:error] result[:message] end
Source
# File plugins/blackjack.rb, line 132 def self.blackjack?(cards) cards.length == 2 && hand_value(cards) == 21 end
Source
# File plugins/blackjack.rb, line 136 def self.bust?(cards) hand_value(cards) > 21 end
Source
# File plugins/blackjack.rb, line 302 def self.can_double?(game) game[:can_double] && game[:hands].length == 1 && game[:hands][0][:player].length == 2 end
Source
# File plugins/blackjack.rb, line 306 def self.can_split?(game) game[:can_split] && game[:hands].length == 1 && splittable?(game[:hands][0][:player]) end
Source
# File plugins/blackjack.rb, line 17 def self.command_pattern /blackjack(?: (.+))?$/i end
Source
# File plugins/blackjack.rb, line 148 def self.compare_hands(player, dealer) return :blackjack_win if blackjack?(player) && !blackjack?(dealer) return :lose if blackjack?(dealer) && !blackjack?(player) return :push if blackjack?(player) && blackjack?(dealer) player_value = hand_value(player) dealer_value = hand_value(dealer) return :lose if player_value > 21 return :win if dealer_value > 21 return :win if player_value > dealer_value return :lose if player_value < dealer_value :push end
Source
# File plugins/blackjack.rb, line 310 def self.current_hand(game) game[:hands][game[:current_hand]] end
Source
# File plugins/blackjack.rb, line 144 def self.dealer_should_hit?(dealer_hand) hand_value(dealer_hand) < 17 end
Source
# File plugins/blackjack.rb, line 95 def self.default_inventory Game.default_inventory end
Source
# File plugins/blackjack.rb, line 91 def self.default_stats Game.default_stats end
Source
# File plugins/blackjack.rb, line 87 def self.default_store Game.default_store end
Source
# File plugins/blackjack.rb, line 730 def self.dispatch_command(args_text, user:, store: default_store, games: default_games, stats: default_stats, inventory: default_inventory, deck: nil, rand: ->(max) { Random.rand(0...max) }) parsed = parse_command(args_text.to_s) return { error: parsed[:error] } if parsed[:error] return { message: usage_message } if parsed[:action] == :help case parsed[:action] when :balance { message: format_balance(user, balance_for(user, store: store)) } when :leaderboard { message: format_leaderboard(store) } when :new result = start_game(user, parsed[:amount], store: store, games: games, inventory: inventory, deck: deck, rand: rand) return { error: result[:error] } if result[:error] { message: result[:message] } when :hit result = hit(user, store: store, games: games, stats: stats, inventory: inventory, rand: rand) return { error: result[:error] } if result[:error] { message: result[:message] } when :hold result = hold(user, store: store, games: games, stats: stats, inventory: inventory, rand: rand) return { error: result[:error] } if result[:error] { message: result[:message] } when :double result = double(user, store: store, games: games, stats: stats, inventory: inventory, rand: rand) return { error: result[:error] } if result[:error] { message: result[:message] } when :split result = split(user, store: store, games: games, inventory: inventory) return { error: result[:error] } if result[:error] { message: result[:message] } when :shop { message: format_shop } when :shop_buy result = shop_buy(user, parsed[:item], store: store, inventory: inventory) return { error: result[:error] } if result[:error] { message: result[:message] } when :shop_inventory { message: format_shop_inventory(user, inventory: inventory) } else { message: usage_message } end end
Source
# File plugins/blackjack.rb, line 667 def self.double(user, store: default_store, games: default_games, stats: default_stats, inventory: default_inventory, rand: ->(max) { Random.rand(0...max) }) game = active_game(user, games: games) return { error: "#{user}, no active hand — use !blackjack new <amount> to deal." } unless game return { error: "#{user}, you can only double on your first two cards." } unless can_double?(game) balance = balance_for(user, store: store) return { error: "#{user}, not enough credits to double (balance: #{balance})." } if balance < game[:bet] deduct_bet(user, game[:bet], store: store) game[:bet] *= 2 game[:can_double] = false game[:can_split] = false hand_entry = current_hand(game) card, deck = deal_card(game[:deck]) hand_entry[:player] = hand_entry[:player] + [card] game[:deck] = deck if bust?(hand_entry[:player]) hand_entry[:busted] = true return resolve_game(user, game, store: store, games: games, stats: stats, inventory: inventory, rand: rand) end hand_entry[:stood] = true resolve_game(user, game, store: store, games: games, stats: stats, inventory: inventory, rand: rand) end
Source
# File plugins/blackjack.rb, line 314 def self.format_action_hint(can_double: true, can_split: false) parts = [ "#{STYLE_HINT}Hit:#{IRC_RESET} !blackjack hit", "#{STYLE_HINT}Stand:#{IRC_RESET} !blackjack hold" ] parts << "#{STYLE_HINT}Double:#{IRC_RESET} !blackjack double" if can_double parts << "#{STYLE_HINT}Split:#{IRC_RESET} !blackjack split" if can_split parts.join(" #{STYLE_HINT}|#{IRC_RESET} ") end
Source
# File plugins/blackjack.rb, line 352 def self.format_aligned_hand_lines(entries, label_width: nil) labels = entries.map { |entry| entry.fetch(:label).to_s } width = label_width || hand_line_label_width(labels) entries.map do |entry| format_hand_line( label: entry.fetch(:label), total: entry.fetch(:total), cards: entry.fetch(:cards), label_width: width, suffix: entry.fetch(:suffix, "") ) end end
Source
# File plugins/blackjack.rb, line 324 def self.format_balance(user, balance) super(user, balance, title: TITLE) end
Calls superclass method
Game::format_balance
Source
# File plugins/blackjack.rb, line 393 def self.format_dealer_line(dealer:, hide_dealer:, label_width:) dealer_visible = hide_dealer ? format_hand(dealer, hide_second: true) : format_hand(dealer) dealer_total = format_hand_total(hand_value(dealer), hidden: hide_dealer) format_hand_line(label: "Dealer", total: dealer_total, cards: dealer_visible, label_width: label_width) end
Source
# File plugins/blackjack.rb, line 74 def self.format_game_tip(tip = nil, rand: ->(max) { Random.rand(0...max) }) text = tip || random_tip(rand: rand) " #{STYLE_LABEL}│#{IRC_RESET} #{STYLE_HINT}Tip:#{IRC_RESET} #{text}" end
Source
# File plugins/blackjack.rb, line 339 def self.format_hand_line(label:, total:, cards:, label_width:, suffix: "") padded_label = TextAlign.pad(label.to_s, label_width, align: :right) "#{STYLE_LABEL}#{padded_label}:#{IRC_RESET} #{STYLE_VALUE}#{total}#{IRC_RESET} #{STYLE_LABEL}=#{IRC_RESET} #{cards}#{suffix}" end
Source
# File plugins/blackjack.rb, line 348 def self.format_hand_total(value, hidden: false) hidden ? "??" : format("%02d", value) end
Source
# File plugins/blackjack.rb, line 328 def self.format_leaderboard(store) super(store, title: LEADERBOARD_TITLE, empty_message: "No hands played yet — deal yourself in!") end
Calls superclass method
Game::format_leaderboard
Source
# File plugins/blackjack.rb, line 449 def self.format_multi_outcome(outcomes, balance, stats: nil, tip: nil, rand: ->(max) { Random.rand(0...max) }) labels = outcomes.map { |outcome| outcome_label(outcome) }.join(" / ") record = format_record(stats) record_part = record.empty? ? "" : " #{STYLE_LABEL}│#{IRC_RESET} #{STYLE_LABEL}Record:#{IRC_RESET} #{STYLE_VALUE}#{record}#{IRC_RESET}" line = "#{STYLE_TITLE}═══#{IRC_RESET} " \ "#{STYLE_WIN}>>> #{labels} <<<#{IRC_RESET} " \ "#{STYLE_LABEL}│#{IRC_RESET} " \ "#{STYLE_LABEL}Balance:#{IRC_RESET} #{STYLE_VALUE}#{balance}#{IRC_RESET} " \ "#{STYLE_LABEL}credits#{IRC_RESET}#{record_part} " \ "#{STYLE_TITLE}═══#{IRC_RESET}" append_game_tip(line, tip: tip, rand: rand) end
Source
# File plugins/blackjack.rb, line 435 def self.format_outcome(outcome, balance, stats: nil, tip: nil, rand: ->(max) { Random.rand(0...max) }) style = outcome_style(outcome) label = outcome_label(outcome) record = format_record(stats) record_part = record.empty? ? "" : " #{STYLE_LABEL}│#{IRC_RESET} #{STYLE_LABEL}Record:#{IRC_RESET} #{STYLE_VALUE}#{record}#{IRC_RESET}" line = "#{STYLE_TITLE}═══#{IRC_RESET} " \ "#{style}>>> #{label} <<<#{IRC_RESET} " \ "#{STYLE_LABEL}│#{IRC_RESET} " \ "#{STYLE_LABEL}Balance:#{IRC_RESET} #{STYLE_VALUE}#{balance}#{IRC_RESET} " \ "#{STYLE_LABEL}credits#{IRC_RESET}#{record_part} " \ "#{STYLE_TITLE}═══#{IRC_RESET}" append_game_tip(line, tip: tip, rand: rand) end
Source
# File plugins/blackjack.rb, line 366 def self.format_player_hands(game, current_hand: nil, label_width: nil) current_hand ||= game[:current_hand] if game[:hands].length == 1 hand = game[:hands][0][:player] total = format_hand_total(hand_value(hand)) return format_aligned_hand_lines([ { label: "Your hand", total: total, cards: format_hand(hand) } ], label_width: label_width) end entries = game[:hands].each_with_index.map do |hand_entry, index| hand = hand_entry[:player] total = format_hand_total(hand_value(hand)) marker = index == current_hand ? " #{STYLE_HINT}◀#{IRC_RESET}" : "" status = "" status = " #{STYLE_LOSE}(bust)#{IRC_RESET}" if hand_entry[:busted] status = " #{STYLE_LABEL}(stood)#{IRC_RESET}" if hand_entry[:stood] { label: "Hand #{index + 1}", total: total, cards: format_hand(hand), suffix: "#{status}#{marker}" } end format_aligned_hand_lines(entries, label_width: label_width) end
Source
# File plugins/blackjack.rb, line 239 def self.format_player_inventory_icons(user, inventory: default_inventory) owned = inventory_for(user, inventory: inventory) return "" if owned.empty? icons = SHOP_ITEMS.filter_map do |key, item| count = owned[key] next unless count&.positive? item[:icon] * count end " #{icons.join}" end
Source
# File plugins/blackjack.rb, line 213 def self.format_record(stats) return "" unless stats "#{stats[:wins]}W #{stats[:losses]}L #{stats[:pushes]}P #{stats[:blackjack_wins]}BJ" end
Source
# File plugins/blackjack.rb, line 252 def self.format_shop lines = ["#{STYLE_TITLE}#{SHOP_TITLE}#{IRC_RESET}"] SHOP_ITEMS.each do |key, item| lines << "#{STYLE_LABEL}•#{IRC_RESET} #{item[:icon]} #{STYLE_VALUE}#{item[:name]}#{IRC_RESET} " \ "#{STYLE_LABEL}—#{IRC_RESET} #{STYLE_VALUE}#{item[:price]}#{IRC_RESET} " \ "#{STYLE_LABEL}credits#{IRC_RESET} " \ "#{STYLE_HINT}(#{IRC_RESET}#{STYLE_HINT}!blackjack shop buy #{key}#{IRC_RESET}#{STYLE_HINT})#{IRC_RESET}" end lines.join("\n") end
Source
# File plugins/blackjack.rb, line 263 def self.format_shop_inventory(user, inventory: default_inventory) owned = inventory_for(user, inventory: inventory) lines = ["#{STYLE_TITLE}#{INVENTORY_TITLE}#{IRC_RESET} #{STYLE_LABEL}for#{IRC_RESET} " \ "#{STYLE_NICK}#{user}#{IRC_RESET}"] if owned.empty? lines << "#{STYLE_LABEL}Nothing yet — hit the shop when you're rich!#{IRC_RESET}" return lines.join("\n") end owned.each do |key, count| item = SHOP_ITEMS[key] name = item ? item[:name] : key icon = item ? "#{item[:icon]} " : "" lines << "#{STYLE_LABEL}•#{IRC_RESET} #{icon}#{STYLE_VALUE}#{name}#{IRC_RESET} " \ "#{STYLE_LABEL}x#{IRC_RESET}#{STYLE_VALUE}#{count}#{IRC_RESET}" end lines.join("\n") end
Source
# File plugins/blackjack.rb, line 399 def self.format_table(user:, bet:, dealer:, hide_dealer: true, footer: nil, game: nil, player: nil, inventory: default_inventory) game ||= { hands: [{ player: player }], current_hand: 0 } hand_labels = if game[:hands].length == 1 ["Your hand", "Dealer"] else game[:hands].each_with_index.map { |_, index| "Hand #{index + 1}" } + ["Dealer"] end label_width = hand_line_label_width(hand_labels) lines = [ format_table_heading(user: user, bet: bet, inventory: inventory), *format_player_hands(game, label_width: label_width), format_dealer_line(dealer: dealer, hide_dealer: hide_dealer, label_width: label_width) ] lines << footer if footer lines.join("\n") end
Source
# File plugins/blackjack.rb, line 332 def self.format_table_heading(user:, bet:, inventory: default_inventory) items = format_player_inventory_icons(user, inventory: inventory) "#{STYLE_TITLE}══════ ♠ BLACKJACK ♠ ══════#{IRC_RESET} " \ "#{STYLE_LABEL}│#{IRC_RESET} #{STYLE_LABEL}Player:#{IRC_RESET} #{STYLE_NICK}#{user}#{IRC_RESET}#{items} " \ "#{STYLE_LABEL}│#{IRC_RESET} #{STYLE_LABEL}Bet:#{IRC_RESET} #{STYLE_VALUE}#{bet}#{IRC_RESET}" end
Source
# File plugins/blackjack.rb, line 344 def self.hand_line_label_width(labels) labels.map { |label| label.to_s.length }.max end
Source
# File plugins/blackjack.rb, line 122 def self.hand_value(cards) total = cards.sum { |card| rank_value(card[:rank]) } aces = cards.count { |card| card[:rank] == "A" } while total > 21 && aces.positive? total -= 10 aces -= 1 end total end
Source
# File plugins/blackjack.rb, line 597 def self.hit(user, store: default_store, games: default_games, stats: default_stats, inventory: default_inventory, rand: ->(max) { Random.rand(0...max) }) game = active_game(user, games: games) return { error: "#{user}, no active hand — use !blackjack new <amount> to deal." } unless game hand_entry = current_hand(game) card, deck = deal_card(game[:deck]) hand_entry[:player] = hand_entry[:player] + [card] game[:deck] = deck game[:can_double] = false game[:can_split] = false if bust?(hand_entry[:player]) hand_entry[:busted] = true if game[:hands].length > 1 && pending_hands?(game) advance_to_next_hand(game) message = format_table( user: user, bet: game[:bet], game: game, dealer: game[:dealer], footer: game_action_footer(game), inventory: inventory ) return { player: hand_entry[:player], dealer: game[:dealer], message: message } end return resolve_game(user, game, store: store, games: games, stats: stats, inventory: inventory, rand: rand) end if hand_value(hand_entry[:player]) == 21 hand_entry[:stood] = true return hold(user, store: store, games: games, stats: stats, inventory: inventory, rand: rand) end message = format_table( user: user, bet: game[:bet], game: game, dealer: game[:dealer], footer: game_action_footer(game), inventory: inventory ) { player: hand_entry[:player], dealer: game[:dealer], message: message } end
Source
# File plugins/blackjack.rb, line 642 def self.hold(user, store: default_store, games: default_games, stats: default_stats, inventory: default_inventory, rand: ->(max) { Random.rand(0...max) }) game = active_game(user, games: games) return { error: "#{user}, no active hand — use !blackjack new <amount> to deal." } unless game hand_entry = current_hand(game) hand_entry[:stood] = true game[:can_double] = false game[:can_split] = false if game[:current_hand] < game[:hands].length - 1 advance_to_next_hand(game) message = format_table( user: user, bet: game[:bet], game: game, dealer: game[:dealer], footer: game_action_footer(game), inventory: inventory ) return { player: hand_entry[:player], dealer: game[:dealer], message: message } end resolve_game(user, game, store: store, games: games, stats: stats, inventory: inventory, rand: rand) end
Source
# File plugins/blackjack.rb, line 219 def self.inventory_for(user, inventory: default_inventory) key = normalize_user(user) owned = inventory[key] owned.nil? ? {} : owned.dup end
Source
# File plugins/blackjack.rb, line 83 def self.join_command_args(*parts) parts.flatten.compact.map(&:to_s).reject(&:empty?).join(" ").strip end
Source
# File plugins/blackjack.rb, line 417 def self.outcome_label(outcome) case outcome when :blackjack_win then "BLACKJACK!" when :win then "WIN!" when :lose then "LOSE" when :push then "PUSH" when :bust then "BUST" end end
Source
# File plugins/blackjack.rb, line 427 def self.outcome_style(outcome) case outcome when :blackjack_win, :win then STYLE_WIN when :lose, :bust then STYLE_LOSE when :push then STYLE_PUSH end end
Source
# File plugins/blackjack.rb, line 163 def self.parse_command(text) parts = text.to_s.strip.split(/\s+/) return { action: :help } if parts.empty? meta = parse_meta_command(parts) return meta if meta case parts[0].downcase when "new", "deal" amount = parts.length >= 2 ? parts[1].to_i : DEFAULT_BET return command_error unless valid_amount?(amount) return { action: :new, amount: amount } when "hit" return { action: :hit } when "hold", "stand" return { action: :hold } when "double" return { action: :double } when "split" return { action: :split } when "shop" return { action: :shop } if parts.length == 1 return { action: :shop_inventory } if parts[1].casecmp("inventory").zero? return command_error unless parts[1].casecmp("buy").zero? && parts.length >= 3 return { action: :shop_buy, item: parts[2..].join(" ").downcase } end command_error end
Source
# File plugins/blackjack.rb, line 462 def self.payout_for(outcome, bet) case outcome when :blackjack_win then bet + (bet * 1.5).to_i when :win then bet * 2 when :push then bet else 0 end end
Source
# File plugins/blackjack.rb, line 524 def self.pending_hands?(game) game[:hands].each_with_index.any? do |hand_entry, index| next false if hand_entry[:busted] || hand_entry[:stood] index >= game[:current_hand] end end
Source
# File plugins/blackjack.rb, line 99 def self.persist_store_entry!(store, key, value) store[key] = value end
Source
# File plugins/blackjack.rb, line 471 def self.play_dealer(dealer, deck) working_dealer = dealer.dup working_deck = deck while dealer_should_hit?(working_dealer) && !working_deck.empty? card, working_deck = deal_card(working_deck) working_dealer << card end [working_dealer, working_deck] end
Source
# File plugins/blackjack.rb, line 70 def self.random_tip(rand: ->(max) { Random.rand(0...max) }) tips[rand.call(tips.length)] end
Source
# File plugins/blackjack.rb, line 114 def self.rank_value(rank) case rank when "A" then 11 when "K", "Q", "J" then 10 else rank.to_i end end
Source
# File plugins/blackjack.rb, line 199 def self.record_outcome(user, outcome, stats: default_stats) key = normalize_user(user) entry = store_entry(stats, key, STATS_TEMPLATE) case outcome when :win then entry[:wins] += 1 when :lose then entry[:losses] += 1 when :push then entry[:pushes] += 1 when :bust then entry[:busts] += 1 when :blackjack_win then entry[:blackjack_wins] += 1 end persist_store_entry!(stats, key, entry) entry end
Source
# File plugins/blackjack.rb, line 481 def self.resolve_game(user, game, store:, games:, stats: default_stats, inventory: default_inventory, rand: ->(max) { Random.rand(0...max) }) dealer, = play_dealer(game[:dealer], game[:deck]) outcomes = [] game[:hands].each do |hand_entry| if hand_entry[:busted] outcomes << :bust record_outcome(user, :bust, stats: stats) next end outcome = compare_hands(hand_entry[:player], dealer) record_outcome(user, outcome, stats: stats) credit_winnings(user, payout_for(outcome, game[:bet]), store: store) unless outcome == :lose || outcome == :bust outcomes << outcome end balance = balance_for(user, store: store) user_stats = stats_for(user, stats: stats) clear_game(user, games: games) footer = if outcomes.length == 1 format_outcome(outcomes[0], balance, stats: user_stats, rand: rand) else format_multi_outcome(outcomes, balance, stats: user_stats, rand: rand) end message = format_table( user: user, bet: game[:bet], game: game, dealer: dealer, hide_dealer: false, footer: footer, inventory: inventory ) primary = outcomes.length == 1 ? outcomes[0] : outcomes { resolved: true, message: message, outcome: primary, balance: balance } end
Source
# File plugins/blackjack.rb, line 282 def self.shop_buy(user, item_name, store: default_store, inventory: default_inventory) key = shop_item_key(item_name) return { error: "unknown shop item — try !blackjack shop." } unless key item = SHOP_ITEMS[key] balance = balance_for(user, store: store) if balance < item[:price] return { error: "#{user}, not enough credits for a #{item[:name]} (balance: #{balance})." } end deduct_bet(user, item[:price], store: store) add_inventory_item(user, key, inventory: inventory) balance = balance_for(user, store: store) message = "#{STYLE_TITLE}#{SHOP_TITLE}#{IRC_RESET} #{STYLE_NICK}#{user}#{IRC_RESET} " \ "#{STYLE_LABEL}bought#{IRC_RESET} #{item[:icon]} #{STYLE_VALUE}#{item[:name]}#{IRC_RESET}! " \ "#{STYLE_LABEL}Balance:#{IRC_RESET} #{STYLE_VALUE}#{balance}#{IRC_RESET} " \ "#{STYLE_LABEL}credits#{IRC_RESET}" { message: message, balance: balance, item: key } end
Source
# File plugins/blackjack.rb, line 232 def self.shop_item_key(item_name) normalized = item_name.to_s.strip.downcase return normalized if SHOP_ITEMS.key?(normalized) SHOP_ITEMS.keys.find { |key| SHOP_ITEMS[key][:name].downcase == normalized } end
Source
# File plugins/blackjack.rb, line 694 def self.split(user, store: default_store, games: default_games, inventory: default_inventory) game = active_game(user, games: games) return { error: "#{user}, no active hand — use !blackjack new <amount> to deal." } unless game return { error: "#{user}, you can only split once on a matching pair." } unless can_split?(game) balance = balance_for(user, store: store) return { error: "#{user}, not enough credits to split (balance: #{balance})." } if balance < game[:bet] deduct_bet(user, game[:bet], store: store) original = game[:hands][0][:player] card_one, card_two = original game[:hands] = [ { player: [card_one] }, { player: [card_two] } ] game[:current_hand] = 0 game[:can_double] = false game[:can_split] = false 2.times do |index| card, deck = deal_card(game[:deck]) game[:hands][index][:player] << card game[:deck] = deck end message = format_table( user: user, bet: game[:bet], game: game, dealer: game[:dealer], footer: game_action_footer(game), inventory: inventory ) { player: game[:hands][0][:player], dealer: game[:dealer], message: message } end
Source
# File plugins/blackjack.rb, line 140 def self.splittable?(cards) cards.length == 2 && cards[0][:rank] == cards[1][:rank] end
Source
# File plugins/blackjack.rb, line 540 def self.start_game(user, amount, store: default_store, games: default_games, inventory: default_inventory, deck: nil, rand: ->(max) { Random.rand(0...max) }) key = normalize_user(user) return { error: "#{user}, you already have an active hand — hit or hold first." } if games[key] balance = balance_for(user, store: store) return { error: "#{user}, not enough credits (balance: #{balance})." } if amount > balance working_deck = deck ? deck.dup : shuffle_deck(build_deck, rand: rand) deduct_bet(user, amount, store: store) player = [] dealer = [] 2.times do card, working_deck = deal_card(working_deck) player << card end 2.times do card, working_deck = deal_card(working_deck) dealer << card end if blackjack?(player) || blackjack?(dealer) game = { bet: amount, deck: working_deck, dealer: dealer, hands: [{ player: player }], current_hand: 0, can_double: false, can_split: false } return resolve_game(user, game, store: store, games: games, inventory: inventory, rand: rand) .merge(player: player, dealer: dealer) end game = { bet: amount, deck: working_deck, dealer: dealer, hands: [{ player: player }], current_hand: 0, can_double: true, can_split: splittable?(player) } games[key] = game message = format_table( user: user, bet: amount, game: game, dealer: dealer, footer: game_action_footer(game), inventory: inventory ) { player: player, dealer: dealer, message: message } end
Source
# File plugins/blackjack.rb, line 195 def self.stats_for(user, stats: default_stats) store_entry(stats, normalize_user(user), STATS_TEMPLATE) end
Source
# File plugins/blackjack.rb, line 103 def self.store_entry(store, key, template) existing = store[key] existing.nil? ? template.dup : existing.dup end
Source
# File plugins/blackjack.rb, line 108 def self.usage_message "Usage: !blackjack new [amount] — start a hand (default #{DEFAULT_BET}) | !blackjack hit | !blackjack hold | " \ "!blackjack double | !blackjack split | !blackjack balance | !blackjack leaderboard | " \ "!beg (when broke) | !blackjack shop | !blackjack shop buy <item>" end
Public Instance Methods
Source
# File plugins/blackjack.rb, line 797 def blackjack(args_text = nil, **options) self.class.blackjack(args_text, **options) end
Source
# File plugins/blackjack.rb, line 801 def execute(m, *args) args_text = self.class.join_command_args(*args) result = self.class.dispatch_command( args_text, user: m.user.nick, store: self.class.default_store, games: self.class.default_games, stats: self.class.default_stats, inventory: self.class.default_inventory ) themed_flood_safe_reply(m, result[:error] || result[:message]) end
Source
# File plugins/blackjack.rb, line 815 def execute_beg(m) result = GameBeg.dispatch( m.user.nick, store: self.class.default_store ) themed_flood_safe_reply(m, result[:error] || result[:message]) end