# sol --- the game of solitaire include "sol_def.r.i" include SOL_COMMON integer i character ans (2) shortcall mkonu$ (18) external quit_finish call mkonu$ ("QUIT$"v, loc (quit_finish)) call initialize repeat { call message (EOS) Game_over = FALSE Times_thru = 0 call set_game_type call shuffle call deal repeat { call display call turn } until (Game_over) if (Game_type == CASINO) call tally else { call count_unplayed (i) if (i <= 0) call message ("Congratulations, You won!"s) else call message ("Sorry..."s) } call prompt ("Play again? "s) call vtenb (PROMPT_ROW, 13, 2) call vtread (PROMPT_ROW, 13, YES) call vtgetl (ans, PROMPT_ROW, 13, 2) } until (ans (1) ~= 'y'c && ans (1) ~= 'Y'c) call log_results call vtstop stop end # add_card --- place a card on the top of a pile subroutine add_card (card, to) integer card, to include SOL_COMMON Pile (COUNT, to) += 1 Deck (LINK, card) = Pile (TOP_CARD, to) Deck (PLACE, card) = to Pile (TOP_CARD, to) = card return end # card_is_movable --- determine if a card can be moved integer function card_is_movable (card) integer card include SOL_COMMON integer loc loc = Deck (PLACE, card) if (Pile (TOP_CARD, WASTE) == card || 1 <= loc - FACE_UP && loc - FACE_UP <= 7) return (YES) return (NO) end # check_ace --- see if card can be played on the ace stack integer function check_ace (card, suit) integer card, suit include SOL_COMMON integer card_rank, card_suit, loc, rank card_suit = Deck (VALUE, card) / 16 card_rank = mod (Deck (VALUE, card), 16) loc = Deck (PLACE, card) if (Pile (COUNT, ACES + suit) <= 0) rank = 0 else rank = mod (Deck (VALUE, Pile (TOP_CARD, ACES + suit)), 16) if (card_suit ~= suit || card_rank - rank ~= 1 || Pile (TOP_CARD, loc) ~= card) return (NO) else return (YES) end # check_king --- see if card is a king and can be moved integer function check_king (card, pile) integer card, pile include SOL_COMMON integer rank, i rank = mod (Deck (VALUE, card), 16) if (rank ~= 13) # not a king return (NO) for (i = FACE_UP + 1; i <= FACE_UP + 7; i += 1) if (Pile (COUNT, i) <= 0) { pile = i return (YES) } return (NO) end # check_stack --- see if card will play on the specified stack integer function check_stack (card, stack) integer card, stack include SOL_COMMON integer color (4), card_suit, card_rank, suit, rank data color /BLACK, RED, RED, BLACK/ if (Pile (COUNT, FACE_UP + stack) <= 0) return (NO) card_suit = Deck (VALUE, card) / 16 card_rank = mod (Deck (VALUE, card), 16) suit = Deck (VALUE, Pile (TOP_CARD, FACE_UP + stack)) / 16 rank = mod (Deck (VALUE, Pile (TOP_CARD, FACE_UP + stack)), 16) if (color (suit) == color (card_suit) || rank - card_rank ~= 1) return (NO) return (YES) end # count_unplayed --- see how many cards haven't been played out subroutine count_unplayed (count) integer count include SOL_COMMON integer i count = Pile (COUNT, WASTE) + Pile (COUNT, STOCK) do i = 1, 7 count += Pile (COUNT, FACE_DN + i) return end # deal --- deal the cards into the appropriate stacks subroutine deal include SOL_COMMON integer i, j Pile (COUNT, STOCK) = 0 Pile (COUNT, WASTE) = 0 do i = 1, 7; { Pile (COUNT, FACE_UP + i) = 0 Pile (COUNT, FACE_DN + i) = 0 } do i = 1, 4 Pile (COUNT, ACES + i) = 0 for (i = 52; i > 0; i -= 1) call add_card (i, STOCK) do i = 1, 7; { call move_card (STOCK, FACE_UP + i) for (j = i + 1; j <= 7; j += 1) call move_card (STOCK, FACE_DN + j) } return end # display --- show what the table looks like subroutine display include SOL_COMMON integer col, card, i, j character time (9) call date (SYS_TIME, time) # get current time time (6) = EOS # truncate seconds call vtputl (time, TIME_ROW, TIME_COL) do i = 1, 4 # display the ace stacks if (Pile (COUNT, ACES + i) > 0) call display_card (ACE_ROW, ACE_COL + (i - 1) * ACE_WIDTH, Pile (TOP_CARD, ACES + i)) else call vtputl (" "s, ACE_ROW, ACE_COL + (i - 1) * ACE_WIDTH) call vtprt (STOCK_ROW, STOCK_COL, "(*2i)"s, Pile (COUNT, STOCK)) call vtprt (PASS_ROW, PASS_COL, "*2i"s, Times_thru) if (Pile (COUNT, WASTE) > 0) { call display_card (WASTE_ROW, WASTE_COL, Pile (TOP_CARD, WASTE)) if (Pile (COUNT, WASTE) > 1) call vtprt (WASTE_ROW, WASTE_COL + 4, "(*i) "s, Pile (COUNT, WASTE) - 1) else call vtputl (" "s, WASTE_ROW, WASTE_COL + 4) } else { call vtputl (" "s, WASTE_ROW, WASTE_COL) call vtputl (" "s, WASTE_ROW, WASTE_COL + 4) } for (i = 1; i <= 7; i += 1) { col = PILE_COL + (i - 1) * PILE_WIDTH if (Pile (COUNT, FACE_DN + i) > 0) call vtprt (PILE_ROW, col, "(*i)"s, Pile (COUNT, FACE_DN + i)) else call vtputl (" "s, PILE_ROW, col) card = Pile (TOP_CARD, FACE_UP + i) for (j = Pile (COUNT, FACE_UP + i); j > 0; j -= 1) { call display_card (PILE_ROW + j, col, card) card = Deck (LINK, card) } for (j = Pile (COUNT, FACE_UP + i) + 1; j <= 12; j += 1) call vtputl (" "s, PILE_ROW + j, col) } call vtupd (NO) return end # display_card --- show what a card is in a certain position subroutine display_card (row, col, card) integer row, col, card include SOL_COMMON character color integer s, v string suit "shdc" string value "A23456789TJQK" s = Deck (VALUE, card) / 16 v = mod (Deck (VALUE, card), 16) if (s == 2 || s == 3) color = Red else color = Black call vtprt (row, col, "*c*c*c"s, value (v), suit (s), color) return end # flip_stack --- flip to next card on stack subroutine flip_stack include SOL_COMMON integer i if (Pile (COUNT, STOCK) <= 0 && Game_type ~= CASINO) { Times_thru += 1 while (Pile (COUNT, WASTE) > 0) call move_card (WASTE, STOCK) } if (Pile (COUNT, STOCK) <= 0) { # no more cards in stock pile Game_over = TRUE return } if (Game_type == CASINO) call move_card (STOCK, WASTE) else for (i = 1; i <= 3 && Pile (COUNT, STOCK) > 0; i += 1) call move_card (STOCK, WASTE) return end # initialize --- set up the initial playing board, etc. subroutine initialize include SOL_COMMON integer vtinit longint time longint read_clock character term (MAXTERMTYPE) if (vtinit (term) ~= OK) { call print (ERROUT, "Terminal type '*s' not supported*n"s, term) call error (""p) } call vtopt (1, STATUS_ROW) call rnd (ints (read_clock (time))) Red = '*'c Black = ' 'c Total_winnings = 0 Games_played = 0 Game_type = REGULAR call vtputl ("Solitaire"s, 1, 30) call vtputl ("Stack:"s, STOCK_LABEL_ROW, STOCK_LABEL_COL) call vtputl ("Aces:"s, ACE_LABEL_ROW, ACE_LABEL_COL) call vtputl ("times thru:"s, PASS_LABEL_ROW, PASS_LABEL_COL) call vtputl ("Layout:"s, LAYOUT_LABEL_ROW, LAYOUT_LABEL_COL) call vtputl ("Commands:"s, HELP_ROW, 1) call vtputl (" -> flip stack"s, HELP_ROW + 2, 1) call vtputl (" [] -> move"s, HELP_ROW + 3, 1) call vtputl (" one card to another"s, HELP_ROW + 4, 1) call vtputl (" i -> set"s, HELP_ROW + 5, 1) call vtputl (" color indicators"s, HELP_ROW + 6, 1) call vtputl (" x -> exit the game"s, HELP_ROW + 7, 1) call vtputl (" -> redraw game"s, HELP_ROW + 8, 1) call vtupd (YES) return end # interpret_card --- return the deck index of a card integer function interpret_card (str, pos) character str (ARB) integer pos include SOL_COMMON integer s, r, card integer index character mapup string suit "SHDC" string rank "A23456789TJQK" SKIPBL (str, pos) r = index (rank, mapup (str (pos))) pos += 1 SKIPBL (str, pos) s = index (suit, mapup (str (pos))) pos += 1 # position to next character if (r == 0 || s == 0) card = EOF # signal no card else card = Deck_index (13 * (s - 1) + r) return (card) end # log_results --- enter casino results in the log file subroutine log_results include SOL_COMMON file_des fd file_des open character usr (MAXUSERNAME), dat (9), tim (9) if (Total_winnings == 0) return fd = open (LOG_FILE, WRITE) if (fd ~= ERR) { call date (SYS_DATE, dat) call date (SYS_TIME, tim) call date (SYS_USERID, usr) call wind (fd) call print (fd, "*#s *6i *s *s*n"s, MAXUSERNAME - 1, usr, Total_winnings, dat, tim) call close (fd) } return end # message --- write a message on the message line subroutine message (str) character str (ARB) call vtprt (MSG_ROW, 1, "*80s"s, str) call vtupd (NO) return end # move --- handle the moving of one card to another integer function move (c1, c2) integer c1, c2 include SOL_COMMON integer legal_moves, rank, suit, pile, i integer check_king, check_ace, check_stack, card_is_movable if (card_is_movable (c1) == NO) { call message ("You can't move that card"s) return (ERR) } if (c2 == EOF) { suit = Deck (VALUE, c1) / 16 rank = mod (Deck (VALUE, c1), 16) if (check_king (c1, pile) == YES) # form a new king stack call move_stack (c1, pile) elif (rank == 1) # form a new ace stack call move_stack (c1, ACES + suit) else { pile = 0 legal_moves = 0 if (check_ace (c1, suit) == YES) { legal_moves += 1 pile = ACES + suit } do i = 1, 7 if (check_stack (c1, i) == YES) { legal_moves += 1 pile = FACE_UP + i } select when (legal_moves > 1) call message ("Move is ambiguous"s) when (legal_moves < 1) call message ("Move is impossible"s) ifany return (ERR) else call move_stack (c1, pile) } } else { pile = Deck (PLACE, c2) if (1 <= pile - ACES && pile - ACES <= 4 && check_ace (c1, pile - ACES) ~= NO || 1 <= pile - FACE_UP && pile - FACE_UP <= 7 && check_stack (c1, pile - FACE_UP) ~= NO) call move_stack (c1, pile) else { call message ("Move is illegal"s) return (ERR) } } return (OK) end # move_card --- move a card from one pile to another subroutine move_card (from, to) integer from, to include SOL_COMMON integer card integer remove_card if (remove_card (from, card) ~= EOF) call add_card (card, to) return end # move_stack --- move a group of cards from one stack to another subroutine move_stack (card, to) integer card, to include SOL_COMMON integer from Pile (COUNT, DUMMY) = 0 from = Deck (PLACE, card) while (Pile (TOP_CARD, from) ~= card) call move_card (from, DUMMY) call move_card (from, to) while (Pile (COUNT, DUMMY) > 0) call move_card (DUMMY, to) if (Pile (COUNT, from) <= 0 && 1 <= from - FACE_UP && from - FACE_UP <= 7) call move_card (FACE_DN + from - FACE_UP, from) return end # prompt --- write a prompt on the prompt line subroutine prompt (str) character str (ARB) integer length call vtprt (PROMPT_ROW, 1, "*80s"s, str) call vtupd (NO) call vtmove (PROMPT_ROW, length (str) + 1) # put the cursor at the end call vtpad (80) return end # quit_finish --- break handler for amateur card players subroutine quit_finish (dummy) integer dummy include SOL_COMMON if (Game_type == CASINO) call tally call log_results call vtstop stop end # read_clock --- return time since midnite in seconds longint function read_clock (time) longint time integer ar (5) call timdat (ar, 5) time = 60 * ar (4) + ar (5) return (time) end # remove_card --- take the top card off of a pile integer function remove_card (from, card) integer from, card include SOL_COMMON if (Pile (COUNT, from) <= 0) card = EOF else { Pile (COUNT, from) -= 1 card = Pile (TOP_CARD, from) Pile (TOP_CARD, from) = Deck (LINK, card) } return (card) end # set_game_type --- ask player if he wants regular or casino game subroutine set_game_type include SOL_COMMON character ans (2) call prompt ("(c)asino or (r)egular game? "s) call vtenb (PROMPT_ROW, 29, 2) call vtread (PROMPT_ROW, 29, YES) call vtgetl (ans, PROMPT_ROW, 29, 2) if (ans (1) == 'c'c || ans (1) == 'C'c) Game_type = CASINO else Game_type = REGULAR call vtenb (PROMPT_ROW, 29, 0) return end # shuffle --- shuffle the deck subroutine shuffle include SOL_COMMON integer i, j, card, rank, suit integer uniform procedure swap_card (i, j) forward card = 1 do suit = 1, 4 do rank = 1, 13; { Deck (VALUE, card) = 16 * suit + rank Deck (LINK, card) = uniform (1, 1000) Deck (PLACE, card) = STOCK card += 1 } # for (i = 1; i < 52; i += 1) # old swapping function # for (j = i + 1; j <= 52; j += 1) # if (Deck (LINK, i) <= Deck (LINK, j)) # swap_card (i, j) for (i = 1; i < 52; i += 1) # new swapping function for (j = 1; j <= 52; j += 1) swap_card (uniform (1, 52), uniform (1, 52)) do i = 1, 52; { suit = Deck (VALUE, i) / 16 rank = mod (Deck (VALUE, i), 16) Deck_index ((suit - 1) * 13 + rank) = i } return # swap_card --- exchange the position of two cards in the deck procedure swap_card (i, j) { integer i, j local t1, t2 integer t1, t2 t1 = Deck (LINK, i) t2 = Deck (VALUE, i) Deck (LINK, i) = Deck (LINK, j) Deck (LINK, j) = t1 Deck (VALUE, i) = Deck (VALUE, j) Deck (VALUE, j) = t2 } end # tally --- show how things turned out in a casino game subroutine tally include SOL_COMMON integer money, sum, i character wl (5), msg (MAXLINE) Games_played += 1 call count_unplayed (sum) if (sum ~= 0) { sum = 0 do i = 1, 4 sum += Pile (COUNT, ACES + i) } else sum = 52 money = 5 * sum - 50 Total_winnings += money call vtprt (CASINO_ROW, CASINO_COL, "Casino results: Games: *i"s, Games_played) if (Total_winnings >= 0) call vtprt (TALLY_ROW, TALLY_COL, "Won $*-6i"s, Total_winnings) else call vtprt (TALLY_ROW, TALLY_COL, "Lost $*-5i"s, -Total_winnings) select when (money > 0) call ctoc ("won"s, wl, 5) when (money < 0) call ctoc ("lost"s, wl, 5) ifany { call encode (msg, MAXLINE, "You *s $*i"s, wl, iabs (money)) call message (msg) } else # make sure the screen gets updated anyway call vtupd (NO) Game_type = REGULAR # in case he BREAKs return end # turn --- prompt for and process a move or command subroutine turn include SOL_COMMON character command (MAXLINE) integer pos, card1, card2, i integer interpret_card, move repeat { # until a valid move or command is seen call prompt ("Card to move: "s) call vtenb (PROMPT_ROW, 15, 10) call vtread (PROMPT_ROW, 15, YES) call vtgetl (command, PROMPT_ROW, 15, 10) i = 1 SKIPBL (command, i) select when (command (i) == 'x'c, command (i) == 'X'c) Game_over = TRUE when (command (i) == 'i'c, command (i) == 'I'c) if (command (i+1) == EOS || command (i+1) == ' 'c) { Red = '*'c Black = ' 'c } else { # set color indicators Red = command (i+1) Black = command (i+2) } when (command (i) == EOS) call flip_stack ifany { call message (EOS) break } pos = 1 card1 = interpret_card (command, pos) if (card1 ~= EOF) { card2 = interpret_card (command, pos) if (move (card1, card2) == OK) { call message (EOS) break } } else call message ("Enter cards in form "s) } return end # uniform --- generate uniform variate integer function uniform (lwb, upb) integer lwb, upb uniform = lwb + (upb - lwb) * rnd (0) + 0.5 return end