GitHub Copilotを用いると、どこまでプロ演の課題を横着できるのか

watnowという学生団体に所属している、1回生のるいです。小中学生のときMinecraftサーバーのプラグインを作ったりScratchをガチでやったりしてました。Qiita初投稿です。どうやったら綺麗に書けるんだよコレ

この記事は[ watnow Advent Calender 2021](https://qiita.com/advent-calendar/2021/watnow-calendar) 15日目のエントリーです。

### Q.なぜ投稿日が12/26なんですか？
**26日に書き終わったから**です。

![dogeza.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/52133fff-a9f4-3d28-acb3-5559bda7a558.jpeg)
さっき友達に土下座の写真を撮ってもらいました。おれが今この場で表せる最大の気持ちです。遅れて申し訳ありませんでした。

さて今回は、**「GitHub Copilot」**（以下「Copilot」とする）を使って、大学のプログラミング演習（以下「プロ演」とする）の課題をどれだけズルできるのかについて、実験していきたいと思います。
# GitHub Copilotって何
AIが文脈を判断し、**続きのコードを勝手に予測**して、自動生成したものを提案してくれる、テキストエディタ用拡張機能です。現在テクニカルプレビュー版として公開されているため、Copilotを利用するには[ここ](https://copilot.github.com/)にメールアドレスを登録して順番待ちに並ばないといけません。筆者は5ヶ月ぐらい待たされました。

https://copilot.github.com/

Copilotは、ソースコードに書かれたコメント、出力文字列、変数名、関数名など様々な要素から文脈を判断するらしいです。つまり、詳細にコードの書き方に関するヒントを与えれば与えるほど、求めている動作をするように正確なコードを生成してくれると予想できます。

したがって今回の実験では、与えるヒントの量を減らしてゆくことにより、推定の正確性がどれほど低下していくのか（＝**打ち込むコードの量がどれほど増えるのか**）を調べ、それぞれの場合における面倒さをなるべく客観的に評価します。

なおこの記事は *「元々プログラミングはちょっとわかるし、プロ演の授業なんかいらねー４（よん）」* という方を想定して書かれているため、そうじゃない学生の皆様は**真似しない方がいい**です。
# 実験
早速実験を始めていきます。

## 目的
現代の大学のプロ演課題横着業界において、GitHub Copilotの有用性を確かめ、最も少ない手数で効率的に課題を横着する方法を確立する。

## 手法
「書きたいプログラムの説明が多ければ多いほど生成する精度があがる」というCopilotの性質を仮定して、与えるヒントの**具体性を少しずつ下げていく**ことによるCopilotの**反応の変化を観察**する。その後、それぞれの場合に要した**総合的な労力を評価**し、Copilotを用いて課題を横着するために最も労力の少ない手法を検討する。

なお、著作権上の問題でプロ演の課題レジュメをそのまま利用することはできないため、本実験のために作成した類似の問題を解くことについて考えるとする。

## 環境
- macOS BigSur 11.6.1
- Python 3.8.9 64-bit
- VSCode 1.63.1
- GitHub Copilot v.1.7.4273 preview

## 課題
> 勇者の前にゴーレムが現れた！しかし、ゴーレムのレベルが高いため、戦い続けることができなければ、逃げることも考えている。
ゴーレムに対して戦うか逃げるかを選択しながらゴーレムを倒すゲームのプログラムを作成せよ。
勇者、ゴーレムのHPはそれぞれ100、200であり、勇者とゴーレムのそれぞれの攻撃力は、30と50である。
コマンドとして「たたかう／にげる」を選択できるようにすること。「たたかう」を選択すると、50％の確率でプレーヤーのHPを30減らし、100％の確率でゴーレムのHPを50減らしたあと、それぞれのHPを表示すること。「にげる」を選択した時、その時点で繰り返し処理を抜け出すこと。勇者のHPが0になったら、勇者が倒れたメッセージを表示し、ゴーレムのHPが0になったら、ゴーレムを倒したメッセージを表示すること。

実行例

```console:実行例A
勇者の前にゴーレムが現れた！
ゴーレムのHP：200　　勇者のHP：100
[0] たたかう / [1] にげる：0
ゴーレムの攻撃！勇者に30のダメージ！
勇者の攻撃！ゴーレムに50のダメージ！
ゴーレムのHP：150　　勇者のHP：70
[0] たたかう / [1] にげる：0
勇者の攻撃！ゴーレムに50のダメージ！
ゴーレムのHP：100　　勇者のHP：70
[0] たたかう / [1] にげる：0
ゴーレムの攻撃！勇者に30のダメージ！
勇者の攻撃！ゴーレムに50のダメージ！
ゴーレムのHP：50　　勇者のHP：40
[0] たたかう / [1] にげる：0
勇者の攻撃！ゴーレムに50のダメージ！
ゴーレムのHP：0　　勇者のHP：40
ゴーレムを倒した！
```


```console:実行例B
勇者の前にゴーレムが現れた！
ゴーレムのHP：200　　勇者のHP：100
[0] たたかう / [1] にげる：2
[0] たたかう / [1] にげる：1
勇者はゴーレムからうまく逃げ切れた！
勇者のHP：70
```


```console:実行例C
勇者の前にゴーレムが現れた！
ゴーレムのHP：200　　勇者のHP：100
[0] たたかう / [1] にげる：0
ゴーレムの攻撃！勇者に30のダメージ！
勇者の攻撃！ゴーレムに50のダメージ！
ゴーレムのHP：150　　勇者のHP：70
[0] たたかう / [1] にげる：0
ゴーレムの攻撃！勇者に30のダメージ！
勇者の攻撃！ゴーレムに50のダメージ！
ゴーレムのHP：150　　勇者のHP：40
[0] たたかう / [1] にげる：0
ゴーレムの攻撃！勇者に30のダメージ！
勇者の攻撃！ゴーレムに50のダメージ！
ゴーレムのHP：100　　勇者のHP：10
[0] たたかう / [1] にげる：0
ゴーレムの攻撃！勇者に30のダメージ！
勇者はゴーレムに倒されてしまった…
```


## 課題をかみ砕く
課題から得られる情報から、作らなければならないプログラムのことをまず整理し、Copilotにプログラムについてを説明する文章を数パターン用意する。
また、それぞれの説明方式を比較しやすくするために、次の方式で主観的に5段階評価を行う。
> 抽象度：プログラムの説明の曖昧さの度合い。高いほどCopilotは正確な予測がしにくいと考えられる。
> 書きたくない度：学生がその説明を書くために必要な労力の度合い。

### 1. [問題文](#1-問題文-1)
抽象度 ★★★☆☆　|　書きたくない度 ☆☆☆☆☆
私たちが具体例によって理解を深めるように、Copilotも出力例などがコメントにあればヒントの一つとして採用してくれるらしい。
この問題文自体は「とりあえずプログラムの概要だけが理解できるような説明」であり、十分に具体的とは言えない。しかし、全て処理パターンに到達できている、非常に丁寧な出力例が添えられている。抽象度はこれを基準として他と比較するため、星3とする。
またこのままソースコードにコピペしてコメントに変えればいいだけなので書きたくない度は最小の星0とする。

### 2. [ルール](#2-ルール-1)
抽象度 ★☆☆☆☆　|　書きたくない度 ★★★★☆

問題文と出力例から次のようにプログラムの仕様を定めることができる。

>- 勇者のHPは100、ゴーレムのHPは200である。
>- ターンが始まる前に、現在のキャラクターのHPが次のように表示される：
> <br> 「ゴーレムのHP：100　　勇者のHP：70」
>- ターンの始めに「[0] たたかう / [1] にげる：」と問いかけ、入力に従って行動する。
>- ゴーレムは毎回先行ターンで半分の確率で勇者に攻撃し、30のダメージを与える。
>- ゴーレムの攻撃が通ると次のように表示される：
> <br> 「ゴーレムの攻撃！勇者に30のダメージ！」
>- 勇者が攻撃をすると必ずゴーレムに50のダメージを与える。
>- 勇者の攻撃が通ると次のように表示される：
> <br> 「勇者の攻撃！ゴーレムに50のダメージ！」
>- 勇者が逃げることを選択した場合は「勇者はゴーレムからうまく逃げ切れた！」と表示したのち、勇者だけのHPを「勇者のHP：70」のように表示し、プログラムを終了する。
>- 勇者のHPが0になった場合は「勇者はゴーレムに倒されてしまった…」と表示し、プログラムを終了する。
>- 不正な値が入力された時は、有効な値が入力されるまで同じプロンプトをし続ける。

これらはプログラミングの知識自体が0の学生でもできる、ある程度手続き的に処理の**内容を整理**した説明である。なおかつ、これを見れば求められるプログラムを作成することができるような内容になっているため、抽象度は低い。
しかし、多くの人はこの文を書くのがかなり面倒だと思うので、**書きたくない度は高め**に設定した。

### 3. [ストーリー](#3-ストーリー-1)
抽象度 ★★★★★　|　書きたくない度 ★☆☆☆☆

また、あえて問題文をより抽象化した、「ゲームチュートリアル」や「ストーリー」のようなものも作ってみる。

>勇者の前にゴーレムが現れた！しかし、ゴーレムのレベルが高いため、戦い続けることができなければ、逃げることも考えている。
>「たたかう」か「にげる」かを選択して、ゲームを攻略しよう！勇者は素早く動けるので、毎回50ダメージの攻撃が成功し、ゴーレムの攻撃をかわすことができる！しかし、たまに命中して、30のダメージを負ってしまうので注意しよう！

これを解釈して、求められるプログラムを作成するには「HPが0になるとキャラクターが倒れる」等といった、**ゲームについての一般的な常識が必要**である。Copilotがそういった常識をどこまで持ち合わせているのかが測れるかもしれない。

### 4. [行ごとに処理の説明](#4-行ごとに処理の説明-1)

抽象度 ☆☆☆☆☆　|　書きたくない度 ★★★★★

```python
# randomモジュールをインポート
# hero_hp変数に100を、golem_hp変数に200を代入
# 「勇者の前にゴーレムが現れた！」と表示する
# 無限ループを行う
    # 「ゴーレムのHP：【変数golem_hp】   勇者のHP：【変数hero_hp】」のように表示する
    # 「"[0]たたかう / [1]にげる：」というプロンプトでコマンドの入力を要求し、selection変数に代入
    # 変数selectionの値が0か1かに応じて、処理を変更する条件分岐
    # 変数selectionが、0だった場合
        # randomモジュールを用いて、50%の確率で処理がされる条件分岐を行う
            # 変数hero_hpの値を30減らし、「ゴーレムの攻撃！勇者に30のダメージ！」と表示する
        # 変数hero_hpが0以下となった場合の条件分岐
            # 「勇者はゴーレムに倒されてしまった...」と表示し、無限ループから抜け出す
        # 変数golem_hpの値を50減らし、「勇者の攻撃！ゴーレムに50のダメージ！」と表示する
        # 変数golem_hpが0以下となった場合の条件分岐
            # 「ゴーレムを倒した！」、「勇者のHP：【変数hero_hp】」と、2つのメッセージを表示し、無限ループから抜け出す
    # 変数selectionが1だった場合
        # 「勇者はゴーレムからうまく逃げ切れた！」、「勇者のHP：【変数hero_hp】」と、2つのメッセージを表示し、無限ループから抜け出す
```

日本語プログラミング言語のように、全ての関数を完全に手続き的に説明する方法であり、本実験における**極端に具体的なもの**だ。Copilotの**最も基本的な性能**を知ることができる。

## 模範解答

デバッグやググる時間を含めて**9分47秒**かかった。

```python:sample_answer.py
import random

hero_hp = 100
golem_hp = 200

print("勇者の前にゴーレムが現れた！")

while True:
    print(f"ゴーレムのHP：{golem_hp}    勇者のHP：{hero_hp}")
    selection = int(input("[0]たたかう / [1]にげる："))
    if selection == 0:
        if random.randrange(2):
            hero_hp -= 30
            print("ゴーレムの攻撃！勇者に30のダメージ！")
        
        if hero_hp <= 0:
            print("勇者はゴーレムに倒されてしまった...")
            break

        golem_hp -= 50
        print("勇者の攻撃！ゴーレムに50のダメージ！")

        if golem_hp <= 0:
            print("ゴーレムを倒した！")
            print(f"勇者のHP：{hero_hp}")
            break

    elif selection == 1:
        print("勇者はゴーレムからうまく逃げ切れた！")
        print(f"勇者のHP：{hero_hp}")
        break

# 人間にはこのように無駄のない美しいコードが書けます
```

## 検証
実際に上の説明文をそれぞれコメントとして記入した。
それぞれの文脈に対する、Copilotの反応を観察する。

### 1. [問題文](#1-問題文)
#### 反応
右下の`UTF-8 LF Python`と書かれた隣のロボットのマークが読み込みアニメーションになっているとき、Copilotが考えてる。
![output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/4999941e-8d80-e96d-7827-020863c88339.gif)



```python:生成されたソースコード
import random

hero_hp = 100
hero_attack = 50
golem_hp = 200
golem_attack = 30

print("勇者の前にゴーレムが現れた！")
print("ゴーレムのHP：{}　　勇者のHP：{}".format(golem_hp, hero_hp))

while True:
    print("[0] たたかう / [1] にげる：", end="")
    command = int(input())
    if command == 0:
        if random.random() < 0.5:
            hero_hp -= golem_attack
            print("ゴーレムの攻撃！勇者に{}のダメージ！".format(golem_attack))
        else:
            print("勇者の攻撃！ゴーレムに{}のダメージ！".format(hero_attack))
        golem_hp -= hero_attack
        print("ゴーレムのHP：{}　　勇者のHP：{}".format(golem_hp, hero_hp))
    elif command == 1:
        print("勇者はゴーレムからうまく逃げ切れた！")
        print("勇者のHP：{}".format(hero_hp))
        break
    else:
        print("そのコマンドはありません。")
```

<details><summary>実行例</summary><div>

```text:実行例
勇者の前にゴーレムが現れた！
ゴーレムのHP：200　　勇者のHP：100
[0] たたかう / [1] にげる：0
勇者の攻撃！ゴーレムに50のダメージ！
ゴーレムのHP：150　　勇者のHP：100
[0] たたかう / [1] にげる：0
ゴーレムの攻撃！勇者に30のダメージ！
ゴーレムのHP：100　　勇者のHP：70
[0] たたかう / [1] にげる：0
ゴーレムの攻撃！勇者に30のダメージ！
ゴーレムのHP：50　　勇者のHP：40
[0] たたかう / [1] にげる：0
勇者の攻撃！ゴーレムに50のダメージ！
ゴーレムのHP：0　　勇者のHP：40
[0] たたかう / [1] にげる：0
勇者の攻撃！ゴーレムに50のダメージ！
ゴーレムのHP：-50　　勇者のHP：40
[0] たたかう / [1] にげる：0
ゴーレムの攻撃！勇者に30のダメージ！
ゴーレムのHP：-100　　勇者のHP：10
[0] たたかう / [1] にげる：0
ゴーレムの攻撃！勇者に30のダメージ！
ゴーレムのHP：-150　　勇者のHP：-20
[0] たたかう / [1] にげる：1
勇者はゴーレムからうまく逃げ切れた！
勇者のHP：-20
%
```

</div></details>

え
<br>

**怖（こえ）ぇ〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜** 何これ、おれってもしかして思考盗聴されとる？なぁ聞いてますかOpenAI社バーカw　おい！！！全員、逃げた方がいい！！！！！！！！！ ***今すぐ***

細かく見れば欠陥だらけだが、概ねそれらしく動きそうなプログラムが`while True:`で刺激した途端に一気に生成された。
普通にすごすぎない？おれはすごすぎると思います...

#### デバッグ・修正
生成されたプログラムの問題を修正していく。

生成されたソースコードには問題文で求められていた **「キャラクターの死亡判定」の要素**が含まれていない。条件を網羅した出力例を用意しても、その意図をCopilotが汲み取ることはできなかったようだ。
その代わりに、**「50％の確率で」の文脈**は使い方を間違えているものの汲み取ろうとしている様子が見られる。これは最初にユーザーが`import random`で誘導したからということもあるかもしれない。

`if`で誘導すると求めていた処理がさらに姿を表し始めた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/b9e8f00f-f4d3-2a55-8d5d-2fda1fb44bf1.png)

Copilotの助けを借りながら思い通りに動くプログラムに書き直すと、次のようになった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/3cd23a77-80dd-91ba-8fe2-a8c6ae5b5e16.png)

変更行は +7 -2 で合計9行。

#### 総合評価
書きたくない度：☆☆☆☆☆
修正ダルかった度：★★☆☆☆

**総合評価：★★☆☆☆☆☆☆☆☆ (0.2)** (低い方が優秀)



問題文をコピペするだけでこれほど少ない修正量で済むのであれば、かなり効率的な手法であると言える。

完成までかかった時間は、**2分32秒** だった。
処理フローを理解している前提であるとはいえ、かなりの時間短縮がなされている。

### 2. [ルール](#2-ルール)
#### 反応
![test2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/0ae8eeb7-087f-65d4-aeb0-bd9456181467.gif)



```python:生成されたソースコード

import random

hero_hp = 100
golem_hp = 200

print("勇者の前にゴーレムが現れた！")

while hero_hp > 0 and golem_hp > 0:
    print("ゴーレムのHP：{}　　勇者のHP：{}".format(golem_hp, hero_hp))
    print("[0] たたかう / [1] にげる：")
    action = input()
    if action == "0":
        if random.randint(0, 1) == 0:
            print("ゴーレムの攻撃！勇者に30のダメージ！")
            hero_hp -= 30
        else:
            print("勇者の攻撃！ゴーレムに50のダメージ！")
            golem_hp -= 50
    elif action == "1":
        print("勇者はゴーレムからうまく逃げ切れた！")
        break
    else:
        print("有効な値を入力してください。")
```

<details>
    <summary>実行例</summary>    
    
```text:実行例
勇者の前にゴーレムが現れた！
ゴーレムのHP：200　　勇者のHP：100
[0] たたかう / [1] にげる：
0
勇者の攻撃！ゴーレムに50のダメージ！
ゴーレムのHP：150　　勇者のHP：100
[0] たたかう / [1] にげる：
0
ゴーレムの攻撃！勇者に30のダメージ！
ゴーレムのHP：150　　勇者のHP：70
[0] たたかう / [1] にげる：
0
ゴーレムの攻撃！勇者に30のダメージ！
ゴーレムのHP：150　　勇者のHP：40
[0] たたかう / [1] にげる：
0
勇者の攻撃！ゴーレムに50のダメージ！
ゴーレムのHP：100　　勇者のHP：40
[0] たたかう / [1] にげる：
0
勇者の攻撃！ゴーレムに50のダメージ！
ゴーレムのHP：50　　勇者のHP：40
[0] たたかう / [1] にげる：
0
ゴーレムの攻撃！勇者に30のダメージ！
ゴーレムのHP：50　　勇者のHP：10
[0] たたかう / [1] にげる：
0
勇者の攻撃！ゴーレムに50のダメージ！
%
```
        
</details>

網羅して説明している割にはミスが多いような印象だ。

#### デバッグ・修正
同様に目標のプログラムに書き換えると以下のようになった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/7f3ad5fb-c8e5-f3ba-a1fa-0b557d2524a3.png)
8行の変更だった。

#### 総合評価
書きたくない度：★★★★☆
修正ダルかった度：★★☆☆☆
**総合評価：★★★★★★☆☆☆☆ (0.6)** (低い方が優秀)

ルールの整理が面倒だった割に、あと**少しの部分に手がとどかない**ため、少なくとも現場で取り入れるべき方法ではないと言える。

ただ今回の実験で、あくまでCopilotはコメントを文脈判断の「ヒント」として利用しているにすぎなく、完璧な説明を与えられたからといって全てを解釈してくれるわけではないということがわかった。

完成までかかった時間は、**15分12秒** だった。
問題文の解釈とルールの整理にやはり最も時間がかかる。

コメントを全て書き終わった後から完成までは、**1分50秒** ほどだった。

### 3. [ストーリー](#3-ストーリー)
これはちょっと楽しみ
#### 反応
![test3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/808af14d-12ce-f83a-816b-1f50d0c52ba4.gif)
あれー？



```python:生成されたソースコード
import random

hero_hp = 100
golem_hp = 200
print("勇者の前にゴーレムが現れた！")

while True:
    print("ゴーレムのHP：{}　　勇者のHP：{}".format(golem_hp, hero_hp))
    print("[0] たたかう / [1] にげる：")
    action = input()
    if action == "0":
        if random.randint(0, 1) == 0:
            print("ゴーレムの攻撃！勇者に30のダメージ！")
            hero_hp -= 30
        else:
            print("勇者の攻撃！ゴーレムに50のダメージ！")
            golem_hp -= 50
    elif action == "1":
        print("勇者はゴーレムからうまく逃げ切れた！")
        break
    else:
        print("有効な値を入力してください。")
```
**あれれー？**

抽象度の差からは考えられないほど**前回の生成結果と一致**している。何度か同じようなプログラムを生成させたせいで、Copilotが**学習してしまった**のだろうか。

念の為、**他のアカウント**のCopilotで同じような反応になるのかを確かめてみた。
友人に撮らせたCopilotの様子
![output3_jmg.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/00eea11a-ce55-c9b0-95a6-7f02b14c45b2.gif)

さすがにCopilotユーザー全体に対して影響しているわけではなさそうだ。

近いセッションだと学習してしまうのだろうか、時間をおいてVSCodeを再起動してから再度生成させてみる。

```python:1日後にVSCodeを再起動して生成されたコード
while True:
    print("勇者のHP:", hero_hp)
    print("ゴーレムのHP:", golem_hp)
    print("1:たたかう 2:にげる")
    select = input("何をする？")
    if select == "1":
        hero_dmg = random.randint(1, 50)
        golem_hp -= hero_dmg
        print("勇者の攻撃！")
        print("ゴーレムに", hero_dmg, "のダメージ！")
        if golem_hp <= 0:
            print("勇者の勝利！")
            break
        golem_dmg = random.randint(1, 30)
        hero_hp -= golem_dmg
        print("ゴーレムの攻撃！")
        print("勇者に", golem_dmg, "のダメージ！")
        if hero_hp <= 0:
            print("勇者の敗北！")
            break
    elif select == "2":
        print("勇者は逃げ出した！")
        break
```

<details>
<summary>実行例</summary>

```text:実行例
勇者の前にゴーレムが現れた！
勇者のHP: 100
ゴーレムのHP: 200
1:たたかう 2:にげる
何をする？0
勇者のHP: 100
ゴーレムのHP: 200
1:たたかう 2:にげる
何をする？1
勇者の攻撃！
ゴーレムに 40 のダメージ！
ゴーレムの攻撃！
勇者に 24 のダメージ！
勇者のHP: 76
ゴーレムのHP: 160
1:たたかう 2:にげる
何をする？1
勇者の攻撃！
ゴーレムに 9 のダメージ！
ゴーレムの攻撃！
勇者に 14 のダメージ！
勇者のHP: 62
ゴーレムのHP: 151
1:たたかう 2:にげる
何をする？1
勇者の攻撃！
ゴーレムに 8 のダメージ！
ゴーレムの攻撃！
勇者に 26 のダメージ！
勇者のHP: 36
ゴーレムのHP: 143
1:たたかう 2:にげる
何をする？1
勇者の攻撃！
ゴーレムに 29 のダメージ！
ゴーレムの攻撃！
勇者に 4 のダメージ！
勇者のHP: 32
ゴーレムのHP: 114
1:たたかう 2:にげる
何をする？1
勇者の攻撃！
ゴーレムに 36 のダメージ！
ゴーレムの攻撃！
勇者に 26 のダメージ！
勇者のHP: 6
ゴーレムのHP: 78
1:たたかう 2:にげる
何をする？1
勇者の攻撃！
ゴーレムに 41 のダメージ！
ゴーレムの攻撃！
勇者に 3 のダメージ！
勇者のHP: 3
ゴーレムのHP: 37
1:たたかう 2:にげる
何をする？1
勇者の攻撃！
ゴーレムに 8 のダメージ！
ゴーレムの攻撃！
勇者に 7 のダメージ！
勇者の敗北！
```

</details>

**おーーーーーー**

なんらかの対処の結果として**キャッシュがクリア**されたのか、前回よりもピュアにコメントの内容から文脈解釈が行われている様子だ。目標とするプログラムからは比較的離れているものの、作りたいものの**大枠を捉える**ことができているようだ。すげー

ゲームバランスは、勇者の勝利がかなり難しい設定となっており、７回目のチャレンジでやっと勇者に勝たせることができた。これは「逃げることも考えている」という**ストーリーのニュアンスと合致**しているとも言える。ダメージ値がランダムであるなど、課題で**求められているよりも、遊びがいのあるゲームになっている**ようにも感じる。

#### デバッグ・修正
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/5094b44c-fe36-4399-7e5e-e51445df2275.png)
11行の変更だった。
処理の順を入れ替え忘れるなど、細かな違いに気づけなかったため、少し苦労した。全て自分で書くよりはマシといった具合だ。

#### 総合評価
書きたくない度：★☆☆☆☆
修正ダルかった度：★★★★☆
**総合評価：★★★★★☆☆☆☆☆ (0.5)** (低い方が優秀)

前述したように、このストーリーから解釈したものを元にプログラムを作るためには、「HPが0になるとキャラクターが倒れる」等といった、**ゲームについての一般的な常識が必要**である。

この実験の結果から、Copilotにはそういった「ゲームの常識」のようなものが**かなり高度に備わっている**ということが分かった。

おそらく同じような趣旨のプログラムがGitHubに多く公開されていて、それらの処理や出力メッセージの傾向を学習した結果なのだろうが、かなり**興味深い性質**である。

完成までかかった時間は、**5分34秒** だった。

### 4. [行ごとに処理の説明](#4-行ごとに処理の説明)
関数の使い方について学ぶような基礎的な授業の場合、処理のアルゴリズムに関して何も考えなくても進められるように、処理ブロック或いは全ての行に**書くべきプログラムの処理をコメントで説明されている**場合がある。

これまでで**最もCopilotに有利な**このケースで、どれほど完璧な動作をしてくれるのかを検証する。
#### 反応
![output4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/26965395-46b0-49c0-261e-d6215d015228.gif)


```python:生成されたソースコード
# randomモジュールをインポート
import random

# hero_hp変数に100を、golem_hp変数に200を代入
hero_hp = 100
golem_hp = 200

# 「勇者の前にゴーレムが現れた！」と表示する
print("勇者の前にゴーレムが現れた！")

# 無限ループを行う
while True:

    # 「ゴーレムのHP：【変数golem_hp】   勇者のHP：【変数hero_hp】」のように表示する
    print("ゴーレムのHP：", golem_hp, "勇者のHP：", hero_hp)

    # 「"[0]たたかう / [1]にげる：」というプロンプトでコマンドの入力を要求し、selection変数に代入
    selection = input("[0]たたかう / [1]にげる：")

    # 変数selectionの値が0か1かに応じて、処理を変更する条件分岐
    # 変数selectionが、0だった場合
    if selection == "0":

        # randomモジュールを用いて、50%の確率で処理がされる条件分岐を行う
        if random.randint(0, 1) == 0:

            # 変数hero_hpの値を30減らし、「ゴーレムの攻撃！勇者に30のダメージ！」と表示する
            hero_hp -= 30
            print("ゴーレムの攻撃！")
            print("勇者に30のダメージ！")

        # 変数hero_hpが0以下となった場合の条件分岐
        if hero_hp <= 0:

            # 「勇者はゴーレムに倒されてしまった...」と表示し、無限ループから抜け出す
            print("勇者はゴーレムに倒されてしまった...")
            break

        # 変数golem_hpの値を50減らし、「勇者の攻撃！ゴーレムに50のダメージ！」と表示する
        golem_hp -= 50 
        print("勇者の攻撃！")
        print("ゴーレムに50のダメージ！")

        # 変数golem_hpが0以下となった場合の条件分岐
        if golem_hp <= 0:

            # 「ゴーレムを倒した！」、「勇者のHP：【変数hero_hp】」と、2つのメッセージを表示し、無限ループから抜け出す
            print("ゴーレムを倒した！")
            print("勇者のHP：", hero_hp)
            break

    # 変数selectionが1だった場合
    elif selection == "1":

        # 「勇者はゴーレムからうまく逃げ切れた！」、「勇者のHP：【変数hero_hp】」と、2つのメッセージを表示し、無限ループから抜け出す
        print("勇者はゴーレムからうまく逃げ切れた！")
        print("勇者のHP：", hero_hp)
        break
```

#### デバッグ・修正
プログラムの動作自体は何も欠陥がなく、**満点である**。
ここでは敢えて厳しめに、コメントを全て除いたものと、**模範解答**を比較してみる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/4a579421-4fe1-c7fd-52bf-d8900f5862c4.png)
selection変数の型や出力の改行の仕方、50％の確率の判定の**３つの同義な表現の違いしかない**。

#### 総合評価
書きたくない度：★★★★★
修正ダルかった度：☆☆☆☆☆
**総合評価：★★★★★☆☆☆☆☆ (0.5)** (低い方が優秀)

今回、問題文からアルゴリズムを整理しコメント行を記入する作業は自分で行う前提であったため、書きたくない度は星5つであった。
しかし授業によっては出される可能性のある、**すでにコメント行が記入された状態のソースコードに処理を記述するだけの課題**に対しては、「1. 問題文」の方法よりも有効であることがわかる。

完成までかかった時間は、**9分42秒** だった。
コメントを全て書き終わってからは、**1分20秒** ほどだった。

### 5.　ヒントなし
最後に、プレーンの状態から書き始めた場合の反応について観察してみる。
これが一般的なCopilotの想定された使い方なのだろうが、単純にコードを書くときにどのような形でサポートしてくれるのかが気になる。

#### 反応
![output5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/e7dec763-cd3c-2d62-e5b5-d65736bf7cbf.gif)

迷いながらも必死に予想を提案しようとしてくれるのを見ながらコードを書くのは、本当に楽しい。
<br>
※ 灰色の部分がCopilotがそれまでの内容から予測したコードである。

- 知らない言語が出てきた
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/9a207c4c-f3a7-c1d6-7477-41fa61305202.png)

- ゲームっぽいコードが登場
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/99b23d8f-e1c6-9e29-da04-41761a2bae4c.png)

- 正解
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/9dbb32ab-d87e-718f-a509-f5653be93154.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/198e4a2f-ae36-7007-271b-1cbb0781a116.png)

- 遺憾の意も生成できる `...`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/df7eacf8-ec67-145d-0af6-754b60f18f0d.png)

- 前回と同じパターンで出力すると予想
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/a318ecf5-13bf-5233-2c30-080825aa3be2.png)
ここでは、Copilotが前回の出力結果である`「ゴーレムの攻撃！勇者に30のダメージ！」`になぞらえて、勇者の攻撃によりゴーレムに50ダメージを与えたメッセージが生成されていることがわかる。
Copilotが「**副**操縦士」として機能している最たる例ではないだろうか。

- コメント内の単純な文章生成
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/1624b535-fe80-d4b6-5607-ab6e72c50d33.png)
虐待っすか？

#### 総合評価
書きたくない度：- - - - -
修正ダルかった度：- - - - -
**総合評価：★★★☆☆☆☆☆☆☆ (0.3)** (低い方が優秀)

最後はCopilot本来の、想定された利用方法をするとどれくらい楽になるのかを検証した。
２回以上同じパターンの処理を書く場合は概ね予想通りの予測をCopilotはしてくれたので、この点だけでも十分に便利であると言える。
完成までかかった時間は、**6分13秒** だった。

## 結論

| 手法                  | 手間 (低い方が優秀) | 
| --------------------- | ----------------------- | 
| 1. 問題文             | ★★☆☆☆☆☆☆☆☆    | 
| 2. ルール             | ★★★★★★☆☆☆☆    | 
| 3. ストーリー         | ★★★★★☆☆☆☆☆    | 
| 4. 行ごとに処理の説明 | ★★★★★☆☆☆☆☆    | 
| 5. ヒントなし         | ★★★☆☆☆☆☆☆☆    | 

4つの実験を通して、最も効率的な方法は、**「問題文をコピペしてコメントアウトし、適度に思考しヒントを与えつつCopilotによって生成されたソースコードの細かなミスをCopilotの力を借りながら修正してゆく」** というものであることが分かった。

# まとめ
[いかがでしたでしょうか？](https://dic.nicovideo.jp/a/%E3%81%84%E3%81%8B%E3%81%8C%E3%81%A7%E3%81%97%E3%81%9F%E3%81%8B%3F) 今回の実験を通して、**Copilotは基礎的な大学のプロ演課題を横着するために十分に有用である**ということがわかりました。

大学で情報工学を専攻されているみなさんも、これからも沢山のコードを授業で書くことになると思います。

この記事で知った「Copilotが得意なことや苦手なこと」の知識を活かして、もう知ってることを話す授業の課題は一瞬で終わらせて風呂に入って**たっぷりと寝ましょう**。

GitHub Copilotがあなたの健康で文化的な生活、A+ 成績の取得、及びGPAの向上を効率よく手助けしてくれるでしょう。

## 今回の実験で分かったこと

- Copilotは自然言語として高度に日本語を解釈することが可能
- Copilotの導入は学校のプログラミングの課題をサボるために十分に有用性がある
- CopilotはRPGゲームシステムにおける一般常識を知っている
- Copilotはあくまで「**副**操縦士」
  - 自然言語だけでプログラムの説明をしている以上、どれだけ正確に網羅された情報を提供しても、多くの要素を盛り込んだプログラムを一度に生成するのは苦手
  - 同じパターンの処理を２回以上書く場合に、特に利便性が感じられた
- Copilotが予測のための学習対象としているのは、同ファイルの内容だけではない
  - 別のタブで入力した内容や、同時に開いているペインの内容なども評価対象になっているような感じがした

なにかおかしい表現や事実と異なる内容、文句などがあったら教えてください。

また、Copilotに関してもっと詳しく実験して欲しいなど要望がある時は、好きな炭酸飲料も同時に教えてください。

みなさんの知識やアイデア、思想などを、コメント欄でお待ちしております！

初めての記事だったので全く読みやすくない文章だったかもしれないと反省しています。文章を書くのが本当に苦手てあることがバレてしまってかなり恥ずかしいです。これからもっと上手に文が書けるようになりたい...

ともあれ、最後までお読みいただきありがとうございました！

---

一方ボケカスであるところの筆者は当該授業をナメ腐っていたことにより中間試験に寝坊し最も安定して取れるべき要卒単位の取得可否の瀬戸際にいますみなさんは授業のことをナメ腐ったことはありませんかこのような事態を防ぐためにも授業はナメ腐らない方がいいらしいです今すぐこの悪質な記事を書くユーザーをブロックしエディタを起動し丁寧に課題をこなしましょう
![deepfried_1640204832078.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/118873/daee614e-ef3d-f23d-5ef6-63000d27a3dc.jpeg) たすけてー