#author("2021-10-27T17:50:02+09:00","default:takataka","takataka") #author("2021-11-04T08:51:42+09:00","default:takataka","takataka") * 応用プログラミング及び実習 2021年度 ex06 [#xa9b83a6] //&color(red){''工事中''}; #contentsx **注意 [#ib719427] notebook のセルを実行していると,たまにセルの番号のところが [*] となったままで反応が返ってこなくなるときがあります. ありがちな状況と対処法を書いときます + 単に処理に時間がかかってるだけ → 待てばいい + input( ) が入力待ってる → 入力欄になんか入力して Enter + いつまで待ってもだめ → ウィンドウ上部の「■」ボタンを押して,そのセルの実行を強制的に停止させる ** ex06A [#kadaiA] *** Step1 [#j82f0773] 授業で作成したファイル ex06constants.txt を読み込んでディクショナリに格納するコードを書こう. 以下の内容の ex06constants.py というファイルを作成し,それを修正しましょう. 最後の行の修正も忘れずに. #pre{{ # 空のディクショナリを作る constants = {} # ファイルを読み込む → 各行を分解 → 1つ目をキー,2列目を値(浮動小数点数値)としてディクショナリに登録 with open('ex06constants.txt', 'r', encoding='utf-8') as file: # キーの一覧を使ってループし,ディクショナリの内容を表示.p.194参照 for k in constants.keys(): print(k) # キー k の値と,そのキーに対応した数値を小数点以下2桁まで表示させるように修正しよう # 以下を修正して自分の学籍番号と氏名を出力させるようにしといてください print('A01055 ほげほげお') }} *** Step2 [#a0983c64] (1) 以下の内容のファイル ex06readscore.py を作りましょう #pre{{ ##### このファイルの中身は変更する必要ないはずです ##### ### 関数 readScore() の定義 # def readScore(fn): idL = [] # 空のリストを作る.ID用 scoreL = [] # 空のリストを作る.スコア用 with open(fn, 'r', encoding='utf-8') as f: # ファイルを読み込みモードで開く for line in f: # ファイルから1行ずつ読み込んでループ i, s = line.split() # 空白区切りで分割.1つ目を i に,残り(2つ目)を s に idL.append(i) # idL の末尾に i を追加 scoreL.append(float(s)) # scoreL の末尾に float(s) を追加 return idL, scoreL # 授業で説明していないが,以下の if の条件式は,このプログラムが他から import された時は # False, 直接起動されたときは True になる.したがって,if ブロックの中は後者の場合のみ # 実行され,他のプログラムから import されたときは無視される if __name__ == '__main__': # 動作確認 x, y = readScore('gamescores.txt') print(len(x), len(y)) }} (2) 実行して動作確認しましょう.このプログラムは gamescores.txt を読み込みます.同じフォルダ/ディレクトリ内に存在しないといけません.見つからないひとはこちらへ: [[AProg/2021/ex03#kadaiB]] (3) [[AProg/2021/ex03#kadaiB]] で作った ex03score.py をコピーして,ex06score.py というファイルを作り,次のことをやりましょう. - 関数 readScore の定義を削除する - 先頭に, ex06readscore.py を import する文を書く import ex06readscore (4) 上記のことをやっただけでは, 関数 readScore を呼び出してる行でエラーになります.ここを修正しましょう. ex06readscore.py で定義されている関数 readScore を呼び出すようにしてください. ヒント: ''モジュール名.関数名'' で,この場合のモジュール名は...(モジュールって何?ってひとは,[[第3回授業>AProg/2021#ex03]] のその4のnotebookと動画で復習してください). (5) 動作確認しましょう.ex03Bが満点でなかったひとは,指摘された問題点の修正も忘れずに. (6) ex06score.py の最後に,ex06constants.py と同様に自分の学籍番号氏名を出力する行を追加しといてください. ***提出法 [#n5b828ce] 上記の課題で作ったプログラムのうち,以下の二つを提出してください.ファイル名を間違えないよう注意. - ex06constants.py - ex06score.py 提出場所: [[この科目の Moodle コース>https://www-tlab.math.ryukoku.ac.jp/moodle/course/view.php?id=5]] の「ex06課題A」 ** ex06B [#kadaiB] ''顔検出しよう'' *** step1 [#ece4e74d] (0) この課題では,OpenCV という画像処理・コンピュータビジョンのためのライブラリを利用します. 以下のようにして,追加でインストールしましょう. #pre{{ pip3 install opencv-python }} (1) 右のリンク先のファイルをこの授業のためのディレクトリに保存しましょう: [[facedetect.py>https://github.com/takatakamanbou/AProg/blob/main/facedetect.py]] (保存法は .ipynb のファイルと同じです) (2) 正しい場所に保存したら,実行しましょう. ネットワークからファイルをダウンロードするはずです.「終了」と出てたらok (3) 複数人の顔が真正面を向いて写っている画像を適当に探して,同じ場所に保存しましょう. (4) 以下を ex06face.py という名前で保存しましょう. #pre{{ import cv2 import facedetect # 画像を読み込む image = cv2.imread( ここに画像ファイル名を指定 ) print("画像サイズ: {0[1]} x {0[0]}".format(image.shape)) # 顔検出を実行 posizeList = facedetect.facedetect(image) # 顔の位置と大きさを表示 for i, posize in enumerate(posizeList): print("{0}人目: {1}".format(i+1, posize)) # 画像を書き出す cv2.imwrite("hoge.png", image) # 以下を修正して自分の学籍番号と氏名を出力させるようにしといてください print('A01055 ほげほげお') }} (5) 「ここに画像ファイル名を指定」のところを書き換えて,実行してみましょう. - 上記の画像読み込み関数 cv2.imread は,JPEG, PNG, GIF, BMP 等いろいろな画像形式を扱えます. - 実行すると hoge.png という画像ファイルができますが,現時点では入力に指定したものと中身は同じです. - うまく検出できたら,検出された顔の位置などを表す4つ一組の数値が,それぞれの人物ごとに表示されるはずです. -- あまり大きい顔/小さい顔は検出できないかもしれません -- 顔の向きによってもうまく検出できないことがあります. - 4つの数値が何を表しているか考えましょう.コンピュータが扱う画像の世界では,画像の左上が原点で,右に向かってx軸,下に向かってy軸をとった座標系が使われることが多いです.このプログラムで得られる座標もそうなってます. *** step2 [#n2a7fcd0] ex06face.py を修正して,検出した顔に四角い枠をつけた画像を hoge.png として出力するようにしよう. 枠線を描くには,OpenCV の cv2.rectangle() 関数を使いましょう. 引数 color および thickness を指定して,緑色で線の太さ 3 にしてください. 参考: https://note.nkmk.me/python-opencv-draw-function/ *** 提出法 [#p8f8df2e] 提出場所: [[この科目の Moodle コース>https://www-tlab.math.ryukoku.ac.jp/moodle/course/view.php?id=5]] の「ex06課題B」 - facedetect.py の提出は必要ありません - ファイル名間違えないように,最後に自分の学籍番号氏名を出力するように **ex06C [#kadaiC] //&color(#ff0000){工事中}; ''県庁所在地クイズを作ろう'' + [[ex06kencho.txt>AProg:ex06kencho.txt]] をいつもの場所に保存して,エディタで内容を確認しなさい. + このファイルを読み込んで,次のように「県庁所在地クイズ」ができるプログラムを作ろう(以下の実行例の「★」以降はコメント).プログラムのファイル名は ex06kencho.py としてください. #pre{{ 何問やる? 50 ★ 「50」はキーボードからの入力.1以上47以下の整数でなければ入力やり直しになる 何問やる? 0 何問やる? 5 1問目: 和歌山県の県庁所在地は? 和歌山市 ★ 「和歌山市」と入力して Enter,以下同様 正解! 2問目: 熊本県の県庁所在地は? くまもん ちゃうで 3問目: 長野県の県庁所在地は? 長野市 正解! 4問目: 秋田県の県庁所在地は? 秋田犬 ちゃうで 5問目: 岡山県の県庁所在地は? 岡山市 正解! 5問中3問正解やったで A01055 ほげほげお }} + ディクショナリ使うのが素直な方針でしょう.いきなり全体を考えるのは大変なので,次の段階分けを参考にしたらよいでしょう. - Step1: ファイルを読み込んでディクショナリに登録するだけのプログラム.改行文字の除去を忘れずに. - Step2: ディクショナリのキーを格納したリストを作る. keyList = list(hoge.keys()) # この例では hoge がディクショナリ - Step3: とりあえず札幌からの順番のままで最初の5道県でクイズができる(正解不正解を判定して出力する)ものにする - Step4: キーのリストをランダムにならべかえる(random.shuffleがよいかも) - Step5: 細かいところをちゃんとする.問題数決められるように,最後に正解数出すように,etc. > &color(red){''読み込むファイルに記載されている情報が増減してもプログラムを書き換えないで済むようなものにすること.''}; < 提出場所: [[この科目の Moodle コース>https://www-tlab.math.ryukoku.ac.jp/moodle/course/view.php?id=5]] の「ex06課題C」 - ファイル名間違えないように,最後に自分の学籍番号氏名を出力するように **ex06S (omake) [#kadaiS] //&color(#ff0000){工事中}; &color(red){''これはおまけ課題です.やらなくても減点はありません.やったら棒茄子?''}; ''三目並べ = oxゲーム = Tic-Tac-Toe を作ろう'' ***step1 [#u85846b7] + 次の内容のプログラム ex06sammoku.py を(この通りに)作って実行しなさい. #pre{{ ### AProg2021-ex06 三目並べ = oxゲーム = Tic-Tac-Toe # 盤面の状態を表すディクショナリ mark = {0:" ", 1:"o", -1:"x"} ### リストのリストで表された盤面情報を o,x で表示する # def printBoard(board): print(board) # 実行例のように縦横に o や x が並ぶようにしよう # printBoard の動作確認 board = [[0, 1, -1], [1, 0, -1], [0, 0, 0]] printBoard(board) }} + printBoard() の出力が以下のようになるよう修正しなさい.~ &ref(http://www-tlab.math.ryukoku.ac.jp/~takataka/course/AProg/board.png,nolink); + このプログラムに,次のような仕様の関数 isValid() の定義を追加しなさい -- 関数名と引数は以下の通り .board は盤面を表すリストのリスト,(x, y) が盤面位置((横方向,縦方向) isValid(board, x, y) -- 位置 (x, y) が有効(盤面の範囲内かつまだoxがついてない)なら True, さもなくば False ***step2 [#g4c90e7b] +Step1 の成果と以下を組み合わせなさい.さらに,このままでは入力しても ox がつかないままなので,ちゃんと ox がつくように修正しなさい. #pre{{ ### AProg2021-ex06 三目並べ = oxゲーム = Tic-Tac-Toe # 盤面の状態を表すディクショナリ mark = {0:" ", 1:"o", -1:"x"} ### リストのリストで表された盤面情報を o,x で表示する # def printBoard(board): #中身は自分で.mark 使うこと ### 位置 (x, y) が有効(盤面の範囲内かつまだoxがついてない)なら True, さもなくば False # def isValid(board, x, y): #中身は自分で ### 入力を受け付ける.有効な位置でなければ再入力してもらう # def procUserInput(board): while True: msg = "どこに置く? 「0 1」のようにスペース入れて縦横の数を入力してね: " s = input(msg).split() if len(s) == 2: y, x = int(s[0]), int(s[1]) if isValid(board, x, y): return y, x else: print("そこには置けへんで.", end="") print("入力し直して") # 3 x 3 の盤面 board = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] # o の番なら 1, x の番なら -1 turn = 1 while True: printBoard(board) if turn == 1: print(" o の番ほげ.", end="") else: print(" x の番ほげ.", end="") y, x = procUserInput(board) turn *= -1 }} + 以下の関数 checkGameOver() を使って勝敗判定できるようにしなさい.勝ち負け引き分けが決まったら,盤面を表示したのち「oの勝ち」,「xの勝ち」,「引き分け」と表示してプログラムが終了するようにしよう. #pre{{ ### ゲーム終了かどうか判定.戻り値は次の通り.この関数は修正不要 # # -2: 引き分けでゲーム終了, -1: xの勝利 # 0: まだゲーム終了でない 1: oの勝利 # def checkGameOver(board): boardSize = len(board) # 横一行 mark がそろっていたら mark を返す for i in range(boardSize): mark = board[i][0] if mark == 0: continue j = 1 while j < boardSize and board[i][j] == mark: j += 1 if j == boardSize: return mark # 縦一列 mark がそろっていたら mark を返す for j in range(boardSize): mark = board[0][j] if mark == 0: continue i = 1 while i < boardSize and board[i][j] == mark: i += 1 if i == boardSize: return mark # 右下がりに mark がそろっていたら mark を返す mark = board[0][0] if mark != 0: i = 1 while i < boardSize and board[i][i] == mark: i += 1 if i == boardSize: return mark # 右上がりに mark がそろっていたら mark を返す mark = board[boardSize-1][0] if mark != 0: i = 1 while i < boardSize and board[boardSize-1-i][i] == mark: i += 1 if i == boardSize: return mark # 盤面が埋まってたら引き分け for i in range(boardSize): for j in range(boardSize): if board[i][j] == 0: return 0 return -2 }} ***step3 [#g4c90e7b] コンピュータと対戦できるように改造しよう.先手番 o は常に人間側(あなた)で後手番 x は乱数使ってランダムに手を打たせるようにしたらよい. ***step4 [#d28f0147] ここまでのプログラムをうまく作っていたら,盤面を表すリストのリストを 3x3 ではなく 4x4, 5x5 等の適当なサイズにしても動作するようになっているはずです.動作を確認して,そうなっていなければ修正しなさい. ***提出法 [#h4e2bc50] &color(red){''準備中です''}; 1118木1講時の時間中に,対面で高橋に見せてください.