CHaser解説(Python編)

第2部 Python編

0章 Pythonの基礎と、CHaserの書き方

第2部では、CHaserのプログラムをPythonで書くための道具を、順番に学びます。

この部の読み方

入門編では、ステージごとに「課題 → ヒント → 挑戦 → 解答例」の順で、自分でブロックを組む練習をしました。この本では、その考え方をPythonの文法でどう書くかを中心に説明します。だから、ステージの課題を1つずつ解く形ではなく、書き方の解説と、コードの例、つまずきやすい点を中心に進めます。

この部のコードは、チュートリアルのPython画面にそのまま入力できる書き方(walk(Up))で書きます。本番用(Pythonの画面・対戦)では、命令の前にplayer.を付けます。違いは、0章で説明します。

各章は、入門編の次の章に対応しています。

この本の章 内容 入門編の章(ステージ)
0章 Pythonの基礎と、CHaserの書き方(チュートリアルと本番の違い) 第2部0章
1章 移動と繰り返し 第2部1・2章(ステージ1・2)
2章 情報の読み取りと条件分岐 第2部3・4章(ステージ3)
3章 探査と、行動の決め方 第2部5・6章(ステージ4)
4章 ターンの仕組みと、攻撃 第2部7・8章(ステージ5・6)
5章 変数と関数 第2部9章(ステージ7)
6章 総合演習と、Blockly⇔Python対応表 第2部10章(ステージ8)

入門編を読んでいない人のために、各章の最初に「CHaserのしくみの要点」を置きました。入門編を読んだ人は、飛ばして構いません。

0章 Pythonの基礎と、CHaserの書き方

この章では、CHaserのプログラムをPythonで書く場所と、その書き方(2通り)、Pythonの基本ルールを確認します。Pythonを知っている人は、「Pythonを書く2つの場所」から「CHaserで使う名前」までを読めば十分です。

Pythonを書く2つの場所

CHaserには、Pythonのコードを書く場所が2つあります。

チュートリアル(Python) Pythonの画面・対戦
開き方 ステージ選択画面の右上を「Python」に切り替えて、ステージを選ぶ トップ画面の「Python」
目的 ステージをPythonでクリアする練習 対戦用のプログラムを書く・対戦する
命令の書き方 名前だけ:walk(Up) player.を付ける:player.walk(Up)
準備 いらない(importも、接続もなし) 骨組み(下)を使う

この本の第2部のコードは、チュートリアルにそのまま入力できる書き方(walk(Up))で書きます。本番用に使うときは、命令の前にplayer.を付け、骨組みの中に入れます(walk(Up) → player.walk(Up))。第3部の実践では、本番用の書き方で書きます。

チュートリアルのPythonは「簡易版」

チュートリアルのPython画面は、walk(Up)と書くだけで、キャラクターを動かせるように作られた簡易版です。書いたコードは、画面が、CHaserのステージ用に変換してから動かしています。だから、書いたコードは、普通のPythonとしては動きません。たとえば、walk(Up)だけを書いたファイルを、パソコンのPythonで動かすと、「walkという名前がありません(NameError)」というエラーになります。walkなどの命令や、方角の名前は、この画面が用意しているからです。

普通のPythonにはない、次のような決まりと制限があります。

  • 命令の結果が、map_infoなどの変数に、自動で入る(普通のPythonでは、map_info = ...と書かない限り、変数は変わりません)。
  • CPUのいないステージでは、開始時点のmap_infoが最初から入っていて、get_ready()を書かなくても動く。
  • importなしで、UpやBlockが使える。
  • 次の書き方は、エラーになる(実機で確かめた):関数の外のreturn、[walk(Up) for ...]のような内包表記やlambdaの中で命令を使うこと、f = walkのように( )を付けずに命令の名前だけを使うこと。命令は、for文やdef(関数)の中でも、walk(Up)のように書きます。

対戦用のPythonの画面(第3部)は、こちらが正式な形です。命令をplayer.walk(Up)と書き、骨組みの中にコードを書き、map_info = player.get_ready()で自分で受け取ります。チュートリアルで書いたコードは、そのまま本番に貼っても動きません。命令の前にplayer.を付けるなど、書き換えが必要です。チュートリアルは、考え方を練習するための簡易版、本番は、実際に対戦するためのプログラム、と使い分けてください。

チュートリアルのPython画面

ステージ選択画面の右上にある「ブロック/Python」の切り替えを「Python」にしてからステージを選ぶと、Pythonのチュートリアル画面が開きます。切り替えは、次に開いたときにも残ります。ステージのクリア済みの表示は、ブロックとPythonで別々に管理されます。

図:チュートリアルのPython画面(ステージ1-1)。左がエディタ、右が盤面と出力欄
図:チュートリアルのPython画面(ステージ1-1)。左がエディタ、右が盤面と出力欄

画面は、左のエディタ、右の盤面と「残りターン数」、その下の出力欄でできています。ヘッダーのボタンは「実行」「停止」「保存」「開く」で、実行中は「実行」が「リセット」に変わります。「保存」「開く」は、Pythonのファイル(.py)の書き出しと読み込みです。

この画面には、次の決まりがあります。

  • 命令は名前だけで書く:walk(Up)・look(Up)・search(Up)・put(Up)・get_ready()です。player.walk(Up)と書くと、「player という名前はまだ決まっていません」というエラーになります。
  • importなしで、方角(Upなど)とマスの種類(Floor・Block・Item・Enemy)が使える:乱数を使うときだけ、先頭にimport randomと書きます。
  • 命令の結果は、自動で変数に入る:walk・put・get_readyのあとの周辺情報はmap_infoに、lookの結果はlook_infoに、searchの結果はsearch_infoに入ります。map_info = get_ready()のように、戻り値として受け取る書き方でも構いません。
  • get_ready()が必要なのは、相手(CPU)のいるステージ:CPUのいないステージ(ステージ1〜4、7、8など)では、開始時点の周辺情報がすでにmap_infoに入っていて、get_ready()を書かなくても動きます(書いても動きます)。CPUのいるステージ(5-1、6-1など)では、毎ターンget_ready()が必要です。書かないと、最初の1回動いたところで止まります。
  • ブロック数の目標は、判定しない:ステージ2-1〜2-5には行数の目安があり、超えていてもクリアはできます(クリア後に、「くり返し(for)や関数(def)を使うと、もっと短く書けるよ!」と表示されます)。
  • first_indexは使えない:Blocklyの「最初の出現箇所を検索」ブロックをPythonに変換すると出てくるfirst_index(…)は、この画面では定義されていません。かわりにinを使います(3章)。
  • 出力欄:print()の結果や、エラー(行番号つき)、「<< 停止しました >>」などが表示されます。10秒たっても次の行動(walkなど)が出てこないと、止まります。

本番用の骨組み(Pythonの画面・対戦)

ここまで説明してきた、チュートリアルのPythonは、簡易版でした。ここからが、対戦で実際に使う、正式なPythonのプログラムの形です。

Pythonの画面(第3部1章)を開くと、最初から次のようなプログラムが書かれています。これが、本番用のCHaserのプログラムの骨組みです。

import argparse
from lib.pyCHaser import * # lib/pyCHaser.py

player = None
map_info = []

def main(port, name, host):
    global player, map_info

    # サーバーと接続
    player = Client(port=port, name=name, host=host)

    while True:
        # 準備完了 & 周辺探査
        map_info = player.get_ready()
        # ブロックがなければ上へ、あれば右へ
        if map_info[Up] != Block:
            player.walk(Up)
        else:
            player.walk(Right)

その下に、プログラムを起動するための、決まった書き方が続きます。ここは書き換えません。

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-p", "--port", default=2009)
    parser.add_argument("-n", "--name", default="player")
    parser.add_argument("-i", "--host", default="localhost")
    args = parser.parse_args()
    main(port=args.port, name=args.name, host=args.host)

全部を理解する必要はありません。自分で書き換えるのは、while True:の中だけです。ほかの部分は、次のような役割です。

  • from lib.pyCHaser import *:CHaserを動かすための道具を読み込みます。これで、Client、方角の名前(Upなど)、マスの種類の名前(Blockなど)、乱数の道具(random)が使えるようになります。
  • player = Client(...):ゲームに接続して、キャラクターを動かすためのplayerを作ります。以降、player.walk(Up)のように書いて命令を出します。チュートリアルの画面では、このplayerがないので、walk(Up)と書きます。
  • global player, map_info:playerとmap_infoは、プログラムの一番上(mainの外)で作った変数です。globalと書くことで、mainの中での代入がその変数に反映されます。だから、自分で作る関数(5章)の中でも、map_infoとplayerをそのまま使えます。
  • while True::ずっと繰り返す、という意味です(後で説明します)。CHaserのプログラムは、何ターン続くか分からないので、基本的にこの繰り返しで全体を包みます。
  • map_info = player.get_ready():自分の番が来るまで待って、来たら自分の周りの9マスの情報を受け取ります。本番では、map_infoに自動では入らないので、この形で受け取ります。

チュートリアルで書いたコードを、本番に持っていくときは、命令の前にplayer.を付けて、while True:の中身は骨組みのwhile True:の中に、自分で作る関数はdef mainより上に、貼り付けてください。 また、本番では、行動の前に必ずmap_info = player.get_ready()が必要です(4章)。

CHaserで使う名前

CHaserのプログラムで使う名前は、次のとおりです。

命令

チュートリアルの書き方 本番用の書き方 意味
get_ready() player.get_ready() 自分の番を待ち、周辺情報(周囲9マス)を返す
walk(方向) player.walk(方向) 方向に1マス移動する
put(方向) player.put(方向) 方向のマスにブロックを置く
look(方向) player.look(方向) 方向の2マス先を中心とした周囲9マスを調べる(近隣探査)
search(方向) player.search(方向) 方向に一直線9マスを調べる(遠方探査)

方向とマスの種類(どちらの書き方でも同じ)

種類 名前
方向(移動・設置・探査の指定に使える) Up・Down・Left・Right
方角(周辺情報の取り出しに使う) 上の4つ、UpLeft・UpRight・DownLeft・DownRight・Center(自分自身のマス)
マスの種類 Floor(何もない)・Block(ブロック)・Item(アイテム)・Enemy(相手)

これらの名前は、クォート(")で囲まない、あらかじめ用意された値です。実際には、方角の名前はUpLeft = 0、Up = 1、…、DownRight = 8という整数の別名で、map_infoは9個の要素を持つただのリストです。だからmap_info[Up]は、リストの1番目(Upは1)を取り出しています。

Pythonの基本ルール

変数と代入

値に名前を付けて覚えておく仕組みを変数といいます。

direction = Right

これは「directionという名前の変数に、Rightという値を入れる(代入する)」という意味です。左側が変数名、=が代入の記号、右側が入れたい値です。数学の「等しい」という意味の=とは違うので注意してください。

比較演算子

「等しいかどうか」を調べるときは、=を2つ重ねた==を使います。等しくないかは!=、大小は>・<・>=・<=です。

map_info[Up] == Block    # 上がブロックか(TrueかFalseになる)

=(代入)と==(比較)を混同するのは、初心者がもっともよくやってしまう間違いの1つです。

if文(条件分岐)と、インデント

「もし〇〇なら」を表すのがifです。

if map_info[Up] == Block:
    player.walk(Right)
elif map_info[Left] == Item:
    player.walk(Left)
else:
    player.walk(Up)

ifの条件の最後には:(コロン)を付けます。条件が成り立ったときに実行したい処理は、次の行から字下げ(インデント)して書きます。Blocklyでは、ブロックの「内側」にはめ込む形で表していた「中に入っているかどうか」を、Pythonではインデントの深さで表します。同じ深さの行は同じグループで、字下げが浅くなると、そのグループから抜けたことになります。本書では、1段につき半角スペース4つで揃えます。elifは「そうでなくもし」、elseは「どれにも当てはまらなければ」です。上から順に調べて、最初に成り立ったところだけが実行されます。

for文とwhile文(繰り返し)

回数が決まっている繰り返しにはfor、回数が決まっていない繰り返しにはwhileを使います。

for count in range(4):    # 4回繰り返す
    player.walk(Right)

while True:               # ずっと繰り返す
    player.walk(Right)

range(4)は「0, 1, 2, 3」という4つの数字の並びを作ります。for count in range(4):は、その4つの数字を順にcountに入れながら、インデントされた部分を4回繰り返します。while 条件:は、条件が成り立つ間、繰り返します。while True:は条件が常に成り立つので、ずっと繰り返します。

コメント

#から行の終わりまでは、プログラムの動作に影響しない、人間が読むためのメモ(コメント)です。判断の意図を書き残しておくと、後で読み返すときに役に立ちます。

リストと、番号の指定

複数の値を順に並べてまとめて扱うものをリストといいます。map_infoもリストです。角かっこ[ ]に番号か名前(Upなど)を書いて、1つの値を取り出します。

free = []                # 空のリスト
free.append(Up)          # 最後に追加する
free[0]                  # 最初の要素を取り出す(番号は0から数える)

番号は0から数え始めます。searchの結果(search_info[0]が隣、search_info[8]がいちばん遠い)も、この数え方です(3章)。

変数名について(ローマ字の名前)

チュートリアルの画面で、日本語の名前を付けたブロックの変数や関数をPythonに変換すると、ido_hoko(移動方向)のようなローマ字の名前になります。この本のコードでは、directionのように英単語の名前を使いますが、変換されたコードを読むときは「もとは日本語の名前だった」と考えると、意味が追いやすくなります。

注意したいこと

  • =と==を混同する:「代入」と「比較」は見た目が似ていますが、まったく違う意味です。ifの条件の中では必ず==を使います。
  • インデントがずれる:コピー&ペーストをしたときや、タブとスペースを混ぜたときに、インデントが揃わずエラーになることがあります。字下げの深さを目で確認する癖をつけましょう。
  • :(コロン)を忘れる:if、elif、else、for、while、後で学ぶdefの行末には、必ず:が必要です。
  • チュートリアルでplayer.を付けてしまう/本番でplayer.を忘れる:チュートリアルではplayer.walk(Up)が、本番ではwalk(Up)が、それぞれエラーになります。エラーの行番号で、命令の書き方を見直しましょう。
  • while True:の外に、行動を書いてしまう:骨組みのwhile True:の外に書いた命令は、1回しか実行されません。毎ターン実行したい処理は、while True:の中に書きます。

演習問題

  1. チュートリアルのステージ1-1をPythonで解いてみましょう(ステージ選択画面の右上を「Python」に切り替えます)。上に2マス進むには、どう書きますか。
  2. 本番用の骨組みを、Pythonの画面で動かして、キャラクターがどう動くか観察してみましょう。「上がブロックでないとき」と「ブロックのとき」で、どう動きが変わりますか。
  3. 骨組みのif map_info[Up] != Block:を==に書き換えると、動きはどう変わりますか。実際に動かして確かめてみましょう。

YouTubeチャンネル

U-15プロコン舞鶴のYouTubeチャンネルを開設しました。大会の情報などを発信しています。 チャンネルを見る