簡単なGameを通してコードの組み立て方Study

#Pythonの学習例題”Hangman game”
を通して、ある程度目的を持ったひとまとまりなコードの書き方を学んでいこうと思います。

さて”Hangman game”は、いわゆる文字当てゲームですね。それを踏まえて、例題コードをとりあえず書いてみます。

##例題のコード
<details><summary>sample code</summary><div>

```python
#Hello _hangman study

def hangman(word):
    wrong = 0
    stages = ["",
                    "-----",
                    "    |",
                    "    |",
                    "    O",
                    "    O",
                    "    O",
                    "    P",
                    "    S",
                    "    !"
                    ]
    rletters = list(word)
    board = ["_"] * len(word)
    win = False
    print ("Wellcome to The  Hangman")
    
    while wrong < len(stages):
        print("\n")
        msg = "Guess a alphabet   :"
        char = input (msg)
        if char in rletters:
            cind = rletters.index(char)
            board[cind] = char
            rletters[cind] = "$"
        else:
            wrong += 1
        print (" ".join(board))
        e = wrong + 1
        print ("\n".join(stages[0:e]))
        if "_" not in board:
            print ("You Win !!")
            print ("".join(board))
            win = True
            break
    if not win:
        print ("\n".join(stages[0:wrong]))
        print ("You Lose... Answer is {}.".format(word))
        
hangman("catch")
```
</div></details>
##とりあえず、走りました。
さて、このコードを理解するため、この仕様を書き出していこうと思います。
【個人的に吊り下げるものを変えています】

##このゲーム実装の条件
　1.ユーザーに一文字選ばせる
　2.正解とヒントを案内する
　3.”GAME OVER”までのカウントを表示する
　4.当然、正解と間違いを判定する
以上でしょうか。

##さて、コードを見ていくと、
条件を満たすために変数と処理がなされていることが読み取れました。順番にみて理解していこうと思います。

***wrong:***ユーザーの間違え回数ですね。当然初期値はゼロです。
***stages:***GAME OVERまでのカウントを視覚化するため、リストの列つかって進み具合でカウントしています。
***rletters:***ユーザーが選択したAlphabetが答えに含まれているか判断しやすいように、文字列を分解してリスト化しています。
***board:***GAME OVERまでのカウント数を予め宣言せず、stagesの数をlen関数で長さとして数えています。　また、最初はユーザーにヒントとして、文字数のみ視覚化するための”＿”をその数だけ格納していますね。　ここらへんが表示とカウントを同一のもので扱うテクニックな感じがします。

***さて次に***
 "msg"に質問の文字列を入れて、次のユーザー入力変数"char"に入れています。　これは一行でも書けますが、もっと複雑なコードを書くときの、処理と変数の分理化する考えとしてわかり良いと思います。

```python
if char in rletters:
```
にて、ユーザー入力文字がリスト化した"rletters"の中に無いかを順番に確認してます。

```python
cind = rletters.index(char)
```
このindexメソッドを使い、rlettersの中の何番目にユーザー文字があったかを格納。　それを使ってboardリストの対応する場所の文字のみを更新しています。　あと、同じ文字が複数ある場合の対策として、rlettersリストの正解した文字のみ代替え文字”＄”を代替えしています。

あとはelseで間違えた場合のwrongインクリメントです。　ここまででゲームの処理が終わっており、あとは表示処理になりますね。

```python
print (" ".join(board))
```
では、ヒントの状態表示。一文字毎に空白を開けることで文字数がわかりやすくなります。　joinメソッドで~~リストを順番にすべて~~リストを連結して表示しています。【更新】

```python
print ("\n".join(stages[0:e]))
```
これで、間違えた回数（GAME OVERまでのカウント）分のステージ進捗を表示します。　直前でe=wrong + 1しているのはスタートがゼロだからですね。

次に

```python
if "_" not in board:
```
で、アンダーバー、つまり未回答文字が残っていなければすべての文字を当てているため、ユーザーの勝ちです。　勝ち後の正解文字表示がまだ無いので次の***"print("".join(board))"***で表示させ、winに勝ちをTrueにし、ループを抜けます。
　ループの成立条件がstagesつまりGAME OVERまでのカウント以下にしているので、それを超えて"if not win"で初期値Falseのままならユーザーの負けです。　負けの表示を次のコードで出します。

```python
print("\n".join(stages[0:wrong]))
```
~~これは最終行の表示ですね。~~
これは二重表示になりますね。　不要なので手直し時に消します。【更新】


```python
print("You lose... {}".format(word))
```
この"{}"が文字列中の置き換え文字で、後のformatメソッドでその文字列が指定されています。

最後にゲームの本体を呼び出してスタート（終わり）ですね。

##次に、追加課題の”問題の文字をランダムにする”を考えてみます。

##追加条件
　1.Hangmanの指定文字から、ある程度複数の文字リストを持っている。
　2.答えはリストのなかからランダムで選ばれる。
ですね。

##では、追加部分のコードを考えてみます。

```python
import random
def hangman_random():
    words=["cat","catch","hat","egg","apple","orange"]
    word=words[0:random.randint(0,5)]
    hangman(word)

hangman_random()
```

##実行結果

Answer is ["cat","catch","hat","egg","apple","orange"]

##む！？
ダメですね。 あ、わかりました。リストを文字列にしていませんね。ここはjoinメソッドをつかって、

```python
word="".join(words[0:random.randint(1,4)])
```

うまくいった・・・？
~~いや、何個か文字が連結されています。連結数もランダムです。　これはランダムオブジェクトの使い方が間違えていますね。~~
そもそもjoinメソッドは連結なので、上記だと0からランダム（１～４）を連結して格納になるので根本的に間違いでした。【更新】

##調べます。

また自分のなんとなく調べて使ってみる悪い癖が原因でした。
今回適切なメソッドは~~"choices"~~"***choice***" これで関数指定したリストの中でランダムチョイスしてくれます。

また、当初使いたかった、randintメソッドですがそれで書くなら

```python
def hangman_random():
    words=["cat","catch","hat","egg","apple","orange"]
    i=len(words) -1
    word=words[random.randint(0,i)]
    hangman(word)
```
になるかな？
無駄に長くなるので今回はchoice()を使います。【更新】

##では最終完成形
<details><summary>Final code</summary><div>

```python
#Hello _hangman study
import random

def hangman(word):
    wrong = 0
    stages = ["",
                    "-----",
                    "    |",
                    "    |",
                    "    O",
                    "    O",
                    "    O",
                    "    P",
                    "    S",
                    "    !"
                    ]
    rletters = list(word)
    board = ["_"] * len(word)
    win = False
    print ("Wellcome to The  Hangman")
    
    while wrong < len(stages):
        print("\n")
        msg = "Guess a alphabet   :"
        char = input (msg)
        if char in rletters:
            cind = rletters.index(char)
            board[cind] = char
            rletters[cind] = "$"
        else:
            wrong += 1
        print (" ".join(board))
        print ("\n".join(stages[0:wrong + 1]))
        if "_" not in board:
            print ("You Win !!")
            print ("".join(board))
            win = True
            break
    if not win:
        print ("You Lose... Answer is {}.".format(word))
        
def hangman_random():
    words = ["Panasonic", "SHARP", "TOSHIBA", "SONY", "Mitsubishi","Hitachi","NEC"]
    word = "".join(random.choice(words))
    hangman(word)

hangman_random()
```
</div></details>
これで意図した動作になりました。
ここまでの組み込み関数のおさらいから、目的にあわせた追加関数とその扱い方など理解が少しできたかなと思います。

やはりできるだけ一つ一つの動作を追っていくと動作理解が進みます。

使用教材：独学プログラマー　Python言語の基礎から仕事のやり方まで（コーリー・アルソフ）

修正更新中いただいたコメントですが、PEP8な書き方が身についていない自分にとってはとても貴重なので、参考に自己学習させて頂こうと思います。【更新】

***Mahalo!***