みなくんの日記

やる気が皆無で自由気ままに生きてる人のブログ

プログラムよ、とにかく動け ~4桁のヒット&ブローゲーム「4Numbers」を作ろう~その1

今回の目標

毎回この講座的なアレは目標を最初に書いてからやって行こうと思います。というのも、自分の性格上冗長になりやすい性質があって話が無限に脱線してしまうからです。
あとは最初の所をみてもらえば何をやってるかが理解してもらえるかな、と。

目標

  • 「4Numbers」で実際に行われている処理を明確にして(または処理したいことを盛り込んで)「設計図」の作成
  • [Chapter.1] ランダムで重複のない数字の生成

はじめの一歩-設計図の作成-

まずは今回の処理を大まかに説明できるような設計図を簡単に作成したいと思います。私もこれがないとコードが書けなくなってしまうので、アイデア保管庫的な意味合いも込めてこのセクションを入れています。
今回の設計図はこちら。(汚くてすみません) f:id:minamint:20170618160330p:plain
まずは流れを書き、「どんなことをするのか」を明確に。次に「それを実現するにはどんな処理が必要なのか」を端的に。これさえ見れば、書かずとも少しはイメージが浮かぶのではないでしょうか。
今回のプログラミング講座では、この設計図を基にプログラムを作成していきたいと思います。
なんでわざわざ設計図なんて書いたんだ、とかそういう諸々については「蛇足ですが」に長々と。

Chapter.1 ランダムで重複のない数字の生成をしよう

では早速設計図の1番目に書いてある「重複のないランダム4桁の生成」を行いましょう。実はこれとても簡単で、pythonに準備されている関数を使うだけで簡単に出来ちゃうわけです。
それは、randomモジュールのsampleメソッド。これを用いるだけで簡単に重複のないランダム数字が生成できてしまいます。インタプリタで軽く例題として出力してみると…

>>>import random
>>>test=random.sample(range(10),4)
>>>test
[2,4,9,5] 
>>>#複数回ランダムに数字を生成してみる
...for i in range(5):
...    test=random.sample(range(10),4)
...    test
...
[8,2,5,4]
[6,4,8,9]
[7,4,2,1]
[8,1,7,2]
[1,4,0,8]
>>>exit()

といったように、たった1文(importするので2文ですが)だけ書けばすぐに正解桁は作れてしまうわけです。ではこれを実際にパーツとして組み込んでいきますが、ここで一つ問題が発生します。
見て分かるように、ランダムで取った値はリストに入れられており,連続する4桁の数字ではないわけです。そこで、この4つの数字を4桁にするという処理を行うことにしましょう。
操作は簡単です。4つの数字をa,b,c,dとするならば、a×1+b×10+c×100+d×1000にするだけです。これで誰もが納得する4桁の数字になりますね。
納得出来ないですよね。知ってました。
4桁の数字にならない可能性があります。それはd=0であったとき。0に何をかけても0なので、1000の桁が存在しないことになり、3桁になってしまうんですね。
さあどうしましょう。
数字のままだと0から始まる値なんて作れないので、文字列に変換して最初に0を付け足してしまえば簡単なので、今回はそうしてしまいましょう。
文章であれこれ説明したものを実際に組み込んだのが以下のコードになります。コードがクラスを用いて書かれていますが、各chapter毎に作成したものをパーツとして利用するためにやっています。この方が最後のメインプログラムを書く際に今まで作ったものをインポートして利用するだけでプログラムが完成するため、非常に便利になるわけです。

import random

class create_random:
    def create_num(self):
        data = random.sample(range(10),4) #0-9のなかで4桁をランダム&重複なしに設定
        number=0 #正解桁を返す際の変数定義
        mul=[1,10,100,1000] #掛け算に使用するリスト
        for i in range(4):
            number += data[i] * mul[i] #ランダムで取り出したバラバラの数字を4桁の数字にしていく
        number=str(number) #数値を文字列に変換(この方が都合が良いため)
        if len(number)==3: #3桁しかない,即ち0から始まる値(0123など)の場合
            number = "0" + number #0を最初に付け加える
        return number
#---test section---

#number=create_random.create_num()
#print(number)

#---test end---

実行結果(結果は毎回変わります)

8390

0から始まる値になった場合の実行結果(0以降の値は毎回変わります)

0743

test sectionはちゃんと動作してくれるかを確かめているメモ書きみたいなものなので、本作成時には使いません。このコードを動かすと、ランダムな数字が表示され、ちゃんと動作していることが確認できます。
さて、コメントで説明はしていますが、例の問題の解決策として、数値を文字列に変換した後if文で3桁の場合の処理を追加しました。3桁であるなら、最初に0を付けて4桁にする。ただそれだけです。これを数値のまま0を付け足す処理を行おうとすると、「int型を持って来られても、str型でしかその処理出来ないので無理」と怒られるので気を付けましょう。
これで正解桁の準備ができましたね!次はユーザの入力処理を行っていきましょう!

まとめ

  • 設計図を書いて大まかな動きを理解した
  • ランダム数字生成はrandom.sample()で簡単に行えた!
    以上、案外あっさり終わってしまって少し困惑したみなくんでした。

蛇足ですが

設計図の必要性について長ったらしく生意気に語ります。自分へのメッセージとして。
例えば、「Push "1" to say "hello"」という文章を表示し、ユーザーに「1」と入力された場合に「Hello!」と返す単純なプログラムを書くとしましょう。
この例題をあなたがお好きな言語でコードを書いて解くとき、どうやって解いているでしょうか。感覚で解きますか?それとも「あれがこうなって…」と脳内で考えながらその場でパパっと書きますか?
今回の例題は動かすことだけを考えた場合実に簡単です。以下にcとpythonでの例題に沿ったコードを乗せてみます。
test.c(c言語)

#include<stdio.h>

int main(void){
    int i;
    printf("Push \"1\" to say \"hello\"\n");
    scanf("%d",&i); //ユーザからのキーボード入力を受け付ける
    if(i==1) //もし1だったら
        printf("hello!\n");

    return 0;
}

test.py(python言語)

print("Push \"1\" to say \"hello\"")
test=input() #ユーザからのキーボード入力を受け付ける
if test=='1': #もし1だったら
    print("hello!")

これで半角数字「1」を入力された場合、仕様通りの動作をするプログラムが完成します。
さて、この例題は簡単だったので、感覚で解くというのは容易だったと思います。python3系使いの方で感覚で解いた方は、恐らく「「Push "1" to say "hello"」という文章を表示("print()"で一発)し、ユーザーに「1」と入力された場合("input()"で一発)に「Hello!」と返す("print()"で一発)」と脳内で考えてコードを書いたことでしょう。 もっと簡単に言えば「喋って入力させて喋ったら終わりでしょ?簡単じゃん」――仰る通りです。
じゃあ、こんな例題はどうでしょうか。「スタックを用いてユーザの入力したデータを保存し、それを最後に結果として出すプログラムを作ってよ」と言われた場合。
最初の例題と同じく動作説明は明確に行われていますが、文章を見ただけでコードが見えるでしょうか。少し難しいのではないかと思います。
つまり脳内だけで処理してしまうと記憶保持や順序がバラバラになってしまうといった事態が発生しかねないので、アウトプットする意味で設計図を書く必要があるわけです。あとは誰かに説明したいとき、手伝ってほしいときに理解してもらえる、というメリットもあります。具体的な処理内容は後から記述する際にまとめていけばいいので、まずは大まかな動作図を示しましょう。「こうしたいんだよ」というイメージが明確でないと、コードは書けません。
ではスタックの例題で設計図を作ってみましょう!
1. スタックを用いて → データ格納(pushメソッド)とデータ取り出し(popメソッド)が必要
2. ユーザの入力した → キーボード入力を受け付けないと
3. データを保存し  → キーボード入力を受け付けて、それからpushさせるのか!
4. 最後に結果で出す → すべて終わったら、全部popすれば良いわけだ

と、全4段階に区切って考えることができます。そして区切った後に「どのような処理をさせると良いのか」を書いています。これを少し書くだけで、どんなコードを書けばいいのか理解しやすいですね。 「この仕様通りの動きにしたいけど、どうやったらいいか分からない」と言いながら仕様文だけ読んでじーっと考えてる人がたまにいます。ぜひ考える前に、大まかな動作が分かる設計図を作ってください。そうすれば、コードは見えずともどういう処理をすればいいのかは分かるはずですから。
上から順番にプログラムを書くのではなく、大まかな形を作りながら書く、といったように。枠を作ってから後で中身を詳しく書いていく、というスタイルにしてみると随分書きやすくなるかと思います。今まで考えたこともなかったと言う人がいたら、是非トライしてみてください!