; The Game of Scat

; Game framework by: Andrew Merrill
; Assignment completed by: 
; For more details about the game and rules, see http://www.pagat.com/draw/scat.html

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Set the constant display-computers-hand to true when debugging, so you can see
;   what cards the computer has.  Set it to false to play the game fairly.
(define display-computers-hand true)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; These three structures are used in this program.
; Please do not alter the definition of any of these structs.

(define-struct card (value suit))
; a structure for a playing card, where
;  value is a number (between 2-10) or a string ("jack", "queen", "king", or "ace")
;  suit is a string ("hearts", "diamonds", "clubs", or "spades")

(define-struct hand (card1 card2 card3))
; a structure for a hand of three card, where
; card1, card2, and card3 are all card structs

(define-struct table (person-hand computer-hand discard-card turn-player knocked-player winner-player))
; a structure for an entire game table, where
; person-hand and computer-hand are both hand structs
; discard-card is a card struct (holds the face-up card on the discard pile)
; turn-player is a string ("person", or "computer") indicating whose turn it is
; knocked-player is a string ("none", "person", or "computer") indicating who, if anyone, has knocked
; winner-player is a string ("none", "person", "computer", or "tie") indicating who, if anyone, has won

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; You need to finish writing these functions.

; You may write any additional helper functions you want.

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; get-card-from-hand: hand number -> card
; returns the card from the given position in the given hand
(define (get-card-from-hand old-hand position)
  ...)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; replace-card-in-hand: hand card number -> hand
; returns a hand struct that is identical to the given hand, 
; except that the card in the given position has been replaced
; by the given new card
(define (replace-card-in-hand old-hand new-card position)
  ...)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; hand-points: hand -> number
; given a hand struct, returns the points that the hand is worth
; according to the rules of the game Scat
(define (hand-points a-hand)
  ...)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; random-card:  void -> card
; returns a random card struct
(define (random-card)
  ...)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; random-hand:  void -> hand
; returns a hand struct containing three random cards
(define (random-hand)
  ...)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; decide-winner: table -> string
; Returns one of the strings "person", "computer", or "tie"
; depending on who has won the game, according to these rules:
;   - the player with the highest point value in their hand wins
;   - if it is a tie in points, then the player that knocked wins
;   - if it is a tie in points and no player knocked, then the game is a tie
(define (decide-winner the-table)
  ...)


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
;   You do not need to write or modify any functions after here
;
;    You are welcome to read these functions, but you are not
;    required to understand how they work.  These functions will
;    call the functions that you wrote above.
;
;    If you want to make the computer player smarter (optional!)
;    then you should have a look at the computer-play function.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; display-card: card -> void
; displays a verbal description of a card struct
(define (display-card a-card)
  (begin
    (display (card-value a-card))
    (display " of ")
    (display (card-suit a-card))))

; display-hand: hand -> void
; displays a verbals description of a hand struct
(define (display-hand a-hand)
  (begin
    (display-card (hand-card1 a-hand))
    (display ", ")
    (display-card (hand-card2 a-hand))
    (display ", ")
    (display-card (hand-card3 a-hand))
    (display "  (value ")
    (display (hand-points a-hand))
    (display ")")
    (newline)))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; person-play: table -> table
; This is where a person's turn starts.
; First, if they already knocked (on their previous turn), the game is over.
; Otherwise, display their hand and ask what action they want to take.
(define (person-play the-table)
  (cond
    [(string=? (table-knocked-player the-table) "person") (game-over the-table)]
    [else 
     (begin
       (newline)
       (cond
         [(boolean=? display-computers-hand true) 
          (begin (display "Computer's hand: ")
                 (display-hand (table-computer-hand the-table)))]
         [else true])
       (display "Your hand: ")
       (display-hand (table-person-hand the-table))
       (display "The old card on the discard pile is ")
       (display-card (table-discard-card the-table))
       (newline)
       (newline)
       (display "What do you want to do?")
       (newline)
       (display "  your options are: draw-new, draw-old, knock, or declare")
       (newline)
       (person-action the-table (read)))]))

; person-action: table symbol -> table
; This carries out the action the person wants to take.
(define (person-action the-table action)
  (cond
    [(symbol=? action 'draw-new) (person-ask-discard the-table (random-card))]
    [(symbol=? action 'draw-old) (person-ask-discard the-table (table-discard-card the-table))]
    [(symbol=? action 'knock)    (person-knock the-table)]
    [(symbol=? action 'declare)  (person-declare31 the-table)]
    [else (begin
            (display "That isn't one of your options.")
            (newline)
            (newline)
            the-table)]))

; person-ask-discard: table card -> table
; This tells the person what new card they drew,
; displays all four cards they now have,
; and asks which card they want to discard.
(define (person-ask-discard the-table new-card)
  (begin
    (newline)
    (display "You now have a ")
    (display-card new-card)
    (newline)
    (newline)
    (display "Your cards now are:")
    (newline)
    (display "  1)  ")
    (display-card (hand-card1 (table-person-hand the-table)))
    (newline)
    (display "  2)  ")
    (display-card (hand-card2 (table-person-hand the-table)))
    (newline)
    (display "  3)  ")
    (display-card (hand-card3 (table-person-hand the-table)))
    (newline)
    (display "  4)  ")
    (display-card new-card)
    (newline)
    (newline)
    (display "Select which card to discard: (1, 2, 3, or 4)  ")
    (person-discard the-table new-card (read))))

; person-discard: table card number -> table
; Now that we know what card the person wants to discard,
; we build a new table that includes the new person hand
; with the indicated card discarded.
; Then the person's turn is over, so it's the computer's turn.
(define (person-discard the-table new-card discard-position)
  (cond
    [(< discard-position 4)
     (make-table (replace-card-in-hand (table-person-hand the-table)
                                       new-card 
                                       discard-position)
                 (table-computer-hand the-table)
                 (get-card-from-hand (table-person-hand the-table) discard-position)
                 "computer"
                 (table-knocked-player the-table)
                 "none")]
    [(= discard-position 4)
     (make-table (table-person-hand the-table)
                 (table-computer-hand the-table)
                 new-card
                 "computer"
                 (table-knocked-player the-table)
                 "none")]))

; person-knock: table -> table
; This function is called if the person choose to knock.
; If the computer has already knocked, then it is too late.
; Otherwise, we let the computer play one last turn.
(define (person-knock the-table)
  (cond
    [(string=? (table-knocked-player the-table) "computer") 
     (begin
       (display "Sorry, but the computer player has already knocked.")
       (newline)
       the-table)]
    [else (make-table (table-person-hand the-table)
                      (table-computer-hand the-table)
                      (table-discard-card the-table)
                      "computer"
                      "person"
                      "none")]))

; person-declare31: table -> table
; This function is called if the person is declaring a win.
; If they don't have at least 31 points, then we make them play on.
; Otherwise, the game is over.
(define (person-declare31 the-table)
  (cond
    [(< (hand-points (table-person-hand the-table)) 31) 
     (begin
       (display "You can't declare a win if you don't have a 31 point hand.")
       (newline)
       (newline)
       the-table)]
    [else (game-over the-table)]))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; computer-play: table -> table
; This is where the computer's turn starts.
; First, if the computer knocked (on the previous turn), then the game is over.
; Otherwise, the computer takes its turn.
(define (computer-play the-table)
  (cond
    [(string=? (table-knocked-player the-table) "computer") (game-over the-table)]
    [else 
     (cond
       
       ; if the computer has 31 or more points, then declare a win.
       [(>= (hand-points (table-computer-hand the-table)) 31)
        (begin
          (newline)
          (display "The computer declares 31!")
          (newline)
          (game-over the-table))]
       
       ; if the computer has more than 25 points, then the computer knocks.
       [(> (hand-points (table-computer-hand the-table)) 25)
        (begin
          (newline)
          (display "The computer knocks!")
          (newline)
          (make-table (table-person-hand the-table)
                      (table-computer-hand the-table)
                      (table-discard-card the-table)
                      "person"
                      "computer"
                      "none"))]
       
       ; half the time, the computer draws a new card
       [(= (random 2) 0)
        (begin 
          (newline)
          (display "The computer draws a new card.")
          (newline)
          (computer-pick-discard the-table (random-card)))]
       
       ; otherwise, the computer takes the discard card
       [else
        (begin
          (newline)
          (display "The computer takes the discard card.")
          (newline)
          (computer-pick-discard the-table (table-discard-card the-table)))])]))

; computer-pick-discard: table card -> table
; The computer picks a random card from the three in its hand to discard.
(define (computer-pick-discard the-table new-card)
  (computer-discard the-table new-card (+ 1 (random 3))))

; computer-discard: table card number -> table
; The computer discards the card in the given position and
; replaces it with the given new card.
; Then, it's the person's turn to play again.
(define (computer-discard the-table new-card discard-position)
  (make-table (table-person-hand the-table)
              (replace-card-in-hand (table-computer-hand the-table)
                                    new-card 
                                    discard-position)
              (get-card-from-hand (table-computer-hand the-table) 
                                  discard-position)
              "person"
              (table-knocked-player the-table)
              "none"))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; game-over: table -> table
; This is called when the game is over.
; Both players show their hands, and we decide the winner.
(define (game-over the-table)
  (begin
    (newline)
    (display "The game is over!")
    (newline)
    (display "Your hand: ")
    (display-hand (table-person-hand the-table))
    (display "Computer's hand: ")
    (display-hand (table-computer-hand the-table))
    (newline)
    (make-table (table-person-hand the-table)
                (table-computer-hand the-table)
                (table-discard-card the-table)
                (table-turn-player the-table)
                (table-knocked-player the-table)
                (decide-winner the-table))))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; run-scat-game: number number -> string
; Runs a game of Scat, given the number of lives that each player has left.
(define (run-scat-game person-lives computer-lives)
  (begin
    (newline)
    (display "*****************************************")
    (newline)
    (newline)
    (display "Let's play Scat!")
    (newline)
    (newline)
    (display "You have ")
    (display person-lives)
    (display " lives left in the game.")
    (newline)
    (display "The computer has ")
    (display computer-lives)
    (display " lives left in the game.");
    (newline)
    (newline)
    (cond
      [(< person-lives 0) (begin
                            (display "You have no more lives left.")
                            (newline)
                            (display "The computer wins the game!")
                            (newline)
                            "computer-wins")]
      [(< computer-lives 0) (begin
                              (display "The computer has no more lives left.")
                              (newline)
                              (display "You win the game!")
                              (newline)
                              "person-wins")]
      [else (play-scat person-lives 
                       computer-lives
                       (make-table (random-hand) (random-hand) (random-card) "person" "none" "none"))])))

;play-scat: number number table -> string
(define (play-scat person-lives computer-lives old-table)
  (continue-playing-scat person-lives computer-lives
   (cond
     [(string=? (table-turn-player old-table) "person")  (person-play old-table)]
     [else (computer-play old-table)])))
    

;continue-playing-scat: number number table -> string
(define (continue-playing-scat person-lives computer-lives new-table)
    (cond
      [(not (string=? (table-winner-player new-table) "none")) 
       (display-winner person-lives computer-lives
                       (table-winner-player new-table))]
      [else (play-scat person-lives computer-lives new-table)]))


;display-winner: number number string -> string
; After a hand has been played out, checks to see who won
; and takes away a life from the loser.
; Then, we play again.
(define (display-winner person-lives computer-lives result)
  (cond
    [(string=? result "person") (begin
                                  (display "You won the hand.")
                                  (newline)
                                  (run-scat-game person-lives (- computer-lives 1)))]
    [(string=? result "computer") (begin
                                    (display "The computer won the hand.")
                                    (newline)
                                    (run-scat-game (- person-lives 1) computer-lives ))]
    [else (begin
            (display "It was a tie.")
            (newline)
            (run-scat-game person-lives computer-lives))]))

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; This is the call that starts the game, and sets how many lives
; each player gets to start with.

(run-scat-game 3 3)
