Skip to content

Add episode 2 challenge 1 #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 26 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e103cc3
WIP: Add episode 2 challenge 1
connoraird Jun 6, 2025
95ce18e
Remove unused valiables and executable
connoraird Jun 9, 2025
646c99a
Add fpm.toml and move models out of src
connoraird Jun 9, 2025
e28a202
Cleanup main src and rename file
connoraird Jun 9, 2025
c2aafdd
Add docstrings
connoraird Jun 9, 2025
bd8b0f3
Fix mistake find and replace
connoraird Jun 9, 2025
9d9c90a
Make sum in evolve clearer
connoraird Jun 9, 2025
c46ce42
Add solution and first pass at tests
connoraird Jun 9, 2025
f110b65
Update tests to be more flexible
connoraird Jun 9, 2025
d236857
Add tests for non steady state boards
connoraird Jun 9, 2025
eddaf59
Add tests for evolve_board
connoraird Jun 9, 2025
dfb87b2
Remove input_boards_t
connoraird Jun 9, 2025
a2d03ef
Move solution into solution dir
connoraird Jun 9, 2025
8320ce9
Replace fix me tag info with building info in solution
connoraird Jun 10, 2025
2700243
Extract the IO into a procedure
connoraird Jun 10, 2025
ab61266
Add first test for read_model_from_file
connoraird Jun 10, 2025
24fefe8
Use allocatable error message instead of status integer
connoraird Jun 11, 2025
a570f6a
add sad path tests for read_model_from_file
connoraird Jun 11, 2025
75fe456
Remove TODO
connoraird Jun 11, 2025
596827a
Remove sol from repo
connoraird Jun 16, 2025
35f1fe1
Split tests into one file per procedure to be tested
connoraird Jun 16, 2025
57f6054
Move challenge into a challenge dir
connoraird Jun 18, 2025
54a476f
Rename tests, fix src and remove constructors
connoraird Jun 18, 2025
4c077e1
Document FIX locations in solution README
connoraird Jul 8, 2025
2acbb54
Add documentation for running the challenge src
connoraird Jul 8, 2025
187598e
Add link to game of life
connoraird Jul 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Episode 2 - Challenge 1: Identify bad practice for unit testing Fortran

Take a look at the [src](./src) code provided. This is an implementation of
[Conway's game of life](http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life). The program
reads in a data file which represents the starting state of the system. The system is then
evolved and printed to the terminal screen for each time step. To build and run the src
code use the following command from within this dir.

```bash
fpm run -- ../models/model-1.dat # Or another dat file
```

## Questions

1. Can you identify the aspects of this Fortran code which make it difficult to unit test?
2. Try to improve the src to make it more unit testable.

A solution is provided in the [solution](./solution/) dir.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
name = "episode-2-challenge-1"

[[executable]]
name = "game-of-life"
source-dir = "src"
main = "game_of_life.f90"
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
! =======================================================
! Conway's game of life
!
! =======================================================
! Adapted from https://github.com/tuckerrc/game_of_life
! =======================================================
program game_of_life

implicit none

!! Board args
integer, parameter :: max_nrow = 100, max_ncol = 100, max_generations = 100
integer :: nrow, ncol
integer :: row, generation_number
integer, dimension(:,:), allocatable :: current_board, new_board

!! Animation args
integer, dimension(8) :: date_time_values
integer :: mod_ms_step, ms_per_step = 250
logical :: steady_state = .false.

!! CLI args
integer :: argl
character(len=:), allocatable :: cli_arg_temp_store, input_fname

!! File IO args
character(len=80) :: text_to_discard
integer :: input_file_io
integer :: iostat

! Get current_board file path from command line
if (command_argument_count() == 1) then
call get_command_argument(1, length=argl)
allocate(character(argl) :: input_fname)
call get_command_argument(1, input_fname)
else
write(*,'(A)') "Error: Invalid input"
call get_command_argument(0, length=argl)
allocate(character(argl) :: cli_arg_temp_store)
call get_command_argument(0, cli_arg_temp_store)
write(*,'(A,A,A)') "Usage: ", cli_arg_temp_store, " <input_file_name>"
deallocate(cli_arg_temp_store)
stop
end if

! Open input file
open(unit=input_file_io, &
file=input_fname, &
status='old', &
IOSTAT=iostat)

if( iostat /= 0) then
write(*,'(a)') ' *** Error when opening '//input_fname
stop 1
end if

! Read in current_board from file
read(input_file_io,'(a)') text_to_discard ! Skip first line
read(input_file_io,*) nrow, ncol

! Verify the date_time_values read from the file
if (nrow < 1 .or. nrow > max_nrow) then
write (*,'(a,i6,a,i6)') "nrow must be a positive integer less than ", max_nrow, " found ", nrow
stop 1
end if

if (ncol < 1 .or. ncol > max_ncol) then
write (*,'(a,i6,a,i6)') "ncol must be a positive integer less than ", max_ncol, " found ", ncol
stop 1
end if

allocate(current_board(nrow, ncol))
allocate(new_board(nrow, ncol))

read(input_file_io,'(a)') text_to_discard ! Skip next line
! Populate the boards starting state
do row = 1, nrow
read(input_file_io,*) current_board(row, :)
end do

close(input_file_io)

new_board = 0
generation_number = 0

! Clear the terminal screen
call system ("clear")

! Iterate until we reach a steady state
do while(.not. steady_state .and. generation_number < max_generations)
! Advance the simulation in the steps of the requested number of milliosecons
call date_and_time(VALUES=date_time_values)
mod_ms_step = mod(date_time_values(8), ms_per_step)

if (mod_ms_step == 0) then
call evolve_board()
call check_for_steady_state()
current_board = new_board
call draw_board()

generation_number = generation_number + 1
end if

end do

if (steady_state) then
write(*,'(a,i6,a)') "Reached steady after ", generation_number, " generations"
else
write(*,'(a,i6,a)') "Did NOT Reach steady after ", generation_number, " generations"
end if

deallocate(current_board)
deallocate(new_board)

contains

!> Evolve the board into the state od the next iteration
subroutine evolve_board()
integer :: row, col, sum

do row=2, nrow-1
do col=2, ncol-1
sum = 0
sum = current_board(row, col-1) &
+ current_board(row+1, col-1) &
+ current_board(row+1, col) &
+ current_board(row+1, col+1) &
+ current_board(row, col+1) &
+ current_board(row-1, col+1) &
+ current_board(row-1, col) &
+ current_board(row-1, col-1)
if(current_board(row,col)==1 .and. sum<=1) then
new_board(row,col) = 0
elseif(current_board(row,col)==1 .and. sum<=3) then
new_board(row,col) = 1
elseif(current_board(row,col)==1 .and. sum>=4)then
new_board(row,col) = 0
elseif(current_board(row,col)==0 .and. sum==3)then
new_board(row,col) = 1
endif
enddo
enddo

return
end subroutine evolve_board

!> Check if we have reached steady state, i.e. current and new board match
subroutine check_for_steady_state()
integer :: row, col

do row=1, nrow
do col=1, ncol
if (.not. current_board(row, col) == new_board(row, col)) then
steady_state = .false.
return
end if
end do
end do
steady_state = .true.
end subroutine check_for_steady_state

!> Output the current board to the terminal
subroutine draw_board()
integer :: row, col
character(nrow) :: output

! Clear the terminal screen
call system("clear")

do row=1, nrow
output = ""
do col=1, ncol
if (current_board(row,col) == 1) then
output = trim(output)//"#"
else
output = trim(output)//"."
endif
enddo
print *, output
enddo
end subroutine draw_board

end program game_of_life
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
nrow ncol
31 31
Board
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
nrow ncol
31 31
Board
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Loading