見出し画像

センター試験「情報関係基礎」2020年の問3 宝探しをプログラミングする

 昨年末から,センター試験「情報関係基礎」の過去問をプログラミングする話を書いてきた。実際に授業でやっているものだ。
 授業ではCindyscriptを用いているが,Pythonだとどうなるか。JavaScriptだとどうなるか。

 最初は2009年の問題だった。Pythonで過去問に取り組み始めたころで,ほんとに初心者。この時点で,
 (1) リスト処理
 (2) 変数の型と割り算の演算子 // の問題
 (3) print() の仕様
などの問題が生じていた。今となればなんでもないこと(慣れた)だが,初めは迷った。
このとき,2020年の宝探しの話も書いているが,Pythonでできる見通しはまったく立っていなかった。

2007年のブロック落としは3通りでやった。
センター試験「情報関係基礎」2007年のブロック落としをCindyScriptで書く
センター試験「情報関係基礎」2007年のブロック落としをPythonで書く
センター試験「情報関係基礎」2007年のブロック落としをJavaScriptで書く

このときの問題は,図をどうするかだったが,Pythonでは はじめて matplotlib を使った。このときは,変数のスコープの問題に直面した。
JavaScript では,結局まともな図が描けなかった。Webで探して真似して作ったが,真似しただけで,わかっているわけではない。開発環境にも苦労した。簡単なエラーなのに,気がつきにくい。

次は2017年の三角形の個数の問題。
「情報関係基礎」2017年の三角形の個数の問題をPythonで書く
「情報関係基礎」2017年の三角形の個数の問題をJavaScriptで書く

ここで,「以上,教員側には生徒の何倍もの知識が必要ということだ」と書いている。図形の表示はないが,教材を準備するために,多くの知識が必要,という話。
しかし,図がないだけ,移植は楽だった。

続いて2018年迷路。
センター試験「情報関係基礎」2018年の迷路をCindyScriptで書く
センター試験「情報関係基礎」2018年の迷路をPythonで書く

再び,図をどう描くかが問題になった。迷路の状態は2次元のリストで表しており,これを画面に表示するのに座標の順序の問題が生じる。
Cindyscriptではたいしたことはないのだが,Pythonでは難航した。
まず,リストのコピーの問題。ミュータブルとイミュータブルだ。deepcopy を発見して解決。
次に図。matplotlib ではうまくいかない。matplotlib.patches を使ってなんとか描けた。
しかし,その結果,次のことがわかった。

しかし,これで「めでたし,めでたし」とはいかないのだ。
なぜなら,でき上がった表示のためのコードは,センター試験の問題のコードとは似ても似つかぬものになっているではないか。これでは授業で「問題を見て書きなさい」とはいえない。できた状態で,袋小路を塗る部分だけを書かせるしかない。
Cindyscriptなら簡単で同じ形をしているから,表示のコードもところどころを空欄にして書かせることができる。Pythonではそうはいかないわけだ。

なお,この時点で,JavaScriptを使うことは諦めた。二兎を追うもの・・である。

この2018年の迷路について書いたのが昨年の12月9日。授業ではこのつぎが2020年の宝探しなのだが,これが書けるまで3ヶ月を要した。その間,高校教員向けのマニュアルや入門書を書きながら,いろいろ調べてきた。2020年の宝探しをやるには次のことができなければならない。

・ボタンを作ること
・画像を表示すること

片方だけではダメ。両方できなければ。

そうして,やっとTkinterにたどり着いたのだが,これまでのPythonで苦労したことがここでもあった。
まだ完成はしていないが,できたものは,Cindyscriptのコードとは似て非なるものとなりそうだ。

 少しずつ説明していこう。問題の概要については,「大学入試センター試験「情報関係基礎」問3をプログラムする」を読んでおいてもらうといいだろう。

まず,ゲームの画面を再掲しておこう。

画像1

これを Tkinter で描く。マスの部分はキャンバスを作ってその上に描く。それとは別にボタンやガイド(ラベル)をおく。これらを Tkinter ではウィジェットという。

問題の順序にしたがって考えよう。

画像2

ア・イのところをプログラムする。方向指定ボタンを作って,d_x,d_y に値を代入する関数を呼び出すようにする。その関数は,Cindyscriptではこうだ。

// 方向指定ボタンで呼ばれる 上・下・左・右
up():=(
  dx=0;
  dy=-1;
);
down():=(
);
left():=(
);
right():=(
);

up() だけを作って,あとを問題にした。問題の本文を読めば下方向は書いてあるし,右方向が ア・イ である。なお,Cindyscript ではアンダースコアは別の意味を持つので,アンダースコアはつけない。このあとPythonでもつけない形で書く。

これにならって,Pythonでも作る。

def up():
    dx = 0
    dy = -1

ボタンは次のように作る。
まず,ボタンを定義

btn1 = tk.Button(text="上", command = up) 

command = up で,このボタンをクリックすると,関数 up を呼び出すことができる。引数は書かない。つまり,渡せない。
できたボタンを配置する。

btn1.place(x=550, y=200)

同じようにして他のボタンも作る。

さあ,他も書けた。実行。
ボタンを押して・・・ 動かない・・・・ エラーメッセージは出ない。
なぜ? 答えは後で。Pythonに詳しい人ならすぐわかるだろう。
いや,詳しくなくても,昨日の記事を読めばわかるか。

とりあえず,次の説明。先程の問題文にある図2。

画像3

これは問題ない。これ全体を関数 move としておこう。ウ,エは YOKO , TATE

Cindyscriptでは

// 移動ボタンが押されたときの手続き (図2)
move():=(
 message="";
 if(robox+dx>0 & robox+dx<=YOKO &
   roboy+dy>0 & roboy+dy<=TATE,
   robox=robox+dx;
   roboy=roboy+dy;
 );
 nokori=nokori-1;
 if(takarax==robox & takaray==roboy,
   message="宝を見つけた! 宝探し成功!";
 );
);

問題の通りに書ける。はじめの massage = "" は問題にはないが,罠に落ちた時のメッセージが表示されたままになるのを修正するために付けたものである。
Pythonでは

# 移動ボタンが押されたときの手続き (図2)
def move():
    message = ""
    if (robox+dx > 0 and robox+dx <= YOKO and
          roboy+dy > 0 and roboy+dy <= TATE):
        robox = robox + dx
        roboy = roboy + dy
    nokori = nokori - 1
    if takarax == robox and takaray == roboy:
        message = "宝を見つけた! 宝探し成功!"

構造は全く同じだ。でもPythonでは動かない。

理由はさっきと同じ。

画像4

構造としては問題なく書ける。Cindyscriptは略そう。

# 最小歩数の計算 (図3)
def calc():
    sax = takarax - robox
    say = takaray - roboy
    if sax < 0:
        sax = sax * (-1)
    if say < 0:
        say = say * (-1)
    hosuu = sax + say

その次。

画像5

# 罠のマス判定 (図4)
def hantei():
    for i in range(WANASUU):
        if Wanax[i] == robox and Wanay[i] == roboy:
            message = "罠にかかった! ダメージを受けた!"
            miss = miss + 1
    if miss == 3:
        message = "3回目だ! ついに壊れた・・宝探し失敗"

これも構造上は全く同じように書ける。

画像6

ここは同じようにはいかない。02 行目,ボタンが押されたならば だからまずボタンを用意する。そのボタンで図2の関数を実行する。

btn5 = tk.Button(text="移 動",command = move)

したがって,書くのは後半。罠探知ボタンも作っておいてこれを呼び出す。

# 罠探知 (図5後半)
def detection():
    for i in range(WANASUU):
        if Wanax[i] == robox+dx and Wanay[i] == roboy+dy:
            message = "罠を発見した"
            Wanahyoji[i] = 1
    nokori=nokori-1;

ひとまずここまで。このあと,全体を動かすための手続きがある。

画像7

一見すると,01,02 行目は初期設定で,07行目からはwhile を使って,これまでに作った関数を呼び出せばよさそうだ。しかし,この通りにはできない。
なぜなのか,どうすればいいかも読者諸氏には考えていただこう。

ここまで,「構造としては同じ」と書いた部分は,そのまま教材として穴埋め形式で出せる。一度問題は解いて,答え合わせもしているので,そこを埋めながら打ち込んでいけばよい。

では,また明日。