Application Othello (application iOS) réalisée avec Python (Kivy)

introduction

J'ai créé une application Othello en utilisant kivy, une bibliothèque open source pour développer des applications multi-tap en Python. Enfin, je l'ai construit avec (xcode) dans le simulateur iOS.

environnement

python: 3.7.7 kivy: 1.11.1 xcode: 11.7

Création

Formulaire rempli Sep-06-2020 20-30-30.gif

Explication du code source

J'expliquerai en suivant l'ordre dans lequel l'application Othello créée cette fois a été développée.

1. Placez le plateau d'Othello et les pierres initiales

Créer jusqu'à l'état suivant スクリーンショット 2020-09-06 20.44.30.png

class OthelloApp(App):
    title = 'Othello'
    def build(self):
        return OthelloGrid()


OthelloApp().run()

Retour de la classe OthelloGrid dans la classe APP. Le traitement de cette application se fait dans cette classe OthelloGrid.

class OthelloGrid(Widget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.num = 8
        self.tile = [[' ' for x in range(self.num)] for x in range(self.num)]
        self.turn = 'W'
        self.grid = GridLayout(cols=self.num, spacing=[3,3], size=(Window.width, Window.height))

        for x in range(self.num):
            for y in range(self.num):
                if x == 3 and y == 3 or x == 4 and y == 4:
                    self.grid.add_widget(WhiteStone())
                    self.tile[x][y] = 'W'
                elif x == 4 and y == 3 or x == 3 and y == 4:
                    self.grid.add_widget(BlackStone())
                    self.tile[x][y] = 'B'
                else:
                    self.grid.add_widget(PutButton(background_color=(0.451,0.3059,0.1882,1), background_normal='', tile_id=[x, y]))
        self.add_widget(self.grid)

self.num est le nombre de carrés verticaux et horizontaux sur le plateau d'Othello. self.tile est une liste pour mémoriser l'état du tableau. Lorsque le blanc est placé'W', lorsque le noir est placé B', rien n'est placé. Prend la valeur de quand `` '' '. self.turnse souvient si le virage actuel est noir ou blanc et commence initialement par un virage blanc. Le plateau qui est réellement dessiné sur l'écran est défini parself.grid (GridLayout), et pour le carré où la pierre est déjà placée, la classe WhiteStone pour les pierres blanches et la classe BlackStone pour les pierres noires. On ajoute_widget ʻet add_widget PutButton class aux carrés où aucune pierre n'a encore été placée.

class WhiteStone(Label):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.bind(pos=self.update)
        self.bind(size=self.update)
        self.update()
    def update(self, *args):
        self.canvas.clear()
        self.canvas.add(Color(0.451,0.3059,0.1882,1))
        self.canvas.add(Rectangle(pos=self.pos, size=self.size))
        self.canvas.add(Color(1,1,1,1))
        self.canvas.add(Ellipse(pos=self.pos, size=self.size))

class BlackStone(Label):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.bind(pos=self.update)
        self.bind(size=self.update)
        self.update()
    def update(self, *args):
        self.canvas.clear()
        self.canvas.add(Color(0.451,0.3059,0.1882,1))
        self.canvas.add(Rectangle(pos=self.pos, size=self.size))
        self.canvas.add(Color(0,0,0,1))
        self.canvas.add(Ellipse(pos=self.pos, size=self.size))

class PutButton(Button):
    def __init__(self, tile_id, **kwargs):
        super().__init__(**kwargs)
        self.tile_id = tile_id

Les classes WhiteStone et BlackStone héritent de la classe Label et ont simplement une pierre elliptique ʻEllipse (pos = self.pos) au-dessus de la masse Rectangle (pos = self.pos, size = self.size). , size = self.size) ʻest juste dessiné.

La classe PutButton hérite de la classe Button, et il n'y a pas de traitement lorsqu'elle est encore enfoncée. En tant que tile_id, l'instance elle-même peut se souvenir de la position sur la grille.

2. Placez une pierre lorsque vous appuyez sur le carré

Créer jusqu'à l'état suivant Sep-06-2020 21-29-16.gif

Créez une fonction ʻon_press dans la classe PutButton` comme indiqué ci-dessous, et ajoutez le traitement lorsque la cellule est tapée.

Classe PutButton


def on_press(self):
    put_x = self.tile_id[0]
    put_y = self.tile_id[1]
    turn = self.parent.parent.turn
    
    self.parent.parent.tile[put_x][put_y] = turn
    self.parent.parent.put_stone()

Remplacez "put_x" et "put_y" par le numéro de cellule tapé et remplacez le tour actuel par "turn". Appelez la fonction put_stone en affectant la valeur de turn à l'emplacement de la cellule tapée de tile dans la classe parent (ʻOthelloGrid). put_stone est une fonction créée dans ʻOthello Grid comme indiqué ci-dessous, et est une fonction pour recréer le tableau à partir du contenu de tile.

Classe OthelloGrid


def put_stone(self):
    self.clear_widgets()
    self.grid = GridLayout(cols=self.num, spacing=[3,3], size=(Window.width, Window.height))
    next_turn = 'W' if self.turn == 'B' else 'B'

    for x in range(self.num):
        for y in range(self.num):
            if self.tile[x][y] == 'W':
                self.grid.add_widget(WhiteStone())
            elif self.tile[x][y] == 'B':
                self.grid.add_widget(BlackStone())
            else:
                self.grid.add_widget(PutButton(background_color=(0.451,0.3059,0.1882,1), background_normal='', tile_id=[x, y]))

3. Retournez la pierre pincée (ne faites rien si vous touchez un carré qui ne peut pas être retourné)

Créer jusqu'à l'état suivant Sep-06-2020 21-50-38.gif

Ajoutez les fonctions can_reverse_check et reverse_list à la classe ʻOthelloGrid` pour vérifier s'il y a des pierres qui peuvent être retournées à partir des coordonnées de la masse et du tour actuel comme indiqué ci-dessous.

Classe OthelloGrid


def can_reverse_check(self, check_x, check_y, turn):
    check =[]
    #Confirmation en haut à gauche
    check += self.reverse_list(check_x, check_y, -1, -1, turn)
    #Confirmé ci-dessus
    check += self.reverse_list(check_x, check_y, -1, 0, turn)
    #Confirmation en haut à droite
    check += self.reverse_list(check_x, check_y, -1, 1, turn)
    #Bonne confirmation
    check += self.reverse_list(check_x, check_y, 0, 1, turn)
    #Confirmation en bas à droite
    check += self.reverse_list(check_x, check_y, 1, 1, turn)
    #Vérifiez ci-dessous
    check += self.reverse_list(check_x, check_y, 1, 0, turn)
    #Confirmation en bas à gauche
    check += self.reverse_list(check_x, check_y, 1, -1, turn)
    #Confirmation gauche
    check += self.reverse_list(check_x, check_y, 0, -1, turn)
    return check

def reverse_list(self, check_x, check_y, dx, dy, turn):
    tmp = []
    while True:
        check_x += dx
        check_y += dy
        if check_x < 0 or check_x > 7:
            tmp = []
            break
        if check_y < 0 or check_y > 7:
            tmp = []
            break

        if self.tile[check_x][check_y] == turn:
            break
        elif self.tile[check_x][check_y] == ' ':
            tmp = []
            break
        else:
            tmp.append((check_x, check_y))
    return tmp

can_reverse_check appelle reverse_list, qui est une fonction pour vérifier s'il y a une pierre qui peut être retournée dans chaque direction à partir du carré où la pierre doit être placée. En tant que valeur de retour, une liste des coordonnées des pierres qui peuvent être retournées est renvoyée.

Comme indiqué ci-dessous, ce can_reverse_check est appelé lorsque la classe PutButton est tapée (dans ʻon_press), et s'il y a un contenu dans la liste des valeurs de retour, la valeur de tileest mise à jour et le tableau est affiché. Recréez (appelezput_stone`). Si la liste est vide, ne faites rien.

Classe PutButton


def on_press(self):
    put_x = self.tile_id[0]
    put_y = self.tile_id[1]
    check =[]
    turn = self.parent.parent.turn
    
    check += self.parent.parent.can_reverse_check(self.tile_id[0], self.tile_id[1], turn)
    if check:
        self.parent.parent.tile[put_x][put_y] = turn
        for x, y in check:
            self.parent.parent.tile[x][y] = turn
        self.parent.parent.put_stone()

4. Ajout de la fonction de passe et du traitement à la fin du jeu

Fonction de passage quand il n'y a pas de place pour mettre des pierres Sep-06-2020 22-25-46.gif

Jugement gagnant / perdant à la fin de la partie Sep-06-2020 22-29-12.gif

ʻExtend the put_stone de la classe OthelloGrid` comme suit

Classe OthelloGrid


def put_stone(self):
    pass_flag = True
    finish_flag = True
    check = []
    self.clear_widgets()
    self.grid = GridLayout(cols=self.num, spacing=[3,3], size=(Window.width, Window.height))
    next_turn = 'W' if self.turn == 'B' else 'B'

    for x in range(self.num):
        for y in range(self.num):
            if self.tile[x][y] == 'W':
                self.grid.add_widget(WhiteStone())
            elif self.tile[x][y] == 'B':
                self.grid.add_widget(BlackStone())
            else:
                self.grid.add_widget(PutButton(background_color=(0.451,0.3059,0.1882,1), background_normal='', tile_id=[x, y]))
    
    for x in range(self.num):
        for y in range(self.num):
            if self.tile[x][y] == ' ':
                finish_flag = False
                check += self.can_reverse_check(x, y, next_turn)
            if check:
                pass_flag = False
                break

    if finish_flag:
        content = Button(text=self.judge_winner())
        popup = Popup(title='Game set!', content=content, auto_dismiss=False, size_hint=(None, None), size=(Window.width, Window.height/3))
        content.bind(on_press=popup.dismiss)
        popup.open()
    else:    
        if pass_flag:
            skip_turn_text = 'White Turn' if self.turn == 'B' else 'Black Turn'
            content = Button(text='OK')
            popup = Popup(title=skip_turn_text+' Skip!', content=content, auto_dismiss=False, size_hint=(None, None), size=(Window.width, Window.height/3))
            content.bind(on_press=popup.dismiss)
            popup.open()
        else:
            self.turn = next_turn

        self.add_widget(self.grid)

Préparez pass_flag et finish_flag et utilisez-les pour juger de la réussite ou de la fin de la partie. Pour toutes les cases vides de «tuile» (cases avec une valeur de «» »), vérifiez s'il y a une pierre à retourner lorsque le joueur pose une pierre sur cette case au tour suivant. Sinon, sautez le tour suivant. À ce moment-là, affichez sur l'écran que vous avez ignoré avec Popup.

S'il n'y a pas d'espace vide dans tile, on considère que le jeu est terminé, et la fonction juge_winner suivante détermine celui qui gagne et l'affiche à l'écran avec Popup.

Classe OthelloGrid


def judge_winner(self):
    white = 0
    black = 0
    for x in range(self.num):
        for y in range(self.num):
            if self.tile[x][y] == 'W':
                white += 1
            elif self.tile[x][y] == 'B':
                black += 1
    print(white)
    print(black)
    return 'White Win!' if white >= black else 'Black Win!'

C'est la fin du traitement d'Othello.

Code source complet

Nous avons également ajouté ResetButton et une étiquette qui affiche le virage, mais veuillez vérifier l'intégralité du code source ci-dessous pour cette zone. (Je le donne aussi à git. Https://github.com/fu-yuta/kivy-project/tree/master/Othello)

main.py


from kivy.app import App
from kivy.uix.widget import Widget
from kivy.core.window import Window
from kivy.uix.gridlayout import GridLayout
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.graphics import Color, Ellipse, Rectangle

class OthelloGrid(Widget):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.num = 8
        self.tile = [[' ' for x in range(self.num)] for x in range(self.num)]
        self.turn = 'W'
        self.grid = GridLayout(cols=self.num, spacing=[3,3], size_hint_y=7)

        for x in range(self.num):
            for y in range(self.num):
                if x == 3 and y == 3 or x == 4 and y == 4:
                    self.grid.add_widget(WhiteStone())
                    self.tile[x][y] = 'W'
                elif x == 4 and y == 3 or x == 3 and y == 4:
                    self.grid.add_widget(BlackStone())
                    self.tile[x][y] = 'B'
                else:
                    self.grid.add_widget(PutButton(background_color=(0.451,0.3059,0.1882,1), background_normal='', tile_id=[x, y]))

        self.creat_view('White Turn')
    
    def put_stone(self):
        self.grid = GridLayout(cols=self.num, spacing=[3,3], size_hint_y=7)
        pass_flag = True
        finish_flag = True
        check = []
        next_turn = 'W' if self.turn == 'B' else 'B'

        for x in range(self.num):
            for y in range(self.num):
                if self.tile[x][y] == 'W':
                    self.grid.add_widget(WhiteStone())
                elif self.tile[x][y] == 'B':
                    self.grid.add_widget(BlackStone())
                else:
                    self.grid.add_widget(PutButton(background_color=(0.451,0.3059,0.1882,1), background_normal='', tile_id=[x, y]))
        
        for x in range(self.num):
            for y in range(self.num):
                if self.tile[x][y] == ' ':
                    finish_flag = False
                    check += self.can_reverse_check(x, y, next_turn)
                if check:
                    pass_flag = False
                    break

        if finish_flag:
            content = Button(text=self.judge_winner())
            popup = Popup(title='Game set!', content=content, auto_dismiss=False, size_hint=(None, None), size=(Window.width, Window.height/3))
            content.bind(on_press=popup.dismiss)
            popup.open()
            self.restart_game()
        else:    
            if pass_flag:
                skip_turn_text = 'White Turn' if self.turn == 'B' else 'Black Turn'
                content = Button(text='OK')
                popup = Popup(title=skip_turn_text+' Skip!', content=content, auto_dismiss=False, size_hint=(None, None), size=(Window.width, Window.height/3))
                content.bind(on_press=popup.dismiss)
                popup.open()
            else:
                self.turn = next_turn

            turn_text = 'Black Turn' if self.turn == 'B' else 'White Turn'
            self.creat_view(turn_text)
    
    def can_reverse_check(self, check_x, check_y, turn):
        check =[]
        #Confirmation en haut à gauche
        check += self.reverse_list(check_x, check_y, -1, -1, turn)
        #Confirmé ci-dessus
        check += self.reverse_list(check_x, check_y, -1, 0, turn)
        #Confirmation en haut à droite
        check += self.reverse_list(check_x, check_y, -1, 1, turn)
        #Bonne confirmation
        check += self.reverse_list(check_x, check_y, 0, 1, turn)
        #Confirmation en bas à droite
        check += self.reverse_list(check_x, check_y, 1, 1, turn)
        #Vérifiez ci-dessous
        check += self.reverse_list(check_x, check_y, 1, 0, turn)
        #Confirmation en bas à gauche
        check += self.reverse_list(check_x, check_y, 1, -1, turn)
        #Confirmation gauche
        check += self.reverse_list(check_x, check_y, 0, -1, turn)
        return check

    def reverse_list(self, check_x, check_y, dx, dy, turn):
        tmp = []
        while True:
            check_x += dx
            check_y += dy
            if check_x < 0 or check_x > 7:
                tmp = []
                break
            if check_y < 0 or check_y > 7:
                tmp = []
                break

            if self.tile[check_x][check_y] == turn:
                break
            elif self.tile[check_x][check_y] == ' ':
                tmp = []
                break
            else:
                tmp.append((check_x, check_y))
        return tmp

    def judge_winner(self):
        white = 0
        black = 0
        for x in range(self.num):
            for y in range(self.num):
                if self.tile[x][y] == 'W':
                    white += 1
                elif self.tile[x][y] == 'B':
                    black += 1
        print(white)
        print(black)
        return 'White Win!' if white >= black else 'Black Win!'

    def restart_game(self):
        print("restart game")
        self.tile = [[' ' for x in range(self.num)] for x in range(self.num)]
        self.turn = 'W'
        self.grid = GridLayout(cols=self.num, spacing=[3,3], size_hint_y=7)

        for x in range(self.num):
            for y in range(self.num):
                if x == 3 and y == 3 or x == 4 and y == 4:
                    self.grid.add_widget(WhiteStone())
                    self.tile[x][y] = 'W'
                elif x == 4 and y == 3 or x == 3 and y == 4:
                    self.grid.add_widget(BlackStone())
                    self.tile[x][y] = 'B'
                else:
                    self.grid.add_widget(PutButton(background_color=(0.451,0.3059,0.1882,1), background_normal='', tile_id=[x, y]))

        self.creat_view('White Turn')

    def creat_view(self, turn_text):
        self.clear_widgets()
        self.turn_label = Label(text=turn_text, width=Window.width , size_hint_y=1, font_size='30sp')
        self.restart_button = RestartButton(text='Restart')
        self.layout = BoxLayout(orientation='vertical', spacing=10, size=(Window.width, Window.height))
        self.layout.add_widget(self.turn_label)
        self.layout.add_widget(self.grid)
        self.layout.add_widget(self.restart_button)
        self.add_widget(self.layout)


class WhiteStone(Label):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.bind(pos=self.update)
        self.bind(size=self.update)
        self.update()
    def update(self, *args):
        self.canvas.clear()
        self.canvas.add(Color(0.451,0.3059,0.1882,1))
        self.canvas.add(Rectangle(pos=self.pos, size=self.size))
        self.canvas.add(Color(1,1,1,1))
        self.canvas.add(Ellipse(pos=self.pos, size=self.size))

class BlackStone(Label):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.bind(pos=self.update)
        self.bind(size=self.update)
        self.update()
    def update(self, *args):
        self.canvas.clear()
        self.canvas.add(Color(0.451,0.3059,0.1882,1))
        self.canvas.add(Rectangle(pos=self.pos, size=self.size))
        self.canvas.add(Color(0,0,0,1))
        self.canvas.add(Ellipse(pos=self.pos, size=self.size))

class PutButton(Button):
    def __init__(self, tile_id,  **kwargs):
        super().__init__(**kwargs)
        self.tile_id = tile_id

    def on_press(self):
        print(self.tile_id)
        put_x = self.tile_id[0]
        put_y = self.tile_id[1]
        check =[]
        turn = self.parent.parent.parent.turn
        
        check += self.parent.parent.parent.can_reverse_check(self.tile_id[0], self.tile_id[1], turn)
        if check:
            self.parent.parent.parent.tile[put_x][put_y] = turn
            for x, y in check:
                self.parent.parent.parent.tile[x][y] = turn
            self.parent.parent.parent.put_stone()

class RestartButton(Button):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def on_press(self):
        content = Button(text='OK')
        popup = Popup(title='Restart Game!', content=content, auto_dismiss=False, size_hint=(None, None), size=(Window.width, Window.height/3))
        content.bind(on_press=popup.dismiss)
        popup.open()
        self.parent.parent.restart_game()

class OthelloApp(App):
    title = 'Othello'
    def build(self):
        return OthelloGrid()


OthelloApp().run()

Construire avec le simulateur iOS

J'ai fait référence à l'article suivant. https://qiita.com/sobassy/items/b06e76cf23046a78ba05

Si l'outil de ligne de commande Xcode n'est pas inclus, exécutez la commande suivante.

xcode-select --install

Installer les dépendances

brew install autoconf automake libtool pkg-config
brew link libtool

Installez Cython

pip install cython

git clone kivy-ios.

git clone https://github.com/kivy/kivy-ios.git
cd kivy-ios

Exécutez la commande suivante pour générer kivy pour iOS (cela peut prendre des dizaines de minutes)

python toolchain.py build kivy

Une fois ce qui précède terminé, créez le programme kivy pour Xcode.

python toolchain.py create [Nom du projet Xcode (n'importe quel nom)] [nom du dossier du programme kivy]

À ce stade, le nom du fichier programme kivy doit être «main.py». Si le nom du projet Xcode est ʻApp, un répertoire appelé ʻApp-ios est créé et ʻApp.xcodeproj` est créé dans ce répertoire. Ouvrez ce projet dans Xcode.

open App.xcodeproj

Si vous spécifiez Simulator dans Xcode et que vous le construisez, l'application démarre. Si vous mettez à jour le programme kivy, vous devez également mettre à jour le projet Xcode avec la commande suivante.

python toolchain.py update App

À la fin

J'ai créé une application Othello en utilisant kivy, l'une des bibliothèques python, et l'ai construite avec Simulater sur iOS. Cette fois, j'ai écrit tout le traitement dans main.py, mais kivy est un langage kv, et wighet etc. peut être écrit séparément, donc j'aimerais envisager de porter vers cela. De plus, nous aimerions intégrer l'IA d'Othello et ajouter une fonction de combat joueur-processeur à l'avenir.

référence

https://qiita.com/sobassy/items/b06e76cf23046a78ba05 https://github.com/PrestaMath/reverse_tile

Recommended Posts

Application Othello (application iOS) réalisée avec Python (Kivy)
Othello fait avec python (comme GUI)
Développement de jeux Othello avec Python
J'ai fait un blackjack avec du python!
J'ai fait un blackjack avec Python.
J'ai créé wordcloud avec Python.
Numer0n avec des objets fabriqués avec Python
J'ai fait une loterie avec Python.
J'ai créé un démon avec Python
Développement d'applications IOS avec Python (kivy) que même les singes peuvent faire
J'ai fait Othello pour enseigner Python3 aux enfants (4)
Client API Slack simple réalisé avec Python
Type de téléchargement de partage HTTP réalisé avec Python
Uncle SES modernise l'application VBA avec Python
J'ai fait un compteur de caractères avec Python
J'ai fait Othello pour enseigner Python3 aux enfants (2)
Écran partagé avec l'application python exe
Démonisez une application Web Python avec Supervisor
J'ai fait Othello pour enseigner Python3 aux enfants (5)
Application Web facile avec Python + Flask + Heroku
API de reconnaissance faciale sans serveur conçue avec Python
Créez une application de bureau avec Python avec Electron
J'ai fait un jeu rogue-like avec Python
J'ai fait Othello pour enseigner Python3 aux enfants (3)
J'ai fait Othello pour enseigner Python3 aux enfants (1)
J'ai fait un simple blackjack avec Python
J'ai créé un fichier de configuration avec Python
J'ai fait un simulateur de neurones avec Python
[Python] Python et sécurité-② Outil d'analyse de port réalisé avec Python
FizzBuzz en Python3
Grattage avec Python
J'ai fait une prévision météo de type bot avec Python.
Statistiques avec python
Créez une application qui devine les étudiants avec Python
Grattage avec Python
Python avec Go
Gagnez l'application Web Python + Flask avec Jenkins
Outil de rognage d'image GUI réalisé avec Python + Tkinter
Twilio avec Python
Procédure de création d'un LineBot réalisé avec Python
Automatisation de l'interface graphique avec le pilote d'application Python x Windows
Intégrer avec Python
Jouez avec 2016-Python
AES256 avec python
Testé avec Python
python commence par ()
avec syntaxe (Python)
Procédure de création d'application multi-plateforme avec kivy
Zundokokiyoshi avec python
Décrypter une chaîne chiffrée sur iOS avec Python
Excel avec Python
J'ai fait un jeu de cueillette avec Python
Made Mattermost Bot avec Python (+ Flask)
Cast avec python
J'ai fait un Twitter BOT avec GAE (python) (avec une référence)
Pages HTML dynamiques créées avec AWS Lambda et Python
J'ai fait un jeu d'éclairage de sapin de Noël avec Python
[Pratique] Créez une application Watson avec Python! # 1 [Discrimination linguistique]
[Mac OS] Utilisez Kivy avec PyCharm! [Développement d'applications Python]
J'ai créé un environnement Python3 sur Ubuntu avec direnv.