Sunday, January 31, 2021

Python - Project - Snake Game - Part 1 (Day 20)

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)


Note



1. In order to make our snake move smoothly, using tracer to turn off the animation.

from turtle import Screen
# Get screen instance
screen = Screen()

# Turn turtle animation off
screen.tracer(0)

# Perform a TurtleScreen update
screen.update()


2. Using time module to slow down the while loop

import time

time.sleep(0.1)


3. Create a Snake Class to wrap the relating functionality


Snake Game (Part 1)



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:
            segment = Turtle()
            segment.shape("square")
            segment.color("white")
            segment.penup()
            segment.setpos(position)

            self.segments.append(segment)

    # 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

# 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()

# 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()


# Bind bye() method to mouse clicks on the Screen.
screen.exitonclick()


Highlight



1. Defining constants can help you to config the game easily

2. Define attributes if possible, and please don't use magic number

Saturday, January 30, 2021

Python - Higher Order Functions, Instances and States (Day 19)

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.


Higher Order Functions



Can a function be passed to another function as an input?

A function is called Higher Order Function if it contains other functions as a parameter or returns a function as an output i.e, the functions that operate with another function are known as Higher Order Functions. Reference

Ex:
# Normal Function
def add(num_1, num_2):
    """add"""
    return num_1 + num_2

# Normal Function
def subtract(num_1, num_2):
    """subtract"""
    return num_1 - num_2

# This is a Higher Order Function
def calculator(num_1, num_2, func):
    """calculator"""
    return func(num_1, num_2)


# Call calculator func and pass add func as an input
# Notice that there is no () after 'add'
result = calculator(1, 2, add)
print(result)



Event Listener in Turtle Graphics 



Using Event Listener of Turtle Graphics to practice how to pass a function as an input.

Ex: Make turtle can move forward and back, and turn left and right
# Import Turtle and Screen Class from turtle module
from turtle import Turtle, Screen


def move_up():
    my_turtle.setheading(90)
    my_turtle.forward(50)


def move_down():
    my_turtle.setheading(270)
    my_turtle.forward(50)


def move_right():
    my_turtle.setheading(0)
    my_turtle.forward(50)


def move_left():
    my_turtle.setheading(180)
    my_turtle.forward(50)


# Get Screen Instance
screen = Screen()
# Bind each functions to key-release event
# of 'Up', 'Down', 'Right' and 'Left' key
screen.onkey(move_up, "Up")
screen.onkey(move_down, "Down")
screen.onkey(move_right, "Right")
screen.onkey(move_left, "Left")
# Set focus on TurtleScreen (in order to collect key-events)
screen.listen()

# Get turtle object
my_turtle = Turtle()

# Bind bye() method to mouse clicks on the Screen.
screen.exitonclick()



Instances and States



Since 'Turtle' class is a blueprint, we can build lots of turtle objects.

                object class
(instance 1)  my_turtle_1 = Turtle()
  (instance 2)  my_turtle_2 = Turtle()


Then in programming, we call my_turtle_1 and my_turtule_2 are separated instance.
Also, each instance (created by the same blueprint) maintains its own state.

my_turtle_1.shape('turtle')
my_turtle_2.shape('arrow')



Challenge - Race Game



Build a turtle race game, and allow users to guess which turtle will win the race?

import random
from turtle import Turtle, Screen

# Get Screen Instance
screen = Screen()

# Pop up a dialog window for input of a number
user_guess = screen.textinput(
    "Guess",
    "Which Turtle will win this race? input color
(red, orange, yellow, green and blue):",
)

turtles = []
colors = ["red", "orange", "yellow", "green", "blue"]
y_positions = [-100, -50, 0, 50, 100]

# Init all turtles
for index in range(0, 5):
    my_turtle = Turtle()
    my_turtle.shape("turtle")
    my_turtle.speed("fastest")
    my_turtle.color(colors[index])
    my_turtle.penup()
    my_turtle.setpos(-250, y_positions[index])
    turtles.append(my_turtle)


game_end = False

while not game_end:
    # make all turtles run randomly
    for index in range(0, 5):
        turtles[index].forward(random.randint(10, 50))

    # terminate race game if at lease one turtle reach finished line
    for index in range(0, 5):
        if turtles[index].xcor() > 250:
            game_end = True

# Custom sorting function to determine the sorting criteria
def my_sort(item):
    """my_sort"""
    return item.xcor()


# Sorting turtles list based on a custom function
# The fastest turtle will be the first element in this list after sorting
turtles.sort(reverse=True, key=my_sort)

# Checking the guess result
if user_guess == turtles[0].pencolor():
    print("You win")
else:
    print(f"You lose, the winner is the {turtles[0].pencolor()} turtle")

# Bind bye() method to mouse clicks on the Screen.
screen.exitonclick()

Friday, January 29, 2021

Vim notes

# Text editor - vi/vim

## Command Mode (default)
### Basic movement
    h       left
    j       down
    k       up
    l       right
    ^       go to beginning of the line
    $       go to the end of the line
    gg      go to the first line
    G       go to the last line
    w       Move by word to 'start'
    e       Move by word to 'end'
    :n      Go to line n(:set nu display line number)

### Deleting Text
    x       delete character
    dd      delete line
    dw      delete word

### Copy/paste
    yy      yank line
    p       paste after cursor
    P       paste before cursor

### Undo/Redo
    u       undo
    Ctrl r  redo

## Insert Mode
### Inserting Text
    i        insert an cursor
    I        insert an the beginning of the line
    a        append after cursor
    A        append at the end of the line
    o        open a new line below the current line
    O       open a new line above the current line

### Save and Quit
    :w      Write(save)
    :wq    Write and quit
    :q       Quit
    :q!      Force quit, don't save changes
    :wq!   Force write and quit

## Visual Mode
    v       start visual mode
    V       start linewise visual mode

Linux 101 - Basic Operation

# Shell
    A shell is a special-purpose program designed to read commands typed by a user and execute 
appropriate programs in response to those commands.
    Such as program is sometimes known as a command interpreter - 
definition from 'Linux Programming Interface'

        User
           |
        Shell
           |
        Kernel
           |
        Hardware

    The shell is the Linux command line interpreter.
    It provides an interface between the user and the kernel and executes programs called commands.
    For example, if a user enters ls then the shell executes the ls command.
    The shell can also execute other programs such as applications, scripts, and user programs. 
(Refer this article)

# Bash
    Bash (Bourne-Again Shell) is the default shell for Linux system

    $ echo $SHELL
    /bin/bash

# Basic operation between files and directories
    // Print name of current/working directory
    $ pwd
    /home/fcheng

    // List directory contents
    $ ls

    // Show detail
    $ ls --help
    $ man ls

    // do not ignore entries starting with . (hidden files)
    $ ls -a

    // Use a long listing format
    $ ls -l
    -rw-rw-r-- 1 fcheng fcheng    0 Jan 28 22:46 test.txt
    drwxr-xr-x 2 fcheng fcheng 4096 Jan 28 02:28 Videos

    NOTE:
        -: file
        d: directory

    // Sort by modification time
    $ ls -t

    // Show size
    $ ls -s

    // Human readable
    $ ls -h

    // Clear screen
    $ clear

    // Change file timestampes (Create empty file if that file does not exist)
    $ touch demo.txt

    // Remove a file
    $ rm demo.txt

    // Create a folder
    $ mkdir demo

    // Remove a folder
    $ rm -rf demo

    // Show commands history
    $ history

# File Directory Structure

    /
        /bin    User Binaries                ls, touch, rm ...
        /sbin   System Binaries              ip
        /etc    Configuration Files          /etc/mysel/conf.d
        /dev    Device Files
        /proc   Process Information
        /var    Vriable Files                /var/log/
        /tmp    Temporary Files
        /usr    User Programs
        /home   Home Directories             /home/fcheng/
        /boot   Boot Loader Files
        /lib    System Libraries
        /opt    Optional add-on Apps
        /mnt    Mount Directory
        /media  Removable Devices
        /src    Service Data

# Useful commands to print files
## cat
    // concatenate files and print on the standard output
    $ cat /etc/profile

    // Show line number (ignore blank)
    $ cat -b /etc/profile

## more
    // file perusal filter for crt viewing
    $ more /var/log/syslog

## less
    // less - opposite of more
    $ less /var/log/syslog

### less commands
    After executing less, you can use following commands to do more

    // next page
    f

    // Go to page start
    g

    // Go to page end
    G

    // Search
    /

## head
    // output the first part of files
    $ head /var/log/syslog

## tail
    // output the first part of files
    $ tail /var/log/syslog

    NOTE: It is useful
    // output appended data as the file grows;
    $ tail -f /var/log/syslog

Wednesday, January 27, 2021

Python - Turtle Graphics (Day 18)

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.


Goal



This section is to guide you how to use a new package/module.
It uses turtle package as an example.

Researching skill is important. You need to lean how to google, how to read documentation, and how to ask question from stackoverflow. In real life, as a developer, you need to solve problems by yourself or improve your skill by googling without guiding by teachers step by step.


How to import package/module


Basic import

Ex:
# import is a keyword
# turtle is a module name
import turtle

# Using the Turtle class in turtle module to create an object
my_turtle = turtle.Turtle()


from ... import ...

Ex:

# from is a keyword
# import is a keyword
# turtle is a module name
# Turtle is a thing in turtle module
from turtle import Turtle

# Using Turtle class to create an object
my_turtle = Turtle()


We even can import all things in module (but it is not recommended)
Ex:
# Import all things in turtle module
from turtle import *

# Using Turtle class to create an object
my_turtle = Turtle()


It can help you to reduce typing. But the drawback is that you cannot clearly know where the class comes from. Please try to avoid this coding style.

Aliasing Modules


Sometimes module names are quite long, and you may want to give them alias name to reduce the typing.

# import is a keyword
# as is a keyword
# turtle is a module name
# t is the alias name of turtle module name
import turtle as t

# Using Turtle class to create an object
my_turtle = t.Turtle()


Installing Modules


'turtle' is packed in the python standard library, so you don't need to install it before importing.

For example, if we want to use this hero package which is not in python standard library, then first of all, we need to run 'pip install heroes' to install it. Otherwise, you will get error such as "No module named 'heros'"

# import heroes package
import heroes

print(heroes.gen())


Error:
ModuleNotFoundError: No module named 'heroes'


Once you have installed it, the error above will be gone.


Tuple V.S. List



 * Immutable *

A tuple in Python is similar to a list.
The difference between these two is that we cannot change the elements of a tuple once it is assigned whereas we can change the elements of a list. Reference

my_tuple = (1, 3, 5)

# Try to update tuple
my_tuple[2] = 6


Error: 
typeError: 'tuple' object does not support item assignment



Exercise for turtle graphic


Exercise 1 - Draw a square

# import Turtlr Class from turtle module
from turtle import Turtle

# Initiate a turtle object
my_turtle = Turtle()
# Setup turtle shape
my_turtle.shape("turtle")

# loop for it four times for each edge of a square
for _ in range(4):
    my_turtle.forward(50)
    my_turtle.left(90)


Exercise 2 - Draw a dashed line

# import Turtlr Class from turtle module
from turtle import Turtle

# Initiate a turtle object
my_turtle = Turtle()
# Setup turtle shape
my_turtle.shape("turtle")

# How many dashed line
for i in range(5):
    # pendown: drawing when moving.
    my_turtle.pendown()
    my_turtle.forward(5)
    # penup: no drawing when moving.
    my_turtle.penup()
    my_turtle.forward(5)


Exercise 3 - Draw a triangle, square, pentagon, hexagon, heptagon, octagon, nonagon, and decagon

# import Turtlr Class from turtle module
from turtle import Turtle

# Initiate a turtle object
my_turtle = Turtle()
# Setup turtle shape
my_turtle.shape("turtle")

# Define a draw function
def draw(num_sides):
    """draw"""
    for _ in range(num_sides):
        my_turtle.forward(50)
        my_turtle.left(360 / num_sides)


# Ask user input
user_selection = int(
    input(
        "Draw a triangle, square, pentagon, hexagon, heptagon,
octagon, nonagon, and decagon? "
    )
)

draw(user_selection)


Exercise 4 - Generate a random walk

# import random module
import random

# import Turtlr Class from turtle module
from turtle import Turtle

# Initiate a turtle object
my_turtle = Turtle()
# Setup turtle shape
my_turtle.shape("turtle")

# Define direction list
# Reference:
# https://docs.python.org/3/library/turtle.html#turtle.setheading
diections = [0, 90, 180, 270]

# run 500 times
for _ in range(500):
    my_turtle.forward(random.randint(1, 40))
    my_turtle.setheading(random.choice(diections))


Exercise 5 - Draw a Spirograph

# import Turtle Class from turtle module
from turtle import Turtle

# Initiate a turtle object
my_turtle = Turtle()
# Setup turtle shape
my_turtle.shape("turtle")
# Setup turtle speed
my_turtle.speed("fastest")

# Draw
def draw(size_of_gap):
    """draw"""

    # how many circle
    for _ in range(int(360 / size_of_gap)):
        my_turtle.circle(100)
        my_turtle.setheading(my_turtle.heading() + size_of_gap)


draw(10)



Challenge - Hirst Painting


1. Using https://pypi.org/project/colorgram.py/ to extract colors from an image
2. Using turtle graphic to draw a spot painting like Hirst's with those extracted colors

Ex: Extract colors from an image via colorgram package 
# Import colorgram package
import colorgram

# Define a list to store tuple elements
color_list = []
# Extract 15 colors from an image.
colors = colorgram.extract("image.jpg", 15)

# Loop through all colors
for c in colors:
    color_list.append((c.rgb[0], c.rgb[1], c.rgb[2]))

# Print list
print(color_list)


Result:
[(221, 223, 226), (234, 229, 219), (236, 227, 232), (223, 231, 227),
(196, 164, 125), (156, 164, 179), (210, 205, 141), (148, 83, 61),
(83, 92, 116), (158, 176, 169), (188, 153, 162), (22, 25, 53),
(61, 21, 13), (183, 167, 42), (40, 23, 30)]


Ex: Use turtle graphic to draw a spot painting based on the colors from the first challenge
# import Turtle and Screen Class from turtle module
import random
from turtle import Turtle, Screen

# Define colors from the previous challenge
colors = [
    (221, 223, 226),
    (234, 229, 219),
    (236, 227, 232),
    (223, 231, 227),
    (196, 164, 125),
    (156, 164, 179),
    (210, 205, 141),
    (148, 83, 61),
    (83, 92, 116),
    (158, 176, 169),
    (188, 153, 162),
    (22, 25, 53),
    (61, 21, 13),
    (183, 167, 42),
    (40, 23, 30),
]

# Get Screen instance
screen = Screen()
# Setup color mode (rgb)
screen.colormode(255)

# Initiate a turtle object
my_turtle = Turtle()
# Hide turtle
my_turtle.hideturtle()
# Setup turtle speed
my_turtle.speed("fastest")
# No drawing when moving
my_turtle.penup()
# Setup starting position of turtle
my_turtle.setpos(-250, -250)

# Define how many dots we want to draw
remaining_dots = 100

# if 100 dots have not been drawn yet
while remaining_dots > 0:
    # Change Y position for every 10 dots
    if remaining_dots != 100 and remaining_dots % 10 == 0:
        my_turtle.setpos(-250, my_turtle.ycor() + 50)

    # Draw a circular dot with diameter size and using color
    my_turtle.dot(20, random.choice(colors))
    # Move
    my_turtle.forward(50)

    remaining_dots -= 1

# Bind bye() method to mouse clicks on the Screen.
screen.exitonclick()