This is a 100 Days challenge to learn a new language (Python). 100 Days of Code - The Complete Python Pro Bootcamp
I will post some notes to motivate myself to finish this challenge.
Project - Snake Game
We will build a snake game on day 20 and 21.
Break down the problem:
1. Create a snake body (day 20)
2. Move the snake (day 20)
3. Control the snake (day 20)
4 Detect collision with food (day 21)
5. Create a scoreboard (day 21)
6. Detect collision with wall (day 21)
7. Detect collision with tail (day 21)
In order to complete our snake game, we need to introduce inheritance first.
Inheritance
Inheritance allows us to define a class (child) that inherits all the attributes and methods from another class (parent).
Ex:
class Animal:
"""Animal"""
def __init__(self):
self.num_eyes = 2
print("I am an animal")
def show_eye_number(self):
"""get_eye_number"""
print(f"I have {self.num_eyes} eyes")
def breath(self):
"""breath"""
print("I am breathing")
# Define a Fish class which inherit from Animal Class
class Fish(Animal):
"""Fish"""
def __init__(self):
# Trigger parent's constructor
super().__init__()
print("I am a fish")
# Override parent function
def breath(self):
"""breath"""
# Trigger parrent's breath function
super().breath()
print("I am breathing under the water")
# New child function
def swin(self):
"""swim"""
print("I am swimming")
# Initiate an animal
animal = Animal()
animal.show_eye_number()
animal.breath()
# Initiate a fish
fish = Fish()
# Call an inherited function
fish.show_eye_number()
# Call an overridden function
fish.breath()
# Call Fish's own function
fish.swin()
If child class define the same methods like that of parent class, it will override the methods which are inherited from the parent class
Python provide super() to help to call parent's methods
Snake Game (Part 2)
food.py
import random
from turtle import Turtle
class Food(Turtle):
"""Food"""
# Constructor
def __init__(self):
# Call Turtle's constructor
super().__init__()
self.create_food()
# create_food
def create_food(self):
"""create_food"""
# Setup food
self.shape("circle")
self.penup()
self.color("red")
self.shapesize(stretch_wid=0.5, stretch_len=0.5)
self.speed("fastest")
# refresh food with random position
self.refresh()
# Update food position
def refresh(self):
"""refresh"""
x_position = random.randint(-280, 280)
y_position = random.randint(-280, 280)
self.setpos(x_position, y_position)
scoreboard.py
from turtle import Turtle
class Scoreboard(Turtle):
"""Scoreboard"""
# Constructor
def __init__(self):
super().__init__()
# Define an attribute to track score
self.score = 0
self.create_scoreboard()
# create_scoreboard
def create_scoreboard(self):
"""create_scoreboard"""
self.hideturtle()
self.color("white")
self.penup()
self.setpos(0, 280)
self.refresh_score()
# increase_score
def increase_score(self):
"""increase_score"""
self.score += 1
self.refresh_score()
# refresh_score
def refresh_score(self):
"""refresh_score"""
self.clear()
self.write("Score: " + str(self.score), False, align="center")
# game_over
def game_over(self):
"""game_over"""
self.setpos(0, 0)
self.write("Game Over", False, align="center")
snake.py
from turtle import Turtle
# Constants
STARTING_POSITIONS = [(0, 0), (-20, 0), (-40, 0)]
MOVING_DISTANCE = 20
UP = 90
DOWN = 270
LEFT = 180
RIGHT = 0
class Snake:
"""Snake Class"""
# Constructor
def __init__(self):
# Define an attribute to track snake body
self.segments = []
# Call func to init snake
self.create_snake()
# Create a attribute instead of using magic number 0
self.head = self.segments[0]
# Methods - create snake
def create_snake(self):
"""Adding segment(turtle object) to segments list"""
for position in STARTING_POSITIONS:
# add segment with passing position one by one
self.add_segment(position)
# add_segment
def add_segment(self, position):
"""add_segment"""
segment = Turtle()
segment.shape("square")
segment.color("white")
segment.penup()
segment.setpos(position)
self.segments.append(segment)
# extend
def extend(self):
"""extend"""
self.add_segment(self.segments[-1].pos())
# Methods - move
def move(self):
"""move"""
# Starting from tail,
# make each segment move to the position of the previous segment
for index in range(len(self.segments) - 1, 0, -1):
new_x = self.segments[index - 1].xcor()
new_y = self.segments[index - 1].ycor()
self.segments[index].setpos(new_x, new_y)
# Magic number!!
# self.segments[0].forward(MOVING_DISTANCE)
# Using attribute instead of magic number
self.head.forward(MOVING_DISTANCE)
def move_up(self):
"""move_up"""
if self.head.heading() != DOWN:
self.head.setheading(UP)
def move_right(self):
"""move_right"""
if self.head.heading() != LEFT:
self.head.setheading(RIGHT)
def move_left(self):
"""move_left"""
if self.head.heading() != RIGHT:
self.head.setheading(LEFT)
def move_down(self):
"""move_down"""
if self.head.heading() != UP:
self.head.setheading(DOWN)
main.py
import time
from turtle import Screen
from snake import Snake
from food import Food
from scoreboard import Scoreboard
# Screen Setup
screen = Screen()
screen.setup(width=600, height=600)
screen.title("My Snake Game")
screen.bgcolor("black")
# Turn turtle animation off# Disable screen
screen.tracer(0)
# init a snake
snake = Snake()
# init a food
food = Food()
# init a scoreboard
scoreboard = Scoreboard()
# Listen events
# Bind fun to key-release event of key
screen.onkey(snake.move_up, "Up")
screen.onkey(snake.move_down, "Down")
screen.onkey(snake.move_right, "Right")
screen.onkey(snake.move_left, "Left")
# Set focus on TurtleScreen (in order to collect key-events)
screen.listen()
game_is_on = True
while game_is_on:
# Perform a TurtleScreen update# Update Screen
screen.update()
# Give some delay for this while loop
time.sleep(0.1)
# Keep moving
snake.move()
# Check if there is a collision for food
if snake.head.distance(food) < 15:
# Increase score
scoreboard.increase_score()
# Make snake longer
snake.extend()
# Update food position
food.refresh()
# Check if there is a collision for wall
if (
snake.head.xcor() >= 285
or snake.head.xcor() <= -285
or snake.head.ycor() >= 285
or snake.head.ycor() <= -285
):
# Terminate the while loop
game_is_on = False
# Show game over message
scoreboard.game_over()
# Check if there is a collision for tails
for segment in snake.segments:
# Ignore head
if segment == snake.head:
continue
if snake.head.distance(segment) <= 15:
# Terminate the while loop
game_is_on = False
# Show game over message
scoreboard.game_over()
# Bind bye() method to mouse clicks on the Screen.
screen.exitonclick()
Highlight
1. In Python, negative index of list mean it counts from the right (instead of from the left) Reference
snake.py
# extend
def extend(self):
"""extend"""
self.add_segment(self.segments[-1].pos())
2. The following code can be improved by using list slicing.
main.py
# Check if there is a collision for tails
for segment in snake.segments:
# Ignore head
if segment == snake.head:
continue
if snake.head.distance(segment) <= 15:
# Terminate the while loop
game_is_on = False
# Show game over message
scoreboard.game_over()
main.py (Using List Slicing)
# Check if there is a collision for tails
for segment in snake.segments[1:]:
if snake.head.distance(segment) <= 15:
# Terminate the while loop
game_is_on = False
# Show game over message
scoreboard.game_over()
List Slicing
If we want to take some item from a list, we can iterate it and pick up what we want based on some conditions. In python, it provide a handy way to achieve it. It is slicing.
Format:
list[start:end:jump]
Ex:
list = ["a", "b", "c", "d", "e"]
print(list[::])
# ['a', 'b', 'c', 'd', 'e']
list = ["a", "b", "c", "d", "e"]
print(list[2::])
# ['c', 'd', 'e']
list = ["a", "b", "c", "d", "e"]
print(list[:2:])
# ['a', 'b']
list = ["a", "b", "c", "d", "e"]
print(list[::2])
# ['a', 'c', 'e']
list = ["a", "b", "c", "d", "e"]
print(list[::-1])
# ['e', 'd', 'c', 'b', 'a']
No comments:
Post a Comment