Tải bản đầy đủ (.pdf) (5 trang)

Thuật toán xây dựng mê cung

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (50.46 KB, 5 trang )

VIETBOOK

Thuật toán xây dựng mê cung
Trong thần thoại Hi Lạp, có con quỷ Minôto rất hung dữ, ở trong một hang sâu. Đờng đi vào hang là một
mê cung, ai có can đảm vào diệt quỷ thì cũng không dễ gì lần đợc vào hang quỷ mà còn có thể bị lạc,
không tìm đợc lối ra. Ngời anh hùng Têzê đã liều mình vào hang quỷ. Để giúp anh, nàng Arian đã đa
cho Têzê một cuộn chỉ và nàng cầm đầu mối. Khi vào mê lộ thì Têzê kéo dần cuộn chỉ, đến lúc quay về thì
chỉ cần cuốn chỉ lại để lần theo đó mà ra khỏi mê cung.
Chuyện thần thoại thì là nh vậy, còn mê cung thì đã hấp dẫn nhiều nhà kiến trúc, hoạ sĩ, thi sĩ trong hàng
chục thế kỉ qua. Các nhà toán học cũng chú ý đến mê cung vì nó mang nhiều ý nghĩa sâu sắc liên quan đến
nhiều ngành của toán học hiện đại. Ngay trong cuộc sống thờng ngày, chúng ta cũng thờng gặp mê cung
trong các bài toán đố vui "Tìm đờng trong mê cung". Trong bài báo này tôi xin giới thiệu với các bạn một
thuật toán xây dựng mê cung kích thớc tuỳ ý.
Ta xem tất cả các đờng đi trong mê cung là một tập hợp các ô đợc nối với nhau. Ban đầu tất cả các ô
đều không đợc nối và xung quanh tất cả các ô đều có tờng. Lấy một bức tờng bất kì và kiểm tra xem ô ở
bên kia bức tờng có đợc nối bằng một đờng đi nào đó hay không. Nếu đúng nh vậy, thử một bức tờng
khác. Nếu không thì phá bức tờng và kết hợp hai tập hợp đờng đi với nhau.
Hình: Một mê cung kích thớc 20x20.
Ta dùng một mảng hai chiều để lu lại mê cung xây dựng đợc. Mỗi thành phần của mảng chứa giá trị 1,
2 hoặc bằng 1 or 2, trong đó 1 biểu thị là ô có tờng dọc, còn 2 biểu thị ô có tờng ngang. Ta sử dụng hai
cấu trúc sau để thể hiện tập hợp đờng đi và tập hợp tờng.
PMazeCell = ^MazeCell;
MazeCell = record
(* Trỏ tới đầu danh sách tất cả các ô có thể tới đợc từ ô này.
Dễ dàng nhận biết để có thể so sánh *)
mset:PMazeCell;
(* Trỏ tới ô tiếp theo của tập hợp các ô nối với nhau này *)
next:PMazeCell;
end;
PMazeWall=^MazeWall;
MazeWall = record


(* Có tờng hay không, tờng ngang hay dọc *)
wall:byte;
(* Toạ độ của bức tờng *)
x,y:integer;
end;
Thuật toán xây dựng mê cung:
1. Khởi tạo:
Gọi walls và cells là hai biến trỏ đến mảng tờng và ô. Dễ thấy số ô bằng width*height, còn số bức tờng tối
đa là (width-1)*height+(height-1)*width (không kể các bức tờng nằm ở các cạnh của mê cung). Ta gọi
hàm GetMem để cấp phát bộ nhớ cho hai con trỏ trên. Xây tất cả các bức tờng bởi vậy lúc này hai ô bất kì
không thể đi đến nhau. Với tất cả các ô, khởi tạo trờng mset trỏ tới chính nó, còn trờng next đặt bằng nil
(tập hợp đờng đi chỉ gồm một ô).
2. Đổi chỗ ngẫu nhiên mảng tờng:
Ban đầu mảng tờng đợc sắp xếp theo thứ tự: đầu tiên là các tờng dọc, sau đến là các tờng ngang theo
thứ tự tăng dần của toạ độ x và y. Ta lần lợt chọn các cặp tờng bất kì và đổi chỗ chúng cho nhau để đợc
một mảng tờng ngẫu nhiên.
3. Xoá các bức tờng để nhập các ô vào với nhau:
Bây giờ ta đã có một danh sách ngẫu nhiên các bức tờng. Ta duyệt lại danh sách các bức tờng. Với mỗi
bức tờng, ta chọn ô có toạ độ là toạ độ của bức tờng. Xem xét ô ở phía bên kia bức tờng liệu hai ô có
đợc nối với nhau bằng một đờng nào đó hay không. Điều này có thể kiểm tra bằng một lệnh if nh sau:
if ca^.mset = cb^.mset then
Nếu chúng cha đợc nối thì ta phá bức tờng (đặt trờng wall = 0 ) và nối chúng lại bằng thủ tục
Trang 1


VIETBOOK

ConnectSets:
procedure ConnectSets(mset,add:PMazeCell);
begin

(* Chuyển đến cuối danh sách *)
while mset^.next<>nil do mset:=mset^.next;
(* Trỏ đến đầu danh sách *)
add:=add^.mset;
(* Gắn vào các ô mới *)
mset^.next:=add;
(* Thay đổi tính đồng nhất của các ô mới *)
while add <> nil do
begin
add^.mset:=mset^.mset;
add:=add^.next;
end;
end;
4. Ghi kết quả:
Công việc còn lại bây giờ chỉ là ghi kết quả ra mảng output. Trớc hết ta đặt tất cả các bức tờng ở các cạnh
của mê cung. Và sau đó thì chép các bức tờng từ mảng tờng vào mảng output.
Trên đây là một thuật toán để tạo mê cung, rất mong nhận đợc sự góp ý của các bạn.
Nguyễn Minh Thắng
11A Tin Đại Học Khoa Học Tự Nhiên, Hà Nội
uses crt,graph;
type
PMazeCell = ^MazeCell;
MazeCell = record
mset,next:PMazeCell;
end;
PMazeWall=^MazeWall;
MazeWall = record
wall:byte;
x,y:integer;
end;

const
Vert=1;
Horiz=2;
var
maze:array[0..100,0..100] of byte;
gd,gm,w,h:integer;
procedure ConnectSets(mset,add:PMazeCell);
begin
while mset^.next<>nil do mset:=mset^.next;
add:=add^.mset;
mset^.next:=add;
while add<> nil do
begin
add^.mset:=mset^.mset;
add:=add^.next;
end;
end;
procedure GenerateMaze(width,height:integer);
Trang 2


VIETBOOK

var
cells:PMazeCell;
walls:PMazeWall;
ncells,nwalls:integer;
i,x,y:integer;
ca,cb:PMazeCell;
w,temp:PMazeWall;

wt:MazeWall;
function CellAt(x,y:integer):pointer;
begin
CellAt:=pointer(longint(cells)+(x+y*width)*sizeof(MazeCell));
end;
begin
randomize;
ncells:=width*height;
GetMem(cells,sizeof(MazeCell)*ncells);
nwalls:=(width-1)*height+(height-1)*width;
GetMem(walls,sizeof(MazeWall)*nwalls);
ca:=cells;
for i:=1 to ncells do
begin
ca^.mset:=ca;
ca^.next:=nil;
ca:=pointer(longint(ca)+sizeof(MazeCell));
end;
w:=walls;
for x:=1 to width-1 do
for y:=0 to height-1 do
begin
w^.wall:=Vert;
w^.x:=x;
w^.y:=y;
w:=pointer(longint(w)+sizeof(mazewall));
end;
for y:=1 to height-1 do
for x:=0 to width-1 do
begin

w^.wall:=Horiz;
w^.x:=x;
w^.y:=y;
w:=pointer(longint(w)+sizeof(MazeWall));
end;
for i:=nwalls-1 downto 1 do
begin
w:=pointer(longint(walls)+random(i)*sizeof(MazeWall));
wt:=w^;
temp:=pointer(longint(walls)+i*sizeof(MazeWall));
w^:=temp^;
temp^:=wt;
end;
w:=walls;
for i:=0 to nwalls-1 do
begin
Trang 3


VIETBOOK

ca:=CellAt(w^.x,w^.y);
if w^.wall=Horiz then
cb:=CellAt(w^.x,w^.y-1)
else
cb:=CellAt(w^.x-1,w^.y);
if ca^.mset <> cb^.mset then
begin
ConnectSets(ca,cb);
w^.wall:=0;

end;
w:=pointer(longint(w)+sizeof(mazewall))
end;
FillChar(maze,SizeOf(maze),0);
for x:=0 to width-1 do
begin
maze[x,0]:=Horiz;
maze[x,height]:=Horiz;
end;
for y:=0 to height-1 do
begin
maze[0,y]:=Vert;
maze[width,y]:=Vert;
end;
w:=walls;
for i:=0 to nwalls-1 do
begin
maze[w^.x,w^.y]:=maze[w^.x,w^.y] or w^.wall;
w:=pointer(longint(w)+sizeof(mazewall))
end;
FreeMem(cells,sizeof(MazeCell)*ncells);
FreeMem(walls,sizeof(MazeWall)*nwalls);
end;
procedure TestMaze;
var
i,j:integer;
ch:char;
begin
clrscr;
Writeln('Hay nhap vao kich thuoc me cung');

Write('Rong (<=55): ');readln(w);
Write('Cao (<=55): ');readln(h);
gd:=detect;
InitGraph(gd,gm,'');
Inc(w);Inc(h);
repeat
cleardevice;
GenerateMaze(w,h);
MoveTo((GetMaxX-(w-1)*8) div 2,(GetMaxY-(h-1)*8) div 2);
Rectangle(GetX,GetY,GetX+(w-1)*8,GetY+(h-1)*8);
for i:=1 to w-1 do
for j:=1 to h-1 do
begin
if (maze[i,j] and Vert)<>0 then
Trang 4


VIETBOOK

Line(GetX+i*8,GetY+(j-1)*8,GetX+i*8,GetY+j*8);
if (maze[i,j] and Horiz)<>0 then
Line(GetX+(i-1)*8,GetY+j*8,GetX+i*8,GetY+j*8);
end;
repeat
ch:=readkey;
until (ch=#27) or (ch=#13);
until ch=#27;
end;
begin
TestMaze;

end.

Trang 5



×