首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Toe与AI的游戏

Toe与AI的游戏
EN

Code Review用户
提问于 2016-10-27 20:51:36
回答 1查看 804关注 0票数 3

我最近开始学习球拍,并决定重新考虑引入计算机编程的问题:用人工智能制作一个Tac Toe游戏。

下面是代码:

代码语言:javascript
复制
#lang racket
(require math/array)


;; PLAYER DEFINITION
(define empty 'EMPTY)
(define draw 'DRAW)
(define player1 'PLAYER_ONE)
(define player2 'PLAYER_TWO)

(define (oponent player)
  (cond [(eq? player player1) player2]
        [(eq? player player2) player1]
        [else empty]))


;; BOARD DEFINITION

;; Position looks like '(x . y)
(define make-position cons)
(define get-x car)
(define get-y cdr)

(define board-indexes '(0 1 2))

(define (make-board)
  (make-vector 9 empty))

(define (board-position pos)
  ;; Used internally to convert 2D position to 1D
  (+ (get-y pos) (* (get-x pos) 3)))

(define (board-ref board pos)
  (vector-ref board (board-position pos)))

(define (board-set! board pos value)
  (vector-set! board (board-position pos) value))

;; Game looks like '(board . player)
(define get-player cdr)
(define get-board car)
(define make-game cons)


;; RULES

;; A line is a list with 3 positions
(define (make-line n f)
  (foldl (lambda (k l)
           (cons (f n k) l))
         (list)
         board-indexes))

(define (make-column n) (make-line n make-position))
(define (make-row n) (make-line n (lambda (a b) (make-position b a))))

(define win-lines
  ;; List with the lines that are relevant for determining wins
  (foldl (lambda (n l)
           (cons (make-row n) (cons (make-column n) l)))
         (list
          (make-line #f (lambda (_ n) ;; Diagonal one
                          (make-position n n)))
          (make-line #f (lambda (_ n) ;; Diagonal two
                          (make-position n (- 2 n)))))
         board-indexes))

(define (check-line board line)
  ;; Checks if any player won in a given line
  (apply
   (lambda (a b c)
     (if (and (eq? a b) (eq? b c) (not (eq? a empty)))
         a
         #f))
   (map(lambda (pos)
         (board-ref board pos))
       line)))

(define (who-won board)
  ;; Checks if one of the players already won
  (foldl (lambda (line old)
           (or
            old
            (check-line board line)))
         #f
         win-lines))

(define (is-over board)
  (not (vector-member empty board)))

(define (score board)
  ;; Returns the player who won, false if the game isn't over
  (or (who-won board) (if (is-over board) draw #f)))


;; PLAYER INTERFACE
(define all-cells
  ;; List with all '(x y) cells
  (foldl (lambda (i l)
           (append l (map (lambda (j) (cons i j)) board-indexes)))
         (list)
         board-indexes))

(define (try-play game pos fn)
  ;; Temporarily applies a play on the 'pos' cell and runs 'fn' on the resulting game
  (let ([board (get-board game)]
        [player (get-player game)])
    (board-set! board pos player)
    (define return (fn (make-game board (oponent player))))
    (board-set! board pos empty)
    return))

(define (fold-plays game selector)
  ;; Handy method to choose from all possible play options
  (foldl (lambda (pos current)
           (let ([board (get-board game)]
                 [player (get-player game)])
             (if (eq? empty (board-ref board pos))
                 (try-play game pos (lambda (game)
                                      (selector game current pos)))
                 current)))
         #f
         all-cells))

(define (play game pos)
  ;; Returns the game state updated after playing on position pos
  (let ([new-board (vector-copy (get-board game))]
        [player (get-player game)])
    (cond [(not (eq? (board-ref new-board pos) empty)) (error 'BAD_PLAY)]
          [else
           (board-set! new-board pos player)
           (make-game new-board (oponent player))])))

(define (ai-play game)
  ;; Artificial inteligente returns the ideal position to play on
  (define (predict game)
    (or (score (get-board game)) (try-play game (ai-play game) predict)))
  (let ([player (get-player game)])
    ;; Keep track of best '(score . position), but only return the position
    (cdr (fold-plays game (lambda (game best pos)
                            (cond [(not best) (cons (predict game) pos)]
                                  [(eq? (car best) player) best]
                                  [(eq? (predict game) player) (cons (predict game) pos)]
                                  [(eq? (car best) draw) best]
                                  [else (cons (predict game) pos)]))))))

(define (show game)
  ;; Displays current state of the game
  (for ([y board-indexes])
    (for ([x board-indexes])
      (print (board-ref (get-board game) (make-position x y))))
    (println '()))
  (print "PLAYNG: ")
  (print (get-player game))
  (println '_)
  (println '_))

;; Simple example of how to play, needs a better interface
(define board (make-board))
(define game (make-game board player1))
(set! game (play game '(0 . 0)))
(set! game (play game (ai-play game)))
(show game)

我希望主要就下列议题提供一些反馈意见:

  • 清晰性:代码是否简单易懂/扩展/修改?
  • 性能:在不牺牲前一点的情况下,是否有任何简单的更改可以使其更快?
  • 一般提示:我有什么计划程序员通常不会做的事情吗?

有些我不太关心的话题:

  • 界面:我知道现在情况不好,以后我会改进的。^
EN

回答 1

Code Review用户

发布于 2016-11-08 05:59:46

模数

所有的定义都在同一个词法范围内。例如,all-cellsfold-plays的词法作用域相同,尽管all-cells被引用的唯一位置是在fold-plays中。此外,还在它们之间定义了try-play。组织守则:

代码语言:javascript
复制
(define (fold-plays game selector)
  (define all-cells...))

将改善结构,并可能提高可读性。

命名

名称,如fold-plays混合游戏级逻辑的游戏与实现细节的折叠。这反映了抽象层的一般混合,这可能与代码中有限的模块化有关。

其他

  • firstrest在球拍中比carcdr更典型。
  • 一个功能,需要一个板和一个移动,并产生一个新的板将更典型的球拍,而不是变异板与Vector-set!。似乎没有使用数学/数组`。
票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/145464

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档