Nếu chuyển động của vật cản có thể được đoán nhận trước thì, hoàn toàn có khả năng xác định được vị trí của nó trong tương lai để sử dụng cho việc tìm đường. Một thuật toán tìm đường như A* có một hàm chi phí dùng để xác định độ khó để đi qua một điểm của bản đồ.
A* có thể được cải biến để duy trì khoảng thời gian cần thiết để đi tới một điểm nào đó (xác định bằng độ dài đường đi hiện tại), và khoảng thời gian này có thể được chuyển thành hàm chi phí. Hàm chi phí có thể tính toán được thời gian, và sử dụng tiên đoán vị trí của vật cản tại một thời điểm để xác định liệu một ô có thể đi qua hay không. Cải biến này không thật hoàn hảo tuy nhiên, nó sẽ không phải chi
phí một lượng thời gian để chờ một vật cản di chuyển khỏi một vị trí nào đó, và A* không phải thiết kế để phân biệt các đường đi dọc theo cùng một tuyến đường, mà là các vị trí khác nhau trong cùng một thời điểm.
Chương 3. CHƯƠNG TRÌNH THỰC NGHIỆM 3.1. Phân tích và thiết kế lựa chọn công cụ
3.1.1. Phác thảo chương trình
Thuật toán A* đối với mọi ngôn ngữ đều khá là đơn giản. Có 2 tập hợp được sử dụng, Open và Closed. Tập Open chứa các ô mà sẽ được kiểm tra. Khởi đầu, thì tập Open chỉ chứa duy nhất một phần tử là phần tử. Tập Closed chứa những ô đã được kiểm tra. Khởi đầu thì tập Closed rỗng. Về mặt hình học thì tập Open là biên, còn tập Closed là nội địa của những khu vực đã được đi qua. Mỗi một ô đều lưu một biến con trỏ để xác định ô cha để ta có thể biết được nó được thiết lập bằng cách nào.
Có một vòng lặp chính trong chương trình để chọn ra một ô N tốt nhất trong danh sách Open (ô có chứa giá trị F nhỏ nhất) và kiểm tra nó. Nếu N là đích, thì ta đã hoàn thành công việc, nếu không thì ta sẽ di chuyển N từ danh sách Open sang danh sách Closed. Sau đó các láng giềng của N được kiểm tra. Những láng giềng nào đã ở trong danh sách Closed , thì ta sẽ không phải kiểm tra lại nữa. một láng giềng đang nằm trong danh sách Open thì sẽ được kiểm tra vì thế ta cũng sẽ không phải quan tâm tới nó ngay (*). Trường hợp còn lại ta sẽ thêm ô đó vào danh sách Open và ghi nhớ ô cha của nó là ô N. Chi phí đường đi để tới '
N , G( '
N ) = G(N) + chi phí di chuyển từ N đến N'.
(*) Ở đây ta bỏ qua một chi tiết nhỏ, là nếu trường hợp một láng giềng đã nằm trong danh sách Open ta sẽ kiểm tra hai giá trị G nếu giá trị G mới bé hơn ta sẽ cập nhật lại các thông số của ô đó trong danh sách Open. Nếu không thì ta sẽ bỏ qua.
1. Đưa ô khởi đầu vào danh sách Open,danh sách Closed rỗng. 2. Tiến hành lặp như sau :
A. Tìm ô có giá trị F nhỏ nhất, ghi chú nó là ô hiện tại B. Chuyển nó vào danh sách Closed.
C. Với các 8 láng giềng của ô hiện tại ta kiểm tra
- Nếu nó là vật cản hoặc đã nằm trong danh sách Closed thì bỏ qua ô này, nếu không thì thực hiện bước tiếp theo
- Nếu nó chưa có trong danh sách Open thì thêm ô này vào danh sách Open. Ghi chú ô hiện tại làm ô cha của ô này và các giá trị F, G, H.
- Nếu nó đã nằm trong danh sách Open kiểm tra xem đường đi tới ô đó có tốt hơn nếu đi qua ô hiện tại bằng cách sử dụng giá trị G làm giá trị đo. Nếu giá trị G nhỏ hơn có nghĩa là đường đi sẽ tốt hơn, nếu như vậy ta sẽ thay đổi lại ô cha của ô này thành ô hiện tại và tính toán lại các giá trị F,G,H của ô này. Nếu như trước đó bạn đã sắp xếp danh sách Open theo thứ tự F thì bây giờ sẽ phải thay đổi lại thứ tự của danh sách.
D. Dừng thuật toán lại khi :
- Đưa được ô đích vào danh sách Closed, trong trường hợp này thì đường đi đã được tìm ra nếu không
- Thuật toán không tìm được ô đích, và danh sách Open rỗng khi đó thì không có đường đi.
3. Ghi nhớ lại đường đi. Lần ngược lại từ ô đích tới ô cha của nó cho tới khi đến được ô khởi đầu, và ta đã xác định được đường đi.
3.1.2. Không gian bộ nhớ cần để tính toán lại đường đi
Đôi khi yếu tố thời gian không phải là yếu tố quyết định cho việc tính toán đường đi mà là không gian bộ nhớ dung để lưu trữ đường đi cho hang trăm đối tượng mới là yếu tố quyết định. Chương trình yêu cầu không gian bộ nhớ để nó có thể thực thi thuật toán, cộng them với bộ nhớ cần thiết để lưu trữ đường đi. Không gian bộ nhớ tạm thời cần thiết để thực thi thuật toán (trong A* là tập Open và Closed)thông thường sẽ lớn hơn không gian bộ nhớ cần thiết để lưu trữ kết quả đường đi. Để tránh thuật toán của ta chỉ tính toán một đường đi tại một thời điểm, thì ta có thể cực tiểu hóa tổng số không gian bộ nhớ tạm thời xuống. Thêm vào đó
lựa chọn cấu trúc dữ liệu cho tập Open và Closed cũng có thể tạo ra những sự khác biệt lớn trong việc cực tiểu hóa không gian bộ nhớ tạm thời. Trong mục này ta sẽ tập trung vào việc cực tiểu hóa không gian bộ nhớ được sử dụng để lưu trữ kết quả đường đi.
3.1.2.1. Vị trí và hướng di chuyển
Đường đi bao gồm cả vị trí và hướng di chuyển. Vị trí chiếm nhiều không gian bộ nhớ hơn, tuy nhiên nó lại có lợi thế là dễ dàng xác định được một vị trí hoặc một hướng bất kì trên đường đi mà không cần cắt ngang đường đi. Trong khi đó lưu trữ hướng, thì chỉ có hướng là có thể xác định được dễ dàng ; vị trí chỉ có thể được xác định bằng cách đi qua toàn bộ đường đi, theo hướng đi đó. Trong một lưới bản đồ thông thường, vị trí có thể được lưu trữ dưới dạng 2 số nguyên 16 bit, thực hiện một bước đi cần 32 bit. Tuy vậy càng ít hướng di chuyển thì càng cần ít không gian lưu trữ hơn. Nếu đối tượng chỉ có thể di chuyển theo 4 hướng, mỗi bước đi chỉ cần 2 bit; nếu đối tượng có thể di chuyển theo 6 hoặc 8 hướng thì mỗi bước di chuyển sẽ cần 3 bit. Cho dù thế nào thì nó cũng tiết kiệm được đáng kể lượng bộ nhớ dung để lưu trữ vị trí của đường đi. Ngoài ra ta cũng có thể giảm được lượng không gian lưu trữ cần thiết bằng cách lưu trữ hướng tương đối(“ rẽ phải 60 độ”) thay vì hướng tuyệt đối(“đi theo hướng Bắc”). Tuy nhiên một vài hướng tương đối có vẻ như không phù hợp với một số loại đối tượng. Ví dụ như, nếu đối tượng của ta di chuyển theo hướng bắc, thì gần như sẽ không có chuyện bước tiếp theo đối tượng sẽ di chuyển theo hướng nam. Trong lập trình game 6 hướng, thì ta sẽ chỉ có 5 hướng đi là có ý nghĩa. Còn đối với một vài bản đồ thì, có thể chỉ có 3 hướng đi (đi thẳng, rẽ trái 60 độ, rẽ phải 60 độ) là có nghĩa, nhưng đối với những bản đồ khác thì rẽ phải 120 độ lại là bước di chuyển hợp lý (ví dụ như ta di chuyển theo đường dốc lên núi với những đoạn đường cua gấp).
3.1.2.2. Nén đường đi
Một khi đường đi được xác định, thì nó có thể được nén lại theo một số cách. Một thuật toán nén đường đi có thể theo được sử dụng để rút gọn đường đi dựa
theo hướng. hoặc vi trí. Trước khi đi tới quyết định, ta phải xem xét một đường đi tiêu biểu trong game để quyết định phương pháp nén nào sẽ thực hiện tốt nhất. Nếu như ta giới hạn số đối tượng bới 300, và chỉ có tối đa 50 đối tượng di chuyển trong cùng một thời điểm, và đường đi chỉ trong vòng 100 bước, thì tổng số bộ nhớ cần thiết sẽ chỉ <50k, và sẽ không đáng kể để ta phải bận tâm vào việc nén đường đi.
3.1.2.3. Lưu trữ vị trí
Trong một bản đồ thì vị trí của vật cản chứ không phải là địa hình mới là yếu tốt chính quyết định tới đường đi, trong đó có thể chứa các đường thẳng. Nếu xảy ra trường hợp này thì đường đi sẽ chỉ cần lưu trữ điểm cuối của các đường thẳng đó. Di chuyển bao gồm kiểm tra điểm tiếp theo của đường đi và di chuyển thẳng tới đó.
3.1.2.4. Lưu trữ hướng đi
Khi mà hướng đi được lưu trữ, sẽ có thể xảy ra trường hợp là nhiều bước liên tiếp có hướng di chuyển thẳng hàng nhau. Ta có thể sử dụng lợi thế đó để lưu trữ đường đi mà cần ít bộ nhớ hơn.
Một cách để lưu trữ đường đi là lưu trữ cả hướng đi và 1 con số thể hiện đối tượng sẽ di chuyển theo hướng đó bao nhiêu lần. Không giống như trong lưu trữ vị trí, thì cách lưu trữ này sẽ không còn tốt nữa nếu 1 hướng đi không được thực hiện nhiều lần liên tiếp. Cũng giống như trường hợp có rất nhiều các đoạn thẳng thì nén vị trí là hữu ích, khi mà nén hướng đi không có lợi, thì các đường đi sẽ không canh thẳng hướng với nhau nữa. Với hướng đi tương đối, ta có thể loại bỏ việc “ liên tục tiến về phía trước ”, thực tế đã chỉ ra rằng với 8 hướng di chuyển ta có thể loại bỏ được hướng tiến, lùi rẽ trái và phải 135 độ, và ta sẽ chỉ phải lưu trữ hướng đi với 2 bit.
Một cách khác để lưu trữ đường đi là ta sẽ sử dụng biến mã hóa độ dài. Ý tưởng là sử dụng 1 bit đơn(0) cho phần lớn những bước di chuyển chung: “đi thẳng”. Sử dụng (1) để đánh dấu những bước rẽ, và theo sau 1 là một vài bit thể
hiện cho hướng rẽ. Trong bản đồ 4 hướng thì, ta sẽ chỉ có thể rẽ trái và phải thì ta có thể sử dụng (10) để thể hiện rẽ trái và (11) để thể hiện rẽ phải.
Sử dụng biến mã hóa độ dài có vẻ tổng quát hơn, và nén tốt hơn cách mã hóa ở trên với đường đi hỗn tạp, nhưng với đường đi chứa nhiều đường thẳng dài. Lấy ví dụ như trong dãy đường đi (Hướng nam, đi thẳng 6 bước, rẽ trái, đi thẳng 3 bước, rẽ phải, đi thẳng 5 bước, rẽ trái, đi thẳng 2 bước) khi đó sẽ được mã hóa [(North,6), (West,3), (North,5),(West,5)], với cách mã hóa trên nếu mỗi hướng đi được mã hóa bởi 2 bit, và mỗi khoảng cách được mã hóa bởi 8 bit thì đoạn đường đi này cần tới 40 bit để lưu trữ.
Còn với mã hóa biến độ dài, ta chỉ cần sử dụng 1 bit cho mỗi bước di chuyển và 2 bit cho mỗi bước rẽ, ta sẽ có [North 0 0 0 0 0 0 10 0 0 0 11 0 0 0 0 0 10 0 0] tổng cộng sẽ là 24 bit. Nếu hướng đi ban đầu và mỗi bước rẽ thể hiện 1 bước đi, thì ta còn có thể tiết kiệm 1 bit mỗi bước đi. Và kết quả là chỉ cần 20 bit để lưu trữ đoạn đường đi trên. Tuy nhiên, đường đi dài hơn sẽ cần nhiều không gian bộ nhớ hơn với mã hóa biến độ dài. Ví dụ như với dãy (North,đi thẳng 200 bước) thì sẽ chỉ cần thể hiện [(North,200)] cho cách mã hóa trên, tổng cộng sẽ là 10 bit. Còn với cách mã hóa biến độ dài thì sẽ là (North 0 0 0… 0 0) tổng cộng sẽ là 202 bit.
3.1.2.5. Tính toán điểm cuối
Thay vì lưu trữ mỗi bước dọc theo đường đi, thì sau khi sử lý được một số bước ta có thể thu nhiều bước lại thành duy nhất 1 điểm cuối, thông thường là vị trí mà ta sẽ chuyển hướng di chuyển hoặc một vị trí quan trọng chằng hạn như một thành phố. Thuật toán di chuyển sau đó sẽ đi theo đường đi giữa các điểm cuối đó.
3.1.2.6. Giới hạn độ dài đường đi
Khi mà các điều kiện hoặc các sắp xếp của bản đồ có thể thay đổi, thì sẽ là không hợp lý nếu ta lưu trữ một đường đi dài, khi đó phần còn lại của đường đi sẽ không còn sử dụng được nữa. Mỗi đối tượng sẽ lưu trữ 1 lượng bước đi cố định taị thời điểm bắt đầu đường đi, phần còn lại của đường đi sẽ được tính toán lại. Phương pháp này cho phép ta kiểm soát được tổng số dữ liệu trên mỗi đối tượng.
Trong báo cáo này em sẽ xây dựng một chương trình mô phỏng một hệ thống giao thông trong đó các phương tiện tham gia giao thông sẽ tự động tránh đường nhau tuân theo những quy tắc của luật giao thông cơ bản.
3.2. Một số kết quả của chương trình
Chương trình được cài đặt bằng ngôn ngữ lập trình C++.net, gồm 4 chức năng trong đó có 2 chức năng chính:
- Tìm đường: Chức năng này sẽ mô phỏng việc tìm đường đi cho đối tượng trong một khung cảnh đã được xây dựng sẵn.
- Tạo khung cảnh: Là chức năng cho phép người sử dụng có thể thay đổi khung cảnh mà đối tượng sẽ thực hiện việc tìm đường.
Hình 3.1. Giao diện chương trình đề mô
Đây là giao diện chính của chương trình khi chọn chức năng tìm đường, chương trình sẽ mô phỏng quá trình tìm đường đi của đối tượng trong một khung cảnh đã tạo sẵn.
Hình 3.2. Chức năng tìm đường đi cho đối tượng
Chức năng này thực hiện việc tìm đường cho đối tượng trong môi trường động. Mỗi khi người sử dụng thay đổi hướng đi của đối tượng ( thay đổi đích – trạng thái kết thúc) thì chương trình dẽ thực hiện việc tính toán lại đường đi đối với trạng thái đích mới tại thời điểm người sử dụng thay đổi đích.
Hình 3.3. Chức năng tạo khung cảnh
Chức năng này cho phép người sử dụng có thể thay đổi khung cảnh ( thay đổi môi trường) của bài toán.
KẾT LUẬN 1. Kết luận
Một trong những cách kinh điển trong việc tạo chuyển động cho đối tượng trong môi trường thực tại ảo đó là việc tạo chuyển động theo đường cong ( hay quỹ đạo) xác định trước, nhưng việc tạo chuyển động này thường cứng nhắc và không mềm mại nhất là trong trường hợp môi trường có nhiều đối tượng cùng tham gia. Một trong những cách giải quyết có thể tiếp cận đó là việc chia nhỏ đường cong ra thành từng đoạn và ứng với mỗi đoạn ta sẽ để đối tượng dò tìm đường đi một cách tự động tùy thuộc vào ngữ cảnh lúc đó.
Luận văn nhằm nghiên cứu một số thuật toán tìm đường đi trên đồ thị, trên cơ sở đó nghiên cứu tìm cách áp dụng vào việc tính toán đường đi trong môi trường giao thông động và đã đạt được một số kết quả cụ thể sau:
Trình bày khái quát về giải thuật tìm đường và bài toán tìm đường trong môi trường giao thông động.
Hệ thống hóa một số vấn đề trong tìm đường đi trong môi trường giao thông động.
Cài đặt thử nghiệm bài toán tìm đường đi cho đối tượng trên bản đồ giao thông, trên cơ sở áp dụng thuật toán tìm đường đi trong đồ thị. Do thời gian có hạn cũng như những hạn chế của bản thân nên luận văn chưa đi sâu vào tìm hiểu được mối ràng buộc giữa các thuộc tính, các đối tượng chuyển động, ràng buộc giữa chuyển động với bề mặt đối tượng. Việc tạo ta mối ràng buộc giữa các thuộc tính khi đối tượng chuyển động là khá khó khăn và phức tạp, nếu thực hiện tốt thì nó sữ tạo ra một hoạt cảnh chuyển động linh hoạt phức tạp như trong thực tế.
2. Hướng phát triển của đề tài
Trong thời gian tới luận văn sẽ mở rộng nghiên cứu về:
Mối ràng buộc giữa các thuộc tính khi đối tượng chuyển động.
Mối ràng buộc giữa thuộc tính của đối tượng với chuyển động và của chuyển động với bề mặt của đối tượng.
TÀI LIỆU THAM KHẢO Tài liệu tiếng Việt
1. Phạm Thế Anh, Trần Thanh Hiệp, Đỗ Năng Toàn (2006), “Mô phỏng va chạm