├── README.md ├── lesson04 ├── factorial.py ├── factorial2.py ├── hello.py ├── hello2.py ├── hello3.py ├── hello4.py ├── index.html ├── string_format.py ├── string_rectangle.py └── unicode_script.py ├── lesson05 ├── Fibonacci_spiral.py ├── animation_off.py ├── color.py ├── coordinates.py ├── draw_string.py ├── egg.py ├── golden_spiral.py ├── index.html ├── mushroom.py ├── square.py ├── star.py ├── star2.py └── yin_yang.py ├── lesson06 ├── BMI.py ├── coin_toss.py ├── coin_toss2.py ├── exam.py ├── exam2.py ├── index.html ├── quad_equation.py └── turtle_escape.py ├── lesson07 ├── Collatz_conj.py ├── Fibonacci_spiral.py ├── chicken_dog.py ├── chicken_dog2.py ├── clock.py ├── counter.py ├── factorial.py ├── factorial2.py ├── flower_script.py ├── golden_spiral.py ├── index.html ├── perfect_square.py ├── perfect_square2.py ├── square.py ├── square2.py ├── square3.py ├── triangle.py ├── triangle2.py └── turtle_draw.py ├── lesson08 ├── Newton_sqrt.py ├── Newton_sqrt2.py ├── chess_board.py ├── docstring.py ├── expression_stm.py ├── expression_value.py ├── figures.py ├── hello.py ├── hello2.py ├── index.html ├── random_square.py ├── sqrt.py ├── sqrt_cal.py ├── sqrt_cal2.py ├── sqrt_program.py └── turtle_draw.py ├── lesson09 ├── Maclaurin_cos.py ├── Pisa_tower.py ├── bisection_sqrt.py ├── bloom_square.py ├── figures.py ├── index.html ├── number_digits.py ├── number_digits2.py ├── number_digits3.py ├── number_digits4.py ├── para_curves.py ├── para_curves2.py ├── pp_curves.py ├── pp_curves_exercise.py └── prime.py ├── lesson10 ├── amodule.py ├── func_cls.py ├── hello.py ├── index.html ├── pizza.gif ├── scope.py ├── turtle_draw.py └── turtle_race.py ├── lesson11 ├── Pascal_triangle.py ├── ascending.py ├── cmd_args.py ├── hello.py ├── index.html ├── k_max.py ├── list_remove_dup.py ├── max.py ├── polynomial.py ├── polynomial2.py ├── score.py ├── second_max.py ├── sqrt_cal.py ├── sqrt_cal2.py ├── sqrt_cal3.py ├── sqrt_script.py └── turtle_draw.py ├── lesson12 ├── char_freq.py ├── char_freq2.py ├── chicken_dog.py ├── hello.py ├── hello2.py ├── hello3.py ├── index.html ├── turtle_draw.py ├── turtle_draw2.py └── turtle_escape.py ├── lesson13 ├── Monty_Hall.py ├── bau_cua.py ├── chart.py ├── coin_toss.py ├── coin_toss2.py ├── con_lac_don.gif ├── con_lac_don.py ├── histogram_boxplot.png ├── histogram_boxplot.py ├── image.jpeg ├── image_dot.png ├── image_violet.jpeg ├── image_walk.png ├── index.html ├── interactive_plot.py ├── parabol_area.py ├── parabol_draw.py ├── pillow_draw.png ├── pillow_draw.py ├── random_dots.mp4 ├── random_dots.py ├── random_walks.py ├── randot_video.py └── violet_img.py ├── lesson14 ├── fraction.py ├── fraction2.py ├── fraction3.py ├── index.html ├── k_max.py ├── many_wheels.py ├── pizza.gif ├── range_iterator.py ├── record.py ├── sqrt_cal.py ├── students_list.py ├── students_list2.py ├── tri_color_wheel.py ├── turtle_race.py ├── turtle_race2.py ├── write_char.py └── write_char2.py ├── lesson15 ├── Newton_sqrt.py ├── REP_emulate.py ├── factorial.py ├── factorial2.py ├── factorial3.py ├── factorial4.py ├── index.html ├── run_factorial.py ├── run_factorial2.py ├── sqrt_cal.py └── turtle_escape.py ├── lesson16 ├── index.html ├── pygame_draw.py ├── pygame_intro.py ├── screen_saver.py ├── snake.py └── tetris │ ├── index.html │ ├── music.mp3 │ ├── sound_gameover.wav │ ├── sound_landing.wav │ ├── sound_match.wav │ ├── sound_move.wav │ ├── sound_pause.wav │ ├── sound_play.wav │ ├── sound_rotate.wav │ └── tetris.py ├── lesson17 ├── Pascal_redirect.py ├── Pascal_triangle.py ├── Pascal_triangle.txt ├── Zen_of_Python.txt ├── asc_redirect.py ├── asc_redirect2.py ├── ascending.py ├── hello.py ├── hello_message.txt ├── index.html ├── rand_int.py ├── rand_int.txt ├── read_CSV.py ├── read_CSV2.py ├── read_JSON.py ├── read_ZoP.py ├── read_ZoP2.py ├── read_ZoP3.py ├── sorted_int.txt ├── student_data_model.py ├── sv.csv ├── sv.json ├── sv2.csv ├── triangle.py ├── triangle.txt ├── write_CSV.py ├── write_CSV2.py └── write_JSON.py ├── lesson18 ├── Euclid_gcd.py ├── Euclid_gcd2.py ├── Ha_Noi_Tower.py ├── Sierpinski_carpet.py ├── factorial_rec.py ├── find_primes.py ├── get_removed_duplicates.py ├── guess_number.py ├── guess_number2.py ├── index.html ├── naive_gcd.py ├── remove_duplicates_rec.py ├── sum_rec.py ├── sumdigit_rec.py └── sumlist_rec.py ├── lesson19 ├── caro_game │ ├── about_icon.png │ ├── caro_dialog.py │ ├── caro_game.py │ ├── caro_game_grid.py │ ├── caro_icon.png │ ├── index.html │ ├── new_icon.png │ ├── open_icon.png │ ├── redo_icon.png │ ├── save_icon.png │ ├── settings_icon.png │ └── undo_icon.png ├── factorial_GUI.py ├── index.html └── student_management │ ├── index.html │ ├── student_data_model.py │ ├── student_management.py │ └── student_widgets.py └── python_part_1_2.pdf /README.md: -------------------------------------------------------------------------------- 1 | # hBook Bí kíp luyện Lập trình nhập môn với Python, Vũ Quốc Hoàng 2 | 3 | Sách tự học lập trình cho nhiều đối tượng (từ lớp 8 trở lên). Dự kiến gồm 3 phần với 20 bài. Chúng tôi đang hoàn thiện cuốn sách. 4 | 5 | Bản thảo Phần 1-2 được để [tại đây](https://github.com/vqhBook/python/blob/master/python_part_1_2.pdf). 6 | 7 | Bạn có thể tạo một issue [tại đây](https://github.com/vqhBook/python/issues) để đóng góp ý kiến hoặc gởi mail về vqhoang.books@gmail.com. Hoan nghênh mọi ý kiến đóng góp! 8 | 9 | *Click Star nếu bạn thấy nội dung cuốn sách có ích. Cảm ơn bạn!* 10 | 11 | Copyright © 2020 Vũ Quốc Hoàng (hBook). 12 | 13 | **Nghiêm cấm mọi hình thức kiếm tiền từ tài liệu này!** 14 | -------------------------------------------------------------------------------- /lesson04/factorial.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | a = input("Nhập n: ") 4 | n = int(a) 5 | f = math.factorial(n) 6 | print("Giai thừa là:", f) 7 | -------------------------------------------------------------------------------- /lesson04/factorial2.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | n = int(input("Nhập n: ")) 4 | print("Giai thừa là:", math.factorial(n)) 5 | -------------------------------------------------------------------------------- /lesson04/hello.py: -------------------------------------------------------------------------------- 1 | # The first Python program 2 | 3 | name = input("What's your name? ") 4 | print("Hello", name) 5 | -------------------------------------------------------------------------------- /lesson04/hello2.py: -------------------------------------------------------------------------------- 1 | # Chương trình Python đầu tiên 2 | 3 | tên = input("Tên của bạn là gì? ") 4 | print("Chào", tên) 5 | -------------------------------------------------------------------------------- /lesson04/hello3.py: -------------------------------------------------------------------------------- 1 | nhập = input; xuất = print 2 | 3 | tên = nhập("Tên của bạn là gì? ") 4 | xuất("Chào", tên) 5 | -------------------------------------------------------------------------------- /lesson04/hello4.py: -------------------------------------------------------------------------------- 1 | # Python program with "pausing" 2 | 3 | name = input("What's your name? ") 4 | print("Hello", name) 5 | 6 | input("\nPress any key to quit") 7 | -------------------------------------------------------------------------------- /lesson04/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson04/string_format.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | r = float(input("Nhập bán kính: ")) 4 | c = 2 * math.pi * r 5 | s = math.pi * r**2 6 | print("Chu vi là: %.2f" % c) 7 | print("Diện tích là: %.2f" % s) 8 | -------------------------------------------------------------------------------- /lesson04/string_rectangle.py: -------------------------------------------------------------------------------- 1 | char = input("Nhập kí hiệu vẽ: ") 2 | ncol = int(input("Nhập số cột: ")) 3 | nrow = int(input("Nhập số dòng: ")) 4 | line = char * ncol + "\n" 5 | rect = line * nrow 6 | print("\n" + rect) 7 | -------------------------------------------------------------------------------- /lesson04/unicode_script.py: -------------------------------------------------------------------------------- 1 | print("I \u2764 you!") 2 | print(10, chr(0x2264), 11) 3 | print(f"Some emoji: \U0001F600, {chr(0x1F609)}") 4 | import math; print(f"\N{GREEK SMALL LETTER PI} = {math.pi}") 5 | print("Python can be pronounced as /ˈpaɪθən/ or /ˈpaɪθɑːn/") 6 | print("ฉันรักคุณ", "我爱你") 7 | -------------------------------------------------------------------------------- /lesson05/Fibonacci_spiral.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | t.speed("slowest") 3 | 4 | t.circle(0, 90); t.circle(1, 90); t.circle(1, 90) 5 | t.circle(2, 90); t.circle(3, 90); t.circle(5, 90) 6 | t.circle(8, 90); t.circle(13, 90); t.circle(21, 90) 7 | t.circle(34, 90); t.circle(55, 90); t.circle(89, 90) 8 | t.circle(144, 90); t.circle(233, 90); t.circle(377, 90) 9 | 10 | t.hideturtle() 11 | -------------------------------------------------------------------------------- /lesson05/animation_off.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | 3 | x = t.numinput("Hello", "Enter radius: ") 4 | y = 2**0.5 * x 5 | t.hideturtle() 6 | t.color("black", "yellow") 7 | 8 | t.tracer(False) 9 | t.up(); t.forward(x); t.left(135); t.down() 10 | t.begin_fill() 11 | t.forward(y); t.left(90); t.forward(y); t.left(90) 12 | t.forward(y); t.left(90); t.forward(y); t.left(90) 13 | t.end_fill() 14 | t.update() 15 | -------------------------------------------------------------------------------- /lesson05/color.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | 3 | r = 120 4 | t.up() 5 | t.left(90); t.forward(r); t.left(90) 6 | t.down() 7 | 8 | t.color("#FF0000") # Red 9 | t.begin_fill(); t.circle(r, 120); t.end_fill() 10 | 11 | t.colormode(255) 12 | t.color((0, 255, 0)) # Green 13 | t.begin_fill(); t.circle(r, 120); t.end_fill() 14 | 15 | t.colormode(1.0) 16 | t.color((0.0, 0.0, 1.0)) # Blue 17 | t.begin_fill(); t.circle(r, 120); t.end_fill() 18 | 19 | t.hideturtle() 20 | -------------------------------------------------------------------------------- /lesson05/coordinates.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | 3 | t.setworldcoordinates(0, 0, 7, 6) 4 | t.hideturtle() 5 | 6 | t.up(); t.goto(0, 2); t.down() 7 | t.goto(7, 2); t.goto(5, 0); t.goto(2, 0); t.goto(0, 2) 8 | t.up(); t.goto(2, 2); t.down() 9 | t.goto(2, 6); t.goto(4, 3); t.goto(2, 2) 10 | t.up(); t.goto(5, 2); t.down() 11 | t.goto(5, 4); t.goto(6, 2.5); t.goto(5, 2) 12 | -------------------------------------------------------------------------------- /lesson05/draw_string.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | t.hideturtle() 3 | 4 | t.up(); t.dot(5); t.write("Python 1") 5 | t.goto(100, 0); t.color("red"); t.dot(5) 6 | t.write("Python 2", align="center") 7 | t.goto(50, 100); t.color("blue"); t.dot(15) 8 | t.write("Python 3", align="right", font=("Arial", 30, "italic")) 9 | -------------------------------------------------------------------------------- /lesson05/egg.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | t.shape("turtle") 3 | t.width(2) 4 | r = 100 5 | 6 | t.right(90) 7 | t.circle(r, 90) 8 | t.circle(2*r, 45) 9 | t.circle(2*r - 2**0.5*r, 90) 10 | t.circle(2*r, 45) 11 | t.circle(r, 90) 12 | -------------------------------------------------------------------------------- /lesson05/golden_spiral.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | t.speed("slowest") 3 | 4 | PHI = (1 + 5**0.5)/2 5 | r = 377 6 | 7 | t.left(90) 8 | t.circle(-r, 90); t.circle(-r/PHI, 90) 9 | t.circle(-r/PHI**2, 90); t.circle(-r/PHI**3, 90) 10 | t.circle(-r/PHI**4, 90); t.circle(-r/PHI**5, 90) 11 | t.circle(-r/PHI**6, 90); t.circle(-r/PHI**7, 90) 12 | t.circle(-r/PHI**8, 90); t.circle(-r/PHI**9, 90) 13 | t.circle(-r/PHI**10, 90); t.circle(-r/PHI**11, 90) 14 | 15 | t.hideturtle() 16 | -------------------------------------------------------------------------------- /lesson05/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson05/mushroom.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | t.hideturtle() 3 | 4 | t.color("saddle brown") 5 | t.begin_fill() 6 | t.goto(100, 0); t.goto(100, -200); 7 | t.goto(-100, -200); t.goto(-100, 0) 8 | t.end_fill() 9 | 10 | t.color("orange red") 11 | t.begin_fill() 12 | t.goto(200, 0); t.setheading(90); t.circle(200, 180); t.goto(0, 0) 13 | t.end_fill() 14 | -------------------------------------------------------------------------------- /lesson05/square.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | t.shape("turtle") 3 | d = int(input("Kích thước hình vuông? ")) 4 | t.forward(d); t.left(90) 5 | t.forward(d); t.left(90) 6 | t.forward(d); t.left(90) 7 | t.forward(d); t.left(90) 8 | -------------------------------------------------------------------------------- /lesson05/star.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | t.shape("turtle") 3 | 4 | d = 200 5 | 6 | t.forward(d); t.left(120) # 1 --> 2 7 | t.forward(d); t.left(120) # 2 --> 3 8 | t.forward(d); t.left(120) # 3 --> 4 (1) 9 | 10 | t.up() 11 | t.forward(d/3); t.right(60) # 1 --> 5 (up) 12 | t.forward(d/3); t.left(120) # 5 --> 6 (up) 13 | t.down() 14 | 15 | t.forward(d); t.left(120) # 6 --> 7 16 | t.forward(d); t.left(120) # 7 --> 8 17 | t.forward(d); t.left(120) # 8 --> 9 (6) 18 | -------------------------------------------------------------------------------- /lesson05/star2.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | t.shape("turtle") 3 | t.speed("slowest") 4 | 5 | d = 200 6 | 7 | t.forward(d); t.left(120) 8 | t.forward(d); t.left(120) 9 | t.forward(2*d/3); t.left(60) 10 | t.forward(2*d/3); t.left(120) 11 | t.forward(d); t.left(120) 12 | t.forward(d); t.left(120) 13 | t.forward(d/3); t.right(60) 14 | t.forward(d/3); t.left(120) 15 | -------------------------------------------------------------------------------- /lesson05/yin_yang.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | r = 120 3 | 4 | t.color("red") 5 | t.begin_fill(); t.circle(r); t.end_fill() 6 | 7 | t.color("black") 8 | t.begin_fill() 9 | t.circle(r, 180); t.circle(r/2, 180); t.circle(-r/2, 180) 10 | t.end_fill() 11 | 12 | t.right(90); t.up(); t.forward(r/2 - r/10); t.right(90) 13 | t.color("black") 14 | t.begin_fill(); t.circle(r/10); t.end_fill() 15 | 16 | t.left(90); t.up(); t.forward(r); t.right(90) 17 | t.color("red") 18 | t.begin_fill(); t.circle(r/10); t.end_fill() 19 | 20 | t.hideturtle() 21 | -------------------------------------------------------------------------------- /lesson06/BMI.py: -------------------------------------------------------------------------------- 1 | print("Hoan nghênh đo chiều cao, cân nặng, đo huyết áp, thử sức kéo!") 2 | 3 | cân_nặng = float(input("Nhập cân nặng của bạn (kg): ")) 4 | chiều_cao = float(input("Nhập chiều cao của bạn (m): ")) 5 | 6 | BMI = cân_nặng / chiều_cao**2 7 | print(f"Chỉ số BMI của bạn là: {BMI:.1f}") 8 | 9 | if BMI < 18.5: 10 | print("Thân hình hơi gầy một tí!") 11 | print("Đề nghị ăn uống bồi dưỡng thêm.") 12 | elif BMI < 25: 13 | print("Thân hình hoàn toàn bình thường!") 14 | elif BMI < 30: 15 | print("Thân hình hơi béo một tí!") 16 | else: # BMI >= 30 17 | print("Thân hình không được bình thường!") 18 | print("Đề nghị tập thể dục thường xuyên.") 19 | -------------------------------------------------------------------------------- /lesson06/coin_toss.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | coin = random.randint(0, 1) 4 | choice = input("Bạn chọn ngửa hay sấp(N/S)? ") 5 | if coin == 1: 6 | print("Đồng xu ra ngửa") 7 | if choice == "N": 8 | print("Bạn chọn ngửa") 9 | print("Bạn lên voi!") 10 | else: 11 | print("Bạn chọn sấp") 12 | print("Bạn xuống chó!") 13 | else: # coin là 0 14 | print("Đồng xu ra sấp") 15 | if choice == "N": 16 | print("Bạn chọn ngửa") 17 | print("Bạn xuống chó!") 18 | else: 19 | print("Bạn chọn sấp") 20 | print("Bạn lên voi!") 21 | -------------------------------------------------------------------------------- /lesson06/coin_toss2.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | coin = random.randint(0, 1) 4 | choice = input("Bạn chọn ngửa hay sấp(N/S)? ") 5 | print("Đồng xu ra " + ("ngửa" if coin == 1 else "sấp")) 6 | print("Bạn chọn " + ("ngửa" if choice == "N" else "sấp")) 7 | if (coin == 1 and choice == "N" 8 | or coin == 0 and choice == "S"): 9 | print("Bạn lên voi!") 10 | else: 11 | print("Bạn xuống chó!") 12 | -------------------------------------------------------------------------------- /lesson06/exam.py: -------------------------------------------------------------------------------- 1 | điểm_thi = float(input("Bạn thi được bao nhiêu điểm? ")) 2 | if điểm_thi < 5: 3 | print("Chúc mừng! Bạn đã rớt.") 4 | else: 5 | print("Chia buồn! Bạn đã đậu.") 6 | -------------------------------------------------------------------------------- /lesson06/exam2.py: -------------------------------------------------------------------------------- 1 | điểm_thi = float(input("Bạn thi được bao nhiêu điểm? ")) 2 | if điểm_thi == 10: 3 | print("Bạn đỗ thủ khoa!") 4 | print("Liên hoan thôi") 5 | print("Ăn, uống, ngủ, nghỉ") 6 | -------------------------------------------------------------------------------- /lesson06/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson06/quad_equation.py: -------------------------------------------------------------------------------- 1 | print("Chương trình giải phương trình bậc 2: ax^2 + bx + c = 0 (a \u2260 0).") 2 | a = float(input("Nhập hệ số a (a \u2260 0): ")) 3 | b = float(input("Nhập hệ số b: ")) 4 | c = float(input("Nhập hệ số c: ")) 5 | 6 | if (delta := b**2 - 4*a*c) > 0: 7 | x1 = (-b + delta**0.5)/(2*a) 8 | x2 = (-b - delta**0.5)/(2*a) 9 | print("Phương trình có 2 nghiệm phân biệt:") 10 | print(f"x1 = {x1:.2f}, x2 = {x2:.2f}.") 11 | elif delta < 0: 12 | print("Phương trình vô nghiệm.") 13 | else: # delta == 0 14 | x = -b/(2*a) 15 | print("Phương trình có nghiệm kép:") 16 | print(f"x1 = x2 = {x:.2f}.") 17 | -------------------------------------------------------------------------------- /lesson06/turtle_escape.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | import random 3 | 4 | t.shape("turtle") 5 | d = 20 6 | while (abs(t.xcor()) < t.window_width()/2 7 | and abs(t.ycor()) < t.window_height()/2): 8 | direction = random.choice("LRUD") 9 | if direction == "L": 10 | t.setheading(180) 11 | elif direction == "R": 12 | t.setheading(0) 13 | elif direction == "U": 14 | t.setheading(90) 15 | else: # direction == "D" 16 | t.setheading(270) 17 | t.forward(d) 18 | print("Congratulations!") 19 | -------------------------------------------------------------------------------- /lesson07/Collatz_conj.py: -------------------------------------------------------------------------------- 1 | while text := input("Nhập số nguyên dương n: "): 2 | n = int(text) 3 | num = 0 4 | print(n, end=" ") 5 | while n != 1: 6 | if n % 2 == 0: # n chẵn 7 | n = n // 2 8 | else: # n lẻ 9 | n = 3*n + 1 10 | num += 1 11 | print(n, end=" ") 12 | 13 | print("\nSố lần lặp là", num) 14 | -------------------------------------------------------------------------------- /lesson07/Fibonacci_spiral.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | 3 | x, y = 0, 1 4 | while x <= 377: 5 | t.circle(x, 90) 6 | x, y = y, x + y 7 | 8 | t.hideturtle() 9 | -------------------------------------------------------------------------------- /lesson07/chicken_dog.py: -------------------------------------------------------------------------------- 1 | for x in range(1, 37): 2 | for y in range(1, 37): 3 | if x + y == 36 and 2*x + 4*y == 100: 4 | print(f"Số gà là: {x}, số chó là: {y}.") 5 | -------------------------------------------------------------------------------- /lesson07/chicken_dog2.py: -------------------------------------------------------------------------------- 1 | for x in range(1, 37): 2 | y = 36 - x 3 | if 2*x + 4*y == 100: 4 | print(f"Số gà là: {x}, số chó là: {y}.") 5 | -------------------------------------------------------------------------------- /lesson07/clock.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | import time 3 | 4 | t.hideturtle(); t.tracer(False) 5 | while True: 6 | now = time.localtime() 7 | time_str = "%02d:%02d:%02d" % (now.tm_hour, now.tm_min, now.tm_sec) 8 | t.clear() 9 | t.write(time_str, align="center", font=("Arial", 100, "normal")) 10 | t.update() 11 | time.sleep(0.1) 12 | -------------------------------------------------------------------------------- /lesson07/counter.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | import time 3 | 4 | t.hideturtle(); t.tracer(False) 5 | for i in range(100): 6 | t.clear() 7 | t.write(i, align="center", font=("Arial", 150, "normal")) 8 | t.update() 9 | time.sleep(1) 10 | -------------------------------------------------------------------------------- /lesson07/factorial.py: -------------------------------------------------------------------------------- 1 | n = int(input("Nhập số nguyên dương: ")) 2 | f = 1 3 | for i in range(1, n + 1): 4 | f *= i 5 | print(f"{n}! = {f}") 6 | -------------------------------------------------------------------------------- /lesson07/factorial2.py: -------------------------------------------------------------------------------- 1 | n = int(input("Nhập số nguyên dương: ")) 2 | f, i = 1, 1 3 | while i <= n: 4 | f *= i 5 | i += 1 6 | print(f"{n}! = {f}") 7 | -------------------------------------------------------------------------------- /lesson07/flower_script.py: -------------------------------------------------------------------------------- 1 | print("\u2740\u2740\u2740\u2740\u2740") 2 | print("\u2740\u2740\u2740\u2740\u2740" 3 | "\u2740\u2740\u2740\u2740\u2740") 4 | for i in range(100): 5 | print("\u2740", end="") 6 | -------------------------------------------------------------------------------- /lesson07/golden_spiral.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | t.speed("slowest") 3 | 4 | phi = (1 + 5**0.5)/2 5 | r = 377 6 | t.left(90) 7 | for i in range(12): 8 | t.circle(-r / phi**i, 90) 9 | 10 | t.hideturtle() 11 | -------------------------------------------------------------------------------- /lesson07/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson07/perfect_square.py: -------------------------------------------------------------------------------- 1 | a, b = 10, 100 2 | for i in range(a, b + 1): # tìm số chính phương trong [a, b] 3 | if i % 2 == 0: 4 | pass # TODO: thay bằng continue để bỏ qua số chẵn 5 | 6 | if int(i**0.5)**2 == i: # i là số chính phương 7 | print(i) 8 | pass # TODO: thay bằng break nếu chỉ muốn tìm một số 9 | 10 | print("Done!") 11 | -------------------------------------------------------------------------------- /lesson07/perfect_square2.py: -------------------------------------------------------------------------------- 1 | a, b = 10, 100 2 | for i in range(a, b + 1): 3 | if i % 2 == 0: 4 | continue 5 | 6 | if int(i**0.5)**2 == i: # i là số chính phương 7 | print(i) 8 | break 9 | 10 | else: 11 | print("Không tìm thấy số nào!") 12 | 13 | print("Done!") 14 | -------------------------------------------------------------------------------- /lesson07/square.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | 3 | for _ in range(4): 4 | t.forward(200) 5 | t.left(90) 6 | -------------------------------------------------------------------------------- /lesson07/square2.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | t.speed("fastest") 3 | 4 | for _ in range(91): 5 | t.forward(200) 6 | t.left(91) 7 | -------------------------------------------------------------------------------- /lesson07/square3.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | t.speed("fastest") 3 | 4 | for i in range(200): 5 | t.forward(i) 6 | t.left(90) 7 | -------------------------------------------------------------------------------- /lesson07/triangle.py: -------------------------------------------------------------------------------- 1 | char = input("Nhập kí hiệu vẽ: ") 2 | n = int(input("Nhập chiều cao: ")) 3 | for i in range(1, n + 1): 4 | for _ in range(i): 5 | print("*", end="") 6 | print() # xuống dòng 7 | -------------------------------------------------------------------------------- /lesson07/triangle2.py: -------------------------------------------------------------------------------- 1 | char = input("Nhập kí hiệu vẽ: ") 2 | n = int(input("Nhập chiều cao: ")) 3 | for i in range(1, n + 1): 4 | print(char * i) 5 | -------------------------------------------------------------------------------- /lesson07/turtle_draw.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | 3 | t.shape("turtle") 4 | t.speed("slowest") 5 | d = 20 6 | while ins := input("Nhập chuỗi lệnh (L, R, U, D): "): 7 | for i in range(len(ins)): 8 | if ins[i] == "L": 9 | t.setheading(180) 10 | elif ins[i] == "R": 11 | t.setheading(0) 12 | elif ins[i] == "U": 13 | t.setheading(90) 14 | elif ins[i] == "D": 15 | t.setheading(270) 16 | else: 17 | continue 18 | t.forward(d) 19 | print("Done!") 20 | -------------------------------------------------------------------------------- /lesson08/Newton_sqrt.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def Newton_sqrt(x): 4 | y = x 5 | for i in range(100): 6 | y = y/2 + x/(2*y) 7 | return y 8 | 9 | print(f"Căn của 2 theo Newton: {Newton_sqrt(2):.9f}") 10 | print(f"Căn của 2 theo math’s sqrt: {math.sqrt(2):.9f}") 11 | 12 | x = float(input("Nhập số cần tính căn: ")) 13 | print(f"Căn của {x} theo Newton: {Newton_sqrt(x):.9f}") 14 | print(f"Căn của {x} theo math’s sqrt: {math.sqrt(x):.9f}") 15 | -------------------------------------------------------------------------------- /lesson08/Newton_sqrt2.py: -------------------------------------------------------------------------------- 1 | def Newton_sqrt(x): 2 | if x < 0: 3 | return 4 | if x == 0: 5 | return 0 6 | 7 | y = x 8 | for i in range(100): 9 | y = y/2 + x/(2*y) 10 | return y 11 | -------------------------------------------------------------------------------- /lesson08/chess_board.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | import figures 3 | 4 | t.hideturtle(); t.tracer(False) 5 | width = 30 6 | for i in range(8): 7 | for j in range(8): 8 | color = ("blue" if (i + j) % 2 == 0 else "green") 9 | figures.square(i*width, j*width, width, color, color) 10 | t.update() 11 | -------------------------------------------------------------------------------- /lesson08/docstring.py: -------------------------------------------------------------------------------- 1 | def Newton_sqrt(x): 2 | "Tính căn theo phương pháp Newton." 3 | y = x 4 | for i in range(100): 5 | y = y/2 + x/(2*y) 6 | return y 7 | 8 | print(f"Căn của 2 theo Newton: {Newton_sqrt(2):.9f}") 9 | print(Newton_sqrt.__doc__) 10 | help(Newton_sqrt) 11 | -------------------------------------------------------------------------------- /lesson08/expression_stm.py: -------------------------------------------------------------------------------- 1 | # This is a comment ... 2 | # ... and comment 3 | 4 | """ This is a multiline string literal ... 5 | ... but is used as a comment ...""" 6 | 7 | 1, 2, ..., 100 8 | s = 'This is a "real" string literal' 9 | print((s + "\n") * 10) 10 | 2 ** 1000 == pow(2, 1000) 11 | pow(2, "1000") 12 | print(print(print)) 13 | -------------------------------------------------------------------------------- /lesson08/expression_value.py: -------------------------------------------------------------------------------- 1 | def N(x, y): 2 | return 8*(x**3) - 12*(x**2)*y + 6*x*(y**2) - y**3 3 | 4 | print(N(6, -8)) 5 | print(N(-8, 6)) 6 | print(N(x=6, y=-8)) 7 | print(N(y=-8, x=6)) 8 | print(N(x=-8, y=6)) 9 | print(N(6, y=-8)) 10 | # print(N(y=-8, 6)) # SyntaxError 11 | -------------------------------------------------------------------------------- /lesson08/figures.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | 3 | def square(x, y, width, line_color="black", fill_color=None): 4 | t.up(); t.goto(x, y); t.down() 5 | t.setheading(0); t.pencolor(line_color) 6 | if fill_color: 7 | t.fillcolor(fill_color) 8 | t.begin_fill() 9 | for _ in range(4): 10 | t.forward(width) 11 | t.left(90) 12 | if fill_color: 13 | t.end_fill() 14 | -------------------------------------------------------------------------------- /lesson08/hello.py: -------------------------------------------------------------------------------- 1 | def sayhello(name): 2 | hello_string = " Chào " + name + " " 3 | print("=" * (len(hello_string) + 2)) 4 | print("|" + hello_string + "|") 5 | print("=" * (len(hello_string) + 2)) 6 | 7 | sayhello("Python") 8 | sayhello("Vũ Quốc Hoàng") 9 | sayhello("Guido van Rossum") 10 | a_name = input("Tên của bạn là gì? ") 11 | sayhello(a_name) 12 | -------------------------------------------------------------------------------- /lesson08/hello2.py: -------------------------------------------------------------------------------- 1 | def sayhello(name="World", language="en"): 2 | hello_verb = ("Chào" if language == "vi" else "Hello") 3 | hello_string = f" {hello_verb} {name} " 4 | print("=" * (len(hello_string) + 2)) 5 | print("|" + hello_string + "|") 6 | print("=" * (len(hello_string) + 2)) 7 | 8 | sayhello() 9 | sayhello("Python") 10 | sayhello(language="en") 11 | sayhello("Vũ Quốc Hoàng", language="vi") 12 | sayhello(language="en", name="Guido van Rossum") 13 | a_name = input("Tên của bạn là gì? ") 14 | sayhello(a_name, "vi") 15 | -------------------------------------------------------------------------------- /lesson08/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson08/random_square.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | import random 3 | import figures 4 | 5 | t.hideturtle(); t.speed("fastest"); t.colormode(1.0) 6 | for _ in range(100): 7 | w_width = t.window_width() 8 | w_height = t.window_height() 9 | x = random.randint(-w_width//2, w_width//2) 10 | y = random.randint(-w_height//2, w_height//2) 11 | width = min(random.randint(0, w_width//2 - x), 12 | random.randint(0, w_height//2 - y)) 13 | figures.square(x, y, width, 14 | (random.random(), random.random(), random.random())) 15 | -------------------------------------------------------------------------------- /lesson08/sqrt.py: -------------------------------------------------------------------------------- 1 | """Module hỗ trợ việc tính căn bằng phương pháp Newton. 2 | 3 | Tác giả: Vũ Quốc Hoàng. 4 | Ngày viết: 10-03-2020. 5 | """ 6 | 7 | def Newton_sqrt(x): 8 | """Tính căn theo phương pháp Newton. 9 | 10 | Số cần tính căn, x, phải là số dương. 11 | """ 12 | y = x 13 | for i in range(100): 14 | y = y/2 + x/(2*y) 15 | return y 16 | -------------------------------------------------------------------------------- /lesson08/sqrt_cal.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def builtin_pow_sqrt(x): 4 | return pow(x, 0.5) 5 | 6 | def math_pow_sqrt(x): 7 | return math.pow(x, 0.5) 8 | 9 | def exp_operator_sqrt(x): 10 | return x ** 0.5 11 | 12 | def Newton_sqrt(x): 13 | y = x 14 | for i in range(100): 15 | y = y/2 + x/(2*y) 16 | return y 17 | 18 | def cal_sqrt(method, method_name): 19 | print(f"Tính căn bằng phương pháp {method_name}:") 20 | print(f"a) Căn của 0.0196 là {method(0.0196):.9f}") 21 | print(f"b) Căn của 1.21 là {method(1.21):.9f}") 22 | print(f"c) Căn của 2 là {method(2):.9f}") 23 | print(f"d) Căn của 3 là {method(3):.9f}") 24 | print(f"e) Căn của 4 là {method(4):.9f}") 25 | print(f"f) Căn của {225/256} là {method(225/256):.9f}") 26 | 27 | cal_sqrt(math.sqrt, "math’s sqrt") 28 | cal_sqrt(builtin_pow_sqrt, "built-in pow") 29 | cal_sqrt(math_pow_sqrt, "math’s pow") 30 | cal_sqrt(exp_operator_sqrt, "exponentiation operator") 31 | cal_sqrt(Newton_sqrt, "Newton’s sqrt") 32 | -------------------------------------------------------------------------------- /lesson08/sqrt_cal2.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def Newton_sqrt(x): 4 | y = x 5 | for i in range(100): 6 | y = y/2 + x/(2*y) 7 | return y 8 | 9 | def cal_sqrt(method, method_name): 10 | print(f"Tính căn bằng phương pháp {method_name}:") 11 | print(f"a) Căn của 0.0196 là {method(0.0196):.9f}") 12 | print(f"b) Căn của 1.21 là {method(1.21):.9f}") 13 | print(f"c) Căn của 2 là {method(2):.9f}") 14 | print(f"d) Căn của 3 là {method(3):.9f}") 15 | print(f"e) Căn của 4 là {method(4):.9f}") 16 | print(f"f) Căn của {225/256} là {method(225/256):.9f}") 17 | 18 | cal_sqrt(math.sqrt, "math’s sqrt") 19 | cal_sqrt(lambda x: pow(x, 0.5), "built-in pow") 20 | cal_sqrt(lambda x: math.pow(x, 0.5), "math’s pow") 21 | cal_sqrt(lambda x: x ** 0.5, "exponentiation operator") 22 | cal_sqrt(Newton_sqrt, "Newton’s sqrt") 23 | -------------------------------------------------------------------------------- /lesson08/sqrt_program.py: -------------------------------------------------------------------------------- 1 | import math 2 | import sqrt 3 | 4 | x = float(input("Nhập số cần tính căn: ")) 5 | print(f"Căn của {x} theo Newton: {sqrt.Newton_sqrt(x):.9f}") 6 | print(f"Căn của {x} theo math’s sqrt: {math.sqrt(x):.9f}") 7 | -------------------------------------------------------------------------------- /lesson08/turtle_draw.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | 3 | def forward(deg): 4 | t.setheading(deg) 5 | t.forward(d) 6 | 7 | d = 20 8 | t.shape("turtle") 9 | t.speed("slowest") 10 | t.onkey(lambda: forward(180), "Left") 11 | t.onkey(lambda: forward(0), "Right") 12 | t.onkey(lambda: forward(90), "Up") 13 | t.onkey(lambda: forward(270), "Down") 14 | t.onkey(t.bye, "Escape") 15 | t.listen() 16 | t.mainloop() 17 | -------------------------------------------------------------------------------- /lesson09/Maclaurin_cos.py: -------------------------------------------------------------------------------- 1 | from math import factorial, radians, cos 2 | 3 | def mycos1(x, n=20): 4 | s = 0 5 | for i in range(0, n + 1): 6 | s += (-1)**i * x**(2*i)/factorial(2*i) 7 | return s 8 | 9 | def mycos2(x, n=20): 10 | s = 0 11 | for i in range(0, n + 1): 12 | mu_2i, gt_2i = 1.0, 1.0 # tính x^(2i) và (2i)! 13 | for j in range(1, 2*i + 1): 14 | mu_2i *= x 15 | gt_2i *= j 16 | s += (1 if i%2 == 0 else -1) * mu_2i/gt_2i 17 | return s 18 | 19 | def mycos3(x, n=20): 20 | s = term = 1.0 # tính (-1)^i * x^(2i)/(2i)! 21 | for i in range(1, n + 1): 22 | term *= -1 * x*x / ((2*i-1)*(2*i)) 23 | s += term 24 | return s 25 | -------------------------------------------------------------------------------- /lesson09/Pisa_tower.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | import time 3 | import figures as fg 4 | 5 | def log(): 6 | print(f"Hit(s): {hit}, Time: {time.time() - st:.2f}s, " 7 | f"Speed: {abs(v):.2f}m/s, Height: {h:.2f}m.") 8 | 9 | def render(): 10 | t.clear() 11 | fg.line(0, 0, 3, 0); fg.line(1, 0, 1, HEIGHT) 12 | fg.string(1, h, f"{h:.1f}", align="right", font_size=12) 13 | fg.line(1, h, 2, h, "blue"); fg.dot(2, h, 10, "red") 14 | fg.string(2, h, str(hit), color="green", align="center", font_size=20) 15 | t.update() 16 | 17 | def run(): 18 | global v, h, hit, t0, v0, h0 19 | 20 | tm = time.time() - t0 21 | v, h = v0 - G*tm, h0 + v0*tm - (G/2)*(tm**2) 22 | if h < 0: 23 | h = 0; hit += 1; t0 = time.time() 24 | v0, h0 = K * (VMAX if hit == 1 else v0), 0 25 | if v0 == 0: 26 | v, h = 0, 0 27 | render() 28 | return 29 | 30 | render() 31 | t.ontimer(run, 1000//24) 32 | 33 | HEIGHT = 55.86; G = 9.8; VMAX = (2*G*HEIGHT)**0.5; K = 0.9 34 | st = time.time(); hit = 0 35 | t0, v0, h0 = st, 0, HEIGHT 36 | 37 | fg.coordinate_system(0, -HEIGHT/10, 3, HEIGHT*11/10, None) 38 | t.hideturtle(); t.tracer(False) 39 | t.onkey(log, "space"); t.listen() 40 | run(); t.exitonclick() 41 | -------------------------------------------------------------------------------- /lesson09/bisection_sqrt.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def bisec_sqrt(x): # x > 0 4 | l, r = 0, max(x, 1) 5 | y = (l + r)/2 6 | while not math.isclose(y*y, x): 7 | if y*y > x: 8 | r = y 9 | else: 10 | l = y 11 | y = (l + r)/2 12 | return y 13 | 14 | def Newton_sqrt(x): # x > 0 15 | y = x/2 16 | while not math.isclose(y*y, x): 17 | y = y/2 + x/(2*y) 18 | return y 19 | -------------------------------------------------------------------------------- /lesson09/bloom_square.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | import time 3 | import figures 4 | 5 | def toggle_bloom(): 6 | global bloom 7 | bloom = not bloom 8 | 9 | def run(): 10 | global bloom, width 11 | 12 | max_width = min(t.window_width(), t.window_height()) 13 | if width <= 0 and not bloom: bloom = True 14 | if width >= max_width and bloom: bloom = False 15 | width += (1 if bloom else -1) 16 | 17 | t.clear() 18 | figures.square(-width//2, -width//2, width, "red", "red") 19 | t.update() 20 | t.ontimer(run, 1000//24) # 24 fps 21 | 22 | width = 0; bloom = True 23 | t.hideturtle(); t.tracer(False) 24 | t.onkey(toggle_bloom, "space"); t.listen() 25 | run(); t.exitonclick() 26 | -------------------------------------------------------------------------------- /lesson09/figures.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | from math import ceil, floor, cos, sin, radians 3 | 4 | def square(x, y, width, line_color="black", fill_color=None): 5 | t.up(); t.goto(x, y); t.down() 6 | t.setheading(0); t.pencolor(line_color) 7 | if fill_color: 8 | t.fillcolor(fill_color) 9 | t.begin_fill() 10 | for _ in range(4): 11 | t.forward(width) 12 | t.left(90) 13 | if fill_color: 14 | t.end_fill() 15 | 16 | def para_curve(f, g, tS, tE, n, line_color="black", fill_color=None): 17 | t.pencolor(line_color) 18 | if fill_color: 19 | t.fillcolor(fill_color) 20 | t.begin_fill() 21 | 22 | d = (tE - tS)/n 23 | for i in range(n + 1): 24 | ti = tS + i*d 25 | x, y = f(ti), g(ti) 26 | if i == 0: 27 | t.up() 28 | else: 29 | t.down() 30 | t.goto(x, y) 31 | 32 | if fill_color: 33 | t.end_fill() 34 | 35 | def coordinate_system(minx, miny, maxx, maxy, coord_color="gray"): 36 | t.setworldcoordinates(minx - 0.4, miny - 0.4, maxx + 0.4, maxy + 0.4) 37 | t.hideturtle() 38 | 39 | if coord_color: 40 | t.pencolor(coord_color) 41 | 42 | t.up(); t.goto(minx - 0.2, 0); t.down(); t.goto(maxx + 0.2, 0) 43 | t.up(); t.goto(0, miny - 0.2); t.down(); t.goto(0, maxy + 0.2) 44 | 45 | t.up() 46 | for i in range(ceil(minx), floor(maxx) + 1): 47 | t.goto(i, 0); t.dot(3) 48 | if i != 0: 49 | t.write(i, align="right", font=("Arial", 10, "normal")) 50 | for i in range(ceil(miny), floor(maxy) + 1): 51 | t.goto(0, i); t.dot(3) 52 | t.write(i, align="right",font=("Arial", 10, "normal")) 53 | 54 | def line(x1, y1, x2, y2, line_color="black"): 55 | t.pencolor(line_color) 56 | t.up(); t.goto(x1, y1) 57 | t.down(); t.goto(x2, y2) 58 | 59 | def string(x, y, text, color="black", align="left", 60 | font_name="Arial", font_size=8, font_style="normal"): 61 | t.up(); t.goto(x, y) 62 | t.pencolor(color) 63 | t.write(text, align=align, font=(font_name, font_size, font_style)) 64 | 65 | def dot(x, y, size=2, color="black"): 66 | t.up(); t.goto(x, y) 67 | t.dot(size, color) 68 | 69 | def polar_para_curve(f, g, tS, tE, n, line_color="black", fill_color=None): 70 | t.pencolor(line_color) 71 | if fill_color: 72 | t.fillcolor(fill_color) 73 | t.begin_fill() 74 | 75 | d = (tE - tS)/n 76 | for i in range(n + 1): 77 | ti = tS + i*d 78 | r, phi = f(ti), g(ti) 79 | x, y = r*cos(radians(phi)), r*sin(radians(phi)) 80 | if i == 0: 81 | t.up() 82 | else: 83 | t.down() 84 | t.goto(x, y) 85 | 86 | if fill_color: 87 | t.end_fill() 88 | -------------------------------------------------------------------------------- /lesson09/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson09/number_digits.py: -------------------------------------------------------------------------------- 1 | n = int(input("Nhập số có không quá 3 chữ số: ")) 2 | đơn_vị, chục, ngàn = n % 10, (n % 100) // 10, n // 100 3 | tổng = đơn_vị + chục + ngàn 4 | print("Tổng các chữ số là:", tổng) 5 | -------------------------------------------------------------------------------- /lesson09/number_digits2.py: -------------------------------------------------------------------------------- 1 | n = int(input("Nhập số có không quá 3 chữ số: ")) 2 | tổng = 0 3 | 4 | tổng += n % 10; n //= 10 5 | tổng += n % 10; n //= 10 6 | tổng += n % 10; n //= 10 7 | 8 | print("Tổng các chữ số là:", tổng) 9 | -------------------------------------------------------------------------------- /lesson09/number_digits3.py: -------------------------------------------------------------------------------- 1 | n = int(input("Nhập số có không quá 3 chữ số: ")) 2 | tổng = 0 3 | 4 | for _ in range(3): 5 | tổng += n % 10 6 | n //= 10 7 | 8 | print("Tổng các chữ số là:", tổng) 9 | -------------------------------------------------------------------------------- /lesson09/number_digits4.py: -------------------------------------------------------------------------------- 1 | n = int(input("Nhập số nguyên không âm: ")) 2 | tổng = 0 3 | 4 | while n > 0: 5 | tổng += n % 10 6 | n //= 10 7 | 8 | print("Tổng các chữ số là:", tổng) 9 | -------------------------------------------------------------------------------- /lesson09/para_curves.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | from math import cos, sin, radians as rad 3 | from figures import para_curve as pcur, coordinate_system 4 | 5 | t.setup(600, 600) 6 | t.tracer(False) 7 | coordinate_system(-4, -2, 4, 6) 8 | 9 | pcur(lambda t: t, lambda t: t**2, -4, 4, 30, 10 | line_color="red") 11 | pcur(lambda t: t, lambda t: t + 2, -4, 4, 1, 12 | line_color="green") 13 | pcur(lambda t: 2*cos(rad(t)), lambda t: 2*sin(rad(t)), 14 | 0, 360, 50, line_color="blue") 15 | pcur(lambda t: 4*cos(rad(t)), lambda t: 2*sin(rad(t)), 16 | 0, 360, 50, line_color="orange") 17 | 18 | t.update() 19 | -------------------------------------------------------------------------------- /lesson09/para_curves2.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | import math 3 | import time 4 | import figures 5 | 6 | t.tracer(False) 7 | t.hideturtle() 8 | 9 | t.title("Parabol y = kx^2") 10 | figures.coordinate_system(-4, -10, 4, 10) 11 | for k in range(-25, 26): 12 | figures.para_curve(lambda t: t, lambda t: k/20 * t**2, -4, 4, 50, line_color="red") 13 | t.update() 14 | time.sleep(5) 15 | 16 | t.clear() 17 | t.title("Line y = kx") 18 | figures.coordinate_system(-4, -10, 4, 10) 19 | for k in range(-25, 26): 20 | figures.para_curve(lambda t: t, lambda t: k/5*t, -4, 4, 50, line_color="red") 21 | t.update() 22 | time.sleep(5) 23 | 24 | t.clear() 25 | t.title("Ellipse and Circle") 26 | figures.coordinate_system(-25, -25, 25, 25, coord_color=None) 27 | for a in range(26): 28 | figures.para_curve(lambda t: a*math.cos(math.radians(t)), 29 | lambda t: 25*math.sin(math.radians(t)), 0, 360, 50, line_color="red") 30 | for b in range(26): 31 | figures.para_curve(lambda t: 25*math.cos(math.radians(t)), 32 | lambda t: b*math.sin(math.radians(t)), 0, 360, 50, line_color="red") 33 | t.update() 34 | 35 | -------------------------------------------------------------------------------- /lesson09/pp_curves.py: -------------------------------------------------------------------------------- 1 | from figures import polar_para_curve as ppcur 2 | 3 | ppcur(lambda t: 250, lambda t: t, 0, 360, 4 | 50, line_color="red") 5 | ppcur(lambda t: 0.15*t, lambda t: t, 0, 4*360, 6 | 4*50, line_color="blue") 7 | -------------------------------------------------------------------------------- /lesson09/pp_curves_exercise.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | import math 3 | import time 4 | import figures 5 | 6 | t.hideturtle() 7 | 8 | t.clear() 9 | t.title("Hyperbolic spiral") 10 | figures.polar_para_curve(lambda t: 300*360/t, 11 | lambda t: t, 360, 5*360, 4*50, line_color="red") 12 | t.update() 13 | time.sleep(2) 14 | 15 | t.clear() 16 | t.title("Golden spiral") 17 | phi = (1 + math.sqrt(5))/2 18 | figures.polar_para_curve(lambda t: phi**(t/90), 19 | lambda t: t, 0, 3*360, 3*50, line_color="red") 20 | t.update() 21 | time.sleep(2) 22 | 23 | t.clear() 24 | t.title("Rose 1") 25 | figures.polar_para_curve(lambda t: 200*math.cos((math.pi/180) * (5/2*t)), 26 | lambda t: t, 0, 2*360, 200, line_color="red") 27 | t.update() 28 | time.sleep(2) 29 | 30 | t.clear() 31 | t.title("Rose 2") 32 | figures.polar_para_curve(lambda t: 200*math.cos((math.pi/180) * (5/6*t)), 33 | lambda t: t, 0, 6*360, 6*50, line_color="red") 34 | t.update() 35 | time.sleep(2) 36 | 37 | t.clear() 38 | t.title("Rose 3") 39 | figures.polar_para_curve(lambda t: 40 + 200*math.cos((math.pi/180) * (12*t)), 40 | lambda t: t, 0, 360, 500, line_color="pink", fill_color="pink") 41 | -------------------------------------------------------------------------------- /lesson09/prime.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def prime1(n): # n là số nguyên > 1 4 | ndivs = 0 # số lượng ước số dương của n 5 | for i in range(1, n + 1): 6 | if n % i == 0: # i là ước của n 7 | ndivs += 1 8 | return (ndivs == 2) # n có đúng 2 ước số 9 | 10 | def prime2(n): # n là số nguyên > 1 11 | for i in range(2, n): 12 | if n % i == 0: 13 | return False # n có ước > 1 và < n 14 | return True 15 | 16 | def prime3(n): # n là số nguyên > 1 17 | for i in range(2, math.isqrt(n) + 1): 18 | if n % i == 0: 19 | return False # n có ước > 1 và <= căn n 20 | return True 21 | -------------------------------------------------------------------------------- /lesson10/amodule.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def sqrt(): 4 | return 2 * a 5 | 6 | a = b = 10 7 | -------------------------------------------------------------------------------- /lesson10/func_cls.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | import string 3 | 4 | def write(char): 5 | def _write(): 6 | t.clear() 7 | t.write(char, align="center", font=f"Arial {s}") 8 | t.update() 9 | return _write 10 | 11 | s = 300 12 | t.hideturtle(); t.tracer(False); t.color("red") 13 | t.up(); t.right(90); t.forward(2*s//3); t.down() 14 | 15 | chars = string.ascii_letters + string.digits 16 | for i in range(len(chars)): 17 | t.onkey(write(chars[i]), chars[i]) 18 | 19 | t.listen() 20 | t.exitonclick() 21 | -------------------------------------------------------------------------------- /lesson10/hello.py: -------------------------------------------------------------------------------- 1 | # The first Python program in VS Code 2 | 3 | name = input("What's your name? ") 4 | print("Hello", name, "from VS Code") -------------------------------------------------------------------------------- /lesson10/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson10/pizza.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson10/pizza.gif -------------------------------------------------------------------------------- /lesson10/scope.py: -------------------------------------------------------------------------------- 1 | def func1(d): 2 | # global a 3 | a = d 4 | print(a) 5 | 6 | def func2(d): 7 | def func3(): 8 | # nonlocal a 9 | a = 2 * d 10 | print(a) 11 | 12 | a = d 13 | func3() 14 | print(a) 15 | 16 | a = 0 17 | func1(1) 18 | print(a) 19 | func2(2) 20 | print(a) 21 | # func3() 22 | -------------------------------------------------------------------------------- /lesson10/turtle_draw.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | 3 | def rotate(deg): 4 | t.setheading(deg) 5 | 6 | def forward(level): 7 | def _forward(): 8 | t.forward(level * d) 9 | return _forward 10 | 11 | d = 5 12 | t.shape("turtle") 13 | t.onkey(lambda: rotate(180), "Left") 14 | t.onkey(lambda: rotate(0), "Right") 15 | t.onkey(lambda: rotate(90), "Up") 16 | t.onkey(lambda: rotate(270), "Down") 17 | t.onkey(t.bye, "Escape") 18 | 19 | keys = "123456789" 20 | for i in range(len(keys)): 21 | t.onkey(forward(int(keys[i])), keys[i]) 22 | 23 | t.listen() 24 | t.mainloop() 25 | -------------------------------------------------------------------------------- /lesson10/turtle_race.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | from turtle import window_width as ww, window_height as wh 3 | from random import randint as rint 4 | 5 | def random_point(r): 6 | x = rint(-ww()//2 + r, ww()//2 - r) 7 | y = rint(-wh()//2 + r, wh()/2 - r) 8 | return (x, y) 9 | 10 | def check_winner(): 11 | for tur in FOUR_TURTLES: 12 | if Pizza.distance(tur) < Pizza.radius/2: 13 | print(tur.name + " win the race!") 14 | tur.turtlesize(2.5, 2.5) 15 | return True 16 | return False 17 | 18 | def forward_and_reflex(): 19 | for tur in FOUR_TURTLES: 20 | if abs(tur.xcor()) >= ww()/2 - tur.radius: 21 | tur.setheading((180 - tur.heading()) % 360) 22 | if abs(tur.ycor()) >= wh()/2 - tur.radius: 23 | tur.setheading((360 - tur.heading()) % 360) 24 | tur.forward(2) 25 | 26 | def check_collision(): 27 | for tur in FOUR_TURTLES: 28 | for other_tur in FOUR_TURTLES: 29 | if (tur is not other_tur and 30 | tur.distance(other_tur) < 2*tur.radius): 31 | tur.setheading(rint(0, 360)) 32 | 33 | def run(): 34 | if check_winner(): 35 | t.update() 36 | return 37 | forward_and_reflex(); check_collision() 38 | t.update(); t.ontimer(run, 10) 39 | 40 | t.title("Teenage Mutant Ninja Turtles Race for Pizza!") 41 | t.addshape("pizza.gif"); t.tracer(False) 42 | 43 | Pizza = t.Turtle() 44 | Pizza.shape("pizza.gif"); Pizza.radius = 20 45 | Pizza.up(); Pizza.goto(random_point(Pizza.radius)) 46 | Pizza.ondrag(Pizza.goto) 47 | 48 | Leo, Mike, Don, Raph = t.Turtle(), t.Turtle(), t.Turtle(), t.Turtle() 49 | for tur, name, color in [(Leo, "Leonardo", "blue"), 50 | (Mike, "Michelangelo", "orange"), 51 | (Don, "Donatello", "purple"), 52 | (Raph, "Raphael", "red")]: 53 | tur.shape("turtle"); tur.radius = 10 54 | tur.name = name; tur.color(color, color) 55 | tur.setheading(rint(0, 360)) 56 | tur.up(); tur.goto(random_point(tur.radius)); tur.down() 57 | FOUR_TURTLES = [Leo, Mike, Don, Raph] 58 | t.onkeypress(t.bye); t.listen() 59 | run(); t.mainloop() 60 | -------------------------------------------------------------------------------- /lesson11/Pascal_triangle.py: -------------------------------------------------------------------------------- 1 | n = int(input("Nhập số dòng n: ")) 2 | a = [] # Pascal triangle 3 | 4 | for i in range(n): 5 | a.append([1] * (i + 1)) 6 | 7 | for i in range(1, n): 8 | for j in range(1, i): 9 | a[i][j] = a[i-1][j-1] + a[i-1][j] 10 | 11 | for i in range(n): 12 | for j in range(i + 1): 13 | print("%5d" % a[i][j], end="") 14 | print() 15 | -------------------------------------------------------------------------------- /lesson11/ascending.py: -------------------------------------------------------------------------------- 1 | nums = [] 2 | while text := input("Nhập số nguyên (Enter để dừng): "): 3 | nums.append(int(text)) 4 | 5 | if len(nums) > 0: 6 | print("Các số đã nhập theo thứ tự tăng dần là:") 7 | for num in sorted(nums): 8 | print(num, end=" ") 9 | -------------------------------------------------------------------------------- /lesson11/cmd_args.py: -------------------------------------------------------------------------------- 1 | import sys 2 | print(len(sys.argv), sys.argv) 3 | -------------------------------------------------------------------------------- /lesson11/hello.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | if len(sys.argv) < 2: 4 | name = input("What's your name? ") 5 | else: 6 | name = " ".join(sys.argv[1:]) 7 | print("Hello", name) 8 | -------------------------------------------------------------------------------- /lesson11/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson11/k_max.py: -------------------------------------------------------------------------------- 1 | nums = [] 2 | while text := input("Nhập số nguyên (Enter để dừng): "): 3 | nums.append(int(text)) 4 | 5 | nums.sort(reverse=True) 6 | 7 | k = int(input("Bạn muốn tìm số lớn thứ mấy? ")) 8 | if 0 < k <= len(nums): 9 | print(f"Số lớn thứ {k} đã nhập là {nums[k - 1]}") 10 | -------------------------------------------------------------------------------- /lesson11/list_remove_dup.py: -------------------------------------------------------------------------------- 1 | def remove_duplicates(li): 2 | i = 0 3 | while i < len(li): 4 | j = i + 1 5 | while j < len(li): 6 | if li[j] == li[i]: 7 | del li[j] 8 | else: 9 | j += 1 10 | i += 1 11 | -------------------------------------------------------------------------------- /lesson11/max.py: -------------------------------------------------------------------------------- 1 | max = None 2 | while text := input("Nhập số nguyên (Enter để dừng): "): 3 | num = int(text) 4 | if not max or max < num: 5 | max = num 6 | 7 | if max: 8 | print("Số lớn nhất đã nhập là", max) 9 | -------------------------------------------------------------------------------- /lesson11/polynomial.py: -------------------------------------------------------------------------------- 1 | def eval_polynomial(P, x): 2 | s = 0 3 | for k, a in enumerate(P): 4 | s += a * x**k 5 | return s 6 | 7 | # Tính giá trị của đa thức x + 2x^3 - 3x^5 tại x = 2 8 | P = [0, 1, 0, 2, 0, -3] 9 | print(eval_polynomial(P, 2)) 10 | -------------------------------------------------------------------------------- /lesson11/polynomial2.py: -------------------------------------------------------------------------------- 1 | def eval_polynomial(P, x): 2 | return sum([a * x**k for k, a in enumerate(P)]) 3 | 4 | # Tính giá trị của đa thức x + 2x^3 - 3x^5 tại x = 2 5 | print(eval_polynomial([0, 1, 0, 2, 0, -3], 2)) 6 | -------------------------------------------------------------------------------- /lesson11/score.py: -------------------------------------------------------------------------------- 1 | ds_mốc_điểm = [9, 8, 7, 6, 5, 4, 0] 2 | ds_đạt = [True, True, True, True, True, False, False] 3 | ds_học_lực = ["Xuất sắc", "Giỏi", "Khá", "Trung bình khá", 4 | "Trung bình", "Yếu", "Kém"] 5 | 6 | điểm = float(input("Nhập điểm: ")) 7 | for i in range(len(ds_mốc_điểm)): 8 | if điểm >= ds_mốc_điểm[i]: 9 | print("Bạn " + ("đạt" if ds_đạt[i] else "không đạt")) 10 | print("Học lực " + ds_học_lực[i]) 11 | break 12 | -------------------------------------------------------------------------------- /lesson11/second_max.py: -------------------------------------------------------------------------------- 1 | max1, max2 = None, None 2 | while text := input("Nhập số nguyên (Enter để dừng): "): 3 | num = int(text) 4 | if not max1 or max1 < num: 5 | max2, max1 = max1, num 6 | elif (num < max1) and (not max2 or max2 < num): 7 | max2 = num 8 | 9 | if max2: 10 | print("Số lớn thứ hai đã nhập là:", max2) 11 | else: 12 | print("Không có số lớn thứ hai") 13 | -------------------------------------------------------------------------------- /lesson11/sqrt_cal.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | # danh sách các số cần tính căn 4 | nums = [0.0196, 1.21, 2, 3, 4, 225/256] 5 | for i in nums: 6 | print(math.sqrt(i)) 7 | -------------------------------------------------------------------------------- /lesson11/sqrt_cal2.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | # danh sách các số cần tính căn 4 | nums = [0.0196, 1.21, 2, 3, 4, 225/256] 5 | for i in range(len(nums)): 6 | print(math.sqrt(nums[i])) 7 | -------------------------------------------------------------------------------- /lesson11/sqrt_cal3.py: -------------------------------------------------------------------------------- 1 | import math 2 | from string import ascii_lowercase as alphabet 3 | 4 | def cal_sqrt(methods, nums): 5 | for method, method_name in methods: 6 | print(f"Tính căn bằng phương pháp {method_name}:") 7 | for i in range(len(nums)): 8 | print("%s) Căn của %g là %.9f" % 9 | (alphabet[i], nums[i], method(nums[i]))) 10 | 11 | # danh sách các số cần tính căn 12 | nums = [0.0196, 1.21, 2, 3, 4, 225/256] 13 | 14 | # danh sách các phương pháp tính căn (và tên) 15 | methods = [ 16 | (math.sqrt, "math's sqrt"), 17 | (lambda x: math.pow(x, 0.5), "math's pow"), 18 | (lambda x: pow(x, 0.5), "built-in pow"), 19 | (lambda x: x ** 0.5, "exponentiation operator") 20 | ] 21 | 22 | cal_sqrt(methods, nums) 23 | -------------------------------------------------------------------------------- /lesson11/sqrt_script.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | print(math.sqrt(0.0196)) # Câu (a) 4 | print(math.sqrt(1.21)) # Câu (b) 5 | print(math.sqrt(2)) # Câu (c) 6 | print(math.sqrt(3)) # Câu (d) 7 | print(math.sqrt(4)) # Câu (e) 8 | print(math.sqrt(225/256)) # Câu (f) 9 | 10 | for i in range(1, 101): 11 | print(math.sqrt(i)) 12 | -------------------------------------------------------------------------------- /lesson11/turtle_draw.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | 3 | def rotate(deg): 4 | def _rotate(): 5 | t.setheading(deg) 6 | return _rotate 7 | 8 | def forward(level): 9 | def _forward(): 10 | t.forward(level * d) 11 | return _forward 12 | 13 | d = 5 14 | t.shape("turtle") 15 | 16 | for key, deg in zip(["Left", "Right", "Up", "Down"], 17 | [180, 0, 90, 270]): 18 | t.onkey(rotate(deg), key) 19 | 20 | for key in [str(i) for i in range(1, 10)]: 21 | t.onkey(forward(int(key)), key) 22 | 23 | t.onkey(t.bye, "Escape") 24 | t.listen() 25 | t.mainloop() 26 | -------------------------------------------------------------------------------- /lesson12/char_freq.py: -------------------------------------------------------------------------------- 1 | def char_freq(text): 2 | chars = set(text) 3 | print(f"Có {len(chars)} kí tự trong chuỗi {text}") 4 | for c in sorted(chars): 5 | print(f"{c} xuất hiện {text.count(c)} lần") 6 | if chars: 7 | most = max([(c, text.count(c)) for c in chars], 8 | key=lambda pair: pair[1]) 9 | print("Nhiều nhất là %s xuất hiện %d lần" % most) 10 | 11 | char_freq(input("Nhập chuỗi nào đó: ")) 12 | -------------------------------------------------------------------------------- /lesson12/char_freq2.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | 3 | def char_freq(text): 4 | chars = Counter(text) 5 | print(f"Có {len(chars)} kí tự trong chuỗi {text}") 6 | for c in sorted(chars): 7 | print(f"{c} xuất hiện {chars[c]} lần") 8 | if chars: 9 | most = chars.most_common(1)[0] 10 | print("Nhiều nhất là %s xuất hiện %d lần" % most) 11 | 12 | char_freq(input("Nhập chuỗi nào đó: ")) 13 | -------------------------------------------------------------------------------- /lesson12/chicken_dog.py: -------------------------------------------------------------------------------- 1 | for x, y in [(i, j) for i in range(37) for j in range(37)]: 2 | if x + y == 36 and 2*x + 4*y == 100: 3 | print(f"Số gà là: {x}, số chó là: {y}.") 4 | -------------------------------------------------------------------------------- /lesson12/hello.py: -------------------------------------------------------------------------------- 1 | def sayhello(*names): 2 | for name in names: 3 | hello_string = " Chào " + name + " " 4 | print("=" * (len(hello_string) + 2)) 5 | print("|" + hello_string + "|") 6 | print("=" * (len(hello_string) + 2)) 7 | 8 | sayhello("Python", "Vũ Quốc Hoàng", "Guido van Rossum") 9 | -------------------------------------------------------------------------------- /lesson12/hello2.py: -------------------------------------------------------------------------------- 1 | def sayhello(*names, language="en"): 2 | if not names: 3 | names = ("World", ) # Bộ có 1 phần tử 4 | for name in names: 5 | hello_verb = ("Chào" if language == "vi" else "Hello") 6 | hello_string = f" {hello_verb} {name} " 7 | print("=" * (len(hello_string) + 2)) 8 | print("|" + hello_string + "|") 9 | print("=" * (len(hello_string) + 2)) 10 | 11 | sayhello() 12 | sayhello("Python", "Hoang Vu Quoc", "Guido van Rossum") 13 | 14 | names = [] 15 | while name := input("Nhập tên: "): 16 | names.append(name) 17 | sayhello(*names, language="vi") 18 | -------------------------------------------------------------------------------- /lesson12/hello3.py: -------------------------------------------------------------------------------- 1 | def sayhello(*name_language_pairs): 2 | if not name_language_pairs: 3 | name_language_pairs = (("World", "en"), ) # Bộ có 1 phần tử 4 | for name, language in name_language_pairs: 5 | hello_verb = ("Chào" if language == "vi" else "Hello") 6 | hello_string = f" {hello_verb} {name} " 7 | print("=" * (len(hello_string) + 2)) 8 | print("|" + hello_string + "|") 9 | print("=" * (len(hello_string) + 2)) 10 | 11 | sayhello() 12 | sayhello(("Python", "en"), ("Vũ Quốc Hoàng", "vi"), ("Guido", "en")) 13 | 14 | names = [] 15 | while name := input("Nhập tên: "): 16 | if name.isascii(): 17 | names.append((name, "en")) 18 | else: 19 | names.append((name, "vi")) 20 | sayhello(*names) 21 | -------------------------------------------------------------------------------- /lesson12/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson12/turtle_draw.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | 3 | t.shape("turtle") 4 | d = 20 5 | actions = {"L": 180, "R": 0, "U": 90, "D": 270} 6 | while ins := input("Nhập chuỗi lệnh cho con rùa (L, R, U, D): "): 7 | for act in ins: 8 | if act in actions: 9 | t.setheading(actions[act]) 10 | else: 11 | continue 12 | t.forward(d) 13 | print("Done!") 14 | -------------------------------------------------------------------------------- /lesson12/turtle_draw2.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | 3 | def forward(deg): 4 | def _forward(): 5 | t.setheading(deg) 6 | t.forward(d) 7 | 8 | return _forward 9 | 10 | t.shape("turtle") 11 | d = 20 12 | actions = {"Left": 180, "Right": 0, "Up": 90, "Down": 270} 13 | for act in actions: 14 | t.onkey(forward(actions[act]), act) 15 | t.onkey(t.bye, "Escape") 16 | t.listen() 17 | t.mainloop() 18 | -------------------------------------------------------------------------------- /lesson12/turtle_escape.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | import random 3 | 4 | t.shape("turtle") 5 | d = 20 6 | actions = {"L": 180, "R": 0, "U": 90, "D": 270} 7 | while (abs(t.xcor()) < t.window_width()/2 and 8 | abs(t.ycor()) < t.window_height()/2): 9 | direction = random.choice("LRUD") 10 | t.setheading(actions[direction]) 11 | t.forward(d) 12 | print("Congratulations!") 13 | -------------------------------------------------------------------------------- /lesson13/Monty_Hall.py: -------------------------------------------------------------------------------- 1 | from random import choice 2 | import matplotlib.pyplot as plt 3 | from matplotlib.animation import FuncAnimation 4 | 5 | def Monty_Hall(doors={"#1", "#2", "#3"}): 6 | car_door = choice(list(doors)) 7 | choice_door = choice(list(doors)) 8 | open_door = choice(list(doors - {choice_door, car_door})) 9 | op_door = choice(list(doors - {choice_door, open_door})) 10 | 11 | return car_door == choice_door, car_door == op_door 12 | 13 | def update(_): 14 | global N, freqs 15 | 16 | N += K 17 | results = [Monty_Hall() for _ in range(K)] 18 | freqs[0] += sum([v for v, _ in results]) 19 | freqs[1] += sum([v for _, v in results]) 20 | fr = [f/N for f in freqs] 21 | 22 | ax.clear() 23 | ax.set_ylim(0, 1) 24 | ax.bar(["Giữ", "Đổi"], fr, color=["red", "blue"]) 25 | ax.text(0, fr[0], f"{fr[0]*100:.2f}%", 26 | horizontalalignment='center') 27 | ax.text(1, fr[1], f"{fr[1]*100:.2f}%", 28 | horizontalalignment='center') 29 | ax.set_title(f"Lần chơi thứ {N}") 30 | 31 | K = 1000 # số lần chơi mỗi lần update 32 | N = 0 33 | freqs = [0, 0] # Số lượng thắng khi Giữ, Đổi 34 | 35 | fig, ax = plt.subplots() 36 | ani = FuncAnimation(fig, update, interval=1000//24) 37 | plt.show() 38 | -------------------------------------------------------------------------------- /lesson13/bau_cua.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | def chung_tiền(ds_cược, mặt_ra): 4 | cược = sum(ds_cược.values()) 5 | for lv in set(LINH_VẬT) - set(mặt_ra): 6 | cược -= ds_cược.get(lv, 0) 7 | for lv in mặt_ra: 8 | cược += ds_cược.get(lv, 0) 9 | return cược 10 | 11 | def bầu_cua(): 12 | mặt_ra = random.choices(LINH_VẬT, k=3) 13 | t1 = chung_tiền({"TÔM": 3}, mặt_ra) 14 | t2 = chung_tiền({"TÔM": 2, "CUA": 1}, mặt_ra) 15 | t3 = chung_tiền({"TÔM": 1, "CUA": 1, "BẦU": 1}, mặt_ra) 16 | return t1, t2, t3 17 | 18 | def trung_bình(N): 19 | k_quả = [bầu_cua() for _ in range(N)] 20 | return [sum([k[c] for k in k_quả])/N for c in [0, 1, 2]] 21 | 22 | LINH_VẬT = ["TÔM", "CUA", "BẦU", "CÁ", "GÀ", "NAI"] 23 | -------------------------------------------------------------------------------- /lesson13/chart.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | 3 | labels = ["Giỏi", "Khá", "Trung bình", "Yếu"] 4 | freqs = [5, 30, 10, 5] 5 | clrs = ["seagreen", "darkorange", "darkviolet", "cyan"] 6 | 7 | fig, (ax1, ax2) = plt.subplots(1, 2) 8 | 9 | ax1.bar(labels, freqs, color=clrs) 10 | for i, f in enumerate(freqs): 11 | ax1.text(i, f+0.2, str(f), horizontalalignment='center') 12 | ax1.set_ylabel("Số lượng") 13 | 14 | ax2.pie(freqs, labels=labels, autopct="%.0f%%", colors=clrs) 15 | 16 | plt.show() 17 | -------------------------------------------------------------------------------- /lesson13/coin_toss.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | N = int(input("Bạn muốn tung bao nhiêu lần: ")) 4 | coins = [random.randint(0, 1) for i in range(N)] 5 | N_ngua = sum(coins) 6 | N_sap = N - N_ngua 7 | print(f"Số lần ngửa là {N_ngua}/{N} ({N_ngua/N*100:.2f}%)") 8 | print(f"Số lần sấp là {N_sap}/{N} ({N_sap/N*100:.2f}%)") 9 | -------------------------------------------------------------------------------- /lesson13/coin_toss2.py: -------------------------------------------------------------------------------- 1 | import random 2 | import matplotlib.pyplot as plt 3 | from matplotlib.animation import FuncAnimation 4 | 5 | def update(frame): 6 | global N, freqs 7 | 8 | N += 1 9 | freqs[random.randint(0, 1)] += 1 10 | fr = [f/N for f in freqs] 11 | 12 | ax.clear() 13 | ax.set_ylim(0, 1) 14 | ax.bar(["Sấp", "Ngửa"], fr, color=["red", "blue"]) 15 | ax.text(0, fr[0] + 0.01, f"{fr[0]*100:.2f}%", 16 | horizontalalignment='center') 17 | ax.text(1, fr[1]+ 0.01, f"{fr[1]*100:.2f}%", 18 | horizontalalignment='center') 19 | ax.set_title(f"Lần tung thứ {N}") 20 | 21 | N = 0 22 | freqs = [0, 0] # Số lượng sấp, ngửa 23 | 24 | fig, ax = plt.subplots() 25 | ani = FuncAnimation(fig, update, interval=1000//24) 26 | plt.show() 27 | -------------------------------------------------------------------------------- /lesson13/con_lac_don.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson13/con_lac_don.gif -------------------------------------------------------------------------------- /lesson13/con_lac_don.py: -------------------------------------------------------------------------------- 1 | from math import pi as PI, sin, cos, sqrt 2 | import matplotlib.pyplot as plt 3 | from matplotlib.animation import FuncAnimation 4 | 5 | def update(frame): 6 | t = frame/FPS 7 | anpha = A0*cos(OMEGA*t - PI) 8 | x, y = L*sin(anpha), L*(1 - cos(anpha)) 9 | 10 | ax.clear() 11 | ax.axis("equal"); ax.axis("off") 12 | ax.set_xlim([-1.1*L*sin(A0), 1.1*L*sin(A0)]) 13 | ax.set_ylim([-0.1*L, 1.1*L]) 14 | ax.plot([0, x], [L, y], color="black", linewidth=1) 15 | ax.plot([x], [y], "o", color="red", markersize=10) 16 | 17 | L = 1 # chiều dài dây treo (m) 18 | OMEGA = sqrt(9.81/L) # tần số góc (1/s^2) 19 | T = 2*PI/OMEGA # chu kì dao động (s) 20 | A0 = PI/15 # biên độ góc (rad) 21 | FPS = 24 # frames per second 22 | 23 | fig, ax = plt.subplots() 24 | ani = FuncAnimation(fig, update, interval=1000//FPS, frames=round(T*FPS)) 25 | ani.save("con_lac_don.gif", writer="pillow") 26 | print("Done!") 27 | -------------------------------------------------------------------------------- /lesson13/histogram_boxplot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson13/histogram_boxplot.png -------------------------------------------------------------------------------- /lesson13/histogram_boxplot.py: -------------------------------------------------------------------------------- 1 | import random 2 | import matplotlib.pyplot as plt 3 | 4 | N = 10_000 5 | nums = [random.random() for _ in range(N)] 6 | 7 | fig, (ax1, ax2) = plt.subplots(1, 2) 8 | ax1.hist(nums, bins=10, rwidth=0.95) 9 | ax2.boxplot(nums) 10 | plt.show() 11 | -------------------------------------------------------------------------------- /lesson13/image.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson13/image.jpeg -------------------------------------------------------------------------------- /lesson13/image_dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson13/image_dot.png -------------------------------------------------------------------------------- /lesson13/image_violet.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson13/image_violet.jpeg -------------------------------------------------------------------------------- /lesson13/image_walk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson13/image_walk.png -------------------------------------------------------------------------------- /lesson13/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson13/interactive_plot.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | from matplotlib.widgets import Slider 3 | 4 | def update(val): 5 | t = val = int(val) 6 | if val == 1: 7 | tex = "y = x" 8 | elif val > 1: 9 | tex = "y = $x^{%d}$" % val 10 | else: 11 | t = 1/(-val + 2) 12 | tex = r"y = $x^{\frac{1}{%d}}$" % (-val + 2) 13 | 14 | ys = [x**t for x in xs] 15 | ax.clear() 16 | ax.plot(xs, ys, color="r") 17 | ax.set_title(tex) 18 | 19 | fig, ax = plt.subplots() 20 | plt.subplots_adjust(left=0.1, right=0.9, bottom=0.2) 21 | slider_ax = plt.axes([0.1, 0.1, 0.8, 0.03]) 22 | slider = Slider(slider_ax, "val", -14, 15, valinit=1, valstep=1, valfmt='%d') 23 | slider.on_changed(update) 24 | 25 | xs = [i/1000 for i in range(1001)] 26 | ax.set_ylim(0, 1) 27 | ax.set_xlim(0, 1) 28 | update(1) 29 | 30 | plt.show() 31 | -------------------------------------------------------------------------------- /lesson13/parabol_area.py: -------------------------------------------------------------------------------- 1 | from random import random 2 | 3 | def parabol_area(N): # N là số điểm gieo mô phỏng 4 | points = [(random(), random()) for _ in range(N)] 5 | N_in = sum([y <= x**2 for x, y in points]) 6 | return N_in/N 7 | 8 | if __name__ == "__main__": 9 | N = int(input("Bạn muốn gieo bao nhiêu điểm: ")) 10 | print(f"Diện tích vùng dưới parabol là {parabol_area(N):.4f}") 11 | -------------------------------------------------------------------------------- /lesson13/parabol_draw.py: -------------------------------------------------------------------------------- 1 | import matplotlib.pyplot as plt 2 | import matplotlib 3 | 4 | fig, ax = plt.subplots() 5 | 6 | # Đặt hệ trục đều (đơn vị bằng nhau cho 2 trục) 7 | ax.axis("equal") 8 | 9 | # Không hiển thị hệ trục 10 | ax.axis("off") 11 | 12 | # Vẽ và tô hình vuông đơn vị 13 | ax.fill([0, 0, 1, 1], [0, 1, 1, 0], facecolor='bisque', edgecolor='black', linewidth=0.5) 14 | 15 | # Vẽ đoạn thẳng chéo (0, 0) đến (1, 1) 16 | xs = [i/100 for i in range(101)] 17 | ax.plot(xs, xs, color="red") 18 | 19 | # Vẽ parabol y = x^2 trong khoảng [0, 1] 20 | ys = [x**2 for x in xs] 21 | ax.plot(xs, ys, color="blue") 22 | 23 | # Tô vùng bên dưới parabol 24 | ax.fill(xs + [1, 0], ys + [0, 0], facecolor='cornflowerblue', edgecolor='black', linewidth=0.5) 25 | 26 | # Vẽ 3 điểm minh họa cùng với các đường gióng 27 | x0 = 0.75 28 | psx, psy = [x0]*3, [0.4, 0.75**2, 0.9] 29 | ax.plot(psx, psy, "o", color="black") 30 | ax.plot([x0, x0], [0, max(psy)], ":", color="black", linewidth=0.5) 31 | for x, y in zip(psx, psy): 32 | ax.plot([0, x], [y, y], ":", color="black", linewidth=0.5) 33 | 34 | # Đặt font mặc định là serif với size là 11 pt 35 | matplotlib.rcParams["font.size"] = 11 36 | matplotlib.rcParams["font.family"] = "serif" 37 | matplotlib.rcParams["mathtext.fontset"] = "cm" 38 | 39 | # Xuất các điểm tọa độ (dùng Latex để hiển thị công thức Toán) 40 | ax.text(-0.06, -0.06, "0") 41 | ax.text(1, -0.06, "1") 42 | ax.text(-0.06, 1, "1") 43 | ax.text(x0, -0.06, "$x_0$") 44 | for index, y in enumerate(psy): 45 | ax.text(-0.06, y, r"$y_{%d}$" % (index + 1)) 46 | ax.text(x0 + 0.02, y, r"$P_{%d}$" % (index + 1)) 47 | 48 | # Chú thích các đường (dùng Latex để hiển thị công thức Toán) 49 | ax.annotate("$y = x$", xy=(0.5, 0.5), xytext=(0.25, 0.65), 50 | arrowprops=dict(facecolor='black', width=0.5, headwidth=5, shrink=0.05)) 51 | ax.annotate("$y = x^2$", xy=(0.5, 0.25), xytext=(0.2, 0.48), 52 | arrowprops=dict(facecolor='black', width=0.5, headwidth=5, shrink=0.05)) 53 | 54 | plt.show() 55 | -------------------------------------------------------------------------------- /lesson13/pillow_draw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson13/pillow_draw.png -------------------------------------------------------------------------------- /lesson13/pillow_draw.py: -------------------------------------------------------------------------------- 1 | from PIL import Image, ImageDraw 2 | 3 | size = (250, 250) 4 | im = Image.new("RGB", size, "red") 5 | d = ImageDraw.Draw(im) 6 | d.ellipse([(0, 0), size], fill="blue") 7 | im.save("pillow_draw.png") 8 | im.show() 9 | -------------------------------------------------------------------------------- /lesson13/random_dots.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson13/random_dots.mp4 -------------------------------------------------------------------------------- /lesson13/random_dots.py: -------------------------------------------------------------------------------- 1 | import random 2 | from PIL import Image 3 | import matplotlib.pyplot as plt 4 | from matplotlib.animation import FuncAnimation 5 | 6 | def update(_): 7 | xy_choices = random.choices(xys, weights, k=K) 8 | for xy in xy_choices: 9 | out_im.putpixel(xy, (0, 0, 0)) 10 | 11 | ax.clear() 12 | ax.imshow(out_im) 13 | 14 | t = 4.0 # tham số điều khiển mật độ 15 | K = 200 # số điểm trong một lần update 16 | 17 | im = Image.open("image.jpeg").convert("L") 18 | xys = [(x, y) for x in range(im.width) for y in range(im.height)] 19 | weights = [((255 - im.getpixel(xy))/255)**t for xy in xys] 20 | out_im = Image.new("RGB", im.size, color=(255, 255, 255)) 21 | 22 | fig, ax = plt.subplots() 23 | ani = FuncAnimation(fig, update, interval=1000//24) 24 | plt.show() 25 | 26 | out_im.save("image_dot.png") 27 | -------------------------------------------------------------------------------- /lesson13/random_walks.py: -------------------------------------------------------------------------------- 1 | import random 2 | from PIL import Image, ImageDraw 3 | import matplotlib.pyplot as plt 4 | from matplotlib.animation import FuncAnimation 5 | 6 | def neighbours(p): 7 | x, y = xys[p] 8 | neigs = [(x + i, y + j) for i in range(-R, R + 1) for j in range(-R, R + 1) 9 | if 0 <= x + i < im.width and 0 <= y + j < im.height] 10 | weigs = [weights[i*im.height + j] for i, j in neigs] 11 | return neigs, weigs 12 | 13 | def update(_): 14 | for _ in range(K): 15 | for i, p in enumerate(walks): 16 | [(x, y)] = random.choices(*neighbours(p)) 17 | lengths[i] -= 1 18 | if lengths[i] < 0: 19 | [walks[i]] = random.choices(range(len(xys)), weights) 20 | lengths[i] = L 21 | else: 22 | out_draw.line([xys[p], (x, y)], fill="black", width=1) 23 | walks[i] = x*im.height + y 24 | 25 | ax.clear() 26 | ax.imshow(out_im) 27 | 28 | t = 4.0 # tham số điều khiển mật độ 29 | K = 50 # số lần đi trong một lần update 30 | N = 10 # số walk 31 | R = 6 # kích thước lân cận 32 | L = 200 # chiều dài tối đa của walk 33 | 34 | im = Image.open("image.jpeg").convert("L") 35 | xys = [(x, y) for x in range(im.width) for y in range(im.height)] 36 | weights = [((255 - im.getpixel(xy))/255)**t for xy in xys] 37 | out_im = Image.new("RGB", im.size, color=(255, 255, 255)) 38 | out_draw = ImageDraw.Draw(out_im) 39 | walks = random.choices(range(len(xys)), weights, k=N) 40 | lengths = [L for _ in range(N)] 41 | 42 | fig, ax = plt.subplots() 43 | ani = FuncAnimation(fig, update, interval=1000//24) 44 | plt.show() 45 | 46 | out_im.save("image_walk.png") 47 | -------------------------------------------------------------------------------- /lesson13/randot_video.py: -------------------------------------------------------------------------------- 1 | import random 2 | from PIL import Image 3 | import matplotlib 4 | import matplotlib.pyplot as plt 5 | from matplotlib.animation import FuncAnimation, FFMpegWriter 6 | from matplotlib.figure import figaspect 7 | 8 | def update(_): 9 | xy_choices = random.choices(xys, weights, k=K) 10 | for xy in xy_choices: 11 | out_im.putpixel(xy, (0, 0, 0)) 12 | 13 | ax.clear() 14 | ax.axis("off") 15 | ax.imshow(out_im) 16 | 17 | t = 4.0 # tham số điều khiển mật độ 18 | K = 100 # số điểm trong một lần update 19 | 20 | im = Image.open("image.jpeg").convert("L") 21 | xys = [(x, y) for x in range(im.width) for y in range(im.height)] 22 | weights = [((255 - im.getpixel(xy))/255)**t for xy in xys] 23 | out_im = Image.new("RGB", im.size, color=(255, 255, 255)) 24 | 25 | FPS = 24 26 | fig, ax = plt.subplots(figsize=figaspect(im.height/im.width)) 27 | plt.subplots_adjust(left=0, right=1, bottom=0, top=1) 28 | matplotlib.rcParams['animation.ffmpeg_path'] = r'D:\ffmpeg\bin\ffmpeg.exe' 29 | ani = FuncAnimation(fig, update, frames=FPS*60) 30 | 31 | print("Wait a Minute!") 32 | ani.save("random_dots.mp4", writer=FFMpegWriter(fps=FPS)) 33 | print("Done!") 34 | -------------------------------------------------------------------------------- /lesson13/violet_img.py: -------------------------------------------------------------------------------- 1 | from PIL import Image 2 | 3 | im = Image.open("image.jpeg") 4 | red, _, blue = im.split() 5 | green = Image.new("L", im.size, 0) 6 | out_im = Image.merge("RGB", (red, green, blue)) 7 | out_im.save("image_violet.jpeg") 8 | out_im.show() 9 | -------------------------------------------------------------------------------- /lesson14/fraction.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class Fraction: 4 | def __init__(self, numer=0, denom=1): 5 | if isinstance(numer, str): 6 | # đối số là chuỗi tử/mẫu hoặc chỉ có tử 7 | nums = numer.split("/") 8 | numer = int(nums[0]) 9 | denom = 1 if len(nums) == 1 else int(nums[1]) 10 | 11 | gcd = math.gcd(numer, denom) 12 | if denom < 0: 13 | gcd = -gcd 14 | self._numer = numer // gcd # tử số 15 | self._denom = denom // gcd # mẫu số 16 | 17 | def __repr__(self): 18 | if self._denom == 1: 19 | return str(self._numer) 20 | else: 21 | return "%d/%d" % (self._numer, self._denom) 22 | 23 | def __float__(self): 24 | return self._numer / self._denom 25 | 26 | def __lt__(self, other): 27 | return float(self) < float(other) 28 | -------------------------------------------------------------------------------- /lesson14/fraction2.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class Fraction: 4 | def __init__(self, numer=0, denom=1): 5 | if isinstance(numer, str): 6 | # đối số là chuỗi tử/mẫu hoặc chỉ có tử 7 | nums = numer.split("/") 8 | numer = int(nums[0]) 9 | denom = 1 if len(nums) == 1 else int(nums[1]) 10 | 11 | gcd = math.gcd(numer, denom) 12 | if denom < 0: 13 | gcd = -gcd 14 | self._numer = numer // gcd # tử số 15 | self._denom = denom // gcd # mẫu số 16 | 17 | def __repr__(self): 18 | if self._denom == 1: 19 | return str(self._numer) 20 | else: 21 | return "%d/%d" % (self._numer, self._denom) 22 | 23 | def __float__(self): 24 | return self._numer / self._denom 25 | 26 | def __lt__(self, other): 27 | return float(self) < float(other) 28 | 29 | def __add__(self, other): 30 | r_numer = self._numer*other._denom + self._denom*other._numer 31 | r_denom = self._denom*other._denom 32 | return Fraction(r_numer, r_denom) 33 | -------------------------------------------------------------------------------- /lesson14/fraction3.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class Fraction: 4 | def __init__(self, numer=0, denom=1): 5 | if isinstance(numer, str): 6 | # đối số là chuỗi tử/mẫu hoặc chỉ có tử 7 | nums = numer.split("/") 8 | numer = int(nums[0]) 9 | denom = 1 if len(nums) == 1 else int(nums[1]) 10 | 11 | gcd = math.gcd(numer, denom) 12 | if denom < 0: 13 | gcd = -gcd 14 | self._numer = numer // gcd # tử số 15 | self._denom = denom // gcd # mẫu số 16 | 17 | def __repr__(self): 18 | if self._denom == 1: 19 | return str(self._numer) 20 | else: 21 | return "%d/%d" % (self._numer, self._denom) 22 | 23 | def __float__(self): 24 | return self._numer / self._denom 25 | 26 | def __lt__(self, other): 27 | return float(self) < float(other) 28 | 29 | def __hash__(self): 30 | return hash((self._numer, self._denom)) 31 | 32 | def __eq__(self, other): 33 | return hash(self) == hash(other) 34 | -------------------------------------------------------------------------------- /lesson14/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson14/k_max.py: -------------------------------------------------------------------------------- 1 | from fraction import Fraction 2 | 3 | nums = [] 4 | while text := input("Nhập phân số (Enter để dừng): "): 5 | nums.append(Fraction(text)) 6 | 7 | nums.sort(reverse=True) 8 | print("Các số đã nhập theo thứ tự giảm dần là:") 9 | print(nums) 10 | 11 | k = int(input("Bạn muốn tìm số lớn thứ mấy? ")) 12 | if 0 < k <= len(nums): 13 | print(f"Số lớn thứ {k} đã nhập là {nums[k - 1]}") 14 | -------------------------------------------------------------------------------- /lesson14/many_wheels.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | import time 3 | from random import randint, random 4 | from tri_color_wheel import TriColorWheel 5 | 6 | def on_click(x, y): 7 | for i in range(len(tcws)): 8 | if tcws[i].contain((x, y)): 9 | tcws[i], tcws[-1] = tcws[-1], tcws[i] 10 | return 11 | 12 | def run(): 13 | global tm 14 | 15 | t.clear() 16 | for tcw in tcws: 17 | tcw.rotate(time.time() - tm) 18 | t.update() 19 | tm = time.time() 20 | t.ontimer(run, 1000//24) # 24 fps 21 | 22 | W, H = 600, 600 23 | tcws = [] 24 | for _ in range(25): 25 | radius = randint(10, H//4) 26 | center = (randint(-W//2, W//2), randint(-H//2, H//2)) 27 | omega = randint(0, 360*2) 28 | colors = tuple((random(), random(), random()) 29 | for _ in range(3)) 30 | tcws.append(TriColorWheel(omega, center, radius, colors)) 31 | 32 | t.setup(W, H); t.tracer(False); t.hideturtle() 33 | t.onscreenclick(on_click) 34 | t.onkeypress(lambda: tcws[-1].speed_up(), "Left") 35 | t.onkeypress(lambda: tcws[-1].speed_down(), "Right") 36 | t.onkeypress(t.bye, "Escape") 37 | t.listen(); tm = time.time() 38 | run(); t.mainloop() 39 | -------------------------------------------------------------------------------- /lesson14/pizza.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson14/pizza.gif -------------------------------------------------------------------------------- /lesson14/range_iterator.py: -------------------------------------------------------------------------------- 1 | class range: 2 | def __init__(self, start=0, stop=None, step=1): 3 | self._start = start 4 | self._stop = stop 5 | self._step = step 6 | self._cur = self._start 7 | 8 | def __iter__(self): 9 | # self._cur = self._start 10 | return self 11 | 12 | def __next__(self): 13 | if (self._stop != None and 14 | (self._step > 0 and self._cur >= self._stop 15 | or self._step < 0 and self._cur <= self._stop)): 16 | raise StopIteration 17 | self._cur = (prev := self._cur) + self._step 18 | return prev 19 | -------------------------------------------------------------------------------- /lesson14/record.py: -------------------------------------------------------------------------------- 1 | def Record(typename, field_names, pri_key=None): 2 | class _Record: 3 | nonlocal pri_key 4 | 5 | __slots__ = field_names 6 | if pri_key and not pri_key in __slots__: 7 | pri_key = None 8 | 9 | def __init__(self, **kwargs): 10 | for name in self.__slots__: 11 | setattr(self, name, kwargs.get(name, None)) 12 | 13 | def __repr__(self): 14 | atts = [f"{name}={getattr(self, name)}" 15 | for name in self.__slots__] 16 | return "%s(%s)" % (typename, ", ".join(atts)) 17 | 18 | def __eq__(self, other): 19 | if pri_key: 20 | return (getattr(self, pri_key) 21 | == getattr(other, pri_key)) 22 | else: 23 | atts = [getattr(self, name) == getattr(other, name) 24 | for name in self.__slots__] 25 | return all(atts) 26 | 27 | def __delattr__(self, name): 28 | if name in self.__slots__: 29 | setattr(self, name, None) 30 | 31 | _Record.__name__ = typename 32 | return _Record 33 | -------------------------------------------------------------------------------- /lesson14/sqrt_cal.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class SqrtMethod: 4 | """Class for square root methods""" 5 | 6 | def __init__(self, func, name): 7 | self.func = func 8 | self.name = name 9 | 10 | def cal_sqrt(self, nums): 11 | print(f"Tính căn bằng phương pháp {self.name}:") 12 | for num in nums: 13 | print(f"Căn của {num} là {self.func(num):.9f}") 14 | 15 | if __name__ == "__main__": 16 | sqrt_methods = [ 17 | SqrtMethod(math.sqrt, "math's sqrt"), 18 | SqrtMethod(lambda x: math.pow(x, 0.5), "math's pow"), 19 | SqrtMethod(lambda x: pow(x, 0.5), "built-in pow"), 20 | SqrtMethod(lambda x: x ** 0.5, "exponentiation operator"), 21 | ] 22 | nums = [0.0196, 1.21, 2, 3, 4, 225/256] 23 | for sqrt_method in sqrt_methods: 24 | sqrt_method.cal_sqrt(nums) 25 | -------------------------------------------------------------------------------- /lesson14/students_list.py: -------------------------------------------------------------------------------- 1 | import pprint 2 | from collections import namedtuple 3 | 4 | SV = namedtuple("SinhViên", ["mã_số", "tên", "điểm"]) 5 | 6 | sv = [SV(mã_số=19001, tên="SV A", điểm=6.0), 7 | SV(19005, "SV B", 7.5), 8 | SV(19004, điểm=5.0, tên="SV C"), 9 | SV(tên="SV D", mã_số=19010, điểm=0.5), 10 | SV(19009, "SV E", 10.0)] 11 | 12 | sv.sort(key=lambda sinh_vien: sinh_vien.mã_số) 13 | pprint.pprint(sv) 14 | print(max(sv, key=lambda sinh_vien: sinh_vien.điểm)) 15 | -------------------------------------------------------------------------------- /lesson14/students_list2.py: -------------------------------------------------------------------------------- 1 | import pprint 2 | from record import Record 3 | 4 | SV = Record("SinhViên", ["mã_số", "tên", "điểm"], 5 | pri_key="mã_số") 6 | 7 | sv = [SV(mã_số=19001, tên="SV A", điểm=6.0), 8 | SV(mã_số=19005, tên="SV", điểm=7.5), 9 | SV(mã_số=19004, tên="SV C", điểm=50.0), 10 | SV(mã_số=19010, tên="SV D", điểm=0.5), 11 | SV(mã_số=19009, tên="SV E", điểm=10.0)] 12 | sv[1].tên = "SV B" 13 | sv[sv.index(SV(mã_số=19004))].điểm = 5.0 14 | 15 | sv.sort(key=lambda sinh_vien: sinh_vien.mã_số) 16 | pprint.pprint(sv) 17 | print(max(sv, key=lambda sinh_vien: sinh_vien.điểm)) 18 | -------------------------------------------------------------------------------- /lesson14/tri_color_wheel.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | import time 3 | import math 4 | 5 | class TriColorWheel: 6 | def __init__(self, omega, center=(0, 0), radius=100, 7 | colors=("red", "green", "blue"), 8 | up_key=None, down_key=None): 9 | self.center = center 10 | self.radius = radius 11 | self.colors = colors 12 | self.omega = omega # vận tốc góc độ/giây (+, 0, -) 13 | self.angle = 0 # góc điểm chốt A 14 | if up_key: t.onkeypress(self.speed_up, up_key) 15 | if down_key: t.onkeypress(self.speed_down, down_key) 16 | 17 | def speed_up(self): self.omega += 2 18 | 19 | def speed_down(self): self.omega -= 2 20 | 21 | def rotate(self, dt): 22 | self.angle += self.omega * dt 23 | A = self.angle; B = A + 120; C = B + 120 24 | t.up(); t.goto(self.center); t.down() 25 | for angle, color in zip((A, B, C), self.colors): 26 | t.color(color) 27 | t.begin_fill() 28 | t.setheading(angle); t.forward(self.radius); t.left(90) 29 | t.circle(self.radius, 120); t.goto(self.center) 30 | t.end_fill() 31 | 32 | def contain(self, point): 33 | return math.dist(point, self.center) <= self.radius 34 | 35 | if __name__ == "__main__": 36 | def run(): 37 | global tm 38 | 39 | t.clear() 40 | tcw1.rotate(time.time() - tm) 41 | tcw2.rotate(time.time() - tm) 42 | t.update() 43 | tm = time.time() 44 | t.ontimer(run, 1000//24) # 24 fps 45 | 46 | tcw1 = TriColorWheel(180, center=(100, 0), 47 | up_key="Left", down_key="Right") 48 | tcw2 = TriColorWheel(-180, center=(-100, 0), 49 | colors=("cyan", "magenta", "yellow"), 50 | up_key="a", down_key="d") 51 | 52 | t.tracer(False); t.hideturtle(); t.listen() 53 | tm = time.time(); run(); t.exitonclick() 54 | -------------------------------------------------------------------------------- /lesson14/turtle_race.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | from turtle import window_width as ww, window_height as wh 3 | from random import randint as rint 4 | 5 | class PizzaTurtle(t.Turtle): 6 | all_pizzas = [] 7 | 8 | def __init__(self, shape_file, radius=20, value=1): 9 | super().__init__() 10 | self.radius = radius; self.value = value 11 | self.shape(shape_file); self.ondrag(self.goto) 12 | self.up(); self.goto(random_point(self.radius)) 13 | 14 | PizzaTurtle.all_pizzas.append(self) 15 | 16 | class NinjaTurtle(t.Turtle): 17 | all_ninjas = [] 18 | 19 | def __init__(self, name, color="black", radius=10): 20 | super().__init__() 21 | self.name = name; self.radius = radius; 22 | self.score = 0; self.color(color) 23 | self.shape("turtle") 24 | self.setheading(rint(0, 360)) 25 | self.up() 26 | self.goto(random_point(self.radius)) 27 | self.down() 28 | 29 | NinjaTurtle.all_ninjas.append(self) 30 | 31 | def move(self): 32 | for other_tur in NinjaTurtle.all_ninjas: 33 | if (self is not other_tur and 34 | self.distance(other_tur) < 2*self.radius): 35 | self.setheading(rint(0, 360)) 36 | 37 | if abs(self.xcor()) >= ww()/2 - self.radius: 38 | self.setheading((180 - self.heading()) % 360) 39 | if abs(self.ycor()) >= wh()/2 - self.radius: 40 | self.setheading((360 - self.heading()) % 360) 41 | self.forward(2) 42 | 43 | def random_point(r): 44 | x = rint(-ww()//2 + r, ww()//2 - r) 45 | y = rint(-wh()//2 + r, wh()/2 - r) 46 | return (x, y) 47 | 48 | def check_winner(): 49 | for pizza in PizzaTurtle.all_pizzas: 50 | removed = False 51 | for tur in NinjaTurtle.all_ninjas: 52 | if pizza.distance(tur) < pizza.radius/2: 53 | tur.score += pizza.value 54 | print(tur.name, "win pizza value", pizza.value) 55 | print(tur.name, "'s score is", tur.score) 56 | removed = True 57 | if removed: 58 | pizza.hideturtle() 59 | t.update() 60 | PizzaTurtle.all_pizzas.remove(pizza) 61 | 62 | if len(PizzaTurtle.all_pizzas) == 0: 63 | max_score = (max(NinjaTurtle.all_ninjas, 64 | key=(lambda tur: tur.score))).score 65 | for tur in NinjaTurtle.all_ninjas: 66 | if tur.score == max_score: 67 | print(tur.name, "win the race!") 68 | tur.turtlesize(2.5, 2.5) 69 | print("The winning score is", max_score) 70 | return True 71 | return False 72 | 73 | def run(): 74 | if check_winner(): 75 | t.update() 76 | return 77 | for tur in NinjaTurtle.all_ninjas: 78 | tur.move() 79 | t.update(); t.ontimer(run, 10) 80 | 81 | t.title("Teenage Mutant Ninja Turtles Race for Pizza!") 82 | t.addshape("pizza.gif"); t.tracer(False) 83 | 84 | for i in range(10): 85 | PizzaTurtle("pizza.gif") 86 | for name, color in [("Leonardo", "blue"), 87 | ("Michelangelo", "orange"), 88 | ("Donatello", "purple"), 89 | ("Raphael", "red")]: 90 | NinjaTurtle(name, color) 91 | 92 | t.onkeypress(t.bye); t.listen() 93 | run(); t.mainloop() 94 | -------------------------------------------------------------------------------- /lesson14/turtle_race2.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | from turtle import window_width as ww, window_height as wh 3 | from random import randint as rint, random as rfloat 4 | 5 | class PizzaTurtle(t.Turtle): 6 | all_pizzas = [] 7 | 8 | def __init__(self, shape_file, radius=20, value=1): 9 | super().__init__() 10 | self.radius = radius; self.value = value 11 | self.shape(shape_file); self.ondrag(self.goto) 12 | self.up(); self.goto(random_point(self.radius)) 13 | 14 | PizzaTurtle.all_pizzas.append(self) 15 | 16 | class NinjaTurtle(t.Turtle): 17 | all_ninjas = [] 18 | 19 | def __init__(self, name, color="black", radius=10): 20 | super().__init__() 21 | self.name = name; self.radius = radius; 22 | self.score = 0; self.color(color) 23 | self.shape("turtle") 24 | self.setheading(rint(0, 360)) 25 | self.up() 26 | self.goto(random_point(self.radius)) 27 | self.down() 28 | 29 | NinjaTurtle.all_ninjas.append(self) 30 | 31 | def move(self): 32 | for other_tur in NinjaTurtle.all_ninjas: 33 | if (self is not other_tur and 34 | self.distance(other_tur) < 2*self.radius): 35 | self.setheading(rint(0, 360)) 36 | 37 | if abs(self.xcor()) >= ww()/2 - self.radius: 38 | self.setheading((180 - self.heading()) % 360) 39 | if abs(self.ycor()) >= wh()/2 - self.radius: 40 | self.setheading((360 - self.heading()) % 360) 41 | self.forward(2) 42 | 43 | def random_point(r): 44 | x = rint(-ww()//2 + r, ww()//2 - r) 45 | y = rint(-wh()//2 + r, wh()/2 - r) 46 | return (x, y) 47 | 48 | def check_winner(): 49 | for pizza in PizzaTurtle.all_pizzas: 50 | removed = False 51 | for tur in NinjaTurtle.all_ninjas: 52 | if pizza.distance(tur) < pizza.radius/2: 53 | tur.score += pizza.value 54 | print(tur.name, "win pizza value", pizza.value) 55 | print(tur.name, "'s score is", tur.score) 56 | removed = True 57 | if removed: 58 | pizza.hideturtle() 59 | t.update() 60 | PizzaTurtle.all_pizzas.remove(pizza) 61 | 62 | if len(PizzaTurtle.all_pizzas) == 0: 63 | max_score = (max(NinjaTurtle.all_ninjas, 64 | key=(lambda tur: tur.score))).score 65 | for tur in NinjaTurtle.all_ninjas: 66 | if tur.score == max_score: 67 | print(tur.name, "win the race!") 68 | tur.turtlesize(2.5, 2.5) 69 | print("The winning score is", max_score) 70 | return True 71 | return False 72 | 73 | def run(): 74 | if check_winner(): 75 | t.update() 76 | return 77 | for tur in NinjaTurtle.all_ninjas: 78 | tur.move() 79 | t.update(); t.ontimer(run, 10) 80 | 81 | t.title("Teenage Mutant Ninja Turtles Race for Pizza!") 82 | t.addshape("pizza.gif"); t.tracer(False) 83 | 84 | for i in range(50): 85 | PizzaTurtle("pizza.gif") 86 | for i in range(100): 87 | NinjaTurtle(f"#{i+1}", color=(rfloat(), rfloat(), rfloat())) 88 | 89 | t.onkeypress(t.bye); t.listen() 90 | run(); t.mainloop() 91 | -------------------------------------------------------------------------------- /lesson14/write_char.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | import string 3 | 4 | class WriteChar: 5 | def __init__(self, char): 6 | self.char = char 7 | 8 | def write(self): 9 | t.clear() 10 | t.write(self.char, align="center", font=f"Arial {s}") 11 | t.update() 12 | 13 | 14 | s = 300 15 | t.hideturtle(); t.tracer(False); t.color("red") 16 | t.up(); t.right(90); t.forward(2*s//3); t.down() 17 | 18 | for char in string.ascii_letters + string.digits: 19 | t.onkey(WriteChar(char).write, char) 20 | 21 | t.listen() 22 | t.exitonclick() 23 | -------------------------------------------------------------------------------- /lesson14/write_char2.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | import string 3 | 4 | class WriteChar: 5 | def __init__(self, char): 6 | self.char = char 7 | 8 | def __call__(self): 9 | t.clear() 10 | t.write(self.char, align="center", font=f"Arial {s}") 11 | t.update() 12 | 13 | 14 | s = 300 15 | t.hideturtle(); t.tracer(False); t.color("red") 16 | t.up(); t.right(90); t.forward(2*s//3); t.down() 17 | 18 | for char in string.ascii_letters + string.digits: 19 | t.onkey(WriteChar(char), char) 20 | 21 | t.listen() 22 | t.exitonclick() 23 | -------------------------------------------------------------------------------- /lesson15/Newton_sqrt.py: -------------------------------------------------------------------------------- 1 | def Newton_sqrt(x): 2 | if x < 0: 3 | raise ValueError("math domain error") 4 | if x == 0: 5 | return 0.0 6 | 7 | y = x 8 | for i in range(100): 9 | y = y/2 + x/(2*y) 10 | return y 11 | -------------------------------------------------------------------------------- /lesson15/REP_emulate.py: -------------------------------------------------------------------------------- 1 | from math import * 2 | 3 | while True: 4 | exp = input(">>> ") 5 | if exp == "quit()": 6 | break 7 | if exp.strip() == "": 8 | continue 9 | 10 | try: 11 | val = eval(exp) 12 | except Exception as e: 13 | print(f"{type(e).__name__}: {e}") 14 | else: 15 | if val != None: 16 | print(val) 17 | -------------------------------------------------------------------------------- /lesson15/factorial.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | try: 4 | n = int(input("Nhập n: ")) 5 | except ValueError: 6 | n = 1 7 | print(f"Giá trị nhập không hợp lệ! n nhận giá trị mặc định là {n}.") 8 | 9 | print(f"Giai thừa của {n} là {math.factorial(n)}") 10 | -------------------------------------------------------------------------------- /lesson15/factorial2.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | while True: 4 | try: 5 | n = int(input("Nhập n: ")) 6 | break 7 | except ValueError: 8 | print(f"Giá trị nhập không hợp lệ!") 9 | 10 | print(f"Giai thừa của {n} là {math.factorial(n)}") 11 | -------------------------------------------------------------------------------- /lesson15/factorial3.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | try: 4 | while True: 5 | try: 6 | n = int(input("Nhập n: ")) 7 | break 8 | except ValueError: 9 | print(f"Giá trị nhập không hợp lệ!") 10 | print(f"Giai thừa của {n} là {math.factorial(n)}") 11 | except: 12 | print("Có bất thường gì đó!!!") 13 | raise 14 | -------------------------------------------------------------------------------- /lesson15/factorial4.py: -------------------------------------------------------------------------------- 1 | import math 2 | import warnings 3 | 4 | n = int(input("Nhập n: ")) 5 | 6 | if n >= 0: 7 | f = math.factorial(n) 8 | else: # n < 0 9 | warnings.warn("n nên không âm!", UserWarning) 10 | f = -math.factorial(-n) 11 | 12 | print(f"Giai thừa của {n} là {f}") 13 | -------------------------------------------------------------------------------- /lesson15/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson15/run_factorial.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | import runpy 3 | 4 | warnings.simplefilter("error", UserWarning) 5 | runpy.run_module("factorial4") 6 | -------------------------------------------------------------------------------- /lesson15/run_factorial2.py: -------------------------------------------------------------------------------- 1 | import warnings 2 | import runpy 3 | 4 | warnings.simplefilter("ignore", UserWarning) 5 | runpy.run_module("factorial4") 6 | -------------------------------------------------------------------------------- /lesson15/sqrt_cal.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | class SqrtMethod: 4 | def __init__(self, func, name): 5 | self.name = name 6 | try: 7 | func(1.0) 8 | self.func = func 9 | except TypeError: 10 | raise SqrtMethodError(f"hàm tính căn {name} không hợp lệ!", func) 11 | 12 | def cal_sqrt(self, nums): 13 | print(f"Tính căn bằng phương pháp {self.name}:") 14 | for num in nums: 15 | print(f"Căn của {num} là {self.func(num):.9f}") 16 | 17 | 18 | class SqrtMethodError(TypeError): 19 | def __init__(self, msg, func): 20 | super().__init__(msg) 21 | self.func = func 22 | 23 | 24 | try: 25 | sqrt1 = SqrtMethod(math.sqrt, "math's sqrt") 26 | sqrt1.cal_sqrt([2, 4]) 27 | sqrt2 = SqrtMethod(math.pow, "math's pow") 28 | sqrt2.cal_sqrt([2, 4]) 29 | except SqrtMethodError as err: 30 | print(f"Có lỗi tính căn: {err.args[0]}, {err.func}") 31 | -------------------------------------------------------------------------------- /lesson15/turtle_escape.py: -------------------------------------------------------------------------------- 1 | import turtle as t 2 | import random 3 | 4 | t.shape("turtle") 5 | d = 20 6 | 7 | try: 8 | while (abs(t.xcor()) < t.window_width()/2 9 | and abs(t.ycor()) < t.window_height()/2): 10 | direction = random.choice("LRUD") 11 | if direction == "L": 12 | t.setheading(180) 13 | elif direction == "R": 14 | t.setheading(0) 15 | elif direction == "U": 16 | t.setheading(90) 17 | else: # direction == "D" 18 | t.setheading(270) 19 | t.forward(d) 20 | print("Congratulations!") 21 | 22 | except t.Terminator: 23 | pass 24 | -------------------------------------------------------------------------------- /lesson16/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson16/pygame_draw.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | 3 | pygame.init() 4 | screen = pygame.display.set_mode((600, 600)) 5 | pygame.display.set_caption("Draw with Pygame") 6 | 7 | WHITE = (255, 255, 255) 8 | RED = (255, 0, 0) 9 | 10 | screen.fill(WHITE) 11 | drawing = False 12 | running = True 13 | while running: 14 | for event in pygame.event.get(): 15 | if event.type == pygame.QUIT: 16 | running = False 17 | if event.type == pygame.MOUSEBUTTONDOWN: 18 | if event.button == 1: 19 | drawing = True 20 | pos = event.pos 21 | else: 22 | screen.fill(WHITE) 23 | if event.type == pygame.MOUSEBUTTONUP and event.button == 1: 24 | drawing = False 25 | 26 | if drawing: 27 | newpos = pygame.mouse.get_pos() 28 | pygame.draw.line(screen, RED, pos, newpos, 1) 29 | pos = newpos 30 | 31 | pygame.display.update() 32 | 33 | pygame.quit() 34 | -------------------------------------------------------------------------------- /lesson16/pygame_intro.py: -------------------------------------------------------------------------------- 1 | import time 2 | import pygame 3 | 4 | w = 20 5 | pygame.init() 6 | screen = pygame.display.set_mode((60*w, 6*w)) 7 | pygame.display.set_caption("Welcome to Pygame") 8 | 9 | WHITE = (255, 255, 255) 10 | DARK_GREEN = (0, 100, 0) 11 | ORANGE_RED = (255, 69, 0) 12 | MAROON = (128, 0, 0) 13 | PINK = (255, 192, 203) 14 | 15 | (car := pygame.Surface((9*w, 5*w))).fill(WHITE) 16 | pygame.draw.polygon(car, DARK_GREEN, [(3*w, 0), (6*w, 0), (7*w, int(1.5*w)), 17 | (9*w, 2*w), (9*w, 4*w), (0, 4*w), (0, int(2.5*w)), (2*w, int(1.5*w))]) 18 | pygame.draw.rect(car, ORANGE_RED, (3*w, w, int(1.2*w), 2*w)) 19 | pygame.draw.rect(car, ORANGE_RED, (int(4.8*w), w, int(1.2*w), 2*w)) 20 | pygame.draw.circle(car, MAROON, (2*w, 4*w), w) 21 | pygame.draw.circle(car, PINK, (2*w, 4*w), w//2) 22 | pygame.draw.circle(car, MAROON, (7*w, 4*w), w) 23 | pygame.draw.circle(car, PINK, (7*w, 4*w), w//2) 24 | 25 | x = -9*w 26 | running = True 27 | while running: 28 | for event in pygame.event.get(): 29 | if event.type == pygame.QUIT: 30 | running = False 31 | screen.fill(WHITE) 32 | screen.blit(car, (x, w)) 33 | pygame.display.update() 34 | time.sleep(0.005) 35 | x += 1 36 | if x > 60*w: 37 | x = -9*w 38 | 39 | pygame.quit() 40 | -------------------------------------------------------------------------------- /lesson16/screen_saver.py: -------------------------------------------------------------------------------- 1 | import time, random 2 | import pygame 3 | 4 | def draw_random_rectangle(): 5 | sw, sh = screen.get_size() 6 | x, y = random.randrange(sw), random.randrange(sh) 7 | width, height = random.randrange(sw - x), random.randrange(sh - y) 8 | color = (random.randrange(256), random.randrange(256), random.randrange(0, 256)) 9 | pygame.draw.rect(screen, color, (x, y, width, height), 0) 10 | 11 | pygame.init() 12 | screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN) 13 | pygame.mouse.set_visible(False) 14 | 15 | screen.fill((255, 192, 203)) # PINK 16 | running = True 17 | while running: 18 | for event in pygame.event.get(): 19 | if event.type == pygame.KEYDOWN or event.type == pygame.MOUSEBUTTONDOWN: 20 | running = False 21 | 22 | draw_random_rectangle() 23 | pygame.display.update() 24 | time.sleep(0.25) 25 | 26 | pygame.quit() 27 | -------------------------------------------------------------------------------- /lesson16/snake.py: -------------------------------------------------------------------------------- 1 | import random, time 2 | import pygame 3 | 4 | class Settings: 5 | def __init__(self): 6 | self.back_color = (255, 192, 203) # Pink 7 | self.text_color = (169,169,169) # Dark Gray 8 | self.head_color = (0,0,128) # Navy 9 | self.body_color = (0,0,255) # Blue 10 | self.item_color = (255,69,0) # Orangered 11 | self.gameover_color = (255, 0, 0) # Red 12 | 13 | self.cell_width = 15 # pixels 14 | self.width, self.height = 50, 25 # cells 15 | self.font_scale = 2 16 | 17 | self.level = 1 18 | self.score = 0 19 | self.speed = 0.3 # s/move 20 | 21 | self.DELTA = {"Right": (1, 0), "Left": (-1, 0), 22 | "Up": (0, -1), "Down": (0, 1)} 23 | self.KEY_DIR = {pygame.K_RIGHT: ("Left", "Right"), 24 | pygame.K_LEFT: ("Right", "Left"), 25 | pygame.K_UP: ("Down", "Up"), 26 | pygame.K_DOWN: ("Up", "Down")} 27 | 28 | def incr_score(self): 29 | self.score += 1 30 | if self.score % 5 == 0: 31 | self.level += 1 32 | self.speed *= 0.8 33 | 34 | 35 | class Board: 36 | def __init__(self): 37 | self._setup() 38 | pygame.init() 39 | self._screen = pygame.display.set_mode( 40 | (self.sett.width * self.sett.cell_width, 41 | self.sett.height * self.sett.cell_width)) 42 | pygame.display.set_caption("Snake Game") 43 | self._font = pygame.font.SysFont(None, 44 | self.sett.font_scale * self.sett.cell_width) 45 | 46 | def _setup(self): 47 | self.sett = Settings() 48 | self._snake_cells = [(self.sett.width//2 + d, 49 | self.sett.height//2) for d in range(-1, 2)] 50 | self._item_cell = self._rand_item_cell() 51 | self._direction = "Right" 52 | self._pausing = True 53 | self._gameover = False 54 | 55 | def _rand_item_cell(self): 56 | while True: 57 | x, y = (random.randint(0, self.sett.width - 1), 58 | random.randint(0, self.sett.height - 1)) 59 | if (x, y) not in self._snake_cells: 60 | return (x, y) 61 | 62 | def _draw_board(self): 63 | self._screen.fill(self.sett.back_color) 64 | if self._gameover: 65 | text = self._font.render( 66 | f"Game over! Score: {self.sett.score}. Press Space to Play", True, 67 | self.sett.gameover_color, self.sett.back_color) 68 | elif self._pausing: 69 | text = self._font.render( 70 | "Press Space to Play/Pause", True, 71 | self.sett.text_color, self.sett.back_color) 72 | else: 73 | text = self._font.render( 74 | f"Level: {self.sett.level} - Score: {self.sett.score}", True, 75 | self.sett.text_color, self.sett.back_color) 76 | 77 | text_rect = text.get_rect() 78 | text_rect.centerx = self._screen.get_rect().centerx 79 | text_rect.centery = self.sett.font_scale * self.sett.cell_width 80 | self._screen.blit(text, text_rect) 81 | 82 | if self._gameover: 83 | self._draw_cell(self._snake_cells[-1], 84 | self.sett.gameover_color) 85 | else: 86 | self._draw_cell(self._snake_cells[-1], 87 | self.sett.head_color) 88 | self._draw_cell(self._item_cell, 89 | self.sett.item_color) 90 | for i in range(len(self._snake_cells) - 1): 91 | self._draw_cell(self._snake_cells[i], 92 | self.sett.body_color) 93 | 94 | def _draw_cell(self, cell, color): 95 | pygame.draw.rect(self._screen, color, 96 | (cell[0] * self.sett.cell_width, 97 | cell[1] * self.sett.cell_width, 98 | self.sett.cell_width, self.sett.cell_width)) 99 | 100 | def _move_snake(self, direction): 101 | next_x = (self._snake_cells[-1][0] 102 | + self.sett.DELTA[direction][0]) 103 | next_y = (self._snake_cells[-1][1] 104 | + self.sett.DELTA[direction][1]) 105 | if (next_x not in range(self.sett.width) 106 | or next_y not in range(self.sett.height) 107 | or (next_x, next_y) in self._snake_cells): 108 | return False 109 | 110 | self._snake_cells.append((next_x, next_y)) 111 | if self._item_cell == (next_x, next_y): 112 | self._item_cell = self._rand_item_cell() 113 | self.sett.incr_score() 114 | else: 115 | self._snake_cells.pop(0) 116 | return True 117 | 118 | def run(self): 119 | while True: 120 | for event in pygame.event.get(): 121 | if event.type == pygame.QUIT: 122 | return 123 | if event.type == pygame.KEYDOWN: 124 | if event.key == pygame.K_SPACE: 125 | if self._gameover: 126 | self._setup() 127 | else: 128 | self._pausing = not self._pausing 129 | if not self._pausing: 130 | start_time = time.time() 131 | if (not self._pausing 132 | and event.key in self.sett.KEY_DIR): 133 | if self._direction != self.sett.KEY_DIR[event.key][0]: 134 | self._direction = self.sett.KEY_DIR[event.key][1] 135 | 136 | if not self._pausing: 137 | tm = time.time() 138 | if tm - start_time >= self.sett.speed: 139 | if self._move_snake(self._direction): 140 | start_time = time.time() 141 | else: # Gameover 142 | self._pausing = True 143 | self._gameover = True 144 | 145 | self._draw_board() 146 | pygame.display.update() 147 | 148 | def quit(self): 149 | pygame.quit() 150 | print("Bye!") 151 | 152 | board = Board() 153 | board.run() 154 | board.quit() 155 | -------------------------------------------------------------------------------- /lesson16/tetris/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson16/tetris/music.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson16/tetris/music.mp3 -------------------------------------------------------------------------------- /lesson16/tetris/sound_gameover.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson16/tetris/sound_gameover.wav -------------------------------------------------------------------------------- /lesson16/tetris/sound_landing.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson16/tetris/sound_landing.wav -------------------------------------------------------------------------------- /lesson16/tetris/sound_match.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson16/tetris/sound_match.wav -------------------------------------------------------------------------------- /lesson16/tetris/sound_move.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson16/tetris/sound_move.wav -------------------------------------------------------------------------------- /lesson16/tetris/sound_pause.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson16/tetris/sound_pause.wav -------------------------------------------------------------------------------- /lesson16/tetris/sound_play.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson16/tetris/sound_play.wav -------------------------------------------------------------------------------- /lesson16/tetris/sound_rotate.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson16/tetris/sound_rotate.wav -------------------------------------------------------------------------------- /lesson16/tetris/tetris.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | import random, time 3 | 4 | TETROMINOS = { 5 | "I": { "box": [[0, -1, -1, 0, 0], 6 | [-1, -1, -1, 0, 0], 7 | [1, 1, 1, 1, -1], 8 | [0, 0, -1, -1, -1], 9 | [0, 0, -1, -1, 0]], 10 | "size": (5, 5), # width, height 11 | "center": (1.5, 2.5) # xc, yc 12 | }, 13 | "T": { "box": [[0, 0, -1, 0, 0], 14 | [0, -1, 1, -1, 0], 15 | [-1, 1, 1, 1, -1], 16 | [0, 0, -1, -1, 0], 17 | [0, 0, -1, 0, 0]], 18 | "size": (5, 5), 19 | "center": (2.0, 2.0) 20 | }, 21 | "J": { "box": [[0, -1, -1, -1, 0], 22 | [0, 1, -1, -1, 0], 23 | [-1, 1, 1, 1, -1], 24 | [0, 0, -1, -1, 0], 25 | [0, 0, -1, 0, 0]], 26 | "size": (5, 5), 27 | "center": (2.0, 2.0) 28 | }, 29 | "L": { "box": [[0, 0, -1, 0, 0], 30 | [0, -1, -1, 1, -1], 31 | [-1, 1, 1, 1, -1], 32 | [0, 0, -1, -1, -1], 33 | [0, 0, -1, 0, 0]], 34 | "size": (5, 5), 35 | "center": (2.0, 2.0) 36 | }, 37 | "O": { "box": [[0, -1, -1, 0, 0], 38 | [-1, 1, 1, -1, 0], 39 | [-1, 1, 1, -1, 0], 40 | [0, -1, -1, 0, 0], 41 | [0, 0, 0, 0, 0]], 42 | "size": (5, 5), 43 | "center": (1.5, 1.5) 44 | }, 45 | "S": { "box": [[0, 0, -1, 0, 0], 46 | [0, -1, 1, 1, -1], 47 | [-1, 1, 1, -1, -1], 48 | [0, 0, -1, -1, -1], 49 | [0, 0, 0, 0, 0]], 50 | "size": (5, 5), 51 | "center": (2.0, 2.0) 52 | }, 53 | "Z": { "box": [[0, -1, -1, -1, 0], 54 | [0, 1, 1, -1, 0], 55 | [0, -1, 1, 1, -1], 56 | [0, 0, -1, -1, 0], 57 | [0, 0, -1, 0, 0]], 58 | "size": (5, 5), 59 | "center": (2.0, 2.0) 60 | } 61 | } 62 | 63 | class Settings: 64 | def __init__(self): 65 | self.back_color = (255, 192, 203) # Pink 66 | self.inf_back_color =(169, 169, 169) # Dark Gray 67 | self.text_color = (0, 0, 0) # Black 68 | self.nexttet_color = (255, 69, 0) # Orangered 69 | self.matrix_color = (0, 0, 128) # Navy 70 | self.tet_color = (0, 0, 255) # Blue 71 | self.gameover_color = (255, 0, 0) # Red 72 | 73 | self.move_sound = pygame.mixer.Sound("sound_move.wav") 74 | self.rotate_sound = pygame.mixer.Sound("sound_rotate.wav") 75 | self.landing_sound = pygame.mixer.Sound("sound_landing.wav") 76 | self.match_sound = pygame.mixer.Sound("sound_match.wav") 77 | self.play_sound = pygame.mixer.Sound("sound_play.wav") 78 | self.pause_sound = pygame.mixer.Sound("sound_pause.wav") 79 | self.gameover_sound = pygame.mixer.Sound("sound_gameover.wav") 80 | 81 | self.cell_width = 28 # pixels 82 | self.width, self.height, self.inf_width = 10, 20, 7 # cells 83 | self.font_scale = 1 84 | self.level = 1 85 | self.score = 0 86 | self.nextlvscore = 50 87 | self.speed = 1.0 # 1s/move 88 | self.music_on = True 89 | 90 | self.KEY_DIR = {pygame.K_RIGHT: (0, 1), pygame.K_LEFT: (0, -1), 91 | pygame.K_DOWN: (1, 0)} 92 | def incr_score(self, totalrow): 93 | self.score += totalrow * 10 + (totalrow - 1) * 5 94 | if self.score >= self.nextlvscore: 95 | self.level += 1 96 | self.nextlvscore += 50 97 | self.speed *= 0.6 98 | 99 | class Board: 100 | def _map(self, pos): 101 | xc, yc = TETROMINOS[self.tet]["center"] 102 | if self.tetdir == 90: 103 | return (int(pos[1] - xc + yc), int(-pos[0] + yc + xc)) 104 | elif self.tetdir == 180: 105 | return (int(-pos[0] + 2*yc), int(-pos[1] + 2*xc)) 106 | elif self.tetdir == 270: 107 | return (int(-pos[1] + xc + yc), int(pos[0] - yc + xc)) 108 | else: 109 | return pos 110 | 111 | def _nexttet(self): 112 | self.tet = self.next_tet 113 | self.tetrow = -2 114 | self.tetcol = self.sett.width//2 - TETROMINOS[self.tet]["size"][0]//2 115 | self.tetdir = 0 116 | self.next_tet = random.choice(list(TETROMINOS)) 117 | return self._canmove((0, 0)) 118 | 119 | 120 | def _canmove(self, pos): # r, c 121 | for r in range(TETROMINOS[self.tet]["size"][1]): 122 | for c in range(TETROMINOS[self.tet]["size"][0]): 123 | if TETROMINOS[self.tet]["box"][r][c] == 1: 124 | nr, nc = self._map((r, c)) 125 | nr += self.tetrow + pos[0] 126 | nc += self.tetcol + pos[1] 127 | if nr >= self.sett.height or nc not in range(self.sett.width) \ 128 | or (nr >= 0 and self.matrix[nr][nc]) == 1: 129 | return False 130 | return True 131 | 132 | def _canrotate(self): 133 | for r in range(TETROMINOS[self.tet]["size"][1]): 134 | for c in range(TETROMINOS[self.tet]["size"][0]): 135 | if TETROMINOS[self.tet]["box"][r][c] == -1: 136 | nr, nc = self._map((r, c)) 137 | nr += self.tetrow 138 | nc += self.tetcol 139 | if nr >= self.sett.height or nc not in range(self.sett.width) \ 140 | or (nr >= 0 and self.matrix[nr][nc] == 1): 141 | return False 142 | return True 143 | 144 | def _rotate(self): 145 | self.tetdir = (self.tetdir + 90) % 360 146 | 147 | def _fixtet(self): 148 | self.sett.landing_sound.play() 149 | 150 | for r in range(TETROMINOS[self.tet]["size"][1]): 151 | for c in range(TETROMINOS[self.tet]["size"][0]): 152 | if TETROMINOS[self.tet]["box"][r][c] == 1: 153 | nr, nc = self._map((r, c)) 154 | nr += self.tetrow 155 | nc += self.tetcol 156 | if nr in range(self.sett.height) and nc in range(self.sett.width): 157 | self.matrix[nr][nc] = 1 158 | 159 | self.clearing = True 160 | nfullrow = 0 161 | row = self.sett.height - 1 162 | while row >= 0: 163 | if all([self.matrix[row][col] == 1 for col in range(self.sett.width)]): 164 | nfullrow += 1 165 | for col in range(self.sett.width): 166 | self.matrix[row][col] = 0 167 | self.draw_board() 168 | pygame.display.update() 169 | self.sett.match_sound.play() 170 | time.sleep(0.30) 171 | for ra in range(row - 1, -1, -1): 172 | for col in range(self.sett.width): 173 | self.matrix[ra + 1][col] = self.matrix[ra][col] 174 | self.draw_board() 175 | pygame.display.update() 176 | time.sleep(0.30) 177 | else: 178 | row -= 1 179 | self.clearing = False 180 | 181 | if nfullrow > 0: 182 | self.sett.incr_score(nfullrow) 183 | 184 | def rand_item_cell(self): 185 | while True: 186 | x, y = random.randint(0, self.sett.width - 1), random.randint(0, self.sett.height - 1) 187 | if (x, y) not in self.snake_cells: 188 | return (x, y) 189 | 190 | def draw_cell(self, cell, color): # (r, c) 191 | pygame.draw.rect(self.screen, color, 192 | (cell[1] * self.sett.cell_width + 1, cell[0] * self.sett.cell_width + 1, self.sett.cell_width - 2, self.sett.cell_width - 2)) 193 | 194 | def draw_board(self): 195 | self.screen.fill(self.sett.inf_back_color) 196 | pygame.draw.rect(self.screen, self.sett.back_color, 197 | (0, 0, self.sett.width * self.sett.cell_width, self.sett.height * self.sett.cell_width)) 198 | 199 | text = self.font.render(f"Level: {self.sett.level}", True, self.sett.text_color, self.sett.inf_back_color) 200 | self.screen.blit(text, ((self.sett.width + 1) * self.sett.cell_width, 6 * self.sett.font_scale * self.sett.cell_width)) 201 | text = self.font.render(f"Score: {self.sett.score}", True, self.sett.text_color, self.sett.inf_back_color) 202 | self.screen.blit(text, ((self.sett.width + 1) * self.sett.cell_width, 7 * self.sett.font_scale * self.sett.cell_width)) 203 | text = self.font.render(f"Next: {self.sett.nextlvscore}", True, self.sett.text_color, self.sett.inf_back_color) 204 | self.screen.blit(text, ((self.sett.width + 1) * self.sett.cell_width, 8 * self.sett.font_scale * self.sett.cell_width)) 205 | text = self.font.render(f"Music (M): {'On' if self.sett.music_on else 'Off'}", True, self.sett.text_color, self.sett.inf_back_color) 206 | self.screen.blit(text, ((self.sett.width + 1) * self.sett.cell_width, 9 * self.sett.font_scale * self.sett.cell_width)) 207 | 208 | # draw next tet 209 | for r in range(TETROMINOS[self.next_tet]["size"][1]): 210 | for c in range(TETROMINOS[self.next_tet]["size"][0]): 211 | if TETROMINOS[self.next_tet]["box"][r][c] == 1: 212 | self.draw_cell((r + 1, c + self.sett.width + 1), self.sett.nexttet_color) 213 | 214 | # draw matrix 215 | for r in range(self.sett.height): 216 | for c in range(self.sett.width): 217 | if self.matrix[r][c] == 1: 218 | self.draw_cell((r, c), self.sett.matrix_color) 219 | 220 | # draw cur tet 221 | if not self.clearing: 222 | for r in range(TETROMINOS[self.tet]["size"][1]): 223 | for c in range(TETROMINOS[self.tet]["size"][0]): 224 | if TETROMINOS[self.tet]["box"][r][c] == 1: 225 | nr, nc = self._map((r, c)) 226 | nr += self.tetrow 227 | nc += self.tetcol 228 | if nr in range(self.sett.height) and nc in range(self.sett.width): 229 | self.draw_cell((nr, nc), self.sett.tet_color) 230 | 231 | text = None 232 | if self.gameover: 233 | text = self.font.render(f"Game over! Score: {self.sett.score}", True, self.sett.gameover_color, self.sett.back_color) 234 | elif self.pausing: 235 | text = self.font.render("Press Space to Play/Pause", True, self.sett.text_color, self.sett.back_color) 236 | 237 | if text is not None: 238 | text_rect = text.get_rect() 239 | text_rect.centerx = self.sett.width * self.sett.cell_width // 2 240 | text_rect.centery = self.sett.height * self.sett.cell_width // 2 241 | self.screen.blit(text, text_rect) 242 | 243 | def _start(self): 244 | self.sett = Settings() 245 | self.matrix = [[0 for w in range(self.sett.width)] for h in range(self.sett.height)] 246 | self.next_tet = random.choice(list(TETROMINOS)) 247 | self._nexttet() 248 | self.pausing = True 249 | self.clearing = False 250 | self.gameover = False 251 | 252 | def __init__(self): 253 | pygame.init() 254 | pygame.mixer.music.load("music.mp3") 255 | pygame.mixer.music.play(-1, 0) 256 | self._start() 257 | self.screen = pygame.display.set_mode(((self.sett.width + self.sett.inf_width) * self.sett.cell_width, self.sett.height * self.sett.cell_width)) 258 | pygame.display.set_caption("Tetris Game") 259 | self.font = pygame.font.SysFont(None, self.sett.font_scale * self.sett.cell_width) 260 | 261 | def run(self): 262 | while True: 263 | for event in pygame.event.get(): 264 | if event.type == pygame.QUIT: 265 | return 266 | if event.type == pygame.KEYDOWN: 267 | if event.key == pygame.K_SPACE: 268 | if self.gameover: 269 | self._start() 270 | else: 271 | self.pausing = not self.pausing 272 | if self.pausing: 273 | self.sett.pause_sound.play() 274 | else: 275 | self.sett.play_sound.play() 276 | if not self.pausing: 277 | start_time = time.time() 278 | if event.key == pygame.K_m: 279 | self.sett.music_on = not self.sett.music_on 280 | if self.sett.music_on: 281 | pygame.mixer.music.play(-1, 0) 282 | else: 283 | pygame.mixer.music.stop() 284 | if not self.pausing: 285 | if (event.key in self.sett.KEY_DIR) and self._canmove(self.sett.KEY_DIR[event.key]): 286 | # move 287 | self.sett.move_sound.play() 288 | self.tetcol += self.sett.KEY_DIR[event.key][1] 289 | self.tetrow += self.sett.KEY_DIR[event.key][0] 290 | if event.key == pygame.K_UP and self._canrotate(): 291 | # rotate 292 | self.sett.rotate_sound.play() 293 | self._rotate() 294 | 295 | if not self.pausing: 296 | tm = time.time() 297 | if tm - start_time >= self.sett.speed: 298 | if self._canmove((1, 0)): 299 | self.tetrow += 1 300 | elif self._fixtet(): 301 | # tang level 302 | self.incr_score() 303 | elif not self._nexttet(): 304 | # gameover 305 | self.sett.gameover_sound.play() 306 | self.pausing = True 307 | self.gameover = True 308 | start_time = time.time() 309 | 310 | self.draw_board() 311 | pygame.display.update() 312 | 313 | def quit(self): 314 | pygame.quit() 315 | print("Bye!") 316 | 317 | board = Board() 318 | board.run() 319 | board.quit() 320 | -------------------------------------------------------------------------------- /lesson17/Pascal_redirect.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import runpy 3 | 4 | stdout = sys.stdout 5 | with open("Pascal_triangle.txt", "wt") as f: 6 | sys.stdout = f 7 | runpy.run_module("Pascal_triangle") 8 | sys.stdout = stdout 9 | -------------------------------------------------------------------------------- /lesson17/Pascal_triangle.py: -------------------------------------------------------------------------------- 1 | n = int(input()) # Không có chuỗi thông báo nhập 2 | a = [] # Pascal triangle 3 | 4 | for i in range(n): 5 | a.append([1] * (i + 1)) 6 | 7 | for i in range(1, n): 8 | for j in range(1, i): 9 | a[i][j] = a[i-1][j-1] + a[i-1][j] 10 | 11 | w = 1 + len(str(max([max(row) for row in a]))) # độ rộng canh phải 12 | for i in range(n): 13 | for j in range(i + 1): 14 | print(f"%{w}d" % a[i][j], end="") 15 | print() 16 | -------------------------------------------------------------------------------- /lesson17/Pascal_triangle.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 1 1 3 | 1 2 1 4 | 1 3 3 1 5 | 1 4 6 4 1 6 | 1 5 10 10 5 1 7 | 1 6 15 20 15 6 1 8 | 1 7 21 35 35 21 7 1 9 | 1 8 28 56 70 56 28 8 1 10 | 1 9 36 84 126 126 84 36 9 1 11 | -------------------------------------------------------------------------------- /lesson17/Zen_of_Python.txt: -------------------------------------------------------------------------------- 1 | Beautiful is better than ugly. 2 | Explicit is better than implicit. 3 | Simple is better than complex. 4 | Complex is better than complicated. 5 | Flat is better than nested. 6 | Sparse is better than dense. 7 | Readability counts. 8 | Special cases aren't special enough to break the rules. 9 | Although practicality beats purity. 10 | Errors should never pass silently. 11 | Unless explicitly silenced. 12 | In the face of ambiguity, refuse the temptation to guess. 13 | There should be one-- and preferably only one --obvious way to do it. 14 | Although that way may not be obvious at first unless you're Dutch. 15 | Now is better than never. 16 | Although never is often better than *right* now. 17 | If the implementation is hard to explain, it's a bad idea. 18 | If the implementation is easy to explain, it may be a good idea. 19 | Namespaces are one honking great idea -- let's do more of those! -------------------------------------------------------------------------------- /lesson17/asc_redirect.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import runpy 3 | 4 | stdin = sys.stdin 5 | with open("rand_int.txt", "rt") as f: 6 | sys.stdin = f 7 | runpy.run_module("ascending") 8 | sys.stdin = stdin 9 | -------------------------------------------------------------------------------- /lesson17/asc_redirect2.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import runpy 3 | 4 | stdin, stdout = sys.stdin, sys.stdout 5 | with open("rand_int.txt", "rt") as infile, \ 6 | open("sorted_int.txt", "wt") as outfile: 7 | sys.stdin = infile 8 | sys.stdout = outfile 9 | runpy.run_module("ascending") 10 | sys.stdin, sys.stdout = stdin, stdout 11 | -------------------------------------------------------------------------------- /lesson17/ascending.py: -------------------------------------------------------------------------------- 1 | nums = [] 2 | while True: 3 | try: 4 | nums.append(int(input())) 5 | except: 6 | break 7 | 8 | for num in sorted(nums): 9 | print(num) 10 | -------------------------------------------------------------------------------- /lesson17/hello.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | def sayhello(*names, file=sys.stdout): 4 | for name in names: 5 | hello_string = " Chào " + name + " " 6 | print("=" * (len(hello_string) + 2), file=file) 7 | print("|" + hello_string + "|", file=file) 8 | print("=" * (len(hello_string) + 2), file=file) 9 | 10 | sayhello("Python", "Vũ Quốc Hoàng", "Guido van Rossum") 11 | f = open("hello_message.txt", "wt", encoding="utf-8") 12 | sayhello("Python", "Vũ Quốc Hoàng", "Guido van Rossum", file=f) 13 | f.close() 14 | -------------------------------------------------------------------------------- /lesson17/hello_message.txt: -------------------------------------------------------------------------------- 1 | =============== 2 | | Chào Python | 3 | =============== 4 | ====================== 5 | | Chào Vũ Quốc Hoàng | 6 | ====================== 7 | ========================= 8 | | Chào Guido van Rossum | 9 | ========================= 10 | -------------------------------------------------------------------------------- /lesson17/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson17/rand_int.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import random 3 | 4 | n = int(input("Số lượng số: ")) 5 | 6 | stdout = sys.stdout 7 | with open("rand_int.txt", "wt") as f: 8 | sys.stdout = f 9 | for i in range(n): 10 | print(random.randint(0, 1_000_000)) 11 | sys.stdout = stdout 12 | -------------------------------------------------------------------------------- /lesson17/rand_int.txt: -------------------------------------------------------------------------------- 1 | 3689 2 | 277842 3 | 362470 4 | 542772 5 | 807532 6 | 322434 7 | 743728 8 | 636910 9 | 489723 10 | 658620 11 | 826956 12 | 522616 13 | 322103 14 | 352301 15 | 821660 16 | 228195 17 | 827305 18 | 209073 19 | 846484 20 | 954739 21 | -------------------------------------------------------------------------------- /lesson17/read_CSV.py: -------------------------------------------------------------------------------- 1 | sv = [] 2 | with open("sv.csv", "rt", encoding="utf-8") as f: 3 | f.readline() # header 4 | for line in f: 5 | s = line.strip().split(",") 6 | sv.append((int(s[0]), s[1], float(s[2]))) 7 | print(max(sv, key=lambda sinh_vien: sinh_vien[2])) 8 | -------------------------------------------------------------------------------- /lesson17/read_CSV2.py: -------------------------------------------------------------------------------- 1 | import csv 2 | 3 | sv = [] 4 | with open("sv2.csv", "rt", encoding="utf-8", newline="") as f: 5 | reader = csv.reader(f) 6 | sv = list(reader) 7 | print(max(sv, key=lambda sinh_vien: sinh_vien[2])) 8 | -------------------------------------------------------------------------------- /lesson17/read_JSON.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | with open("sv.json", "rt") as f: 4 | sv = json.load(f) 5 | print(max(sv, key=lambda sinh_vien: sinh_vien[2])) 6 | -------------------------------------------------------------------------------- /lesson17/read_ZoP.py: -------------------------------------------------------------------------------- 1 | f = open("Zen_of_Python.txt", "rt") 2 | print(f.read()) 3 | f.close() 4 | -------------------------------------------------------------------------------- /lesson17/read_ZoP2.py: -------------------------------------------------------------------------------- 1 | f = open("Zen_of_Python.txt", "rt") 2 | aphorisms = [line.strip() for line in f.readlines()] 3 | f.close() 4 | 5 | print(f"Có {len(aphorisms)} khẩu quyết") 6 | while key := input("Bạn muốn đọc khẩu quyết nào? "): 7 | try: 8 | # key là số nguyên, tra khẩu quyết thứ key (tính từ 1) 9 | index = int(key) - 1 10 | if 0 <= index < len(aphorisms): 11 | print("#%d: %s" % (index + 1, aphorisms[index])) 12 | except ValueError: 13 | # tra khẩu quyết chứa key 14 | for i, aphorism in enumerate(aphorisms, 1): 15 | if key in aphorism: 16 | print("#%d: %s" % (i, aphorism)) 17 | -------------------------------------------------------------------------------- /lesson17/read_ZoP3.py: -------------------------------------------------------------------------------- 1 | with open("Zen_of_Python.txt", "rt") as f: 2 | print(f.read()) 3 | -------------------------------------------------------------------------------- /lesson17/sorted_int.txt: -------------------------------------------------------------------------------- 1 | 3689 2 | 209073 3 | 228195 4 | 277842 5 | 322103 6 | 322434 7 | 352301 8 | 362470 9 | 489723 10 | 522616 11 | 542772 12 | 636910 13 | 658620 14 | 743728 15 | 807532 16 | 821660 17 | 826956 18 | 827305 19 | 846484 20 | 954739 21 | -------------------------------------------------------------------------------- /lesson17/student_data_model.py: -------------------------------------------------------------------------------- 1 | import csv 2 | 3 | def open_student_file(file_name, field_names): 4 | try: 5 | with open(file_name, mode='r', encoding="utf-8", newline="") as csv_file: 6 | reader = csv.DictReader(csv_file) 7 | students = list(reader) 8 | # kiểm tra có chứa các field_names 9 | if len(students) > 0 and (set(field_names) <= set(students[0])): 10 | return students 11 | except: 12 | return None 13 | 14 | def save_student_file(file_name, field_names, students): 15 | try: 16 | with open(file_name, mode='w', encoding="utf-8", newline="") as csv_file: 17 | writer = csv.DictWriter(csv_file, field_names) 18 | writer.writeheader() 19 | for student in students: 20 | writer.writerow(student) 21 | return True 22 | except: 23 | return False 24 | -------------------------------------------------------------------------------- /lesson17/sv.csv: -------------------------------------------------------------------------------- 1 | Mã số,Tên,Điểm 2 | 19001,SV A,6.0 3 | 19004,SV C,5.0 4 | 19005,SV B,7.5 5 | 19009,SV E,10.0 6 | 19010,SV D,0.5 7 | -------------------------------------------------------------------------------- /lesson17/sv.json: -------------------------------------------------------------------------------- 1 | [[19001, "SV A", 6.0], [19004, "SV C", 5.0], [19005, "SV B", 7.5], [19009, "SV E", 10.0], [19010, "SV D", 0.5]] -------------------------------------------------------------------------------- /lesson17/sv2.csv: -------------------------------------------------------------------------------- 1 | 19001,SV A,6.0 2 | 19004,SV C,5.0 3 | 19005,SV B,7.5 4 | 19009,SV E,10.0 5 | 19010,SV D,0.5 6 | -------------------------------------------------------------------------------- /lesson17/triangle.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | char = input("Nhập kí hiệu vẽ: ") 4 | n = int(input("Nhập chiều cao: ")) 5 | 6 | stdout = sys.stdout 7 | with open("triangle.txt", "wt") as f: 8 | sys.stdout = f 9 | for i in range(1, n + 1): 10 | print(char * i) 11 | sys.stdout = stdout 12 | print("Done!") 13 | -------------------------------------------------------------------------------- /lesson17/triangle.txt: -------------------------------------------------------------------------------- 1 | # 2 | ## 3 | ### 4 | #### 5 | ##### 6 | ###### 7 | ####### 8 | ######## 9 | ######### 10 | ########## 11 | -------------------------------------------------------------------------------- /lesson17/write_CSV.py: -------------------------------------------------------------------------------- 1 | sv = [(19001, "SV A", 6.0), 2 | (19005, "SV B", 7.5), 3 | (19004, "SV C", 5.0), 4 | (19010, "SV D", 0.5), 5 | (19009, "SV E", 10.0)] 6 | 7 | sv.sort(key=lambda sinh_vien: sinh_vien[0]) 8 | with open("sv.csv", "wt", encoding="utf-8") as f: 9 | f.write("Mã số,Tên,Điểm\n") # header 10 | for ms, ten, diem in sv: 11 | f.write(f"{ms},{ten},{diem}\n") 12 | -------------------------------------------------------------------------------- /lesson17/write_CSV2.py: -------------------------------------------------------------------------------- 1 | import csv 2 | 3 | sv = [(19001, "SV A", 6.0), 4 | (19005, "SV B", 7.5), 5 | (19004, "SV C", 5.0), 6 | (19010, "SV D", 0.5), 7 | (19009, "SV E", 10.0)] 8 | 9 | sv.sort(key=lambda sinh_vien: sinh_vien[0]) 10 | with open("sv2.csv", "wt", encoding="utf-8", newline="") as f: 11 | writer = csv.writer(f) 12 | writer.writerows(sv) 13 | -------------------------------------------------------------------------------- /lesson17/write_JSON.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | sv = [(19001, "SV A", 6.0), 4 | (19005, "SV B", 7.5), 5 | (19004, "SV C", 5.0), 6 | (19010, "SV D", 0.5), 7 | (19009, "SV E", 10.0)] 8 | sv.sort(key=lambda sinh_vien: sinh_vien[0]) 9 | with open("sv.json", "wt") as f: 10 | json.dump(sv, f) 11 | -------------------------------------------------------------------------------- /lesson18/Euclid_gcd.py: -------------------------------------------------------------------------------- 1 | def Euclid_gcd(a, b): # a, b >= 0 2 | if b == 0: 3 | return a 4 | return Euclid_gcd(b, a % b) 5 | -------------------------------------------------------------------------------- /lesson18/Euclid_gcd2.py: -------------------------------------------------------------------------------- 1 | def Euclid_gcd2(a, b): # a, b >= 0 2 | while b > 0: 3 | a, b = b, a % b 4 | return a 5 | -------------------------------------------------------------------------------- /lesson18/Ha_Noi_Tower.py: -------------------------------------------------------------------------------- 1 | import time 2 | import pygame 3 | 4 | def draw(): 5 | screen.fill(PINK) 6 | text = font.render(f"{num_moves}", True, DARK_GRAY, PINK) 7 | screen.blit(text, (screen.get_rect().centerx - text.get_rect().width//2, h)) 8 | for rod, rod_x in zip([A, B, C], [w, int(2.5*w), 4*w]): 9 | pygame.draw.rect(screen, MAROON, (rod_x - 5, 4*h, 10, (n + 1)*h)) 10 | y = screen.get_rect().height 11 | for disk in rod: 12 | disk_w = int((0.3 + 0.7*disk/n) * w) 13 | disk_color = (0, 0, int(disk/n*255)) 14 | pygame.draw.rect(screen, disk_color, (rod_x - disk_w//2, y - h, disk_w, h - 1)) 15 | y -= h 16 | pygame.display.update() 17 | 18 | def move(num_disk, rod1, rod2, rod3): 19 | global num_moves 20 | 21 | if num_disk == 0: 22 | return 23 | move(num_disk - 1, rod1, rod3, rod2) 24 | rod2.append(rod1.pop()) 25 | num_moves += 1 26 | draw(); time.sleep(1) 27 | move(num_disk - 1, rod3, rod2, rod1) 28 | 29 | n = 5 30 | w, h = 200, 25 31 | MAROON = (128, 0, 0) 32 | PINK = (255, 192, 203) 33 | DARK_GRAY = (169,169,169) 34 | 35 | pygame.init() 36 | screen = pygame.display.set_mode((5*w, (n + 5)*h)) 37 | pygame.display.set_caption("Ha Noi Tower") 38 | font = pygame.font.SysFont(None, 2*h) 39 | 40 | A, B, C = list(range(n, 0, -1)), [], [] 41 | num_moves = 0 42 | draw(); time.sleep(1) 43 | move(n, A, B, C) 44 | 45 | input("Press Enter to quit. ") 46 | pygame.quit() 47 | -------------------------------------------------------------------------------- /lesson18/Sierpinski_carpet.py: -------------------------------------------------------------------------------- 1 | import pygame 2 | 3 | def Sierpinski(x0, y0, w, level): 4 | if level == stop_level: 5 | return 6 | 7 | for i in range(3): 8 | for j in range(3): 9 | if i == 1 and j == 1: 10 | pygame.draw.rect(screen, WHITE, (x0 + w//3, y0 + w//3, w//3, w//3)) 11 | else: 12 | Sierpinski(x0 + i*w//3, y0 + j*w//3, w//3, level + 1) 13 | 14 | width = 600 15 | stop_level = 5 16 | WHITE = (255, 255, 255) 17 | PINK = (255, 192, 203) 18 | 19 | pygame.init() 20 | screen = pygame.display.set_mode((width, width)) 21 | pygame.display.set_caption("Sierpinski carpet") 22 | 23 | screen.fill(PINK) 24 | Sierpinski(0, 0, width, 0) 25 | pygame.display.update() 26 | 27 | input("Press Enter to quit. ") 28 | pygame.quit() 29 | -------------------------------------------------------------------------------- /lesson18/factorial_rec.py: -------------------------------------------------------------------------------- 1 | def fact(n): # n là số nguyên dương 2 | if n == 1: 3 | return 1 4 | 5 | return fact(n - 1) * n 6 | -------------------------------------------------------------------------------- /lesson18/find_primes.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def prime(n): # n là số nguyên > 1 4 | for i in range(2, math.isqrt(n) + 1): 5 | if n % i == 0: # n có ước > 1 và <= căn n 6 | return False 7 | return True 8 | 9 | def naive_find_primes(n): 10 | return [i for i in range(2, n+1) if prime(i)] 11 | 12 | def Sieve_of_Eratosthenes(n): 13 | sieve = [True for _ in range(n + 1)] 14 | for i in range(2, math.isqrt(n) + 1): 15 | if sieve[i]: 16 | m = i**2 17 | while m <= n: 18 | sieve[m] = False 19 | m += i 20 | return [i for i in range(2, n+1) if sieve[i]] 21 | 22 | import time 23 | n = 1_000_000 24 | t0 = time.time(); Sieve_of_Eratosthenes(n); print(time.time() - t0) 25 | t0 = time.time(); naive_find_primes(n); print(time.time() - t0) 26 | -------------------------------------------------------------------------------- /lesson18/get_removed_duplicates.py: -------------------------------------------------------------------------------- 1 | def get_removed_duplicates(li): 2 | if len(li) == 0: 3 | return [] 4 | else: 5 | rest = get_removed_duplicates(li[1:]) 6 | return rest if li[0] in rest else [li[0]] + rest 7 | 8 | li = [1, 2, 3, 2, 1] 9 | print(get_removed_duplicates(li)) 10 | print(li) 11 | -------------------------------------------------------------------------------- /lesson18/guess_number.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | max = 1000 4 | print("Trò chơi đoán số!") 5 | print(f"Bạn đoán một con số nguyên trong phạm vi 1-{max}.") 6 | 7 | secret = random.randint(1, max) 8 | count = 0 9 | while True: 10 | count += 1 11 | n = int(input(f"Mời bạn đoán lần {count}: ")) 12 | 13 | if n < secret: 14 | print("Số bạn đoán nhỏ quá!") 15 | elif n > secret: 16 | print("Số bạn đoán lớn quá!") 17 | else: 18 | print(f"Bạn đoán đúng số {secret} sau {count} lần.") 19 | break 20 | -------------------------------------------------------------------------------- /lesson18/guess_number2.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | def binary_guess(): 4 | if left <= right: 5 | return (left + right) // 2 6 | else: 7 | return None 8 | 9 | def hint(n, msg): 10 | global left, right 11 | 12 | if msg == "less": 13 | left = n + 1 14 | else: 15 | right = n - 1 16 | 17 | max = 1000 18 | print("Trò chơi đoán số!") 19 | print(f"Bạn đoán một con số nguyên trong phạm vi 1-{max}.") 20 | 21 | left, right = 1, max 22 | secret = random.randint(1, max) 23 | count = 0 24 | while True: 25 | count += 1 26 | input("Press Enter to guess.") 27 | n = binary_guess() 28 | print(f"Đoán lần {count} số {n}") 29 | 30 | if n < secret: 31 | print("Số bạn đoán nhỏ quá!") 32 | hint(n, "less") 33 | elif n > secret: 34 | print("Số bạn đoán lớn quá!") 35 | hint(n, "greater") 36 | else: 37 | print(f"Bạn đoán đúng số {secret} sau {count} lần.") 38 | break 39 | -------------------------------------------------------------------------------- /lesson18/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson18/naive_gcd.py: -------------------------------------------------------------------------------- 1 | def naive_gcd(a, b): # a, b > 0 2 | divisors_a = {d for d in range(1, a+1) if a % d == 0} 3 | divisors_b = {d for d in range(1, b+1) if b % d == 0} 4 | common_divisors = divisors_a & divisors_b 5 | return max(common_divisors) 6 | -------------------------------------------------------------------------------- /lesson18/remove_duplicates_rec.py: -------------------------------------------------------------------------------- 1 | def remove_duplicates(li, s=0): 2 | if s >= len(li): 3 | return 4 | 5 | if li[s] in li[s+1:]: 6 | del li[s] 7 | remove_duplicates(li, s) 8 | else: 9 | remove_duplicates(li, s + 1) 10 | 11 | li = [1, 2, 3, 2, 1] 12 | print(remove_duplicates(li)) 13 | print(li) 14 | -------------------------------------------------------------------------------- /lesson18/sum_rec.py: -------------------------------------------------------------------------------- 1 | def S(n): # n là số nguyên không âm 2 | if n == 0: 3 | return 0 4 | 5 | return S(n - 1) + n 6 | -------------------------------------------------------------------------------- /lesson18/sumdigit_rec.py: -------------------------------------------------------------------------------- 1 | def sum_digits(n): 2 | if n < 10: 3 | return n 4 | 5 | return sum_digits(n // 10) + n % 10 6 | -------------------------------------------------------------------------------- /lesson18/sumlist_rec.py: -------------------------------------------------------------------------------- 1 | def sum_list(li): 2 | if len(li) == 0: 3 | return 0 4 | 5 | return li[0] + sum_list(li[1:]) 6 | -------------------------------------------------------------------------------- /lesson19/caro_game/about_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson19/caro_game/about_icon.png -------------------------------------------------------------------------------- /lesson19/caro_game/caro_dialog.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk, colorchooser 3 | 4 | class Dialog(tk.Toplevel): 5 | def __init__(self, parent, title, dx, dy, *args, **kwargs): 6 | super().__init__(parent, *args, **kwargs) 7 | self._parent = parent 8 | self._ok = False 9 | self.title(title) 10 | self.geometry(f"+{parent.winfo_x() + dx}+{parent.winfo_y() + dy}") 11 | self.resizable(False, False) 12 | 13 | self.main_frame = ttk.Frame(self) 14 | self.main_frame.grid(sticky="WENS", padx=6, pady=4) 15 | (button_frame := ttk.Frame(self)).grid(row=1, sticky="WE", padx=6, pady=8) 16 | ttk.Button(button_frame, text="Cancel", command=self._on_cancel).pack(side="right") 17 | ttk.Button(button_frame, text="OK", command=self._on_ok).pack(side="right", padx=10) 18 | 19 | self.bind('', self._on_ok) 20 | self.bind('', self._on_cancel) 21 | 22 | def _on_ok(self, event=None): 23 | self._ok = True 24 | self.destroy() 25 | 26 | def _on_cancel(self, event=None): 27 | self._ok = False 28 | self.destroy() 29 | 30 | def do_modal(self): 31 | self.focus_set() 32 | self.grab_set() 33 | self.transient(self._parent) 34 | self._parent.wait_window(self) 35 | 36 | def is_ok(self): 37 | return self._ok 38 | 39 | 40 | class NewDialog(Dialog): 41 | 42 | MAN_VS_MAN, MAN_VS_APP, APP_VS_MAN = 0, 1, 2 43 | 44 | def __init__(self, parent, title, dx, dy, *args, **kwargs): 45 | super().__init__(parent, title, dx, dy, *args, **kwargs) 46 | self._initialize() 47 | 48 | def _initialize(self): 49 | ttk.Label(self.main_frame, text="Mode:").grid(row=0, sticky="W") 50 | ttk.Label(self.main_frame, text="1st player:").grid(row=1, sticky="W") 51 | ttk.Label(self.main_frame, text="1st name:").grid(row=2, sticky="W") 52 | ttk.Label(self.main_frame, text="2nd name:").grid(row=3, sticky="W") 53 | 54 | self._mode = tk.IntVar() 55 | self._is_O_first = tk.BooleanVar() 56 | self._is_O_first.trace("w", self._on_change_first_player) 57 | self._1st_name = tk.StringVar() 58 | self._2nd_name = tk.StringVar() 59 | 60 | (mode_frame := ttk.Frame(self.main_frame)).grid(row=0, column=1, sticky="W") 61 | ttk.Radiobutton(mode_frame, text="man vs man", variable=self._mode, value=NewDialog.MAN_VS_MAN).pack(side="left") 62 | ttk.Radiobutton(mode_frame, text="man vs app", variable=self._mode, value=NewDialog.MAN_VS_APP).pack(side="left", padx=4) 63 | ttk.Radiobutton(mode_frame, text="app vs man", variable=self._mode, value=NewDialog.APP_VS_MAN).pack(side="left", padx=4) 64 | 65 | (firstplayer_frame := ttk.Frame(self.main_frame)).grid(row=1, column=1, sticky="W") 66 | ttk.Radiobutton(firstplayer_frame, text="X", variable=self._is_O_first, value=False).pack(side="left") 67 | ttk.Radiobutton(firstplayer_frame, text="O", variable=self._is_O_first, value=True).pack(side="left", padx=4) 68 | 69 | ttk.Entry(self.main_frame, textvariable=self._1st_name).grid(row=2, column=1, sticky="WE") 70 | ttk.Entry(self.main_frame, textvariable=self._2nd_name).grid(row=3, column=1, sticky="WE") 71 | 72 | for child in self.main_frame.winfo_children(): 73 | child.grid(pady=2) 74 | 75 | self._is_O_first.set(False) 76 | 77 | def _on_change_first_player(self, *args): 78 | if self._1st_name.get() in ["", "O", "o", "X", "x"]: 79 | self._1st_name.set("O" if self._is_O_first.get() else "X") 80 | if self._2nd_name.get() in ["", "O", "o", "X", "x"]: 81 | self._2nd_name.set("X" if self._is_O_first.get() else "O") 82 | 83 | def get_mode(self): 84 | return self._mode.get() 85 | 86 | def is_O_first(self): 87 | return self._is_O_first.get() 88 | 89 | def get_1st_name(self): 90 | if name1 := self._1st_name.get(): 91 | return name1 92 | return "O" if self._is_O_first.get() else "X" 93 | 94 | def get_2nd_name(self): 95 | if name2 := self._2nd_name.get(): 96 | return name2 97 | return "X" if self._is_O_first.get() else "O" 98 | 99 | 100 | class SettingsDialog(Dialog): 101 | # color_dic: {key: {"name": , "color": }} 102 | def __init__(self, parent, title, dx, dy, color_dict, *args, **kwargs): 103 | super().__init__(parent, title, dx, dy, *args, **kwargs) 104 | self._color_dict = color_dict 105 | self._initialize() 106 | 107 | def _initialize(self): 108 | self._buttons = {} 109 | for index, key in enumerate(self._color_dict): 110 | ttk.Label(self.main_frame, text=f"{self._color_dict[key]['name']} color:").grid(row=index, sticky="W") 111 | self._buttons[key] = tk.Button(self.main_frame, width=10, borderwidth=1, bg=self._color_dict[key]["color"], 112 | command=self._on_change_color(key)) 113 | self._buttons[key].grid(row=index, column=1, sticky="W") 114 | 115 | for child in self.main_frame.winfo_children(): 116 | child.grid(pady=2) 117 | 118 | def _on_change_color(self, key): 119 | def _chage_color(): 120 | if choosen_color := colorchooser.askcolor(parent=self, title=f"Choose the {self._color_dict[key]['name']} color", 121 | initialcolor=self._color_dict[key]['color'])[1]: 122 | self._buttons[key].configure(bg=choosen_color) 123 | self._color_dict[key]["color"] = choosen_color 124 | 125 | return _chage_color 126 | 127 | def get_color_dict(self): 128 | return self._color_dict 129 | -------------------------------------------------------------------------------- /lesson19/caro_game/caro_game.py: -------------------------------------------------------------------------------- 1 | import string, json 2 | import tkinter as tk 3 | from tkinter import ttk, messagebox, filedialog 4 | import caro_dialog 5 | 6 | class Settings: 7 | def __init__(self): 8 | # https://www.rapidtables.com/web/color/gray-color.html 9 | self.back_color = "#FFC0CB" # Pink 10 | self.grid_color = "#A9A9A9" # Dark Gray 11 | self.highlight_color = "#FFFF00" # Yellow 12 | self.X_color = "#FF0000" # Red 13 | self.O_color = "#0000FF" # Blue 14 | 15 | self.title = "Caro Game" 16 | self.cell_width = 25 # pixels 25 17 | self.width, self.height = 20, 20 # cells 20, 20 18 | self.padx, self.pady = 5, 5 # pixel each margin 5, 5 19 | self.X_char, self.O_char = "X", "O" 20 | self.XO_font = ("Arial", self.cell_width - 6, "bold") 21 | 22 | # https://www.iconfinder.com/iconsets/32x32-free-design-icons 23 | self.app_icon = tk.PhotoImage(file="caro_icon.png") 24 | self.new_icon = tk.PhotoImage(file="new_icon.png") 25 | self.open_icon = tk.PhotoImage(file="open_icon.png") 26 | self.save_icon = tk.PhotoImage(file="save_icon.png") 27 | self.undo_icon = tk.PhotoImage(file="undo_icon.png") 28 | self.redo_icon = tk.PhotoImage(file="redo_icon.png") 29 | self.about_icon = tk.PhotoImage(file="about_icon.png") 30 | self.settings_icon = tk.PhotoImage(file="settings_icon.png") 31 | 32 | class CaroGame(tk.Tk): 33 | def __init__(self, *args, **kwargs): 34 | super().__init__(*args, **kwargs) 35 | self._cur_cell_id = None 36 | self._moves_id = [] 37 | self._cell_line_id = [] 38 | self.sett = Settings() 39 | self.title(self.sett.title) 40 | self.resizable(False, False) 41 | self.iconphoto(True, self.sett.app_icon) 42 | 43 | self._initialize() 44 | self._start("Press New to play new game or Open to open an existing game") 45 | 46 | def _initialize(self): 47 | (toolbar_frame := ttk.Frame(self, relief="raised")).pack(ipady=2, fill="x") 48 | ttk.Button(toolbar_frame, image=self.sett.new_icon, command=self._on_new).pack(side="left") 49 | ttk.Button(toolbar_frame, image=self.sett.open_icon, command=self._on_open).pack(side="left") 50 | self._save_button = ttk.Button(toolbar_frame, image=self.sett.save_icon, command=self._on_save) 51 | self._save_button.pack(side="left") 52 | ttk.Button(toolbar_frame, image=self.sett.settings_icon, command=self._on_settings).pack(side="left", padx=10) 53 | self._undo_button = ttk.Button(toolbar_frame, image=self.sett.undo_icon, command=self._on_undo) 54 | self._undo_button.pack(side="left") 55 | self._redo_button = ttk.Button(toolbar_frame, image=self.sett.redo_icon, command=self._on_redo) 56 | self._redo_button.pack(side="left") 57 | ttk.Button(toolbar_frame, image=self.sett.about_icon, command=self._on_about).pack(side="left", padx=10) 58 | 59 | self._canvas = tk.Canvas(self, background=self.sett.back_color, width=self.sett.width*self.sett.cell_width + 2*self.sett.padx, 60 | height=self.sett.height*self.sett.cell_width + 2*self.sett.pady) 61 | self._canvas.pack() 62 | for row in range(self.sett.height + 1): 63 | self._canvas.create_line(self._get_canvas_coord(row, 0), self._get_canvas_coord(row, self.sett.width), 64 | fill=self.sett.grid_color) 65 | for col in range(self.sett.width + 1): 66 | self._canvas.create_line(self._get_canvas_coord(0, col), self._get_canvas_coord(self.sett.height, col), 67 | fill=self.sett.grid_color) 68 | 69 | (statusbar_frame := ttk.Frame(self, relief="raised")).pack(ipady=2, fill="x") 70 | self._info_text = tk.StringVar() 71 | ttk.Label(statusbar_frame, textvariable=self._info_text, foreground="blue").pack(side="left", padx=4) 72 | self._rowcol_text = tk.StringVar() 73 | ttk.Label(statusbar_frame, textvariable=self._rowcol_text, foreground="blue").pack(side="right", padx=4) 74 | 75 | self._canvas.bind("", self._on_mouse_move) 76 | self._canvas.bind("", self._on_mouse_click) 77 | 78 | # Ket thuc cung goi _start de bat dau lai 79 | def _start(self, info_text): 80 | self._playing = False 81 | self._canvas.delete(self._cur_cell_id) 82 | self._info_text.set(info_text) 83 | self._rowcol_text.set("") 84 | self._save_button.configure(state="disabled") 85 | self._undo_button.configure(state="disabled") 86 | self._redo_button.configure(state="disabled") 87 | 88 | def _start_game(self, info, cur_cell): 89 | self._canvas.delete(self._cur_cell_id) 90 | for move_id in self._moves_id: 91 | self._canvas.delete(move_id) 92 | for cell_id in self._cell_line_id: 93 | self._canvas.delete(cell_id) 94 | 95 | self._info = info 96 | self._redo_list = [] 97 | self._cells = [[None for col in range(self.sett.width)] for row in range(self.sett.height)] 98 | self._moves_id = [] 99 | for (isO, (row, col)) in info["moves"]: 100 | self._cells[row][col] = isO 101 | if isO: 102 | self._moves_id.append(self._canvas.create_text(self._get_canvas_coord(row, col, True), 103 | fill=self.sett.O_color, text=self.sett.O_char, font=self.sett.XO_font)) 104 | else: 105 | self._moves_id.append(self._canvas.create_text(self._get_canvas_coord(row, col, True), 106 | fill=self.sett.X_color, text=self.sett.X_char, font=self.sett.XO_font)) 107 | self._cur_cell_id = self._canvas.create_rectangle(self._get_canvas_coord(*cur_cell), 108 | self._get_canvas_coord(cur_cell[0] + 1, cur_cell[1] + 1), 109 | outline=self.sett.highlight_color, width=3) 110 | self._set_turn(self._info["O_turn"]) 111 | self._playing = True 112 | self._save_button.configure(state="normal") 113 | self._undo_button.configure(state="normal" if self._info["moves"] else "disabled") 114 | self._redo_button.configure(state="disabled") 115 | 116 | def _get_canvas_coord(self, row, col, center=False): 117 | center_w = self.sett.cell_width//2 if center else 0 118 | return self.sett.padx + col*self.sett.cell_width + center_w, \ 119 | self.sett.pady + row*self.sett.cell_width + center_w 120 | 121 | def _get_cell_coord(self, x, y): 122 | col = (x - self.sett.padx) // self.sett.cell_width 123 | row = (y - self.sett.pady) // self.sett.cell_width 124 | if row in range(self.sett.height) and col in range(self.sett.width): 125 | return row, col 126 | else: 127 | return None 128 | 129 | def _set_turn(self, O_turn): 130 | self._info["O_turn"] = O_turn 131 | if self._info["O_turn"]: 132 | self._info_text.set(self._info["O_name"] + "'s turn") 133 | else: 134 | self._info_text.set(self._info["X_name"] + "'s turn") 135 | 136 | def _create_XO(self, cell, isO): 137 | if isO: 138 | return self._canvas.create_text(self._get_canvas_coord(cell[0], cell[1], True), 139 | fill=self.sett.O_color, text=self.sett.O_char, font=self.sett.XO_font) 140 | else: 141 | return self._canvas.create_text(self._get_canvas_coord(cell[0], cell[1], True), 142 | fill=self.sett.X_color, text=self.sett.X_char, font=self.sett.XO_font) 143 | 144 | def _check_win(self, cell, turn): 145 | if len(cell_line := self._get_cell_line(cell, turn, 1, 0)) >= 5: # hang ngang - 146 | return cell_line 147 | if len(cell_line := self._get_cell_line(cell, turn, 0, 1)) >= 5: # hang doc | 148 | return cell_line 149 | if len(cell_line := self._get_cell_line(cell, turn, 1, 1)) >= 5: # hang cheo \ 150 | return cell_line 151 | if len(cell_line := self._get_cell_line(cell, turn, 1, -1)) >= 5: # hang cheo / 152 | return cell_line 153 | return None 154 | 155 | def _get_cell_line(self, cell, turn, dx, dy): 156 | cell_line = [] 157 | row, col = cell 158 | while row in range(self.sett.height) and col in range(self.sett.width) \ 159 | and self._cells[row][col] == turn: 160 | cell_line.append((row, col)) 161 | row, col = row + dy, col + dx 162 | row, col = cell[0] - dy, cell[1] - dx 163 | while row in range(self.sett.height) and col in range(self.sett.width) \ 164 | and self._cells[row][col] == turn: 165 | cell_line.append((row, col)) 166 | row, col = row - dy, col - dx 167 | return cell_line 168 | 169 | def _on_new(self): 170 | (new_dialog := caro_dialog.NewDialog(self, "New Game", 84, 200)).do_modal() 171 | if new_dialog.is_ok(): 172 | if new_dialog.get_mode() == caro_dialog.NewDialog.MAN_VS_MAN: 173 | info = {} 174 | info["O_turn"] = new_dialog.is_O_first() 175 | info["O_name"] = new_dialog.get_1st_name() if new_dialog.is_O_first() else new_dialog.get_2nd_name() 176 | info["X_name"] = new_dialog.get_2nd_name() if new_dialog.is_O_first() else new_dialog.get_1st_name() 177 | info["moves"] = [] 178 | self._start_game(info, (self.sett.width//2, self.sett.height//2)) 179 | else: 180 | messagebox.showwarning(self.sett.title, "The chosen game mode is currently not available!") 181 | 182 | def _on_open(self): 183 | if (file := filedialog.askopenfile(parent=self, title="Open game from a JSON file", 184 | filetypes=[("JSON File", "*.json")])): 185 | try: 186 | with file: 187 | info = json.load(file) 188 | if not set(["O_turn", "O_name", "X_name", "moves"]) <= set(info.keys()): 189 | raise Exception 190 | if info["moves"]: 191 | self._start_game(info, info["moves"][-1][1]) 192 | else: 193 | self._start_game(info, (self.sett.width//2, self.sett.height//2)) 194 | except: 195 | messagebox.showerror(self.sett.title, "Can not open the file!") 196 | 197 | def _on_save(self): 198 | if (file := filedialog.asksaveasfile(parent=self, title="Save game to a JSON file", 199 | filetypes=[("JSON File", "*.json")])): 200 | try: 201 | with file: 202 | json.dump(self._info, file) 203 | except: 204 | messagebox.showerror(self.sett.title, "Can not save the file!") 205 | 206 | def _on_settings(self): 207 | color_dict = {} 208 | color_dict["back_color"] = {"name": "Background", "color": self.sett.back_color} 209 | color_dict["highlight_color"] = {"name": "Highlight", "color": self.sett.highlight_color} 210 | color_dict["X_color"] = {"name": "X's", "color": self.sett.X_color} 211 | color_dict["O_color"] = {"name": "O's", "color": self.sett.O_color} 212 | (settings_dialog := caro_dialog.SettingsDialog(self, "Settings", 160, 200, color_dict)).do_modal() 213 | if settings_dialog.is_ok(): 214 | color_dict = settings_dialog.get_color_dict() 215 | self.sett.back_color = color_dict["back_color"]["color"] 216 | self._canvas.configure(background=self.sett.back_color) 217 | self.sett.highlight_color = color_dict["highlight_color"]["color"] 218 | self._canvas.itemconfig(self._cur_cell_id, outline=self.sett.highlight_color) 219 | self.sett.X_color = color_dict["X_color"]["color"] 220 | self.sett.O_color = color_dict["O_color"]["color"] 221 | for index, XO_id in enumerate(self._moves_id): 222 | self._canvas.itemconfig(XO_id, fill=self.sett.O_color \ 223 | if self._info["moves"][index][0] else self.sett.X_color) 224 | 225 | def _on_undo(self): 226 | if self._info["moves"]: 227 | turn, cell = self._info["moves"].pop() 228 | self._redo_list.append((turn, cell)) 229 | self._cells[cell[0]][cell[1]] = None 230 | self._set_turn(not self._info["O_turn"]) 231 | self._canvas.delete(self._moves_id.pop()) 232 | if self._info["moves"]: 233 | cell = self._info["moves"][-1][1] 234 | self._canvas.coords(self._cur_cell_id, *self._get_canvas_coord(*cell), 235 | *self._get_canvas_coord(cell[0] + 1, cell[1] + 1)) 236 | else: 237 | self._undo_button.configure(state="disabled") 238 | self._canvas.itemconfigure(self._cur_cell_id, state="hidden") 239 | 240 | self._redo_button.configure(state="normal") 241 | 242 | def _on_redo(self): 243 | if self._redo_list: 244 | turn, cell = self._redo_list.pop() 245 | self._info["moves"].append((turn, cell)) 246 | self._cells[cell[0]][cell[1]] = turn 247 | self._set_turn(not self._info["O_turn"]) 248 | self._moves_id.append(self._create_XO(cell, turn)) 249 | if not self._redo_list: 250 | self._redo_button.configure(state="disabled") 251 | self._canvas.coords(self._cur_cell_id, *self._get_canvas_coord(*cell), 252 | *self._get_canvas_coord(cell[0] + 1, cell[1] + 1)) 253 | self._canvas.itemconfigure(self._cur_cell_id, state="normal") 254 | self._undo_button.configure(state="normal") 255 | 256 | def _on_about(self): 257 | messagebox.showinfo(title="About " + self.sett.title, message=self.sett.title + " Application", 258 | detail="Version: 0.0.1\nAuthor: Vũ Quốc Hoàng\nE-mail: vqhoang@fit.hcmus.edu.vn") 259 | 260 | def _on_mouse_move(self, event): 261 | if self._playing and (cell := self._get_cell_coord(event.x, event.y)): 262 | # danh row, col theo kieu Excel A2 (row 2, col 1) 263 | if cell[1] in range(len(string.ascii_uppercase)): 264 | self._rowcol_text.set(f"{string.ascii_uppercase[cell[1]]}{cell[0] + 1}") 265 | self._canvas.coords(self._cur_cell_id, *self._get_canvas_coord(*cell), 266 | *self._get_canvas_coord(cell[0] + 1, cell[1] + 1)) 267 | if self._cells[cell[0]][cell[1]] == None: 268 | self._canvas.itemconfigure(self._cur_cell_id, state="normal") 269 | else: 270 | self._canvas.itemconfigure(self._cur_cell_id, state="hidden") 271 | 272 | def _on_mouse_click(self, event): 273 | if self._playing and (cell := self._get_cell_coord(event.x, event.y)) \ 274 | and self._cells[cell[0]][cell[1]] == None: 275 | self._redo_list = [] # xoa redo 276 | self._redo_button.configure(state="disabled") 277 | self._undo_button.configure(state="normal") 278 | 279 | self._cells[cell[0]][cell[1]] = self._info["O_turn"] 280 | self._info["moves"].append((self._info["O_turn"], cell)) 281 | self._moves_id.append(self._create_XO(cell, self._info["O_turn"])) 282 | self._set_turn(not self._info["O_turn"]) 283 | 284 | if cell_line := self._check_win(cell, turn := not self._info["O_turn"]): 285 | winner_name = self._info["O_name"] if turn else self._info["X_name"] 286 | winner_char = self.sett.O_char if turn else self.sett.X_char 287 | winner_color = self.sett.O_color if turn else self.sett.X_color 288 | for cell in cell_line: 289 | self._cell_line_id.append(self._canvas.create_rectangle(self._get_canvas_coord(*cell), 290 | self._get_canvas_coord(cell[0] + 1, cell[1] + 1), 291 | outline=winner_color, width=2)) 292 | messagebox.showinfo(self.sett.title, f"{winner_name} wins!") 293 | self._start(f"{winner_char} wins! Press New to play new game or Open to open an existing game") 294 | elif len(self._info["moves"]) == self.sett.width*self.sett.height: # draw 295 | messagebox.showinfo(self.sett.title, "Draw!") 296 | self._start("Draw! Press New to play new game or Open to open an existing game") 297 | 298 | 299 | if __name__ == "__main__": 300 | app = CaroGame() 301 | app.mainloop() 302 | -------------------------------------------------------------------------------- /lesson19/caro_game/caro_game_grid.py: -------------------------------------------------------------------------------- 1 | import string, json 2 | import tkinter as tk 3 | from tkinter import ttk, messagebox, filedialog 4 | import caro_dialog 5 | 6 | class Settings: 7 | def __init__(self): 8 | # https://www.rapidtables.com/web/color/gray-color.html 9 | self.back_color = "#FFC0CB" # Pink 10 | self.grid_color = "#A9A9A9" # Dark Gray 11 | self.highlight_color = "#FFFF00" # Yellow 12 | self.X_color = "#FF0000" # Red 13 | self.O_color = "#0000FF" # Blue 14 | 15 | self.title = "Caro Game" 16 | self.cell_width = 25 # pixels 25 17 | self.width, self.height = 20, 20 # cells 20, 20 18 | self.padx, self.pady = 0, 0 # pixel each margin 5, 5 19 | self.X_char, self.O_char = "X", "O" 20 | self.XO_font = ("Arial bold", self.cell_width - 6) 21 | 22 | # https://www.iconfinder.com/iconsets/32x32-free-design-icons 23 | self.app_icon = tk.PhotoImage(file="caro_icon.png") 24 | self.new_icon = tk.PhotoImage(file="new_icon.png") 25 | self.open_icon = tk.PhotoImage(file="open_icon.png") 26 | self.save_icon = tk.PhotoImage(file="save_icon.png") 27 | self.undo_icon = tk.PhotoImage(file="undo_icon.png") 28 | self.redo_icon = tk.PhotoImage(file="redo_icon.png") 29 | self.settings_icon = tk.PhotoImage(file="settings_icon.png") 30 | 31 | class CaroGame(tk.Tk): 32 | def __init__(self, *args, **kwargs): 33 | super().__init__(*args, **kwargs) 34 | self._cur_cell_id = None 35 | self._moves_id = [] 36 | self._cell_line_id = [] 37 | self.sett = Settings() 38 | self.title(self.sett.title) 39 | self.resizable(False, False) 40 | self.iconphoto(True, self.sett.app_icon) 41 | 42 | self._initialize() 43 | self._start("Press New to play new game or Open to open an existing game") 44 | 45 | def _initialize(self): 46 | (toolbar_frame := ttk.Frame(self, relief="raised")).grid(ipady=2, sticky="EW") 47 | ttk.Button(toolbar_frame, image=self.sett.new_icon, command=self._on_new).pack(side="left") 48 | ttk.Button(toolbar_frame, image=self.sett.open_icon, command=self._on_open).pack(side="left") 49 | self._save_button = ttk.Button(toolbar_frame, image=self.sett.save_icon, command=self._on_save) 50 | self._save_button.pack(side="left") 51 | ttk.Button(toolbar_frame, image=self.sett.settings_icon, command=self._on_settings).pack(side="left", padx=10) 52 | self._undo_button = ttk.Button(toolbar_frame, image=self.sett.undo_icon, command=self._on_undo) 53 | self._undo_button.pack(side="left") 54 | self._redo_button = ttk.Button(toolbar_frame, image=self.sett.redo_icon, command=self._on_redo) 55 | self._redo_button.pack(side="left") 56 | 57 | self._canvas = tk.Canvas(self, background=self.sett.back_color, width=self.sett.width*self.sett.cell_width + 2*self.sett.padx, 58 | height=self.sett.height*self.sett.cell_width + 2*self.sett.pady) 59 | self._canvas.grid(row=1) 60 | 61 | for row in range(self.sett.height + 1): 62 | self._canvas.create_line(self._get_canvas_coord(row, 0), self._get_canvas_coord(row, self.sett.width), 63 | fill=self.sett.grid_color) 64 | for col in range(self.sett.width + 1): 65 | self._canvas.create_line(self._get_canvas_coord(0, col), self._get_canvas_coord(self.sett.height, col), 66 | fill=self.sett.grid_color) 67 | 68 | (statusbar_frame := ttk.Frame(self, relief="raised")).grid(row=2, sticky="EW", ipady=4) 69 | self._info_text = tk.StringVar() 70 | ttk.Label(statusbar_frame, textvariable=self._info_text, foreground="blue").pack(side="left", padx=4) 71 | self._rowcol_text = tk.StringVar() 72 | ttk.Label(statusbar_frame, textvariable=self._rowcol_text, foreground="blue").pack(side="right", padx=4) 73 | 74 | self._canvas.bind("", self._on_mouse_move) 75 | self._canvas.bind("", self._on_mouse_click) 76 | 77 | # Ket thuc cung goi _start de bat dau lai 78 | def _start(self, info_text): 79 | self._playing = False 80 | self._canvas.delete(self._cur_cell_id) 81 | self._info_text.set(info_text) 82 | self._rowcol_text.set("") 83 | self._save_button.configure(state="disabled") 84 | self._undo_button.configure(state="disabled") 85 | self._redo_button.configure(state="disabled") 86 | 87 | def _start_game(self, info, cur_cell): 88 | self._canvas.delete(self._cur_cell_id) 89 | for move_id in self._moves_id: 90 | self._canvas.delete(move_id) 91 | for cell_id in self._cell_line_id: 92 | self._canvas.delete(cell_id) 93 | 94 | self._info = info 95 | self._redo_list = [] 96 | self._cells = [[None for col in range(self.sett.width)] for row in range(self.sett.height)] 97 | self._moves_id = [] 98 | for (isO, (row, col)) in info["moves"]: 99 | self._cells[row][col] = isO 100 | if isO: 101 | self._moves_id.append(self._canvas.create_text(self._get_canvas_coord(row, col, True), 102 | fill=self.sett.O_color, text=self.sett.O_char, font=self.sett.XO_font)) 103 | else: 104 | self._moves_id.append(self._canvas.create_text(self._get_canvas_coord(row, col, True), 105 | fill=self.sett.X_color, text=self.sett.X_char, font=self.sett.XO_font)) 106 | self._cur_cell_id = self._canvas.create_rectangle(self._get_canvas_coord(*cur_cell), 107 | self._get_canvas_coord(cur_cell[0] + 1, cur_cell[1] + 1), 108 | outline=self.sett.highlight_color, width=3) 109 | self._set_turn(self._info["O_turn"]) 110 | self._playing = True 111 | self._save_button.configure(state="normal") 112 | self._undo_button.configure(state="normal" if self._info["moves"] else "disabled") 113 | self._redo_button.configure(state="disabled") 114 | 115 | def _get_canvas_coord(self, row, col, center=False): 116 | center_w = self.sett.cell_width//2 if center else 0 117 | return self.sett.padx + col*self.sett.cell_width + center_w, \ 118 | self.sett.pady + row*self.sett.cell_width + center_w 119 | 120 | def _get_cell_coord(self, x, y): 121 | col = (x - self.sett.padx) // self.sett.cell_width 122 | row = (y - self.sett.pady) // self.sett.cell_width 123 | if row in range(self.sett.height) and col in range(self.sett.width): 124 | return row, col 125 | else: 126 | return None 127 | 128 | def _set_turn(self, O_turn): 129 | self._info["O_turn"] = O_turn 130 | if self._info["O_turn"]: 131 | self._info_text.set(self._info["O_name"] + "'s turn") 132 | else: 133 | self._info_text.set(self._info["X_name"] + "'s turn") 134 | 135 | def _create_XO(self, cell, isO): 136 | if isO: 137 | return self._canvas.create_text(self._get_canvas_coord(cell[0], cell[1], True), 138 | fill=self.sett.O_color, text=self.sett.O_char, font=self.sett.XO_font) 139 | else: 140 | return self._canvas.create_text(self._get_canvas_coord(cell[0], cell[1], True), 141 | fill=self.sett.X_color, text=self.sett.X_char, font=self.sett.XO_font) 142 | 143 | def _on_new(self): 144 | (new_dialog := caro_dialog.NewDialog(self, "New Game", 84, 200)).do_modal() 145 | if new_dialog.is_ok(): 146 | if new_dialog.get_mode() == caro_dialog.NewDialog.MAN_VS_MAN: 147 | info = {} 148 | info["O_turn"] = new_dialog.is_O_first() 149 | info["O_name"] = new_dialog.get_1st_name() if new_dialog.is_O_first() else new_dialog.get_2nd_name() 150 | info["X_name"] = new_dialog.get_2nd_name() if new_dialog.is_O_first() else new_dialog.get_1st_name() 151 | info["moves"] = [] 152 | self._start_game(info, (self.sett.width//2, self.sett.height//2)) 153 | else: 154 | messagebox.showwarning(self.sett.title, "The chosen game mode is currently not available!") 155 | 156 | def _on_open(self): 157 | if (file := filedialog.askopenfile(parent=self, title="Open game from a JSON file", 158 | filetypes=[("JSON File", "*.json")])): 159 | try: 160 | with file: 161 | info = json.load(file) 162 | if not set(["O_turn", "O_name", "X_name", "moves"]) <= set(info.keys()): 163 | raise Exception 164 | if info["moves"]: 165 | self._start_game(info, info["moves"][-1][1]) 166 | else: 167 | self._start_game(info, (self.sett.width//2, self.sett.height//2)) 168 | except: 169 | messagebox.showerror(self.sett.title, "Can not open the file!") 170 | 171 | def _on_save(self): 172 | if (file := filedialog.asksaveasfile(parent=self, title="Save game to a JSON file", 173 | filetypes=[("JSON File", "*.json")])): 174 | try: 175 | with file: 176 | json.dump(self._info, file) 177 | except: 178 | messagebox.showerror(self.sett.title, "Can not save the file!") 179 | 180 | def _on_settings(self): 181 | color_dict = {} 182 | color_dict["back_color"] = {"name": "Background", "color": self.sett.back_color} 183 | color_dict["highlight_color"] = {"name": "Highlight", "color": self.sett.highlight_color} 184 | color_dict["X_color"] = {"name": "X's", "color": self.sett.X_color} 185 | color_dict["O_color"] = {"name": "O's", "color": self.sett.O_color} 186 | (settings_dialog := caro_dialog.SettingsDialog(self, "Settings", 160, 200, color_dict)).do_modal() 187 | if settings_dialog.is_ok(): 188 | color_dict = settings_dialog.get_color_dict() 189 | self.sett.back_color = color_dict["back_color"]["color"] 190 | self._canvas.configure(background=self.sett.back_color) 191 | self.sett.highlight_color = color_dict["highlight_color"]["color"] 192 | self._canvas.itemconfig(self._cur_cell_id, outline=self.sett.highlight_color) 193 | self.sett.X_color = color_dict["X_color"]["color"] 194 | self.sett.O_color = color_dict["O_color"]["color"] 195 | for index, XO_id in enumerate(self._moves_id): 196 | self._canvas.itemconfig(XO_id, fill=self.sett.O_color \ 197 | if self._info["moves"][index][0] else self.sett.X_color) 198 | 199 | def _on_undo(self): 200 | if self._info["moves"]: 201 | turn, cell = self._info["moves"].pop() 202 | self._redo_list.append((turn, cell)) 203 | self._cells[cell[0]][cell[1]] = None 204 | self._set_turn(not self._info["O_turn"]) 205 | self._canvas.delete(self._moves_id.pop()) 206 | if self._info["moves"]: 207 | cell = self._info["moves"][-1][1] 208 | self._canvas.coords(self._cur_cell_id, *self._get_canvas_coord(*cell), 209 | *self._get_canvas_coord(cell[0] + 1, cell[1] + 1)) 210 | else: 211 | self._undo_button.configure(state="disabled") 212 | self._canvas.itemconfigure(self._cur_cell_id, state="hidden") 213 | 214 | self._redo_button.configure(state="normal") 215 | 216 | def _on_redo(self): 217 | if self._redo_list: 218 | turn, cell = self._redo_list.pop() 219 | self._info["moves"].append((turn, cell)) 220 | self._cells[cell[0]][cell[1]] = turn 221 | self._set_turn(not self._info["O_turn"]) 222 | self._moves_id.append(self._create_XO(cell, turn)) 223 | if not self._redo_list: 224 | self._redo_button.configure(state="disabled") 225 | self._canvas.coords(self._cur_cell_id, *self._get_canvas_coord(*cell), 226 | *self._get_canvas_coord(cell[0] + 1, cell[1] + 1)) 227 | self._canvas.itemconfigure(self._cur_cell_id, state="normal") 228 | self._undo_button.configure(state="normal") 229 | 230 | def _on_mouse_move(self, event): 231 | if self._playing and (cell := self._get_cell_coord(event.x, event.y)): 232 | # danh row, col theo kieu Excel A2 (row 2, col 1) 233 | if cell[1] in range(len(string.ascii_uppercase)): 234 | self._rowcol_text.set(f"{string.ascii_uppercase[cell[1]]}{cell[0] + 1}") 235 | self._canvas.coords(self._cur_cell_id, *self._get_canvas_coord(*cell), 236 | *self._get_canvas_coord(cell[0] + 1, cell[1] + 1)) 237 | if self._cells[cell[0]][cell[1]] == None: 238 | self._canvas.itemconfigure(self._cur_cell_id, state="normal") 239 | else: 240 | self._canvas.itemconfigure(self._cur_cell_id, state="hidden") 241 | 242 | def _on_mouse_click(self, event): 243 | if self._playing and (cell := self._get_cell_coord(event.x, event.y)) \ 244 | and self._cells[cell[0]][cell[1]] == None: 245 | self._redo_list = [] # xoa redo 246 | self._redo_button.configure(state="disabled") 247 | self._undo_button.configure(state="normal") 248 | 249 | self._cells[cell[0]][cell[1]] = self._info["O_turn"] 250 | self._info["moves"].append((self._info["O_turn"], cell)) 251 | self._moves_id.append(self._create_XO(cell, self._info["O_turn"])) 252 | self._set_turn(not self._info["O_turn"]) 253 | 254 | if cell_line := self._check_win(cell, turn := not self._info["O_turn"]): 255 | winner_name = self._info["O_name"] if turn else self._info["X_name"] 256 | winner_char = self.sett.O_char if turn else self.sett.X_char 257 | winner_color = self.sett.O_color if turn else self.sett.X_color 258 | for cell in cell_line: 259 | self._cell_line_id.append(self._canvas.create_rectangle(self._get_canvas_coord(*cell), 260 | self._get_canvas_coord(cell[0] + 1, cell[1] + 1), 261 | outline=winner_color, width=2)) 262 | messagebox.showinfo(self.sett.title, f"{winner_name} wins!") 263 | self._start(f"{winner_char} wins! Press New to play new game or Open to open an existing game") 264 | elif len(self._info["moves"]) == self.sett.width*self.sett.height: # draw 265 | messagebox.showinfo(self.sett.title, "Draw!") 266 | self._start("Draw! Press New to play new game or Open to open an existing game") 267 | 268 | def _check_win(self, cell, turn): 269 | if len(cell_line := self._get_cell_line(cell, turn, 1, 0)) >= 5: # hang ngang - 270 | return cell_line 271 | if len(cell_line := self._get_cell_line(cell, turn, 0, 1)) >= 5: # hang doc | 272 | return cell_line 273 | if len(cell_line := self._get_cell_line(cell, turn, 1, 1)) >= 5: # hang cheo \ 274 | return cell_line 275 | if len(cell_line := self._get_cell_line(cell, turn, 1, -1)) >= 5: # hang cheo / 276 | return cell_line 277 | return None 278 | 279 | def _get_cell_line(self, cell, turn, dx, dy): 280 | cell_line = [] 281 | row, col = cell 282 | while row in range(self.sett.height) and col in range(self.sett.width) \ 283 | and self._cells[row][col] == turn: 284 | cell_line.append((row, col)) 285 | row, col = row + dy, col + dx 286 | row, col = cell[0] - dy, cell[1] - dx 287 | while row in range(self.sett.height) and col in range(self.sett.width) \ 288 | and self._cells[row][col] == turn: 289 | cell_line.append((row, col)) 290 | row, col = row - dy, col - dx 291 | return cell_line 292 | 293 | 294 | if __name__ == "__main__": 295 | app = CaroGame() 296 | app.mainloop() 297 | -------------------------------------------------------------------------------- /lesson19/caro_game/caro_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson19/caro_game/caro_icon.png -------------------------------------------------------------------------------- /lesson19/caro_game/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson19/caro_game/new_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson19/caro_game/new_icon.png -------------------------------------------------------------------------------- /lesson19/caro_game/open_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson19/caro_game/open_icon.png -------------------------------------------------------------------------------- /lesson19/caro_game/redo_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson19/caro_game/redo_icon.png -------------------------------------------------------------------------------- /lesson19/caro_game/save_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson19/caro_game/save_icon.png -------------------------------------------------------------------------------- /lesson19/caro_game/settings_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson19/caro_game/settings_icon.png -------------------------------------------------------------------------------- /lesson19/caro_game/undo_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/lesson19/caro_game/undo_icon.png -------------------------------------------------------------------------------- /lesson19/factorial_GUI.py: -------------------------------------------------------------------------------- 1 | import math 2 | import tkinter as tk 3 | from tkinter import ttk 4 | 5 | def on_calculate(): 6 | try: 7 | fact = math.factorial(num.get()) 8 | except: 9 | num.set(0) 10 | fact = 1 11 | finally: 12 | fact_label.configure(text=str(fact)) 13 | num_entry.focus() 14 | 15 | win = tk.Tk() 16 | win.title("Calculating Factorial") 17 | win.resizable(False, False) 18 | 19 | num = tk.IntVar() 20 | 21 | ttk.Label(win, text="n = ").grid(row=0, column=0, sticky="E") 22 | (num_entry := ttk.Entry(win, textvariable=num)).grid(row=0, column=1, sticky="W") 23 | ttk.Label(win, text="n! = ").grid(row=1, column=0, sticky="E") 24 | (fact_label := ttk.Label(win, width=40, text="1", foreground="red")).grid(row=1, column=1, sticky="W") 25 | ttk.Button(win, text="Calculate", command=on_calculate).grid(row=2, column=0, sticky="E") 26 | ttk.Button(win, text="Exit", command=win.destroy).grid(row=2, column=1, sticky="W") 27 | 28 | win.bind('', lambda _: on_calculate()) 29 | win.bind('', lambda _: win.destroy()) 30 | 31 | for child in win.winfo_children(): 32 | child.grid(padx=4, pady=4) 33 | 34 | num_entry.focus() 35 | 36 | win.mainloop() 37 | -------------------------------------------------------------------------------- /lesson19/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson19/student_management/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lesson19/student_management/student_data_model.py: -------------------------------------------------------------------------------- 1 | import csv 2 | 3 | def open_student_file(file_name, field_names): 4 | try: 5 | with open(file_name, mode='r', encoding="utf-8", newline="") as csv_file: 6 | reader = csv.DictReader(csv_file) 7 | students = list(reader) 8 | # kiểm tra có chứa các field_names 9 | if len(students) > 0 and (set(field_names) <= set(students[0])): 10 | return students 11 | except: 12 | return None 13 | 14 | def save_student_file(file_name, field_names, students): 15 | try: 16 | with open(file_name, mode='w', encoding="utf-8", newline="") as csv_file: 17 | writer = csv.DictWriter(csv_file, field_names) 18 | writer.writeheader() 19 | for student in students: 20 | writer.writerow(student) 21 | return True 22 | except: 23 | return False 24 | -------------------------------------------------------------------------------- /lesson19/student_management/student_management.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk, messagebox, filedialog 3 | 4 | import student_widgets as sw 5 | import student_data_model as sdm 6 | 7 | class Application(tk.Tk): 8 | _TITLE = "Students Management" 9 | _COLUMNS = { 10 | "sid": {"text": "MSSV"}, 11 | "name": {"text": "Họ tên", "anchor": "w", "stretch": True, "width": 200, "minwidth": 150}, 12 | "gender": {"text": "Giới tính"}, 13 | "grade": {"text": "Điểm", "anchor": "e", "numeric": True}, 14 | } 15 | 16 | def __init__(self, title=_TITLE, *args, **kwargs): 17 | super().__init__(*args, **kwargs) 18 | self.title(title) 19 | self.geometry("500x590") 20 | self._has_change = False # Cho biết có thay đổi (để báo khi New, Save) 21 | 22 | self._create_widgets() 23 | 24 | self._on_new() 25 | 26 | def _create_widgets(self): 27 | self._slist_view = sw.StudentsListView(self, text="Students List", COLUMNS=Application._COLUMNS, 28 | student_edit_handler=self._on_student_edit, sid_list_change_handler=self._on_sid_list_change, 29 | student_delete_handler=self._on_student_delete) 30 | self._slist_view.grid(row=0, column=0, sticky="WENS") 31 | 32 | self._sadd_panel = sw.StudentAddUpdatePanel(self, text="Add/Edit Student", sid_change_handler=self._on_sid_change, 33 | student_add_or_update_handler=self._on_student_add_or_update, error_handler=self._on_error_handler) 34 | self._sadd_panel.grid(row=1, column=0, sticky="EW") 35 | 36 | self._status_bar = sw.StatusBar(self) 37 | self._status_bar.grid(row=2, column=0, sticky="EW") 38 | 39 | self.columnconfigure(0, weight=1) 40 | self.rowconfigure(0, weight=1) 41 | 42 | for child in self.winfo_children(): 43 | child.grid(padx=4, pady=2) 44 | 45 | self._create_menu() 46 | 47 | def _create_menu(self): 48 | main_menu = tk.Menu(self) 49 | self.configure(menu=main_menu) 50 | 51 | self._file_menu = tk.Menu(main_menu, tearoff=False) 52 | self._file_menu.add_command(label="New", accelerator="Ctrl+N", command=self._on_new) 53 | self._file_menu.add_command(label="Open...", accelerator="Ctrl+O", command=self._on_open) 54 | self._file_menu.add_command(label="Save As...", accelerator="Ctrl+S", command=self._on_save_as) 55 | self._file_menu.add_separator() 56 | self._file_menu.add_command(label="Exit", accelerator="Alt+F4", command=self._on_exit) 57 | 58 | help_menu = tk.Menu(main_menu, tearoff=False) 59 | help_menu.add_command(label = "About...", accelerator="F1", command=self._on_about) 60 | 61 | main_menu.add_cascade(label="File", menu=self._file_menu) 62 | main_menu.add_cascade(label="Help", menu=help_menu) 63 | 64 | self.bind("", self._on_new) 65 | self.bind("", self._on_new) 66 | self.bind("", self._on_open) 67 | self.bind("", self._on_open) 68 | self.bind("", self._on_about) 69 | 70 | self._set_save_as_state(False) 71 | 72 | self.protocol("WM_DELETE_WINDOW", self._on_exit) 73 | 74 | def _ask_save_changes(self): 75 | if self._has_change and self._slist_view.get_students_len() > 0: 76 | if (answer := messagebox.askyesnocancel(Application._TITLE, "Do you want to save changes?")) is None: 77 | return True 78 | 79 | if answer and not self._on_save_as(): 80 | return True 81 | return False 82 | 83 | # Tao moi (xoa), hoi luu khi co thay doi 84 | def _on_new(self, event=None): 85 | if self._ask_save_changes(): 86 | return 87 | 88 | self._slist_view.clear() 89 | self._sadd_panel.clear() 90 | self._status_bar.set_text("Enter new data and/or open data from CSV file.") 91 | self._has_change = False 92 | 93 | def _on_open(self, event=None): 94 | if self._ask_save_changes(): 95 | return 96 | 97 | if not (file_name := filedialog.askopenfilename(parent=self, title="Open a CSV student file", 98 | filetypes=[("CSV File", "*.csv")])): 99 | return 100 | 101 | if (students := sdm.open_student_file(file_name, list(Application._COLUMNS))): 102 | if self._slist_view.get_students_len() > 0: 103 | if messagebox.askyesno(Application._TITLE, "Do you want to clear current student list?"): 104 | self._slist_view.clear() 105 | self._has_change = False 106 | else: 107 | self._has_change = True 108 | self._sadd_panel.clear() 109 | self._slist_view.insert_or_replace(students) 110 | self._status_bar.set_text(f"Open {self._slist_view.get_students_len()} student(s) successfully.") 111 | else: 112 | self._status_bar.set_text_error("Can not open the file or the file is invalid!") 113 | 114 | def _on_save_as(self, event=None): 115 | if not (file_name := filedialog.asksaveasfilename(parent=self, title="Save to a CSV student file", 116 | filetypes=[("CSV File", "*.csv")])): 117 | return False 118 | 119 | if sdm.save_student_file(file_name, list(Application._COLUMNS), self._slist_view.get_students()): 120 | self._status_bar.set_text(f"Save {self._slist_view.get_students_len()} student(s) successfully.") 121 | self._has_change = False 122 | return True 123 | self._status_bar.set_text_error("Can not save data to the file!") 124 | return False 125 | 126 | def _on_exit(self, event=None): 127 | if self._ask_save_changes(): 128 | return 129 | self.destroy() 130 | 131 | def _on_about(self, event=None): 132 | messagebox.showinfo(title="About " + Application._TITLE, message=Application._TITLE + " Application", 133 | detail="Version: 0.0.1\nAuthor: Vũ Quốc Hoàng\nE-mail: vqhoang@fit.hcmus.edu.vn") 134 | 135 | def _set_save_as_state(self, normal): 136 | if normal: 137 | self._file_menu.entryconfig(2, state="normal") 138 | self.bind("", self._on_save_as) 139 | self.bind("", self._on_save_as) 140 | else: 141 | self._file_menu.entryconfig(2, state="disabled") 142 | self.unbind("") 143 | self.unbind("") 144 | 145 | def _on_student_edit(self, student): 146 | student["gender"] = True if student["gender"] == "Nam" else False 147 | self._sadd_panel.set_student(student) 148 | 149 | def _on_student_delete(self, delete_sids): 150 | self._status_bar.set_text(f"Delete {len(delete_sids)} student(s) successfully.") 151 | self._has_change = True 152 | 153 | def _on_sid_list_change(self, sid_list): 154 | self._sadd_panel.set_sid_list(sid_list) 155 | self._set_save_as_state(bool(sid_list)) 156 | 157 | def _on_sid_change(self, sid): 158 | self._slist_view.choose(sid) 159 | 160 | def _on_student_add_or_update(self, student, added): 161 | student["gender"] = "Nam" if student["gender"] else "Nữ" 162 | self._slist_view.insert_or_replace([student]) 163 | self._slist_view.choose(student["sid"]) 164 | if added: 165 | self._status_bar.set_text("Add 1 student successfully.") 166 | else: 167 | self._status_bar.set_text("Update 1 student successfully.") 168 | self._has_change = True 169 | 170 | def _on_error_handler(self, error_message): 171 | self._status_bar.set_text_error(error_message) 172 | 173 | if __name__ == "__main__": 174 | app = Application() 175 | app.mainloop() 176 | -------------------------------------------------------------------------------- /lesson19/student_management/student_widgets.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from tkinter import ttk 3 | 4 | class StatusBar(ttk.Frame): 5 | def __init__(self, parent, *args, **kwargs): 6 | super().__init__(parent, *args, **kwargs) 7 | self.configure(relief="groove") 8 | 9 | self._create_widgets() 10 | 11 | def _create_widgets(self): 12 | self._status_label = ttk.Label(self, text="", foreground="blue") 13 | self._status_label.grid(padx=2, pady=2, sticky="W") 14 | 15 | def set_text(self, text): 16 | self._status_label.configure(text=text, foreground="blue") 17 | 18 | def set_text_error(self, text): 19 | self._status_label.configure(text=text, foreground="red") 20 | 21 | def clear(self): 22 | self._status_label.configure(text="") 23 | 24 | 25 | class StudentsListView(ttk.LabelFrame): 26 | # COLUMNS là danh sách các cột, sid (MSSV) là khóa chính 27 | def __init__(self, parent, COLUMNS, student_edit_handler=None, sid_list_change_handler=None, 28 | student_delete_handler=None, *args, **kwargs): 29 | super().__init__(parent, *args, **kwargs) 30 | self._COLUMNS = COLUMNS 31 | self._student_edit_handler = student_edit_handler 32 | self._sid_list_change_handler = sid_list_change_handler 33 | self._student_delete_handler = student_delete_handler 34 | 35 | self._create_widgets() 36 | 37 | def _create_widgets(self): 38 | self._list_view = ttk.Treeview(self, columns=list(self._COLUMNS), selectmode="extended") 39 | self._list_view.grid(row=0, column=0, columnspan=5, sticky="EWNS") 40 | self._list_view.column("#0", stretch=False, width=0, minwidth=0) 41 | for column, info in self._COLUMNS.items(): 42 | self._list_view.heading(column, text=info.get("text", ""), anchor=info.get("anchor", "center"), 43 | command=self._on_sort(column)) 44 | self._list_view.column(column, stretch=info.get("stretch", False), anchor=info.get("anchor", "center"), 45 | width=info.get("width", 80), minwidth=info.get("minwidth", 60)) 46 | scrollbar = ttk.Scrollbar(self, orient="vertical", command=self._list_view.yview) 47 | scrollbar.grid(row=0, column=5, sticky="WNS") 48 | self._list_view.configure(yscrollcommand=scrollbar.set) 49 | self._list_view.bind("<>", self._on_open_items) 50 | self._list_view.bind("<>", self._on_select_change) 51 | self._list_view.bind("", self._on_select_all) 52 | self._list_view.bind("", self._on_select_all) 53 | self._list_view.bind("", self._on_delete) 54 | 55 | ttk.Label(self, text="Sort:").grid(row=1, column=0, sticky="E") 56 | self._descending = tk.BooleanVar() 57 | ttk.Checkbutton(self, text="Descending", variable=self._descending).grid(row=1, column=1, sticky="W") 58 | 59 | self._delete_button = ttk.Button(self, text="Delete", state="disabled", command=self._on_delete) 60 | self._delete_button.grid(row=1, column=3) 61 | self._edit_button = ttk.Button(self, text="Edit", state="disabled", command=self._on_open_items) 62 | self._edit_button.grid(row=1, column=4, columnspan=2) 63 | 64 | self.columnconfigure(2, weight=1) 65 | self.rowconfigure(0, weight=1) 66 | 67 | for child in self.winfo_children(): 68 | child.grid(padx=2, pady=2) 69 | 70 | def _on_sort(self, column): 71 | def sort(): 72 | items = list(self._list_view.get_children()) 73 | if self._COLUMNS[column].get("numeric", False): 74 | items.sort(reverse=self._descending.get(), key=lambda x: float(self._list_view.set(x, column))) 75 | else: 76 | items.sort(reverse=self._descending.get(), key=lambda x: self._list_view.set(x, column)) 77 | for index, iid in enumerate(items): 78 | self._list_view.move(iid, self._list_view.parent(iid), index) 79 | 80 | return sort 81 | 82 | def _on_open_items(self, *args): 83 | if selected_sids := self._list_view.selection(): 84 | if self._student_edit_handler: 85 | student = {column: self._list_view.set(selected_sids[0], column) for column in self._COLUMNS} 86 | self._student_edit_handler(student) 87 | 88 | def _on_select_all(self, *args): 89 | self._list_view.selection_set(*self._list_view.get_children()) 90 | 91 | def _on_delete(self, event=None): 92 | if sel_list := self._list_view.selection(): 93 | self._list_view.delete(*sel_list) 94 | if self._sid_list_change_handler: 95 | self._sid_list_change_handler(self._list_view.get_children()) 96 | if self._student_delete_handler: 97 | self._student_delete_handler(sel_list) 98 | 99 | def _on_select_change(self, *args): 100 | self._delete_button.configure(state="normal" if len(self._list_view.selection()) > 0 else "disabled") 101 | self._edit_button.configure(state="normal" if len(self._list_view.selection()) > 0 else "disabled") 102 | 103 | # chon student voi sid (neu co), khong thi bo chon 104 | def choose(self, sid): 105 | if self._list_view.exists(sid): 106 | self._list_view.selection_set(sid) 107 | self._list_view.see(sid) 108 | else: 109 | self._list_view.selection_set() 110 | 111 | # xoa het cac dong 112 | def clear(self): 113 | if sel_list := self._list_view.get_children(): 114 | self._list_view.delete(*sel_list) 115 | if self._sid_list_change_handler: 116 | self._sid_list_change_handler(self._list_view.get_children()) 117 | if self._student_delete_handler: 118 | self._student_delete_handler(sel_list) 119 | 120 | # them nhieu sinh vien neu chua co (sid) va thay the neu da co 121 | def insert_or_replace(self, students): 122 | for student in students: 123 | if self._list_view.exists(student["sid"]): 124 | for column in self._COLUMNS: 125 | self._list_view.set(student["sid"], column, student[column]) 126 | else: 127 | self._list_view.insert("", "end", iid=student["sid"], values=list(student.values())) 128 | if self._sid_list_change_handler: 129 | self._sid_list_change_handler(self._list_view.get_children()) 130 | 131 | # lay so luong sinh vien (row), hieu qua hon lay danh sach 132 | def get_students_len(self): 133 | return len(self._list_view.get_children()) 134 | 135 | # Lay danh sách các student (moi student la mot dict cac string) 136 | def get_students(self): 137 | students = [] 138 | for sid in self._list_view.get_children(): 139 | student = {} 140 | for column in self._COLUMNS: 141 | student[column] = self._list_view.set(sid, column) 142 | students.append(student) 143 | return students 144 | 145 | # Đặt trùng tên _name sửa thành _namevar 146 | class StudentAddUpdatePanel(ttk.LabelFrame): 147 | # Thông báo: sid_change (khi khóa chính là MSSV thay đổi), student_add_or_update, error_handler 148 | def __init__(self, parent, sid_change_handler=None, student_add_or_update_handler=None, 149 | error_handler=None, *args, **kwargs): 150 | super().__init__(parent, *args, **kwargs) 151 | self._sid_change_handler = sid_change_handler 152 | self._student_add_or_update_handler = student_add_or_update_handler 153 | self._error_handler = error_handler 154 | self._sid_list = [] # danh sách các MSSV đã có (để quyết định là add hay update) 155 | 156 | self._create_widgets() 157 | 158 | def _create_widgets(self): 159 | self._sid = tk.StringVar() 160 | self._sid.trace("w", self._on_sid_change) 161 | self._namevar = tk.StringVar() 162 | self._gender = tk.BooleanVar() # gioi tinh: True la Name 163 | self._grade = tk.StringVar() # diem 164 | self._add_update_text = tk.StringVar() # add or update 165 | 166 | ttk.Label(self, text="MSSV:").grid(row=0, column=0, sticky="E") 167 | ttk.Label(self, text="Họ tên:").grid(row=0, column=2, sticky="E") 168 | ttk.Label(self, text="Điểm:").grid(row=1, column=0, sticky="E") 169 | ttk.Label(self, text="Giới tính:").grid(row=1, column=2, sticky="E") 170 | 171 | self._sid_entry = ttk.Combobox(self, width=10, textvariable=self._sid) 172 | self._sid_entry.grid(row=0, column=1, sticky="W") 173 | name_entry = ttk.Entry(self, textvariable=self._namevar) 174 | name_entry.grid(row=0, column=3, columnspan=3, sticky="WE") 175 | grade_entry = ttk.Spinbox(self, width=4, values=[i/2 for i in range(0, 21)], textvariable=self._grade) 176 | grade_entry.grid(row=1, column=1, sticky="W") 177 | gender_checkbox = ttk.Checkbutton(self, text="Nam", variable=self._gender) 178 | gender_checkbox.grid(row=1, column=3, sticky="W") 179 | ttk.Button(self, text="Clear", command=self._on_clear).grid(row=1, column=4) 180 | ttk.Button(self, textvariable=self._add_update_text, command=self._on_add_update).grid(row=1, column=5) 181 | 182 | for widget in [self._sid_entry, name_entry, grade_entry, gender_checkbox]: 183 | widget.bind("", self._on_add_update) 184 | 185 | self.columnconfigure(3, weight=1) 186 | 187 | for child in self.winfo_children(): 188 | child.grid(padx=2, pady=2) 189 | 190 | def _on_sid_change(self, *args): 191 | self._add_update_text.set("Update" if self._sid.get() in self._sid_list else "Add") 192 | if self._sid_change_handler: 193 | self._sid_change_handler(self._sid.get()) 194 | 195 | def _on_add_update(self, event=None): 196 | try: 197 | if self._sid.get() == "": 198 | raise Exception("Student ID must be not empty!") 199 | if self._namevar.get() == "": 200 | raise Exception("Student name must be not empty!") 201 | if self._grade.get() not in [str(i/2) for i in range(0, 21)] + [str(i) for i in range(11)]: 202 | raise Exception("Student grade is not valid!") 203 | student = {} 204 | student["sid"] = self._sid.get() 205 | student["name"] = self._namevar.get() 206 | student["gender"] = self._gender.get() 207 | student["grade"] = self._grade.get() 208 | added = self._sid.get() not in self._sid_list 209 | if self._student_add_or_update_handler: 210 | self._student_add_or_update_handler(student, added) 211 | self._sid_entry.focus_set() 212 | except Exception as e: 213 | if self._error_handler: 214 | self._error_handler(str(e)) 215 | 216 | def _on_clear(self): 217 | self.set_student({}) 218 | 219 | def clear(self): 220 | self.set_sid_list([]) 221 | self.set_student({}) 222 | 223 | # dat cac gia tri tu dict 224 | def set_student(self, value): 225 | self._sid.set(value.get("sid", "")) 226 | self._namevar.set(value.get("name", "")) 227 | self._gender.set(value.get("gender", False)) 228 | self._grade.set(value.get("grade", "")) 229 | 230 | # dat danh sach cac sid 231 | def set_sid_list(self, sid_list): 232 | self._sid_list = sid_list 233 | self._sid_entry.configure(values=self._sid_list) 234 | self._sid.set("") 235 | -------------------------------------------------------------------------------- /python_part_1_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vqhBook/python/6d3c6214ba44d1279ae484ab32e12869bfe335ae/python_part_1_2.pdf --------------------------------------------------------------------------------