【Python3】０から作るPython初心者プログラミング【04】-あっち向いてほいプログラム-

#【ご挨拶】こんにちは! ぬかさんエンジニアリングです。(４回目‼)
今回は、大人数であっち向いてほいトーナメントバトルが出来るプログラムをつくりたいと思います！
基本的には土台として前回の[大人数じゃんけんプログラム](https://qiita.com/Nuka-san-Engineering/items/f94c441c9750656b7f70#%E7%AC%AC03%E5%9B%9E--%E5%A4%A7%E4%BA%BA%E6%95%B0%E3%81%A7%E3%81%98%E3%82%83%E3%82%93%E3%81%91%E3%82%93%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0-)のコードを転用して作ることになります。作り方だけ見たいという方は前回の記事も一緒に見てもらった方が理解度は高まるかと思います。己の力のみで０からお題に挑戦したいという方ものために「機能要件」を用意していますので、それだけ見て作ってみるのもいいかもしれません。
今回のプログラムからいよいよゲームらしくなっていきます。それに伴って初心者の中には少し難易度が高いと感じる方もいるかもしれません。どうしても行き詰った場合はまず寝てください。もしくは〔作り方〕をサラッと見てください。いずれにせよ最後まで作りきることを目標にして下さい。私もこの記事を作るために<font color="81C4FF">**クラス**</font>について勉強し直して結構時間をが掛かりましたが、完成した時の達成感と脱力感はいい物でした。質問等も受け付けておりますので一緒に頑張りましょう！
<font color="LimeGreen">**LGTM**</font>も是非よろしくお願いします‼










#本シリーズ初めての方へ
<details><summary><strong>【趣旨】</strong></summary>
Python初心者プログラマーが入門書で学んだ知識を実践的なコーディングを通じて身に着けていくためのお題を提供します。
お題を基に各自コーディングに挑戦していただいた後、この記事でコーディングの過程を答え合わせします。
</details>


<details><summary><strong>【対象】</strong></summary>
Pythonの入門書を読んで理解はしたけど何か目的をもって実践的にコーディングをしたい方。
ProgateでPythonコースをLv5まで勉強したけど応用力が身に着いていないと感じている方。
</details>


<details><summary><strong>【初心者とは】</strong></summary>
この記事シリーズでの初心者は下記の項目を理解済みであることが目安となっています。
[演算子, 標準ライブラリ, 条件分岐, 繰り返し処理, 例外処理, リスト, タプル, セット, 辞書, オブジェクト指向]
</details>


<details><summary><strong>【利用ライブラリ & Python3 --version】<strong></summary><div>
・Google Colaboratory
以下のリンクからアクセスして使い方を確認した後、左上のファイルタブから「ノートブックを新規作成」を選択して自分のプログラムを作りましょう。ファイルはGoogleDriveに保存されるため、自分のGoogleアカウントと連携させるのを忘れないようにしましょう。

<a href="https://colab.research.google.com/notebooks/welcome.ipynb?hl=ja#scrollTo=2fhs6GZ4qFMx">Colaboratory へようこそ</a>  ←ここからリンクへ飛ぶ

・Python 3.7.12

</div></details>

$$$$

それではさっそく本題に入っていきましょう:muscle:

#第【04】回 -あっち向いてほいプログラム-
このシリーズ第四回目の今回は、前回の<font color="Coral">**大人数じゃんけんプログラム**</font>から更にゲーム性をアップして<font color="Coral">**あっち向いてほいトーナメントバトル**</font>が出来るプログラムを実装します！
ゲーム性がアップしただけありゲームの進み方も複雑になっていきます。どんな可能性でもプログラムがエラーを起こさず最後まで処理できるように何度もチェックしながら決められた<font color="Coral">**「要件」**</font>を満たすプログラムを一緒に作っていきましょう！










##【お題】あっち向てほいでトーナメントバトルが出来るプログラムをつくろう！
```py
hoi_battle_ground()

#出力

==============================

あっち向いてほいBATTLEGROUNDS


       >>GAMESTART<<

          Press A

==============================
A





============================================================

        あっち向いてほいBATTLEGROUNDSへようこそ！


    このゲームでは最大６４人でトーナメント戦を行います。
                    ルールは単純です。
  あっち向いてほいで決められた点数を先に獲得したら勝利です。


   トーナメントを勝ち抜いて優勝を目指して頑張りましょう！

 ============================================================





============================================================

あっち向いてほいBATTLEGROUNDSの設定を決めてください


参加人数を4,8,16,32,64人から選択してください >8


プレイヤーの人数を選択してください(最大人数は8人です) >3


プレイヤー名を記入してください。>nu
<nu>さんですね。エントリー完了しました。


プレイヤー名を記入してください。>ka
<ka>さんですね。エントリー完了しました。


プレイヤー名を記入してください。>san
<san>さんですね。エントリー完了しました。


試合のマッチポイントを入力してください。 >3

============================================================








3ポイントマッチ制 8人トーナメントスタート！！


-----------------------------------
第1回戦対戦表

コンピュータ5 vs ka
コンピュータ3 vs コンピュータ4
コンピュータ1 vs nu
コンピュータ2 vs san
-----------------------------------
-----------------------------------
第1回戦 第1試合

コンピュータ5 vs ka
-----------------------------------

>>>じゃんけんぽん！<<<
選択肢: {1: 'グー', 2: 'チョキ', 3: 'パー'}
kaの出す手を入力してください(整数：1, 2, 3) 〉1
コンピュータ5の手：パー
kaの手：グー


◎◎コンピュータ5の勝ち！◎◎


>>>あっち向いてほい！<<<
あなたの顔の向きを選択してください(整数：1,2,3,4) 〉1
コンピュータ5の手:←
kaの手:↑
××じゃんけんに戻る××

>>>じゃんけんぽん！<<<
選択肢: {1: 'グー', 2: 'チョキ', 3: 'パー'}
kaの出す手を入力してください(整数：1, 2, 3) 〉1
コンピュータ5の手：グー
kaの手：グー

△△あいこでしょ！！△△
選択肢: {1: 'グー', 2: 'チョキ', 3: 'パー'}
kaの出す手を入力してください(整数：1, 2, 3) 〉1
コンピュータ5の手：チョキ
kaの手：グー


◎◎kaの勝ち！◎◎


>>>あっち向いてほい！<<<
{1: '↑', 2: '→', 3: '↓', 4: '←'}
あなたの指の向きを選択してください(整数：1,2,3,4) 〉1
kaの手:↑
コンピュータ5の手:↑


◎◎◎kaの勝利！1ポイント獲得！◎◎◎


現在のポイント               
コンピュータ5:0ポイント               
ka:1ポイント

>>>じゃんけんぽん！<<<
選択肢: {1: 'グー', 2: 'チョキ', 3: 'パー'}
kaの出す手を入力してください(整数：1, 2, 3) 〉1
コンピュータ5の手：チョキ
kaの手：グー


◎◎kaの勝ち！◎◎


>>>あっち向いてほい！<<<
{1: '↑', 2: '→', 3: '↓', 4: '←'}
あなたの指の向きを選択してください(整数：1,2,3,4) 〉1
kaの手:↑
コンピュータ5の手:↑


◎◎◎kaの勝利！1ポイント獲得！◎◎◎


現在のポイント               
コンピュータ5:0ポイント               
ka:2ポイント

>>>じゃんけんぽん！<<<
選択肢: {1: 'グー', 2: 'チョキ', 3: 'パー'}
kaの出す手を入力してください(整数：1, 2, 3) 〉1
コンピュータ5の手：グー
kaの手：グー

△△あいこでしょ！！△△
選択肢: {1: 'グー', 2: 'チョキ', 3: 'パー'}
kaの出す手を入力してください(整数：1, 2, 3) 〉1
コンピュータ5の手：チョキ
kaの手：グー


◎◎kaの勝ち！◎◎


>>>あっち向いてほい！<<<
{1: '↑', 2: '→', 3: '↓', 4: '←'}
あなたの指の向きを選択してください(整数：1,2,3,4) 〉1
kaの手:↑
コンピュータ5の手:↑


◎◎◎kaの勝利！1ポイント獲得！◎◎◎


><><><>3ポイント先取によりkaの勝利！<><><><


-----------------------------------
第1回戦 第2試合

コンピュータ3 vs コンピュータ4
-----------------------------------

>>>じゃんけんぽん！<<<
コンピュータ3の手：パー
コンピュータ4の手：パー

△△あいこでしょ！！△△
コンピュータ3の手：グー
コンピュータ4の手：チョキ


◎◎コンピュータ3の勝ち！◎◎


>>>あっち向いてほい！<<<
コンピュータ3の手:↓
コンピュータ4の手:↑
××じゃんけんに戻る××

>>>じゃんけんぽん！<<<
コンピュータ3の手：チョキ
コンピュータ4の手：チョキ

△△あいこでしょ！！△△
コンピュータ3の手：グー
コンピュータ4の手：チョキ


◎◎コンピュータ3の勝ち！◎◎


>>>あっち向いてほい！<<<
コンピュータ3の手:→
コンピュータ4の手:→


◎◎◎コンピュータ3の勝利！1ポイント獲得！◎◎◎


現在のポイント               
コンピュータ3:1ポイント               
コンピュータ4:0ポイント

>>>じゃんけんぽん！<<<
コンピュータ3の手：パー
コンピュータ4の手：パー

△△あいこでしょ！！△△
コンピュータ3の手：チョキ
コンピュータ4の手：グー


◎◎コンピュータ4の勝ち！◎◎


>>>あっち向いてほい！<<<
コンピュータ4の手:←
コンピュータ3の手:←


◎◎◎コンピュータ4の勝利！1ポイント獲得！◎◎◎


現在のポイント               
コンピュータ3:1ポイント               
コンピュータ4:1ポイント

>>>じゃんけんぽん！<<<
コンピュータ3の手：チョキ
コンピュータ4の手：パー


◎◎コンピュータ3の勝ち！◎◎


>>>あっち向いてほい！<<<
コンピュータ3の手:←
コンピュータ4の手:←


◎◎◎コンピュータ3の勝利！1ポイント獲得！◎◎◎


現在のポイント               
コンピュータ3:2ポイント               
コンピュータ4:1ポイント

>>>じゃんけんぽん！<<<
コンピュータ3の手：パー
コンピュータ4の手：チョキ


◎◎コンピュータ4の勝ち！◎◎


>>>あっち向いてほい！<<<
コンピュータ4の手:↓
コンピュータ3の手:↓


◎◎◎コンピュータ4の勝利！1ポイント獲得！◎◎◎


現在のポイント               
コンピュータ3:2ポイント               
コンピュータ4:2ポイント

>>>じゃんけんぽん！<<<
コンピュータ3の手：チョキ
コンピュータ4の手：グー


◎◎コンピュータ4の勝ち！◎◎


>>>あっち向いてほい！<<<
コンピュータ4の手:↓
コンピュータ3の手:↓


◎◎◎コンピュータ4の勝利！1ポイント獲得！◎◎◎


><><><>3ポイント先取によりコンピュータ4の勝利！<><><><


-----------------------------------
第1回戦 第3試合

コンピュータ1 vs nu
-----------------------------------

>>>じゃんけんぽん！<<<
選択肢: {1: 'グー', 2: 'チョキ', 3: 'パー'}
nuの出す手を入力してください(整数：1, 2, 3) 〉2
コンピュータ1の手：グー
nuの手：チョキ

                    ・
                    ・
                    ・
                    ・
                    ・            

>>>じゃんけんぽん！<<<
選択肢: {1: 'グー', 2: 'チョキ', 3: 'パー'}
nuの出す手を入力してください(整数：1, 2, 3) 〉2
コンピュータ1の手：グー
nuの手：チョキ


◎◎コンピュータ1の勝ち！◎◎


>>>あっち向いてほい！<<<
あなたの顔の向きを選択してください(整数：1,2,3,4) 〉2
コンピュータ1の手:→
nuの手:→


◎◎◎コンピュータ1の勝利！1ポイント獲得！◎◎◎


><><><>3ポイント先取によりコンピュータ1の勝利！<><><><


-----------------------------------
第1回戦 第4試合

コンピュータ2 vs san
-----------------------------------

>>>じゃんけんぽん！<<<
選択肢: {1: 'グー', 2: 'チョキ', 3: 'パー'}
sanの出す手を入力してください(整数：1, 2, 3) 〉3
コンピュータ2の手：チョキ
sanの手：パー

                    ・
                    ・
                    ・
                    ・
                    ・

>>>じゃんけんぽん！<<<
選択肢: {1: 'グー', 2: 'チョキ', 3: 'パー'}
sanの出す手を入力してください(整数：1, 2, 3) 〉3
コンピュータ2の手：パー
sanの手：パー

△△あいこでしょ！！△△
選択肢: {1: 'グー', 2: 'チョキ', 3: 'パー'}
sanの出す手を入力してください(整数：1, 2, 3) 〉3
コンピュータ2の手：チョキ
sanの手：パー


◎◎コンピュータ2の勝ち！◎◎


>>>あっち向いてほい！<<<
あなたの顔の向きを選択してください(整数：1,2,3,4) 〉3
コンピュータ2の手:↓
sanの手:↓


◎◎◎コンピュータ2の勝利！1ポイント獲得！◎◎◎


><><><>3ポイント先取によりコンピュータ2の勝利！<><><><


-----------------------------------
準決勝対戦表
ka vs コンピュータ4
コンピュータ1 vs コンピュータ2
-----------------------------------
-----------------------------------
準決勝戦 第1試合

ka vs コンピュータ4
-----------------------------------

>>>じゃんけんぽん！<<<
選択肢: {1: 'グー', 2: 'チョキ', 3: 'パー'}
kaの出す手を入力してください(整数：1, 2, 3) 〉2
kaの手：チョキ
コンピュータ4の手：パー

                    ・
                    ・
                    ・
                    ・
                    ・

>>>じゃんけんぽん！<<<
選択肢: {1: 'グー', 2: 'チョキ', 3: 'パー'}
kaの出す手を入力してください(整数：1, 2, 3) 〉2
kaの手：チョキ
コンピュータ4の手：グー


◎◎コンピュータ4の勝ち！◎◎


>>>あっち向いてほい！<<<
あなたの顔の向きを選択してください(整数：1,2,3,4) 〉2
コンピュータ4の手:→
kaの手:→


◎◎◎コンピュータ4の勝利！1ポイント獲得！◎◎◎


><><><>3ポイント先取によりコンピュータ4の勝利！<><><><


-----------------------------------
準決勝戦 第2試合

コンピュータ1 vs コンピュータ2
-----------------------------------

>>>じゃんけんぽん！<<<
コンピュータ1の手：パー
コンピュータ2の手：グー

           ・
           ・
           ・
           ・
           ・

>>>じゃんけんぽん！<<<
コンピュータ1の手：パー
コンピュータ2の手：グー


◎◎コンピュータ1の勝ち！◎◎


>>>あっち向いてほい！<<<
コンピュータ1の手:←
コンピュータ2の手:←


◎◎◎コンピュータ1の勝利！1ポイント獲得！◎◎◎


><><><>3ポイント先取によりコンピュータ1の勝利！<><><><


-----------------------------------
決勝対戦表
コンピュータ4 vs コンピュータ1
-----------------------------------
-----------------------------------
決勝戦

コンピュータ4 vs コンピュータ1
-----------------------------------

>>>じゃんけんぽん！<<<
コンピュータ4の手：チョキ
コンピュータ1の手：グー

           ・
           ・
           ・
           ・
           ・

>>>じゃんけんぽん！<<<
コンピュータ4の手：チョキ
コンピュータ1の手：パー


◎◎コンピュータ4の勝ち！◎◎


>>>あっち向いてほい！<<<
コンピュータ4の手:↑
コンピュータ1の手:↑


◎◎◎コンピュータ4の勝利！1ポイント獲得！◎◎◎


><><><>3ポイント先取によりコンピュータ4の勝利！<><><><


 +*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*
*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+
     栄えある優勝者はコンピュータ4！！おめでとうございます！！！
*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+
 +*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*


もう一度プレイしますか？(Yes:y/No:n)n
```
***
【要件定義】
1.最大６４人対戦が出来るようにしてください。

2.人間のプレイヤーの数は一人～プレイヤー全員まで自由に選択できるようにしてください。

3.プレイヤー全員のうち人間のプレイヤー以外は自動的にコンピュータを割り当ててください。

4.人間のプレイヤーに名前を付けて試合中もその名前で表示されるようにしてください。

5.試合のマッチポイントは自由に選択できるようにしてください。

6.トーナメントの各回戦の初めに全試合の対戦表を表示して分かりやすくしてください。

7.各試合の初めにその試合が「第○回戦第○試合」で「誰と誰」の対戦か表示して分かりやすくしてください。

8.試合で得点が入るたびに両者の現在のポイントを表示して進行具合を分かりやすくしてください。

9.各試合の勝者の名前を表示してください。

10．優勝者を表示してください。

11.ゲーム終了後に再度プレイするか選択できるようにしてください。
***
【機能要件】
(0.このゲームの概要とルール説明を表示する)←有無は問わない

1.４人、８人、１６人、３２人、６４人からトーナメントバトルの参加人数を選択(入力)できる機構を作成してゲームに反映できるようにしてください。

2.人間が操作するプレイヤーの人数を入力できる機構を作成してゲームに反映できるようにしてください。

3.各人間プレイヤーの名前を入力できる機構を作成してゲームに反映できるようにしてください。

4.試合のマッチポイント数を入力できる機構を作成してゲームに反出来るようにしてください。

5.トーナメントにおけるプレイヤーの組み合わせがランダムになるように設計してください。

6.トーナメントの初めに「○ポイントマッチ制 ○人トーナメントスタート」表示してください。

7.トーナメントの各回戦の最初に「第○回戦対戦表」と全試合分の「プレイヤー名 vs プレイヤー名」を出力してください。

8.各試合の最初に「第○回戦 第○試合」とその試合の「プレイヤー名 vs プレイヤー名」を出力してください。

9.最後とそのひとつ前の回戦は「第○回戦」を「決勝」「準決勝」に変換して表示してください。

10.試合のルールは、じゃんけんに勝った人が指の向きを、じゃんけんに負けた人が顔の向きを選択し、同じ向きであった場合じゃんけんの勝者に勝利ポイントが入ります。
あっち向いてほいでお互いが選択した向きが異なる場合は再びじゃんけんからやり直すように設計してください。ポイントのマイナスはありません。

11.試合中どちらかがポイントを獲得したタイミングで両者の現在のポイントを表示するようにしてください。

12.決勝で勝ち残ったプレイヤーの名前を表示して表彰してください。

13.再度プレイするか選択できる機構を作成してください。
***










###〔お助けヒント〕
<details><summary><font color="DarkMagenta">**ヒント１**</font></summary>
プレイヤーの<font color="OliveDrab">**インスタンス**</font>は一つ一つ変数に代入せずに配列にまとめて格納し、取り出す場合はfor文を使うようにすると便利です。</details>

<details><summary><font color="DarkMagenta">**ヒント２**</font></summary>
新たに<font color="81C4FF">**クラス**</font>を作成し初期化<font color="0C6CCC">**メソッド**</font>でプレイヤーの<font color="OliveDrab">**インスタンス**</font>の配列を<font color="OliveDrab">**インスタンス**</font><font color="5008CC">**変数**</font>に代入します。そして、試合の一連の流れを実行できる<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>たちを作成して<font color="81C4FF">**クラス**</font>にまとめるとあっち向いてほいプログラムが作りやすくなります。</details>

<details><summary><font color="DarkMagenta">**ヒント３**</font></summary>
このプログラムを要約すると「for文で１回戦～決勝まで回す中で更にfor文で第一試合～最終試合まで回し、その中で更にwhile文でどちらかがマッチポイントを獲るまでじゃんけんとあっち向いてほいを繰り返すプログラム」です。こんがらがったらまずは図示！</details>

<details><summary><font color="DarkMagenta">**ヒント４**</font></summary>
<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>内で<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を実行するネスト構造を使って処理を連鎖させて、返り値はネストを遡ってくる方法を使うと便利です。</details>










##【解答】
解答は以下の通りです。
※この解答はあくまで私の回答です。各々の方法でお題が解けていればそれで全然かまいません。むしろ、もっと簡潔な方法があればご教示頂けると助かります:bow_tone1:

```py
import random

#スコアシートクラス(プレイヤーの名前を保持、プレイヤーの得点を保持)
class Scoresheet:

  def __init__(self, names, score=0):
    self.names = names
    self.score = score


#ヒューマンクラス(じゃんけんの手の選択、あっち向いてほいの手の選択)
class Human(Scoresheet):

  ##人間プレイヤーの手を選択
  def pon(self):
    print('選択肢:', HANDS)
    while True:
      try:
        hand = input(f"{self.names}の出す手を入力してください(整数：1, 2, 3) 〉")
        if hand in ("1", "2", "3"):
            self.hand = HANDS[int(hand)]
            return
      except:
         print("整数：1, 2, 3を入力してください。")

  ##人間プレイヤーの指か顔の向きを選択
  def hoi(self, j_result):
    if j_result == "w":
      print(FINGER)
      while True:
        try:
          finger = input("あなたの指の向きを選択してください(整数：1,2,3,4) 〉")
          if finger in ("1","2","3","4"):
            self.finger = FINGER[int(finger)]
            return
        except:
          print("整数1,2,3,4を入力してください")  
    else:
      while True:
        try:
          face = input("あなたの顔の向きを選択してください(整数：1,2,3,4) 〉")
          if face in ("1","2","3","4"):
            self.face = FACE[int(face)]
            return
        except:
          print("整数1,2,3,4を入力して下さい")


#コンピュータクラス(じゃんけんの手の選択、あっち向いてほいの手の選択)
class Computer(Scoresheet):

  ##コンピュータの手を選択
  def pon(self):
    self.hand = random.choice(list(HANDS.values()))

  ##コンピュータの指か顔の向きを選択
  def hoi(self, j_result):
    if j_result == "w":
        self.finger = random.choice(list(FINGER.values()))
    else:
        self.face = random.choice(list(FACE.values()))


#トーナメントクラス(試合のマッチング、各回戦の一連の試合を実行し敗者を選出、各回戦のスコアリセット)
class Tournament:

  def __init__(self, players, tournament_num=None):
    self.players = players
    self.tournament_num = tournament_num
  
  ##プレイヤーの順番をランダムにシャッフル
  def shuffle(self):  
      random_list = [player for player in self.players]
      random.shuffle(random_list)
      return random_list

  ##2人ずつのグループに分けて対戦表を作成＆出力
  def match(self):
    game_num = int(len(self.players) / 2)

    print("-"*35)
    if game_num == 1:
      print("決勝対戦表")
    elif game_num == 2:
      print("準決勝対戦表")
    else:
      print(f"第{self.tournament_num}回戦対戦表\n")

    for n in range(0, game_num):
      group_n = self.players[n*2:n*2+2] 
      print(f"{group_n[0].names} vs {group_n[1].names}")
    print("-"*35)
    return game_num
  
  ##第〇回戦の全試合を実施して負けた参加者を配列(リスト型)に格納
  def battle(self, game_num, match_point):
    losers_list = []
    for n in range(0, game_num):
      group_n = self.players[n*2:n*2+2] 
      print("-"*35)
      if game_num == 1:
        print("決勝戦")
      elif game_num == 2:
        print(f"準決勝戦 第{n+1}試合")
      else:
        print(f"第{self.tournament_num}回戦 第{n+1}試合")
      print(f"\n{group_n[0].names} vs {group_n[1].names}")
      print("-"*35)
      while True:
        loser = Janken(group_n).play(match_point)
        if loser:
          losers_list.append(loser)
          break
    return losers_list

  ##スコアリセット
  def score_reset(self):
    for player in self.players:
      player.score = 0


#じゃんけんクラス(じゃんけんをする際の手の選択と出力、判定、ポイントの加算と出力)
class Janken:

  def __init__(self, group_n):
    self.group_n = group_n

  ##じゃんけんの一連の流れを実行
  def play(self, match_point):
    print("\n>>>じゃんけんぽん！<<<")
    self.pon()
    while True:
      j_winner, j_loser = self.judge()           
      if j_winner:
        print(f"\n\n◎◎{j_winner.names}の勝ち！◎◎")
        break
      else:  
        print("\n△△あいこでしょ！！△△")
        self.pon()
    return Hoi(j_winner, j_loser).play(self.group_n, match_point)
  
  ##ヒューマンクラスとコンピュータクラスのponインスタンスメソッドを実行して各参加者のじゃんけんの手を出力 
  def pon(self):
    for player in self.group_n:
      player.pon()
    for player in self.group_n:
      print(f"{player.names}の手：{player.hand}")

  ##プレイヤーのじゃんけんの手があいこか否かを判定、あいこでなければ勝者を決定
  def judge(self):
    hands = set(player.hand for player in self.group_n)
    if len(hands) != 2:
      return None, None
    hand1, hand2 = hands
    win = hand1 if hand1.stronger_than(hand2) else hand2
    for player in self.group_n:
      if player.hand == win:
        winner = player 
      else:
        loser = player
    return winner, loser


#あっち向いてほいクラス(あっち向いてほいをする際の手と指の選択＆出力、勝敗判定)
class Hoi:

  def __init__(self, j_winner, j_loser):
    self.j_winner = j_winner
    self.j_loser = j_loser
    

  ##あっち向いてほいをプレイして勝者を出力し敗者を返り値で返す
  def play(self, group_n, match_point):
    print("\n\n>>>あっち向いてほい！<<<")
    self.hoi()
    if self.j_winner.finger == self.j_loser.face:
      print(f"\n\n◎◎◎{self.j_winner.names}の勝利！1ポイント獲得！◎◎◎")
      self.j_winner.score += 1
      if self.j_winner.score < match_point:
        print(f"\n\n現在のポイント \
              \n{group_n[0].names}:{group_n[0].score}ポイント \
              \n{group_n[1].names}:{group_n[1].score}ポイント")
        return
      else:
        print(f"\n\n><><><>{match_point}ポイント先取により{self.j_winner.names}の勝利！<><><><\n\n")
        return self.j_loser        
    else:
      print("××じゃんけんに戻る××")
      return

  ##プレイヤー同士の出した指と顔の向きを出力
  def hoi(self):
    self.j_winner.hoi("w")
    self.j_loser.hoi("l")
    print(f"{self.j_winner.names}の手:{self.j_winner.finger}")
    print(f"{self.j_loser.names}の手:{self.j_loser.face}")

#ハンドクラス(判定で2つの手の勝敗を決定) 
class Hand:

  def __init__(self, shape, stronger_than):
      self.shape = shape
      self._stronger_than = stronger_than

  ##デバッグ時にインスタンスを指定の形で出力する用
  def __repr__(self):
      return repr(self.shape)

  ##インスタンスをstr型で出力する際にself.shapeを出力
  def __str__(self):
      return self.shape
  
  ##2つの手のうち、一方の手が勝つ時の相手の手と実際の相手の手が同じだという返り値を返す
  def stronger_than(self, enemy):
      return self._stronger_than == enemy.shape


#じゃんけんの手、あっち向いてほいの顔、あっち向いてほいの指の辞書
HANDS = {1: Hand("グー", "チョキ"),
         2: Hand("チョキ", "パー"),
         3: Hand("パー", "グー")}
FACE = {1:"↑",2:"→",3:"↓",4:"←"}
FINGER = {1:"↑",2:"→",3:"↓",4:"←"}


#あっち向いてほいバトルグラウンドをスタート
def hoi_battle_ground():

  print("="*30 + "\n\nあっち向いてほいBATTLEGROUNDS\n\n\n       >>GAMESTART<<\n\n          Press A\n\n" + "="*30)
  while True:
    Button = input()
    if Button == "A":
      break

  print("\n\n\n\n\n" + "="*60 + "\n\n        あっち向いてほいBATTLEGROUNDSへようこそ！" \
  + "\n\n\n    このゲームでは最大６４人でトーナメント戦を行います。" \
  + "\n                    ルールは単純です。" \
  + "\n  あっち向いてほいで決められた点数を先に獲得したら勝利です。" \
  + "\n\n\n   トーナメントを勝ち抜いて優勝を目指して頑張りましょう！\n\n " + "="*60
  )

  print("\n\n\n\n\n" + "="*60 + "\n\nあっち向いてほいBATTLEGROUNDSの設定を決めてください")

  ##トーナメントプレイ人数を選択
  while True:
    try :
      all_player_num = int(input("\n\nプレイ人数を4,8,16,32,64人から選択してください >"))
      if all_player_num in [4, 8, 16, 32, 64]:
        break
    except:
      print("4,8,16,32,64を入力してください。")

  ##人間プレイヤーの人数を選択  
  while True:
    try:
      player_num = int(input(f"\n\n人間プレイヤーの人数を選択してください(最大人数は{all_player_num}人です) >"))
      if 1 <= player_num & player_num <= all_player_num:
        break
    except:
      print("数字以外を入力しないでください。")

  ##各人間プレイヤーの名前の入力
  player_name_list =[]
  for _ in range(0, player_num):
    player_name = input("\n\n人間プレイヤーの名前を記入してください。>")   
    player_name_list.append(player_name)
    print(f"<{player_name}>さんですね。エントリー完了しました。")

  ##試合のマッチポイントの入力
  while True:
    try:
      match_point = int(input(f"\n\n試合のマッチポイントを入力してください。 >"))
      if match_point >= 1:
        break
    except:
      print("1以上の整数を入力してください。")

  print("\n" + "="*60 + "\n\n\n\n\n")

  ##プレイヤーのインスタンスを配列(タプル型)に格納
  com_num = all_player_num - player_num
  players = *[Human(i) for i in player_name_list], *[Computer(f"コンピュータ{i}") for i in range(1, com_num + 1)]

  ##プレイヤーのインスタンスをシャッフルして配列(リスト型)に格納
  random_list = Tournament(players).shuffle()

  ##トーナメントが何回戦まであるか算出
  tournament_num = [i for i in range(1, all_player_num + 1) if all_player_num % i == 0]

  ##試合開始
  print(f"\n\n\n{match_point}ポイントマッチ制 {all_player_num}人トーナメントスタート！！\n\n")
  for n in range(1, len(tournament_num)):
      ###各2人組に分けた際の試合数
      game_num = Tournament(random_list, n).match()
      ###プレイヤーのリストから試合の敗者を除外
      losers_list = Tournament(random_list, n).battle(game_num, match_point)
      for loser in losers_list:
        random_list.remove(loser)
        if len(random_list) == 1:
          print(" " + "+*"*33) 
          print("*+"*34)
          print(f"     栄えある優勝者は{random_list[0].names}！！おめでとうございます！！！")
          print("*+"*34)
          print(" " + "+*"*33)
      ###勝ち残った参加者の勝利ポイントをリセット
      Tournament(random_list).score_reset()
          
  ##もう一度プレイするか選択
  while True:
    try:
      restart = input("\n\nもう一度プレイしますか？(Yes:y/No:n)")
      if restart == "y":
        hoi_battle_ground()
        break
      elif restart == "n":
        break
    except:
      print("yかnを入力してください")

#あっち向いてほいグラウンドメソッドを実行します。
  hoi_battle_ground()
```









##【解説】










###〔作り方〕










####<font color="DarkMagenta">**①各種ゲーム設定を入力できる機構を作成**</font>

⑴プレイヤー人数入力機構

```py
  ##トーナメントプレイ人数を選択
  while True:
    try :
      all_player_num = int(input("\n\nプレイ人数を4,8,16,32,64人から選択してください >"))
      if all_player_num in [4, 8, 16, 32, 64]:
        break
    except:
      print("4,8,16,32,64を入力してください。")
```
⑵人間プレイヤー人数入力機構

```py
  ##人間プレイヤーの人数を選択  
  while True:
    try:
      player_num = int(input(f"\n\n人間プレイヤーの人数を選択してください(最大人数は{all_player_num}人です) >"))
      if 1 <= player_num & player_num <= all_player_num:
        break
    except:
      print("数字以外を入力しないでください。")
```
⑶各人間プレイヤーの名前入力機構

```py
  ##各人間プレイヤーの名前の入力
  player_name_list =[]
  for _ in range(0, player_num):
    player_name = input("\n\n人間プレイヤーの名前を記入してください。>")   
    player_name_list.append(player_name)
    print(f"<{player_name}>さんですね。エントリー完了しました。")
```
⑷試合のマッチポイント入力機構

```py
  ##試合のマッチポイントの入力
  while True:
    try:
      match_point = int(input(f"\n\n試合のマッチポイントを入力してください。 >"))
      if match_point >= 1:
        break
    except:
      print("1以上の整数を入力してください。")
```










####<font color="DarkMagenta">**②プレイヤーのインスタンスを配列(タプル)に格納**</font>

⑴プレイヤーの<font color="OliveDrab">**インスタンス**</font>を配列に格納してまとめて扱いやすくする

```py
  ##プレイヤーとコンピュータのインスタンスを配列(タプル型)に格納
  com_num = all_player_num - player_num
  players = *[Human(i) for i in player_name_list], *[Computer(f"コンピュータ{i}") for i in range(1, com_num + 1)]

"""
playersの中身(player:3, computer:5)
(<__main__.Human object at 0x7fd29607cad0>, <__main__.Human object at 0x7fd29607cc90>,
 <__main__.Human object at 0x7fd29607c2d0>, <__main__.Computer object at 0x7fd2960cd050>,
 <__main__.Computer object at 0x7fd29607cc50>, <__main__.Computer object at 0x7fd29607cb10>,
 <__main__.Computer object at 0x7fd29607c8d0>, <__main__.Computer object at 0x7fd29607ce90>)
"""
```
詳しい解説は前回の[「③参加者のインスタンス配列を受け取りじゃんけんを実行するJankenクラスを作成」](https://qiita.com/Nuka-san-Engineering/items/f94c441c9750656b7f70#%E5%8F%82%E5%8A%A0%E8%80%85%E3%81%8C%E4%BD%95%E4%BA%BA%E3%81%A7%E3%82%82%E5%87%A6%E7%90%86%E3%81%97%E3%82%84%E3%81%99%E3%81%84%E3%82%88%E3%81%86%E3%81%AB%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%82%92%E9%85%8D%E5%88%97%E3%82%BF%E3%83%97%E3%83%AB%E3%81%AB%E6%A0%BC%E7%B4%8D)をご覧ください。










####<font color="DarkMagenta">**③各回戦の結果から優勝者を出力する機構を作成**</font>

⑴各回戦の敗者をプレイヤーリストから除き、最後の一人を優勝者として出力する機構

```py
  ##トーナメントが何回戦まであるか算出(総プレイヤー数の約数-1)
  tournament_num = [i for i in range(1, all_player_num + 1) if all_player_num % i == 0]

  ##試合開始
  print(f"\n\n\n{match_point}ポイントマッチ制 {all_player_num}人トーナメントスタート！！\n\n")
  for n in range(1, len(tournament_num)):
      ###各2人組に分けた際の試合数
      game_num = Tournament(random_list, n).match()
      ###プレイヤーのリストから試合の敗者を除外
      losers_list = Tournament(random_list, n).battle(game_num, match_point)
      for loser in losers_list:
        random_list.remove(loser)
        if len(random_list) == 1:
          print(" " + "+*"*33) 
          print("*+"*34)
          print(f"     栄えある優勝者は{random_list[0].names}！！おめでとうございます！！！")
          print("*+"*34)
          print(" " + "+*"*33)
      ###勝ち残った参加者の勝利ポイントをリセット
      Tournament(random_list).score_reset()
```
この機構では、総プレイヤー人数からトーナメントが何回戦まであるか算出しその回数分for文を回します。各回戦ごとに`losers_list`に敗者の<font color="OliveDrab">**インスタンス**</font>を格納して`random_list`から`remove`<font color="0C6CCC">**メソッド**</font>を使って除いていきます。
敗者を取り除いた`random_list`にプレイヤーの<font color="OliveDrab">**インスタンス**</font>が複数残っている場合はfor文で次回戦に移ります。その際、前回戦で獲得したスコアを０ポイントにリセットします。
<font color="OliveDrab">**インスタンス**</font>が１つしか残っていない場合はその<font color="OliveDrab">**インスタンス**</font>を優勝者として名前を出力します。

`Tournament`<font color="81C4FF">**クラス**</font>の<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>が何をしているかについては③で詳しく解説します。

トーナメントが何回戦まであるか算出する方法
総プレイヤー数を1～総プレイヤー数で割って整数で割り切れる回数-1 == 約数-1
16人の場合
16/1,16/2,16/4,16/8,16/16 →４回戦まで
32人の場合
32/1,32/2,32/4,32/8,32/16,32/32 →５回戦まで

![あっち向いてほいプログラムトーナメント表.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668448/5c282fcc-2c0e-32e4-230a-f5c9ea555146.png)










####<font color="DarkMagenta">**④プレイヤーのインスタンス配列を受け取りトーナメントの各試合を実行するTournamentクラスを作成**</font>

⑴初期化<font color="0C6CCC">**メソッド**</font>を作成

```py
#トーナメントクラス(試合のマッチング、各回戦の一連の試合を実行し敗者を選出、各回戦のスコアリセット)
class Tournament:

  def __init__(self, players, tournament_num=None):
    self.players = players
    self.tournament_num = tournament_num
```
この`Tournament`<font color="81C4FF">**クラス**</font>は初期化<font color="0C6CCC">**メソッド**</font>で<font color="OliveDrab">**インスタンス**</font><font color="5008CC">**変数**</font>`self.players`に全プレイヤーの<font color="OliveDrab">**インスタンス**</font>をまとめて代入します。
前回は`Janken`<font color="81C4FF">**クラス**</font>がこの役割を果たしていましたが、今回はこの`Tournament`<font color="81C4FF">**クラス**</font>が担っています。全プレイヤーを扱う<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を作成して有効的に<font color="OliveDrab">**インスタンス**</font><font color="5008CC">**変数**</font>`self.players`を使っていきます。

⑵プレイヤーの<font color="OliveDrab">**インスタンス**</font>配列の順序をランダムに変更する`shuffle`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を作成

```py
  ##プレイヤーの順番をランダムにシャッフル
  def shuffle(self):  
      random_list = [player for player in self.players]
      random.shuffle(random_list)
      return random_list

  ##プレイヤーのインスタンスをシャッフルして配列(リスト型)に格納
  random_list = Tournament(players).shuffle()
```
`Tournament`<font color="81C4FF">**クラス**</font>の<font color="OliveDrab">**インスタンス**</font>作成と`shuffle` <font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>の実行を同時に行うと、`players`の中身の<font color="OliveDrab">**インスタンス**</font>がランダムにシャッフルされ`random_list`として戻ってきます。
一回トーナメントバトルが終わるまではずっとここで決まった<font color="OliveDrab">**インスタンス**</font>の組み合わせで試合が行われます。

⑶各回戦ごとにトーナメントの対戦表を出力する`match`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を作成

```py
  ##2人ずつのグループに分けて対戦表を作成＆出力
  def match(self):
    game_num = int(len(self.players) / 2)

    print("-"*35)
    if game_num == 1:
      print("決勝対戦表")
    elif game_num == 2:
      print("準決勝対戦表")
    else:
      print(f"第{self.tournament_num}回戦対戦表\n")

    for n in range(0, game_num):
      group_n = self.players[n*2:n*2+2] 
      print(f"{group_n[0].names} vs {group_n[1].names}")
    print("-"*35)
    return game_num

##試合開始
  print(f"\n\n\n{match_point}ポイントマッチ制 {all_player_num}人トーナメントスタート！！\n\n")
  for n in range(1, len(tournament_num)):
      ###各2人組に分けた際の試合数
      game_num = Tournament(random_list, n).match() #<<<<<<<<<<
```
試合開始して各回戦が始まる前に`Tournament`<font color="81C4FF">**クラス**</font>の`match`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を実行します。
この<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>は各回戦の試合数を計算し`game_num`に代入して返り値として返します。また、各回戦のトーナメント対戦表を出力してくれます。

⑷各試合の一連の流れを実行する`battle`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を作成

```py
  ##第〇回戦の全試合を実施して負けた参加者を配列(リスト型)に格納
  def battle(self, game_num, match_point):
    losers_list = []
    for n in range(0, game_num):
      group_n = self.players[n*2:n*2+2] 
      print("-"*35)
      if game_num == 1:
        print("決勝戦")
      elif game_num == 2:
        print(f"準決勝戦 第{n+1}試合")
      else:
        print(f"第{self.tournament_num}回戦 第{n+1}試合")
      print(f"\n{group_n[0].names} vs {group_n[1].names}")
      print("-"*35)
      while True:
        loser = Janken(group_n).play(match_point)
        if loser:
          losers_list.append(loser)
          break
    return losers_list

##試合開始
  print(f"\n\n\n{match_point}ポイントマッチ制 {all_player_num}人トーナメントスタート！！\n\n")
  for n in range(1, len(tournament_num)):
      ###各2人組に分けた際の試合数
      game_num = Tournament(random_list, n).match()
      ###プレイヤーのリストから試合の敗者を除外
      losers_list = Tournament(random_list, n).battle(game_num, match_point) #<<<<<<<<<<
```
試合開始して各回戦が始まると`Tournament`<font color="81C4FF">**クラス**</font>の`battle`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を実行します。この<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を実行すると、各回戦ごとの全試合から敗者をリストにして返してくれます。
`battle`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>の引数に`game_num`(試合数)と`match_point`を渡すことで、一試合`match_point`分先取制で受け取った試合数`game_num`分をfor文で繰り返します。
一試合の流れを説明すると、まずその試合が何回戦で第何試合で誰と誰が対戦しているのか出力します。
次に、`Janken`<font color="81C4FF">**クラス**</font>の`play`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を実行して返り値として敗者の<font color="OliveDrab">**インスタンス**</font>が帰ってきたらwhile文を終了して`losers_list`に敗者の<font color="OliveDrab">**インスタンス**</font>を追加します。
for文を全試合数分回したら`loser_list`を`battle`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>の返り値として返します。


⑸各回戦ごとに勝ち残ったプレイヤーの勝利ポイントを０にリセットする`score_reset`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を作成

```py
  ##スコアリセット
  def score_reset(self):
    for player in self.players:
      player.score = 0

  ##試合開始
  print(f"\n\n\n{match_point}ポイントマッチ制 {all_player_num}人トーナメントスタート！！\n\n")
  for n in range(1, len(tournament_num)):
      ###各2人組に分けた際の試合数
      game_num = Tournament(random_list, n).match()
      ###プレイヤーのリストから試合の敗者を除外
      losers_list = Tournament(random_list, n).battle(game_num, match_point)
      for loser in losers_list:
        random_list.remove(loser)
        if len(random_list) == 1:
          print(" " + "+*"*33) 
          print("*+"*34)
          print(f"     栄えある優勝者は{random_list[0].names}！！おめでとうございます！！！")
          print("*+"*34)
          print(" " + "+*"*33)
      ###勝ち残った参加者の勝利ポイントをリセット
      Tournament(random_list).score_reset() #<<<<<<<<<<
```
各回戦が終了した時点で勝ち残っているプレイヤーの勝利ポイントは全員３ポイントあります。このまま次回戦を始めてしまうと最初から３ポイント獲得した状態になってしまいます。
この問題を解決するのがこの`score_reset`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>です。
この<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を実行すると各プレイヤーの勝利ポイントに０を代入してくれます。これで問題なく次回戦に進ませることが出来ます。










####<font color="DarkMagenta">**⑤Jankenクラスを一部変更**</font>

⑴勝敗判定部分を一部変更

```py
  ##じゃんけんの一連の流れを実行
  def play(self, match_point):
    print("\n>>>じゃんけんぽん！<<<")
    self.pon()
    while True:
      ###judgeインスタンスメソッドの返り値の第一項をj_winnerに、第二項をj_loserに代入
      j_winner, j_loser = self.judge()           
      if j_winner:
        ###j_winnerがTrue、つまり勝者がいる場合
        print(f"\n\n◎◎{j_winner.names}の勝ち！◎◎")
        break
      else:
        ###いない場合はもう一度じゃんけん  
        print("\n△△あいこでしょ！！△△")
        self.pon()
    ##勝者が決まったら返り値であっち向いてほいを実行する
    return Hoi(j_winner, j_loser).play(self.group_n, match_point)


  ##プレイヤーのじゃんけんの手があいこか否かを判定、あいこでなければ勝者を決定
  def judge(self):
    hands = set(player.hand for player in self.group_n)
    if len(hands) != 2:
      return None, None
    hand1, hand2 = hands
    win = hand1 if hand1.stronger_than(hand2) else hand2
    ###二人のうち勝者はインスタンスをwinnerに代入、敗者はloserに代入する形に変更
    for player in self.group_n:
      if player.hand == win:
        winner = player 
      else:
        loser = player
    ###複数返り値
    return winner, loser
```
前回の[大人数じゃんけんプログラム](https://qiita.com/Nuka-san-Engineering/items/f94c441c9750656b7f70#%E5%8F%82%E5%8A%A0%E8%80%85%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E9%85%8D%E5%88%97%E3%82%92%E5%8F%97%E3%81%91%E5%8F%96%E3%82%8A%E3%81%98%E3%82%83%E3%82%93%E3%81%91%E3%82%93%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8Bjanken%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%92%E4%BD%9C%E6%88%90)で使用したコードを基本的には転用していますが、今回は勝者に加え敗者も明確にする必要があるので`judge`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を変更しました。
詳しくは上記のコードのメモをご覧ください。

⑵返り値で`Hoi`<font color="81C4FF">**クラス**</font>の`play`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を実行

```py
  ##じゃんけんの一連の流れを実行
  def play(self, match_point):
    print("\n>>>じゃんけんぽん！<<<")
    self.pon()
    while True:
      ###judgeインスタンスメソッドの返り値の第一項をj_winnerに、第二項をj_loserに代入
      j_winner, j_loser = self.judge()           
      if j_winner:
        ###j_winnerがTrue、つまり勝者がいる場合
        print(f"\n\n◎◎{j_winner.names}の勝ち！◎◎")
        break
      else:
        ###いない場合はもう一度じゃんけん  
        print("\n△△あいこでしょ！！△△")
        self.pon()
    ##勝者が決まったら返り値であっち向いてほいを実行する
    return Hoi(j_winner, j_loser).play(self.group_n, match_point) #<<<<<<<<<<
```
`Hoi`<font color="81C4FF">**クラス**</font>の<font color="OliveDrab">**インスタンス**</font>に`j_winner, j_loser`(勝者と敗者)を渡します。
同時に`Hoi`<font color="81C4FF">**クラス**</font>の`play`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>に引数として`self.group_n`(その試合の二人のプレイヤー)を第一項に、`match_point`を第二項に渡しています。
これであっち向いてほいを実行することができます。
詳しい意味は⑥で説明します。










####<font color="DarkMagenta">**⑥Hoiクラスを作成**</font>

⑴初期化<font color="0C6CCC">**メソッド**</font>を作成

```py
#あっち向いてほいクラス(あっち向いてほいをする際の手と指の選択＆出力、勝敗判定)
class Hoi:

  def __init__(self, j_winner, j_loser):
    self.j_winner = j_winner
    self.j_loser = j_loser
```
`Janken`<font color="81C4FF">**クラス**</font>の返り値として`Hoi`<font color="81C4FF">**クラス**</font>の<font color="OliveDrab">**インスタンス**</font>を作成しました。その際に受け取った勝者と敗者の<font color="OliveDrab">**インスタンス**</font>を初期化<font color="0C6CCC">**メソッド**</font>で`self.j_winner`、`self.j_loser`に保持します。

⑵あっち向いてほいの一連の流れを実行する`play`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を作成

```py
  ##あっち向いてほいをプレイして勝者を出力し敗者を返り値で返す
  def play(self, group_n, match_point):
    print("\n\n>>>あっち向いてほい！<<<")
    self.hoi()
    if self.j_winner.finger == self.j_loser.face:
      print(f"\n\n◎◎◎{self.j_winner.names}の勝利！1ポイント獲得！◎◎◎")
      self.j_winner.score += 1
      if self.j_winner.score < match_point:
        print(f"\n\n現在のポイント \
              \n{group_n[0].names}:{group_n[0].score}ポイント \
              \n{group_n[1].names}:{group_n[1].score}ポイント")
        return
      else:
        print(f"\n\n><><><>{match_point}ポイント先取により{self.j_winner.names}の勝利！<><><><\n\n")
        return self.j_loser        
    else:
      print("××じゃんけんに戻る××")
      return
```
この<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>ではあっち向いてほいの一連の流れを実行します。
まずは`self.hoi`を実行してプレイヤーに顔か指を選択させ出力します。
詳しい内容は⑥の⑶で解説します。
次に、じゃんけん勝者の指とじゃんけん敗者の顔の向きが同じか判定します。
向きが同じ場合はじゃんけん勝者の勝利となり、勝者の名前を出力した後にポイントを追加します。
勝利ポイントがマッチポイント未満である場合は両者の現在のポイントを出力した後に空の返り値を返します。ここで空の返り値を返すと`Janken`<font color="81C4FF">**クラス**</font>の`play`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>の返り値も空になり、`Tournament`<font color="81C4FF">**クラス**</font>の`battle`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>のwhile文から抜け出せずもう一度じゃんけんからやり直しになります。
勝利ポイントがマッチポイントまで達した場合は勝者の名前を出力した上で敗者の<font color="OliveDrab">**インスタンス**</font>を返り値として返します。敗者を受け取ることで`Tournament`<font color="81C4FF">**クラス**</font>の`battle`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>の`while`文から抜け出せて次の試合に移ることができます。 向きが異なる場合は空の返り値を返して、勝利ポイントがマッチポイント未満であった場合と同じくじゃんけんをやり直します。

![あっち向いてほいプログラムフローチャート.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668448/130f4633-ac36-b2f5-896f-7c9644ac6a69.png)

⑶プレイヤーに顔か指を選択させ出力する`hoi`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を作成

```py
  ##プレイヤー同士の出した指と顔の向きを出力
  def hoi(self):
    self.j_winner.hoi("w")
    self.j_loser.hoi("l")
    print(f"{self.j_winner.names}の手:{self.j_winner.finger}")
    print(f"{self.j_loser.names}の手:{self.j_loser.face}")
```
この<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>ではプレイヤーにあっち向いてほいの顔か指を選択させます。
`Human`<font color="81C4FF">**クラス**</font>の<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>`self.j_winner.hoi()`と`Computer`<font color="81C4FF">**クラス**</font>の<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>`self.j_loser.hoi()`を実行することで選択してもらいます。引数で"ｗ"と"ｌ"を渡しているのは、`Human`<font color="81C4FF">**クラス**</font>と`Computer`<font color="81C4FF">**クラス**</font>側で顔の選択か指の選択か場合分けを行う為です。詳しくは⑦の⑴と⑵でコードを確認してみてください。










####<font color="DarkMagenta">**⑦HumanクラスとComputerクラスを一部変更**</font>

⑴ヒューマン<font color="81C4FF">**クラス**</font>に`hoi`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を作成

```py
#ヒューマンクラス(じゃんけんの手の選択、あっち向いてほいの手の選択)
class Human(Scoresheet):

  ##人間プレイヤーの手を選択
  def pon(self):
    print('選択肢:', HANDS)
    while True:
      try:
        hand = input(f"{self.names}の出す手を入力してください(整数：1, 2, 3) 〉")
        if hand in ("1", "2", "3"):
            self.hand = HANDS[int(hand)]
            return
      except:
         print("整数：1, 2, 3を入力してください。")

  ##人間プレイヤーの指か顔の向きを選択
  def hoi(self, j_result):
    if j_result == "w":
      print(FINGER)
      while True:
        try:
          finger = input("あなたの指の向きを選択してください(整数：1,2,3,4) 〉")
          if finger in ("1","2","3","4"):
            self.finger = FINGER[int(finger)]
            return
        except:
          print("整数1,2,3,4を入力してください")  
    else:
      while True:
        try:
          face = input("あなたの顔の向きを選択してください(整数：1,2,3,4) 〉")
          if face in ("1","2","3","4"):
            self.face = FACE[int(face)]
            return
        except:
          print("整数1,2,3,4を入力して下さい")

FACE = {1:"↑",2:"→",3:"↓",4:"←"}
FINGER = {1:"↑",2:"→",3:"↓",4:"←"}
```
`Human`<font color="81C4FF">**クラス**</font>に`hoi`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を追加します。
`Hoi`<font color="81C4FF">**クラス**</font>の`hoi`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>で渡された引数"w"か"l"によって顔の選択か指の選択か場合分けを行います。指は`self.finger`に、顔は`self.face`に代入します。

⑵コンピュータ<font color="81C4FF">**クラス**</font>に`hoi`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を作成

```py
#コンピュータクラス(じゃんけんの手の選択、あっち向いてほいの手の選択)
class Computer(Scoresheet):

  ##コンピュータの手を選択
  def pon(self):
    self.hand = random.choice(list(HANDS.values()))

  ##コンピュータの指か顔の向きを選択
  def hoi(self, j_result):
    if j_result == "w":
        self.finger = random.choice(list(FINGER.values()))
    else:
        self.face = random.choice(list(FACE.values()))

FACE = {1:"↑",2:"→",3:"↓",4:"←"}
FINGER = {1:"↑",2:"→",3:"↓",4:"←"}
```
`Computer`<font color="81C4FF">**クラス**</font>に`hoi`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を追加します。
`Hoi`<font color="81C4FF">**クラス**</font>の`hoi`<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>で渡された引数"w"か"l"によって顔の選択か指の選択か場合分けを行います。指は`self.finger`に、顔は`self.face`に代入します。










####<font color="DarkMagenta">**⑧スーパークラスScoresheetを作成**</font>

⑴`Human`<font color="81C4FF">**クラス**</font>と`Computer`<font color="81C4FF">**クラス**</font>に共通する「属性」である"名前"と"勝利ポイント"をスーパー<font color="81C4FF">**クラス**</font>の<font color="OliveDrab">**インスタンス**</font><font color="5008CC">**変数**</font>に保持させる

```py
#スコアシートクラス(プレイヤーの名前を保持、プレイヤーの得点を保持)
class Scoresheet:

  def __init__(self, names, score=0):
    self.names = names
    self.score = score
```
`Human`<font color="81C4FF">**クラス**</font>と`Computer`<font color="81C4FF">**クラス**</font>に共通してある「属性」は、"名前"と"勝利ポイント"です。前回の[「②ヒューマンクラスとコンピュータクラスに名前と勝利ポイントを受け取る初期化メソッドを追加」](https://qiita.com/Nuka-san-Engineering/items/f94c441c9750656b7f70#%E3%83%92%E3%83%A5%E3%83%BC%E3%83%9E%E3%83%B3%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%A8%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF%E3%82%AF%E3%83%A9%E3%82%B9%E3%81%AB%E5%90%8D%E5%89%8D%E3%81%A8%E5%8B%9D%E5%88%A9%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88%E3%82%92%E5%8F%97%E3%81%91%E5%8F%96%E3%82%8B%E5%88%9D%E6%9C%9F%E5%8C%96%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%82%92%E8%BF%BD%E5%8A%A0)では`Human`<font color="81C4FF">**クラス**</font>と`Computer`<font color="81C4FF">**クラス**</font>にそれぞれ初期化<font color="0C6CCC">**メソッド**</font>で"名前"と"勝利ポイント"を保持していたのですが、今回は`Scoresheet`スーパー<font color="81C4FF">**クラス**</font>を使います。`Scoresheet`<font color="81C4FF">**クラス**</font>は`Human`<font color="81C4FF">**クラス**</font>や`Computer`<font color="81C4FF">**クラス**</font>を包含する関係にあるため、プレイヤーの”名前”と”勝利ポイント”をまとめて全員分一つの<font color="81C4FF">**クラス**</font>の初期化<font color="0C6CCC">**メソッド**</font>に保持しておくと関係が分かりやすいです。また、後々全員の「属性」に対してまとめて処理を施したいときに役立ちます。

**<font color="81C4FF">クラス</font>の継承の基礎について**
既存の<font color="81C4FF">**クラス**</font>Aの<font color="OliveDrab">**インスタンス**</font><font color="5008CC">**変数**</font>や<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>を新たに作った<font color="81C4FF">**クラス**</font>Bが引き継いで使用することができます。この仕組みを継承と言います。
継承される<font color="81C4FF">**クラス**</font>を「スーパー<font color="81C4FF">**クラス**</font>」「親<font color="81C4FF">**クラス**</font>」「基底<font color="81C4FF">**クラス**</font>」、
継承する<font color="81C4FF">**クラス**</font>を「サブ<font color="81C4FF">**クラス**</font>」「子<font color="81C4FF">**クラス**</font>」「派生<font color="81C4FF">**クラス**</font>」などと言います。
※今後は「スーパー<font color="81C4FF">**クラス**</font>/サブ<font color="81C4FF">**クラス**</font>」を使って説明していきます。
![あっち向いてほいプログラム 継承.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1668448/eafdff57-dff7-24f5-119f-df67b39f28a4.png)

**オーバーライドについて**
スーパー<font color="81C4FF">**クラス**</font>に既存の<font color="OliveDrab">**インスタンス**</font><font color="5008CC">**変数**</font>や<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>をサブ<font color="81C4FF">**クラス**</font>で新たに書き換えたい場合に行うのがオーバーライドです。
だた、書き換えた後に元の<font color="OliveDrab">**インスタンス**</font><font color="0C6CCC">**メソッド**</font>や<font color="OliveDrab">**インスタンス**</font><font color="5008CC">**変数**</font>を使いたい場合があります。そんな時に使うのが`super()`です。
**<font color="OliveDrab">インスタンス</font><font color="0C6CCC">メソッド</font>の場合**
`super().スーパー<font color="81C4FF">**クラス**</font>のインスタンスメソッド名()`
**<font color="OliveDrab">インスタンス</font><font color="5008CC">変数</font>の場合**
`def __init__(self, スーパークラスの初期化メソッドの引数, サブクラスの初期化メソッドの引数)：`
`super().__init__(スーパークラスの初期化メソッドの引数)`

このようにしてサブ<font color="81C4FF">**クラス**</font>で書き換えたものと元のスーパー<font color="81C4FF">**クラス**</font>のもの両方使えるようになります。













####<font color="DarkMagenta">**⑨再度プレイするか選択させる機構を作成**</font>

⑴再度プレイ機構

```py
  ##もう一度プレイするか選択
  while True:
    try:
      restart = input("\n\nもう一度プレイしますか？(Yes:y/No:n)")
      if restart == "y":
        hoi_battle_ground()
        break
      elif restart == "n":
        break
    except:
      print("yかnを入力してください")
```









#【終わりに】
今回は<font color="Coral">**あっち向いてほいプログラム**</font>を作っていきました。
難易度はいかがだったでしょうか。機能要件はいかがだったでしょうか。解説はいかがだったでしょうか。なるべく正確かつ分かりやすい解説に努めましたが、もしもコーディングの部分で間違いなどありましたら編集リクエストからご指摘いただけると幸いです。また、その他質問などございましたらコメントをお願い致します。

今回作ったプログラムで地味に大変だったのが、出力の見やすさを確保するために`print`での改行や装飾の調整をすることでした。もっと見やすい形で出力できないかなと考えていた時に出会ったのが<font color="Coral">**ＧＵＩ**</font>でした。<font color="Coral">**ＧＵＩ**</font>は<font color="Coral">**Graphical User Interface**</font>と言って、ユーザーが見やすいようにウィンドウ上で図やボタンなどを表示して選択や操作ができるインターフェイスのことです。私たちが利用しているユーザー向けアプリのほとんどはこのＧＵＩを使って使いやすさを実現しているようです。
ということで次回は、今回作ったあっち向いてほいプログラムから<font color="Coral">**「あっち向いてほいプログラム」のＧＵＩアプリ**</font>を作りたいと思います。下記リンクからどうぞ！
→[ぬかさんエンジニアリングの次回作をお待ちください]()

この記事が良かったと感じたら<font color="LimeGreen">**LGTM**</font>を宜しくお願いします！それではまた次回！:wave: