我最近开始学习球拍,并决定重新考虑引入计算机编程的问题:用人工智能制作一个Tac Toe游戏。
下面是代码:
#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)我希望主要就下列议题提供一些反馈意见:
有些我不太关心的话题:
发布于 2016-11-08 05:59:46
所有的定义都在同一个词法范围内。例如,all-cells与fold-plays的词法作用域相同,尽管all-cells被引用的唯一位置是在fold-plays中。此外,还在它们之间定义了try-play。组织守则:
(define (fold-plays game selector)
(define all-cells...))将改善结构,并可能提高可读性。
名称,如fold-plays混合游戏级逻辑的游戏与实现细节的折叠。这反映了抽象层的一般混合,这可能与代码中有限的模块化有关。
first和rest在球拍中比car和cdr更典型。Vector-set!。似乎没有使用数学/数组`。https://codereview.stackexchange.com/questions/145464
复制相似问题