FORTRAN

Error message

Deprecated function: implode(): Passing glue string after array is deprecated. Swap the parameters in drupal_get_feeds() (line 394 of /var/www/pied-piper.ermarian.net/includes/common.inc).
AuthorTopic: FORTRAN
Law Bringer
Member # 2984
Profile Homepage #0
Since there are already threads for C++ and Java, I might as well add another language. However, this topic is primarily to show off my first working algorithm written entirely in Fortran.

It finds the shortest path through a labyrinth.

The labyrinth can look like this (using rogue-like dungeon notation). So the computer is trying to walk downward through a primitive Angband level, from stairway to stairway:

#<####.##
#..#>.#..
##.####.#
#.##...##
##.##.###
##....###
This is the program:

MODULE NEIGHBOURS
IMPLICIT NONE
CONTAINS
LOGICAL FUNCTION FIND_NEIGHBOUR(MATRIX,FROM,TO)
INTEGER(KIND=SELECTED_INT_KIND(2)),DIMENSION(6,9),INTENT(IN)::MATRIX
INTEGER(KIND=SELECTED_INT_KIND(2)),INTENT(IN)::FROM,TO
INTEGER::I,J,K,L
FIND_NEIGHBOUR=.false.
WRITE(*,*) ' Can target be reached in one step?'
WRITE(*,2000) FROM,TO
2000 FORMAT(' Looking for way from ',I3,' to ',I3)
DO I=1,6
DO J=1,9
IF (MATRIX(I,J)==FROM) THEN
WRITE(*,*) 'Found our current position!'
DO K=-1,1
IF (I+K<1 .or. I+K>6) CYCLE
DO L=-1,1
IF (J+L<1 .or. J+L>9) CYCLE
IF (MATRIX(I+K,J+L)==TO) THEN
FIND_NEIGHBOUR=.true.
WRITE(*,*) ' Found the target!'
GOTO 10
END IF
END DO
END DO
END IF
END DO
END DO
WRITE(*,*) ' Did not find the target. Continue.'
WRITE(*,*) ''
10 CONTINUE
END FUNCTION FIND_NEIGHBOUR

SUBROUTINE SET_NEIGHBOURS(MATRIX,FROM,TO,WHERE,CHANGED)
INTEGER(KIND=SELECTED_INT_KIND(2)),DIMENSION(6,9),INTENT(INOUT)::MATRIX
INTEGER(KIND=SELECTED_INT_KIND(2)),INTENT(IN)::FROM,TO,WHERE
LOGICAL,INTENT(OUT)::CHANGED
INTEGER::I,J,K,L
CHANGED=.FALSE.
WRITE(*,*) ' Mark all clear fields from our current positions.'
DO I=1,6
DO J=1,9
IF (MATRIX(I,J)==FROM) THEN
DO K=-1,1
IF (I+K<1 .or. I+K>6) CYCLE
DO L=-1,1
IF (J+L<1 .or. J+L>9) CYCLE
IF (MATRIX(I+K,J+L)==WHERE) THEN
CHANGED=.TRUE.
MATRIX(I+K,J+L)=TO
END IF
END DO
END DO
END IF
END DO
END DO
10 CONTINUE
END SUBROUTINE SET_NEIGHBOURS
END MODULE NEIGHBOURS

PROGRAM LABYRINTH
USE NEIGHBOURS
IMPLICIT NONE
INTEGER(KIND=SELECTED_INT_KIND(2)),DIMENSION(6,9)::MATRIX=1
INTEGER(KIND=SELECTED_INT_KIND(2))::FROM=-2,TO=1,I,WHERE=0,TARGET=-3
CHARACTER,DIMENSION(6,9)::MATMAP
LOGICAL::CHANGED=.TRUE.
INTEGER::J

WRITE(*,*) 'Reading labyrinth...'
CALL GET_MATRIX(MATRIX)

WRITE(*,*) 'Finding path...'
WRITE(*,2003) TO
DO WHILE (.NOT. FIND_NEIGHBOUR(MATRIX,FROM,TARGET) .AND. TO<32)
WRITE(*,2003) TO
CALL SET_NEIGHBOURS(MATRIX,FROM,TO,WHERE,CHANGED)
IF (.NOT. CHANGED) GOTO 30
FROM=TO
TO=TO+1
WRITE(*,*) ''
CALL TO_MAP(MATRIX,MATMAP)
WRITE(*,2000) (MATRIX(I,1:9),I=1,6)
2000 FORMAT(9(2X,I3))
1000 FORMAT(9(A1))
WRITE(*,*) ''
END DO

2003 FORMAT(' At step ',I5)

WRITE(*,2001) TO
2001 FORMAT(' This labyrinth takes at least ',I2,' steps to traverse.')

WRITE(*,2000) (MATRIX(I,1:9),I=1,6)

STOP

WRITE(*,2000) (MATRIX(I,1:9),I=1,6)

30 WRITE(*,2002) FROM
2002 FORMAT(' This labyrinth cannot be traversed! Dead end at ',I2)
STOP
END PROGRAM LABYRINTH

SUBROUTINE GET_MATRIX(MATRIX)
INTEGER(KIND=SELECTED_INT_KIND(2)),DIMENSION(6,9),INTENT(OUT)::MATRIX
CHARACTER,DIMENSION(6,9)::MATMAP
INTEGER::I,J
WRITE(*,*) ''
READ(*,1000) (MATMAP(I,1:9),I=1,6)
WRITE(*,1000) (MATMAP(I,1:9),I=1,6)
WRITE(*,*) ''
WRITE(*,*) 'Converting labyrinth to int...'
WRITE(*,*) ''
1000 FORMAT(9(A1))
DO I=1,6
DO J=1,9
CALL CONVERT_CHAR(MATMAP(I,J),MATRIX(I,J))
END DO
END DO
WRITE(*,*) ''
WRITE(*,2000) (MATRIX(I,1:9),I=1,6)
2000 FORMAT(9(2X,I3))
WRITE(*,*) ''
END SUBROUTINE GET_MATRIX

SUBROUTINE CONVERT_CHAR(CHARA,INT)
CHARACTER,INTENT(IN)::CHARA
INTEGER(KIND=SELECTED_INT_KIND(2)),INTENT(OUT)::INT
!WRITE(*,*) CHARA
IF (CHARA=='#') INT=-1
IF (CHARA=='.') INT=0
IF (CHARA=='<') INT=-2
IF (CHARA=='>') INT=-3
END SUBROUTINE CONVERT_CHAR
And the last lines of the output:

Can target be reached in one step?
Looking for way from 12 to -3
Found our current position!
Found the target!
This labyrinth takes at least 13 steps to traverse.
-1 -2 -1 -1 -1 -1 11 -1 -1
-1 1 1 -1 -3 12 -1 10 10
-1 -1 2 -1 -1 -1 -1 9 -1
-1 3 -1 -1 8 8 8 -1 -1
-1 -1 4 -1 -1 7 -1 -1 -1
-1 -1 5 5 6 7 -1 -1 -1


[ Monday, March 12, 2007 05:27: Message edited by: Dr. Johann Georg Faust ]

--------------------
Encyclopaedia ErmarianaForum ArchivesForum StatisticsRSS [Topic / Forum]
My BlogPolarisI eat novels for breakfast.
Polaris is dead, long live Polaris.
Look on my works, ye mighty, and despair.
Posts: 8752 | Registered: Wednesday, May 14 2003 07:00
Guardian
Member # 6670
Profile Homepage #1
Ah, FORTRAN. That explains it.

GOTO is still a sign of a sick and twisted mind, though. It brings back horrid memories of the monstrosities I wrote in BASIC.

--------------------
EDIT: Someone doesn't like me remote-loading pictures...

[ Monday, March 12, 2007 08:40: Message edited by: Dintiradan ]
Posts: 1509 | Registered: Tuesday, January 10 2006 08:00
Law Bringer
Member # 2984
Profile Homepage #2
Well, I used the first one because I couldn't figure out how to elegantly leave those 4 nested loops all at once. And the second one... well, it's a simple decision of whether the algorithm succeeds or fails. I suppose I could have done it with a boolean too...

In my defense, neither of those jump destinations were more than a screen away, and they're pretty unambiguous too.

Although you should have seen our teacher use them. Very fond of them, especially for foot-oriented loops. Admittedly, sometimes a loop needs to have its condition at the end, but can it justify something like this?

10 CONTINUE
I+I=1
IF (I<100) GOTO 10
Edit: Heureka! I now know how to get the actual path - so far, I've only determined the length of the shortest path, not it's actual steps.

But those can be found by following the trail backwards. Just start at the end point, take the lowest positive neighbour, then always find the neighbour that is one less than the current position. Even if there are several equivalent shortest paths, they will all be found this way.

Edit2: Sorry, the above doesn't make any sense unless you understood the algorithm the program currently uses.

[ Monday, March 12, 2007 08:43: Message edited by: Dr. Johann Georg Faust ]

--------------------
Encyclopaedia ErmarianaForum ArchivesForum StatisticsRSS [Topic / Forum]
My BlogPolarisI eat novels for breakfast.
Polaris is dead, long live Polaris.
Look on my works, ye mighty, and despair.
Posts: 8752 | Registered: Wednesday, May 14 2003 07:00
The Establishment
Member # 6
Profile #3
Although it really doesn't matter much, you could replace GOTO 10 with RETURN. They will do the same thing in this case and you avoid the use of gotos. Also, you can place the format specifiers in the WRITE statements themselves too.

Minor style with Fortran (yes, it is lowercase for versions f90 and higher), is to have commands as UPPERCASE and variables and user defined functions as lowercase.

WHERE is also an intrinsic in f90, so you should probably use a different variable. Also, you may want to have dynamic array bounds using the ALLOCATE statement. Just don't forget to DEALLOCATE or you will incur memory leakage.

--------------------
Your flower power is no match for my glower power!
Posts: 3726 | Registered: Tuesday, September 18 2001 07:00
Law Bringer
Member # 2984
Profile Homepage #4
Memory leakage until the program closes, or permanently (until I restart my computer)?

Thanks for the hint about ALLOCATE, I hadn't even thought of that...

--------------------
Encyclopaedia ErmarianaForum ArchivesForum StatisticsRSS [Topic / Forum]
My BlogPolarisI eat novels for breakfast.
Polaris is dead, long live Polaris.
Look on my works, ye mighty, and despair.
Posts: 8752 | Registered: Wednesday, May 14 2003 07:00
The Establishment
Member # 6
Profile #5
Memory leakage, it depends on the OS as far as I can tell. On Win XP, I had a program that took 90% of the available memory. I accidentally fed it some totally invalid input and it crashed. I had to restart to make things normal again.

I've never had analogous problems in my LINUX box regarding this, leading me to believe the problem is handled a bit better in this OS. I haven't tried a lot with OS X, but I'd guess it would be similar.

Basically, memory gets set aside for some purpose and if some other process does not tell it to put it back for general use, it is effectively "lost".

--------------------
Your flower power is no match for my glower power!
Posts: 3726 | Registered: Tuesday, September 18 2001 07:00
Law Bringer
Member # 2984
Profile Homepage #6
By the way, one of the problems I've had with dynamic arrays was that I couldn't find a way to get their size back (as LEN() does for the string length). Do I have to store the number that was used in the ALLOCATE command separately, and pass it to any subroutine that needs it, or can I just pass the array and somehow get its length back with a function?

--------------------
Encyclopaedia ErmarianaForum ArchivesForum StatisticsRSS [Topic / Forum]
My BlogPolarisI eat novels for breakfast.
Polaris is dead, long live Polaris.
Look on my works, ye mighty, and despair.
Posts: 8752 | Registered: Wednesday, May 14 2003 07:00
The Establishment
Member # 6
Profile #7
The SIZE intrinsic will return the array dimensions. Between functions/subroutines you can, as a crude and dirty way, specifically pass the lengths as an arguments:

SUBROUTINE test(xyz,siz_xyz)
IMPLICIT NONE
INTEGER :: siz_xyz
REAL(8), DIMENSION(siz_xyz) :: xyz
...
Otherwise you can use INTERFACE blocks as well to perform these tasks with assumed shape arrays.

--------------------
Your flower power is no match for my glower power!
Posts: 3726 | Registered: Tuesday, September 18 2001 07:00
Guardian
Member # 6670
Profile Homepage #8
Using a language that keeps track of an array's size for you makes you weak. ;)

--------------------
The poor d12. So maligned, so misunderstood. Shunned by its more popular d6 and d20 bretheren, even the d4 secretly laughs at the poor twelve-sider. The d3 gets more play, and it doesn't even exist.
- Elan (OotS #121)
Posts: 1509 | Registered: Tuesday, January 10 2006 08:00
Law Bringer
Member # 2984
Profile Homepage #9
SUBROUTINE DIE()
DEALLOCATE BACKUS
RIP
END SUBROUTINE DIE
http://www.nytimes.com/2007/03/20/business/20backus.html?_r=1&oref=slogin

:(

[ Wednesday, March 21, 2007 06:24: Message edited by: Dr. Johann Georg Faust ]

--------------------
Encyclopaedia ErmarianaForum ArchivesForum StatisticsRSS [Topic / Forum]
My BlogPolarisI eat novels for breakfast.
Polaris is dead, long live Polaris.
Look on my works, ye mighty, and despair.
Posts: 8752 | Registered: Wednesday, May 14 2003 07:00