24-Game Solver  2.0
Using recursive search and pruning
Loading...
Searching...
No Matches
game24_module Module Reference

Functions/Subroutines

subroutine convert_to_number (input_str, number, ios)
 Converts user input (cards or numbers) into numeric values.
 
subroutine remove_decimal_zeros (str, result)
 Removes trailing zeros after the decimal point in a string.
 
subroutine create_new_arrays (nums, exprs, idx1, idx2, result, new_expr, new_nums, new_exprs)
 Creates new arrays after performing an operation.
 
subroutine update_progress_bar ()
 Updates and displays the horizontal percentage-based progress bar.
 
recursive subroutine solve_24 (nums, exprs, found)
 Recursively solves the 24 game by trying all possible operations.
 

Variables

integer, parameter max_limit = 8
 
integer, parameter expr_len = 200
 
integer(int64), parameter total_calls_n6 = 20000000_int64
 
integer(int64), parameter total_calls_n7 = 2648275200_int64
 
integer(int64), parameter total_calls_n8 = 444557593600_int64
 
integer(int64) total_calls = 0
 
integer(int64) completed_calls = 0
 
integer last_percentage = -1
 
integer, parameter progress_bar_width = 50
 
character(len=1) carriage_return = char(13)
 
logical show_progress = .false.
 

Function/Subroutine Documentation

◆ convert_to_number()

subroutine game24_module::convert_to_number ( character(len=*), intent(in) input_str,
real, intent(out) number,
integer, intent(out) ios )

Converts user input (cards or numbers) into numeric values.

Handles card values such as 'A', 'J', 'Q', 'K'.

Parameters
input_strString representing the number or card value
numberThe corresponding numeric value after conversion
iosI/O status indicator (0 for success)
89 implicit none
90 character(len=*), intent(in) :: input_str
91 real, intent(out) :: number
92 integer, intent(out) :: ios
93 character(len=1) :: first_char
94 real :: temp_number
95
96 ios = 0 ! Reset the I/O status to 0 (valid input by default)
97 first_char = input_str(1:1)
98
99 select case (first_char)
100 case ('A', 'a')
101 number = 1.0
102 case ('J', 'j')
103 number = 11.0
104 case ('Q', 'q')
105 number = 12.0
106 case ('K', 'k')
107 number = 13.0
108 case default
109 read (input_str, *, iostat=ios) temp_number ! Attempt to read a real number
110
111 ! If input is not a valid real number or is not an integer, set ios to 1
112 if (ios /= 0 .or. mod(temp_number, 1.0) /= 0.0) then
113 ios = 1 ! Invalid input
114 else
115 number = temp_number ! Valid integer input
116 end if
117 end select

◆ create_new_arrays()

subroutine game24_module::create_new_arrays ( real, dimension(:), intent(in) nums,
character(len=expr_len), dimension(:), intent(in) exprs,
integer, intent(in) idx1,
integer, intent(in) idx2,
real, intent(in) result,
character(len=expr_len), intent(in) new_expr,
real, dimension(:), intent(out), allocatable new_nums,
character(len=expr_len), dimension(:), intent(out), allocatable new_exprs )

Creates new arrays after performing an operation.

Parameters
numsInput array of numbers
exprsInput array of expressions
idx1Index of the first element to remove
idx2Index of the second element to remove
resultResult of the operation
new_exprNew expression string
new_numsOutput array of numbers with elements removed and result added
new_exprsOutput array of expressions with elements removed and new_expr added
155 implicit none
156 real, intent(in) :: nums(:) ! Input: Array of numbers
157 character(len=expr_len), intent(in) :: exprs(:) ! Input: Array of expressions
158 integer, intent(in) :: idx1, idx2 ! Input: Indices of elements to remove
159 real, intent(in) :: result ! Input: Result of the operation
160 character(len=expr_len), intent(in) :: new_expr ! Input: New expression
161 real, allocatable, intent(out) :: new_nums(:) ! Output: New array of numbers
162 character(len=expr_len), allocatable, intent(out) :: new_exprs(:) ! Output: New array of expressions
163 integer :: i, j, n ! Loop counters and size of input arrays
164
165 n = size(nums)
166 allocate (new_nums(n - 1))
167 allocate (new_exprs(n - 1))
168
169 j = 0
170 do i = 1, n
171 if (i /= idx1 .and. i /= idx2) then
172 j = j + 1
173 new_nums(j) = nums(i)
174 new_exprs(j) = exprs(i)
175 end if
176 end do
177
178 ! Add the result of the operation to the new arrays
179 new_nums(n - 1) = result
180 new_exprs(n - 1) = new_expr

◆ remove_decimal_zeros()

subroutine game24_module::remove_decimal_zeros ( character(len=*), intent(in) str,
character(len=*), intent(out) result )

Removes trailing zeros after the decimal point in a string.

Parameters
strInput string that may contain trailing zeros
resultOutput string with trailing zeros removed
124 implicit none
125 character(len=*), intent(in) :: str ! Input: String to remove zeros from
126 character(len=*), intent(out) :: result ! Output: String without trailing zeros
127 integer :: i, len_str ! Loop counter and string length
128
129 len_str = len_trim(str)
130 result = adjustl(str(1:len_str))
131
132 ! Find the position of the decimal point
133 i = index(result, '.')
134
135 ! If there's a decimal point, remove trailing zeros
136 if (i > 0) then
137 do while (len_str > i .and. result(len_str:len_str) == '0')
138 len_str = len_str - 1
139 end do
140 if (result(len_str:len_str) == '.') len_str = len_str - 1
141 result = result(1:len_str)
142 end if

◆ solve_24()

recursive subroutine game24_module::solve_24 ( real, dimension(:), intent(in) nums,
character(len=expr_len), dimension(:), intent(in) exprs,
logical, intent(inout) found )

Recursively solves the 24 game by trying all possible operations.

Utilizes OpenMP tasks for parallelization.

Parameters
numsArray of numbers to use in the game
exprsArray of string expressions representing the numbers
foundLogical flag indicating if a solution has been found
226 use omp_lib
227 implicit none
228 real, intent(in) :: nums(:) ! Input: Array of numbers
229 character(len=expr_len), intent(in) :: exprs(:) ! Input: Array of expressions
230 logical, intent(inout) :: found ! Input/Output: Flag indicating if a solution is found
231 integer :: n ! Size of the input arrays
232 integer :: i, j, op ! Loop counters
233 real :: a, b, result ! Temporary variables for calculations
234 real, allocatable :: new_nums(:) ! Temp array to store numbers after an operation
235 character(len=expr_len), allocatable :: new_exprs(:) ! Temp array to store expressions after an operation
236 character(len=expr_len) :: expr_a, expr_b, new_expr ! Temp variables for expressions
237
238 n = size(nums)
239
240 ! Increment the completed_calls counter and update progress bar
241 if (show_progress) then
242 !$omp atomic
243 completed_calls = completed_calls + 1
244 call update_progress_bar()
245 end if
246
247 ! If a solution is found, return
248 if (found) return
249
250 ! Base case: If only one number is left, check if it is 24
251 if (n == 1) then
252 if (abs(nums(1) - 24.0) < 1e-4) then
253 if (show_progress) then
254 write (*, '(A, F5.1, A)', advance='no') carriage_return//'['//repeat('=', progress_bar_width)//'] ', 100.0, '%'
255 write (*, '(A)') '' ! Insert a blank line
256 end if
257 !$omp critical
258 write (*, '(A, A, A, F10.7, A)') 'Solution found:', trim(exprs(1)), '= 24 (', nums(1), ')'
259 found = .true.
260 !$omp end critical
261 end if
262 return
263 end if
264
265 ! Iterate over all pairs of numbers
266 do i = 1, n - 1
267 do j = i + 1, n
268 a = nums(i)
269 b = nums(j)
270 expr_a = exprs(i)
271 expr_b = exprs(j)
272
273 ! Iterate over all operators
274 do op = 1, 4
275 ! Avoid division by zero
276 if ((op == 4 .and. abs(b) < 1e-6)) cycle
277
278 ! Perform the operation and create the new expression
279 select case (op)
280 case (1)
281 result = a + b
282 new_expr = '('//trim(expr_a)//'+'//trim(expr_b)//')'
283 case (2)
284 result = a - b
285 new_expr = '('//trim(expr_a)//'-'//trim(expr_b)//')'
286 case (3)
287 result = a * b
288 new_expr = '('//trim(expr_a)//'*'//trim(expr_b)//')'
289 case (4)
290 result = a / b
291 new_expr = '('//trim(expr_a)//'/'//trim(expr_b)//')'
292 end select
293
294 ! Create new arrays with the selected numbers removed
295 call create_new_arrays(nums, exprs, i, j, result, new_expr, new_nums, new_exprs)
296
297 ! For the first few recursion levels, create parallel tasks
298 if (n >= 6 .and. omp_get_level() < 2) then
299 !$omp task shared(found) firstprivate(new_nums, new_exprs)
300 call solve_24(new_nums, new_exprs, found)
301 !$omp end task
302 else
303 call solve_24(new_nums, new_exprs, found)
304 end if
305
306 ! If a solution is found, deallocate memory and return
307 if (found) then
308 deallocate (new_nums)
309 deallocate (new_exprs)
310 return
311 end if
312
313 ! Handle commutative operations only once
314 if (op == 1 .or. op == 3) cycle
315
316 ! Swap operands for subtraction and division
317 if (op == 2 .or. op == 4) then
318 if (op == 4 .and. abs(a) < 1e-6) cycle ! Avoid division by zero
319
320 select case (op)
321 case (2)
322 result = b - a
323 new_expr = '('//trim(expr_b)//'-'//trim(expr_a)//')'
324 case (4)
325 result = b / a
326 new_expr = '('//trim(expr_b)//'/'//trim(expr_a)//')'
327 end select
328
329 ! Create new arrays with the selected numbers removed
330 call create_new_arrays(nums, exprs, i, j, result, new_expr, new_nums, new_exprs)
331
332 ! For the first few recursion levels, create parallel tasks
333 if (n >= 6 .and. omp_get_level() < 2) then
334 !$omp task shared(found) firstprivate(new_nums, new_exprs)
335 call solve_24(new_nums, new_exprs, found)
336 !$omp end task
337 else
338 ! Recursively call the solve_24 function with the new arrays
339 call solve_24(new_nums, new_exprs, found)
340 end if
341
342 ! If a solution is found, deallocate memory and return
343 if (found) then
344 deallocate (new_nums)
345 deallocate (new_exprs)
346 return
347 end if
348 end if
349
350 end do ! End of operator loop
351 end do ! End of j loop
352 end do ! End of i loop

◆ update_progress_bar()

subroutine game24_module::update_progress_bar

Updates and displays the horizontal percentage-based progress bar.

185 implicit none
186 real :: percentage
187 integer :: filled_length
188 character(len=progress_bar_width) :: bar
189 integer :: int_percentage
190
191 if (total_calls == 0 .or. .not. show_progress) return ! Avoid division by zero and check the flag
192
193 percentage = real(completed_calls) / real(total_calls) * 100.0
194
195 ! Ensure percentage does not exceed 100%
196 if (percentage > 100.0) percentage = 100.0
197
198 ! Calculate integer percentage
199 int_percentage = int(percentage)
200
201 ! Update progress bar only when percentage increases by at least 1%
202 if (int_percentage > last_percentage) then
203 last_percentage = int_percentage
204
205 ! Calculate the filled length of the progress bar
206 filled_length = min(int(percentage / 100.0 * progress_bar_width), progress_bar_width)
207
208 ! Construct the progress bar string
209 bar = repeat('=', filled_length)
210 if (filled_length < progress_bar_width) then
211 bar = bar//'>'//repeat(' ', progress_bar_width - filled_length - 1)
212 end if
213
214 ! Print the progress bar and integer percentage
215 write (*, '(A, F4.1, A)', advance='no') carriage_return//'['//bar//'] ', percentage, '%'
216 call flush (0) ! Ensure output is displayed immediately
217 end if

Variable Documentation

◆ carriage_return

character(len=1) game24_module::carriage_return = char(13)
55 character(len=1) :: carriage_return = char(13) ! Carriage return character

◆ completed_calls

integer(int64) game24_module::completed_calls = 0
52 integer(int64) :: completed_calls = 0 ! Number of completed recursive calls

◆ expr_len

integer, parameter game24_module::expr_len = 200
43 integer, parameter :: expr_len = 200 ! Maximum length for expressions

◆ last_percentage

integer game24_module::last_percentage = -1
53 integer :: last_percentage = -1 ! Last percentage reported

◆ max_limit

integer, parameter game24_module::max_limit = 8
42 integer, parameter :: max_limit = 8 ! Maximum allowed value for the number of inputs

◆ progress_bar_width

integer, parameter game24_module::progress_bar_width = 50
54 integer, parameter :: progress_bar_width = 50 ! Width of the progress bar

◆ show_progress

logical game24_module::show_progress = .false.
56 logical :: show_progress = .false. ! Flag to show progress bar

◆ total_calls

integer(int64) game24_module::total_calls = 0
51 integer(int64) :: total_calls = 0 ! Total number of recursive calls

◆ total_calls_n6

integer(int64), parameter game24_module::total_calls_n6 = 20000000_int64
46 integer(int64), parameter :: total_calls_n6 = 20000000_int64

◆ total_calls_n7

integer(int64), parameter game24_module::total_calls_n7 = 2648275200_int64
47 integer(int64), parameter :: total_calls_n7 = 2648275200_int64

◆ total_calls_n8

integer(int64), parameter game24_module::total_calls_n8 = 444557593600_int64
48 integer(int64), parameter :: total_calls_n8 = 444557593600_int64