トラック
/
Python
Python
/
演習
/
ブラックジャック
ブラックジャック

ブラックジャック

学習演習

はじめに

比較

Pythonは、次の基本的な比較演算子をサポートしています。

演算子 演算 説明
> 「より大きい」 a > bは、aの値がbより厳密に大きい場合にTrueになります
< 「より小さい」 a < bは、aの値がbより厳密に小さい場合にTrueになります
== 「等しい」 a == bは、aの値がbと厳密に等しい場合にTrueになります
>= 「以上」 a >= bは、a > bであるか、または値がa == bの場合にTrueになります
<= 「以下」 a <= bは、a < bまたはa == bの場合にTrueになります
!= 「等しくない」 a != bは、a == bがFalseの場合にTrueになります
is 「同一性」 a is bは、aとbが同じ_オブジェクト_である場合にのみTrueになります
is not 「同一性の否定」 a is not bは、aとbが同じ_オブジェクト_でない場合にTrueになります
in 「包含テスト」 a in bは、aがbのメンバー、部分集合、または要素である場合にTrueになります
not in 「包含テストの否定」 a not in bは、aがbのメンバー、部分集合、または要素でない場合にTrueになります

これらの優先順位はすべて同じです(これはブール演算より高く、算術演算やビット演算より低くなっています)。

異なるデータ型同士の比較

型が異なるオブジェクトは(数値型を除いて)、デフォルトでは決して等しいと比較されません。 classの非同一のインスタンスも、そのclassがデフォルトのobject比較動作をカスタマイズする特別な拡張比較メソッドを定義していない限り、_等しい_とは比較されません。 rich comparisonsによるカスタマイズは、続く演習で扱います。 このトピックの(はるかに)詳しい説明は、Pythonのドキュメントの値の比較を参照してください。

数値型は(ほとんどが)この型一致の規則の例外です。 比較のために型を暗黙に変換できるのであれば、integerはfloat(またはoctalがhexadecimalと等しい)と等しいと見なされることがあります。

Python標準ライブラリにある他の数値型(複素数、十進数、分数)では、比較演算子は「意味を成す」ところ(暗黙の変換が結果を変えないところ)で定義されていますが、比較のために元のオブジェクトを正確に変換できない場合はTypeErrorを送出します。 Pythonが_数値変換_に用いる規則の詳細は、Pythonのドキュメントの算術変換を参照してください。

>>> import fractions

# A string cannot be converted to an int.
>>> 17 == '17'
False

# An int can be converted to float for comparison.
>>> 17 == 17.0
True

# The fraction 6/3 can be converted to the int 2
# The int 2 can be converted to 0b10 in binary.
>>> 6/3 == 0b10
True

# An int can be converted to a complex number with a 0 imaginary part.
>>> 17 == complex(17)
True

# The fraction 2/5 can be converted to the float 0.4
>>> 0.4 == 2/5
True

>>> complex(2/5, 1/2) == complex(0.4, 0.5)
True

数値とNaN(not a number、非数)型との順序比較は、すべてFalseになります。 PythonのNaNの定義による紛らわしい副作用として、NaNはNaNと決して等しいと比較されません。

>>> x = float('NaN')

>>> 3 < x
False

>>> x < 3
False

# NaN never compares equal to NaN
>>> x == x
False

文字列の比較

数値と違い、文字列(str)は、それぞれのUnicodeコードポイント(str内の各コードポイントを組み込み関数ord()に渡した結果で、これはintを返します)を使って、辞書式順序で比較されます。 両方の文字列のすべてのコードポイントが一致し、_かつ同じ順序である_場合、その2つの文字列は等しいと見なされます。 この比較は「ペアごと」に行われます。つまり、1文字目と1文字目、2文字目と2文字目、というように進みます。 Python 3.xでは、strとbytesを直接強制変換したり比較したりすることはできません。

>>> 'Python' > 'Rust'
False

>>> 'Python' > 'JavaScript'
True

# Examples with Mandarin.
# hello < goodbye
>>> '你好' < '再见'
True

# ord() of first characters
>>> ord('你'), ord('再')
(20320, 20877)

# ord() of second characters
>>> ord('好'), ord('见')
(22909, 35265)

# And with Korean words.
# Pretty < beautiful.
>>> '예쁜' < '아름다운'
False

>>> ord('예'), ord('아')
(50696, 50500)

比較の連結

比較演算子は_自由に_連結できます。つまり、どのような組み合わせでも、どのような長さでも使えます。 式の評価はleftからrightへと行われることに注意してください。

例として、x < y <= zはx < y and y <= zと等価ですが、yが一度だけ評価されるという点が異なります。 どちらの場合も、x < yがFalseと判明した時点で、zはまったく評価され_ません_。 これは一般にshort-circuit evaluationと呼ばれます。式の真偽値がすでに決まっていれば、評価は止まるのです。

Short circuitingは、さまざまなブール演算子や関数でサポートされており、Pythonでは比較の連結でもサポートされています。 C、C++、C#、Javaなど多くのプログラミング言語と違い、Pythonではa < b < cのような連結式が、従来の数学的な解釈と優先順位を持ちます。

>>> x = 2
>>> y = 5
>>> z = 10

>>> x < y < z
True

>>> x < y > z
False

>>> x > y < z
False

オブジェクトの同一性の比較

演算子isとis notは、オブジェクトの_値_ではなく、オブジェクトの同一性を調べます。 オブジェクトの同一性は作成後に変わることはなく、id()関数を使うと調べられます。

<apple> is <orange>は、id(<apple>) == id(<orange>)の場合にのみTrueになります。 <apple> is not <orange>はその逆になります。

NoneとNotImplementedはシングルトンであるため、常にisとis notを使って項目と比較するべきです。 この慣習の詳細については、Pythonリファレンスの値の比較とPEP8を参照してください。

>>> my_fav_numbers = [1, 2, 3]

>>> your_fav_numbers = my_fav_numbers

>>> my_fav_numbers is your_fav_numbers
True

# The returned id will differ by system and python version.
>>> id(my_fav_numbers)
4517478208

# your_fav_numbers is only an alias pointing to the original my_fav_numbers object.
# Assigning a new name does not create a new object.
>>> id(your_fav_numbers)
4517478208


>>> my_fav_numbers is not your_fav_numbers
False

>>> my_fav_numbers is not None
True

>>> my_fav_numbers is NotImplemented
False

メンバーシップの比較

演算子inとnot inは、メンバーシップ_を調べます。 <fish> in <soup>は、<fish>が<soup>のメンバーである場合(<fish>が<soup>の部分集合であるか、<soup>に含まれている場合_)にTrueになり、それ以外の場合はFalseになります。 <fish> not in <soup>は、その否定、つまり<fish> in <soup>の_反対_を返します。

文字列型とバイト型では、<name> in <fullname>は、<name>が<fullname>の部分文字列である場合にのみTrueになります。

# A set of lucky numbers.
>>> lucky_numbers = {11, 22, 33}
>>> 22 in lucky_numbers
True

>>> 44 in lucky_numbers
False

# A dictionary of employee information.
>>> employee = {'name': 'John Doe', 
                'id': 67826, 'age': 33, 
                'title': 'ceo'}

# Checking for the membership of certain keys.
>>> 'age' in employee
True

>>> 33 in employee
False

>>> 'lastname' not in employee
True

# Checking for substring membership
>>> name = 'Super Batman'
>>> 'Bat' in name
True

>>> 'Batwoman' in name
False

説明

この演習では、ブラックジャックのルールの一部を実装します。たとえば、ゲームの進め方や得点の計算方法などです。

注意:この演習では、_Aはエース、Jはジャック、Qはクイーン、K_はキングを表します。ジョーカーは使いません。標準的なフランス式の52枚組のデッキを想定していますが、ほとんどの場合は複数のデッキを混ぜてプレイします。

1. カードの値を計算する

ブラックジャックでは、エースを1点にするか11点にするかは、プレイヤーがそれぞれ自分で決められます(詳しくは後ほど)。絵札(J、Q、K)は10点で、それ以外のカードは「ピップ」(数字)の値と同じ点数になります。

cardを仮引数とするvalue_of_card(<card>)関数を定義してください。この関数は、渡されたカードの文字列の_数値_を返します。エースは複数の値(1または11)を取ることができるので、この関数ではとりあえずエースのカードの値を1に固定しておきます。後ほど、手札がすでにある状態でエースの値を決める関数を実装します。

>>> value_of_card('K')
10

>>> value_of_card('4')
4

>>> value_of_card('A')
1

2. どちらのカードの値が大きいかを判定する

card_oneとcard_twoを仮引数とするhigher_card(<card_one>, <card_two>)関数を定義してください。得点計算では、J、Q、Kの値は10です。この関数は、得点計算上どちらのカードの値が大きいかを返します。両方のカードの値が等しい場合は、両方を返します。両方のカードを返すには、return文でカンマを使います。

# Using a comma in a return creates a Tuple.  Tuples will be covered in a later exercise.
>>> def returning_two_values(value_one, value_two):
        return value_one, value_two

>>> returning_two_values('K', '3')
('K', '3')

エースは複数の値を取ることができるので、このタスクではAのカードの値を1に固定します。

>>> higher_card('K', '10')
('K', '10')

>>> higher_card('4', '6')
'6'

>>> higher_card('K', 'A')
'K'

3. エースの値を計算する

すでに触れたとおり、エースは1点または11点の_どちらか_になります。プレイヤーは、21点を_超える_ことなく(これを「バスト」といいます)、できるだけ21点に近づけようとします。

card_oneとcard_twoを仮引数とするvalue_of_ace(<card_one>, <card_two>)関数を定義してください。この2枚は、エースのカードを引く_前に_すでに手札にあるカードです。この関数は、これから引くエースを1にするか11にするかを判断し、その値を返す必要があります。エースを含めた手札の値が、21点を超えない範囲でできるだけ大きくなるようにしてください。

ヒント:すでに手札にエースがある場合、これから引くエースの値は1になります。

>>> value_of_ace('6', 'K')
1

>>> value_of_ace('7', '3')
11

4. 「ナチュラル」または「ブラックジャック」の手札を判定する

最初の2枚としてエース(A)と10のカード(10、K、Q、J)が配られると、プレイヤーの得点は21点になります。これはブラックジャックの手札と呼ばれます。

card_oneとcard_twoを仮引数とするis_blackjack(<card_one>, <card_two>)関数を定義してください。この2枚はカードの組です。2枚の手札がブラックジャックかどうかを判定し、そうであれば真偽値のTrueを、そうでなければFalseを返してください。

注意:得点の_計算_はさまざまな方法で行えます。ですが、できれば手札の値を_合計する_のではなく、手札に(または特定の位置に)エースと10のカードがあるかどうかを確認するようにしてください。

>>> is_blackjack('A', 'K')
True

>>> is_blackjack('10', '9')
False

5. ペアを分割する

手札の最初の2枚が同じ値の場合(たとえば、6が2枚、あるいはQとKなど)、プレイヤーはそれらを2つの別々の手札として扱うことを選べます。これは「ペアの分割」と呼ばれます。

card_oneとcard_twoを仮引数とするcan_split_pairs(<card_one>, <card_two>)関数を定義してください。この2枚はカードの組です。この2枚の手札を2つのペアに分割できるかどうかを判定してください。分割できる場合は真偽値のTrueを返し、そうでなければFalseを返します。

>>> can_split_pairs('Q', 'K')
True

>>> can_split_pairs('10', 'A')
False

6. ダブルダウン

配られた最初の2枚の合計が9、10、11点のとき、プレイヤーは元の賭け金と同額の追加の賭け金を置くことができます。これは「ダブルダウン」と呼ばれます。

card_oneとcard_twoを仮引数とするcan_double_down(<card_one>, <card_two>)関数を定義してください。この2枚はカードの組です。この2枚の手札を「ダブルダウン」できるかどうかを判定し、できる場合は真偽値のTrueを、そうでなければFalseを返してください。

>>> can_double_down('A', '9')
True

>>> can_double_down('10', '2')
False
GitHubで編集する リンクは新しいウィンドウまたはタブで開きます
Python Exercism

ブラックジャックを始める準備はできましたか?

Exercismに登録すれば、17個のコンセプト146個の演習、そして本物の人間によるメンタリングとともに、Pythonを学んでマスターできます。すべて無料です。