LoginSignup
0
0

Pythonで〇×ゲームのAIを一から作成する その37 〇×ゲームを遊ぶためのメソッドの定義

Last updated at Posted at 2023-12-17

目次と前回の記事

前回までのおさらい

〇×ゲームの仕様と進捗状況

  1. 正方形で区切られた 3 x 3 の 2 次元のゲーム盤上でゲームを行う
  2. ゲーム開始時には、ゲーム盤のすべてのマスは空になっている
  3. 2 人のプレイヤーが遊ぶゲームであり、一人は 〇 を、もう一人は × のマークを受け持つ
  4. 2 人のプレイヤーは、交互に空いている好きなマスに自分のマークを 1 つ置く
  5. 先手は 〇 のプレイヤーである
  6. プレイヤーがマークを置いた結果、縦、横、斜めのいずれかの一直線の 3 マスに同じマークが並んだ場合、そのマークのプレイヤーの勝利とし、ゲームが終了する
  7. すべてのマスが埋まった時にゲームの決着がついていない場合は引き分けとする

仕様の進捗状況は、以下のように表記します。

  • 実装が完了した部分を 背景が灰色の長方形 で記述する
  • 実装の一部が完了した部分を、太字 で記述する

これまでに作成したモジュール

以下のリンクから、これまでに作成したモジュールを見ることができます。

なお、今回の記事では test.py は使用しないので test.py は割愛します。

前回までのおさらい

前回の記事までで、judge メソッドを実装するための、さまざまなアルゴリズムを紹介しました。今回の記事では〇×ゲームを実際に遊ぶためのメソッドを定義します。

〇×ゲームを遊ぶためのメソッドの定義

前回の記事で、〇×ゲームの すべての仕様実装が完了 しました。現時点では、〇×ゲームの AI を実装していないので、AI を相手に〇×ゲームの対戦を行うことはまだできませんが、〇 と × の 両方人間が担当 すれば 〇×ゲームを 実際に遊ぶ ことができます。

しかし、現状では、〇×ゲームを遊ぶために、Marubatsu クラスの インスタンスを作成した後 で、下記の アルゴリズム で Python の プログラムを記述 し、実行する必要 があります。

  1. restart メソッドを使って、〇×ゲームを 再起動 する
  2. print を使って ゲーム盤を表示 する
  3. プレイヤーに 着手を行う 座標を入力 させる
  4. プレイヤーが 入力した座標move メソッドで 着手を行う
  5. judge メソッドで 勝敗判定 を行い、ゲームの 決着がついていない場合手順 2 へ戻る

下記のプログラムは、上記の アルゴリズム実際に記述 して〇×ゲームを遊んだ場合の ですが、〇×ゲームを 遊ぶ際 に、下記のようなプログラムを 記述して実行 するのは 大変 なので、このままでは〇×ゲームを 気軽に遊ぶ ことは できません。そこで、〇×ゲームを遊ぶ際に必要となる、上記の アルゴリズム処理 を行う メソッドを定義 することにします。

なお、下記のプログラムのそれぞれの処理が、上記のアルゴリズムのどの部分に対応しているかについて、コメントに記しました。

from marubatsu import Marubatsu

# Marubatsu クラスのインスタンスを作成する
mb = Marubatsu()

# ここから〇×ゲームのプレイを開始する
# 手順 1:〇× ゲームを再起動する
mb.restart()
# 手順 2:ゲーム盤の表示
print(mb)
# 手順 3:(1, 1) への着手を表すデータの入力
x = 1
y = 1
# 手順 4:move メソッドによる着手
mb.move(x, y)
# 手順 5:judge メソッドによる勝敗判定
print(mb.judge())
# 上記で決着がついていないことを表す playing が表示されるので手順 2 に戻る
# 手順 2:ゲーム盤の表示
print(mb)
# 手順 3:(0, 0) への着手を表すデータの入力
x = 0
y = 0
# 手順 4:move メソッドによる着手
mb.move(x, y)
# 手順 5:judge メソッドによる勝敗判定
print(mb.judge())
# 以下決着がつくまで繰り返す

実行結果

Turn o
...
...
...

playing
Turn x
...
.o.
...

playing

play メソッドの定義

具体的には、以下のような メソッドを定義 します。

  • 処理:〇×ゲームを遊ぶために必要な上記の 手順 1 ~ 5 の処理 を行う
  • 名前:〇×ゲームを 遊ぶ (play)ための 処理 を行うので、play とする
  • 入力:なし
  • 出力:なし

なお、play メソッドは 返り値を返さない ので 出力 の所に「なし」と記述しましたが、画面に文字列を表示 するという 意味 での 出力は行います

このメソッドの定義は下記のプログラムのようになります。今回の記事では、手順 1 から順番play メソッドを 実装 を行います。

def play(self):
    # 手順 1 の処理
    # 手順 2 の処理
    # 手順 3 の処理
    # 手順 4 の処理
    # 手順 5 の処理

Marubatsu.play = play

手順 1 と 2 の実装

手順 1 の「restart メソッドを使って、〇×ゲームを再起動する」は、下記のプログラムの 3 行目のように記述できます。play メソッドを 呼び出した際 に、仮引数 self に、play メソッドを 呼び出した Marubatsu クラスの インスタンスが代入 されるので、self.restart() によって 〇×ゲームの再起動 を行うことができます。

手順 2 の「print を使ってゲーム盤を表示する」は、下記のプログラムの 5 行目のように記述できます。特に難しい点はないと思いますので説明は省略します。

 1  def play(self):
 2      # 〇×ゲームを再起動する
 3      self.restart()
 4      # ゲーム盤の表示
 5      print(self)
 6      # 手順 3 の処理
 7      # 手順 4 の処理
 8      # 手順 5 の処理
 9
10  Marubatsu.play = play
行番号のないプログラム
def play(self):
    # 〇×ゲームを再起動する
    self.restart()
    # ゲーム盤の表示
    print(self)
    # 手順 3 の処理
    # 手順 4 の処理
    # 手順 5 の処理

Marubatsu.play = play

下記は、play メソッドの手順 1 と 2 の処理が 正しく実装 できたかどうかを 確認 するためのプログラムです。3 行目で、(1, 1) のマスに 着手 を行っているのは、play メソッドを実行することで、手順 1 の 〇×ゲームの 再起動 が行われているかどうかが 確認 できるようにするためです。実行結果から、〇 の手番 で、マークが 一つも配置されていない ゲーム盤が 表示 されるので、手順 1 と 手順 2 が正しく実装されていることが確認できます。

mb = Marubatsu()

mb.move(1, 1)
mb.play()

実行結果

Turn o
...
...
...

手順 3 の実装と組み込み関数 input

手順 3 の「プレイヤーに着手を行う座標を入力させる」を実装するためには、プレイヤーが どのような方法座標を入力 するかについての、ユーザーインタフェース(以下 UI と表記します)を 決める 必要があります1。現時点では、〇×ゲームの ゲーム盤を表示 する UI は、文字だけ で行う CUI なので、座標の入力UI、文字だけを使った CUI で実装 することにします。なお、ゲーム盤を画像で表示し、マウスを使って座標を入力する GUI については今後の記事で実装する予定です。

Python には、キーボードから入力 した 文字列返り値として返す という、CUI の入力処理 を行う input という 組み込み関数 があります。VSCode の JupyterLab2 では、input を呼び出す すると、下図のように VSCode の タイトルバーの付近テキストボックスが表示 され、そのテキストボックスにキーボードから 文字を入力 できるようになります。

print(input())

input を呼び出すと、上図に記述されているように、Enter キー または、Escape キー押すまで プログラムの 処理が中断 され、Enter キー を押した場合はテキストボックスに 入力した文字列 が、Escape キー を押した場合は 空文字返り値 として返されます。

下記は、上記のプログラムを実行し、テキストボックスに "abc" を入力 して Enter キー を押した場合の実行結果です。

abc

input の詳細については、下記のリンク先を参照して下さい。

手順 3 は、下記のプログラムの 7 行目のように実装できます。下記のプログラムでは、キーボードから入力した座標を表す文字列を coord という ローカル変数に代入 しています。

 1  def play(self):
 2      # 〇×ゲームを再起動する
 3      self.restart()
 4      # ゲーム盤の表示
 5      print(self)
 6      # キーボードからの座標の入力
 7      coord = input()
 8      # 手順 4 の処理
 9      # 手順 5 の処理
10
11  Marubatsu.play = play
行番号のないプログラム
def play(self):
    # 〇×ゲームを再起動する
    self.restart()
    # ゲーム盤の表示
    print(self)
    # キーボードからの座標の入力
    coord = input()
    # 手順 4 の処理
    # 手順 5 の処理

Marubatsu.play = play
修正箇所
def play(self):
    # 〇×ゲームを再起動する
    self.restart()
    # ゲーム盤の表示
    print(self)
    # キーボードからの座標の入力
+   coord = input()
    # 手順 4 の処理
    # 手順 5 の処理

Marubatsu.play = play

input に関する注意点

input返り値常に文字列型 のデータになる点に 注意が必要 です。

例えば、下記のプログラムを実行し、input で表示されるテキストボックスに 1 を入力して Enter キーを押すと下記のような エラーが発生 します。

print(input() + 2)

実行結果

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
c:\Users\ys\ai\marubatsu\037\marubatsu.ipynb セル 7 line 1
----> 1 print(input() + 2)

TypeError: can only concatenate str (not "int") to str

上記のエラーメッセージは、以下のような意味を持ちます。

  • TypeError
    データ型(type)に関するエラー
  • can only concatenate str (not "int") to str
    文字列型(str)には、(整数型ではなく(not "int"))文字列型(str)のみ(only)結合(concatenate)できる(can)

input で表示されるテキストボックスに 1 のような、数値に見える ような データを入力 しても、input返り値必ず "1" のような 文字列型のデータ になります。 従って、input入力した文字列 を、数値として扱いたい 場合は、intfloat を使って、下記のプログラムのように 整数型浮動小数点数型 のデータに 変換する必要 があります。

print(int(input()) + 2)

実行結果(テキストボックスに 1 を入力して Enter キーを押した場合)

3

同様の理由 で、テキストボックスに [1, 2] のようなデータを記述しても、input() の返り値が list になる ことは ありません。この場合は "[1, 2]" という 文字列 が返ります。

input の返り値 は、どのような場合でも 必ず文字列型 のデータになる。

input で入力した文字列を、数値型 や list などの、他のデータ型 として 扱いたい場合 は、明示的な型変換 を行う必要がある。

手順 4 の実装

手順 4 の「プレイヤーが入力した座標に move メソッドで着手を行う」では、手順 3 でテキストボックスに 入力した座標にmove メソッドで 着手を行う 必要がありますが、そのためにはテキストボックスに入力する 座標のデータ構造(どのように記述するか)を 決める必要 があります。どのようなデータ構造にすればよいかについて少し考えてみて下さい。

座標 を表す 文字列 の _データ構造 として、以下のようなものが考えられるでしょう。

  1. 数学の座標 と同様に、(1, 2) のように記述する
  2. 上記と同様だが、外側の () を省略 して 1,2 のように記述する
  3. テストケースを記述する際に利用した A1 のような Excel 座標として記述する

上記のいずれの方法を使ってもかまいませんが、本記事では 2 番目1,2 のように記述するデータ構造を 採用 することにします。3 番目の Excel 座標を採用したい人は、以前の記事 で定義した excel_to_xy を利用する必要があります。また、上記以外で、採用したいデータ構造を思いついた方は、そのデータ構造を採用したプログラムを記述してみて下さい。

2 番目のデータ構造を採用した 理由 は、以前の記事 で紹介した split メソッドを使って、下記のプログラムの 9 行目ように、文字列x 座標y 座標要素 とする list に変換 し、list の展開 を使って xyそれぞれの座標を代入 できるからです。

xy に代入されたデータは 文字列型 なので、11 行目で move を使って (x, y) に着手を行う際に、int を使って 整数型 のデータに 変換 する 必要がある点 に注意して下さい。

 1  def play(self):
 2      # 〇×ゲームを再起動する
 3      self.restart()
 4      # ゲーム盤の表示
 5      print(self)
 6      # キーボードからの座標の入力
 7      coord = input()
 8      # x 座標と y 座標の計算
 9      x, y = coord.split(",")
10      # (x, y) に着手を行う
11      self.move(int(x), int(y))
12      # 手順 5 の処理
13
14  Marubatsu.play = play
行番号のないプログラム
def play(self):
    # 〇×ゲームを再起動する
    self.restart()
    # ゲーム盤の表示
    print(self)
    # キーボードからの座標の入力
    coord = input()
    # x 座標と y 座標の計算
    x, y = coord.split(",")
    # (x, y) に着手を行う
    self.move(int(x), int(y))
    # 手順 5 の処理

Marubatsu.play = play
修正箇所
def play(self):
    # 〇×ゲームを再起動する
    self.restart()
    # ゲーム盤の表示
    print(self)
    # キーボードからの座標の入力
    coord = input()
    # x 座標と y 座標の計算
+   x, y = coord.split(",")
    # (x, y) に着手を行う
+   self.move(int(x), int(y))
    # 手順 5 の処理

Marubatsu.play = play

下記は、play メソッドを呼び出し、2 行目でゲーム盤を表示することで、手順 4 の着手 が正しく行われたかどうかを 確認 するプログラムです。下記は、テキストボックスに 1,1 を入力した場合の実行結果で、手順 3、4 の処理が正しく行われたことが確認できます。

mb.play()
print(mb)

実行結果

Turn o
...
...
...

Turn x
...
.o.
...

input でメッセージを表示する方法

テキストボックスに入力する 座標のデータ構造 は、play メソッドを記述した人は当然知っていますが、play メソッドを 利用するだけの人 はそのことを 知りません 。そのため、座標の 入力方法 という、ゲームの遊び方 を知らせたほうが 親切 です。

input実引数文字列を記述 することで、記述した文字列 が、下図のように テキストボックスの下に表示 されるので、これを利用すると良いでしょう。

input("x,y の形式で座標を入力して下さい")

下記はそのように play メソッドを修正したプログラムです。

def play(self):
    # 〇×ゲームを再起動する
    self.restart()
    # ゲーム盤の表示
    print(self)
    # キーボードからの座標の入力
    coord = input("x,y の形式で座標を入力して下さい")
    # x 座標と y 座標の計算
    x, y = coord.split(",")
    # (x, y) に着手を行う
    self.move(int(x), int(y))
    # 手順 5 の処理

Marubatsu.play = play
修正箇所
def play(self):
    # 〇×ゲームを再起動する
    self.restart()
    # ゲーム盤の表示
    print(self)
    # キーボードからの座標の入力
-   coord = input()
+   coord = input("x,y の形式で座標を入力して下さい")
    # x 座標と y 座標の計算
    x, y = coord.split(",")
    # (x, y) に着手を行う
    self.move(int(x), int(y))
    # 手順 5 の処理

Marubatsu.play = play

下記のプログラムを実行することで、修正後に play メソッドが正しく動作することを確認できます。実行結果は先ほどと同様なので省略します。

mb.play()
print(mb)

手順 5 の実装と while

手順 5 の「judge メソッドで勝敗判定を行い、ゲームの決着がついていなければ手順 2 へ戻る」という処理は、別の言葉で 言い換える と、「〇×ゲームの 決着がつくまで手順 2 ~ 5 の処理を 繰り返す」という処理です。

これまでに利用してきた、for 文 では、for i in range(10) などのように、あらかじめ 繰り返す 回数決まっている ような 繰り返し処理記述 してきました。

一方、〇×ゲームは、決着がつくまで 何回 着手を 行う必要があるか は、ゲームを行ってみなければ わからない ので、手順 2 ~ 5 の処理を 何回繰り返せばよいか を、繰り返し処理を行う前に あらかじめ知る ことは 不可能 です。

このような、繰り返す回数あらかじめ知ることができない ような繰り返しは、while 文 で記述します。while 文は下記のように記述し、条件式True の間、while 文の ブロック に記述された処理を 繰り返し実行 します。

while 条件式:
    条件式が True の間繰り返し処理を行うブロック

下図は while 文のフローチャートです。

先程のアルゴリズムの 手順 2 ~ 5 は、ゲームの決着がつくまで 無限に 手順 2 ~ 4 の処理を 繰り返す というアルゴリズムです。無限に 処理を 繰り返す ような 繰り返し処理 の事を、無限ループ と呼びます。while 文 は、条件式True の間 繰り返し処理を行うので、下記のようなプログラムで、無限ループを記述 できます。

while True:
    while 文のブロックの処理

単に無限ループを記述しただけでは、プログラムの 処理が永遠に終わらない ことになってしまいます。そのため 無限ループ記述 された プログラムを実行 すると、プログラムが 固まって動かななくなる という バグが発生 します。そのため、while 文条件式True を記述 した場合は、while 文の ブロックの中 で、特定の条件が満たされた場合break 文記述する ことで、無限ループ から 抜け出す ような処理を 記述する必要 があります。

while 文のブロックを何度繰り返しても while 文条件式False にならない ような場合も 無限ループになりますwhile 文 は、for 文と比べて 無限ループが発生しやすい ので、while 文を記述する際には 無限ループが発生しない ように 注意しながら プログラムを 記述する必要 があります。

上記のことから、手順 2 ~ 5 の処理は、下記のプログラムのように記述できます。

  • 5 行目:while 文による 無限ループを記述 する
  • 7 ~ 13 行目:元のプログラムに インデントを入れてwhile 文ブロックの処理にする
  • 15、16 行目judge メソッドの 返り値Marubatsu.PLAYING 以外 の場合は、ゲームの 決着がついた場合 なので、16 行目で break 文 を記述して 無限ループを終了 する
 1  def play(self):
 2      # 〇×ゲームを再起動する
 3      self.restart()
 4      # 無限ループを行う
 5      while True:
 6          # ゲーム盤の表示
 7          print(self)
 8          # キーボードからの座標の入力
 9          coord = input("x,y の形式で座標を入力して下さい")
10          # x 座標と y 座標の計算
11          x, y = coord.split(",")
12          # (x, y) に着手を行う
13          self.move(int(x), int(y))
14          # ゲームの決着がついた場合は、無限ループを抜ける
15          if self.judge() != Marubatsu.PLAYING:
16              break
17
18  Marubatsu.play = play
行番号のないプログラム
def play(self):
    # 〇×ゲームを再起動する
    self.restart()
    # 無限ループを行う
    while True:
        # ゲーム盤の表示
        print(self)
        # キーボードからの座標の入力
        coord = input("x,y の形式で座標を入力して下さい")
        # x 座標と y 座標の計算
        x, y = coord.split(",")
        # (x, y) に着手を行う
        self.move(int(x), int(y))
        # ゲームの決着がついた場合は、無限ループを抜ける
        if self.judge() != Marubatsu.PLAYING:
            break

Marubatsu.play = play
修正箇所(while 文のブロックのインデントの追加に関する修正箇所は、表示するとわかりづらくなるので省略しました)
def play(self):
    # 〇×ゲームを再起動する
    self.restart()
    # 無限ループを行う
+   while True:
        # ゲーム盤の表示
        print(self)
        # キーボードからの座標の入力
        coord = input("x,y の形式で座標を入力して下さい")
        # x 座標と y 座標の計算
        x, y = coord.split(",")
        # (x, y) に着手を行う
        self.move(int(x), int(y))
        # ゲームの決着がついた場合は、無限ループを抜ける
+       if self.judge() != Marubatsu.PLAYING:
+           break

Marubatsu.play = play

下記のプログラムを実行し、0,00,11,01,12,0 の順でテキストボックスに入力すると、下記のような実行結果が表示されます。(2, 0)着手を行う 事で 〇が勝利する ため 16 行目の break 文が実行 されるため、無限ループが終了 します。そのことは、テキストボックスが表示されなくなる ことで確認できます。

mb.play()

実行結果

Turn o
...
...
...

Turn x
o..
...
...

Turn o
o..
x..
...

Turn x
oo.
x..
...

Turn o
oo.
xx.
...

VSCode の JupyterLab では、セルの プログラムが実行中 の場合は、下図のように、セルの下矢印が回転 するな アニメーションが表示 されます。

また、セルの プログラムの実行が終了 すると、下図のように、セルの下緑色のチェックマーク と、そのセルの プログラムの処理にかかった時間が表示 されるので、この部分を見ることでも プログラムの実行が終了しているか確認 できます。

手順 5 の別の記述方法

上記のプログラムの アルゴリズム は、無限ループブロックの中 で、無限ループを抜け出す ための 条件式を記述 し、その 条件式が True になった場合に break 文 を実行して 無限ループから抜け出す というものです。while 文条件式 は、繰り返し続ける ための 条件 なので、while 文の 条件式 に繰り返しを 続けるための条件式記述 することで、break 文記述する必要なくなります。具体的には、下記のプログラムのように記述します。

  • 5 行目:while 文の条件式に、決着がついていない ことを 判定 する 式を記述する
  • 元のプログラムにあった、if 文と break 文を削除 する

while 文条件式 は、先程のプログラムの if 文の条件式 で、繰り返しを 続けるための条件 を表すので、!= ではなく == を記述 する必要がある点に 注意 して下さい。

 1  def play(self):
 2      # 〇×ゲームを再起動する
 3      self.restart()
 4      # ゲームの決着がついていない間繰り返す
 5      while self.judge() == Marubatsu.PLAYING:
 6          # ゲーム盤の表示
 7          print(self)
 8          # キーボードからの座標の入力
 9          coord = input("x,y の形式で座標を入力して下さい")
10          # x 座標と y 座標の計算
11          x, y = coord.split(",")
12          # (x, y) に着手を行う
13          self.move(int(x), int(y))
14
15  Marubatsu.play = play
行番号のないプログラム
def play(self):
    # 〇×ゲームを再起動する
    self.restart()
    # ゲームの決着がついていない間繰り返す
    while self.judge() == Marubatsu.PLAYING:
        # ゲーム盤の表示
        print(self)
        # キーボードからの座標の入力
        coord = input("x,y の形式で座標を入力して下さい")
        # x 座標と y 座標の計算
        x, y = coord.split(",")
        # (x, y) に着手を行う
        self.move(int(x), int(y))

Marubatsu.play = play
修正箇所
def play(self):
    # 〇×ゲームを再起動する
    self.restart()
    # ゲームの決着がついていない間繰り返す
-   while True:
+   while self.judge() == Marubatsu.PLAYING:
        # ゲーム盤の表示
        print(self)
        # キーボードからの座標の入力
        coord = input("x,y の形式で座標を入力して下さい")
        # x 座標と y 座標の計算
        x, y = coord.split(",")
        # (x, y) に着手を行う
        self.move(int(x), int(y))
-       if self.judge() != Marubatsu.PLAYING:
-           break
           
Marubatsu.play = play

下記のプログラムを実行し、0,00,11,01,12,0 の順でテキストボックスに入力することで、play メソッドが正しく動作することを確認できます。実行結果は先ほどと同様なので省略します。

mb.play()

while 文の記述に関する補足

while 文のブロックの中に break を記述して繰り返しを抜けるよりも、上記のように while 文条件式 に繰り返しの 続行を判定する式 を記述したほうがプログラムが わかりやすい ので、一般的 には while 文は 上記のように記述 したほうが 良い でしょう。本記事でも上記の記述方法を採用することにします。

ただし、常に 上記のような記述をすれば良いとは 限りません。例えば、上記のような記述をしてしまうと、while 文を実行した際に、最初から while 文の 条件式が False になってしまう場合は、一度も while 文の ブロックが実行されません 。そのため、while 文のブロック を最低でも 1 度は実行したい 場合は、while 文の ブロックの最後 に無限ループを 抜けるためif 文と break 文を記述 する必要があります。他にも、while 文のブロックの 繰り返しを続行 する 条件が複雑 な場合は、while 文の 条件式が長くなる ため、if 文と break 文 を使ったほうが わかりやすい でしょう。言葉だけの説明ではわかりづらいと思いますが、今後の記事で具体的な実例が出た際に、このことを詳しく説明することにします。

play メソッドの問題点

play メソッドを実装したことで、座標をテキストボックスに入力するだけで〇×ゲームを遊べるようになりましたが、現状の play メソッドには いくつかの問題 があるので、次回の記事ではそれらの 問題点を修正 して play メソッドを 改良 することにします。

次回の記事を見る前に、実際に 何度か play メソッドを 実行 して〇×ゲームを 遊んでみてどのような問題があるか を考えておいてください。

本記事で入力したプログラム

以下のリンクから、本記事で入力して実行した JupyterLab のファイルを見ることができます。

以下のリンクは、今回の記事で更新した marubatsu.py です。

次回の記事

  1. ユーザーインターフェース、CUI、GUI などの用語の意味について忘れてしまった方は、以前の記事 を参照してください

  2. VSCode 以外の JupyterLab の場合などでは、input() を実行した JupyterLab のセルのすぐ下にテキストボックスが表示される場合があります。また、JupyterLab 以外の方法で Python のプログラムを実行した場合などで、テキストボックスが表示されない場合もありますが、いずれの場合でも、何らかの方法キーボードから文字を入力できるようになる 点に変わりはありません

0
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0