LoginSignup
0
0

More than 3 years have passed since last update.

大貧民(どう書く)

Last updated at Posted at 2021-01-10

トランプゲーム「大貧民」(「大富豪」ともいいます)の場の札と手札が与えられた場合、次に出せるすべての手の組み合わせを出力する問題です。

ジョーカーがワイルドカードになっているので、それをどう処理するかが考えどころになります。ここではジョーカーが手札に含まれている場合の処理を別にしました。

Ruby
class Card
  Rank = %W(3 4 5 6 7 8 9 T J Q K A 2)
  def initialize(str)
    @value = str
    @rank = (str == "Jo") ? 13 : Rank.index(str[1])
  end
  attr_reader :value, :rank

  alias to_s value
end

module Daihinmin
  module_function

  def play(input)
    table, hand = input.split(",")
    return "-" unless hand 
    hand = hand.scan(/../).map { |c| Card.new(c) }
    joker = hand.find {|c| c.value == "Jo"}    
    table = table.scan(/../).map { |c| Card.new(c) }
    t_rank = table.map(&:rank).min
    t_num = table.size

    cs = hand.group_by(&:rank).select { |k, v| k > t_rank }.values
    result = cs.select { |ary| ary.size >= t_num }
               .flat_map { |ary| ary.combination(t_num).to_a }
    if joker && t_num >= 2
      result +=
        cs.select { |ary| ary.size >= t_num - 1 && ary[0] != joker }
          .flat_map {|ary|
            ary.combination(t_num - 1).map { |cards| cards + [joker] }
          }
    end

    result.empty? ? "-" : result.map(&:join).join(",")
  end
end


if __FILE__ == $0
  [
    "DJ,",
    "H7,HK",
    "S3,D4D2",
    "S9,C8H4",
    "S6,S7STCK",
    "H4,SAS8CKH6S4",
    "ST,D6S8JoC7HQHAC2CK",
    "SA,HAD6S8S6D3C4H2C5D4CKHQS7D5",
    "S2,D8C9D6HQS7H4C6DTS5S6C7HAD4SQ",
    "Jo,HAC8DJSJDTH2",
    "S4Jo,CQS6C9DQH9S2D6S3",
    "CTDT,S9C2D9D3JoC6DASJS4",
    "H3D3,DQS2D6H9HAHTD7S6S7Jo",
    "D5Jo,CQDAH8C6C9DQH7S2SJCKH5",
    "C7H7,S7CTH8D5HACQS8JoD6SJS5H4",
    "SAHA,S7SKCTS3H9DJHJH7S5H2DKDQS4",
    "JoC8,H6D7C5S9CQH9STDTCAD9S5DAS2CT",
    "HTST,SJHJDJCJJoS3D2",
    "C7D7,S8D8JoCTDTD4CJ",
    "DJSJ,DTDKDQHQJoC2",
    "C3H3D3,CKH2DTD5H6S4CJS5C6H5S9CA",
    "D8H8S8,CQHJCJJoHQ",
    "H6D6S6,H8S8D8C8JoD2H2",
    "JoD4H4,D3H3S3C3CADASAD2",
    "DJHJSJ,SQDQJoHQCQC2CA",
    "H3D3Jo,D4SKH6CTS8SAS2CQH4HAC5DADKD9",
    "C3JoH3D3,S2S3H7HQCACTC2CKC6S7H5C7",
    "H5C5S5D5,C7S6D6C3H7HAH6H4C6HQC9",
    "H7S7C7D7,S5SAH5HAD5DAC5CA",
    "D4H4S4C4,S6SAH6HAD6DAC6CAJo",
    "DTCTSTHT,S3SQH3HQD3DQC3CQJo",
    "JoS8D8H8,S9DTH9CTD9STC9CAC2"
  ].each do |input|
    puts Daihinmin.play(input)
  end
end

カードをどう表現するか迷って、ここではクラス化しました(Cardクラス)。カードのランクを数値化し、弱い順に 0 からの Integer を与えてあります(Card#rank)。単独でいちばん強いジョーカーのランクは 13 です。String で表されるカードの表現はCard#valueで取得できます。

処理としては、まず場の札をtable、手札をhandとします。ジョーカーが存在すればjokerに入ります(存在しなければnil)。
手札をランクによってグループ化し、場の札のランク(t_rank)よりランクの高いものだけを残したのがcsです。

csから、場の札の枚数(t_num)以上のものを残し、あとは場の札の枚数だけの組み合わせを取ってresultに格納します。
手札にジョーカーがあり、かつ場の札の枚数(t_num)が2枚以上の場合は、ジョーカーをワイルドカードとしながらさらに同様のことをします。

結果を String で表現する(Array#join を使っています)ため、Card#to_sCard#valueのエイリアスとして設定しています。

なお、出力は順序の不定性があるので、テストをサボっています(笑)。

追記

テストも書いてみました。


if __FILE__ == $0
  def same?(input, expect)
    inputs = input.split(",")
    expects = expect.split(",")
    return false unless inputs.size == expects.size
    equal = ->(a, b) {
      a == b || a.scan(/../).sort == b.scan(/../).sort
    }
    is_found = ->(ans) {
      s = expects.find { |e| equal.(e, ans) }
      return false unless s
      expects.delete(s)
      true
    }
    inputs.all?(&is_found)
  end

  [
    ["DJ,", "-"],
    ["H7,HK", "HK"],
    ["S3,D4D2", "D4,D2"],
    ["S9,C8H4", "-"],
    ["S6,S7STCK", "CK,ST,S7"],
    ["H4,SAS8CKH6S4", "S8,CK,H6,SA"],
    ["ST,D6S8JoC7HQHAC2CK", "Jo,C2,CK,HA,HQ"],
    ["SA,HAD6S8S6D3C4H2C5D4CKHQS7D5", "H2"],
    ["S2,D8C9D6HQS7H4C6DTS5S6C7HAD4SQ", "-"],
    ["Jo,HAC8DJSJDTH2", "-"],
    ["S4Jo,CQS6C9DQH9S2D6S3", "DQCQ,D6S6,H9C9"],
    ["CTDT,S9C2D9D3JoC6DASJS4", "JoC2,SJJo,DAJo"],
    ["H3D3,DQS2D6H9HAHTD7S6S7Jo", "JoHA,JoD6,JoH9,D6S6,D7S7,JoS6,HTJo,JoDQ,S2Jo,JoD7,JoS7"],
    ["D5Jo,CQDAH8C6C9DQH7S2SJCKH5", "CQDQ"],
    ["C7H7,S7CTH8D5HACQS8JoD6SJS5H4", "HAJo,JoSJ,H8S8,H8Jo,CQJo,CTJo,JoS8"],
    ["SAHA,S7SKCTS3H9DJHJH7S5H2DKDQS4", "-"],
    ["JoC8,H6D7C5S9CQH9STDTCAD9S5DAS2CT", "CTDT,H9D9,S9D9,DACA,CTST,H9S9,DTST"],
    ["HTST,SJHJDJCJJoS3D2", "DJCJ,SJDJ,JoHJ,CJHJ,SJJo,HJSJ,DJJo,JoCJ,JoD2,SJCJ,DJHJ"],
    ["C7D7,S8D8JoCTDTD4CJ", "D8S8,JoS8,CTJo,DTJo,JoCJ,CTDT,D8Jo"],
    ["DJSJ,DTDKDQHQJoC2", "JoDK,HQDQ,DQJo,C2Jo,JoHQ"],
    ["C3H3D3,CKH2DTD5H6S4CJS5C6H5S9CA", "S5H5D5"],
    ["D8H8S8,CQHJCJJoHQ", "JoCQHQ,JoHJCJ"],
    ["H6D6S6,H8S8D8C8JoD2H2", "D2H2Jo,D8JoS8,D8S8C8,C8D8H8,JoC8S8,H8JoC8,S8H8C8,JoS8H8,C8JoD8,D8H8S8,D8JoH8"],
    ["JoD4H4,D3H3S3C3CADASAD2", "DACASA"],
    ["DJHJSJ,SQDQJoHQCQC2CA", "SQJoCQ,DQCQJo,JoSQHQ,SQCQHQ,DQHQSQ,HQDQCQ,HQDQJo,SQDQCQ,CQJoHQ,SQJoDQ"],
    ["H3D3Jo,D4SKH6CTS8SAS2CQH4HAC5DADKD9", "HASADA"],
    ["C3JoH3D3,S2S3H7HQCACTC2CKC6S7H5C7", "-"],
    ["H5C5S5D5,C7S6D6C3H7HAH6H4C6HQC9", "C6D6S6H6"],
    ["H7S7C7D7,S5SAH5HAD5DAC5CA", "SADACAHA"],
    ["D4H4S4C4,S6SAH6HAD6DAC6CAJo", "C6H6S6D6,SAJoDACA,S6H6C6Jo,SACAJoHA,HADASAJo,HADAJoCA,CADAHASA,D6C6JoH6,S6D6C6Jo,H6JoS6D6"],
    ["DTCTSTHT,S3SQH3HQD3DQC3CQJo", "HQSQJoDQ,SQCQDQJo,DQCQHQJo,SQHQJoCQ,CQDQHQSQ"],
    ["JoS8D8H8,S9DTH9CTD9STC9CAC2", "H9C9D9S9"],
  ].each do |input, expect|
    p same? Daihinmin.play(input), expect
  end
end
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0