pythonでコネクトフォー作ってみた PART1

おはようございます!にわこまです。

今回はpythonでコネクトフォーというゲームを作ってみました。意外と簡単に作ることができたため、pythonで何か作りたい人におすすめです。

何かご指摘があればご連絡ください!

 

 

スポンサードサーチ


pythonでコネクトフォー作ってみた!

今回はpythonでコネクトフォーというゲームを作ってみました。

 

そもそもコネクトフォーって何?

コネクトフォーとは、1対1のボードゲーム(アナログゲーム)です。6×7に分けられたグリッドに自分の色のディスクを交互に入れていき、先に縦横斜めのどれかで4つつなげた人の勝ちというルールです。

日本では、四目並べや五目並べと言われています

 

 

今回作るコネクトフォーは、いわゆる重力付きコネクトフォーです。

重力付きとは、自分のディスクを6×7のグリッドの好きな所へ置くわけではなく、列を指定してディスクを入れると、下に落ちていくというものです。

下にイメージ図を示します。重力がかかっている方向を上から下であるとして、右から2つ目の列を指定してディスクを入れると以下のようになります。

 

pythonでコネクトフォーを作ると、約300行でコードを書くことができるため、pythonでゲームを作ってみたい人におすすめです。

また、今回はコマンドプロンプトで遊べるコードを書きました。pygameでも遊べるようにコードを書き換えるので、待っていてください!

 

コードはこちらからどうぞ

 

 

作る手順

コネクトフォーを作るにあたって、コネクトフォーの動作を細かくしていきます。そして、実際にどのように作っていったのか説明していきます。

 

コネクトフォー動作

1.列を指定する(1~7)
2.指定した列にディスクを入れられるか判定する
3-1.入れられたら、4に移行
3-2.入れられなかったら終了
4.底辺から数えて1番最初に見つけた空場所にディスクを落とす
5.grid(field)の更新
6.勝利判定
7-1.勝ち負けが決まったら終了
7-2.勝ち負けが決まらなかったら7に移行
8.手番が相手に移り、手順1にもどる

 

コネクトフォーの動作はこれくらいかなと思いました。

 

実際に作っていく手順

1.grid(field)をつくる
2.列の指定
3.指定した列にディスクを入れられるか判定
4.底辺に1番近い空場所の座標を返す
5.座標からgrid(field)の更新

1~5をプレイヤーバージョンとコンピュータバージョンつくる

6.勝利判定
勝利判定は、縦、横、斜めについて行います。

斜めは、「左上から右下(スラッシュ)」と「右上から左下(バックスラッシュ)」があります。

 

最後にmain文を書いて、1通り動くか確認します。

 

 

スポンサードサーチ


コードの説明

プレイヤークラスの説明(PlayerClass)

class PlayerClass:

    def update_point(self, field, number):
        (x, y) = field.shape
        if((number <= 0) or (y < number)):
            print("Error: number is out of range.(select_point)")
            sys.exit()
        for i in range(len(field)-1, -1, -1):
            point = field[(i, number - 1)]
            if(point == 0):
                return (i, number - 1)
        print("Error: that column in not empty.(select_point)")
        sys.exit()

    def update_field(self, field, xpos, ypos):
        if(field[(xpos, ypos)] != 0):
            print("Error: that point is not 0.(update_field)")
            sys.exit()
        field[xpos, ypos] = PLAYER
        return field

 

「update_point」という関数で、指定された列の判定をする機能と指定された列の空場所の座標を返す機能を担っています。

「update_field」という関数で、受け取った座標に自分の座標を代入しています。また、更新したgrid(field)を返却しています。

 

 

コンピュータクラスの説明(ComputerClass)

class ComputerClass:

    def choose_number(self, field):
        number_list = []
        transpose_field = np.transpose(field)
        for i in range(len(transpose_field)):
            if(0 in transpose_field[i]):
                number_list.append(i+1)
        r = random.randrange(0, len(number_list))
        return number_list[r]

    def choose_number2_func1(self, field):
        # rowにリーチがかかっているか確認
        point_list = []
        tmp_field = copy.copy(field)
        # -1を削除
        tmp_field[field < 0] = 0
        for i in range(0, len(field)):
            val = 0
            for j in range(0, len(field[0])):
                val = val * tmp_field[(i, j)] + tmp_field[(i, j)]
                tmp_field[(i, j)] = val
                # val == 2
                if((val == 2) and ((j+1) < len(field[0])) and ((j+2) < len(field[0]))):
                    if((field[(i, j+1)] == 0) and (field[(i, j+2)] == 1)):
                        point_list.append((i, j+1))
                if((val == 2) and ((j-2) >= 0) and ((j-3) >= 0)):
                    if((field[(i, j-2)] == 0) and (field[(i, j-3)] == 1)):
                        point_list.append((i, j-2))
                # val == 3
                if((val == 3) and ((j+1) < len(field[0]))):
                    if((field[(i, j+1)] == 0) and ((i, j+1) not in point_list)):
                        point_list.append((i, j+1))
                if((val == 3) and ((j-3) >= 0)):
                    if((field[(i, j-3)] == 0) and ((i, j-3) not in point_list)):
                        point_list.append((i, j-3))
        return point_list

    def choose_number2_func2(self, field):
        # colにリーチがかかっていないか確認
        transpose_field = np.transpose(field)
        point_list = self.choose_number2_func1(transpose_field)
        new_point_list = []
        for i in range(0, len(point_list)):
            v1, v2 = point_list[i]
            new_point_list.append((v2, v1))
        return new_point_list

    def choose_number2(self, field):
        func1_list = self.choose_number2_func1(field)
        func2_list = self.choose_number2_func2(field)
        point_list = list(set(func1_list) | set(func2_list))
        if(len(point_list) != 0):
            number_list = []
            for i in point_list:
                v1, v2 = i
                if((v2+1) not in number_list):
                    number_list.append(v2+1)
            r = random.randrange(0, len(number_list))
            return number_list[r]
        elif(len(point_list) == 0):
            return self.choose_number(field)
        else:
            print("Error: choose_number2")
            sys.exit()

    def update_point(self, field, number):
        (x, y) = field.shape
        if((number <= 0) or (y < number)):
            print("Error: number is out of range.(select_point)")
            sys.exit()
        for i in range(len(field)-1, -1, -1):
            point = field[(i, number - 1)]
            if(point == 0):
                return (i, number - 1)
        print("Error: that column in not empty.(select_point)")
        sys.exit()

    def update_field(self, field, xpos, ypos):
        if(field[(xpos, ypos)] != 0):
            print("Error: that point is not 0.(update_field)")
            sys.exit()
        field[xpos, ypos] = COMPUTER
        return field

 

「update_point」と「update_field」に関しては、プレイヤークラスと同じです。

その他のクラスで主に使う関数は「choose_number」と「choose_number2」です。

「choose_number」という関数では、ディスクを入れられる列を導き出し、その中からランダムで数字を選んで返します。

「choose_number2」という関数では、「choose_number」があまりにも弱すぎたため、リーチがかかったらそれを回避するように数字を選んで返します。

 

「choose_number2_func1」と「choose_number2_func2」という関数は、「choose_number2」に必要な関数です。行と列において、リーチがかかっていないか判定を行い、数字を返却します。

 

 

勝利判定クラスの説明(VictoryCondition)

「victory_condition」という関数が、最終的に勝利判定を行います。その他の関数「victory_condition_row」、「victory_condition_col」、「victory_condition_slash」、「victory_condition_backslash」はそれぞれ、横、縦、斜め(スラッシュ方向)、斜め(バックスラッシュ方向)で4つ連なっていないか判定しています。

 

 

「victory_condition_row」について詳しく説明していきます。この関数では横方向について勝利判定を行います。

横方向に対して左のマスから1マスずつ処理を行っていきます。

プレイヤーのディスクを1、コンピュータのディスクを-1、空欄を0として表しています。 プレイヤーのあるマスxに対して処理を行う場合、コンピュータのディスクを0に置き換えてから、処理を行います。

 

x-1のマスの数字にxの数字をかけ合わせます、その後その数字にxの数字を足します。初期値は0としてあります。

下記の画像に従って例を示します。

1マス目: 0 × 1 + 1 = 1

2マス目: 1(=1マス目の数字) × 0 + 0 = 0

3マス目: 0(=2マス目の数字) × 1 + 1 = 1

4マス目: 1(=3マス目の数字) × 1 + 1 = 2

5マス目: 2(=4マス目の数字) × 1 + 1 = 3

6マス目: 3(=5マス目の数字) × 1 + 1 = 4

7マス目: 4(=6マス目の数字) × 0 + 0 = 0

よって以下のようになります。

このように計算することで4つ連なっている時は「4」が現れます。最後に横方向ごとに4が含まれているか確認して、含まれていれば「True」を含まれていなければ「False」を返却します。

 

 

次に、「victory_condition_col」という関数について説明します。この関数は縦方向について勝利判定を行います。

この関数では、grid(field)を転置したものを横方向の勝利判定を行う「victory_condition_row」に処理してもらうことで勝利判定を行っています。

下のような図である場合

転置すると下のような図になります。よって、転置したものを横方向の勝利判定を行う「victory_condition_row」に処理してもらうことで、縦方向の勝利判定を行うことが出来ます。

 

 

次に、「victory_condition_slash」という関数について説明します。この関数では、斜め方向(左上から右下)の勝利判定を行います。

この関数は、行ごとに左へいくつかずらしてから、「victory_condition_col」を利用して斜め方向の勝利判定を行っています。

1行目はそのまま、2行目は左へ1つずらす、3行目は左へ2つずらす、、、のようにずらしていきます。

 

例を以下に示します。下の図のような場合を考えます。 分かりやすくするため色を付けています。

ずらす処理を行うと、後は縦方向に勝利判定を行うだけです。

 

 

最後に、「victory_condition_backslash」という関数について説明します。この関数では、斜め方向(右上から左下)の勝利判定を行います。

この関数は、行ごとに右へいくつかずらしてから、「victory_condition_col」を利用して斜め方向の勝利判定を行っています。

「victory_condition_slash」の右へずらすバージョンです。ほぼ同じ処理であるため説明は省略します。

 

 

使い方(遊び方)

まず、こちらからコードを手に入れてください。

プライベートファイル - アクセス禁止

 

1.任意のフォルダーにファイルをダウンロードしてください

2.コマンドプロンプトを開いてください

windowsである場合は左下の検索ボックスに「cmd」と打ち込んでいただければコマンドプロンプトが表示されます。

3.ファイルを保存したフォルダーまで移動します。

4.「python ファイル名.py」と打ち込むとコネクトフォーを遊ぶことが出来ます。 ファイル名のところは保存したときの名前を入れてください。

pythonがパソコンに入っていないと遊ぶことが出来ません。

 

 

スポンサードサーチ


まとめ

今回は、コネクトフォーというゲームを作ってみました。

ルールがシンプルであるため、必要な関数が少なく済みました。ゆえに、pythonで何か作りたい人におすすめです。

私は約300行で書くことが出来ました。

また、コンピュータ側の手をもっと強くするなど、このプログラムはもっと良くすることが可能です。

友達と競い合って、より強いコンピュータを作ってみたりしてはいかがですか?

 

最後までお読みいただきありがとうございます。


スポンサードサーチ