这次回顾Assignment 2 The game of twenty-one。

学习资料:

https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-001-structure-and-interpretation-of-computer-programs-spring-2005/index.htm

https://github.com/DeathKing/Learning-SICP

https://mitpress.mit.edu/sites/default/files/sicp/index.html

https://www.bilibili.com/video/BV1Xx41117tr?from=search&seid=14983483066585274454

作业地址:

https://mitpress.mit.edu/sites/default/files/sicp/psets/index.html

说明:

mit scheme的read有问题,所以这里使用chez scheme,在wsl中的运行命令为:

scheme.exe file.scm

相关资料:

https://stackoverflow.com/questions/57837383/how-to-use-read-correctly-in-mit-scheme

https://doraemonzzz.com/2020/11/29/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A8%8B%E5%BA%8F%E7%9A%84%E6%9E%84%E9%80%A0%E5%92%8C%E8%A7%A3%E9%87%8A(SICP)%E7%8E%AF%E5%A2%83%E9%85%8D%E7%BD%AE/

游戏规则说明

  • 两个人抽值为1~10的牌,最后比较谁手里的牌之和大,如果牌之和大于21,则失败;
  • 每个会给对手看一张牌(为第一次抽到的牌,整局保持不变);
  • 每人设计自己策略;

Problem 1

运行结果:

$ scheme.exe ps2tw1.scm 
Chez Scheme Version 9.5.4
Copyright 1984-2020 Cisco Systems, Inc.


Opponent up card
8

Your Total:
9

Hit?
y

Opponent up card
8

Your Total:
14

Hit?
y

Opponent up card
8

Your Total:
18
Hit?
y

Opponent up card
9

Your Total:
11

Hit?
y

Opponent up card
9

Your Total:
21

Hit?
n

Problem 2

代码:

(define (stop-at value)
  (lambda (your-hand opponent-up-card)
    (if (< (hand-total your-hand) value)
        #t
        #f)))

(twenty-one hit? (stop-at 16))

运行结果:

$ scheme.exe ps2tw1.scm 
Chez Scheme Version 9.5.4
Copyright 1984-2020 Cisco Systems, Inc.


Opponent up card 
8

Your Total:      
9

Hit?
y

Opponent up card 
8

Your Total:      
14

Hit?
y

Opponent up card 
8

Your Total:      
18

Hit?
n

Problem 3

代码:

(define (test-strategy player-strategy house-strategy n)
  (define (display-res player-win)
    (newline)
    (display "player win ")
    (display player-win)
    (display " in ")
    (display n)
    (display " games!"))
  (define (iter num player-win)
    (cond ((= n num) (display-res player-win))
          (else (iter (+ num 1) (+ player-win (twenty-one player-strategy house-strategy))))))
  (iter 0 0))

(test-strategy (stop-at 16) (stop-at 15) 100)

实验结果:

$ scheme.exe ps2tw1.scm 
Chez Scheme Version 9.5.4
Copyright 1984-2020 Cisco Systems, Inc.


player win 44 in 100 games!

Problem 4

代码:

(define (watch-player strategy)
  (lambda (your-hand opponent-up-card)
    (newline)
    (princ "Opponent up card ")
    (princ opponent-up-card)
    (newline)
    (princ "Your Total: ")
    (princ (hand-total your-hand))
    (newline)
    (strategy your-hand opponent-up-card)))

(test-strategy (watch-player (stop-at 16))
               (watch-player (stop-at 15))
               1)

(test-strategy (stop-at 16)
               (stop-at 15)
               1)

实验结果:

$ scheme.exe ps2tw1.scm 
Chez Scheme Version 9.5.4
Copyright 1984-2020 Cisco Systems, Inc.


Opponent up card
8

Your Total:
9


Opponent up card
8

Your Total:
14

9

Your Total:
8


Opponent up card
9

Your Total:
11


Opponent up card
9

Your Total:
21


player win 0 in 1 games!
player win 1 in 1 games!

可以看到watch-player给出了提示信息。

Problem 5

代码:

(define (louis your-hand opponent-up-card)
  (cond ((< (hand-total your-hand) 12) #t)
        ((> (hand-total your-hand) 16) #f)
        ((and (= (hand-total your-hand) 12) (< opponent-up-card 4)) #t)
        ((and (= (hand-total your-hand) 16) (= opponent-up-card 10)) #f)
        ((> opponent-up-card 6) #t)
        (else #f)))

(test-strategy louis (stop-at 15) 100)
(test-strategy louis (stop-at 16) 100)
(test-strategy louis (stop-at 17) 100)

实验结果:

$ scheme.exe ps2tw1.scm 
Chez Scheme Version 9.5.4
Copyright 1984-2020 Cisco Systems, Inc.


player win 39 in 100 games!
player win 44 in 100 games!
player win 46 in 100 games!

Problem 6

代码:

(define (both s1 s2)
  (lambda (your-hand opponent-up-card)
    (and (s1 your-hand opponent-up-card)
         (s2 your-hand opponent-up-card))))

(test-strategy (both louis (stop-at 19)) (stop-at 18) 100)

实验结果:

$ scheme.exe ps2tw1.scm 
Chez Scheme Version 9.5.4
Copyright 1984-2020 Cisco Systems, Inc.


player win 37 in 100 games!

Tutorial exercise 1

增加花色,利用1, 2, 3, 4表示。

代码:

;;; Scheme code for Twenty-One Simulator [PS2 Fall '90]

(define (twenty-one player-strategy house-strategy)
  (let ((house-initial-hand (make-new-hand (deal))))
    (let ((player-hand
           (play-hand player-strategy
                      (make-new-hand (deal))
                      (hand-up-card house-initial-hand))))
      (if (> (hand-total player-hand) 21)
          0                                ; ``bust'': player loses
          (let ((house-hand 
                 (play-hand house-strategy
                            house-initial-hand
                            (hand-up-card player-hand))))
            (cond ((> (hand-total house-hand) 21)
                   1)                      ; ``bust'': house loses
                  ((> (hand-total player-hand)
                      (hand-total house-hand))
                   1)                      ; house loses
                  (else 0)))))))           ; player loses

; (total ((v1 s1) (v2 s2)))

(define (card value suit)
    (list value suit))

(define (card-value card)
    (car card))

(define (card-suit card)
    (cadr card))

(define (deal) 
    (card (+ 1 (random 10)) (+ 1 (random 4))))

(define (make-new-hand first-card)
  (make-hand (list first-card) (card-value first-card)))

(define (make-hand up-card total)
  (list total up-card))

(define (hand-up-card hand)
  (caadr hand))

(define (hand-total hand)
  (car hand))

(define (hand-cards hand)
  (cadr hand))

(define (play-hand strategy my-hand opponent-up-card)
  (cond ((> (hand-total my-hand) 21) my-hand) ; I lose... give up
        ((strategy my-hand opponent-up-card) ; hit?
         (play-hand strategy
                    (hand-add-card my-hand (deal))
                    opponent-up-card))
        (else my-hand)))                ; stay

; append new-card
(define (hand-add-card hand new-card)
  ; (newline)
  ; (display hand)
  (make-hand (append (cadr hand) (list new-card))
             (+ (card-value new-card) (hand-total hand))))

(define (hit? your-hand opponent-up-card)
  (newline)
  (princ "Opponent up card ")
  (princ opponent-up-card)
  (newline)
  (princ "Your Total: ")
  (princ (hand-total your-hand))
  (newline)
  (princ "Your cards: ")
  (princ (hand-cards your-hand))
  (newline)
  (princ "Hit? ")
  (user-says-y?))

(define (user-says-y?) (eq? (read) 'y))

; add
(define (princ cmd)
  (display cmd)
  (newline))

(twenty-one hit? hit?)

实验结果:

$ scheme.exe tu_e1.scm 
Chez Scheme Version 9.5.4
Copyright 1984-2020 Cisco Systems, Inc.


Opponent up card
(9 4)

Your Total:
4
Opponent up card
(9 4)

Your Total:
14

Your cards:
((4 3) (10 1))

Hit?
y

Opponent up card
(9 4)

Your Total:
17

Your cards:
((4 3) (10 1) (3 1))

Hit?
y

Tutorial exercise 2

a

代码:

; a
(define (fresh-deck)
    (let ((n 10))
        (define (iter i res)
            (if (= i 0)
                res
                (iter (- i 1)
                      (append (list i i i i) res))))
        (iter n '())))

(display (fresh-deck))

(define (fresh-deck-change)
    (let ((n 10))
        (define (iter i d res)
            (cond ((= i 0) res)
                  ((<= i n) (iter (- i 1) (- d 1) (append (list d d d d) res)))
                  (else (iter (- i 1) d (append (list d d d d) res)))))
        (iter (+ n 3) n '())))

(display (fresh-deck-change))

实验结果:

(1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 5 5 5 5 6 6 6 6 7 7 7 7 8 8 8 8 9 9 9 9 10 10 10 10)

(1 1 1 1 2 2 2 2 3 3 3 3 4 4 4 4 5 5 5 5 6 6 6 6 7 7 7 7 8 8 8 8 9 9 9 9 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10 10)

b

代码:

; b
(define (length arr)
    (define (iter arr l)
        (if (eq? arr '())
            l
            (iter (cdr arr) (+ l 1))))
    (iter arr 0))

; arr[:i], arr[i:]
(define (get-arr arr i)
    (define (iter j a1 a2)
        (if (= j 0)
            (list a1 a2)
            (iter (- j 1) (append a1 (list (car a2))) (cdr a2))))
    (iter i '() arr))

(define a (list 1 2 3))

(get-arr a 1)

(define (shuffle arr)
    (let ((n (length arr)))
        ; n // 2
        (let ((m (quotient n 2)))
            (let ((arrs (get-arr arr m)))
                (let ((a1 (car arrs))
                      (a2 (cadr arrs)))
                    (define (iter arr1 arr2 res flag)
                        (cond ((eq? arr1 '()) (append res arr2))
                              ((eq? arr2 '()) (append res arr1))
                              ((eq? flag 0) (iter (cdr arr1) arr2 (append res (list (car arr1))) 1))
                              (else (iter arr1 (cdr arr2) (append res (list (car arr2))) 0))))
                    (iter a1 a2 '() 0))))))

(shuffle (list 1 2 3 4 5 6))

实验结果:

((1) (2 3))

(1 4 2 5 3 6)

c

代码:

; c
(define (random-shuffle arr)
    (let ((n (length arr)))
        (let ((m (quotient n 2)))
            (let ((arrs (get-arr arr m)))
                (let ((a1 (car arrs))
                      (a2 (cadr arrs)))
                    (define (get-random-num)
                        (+ 1 (random 5)))
                    (define (iter arr1 arr2 res num flag)
                        (let ((m (get-random-num)))
                            (cond ((eq? arr1 '()) (append res arr2))
                                  ((eq? arr2 '()) (append res arr1))
                                  ((> num 0)
                                        (if (eq? flag 0)
                                            (iter (cdr arr1) arr2 (append res (list (car arr1))) (- num 1) 0)
                                            (iter arr1 (cdr arr2) (append res (list (car arr2))) (- num 1) 1)))
                                  (else (iter arr1 arr2 res m (- 1 flag))))))
                    (iter a1 a2 '() 0 1))))))

(random-shuffle (list 1 2 3 4 5 6 7 8 9 10 11 12))

实验结果:

(1 2 3 4 9 10 5 6 11 12 13 14 15 7 8 16)

d

代码:

; d
(define (get-deck player-hand-and-deck)
  (cadr player-hand-and-deck))

(define (get-player-hand player-hand-and-deck)
  (car player-hand-and-deck))

(define (twenty-one player-strategy house-strategy)
  (let ((deck (new-deck)))
        (let ((house-initial-hand (make-new-hand (deal deck))))
              (let ((player-hand-and-deck
                    (play-hand player-strategy
                                (make-new-hand (deal (cdr deck)))
                                (hand-up-card house-initial-hand)
                                (cddr deck))))
                (let ((player-deck (get-deck player-hand-and-deck))
                      (player-hand (get-player-hand player-hand-and-deck)))
                  ; 如果牌没了
                  (cond ((eq? player-deck '()) (random-res))
                    (else
                      (if (> (hand-total player-hand) 21)
                          0                                ; ``bust'': player loses
                          (let ((house-hand-and-deck 
                                (play-hand house-strategy
                                          house-initial-hand
                                          (hand-up-card player-hand)
                                          player-deck)))
                            (let ((house-deck (get-deck house-hand-and-deck))
                                  (house-hand (get-player-hand house-hand-and-deck)))
                              (cond ((> (hand-total house-hand) 21)
                                    1)                      ; ``bust'': house loses
                                    ((> (hand-total player-hand)
                                        (hand-total house-hand))
                                    1)                      ; house loses
                                    (else 0))))))))))))       ; player loses

(define (play-hand strategy my-hand opponent-up-card deck)
  (cond ((eq? deck '()) (list my-hand deck))
        ((> (hand-total my-hand) 21) (list my-hand deck)) ; I lose... give up
        ((strategy my-hand opponent-up-card) ; hit?
         (play-hand strategy
                    (hand-add-card my-hand (deal deck))
                    opponent-up-card
                    (cdr deck)))
        (else (list my-hand deck))))                ; stay

(define (new-deck)
    (let ((deck (fresh-deck-change))
          (n 10))
         (define (shuffle-iter i res)
            (if (= i 0)
                res
                (shuffle-iter (- i 1) (random-shuffle res))))
         (shuffle-iter n deck)))

(define (random-res)
  (random 2))

(define (deal deck)
  (car deck))

(define (make-new-hand first-card)
  (make-hand first-card first-card))

(define (make-hand up-card total)
  (cons up-card total))

(define (hand-up-card hand)
  (car hand))

(define (hand-total hand)
  (cdr hand))

(define (hand-add-card hand new-card)
  (make-hand (hand-up-card hand)
             (+ new-card (hand-total hand))))

(define (hit? your-hand opponent-up-card)
  (newline)
  (princ "Opponent up card ")
  (princ opponent-up-card)
  (newline)
  (princ "Your Total: ")
  (princ (hand-total your-hand))
  (newline)
  (princ "Hit? ")
  (user-says-y?))

(define (user-says-y?) (eq? (read) 'y))

; add
(define (princ cmd)
  (display cmd)
  (newline))

(define (stop-at value)
  (lambda (your-hand opponent-up-card)
    (if (< (hand-total your-hand) value)
      #t
      #f)))

(define (test-strategy player-strategy house-strategy n)
  (define (display-res player-win)
    (newline)
    (display "player win ")
    (display player-win)
    (display " in ")
    (display n)
    (display " games!"))
  (define (iter num player-win)
    (cond ((= n num) (display-res player-win))
        (else (iter (+ num 1) (+ player-win (twenty-one player-strategy house-strategy))))))
  (iter 0 0))

(test-strategy (stop-at 16) (stop-at 15) 100)

实验结果:

player win 51 in 100 games!