Trò chơi này đòi hỏi người chơi dichuyển các đĩa từ cột này sang cột khác theo quy tắc kích thước đĩa trên phải bé hơn kíchthước đĩa dưới, giúp phát triển kỹ năng tư duy thuật toán và qu
Trang 1TRƯỜNG ĐẠI HỌC SƯ PHẠM KỸ THUẬT TP.HCM
KHOA CÔNG NGHỆ THÔNG TIN
BỘ MÔN CẤU TRÚC DỮ LIỆU VÀ GIẢI THUẬT
HUỲNH THIÊN HẠO - 23162021 CAO ĐĂNG HUY - 23162028
Trang 2LỜI CẢM ƠN
Trước hết, em xin gửi lời cảm ơn chân thành và sâu sắc nhất đến Thầy Lê Minh Tân vì đã tận tình hướng dẫn, chỉ bảo nhóm chúng
em trong suốt quá trình thực hiện đồ án Nhờ sự chỉ dẫn quý báu
và sự động viên của thầy, đã tiếp thêm động lực cho chúng em kịp thời hoàn thành được đồ án này
Em cũng xin gửi lời cảm ơn đến Ban giám hiệu và các thầy cô trong Khoa Công Nghệ Thông Tin đã tạo điều kiện và cung cấp nguồn tài liệu, kiến thức cần thiết để nhóm em có thể thực hiện đồ
án một cách thuận lợi, suôn sẻ.
Em xin cảm ơn các bạn bè trong lớp đã hỗ trợ, chia sẻ kinh nghiệm
và giúp đỡ nhóm em trong quá trình nghiên cứu và hoàn thiện đồ án kết thúc môn học.
Trang 3KẾ HOẠCH THỰC HIỆN
(Ghi rõ từng thời gian tiến hành các công việc của luận văn, thời gian thí nghiệm, thăm quan hiện trường, )
1 25/10/2024 Họp meet lên kế hoạch cho đồ án Đã hoàn thành
2 4/11/2024 Thảo luận nên dùng các cấu trúc
dữ liệu nào và thêm các tính năng cho game
Người viết đề cương
Ý kiến của giáo viên hướng dẫn
(ký và ghi rõ họ tên)
Trang 4MỤC LỤC
1 Phần MỞ ĐẦU
1.1 Tính cấp thiết của đề tài
1.2 Mục đích của đề tài
1.3 Cách tiếp cận và phương pháp nghiên cứu
- Đối tượng nghiên cứu
- Phạm vi nghiên cứu
2 Phần NỘI DUNG
1 Chương 1: Lí thuyết thứ nhất: Stack
1.1 Giải thích kiểu dữ liệu Stack
1.1.1 Định nghĩa 1.1.2 Đặc điểm
1.2 Ứng dụng Stack vào trong game Tháp Hà Nội
1.2.1 Lí do chọn Stack 1.2.2 Nguyên lí hoạt động của Stack ở các cột 1.2.3 Tối ưu hoá thuật toán
1.3 Phân tích và triển khai Stack trong game Tháp Hà Nội
1.3.1 Triển khai code 1.3.2 Phân tích thuật toán đệ quy và Stack
1.4 Đánh giá tính hiệu quả của Stack
2 Chương 2: Lí thuyết thứ hai: Doubly Linked List
2.1 Giải thích kiểu dữ liệu Doubly Linked List
2.1.1 Định nghĩa 2.1.2 Đặc điểm
2.2 Ứng dụng Doubly Linked List vào trong game Tháp Hà Nội
2.2.1 Lí do chọn Doublt Linked List 2.2.2 Cấu trúc Double Linked List ứng dụng vào quản lý danh sách người chơi
Trang 52.4 Đánh giá tính hiệu quả của Doubly Linked List
3 Chương 3: Lí thuyết thứ ba: Bubbe Sort
3.1 Giải thích kiểu dữ liệu Bubble Sort
3.1.1 Định nghĩa 3.1.2 Đặc điểm
3.2 Ứng dụng Bubble Sort vào trong game Tháp Hà Nội
3.2.1 Lí do chọn Bubble Sort 3.2.2 Cấu trúc của Bubble Sort trong game Tháp Hà Nội
3.3 Phân tích và triển khai Bubble Sort trong game Tháp Hà Nội
3.4 Đánh giá tính hiệu quả của Doubly Linked List
4 Chương 4: Lí thuyết thứ tư: Linear Search
4.1 Giải thích kiểu dữ liệu Linear Search
4.1.1 Định nghĩa 4.1.2 Đặc điểm
4.2 Ứng dụng Linear Search vào trong game Tháp Hà Nội
4.2.1 Lí do chọn Linear Search 4.2.2 Cấu trúc của Linear Search trong game Tháp Hà Nội
4.3 Phân tích và triển khai Linear Search trong game Tháp Hà Nội 4.4 Đánh giá tính hiệu quả của Linear Search
Trang 61 TÍNH CẤP THIẾT CỦA ĐỀ TÀI
Trò chơi Tháp Hà Nội là một trò chơi toán học cổ điển, lâu đời và nổi tiếng với tính thửthách về tư duy logic và khả năng giải quyết vấn đề Trò chơi này đòi hỏi người chơi dichuyển các đĩa từ cột này sang cột khác theo quy tắc kích thước đĩa trên phải bé hơn kíchthước đĩa dưới, giúp phát triển kỹ năng tư duy thuật toán và quản lý dữ liệu Việc xây dựngtrò chơi Tháp Hà Nội trong lập trình không chỉ giúp hiểu rõ về cấu trúc dữ liệu stack mà cònnâng cao khả năng phân tích, thiết kế thuật toán Đề tài này có ý nghĩa cấp thiết đối với sinhviên để củng cố kiến thức về cấu trúc dữ liệu, đồng thời hỗ trợ phát triển các ứng dụng logicliên quan đến quản lý bộ nhớ và thứ tự xử lý dữ liệu
2 ĐỐI TƯỢNG, PHẠM VI NGHIÊN CỨU
Đối tượng nghiên cứu: Đối tượng của nghiên cứu là thuật toán và cấu trúc dữ liệu stack,
cùng với các phương pháp áp dụng stack vào việc triển khai và giải quyết bài toán Tháp HàNội
Phạm vi nghiên cứu: Đề tài sẽ tập trung vào việc lập trình trò chơi Tháp Hà Nội bằng
ngôn ngữ C++, sử dụng cấu trúc dữ liệu stack Phạm vi nghiên cứu sẽ bao gồm cả các thuậttoán cơ bản để di chuyển đĩa giữa các cột và thuật toán tối ưu cho các chức năng khác của tròchơi
3 PHÂN TÍCH HƯỚNG NGHIÊN CỨU LIÊN QUAN
Trong lĩnh vực khoa học máy tính, stack là một cấu trúc dữ liệu quan trọng, hoạt độngtheo nguyên tắc "LIFO" (Last In, First Out) hoặc “FILO” (First in, Last out) Stack thườngđược dùng trong các ứng dụng yêu cầu quản lý thứ tự thực hiện như lưu trữ biến, xử lý đệ
Trang 7quy, và quản lý bộ nhớ Trong trò chơi Tháp Hà Nội, stack giúp lưu trữ các đĩa theo thứ tự từlớn đến nhỏ, phù hợp với yêu cầu bài toán Đề tài này sẽ nghiên cứu cách áp dụng stack đểgiải quyết bài toán, từ đó mở rộng kiến thức và kỹ năng về các ứng dụng thực tiễn của stacktrong phát triển trò chơi và giải thuật
4 KẾT QUẢ DỰ KIẾN
Sau khi hoàn thành nghiên cứu, đề tài dự kiến đạt được các kết quả sau:
Xây dựng trò chơi Tháp Hà Nội bằng ngôn ngữ C++ với giao diện Console, giúp người dùng
dễ dàng thao tác và nắm rõ cách hoạt động của trò chơi
Triển khai và tối ưu hóa thuật toán giải quyết bài toán Tháp Hà Nội tự động sử dụng cấu trúc
stack
Tích hợp các tính năng tính điểm và hiển thị thời gian thực để đánh giá hiệu quả và thành
tích của người chơi
Cung cấp tài liệu hướng dẫn sử dụng và mã nguồn chi tiết để phục vụ cho các lập trình viên
và người nghiên cứu khác, hỗ trợ họ trong việc phát triển các ứng dụng liên quan đến stack vàthuật toán giải bài toán logic
Trang 84.1.2 Đặc điểm
Stack có 2 thao tác cơ bản: thêm phần tử vào được gọi là push, xoá phần tử ra được gọi là pop Việc cố gắng pop một stack trống được gọi là underflow và cố gắng đẩy một phần tử trong một stack đầy được gọi là overflow Khi những điều trên xảy ra,chúng ta gọi chúng là exceptions (ngoại lệ)
Ví dụ: Hình ảnh sau minh họa cách hoạt động của ngăn xếp (stack) với các thao tác cơ bản "push" (đẩy) và "pop" (lấy ra)
Bước 1: Trạng thái ban đầu của ngăn xếp bao gồm các phần tử lần lượt từ dưới lên là
A, B, và C Phần tử trên cùng là C và được đánh dấu bằng top
Bước 2: Pushing D, ở bước này ta thực hiện thao tác "push" phần tử D vào ngăn xếp
Phần tử D sẽ được thêm vào vị trí trên cùng, và con trỏ top sẽ được cập nhật để chỉ phần tử D Bây giờ, ngăn xếp có thứ tự từ dưới lên là A, B, C, và D
Bước 3: Popping , ở bước này ta thực hiện thao tác "pop", nghĩa là lấy phần tử D ra
khỏi ngăn xếp Sau khi "pop" phần tử D, con trỏ top sẽ quay về chỉ phần tử C, và phần
tử D không còn trong ngăn xếp nữa Hiện tại, ngăn xếp trở về trạng thái ban đầu với các phần tử A, B, và C
Trang 94.2.2 Nguyên lý hoạt động của stack ở các cột
Mỗi cột trong tháp Hà Nội tương ứng với một ngăn xếp (stack) độc lập, các đĩađược đặt lên các stack theo thứ tự từ lớn đến nhỏ với đĩa lớn nhất ở dưới và đĩa nhỏnhất ở trên cùng Ngăn xếp “source” chứa tất cả các đĩa ban đầu, “temp” là cột giữa,
“target” là cột đích Mỗi thao tác di chuyển đĩa giữa các cột thực chất là các thao tácpush và pop trên các ngăn xếp này
Ví dụ: Khi di chuyển một đĩa từ cột A sang cột B thì đĩa ở đỉnh cột A là đĩa nhỏ nhấtđược lấy ra (pop) khỏi cột A và được đưa vào (push) cột B trở thành đĩa đỉnh ở cột B.Thực hiện tương tự giữa cột A và C, cột B và cột C cho đến khi các đĩa ở cột A dichuyển hết đến cột C
4.2.3 Tối ưu hoá thuật toán
Để tối ưu hoá thuật toán, việc sử dụng thuật toán đệ quy kết hợp với stack làmột cách tiếp cận hiệu quả vì thuật toán đệ quy giúp chia bài toán lớn thành các bàitoán con nhỏ hơn, dễ giải quyết Đồng thời việc kết hợp thuật toán đệ quy cùng vớicác thao tác push và pop trên stack giúp tối ưu hóa thời gian thực thi, đặc biệt khi sốlượng đĩa lớn
4.3 Phân tích và triển khai Stack trong game Tháp Hà Nội
4.3.1 Triển khai code
Để xây dựng trò chơi Tháp Hà Nội dùng Stack, ta định nghĩa một cấu trúcStack với các phương thức cần thiết để quản lý các đĩa trên từng cột
Cấu trúc Stack bao gồm các thuộc tính:
MAX: số lượng đĩa tối đa có thể lưu trữ.
top: chỉ số của phần tử đầu trong stack.
count: số lượng phần tử hiện tại trong stack.
Trang 10a: mảng lưu trữ các đĩa.
Các phương thức quan trọng trong Stack:
isEmpty(): kiểm tra stack có rỗng hay không, trả về true nếu rỗng.
isFull(): kiểm tra stack có đầy hay không, trả về true nếu đầy.
push(int value): thêm một đĩa vào stack, trả về true nếu thêm thành công, false nếu
stack đầy
pop(int* n): lấy một đĩa khỏi stack, lưu giá trị của đĩa vào biến n, trả về true nếu lấy
thành công, false nếu stack rỗng
peek(): lấy giá trị đĩa trên cùng mà không xóa khỏi stack.
getLength(): trả về số lượng đĩa hiện tại trong stack.
getData(): trả về chuỗi chứa các đĩa trong stack, phục vụ cho việc hiển thị trên giao
diện
Phần xử lý game nằm trong hàm towerOfHanoi, sử dụng thuật toán đệ quy để dichuyển đĩa giữa các cột Hàm này gọi đệ quy để di chuyển các đĩa nhỏ hơn từ cộtnguồn qua cột tạm, sau đó di chuyển đĩa lớn nhất từ cột nguồn sang cột đích, và cuốicùng di chuyển các đĩa nhỏ từ cột tạm qua cột đích
4.3.2 Phân tích thuật toán đệ quy và stack
Thuật toán Tháp Hà Nội dựa trên ý tưởng đệ quy, chia bài toán lớn thành cácbài toán nhỏ hơn Nếu số lượng đĩa là n, bài toán được chia làm ba bước:
Bước 1: Di chuyển n-1 đĩa từ cột nguồn (source) sang cột tạm (temp) bằng cột đích
(target)
Bước 2: Di chuyển đĩa lớn nhất (đĩa thứ n) từ cột nguồn sang cột đích.
Bước 3: Di chuyển n-1 đĩa từ cột tạm sang cột đích bằng cột nguồn.
Stack giúp triển khai thuật toán này dễ dàng hơn nhờ nguyên lý LIFO (Last in, Firstout) Khi đĩa được di chuyển giữa các cột, thao tác pop và push của stack sẽ giúp thựchiện các di chuyển theo đúng thứ tự và bảo toàn tính chính xác của bài toán
Trang 114.4 Đánh giá tính hiệu quả
Việc sử dụng stack cho phép quản lý các đĩa một cách rõ ràng và hiệu quả, tuânthủ chặt chẽ nguyên tắc "LIFO" để đảm bảo không có đĩa nào bị xếp sai vị trí Mỗithao tác push và pop đều có độ phức tạp thời gian O(1), đảm bảo hiệu quả khi thao tácvới số lượng đĩa lớn Về thuật toán, độ phức tạp của thuật toán Tháp Hà Nội làO(2^n), do số lần di chuyển tăng theo cấp số nhân với mỗi đĩa thêm vào Điều nàylàm cho bài toán trở nên phức tạp với n lớn Tuy nhiên, việc sử dụng stack vẫn giúp tổchức và quản lý các thao tác này một cách dễ hiểu, đơn giản và trực quan
Với sự kết hợp giữa thuật toán đệ quy và cấu trúc stack, chương trình Tháp Hà Nộiđược xây dựng không chỉ tối ưu về logic, thuật toán mà còn dễ sửa lỗi, nâng cấp chocác tính năng mới của trò chơi trong tương lai
5 LÍ THUYẾT THỨ 2: DOUBLE LINKED LIST
5.1 Giải thích double linked list
5.1.1 Định nghĩa
Danh sách liên kết đôi (Doubly Linked List) là một biến thể của danh sách liênkết đơn (Singly Linked List), trong đó hoạt động duyệt qua các nút có thể được thựchiện theo hai chiều: về trước và về sau một cách dễ dàng khác với danh sách liênkết đơn Mỗi nút trong danh sách chứa ba thành phần cơ bản gồm: Value (giá trị dữliệu), Next (con trỏ tới nút tiếp theo) và Prev (con trỏ tới nút trước nó)
5.1.2 Đặc điểm
Danh sách liên kết đôi có hai con trỏ đặc biệt được gọi là Head và Tail
Con trỏ Head trỏ đến nút đầu tiên còn con trỏ Tail thì trỏ đến nút cuối cùng
Mỗi nút mang một trường dữ liệu và hai trường được gọi lần lượt là Next và Prev.Mỗi nút được liên kết với phần tử kế tiếp bởi sử dụng con trỏ Next
Mỗi nút được liên kết với phần tử phía trước bởi sử dụng con trỏ Prev
Nút cuối cùng luôn trỏ Next tới NULL để đánh dấu là phần tử cuối của danh sách.Nút đầu tiên luôn trỏ Prev tới NULL để đánh dấu là phần tử đầu của danh sách
Trang 125.2 Ứng dụng Double Linked List vào đề tài
5.2.1 Lý do chọn Double Linked List
Trong trò chơi Tháp Hà Nội, việc quản lý danh sách người chơi (Player) là mộtphần quan trọng để lưu trữ, sắp xếp và truy xuất các thông tin như tên người chơi, sốlượng đĩa đã di chuyển, số bước di chuyển, thời gian hoàn thành, và điểm số DoubleLinked List là một cấu trúc dữ liệu phù hợp để thực hiện các thao tác này, giúp quản lýdanh sách dễ dàng, hiệu quả và linh hoạt hơn Ngoài ra việc có con trỏ Prev và Nextgiúp dễ dàng duyệt danh sách theo cả hai chiều, thuận lợi cho việc sắp xếp và tìmkiếm
5.2.2 Cấu trúc Double Linked List ứng dụng vào quản lý danh sách người chơi
Cấu trúc DoubleListPlayer được triển khai với hai con trỏ đặc biệt head và tail,lần lượt trỏ đến phần tử đầu tiên và phần tử cuối cùng của danh sách người chơi Mỗinút trong danh sách bao gồm một cấu trúc Player, lưu các thông tin về người chơi, vàcác con trỏ prev và next giúp di chuyển qua lại giữa các nút trong danh sách Từ đóthực hiện các chức năng khác như thêm người chơi, sắp xếp thứ hạng, tìm kiếm thôngtin người chơi, lưu trữ và đọc dữ liệu file
5.3 Nguyên lý hoạt động của Doubly Linked List trong quản lý danh sách người chơi
5.3.1 Cấu trúc Node
struct Player { string name;
Node* prev;
Node* next;
Trang 13Mỗi Node trong danh sách chứa dữ liệu của người chơi (Player) (tên, số đĩa, số bước,thời gian, điểm) Con trỏ Prev đến node trước đó còn con trỏ Next đến node tiếp theo
5.3.2 Quản lý danh sách
Sử dụng hai con trỏ head và tail để quản lý danh sách Còn biến count để theo dõi sốlượng người chơi
5.3.3 Khởi tạo node mới
Cấp phát bộ nhớ động cho node mới và gán địa chỉ của nó cho con trỏ p Đồng thời khởi tạo các con trỏ prev và next là NULL
5.3.4 Thêm người chơi mới
struct DoubleListPlayer { Node* head = NULL;
Node* tail = NULL;
}
Trang 14Quy trình thêm người chơi mới: Đầu tiên tạo node mới chứa thông tin người chơi sau đó kiểmtra danh sách nếu rỗng thì node mới trở thành cả head và tail còn nếu không rỗng thì nối tail hiện tại với node mới và cập nhập tail mới lên node mới tạo Mỗi lần thêm người chơi mới thìcount sẽ được cập nhập
Trang 15Đầu tiên tạo một con trỏ p trỏ tới head nếu p khác rỗng thì duyệt qua toàn bộ danh sách cho đến khi p NULL thì dừng Tiếp theo lưu thông tin của người chơi (name,disks,moves,time,score) vào file txt Sau đó di chuyển con trỏ p sang node tiếp theo trong danh sách, điều này giúp duyệt qua từng node cho đến khi bằng NULL
Trang 16Quy trình sắp xếp: Đầu tiên gán một con trỏ p đến head, tạo một vòng lặp ngoài duyệt qua từng node trong danh sách, từ head cho đến node gần cuối Vòng lặp dừng khi p lànode cuối cùng, vì ở node cuối không cần so sánh thêm Tiếp theo gán con trỏ q ngay sau p rồi thực hiện vòng lặp từ q cho đến phần tử cuối cùng của danh sách Điều này giúp đảm bảo p luôn được so sánh với các phần tử đứng sau nó Tiếp đến là các điều kiện so sánh: Nếu p có số đĩa ít hơn q hoặc nếu p có số đĩa bằng q thì ta xét đến điều kiện tiếp theo là số điểm Xong phần điều kiện thì ta hoán đổi bằng cách gán dữ liệu p cho biến tạm (temp), sau đó gán dữ liệu của q cho dữ liệu p và cuối cùng gán temp cho
dữ liệu của q
5.3.7 Tìm kiếm người chơi
void linearSearch(string name, MyWindows console) {
Trang 17Duyệt từ đầu danh sách So sánh tên người chơi cần tìm với từng người chơi trong danh sách Nếu tìm thấy thì check bằng True
5.4 Đánh giá tính hiệu quả của Doubly Linked List
Với việc sử dụng Doubly Linked List vào trong việc quản lý danh sách người chơi làhợp lý vì cấu trúc này có thể thao tác thêm/xoá nhanh chóng và dễ dàng cập nhập, duy trìdanh sách Đồng thời cấu trúc này còn tối ưu truy xuất khi tìm kiếm hay sắp xếp thông tinngười chơi Tóm lại việc áp dụng cấu trúc dữ liệu liên kết kép (Doubly Linked List) vàoquản lý danh sách người chơi là một sự lựa chọn phù hợp và lý tưởng vì nó đáp ứng tốtcác yêu cầu về lưu trữ và xử lý thông tin trong game đồng thời còn dễ dàng mở rộng vàphát triển thêm các tính năng mới
6 LÍ THUYẾT THỨ 3: BUBBLE SORT
6.1. Giải thích Bubble sort
6.1.1 Định nghĩa
Sắp xếp nổi bọt là một giải thuật sắp xếp đơn giản Giải thuật sắp xếp này được tiến hành dựa trên việc so sánh cặp phần tử liền kề nhau và tráo đổi thứ tự nếu chúng không theo thứ tự
6.1.2 Đặc điểm
Bubble sort là thuật toán sắp xếp đơn giản, dễ hiểu và dễ thiết lập với độ phứctạp O(n^2) Mặc dù thuật toán này không phù hợp với các kích thước dữ liệu lớnnhưng rất phù hợp với các kích thước dữ liệu nhỏ hoặc vừa Bubble sort hoạt độngbằng cách lặp lại so sánh và hoán đổi các phần tử liền kề nếu chúng không theo thứ tựmong muốn Cứ như vậy sau mỗi vòng lặp phần tử lớn nhất sẽ được đẩy về cuối Điềunày làm cho các vòng lặp sau đó không cần phải xét đến phần tử này nữa, thuật toánkết thúc khi không còn phần tử nào cần được hoán đổi nữa