DAY25 pandasを使ってデータを整形&日本地図都道府県名ゲームを作る
25日目ではcsvを読み込んでデータを扱う授業。
Angela YuのUdemyではアメリカの州を当てるクイズを作成しましたが、
今回は日本地図で応用してみました。
csvは今までは
import csv
with openとかで読み込んでましたが、今回はpandasモジュールを使います。
pandasモジュールはpythonでデータ分析などをするならかなり重要なので
勉強してたら絶対見聞きするもの。
csvを表にしてcsvデータの取得や入力・変更などを簡単にできるモジュール。
pandasは後々の授業で詳しくしていたので後々復習。
イメージはこんな感じ
流れとしては
- 都道府県名を答える入力ポップアップが出てくる
- 答えがあっていたら、その県名の位置か近くに県名が表示される
- 答えられると答えた数と表示される県名がどんどん増える
- saveを入力すると、答えていない都道府県名だけをcsvにして保存する
こんな感じです。
今回は保存するだけでプロジェクトを再起動した時に残りの都道府県からはじまる、というような機能は持ち合わせていないです。
あくまでpandasでcsvを保存する機能を使う、というだけ。
はじめます。
今回もturtleモジュールを使います。turtle便利。
import pandas
import turtle
screen = turtle.Screen()
screen.title("日本地図都道府県当てゲーム")
screen.setup(883, 800)
image = "map.gif"
screen.addshape(image)
turtle.shape(image)
screenでサイズを設定して、
タートルの形を日本地図の形に設定。
data = pandas.read_csv("47ken.csv")
all_states = data.state.to_list()
states_num = []
pandas.read_csv("読み込みたいファイル")
で読み込むことができます。
pandasで読み込むと、
コンソール上でもこんな感じで綺麗に表示されます。
x、yは表記される位置を表しています。
all_states はこの表では質問と答えを照らし合わせるものにx,yは必要ないので
都道府県名のみをto_listでリスト化しています。
data.stateはdata["state"]と同じ。
states_numの空リストは、saveを押した時に答えた都道府県じゃないものを保存するためのリスト。
答えを求める入力ウィンドウを表示する
全部答えたら終わりというwhileを設定して、
inputで都道府県入力ウィンドウを出します。
while len(states_num) < 48:
answer = screen.textinput(f"{len(states_num)}/47県 正解!", "都道府県名を答えてください")
答えがall_statesのリストにあったら、
dataのxとyの位置に答えを入力するというコードを書きます。
if answer in all_states:
ken = turtle.Turtle()
ken.ht()
ken.penup()
state_data = data[data.state == answer]
ken.goto(int(state_data.x), int(state_data.y))
ken.write(answer)
states_num.append(answer)
state_data = data[data.state == answer]というのは
dataのテーブルの要素と答えたものの要素だけを抽出したものをstate_dataに抽出しています。pandasだからできること(なはず)
なのでpandasの表のスクショを見てもらって
答えた都道府県が北海道の場合は
0 北海道 88 305 というデータがstate_dataに保存されているということ。
その後のgotoで、state_data.x = 88 , sate_data.y = 305 の場所に
answer = 北海道 を入力するという動作になります。
ここはstate_data.stateになぜしないのかというとstate_data.stateというのは
あくまでdataというオブジェクトから抽出しているので本体はdata。
試しにwrite(state_data.state)にしてみると、こうなる。
余計なものがついてくる。
最後にstates_numに答えた都道府県を空リストに追加します。
これでほぼゲームは完成。
あとはsaveを入力した時に答えていない都道府県をcsvで保存します。
elif answer == "save":
remain_states = []
for state in all_states:
if state not in states_num:
remain_states.append(state)
remain_data = pandas.DataFrame(remain_states)
remain_data.to_csv("remain_states.csv", header=False, index=False)
break
screen.exitonclick()
ちょっとややこしい部分がfor以下の部分ですが
remain_states = []は、全ての都道府県が入っているall_statesリストから
最初に定義した答えをリストに入れていった states_num を引いたものをいれるためのリスト。
なのでfor以下を説明すると
all_statesから1つずつ回していって、答えのリストに入ってなかったら
remain_statesに入れていって全て終わったらremain_states.csvに保存する、ということ。
header=False, index=Falseというのはエクセルでいうところの一番上の行と左の行の数字はいらないよという指示をしています。
saveをしたらbreakで質問がでなくなるので、画面をクリックして終了。
全てのデータはこちら。
python DAY-24 with open... を使ってスネークゲームにハイスコア機能を実装する
DAY24ではDAY20で作ったスネークゲームにハイスコア機能をつけます。
今までであればプログラムを閉じたら全てのデータは消去されてしまい
起動すると全て初期化されてしまうのですが、
大事な部分はデータとして残しておくようにします。
全データはgitにて公開しています。
今回改修する部分はスコアなので、score_board.py部分。
- 初期値にハイスコアの変数
- 表示部分にハイスコアの表示
- プログラム起動時にハイスコアのデータを読み込む
- ゲーム時にハイスコアが更新されたら、現在のスコアがハイスコアになる
- ゲーム終了時に更新されたハイスコアをデータに保存する
このような感じでしょうか。
更新したコードはこちら。
from turtle import Turtle
class ScoreBoard(Turtle):
def __init__(self):
super(ScoreBoard, self).__init__()
self.score = 0
self.high_score = 0
self.penup()
self.ht()
self.goto(0, 270)
self.color("white")
self.get_highscore()
def get_score(self):
self.write(f"Score: {self.score} Highscore: {self.high_score}", font=("Sans", 20, "bold"), align="center")
def get_highscore(self):
try:
with open("score.txt", mode="r") as data:
self.high_score = int(data.read())
except:
pass
self.get_score()
def game_over(self):
self.goto(0, 0)
self.write("Game Over.", font=("Sans", 20, "bold"), align="center")
def increase_score(self):
self.clear()
self.score += 1
if self.score > self.high_score:
self.high_score = self.score
with open("score.txt", mode="w") as data:
data.write(str(self.high_score))
self.get_score()
前は初期値でget_score()を起動するようにしていましたが、
今回はget_highscore()を起動するようにしています。
with open('<file name>', mode="w or a or r") as <name>
でファイルをあけられるコードで、modeがwだと上書き、aで追記、rで読み取り
ができます。 as の後に変数名をいれることで以降その変数でコードができます。
try, exceptは後々やるのですが初めてやる場合に読み込む画像がない場合
エラーになってしまうので、入れてます。
tryでwith openを試して問題なければそのコードをそのまま続けますが、
問題があればスルーしてget_score()を動かすような流れです。
get_score()にハイスコアのコードを追加。
increase_score()には
もし現在のスコアがハイスコアより大きくなった場合に
スコア=ハイスコアを表示しつつテキストにもハイスコアを保存するようにしています。
書き込みはgame_over()の時にした方がいいのかもしれません。
今回は読み込みしたものはtxtでしたが、csvももちろん読み込めます。
今後スクレイピングなどでよく使うので覚えておかないと。
DAY23 Turtleモジュールをもっと応用して障害物よけゲームを作る
今回はTurtleモジュールを使ってもう1個ゲームを作る講座。
今回は障害物よけゲームです。
Udemyの講座では長方形が動くようにしていますが、
自分のは復習と応用も兼ねてshapeをタートルにしてクリアするたびに難易度があがる設定を追加しています。
スクラッチから書けたので、コーディング自体は荒削りだと思います。
スクラッチからってなんだよって始めたとき思ってたけど
「最初から」ということなんですね。
フルスクラッチでというのはネットからのコピペもなしでということ…すごいわ
こんな感じのゲームイメージ
白い亀がタートルから通り過ぎるタートルを避けて上まで通り抜けるというゲーム。
流れとしてはこんな感じ
- プレイヤーのタートルを作り、下からスタートする。
上のみ動ける仕様にする。 - 敵のクラスを作る。色はランダムにし、動くスピードもランダムにする。
右端でy軸はランダムに設置していく。 - 上に行ったらゴール・次のレベルを表示し、下に戻る。
それでは始めます。
プレイヤーのクラスを作る
tim.py
これは今までの流れからすると難しいことはしていないので
全部のコードを。
from turtle import Turtle
import random
START_POSITION = (0, -280)
class Tim(Turtle):
def __init__(self):
super(Tim, self).__init__()
self.shape("turtle")
self.color("white")
self.penup()
self.setheading(90)
self.start()
self.goto(START_POSITION)
def start(self):
self.goto(random.randint(-200, 200), -280)
def move(self):
new_y = self.ycor() + 20
self.goto(self.xcor(), new_y)
udemyの講座だと常にx軸は真ん中からのスタートにしたけど
今回はゴールするたびにx軸も-200から200の間の何処かからのスタートに。
次に、横切るタートルのクラスを作成
enemies.py
main.pyでたくさんのタートルを作るため、このクラスの初期値では
作るたびにタートルの色とはじまる位置を変更する機能をいれます。
右から左にいくので、頭の位置は左向きに。
from turtle import Turtle
import random
class Enemies(Turtle):
def __init__(self):
super(Enemies, self).__init__()
self.shape("turtle")
self.penup()
col_r = random.randint(100, 255)
col_g = random.randint(100, 255)
col_b = random.randint(100, 255)
random_y = random.randint(-250, 250)
self.color(col_r, col_g, col_b)
self.setheading(180)
self.goto(300, random_y)
ランダムなスピードで歩く機能を追加します。
スピードは毎回違うように設定します。
def move(self):
new_x = self.xcor() - random.randint(4, 20)
self.goto(new_x, self.ycor())
これで横切るタートルの設定はおしまい。
メッセージ表示部分のクラスを作成
msg.py
- 左上に現在のレベル表示
- 上に着いたらゴール!次のレベルを表示
- 敵に当たったらゲームオーバー
の3つを表示する機能を作ります。
from turtle import Turtle
import time
class Msg(Turtle):
def __init__(self):
super(Msg, self).__init__()
self.color("white")
self.penup()
self.ht()
self.level_num = 1
self.display_level()
初期値はこんな感じ。
display_levelは左上にgoto()して、writeでlevel_numの変数を呼び出して表示します。
def display_level(self):
self.clear()
self.goto(-280, 260)
self.write(f"Level {self.level_num}", font=("Futura", 20, "bold"), align="left")
ゴールしたときの表示はこんな感じ
レベル表示で書く位置が左上に行ってるので、最初にgoto(0, 0)にしてから書く。
ここでimportしたtimeを使います。Goal!と2秒表示、その後次のレベルを2秒表示。
def goal(self):
self.goto(0, 0)
self.clear()
self.write("Goal!!", font=("Futura", 40, "bold"), align="center")
time.sleep(2)
self.clear()
self.level_num += 1
self.write(f"Level {self.level_num}", font=("Futura", 40, "bold"), align="center")
time.sleep(2)
self.clear()
負けたらゲームオーバーのテキストを真ん中に表示します。
def game_over(self):
self.goto(0, 0)
self.write("Game Over.", font=("Futura", 20, "bold"), align="center")
これでクラスファイルの作成は終わり。
main.pyでオブジェクトをまとめてゲームにしていく
仕上げていきます。
今まで作ったものは全てインポートして、こちらもscreen, timeなどもインポートしていきます。
from turtle import Screen
from tim import Tim
from msg import Msg
from enemies import Enemies
import random
import time
tim = Tim()
msg = Msg()
screen = Screen()
screen.colormode(255)
screen.setup(600, 600)
screen.bgcolor("black")
screen.tracer(0)
enemies_list = []
screen.listen()
screen.onkey(tim.move, "Up")
enemies_list のリストは、スネークゲームの時と同じように
敵のタートルを1つずつリストに格納していってそれぞれが独立したランダムな動きをするために設定しています。
screen.listen()とonkey()のセットで、上のキーを押すたびに上にあがる機能に。
difficulty = [0,1,0]
game_on = True
while game_on:
screen.update()
time.sleep(0.1)
for i in range(random.choice(difficulty)):
enemies = Enemies()
enemies_list.append(enemies)
for enemie in enemies_list:
enemie.move()
if enemie.xcor() <= -330:
enemies_list.remove(enemie)
if tim.distance(enemie) <= 20:
msg.game_over()
game_on = False
difficultyのリストは敵のタートルを出現するときに何人追加するかをランダムで決めるためのリストです。
whileで動かすたびに敵を作ってたら大渋滞になるので(試しにこのリストを1,1とかにしてみてください)
作らない時もあるようにしています。
で作ったらenemies_listに格納していって、次のforでそれぞれを動かしていきます。
xcor() <= -330の部分のifは、敵のタートルが左端にいってもそのままにしとくと
オブジェクトはずっとたまっていくので、リストがどんどん増えていきます。
超上級者がいた場合もしかしたら動きがもっさりする可能性もあるので、
敵のタートルが左端へ消えたらリストから削除するようにremoveを入れています。
また最後に敵タートルにぶつかったときに
ゲームオーバーの文字を表示すると同時にgame_on = Falseにして動きをストップしています。
time.sleepはゲームスピードを調整しています。
ここをいじることで難易度がより調整できます。
if tim.ycor() >= 300:
msg.goal()
for enemie in enemies_list:
enemie.goto(-320, enemie.ycor())
if len(difficulty) <= 8:
difficulty.append(1)
else:
difficulty.append(random.randint(1,2))
tim.start()
msg.display_level()
screen.exitonclick()
仕上げに、プレイヤーが上に行ったときの表示を設定していきます。
難易度の調整もゴールに行ったときにしていて、
difficultyのリスト数が8以下ならリストに1を追加していって敵タートルの出現確率を上げるようにしています。
さらにLEVEL 6以降は出現数を2にしたりもして難易度を上げるようにしています。
最後にscreen.exitonclick()で締め。
今回は復習ということでとりあえず自分で作れるなら作ってみよう、
どうせ作れなさそうだしあとで動かないところお手本ソースコピーして勉強しよう
くらいに思ってたら全部作れたので嬉しい。笑
今回の全てのコードはGitに上げています。
実務だとGitもいるだろうし今回からはGitでアップロードするようにします。
DAY22 Turtleモジュールを使ってピンポンゲームを作る
Angela Yu先生による22日目の講座実践。
今回はTurtleモジュールをさらに応用してレトロゲームのピンポンゲームを作ります。
両端にバーがあって、キーボードで動かして、
ボールを跳ね返していき、はねかえせずにバーを通り過ぎてしまったら負け。
ゲーセンのホッケーみたいな感じですね。
それでは始めます。
流れとしては
- スクリーンを用意して、上部にスコアを表示。また負けたらメッセージを表示
- 両端にバーを表示。キーボードで動かせるようにする
- 常に動くボールを用意。上下とバーに当たったら跳ね返るようにする。
それぞれのクラスの定義とスクリーンの基本表示
main.py
from turtle import Screen
from bar import Bar
from scoreboard import ScoreBoard
from ball import Ball
import time
screen = Screen()
screen.bgcolor("black")
screen.setup(800, 600)
screen.title("PONG")
screen.tracer(0)
ball = Ball()
score_board = ScoreBoard()
ざっくりとここまで。
from …のbar,scoreboard, ball部分とそれぞれの定義は
後でそれぞれを書いていくので今書いても、書かなくても。
前回と同じくscreenでスクリーンを定義して、setupでサイズを設定。
背景を黒に、ウィンドウのタイトルをPONGにしています。
またアニメーションをスムーズにするためのscreen.tracer(0)も記載。
後でscreen.update()も書きます。
screen.listen()
screen.onkey(bar_r.go_up, "[")
screen.onkey(bar_r.go_down, "]")
screen.onkey(bar_l.go_up, "q")
screen.onkey(bar_l.go_down, "a")
キーボード入力で動くようにするのはscreen.listen()からの.onkey設定で。
これもとりあえず右のバーは[ ]を押すことで上下へ動くように、
左のバーはq a で動くように設定。
game_on = True
while game_on:
screen.update()
screen.exitonclick()
とりあえずゲームが進む基本的なところまで。
両端にバーを表示する
次にbarのクラスをコーディングしていきます。
bar.py
from turtle import Turtle
class Bar(Turtle):
def __init__(self,x, y):
super(Bar, self).__init__()
self.shape("square")
self.color("white")
self.penup()
self.shapesize(5, 1)
self.goto(x,y)
turtleをインポートして機能を継承できるようにBar()の中にTurtleを入れる。
super().__init__()を結構忘れてしまうのでセットで覚えておいた方がいい。
以下部分は前回もやったけどshapesize(5,1)部分はやってないので説明すると
そのshapeの縦横比率を設定できる。
main.pyにもbarを定義する。
main.pyに書くよ
bar_l = Bar(-360, 40)
bar_r = Bar(350, 40)
スネークゲームのときは1つ1つが前を追うように動いてたので設定しなかったけど
今回のバーの場合は上下しか動かないのでfor . in で1こずつ作る必要がない。
shapesize()で設定するだけで縦がブロック5個分の長さを持つバーが出来上がる。
上下に動くファンクションを作る
バーのy座標を取得して、それより20上、または下にいくようにする。
今回のdef のなまえはmain.pyに書いたscreen.onkeyの最初の関数と同じもので。
def go_up(self):
new_y = self.ycor() + 20
self.goto(self.xcor(), new_y)
def go_down(self):
new_y = self.ycor() - 20
self.goto(self.xcor(), new_y)
これでbar.pyは終わり。
ball.pyでボールクラスを作る
ball.py
まずは初期値を設定する。ボールの大きさ、色などに加えて
常に動く数値も設定する。
from turtle import Turtle
class Ball(Turtle):
def __init__(self):
super(Ball, self).__init__()
self.shape("circle")
self.color("yellow")
self.shapesize(1)
self.penup()
self.x_move = 10
self.y_move = 10
次に常に動く機能を実装する
現在のxとyの位置を取得して、上に設定したx_moveとy_move分動くように。
def move(self):
new_x = self.xcor() + self.x_move
new_y = self.ycor() + self.y_move
self.goto(new_x, new_y)
当たったら跳ね返る機能を実装する
ycor()でスクリーンサイズからはみ出たら、y_move に -1をかけて逆にいくようにする。
上に行ったら 10 x -1 で-10で下にいくように。
def bounce_y(self):
self.y_move *= -1
同じ要領でバーに当たったら跳ね返る機能も実装。
def bounce_x(self):
self.x_move *= -1
最後に、どちらかが勝った時はボールが真ん中に戻るようにする機能も実装。
def ball_reset(self):
self.goto(0, 0)
self.ball_speed = 0.1
self.bounce_x()
これで終わり。
スコア・メッセージを表示するクラスの定義
最後のクラスでスコアボードを実装。
from turtle import Turtle
import time
class ScoreBoard(Turtle):
def __init__(self):
super(ScoreBoard, self).__init__()
self.penup()
self.ht()
self.color("white")
self.l_score = 0
self.r_score = 0
self.write_score()
def write_score(self):
self.clear()
self.goto(-100, 260)
self.write(self.l_score, align="center", font=("Futura", 30, "italic"))
self.goto(100, 260)
self.write(self.r_score, align="center", font=("Futura", 30, "italic"))
初期値は0点にして、init ではなく別でスコアを書く機能を実装しています。
勝ったときにスコアを書き換えるので流用するためです。
書くときは毎回文字をクリアしてから指定の場所にいって、書いて、いって、書いて。
どちらかが勝ったときに点数を加算し、メッセージを書いて少しポーズする
最後にどちらかが勝ったときにポイントを追加する。
udemyにはなかったけど左の勝ち!みたいなメッセージ欲しいなと思って
昨日追加。
右と左で処理を分けたいので、アーギュメントがrightかleftで動作を変えるようにする。
def get_point(self, player):
self.goto(0, 0)
if player == "left":
self.l_score += 1
self.write("Left Player WIN!", align="center", font=("Futura", 45, "italic"))
elif player == "right":
self.r_score += 1
self.write("Right Player WIN!", align="center", font=("Futura", 45, "italic"))
time.sleep(3)
self.write_score()
再度main.pyにて動作を実装していく
再度main.pyに戻って、whileの内側に作った機能を盛り込んでいきます。
- ボールが動き続ける
- ボールが上か下の端にいったら跳ね返る
- バーにあたったら跳ね返る
- ボールが右端、または左端にいったら勝利者にポイントとメッセージが表示
これをそのままコーディングしていきます。
game_on = True
while game_on:
ball.move()
if ball.ycor() >= 280 or ball.ycor() <= -280:
ball.bounce_y()
if ball.xcor() >= 400:
score_board.get_point("left")
ball.ball_reset()
if ball.xcor() <= -400:
score_board.get_point("right")
ball.ball_reset()
if bar_l.distance(ball) < 30 and ball.xcor() <= -340 or bar_r.distance(ball) < 30 and ball.xcor() >= 310:
ball.bounce_x()
screen.update()
screen.exitonclick()
これでほぼ完成!
長く続くほどにボールスピードの上がる機能を実装
同じスピードだと上手くなるごとに延々と続けれる可能性が出てくるので、
ボールのスピードを上げていくようにします。
まずball.pyの__init__部分にball_speedを定義。
class Ball(Turtle):
def __init__(self):
super(Ball, self).__init__()
self.shape("circle")
self.color("yellow")
self.shapesize(1)
self.penup()
self.x_move = 10
self.y_move = 10
self.ball_speed = 0.1
跳ね返るごとに少しずつ早くしていきます。
ただ、どちらかが負けたときにはボールスピードを初期値に戻します。
def bounce_x(self):
self.x_move *= -1
self.ball_speed *= 0.9
def ball_reset(self):
self.goto(0, 0)
self.ball_speed = 0.1
self.bounce_x()
この設定を実装するように、main.pyで設定。
game_on = True
while game_on:
time.sleep(ball.ball_speed)
ball.move()
これで完成!
全てのコードは
main.py
from turtle import Screen
from bar import Bar
from scoreboard import ScoreBoard
from ball import Ball
import time
screen = Screen()
screen.bgcolor("black")
screen.setup(800, 600)
screen.title("PONG")
screen.tracer(0)
ball = Ball()
score_board = ScoreBoard()
bar_l = Bar(-360, 40)
bar_r = Bar(350, 40)
screen.listen()
screen.onkey(bar_r.go_up, "[")
screen.onkey(bar_r.go_down, "]")
screen.onkey(bar_l.go_up, "q")
screen.onkey(bar_l.go_down, "a")
game_on = True
while game_on:
time.sleep(ball.ball_speed)
ball.move()
if ball.ycor() >= 280 or ball.ycor() <= -280:
ball.bounce_y()
if ball.xcor() >= 400:
score_board.get_point("left")
ball.ball_reset()
if ball.xcor() <= -400:
score_board.get_point("right")
ball.ball_reset()
if bar_l.distance(ball) < 30 and ball.xcor() <= -340 or bar_r.distance(ball) < 30 and ball.xcor() >= 310:
ball.bounce_x()
screen.update()
screen.exitonclick()
ball.py
from turtle import Turtle
class Ball(Turtle):
def __init__(self):
super(Ball, self).__init__()
self.shape("circle")
self.color("yellow")
self.shapesize(1)
self.penup()
self.x_move = 10
self.y_move = 10
self.ball_speed = 0.1
def move(self):
new_x = self.xcor() + self.x_move
new_y = self.ycor() + self.y_move
self.goto(new_x, new_y)
def bounce_y(self):
self.y_move *= -1
def bounce_x(self):
self.x_move *= -1
self.ball_speed *= 0.9
def ball_reset(self):
self.goto(0, 0)
self.ball_speed = 0.1
self.bounce_x()
bar.py
from turtle import Turtle
class Bar(Turtle):
def __init__(self,x, y):
super(Bar, self).__init__()
self.shape("square")
self.color("grey")
self.penup()
self.shapesize(4, 1)
self.goto(x,y)
def go_up(self):
new_y = self.ycor() + 20
self.goto(self.xcor(), new_y)
def go_down(self):
new_y = self.ycor() - 20
self.goto(self.xcor(), new_y)
scoreboard.py
from turtle import Turtle
import time
class ScoreBoard(Turtle):
def __init__(self):
super(ScoreBoard, self).__init__()
self.penup()
self.ht()
self.color("white")
self.l_score = 0
self.r_score = 0
self.write_score()
def write_score(self):
self.clear()
self.goto(-100, 260)
self.write(self.l_score, align="center", font=("Futura", 30, "italic"))
self.goto(100, 260)
self.write(self.r_score, align="center", font=("Futura", 30, "italic"))
def get_point(self, player):
self.goto(0, 0)
if player == "left":
self.l_score += 1
self.write("Left Player WIN!", align="center", font=("Futura", 45, "italic"))
elif player == "right":
self.r_score += 1
self.write("Right Player WIN!", align="center", font=("Futura", 45, "italic"))
time.sleep(3)
self.write_score()
DAY20-21 タートルモジュールを使ってスネークゲームを作る
DAY 19 20の二日間ではタートルモジュールを使って
レトロゲームのスネークゲームを作ります。
スネークを上下左右に操って、丸い餌を食べることで
体がとスコアが1つずつ増えていってどれだけスコアを上げられるかというゲームです。
ざっくりした流れは
- 餌を食べるスネークを作る
- ランダムに現れる餌を作成する
- スネークが餌を食べたら増えるスコアを作成する
- 四方の壁か自分の体に先頭が当たったらGameOver
こんな感じ。
それでは始めます。
初めにスネークから。
Turtleのshapeをsquareにして最初は3つに並べれば
体ができる。
ただ、餌を食べれば増えていくので初めにおきたい場所だけ設定しておいて、その後は最後尾に増えるようなコーディングにしたい。
snake.py
from turtle import Turtle
STARTING_POSITIONS = [(0, 0), (-20, 0), (-40, 0)]
class Snake:
def __init__(self):
self.snake_body = []
for i in STARTING_POSITIONS:
self.add_body(i)
self.head = self.snake_body[0]
def add_body(self, position):
new_body = Turtle("square")
new_body.color("white")
new_body.penup()
new_body.goto(position)
self.snake_body.append(new_body)
初期値でsnake_bodyというリストを作り、
add_bodyで四角をSTARTING_POSITIONにある3つの位置に置く。
4つ目以降は一番最後に置くようにする。
def increase_snake(self):
self.add_body(self.snake_body[-1].position())
def move(self):
for seg_num in range(len(self.snake_body)-1, 0, -1):
new_x = self.snake_body[seg_num - 1].xcor()
new_y = self.snake_body[seg_num - 1].ycor()
self.snake_body[seg_num].goto(new_x, new_y)
self.head.forward(20)
moveのrangeの内容はスネークの最後から順次20ずつ進むようにしているのは、アニメーションと体が増えるときに違和感のないようにしている。
というのも、最初に頭から移動すると、
1■■■
2■■ ■
3■ ■■
4 ■■■
と言う感じになって体1つ分隙間ができてしまう。
逆からやると、
1■■■
2 ■■ (左から2つ目が重なる)
3 ■■ (左から3つ目が重なる)
4 ■■■
と言う感じで体が離れずに進むことができる。
また、体が一つ増える分に関しては
snake_bodyの一番最後の場所を取得してから進み、
最後にその場所に四角を増やす、という仕組み。
同じような流れで言うと、
1■■■
2 ■■ (左から2つ目が重なる)
3 ■■ (左から3つ目が重なる)
4 ■■■
5■■■■(最後のpositionに新しい体を追加する)
6 ■■■(同じように進む)
最後に、上下左右に向きを変えるようための動作
UP = 90
DOWN = 270
RIGHT = 0
LEFT = 180
def up(self):
if self.head.heading() != DOWN:
self.head.setheading(UP)
def down(self):
if self.head.heading() != UP:
self.head.setheading(DOWN)
def left(self):
if self.head.heading() != RIGHT:
self.head.setheading(LEFT)
def right(self):
if self.head.heading() != LEFT:
self.head.setheading(RIGHT)
これでsnake.pyは終わり。
次に、food.pyとしてエサのクラスを作成する
food.py
これはシンプルに、エサの形状と
ランダムに表示される設定を作成する。
from turtle import Turtle
import random
class Food(Turtle):
def __init__(self):
super().__init__()
self.shape("circle")
self.color("yellow")
self.shapesize(0.3)
self.penup()
self.refresh()
def refresh(self):
random_x = random.randint(-280, 280)
random_y = random.randint(-280, 280)
self.goto(random_x, random_y)
food = Turtle()をつけなくてもclassのかっこにimportをいれて、
__init__下部分にsuper().__init__()
を入れることでTurtle()と同じことができるようになる。
次にスコアを表示
score_board.py
from turtle import Turtle
class ScoreBoard(Turtle):
def __init__(self):
super(ScoreBoard, self).__init__()
self.score = 0
self.penup()
self.ht()
self.goto(0, 270)
self.color("white")
self.get_score()
def get_score(self):
self.write(f"Score : {self.score}", font=("Sans", 20, "bold"), align="center")
def game_over(self):
self.goto(0, 0)
self.write("Game Over.", font=("Sans", 20, "bold"), align="center")
def increase_score(self):
self.clear()
self.score += 1
self.get_score()
スコアでは文字を取り扱うパーツにする。
スコアを初期値は0にして、get_scoreで表示をする。
スクリーンに文字を入れるには.writeメソッドを使う。
.write("表示したい文字", font=("フォント". 文字の大きさ,"太字とかイタリックとか", align="centerとか")
で文字の装飾も可能。
main.pyでゲームをまとめていく
それぞれのクラスが設定できたので、まとめる。
まずはそれぞれを呼び出して、ウィンドウの設定をしていく。
from turtle import Turtle, Screen
from snake import Snake
from food import Food
from score_board import ScoreBoard
import random
import time
screen = Screen()
screen.setup(600, 600)
screen.bgcolor("black")
screen.title("Snake Game")
screen.tracer(0)
snake = Snake()
food = Food()
score = ScoreBoard()
.tracerについては後述。
前回のタートルゲームでやったキーを押すことでウィンドウ内にアクションができる機能 listen()とonkey()を入力して上下左右に向きを変えられるようにする。
screen.listen()
screen.onkey(snake.up, "Up")
screen.onkey(snake.down, "Down")
screen.onkey(snake.left, "Left")
screen.onkey(snake.right, "Right")
ゲーム開始部分に。
ゲームが終わるまではずっとスネークが動かせるようにする。
スネークが四方にはみ出たらゲームオーバー。
また、スネークが自分の体にあたってもゲームオーバー。
スネークがエサを食べたら体が伸びてスコアもアップする。
この3つを盛り込めば完成。
start_game = True
while start_game:
screen.update()
time.sleep(0.5)
snake.move()
if snake.head.distance(food) < 20:
food.refresh()
score.increase_score()
snake.increase_snake()
if snake.head.xcor() >= 300 or snake.head.xcor() <= -300 or \
snake.head.ycor() >= 300 or snake.head.ycor() <= -300:
score.game_over()
start_game = False
for snake_body in snake.snake_body:
if snake_body == snake.head:
pass
elif snake.head.distance(snake_body) < 10:
score.game_over()
start_game = False
screen.exitonclick()
エサを食べるというのはどう言う判定をするか、というのは
スネークの頭とエサの距離が20以下になったら、と判定する。
(スネークの体1つが20なので)
それができるのがdistance関数。1つ目のifがそのコード。
2つ目のコードは四方の壁からスネークがはみ出たらゲームオーバーになるコード。
壁からはみ出るのを判定するのは、
スネークの頭をxcor()、ycor()で場所を取得して、
ウィンドウサイズより大きい、または少ないを判定している。
for部分では頭が体に追突したらゲームオーバーになるコード。
snake_body == snake.headの場合はpassとしているが、
これをしないとsnake.headの距離は0なのですぐゲームオーバーになってしまうため。
先ほどの.tracer()と今回の.update()について
これがないとgoto()や向きを変更するときに律儀にくるっと回転するアニメーションまで出てきてしまうので、
time.sleepと合わせて使用する。
伝え方が難しいので、コードをない場合とある場合で試してみるといいかも。
これで完成。
それぞれのファイルまとめ
main.py
from turtle import Turtle, Screen
from snake import Snake
from food import Food
from score_board import ScoreBoard
import random
import time
screen = Screen()
screen.setup(600, 600)
screen.bgcolor("black")
screen.title("Snake Game")
screen.tracer(0)
snake = Snake()
food = Food()
score = ScoreBoard()
screen.listen()
screen.onkey(snake.up, "Up")
screen.onkey(snake.down, "Down")
screen.onkey(snake.left, "Left")
screen.onkey(snake.right, "Right")
def count_num(num):
count_nums = Turtle()
count_nums.ht()
count_nums.color("white")
count_nums.penup()
count_nums.goto(0, 20)
for i in range(1,num):
count_nums.clear()
count_nums.write(num - i, font=("Sans", 20, "bold"), align="center")
time.sleep(1)
count_nums.clear()
count_num(4)
start_game = True
while start_game:
screen.update()
time.sleep(0.1)
snake.move()
if snake.head.distance(food) < 20:
food.refresh()
score.increase_score()
snake.increase_snake()
if snake.head.xcor() >= 300 or snake.head.xcor() <= -300 or \
snake.head.ycor() >= 300 or snake.head.ycor() <= -300:
score.game_over()
start_game = False
for snake_body in snake.snake_body:
if snake_body == snake.head:
pass
elif snake.head.distance(snake_body) < 10:
score.game_over()
start_game = False
screen.exitonclick()
snake.py
from turtle import Turtle
STARTING_POSITIONS = [(0, 0), (-20, 0), (-40, 0)]
UP = 90
DOWN = 270
RIGHT = 0
LEFT = 180
class Snake:
def __init__(self):
self.snake_body = []
for i in STARTING_POSITIONS:
self.add_body(i)
self.head = self.snake_body[0]
def add_body(self, position):
new_body = Turtle("square")
new_body.color("white")
new_body.penup()
new_body.goto(position)
self.snake_body.append(new_body)
def increase_snake(self):
self.add_body(self.snake_body[-1].position())
def move(self):
for seg_num in range(len(self.snake_body)-1, 0, -1):
new_x = self.snake_body[seg_num - 1].xcor()
new_y = self.snake_body[seg_num - 1].ycor()
self.snake_body[seg_num].goto(new_x, new_y)
self.head.forward(20)
def up(self):
if self.head.heading() != DOWN:
self.head.setheading(UP)
def down(self):
if self.head.heading() != UP:
self.head.setheading(DOWN)
def left(self):
if self.head.heading() != RIGHT:
self.head.setheading(LEFT)
def right(self):
if self.head.heading() != LEFT:
self.head.setheading(RIGHT)
score_board.py
from turtle import Turtle
class ScoreBoard(Turtle):
def __init__(self):
super(ScoreBoard, self).__init__()
self.score = 0
self.penup()
self.ht()
self.goto(0, 270)
self.color("white")
self.get_score()
def get_score(self):
self.write(f"Score : {self.score}", font=("Sans", 20, "bold"), align="center")
def game_over(self):
self.goto(0, 0)
self.write("Game Over.", font=("Sans", 20, "bold"), align="center")
def increase_score(self):
self.clear()
self.score += 1
self.get_score()
food.py
from turtle import Turtle
import random
class Food(Turtle):
def __init__(self):
super().__init__()
self.shape("circle")
self.color("yellow")
self.shapesize(0.3)
self.penup()
self.refresh()
def refresh(self):
random_x = random.randint(-280, 280)
random_y = random.randint(-280, 280)
self.goto(random_x, random_y)
DAY19 python - Turtleモジュールを使って簡単なレースゲームを作る
DAY18で学習したTurtleオブジェクトをさらに応用して、
今回はタートルレースゲームを作成します。
今回の流れとしては
- ウィンドウサイズを設定する
- 複数のタートルをスタートラインに立たせる
- どのタートルが勝つか予想をinputに入力する
- レーススタート!
- ウィンドウ右端にいったタートルが勝ち
- もう一回するか聞く
- 上のすべてをdefで囲んでリピートできるようにする
こんな感じです。
では始めます。
まずはモジュールをインポート。
タートルがfoward()で進む数をランダムで指定したいので、randomも。
from turtle import Turtle, Screen
import random
ウィンドウサイズの調整
次にウィンドウサイズの調整。
screen.setup(x, y)でできる。
また、タートルの区別をするために1体ずつ違う色にしたいので、
色のリストを追加してそこから1つずつ色をとってくる動作にする。
screen = Screen()
screen.setup(500, 500)
color_list = ["orange", "red", "purple", "blue", "black", "green", "pink"]
次にゲーム部分をファンクションで作っていく。
ウィンドウサイズが500ピクセルの正方形で、タートルのサイズも考えて
最初のタートルの位置は-230, -90くらいにしておく。
そこから30ずつ上に7体くらい作ってみよう。
それをコーディングしていくとこんな感じになった。
def game_start():
x = -230
y = -90
all_turtles = []
for i in range(7):
tim = Turtle()
tim.ht()
tim.color(color_list[i])
tim.shape("turtle")
tim.penup()
tim.goto(x, y)
tim.st()
y += 30
all_turtles.append(tim)
forでタートルを定義し、初期設定を終えた後に左端に行くようなコードです。
それをall_turtlesというリストに追加してそれぞれが判別できるように入れています。
ここで、「どの色のタートルが勝つ?色をタイプして!」という入力ウィンドウを出す。
これはscreen.textinput(title="", prompt="")でできる。
user_bet = screen.textinput(title="Make Your Bet", prompt="Which turtle will win the race? Enter a color : ")
print(user_bet)
一応自分が入力した文字がわかるようにprintで記録しておく。
そして、これが入力されたらレースが始まるようにする。
if user_bet:
is_race_start = Tru
while is_race_start:
それぞれのタートルを動かすには、1体ずつforward(ランダムな数)で進ませるという方法でいく。for in ですね。
for turtle in all_turtles:
turtle.forward(random.randint(5,20))
最小で5、最大20進むランダムな数値で進むようにしています。
この辺の数値は任意で変えたらまた逆転ゴールとかあって楽しいかも笑
最終的に右端にいったらwhileはFalseにして止めるのと同時に
勝った亀の色を取得する。
右端も考慮して220にする。
横軸の数値を取得するには、xcor()でわかる。
if turtle.xcor() >= 220:
is_race_start = False
win_color = turtle.pencolor()
賭けに勝った場合と負けた場合で表示を変えたいので、
ifで入れ子にする。
if user_bet == win_color:
restart = screen.textinput(title="You Win!", prompt="Do you wanna again? y / n")
if restart == "y":
screen.clear()
game_start()
else:
return False
else:
restart = screen.textinput(title=f"You Lose! {win_color} Win!", prompt="Do you wanna again? y / n")
if restart == "y":
screen.clear()
game_start()
else:
return False
もう一回やるかの確認でy以外をタイプした場合はゲーム終了として、
return Falseを追加しました。
また、ただ単にgame_start()で繰り返すとゴールしたタートルたちが右端に残ってしまうので、
screen.clear()でいったんウィンドウをクリアしてからゲームをスタートしてます。
最終コードはこちら。
from turtle import Turtle, Screen
import random
screen = Screen()
screen.setup(500, 500)
color_list = ["orange", "red", "purple", "blue", "black", "green", "pink"]
def game_start():
x = -230
y = -90
all_turtles = []
for i in range(7):
tim = Turtle()
tim.ht()
tim.color(color_list[i])
tim.shape("turtle")
tim.penup()
tim.goto(x, y)
tim.st()
y += 30
all_turtles.append(tim)
user_bet = screen.textinput(title="Make Your Bet", prompt="Which turtle will win the race? Enter a color : ")
print(user_bet)
if user_bet:
is_race_start = True
while is_race_start:
for turtle in all_turtles:
turtle.forward(random.randint(5,30))
if turtle.xcor() >= 220:
is_race_start = False
win_color = turtle.pencolor()
if user_bet == win_color:
restart = screen.textinput(title="You Win!", prompt="Do you wanna again? y / n")
if restart == "y":
screen.clear()
game_start()
else:
return False
else:
restart = screen.textinput(title=f"You Lose! {win_color} Win!", prompt="Do you wanna again? y / n")
if restart == "y":
screen.clear()
game_start()
else:
return False
game_start()
screen.exitonclick()
Turtleでも色々楽しめますね!
DAY18 python - Turtleモジュールを使ってダミアンハースト的なアート画像を作る
今回は今までと打って変わって新しいモジュールを使って
アートな画像を作る講座。
アートを知っている人くらいしか知らないマニアックだけど
アート界では巨匠な人 ダミアンハーストの画像を作ります。
こんな感じのドット並べてるだけの絵なんだけど、これがすごいらしい笑
Turtleモジュールはインポートして使う。
プログラミング入門用に使えるモジュールで亀を動かすこととか絵を描くこともできるし、アイデアによってはゲームとかも作れる。
ドキュメントはこちら
ざっくりと簡単な機能を言うと
- from turtle import Turtle, Screen で機能を呼び出す
- 前に進むはforward(i) でiには数字を入れるとその分前にすすむ。
- 曲がりたいときはright(i)かleft(i)でiに数値を入れるとその角度分曲がる。
- 基本的に動くとその跡は線となるのだけど、penup()で線は消せる。pendown()で再度線が描かれる。
これだけあれば四角形とか簡単な図形はかけたりタートルを動かせる。
今回はタートルはいらなくて色の着いたドットだけを書いていく。
流れとしてはこんな感じ
- ダミアンハーストの色味をcolorgram.pyを使って色を取得する
- その色をランダムに選んで左下から等間隔に右に塗っていく
- 10点塗り終わったら左端に戻って、50ピクセル上に上がってから同じことを繰り返す
- 10回繰り返したら完成
ではやっていきます。
colorgram.pyで使われている色を取得する
import colorgram
まずcolorgramをインポート。
多分システムにないと思うのでpycharmでインストールするか、
ターミナルでドキュメントに書いてある通り
pip install colorgram.py
でインストールしてからimportする。
colors = colorgram.extract('image.jpg', 30)
このコードで、image.jpgにある色を30色取得してくれる。
colorsはリストの中に30色分のcolors.rgbのオブジェクトで取得された色がある。
このままだと色が呼び出せないので、.rgbメソッドでrgbデータを取得する。
rgb_colors = []
for color in colors:
rgb_colors.append(color.rgb)
rgb_colorsのリストの中にcolor.rgbで吐き出されたrgbデータを追加していく。
Turtle, Screenのインポート
from turtle import Turtle, Screen
次にタートルモジュールとウィンドウの調整ができるScreenをインポートする。
tim = Turtle()
screen = Screen()
screen.colormode(255)
screenはタートルを動かすウィンドウの設定のことで、
colormode(255)にすることでrgbの設定が使えるようになる。
これだけだと絵を描き終わったらすぐウィンドウが閉じてしまうので、
一番最後に
screen.exitonclick()
を書いておく。これは自動的に閉じるんじゃなくて、
描画が終わった後にクリックをしたら閉じるメソッド。
スクリーン・タートルの初期値を設定する
tim.speed(0)
tim.ht()
tim.penup()
x = -250
y = -250
tim.setpos(x, y)
タートルはデフォルトだと矢印が出るので、消したい。
消すメソッドはht()、もしくはhideturtle()で消える。
最初に動くのを見たい場合は書かないで動かしてもいいかも。
penup()は文字通りペンが浮いている状態なので、タートルを動かしても線は出ない状態。
setpos(x,y)ではタートルをどの部分に置くかを設定できる。
キャンバスの真ん中が0,0としているので、左と下に-250ずつ移動している状態。
横に10回を上に10回繰り返して100個のドットを作る
for i in range(10):
tim.setpos(x, y)
for i in range(10):
tim.pendown()
tim.pencolor(random.choice(rgb_colors))
tim.pensize(25)
tim.forward(1)
tim.penup()
tim.forward(50)
y += 50
screen.exitonclick()
for分を横10個を繰り返す分と、
これを10回繰り返す分と入れ子にして描画するようにしています。
最初にポジションをセットして、pendown()で描画開始。
.pencolor()でドットの色を先ほどリストに入れた色からランダムで選んで指定。
.pensize()でドットの大きさを指定しています。
.forward(1)で1ピクセルだけ移動しているので実質点を描画。
終わったらpenup()で再び線をひかないようにしてから右に50進んでいます。
これを10回繰り返すと10個のランダムに色付けされた点ができます。
10回くらい返した後に、yを50プラスすることで11個目のドットが
左端で50上に行ったところからスタートするようになり、また10このドットを描画…という流れ。
これであっというまにアート作品のできあがり。
全体のコードはこちら。
from turtle import Turtle, Screen
import random
import colorgram
rgb_colors = []
colors = colorgram.extract('hirst-painting-start/image.jpg', 50)
for color in colors:
rgb_colors.append(color.rgb)
tim = Turtle()
screen = Screen()
screen.colormode(255)
tim.ht()
tim.penup()
x = -250
y = -250
tim.setpos(x, y)
for i in range(10):
tim.setpos(x, y)
for i in range(10):
tim.pendown()
tim.pencolor(random.choice(rgb_colors))
tim.pensize(25)
tim.forward(1)
tim.penup()
tim.forward(50)
y += 50
screen.exitonclick()