Szukanie najkrótszej drogi - SEGMENTATION ERROR

0

Witam, mam problem odnośnie szukanie najkrótszej drogi. Potrzebne mi to jest do gry snake, do duszków, które będą usiłować zjeść papu przed nami. Oto problematyczny kawałek kodu. Problem tkwi w tym, że debugger nie chce za pomocą trace into wejść wewnątrz wewnątrz funkcji makeRoad..

Juz objaśniam koncepcję - za pomocą makeGraph tworzę graf reprezentowany jako macierz odległości. Wszystkie punkty z mapy to wierzchołki grafu i układam je w kolejności wg. wzoru c= x + (y-1) *78 (78 punków w wierszu).
W tablicy map[][] mam przechowane współrzędne przeszkód i teleportów. (map[][1] to współrzędne x, map[][2] to y, map[1][], i map[2][] to są dwa teleporty, dalej są przeszkody, kończę gdy spotkam wartość 99).

Mogę liczyć na pomoc?

type
	matrix = array[1..300] of array[1..2] of integer; //do wczytywania plansz
	psnake=^snake;//waz
	point=record //jedzenie
		x,y:integer;
		typ:string;
	end;
	snake=record 
		x,y:integer;
		next,prev:psnake;
	end;
	graf=array[1..3354]of array[1..3354] of integer; //graf do dijkstry, 3354-ilosc pol wewnatrz planszy
	distances=array[1..3354]of integer; //wektor odleglosci od głowy do punktów distances[i] wg wzoru nr wieszcholka c=x+(y-1)*78;
	queue=array[1..3354]of boolean;//za pomoca tego wierzchołki przez które już przeszedłem  false-juz byl, true w kolejce
	pmoves=^moves;
	moves=record//kolejne pola do ruchu dla ai
		x,y:integer;
		next,prev:pmoves;
	end;

function dijkstra(map:matrix;head:psnake;food:point;ggraf:graf):distances;//zwraca tablice najkrotszych drog do wszystkich punktow
var 
	D:distances;
	Q:queue;
	i,j,c,min:integer;

function isEmpty(Q:queue):boolean;//czy sa jeszcze punkty do badania
var
	i:integer;
begin
	isEmpty:=true;
	i:=1;
	while isEmpty do begin
		if Q[i] then isEmpty:=false;
		i:=i+1;
	end;
end;

function minimum(D:distances):integer;//zwraca indeks i dla ktorego D[i] jest najmniejsze (obecny najkrotszy wektor)
var 
	i:integer;
begin
	minimum:=0;
	i:=1;
	while minimum=0 do begin
		if Q[i]=true then minimum:=i;
		i:=i+1;
	end;
	for i:=1 to 3354 do
		if (Q[i]=true) and (D[i]<D[minimum]) then minimum:=i;
end;

begin
	c:=head^.x+(head^.y-1)*78;//nr kol. glowy weza - wieszcholka poczatkowego
	for i:=1 to 3354 do begin //ustawiam zbior punktow Q, oraz poczatkowe odleglosci D
		D[i]:=ggraf[c][i];
		if i<>c then Q[i]:=true
		else Q[i]:=false;
	end;
		
	while not isEmpty(Q) do begin
		min:=minimum(D);
		Q[min]:=false;
		
		for i:=1 to 3354 do begin
			if ggraf[min][i]=1 then
				if D[i]>D[min]+1 then D[i]:=D[min]+1;
		end;
	end;
	dijkstra:=D;
end;

function makeRoad(map:matrix;head:psnake;food:point):pmoves;
var 
	q:pmoves;
	D:distances;
	c:integer;
	ggraf:graf;
	
function makeGraf(map:matrix;head:psnake):graf;
var
	x,y,c:integer;
function obstacle(x,y:integer):boolean;
var
	i:integer;
	q:psnake;
begin
	obstacle:=false;
	i:=3;
	while map[i][1]<>99 do begin
		if (map[i][1]=x) and (map[i][2]=y) then obstacle:=true;
		i:=i+1;
	end;
	q:=head;
	while q<>nil do begin
		if (q^.x=x) and (q^.y=y) then obstacle:=true;
		q:=q^.next;
	end;	
end;
begin
	{c = x+(y-1)*78(max_x-2)
	 x=c mod 78
	 y=c div 78  +1
	}
	//ustawiam graf na domyslna wersje - zero powiazan i 0 na przekatnej
	for x:=1 to 3354 do
		for y:=1 to 3354 do
			makeGraf[x][y]:=INF;
	for x:=1 to 3354 do
		makeGraf[x][x]:=0;
	//teraz zalatwiam srodkowa czesc
	for x:=2 to 77 do begin
		for y:=2 to 42 do begin
			c:=x+(y-1)*78;
			if not obstacle(x-1,y+1) then makeGraf[c][x-1+(y)*78]:=1;
			if not obstacle(x,y+1) then makeGraf[c][x+(y)*78]:=1;
			if not obstacle(x+1,y+1) then makeGraf[c][x+1+(y)*78]:=1;
			if not obstacle(x-1,y) then makeGraf[c][x-1+(y-1)*78]:=1;
			if not obstacle(x+1,y) then makeGraf[c][x+1+(y-1)*78]:=1;
			if not obstacle(x-1,y-1) then makeGraf[c][x-1+(y-2)*78]:=1;
			if not obstacle(x,y-1) then makeGraf[c][x+(y-2)*78]:=1;
			if not obstacle(x+1,y-1) then makeGraf[c][x+1+(y-2)*78]:=1;
		end;
	end;
	//teraz wypelniam 4 krawedzie
	for x:=1 to 78 do begin
		if not obstacle(x,0) then makeGraf[x][x+42*78]:=1;
		if not obstacle(x,44) then makeGraf[x+42*78][x]:=1;
	end;
	for y:=1 to 43 do begin
		if not obstacle(0,y) then makeGraf[1+(y-1)*78][78+(y-1)*78]:=1;
		if not obstacle(79,y) then makeGraf[78+(y-1)*78][1+(y-1)*78]:=1;
	end;
	//pora i na teleporty
	if not obstacle(map[2][1],map[2][2]+1) then makeGraf[map[1][1]+(map[1][2]-2)*78][map[2][1]+(map[2][2])*78]:=1;
	if not obstacle(map[2][1]-1,map[2][2]) then makeGraf[map[1][1]+1+(map[1][2]-1)*78][map[2][1]-1+(map[2][2]-1)*78]:=1;
	if not obstacle(map[2][1],map[2][2]-1) then makeGraf[map[1][1]+(map[1][2])*78][map[2][1]+(map[2][2]-2)*78]:=1;
	if not obstacle(map[2][1]+1,map[2][2]) then makeGraf[map[1][1]-1+(map[1][2]-1)*78][map[2][1]+1+(map[2][2]-1)*78]:=1;
	
	if not obstacle(map[1][1],map[1][2]+1) then makeGraf[map[2][1]+(map[2][1]-2)*78][map[1][1]+(map[1][2])*78]:=1;
	if not obstacle(map[1][1]-1,map[1][2]) then makeGraf[map[2][1]+1+(map[2][1]-1)*78][map[1][1]-1+(map[1][2]-1)*78]:=1;
	if not obstacle(map[1][1],map[1][2]-1) then makeGraf[map[2][1]+(map[2][1])*78][map[1][1]+(map[1][2]-2)*78]:=1;
	if not obstacle(map[1][1]+1,map[1][2]) then makeGraf[map[2][1]-1+(map[2][1]-1)*78][map[1][1]+1+(map[1][2]-1)*78]:=1;
end;
	
begin
	ggraf:=makeGraf(map,head);
	D:=dijkstra(map,head,food,ggraf);
	new(q);
	q^.next:=nil;
	q^.prev:=nil;
	q^.x:=food.x;
	q^.y:=food.y;
	
	while (q^.x<>head^.x) or (q^.y<>head^.y) do begin
		c:=1;
		while (D[c]<>D[q^.x+(q^.y-1)*78]-1) or (ggraf[q^.x+(q^.y-1)*78][c]<>1) do c:=c+1;
		new(q^.prev);
		q^.prev^.next:=q;
		q:=q^.prev;
		q^.prev:=nil;
		q^.x:=c mod 78;
		q^.y:=(c div 78)+1;
	end;
	makeRoad:=q;
end; 
0

Problem tkwi w tym, że debugger nie chce za pomocą trace into wejść wewnątrz wewnątrz funkcji makeRoad..

To nie on nie może wejść, tylko ty masz problem żeby go obsługiwać...
Skoro nawet nie powiedziałeś co to za debugger to wróżę z fusów: naciśnij Alt+F12

queue=array[1..3354]of boolean;

też lubię marnować pamięć no ale może bez przesady? Dynamic array?

''if not obstacle(map[2][1],map[2][2]+1) then makeGraf[map[1][1]+(map[1][2]-2)*78][map[2][1]+(map[2][2])*78]:=1;
if not obstacle(map[2][1]-1,map[2][2]) then makeGraf[map[1][1]+1+(map[1][2]-1)*78][map[2][1]-1+(map[2][2]-1)*78]:=1;
if not obstacle(map[2][1],map[2][2]-1) then makeGraf[map[1][1]+(map[1][2])*78][map[2][1]+(map[2][2]-2)*78]:=1;
if not obstacle(map[2][1]+1,map[2][2]) then makeGraf[map[1][1]-1+(map[1][2]-1)*78][map[2][1]+1+(map[2][2]-1)*78]:=1;''

Twój kod jest bardziej nieczytelny niż mój bot do warcab (który jak myślałem jest już nieczytelny na maxa).

poza tym nie opisałeś w żaden sposób problemu. Śmiem wątpić że komukolwiek będzie się chciało zagłębiać w twój problem, zwłaszcza że nawet nie napisałeś co i jak.
Na twoim miejscu spróbowałbym ogarnąć debugger i dodał zabezpieczenia kodu, to mi pomogło przy bocie do warcab.

0

Kompiluję i debuguję we free pascalu. Może i nie umiem się obsługiwać debuggerem, ale umiem użyć step over/trace into. Trace into nie wchodzi 'do środka' funkcji makeRoad (a z tego co wiem, to powinien wejść i skończyć na linii która wywala. popraw mnie jeśli się mylę). Walę F7 (trace into) w momencie kiedy ma być wykonana funkcja makeRoad. Debugger wypisuje segmentation error nie prubując wykonać ani jednej linii wyżej wspomnianej funkcji.

Kod może jest nieczytelny, ale i tak jestem z siebie zadowolony, nie mam doświadczenia w programowaniu, to jest pierwszy projekt który ma więcej niż 150 linii.

queue=array[1..3354]of boolean

, marnotrawstwo pamięci? 3354 bajty to chyba nie dużo. Poza tym tyle mam wierzchołków w kolejce do obejścia i dynamiczna tablica (są takie w pascalu?) i tak na początku musiała by przechowywać 3354 elementy. Lub na końcu.

edit: po głębszej analizie i kilku zmianach doszedłem do wniosku, że funkcja makeGraf jest winna. Program wywala w funkcji/procedurze w której jest wywołana funkcja makeGraf (tak, jak pisałem wyżej od razu, nie dochodzi do tej funcji, nawet trace into. wywala napotykając procedure/funkcję w której makeGraf jest wywołana).
Konsoli pojawia się błąd runtime error 202, czyli stack overflow (debugger daje segmentation fault). Próbowałem za pomocą dyrektywy {M..} zwiększyć stos, ale to nic nie dało. Jakieś pomysły? Poniżej jest ta funkcja, dołożyłem wszelkich starań aby zrobić ją bardziej czytelną, dodałem komentarze.

//Koncepcja jest taka: mam snake, który ma sam chodzić. Mam planszę, każdy z punktów to wierzchołek grafu, który będzie mi potrzebny do znalezienia
//najkrótszej drogi. Dalej tak, jak w kodzie w pierszym poście używam Dijkstry, a potem ukladam drogę i zwracam ją jako listę jednokierunkową.
//Układam macierz odległości między wieszchołkami grafu:

function makeGraf(map:matrix;head:psnake):graf;
var
	x,y,c:integer;
	G:graf;
	
procedure makecon(x1,y1,x2,y2:integer); //nadaje w grafie powiazania pomiedzy wierzchołkami (x1,y1) i (x2,y2) (jeśli to możliwe). wierzchołki to są punkty na planszy
	function makeC(x,y:integer):integer; //zamieniam punkt (x,y) na jego nr porz. potrzebne do dijkstry, muszę mieć "listę" wszystkich wierzchołków po kolei
	begin
		makeC:=x+(y-1*78);
	end;
	
	function obstacle():boolean; //sprawdzam czy ktorys z punktow (x1,y1),(x2,y2) nie jest przeszkodą. przeszkody są zapisane
	var										//w map[][], gdzie map[][1] to wsp. x, a map[][2] to y. pierwsze dwie pozycje to teleporty
		i:integer;
		q:psnake;
	begin
		obstacle:=false;
		i:=1;
		while map[i][1]<>99 do begin
			if (map[i][1]=x1) and (map[i][2]=y1) then obstacle:=true;
			if (map[i][1]=x2) and (map[i][2]=y2) then obstacle:=true;
			i:=i+1;
		end;
		q:=head;
		while q<>nil do begin
			if (q^.x=x1) and (q^.y=y1) then obstacle:=true;
			if (q^.x=x2) and (q^.y=y2) then obstacle:=true;
			q:=q^.next;
		end;
		if (not x1 in [1..78]) or (not y1 in [1..43]) or (not x2 in [1..78]) or (not y2 in [1..43]) then obstacle:=true; //dodaje opcje, aby nie nadawac polaczen z liniami granicznymi
	end;																												//albo wychodzacymi poza tablice
	
begin
	if (not obstacle()) then G[makeC(x1,y1)][makeC(x2,y2)]:=1;
end;

begin
	{c = x+(y-1)*78(max_x-2)
	 x=c mod 78
	 y=c div 78  +1
	}
	//ustawiam graf na początkową wersje - zero powiazan, odległosci punktow pomiedzy nimi samymi to 0 (przekątna)
	for x:=1 to 3354 do
		for y:=1 to 3354 do
			G[x][y]:=INF;
	for x:=1 to 3354 do
		G[x][x]:=0;
	//nadaje polaczenia, oprocz teleportow i przejsc przez sciany
	for x:=1 to 78 do begin
		for y:=1 to 43 do begin
			makecon(x,y,x,y-1);
			makecon(x,y,x+1,y);
			makecon(x,y,x,y+1);
			makecon(x,y,x-1,y);
		end;
	end;
	//teraz wypelniam 4 krawedzie
	for x:=1 to 78 do begin
		makecon(x,1,x,43);
		makecon(x,43,x,1);
	end;
	for y:=1 to 43 do begin
		makecon(1,y,78,y);
		makecon(78,y,1,y);
	end;
	//teraz nadaje połączenie pomiędzy teleportami
	makecon(map[1][1],map[1][2]-1,map[2][1],map[2][2]+1);
	makecon(map[1][1]+1,map[1][2],map[2][1]-1,map[2][2]);
	makecon(map[1][1],map[1][2]+1,map[2][1],map[2][2]-1);
	makecon(map[1][1]-1,map[1][2],map[2][1]+1,map[2][2]);
	
	makecon(map[2][1],map[2][2]-1,map[1][1],map[1][2]+1);
	makecon(map[2][1]+1,map[2][2],map[1][1]-1,map[1][2]);
	makecon(map[2][1],map[2][2]+1,map[1][1],map[1][2]-1);
	makecon(map[2][1]-1,map[2][2],map[1][1]+1,map[1][2]);
	
	makeGraf:=G;
end; 
1

Konsoli pojawia się błąd runtime error 202, czyli stack overflow (debugger daje segmentation fault). Próbowałem za pomocą dyrektywy {M..} zwiększyć stos, ale to nic nie dało. Jakieś pomysły? Poniżej jest ta funkcja, dołożyłem wszelkich starań aby zrobić ją bardziej czytelną, dodałem komentarze.

FreePascal nie jest ani nie posiada debuggera, debugger posiada IDE FPC (gdb) i Lazarus IDE (również gdb chociaż można podpiąć coś dziwnego). A jeżeli używasz IDE FPC, to jest duża szansa że powinieneś się przerzucić na Lazarus IDE bo FPC ma to do siebie że np. mój projekt wywalało kiedy na Lazarusie (i czystym fp) chodziło... To może poprawić problem z debuggerem, możesz też dać breakpointa w tej procedurze co ci do niej nie wchodzi, a co do problemu:

Skoro stos ci się przepełnia to albo masz straszny poziom zagłębienia albo masz nieskończone zagłębienie. innego wytłumaczenia nie ma. Spróbuj sobie dać jakieś message z informacjami przydatnymi to będzie wszystko łatwiej tracować.

0

Znalazłem linijkę, która powoduje błąd, jednak nie umiem tego nijak wytłumaczyć. Jest to deklaracja zmiennej w funkcji makeRoad - nawet gdy wyciąłem całe ciało funckji wywalało błąd. Błąd pozostawał nawet gdy w funckji jedyną rzeczą do zrobienie była deklaracja zmiennej ggraf:graf. Dlatego debugger nie wskazywał linijki na której się kończy. Dlaczego tak się dzieje? Linijka zaznaczona w kodzie.

e:Tak, używam Free Pascal IDE. Lazarus nie widzi modułu graf(nawet jak dam w ust. adresy tego z fpc)

type
graf=array[1..3354,1..3354] of integer; //graf do dijkstry, 3354-ilosc pol wewnatrz planszy

function makeRoad(var map:matrix;head:psnake;food:point):pmoves; //tu układam ciag punktow wg ktorych ma poruszac sie waz
var 														// w postaci listy 
	q:pmoves;
	D:distances;
	c:integer;
	ggraf:graf; //TU!!!!!!!!!!!! ta linijka powoduje błąd!!!!!!!!!!!!!!!!!! ////////////////////////////////////////////////
	
	
begin
	ggraf:=makeGraf(map,head);
	D:=dijkstra(map,head,food,ggraf);
	new(q);
	q^.next:=nil;
	q^.prev:=nil;
	q^.x:=food.x;
	q^.y:=food.y;
	
	while (q^.x<>head^.x) or (q^.y<>head^.y) do begin
		c:=1;
		while (D[c]<>D[q^.x+(q^.y-1)*78]-1) or (ggraf[q^.x+(q^.y-1)*78][c]<>1) do c:=c+1;
		new(q^.prev);
		q^.prev^.next:=q;
		q:=q^.prev;
		q^.prev:=nil;
		q^.x:=c mod 78;
		q^.y:=(c div 78)+1;
	end;}
	makeRoad:=q;
end; 
0

Bo na stosie alokujesz ogromną tablicę, a stos ma ograniczony rozmiar.
Alokuj to jako tablica dynamiczna (wtedy to idzie na normalną pamięć) lub poza funkcją (wtedy idzie też na normalną pamięć).

I spełniło się to co mówiłem o zużyciu pamięci...

Poczytaj sobie jeszcze o tym jak działa stos, przyda ci się.

0

Dziękuję za pomoc, dzięki temu już kończę mój projekt.

Mam jeszcze jedno pytanie - im bardziej rozbudowywałem program tym częściej zdarza się że program się zawiesi. Na początku snake zawieszał się raz na jakiś czas, dodałem labirynty, trochę częściej, dodałem samochodzącego snake'a i duszki, praktycznie nie ma szans na prezentację gry bez zawieszenia.

Najgorsze jest to, że zawiesza się w różnych momentach - raz podczas inicjalizacji modułu graph, raz podczas ruchu, raz po zjedzeniu pokarmu. I nie jest to wywaklanie z programu, po prostu przestaje się cokolwiek dziać. Bardzo ciężko jest wyłapać ten moment debuggerem (trzeba by powoli przechodzić każdą linijkę bo szybko się nie wyłapie błędu, poza tym tak jak pisałem są to różnie momenty), bo gdy zacina się snake, zacina się cały fpc, zdąży tylko wyświetlić jakiś dziwny exitcode (-1073741510).

Ogólnie takie zawieszenie to chyba wejście w jakąś nieskończoną pętle? Ale dlaczego w losowych momentach..
Czy jest możliwe, że kompilator generuje błędny kod? Próbowałem jeszcze dev pascalem, ale z tego co wiem wszystkie są oparte na fp.

0

Bardzo ciężko jest wyłapać ten moment debuggerem (trzeba by powoli przechodzić każdą linijkę bo szybko się nie wyłapie błędu, poza tym tak jak pisałem są to różnie momenty), bo gdy zacina się snake, zacina się cały fpc, zdąży tylko wyświetlić jakiś dziwny exitcode (-1073741510).

Mówiłem żeby nie używać IDE FPC tylko Lazarusa... Debugger ma możliwość zapauzowania procesu w dowolnym momencie (ten Lazarusowy), wtedy patrzysz na call stack i kombinujesz czemu się zawiesza.

Co do dziwnego exitcoda - normalka :-] IDE FPC nie jest już rozwijane i nie powinieneś go generalnie używać pod Windą gdzie jest Lazarus.

Ogólnie takie zawieszenie to chyba wejście w jakąś nieskończoną pętle? Ale dlaczego w losowych momentach..
Czy jest możliwe, że kompilator generuje błędny kod?

Możliwe, ale tak mało prawdopodobne, że to się tobie nie zdarzyło. :-] Swoją drogą, to piszę teraz bo instaluje mi się nowy FPC bo stary się wywala podczas próby kompilacji mojej biblioteki, więc cóż... jak widać zdarza się, ficzury OpenSource :P .

Jeżeli nie masz wątków a mimo to wydaje ci się losowe, to przede wszystkim, włącz generowanie kodu zabezpieczającego. W Lazarusie w opcjach projektu zaznacz sobie wszystkie debugi i daj optymalizację najlepiej na 1 ew. 0.

poza tym, złym rozwiązaniem było rozwijanie programu mimo crashów. Teraz będzie ciężko powiedzieć co to powoduje, jeżeli debugger nie pomoże to wrzuć nam call stack (+kawałki kodu o które chodzi) i próbuj wyłączać różne części kodu, które mogą być niebezpieczne.

No i najlepiej daj cały kod to odpalę u siebie, ale domyślam się że tego nie będziesz chciał zrobić...

0

Znalazłem sposób na szukanie błędów, okazało się, że nie jest ich tak mało ;)
po prostu w miejscach gdzie podejrzewam że wywala wstawiam linijke zapisującą do pliku ostatnio wykonaną komendę. Wwaliłem trochę tego do kodu i wiem, gdzie wywala. Mam nadzieję że sobie dalej poradzę sam :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1