Giúp robot Reeborg vượt thử thách

Reeborg’s world là một trang web được thiết kế cho việc học lập trình, cụ thể là thực hành lập trình Python. Điều thú vị ở đây là trong thế giới Reeborg’s World, việc thực hành Python chính là điều khiển chú robot sao cho bạn ấy thực hiện đúng yêu cầu của các thử thách. 

Trong bài blog này, mình sẽ dẫn bạn robot của mình đi dạo với 2 thử thách.

👉 Thử thách “Hurdle 4”: bạn robot cần vượt qua các chướng ngại vật và về đích

👉 Thử thách “Maze” bạn robot cần tìm đường ra khỏi mê cung

Để vượt qua các thử thách này, bạn hãy đọc kỹ đề ở “World Info” và xem các gợi ý về cú pháp Python cũng như các hàm điều kiện cho sẵn để có thể gọi cho robot di chuyển, ví dụ là: turn_left() để rẽ trái hay move() để đi thẳng.

Thử thách "Hurdle 4" vượt chướng ngại vật

hurdle4

Phân tích thử thách

Reeborg đang ở trong một cuộc đua vượt rào. Trò chơi yêu cầu bạn ấy cần di chuyển theo các đường mũi trên đang được hiển thị để có thể vượt rào thành công. 

Một lưu ý là các vị trí cũng như độ cao của rào sẽ thay đổi mỗi khi bạn tải lại thử thách. Và thử thách này là thử thách vượt rào khó nhất, phần lập trình này yêu cầu cũng hoạt động tốt cho các thử thách Hurdles1, Hurdles2, Hurdles3.

Reeborg's basic keyboard

Đầu tiên, bạn hãy click vào “Reeborg’s basic keyboard” sẽ thấy cửa sổ hướng dẫn sau:

reeborg keyboard

Có 5 tabs là:

– “Commands“: các hàm có sẵn để dùng như move(), turn_left(), …

– “Conditions“: các hàm có sẵn để kiểm tra điều kiện như đến đích với at_goal(), phía trước có trống không? với front_is_clear(), có tường phía trước không? wall_in_front(), …

– “Python“: các cú pháp Python gợi ý

– “Objects“, Special“: các đối tượng và các ký tự đặc biệt, cái này thì trong bài này mình sẽ không dùng.

Viết các hàm phụ trợ

Rẽ phải

Như bạn có thể thấy ở trên, mình có hàm di chuyển từng ô với move() và rẽ trái với turn_left(). 

Nhưng rõ là mình sẽ cần rẽ phải, cho nên mình cần viết một hàm để giúp mình làm việc này.

Giả sử Reeborg của mình đang hướng lên phía trên theo chiều mũi tên như trong hình, thì việc rẽ phải sẽ là xoay qua trái 3 lần. 

Bạn nhớ hồi xưa mình đi học hay có học quay trái, quay phải như thực hành nghi thức Đội không? Đây chính là một kiểu như thế ấy, “Bên trái, quay!” ba lần như thế là thành sang bên phải.

turn-right

Vậy là mình viết được hàm rẽ phải, nó chỉ đơn giản là như thế này:

def turn_right():
    turn_left()
    turn_left()
    turn_left()

Nhảy qua chướng ngại vật

Giả sử Reeborg đang đứng ở đáy của rào, hướng về phía bên phải như hình bên dưới. Reeborg cần thực hiện các thao tác sau để có thể vượt rào:

Bước 1: rẽ trái để bắt đầu vượt rào

Bước 2: vì độ cao của rào có thể thay đổi, nên Reeborg cần tiến về phía trước cho đến khi bạn ấy biết được đâu là đỉnh của rào. Vậy bạn có đoán được khi nào là đỉnh của rào không? Mình nghĩ bạn có thể đoán được, đỉnh của rào chính là khi bạn ấy có thể rẽ phải, tức bên phải trống để có thể rẽ được.

Tại đây mình có thể sử dụng hàm right_is_clear() để kiểm tra bên phải có trống hay không?

Nếu bên phải trống thì bạn ấy có thể rẽ phải, nếu không trống thì tiếp tục đi thẳng.

Bước 3: Tiếp theo sau khi đã rẽ sang phải rồi, vì rào chỉ bằng 1 lần di chuyển, cho nên chỉ cần di chuyển 1 lần nữa và tiếp tục rẽ phải để có thể hướng robot xuống phía dưới.

Bước 4: Reeborg sẽ tiếp tục tiến về phía trước theo hướng mặt đất cho đến khi nào gặp mặt đất thì rẽ trái để quay về hướng sẵn sàng cho lần di chuyển tiếp theo.

how-to-jump

Dựa vào cách di chuyển để Reeborg có thể vượt rào ở trên, hàm jump() được viết là:

def jump():
    turn_left()
    move()

    while not right_is_clear():
        move()

    turn_right()
    move()
    turn_right()

    while front_is_clear():
        move()

    turn_left()

Chinh phục thử thách

Sau khi đã có cách vượt rào rồi, mình sẽ cho Reeborg tiến về phía trước, nếu gặp rào thì jump(), và cứ tiếp tục như vậy cho đến khi về đích với hàm at_goat() trả về True thì dừng lại.

while not at_goal():
    if wall_in_front():
        jump()
    else:
        move()

Cùng xem kết quả cuối cùng của thử thách vượt rào nhé.

hurdle-4-finish

Thử thách "Maze" vượt mê cung

Vừa vượt rào xong đến đích thì Reeborg tưởng đâu là đã về đến rồi. Thế nhưng thì ra là bạn ấy đã vô tình bị lọt tiếp vào ngay trong mê cung 😱 🥲

Giờ làm sao có thể thoát ra khỏi mê cung này và về nhà đây, chúng mình cùng giúp bạn ấy nhé!

maze-reeborg-world

Phân tích thử thách

Có một cách khá đơn giản để thoát khỏi mê cung đó là hay cứ đi về một hướng thôi, có thể là bên trái hoặc bên phải, cách này gọi là “Right-Hand Rule” nên thường sẽ đi về bên phải (xem thêm ở đây).

Cùng thử xem cách này có giúp Reeborg thoát khỏi mê cung không nhé.

Chinh phục thử thách

Đi về bên phải thì mình có thể dùng lại hàm turn_right() đã viết ở thử thách vượt rào. 

Tiếp theo, làm sao để robot cứ đi bên phải nhỉ? 

Hẳn là nếu bên phải trống thì mình sẽ cho phép bạn ấy rẽ phải, còn không trống, thì sẽ cứ đi thẳng dọc tường. 

Hình dưới mô tả việc kiểm tra bên phải phía trước, từ đó quyết định cho robot hành động rẽ phải hoặc đi thẳng. 

Việc kiểm tra này có thể dùng các hàm right_is_clear() và front_is_clear().

Thế khi robot đâm vào ngõ cụt thì sao? 

Khi đó bên phải không trống, phía trước cũng không trống, bạn ấy sẽ cứ đứng im vậy thôi. 

Lúc này, là lúc mình cần cho bạn ấy rẽ trái để phía bên phải sẽ trống khi mình xoay đến hướng ngược lại của ngõ cụt.

Và đây là kết quả của cách “Right-Hand Rule”, cùng xem bạn robot có đi khỏi mê cung không nhé.

def turn_right():
    turn_left()
    turn_left()
    turn_left()


while not at_goal():
    if right_is_clear():
        turn_right()
        move()
    elif front_is_clear():
        move()
    else:
        turn_left()

Trên đây là kết quả bạn ấy đã thoát khỏi được mê cung rồi, dù hơi đi vòng vèo một tẹo hihi.

Tuy nhiên, trong lúc thử nghiệm mình cũng thấy đôi khi bạn robot của mình bị bế tắc khi đi thành hình vuông mãi vậy á, cùng tìm hiểu thêm trường hợp đặc biệt đó ở mục tiếp theo hen.

stuck-round

Vấn đề: đi xà quần bất tận

Tạo thử thách mô phỏng vấn đề

Đầu tiên, mình cần tạo một phiên bản trò chơi mô phỏng vấn đề này, để khi nào cũng có thể mở lên và thực hành được. 

Reeborg’s world cho phép mình đổi vị trí của robot và lưu thành một bản game riêng bằng cách:

Chọn “Additional Options” > tại “World: creation, edition, …” chọn “Edit world” > Chọn “Robot” và chọn “Position” để có thể bắt đầu sửa vị trí robot.

Click chọn vào robot để bỏ chọn vị trí đang đứng, và chọn lại vị trí như sau. 

Tiếp theo, chọn “Onload” để nó chuyển thành dấu x để chương trình không tự động load vị trí ngẫu nhiêu của robot nữa.

Cuối cùng chọn “Save world in browser” và đặt tên cho thử thách, có thể là “maze-problem”. 

Nếu bạn vẫn chưa tạo được mô phỏng thì có thể tải về bản mình problem_world.json mình đã có ở đây, và nạp vào nhé.

Phân tích vấn đề

Vậy là mình đã cùng nhau mô phỏng được vấn đề rồi. Thế thì lý do vì sao mà bạn robot của mình đi xà quần mãi như thế nhỉ? 😅

Một điều mình có thể thấy ở đây là do vị trí ban đầu của bạn ấy, có vị trí sẽ không gặp vấn đề này, có vị trí lại gặp. Vậy cần xem xét vị trí ban đầu cho tình huống này có gì đặc biệt?

Ở vị trí nào mà bạn ấy có thể đi thành hình vuông, thì nơi đó có khả năng sinh ra vấn đề. 

Lý do là vì bạn robot của mình sẽ rẽ phải khi có thể rồi lại tiến về trước rồi lại rẽ phải. Nếu bên phải luôn luôn có thể rẽ thì bạn ấy sẽ đi được hình vuông như trên, và cứ lặp lại như thế.

Vậy làm sao để giải quyết nó đây?

Giải quyết vấn đề

Bạn có đoán được chưa? Thử đoán xem thế nào đã nhé 😉 ☕️

Nếu bên phải luôn trống sẽ sinh vấn đề thì có cách nào bạn robot của mình luôn có thể bắt đầu với bên phải không trống không nhỉ? Và đây là cách mình giúp bên phải bạn ấy không trống(có tường): 

Trước khi bắt đầu tìm đường đi, nếu phía trước robot trống thì bạn ấy cứ tiến về phía trước cho đến khi nào chạm tường rồi thì quẹo trái.

Cùng xem mô phỏng của cách di chuyển này ở hình bên dưới:

maze-problem-solution-1

maze-problem-solution-2

Công việc này được thực hiện bằng cách kiểm tra và di chuyển robot về vị trí phù hợp ngay trước khi bắt đầu tìm đường ra khỏi mê cung, để đảm bảo bên phải bạn ấy luôn là tường. 

Các câu lệnh như sau:

while front_is_clear():
    move()

turn_left()

Từ đó, mình có chương trình đầy đủ là:

def turn_right():
    turn_left()
    turn_left()
    turn_left()

while front_is_clear():
    move()

turn_left()

while not at_goal():
    if right_is_clear():
        turn_right()
        move()
    elif front_is_clear():
        move()
    else:
        turn_left()

Cùng xem thành quả của Reeborg vượt mê cung thành công trong trường hợp khó nhằn này 🥳

result-problem-maze

Vậy là tụi mình đã cùng dẫn robot đi dạo và cũng giúp bạn ấy vượt qua các chướng ngại vật và thoát khỏi mê cung rồi. Thật là thú vị phải không 🥰 

Nếu có thêm thời gian bạn hãy rèn thêm kỹ năng Python của mình với các thử thách khác nhé.

Bài viết này mình có tham khảo một khóa học online trên Udemy mà mình đã học qua, hi vọng sẽ hữu ích với mọi người.

BeautyOnCode.