เฉลยแบบฝึกหัด: คาเรล#

โจทย์แบบฝึกหัด#

ข้อ 1#

collect_newspaper_karel.py จงเขียนโปรแกรมสั่งให้คาเรลหยิบ beeper และเดินกลับมาที่เดิม โดยให้เขียนฟังก์ชันย่อยตามหลักการย่อยโจทย์ (decomposition) เพื่อทำให้โค้ดดูสะอาดตา อ่านง่ายเข้าใจง่ายขึ้น

def main():
    """
    แนวทางการคิดคือ แบ่งงานออกเป็นขาเดินไปเก็บ beeper และขาเดินกลับไปวาง beeper ที่จุดเริ่มต้น
    """
    go_collect()
    return_home()

# ฟังก์ชันย่อย
def go_collect():
    turn_right()
    move()
    turn_left()
    for i in range(3):
        move()
    pick_beeper()

def return_home():
    turn_around()
    for i in range(3):
        move()
    turn_right()
    move()
    put_beeper()

def turn_right():
    for i in range(3):
        turn_left()

def turn_around():
    for i in range(2):
        turn_left()

ข้อ 2#

corner_to_corner_karel.py จงเขียนโปรแกรมสั่งให้คาเรล วิ่งจากจุดเริ่มต้นที่มุมซ้ายล่างไปมุมขวาบน โดยที่เราไม่รู้ว่า world จะมีขนาดเท่าไร ให้ลองทดสอบกับ world หลายๆ แบบ

def main():
    """
    แนวทางการคิด
    1. เดินไปเรื่อยๆ จนกว่าจะเจอทางตัน + หันซ้าย + เดินไปเรื่อยๆ จนกว่าจะเจอทางตัน
    2. ใช้ while front_is_clear เพื่อให้ karel เดินไปเรื่อยๆ จนกว่าจะเจอทางตัน
    """
    # 1. ซ้ายล่าง -> ขวาล่าง
    move_l()
    # 2. ซ้ายหัน
    turn_left()
    # 3. ขวาล่าง -> ขวาบน
    move_l()

# ฟังก์ชันย่อย
def move_l():
    # เดินหน้าจนกว่าจะเจอทางตัน
    while front_is_clear():
        move()

ข้อ 3#

four_corner_karel.py จงเขียนโปรแกรมสั่งให้คาเรลวาง beeper ไปทั้งสี่มุม มุมละ 3 beeper โดยที่เราไม่รู้ว่า world จะมีขนาดเท่าไร ให้ลองทดสอบกับ world หลายๆ แบบ

def main():
    """
    แนวทางการคิด
    1. รูปแบบการเดินคือ เดินไปจนสุดทาง + วาง beeper
    2. หันซ้ายเพื่อเดินแนวต่อไป
    3. เดินรอบกรอบสี่เหลี่ยม ใช้ for loop เพื่อวน 4 รอบ
    """
    for i in range(4):
        walk_to_wall_and_put_beeper()
        turn_left()

# ฟังก์ชันย่อย
def walk_to_wall_and_put_beeper():
    # เดินต่อไปถ้าข้างหน้ายัง clear
    while front_is_clear():
        move()
    # เมื่อหยุดเดินแล้วแล้ว วาง beeper
    put_beeper()

ข้อ 4#

roomba_karel.py จงเขียนโปรแกรมสั่งให้คาเรลเก็บ beeper ที่กระจัดกระจายอยู่ทั้งห้อง โดยโปรแกรมจะต้องทำงานทั้ง roomba_karel.w และ roomba_karel2.w

def main():
    """
    แนวทางการคิด
    1. หาวิธีเดินให้ได้ครบทุกช่องก่อน
    2. พอเดินได้ครบทุกช่องแล้ว ก็ค่อยเพิ่มการ pick beepers ไปในทุกช่องที่เดินผ่าน
    3. Done

    Trick:
    ก่อนขึ้นแถวถัดไปให้เดินกลับมาช่องเดิมก่อน
    จะช่วยให้เขียนเงื่อนไขการเปลี่ยนแถวได้ง่ายขึ้น
    เพราะจะอยู่ในสภาวะเดียวกันทุกครั้ง
    """
    # 1. เก็บแถวแรก
    clean_row()
    # 2. เดินกลับมาจุดเริ่มต้น
    come_back()
    # 3. ถ้าขวาว่าง เท่ากับยังมีแถวต่อไป ก็เปลี่ยนแถวแล้ว clean แถวใหม่วนไป
    while right_is_clear():
        # ขึ้นไปแถวใหม่
        turn_right()
        move()
        turn_right()
        # ทำเหมือนแถวแรกอีกครั้ง
        clean_row()
        come_back()

# ฟังก์ชันย่อย
def turn_right():
    turn_left()
    turn_left()
    turn_left()

def clean_beepers():
    # เก็บ beeper จนกว่าจะไม่เหลือ
    while beepers_present():
        pick_beeper()

def clean_row():
    # เก็บช่องแรก
    clean_beepers()
    # เดินไปเก็บช่องที่เหลือในแถว
    while front_is_clear():
        move()
        clean_beepers()

def come_back():
    # กลับหลังหัน
    turn_left()
    turn_left()
    # เดินไปจนสุด
    while front_is_clear():
        move()

ข้อ 5#

alternate_beeper.py จงเขียนโปรแกรมสั่งให้คาเรลวาง beeper แบบช่องเว้นช่องเฉพาะแถวล่าง โดยที่เราไม่รู้ว่า world จะมีขนาดเท่าไร ให้ลองทดสอบกับ world หลายๆ แบบ

def main():
    """
    แนวทางการคิด
    1. รูปแบบการเดิน คือ เดิน-วาง-เดิน จึงใช้ while front_is_clear เพื่อทำ action รูปแบบเดิมไปเรื่อยๆ จนกว่าจะเจอทางตัน 
    2. แต่ while front_is_clear จะเช็กข้างหน้าก็ต่อเมื่อทำ action 3 อันครบในแต่ละรอบเท่านั้น หลังจากที่วาง beeper แล้วเดินก้าวต่อไป จึงจำเป็นต้องเช็กว่าข้างหน้า clear หรือไม่ จึงใช้ if front_is_clear
    """
    # เดินหน้าจนกว่าจะเจอทางตัน
    while front_is_clear():
        move() # เริ่มต้นด้วยการเดินหน้า
        put_beeper() # วาง beeper

        # ถ้าข้างหน้ายังว่างก็เดินหน้าต่อไป
        if front_is_clear():
            move()