Guess the number game in Fortran
I recently came across this blog post on writing simple test programs in different programming languages as a way to get a feel for a language. At the bottom of the post, there were links to articles on implementations of a number guessing game in 13 different languages, including Fortran. I was curious about the Fortran example because I've been interested in learning a little Fortran after hearing about efforts to improve the tooling around Fortran (e.g., here and here). Well, the Fortran example was written in Fortran 77 so I decided that re-writing it in modern Fortran would be a nice little exercise.
I'm not going to write about the Fortran 77 version. It is well described in the other article. Before I show the code for the "modern" version, I will introduce a couple of subroutines that I plucked from this random number tutorial.
subroutine random_stduniform(u)
implicit none
real,intent(out) :: u
real :: r
call random_number(r)
u = 1 - r
end subroutine random_stduniform
! assuming a<b
subroutine random_uniform(a,b,x)
implicit none
real,intent(in) :: a,b
real,intent(out) :: x
real :: u
call random_stduniform(u)
x = (b - a) * u + a
end subroutine random_uniform
The first thing to note is that Fortran has both subroutines and functions. I have only a fuzzy understanding of when you would choose one over the other. In this case, it seems that random_stduniform
and random_uniform
could have also been written as functions (but I didn't try). Perhaps the fact that random_number
is a subroutine makes it more intuitive to build subroutines on top of it. When calling a subroutine, you need to preface the statement with call
, which is not required for functions. Also, in a subroutine, assignment is handled by passing the variable name as an argument. For example, in random_stduniform
, the variable r
is declared with type real
and then assigned a value when passed to the random_normal
subroutine.
One of the other unusual (to me) components of these subroutines is implicit none
, which means that the types of variables need to be explicitly declared and not implicitly inferred from the variable names. Here is an argument for embracing implicit none
rather than seeking to change it in a future version of the Fortran Standard.
Armed with those two sub-routines, here is the full program for the number guessing game:
program guessnum
implicit none
real :: number
integer :: number_int, guess
call random_seed()
call random_uniform(0.999, 100.0, number)
number_int = int(number)
guess = 0
print *, "Guess a number between 1 and 100"
do while (guess /= number_int)
read *, guess
if (guess < number_int) then
print *, "too low"
else if (guess > number_int) then
print *, "too high"
end if
end do
print *, "correct!"
end program guessnum
After declaring the types of our variables, we set a random seed and generate a number from a random uniform distribution that is > 0.999 and <= 100. random_uniform
assigns a real number to the variable number
so we coerce it to an integer with int
, which has the same effect as floor
or truncate
operators in other languages. guess
is initialized at zero to get us into our while
loop because number_int
should never be equal to zero in this program.
We print the guessing instructions, enter the while
loop, and read the input from the user. Then, we compare the user's guess
to number_int
, give feedback, and read their next guess. When guess
matches number_int
, we break out of the while
loop and print correct!
before exiting the program.
If you have the GNU Fortran compiler installed, and the code above in a file called guess.f90
, you can compile the program with gfortran guess.f90 -o guess
and run the program with ./guess
. Here is example output from running the program:
Guess a number between 1 and 100
50
too high
25
too low
37
too low
43
too high
40
too high
39
too high
38
correct!
That was a fun first experience with Fortran. I'm happy to trade some verbosity for simplicity. I think the Fortran example is a lot easier to understand than the Rust example. Despite working for many years as a scientific programmer (but never officially my job title), I had never considered learning Fortran. I assumed it was an ugly relict hanging on in legacy codebases, but I no longer think it is ugly and it even moved back into the top 20 of the Tiobe index.