Something fun. My youngest daughter is a senior in high school & is taking a Computer Programming class. One of her exercises was based on a contest problem sponsored by the ACSL (American Computer Science League). The task is to create an algorithm for a simple card game.
I had a little free time so I quickly created a solution in 2 languages - SQR & WinBatch. My primary goal was to streamline the logic in such as way so it's easy to understand & follow. Parents weren't allowed to help so she was on her own... but I will definitely follow up with her.
***UPDATE*** - PC/370 Assembler Version Created! CLICK HERE
INSTRUCTIONS:
The instructions on the AGRAM game itself are vague - it doesn't provide an explanation on how to conduct and win a complete game. Rather it focuses on the logic within a single hand.
Here is the ACSL PDF version of the rules: CLICK HERE
The assumption is that ALL CARDS are VALID members of a 52 card deck. No error checking is incorporated or required. I also ignored the unnecessary restriction of 5 cards - the logic works for 1 through 51 cards - it's driven entirely by the number of dealer cards presented.
The SQR version follows the INPUT instructions with each hand represented by a comma delimited record in a file. (The WinBatch version will utilize a prompt).
INPUT:
5D,2D,6H,9D,TD,6H
TC,AC,KC,QH,JS,TD
3D,4H,5C,6S,2D,7H
KS,TH,QC,7H,9H,3H
AC,AD,KH,JS,KS,QS
3H,2D,AC,9S,AH,QH,KH,AH,AS,AH,TD,3C,KC,2H,5H,6H,6S,9D,9D,9D,4H,KS
KH,2H,4C,5S,AH,KD
KH,2H,4C,5S,3H,KD
SQR VERSION:
The program reads one record at a time with each one performing the Process_Cards routine (shown below).
The XXX variables represent the card chosen by the dealer - it will contain the final choice once all cards in the dealers hand have been evaluated.
The OPP variables are used to hold the Opponent's card. Simple enough.
The DLR variables are used to hold the latest card (evaluated one by one) in the Dealer's Hand.
I used my own custom Parsing functions TDF_PARSE_Put() & TDF_PARSE_Get() to extract the individual data elements from the record. Index 0 contains the Opponent Card while Index 1 goes up to (but not including) the #O_cols counter. So a 5 card dealer hand has indexes 1 thru 5.
The 2-character card must be broken down & weighted. The function CARD_Parse() is utilized for all cards & returns the suit (C=Clubs, D=Diamonds, H=Hearts, S=Spades) and a weighted value. In this case, the "single-digit" number cards equal themselves (2 thru 9) - an A (Ace) has a value of 1 - while T (Ten), J (Jack), Q (Queen) & K (King) have the values 10 thru 13.
!**********************************************************************
!* Process Cards *
!**********************************************************************
begin-procedure Process_Cards
let $XXX_card = 'XX'
let $XXX_suit = 'X'
let #XXX_val = 0
do TDF_PARSE_Put($rec, ',', '', #O_cols)
do TDF_PARSE_Get( 0, $OPP_card)
do CARD_Parse($OPP_card, $OPP_suit, #OPP_val)
show 'Opponent: ' $OPP_card ' . ' $OPP_suit ' . ' #OPP_val edit 999
let #idx = 1
while #idx < #O_cols
do TDF_PARSE_Get(#idx, $DLR_card)
do CARD_Parse($DLR_card, $DLR_suit, #DLR_val)
show ' Dealer: ' $DLR_card ' . ' $DLR_suit ' . ' #DLR_val edit 999
! Always Select the First Card (Skip Comparisons)
! ===============================================
if #idx = 1
let $XXX_card = $DLR_card
let $XXX_suit = $DLR_suit
let #XXX_val = #DLR_val
else
! No Suit Match Yet - If Lower Value OR Suit Select Card
! ======================================================
if $XXX_suit <> $OPP_suit
if #DLR_val < #XXX_val
or $DLR_suit = $OPP_suit
let $XXX_card = $DLR_card
let $XXX_suit = $DLR_suit
let #XXX_val = #DLR_val
end-if
! Same Suit - Ignore All other Suits from Dealer
! ==============================================
else
if $XXX_suit = $DLR_suit
! OVER Opponent Value - Select CARD if also over but < prior pick
! ===============================================================
if #XXX_val > #OPP_val
if #DLR_val > #OPP_val
and #DLR_val < #XXX_val
let $XXX_card = $DLR_card
let $XXX_suit = $DLR_suit
let #XXX_val = #DLR_val
end-if
! UNDER Opponent Value - Select CARD if OVER Opponent or < prior pick
! ===================================================================
else
if #XXX_val < #DLR_val
if #OPP_val < #DLR_val
let $XXX_card = $DLR_card
let $XXX_suit = $DLR_suit
let #XXX_val = #DLR_val
end-if
else
let $XXX_card = $DLR_card
let $XXX_suit = $DLR_suit
let #XXX_val = #DLR_val
end-if
end-if
end-if
end-if
end-if
let #idx = #idx + 1
end-while
show ' '
show 'Results: ' $rec ' ==> ' $XXX_card
show ' '
end-procedure
!**********************************************************************
The CARD_Parse() routine does NOT use hard-coded evaluate, switch or case constructs - or worse yet a series of convoluted If-Then-Else statements. I control the weighted values using a string of face characters represented by the order of magnitude. If I want a character to be high I place it at the end - any character - it could be a 7 if I like - who cares. The INSTR function returns the position in the string and uses that for the weighted value. Zero complexity.
!**********************************************************************
!* CARD Parse *
!**********************************************************************
begin-procedure CARD_Parse($I_card, :$O_suit, :#O_val)
let $X_face = substr($I_card, 1, 1)
let $O_suit = substr($I_card, 2, 1)
let #O_val = instr('A23456789TJQK', $X_face, 1)
end-procedure
!**********************************************************************
Results (Snipped from LOG File):
Results: 5D,2D,6H,9D,TD,6H ==> 9D
Results: TC,AC,KC,QH,JS,TD ==> KC
Results: 3D,4H,5C,6S,2D,7H ==> 2D
Results: KS,TH,QC,7H,9H,3H ==> 3H
Results: AC,AD,KH,JS,KS,QS ==> AD
Results: 3H,2D,AC,9S,AH,QH,KH,AH,AS,AH,TD,3C,KC,2H,5H,6H,6S,9D,9D,9D,4H,KS ==> 4H
Results: KH,2H,4C,5S,AH,KD ==> AH
Results: KH,2H,4C,5S,3H,KD ==> 2H
WINBATCH VERSION:
For WinBatch I created a dialog panel to enter the opponent's card along with a comma delimited string of dealer cards. When the OK button is pressed the proper dealer card is selected & displayed in RED.
I'll show the Sample Results first this time followed by the routine.
The dialog has the GUI variables eOpponent & eCards to hold the input (as opposed to the file used in the SQR version). The GUI variable vResult will hold the result. In general the logic is identical to the SQR version - the only difference being syntax/functions between the two languages. For example, StrIndex() is used to weight the cards instead of the Instr() function - same result.
;**********************************************************************
;* Process Cards *
;**********************************************************************
:Process_Cards
DLR_hand = eCards
OPP_card = eOpponent
OPP_xlat = StrSub(OPP_card, 1, 1)
OPP_suit = StrSub(OPP_card, 2, 1)
OPP_val = StrIndex('A23456789TJQK', OPP_xlat, 1, @FWDSCAN)
XXX_card = 'XX'
XXX_suit = 'X'
XXX_val = 0
count = ItemCountCSV(DLR_hand, 0,",")
For x = 1 to count
DLR_card = ItemExtractCSV(x, DLR_hand, 0, ",")
DLR_xlat = StrSub(DLR_card, 1, 1)
DLR_suit = StrSub(DLR_card, 2, 1)
DLR_val = StrIndex('A23456789TJQK', DLR_xlat, 1, @FWDSCAN)
; Always Select the First Card (Skip Comparisons)
; ===============================================
if x == 1
XXX_card = DLR_card
XXX_suit = DLR_suit
XXX_val = DLR_val
else
; No Suit Match Yet - If Lower Value OR Suit Select Card
; ======================================================
if XXX_suit <> OPP_suit
if DLR_val < XXX_val || DLR_suit == OPP_suit
XXX_card = DLR_card
XXX_suit = DLR_suit
XXX_val = DLR_val
endif
; Same Suit - Ignore All other Suits from Dealer
; ==============================================
else
if XXX_suit == DLR_suit
; OVER Opponent Value - Select CARD if also over but < prior pick
; ===============================================================
if XXX_val > OPP_val
if DLR_val > OPP_val && DLR_val < XXX_val
XXX_card = DLR_card
XXX_suit = DLR_suit
XXX_val = DLR_val
endif
; UNDER Opponent Value - Select CARD if OVER Opponent or < prior pick
; ===================================================================
else
if XXX_val < DLR_val
if OPP_val < DLR_val
XXX_card = DLR_card
XXX_suit = DLR_suit
XXX_val = DLR_val
endif
else
XXX_card = DLR_card
XXX_suit = DLR_suit
XXX_val = DLR_val
endif
endif
endif
endif
endif
Next
vResult = XXX_card
Return
;**********************************************************************
I'm looking forward to the next exercise my daughter brings home...