program main
!*****************************************************************************80
!
!! THIRTY_ONE simulates the game of "31".
!
! Discussion:
!
! Two players draw alternately from a faceup tableau of cards, consisting
! of four copies each of the numbers 1 through 6.
!
! The total of all cards taken by both players is the score.
!
! A player wins by picking a card which results in the score reaching
! exactly 31.
!
! If the score goes over 31 without hitting that value exactly, the game
! is a draw.
!
! Licensing:
!
! This code is distributed under the MIT license.
!
! Modified:
!
! 19 March 2011
!
! Author:
!
! Yuen-Yick Kwan
!
implicit none
integer :: cards(6)
integer :: k
logical :: legal
integer :: next
integer :: total
cards = 4
total = 0
!
! Each iteration simulates one move by the player and one by the computer.
!
do
print *, 'Cards left:'
call print_cards ( cards )
!
! Player selects a card.
!
legal = .false.
do while ( .not. legal )
write(*,'(a)',advance='no') ' Your pick: '
read(*,*) next
if ( 1 <= next .and. next <= 6 ) then
legal = ( 0 < cards(next) .and. total + next <= 31 )
end if
end do
cards(next) = cards(next) - 1
total = total + next
if ( total == 31 ) then
print *, 'Your win!'
exit
end if
!
! Computer selects a card.
!
next = next_play ( total, cards )
select case ( next )
case ( -6 : -1 )
next = abs ( next )
case ( -7 )
print *, 'Draw!'
exit
case ( 0 )
next = 31-total
do while (next > 6)
next = next-7
end do
if ( cards(next) == 0 ) then
next = 1
end if
end select
cards(next) = cards(next) - 1
total = total + next
write(*,'(a,i1,a,i2)') ' I pick ', next, ', Total = ', total
print *, ''
if ( total == 31 ) then
print *, 'I win!'
exit
end if
legal = .false.
do k = 1, 6
if ( 0 < cards(k) .and. total + k <= 31 ) then
legal = .true.
end if
end do
if ( .not. legal ) then
print *, 'Draw!'
exit
end if
end do
stop
contains
subroutine print_cards ( cards )
!*****************************************************************************80
!
!! PRINT_CARDS prints the current card tableau.
!
! Licensing:
!
! This code is distributed under the MIT license.
!
! Modified:
!
! 19 March 2011
!
! Author:
!
! Yuen-Yick Kwan
!
implicit none
integer, intent(in) :: cards(6)
integer :: i, k
do k = 1, 4
do i = 1, 6
if (cards(i) >= k) then
write(*,'(i3)',advance='no') i
else
write(*,'(a3)',advance='no') ''
end if
end do
print *, ''
end do
print *, ''
return
end subroutine print_cards
recursive function next_play ( total, cards ) result ( next )
!*****************************************************************************80
!
!! NEXT_PLAY chooses the computer's next move.
!
! Licensing:
!
! This code is distributed under the MIT license.
!
! Modified:
!
! 19 March 2011
!
! Author:
!
! Yuen-Yick Kwan
!
! Parameters:
!
! Input, integer TOTAL, the computer's current total.
!
! Input, integer CARDS(6), the current card tableau.
!
implicit none
integer, intent(in) :: cards(6)
integer :: k
logical :: legal
integer :: next
integer :: tcards(6)
integer, intent(in) :: total
integer :: trial
!
! Determine the distance to 31.
!
next = 31 - total
!
! If a winning card is available, pick it.
!
if ( next <= 6 ) then
if ( 0 < cards(next) ) then
return
end if
end if
!
! Search for a legal move that doesn't give the opponent an immediate win.
!
tcards = cards
legal = .false.
next = 0
do k = 6, 1, -1
if ( 0 < cards(k) .and. total + k < 31 ) then
legal = .true.
tcards(k) = tcards(k) - 1
trial = next_play ( total + k, tcards )
tcards(k) = tcards(k) + 1
!
! If the computer takes card K, and the opponent can't win on the next turn,
! then take card K.
!
if ( trial == 0 ) then
next = k
return
!
! If the computer takes card K and the opponent is forced over 31 on the next
! turn, consider taking card K (but keep searching!)
!
else if ( next == 0 .and. trial < 0 ) then
next = -k
end if
end if
end do
!
! Return NEXT = - 7 if all remaining moves take the total over 31.
!
if ( .not. legal ) then
next = - 7
end if
return
end function next_play
end program