!written on aug 26, 2000 by siva@cse.iitb.ernet.in
Program KnightWalk
Implicit none
Logical, Dimension(8,8) :: Board 
Integer :: nextchar = 1
Integer :: Row = 0, Col = 0, RowNext = 0, ColNext=0
Integer :: WalkLength = 0, MoveNum = 1
Integer :: GetNum  ! same function we used last time. why declared?

! now some declarations for PGPLOT
Integer ::  PGBEG, IER ! needed for pgplot package
Character(len = 2) :: makeAscii
Character status

!initialize all squares to .false. since none has been visited
Board = .false. 

! Initialize PGPLOT
IER = PGBEG(0,'?',1,1) 
IF (IER.NE.1) STOP     ! Unable to open the DEVICE, then stop.
Call PGPAP(6.0,1.0)    ! Resize the window to 6 inch * 6 inch board
Call PGSWIN(0.0,16.0,0.0,16.0) ! set the world co-cordinates 0 - 16.0
! Draw a box with  grid, axes labelled, tick on axes
CALL PGBOX("ABCGST",2.0,0,"ABCGST",2.0,0) 
Call PGSFS(1)  ! set fill style to solid
Call PGSCI(7)  ! set pen colour to yellow
Do Row = 1, 8  ! shade the board
 Do Col = 1, 8
  If (Mod(Col,2) == Mod(Row,2)) Then
   Call PGSCI(5)  ! colour 5 = bluish
  Else
   Call PGSCI(7)  ! colour 7 = yellow
  End If
  Call PGRECT((Row - 1) * 2.0, Row  * 2.0,  &
              (Col - 1) * 2.0, Col  * 2.0 )
 End Do
End Do
Call PGSCI(6) !set pen color pink

! get start row and col from user
Row = Getnum(1,8,'Enter Start Row: ')
Col = Getnum(1,8,'Enter Start Col: ')
Board(Row,Col) = .true. ! we have visited this square already.

! start the walk
Do
 Call PGTEXT(Row*2.0 - 1.0,Col*2.0 - 1.0,makeAscii(MoveNum))
 Call WaitforChar()
 Call NextMove(Board, Row, Col, RowNext, ColNext)
 If (RowNext == 0) Exit  ! rowNext is 0 only when we reach a dead end
 Write (*,*) 'Move from: <', Row, ',', Col, '> to  <',    &
                         RowNext, ',', ColNext, '>'
 WalkLength = WalkLength + 1
 MoveNum = MoveNum + 1
 Row = RowNext ! this is next position
 Col = ColNext
End Do

Write (*,*)  'Walk ended after ', WalkLength, ' steps. Bye.'
CALL PGEND
End Program KnightWalk

Subroutine WaitforChar()
 Implicit None
 Character status
 Write (*,*)  "Enter any character (e.g. 'y') to go proceed" 
 Read *, status
End Subroutine WaitforChar

 Character(len=2) Function makeAscii(Num)
 Implicit none
! Num is assumed to be 2 digits and 2 char string is returned.
 Integer, intent(in) :: Num
 Character(len=1) :: digit1 , digit2 !local vars
 digit1 = achar(Mod(Num,10) + 48) !adding 48 for ascii of 0
 digit2 = achar(Num/10 + 48)
 makeAscii =  digit2 // digit1 ! // is concatenate
 End Function makeAscii

 Integer Function Getnum(low, high, msg)
    Implicit none
    Integer, Intent(in) :: low, high
     ! note below that Len = * is ok since msg is input.
    Character(Len=*), Intent(in) :: msg
    Integer :: ans

    Do
       Write (*,*) msg, '(Between ', low, ' and ', high, ')'
       Read (*,*) ans
       If ((low <= ans) .and. (ans <= high)) Exit
       Write (*,*) 'You did not type a valid input. Please try again.'
    End Do
   Getnum = ans
  End Function Getnum

 Subroutine NextMove(Board, X, Y, X1, Y1)
  Implicit None
 ! example of more than one attribute for a variable
 ! remember: logical is type, intent and dimension are attributes.
 Logical, Intent(Inout), Dimension(8,8) :: Board
 Integer, Intent(In) :: X, Y
 Integer, Intent(Out) :: X1, Y1
 ! these two are local variables
 Integer :: Hor, Ver
 Logical :: MoveFound

 ! Is the logic of finding the next move is "complex"?
 ! no, only the representation (data structure + control structure)
 !        can be confusing unless we are systematic.
 ! -2, -1, 1, 2 are valid strides in either axis, but not 0
 ! so we need a DO loop where for counter = 0 we simply CYCLE (i.e. ignore)
 ! but neither horizontal nor vertical stride must take us off the board
 !  so check 1 <= new-pos <= 8 for both axis
 ! but if hor stride  is 1, then vertical MUST be 2 and vice versa
 ! so we need to check that abs(horizontal-stride) /= abs(vertical-stride)
 ! the above "tricks" make the code compact. 
 ! otherwise, we have to "enumerate" (brute-force) the 8 moves.
 MoveFound = .false.
 Do Hor = -2, 2   ! hor takes 5 values, but 0 is not ok.
   If ((Hor == 0) .or. ((X+Hor) < 1) .or. ((X+Hor) > 8)) Cycle

   Do Ver = -2, 2  ! ver takes 5 values, but 0 is not ok.
      ! also hor and ver cannot both be 1 (or both 2) for knight move

    If ((Ver == 0) .or. (abs(Hor) == abs(Ver)) .or.           &
              ((Y+Ver) < 1) .or. ((Y+ver) > 8)) Cycle
    If (.NOT. Board(X+Hor, Y+Ver)) MoveFound = .true.
    ! we have found a free square
    IF (MoveFound) Exit
   End Do
   If (MoveFound) Exit  ! we have found a free square. 
                      ! Isn't this a "tricky" way? checking ver < 3?
 End Do

 If (MoveFound) Then    ! same tricky way (hor < 3) used to check if we
   X1 = X + Hor       ! exited loop normally or after finding a move.
   Y1 = Y + Ver
   Board(X1,Y1) = .true.
 Else   ! we will reach here with Hor=3 only if we cant find a move
   X1 = 0
   Y1 = 0
 End If
 End Subroutine NextMove

